This commit is contained in:
Ken Van Hoeylandt 2026-09-11 00:15:07 +02:00
parent 026532b1f0
commit 15735af476
29 changed files with 282 additions and 304 deletions

View File

@ -32,21 +32,11 @@ sdkconfig.CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
sdkconfig.CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
sdkconfig.CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
sdkconfig.CONFIG_SLAVE_IDF_TARGET_ESP32C6=y
sdkconfig.CONFIG_ESP_HOSTED_ENABLED=y
sdkconfig.CONFIG_ESP_HOSTED_SDIO_HOST_INTERFACE=y
sdkconfig.CONFIG_ESP_HOSTED_SDIO_SLOT_1=y
sdkconfig.CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_CMD_SLOT_1=13
sdkconfig.CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_CLK_SLOT_1=12
sdkconfig.CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D0_SLOT_1=11
sdkconfig.CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D1_4BIT_BUS_SLOT_1=10
sdkconfig.CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D2_4BIT_BUS_SLOT_1=9
sdkconfig.CONFIG_ESP_HOSTED_PRIV_SDIO_PIN_D3_4BIT_BUS_SLOT_1=8
sdkconfig.CONFIG_ESP_HOSTED_SDIO_GPIO_RESET_SLAVE=15
# Fixes recent changes to esp_hosted
sdkconfig.CONFIG_ESP_HOSTED_USE_MEMPOOL=n
sdkconfig.CONFIG_ESP_HOSTED_HOST_TRANSPORT_BUS_SDIO=y
# esp_hosted 3.x ships a native pin/reset-GPIO preset for this exact board.
sdkconfig.CONFIG_ESP32P4_TAB5_C6_BOARD=y
# ESP-NOW bridge (EspNowBackendHosted.cpp) registers 6 custom-RPC handlers (4 resp + 2 event); default cap is 3
sdkconfig.CONFIG_ESP_HOSTED_ENABLE_PEER_DATA_TRANSFER=y
sdkconfig.CONFIG_ESP_HOSTED_MAX_CUSTOM_MSG_HANDLERS=8
sdkconfig.CONFIG_ESP_HOSTED_HOST_FEAT_PEER_DATA_MAX_CUSTOM_MSG_HANDLERS=8
# Performance: larger L2 cache reduces PSRAM stalls for draw/DPI buffers
sdkconfig.CONFIG_CACHE_L2_CACHE_256KB=y
# Performance: cache-line-aligned draw buffers, required for PPA/DMA2D coherency (see lvgl-module's lvgl_display_alloc_buffer())

View File

