From c596271748d03074441b2316a93b520fd699dca9 Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Sun, 29 Mar 2026 16:56:13 -0600 Subject: [PATCH] refactor: update Python examples for fixed-size LE wire format Remove all varint/zigzag encoding. Every field is now struct.pack/unpack with fixed-size little-endian format strings. RadioConfig is a single struct.pack(' bytes | None: return None -# ── Postcard encoding ──────────────────────────────────────────── - -def varint(n: int) -> bytes: - out = [] - while n >= 0x80: - out.append((n & 0x7F) | 0x80) - n >>= 7 - out.append(n & 0x7F) - return bytes(out) - - -def zigzag(n: int) -> bytes: - return varint((n << 1) ^ (n >> 31) if n >= 0 else ((-n - 1) << 1) | 1) - - -def decode_varint(data: bytes) -> tuple[int, bytes]: - n, shift = 0, 0 - for i, b in enumerate(data): - n |= (b & 0x7F) << shift - shift += 7 - if not (b & 0x80): - return n, data[i + 1:] - return n, b"" - - -def decode_zigzag(data: bytes) -> tuple[int, bytes]: - n, rest = decode_varint(data) - return (n >> 1) ^ -(n & 1), rest - - -# ── Command encoding ───────────────────────────────────────────── +# ── Fixed-size LE encoding ──────────────────────────────────────── def encode_config(cfg: dict) -> bytes: - """Encode a RadioConfig dict to postcard bytes. - - Postcard wire format: u8/i8 are raw bytes, u16/u32 are varint, - i16/i32 are zigzag+varint, enums are varint variant index. - """ - out = varint(cfg["freq_hz"]) # u32: varint - out += varint(cfg["bw"]) # enum: varint variant index - out += struct.pack("B", cfg["sf"]) # u8: raw byte - out += struct.pack("B", cfg["cr"]) # u8: raw byte - out += varint(cfg["sync_word"]) # u16: varint - out += struct.pack("b", cfg["tx_power_dbm"]) # i8: raw signed byte - return out + """Encode a RadioConfig to 10 fixed-size LE bytes.""" + return struct.pack(" bytes: - """Encode a command name + kwargs to postcard bytes.""" - commands = { + """Encode a command to fixed-size LE bytes.""" + tags = { "Ping": 0, "GetConfig": 1, "SetConfig": 2, "StartRx": 3, "StopRx": 4, "Transmit": 5, "DisplayOn": 6, "DisplayOff": 7, } - idx = commands[cmd] - out = varint(idx) + out = bytes([tags[cmd]]) if cmd == "SetConfig": out += encode_config(kwargs["config"]) elif cmd == "Transmit": @@ -144,36 +110,37 @@ def encode_command(cmd: str, **kwargs) -> bytes: else: out += b"\x01" + encode_config(config) payload = kwargs["payload"] - out += varint(len(payload)) + payload + out += struct.pack(" dict: - """Decode a postcard-encoded response into a dict.""" + """Decode a fixed-size LE response.""" if not data: return {"type": "Empty"} - variant = data[0] + tag = data[0] rest = data[1:] - if variant == 0: + if tag == 0: return {"type": "Pong"} - elif variant == 1: + elif tag == 1: return {"type": "Config", "raw": rest.hex()} - elif variant == 2: - rssi, rest = decode_zigzag(rest) - snr, rest = decode_zigzag(rest) - plen, rest = decode_varint(rest) - return {"type": "RxPacket", "rssi": rssi, "snr": snr, "payload": rest[:plen]} - elif variant == 3: + elif tag == 2: + rssi = struct.unpack_from("