Fork fix: the SDIO wedge is FIXED (esp-hosted-mcu #167)
Test, Build and Push / test-gateway (push) Successful in 12s
Test, Build and Push / release (push) Skipped
Test, Build and Push / build-gateway (push) Skipped

Root cause (verified against our exact IDF tree, not the community guess):
the "258" in "sdio_write_task: Failed to send data: 258" is NOT a timeout
(that is 263). 258 = 0x102 = ESP_ERR_INVALID_ARG. On the ESP32-P4, block-
mode CMD53 writes require the SOURCE buffer to be 64-byte (cache-line)
aligned; the IDF sdmmc driver rejects a misaligned source with INVALID_ARG
BEFORE any bus activity. esp_hosts write loop then declares "Unrecoverable
host sdio state" and reboots the whole P4. The audio TX payload is not
64-aligned, so streaming mic audio wedged on the very FIRST frame (which is
exactly what we saw: listening -> instant Failed to send -> reboot).

This also explains why buffer/queue/clock/retry tuning all did nothing: the
write never reached the bus. And why our symptom was instant, not after
~100 writes (the community block-mode-desync theory) — it is the first
misaligned buffer, every time.

Fix: vendored esp_hosted 2.12.11 as an editable local component (overrides
the registry copy) and bounce a misaligned TX payload through one aligned
DMA scratch buffer in hosted_sdio_write_block (port_esp_hosted_host_sdio.c).
TX is serialized by the bus lock so a single static bounce buffer is safe;
freed in hosted_sdio_deinit. Host-only change — no C6 reflash.

VERIFIED ON HARDWARE (autonomous self-test): 40s of continuous mic-audio
upstream streaming — the traffic that previously wedged on the first frame
— ran clean, zero timeouts, zero reboots. A guarded SDIO_TX_SELFTEST harness
is kept (compiled out) for future SDIO stress testing.

Credit: root cause + patch designed via multi-agent investigation; the
precise 258=INVALID_ARG decode (correcting the upstream community timeout
assumption) came from checking our actual esp_err.h.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 09:50:27 +02:00
co-authored by Claude Fable 5
parent ac9d24e0f9
commit cb5826e02b
666 changed files with 216925 additions and 0 deletions
@@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(itwt)
@@ -0,0 +1,31 @@
| Supported Hosts | ESP32-P4 |
| --------------- | -------- |
| Supported Co-processors | ESP32-C5 | ESP32-C6 |
| ----------------------- | -------- | -------- |
# Wifi itwt Example
## Introduction
This example shows how to use itwt of wifi.
Itwt only works in station mode. And AP needs to support the capability of itwt.
Itwt can works under different power save mode.
### Modem sleep (supported)
This is default mode. Under this mode, can support console command "itwt, probe" to config itwt.
* itwt: this command is for itwt setup/teardown/suspend.
* probe: this command will send a probe request to update tsf time with ap
### Light Sleep (will support)
Need system support light sleep. Console command will not support in this mode.
### Typical current consumption with Itwt enabled
### Typical current consumption with Itwt disabled
Note that current consumption and average current are higher when disabled.
@@ -0,0 +1,5 @@
idf_component_register(SRCS "itwt_main.c"
"wifi_stats_cmd.c"
"wifi_itwt_cmd.c"
PRIV_REQUIRES lwip esp_wifi esp_timer esp_pm
INCLUDE_DIRS ".")
@@ -0,0 +1,189 @@
menu "Example Configuration"
config EXAMPLE_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config EXAMPLE_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config EXAMPLE_ENABLE_STATIC_IP
bool "enable static ip"
default y
help
Enable static IP
config EXAMPLE_STATIC_IP_ADDR
string "Static IP address"
default "192.168.4.2"
depends on EXAMPLE_ENABLE_STATIC_IP
help
Set static IP address.
config EXAMPLE_STATIC_NETMASK_ADDR
string "Static netmask address"
default "255.255.255.0"
depends on EXAMPLE_ENABLE_STATIC_IP
help
Set static netmask address.
config EXAMPLE_STATIC_GW_ADDR
string "Static gateway address"
default "192.168.4.1"
depends on EXAMPLE_ENABLE_STATIC_IP
help
Set static gateway address.
config EXAMPLE_TWT_ENABLE_KEEP_ALIVE_QOS_NULL
bool "enable keep alive qos null"
default n
help
Enable send QOS NULL to keep alive during TWT.
menu "iTWT Configuration"
config EXAMPLE_ITWT_TRIGGER_ENABLE
bool "trigger-enabled"
default y
help
0- a non-trigger-enabled TWT, 1-a trigger-enabled TWT
config EXAMPLE_ITWT_ANNOUNCED
bool "announced"
default y
help
0- an unannounced TWT, 1-an announced TWT
config EXAMPLE_ITWT_MIN_WAKE_DURA
int "itwt minimum wake duration"
range 1 255
default 255
help
Nominal Minimum Wake Duration, indicates the minimum amount of time, in unit of 256 us, that the TWT
requesting STA expects that it needs to be awake. The value range is [1, 255].
config EXAMPLE_ITWT_WAKE_DURATION_UNIT
int "itwt wake duration unit"
range 0 1
default 0
help
TWT wake duration unit, 0: 256us 1: TU (TU = 1024us)
config EXAMPLE_ITWT_WAKE_INVL_EXPN
int "itwt wake interval exponent"
range 0 31
default 10
help
TWT Wake Interval Exponent, in microseconds. The value range is [0, 31].
config EXAMPLE_ITWT_WAKE_INVL_MANT
int "itwt wake interval mantissa"
range 1 65535
default 512
help
TWT Wake Interval Mantissa, in microseconds. The value range is [1, 65535].
config EXAMPLE_ITWT_ID
int "itwt connection id"
range 0 32767
default 0
help
TWT Connection id. The value range is [0, 32767].
config EXAMPLE_ITWT_SETUP_TIMEOUT_TIME_MS
int "itwt setup timeout times"
range 100 65535
default 5000
help
TWT setup timeout time, in microseconds. The value range is [100, 65535].
endmenu
choice EXAMPLE_MAX_CPU_FREQ
prompt "Maximum CPU frequency"
default EXAMPLE_MAX_CPU_FREQ_96 if IDF_TARGET_ESP32H2
default EXAMPLE_MAX_CPU_FREQ_80
depends on PM_ENABLE
help
Maximum CPU frequency to use for dynamic frequency scaling.
config EXAMPLE_MAX_CPU_FREQ_96
bool "96 MHz"
depends on IDF_TARGET_ESP32H2
config EXAMPLE_MAX_CPU_FREQ_80
bool "80 MHz"
depends on !IDF_TARGET_ESP32P4
config EXAMPLE_MAX_CPU_FREQ_90
bool "90 MHz"
depends on IDF_TARGET_ESP32P4
config EXAMPLE_MAX_CPU_FREQ_120
bool "120 MHz"
depends on IDF_TARGET_ESP32C2
config EXAMPLE_MAX_CPU_FREQ_160
bool "160 MHz"
depends on !IDF_TARGET_ESP32P4
config EXAMPLE_MAX_CPU_FREQ_180
bool "180 MHz"
depends on IDF_TARGET_ESP32P4
config EXAMPLE_MAX_CPU_FREQ_240
bool "240 MHz"
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C5
endchoice
config EXAMPLE_MAX_CPU_FREQ_MHZ
int
default 64 if EXAMPLE_MAX_CPU_FREQ_64
default 80 if EXAMPLE_MAX_CPU_FREQ_80
default 90 if EXAMPLE_MAX_CPU_FREQ_90
default 96 if EXAMPLE_MAX_CPU_FREQ_96
default 120 if EXAMPLE_MAX_CPU_FREQ_120
default 160 if EXAMPLE_MAX_CPU_FREQ_160
default 180 if EXAMPLE_MAX_CPU_FREQ_180
default 240 if EXAMPLE_MAX_CPU_FREQ_240
choice EXAMPLE_MIN_CPU_FREQ
prompt "Minimum CPU frequency"
default EXAMPLE_MIN_CPU_FREQ_10M if !IDF_TARGET_ESP32C5
default EXAMPLE_MIN_CPU_FREQ_12M if IDF_TARGET_ESP32C5
depends on PM_ENABLE
help
Minimum CPU frequency to use for dynamic frequency scaling.
Should be set to XTAL frequency or XTAL frequency divided by integer.
config EXAMPLE_MIN_CPU_FREQ_40M
bool "40 MHz (use with 40MHz XTAL)"
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_20M
bool "20 MHz (use with 40MHz XTAL)"
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_16M
bool "16 MHz (use with 32MHz XTAL)"
depends on XTAL_FREQ_32 || XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_12M
bool "12 MHz (use with 48MHz XTAL)"
depends on XTAL_FREQ_48 || XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_10M
bool "10 MHz (use with 40MHz XTAL)"
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_26M
bool "26 MHz (use with 26MHz XTAL)"
depends on XTAL_FREQ_26 || XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_13M
bool "13 MHz (use with 26MHz XTAL)"
depends on XTAL_FREQ_26 || XTAL_FREQ_AUTO
endchoice
config EXAMPLE_MIN_CPU_FREQ_MHZ
int
default 40 if EXAMPLE_MIN_CPU_FREQ_40M
default 20 if EXAMPLE_MIN_CPU_FREQ_20M
default 10 if EXAMPLE_MIN_CPU_FREQ_10M
default 26 if EXAMPLE_MIN_CPU_FREQ_26M
default 16 if EXAMPLE_MIN_CPU_FREQ_16M
default 13 if EXAMPLE_MIN_CPU_FREQ_13M
default 12 if EXAMPLE_MIN_CPU_FREQ_12M
endmenu
@@ -0,0 +1,14 @@
dependencies:
cmd_nvs:
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_nvs
cmd_system:
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_system
espressif/esp_hosted:
rules:
- if: target in [esp32p4, esp32h2]
version: '*'
espressif/esp_wifi_remote:
rules:
- if: target in [esp32p4, esp32h2]
- if: idf_version <6.1
version: '>=1.3.1'
@@ -0,0 +1,386 @@
/*
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* itwt Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
this example shows how to use itwt
set a router or a AP using the same SSID&PASSWORD as configuration of this example.
start esp32c6 and when it connected to AP it will setup itwt.
*/
#include <netdb.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "esp_console.h"
#include "cmd_system.h"
#include "esp_wifi_he.h"
#include "esp_pm.h"
#include "esp_timer.h"
#include "esp_idf_version.h"
#include "wifi_cmd.h"
/*******************************************************
* Constants
*******************************************************/
static const char *TAG = "itwt";
/*******************************************************
* Structures
*******************************************************/
/*******************************************************
* Variable Definitions
*******************************************************/
/*set the ssid and password via "idf.py menuconfig"*/
#define DEFAULT_SSID CONFIG_EXAMPLE_WIFI_SSID
#define DEFAULT_PWD CONFIG_EXAMPLE_WIFI_PASSWORD
#define ITWT_SETUP_SUCCESS 1
#if CONFIG_EXAMPLE_TWT_ENABLE_KEEP_ALIVE_QOS_NULL
bool keep_alive_enabled = true;
#else
bool keep_alive_enabled = false;
#endif
#if CONFIG_EXAMPLE_ITWT_TRIGGER_ENABLE
uint8_t trigger_enabled = 1;
#else
uint8_t trigger_enabled = 0;
#endif
#if CONFIG_EXAMPLE_ITWT_ANNOUNCED
uint8_t flow_type_announced = 1;
#else
uint8_t flow_type_announced = 0;
#endif
esp_netif_t *netif_sta = NULL;
const int CONNECTED_BIT = BIT0;
const int DISCONNECTED_BIT = BIT1;
EventGroupHandle_t wifi_event_group;
/*******************************************************
* Function Declarations
*******************************************************/
/*******************************************************
* Function Definitions
*******************************************************/
static void example_set_static_ip(esp_netif_t *netif)
{
#if CONFIG_EXAMPLE_ENABLE_STATIC_IP
if (esp_netif_dhcpc_stop(netif) != ESP_OK) {
ESP_LOGE(TAG, "Failed to stop dhcp client");
return;
}
esp_netif_ip_info_t ip;
memset(&ip, 0 , sizeof(esp_netif_ip_info_t));
ip.ip.addr = ipaddr_addr(CONFIG_EXAMPLE_STATIC_IP_ADDR);
ip.netmask.addr = ipaddr_addr(CONFIG_EXAMPLE_STATIC_NETMASK_ADDR);
ip.gw.addr = ipaddr_addr(CONFIG_EXAMPLE_STATIC_GW_ADDR);
if (esp_netif_set_ip_info(netif, &ip) != ESP_OK) {
ESP_LOGE(TAG, "Failed to set ip info");
return;
}
ESP_LOGI(TAG, "Success to set static ip: %s, netmask: %s, gw: %s",
CONFIG_EXAMPLE_STATIC_IP_ADDR, CONFIG_EXAMPLE_STATIC_NETMASK_ADDR, CONFIG_EXAMPLE_STATIC_GW_ADDR);
#endif
}
static const char *itwt_probe_status_to_str(wifi_itwt_probe_status_t status)
{
switch (status) {
case ITWT_PROBE_FAIL: return "itwt probe fail";
case ITWT_PROBE_SUCCESS: return "itwt probe success";
case ITWT_PROBE_TIMEOUT: return "itwt probe timeout";
case ITWT_PROBE_STA_DISCONNECTED: return "Sta disconnected";
default: return "Unknown status";
}
}
static void got_ip_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
xEventGroupClearBits(wifi_event_group, DISCONNECTED_BIT);
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
/* setup a trigger-based announce individual TWT agreement. */
wifi_phy_mode_t phymode;
wifi_config_t sta_cfg = { 0, };
esp_wifi_get_config(WIFI_IF_STA, &sta_cfg);
esp_wifi_sta_get_negotiated_phymode(&phymode);
if (phymode == WIFI_PHY_MODE_HE20) {
esp_err_t err = ESP_OK;
#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 3, 0)
wifi_itwt_setup_config_t setup_config
#else
// wifi_itwt_setup_config_t only found in IDF v5.3.1 and above
wifi_twt_setup_config_t setup_config
#endif
= {
.setup_cmd = TWT_REQUEST,
.flow_id = 0,
.twt_id = CONFIG_EXAMPLE_ITWT_ID,
.flow_type = flow_type_announced ? 0 : 1,
.min_wake_dura = CONFIG_EXAMPLE_ITWT_MIN_WAKE_DURA,
.wake_duration_unit = CONFIG_EXAMPLE_ITWT_WAKE_DURATION_UNIT,
.wake_invl_expn = CONFIG_EXAMPLE_ITWT_WAKE_INVL_EXPN,
.wake_invl_mant = CONFIG_EXAMPLE_ITWT_WAKE_INVL_MANT,
.trigger = trigger_enabled,
.timeout_time_ms = CONFIG_EXAMPLE_ITWT_SETUP_TIMEOUT_TIME_MS,
};
err = esp_wifi_sta_itwt_setup(&setup_config);
if (err != ESP_OK) {
ESP_LOGE(TAG, "itwt setup failed, err:0x%x", err);
ESP_LOGE(TAG, "Error: %s", esp_err_to_name(err));
}
} else {
ESP_LOGE(TAG, "Must be in 11ax mode to support itwt");
}
}
static void start_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "sta connect to %s", DEFAULT_SSID);
esp_wifi_connect();
}
static void disconnect_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ESP_LOGI(TAG, "sta disconnect, reconnect...");
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
esp_wifi_connect();
}
static void itwt_setup_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
wifi_event_sta_itwt_setup_t *setup = (wifi_event_sta_itwt_setup_t *) event_data;
if (setup->status == ITWT_SETUP_SUCCESS) {
/* TWT Wake Interval = TWT Wake Interval Mantissa * (2 ^ TWT Wake Interval Exponent) */
ESP_LOGI(TAG, "<WIFI_EVENT_ITWT_SETUP>twt_id:%d, flow_id:%d, %s, %s, wake_dura:%d, wake_dura_unit:%d, wake_invl_e:%d, wake_invl_m:%d", setup->config.twt_id,
setup->config.flow_id, setup->config.trigger ? "trigger-enabled" : "non-trigger-enabled", setup->config.flow_type ? "unannounced" : "announced",
setup->config.min_wake_dura, setup->config.wake_duration_unit, setup->config.wake_invl_expn, setup->config.wake_invl_mant);
ESP_LOGI(TAG, "<WIFI_EVENT_ITWT_SETUP>target wake time:%lld, wake duration:%d us, service period:%d us", setup->target_wake_time, setup->config.min_wake_dura << (setup->config.wake_duration_unit == 1 ? 10 : 8),
setup->config.wake_invl_mant << setup->config.wake_invl_expn);
} else {
if (setup->status == ESP_ERR_WIFI_TWT_SETUP_TIMEOUT) {
ESP_LOGE(TAG, "<WIFI_EVENT_ITWT_SETUP>twt_id:%d, timeout of receiving twt setup response frame", setup->config.twt_id);
} else if (setup->status == ESP_ERR_WIFI_TWT_SETUP_TXFAIL) {
ESP_LOGE(TAG, "<WIFI_EVENT_ITWT_SETUP>twt_id:%d, twt setup frame tx failed, reason: %d", setup->config.twt_id, setup->reason);
} else if (setup->status == ESP_ERR_WIFI_TWT_SETUP_REJECT) {
ESP_LOGE(TAG, "<WIFI_EVENT_ITWT_SETUP>twt_id:%d, twt setup request was rejected, setup cmd: %d", setup->config.twt_id, setup->config.setup_cmd);
} else {
ESP_LOGE(TAG, "<WIFI_EVENT_ITWT_SETUP>twt_id:%d, twt setup failed, status: %d", setup->config.twt_id, setup->status);
}
}
}
static void itwt_teardown_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
wifi_event_sta_itwt_teardown_t *teardown = (wifi_event_sta_itwt_teardown_t *) event_data;
if (teardown->status == ITWT_TEARDOWN_FAIL) {
ESP_LOGE(TAG, "<WIFI_EVENT_ITWT_TEARDOWN>flow_id %d%s, twt teardown frame tx failed", teardown->flow_id, (teardown->flow_id == 8) ? "(all twt)" : "");
} else {
ESP_LOGI(TAG, "<WIFI_EVENT_ITWT_TEARDOWN>flow_id %d%s", teardown->flow_id, (teardown->flow_id == 8) ? "(all twt)" : "");
}
}
static void itwt_suspend_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
wifi_event_sta_itwt_suspend_t *suspend = (wifi_event_sta_itwt_suspend_t *) event_data;
ESP_LOGI(TAG, "<WIFI_EVENT_ITWT_SUSPEND>status:%d, flow_id_bitmap:0x%x, actual_suspend_time_ms:[%lu %lu %lu %lu %lu %lu %lu %lu]",
suspend->status, suspend->flow_id_bitmap,
suspend->actual_suspend_time_ms[0], suspend->actual_suspend_time_ms[1], suspend->actual_suspend_time_ms[2], suspend->actual_suspend_time_ms[3],
suspend->actual_suspend_time_ms[4], suspend->actual_suspend_time_ms[5], suspend->actual_suspend_time_ms[6], suspend->actual_suspend_time_ms[7]);
}
static void itwt_probe_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
wifi_event_sta_itwt_probe_t *probe = (wifi_event_sta_itwt_probe_t *) event_data;
ESP_LOGI(TAG, "<WIFI_EVENT_ITWT_PROBE>status:%s, reason:0x%x", itwt_probe_status_to_str(probe->status), probe->reason);
}
static void wifi_itwt(void)
{
ESP_ERROR_CHECK(esp_netif_init());
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_create_default());
netif_sta = esp_netif_create_default_wifi_sta();
assert(netif_sta);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
WIFI_EVENT_STA_START,
&start_handler,
NULL,
NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
WIFI_EVENT_STA_DISCONNECTED,
&disconnect_handler,
NULL,
NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&got_ip_handler,
NULL,
NULL));
/* itwt */
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
WIFI_EVENT_ITWT_SETUP,
&itwt_setup_handler,
NULL,
NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
WIFI_EVENT_ITWT_TEARDOWN,
&itwt_teardown_handler,
NULL,
NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
WIFI_EVENT_ITWT_SUSPEND,
&itwt_suspend_handler,
NULL,
NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
WIFI_EVENT_ITWT_PROBE,
&itwt_probe_handler,
NULL,
NULL));
wifi_config_t wifi_config = {
.sta = {
.ssid = DEFAULT_SSID,
.password = DEFAULT_PWD,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 3, 0)
wifi_twt_config_t wifi_twt_config = {
.post_wakeup_event = false,
#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 3, 1)
// twt_enable_keep_alive only found in IDF v5.3.2 and above
.twt_enable_keep_alive = keep_alive_enabled,
#endif
};
// esp_wifi_sta_twt_config only found in IDF v5.3.1 and above
ESP_ERROR_CHECK(esp_wifi_sta_twt_config(&wifi_twt_config));
#endif
#if CONFIG_SOC_WIFI_SUPPORT_5G || CONFIG_SLAVE_SOC_WIFI_SUPPORT_5G
wifi_bandwidths_t bw = {
.ghz_2g = WIFI_BW20,
.ghz_5g = WIFI_BW20,
};
esp_wifi_set_bandwidths(WIFI_IF_STA, &bw);
wifi_protocols_t protocol = {
.ghz_2g = WIFI_PROTOCOL_11AX,
.ghz_5g = WIFI_PROTOCOL_11AX,
};
esp_wifi_set_protocols(WIFI_IF_STA, &protocol);
#else
esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW20);
esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX);
#endif
esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
#if CONFIG_EXAMPLE_ENABLE_STATIC_IP
example_set_static_ip(netif_sta);
#endif
ESP_ERROR_CHECK(esp_wifi_start());
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_MU_STATS
esp_wifi_enable_rx_statistics(true, true);
#else
esp_wifi_enable_rx_statistics(true, false);
#endif
#endif
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
esp_wifi_enable_tx_statistics(ESP_WIFI_ACI_VO, true); //VO, mgmt
esp_wifi_enable_tx_statistics(ESP_WIFI_ACI_BE, true); //BE, data
#endif
}
void app_main(void)
{
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
#if CONFIG_PM_ENABLE
// Configure dynamic frequency scaling:
// maximum and minimum frequencies are set in sdkconfig,
// automatic light sleep is enabled if tickless idle support is enabled.
esp_pm_config_t pm_config = {
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
// SDIO currently does not work with auto light sleep
//.light_sleep_enable = true
.light_sleep_enable = false
#endif
};
ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );
ESP_ERROR_CHECK( ret );
#else
printf("\n =================================================\n");
printf(" | Test WiFi 6 itwt |\n");
printf(" =================================================\n\n");
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
repl_config.prompt = "itwt>";
// init console REPL environment
#if CONFIG_ESP_CONSOLE_UART
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
#elif CONFIG_ESP_CONSOLE_USB_CDC
esp_console_dev_usb_cdc_config_t cdc_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&cdc_config, &repl_config, &repl));
#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
esp_console_dev_usb_serial_jtag_config_t usbjtag_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&usbjtag_config, &repl_config, &repl));
#endif
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(repl));
#endif
//start wifi
wifi_itwt();
// register commands
register_system();
register_wifi_itwt();
register_wifi_stats();
}
@@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void register_wifi_itwt(void);
void register_wifi_stats(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,259 @@
/*
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#if CONFIG_SOC_WIFI_HE_SUPPORT || CONFIG_SLAVE_SOC_WIFI_HE_SUPPORT
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_wifi_he.h"
/*******************************************************
* Constants
*******************************************************/
static const char *TAG = "twt";
/*******************************************************
* Structures
*******************************************************/
typedef struct {
struct arg_int *setup;
struct arg_lit *teardown;
struct arg_lit *suspend;
struct arg_lit *getflowid;
struct arg_int *waketimeoffset;
struct arg_int *trigger; //1-trigger-enabled, 0-non-trigger-enabled, setup
struct arg_int *flowtype; //1-unannounced, 0-announced, setup
struct arg_int *negtype;
struct arg_int *wakeinvlexp; //setup
struct arg_int *wakeduraunit; //1-TU, 0-256us
struct arg_int *wakeinvlman; //setup
struct arg_int *minwakedur; //setup
struct arg_int *flowid;
struct arg_int *twtid;
struct arg_int *setup_timeout_time_ms;
struct arg_int *suspend_time_ms;
struct arg_int *all_twt;
struct arg_end *end;
} wifi_itwt_args_t;
typedef struct {
struct arg_int *timeout;
struct arg_end *end;
} wifi_itwt_send_probereq_t;
/*******************************************************
* Variable Definitions
*******************************************************/
static wifi_itwt_args_t itwt_args;
static wifi_itwt_send_probereq_t itwt_probe_args;
/*******************************************************
* Function Declarations
*******************************************************/
/*******************************************************
* Function Definitions
*******************************************************/
static int wifi_cmd_itwt(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &itwt_args);
if (nerrors != 0) {
arg_print_errors(stderr, itwt_args.end, argv[0]);
return 1;
}
esp_err_t err = ESP_OK;
if (itwt_args.setup->count) {
if (itwt_args.wakeinvlman->count) {
if (itwt_args.wakeinvlman->ival[0] < 0 || itwt_args.wakeinvlman->ival[0] > 65535) {
ESP_LOGE(TAG, "(itwt)expect [0, 65535], wake_invl_mant: %d", itwt_args.wakeinvlman->ival[0]);
return 1;
}
}
if (itwt_args.wakeinvlexp->count) {
if (itwt_args.wakeinvlexp->ival[0] < 0 || itwt_args.wakeinvlexp->ival[0] > 31) {
ESP_LOGE(TAG, "(itwt)expect [0, 31], wake_invl_expn: %d", itwt_args.wakeinvlexp->ival[0]);
return 1;
}
}
if (itwt_args.minwakedur->count) {
if (itwt_args.minwakedur->ival[0] < 0 || itwt_args.minwakedur->ival[0] > 255) {
ESP_LOGE(TAG, "(itwt)expect [0, 255], min_wake_dura: %d", itwt_args.minwakedur->ival[0]);
return 1;
}
}
if (itwt_args.wakeduraunit->count) {
if (itwt_args.wakeduraunit->ival[0] < 0 || itwt_args.wakeduraunit->ival[0] > 1) {
ESP_LOGE(TAG, "(itwt)expect [0, 1], wake duration unit: %d", itwt_args.wakeduraunit->ival[0]);
return 1;
}
}
if (itwt_args.twtid->count) {
if (itwt_args.twtid->ival[0] < 0 || itwt_args.twtid->ival[0] > 32767) {
ESP_LOGE(TAG, "(itwt)expect [0, 32767], twt id: %d", itwt_args.twtid->ival[0]);
return 1;
}
}
if (itwt_args.setup_timeout_time_ms->count) {
if (itwt_args.setup_timeout_time_ms->ival[0] < 0 || itwt_args.setup_timeout_time_ms->ival[0] > 65535) {
ESP_LOGE(TAG, "(itwt)expect [0, 65535], setup timeout time: %d", itwt_args.setup_timeout_time_ms->ival[0]);
return 1;
}
}
wifi_twt_setup_config_t setup_config = {
.setup_cmd = (itwt_args.setup->ival[0] <= TWT_DEMAND) ? itwt_args.setup->ival[0] : TWT_REQUEST,
.flow_id = itwt_args.flowid->count ? itwt_args.flowid->ival[0] : 0,
.twt_id = itwt_args.twtid->count ? itwt_args.twtid->ival[0] : 0,
.flow_type = itwt_args.flowtype->count ? ((itwt_args.flowtype->ival[0] == 0) ? 0 : 1) : 0,
.min_wake_dura = itwt_args.minwakedur->count ? itwt_args.minwakedur->ival[0] : 255,
.wake_duration_unit = itwt_args.wakeduraunit->count ? itwt_args.wakeduraunit->ival[0] : 0,
.wake_invl_expn = itwt_args.wakeinvlexp->count ? itwt_args.wakeinvlexp->ival[0] : 10,
.wake_invl_mant = itwt_args.wakeinvlman->count ? itwt_args.wakeinvlman->ival[0] : 512,
.trigger = itwt_args.trigger->count ? (itwt_args.trigger->ival[0] ? 1 : 0) : 1,
.timeout_time_ms = itwt_args.setup_timeout_time_ms->count ? itwt_args.setup_timeout_time_ms->ival[0] : 5000,
};
err = esp_wifi_sta_itwt_setup(&setup_config);
ESP_LOGI(TAG, "(itwt)setup, trigger:%d, %s, flow_id:%d, err:0x%x",
setup_config.trigger, setup_config.flow_type ? "unannounce" : "announced", setup_config.flow_id, err);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error: %s", esp_err_to_name(err));
}
}
if (itwt_args.teardown->count) {
// teardown a given flow id, all_twt has a high priority
int flow_id = itwt_args.flowid->count ? itwt_args.flowid->ival[0] : (-1);
bool all_twt = itwt_args.all_twt->count ? ((itwt_args.all_twt->ival[0] == 1) ? true : false) : false;
flow_id = (all_twt == true) ? FLOW_ID_ALL : flow_id;
ESP_LOGW(TAG, "flow_id %d", flow_id);
if (flow_id >= 0) {
err = esp_wifi_sta_itwt_teardown(flow_id);
ESP_LOGI(TAG, "(itwt)teardown, flow_id:%d, all_twt:%d, err:0x%x", flow_id, all_twt, err);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error: %s", esp_err_to_name(err));
}
} else {
ESP_LOGE(TAG, "(itwt)teardown, should specify an existing flow id");
}
}
if (itwt_args.suspend->count) {
// suspend a given flow id
int flow_id = itwt_args.flowid->count ? itwt_args.flowid->ival[0] : (-1);
bool all_twt = itwt_args.all_twt->count ? (itwt_args.all_twt->ival[0] ? true : false) : false;
flow_id = (all_twt == true) ? FLOW_ID_ALL : flow_id;
int suspend_time_ms = itwt_args.suspend_time_ms->count ? itwt_args.suspend_time_ms->ival[0] : 0;
if (flow_id >= 0) {
err = esp_wifi_sta_itwt_suspend(flow_id, suspend_time_ms);
ESP_LOGI(TAG, "(itwt)suspend, flow_id:%d, all_twt:%d, suspend:%d ms, err:0x%x", flow_id, all_twt, suspend_time_ms, err);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error: %s", esp_err_to_name(err));
}
} else {
ESP_LOGE(TAG, "(itwt)suspend, should specify an existing flow id");
}
}
if (itwt_args.getflowid->count) {
int flow_id_bitmap;
err = esp_wifi_sta_itwt_get_flow_id_status(&flow_id_bitmap);
if (err == ESP_OK) {
ESP_LOGI(TAG, "flowid bitmap: 0x%" PRIx16, flow_id_bitmap);
} else {
ESP_LOGE(TAG, "Error: %s", esp_err_to_name(err));
}
}
if (itwt_args.waketimeoffset->count) {
int offset_us = itwt_args.waketimeoffset->ival[0];
err = esp_wifi_sta_itwt_set_target_wake_time_offset(offset_us);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error: %s", esp_err_to_name(err));
}
}
return 0;
}
static int wifi_cmd_itwt_probe(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &itwt_probe_args);
if (nerrors != 0) {
arg_print_errors(stderr, itwt_probe_args.end, argv[0]);
return 1;
}
esp_err_t err = ESP_OK;
if (itwt_probe_args.timeout->count) {
if (itwt_probe_args.timeout->ival[0] > 0) {
ESP_LOGI(TAG, "(itwt)send probe req, timeout:%d ms", itwt_probe_args.timeout->ival[0]);
err = esp_wifi_sta_itwt_send_probe_req(itwt_probe_args.timeout->ival[0]);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error: %s", esp_err_to_name(err));
}
} else {
ESP_LOGE(TAG, "(itwt)invalid input, timeout:%d ms", itwt_probe_args.timeout->ival[0]);
}
}
ESP_LOGI(TAG, "err:0x%x", err);
return err;
}
void register_wifi_itwt(void)
{
/* itwt setup/teardown */
itwt_args.setup = arg_int0(NULL, "setup", "<setup>", "twt setup/teardown an individual flow id");
itwt_args.teardown = arg_lit0(NULL, "teardown", "twt setup/teardown an individual flow id");
itwt_args.suspend = arg_lit0(NULL, "suspend", "twt setup/teardown an individual flow id");
itwt_args.getflowid = arg_lit0(NULL, "getflowid", "get flow ids");
itwt_args.waketimeoffset = arg_int0(NULL, "waketimeoffset", "<offset us>", "set wake time offset (in us)");
itwt_args.trigger = arg_int0("t", NULL, "<trigger>", "trigger");
itwt_args.flowtype = arg_int0("f", NULL, "<flow_type>", "flow type: 0-announced, 1-unannounced");
itwt_args.negtype = arg_int0("n", NULL, "<neg_type>", "negotiate type");
itwt_args.minwakedur = arg_int0("d", NULL, "<minwakedur>", "Norminal Min. Wake Duration");
itwt_args.wakeduraunit = arg_int0("u", NULL, "<wakeduraunit>", "wake duration unit 0-256us, 1-TU (TU = 1024us)");
itwt_args.wakeinvlexp = arg_int0("e", NULL, "<wakeinvlexp>", "Wake Interval Exponent");
itwt_args.wakeinvlman = arg_int0("m", NULL, "<wakeinvlman>", "Wake Interval Mantissa");
itwt_args.flowid = arg_int0("i", NULL, "<flow_id>", "Flow ID");
itwt_args.suspend_time_ms = arg_int0("s", NULL, "<suspend_time_ms>", "time of suspending iTWT agreements, unit ms");
itwt_args.twtid = arg_int0("w", NULL, "<twt_id>", "TWT ID");
itwt_args.setup_timeout_time_ms = arg_int0("u", NULL, "<setup_timeout_time_ms>", "iTWT setup timeout time, unit ms");
itwt_args.all_twt = arg_int0("a", NULL, "<all_twt>", "All TWT");
itwt_args.end = arg_end(1);
const esp_console_cmd_t itwt_cmd = {
.command = "itwt",
.help = "itwt setup, teardown or suspend",
.hint = NULL,
.func = &wifi_cmd_itwt,
.argtable = &itwt_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&itwt_cmd));
/* itwt probe */
itwt_probe_args.timeout = arg_int0("t", NULL, "[timeout]", "time of sending a probe request frame and receiving a probe response frame from ap, unit ms");
itwt_probe_args.end = arg_end(1);
const esp_console_cmd_t itwt_probe_cmd = {
.command = "probe",
.help = "send probe request for TSF update when at lease one itwt agreement setup",
.hint = NULL,
.func = &wifi_cmd_itwt_probe,
.argtable = &itwt_probe_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&itwt_probe_cmd));
}
#else
void register_wifi_itwt(void)
{
;
}
#endif /* CONFIG_SOC_WIFI_HE_SUPPORT || CONFIG_SLAVE_SOC_WIFI_HE_SUPPORT */
@@ -0,0 +1,584 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS || CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_wifi_types.h"
#include "esp_private/esp_wifi_he_private.h"
/*******************************************************
* Macros
*******************************************************/
/*
* enable/disable rx/tx statistics after Wi-Fi started:
* (1) esp_wifi_enable_rx_statistics(true, true); //rx_stats=true, rx_mu_stats=true
* (2) esp_wifi_enable_tx_statistics(ESP_WIFI_ACI_BE, true); //aci=ESP_WIFI_ACI_BE, tx_stats=true
*/
/*******************************************************
* Constants
*******************************************************/
static const char *TAG = "stats";
/*******************************************************
* Structures
*******************************************************/
/*******************************************************
* Variable Definitions
*******************************************************/
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_MU_STATS
esp_test_rx_mu_statistics_t rx_mu_stats = { 0, }; //10932 bytes
#endif
/*******************************************************
* Function Declarations
*******************************************************/
/*******************************************************
* Function Definitions
*******************************************************/
const char *tx_fail_error2str(esp_test_tx_fail_error_t error)
{
switch (error) {
case TEST_TX_FAIL_ERROR_H00:
return "0x00";
case TEST_TX_FAIL_ERROR_H53:
return "0x53";
case TEST_TX_FAIL_ERROR_H63:
return "0x63";
case TEST_TX_FAIL_ERROR_H75:
return "0x75";
case TEST_TX_FAIL_ERROR_H41:
return "0x41";
case TEST_TX_FAIL_ERROR_H42:
return "0x42";
case TEST_TX_FAIL_ERROR_H47:
return "0x47";
case TEST_TX_FAIL_ERROR_H80:
return "0x80";
case TEST_TX_FAIL_ERROR_H5A:
return "0x5A";
case TEST_TX_FAIL_ERROR_HXX:
return "Others";
case TEST_TX_FAIL_ERROR_MAX:
return "Undefined";
}
return "Undefined";
}
const char *tx_fail_match2str(esp_test_tx_fail_match_t match)
{
switch (match) {
case TEST_TX_WAIT_MATCH:
return "MATCH";
case TEST_TX_WAIT_NOT2SELF:
return "NOT2SELF";
case TEST_TX_MISMATCH:
return "MISMATCH";
case TEST_TX_WAIT_TIMEOUT:
return "TIMEOUT";
case TEST_TX_WAIT_MAX:
return "Undefined";
}
return "Undefined";
}
const char *tx_fail_state2str(esp_test_tx_fail_state_t state)
{
switch (state) {
case TEST_TX_SUCCESS:
return "TX Success";
case TEST_TX_FAIL_RTS:
return "TX RTS";
case TEST_TX_WAIT_CTS: //RX
return "Wait CTS";
case TEST_TX_FAIL_CTS:
return "TX RTS";
case TEST_TX_FAIL_DATA:
return "TX DATA";
case TEST_TX_WAIT_ACK: //RX
return "Wait ACK/BA";
case TEST_TX_FAIL_MAX:
return "Undefined";
}
return "Undefined";
}
int wifi_cmd_clr_tx_statistics(int argc, char **argv)
{
ESP_LOGW(TAG, "Clear tx statistics");
int i;
for (i = 0; i < 3; i++) {
esp_wifi_clr_tx_statistics(i); //BE
esp_wifi_clr_tx_tb_statistics(i);
}
esp_test_clr_hw_statistics();
return 0;
}
void print_hw_tb_statistics(void)
{
esp_test_hw_tb_statistics_t hw_tb_stats = { 0, };
esp_test_get_hw_tb_statistics(&hw_tb_stats);
printf("(test)rx_trig:%d, tx_bfrpt:%d, tb_times:%d, tb_qos_null:%d, tb_qos_data:%d, tb_cca_cancel:%d, tb_sifs_abort:%d, tb_pwr_outof_range:%d\n",
hw_tb_stats.rx_trig,
hw_tb_stats.tx_bfrpt, //including TB and Non-TB
hw_tb_stats.tb_times,
hw_tb_stats.tb_qos_null,
hw_tb_stats.tb_times - hw_tb_stats.tb_qos_null,
hw_tb_stats.tb_cca_cancel,
hw_tb_stats.tb_sifs_abort,
hw_tb_stats.tb_pwr_outof_range);
}
int wifi_cmd_get_tx_statistics(int argc, char **argv)
{
uint8_t i, h, j, k;
ESP_LOGW(TAG, "Get tx statistics");
esp_test_tx_tb_statistics_t tb_stats = { 0, }; //32 bytes
esp_test_tx_statistics_t tx_stats = { 0, }; //136 bytes
esp_test_tx_fail_statistics_t tx_fail[TEST_TX_FAIL_MAX] = { 0, }; //TEST_TX_FAIL_MAX * 164 bytes
print_hw_tb_statistics();
//only check BE
for (i = 2; i < 3; i++) {
esp_wifi_get_tx_tb_statistics(i, &tb_stats);
/* TB */
printf("(test)aci:%" PRIu8 ", tb(suc:%" PRIu32 ", ack:%" PRIu32 ", err:%" PRIu32 "), "
"count(suc:%" PRIu32 ", ack:%" PRIu32 ", err:%" PRIu32 ", tot:%" PRIu32 ", max_sent:%" PRIu32 ")\n",
i,
tb_stats.complete_suc_tb,
tb_stats.complete_ack_tb,
tb_stats.complete_err_tb,
tb_stats.complete_tb_suc_count,
tb_stats.complete_tb_ack_count,
tb_stats.complete_tb_err_count,
tb_stats.complete_tb_tot_count,
tb_stats.complete_tb_pack_sent);
esp_wifi_get_tx_statistics(i, &tx_stats, (esp_test_tx_fail_statistics_t *) &tx_fail);
int tot_tx_times = tx_stats.tb_times + (tx_stats.tx_enable - tx_stats.tb_last); //TB + EDCA
int tot_fail = tx_fail[1].count + tx_fail[2].count + tx_fail[3].count + tx_fail[4].count + tx_fail[5].count;
printf("(test)aci:%" PRIu8 ", enable:%" PRIu32 ", complete:%" PRIu32 ", tb_times:%" PRIu32 ", tb_last:%" PRIu32 ", edca:%" PRIu32 ", "
"succ:%" PRIu32 ", fail(%" PRIu32 ",%" PRIu32 ",%" PRIu32 ", cts:%" PRIu32 "/%2.2f%%, ack:%" PRIu32 "/%2.2f%%, tot:%d, %.2f%%), "
"edca(ack:%" PRIu32 ", ba:%" PRIu32 "), tb(hw-ba:%" PRIu32 ", sw-ba:%" PRIu32 ")\n",
i, tx_stats.tx_enable,
tx_stats.tx_complete,
tx_stats.tb_times,
tx_stats.tb_last,
tx_stats.tx_enable - tx_stats.tb_last,
tx_fail[0].count,
tx_fail[1].count,
tx_fail[3].count,
tx_fail[4].count,
tx_fail[2].count,
(float) ((float) tx_fail[2].count / (float) tot_tx_times) * 100, //rx cts
tx_fail[5].count, (float) ((float) tx_fail[5].count / (float) tot_tx_times) * 100, //rx ack
tot_fail,
(float) ((float) tot_fail / (float) tot_tx_times) * 100,
tx_stats.rx_ack,
tx_stats.rx_ba,
tx_stats.tb_rx_ba, //including ACKs
tx_stats.rx_dump_ba);
printf("(test)aci:%" PRIu8 ", txFrames:%" PRIu32 ", s-mpdu:%" PRIu32 "(%.2f%%), "
"bitmap(max:%d, min:%d, tot:%" PRIu32 ", avg:%.2f), "
"retry(edca:%" PRIu32 ", tb:%" PRIu32 ", %.2f%%), collision:%" PRIu32 ", timeout:%" PRIu32 "\n",
i,
tx_stats.tx_succ,
tx_stats.rx_ack,
((float) (tx_stats.rx_ack) / (float) tot_tx_times) * 100,
tx_stats.rx_max_bitmap,
tx_stats.rx_min_bitmap,
tx_stats.rx_tot_bitmap,
(float) tx_stats.rx_tot_bitmap / (float) (tx_stats.tb_rx_ba + tx_stats.rx_ba),
tx_stats.retry_edca, tx_stats.retry_tb, (float) (tx_stats.retry_edca + tx_stats.retry_tb) / (float) tx_stats.tx_succ * 100,
tx_stats.collision, tx_stats.timeout);
float tot_rtt_ms = (float) tx_stats.tx_tot_rtt / (float) 1000;
printf("(test)aci:%" PRIu8 ", seqno_rtt[%" PRIu32 ",%" PRIu32 "], hw_rtt[%" PRIu32 ", %" PRIu32 "], muedca[enable:%" PRIu32 ", times:%" PRIu32 ", %.2f, %.2f, tot:%.2f], avg:%.3f ms, tot:%.3f secs\n",
i,
tx_stats.tx_seq_min_rtt,
tx_stats.tx_seq_max_rtt,
tx_stats.tx_min_rtt,
tx_stats.tx_max_rtt,
tx_stats.tx_muedca_enable,
tx_stats.muedca_times,
(float) tx_stats.tx_min_muedca_time / (float) 1000,
(float) tx_stats.tx_max_muedca_time / (float) 1000,
(float) tx_stats.tx_tot_muedca_time / (float) 1000, //ms
(float) tot_rtt_ms / (float) tot_tx_times, //ms
(float) tot_rtt_ms / (float) 1000); //seconds
/* fail state */
for (h = 1; h < TEST_TX_FAIL_MAX; h++) { //state
for (j = 0; j < TEST_TX_WAIT_MAX; j++) { //match
for (k = 0; k < TEST_TX_FAIL_ERROR_MAX; k++) { //error
if (tx_fail[h].match[j][k]) {
printf("(test)[%d][%d][%d](%16s + %16s + %16s)%3" PRIu32 "/%3" PRIu32 "(%.2f%%)\n", h, j, k, tx_fail_state2str(h),
tx_fail_match2str(j), tx_fail_error2str(k),
tx_fail[h].match[j][k], tx_fail[h].count,
((float) tx_fail[h].match[j][k] / (float) tx_fail[h].count) * 100);
}
}
}
}
printf("\n");
}
wifi_cmd_clr_tx_statistics(0, 0);
return 0;
}
void print_rx_statistics_nonmimo(const esp_test_rx_mu_statistics_t *mu_stats)
{
if (!mu_stats->nonmimo_rx) {
return;
}
int i, j;
int tot_rx_nonmimo = 0;
ESP_LOGW(TAG, "(nonmimo)dut rx:%" PRIu32, mu_stats->nonmimo_rx);
ESP_LOGW(TAG, "(nonmimo)ru_alloc_96_num_2046:%" PRIu32 ", ru_alloc_112_num_2046:%" PRIu32, mu_stats->ru_alloc_96_num_2046, mu_stats->ru_alloc_112_num_2046);
ESP_LOGW(TAG, "(nonmimo)sigb, mcs0:%" PRIu32 "(%2.2f%%), mcs1:%" PRIu32 "(%2.2f%%), mcs2:%" PRIu32 "(%2.2f%%), mcs3:%" PRIu32 "(%2.2f%%), mcs4:%" PRIu32 "(%2.2f%%), mcs5:%" PRIu32 "(%2.2f%%)",
mu_stats->nonmimo_sigb_mcs[0], ((float) mu_stats->nonmimo_sigb_mcs[0] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[1], ((float) mu_stats->nonmimo_sigb_mcs[1] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[2], ((float) mu_stats->nonmimo_sigb_mcs[2] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[3], ((float) mu_stats->nonmimo_sigb_mcs[3] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[4], ((float) mu_stats->nonmimo_sigb_mcs[4] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[5], ((float) mu_stats->nonmimo_sigb_mcs[5] / (float) mu_stats->nonmimo_rx) * 100);
ESP_LOGW(TAG, "(nonmimo)users, num1:%" PRIu32 "(%2.2f%%), num2:%" PRIu32 "(%2.2f%%), num3:%" PRIu32 "(%2.2f%%), num4:%" PRIu32 "(%2.2f%%), num5:%" PRIu32 "(%2.2f%%), num6:%" PRIu32 "(%2.2f%%), num7:%" PRIu32 "(%2.2f%%), num8:%" PRIu32 "(%2.2f%%), num9:%" PRIu32 "(%2.2f%%)",
mu_stats->nonmimo_user_num_occu[0], ((float) mu_stats->nonmimo_user_num_occu[0] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[1], ((float) mu_stats->nonmimo_user_num_occu[1] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[2], ((float) mu_stats->nonmimo_user_num_occu[2] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[3], ((float) mu_stats->nonmimo_user_num_occu[3] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[4], ((float) mu_stats->nonmimo_user_num_occu[4] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[5], ((float) mu_stats->nonmimo_user_num_occu[5] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[6], ((float) mu_stats->nonmimo_user_num_occu[6] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[7], ((float) mu_stats->nonmimo_user_num_occu[7] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[8], ((float) mu_stats->nonmimo_user_num_occu[8] / (float) mu_stats->nonmimo_rx) * 100);
for (i = 0; i < 256; i++) {
for (j = 0; j < 9; j++) {
if (!mu_stats->nonmimo_ru_alloc[i][j]) {
continue;
}
ESP_LOGI(TAG, "(nonmimo)ru_allocation:0x%2x(%3" PRIu8 "), position:%" PRIu8 ", %5" PRIu32 "(%2.2f%%)", i, i, j + 1, mu_stats->nonmimo_ru_alloc[i][j],
((float) mu_stats->nonmimo_ru_alloc[i][j] / (float) mu_stats->nonmimo_rx) * 100);
}
}
for (i = 0; i < ESP_TEST_RX_MU_USER_NUM; i++) {
if (!mu_stats->nonmimo[i].aid) {
continue;
}
if (mu_stats->aid != mu_stats->nonmimo[i].aid) {
continue;
}
tot_rx_nonmimo = mu_stats->nonmimo[i].occu_nsts[0] + mu_stats->nonmimo[i].occu_nsts[1] + mu_stats->nonmimo[i].occu_nsts[2] + mu_stats->nonmimo[i].occu_nsts[3];
printf("[%" PRIu8 "]%said:0x%x, txbf:%" PRIu32 ", dcm:%" PRIu32 "\n", i, (mu_stats->aid == mu_stats->nonmimo[i].aid) ? "#" : " ", mu_stats->nonmimo[i].aid,
mu_stats->nonmimo[i].txbf, mu_stats->nonmimo[i].dcm);
printf("[%d]%said:0x%x, "
"mcs0:%" PRIu32 "(%2.2f%%), mcs1:%" PRIu32 "(%2.2f%%), mcs2:%" PRIu32 "(%2.2f%%), mcs3:%" PRIu32 "(%2.2f%%), mcs4:%" PRIu32 "(%2.2f%%), "
"mcs5:%" PRIu32 "(%2.2f%%), mcs6:%" PRIu32 "(%2.2f%%), mcs7:%" PRIu32 "(%2.2f%%), mcs8:%" PRIu32 "(%2.2f%%), mcs9:%" PRIu32 "(%2.2f%%), "
"mcs10:%" PRIu32 "(%2.2f%%), mcs11:%" PRIu32 "(%2.2f%%)\n",
i, (mu_stats->aid == mu_stats->nonmimo[i].aid) ? "#" : " ", mu_stats->nonmimo[i].aid,
mu_stats->nonmimo[i].occu_mcs[0], ((float) mu_stats->nonmimo[i].occu_mcs[0] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[1], ((float) mu_stats->nonmimo[i].occu_mcs[1] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[2], ((float) mu_stats->nonmimo[i].occu_mcs[2] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[3], ((float) mu_stats->nonmimo[i].occu_mcs[3] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[4], ((float) mu_stats->nonmimo[i].occu_mcs[4] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[5], ((float) mu_stats->nonmimo[i].occu_mcs[5] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[6], ((float) mu_stats->nonmimo[i].occu_mcs[6] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[7], ((float) mu_stats->nonmimo[i].occu_mcs[7] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[8], ((float) mu_stats->nonmimo[i].occu_mcs[8] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[9], ((float) mu_stats->nonmimo[i].occu_mcs[9] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[10], ((float) mu_stats->nonmimo[i].occu_mcs[10] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[11], ((float) mu_stats->nonmimo[i].occu_mcs[11] / (float) tot_rx_nonmimo) * 100);
printf("[%" PRIu8 "]%said:0x%x, "
"nsts0:%" PRIu32 "(%2.2f%%), nsts1:%" PRIu32 "(%2.2f%%), nsts2:%" PRIu32 "(%2.2f%%), nsts3:%" PRIu32 "(%2.2f%%)\n",
i, (mu_stats->aid == mu_stats->nonmimo[i].aid) ? "#" : " ", mu_stats->nonmimo[i].aid,
mu_stats->nonmimo[i].occu_nsts[0], ((float) mu_stats->nonmimo[i].occu_nsts[0] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_nsts[1], ((float) mu_stats->nonmimo[i].occu_nsts[1] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_nsts[2], ((float) mu_stats->nonmimo[i].occu_nsts[2] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_nsts[3], ((float) mu_stats->nonmimo[i].occu_nsts[3] / (float) tot_rx_nonmimo) * 100);
printf("[%" PRIu8 "]%said:0x%x, "
"tot_rx_nonmimo:%8d, sta/dut:%2.2f%%\n",
i, (mu_stats->aid == mu_stats->nonmimo[i].aid) ? "#" : " ", mu_stats->nonmimo[i].aid,
tot_rx_nonmimo, ((float) tot_rx_nonmimo / (float) mu_stats->nonmimo_rx) * 100);
}
}
void print_rx_statistics_mimo(const esp_test_rx_mu_statistics_t *mu_stats)
{
if (!mu_stats->mimo_rx) {
return;
}
int i;
int tot_rx_mimo = 0;
ESP_LOGW(TAG, "(mimo)dut rx:%" PRIu32 "", mu_stats->mimo_rx);
ESP_LOGW(TAG, "(mimo)sigb, mcs0:%" PRIu32 "(%2.2f%%), mcs1:%" PRIu32 "(%2.2f%%), mcs2:%" PRIu32 "(%2.2f%%), mcs3:%" PRIu32 "(%2.2f%%), mcs4:%" PRIu32 "(%2.2f%%), mcs5:%" PRIu32 "(%2.2f%%)",
mu_stats->mimo_sigb_mcs[0], ((float) mu_stats->mimo_sigb_mcs[0] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[1], ((float) mu_stats->mimo_sigb_mcs[1] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[2], ((float) mu_stats->mimo_sigb_mcs[2] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[3], ((float) mu_stats->mimo_sigb_mcs[3] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[4], ((float) mu_stats->mimo_sigb_mcs[4] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[5], ((float) mu_stats->mimo_sigb_mcs[5] / (float) mu_stats->mimo_rx) * 100);
ESP_LOGW(TAG, "(mimo)users num2:%" PRIu32 "(%2.2f%%), num3:%" PRIu32 "(%2.2f%%), num4:%" PRIu32 "(%2.2f%%), num5:%" PRIu32 "(%2.2f%%), num6:%" PRIu32 "(%2.2f%%), num7:%" PRIu32 "(%2.2f%%), num8:%" PRIu32 "(%2.2f%%)",
mu_stats->mimo_user_num_occu[0], ((float) mu_stats->mimo_user_num_occu[0] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[1], ((float) mu_stats->mimo_user_num_occu[1] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[2], ((float) mu_stats->mimo_user_num_occu[2] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[3], ((float) mu_stats->mimo_user_num_occu[3] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[4], ((float) mu_stats->mimo_user_num_occu[4] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[5], ((float) mu_stats->mimo_user_num_occu[5] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[6], ((float) mu_stats->mimo_user_num_occu[6] / (float) mu_stats->mimo_rx) * 100);
for (i = 0; i < ESP_TEST_RX_MU_USER_NUM; i++) {
if (!mu_stats->mimo[i].aid) {
continue;
}
tot_rx_mimo = mu_stats->mimo[i].occu_ss[0] + mu_stats->mimo[i].occu_ss[1] + mu_stats->mimo[i].occu_ss[2] + mu_stats->mimo[i].occu_ss[3];
printf("[%" PRIu8 "]%said:0x%x, "
"mcs0:%" PRIu32 "(%2.2f%%), mcs1:%" PRIu32 "(%2.2f%%), mcs2:%" PRIu32 "(%2.2f%%), mcs3:%" PRIu32 "(%2.2f%%), mcs4:%" PRIu32 "(%2.2f%%), "
"mcs5:%" PRIu32 "(%2.2f%%), mcs6:%" PRIu32 "(%2.2f%%), mcs7:%" PRIu32 "(%2.2f%%), mcs8:%" PRIu32 "(%2.2f%%), mcs9:%" PRIu32 "(%2.2f%%), "
"mcs10:%" PRIu32 "(%2.2f%%), mcs11:%" PRIu32 "(%2.2f%%)\n",
i, (mu_stats->aid == mu_stats->mimo[i].aid) ? "#" : " ", mu_stats->mimo[i].aid,
mu_stats->mimo[i].occu_mcs[0], ((float) mu_stats->mimo[i].occu_mcs[0] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[1], ((float) mu_stats->mimo[i].occu_mcs[1] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[2], ((float) mu_stats->mimo[i].occu_mcs[2] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[3], ((float) mu_stats->mimo[i].occu_mcs[3] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[4], ((float) mu_stats->mimo[i].occu_mcs[4] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[5], ((float) mu_stats->mimo[i].occu_mcs[5] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[6], ((float) mu_stats->mimo[i].occu_mcs[6] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[7], ((float) mu_stats->mimo[i].occu_mcs[7] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[8], ((float) mu_stats->mimo[i].occu_mcs[8] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[9], ((float) mu_stats->mimo[i].occu_mcs[9] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[10], ((float) mu_stats->mimo[i].occu_mcs[10] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[11], ((float) mu_stats->mimo[i].occu_mcs[11] / (float) tot_rx_mimo) * 100);
printf("[%" PRIu8 "]%said:0x%x, "
"ss0:%" PRIu32 "(%2.2f%%), ss1:%" PRIu32 "(%2.2f%%), ss2:%" PRIu32 "(%2.2f%%), ss3:%" PRIu32 "(%2.2f%%)\n",
i, (mu_stats->aid == mu_stats->mimo[i].aid) ? "#" : " ", mu_stats->mimo[i].aid,
mu_stats->mimo[i].occu_ss[0], ((float) mu_stats->mimo[i].occu_ss[0] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_ss[1], ((float) mu_stats->mimo[i].occu_ss[1] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_ss[2], ((float) mu_stats->mimo[i].occu_ss[2] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_ss[3], ((float) mu_stats->mimo[i].occu_ss[3] / (float) tot_rx_mimo) * 100);
printf("[%" PRIu8 "]%said:0x%x, "
"tot_rx_mimo:%8d, sta/dut:%2.2f%%\n",
i, (mu_stats->aid == mu_stats->mimo[i].aid) ? "#" : " ", mu_stats->mimo[i].aid,
tot_rx_mimo, ((float) tot_rx_mimo / (float) mu_stats->mimo_rx) * 100);
}
}
void print_hw_rx_statistics(void)
{
esp_test_hw_rx_statistics_t hw_rx_stats = { 0, };
esp_test_get_hw_rx_statistics(&hw_rx_stats);
printf(
"WDEVRX_FCS_ERR :%d\n"
"WDEVRX_ABORT :%d\n"
"WDEVRX_ABORT_FCS_PASS :%d\n"
"NRX_ERR_PWRDROP :%d\n"
"NRX_HESIGB_ERR :%d\n"
"WDEVRX_SAMEBM_ERRCNT :%d\n"
"WDEVRX_MPDU :%d\n"
"WDEVRX_END_CNT :%d\n"
"WDEVRX_DATASUC :%d\n"
"WDEVRX_LASTUNMATCH_ERR :%d\n"
"RXHUNG_STATIS :%d\n"
"TXHUNG_STATIS :%d\n"
"RXTXHUNG :%" PRIu32 "\n"
"WDEVRX_CFO :%d\n"
"WDEVRX_SF :%d\n"
"WDEVRX_OTHER_UCAST :%d\n"
"WDEVRX_BUF_FULLCNT :%d\n"
"WDEVRX_FIFO_OVFCNT :%d\n"
"WDEVRX_TKIP_ERRCNT :%d\n"
"WDEVRX_BTBLOCK_ERR :%d\n"
"WDEVRX_FREQHOP_ERR :%d\n"
"WDEVRX_ACK_INT_CNT :%d\n"
"WDEVRX_RTS_INT_CNT :%d\n"
"BRX_ERR_AGC :%d\n"
"BRX_ERR :%d\n"
"NRX_ERR :%d\n"
"NRX_ERR_ABORT :%d\n"
"NRX_ERR_AGCEXIT :%d\n"
"NRX_ERR_BBOFF :%d\n"
"NRX_ERR_FDM_WDG :%d\n"
"NRX_ERR_RESTART :%d\n"
"NRX_ERR_SERV :%d\n"
"NRX_ERR_TXOVER :%d\n"
"NRX_HE_UNSUPPORT :%d\n"
"NRX_HTSIG_ERR :%d\n"
"NRX_HEUNSUPPORT :%d\n"
"NRX_HESIGA_CRC :%d\n",
hw_rx_stats.rx_fcs_err,
hw_rx_stats.rx_abort,
hw_rx_stats.rx_abort_fcs_pass,
hw_rx_stats.nrx_err_pwrdrop,
hw_rx_stats.nrx_hesigb_err,
hw_rx_stats.rx_samebm_errcnt,
hw_rx_stats.rx_mpdu,
hw_rx_stats.rx_end_cnt,
hw_rx_stats.rx_datasuc,
hw_rx_stats.rx_lastunmatch_err,
hw_rx_stats.rxhung_statis,
hw_rx_stats.txhung_statis,
hw_rx_stats.rxtxhung,
hw_rx_stats.rx_cfo_hz,
hw_rx_stats.rx_sf,
hw_rx_stats.rx_other_ucast,
hw_rx_stats.rx_buf_fullcnt,
hw_rx_stats.rx_fifo_ovfcnt,
hw_rx_stats.rx_tkip_errcnt,
hw_rx_stats.rx_btblock_err,
hw_rx_stats.rx_freqhop_err,
hw_rx_stats.rx_ack_int_cnt,
hw_rx_stats.rx_rts_int_cnt,
hw_rx_stats.brx_err_agc,
hw_rx_stats.brx_err,
hw_rx_stats.nrx_err,
hw_rx_stats.nrx_err_abort,
hw_rx_stats.nrx_err_agcexit,
hw_rx_stats.nrx_err_bboff,
hw_rx_stats.nrx_err_fdm_wdg,
hw_rx_stats.nrx_err_restart,
hw_rx_stats.nrx_err_serv,
hw_rx_stats.nrx_err_txover,
hw_rx_stats.nrx_err_unsupport,
hw_rx_stats.nrx_htsig_err,
hw_rx_stats.nrx_heunsupport,
hw_rx_stats.nrx_hesiga_crc
);
}
int wifi_cmd_clr_rx_statistics(int argc, char **argv)
{
ESP_LOGW(TAG, "Clear rx statistics");
esp_wifi_clr_rx_statistics(0);
esp_wifi_clr_rx_statistics(7);
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_MU_STATS
esp_test_clr_rx_error_occurs();
esp_wifi_clr_rx_mu_statistics();
#endif
esp_test_clr_hw_statistics();
return 0;
}
void print_rx_mu_statistics(void)
{
/* mu */
esp_wifi_get_rx_mu_statistics(&rx_mu_stats);
/* MIMO */
print_rx_statistics_mimo(&rx_mu_stats);
/* non-MIMO */
print_rx_statistics_nonmimo(&rx_mu_stats);
}
int wifi_cmd_get_rx_statistics(int argc, char **argv)
{
ESP_LOGW(TAG, "Get rx statistics");
esp_test_rx_statistics_t rx_stats = { 0, };
esp_test_rx_error_occurs_t rx_error_occurs = { 0, };
esp_wifi_get_rx_statistics(0, &rx_stats); //tid=0
print_hw_tb_statistics();
ESP_LOGW(TAG, "(0)legacy:%" PRIu32 ", ht(ht:%" PRIu32 ", ht_retry:%" PRIu32 "/%2.2f%%, ht_noeb:%" PRIu32 "/%2.2f%%)",
rx_stats.legacy,
rx_stats.ht, rx_stats.ht_retry,
rx_stats.ht_retry ? ((float) ((float) rx_stats.ht_retry / (float) rx_stats.ht) * 100) : 0,
rx_stats.ht_noeb, rx_stats.ht_noeb ? ((float) ((float) rx_stats.ht_noeb / (float) rx_stats.ht) * 100) : 0);
ESP_LOGW(TAG, "(0)su(su:%" PRIu32 ", su_txbf:%" PRIu32 ", su_stbc:%" PRIu32 ", su_retry:%" PRIu32 "/%2.2f%%, ersu:%" PRIu32 ", ersu_dcm:%" PRIu32 ", su_noeb:%" PRIu32 "/%2.2f%%)",
rx_stats.su,
rx_stats.su_txbf, rx_stats.su_stbc,
rx_stats.su_retry,
rx_stats.su_retry ? ((float) ((float) rx_stats.su_retry / (float) rx_stats.su) * 100) : 0,
rx_stats.ersu,
rx_stats.ersu_dcm,
rx_stats.su_noeb, rx_stats.su_noeb ? ((float) ((float) rx_stats.su_noeb / (float) rx_stats.su) * 100) : 0);
ESP_LOGW(TAG, "(0)mu(mu:%" PRIu32 ", mimo:%" PRIu32 ", non-mimo:%" PRIu32 ", txbf:%" PRIu32 ", stbc:%" PRIu32 ", mu_retry:%" PRIu32 "/%2.2f%%, mu_noeb:%" PRIu32 "/%2.2f%%)",
rx_stats.mu,
rx_stats.mu_mimo,
rx_stats.mu_ofdma, rx_stats.mu_txbf, rx_stats.mu_stbc,
rx_stats.mu_retry,
rx_stats.mu_retry ? ((float) ((float) rx_stats.mu_retry / (float) rx_stats.mu) * 100) : 0,
rx_stats.mu_noeb, rx_stats.mu_noeb ? ((float) ((float) rx_stats.mu_noeb / (float) rx_stats.mu) * 100) : 0);
memset(&rx_stats, 0, sizeof(rx_stats));
esp_wifi_get_rx_statistics(7, &rx_stats); //tid=7
ESP_LOGW(TAG, "(7)legacy:%" PRIu32 ", ht:%" PRIu32 ", su:%" PRIu32 ", su_txbf:%" PRIu32 ", ersu:%" PRIu32 ", mu:%" PRIu32, rx_stats.legacy,
rx_stats.ht, rx_stats.su, rx_stats.su_txbf, rx_stats.ersu, rx_stats.mu);
ESP_LOGW(TAG, "(hw)isr:%" PRIu32 ", nblks:%" PRIu32, rx_stats.rx_isr, rx_stats.rx_nblks);
/* hw rx statistics */
print_hw_rx_statistics();
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_MU_STATS
print_rx_mu_statistics();
#endif
esp_test_get_rx_error_occurs(&rx_error_occurs);
ESP_LOGW(TAG, "(rx)tot_errors:%" PRIu32, rx_error_occurs.tot);
int known_errors = 0; //rx error: 0x40-0xff
int i;
for (i = 0; i < 2; i++) {
if (rx_error_occurs.occurs[i]) {
known_errors += rx_error_occurs.occurs[i];
printf("[%3d] 0x%x, %8" PRIu32 ", %2.2f%%\n", i, (i ? 0xf5 : 0xc6), rx_error_occurs.occurs[i], ((float) rx_error_occurs.occurs[i] / (float) rx_error_occurs.tot) * 100);
}
}
if (rx_error_occurs.tot - known_errors) {
printf("[%3d]others, %8" PRIu32 ", %2.2f%%\n\n", i, rx_error_occurs.tot - known_errors, ((float) known_errors / (float) rx_error_occurs.tot) * 100);
}
wifi_cmd_clr_rx_statistics(0, 0);
return 0;
}
#endif /* CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS || CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS */
void register_wifi_stats(void)
{
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
/* get tx statistics */
const esp_console_cmd_t tx_stats_cmd = {
.command = "tx",
.help = "get tx statistics",
.hint = NULL,
.func = &wifi_cmd_get_tx_statistics,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&tx_stats_cmd));
/* clear tx statistics */
const esp_console_cmd_t clr_cmd = {
.command = "clrtx",
.help = "clear tx statistics",
.hint = NULL,
.func = &wifi_cmd_clr_tx_statistics,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&clr_cmd));
#endif
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
/* get rx statistics */
const esp_console_cmd_t rx_stats_cmd = {
.command = "rx",
.help = "get rx statistics",
.hint = NULL,
.func = &wifi_cmd_get_rx_statistics,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&rx_stats_cmd));
/* clear rx statistics */
const esp_console_cmd_t clr_rx_cmd = {
.command = "clrrx",
.help = "clear rx statistics",
.hint = NULL,
.func = &wifi_cmd_clr_rx_statistics,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&clr_rx_cmd));
#endif
}
@@ -0,0 +1,19 @@
# Use lower CPU frequency
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80=y
# Enable support for power management
CONFIG_PM_ENABLE=y
# Enable tickless idle mode
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
# Put related source code in IRAM
CONFIG_PM_SLP_IRAM_OPT=y
CONFIG_PM_RTOS_IDLE_OPT=y
# Disable all GPIO at light sleep
CONFIG_PM_SLP_DISABLE_GPIO=y
# Enable wifi sleep iram optimization
CONFIG_ESP_WIFI_SLP_IRAM_OPT=y
CONFIG_LWIP_ESP_GRATUITOUS_ARP=n
CONFIG_FREERTOS_HZ=1000
# CONFIG_LWIP_ESP_GRATUITOUS_ARP is not set
# CONFIG_ESP_GRATUITOUS_ARP is not set
@@ -0,0 +1 @@
CONFIG_EXAMPLE_MAX_CPU_FREQ_90=y