• Below is some code to make a Puck.js (or Pixl.js or MDBT42) act as a beacon to be tracked via Apple's massive Find My network ...act as an Apple AirTag equivalent if you will. Thus you can find your Puck.js if you lose it.

    An Apple Mac computer is required though.

    It is some Espruino code to mimic the beacon code of the OpenHaystack framework. It is based on Eric Betts original code.

    The code:

    /*
    OPENHAYSTACK
    A framework for tracking personal Bluetooth devices via Apple's massive Find My network
    2021-07-15 SK
    */
    print("OpenHaystack Beacon\n===================\n");
    
    const key_b64 = "Fy5SNl1ehMqUe49PJ4sxgrvTJLj/hlNjVYy5qg=­="; // replace with your public (advertisement) key
    
    function b64ToArray(str) {
        let bstr = atob(str);
        let arr = [];
        for (let i = 0; i < bstr.length; i++) {
            arr[i] = bstr.charCodeAt(i);
        }
        return arr;
    }
    
    const key = b64ToArray(key_b64); // public key
    
    print("Public key:");
    print("- base64:\n" + key_b64);
    print("- hexadecimal:\n" + key.map(x => x.toString(16).padStart(2, '0')).join(' '));
    
    const mac = [ key[0] | 0b11000000, key[1], key[2], key[3], key[4], key[5] ].map(x => x.toString(16).padStart(2, '0')).join(':'); // mac address
    
    print("\nMAC address:\n" + mac);
    
    const adv = [ 0x1e, 0xff, 0x4c, 0x00, 0x12, 0x19, 0x00, key[6], key[7], key[8], key[9], key[10], key[11], key[12], key[13], key[14], key[15], key[16], key[17], key[18], key[19], key[20], key[21], key[22], key[23], key[24], key[25], key[26], key[27], key[0] >> 6, 0x00 ]; // advertising packet
    
    print("\nAdvertising packet:\n" + adv.map(x => x.toString(16).padStart(2, '0')).join(' ') + "\n");
    
    NRF.setAddress(mac);
    NRF.setAdvertising(adv, {interval:5000});
    
    print("\nTo start beacon click on IDE upper-left icon to disconnect board. BLE stack will restart");
    print("Then reset (if code saved in RAM) / hard-reset (if code in Flash) board to reconnect IDE");
    print("To hard-reset hold BTN1 for >5s while repowering the board. Type reset(1) to erase Flash");
    

    ...yields:

     ____                 _
    |  __|___ ___ ___ _ _|_|___ ___
    |  __|_ -| . |  _| | | |   | . |
    |____|___|  _|_| |___|_|_|_|___|
             |_| espruino.com
     2v09 (c) 2021 G.Williams
    >
    OpenHaystack Beacon
    ===================
    
    Public key:
    - base64:
    Fy5SNl1ehMqUe49PJ4sxgrvTJLj/hlNjVYy5qg==­
    - hexadecimal:
    17 2e 52 36 5d 5e 84 ca 94 7b 8f 4f 27 8b 31 82 bb d3 24 b8 ff 86 53 63 55 8c b9 aa
    
    MAC address:
    d7:2e:52:36:5d:5e
    
    Advertising packet:
    1e ff 4c 00 12 19 00 84 ca 94 7b 8f 4f 27 8b 31 82 bb d3 24 b8 ff 86 53 63 55 8c b9 aa 00 00
    
    BLE Connected, queueing BLE restart for later
    
    To start beacon click on IDE upper-left icon to disconnect board. BLE stack will restart
    Then reset (if code saved in RAM) / hard-reset (if code in Flash) board to reconnect IDE
    To hard-reset hold BTN1 for >5s while repowering the board. Type reset(1) to erase Flash
    
  • Below are some power measurements (current @ 3V) of the Puck.js programmed using this code made using my NORDIC Power Profiler Kit II:

    Current shape when switching from being connected to the IDE to restarting the BLE stack to act as a beacon

    Average current consumption of the beacon over a minute ~ 6.30uA

    Average current and shape of the beacon while sleeping ~2.10uA


    Average current while broadcasting ~ 2.91mA

    TL;DR

    The Puck.js running this code is very energy efficient:
    According to those readings, the average power consumption is 6.30uA which makes a 3V CR2032 230mAh battery last more than 4 years:

    230/0.00630 = 36507 hours = 1521 days = 4.16 years

  • There are some spikes on the graphs I cannot explain though:
    See the little spike in the middle of the two broadcasting spikes (separated by 5s as programmed):

    The average current on the 4.999s selected range when the beacon is supposed to be sleeping is 3.52uA and could be reduced further (2.08uA, see previous post) if this spike in the middle wouldn't be there.

    This spike when zoomed in, looks like this:

    It is not as greedy as a broadcasting spike though (386.58uA vs 2.91mA).

    Such spikes are spread regularly at a 8s intervals:

  • The 8s interval spikes (see above post) might be due to Espruino or Puck.js's hardware themselves as they don't occur when running the OpenHaystack (ultra low power) alternative firmware on a nRF51822 (see measurement N51-XTAL-LDO in this post on GitHub).

    Note that, despite those 8s interval spikes, it seems the nRF52832 is more power efficient than the nRF51822 (6.30uA vs 7.60uA).

    There is no external low-frequency 32,768kHz crystal available on the Puck.js, thus the internal low-frequency RC oscillator used instead that requires regular clock calibration, might be responsible for the 8s interval spikes. On the nRF51822, clock calibration when using the internal RC circuit seems to be more erratic though (see measurement N51-RC-LDO).

  • After further analysis it seems the spikes on N51-RC-LDO in this post on GitHub are not as erratic as I thought:

    Those are 4s interval spikes and are certainly due to the clock calibration involved when using the internal low-frequency RC oscillator (as there is no such spikes when enabling the external low-frequency 32.768kHz crystal) (see N51-XTAL-LDO).

    Thus the 8s interval spikes on the nRF52832 might be due to clock calibration as well (at half the frequency though). As the Puck.js is not equipped with an external low-frequency 32,768kHz crystal, I cannot enable it and thus clock calibration is mandatory.

  • Nice! Thanks for posting up the code!

    This is the kind of thing that'd be quite cool in https://www.espruino.com/apps I guess - then users could just post in the code they get from OpenHaystack (maybe OpenHaystack could even be modified to post up a URL that would automatically load up a Puck)

    But yes, those spikes may well be due to clock calibration. What do you think the average power draw from them is?

  • Indeed I am planning to submit you a pull request with the code on GitHub.
    (It may take me some time thought as I am not that familiar with the Espruino Store architecture and Javascript in general.)
    I would like to design a popup window where someone enters his/her OpenHaystack advertisement key (aka public key) before uploading the code.
    (I have seen some examples on the Store I could get inspired from.)


    To compute the extra average current consumption due to clock calibration, data is taken from the above measurements (@3V):

    • 386.58uA clock calibration average current during 18.64ms every 8s
    • 2.14uA deep sleep average current

    Thus, alternating calibration for 0.001864s and deep sleep for 8-0.001864 = 7.998136s every 8s, the average current required for such cycles can be computed as a weighted arithmetic mean:

    ( 386.58*0.001864 + 2.14*7.998136 ) / 8 = 2.23uA

    The additional average current consumption due to clock calibration (mandatory when using the internal RC oscillator) with respect to a board that deep sleeps only (thus equipped with an external low-frequency crystal) is:

    2.23 - 2.14 = 0.09uA

    This represents 0.09 / 2.14 = 0.042 = 4.2% more


    PS: Could you move this thread to Home / Projects in the Forum?
    I believe it rather belongs there than where it is currently at Home / Official Espruino Devices / Puck.js, Pixl.js and MDBT42.

  • Thanks! That's really interesting - so it is definitely more, but 0.09uA is not something crazy. On a Puck it's not going to make a huge difference to battery life.

    And sure - I'll move it :)

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

OpenHaystack beacon to be tracked via Apple's massive "Find My" network

Posted by Avatar for sebi @sebi

Actions