diff --git a/src/helpers/nrf52/EthernetCLI.h b/src/helpers/nrf52/EthernetCLI.h index 34802e56..5a9fe7e4 100644 --- a/src/helpers/nrf52/EthernetCLI.h +++ b/src/helpers/nrf52/EthernetCLI.h @@ -27,6 +27,26 @@ static SPIClass ETHERNET_SPI_PORT(NRF_SPIM1, PIN_SPI1_MISO, PIN_SPI1_SCK, PIN_SP #define ETHERNET_RETRY_INTERVAL_MS 30000 +#ifdef WITH_W5100S_POE + // Give the RAK19018 (Silvertel) PoE converter time to latch on the current + // the W5100S is already drawing (board.begin()'s early RST release + + // bit-bang soft-reset) before doing the *disruptive* Ethernet-library + // bring-up (another PHY soft-reset + blocking DHCP) — doing that + // immediately reliably collapsed the marginal PoE supply. + #ifndef ETH_POE_DEFER_MS + #define ETH_POE_DEFER_MS 6000 + #endif + #ifndef ETH_STATIC_IP + #define ETH_STATIC_IP 192,168,1,50 + #endif + #ifndef ETH_GATEWAY + #define ETH_GATEWAY 192,168,1,1 + #endif + #ifndef ETH_SUBNET + #define ETH_SUBNET 255,255,255,0 + #endif +#endif + static EthernetServer ethernet_server(ETHERNET_TCP_PORT); static EthernetClient ethernet_client; static volatile bool ethernet_running = false; @@ -35,6 +55,10 @@ static volatile bool ethernet_running = false; static void ethernet_task(void* param) { (void)param; +#ifdef WITH_W5100S_POE + vTaskDelay(pdMS_TO_TICKS(ETH_POE_DEFER_MS)); +#endif + Serial.println("ETH: Initializing hardware"); // WB_IO2 (power enable) is already driven HIGH by early constructor // in RAK4631Board.cpp to support POE boot. @@ -51,6 +75,22 @@ static void ethernet_task(void* param) { Serial.printf("ETH: MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +#ifdef WITH_W5100S_POE + // Bounded DHCP with a static-IP fallback, so the node stays reachable even + // without a DHCP server (no infinite retry — this build has no battery and + // must always come up somewhere). + Serial.println("ETH: Attempting DHCP..."); + if (Ethernet.begin(mac, 10000, 2000) == 0) { + Serial.println("ETH: DHCP failed -> static IP fallback"); + IPAddress ip(ETH_STATIC_IP), gw(ETH_GATEWAY), sn(ETH_SUBNET); + Ethernet.begin(mac, ip, gw, gw, sn); + } + IPAddress ip = Ethernet.localIP(); + Serial.printf("ETH: IP: %u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); + Serial.printf("ETH: Listening on TCP port %d\n", ETHERNET_TCP_PORT); + ethernet_server.begin(); + ethernet_running = true; +#else // Retry loop: keep trying until we get an IP while (!ethernet_running) { Serial.println("ETH: Attempting DHCP..."); @@ -75,8 +115,9 @@ static void ethernet_task(void* param) { ethernet_server.begin(); ethernet_running = true; } +#endif - // DHCP succeeded, task is done + // DHCP succeeded (or static fallback applied), task is done vTaskDelete(NULL); } diff --git a/src/helpers/nrf52/SerialEthernetInterface.cpp b/src/helpers/nrf52/SerialEthernetInterface.cpp index e766b915..edbfe50b 100644 --- a/src/helpers/nrf52/SerialEthernetInterface.cpp +++ b/src/helpers/nrf52/SerialEthernetInterface.cpp @@ -48,7 +48,7 @@ static void eth_init_spi_and_pins() { } // Bring up the DHCP/static IP + start listening. Returns true on success. -static bool eth_bring_up(const uint8_t mac[6]) { +static bool eth_bring_up(uint8_t mac[6]) { #ifdef WITH_W5100S_POE // Bounded DHCP with a static-IP fallback, so the node stays reachable even // without a DHCP server and never blocks indefinitely at cold start. diff --git a/variants/rak4631/W5100SPoE.cpp b/variants/rak4631/W5100SPoE.cpp index f56ac0c9..8ff9814a 100644 --- a/variants/rak4631/W5100SPoE.cpp +++ b/variants/rak4631/W5100SPoE.cpp @@ -4,29 +4,15 @@ #include #include "W5100SPoE.h" -// ── Early power-rail + RST release (constructor priority 200) ──────────────── -// Runs before setup(), right after SystemInit. Two jobs, as early as possible -// so the W5100S draws its full operating current before the RAK19018 -// (Silvertel) converter folds back during PoE cold-start: -// -// 1. Drive PIN_3V3_EN (P1.02 / WB_IO2 / Arduino 34) HIGH — this is the -// RAK19007 3.3 V PERIPHERAL POWER ENABLE that feeds the RAK13800/W5100S. -// Meshtastic does this in initVariant(); stock MeshCore never did, so our -// W5100S was only weakly powered via a default path (responds on USB but -// can't pull its full ~130 mA on the marginal PoE rail). -// 2. Drive W5100S RST (P0.21 / WB_IO3 / Arduino 21) HIGH — out of reset. +// ── Early RST release (constructor priority 200) ───────────────────────────── +// Runs before setup(), right after SystemInit — earlier than the ETHERNET_ENABLED +// constructor in RAK4631Board.cpp that powers the peripheral rail (WB_IO2, +// priority 102 runs first) but well before setup()/board.begin(), so the +// W5100S starts drawing its full operating current as early as possible, +// before the RAK19018 (Silvertel) converter's foldback timer expires. // // Raw registers because the Arduino GPIO layer isn't up this early. -static void __attribute__((constructor(200))) w5100s_early_power_init() { - // P1.02 = 3V3_EN → HIGH (power the peripheral rail FIRST) - NRF_P1->PIN_CNF[2] = - (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) | - (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | - (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) | - (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | - (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos); - NRF_P1->OUTSET = (1UL << 2); - +static void __attribute__((constructor(200))) w5100s_early_rst_release() { // P0.21 = W5100S RST → HIGH (release from reset) NRF_P0->PIN_CNF[21] = (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) | @@ -83,13 +69,11 @@ static uint8_t bb_read_reg(uint16_t addr) { } // ── Full W5100S bring-up ──────────────────────────────────────────────────── -// Called from RAK4631Board::begin(). Confirms the 3V3 rail + RST are driven -// (Arduino API, in case the core re-init touched them), then soft-resets and -// reads VERSIONR. Returns VERSIONR (0x51 = healthy W5100S). +// Called from RAK4631Board::begin(). Confirms RST is driven (Arduino API, in +// case the core re-init touched it), then soft-resets and reads VERSIONR. +// Returns VERSIONR (0x51 = healthy W5100S). uint8_t w5100s_poe_init() { - // Make sure the peripheral power rail stays driven HIGH. - pinMode(W5100S_3V3_EN_PIN, OUTPUT); digitalWrite(W5100S_3V3_EN_PIN, HIGH); - delay(20); // let the rail/W5100S settle after enable + delay(20); // let the rail/W5100S settle after the ETHERNET_ENABLED ctor's WB_IO2 enable // Park both chip-selects HIGH on the shared bus, RST released. pinMode(LORA_NSS_PIN, OUTPUT); digitalWrite(LORA_NSS_PIN, HIGH); diff --git a/variants/rak4631/W5100SPoE.h b/variants/rak4631/W5100SPoE.h index c3582f0d..98c91406 100644 --- a/variants/rak4631/W5100SPoE.h +++ b/variants/rak4631/W5100SPoE.h @@ -1,6 +1,7 @@ #pragma once -// W5100S activation for PoE operation on RAK10720 (RAK4631 + RAK13800 + RAK19018). +// W5100S early activation for PoE operation on RAK10720 (RAK4631 + RAK13800 + +// RAK19018). // // ROOT CAUSE (confirmed via RAK forum + Meshtastic rak4631_eth_gw variant): // The RAK19018 PoE module (Silvertel Ag9905MT) enters a non-continuous @@ -8,14 +9,14 @@ // repeater draws only a few mA, so the converter never latches → the supply // pulses and the device never boots (fade-in / brighten / die / repeat LED). // -// The fix that lets Meshtastic boot on PoE without a battery: bring the -// W5100S PHY into its active state. An active W5100S draws ~120 mA — enough -// to keep the Silvertel converter latched in continuous mode. -// -// The W5100S has no power-enable pin on this board (always powered), but its -// RST must be HIGH for the PHY to run. We release RST as early as possible -// (before setup(), via a constructor) so the PHY draws current and latches -// the converter before its foldback timer expires. +// The fix: bring the W5100S PHY into its active state as early as possible. +// An active W5100S draws ~120 mA — enough to keep the Silvertel converter +// latched in continuous mode. The peripheral power rail (WB_IO2 / P1.02) is +// already driven HIGH by an early constructor in RAK4631Board.cpp whenever +// ETHERNET_ENABLED is set; this file only handles releasing RST (the rail +// alone isn't enough — the chip must actually be brought out of reset and +// made to draw its full current well before the Ethernet library's own +// (later, disruptive) bring-up runs). // // Pin mapping (from Meshtastic rak4631_eth_gw variant.h): // RST → PIN_ETHERNET_RESET = 21 (P0.21 / WB_IO3) @@ -30,13 +31,6 @@ #define W5100S_CS_PIN 26 // WB_SPI_CS #endif -// RAK19007 3.3 V peripheral power enable (feeds the RAK13800/W5100S). -// Must be driven HIGH or the W5100S is only weakly powered — exactly what -// Meshtastic's initVariant() does and stock MeshCore omits. -#ifndef W5100S_3V3_EN_PIN - #define W5100S_3V3_EN_PIN 34 // P1.02 / WB_IO2 -#endif - // Called from RAK4631Board::begin(); fully brings up the W5100S (soft reset + // activation) so it draws full operating current. Returns VERSIONR (0x51 = ok). uint8_t w5100s_poe_init(); diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 31b507b4..5efc291c 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -267,3 +267,27 @@ build_src_filter = ${rak4631.build_src_filter} +<../examples/kiss_modem/> lib_deps = ${rak4631.lib_deps} + +; RAK10720 (RAK4631 + RAK13800/W5100S + RAK19018 PoE) variants. +; WITH_W5100S_POE adds: early W5100S RST release + activation (so the +; RAK19018/Silvertel converter latches on PoE with no battery), the +; no-battery boot-voltage bypass, and PoE-safe deferred Ethernet bring-up +; (DHCP with a bounded timeout + static-IP fallback, after the converter +; is solidly latched). +[env:RAK_4631_repeater_ethernet_poe] +extends = env:RAK_4631_repeater_ethernet +build_flags = + ${env:RAK_4631_repeater_ethernet.build_flags} + -D WITH_W5100S_POE=1 + +[env:RAK_4631_room_server_ethernet_poe] +extends = env:RAK_4631_room_server_ethernet +build_flags = + ${env:RAK_4631_room_server_ethernet.build_flags} + -D WITH_W5100S_POE=1 + +[env:RAK_4631_companion_radio_ethernet_poe] +extends = env:RAK_4631_companion_radio_ethernet +build_flags = + ${env:RAK_4631_companion_radio_ethernet.build_flags} + -D WITH_W5100S_POE=1