ESP32-S3 : Hidden WiFi driver counters

Hello.

ESP32-S3 (and likely ESP32) has a hidden internal WiFi statistics counter structure managed by the WiFi driver.

It does not count traffic (bytes) and does not track hardware errors, but some useful information can still be derived from it.

To access these counters, refer to the code below: it is an Arduino sketch which is pretty self-explanatory.

The code should compile as-is in the Arduino framework.

// This code demonstrates access to WiFi driver counters on ESP32 / ESP32-S3 in Arduino Framework.
//


#include <Arduino.h>
#include <stdint.h>

// Counters incremented by the WiFi driver. I did not find any place in Espressif's code
// where these counters are cleared, so it is up to the user to reset them if needed.
// The counters are cleared on startup, but are not reset when the interface goes
// up or down.
//
typedef struct {

  // STA RX stats
  uint32_t sta_total_rx;         // Total number of WiFi frames received by the STA
  uint32_t sta_data;             // Total number of WiFi data frames received by the STA
  uint16_t sta_ctl;              // Control frames
  uint16_t sta_mgmt;             // Management frames
  uint16_t sta_bcn_probe;        // Beacon/Probe frames

  // It is not clear why the counters below are 8-bit. Perhaps Espressif saves memory,
  // assuming these events are relatively rare. In any case, all counters in this
  // structure may (and will) overflow and wrap around.
  uint8_t  sta_assoc;            // Number of ASSOC requests?
  uint8_t  sta_auth;             // Number of successful authentication attempts?
  uint8_t  sta_deauth;           // Number of deauth frames?
  uint8_t  sta_action;           // Number of action frames?

  uint16_t sta_amsdu;            // AMSDU frames

  // AP RX stats
  uint32_t ap_total_rx;          // Same as above, but for the AP interface
  uint32_t ap_data;           
  uint16_t ap_ctl;            
  uint16_t ap_mgmt;           
  uint16_t ap_bcn_probe;      
  uint8_t  ap_assoc;          
  uint8_t  ap_auth;          
  uint8_t  ap_deauth;         
  uint8_t  ap_action;         
  uint16_t ap_amsdu;          

  // TX stats
  uint32_t ap_total_tx;          // AP: total number of transmitted frames
  uint32_t sta_total_tx;         // STA: total number of transmitted frames

  // Power Save Queue? Not sure about this block.
  // Structure field names were taken from Espressif code.
  //
  uint16_t psq_tx;               // Frames queued?
  uint16_t psq_mc;               // Multicast frames queued?
  uint16_t psq_uc;               // Unicast frames queued?

  uint16_t reorder;              // AMPDU reorder events?   <-- incremented on some errors
  uint16_t oos;                  // Out-of-sequence events? <-- incremented on some errors

  uint16_t ps_uc1;               // Unknown

  uint16_t tx_amsdu;             // TX AMSDU

} hmac_cnt_t;


// Declare the counters variable. The linker does the rest.
//
#ifdef __cplusplus
extern "C" {
#endif

extern hmac_cnt_t g_hmac_cnt;

#ifdef __cplusplus
};
#endif


void setup() {
  Serial.begin(115200);
}


void loop() {

  delay(1000);

  Serial.printf("STA total frames received=%lu, data frames=%lu, ctrl=%u, mgmt=%u\r\n"
                "STA total frames sent %lu\r\n",
                g_hmac_cnt.sta_total_rx,
                g_hmac_cnt.sta_data,
                g_hmac_cnt.sta_ctl,
                g_hmac_cnt.sta_mgmt,
                g_hmac_cnt.sta_total_tx
                );

}

Have you or will you check other WiFi implementations for these?

I am going to check ESP32 next :)

I quickly looked at ESP32 and ESP32-C6 WiFi code and yes, both have g_hmac_cnt exposed as public symbol of the same size.

Please, Espressif, keep it like that, don’t change anything :-D

Better to declare counters like this (note volatile)

typedef volatile struct {

...
...
...

} hmac_cnt_t;


Not surprised, an esp32 is an esp32. I meant Arduino, PICO, etc etc. Many of them use esp chips so they likely have it.

when I get a few minutes I will check a few of my non esp boards.

if your boards use espressif binary blobs, then you can simply search for “g_hmac_cnt“ in these blobs. If they are Espressif-based, then chances are high