#include #include #include //#include #include #include #include #include //#define FASTLED_INTERRUPT_RETRY_COUNT 1 #define FASTLED_ALLOW_INTERRUPTS 0 #include "FastLED.h" /************ WIFI and MQTT Information (CHANGE THESE FOR YOUR SETUP) ******************/ const char* ssid = "YOURSSID"; const char* password = "YOURPASSWD"; const char* mqtt_server = "MQTT_IP"; const int mqtt_port = 1883; const char *mqtt_username = NULL; const char *mqtt_password = NULL; #define MILLION 1000000 #define SENSORNAME "bedroom" /************ OTA Updates ******************/ ESP8266WebServer httpServer(80); ESP8266HTTPUpdateServer httpUpdater; String update_status; SimpleTimer timer; /************* MQTT TOPICS (change these topics as you wish) **************************/ const char* light_state_topic = "bruh/bedroom"; const char* light_set_topic = "bruh/bedroom/set"; const char* on_cmd = "ON"; const char* off_cmd = "OFF"; const char* effect = "solid"; String effectString = "solid"; String oldeffectString = "solid"; /****************************************FOR JSON***************************************/ const int BUFFER_SIZE = JSON_OBJECT_SIZE(10); #define MQTT_MAX_PACKET_SIZE 512 /*********************************** FastLED Defintions ********************************/ #define NUM_LEDS 46 #define DATA_PIN D5 //#define CLOCK_PIN 5 #define CHIPSET WS2812B #define COLOR_ORDER GRB byte realRed = 0; byte realGreen = 0; byte realBlue = 0; byte red = 255; byte green = 255; byte blue = 255; byte brightness = 255; /******************************** GLOBALS for fade/flash *******************************/ bool stateOn = false; bool startFade = false; bool inFade = false; unsigned long lastLoop = 0; int transitionTime = 0; int effectSpeed = 0; int loopCount = 0; int stepR, stepG, stepB; int redVal, grnVal, bluVal; /********************************** GLOBALS for EFFECTS ******************************/ //RAINBOW uint8_t thishue = 0; // Starting hue value. uint8_t deltahue = 10; //CANDYCANE CRGBPalette16 currentPalettestriped; //for Candy Cane CRGBPalette16 gPal; //for fire //NOISE static uint16_t dist; // A random number for our noise generator. uint16_t scale = 30; // Wouldn't recommend changing this on the fly, or the animation will be really blocky. uint8_t maxChanges = 48; // Value for blending between palettes. CRGBPalette16 targetPalette(OceanColors_p); CRGBPalette16 currentPalette(CRGB::Black); //TWINKLE #define DENSITY 80 //RIPPLE uint8_t colour; // Ripple colour is randomized. int center = 0; // Center of the current ripple. int step = -1; // -1 is the initializing step. uint8_t myfade = 255; // Starting brightness. #define maxsteps 16 // Case statement wouldn't allow a variable. uint8_t bgcol = 0; // Background colour rotates. int thisdelay = 20; // Standard delay value. //DOTS uint8_t count = 0; // Count up to 255 and then reverts to 0 uint8_t fadeval = 224; // Trail behind the LED's. Lower => faster fade. uint8_t bpm = 30; //LIGHTNING uint8_t frequency = 50; // controls the interval between strikes uint8_t flashes = 8; //the upper limit of flashes per strike unsigned int dimmer = 1; uint8_t ledstart; // Starting location of a flash uint8_t ledlen; int lightningcounter = 0; //FUNKBOX int idex = 0; //-LED INDEX (0 to NUM_LEDS-1 int TOP_INDEX = int(NUM_LEDS / 2); int thissat = 255; //-FX LOOPS DELAY VAR uint8_t thishuepolice = 0; int antipodal_index(int i) { int iN = i + TOP_INDEX; if (i >= TOP_INDEX) { iN = ( i + TOP_INDEX ) % NUM_LEDS; } return iN; } //FIRE #define COOLING 55 #define SPARKING 120 bool gReverseDirection = false; //BPM uint8_t gHue = 0; WiFiClient espClient; PubSubClient client(espClient); struct CRGB leds[NUM_LEDS]; /********************************** START SETUP*****************************************/ void setup() { Serial.begin(115200); FastLED.addLeds(leds, NUM_LEDS); FastLED.setMaxRefreshRate(90); setupStripedPalette( CRGB::Red, CRGB::Red, CRGB::White, CRGB::White); //for CANDY CANE gPal = HeatColors_p; //for FIRE setup_wifi(); checkForUpdate(); httpServer.on("/", handle_root); httpServer.on("/reboot", reboot); httpUpdater.setup(&httpServer); httpServer.begin(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); Serial.println("Ready"); timer.setInterval(4 * 60 * 60 * 1000, checkForUpdate); timer.setInterval(10000, timer_10s); } void checkForUpdate() { update_status = F("Checking for update...\n"); BearSSL::WiFiClientSecure UpdateClient; UpdateClient.setInsecure(); t_httpUpdate_return result = ESPhttpUpdate.update(UpdateClient, F("https://10.1.1.93/arduino/update/")); //WiFiClient UpdateClient; //t_httpUpdate_return result = ESPhttpUpdate.update(UpdateClient, F("http://10.1.1.93/arduino/update/")); //t_httpUpdate_return result = ESPhttpUpdate.update("10.1.1.93", 80, "/arduino/update/"); update_status += F("Returned: "); switch(result) { case HTTP_UPDATE_FAILED: update_status += F("Update failed:\nLastError: "); update_status += ESPhttpUpdate.getLastError(); update_status += F("\nError: "); update_status += ESPhttpUpdate.getLastErrorString().c_str(); update_status += F("\n"); break; case HTTP_UPDATE_NO_UPDATES: update_status += F("No Update Available.\n"); break; case HTTP_UPDATE_OK: update_status += F("Updated OK.\n"); break; } } void handle_root() { String webpage = "

