From 1900a08bf0e82cdcc608d650c8e60a3d946968ca Mon Sep 17 00:00:00 2001 From: Alexander Balya Date: Thu, 14 May 2026 22:42:08 +0200 Subject: [PATCH] feat(esp32): add native Ethernet support --- .gitignore | 12 ++ src/DebugConfiguration.h | 16 +-- src/main.cpp | 4 +- src/mesh/InterfacesTemplates.cpp | 8 +- src/mesh/api/ServerAPI.cpp | 49 ++++++-- src/mesh/api/ServerAPI.h | 14 ++- src/mesh/api/WiFiServerAPI.cpp | 4 +- src/mesh/api/WiFiServerAPI.h | 6 +- src/mesh/api/ethServerAPI.cpp | 31 +++-- src/mesh/api/ethServerAPI.h | 25 +++- src/mesh/eth/ethClient.cpp | 2 +- src/mesh/wifi/WiFiAPClient.cpp | 112 +++++++++++++----- src/mesh/wifi/WiFiAPClient.h | 15 ++- src/modules/AdminModule.cpp | 30 ++++- src/mqtt/MQTT.h | 16 ++- variants/esp32/wt32-eth01-e22/platformio.ini | 22 ++++ variants/esp32/wt32-eth01-e22/variant.h | 52 ++++++++ .../diy/esp32s3-e22-w5500/platformio.ini | 33 ++++++ .../esp32s3/diy/esp32s3-e22-w5500/variant.h | 71 +++++++++++ 19 files changed, 435 insertions(+), 87 deletions(-) create mode 100644 variants/esp32/wt32-eth01-e22/platformio.ini create mode 100644 variants/esp32/wt32-eth01-e22/variant.h create mode 100644 variants/esp32s3/diy/esp32s3-e22-w5500/platformio.ini create mode 100644 variants/esp32s3/diy/esp32s3-e22-w5500/variant.h diff --git a/.gitignore b/.gitignore index eebd94ef9c2..560c9b8fae6 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,20 @@ web.tar .platformio .local .cache +.history +# macOS .DS_Store +.DS_Store? +._* +.AppleDouble +.LSOverride +.Spotlight-V100 +.Trashes +.fseventsd +.AppleDB +.AppleDesktop + Thumbs.db .autotools .built diff --git a/src/DebugConfiguration.h b/src/DebugConfiguration.h index a78dde78e31..9e348364e11 100644 --- a/src/DebugConfiguration.h +++ b/src/DebugConfiguration.h @@ -152,16 +152,18 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...); // Default Bluetooth PIN #define defaultBLEPin 123456 -#if HAS_ETHERNET && defined(USE_CH390D) +#if HAS_ETHERNET +#if defined(USE_CH390D) #include -#elif HAS_ETHERNET && !defined(USE_WS5500) -#include -#endif // HAS_ETHERNET - -#if HAS_ETHERNET && defined(USE_WS5500) +#elif defined(USE_WS5500) #include #define ETH ETH2 -#endif // HAS_ETHERNET +#elif defined(ESP32) && defined(ETH_PHY_TYPE) +#include +#else +#include +#endif +#endif #if HAS_WIFI #include diff --git a/src/main.cpp b/src/main.cpp index 28842734c89..8d8ec698d66 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,15 +59,17 @@ NimbleBluetooth *nimbleBluetooth = nullptr; NRF52Bluetooth *nrf52Bluetooth = nullptr; #endif -#if HAS_WIFI || defined(USE_WS5500) || defined(USE_CH390D) +#if HAS_WIFI || defined(USE_WS5500) || defined(USE_CH390D) || defined(ETH_PHY_TYPE) #include "mesh/api/WiFiServerAPI.h" #include "mesh/wifi/WiFiAPClient.h" #endif #if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D) #include "mesh/api/ethServerAPI.h" +#if !defined(ETH_PHY_TYPE) #include "mesh/eth/ethClient.h" #endif +#endif #if !MESHTASTIC_EXCLUDE_MQTT #include "mqtt/MQTT.h" diff --git a/src/mesh/InterfacesTemplates.cpp b/src/mesh/InterfacesTemplates.cpp index 6f6b2d2a14d..2974e357911 100644 --- a/src/mesh/InterfacesTemplates.cpp +++ b/src/mesh/InterfacesTemplates.cpp @@ -34,12 +34,14 @@ template class SX126xInterface; #if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D) #include "api/ethServerAPI.h" -template class ServerAPI; -template class APIServerPort; +#if !(defined(ESP32) && HAS_WIFI) +template class ServerAPI; +#endif +template class APIServerPort; #endif #if HAS_WIFI #include "api/WiFiServerAPI.h" template class ServerAPI; template class APIServerPort; -#endif \ No newline at end of file +#endif diff --git a/src/mesh/api/ServerAPI.cpp b/src/mesh/api/ServerAPI.cpp index f3e7854caee..ce3b1cf094e 100644 --- a/src/mesh/api/ServerAPI.cpp +++ b/src/mesh/api/ServerAPI.cpp @@ -2,8 +2,18 @@ #include "Throttle.h" #include "configuration.h" #include +#include -static constexpr uint32_t TCP_IDLE_TIMEOUT_MS = 15 * 60 * 1000UL; +#ifndef MESHTASTIC_TCP_API_IDLE_TIMEOUT_MS +#define MESHTASTIC_TCP_API_IDLE_TIMEOUT_MS (15 * 60 * 1000UL) +#endif + +#ifndef MESHTASTIC_TCP_API_MAX_CLIENTS +#define MESHTASTIC_TCP_API_MAX_CLIENTS 1 +#endif + +static constexpr uint32_t TCP_IDLE_TIMEOUT_MS = MESHTASTIC_TCP_API_IDLE_TIMEOUT_MS; +static constexpr size_t TCP_API_MAX_CLIENTS = MESHTASTIC_TCP_API_MAX_CLIENTS; template ServerAPI::ServerAPI(T &_client) : StreamAPI(&client), concurrency::OSThread("ServerAPI"), client(_client) @@ -55,9 +65,13 @@ template void APIServerPort::init() template int32_t APIServerPort::runOnce() { - // Clean up previous connection if its client already disconnected - if (openAPI && !openAPI->checkIsConnected()) { - openAPI.reset(); + // Clean up connections whose clients already disconnected. + for (auto api = openAPIs.begin(); api != openAPIs.end();) { + if (!(*api)->checkIsConnected()) { + api = openAPIs.erase(api); + } else { + ++api; + } } #ifdef ARCH_ESP32 @@ -72,12 +86,13 @@ template int32_t APIServerPort::runOnce() auto client = U::available(); #endif if (client) { - // Close any previous connection (see FIXME in header file) - if (openAPI) { + if (openAPIs.size() >= TCP_API_MAX_CLIENTS) { +#if MESHTASTIC_TCP_API_MAX_CLIENTS <= 1 + // Preserve historical single-client behavior unless a variant explicitly opts into a client pool. #if RAK_4631 - // RAK13800 Ethernet requests periodically take more time - // This backoff addresses most cases keeping max wait < 1s - // Reconnections are delayed by full wait time + // RAK13800 Ethernet requests periodically take more time. + // This backoff addresses most cases keeping max wait < 1s. + // Reconnections are delayed by full wait time. if (waitTime < 400) { waitTime *= 2; LOG_INFO("Previous TCP connection still open, try again in %dms", waitTime); @@ -85,14 +100,24 @@ template int32_t APIServerPort::runOnce() } #endif LOG_INFO("Force close previous TCP connection"); - openAPI.reset(); + openAPIs.clear(); +#else + auto oldest = + std::min_element(openAPIs.begin(), openAPIs.end(), [](const std::unique_ptr &a, const std::unique_ptr &b) { + return a->getLastContactMsec() < b->getLastContactMsec(); + }); + if (oldest != openAPIs.end()) { + LOG_WARN("TCP API client limit reached (%u), closing oldest connection", (unsigned)TCP_API_MAX_CLIENTS); + openAPIs.erase(oldest); + } +#endif } - openAPI.reset(new T(client)); + openAPIs.emplace_back(new T(client)); } #if RAK_4631 waitTime = 100; #endif return 100; // only check occasionally for incoming connections -} \ No newline at end of file +} diff --git a/src/mesh/api/ServerAPI.h b/src/mesh/api/ServerAPI.h index 2da77c8e90d..5a3c06fe943 100644 --- a/src/mesh/api/ServerAPI.h +++ b/src/mesh/api/ServerAPI.h @@ -2,6 +2,7 @@ #include "StreamAPI.h" #include +#include #define SERVER_API_DEFAULT_PORT 4403 @@ -25,6 +26,8 @@ template class ServerAPI : public StreamAPI, private concurrency::OSTh /// Check the current underlying physical link to see if the client is currently connected virtual bool checkIsConnected() override; + uint32_t getLastContactMsec() const { return lastContactMsec; } + protected: /// We override this method to prevent publishing EVENT_SERIAL_CONNECTED/DISCONNECTED for wifi links (we want the board to /// stay in the POWERED state to prevent disabling wifi) @@ -38,12 +41,11 @@ template class ServerAPI : public StreamAPI, private concurrency::OSTh */ template class APIServerPort : public U, private concurrency::OSThread { - /** The currently open port - * - * FIXME: We currently only allow one open TCP connection at a time, because we depend on the loop() call in this class to - * delegate to the worker. Once coroutines are implemented we can relax this restriction. - */ - std::unique_ptr openAPI; + /// Active TCP API sessions for this listening port. + /// + /// The listener owns session lifetimes only: each ServerAPI instance is also an OSThread and services its own client from + /// the main controller. Keep this pool bounded because every active TCP API session consumes memory and scheduler time. + std::vector> openAPIs; #if defined(RAK_4631) || defined(RAK11310) // Track wait time for RAK13800 Ethernet requests int32_t waitTime = 100; diff --git a/src/mesh/api/WiFiServerAPI.cpp b/src/mesh/api/WiFiServerAPI.cpp index 4d729f5c71d..6aa3aa6a291 100644 --- a/src/mesh/api/WiFiServerAPI.cpp +++ b/src/mesh/api/WiFiServerAPI.cpp @@ -1,7 +1,7 @@ #include "configuration.h" #include -#if HAS_WIFI +#if HAS_WIFI || defined(USE_WS5500) || defined(USE_CH390D) #include "WiFiServerAPI.h" static WiFiServerPort *apiPort; @@ -30,4 +30,4 @@ WiFiServerAPI::WiFiServerAPI(WiFiClient &_client) : ServerAPI(_client) } WiFiServerPort::WiFiServerPort(int port) : APIServerPort(port) {} -#endif \ No newline at end of file +#endif diff --git a/src/mesh/api/WiFiServerAPI.h b/src/mesh/api/WiFiServerAPI.h index 5f20199830b..0dbfdeae7fe 100644 --- a/src/mesh/api/WiFiServerAPI.h +++ b/src/mesh/api/WiFiServerAPI.h @@ -1,5 +1,8 @@ #pragma once +#include "configuration.h" + +#if HAS_WIFI || defined(USE_WS5500) || defined(USE_CH390D) #include "ServerAPI.h" #include @@ -28,4 +31,5 @@ class WiFiServerPort : public APIServerPort }; void initApiServer(int port = SERVER_API_DEFAULT_PORT); -void deInitApiServer(); \ No newline at end of file +void deInitApiServer(); +#endif diff --git a/src/mesh/api/ethServerAPI.cpp b/src/mesh/api/ethServerAPI.cpp index c75d53ff7c2..233a1f3362e 100644 --- a/src/mesh/api/ethServerAPI.cpp +++ b/src/mesh/api/ethServerAPI.cpp @@ -1,32 +1,43 @@ #include "configuration.h" -#include -#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D) +#include #include "ethServerAPI.h" -static ethServerPort *apiPort; +#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D) -void initApiServer(int port) +static ethServerPort *apiPort = nullptr; + +void initEthApiServer(int port) { - // Start API server on port 4403 if (!apiPort) { apiPort = new ethServerPort(port); - LOG_INFO("API server listening on TCP port %d", port); + LOG_INFO("Ethernet API server listening on TCP port %d", port); apiPort->init(); } } -void deInitApiServer() +void deInitEthApiServer() { if (apiPort) { - LOG_INFO("Deinit API server"); + LOG_INFO("Deinit Ethernet API server"); delete apiPort; apiPort = nullptr; } } -ethServerAPI::ethServerAPI(EthernetClient &_client) : ServerAPI(_client) +#if !HAS_WIFI +void initApiServer(int port) +{ + initEthApiServer(port); +} +void deInitApiServer() +{ + deInitEthApiServer(); +} +#endif + +ethServerAPI::ethServerAPI(MeshEthernetClient &_client) : ServerAPI(_client) { LOG_INFO("Incoming ethernet connection"); api_type = TYPE_ETH; @@ -34,4 +45,4 @@ ethServerAPI::ethServerAPI(EthernetClient &_client) : ServerAPI(_client) ethServerPort::ethServerPort(int port) : APIServerPort(port) {} -#endif \ No newline at end of file +#endif diff --git a/src/mesh/api/ethServerAPI.h b/src/mesh/api/ethServerAPI.h index 07de392cf1f..22923be848d 100644 --- a/src/mesh/api/ethServerAPI.h +++ b/src/mesh/api/ethServerAPI.h @@ -1,28 +1,45 @@ #pragma once +#include "configuration.h" + #include "ServerAPI.h" -#if !defined(USE_WS5500) && !defined(USE_CH390D) + +#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D) +#if defined(ESP32) && defined(ETH_PHY_TYPE) +#include +#include +typedef WiFiClient MeshEthernetClient; +typedef WiFiServer MeshEthernetServer; +#else #include +typedef EthernetClient MeshEthernetClient; +typedef EthernetServer MeshEthernetServer; +#endif /** * Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs * (and starts dropping debug printing - FIXME, eventually those prints should be encapsulated in protobufs). */ -class ethServerAPI : public ServerAPI +class ethServerAPI : public ServerAPI { public: - explicit ethServerAPI(EthernetClient &_client); + explicit ethServerAPI(MeshEthernetClient &_client); }; /** * Listens for incoming connections and does accepts and creates instances of EthernetServerAPI as needed */ -class ethServerPort : public APIServerPort +class ethServerPort : public APIServerPort { public: explicit ethServerPort(int port); }; +void initEthApiServer(int port = SERVER_API_DEFAULT_PORT); +void deInitEthApiServer(); + +#if !HAS_WIFI void initApiServer(int port = SERVER_API_DEFAULT_PORT); void deInitApiServer(); #endif +#endif diff --git a/src/mesh/eth/ethClient.cpp b/src/mesh/eth/ethClient.cpp index bb62327272f..a5516e21a09 100644 --- a/src/mesh/eth/ethClient.cpp +++ b/src/mesh/eth/ethClient.cpp @@ -127,7 +127,7 @@ static int32_t reconnectETH() #if !MESHTASTIC_EXCLUDE_SOCKETAPI if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { - initApiServer(); + initApiServer(SERVER_API_DEFAULT_PORT); } #endif #if HAS_UDP_MULTICAST diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp index 9930d0a55d7..7e9cf4a462f 100644 --- a/src/mesh/wifi/WiFiAPClient.cpp +++ b/src/mesh/wifi/WiFiAPClient.cpp @@ -1,5 +1,5 @@ #include "configuration.h" -#if HAS_WIFI +#if HAS_WIFI || HAS_ETHERNET #include "NodeDB.h" #include "RTC.h" #include "concurrency/Periodic.h" @@ -7,13 +7,26 @@ #include "main.h" #include "mesh/api/WiFiServerAPI.h" + +#if HAS_ETHERNET +#include "mesh/api/ethServerAPI.h" +#endif + #include "target_specific.h" + +#if HAS_WIFI || defined(ARCH_ESP32) #include +#endif -#if HAS_ETHERNET && defined(USE_WS5500) +#if HAS_ETHERNET +#if defined(ARCH_ESP32) && defined(ETH_PHY_TYPE) +#include +#endif +#if defined(USE_WS5500) #include #define ETH ETH2 -#endif // HAS_ETHERNET +#endif +#endif #if HAS_ETHERNET && defined(USE_CH390D) #include "ESP32_CH390.h" @@ -28,8 +41,8 @@ #endif #include #include -static void WiFiEvent(WiFiEvent_t event); -#elif defined(ARCH_RP2040) +static void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info); +#elif defined(ARCH_RP2040) && HAS_WIFI #include #endif @@ -52,24 +65,26 @@ uint8_t wifiDisconnectReason = 0; // Stores our hostname char ourHost[16]; +#if HAS_WIFI // To replace blocking wifi connect delay with a non-blocking sleep static unsigned long wifiReconnectStartMillis = 0; + static bool wifiReconnectPending = false; +bool needReconnect = true; // If we create our reconnector, run it once at the beginning +bool isReconnecting = false; // If we are currently reconnecting +Periodic *wifiReconnect; +#endif bool APStartupComplete = 0; unsigned long lastrun_ntp = 0; -bool needReconnect = true; // If we create our reconnector, run it once at the beginning -bool isReconnecting = false; // If we are currently reconnecting -#if defined(USE_WS5500) || defined(USE_CH390D) -static volatile bool ethNetworkConnectedPending = false; -#endif - WiFiUDP syslogClient; meshtastic::Syslog syslog(syslogClient); -Periodic *wifiReconnect; +#if defined(USE_WS5500) || defined(USE_CH390D) +static volatile bool ethNetworkConnectedPending = false; +#endif #if defined(USE_WS5500) || defined(USE_CH390D) static void onNetworkConnected(); @@ -99,20 +114,41 @@ static int32_t ethNetworkConnectedPoll() } #endif -#ifdef USE_WS5500 +#if (defined(ETH_PHY_TYPE) && !defined(USE_CH390D)) || defined(USE_WS5500) // Startup Ethernet bool initEthernet() { - if ((config.network.eth_enabled) && (ETH.begin(ETH_PHY_W5500, 1, ETH_CS_PIN, ETH_INT_PIN, ETH_RST_PIN, SPI3_HOST, - ETH_SCLK_PIN, ETH_MISO_PIN, ETH_MOSI_PIN))) { - WiFi.onEvent(WiFiEvent); + if (!config.network.eth_enabled) { + return false; + } + + WiFi.onEvent(WiFiEvent); + bool ok = false; +#if defined(ESP32) && defined(ETH_PHY_TYPE) + // Native ESP32 Ethernet (LAN8720, etc.) + LOG_INFO("Starting Native Ethernet..."); + ok = ETH.begin(ETH_PHY_ADDR, ETH_PHY_POWER, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_TYPE, ETH_CLK_MODE); +#elif defined(USE_WS5500) + // SPI Ethernet (W5500) + LOG_INFO("Starting W5500 Ethernet..."); + ok = ETH.begin(ETH_PHY_W5500, 1, ETH_CS_PIN, ETH_INT_PIN, ETH_RST_PIN, SPI3_HOST, ETH_SCLK_PIN, ETH_MISO_PIN, ETH_MOSI_PIN); +#endif + + if (ok) { #if !MESHTASTIC_EXCLUDE_WEBSERVER - createSSLCert(); // For WebServer + if (xPortGetFreeHeapSize() > 50000) { + createSSLCert(); // For WebServer + } else { + LOG_WARN("Not enough memory for SSL cert, WebServer will be HTTP only"); + } #endif +#if defined(USE_WS5500) new concurrency::Periodic("EthConnect", ethNetworkConnectedPoll); +#endif return true; } + LOG_ERROR("Ethernet failed to start"); return false; } #endif @@ -144,6 +180,7 @@ bool initEthernet() return true; } + LOG_ERROR("Ethernet failed to start"); return false; } #endif @@ -154,6 +191,7 @@ static void onNetworkConnected() // Start web server LOG_INFO("Start network services"); +#if defined(ARCH_ESP32) || (defined(ARCH_RP2040) && HAS_WIFI) // start mdns if (!MDNS.begin("Meshtastic")) { LOG_ERROR("Error setting up mDNS responder!"); @@ -166,13 +204,14 @@ static void onNetworkConnected() MDNS.addServiceTxt("meshtastic", "tcp", "id", String(nodeDB->getNodeId().c_str())); MDNS.addServiceTxt("meshtastic", "tcp", "pio_env", optstr(APP_ENV)); // ESP32 prints obtained IP address in WiFiEvent -#elif defined(ARCH_RP2040) +#elif defined(ARCH_RP2040) && HAS_WIFI MDNS.addServiceTxt("meshtastic", "shortname", owner.short_name); MDNS.addServiceTxt("meshtastic", "id", nodeDB->getNodeId().c_str()); MDNS.addServiceTxt("meshtastic", "pio_env", optstr(APP_ENV)); LOG_INFO("Obtained IP address: %s", WiFi.localIP().toString().c_str()); #endif } +#endif #ifndef DISABLE_NTP LOG_INFO("Start NTP time client"); @@ -207,7 +246,11 @@ static void onNetworkConnected() #endif #if !MESHTASTIC_EXCLUDE_SOCKETAPI if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { - initApiServer(); +#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D) + initEthApiServer(SERVER_API_DEFAULT_PORT); +#else + initApiServer(SERVER_API_DEFAULT_PORT); +#endif } #endif APStartupComplete = true; @@ -220,6 +263,7 @@ static void onNetworkConnected() #endif } +#if HAS_WIFI static int32_t reconnectWiFi() { const char *wifiName = config.network.wifi_ssid; @@ -295,29 +339,30 @@ static int32_t reconnectWiFi() return 300000; // every 5 minutes } } +#endif bool isWifiAvailable() { - if (config.network.wifi_enabled && (config.network.wifi_ssid[0])) { return true; -#if defined(USE_WS5500) || defined(USE_CH390D) - } else if (config.network.eth_enabled) { + } +#if HAS_ETHERNET + else if (config.network.eth_enabled) { return true; + } #endif -#ifndef ARCH_PORTDUINO - } else if (WiFi.status() == WL_CONNECTED) { - // it's likely we have wifi now, but user intends to turn it off in config! +#if HAS_WIFI && !defined(ARCH_PORTDUINO) + else if (WiFi.status() == WL_CONNECTED) { return true; -#endif - } else { - return false; } +#endif + return false; } // Disable WiFi void deinitWifi() { +#if HAS_WIFI LOG_INFO("WiFi deinit"); if (isWifiAvailable()) { @@ -330,11 +375,13 @@ void deinitWifi() LOG_INFO("WiFi Turned Off"); // WiFi.printDiag(Serial); } +#endif } // Startup WiFi bool initWifi() { +#if HAS_WIFI if (config.network.wifi_enabled && config.network.wifi_ssid[0]) { const char *wifiName = config.network.wifi_ssid; @@ -404,6 +451,8 @@ bool initWifi() LOG_INFO("Not using WIFI"); return false; } +#endif + return false; } #ifdef ARCH_ESP32 @@ -425,7 +474,7 @@ IPv6Address GlobalIPv6() } #endif // Called by the Espressif SDK to -static void WiFiEvent(WiFiEvent_t event) +static void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { LOG_DEBUG("Network-Event %d: ", event); @@ -567,7 +616,10 @@ static void WiFiEvent(WiFiEvent_t event) LOG_INFO("Ethernet disconnected"); break; case ARDUINO_EVENT_ETH_GOT_IP: -#if defined(USE_WS5500) || defined(USE_CH390D) +#if defined(ETH_PHY_TYPE) + LOG_INFO("Ethernet Obtained IP address: %s", ETH.localIP().toString().c_str()); + onNetworkConnected(); +#elif defined(USE_WS5500) || defined(USE_CH390D) LOG_INFO("Obtained IP address: %s, %u Mbps, %s", ETH.localIP().toString().c_str(), ETH.linkSpeed(), ETH.fullDuplex() ? "FULL_DUPLEX" : "HALF_DUPLEX"); ethNetworkConnectedPending = true; diff --git a/src/mesh/wifi/WiFiAPClient.h b/src/mesh/wifi/WiFiAPClient.h index 1de897d7a58..e913b166f01 100644 --- a/src/mesh/wifi/WiFiAPClient.h +++ b/src/mesh/wifi/WiFiAPClient.h @@ -9,10 +9,17 @@ #include #endif -#if HAS_ETHERNET && defined(USE_WS5500) +#if HAS_ETHERNET +#if defined(ESP32) && defined(ETH_PHY_TYPE) +#include +#elif defined(USE_WS5500) #include #define ETH ETH2 -#endif // HAS_ETHERNET +#elif defined(USE_CH390D) +#include +#define ETH CH390 +#endif +#endif extern bool needReconnect; extern concurrency::Periodic *wifiReconnect; @@ -26,7 +33,7 @@ bool isWifiAvailable(); uint8_t getWifiDisconnectReason(); -#if defined(USE_WS5500) || defined(USE_CH390D) +#if HAS_ETHERNET && (defined(USE_WS5500) || defined(USE_CH390D) || defined(ETH_PHY_TYPE)) // Startup Ethernet bool initEthernet(); -#endif \ No newline at end of file +#endif diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index ba1cfdb97e0..2135f1b863f 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -28,6 +28,16 @@ #include "TypeConversions.h" #include "mesh/RadioLibInterface.h" +#if HAS_ETHERNET && defined(USE_CH390D) +#include +#define ETH CH390 +#elif HAS_ETHERNET && defined(USE_WS5500) +#include +#define ETH ETH2 +#elif HAS_ETHERNET && defined(ETH_PHY_TYPE) +#include +#endif + #if !MESHTASTIC_EXCLUDE_MQTT #include "mqtt/MQTT.h" #endif @@ -1318,12 +1328,26 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r } #endif -#if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D) +#if HAS_ETHERNET conn.has_ethernet = true; conn.ethernet.has_status = true; - if (Ethernet.linkStatus() == LinkON) { + bool isConnected = false; + +#if defined(ESP32) && (defined(ETH_PHY_TYPE) || defined(USE_WS5500) || defined(USE_CH390D)) + isConnected = ETH.linkUp(); +#else + isConnected = (Ethernet.linkStatus() == LinkON); +#endif + + if (isConnected) { conn.ethernet.status.is_connected = true; - conn.ethernet.status.ip_address = Ethernet.localIP(); + +#if defined(ESP32) && (defined(ETH_PHY_TYPE) || defined(USE_WS5500) || defined(USE_CH390D)) + conn.ethernet.status.ip_address = (uint32_t)ETH.localIP(); +#else + conn.ethernet.status.ip_address = (uint32_t)Ethernet.localIP(); +#endif + #if !MESHTASTIC_EXCLUDE_MQTT conn.ethernet.status.is_mqtt_connected = mqtt && mqtt->isConnectedDirectly(); #endif diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index b1b143f04cc..3f3d18e2e0c 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -9,15 +9,24 @@ #if !defined(ARCH_NRF52) || NRF52_USE_JSON #include "serialization/JSON.h" #endif -#if HAS_WIFI + +#if HAS_WIFI || (defined(ESP32) && (defined(ETH_PHY_TYPE) || defined(USE_WS5500) || defined(USE_CH390D))) #include #if __has_include() #include #endif +#elif HAS_ETHERNET +#if defined(ESP32) +#include +#elif defined(ARCH_NRF52) || defined(ARCH_RP2040) +#include +#else +#include #endif #if HAS_ETHERNET && !defined(USE_WS5500) && !defined(USE_CH390D) #include #endif +#endif #if HAS_NETWORKING #include @@ -80,7 +89,8 @@ class MQTT : private concurrency::OSThread #ifndef PIO_UNIT_TESTING private: #endif -#if HAS_WIFI + +#if HAS_WIFI || (defined(ESP32) && (defined(ETH_PHY_TYPE) || defined(USE_WS5500) || defined(USE_CH390D))) using MQTTClient = WiFiClient; #if __has_include() using MQTTClientTLS = WiFiClientSecure; @@ -140,4 +150,4 @@ class MQTT : private concurrency::OSThread void mqttInit(); -extern MQTT *mqtt; \ No newline at end of file +extern MQTT *mqtt; diff --git a/variants/esp32/wt32-eth01-e22/platformio.ini b/variants/esp32/wt32-eth01-e22/platformio.ini new file mode 100644 index 00000000000..57e4da7cdba --- /dev/null +++ b/variants/esp32/wt32-eth01-e22/platformio.ini @@ -0,0 +1,22 @@ +[env:wt32-eth01-e22] +extends = esp32_base +board = esp32dev +board_build.partitions = partition-table.csv +upload_speed = 115200 +build_flags = + ${esp32_base.build_flags} + -I variants/esp32/wt32-eth01-e22 + -D WT32_ETH01_E22 + -D HAS_ETHERNET=1 + -D ETH_PHY_TYPE=ETH_PHY_LAN8720 + -D ETH_PHY_ADDR=1 + -D ETH_TYPE=0 + -D MESHTASTIC_EXCLUDE_WEBSERVER=1 + -D MESHTASTIC_EXCLUDE_PKT_LOG=1 + -D MESHTASTIC_EXCLUDE_AUDIO=1 + -D MESHTASTIC_EXCLUDE_STOREFORWARD=1 + -D MESHTASTIC_EXCLUDE_REMOTEHARDWARE=1 + -D MESHTASTIC_EXCLUDE_CANNED_MESSAGES=1 + -D MESHTASTIC_EXCLUDE_PAXCOUNTER=1 + -D MESHTASTIC_EXCLUDE_BLUETOOTH=1 + -D MESHTASTIC_EXCLUDE_SCREENSHOT=1 \ No newline at end of file diff --git a/variants/esp32/wt32-eth01-e22/variant.h b/variants/esp32/wt32-eth01-e22/variant.h new file mode 100644 index 00000000000..fe1081d1e87 --- /dev/null +++ b/variants/esp32/wt32-eth01-e22/variant.h @@ -0,0 +1,52 @@ +#pragma once + +// Meshtastic +#define IS_STATION 1 +#define HAS_SCREEN 1 +#define SCREEN_TYPE_SSD1306 +#define SCREEN_SDA I2C_SDA +#define SCREEN_SCL I2C_SCL +#define SCREEN_RESET -1 + +// GPS +#define HAS_GPS 0 +#define GPS_RX_PIN -1 +#define GPS_TX_PIN -1 + +// Ethernet +#define HAS_ETHERNET 1 +#define ETH_PHY_TYPE ETH_PHY_LAN8720 +#define ETH_PHY_ADDR 1 +#define ETH_PHY_MDC 23 +#define ETH_PHY_MDIO 18 +#define ETH_PHY_POWER 16 +#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN + +// LoRa +#define HAS_LORA 1 +#define USE_SX1262 +#define LORA_SCK 14 +#define LORA_MISO 12 +#define LORA_MOSI 15 +#define LORA_CS 2 +#define LORA_DIO1 39 +#define LORA_RESET 4 +#define LORA_BUSY 36 + +// SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_RESET LORA_RESET +#define SX126X_BUSY LORA_BUSY + +// E22-900M30S +#define SX126X_RXEN 17 +#define SX126X_TXEN RADIOLIB_NC +#define SX126X_DIO2_AS_RF_SWITCH 1 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 +#define SX126X_MAX_POWER 22 + +// i2c +#define HAS_I2C 1 +#define I2C_SDA 32 +#define I2C_SCL 33 \ No newline at end of file diff --git a/variants/esp32s3/diy/esp32s3-e22-w5500/platformio.ini b/variants/esp32s3/diy/esp32s3-e22-w5500/platformio.ini new file mode 100644 index 00000000000..c2effd98a32 --- /dev/null +++ b/variants/esp32s3/diy/esp32s3-e22-w5500/platformio.ini @@ -0,0 +1,33 @@ +[env:esp32s3-e22-w5500] +extends = esp32s3_base +board = esp32-s3-devkitc-1 +board_level = extra +board_check = true +board_build.partitions = default_16MB.csv +board_upload.maximum_size = 16777216 +board_build.flash_mode = qio +board_build.psram_type = opi +board_upload.flash_size = 16MB +board_build.f_flash = 80000000L +board_build.arduino.memory_type = qio_opi + +build_unflags = + ${esp32s3_base.build_unflags} + -D LED_BUILTIN=-1 + +build_flags = + ${esp32s3_base.build_flags} + -D PRIVATE_HW + -D BOARD_HAS_PSRAM + -D MESHTASTIC_EXCLUDE_WEBSERVER=1 + -D MESHTASTIC_TCP_API_MAX_CLIENTS=4 + -D MESHTASTIC_TCP_API_IDLE_TIMEOUT_MS=120000 + -D HAS_UDP_MULTICAST=1 + -I variants/esp32s3/diy/esp32s3-e22-w5500 + +lib_ignore = + Ethernet + +lib_deps = + ${esp32s3_base.lib_deps} + https://github.com/meshtastic/ETHClass2/archive/v1.0.0.zip diff --git a/variants/esp32s3/diy/esp32s3-e22-w5500/variant.h b/variants/esp32s3/diy/esp32s3-e22-w5500/variant.h new file mode 100644 index 00000000000..3c5f28edc8b --- /dev/null +++ b/variants/esp32s3/diy/esp32s3-e22-w5500/variant.h @@ -0,0 +1,71 @@ +#pragma once + +// SX1262 +#define USE_SX1262 +#define SX126X_SCK 21 +#define SX126X_MOSI 38 +#define SX126X_MISO 39 +#define SX126X_CS 14 +#define SX126X_DIO1 42 +#define SX126X_RESET 40 +#define SX126X_BUSY 41 +#define SX126X_TXEN 9 +#define SX126X_RXEN 10 +#define SX126X_MAX_POWER 22 +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +// Lora +#define LORA_SCK SX126X_SCK +#define LORA_MOSI SX126X_MOSI +#define LORA_MISO SX126X_MISO +#define LORA_CS SX126X_CS +#define LORA_DIO1 SX126X_DIO1 +#define LORA_RESET SX126X_RESET + +// LED +// #define LED_POWER 1 +// #define LED_STATE_ON 1 +// #define EXT_NOTIFY_OUT 2 + +// Buzzer +#define PIN_BUZZER 11 + +// Battery +#define BATTERY_PIN -1 +#define ADC_CHANNEL ADC1_GPIO1_CHANNEL +#define BATTERY_SENSE_RESOLUTION_BITS 12 + +// RGB LED +#define ENABLE_AMBIENTLIGHTING +#define HAS_NEOPIXEL +#define NEOPIXEL_COUNT 1 +#define NEOPIXEL_DATA 48 +#define NEOPIXEL_TYPE (NEO_GRB + NEO_KHZ800) + +// I2C +#define I2C_SCL 18 +#define I2C_SDA 8 + +// OLED +#define HAS_SCREEN 1 +#define USE_SSD1306 +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 + +// GPS +// #define HAS_GPS 1 +// #define PIN_GPS_EN 15 +// #define GPS_EN_ACTIVE 1 +// #define GPS_TX_PIN 16 +// #define GPS_RX_PIN 17 + +// Ethernet +#define HAS_ETHERNET 1 +#define USE_WS5500 1 +#define ETH_ADDR 1 +#define ETH_MISO_PIN 4 +#define ETH_MOSI_PIN 5 +#define ETH_SCLK_PIN 6 +#define ETH_CS_PIN 7 +#define ETH_INT_PIN 47 +#define ETH_RST_PIN -1