Commit Graph

1651 Commits

Author SHA1 Message Date
velxio-deploy e661e64605 chore(examples): refresh 2 thumb file(s) [auto] 2026-08-29 04:00:11 +02:00
David Montero a92c59ee50 fix(espidf): no build-config header may drag in a library
Generalises the lv_conf.h fix, because it was never an LVGL problem. One
live compile resolved SIX config headers against the shared library dir in a
single build (sdkconfig.h, lv_conf.h, lv_conf_kconfig.h,
lv_rt_thread_conf.h, User_Config.h, zconf.h): six chances to merge a library
nobody asked for, and one of them did exactly that.

The rule is now about the shape of the name (*conf.h, *config.h, *_setup.h,
plus the known ones) and applies only to headers reached TRANSITIVELY -- a
config header the sketch includes itself is intent and still resolves.

Why skipping the lookup is safe rather than merely convenient: once a library
is merged its own headers are on the include path, so a config header shipped
BY the library that asks for it resolves at compile time with no lookup at
all. The lookup can only ever pull in a DIFFERENT library, which for a config
header is never what the project meant. A genuinely missing header now fails
where it should -- named, in the compiler output -- instead of inside the
sources of a library the sketch never mentioned.

Tests cover all six shapes, the direct-include exception and an ordinary
transitive dependency (Adafruit GFX -> BusIO) that must keep merging.
2026-08-29 00:08:32 +02:00
David Montero 467cf108ff fix(espidf): lv_conf.h stops dragging an unrelated library into the build
A sketch whose only LVGL include was <lvgl.h> died on a wall of errors from
Adafruit LvGL Glue, a library it never included. LVGL's sources carry
`#include "lv_conf.h"` behind `#if !LV_CONF_SKIP`; the include scan is
textual and does not evaluate that guard, so the header was queued even for a
build defining LV_CONF_SKIP=1, resolved against the shared library dir, and
found the copy the glue library vendors for LVGL v7. That merged the whole
library, whose sources then failed against the LVGL v8 actually in use
('lv_fs_drv_t' has no member named 'user_data').

