Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <Arduino.h>
- #include <FastLED.h>
- #include <WiFiManager.h>
- #include <HTTPClient.h>
- #include <Wire.h>
- #include <Adafruit_I2CDevice.h>
- #include <Adafruit_GFX.h>
- #include <Adafruit_SSD1306.h>
- #include <ESP32Encoder.h>
- // Pins Used:
- // 4, 13, 15, 16, 17, 18, 19, 21, 22, 23, 26, 27, 32, 34, 36, 39
- #define DEBUGMODE true
- #define REDPIN 36
- #define GREENPIN 39
- #define BLUEPIN 34
- #define LEDPIN 32
- #define NUMLEDS 6
- #define POWERPIN 26
- #define LEDUPDATEMS 100
- #define MESSAGERATEMS 250
- #define MESSAGETRIGGERPIN 33
- #define MESSAGETOGGLEPIN 25
- #define ZONEPIN_CABU 15
- #define ZONEPIN_CABD 16
- #define ZONEPIN_WALL 17
- #define ZONEPIN_CLDN 18
- #define ZONEPIN_GZBO 19
- #define NUMZONES 5
- #define DEBOUNCEMS 15
- #define OLED_SCL 22
- #define OLED_SDA 21
- #define SCREEN_WIDTH 128
- #define SCREEN_HEIGHT 64
- #define ROTARYPBPIN 23
- #define ROTARYPIN1 13
- #define ROTARYPIN2 27
- #define COLORMODEPIN 4
- struct ZoneStruct {
- unsigned int pin;
- unsigned int id;
- bool active;
- ZoneStruct() {
- pin = 0;
- id = 0;
- active = false;
- }
- ZoneStruct(unsigned int p, unsigned int i) {
- this->pin = p;
- this->id = i;
- this->active = false;
- }
- };
- // Create OLED display object
- Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
- // Yeah, yeah. The zones and zone IDs are hard coded.
- unsigned int dimmerPins[3] = {REDPIN, GREENPIN, BLUEPIN};
- ZoneStruct zones[NUMZONES] = {
- ZoneStruct(ZONEPIN_CABU, 1),
- ZoneStruct(ZONEPIN_CABD, 2),
- ZoneStruct(ZONEPIN_WALL, 3),
- ZoneStruct(ZONEPIN_CLDN, 5),
- ZoneStruct(ZONEPIN_GZBO, 6)
- };
- // Define the array for the addressable LEDs
- CRGB leds[NUMLEDS];
- unsigned int red = 0, green = 0, blue = 0;
- unsigned int lastRed = 0, lastGreen = 0, lastBlue = 0;
- // A couple main loop variables to keep track of timing triggers
- // Only the "trigger" button is debounced since the zone switches
- // and "toggle" switch are not time sensitive
- unsigned long lastDimmerSample, lastDebugMessage, lastTriggerDebounceTime;
- // Variable to track the state of the "trigger" button. Button/switch
- // inputs are pulled HIGH so a LOW state means the button is pressed
- unsigned short triggerButtonState = HIGH, lastTriggerButtonState = HIGH;
- // Triggered messages should only be sent once per press.
- // Same for pattern messages. These variables tracks that
- bool triggerMessageSent = false, patternMessageSent = false;
- // Variable to track the state of the rotary encoder push button
- // inputs are pulled HIGH so a LOW state means the button is pressed
- unsigned short rotaryPBState = HIGH, lastRotaryPBState = HIGH;
- unsigned long lastRPBDebounceTime;
- // The Rotary Encoder for the pattern menu
- ESP32Encoder encoder;
- unsigned int encoderIndex = 0;
- unsigned int maxEncoderIndex = 0;
- // Keep track of color patterns pulled from server
- unsigned int patternCount = 0;
- char patterns[100][11];
- unsigned short colorModeState = HIGH, lastColorModeState = HIGH;
- unsigned long lastColorModeDebounceTime;
- // Variable for pulse-repaint of OLED
- bool needRepopulate = false;
- unsigned long repopulateTime;
- // Variable to monitor if we are in "Demo" mode
- // which means no WiFi connection attempt and no HTTP messages
- bool demoModeEnabled = false;
- // A simple function to get the dimmer/POT values
- // and return them as a mapped value for FastLED colors
- unsigned int getColorValue(unsigned int dimmerPin) {
- // Take ten samples of the dimmer and average them.
- // This smooths out the abysmal ADC built into the ESP32
- unsigned long sum = 0;
- for ( int i=0; i<10; i++ ) {
- sum += analogRead(dimmerPin);
- }
- sum = sum/10;
- // Map the sampled 10bit values to 8bit FastLED colors
- return map(sum, 0, 1023, 0, 255);
- }
- // Function to send messages to the back-end LED controller
- // for setting the current pattern active.
- void sendPatternMessage() {
- // Build our GET message for seting pattern
- char webMsg[255] = "";
- strcat(webMsg, "http://192.168.2.1/l/setpattern.php?");
- // This varieble will only be set to true
- // if any of the zone switches are on
- bool haveTarget = false;
- // Add all the target IDs
- // This is done using the "[]" notation
- // for URL array parameters native to PHP
- for ( int i=0; i<NUMZONES; i++ ) {
- if ( zones[i].active == true ) {
- char target[3] = "";
- sprintf(target, "%d", zones[i].id);
- strcat(webMsg, "t[]=");
- strcat(webMsg, target);
- strcat(webMsg, "&");
- haveTarget = true;
- }
- }
- // Bail out of all the targets are false
- if ( !haveTarget ) return;
- strcat(webMsg, "p=");
- strcat(webMsg, patterns[encoderIndex]);
- // Send the message if not in demo mode
- if ( !demoModeEnabled ) {
- HTTPClient http;
- http.begin(webMsg);
- http.GET();
- http.end();
- }
- // Set patternMessageSent to true if it wasn't and the trigger button is pressed
- if ( !rotaryPBState && !patternMessageSent ) patternMessageSent = true;
- if ( DEBUGMODE ) Serial.println(webMsg);
- }
- // Function to send messages to the back-end LED controller
- // We could send UDP messages from here, but it's better to
- // rely on the back end so this code doesn't need to change
- // if the LED control message format changes.
- void sendColorMessage() {
- // Our variables to hold the nex values
- char redHex[3] = "", greenHex[3] = "", blueHex[3] = "";
- // Set the hex values for the three colors from the global variables
- sprintf(redHex, "%02x", red);
- sprintf(greenHex, "%02x", green);
- sprintf(blueHex, "%02x", blue);
- // Build our GET message for setting colors
- char webMsg[255] = "";
- strcat(webMsg, "http://192.168.2.1/l/setcolor.php?");
- // This varieble will only be set to true
- // if any of the zone switches are on
- bool haveTarget = false;
- // Add all the target IDs
- // This is done using the "[]" notation
- // for URL array parameters native to PHP
- for ( int i=0; i<NUMZONES; i++ ) {
- if ( zones[i].active == true ) {
- char target[3] = "";
- sprintf(target, "%d", zones[i].id);
- strcat(webMsg, "t[]=");
- strcat(webMsg, target);
- strcat(webMsg, "&");
- haveTarget = true;
- }
- }
- // Bail out of all the targets are false
- if ( !haveTarget ) return;
- // Add the converted hex color value
- strcat(webMsg, "c=");
- strcat(webMsg, redHex);
- strcat(webMsg, greenHex);
- strcat(webMsg, blueHex);
- // Send the message if not in demo mode
- if ( !demoModeEnabled ) {
- HTTPClient http;
- http.begin(webMsg);
- http.GET();
- http.end();
- }
- // Set triggerMessageSent to true if it wasn't and the trigger button is pressed
- if ( !triggerButtonState && !triggerMessageSent ) triggerMessageSent = true;
- if ( DEBUGMODE ) Serial.println(webMsg);
- }
- unsigned int getEncoderIndex() {
- // If we aren't in "pattern" mode then
- // reset the current encoder count and return
- if ( colorModeState == HIGH ) {
- encoder.setCount(encoderIndex);
- return encoderIndex;
- }
- double currentIndex = encoder.getCount();
- if ( currentIndex > maxEncoderIndex ) {
- currentIndex = maxEncoderIndex;
- encoder.setCount(maxEncoderIndex);
- } else if ( currentIndex < 0 ) {
- currentIndex = 0;
- encoder.setCount(0);
- }
- return (unsigned int)currentIndex;
- }
- void getColorPatterns() {
- if ( demoModeEnabled ) {
- // If in demo mode make some dummy patterns
- strcpy(patterns[0], "Demo Time!");
- strcpy(patterns[1], "Happyness");
- strcpy(patterns[2], "Hilarious");
- strcpy(patterns[3], "California");
- strcpy(patterns[4], "Tortoise");
- patternCount = 5;
- maxEncoderIndex = patternCount - 1;
- return;
- }
- // Make the web call to get the color patterns
- HTTPClient http;
- http.begin("http://192.168.2.1/l/getpatterns.php");
- int httpCode = http.GET();
- String payload = http.getString();
- http.end();
- // If we don't have a good HTTP return code
- // or the payload is empty, then bail
- if ( (httpCode == 0) || (payload.length() == 0) ) return;
- // Parse the payload delimited by commas
- char values[payload.length() + 1];
- memset(values, '\0', sizeof(values));
- strcpy(values, payload.c_str());
- char *token = strtok(values, ",");
- while ( token != NULL ) {
- //strncpy(patterns[patternCount], token, strlen(token));
- strcpy(patterns[patternCount], token);
- patternCount++;
- token = strtok(NULL, ",");
- }
- // Update the maximum encoder value
- maxEncoderIndex = patternCount - 1;
- }
- void populateOLED(bool inverted = false) {
- unsigned short foreground = WHITE, background = BLACK;
- if ( inverted ) {
- foreground = BLACK;
- background = WHITE;
- needRepopulate = true;
- repopulateTime = millis() + 150;
- }
- oled.clearDisplay();
- oled.setCursor(0, 0);
- if ( colorModeState == HIGH ) {
- // State HIGH means in "single color mode"
- oled.setTextColor(foreground, background);
- oled.println("TURN DIALS");
- oled.println("PICK COLOR");
- oled.println("HIT BUTTON");
- oled.println("BE AMAZED!");
- } else {
- // State LOW means in "pattern mode"
- unsigned int startingIndex = 0;
- if ( encoderIndex > 2 ) {
- startingIndex = encoderIndex - 2;
- }
- unsigned int endingIndex = ((startingIndex + 3) > patternCount) ? patternCount : (startingIndex + 3);
- for ( unsigned int i=startingIndex; i<=endingIndex; i++ ) {
- if ( i == encoderIndex ) {
- oled.setTextColor(background, foreground);
- } else {
- oled.setTextColor(foreground, background);
- }
- char line[11] = "";
- // Buffer variable for string padding
- char spaceBuffer[] = " ";
- strcpy(line, patterns[i]);
- strncat(line, spaceBuffer, (10-strlen(patterns[i])));
- oled.println(line);
- }
- }
- oled.display();
- }
- void setup() {
- // Set COLORMODEPIN to INTPUT_PULLUP
- pinMode(COLORMODEPIN, INPUT_PULLUP);
- colorModeState = digitalRead(COLORMODEPIN);
- lastColorModeState = colorModeState;
- // Initialize color pattern name array
- memset(patterns, '\0', sizeof(patterns));
- // Turn on the "power" LED
- pinMode(POWERPIN, OUTPUT);
- ledcSetup(0, 5000, 8);
- ledcAttachPin(POWERPIN, 0);
- ledcWrite(0, 128);
- // Set up the serial console
- Serial.begin(115200);
- // Set up the Rotary Encoder push button input
- pinMode(ROTARYPBPIN, INPUT_PULLUP);
- //ESP32Encoder::useInternalWeakPullResistors=UP;
- encoder.attachSingleEdge(ROTARYPIN1, ROTARYPIN2);
- encoder.setCount(encoderIndex);
- // Set up the two pins for initiating a set color message to network
- pinMode(MESSAGETRIGGERPIN, INPUT_PULLUP);
- pinMode(MESSAGETOGGLEPIN, INPUT_PULLUP);
- // Set up the pins for tracking the zone enable/disable switches
- for ( int i=0; i<NUMZONES; i++ ) {
- pinMode(zones[i].pin, INPUT_PULLUP);
- }
- // Set up our in-box addressable LEDs and set them all to off
- FastLED.addLeds<WS2812B, LEDPIN, GRB>(leds, NUMLEDS);
- fill_solid(leds, NUMLEDS, CRGB::Black);
- FastLED.show();
- // Turn on OLED
- if ( !oled.begin(SSD1306_SWITCHCAPVCC, 0x3C) ) {
- Serial.println(F("Failed to start OLED Display!"));
- while (1);
- }
- // Clear the OLED display
- oled.clearDisplay();
- oled.display();
- // Test if we are in "Demo" mode. If the Rotary Push Button and
- // the "Trigger" button are held during boot the system will go
- // into demo mode. In demo mode no WiFi connection will be made
- // and no HTTP messages will be sent. If demo mode is detected
- // the system will cycle the addressable LED strip until both
- // buttons are released before continuing.
- if ( (digitalRead(ROTARYPBPIN) == LOW) && (digitalRead(MESSAGETRIGGERPIN) == LOW) ) {
- demoModeEnabled = true;
- short activeLED = 0;
- bool countingUp = true;
- oled.clearDisplay();
- oled.setCursor(0, 0);
- oled.setTextSize(2);
- oled.setTextColor(WHITE);
- oled.println("DEMO MODE");
- oled.println(" DETECTED ");
- oled.println("----------");
- oled.println(" RELEASE! ");
- oled.display();
- // Wait until both buttons have been released before continuing
- // Cycle the LEDs like KITT while waiting :)
- while ( (digitalRead(ROTARYPBPIN) == LOW) || (digitalRead(MESSAGETRIGGERPIN) == LOW) ) {
- leds[activeLED] = CRGB::Red;
- FastLED.show();
- delay(200);
- leds[activeLED] = CRGB::Black;
- FastLED.show();
- if ( countingUp ) {
- activeLED += 1;
- if ( activeLED == NUMLEDS ) {
- countingUp = false;
- activeLED = NUMLEDS - 2;
- }
- } else {
- activeLED -= 1;
- if ( activeLED == -1 ) {
- countingUp = true;
- activeLED = 1;
- }
- }
- }
- fill_solid(leds, NUMLEDS, CRGB::Black);
- FastLED.show();
- }
- // Set the analog resolution for color dials to 10 bits (0-1023)
- analogReadResolution(10);
- oled.clearDisplay();
- oled.setTextSize(2);
- oled.setTextColor(WHITE);
- oled.setCursor(0, 0);
- oled.println("CONNECTING");
- oled.println(" TO");
- oled.println(" WiFi");
- oled.println("..........");
- oled.display();
- // Join the network or start the captive portal AP for configuration
- // Turn the first LED blue while configuring WiFi so we know when it's done
- // Only do this if not in demo mode
- if ( !demoModeEnabled ) {
- fill_rainbow(leds, NUMLEDS, 0, 42);
- FastLED.show();
- WiFiManager wifiManager;
- wifiManager.autoConnect("JAJLedColorPicker");
- fill_solid(leds, NUMLEDS, CRGB::Black);
- FastLED.show();
- }
- getColorPatterns();
- populateOLED();
- }
- void loop() {
- // Check the state of the "trigger" button and debounce if needed
- unsigned short triggerCheck = digitalRead(MESSAGETRIGGERPIN);
- if ( triggerCheck != lastTriggerButtonState ) lastTriggerDebounceTime = millis();
- // If the button state has settled/debounced then take action
- if ( millis() > (lastTriggerDebounceTime + DEBOUNCEMS) ) {
- if ( triggerCheck != triggerButtonState ) {
- triggerButtonState = triggerCheck;
- // If the trigger button was released set the message tracker to false
- if ( triggerButtonState == HIGH ) triggerMessageSent = false;
- }
- }
- lastTriggerButtonState = triggerCheck;
- // Check the state of the "pattern mode" switch
- unsigned short colorModeCheck = digitalRead(COLORMODEPIN);
- if ( colorModeCheck != lastColorModeState ) lastColorModeDebounceTime = millis();
- // If the pattern mode switch state has settled/debounced then take action
- if ( millis() > (lastColorModeDebounceTime + DEBOUNCEMS) ) {
- if ( colorModeCheck != colorModeState ) {
- colorModeState = colorModeCheck;
- if ( colorModeState == LOW ) {
- populateOLED();
- fill_solid(leds, NUMLEDS, CRGB::Black);
- FastLED.show();
- } else {
- populateOLED();
- }
- }
- }
- lastColorModeState = colorModeCheck;
- // Check the state of the RotaryPB and debounce if needed
- unsigned short rotaryPBCheck = digitalRead(ROTARYPBPIN);
- if ( rotaryPBCheck != lastRotaryPBState ) lastRPBDebounceTime = millis();
- // If the rotary PB state has settled/debounced then take action
- if ( millis() > (lastRPBDebounceTime + DEBOUNCEMS) ) {
- if ( rotaryPBCheck != rotaryPBState ) {
- rotaryPBState = rotaryPBCheck;
- if ( (colorModeState == LOW) && (rotaryPBState == LOW) ) {
- // Poll all the zone switches
- for ( int i=0; i<NUMZONES; i++ ) {
- zones[i].active = (digitalRead(zones[i].pin) == LOW) ? true : false;
- }
- populateOLED(true);
- sendPatternMessage();
- }
- }
- }
- lastRotaryPBState = rotaryPBCheck;
- // Only sample the dimmers and update LED colors every LEDUPDATEMS milliseconds
- // Or if the trigger button has been pressed and no message sent yet
- if ( (colorModeState == HIGH) && ((!triggerButtonState && !triggerMessageSent) || (millis() > (lastDimmerSample + LEDUPDATEMS))) ) {
- // Grab the current color values
- red = getColorValue(REDPIN);
- green = getColorValue(GREENPIN);
- blue = getColorValue(BLUEPIN);
- // Set all the LEDs to the current color
- for ( int i=0; i<NUMLEDS; i++ ) {
- leds[i] = CRGB(red, green, blue);
- }
- FastLED.show();
- lastDimmerSample = millis();
- // If the "trigger" button has been pressed, or the "toggle" switch for
- // continuous message sending is turned on then we should send a set color
- // messages to the back-end. A triggered message will only be sent once
- // per press of the button. Continuous messages will only be sent every
- // LEDUPDATEMS milliseconds (i.e. the same rate as the sampling)
- if ( !digitalRead(MESSAGETOGGLEPIN) || (!digitalRead(MESSAGETRIGGERPIN) && !triggerButtonState && !triggerMessageSent) ) {
- for ( int i=0; i<NUMZONES; i++ ) {
- zones[i].active = (digitalRead(zones[i].pin) == LOW) ? true : false;
- }
- if ( !triggerButtonState && !triggerMessageSent ) populateOLED(true);
- sendColorMessage();
- }
- lastRed = red;
- lastGreen = green;
- lastBlue = blue;
- }
- // Check if the rotary encoder has been turned and take action
- unsigned int currentEncoderIndex = getEncoderIndex();
- if ( (colorModeState == LOW) && (currentEncoderIndex != encoderIndex) ) {
- encoderIndex = currentEncoderIndex;
- populateOLED();
- if ( DEBUGMODE ) Serial.println("Encoder: " + String(encoderIndex));
- }
- // Send state message to serial monitor port if DEBUGMODE is enabled
- if ( DEBUGMODE && (millis() > (lastDebugMessage + 1000)) ) {
- //Serial.println("Colors: " + String(red) + ", " + String(green) + ", " + String(blue) + ", " + String(getColorValue(REDPIN)));
- lastDebugMessage = millis();
- }
- // If we need to repopulate/repaint due to "pulse invert"
- if ( needRepopulate && (millis() > repopulateTime) ) {
- populateOLED();
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement