Summary
Two bugs prevented send_raw_data from working correctly end-to-end:
- messaging.py – The command frame sent to the firmware was missing the mandatory
path_len byte, causing every call to return ERR_CODE_UNSUPPORTED_CMD.
- reader.py – The incoming
PUSH_CODE_RAW_DATA frame was parsed incorrectly: a reserved byte was not skipped and only 4 payload bytes were read, silently truncating any payload longer than 3 bytes.
Fix 1 – commands/messaging.py: wrong frame format for CMD_SEND_RAW_DATA
Root cause:
The firmware expects the frame 0x19 | path_len (1 byte, signed int8) | path (path_len bytes) | payload (≥ 4 bytes) – see MyMesh.cpp (companion_radio example).
The old implementation sent b"\x19" + payload, skipping path_len entirely. The firmware read payload[0] as path_len (e.g. 0x3D = 61), then checked 2 + 61 + 4 ≤ frame_length → FALSE → writeUnsupportedCmdFrame().
Additionally, the firmware responds with writeOKFrame() (RESP_CODE_OK = 0x00) on success, not with PUSH_CODE_MSG_SENT. The old code awaited EventType.MSG_SENT, so a successful send was never resolved.
Before:
data = b"\x19" + bytes(payload)
return await self.send(data, [EventType.MSG_SENT, EventType.ERROR])
After:
data = bytes([0x19, len(path)]) + path + bytes(payload)
return await self.send(data, [EventType.OK, EventType.ERROR])
Fix 2 – reader.py: PUSH_CODE_RAW_DATA frame not parsed correctly
Root cause:
The firmware pushes [0x84, SNR×4 (int8), RSSI (int8), 0xFF (reserved), payload…] – the fourth byte is a reserved 0xFF placeholder (possibly path_len in a future firmware version).
The old code called dbuf.read(4) after RSSI, which consumed [0xFF, payload[0], payload[1], payload[2]] – the reserved byte ended up in the payload hex string and the last payload byte was dropped.
Before:
res["RSSI"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True)
res["payload"] = dbuf.read(4).hex()
After:
res["RSSI"] = int.from_bytes(dbuf.read(1), byteorder="little", signed=True)
dbuf.read(1) # skip reserved byte (0xFF, possibly path_len in future)
res["payload"] = dbuf.read().hex() # read all remaining payload bytes
Notes
- Flood mode (
path_len < 0) is explicitly unsupported in the current firmware and raises ERR_CODE_UNSUPPORTED_CMD. Only direct routing (path_len ≥ 0) is supported.
- The minimum payload size of 4 bytes is enforced by the firmware (
len > 1 + path_len + 4).
I
Summary
Two bugs prevented
send_raw_datafrom working correctly end-to-end:path_lenbyte, causing every call to returnERR_CODE_UNSUPPORTED_CMD.PUSH_CODE_RAW_DATAframe was parsed incorrectly: a reserved byte was not skipped and only 4 payload bytes were read, silently truncating any payload longer than 3 bytes.Fix 1 –
commands/messaging.py: wrong frame format forCMD_SEND_RAW_DATARoot cause:
The firmware expects the frame
0x19 | path_len (1 byte, signed int8) | path (path_len bytes) | payload (≥ 4 bytes)– seeMyMesh.cpp(companion_radio example).The old implementation sent
b"\x19" + payload, skippingpath_lenentirely. The firmware readpayload[0]aspath_len(e.g.0x3D = 61), then checked2 + 61 + 4 ≤ frame_length→FALSE→writeUnsupportedCmdFrame().Additionally, the firmware responds with
writeOKFrame()(RESP_CODE_OK = 0x00) on success, not withPUSH_CODE_MSG_SENT. The old code awaitedEventType.MSG_SENT, so a successful send was never resolved.Before:
After:
Fix 2 – reader.py:
PUSH_CODE_RAW_DATAframe not parsed correctlyRoot cause:
The firmware pushes
[0x84, SNR×4 (int8), RSSI (int8), 0xFF (reserved), payload…]– the fourth byte is a reserved0xFFplaceholder (possiblypath_lenin a future firmware version).The old code called
dbuf.read(4)afterRSSI, which consumed[0xFF, payload[0], payload[1], payload[2]]– the reserved byte ended up in the payload hex string and the last payload byte was dropped.Before:
After:
Notes
path_len < 0) is explicitly unsupported in the current firmware and raisesERR_CODE_UNSUPPORTED_CMD. Only direct routing (path_len ≥ 0) is supported.len > 1 + path_len + 4).I