diff --git a/core/archipelago/src/mesh/listener/mod.rs b/core/archipelago/src/mesh/listener/mod.rs index 0e3c7f02..f486968d 100644 --- a/core/archipelago/src/mesh/listener/mod.rs +++ b/core/archipelago/src/mesh/listener/mod.rs @@ -87,6 +87,18 @@ const RECONNECT_DELAY_INIT: Duration = Duration::from_secs(5); /// Maximum reconnect delay (cap for exponential backoff). const RECONNECT_DELAY_MAX: Duration = Duration::from_secs(60); +/// Minimum time a session must run before we trust it enough to reset +/// backoff to the minimum. Without this gate, a device that connects then +/// fails again within a couple of seconds (e.g. mid-boot-loop) never backs +/// off — every retry immediately re-opens the port, which toggles DTR/RTS +/// (resets many ESP32 boards' MCU on native-USB and CP2102/CH340 +/// auto-reset-circuit boards alike), turning a device that's merely +/// unstable into a self-sustaining boot loop that outlasts whatever +/// triggered the original instability. Confirmed live 2026-07-23: a Heltec +/// V3 stuck retrying every ~5-15s for 5+ minutes after a failed firmware +/// flash left it in a marginal state. +const STABLE_SESSION_THRESHOLD: Duration = Duration::from_secs(20); + /// Number of consecutive write failures before we consider the device dead /// and trigger a reconnection cycle. const MAX_CONSECUTIVE_WRITE_FAILURES: u32 = 3; @@ -560,6 +572,7 @@ pub fn spawn_mesh_listener( return; } + let session_start = std::time::Instant::now(); match session::run_mesh_session( &state, &data_dir, @@ -582,13 +595,14 @@ pub fn spawn_mesh_listener( { Ok(()) => { info!("Mesh session ended cleanly"); - // Session was established before ending — reset backoff - reconnect_delay = RECONNECT_DELAY_INIT; + // Only trust a session that actually ran for a while — + // see STABLE_SESSION_THRESHOLD's doc comment. + if session_start.elapsed() >= STABLE_SESSION_THRESHOLD { + reconnect_delay = RECONNECT_DELAY_INIT; + } } Err(e) => { - // Check if session was ever connected (vs failed to open) - let was_connected = state.status.read().await.device_connected; - if was_connected { + if session_start.elapsed() >= STABLE_SESSION_THRESHOLD { reconnect_delay = RECONNECT_DELAY_INIT; } error!("Mesh session error: {} (retry in {:?})", e, reconnect_delay);