Fork fix: the SDIO wedge is FIXED (esp-hosted-mcu #167)
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:
@@ -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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user