# Stats Binary Frame Structures Binary frame structures for companion radio stats commands. All multi-byte integers use little-endian byte order. ## Command Codes | Command | Code | Description | |---------|------|-------------| | `CMD_GET_STATS` | 56 | Get statistics (2-byte command: code + sub-type) | ### Stats Sub-Types The `CMD_GET_STATS` command uses a 2-byte frame structure: - **Byte 0:** `CMD_GET_STATS` (56) - **Byte 1:** Stats sub-type: - `STATS_TYPE_CORE` (0) - Get core device statistics - `STATS_TYPE_RADIO` (1) - Get radio statistics - `STATS_TYPE_PACKETS` (2) - Get packet statistics ## Response Codes | Response | Code | Description | |----------|------|-------------| | `RESP_CODE_STATS` | 24 | Statistics response (2-byte response: code + sub-type) | ### Stats Response Sub-Types The `RESP_CODE_STATS` response uses a 2-byte header structure: - **Byte 0:** `RESP_CODE_STATS` (24) - **Byte 1:** Stats sub-type (matches command sub-type): - `STATS_TYPE_CORE` (0) - Core device statistics response - `STATS_TYPE_RADIO` (1) - Radio statistics response - `STATS_TYPE_PACKETS` (2) - Packet statistics response --- ## RESP_CODE_STATS + STATS_TYPE_CORE (24, 0) **Total Frame Size:** 11 bytes | Offset | Size | Type | Field Name | Description | Range/Notes | |--------|------|------|------------|-------------|-------------| | 0 | 1 | uint8_t | response_code | Always `0x18` (24) | - | | 1 | 1 | uint8_t | stats_type | Always `0x00` (STATS_TYPE_CORE) | - | | 2 | 2 | uint16_t | battery_mv | Battery voltage in millivolts | 0 - 65,535 | | 4 | 4 | uint32_t | uptime_secs | Device uptime in seconds | 0 - 4,294,967,295 | | 8 | 2 | uint16_t | errors | Error flags bitmask | - | | 10 | 1 | uint8_t | queue_len | Outbound packet queue length | 0 - 255 | ### Example Structure (C/C++) ```c struct StatsCore { uint8_t response_code; // 0x18 uint8_t stats_type; // 0x00 (STATS_TYPE_CORE) uint16_t battery_mv; uint32_t uptime_secs; uint16_t errors; uint8_t queue_len; } __attribute__((packed)); ``` --- ## RESP_CODE_STATS + STATS_TYPE_RADIO (24, 1) **Total Frame Size:** 14 bytes | Offset | Size | Type | Field Name | Description | Range/Notes | |--------|------|------|------------|-------------|-------------| | 0 | 1 | uint8_t | response_code | Always `0x18` (24) | - | | 1 | 1 | uint8_t | stats_type | Always `0x01` (STATS_TYPE_RADIO) | - | | 2 | 2 | int16_t | noise_floor | Radio noise floor in dBm | -140 to +10 | | 4 | 1 | int8_t | last_rssi | Last received signal strength in dBm | -128 to +127 | | 5 | 1 | int8_t | last_snr | SNR scaled by 4 | Divide by 4.0 for dB | | 6 | 4 | uint32_t | tx_air_secs | Cumulative transmit airtime in seconds | 0 - 4,294,967,295 | | 10 | 4 | uint32_t | rx_air_secs | Cumulative receive airtime in seconds | 0 - 4,294,967,295 | ### Example Structure (C/C++) ```c struct StatsRadio { uint8_t response_code; // 0x18 uint8_t stats_type; // 0x01 (STATS_TYPE_RADIO) int16_t noise_floor; int8_t last_rssi; int8_t last_snr; // Divide by 4.0 to get actual SNR in dB uint32_t tx_air_secs; uint32_t rx_air_secs; } __attribute__((packed)); ``` --- ## RESP_CODE_STATS + STATS_TYPE_PACKETS (24, 2) **Total Frame Size:** 26 bytes (legacy) or 30 bytes (includes `recv_errors`) | Offset | Size | Type | Field Name | Description | Range/Notes | |--------|------|------|------------|-------------|-------------| | 0 | 1 | uint8_t | response_code | Always `0x18` (24) | - | | 1 | 1 | uint8_t | stats_type | Always `0x02` (STATS_TYPE_PACKETS) | - | | 2 | 4 | uint32_t | recv | Total packets received | 0 - 4,294,967,295 | | 6 | 4 | uint32_t | sent | Total packets sent | 0 - 4,294,967,295 | | 10 | 4 | uint32_t | flood_tx | Packets sent via flood routing | 0 - 4,294,967,295 | | 14 | 4 | uint32_t | direct_tx | Packets sent via direct routing | 0 - 4,294,967,295 | | 18 | 4 | uint32_t | flood_rx | Packets received via flood routing | 0 - 4,294,967,295 | | 22 | 4 | uint32_t | direct_rx | Packets received via direct routing | 0 - 4,294,967,295 | | 26 | 4 | uint32_t | recv_errors | Receive/CRC errors (RadioLib); present only in 30-byte frame | 0 - 4,294,967,295 | ### Notes - Counters are cumulative from boot and may wrap. - `recv = flood_rx + direct_rx` - `sent = flood_tx + direct_tx` - Clients should accept frame length ≥ 26; if length ≥ 30, parse `recv_errors` at offset 26. ### Example Structure (C/C++) ```c struct StatsPackets { uint8_t response_code; // 0x18 uint8_t stats_type; // 0x02 (STATS_TYPE_PACKETS) uint32_t recv; uint32_t sent; uint32_t flood_tx; uint32_t direct_tx; uint32_t flood_rx; uint32_t direct_rx; uint32_t recv_errors; // present when frame size is 30 } __attribute__((packed)); ``` --- ## Command Usage Example (Python) ```python # Send CMD_GET_STATS command def send_get_stats_core(serial_interface): """Send command to get core stats""" cmd = bytes([56, 0]) # CMD_GET_STATS (56) + STATS_TYPE_CORE (0) serial_interface.write(cmd) def send_get_stats_radio(serial_interface): """Send command to get radio stats""" cmd = bytes([56, 1]) # CMD_GET_STATS (56) + STATS_TYPE_RADIO (1) serial_interface.write(cmd) def send_get_stats_packets(serial_interface): """Send command to get packet stats""" cmd = bytes([56, 2]) # CMD_GET_STATS (56) + STATS_TYPE_PACKETS (2) serial_interface.write(cmd) ``` --- ## Response Parsing Example (Python) ```python import struct def parse_stats_core(frame): """Parse RESP_CODE_STATS + STATS_TYPE_CORE frame (11 bytes)""" response_code, stats_type, battery_mv, uptime_secs, errors, queue_len = \ struct.unpack('= 26, "STATS_TYPE_PACKETS frame too short" response_code, stats_type, recv, sent, flood_tx, direct_tx, flood_rx, direct_rx = \ struct.unpack('= 30: (recv_errors,) = struct.unpack('= 30) { result.recv_errors = view.getUint32(26, true); } return result; } ``` --- ## Field Size Considerations - Packet counters (uint32_t): May wrap after extended high-traffic operation. - Time fields (uint32_t): Max ~136 years. - SNR (int8_t, scaled by 4): Range -32 to +31.75 dB, 0.25 dB precision.