/* * Serial LED Controller, IM Dendrite module (for ESP8266) * Created for Interplaymedium™ project (https://interplaymedium.org) * Copyright © 2016 Dmitry Shalnov [interplaymedium.org] * Licensed under the Apache License, Version 2.0 */ #define SERIAL 0 #include "../../info" #include "version.c" #include #include #include #include #include #include #define LED_STRIPE WS2811 // WS2812B // APA106 // type of LEDs Supported LED chipsets: http://fastled.io/docs/3.1/md__r_e_a_d_m_e.html // -------------------- FastLED settings -------------- #define FASTLED_ESP8266_RAW_PIN_ORDER #define FASTLED_INTERNAL // to remove #pragma diagnostics messages #include // Serial // Switch 4 channel #define DATA_PIN 3 // 3 // 2 // DIn controller pin (GPIO2) TODO: switch to GPIO0 + pull down (to avoid blink on reset) #define ENABLE_GATE_PIN 2 // 2 // 3 // common ping for SN74HC08 AND gate (to prevent blink on power on + pull up the level) #define MAX_NUM_LEDS 256 // maximum number of leds in stripe #define DEF_NUM_LEDS 24 // default number of leds in stripe #define DEF_MAIN_DELAY 1 // default FX delay (in loop) #define FULL_OUTPUT 2 // default quiet level CRGB LEDStripe_RGB[ MAX_NUM_LEDS ]; // This is an array of leds. One item for each led in your stripe. CHSV color_HSV; String param; uint8_t paramCnt = 0; uint8_t JSONon = 0; uint8_t numberOfLEDs = DEF_NUM_LEDS; uint16_t mainDelay = 0, mainDelayNew = 0; unsigned char FX_PhaseXDelay = 0, FX_PhaseYDelay = 0, FX_PhaseZDelay = 0; // uint8_t EXP[ 256 ]; // lookup table unsigned long timeStamp = 0; uint8_t changeColor = 0, quiet = FULL_OUTPUT, commandReboot = false; // -------------------- EEPROM addresing --------------- #define EEPROM_HOST 0 #define EEPROM_HOST_MAX_LEN 57 // Host name max length, can't be > 57 (could be 64, buth there is a bug in avahi https://github.com/lathiat/avahi/issues/273) #define EEPROM_OTHER_MAX_LEN 13 // total size of other EEPROM parameters (summ of all EEPROM values below, included EEPROM_FLAG) #define EEPROM_FLAG EEPROM_HOST_MAX_LEN + 1 // EEPROM has been changed (avoid overlap: EEPROM_HOST_MAX_LEN) #define EEPROM_numberOfLEDs EEPROM_HOST_MAX_LEN + 2 #define EEPROM_HgradBegin EEPROM_HOST_MAX_LEN + 3 // (the order of defines matter) #define EEPROM_SgradBegin EEPROM_HOST_MAX_LEN + 4 #define EEPROM_VgradBegin EEPROM_HOST_MAX_LEN + 5 #define EEPROM_HgradEnd EEPROM_HOST_MAX_LEN + 6 #define EEPROM_SgradEnd EEPROM_HOST_MAX_LEN + 7 #define EEPROM_VgradEnd EEPROM_HOST_MAX_LEN + 8 #define EEPROM_PERIOD EEPROM_HOST_MAX_LEN + 9 #define EEPROM_FX_PhaseX EEPROM_HOST_MAX_LEN + 10 #define EEPROM_FX_PhaseY EEPROM_HOST_MAX_LEN + 11 #define EEPROM_FX_PhaseZ EEPROM_HOST_MAX_LEN + 12 #define EEPROM_mainDelayNew EEPROM_HOST_MAX_LEN + 13 // ------------------- array of parameters ------------- (the order of defines matter) #define NUMBER_OF_PARAM 10 #define H_GRAD_BEG 0 #define S_GRAD_BEG 1 #define V_GRAD_BEG 2 #define H_GRAD_END 3 #define S_GRAD_END 4 #define V_GRAD_END 5 #define PERIOD 6 #define PHASE_X_INC 7 #define PHASE_Y_INC 8 #define PHASE_Z_INC 9 float FX_Arr[ NUMBER_OF_PARAM ] = {}; float FX_ArrNew[ NUMBER_OF_PARAM ] = {}; float FX_Coeff[ NUMBER_OF_PARAM ] = {}; float Phase[ 3 ] = {}; float FX_CoeffMax = 1.0; #define PHASE_X 0 #define PHASE_Y 1 #define PHASE_Z 2 // ------------------- server settings ----------------- #define HOST_DEFAULT "im_serial_" #define NTP_SERVER "europe.pool.ntp.org" char host[ EEPROM_HOST_MAX_LEN ]; // const char* ssid = "axod_ap"; // const char* password = "superstrongpassword" const char* ssid = IM_WIFI_SSID; const char* password = IM_WIFI_PASS; ESP8266WebServer server(80); String hostDefURI; String HTTPOut; String HTTPErr; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, NTP_SERVER , 0, 60000); // ---------------- EEPROM String r/w ------------------ void EEPROMStrRead( uint8_t addr, char * str ){ for (uint8_t a = addr; a < EEPROM_HOST_MAX_LEN; a++ ){ str[ a ] = EEPROM.read( a ); if (str[ a ] == 0) break; } } void EEPROMStrWrite( uint8_t addr, char * str ){ uint8_t a = 0; for (a = addr; a < EEPROM_HOST_MAX_LEN; a++ ){ EEPROM.write( a, str[ a ] ); if (str[ a ] == 0) break; } EEPROM.write( a, 0 ); } // ---------------- misc ------------------------------- int str2HEX(const String str) { return strtol( str.c_str(), 0, 16 ); } String printNum(unsigned long num, int base, char sign) { char outbuf[12]; int i = 12; int j = 0; int n; do { outbuf[i] = "0123456789ABCDEF"[num % base]; i--; num = num/base; } while ( num > 0 ); if ( sign != ' ' ){ outbuf[0] = sign; ++j; } while ( ++i < 13 ){ outbuf[j++] = outbuf[i]; } if ( j == 1 ) { outbuf[ 1 ] = outbuf[ 0 ]; outbuf[ 0 ] = '0'; ++j; } outbuf[j] = 0; return outbuf; } uint8_t is_number( const char *s ){ while (*s) { Serial.printf("s: %c\n", *s); if ( *s++ <48 || *s >57 ) return 0; } return true; } // ----------- explode for selected substring ----------- String expld( String str, unsigned int numb, char delimiter ){ unsigned int cnt = 0, a = 0, p2 = 0, p1 = 0, lng = 0; lng = str.length(); for ( a = 0; a < lng; a++ ){ if ( str.charAt( a ) == delimiter ) { p2 = p1; p1 = a; if ( cnt == numb ) break; cnt ++; } } if ( cnt < numb ) return ""; if ( a == lng ) { p2 = p1; p1 = lng; } if ( numb > 0 ) p2 ++; return str.substring(p2, p1); } // ---------------- FX --------------------------------- void FX_Gradient_RT( uint8_t HgradBegin, uint8_t SgradBegin, uint8_t VgradBegin, uint8_t HgradEnd, uint8_t SgradEnd, uint8_t VgradEnd, uint8_t period, uint8_t phaseX, uint8_t phaseY, uint8_t phaseZ ) { unsigned int i = 0; CHSV color; #define Y_PHASE_SHIFT 128 // 128 // to avoid solid fill on phase = 0 // NOTE! phaseZ is for color component only since it is cyclic for (i = 0; i < numberOfLEDs; i++) { // newHueColor = pow( (float)sin( (float)i*PI / (period * 2) ) ,2 ) * (gradEnd - gradBegin) + gradBegin; // MEMO! floating point if ( HgradEnd != HgradBegin ) color.h = ( ((int)cos8( (i + phaseX) * 128 / period ) - 128) * ((int)sin8( (phaseY + Y_PHASE_SHIFT) * 128 / period ) - 128) / 128 + 128 ) / ( 256 / ( HgradEnd - HgradBegin ) ) + HgradBegin; else color.h = HgradEnd; if ( SgradEnd != SgradBegin ) color.s = ( ((int)cos8( (i + phaseX) * 128 / period ) - 128) * ((int)sin8( (phaseY + Y_PHASE_SHIFT) * 128 / period ) - 128) / 128 + 128 ) / ( 256 / ( SgradEnd - SgradBegin ) ) + SgradBegin; else color.s = SgradEnd; if ( VgradEnd != VgradBegin ) color.v = ( ((int)cos8( (i + phaseX) * 128 / period ) - 128) * ((int)sin8( (phaseY + Y_PHASE_SHIFT) * 128 / period ) - 128) / 128 + 128 ) / ( 256 / ( VgradEnd - VgradBegin ) ) + VgradBegin; else color.v = VgradEnd; // LEDStripe_RGB[ i ] = CHSV( color.h + phaseZ , color.s, color.v ); color.h += phaseZ; hsv2rgb_spectrum(color, LEDStripe_RGB[ i ]); } } // ----------------------------------------------------- // --------------- Init -------------------------------- // ----------------------------------------------------- void setup(void) { HTTPOut.reserve(800); // lenght of Help message generally hostDefURI.reserve( EEPROM_HOST_MAX_LEN ); hostDefURI = HOST_DEFAULT + WiFi.macAddress().substring(12, 14) + WiFi.macAddress().substring(15, 17); // 00:00:00:00:00:00 // calculate lookup array // for (unsigned int a = 1; a <= 255; a++) EXP[ a ] = round( pow( 255, (double)a / (255) ) ); // calculate exponential lookup array (for PWM etc..) // reset FX_Coeff (maybe not required) for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) FX_Coeff[ a ] = 1.0; #if SERIAL == 1 // Serial init Serial.begin(115200); Serial.println(); Serial.println("Booting Sketch..."); #endif // read parameters from EEPROM (if it has been saved early) EEPROM.begin( EEPROM_HOST_MAX_LEN + EEPROM_OTHER_MAX_LEN ); if ( EEPROM.read( EEPROM_FLAG ) == 1 ){ EEPROMStrRead( EEPROM_HOST, host ); numberOfLEDs = EEPROM.read( EEPROM_numberOfLEDs ); mainDelayNew = EEPROM.read( EEPROM_mainDelayNew ); for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) FX_ArrNew[ a ] = EEPROM.read( EEPROM_HgradBegin + a ); changeColor = true; } else { FX_ArrNew[ PERIOD ] = DEF_NUM_LEDS; strcpy( host, (char*)hostDefURI.c_str() ); // default URI and host name } // Fast LED RGB init pinMode(ENABLE_GATE_PIN, OUTPUT); digitalWrite( ENABLE_GATE_PIN, HIGH ); delay(1000); // 2000 // sanity check delay - allows reprogramming if accidently blowing power w/leds FastLED.addLeds( LEDStripe_RGB, numberOfLEDs ); fill_solid( LEDStripe_RGB, numberOfLEDs, CRGB(0, 0, 0) ); FastLED.show(); // WiFi init if ( host == "" ) strcpy( host, (char*)hostDefURI.c_str() ); // if it was changed without parameters by mistake WiFi.hostname( host ); // WiFi.softAP(APssid, APpassword); // WiFi.mode(WIFI_AP); // WiFi.mode(WIFI_AP_STA); WiFi.mode(WIFI_STA); WiFi.setAutoReconnect( true ); WiFi.begin(ssid, password); // main events handlers if (WiFi.waitForConnectResult() == WL_CONNECTED) { MDNS.begin( host ); // get time // timeClient.setTimeOffset(0); timeClient.begin(); timeClient.update(); // start server server.begin(); MDNS.addService("http", "tcp", 80); #if SERIAL == 1 Serial.printf("Ready. Try \"curl %s/help\" to see the list of commands and parameters.\n", host); #endif server.onNotFound( []() { server.sendHeader("Connection", "close"); param = ""; // HTTPOut = ""; // no command given (see handlers of commands below) if ( server.uri() != "/" )HTTPErr += "Command '" + server.uri() + "' not exist.\n"; for ( uint8_t i=0; i < server.args(); i++ ){ paramCnt = 0; // ------------- specific parameters ----------------- // change number of leds in stripe if ( server.argName(i) == "number" ) { // TODO make it in MAIN with dimming fill_solid( LEDStripe_RGB, numberOfLEDs, CRGB(0, 0, 0) ); FastLED.show(); numberOfLEDs = server.arg("number").toInt(); FastLED.addLeds( LEDStripe_RGB, numberOfLEDs ); paramCnt++; } // color gradient (hex) if ( server.argName(i) == "gradient" ) { param = server.arg("gradient"); for ( uint8_t a = 0; a < 6; a++ ) FX_ArrNew[ H_GRAD_BEG + a ] = str2HEX( param.substring( a*2, a*2+2) ); changeColor = true; paramCnt++; } // sin/cos period (dec) if ( server.argName(i) == "period" ) { FX_ArrNew[ PERIOD ] = server.arg("period").toInt(); changeColor = true; paramCnt++; } // pattern motion parameters (dec) if ( server.argName(i) == "slidex" ) { FX_ArrNew [ PHASE_X_INC ] = (float)server.arg("slidex").toInt(); changeColor = true; paramCnt++; } if ( server.argName(i) == "slidey" ) { FX_ArrNew [ PHASE_Y_INC ] = (float)server.arg("slidey").toInt(); changeColor = true; paramCnt++; } if ( server.argName(i) == "slidez" ) { FX_ArrNew [ PHASE_Z_INC ] = (float)server.arg("slidez").toInt(); changeColor = true; paramCnt++; } // delay between frames if ( server.argName(i) == "delay" ) { mainDelayNew = server.arg("delay").toInt(); // changeColor = true; paramCnt++; } // ------------- common parameters ------------------- // debug data {Number R G B H S V} if ( server.argName(i) == "debug" ){ // || param != 0 HTTPOut += "Stripe [debug]: "; for (unsigned int i = 0; i < numberOfLEDs; i++) { color_HSV = rgb2hsv_approximate( LEDStripe_RGB[ i ] ); HTTPOut += String( i ) + "\t" + String( LEDStripe_RGB[ i ].r ) + "\t" + String( LEDStripe_RGB[ i ].g ) + "\t" + String( LEDStripe_RGB[ i ].b ) + "\t" + String( color_HSV.h ) + "\t" + String( color_HSV.s ) + "\t" + String( color_HSV.v ) + "\n"; } paramCnt++; } // set time if ( server.argName(i) == "time" ){ // || param != 0 if ( is_number( server.arg("time").c_str() ) ) { timeClient.update(); timeStamp = strtol( server.arg("time").c_str(), 0, 10 ); } else { HTTPErr += "Parameter 'time' must be specified in UNIX timestamp format.\n"; // 1631848103 } paramCnt++; } // rename host if ( server.argName(i) == "host" ) { if ( server.arg("host") == "" ) { HTTPErr += "Please specify new unit hostname.\n"; } else if ( server.arg("host").length() > EEPROM_HOST_MAX_LEN ) { HTTPErr += "Unit hostname can not exceed " + String( EEPROM_HOST_MAX_LEN ) + " symbols.\n"; } else { strcpy( host, (char*)server.arg("host").c_str() ); // host = (char *)server.arg("host").c_str(); WiFi.hostname( host ); MDNS.begin( host ); HTTPOut += "The host name has been changed to '" + String( host ) + "'. Do not forget to save current settings using the '/save' command.\n"; } paramCnt++; } // save all parameters to EEPROM if (server.argName(i) == "save" ){ EEPROM.write( EEPROM_numberOfLEDs, numberOfLEDs ); EEPROM.write( EEPROM_mainDelayNew, mainDelayNew ); for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) EEPROM.write( EEPROM_HgradBegin + a, (uint8_t)roundf( FX_Arr[ a ] ) ); EEPROMStrWrite( EEPROM_HOST, host ); EEPROM.write( EEPROM_FLAG, 1 ); // EEPROM changed EEPROM.commit(); HTTPOut += "The current settings are saved.\n"; paramCnt++; } // reset to default parameters if ( server.argName(i) == "reset" ){ numberOfLEDs = DEF_NUM_LEDS; mainDelayNew = DEF_MAIN_DELAY; for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) FX_ArrNew[ a ] = 0; FX_Arr[ PERIOD ] = DEF_NUM_LEDS; FX_ArrNew[ PERIOD ] = DEF_NUM_LEDS; strcpy( host, (char*)hostDefURI.c_str() ); WiFi.hostname( host ); server.begin(); if ( quiet > 1 ) HTTPOut += "All parameters are set to default values. Don't forget to /save them.\n"; paramCnt++; changeColor = true; } // reboot if ( server.argName(i) == "reboot" ){ FX_ArrNew[ V_GRAD_BEG ] = 0; FX_ArrNew[ V_GRAD_END ] = 0; mainDelayNew = DEF_MAIN_DELAY; if ( quiet > 1 ) { server.send_P(200, "text/plain", PSTR("Unit rebooting...\n") ); server.begin(); } paramCnt++; changeColor = true; commandReboot = true; } // quiet, level of output if ( server.argName(i) == "quiet" ) { quiet = str2HEX( server.arg("quiet") ); paramCnt++; } else { quiet = FULL_OUTPUT; } // json on if ( server.argName(i) == "json" ) { JSONon = true; paramCnt++; } if ( !paramCnt ) HTTPErr += "Parameter '" + server.argName(i) + "' not exist.\n"; } // ---------------- host default return ------------------------ if (HTTPErr != "" ) { // list of errors String Err = ""; paramCnt = 0; if (quiet > 0) { if ( JSONon ) { HTTPOut += "{["; while ( 1 ){ Err = expld( HTTPErr, paramCnt, '\n' ); if ( Err != "" ) HTTPOut += "\"" + Err + "\""; else break; if ( expld( HTTPErr, paramCnt+1, '\n' ) != "" ) HTTPOut += ","; paramCnt ++; } HTTPOut += "]}"; } else { while ( 1 ){ Err = expld( HTTPErr, paramCnt, '\n' ); if ( Err != "" ) HTTPOut += "Error: " + Err + "\n"; else break; paramCnt ++; } // HTTPOut += "See '/help' for details\n"; } } } else { // standart output if ( quiet > 1 && HTTPOut == "" ) { if ( JSONon ){ HTTPOut = "{"; HTTPOut += "\"MAC\":\"" + String( WiFi.macAddress() ) + "\",\n"; HTTPOut += "\"Host\":\"" + String( host ) + "\",\n"; HTTPOut += "\"Timestamp\":\"" + String( timeClient.getEpochTime() ) + "\",\n"; HTTPOut += "\"Target_timestamp\":\"" + String( timeStamp ) + "\",\n"; HTTPOut += "\"TNumber_of_LEDs\":\"" + String( numberOfLEDs ) + "\",\n"; HTTPOut += "\"SlideX:\":\"" + String( (int)roundf( FX_ArrNew[ PHASE_X_INC ] ) ) + "\",\n"; HTTPOut += "\"SlideY:\":\"" + String( (int)roundf( FX_ArrNew[ PHASE_Y_INC ] ) ) + "\",\n"; HTTPOut += "\"SlideZ:\":\"" + String( (int)roundf( FX_ArrNew[ PHASE_Z_INC ] ) ) + "\",\n"; HTTPOut += "\"Period:\":\"" + String( (int)roundf( FX_ArrNew[ PERIOD ] ) ) + "\",\n"; HTTPOut += "\"Delay:\":\"" + String( mainDelayNew ) + "\",\n"; HTTPOut += "\"Gradient_HSV:\":\""; for ( uint8_t a = 0; a < 6 ; a++ ) HTTPOut += String( printNum( FX_ArrNew[ a ], 16, ' ') ); HTTPOut += "\",\n"; HTTPOut += "}"; } else { HTTPOut = "MAC:" + String( WiFi.macAddress() ) + "\n"; HTTPOut += "Host:" + String( host ) + "\n"; HTTPOut += "Timestamp:" + String( timeClient.getEpochTime() ) + "\n"; HTTPOut += "Target timestamp:" + String( timeStamp ) + "\n"; HTTPOut += "Number of LEDs:" + String( numberOfLEDs ) + "\n"; HTTPOut += "Slide X:" + String( (int)roundf( FX_ArrNew[ PHASE_X_INC ] ) ) + "\n"; HTTPOut += "Slide Y:" + String( (int)roundf( FX_ArrNew[ PHASE_Y_INC ] ) ) + "\n"; HTTPOut += "Slide Z:" + String( (int)roundf( FX_ArrNew[ PHASE_Z_INC ] ) ) + "\n"; HTTPOut += "Period:" + String( (int)roundf( FX_ArrNew[ PERIOD ] ) ) + "\n"; HTTPOut += "Delay:" + String( mainDelayNew ) + "\n"; HTTPOut += "Gradient (HSV):"; for ( uint8_t a = 0; a < 6 ; a++ ) HTTPOut += String( printNum( roundf( FX_ArrNew[ a ] ), 16, ' ') ) + " "; HTTPOut += "\n"; } } } server.send(200, "text/plain", HTTPOut ); HTTPOut = ""; HTTPErr = ""; JSONon = 0; }); // ui interface TODO xml inteface server.on("/ui", HTTP_GET, []() { HTTPOut = "User interface. Not yet implemented.\n"; server.send(200, "text/plain", HTTPOut); }); // help server.on("/help", HTTP_GET, []() { HTTPOut = "Interplay Medium ESP8266 Serial LED Controller. Version: " + String(VERSION) + "\n"; HTTPOut += "Created by Dmitry Shalnov (c) 2017. License GPLv3+: GNU GPL version 3 or later . \n\n"; HTTPOut += "Parameters:\n"; HTTPOut += " ?number= Number of LEDs in stripe (max MAX_NUM_LEDS)\n"; HTTPOut += " ?gradient= Start and end colors of gradient (hex, 24bit/color in HLS format, e.g. 001122FFEECC)\n"; HTTPOut += " ?period= Sine period (number of leds)\n"; HTTPOut += " ?delay= Delay of state changing, integer\n"; HTTPOut += " ?slidex= Step of X shift per cycle, signed integer\n"; HTTPOut += " ?slidey= Step of Y shift per cycle, signed integer\n"; HTTPOut += " ?slidez= Step of Z shift per cycle, signed integer\n"; HTTPOut += " ?time= Target timestamp (when the changes will take effect, good for synch), decimal UNIX Timestamp\n\n"; HTTPOut += " ?host= Rename the unit\n"; HTTPOut += " ?quiet= Level of output (0/empty = no output, 1 = OK/Error)\n\n"; HTTPOut += "Commands:\n"; HTTPOut += " ?save Save current settings\n"; HTTPOut += " ?reset Reset to initial settings\n"; HTTPOut += " ?reboot Reboot unit\n"; HTTPOut += " \n"; HTTPOut += " /ui Output in XML UI format\n"; HTTPOut += " /json Output in JSON format\n"; HTTPOut += " /update Wireless update of firmware (see example below)\n"; HTTPOut += " /help This help\n\n"; HTTPOut += "Usage: curl " + String(host) + "?=\n"; HTTPOut += " curl " + String(host) + "/\n"; HTTPOut += "Examples: curl \"" + String(host) + "?gradient=ffffffddff00&period=30\" \n"; HTTPOut += " curl -F image=@firmware.bin " + String(host) + "/update \n"; server.sendHeader("Connection", "close"); server.send( 200, "text/plain", HTTPOut ); }); // firmware update server.on("/update", HTTP_POST, []() { server.sendHeader("Connection", "close"); server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); ESP.restart(); }, []() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { #if SERIAL == 1 Serial.setDebugOutput(true); #endif WiFiUDP::stopAll(); #if SERIAL == 1 Serial.printf("Update: %s\n", upload.filename.c_str()); #endif uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; if (!Update.begin(maxSketchSpace)) { //start with max available size #if SERIAL == 1 Update.printError(Serial); #endif } } else if (upload.status == UPLOAD_FILE_WRITE) { if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { #if SERIAL == 1 Update.printError(Serial); #endif } } else if (upload.status == UPLOAD_FILE_END) { if (Update.end(true)) { //true to set the size to the current progress server.sendHeader("Connection", "close"); server.send_P(200, "text/plain", PSTR("Success. Please wait until device replace firmware and boot up...\n") ); #if SERIAL == 1 Serial.printf("Update Success: %u\nRebooting....\n", upload.totalSize); #endif } else { server.sendHeader("Connection", "close"); server.send_P(200, "text/plain", PSTR("Something went wrong. Please reset device and try again.\n") ); #if SERIAL == 1 Update.printError(Serial); #endif } #if SERIAL == 1 Serial.setDebugOutput(false); #endif } yield(); }); } else { #if SERIAL == 1 Serial.println("WiFi Failed"); #endif } } // ----------------------------------------------------- // ------------------- MAIN ---------------------------- // ----------------------------------------------------- // uint8_t rebootColorsCnt = 0; void loop(void) { server.handleClient(); MDNS.update(); // reconnect if ( WiFi.status() != WL_CONNECTED ) { #if SERIAL == 1 Serial.printf("."); #endif if (WiFi.waitForConnectResult() == WL_CONNECTED) { #if SERIAL == 1 Serial.println("Reconnected"); #endif MDNS.begin( host ); timeClient.begin(); timeClient.update(); } } if ( changeColor ) { // set float coefficents for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) { FX_Coeff[ H_GRAD_BEG + a ] = (float)FX_ArrNew[ H_GRAD_BEG + a ] - FX_Arr[ H_GRAD_BEG + a ]; if ( abs( FX_Coeff[ H_GRAD_BEG + a ] ) > FX_CoeffMax ) FX_CoeffMax = abs( FX_Coeff[ H_GRAD_BEG + a ] ); } for ( uint8_t a = 0; a < 6; a++ ) FX_Coeff[ H_GRAD_BEG + a ] = FX_Coeff[ H_GRAD_BEG + a ] / FX_CoeffMax; changeColor = false; #if SERIAL == 1 Serial.printf("FX_ArrNew: "); for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) Serial.printf("%f ", FX_ArrNew[ a ] ); Serial.printf("\n"); Serial.printf("FX_Coeff: "); for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) Serial.printf("%f ", FX_Coeff[ a ] ); Serial.printf("\n"); #endif } else { if ( ++mainDelay == mainDelayNew /* && FX_Delay != 0 */ ) { #if SERIAL == 1 // Serial.printf("FX_Arr: "); // for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) Serial.printf("%f ", FX_Arr[ a ] ); // Serial.printf("\n"); // Serial.printf("Phase: %f %f %f\n", Phase[ PHASE_X ], Phase[ PHASE_Y ], Phase[ PHASE_Z ] ); #endif mainDelay = 0; // fade out and reboot if ( commandReboot && roundf( FX_Arr[ V_GRAD_BEG ] ) == 0 && roundf( FX_Arr[ V_GRAD_END ]) == 0 ) { commandReboot = false; #if SERIAL == 1 Serial.printf("Reboot......\n"); #endif ESP.restart(); // Serial.printf("Reboot.2...\n"); } if ( timeClient.getEpochTime() > timeStamp ) { // change color for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) { if ( FX_ArrNew [ a ] != roundf( FX_Arr[ a ] ) ) { FX_Arr[ a ] += FX_Coeff[ a ]; } else FX_Arr[ a ] = (float)FX_ArrNew [ a ]; } // change phase for ( uint8_t a = 0; a < 3; a++ ) { if ( FX_Arr[ PHASE_X_INC + a ] == 0 ) { // shift phase to 0 after slide. = 0 if ( (uint8_t)round( Phase[ a ] ) != 0 ) Phase[ a ] -= FX_Coeff[ PHASE_X_INC + a ]/100; else Phase[ a ] = 0; } else { // routine shift phase Phase[ a ] += FX_Arr[ PHASE_X_INC + a ]/100; } } } if ( FX_Arr[ PERIOD ] == 0 ) FX_Arr[ PERIOD ] = 1; FX_Gradient_RT( roundf( FX_Arr[ H_GRAD_BEG ] ), roundf( FX_Arr[ S_GRAD_BEG ] ), roundf( FX_Arr[ V_GRAD_BEG ] ), roundf( FX_Arr[ H_GRAD_END ] ), roundf( FX_Arr[ S_GRAD_END ] ), roundf( FX_Arr[ V_GRAD_END ] ), roundf( FX_Arr[ PERIOD ] ), roundf( Phase[ PHASE_X ] ), roundf( Phase[ PHASE_Y ] ), roundf( Phase[ PHASE_Z ] ) ); FastLED.show(); delay(3); } } }