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
|
/// `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
|
/// that one device's probe runs, so a non-matching firmware's init bytes are
|
||||||
/// never injected into the port. `None` keeps the strict
|
/// 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(
|
async fn auto_detect_and_open(
|
||||||
data_dir: &Path,
|
data_dir: &Path,
|
||||||
our_ed_pubkey_hex: &str,
|
our_ed_pubkey_hex: &str,
|
||||||
@ -274,6 +274,34 @@ async fn auto_detect_and_open(
|
|||||||
}
|
}
|
||||||
for path in &paths {
|
for path in &paths {
|
||||||
debug!(path = %path, "Probing for mesh radio device");
|
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) {
|
if device_kind.is_none_or(|k| k == DeviceType::Meshcore) {
|
||||||
match MeshcoreDevice::open(path).await {
|
match MeshcoreDevice::open(path).await {
|
||||||
Ok(mut dev) => match dev.initialize().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"),
|
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!(
|
anyhow::bail!(
|
||||||
"No supported mesh radio found on {} candidate ports: {:?}",
|
"No supported mesh radio found on {} candidate ports: {:?}",
|
||||||
@ -381,20 +385,10 @@ async fn open_preferred_path(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match MeshcoreDevice::open(path).await {
|
// Reticulum first — see the matching comment on auto_detect_and_open:
|
||||||
Ok(mut dev) => match dev.initialize().await {
|
// its cheap probe_rnode gate fails in ~1s for non-RNode firmware, while
|
||||||
Ok(info) => return Ok((MeshRadioDevice::Meshcore(dev), info)),
|
// trying Meshcore/Meshtastic first was observed leaving a real RNode
|
||||||
Err(e) => debug!(path = %path, error = %e, "Preferred path is not Meshcore"),
|
// board unresponsive by the time Reticulum's turn came.
|
||||||
},
|
|
||||||
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"),
|
|
||||||
}
|
|
||||||
match ReticulumLink::open(
|
match ReticulumLink::open(
|
||||||
path,
|
path,
|
||||||
data_dir,
|
data_dir,
|
||||||
@ -404,10 +398,24 @@ async fn open_preferred_path(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(mut dev) => match dev.initialize().await {
|
Ok(mut dev) => match dev.initialize().await {
|
||||||
Ok(info) => Ok((MeshRadioDevice::Reticulum(dev), info)),
|
Ok(info) => return Ok((MeshRadioDevice::Reticulum(dev), info)),
|
||||||
Err(e) => Err(e).context("Preferred path is not a working Reticulum RNode"),
|
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