/* * RGB strip controller for ESP8266 * v1.2 - 2016.11.29 * techfactory.hu */ #include #include #include "FS.h" #include #include //debug mode #define TF_DEBUG 1 #define MAX_TCP_CLIENTS 3 //#define MAX_UDP_CLIENTS 3 //Enable IR receiver //#define IRRECVPIN 5 //RGB strips count #define RGBSCOUNT 2 //Max program lines #define PROGCount 20 /* * VARIABLES * --------- */ //*** AP & Device name *** String APssid = "TFRGB-DEVICE"; String APpassword = "A1234B5678"; String DName = "RGB-DEVICE"; //Disable AP mode if STA mode is active bool DisableAPSTA = false; //*** Others *** String DTimeServer = "http://techfactory.hu/esp/time/"; String DHWAddr = ""; String DCFGServer = ""; const String FW_VER = "tfrgb-1.2"; const unsigned int TCPLocalPort = 9000; const String UDPSearchString = "TECHFACTORYESPBRCAST"; uint8_t int_error = 0; bool prog_tcpdbg = false; bool rgbprog_enabled = false; //***APmode variables *** IPAddress APlocalIP(192, 168, 10, 1); IPAddress APgateway(192, 168, 10, 1); IPAddress APsubnet(255, 255, 255, 0); bool SoftApRunning = false; //***STAmode variables *** unsigned long wifista_last_check = 0; bool STAConnected = false; //***UDP Variables*** const unsigned int UDPLocalPort = 42910; struct UDPClientData { IPAddress ip; unsigned long lastaction; }; WiFiUDP Udp; char UDPpacket[255]; //UDPClientData UDPClients[MAX_UDP_CLIENTS]; //***TCP Server variables*** WiFiServer tcpServer(TCPLocalPort); WiFiClient tcpServerClients[MAX_TCP_CLIENTS]; String incomingPacketTcp[MAX_TCP_CLIENTS]; HTTPClient http; t_httpUpdate_return update_fw_status; //*** IR Remote *** #if defined(IRRECVPIN) && (IRRECVPIN > 0) #include IRrecv IR(IRRECVPIN); decode_results IRResults; String IRLastCmd; #endif //*** PIN Settings *** struct RGBStrip { String name; byte redPin; byte greenPin; byte bluePin; }; const RGBStrip RGBStrips[RGBSCOUNT] = { {"RGB#1", 13, 12, 16}, {"RGB#2", 15, 14, 4} }; //*** RGB control stuff *** struct RGB { byte r; byte g; byte b; }; struct PROG_STATUS { byte mode; //0=off, 1=simple_color, 2=simple_color+fading, 3=program_mode, 4=user_program, 5=network byte progid; //internal program id, or user program id bool once; unsigned long chgt; unsigned long holdt; byte repeat; byte active; byte speed; byte bright; RGB color; byte _last; byte repcount; byte lastrep; bool lockTiming; //true=all strips use the first strip timing, false=strips has separated timing bool sameProg; //true=all strips get the first strip color program, false=strips has separated program }; struct PROG { byte mode; //0=empty, 1=off, 2=on, 3=fading int chgTime; // change time (startColor to stopColor) in ms int holdTime; // hold time in ms (before and after fading or for a single color) byte randMode; // 0=off, 1-255 = random mode : this program runs randomly chance: rand(0, RANDVAL) == 1 int repeat; // -255 - 0 -255 x repeat this line (negative numbers enables random repeat rand(0,abs(randval)) == 1) RGB startColor; RGB endColor; }; int PWMFreq = 4000; //*** Pre defined internal programs (FLASH,FADE,STROBE) *** PROG progs[RGBSCOUNT][PROGCount] = {}; PROG_STATUS pstatus[RGBSCOUNT] = {}; PROG userprogs[RGBSCOUNT][PROGCount] = {}; String userprog_name = ""; bool userprog_locktiming = false; bool userprog_sameprog = false; const PROG staticProgs[3][7] PROGMEM = { //0=flash { {2, 500, 2000, 0, 0, {255,0,0}, {255,0,0}}, {1, 0, 1000, 0, 0, {0,0,0}, {0,0,0}}, {2, 500, 2000, 0, 0, {0,255,0}, {0,255,0}}, {1, 0, 1000, 0, 0, {0,0,0}, {0,0,0}}, {2, 500, 2000, 0, 0, {0,0,255}, {0,0,255}}, {1, 0, 1000, 0, 0, {0,0,0}, {0,0,0}} }, //1=fade { {3, 2000,5000, 0, 0, {255,0,0}, {255,255,0}}, {3, 2000,5000, 0, 0, {255,255,0},{0,255,0}}, {3, 2000,5000, 0, 0, {0,255,0}, {0,255,255}}, {3, 2000,5000, 0, 0, {0,255,255},{0,0,255}}, {3, 2000,5000, 0, 0, {0,0,255}, {255,0,255}}, {3, 2000,5000, 0, 0, {255,0,255},{255,0,0}} }, //2=strobe { {3, 2000, 500, 0, 0, {255,0,0}, {0,0,0}}, {3, 2000, 500, 0, 0, {255,255,0}, {0,0,0}}, {3, 2000, 500, 0, 0, {0,255,0}, {0,0,0}}, {3, 2000, 500, 0, 0, {0,255,255}, {0,0,0}}, {3, 2000, 500, 0, 0, {0,0,255}, {0,0,0}}, {3, 2000, 500, 0, 0, {255,0,255}, {0,0,0}} } }; //***TIME variables*** struct STIME { byte d; byte h; byte m; byte s; unsigned long ts; }; struct STIMER { byte d; //0: off, 1-7 days from monday to sunday, 8 - every day, 9 - every weekday, 10 - every weekend, 10 > = every (x-10) day (11 = every day,12 = every second day,13 = every third day, ect...) byte h; //0-23 hour, byte m; byte prog_mode; byte prog_id; RGB color; int hold; int fade; unsigned long lastrun; }; STIMER timers[10] {}; bool timerEngine = false; unsigned long timestamp = 0; const int updateInterval = 2160000; //every 6. hour unsigned long lasttimeupdate = 0; unsigned long timererror = 0; unsigned long lasttimerrun = 0; /* * PROGRAM FUNCTIONS * ----------------- */ //SETUP function void setup() { Serial.begin(115200); //fire up the serial port #ifdef TF_DEBUG Serial.println("Starting up..."); #endif resetPins(); //get HW nr GetDeviceHWAddr(); //setup pstatus resetProg(-1); #if defined(IRRECVPIN) && (IRRECVPIN > 0) #ifdef TF_DEBUG Serial.println("IR SENSOR: ON"); #endif IR.enableIRIn(); #endif if (!SPIFFS.begin()){ #ifdef TF_DEBUG Serial.println("FS not ready..."); #endif } //Load AP config if (!loadConfig("config.dat")) { // writeConfig(); #ifdef TF_DEBUG Serial.println("[LOADCFG] No config.dat found"); #endif } if (PWMFreq > 0) { analogWriteFreq(PWMFreq); } //Init AP WiFi.mode(WIFI_AP_STA); SoftAPConnect(); //Load user program loadUserProgram(); //Timers if (timerEngine) { if (!loadConfig("timers.dat")) { #ifdef TF_DEBUG Serial.println("[TIMER] No saved timers."); #endif } } //Communication (UDP/TCP) Udp.begin(UDPLocalPort); #ifdef TF_DEBUG Serial.print("[UDP] local UDP port: "); Serial.println(UDPLocalPort); #endif //Start the TCP server tcpServer.begin(); tcpServer.setNoDelay(true); //Start //Start(0); } //MAIN LOOP void loop() { #if defined(IRRECVPIN) && (IRRECVPIN > 0) if (IR.decode(&IRResults)) { IRCommand(); IR.resume(); } #endif if (Serial.available()>0) { String rd = Serial.readStringUntil('\n'); char c[rd.length()]; rd.toCharArray(c, rd.length()); String response = handleCommands(c,true); if (response.length()>0) { Serial.println(">"+response); } } //Handle udp packets UDPWorker(); //Handle tcp connections TCPServerWorker(); //checking wifi sta CheckWifiSTA(false); //update network time if needed updateTime(false); //run timers if needed runTimers(); if (rgbprog_enabled) { RGBLedLoop(); } } /* * RGB Led control functions */ void ColorMode(RGB color, RGB ecolor, int fade = 0, int hold = 0, int once = 0, int strip = -1) { rgbprog_enabled = false; for (int i = 0; i< RGBSCOUNT; i++) { if (i == strip || strip == -1) { pstatus[i].color = color; pstatus[i].chgt = 0; pstatus[i].holdt = 0; pstatus[i]._last = 255; pstatus[i].once = once == 1 ? true : false; if (fade>0) { //simple color with fading pstatus[i].mode = 2; pstatus[i].lockTiming = false; pstatus[0].sameProg = false; progs[i][0].mode = 3; progs[i][0].startColor = color; progs[i][0].endColor = ecolor; progs[i][0].holdTime = hold; progs[i][0].chgTime = fade; progs[i][0].repeat = 0; }else { //simple color mode pstatus[i].mode = 1; pstatus[i].lockTiming = false; pstatus[i].sameProg = false; progs[i][0].mode = 0; } for (int x=1;x 200) { pstatus[i].speed = 200; } } } #ifdef TF_DEBUG Serial.println("[SPEED:"+String(i)+"] "+String(pstatus[i].speed)); #endif } } void _RGBLedLoopHelper(PROG p, int strip, int active, bool sndall = false, unsigned long ms = 0) { int randN = 0; int mode = p.mode; unsigned long currTime = ms==0?millis():ms; if (pstatus[strip].chgt == 0 && p.randMode > 0) { randN = random(0,(int)p.randMode+1); if (randN!=1) { mode = 0; } } //off/on while holdtime if (mode == 1 || mode == 2) { if (pstatus[strip].holdt == 0) { pstatus[strip].holdt = currTime + floor((float)p.holdTime * (float)pstatus[strip].speed / 100); SetRGBColor(mode==1?RGB{0,0,0}:p.startColor,sndall?-1:strip); }else if(pstatus[strip].holdt > millis()) { return; }else { pstatus[strip].holdt = 0; NextRGBProgram(strip); } //fading startColors to endColors }else if(mode == 3) { //start "animation" if (pstatus[strip].chgt==0) { SetRGBColor(p.startColor,sndall ? -1 :strip); pstatus[strip].holdt = 0; if (p.holdTime > 0) { pstatus[strip].chgt = 1; pstatus[strip].holdt = currTime+floor((float)p.holdTime * (float)pstatus[strip].speed / 100); }else { pstatus[strip].chgt = currTime; } } else if(pstatus[strip].chgt==1) { if ( pstatus[strip].holdt < currTime) { pstatus[strip].holdt = 0; pstatus[strip].chgt = currTime; } }else { int theChgTime = floor(p.chgTime * pstatus[strip].speed / 100); if ( p.chgTime > 0 && ((unsigned long) currTime - pstatus[strip].chgt) <= theChgTime) { FadeRGBColor(p.startColor, p.endColor, sndall ? -1 : strip, theChgTime); }else { if (p.holdTime >0 && pstatus[strip].holdt == 0) { SetRGBColor(p.endColor,sndall?-1:strip); pstatus[strip].holdt = currTime+floor((float)p.holdTime * (float)pstatus[strip].speed / 100); }else if(pstatus[strip].holdt > currTime){ }else { pstatus[strip].holdt = 0; pstatus[strip].chgt = 0; if (p.repeat < 0 && pstatus[strip].repcount == 0) { pstatus[strip].repcount = random(0,(int) abs(p.repeat)+1); if (pstatus[strip].repcount == 0) { NextRGBProgram(strip); } }else if(p.repeat < 0 && pstatus[strip].repcount > 0) { if (pstatus[strip].lastrep < pstatus[strip].repcount) { pstatus[strip].lastrep++; }else { NextRGBProgram(strip); } }else if (p.repeat > 0) { if (pstatus[strip].lastrep < p.repeat) { pstatus[strip].lastrep++; }else { NextRGBProgram(strip); } }else { NextRGBProgram(strip); } } } } }else { NextRGBProgram(strip); } } void RGBLedLoop() { unsigned long currTime = millis(); //prog status for (int strip=0; strip < RGBSCOUNT; strip++) { //program if (pstatus[strip].mode == 2 || pstatus[strip].mode == 3 || pstatus[strip].mode == 4){ int ac = pstatus[strip].active; PROG p = progs[strip][ac]; if (pstatus[0].sameProg == true) { _RGBLedLoopHelper(p,0,ac,true,currTime); for (int x=1; x0) {snd += ","; } snd += pstatus[i].active; if (pstatus[0].sameProg) { snd += ","+pstatus[0].active; break;} } SendToTCPClients(snd); } } int TranslateColorToESP (int c) { float d2 = ((float)c/(float)255); return constrain(ceil(d2 * 1023), 0, 1023); } void FadeRGBColor(RGB startc, RGB endc, int strip, int chgTime) { float percent = (float)(millis() - pstatus[strip].chgt) / chgTime; int r = _FadeRGBHelper(TranslateColorToESP(startc.r), TranslateColorToESP(endc.r), percent); int g = _FadeRGBHelper(TranslateColorToESP(startc.g), TranslateColorToESP(endc.g), percent); int b = _FadeRGBHelper(TranslateColorToESP(startc.b), TranslateColorToESP(endc.b), percent); if (strip>-1) { analogWrite(RGBStrips[strip].redPin, r); analogWrite(RGBStrips[strip].greenPin, g); analogWrite(RGBStrips[strip].bluePin, b); }else { for (int i=0;i 100) { pstatus[i].bright = 100; } } #ifdef TF_DEBUG Serial.println("[BRIGHT:"+String(i)+"] "+String(pstatus[i].bright)); #endif //simple_color if (pstatus[i].mode == 1) { int r = constrain(floor(pstatus[i].color.r * pstatus[i].bright / 100),0,255); int g = constrain(floor(pstatus[i].color.g * pstatus[i].bright / 100),0,255); int b = constrain(floor(pstatus[i].color.b * pstatus[i].bright / 100),0,255); SetRGBColor(RGB{r,g,b},i); } } } } } int countUserProgram() { int c = 0; for (int s=0; s 0) { c++; }else { return c; } } } return c; } void switchInternalProgram(int pn, int strip) { for (int i=0; i<6; i++) { int mode = pgm_read_byte(&staticProgs[pn][i].mode); for (int s=0; s 0 && br == false) { /*progs[s][i] = PROG{}; progs[s][i].mode = userprogs[s][i].mode; progs[s][i].chgTime = userprogs[s][i].chgTime; progs[s][i].holdTime = userprogs[s][i].holdTime; progs[s][i].randMode = userprogs[s][i].randMode; progs[s][i].repeat = userprogs[s][i].repeat; progs[s][i].startColor.r = userprogs[s][i].startColor.r; progs[s][i].startColor.g = userprogs[s][i].startColor.g; progs[s][i].startColor.b = userprogs[s][i].startColor.b; progs[s][i].endColor.r = userprogs[s][i].endColor.r; progs[s][i].endColor.g = userprogs[s][i].endColor.g; progs[s][i].endColor.b = userprogs[s][i].endColor.b;*/ progs[s][i] = userprogs[s][i]; pc++; }else { br=true; progs[s][i] = PROG{}; } } pstatus[s].mode = 4; pstatus[s].progid = 0; pstatus[s].lockTiming = userprog_locktiming; pstatus[s].sameProg = userprog_sameprog; } } if (pc==0) { return false; } return true; } bool loadUserProgram() { File f = OpenConfigFile("/userprog.dat","r"); if (!f) { return false; } String line; while (line = f.readStringUntil('\n')) { if (line != NULL) { char *seq = strtok(&line[0],"="); if (seq != NULL) { char *command = seq; seq = strtok(NULL,"="); char *value = seq; _parseUserProgram(command, value); } }else { break; } } return true; } bool _parseUserProgram(char* cmd, char* value) { char *seq; //userprogram data = [LOCKTIMING],[SAMEPROG],[NAME] if (String(cmd) == "PD" || String(cmd) == "+PD") { seq = strtok(value,","); if (seq != NULL) { userprog_locktiming = atoi(seq) == 1 ? true : false; seq = strtok(NULL,","); userprog_sameprog = atoi(seq) == 1 ? true : false; seq = strtok(NULL,","); userprog_name = removeSpecialChars(String(seq)); } return true; } else if (String(cmd) == "PL" || String(cmd) == "+PL") { userprog_locktiming = String(value)=="1"?true:false; return true; } else if (String(cmd) == "PDBG" || String(cmd) == "+PDBG") { for (int s=0; s 0) { Serial.print("#"+String(i)+":"); Serial.print(String(userprogs[s][i].chgTime)+","); Serial.print(String(userprogs[s][i].holdTime)+","); Serial.print(String(userprogs[s][i].randMode)+","); Serial.print(String(userprogs[s][i].repeat)+",["); Serial.print(String(userprogs[s][i].startColor.r)+","); Serial.print(String(userprogs[s][i].startColor.g)+","); Serial.print(String(userprogs[s][i].startColor.b)+"],["); Serial.print(String(userprogs[s][i].endColor.r)+","); Serial.print(String(userprogs[s][i].endColor.g)+","); Serial.println(String(userprogs[s][i].endColor.b)+"]"); } } } } else if (String(cmd[0]) == "P" || (String(cmd[0]) == "+" && String(cmd[1]) == "P")) { int stin = String(cmd[0]) == "P" ? 1 : 2; int index = String(cmd[stin]).toInt(); if (index < 0 || index >= PROGCount) { return false; } int strip = -1; PROG tmpProg = {}; stin++; if (cmd[stin] != NULL && String(cmd[stin]) == ":") { stin++; if (cmd[stin] != NULL) { strip = String(cmd[stin]).toInt(); if (strip < 0 || strip >= RGBSCOUNT) { strip = -1; } } } seq = strtok(value,","); if (seq != NULL) { tmpProg.mode = atoi(seq); }else { return false; } if (tmpProg.mode > 0) { seq = strtok(NULL,","); if (seq != NULL) {tmpProg.chgTime = atoi(seq);}else { return false; } seq = strtok(NULL,","); if (seq != NULL) {tmpProg.holdTime = atoi(seq);}else { return false; } seq = strtok(NULL,","); if (seq != NULL) {tmpProg.randMode = atoi(seq);}else { return false; } seq = strtok(NULL,","); if (seq != NULL) { tmpProg.repeat = atoi(seq);}else { return false; } int r,g,b = 0; seq = strtok(NULL,","); if (seq != NULL) {r = atoi(seq);}else { return false; } seq = strtok(NULL,","); if (seq != NULL) {g = atoi(seq);}else { return false; } seq = strtok(NULL,","); if (seq != NULL) {b = atoi(seq);}else { return false; } if (checkRGB(r,g,b)) { tmpProg.startColor = RGB{r,g,b}; }else { tmpProg.mode = 0; } seq = strtok(NULL,","); if (seq != NULL) { r = atoi(seq);}else { return false; } seq = strtok(NULL,","); if (seq != NULL) {g = atoi(seq);}else { return false; } seq = strtok(NULL,","); if (seq != NULL) {b = atoi(seq);}else { return false; } if (checkRGB(r,g,b)) { tmpProg.endColor = RGB{r,g,b}; }else { tmpProg.mode = 0; } } if (strip != -1) { userprogs[strip][index] = tmpProg; }else { for (int i=0; i0) { File f = OpenConfigFile("/userprog.dat","w"); if (!f) { return false; } f.print("PN="+userprog_name+"\n"); f.print("PD="); if (userprog_locktiming) { f.print("1,"); } else { f.print("0,"); } if (userprog_sameprog) { f.print("1,"); } else { f.print("0,"); } f.print(userprog_name+"\n"); for (int s=0; s < RGBSCOUNT; s++) { for (int i = 0; i< PROGCount; i++) { if (userprogs[s][i].mode > 0) { String progLine = "P"+String(i)+":"+String(s)+"="; progLine += String(userprogs[s][i].mode)+","; progLine += String(userprogs[s][i].chgTime)+","; progLine += String(userprogs[s][i].holdTime)+","; progLine += String(userprogs[s][i].randMode)+","; progLine += String(userprogs[s][i].repeat)+","; progLine += String(userprogs[s][i].startColor.r)+","+String(userprogs[s][i].startColor.g)+","+String(userprogs[s][i].startColor.b)+","; progLine += String(userprogs[s][i].endColor.r)+","+String(userprogs[s][i].endColor.g)+","+String(userprogs[s][i].endColor.b); f.print(progLine+"\n"); } } } f.close(); return true; } return false; } void Start(int prog_mode, int prog_id = 0, int strip = -1) { rgbprog_enabled = false; resetProg(strip); //auto start if (prog_mode == 0) { if (countUserProgram() > 0) { prog_mode = 3; }else { prog_mode = 1; } } if(prog_mode == 1) { switchInternalProgram(prog_id, strip); }else if (prog_mode == 3) { switchUserProgram(-1); } rgbprog_enabled = true; } void Stop(int strip = -1) { if (strip == -1) { rgbprog_enabled = false; } resetProg(strip); SetRGBColor(RGB{0,0,0},strip); prog_tcpdbg = false; } /* * Wifi functions * AP + STA, Wifi AP scan ... */ void SoftAPConnect() { if (SoftApRunning) { return; } #ifdef TF_DEBUG Serial.print("Soft-AP configuration ... "); #endif if (WiFi.softAPConfig(APlocalIP, APgateway, APsubnet)) { if (WiFi.softAP(APssid.c_str(), APpassword.c_str(), 8, 0)) { SoftApRunning = true; #ifdef TF_DEBUG Serial.println(WiFi.softAPIP()); #endif }else { //setError(1); #ifdef TF_DEBUG Serial.println("Failed to set SID/PWD"); #endif } }else { //setError(1); #ifdef TF_DEBUG Serial.println("Failed to set IP"); #endif } } void SoftApDisconnect() { if (SoftApRunning) { #ifdef TF_DEBUG Serial.println("Disconnecting softAp"); #endif SoftApRunning = false; WiFi.softAPdisconnect(true); } } void STAConnect(String STAssid, String STApwd) { WiFi.disconnect(); #ifdef TF_DEBUG Serial.println("Station connecting to "+STAssid+"/"+STApwd); #endif //WiFi.begin(STAssid,STApassword); WiFi.begin(STAssid.c_str(), STApwd.c_str()); //WiFi.config(STAlocal_IP, STAgateway, STAsubnet); } bool CheckWifiSTA(bool wait) { if (WiFi.SSID()) { if (!wait) { if (wifista_last_check < millis()) { wifista_last_check = millis()+10000; if (WiFi.status() != WL_CONNECTED) { SoftAPConnect(); }else if(WiFi.status() == WL_CONNECTED) { if (DisableAPSTA) { SoftApDisconnect(); } } } return true; }else { unsigned long start = millis()+10000; wifista_last_check = millis()+20000; while (WiFi.status() != WL_CONNECTED) { if (start < millis()) { //setError(2); return false; break; } delay(10); } return true; } } } String ScanWifiNetworks() { int n = WiFi.scanNetworks(); String json = "["; if (n == 0) { return "{}"; } else { for (int i = 0; i < n; ++i) { // Print SSID and RSSI for each network found json += "{\"ssid\":\"" +String(WiFi.SSID(i))+ "\",\"rssi\":\""+String(WiFi.RSSI(i))+"\",\"encr\":" + ((WiFi.encryptionType(i) == ENC_TYPE_NONE)?"false":"true") + "}"; if (i+1 < n) { json += ","; } delay(10); } json += "]"; } return json; } /* * NETWORKING * Udp & TCP functions */ void TCPServerWorker() { if (tcpServer.hasClient()){ bool found = false; for(int i = 0; i < MAX_TCP_CLIENTS; i++){ //find free/disconnected spot if (!tcpServerClients[i] || !tcpServerClients[i].connected()){ if(tcpServerClients[i]) tcpServerClients[i].stop(); tcpServerClients[i] = tcpServer.available(); #ifdef TF_DEBUG Serial.print("[TCP] New tcp client: "); Serial.println(i); #endif found = true; break; } } if (!found) { //no free/disconnected spot so reject WiFiClient serverClient = tcpServer.available(); serverClient.println("-err=No free slot"); serverClient.stop(); } } //check clients for data for(int i = 0; i < MAX_TCP_CLIENTS; i++){ if (tcpServerClients[i] && tcpServerClients[i].connected()){ int charsWaiting = tcpServerClients[i].available(); if(charsWaiting>0){ while (charsWaiting>0) { char c = tcpServerClients[i].read(); if (incomingPacketTcp[i].length() >= 200) { incomingPacketTcp[i] = ""; charsWaiting=0; }else { if (c != 0x0a && c != 0x0d) { incomingPacketTcp[i] += c; }else { if (incomingPacketTcp[i].length()>0) { String response = handleCommandsFromString(incomingPacketTcp[i],false); if (response.length()>0) { tcpServerClients[i].println(response); } incomingPacketTcp[i] = ""; } } charsWaiting--; } } } }else { if(tcpServerClients[i]) tcpServerClients[i].stop(); } } } void SendToTCPClients(String msg) { for (int i = 0; i < MAX_TCP_CLIENTS; i++){ if (tcpServerClients[i] && tcpServerClients[i].connected()){ tcpServerClients[i].println(msg); } } } void SendTCPStatus() { SendToTCPClients("+S:"+GetDeviceStatus()); } //UDP int GetUDPSlot() { /*unsigned long minval = millis(); int minIndex = 0; for (int i = 0; i < MAX_UDP_CLIENTS; i++) { if (!UDPClients[i].lastaction) { return i; } else { if (UDPClients[i].lastaction < minval) { minIndex = i; minval = UDPClients[i].lastaction; } } } //10s timeout check on min value if (minval > millis()-100000) { return -1; } return minIndex;*/ } String GetDeviceInfo() { String json = "{"; json += "\"fw\":\""+String(FW_VER)+"\",\"hwnr\":\""+DHWAddr+"\",\"devs\":["; for (int i=0; i 0 && len < 255) { incomingPacket[len] = 0; }else if (len >= 255) { return; } #ifdef TF_DEBUG //Serial.printf("[UDP] packet contents: %s\n", incomingPacket); #endif //Send device info if (String(incomingPacket) == UDPSearchString) { //disabled /* for (int i = 0; i < MAX_UDP_CLIENTS; i++) { if (UDPClients[i].ip == Udp.remoteIP()) { knownClient = true; UDPClients[i].lastaction = millis(); break; } } if (!knownClient) { int slotindex = GetUDPSlot(); //currently we have no free slot ... if (slotindex == -1) { return; } UDPClients[slotindex] = UDPClientData {Udp.remoteIP(), millis()}; }*/ Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); Udp.print(GetDeviceInfo()); Udp.endPacket(); }else { /* for (int i = 0; i < MAX_UDP_CLIENTS; i++) { if (UDPClients[i].ip == Udp.remoteIP()) {*/ String response = handleCommands(incomingPacket,true); if (response.length()>0) { Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); Udp.print(response); Udp.endPacket(); } // break; /* } }*/ } } } /* * TIMER functions */ String getTimers(int md = 0) { String tm; String sep; if (md==0) { sep = "|"; } else { sep = ","; } for (int i = 0; i < 10; ++i) { // Print SSID and RSSI for each network found if (timers[i].d > 0) { if (md==0) { tm += (tm.length()>0)?",":""; }else { tm += "T"+String(i)+"="; } tm += String(timers[i].d)+sep+String(timers[i].h)+sep+String(timers[i].m)+sep+String(timers[i].prog_mode)+sep; if (timers[i].prog_mode == 1) { tm += timers[i].prog_id; }else if(timers[i].prog_mode == 2) { tm += String(timers[i].color.r)+sep+String(timers[i].color.g)+sep+String(timers[i].color.b)+sep+String(timers[i].fade)+sep+String(timers[i].hold); }else { tm += "0"; } if (md==1) { tm += "\n"; } } } return tm; } STIME getTime() { if (timerEngine) { if (lasttimeupdate > 0 && timestamp > 0) { unsigned long currts = (timestamp * 1000) + (millis()-lasttimeupdate); unsigned long remainder = 0; int currday = (int) floor( currts / 86400000); remainder = currts % 86400000; int currhour = (int)floor(remainder / 3600000); remainder -= currhour*3600000; int currmin = (int)floor (remainder / 60000); remainder -= currmin*60000; int currsec = (int)floor(remainder/1000); STIME currTime = {d:(byte)currday, h:(byte)currhour, m:(byte)currmin, s:(byte)currsec, ts:currts}; #ifdef TF_DEBUG Serial.println("[TIME] Day: "+String(currday)+" - "+String(currhour)+":"+String(currmin)+":"+String(currsec)); #endif return currTime; }else { #ifdef TF_DEBUG Serial.println("[TIME] Cant get timestamp"); #endif return STIME {}; } } } void sleepTimer() { //60s delay if (timerEngine) { lasttimerrun = millis() + 60000; } } void runTimers() { if (timerEngine) { if (lasttimerrun + 15000 > millis()) { return; } lasttimerrun = millis(); STIME ctime = getTime(); for (int i = 0; i < 10; i++) { if (timers[i].d > 0) { /* #ifdef TF_DEBUG Serial.println("Checking timer "+String(i)+" ... "+String(timers[i].d)+"/"+String(timers[i].h)+"/"+String(timers[i].m)); #endif*/ if (timers[i].d == 10 || timers[i].d == ctime.d || (timers[i].d == 8 && ctime.d > 0 && ctime.d < 6) || (timers[i].d == 9 && ctime.d > 5 && ctime.d < 8) || (timers[i].d > 10 && (ctime.d % (timers[i].d-10)) == 0)) { /* #ifdef TF_DEBUG Serial.println("[TIMER-DAY]: Match "+String(timers[i].d)); #endif*/ if (timers[i].h == ctime.h || (timers[i].h > 100 && (ctime.h % (timers[i].h-100)) == 0)) { /* #ifdef TF_DEBUG Serial.println("[TIMER-HOUR]: Match "+String(timers[i].h)); #endif*/ if (timers[i].m == ctime.m || (timers[i].m > 100 && (ctime.m % (timers[i].m-100)) == 0)) { /* #ifdef TF_DEBUG Serial.println("[TIMER-MIN]: Match "+String(timers[i].m)); #endif*/ if (timers[i].prog_mode == 0) { Stop(); }else if(timers[i].prog_mode == 1) { Start(1,timers[i].prog_id,-1); }else if(timers[i].prog_mode == 3) { Start(3,0,-1); }else if (timers[i].prog_mode == 4) { if (random(0,2) == 1) { //internal prog if (random(0,2) == 1) { Start(1, random(0,3), -1); }else { Start(0); } }else { Stop(); } } } } } } } } } /* * Load/Save configuration */ bool loadConfig(String filename) { File f = OpenConfigFile("/"+filename, "r"); if (!f) { return false;} String line; int index; int linenr = 0; char *seq; char *seq2; while (line = f.readStringUntil('\n')) { seq = strtok(&line[0],"="); index = 0; if (seq != NULL) { //timer engine if(String(seq) == "ST") { seq = strtok(NULL,"="); if (seq != NULL && String(seq) != "0") { timerEngine = true; DTimeServer = String(seq); }else { timerEngine = false; } //AP SSID }else if(String(seq) == "AS") { seq = strtok(NULL,"="); if (seq != NULL) { APssid = String(seq); } //AP Password }else if (String(seq) == "AP") { seq = strtok(NULL,"="); if (seq != NULL) { APpassword = String(seq); } //DisableSoftAP on STA mode }else if (String(seq) == "AD") { seq = strtok(NULL,"="); if (seq != NULL) { DisableAPSTA = atoi(seq)==0 ? false : true; }else { DisableAPSTA = false; } //Device name }else if (String(seq) == "DN") { seq = strtok(NULL,"="); if (seq != NULL) { DName = String(seq); } } //Saved timers else if (String(seq[0]) == "T") { int index = String(seq[1]).toInt(); if (index >= 0 && index < 10) { seq = strtok(NULL,"="); seq = strtok(seq,","); if (seq != NULL) { timers[index].d = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { timers[index].h = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { timers[index].m = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { timers[index].prog_mode = atoi(seq); }else { break; } if (timers[index].prog_mode == 1) { seq = strtok(NULL,","); if (seq != NULL) { timers[index].prog_id = atoi(seq); }else { break; } }else if(timers[index].prog_mode == 2) { // timers[index].color = RGB{0,0,0}; seq = strtok(NULL,","); /*if (seq != NULL) { timers[index].color.r = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { timers[index].color.g = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { timers[index].color.b = atoi(seq); }else { break; }*/ seq = strtok(NULL,","); if (seq != NULL) { timers[index].fade = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { timers[index].hold = atoi(seq); }else { break; } } timers[index].lastrun = 0; } } }else { break; } linenr++; } return true; } bool writeTimersConfig() { File f = OpenConfigFile("/timers.dat","w"); if (!f) { return false;} f.print(getTimers(1)); f.close(); return true; } //basic configuration (AP,APPWD,device info) bool writeConfig() { File f = OpenConfigFile("/config.dat","w"); if (!f) {return false;} f.print("DN="+DName+"\n"); f.print("AS="+APssid+"\n"); f.print("AP="+APpassword+"\n"); f.print("AD="+String(DisableAPSTA==true ? 1 :0)+"\n"); f.print("PWMFREQ="+String(PWMFreq)+"\n"); if (timerEngine) { f.print("ST="+DTimeServer+"\n"); }else { f.print("ST=\n"); } f.close(); return true; } File OpenConfigFile(String filename, char* type) { File f = SPIFFS.open(filename, type); if (!f) { #ifdef TF_DEBUG Serial.println("[WRITECFG] Cant open "+filename+" file for writing... "); #endif } return f; } /* * Helpers & others */ String GetDeviceStatus() { String respJSON = "["; for (int i=0; i=0 && r<=255 && g >= 0 && g <=255 && b >= 0 && b <= 255) { return true; } return false; } void GetDeviceHWAddr() { uint8_t MAC_softAP[6]; WiFi.softAPmacAddress(MAC_softAP); DHWAddr = ""; for (int i = 0; i < sizeof(MAC_softAP); ++i){ DHWAddr += String( MAC_softAP[i], HEX); } #ifdef TF_DEBUG Serial.println("Device serial number: "+DHWAddr); #endif } String handleCommandsFromString(String str, bool isUDP) { char c[str.length()]; str.toCharArray(c, str.length()+1); return handleCommands(c,isUDP); } char* strToChar(String str) { char c[str.length()]; int i = 0; for (i=0;i 0) { String cfg = http.getString(); File f = SPIFFS.open("/"+configFile, "w"); if (!f) { #ifdef TF_DEBUG Serial.println("[DLCFG] Cant open "+configFile+" file for writing... "); #endif return false; } f.print(cfg); f.close(); http.end(); return true; }else { #ifdef TF_DEBUG Serial.println("[DLCFG] Cant open url: "+configUrl); #endif } return false; } bool updateFirmware(String url) { update_fw_status = ESPhttpUpdate.update(url); switch(update_fw_status) { case HTTP_UPDATE_FAILED: #ifdef TF_DEBUG Serial.printf("[FWUPDATE] HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); #endif return false; break; case HTTP_UPDATE_NO_UPDATES: #ifdef TF_DEBUG Serial.println("[FWUPDATE] HTTP_UPDATE_NO_UPDATES"); #endif return false; break; case HTTP_UPDATE_OK: #ifdef TF_DEBUG Serial.println("[FWUPDATE] HTTP_UPDATE_OK"); #endif return true; break; } return false; } bool updateTime(bool forced) { if (timerEngine || forced) { if (millis() < lasttimeupdate) { lasttimeupdate = 0; } if (forced || (timererror > 0 && timererror < millis()) || lasttimeupdate == 0 || lasttimeupdate + updateInterval < millis()) { #ifdef TF_DEBUG Serial.println("[TIMER] Updating time"); #endif http.begin(DTimeServer); int httpCode = http.GET(); if(httpCode > 0) { String payload = http.getString(); timestamp = payload.toInt(); #ifdef TF_DEBUG Serial.println("[TIMER] Timestamp is : "+String(timestamp)); #endif lasttimeupdate = millis(); timererror = 0; http.end(); return true; }else { timererror = millis() + 360000; lasttimeupdate = millis(); } } } return false; } /* * IR Receiver */ #if defined(IRRECVPIN) && (IRRECVPIN > 0) int IRCmdCount = 0; boolean IREqual(String w, String value) { return w.equalsIgnoreCase(value); } void IRCommand() { String value = String(IRResults.value, HEX); bool isRepeat = false; bool valid = false; #ifdef TF_DEBUG Serial.println("[IR] "+value); #endif //stop if (IREqual(value,"94f87fb")) { Stop(); valid = true; } //start else if(IREqual(value,"fa34c27f")) { Start(0); valid = true; } //Red button else if (IREqual(value, "4913289b")) { ColorMode(RGB{255,0,0}, RGB{0,0,0}, 0, 0, 0); valid = true; } //Green button else if (IREqual(value, "a2c0809f")) { ColorMode(RGB{0,255,0}, RGB{0,0,0},0,0,0); valid = true; } //Blue button else if (IREqual(value, "f6c07237")) { ColorMode(RGB{0,0,255}, RGB{0,0,0},0,0,0); valid = true; } //White button else if (IREqual(value, "e7a5acbb")) { ColorMode(RGB{255,255,255}, RGB{0,0,0},0,0,0); valid = true; } //Start FADE program else if (IREqual(value, "858c2a3b")) { Start(1, 1); valid = true; } //Start FLASH program if (IREqual(value, "cc67539b")) { Start(1, 0); valid = true; } //Start smooth (user) program else if (IREqual(value, "531649ff")) { Start(0); valid = true; } //Start Strobe program else if (IREqual(value, "2cd82ddf")) { Start(1, 2); valid = true; } //brightness +1 else if (IREqual(value, "f16e011f")) { if (pstatus[0].mode == 1) { ChangeBrightness(10,-1); }else if(pstatus[0].mode == 2 || pstatus[0].mode == 3 || pstatus[0].mode == 4) { ChangeSpeed(-10,-1); } valid = true; } //brightness -1 else if (IREqual(value, "81e2531b")) { if (pstatus[0].mode == 1) { ChangeBrightness(-10,-1); }else if(pstatus[0].mode == 2 || pstatus[0].mode == 3 || pstatus[0].mode == 4) { ChangeSpeed(10,-1); } valid = true; } //red rows else if (IREqual(value,"835150fb")) { ColorMode(RGB{255,50,0}, RGB{0,0,0},0,0,0); valid = true; } else if (IREqual(value,"8e45a9bf")) { ColorMode(RGB{255,100,0}, RGB{0,0,0},0,0,0); valid = true; } else if (IREqual(value,"3c76279b")) { ColorMode(RGB{255,150,0}, RGB{0,0,0},0,0,0); valid = true; } else if (IREqual(value,"a83875f")) { ColorMode(RGB{255,255,0}, RGB{0,0,0},0,0,0); valid = true; } //green rows else if (IREqual(value,"dcfea8ff")) { ColorMode(RGB{0,255,50}, RGB{0,0,0},0,0,0); valid = true; } else if (IREqual(value,"e7f301c3")) { ColorMode(RGB{0,255,100}, RGB{0,0,0},0,0,0); valid = true; } else if (IREqual(value,"96237f9f")) { ColorMode(RGB{0,255,150}, RGB{0,0,0},0,0,0); valid = true; } else if (IREqual(value,"9af7d95b")) { ColorMode(RGB{0,255,255}, RGB{0,0,0},0,0,0); valid = true; } //blue rows else if (IREqual(value,"21b804df")) { ColorMode(RGB{0,50,255}, RGB{0,0,0},0,0,0); valid = true; } else if (IREqual(value,"3bf2f35b")) { ColorMode(RGB{50,50,255}, RGB{0,0,0},0,0,0); valid = true; } else if (IREqual(value,"eaa6b137")) { ColorMode(RGB{100,0,255}, RGB{0,0,0},0,0,0); valid = true; } else if (IREqual(value,"b830d0fb")) { ColorMode(RGB{255,0,255}, RGB{0,0,0},0,0,0); valid = true; } IRLastCmd = value; if (valid) { SendTCPStatus(); } } #endif /* * TCP & UDP Command parser */ String removeSpecialChars(String in) { in.replace(",","_"); in.replace("'",""); in.replace("\"",""); return in; } String handleCommands(char* packet, bool isUDP) { char *seq; int index = 0; seq = strtok(packet,"="); if (seq != 0) { //user_program_cmd if (String(seq[0]) == "+") { /* * UserProg commands */ if (String(seq[1]) == "P") { //save to fs if (String(seq[2]) == "W") { if (writeUserProgram()) { return "+ok=+PW"; }else { return "-err=+PW";} //prog speed }else if(String(seq[2]) == "S") { seq = strtok(NULL,"="); if (seq != NULL) { int sp = atoi(seq); int strip = -1; seq = strtok(seq,","); if (seq != NULL) { sp = atoi(seq); seq = strtok(NULL,","); if (seq != NULL) { if (String(seq) != "-1") { strip = atoi(seq); } } } if (sp >= 1 && sp <= 200) { SetSpeed(sp,strip); } return "+ok=+PS:"+String(sp)+","+String(strip); } return "-err=+PS"; } else { char *value = strtok(NULL,"="); if (_parseUserProgram(seq, value) == false) { return "-err="+String(seq); }else {return "+ok="+String(seq); } } } /* * AT-Commands */ else if (String(seq[1]) == "A" && String(seq[2]) == "T") { if (String(packet) == "+AT-REST") { ESP.restart(); } //reload config file else if (String(seq) == "+AT-RLDCFG") { seq = strtok(NULL,"="); if (seq != NULL) { if (String(seq) == "userprog.dat") { if (loadUserProgram()) { return "+ok=+AT-RLDCFG"; } } else if (loadConfig(String(seq))) { return "+ok=+AT-RLDCFG"; } else {} } return "-err=+AT-RLDCFG"; } //timer service else if (String(seq) == "+AT-TIMER") { seq = strtok(NULL,"="); if (seq == NULL) { if (timerEngine) { return "+ok=+AT-TIMER:on"; }else { return "+ok=+AT-TIMER:off"; } }else { if (String(seq) != "0") { if (String(seq)=="1") { DTimeServer = "http://techfactory.hu/esp/time/"; }else { DTimeServer = seq; } timerEngine = true; }else { DTimeServer = ""; timerEngine = false; } writeConfig(); if (timerEngine) { return "+ok=+AT-TIMER:ok"; }else { return "+ok=+AT-TIMER:off"; } } } else if (String(seq) == "+AT-UPDTIM") { if (timerEngine) { if (updateTime(true)) { STIME ctime = getTime(); return "+ok=+AT-UPDTIM:"+String(ctime.d)+","+String(ctime.h)+","+String(ctime.m)+","+String(ctime.s); }else { return "-err=+AT-UPDTIM:Cant update time!"; } }else { return "+ok=+AT-TIMER:off"; } } else if (String(packet) == "+AT-SCAN") { return "+ok="+String(packet)+":"+ScanWifiNetworks(); } else if (String(seq) == "+AT-FWUP") { seq = strtok(NULL,"="); if (seq != NULL) { if (updateFirmware( String(seq) )) { return "+ok=+AT-FWUP:1"; }else { switch(update_fw_status) { case HTTP_UPDATE_FAILED: return "-err=+AT-FWUP:" +String(ESPhttpUpdate.getLastError())+","+String(ESPhttpUpdate.getLastErrorString()); break; case HTTP_UPDATE_NO_UPDATES: return "+ok=+AT-FWUP:0"; break; } } } return "-err=+AT-FWUP:0"; } else if (String(seq) == "+AT-DLCFG") { seq = strtok(NULL,"="); if (seq != NULL) { seq = strtok(seq,","); if (seq != NULL) { String url = String(seq); seq = strtok(NULL,","); if (seq != NULL) { String file = String(seq); if (downloadConfig(url,file)) { return "+ok=+AT-DLCFG"; } } } } return "-err=+AT-DLCFG"; } else if (String(seq) == "+AT-STA") { seq = strtok(NULL,"="); if (seq != 0) { seq = strtok(seq,","); String STAssid = String(seq); if (STAssid == "-1") { //WiFi.persistent(false); WiFi.disconnect(); WiFi.begin("",""); return ""; }else { seq = strtok(NULL,","); String STApassword = String(seq); WiFi.persistent(true); STAConnect(STAssid, STApassword); seq = strtok(NULL,","); if (seq != NULL) { DisableAPSTA = atoi(seq)==0?false:true; } if (CheckWifiSTA(true)) { ESP.restart(); return "+ok=+AT-STA:"+String(STAssid); }else { return "-err=+AT-STA:"+String(STAssid); } } }else { if (WiFi.SSID()) { if (WiFi.status() != WL_CONNECTED) { return "-err=+AT-STA:"+String(WiFi.SSID()); }else { return "+ok=+AT-STA:"+String(WiFi.SSID()); } }else { return "-err=+AT-STA:0"; } } } //AP config (SSID,PASSWD) else if(String(seq) == "+AT-AP") { seq = strtok(NULL,"="); if (seq == NULL) { return "+ok=+AT-AP:"+APssid; }else { seq = strtok(NULL,","); if (seq != NULL) { APssid = String(seq); } seq = strtok(NULL,","); if (seq != NULL) { APpassword = String(seq); } seq = strtok(NULL,","); if (seq != NULL) { DisableAPSTA = atoi(seq)==0?false:true; } writeConfig(); return "+ok=+AT-AP"; } } //Enable/Disable AP/STA mode else if(String(seq) == "+AT-APSTA") { seq = strtok(NULL,"="); if (seq == NULL) { return "+ok=+AT-APSTA:"+(DisableAPSTA ? String("0") : String("1")); }else { if (atoi(seq)==0) { DisableAPSTA = true; } else { DisableAPSTA = false; } writeConfig(); return "+ok=+AT-APSTA"; } } //Device name else if(String(seq) == "+AT-NAME") { seq = strtok(NULL,"="); if (seq == NULL) { return "+ok=+AT-NAME:"+DName; }else { DName = removeSpecialChars(String(seq)); writeConfig(); return "+ok=+AT-NAME"; } } //PWM freq else if(String(seq) == "+AT-PWMFREQ") { seq = strtok(NULL,"="); if (seq == NULL) { return "+ok=+AT-PWMFREQ:"+PWMFreq; }else { PWMFreq = atoi(seq); analogWriteFreq(PWMFreq); writeConfig(); return "+ok=+AT-PWMFREQ"; } } return "-err="+String(seq); } /* * Control commands*/ //start program mode else if(String(seq) == "+START") { Start(0); sleepTimer(); return "+ok="+String(seq); } //stop all programs and turn off lights else if(String(seq) == "+STOP") { sleepTimer(); seq = strtok(NULL,"="); if (seq != NULL) { if (atoi(seq) >= -1 && atoi(seq) < RGBSCOUNT) { Stop(atoi(seq)); }else { return "-err="+String(seq); } }else { Stop(-1); } return "+ok="+String(seq); } else if(String(seq) == "+STAT") { return "+ok=+STAT:"+GetDeviceStatus(); } else if(String(seq) == "+TIME") { STIME ctime = getTime(); return "+ok=+TIME:"+String(ctime.d)+","+String(ctime.h)+","+String(ctime.m)+","+String(ctime.s); } //write timers else if (String(seq) == "+TW") { if (writeTimersConfig()) { return "+ok=+TW"; }else { return "-err=+TW"; } } //set timers else if (String(seq[1]) == "T") { if (seq[2] == NULL) { return "-err=+T"; }else if(String(seq[2]) == "R") { return "+ok=+TR:"+getTimers(0); } int index = String(seq[2]).toInt(); if (index < 0 || index > 9) { return "-err=+T:Out of range"; } seq = strtok(NULL,"="); if (seq == NULL) { return "-err=+T'+String(index)+':No data"; }else { if (index >= 0 && index < 10) { STIMER tim = STIMER {}; seq = strtok(seq,","); tim.d = atoi(seq); if (tim.d > 0) { seq = strtok(NULL,","); tim.h = atoi(seq); seq = strtok(NULL,","); tim.m = atoi(seq); seq = strtok(NULL,","); tim.prog_mode = atoi(seq); if (tim.prog_mode == 1) { seq = strtok(NULL,","); tim.prog_id = atoi(seq); } else { tim.prog_id = 0; } if (tim.prog_mode == 2) { int r = 0; int g = 0; int b = 0; seq = strtok(NULL,","); r = atoi(seq); seq = strtok(NULL,","); g = atoi(seq); seq = strtok(NULL,","); b = atoi(seq); if (checkRGB(r,g,b)) { //tim.color = RGB{r,g,b}; } seq = strtok(NULL,","); tim.fade = atoi(seq); seq = strtok(NULL,","); tim.hold = atoi(seq); } }else { tim.h = 0; tim.m = 0; tim.prog_id = 0; tim.prog_mode = 0; } tim.lastrun = 0; timers[index] = tim; return "+ok=+T"+String(index); }else { return "-err=+T"+String(index); } } } //+SET[S]=[COLOR_ID],[VALUE],[STRIP_ID] else if(String(seq) == "+SET" || String(seq) == "+SETS") { sleepTimer(); bool sendResponse = false; if (String(seq) == "+SETS") { sendResponse = true; } seq = strtok(NULL,"="); if (seq != NULL) { seq = strtok(seq,","); int strip = -1; int color = 0; int value = 0; if (seq != NULL) { if (atoi(seq) >= 0 && atoi(seq) <= 2) { color = atoi(seq); } seq = strtok(NULL,","); value = atoi(seq); seq = strtok(NULL,","); if (atoi(seq) >= 0) { strip = atoi(seq); } SetColorValue(strip, color, value); return sendResponse?"+ok=+SET":""; } } } //set current color else if (String(seq[1]) == "C") { sleepTimer(); //+C=SR,SG,SB,ER,EG,EB,FADETIME,HOLDTIME,ONCE,STRIP,TURN_OFF_OTHERS bool sendResponse = false; if (String(seq[2]) == "S") { sendResponse = true; } seq = strtok(NULL,"="); if (seq != 0) { int sr,sg,sb, er,eg,eb = 0; seq = strtok(seq,","); if (seq != NULL) { sr = atoi(seq); } else { return "-err=+C=SR"; } seq = strtok(NULL,","); if (seq != NULL) { sg = atoi(seq); } else { return "-err=+C=SG"; } seq = strtok(NULL,","); if (seq != NULL) { sb = atoi(seq); } else { return "-err=+C=SB"; } seq = strtok(NULL,","); if (seq != NULL) { er = atoi(seq); } seq = strtok(NULL,","); if (seq != NULL) { eg = atoi(seq); } seq = strtok(NULL,","); if (seq != NULL) { eb = atoi(seq); } int f,h,o,strip = 0; seq = strtok(NULL,","); if (seq != 0) { f = atoi(seq); } seq = strtok(NULL,","); if (seq != 0) { h = atoi(seq); } seq = strtok(NULL,","); if (seq != 0) { o = atoi(seq); } seq = strtok(NULL,","); if (seq != NULL) { strip = atoi(seq); }else { strip = -1; } if (strip >-1) { seq = strtok(NULL,","); if (seq != NULL) { //turn off other strips for (int x = 0; x< RGBSCOUNT; x++) { if (x != strip) { ColorMode(RGB{0,0,0},RGB{0,0,0},0,0,0,x); } } } } if (checkRGB(sr,sg,sb)) { ColorMode(RGB {sr,sg,sb}, RGB{er,eg,eb}, f, h, o, strip); return sendResponse ? "+ok=+CS" : ""; }else { return "-err=+C=Wrong RGB color"; } } return "-err="+String(seq); } //start program else if (String(seq[1]) == "M") { sleepTimer(); if (seq[2] != NULL && String(seq[2])=="D") { prog_tcpdbg = true; } else { prog_tcpdbg = false; } seq = strtok(NULL,"="); int v = 0; int strip = -1; if (seq != NULL) { v = atoi(seq); seq = strtok(seq,","); if (seq != NULL) { v = atoi(seq); seq = strtok(NULL,","); if (seq != NULL) { strip = atoi(seq); } } if (v>-1&&v<3) { prog_tcpdbg = false; Start(1,int(v),strip); return "+ok="+String(seq); }else if(v==3) { //start user defined program if (countUserProgram() > 0) { Start(3,0,-1); return prog_tcpdbg?"+ok=+MD":"+ok=+M"; }else { return "-err=+M:No user program defined"; } } } return "-err=+M"; } }else { return "-err="+String(seq); } } return "-err"; }