PPS synced MIDI metronome over BLE
The idea is as follows: I let an ESP32-C3 listen for a PPS (pulse per second) signal from a GPS and then let a metronome sync on that. The result would be send as MIDI messages of BLE (bluetooth-low-energy).
If it would've worked, I would have added a nice web-interface via which you could configure the beats-per-minute. Maybe make it configurable via MIDI messages as well, but:
TL;DR it doesn't work: the jitter of MIDI-BLE is around 100 milliseconds, more than 5 milliseconds is audible (for the average listener).
step one: connect a GPS to an ESP32-C3
This is simple enough. You only need the PPS pin of the GPS receiver (only interval is relevant, not the exact point in time). Cheap GPS modules (less than 3 Euro) can be obtained from e.g. aliexpress.
Next, in the software of the thing, attach an interrupt-handler to the pin on which the GPS is connected for a rising edge.
step two: measure the ESP32 C3 clock
My goal was to have an accurate metronome, like around 1 microsecond precision. The ESP32 SDK has a esp_timer_get_time function returning the the number of microseconds that ticked since the ESP32 was powered up (well and the timer functionality started). I then simply measure how my of those ticks ticked during a PPS interval. Then maybe pull the result through a Kalman filter, divide by a value to get the 'BPM' wanted (beats per minute) and sleep that duration between each MIDI message (compensate for the time it takes to send a message of course).
step three: send MIDI of BLE
Luckily someone had implemented a library for that: https://github.com/max22-/ESP32-BLE-MIDI.
step four: measure
ESP32 clockticks in a second interval
I added code to the Arduino-script that would print the measured clockticks-per-second each time a note was sent. This looked quite good:
S999974 S999974 S999974 S999974 S999974 S999974 S999975 S999973 S999974 S999974 S999975 S999973 S999975 S999974 S999974 ...(the 'S' means that the GPS-receiver is locked onto the GPS satellites).
MIDI over BLE
For this, I connected my laptop (over BLE) to the ESP32 (using the MIDI profile) and connected the virtual MIDI-port created on the Linux system to 'pw-mididump' (part of pipewire - the Linux audio system) using helvum (helvum is a software patchbay/router). pw-mididump shows the note played and also the timestamp where it was received - very convenient. Unfortunately this was the point where the ship beached - the jitter is horrible:
833: track: 0 sec:1630.550659 Note On (channel 10): note A0, velocity 127 834: track: 0 sec:1631.574707 Note On (channel 10): note A0, velocity 127 528: track: 0 sec:1632.549683 Note On (channel 10): note A0, velocity 127 526: track: 0 sec:1633.573608 Note On (channel 10): note A0, velocity 127 534: track: 0 sec:1634.597778 Note On (channel 10): note A0, velocity 127 226: track: 0 sec:1635.572632 Note On (channel 10): note A0, velocity 127 946: track: 0 sec:1636.547607 Note On (channel 10): note A0, velocity 127 950: track: 0 sec:1637.571777 Note On (channel 10): note A0, velocity 127 638: track: 0 sec:1638.546631 Note On (channel 10): note A0, velocity 127 590: track: 0 sec:1639.569702 Note On (channel 10): note A0, velocity 127 284: track: 0 sec:1640.544678 Note On (channel 10): note A0, velocity 127 338: track: 0 sec:1641.569702 Note On (channel 10): note A0, velocity 127 ...(look for the 'SEC:...' part).
Earlier I had tried to do this over MIDI-over-RTP (also called "Apple-MIDI", which is in this case MIDI over WIFI in RTP packets) but the jitter over WiFi was also dramatic. Anyway at this point I gave up.
picture
Click on the photo for higher resolution

source code
ubermetronome.src.zip (Arduino code)
Teensy 4.1 uC
Of course with a Teensy 4.1 microcontroller all problems disappear. Ok no BLE nor MIDI over WiFi, but MIDI over USB works very well!
Not synced to PPS:
83: track: 0 sec:856.428406 Note On (channel 1): note C8, velocity 55 600: track: 0 sec:857.420471 Note On (channel 1): note A#6, velocity 55 638: track: 0 sec:858.424011 Note On (channel 1): note A#6, velocity 55 453: track: 0 sec:859.422729 Note On (channel 1): note F#0, velocity 55 378: track: 0 sec:860.423828 Note On (channel 1): note D#6, velocity 55 398: track: 0 sec:861.427002 Note On (channel 1): note D2, velocity 55 318: track: 0 sec:862.427979 Note On (channel 1): note A#0, velocity 55 100: track: 0 sec:863.426086 Note On (channel 1): note C#6, velocity 55 61: track: 0 sec:864.427917 Note On (channel 1): note B8, velocity 55
Teensy 4.1 clockticks in a second interval
S999992 S999992 S999993 S999992 S999992 S999992 S999992 S999992 S999992 S999992 S999992 S999992 S999992 S999993 S999992 S999992 S999992 S999992Closer to the second.
MIDI timing over USB
861: track: 0 sec:28627.666016 Note On (channel 10): note A0, velocity 127 733: track: 0 sec:28628.666016 Note On (channel 10): note A0, velocity 127 607: track: 0 sec:28629.666016 Note On (channel 10): note A0, velocity 127 481: track: 0 sec:28630.666016 Note On (channel 10): note A0, velocity 127 352: track: 0 sec:28631.666016 Note On (channel 10): note A0, velocity 127 224: track: 0 sec:28632.666016 Note On (channel 10): note A0, velocity 127 92: track: 0 sec:28633.666016 Note On (channel 10): note A0, velocity 127 990: track: 0 sec:28634.666016 Note On (channel 10): note A0, velocity 127 862: track: 0 sec:28635.666016 Note On (channel 10): note A0, velocity 127 733: track: 0 sec:28636.666016 Note On (channel 10): note A0, velocity 127 605: track: 0 sec:28637.666016 Note On (channel 10): note A0, velocity 127