fix(esp32): stub network + ntptime modules for MicroPython in QEMU

Inject a compat shim into the raw-REPL prelude that replaces
sys.modules["network"] and sys.modules["ntptime"] with no-op stubs
BEFORE user main.py runs.

Why: the picsimlab QEMU fork's esp32_wifi NIC emulation handles
Arduino's lightweight WiFi.h but not MicroPython's full esp_wifi_init
path. Calling network.WLAN(STA_IF) (which is what every
network-using MP sketch does) drives the firmware to wait on
peripheral status bits QEMU never sets, eventually tripping the
FreeRTOS task watchdog (TG1WDT_SYS_RESET ~26s after boot, or
TG0WDT ~14s if the NIC is partially attached).

With the stub:
  network.WLAN(STA_IF).isconnected() -> False
  network.WLAN(STA_IF).connect(...)  -> no-op
  ntptime.settime()                   -> raises OSError

Sketches that already have try/except around sync_time (which is
most of the 100-days examples) now degrade gracefully: WELCOME +
EYES screens run, TIME and WEATHER screens show their fallback
behaviour, no panic, no reboot.

Doesn't affect Arduino C++ — sketches that #include <WiFi.h> use
real WiFi.begin() and the existing esp32_wifi NIC handles those fine.

A proper fix is to extend the picsimlab WiFi emulation to support
the full ESP-IDF API, but that's a multi-day project. This stub
unblocks the 31 MicroPython examples shipping with network imports.
This commit is contained in:
David Montero 2026-05-23 23:13:20 +02:00
parent e4ecefe46a
commit 835ca6d7a8
1 changed files with 42 additions and 3 deletions

View File

@ -1278,9 +1278,48 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
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'
: '';
// WiFi compat shim: replace `network` and `ntptime` with no-op
// stubs BEFORE user main.py imports them. The picsimlab QEMU
// fork's esp32_wifi NIC emulation is sufficient for Arduino's
// lightweight WiFi.h but not for MicroPython's full esp_wifi_init
// path — calling `network.WLAN(STA_IF)` hangs forever waiting
// for peripheral status bits that QEMU never sets, tripping the
// FreeRTOS task watchdog (TG1WDT_SYS_RESET ~26s after boot).
// The stubs let sketches degrade gracefully:
// wlan.isconnected() → False
// wlan.connect(...) → no-op
// ntptime.settime() → raises OSError (most sketches already
// catch and print "Sync Failed")
const wifiStub = [
'import sys',
'class _StubWLAN:',
' def __init__(self, *a, **k): pass',
' def active(self, on=None): return False',
' def connect(self, ssid=None, pwd=None): pass',
' def disconnect(self): pass',
' def isconnected(self): return False',
' def ifconfig(self, c=None): return ("0.0.0.0", "0.0.0.0", "0.0.0.0", "0.0.0.0")',
' def config(self, *a, **k): return None',
' def status(self, *a): return -1',
' def scan(self): return []',
'class _StubNetwork:',
' STA_IF = 0',
' AP_IF = 1',
' WLAN = _StubWLAN',
'sys.modules["network"] = _StubNetwork()',
'class _StubNTP:',
' host = "pool.ntp.org"',
' timeout = 1',
' @staticmethod',
' def settime(): raise OSError("WiFi unavailable in simulator")',
' @staticmethod',
' def time(): raise OSError("WiFi unavailable in simulator")',
'sys.modules["ntptime"] = _StubNTP()',
].join('\n');
const prelude = wifiStub + '\n' +
(preludeLines.length ? preludeLines.join('\n') + '\n' : '');
esp32Bridge.setPendingMicroPythonCode(prelude + mainFile.content);
}
} else {