home..
DIY Airtags
Using Macless Haystack, anyone can use the Apple Findmy network with a BLE beacon and Linux server. No Apple device needed! Lets get started. I developed this program for the Nicenano v1(NRF52840) using the Zephyr 2.5.0 SDK:
main.cpp:
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gap.h>
#include <zephyr/bluetooth/controller.h>
static uint8_t pubKey[29] = {
<SET THIS TO THE HEXDUMP OF THE RAW PUBLIC KEY, NOT BASE64 ENCODED!!!>
};
static uint8_t adv_data[29] = {
0x4c, 0x00, /* Company ID (Apple) */
0x12, 0x19, /* Offline Finding type and length */
0x00, /* State */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, /* First two bits */
0x00, /* Hint (0x00) */
};
void set_payload_from_key(uint8_t *payload, uint8_t *public_key) {
/* copy last 22 bytes */
memcpy(&payload[5], &public_key[7], 22);
/* append two bits of public key */
payload[27] = public_key[1] >> 6;
}
static const struct bt_data ad[] = {
/* STEP 4.1.2 - Set the advertising flags */
//BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
/* STEP 4.1.3 - Set the advertising packet data */
//BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME)),
};
static const struct bt_data sd[] = {
BT_DATA(BT_DATA_MANUFACTURER_DATA, adv_data,sizeof(adv_data)),
};
void set_addr_from_key(bt_addr_t *addr, uint8_t *public_key) {
addr->val[0] = public_key[6];
addr->val[1] = public_key[5];
addr->val[2] = public_key[4];
addr->val[3] = public_key[3];
addr->val[4] = public_key[2];
addr->val[5] = public_key[1] | 0b11000000;
}
static bt_addr_le_t addr;
static bt_addr_t bt_addr;
int main(void) {
set_addr_from_key(&bt_addr,pubKey);
addr.a=bt_addr;
addr.type=BT_ADDR_LE_RANDOM;
//bt_addr_le_from_str("FF:AA:AA:AA:AA:FF", "random", &addr);
// bt_addr_le_from_str("FF:AA:AA:AA:AA:FF", "random", &addr2);
bt_id_create(&addr, NULL);
// bt_id_create(&addr2, NULL);
struct bt_le_adv_param adv_param = {
.id = 0,
.sid = 0,
.secondary_max_skip = 0,
.options = BT_LE_ADV_OPT_USE_IDENTITY,
.interval_min = 3200,
.interval_max = 3300,
.peer = NULL,
};
bt_enable(NULL);
set_payload_from_key(adv_data,pubKey);
bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
while(1) {
k_sleep(K_FOREVER);
}
}
prj.conf:
CONFIG_BT=y
CONFIG_BT_ID_MAX=2
CONFIG_BT_CTLR_TX_PWR_PLUS_4=y
CONFIG_LOG=n
CONFIG_GPIO=n
CONFIG_SERIAL=n
CONFIG_CONSOLE=n
CONFIG_PINCTRL=n
CONFIG_SPI=n
CONFIG_FLASH=n
CONFIG_USB_CDC_ACM_LOG_LEVEL_OFF=n
CONFIG_USB_DEVICE_STACK=n
CONFIG_MPU_ALLOW_FLASH_WRITE=n
CONFIG_NVS=n
CONFIG_SETTINGS_NVS=n
CONFIG_FLASH_PAGE_LAYOUT=n
CONFIG_FLASH_MAP=n
CONFIG_PM_DEVICE=y
The prj.conf file is very important!! It disabled all unessecary SPI, external SPI flash, GPIO, etc to minimize power consumption. I measureed with my multimeter and its roughtly 30micro A!!! Very good. A single CR2032 will last around 250 days. Lets try with a Xiao BLE to see if I can get even better low power usage. First I need to unbrick it because I accidently flashed wrong firmware onto it. DFU does not work at all, so I need to flash the original bootloader back.
© 2025 Wayne Zeng
•
Theme Moonwalk