From 6fe36cbcbf5b9fca0f56e592976ddb20ef25c7b9 Mon Sep 17 00:00:00 2001 From: tyeth Date: Wed, 26 Aug 2026 21:44:57 +0100 Subject: [PATCH 01/17] espressif: keep IDF heap headroom while wifi/BLE are enabled With MICROPY_GC_SPLIT_HEAP_AUTO the Python heap grows into the largest free IDF block until nothing is left. The wifi driver, lwIP and NimBLE allocate at runtime (TX buffers, station association, DHCP replies, ESP-NOW frames), so a Python program that fills its heap silently breaks the radio: esp_now_send() fails with ESP_ERR_ESPNOW_NO_MEM, softAP clients associate but never get a DHCP lease (udp_sendto fails), and start_ap() can hard fault. Reserve CIRCUITPY_ESP_RADIO_HEAP_RESERVE (32 KB, board-overridable) of the IDF heap whenever the wifi radio or the BLE adapter is enabled, by capping what port_heap_get_largest_free_size() reports to the GC. Programs that never turn a radio on keep the whole heap. Measured on an ESP32-C6 devkit (no PSRAM) running a wifi+ESP-NOW collector: before, every ESP-NOW send failed 0x3067 with idf_free=7884 largest=7680 and phones stuck at "obtaining IP address" (DHCP ACK udp_sendto -> -1); after, sends succeed (idf_free ~31 KB) and phones get leases. --- ports/espressif/mpconfigport.h | 8 ++++++++ ports/espressif/supervisor/port.c | 25 +++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 7de44130971..92f95f7f627 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -73,6 +73,14 @@ extern portMUX_TYPE background_task_mutex; #define CIRCUITPY_WIFI_DEFAULT_TX_POWER (20) #endif +// IDF heap kept free while the wifi radio or BLE adapter is enabled. The GC heap +// grows on demand (MICROPY_GC_SPLIT_HEAP_AUTO) and would otherwise consume the +// whole system heap, leaving the wifi driver, lwIP and NimBLE unable to allocate +// (ESP-NOW sends fail, softAP stations get no DHCP lease). Boards can override. +#ifndef CIRCUITPY_ESP_RADIO_HEAP_RESERVE +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (32 * 1024) +#endif + #ifndef CIRCUITPY_ESP32P4_SWAP_LSFS #define CIRCUITPY_ESP32P4_SWAP_LSFS (0) #endif diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 91fe8cf5a2e..ad6294a4df6 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -39,6 +39,8 @@ #include "common-hal/watchdog/WatchDogTimer.h" #include "common-hal/socketpool/Socket.h" #include "common-hal/wifi/__init__.h" +#include "shared-bindings/wifi/__init__.h" +#include "shared-bindings/wifi/Radio.h" #include "supervisor/background_callback.h" #include "supervisor/shared/tick.h" #include "shared-bindings/microcontroller/__init__.h" @@ -57,6 +59,7 @@ #if CIRCUITPY_BLEIO_NATIVE #include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" #endif #if CIRCUITPY_ESPCAMERA @@ -381,9 +384,27 @@ void *port_realloc(void *ptr, size_t size, bool dma_capable) { return heap_caps_realloc(ptr, size, caps); } +// Cap what the GC may take so CIRCUITPY_ESP_RADIO_HEAP_RESERVE of the IDF heap +// stays available to the radio stacks while one is enabled (see mpconfigport.h). size_t port_heap_get_largest_free_size(void) { - size_t free_size = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); - return free_size; + size_t largest = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); + size_t reserve = 0; + #if CIRCUITPY_WIFI + if (common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) { + reserve = CIRCUITPY_ESP_RADIO_HEAP_RESERVE; + } + #endif + #if CIRCUITPY_BLEIO_NATIVE + if (common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { + reserve = CIRCUITPY_ESP_RADIO_HEAP_RESERVE; + } + #endif + if (reserve == 0) { + return largest; + } + size_t total = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t allowed = total > reserve ? total - reserve : 0; + return MIN(largest, allowed); } #if MICROPY_PERSISTENT_CODE_LOAD_NATIVE From 45ad223aa35d9219b7b7d56d8437fec7db1d17e9 Mon Sep 17 00:00:00 2001 From: tyeth Date: Wed, 26 Aug 2026 21:44:57 +0100 Subject: [PATCH 02/17] ssl: issue TLS session tickets for server-side sockets A server-side SSLSocket did a full handshake for every connection. Browsers open several connections per page, and on a small MCU with an RSA-2048 certificate each handshake costs ~1.3 s of CPU (ESP32-C6), so pages stalled and speculative connections were abandoned. Configure mbedTLS session tickets (AES-256-GCM via PSA, 24 h lifetime, one process-wide key) on server-side contexts when MBEDTLS_SSL_SESSION_TICKETS and MBEDTLS_SSL_TICKET_C are available (ESP-IDF enables them by default), so returning clients resume with an abbreviated handshake and no public-key operation. Client-side behaviour is unchanged. --- shared-module/ssl/SSLSocket.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/shared-module/ssl/SSLSocket.c b/shared-module/ssl/SSLSocket.c index 6d306c2f071..7dff225b8ae 100644 --- a/shared-module/ssl/SSLSocket.c +++ b/shared-module/ssl/SSLSocket.c @@ -22,6 +22,8 @@ #include "shared-bindings/socketpool/enum.h" +#include "mbedtls/ssl_ticket.h" + #define MP_STREAM_POLL_RDWR (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR) #if defined(MBEDTLS_ERROR_C) @@ -372,6 +374,23 @@ ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t goto cleanup; } } + #if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_TICKET_C) + if (server_side) { + // Session tickets let a returning client resume without the (expensive) public-key + // handshake. One process-wide ticket key, rotated by lifetime; nothing to persist. + static mbedtls_ssl_ticket_context ticket_ctx; + static bool ticket_ready = false; + if (!ticket_ready) { + mbedtls_ssl_ticket_init(&ticket_ctx); + if (mbedtls_ssl_ticket_setup(&ticket_ctx, PSA_ALG_GCM, PSA_KEY_TYPE_AES, 256, 86400) == 0) { + ticket_ready = true; + } + } + if (ticket_ready) { + mbedtls_ssl_conf_session_tickets_cb(&o->conf, mbedtls_ssl_ticket_write, mbedtls_ssl_ticket_parse, &ticket_ctx); + } + } + #endif return o; cleanup: mbedtls_pk_free(&o->pkey); From 7af1222ea8b34813efd5e996709d2beb7346aac7 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 11 Sep 2026 19:30:20 +0100 Subject: [PATCH 03/17] espressif: 40KB radio heap reserve on the Feather/Metro/QT Py boards and bench devkits The base branch defaults CIRCUITPY_ESP_RADIO_HEAP_RESERVE to 32 KB, which is what a plain softAP + ESP-NOW workload needs. Adding a TLS server on top needs more: at 32 KB the C6 hub still hit MemoryError starting a handshake and lost DHCP replies. Override to 40 KB on the Adafruit ESP32 / ESP32-S3 / ESP32-C6 Feathers, Metro S3, QT Py ESP32 / ESP32-S3, and the two bench devkits. Other Adafruit ESP32/S3 boards (ItsyBitsy, MatrixPortal, Sparkle Motion, Qualia, cameras) are deliberately left on the 32 KB default for now. Replaces the branch's old global 58140f6820 ("radio heap reserve 40KB"), which raised the port-wide default instead of overriding per board. --- .../boards/adafruit_feather_esp32_v2/mpconfigboard.h | 5 +++++ .../mpconfigboard.h | 5 +++++ .../mpconfigboard.h | 5 +++++ .../boards/adafruit_feather_esp32s3_nopsram/mpconfigboard.h | 5 +++++ .../adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h | 5 +++++ .../boards/adafruit_feather_esp32s3_tft/mpconfigboard.h | 5 +++++ .../boards/adafruit_feather_huzzah32/mpconfigboard.h | 5 +++++ .../espressif/boards/adafruit_metro_esp32s3/mpconfigboard.h | 5 +++++ .../boards/adafruit_qtpy_esp32_pico/mpconfigboard.h | 5 +++++ .../adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.h | 5 +++++ .../boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h | 5 +++++ .../boards/espressif_esp32c6_devkitc_1_n8/mpconfigboard.h | 5 +++++ .../boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h | 5 +++++ 13 files changed, 65 insertions(+) diff --git a/ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h index 38b014fd59f..722598eaaf5 100644 --- a/ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h @@ -34,3 +34,8 @@ // UART pins attached to the USB-serial converter chip #define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1) #define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO3) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.h index 969bf468574..363182ecf80 100644 --- a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.h @@ -32,3 +32,8 @@ // Explanation of how a user got into safe mode #define BOARD_USER_SAFE_MODE_ACTION MP_ERROR_TEXT("You pressed the BOOT button at start up.") + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.h index 7abd61a7d42..22c17bdb966 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.h @@ -27,3 +27,8 @@ #define DEFAULT_UART_BUS_TX (&pin_GPIO39) #define DOUBLE_TAP_PIN (&pin_GPIO34) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/mpconfigboard.h index a47bc8d859e..2c3af372b45 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/mpconfigboard.h @@ -27,3 +27,8 @@ #define DEFAULT_UART_BUS_TX (&pin_GPIO39) #define DOUBLE_TAP_PIN (&pin_GPIO34) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h index 5053a1b2b58..29b2239e88c 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h @@ -27,3 +27,8 @@ #define DEFAULT_UART_BUS_TX (&pin_GPIO39) #define DOUBLE_TAP_PIN (&pin_GPIO34) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.h index 243777fd8df..3def649a3aa 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.h @@ -27,3 +27,8 @@ #define DEFAULT_UART_BUS_TX (&pin_GPIO1) #define DOUBLE_TAP_PIN (&pin_GPIO38) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_feather_huzzah32/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_huzzah32/mpconfigboard.h index 9bd51044f4e..c6ace45d75a 100644 --- a/ports/espressif/boards/adafruit_feather_huzzah32/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_huzzah32/mpconfigboard.h @@ -25,3 +25,8 @@ // UART pins attached to the USB-serial converter chip #define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1) #define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO3) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.h b/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.h index 2442d28e712..3cd07dc2a68 100644 --- a/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.h @@ -26,3 +26,8 @@ #define DEFAULT_UART_BUS_TX (&pin_GPIO40) #define DOUBLE_TAP_PIN (&pin_GPIO38) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.h b/ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.h index a0bc2b9f374..9864578e595 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.h @@ -29,3 +29,8 @@ // Reduce wifi.radio.tx_power due to the antenna design of this board #define CIRCUITPY_WIFI_DEFAULT_TX_POWER (15) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.h b/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.h index 9760bbfeb44..09430af6327 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.h @@ -28,3 +28,8 @@ // Reduce wifi.radio.tx_power due to the antenna design of this board #define CIRCUITPY_WIFI_DEFAULT_TX_POWER (15) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h b/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h index 6073bd47d0e..f6409a25ed1 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h @@ -28,3 +28,8 @@ // Reduce wifi.radio.tx_power due to the antenna design of this board #define CIRCUITPY_WIFI_DEFAULT_TX_POWER (15) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/mpconfigboard.h b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/mpconfigboard.h index e5f2606cf49..96c91c98d9b 100644 --- a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/mpconfigboard.h +++ b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/mpconfigboard.h @@ -15,3 +15,8 @@ #define DEFAULT_UART_BUS_RX (&pin_GPIO17) #define DEFAULT_UART_BUS_TX (&pin_GPIO16) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) diff --git a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h index ab942513937..01da6c40bdc 100644 --- a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h +++ b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.h @@ -15,3 +15,8 @@ #define DEFAULT_UART_BUS_RX (&pin_GPIO44) #define DEFAULT_UART_BUS_TX (&pin_GPIO43) + +// Bench finding: 32 KB of IDF headroom is not enough for a softAP that also +// serves TLS -- handshakes hit MemoryError and DHCP replies fail while the +// Python heap still reports free space. 40 KB is what the C6 hub needed. +#define CIRCUITPY_ESP_RADIO_HEAP_RESERVE (40 * 1024) From d63d6a76a1656c0e7b1488d4ce6dd19a149a91b9 Mon Sep 17 00:00:00 2001 From: tyeth Date: Sun, 13 Sep 2026 22:19:14 +0100 Subject: [PATCH 04/17] espressif: freeze the env-collector's libraries on the 13 boards it targets The ESP-NOW / WiFi / BLE environmental collector ships ~20 libraries in lib/. On a board without PSRAM that is the difference between running and not. Measured on a Feather ESP32-S3 No PSRAM (2026-09-13), with the application itself already cross-compiled to .mpy: * BLE + ESP-NOW + softAP all come up, and then `import datastore` dies with `MemoryError: memory allocation failed, allocating 158 bytes`; * with BLE off it reaches the eInk dashboard and the SD mount fails for want of 832 bytes, with 15.9 KB free. Cross-compiling the application removes the on-device *compiler* peak -- which is what made the C6 unbootable -- but not the resident bytecode. Freezing does: a frozen module executes in place from flash and never occupies heap. Twenty-two libraries, on the 13 espressif boards this project targets -- the same 13 the custom-build workflow has been dispatched for, and on the branch those builds actually used. Nine were not yet in frozen/ and are added as submodules: IL0373, JD79667, MAX1704x, NTP, SCD4X, SCD30, SEN6x, PCF8523, DS1307. The other thirteen were already there. The 22 lines are repeated verbatim in each board's mpconfigboard.mk rather than factored into one include. That is not an aesthetic choice: tools/ ci_fetch_deps.py reads each board file line by line looking for "FROZEN_MPY_DIRS += $(TOP)/" to decide which submodules to check out, and it does not follow includes. The include version builds fine locally, FREEZE lists all 22 dirs, and then CI fails at MKMANIFEST with every frozen directory empty. Each copy of the block carries a comment saying so before someone tidies it back. Deliberately not frozen: * adafruit_pixelbuf -- already a built-in C module. * sensirion_i2c_driver / sensirion_i2c_sen5x -- a third-party circup bundle rather than a library repo, and node_sensors.py carries a minimal built-in SEN5x driver for boards without them. * PCF85063A -- no Adafruit library exists; the project drives that chip directly from its own extrtc.py. Size: ~162 KB of frozen bytecode from 621 KB of source, uniform across all 13. Comfortable on the 8 MB and 16 MB boards; the 4 MB ones are what CI has to answer, and if one overflows the fix is to split the list rather than shrink it everywhere. NOTE for whoever deploys against these builds: sys.path puts /lib ahead of .frozen, so a copy left in lib/ SHADOWS the frozen one and the board pays the RAM anyway. Emptying lib/ is what collects the benefit -- the same trap as leaving a .py beside a .mpy. Co-Authored-By: Claude Opus 5 --- .gitmodules | 27 ++++++++++++++ frozen/Adafruit_CircuitPython_DS1307 | 1 + frozen/Adafruit_CircuitPython_IL0373 | 1 + frozen/Adafruit_CircuitPython_JD79667 | 1 + frozen/Adafruit_CircuitPython_MAX1704x | 1 + frozen/Adafruit_CircuitPython_NTP | 1 + frozen/Adafruit_CircuitPython_PCF8523 | 1 + frozen/Adafruit_CircuitPython_SCD30 | 1 + frozen/Adafruit_CircuitPython_SCD4X | 1 + frozen/Adafruit_CircuitPython_SEN6x | 1 + .../mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ .../adafruit_metro_esp32s3/mpconfigboard.mk | 37 +++++++++++++++++++ .../adafruit_qtpy_esp32_pico/mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ .../mpconfigboard.mk | 37 +++++++++++++++++++ 23 files changed, 517 insertions(+) create mode 160000 frozen/Adafruit_CircuitPython_DS1307 create mode 160000 frozen/Adafruit_CircuitPython_IL0373 create mode 160000 frozen/Adafruit_CircuitPython_JD79667 create mode 160000 frozen/Adafruit_CircuitPython_MAX1704x create mode 160000 frozen/Adafruit_CircuitPython_NTP create mode 160000 frozen/Adafruit_CircuitPython_PCF8523 create mode 160000 frozen/Adafruit_CircuitPython_SCD30 create mode 160000 frozen/Adafruit_CircuitPython_SCD4X create mode 160000 frozen/Adafruit_CircuitPython_SEN6x diff --git a/.gitmodules b/.gitmodules index f02565e6915..1e13cbc8bc4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -424,3 +424,30 @@ [submodule "ports/espressif/esp-hosted"] path = ports/espressif/esp-hosted url = https://github.com/espressif/esp-hosted-mcu.git +[submodule "frozen/Adafruit_CircuitPython_IL0373"] + path = frozen/Adafruit_CircuitPython_IL0373 + url = https://github.com/adafruit/Adafruit_CircuitPython_IL0373.git +[submodule "frozen/Adafruit_CircuitPython_JD79667"] + path = frozen/Adafruit_CircuitPython_JD79667 + url = https://github.com/adafruit/Adafruit_CircuitPython_JD79667.git +[submodule "frozen/Adafruit_CircuitPython_MAX1704x"] + path = frozen/Adafruit_CircuitPython_MAX1704x + url = https://github.com/adafruit/Adafruit_CircuitPython_MAX1704x.git +[submodule "frozen/Adafruit_CircuitPython_NTP"] + path = frozen/Adafruit_CircuitPython_NTP + url = https://github.com/adafruit/Adafruit_CircuitPython_NTP.git +[submodule "frozen/Adafruit_CircuitPython_SCD4X"] + path = frozen/Adafruit_CircuitPython_SCD4X + url = https://github.com/adafruit/Adafruit_CircuitPython_SCD4X.git +[submodule "frozen/Adafruit_CircuitPython_SCD30"] + path = frozen/Adafruit_CircuitPython_SCD30 + url = https://github.com/adafruit/Adafruit_CircuitPython_SCD30.git +[submodule "frozen/Adafruit_CircuitPython_SEN6x"] + path = frozen/Adafruit_CircuitPython_SEN6x + url = https://github.com/adafruit/Adafruit_CircuitPython_SEN6x.git +[submodule "frozen/Adafruit_CircuitPython_PCF8523"] + path = frozen/Adafruit_CircuitPython_PCF8523 + url = https://github.com/adafruit/Adafruit_CircuitPython_PCF8523.git +[submodule "frozen/Adafruit_CircuitPython_DS1307"] + path = frozen/Adafruit_CircuitPython_DS1307 + url = https://github.com/adafruit/Adafruit_CircuitPython_DS1307.git diff --git a/frozen/Adafruit_CircuitPython_DS1307 b/frozen/Adafruit_CircuitPython_DS1307 new file mode 160000 index 00000000000..d9ccc8cbc90 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_DS1307 @@ -0,0 +1 @@ +Subproject commit d9ccc8cbc90af7f5daf380fca9492d3a337430d6 diff --git a/frozen/Adafruit_CircuitPython_IL0373 b/frozen/Adafruit_CircuitPython_IL0373 new file mode 160000 index 00000000000..645f0bf78cd --- /dev/null +++ b/frozen/Adafruit_CircuitPython_IL0373 @@ -0,0 +1 @@ +Subproject commit 645f0bf78cd9e09ef07eaca37871ee310fa36445 diff --git a/frozen/Adafruit_CircuitPython_JD79667 b/frozen/Adafruit_CircuitPython_JD79667 new file mode 160000 index 00000000000..4a5ce533021 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_JD79667 @@ -0,0 +1 @@ +Subproject commit 4a5ce53302152058cac151cef6d5c528304d3437 diff --git a/frozen/Adafruit_CircuitPython_MAX1704x b/frozen/Adafruit_CircuitPython_MAX1704x new file mode 160000 index 00000000000..ad881ac7f2a --- /dev/null +++ b/frozen/Adafruit_CircuitPython_MAX1704x @@ -0,0 +1 @@ +Subproject commit ad881ac7f2adc0ba06f1a153e91658ebcbbacf18 diff --git a/frozen/Adafruit_CircuitPython_NTP b/frozen/Adafruit_CircuitPython_NTP new file mode 160000 index 00000000000..20cb912e92a --- /dev/null +++ b/frozen/Adafruit_CircuitPython_NTP @@ -0,0 +1 @@ +Subproject commit 20cb912e92a8edcb7a69e64f938b33937f55d475 diff --git a/frozen/Adafruit_CircuitPython_PCF8523 b/frozen/Adafruit_CircuitPython_PCF8523 new file mode 160000 index 00000000000..28187e912a1 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_PCF8523 @@ -0,0 +1 @@ +Subproject commit 28187e912a1ddd5ae544857fb449198ea1914197 diff --git a/frozen/Adafruit_CircuitPython_SCD30 b/frozen/Adafruit_CircuitPython_SCD30 new file mode 160000 index 00000000000..a13f91cb423 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_SCD30 @@ -0,0 +1 @@ +Subproject commit a13f91cb423e985e03f9b67ec77bb86df8e24616 diff --git a/frozen/Adafruit_CircuitPython_SCD4X b/frozen/Adafruit_CircuitPython_SCD4X new file mode 160000 index 00000000000..2c625693826 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_SCD4X @@ -0,0 +1 @@ +Subproject commit 2c62569382663b42d79f1780ee647895c58dbc6e diff --git a/frozen/Adafruit_CircuitPython_SEN6x b/frozen/Adafruit_CircuitPython_SEN6x new file mode 160000 index 00000000000..b8dacfc9552 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_SEN6x @@ -0,0 +1 @@ +Subproject commit b8dacfc955208db3127efdf48b52a8e4398bb361 diff --git a/ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.mk index 2569b1cabb1..b93c59e5f3a 100644 --- a/ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.mk @@ -10,3 +10,40 @@ CIRCUITPY_ESP_FLASH_SIZE = 8MB CIRCUITPY_ESP_PSRAM_SIZE = 2MB CIRCUITPY_ESP_PSRAM_MODE = qio CIRCUITPY_ESP_PSRAM_FREQ = 40m + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.mk index 28821ed7b48..c2498059ec4 100644 --- a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.mk @@ -9,3 +9,40 @@ CIRCUITPY_ESP_FLASH_SIZE = 4MB # Board was originally defined with a 2MB firmware, almost 2MB user filesystem. Leave it that way. CIRCUITPY_4MB_FLASH_LARGE_USER_FS_LAYOUT = 1 + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk index 6fafa82485a..d3363e6e9b4 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk @@ -12,3 +12,40 @@ CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_PSRAM_SIZE = 2MB CIRCUITPY_ESP_PSRAM_MODE = qio CIRCUITPY_ESP_PSRAM_FREQ = 80m + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/mpconfigboard.mk index cdf1657d8be..140d2150891 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/mpconfigboard.mk @@ -8,3 +8,40 @@ IDF_TARGET = esp32s3 CIRCUITPY_ESP_FLASH_MODE = qio CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_FLASH_SIZE = 8MB + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.mk index a82ae6d4f04..321019debb9 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.mk @@ -16,3 +16,40 @@ CIRCUITPY_ESP_PSRAM_FREQ = 80m # Already have a display. CIRCUITPY_PARALLELDISPLAYBUS = 0 + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.mk index 71ef6d222a2..e8cb5b57c02 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.mk @@ -16,3 +16,40 @@ CIRCUITPY_ESP_PSRAM_FREQ = 80m # Already have a display. CIRCUITPY_PARALLELDISPLAYBUS = 0 + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_feather_huzzah32/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_huzzah32/mpconfigboard.mk index f948521eb6d..fb434010d84 100644 --- a/ports/espressif/boards/adafruit_feather_huzzah32/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_huzzah32/mpconfigboard.mk @@ -6,3 +6,40 @@ IDF_TARGET = esp32 CIRCUITPY_ESP_FLASH_MODE = qio CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_FLASH_SIZE = 4MB + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk b/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk index 2fd5a40f259..7b9d8f60723 100644 --- a/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk @@ -12,3 +12,40 @@ CIRCUITPY_ESP_FLASH_SIZE = 16MB CIRCUITPY_ESP_PSRAM_MODE = opi CIRCUITPY_ESP_PSRAM_FREQ = 80m CIRCUITPY_ESP_PSRAM_SIZE = 8MB + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.mk b/ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.mk index e27e48cf51e..07aaaaa91a4 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_qtpy_esp32_pico/mpconfigboard.mk @@ -13,3 +13,40 @@ CIRCUITPY_ESP_PSRAM_FREQ = 80m # Not enough pins. CIRCUITPY_PARALLELDISPLAYBUS = 0 + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk b/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk index 09571a63a59..739c12796a3 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk @@ -16,3 +16,40 @@ CIRCUITPY_ESP_PSRAM_FREQ = 80m # Not enough pins. CIRCUITPY_ESPCAMERA = 0 CIRCUITPY_PARALLELDISPLAYBUS = 0 + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.mk b/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.mk index 053b15647c1..b0955f310b6 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.mk @@ -12,3 +12,40 @@ CIRCUITPY_ESP_FLASH_SIZE = 8MB # Few pins. CIRCUITPY_PARALLELDISPLAYBUS = 0 + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/mpconfigboard.mk index e15d2bf41e8..b4507b34995 100644 --- a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/mpconfigboard.mk +++ b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/mpconfigboard.mk @@ -6,3 +6,40 @@ IDF_TARGET = esp32c6 CIRCUITPY_ESP_FLASH_MODE = qio CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_FLASH_SIZE = 8MB + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 diff --git a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.mk index ea5597d3702..b67fa3c80c6 100644 --- a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.mk +++ b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/mpconfigboard.mk @@ -8,3 +8,40 @@ IDF_TARGET = esp32s3 CIRCUITPY_ESP_FLASH_MODE = qio CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_FLASH_SIZE = 8MB + +# --- environmental-collector frozen libraries ------------------------------- +# Frozen bytecode executes in place from flash; the same library imported +# from lib/ is read into RAM and stays there. On a board without PSRAM that +# is the difference between running and not -- a Feather ESP32-S3 No PSRAM +# running this project, with its own code already cross-compiled to .mpy, +# had 6 KB of heap left once BLE + ESP-NOW + softAP were up and died on the +# next import. +# +# Listed literally rather than behind an include, even though the same 22 +# lines repeat across every board this project targets: tools/ +# ci_fetch_deps.py scans THIS file for "FROZEN_MPY_DIRS += $(TOP)/" to +# decide which submodules to check out, and it does not follow includes. An +# include builds locally and then fails CI at MKMANIFEST with empty frozen +# directories. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 From e96c53a51780ff35b5a4f14849307b876226b30e Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 10 Sep 2026 15:16:30 +0100 Subject: [PATCH 05/17] py/version.py: treat an empty CP_VERSION as unset build-board-custom.yml calls .github/actions/deps/submodules without version: true, so steps.set-up-submodules.outputs.version is empty. For a board with frozen modules the mpy_cross action then runs make with CP_VERSION="" in its environment, and get_version_info_from_git() took the presence of the variable as authoritative, returned an empty tag, and makeversionhdr.py aborted with "Cannot determine version". Boards without frozen modules never run that step, which is why the Pico 2 W asset build passed while the Pico W one failed twice on the same commit. Fall back to git describe when CP_VERSION is present but empty. Co-Authored-By: Claude Fable 5.1 --- py/version.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/py/version.py b/py/version.py index 7ef2a587628..76ebe247e1a 100755 --- a/py/version.py +++ b/py/version.py @@ -3,7 +3,11 @@ def get_version_info_from_git(repo_path, extra_args=[]): - if "CP_VERSION" in os.environ: + # CIRCUITPY-CHANGE: treat an empty CP_VERSION as unset. A workflow that routes an + # unpopulated step output through `env:` (build-board-custom.yml -> + # .github/actions/mpy_cross does this for boards with frozen modules) exports + # CP_VERSION="" and must fall back to git describe instead of failing. + if os.environ.get("CP_VERSION"): git_tag = os.environ["CP_VERSION"] else: # Note: git describe doesn't work if no tag is available From 328b81bd373624c0f392967abca171b259f0451c Mon Sep 17 00:00:00 2001 From: tyeth Date: Sun, 13 Sep 2026 22:35:09 +0100 Subject: [PATCH 06/17] espressif: state _bleio explicitly on the 4MB + 2MB PSRAM targets The hub serves its portal over a Nordic UART service and the nodes take their config the same way, so _bleio is not optional for this project on the boards it is aimed at. Reading ports/espressif/mpconfigport.mk, it should already be on: CIRCUITPY_BLEIO_NATIVE defaults to 1 and is forced off only for the S2 and the P4 (no BLE hardware) and for 2MB flash -- not 4MB, which only loses dualbank and gains -Os. So on these four boards this is a no-op that says so out loud, and it stops a future flash-size trim taking BLE away silently the way the 2MB block already does. Boards: feather_esp32s3_4mbflash_2mbpsram, qtpy_esp32s3_4mbflash_2mbpsram, feather_esp32s3_tft, feather_esp32s3_reverse_tft. Size is not the constraint here, contrary to what a 4MB board suggests: the last green build of feather_esp32s3_4mbflash_2mbpsram used 1,841,040 of 2,883,584 bytes of firmware space, leaving 1,042,544 free -- room for this project's ~162 KB of frozen bytecode several times over. --- .../mpconfigboard.mk | 7 +++++++ .../adafruit_feather_esp32s3_reverse_tft/mpconfigboard.mk | 7 +++++++ .../boards/adafruit_feather_esp32s3_tft/mpconfigboard.mk | 7 +++++++ .../mpconfigboard.mk | 7 +++++++ 4 files changed, 28 insertions(+) diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk index d3363e6e9b4..deeaa2e728d 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk @@ -13,6 +13,13 @@ CIRCUITPY_ESP_PSRAM_SIZE = 2MB CIRCUITPY_ESP_PSRAM_MODE = qio CIRCUITPY_ESP_PSRAM_FREQ = 80m +# This project needs _bleio on this board: the hub serves its portal over +# a Nordic UART service and the nodes take their config the same way. The +# port already defaults CIRCUITPY_BLEIO_NATIVE to 1 and only forces it off +# for 2MB flash, the S2 and the P4 -- stated here so a future flash-size +# trim cannot take it away silently. +CIRCUITPY_BLEIO_NATIVE = 1 + # --- environmental-collector frozen libraries ------------------------------- # Frozen bytecode executes in place from flash; the same library imported # from lib/ is read into RAM and stays there. On a board without PSRAM that diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.mk index 321019debb9..c51218faf2b 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.mk @@ -17,6 +17,13 @@ CIRCUITPY_ESP_PSRAM_FREQ = 80m # Already have a display. CIRCUITPY_PARALLELDISPLAYBUS = 0 +# This project needs _bleio on this board: the hub serves its portal over +# a Nordic UART service and the nodes take their config the same way. The +# port already defaults CIRCUITPY_BLEIO_NATIVE to 1 and only forces it off +# for 2MB flash, the S2 and the P4 -- stated here so a future flash-size +# trim cannot take it away silently. +CIRCUITPY_BLEIO_NATIVE = 1 + # --- environmental-collector frozen libraries ------------------------------- # Frozen bytecode executes in place from flash; the same library imported # from lib/ is read into RAM and stays there. On a board without PSRAM that diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.mk index e8cb5b57c02..81021c36f4f 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32s3_tft/mpconfigboard.mk @@ -17,6 +17,13 @@ CIRCUITPY_ESP_PSRAM_FREQ = 80m # Already have a display. CIRCUITPY_PARALLELDISPLAYBUS = 0 +# This project needs _bleio on this board: the hub serves its portal over +# a Nordic UART service and the nodes take their config the same way. The +# port already defaults CIRCUITPY_BLEIO_NATIVE to 1 and only forces it off +# for 2MB flash, the S2 and the P4 -- stated here so a future flash-size +# trim cannot take it away silently. +CIRCUITPY_BLEIO_NATIVE = 1 + # --- environmental-collector frozen libraries ------------------------------- # Frozen bytecode executes in place from flash; the same library imported # from lib/ is read into RAM and stays there. On a board without PSRAM that diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk b/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk index 739c12796a3..6898b808757 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.mk @@ -17,6 +17,13 @@ CIRCUITPY_ESP_PSRAM_FREQ = 80m CIRCUITPY_ESPCAMERA = 0 CIRCUITPY_PARALLELDISPLAYBUS = 0 +# This project needs _bleio on this board: the hub serves its portal over +# a Nordic UART service and the nodes take their config the same way. The +# port already defaults CIRCUITPY_BLEIO_NATIVE to 1 and only forces it off +# for 2MB flash, the S2 and the P4 -- stated here so a future flash-size +# trim cannot take it away silently. +CIRCUITPY_BLEIO_NATIVE = 1 + # --- environmental-collector frozen libraries ------------------------------- # Frozen bytecode executes in place from flash; the same library imported # from lib/ is read into RAM and stays there. On a board without PSRAM that From 7e55dec49cee02b1401b8a53a6af407d46a386f3 Mon Sep 17 00:00:00 2001 From: tyeth Date: Wed, 9 Sep 2026 15:27:45 +0100 Subject: [PATCH 07/17] ssl: do not demand a client certificate from a server-side context wrap_socket(server_side=True) configured mbedTLS with VERIFY_REQUIRED whenever the context had the default root bundle attached, which every ssl.SSLContext() does. On a server that means "require a client certificate", so a plain HTTPS server -- load_cert_chain() and nothing else -- failed every handshake with MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE (-0x7480) and the client saw a timeout. Default server-side contexts to VERIFY_NONE, as CPython's do (CERT_NONE for servers); a context that loaded its own CA with load_verify_locations() still verifies clients. Client-side behaviour is unchanged. Seen on a Pico 2 W serving a captive portal over its softAP; the same code runs on every port using the shared mbedTLS module. Co-Authored-By: Claude Opus 5 --- shared-module/ssl/SSLSocket.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/shared-module/ssl/SSLSocket.c b/shared-module/ssl/SSLSocket.c index 7dff225b8ae..0d18b0ec5bb 100644 --- a/shared-module/ssl/SSLSocket.c +++ b/shared-module/ssl/SSLSocket.c @@ -327,7 +327,16 @@ ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t goto cleanup; } - if (self->crt_bundle_attach != NULL) { + if (server_side && !(self->cacert_buf && self->cacert_bytes)) { + // On a server the CA store is about authenticating *clients*. A + // context that only had load_cert_chain() called on it -- the normal + // HTTPS-server setup -- must not demand a client certificate, which is + // what the default root bundle turned into: every handshake failed + // with MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE. Match CPython, where a + // server-side context defaults to CERT_NONE; a program that loads its + // own CA with load_verify_locations() still gets client verification. + mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_NONE); + } else if (self->crt_bundle_attach != NULL) { mbedtls_ssl_conf_authmode(&o->conf, MBEDTLS_SSL_VERIFY_REQUIRED); self->crt_bundle_attach(&o->conf); } else if (self->cacert_buf && self->cacert_bytes) { From 30f972da59151ae202041f17a1c2ebc7a21c6154 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 22 Oct 2026 01:35:44 +0100 Subject: [PATCH 08/17] espressif: allow collector TLS certificate chains on bench devkits --- .../espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig | 4 ++++ .../espressif/boards/espressif_esp32s3_devkitc_1_n8/sdkconfig | 3 +++ 2 files changed, 7 insertions(+) diff --git a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig index e9628662160..af7aecad761 100644 --- a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig +++ b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig @@ -12,3 +12,7 @@ # end of Component config # end of Espressif IoT Development Framework Configuration + +# The collector serves a certificate chain larger than the upstream 2 KB record. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 diff --git a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/sdkconfig b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/sdkconfig index e9628662160..d4399467e7f 100644 --- a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/sdkconfig +++ b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/sdkconfig @@ -12,3 +12,6 @@ # end of Component config # end of Espressif IoT Development Framework Configuration +# The collector serves a certificate chain larger than the upstream 2 KB record. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 From b7174726e47c4a591abcc1b4ac70f972d3708f0d Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 22 Oct 2026 01:49:06 +0100 Subject: [PATCH 09/17] Give frozen collector firmware room in C6 8MB OTA slots --- .../boards/espressif_esp32c6_devkitc_1_n8/partitions.csv | 7 +++++++ .../boards/espressif_esp32c6_devkitc_1_n8/sdkconfig | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/partitions.csv diff --git a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/partitions.csv b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/partitions.csv new file mode 100644 index 00000000000..91c771c6b8c --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/partitions.csv @@ -0,0 +1,7 @@ +# Collector build only: 3 MB OTA slots, 1984 KB filesystem on an 8 MB board. +# Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 20K +otadata, data, ota, 0xe000, 8K +ota_0, app, ota_0, 0x10000, 3072K +ota_1, app, ota_1, 0x310000, 3072K +user_fs, data, fat, 0x610000, 1984K diff --git a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig index af7aecad761..79a70a18721 100644 --- a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig +++ b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig @@ -16,3 +16,8 @@ # The collector serves a certificate chain larger than the upstream 2 KB record. CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 + +# Frozen collector libraries exceed the upstream 2 MB application slot on C6. +# This layout moves user_fs: back up CIRCUITPY before installing or reverting. +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="boards/espressif_esp32c6_devkitc_1_n8/partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="boards/espressif_esp32c6_devkitc_1_n8/partitions.csv" From 5c7c2dafffe26c4f55bb74c28f08fc893350a6bc Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 22 Oct 2026 02:04:24 +0100 Subject: [PATCH 10/17] Include C5 and C61 devkits in the collector firmware builds --- .../mpconfigboard.mk | 24 +++++++++++++++++++ .../sdkconfig | 3 +++ .../mpconfigboard.mk | 24 +++++++++++++++++++ .../sdkconfig | 3 +++ 4 files changed, 54 insertions(+) diff --git a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/mpconfigboard.mk index 0da008596e3..b77640089cc 100644 --- a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/mpconfigboard.mk +++ b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/mpconfigboard.mk @@ -3,6 +3,30 @@ CIRCUITPY_CREATION_ID = 0x00C50001 IDF_TARGET = esp32c5 +# Freeze the collector dependencies to match the other Env Hub builds. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 + CIRCUITPY_ESP_FLASH_MODE = dio CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_FLASH_SIZE = 8MB diff --git a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig index e9628662160..6cf26f3380c 100644 --- a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig +++ b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig @@ -12,3 +12,6 @@ # end of Component config # end of Espressif IoT Development Framework Configuration +# Allow the collector's TLS certificate chain to fit in a record. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk index 1092a874a68..7713af5e293 100644 --- a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk @@ -2,6 +2,30 @@ CIRCUITPY_CREATOR_ID = 0x000C303A CIRCUITPY_CREATION_ID = 0x00C61001 IDF_TARGET = esp32c61 + +# Freeze the collector dependencies to match the other Env Hub builds. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NTP +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Ticks +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SEN6x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD4X +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SCD30 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_MAX1704x +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_LC709203F +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_JD79667 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IL0373 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Bitmap_Font +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8563 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS3231 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_PCF8523 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DS1307 IDF_TARGET_ARCH = riscv CIRCUITPY_ESP_FLASH_MODE = qio diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig index e9628662160..6cf26f3380c 100644 --- a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig @@ -12,3 +12,6 @@ # end of Component config # end of Espressif IoT Development Framework Configuration +# Allow the collector's TLS certificate chain to fit in a record. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 From fcffee9ab7c9d92b489068350c5d02274fb7bf83 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 22 Oct 2026 02:09:52 +0100 Subject: [PATCH 11/17] Fit C5 collector build in 3MB OTA slots --- .../boards/espressif_esp32c5_devkitc_1_n8r8/partitions.csv | 7 +++++++ .../boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig | 3 +++ 2 files changed, 10 insertions(+) create mode 100644 ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/partitions.csv diff --git a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/partitions.csv b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/partitions.csv new file mode 100644 index 00000000000..91c771c6b8c --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/partitions.csv @@ -0,0 +1,7 @@ +# Collector build only: 3 MB OTA slots, 1984 KB filesystem on an 8 MB board. +# Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 20K +otadata, data, ota, 0xe000, 8K +ota_0, app, ota_0, 0x10000, 3072K +ota_1, app, ota_1, 0x310000, 3072K +user_fs, data, fat, 0x610000, 1984K diff --git a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig index 6cf26f3380c..3dd75028f3a 100644 --- a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig +++ b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig @@ -15,3 +15,6 @@ # Allow the collector's TLS certificate chain to fit in a record. CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 +# Frozen libraries need a larger application slot; back up user_fs before flashing. +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="boards/espressif_esp32c5_devkitc_1_n8r8/partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="boards/espressif_esp32c5_devkitc_1_n8r8/partitions.csv" From 2a7f9987f3463c432f0742f51a096f258dc50c49 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 22 Oct 2026 02:13:49 +0100 Subject: [PATCH 12/17] Allow C5 and C61 bench consoles through their UART bridges --- .../espressif_esp32c5_devkitc_1_n8r8/mpconfigboard.h | 8 ++++++++ .../espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/mpconfigboard.h b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/mpconfigboard.h index ca05bf78b6a..a13e0c67948 100644 --- a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/mpconfigboard.h +++ b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/mpconfigboard.h @@ -15,3 +15,11 @@ #define DEFAULT_UART_BUS_RX (&pin_GPIO12) #define DEFAULT_UART_BUS_TX (&pin_GPIO11) + +// Optional bench console through the onboard CP2102N. This uses fewer USB/IP +// host channels than the native composite USB-JTAG interface on cascaded hubs. +// Build with CIRCUITPY_ESP_USB_SERIAL_JTAG=0; the normal USB console stays default. +#if !CIRCUITPY_ESP_USB_SERIAL_JTAG +#define CIRCUITPY_CONSOLE_UART_RX DEFAULT_UART_BUS_RX +#define CIRCUITPY_CONSOLE_UART_TX DEFAULT_UART_BUS_TX +#endif diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h index d901bfb37ba..887342b4c1a 100644 --- a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h @@ -13,3 +13,11 @@ #define DEFAULT_UART_BUS_RX (&pin_GPIO10) #define DEFAULT_UART_BUS_TX (&pin_GPIO11) + +// Optional bench console through the onboard CP2102N. This uses fewer USB/IP +// host channels than the native composite USB-JTAG interface on cascaded hubs. +// Build with CIRCUITPY_ESP_USB_SERIAL_JTAG=0; the normal USB console stays default. +#if !CIRCUITPY_ESP_USB_SERIAL_JTAG +#define CIRCUITPY_CONSOLE_UART_RX DEFAULT_UART_BUS_RX +#define CIRCUITPY_CONSOLE_UART_TX DEFAULT_UART_BUS_TX +#endif From 2c096e11e7ca3bc432dea64e7f257635bcac32b7 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 22 Oct 2026 03:42:04 +0100 Subject: [PATCH 13/17] Fit frozen Env Hub firmware and TLS chains on Feather and Metro boards --- .../mpconfigboard.mk | 5 +++-- .../sdkconfig | 2 ++ .../sdkconfig | 2 ++ .../boards/adafruit_feather_esp32s3_nopsram/sdkconfig | 2 ++ .../boards/adafruit_metro_esp32s3/partitions.csv | 11 +++++++++++ .../espressif/boards/adafruit_metro_esp32s3/sdkconfig | 6 ++++++ 6 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 ports/espressif/boards/adafruit_metro_esp32s3/partitions.csv diff --git a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.mk index c2498059ec4..28ee74d2e95 100644 --- a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.mk @@ -7,8 +7,9 @@ CIRCUITPY_ESP_FLASH_MODE = qio CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_FLASH_SIZE = 4MB -# Board was originally defined with a 2MB firmware, almost 2MB user filesystem. Leave it that way. -CIRCUITPY_4MB_FLASH_LARGE_USER_FS_LAYOUT = 1 +# Frozen Env Hub libraries exceed 2 MB. Use the standard larger app layout; +# back up and restore the filesystem when upgrading from the old layout. +CIRCUITPY_4MB_FLASH_LARGE_USER_FS_LAYOUT = 0 # --- environmental-collector frozen libraries ------------------------------- # Frozen bytecode executes in place from flash; the same library imported diff --git a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/sdkconfig index e9628662160..28916dbb8bd 100644 --- a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/sdkconfig @@ -12,3 +12,5 @@ # end of Component config # end of Espressif IoT Development Framework Configuration +# Fit the Env Hub server certificate chain in an outbound TLS record. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/sdkconfig index e9628662160..28916dbb8bd 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/sdkconfig @@ -12,3 +12,5 @@ # end of Component config # end of Espressif IoT Development Framework Configuration +# Fit the Env Hub server certificate chain in an outbound TLS record. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/sdkconfig index e9628662160..28916dbb8bd 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/sdkconfig @@ -12,3 +12,5 @@ # end of Component config # end of Espressif IoT Development Framework Configuration +# Fit the Env Hub server certificate chain in an outbound TLS record. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 diff --git a/ports/espressif/boards/adafruit_metro_esp32s3/partitions.csv b/ports/espressif/boards/adafruit_metro_esp32s3/partitions.csv new file mode 100644 index 00000000000..648d43e07b8 --- /dev/null +++ b/ports/espressif/boards/adafruit_metro_esp32s3/partitions.csv @@ -0,0 +1,11 @@ +# Env Hub build: 3 MB OTA slots on the 16 MB Metro ESP32-S3. +# Keep the existing TinyUF2 factory app at 0x410000. The gap before it +# preserves that address while allowing ota_0 to grow. user_fs moves: +# back up its files and reformat/restore when installing this layout. +# Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 20K +otadata, data, ota, 0xe000, 8K +ota_0, app, ota_0, 0x10000, 3072K +uf2, app, factory, 0x410000, 256K +ota_1, app, ota_1, 0x450000, 3072K +user_fs, data, fat, 0x750000, 8896K diff --git a/ports/espressif/boards/adafruit_metro_esp32s3/sdkconfig b/ports/espressif/boards/adafruit_metro_esp32s3/sdkconfig index e9628662160..0f430f3d1dd 100644 --- a/ports/espressif/boards/adafruit_metro_esp32s3/sdkconfig +++ b/ports/espressif/boards/adafruit_metro_esp32s3/sdkconfig @@ -12,3 +12,9 @@ # end of Component config # end of Espressif IoT Development Framework Configuration +# Env Hub frozen libraries exceed the default 2 MB application slot. +# Preserve TinyUF2's factory offset; back up user_fs before upgrading. +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="boards/adafruit_metro_esp32s3/partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="boards/adafruit_metro_esp32s3/partitions.csv" +# Fit the Env Hub server certificate chain in an outbound TLS record. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 From 4730c980cafabb18ed889d680db26ff4ffafeb28 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 22 Oct 2026 06:51:47 +0100 Subject: [PATCH 14/17] Fit Feather ESP32 V2 Env Hub image and TLS certificate records --- .../boards/adafruit_feather_esp32_v2/partitions.csv | 7 +++++++ .../espressif/boards/adafruit_feather_esp32_v2/sdkconfig | 8 ++++++++ 2 files changed, 15 insertions(+) create mode 100644 ports/espressif/boards/adafruit_feather_esp32_v2/partitions.csv diff --git a/ports/espressif/boards/adafruit_feather_esp32_v2/partitions.csv b/ports/espressif/boards/adafruit_feather_esp32_v2/partitions.csv new file mode 100644 index 00000000000..bb0a866752c --- /dev/null +++ b/ports/espressif/boards/adafruit_feather_esp32_v2/partitions.csv @@ -0,0 +1,7 @@ +# Env Hub build: 3 MiB OTA slots and 1984 KiB filesystem on the 8 MiB Feather V2. +# Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 20K +otadata, data, ota, 0xe000, 8K +ota_0, app, ota_0, 0x10000, 3072K +ota_1, app, ota_1, 0x310000, 3072K +user_fs, data, fat, 0x610000, 1984K diff --git a/ports/espressif/boards/adafruit_feather_esp32_v2/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32_v2/sdkconfig index e69de29bb2d..28b6626b786 100644 --- a/ports/espressif/boards/adafruit_feather_esp32_v2/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32_v2/sdkconfig @@ -0,0 +1,8 @@ +# Frozen Env Hub libraries exceed the original 2 MiB application slot. +# Updating this layout moves user_fs; back up and restore files when flashing. +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="boards/adafruit_feather_esp32_v2/partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="boards/adafruit_feather_esp32_v2/partitions.csv" + +# Allow the collector's certificate chain to fit in an outbound TLS record. +# Keep the upstream 16 KiB inbound record limit. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 From cb5301b6fb9ede7442d84e8aba72baad87430a80 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 22 Oct 2026 08:18:06 +0100 Subject: [PATCH 15/17] espressif: size Env Hub TLS records for complete current CA chains --- ports/espressif/boards/adafruit_feather_esp32_v2/sdkconfig | 3 ++- .../adafruit_feather_esp32c6_4mbflash_nopsram/sdkconfig | 3 ++- .../adafruit_feather_esp32s3_4mbflash_2mbpsram/sdkconfig | 3 ++- .../boards/adafruit_feather_esp32s3_nopsram/sdkconfig | 3 ++- ports/espressif/boards/adafruit_metro_esp32s3/sdkconfig | 3 ++- .../boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig | 5 +++-- .../boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig | 5 +++-- .../boards/espressif_esp32c6_devkitc_1_n8/sdkconfig | 5 +++-- .../boards/espressif_esp32s3_devkitc_1_n8/sdkconfig | 5 +++-- 9 files changed, 22 insertions(+), 13 deletions(-) diff --git a/ports/espressif/boards/adafruit_feather_esp32_v2/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32_v2/sdkconfig index 28b6626b786..1940c5a74e2 100644 --- a/ports/espressif/boards/adafruit_feather_esp32_v2/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32_v2/sdkconfig @@ -5,4 +5,5 @@ CONFIG_PARTITION_TABLE_FILENAME="boards/adafruit_feather_esp32_v2/partitions.csv # Allow the collector's certificate chain to fit in an outbound TLS record. # Keep the upstream 16 KiB inbound record limit. -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# The current three-certificate chain needs 4159 bytes including TLS framing. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=8192 diff --git a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/sdkconfig index 28916dbb8bd..a3c8f48f955 100644 --- a/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/sdkconfig @@ -13,4 +13,5 @@ # end of Espressif IoT Development Framework Configuration # Fit the Env Hub server certificate chain in an outbound TLS record. -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# The current three-certificate chain needs 4159 bytes including TLS framing. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=8192 diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/sdkconfig index 28916dbb8bd..a3c8f48f955 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32s3_4mbflash_2mbpsram/sdkconfig @@ -13,4 +13,5 @@ # end of Espressif IoT Development Framework Configuration # Fit the Env Hub server certificate chain in an outbound TLS record. -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# The current three-certificate chain needs 4159 bytes including TLS framing. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=8192 diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/sdkconfig index 28916dbb8bd..a3c8f48f955 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32s3_nopsram/sdkconfig @@ -13,4 +13,5 @@ # end of Espressif IoT Development Framework Configuration # Fit the Env Hub server certificate chain in an outbound TLS record. -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# The current three-certificate chain needs 4159 bytes including TLS framing. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=8192 diff --git a/ports/espressif/boards/adafruit_metro_esp32s3/sdkconfig b/ports/espressif/boards/adafruit_metro_esp32s3/sdkconfig index 0f430f3d1dd..51f3f883e07 100644 --- a/ports/espressif/boards/adafruit_metro_esp32s3/sdkconfig +++ b/ports/espressif/boards/adafruit_metro_esp32s3/sdkconfig @@ -17,4 +17,5 @@ CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="boards/adafruit_metro_esp32s3/partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="boards/adafruit_metro_esp32s3/partitions.csv" # Fit the Env Hub server certificate chain in an outbound TLS record. -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# The current three-certificate chain needs 4159 bytes including TLS framing. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=8192 diff --git a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig index 3dd75028f3a..dc35b010ca2 100644 --- a/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig +++ b/ports/espressif/boards/espressif_esp32c5_devkitc_1_n8r8/sdkconfig @@ -13,8 +13,9 @@ # end of Espressif IoT Development Framework Configuration # Allow the collector's TLS certificate chain to fit in a record. -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 -CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 +# The current three-certificate chain needs 4159 bytes including TLS framing. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=8192 +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 # Frozen libraries need a larger application slot; back up user_fs before flashing. CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="boards/espressif_esp32c5_devkitc_1_n8r8/partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="boards/espressif_esp32c5_devkitc_1_n8r8/partitions.csv" diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig index 6cf26f3380c..c19c2594eac 100644 --- a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig @@ -13,5 +13,6 @@ # end of Espressif IoT Development Framework Configuration # Allow the collector's TLS certificate chain to fit in a record. -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 -CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 +# The current three-certificate chain needs 4159 bytes including TLS framing. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=8192 +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 diff --git a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig index 79a70a18721..b372712b4ca 100644 --- a/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig +++ b/ports/espressif/boards/espressif_esp32c6_devkitc_1_n8/sdkconfig @@ -14,8 +14,9 @@ # end of Espressif IoT Development Framework Configuration # The collector serves a certificate chain larger than the upstream 2 KB record. -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 -CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 +# The current three-certificate chain needs 4159 bytes including TLS framing. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=8192 +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 # Frozen collector libraries exceed the upstream 2 MB application slot on C6. # This layout moves user_fs: back up CIRCUITPY before installing or reverting. diff --git a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/sdkconfig b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/sdkconfig index d4399467e7f..095c89509ee 100644 --- a/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/sdkconfig +++ b/ports/espressif/boards/espressif_esp32s3_devkitc_1_n8/sdkconfig @@ -13,5 +13,6 @@ # end of Espressif IoT Development Framework Configuration # The collector serves a certificate chain larger than the upstream 2 KB record. -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 -CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=4096 +# The current three-certificate chain needs 4159 bytes including TLS framing. +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=8192 +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 From ea06dcb44e3b953e3b750b28e2e60538d9904fa7 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 23 Oct 2026 02:46:54 +0100 Subject: [PATCH 16/17] espressif: synchronize complete ESP-NOW receive packets --- ports/espressif/bindings/espnow/ESPNow.c | 4 +- ports/espressif/common-hal/espnow/ESPNow.c | 51 +++-- ports/espressif/common-hal/espnow/ESPNow.h | 2 + tools/check_espnow_rx_lock.py | 250 +++++++++++++++++++++ 4 files changed, 288 insertions(+), 19 deletions(-) create mode 100755 tools/check_espnow_rx_lock.py diff --git a/ports/espressif/bindings/espnow/ESPNow.c b/ports/espressif/bindings/espnow/ESPNow.c index 345d51b6dff..409490fad93 100644 --- a/ports/espressif/bindings/espnow/ESPNow.c +++ b/ports/espressif/bindings/espnow/ESPNow.c @@ -308,7 +308,7 @@ static mp_uint_t espnow_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintpt case MP_STREAM_POLL: { mp_uint_t flags = arg; mp_uint_t ret = 0; - if ((flags & MP_STREAM_POLL_RD) && ringbuf_num_filled(self->recv_buffer) > 0) { + if ((flags & MP_STREAM_POLL_RD) && common_hal_espnow_get_bytes_available(self) > 0) { ret |= MP_STREAM_POLL_RD; } return ret; @@ -337,7 +337,7 @@ static const mp_stream_p_t espnow_stream_p = { static mp_obj_t espnow_unary_op(mp_unary_op_t op, mp_obj_t self_in) { espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); espnow_check_for_deinit(self); - size_t len = ringbuf_num_filled(self->recv_buffer); + size_t len = common_hal_espnow_get_bytes_available(self); switch (op) { case MP_UNARY_OP_BOOL: return mp_obj_new_bool(len != 0); diff --git a/ports/espressif/common-hal/espnow/ESPNow.c b/ports/espressif/common-hal/espnow/ESPNow.c index eb3b57174a3..1dbd42d0afa 100644 --- a/ports/espressif/common-hal/espnow/ESPNow.c +++ b/ports/espressif/common-hal/espnow/ESPNow.c @@ -20,6 +20,8 @@ #include "esp_now.h" #include "mphalport.h" +#include "freertos/FreeRTOS.h" + #include "esp_now.h" #define ESPNOW_MAGIC 0x99 @@ -52,6 +54,10 @@ typedef struct { uint8_t msg[0]; // Message is up to 250 bytes } __attribute__((packed)) espnow_packet_t; +// The Wi-Fi task may run on a different core from the VM. Protect complete +// packets and the ring buffer's shared counters, not just individual copies. +static portMUX_TYPE recv_buffer_mutex = portMUX_INITIALIZER_UNLOCKED; + // --- The ESP-NOW send and recv callback routines --- // Callback triggered when a sent packet is acknowledged by the peer (or not). @@ -73,8 +79,11 @@ static void recv_cb(const esp_now_recv_info_t *esp_now_info, const uint8_t *msg, espnow_obj_t *self = MP_STATE_PORT(espnow_singleton); ringbuf_t *buf = self->recv_buffer; - if (sizeof(espnow_packet_t) + msg_len > ringbuf_num_empty(buf)) { + portENTER_CRITICAL(&recv_buffer_mutex); + if (msg_len < 0 || msg_len > ESP_NOW_MAX_DATA_LEN || + sizeof(espnow_packet_t) + msg_len > ringbuf_num_empty(buf)) { self->read_failure++; + portEXIT_CRITICAL(&recv_buffer_mutex); return; } @@ -89,6 +98,7 @@ static void recv_cb(const esp_now_recv_info_t *esp_now_info, const uint8_t *msg, ringbuf_put_n(buf, msg, msg_len); self->read_success++; + portEXIT_CRITICAL(&recv_buffer_mutex); } bool common_hal_espnow_deinited(espnow_obj_t *self) { @@ -183,33 +193,40 @@ mp_obj_t common_hal_espnow_send(espnow_obj_t *self, const mp_buffer_info_t *mess return mp_const_none; } +size_t common_hal_espnow_get_bytes_available(espnow_obj_t *self) { + portENTER_CRITICAL(&recv_buffer_mutex); + size_t available = ringbuf_num_filled(self->recv_buffer); + portEXIT_CRITICAL(&recv_buffer_mutex); + return available; +} + mp_obj_t common_hal_espnow_read(espnow_obj_t *self) { + // Copy the entire packet while locked. Allocating Python objects or raising + // an exception must happen after releasing the cross-core critical section. + espnow_header_t header; + uint8_t mac_buf[ESP_NOW_ETH_ALEN]; + uint8_t msg_buf[ESP_NOW_MAX_DATA_LEN]; + + portENTER_CRITICAL(&recv_buffer_mutex); if (!ringbuf_num_filled(self->recv_buffer)) { + portEXIT_CRITICAL(&recv_buffer_mutex); return mp_const_none; } - // Read the packet header from the incoming buffer - espnow_header_t header; - if (ringbuf_get_n(self->recv_buffer, (uint8_t *)&header, sizeof(header)) != sizeof(header)) { - mp_arg_error_invalid(MP_QSTR_buffer); - } - - uint8_t msg_len = header.msg_len; - - uint8_t mac_buf[ESP_NOW_ETH_ALEN]; - uint8_t msg_buf[msg_len]; + bool valid = ringbuf_get_n(self->recv_buffer, (uint8_t *)&header, sizeof(header)) == sizeof(header) && + header.magic == ESPNOW_MAGIC && + header.msg_len <= ESP_NOW_MAX_DATA_LEN && + ringbuf_get_n(self->recv_buffer, mac_buf, ESP_NOW_ETH_ALEN) == ESP_NOW_ETH_ALEN && + ringbuf_get_n(self->recv_buffer, msg_buf, header.msg_len) == header.msg_len; + portEXIT_CRITICAL(&recv_buffer_mutex); - // Check the message packet header format and read the message data - if (header.magic != ESPNOW_MAGIC || - msg_len > ESP_NOW_MAX_DATA_LEN || - ringbuf_get_n(self->recv_buffer, mac_buf, ESP_NOW_ETH_ALEN) != ESP_NOW_ETH_ALEN || - ringbuf_get_n(self->recv_buffer, msg_buf, msg_len) != msg_len) { + if (!valid) { mp_arg_error_invalid(MP_QSTR_buffer); } mp_obj_t elems[4] = { mp_obj_new_bytes(mac_buf, ESP_NOW_ETH_ALEN), - mp_obj_new_bytes(msg_buf, msg_len), + mp_obj_new_bytes(msg_buf, header.msg_len), MP_OBJ_NEW_SMALL_INT(header.rssi), mp_obj_new_int(header.time_ms), }; diff --git a/ports/espressif/common-hal/espnow/ESPNow.h b/ports/espressif/common-hal/espnow/ESPNow.h index b68e1544fb8..c842eb2db2d 100644 --- a/ports/espressif/common-hal/espnow/ESPNow.h +++ b/ports/espressif/common-hal/espnow/ESPNow.h @@ -37,3 +37,5 @@ extern void common_hal_espnow_set_pmk(espnow_obj_t *self, const uint8_t *key); extern mp_obj_t common_hal_espnow_send(espnow_obj_t *self, const mp_buffer_info_t *message, const uint8_t *mac); extern mp_obj_t common_hal_espnow_read(espnow_obj_t *self); + +extern size_t common_hal_espnow_get_bytes_available(espnow_obj_t *self); diff --git a/tools/check_espnow_rx_lock.py b/tools/check_espnow_rx_lock.py new file mode 100755 index 00000000000..e3ab0a78f73 --- /dev/null +++ b/tools/check_espnow_rx_lock.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +"""Exercise actual ESP-NOW packet functions with deterministic two-thread scheduling. + +Requires a POSIX C compiler. Kernel critical sections are mapped to pthread +mutexes; packet and ring-buffer algorithms are extracted unchanged from source. +""" +import argparse +import hashlib +import json +from pathlib import Path +import re +import subprocess +import tempfile + + +def function(text, name): + start = re.search(r'^[\w *]+\b' + re.escape(name) + r'\([^;]*?\)\s*\{', text, re.M).start() + opening = text.index('{', start) + depth, end = 1, opening + 1 + while depth: + depth += (text[end] == '{') - (text[end] == '}') + end += 1 + return text[start:end] + + +PREFIX = r''' +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define ESPNOW_MAGIC 0x99 +#define ESP_NOW_ETH_ALEN 6 +#define ESP_NOW_MAX_DATA_LEN 250 +#define m_malloc malloc +#define portMUX_TYPE pthread_mutex_t +#define portMUX_INITIALIZER_UNLOCKED PTHREAD_MUTEX_INITIALIZER +static _Thread_local int locked, is_worker; +static atomic_int worker_attempted, worker_go, worker_done; +static void enter(pthread_mutex_t *m) { + if (is_worker) atomic_store(&worker_attempted,1); + assert(pthread_mutex_lock(m)==0); locked++; +} +static void leave(pthread_mutex_t *m) { + assert(locked==1); locked--; assert(pthread_mutex_unlock(m)==0); +} +#define portENTER_CRITICAL(m) enter(m) +#define portEXIT_CRITICAL(m) leave(m) +typedef uintptr_t mp_obj_t; +typedef int mp_obj_type_t; +static int espnow_packet_type_obj; +#define mp_const_none ((mp_obj_t)0) +#define MP_QSTR_buffer 1 +#define MP_OBJ_NEW_SMALL_INT(v) ((mp_obj_t)(v)) +static _Thread_local jmp_buf error_target; +static _Thread_local uint8_t got_mac[6],got_payload[250]; +static _Thread_local size_t got_length; +static _Thread_local int bytes_index; +static void mp_arg_error_invalid(int q) { (void)q; assert(!locked); longjmp(error_target,1); } +static mp_obj_t mp_obj_new_bytes(const uint8_t *p,size_t n) { + assert(!locked); + if (bytes_index++==0) { assert(n==6); memcpy(got_mac,p,n); } + else { assert(n<=250);memcpy(got_payload,p,n);got_length=n; } + return 1; +} +static mp_obj_t mp_obj_new_int(uint32_t n) { assert(!locked);return n; } +static mp_obj_t namedtuple_make_new(const mp_obj_type_t *t,size_t n,size_t k,const mp_obj_t *e) { + (void)t;(void)n;(void)k;(void)e;assert(!locked);return 1; +} +static uint32_t mp_hal_ticks_ms(void) { return 123; } +typedef struct { int8_t rssi; } rx_ctrl_t; +typedef struct { const uint8_t *src_addr; rx_ctrl_t *rx_ctrl; } esp_now_recv_info_t; +''' + +MIDDLE = r''' +typedef struct { ringbuf_t *recv_buffer;unsigned read_failure,read_success; } espnow_obj_t; +static espnow_obj_t obj; +#define MP_STATE_PORT(x) (&obj) +static int inject_put,inject_get; +static void scheduling_point(void) { + atomic_store(&worker_go,1); + // On the fixed source the worker reaches the lock but cannot enter. On the + // baseline it finishes reading the partial packet before the writer resumes. + while (!atomic_load(&worker_attempted) && !atomic_load(&worker_done)) sched_yield(); +} +static size_t scheduling_put(ringbuf_t *r,const uint8_t *b,size_t n) { + size_t result=ringbuf_put_n(r,b,n); + if (inject_put && !is_worker) { inject_put=0;scheduling_point(); } + return result; +} +static size_t scheduling_get(ringbuf_t *r,uint8_t *b,size_t n) { + size_t result=ringbuf_get_n(r,b,n); + if (inject_get && !is_worker) { inject_get=0;scheduling_point(); } + return result; +} +#define ringbuf_put_n scheduling_put +#define ringbuf_get_n scheduling_get +''' + +MAIN = r''' +#undef ringbuf_put_n +#undef ringbuf_get_n +static uint8_t storage[1024],mac[6]={2,3,4,5,6,7},payload[250]; +static ringbuf_t rb; +static rx_ctrl_t rx={-30}; +static esp_now_recv_info_t info={mac,&rx}; +static int worker_action,worker_result; +static size_t worker_available; +static void reset_case(size_t capacity) { + assert(!locked);ringbuf_init(&rb,storage,capacity);obj=(espnow_obj_t){.recv_buffer=&rb}; + inject_put=inject_get=0;atomic_store(&worker_go,0);atomic_store(&worker_done,0); + atomic_store(&worker_attempted,0);worker_result=0;worker_available=0; +} +static int safe_read(void) { + bytes_index=0; + if (setjmp(error_target)) return -1; + return common_hal_espnow_read(&obj) ? 1 : 0; +} +static void check_payload(size_t length) { + assert(got_length==length && memcmp(got_mac,mac,6)==0); + assert(memcmp(got_payload,payload,length)==0); +} +static void *worker(void *unused) { + (void)unused;is_worker=1; + while(!atomic_load(&worker_go)) sched_yield(); + if(worker_action==0) { worker_result=safe_read();if(worker_result==1) check_payload(20); } + else if(worker_action==1) recv_cb(&info,payload,20); + else worker_available=common_hal_espnow_get_bytes_available(&obj); + assert(!locked);atomic_store(&worker_done,1);return NULL; +} +static void start_worker(pthread_t *t,int action) { + worker_action=action;assert(pthread_create(t,NULL,worker,NULL)==0); +} +static atomic_int producer_done; +static unsigned delivered; +static void *producer(void *unused) { + (void)unused; + uint8_t p[250]; + for(uint32_t seq=1;seq<=20000;seq++) { + memcpy(p,&seq,4);for(size_t k=4;k=0); + if(result) { + assert(got_length==250 && memcmp(got_mac,mac,6)==0); + uint32_t seq;memcpy(&seq,got_payload,4);assert(seq>previous && seq<=20000);previous=seq; + for(size_t k=4;k<250;k++)assert(got_payload[k]==(uint8_t)(seq+k)); + delivered++; + } else sched_yield(); + } + return NULL; +} +int main(void) { + memset(payload,0x42,sizeof(payload));pthread_t t; + reset_case(sizeof(storage));assert(safe_read()==0);assert(!locked); + puts("PASS empty_read_unlocks"); + for(size_t n=0;n<=250;n+=(n==0?1:249)) { + reset_case(sizeof(espnow_packet_t)+n);recv_cb(&info,payload,n); + assert(safe_read()==1);check_payload(n);assert(rb.used==0 && obj.read_success==1); + } + puts("PASS zero_one_maximum_payload_exact_capacity"); + reset_case(sizeof(espnow_packet_t)+19);recv_cb(&info,payload,20); + assert(obj.read_failure==1 && rb.used==0 && !locked); + puts("PASS insufficient_capacity_drops_whole_packet_and_unlocks"); + reset_case(sizeof(storage));start_worker(&t,0);inject_put=1; + recv_cb(&info,payload,20);assert(pthread_join(t,NULL)==0); + if(EXPECT_RACE) { + assert(worker_result==-1 && safe_read()==-1); + puts("REPRODUCED partial_header_read_and_corrupt_followup_on_baseline");return 0; + } + assert(worker_result==1 && rb.used==0 && obj.read_success==1); + puts("PASS reader_waits_for_complete_packet"); + reset_case(sizeof(storage));start_worker(&t,2);inject_put=1; + recv_cb(&info,payload,20);assert(pthread_join(t,NULL)==0); + assert(worker_available==sizeof(espnow_packet_t)+20 && safe_read()==1); + puts("PASS readiness_waits_for_complete_packet"); + reset_case(sizeof(storage));recv_cb(&info,payload,20);start_worker(&t,1);inject_get=1; + assert(safe_read()==1);check_payload(20);assert(pthread_join(t,NULL)==0); + assert(safe_read()==1);check_payload(20);assert(rb.used==0); + puts("PASS producer_waits_for_complete_dequeue"); + reset_case(sizeof(espnow_packet_t)+250); + for(int i=0;i<400;i++) { + size_t n=(size_t)(i%251);recv_cb(&info,payload,n);assert(safe_read()==1);check_payload(n); + } + puts("PASS wraparound_variable_lengths"); + reset_case(sizeof(storage));recv_cb(&info,payload,-1);recv_cb(&info,payload,251); + assert(obj.read_failure==2 && rb.used==0 && !locked); + puts("PASS_invalid_callback_lengths_drop_and_unlock"); + for(int corrupt=0;corrupt<4;corrupt++) { + reset_case(sizeof(storage));recv_cb(&info,payload,20); + if(corrupt==0)storage[0]=0; + if(corrupt==1)storage[1]=251; + if(corrupt==2)rb.used=2; + if(corrupt==3)rb.used=sizeof(espnow_packet_t)+19; + assert(safe_read()==-1 && !locked); + } + puts("PASS malformed_header_and_truncation_raise_after_unlock"); + reset_case(sizeof(storage));atomic_store(&producer_done,0);delivered=0; + pthread_t p,c;assert(pthread_create(&c,NULL,consumer,NULL)==0); + assert(pthread_create(&p,NULL,producer,NULL)==0); + assert(pthread_join(p,NULL)==0 && pthread_join(c,NULL)==0); + assert(delivered>0 && delivered==obj.read_success && delivered+obj.read_failure==20000 && rb.used==0); + printf("PASS concurrent_20000_packets_integrity_order_accounting delivered=%u dropped=%u\n",delivered,obj.read_failure); + return 0; +} +''' + + +def main(): + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('circuitpython',type=Path) + parser.add_argument('--revision') + parser.add_argument('--expect-race',action='store_true') + parser.add_argument('--output',type=Path,required=True) + args=parser.parse_args();root=args.circuitpython.resolve() + files=['ports/espressif/common-hal/espnow/ESPNow.c','py/ringbuf.c','py/ringbuf.h'] + def read(p): + if args.revision: + return subprocess.check_output(['git','-C',str(root),'show',f'{args.revision}:{p}'],text=True) + return (root/p).read_text() + sources={p:read(p) for p in files};esp,ring,header=(sources[p] for p in files) + header=header.replace('#include "py/gc.h"','').replace('#pragma once','') + ring=ring.replace('#include "ringbuf.h"','') + types=esp[esp.index('typedef struct {'):esp.index('// --- The ESP-NOW send')] + available='common_hal_espnow_get_bytes_available' + getter=function(esp,available) if available in esp else f'size_t {available}(espnow_obj_t *self) {{return ringbuf_num_filled(self->recv_buffer);}}' + code=PREFIX+header+types+ring+MIDDLE+getter+function(esp,'common_hal_espnow_read')+function(esp,'recv_cb')+MAIN + code=f'#define EXPECT_RACE {int(args.expect_race)}\n'+code + with tempfile.TemporaryDirectory(prefix='espnow-rx-lock-') as temp: + src=Path(temp)/'check.c';exe=Path(temp)/'check';src.write_text(code) + subprocess.run(['cc','-std=c11','-O2','-Wall','-Wextra','-Werror','-Wno-unused-function','-pthread',str(src),'-o',str(exe)],check=True) + result=subprocess.run([str(exe)],capture_output=True,text=True,timeout=30) + data=dict(revision=args.revision or 'working-tree',source_sha256={p:hashlib.sha256(v.encode()).hexdigest() for p,v in sources.items()},generated_c_sha256=hashlib.sha256(code.encode()).hexdigest(),expected_race=args.expect_race,returncode=result.returncode,checks=result.stdout.splitlines(),stderr=result.stderr,scope='Actual packet/ring-buffer source with pthread critical sections, deterministic scheduling and concurrent integrity tests; not physical incidence data') + args.output.write_text(json.dumps(data,indent=2)+'\n');print(json.dumps(data,indent=2)) + raise SystemExit(result.returncode) + + +if __name__=='__main__':main() From e46718f34b321ae8021a19b99d80b45bdcd2a3a7 Mon Sep 17 00:00:00 2001 From: tyeth Date: Fri, 23 Oct 2026 14:33:39 +0100 Subject: [PATCH 17/17] Clarify ESP-NOW default sends exclude broadcast peers --- ports/espressif/bindings/espnow/ESPNow.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/espressif/bindings/espnow/ESPNow.c b/ports/espressif/bindings/espnow/ESPNow.c index 409490fad93..f302cf7810a 100644 --- a/ports/espressif/bindings/espnow/ESPNow.c +++ b/ports/espressif/bindings/espnow/ESPNow.c @@ -109,7 +109,10 @@ static MP_DEFINE_CONST_FUN_OBJ_1(espnow_deinit_obj, espnow_deinit); //| This blocks until a timeout of ``2`` seconds if the ESP-NOW internal buffers are full. //| //| :param ReadableBuffer message: The message to send (length <= 250 bytes). -//| :param Peer peer: Send message to this peer. If `None`, send to all registered peers. +//| :param Peer peer: Send message to this peer. If `None`, send to registered +//| unicast peers only. To broadcast or multicast, pass the corresponding +//| registered `Peer` explicitly. If no unicast peers are registered, +//| sending with `None` raises `OSError`. //| """ //| ... //|