@@ -94,7 +94,7 @@ struct StatsRadio {
9494
9595## RESP_CODE_STATS + STATS_TYPE_PACKETS (24, 2)
9696
97- ** Total Frame Size:** 26 bytes
97+ ** Total Frame Size:** 26 bytes (legacy) or 30 bytes (includes ` recv_errors ` )
9898
9999| Offset | Size | Type | Field Name | Description | Range/Notes |
100100| --------| ------| ------| ------------| -------------| -------------|
@@ -106,12 +106,14 @@ struct StatsRadio {
106106| 14 | 4 | uint32_t | direct_tx | Packets sent via direct routing | 0 - 4,294,967,295 |
107107| 18 | 4 | uint32_t | flood_rx | Packets received via flood routing | 0 - 4,294,967,295 |
108108| 22 | 4 | uint32_t | direct_rx | Packets received via direct routing | 0 - 4,294,967,295 |
109+ | 26 | 4 | uint32_t | recv_errors | Receive/CRC errors (RadioLib); present only in 30-byte frame | 0 - 4,294,967,295 |
109110
110111### Notes
111112
112113- Counters are cumulative from boot and may wrap.
113114- ` recv = flood_rx + direct_rx `
114115- ` sent = flood_tx + direct_tx `
116+ - Clients should accept frame length ≥ 26; if length ≥ 30, parse ` recv_errors ` at offset 26.
115117
116118### Example Structure (C/C++)
117119
@@ -125,6 +127,7 @@ struct StatsPackets {
125127 uint32_t direct_tx;
126128 uint32_t flood_rx;
127129 uint32_t direct_rx;
130+ uint32_t recv_errors; // present when frame size is 30
128131} __attribute__((packed));
129132```
130133
@@ -183,18 +186,23 @@ def parse_stats_radio(frame):
183186 }
184187
185188def parse_stats_packets (frame ):
186- """ Parse RESP_CODE_STATS + STATS_TYPE_PACKETS frame (26 bytes)"""
189+ """ Parse RESP_CODE_STATS + STATS_TYPE_PACKETS frame (26 or 30 bytes)"""
190+ assert len (frame) >= 26 , " STATS_TYPE_PACKETS frame too short"
187191 response_code, stats_type, recv, sent, flood_tx, direct_tx, flood_rx, direct_rx = \
188- struct.unpack(' <B B I I I I I I' , frame)
192+ struct.unpack(' <B B I I I I I I' , frame[: 26 ] )
189193 assert response_code == 24 and stats_type == 2 , " Invalid response type"
190- return {
194+ result = {
191195 ' recv' : recv,
192196 ' sent' : sent,
193197 ' flood_tx' : flood_tx,
194198 ' direct_tx' : direct_tx,
195199 ' flood_rx' : flood_rx,
196200 ' direct_rx' : direct_rx
197201 }
202+ if len (frame) >= 30 :
203+ (recv_errors,) = struct.unpack(' <I' , frame[26 :30 ])
204+ result[' recv_errors' ] = recv_errors
205+ return result
198206```
199207
200208---
@@ -251,6 +259,7 @@ interface StatsPackets {
251259 direct_tx: number ;
252260 flood_rx: number ;
253261 direct_rx: number ;
262+ recv_errors? : number ; // present when frame is 30 bytes
254263}
255264
256265function parseStatsCore(buffer : ArrayBuffer ): StatsCore {
@@ -286,19 +295,26 @@ function parseStatsRadio(buffer: ArrayBuffer): StatsRadio {
286295
287296function parseStatsPackets(buffer : ArrayBuffer ): StatsPackets {
288297 const view = new DataView (buffer );
298+ if (buffer .byteLength < 26 ) {
299+ throw new Error (' STATS_TYPE_PACKETS frame too short' );
300+ }
289301 const response_code = view .getUint8 (0 );
290302 const stats_type = view .getUint8 (1 );
291303 if (response_code !== 24 || stats_type !== 2 ) {
292304 throw new Error (' Invalid response type' );
293305 }
294- return {
306+ const result : StatsPackets = {
295307 recv: view .getUint32 (2 , true ),
296308 sent: view .getUint32 (6 , true ),
297309 flood_tx: view .getUint32 (10 , true ),
298310 direct_tx: view .getUint32 (14 , true ),
299311 flood_rx: view .getUint32 (18 , true ),
300312 direct_rx: view .getUint32 (22 , true )
301313 };
314+ if (buffer .byteLength >= 30 ) {
315+ result .recv_errors = view .getUint32 (26 , true );
316+ }
317+ return result ;
302318}
303319```
304320
0 commit comments