Work / Embedded · wireless · hardware

LED rings that stay in sync with no leader

39 wearable LED rings. Each one is its own radio node. When a few are near each other their patterns line up, with no master clock and no leader election. I did the protocol, firmware, board, enclosure, OTA updates and the tests.

The problem

People wear these and wander around in groups. Rings that can hear each other over radio need to agree on the time to within about a video frame or the patterns look wrong. A master clock or an elected leader stops working the moment that person walks off, so there isn't one.

How it works

Each ring keeps a clock of the form global = rate × local + offset and broadcasts it in a small beacon. It nudges its own clock toward a trust-weighted median of what it hears, and slews instead of jumping so a pattern never visibly skips. This is basically Sommer and Wattenhofer's gradient time sync.

Patterns are a function of the shared clock plus a seed carried in the beacon. Nothing is streamed. Two rings with the same time and seed draw the same frame.

Chart: worst disagreement between six simulated clocks falls from 814 ms to under 11 ms within 30 seconds of two groups meeting
Simulated: six nodes running the firmware's own clock code, two groups that start 0.8 s apart and meet at t = 0.

Checking it with a camera

A simulator can't tell you the LEDs actually line up, so I filmed two rings at 30 fps and measured the flashes. Median offset was under 4 ms. Before the sync code existed the same two rings sat almost 50 ms apart. Scramble the clocks on purpose and they're back in step in about ten seconds. That camera test is now a release gate.

Updates that can't brick a ring

The rings are sealed and handed out, so updates go over the air, signed, through MCUboot with rollback. An image that boots but doesn't confirm itself gets reverted. One that hangs shows up as a USB drive within a minute so it can be reflashed without a button.

Test on real boardsResult
Reboot soak121 of 124 trials
Forced rollback, final run5 of 5
USB recovery36 of 36
Hung image to USB drive51 s
Releases shipped41

Things that went wrong

  • The board. Rev A had 12-pin sockets for a 13-pin radio module. Found at bring-up, written up as an erratum next to the design files.
  • White flashes. Bright scenes sometimes flashed the whole ring white. Not a colour bug: the supply rail was sagging, because a scene that lights every LED pulls about six times the idle load. Fixed with a per-frame power budget in the firmware.
  • Everyone alone in a crowd. In big groups every ring would sometimes drop to solo mode even though they were all in range. I pulled the grouping logic out into plain C, wrapped seeded simulators around the same files the firmware uses, and reproduced it deterministically before touching the firmware. One release during this went out with a stack overflow and was pulled. That's in the changelog too.
A 16-LED ring, an nRF52840 module and a 1000 mAh cell laid out on a cutting mat
What goes in one: the LED ring, the nRF52840 module, a 1000 mAh cell.

Not shown

The biggest group tested on hardware is six rings. The designed cap of 24 has only been run in simulation. The camera measurements are of two rings.