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,4 @@
set(srcs "main.c" "station_example.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ".")
@@ -0,0 +1,110 @@
menu "Example Configuration"
config EXAMPLE_HEARTBEAT_INTERVAL_SEC
int "Heartbeat Interval in Seconds"
default 5
help
Set the co-processor heartbeat interval
config EXAMPLE_ENABLE_HEARTBEAT_MONITOR
bool "Enable heartbeat timeout monitor"
default y
help
Detect if there is a Heartbeat timeout
config EXAMPLE_HEARTBEAT_TIMEOUT_SEC
depends on EXAMPLE_ENABLE_HEARTBEAT_MONITOR
int "Heartbeat Timeout in Seconds"
default 6
help
Set the co-processor heartbeat timeout interval (minimum value should be the Heartbeat interval)
choice EXAMPLE_HOSTED_RECOVERY_METHOD
bool "Co-processor Recovery method"
default EXAMPLE_DO_HOSTED_RECOVERY
help
Select the recovery method to use
config EXAMPLE_DO_HOSTED_RECOVERY
bool "Restart ESP-Hosted transport"
help
Restart the ESP-Hosted transport to attempt recovery
config EXAMPLE_DO_HOST_RESET
bool "Restart the Host"
help
Restart the Host to recover the system
config EXAMPLE_DO_NOTHING
bool "Do nothing"
help
Don't do anything when error is received
endchoice
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
choice ESP_WIFI_SAE_MODE
prompt "WPA3 SAE mode selection"
default ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH
help
Select mode for SAE as Hunt and Peck, H2E or both.
config ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HUNT_AND_PECK
bool "HUNT AND PECK"
config ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT
bool "H2E"
depends on ESP_WIFI_ENABLE_SAE_H2E
config ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH
bool "BOTH"
depends on ESP_WIFI_ENABLE_SAE_H2E
endchoice
config ESP_WIFI_PW_ID
string "PASSWORD IDENTIFIER"
depends on ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT|| ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH
default ""
help
password identifier for SAE H2E
config ESP_MAXIMUM_RETRY
int "Maximum retry"
default 5
help
Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
choice ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD
prompt "WiFi Scan auth mode threshold"
default ESP_WIFI_AUTH_WPA2_PSK
help
The weakest authmode to accept in the scan mode.
This value defaults to ESP_WIFI_AUTH_WPA2_PSK in case password is present and ESP_WIFI_AUTH_OPEN is used.
Please select ESP_WIFI_AUTH_WEP/ESP_WIFI_AUTH_WPA_PSK in case AP is operating in WEP/WPA mode.
config ESP_WIFI_AUTH_OPEN
bool "OPEN"
config ESP_WIFI_AUTH_WEP
bool "WEP"
config ESP_WIFI_AUTH_WPA_PSK
bool "WPA PSK"
config ESP_WIFI_AUTH_WPA2_PSK
bool "WPA2 PSK"
config ESP_WIFI_AUTH_WPA_WPA2_PSK
bool "WPA/WPA2 PSK"
config ESP_WIFI_AUTH_WPA3_PSK
bool "WPA3 PSK"
config ESP_WIFI_AUTH_WPA2_WPA3_PSK
bool "WPA2/WPA3 PSK"
config ESP_WIFI_AUTH_WAPI_PSK
bool "WAPI PSK"
endchoice
endmenu
@@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __APP_COMMON_H__
#define __APP_COMMON_H__
void example_wifi_init_once(void);
void example_wifi_init_sta(void);
void example_wifi_deinit_sta(void);
void example_wifi_sta_netif_close(void);
#endif
@@ -0,0 +1,10 @@
dependencies:
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,332 @@
/*
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_log.h"
#include "esp_event.h"
#include "esp_hosted.h"
#include "esp_timer.h"
#include "esp_system.h"
#include "app_common.h"
#define HEARTBEAT_INTERVAL_SEC CONFIG_EXAMPLE_HEARTBEAT_INTERVAL_SEC
#define ENABLE_HEARTBEAT_MONITOR CONFIG_EXAMPLE_ENABLE_HEARTBEAT_MONITOR
#define DO_HOSTED_RECOVERY CONFIG_EXAMPLE_DO_HOSTED_RECOVERY
#define DO_HOST_RESET CONFIG_EXAMPLE_DO_HOST_RESET
#define DO_NOTHING CONFIG_EXAMPLE_DO_NOTHING
#if ENABLE_HEARTBEAT_MONITOR
#define HEARTBEAT_TIMEOUT_SEC CONFIG_EXAMPLE_HEARTBEAT_TIMEOUT_SEC
#define HEARTBEAT_TIMEOUT_NSEC (HEARTBEAT_TIMEOUT_SEC * 1000 * 1000)
static esp_timer_handle_t heartbeat_timer_handle = NULL;
#endif
#define ESP_HOSTED_RESET_BIT BIT0
static EventGroupHandle_t s_esp_hosted_event_group;
static bool resetting_esp_hosted_transport = false;
static bool first_init_event = false;
static bool first_heartbeat = false;
static uint32_t prev_heartbeat = 0;
static SemaphoreHandle_t sem_hosted_is_up;
static const char* TAG = "hosted_events";
#if ENABLE_HEARTBEAT_MONITOR
void my_timer_cb(void *arg)
{
ESP_LOGI(TAG, "*** TIMER: HEARTBEAT timeout ***");
if (!resetting_esp_hosted_transport) {
xEventGroupSetBits(s_esp_hosted_event_group, ESP_HOSTED_RESET_BIT);
}
// destroy the timer
esp_timer_delete(heartbeat_timer_handle);
heartbeat_timer_handle = NULL;
}
#endif
static void init_cp_error_detection(void)
{
first_init_event = false;
first_heartbeat = false;
prev_heartbeat = 0;
}
static void deinit_cp_error_detection(void)
{
// currently does nothing
}
/**
* This event handler handles ESP-Hosted events
*
* See station_example.c for the event handler for Wi-Fi and IP events
*/
static void esp_hosted_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == ESP_HOSTED_EVENT) {
if (event_id == ESP_HOSTED_EVENT_CP_INIT) {
ESP_LOGI(TAG, "*** got INIT event from co-processor ***");
esp_hosted_event_init_t *event = (esp_hosted_event_init_t *)event_data;
ESP_LOGI(TAG, "*** Co-processor Reset Reason %"PRIu16" ***", event->reason);
if (!first_init_event) {
// this is the first init event and is expected
first_init_event = true;
ESP_LOGI(TAG, "Expected INIT event");
} else if (!resetting_esp_hosted_transport) {
// unexpected init event
xEventGroupSetBits(s_esp_hosted_event_group, ESP_HOSTED_RESET_BIT);
ESP_LOGI(TAG, "*** Unexpected INIT event");
}
} else if (event_id == ESP_HOSTED_EVENT_TRANSPORT_UP) {
ESP_LOGI(TAG, "ESP-Hosted Transport is UP");
xSemaphoreGive(sem_hosted_is_up);
} else if (event_id == ESP_HOSTED_EVENT_TRANSPORT_DOWN) {
ESP_LOGI(TAG, "ESP-Hosted Transport is DOWN");
} else if (event_id == ESP_HOSTED_EVENT_TRANSPORT_FAILURE) {
if (!resetting_esp_hosted_transport) {
xEventGroupSetBits(s_esp_hosted_event_group, ESP_HOSTED_RESET_BIT);
ESP_LOGI(TAG, "*** Transport Failure ***");
}
} else if (event_id == ESP_HOSTED_EVENT_CP_HEARTBEAT) {
esp_hosted_event_heartbeat_t *event = (esp_hosted_event_heartbeat_t *)event_data;
ESP_LOGI(TAG, "*** Heartbeat %"PRIu32" ***", event->heartbeat);
uint32_t curr_heartbeat = event->heartbeat;
#if ENABLE_HEARTBEAT_MONITOR
if (heartbeat_timer_handle) {
if (esp_timer_is_active(heartbeat_timer_handle)) {
// restart the timer
if (ESP_OK != esp_timer_restart(heartbeat_timer_handle, HEARTBEAT_TIMEOUT_NSEC)) {
ESP_LOGE(TAG, "Failed to restart the timer");
}
} else {
// start the timer
if (ESP_OK != esp_timer_start_once(heartbeat_timer_handle, HEARTBEAT_TIMEOUT_NSEC)) {
ESP_LOGE(TAG, "Failed to start the timer");
}
}
}
#endif
if (!first_heartbeat) {
first_heartbeat = true;
} else if (curr_heartbeat != (prev_heartbeat + 1)) {
ESP_LOGW(TAG, "heartbeat: expected %"PRIu32", but got %"PRIu32, prev_heartbeat + 1, curr_heartbeat);
}
prev_heartbeat = curr_heartbeat;
} else {
ESP_LOGI(TAG, "Got UNKNOWN ESP_HOSTED event");
}
}
}
static void app_init_once(void)
{
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_event_handler_instance_t instance_got_hosted_event;
ESP_ERROR_CHECK(esp_event_handler_instance_register(ESP_HOSTED_EVENT,
ESP_EVENT_ANY_ID,
&esp_hosted_event_handler,
NULL,
&instance_got_hosted_event));
// set up event group, used to signal that ESP-Hosted transport has gone down
s_esp_hosted_event_group = xEventGroupCreate();
// create a binary semaphore for app to wait on until ESP-Hosted transport is up
sem_hosted_is_up = xSemaphoreCreateBinary();
assert(sem_hosted_is_up);
xSemaphoreGive(sem_hosted_is_up);
xSemaphoreTake(sem_hosted_is_up, portMAX_DELAY);
}
static void app_esp_hosted_init(void)
{
esp_hosted_init();
esp_hosted_connect_to_slave();
}
#if DO_HOSTED_RECOVERY
static void app_esp_hosted_deinit(void)
{
esp_hosted_deinit();
}
#endif
static bool app_esp_hosted_verify_up(void)
{
// to verify ESP-Hosted is UP, we get the co-processor fw info
ESP_LOGI(TAG, "getting fw version");
esp_hosted_coprocessor_fwver_t fwver;
if (ESP_OK == esp_hosted_get_coprocessor_fwversion(&fwver)) {
ESP_LOGI(TAG, "FW Version: %" PRIu32 ".%" PRIu32 ".%" PRIu32,
fwver.major1, fwver.minor1, fwver.patch1);
return true;
} else {
ESP_LOGE(TAG, "failed to get fw version");
return false;
}
}
#if ENABLE_HEARTBEAT_MONITOR
static esp_err_t app_setup_heartbeat_monitor(void)
{
esp_err_t res;
if (!heartbeat_timer_handle) {
// create heartbeat timer
esp_timer_create_args_t my_timer = {
.callback = my_timer_cb,
.arg = NULL,
.dispatch_method = ESP_TIMER_TASK,
.name = "Heartbeat Timer",
.skip_unhandled_events = true,
};
res = esp_timer_create(&my_timer, &heartbeat_timer_handle);
if (res == ESP_OK) {
// start the timer
res = esp_timer_start_once(heartbeat_timer_handle, HEARTBEAT_TIMEOUT_NSEC);
if (res == ESP_OK) {
ESP_LOGI(TAG, "heartbeat timer started");
} else {
ESP_LOGE(TAG, "failed to start heartbeat timer");
return res;
}
} else {
ESP_LOGE(TAG, "failed to create heartbeat timer");
heartbeat_timer_handle = NULL;
return res;
}
} else {
// heartbeat timer already created: restart it
if (esp_timer_is_active(heartbeat_timer_handle)) {
// restart the timer
res = esp_timer_restart(heartbeat_timer_handle, HEARTBEAT_TIMEOUT_NSEC);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to restart the timer");
return res;
}
} else {
// start the timer
res = esp_timer_start_once(heartbeat_timer_handle, HEARTBEAT_TIMEOUT_NSEC);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to start the timer");
return res;
}
}
}
return ESP_OK;
}
#endif
/**
* this function handles what to do when a co-processor error is encountered
*
* here, we either try to recover from the error, or reset the host
*/
static bool app_do_cp_recovery(void)
{
#if DO_HOSTED_RECOVERY
example_wifi_sta_netif_close(); // inform netif for sta that connection was lost
example_wifi_deinit_sta(); // deinit the station
app_esp_hosted_deinit();
return true;
#elif DO_HOST_RESET
ESP_LOGI(TAG, "********* Restarting host to avoid sync issues **********************");
vTaskDelay(2000 / portTICK_PERIOD_MS);
esp_restart();
return true;
#elif DO_NOTHING
ESP_LOGI(TAG, "Nothing done to recover");
return false;
#else
#error No co-processor recovery method selected
#endif
}
void app_main(void)
{
app_init_once(); // one time app init
example_wifi_init_once(); // one time wifi init
while (true) {
init_cp_error_detection();
ESP_LOGI(TAG, "init ESP-Hosted");
app_esp_hosted_init();
// wait until ESP-Hosted is up before continuing
xSemaphoreTake(sem_hosted_is_up, portMAX_DELAY);
ESP_LOGI(TAG, "ESP-Hosted is ready");
bool esp_hosted_is_okay = app_esp_hosted_verify_up();
if (esp_hosted_is_okay) {
// configure heartbeat
if (ESP_OK != esp_hosted_configure_heartbeat(true, HEARTBEAT_INTERVAL_SEC)) {
ESP_LOGE(TAG, "failed to set heartbeat");
}
#if ENABLE_HEARTBEAT_MONITOR
if (ESP_OK != app_setup_heartbeat_monitor()) {
ESP_LOGE(TAG, "failed to setup heartbeat monitor");
}
#endif
// connect to an AP
example_wifi_init_sta();
// here, you can start a thread to do data transfers with the AP
// we wait until we encounter an error and need to reset the transport
EventBits_t bits = xEventGroupWaitBits(s_esp_hosted_event_group,
ESP_HOSTED_RESET_BIT,
pdTRUE, // clear on exit
pdTRUE, // wait for all bits to be set
portMAX_DELAY);
if (bits & !ESP_HOSTED_RESET_BIT) {
ESP_LOGW(TAG, "*** Unexpected group event event bits ***");
return;
}
// here, you should tell the data transfer thread to abort data transfer
} else {
ESP_LOGE(TAG, "Failed to start up ESP-Hosted");
return;
}
/**
* should only come here if ESP-Hosted encountered a transport error
* or failed to connect
*/
// connection to ESP-Hosted has now failed
ESP_LOGI(TAG, "heartbeat timeout, transport failure or unexpected INIT event: reinit Hosted");
// restart the ESP-Hosted transport
resetting_esp_hosted_transport = true;
bool result = app_do_cp_recovery();
resetting_esp_hosted_transport = false;
deinit_cp_error_detection();
if (!result) {
// do cp recovery failed
ESP_LOGI(TAG, "Did not recover from co-processor failure. Exiting app");
break;
}
ESP_LOGI(TAG, "restarting ESP-Hosted");
}
}
@@ -0,0 +1,240 @@
/*
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* WiFi station 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.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "app_common.h"
/* The examples use WiFi configuration that you can set via project configuration menu
If you'd rather not, just change the below entries to strings with
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY
#if CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HUNT_AND_PECK
#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HUNT_AND_PECK
#define EXAMPLE_H2E_IDENTIFIER ""
#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT
#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HASH_TO_ELEMENT
#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID
#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH
#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_BOTH
#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID
#endif
#if CONFIG_ESP_WIFI_AUTH_OPEN
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN
#elif CONFIG_ESP_WIFI_AUTH_WEP
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP
#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK
#endif
/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static const char *TAG = "wifi station";
static int s_retry_num = 0;
static bool do_not_reconnect = false;
static esp_event_handler_instance_t instance_any_id = NULL;
static esp_event_handler_instance_t instance_got_ip = NULL;
static esp_netif_t *sta_netif = NULL;
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (!do_not_reconnect) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
ESP_LOGI(TAG,"connect to the AP fail");
}
} else {
ESP_LOGI(TAG,"do not reconnect");
}
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
/**
* these events will inform netif to close its current network connection
* so it is ready for the next sta connection
*/
void example_wifi_sta_netif_close(void)
{
// broadcast a wi-fi disconnect event so NETIF is aware that the connection went down
do_not_reconnect = true; // we don't try to reconnect
wifi_event_sta_disconnected_t event = { 0 };
event.reason = WIFI_REASON_CONNECTION_FAIL;
esp_event_post(WIFI_EVENT,
WIFI_EVENT_STA_DISCONNECTED,
&event,
sizeof(wifi_event_sta_disconnected_t),
portMAX_DELAY);
// sleep for a while to give NETIF time to react to the event
vTaskDelay(100 / portTICK_PERIOD_MS);
// this informs the event handler not to try to reconnect
do_not_reconnect = false;
// broadcast a wi-fi stop event so NETIF is aware the station has stopped
esp_event_post(WIFI_EVENT,
WIFI_EVENT_STA_STOP,
NULL,
0,
portMAX_DELAY);
// sleep for a while to give NETIF time to react to the event
vTaskDelay(100 / portTICK_PERIOD_MS);
}
void example_wifi_deinit_sta(void)
{
if (sta_netif) {
esp_netif_destroy_default_wifi(sta_netif);
sta_netif = NULL;
}
if (!instance_any_id) {
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT,
ESP_EVENT_ANY_ID,
instance_any_id));
instance_any_id = NULL;
}
if (!instance_got_ip) {
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT,
ESP_EVENT_ANY_ID,
instance_got_ip));
instance_got_ip = NULL;
}
vEventGroupDelete(s_wifi_event_group);
}
void example_wifi_init_once(void)
{
ESP_ERROR_CHECK(esp_netif_init());
}
void example_wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
if (!instance_any_id) {
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
}
if (!instance_got_ip) {
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
}
if (!sta_netif) {
sta_netif = esp_netif_create_default_wifi_sta();
}
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
wifi_config_t wifi_config = {
.sta = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.password = EXAMPLE_ESP_WIFI_PASS,
/* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (password len => 8).
* If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
* to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
* WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
*/
.threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
.sae_pwe_h2e = ESP_WIFI_SAE_MODE,
.sae_h2e_identifier = EXAMPLE_H2E_IDENTIFIER,
#ifdef CONFIG_ESP_WIFI_WPA3_COMPATIBLE_SUPPORT
.disable_wpa3_compatible_mode = 0,
#endif
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
* happened. */
if (bits & WIFI_CONNECTED_BIT) {
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else if (bits & WIFI_FAIL_BIT) {
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
} else {
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
}