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.