So I have a bunch of devices that all use the ESP32 which I need to synchronize because the clocks are never quite precise enough over a long time.
My usual solution to this is to have one "master" device send out "clock" pulses at a much slower rate to the other devices to increment the state so that they are all in the same place in their code at the same time. This works great on wires, but I'd like to do it wirelessly. They are strobe lights at changing strobe rates from 10-50Hz so that means accuracy should really be better than 10ms or else they rapidly (minutes) get too far out of phase and interfere with each other.
Problem is, most protocols have too much latency for this to work reliably -- the solution I can find online suggests using the NRF24L01 over SPI to the ESP32 for this function, but that seems like something the ESP32 surely must be able to do with an entire second core...
Is this impossible? I feel ridiculous having two separate transceivers on one board when the one I'm adding is even lower function! Can I use the ESP32 to receive and interpret wireless information faster than say 1-2ms from reception? I'm happy to use something way lower level than wifi or bluetooth, I'll just have one transmitter sending some byte with a code that each other light will receive and if the code matches the reference it'll increment the state. At least it won't drift... but even a few ms of delay between them will be visibly annoying due to the nature of the strobing.
ESP32 with low-level RF?
-
- Posts: 9766
- Joined: Thu Nov 26, 2015 4:08 am
Re: ESP32 with low-level RF?
Here's an idea I've been thinking about but never tried. Let's start by noting the ESP32 needs a +-10ppm crystal for WiFi to work correctly, meaning you need to synchronize at most once every 16 minutes. Also notice that WiFi doesn't really have a high latency - it mostly has high jitter, as the WiFi infrastructure sometimes needs to 'wait' for other things, but if that doesn't happen, transmit to receive should be well within a microsecond. Also notice that time is linear - any delay can only be positive, never negative, as a receiver cannot receive the signal before the transmitter has sent it.
With all this, you may be able to use synchronization this way. Have a master that has a clock going; the strobes that it sends out are synchronized to this clock. Every, say, second or so, send a WiFi packet with the current value of this clock. Now, on the receiver side, also have a clock going, but have it run at least 20ppm slow: you want it to always run a bit slower than the master, to compensate for the fact that the master crystal can be 10ppm slow and the slave can be 10ppm fast. This guarantees the slave will always have a clock that's either the same or a bit slower than the master. Now, every time the slave receives a WiFi packet from the master, it'll look at the clock value in it: if it's older than its own clock, it will ignore it as it probably was delayed, if it's newer than its own clock, it sets its own clock to that value.
This setup should be able to synchronize everything, given that at least 1 packet in 1000 comes through without any delay.
Note that to increase chances of undelayed sending, you probably want to use either UDP or ESP-Now for this. Also, make sure you broadcast one packet to all slaves, instead of addressing them individually. Also note that you can even make this into a master-less setup: have all devices have their own clock and broadcast the value of it periodically, and if any device receives a value that's higher than its own clock, set the internal clock to that value.
With all this, you may be able to use synchronization this way. Have a master that has a clock going; the strobes that it sends out are synchronized to this clock. Every, say, second or so, send a WiFi packet with the current value of this clock. Now, on the receiver side, also have a clock going, but have it run at least 20ppm slow: you want it to always run a bit slower than the master, to compensate for the fact that the master crystal can be 10ppm slow and the slave can be 10ppm fast. This guarantees the slave will always have a clock that's either the same or a bit slower than the master. Now, every time the slave receives a WiFi packet from the master, it'll look at the clock value in it: if it's older than its own clock, it will ignore it as it probably was delayed, if it's newer than its own clock, it sets its own clock to that value.
This setup should be able to synchronize everything, given that at least 1 packet in 1000 comes through without any delay.
Note that to increase chances of undelayed sending, you probably want to use either UDP or ESP-Now for this. Also, make sure you broadcast one packet to all slaves, instead of addressing them individually. Also note that you can even make this into a master-less setup: have all devices have their own clock and broadcast the value of it periodically, and if any device receives a value that's higher than its own clock, set the internal clock to that value.
Re: ESP32 with low-level RF?
So usually what I do is have each device increment it's state according to received lower frequency clock signals, like how a RTOS will check for things every ms or so. Basically each device has it's CPU clock, and it's "program clock" that's way slower and only handles big changes.
In my case, big changes mean when to change the color and brightness of LEDs, so changing those at 1kHz is plenty. But sending sequential integers so that a device can recover if it misses a clock seems wise.
I'm just sticking on a NRF24L01+ module since I can't find an obvious answer for how I might do trivial point to multipoint communication with the ESP32 with minimal overhead. All I want is a code byte to indicate it's from the right source, and I suppose two bytes with the current time count (in ms) that rolls over gracefully.
What I don't want is to have to worry about acknowledgments, retransmissions, etc that clog the RF environment. I can get like 10-15 devices kind of synchronized with wifi only, but I think the limitation was much more due to receivers sending back packets and dirtying up the RF environment than the one sender broadcasting to 255.255.255.255. So I want something closer to UDP with no target addressing (all packets always go to all receivers).
In my case, big changes mean when to change the color and brightness of LEDs, so changing those at 1kHz is plenty. But sending sequential integers so that a device can recover if it misses a clock seems wise.
I'm just sticking on a NRF24L01+ module since I can't find an obvious answer for how I might do trivial point to multipoint communication with the ESP32 with minimal overhead. All I want is a code byte to indicate it's from the right source, and I suppose two bytes with the current time count (in ms) that rolls over gracefully.
What I don't want is to have to worry about acknowledgments, retransmissions, etc that clog the RF environment. I can get like 10-15 devices kind of synchronized with wifi only, but I think the limitation was much more due to receivers sending back packets and dirtying up the RF environment than the one sender broadcasting to 255.255.255.255. So I want something closer to UDP with no target addressing (all packets always go to all receivers).
Who is online
Users browsing this forum: No registered users and 31 guests