Skip to main content

Wire Format

Every frame follows the OpenSynaptic FULL packet layout (C89, big-endian):

[cmd:1][route:1][aid:4BE][tid:1][ts:6BE][body...][crc8:1][crc16:2]
─────────────── 13-byte header ─────────────── ─body─ ──3 CRC──

Field Definitions

FieldSizeC89 typeDescription
cmd1 Bosrx_u80x3F (63) = DATA_FULL plaintext. 0x40 (64) = encrypted (not decoded by RX).
route1 Bosrx_u8Routing/hop-count flags
aid4 Bosrx_u32Source agent ID, big-endian
tid1 Bosrx_u8Transaction ID (wraps 0–255)
ts6 Bosrx_u64Unix timestamp seconds, 48-bit big-endian
bodyvariablesensor_id|unit|b62value
crc81 Bosrx_u8CRC-8/SMBUS (poly 0x07, init 0x00) over body only
crc162 Bosrx_u16CRC-16/CCITT-FALSE (poly 0x1021, init 0xFFFF) over full frame

Minimum frame size: 19 bytes (2-char sid, 1-char unit, 1-char b62 value).


Body Format

The body contains pipe-delimited ASCII fields:

<sensor_id> '|' <unit> '|' <b62_value>
Sub-fieldMax length (default)Description
sensor_id8 chars + NUL (OSRX_ID_MAX=9)Short alphanumeric sensor identifier, e.g. T1, HUMID
unit8 chars + NUL (OSRX_UNIT_MAX=9)UCUM-style unit string, e.g. Cel, Pct, m/s2
b62_value13 chars + NUL (OSRX_B62_MAX=14)Base62-encoded fixed-point integer (scale 10000)

Example body (hex, ASCII shown):

54 31 7C 43 65 6C 7C 4E 41 31
T 1 | C e l | N A 1

This decodes as T1 | Cel | NA1 → sensor T1, unit Celsius, value = Base62 decode of "NA1".


CRC Algorithms

CRC-8/SMBUS

  • Polynomial: 0x07
  • Initial value: 0x00
  • Input/Output reflected: No
  • Covers: body bytes only (from first byte after header to byte before CRC-8)

Standard check value: 0xF4 for ASCII string "123456789".

/* Bit-loop CRC-8 — no lookup table, 0 B RAM */
osrx_u8 osrx_crc8(const osrx_u8 *data, int len, osrx_u8 poly, osrx_u8 init);

CRC-16/CCITT-FALSE

  • Polynomial: 0x1021
  • Initial value: 0xFFFF
  • Input/Output reflected: No
  • Covers: entire frame from cmd through crc8 (inclusive)

Standard check value: 0x29B1 for ASCII string "123456789".

/* Bit-loop CRC-16 — no lookup table, 0 B RAM */
osrx_u16 osrx_crc16(const osrx_u8 *data, int len, osrx_u16 poly, osrx_u16 init);

Base62 Decode Algorithm

The b62_value field encodes a signed 32-bit integer using Base62 (alphabet 0–9A–Za–z). The encoding is big-endian positional, with the most-significant digit first.

alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

Decode formula (for N characters):

value = d[0]×62^(N-1) + d[1]×62^(N-2) + ... + d[N-1]×62^0

Where d[i] is the digit position of character s[i] in the alphabet.

b62 stringDecoded int32Real value (÷10000)
000.0000
A100.0010
a360.0036
NA1(23×62² + 10×62 + 1) = 88,2498.8249
2Rbs215,00021.5000 °C

OSynaptic-RX provides osrx_b62_decode():

osrx_i32 osrx_b62_decode(const char *s, int len, int *ok);
/* s need not be NUL-terminated; len = number of significant characters */

Annotated Hex Dump

A real 30-byte sensor frame carrying T1=21.5 °C from agent 0x00000001:

Offset  Hex   Meaning
──────────────────────────────────────────────────────────────
0 3F cmd = 0x3F (DATA_FULL plaintext)
1 00 route flags
2-5 00 00 00 01 aid = 1 (big-endian)
6 05 tid = 5
7-12 00 00 65 F0 3D 00 ts = Unix seconds (big-endian 48-bit)
13 54 31 7C 43 65 6C 7C 32 52 62 73 body = "T1|Cel|2Rbs"
24 XX crc8 (over body bytes 13..23)
25-26 YY ZZ crc16 (over entire frame 0..24, big-endian)

Full detailed specification: docs/03-wire-format.md in the repository.