An ESPHome application firmware: a Thread border router backhauled over Wi-Fi HaLow, with 2.4 GHz captive-portal provisioning and Home-Assistant-managed onboarding — the whole lifecycle in one image.
- Boots unprovisioned → brings up a 2.4 GHz
halow-setupAP + captive portal (scan for HaLow networks, pick one, enter the passphrase). - On submit, connects HaLow and tears the setup AP down (credentials persist in NVS).
- Once HaLow is up, starts the Thread border router (802.15.4 radio, HaLow backbone) and an OTBR REST API on :80 so Home Assistant can adopt it and push a Thread dataset.
Built on the reusable morse-zandr/esphome-halow
component (this repo pulls it via external_components; see the examples).
- ESPHome 2026.7.0 — the bundled
openthreadoverride is version-pinned, and it must match the ESPHome release thatesphome-halow'snetworkoverride targets (they compile into one image together — see Version coupling below). - ESP-IDF framework; a single-app partition (
partitions-singleapp.csv) to fit OpenThread BR + the mmhalow stack on 4 MB (no OTA slot). - ESP32-C6 host (native 802.15.4 for Thread) + a Morse Micro MM6108/MM8108 module over SPI.
| Component | Role |
|---|---|
halow_provision |
2.4 GHz SoftAP + captive portal (scan, SAE/OWE detect, re-provisioning) |
halow_thread_br |
binds the HaLow netif as the OT backbone + border_router_init |
openthread |
ESPHome's openthread, vendored with border-router patches |
xiao_c6_antenna |
drives the XIAO ESP32-C6 2.4 GHz antenna RF switch |
otbr_rest/ |
vendored REST-only OTBR web server (ot-br-posix-compatible, port 80) |
All pull esphome-halow from a sibling checkout (../esphome-halow/components) for local
dev; swap to the github:// source (commented in each) for release. Clone both repos side by
side:
some-dir/
esphome-halow/ # the component repo
halow-thread-br/ # this repo
example-halow.yaml— the deployable product: unified provision → HaLow → BR, one image.example-halow-provision.yaml— provisioning only (MM8108 / 3-0067 carrier).example-halow-seeed-provision.yaml— provisioning on the Seeed XIAO HaLow board preset.example-halow-thread-br.yaml— BR with bench (YAML) HaLow credentials, no captive portal.
Once the BR is up, HA discovers it via _meshcop._udp mDNS but shows it read-only until you
add the OpenThread Border Router integration pointed at http://<device-ip> (port 80) —
the meshcop advert doesn't carry the REST port, so HA can't auto-manage it. After that, HA's
"reset / create network" pushes a dataset (PUT /node/dataset/active) and the BR adopts it.
- OpenThread is deferred until HaLow connects. A HaLow-backhauled BR has nothing to route
without its backbone, and starting the 802.15.4 radio during provisioning contends with the
2.4 GHz SoftAP on the shared C6 radio (the setup AP beacons but clients can't associate). So
the
openthreadstart is gated on HaLow being connected (underUSE_HALOW_THREAD_BR). border_router_initwaits for OT to be fully started (openthread::is_started()), not just instantiated — otherwise it races OT settings init and asserts.- The
:80handoff is sequenced: the captive portal owns :80 while provisioning; the OTBR REST server only starts afterborder_router_init, by which point the portal is gone.
This is not a standalone repo. Its components depend on (and #include) the halow
component, and its vendored openthread override compiles together with esphome-halow's
network override — both are patches of the same ESPHome release. On an ESPHome upgrade,
re-sync components/openthread/ here and components/network/ in esphome-halow to the same
version, in lockstep.
First-party code is clang-formatted with Zephyr's .clang-format (repo root). The vendored
openthread/ and otbr_rest/ trees carry DisableFormat guards (kept byte-diffable against
upstream).
GPLv3 — see LICENSE. Bundles ESPHome's openthread (GPLv3) and Espressif's
esp_ot_br_server REST server (otbr_rest/, Apache-2.0 — one-way compatible with GPLv3, so
the combined work is GPLv3). The bundled Lato font (embedded in halow_provision) keeps its
own SIL Open Font License 1.1.