From 9c93d99802c037a81404e595845b6340e48f8f84 Mon Sep 17 00:00:00 2001 From: davidmonterocrespo24 Date: Sun, 10 May 2026 01:42:11 +0200 Subject: [PATCH] fix(micropython-esp32): write helper .py files to flash before main.py loadMicroPythonProgram only forwarded main.py (or files[0]) to the bridge for raw-paste injection. Any auxiliary module the project imported (mylib.py, drivers, etc.) never reached the device, so `import mylib` died with ModuleNotFoundError. Build a Python prelude that writes every other .py file to the MicroPython filesystem via raw REPL, then runs main.py in the same paste. JSON.stringify produces an ASCII-safe Python-compatible string literal for the file body, which keeps the prelude inside the existing chunked-UART path Esp32Bridge already uses to feed the 128-byte FIFO. The RP2040 path was already multi-file via sim.loadMicroPython(files), so it stays untouched. Reproduces with the project shared in the bug report: https://velxio.dev/project/ac7e285c-8dc3-4d51-8751-b4aba9912f9e --- frontend/src/store/useSimulatorStore.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts index 8e85489a..e265995d 100644 --- a/frontend/src/store/useSimulatorStore.ts +++ b/frontend/src/store/useSimulatorStore.ts @@ -944,10 +944,29 @@ export const useSimulatorStore = create((set, get) => { const b64 = uint8ArrayToBase64(padToFlashSize(firmware, board.boardKind)); esp32Bridge.loadFirmware(b64); - // Queue code injection for after REPL boots + // Queue code injection for after REPL boots. Multi-file projects: + // every .py file other than the entry point gets materialized to the + // MicroPython filesystem (via a prelude executed inside the same raw + // REPL paste) before main.py runs, so `import mylib` resolves. + // Without this, ESP32 projects with helper modules crashed at runtime + // with ModuleNotFoundError. const mainFile = files.find((f) => f.name === 'main.py') ?? files[0]; if (mainFile) { - esp32Bridge.setPendingMicroPythonCode(mainFile.content); + const auxFiles = files.filter( + (f) => f !== mainFile && f.name.endsWith('.py'), + ); + const preludeLines = auxFiles.map((f) => { + // JSON.stringify produces an ASCII-safe Python-compatible + // string literal (both languages share the same \n \r \t \" \\ + // escapes, and JSON does not emit any escape Python rejects). + const lit = JSON.stringify(f.content); + const path = JSON.stringify(f.name); + return `with open(${path},'w') as _f:\n _f.write(${lit})`; + }); + const prelude = preludeLines.length + ? preludeLines.join('\n') + '\n' + : ''; + esp32Bridge.setPendingMicroPythonCode(prelude + mainFile.content); } } else { // RP2040 path: load firmware + filesystem in browser