PlatformIO/Zephyr firmware for an nRF52832 coin-cell tracker that broadcasts both:
- an Apple OpenHaystack / Find My advertisement
- a Google Find Hub advertisement
The firmware is intentionally non-connectable. It has no UART, LEDs, buttons, GATT application, scanning, pairing, or periodic application loop. After startup, the BLE controller schedules both advertising sets while the CPU sleeps.
This is experimental, unofficial firmware. It is not certified by Apple or Google. Use it only for property you own.
Apple and Google do not take turns. Zephyr creates two independent BLE advertising sets and the nRF52 controller keeps both active concurrently:
| Configured strings | Active advertising |
|---|---|
| Apple only | Apple only |
| Google only | Google only |
| Apple + Google | Apple and Google simultaneously |
| Neither | No tracking advertisement |
Both sets transmit every two seconds at 0 dBm by default. Once they have started, the application thread sleeps indefinitely.
- Install Visual Studio Code and the PlatformIO IDE extension.
- Open this project directory in PlatformIO.
- Open
include/tracker_keys.h. - Paste the Apple and/or Google advertisement strings between the existing quotes. Optionally adjust the interval and transmit power in the same file.
- Connect a CMSIS-DAP/DAPLink or J-Link probe to the nRF52832 SWD pins.
- Select PlatformIO: Upload, or run
pio run -t upload.
The nRF52832 is significantly more suitable than an ESP32 for a CR2032 beacon:
- low sleep current
- controller-managed BLE advertising
- integrated DC/DC support
- small modules with exposed SWD pads
- direct operation from a coin cell when the board has no inefficient regulator
Actual battery life depends on the module, regulator, LEDs, temperature, battery quality, radio power, and advertising interval. The default uses two advertising sets at 2 seconds and 0 dBm. Do not treat any battery-life estimate as a guarantee.
The build target is nrf52_dk, but the application uses no DK-specific pins.
It can therefore be flashed to many generic nRF52832 modules.
Good module characteristics:
- nRF52832
- exposed
SWDIO,SWCLK,VDD, andGND - no always-on power LED
- no USB-to-serial chip
- no high-quiescent-current regulator
- CR2032 holder or direct 3 V input
- 32.768 kHz crystal when maximum battery life is required
Examples include compact Holyiot, Minew, Ebyte, and generic nRF52832 beacon boards. Verify the exact pinout and voltage before connecting a programmer.
Edit include/tracker_keys.h.
In Find_My_Web:
- Generate an Apple P-224 identity or open an existing device.
- Copy Apple advertisement key · Base64 string ready for the firmware.
- Paste that exact 40-character ASCII string:
#define APPLE_ADVERTISEMENT_KEY_BASE64 "PASTE_BASE64_HERE"The value must decode to 28 bytes. Do not place the Apple private key in the firmware. P-224 key generation applies only to Apple.
Register the tracker with GoogleFindMyTools or traccar/google-find-hub-sync. Google keys are not generated by Find_My_Web. Import or paste the resulting advertisement EID into the device's Google advertisement key field so the web application can retain it for firmware setup.
Copy the 20-byte EID, represented as exactly 40 hexadecimal ASCII characters:
#define GOOGLE_ADVERTISEMENT_KEY_HEX "00112233445566778899aabbccddeeff00112233"Do not add 0x, commas, spaces, or separators.
The strings use different encodings by protocol design:
- Apple: 40-character Base64 text, decoding to 28 bytes.
- Google: 40 hexadecimal characters, representing 20 bytes.
Either macro may remain "". The other provider continues to work normally.
From the PlatformIO terminal:
pio runThe first build downloads the pinned Nordic nRF52 platform and Zephyr packages. The main output is:
.pio/build/nrf52_dk/firmware.hex
The repository also includes a GitHub Actions workflow that compiles every
push and pull request. Its unconfigured build is useful for CI only: a
personalized tracker must be rebuilt after inserting its own strings in
tracker_keys.h.
The default configuration uses cmsis-dap, which works with many inexpensive
DAPLink probes.
Wire the probe to the tracker:
| Probe | nRF52832 |
|---|---|
SWDIO |
SWDIO |
SWCLK |
SWCLK |
GND |
GND |
VTref |
tracker VDD |
Power the tracker from one source only. If the probe supplies power, use 3.0 or 3.3 V and remove the coin cell.
Upload:
pio run -t uploadIf the chip is protected, perform a mass erase/recover with the probe's tool, then upload again.
Change platformio.ini:
debug_tool = jlink
upload_protocol = jlinkThen run:
pio run -t uploadA Nordic nRF52 DK can also program an external target through its Debug Out header.
ST-Link is not the default PlatformIO uploader for this project. Use OpenOCD or pyOCD when the probe firmware supports CMSIS-DAP. A genuine CMSIS-DAP/DAPLink probe or J-Link is generally simpler for nRF52.
The radio settings are directly below the keys in
include/tracker_keys.h:
#define ADVERTISING_INTERVAL_MS 2000U
#define ADVERTISING_TX_POWER_DBM 0Both providers advertise independently at this interval. Increasing it reduces radio use but also reduces the probability and speed of sightings.
Google's public Find Hub accessory specification recommends at least one Find Hub frame every two seconds and at least 0 dBm conducted transmit power. The defaults follow those values.
| Goal | Interval | TX power | Notes |
|---|---|---|---|
| Recommended dual-network balance | 2000U |
0 |
Meets the Google timing and power recommendations |
| Faster nearby sightings | 1000U |
0 |
Roughly doubles advertising activity and radio energy |
| Maximum practical range | 2000U |
4 |
Highest nRF52832 power; higher peak current |
| Indoor battery saving | 2000U |
-4 |
Lower range; below Google's published 0 dBm recommendation |
| Aggressive battery saving | 4000U |
-8 |
Slower sightings and outside Google's two-second recommendation |
The nRF52832 accepts intervals from 20 to 10,240 ms and power requests from -40 to +4 dBm. Its controller selects the nearest supported hardware power step. The firmware applies the configured power separately to both simultaneous advertising sets.
For a public dual-network tracker, start with 2000U and 0. Change one value
at a time and measure current on the finished board. Coin cells are sensitive
to peak load, so maximum range is not automatically the best configuration.
The default configuration uses the RC low-frequency clock because it works on
generic modules without an LFXO. For the lowest current on a board that has a
32.768 kHz crystal, edit zephyr/prj.conf:
CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=n
CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y
CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION=nRebuild and flash.
Removing an always-on LED can save more power than a firmware optimization. Measure current on the actual assembled board. A bare nRF52832 module can sleep efficiently; a development board with LEDs, USB, and a poor regulator cannot.
Use a BLE scanner such as nRF Connect for Mobile.
Apple frames contain manufacturer data beginning with:
4C 00 12 19
Google frames contain service data for UUID:
FEAA
The device is intentionally non-connectable and does not advertise a friendly name.
- two concurrently active, controller-managed advertising sets
- no application timer used for switching providers
- CPU sleeps forever after initialization
- no scanning or BLE connections
- no serial console or logging
- no LEDs
- DC/DC enabled
- 0 dBm TX power
- 2-second advertising interval
| File | Purpose |
|---|---|
include/tracker_keys.h |
ASCII Apple and Google key configuration |
src/main.c |
decoding and simultaneous dual-advertising setup |
zephyr/prj.conf |
low-power Zephyr and Bluetooth configuration |
platformio.ini |
pinned PlatformIO target and SWD uploader |
.github/workflows/build.yml |
reproducible PlatformIO CI build |
- OpenHaystack
- macless-haystack
- GoogleFindMyTools
- traccar/google-find-hub-sync
- Google Find Hub accessory specification
MIT. Third-party projects retain their own licenses.