// install pfodParser library from the Arduino Library Manager OR // download from http://www.forward.com.au/pfod/pfodParserLibraries/index.html #include // includes pfodParser.h, pfodSecurity only used for WiFi/SMS connections // Using ESP32 based board programmed via Arduino IDE // follow the steps given on http://www.forward.com.au/pfod/ESP32/index.html to install ESP32 support for Arduino IDE // NOTE: The latest versions of the ESP32 V2.0.0 - V2.0.5 Arduino board support have problems with WiFi connections // for the ESP32 chip (e.g. Sparkfun ESP32 Thing, tec). // It may take 3 or more re-connections to get a stable connection after reboot or reprogramming. // ESP32 BLE and Bluetoot are OK on V2.0.4 as is ESP32C3 WiFi (different chip). // For ESP8266 V3.0.2 works. // You need to modify the WLAN_SSID, WLAN_PASS settings below // to match your 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); /* Code generated by pfodGUIdesigner V1.0.4192 */ /* * (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 generated code may be freely used for both private and commercial use * provided this copyright is maintained. */ // split here for .h file // ========= Start of OnOffSlider.h generated code ========== #ifndef ONOFFSLIDER_H #define ONOFFSLIDER_H // OnOffSlider.h from OnOffSlider by pfodGUIdesigner V1.0.4192 /* (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 Provided this copyright is maintained. */ #include #include #include class OnOffSlider : public pfodDrawing { public: OnOffSlider(pfodParser* parserPtr, pfodDwgs* dwgsPtr); 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_13; pfodAutoIdx _update_idx_14; pfodAutoIdx _update_idx_15; pfodAutoCmd _touchZone_cmd_5; }; #endif // ========= End of OnOffSlider.h generated code ========== // split here for .cpp file // ========= Start of OnOffSlider.cpp generated code ========== // OnOffSlider.cpp from OnOffSlider by pfodGUIdesigner V1.0.4192 /* (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 Provided this copyright is maintained. */ #ifndef ONOFFSLIDER_H #include "OnOffSlider.h" #endif OnOffSlider::OnOffSlider(pfodParser* parserPtr, pfodDwgs* dwgsPtr) : pfodDrawing(parserPtr, dwgsPtr) { forceReload = true; // force reload on reboot, e.g. reprogrammed } bool OnOffSlider::processDwgCmds() { // return true if handled else false byte dwgCmd = parserPtr->parseDwgCmd(); // dwgCmd is '\0' if this is NOT a dwg cmd if (!dwgCmd) { return false; // not dwg cmd not handled } if (parserPtr->dwgCmdEquals(_touchZone_cmd_5)) { // handle _touchZone_cmd_.. Serial.print(" Got pfodAutoCmd:"); Serial.println("_touchZone_cmd_5"); printDwgCmdReceived(&Serial); // does nothing if passed NULL // add your cmd handling code here // send update sendUpdate(); return true; } return false; // not handled } void OnOffSlider::drawDwg0() { dwgsPtr->circle().color(10).offset(6.00,0.00).radius(2.50).filled().send(); dwgsPtr->circle().color(7).offset(-6.00,0.00).radius(2.50).filled().send(); dwgsPtr->index().idx(_update_idx_13).send(); dwgsPtr->index().idx(_update_idx_14).send(); dwgsPtr->index().idx(_update_idx_15).send(); dwgsPtr->touchZone().cmd(_touchZone_cmd_5).send(); } void OnOffSlider::updateDwg() { dwgsPtr->rectangle().idx(_update_idx_13).color(7).offset(0.00,0.00).size(12.00,5.00).filled().centered().send(); dwgsPtr->circle().idx(_update_idx_14).color(7).offset(6.00,0.00).radius(5.00).filled().send(); dwgsPtr->circle().idx(_update_idx_15).color(0).offset(6.00,0.00).radius(1.00).filled().send(); dwgsPtr->touchZone().cmd(_touchZone_cmd_5).offset(0.00,0.00).size(22.00,10.00).centered().send(); dwgsPtr->touchAction().cmd(_touchZone_cmd_5).action( dwgsPtr->circle().idx(_update_idx_15).color(0).offset(-6.00,0.00).radius(1.00).filled() ).send(); dwgsPtr->touchAction().cmd(_touchZone_cmd_5).action( dwgsPtr->circle().idx(_update_idx_14).color(10).offset(-6.00,0.00).radius(5.00).filled() ).send(); dwgsPtr->touchAction().cmd(_touchZone_cmd_5).action( dwgsPtr->rectangle().idx(_update_idx_13).color(10).offset(0.00,0.00).size(12.00,5.00).filled().centered() ).send(); } bool OnOffSlider::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.print("\n"); //======= 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 OnOffSlider::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 OnOffSlider::sendUpdate() { dwgsPtr->startUpdate(); // dwg refresh updates must be <1024 bytes, i.e. can only be 1 msg updateDwg(); dwgsPtr->end(); } void OnOffSlider::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 OnOffSlider.cpp generated code ========== // end of OnOffSlider 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 /* (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 Provided this copyright is maintained. */ #include #ifndef ONOFFSLIDER_H #include "OnOffSlider.h" #endif #include #include #include const unsigned long BAUD_RATE = 115200ul; // debug baud rate if connection is not Serial pfodSecurity parser("V1"); // create a parser with menu version string to handle the pfod messages pfodDwgs dwgs(&parser); // drawing support // add your pfod Password here for 128bit security // eg "b0Ux9akSiwKkwCtcnjTnpWp" but generate your own key, "" means no pfod password #define pfodSecurityCode "" // see http://www.forward.com.au/pfod/ArduinoWiFi_simple_pfodDevice/index.html for more information and an example // and QR image key generator. pfodESPBufferedClient bufferedClient; 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 it goes back to the previous menu/screen OnOffSlider _onoffslider(&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,25,1); // position and scale the inserted designed dwg dwgs.insertDwg().loadCmd(_onoffslider).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 } 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 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 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 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()); parser.connect(bufferedClient.connect(&pfodServerClients[i]), F(pfodSecurityCode)); // sets new io stream to read from and write to 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 ==========