Skip to content

Commit b558052

Browse files
authored
Merge pull request #3540 from reelyactive/master
Added OpenLocate Beacon app
2 parents a5930b1 + d66fc98 commit b558052

File tree

6 files changed

+164
-0
lines changed

6 files changed

+164
-0
lines changed

apps/openlocatebeacon/ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
0.01: New App!
2+
0.02: Corrected NaN test for GPS
3+
0.03: Removed remaining invalid references to Number.isFinite
4+
0.04: Improved menu/display interaction

apps/openlocatebeacon/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# OpenLocate Beacon
2+
3+
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.
4+
5+
## Usage
6+
7+
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.
8+
9+
## Features
10+
11+
Advertises packets with the OpenLocate Beacon geolocation element when a GPS fix is available, and packets with the name "Bangle.js" otherwise.
12+
13+
## Requests
14+
15+
[Contact reelyActive](https://www.reelyactive.com/contact/) for support/updates.
16+
17+
## Creator
18+
19+
Developed by [jeffyactive](https://github.com/jeffyactive) of [reelyActive](https://www.reelyactive.com)

apps/openlocatebeacon/metadata.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"id": "openlocatebeacon",
3+
"name": "OpenLocate Beacon",
4+
"shortName": "OpenLocate Beacon",
5+
"version": "0.04",
6+
"description": "Advertise GPS geolocation data using the OpenLocate Beacon packet specification.",
7+
"icon": "openlocatebeacon.png",
8+
"screenshots": [],
9+
"type": "app",
10+
"tags": "tool,sensors,bluetooth",
11+
"supports" : [ "BANGLEJS2" ],
12+
"allow_emulator": true,
13+
"readme": "README.md",
14+
"storage": [
15+
{ "name": "openlocatebeacon.app.js", "url": "openlocatebeacon.js" },
16+
{ "name": "openlocatebeacon.img", "url": "openlocatebeacon-icon.js",
17+
"evaluate": true }
18+
]
19+
}

apps/openlocatebeacon/openlocatebeacon-icon.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* Copyright reelyActive 2024
3+
* We believe in an open Internet of Things
4+
*/
5+
6+
7+
// Non-user-configurable constants
8+
const APP_ID = 'openlocatebeacon';
9+
const ADVERTISING_OPTIONS = { showName: false, interval: 5000 };
10+
11+
12+
// Global variables
13+
let bar, gps;
14+
let sequenceNumber = 0;
15+
16+
17+
// Menus
18+
let mainMenu = {
19+
"": { "title": "OpenLocateBcn" },
20+
"Lat": { value: null },
21+
"Lon": { value: null },
22+
"Altitude": { value: null },
23+
"Satellites": { value: null }
24+
};
25+
26+
27+
// Encode the OpenLocate geo location element advertising packet
28+
function encodeGeoLocationElement() {
29+
let lci = new Uint8Array(16);
30+
let seqFrag = ((sequenceNumber++ & 0x0f) << 4) + 0x01;
31+
let rfc6225lat = toRfc6225Coordinate(gps.lat);
32+
let rfc6225lon = toRfc6225Coordinate(gps.lon);
33+
let rfc6225alt = toRfc6225Altitude(bar.altitude);
34+
lci[0] = rfc6225lat.integer >> 7;
35+
lci[1] = ((rfc6225lat.integer & 0xff) << 1) + (rfc6225lat.fraction >> 24);
36+
lci[2] = (rfc6225lat.fraction >> 16) & 0xff;
37+
lci[3] = (rfc6225lat.fraction >> 8) & 0xff;
38+
lci[4] = rfc6225lat.fraction & 0xff;
39+
lci[5] = rfc6225lon.integer >> 7;
40+
lci[6] = ((rfc6225lon.integer & 0xff) << 1) + (rfc6225lon.fraction >> 24);
41+
lci[7] = (rfc6225lon.fraction >> 16) & 0xff;
42+
lci[8] = (rfc6225lon.fraction >> 8) & 0xff;
43+
lci[9] = rfc6225lon.fraction & 0xff;
44+
lci[10] = bar.altitude ? 0x10 : 0x00;
45+
lci[11] = (rfc6225alt.integer >> 16) & 0xff;
46+
lci[12] = (rfc6225alt.integer >> 8) & 0xff;
47+
lci[13] = rfc6225alt.integer & 0xff;
48+
lci[14] = rfc6225alt.fraction & 0xff;
49+
lci[15] = 0x41;
50+
51+
return [
52+
0x02, 0x01, 0x06, // Flags
53+
0x16, 0x16, 0x94, 0xfd, 0x09, seqFrag, 0x30, lci[0], lci[1], lci[2],
54+
lci[3], lci[4], lci[5], lci[6], lci[7], lci[8], lci[9], lci[10], lci[11],
55+
lci[12], lci[13], lci[14], lci[15]
56+
];
57+
}
58+
59+
60+
// Convert a latitude or longitude coordinate to RFC6225
61+
function toRfc6225Coordinate(coordinate) {
62+
let integer = Math.floor(coordinate);
63+
let fraction = Math.round((coordinate - integer) * 0x1ffffff);
64+
65+
if(integer < 0) {
66+
integer += 0x1ff + 1;
67+
}
68+
69+
return { integer: integer, fraction: fraction };
70+
}
71+
72+
73+
// Convert altitude to RFC6225
74+
function toRfc6225Altitude(altitude) {
75+
if(!altitude) {
76+
return { integer: 0, fraction: 0 };
77+
}
78+
79+
let integer = Math.floor(altitude);
80+
let fraction = Math.round((altitude - integer) * 0xff);
81+
82+
if(integer < 0) {
83+
integer += 0x3fffff + 1;
84+
}
85+
86+
return { integer: integer, fraction: fraction };
87+
}
88+
89+
90+
// Update barometer
91+
Bangle.on('pressure', (newBar) => {
92+
bar = newBar;
93+
94+
mainMenu.Altitude.value = bar.altitude.toFixed(1) + 'm';
95+
E.showMenu(mainMenu);
96+
});
97+
98+
99+
// Update GPS
100+
Bangle.on('GPS', (newGps) => {
101+
gps = newGps;
102+
103+
mainMenu.Lat.value = gps.lat.toFixed(4);
104+
mainMenu.Lon.value = gps.lon.toFixed(4);
105+
mainMenu.Satellites.value = gps.satellites;
106+
E.showMenu(mainMenu);
107+
108+
if(!isNaN(gps.lat) && !isNaN(gps.lon)) {
109+
NRF.setAdvertising(encodeGeoLocationElement(), ADVERTISING_OPTIONS);
110+
}
111+
else {
112+
NRF.setAdvertising({}, { name: "Bangle.js" });
113+
}
114+
});
115+
116+
117+
// On start: enable sensors and display main menu
118+
g.clear();
119+
Bangle.setGPSPower(true, APP_ID);
120+
Bangle.setBarometerPower(true, APP_ID);
121+
E.showMenu(mainMenu);
1.93 KB
Loading

0 commit comments

Comments
 (0)