convoy-mesh
Off-grid, phone-native mesh comms for a convoy of vehicles, using cheap ESP8266 boards as a pure transport backbone.
Born from a real problem: going off-roading with Jeeps where there's no cell coverage, and wanting free, real-time communication between cars — voice, position, and shared media — with no carrier, no internet, and no radio license.
The core idea
The phones are the computers. The ESPs are the ISP.
- Phones own everything smart: GPS, accelerometer, cameras, microphone, speakers, storage, UI, and the audio/video codecs. All sensing, encoding, decoding and application logic live here.
- ESP8266 boards do exactly one thing: move bytes between cars. They form a self-relaying wireless backbone and never touch audio or application logic.
You build the transport firmware once. After that, every feature is just a phone app talking over the bus — no firmware changes ever again. That decoupling is what makes this a platform rather than a single gadget.
Phone A <--WiFi--> ESP A <==mesh==> ESP B <==mesh==> ESP C <--WiFi--> Phone C
(sensors, (dumb (relay) (dumb (sensors,
codecs, UI) router) router) codecs, UI)
Why the ESP8266 is the right transport (and where its limits really are)
Real numbers, not vibes:
- ESP8266 usable throughput: ~1–5 Mbps (single 80/160 MHz core, modest Wi-Fi MAC).
- It's a shared, half-duplex 2.4 GHz medium: the whole mesh shares airtime, and each relay hop roughly halves end-to-end throughput (one radio, store-and-forward).
- No FPU → heavy codecs (Opus) are too slow on-chip. Solution: don't encode on the ESP. The phone encodes; the ESP relays already-compressed bytes.
The trick that makes voice trivial: keep audio DSP on the phone, let the ESP be a dumb, fast packet forwarder.
Transport design: a prioritized message bus
The ESP mesh is not a full IP network (that's the OpenWRT-router tier). It's an application-layer message fabric:
- Each phone joins its own car's ESP soft-AP and opens one connection
(a websocket — cnlohr's
esp82xxframework is a natural fit — or a WebRTC datachannel for UDP-style delivery). - Phones send addressed messages:
broadcast, orto: car3. - The mesh forwards hop-to-hop. For 3 cars, ESP-NOW broadcast is ideal: connectionless,
~1–2 ms/packet, one transmit reaches all peers in range; a middle car re-broadcasts when the
ends can't hear each other. (
painlessMeshis the alternative for larger, self-healing meshes.)
The one firmware feature that makes or breaks it: two priority lanes.
| Lane | Traffic | Rule |
|---|---|---|
| Realtime | voice frames, telemetry, control | always sent first, never blocked |
| Best-effort | photos, music files, logs | fills spare airtime, yields to realtime |
Without this, a photo transfer stalls a conversation. With it, everything coexists.
Feasibility by use case
Ceiling to keep in mind: ~1–5 Mbps shared, halving per hop.
| Use case | Cost | Verdict on ESP8266 mesh |
|---|---|---|
| Voice (Opus ~24 kbps; Codec2 as low as ~3 kbps) | ~24 kbps/talker | ✅ Trivial, real-time |
| Live car data (GPS, accel, OBD-II) | few kbps/car | ✅ Basically free — the killer app |
| Shared map / who's-tilted / breadcrumbs | few kbps | ✅ Free |
| Synced music — control + timing | few kbps | ✅ Yes, and the smart way (see below) |
| Music as a live audio stream | 128–320 kbps | ⚠️ One stream OK; tight on multi-hop |
| Photo / snapshot sharing | 50–200 KB bursts | ✅ Best-effort lane |
| Live video — one low-res stream, one hop | 0.5–1.5 Mbps | ⚠️ Borderline, near ceiling |
| Live video — everyone to everyone | several Mbps | ❌ Exceeds ESP8266 → ESP32 / HaLow tier |
Sweet spot: voice + telemetry + playback-sync + snapshots, comfortably up to ~6–8 cars before shared-channel contention bites.
Real-time voice latency budget (3 cars, phones do the DSP)
Target: under ~150 ms one-way feels like a phone call (ITU G.114).
| Stage | Latency |
|---|---|
| Mic capture + Opus frame (phone) | ~20–40 ms |
| Phone → ESP (Wi-Fi, one hop) | ~2–5 ms |
| ESP → ESP relay (ESP-NOW, 1–2 hops) | ~2–8 ms |
| ESP → phone (Wi-Fi) | ~2–5 ms |
| Jitter buffer (phone) | ~40–60 ms |
| Decode + play | ~10 ms |
| Total one-way | ~80–130 ms ✅ |
With 3 voice streams at ~24 kbps = ~72 kbps, the channel sits ~95% idle. Bandwidth was never the problem; range and topology are.
The feature that falls out for free: time sync
Because it's a shared bus, run a lightweight clock sync over it (one car = time master, others discipline to it — crude PTP, few-ms accuracy). At near-zero bandwidth this unlocks:
- Convoy-wide synchronized music — pre-share tracks over the best-effort lane while parked,
then
play track 7 at T+3.000smakes every car's speakers play in unison. The whole convoy becomes one sound system. This is the right way to do "shared playlist": sync the clock and control, not the audio bytes. - Same-moment multi-cam — synchronized snapshot from every phone on a countdown.
- Coherent telemetry — everyone's GPS/accelerometer on one timeline; the shared map shows in real time who's climbing, stopped, or just took a hard hit.
Then the app grows forever on the same bus: Marco-Polo find-me ping, auto "I'm stuck + exact position + photo", tire-pressure/temp alerts, distributed dashcam (record locally, share clips later), a trip log stitched from everyone's GPS.
Honest limits
- Aggregate ~1–5 Mbps, shared, halving per hop. Great up to ~6–8 cars for realtime+data+snapshots.
- All-to-all live video is out — that's the genuine ceiling. Step to ESP32 (faster radio/CPU) or a Wi-Fi HaLow / OpenWRT router mesh if video-to-everyone is a must-have.
- Range per hop is 2.4 GHz physics (~100–200 m line-of-sight per hop, less through terrain). An antenna problem, fought with external whips and a relaying middle car (~2× end-to-end via relay).
- soft-AP + mesh must share one channel on the ESP8266 — the fiddly integration bit (cleaner on ESP32).
- Moving vehicles fade / drop packets — inherent; Opus packet-loss concealment + a little FEC hide it. Push-to-talk (one talker) removes contention and masks loss.
Hardware notes (learned the hard way)
- The NodeMCU v3 (LoLin, ESP-12E) has no antenna connector — the gold zig-zag trace on the module is a PCB antenna, permanently attached. Fine for bench prototyping.
- For real range, use boards with a u.FL/IPEX or SMA connector (ESP-07S; or ESP32 boards that ship with an SMA whip). Modding a u.FL pigtail onto an ESP-12E is possible but delicate SMD rework.
- Biggest range factor in a car: the metal cabin is a Faraday cage. Placement beats antenna type — get the radiating element outside the bodywork (windshield base, or a roof/hood whip).
Roadmap
- Two-board voice — ESP-NOW relay firmware + phone web app (Opus over WebRTC datachannel or
esp82xxwebsocket). Prove <150 ms real-time voice between two cars. - Add telemetry — GPS + accelerometer on the realtime lane; shared live map.
- Add clock sync + synced music — pre-shared tracks, unison playback.
- Add snapshots — best-effort photo lane with priority yielding.
- Scale + range — third car (relay path), external antennas, in-cabin mounting, PTT UX.
- If video-to-everyone is needed — migrate transport tier to ESP32 or HaLow/OpenWRT mesh.
Status
Concept + feasibility captured. Hardware on hand: 2× NodeMCU v3 (ESP8266, PCB-antenna only) — enough to prototype phases 1–4.
Inspiration: the cnlohr school of embedded — treat peripherals as raw timing engines and refuse to accept a datasheet limit before you've done the math.