How to check a decoded packet by hand
- Download the decoder config(s) below (one file per packet type and schema, or all in one file) and decode_frame.py.
- Take the raw frame of a packet (hex string) from the Packet page, or from the device.
- Run
python3 decode_frame.py --config all-decoder-configs.json --hex "<hex>". The script reads the header, picks the config the same way the pipeline does, and prints every byte range with its decoded value.
- Change a field in the config file (offset, length, type, factor) and run again to see the effect. The pipeline itself only changes when the config is updated in the backend.
- To compare with what the dashboard decoded, pass
--expect event.json (the event's data JSON).
1. Frame layout (what the first bytes mean)
All multi-byte numbers are little-endian. Offsets are from the first byte of the frame (byte 0).
Header · 20 bytes
Payload · N bytes (decoded by config)
CRC · 4
HMAC · 32
| Byte | Size | Field | Meaning |
| 0 | 1 | protocolVersion | Supported: 1 or 13 |
| 1 | 1 | headerVersion | Always 1 |
| 2 | 1 | schemaVersion | Chooses the config version (1 → v1, 2 → v2, 5 → v5) |
| 3 | 1 | packetTypeCode | Chooses the config packet type (list below) |
| 4 | 1 | deviceTypeCode | Device family |
| 5–8 | 4 | deviceNumericId | u32, the device id (IMEI number) |
| 9–12 | 4 | sequenceNo | u32, increases per packet |
| 13–16 | 4 | timestamp | u32, event time (epoch seconds) |
| 17 | 1 | firmwareVersion | Build number |
| 18–19 | 2 | reserved | Not used |
| 20 … | N | payload | N = frame length − 56. Config field index counts from byte 20. |
| last 36 bytes, first 4 | 4 | CRC32 | IEEE CRC of header + payload (0 = not checked) |
| last 32 | 32 | HMAC | Signature slot (not verified by the decoder) |
Packet type codes:
1 heartbeat · 2 location · 3 battery_summary · 4 vehicle_telemetry · 5 cell_detail · 6 fault_event · 7 diagnostic · 8 config_ack · 20 batch_location · 21 batch_telemetry · 22 combined_telemetry · 30 can_frame_bundle · 31 realtime_telemetry_bundle · 32 extended_data_bundle · 33 full_snapshot_bundle · 34 accident_snapshot_bundle · 35 logfile_meta.
The frame can arrive as raw bytes or as an ASCII hex string; the script takes the hex form.
2. How the config turns payload bytes into values
Each entry in
fields describes one value:
| Key | Meaning |
name / normalizedName | Name in the raw decoded output / in the normalized output. A dotted name such as battery.soc becomes a nested object when expandDotPaths is true. |
index | Start byte inside the payload (frame byte = 20 + index). |
length | Number of bytes. Must match the type (uint16 = 2, uint32 = 4 …). |
type | uint8/16/32/64, int8/16/32/64, ascii, bytes, flags (bit map), or array (with subLength per element). |
conversionFactor | Raw number is multiplied by this (e.g. 0.1 for a value sent in tenths). |
min / max | Allowed range. Outside it a warning is added (WARN) or the packet is rejected (STRICT). |
flags | For type flags: bit number → name. |
Top level:
endianness (little by default),
expectedPayloadLength (payload must have exactly this many bytes, 0 = not checked),
validationMode (WARN keeps the packet and lists problems, STRICT rejects it).
Config lookup: device type + packet type + protocol version + schema version from the header, newest active config wins. If none exists, schema v2 is tried as a fallback (
--fallback-schema v2 in the script).
3. Decoder configs (active)
| Packet type | Schema | Protocol | Payload bytes | Fields | Mode | Updated | |
| Loading... |
4. Script examples
python3 decode_frame.py --config all-decoder-configs.json --hex "01010205..."
python3 decode_frame.py --config decoder-realtime_telemetry_bundle-v5.json --hex-file frame.hex
python3 decode_frame.py --config all-decoder-configs.json --list
python3 decode_frame.py --config all-decoder-configs.json --hex "..." --json
python3 decode_frame.py --config all-decoder-configs.json --hex "..." --expect event.json
The script needs only Python 3 (no packages). It exits with code 1 when the pipeline would reject the frame (bad length, CRC, no matching config) or when
--expect finds a difference.