Same failure shape as the Hash.h -> stemihexapod case already documented
next to it, so the fix is the same: a project-supplied config header never
resolves against user libraries. A project shipping its own lv_conf.h is
unaffected (sketch files are found in the build's own source dir), and a
project that really wants the glue library still reaches it by including
its own header.
2026-08-28 23:58:45 +02:00
David Montero 28a1c62887 feat(boards): ESP32-P4 Preview starter card and picker entry
The bare-DevKit companion to the ESP32-P4-Function-EV: same silicon, every
GPIO broken out, for circuits built on the canvas instead of the kit's fixed
peripherals. The board itself is overlay-registered; this is the shop-window
card plus its starter-dialog entry.
2026-08-28 20:26:33 +02:00
velxio-deploy de7daec4f1 chore(examples): refresh 1 thumb file(s) [auto] 2026-08-28 16:14:27 +02:00
velxio-deploy add3727a4d chore(examples): refresh 16 thumb file(s) [auto] 2026-08-28 06:52:56 +02:00
velxio-deploy 8ca70e3ac0 chore(examples): refresh 20 thumb file(s) [auto] 2026-08-28 06:09:05 +02:00
David Montero 655840d416 fix(editor): the S3 Sense joins the ESP32 starter section; no separate Seeed block
The ESP32 section already carries the XIAO family as its generation
representatives, so a Seeed Studio section repeated the brand with a single
card. The Sense now sits next to its sibling XIAO S3 (operator call,
2026-08-28).
2026-08-28 05:40:34 +02:00
velxio-deploy 0a0d7c2c8a chore(examples): refresh 5 thumb file(s) [auto] 2026-08-28 05:12:54 +02:00
David Montero 73b40f8256 fix(canvas): WiFi badge follows the run, Wokwi-style
The badge used to render from the moment a radio-capable board was on
the canvas, so a fresh XIAO ESP32-S3 project showed a WiFi icon before
Run was ever pressed. Now it appears when the simulation starts (gray
while the stack boots, green on got_ip) and leaves on Stop.

startBoard also clears the board's stale wifiStatus/bleStatus: they were
never reset, so a re-run would have opened the badge showing the
previous run's association.
2026-08-28 05:05:11 +02:00
David Montero 43805ff631 fix(previews): ESP32 variants with dedicated art stop borrowing the DevKit V1 drawing
The Arduino Nano ESP32 starter card rendered a plain DevKit V1; the art for
it and for S3/C3/CAM/DevKitC-v4/XIAO/SuperMini has existed in public/boards/
all along. BOARD_COPIES now normalizes them into component-svgs/ and
BOARD_DEFS points each kind at its own file (w/h keep each SVG's aspect).
2026-08-28 04:19:24 +02:00
David Montero e4c87ac7bb feat(editor): partner board sections (Seeed, DFRobot, Pimoroni, Espressif) in the starter dialog
Below M5Stack, same pro() mechanics: each section lists only the boards the
overlay registered, so OSS builds and launch-embargoed kits render nothing.
Preferred first-run examples wired for every partner kind; Stellar/Badger
entries are required, their gallery examples use boards[] + boardFilter and
the generic blink search never matches them.
2026-08-28 04:19:24 +02:00
David Montero Crespo 34162e2de1 i18n(canvas): the WiFi panel caret tooltip is translated
The split-button caret's title was hardcoded 'WiFi panel'; now
editor.canvas.wifiPanel across all nine locales.
2026-08-27 20:31:39 +02:00
David Montero 0251bd25a0 fix(gateway): stop offering a link a SoftAP sketch can never open
The serial monitor turns any 192.168.4.x it sees into an "Open IoT Gateway"
link, because that is the subnet QEMU's slirp leases to a connected board. The
ESP32's SoftAP default address is the same 192.168.4.1, so a captive-portal
sketch got the same link. It cannot work: an access point never receives a DHCP
lease, so it never reaches 'got_ip', so it never registers the bridge the proxy
looks up. The click landed on a 404 whose text then told the user to make sure
the sketch connected to WiFi, which is precisely what that sketch had chosen not
to do. The user is doing everything right and the product is lying twice.

Gate the link on the status every family already reports: QEMU through
wifi_status_parser, the in-browser JS engines, and the Pico W's cyw43
peripheral all emit 'got_ip'. Without it the address stays plain text.

Measured on a XIAO ESP32S3 Sense, one sketch per mode:
  softAP  192.168.4.1   gateway links 1 -> 0
  station 10.13.37.42   gateway links 1 -> 1   (unchanged)

The 404 body is rewritten to name softAP as the likely cause and point at a
network that exists in the simulator, instead of accusing the sketch. Covered
by a test that fails if the accusatory sentence comes back.
2026-08-27 07:19:20 +02:00
velxio-deploy 45410df6af chore(examples): refresh 1 thumb file(s) [auto] 2026-08-27 04:51:09 +02:00
David Montero Crespo 2bd07e49b0 fix(espidf): thread custom_wifi_ssids into _compile_in_dir
The stand-down check ran inside _compile_in_dir, which never received
the parameter compile() grew -- every ESP32 compile died with
NameError at runtime (ast-parse clean, scope-blind). Caught by the
staging warm-up compile minutes after deploy.
2026-08-26 22:10:50 +02:00
David Montero Crespo a765a0ed04 feat(canvas): the WiFi badge becomes a split button
David's review of the panel-first click: the one-click "icon -> the
board's web page" flow is muscle memory and must not grow a second
click. So the badge is now a split button, the standard pattern for
primary-action-plus-related-surface:

- the WiFi glyph keeps its one-click action: with an IP it opens the
  IoT gateway exactly as always (in-app iframe or new tab); before an
  IP it opens the overlay's WiFi panel, since there is nothing else
  to open yet.
- an attached caret is the panel's handle -- always reachable, run or
  not, which is when PCAP matters most. Touch-friendly and visible,
  unlike the right-click experiment it replaces.

The caret renders only when an overlay installed the panel hook; the
OSS build shows exactly the badge it always did.
2026-08-26 21:50:24 +02:00
David Montero Crespo 4e5ba634f2 feat(canvas): the WiFi badge renders pre-Run and can hand its click to an overlay panel
The badge used to exist only once a run produced a wifiStatus, which
left no stable place to hang a WiFi panel off. It now renders from the
moment a radio-capable board is active (disconnected style before a
Run), and its click consults window.__velxio_wifi_panel_toggle__
first -- a private overlay claims it for its panel, which carries the
gateway-open action forward. OSS builds have no hook, so the badge
behaves exactly as always: nothing before an IP, gateway on click
after one.
2026-08-26 21:38:47 +02:00
David Montero Crespo 7cfeb7399d feat(wifi): the SSID rewriter stands down when a project defines its own airspace
A private overlay can install window.__velxio_custom_wifi_ssids__ — a
nullary provider naming the WiFi networks the project's access-point
parts broadcast. Three consumers, one seam:

- the compile payload gains custom_wifi_ssids; when non-empty the
  ESP-IDF compiler skips SSID normalization entirely (no literal
  rewrite, no channel forcing) — the sketch connects to the network
  the user actually wrote, and a typo now fails truthfully. The dedup
  key hashes the list so with/without-APs builds never alias.
- the MicroPython network shim redirects unknown SSIDs to the FIRST
  project network instead of the hardcoded Velxio-GUEST, and its tuple
  gains the trailing comma that made a one-network list a string.
- the firmware-upload note lists the project's networks and says "add
  an access point part with this SSID" instead of "rebuild".

No provider installed (OSS build) = exactly the old behavior; the
issue-270 suite stays green (12/12).
2026-08-26 20:42:48 +02:00
David Montero Crespo 68d062f526 feat(canvas): wifi-panel overlay slot next to the WiFi badge
Empty span in the OSS build, same pattern as board-status and
serial-actions. A private overlay mounts its network panel here.
2026-08-26 18:57:27 +02:00
David Montero Crespo 52afdd6f23 fix(espidf): patch esp-hosted-mcu's sync-RPC race into every P4 build
Upstream esp-hosted-mcu 2.12.12 mints RPC uids and claims sync-response
table slots with no locking. Two tasks issuing sync RPCs in the same
instant cross-wire: the losing waiter re-reads a slot the winner already
cleared and asserts inside hosted_destroy_semaphore(NULL), panicking the
core the Arduino event task runs on -- so a P4 sketch associates, gets a
DHCP lease, and never sees WL_CONNECTED. Real boards dodge it by timing
jitter; the deterministic in-browser engine hits it on every run, which
is how it was found (watchpoint on the destroy's address, return address
resolved against the ELF).

Managed components are hash-checked and self-restoring, so the fix rides
the component manager's own escape hatch: after the configure that
fetches dependencies, esp32p4 arduino builds get a patched copy of
esp_hosted in components/, which overrides the managed one, and configure
runs once more to adopt it. The patch is five anchored replacements (a
portMUX around uid minting, slot claim, the waiter's read-wait-clear, and
the poster's give); if a future component bump moves the anchors, the
build proceeds UNPATCHED with a loud warning instead of guessing.

The reference diff and the full story live in velxio-prod:
project/espressif-devkits-2026-08/patches/ and STATUS.md (2026-08-26).
2026-08-26 06:52:54 +02:00
David Montero Crespo 98e58f145a feat(editor): File menu row to connect an external AI agent (Claude/Codex)
Declares the project.connectAgent command id and a File-menu row for it.
The row is optional+pro: only the velxio-prod overlay registers a handler
(it owns accounts, the MCP endpoint and the token flow), so bare OSS hides
the row instead of greying it out — same seam as the account.* commands.
2026-08-26 06:19:52 +02:00
David Montero 3989f28eb1 feat(boards): the Zero, the 1B+ and the 2B stop pretending to be a Pi 3
Four Raspberry Pi kinds shipped one picture. BoardOnCanvas routed
raspberry-pi-zero, -1 and -2 straight to <RaspberryPi3>, and the picker card
did the same, both with a comment explaining that the 40-pin header is "the
canvas identity" so the art could be shared.

It cannot, for the Zero. That board is 65 x 30 mm against the Pi 3's
85 x 56, it ships its header UNPOPULATED, and it has neither Ethernet nor a
full-size USB port. Drawing it as a Pi 3 tells a student the opposite of what
is in their hand, and it is a teaching simulator. The 1B+ and the 2B really do
share the Pi 3's outline and connector positions, but not the silicon and not
the silkscreen, and those are exactly what someone reads a board photo for.

Three new illustrations, flat-vector in the house style: the palette is lifted
verbatim from Raspberry_Pi_3_illustration.svg (PCB #037e3f, gold pads #90814e,
mount rings #f9e494, HDMI #e8914f, steel #e5e5e3) and the raspberry silk is the
same path the Pico art uses, so they sit next to the Pi 3 without a seam.

  Zero 2 W   650 x 314 units — bare plated header holes, RP3A0-AU package,
             RF can, microSD cage, mini-HDMI, two micro-USB, CSI on the edge
  1 Model B+ 884 x 578 — BCM2835 / ARM1176JZF-S, "Model B+ V1.2" silk
  2 Model B  884 x 578 — BCM2836 / Cortex-A7, "Pi 2 Model B V1.1" silk

buildPi40PinHeader() was already in the tree, already carrying the BCM name
map, and used by nothing — its geometry was hardcoded to a 250x160 footprint.
It now takes the header origin, pitch and row offsets, so each board passes
the numbers ITS OWN art is drawn from and the pin tips cannot drift off the
pads. Verified by overlaying the computed positions on the rendered art: all
40 land on the drawn holes on all three boards.

Not touched: ComponentRegistry still gives these kinds tagName
velxio-raspberry-pi-3. That tag only feeds PartInspectorDialog's preview, and
the element behind it draws a crude red rectangle for the Pi 3 too — a
separate wart, and one that wants its own fix rather than three more copies.

tsc --noEmit clean across the frontend, build:docker green, and the 76 tests in
board-kinds-coverage + multi-board-integration pass.
2026-08-26 05:06:28 +02:00
David Montero Crespo b35b5aead4 fix(firmware): explain the blank-NVS WiFi failure, and detect WiFi in stripped images (#270)
The reporter sent his three Visuino binaries, and running them settles what his
error was. It was never the network name, and it was not his delay() theory
either.

His first binary reproduces his exact report on the in-browser engine —
`[40][E][STA.cpp:357] connect(): STA connect failed! 0x300a: ESP_ERR_WIFI_SSID`,
timestamps and all — and his latest binary connects and prints its lease, the
same `NAN WiFi stop` line and IP his real-hardware screenshot shows. Same
emulator, same day. The difference is inside the firmware: the failing build
reaches esp_wifi_connect with no usable SSID, and a sketch calling
WiFi.begin() with NO arguments on this simulator produces his error verbatim —
verified. On his real DevKit that same call works, because begin() with no
credentials reuses whatever some earlier firmware saved in NVS, and his chip's
flash had a network in it. The simulator's flash starts blank. Nothing in the
output said any of that, and it cost him days.

So the serial monitor now says it: when `0x300a: ESP_ERR_WIFI_SSID` scrolls
past, one [velxio] line explains the NVS mechanism and gives the literal fix
(`WiFi.begin("Velxio-GUEST", "")`). Wired at the serial batcher, which both
backends feed; it fires once per run, re-arming naturally because a fresh run
starts a fresh log. The delay() theory was measured and does not hold — the
same sketch with and without delay(1) both associate, 48s vs 52s on the
engine, 8.0s vs 8.2s on QEMU.

His binaries also exposed d64b6796's detection as built on sand: it keyed on
Arduino's log strings ('WiFiGeneric', 'STA.cpp'), which vanish when a build
lowers its debug level — his latest image contains neither, so the note would
have stayed silent on precisely the firmware it was written for. The markers
are now IDF driver names (net80211, phy_init, esp_wifi, wifi_init), chosen by
measuring a blink build against a WiFi build: each is absent from the former
and present in the latter. WIFI_INIT looked obvious and is in BOTH, so it is
not on the list. Checked against all three of his images: WiFi detected, his
correct SSID found, note rightly silent.

Separate finding, out of scope here: his merged images reboot-loop on the QEMU
machine (flash-init assert). His build carries CONFIG_SPIRAM=y — the core 3.x
precompiled libs enable it regardless of the PSRAM=disabled menu choice — and
our own build with PSRAM enabled fails the same way on QEMU, which models no
PSRAM chip ('PSRAM ID read error: 0x00000000'). velxio.dev routes ESP32 to the
in-browser engine, where his images run, so this affects only the self-hosted
QEMU path.
2026-08-26 04:51:16 +02:00
David Montero Crespo 45ac020a88 fix(espidf): no interactive REPL on a cloud-built P4 sketch
esp_hosted ships a CLI -- an esp_console REPL on the serial console --
enabled by default, and every ESP32-P4 arduino build was carrying it.
On a cloud-compiled sketch it is worse than useless: its "host>" prompt
interleaves with the user's own serial output, and its console task polls
a UART that in the emulator never blocks, pinning one HP core at
priority 23 and defeating the engine's idle fast-forward. Found while
bringing up the P4's esp_hosted WiFi companion, where it made a
mostly-idle guest emulate two orders of magnitude slower than it should.

Appended in the same esp32p4 block that pins the rev0 mask ROM, for the
same reason: it is a property of running on the emulator's terms, not a
user choice.
2026-08-26 01:12:41 +02:00
David Montero 3348e1fa48 fix(boards): a QEMU-Linux overlay board is not a Raspberry Pi
Two answers about the UNIHIKER M10 came from the family whose run path it
borrows rather than from the board itself.

The upgrade prompt told a free user that "Raspberry Pi emulation is a Pro
feature" about a DFRobot SBC: proBoardFeatureName named the family, so every
piFamily overlay board inherited the Pi's name. It now uses the board's own
label whenever the kind is registered; the OSS families keep theirs.

loadExample gated the guest-script branch on isPiBoardKind(), which reads a
registry the overlay only fills at mount. A direct /example/<id> link could
load before that: the branch was skipped and the editor opened on the board
group's Arduino blink default instead of the example's script.py. vfsFiles is
itself the declaration that this board's code is a guest script, so the
branch no longer consults the registry.

The server-side gate message names the two families instead of one board,
since the QEMU-Linux lane also carries boards that are not Raspberry Pis.

Test covers both, with the unregistered-kind race reproduced.
2026-08-25 19:22:29 +02:00
David Montero Crespo 0430e8e0d4 fix(ci): the release announce rides the same relay as a paid agent account 2026-08-24 22:02:39 -03:00
David Montero Crespo ca427db8ac fix(ci): the metadata gate failed over a missing newline, on every commit
`Fail if components-metadata.json is out of date` has been red on every push
for days. The diff it printed was one line — the last one:

    -}
    +}
    \ No newline at end of file

The committed JSON ends with a newline; the generator wrote
JSON.stringify(...) with none, so `git diff` was never empty and no content
change could ever clear it. The generator now writes the trailing newline,
which makes a fresh run byte-identical to what is committed.

Verified by running the real generator (needs the wokwi-elements clone, and
NODE_PATH pointing at frontend/node_modules): 156 components, `git diff`
empty.
2026-08-25 02:36:06 +02:00
David Montero Crespo 7d35fa842b feat(picker): every closed component now has its ONLINE card
The last ten: the thermal trio (DC motor, heater block, SSR) and the seven
DFRobot Gravity parts — CO, ECG, soil moisture, industrial pH, the C4001
radar, the HuskyLens and the DF2301Q voice recogniser.

That completes the shop window: every part the hosted overlay merges is now
advertised in OSS, thumbnail and all, instead of a self-hoster having no way
to learn it exists. Each card still retires itself in the hosted build.
2026-08-25 02:02:08 +02:00
David Montero Crespo 536af9104e feat(picker): the rest of the closed catalogue gets its ONLINE card too
DS18B20, the three Maxim thermocouple/RTD front-ends and the ILI9341+touch
panel are as free and as online-only as the four added a moment ago; OSS just
showed nothing for them. Same card, same pill, same self-retiring contract.
2026-08-25 01:53:03 +02:00
David Montero Crespo 20e9982836 feat(picker): ONLINE cards for the four Pimoroni/Bosch/Sensirion parts
The overlay's parts are free — they just live in the hosted build. OSS showed
nothing at all for the SCD41, the BME688, the BME280 and the RGB Encoder
Wheel, so a self-hoster could not tell they exist. They get the same ad card
the M5Stack matrices and the Seeed boards already have: real thumbnail, ONLINE
pill, link to the editor. Each one vanishes on its own in the hosted build,
where the registry has the real component.
2026-08-25 01:44:51 +02:00
David Montero Crespo af55a9efb8 docs: stop sending readers to a release that no longer has the binaries
The QEMU libraries left the public `qemu-prebuilt` release: they are
GPL-2.0 derivatives of QEMU, and distributing one obliges us to hand the
recipient the corresponding source, so binary and source now live in one
place -- velxio.dev's gated endpoint, where the qemu-source-<commit>
archive sits beside them.

Six documents still pointed at the release. Two of them shipped runnable
commands that now 404: the deploy-to-container curl pair in the WiFi
notes, and the e2e README's claim about where CI gets its libraries
(that workflow had in fact moved to the gated endpoint some time ago --
only the README was stale).

Also corrects two things that were already wrong before today:
QEMU_RELEASE_URL is documented with the release as its default, but
Dockerfile.standalone defaults it to empty and reaches the binaries via
VELXIO_LICENSE_KEY; and the asset inventory omitted the macOS, Windows
and esp32s3_rev0_rom entries.
2026-08-25 00:24:54 +02:00
David Montero Crespo f035bebf45 test(config): stop running the rsynced copy of the pro overlay
`frontend/src/pro/` is a gitignored COPY some dev setups drop in for a local
pro build. `src/**/__tests__/**` picked it up, so every pro test ran twice —
once from the source at ../../pro/, once from a snapshot — and any file the
snapshot reads from outside frontend/ resolves against velxio/ instead of
pro/. That is how pi-vision-example.test.ts started failing the deploy gate
on `velxio/guest-shims/velxio_screen.py`, a path that has never existed, for
a file nobody had touched.

A clean OSS clone has no src/pro, so the exclude is a no-op there.
2026-08-25 00:13:35 +02:00
David Montero 0b3e165784 feat(intellisense): answer from the local sketchbook, not only the cache
The symbol server assumed one library layout: the content-addressed cache,
where an entry dir name IS its content hash. A host without that cache --
Velxio Desktop's sidecar, and any self-host -- has its libraries where
arduino-cli put them, under plain folder names with the version inside
library.properties, and got a 404 for every request. The editor there falls
back to the engine's embedded catalog, so `client.` on a library the user
installed himself resolves to nothing.

Both layouts are now sources, tried best-first. The only real difference is
mutability: `lib upgrade` rewrites a sketchbook folder in place and keeps its
name, so a scan cannot be keyed by that name. Sketchbook entries are keyed by
a cheap stat-only stamp (file count, total size, newest mtime) in the same
`<norm_name>@<norm_version>-<12 hex>` shape, which makes the disk cache exact
rather than merely long-lived. Cache entries keep their existing keys, so no
deployed scan is invalidated.

Where the sketchbook is: an explicit override, then the sketchbook env vars,
then arduino-cli itself (`config get directories.user`, probed once and
remembered) -- guessing ~/Arduino is wrong on the platforms whose default is
~/Documents/Arduino. A sketchbook that resolves to the cache is dropped: the
pro overlay points arduino-cli at a symlink to the cache root, and scanning
it twice under two shapes buys nothing.

Scans for a sketchbook are written to `<sketchbook>/.velxio-symbols/`, beside
`libraries/` and never inside it, so nothing Velxio writes shows up in
`arduino-cli lib list`.

The type index now keys on a fingerprint of the source's entry keys instead
of the entry count, which is exact for both layouts (an in-place upgrade
changes no count). Deployed indexes rebuild once, ~1s from the cached scans.
2026-08-25 00:06:10 +02:00
David Montero Crespo 4583d9013c docs(qemu): the build guide now describes a build that works
Reported in velxio#271: the guide pointed at commit 822927b6 of
lcgamboa/qemu, which exists in no repository, and told the reader to
recover the canonical SHA by grepping Dockerfile.standalone, which
records no SHA. It also promised the result would match our prebuilts
byte-for-byte.

Three more errors nobody had reached yet, because the bad commit id
stopped them first:

  - configure was invoked with --enable-shared-lib, an option QEMU
    does not have. The library comes from relinking the qemu-system-*
    objects without system_main, which build_libqemu-esp32.sh does.
  - The dependency list omitted libgcrypt, libslirp, libfdt, zlib and
    jq, the meson==1.2.3 pin, tomli (configure dies without it on
    Python < 3.11) and the empty libiconv.a stub glibc hosts need.
  - The ROM list omitted esp32s3_rev0_rom.bin, the S3's own.

Rewritten around what the release CI actually runs. States up front
that an upstream clone cannot build what Velxio runs -- upstream has
Espressif's plain -M esp32s3, not the esp32s3-picsimlab variant the
worker binds its callbacks to, nor the GPSPI2 and ESP32-CAM device
models -- and points at the corresponding-source archive published
next to the binaries on the download endpoint.
2026-08-24 23:54:56 +02:00
velxio-deploy 6ef1e1198b chore(examples): refresh 1 thumb file(s) [auto] 2026-08-24 23:10:22 +02:00
David Montero 6a80f88954 fix(compile): when the scan-all retry fails too, report ITS error
A manifest-scoped compile only sees the libraries the project declares, so
it dies on the first undeclared transitive dependency — a header the user
had no reason to name. The scan-all retry exists to rescue exactly that,
and when it succeeds nothing is lost. When it fails as well, we returned
the SCOPED failure, which describes a library only our own scoping made
missing.

Today a hand-written SSD1306 sketch was reported as

  Adafruit_GFX.h:12:10: fatal error: Adafruit_I2CDevice.h: No such file

while the retry had already resolved BusIO and reached the real bug, an
`#include <Adafruit_SSD1306>` with no .h. The user went looking for a
missing library that was installed all along.

The retry runs in the superset environment, so its error is the sketch's
own in both compilers. Prefer it whenever it carries text; the scoped
result still stands in when it does not.
2026-08-24 22:49:11 +02:00
velxio-deploy 1e47069ad8 chore(examples): refresh 2 thumb file(s) [auto] 2026-08-24 22:18:40 +02:00
velxio-deploy 89fba57b49 chore(examples): refresh 2 thumb file(s) [auto] 2026-08-24 21:35:50 +02:00
David Montero Crespo b2f5a4f1f3 perf(idle-spin): answer the common case first
observe() is called once per emulated instruction, and nearly every one of
those is a step forward inside the same code — not a backward branch, not a
long jump. It was doing the loop-close bookkeeping first anyway: two range
updates and a counter on fields of a foreign object, then the test that would
have told it none of that was needed yet.

The forward case now returns after one comparison and at most one field write.
The loop's lowest PC is only updated where it can actually change — a backward
branch IS the loop top — so nothing about what counts as an idle spin moves.

Measured on the RP2350 host (BadgeOS, MicroPython on the M33), together with a
one-read-per-turn change on the caller's side: +11% instructions per CPU
second, four rounds out of four.
2026-08-24 18:51:13 +02:00
David Montero Crespo 71235f1335 fix(idle-spin): an interpreter's dispatch loop is not a busy-wait
IdleSpinDetector reported a spin from two signals: the same backward branch
closing over and over, and no GPIO change while it did. A bytecode interpreter
matches both — MicroPython's dispatch closes the same loop millions of times
and touches no pin — and eliding it does not skip the work, it only races the
simulated clock ahead of work that is really happening. On the RP2350 that cost
the Badger 2350 its frame budget: BadgeOS executed 500 k guest instructions per
second of guest time where the emulator can do four million.

Two more things now have to hold before a loop counts as idle: its body must be
narrower than 128 bytes, and one turn of it must take fewer than 24
instructions. A delay() that polls the timer is a handful of instructions in a
handful of bytes; a dispatch loop is neither.

Both are ceilings, so this can only ever elide LESS than before — the RP2040's
own delay() elision is unaffected.
2026-08-24 16:01:19 +02:00
David Montero Crespo b33f83b149 fix(examples): one malformed example must not take the gallery down
Typing anything into the /examples search box threw "Cannot read
properties of undefined (reading 'map')" and blanked the page. The
haystack the search matches against is built from
`example.components.map(...)`, and `components` was undefined on 27
examples.

`components` is required by `ExampleProject`, so this reads as
impossible — but the gallery is assembled from overlay example modules
whose factories end in `as unknown as ExampleProject`, and the compiler
stops checking there. The field is a promise the type makes and cannot
keep, so the three places that read it now cope with its absence.

CircuitPreview is the one that matters most: it renders as the fallback
whenever an example's thumbnail 404s, so it meets exactly the examples
nobody has generated art for yet.
2026-08-24 15:22:04 +02:00
David Montero Crespo 8cff0b31e4 fix(boards): the other two compile sites read the Arduino FQBN too
fqbnForLanguage landed on the single-board Run path and on
compileBoardForFlash, but compileAllBoards still read BOARD_KIND_FQBN
directly — so a canvas with more than one board, or any run that goes
through the multi-board path, still refused an ESP32-C5 kit with 'no FQBN
configured' while its ESP-IDF project built fine on its own.
2026-08-24 06:15:40 +02:00
David Montero Crespo f8bef1d1a4 feat(espidf): LVGL and esp_lvgl_port, the layer every official demo's UI sits on
Probed against staging the same way as the previous commit: lvgl.h and
esp_lvgl_port.h both died at "No such file or directory", so a visitor
could not paste a single screen of esp-dev-kits UI code. Declared only
when the source includes the header, so no other build pays for it.
2026-08-24 06:11:24 +02:00
David Montero Crespo fcd83f7424 feat(espidf): the components Espressif's own board demos are built from
A header that lives in a registry component and is not on
_MANAGED_COMPONENT_INCLUDES dies at "No such file or directory": the
project template ships no idf_component.yml, so nothing fetches it. That
list covered five display/camera components, which left the pieces the
official Espressif board demos are actually made of unavailable —
verified by compiling a one-include probe for each against staging:

  led_strip.h            FAIL  (the C3-LCDkit knob_panel's WS2812 driver)
  iot_button.h           FAIL  (its BOOT/knob-press handling)
  iot_knob.h             FAIL  (its EC11 quadrature decoder)

esp_lcd_touch_gt911 (the P4 devkit's panel) and esp_codec_dev (every
audio demo on the VoCat and the P4) are added on the same grounds.

Anyone pasting code from esp-dev-kits or esp-bsp hit this on the first
line. Detection is unchanged — the dependency is declared only when the
source actually includes the header.

Tests: ManagedComponentDetectionTests in test_espidf_pure_mode.py, incl.
negative cases (a user's own my_led_strip.h must not match).
2026-08-24 06:11:24 +02:00
David Montero Crespo 87101175ce fix(boards): an overlay board could never enter ESP-IDF mode
BOARD_SUPPORTS_ESPIDF is a literal set of OSS board kinds and it gates
both the toolbar's ESP-IDF option and setBoardLanguageMode, so no overlay
board could ever be switched into ESP-IDF: loadExample called
setBoardLanguageMode(id, 'espidf') for an `languageMode: 'espidf'` gallery
example, the guard returned early, and the example opened in Arduino mode
with the toolbar saying "Arduino C++". It stayed invisible because the
backend sniffs `app_main` and compiles those sketches as pure IDF anyway —
until a board with no Arduino FQBN at all hit it, and the run died in the
browser at "No FQBN for board kind" before any request left the page.

- ProBoardDef.supportsEspIdf registers the kind into BOARD_SUPPORTS_ESPIDF,
  the same way supportsMicroPython already did for MicroPython.
- ProBoardDef.espidfFqbn + BOARD_KIND_ESPIDF_FQBN + fqbnForLanguage give the
  IDF lane a target for a board whose Arduino FQBN is null (the ESP32-C5
  kits: no arduino-esp32 core supports the C5, and the backend refuses that
  target for Arduino on purpose, but idf.py builds it fine). Both compile
  sites resolve through it instead of reading BOARD_KIND_FQBN directly.
- The language select shows when EITHER alternative mode exists (the P4 is
  ESP-IDF but not MicroPython, so it had no select at all), hides options
  the board does not support, and hides Arduino for a board with no FQBN.
- addBoard no longer seeds an Arduino-less board into 'arduino', where it
  could never compile and the select would show a value it does not carry.

Regression test: overlay-espidf-mode.test.ts.
2026-08-24 06:11:24 +02:00
velxio-deploy c15d099c9e chore(examples): refresh 16 thumb file(s) [auto] 2026-08-24 05:14:16 +02:00
velxio-deploy c26dc77a47 chore(examples): refresh 20 thumb file(s) [auto] 2026-08-24 04:47:19 +02:00
David Montero bc0731f74a fix(sim): the other models that own a pad, and a button with no circuit
Auditing the catalog for parts in the same situation as the DHT22 and the
HC-SR04 turned up three things.

Two more models own a real pad and were not declared: a matrix keypad's
COLUMNS (the QEMU worker even keeps a `_keypad_cols_owned` set, idles them
HIGH and drives them on each scan) and an ePaper panel's BUSY line. Neither is
broken with plain wiring today, only because the connector's older
`sourcedNets` gate happens to skip a net nothing else sits on — put a pull-up
on a keypad column and that gate stops holding, which is exactly how a
level-shifted HC-SR04 lost its echo. Their DC / CS / RST stay absent on
purpose: the host drives those.

Ownership is therefore no longer "is this a single-wire sensor?" but "which
fields does this model declare as pads it drives", which also lets a field
hold a LIST of pads (the keypad's columns).

And `pushbutton-6mm` did nothing at all, on every board. Three parts hand
control of their pin to the electrical solve rather than seeding it; two of
them are modelled by SPICE and the 6mm variant was not, so it stood down for a
circuit that never modelled it. It is the same switch as the full-size one, so
it now shares that mapper through a MAPPER_ALIASES table. Verified in a real
browser: on production the button visibly depresses while the sketch keeps
reading `released`; with this build it reads PRESSED and returns.

The last part is the guard: a test reads the parts themselves and fails if any
part that defers to SPICE has no model, so the next variant is covered the day
it is written. It catches pushbutton-6mm when the alias is removed.
2026-08-23 08:18:18 +02:00
David Montero Crespo 041b08fa21 feat(pio-peripheral): let a peripheral take the words instead of the PIO
Two additions, both for peripherals that model the far end of the wire
rather than watch it arrive bit by bit.

feedWord() now carries which state machine wrote the word, where the host
plumbing knows it, so a peripheral can tell one program from another.

consumesTxWords() is the new one that matters: it says "I have this word,
the PIO does not need to shift it out". A Pimoroni Unicorn's panel spends
about 300,000 PIO cycles per refresh in the program's BCD delay loops, and
every one of them is wasted once the pixels have been read out of the FIFO
word. Unlike inDiscardableWriteData(), the peripheral is still fed the
word — that is the whole point of it.
2026-08-23 07:45:05 +02:00