diff --git a/.gitignore b/.gitignore index 151ca5a..2f5ae31 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,5 @@ Release/* .clang_complete .gcc-flags.json .pio/ -.vscode/ +.vscode/* config.h -.vscode/extensions.json diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index dbce4e7..f6d16ad 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -57,7 +57,12 @@ void setup() // Responsibility 1: Start the usb connection for diagnostics // This is normally Serial but uses SerialUSB on a SAMD processor + // Uno Wifi Rev2 is stupid wont read past this baud rate for some reason + #ifdef ARDUINO_AVR_UNO_WIFI_REV2 + Serial.begin(74880); + #else Serial.begin(115200); + #endif DIAG(F("DCC++ EX v%S"),F(VERSION)); CONDITIONAL_LCD_START { diff --git a/StringFormatter.cpp b/StringFormatter.cpp index 204e0df..3a61867 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -22,7 +22,7 @@ #if defined(ARDUINO_ARCH_SAMD) // Some processors use a gcc compiler that renames va_list!!! #include - Print * StringFormatter::diagSerial= &SerialUSB; + Print * StringFormatter::diagSerial= &SerialUSB; #else Print * StringFormatter::diagSerial=&Serial; #endif diff --git a/WifiInterfaceRev2.cpp b/WifiInterfaceRev2.cpp index 294d290..009f699 100644 --- a/WifiInterfaceRev2.cpp +++ b/WifiInterfaceRev2.cpp @@ -1,5 +1,5 @@ /* - * © 2021, Chris Harlow. All rights reserved. + * © 2021, Chris Harlow, Anthony Williams. All rights reserved. * * This file is part of DCC-EX/CommandStation-EX * @@ -18,86 +18,292 @@ * */ #ifdef ARDUINO_AVR_UNO_WIFI_REV2 -// This code is ONLY compiled on a unoWifiRev2 processor which uses a different architecture +// This code is ONLY compiled on a unoWifiRev2 processor which uses a different architecture #include "WifiInterfaceRev2.h" #include "DIAG.h" #include "CommandDistributor.h" -#include -#include +WifiInterface * WifiInterface::singleton; WiFiServer WifiInterface::server(2560); -bool WifiInterface::connected=false; +bool WifiInterface::connected = false; /** * @brief Setup Wifi Connection * */ - - bool WifiInterface::setup(long serial_link_speed, + +bool WifiInterface::setup(long serial_link_speed, const FSH *wifiESSID, const FSH *wifiPassword, const FSH *hostname, - const int port) { - (void)serial_link_speed; - (void)port; // obsolete - (void)hostname; // To be implemented - - if (WiFi.status() == WL_NO_MODULE) { - DIAG(F("Wifi- hardware failed\n")); - return false; - } - DIAG(F("Wifi Firmware=%s expected=%S"),WiFi.firmwareVersion(),F(WIFI_FIRMWARE_LATEST_VERSION)); + const int port) +{ + singleton = new WifiInterface(serial_link_speed, wifiESSID, wifiPassword, hostname, port); + //return singleton->connected; +} +WifiInterface::WifiInterface(long serial_link_speed, + const FSH *wifiESSID, + const FSH *wifiPassword, + const FSH *hostname, + const int port) +{ + (void)serial_link_speed; + (void)port; // obsolete + (void)hostname; // To be implemented + + if (WiFi.status() == WL_NO_MODULE) + { + DIAG(F("Wifi- hardware failed\n")); + } + DIAG(F("Wifi Firmware=%s expected=%S"), WiFi.firmwareVersion(), F(WIFI_FIRMWARE_LATEST_VERSION)); int status = WL_IDLE_STATUS; int attempts = 4; - while (status != WL_CONNECTED) { - if (attempts-- <= 0) { + while (status != WL_CONNECTED) + { + if (attempts-- <= 0) + { DIAG(F("\nFAILED - No Wifi\n")); - return false; } - DIAG(F("\nAttempting to connect to %s\n"),wifiESSID); + DIAG(F("\nAttempting to connect to %s\n"), wifiESSID); status = WiFi.begin(wifiESSID, wifiPassword); // wait 10 seconds for connection: delay(10000); } - server.begin(); // start the server on port 2560 + server.begin(); // start the server on port 2560 IPAddress ip = WiFi.localIP(); - LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); - LCD(5,F("Port:2560")); - outboundRing=new RingStream(OUTBOUND_RING_SIZE); + LCD(4, F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); + LCD(5, F("Port:2560")); +} + +void WifiInterface::loop() { + singleton->loop1(); } /** * @brief Main loop for the WifiInterfaceRev2 * */ -void WifiInterface::loop() +void WifiInterface::loop1() { - WiFiClient client = server.available(); // listen for incoming clients - if (client) - { - // read bytes from a client - byte buffer[MAX_NINA_BUFFER]; - int count = client.read(buffer, MAX_NINA_BUFFER-1); - buffer[count] = '\0'; // terminate the string properly - if (Diag::WIFI) DIAG(F("WIFI:%e\n"), buffer); - // TEMPORARY - Assume all clients are client 1, this will confuse WiThrottle! - outboundRing->mark(1); - // TEMPORARY - Assume all clients are client 1, this will confuse WiThrottle! - CommandDistributor::parse(1,buffer,outboundRing); - outboundRing->commit(); - int socketOut=outboundRing->read(); - if (socketOut>=0) { - int count=outboundRing->count(); - if (Diag::WIFI) DIAG(F("Wifi Reply count=:%d\n"), count); - for(;count>0;count--) client.write(outboundRing->read()); - client.flush(); //maybe - } - } + + if (loop2() != INBOUND_IDLE) + return; + // WiFiClient client = server.available(); // listen for incoming clients + // if (client) + // { + // // read bytes from a client + // byte buffer[MAX_NINA_BUFFER]; + // int count = client.read(buffer, MAX_NINA_BUFFER - 1); + // buffer[count] = '\0'; // terminate the string properly + // if (Diag::WIFI) + // DIAG(F("WIFI:%e\n"), buffer); + // // TEMPORARY - Assume all clients are client 1, this will confuse WiThrottle! + // outboundRing->mark(1); + // // TEMPORARY - Assume all clients are client 1, this will confuse WiThrottle! + // CommandDistributor::parse(1, buffer, outboundRing); + // outboundRing->commit(); + // int socketOut = outboundRing->read(); + // if (socketOut >= 0) + // { + // int count = outboundRing->count(); + // if (Diag::WIFI) + // DIAG(F("Wifi Reply count=:%d\n"), count); + // for (; count > 0; count--) + // client.write(outboundRing->read()); + // client.flush(); //maybe + // } + // } } +WifiInterface::INBOUND_STATE WifiInterface::loop2() +{ + WiFiClient client = server.available(); // listen for incoming clients + if (client) + { + while (client.available()) + { + int ch = client.read(); + + // echo the char to the diagnostic stream in escaped format + if (Diag::WIFI) + { + // DIAG(F(" %d/"), loopState); + StringFormatter::printEscape(ch); // DIAG in disguise + } + + switch (loopState) + { + case ANYTHING: // looking for +IPD, > , busy , n,CONNECTED, n,CLOSED, ERROR, SEND OK + + if (ch == '+') + { + loopState = IPD; + break; + } + + if (ch == '>') + { + if (Diag::WIFI) + DIAG(F("[XMIT %d]"), currentReplySize); + // for (int i = 0; i < currentReplySize; i++) + // { + // int cout = outboundRing->read(); + // client.print(cout); + // if (Diag::WIFI) + // StringFormatter::printEscape(cout); // DIAG in disguise + // } + clientPendingCIPSEND = -1; + pendingCipsend = false; + loopState = SKIPTOEND; + break; + } + + if (ch == 'R') + { // Received ... bytes + loopState = SKIPTOEND; + break; + } + + if (ch == 'S') + { // SEND OK probably + loopState = SKIPTOEND; + break; + } + + if (ch == 'b') + { // This is a busy indicator... probabaly must restart a CIPSEND + pendingCipsend = (clientPendingCIPSEND >= 0); + loopState = SKIPTOEND; + break; + } + + if (ch >= '0' && ch <= '9') + { + runningClientId = ch - '0'; + loopState = GOT_CLIENT_ID; + break; + } + + if (ch == 'E' || ch == 'l') + { // ERROR or "link is not valid" + if (clientPendingCIPSEND >= 0) + { + // A CIPSEND was errored... just toss it away + //purgeCurrentCIPSEND(); + } + loopState = SKIPTOEND; + break; + } + + break; + + case IPD: // Looking for I in +IPD + loopState = (ch == 'I') ? IPD1 : SKIPTOEND; + break; + + case IPD1: // Looking for P in +IPD + loopState = (ch == 'P') ? IPD2 : SKIPTOEND; + break; + + case IPD2: // Looking for D in +IPD + loopState = (ch == 'D') ? IPD3 : SKIPTOEND; + break; + + case IPD3: // Looking for , After +IPD + loopState = (ch == ',') ? IPD4_CLIENT : SKIPTOEND; + break; + + case IPD4_CLIENT: // reading connection id + if (ch >= '0' || ch <= '9') + { + runningClientId = ch - '0'; + loopState = IPD5; + } + else + loopState = SKIPTOEND; + break; + + case IPD5: // Looking for , After +IPD,client + loopState = (ch == ',') ? IPD6_LENGTH : SKIPTOEND; + dataLength = 0; // ready to start collecting the length + break; + + case IPD6_LENGTH: // reading for length + if (ch == ':') + { + if (dataLength == 0) + { + loopState = ANYTHING; + break; + } + if (Diag::WIFI) + DIAG(F("\nWifi inbound data(%d:%d):"), runningClientId, dataLength); + // if (server.freeSpace() <= (dataLength + 1)) + // { + // // This input would overflow the inbound ring, ignore it + // loopState = IPD_IGNORE_DATA; + // if (Diag::WIFI) + // DIAG(F("\nWifi OVERFLOW IGNORING:")); + // break; + // } + // server.mark(runningClientId); + loopState = IPD_DATA; + break; + } + dataLength = dataLength * 10 + (ch - '0'); + break; + + case IPD_DATA: // reading data + server.write(ch); + dataLength--; + if (dataLength == 0) + { + //server.commit(); + loopState = ANYTHING; + } + break; + + case IPD_IGNORE_DATA: // ignoring data that would not fit in inbound ring + dataLength--; + if (dataLength == 0) + loopState = ANYTHING; + break; + + case GOT_CLIENT_ID: // got x before CLOSE or CONNECTED + loopState = (ch == ',') ? GOT_CLIENT_ID2 : SKIPTOEND; + break; + + case GOT_CLIENT_ID2: // got "x," + if (ch == 'C') + { + // got "x C" before CLOSE or CONNECTED, or CONNECT FAILED + //if (runningClientId == clientPendingCIPSEND) + //purgeCurrentCIPSEND(); + } + loopState = SKIPTOEND; + break; + + case SKIPTOEND: // skipping for /n + if (ch == '\n') + loopState = ANYTHING; + break; + } // switch + } + } // available + return (loopState == ANYTHING) ? INBOUND_IDLE : INBOUND_BUSY; +} + +// void WifiInboundHandler::purgeCurrentCIPSEND() { +// // A CIPSEND was sent but errored... or the client closed just toss it away +// if (Diag::WIFI) DIAG(F("Wifi: DROPPING CIPSEND=%d,%d\n"),clientPendingCIPSEND,currentReplySize); +// for (int i=0;i<=currentReplySize;i++) outboundRing->read(); +// pendingCipsend=false; +// clientPendingCIPSEND=-1; +// } + #endif diff --git a/WifiInterfaceRev2.h b/WifiInterfaceRev2.h index 2afb882..056a8ab 100644 --- a/WifiInterfaceRev2.h +++ b/WifiInterfaceRev2.h @@ -30,15 +30,60 @@ class WifiInterface { public: - static bool setup(long serial_link_speed, // ignored - const FSH *wifiESSID, - const FSH *wifiPassword, - const FSH *hostname, - const int port = 2560); // ignored + static bool setup(long serial_link_speed, // ignored + const FSH *wifiESSID, + const FSH *wifiPassword, + const FSH *hostname, + const int port = 2560); // ignored static void loop(); + private: - static WiFiServer server; - static bool connected; - static RingStream * outboundRing; + static WifiInterface *singleton; + + WifiInterface(long serial_link_speed, + const FSH *wifiESSID, + const FSH *wifiPassword, + const FSH *hostname, + const int port); + + enum INBOUND_STATE : byte + { + INBOUND_BUSY, // keep calling in loop() + INBOUND_IDLE // Nothing happening, outbound may xcall CIPSEND + }; + + enum LOOP_STATE : byte + { + ANYTHING, // ready for +IPD, n CLOSED, n CONNECTED, busy etc... + SKIPTOEND, // skip to newline + + // +IPD,client,length:data + IPD, // got + + IPD1, // got +I + IPD2, // got +IP + IPD3, // got +IPD + IPD4_CLIENT, // got +IPD, reading cient id + IPD5, // got +IPD,c + IPD6_LENGTH, // got +IPD,c, reading length + IPD_DATA, // got +IPD,c,ll,: collecting data + IPD_IGNORE_DATA, // got +IPD,c,ll,: ignoring the data that won't fit inblound Ring + + GOT_CLIENT_ID, // clientid prefix to CONNECTED / CLOSED + GOT_CLIENT_ID2 // clientid prefix to CONNECTED / CLOSED + }; + + LOOP_STATE loopState = ANYTHING; + int runningClientId; // latest client inbound processing data or CLOSE + int dataLength; // dataLength of +IPD + int clientPendingCIPSEND = -1; + int currentReplySize; + bool pendingCipsend; + void purgeCurrentCIPSEND(); + + static WiFiServer server; + static bool connected; + //static RingStream *outboundRing; + void loop1(); + INBOUND_STATE loop2(); }; #endif diff --git a/defines.h b/defines.h index 6db42e1..6108aa3 100644 --- a/defines.h +++ b/defines.h @@ -23,7 +23,7 @@ // WIFI_ON: All prereqs for running with WIFI are met // -#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO)) +#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO)) || defined(ARDUINO_AVR_UNO_WIFI_REV2) #define WIFI_ON true #else #define WIFI_ON false diff --git a/platformio.ini b/platformio.ini index 468a3b3..83ea030 100644 --- a/platformio.ini +++ b/platformio.ini @@ -63,10 +63,11 @@ framework = arduino lib_deps = ${env.lib_deps} DIO2 + WiFiNINA arduino-libraries/Ethernet SPI marcoschwartz/LiquidCrystal_I2C -monitor_speed = 115200 +monitor_speed = 74880 monitor_flags = --echo build_flags = "-DF_CPU=16000000L -DARDUINO=10813 -DARDUINO_AVR_UNO_WIFI_DEV_ED -DARDUINO_ARCH_AVR -DESP_CH_UART -DESP_CH_UART_BR=19200"g