fix(mesh): add txt_type + timestamp to CMD_SEND_CHANNEL_TXT_MSG frame

MeshCore firmware frame for cmd 0x03 is
`[cmd][txt_type][channel][timestamp_le32][text]`, not `[cmd][channel][text]`.
Missing txt_type + timestamp caused every channel broadcast to come
back with ERR_UNSUPPORTED, which broke the DM-via-channel path
entirely (nothing was reaching the radio). Bring the frame into
spec — verified against meshcore-dev/MeshCore docs/companion_protocol.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian 2026-04-14 11:22:20 -04:00
parent 164f938982
commit 5616bb74e6

View File

@ -222,6 +222,10 @@ pub fn build_send_text(dest_pubkey_prefix: &[u8; 6], msg: &[u8]) -> Result<Vec<u
}
/// CMD_SEND_CHANNEL_TXT_MSG (0x03): Broadcast a text message on a channel.
/// Frame layout per meshcore companion protocol:
/// `[0x03][txt_type=0][channel][timestamp_le32][text…]`
/// The txt_type and timestamp fields are mandatory — without them the
/// firmware rejects the command with ERR_UNSUPPORTED.
pub fn build_send_channel_text(channel: u8, msg: &[u8]) -> Result<Vec<u8>> {
if msg.len() > MAX_MESSAGE_LEN {
anyhow::bail!(
@ -230,7 +234,12 @@ pub fn build_send_channel_text(channel: u8, msg: &[u8]) -> Result<Vec<u8>> {
MAX_MESSAGE_LEN
);
}
let mut data = vec![CMD_SEND_CHANNEL_TXT_MSG, channel];
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs() as u32)
.unwrap_or(0);
let mut data = vec![CMD_SEND_CHANNEL_TXT_MSG, 0x00, channel];
data.extend_from_slice(&timestamp.to_le_bytes());
data.extend_from_slice(msg);
Ok(encode_frame(&data))
}
@ -697,10 +706,13 @@ mod tests {
#[test]
fn test_build_send_channel_text() -> Result<()> {
let frame = build_send_channel_text(0, b"test")?;
let frame = build_send_channel_text(2, b"test")?;
// Frame: [marker][len_lo][len_hi][cmd][txt_type][channel][ts(4)][text]
assert_eq!(frame[3], CMD_SEND_CHANNEL_TXT_MSG);
assert_eq!(frame[4], 0); // channel 0
assert_eq!(&frame[5..], b"test");
assert_eq!(frame[4], 0); // txt_type
assert_eq!(frame[5], 2); // channel idx
// frame[6..10] = timestamp, non-deterministic
assert_eq!(&frame[10..], b"test");
Ok(())
}