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
This commit is contained in:
davidmonterocrespo24 2026-05-10 01:42:11 +02:00
parent 6049bd1d81
commit 9c93d99802
1 changed files with 21 additions and 2 deletions

View File

@ -944,10 +944,29 @@ export const useSimulatorStore = create<SimulatorState>((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