velxio/backend/app/services/esp-idf-template/main/CMakeLists.txt

144 lines
7.0 KiB
CMake
Raw Normal View History

feat(esp32): pure ESP-IDF language mode for the ESP32 family (#139) Adds a third entry to the board language selector next to Arduino C++ and MicroPython: ESP-IDF. In this mode the user writes a plain ESP-IDF project — app_main() entry point, FreeRTOS + driver APIs — and the backend compiles it through the same ESP-IDF toolchain it already uses for ESP32 Arduino sketches, just without the arduino-esp32 component. Backend: - CompileRequest.language ('espidf') threaded through the sync + async compile paths and folded into the dedup job key (language='arduino' and omitted hash identically so old clients keep dedupping). - espidf_compiler: pure_idf flag. User files are written into main/ as-is (no Arduino.h wrap, no velxio_compat.h, Arduino library resolution skipped), ARDUINO_ESP32_PATH is dropped from the build env and VELXIO_PURE_SKETCH raised so the template CMake compiles the user's own sources via a glob branch. Pure builds get their own persistent build-dir variant through the eff_hash fold. - QEMU WiFi compat for IDF-style code: esp_wifi.h/esp_wifi_init detection sets has_wifi, and literal #define SSID/PASS plus wifi_config_t designated initializers are normalized to the QEMU AP. - CONFIG_ARDUINO_* lines are stripped from sdkconfig.defaults in pure mode (the symbols don't exist without the arduino component). Frontend: - LanguageMode gains 'espidf'; BOARD_SUPPORTS_ESPIDF covers the ESP32 family (Xtensa, S3, C3). Toolbar shows the option only for those. - Switching modes seeds a main.c blink skeleton (app_main + gpio driver), mirroring the MicroPython main.py flow. - compileCode sends language='espidf'; run/stop paths are unchanged (the QEMU worker consumes the same merged flash image). - New gallery example: esp32-idf-blink (LED + resistor on GPIO 2). Tests: unit coverage for the build-env switch, IDF wifi normalization, job-key variance, file-group seeding and the new example; verified end-to-end in a container from the prod image (pure build produces a bootable flash image; Arduino-mode build unchanged, same variant hash).
2026-07-24 11:37:01 +07:00
# Pure ESP-IDF language mode (issue #139): the user's files are the main
# component sources — they provide app_main() and use IDF APIs directly.
# espidf_compiler sets VELXIO_PURE_SKETCH (and drops ARDUINO_ESP32_PATH from
# the build env) when the frontend requests language='espidf'. As the `main`
# component, ESP-IDF gives these sources an implicit dependency on every
# other component in the build, so driver/, esp_wifi, esp_http_server etc.
# resolve without an explicit REQUIRES.
if(DEFINED ENV{VELXIO_PURE_SKETCH})
# No CONFIGURE_DEPENDS — see the comment on the Arduino glob below.
file(GLOB _user_srcs
"${CMAKE_CURRENT_LIST_DIR}/*.cpp"
"${CMAKE_CURRENT_LIST_DIR}/*.c")
idf_component_register(
SRCS ${_user_srcs}
INCLUDE_DIRS "."
)
# Same -Werror relaxations as the Arduino branch: IDF's defaults are
# ruthless for user code pasted from tutorials.
target_compile_options(${COMPONENT_LIB} PRIVATE
-Wno-error=comment
-Wno-error=parentheses
-Wno-error=sign-compare
-Wno-error=narrowing
-Wno-error=write-strings
-Wno-error=missing-field-initializers
-Wno-error=reorder
-Wno-error=unused-variable
-Wno-error=unused-but-set-variable)
# If Arduino component is available, use C++ main and link Arduino.
# The component name matches the directory basename of ARDUINO_ESP32_PATH
# (e.g. "arduino-esp32" when cloned from GitHub).
feat(esp32): pure ESP-IDF language mode for the ESP32 family (#139) Adds a third entry to the board language selector next to Arduino C++ and MicroPython: ESP-IDF. In this mode the user writes a plain ESP-IDF project — app_main() entry point, FreeRTOS + driver APIs — and the backend compiles it through the same ESP-IDF toolchain it already uses for ESP32 Arduino sketches, just without the arduino-esp32 component. Backend: - CompileRequest.language ('espidf') threaded through the sync + async compile paths and folded into the dedup job key (language='arduino' and omitted hash identically so old clients keep dedupping). - espidf_compiler: pure_idf flag. User files are written into main/ as-is (no Arduino.h wrap, no velxio_compat.h, Arduino library resolution skipped), ARDUINO_ESP32_PATH is dropped from the build env and VELXIO_PURE_SKETCH raised so the template CMake compiles the user's own sources via a glob branch. Pure builds get their own persistent build-dir variant through the eff_hash fold. - QEMU WiFi compat for IDF-style code: esp_wifi.h/esp_wifi_init detection sets has_wifi, and literal #define SSID/PASS plus wifi_config_t designated initializers are normalized to the QEMU AP. - CONFIG_ARDUINO_* lines are stripped from sdkconfig.defaults in pure mode (the symbols don't exist without the arduino component). Frontend: - LanguageMode gains 'espidf'; BOARD_SUPPORTS_ESPIDF covers the ESP32 family (Xtensa, S3, C3). Toolbar shows the option only for those. - Switching modes seeds a main.c blink skeleton (app_main + gpio driver), mirroring the MicroPython main.py flow. - compileCode sends language='espidf'; run/stop paths are unchanged (the QEMU worker consumes the same merged flash image). - New gallery example: esp32-idf-blink (LED + resistor on GPIO 2). Tests: unit coverage for the build-env switch, IDF wifi normalization, job-key variance, file-group seeding and the new example; verified end-to-end in a container from the prod image (pure build produces a bootable flash image; Arduino-mode build unchanged, same variant hash).
2026-07-24 11:37:01 +07:00
elseif(DEFINED ENV{ARDUINO_ESP32_PATH})
get_filename_component(_arduino_comp_name $ENV{ARDUINO_ESP32_PATH} NAME)
feat: ESP32-CAM emulation with webcam frame bridge First open-source end-to-end emulation of the AI-Thinker ESP32-CAM in QEMU, paired with a browser webcam → firmware bridge so users can develop camera sketches without hardware. Status: esp_camera_init() returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by the upstream driver. Final piece (cam_task accepting frames) is in progress — descriptor walker fix landed in this commit. Backend (Python/FastAPI): - simulation.py: camera_attach/frame/detach WS handlers - esp32_worker.py: ctypes binding to velxio_push_camera_frame + feature-detection fallback for older DLLs - esp32_lib_manager.py: forward camera commands to the worker stdin - esp-idf-template/main/CMakeLists.txt: esp32-camera headers added via add_prebuilt_library + REQUIRES driver (resolves i2c_master_* symbols). LED_BUILTIN=2 fallback for sketches that hardcode it. Frontend (React/TS): - EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family) added to isQemuBoard list — Run button now starts the QEMU bridge for these boards instead of falling through to the AVR path - useWebcamFrames.ts: getUserMedia → OffscreenCanvas → toBlob('image/jpeg') → base64 → WS at ~10 fps - CameraToggle.tsx: header button with status colors + frame counter - SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards - Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa - useSimulatorStore.ts: diagnostic log on compileBoardProgram - components-metadata.json: regen including esp32-cam component Submodule pointer: - wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32) Investigation + tests in test/test-esp32-cam/: - 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S spec, build blueprint, blockers resolved, descriptor walker fix) - 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip, webcam_demo) + 8 live + WS regression tests - README with the user-facing flow .gitignore: - libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated) - wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path, not part of this repo) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
# arduino-esp32 ships esp32-camera as a precompiled static lib but does
# NOT export its headers from its main CMakeLists. Sketches that
# #include "esp_camera.h" therefore fail to compile under the IDF
# build path. We add the bundled headers to the main component's
# INCLUDE_DIRS unconditionally — harmless for non-camera sketches and
# the only reliable path for camera sketches under our QEMU pipeline.
# Normalize Windows backslashes → forward slashes; otherwise CMake
# parses the resulting paths as escape sequences (\E, \U etc).
file(TO_CMAKE_PATH "$ENV{ARDUINO_ESP32_PATH}" _arduino_path_norm)
set(_cam_root "${_arduino_path_norm}/tools/sdk/${IDF_TARGET}/include/esp32-camera")
set(_cam_includes "")
if(EXISTS "${_cam_root}/driver/include")
list(APPEND _cam_includes "${_cam_root}/driver/include")
endif()
if(EXISTS "${_cam_root}/conversions/include")
list(APPEND _cam_includes "${_cam_root}/conversions/include")
endif()
# Glob every .cpp / .c that the espidf_compiler dropped into main/
# alongside main.cpp. Multi-file Arduino projects (robot-desktop-eyes,
# any wokwi.zip with helper .cpp files, etc.) define classes/free
# functions in their own translation units; if SRCS only names
# "main.cpp" the linker dies with "undefined reference to Foo::bar()"
# for every symbol that lives in a helper .cpp.
#
# No CONFIGURE_DEPENDS — ESP-IDF's component_get_requirements.cmake
# evaluates each component's CMakeLists in script mode where
# CONFIGURE_DEPENDS isn't supported, and the build aborts with
# "CONFIGURE_DEPENDS is invalid for script and find package modes".
# We don't need it anyway: espidf_compiler wipes + restores main/
# from the template on every compile (touching CMakeLists.txt's
# mtime), so cmake re-globs naturally on the next configure run.
file(GLOB _user_srcs
"${CMAKE_CURRENT_LIST_DIR}/*.cpp"
"${CMAKE_CURRENT_LIST_DIR}/*.c")
idf_component_register(
SRCS ${_user_srcs}
feat: ESP32-CAM emulation with webcam frame bridge First open-source end-to-end emulation of the AI-Thinker ESP32-CAM in QEMU, paired with a browser webcam → firmware bridge so users can develop camera sketches without hardware. Status: esp_camera_init() returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by the upstream driver. Final piece (cam_task accepting frames) is in progress — descriptor walker fix landed in this commit. Backend (Python/FastAPI): - simulation.py: camera_attach/frame/detach WS handlers - esp32_worker.py: ctypes binding to velxio_push_camera_frame + feature-detection fallback for older DLLs - esp32_lib_manager.py: forward camera commands to the worker stdin - esp-idf-template/main/CMakeLists.txt: esp32-camera headers added via add_prebuilt_library + REQUIRES driver (resolves i2c_master_* symbols). LED_BUILTIN=2 fallback for sketches that hardcode it. Frontend (React/TS): - EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family) added to isQemuBoard list — Run button now starts the QEMU bridge for these boards instead of falling through to the AVR path - useWebcamFrames.ts: getUserMedia → OffscreenCanvas → toBlob('image/jpeg') → base64 → WS at ~10 fps - CameraToggle.tsx: header button with status colors + frame counter - SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards - Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa - useSimulatorStore.ts: diagnostic log on compileBoardProgram - components-metadata.json: regen including esp32-cam component Submodule pointer: - wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32) Investigation + tests in test/test-esp32-cam/: - 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S spec, build blueprint, blockers resolved, descriptor walker fix) - 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip, webcam_demo) + 8 live + WS regression tests - README with the user-facing flow .gitignore: - libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated) - wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path, not part of this repo) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
INCLUDE_DIRS "." ${_cam_includes}
# `driver` brings in the i2c_master_* + i2c_cmd_link_* symbols
# libesp32-camera.a's sccb.c references at link time. arduino-esp32
# doesn't transitively expose it, so we name it here.
REQUIRES ${_arduino_comp_name} driver
)
feat: ESP32-CAM emulation with webcam frame bridge First open-source end-to-end emulation of the AI-Thinker ESP32-CAM in QEMU, paired with a browser webcam → firmware bridge so users can develop camera sketches without hardware. Status: esp_camera_init() returns ESP_OK; OV2640 chip-id verifies (PID/VER/MIDH/MIDL exactly match the datasheet); GPIO 25 VSYNC NEGEDGE interrupt enabled by the upstream driver. Final piece (cam_task accepting frames) is in progress — descriptor walker fix landed in this commit. Backend (Python/FastAPI): - simulation.py: camera_attach/frame/detach WS handlers - esp32_worker.py: ctypes binding to velxio_push_camera_frame + feature-detection fallback for older DLLs - esp32_lib_manager.py: forward camera commands to the worker stdin - esp-idf-template/main/CMakeLists.txt: esp32-camera headers added via add_prebuilt_library + REQUIRES driver (resolves i2c_master_* symbols). LED_BUILTIN=2 fallback for sketches that hardcode it. Frontend (React/TS): - EditorToolbar.tsx: ESP32-CAM (and the rest of the ESP32 family) added to isQemuBoard list — Run button now starts the QEMU bridge for these boards instead of falling through to the AVR path - useWebcamFrames.ts: getUserMedia → OffscreenCanvas → toBlob('image/jpeg') → base64 → WS at ~10 fps - CameraToggle.tsx: header button with status colors + frame counter - SimulatorCanvas.tsx: render CameraToggle for esp32-cam boards - Esp32Bridge.ts: sendCameraAttach/Frame/Detach + chunked btoa - useSimulatorStore.ts: diagnostic log on compileBoardProgram - components-metadata.json: regen including esp32-cam component Submodule pointer: - wokwi-libs/qemu-lcgamboa → ff8eee0 (camera devices commit on davidmonterocrespo24/qemu-lcgamboa branch picsimlab-esp32) Investigation + tests in test/test-esp32-cam/: - 13 autosearch markdown docs (overview, SOTA, OV2640 spec, DVP/I2S spec, build blueprint, blockers resolved, descriptor walker fix) - 5 sketches (camera_init, sccb_probe, dma_smoke, frame_roundtrip, webcam_demo) + 8 live + WS regression tests - README with the user-facing flow .gitignore: - libqemu-*.dll.{pre-camera,new,bak} (rollback points, regenerated) - wokwi-libs/esp32-camera/ (clone consumed by arduino-esp32 path, not part of this repo) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:28:55 +07:00
# Link the precompiled libesp32-camera.a so esp_camera_init / fb_get
# symbols resolve at link time. The .a ships under the same arduino-esp32
# tree; if it is missing (older bundle) we silently skip.
# Use ESP-IDF's add_prebuilt_library so cmake creates a proper imported
# target with REQUIRES for driver (the .a depends on i2c_master_* +
# i2c_cmd_link_*) — that way the linker resolves the cross-lib refs.
# target_link_libraries with INTERFACE doesn't propagate REQUIRES, which
# is why a bare .a link fails with "undefined reference to i2c_master_…".
set(_cam_lib "${_arduino_path_norm}/tools/sdk/${IDF_TARGET}/lib/libesp32-camera.a")
if(EXISTS "${_cam_lib}")
add_prebuilt_library(esp32_camera_prebuilt "${_cam_lib}"
REQUIRES driver ${_arduino_comp_name})
target_link_libraries(${COMPONENT_LIB} PUBLIC esp32_camera_prebuilt)
endif()
# arduino-esp32 v2.x's generic `esp32` variant pins_arduino.h does NOT
# define LED_BUILTIN, so the default Arduino Blink sketch (which Velxio
# ships in every new project) fails to compile with
# error: 'LED_BUILTIN' was not declared in this scope
# Provide a fallback so sketches "just compile". GPIO 2 is the
# convention on most ESP32 dev boards (DOIT, NodeMCU, Wemos D1 R32);
# for ESP32-CAM where GPIO 2 isn't broken out, the user should use an
# explicit pin number — but this default keeps the new-project flow
# smooth.
fix(spice+pipeline): LED visualization, INPUT_PULLUP, ESP32-C3, PWM fade, examples End-to-end pipeline fixes uncovered while auditing the /examples gallery. Each bug shipped past green unit + snapshot tests because none of those run firmware + render LEDs. Added scripts/visual-led-test.mjs as a CDP-driven visual harness that loads each example, runs the simulator, samples `wokwi-led.brightness`, and asserts toggle / gradient / initial-off invariants — exits non-zero on any regression. Frontend simulator - PinManager.updatePort: new optional ddrMask param. A pin is added to `outputPins` only if the DDR bit is set, so the PORTx write that enables INPUT_PULLUP (DDR=0, PORT=1) no longer falsely marks the pin as MCU output. AVRSimulator now reads DDRB/C/D (0x24/0x27/0x2A on Uno/Nano, 0x37 on ATtiny85, per-port table on Mega) and forwards it. - AVRSimulator: pass DDR mask alongside every port-listener fire. - BasicParts pushbutton{,-6mm}: seed pin HIGH in attachEvents so `digitalRead()` returns HIGH while idle. avr8js doesn't auto-simulate INPUT_PULLUP — without this the firmware reads LOW from boot and thinks the button is permanently pressed (the "LED is always on, pressing does nothing" UX bug). - connectMcuEdgesToService: suppress synthetic digital edges on pins with active PWM, AND subscribe to onPwmChange to re-tick the netlist on duty changes. Fade-LED now produces a true gradient (6 distinct brightness levels across a fade cycle) instead of a binary 0/full toggle. - CircuitSimulationService.handleMcuEdge: replace single-slot pendingMcuEdge with a per-pin Map. Multiple pins toggling during the same in-flight tick used to overwrite each other; now every pin's most-recent edge replays after the tick. Fixes Traffic-Light RED→ YELLOW→GREEN sequencing. - NetlistBuilder: new sanitizeSpiceId() helper replaces hyphens with underscores in V-source names. ngspice's interactive `alter` command treats `-` as an operator and silently no-ops on hyphenated source names, so mid-simulation MCU pin transitions stopped propagating after the first solve. MixedModeScheduler.onMcuPinChange and CircuitSimulationService self-heal use the same sanitizer so names stay consistent across emit/alter/lookup. Also added a regex-based fallback in step 2 so any board pin matching `GND.\d+` canonicalises to net "0" — ESP32-C3 dev kits expose up to 10 GND pins and the per-board `groundPinNames` list missed several, leaving wires floating instead of grounded. - collectPinStates: emit V-sources only for pins in `outputPins`, not every wired board pin. Leaves INPUT pins (analog sensors on A0, pull-down dividers, etc.) free for the SPICE solver instead of being shorted to 0 V by an ideal MCU V-source. - start.ts: extended __spiceDebug to also expose outputPinsByBoard + nodeVoltages + pinNetMapEntries for the visual harness. - ESP32 / RP2040 / RISC-V / C3 simulators: pass `'mcu'` source flag to triggerPinChange / setPinState so the new outputPins tracking fires on those boards too (was AVR-only before). - useSimulatorStore: stopBoard/resetBoard call pm.resetPinStates() so outputPins clears between runs; Esp32Bridge.onPinChange passes the `'mcu'` flag in all three places it's wired. - types/board.ts: ATtiny85 FQBN `clock=internal16mhz` → `clock=16pll` (ATTinyCore 1.5.2 renamed the option). Backend - esp-idf-template/main/CMakeLists.txt: skip the `-DLED_BUILTIN=2` fallback for esp32c3 and esp32s3 targets. Both variants already define LED_BUILTIN in pins_arduino.h via a self-define macro (`#define LED_BUILTIN LED_BUILTIN` + `static const uint8_t LED_BUILTIN = ...;`). Pre-defining the symbol from the command line expanded the static-const declaration to `static const uint8_t 2 = ...;` — a syntax error that broke every ESP32-C3 / S3 build (`expected unqualified-id before numeric constant`). Examples - examples.ts: bulk-fix 72 wire endpoints that referenced `componentId: 'nano-rp2040'` / `'esp32-c3'` etc. (boards that don't exist on the canvas). Replaced with `'arduino-uno'` (the canvas board-id convention) and converted `D<n>` pin names to `GP<n>` for Pico-style boards. Affects pico-blink, pico-i2c-scanner, pico-i2c-rtc-read, pico-spi-loopback, c3-blink and others. Tests - scripts/visual-led-test.mjs: CDP-driven harness. Default suite covers Blink (single-pin), Button (idle-OFF invariant — catches the INPUT_PULLUP regression), Traffic-Light (multi-pin sequencing), Fade-LED (PWM gradient — ≥3 distinct levels), RGB-LED (≥3 PWM pins driven). Run via `npm --prefix frontend run test:visual` against a Chrome on `:9222` + vite on `:5174` + backend on `:8001`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 07:47:45 +07:00
#
# ONLY on the generic `esp32` variant: every other variant we build
# (C3/S3 on the 2.x core; C3/C6/S2/S3 on the 3.x core) defines
# LED_BUILTIN itself (as `static const uint8_t LED_BUILTIN = ...;`
fix(spice+pipeline): LED visualization, INPUT_PULLUP, ESP32-C3, PWM fade, examples End-to-end pipeline fixes uncovered while auditing the /examples gallery. Each bug shipped past green unit + snapshot tests because none of those run firmware + render LEDs. Added scripts/visual-led-test.mjs as a CDP-driven visual harness that loads each example, runs the simulator, samples `wokwi-led.brightness`, and asserts toggle / gradient / initial-off invariants — exits non-zero on any regression. Frontend simulator - PinManager.updatePort: new optional ddrMask param. A pin is added to `outputPins` only if the DDR bit is set, so the PORTx write that enables INPUT_PULLUP (DDR=0, PORT=1) no longer falsely marks the pin as MCU output. AVRSimulator now reads DDRB/C/D (0x24/0x27/0x2A on Uno/Nano, 0x37 on ATtiny85, per-port table on Mega) and forwards it. - AVRSimulator: pass DDR mask alongside every port-listener fire. - BasicParts pushbutton{,-6mm}: seed pin HIGH in attachEvents so `digitalRead()` returns HIGH while idle. avr8js doesn't auto-simulate INPUT_PULLUP — without this the firmware reads LOW from boot and thinks the button is permanently pressed (the "LED is always on, pressing does nothing" UX bug). - connectMcuEdgesToService: suppress synthetic digital edges on pins with active PWM, AND subscribe to onPwmChange to re-tick the netlist on duty changes. Fade-LED now produces a true gradient (6 distinct brightness levels across a fade cycle) instead of a binary 0/full toggle. - CircuitSimulationService.handleMcuEdge: replace single-slot pendingMcuEdge with a per-pin Map. Multiple pins toggling during the same in-flight tick used to overwrite each other; now every pin's most-recent edge replays after the tick. Fixes Traffic-Light RED→ YELLOW→GREEN sequencing. - NetlistBuilder: new sanitizeSpiceId() helper replaces hyphens with underscores in V-source names. ngspice's interactive `alter` command treats `-` as an operator and silently no-ops on hyphenated source names, so mid-simulation MCU pin transitions stopped propagating after the first solve. MixedModeScheduler.onMcuPinChange and CircuitSimulationService self-heal use the same sanitizer so names stay consistent across emit/alter/lookup. Also added a regex-based fallback in step 2 so any board pin matching `GND.\d+` canonicalises to net "0" — ESP32-C3 dev kits expose up to 10 GND pins and the per-board `groundPinNames` list missed several, leaving wires floating instead of grounded. - collectPinStates: emit V-sources only for pins in `outputPins`, not every wired board pin. Leaves INPUT pins (analog sensors on A0, pull-down dividers, etc.) free for the SPICE solver instead of being shorted to 0 V by an ideal MCU V-source. - start.ts: extended __spiceDebug to also expose outputPinsByBoard + nodeVoltages + pinNetMapEntries for the visual harness. - ESP32 / RP2040 / RISC-V / C3 simulators: pass `'mcu'` source flag to triggerPinChange / setPinState so the new outputPins tracking fires on those boards too (was AVR-only before). - useSimulatorStore: stopBoard/resetBoard call pm.resetPinStates() so outputPins clears between runs; Esp32Bridge.onPinChange passes the `'mcu'` flag in all three places it's wired. - types/board.ts: ATtiny85 FQBN `clock=internal16mhz` → `clock=16pll` (ATTinyCore 1.5.2 renamed the option). Backend - esp-idf-template/main/CMakeLists.txt: skip the `-DLED_BUILTIN=2` fallback for esp32c3 and esp32s3 targets. Both variants already define LED_BUILTIN in pins_arduino.h via a self-define macro (`#define LED_BUILTIN LED_BUILTIN` + `static const uint8_t LED_BUILTIN = ...;`). Pre-defining the symbol from the command line expanded the static-const declaration to `static const uint8_t 2 = ...;` — a syntax error that broke every ESP32-C3 / S3 build (`expected unqualified-id before numeric constant`). Examples - examples.ts: bulk-fix 72 wire endpoints that referenced `componentId: 'nano-rp2040'` / `'esp32-c3'` etc. (boards that don't exist on the canvas). Replaced with `'arduino-uno'` (the canvas board-id convention) and converted `D<n>` pin names to `GP<n>` for Pico-style boards. Affects pico-blink, pico-i2c-scanner, pico-i2c-rtc-read, pico-spi-loopback, c3-blink and others. Tests - scripts/visual-led-test.mjs: CDP-driven harness. Default suite covers Blink (single-pin), Button (idle-OFF invariant — catches the INPUT_PULLUP regression), Traffic-Light (multi-pin sequencing), Fade-LED (PWM gradient — ≥3 distinct levels), RGB-LED (≥3 PWM pins driven). Run via `npm --prefix frontend run test:visual` against a Chrome on `:9222` + vite on `:5174` + backend on `:8001`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 07:47:45 +07:00
# plus `#define LED_BUILTIN LED_BUILTIN`). Pre-defining it from the
# command line clashes with the self-define: the preprocessor expands
# `static const uint8_t LED_BUILTIN = ...` to `static const uint8_t 2 = ...`
# which is a syntax error. A whitelist (instead of the old C3/S3
# blacklist) keeps new targets safe by default — the ESP32-C6 variant
# was the first to hit the blacklist gap.
if(IDF_TARGET STREQUAL "esp32")
fix(spice+pipeline): LED visualization, INPUT_PULLUP, ESP32-C3, PWM fade, examples End-to-end pipeline fixes uncovered while auditing the /examples gallery. Each bug shipped past green unit + snapshot tests because none of those run firmware + render LEDs. Added scripts/visual-led-test.mjs as a CDP-driven visual harness that loads each example, runs the simulator, samples `wokwi-led.brightness`, and asserts toggle / gradient / initial-off invariants — exits non-zero on any regression. Frontend simulator - PinManager.updatePort: new optional ddrMask param. A pin is added to `outputPins` only if the DDR bit is set, so the PORTx write that enables INPUT_PULLUP (DDR=0, PORT=1) no longer falsely marks the pin as MCU output. AVRSimulator now reads DDRB/C/D (0x24/0x27/0x2A on Uno/Nano, 0x37 on ATtiny85, per-port table on Mega) and forwards it. - AVRSimulator: pass DDR mask alongside every port-listener fire. - BasicParts pushbutton{,-6mm}: seed pin HIGH in attachEvents so `digitalRead()` returns HIGH while idle. avr8js doesn't auto-simulate INPUT_PULLUP — without this the firmware reads LOW from boot and thinks the button is permanently pressed (the "LED is always on, pressing does nothing" UX bug). - connectMcuEdgesToService: suppress synthetic digital edges on pins with active PWM, AND subscribe to onPwmChange to re-tick the netlist on duty changes. Fade-LED now produces a true gradient (6 distinct brightness levels across a fade cycle) instead of a binary 0/full toggle. - CircuitSimulationService.handleMcuEdge: replace single-slot pendingMcuEdge with a per-pin Map. Multiple pins toggling during the same in-flight tick used to overwrite each other; now every pin's most-recent edge replays after the tick. Fixes Traffic-Light RED→ YELLOW→GREEN sequencing. - NetlistBuilder: new sanitizeSpiceId() helper replaces hyphens with underscores in V-source names. ngspice's interactive `alter` command treats `-` as an operator and silently no-ops on hyphenated source names, so mid-simulation MCU pin transitions stopped propagating after the first solve. MixedModeScheduler.onMcuPinChange and CircuitSimulationService self-heal use the same sanitizer so names stay consistent across emit/alter/lookup. Also added a regex-based fallback in step 2 so any board pin matching `GND.\d+` canonicalises to net "0" — ESP32-C3 dev kits expose up to 10 GND pins and the per-board `groundPinNames` list missed several, leaving wires floating instead of grounded. - collectPinStates: emit V-sources only for pins in `outputPins`, not every wired board pin. Leaves INPUT pins (analog sensors on A0, pull-down dividers, etc.) free for the SPICE solver instead of being shorted to 0 V by an ideal MCU V-source. - start.ts: extended __spiceDebug to also expose outputPinsByBoard + nodeVoltages + pinNetMapEntries for the visual harness. - ESP32 / RP2040 / RISC-V / C3 simulators: pass `'mcu'` source flag to triggerPinChange / setPinState so the new outputPins tracking fires on those boards too (was AVR-only before). - useSimulatorStore: stopBoard/resetBoard call pm.resetPinStates() so outputPins clears between runs; Esp32Bridge.onPinChange passes the `'mcu'` flag in all three places it's wired. - types/board.ts: ATtiny85 FQBN `clock=internal16mhz` → `clock=16pll` (ATTinyCore 1.5.2 renamed the option). Backend - esp-idf-template/main/CMakeLists.txt: skip the `-DLED_BUILTIN=2` fallback for esp32c3 and esp32s3 targets. Both variants already define LED_BUILTIN in pins_arduino.h via a self-define macro (`#define LED_BUILTIN LED_BUILTIN` + `static const uint8_t LED_BUILTIN = ...;`). Pre-defining the symbol from the command line expanded the static-const declaration to `static const uint8_t 2 = ...;` — a syntax error that broke every ESP32-C3 / S3 build (`expected unqualified-id before numeric constant`). Examples - examples.ts: bulk-fix 72 wire endpoints that referenced `componentId: 'nano-rp2040'` / `'esp32-c3'` etc. (boards that don't exist on the canvas). Replaced with `'arduino-uno'` (the canvas board-id convention) and converted `D<n>` pin names to `GP<n>` for Pico-style boards. Affects pico-blink, pico-i2c-scanner, pico-i2c-rtc-read, pico-spi-loopback, c3-blink and others. Tests - scripts/visual-led-test.mjs: CDP-driven harness. Default suite covers Blink (single-pin), Button (idle-OFF invariant — catches the INPUT_PULLUP regression), Traffic-Light (multi-pin sequencing), Fade-LED (PWM gradient — ≥3 distinct levels), RGB-LED (≥3 PWM pins driven). Run via `npm --prefix frontend run test:visual` against a Chrome on `:9222` + vite on `:5174` + backend on `:8001`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 07:47:45 +07:00
target_compile_definitions(${COMPONENT_LIB} PRIVATE
"LED_BUILTIN=2")
endif()
# Relax `-Werror=` for user sketches. ESP-IDF defaults treat several
# warning categories as errors (-Werror=comment, -Werror=missing-field-
# initializers, etc.) which is great for IDF itself but ruthless for
# user Arduino code that often has nested /* */ comments,
# parenthesized macros, or other idioms that compile fine under
# arduino-cli's permissive defaults. Demoting these to plain warnings
# gives users the same "it just compiles" experience they expect from
# the Arduino IDE without disabling -Wall entirely.
target_compile_options(${COMPONENT_LIB} PRIVATE
-Wno-error=comment
-Wno-error=parentheses
-Wno-error=sign-compare
-Wno-error=narrowing
-Wno-error=write-strings
-Wno-error=missing-field-initializers
-Wno-error=reorder)
else()
# Pure ESP-IDF mode: main.c #includes sketch_translated.c
idf_component_register(
SRCS "main.c"
INCLUDE_DIRS "."
)
endif()