From 0290b24a477b94f95c9d60ded56a15da754dc8c2 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Tue, 24 Mar 2026 14:38:08 -0300 Subject: [PATCH 1/3] feat: remove unnecessary logging and diagnostic dumps in ESP32 worker; update WS2812 event handling in simulator store --- backend/app/services/esp32_worker.py | 61 ++----------------- frontend/src/simulation/parts/ComplexParts.ts | 6 -- frontend/src/simulation/parts/partUtils.ts | 1 - frontend/src/store/useSimulatorStore.ts | 6 ++ 4 files changed, 10 insertions(+), 64 deletions(-) diff --git a/backend/app/services/esp32_worker.py b/backend/app/services/esp32_worker.py index 7268100..286ab2b 100644 --- a/backend/app/services/esp32_worker.py +++ b/backend/app/services/esp32_worker.py @@ -232,8 +232,6 @@ def main() -> None: # noqa: C901 (complexity OK for inline worker) # ESP32 signal indices: 72-79 = LEDC HS ch 0-7, 80-87 = LEDC LS ch 0-7 _ledc_gpio_map: dict[int, int] = {} - _out_sel_dumped = [False] # one-time diagnostic dump flag - def _refresh_ledc_gpio_map() -> None: """Scan gpio_out_sel[40] registers and update _ledc_gpio_map. @@ -243,25 +241,16 @@ def main() -> None: # noqa: C901 (complexity OK for inline worker) try: out_sel_ptr = lib.qemu_picsimlab_get_internals(2) if not out_sel_ptr: - _log('LEDC gpio_out_sel: internals(2) returned NULL') return out_sel = (ctypes.c_uint32 * 40).from_address(out_sel_ptr) - # One-time dump of ALL gpio_out_sel values for diagnostics - if not _out_sel_dumped[0]: - _out_sel_dumped[0] = True - non_default = {pin: int(out_sel[pin]) for pin in range(40) - if int(out_sel[pin]) != 256 and int(out_sel[pin]) != 0} - _log(f'LEDC gpio_out_sel dump (non-default): {non_default}') - _log(f'LEDC gpio_out_sel ALL: {[int(out_sel[i]) for i in range(40)]}') for gpio_pin in range(40): signal = int(out_sel[gpio_pin]) & 0xFF if 72 <= signal <= 87: ledc_ch = signal - 72 if _ledc_gpio_map.get(ledc_ch) != gpio_pin: _ledc_gpio_map[ledc_ch] = gpio_pin - _log(f'LEDC map: ch{ledc_ch} -> GPIO{gpio_pin} (signal={signal})') - except Exception as e: - _log(f'LEDC gpio_out_sel scan error: {e}') + except Exception: + pass # Sensor state: gpio_pin → {type, properties..., saw_low, responding} _sensors: dict[int, dict] = {} @@ -456,7 +445,6 @@ def main() -> None: # noqa: C901 (complexity OK for inline worker) if marker == 0x5000: # LEDC duty change (from esp32_ledc.c) ledc_ch = (direction >> 8) & 0x0F intensity = direction & 0xFF # 0-100 percentage - _log(f'0x5000 callback: direction=0x{direction:04X} ch={ledc_ch} intensity={intensity} map={dict(_ledc_gpio_map)}') gpio = _ledc_gpio_map.get(ledc_ch, -1) if gpio == -1: _refresh_ledc_gpio_map() @@ -622,52 +610,13 @@ def main() -> None: # noqa: C901 (complexity OK for inline worker) def _ledc_poll_thread() -> None: # Track last-emitted duty to avoid flooding identical updates _last_duty = [0.0] * 16 - _diag_count = [0] - _first_nonzero_logged = [False] - _log('LEDC poll thread started') while not _stopped.wait(0.1): try: ptr = lib.qemu_picsimlab_get_internals(6) # LEDC_CHANNEL_DUTY - _diag_count[0] += 1 if ptr is None or ptr == 0: continue - # duty[] is float[16] in QEMU (percentage 0-100) arr = (ctypes.c_float * 16).from_address(ptr) - # Refresh LEDC→GPIO mapping from gpio_out_sel[40] registers _refresh_ledc_gpio_map() - # Log once when nonzero duties first appear - if not _first_nonzero_logged[0]: - nonzero = {ch: round(float(arr[ch]), 2) for ch in range(16) - if float(arr[ch]) != 0.0} - if nonzero: - _log(f'LEDC first nonzero at poll #{_diag_count[0]}: ' - f'duties={nonzero} gpio_map={dict(_ledc_gpio_map)}') - _first_nonzero_logged[0] = True - # Periodic diagnostic dump every 50 polls (~5s) - if _diag_count[0] % 50 == 0: - all_duties = {ch: round(float(arr[ch]), 4) for ch in range(16) - if float(arr[ch]) != 0.0} - # Also read LEDC channel conf (internals(4)) and timer freq (internals(5)) - diag_parts = [f'duties={all_duties}', f'gpio_map={dict(_ledc_gpio_map)}'] - try: - conf_ptr = lib.qemu_picsimlab_get_internals(4) - if conf_ptr: - conf_arr = (ctypes.c_uint32 * 16).from_address(conf_ptr) - nonzero_conf = {ch: hex(int(conf_arr[ch])) for ch in range(16) - if int(conf_arr[ch]) != 0} - diag_parts.append(f'ch_conf={nonzero_conf}') - except Exception: - pass - try: - freq_ptr = lib.qemu_picsimlab_get_internals(5) - if freq_ptr: - freq_arr = (ctypes.c_uint32 * 8).from_address(freq_ptr) - nonzero_freq = {t: int(freq_arr[t]) for t in range(8) - if int(freq_arr[t]) != 0} - diag_parts.append(f'timer_freq={nonzero_freq}') - except Exception: - pass - _log(f'LEDC poll #{_diag_count[0]}: {" | ".join(diag_parts)}') for ch in range(16): duty_pct = float(arr[ch]) if abs(duty_pct - _last_duty[ch]) < 0.01: @@ -679,9 +628,8 @@ def main() -> None: # noqa: C901 (complexity OK for inline worker) 'duty': round(duty_pct, 2), 'duty_pct': round(duty_pct, 2), 'gpio': gpio}) - except Exception as e: - import traceback - _log(f'LEDC poll error: {e}\n{traceback.format_exc()}') + except Exception: + pass threading.Thread(target=_ledc_poll_thread, daemon=True, name='ledc-poll').start() @@ -706,7 +654,6 @@ def main() -> None: # noqa: C901 (complexity OK for inline worker) raw_v = int(int(cmd['millivolts']) * 4095 / 3300) ch = int(cmd['channel']) clamped = max(0, min(4095, raw_v)) - _log(f'set_adc: ch={ch} mv={cmd["millivolts"]} raw={clamped}') lib.qemu_picsimlab_set_apin(ch, clamped) elif c == 'set_adc_raw': diff --git a/frontend/src/simulation/parts/ComplexParts.ts b/frontend/src/simulation/parts/ComplexParts.ts index e01e491..414fccd 100644 --- a/frontend/src/simulation/parts/ComplexParts.ts +++ b/frontend/src/simulation/parts/ComplexParts.ts @@ -71,12 +71,9 @@ PartSimulationRegistry.register('potentiometer', { const isESP32 = typeof (simulator as any).setAdcVoltage === 'function'; const refVoltage = (isRP2040 || isESP32) ? 3.3 : 5.0; - console.log(`[Pot] attached: pin=${pin} isESP32=${isESP32} refV=${refVoltage}`); - const onInput = () => { const raw = parseInt((element as any).value || '0', 10); const volts = (raw / 1023.0) * refVoltage; - console.log(`[Pot] onInput: raw=${raw} volts=${volts.toFixed(3)} pin=${pin}`); setAdcVoltage(simulator, pin, volts); }; @@ -338,14 +335,11 @@ PartSimulationRegistry.register('servo', { // 544µs = 2.72%, 2400µs = 12.0% const MIN_DC = MIN_PULSE_US / 20000; // 0.0272 const MAX_DC = MAX_PULSE_US / 20000; // 0.12 - console.log(`[Servo:ESP32] registering onPwmChange on pin=${pinSIG}`); const unsubscribe = pinManager.onPwmChange(pinSIG, (_pin, dutyCycle) => { - console.log(`[Servo:ESP32] onPwmChange pin=${_pin} dutyCycle=${dutyCycle.toFixed(4)}`); if (dutyCycle < 0.01 || dutyCycle > 0.20) return; // ignore out-of-range const angle = Math.round( ((dutyCycle - MIN_DC) / (MAX_DC - MIN_DC)) * 180 ); - console.log(`[Servo:ESP32] angle=${angle}`); el.angle = Math.max(0, Math.min(180, angle)); }); return () => { unsubscribe(); }; diff --git a/frontend/src/simulation/parts/partUtils.ts b/frontend/src/simulation/parts/partUtils.ts index 9342af7..f6b8196 100644 --- a/frontend/src/simulation/parts/partUtils.ts +++ b/frontend/src/simulation/parts/partUtils.ts @@ -33,7 +33,6 @@ export function setAdcVoltage(simulator: AnySimulator, pin: number, voltage: num const channel = pin - 26; // RP2040 ADC: 12-bit, 3.3V reference const adcValue = Math.round((voltage / 3.3) * 4095); - console.log(`[setAdcVoltage] RP2040 ch${channel} = ${adcValue} (${voltage.toFixed(3)}V)`); simulator.setADCValue(channel, adcValue); return true; } diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts index 30dc70c..013ddea 100644 --- a/frontend/src/store/useSimulatorStore.ts +++ b/frontend/src/store/useSimulatorStore.ts @@ -862,6 +862,12 @@ export const useSimulatorStore = create((set, get) => { }); }; bridge.onLedcUpdate = makeLedcUpdateHandler(boardId); + bridge.onWs2812Update = (channel, pixels) => { + const eventTarget = document.getElementById(`ws2812-${boardId}-${channel}`); + if (eventTarget) { + eventTarget.dispatchEvent(new CustomEvent('ws2812-pixels', { detail: { pixels } })); + } + }; esp32BridgeMap.set(boardId, bridge); const shim = new Esp32BridgeShim(bridge, pm); shim.onSerialData = serialCallback; From 09118fcdefabbf3f3e22aae25c6309201423967c Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Tue, 24 Mar 2026 16:47:19 -0300 Subject: [PATCH 2/3] feat: update .gitignore to include marketing directory; modify example texts to reflect Velxio branding; mark subproject commits as dirty --- .gitignore | 1 + frontend/src/data/examples.ts | 24 ++---------------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 6ecd25f..55deb02 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,4 @@ test/esp32-emulator/out_*/ # Google Cloud service account credentials velxio-ba3355a41944.json +marketing/* \ No newline at end of file diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts index e786892..b814edb 100644 --- a/frontend/src/data/examples.ts +++ b/frontend/src/data/examples.ts @@ -670,7 +670,7 @@ void drawStaticUI() { tft.setTextSize(3); tft.setTextColor(tft.color565(255, 220, 0)); tft.setCursor(20, 10); - tft.print("WOKWI TFT"); + tft.print("VELXIO TFT"); // Subtitle tft.setTextSize(2); @@ -760,7 +760,7 @@ void setup() { // Print a message to the LCD. lcd.print("Hello, Arduino!"); lcd.setCursor(0, 1); - lcd.print("Wokwi Emulator"); + lcd.print("Velxio Emulator"); lcd.setCursor(0, 2); lcd.print("LCD 2004 Test"); } @@ -2034,7 +2034,6 @@ void loop() { // Blinks the built-in LED (GPIO2) and an external LED (GPIO4) // Requires arduino-esp32 2.0.17 (IDF 4.4.x) — see docs/ESP32_EMULATION.md -#include #include #include @@ -2042,7 +2041,6 @@ void loop() { #define LED_EXT_PIN 4 // External red LED void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; @@ -2092,12 +2090,10 @@ void loop() { // Echoes anything received on Serial (UART0) back to the sender. // Open the Serial Monitor, type something, and see it echoed back. -#include #include #include void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; @@ -2937,7 +2933,6 @@ void loop() { code: `// ESP32 — 7-Segment Display Counter 0-9 // Segments: a=12, b=13, c=14, d=25, e=26, f=27, g=32 -#include #include #include @@ -2957,7 +2952,6 @@ const bool DIGITS[10][7] = { }; void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; @@ -3757,7 +3751,6 @@ void loop() { // Wiring: DATA → GPIO4 | VCC → 3V3 | GND → GND #include -#include #include #include @@ -3768,7 +3761,6 @@ DHT dht(DHT_PIN, DHT_TYPE); // Disable hardware Timer Group WDTs (QEMU emulation is slower than real time) void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; @@ -3818,7 +3810,6 @@ void loop() { code: `// ESP32 — HC-SR04 Ultrasonic Distance Sensor // Wiring: TRIG → D18 | ECHO → D19 | VCC → 3V3 | GND → GND -#include #include #include @@ -3826,7 +3817,6 @@ void loop() { #define ECHO_PIN 19 void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; @@ -3882,7 +3872,6 @@ void loop() { // Requires: Adafruit MPU6050, Adafruit Unified Sensor libraries // Wiring: SDA → D21 | SCL → D22 | VCC → 3V3 | GND → GND -#include #include #include #include @@ -3892,7 +3881,6 @@ void loop() { Adafruit_MPU6050 mpu; void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; @@ -3948,7 +3936,6 @@ void loop() { code: `// ESP32 — PIR Motion Sensor // Wiring: OUT → D5 | VCC → 3V3 | GND → GND -#include #include #include @@ -3959,7 +3946,6 @@ bool prevMotion = false; unsigned long detections = 0; void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; @@ -4015,7 +4001,6 @@ void loop() { // Pot : SIG → D34 | VCC → 3V3 | GND → GND #include -#include #include #include @@ -4026,7 +4011,6 @@ Servo myServo; // Disable hardware Timer Group WDTs (QEMU emulation is slower than real time) void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; @@ -4075,7 +4059,6 @@ void loop() { // Wiring: HORZ → D35 | VERT → D34 | SEL → D15 // VCC → 3V3 | GND → GND -#include #include #include @@ -4084,7 +4067,6 @@ void loop() { #define JOY_BTN 15 // GPIO with pull-up void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; @@ -4139,7 +4121,6 @@ void loop() { // Wiring: DATA → GPIO3 | VCC → 3V3 | GND → GND #include -#include #include #include @@ -4150,7 +4131,6 @@ DHT dht(DHT_PIN, DHT_TYPE); // Disable hardware Timer Group WDTs (QEMU emulation is slower than real time) void disableAllWDT() { - esp_task_wdt_deinit(); TIMERG0.wdt_wprotect = TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.en = 0; TIMERG0.wdt_wprotect = 0; From 9d45484fce2af2e2b6b9551032ad9e8f8031ff9b Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Tue, 24 Mar 2026 16:52:01 -0300 Subject: [PATCH 3/3] feat: enhance EditorToolbar with always-visible Libraries button and missing library hint; remove overflow collapse logic --- .../src/components/editor/EditorToolbar.css | 75 ++++++- .../src/components/editor/EditorToolbar.tsx | 189 ++++++++---------- 2 files changed, 155 insertions(+), 109 deletions(-) diff --git a/frontend/src/components/editor/EditorToolbar.css b/frontend/src/components/editor/EditorToolbar.css index 3e017c3..9a35d56 100644 --- a/frontend/src/components/editor/EditorToolbar.css +++ b/frontend/src/components/editor/EditorToolbar.css @@ -88,7 +88,34 @@ color: #ccc; } -/* Libraries */ +/* Libraries button (always visible, with label) */ +.tb-btn-libraries { + display: flex; + align-items: center; + gap: 5px; + padding: 4px 10px 4px 8px; + border: 1px solid rgba(0, 184, 212, 0.3); + border-radius: 6px; + cursor: pointer; + background: rgba(0, 184, 212, 0.08); + color: #00e5ff; + font-size: 12px; + font-weight: 500; + font-family: inherit; + white-space: nowrap; + transition: background 0.15s, border-color 0.15s, color 0.15s; + flex-shrink: 0; +} +.tb-btn-libraries:hover { + background: rgba(0, 184, 212, 0.18); + border-color: rgba(0, 184, 212, 0.5); + color: #fff; +} +.tb-libraries-label { + line-height: 1; +} + +/* Legacy lib icon-only button (for overflow items) */ .tb-btn-lib { color: #00b8d4; } @@ -97,6 +124,52 @@ color: #00e5ff; } +/* Missing library hint banner */ +.tb-lib-hint { + display: flex; + align-items: center; + gap: 6px; + padding: 5px 12px; + background: rgba(255, 152, 0, 0.1); + border-bottom: 1px solid rgba(255, 152, 0, 0.25); + color: #ffb74d; + font-size: 12px; + font-family: inherit; +} +.tb-lib-hint svg { + flex-shrink: 0; + color: #ffa726; +} +.tb-lib-hint-btn { + background: rgba(0, 184, 212, 0.15); + border: 1px solid rgba(0, 184, 212, 0.4); + border-radius: 4px; + color: #00e5ff; + font-size: 12px; + font-weight: 600; + font-family: inherit; + padding: 2px 8px; + cursor: pointer; + transition: background 0.12s; +} +.tb-lib-hint-btn:hover { + background: rgba(0, 184, 212, 0.3); + color: #fff; +} +.tb-lib-hint-close { + margin-left: auto; + background: none; + border: none; + color: #888; + font-size: 16px; + cursor: pointer; + padding: 0 4px; + line-height: 1; +} +.tb-lib-hint-close:hover { + color: #ccc; +} + /* Output Console toggle */ .tb-btn-output { color: #9d9d9d; diff --git a/frontend/src/components/editor/EditorToolbar.tsx b/frontend/src/components/editor/EditorToolbar.tsx index f9ee3c4..c94d174 100644 --- a/frontend/src/components/editor/EditorToolbar.tsx +++ b/frontend/src/components/editor/EditorToolbar.tsx @@ -67,20 +67,12 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi const [installModalOpen, setInstallModalOpen] = useState(false); const importInputRef = useRef(null); const toolbarRef = useRef(null); - const [overflowCollapsed, setOverflowCollapsed] = useState(false); const [overflowOpen, setOverflowOpen] = useState(false); const overflowMenuRef = useRef(null); + const [missingLibHint, setMissingLibHint] = useState(false); - // Collapse secondary buttons when toolbar is too narrow - useEffect(() => { - const el = toolbarRef.current; - if (!el) return; - const ro = new ResizeObserver(([entry]) => { - setOverflowCollapsed(entry.contentRect.width < 500); - }); - ro.observe(el); - return () => ro.disconnect(); - }, []); + // (ResizeObserver removed — Library Manager is always visible now, + // only import/export live in the overflow menu) // Close overflow dropdown on outside click useEffect(() => { @@ -143,8 +135,14 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi compileBoardProgram(activeBoardId, program); } setMessage({ type: 'success', text: 'Compiled successfully' }); + setMissingLibHint(false); } else { - setMessage({ type: 'error', text: result.error || result.stderr || 'Compile failed' }); + const errText = result.error || result.stderr || 'Compile failed'; + setMessage({ type: 'error', text: errText }); + // Detect missing library errors — common patterns: + // "No such file or directory" for #include, "fatal error: XXX.h" + const looksLikeMissingLib = /No such file or directory|fatal error:.*\.h|library not found/i.test(errText); + setMissingLibHint(looksLikeMissingLib); } } catch (err) { const errMsg = err instanceof Error ? err.message : 'Compile failed'; @@ -428,104 +426,61 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi onChange={handleImportFile} /> - {/* Secondary buttons — hidden when toolbar is narrow */} - {!overflowCollapsed && ( - <> - {/* Import Wokwi zip */} - + {/* Library Manager — always visible with label */} + - {/* Export Wokwi zip */} - + {/* Import / Export — overflow menu */} +
+ - {/* Libraries */} - - - )} - - {/* Overflow (…) button — shown when toolbar is narrow */} - {overflowCollapsed && ( -
- - - {overflowOpen && ( -
- - - -
- )} -
- )} + {overflowOpen && ( +
+ + +
+ )} +
@@ -549,6 +504,24 @@ export const EditorToolbar = ({ consoleOpen, setConsoleOpen, compileLogs: _compi
{message.text}
)} + {/* Missing library hint */} + {missingLibHint && ( +
+ + + + + + Missing library? Install it from the + + +
+ )} + setLibManagerOpen(false)} />