Files
desklock/firmware/components/esp_hosted/examples/host_bluedroid_host_only
jpmschweitzerandClaude Fable 5 cb5826e02b
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
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>
2026-07-15 09:50:27 +02:00
..

Supported Hosts ESP32 ESP32-P Series ESP32-H Series ESP32-C Series ESP32-S Series Any other MCU hosts
Supported Co-Processors ESP32

ESP-IDF ESP-Hosted Hosted HCI Host

This is a Bluetooth Host using ESP-Hosted as HCI IO to the BT Controller.

Example Layout

This example is modified based on bt_discovery, and all modifications are listed below:

  • Removed all dependencies on controller from main.c.
#include "esp_bt.h"

...

ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));

esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
    ESP_LOGE(GAP_TAG, "%s initialize controller failed: %s", __func__, esp_err_to_name(ret));
    return;
}

if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) {
    ESP_LOGE(GAP_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
    return;
}
  • Add support for ESP-Hosted HCI interface: esp_hosted_bt.h.

  • Open HCI interface in main.c.

#include "esp_hosted_bt.h"

...

/* initialize TRANSPORT first */
hosted_hci_bluedroid_open();

/* get HCI driver operations */
esp_bluedroid_hci_driver_operations_t operations = {
    .send = hosted_hci_bluedroid_uart_send,
    .check_send_available = hosted_hci_bluedroid_check_send_available,
    .register_host_callback = hosted_hci_bluedroid_register_host_callback,
};
esp_bluedroid_attach_hci_driver(&operations);

How to use example

Hardware Required

This example runs on the ESP32-P4 Dev Board connected to a ESP32 via the GPIO header, using SPI FD (full duplex) as Hosted HCI transport. The following GPIO settings were used:

SPI Function ESP32 GPIO ESP32-P4 GPIO
MOSI 13 4
MISO 12 5
CLK 14 26
CS 15 6
Handshake 26 20
Data Ready 4 36
Reset -1 2

Note

SPI Mode 2 was used on both the ESP32-P4 and ESP32.

Users are free to choose which supported ESP-Hosted transport to use. See the main ESP-Hosted README for a list of supported transports.

For standard HCI, configure the co-processor Bluetooth Controller to use UART as the HCI transport, then select appropriate GPIOs on the ESP32-P4 to configure as a UART. In this mode, ESP-Hosted is not involved in transporting the HCI data.

See the ESP-IDF UART HCI Host example on how to set-up UART for the Bluetooth Host.

Configure the project

First, set the host target to ESP32-P4:

idf.py set-target esp32p4

For the ESP32 co-processor, run idf.py menuconfig and configure Example Configuration for SPI Full-duplex with the correct SPI mode and GPIOs.

For the ESP32-P4 host, run idf.py menuconfig and under Component config ---> ESP-Hosted config:

  • set the transport to be SPI Full-duplex with the correct SPI modem GPIOs (see above table) and SPI Clock frequency (10 MHz max).
  • set the Slave chipset used as ESP32.
  • set Bluetooth Support ---> Enable Hosted Bluedroid Bluetooth support to enable Bluedroid support. Leave the HCI type as VHCI.

Build and Flash

After setting the host target and configuring the project, build and flash the co-processor and host projects, then run monitor tool to view serial output on both the ESP32 and ESP32-P4:

idf.py -p PORT flash monitor

(Replace PORT with the name of the serial port to use.)

(To exit the serial monitor, type Ctrl-].)