Add support for iLabs Hearth, a Matter compatible framework for RP2040 and RP2350 - #3498
Merged
Conversation
Part 1: bundle the iLabs_ESP-NOW library as a git submodule under libraries/, the same way ESPHost/AsyncUDP/SdFat ship, pinned to v1.0.0. Users get the ESP-NOW API out of the box. The library's AT+EN protocol uses an "+ENVER" firmware-version token that codespell reads as a typo of "never", so "enver" is added to the codespell ignore_words_list. Part 2: expose ESP-NOW as a selectable "ESP Wifi Type" for the iLabs boards (tools/makeboards.py + regenerated boards.txt/JSON): - BuildWifiType gains a 3rd option (ESP-NOW) for the two boards that already have the menu (challenger_2040_wifi_ble, challenger_2350_wifi6_ble5). - New BuildWifiTypeAT (ESP AT default + ESP-NOW) wired to the five previously-hardcoded WIFIESPAT2 boards (challenger_2040_wifi, challenger_2040_wifi6_ble, challenger_nb_2040_wifi, connectivity_2040_lte_wifi_ble, ilabs_rpico32). - The hardcoded ["WIFIESPAT2"] extra_flag is removed from those five boards; the menu's ESP AT default now supplies -DWIFIESPAT2, so selecting ESP-NOW yields -DILABS_ESPNOW alone (no double define). ESP-AT stays the default on every board, so existing builds are unchanged. PlatformIO users of the five converted boards must now set the desired flag in platformio.ini (matching the existing menu boards, whose JSON already carries no espwifitype define). Part 3: gate the variant ESP reset on ESP-NOW not being selected. When -DILABS_ESPNOW is set the iLabs_ESP-NOW library owns the ESP reset sequence (ESP_NOW.setLink()), so the AT-style reset in initVariant() is compiled out under #ifndef ILABS_ESPNOW: - challenger_2040_wifi, challenger_2040_wifi_ble, challenger_2040_wifi6_ble, challenger_2350_wifi6_ble5 and challenger_nb_2040_wifi already reset Challenger2040WiFi in initVariant(); that call is now gated. - ilabs_rpico32 had no board_init.cpp; add one that gates the reset of its Ilabs2040WiFi object the same way. Part 4: document the feature in docs/ilabs_espnow.rst (linked from the index toctree): what ESP-NOW is, the supported iLabs boards, selecting it from the ESP Wifi Type menu, the two sketch changes versus an ESP32, a first sketch, board reset handling and discovery. Part 5: skip the library examples on the RP2350 CI jobs. They require a board variant that defines ESP_SERIAL_PORT; CI builds against a generic rpipico2, and the .ci.defines stub only reaches rpipico. This mirrors how lwIP_ESPHost / lwIP_WINC1500 examples are already skipped on rp2350. The examples still build on the rp2040 job, which is enough to cover them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNWVafzGfoYtpWKUuy46eG
…eeds libraries/iLabs_Hearth pinned at 1.0.0, the same way ESPHost, AsyncUDP, SdFat and iLabs_ESP-NOW already ship. tools/esptool and tools/intelhex are here for Burn Bootloader, which a later commit wires up. The library's fw/flash.py refuses to run without the iLabs esptool fork, and this core's bundled interpreter has none. esptool declares cryptography, a compiled wheel that could not ship as a submodule, but nothing in the esptool package imports it: cryptography lives in espsecure, which an unencrypted write_flash never reaches. With the fork's CLI split out, the closure is pyserial, which this core already vendored, plus intelhex.
The two menu builders cut across the line that matters. BuildWifiType served the 2350 and challenger_2040_wifi_ble, which carries an ESP32-C3. BuildWifiTypeAT served five boards of which one is a C6 and two are ESP8285. A fourth print in either would have offered a Matter firmware to a co-processor that cannot run it, so the two collapse into one parameterised function and the call sites name the boards individually. The five non-C6 boards' generated output is byte-identical by construction, which is the acceptance test: boards.txt may differ only inside the two C6 blocks. The three options are identical to the compiler and differ only in the image the flasher writes. The label names the product and describes the protocol: Matter is a CSA word mark and is not this firmware's name.
bootloader.tool appeared zero times in a 20,000-line boards.txt and programmers.txt held nothing but its licence header, because an RP2040/RP2350 has its boot ROM in silicon and there is no bootloader to burn. So the hook was free. An uploadmethod entry was the alternative and would have silently stopped Upload from uploading sketches. The semantics happen to fit. Flashing the C6 goes through the RP2350 bridge sketch, which flash.py writes over the running sketch via a 1200-baud BOOTSEL touch, so burning the co-processor always costs the host sketch. That is what a user expects Burn Bootloader to mean. tools/hearthfw.py is a shim, not a second flasher: it validates the variant, puts pyserial, the esptool fork and intelhex on sys.path ahead of site-packages, and hands off to the library's own fw/flash.py. With no Hearth option selected the variant is empty and it says which menu to use rather than picking an image for you.
Review ran the tool the only way a user ever reaches it, through
arduino-cli burn-bootloader, and it failed four times over before the
shim's first line ran.
arduino-cli 1.4.1 resolves the burn tool through bootloader.tool.default
and does not fall back to the bare key, so both forms are now emitted on
the two C6 boards, matching how every upload.tool line in this platform
already works.
Burn Bootloader runs an erase recipe before the bootloader recipe, and a
missing recipe is a hard error rather than a no-op. An empty
tools.hearthfw.erase.pattern is accepted and skipped.
arduino-cli's command splitter drops the empty token from
--variant "{bootloader.variant}" when no Hearth option is selected, so
the shim ran as --variant --port /dev/... and argparse printed a usage
error instead of the Hearth menu message. The pattern now uses the
single-token --variant= form, which survives the splitter; the shim
already parsed it correctly, this only needed proving.
programmers.txt held nothing but its licence header, so arduino-cli
refused up front with no Programmer to select. It gets one name-only
entry. There is no mechanism to scope a programmer to a board, so it is
visible under every board in the core by construction; picking it
anywhere but a Challenger with a Hearth-capable C6 just reaches the
shim's own message.
Two minors while in the shim: an unrecognised non-empty variant now says
so, rather than reusing the no-variant-selected wording, and a comment
notes that arduino-cli always supplies --port on the path that reaches
the pattern, so the no-port branch is a shell-only convenience.
Verified against arduino-cli 1.4.1 itself, not just a shell invocation
of the shim: the empty-variant path prints the five-line menu message
and fails cleanly before flash.py runs, and the dry-run path reaches
flash.py, prints the full flash plan and exits 0, with no hardware
touched either time.
76 example sketches, 54 of them under examples/FullAPI, would have gone into the CI matrix and added roughly half an hour to every pull request. The library carries its own 4716-check host suite and a hardware regression harness. What is given up is -Werror coverage against a plain rpipico, which is a real but small loss against that. The entry goes in the general skiplist rather than the rp2350-only one where iLabs_ESP-NOW sits, so it applies on both architectures. CLAUDE.md's submodule list was not touched: this repo's CLAUDE.md is gitignored and untracked, so there is nothing to commit there.
Named for the product rather than the protocol, matching ilabs_espnow.rst and for the same reason: Matter is a CSA word mark and is not this firmware's name. Three things a user would otherwise find out the hard way are on the page rather than in a commit message: Burn Bootloader overwrites the sketch on the host, because flashing the C6 runs through a bridge sketch written over whatever was there; the endpoint ceiling from the IDE is 24 and not the firmware's 28; and the firmware is uncertified with development credentials, so consumer hubs are expected to refuse it.
…ay less The flasher detects the RP2350's mass-storage mount on linux and darwin and nowhere else, and the esptool fork does not try the RP2040 bridge reset on Windows, so a Windows user who clicks Burn Bootloader gets a failure the page never warned about. Now it does, with the two places in the code that make it so. The esptool pin moves to the re-cut v5.3.1-ilabs.1, whose only change is that the RP2040 reset strategy stops printing a debug line into every Burn Bootloader console.
…-NOW's Upstream will not take a repurposed Burn Bootloader, nor the platform-wide Programmer row it needed to be reachable at all. This reverts 2233f9e and 1916f3f (the shim, the platform.txt tool block, the programmers.txt entry and the bootloader.* properties on the two C6 boards), drops the tools/esptool and tools/intelhex submodules that existed only for the shim, and trims the two credit lines that went with them. What stays is the ESP-NOW shape: the library as a submodule, the three Hearth options in the ESP Wifi Type menu, and libraries/iLabs_Hearth/fw/ flash.py as the one way to put firmware on the C6. The docs page's flashing section now documents that path, the esptool fork it needs via ILABS_ESPTOOL_PATH, and the Linux/macOS limit, which was the flasher's all along and not the hook's. boards.txt differs from master only inside the two C6 blocks, 18 lines, the same confinement the menu commit was held to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018cTWDNcf3NfGyxmvJv3Nzb
…TH too The iLabs_Hearth library owns the co-processor reset the same way the ESP-NOW library does: it resets the C6 on first use and waits for +MTREADY. With the variant also resetting it in initVariant() the C6 was reset twice per boot, harmless but pointless, and a difference from the ESP-NOW gate that would need explaining. Only the two boards that can see ILABS_HEARTH change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018cTWDNcf3NfGyxmvJv3Nzb
It has been run on hardware since the page was written, so the two caveat paragraphs come out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018cTWDNcf3NfGyxmvJv3Nzb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
iLabs Hearth is a Matter firmware for the ESP32-C6 on the Challenger WiFi6/BLE boards, driven from the RP2040/RP2350 over the same UART and AT-style protocol the bundled iLabs_ESP-NOW library uses. The host library is source-compatible with the arduino-esp32 Matter class API: an unmodified MatterOnOffLight sketch from arduino-esp32 builds, commissions over BLE and runs over WiFi or Thread. 52 device types, from on/off light through thermostat, door lock, RVC, EVSE and the energy management set.
Part 1: bundle the iLabs_Hearth library as a git submodule under libraries/, pinned to 1.0.0, exactly as iLabs_ESP-NOW ships. The library carries the three prebuilt C6 images (WiFi, Thread, WiFi+Thread) and a terminal flasher, fw/flash.py, so a user never needs the IDF toolchain. The library's 76 example sketches are skiplisted in tests/common.sh; it has its own host test suite (4716 checks) and a hardware regression harness.
Part 2: three new ESP Wifi Type options, Hearth (Matter, WiFi | Thread | WiFi+Thread), on the two ESP32-C6 boards only (challenger_2350_wifi6_ble5, challenger_2040_wifi6_ble). The two menu builders in makeboards.py collapse into one BuildWifiType(name, hosted, matter) so a board gets exactly the options its co-processor can run. boards.txt differs from master only inside those two boards' blocks; ESP AT stays the default everywhere and no existing build changes. The define is -DILABS_HEARTH, set alone, with -DWIFIESPAT2 absent.
Part 3: docs/ilabs_hearth.rst, linked from the toctree: what the firmware is, choosing an image, flashing the C6 from the terminal, writing a sketch, the 24-endpoint ceiling, and what the firmware does not claim (uncertified, development VID, consumer hubs are expected to refuse it).
What a user gets: install the core, pick the board, pick a Hearth option, flash the C6 once with fw/flash.py, open an arduino-esp32 Matter example, upload. Verified end to end on a Challenger RP2350 WiFi6/BLE5 with all three images, and run on the RP2040 WiFi6/BLE.
This was designed, implemented, reviewed and bench-tested by me working with Claude Code, almost entirely autonomously on its side: it wrote the spec and plan, which we reviewed and amended in several iterations, dispatched its own implementers and reviewers per task, ran the hardware verification fully autonomously. I set the direction and made the calls; it did the work. For me it has been a very interesting experiment in how AI can be used to create and iterate plans then write specs for review and then it goes away and creates everything by itself, even testing it on hardware connected to a development server.