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,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(gpio_expander_example)
|
||||
@@ -0,0 +1,38 @@
|
||||
# ESP-Hosted GPIO Control Example
|
||||
|
||||
This example demonstrates how to use the ESP-Hosted solution to control the GPIOs of the slave (co-processor) from the host MCU.
|
||||
|
||||
## How to Use
|
||||
|
||||
### Feature Switch
|
||||
Feature can be controlled at Host and Slave using `CONFIG_ESP_HOSTED_ENABLE_GPIO_EXPANDER` option.
|
||||
It is by default disabled at slave.
|
||||
|
||||
|
||||
**Slave Configuration:**
|
||||
* The firmware flashed on the slave board must have the GPIO RPC feature enabled. In the slave's `idf.py menuconfig`, set the following:
|
||||
```
|
||||
(Top) →
|
||||
Example Configuration --->
|
||||
[*] Enable GPIO Expander support (host can control slave GPIOs)
|
||||
```
|
||||
This corresponds to the `CONFIG_ESP_HOSTED_ENABLE_GPIO_EXPANDER=y` option.
|
||||
|
||||
|
||||
### Build and Flash
|
||||
|
||||
1. **Slave**
|
||||
* Slave build & flashed with above config
|
||||
|
||||
2. **Host:**
|
||||
* Set your target board (e.g., `idf.py set-target esp32p4`).
|
||||
* Build and flash the project: `idf.py -p <host_port> build flash monitor`.
|
||||
|
||||
The host application will:
|
||||
1. Initialize the ESP-Hosted transport.
|
||||
2. Configure a specific GPIO pin on the slave as an output.
|
||||
3. Toggle the GPIO level (HIGH and LOW) every second.
|
||||
4. After a few toggles, it will reconfigure the same GPIO as an input.
|
||||
5. Read and print the GPIO level.
|
||||
|
||||
You can connect an LED to the specified GPIO on the slave board to visually verify the output functionality.
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "host_gpio_expander_example.c"
|
||||
PRIV_REQUIRES nvs_flash
|
||||
INCLUDE_DIRS ".")
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_hosted.h"
|
||||
|
||||
static const char* TAG = "gpio_expander_example";
|
||||
|
||||
#define SLAVE_GPIO_PIN 2
|
||||
|
||||
static esp_err_t configure_and_test_gpio(esp_hosted_cp_gpio_config_t *config, const char *demo_name)
|
||||
{
|
||||
esp_err_t ret;
|
||||
int level;
|
||||
|
||||
ESP_LOGW(TAG, "---- %s ----", demo_name);
|
||||
|
||||
ret = esp_hosted_cp_gpio_config(config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GPIO config failed: 0x%x", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// For output modes, toggle the pin
|
||||
if (config->mode == H_CP_GPIO_MODE_OUTPUT ||
|
||||
config->mode == H_CP_GPIO_MODE_OUTPUT_OD) {
|
||||
|
||||
ESP_LOGI(TAG, "Setting GPIO %d LOW", SLAVE_GPIO_PIN);
|
||||
esp_hosted_cp_gpio_set_level(SLAVE_GPIO_PIN, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
|
||||
ESP_LOGI(TAG, "Setting GPIO %d HIGH", SLAVE_GPIO_PIN);
|
||||
esp_hosted_cp_gpio_set_level(SLAVE_GPIO_PIN, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
|
||||
ESP_LOGI(TAG, "GPIO toggled successfully");
|
||||
} else {
|
||||
// For input modes, read the level
|
||||
ret = esp_hosted_cp_gpio_get_level(SLAVE_GPIO_PIN, &level);
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "GPIO %d level: %d", SLAVE_GPIO_PIN, level);
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
esp_hosted_cp_gpio_config_t gpio_config = {
|
||||
.pin_bit_mask = (1ULL << SLAVE_GPIO_PIN),
|
||||
};
|
||||
|
||||
// Initialize ESP-Hosted
|
||||
ret = esp_hosted_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ESP-Hosted init failed: 0x%x", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_hosted_connect_to_slave();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Connect to slave failed: %s", esp_err_to_name(ret));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "ESP-Hosted initialized successfully");
|
||||
|
||||
// Demo 1: Standard output
|
||||
gpio_config.mode = H_CP_GPIO_MODE_OUTPUT;
|
||||
gpio_config.pull_up_en = 0;
|
||||
gpio_config.pull_down_en = 0;
|
||||
if (configure_and_test_gpio(&gpio_config, "Demo 1: Standard Output") != ESP_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Demo 2: Open-drain output with pull-up
|
||||
gpio_config.mode = H_CP_GPIO_MODE_OUTPUT_OD;
|
||||
gpio_config.pull_up_en = 1;
|
||||
gpio_config.pull_down_en = 0;
|
||||
if (configure_and_test_gpio(&gpio_config, "Demo 2: Open-Drain Output") != ESP_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Demo 3: Input with pull-up
|
||||
gpio_config.mode = H_CP_GPIO_MODE_INPUT;
|
||||
gpio_config.pull_up_en = 1;
|
||||
gpio_config.pull_down_en = 0;
|
||||
if (configure_and_test_gpio(&gpio_config, "Demo 3: Input with Pull-Up") != ESP_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Demo 4: Input with pull-down
|
||||
gpio_config.mode = H_CP_GPIO_MODE_INPUT;
|
||||
gpio_config.pull_up_en = 0;
|
||||
gpio_config.pull_down_en = 1;
|
||||
if (configure_and_test_gpio(&gpio_config, "Demo 4: Input with Pull-Down") != ESP_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "All demos completed successfully!");
|
||||
|
||||
cleanup:
|
||||
esp_hosted_deinit();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
dependencies:
|
||||
espressif/esp_hosted:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
version: '>=2.9.5'
|
||||
espressif/esp_wifi_remote:
|
||||
rules:
|
||||
- if: target in [esp32p4, esp32h2]
|
||||
- if: idf_version <6.1
|
||||
version: '>=1.3.1'
|
||||
@@ -0,0 +1,4 @@
|
||||
# ESP-Hosted GPIO Expander Example Configuration
|
||||
|
||||
# Enable GPIO expander support
|
||||
CONFIG_ESP_HOSTED_ENABLE_GPIO_EXPANDER=y
|
||||
Reference in New Issue
Block a user