EN

Stats-Binärframes

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

BefehlCodeBeschreibung
CMD_GET_STATS56Statistiken 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

AntwortCodeBeschreibung
RESP_CODE_STATS24Statistik-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
OffsetGrößeTypFeldnameBeschreibungWertebereich / Hinweise
01uint8_tresponse_codeImmer 0x18 (24)-
11uint8_tstats_typeImmer 0x00 (STATS_TYPE_CORE)-
22uint16_tbattery_mvBatteriespannung in Millivolt0 – 65.535
44uint32_tuptime_secsBetriebszeit des Geräts in Sekunden0 – 4.294.967.295
82uint16_terrorsFehler-Flags als Bitmaske-
101uint8_tqueue_lenLänge der ausgehenden Paket-Warteschlange0 – 255

Beispielstruktur (C/C++)

CLI
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
OffsetGrößeTypFeldnameBeschreibungWertebereich / Hinweise
01uint8_tresponse_codeImmer 0x18 (24)-
11uint8_tstats_typeImmer 0x01 (STATS_TYPE_RADIO)-
22int16_tnoise_floorGrundrauschen (Noise Floor) des Funkmoduls in dBm-140 bis +10
41int8_tlast_rssiSignalstärke (RSSI) des letzten empfangenen Pakets in dBm-128 bis +127
51int8_tlast_snrSNR (Signal-Rausch-Verhältnis), skaliert mit Faktor 4Durch 4,0 teilen für den Wert in dB
64uint32_ttx_air_secsKumulative Sendezeit (Airtime) in Sekunden0 – 4.294.967.295
104uint32_trx_air_secsKumulative Empfangszeit (Airtime) in Sekunden0 – 4.294.967.295

Beispielstruktur (C/C++)

CLI
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)
OffsetGrößeTypFeldnameBeschreibungWertebereich / Hinweise
01uint8_tresponse_codeImmer 0x18 (24)-
11uint8_tstats_typeImmer 0x02 (STATS_TYPE_PACKETS)-
24uint32_trecvGesamtanzahl empfangener Pakete0 – 4.294.967.295
64uint32_tsentGesamtanzahl gesendeter Pakete0 – 4.294.967.295
104uint32_tflood_txÜber Flood-Routing (Flut-Weiterleitung) gesendete Pakete0 – 4.294.967.295
144uint32_tdirect_txÜber Direct-Routing (direkte Weiterleitung) gesendete Pakete0 – 4.294.967.295
184uint32_tflood_rxÜber Flood-Routing empfangene Pakete0 – 4.294.967.295
224uint32_tdirect_rxÜber Direct-Routing empfangene Pakete0 – 4.294.967.295
264uint32_trecv_errorsEmpfangs-/CRC-Fehler (RadioLib); nur im 30-Byte-Frame vorhanden0 – 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
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
# 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
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
// 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
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.

Quelle: docs.meshcore.io