Skip to content

Add support for iLabs Hearth, a Matter compatible framework for RP2040 and RP2350 - #3498

Merged
earlephilhower merged 14 commits into
earlephilhower:masterfrom
PontusO:master
Aug 24, 2026
Merged

Add support for iLabs Hearth, a Matter compatible framework for RP2040 and RP2350#3498
earlephilhower merged 14 commits into
earlephilhower:masterfrom
PontusO:master

Conversation

@PontusO

@PontusO PontusO commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

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.

pontus-svep and others added 14 commits July 23, 2026 17:32
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
@earlephilhower
earlephilhower merged commit 2ddbe72 into earlephilhower:master Aug 24, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants