mirror of https://github.com/espruino/BangleApps
commit
b558052d83
|
@ -0,0 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Corrected NaN test for GPS
|
||||
0.03: Removed remaining invalid references to Number.isFinite
|
||||
0.04: Improved menu/display interaction
|
|
@ -0,0 +1,19 @@
|
|||
# OpenLocate Beacon
|
||||
|
||||
Collect geolocation sensor data from the Bangle.js 2's GPS and barometer, display the live readings on-screen, and broadcast in Bluetooth Low Energy (BLE) OpenLocate Beacon packets (LCI over BLE) to any listening devices in range.
|
||||
|
||||
## Usage
|
||||
|
||||
The advertising packets will be recognised by [Pareto Anywhere](https://www.reelyactive.com/pareto/anywhere/) open source middleware and any other program which observes the standard packet types. See our [Bangle.js Development Guide](https://reelyactive.github.io/diy/banglejs-dev/) for details.
|
||||
|
||||
## Features
|
||||
|
||||
Advertises packets with the OpenLocate Beacon geolocation element when a GPS fix is available, and packets with the name "Bangle.js" otherwise.
|
||||
|
||||
## Requests
|
||||
|
||||
[Contact reelyActive](https://www.reelyactive.com/contact/) for support/updates.
|
||||
|
||||
## Creator
|
||||
|
||||
Developed by [jeffyactive](https://github.com/jeffyactive) of [reelyActive](https://www.reelyactive.com)
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": "openlocatebeacon",
|
||||
"name": "OpenLocate Beacon",
|
||||
"shortName": "OpenLocate Beacon",
|
||||
"version": "0.04",
|
||||
"description": "Advertise GPS geolocation data using the OpenLocate Beacon packet specification.",
|
||||
"icon": "openlocatebeacon.png",
|
||||
"screenshots": [],
|
||||
"type": "app",
|
||||
"tags": "tool,sensors,bluetooth",
|
||||
"supports" : [ "BANGLEJS2" ],
|
||||
"allow_emulator": true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{ "name": "openlocatebeacon.app.js", "url": "openlocatebeacon.js" },
|
||||
{ "name": "openlocatebeacon.img", "url": "openlocatebeacon-icon.js",
|
||||
"evaluate": true }
|
||||
]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwhHXAB+sr1dnMzmcPh4DBnNdr2sDyAsOropCABczroyaxE5FhoAFnOIFqutFqgxE1ouSrwdHnWIxGz2YPBAYIHBnQTHrwuQLgwsBURwyGnIWN2bmFnR4S1oxFmZyCFx0zLY2r0md4AACzuk1ZjGDoowKCAk6JwuBFggAFzuBOApiEmYuIBwjSE2ddvOcFxIABzl5rpWErxQJN4QuIPIN5FpYADvKlFGAivGHZAuSGBCDEFwg6EOoZnBFyQwCK4mzQg4IECIYuBqzqKehVWDwxWFLwc5M4czLxPB5HP5/I4JgJmYfDnJgFEwJeFG4MyLw4tB6wPB6wxBMA8yRAhgDHAOtY44FBq2cFw3Qa4nX6AwGziQBEIwAB1o1DHoyOG4PPFwoAB56SGSAKBGA4SVDBgc6AwKOG45eGMAXHSAwbBnSQGnIvD1psFF43IXgQAF6yQGF4SQDXQczdwezF5pfJF5uzF4bED1gACF5KPSKoMzEY0PgAAJuQdFMAIvH5wQGuQkKABVOzhgG57BE63PLw2cpwvVkgvGGAPI5/Q6HP5AuGF4MkF6pgIAAPB4/H4ILHLxsrMBbBHABlyLxcrF5ZgKABJeOqyRMMCVyEBlWwLyNywuPyzsNwPXeS6NTAAPX67AMSKCNNXwIvBSBqRBGBlyRpqOCAAIRNSJiNPRwQABqwwPF5IuPqwvD1gUOSJKNPgGsF4bBPgEqSI2clQYOXoYADlbCUXiErFwyRQgCREuQVPRoqRTkmWFwOWXh6NHGCaRBRqAuLGCIAQFxowgFx70ClYtZlbqJMUZcRAA1WFqdWFq5jESp0rLbAyJq0rGggFBqwsSA=="))
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* Copyright reelyActive 2024
|
||||
* We believe in an open Internet of Things
|
||||
*/
|
||||
|
||||
|
||||
// Non-user-configurable constants
|
||||
const APP_ID = 'openlocatebeacon';
|
||||
const ADVERTISING_OPTIONS = { showName: false, interval: 5000 };
|
||||
|
||||
|
||||
// Global variables
|
||||
let bar, gps;
|
||||
let sequenceNumber = 0;
|
||||
|
||||
|
||||
// Menus
|
||||
let mainMenu = {
|
||||
"": { "title": "OpenLocateBcn" },
|
||||
"Lat": { value: null },
|
||||
"Lon": { value: null },
|
||||
"Altitude": { value: null },
|
||||
"Satellites": { value: null }
|
||||
};
|
||||
|
||||
|
||||
// Encode the OpenLocate geo location element advertising packet
|
||||
function encodeGeoLocationElement() {
|
||||
let lci = new Uint8Array(16);
|
||||
let seqFrag = ((sequenceNumber++ & 0x0f) << 4) + 0x01;
|
||||
let rfc6225lat = toRfc6225Coordinate(gps.lat);
|
||||
let rfc6225lon = toRfc6225Coordinate(gps.lon);
|
||||
let rfc6225alt = toRfc6225Altitude(bar.altitude);
|
||||
lci[0] = rfc6225lat.integer >> 7;
|
||||
lci[1] = ((rfc6225lat.integer & 0xff) << 1) + (rfc6225lat.fraction >> 24);
|
||||
lci[2] = (rfc6225lat.fraction >> 16) & 0xff;
|
||||
lci[3] = (rfc6225lat.fraction >> 8) & 0xff;
|
||||
lci[4] = rfc6225lat.fraction & 0xff;
|
||||
lci[5] = rfc6225lon.integer >> 7;
|
||||
lci[6] = ((rfc6225lon.integer & 0xff) << 1) + (rfc6225lon.fraction >> 24);
|
||||
lci[7] = (rfc6225lon.fraction >> 16) & 0xff;
|
||||
lci[8] = (rfc6225lon.fraction >> 8) & 0xff;
|
||||
lci[9] = rfc6225lon.fraction & 0xff;
|
||||
lci[10] = bar.altitude ? 0x10 : 0x00;
|
||||
lci[11] = (rfc6225alt.integer >> 16) & 0xff;
|
||||
lci[12] = (rfc6225alt.integer >> 8) & 0xff;
|
||||
lci[13] = rfc6225alt.integer & 0xff;
|
||||
lci[14] = rfc6225alt.fraction & 0xff;
|
||||
lci[15] = 0x41;
|
||||
|
||||
return [
|
||||
0x02, 0x01, 0x06, // Flags
|
||||
0x16, 0x16, 0x94, 0xfd, 0x09, seqFrag, 0x30, lci[0], lci[1], lci[2],
|
||||
lci[3], lci[4], lci[5], lci[6], lci[7], lci[8], lci[9], lci[10], lci[11],
|
||||
lci[12], lci[13], lci[14], lci[15]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// Convert a latitude or longitude coordinate to RFC6225
|
||||
function toRfc6225Coordinate(coordinate) {
|
||||
let integer = Math.floor(coordinate);
|
||||
let fraction = Math.round((coordinate - integer) * 0x1ffffff);
|
||||
|
||||
if(integer < 0) {
|
||||
integer += 0x1ff + 1;
|
||||
}
|
||||
|
||||
return { integer: integer, fraction: fraction };
|
||||
}
|
||||
|
||||
|
||||
// Convert altitude to RFC6225
|
||||
function toRfc6225Altitude(altitude) {
|
||||
if(!altitude) {
|
||||
return { integer: 0, fraction: 0 };
|
||||
}
|
||||
|
||||
let integer = Math.floor(altitude);
|
||||
let fraction = Math.round((altitude - integer) * 0xff);
|
||||
|
||||
if(integer < 0) {
|
||||
integer += 0x3fffff + 1;
|
||||
}
|
||||
|
||||
return { integer: integer, fraction: fraction };
|
||||
}
|
||||
|
||||
|
||||
// Update barometer
|
||||
Bangle.on('pressure', (newBar) => {
|
||||
bar = newBar;
|
||||
|
||||
mainMenu.Altitude.value = bar.altitude.toFixed(1) + 'm';
|
||||
E.showMenu(mainMenu);
|
||||
});
|
||||
|
||||
|
||||
// Update GPS
|
||||
Bangle.on('GPS', (newGps) => {
|
||||
gps = newGps;
|
||||
|
||||
mainMenu.Lat.value = gps.lat.toFixed(4);
|
||||
mainMenu.Lon.value = gps.lon.toFixed(4);
|
||||
mainMenu.Satellites.value = gps.satellites;
|
||||
E.showMenu(mainMenu);
|
||||
|
||||
if(!isNaN(gps.lat) && !isNaN(gps.lon)) {
|
||||
NRF.setAdvertising(encodeGeoLocationElement(), ADVERTISING_OPTIONS);
|
||||
}
|
||||
else {
|
||||
NRF.setAdvertising({}, { name: "Bangle.js" });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// On start: enable sensors and display main menu
|
||||
g.clear();
|
||||
Bangle.setGPSPower(true, APP_ID);
|
||||
Bangle.setBarometerPower(true, APP_ID);
|
||||
E.showMenu(mainMenu);
|
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
Loading…
Reference in New Issue