Docker stacks: Zigbee (EZSP 18) and same-channel Zigbee + Thread¶
The stable path runs Zigbee alone. An experimental second path runs Zigbee and
Thread concurrently on a Lidl Series 1 radio, provided both networks use the
same 802.15.4 channel. See
cpcd-zigbeed-otbr/README.md.
Use Cases at a Glance¶
| # | Use case | Compose file | EFR32 firmware | Status |
|---|---|---|---|---|
| 1 | Zigbee (EmberZNet 8.2.2 / EZSP 18) | docker-compose-zigbee.yml |
./flash_efr32.sh rcp |
Tested, stable |
| 2 | Multi-PAN (same-channel Zigbee + Thread) | docker-compose-multipan.yml |
./flash_efr32.sh rcp |
Experimental on Lidl EFR32MG1B (limits) |
Use case 1: Zigbee
┌──────────────────────┐
│ Zigbee2MQTT │
Docker host │ + Mosquitto │
│ Web UI :8080 │
└───────┬──────────────┘
│
┌───────┴──────────────┐
│ cpcd-zigbeed │
│ ├── cpcd │
│ └── zigbeed (IID=1) │
└───────┬──────────────┘
│ TCP :8888
┌───────┴──────────────┐
Gateway │ rtl8196e-uart- │
(RTL8196E) │ bridge (kernel) │
└───────┬──────────────┘
│ UART 460800
┌───────┴──────────────┐
EFR32 │ RCP (IID 1 active) │
└──────────────────────┘
For a production single-protocol Matter-over-Thread deployment, reflash the
EFR32 with the standalone OT-RCP firmware (./flash_efr32.sh otrcp from the
repo root) and use the Thread Border Router compose at
../../26-OT-RCP/docker/docker-compose-otbr-host.yml.
Requirements¶
On the Gateway¶
- EFR32 flashed with RCP firmware (via
./flash_efr32.sh rcp) - Gateway running kernel 6.18 or newer with the in-kernel UART bridge
(
rtl8196e-uart-bridge) armed on TCP:8888 (automatic viaS50uart_bridgeat boot)
On Your Computer¶
- Docker and Docker Compose
- Wired Ethernet to the gateway (recommended — cpcd is latency-sensitive)
Use Case 1: Zigbee — EmberZNet 8.2.2 / EZSP 18¶
Runs Zigbee2MQTT with the ember adapter. The Zigbee stack (zigbeed,
EmberZNet 8.2.2 / EZSP 18, built from Simplicity SDK 2025.6.3) runs in the
cpcd-zigbeed container:
cpcd uses its native bus_type: TCP to dial the gateway bridge directly, and
zigbeed listens natively on :9999 (tcp-listen://0.0.0.0:9999) — no socat,
no PTY. Zigbee2MQTT connects with:
The listener serves one EZSP client at a time: a second connection is
closed without disturbing the active one, and when the client disconnects
(Zigbee2MQTT restart) zigbeed and cpcd keep running and the next client
reconnects to the same stack. The health check never connects to :9999; it
checks the cpcd / zigbeed processes and the listening socket instead.
Quick Start¶
-
Edit
docker-compose-zigbee.yml— set your gateway IP: -
Start:
-
Wait ~60 seconds for the stack to initialize. Check:
-
Open http://localhost:8080
Files¶
| File | Description |
|---|---|
docker-compose-zigbee.yml |
Mosquitto + cpcd-zigbeed + Zigbee2MQTT |
z2m/configuration.yaml |
Z2M config (adapter, MQTT) |
mosquitto/mosquitto.conf |
MQTT broker (anonymous, ports 1883/9001) |
cpcd-zigbeed/ |
Dockerfile and configs for the cpcd+zigbeed container |
Pre-built Image¶
| Tag | cpcd | EmberZNet | EZSP |
|---|---|---|---|
latest, cpcd4.5.3-ezsp18 |
4.5.3 | 8.2.2 | v18 |
<release> (e.g. 4.5.2) |
4.5.3 | 8.2.2 | v18 |
latest and cpcd4.5.3-ezsp18 follow the latest release that changed the
image; the release tags are immutable. A release that leaves the image alone
publishes no new tag: use the most recent one.
Services¶
| Port | Service |
|---|---|
| 8080 | Zigbee2MQTT Web UI |
| 1883 | Mosquitto MQTT |
| 9001 | Mosquitto WebSocket |
Use Case 2: Experimental same-channel multi-PAN¶
Runs zigbeed on IID 1 and OTBR on IID 2 through one cpcd, with IID 0
reserved for broadcast. The OTBR image is built with OT_MULTIPAN_RCP=ON;
without that host option, the OpenThread URL parser rejects the IID list before
contacting the RCP.
Series 1 supports this arrangement only when Zigbee and Thread share a channel.
Independent-channel Concurrent Listening remains a Series 2 requirement. The
test so far was short and used no real Thread device, so this remains
experimental. Run this compose file with a rootful native Linux Docker engine;
OTBR needs host networking, /dev/net/tun, IPv4/IPv6 forwarding, mDNS, and
host firewall access. Follow the
multi-PAN guide for the Linux host setup.
Commands Reference¶
# Zigbee stack
docker compose -f docker-compose-zigbee.yml up -d
docker compose -f docker-compose-zigbee.yml down
docker compose -f docker-compose-zigbee.yml logs -f cpcd-zigbeed
# Experimental same-channel Zigbee + Thread stack
docker compose -f docker-compose-multipan.yml pull otbr-agent
docker compose -f docker-compose-multipan.yml up -d
docker compose -f docker-compose-multipan.yml logs -f cpcd-zigbeed otbr-agent
# Optional: reproduce the pinned OTBR image locally from the shipped sources
docker compose -f docker-compose-multipan.yml build otbr-agent
# Full reset (deletes all Zigbee data, Z2M database)
docker compose -f docker-compose-zigbee.yml down -v
Troubleshooting¶
"Cannot reach RCP endpoint"¶
- Check the IP is correct in the compose file
- Test connectivity:
nc -zv <gateway-ip> 8888 - Check the in-kernel UART bridge is armed on the gateway:
cat /sys/module/rtl8196e_uart_bridge/parameters/armed→1
"EZSP protocol version not supported"¶
Requires Zigbee2MQTT 2.7.2 or newer (for EZSP v18 support).
"zigbeed entered FATAL state"¶
Common causes: network instability (use Ethernet, not Wi-Fi), or a gateway
bridge armed at a baud that does not match the RCP firmware (re-run
flash_efr32.sh, which records the right baud in radio.conf).