fix(mesh): probe Reticulum/RNode before Meshcore/Meshtastic during auto-detect
Confirmed on real hardware (Heltec V4, RNode firmware): the board answers the exact KISS detect probe correctly and instantly on a fresh port open, but auto_detect_and_open (and open_preferred_path's unpinned branch) tried Meshcore (~5s timeout) then Meshtastic (~5s timeout) first, leaving the RNode firmware unresponsive by the time Reticulum's turn came ~10.6s later. ReticulumLink::open() already gates its expensive daemon-spawn behind a cheap ~1s probe_rnode check, so trying it first only costs ~1s extra when the device turns out to be Meshcore/Meshtastic instead — the previous comment's "most expensive, so goes last" reasoning only applies to a successful match, not a failed one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
9bbbb046a8
commit
893508b700
@ -261,7 +261,7 @@ impl MeshRadioDevice {
|
||||
/// `MeshConfig.device_kind` — see the plan's §2c reflashable-board note): only
|
||||
/// that one device's probe runs, so a non-matching firmware's init bytes are
|
||||
/// never injected into the port. `None` keeps the strict
|
||||
/// Meshcore→Meshtastic→Reticulum probe order.
|
||||
/// Reticulum→Meshcore→Meshtastic probe order.
|
||||
async fn auto_detect_and_open(
|
||||
data_dir: &Path,
|
||||
our_ed_pubkey_hex: &str,
|
||||
@ -274,6 +274,34 @@ async fn auto_detect_and_open(
|
||||
}
|
||||
for path in &paths {
|
||||
debug!(path = %path, "Probing for mesh radio device");
|
||||
// Tried FIRST: `ReticulumLink::open()` gates its expensive daemon
|
||||
// spawn behind a cheap (~1s worst case) RNode KISS-detect probe, so a
|
||||
// failed match here costs about as much as the Meshcore/Meshtastic
|
||||
// probes below. Trying it first matters in practice: on a real RNode
|
||||
// board, Meshcore's and Meshtastic's handshake bytes (~5s timeout
|
||||
// each, ~10.6s combined) sitting on the wire before Reticulum ever
|
||||
// gets a turn was observed to leave the RNode firmware unresponsive
|
||||
// by the time its turn came — confirmed on hardware that responds
|
||||
// correctly to the same KISS probe on a truly fresh port.
|
||||
if device_kind.is_none_or(|k| k == DeviceType::Reticulum) {
|
||||
match ReticulumLink::open(
|
||||
path,
|
||||
data_dir,
|
||||
Some(our_ed_pubkey_hex),
|
||||
Some(our_x25519_pubkey_hex),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(mut dev) => match dev.initialize().await {
|
||||
Ok(info) => {
|
||||
info!(path = %path, "Found Reticulum (RNode) device via auto-detect");
|
||||
return Ok((path.clone(), MeshRadioDevice::Reticulum(dev), info));
|
||||
}
|
||||
Err(e) => debug!(path = %path, error = %e, "Reticulum daemon failed to initialize"),
|
||||
},
|
||||
Err(e) => debug!(path = %path, error = %e, "Not a Reticulum RNode"),
|
||||
}
|
||||
}
|
||||
if device_kind.is_none_or(|k| k == DeviceType::Meshcore) {
|
||||
match MeshcoreDevice::open(path).await {
|
||||
Ok(mut dev) => match dev.initialize().await {
|
||||
@ -298,30 +326,6 @@ async fn auto_detect_and_open(
|
||||
Err(e) => debug!(path = %path, error = %e, "Could not open serial port for Meshtastic"),
|
||||
}
|
||||
}
|
||||
// Tried LAST: the same reflashable board (e.g. Heltec V3) can run
|
||||
// Meshcore, Meshtastic, or RNode firmware, so each probe must fail
|
||||
// strictly before the next is attempted. The RNode KISS-detect probe
|
||||
// is the most expensive (spawns the supervised daemon on a match), so
|
||||
// it goes after the two cheap firmware-specific handshakes above.
|
||||
if device_kind.is_none_or(|k| k == DeviceType::Reticulum) {
|
||||
match ReticulumLink::open(
|
||||
path,
|
||||
data_dir,
|
||||
Some(our_ed_pubkey_hex),
|
||||
Some(our_x25519_pubkey_hex),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(mut dev) => match dev.initialize().await {
|
||||
Ok(info) => {
|
||||
info!(path = %path, "Found Reticulum (RNode) device via auto-detect");
|
||||
return Ok((path.clone(), MeshRadioDevice::Reticulum(dev), info));
|
||||
}
|
||||
Err(e) => debug!(path = %path, error = %e, "Reticulum daemon failed to initialize"),
|
||||
},
|
||||
Err(e) => debug!(path = %path, error = %e, "Not a Reticulum RNode"),
|
||||
}
|
||||
}
|
||||
}
|
||||
anyhow::bail!(
|
||||
"No supported mesh radio found on {} candidate ports: {:?}",
|
||||
@ -381,20 +385,10 @@ async fn open_preferred_path(
|
||||
};
|
||||
}
|
||||
|
||||
match MeshcoreDevice::open(path).await {
|
||||
Ok(mut dev) => match dev.initialize().await {
|
||||
Ok(info) => return Ok((MeshRadioDevice::Meshcore(dev), info)),
|
||||
Err(e) => debug!(path = %path, error = %e, "Preferred path is not Meshcore"),
|
||||
},
|
||||
Err(e) => debug!(path = %path, error = %e, "Could not open preferred path as Meshcore"),
|
||||
}
|
||||
match MeshtasticDevice::open(path).await {
|
||||
Ok(mut dev) => match dev.initialize().await {
|
||||
Ok(info) => return Ok((MeshRadioDevice::Meshtastic(dev), info)),
|
||||
Err(e) => debug!(path = %path, error = %e, "Preferred path is not Meshtastic"),
|
||||
},
|
||||
Err(e) => debug!(path = %path, error = %e, "Could not open preferred path as Meshtastic"),
|
||||
}
|
||||
// Reticulum first — see the matching comment on auto_detect_and_open:
|
||||
// its cheap probe_rnode gate fails in ~1s for non-RNode firmware, while
|
||||
// trying Meshcore/Meshtastic first was observed leaving a real RNode
|
||||
// board unresponsive by the time Reticulum's turn came.
|
||||
match ReticulumLink::open(
|
||||
path,
|
||||
data_dir,
|
||||
@ -404,10 +398,24 @@ async fn open_preferred_path(
|
||||
.await
|
||||
{
|
||||
Ok(mut dev) => match dev.initialize().await {
|
||||
Ok(info) => Ok((MeshRadioDevice::Reticulum(dev), info)),
|
||||
Err(e) => Err(e).context("Preferred path is not a working Reticulum RNode"),
|
||||
Ok(info) => return Ok((MeshRadioDevice::Reticulum(dev), info)),
|
||||
Err(e) => debug!(path = %path, error = %e, "Preferred path is not a working Reticulum RNode"),
|
||||
},
|
||||
Err(e) => Err(e).context("Could not open preferred path as Reticulum"),
|
||||
Err(e) => debug!(path = %path, error = %e, "Could not open preferred path as Reticulum"),
|
||||
}
|
||||
match MeshcoreDevice::open(path).await {
|
||||
Ok(mut dev) => match dev.initialize().await {
|
||||
Ok(info) => return Ok((MeshRadioDevice::Meshcore(dev), info)),
|
||||
Err(e) => debug!(path = %path, error = %e, "Preferred path is not Meshcore"),
|
||||
},
|
||||
Err(e) => debug!(path = %path, error = %e, "Could not open preferred path as Meshcore"),
|
||||
}
|
||||
match MeshtasticDevice::open(path).await {
|
||||
Ok(mut dev) => match dev.initialize().await {
|
||||
Ok(info) => Ok((MeshRadioDevice::Meshtastic(dev), info)),
|
||||
Err(e) => Err(e).context("Preferred path is not a working Meshtastic device"),
|
||||
},
|
||||
Err(e) => Err(e).context("Could not open preferred path as Meshtastic"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user