6.1 KiB
FindMy / OpenHaystack Beacon
This document describes the experimental FindMy locator beacon for nRF52 MeshCore nodes.
Purpose
When enabled, a node advertises an Apple FindMy / OpenHaystack "offline finding" beacon alongside its normal mesh duties. Nearby iPhones anonymously relay its encrypted location to Apple's servers, letting you locate a deployed node through the global Find My network — no extra infrastructure, GPS, or cellular needed.
This is particularly useful for repeater nodes. To maximise coverage they are often deployed in exposed, unattended, hard-to-reach spots — rooftops, towers, hilltops, remote solar sites — which makes them especially prone to going missing, whether through theft, weather, a failed mount, or simply being forgotten. A FindMy beacon gives you a way to locate (or recover) such a node, or at least get a last-known position, without having to physically visit the site.
The node only ever broadcasts a public advertising key. Decrypting the location reports requires the matching private key, which you keep off-device on your own machine.
Supported hardware
nRF52840 boards only (Adafruit Bluefruit BLE stack), but the implementation is easily ported to ESP32 and other BLE capable chips. The challenge is with concurrent BLE usage, ie. companion nodes. TODO/Future work check if ble adverisements can be sent when the companion is disconnected from the phone, and disable when reconnected.
Pre-defined build environments:
| Environment | Board |
|---|---|
RAK_4631_repeater_findmy |
RAK4631 / RAK WisMesh (repeater, incl. Solar Repeater Mini) |
LilyGo_T-Echo_repeater_findmy |
LilyGo T-Echo |
The feature is gated behind the WITH_FINDMY_BEACON build flag, so it adds nothing to other
builds. It is intended for always-on roles (repeater/sensor) where BLE is otherwise unused. It
is not for the phone-companion firmware, which needs BLE for its own link — a node can be a
FindMy beacon or a phone companion, not both at once.
Generating keys
Keys are generated off-device with the OpenHaystack / macless-haystack tooling — see
macless-haystack (or the original
OpenHaystack). Its generate_keys.py produces
three values per key; use them as follows:
| Value | Where it goes | Purpose |
|---|---|---|
| Advertisement key (public, 28 bytes) | the node (set findmy.key) |
broadcast in the BLE advert + MAC |
| Private key | your server only | decrypts the location reports |
| Hashed adv key | your server (lookup id) | used to query Apple for reports |
Only the advertisement (public) key goes on the node. Never load the private key onto the device — it would be broadcast publicly and reports would not decrypt. (Note: the private key is also 28 bytes, so the firmware cannot tell them apart by length — pick the value labelled advertisement.)
Node configuration
Configure over the local USB serial console (115200 baud) or remotely from the MeshCore app's
Command Line tab when logged in as admin. Configuration is stored in /findmy on the node's
internal filesystem (independent of the normal node preferences).
set findmy.key <base64-advertisement-key> -> OK - reboot to apply
set findmy on -> OK - on, reboot to apply
get findmy -> > on, mac DF:41:B5:D7:F3:BF
reboot
set findmy.keymust decode to exactly 28 bytes, otherwise it reportsError: decoded N bytes, expected 28.get findmyshows the enabled state and the derived BLE MAC (it never echoes the key). The MAC iskey[0]|0xC0 : key[1] : key[2] : key[3] : key[4] : key[5].- The beacon starts only at boot, so
reboot(or a power cycle) is required to apply changes. On boot the node printsFindMy beacon started. set findmy offthenrebootstops advertising (the stored key is kept).
Verifying
- On air: scan with a BLE tool (e.g. nRF Connect). You should see a non-connectable
advertiser at the address
get findmyreported (address type Random) carrying Apple manufacturer data beginning4C 00 12 19 …. - Key sanity: run heystack's
tools/showmac.pyon the advertisement key you loaded — it must print the same MAC asget findmy. A mismatch means the wrong key was loaded.
Retrieving location data
Use your own server with the private key — the node cannot retrieve anything itself. With
macless-haystack, query and decrypt reports using its scripts, e.g. fetch_reports.py (a
findmy.py-style query), which authenticates to Apple (via Anisette), pulls the encrypted
reports for the hashed adv key, and decrypts them with the private key. See the
macless-haystack README for setup
(Anisette/Apple-ID requirements) and exact invocation.
Notes and caveats
- Latency: Find My is deliberately latency-tolerant. Reports appear only after the node is near a passing iPhone and can take minutes to a few hours.
- Power: continuous BLE advertising keeps the SoftDevice awake, so idle current is higher
than a node with BLE off. The advertising interval defaults to ~2 s; override with
-D FINDMY_ADV_INTERVAL=<0.625ms-units>. - Privacy: the key is static (no rotation), so anyone holding it can track the node.
- Static key may be rate-limited / blocked (TODO): genuine AirTags rotate their advertising key roughly daily, and Apple's network is tuned for that. A single never-changing key can be treated as anomalous and may have its reports throttled or dropped over time, so long-term reliability is not guaranteed. Future work: add key rotation — store a sequence of keys and advance the index on a daily schedule (matching AirTag behaviour), and extend the key generator to emit a batch of keys with daily-incrementing indices that the server side can resolve.
- OTA: the beacon coexists with
start ota— triggering a firmware update reuses the running BLE stack and switches to the DFU advertiser automatically.