Repository navigation
Description
Found during the #318 human test pass, scenario 9, on the v0.0.8 Windows bundled MSI (Windows 10 Home 22H2, 19045). This is not the compact-build fetcher problem in #514, and it is past the point #516 fixed — the board image downloads fine now.
What happens
No emulated board can boot. Both the internal probe and the ordinary user path fail identically:
> board.probe {"board": "Generic_E22_sx1262"}
boot failed engine: firmware on 2 of 3 nodes; 1 failed,
first: bc-under-test: firmware started but never connected within 40s
build passed image Generic_E22_sx1262_repeater-v1.17.1-d929643-merged.bin
fem/flood/power/radio/rx/tx untested
> node.set_board {"node": "Abernethy Repeater", "board": "Generic_E22_sx1262"}
> node.set_firmware {"version": "v1.17.1", "board": "Generic_E22_sx1262", "role": "simple_repeater"}
> node.start
ERROR: engine: firmware on 0 of 1 nodes; 1 failed,
first: Abernethy Repeater: firmware started but never connected within 40s
Reproducible: 2 of 2 probes, and the user path, with sim.unverified_wiring on. The node then sits down for ever — firmware.state stays at running: 55 of 56.
The cause
nodefs/<node>/emulator.log says it exactly:
Adding SPI flash device
C:\Program Files\MeshBench\qemu\bin\qemu-system-xtensa.exe: sx1262: cannot load the chip
model at C:\Program Files\MeshBench\libvirtualsx1262.dll:
'C:\Program Files\MeshBench\libvirtualsx1262.dll': The specified module could not be found.
and the firmware's own view, from stderr.log:
radio_init: entering std_init
radio_init: std_init returned 1
The DLL is there — C:\Program Files\MeshBench\libvirtualsx1262.dll, 44,087 bytes, exactly where setup.check reports it as ready. "The specified module could not be found" from LoadLibrary means one of the DLL's own dependencies is missing, not the DLL. Parsing its import table:
| import | resolved |
|---|---|
KERNEL32.dll |
C:\Windows\System32 |
msvcrt.dll |
C:\Windows\System32 |
libstdc++-6.dll |
not found |
libstdc++-6.dll is the MinGW C++ runtime. It is not beside the binary, not under qemu\bin, not in System32, and not on PATH. libvirtualsx1262.dll is the only DLL in the install root.
So: the chip model never loads, the virtual SX1262 never appears on the bus, radio_init fails, the firmware never reaches the engine, and forty seconds later the boot is called a timeout. The timeout message is the last symptom in a chain that starts three steps earlier, which is why this reads as "emulation is flaky" rather than "a file is missing".
It is not QEMU-specific
Renode loads the same library, by path, through LoadLibraryW:
renode-support/peripherals/VirtualSX1262Lib.cs:184
return OnWindows ? LoadLibraryW(path) : dlopen(path, 2);
So every emulated board on Windows is affected, ESP32 and nRF52 alike — the missing piece is the radio, which both MCU paths share. Both emulators themselves are fine and run standalone:
qemu-system-xtensa.exe -version -> QEMU emulator version 9.2.2 (v9.2.2_meshbench_sx1262_14)
renode.exe --version -> Renode v1.16.1 (build 1.16.1+20260904gite7196ef71)
Where it comes from
The DLL is a prebuilt asset rather than something this repo compiles — packaging/emulator-pins.env pins CHIPMODEL_RELEASE=v1.3.0, and package.yml unpacks it and keeps one file:
mv dist/meshbench/virtual-sx1262-windows-amd64/lib/libvirtualsx1262.dll dist/meshbench/
rm -rf dist/meshbench/virtual-sx1262-windows-amd64 ...
file dist/meshbench/libvirtualsx1262.dllI checked whether the runtime was being thrown away by that rm -rf, and it is not — virtual-sx1262-windows-amd64.tar.gz at v1.3.0 contains only:
include/virtual_sx1262.h
lib/libvirtualsx1262.a
lib/libvirtualsx1262.dll
LICENSE NOTICE.md README.md
No libstdc++-6.dll. So the upstream asset is incomplete, and the fix belongs in MeshBench/virtual-sx1262 rather than here. This is Windows-only because on Linux and macOS the equivalent runtime is part of the base system; Windows has no system libstdc++.
Suggested fix
Preferred, upstream in MeshBench/virtual-sx1262: link the Windows DLL with -static-libstdc++ -static-libgcc. One 44 kB file with no runtime dependencies beyond KERNEL32/msvcrt, nothing to ship alongside, and nothing to keep in step with the mingw version the asset was built with.
Otherwise: ship libstdc++-6.dll (and whatever it in turn pulls in — typically libgcc_s_seh-1.dll and libwinpthread-1.dll) beside libvirtualsx1262.dll in the asset, and have package.yml copy them across with it.
And a check worth adding here either way. file dist/meshbench/libvirtualsx1262.dll passes on a DLL that cannot load; setup.check reports the row ready on the strength of the path existing. Something that walks the import table, or simply tries to LoadLibrary it, would have failed this release rather than shipping it — the same shape as the bundle-carries-the-font check added for #608, which did exactly this job one release ago.
Filed from the #318 pass. 🤖 Generated with Claude Code
Activity
A13xB0 commentedon Sep 6, 2026
Confirmed on a second board, and here is what it looks like from the front.
Heltec_v3 (ESP32-S3, SSD1306) fails identically to
Generic_E22_sx1262— different MCU, different flash size (8 MB against 4 MB), same wall:So it is not board-specific, as expected for a missing radio model.
What a user sees
The Hardware tab is where this surfaces, and to its credit it surfaces it well — the failure is right there under last words:
The board screen panel is drawn, correctly sized and labelled, and correctly dark:
SSD1306 128x64 / I2C 0x3C / **not powered**, the TX lamp on pin 35, the PRG button on pin 0, the battery meter on pin 1, and the caption that the panel is what the firmware drew rather than what the glass looks like. All of that is right. It is just permanently unlit on Windows, because the firmware behind it never boots.So the board screen cannot be photographed on Windows at all — not for the #318 capture sweep, and not for the docs.
board.screenshotandboard.screenboth refuse (is not running), andnode.boardviewrefuses with "runs a host build rather than a board image", because a boot that failed never commits the board image: the node keepsfirmware_board: ""and falls back.That last part is worth noting for whoever fixes this — the three verbs that would show a board all refuse for reasons that describe the symptom rather than the cause, so from the outside this reads as "this node is not set up" rather than "the emulator could not load its radio". The one place the real reason appears is last words on the Hardware tab, and only after you have already tried and failed to start it.
One smaller thing found alongside
Clicking Board view… on the Hardware tab does nothing at all — no window, and
ui.saidis empty afterwards. Over the socket the same request answers with a proper reason. A button that is drawn enabled, highlights on hover, and then neither acts nor explains is worse than a disabled one; it could carry the sentencenode.boardviewalready returns.A13xB0 commentedon Sep 6, 2026
Fix opened upstream: MeshBench/virtual-sx1262#1.
To answer the question of where this belongs, since both the bundle and the pipeline were reasonable suspects: it is neither. It is the chip model's own build flags.
virtual-sx1262'sbuild.shlinks the shared library's runtime dynamically on every platform. Correct on Linux and macOS, wrong on Windows, where the result only loads inside an MSYS2 environment.release.ymlstages exactly what that build produces, and this repo'spackage.ymlships exactly what it unpacks. Nothing is dropped anywhere along the chain. I rebuilt v1.3.0's source locally with the unmodified flags and got a DLL whose imports match the shipped one byte for byte in kind — so the released artefact is faithfully what the build asked for. The build asked for the wrong thing.Also worth recording, because it is the obvious first fix and it does not work: shipping the runtime beside
libvirtualsx1262.dllfrom this repo would not help. Windows resolves a dynamically loaded DLL's imports from the loading process's directory, not from the directory of the DLL being loaded. I putlibstdc++-6.dll,libgcc_s_seh-1.dllandlibwinpthread-1.dllnext to it in a real v0.0.8 install and the load failed identically. A copy would have to live inqemu/binand wherever Renode is unpacked, kept in step with the toolchain that built the model. So there is no sensiblepackage.yml-side fix, which is why the PR is upstream.Verified end to end on this machine: built with
-static, dropped in place of the shipped v1.3.0 DLL, probe cache cleared.and the node then pins its board image properly —
firmware_board: "Heltec_v3", which stayed empty for as long as the boot was failing — withboard.screenreporting a real 128x64 SSD1306 instead of refusing.What this issue still needs after that PR merges, since the PR alone does not close it: a v1.3.1 release of
virtual-sx1262, andCHIPMODEL_RELEASEinpackaging/emulator-pins.envbumped fromv1.3.0to it. This repo pins a release rather than a commit, so nothing changes here until both happen.One thing I would suggest adding on this side regardless of the upstream fix.
package.ymlrunsfileon the DLL, which passes happily on one that cannot load, andsetup.checkreports the toolchain rowreadyon the strength of the path existing — both were green through this entire bug. The upstream PR adds an import check at the point of building; the equivalent here is cheap and would catch a bad artefact arriving from anywhere, not just from a build we control.🤖 Generated with Claude Code
package: pin the chip model at v1.3.1, which Windows can open (#638)