Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PuckJS Firmware #59

Open
bettse opened this issue May 11, 2021 · 15 comments
Open

PuckJS Firmware #59

bettse opened this issue May 11, 2021 · 15 comments

Comments

@bettse
Copy link

bettse commented May 11, 2021

I ported the firmware to the PuckJS (https://www.puck-js.com/)

function AirTag(pk) {
  var ad = [
    0x1e, /* Length (30) */
    0xff, /* Manufacturer Specific Data (type 0xff) */
    0x4c, 0x00, /* Company ID (Apple) */
    0x12, 0x19, /* Offline Finding type and length */
    0x00, /* State */
    pk[6], pk[7], pk[8], pk[9], pk[10], pk[11], pk[12], pk[13], 
    pk[14], pk[15], pk[16], pk[17], pk[18], pk[19], pk[20], pk[21], 
    pk[22], pk[23], pk[24], pk[25], pk[26], pk[27],
    0x00, /* First two bits */
    0x00, /* Hint (0x00) */
  ];
  ad[29] = pk[0] >> 6;

  const addr = [
    pk[0], pk[1], pk[2], pk[3], pk[4], pk[5]
  ];

  addr[0] |= 0xC0;

  const s = addr
    .map(x => x.toString(0x10).padStart(2, '0'))
    .join(':')
  
  return {
    Ad: [].slice.call(ad),
    Address: s + " random",
  };
};

const public_key = [
  0xbf, 0x44, 0x1f, 0xa2, 0x44, 0x40, 0x70, 0x65, 0x24, 0xe4, 0x0f, 0x5b,
  0x4b, 0xc0, 0xd1, 0x8e, 0x40, 0x98, 0xe4, 0x6f, 0xa6, 0xd5, 0xd4, 0x14,
  0x0a, 0xfe, 0xa8, 0x05
];

const airtag = AirTag(public_key);

NRF.setAddress(airtag.Address);

NRF.setAdvertising([
  airtag.Ad,
  {} // this will add a 'normal' advertising packet showing name/etc  
], {interval:100});
@gfwilliams
Copy link

This is great! Just to add that it's possible to craft a link of the form https://espruino.com/ide?upload&code=... which will automatically upload the firmware to a Puck, so if there's interest we could add this to OpenHaystack really easily?

@LowEnergyGenerator
Copy link

This is great! Just to add that it's possible to craft a link of the form https://espruino.com/ide?upload&code=... which will automatically upload the firmware to a Puck, so if there's interest we could add this to OpenHaystack really easily?

Did this ever happen? My Puck.js is coming in a few days.

@gfwilliams
Copy link

As far as I know nothing got added past the code here (but uploading this code is itself pretty easy).

Even if this didn't go into openhaystack, it could be added to https://espruino.github.io/EspruinoApps/ or https://banglejs.com/apps/ pretty easily so that uploading could be just a matter of copying/pasting the public key in.

I don't have an iPhone or even a remotely recent Mac, but @LowEnergyGenerator if you're in a position to test then I can add something to https://espruino.github.io/EspruinoApps/ that you can try out.

@ak2k
Copy link

ak2k commented Nov 4, 2022

As a report, I successfully used the code above (deployed using the Chrome App interface) with a puck.js.

Apologies, I misspoke. Some time had passed so I checked more carefully and it seems that I actually used this modification which the author states was based on the code above.

/*
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");

In this variation, the "public (advertisement) key" string takes as input directly the string given by OpenHaystack. Note also the more typical advertising interval -- after a few months of use at this setting, I've experienced barely any battery drain.

@gfwilliams
Copy link

That's great! So what does the public_key string you can copy from OpenHaystack look like?

Is it literally just:

  0xbf, 0x44, 0x1f, 0xa2, 0x44, 0x40, 0x70, 0x65, 0x24, 0xe4, 0x0f, 0x5b,
  0x4b, 0xc0, 0xd1, 0x8e, 0x40, 0x98, 0xe4, 0x6f, 0xa6, 0xd5, 0xd4, 0x14,
  0x0a, 0xfe, 0xa8, 0x05

Or would there be a better format of data for the app to accept?

@LowEnergyGenerator
Copy link

I would be happy to test out whatever if someone wants to walk me through it, I'm new at this.

gfwilliams added a commit to espruino/EspruinoApps that referenced this issue Nov 7, 2022
@gfwilliams
Copy link

Just added - if you go to https://espruino.com/apps you should be able to paste in the base64 code? If this works I'd love to get it added as a Bangle.js app too so users can find their Bangle with it :)

@LowEnergyGenerator
Copy link

LowEnergyGenerator commented Nov 8, 2022

I do not believe it worked. I added my public key in the web app and received this error on the web IDE:

 ____                 _
|  |_ ___ ___ _ ||___ ___
|  | -| . |  | | | |   | . |
|
||  || |
||||_|
         |
| espruino.com
 2v15 (c) 2021 G.Williams

Uncaught SyntaxError: Got UNFINISHED STRING expected EOF
 at line 6 col 59 in .boot0
const key_b64 = "XXMYPUBLICKEYXX";

                                                          ^

OpenHaystack Beacon

Uncaught TypeError: Assignment to a constant
 at line 8 col 17
const key_b64 = "XXXMYPUBLICKEYXXX";

                ^
Public key:
- base64:
XXXMYPUBLICKEYXXX
- hexadecimal:
XX XX...

MAC address:
XX:XX....

Advertising packet:
1e ff .....

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

Screen Shot 2022-11-07 at 9 21 59 PM

And I'm not sure which code string to use, Im guessing I need to be using the Advertisement Key, Base64?

@gfwilliams
Copy link

Sorry - that was a stupid mistake of mine. Please can you try installing again? I think it should be fixed now.

The Uncaught TypeError: Assignment to a constant might have been because you'd already saved the OpenHaystack code in a different file. Sending require("Storage").eraseAll() in the IDE should clear out anything that's left in there which might get rid of that problem

@LowEnergyGenerator
Copy link

Sorry - that was a stupid mistake of mine. Please can you try installing again? I think it should be fixed now.

The Uncaught TypeError: Assignment to a constant might have been because you'd already saved the OpenHaystack code in a different file. Sending require("Storage").eraseAll() in the IDE should clear out anything that's left in there which might get rid of that problem

Ok, that may have worked. Waiting to see if it comes up on OpenHaystack.

@LowEnergyGenerator
Copy link

LowEnergyGenerator commented Nov 10, 2022

Success! One question, does your app load the code into the Flash or RAM? When the battery is removed, will the OpenHaystack code disappear or remain?

@gfwilliams
Copy link

That's great news! The code will remain in flash (it's as if 'save to flash' was selected in the IDE) so it'll remain when the battery is removed

@gfwilliams
Copy link

Just to say I've just added the functionality for Bangle.js smartwatches too, so hopefully folks will now be able to use OpenHaystack to find them if they get lost: https://espruino.github.io/BangleApps/?id=openhaystack

@ak2k
Copy link

ak2k commented Apr 4, 2023

Nice, @gfwilliams! The app indeed makes this super easy.

Out of curiosity, I notice that you've elected not to set a particular interval for advertisements. Are you expecting power usage to be just fine on the default interval?

@gfwilliams
Copy link

Hi, yes - the default interval is 375ms which seems to be a reasonable compromise for most cases giving ~1 year of battery life, and since the Puck is alternating advertisements so still advertising its name, that means that the Haystack beacon is only broadcast every 750ms.

Going any slower would make it hard to connect to the Puck reliably - however I guess there could be an option to lower the Puck's advertising interval, not advertise the Puck's name and also make it non-scannable and non-connectable, and together those could have a big impact on battery life (potentially doubling it)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants