/* * TechFactory * Simple led strip controller for ESP8266 * v1.0 - 2017.01.22 * techfactory.hu */ #include #include #include "FS.h" #include #include #define TF_DEBUG 1 #define MAX_TCP_CLIENTS 3 #define MAX_UDP_CLIENTS 3 #define PINS 8 /* * VARIABLES * --------- */ //*** APP variables *** String APssid = "TFLED-DEVICE"; String APpassword = "A1234B5678"; String DName = "LED-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 = "tfled-1.0"; const unsigned int UDPLocalPort = 42910; const unsigned int TCPLocalPort = 9000; const String UDPSearchString = "TECHFACTORYESPBRCAST"; uint8_t int_error = 0; //***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*** 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; //*** PIN settings **** int PWMFreq = 10000; struct PINSetting { bool enabled; byte pin; String name; byte io; //1 = INPUT, 2 = OUTPUT byte iotype; //1 = ANALOG, 2 = DIGITAL byte defval; }; int PINStatus[PINS]; //available pins const byte avpins[PINS] { 2,4,5,12,13,14,15,16 }; //used pins storage PINSetting usedpins[PINS]; //***TIMER variables*** struct STIME { byte d; byte h; byte m; byte s; unsigned long ts; }; struct STIMER { byte d; //0: off (timer disabled), 1-7 days, 8 - every day, 9 - every weekday, 10 - every weekend, 10 > = every x-10 day (11,12,13 ect) byte h; byte m; byte pin; // 0 ... 8 selected pin; 50 = all available; 51 = random; byte prog_mode; //0=turn off, 1=turn on (prog_value=brightness), 2=random (on / off with random brightness) byte prog_value; //if prog_mode=1, brightness (0-255) 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); #ifdef TF_DEBUG Serial.println("Starting up..."); #endif //get HW addr GetDeviceHWAddr(); if (!SPIFFS.begin()){ #ifdef TF_DEBUG Serial.println("FS not ready..."); #endif } //Load PIN config if (!loadConfig("hwconfig.dat")) { #ifdef TF_DEBUG Serial.println("[LOADCFG] No hwconfig.dat found"); #endif setDefaultConfig(); } //Load AP config if (!loadConfig("config.dat")) { #ifdef TF_DEBUG Serial.println("[LOADCFG] No config.dat found"); #endif } if (PWMFreq > 0) { analogWriteFreq(PWMFreq); } //reset led pinouts resetPins(); //Init AP WiFi.mode(WIFI_AP_STA); SoftAPConnect(); //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); //ready ... } //MAIN program loop void loop() { //Serial communication 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(); } void Stop() { for (int i = 0; i0) { pinMode(usedpins[i].pin, usedpins[i].io == 1 ? INPUT : OUTPUT); if (usedpins[i].io==2) { if (usedpins[i].iotype == 1) { analogWrite(usedpins[i].pin, analogWriteValue(usedpins[i].defval)); PINStatus[i] = analogWriteValue(usedpins[i].defval); }else { digitalWrite(usedpins[i].pin, usedpins[i].defval > 0 ? HIGH : LOW); PINStatus[i] = usedpins[i].defval > 0 ? 1 : 0; } } }else { pinMode(avpins[i], INPUT); PINStatus[i] = 0; } } } //arduino analogWrite maximum value is 255, on esp8266 the maximum is 1023 int analogWriteValue(int value) { float d2 = ((float)value/(float)255); return constrain(ceil(d2 * 1023), 0, 1023); } void writeToPin(PINSetting p, int value) { if (p.io == 2) { if (p.iotype == 1) { int v = analogWriteValue(value); analogWrite(p.pin, v); for (int i=0; i 0 ? HIGH : LOW); } } } int readFromPin(PINSetting p) { if (p.iotype == 1) { if (p.io == 2) { for (int i=0; i0) || !activeOnly) { if (jsonFormat) { if (!first) { out += ","; } out += "{\"id\":"+String(i)+",\"enabled\":"+(usedpins[i].enabled?"1":"0")+",\"pin\":"+String(usedpins[i].pin)+",\"io\":"+String(usedpins[i].io)+",\"iotype\":"+String(usedpins[i].iotype)+",\"defval\":"+String(usedpins[i].defval)+","; out += "\"name\":\""+String(usedpins[i].name)+"\""; if (reportValue) { out += ",\"val\":"+String(readFromPin(usedpins[i])); } out += "}"; first=false; } else { out += "HW="; out += String(i)+","+(usedpins[i].enabled?"1":"0")+","+String(usedpins[i].pin)+","+String(usedpins[i].io)+","+String(usedpins[i].iotype)+","+String(usedpins[i].defval)+","+String(usedpins[i].name); out += "\n"; } } } if (jsonFormat) { out += "]"; } return out; } /* * 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 { #ifdef TF_DEBUG Serial.println("Failed to set SID/PWD"); #endif } }else { #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()) { 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); } } } String GetDeviceInfo() { String json = "{"; json += "\"fw\":\""+String(FW_VER)+"\",\"hwnr\":\""+DHWAddr+"\",\"devs\":["; bool first = true; for (int i=0; i0) { if (!first) { json +=","; } json += "{\"id\":\""+String(i)+"\",\"name\":\""+String(usedpins[i].name)+"\"}"; first = false; } } json += "],\"tcp\":\""+String(TCPLocalPort)+"\",\"name\":\""+String(DName)+"\""; if (SoftApRunning) { json += ",\"ap\":\""+String(APssid)+"\""; } if (WiFi.status() == WL_CONNECTED) { json += ",\"sta\":\""+String(WiFi.SSID())+"\""; } json += "}"; return json; } //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; } void UDPWorker() { int packetSize = Udp.parsePacket(); bool knownClient = false; char incomingPacket[255]; if (packetSize){ int len = Udp.read(incomingPacket, 255); if (len > 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; /* } }*/ } } } /* * HTTP based functions * FW_UPDATE, Download config, update timer */ bool downloadConfig(String configUrl, String configFile) { http.begin(configUrl); if (http.GET() > 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; } /* * 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].pin)+sep; tm += String(timers[i].prog_mode)+sep+String(timers[i].prog_value); 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 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) { 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)) { if (timers[i].h == ctime.h || (timers[i].h > 100 && (ctime.h % (timers[i].h-100)) == 0)) { if (timers[i].m == ctime.m || (timers[i].m > 100 && (ctime.m % (timers[i].m-100)) == 0)) { for (int p=0; p= 50 || timers[i].pin == p) { if (usedpins[p].enabled == 1 && usedpins[p].pin > 0) { if (timers[i].pin == 51 && random(0,2) == 0) { continue; } if (timers[i].prog_mode == 1) { writeToPin(usedpins[p], timers[i].prog_value); }else if(timers[i].prog_mode == 0) { writeToPin(usedpins[p], 0); }else { writeToPin(usedpins[p], random(0,2) == 0 ? 0 : 255); } } } } } } } } } } } /* * Load/Save configuration */ 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; } 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].pin = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { timers[index].prog_mode = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { timers[index].prog_value = atoi(seq); }else { break; } timers[index].lastrun = 0; } } //pin configuration else if(String(seq) == "HW") { seq = strtok(NULL,"="); seq = strtok(seq,","); int pindex = 0; if (seq != NULL) { pindex = atoi(seq); }else { break; } if (pindex > -1 && pindex < PINS) { seq = strtok(NULL,","); if (seq != NULL) { usedpins[pindex].enabled = atoi(seq)==0 ?false:true; }else { break; } seq = strtok(NULL,","); if (seq != NULL) { usedpins[pindex].pin = avpins[pindex]; }else { break; } seq = strtok(NULL,","); if (seq != NULL) { usedpins[pindex].io = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { usedpins[pindex].iotype = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { usedpins[pindex].defval = atoi(seq); }else { break; } seq = strtok(NULL,","); if (seq != NULL) { usedpins[pindex].name = String(seq); }else { usedpins[linenr].name = "Device#"+String(linenr); } } } }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; } //saving pins configuration bool writePinConfig() { File f = OpenConfigFile("/hwconfig.dat","w"); if (!f) { Serial.println("[SPIFF] Cant open file for writing: hwconfig.dat"); return false; } f.print(getPinSettings(false)); f.close(); return true; } /* * Helpers & others */ 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 } char* strToChar(String str) { char c[str.length()]; int i = 0; for (i=0;i -1 && pid < PINS) { seq = strtok(NULL,","); if (atoi(seq)==0) { usedpins[pid].enabled = false; resetPins(); return "+ok=+AT-HWSET:"+String(pid); }else { seq = strtok(NULL,","); if (seq!=NULL && atoi(seq) > 0 && atoi(seq) < 3) { usedpins[pid].io = atoi(seq); }else { return "-err=+AT-HWSET:Wrong IO value"; } seq = strtok(NULL,","); if (seq!=NULL && atoi(seq) > 0 && atoi(seq) < 3) { usedpins[pid].iotype = atoi(seq); }else { return "-err=+AT-HWSET:Wrong IOTYPE value"; } seq = strtok(NULL,","); if (seq!=NULL && atoi(seq) >= 0 && atoi(seq) <= 255) { usedpins[pid].defval = atoi(seq); }else { return "-err=+AT-HWSET:Wrong DEFAULT value"; } seq = strtok(NULL,","); if (seq!=NULL) { usedpins[pid].name = removeSpecialChars(String(seq)); } usedpins[pid].enabled = true; usedpins[pid].pin = avpins[pid]; resetPins(); return "+ok=+AT-HWSET:"+String(pid); } } } return "-err=+AT-HWSET:Wrong parameter count"; } //get pin settings else if(String(seq) == "+AT-HWINF") { return "+ok=+AT-HWINF:"+getPinSettings(true); } //Save PIN settings to hwconfig.dat else if(String(seq) == "+AT-HWSAVE") { if (writePinConfig()) { return "+ok="+String(seq); } } //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); } //get input pin value else if (String(seq) == "+GET") { //+GET=index seq = strtok(NULL,"="); if (seq != NULL) { seq = strtok(seq,","); int index = atoi(seq); if (index < 0 || index >= PINS) { return "-err=+GET:Out of range"; } if (usedpins[index].pin > 0) { return "+ok=+GET:"+String(index)+","+String(readFromPin(usedpins[index])); }else { return "-err=+GET"; } } } //stop (turn off all pins) else if(String(seq) == "+STOP") { Stop(); return "+ok="+String(seq); } else if(String(seq) == "+HWINF") { return "+ok=+HWINF:"+getPinSettings(true,true,true); } else if(String(seq) == "+STAT") { String stat = "["; for (int i=0; i 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.pin = atoi(seq); seq = strtok(NULL,","); tim.prog_mode = atoi(seq); seq = strtok(NULL,","); tim.prog_value = atoi(seq); }else { tim.h = 0; tim.m = 0; tim.pin = 0; tim.prog_value = 0; tim.prog_mode = 0; } tim.lastrun = 0; timers[index] = tim; return "+ok=+T"+String(index); }else { return "-err=+T"+String(index); } } } //+SET[S]=[PIN_ID],[VALUE] else if(String(seq) == "+SET" || String(seq) == "+SETS") { bool sendResponse = false; if (String(seq) == "+SETS") { sendResponse = true; } seq = strtok(NULL,"="); if (seq != NULL) { int pid = -1; int value = 0; seq = strtok(seq,","); if (seq != NULL) { pid = atoi(seq); } if (pid != -1 && pid > PINS-1) {return "-err:+SET:Wrong PIN id"; } seq = strtok(NULL,","); value = atoi(seq); if (value < 0 || value > 255) { return "-err:+SET:Wrong value"; } for (int i=0; i< PINS;i++) { if (pid == i || pid == -1) { if (usedpins[i].enabled && usedpins[i].pin>0) { writeToPin(usedpins[i], value); } } } return sendResponse?"+ok=+SET":""; } } }else { return "-err="+String(seq); } } return "-err"; }