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>
9.2 KiB
This migration guide documents key changes in ESP-Hosted that users must be aware of when migrating from older versions.
Index
- 2.5.2 - Bluetooth Controller on Co-Processor Disabled by Default
- 2.6.0 - ESP-Hosted Slave OTA
- 2.11.0 - ESP-Hosted Host Driver
- 2.12.4 - Custom Msg Callback - User Ptr
{\color{yellow} \text{2.12.4 - Custom Msg Callback - User Ptr}}
Migration needed from versions
| Firmware | Version | Migration required |
|---|---|---|
| Host | < 2.12.4 | ✅ |
| Slave | < 2.12.4 | ✅ |
Reason for change
esp_hosted_register_custom_callback()now supports a user-provided pointer to be passed back on every callback invocation.- This allows external code to maintain per-callback context without global variables.
Old API
esp_err_t esp_hosted_register_custom_callback(
uint32_t msg_id,
void (*callback)(uint32_t msg_id, const uint8_t *data, size_t data_len));
esp_err_t esp_hosted_send_custom_data(uint32_t msg_id, const uint8_t *data, size_t data_len);
New API
esp_err_t esp_hosted_register_custom_callback(uint32_t msg_id_exp,
void (*callback)(uint32_t msg_id_recvd, const uint8_t *data_recvd, size_t data_len_recvd, void *local_context), // <-- Changed
void *local_context); // <-- Extra argument
esp_err_t esp_hosted_send_custom_data(uint32_t msg_id_to_send, const uint8_t *data_to_send, size_t data_len_to_send); // no logical change
Arguments:
msg_id– message ID to register- ⚡
callback– function pointer to handle the message (adds void* as last arg) - ⚡
user– user-provided pointer returned on every callback invocation
Returns: ESP_OK on success, or an error code on failure.
{\color{yellow} \text{2.11.0 - ESP-Hosted Host Driver}}
Migration needed from versions
| Host version | wifi-remote version |
|---|---|
| < 2.11.0 | < 1.3.1 |
-
A double-free memory error can occur in some situations when ESP-Hosted Host receives network data and passes it to the netif
rx()function (registered by netif via the wifi-remote component) for processing. -
This error is resolved in ESP-Hosted v2.11.1. It must be used with wifi-remote v1.3.1 or greater to prevent a memory leak condition during netif initialization.
{\color{yellow} \text{2.6.0 - ESP-Hosted Slave OTA}}
Migration needed from versions
| Slave version | Host version |
|---|---|
| > 2.5.X | > 2.5.X |
Reason for change
- The existing
esp_hosted_slave_ota()API was restrictive, supporting only HTTP-based OTA updates. The OTA APIs are now exposed so developers can implement their own OTA mechanisms. - The port layer previously contained OTA logic, which forced inclusion of the HTTP client in the host codebase even when not required.
Changes required on host
If you are migrating from the old esp_hosted_slave_ota() function, update your code as follows.
Old API (deprecated)
#include "esp_hosted.h"
const char *image_url = "http://example.com/network_adapter.bin";
esp_err_t ret = esp_hosted_slave_ota(image_url);
if (ret == ESP_OK) {
printf("OTA update failed[%d]\n", ret);
}
New APIs
The slave OTA process is now performed using the following APIs.
esp_hosted_slave_ota_begin()
esp_err_t esp_hosted_slave_ota_begin(void);
Initializes the OTA process on the slave.
-
Arguments: None
-
Returns:
ESP_OKon success, or an error code on failure -
What it does:
- Prepares the slave for firmware reception
- Allocates OTA buffers
- Sets up the OTA partition on the slave
esp_hosted_slave_ota_write()
esp_err_t esp_hosted_slave_ota_write(const void *data, size_t size);
Sends firmware data chunks to the slave.
-
Arguments:
data: Pointer to firmware data chunksize: Size of the data chunk (typically 1400–1500 bytes)
-
Returns:
ESP_OKon success, or an error code on failure -
What it does:
- Transmits firmware data over ESP-Hosted transport (SDIO/SPI/UART)
- The slave writes data to its OTA partition
- Can be called multiple times for large firmware images
esp_hosted_slave_ota_end()
esp_err_t esp_hosted_slave_ota_end(void);
Finalizes the OTA process.
-
Arguments: None
-
Returns:
ESP_OKon success, or an error code on failure -
What it does:
- Validates the complete firmware image on the slave
- Calculates and verifies checksums
- Marks the new firmware as valid but not yet active
esp_hosted_slave_ota_activate()
esp_err_t esp_hosted_slave_ota_activate(void);
Activates the newly flashed firmware.
-
Arguments: None
-
Returns:
ESP_OKon success, or an error code on failure -
What it does:
- Switches the slave’s boot partition to the new firmware
- Triggers slave reboot with the new firmware
- Note: After this call, the slave restarts with the new firmware
How to use the new APIs
A dedicated example demonstrates the usage of the new OTA APIs: Slave OTA using ESP-Hosted transport
Tip
The example uses the new ESP-Hosted-MCU Slave OTA APIs. You can reuse or customize it for your own OTA workflow.
Example methods supported:
| Method | Description |
|---|---|
| Partition method | Slave firmware binary stored in Host’s partition table (slave_fw partition). Requires an extra host partition, but no Wi-Fi connectivity. |
| LittleFS method | Host partition formatted as LittleFS and stores the slave firmware. Requires an extra host partition, but no Wi-Fi connectivity. |
| HTTPS method | Slave firmware binary hosted on an HTTPS server. No extra host partition needed, but requires Wi-Fi connectivity. |
{\color{yellow} \text{2.5.2 - Bluetooth Controller on Co-Processor Disabled by Default}}
Migration needed from versions
| Slave version | Host version |
|---|---|
| > 2.5.1 | > 2.5.1 |
Before v2.5.2, the Bluetooth controller on the co-processor was initialized and enabled by default. From v2.5.2 onwards, it starts in a disabled state.
Reason for change
This allows users to modify the Bluetooth MAC address before the controller is initialized, as it can only be changed prior to enabling the controller.
New APIs
esp_hosted_bt_controller_init()
esp_err_t esp_hosted_bt_controller_init(void);
Initializes the Bluetooth controller on the co-processor.
-
Arguments: None
-
Returns:
ESP_OKon success, or an error code on failure -
What it does:
- Allocates and initializes controller resources
- Prepares the controller for activation
esp_hosted_bt_controller_deinit()
esp_err_t esp_hosted_bt_controller_deinit(bool mem_release);
Deinitializes the Bluetooth controller on the co-processor.
-
Arguments:
mem_release: If true, releases controller memory (cannot be reused)
-
Returns:
ESP_OKon success, or an error code on failure -
What it does:
- Stops the Bluetooth controller
- Optionally releases memory used by the controller
- Once released, the controller cannot be reinitialized without reboot
esp_hosted_bt_controller_enable()
esp_err_t esp_hosted_bt_controller_enable(void);
Enables the Bluetooth controller on the co-processor.
-
Arguments: None
-
Returns:
ESP_OKon success, or an error code on failure -
What it does:
- Starts the Bluetooth controller task
- Enables radio and HCI interfaces for Bluetooth operation
esp_hosted_bt_controller_disable()
esp_err_t esp_hosted_bt_controller_disable(void);
Disables the Bluetooth controller on the co-processor.
-
Arguments: None
-
Returns:
ESP_OKon success, or an error code on failure -
What it does:
- Gracefully stops the controller
- Disables the Bluetooth radio
- Must be called before deinitializing the controller
Changes required on host
Before starting the Bluetooth stack on the host:
- Call
esp_hosted_connect_to_slave()to establish a connection with the slave. - (Optional) Set the Bluetooth MAC address using
esp_hosted_iface_mac_addr_set(). - Initialize the Bluetooth controller using
esp_hosted_bt_controller_init(). - Enable the Bluetooth controller using
esp_hosted_bt_controller_enable().
See Initializing the Bluetooth Controller for more details.
How to use the new APIs
You can now start the host Bluetooth stack and use Bluetooth as usual. All ESP-Hosted Bluetooth host examples (NimBLE and BlueDroid) have been updated accordingly.
For an example showing how to change the BT MAC address before starting the controller, refer to: BT Controller Example