Bedroom RGB

"; webpage += "
- Reboot

Update Status:

";
	webpage += update_status;
	webpage += "
"; webpage += ""; webpage += ESP.getFullVersion(); webpage += ""; httpServer.send(200, "text/html", webpage); } void reboot() { String webpage = "Rebooting"; httpServer.send(200, "text/html", webpage); client.publish("bruh/bedroom/status", "Rebooting"); httpServer.handleClient(); client.loop(); delay(100); ESP.restart(); } /********************************** START SETUP WIFI*****************************************/ void setup_wifi() { Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(100); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } /* SAMPLE PAYLOAD: { "brightness": 120, "color": { "r": 255, "g": 100, "b": 100 }, "flash": 2, "transition": 5, "state": "ON" } */ /********************************** START CALLBACK*****************************************/ void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); char message[length + 1]; for (int i = 0; i < length; i++) { message[i] = (char)payload[i]; } message[length] = '\0'; Serial.println(message); if (!processJson(message)) { return; } if (stateOn) { realRed = map(red, 0, 255, 0, brightness); realGreen = map(green, 0, 255, 0, brightness); realBlue = map(blue, 0, 255, 0, brightness); } else { realRed = 0; realGreen = 0; realBlue = 0; } Serial.print("Effect requested: "); Serial.println(effect); startFade = true; inFade = false; // Kill the current fade sendState(); } /********************************** START PROCESS JSON*****************************************/ bool processJson(char* message) { StaticJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.parseObject(message); if (!root.success()) { Serial.println("parseObject() failed"); return false; } if (root.containsKey("state")) { if (strcmp(root["state"], on_cmd) == 0) { stateOn = true; } else if (strcmp(root["state"], off_cmd) == 0) { fadeToBlackBy( leds, NUM_LEDS, 25); effectString = "solid"; stateOn = false; } } if (root.containsKey("color")) { red = root["color"]["r"]; green = root["color"]["g"]; blue = root["color"]["b"]; } if (root.containsKey("color_temp")) { //temp comes in as mireds, need to convert to kelvin then to RGB int color_temp = root["color_temp"]; unsigned int kelvin = MILLION / color_temp; temp2rgb(kelvin); } if (root.containsKey("brightness")) { brightness = root["brightness"]; } if (root.containsKey("effect")) { effect = root["effect"]; effectString = effect; } if (root.containsKey("transition")) { transitionTime = root["transition"]; } else { transitionTime = 5; } return true; } /********************************** START SEND STATE*****************************************/ void sendState() { StaticJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); root["state"] = (stateOn) ? on_cmd : off_cmd; JsonObject& color = root.createNestedObject("color"); color["r"] = red; color["g"] = green; color["b"] = blue; root["brightness"] = brightness; root["effect"] = effectString.c_str(); char buffer[root.measureLength() + 1]; root.printTo(buffer, sizeof(buffer)); client.publish(light_state_topic, buffer, true); } /********************************** START RECONNECT*****************************************/ void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect(SENSORNAME, mqtt_username, mqtt_password)) { Serial.println("connected"); client.subscribe(light_set_topic); setColor(0, 0, 0); sendState(); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } /********************************** START MAIN LOOP*****************************************/ void loop() { if (WiFi.status() != WL_CONNECTED) { Serial.print("WIFI Disconnected. Attempting reconnection."); setup_wifi(); return; } if (!client.connected()) { reconnect(); } httpServer.handleClient(); client.loop(); timer.run(); if ( effectString == "bpm" ) { uint8_t BeatsPerMinute = 62; CRGBPalette16 palette = PartyColors_p; uint8_t beat = beatsin8( BeatsPerMinute, 64, 255); for ( int i = 0; i < NUM_LEDS; i++) { //9948 leds[i] = ColorFromPalette(palette, gHue + (i * 2), beat - gHue + (i * 10)); } gHue++; } else if (effectString == "candy cane") { static uint8_t startIndex = 0; startIndex = startIndex + 1; /* higher = faster motion */ fill_palette( leds, NUM_LEDS, startIndex, 16, currentPalettestriped, 255, LINEARBLEND); } else if ( effectString == "confetti" ) { fadeToBlackBy( leds, NUM_LEDS, 25); int pos = random16(NUM_LEDS); leds[pos] += CRGB(realRed + random8(64), realGreen, realBlue); } else if ( effectString == "cyclon rainbow" ) { static uint8_t hue = 0; // First slide the led in one direction for (int i = 0; i < NUM_LEDS; i++) { // Set the i'th led to red leds[i] = CHSV(hue++, 255, 255); // Show the leds showleds(); // now that we've shown the leds, reset the i'th led to black // leds[i] = CRGB::Black; fadeall(); // Wait a little bit before we loop around and do it again delay(10); } for (int i = (NUM_LEDS) - 1; i >= 0; i--) { // Set the i'th led to red leds[i] = CHSV(hue++, 255, 255); // Show the leds showleds(); // now that we've shown the leds, reset the i'th led to black // leds[i] = CRGB::Black; fadeall(); // Wait a little bit before we loop around and do it again delay(10); } } else if ( effectString == "dots" ) { uint8_t inner = beatsin8(bpm, NUM_LEDS / 4, NUM_LEDS / 4 * 3); uint8_t outer = beatsin8(bpm, 0, NUM_LEDS - 1); uint8_t middle = beatsin8(bpm, NUM_LEDS / 3, NUM_LEDS / 3 * 2); leds[middle] = CRGB::Purple; leds[inner] = CRGB::Blue; leds[outer] = CRGB::Aqua; nscale8(leds, NUM_LEDS, fadeval); } else if ( effectString == "fire" ) { Fire2012WithPalette(); } else if ( effectString == "glitter" ) { fadeToBlackBy( leds, NUM_LEDS, 20); addGlitterColor(80, realRed, realGreen, realBlue); } else if ( effectString == "juggle" ) { fadeToBlackBy(leds, NUM_LEDS, 20); for (int i = 0; i < 8; i++) { leds[beatsin16(i + 7, 0, NUM_LEDS - 1 )] |= CRGB(realRed, realGreen, realBlue); } } else if ( effectString == "lightning" ) { FastLED.clear(); ledstart = random8(NUM_LEDS); // Determine starting location of flash ledlen = random8(NUM_LEDS-ledstart); for (int flashCounter = 0; flashCounter < random8(3,flashes); flashCounter++) { if(flashCounter == 0) dimmer = 5; // the brightness of the leader is scaled down by a factor of 5 else dimmer = random8(1,3); // return strokes are brighter than the leader fill_solid(leds+ledstart,ledlen,CHSV(255, 0, 255/dimmer)); FastLED.show(); // Show a section of LED's delay(random8(4,10)); // each flash only lasts 4-10 milliseconds fill_solid(leds+ledstart,ledlen,CHSV(255,0,0)); // Clear the section of LED's FastLED.show(); if (flashCounter == 0) delay (150); // longer delay until next flash after the leader delay(50+random8(100)); // shorter delay between strokes } delay(random8(frequency)*100); } else if ( effectString == "noise" ) { for (int i = 0; i < NUM_LEDS; i++) { uint8_t index = inoise8(i * scale, dist + i * scale) % 255; leds[i] = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); } dist += beatsin8(10, 1, 4); nblendPaletteTowardPalette(currentPalette, targetPalette, maxChanges); // FOR NOISE ANIMATION } else if ( effectString == "police all" ) { idex++; if (idex >= NUM_LEDS) { idex = 0; } int idexR = idex; int idexB = antipodal_index(idexR); int thathue = (thishuepolice + 160) % 255; leds[idexR] = CHSV(thishuepolice, thissat, 255); leds[idexB] = CHSV(thathue, thissat, 255); } else if (effectString == "police one") { idex++; if (idex >= NUM_LEDS) { idex = 0; } int idexR = idex; int idexB = antipodal_index(idexR); int thathue = (thishuepolice + 160) % 255; for (int i = 0; i < NUM_LEDS; i++ ) { if (i == idexR) { leds[i] = CHSV(thishuepolice, thissat, 255); } else if (i == idexB) { leds[i] = CHSV(thathue, thissat, 255); } else { leds[i] = CHSV(0, 0, 0); } } } else if ( effectString == "rainbow" ) { // FastLED's built-in rainbow generator thishue++; fill_rainbow(leds, NUM_LEDS, thishue, deltahue); } else if ( effectString == "rainbow with glitter" ) { thishue++; fill_rainbow(leds, NUM_LEDS, thishue, deltahue); } else if ( effectString == "ripple" ) { for (int i = 0; i < NUM_LEDS; i++) leds[i] = CHSV(bgcol++, 255, 15); // Rotate background colour. switch (step) { case -1: // Initialize ripple variables. center = random(NUM_LEDS); colour = random8(); step = 0; break; case 0: leds[center] = CHSV(colour, 255, 255); // Display the first pixel of the ripple. step ++; break; case maxsteps: // At the end of the ripples. step = -1; break; default: // Middle of the ripples. leds[(center + step + NUM_LEDS) % NUM_LEDS] += CHSV(colour, 255, myfade / step * 2); // Simple wrap from Marc Miller leds[(center - step + NUM_LEDS) % NUM_LEDS] += CHSV(colour, 255, myfade / step * 2); step ++; // Next step. break; } } else if ( effectString == "sinelon" ) { fadeToBlackBy( leds, NUM_LEDS, 20); int pos = beatsin16(13, 0, NUM_LEDS - 1); leds[pos] += CRGB(realRed, realGreen, realBlue); } else if ( effectString == "twinkle" ) { const CRGB lightcolor(8, 7, 1); for ( int i = 0; i < NUM_LEDS; i++) { if ( !leds[i]) continue; // skip black pixels if ( leds[i].r & 1) { // is red odd? leds[i] -= lightcolor; // darken if red is odd } else { leds[i] += lightcolor; // brighten if red is even } } if ( random8() < DENSITY) { int j = random16(NUM_LEDS); if ( !leds[j] ) leds[j] = lightcolor; } } if (startFade) { loopCount = 0; inFade = true; } if (inFade) { startFade = false; if ( loopCount <= 120 ) { CRGB arrTargetColor(realRed, realGreen, realBlue); fadeTowardColor( leds, NUM_LEDS, arrTargetColor, 8); loopCount++; } else { inFade = false; } } showleds(); } void timer_10s() { random16_add_entropy( random8()); targetPalette = CRGBPalette16(CHSV(random8(), 255, random8(128, 255)), CHSV(random8(), 255, random8(128, 255)), CHSV(random8(), 192, random8(128, 255)), CHSV(random8(), 255, random8(128, 255))); } /********************************** START SHOW LEDS ***********************************************/ void showleds() { if (stateOn or inFade) { FastLED.setBrightness(brightness); FastLED.show(); } else { delay(5); } } /********************************** START Set Color*****************************************/ void setColor(int inR, int inG, int inB) { for (int i = 0; i < NUM_LEDS; i++) { leds[i].red = inR; leds[i].green = inG; leds[i].blue = inB; } FastLED.show(); } // Helper function that blends one uint8_t toward another by a given amount void nblendU8TowardU8( uint8_t& cur, const uint8_t target, uint8_t amount) { if( cur == target) return; if( cur < target ) { uint8_t delta = target - cur; delta = scale8_video( delta, amount); cur += delta; } else { uint8_t delta = cur - target; delta = scale8_video( delta, amount); cur -= delta; } } // Blend one CRGB color toward another CRGB color by a given amount. // Blending is linear, and done in the RGB color space. // This function modifies 'cur' in place. CRGB fadeTowardColor( CRGB& cur, const CRGB& target, uint8_t amount) { nblendU8TowardU8( cur.red, target.red, amount); nblendU8TowardU8( cur.green, target.green, amount); nblendU8TowardU8( cur.blue, target.blue, amount); return cur; } // Fade an entire array of CRGBs toward a given background color by a given amount // This function modifies the pixel array in place. void fadeTowardColor( CRGB* L, uint16_t N, const CRGB& bgColor, uint8_t fadeAmount) { for( uint16_t i = 0; i < N; i++) { fadeTowardColor( L[i], bgColor, fadeAmount); } } /**************************** START STRIPLED PALETTE *****************************************/ void setupStripedPalette( CRGB A, CRGB AB, CRGB B, CRGB BA) { currentPalettestriped = CRGBPalette16( A, A, A, A, A, A, A, A, B, B, B, B, B, B, B, B // A, A, A, A, A, A, A, A, B, B, B, B, B, B, B, B ); } /********************************** START FADE************************************************/ void fadeall() { for (int i = 0; i < NUM_LEDS; i++) { leds[i].nscale8(250); //for CYCLon } } /********************************** START FIRE **********************************************/ void Fire2012WithPalette() { // Array of temperature readings at each simulation cell static byte heat[NUM_LEDS]; // Step 1. Cool down every cell a little for ( int i = 0; i < NUM_LEDS; i++) { heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2)); } // Step 2. Heat from each cell drifts 'up' and diffuses a little for ( int k = NUM_LEDS - 1; k >= 2; k--) { heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; } // Step 3. Randomly ignite new 'sparks' of heat near the bottom if ( random8() < SPARKING ) { int y = random8(7); heat[y] = qadd8( heat[y], random8(160, 255) ); } // Step 4. Map from heat cells to LED colors for ( int j = 0; j < NUM_LEDS; j++) { // Scale the heat value from 0-255 down to 0-240 // for best results with color palettes. byte colorindex = scale8( heat[j], 240); CRGB color = ColorFromPalette( gPal, colorindex); int pixelnumber; if ( gReverseDirection ) { pixelnumber = (NUM_LEDS - 1) - j; } else { pixelnumber = j; } leds[pixelnumber] = color; } } /********************************** START ADD GLITTER *********************************************/ void addGlitter( fract8 chanceOfGlitter) { if ( random8() < chanceOfGlitter) { leds[ random16(NUM_LEDS) ] += CRGB::White; } } /********************************** START ADD GLITTER COLOR ****************************************/ void addGlitterColor( fract8 chanceOfGlitter, int red, int green, int blue) { if ( random8() < chanceOfGlitter) { leds[ random16(NUM_LEDS) ] += CRGB(red, green, blue); } } void temp2rgb(unsigned int kelvin) { int tmp_internal = kelvin / 100.0; // red if (tmp_internal <= 66) { red = 255; } else { float tmp_red = 329.698727446 * pow(tmp_internal - 60, -0.1332047592); if (tmp_red < 0) { red = 0; } else if (tmp_red > 255) { red = 255; } else { red = tmp_red; } } // green if (tmp_internal <=66){ float tmp_green = 99.4708025861 * log(tmp_internal) - 161.1195681661; if (tmp_green < 0) { green = 0; } else if (tmp_green > 255) { green = 255; } else { green = tmp_green; } } else { float tmp_green = 288.1221695283 * pow(tmp_internal - 60, -0.0755148492); if (tmp_green < 0) { green = 0; } else if (tmp_green > 255) { green = 255; } else { green = tmp_green; } } // blue if (tmp_internal >=66) { blue = 255; } else if (tmp_internal <= 19) { blue = 0; } else { float tmp_blue = 138.5177312231 * log(tmp_internal - 10) - 305.0447927307; if (tmp_blue < 0) { blue = 0; } else if (tmp_blue > 255) { blue = 255; } else { blue = tmp_blue; } } }