// for ES32 (e.g Sparkfun Thing ESP32-C3 etc) and ESP8266 (e.g. HUZZAH ESP8266 etc) // Network Settings #include const char staticIP[] = "10.1.1.71"; const char ssid[] = "ssid"; const char pw[] = "password"; IPAddress dns1(8, 8, 8, 8); IPAddress dns2(8, 8, 8, 4); // split here for .h file // ========= Start of Setpoint.h generated code ========== #ifndef SETPOINT_H #define SETPOINT_H // Setpoint.h from Setpoint by pfodGUIdesigner V1.0.46 /* (c)2022 Forward Computing and Control Pty. Ltd. NSW Australia, www.forward.com.au This code is not warranted to be fit for any purpose. You may only use it at your own risk. This code may be freely used for both private and commercial use Provide this copyright is maintained. */ #include #include #include class Setpoint : public pfodDrawing { public: Setpoint(pfodParser &parser, pfodDwgs& dwgs); bool sendDwg(); // returns is dwg sent else false i.e. not this dwg's loadCmd bool processDwgCmds(); // return true if handled else false private: void sendFullDrawing(long dwgIdx); void sendUpdate(); void drawDwg0(); void updateDwg(); bool forceReload; void printDwgCmdReceived(Print *outPtr); pfodAutoIdx _update_idx_46; pfodAutoIdx _update_idx_50; pfodAutoCmd _touchZone_cmd_2; }; #endif // ========= End of Setpoint.h generated code ========== // split here for .cpp file // ========= Start of Setpoint.cpp generated code ========== // Setpoint.cpp from Setpoint by pfodGUIdesigner V1.0.46 /* (c)2022 Forward Computing and Control Pty. Ltd. NSW Australia, www.forward.com.au This code is not warranted to be fit for any purpose. You may only use it at your own risk. This code may be freely used for both private and commercial use Provide this copyright is maintained. */ #ifndef SETPOINT_H #include "Setpoint.h" #endif Setpoint::Setpoint(pfodParser &parser, pfodDwgs& dwgs) : pfodDrawing(parser, dwgs) { forceReload = true; // force reload on reboot, e.g. reprogrammed } bool Setpoint::processDwgCmds() { // return true if handled else false byte dwgCmd = parserPtr->parseDwgCmd(); // dwgCmd is ' if (!dwgCmd) { return false; // not dwg cmd not handled } if (parserPtr->dwgCmdEquals(_touchZone_cmd_2)) { // handle _touchZone_cmd_.. Serial.print(" Got pfodAutoCmd:"); Serial.println("_touchZone_cmd_2"); printDwgCmdReceived(&Serial); // does nothing if passed NULL // add your cmd handling code here // send update sendUpdate(); return true; } return false; // not handled } void Setpoint::drawDwg0() { dwgsPtr->rectangle().color(0).offset(0.00,0.00).size(7.00,4.00).centered().send(); dwgsPtr->label().color(0).offset(-8.00,0.00).text("Temp").fontSize(0).center().send(); dwgsPtr->label().color(0).offset(6.00,0.00).text("℃").fontSize(0).center().send(); dwgsPtr->index().idx(_update_idx_46).send(); dwgsPtr->index().idx(_update_idx_50).send(); dwgsPtr->touchZone().cmd(_touchZone_cmd_2).offset(0.00,0.00).size(7.00,4.00).centered().send(); dwgsPtr->touchActionInput().cmd(_touchZone_cmd_2).backgroundColor(12).color(7).fontSize(6).prompt("Enter setpoint").textIdx(_update_idx_46).send(); } void Setpoint::updateDwg() { dwgsPtr->label().idx(_update_idx_46).color(0).offset(0.00,0.00).fontSize(0).center().value(25.500000).decimals(1).units("").send(); dwgsPtr->label().idx(_update_idx_50).color(9).offset(0.00,4.00).text("Error Message").fontSize(0).italic().bold().center().send(); } bool Setpoint::sendDwg() { if (!parserPtr->cmdEquals(*this)) { return false; // not this dwg's loadCmd } // else // ======== begin debugging Serial.print(" sendDwg() cmd "); Serial.print((char*)parserPtr->getCmd()); if (parserPtr->isRefresh()) { Serial.print(" isRefresh"); } byte* argPtr = parserPtr->getFirstArg(); if (*argPtr) { Serial.print(" args: "); while (*argPtr) { Serial.print(" "); Serial.print((char*)argPtr); argPtr = parserPtr->getNextArg(argPtr); } } Serial.println(); // ======= end debugging ======= if (parserPtr->isRefresh() && !forceReload) { // refresh and not forceReload just send update sendUpdate(); } else { forceReload = false; // long pfodLongRtn = 0; uint8_t* pfodFirstArg = parserPtr->getFirstArg(); // may point to \0 if no arguments in this msg. parserPtr->parseLong(pfodFirstArg, &pfodLongRtn); // parse first arg as a long sendFullDrawing(pfodLongRtn); } return true; } void Setpoint::sendFullDrawing(long dwgIdx) { if (dwgIdx == 0) { dwgsPtr->start(50, 50, dwgsPtr->WHITE, true); // the dwg size 50x50 and color (WHITE) are ignored when this drawing is inserted in the main dwg //true means more to come parserPtr->sendRefreshAndVersion(0); //need to set a version number for image refresh to work!! drawDwg0(); // dwgsPtr->end(); // if drawing msg is >1023 split it in to two or more parts and add extra else if (dwgIdx == .. ) blocks here } else if (dwgIdx == 1) { dwgsPtr->startUpdate(false); // always send update last so this is the last msg => false updateDwg(); // update with current state, // defined in display.cpp dwgsPtr->end(); } else { parserPtr->print(F("{}")); // always return somthing } } void Setpoint::sendUpdate() { dwgsPtr->startUpdate(); // dwg refresh updates must be <1024 bytes, i.e. can only be 1 msg updateDwg(); dwgsPtr->end(); } void Setpoint::printDwgCmdReceived(Print *outPtr) { if (!outPtr) { return; } outPtr->print(" touchZone cmd "); outPtr->print((const char*)parserPtr->getDwgCmd()); outPtr->print(" at "); outPtr->print("("); outPtr->print(parserPtr->getTouchedX()); outPtr->print(","); outPtr->print(parserPtr->getTouchedY()); outPtr->print(") "); if (parserPtr->isTouch()) { outPtr->print("touch type:TOUCHED"); } outPtr->println(); if (*parserPtr->getEditedText()) { // first char not '\0' outPtr->print(" Edited text '"); outPtr->print((const char*)parserPtr->getEditedText()); outPtr->println("'"); } } // ========= End of Setpoint.cpp generated code ========== // end of Setpoint class files // ========= Start of Test Code to test above class generated code ========== // This template code uses above class and inserts that dwg in the main dwg /* Test code for pfodGUIdesigner by Matthew Ford, 2022/10/01 */ /* (c)2022 Forward Computing and Control Pty. Ltd. NSW Australia, www.forward.com.au This code is not warranted to be fit for any purpose. You may only use it at your own risk. This code may be freely used for both private and commercial use Provide this copyright is maintained. */ #if defined (ESP32) #include #elif defined (ESP8266) #include #else #error "Only ESP32 and ESP8266 supported" #endif #include #include #include #ifndef SETPOINT_H #include "Setpoint.h" #endif pfodAutoCmd mainDwgLoadCmd; // the main empty dwg static bool forceMainMenuReload = true; // for testing force complete reload on every reboot static bool forceMainDwgReload = true; // for testing force complete reload on every reboot static unsigned long dwgRefresh = 0;//set to 0 to disable auto refresh or 10000; to refresh every 10sec // when on the front screen, the mobile back button will also refresh, otherwise goes back to the previous menu/screen // install library "ESPAutoWiFiConfig.h" from Arduin or download from https://www.forward.com.au/pfod/ESPAutoWiFiConfig/index.html // for ESPBufferedClient #include const unsigned long BAUD_RATE = 115200ul; pfodParser parser("V1"); // create a parser with menu version string to handle the pfod messages pfodDwgs dwgs(parser); // drawing support ESPBufferedClient bufferedClient; Setpoint _setpoint(parser, dwgs); // create dwg object and add to parser processing // send main dwg that just inserts the designed dwg at the pushZero position and scaling. bool sendMainDwg() { // main dwg just inserts desgined dwg dwgs.start(50, 50, dwgs.WHITE, false); //false means NO more to come, background defaults to WHITE if omitted i.e. dwgs.start(50,30); parser.sendRefreshAndVersion(0); //need to set a version number for image refresh to work!! send the parser version to cache this image, refreshes every 10sec dwgs.pushZero(25.00,25.00,1.00); // position and scale the inserted designed dwg dwgs.insertDwg().loadCmd(_setpoint).send(); // insert the dwg object in the main dwg dwg dwgs.popZero(); dwgs.end(); return true; } void sendMainDwgUpdate() { // nothing here, but insertedDwgs updated by parser.parse() dwgs.startUpdate(); // dwg refresh updates must be <1024 bytes, i.e. can only be 1 msg dwgs.end(); } void handle_pfodParser() { uint8_t cmd = parser.parse(); // parse incoming data from connection // parser returns non-zero when a pfod command is fully parsed and not consumed by an inserted dwg if (cmd != 0) { // have parsed a complete msg { to } // == begin debugging if (cmd == '!') { Serial.print(" handle_pfodParser() closeConnection cmd "); Serial.print((char)cmd); } else { Serial.print(" handle_pfodParser() cmd "); Serial.print((char*)parser.getCmd()); if (parser.isRefresh()) { Serial.print(" isRefresh"); } byte* argPtr = parser.getFirstArg(); if (*argPtr) { Serial.print(" args: "); while (*argPtr) { Serial.print(" "); Serial.print((char*)argPtr); argPtr = parser.getNextArg(argPtr); } } } Serial.println(); // ========== end debugging if ('.' == cmd) { // pfodApp has connected and sent {.} , it is asking for the main menu if (parser.isRefresh() && !forceMainMenuReload) { sendMainMenuUpdate(); // menu is cached just send update } else { forceMainMenuReload = false; sendMainMenu(); // send back the menu designed } } else if (parser.cmdEquals(mainDwgLoadCmd)) { if (parser.isRefresh() && !forceMainDwgReload) { // refresh and not forceReload just send update sendMainDwgUpdate(); } else { forceMainDwgReload = false; // sendMainDwg(); // just inserts dwgs and that will update them } } else if (parser.cmdEquals('A')) { // click in A menu item that holds the main dwg // not used as inserted dwgs handle all the clicks sendMainMenuUpdate(); // always reply to the msg. // handle {@} request } else if ('@' == cmd) { // pfodApp requested 'current' time parser.print(F("{@`0}")); // return `0 as 'current' raw data milliseconds } else if ('!' == cmd) { // CloseConnection command closeConnection(parser.getPfodAppStream()); } else { // unknown command parser.print(F("{}")); // always send back a pfod msg otherwise pfodApp will disconnect. } } } void connect_pfodParser(WiFiClient * client) { parser.connect(bufferedClient.connect(client)); // sets new io stream to read from and write to } void closeConnection(Stream * io) { (void)(io); // unused // add any special code here to force connection to be dropped parser.closeConnection(); // nulls io stream bufferedClient.stop(); // clears client reference } void sendMainMenu() { parser.print(F("{,")); // start a Menu screen pfod message parser.print("~"); // no prompt parser.sendRefreshAndVersion(dwgRefresh); // send menu items parser.print(F("|+A~")); parser.print(mainDwgLoadCmd); // the unique load cmd parser.print(F("}")); // close pfod message } void sendMainMenuUpdate() { parser.print(F("{;")); // start an Update Menu pfod message parser.print(F("~")); // no change to colours and size // send menu items parser.print(F("|+A")); // reload A calls for dwg z update parser.print(F("}")); // close pfod message // ============ end of menu =========== } void setup() { EEPROM.begin(512); // only use 20bytes for pfodSecurity but reserve 512 Serial.begin(115200); Serial.println(); for (int i = 10; i > 0; i--) { Serial.print(i); Serial.print(' '); delay(500); } if (!connectToWiFi(staticIP, ssid, pw)) { Serial.println(" Connect to WiFi failed."); while (1) { delay(10); // wait here but feed WDT } } else { Serial.print(" Connected to "); Serial.print(ssid); Serial.print(" with IP:"); Serial.println(WiFi.localIP()); } start_pfodServer(); } void loop() { handle_pfodServerConnection(); } // ------------------ trival telnet running on port 4989 server ------------------------- //how many clients should be able to telnet to this #define MAX_SRV_CLIENTS 1 WiFiServer pfodServer(4989); WiFiClient pfodServerClients[MAX_SRV_CLIENTS]; void start_pfodServer() { pfodServer.begin(); pfodServer.setNoDelay(true); Serial.print("pfodServer Ready to Use on "); Serial.print(WiFi.localIP()); Serial.println(":4989"); } void handle_pfodServerConnection() { //check if there are any new clients uint8_t i; if (pfodServer.hasClient()) { for (i = 0; i < MAX_SRV_CLIENTS; i++) { //find free/disconnected spot if (!pfodServerClients[i] || !pfodServerClients[i].connected()) { if (pfodServerClients[i]) { pfodServerClients[i].stop(); } pfodServerClients[i] = pfodServer.available(); if (!pfodServerClients[i]) { Serial.println("available broken"); } else { Serial.print("New client: "); Serial.print(i); Serial.print(' '); Serial.println(pfodServerClients[i].remoteIP()); connect_pfodParser(&pfodServerClients[i]); break; } } } if (i >= MAX_SRV_CLIENTS) { //no free/disconnected spot so reject pfodServer.available().stop(); } } //check clients for data for (i = 0; i < MAX_SRV_CLIENTS; i++) { if (pfodServerClients[i] && pfodServerClients[i].connected()) { handle_pfodParser(); } else { if (pfodServerClients[i]) { pfodServerClients[i].stop(); } } } } // returns false if does not connect bool connectToWiFi(const char* staticIP, const char* ssid, const char* password) { WiFi.mode(WIFI_STA); if (staticIP[0] != '\0') { IPAddress ip; bool validIp = ip.fromString(staticIP); if (validIp) { IPAddress gateway(ip[0], ip[1], ip[2], 1); // set gatway to ... 1 IPAddress subnet_ip = IPAddress(255, 255, 255, 0); WiFi.config(ip, gateway, subnet_ip, dns1, dns2); } else { Serial.print("staticIP is invalid: "); Serial.println(staticIP); return false; } } Serial.print(" Connecting to WiFi "); if (WiFi.status() != WL_CONNECTED) { WiFi.begin(ssid, password); } // Wait for connection for 30sec unsigned long pulseCounter = 0; unsigned long delay_ms = 100; unsigned long maxCount = (30 * 1000) / delay_ms; // delay below while ((WiFi.status() != WL_CONNECTED) && (pulseCounter < maxCount)) { pulseCounter++; delay(delay_ms); // often also prevents WDT timing out if ((pulseCounter % 10) == 0) { Serial.print("."); } } if (pulseCounter >= maxCount) { Serial.println(" Failed to connect."); return false; } // else Serial.println(" Connected."); return true; } // ========= End of Test Code ==========