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>
13 KiB
ESP-Hosted Performance Optimization Guide
Quick reference for optimizing ESP-Hosted performance across co-processors and different transport interfaces.
Table of Contents
1 Quick Start - High Performance Config
For immediate performance gains, add these to your host's sdkconfig.defaults.esp32XX file based on the co-processor you are using.
Note
Adjust the values based on your MCU host's and co-processor memory capacity. These values may change when more testing shows better performance figures.
Test conditions for throughput numbers using the performance settings:
- raw: data transferred from sender to receiver over transport
- iPerf used to measure TCP and UDP throughput
A diagram showing the setup used to get the throughput numbers.
flowchart TB
%% floating IP labels — above their boxes, faint leader line
ipAP["192.168.1.1"]:::ip
ipP4["192.168.1.2"]:::ip
ipHost["192.168.1.88 · test<br/>10.0.0.1 · control"]:::ip
ipDev["10.0.0.2"]:::ip
subgraph SB["Shield Box"]
AP["📶 AP / Router"]
subgraph EVB["ESP32-P4-Function-EV-Board 1.2+"]
direction LR
C6["ESP32-C6<br/>Wi-Fi slave"]
P4["ESP32-P4<br/>iperf app"]
C6 ---|"SDIO"| P4
end
AP -.-|"Wi-Fi"| C6
HOST["AP-backend<br/>iperf host"]
AP ===|"LAN cable"| HOST
P4 ---|"USB / UART"| HOST
end
DEV["Dev machine"]
HOST ---|"control"| DEV
%% faint leaders from IP labels to devices
ipAP -.- AP
ipP4 -.- P4
ipHost -.- HOST
ipDev -.- DEV
style SB fill:#fff7ec,stroke:#e8a33d,color:#333
style EVB fill:#efeaf8,stroke:#8a7bbd,color:#333
classDef node fill:#ffffff,stroke:#99aabb,color:#111
classDef ip fill:none,stroke:none,color:#555
class AP,C6,P4,HOST,DEV node
linkStyle 0 stroke:#e8762d,stroke-width:2.5px
linkStyle 1 stroke:#e8762d,stroke-width:2.5px
linkStyle 2 stroke:#e8762d,stroke-width:2.5px
linkStyle 3 stroke:#888888,stroke-width:1.5px,stroke-dasharray:5
linkStyle 4 stroke:#0e9488,stroke-width:2px
linkStyle 5 stroke:#cccccc,stroke-width:1px,stroke-dasharray:2 2
linkStyle 6 stroke:#cccccc,stroke-width:1px,stroke-dasharray:2 2
linkStyle 7 stroke:#cccccc,stroke-width:1px,stroke-dasharray:2 2
linkStyle 8 stroke:#cccccc,stroke-width:1px,stroke-dasharray:2 2
Note
The diagram shows the router and ESP board in a shield box. The performance numbers here were obtained from an 'Open Air' configuration, without using a shield box.
1.1 ESP32-C6 as Co-Procesor
### sdkconfig for ESP32-P4 + C6 as co-processor
# Let P4 know, C6 is attached as slave
CONFIG_SLAVE_IDF_TARGET_ESP32C6=y
CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6=y
# Wi-Fi Performance
CONFIG_WIFI_RMT_STATIC_RX_BUFFER_NUM=16
CONFIG_WIFI_RMT_DYNAMIC_RX_BUFFER_NUM=64
CONFIG_WIFI_RMT_DYNAMIC_TX_BUFFER_NUM=64
CONFIG_WIFI_RMT_AMPDU_TX_ENABLED=y
CONFIG_WIFI_RMT_TX_BA_WIN=32
CONFIG_WIFI_RMT_AMPDU_RX_ENABLED=y
CONFIG_WIFI_RMT_RX_BA_WIN=32
# TCP/IP Performance
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534
CONFIG_LWIP_TCP_WND_DEFAULT=65534
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64
CONFIG_LWIP_TCP_SACK_OUT=y
Throughput using the settings.
Using SDIO Transport, 4-bits, running at 40MHz, connected to a 2.4GHz network over the air
| Type | Direction | MBits/s |
|---|---|---|
| Raw | P4 to C6 | 72 |
| Raw | C6 to P4 | 80 |
| iPerf, TCP | P4 to Test PC | 32 |
| iPerf, UDP | P4 to Test PC | 50 |
| iPerf, TCP | Test PC to P4 | 30 |
| iPerf, UDP | Test PC to P4 | 49 |
1.2 ESP32-C5 as Co-Processor
### sdkconfig for ESP32-P4 + C5 as co-processor
# Let P4 know, C5 is attached as slave
CONFIG_SLAVE_IDF_TARGET_ESP32C5=y
CONFIG_ESP_HOSTED_CP_TARGET_ESP32C5=y
# Optional PCB selection: Uncomment if you are using Pre designed PCB P4_C5_CORE_BOARD (to use correct GPIOs on that PCB)
# CONFIG_ESP_HOSTED_P4_C5_CORE_BOARD=y
# Wi-Fi Performance
CONFIG_WIFI_RMT_STATIC_RX_BUFFER_NUM=10
CONFIG_WIFI_RMT_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_WIFI_RMT_DYNAMIC_TX_BUFFER_NUM=32
CONFIG_WIFI_RMT_AMPDU_TX_ENABLED=y
CONFIG_WIFI_RMT_TX_BA_WIN=32
CONFIG_WIFI_RMT_AMPDU_RX_ENABLED=y
CONFIG_WIFI_RMT_RX_BA_WIN=16
# TCP/IP Performance
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=11520
CONFIG_LWIP_TCP_WND_DEFAULT=32768
CONFIG_LWIP_TCP_RECVMBOX_SIZE=48
CONFIG_LWIP_UDP_RECVMBOX_SIZE=48
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=48
CONFIG_LWIP_TCP_SACK_OUT=y
Throughput using the settings.
Using SDIO Transport, 4-bits, running at 40MHz, connected to a 5GHz network over the air
| Type | Direction | MBits/s |
|---|---|---|
| Raw | P4 to C5 | 72 |
| Raw | C5 to P4 | 81 |
| iPerf, TCP | P4 to Test PC | 23 |
| iPerf, UDP | P4 to Test PC | 67 |
| iPerf, TCP | Test PC to P4 | 32 |
| iPerf, UDP | Test PC to P4 | 68 |
1.3 ESP32-C61 as Co-Processor
### sdkconfig for ESP32-P4 + C61 as co-processor
# Let P4 know, C61 is attached as slave
CONFIG_SLAVE_IDF_TARGET_ESP32C61=y
CONFIG_ESP_HOSTED_CP_TARGET_ESP32C61=y
# Optional PCB selection: Uncomment if you are using Pre designed PCB P4_C61_CORE_BOARD (to use correct GPIOs on that PCB)
# CONFIG_ESP_HOSTED_P4_C61_CORE_BOARD=y
# Wi-Fi Performance
CONFIG_WIFI_RMT_STATIC_RX_BUFFER_NUM=10
CONFIG_WIFI_RMT_DYNAMIC_RX_BUFFER_NUM=16
CONFIG_WIFI_RMT_DYNAMIC_TX_BUFFER_NUM=16
CONFIG_WIFI_RMT_AMPDU_TX_ENABLED=y
CONFIG_WIFI_RMT_TX_BA_WIN=16
CONFIG_WIFI_RMT_AMPDU_RX_ENABLED=y
CONFIG_WIFI_RMT_RX_BA_WIN=16
# TCP/IP Performance
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=12930
CONFIG_LWIP_TCP_WND_DEFAULT=22488
CONFIG_LWIP_TCP_RECVMBOX_SIZE=48
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=48
CONFIG_LWIP_IP_REASS_MAX_PBUFS=15
CONFIG_LWIP_TCP_SACK_OUT=y
CONFIG_LWIP_TCPIP_CORE_LOCKING=y
CONFIG_LWIP_TCPIP_CORE_LOCKING_INPUT=y
Throughput using the settings.
Using SPI-FD Transport, running at 40MHz, connected to a 2.4GHz network over the air
| Type | Direction | MBits/s |
|---|---|---|
| Raw | P4 to C61 | 25 |
| Raw | C61 to P4 | 26 |
| iPerf, TCP | P4 to Test PC | 12 |
| iPerf, UDP | P4 to Test PC | 18 |
| iPerf, TCP | Test PC to P4 | 15 |
| iPerf, UDP | Test PC to P4 | 23 |
1.4 ESP32-C2 as Co-Processor
# Let P4 know, C2 is attached as slave
CONFIG_SLAVE_IDF_TARGET_ESP32C2=y
CONFIG_ESP_HOSTED_CP_TARGET_ESP32C2=y
### sdkconfig for ESP32-P4 + C2 as co-processor
# Wi-Fi Performance
CONFIG_WIFI_RMT_STATIC_RX_BUFFER_NUM=10
CONFIG_WIFI_RMT_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_WIFI_RMT_DYNAMIC_TX_BUFFER_NUM=32
CONFIG_WIFI_RMT_AMPDU_TX_ENABLED=y
CONFIG_WIFI_RMT_TX_BA_WIN=6
CONFIG_WIFI_RMT_AMPDU_RX_ENABLED=y
CONFIG_WIFI_RMT_RX_BA_WIN=6
# TCP/IP Performance
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=16384
CONFIG_LWIP_TCP_WND_DEFAULT=32768
CONFIG_LWIP_TCP_RECVMBOX_SIZE=20
CONFIG_LWIP_UDP_RECVMBOX_SIZE=20
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=20
CONFIG_LWIP_TCP_SACK_OUT=y
Throughput using the settings.
Using SPI-FD Transport, running at 40MHz, connected to a 2.4GHz network over the air
| Type | Direction | MBits/s |
|---|---|---|
| Raw | P4 to C2 | 25 |
| Raw | C2 to P4 | 26 |
| iPerf, TCP | P4 to Test PC | 12 |
| iPerf, UDP | P4 to Test PC | 18 |
| iPerf, TCP | Test PC to P4 | 13 |
| iPerf, UDP | Test PC to P4 | 15 |
1.5 ESP32-S2 as Co-Processor
### sdkconfig for ESP32-P4 + S2 as co-processor
# Let P4 know, S2 is attached as slave
CONFIG_SLAVE_IDF_TARGET_ESP32S2=y
CONFIG_ESP_HOSTED_CP_TARGET_ESP32S2=y
# Wi-Fi Performance
CONFIG_WIFI_RMT_STATIC_RX_BUFFER_NUM=8
CONFIG_WIFI_RMT_DYNAMIC_RX_BUFFER_NUM=24
CONFIG_WIFI_RMT_DYNAMIC_TX_BUFFER_NUM=24
CONFIG_WIFI_RMT_AMPDU_TX_ENABLED=y
CONFIG_WIFI_RMT_TX_BA_WIN=16
CONFIG_WIFI_RMT_AMPDU_RX_ENABLED=y
CONFIG_WIFI_RMT_RX_BA_WIN=16
# TCP/IP Performance
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=17280
CONFIG_LWIP_TCP_WND_DEFAULT=28000
CONFIG_LWIP_TCP_RECVMBOX_SIZE=32
CONFIG_LWIP_UDP_RECVMBOX_SIZE=32
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32
CONFIG_LWIP_TCP_SACK_OUT=y
1.6 ESP32-C3 as Co-Processor
### sdkconfig for ESP32-P4 + C3 as co-processor
# Let P4 know, C3 is attached as slave
CONFIG_SLAVE_IDF_TARGET_ESP32C3=y
CONFIG_ESP_HOSTED_CP_TARGET_ESP32C3=y
# Wi-Fi Performance
CONFIG_WIFI_RMT_STATIC_RX_BUFFER_NUM=20
CONFIG_WIFI_RMT_DYNAMIC_RX_BUFFER_NUM=40
CONFIG_WIFI_RMT_DYNAMIC_TX_BUFFER_NUM=40
CONFIG_WIFI_RMT_AMPDU_TX_ENABLED=y
CONFIG_WIFI_RMT_TX_BA_WIN=32
CONFIG_WIFI_RMT_AMPDU_RX_ENABLED=y
CONFIG_WIFI_RMT_RX_BA_WIN=32
# TCP/IP Performance
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=40960
CONFIG_LWIP_TCP_WND_DEFAULT=40960
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64
Throughput using the settings.
Using SPI-FD Transport, running at 40MHz, connected to a 2.4GHz network over the air
| Type | Direction | MBits/s |
|---|---|---|
| Raw | P4 to C3 | 26 |
| Raw | C3 to P4 | 26 |
| iPerf, TCP | P4 to Test PC | 19 |
| iPerf, UDP | P4 to Test PC | 25 |
| iPerf, TCP | Test PC to P4 | 18 |
| iPerf, UDP | Test PC to P4 | 20 |
Throughput using the settings.
Using SPI-FD Transport, running at 40MHz, connected to a 2.4GHz network over the air
| Type | Direction | MBits/s |
|---|---|---|
| Raw | P4 to S2 | 25 |
| Raw | S2 to P4 | 26 |
| iPerf, TCP | P4 to Test PC | 8 |
| iPerf, UDP | P4 to Test PC | 11 |
| iPerf, TCP | Test PC to P4 | 12 |
| iPerf, UDP | Test PC to P4 | 15 |
2 Transport Optimization
2.1 SDIO (Highest Performance)
- Clock Speed: Start at 20 MHz, optimize up to 50 MHz
- Bus Width: Use 4-bit mode
- Hardware: Use PCB with controlled impedance, external pull-ups (51kΩ)
- Checksum: Optional (SDIO hardware handles verification)
CONFIG_ESP_HOSTED_SDIO_CLOCK_FREQ_KHZ=40000
CONFIG_ESP_HOSTED_SDIO_BUS_WIDTH=4
Note
See Performance and Memory Usage on the trade-off between SDIO Performance and Memory Use
2.2 SPI Full-Duplex
- Clock Speed: ESP32: ≤10 MHz, Others: ≤40 MHz
- Hardware: Use IO_MUX pins, short traces (≤10cm for jumpers)
- Checksum: Mandatory (SPI hardware lacks error detection)
CONFIG_ESP_HOSTED_SPI_CLK_FREQ=40
2.3 SPI Half-Duplex
- Data Lines: Use 4-line (Quad SPI) mode
- Similar optimizations as SPI Full-Duplex
2.4 UART (Lowest Performance)
- Baud Rate: Use 921600 (highest stable rate)
- Best for: Low-throughput applications, debugging
3 Memory Optimization
-
Reduce memory footprint for resource-constrained applications:
# Reduce queue sizes CONFIG_ESP_HOSTED_SDIO_TX_Q_SIZE=10 # Default: 20 CONFIG_ESP_HOSTED_SDIO_RX_Q_SIZE=10 # Default: 20 # Enable memory pooling CONFIG_ESP_HOSTED_USE_MEMPOOL=y -
Disable the not-in-use features
- For example, disable bluetooth if not needed
-
Use external RAM, for higher memory (PSRAM is supported)
-
Optimise internal RAM using ESP-IDF iram optimization tricks
4 Hardware Guidelines
4.1 Critical Requirements
- Signal Integrity: Use PCB designs for production, jumpers only for prototyping
- Power Supply: Stable 3.3V, proper decoupling capacitors
- Trace Length: Match lengths, especially clock vs data lines
- Pull-ups: Required for SDIO (51kΩ) on CMD, D0-D3 lines
4.2 PCB Design Checklist
- Equal trace lengths for communication signals
- Ground plane for signal stability
- Controlled impedance traces (50Ω typical)
- Series termination resistors for high-speed signals
- Extra GPIOs reserved for future features (deep sleep, etc.)
4.3 Development Workflow
- Proof of Concept: Start with jumper wires, low clock speeds
- Incremental Optimization: Increase transport clock step by step
- Hardware Validation: Move to PCB for final validation
- Performance Tuning: Optimize buffers and configurations
- Disable features: Any unused components from ESP-IDF or
ESP-Hosted-MCU features could be disabled for more memory availability.