diff --git a/core/archipelago/src/mesh/listener/session.rs b/core/archipelago/src/mesh/listener/session.rs index b27acaf8..9db19b43 100644 --- a/core/archipelago/src/mesh/listener/session.rs +++ b/core/archipelago/src/mesh/listener/session.rs @@ -366,6 +366,25 @@ pub struct DeviceProbe { /// loop can pick the device up afterwards. Reticulum uses the bare KISS /// DETECT probe — no daemon spawn just to identify a stick. pub(crate) async fn probe_device(path: &str) -> Result { + // The listener's reconnect loop may hold this port for ~10s of every + // backoff cycle, and Linux happily double-opens a tty — two concurrent + // handshakes corrupt each other into silence (observed on framework-pt: + // every firmware "failed" while the listener was mid-cycle). Retry across + // the listener's idle gaps instead of failing on first collision. + let mut last_err = None; + for attempt in 0..3u32 { + if attempt > 0 { + tokio::time::sleep(Duration::from_secs(7)).await; + } + match probe_device_once(path).await { + Ok(p) => return Ok(p), + Err(e) => last_err = Some(e), + } + } + Err(last_err.unwrap_or_else(|| anyhow::anyhow!("probe failed"))) +} + +async fn probe_device_once(path: &str) -> Result { if super::super::reticulum::probe_rnode(path).await.is_ok() { return Ok(DeviceProbe { path: path.to_string(),