Stats-Binärframes – Frame-Strukturen
Diese Seite beschreibt die binären Frame-Strukturen für die Statistik-Befehle (Stats Commands) der Companion-Radio-Schnittstelle. Alle Ganzzahlen, die aus mehreren Bytes bestehen, werden in Little-Endian-Byte-Reihenfolge (niedrigstwertiges Byte zuerst) übertragen.
Befehlscodes
Befehl Code Beschreibung CMD_GET_STATS56 Statistiken abrufen (2-Byte-Befehl: Code + Untertyp)
Statistik-Untertypen
Der Befehl CMD_GET_STATS verwendet eine 2-Byte-Frame-Struktur:
Byte 0: CMD_GET_STATS (56)
Byte 1: Statistik-Untertyp:
- STATS_TYPE_CORE (0) – Grundlegende Gerätestatistiken abrufen
- STATS_TYPE_RADIO (1) – Funk-Statistiken abrufen
- STATS_TYPE_PACKETS (2) – Paket-Statistiken abrufen
Antwortcodes
Antwort Code Beschreibung RESP_CODE_STATS24 Statistik-Antwort (2-Byte-Antwort: Code + Untertyp)
Antwort-Untertypen
Die Antwort RESP_CODE_STATS verwendet eine 2-Byte-Header-Struktur:
Byte 0: RESP_CODE_STATS (24)
Byte 1: Statistik-Untertyp (entspricht dem Untertyp des Befehls):
- STATS_TYPE_CORE (0) – Antwort mit grundlegenden Gerätestatistiken
- STATS_TYPE_RADIO (1) – Antwort mit Funk-Statistiken
- STATS_TYPE_PACKETS (2) – Antwort mit Paket-Statistiken
---
RESP_CODE_STATS + STATS_TYPE_CORE (24, 0)
Gesamte Frame-Größe: 11 Bytes
Offset Größe Typ Feldname Beschreibung Wertebereich / Hinweise 0 1 uint8_t response_code Immer 0x18 (24) - 1 1 uint8_t stats_type Immer 0x00 (STATS_TYPE_CORE) - 2 2 uint16_t battery_mv Batteriespannung in Millivolt 0 – 65.535 4 4 uint32_t uptime_secs Betriebszeit des Geräts in Sekunden 0 – 4.294.967.295 8 2 uint16_t errors Fehler-Flags als Bitmaske - 10 1 uint8_t queue_len Länge der ausgehenden Paket-Warteschlange 0 – 255
Beispielstruktur (C/C++)
CLI
Copy
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)
Gesamte Frame-Größe: 14 Bytes
Offset Größe Typ Feldname Beschreibung Wertebereich / Hinweise 0 1 uint8_t response_code Immer 0x18 (24) - 1 1 uint8_t stats_type Immer 0x01 (STATS_TYPE_RADIO) - 2 2 int16_t noise_floor Grundrauschen (Noise Floor) des Funkmoduls in dBm -140 bis +10 4 1 int8_t last_rssi Signalstärke (RSSI) des letzten empfangenen Pakets in dBm -128 bis +127 5 1 int8_t last_snr SNR (Signal-Rausch-Verhältnis), skaliert mit Faktor 4 Durch 4,0 teilen für den Wert in dB 6 4 uint32_t tx_air_secs Kumulative Sendezeit (Airtime) in Sekunden 0 – 4.294.967.295 10 4 uint32_t rx_air_secs Kumulative Empfangszeit (Airtime) in Sekunden 0 – 4.294.967.295
Beispielstruktur (C/C++)
CLI
Copy
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; // Durch 4.0 teilen für den tatsächlichen SNR-Wert in dB
uint32_t tx_air_secs;
uint32_t rx_air_secs;
} __attribute__((packed));
---
RESP_CODE_STATS + STATS_TYPE_PACKETS (24, 2)
Gesamte Frame-Größe: 26 Bytes (Legacy) oder 30 Bytes (mit recv_errors)
Offset Größe Typ Feldname Beschreibung Wertebereich / Hinweise 0 1 uint8_t response_code Immer 0x18 (24) - 1 1 uint8_t stats_type Immer 0x02 (STATS_TYPE_PACKETS) - 2 4 uint32_t recv Gesamtanzahl empfangener Pakete 0 – 4.294.967.295 6 4 uint32_t sent Gesamtanzahl gesendeter Pakete 0 – 4.294.967.295 10 4 uint32_t flood_tx Über Flood-Routing (Flut-Weiterleitung) gesendete Pakete 0 – 4.294.967.295 14 4 uint32_t direct_tx Über Direct-Routing (direkte Weiterleitung) gesendete Pakete 0 – 4.294.967.295 18 4 uint32_t flood_rx Über Flood-Routing empfangene Pakete 0 – 4.294.967.295 22 4 uint32_t direct_rx Über Direct-Routing empfangene Pakete 0 – 4.294.967.295 26 4 uint32_t recv_errors Empfangs-/CRC-Fehler (RadioLib); nur im 30-Byte-Frame vorhanden 0 – 4.294.967.295
Hinweise
Die Zähler sind kumulativ seit dem letzten Bootvorgang und können bei Überlauf (Wrap-Around) wieder bei null beginnen.
recv = flood_rx + direct_rx
sent = flood_tx + direct_tx
Clients sollten eine Frame-Länge ≥ 26 akzeptieren. Wenn die Länge ≥ 30 beträgt, wird recv_errors ab Offset 26 geparst.
Beispielstruktur (C/C++)
CLI
Copy
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; // Nur vorhanden, wenn die Frame-Größe 30 Bytes beträgt
} __attribute__((packed));
---
Beispiel zur Befehlsverwendung (Python)
CLI
Copy
# CMD_GET_STATS-Befehl senden
def send_get_stats_core(serial_interface):
"""Befehl zum Abrufen der Core-Statistiken senden"""
cmd = bytes([56, 0]) # CMD_GET_STATS (56) + STATS_TYPE_CORE (0)
serial_interface.write(cmd)
def send_get_stats_radio(serial_interface):
"""Befehl zum Abrufen der Funk-Statistiken senden"""
cmd = bytes([56, 1]) # CMD_GET_STATS (56) + STATS_TYPE_RADIO (1)
serial_interface.write(cmd)
def send_get_stats_packets(serial_interface):
"""Befehl zum Abrufen der Paket-Statistiken senden"""
cmd = bytes([56, 2]) # CMD_GET_STATS (56) + STATS_TYPE_PACKETS (2)
serial_interface.write(cmd)
---
Beispiel zum Parsen der Antworten (Python)
CLI
Copy
import struct
def parse_stats_core(frame):
"""RESP_CODE_STATS + STATS_TYPE_CORE Frame (11 Bytes) parsen"""
response_code, stats_type, battery_mv, uptime_secs, errors, queue_len = \
struct.unpack('<B B H I H B', frame)
assert response_code == 24 and stats_type == 0, "Invalid response type"
return {
'battery_mv': battery_mv,
'uptime_secs': uptime_secs,
'errors': errors,
'queue_len': queue_len
}
def parse_stats_radio(frame):
"""RESP_CODE_STATS + STATS_TYPE_RADIO Frame (14 Bytes) parsen"""
response_code, stats_type, noise_floor, last_rssi, last_snr, tx_air_secs, rx_air_secs = \
struct.unpack('<B B h b b I I', frame)
assert response_code == 24 and stats_type == 1, "Invalid response type"
return {
'noise_floor': noise_floor,
'last_rssi': last_rssi,
'last_snr': last_snr / 4.0, # SNR-Skalierung rückgängig machen
'tx_air_secs': tx_air_secs,
'rx_air_secs': rx_air_secs
}
def parse_stats_packets(frame):
"""RESP_CODE_STATS + STATS_TYPE_PACKETS Frame (26 oder 30 Bytes) parsen"""
assert len(frame) >= 26, "STATS_TYPE_PACKETS frame too short"
response_code, stats_type, recv, sent, flood_tx, direct_tx, flood_rx, direct_rx = \
struct.unpack('<B B I I I I I I', frame[:26])
assert response_code == 24 and stats_type == 2, "Invalid response type"
result = {
'recv': recv,
'sent': sent,
'flood_tx': flood_tx,
'direct_tx': direct_tx,
'flood_rx': flood_rx,
'direct_rx': direct_rx
}
if len(frame) >= 30:
(recv_errors,) = struct.unpack('<I', frame[26:30])
result['recv_errors'] = recv_errors
return result
---
Beispiel zur Befehlsverwendung (JavaScript/TypeScript)
CLI
Copy
// CMD_GET_STATS-Befehl senden
const CMD_GET_STATS = 56;
const STATS_TYPE_CORE = 0;
const STATS_TYPE_RADIO = 1;
const STATS_TYPE_PACKETS = 2;
function sendGetStatsCore(serialInterface: SerialPort): void {
const cmd = new Uint8Array([CMD_GET_STATS, STATS_TYPE_CORE]);
serialInterface.write(cmd);
}
function sendGetStatsRadio(serialInterface: SerialPort): void {
const cmd = new Uint8Array([CMD_GET_STATS, STATS_TYPE_RADIO]);
serialInterface.write(cmd);
}
function sendGetStatsPackets(serialInterface: SerialPort): void {
const cmd = new Uint8Array([CMD_GET_STATS, STATS_TYPE_PACKETS]);
serialInterface.write(cmd);
}
---
Beispiel zum Parsen der Antworten (JavaScript/TypeScript)
CLI
Copy
interface StatsCore {
battery_mv: number;
uptime_secs: number;
errors: number;
queue_len: number;
}
interface StatsRadio {
noise_floor: number;
last_rssi: number;
last_snr: number;
tx_air_secs: number;
rx_air_secs: number;
}
interface StatsPackets {
recv: number;
sent: number;
flood_tx: number;
direct_tx: number;
flood_rx: number;
direct_rx: number;
recv_errors?: number; // Nur vorhanden, wenn der Frame 30 Bytes lang ist
}
function parseStatsCore(buffer: ArrayBuffer): StatsCore {
const view = new DataView(buffer);
const response_code = view.getUint8(0);
const stats_type = view.getUint8(1);
if (response_code !== 24 || stats_type !== 0) {
throw new Error('Invalid response type');
}
return {
battery_mv: view.getUint16(2, true),
uptime_secs: view.getUint32(4, true),
errors: view.getUint16(8, true),
queue_len: view.getUint8(10)
};
}
function parseStatsRadio(buffer: ArrayBuffer): StatsRadio {
const view = new DataView(buffer);
const response_code = view.getUint8(0);
const stats_type = view.getUint8(1);
if (response_code !== 24 || stats_type !== 1) {
throw new Error('Invalid response type');
}
return {
noise_floor: view.getInt16(2, true),
last_rssi: view.getInt8(4),
last_snr: view.getInt8(5) / 4.0, // SNR-Skalierung rückgängig machen
tx_air_secs: view.getUint32(6, true),
rx_air_secs: view.getUint32(10, true)
};
}
function parseStatsPackets(buffer: ArrayBuffer): StatsPackets {
const view = new DataView(buffer);
if (buffer.byteLength < 26) {
throw new Error('STATS_TYPE_PACKETS frame too short');
}
const response_code = view.getUint8(0);
const stats_type = view.getUint8(1);
if (response_code !== 24 || stats_type !== 2) {
throw new Error('Invalid response type');
}
const result: StatsPackets = {
recv: view.getUint32(2, true),
sent: view.getUint32(6, true),
flood_tx: view.getUint32(10, true),
direct_tx: view.getUint32(14, true),
flood_rx: view.getUint32(18, true),
direct_rx: view.getUint32(22, true)
};
if (buffer.byteLength >= 30) {
result.recv_errors = view.getUint32(26, true);
}
return result;
}
---
Hinweise zur Feldgröße
Paketzähler (uint32_t): Können nach längerem Betrieb mit hohem Datenaufkommen überlaufen (Wrap-Around).
Zeitfelder (uint32_t): Maximale Darstellung ca. 136 Jahre.
SNR (int8_t, skaliert mit Faktor 4): Wertebereich -32 bis +31,75 dB bei einer Auflösung von 0,25 dB.