Fork fix: the SDIO wedge is FIXED (esp-hosted-mcu #167)
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

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:
2026-07-15 09:50:27 +02:00
co-authored by Claude Fable 5
parent ac9d24e0f9
commit cb5826e02b
666 changed files with 216925 additions and 0 deletions
@@ -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.22)
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(esp_ot_cli)
@@ -0,0 +1,278 @@
| Supported Hosts | ESP32-P4 |
| --------------- | -------- |
| Supported Co-processors | ESP32-C5 | ESP32-C6 | ESP32-H2 |
|-------------------------|----------|----------|----------|
# OpenThread Command Line Example
This example demonstrates an [OpenThread CLI](https://github.com/openthread/openthread/blob/master/src/cli/README.md), with some additional features such as iperf.
## Example 1: OpenThread Host communicating with Openthread RCP over UART
The example runs on an ESP32-P4, connected to a ESP32-C6 DevKit as the co-processor via the GPIO Header. ESP-Hosted transport is SPI-FD (Full duplex) while UART is used for OpenThread communications.
### Setting up the hardware
This table shows the Hardware Connections between the ESP32-P4 and C6.
**SPI-FD Connection Between ESP32-P4 and ESP32-C6 co-processor**
| | ESP32-P4 GPIO | ESP32-C6 GPIO |
|------------|--------------:|--------------:|
| MOSI | 4 | 7 |
| MISO | 5 | 2 |
| CLK | 26 | 6 |
| CS | 6 | 10 |
| Handshake | 20 | 3 |
| Data Ready | 32 | 4 |
| C6 Reset | 2 | RST |
**UART Connection Between ESP32-P4 and ESP32-C6 co-processor**
| ESP32-P4 GPIO | ESP32-C6 GPIO |
|---------------:|--------------:|
| (To RCP Tx) 24 | (Tx) 21 |
| (To RCP Rx) 25 | (Rx) 20 |
### Configure the project for ESP32-P4
On the command-line:
```bash
idf.py set-target esp32p4
idf.py menuconfig
```
Configure the project:
```
Component config
└── ESP-Hosted config
├── Configure GPIOs for Development Board ──> No development board
├── Transport layer ──> SPI Full-duplex
├── SPI Configuration ──> (Configure GPIOs)
│ ⋮
└── [*] Enable OpenThread Host Support
└── OpenThread RCP Configuration
├── OpenThread Transport ──> UART
└── (configure UART parameters)
```
### Configure the project for ESP32-C6
In the ESP-Hosted project `slave` directory:
Edit `sdkconfig.defaults.esp32c6` and enable the OpenThread section:
```
#
# OpenThread
#
CONFIG_OPENTHREAD_ENABLED=y
CONFIG_OPENTHREAD_RADIO=y
CONFIG_OPENTHREAD_DIAG=n
CONFIG_OPENTHREAD_COMMISSIONER=n
CONFIG_OPENTHREAD_JOINER=n
CONFIG_OPENTHREAD_BORDER_ROUTER=n
CONFIG_OPENTHREAD_CLI=n
CONFIG_OPENTHREAD_SRP_CLIENT=n
CONFIG_OPENTHREAD_DNS_CLIENT=n
CONFIG_OPENTHREAD_TASK_SIZE=3072
CONFIG_OPENTHREAD_CONSOLE_ENABLE=n
CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC=n
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
# end of OpenThread
```
On the command-line:
```bash
idf.py set-target esp32c6
idf.py menuconfig
```
Configure the project:
```
Example Configuration
├── Bus Config in between Host and Co-processor
│ ├── Transport layer ──> SPI Full-duplex
│ └── SPI Full-duplex Configuration ──> (Configure GPIOs)
│ ⋮
└── [*] Enable OpenThread RCP (Radio Co-Processor)
└── OpenThread RCP Configuration
├── OpenThread Transport ──> UART
└── (configure UART parameters)
```
If you did not edit `sdkconfig.defaults.esp32c6` to enable OpenThread (above), modify these settings:
```
Component config
├── Wireless Coexistence
│ └── [*] Software controls WiFi/Bluetooth coexistence
└── OpenThread
├── [*] OpenThread
├── Thread Task Parameters
│ └── (3072) Size of OpenThread task
├── Thread Console
│ ├── [ ] Enable OpenThread console
│ └── [ ] Enable Openthread Command-Line Interface
├── Thread Core Features
│ ├── Thread device type ──> Radio Only Device
│ ├── [ ] Enable Commissioner
│ ├── [ ] Enable Joiner
│ ├── [ ] Enable SRP Client
│ ├── [ ] Enable DNS Client
│ ├── [ ] Enable diag Client
└── Thread Log
└── [ ] Enable dynamic log level control
```
### Build, Flash, and Run
Build the projects and flash them to the appropriate boards, then run monitor tool to view serial output:
```bash
idf.py -p PORT build flash monitor
```
On the ESP32-P4 you'll get an OpenThread command line shell.
### Example Output
The `help` command will print all of the supported commands.
```text
esp32h2> ot help
I(7058) OPENTHREAD:[INFO]-CLI-----: execute command: help
bbr
bufferinfo
ccathreshold
channel
child
childip
childmax
childsupervision
childtimeout
coap
contextreusedelay
counters
dataset
delaytimermin
diag
discover
dns
domainname
eidcache
eui64
extaddr
extpanid
factoryreset
...
```
## Set Up Network
To run this example, one more board (for example, an ESP32-H2) with the ESP-IDF [ot_cli example](https://github.com/espressif/esp-idf/tree/master/examples/openthread/ot_cli) is required..
On the ESP32-P4, run the following commands:
```test
esp32p4> ot factoryreset
... # the device will reboot
esp32p4> ot dataset init new
Done
esp32p4> ot dataset commit active
Done
esp32p4> ot ifconfig up
Done
esp32p4> ot thread start
Done
# After some seconds
esp32p4> ot state
leader
Done
```
Now the ESP32-P4 has formed a Thread network as a leader. Get some information which will be used in next steps:
```text
esp32p4> ot ipaddr
fdde:ad00:beef:0:0:ff:fe00:fc00
fdde:ad00:beef:0:0:ff:fe00:8000
fdde:ad00:beef:0:a7c6:6311:9c8c:271b
fe80:0:0:0:5c27:a723:7115:c8f8
# Get the Active Dataset
esp32p4> ot dataset active -x
0e080000000000010000000300001835060004001fffe00208fe7bb701f5f1125d0708fd75cbde7c6647bd0510b3914792d44f45b6c7d76eb9306eec94030f4f70656e5468726561642d35383332010258320410e35c581af5029b054fc904a24c2b27700c0402a0fff8
```
On the ESP32-H2, set the active dataset from leader, and start Thread interface:
```text
esp32h2> ot factoryreset
... # the device will reboot
esp32h2> ot dataset set active 0e080000000000010000000300001835060004001fffe00208fe7bb701f5f1125d0708fd75cbde7c6647bd0510b3914792d44f45b6c7d76eb9306eec94030f4f70656e5468726561642d35383332010258320410e35c581af5029b054fc904a24c2b27700c0402a0fff8
esp32h2> ot ifconfig up
Done
esp32h2> ot thread start
Done
# After some seconds
esp32h2> ot state
router # child is also a valid state
Done
```
The second device has joined the Thread network as a router (or a child).
## Extension commands
You can refer to the [extension command](https://github.com/espressif/esp-thread-br/blob/main/components/esp_ot_cli_extension/README.md) about the extension commands.
The following examples are supported by `ot_cli`:
* TCP and UDP Example
## Using iPerf to measure bandwidth
iPerf is a tool used to obtain TCP or UDP throughput on the Thread network. To run iPerf, you need to have two Thread devices on the same network.
Refer to [the iperf-cmd component](https://components.espressif.com/components/espressif/iperf-cmd) for details on specific configurations.
### Typical usage on a thread network
> [!NOTE]
> The [ML-EID](https://openthread.io/guides/thread-primer/ipv6-addressing#unicast_address_types) address is used for iperf.
For measuring the TCP throughput, first get the ML-EID address, then create an iperf service on one node:
```text
> ot ipaddr mleid
fdde:ad00:beef:0:a7c6:6311:9c8c:271b
Done
> iperf -V -s -t 20 -i 3 -p 5001 -f k
Done
```
Then create an iperf client connecting to the service on another node.
```text
> iperf -V -c fdde:ad00:beef:0:a7c6:6311:9c8c:271b -t 20 -i 1 -p 5001 -l 85 -f k
Done
[ ID] Interval Transfer Bandwidth
[ 1] 0.0- 1.0 sec 3.15 KBytes 25.16 Kbits/sec
[ 1] 1.0- 2.0 sec 2.89 KBytes 23.12 Kbits/sec
[ 1] 2.0- 3.0 sec 2.98 KBytes 23.80 Kbits/sec
...
[ 1] 9.0-10.0 sec 2.55 KBytes 20.40 Kbits/sec
[ 1] 0.0-10.0 sec 27.80 KBytes 22.24 Kbits/sec
```
@@ -0,0 +1,2 @@
idf_component_register(SRCS "esp_ot_cli.c" "esp_hosted_openthread_app.c"
INCLUDE_DIRS ".")
@@ -0,0 +1,74 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*
*/
#include "esp_hosted.h"
#include "esp_hosted_openthread.h"
#include "esp_hosted_openthread_app.h"
#include "esp_log.h"
#define TAG "h_ot_util"
esp_err_t esp_hosted_openthread_app_init(void)
{
esp_err_t res = ESP_OK;
res = esp_hosted_openthread_rcp_query(HOSTED_OPENTHREAD_QUERY_CONFIGURED);
if (ESP_OK != res) {
ESP_LOGE(TAG, "OT RCP not configured (enabled via Kconfig) on co-processor");
return res;
}
res = esp_hosted_openthread_rcp_init();
if (ESP_OK != res) {
ESP_LOGE(TAG, "failed to init OT RCP");
return res;
}
res = esp_hosted_openthread_rcp_query(HOSTED_OPENTHREAD_QUERY_INITED);
if (ESP_OK != res) {
ESP_LOGE(TAG, "OT RCP not inited on co-processor");
return res;
}
res = esp_hosted_openthread_rcp_start();
if (ESP_OK != res) {
ESP_LOGE(TAG, "failed to start OT RCP");
return res;
}
res = esp_hosted_openthread_rcp_query(HOSTED_OPENTHREAD_QUERY_ENABLED);
if (ESP_OK != res) {
ESP_LOGE(TAG, "OT RCP not enabled on co-processor");
return res;
}
res = esp_hosted_openthread_rcp_query(HOSTED_OPENTHREAD_QUERY_READY);
if (ESP_OK != res) {
ESP_LOGE(TAG, "OT RCP not ready on co-processor");
return res;
}
return res;
}
esp_err_t esp_hosted_openthread_app_deinit(void)
{
esp_err_t res = ESP_OK;
res = esp_hosted_openthread_rcp_stop();
if (ESP_OK != res) {
ESP_LOGE(TAG, "OT RCP failed to stop");
return res;
}
res = esp_hosted_openthread_rcp_deinit();
if (ESP_OK != res) {
ESP_LOGE(TAG, "OT RCP failed to deinit");
return res;
}
return res;
}
@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*
*/
#ifndef __ESP_HOSTED_OPENTHREAD_APP_H__
#define __ESP_HOSTED_OPENTHREAD_APP_H__
#include "esp_err.h"
esp_err_t esp_hosted_openthread_app_init(void);
esp_err_t esp_hosted_openthread_app_deinit(void);
#endif
@@ -0,0 +1,148 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*
* OpenThread Command Line 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 <stdio.h>
#include <unistd.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_netif_types.h"
#include "esp_openthread.h"
#include "esp_openthread_lock.h"
#include "esp_openthread_netif_glue.h"
#include "esp_openthread_types.h"
#include "esp_ot_config.h"
#include "esp_vfs_eventfd.h"
#include "nvs_flash.h"
#include "ot_examples_common.h"
#include "esp_hosted.h"
#include "esp_hosted_openthread.h"
#include "esp_hosted_openthread_app.h"
#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE
#include "ot_led_strip.h"
#endif
#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
#include "esp_ot_cli_extension.h"
#endif // CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
#define TAG "ot_esp_cli"
esp_err_t app_init_esp_hosted(void)
{
esp_err_t res = ESP_OK;
res = esp_hosted_init();
if (res != ESP_OK) {
ESP_LOGE(TAG, "esp_hosted_init failed");
return res;
}
res = esp_hosted_connect_to_slave();
if (res != ESP_OK) {
ESP_LOGE(TAG, "esp_hosted_connect_to_slave failed");
return res;
}
return res;
}
void app_main(void)
{
if (ESP_OK != app_init_esp_hosted()) {
ESP_LOGE(TAG, "esp_hosted_util_init failed");
return;
}
if (ESP_OK != esp_hosted_openthread_app_init()) {
ESP_LOGE(TAG, "esp_hosted_openthread_app_init_init failed");
return;
}
// Used eventfds:
// * netif
// * OT task queue
// * radio driver
esp_vfs_eventfd_config_t eventfd_config = {
.max_fds = 3,
};
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
#if CONFIG_OPENTHREAD_PLATFORM_NETIF
ESP_ERROR_CHECK(esp_netif_init());
#endif
ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
#if CONFIG_OPENTHREAD_CLI
ot_console_start();
ot_register_external_commands();
#endif
static esp_openthread_config_t config = {
#if CONFIG_OPENTHREAD_PLATFORM_NETIF
.netif_config = ESP_NETIF_DEFAULT_OPENTHREAD(),
#endif
.platform_config = {
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
},
};
// get radio config from Hosted
esp_hosted_openthread_radio_config_t hosted_radio_config = { 0 };
if (0 != esp_hosted_openthread_get_radio_config(&hosted_radio_config)) {
ESP_LOGE(TAG, "Failed to get OpenThread radio config from Hosted");
return;
}
// use Hosted Radio Config to setup our radio config
#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
if (hosted_radio_config.type != HOSTED_OPENTHREAD_TRANSPORT_UART) {
ESP_LOGE(TAG, "Invalid Hosted Radio Config: expected UART radio config");
return;
}
config.platform_config.radio_config.radio_mode = RADIO_MODE_UART_RCP;
esp_hosted_openthread_uart_config_t *src = &hosted_radio_config.radio_uart_config;
esp_openthread_uart_config_t *dst = &config.platform_config.radio_config.radio_uart_config;
dst->port = src->port;
dst->uart_config.baud_rate = src->baud_rate;
dst->uart_config.data_bits = src->data_bits;
dst->uart_config.parity = src->parity;
dst->uart_config.stop_bits = src->stop_bits;
dst->uart_config.flow_ctrl = src->flow_ctrl;
dst->uart_config.rx_flow_ctrl_thresh = src->rx_flow_ctrl_thresh;
dst->uart_config.source_clk = src->source_clk;
dst->rx_pin = src->rx_pin;
dst->tx_pin = src->tx_pin;
#else
#error "Unsupported Hosted Radio Config"
#endif
ESP_ERROR_CHECK(esp_openthread_start(&config));
#if CONFIG_OPENTHREAD_CLI_ESP_EXTENSION
esp_cli_custom_command_init();
#endif
#if CONFIG_OPENTHREAD_STATE_INDICATOR_ENABLE
ESP_ERROR_CHECK(esp_openthread_state_indicator_init(esp_openthread_get_instance()));
#endif
#if CONFIG_OPENTHREAD_NETWORK_AUTO_START
ot_network_auto_start();
#endif
}
@@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*
* OpenThread Command Line 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.
*/
#pragma once
#include "esp_openthread_types.h"
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = HOST_CONNECTION_MODE_NONE, \
}
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \
{ \
.storage_partition_name = "nvs", \
.netif_queue_size = 10, \
.task_queue_size = 10, \
}
@@ -0,0 +1,18 @@
dependencies:
espressif/esp_hosted:
rules:
- if: target in [esp32p4, esp32h2]
version: '*'
espressif/esp_ot_cli_extension:
version: ~2.0.0
espressif/esp_wifi_remote:
rules:
- if: target in [esp32p4, esp32h2]
- if: idf_version <6.1
version: '>=1.3.1'
idf:
version: '>=4.1.0'
ot_examples_common:
path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_examples_common
ot_led:
path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_led
@@ -0,0 +1,5 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x140000,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 0x140000,
@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
ota_0, app, ota_0, , 1800K,
ota_1, app, ota_1, , 1800K,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 ota_0, app, ota_0, , 1800K,
6 ota_1, app, ota_1, , 1800K,
@@ -0,0 +1,53 @@
####
# Settings copied from https://github.com/espressif/esp-idf/tree/master/examples/openthread/ot_cli
####
#
# Partition Table
#
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y
# end of Partition Table
#
# mbedTLS
#
CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y
CONFIG_MBEDTLS_ECJPAKE_C=y
# end of mbedTLS
#
# OpenThread
#
CONFIG_OPENTHREAD_ENABLED=y
CONFIG_OPENTHREAD_BORDER_ROUTER=n
CONFIG_OPENTHREAD_DNS64_CLIENT=y
CONFIG_OPENTHREAD_TASK_SIZE=10240
CONFIG_OPENTHREAD_CONSOLE_ENABLE=n
# end of OpenThread
#
# lwIP
#
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
CONFIG_LWIP_IPV6_NUM_ADDRESSES=8
CONFIG_LWIP_MULTICAST_PING=y
CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM=y
# end of lwIP
#
# ESP System Settings
#
CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144
# end of ESP System Settings
### SW coexistence on the co-processor is on, so this option must be
### disabled on host to keep OpenThread capabilities consistent
CONFIG_OPENTHREAD_RX_ON_WHEN_IDLE=n
### enable ESP-Hosted OpenThread Support
CONFIG_ESP_HOSTED_HOST_OT_ENABLE=y
@@ -0,0 +1,8 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.esp32p4.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.esp32p4.csv"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
### enable SPIRAM, else application will not start (out of memory)
CONFIG_SPIRAM=y
CONFIG_ESP_HOSTED_DFLT_TASK_FROM_SPIRAM=y