@ -102,12 +102,10 @@ static int pin_or_unused(const GpioPinSpec& pin) {
return pin.gpio_controller == nullptr ? -1 : static_cast<int>(pin.pin);
}
// RGB666 has no ESP-IDF 6.1 lcd_color_format_t equivalent (LL only implements RGB565/RGB888),
// so 18bpp is rejected in start() below rather than silently mis-driving the panel.
static lcd_color_format_t color_format_from_bits_per_pixel(uint8_t bits_per_pixel) {
switch (bits_per_pixel) {
case 18: return LCD_COLOR_FMT_RGB666;
case 24: return LCD_COLOR_FMT_RGB888;
default: return LCD_COLOR_FMT_RGB565;
}
return bits_per_pixel == 24 ? LCD_COLOR_FMT_RGB888 : LCD_COLOR_FMT_RGB565;
}
// Unpacks the devicetree's flat [cmd, data_len, delay_ms, data_len bytes...] encoding (produced
@ -155,6 +153,10 @@ static bool parse_init_sequence(const uint8_t* bytes, uint32_t length, ili9881c_
static error_t start(Device* device) {
const auto* config = GET_CONFIG(device);
if (config->bits_per_pixel == 18) {
LOG_E(TAG, "18bpp (RGB666) is unsupported on this ESP-IDF version");
return ERROR_NOT_SUPPORTED;
}
auto* internal = static_cast<Ili9881cInternal*>(malloc(sizeof(Ili9881cInternal)));
if (internal == nullptr) {
@ -191,7 +193,7 @@ static error_t start(Device* device) {
.bus_id = config->dsi_bus_id,
.num_data_lanes = config->num_data_lanes,
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
.lane_bit_rate_mbps = config->lane_bit_rate_mbps,
.lane_bit_rate_mbps = static_cast<float>(config->lane_bit_rate_mbps),
};
if (esp_lcd_new_dsi_bus(&bus_config, &internal->dsi_bus_handle) != ESP_OK) {
LOG_E(TAG, "Failed to create MIPI DSI bus");
@ -219,8 +221,7 @@ static error_t start(Device* device) {
const esp_lcd_dpi_panel_config_t dpi_config = {
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = config->dpi_clock_freq_mhz,
.pixel_format = (lcd_color_rgb_pixel_format_t)0, // deprecated field - in/out_color_format below take precedence
.dpi_clock_freq_mhz = static_cast<float>(config->dpi_clock_freq_mhz),
.in_color_format = color_format,
.out_color_format = color_format,
.num_fbs = config->num_fbs,
@ -235,7 +236,6 @@ static error_t start(Device* device) {
.vsync_front_porch = config->vsync_front_porch,
},
.flags = {
.use_dma2d = config->use_dma2d,
.disable_lp = config->disable_lp,
},
};
@ -270,6 +270,10 @@ static error_t start(Device* device) {
return ERROR_RESOURCE;
}
if (config->use_dma2d) {
esp_lcd_dpi_panel_enable_dma2d(internal->panel_handle);
}
// Bring-up sequence: reset() pulses (or software-resets) the panel and init() pushes
// init_cmds over the DBI command interface before bringing up the underlying DPI peripheral.
// swap_xy/set_gap are intentionally not called: the ILI9881C driver doesn't override them and

View File

@ -73,12 +73,10 @@ static int pin_or_unused(const GpioPinSpec& pin) {
return pin.gpio_controller == nullptr ? -1 : static_cast<int>(pin.pin);
}
// RGB666 has no ESP-IDF 6.1 lcd_color_format_t equivalent (LL only implements RGB565/RGB888),
// so 18bpp is rejected in start() below rather than silently mis-driving the panel.
static lcd_color_format_t color_format_from_bits_per_pixel(uint8_t bits_per_pixel) {
switch (bits_per_pixel) {
case 18: return LCD_COLOR_FMT_RGB666;
case 24: return LCD_COLOR_FMT_RGB888;
default: return LCD_COLOR_FMT_RGB565;
}
return bits_per_pixel == 24 ? LCD_COLOR_FMT_RGB888 : LCD_COLOR_FMT_RGB565;
}
// Unpacks the devicetree's flat [cmd, data_len, delay_ms, data_len bytes...] encoding (produced
@ -126,6 +124,10 @@ static bool parse_init_sequence(const uint8_t* bytes, uint32_t length, jd9165_lc
static error_t start(Device* device) {
const auto* config = GET_CONFIG(device);
if (config->bits_per_pixel == 18) {
LOG_E(TAG, "18bpp (RGB666) is unsupported on this ESP-IDF version");
return ERROR_NOT_SUPPORTED;
}
auto* internal = static_cast<Jd9165Internal*>(malloc(sizeof(Jd9165Internal)));
if (internal == nullptr) {
@ -162,7 +164,7 @@ static error_t start(Device* device) {
.bus_id = config->dsi_bus_id,
.num_data_lanes = config->num_data_lanes,
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
.lane_bit_rate_mbps = config->lane_bit_rate_mbps,
.lane_bit_rate_mbps = static_cast<float>(config->lane_bit_rate_mbps),
};
if (esp_lcd_new_dsi_bus(&bus_config, &internal->dsi_bus_handle) != ESP_OK) {
LOG_E(TAG, "Failed to create MIPI DSI bus");
@ -190,8 +192,7 @@ static error_t start(Device* device) {
const esp_lcd_dpi_panel_config_t dpi_config = {
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = config->dpi_clock_freq_mhz,
.pixel_format = (lcd_color_rgb_pixel_format_t)0, // deprecated field - in/out_color_format below take precedence
.dpi_clock_freq_mhz = static_cast<float>(config->dpi_clock_freq_mhz),
.in_color_format = color_format,
.out_color_format = color_format,
.num_fbs = config->num_fbs,
@ -206,7 +207,6 @@ static error_t start(Device* device) {
.vsync_front_porch = config->vsync_front_porch,
},
.flags = {
.use_dma2d = config->use_dma2d,
.disable_lp = config->disable_lp,
},
};
@ -240,6 +240,10 @@ static error_t start(Device* device) {
return ERROR_RESOURCE;
}
if (config->use_dma2d) {
esp_lcd_dpi_panel_enable_dma2d(internal->panel_handle);
}
// Bring-up sequence: reset() pulses (or software-resets) the panel and init() pushes
// init_cmds over the DBI command interface before bringing up the underlying DPI peripheral.
// swap_xy/set_gap are intentionally not called: the JD9165 driver doesn't override them and

View File

@ -102,12 +102,10 @@ static int pin_or_unused(const GpioPinSpec& pin) {
return pin.gpio_controller == nullptr ? -1 : static_cast<int>(pin.pin);
}
// RGB666 has no ESP-IDF 6.1 lcd_color_format_t equivalent (LL only implements RGB565/RGB888),
// so 18bpp is rejected in start() below rather than silently mis-driving the panel.
static lcd_color_format_t color_format_from_bits_per_pixel(uint8_t bits_per_pixel) {
switch (bits_per_pixel) {
case 18: return LCD_COLOR_FMT_RGB666;
case 24: return LCD_COLOR_FMT_RGB888;
default: return LCD_COLOR_FMT_RGB565;
}
return bits_per_pixel == 24 ? LCD_COLOR_FMT_RGB888 : LCD_COLOR_FMT_RGB565;
}
// Unpacks the devicetree's flat [cmd, data_len, delay_ms, data_len bytes...] encoding (produced
@ -155,6 +153,10 @@ static bool parse_init_sequence(const uint8_t* bytes, uint32_t length, st7121_lc
static error_t start(Device* device) {
const auto* config = GET_CONFIG(device);
if (config->bits_per_pixel == 18) {
LOG_E(TAG, "18bpp (RGB666) is unsupported on this ESP-IDF version");
return ERROR_NOT_SUPPORTED;
}
auto* internal = static_cast<St7121Internal*>(malloc(sizeof(St7121Internal)));
if (internal == nullptr) {
@ -191,7 +193,7 @@ static error_t start(Device* device) {
.bus_id = config->dsi_bus_id,
.num_data_lanes = config->num_data_lanes,
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
.lane_bit_rate_mbps = config->lane_bit_rate_mbps,
.lane_bit_rate_mbps = static_cast<float>(config->lane_bit_rate_mbps),
};
if (esp_lcd_new_dsi_bus(&bus_config, &internal->dsi_bus_handle) != ESP_OK) {
LOG_E(TAG, "Failed to create MIPI DSI bus");
@ -219,8 +221,7 @@ static error_t start(Device* device) {
const esp_lcd_dpi_panel_config_t dpi_config = {
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = config->dpi_clock_freq_mhz,
.pixel_format = (lcd_color_rgb_pixel_format_t)0, // deprecated field - in/out_color_format below take precedence
.dpi_clock_freq_mhz = static_cast<float>(config->dpi_clock_freq_mhz),
.in_color_format = color_format,
.out_color_format = color_format,
.num_fbs = config->num_fbs,
@ -235,7 +236,6 @@ static error_t start(Device* device) {
.vsync_front_porch = config->vsync_front_porch,
},
.flags = {
.use_dma2d = config->use_dma2d,
.disable_lp = config->disable_lp,
},
};
@ -269,6 +269,10 @@ static error_t start(Device* device) {
return ERROR_RESOURCE;
}
if (config->use_dma2d) {
esp_lcd_dpi_panel_enable_dma2d(internal->panel_handle);
}
// Bring-up sequence: reset() pulses (or software-resets) the panel and init() pushes
// init_cmds over the DBI command interface before bringing up the underlying DPI peripheral.
// swap_xy/set_gap are intentionally not called: the ST7121 driver doesn't override them and

View File

@ -102,12 +102,10 @@ static int pin_or_unused(const GpioPinSpec& pin) {
return pin.gpio_controller == nullptr ? -1 : static_cast<int>(pin.pin);
}
// RGB666 has no ESP-IDF 6.1 lcd_color_format_t equivalent (LL only implements RGB565/RGB888),
// so 18bpp is rejected in start() below rather than silently mis-driving the panel.
static lcd_color_format_t color_format_from_bits_per_pixel(uint8_t bits_per_pixel) {
switch (bits_per_pixel) {
case 18: return LCD_COLOR_FMT_RGB666;
case 24: return LCD_COLOR_FMT_RGB888;
default: return LCD_COLOR_FMT_RGB565;
}
return bits_per_pixel == 24 ? LCD_COLOR_FMT_RGB888 : LCD_COLOR_FMT_RGB565;
}
// Unpacks the devicetree's flat [cmd, data_len, delay_ms, data_len bytes...] encoding (produced
@ -155,6 +153,10 @@ static bool parse_init_sequence(const uint8_t* bytes, uint32_t length, st7123_lc
static error_t start(Device* device) {
const auto* config = GET_CONFIG(device);
if (config->bits_per_pixel == 18) {
LOG_E(TAG, "18bpp (RGB666) is unsupported on this ESP-IDF version");
return ERROR_NOT_SUPPORTED;
}
auto* internal = static_cast<St7123Internal*>(malloc(sizeof(St7123Internal)));
if (internal == nullptr) {
@ -191,7 +193,7 @@ static error_t start(Device* device) {
.bus_id = config->dsi_bus_id,
.num_data_lanes = config->num_data_lanes,
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
.lane_bit_rate_mbps = config->lane_bit_rate_mbps,
.lane_bit_rate_mbps = static_cast<float>(config->lane_bit_rate_mbps),
};
if (esp_lcd_new_dsi_bus(&bus_config, &internal->dsi_bus_handle) != ESP_OK) {
LOG_E(TAG, "Failed to create MIPI DSI bus");
@ -219,8 +221,7 @@ static error_t start(Device* device) {
const esp_lcd_dpi_panel_config_t dpi_config = {
.virtual_channel = 0,
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = config->dpi_clock_freq_mhz,
.pixel_format = (lcd_color_rgb_pixel_format_t)0, // deprecated field - in/out_color_format below take precedence
.dpi_clock_freq_mhz = static_cast<float>(config->dpi_clock_freq_mhz),
.in_color_format = color_format,
.out_color_format = color_format,
.num_fbs = config->num_fbs,
@ -235,7 +236,6 @@ static error_t start(Device* device) {
.vsync_front_porch = config->vsync_front_porch,
},
.flags = {
.use_dma2d = config->use_dma2d,
.disable_lp = config->disable_lp,
},
};
@ -269,6 +269,10 @@ static error_t start(Device* device) {
return ERROR_RESOURCE;
}
if (config->use_dma2d) {
esp_lcd_dpi_panel_enable_dma2d(internal->panel_handle);
}
// Bring-up sequence: reset() pulses (or software-resets) the panel and init() pushes
// init_cmds over the DBI command interface before bringing up the underlying DPI peripheral.
// swap_xy/set_gap are intentionally not called: the ST7123 driver doesn't override them and

View File

@ -12,6 +12,14 @@
extern "C" {
#if CONFIG_IDF_TARGET_ESP32P4
// Newlib doesn't provide this (only Picolibc does); a compat shim is force-linked into the
// firmware for espressif__esp_ipa's sake - see
// Platforms/platform-esp32/source/drivers/esp32p4_libc_compat.c. Side-loaded apps built against
// Newlib need it resolved the same way.
extern int __issignalingf(float);
#endif
static const ModuleSymbol SYMBOLS[] = {
// stdlib.h
DEFINE_MODULE_SYMBOL(malloc),
@ -61,6 +69,9 @@ static const ModuleSymbol SYMBOLS[] = {
DEFINE_MODULE_SYMBOL(fmaxf),
DEFINE_MODULE_SYMBOL(fminf),
DEFINE_MODULE_SYMBOL(roundf),
#if CONFIG_IDF_TARGET_ESP32P4
DEFINE_MODULE_SYMBOL(__issignalingf),
#endif
// Explicit signatures: libstdc++/libc++'s <cmath> float/double/long double overloads make a
// bare `&acos` etc. ambiguous. esp-idf newlib's plain, unoverloaded functions match these signatures
// exactly, so the cast is a no-op there - one list works for all platforms:

View File

@ -44,8 +44,32 @@ Non-template helpers shared by every `std::map`/`std::set` instantiation:
### `std::string`
- `basic_string::_M_replace_cold` - the rarely-taken slow path of `std::string::replace`,
split out of the header-inlined fast path.
Most of the common out-of-line member set (this class ends up entirely out-of-line for any app
built at C++17 or earlier - see `extern template class basic_string<char>` in libstdc++'s
`bits/basic_string.tcc`):
- `_M_replace_cold`, `_M_replace`, `_M_mutate`, `_M_append`, `_M_erase`, `_M_assign`,
`_M_create`, `_M_dispose`, `_M_construct<const char*>`
- `substr`, `find`, `reserve`, `append` (both overloads), `assign`, `push_back`, `pop_back`,
`operator=(basic_string&&)`
- `_S_copy`, `_S_move` (static helpers)
- Free functions returning `basic_string<char>` by value: `__str_concat`, `operator+(basic_string
const&, const char*)`
- `(basic_string const&, pos, len)` and `(const char*, allocator const&)` constructors -
implemented directly rather than exported by address (see below), since GCC doesn't emit a
standalone definition for either.
**Not supported: the move constructor.** Like the two constructors above, GCC never emits a
standalone out-of-line definition for it in a C++20+ build - it's a pure header-inline forwarder
with no real function body to take the address of, even when this module's own code is made to
call it directly. If it turns out to be needed, implement it the same way as the other two: write
a small function whose body does the equivalent construction via placement-new (`new (self)
std::string(...)`), and register its address manually under the mangled name(s) in `SYMBOLS[]`
(the compiler inlines the real header logic into that function, producing a genuine addressable
symbol - the same trick used for `_M_replace_cold` and friends, except *we* supply the body
instead of pointing at one the library already provides). An app whose loaded ELF needs it will
still fail with `Can't find common ...C1.../...C2.../...C5...`.
## Adding a new symbol

View File

@ -5,6 +5,7 @@
#include <cstdint>
#include <new>
#include <string>
#include <utility>
#if defined(__GLIBCXX__) || defined(ESP_PLATFORM)
#define TT_CPP_SYMBOLS_AVAILABLE 1
@ -42,8 +43,54 @@ extern "C" {
#ifdef ESP_PLATFORM
// string - same 32-bit-ABI mangling caveat as operator new/delete above.
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE15_M_replace_coldEPcjPKcjj(void*, char*, unsigned int, char const*, unsigned int, unsigned int);
void* _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6substrEjj(void*, const void*, unsigned int, unsigned int);
char* _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERjj(void*, unsigned int*, unsigned int);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7reserveEj(void*, unsigned int);
unsigned int _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4findEcj(const void*, char, unsigned int);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_disposeEv(void*);
void* _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEjjPKcj(void*, unsigned int, unsigned int, const char*, unsigned int);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag(void*, const char*, const char*, char);
void* _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKc(void*, const char*);
void* _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKcj(void*, const char*, unsigned int);
void* _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6assignEPKc(void*, const char*);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7_S_copyEPcPKcj(char*, const char*, unsigned int);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7_S_moveEPcPKcj(char*, const char*, unsigned int);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE8_M_eraseEjj(void*, unsigned int, unsigned int);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE8pop_backEv(void*);
void* _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcj(void*, const char*, unsigned int);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_assignERKS4_(void*, const void*);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_mutateEjjPKcj(void*, unsigned int, unsigned int, const char*, unsigned int);
void _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9push_backEc(void*, char);
void* _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEaSEOS4_(void*, void*);
// Non-members: return basic_string<char> by value, so the first param is the hidden
// return-value pointer (Itanium ABI), same convention as substr() above.
void* _ZSt12__str_concatINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEET_PKNS6_10value_typeENS6_9size_typeES9_SA_RKNS6_14allocator_typeE(void*, const char*, unsigned int, const char*, unsigned int, const void*);
void* _ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EERKS8_PKS5_(void*, const void*, const char*);
#endif
}
#ifdef ESP_PLATFORM
namespace {
// basic_string(basic_string const&, pos, len) has no out-of-line definition anywhere to take the
// address of - unlike everything else above, GCC never emits one for this ctor under C++20+ (it's
// a pure header-inline forwarder around _M_construct(), confirmed by trying to force an
// instantiation and finding no resulting linkable symbol). Implemented directly instead: this
// function's placement-new triggers the compiler to inline the real construction logic here,
// producing a genuine addressable definition, registered below under the mangled name(s) the ELF
// loader actually looks up.
void construct_basic_string_from_substring(void* self, const void* str, unsigned int pos, unsigned int len) {
new (self) std::string(*static_cast<const std::string*>(str), pos, len);
}
// Same story for basic_string(const char*, allocator<char> const&).
void construct_basic_string_from_cstr(void* self, const char* s, const void* alloc) {
new (self) std::string(s, *static_cast<const std::allocator<char>*>(alloc));
}
// Same story for the move constructor, basic_string(basic_string&&).
void construct_basic_string_move(void* self, void* other) {
new (self) std::string(std::move(*static_cast<std::string*>(other)));
}
}
#endif
#endif
static const ModuleSymbol SYMBOLS[] = {
@ -76,6 +123,37 @@ static const ModuleSymbol SYMBOLS[] = {
#ifdef ESP_PLATFORM
// string - Note: You have to use the mangled names here
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE15_M_replace_coldEPcjPKcjj),
DEFINE_MODULE_SYMBOL(_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6substrEjj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERjj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7reserveEj),
DEFINE_MODULE_SYMBOL(_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4findEcj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_disposeEv),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_replaceEjjPKcj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKc),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKcj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6assignEPKc),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7_S_copyEPcPKcj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7_S_moveEPcPKcj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE8_M_eraseEjj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE8pop_backEv),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_assignERKS4_),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_mutateEjjPKcj),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9push_backEc),
DEFINE_MODULE_SYMBOL(_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEaSEOS4_),
// C1/C2/C5: complete-object, base-object, and comdat-folded aliases of the same constructor.
{ "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_jj", (void*)&construct_basic_string_from_substring },
{ "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2ERKS4_jj", (void*)&construct_basic_string_from_substring },
{ "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC5ERKS4_jj", (void*)&construct_basic_string_from_substring },
{ "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1IS3_EEPKcRKS3_", (void*)&construct_basic_string_from_cstr },
{ "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2IS3_EEPKcRKS3_", (void*)&construct_basic_string_from_cstr },
{ "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC5IS3_EEPKcRKS3_", (void*)&construct_basic_string_from_cstr },
{ "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EOS4_", (void*)&construct_basic_string_move },
{ "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2EOS4_", (void*)&construct_basic_string_move },
{ "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC5EOS4_", (void*)&construct_basic_string_move },
DEFINE_MODULE_SYMBOL(_ZSt12__str_concatINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEET_PKNS6_10value_typeENS6_9size_typeES9_SA_RKNS6_14allocator_typeE),
DEFINE_MODULE_SYMBOL(_ZStplIcSt11char_traitsIcESaIcEENSt7__cxx1112basic_stringIT_T0_T1_EERKS8_PKS5_),
#endif
#endif // TT_CPP_SYMBOLS_AVAILABLE
MODULE_SYMBOL_TERMINATOR

View File

@ -22,10 +22,17 @@ idf_component_register(
if (DEFINED ENV{ESP_IDF_VERSION})
GET_PROPERTY_FILE_CONTENT("${CMAKE_CURRENT_LIST_DIR}/../../sdkconfig" sdkconfig_text)
GET_PROPERTY_VALUE(sdkconfig_text "CONFIG_ESP_HOSTED_ENABLED" esp_host_enabled "n")
GET_PROPERTY_VALUE(sdkconfig_text "CONFIG_ESP_HOSTED_HOST" esp_host_enabled "n")
if (esp_host_enabled STREQUAL "y")
idf_component_optional_requires(PRIVATE espressif__esp_hosted)
endif ()
endif ()
idf_component_optional_requires(PRIVATE bt usb espressif__usb_host_hid espressif__usb_host_msc)
if ("${IDF_TARGET}" STREQUAL "esp32p4")
# Force-links esp32p4_libc_compat.c's __issignalingf() shim - nothing references it until
# espressif__esp_ipa is scanned much later in link order, by which point this archive is
# already passed over.
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,-u,__issignalingf")
endif ()

View File

@ -3,5 +3,5 @@
struct FirmwareOps;
/** @return the esp_hosted co-processor FirmwareOps implementation. Only declared/linked when
* CONFIG_ESP_HOSTED_ENABLED - callers (esp32_wifi.cpp) must guard with the same #if. */
* CONFIG_ESP_HOSTED_HOST - callers (esp32_wifi.cpp) must guard with the same #if. */
const FirmwareOps* esp32_esp_hosted_ota_get_ops();

View File

@ -172,18 +172,5 @@ extern size_t active_hid_rpt_map_len; // esp32_ble_hid.cpp
extern uint16_t hid_appearance; // esp32_ble_hid.cpp
extern BleHidProfile current_hid_profile; // esp32_ble_hid.cpp
// ---- HCI gate (ble_hci_gate.c) — P4/esp-hosted only ----
// Controls whether hci_rx_handler forwards packets to NimBLE.
// Set true after nimble_port_init(), false before nimble_port_stop().
#if defined(CONFIG_ESP_HOSTED_ENABLED)
#ifdef __cplusplus
extern "C" {
#endif
void ble_hci_gate_set_active(bool active);
bool ble_hci_gate_wait_idle(int max_ms);
#ifdef __cplusplus
}
#endif
#endif // CONFIG_ESP_HOSTED_ENABLED
#endif // CONFIG_BT_NIMBLE_ENABLED

View File

@ -1,142 +0,0 @@
// ble_hci_gate.c — Strong override of the weak hci_rx_handler from vhci_drv.c.
// Gates HCI packet delivery so packets are silently dropped when NimBLE is not
// initialised, preventing null-npl_funcs crashes during radio teardown (P4 only).
//
// On ESP32-P4, sdio_process_rx_task runs independently of the NimBLE host and can
// deliver an HCI packet after nimble_port_deinit() has zeroed npl_funcs.
// dispatch_disable() calls ble_hci_gate_set_active(false) + ble_hci_gate_wait_idle()
// before touching NimBLE, ensuring no in-flight or future hci_rx_handler call can
// dereference NimBLE internals.
#include <sdkconfig.h>
#if defined(CONFIG_BT_NIMBLE_ENABLED) && defined(CONFIG_ESP_HOSTED_ENABLED)
#include <stdbool.h>
#include <stdatomic.h>
#include <string.h>
#include <esp_log.h>
#include <esp_err.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
// NimBLE transport + HCI
#include <nimble/transport.h>
#include <nimble/hci_common.h>
#include <os/os_mbuf.h>
// H4 packet type indicators (same values used in vhci_drv.c)
#define HCI_H4_EVT 0x04
#define HCI_H4_ACL 0x02
// Local defines from vhci_drv.c (not exported by any header)
#define BLE_HCI_EVENT_HDR_LEN (2)
static const char* TAG = "ble_hci_gate";
// ---- Gate state (zero-init = gate closed until dispatch_enable opens it) ----
static atomic_bool s_hci_active = ATOMIC_VAR_INIT(false);
static atomic_int s_hci_refcount = ATOMIC_VAR_INIT(0);
void ble_hci_gate_set_active(bool active) {
atomic_store_explicit(&s_hci_active, active, memory_order_seq_cst);
}
// Spin-wait until all in-flight hci_rx_handler calls complete (max_ms timeout).
// Returns true if drained within the timeout.
bool ble_hci_gate_wait_idle(int max_ms) {
for (int elapsed = 0; elapsed < max_ms; elapsed++) {
if (atomic_load_explicit(&s_hci_refcount, memory_order_acquire) == 0)
return true;
vTaskDelay(pdMS_TO_TICKS(1));
}
return (atomic_load_explicit(&s_hci_refcount, memory_order_acquire) == 0);
}
// Strong override — replaces the H_WEAK_REF hci_rx_handler in vhci_drv.c.
// All four esp-hosted transport drivers (SDIO, SPI, SPI-HD, UART) call this symbol.
int hci_rx_handler(uint8_t *buf, size_t buf_len) {
// Fast path: gate closed — NimBLE not ready, drop silently
if (!atomic_load_explicit(&s_hci_active, memory_order_acquire))
return ESP_OK;
// Hold a reference so dispatch_disable() waits for us to finish
atomic_fetch_add_explicit(&s_hci_refcount, 1, memory_order_acquire);
// Double-check: gate may have closed between the first check and the increment
if (!atomic_load_explicit(&s_hci_active, memory_order_acquire)) {
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_OK;
}
// ---- Original vhci_drv.c (NimBLE branch) logic ----
uint8_t *data = buf;
uint32_t len_total_read = buf_len;
int rc;
if (data[0] == HCI_H4_EVT) {
uint8_t *evbuf;
int totlen = BLE_HCI_EVENT_HDR_LEN + data[2];
if (totlen > UINT8_MAX + BLE_HCI_EVENT_HDR_LEN) {
ESP_LOGE(TAG, "Rx: len[%d] > max INT, drop", totlen);
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_FAIL;
}
if (totlen > MYNEWT_VAL(BLE_TRANSPORT_EVT_SIZE)) {
ESP_LOGE(TAG, "Rx: len[%d] > max BLE, drop", totlen);
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_FAIL;
}
if (data[1] == BLE_HCI_EVCODE_HW_ERROR) {
ESP_LOGE(TAG, "Rx: HW_ERROR");
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_FAIL;
}
if ((data[1] == BLE_HCI_EVCODE_LE_META) &&
(data[3] == BLE_HCI_LE_SUBEV_ADV_RPT ||
data[3] == BLE_HCI_LE_SUBEV_EXT_ADV_RPT)) {
evbuf = ble_transport_alloc_evt(1);
if (!evbuf) {
ESP_LOGW(TAG, "Rx: Drop ADV Report: OOM (not fatal)");
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_FAIL;
}
} else {
evbuf = ble_transport_alloc_evt(0);
if (!evbuf) {
ESP_LOGE(TAG, "Rx: transport_alloc_evt(0) failed");
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_FAIL;
}
}
memset(evbuf, 0, sizeof *evbuf);
memcpy(evbuf, &data[1], totlen);
rc = ble_transport_to_hs_evt(evbuf);
if (rc) {
ESP_LOGE(TAG, "Rx: transport_to_hs_evt failed");
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_FAIL;
}
} else if (data[0] == HCI_H4_ACL) {
struct os_mbuf *m = ble_transport_alloc_acl_from_ll();
if (!m) {
ESP_LOGE(TAG, "Rx: alloc_acl_from_ll failed");
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_FAIL;
}
if ((rc = os_mbuf_append(m, &data[1], len_total_read - 1)) != 0) {
ESP_LOGE(TAG, "Rx: os_mbuf_append failed; rc=%d", rc);
os_mbuf_free_chain(m);
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_FAIL;
}
ble_transport_to_hs_acl(m);
}
atomic_fetch_sub_explicit(&s_hci_refcount, 1, memory_order_release);
return ESP_OK;
}
#endif // CONFIG_BT_NIMBLE_ENABLED && CONFIG_ESP_HOSTED_ENABLED

View File

@ -29,14 +29,9 @@ constexpr auto* TAG = "esp32_ble";
#include <tactility/log.h>
#include <esp_timer.h>
#if defined(CONFIG_ESP_HOSTED_ENABLED)
#if defined(CONFIG_ESP_HOSTED_HOST)
#include <esp_hosted.h>
// esp_hosted_misc.h lacks its own extern "C" guard, so its declarations get C++
// name-mangled when included from a .cpp file, causing "undefined reference" at
// link time against the library's plain-C symbols.
extern "C" {
#include <esp_hosted_misc.h>
}
#include <esp_hosted_bt_host_stack.h>
#endif
// ble_store_config_init() is not declared in the public header in some IDF versions.
@ -684,28 +679,16 @@ static void dispatch_enable(BleCtx* ctx) {
ble_publish_event(ctx->device, e);
}
#if defined(CONFIG_ESP_HOSTED_ENABLED)
// Over esp_hosted (SDIO to a co-processor, e.g. C6), the slave's on-chip BT
// controller is never auto-started at slave boot — it only comes up in
// response to these RPCs. Without them, HCI Reset (the first command NimBLE
// ever sends) is handed to esp_vhci_host_send_packet() on the slave with no
// controller underneath to consume it, so it's silently dropped and NimBLE
// times out (BLE_HS_ETIMEOUT_HCI) forever. Must run before nimble_port_init().
// esp_hosted_bt_controller_init/enable() both bail out immediately (no retry,
// no blocking) if the SDIO/SPI transport to the co-processor isn't marked up
// yet. On boot, BT can auto-enable before Wi-Fi has driven that bring-up, so
// explicitly (re)connect here and let it block until the slave handshake
// (the "Attempt connection with slave" / "Card init success" sequence)
// completes — esp_hosted_connect_to_slave() is a thin wrapper that's safe to
// call again if the transport is already up.
#if defined(CONFIG_ESP_HOSTED_HOST)
// Over esp_hosted, the co-processor's BT controller only comes up via
// esp_hosted_bt_host_stack_setup() below, which needs the SDIO/SPI transport already up -
// BT can auto-enable before Wi-Fi has driven that bring-up, so (re)connect here first.
if (esp_hosted_connect_to_slave() != ESP_OK) {
LOG_W(TAG, "esp_hosted_connect_to_slave failed");
}
if (esp_hosted_bt_controller_init() != ESP_OK) {
LOG_W(TAG, "esp_hosted_bt_controller_init failed");
}
if (esp_hosted_bt_controller_enable() != ESP_OK) {
LOG_W(TAG, "esp_hosted_bt_controller_enable failed");
esp_hosted_bt_host_stack_cfg_t hosted_bt_cfg = ESP_HOSTED_BT_HOST_STACK_CONFIG_DEFAULT();
if (esp_hosted_bt_host_stack_setup(&hosted_bt_cfg) != ESP_OK) {
LOG_W(TAG, "esp_hosted_bt_host_stack_setup failed");
}
#endif
@ -776,9 +759,6 @@ static void dispatch_enable(BleCtx* ctx) {
ble_svc_gap_device_name_set(ctx->device_name);
ble_att_set_preferred_mtu(BLE_ATT_MTU_MAX);
#if defined(CONFIG_ESP_HOSTED_ENABLED)
ble_hci_gate_set_active(true); // Open gate: NimBLE transport pool is ready
#endif
// Drain any stale "done" signal left over from a previous cycle (e.g. if the
// last dispatch_disable() timed out waiting on it — see xSemaphoreTake below
// with pdMS_TO_TICKS(2000)) so dispatch_disable() only ever consumes the
@ -809,9 +789,9 @@ static void dispatch_disable(BleCtx* ctx) {
// Blocking: waits for nimble_port_run() to exit.
// Do NOT call ble_gap_adv_stop()/disc_cancel() before — if controller is
// unresponsive they generate more HCI timeouts before the stop takes effect.
// The HCI gate must stay OPEN here: nimble_port_stop() → ble_hs_stop() sends
// The HCI link must stay bound here: nimble_port_stop() → ble_hs_stop() sends
// HCI commands and needs to receive the command-complete events back from the
// controller. Closing the gate before this point starves NimBLE of those events,
// controller. Unbinding before this point starves NimBLE of those events,
// causing HCI timeouts and a cascade of resets that crash in ble_hs_timer_sched.
nimble_port_stop();
// nimble_port_stop()'s internal semaphore only confirms the stop sentinel event was
@ -826,27 +806,17 @@ static void dispatch_disable(BleCtx* ctx) {
LOG_W(TAG, "host task did not signal completion in time");
}
}
#if defined(CONFIG_ESP_HOSTED_ENABLED)
// Close the gate NOW — after nimble_port_stop() returns the NimBLE host task has
// exited and nimble_port_deinit() is about to zero npl_funcs. Any HCI packet
// arriving after this point must not reach ble_transport_alloc_evt().
ble_hci_gate_set_active(false);
if (!ble_hci_gate_wait_idle(20)) {
LOG_W(TAG, "HCI gate drain timed out");
#if defined(CONFIG_ESP_HOSTED_HOST)
// Unbind before nimble_port_deinit() zeros npl_funcs. Unlike the old ble_hci_gate.c this
// repo carried for esp_hosted 2.x, esp_hosted 3.x exposes no drain/wait-for-in-flight
// primitive here, so a packet already inside the RX callback at this exact moment is a
// small theoretical race this migration cannot close without patching esp_hosted itself.
if (esp_hosted_bt_host_stack_teardown() != ESP_OK) {
LOG_W(TAG, "esp_hosted_bt_host_stack_teardown failed");
}
#endif
nimble_port_deinit();
#if defined(CONFIG_ESP_HOSTED_ENABLED)
// Symmetric with the enable-side esp_hosted_bt_controller_init/enable() calls.
if (esp_hosted_bt_controller_disable() != ESP_OK) {
LOG_W(TAG, "esp_hosted_bt_controller_disable failed");
}
if (esp_hosted_bt_controller_deinit(true) != ESP_OK) {
LOG_W(TAG, "esp_hosted_bt_controller_deinit failed");
}
#endif
ctx->spp_conn_handle.store(BLE_HS_CONN_HANDLE_NONE);
ctx->spp_active.store(false);
ctx->midi_conn_handle.store(BLE_HS_CONN_HANDLE_NONE);

View File

@ -2,7 +2,7 @@
#include <sdkconfig.h>
#endif
#if defined(CONFIG_ESP_HOSTED_ENABLED)
#if defined(CONFIG_ESP_HOSTED_HOST)
#include <tactility/drivers/esp32_esp_hosted_ota.h>
#include <tactility/drivers/wifi.h>
@ -251,4 +251,4 @@ const FirmwareOps* esp32_esp_hosted_ota_get_ops() {
return &firmwareOps;
}
#endif // CONFIG_ESP_HOSTED_ENABLED
#endif // CONFIG_ESP_HOSTED_HOST

View File

@ -20,7 +20,7 @@
#include <TactilityCpp/Allocator.h>
#if defined(CONFIG_ESP_HOSTED_ENABLED)
#if defined(CONFIG_ESP_HOSTED_HOST)
#include <tactility/drivers/esp32_esp_hosted_ota.h>
#endif
@ -583,7 +583,7 @@ error_t api_get_firmware_ops(Device* /*device*/, const FirmwareOps** ops, void**
if (ops == nullptr || ctx == nullptr) {
return ERROR_INVALID_ARGUMENT;
}
#if defined(CONFIG_ESP_HOSTED_ENABLED)
#if defined(CONFIG_ESP_HOSTED_HOST)
// Only meaningful on a hosted board (P4+C6/C5 etc.) - this wifi device is backed by a real
// co-processor with its own updatable firmware there. On a native (non-hosted) chip, this
// device's "radio" is the chip's own built-in WiFi, nothing to update via this interface.

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// Tactility forces Newlib (see device.py) instead of ESP-IDF 6.x's Picolibc default, but
// managed_components/espressif__esp_ipa's prebuilt libesp_ipa.a (camera ISP auto-white-balance,
// linked in for boards with a camera sensor node, e.g. m5stack-tab5) was built against Picolibc
// and references its __issignalingf(), which Newlib doesn't provide.
#include <sdkconfig.h>
#if CONFIG_IDF_TARGET_ESP32P4
#include <stdint.h>
int __issignalingf(float x) {
union { float f; uint32_t i; } u = { .f = x };
if (((u.i >> 23) & 0xff) != 0xff) {
return 0; // not NaN or Inf
}
// NaN with mantissa MSB clear is signaling; MSB set is quiet.
return (u.i & 0x007fffff) != 0 && (u.i & 0x00400000) == 0;
}
#endif // CONFIG_IDF_TARGET_ESP32P4

View File

@ -2,7 +2,7 @@
#include <sdkconfig.h>
#endif
#if defined(CONFIG_ESP_HOSTED_ENABLED)
#if defined(CONFIG_ESP_HOSTED_HOST)
#include <Tactility/service/espnow/EspNowBackend.h>
#include <Tactility/service/espnow/EspNowHostedTransport.h>
@ -310,4 +310,4 @@ uint32_t getVersion() {
}
#endif // CONFIG_ESP_HOSTED_ENABLED
#endif // CONFIG_ESP_HOSTED_HOST

View File

@ -2,7 +2,7 @@
#include <sdkconfig.h>
#endif
#if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_ESP_HOSTED_ENABLED)
#if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_ESP_HOSTED_HOST)
#include <Tactility/service/espnow/EspNowBackend.h>
#include <Tactility/service/wifi/Wifi.h>
@ -205,4 +205,4 @@ uint32_t getVersion() {
}
#endif // CONFIG_SOC_WIFI_SUPPORTED && !CONFIG_ESP_HOSTED_ENABLED
#endif // CONFIG_SOC_WIFI_SUPPORTED && !CONFIG_ESP_HOSTED_HOST

View File

@ -6,11 +6,11 @@ dependencies:
rules:
- if: "target in [esp32s3, esp32c6, esp32p4]"
espressif/esp_hosted:
version: "2.12.11"
version: "3.0.7"
rules:
- if: "target == esp32p4"
espressif/esp_wifi_remote:
version: "1.2.3"
version: "1.6.4"
rules:
- if: "target == esp32p4"
espressif/esp_lcd_ili9341:

View File

@ -118,6 +118,12 @@ def write_partition_table(output_file, device_properties: dict, is_dev: bool):
output_file.write("CONFIG_PARTITION_TABLE_CUSTOM=y\n")
output_file.write(f"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"{partition_filename}\"\n")
output_file.write(f"CONFIG_PARTITION_TABLE_FILENAME=\"{partition_filename}\"\n")
idf_target = get_property_or_exit(device_properties, "hardware.target").lower()
if idf_target == "esp32p4":
# ESP-IDF 6.1's P4 bootloader (0x6140 bytes) no longer fits under the default
# CONFIG_PARTITION_TABLE_OFFSET=0x8000 budget (max 0x6000), regardless of device config.
# The partition CSVs leave their offsets blank so they auto-place relative to this value.
output_file.write("CONFIG_PARTITION_TABLE_OFFSET=0x10000\n")
def write_tactility_variables(output_file, device_properties: dict, device_id: str):
# Board and vendor
@ -176,6 +182,14 @@ def write_core_variables(output_file, device_properties: dict):
output_file.write("CONFIG_ESP_WIFI_RX_IRAM_OPT=n\n")
output_file.write("CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH=y\n")
output_file.write("CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y\n")
if idf_target == "esp32p4":
# EspNowBackendHosted.cpp's esp_hosted_send_custom_data()/esp_hosted_register_custom_callback()
# need esp_hosted's peer-data feature enabled to exist at all.
output_file.write("CONFIG_ESP_HOSTED_HOST_FEAT_PEER_DATA=y\n")
# ESP-IDF 6.1 defaults to requiring chip rev >=v3.1; boards in this repo (e.g. m5stack-tab5)
# ship v1.0 silicon, which that bootloader refuses to boot.
output_file.write("CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y\n")
output_file.write("CONFIG_ESP32P4_REV_MIN_100=y\n")
def write_flash_variables(output_file, device_properties: dict):
flash_size = get_property_or_exit(device_properties, "hardware.flashSize")
if not flash_size.endswith("MB"):
@ -411,7 +425,10 @@ def write_bluetooth_variables(output_file, device_properties: dict):
output_file.write("CONFIG_BT_NIMBLE_ENABLED=y\n")
if idf_target == "esp32p4":
output_file.write("CONFIG_BT_NIMBLE_TRANSPORT_UART=n\n")
output_file.write("CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y\n")
# esp_hosted 3.0 removed the legacy ESP_HOSTED_ENABLE_BT_NIMBLE knob in favor
# of a stack-agnostic feature flag; the app binds NimBLE to it explicitly via
# esp_hosted_bt_host_stack_setup() (see esp32_ble.cpp).
output_file.write("CONFIG_ESP_HOSTED_HOST_FEAT_BT=y\n")
# Move NimBLE host buffers to SPIRAM when available, regardless of target.
# The default (INTERNAL) mode causes heap fragmentation after a disable+deinit
# cycle, preventing a subsequent nimble_port_init() from allocating its buffers

View File

@ -1,7 +1,7 @@
# 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, 4M,
# Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 4M,
system, data, fat, , 128k,
data, data, fat, , 1000k,

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 # Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, , 0x1000,
5 factory, app, factory, 0x10000, 4M, factory, app, factory, , 4M,
6 system, data, fat, , 128k,
7 data, data, fat, , 1000k,

View File

@ -1,7 +1,7 @@
# 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, 4M,
# Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 4M,
system, data, fat, , 128k,
data, data, fat, , 11800k,

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 # Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, , 0x1000,
5 factory, app, factory, 0x10000, 4M, factory, app, factory, , 4M,
6 system, data, fat, , 128k,
7 data, data, fat, , 11800k,

View File

@ -1,6 +1,6 @@
# 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, 4M,
# Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 4M,
system, data, fat, , 128k,

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 # Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, , 0x1000,
5 factory, app, factory, 0x10000, 4M, factory, app, factory, , 4M,
6 system, data, fat, , 128k,

View File

@ -1,7 +1,7 @@
# 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, 4M,
# Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 4M,
system, data, fat, , 128k,
data, data, fat, , 1000k,

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 # Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, , 0x1000,
5 factory, app, factory, 0x10000, 4M, factory, app, factory, , 4M,
6 system, data, fat, , 128k,
7 data, data, fat, , 1000k,

View File

@ -1,7 +1,7 @@
# 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, 4M,
# Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 4M,
system, data, fat, , 128k,
data, data, fat, , 28480k,

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 # Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, , 0x1000,
5 factory, app, factory, 0x10000, 4M, factory, app, factory, , 4M,
6 system, data, fat, , 128k,
7 data, data, fat, , 28480k,

View File

@ -1,6 +1,6 @@
# 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, 3800k,
# Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 3800k,
system, data, fat, , 128k,

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 # Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, , 0x1000,
5 factory, app, factory, 0x10000, 3800k, factory, app, factory, , 3800k,
6 system, data, fat, , 128k,

View File

@ -1,7 +1,7 @@
# 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, 4M,
# Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 4M,
system, data, fat, , 128k,
data, data, fat, , 1000k,

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 # Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, , 0x1000,
5 factory, app, factory, 0x10000, 4M, factory, app, factory, , 4M,
6 system, data, fat, , 128k,
7 data, data, fat, , 1000k,

View File

@ -1,7 +1,7 @@
# 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, 4M,
# Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 4M,
system, data, fat, , 128k,
data, data, fat, , 3800k,

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 # Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, , 0x1000,
5 factory, app, factory, 0x10000, 4M, factory, app, factory, , 4M,
6 system, data, fat, , 128k,
7 data, data, fat, , 3800k,

View File

@ -1,6 +1,6 @@
# 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, 4M,
# Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 4M,
system, data, fat, , 128k,

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 # Offsets are left blank so the build auto-places partitions relative to CONFIG_PARTITION_TABLE_OFFSET (see device.py), including on targets where that offset is bumped to fit a larger bootloader.
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, , 0x1000,
5 factory, app, factory, 0x10000, 4M, factory, app, factory, , 4M,
6 system, data, fat, , 128k,