Assumes you already have a Trifecta repeater running and decided a Sidecar makes sense for your setup. If you're still deciding, see the Sidecar overview first.
On the repeater a Sidecar will attach to — set the ESP‑NOW join parameters it'll need to match:
set bridge.channel <selected wifi channel> set bridge.secret <your key> get bridge.channel # confirm + check for a live-channel mismatch get bridge.type # confirms "espnow" (or "espnow+ip" if IP bridge also configured)
Works on the Waveshare ESP32-S3-Zero and the ESP32-C3 "Super Mini" —
same commands either way, only the environment name prefix changes
(ESP32_S3_Zero_* vs ESP32_C3_SuperMini_*):
*_comp_radio_bridge_espnow_dual for a companion,
*_room_server_bridge_espnow for a room server,
*_repeater_bridge_espnow for a repeater-role Sidecar. The
direct-IP variants (*_comp_radio_ip_*,
*_room_server_ip) skip this step entirely — no
ESP‑NOW join to configure, see their own sections below.
Getting the environment onto your board isn't covered here — this page
picks up once you're already running it.
No text CLI — companion firmware speaks the binary phone-app protocol on serial instead. Use the bundled script to join the ESP‑NOW side before or after pairing with a phone app:
python3 set_bridge_params.py list python3 set_bridge_params.py <port> get python3 set_bridge_params.py <port> set <selected wifi channel> <your bridge secret>
Channel/secret must exactly match the repeater's own. Applies immediately, no reboot. Needs pip install pyserial. Everything else (contacts, channels, messaging) is normal companion-app usage from here.
New: a Waveshare ESP32‑S3‑Zero companion build
(ESP32_S3_Zero_comp_radio_ip_* — usb/ble/dual/wifi
variants) that skips ESP‑NOW entirely and talks straight to the
repeater's IP bridge over WiFi (TCP + TLS‑PSK, the same link a
repeater‑to‑repeater IP bridge uses). No ESP‑NOW
proximity to the repeater needed — just WiFi reaching its address.
Also no text CLI, same binary phone-app protocol on serial — use the
bundled script instead:
python3 set_ip_params.py list python3 set_ip_params.py <port> wifi set <ssid> <password> # reboot needed after this one python3 set_ip_params.py <port> ip set <repeater host or IP> <port> <secret> python3 set_ip_params.py <port> status
The secret must match a credential the repeater has registered for
this companion via set ip.peer.add <this device's
identity> <secret> — see Trifecta
setup's "Multiple IP peers" section. This device's identity (the
8‑hex‑char id the repeater side needs) is reported by
set_ip_params.py <port> status once connected, or by
the companion app's own device-info screen. Needs
pip install pyserial.
Real text CLI over USB. Same ESP‑NOW join as above (set bridge.channel/set bridge.secret), then optionally add a channel mirror — see Echo Board for that piece specifically.
New: same idea as the direct-IP companion above, for the room-server
role — ESP32_S3_Zero_room_server_ip and
ESP32_C3_SuperMini_room_server_ip talk straight to a
repeater's IP bridge over WiFi (TCP + TLS‑PSK), no ESP‑NOW
hop or gateway board required. Useful as a standalone sidecar
wherever WiFi reaches the repeater but ESP‑NOW proximity
doesn't. Unlike the companion, this role keeps its normal text CLI —
configure it the same way as any other IP-bridge peer:
set wifi.ssid <name> set wifi.pwd <password> set ip.host <repeater host or IP> set ip.port <port> set ip.secret <key> # must match the repeater's ip.peer.add entry for this device
Once connected, add a channel mirror the same way as the ESP‑NOW room server above — see Echo Board.
Same board class as a companion or room server — no LoRa radio here either — just running repeater-role mesh logic instead. Joins its host repeater over ESP‑NOW the same as any Sidecar (step 1 above). It can also optionally run its own IP bridge leg, acting as a cheap ESP‑NOW↔IP translator that relays onward to a second Trifecta repeater elsewhere — not the same thing as two real repeaters linking directly (see the Trifecta setup page for that):
set wifi.ssid <name> set wifi.pwd <password> set ip.host <peer host or IP> # empty on this side = listen instead of dial out set ip.port <port> set ip.secret <key> # TLS-PSK, must match the peer
bridge.channel must exactly match whichever channel this WiFi access point actually operates on — it doesn't get synced automatically. Lock that AP to a fixed channel rather than leaving it on auto-select; many routers pick a new channel on reboot or via automatic interference avoidance, which would silently break this until bridge.channel is updated to match again.Leaving ip.host empty here makes this board a listener rather than a dialer, and a listener can hold more than one peer at once, each with its own credential instead of one shared ip.secret — see Trifecta setup's "Multiple IP peers" section, the CLI is identical here.
Don't just assume it's working — these cover every hop:
get wifi.status get bridge.channel get ip.status neighbors.all
bridge.channel flags a mismatch explicitly if the ESP-NOW radio's live channel has drifted from what's configured. neighbors.all merges RF-heard and bridge-heard neighbours into one list with a via column, so you can see which peers are reachable and through which path.