Advertisement
PlowmanPlow

LEDChooser ESP32

May 1st, 2022 (edited)
404
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 17.57 KB | None | 0 0
  1. #include <Arduino.h>
  2. #include <FastLED.h>
  3. #include <WiFiManager.h>
  4. #include <HTTPClient.h>
  5. #include <Wire.h>
  6. #include <Adafruit_I2CDevice.h>
  7. #include <Adafruit_GFX.h>
  8. #include <Adafruit_SSD1306.h>
  9. #include <ESP32Encoder.h>
  10.  
  11. // Pins Used:
  12. // 4, 13, 15, 16, 17, 18, 19, 21, 22, 23, 26, 27, 32, 34, 36, 39
  13.  
  14. #define DEBUGMODE true
  15. #define REDPIN 36
  16. #define GREENPIN 39
  17. #define BLUEPIN 34
  18. #define LEDPIN 32
  19. #define NUMLEDS 6
  20. #define POWERPIN 26
  21. #define LEDUPDATEMS 100
  22. #define MESSAGERATEMS 250
  23. #define MESSAGETRIGGERPIN 33
  24. #define MESSAGETOGGLEPIN 25
  25. #define ZONEPIN_CABU 15
  26. #define ZONEPIN_CABD 16
  27. #define ZONEPIN_WALL 17
  28. #define ZONEPIN_CLDN 18
  29. #define ZONEPIN_GZBO 19
  30. #define NUMZONES 5
  31. #define DEBOUNCEMS 15
  32. #define OLED_SCL 22
  33. #define OLED_SDA 21
  34. #define SCREEN_WIDTH 128
  35. #define SCREEN_HEIGHT 64
  36. #define ROTARYPBPIN 23
  37. #define ROTARYPIN1 13
  38. #define ROTARYPIN2 27
  39. #define COLORMODEPIN 4
  40.  
  41. struct ZoneStruct {
  42.   unsigned int pin;
  43.   unsigned int id;
  44.   bool active;
  45.  
  46.   ZoneStruct() {
  47.     pin = 0;
  48.     id = 0;
  49.     active = false;
  50.  
  51.   }
  52.   ZoneStruct(unsigned int p, unsigned int i) {
  53.     this->pin = p;
  54.     this->id = i;
  55.     this->active = false;
  56.   }
  57. };
  58.  
  59. // Create OLED display object
  60. Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
  61.  
  62. // Yeah, yeah. The zones and zone IDs are hard coded.
  63. unsigned int dimmerPins[3] = {REDPIN, GREENPIN, BLUEPIN};
  64. ZoneStruct zones[NUMZONES] = {
  65.   ZoneStruct(ZONEPIN_CABU, 1),
  66.   ZoneStruct(ZONEPIN_CABD, 2),
  67.   ZoneStruct(ZONEPIN_WALL, 3),
  68.   ZoneStruct(ZONEPIN_CLDN, 5),
  69.   ZoneStruct(ZONEPIN_GZBO, 6)
  70. };
  71. // Define the array for the addressable LEDs
  72. CRGB leds[NUMLEDS];
  73. unsigned int red = 0, green = 0, blue = 0;
  74. unsigned int lastRed = 0, lastGreen = 0, lastBlue = 0;
  75. // A couple main loop variables to keep track of timing triggers
  76. // Only the "trigger" button is debounced since the zone switches
  77. // and "toggle" switch are not time sensitive
  78. unsigned long lastDimmerSample, lastDebugMessage, lastTriggerDebounceTime;
  79. // Variable to track the state of the "trigger" button. Button/switch
  80. // inputs are pulled HIGH so a LOW state means the button is pressed
  81. unsigned short triggerButtonState = HIGH, lastTriggerButtonState = HIGH;
  82. // Triggered messages should only be sent once per press.
  83. // Same for pattern messages. These variables tracks that
  84. bool triggerMessageSent = false, patternMessageSent = false;
  85. // Variable to track the state of the rotary encoder push button
  86. // inputs are pulled HIGH so a LOW state means the button is pressed
  87. unsigned short rotaryPBState = HIGH, lastRotaryPBState = HIGH;
  88. unsigned long lastRPBDebounceTime;
  89. // The Rotary Encoder for the pattern menu
  90. ESP32Encoder encoder;
  91. unsigned int encoderIndex = 0;
  92. unsigned int maxEncoderIndex = 0;
  93. // Keep track of color patterns pulled from server
  94. unsigned int patternCount = 0;
  95. char patterns[100][11];
  96. unsigned short colorModeState = HIGH, lastColorModeState = HIGH;
  97. unsigned long lastColorModeDebounceTime;
  98. // Variable for pulse-repaint of OLED
  99. bool needRepopulate = false;
  100. unsigned long repopulateTime;
  101. // Variable to monitor if we are in "Demo" mode
  102. // which means no WiFi connection attempt and no HTTP messages
  103. bool demoModeEnabled = false;
  104.  
  105. // A simple function to get the dimmer/POT values
  106. // and return them as a mapped value for FastLED colors
  107. unsigned int getColorValue(unsigned int dimmerPin) {
  108.   // Take ten samples of the dimmer and average them.
  109.   // This smooths out the abysmal ADC built into the ESP32
  110.   unsigned long sum = 0;
  111.   for ( int i=0; i<10; i++ ) {
  112.     sum += analogRead(dimmerPin);
  113.   }
  114.   sum = sum/10;
  115.   // Map the sampled 10bit values to 8bit FastLED colors
  116.   return map(sum, 0, 1023, 0, 255);
  117. }
  118.  
  119. // Function to send messages to the back-end LED controller
  120. // for setting the current pattern active.
  121. void sendPatternMessage() {
  122.   // Build our GET message for seting pattern
  123.   char webMsg[255] = "";
  124.   strcat(webMsg, "http://192.168.2.1/l/setpattern.php?");
  125.   // This varieble will only be set to true
  126.   // if any of the zone switches are on
  127.   bool haveTarget = false;
  128.   // Add all the target IDs
  129.   // This is done using the "[]" notation
  130.   // for URL array parameters native to PHP
  131.   for ( int i=0; i<NUMZONES; i++ ) {
  132.     if ( zones[i].active == true ) {
  133.       char target[3] = "";
  134.       sprintf(target, "%d", zones[i].id);
  135.       strcat(webMsg, "t[]=");
  136.       strcat(webMsg, target);
  137.       strcat(webMsg, "&");
  138.       haveTarget = true;
  139.     }
  140.   }
  141.   // Bail out of all the targets are false
  142.   if ( !haveTarget ) return;
  143.   strcat(webMsg, "p=");
  144.   strcat(webMsg, patterns[encoderIndex]);
  145.   // Send the message if not in demo mode
  146.   if ( !demoModeEnabled ) {
  147.     HTTPClient http;
  148.     http.begin(webMsg);
  149.     http.GET();
  150.     http.end();
  151.   }
  152.   // Set patternMessageSent to true if it wasn't and the trigger button is pressed
  153.   if ( !rotaryPBState && !patternMessageSent ) patternMessageSent = true;
  154.   if ( DEBUGMODE ) Serial.println(webMsg);
  155. }
  156.  
  157. // Function to send messages to the back-end LED controller
  158. // We could send UDP messages from here, but it's better to
  159. // rely on the back end so this code doesn't need to change
  160. // if the LED control message format changes.
  161. void sendColorMessage() {
  162.   // Our variables to hold the nex values
  163.   char redHex[3] = "", greenHex[3] = "", blueHex[3] = "";
  164.   // Set the hex values for the three colors from the global variables
  165.   sprintf(redHex, "%02x", red);
  166.   sprintf(greenHex, "%02x", green);
  167.   sprintf(blueHex, "%02x", blue);
  168.   // Build our GET message for setting colors
  169.   char webMsg[255] = "";
  170.   strcat(webMsg, "http://192.168.2.1/l/setcolor.php?");
  171.   // This varieble will only be set to true
  172.   // if any of the zone switches are on
  173.   bool haveTarget = false;
  174.   // Add all the target IDs
  175.   // This is done using the "[]" notation
  176.   // for URL array parameters native to PHP
  177.   for ( int i=0; i<NUMZONES; i++ ) {
  178.     if ( zones[i].active == true ) {
  179.       char target[3] = "";
  180.       sprintf(target, "%d", zones[i].id);
  181.       strcat(webMsg, "t[]=");
  182.       strcat(webMsg, target);
  183.       strcat(webMsg, "&");
  184.       haveTarget = true;
  185.     }
  186.   }
  187.   // Bail out of all the targets are false
  188.   if ( !haveTarget ) return;
  189.   // Add the converted hex color value
  190.   strcat(webMsg, "c=");
  191.   strcat(webMsg, redHex);
  192.   strcat(webMsg, greenHex);
  193.   strcat(webMsg, blueHex);
  194.   // Send the message if not in demo mode
  195.   if ( !demoModeEnabled ) {
  196.     HTTPClient http;
  197.     http.begin(webMsg);
  198.     http.GET();
  199.     http.end();
  200.   }
  201.   // Set triggerMessageSent to true if it wasn't and the trigger button is pressed
  202.   if ( !triggerButtonState && !triggerMessageSent ) triggerMessageSent = true;
  203.   if ( DEBUGMODE ) Serial.println(webMsg);
  204. }
  205.  
  206. unsigned int getEncoderIndex() {
  207.   // If we aren't in "pattern" mode then
  208.   // reset the current encoder count and return
  209.   if ( colorModeState == HIGH ) {
  210.     encoder.setCount(encoderIndex);
  211.     return encoderIndex;
  212.   }
  213.   double currentIndex = encoder.getCount();
  214.   if ( currentIndex > maxEncoderIndex ) {
  215.     currentIndex = maxEncoderIndex;
  216.     encoder.setCount(maxEncoderIndex);
  217.   } else if ( currentIndex < 0 ) {
  218.     currentIndex = 0;
  219.     encoder.setCount(0);
  220.   }
  221.   return (unsigned int)currentIndex;
  222. }
  223.  
  224. void getColorPatterns() {
  225.   if ( demoModeEnabled ) {
  226.     // If in demo mode make some dummy patterns
  227.     strcpy(patterns[0], "Demo Time!");
  228.     strcpy(patterns[1], "Happyness");
  229.     strcpy(patterns[2], "Hilarious");
  230.     strcpy(patterns[3], "California");
  231.     strcpy(patterns[4], "Tortoise");
  232.     patternCount = 5;
  233.     maxEncoderIndex = patternCount - 1;
  234.     return;
  235.   }
  236.   // Make the web call to get the color patterns
  237.   HTTPClient http;
  238.   http.begin("http://192.168.2.1/l/getpatterns.php");
  239.   int httpCode = http.GET();
  240.   String payload = http.getString();
  241.   http.end();
  242.   // If we don't have a good HTTP return code
  243.   // or the payload is empty, then bail
  244.   if ( (httpCode == 0) || (payload.length() == 0) ) return;
  245.   // Parse the payload delimited by commas
  246.   char values[payload.length() + 1];
  247.   memset(values, '\0', sizeof(values));
  248.   strcpy(values, payload.c_str());
  249.   char *token = strtok(values, ",");
  250.   while ( token != NULL ) {
  251.     //strncpy(patterns[patternCount], token, strlen(token));
  252.     strcpy(patterns[patternCount], token);
  253.     patternCount++;
  254.     token = strtok(NULL, ",");
  255.   }
  256.   // Update the maximum encoder value
  257.   maxEncoderIndex = patternCount - 1;
  258. }
  259.  
  260. void populateOLED(bool inverted = false) {
  261.   unsigned short foreground = WHITE, background = BLACK;
  262.   if ( inverted ) {
  263.     foreground = BLACK;
  264.     background = WHITE;
  265.     needRepopulate = true;
  266.     repopulateTime = millis() + 150;
  267.   }
  268.   oled.clearDisplay();
  269.   oled.setCursor(0, 0);
  270.   if ( colorModeState == HIGH ) {
  271.     // State HIGH means in "single color mode"
  272.     oled.setTextColor(foreground, background);
  273.     oled.println("TURN DIALS");
  274.     oled.println("PICK COLOR");
  275.     oled.println("HIT BUTTON");
  276.     oled.println("BE AMAZED!");
  277.   } else {
  278.     // State LOW means in "pattern mode"
  279.     unsigned int startingIndex = 0;
  280.     if ( encoderIndex > 2 ) {
  281.       startingIndex = encoderIndex - 2;
  282.     }
  283.     unsigned int endingIndex = ((startingIndex + 3) > patternCount) ? patternCount : (startingIndex + 3);
  284.     for ( unsigned int i=startingIndex; i<=endingIndex; i++ ) {
  285.       if ( i == encoderIndex ) {
  286.         oled.setTextColor(background, foreground);
  287.       } else {
  288.         oled.setTextColor(foreground, background);
  289.       }
  290.       char line[11] = "";
  291.       // Buffer variable for string padding
  292.       char spaceBuffer[] = "          ";
  293.       strcpy(line, patterns[i]);
  294.       strncat(line, spaceBuffer, (10-strlen(patterns[i])));
  295.       oled.println(line);
  296.     }
  297.   }
  298.   oled.display();
  299. }
  300.  
  301. void setup() {
  302.   // Set COLORMODEPIN to INTPUT_PULLUP
  303.   pinMode(COLORMODEPIN, INPUT_PULLUP);
  304.   colorModeState = digitalRead(COLORMODEPIN);
  305.   lastColorModeState = colorModeState;
  306.   // Initialize color pattern name array
  307.   memset(patterns, '\0', sizeof(patterns));
  308.   // Turn on the "power" LED
  309.   pinMode(POWERPIN, OUTPUT);
  310.   ledcSetup(0, 5000, 8);
  311.   ledcAttachPin(POWERPIN, 0);
  312.   ledcWrite(0, 128);
  313.   // Set up the serial console
  314.   Serial.begin(115200);
  315.   // Set up the Rotary Encoder push button input
  316.   pinMode(ROTARYPBPIN, INPUT_PULLUP);
  317.   //ESP32Encoder::useInternalWeakPullResistors=UP;
  318.   encoder.attachSingleEdge(ROTARYPIN1, ROTARYPIN2);
  319.   encoder.setCount(encoderIndex);
  320.   // Set up the two pins for initiating a set color message to network
  321.   pinMode(MESSAGETRIGGERPIN, INPUT_PULLUP);
  322.   pinMode(MESSAGETOGGLEPIN, INPUT_PULLUP);
  323.   // Set up the pins for tracking the zone enable/disable switches
  324.   for ( int i=0; i<NUMZONES; i++ ) {
  325.     pinMode(zones[i].pin, INPUT_PULLUP);
  326.   }
  327.   // Set up our in-box addressable LEDs and set them all to off
  328.   FastLED.addLeds<WS2812B, LEDPIN, GRB>(leds, NUMLEDS);
  329.   fill_solid(leds, NUMLEDS, CRGB::Black);
  330.   FastLED.show();
  331.   // Turn on OLED
  332.   if ( !oled.begin(SSD1306_SWITCHCAPVCC, 0x3C) ) {
  333.     Serial.println(F("Failed to start OLED Display!"));
  334.     while (1);
  335.   }
  336.   // Clear the OLED display
  337.   oled.clearDisplay();
  338.   oled.display();
  339.   // Test if we are in "Demo" mode. If the Rotary Push Button and
  340.   // the "Trigger" button are held during boot the system will go
  341.   // into demo mode. In demo mode no WiFi connection will be made
  342.   // and no HTTP messages will be sent. If demo mode is detected
  343.   // the system will cycle the addressable LED strip until both
  344.   // buttons are released before continuing.
  345.   if ( (digitalRead(ROTARYPBPIN) == LOW) && (digitalRead(MESSAGETRIGGERPIN) == LOW) ) {
  346.     demoModeEnabled = true;
  347.     short activeLED = 0;
  348.     bool countingUp = true;
  349.     oled.clearDisplay();
  350.     oled.setCursor(0, 0);
  351.     oled.setTextSize(2);
  352.     oled.setTextColor(WHITE);
  353.     oled.println("DEMO  MODE");
  354.     oled.println(" DETECTED ");
  355.     oled.println("----------");
  356.     oled.println(" RELEASE! ");
  357.     oled.display();
  358.     // Wait until both buttons have been released before continuing
  359.     // Cycle the LEDs like KITT while waiting :)
  360.     while ( (digitalRead(ROTARYPBPIN) == LOW) || (digitalRead(MESSAGETRIGGERPIN) == LOW) ) {
  361.       leds[activeLED] = CRGB::Red;
  362.       FastLED.show();
  363.       delay(200);
  364.       leds[activeLED] = CRGB::Black;
  365.       FastLED.show();
  366.       if ( countingUp ) {
  367.         activeLED += 1;
  368.         if ( activeLED == NUMLEDS ) {
  369.           countingUp = false;
  370.           activeLED = NUMLEDS - 2;
  371.         }
  372.       } else {
  373.         activeLED -= 1;
  374.         if ( activeLED == -1 ) {
  375.           countingUp = true;
  376.           activeLED = 1;
  377.         }
  378.       }
  379.     }
  380.     fill_solid(leds, NUMLEDS, CRGB::Black);
  381.     FastLED.show();
  382.   }
  383.   // Set the analog resolution for color dials to 10 bits (0-1023)
  384.   analogReadResolution(10);
  385.   oled.clearDisplay();
  386.   oled.setTextSize(2);
  387.   oled.setTextColor(WHITE);
  388.   oled.setCursor(0, 0);
  389.   oled.println("CONNECTING");
  390.   oled.println("    TO");
  391.   oled.println("   WiFi");
  392.   oled.println("..........");
  393.   oled.display();
  394.   // Join the network or start the captive portal AP for configuration
  395.   // Turn the first LED blue while configuring WiFi so we know when it's done
  396.   // Only do this if not in demo mode
  397.   if ( !demoModeEnabled ) {
  398.     fill_rainbow(leds, NUMLEDS, 0, 42);
  399.     FastLED.show();
  400.     WiFiManager wifiManager;
  401.     wifiManager.autoConnect("JAJLedColorPicker");
  402.     fill_solid(leds, NUMLEDS, CRGB::Black);
  403.     FastLED.show();
  404.   }
  405.   getColorPatterns();
  406.   populateOLED();
  407. }
  408.  
  409. void loop() {
  410.   // Check the state of the "trigger" button and debounce if needed
  411.   unsigned short triggerCheck = digitalRead(MESSAGETRIGGERPIN);
  412.   if ( triggerCheck != lastTriggerButtonState ) lastTriggerDebounceTime = millis();
  413.   // If the button state has settled/debounced then take action
  414.   if ( millis() > (lastTriggerDebounceTime + DEBOUNCEMS) ) {
  415.     if ( triggerCheck != triggerButtonState ) {
  416.       triggerButtonState = triggerCheck;
  417.       // If the trigger button was released set the message tracker to false
  418.       if ( triggerButtonState == HIGH ) triggerMessageSent = false;
  419.     }
  420.   }
  421.   lastTriggerButtonState = triggerCheck;
  422.   // Check the state of the "pattern mode" switch
  423.   unsigned short colorModeCheck = digitalRead(COLORMODEPIN);
  424.   if ( colorModeCheck != lastColorModeState ) lastColorModeDebounceTime = millis();
  425.   // If the pattern mode switch state has settled/debounced then take action
  426.   if ( millis() > (lastColorModeDebounceTime + DEBOUNCEMS) ) {
  427.     if ( colorModeCheck != colorModeState ) {
  428.       colorModeState = colorModeCheck;
  429.       if ( colorModeState == LOW ) {
  430.         populateOLED();
  431.         fill_solid(leds, NUMLEDS, CRGB::Black);
  432.         FastLED.show();
  433.       } else {
  434.         populateOLED();
  435.       }
  436.     }
  437.   }
  438.   lastColorModeState = colorModeCheck;
  439.   // Check the state of the RotaryPB and debounce if needed
  440.   unsigned short rotaryPBCheck = digitalRead(ROTARYPBPIN);
  441.   if ( rotaryPBCheck != lastRotaryPBState ) lastRPBDebounceTime = millis();
  442.   // If the rotary PB state has settled/debounced then take action
  443.   if ( millis() > (lastRPBDebounceTime + DEBOUNCEMS) ) {
  444.     if ( rotaryPBCheck != rotaryPBState ) {
  445.       rotaryPBState = rotaryPBCheck;
  446.       if ( (colorModeState == LOW) && (rotaryPBState == LOW) ) {
  447.         // Poll all the zone switches
  448.         for ( int i=0; i<NUMZONES; i++ ) {
  449.           zones[i].active = (digitalRead(zones[i].pin) == LOW) ? true : false;
  450.         }
  451.         populateOLED(true);
  452.         sendPatternMessage();
  453.       }
  454.     }
  455.   }
  456.   lastRotaryPBState = rotaryPBCheck;
  457.   // Only sample the dimmers and update LED colors every LEDUPDATEMS milliseconds
  458.   // Or if the trigger button has been pressed and no message sent yet
  459.   if ( (colorModeState == HIGH) && ((!triggerButtonState && !triggerMessageSent) || (millis() > (lastDimmerSample + LEDUPDATEMS))) ) {
  460.     // Grab the current color values
  461.     red = getColorValue(REDPIN);
  462.     green = getColorValue(GREENPIN);
  463.     blue = getColorValue(BLUEPIN);
  464.     // Set all the LEDs to the current color
  465.     for ( int i=0; i<NUMLEDS; i++ ) {
  466.       leds[i] = CRGB(red, green, blue);
  467.     }
  468.     FastLED.show();
  469.     lastDimmerSample = millis();
  470.     // If the "trigger" button has been pressed, or the "toggle" switch for
  471.     // continuous message sending is turned on then we should send a set color
  472.     // messages to the back-end. A triggered message will only be sent once
  473.     // per press of the button. Continuous messages will only be sent every
  474.     // LEDUPDATEMS milliseconds (i.e. the same rate as the sampling)
  475.     if ( !digitalRead(MESSAGETOGGLEPIN) || (!digitalRead(MESSAGETRIGGERPIN) && !triggerButtonState && !triggerMessageSent) ) {
  476.       for ( int i=0; i<NUMZONES; i++ ) {
  477.         zones[i].active = (digitalRead(zones[i].pin) == LOW) ? true : false;
  478.       }
  479.       if ( !triggerButtonState && !triggerMessageSent ) populateOLED(true);
  480.       sendColorMessage();
  481.     }
  482.     lastRed = red;
  483.     lastGreen = green;
  484.     lastBlue = blue;
  485.   }
  486.   // Check if the rotary encoder has been turned and take action
  487.   unsigned int currentEncoderIndex = getEncoderIndex();
  488.   if ( (colorModeState == LOW) && (currentEncoderIndex != encoderIndex) ) {
  489.     encoderIndex = currentEncoderIndex;
  490.     populateOLED();
  491.     if ( DEBUGMODE ) Serial.println("Encoder: " + String(encoderIndex));
  492.   }
  493.   // Send state message to serial monitor port if DEBUGMODE is enabled
  494.   if ( DEBUGMODE && (millis() > (lastDebugMessage + 1000)) ) {
  495.     //Serial.println("Colors: " + String(red) + ", " + String(green) + ", " + String(blue) + ", " + String(getColorValue(REDPIN)));
  496.     lastDebugMessage = millis();
  497.   }
  498.   // If we need to repopulate/repaint due to "pulse invert"
  499.   if ( needRepopulate && (millis() > repopulateTime) ) {
  500.     populateOLED();
  501.   }
  502. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement