// 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 Push_Button.h generated code ========== #ifndef PUSH_BUTTON_H #define PUSH_BUTTON_H // Push_Button.h from Push_Button 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 Push_Button : public pfodDrawing { public: Push_Button(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_7; pfodAutoIdx _idx_1; pfodAutoCmd _touchZone_cmd_4; }; #endif // ========= End of Push_Button.h generated code ========== // split here for .cpp file // ========= Start of Push_Button.cpp generated code ========== // Push_Button.cpp from Push_Button 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 PUSH_BUTTON_H #include "Push_Button.h" #endif Push_Button::Push_Button(pfodParser* parserPtr, pfodDwgs* dwgsPtr) : pfodDrawing(parserPtr, dwgsPtr) { forceReload = true; // force reload on reboot, e.g. reprogrammed } bool Push_Button::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_4)) { // handle _touchZone_cmd_.. Serial.print(" Got pfodAutoCmd:"); Serial.println("_touchZone_cmd_4"); printDwgCmdReceived(&Serial); // does nothing if passed NULL // add your cmd handling code here // send update sendUpdate(); return true; } return false; // not handled } void Push_Button::drawDwg0() { dwgsPtr->index().idx(_update_idx_7).send(); dwgsPtr->circle().idx(_idx_1).color(0).offset(0.00,0.00).radius(5.00).send(); dwgsPtr->touchZone().cmd(_touchZone_cmd_4).offset(0.00,0.00).size(10.00,10.00).centered().send(); dwgsPtr->touchAction().cmd(_touchZone_cmd_4).action( dwgsPtr->hide().idx(_update_idx_7) ).send(); } void Push_Button::updateDwg() { dwgsPtr->circle().idx(_update_idx_7).color(10).offset(0.00,0.00).radius(5.00).filled().send(); } bool Push_Button::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 Push_Button::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 Push_Button::sendUpdate() { dwgsPtr->startUpdate(); // dwg refresh updates must be <1024 bytes, i.e. can only be 1 msg updateDwg(); dwgsPtr->end(); } void Push_Button::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 Push_Button.cpp generated code ========== // end of Push_Button 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 PUSH_BUTTON_H #include "Push_Button.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 Push_Button _push_button(&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(_push_button).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 ==========