tag:github.com,2008:https://github.com/tostmann/esp-coordinator/releases Release notes from esp-coordinator 2026-07-13T13:42:03Z tag:github.com,2008:Repository/956036346/v1.5.73 2026-07-13T13:43:53Z v1.5.73 — Coordinator IEEE byte-order fix (issue #6) <h1>v1.5.73 — Coordinator IEEE byte-order fix (issue <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="4855142076" data-permission-text="Title is private" data-url="https://github.com/tostmann/esp-coordinator/issues/6" data-hovercard-type="issue" data-hovercard-url="/tostmann/esp-coordinator/issues/6/hovercard" href="https://github.com/tostmann/esp-coordinator/issues/6">#6</a>)</h1> <h2>What was wrong</h2> <p><code>GET_LOCAL_IEEE_ADDR</code> (the NCP command a host uses to read the coordinator's own<br> 64-bit IEEE address) returned the address in <strong>big-endian</strong> byte order. On the<br> ZBOSS NCP link IEEE addresses are <strong>little-endian</strong>, so the host stored the<br> coordinator's IEEE <strong>with all 8 bytes reversed</strong>.</p> <p>Consequence: the host (Zigbee2MQTT / zigbee-herdsman, and any ZBOSS-NCP host)<br> recorded a <em>reversed, phantom</em> coordinator IEEE and used it as the destination<br> when it bound devices to the coordinator. Any device that then has to resolve the<br> coordinator by IEEE — a bound switch (e.g. IKEA E2201/RODRET), or a sensor bound<br> to report to the coordinator — sends <strong>Network Address Requests for an address no<br> device on the mesh owns</strong>, so it never resolves and retries endlessly; its bound<br> commands/reports are never delivered. Devices addressed by short address <code>0x0000</code><br> (most routine traffic) were unaffected, which is why the symptom looked narrow.</p> <h2>What the fix does</h2> <p><code>GET_LOCAL_IEEE_ADDR</code> now returns the operational long address in correct<br> little-endian (wire) order — the same order every other IEEE field already used.<br> Fixed at the source, so a corrected coordinator IEEE is handed out from now on.</p> <h2>Migration — required for existing installs</h2> <p>The firmware fix prevents the wrong address from being handed out again, but two<br> copies of the old reversed address live off-chip and must be refreshed:</p> <ol> <li><strong>Flash v1.5.73.</strong></li> <li><strong>Restart your host (Zigbee2MQTT).</strong> It re-reads the coordinator IEEE on start,<br> sees it changed, logs <code>Coordinator address changed, updating to '0x…'</code>, and<br> corrects its own database automatically. No manual database editing needed.<br> (Verified against zigbee-herdsman end-to-end.)</li> <li><strong>Re-pair or re-bind the affected devices.</strong> A device keeps the old reversed<br> coordinator address in <em>its own</em> binding table until it is re-paired or its<br> bindings are removed and recreated. For each device stuck in a request loop or<br> not delivering bound reports (bound switches, sensors bound to the<br> coordinator): re-pair it, or remove+recreate its bindings / run <em>Reconfigure</em>.</li> </ol> <h2>What is NOT affected</h2> <p>The coordinator's <strong>actual on-air identity, PAN ID, network keys and device<br> associations were always correct</strong> — only the value <em>reported to the host</em> was<br> reversed. No network re-formation is required; the mesh is intact.</p> <h2>ZHA / zigpy-zboss users</h2> <p>zigpy-zboss decodes the IEEE per spec (little-endian, <code>t.EUI64</code>), so the fix is<br> correct for it as well. Its host-side behavior on a coordinator-IEEE change has<br> not been bench-verified here; expect the same "re-bind the affected devices" step<br> and watch the startup log after flashing.</p> <h2>Also in this release</h2> <ul> <li>APSDE request-slot watchdog: reclaims request-table slots left in the executing<br> state (e.g. after an unconfirmed delivery) so the 16-slot table cannot saturate<br> and cause network-wide poll timeouts.</li> </ul> tostmann tag:github.com,2008:Repository/956036346/v1.5.70 2026-07-11T10:45:25Z v1.5.70 — NWK_LEAVE_IND spec-compliant + ZDO indication trims (#6) <h2>v1.5.70 — ZBOSS-NCP indication layout fixes (issue <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="4855142076" data-permission-text="Title is private" data-url="https://github.com/tostmann/esp-coordinator/issues/6" data-hovercard-type="issue" data-hovercard-url="/tostmann/esp-coordinator/issues/6/hovercard" href="https://github.com/tostmann/esp-coordinator/issues/6">#6</a>)</h2> <p>Fixes the coordinator's ZDO/NWK <strong>indication</strong> payloads so they match the ZBOSS NCP Serial Protocol byte layout expected by both host stacks (Zigbee2MQTT <code>adapter:zboss</code> / zigbee-herdsman and zigpy-zboss). Reported in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="4855142076" data-permission-text="Title is private" data-url="https://github.com/tostmann/esp-coordinator/issues/6" data-hovercard-type="issue" data-hovercard-url="/tostmann/esp-coordinator/issues/6/hovercard" href="https://github.com/tostmann/esp-coordinator/issues/6">#6</a> (IKEA E2201 / RODRET).</p> <ul> <li><strong><code>NWK_LEAVE_IND</code> (0x040b)</strong> — previously sent as the raw ZBOSS signal struct, which carries a leading 16-bit short address the protocol does not define here. The host decoded <code>short_addr ++ ieee[0:6]</code> as the IEEE address and the 7th IEEE byte as the rejoin flag — the <em>"Device leave is from unknown or already deleted device"</em> symptom in <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="4855142076" data-permission-text="Title is private" data-url="https://github.com/tostmann/esp-coordinator/issues/6" data-hovercard-type="issue" data-hovercard-url="/tostmann/esp-coordinator/issues/6/hovercard" href="https://github.com/tostmann/esp-coordinator/issues/6">#6</a>. Now emitted spec-exact as <code>[ieee(8) | rejoin(1)]</code>. <strong>Effect of the old bug: on-air device leaves were never processed</strong> (the device stayed in the host database, no leave event).</li> <li><strong><code>ZDO_DEV_ANNCE_IND</code> (0x020c)</strong> and <strong><code>ZDO_DEV_UPDATE_IND</code> (0x0215)</strong> — trimmed to the spec-exact 11-byte payloads (drop the struct alignment pad / the non-spec <code>tc_action</code>+<code>parent_short</code> trailing bytes). Both hosts already tolerated the extra bytes, so this is hygiene, not a behaviour change.</li> <li><code>ZDO_DEV_AUTHORIZED_IND</code> (0x0214) left unchanged (already spec-exact).</li> </ul> <p>Verified end-to-end on test hardware: a real end-device join yields correct <code>DEV_UPDATE</code>/<code>DEV_ANNCE</code> fields, and a device leave yields <code>NWK_LEAVE_IND = [ieee|rejoin]</code> with the true IEEE; the coordinator does not self-wipe across the leave. Firmware rebuilt for all three targets (ESP32-C6 / C5 / H2).</p> <blockquote> <p>Note: the separate <em>"IKEA E2201 switches stuck in a Network Address Request loop / actions lost"</em> part of <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="4855142076" data-permission-text="Title is private" data-url="https://github.com/tostmann/esp-coordinator/issues/6" data-hovercard-type="issue" data-hovercard-url="/tostmann/esp-coordinator/issues/6/hovercard" href="https://github.com/tostmann/esp-coordinator/issues/6">#6</a> is a distinct, sleepy-end-device address-resolution issue still under investigation — not addressed by this release.</p> </blockquote> tostmann tag:github.com,2008:Repository/956036346/v1.5.0 2026-06-23T13:18:31Z v1.5.0 — ESP32-H2 support <h2>ESP32-H2 support</h2> <p>v1.5.0 adds the <strong>ESP32-H2</strong> as a third build target alongside the ESP32-C6 (reference platform) and the ESP32-C5 — the same firmware, from one codebase.</p> <p>Validated end-to-end on real ESP32-H2 silicon: boot + NCP over native USB-Serial/JTAG, 802.15.4 network formation, NVRAM resume across a reboot, and on-air pairing of a real Zigbee device (join → authorize → interview). Independently confirmed beforehand on two further boards and host stacks (Zigbee2MQTT and ZHA), see <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="4684472530" data-permission-text="Title is private" data-url="https://github.com/tostmann/esp-coordinator/issues/5" data-hovercard-type="issue" data-hovercard-url="/tostmann/esp-coordinator/issues/5/hovercard" href="https://github.com/tostmann/esp-coordinator/issues/5">#5</a>.</p> <h3>Flashing</h3> <p>The web flasher auto-detects the connected chip and serves the matching image: <a href="https://install.busware.de/zboss/" rel="nofollow">https://install.busware.de/zboss/</a></p> <h3>Notes for ESP32-H2</h3> <ul> <li>The 1900K application partition requires a <strong>4 MB flash</strong> board (e.g. ESP32-H2-MINI-1).</li> <li>The H2 has no Wi-Fi, so the Wi-Fi-coexistence build does not apply — it is a pure USB/UART coordinator.</li> </ul> <p>The ESP32-C6 remains the reference platform.</p> tostmann tag:github.com,2008:Repository/956036346/v1.4.63 2026-06-19T12:37:01Z v1.4.63 — coordinator-wipe hardening <h2>v1.4.63 — coordinator-wipe hardening</h2> <p><strong>Critical fix.</strong> A routine "remove device" in Zigbee2MQTT could make the coordinator wipe its <strong>own</strong> network. If you have ever lost your Zigbee network unexpectedly, this is very likely why — please update.</p> <h3>What was wrong</h3> <p>When you remove a device, the host sends a ZDO Mgmt_Leave to the coordinator. The firmware routed that through a raw low-level ZBOSS call which, for an unknown/unresolvable target, made the coordinator perform a network-leave on <strong>itself</strong> — wiping its live network and NVRAM with no reboot (~2 out of 3 leaves on the bench; independent of the target address or flags). Removing an offline device (which Zigbee2MQTT force-removes) was enough to trigger it.</p> <h3>Fixes in this release</h3> <ol> <li><strong>Leave self-wipe (the root cause).</strong> Device-leave now goes through the official esp-zigbee-lib API <code>esp_zb_zdo_device_leave_req()</code>, which does not self-wipe; a self-addressed leave (<code>dst=0x0000</code>) is refused outright. Verified end-to-end: a real device removed via Zigbee2MQTT is removed correctly <strong>and</strong> the coordinator keeps its network.</li> <li><strong>Restore buffer-before-commit gate.</strong> <code>RESTORE_NETWORK</code> now buffers the full 40 KB image in RAM and structurally validates it before writing anything to flash, so a corrupt/truncated restore is side-effect-free — your existing network and credentials are preserved, no reboot.</li> <li><strong>Poison-backup guard.</strong> <code>GET_NETWORK_BACKUP</code> refuses to snapshot a coordinator that is not currently on a network, so a host cannot store an un-restorable backup of an already-blank stack.</li> </ol> <h3>Flashing</h3> <p>Web flasher: <a href="https://install.busware.de/zboss/" rel="nofollow">https://install.busware.de/zboss/</a> (ESP32-C6 and ESP32-C5). Or <code>esptool write_flash 0x0</code> with the attached factory image for your chip. Flashing the factory image does <strong>not</strong> erase your Zigbee network — it lives in a separate flash partition.</p> <p>Bench-validated end-to-end on ESP32-C6 over USB.</p> tostmann tag:github.com,2008:Repository/956036346/v1.4.0 2026-06-17T10:30:21Z v1.4.0 — ESP32-C5 support (multi-target) <h2>v1.4.0 — ESP32-C5 support (multi-target)</h2> <p>The firmware now builds and runs on the <strong>ESP32-C5</strong> in addition to the ESP32-C6, from a single codebase. The web flasher auto-detects your chip and serves the matching image.</p> <h3>What's new</h3> <ul> <li><strong>ESP32-C5 support.</strong> Full ZBOSS NCP coordinator on C5 silicon — USB-Serial/JTAG transport, 802.15.4 network formation and NVRAM resume across reboot, validated end-to-end with <code>zigpy-zboss</code>. Memory headroom on the C5 (384 KB SRAM): the coordinator links with ~189 KB of internal RAM free and uses only 34 % of the app partition.</li> <li>Per-target NCP-UART pin defaults — <strong>C5: TX=GPIO23, RX=GPIO24</strong> (the C6 defaults GPIO22/23 land on the C5's SPI-flash pins). Override via <code>menuconfig</code> → <code>NCP_UART_*</code>.</li> <li>Separate per-chip factory images: the C5's second-stage bootloader sits at flash <code>0x2000</code>, the C6's at <code>0x0</code>. Each image is still written from <code>0x0</code>.</li> <li>Build a target with <code>idf.py set-target esp32c6|esp32c5</code> (the default config is no longer pinned to one chip; CMake fails loudly if no target is set).</li> </ul> <h3>Status / caveats</h3> <ul> <li>The <strong>C6 remains the reference platform.</strong> C5 support is validated on one board (rev v1.0) so far.</li> <li>The C5 NCP-UART pin defaults are datasheet-safe on standard Espressif C5 boards but have not been hardware-tested; third-party board pinouts may need an override. The default USB path needs none of this.</li> </ul> <h3>Flash</h3> <p>Web installer: <a href="https://install.busware.de/zboss/" rel="nofollow">https://install.busware.de/zboss/</a> (auto-detects C6/C5)</p> <p>CLI:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="esptool.py --chip esp32c5 write_flash 0x0 binaries/factory-c5.bin # ESP32-C5 esptool.py --chip esp32c6 write_flash 0x0 binaries/factory.bin # ESP32-C6"><pre class="notranslate"><code>esptool.py --chip esp32c5 write_flash 0x0 binaries/factory-c5.bin # ESP32-C5 esptool.py --chip esp32c6 write_flash 0x0 binaries/factory.bin # ESP32-C6 </code></pre></div> tostmann tag:github.com,2008:Repository/956036346/v1.3.57 2026-06-16T12:40:54Z v1.3.57 — GET_NWK_KEYS for zigpy-zboss 2.0.2 / ZHA compatibility <h2>ZHA / zigpy-zboss 2.0.2 compatibility</h2> <p>zigpy-zboss 2.0.2 changed how the network key is read at startup and now <strong>hard-aborts the connection</strong> when <code>GET_NWK_KEYS</code> returns no usable key — where 2.0.1 silently tolerated the unimplemented command. As a result, ZHA (and zigpy-zboss 2.0.2 generally) could no longer resume an already-formed network against this firmware.</p> <p>This release implements <code>GET_NWK_KEYS</code> (0x001e), returning a fixed, recognizable placeholder key so the resume path succeeds. The NCP holds the real key and performs all cryptography itself; the host-side key is bookkeeping only — a ZHA network backup is not restorable against this firmware regardless (the frame-counter restore command is not implemented; use <code>GET_NETWORK_BACKUP</code> / <code>RESTORE_NETWORK</code> for a full NVRAM snapshot). Zigbee2MQTT / herdsman never queries <code>GET_NWK_KEYS</code> and is unaffected.</p> <p><strong>Validated</strong> end-to-end: zigpy-zboss 2.0.2 resume + blank auto-form over USB and over a TCP-bridged link, full Home Assistant / ZHA coordinator init, and a live device pairing.</p> tostmann tag:github.com,2008:Repository/956036346/v1.3.56 2026-06-07T16:00:24Z v1.3.56 — host-controlled permit join, truthful PAN getter, boot-guard safe mode <p>Three independent hardenings, each root-caused and validated on real hardware.</p> <h2>Host-controlled permit join (BOOT-1)</h2> <p>The coordinator no longer opens its network for joining without being told to. Previously, BDB network steering ran at every boot and after each successful formation — steering on a formed network broadcasts <code>Mgmt_Permit_Joining(180 s)</code>, so the network could be open for joining for 3 minutes after a plain reboot with no host attached. The boot path now runs BDB initialization only (NVRAM resume, or idle when factory-new), and permit join is exclusively driven by the host (<code>NWK_PERMIT_JOINING</code> / <code>ZDO_PERMIT_JOINING_REQ</code>, i.e. zigbee2mqtt's <code>permit_join</code>).</p> <p>Validated on-air: a continuously steering factory-new probe device could not join for 220 s after a coordinator reboot, then joined 4 seconds after a host permit-join command.</p> <p>Note: any device already ON the network may still broadcast a 180 s permit-join window after its own steering (e.g. when it rejoins) — that is spec-compliant Zigbee behaviour and unchanged.</p> <h2>Truthful PAN getter (PAN-1)</h2> <p><code>SET_PAN_ID</code> on a formed network is now rejected (<code>OPERATION_FAILED</code>) instead of silently rewriting only the cache that the getters read. Hosts that write their desired settings after forming (zigpy-zboss does) previously saw their requested PAN echoed back by every getter while the device actually ran — and persisted — a different one; after the next reboot the PAN appeared to have changed, which can trip a host's "network changed, reinitialize" logic. Setting the PAN before formation (zigbee-herdsman's flow) keeps working and is honoured at formation time.</p> <h2>Boot-guard safe mode (WEDGE-1)</h2> <p>New resilience layer for a failure class where a power event can leave the stick with a permanently silent application (suspected hang early in stack init). The firmware now counts boots that die before the Zigbee stack comes up (a 30 s deadline converts silent hangs into countable reboots); after 3 consecutive failures it boots into a sticky safe mode: the serial link comes up without the Zigbee stack, <code>GET_MODULE_VERSION</code> and <code>NCP_RESET</code> are served, and everything else answers <code>GENERIC_BLOCKED</code> — the stick stays reachable and diagnosable instead of appearing dead, and <code>NCP_RESET</code> with the factory-reset option doubles as a remote recovery path. Safe mode exits on a host-commanded reset, a true power cycle, or a 10-minute automatic retry.</p> <h2>Validation</h2> <ul> <li>On-air permit-gate probe (new <code>test/permit_gate_check.py</code>): closed for 220 s post-boot, join 4 s after host permit</li> <li>Formation PAN now equals post-reboot PAN across repeated reboots; zigpy-zboss 2.0.0 tolerates the rejected post-formation write</li> <li>Safe-mode chain exercised end-to-end with an artificial pre-stack hang build: deadline-reboot accumulation, sticky safe mode across port reopens, blocked getters, supervised exit</li> <li>Regression on the final build: zigpy-zboss 2.0.0 full startup (network resume) and the v1.3.50 duplicate-drop suite, all pass</li> </ul> <p>Flash via the web flasher at <a href="https://install.busware.de/zboss/" rel="nofollow">install.busware.de/zboss</a> or <code>esptool write_flash 0x0 binaries/factory.bin</code>. Network settings (NVRAM) are preserved across an app update; a backup via the web flasher before any flash is recommended regardless.</p> tostmann tag:github.com,2008:Repository/956036346/v1.3.50 2026-06-07T13:17:55Z v1.3.50 — link-layer duplicate drop <h2>Link-layer duplicate drop (DUP-1)</h2> <p>Fixes handling of retransmitted host data frames in the ZBOSS NCP serial protocol layer.</p> <p><strong>The bug:</strong> the header's retransmit bit was treated as NACK on every frame type. A host that re-sent a data frame after an ACK timeout (stable <code>packet_seq</code> + retransmit flag — exactly what zigpy-zboss ≥ 2.0.0 and zigbee-herdsman do) got its retransmission silently discarded: no re-ACK, no execution. A host retry could therefore never succeed — and an unflagged duplicate would have been executed twice. Latent on USB/TCP (frame loss is rare there), real on lossy UART links.</p> <p><strong>The fix:</strong> the firmware now remembers the sequence number of the last accepted host data frame. A flagged retransmission with a matching sequence is re-ACKed but not executed again (lost-ACK case); a flagged frame with an unseen sequence is accepted as a first delivery (lost-frame case).</p> <p><strong>Validation</strong> (on real hardware, ESP32-C6):</p> <ul> <li>New on-wire probe <code>test/dup_drop_check.py</code>: 4/4 PASS — duplicate re-ACKed and not re-executed, fresh flagged frame dispatched, link continues normally</li> <li>zigpy-zboss 2.0.0 full-startup runs against this build: restart on a formed network and blank-NCP <code>auto_form</code>, both SUCCESS</li> <li>New <code>test/routing_gate_check.py</code>: 4/4 PASS — isolated validation of the v1.3.45 RX routing gate (interface routing flips only on a frame signature; port-prober garbage, e.g. ModemManager AT probes, is discarded; signature split across reads handled)</li> </ul> <p><strong>Who should update:</strong> anyone using the UART transport (TTL header / ser2net setups) or driving the coordinator with zigpy-zboss ≥ 2.0.0 (ZHA path). USB users with Zigbee2MQTT are unaffected in practice but get the hardening for free.</p> <p>Flash via the web flasher at <a href="https://install.busware.de/zboss/" rel="nofollow">install.busware.de/zboss</a> or <code>esptool write_flash 0x0 binaries/factory.bin</code>. Network settings (NVRAM) are preserved across an app update; the web flasher's backup feature is recommended before any flash regardless.</p> tostmann tag:github.com,2008:Repository/956036346/v1.3.49 2026-06-06T09:17:47Z v1.3.49 — ZBOSS thread-safety fix <h2>v1.3.49 — ZBOSS thread-safety fix (recommended upgrade)</h2> <p><strong>Bugfix release.</strong> No new features, no protocol changes — existing setups upgrade by reflashing, network data is preserved.</p> <h3>The fix</h3> <p>The NCP command dispatch called ZBOSS APIs from the application task without holding the ZBOSS port-layer lock (<code>zb_esp_osif_lock_acquire</code> — the same rule esp-zigbee-lib enforces via <code>esp_zb_lock_acquire()</code>: the ZBOSS core is not thread-safe). The two tasks could race inside the stack's interrupt-bracketing primitive, corrupting the FreeRTOS critical-section nesting count and crash-rebooting the coordinator with:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="assert failed: vPortExitCritical port.c:618 (port_uxCriticalNesting[0] &gt; 0)"><pre class="notranslate"><code>assert failed: vPortExitCritical port.c:618 (port_uxCriticalNesting[0] &gt; 0) </code></pre></div> <p>This design flaw is inherited from the original codebase and affects <strong>all previous releases</strong>. It is timing-dependent: it reproduced reliably under device-interview bursts on the WiFi-coexistence development branch, and is rarer (but equally present) on plain USB/UART builds — if your coordinator ever rebooted spontaneously during device pairing or interviews, this was a likely cause.</p> <p>The dispatch is now serialised against the stack task with the port-layer lock. Validated with repeated device-interview stress runs (previously ~80% crash rate on the reproducer, now 0 failures) plus an 8-hour continuous-load soak.</p> <h3>Also in this release</h3> <ul> <li>Boot log line <code>BOOT: reset reason: N</code> (visible on debug builds; aids crash diagnosis on boards whose eFuses suppress the ROM reset banner)</li> <li>Event-queue hardening: dropped-event diagnostics + bounded retry on the host-RX path</li> <li>Release binary built with the documented UART default pins (TX=GPIO22, RX=GPIO23)</li> </ul> <h3>Flashing</h3> <p>Web flasher: <a href="https://install.busware.de/zboss/" rel="nofollow">https://install.busware.de/zboss/</a> — or <code>esptool --chip esp32c6 write_flash 0x0 factory.bin</code>. Reflashing without erase preserves the formed network and paired devices.</p> tostmann tag:github.com,2008:Repository/956036346/v1.3.45 2026-06-05T18:50:36Z v1.3.45 — UART Host Transport, XIAO ESP32-C6 Support, NCP_RESET Fixes <h2>New: UART host transport — run the coordinator without USB</h2> <p>The NCP frame stream is now <strong>additionally available on a hardware UART</strong>, in parallel to USB-Serial/JTAG (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="4593028589" data-permission-text="Title is private" data-url="https://github.com/tostmann/esp-coordinator/issues/2" data-hovercard-type="issue" data-hovercard-url="/tostmann/esp-coordinator/issues/2/hovercard" href="https://github.com/tostmann/esp-coordinator/issues/2">#2</a>, discussion <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="10196722" data-permission-text="Title is private" data-url="https://github.com/tostmann/esp-coordinator/discussions/1" data-hovercard-type="discussion" data-hovercard-url="/tostmann/esp-coordinator/discussions/1/hovercard" href="https://github.com/tostmann/esp-coordinator/discussions/1">#1</a>):</p> <ul> <li>Default pins: <strong>coordinator TX = GPIO22, RX = GPIO23</strong> (= <strong>D4/D5 on the Seeed XIAO ESP32-C6</strong>), <strong>115200 8N1</strong>, no flow control — configurable via <code>menuconfig</code> → <em>Zigbee Network Co-processor</em>.</li> <li>Hook the coordinator straight onto an OpenWRT router's TTL header, a Raspberry Pi GPIO header UART, or a USB-UART adapter / ser2net box. See the <a href="https://github.com/tostmann/esp-coordinator#uart-instead-of-usb-since-v13x">README wiring guide</a>.</li> <li>Both interfaces stay live: responses follow whichever interface last delivered a <strong>valid NCP frame</strong>. The routing flip is signature-gated, so port probers (e.g. ModemManager's AT probes) can neither steal the link nor corrupt the stream.</li> <li>Bonus: on a UART the host's port survives the coordinator's resets — the USB-CDC reset quirks disappear entirely on this path.</li> <li>Validated end-to-end with <strong>zigbee2mqtt</strong> (<code>adapter: zboss</code>) and <strong>zigpy-zboss</strong>: network formation, device pairing, full 40 KB NVRAM backup.</li> </ul> <h2>New: Seeed XIAO ESP32-C6 external antenna option</h2> <p><code>CONFIG_NCP_XIAO_EXT_ANTENNA</code> routes RF to the XIAO's external U.FL connector (GPIO3/GPIO14 RF-switch control per Seeed's reference). Default off; the default build now leaves the switch pins in a defined passive INPUT state — internal antenna on the XIAO, no-op on every other board.</p> <h2>Critical fixes</h2> <ul> <li><strong>NCP_RESET over USB no longer freezes the chip until power-cycle.</strong> The legacy USB "phy detach" register writes hard-freeze the ESP32-C6 on current ESP-IDF builds. They are removed; NVRAM/factory erase now runs at the start of the next boot, before the radio is active.</li> <li><strong>NCP_RESET protocol semantics fixed</strong>: no more pre-reboot "OK" response. Hosts treated that response as "reset complete" and continued immediately — everything sent before the actual reboot died mid-flight. This was the root cause of the long-standing <em>"Backup failed … after 10000ms"</em> failures after factory-reset / restore / formation. The boot-ready frame after the reboot is now the response, so reset → re-form and restore → resume flows complete deterministically.</li> <li><strong>Full-spec <code>NWK_FORMATION</code> response</strong> (<code>NWKAddr + PANID + page + channel</code>) — fixes formation against hosts parsing the complete layout, notably <code>zigpy-zboss</code> ≥ 2.x / Home Assistant ZHA (hardware-validated against <a href="https://github.com/kardia-as/zigpy-zboss/pull/73" data-hovercard-type="pull_request" data-hovercard-url="/kardia-as/zigpy-zboss/pull/73/hovercard">kardia-as/zigpy-zboss#73</a>).</li> <li><strong>Link-layer sequence race closed</strong> (duplicate <code>packet_seq</code> from concurrent ACK/data paths → sporadic "Unexpected packet sequence" on the host).</li> </ul> <h2>Improvements</h2> <ul> <li><code>GET_NETWORK_BACKUP</code> serves all chunks from a point-in-time RAM snapshot — consistent 40 KB images, single bulk flash read.</li> <li>README: UART wiring guide, XIAO pin mapping, refreshed ZHA status.</li> </ul> <p><strong>Flashing:</strong> <a href="https://install.busware.de/zboss/" rel="nofollow">Web flasher</a> (updated to v1.3.45) or <code>esptool.py --chip esp32c6 write_flash 0x0 esp-coordinator-esp32c6-factory.bin</code>.</p> tostmann