🏠 Home → Software → RTLV → Parsing Walkthroughs
🤖 Auto-generated from firmware (SSOT). Do not hand-edit — change the descriptor and regenerate. RTLV v3 ·
source_hash eb6b51128c1b
A teaching decode/encode of RTLV — byte-by-byte, with offsets, types, endianness and arithmetic. Order: frame header → scalar/scaled/composite/bitmask → two LE exceptions → transport (0xF0/0xF5) → unknown tag → ENCODE.
FPort 4 · DD 05 10 01 3F
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| 0 | DD |
prefix | uint8 | DATA_DISCRETE |
hi D=DATA, lo D=DISCRETE |
| 1 | 05 |
seq | uint8 | 5 | rolling counter; not part of TLV body |
| 2 | 10 |
tag | uint8 | gpio_inputs (FPort 4) |
first TLV at tlv_offset=2 |
| 3 | 01 |
len | uint8 | 1 | value length |
| 4 | 3F |
value | bitmask | bits 0–5 set | decoded in WT-5 |
0x05 battery_voltage_mvglobal · D0 05 05 02 0C 1C
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| 4–5 | 0C 1C |
value | uint16 BE | 3100 | 0x0C1C=3072+28; LE misread 0x1C0C=7180 is wrong |
0x02 temperatureglobal · positive DD 05 02 02 00 DC, negative DD 05 02 02 FF C9
| Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|
00 DC |
value | int16 BE | 220 → 22.0 °C | bit15=0 positive; ×0.1 |
FF C9 |
value | int16 BE | −55 → −5.5 °C | 0xFFC9=65481; bit15=1 → 65481−65536=−55; ×0.1 AFTER signed convert |
0x01 device_statusglobal · 01 02 36 40
| Offset | Bytes | Field | Type | Decoded | Explanation |
|---|---|---|---|---|---|
| +0 | 36 |
battery_percent | uint8 | 54 % | 0x36=54 |
| +1 | 40 |
status_flags | uint8 bitfield | bit6 set | 0x40 → bit6=low_batt (only bits 5–6 named in fw) |
0x10 gpio_inputsFPort 4 · 10 01 3F
Value 0x3F = 0b00111111. Test bit n with (value>>n)&1; bits[] is LSB-first.
| Bit | Mask | Name | Set? |
|---|---|---|---|
| 0 | 0x01 |
in1 | 1 |
| 1 | 0x02 |
in2 | 1 |
| 2 | 0x04 |
in3 | 1 |
| 3 | 0x08 |
in4 | 1 |
| 4 | 0x10 |
in5 | 1 |
| 5 | 0x20 |
in6 | 1 |
| 6 | 0x40 |
reserved | 0 |
| 7 | 0x80 |
reserved | 0 |
0x07 DEVICE_TIMEglobal · 07 06 00 F1 53 65 B4 00
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| +0..+3 | 00 F1 53 65 |
unix | uint32 LE | 1700000000 | reverse → 0x6553F100 |
| +4..+5 | B4 00 |
tz_min | int16 LE | +180 (UTC+3) | reverse → 0x00B4=180 |
Contrast: a BE misread of unix = 0x00F15365 = 15 815 525 (mid-1970, off ~54 yr). The truth is the LE reading.
0xCB DAILY_GPIO_EVENTSFPort 100 · CB 18 64 00 00 00 FA 00 00 00 …
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| +0..+3 | 64 00 00 00 |
gpio1 | uint32 LE | 100 | reverse → 0x64 |
| +4..+7 | FA 00 00 00 |
gpio2 | uint32 LE | 250 | reverse → 0xFA |
Note: 0x12 gpio_counters (FPort 4) has the same 6×u32 layout but BE — endianness is per-(fport,tag), not per-shape.
0xF0 COMMAND_RESPONSE envelope + port-scoped echoFPort 3 · F0 09 0A 00 00 00 03 02 02 00 DC
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| 0 | F0 |
prefix | uint8 | COMMAND_RESPONSE | |
| 1 | 09 |
LEN | uint8 | 9 | bytes after [F0][LEN] = 5 header tail + 4 echo |
| 2 | 0A |
cmd_id | uint8 | 10 | |
| 3 | 00 |
status | uint8 | 0=OK | |
| 4 | 00 |
error_code | uint8 | 0 | |
| 5 | 00 |
flags | uint8 | 0 | |
| 6 | 03 |
protocol_version | uint8 | 3 | |
| 7 | 02 |
echo tag | uint8 | temperature_celsius |
tlv_offset=7, port-scoped (FPort 3) |
| 8 | 02 |
echo len | uint8 | 2 | |
| 9–10 | 00 DC |
echo value | int16 BE | 22.0 °C | 220×0.1 |
Error contrast F0 05 12 02 01 00 03: cmd 0x12, status 2 (fail), err 1, LEN=5 (no echo).
0xF5 fragment lead header + reassemblycrafted (1 of 2) · F5 03 01 02 2A D0 05 …
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| 0 | F5 |
prefix | uint8 | FRAGMENT | frame-level |
| 1 | 03 |
header_len | uint8 | 3 | three header bytes follow |
| 2 | 01 |
fragment_index | uint8 | 1 | 1-based on the wire |
| 3 | 02 |
total_fragments | uint8 | 2 | |
| 4 | 2A |
message_id | uint8 | 42 | same across the message |
| 5… | D0 05 … |
payload slice | bytes | inner-frame bytes |
Reassembly: group by message_id, order by index, strip each 5-byte header, concat in index order, parse as one frame.
FPort 199 · CF 05 E1 03 00 FA CE
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| 0 | CF |
prefix | uint8 | CONFIG_UART | config-echo |
| 2 | E1 |
tag | uint8 | 0xE1 |
reference decoder surfaces raw |
| 3 | 03 |
len | uint8 | 3 | |
| 4 | 00 |
value[0] | raw | 0x00 |
config-echo key byte |
| 5–6 | FA CE |
value[1..2] | raw | 0xFACE |
preserved opaque |
Rule: always honor len, read exactly len opaque bytes, advance to offset+2+len; never guess width from the tag number.
set_device_class (length-polymorphic)FPort 199 cmd 0x15
| Form | Bytes | Meaning |
|---|---|---|
| GET | 15 00 |
len0 — read current class |
| SET permanent | 15 01 02 |
len1 — Class C, written to FRAM |
| SET timed | 15 03 02 00 3C |
len3 — class 0x02, duration 0x003C=60 s (u16 BE), runtime-only |
class ∈ {0=A, 2=C} else INVALID_PARAM; duration 0/absent = permanent (lorawan_commands.c:4299).
factory_reset (magic)FPort 1 cmd 0xE1 · E1 02 DE AD
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| 0 | E1 |
cmd_id | uint8 | factory_reset | |
| 1 | 02 |
len | uint8 | 2 | |
| 2–3 | DE AD |
magic | uint16 BE | 0xDEAD=57005 (full) |
0xFACE=soft; success → status REBOOT_PENDING(0x0A) |
Same 0xDEAD guards reset_all_stats (100/0x8F) and factory_reset_keys (199/0xF2); factory_reset_lorawan (199/0xF1) uses 0xCA11.
hart_set_monitor (float32 BE)FPort 6 cmd 0x19 · 19 0A 00 01 42 A0 00 00 00 00 00 00
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| 2 | 00 |
slot | uint8 | 0 | |
| 3 | 01 |
mode | uint8 | 1=above_high | |
| 4–7 | 42 A0 00 00 |
high | float32 BE | 80.0 | IEEE-754: sign0, exp 133−127=6, mant 1.25 → 1.25×2⁶ |
| 8–11 | 00 00 00 00 |
low | float32 BE | 0.0 |
Firmware reassembles BE u32 then memcpy to float (lorawan_commands_port6.c:1182); echoes 0xCF tag 0x1F value [mode][high BE][low BE]. Sanity: hart_set_pv_range 03 0A 00 20 42 C8 00 00 00 00 00 00 → upper 0x42C80000=100.0.
multicast_deleteFPort 199 cmd 0x16 · 16 01 00
| Offset | Bytes | Field | Type/Endian | Decoded | Explanation |
|---|---|---|---|---|---|
| 0 | 16 |
cmd_id | uint8 | multicast_delete | |
| 1 | 01 |
len | uint8 | 1 | len ≥ 1 required |
| 2 | 00 |
group_id | uint8 | 0 | group_id ≥ FRAM_MCAST_MAX_GROUPS → INVALID_PARAM (lorawan_commands.c:4405) |