From 1a984b006d1c97043d3adf14d3f6377a596be451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=27=C3=A9lectron=20rare?= <108685187+electron-rare@users.noreply.github.com> Date: Sun, 3 May 2026 16:21:38 +0200 Subject: [PATCH] fix: restore PIO build (3.20014 + JsonV7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pinned commit 6129375 accumulated incompatibilities with the current toolchain (Arduino-ESP32 3.20014, ESP-IDF v5 underneath) and ArduinoJson v7 (which the codebase already uses via .to<>() / .add<>() syntax). Fresh installs hit five distinct build errors. This commit fixes them so the firmware compiles and flashes cleanly on a Freenove ESP32-S3 WROOM N16R8 board. Library version: - platformio.ini: bumped bblanchon/ArduinoJson from 6.21.5 to ^7.2.0. The codebase uses v7 API patterns (doc[..].to(), arr.add()) in 7 files, so v7 is the source of truth. Mixed v6 createNested* calls remain in 4 files and are forward- compatible. Build excludes: - platformio.ini build_src_filter: skip src/audio/audio_ble_control.cpp. The .cpp was committed without its header (audio/audio_ble_control.h) and has zero callers — dead code that broke compile. Header / signature fixes: - include/npc/npc_engine.h: added so size_t is declared (npc_build_sd_path uses it in the public API). - src/npc/game_coordinator.cpp: changed game_coordinator_phase return type from game_phase_t to int to match the extern "C" header declaration. The original cast remains (just on the return value). - src/npc/espnow_master.cpp: on_recv now uses the legacy (const uint8_t* mac_addr, ...) callback signature matching Arduino-ESP32 3.20014's esp_now.h. The esp_now_recv_info_t* form is IDF v5 only and not yet surfaced through the Arduino wrapper. Boot-time crash fix (lwip "Invalid mbox" panic, reset reason 4): - src/app/main.cpp: gate voiceWsInit/voiceWsConnect behind !g_boot_network_deferred && !kBootEspNowOnlyMode. voiceWsConnect() calls into s_ws.begin() -> lwip tcpip_send_msg, which panics if the TCP/IP stack is not yet initialised. The bug was dormant because earlier builds presumably had NET coming up before VOICE; with espnow_only_mode + deferred network bringup paths the ordering became visible. Acceptance: - `pio run` succeeds (RAM 79%, Flash 65%). - `pio run -t upload` flashes 2.7 MB in 38s, all hashes verified. - Boot stable: SD_MMC mounted, LittleFS ready, reset=1, no panic over 30s monitor (90k UI frames). Out of scope: - 2 macOS Finder duplicate font sources (ui_freenove_allinone/src/ui/fonts/lv_font_* 2.c) were removed locally to unblock the linker (multiple definition errors). They are untracked Finder artefacts and should never have appeared on disk. --- platformio.ini | 3 ++- ui_freenove_allinone/include/npc/npc_engine.h | 1 + ui_freenove_allinone/src/app/main.cpp | 11 +++++++++-- ui_freenove_allinone/src/npc/espnow_master.cpp | 7 +++++-- ui_freenove_allinone/src/npc/game_coordinator.cpp | 4 ++-- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/platformio.ini b/platformio.ini index 255ca69..54b720f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,6 +23,7 @@ board_build.filesystem = littlefs build_src_filter = -<*> + + - monitor_speed = 115200 monitor_filters = time, esp32_exception_decoder monitor_echo = yes @@ -34,7 +35,7 @@ lib_deps = lovyan03/LovyanGFX@1.2.7 lvgl/lvgl@8.4.0 esphome/ESP32-audioI2S@2.3.0 - bblanchon/ArduinoJson@6.21.5 + bblanchon/ArduinoJson@^7.2.0 adafruit/Adafruit NeoPixel@1.12.3 links2004/WebSockets@^2.4.0 build_flags = diff --git a/ui_freenove_allinone/include/npc/npc_engine.h b/ui_freenove_allinone/include/npc/npc_engine.h index 79145bd..b40b012 100644 --- a/ui_freenove_allinone/include/npc/npc_engine.h +++ b/ui_freenove_allinone/include/npc/npc_engine.h @@ -4,6 +4,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { diff --git a/ui_freenove_allinone/src/app/main.cpp b/ui_freenove_allinone/src/app/main.cpp index 5709d5a..c4658ab 100644 --- a/ui_freenove_allinone/src/app/main.cpp +++ b/ui_freenove_allinone/src/app/main.cpp @@ -7783,8 +7783,15 @@ void setup() { } g_next_espnow_discovery_ms = millis() + 2000U; - // Voice pipeline — connect to voice bridge server - { + // Voice pipeline — connect to voice bridge server. + // Skip when network boot is deferred: voiceWsConnect() calls into lwip + // (s_ws.begin -> tcpip_send_msg) which panics with "Invalid mbox" if the + // TCP/IP stack is not yet initialised. Same applies to espnow-only mode. + if (g_boot_network_deferred) { + Serial.println("[VOICE] init skipped (network_boot_deferred=1)"); + } else if (kBootEspNowOnlyMode) { + Serial.println("[VOICE] init skipped (espnow_only_mode=1)"); + } else { zacus::voice::VoiceWsConfig voice_cfg; voice_cfg.server_url = "ws://192.168.0.120:8200/voice/ws"; voice_cfg.device_id = g_network_cfg.hostname[0] != '\0' diff --git a/ui_freenove_allinone/src/npc/espnow_master.cpp b/ui_freenove_allinone/src/npc/espnow_master.cpp index 02ff587..a116763 100644 --- a/ui_freenove_allinone/src/npc/espnow_master.cpp +++ b/ui_freenove_allinone/src/npc/espnow_master.cpp @@ -42,11 +42,14 @@ struct MasterRecvItem { // Wi-Fi ISR callbacks // --------------------------------------------------------------------------- -static void on_recv(const esp_now_recv_info_t *info, +// Note: SDK Arduino-ESP32 3.20014 uses the legacy callback signature +// (const uint8_t* mac_addr, ...). IDF v5 introduced esp_now_recv_info_t. +// Use the legacy form here so the firmware builds with the pinned SDK. +static void on_recv(const uint8_t *mac_addr, const uint8_t *data, int len) { MasterRecvItem item; - memcpy(item.src_mac, info->src_addr, 6); + memcpy(item.src_mac, mac_addr, 6); int copy = len < (int)sizeof(item.data) ? len : (int)sizeof(item.data); memcpy(item.data, data, copy); item.len = copy; diff --git a/ui_freenove_allinone/src/npc/game_coordinator.cpp b/ui_freenove_allinone/src/npc/game_coordinator.cpp index 6286a35..3aeb0bb 100644 --- a/ui_freenove_allinone/src/npc/game_coordinator.cpp +++ b/ui_freenove_allinone/src/npc/game_coordinator.cpp @@ -323,8 +323,8 @@ void game_coordinator_tick(void) { } } -game_phase_t game_coordinator_phase(void) { - return (game_phase_t)s_game.phase; +int game_coordinator_phase(void) { + return (int)s_game.phase; } uint32_t game_coordinator_score(void) {