diff --git a/core/archipelago/src/mesh/meshtastic.rs b/core/archipelago/src/mesh/meshtastic.rs index 5b0d7264..c7806fdb 100644 --- a/core/archipelago/src/mesh/meshtastic.rs +++ b/core/archipelago/src/mesh/meshtastic.rs @@ -856,8 +856,22 @@ fn packet_to_inbound_frame( contacts: &mut HashMap, peer_pubkeys: &mut HashMap>, ) -> Option { - let packet = parse_mesh_packet(data)?; + let Some(packet) = parse_mesh_packet(data) else { + debug!( + len = data.len(), + head = %hex::encode(&data[..data.len().min(16)]), + "Meshtastic FromRadio.packet did not parse into a decoded MeshPacket" + ); + return None; + }; if packet.portnum != TEXT_MESSAGE_APP || packet.payload.is_empty() { + debug!( + from = ?packet.from.map(|n| format!("!{:08x}", n)), + portnum = packet.portnum, + payload_len = packet.payload.len(), + pki = packet.pki_encrypted, + "Meshtastic packet ignored because it is not a text payload" + ); return None; } if packet_is_stale(packet.rx_time) { @@ -870,6 +884,10 @@ fn packet_to_inbound_frame( } let from = packet.from.unwrap_or(0); if Some(from) == local_node_num { + debug!( + from = format!("!{:08x}", from), + "Ignoring Meshtastic local echo packet" + ); return None; } info!( @@ -1132,6 +1150,12 @@ fn encode_mesh_packet(to: u32, portnum: u32, payload: &[u8]) -> Vec { encode_len_field(4, &decoded, &mut packet); encode_fixed32_field(6, next_packet_id(), &mut packet); encode_fixed32_field(7, now_unix_secs(), &mut packet); + // Meshtastic treats an unset hop_limit as zero, i.e. direct-neighbor only. + // Set a normal mesh hop limit so stock-device DMs can route beyond one hop. + encode_varint_field_into(9, 3, &mut packet); + if to != BROADCAST_NUM { + encode_varint_field_into(10, 1, &mut packet); + } packet } diff --git a/neode-ui/src/views/Mesh.vue b/neode-ui/src/views/Mesh.vue index 33186b70..e45e577c 100644 --- a/neode-ui/src/views/Mesh.vue +++ b/neode-ui/src/views/Mesh.vue @@ -1076,12 +1076,12 @@ function isEditedMessage(msg: MeshMessage): number | null { function isDeletedMessage(msg: MeshMessage): boolean { return msg.message_type === 'delete' || msg.typed_payload?.deleted === true } -/// Short label for the per-message transport pill (Mesh / FIPS / Tor), or null +/// Short label for the per-message transport pill (LoRa / FIPS / Tor), or null /// when the transport isn't known. Covers both meshcore and meshtastic since /// the field lives on the shared MeshMessage. function transportLabel(msg: MeshMessage): string | null { switch (msg.transport) { - case 'lora': return 'Mesh' + case 'lora': return 'LoRa' case 'fips': return 'FIPS' case 'tor': return 'Tor' default: return null