From d46a6f092a9a9fb991af22ccabe849fa9cf50c78 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Wed, 1 Nov 2023 17:49:24 +0800 Subject: [PATCH] Giga Wifi driver, based on WifiNINA driver --- CommandStation-EX.ino | 8 + DCCEX.h | 6 + Wifi_NINA.cpp | 367 ++++++++++++++++++++++++++++++++++++++++++ Wifi_NINA.h | 42 +++++ defines.h | 3 + version.h | 3 +- 6 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 Wifi_NINA.cpp create mode 100644 Wifi_NINA.h diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 77e8f40e..073a6ff2 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -96,7 +96,11 @@ void setup() // Start Ethernet if it exists #ifndef ARDUINO_ARCH_ESP32 #if WIFI_ON +#ifndef WIFI_NINA WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP); +#else + WifiNINA::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP); +#endif // WIFI_NINA #endif // WIFI_ON #else // ESP32 needs wifi on always @@ -144,7 +148,11 @@ void loop() // Responsibility 3: Optionally handle any incoming WiFi traffic #ifndef ARDUINO_ARCH_ESP32 #if WIFI_ON +#ifndef WIFI_NINA WifiInterface::loop(); +#else + WifiNINA::loop(); +#endif //WIFI_NINA #endif //WIFI_ON #else //ARDUINO_ARCH_ESP32 #ifndef WIFI_TASK_ON_CORE0 diff --git a/DCCEX.h b/DCCEX.h index 2dc8eb71..1637b1ba 100644 --- a/DCCEX.h +++ b/DCCEX.h @@ -1,4 +1,5 @@ /* + * © 2023 Paul M. Antoine * © 2021 Fred Decker * © 2020-2021 Harald Barth * © 2020-2021 Chris Harlow @@ -33,8 +34,13 @@ #include "SerialManager.h" #include "version.h" #ifndef ARDUINO_ARCH_ESP32 +#ifdef WIFI_NINA +#include "Wifi_NINA.h" +#else #include "WifiInterface.h" +#endif // WIFI_NINA #else +#undef WIFI_NINA #include "WifiESP32.h" #endif #if ETHERNET_ON == true diff --git a/Wifi_NINA.cpp b/Wifi_NINA.cpp new file mode 100644 index 00000000..8d540302 --- /dev/null +++ b/Wifi_NINA.cpp @@ -0,0 +1,367 @@ +/* + © 2023 Paul M. Antoine + © 2021 Harald Barth + © 2023 Nathan Kellenicki + + This file is part of CommandStation-EX + + This is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + It is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CommandStation. If not, see . +*/ +#include "defines.h" + +#ifdef WIFI_NINA +#include +#include +#ifndef ARDUINO_GIGA +#include +#else +#include +#endif +#include "Wifi_NINA.h" +// #include "ESPmDNS.h" +// #include +// #include "esp_wifi.h" +// #include "WifiESP32.h" +// #include +#include "DIAG.h" +#include "RingStream.h" +#include "CommandDistributor.h" +#include "WiThrottle.h" + +// Configure the pins used for the ESP32 connection +#if !defined(ARDUINO_GIGA) && defined(ARDUINO_ARCH_STM32) // Here my STM32 configuration + #define SPIWIFI SPI // The SPI port + #define SPIWIFI_SS PA4 // Chip select pin + #define ESP32_RESETN PA10 // Reset pin + #define SPIWIFI_ACK PB3 // a.k.a BUSY or READY pin + #define ESP32_GPIO0 -1 +#else +#warning "WiFiNINA has no SPI port or pin allocations for this archiecture yet!" +#endif + +class NetworkClient { +public: + NetworkClient(WiFiClient c) { + wifi = c; + }; + bool ok() { + return (inUse && wifi.connected()); + }; + bool recycle(WiFiClient c) { + + if (inUse == true) return false; + + // return false here until we have + // implemented a LRU timer + // if (LRU too recent) return false; + return false; + + wifi = c; + inUse = true; + return true; + }; + WiFiClient wifi; + bool inUse = true; +}; + +static std::vector clients; // a list to hold all clients +static WiFiServer *server = NULL; +static RingStream *outboundRing = new RingStream(10240); +static bool APmode = false; +static IPAddress ip; + +// #ifdef WIFI_TASK_ON_CORE0 +// void wifiLoop(void *){ +// for(;;){ +// WifiNINA::loop(); +// } +// } +// #endif + +char asciitolower(char in) { + if (in <= 'Z' && in >= 'A') + return in - ('Z' - 'z'); + return in; +} + +bool WifiNINA::setup(const char *SSid, + const char *password, + const char *hostname, + int port, + const byte channel, + const bool forceAP) { + bool havePassword = true; + bool haveSSID = true; + bool wifiUp = false; + uint8_t tries = 40; + + // Set up the pins! +#ifndef ARDUINO_GIGA + WiFi.setPins(SPIWIFI_SS, SPIWIFI_ACK, ESP32_RESETN, ESP32_GPIO0, &SPIWIFI); +#endif + // check for the WiFi module: + if (WiFi.status() == WL_NO_MODULE) { + DIAG(F("Communication with WiFi module failed!")); + // don't continue for now! + while (true); + } + + // Print firmware version on the module + String fv = WiFi.firmwareVersion(); + DIAG(F("WifiNINA Firmware version found:%s"), fv.c_str()); + + // clean start + // WiFi.mode(WIFI_STA); + // WiFi.disconnect(true); + // differnet settings that did not improve for haba + // WiFi.useStaticBuffers(true); + // WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); + // WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SECURITY); + + const char *yourNetwork = "Your network "; + if (strncmp(yourNetwork, SSid, 13) == 0 || strncmp("", SSid, 13) == 0) + haveSSID = false; + if (strncmp(yourNetwork, password, 13) == 0 || strncmp("", password, 13) == 0) + havePassword = false; + + if (haveSSID && havePassword && !forceAP) { +#ifndef ARDUINO_GIGA + WiFi.setHostname(hostname); // Strangely does not work unless we do it HERE! +#endif + // WiFi.mode(WIFI_STA); + // WiFi.setAutoReconnect(true); + WiFi.begin(SSid, password); + while (WiFi.status() != WL_CONNECTED && tries) { + Serial.print('.'); + tries--; + delay(500); + } + if (WiFi.status() == WL_CONNECTED) { + // String ip_str = sprintf("%xl", WiFi.localIP()); + DIAG(F("Wifi STA IP %d.%d.%d.%d"), WiFi.localIP()[0], WiFi.localIP()[1],WiFi.localIP()[2],WiFi.localIP()[3],WiFi.localIP()[4],WiFi.localIP()[5]); + wifiUp = true; + } else { + DIAG(F("Could not connect to Wifi SSID %s"),SSid); + DIAG(F("Forcing one more Wifi restart")); + // esp_wifi_start(); + // esp_wifi_connect(); + tries=40; + while (WiFi.status() != WL_CONNECTED && tries) { + Serial.print('.'); + tries--; + delay(500); + } + if (WiFi.status() == WL_CONNECTED) { + ip = WiFi.localIP(); + DIAG(F("Wifi STA IP 2nd try %s"), ip); + wifiUp = true; + } else { + DIAG(F("Wifi STA mode FAIL. Will revert to AP mode")); + haveSSID=false; + } + } + } + if (!haveSSID || forceAP) { + // prepare all strings + String strSSID(forceAP ? SSid : "DCCEX_"); + String strPass(forceAP ? password : "PASS_"); + if (!forceAP) { + byte mac[6]; + WiFi.macAddress(mac); + String strMac; + for (int i = 0; i++; i < 6) { + strMac += String(mac[i], HEX); + } + + DIAG(F("MAC address: %x:%x:%x:%x:%X;%x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + strMac.remove(0,9); + strMac.replace(":",""); + strMac.replace(":",""); + // convert mac addr hex chars to lower case to be compatible with AT software + std::transform(strMac.begin(), strMac.end(), strMac.begin(), asciitolower); + strSSID.concat(strMac); + strPass.concat(strMac); + } + + if (WiFi.beginAP(strSSID.c_str(), + havePassword ? password : strPass.c_str(), + channel) == WL_AP_LISTENING) { + DIAG(F("Wifi AP SSID %s PASS %s"),strSSID.c_str(),havePassword ? password : strPass.c_str()); + ip = WiFi.localIP(); + DIAG(F("Wifi AP IP %s"),ip); + wifiUp = true; + APmode = true; + } else { + DIAG(F("Could not set up AP with Wifi SSID %s"),strSSID.c_str()); + } + } + + + if (!wifiUp) { + DIAG(F("Wifi setup all fail (STA and AP mode)")); + // no idea to go on + return false; + } + + // TODO: we need to run the MDNS_Generic server I suspect + // // Now Wifi is up, register the mDNS service + // if(!MDNS.begin(hostname)) { + // DIAG(F("Wifi setup failed to start mDNS")); + // } + // if(!MDNS.addService("withrottle", "tcp", 2560)) { + // DIAG(F("Wifi setup failed to add withrottle service to mDNS")); + // } + + server = new WiFiServer(port); // start listening on tcp port + server->begin(); + // server started here + +// #ifdef WIFI_TASK_ON_CORE0 +// //start loop task +// if (pdPASS != xTaskCreatePinnedToCore( +// wifiLoop, /* Task function. */ +// "wifiLoop",/* name of task. */ +// 10000, /* Stack size of task */ +// NULL, /* parameter of the task */ +// 1, /* priority of the task */ +// NULL, /* Task handle to keep track of created task */ +// 0)) { /* pin task to core 0 */ +// DIAG(F("Could not create wifiLoop task")); +// return false; +// } + +// // report server started after wifiLoop creation +// // when everything looks good +// DIAG(F("Server starting (core 0) port %d"),port); +// #else + DIAG(F("Server will be started on port %d"),port); +// #endif + return true; +} + +const char *wlerror[] = { + "WL_IDLE_STATUS", + "WL_NO_SSID_AVAIL", + "WL_SCAN_COMPLETED", + "WL_CONNECTED", + "WL_CONNECT_FAILED", + "WL_CONNECTION_LOST", + "WL_DISCONNECTED" +}; + +void WifiNINA::loop() { + int clientId; //tmp loop var + + // really no good way to check for LISTEN especially in AP mode? + wl_status_t wlStatus; + if (APmode || (wlStatus = (wl_status_t)WiFi.status()) == WL_CONNECTED) { + // loop over all clients and remove inactive + for (clientId=0; clientIdavailable()) { + WiFiClient client; + while (client = server->available()) { + for (clientId=0; clientId=clients.size()) { + NetworkClient nc(client); + clients.push_back(nc); + ip = client.remoteIP(); + DIAG(F("New client %d, %s"), clientId, ip); + } + } + } + // loop over all connected clients + for (clientId=0; clientId 0) { + // read data from client + byte cmd[len+1]; + for(int i=0; iread(); + if (clientId >= 0) { + // We have data to send in outboundRing + // and we have a valid clientId. + // First read it out to buffer + // and then look if it can be sent because + // we can not leave it in the ring for ever + int count=outboundRing->count(); + { + char buffer[count+1]; // one extra for '\0' + for(int i=0;iread(); + if (c >= 0) // Panic check, should never be false + buffer[i] = (char)c; + else { + DIAG(F("Ringread fail at %d"),i); + break; + } + } + // buffer filled, end with '\0' so we can use it as C string + buffer[count]='\0'; + if((unsigned int)clientId <= clients.size() && clients[clientId].ok()) { + if (Diag::CMD || Diag::WITHROTTLE) + DIAG(F("SEND %d:%s"), clientId, buffer); + clients[clientId].wifi.write(buffer,count); + } else { + DIAG(F("Unsent(%d): %s"), clientId, buffer); + } + } + } + } else if (!APmode) { // in STA mode but not connected any more + // kick it again + if (wlStatus <= 6) { + DIAG(F("Wifi aborted with error %s. Kicking Wifi!"), wlerror[wlStatus]); + // esp_wifi_start(); + // esp_wifi_connect(); + uint8_t tries=40; + while (WiFi.status() != WL_CONNECTED && tries) { + Serial.print('.'); + tries--; + delay(500); + } + } else { + // all well, probably + //DIAG(F("Running BT")); + } + } +} +#endif // WIFI_NINA \ No newline at end of file diff --git a/Wifi_NINA.h b/Wifi_NINA.h new file mode 100644 index 00000000..f841b4f3 --- /dev/null +++ b/Wifi_NINA.h @@ -0,0 +1,42 @@ +/* + * © 2023 Paul M. Antoine + * © 2021 Harald Barth + * © 2023 Nathan Kellenicki + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +#ifndef WifiNINA_h +#define WifiNINA_h +// #include "FSH.h" +#include +// #include +// #include + +class WifiNINA +{ + +public: + static bool setup(const char *wifiESSID, + const char *wifiPassword, + const char *hostname, + const int port, + const byte channel, + const bool forceAP); + static void loop(); +private: +}; +#endif //WifiNINA_h diff --git a/defines.h b/defines.h index a8499d6d..8c5ada46 100644 --- a/defines.h +++ b/defines.h @@ -153,6 +153,9 @@ #ifndef GIGA_EXT_EEPROM #define DISABLE_EEPROM #endif + #if defined(ENABLE_WIFI) && !defined(WIFI_NINA) + #define WIFI_NINA + #endif //#if !defined(I2C_USE_WIRE) //#define I2C_USE_WIRE //#endif diff --git a/version.h b/version.h index 4daafba2..f0975ce8 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.17" +#define VERSION "5.1.17gw" +// 5.1.17gw - Giga support by Travis Farmer, with WifiNINA integrated to see if it works // 5.1.17 - Divide out C for config and D for diag commands // 5.1.16 - Remove I2C address from EXTT_TURNTABLE macro to work with MUX, requires separate HAL macro to create // 5.1.15 - LCC/Adapter support and Exrail feature-compile-out.