fix(mesh): meshtastic receive — drain frame batch per poll + rx diagnostics
Addresses the open Meshtastic parity bug (project_meshtastic_parity): the running driver received nothing (`mesh.messages` stayed []) though the radio got the packets and sends worked. Root-cause candidate: `try_recv_frame` decoded ONE serial frame per poll and returned Ok(None) for every non-text FromRadio frame, so the session loop slept 50ms between frames. Under Meshtastic's frequent NodeInfo/telemetry stream a received text packet queued behind them, and read_from_radio's 64KB buffer cap could drain (drop) it before it was ever decoded — reception silently dead while sends kept working. - try_recv_frame now drains a bounded batch (64) per poll, processing each frame's side effects and returning the first inbound text frame, so a text packet is decoded the same poll it arrives and the buffer never grows enough to hit the lossy cap. Bounded so a continuous flood still yields to select!. - packet_to_inbound_frame logs every decoded packet (from/portnum/payload_len) and a "did not parse (dropped)" case, so one live radio pass is conclusive. The rest of the decode path was verified correct by inspection (FROM_RADIO_PACKET =2, wire-type-5 handled, parse_mesh_packet sound, 60s heartbeat present) — not a parse bug. cargo check green. NEEDS a live radio pass on a rig that isn't .228 (off-limits: bitcoin testing) to confirm. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
11038cdcc9
commit
3c7c04a662
@ -530,10 +530,25 @@ impl MeshtasticDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn try_recv_frame(&mut self) -> Result<Option<InboundFrame>> {
|
pub async fn try_recv_frame(&mut self) -> Result<Option<InboundFrame>> {
|
||||||
|
// Drain a bounded batch of frames per poll, processing EACH for its side
|
||||||
|
// effects (my_info/config/channel/node_info) and returning the first that
|
||||||
|
// yields an inbound text frame. The old one-frame-per-poll behavior
|
||||||
|
// returned Ok(None) for every non-text frame, so the caller slept 50ms
|
||||||
|
// between frames; under Meshtastic's frequent NodeInfo/telemetry stream a
|
||||||
|
// received text packet queued behind them and the read buffer's 64KB cap
|
||||||
|
// could drain (drop) it before it was ever decoded — silently killing
|
||||||
|
// reception while sends kept working. Draining keeps the buffer short so
|
||||||
|
// the text frame is decoded the same poll it arrives. Bounded to 64 so a
|
||||||
|
// continuous flood still yields back to the session select! loop.
|
||||||
|
for _ in 0..64 {
|
||||||
let Some(frame) = self.read_from_radio().await? else {
|
let Some(frame) = self.read_from_radio().await? else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
Ok(self.handle_from_radio(&frame))
|
if let Some(inbound) = self.handle_from_radio(&frame) {
|
||||||
|
return Ok(Some(inbound));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether we've learned `node_num`'s real PKI (Curve25519) key — from a
|
/// Whether we've learned `node_num`'s real PKI (Curve25519) key — from a
|
||||||
@ -700,7 +715,25 @@ impl MeshtasticDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn packet_to_inbound_frame(&mut self, data: &[u8]) -> Option<InboundFrame> {
|
fn packet_to_inbound_frame(&mut self, data: &[u8]) -> Option<InboundFrame> {
|
||||||
let packet = parse_mesh_packet(data)?;
|
let Some(packet) = parse_mesh_packet(data) else {
|
||||||
|
// Diagnostic for the open receive bug (project_meshtastic_parity): a
|
||||||
|
// forwarded FromRadio.packet that won't parse (e.g. encrypted-only,
|
||||||
|
// no `decoded` field) is dropped here. Watch for this on the live pass.
|
||||||
|
debug!(
|
||||||
|
len = data.len(),
|
||||||
|
head = %hex::encode(&data[..data.len().min(12)]),
|
||||||
|
"Meshtastic FromRadio.packet did not parse (dropped)"
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
// Trace EVERY decoded packet so the live pass shows what actually arrives
|
||||||
|
// and why a text frame is (or isn't) surfaced.
|
||||||
|
debug!(
|
||||||
|
from = format!("!{:08x}", packet.from.unwrap_or(0)),
|
||||||
|
portnum = packet.portnum,
|
||||||
|
payload_len = packet.payload.len(),
|
||||||
|
"Meshtastic FromRadio.packet decoded"
|
||||||
|
);
|
||||||
if packet.portnum != TEXT_MESSAGE_APP || packet.payload.is_empty() {
|
if packet.portnum != TEXT_MESSAGE_APP || packet.payload.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user