diff --git a/backend/app/services/espidf_compiler.py b/backend/app/services/espidf_compiler.py index edb387aa..157ba89c 100644 --- a/backend/app/services/espidf_compiler.py +++ b/backend/app/services/espidf_compiler.py @@ -1540,6 +1540,25 @@ class ESPIDFCompiler: ' alloca=__builtin_alloca memcpy_P=memcpy memcmp_P=memcmp)\n' ) + # The -Werror relaxations the main component gets, for the same reason + # but stronger: these are LIBRARY sources the user cannot even edit. + # M5Stack's own MahonyAHRS.cpp (the fast inverse square root reading a + # float through a long*) fails under -Werror=uninitialized, which made + # installing the vendor's library kill every build that used it. Same + # INTERFACE-library caveat as defs_block: no sources, no options. + warns_block = '' + if cpp_files: + warns_block = ( + '# User libraries must compile as they do in the Arduino IDE.\n' + 'target_compile_options(${COMPONENT_LIB} PRIVATE\n' + ' -Wno-error=comment -Wno-error=parentheses -Wno-error=sign-compare\n' + ' -Wno-error=narrowing -Wno-error=write-strings\n' + ' -Wno-error=missing-field-initializers -Wno-error=reorder\n' + ' -Wno-error=unused-variable -Wno-error=unused-but-set-variable\n' + ' -Wno-error=format -Wno-error=maybe-uninitialized\n' + ' -Wno-error=uninitialized)\n' + ) + cmake_content = ( '# Auto-generated by Velxio — all user libraries merged into one component.\n' '# Directory structure preserved for libraries like ArduinoJson with src/ layout.\n' @@ -1549,6 +1568,7 @@ class ESPIDFCompiler: f' REQUIRES {arduino_comp_name}{extra_requires}\n' ')\n' f'{defs_block}' + f'{warns_block}' ) (comp_dir / 'CMakeLists.txt').write_text(cmake_content, encoding='utf-8') logger.info( diff --git a/frontend/src/simulation/Esp32Bridge.ts b/frontend/src/simulation/Esp32Bridge.ts index a208ac01..d924f3ac 100644 --- a/frontend/src/simulation/Esp32Bridge.ts +++ b/frontend/src/simulation/Esp32Bridge.ts @@ -91,6 +91,11 @@ export interface Ws2812Pixel { export interface LedcDuty { channel: number; duty_pct: number; + /** Carrier frequency in Hz, when the engine knows it. The JS engines derive + * it from the LEDC timer registers; without it a speaker on a PWM pin can + * only ever play one fixed note, which is how the M5Stack Core's piano + * spent months stuck on 660 Hz. */ + freq_hz?: number; } /** GPIO Matrix routing event — `gpio_out_sel[gpio]` was set to * `signal_id`. Maintained by the backend SignalRouter; emitted on diff --git a/frontend/src/simulation/PinManager.ts b/frontend/src/simulation/PinManager.ts index 040231e8..764ebc83 100644 --- a/frontend/src/simulation/PinManager.ts +++ b/frontend/src/simulation/PinManager.ts @@ -280,6 +280,20 @@ export class PinManager { return this.pwmValues.get(pin) ?? 0; } + /** Last known PWM carrier frequency per pin, in Hz. Written by the LEDC + * duty handler when the engine reports it; 0 = never reported. Kept as a + * side table rather than a new callback parameter so the many existing + * two- and three-arg PWM listeners stay untouched. */ + private pwmFreqs: Map = new Map(); + + setPwmFreq(pin: number, freqHz: number): void { + this.pwmFreqs.set(pin, freqHz); + } + + getPwmFreq(pin: number): number { + return this.pwmFreqs.get(pin) ?? 0; + } + // ── Analog voltage API ─────────────────────────────────────────────────── /** diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts index 61fc752b..6b5a2a27 100644 --- a/frontend/src/store/useSimulatorStore.ts +++ b/frontend/src/store/useSimulatorStore.ts @@ -696,7 +696,7 @@ class Esp32BridgeShim { // race window. function makeLedcDutyHandler(boardId: string) { - return (duty: { channel: number; duty_pct: number }) => { + return (duty: { channel: number; duty_pct: number; freq_hz?: number }) => { const boardPm = pinManagerMap.get(boardId); const router = signalRouterMap.get(boardId); if (!boardPm || !router) return; @@ -707,6 +707,10 @@ function makeLedcDutyHandler(boardId: string) { // pins via the GPIO Matrix (rare but documented in TRM). Iterate // all of them — each gets its own updatePwm call. for (const pin of pins) { + // The carrier frequency rides alongside, when the engine knows it: a + // speaker listener reads it back with getPwmFreq to play the sketch's + // actual note rather than a canned one. + if (duty.freq_hz && duty.freq_hz > 0) boardPm.setPwmFreq(pin, duty.freq_hz); boardPm.updatePwm(pin, dutyCycle); } };