From 4e3ed198271211d40831dd35de4854e6bbc76d42 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 31 Aug 2026 08:39:39 -0500 Subject: [PATCH] [NET] ESP32 Ethernet: PHY cold-cycle at boot, default on Theengs Bridge (#2364) Power ETH_PHY_POWER down for 200 ms and back up 200 ms before ETH.begin() so the PHY always gets a true cold start. A warm PHY left in a stale state (reproduced on Theengs Bridge v1.1 with PSRAM enabled after an NVS erase) can flap the link until a manual power cycle: it reports up for one poll, then down for several seconds, forever, so DHCP never completes. The short reset the Ethernet driver performs on its own does not clear that state; a power cycle with a settle window before begin() does. On a hardware-in-the- loop bench the same provocation went from flapping every trial to flapping in none of them (4/4). Enabled via -DETH_PHY_COLD_CYCLE, only active when ETH_PHY_POWER is a valid pin, at the cost of ~400 ms added boot time. Set by default on both Theengs Bridge environments. Co-authored-by: Florian <1technophile@users.noreply.github.com> Co-authored-by: Claude Fable 5 --- environments.ini | 2 ++ main/main.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/environments.ini b/environments.ini index 48aa8db..d6b1a20 100644 --- a/environments.ini +++ b/environments.ini @@ -74,6 +74,7 @@ build_flags = '-DETH_PHY_MDC=23' '-DETH_PHY_MDIO=18' '-DETH_CLK_MODE=ETH_CLOCK_GPIO0_OUT' + '-DETH_PHY_COLD_CYCLE' '-DLED_ADDRESSABLE_PIN1=32' '-DLED_ADDRESSABLE_NUM=4' '-DLED_BROKER=0' @@ -113,6 +114,7 @@ build_flags = '-DETH_PHY_MDC=23' '-DETH_PHY_MDIO=18' '-DETH_CLK_MODE=ETH_CLOCK_GPIO0_OUT' + '-DETH_PHY_COLD_CYCLE' '-DLED_ADDRESSABLE=true' '-DLED_ADDRESSABLE_PIN1=32' '-DLED_ADDRESSABLE_NUM=4' diff --git a/main/main.cpp b/main/main.cpp index 21e67b2..1097149 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2475,6 +2475,19 @@ void setupWiFiManager() { void setup_ethernet_esp32() { bool ethBeginSuccess = false; WiFi.onEvent(WiFiEvent); +# if defined(ETH_PHY_COLD_CYCLE) && defined(ETH_PHY_POWER) && (ETH_PHY_POWER >= 0) + // Give the PHY a true cold start before begin(): a warm PHY left in a stale + // state (seen after an NVS erase with PSRAM enabled on Theengs Bridge v1.1) + // can flap the link until a manual power cycle, and the in-driver reset + // begin() performs (short assert, short delay) does not clear it. Power the + // PHY down, then back up, and let its oscillator settle before begin() + // starts talking to it. + pinMode(ETH_PHY_POWER, OUTPUT); + digitalWrite(ETH_PHY_POWER, LOW); + delay(200); // hold the PHY powered down + digitalWrite(ETH_PHY_POWER, HIGH); + delay(200); // let the PHY oscillator settle +# endif # ifdef NetworkAdvancedSetup THEENGS_LOG_TRACE(F("Adv eth cfg" CR)); // ETH.config() has to be called after ETH.begin(): the underlying netif is