From 7fe88fd02c7b3c3ab383960c59d71140e7e6a7fa Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Thu, 9 Dec 2021 22:26:48 -0500 Subject: [PATCH 01/10] Added temperature and heart rate service advertising --- apps/sensible/sensible.js | 45 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index 45852adab..71e2dabb9 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -7,6 +7,12 @@ // Non-user-configurable constants const APP_ID = 'sensible'; const ESPRUINO_COMPANY_CODE = 0x0590; +const DEFAULT_ADVERTISING_OPTIONS = { + showName: false, + manufacturer: ESPRUINO_COMPANY_CODE, + manufacturerData: JSON.stringify({ name: APP_ID }), + interval: 2000 +}; // Global variables @@ -20,6 +26,12 @@ let isBarEnabled = true; let isGpsEnabled = true; let isHrmEnabled = true; let isMagEnabled = true; +let isNewAccData = false; +let isNewBarData = false; +let isNewGpsData = false; +let isNewHrmData = false; +let isNewMagData = false; + // Menus @@ -104,9 +116,33 @@ function transmitAppName() { } +// Check for new sensor data and update the advertising sequence +function transmitUpdatedSensorData() { + let data = []; + + if(isNewBarData) { + let encT = Math.round(bar.temperature * 100); // TODO: signed int16 + data.push({ 0x2a6e: [ encT & 0xff, (encT >> 8) & 0xff ] }); + isNewBarData = false; + } + + if(isNewHrmData) { + data.push({ 0x2a37: [ 0, hrm.bpm ] }); + isNewHrmData = false; + } + + if(data.length === 0) { + return NRF.setAdvertising({}, DEFAULT_ADVERTISING_OPTIONS); + } + + NRF.setAdvertising(data, { showName: false, interval: 200 }); +} + + // Update acceleration Bangle.on('accel', function(newAcc) { acc = newAcc; + isNewAccData = true; if(isAccMenu) { accMenu.x.value = acc.x.toFixed(2); @@ -119,6 +155,7 @@ Bangle.on('accel', function(newAcc) { // Update barometer Bangle.on('pressure', function(newBar) { bar = newBar; + isNewBarData = true; if(isBarMenu) { barMenu.Altitude.value = bar.altitude.toFixed(1) + 'm'; @@ -131,6 +168,7 @@ Bangle.on('pressure', function(newBar) { // Update GPS Bangle.on('GPS', function(newGps) { gps = newGps; + isNewGpsData = true; if(isGpsMenu) { gpsMenu.Lat.value = gps.lat.toFixed(4); @@ -145,6 +183,7 @@ Bangle.on('GPS', function(newGps) { // Update heart rate monitor Bangle.on('HRM', function(newHrm) { hrm = newHrm; + isNewHrmData = true; if(isHrmMenu) { hrmMenu.BPM.value = hrm.bpm; @@ -156,6 +195,7 @@ Bangle.on('HRM', function(newHrm) { // Update magnetometer Bangle.on('mag', function(newMag) { mag = newMag; + isNewMagData = true; if(isMagMenu) { magMenu.x.value = mag.x; @@ -169,9 +209,10 @@ Bangle.on('mag', function(newMag) { // On start: enable sensors and display main menu g.clear(); -transmitAppName(); +NRF.setAdvertising({}, DEFAULT_ADVERTISING_OPTIONS); Bangle.setBarometerPower(isBarEnabled, APP_ID); Bangle.setGPSPower(isGpsEnabled, APP_ID); Bangle.setHRMPower(isHrmEnabled, APP_ID); Bangle.setCompassPower(isMagEnabled, APP_ID); -E.showMenu(mainMenu); \ No newline at end of file +E.showMenu(mainMenu); +setInterval(transmitUpdatedSensorData, 1000); \ No newline at end of file From 57f6b2a8bb7574541219855a6a575cd635538eb0 Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Fri, 10 Dec 2021 11:36:32 -0500 Subject: [PATCH 02/10] Removed superfluous function, moved temperature encoding to function --- apps/sensible/sensible.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index 71e2dabb9..e604ccf1a 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -103,26 +103,12 @@ let magMenu = { }; -// Transmit the app name under the Espruino company code to facilitate discovery -function transmitAppName() { - let options = { - showName: false, - manufacturer: ESPRUINO_COMPANY_CODE, - manufacturerData: JSON.stringify({ name: APP_ID }), - interval: 2000 - } - - NRF.setAdvertising({}, options); -} - - // Check for new sensor data and update the advertising sequence function transmitUpdatedSensorData() { let data = []; if(isNewBarData) { - let encT = Math.round(bar.temperature * 100); // TODO: signed int16 - data.push({ 0x2a6e: [ encT & 0xff, (encT >> 8) & 0xff ] }); + data.push({ 0x2a6e: encodeTemperature(bar.temperature) }); isNewBarData = false; } @@ -139,6 +125,14 @@ function transmitUpdatedSensorData() { } +// Convert temperature to signed 16-bit integer byte array +// TODO: implement negative temperature as signed int +function encodeTemperature(temperature) { + return [ Math.round(bar.temperature * 100) & 0xff, + (Math.round(bar.temperature * 100) >> 8) & 0xff ]; +} + + // Update acceleration Bangle.on('accel', function(newAcc) { acc = newAcc; From 04af6b9a887865c62cb6d8f4c8f3d8cf03221d69 Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Fri, 10 Dec 2021 11:55:50 -0500 Subject: [PATCH 03/10] Always advertise app name among data frames --- apps/sensible/sensible.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index e604ccf1a..91bbeaa02 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -7,12 +7,9 @@ // Non-user-configurable constants const APP_ID = 'sensible'; const ESPRUINO_COMPANY_CODE = 0x0590; -const DEFAULT_ADVERTISING_OPTIONS = { - showName: false, - manufacturer: ESPRUINO_COMPANY_CODE, - manufacturerData: JSON.stringify({ name: APP_ID }), - interval: 2000 -}; +const APP_ADVERTISING_DATA = [ 0x16, 0xff, 0x90, 0x05, 0x7b, 0x22, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x3a, 0x22, 0x73, 0x65, 0x6e, + 0x73, 0x69, 0x62, 0x6c, 0x65, 0x22, 0x7d ]; // Global variables @@ -105,7 +102,7 @@ let magMenu = { // Check for new sensor data and update the advertising sequence function transmitUpdatedSensorData() { - let data = []; + let data = [ APP_ADVERTISING_DATA ]; // Always advertise at least app name if(isNewBarData) { data.push({ 0x2a6e: encodeTemperature(bar.temperature) }); @@ -117,10 +114,6 @@ function transmitUpdatedSensorData() { isNewHrmData = false; } - if(data.length === 0) { - return NRF.setAdvertising({}, DEFAULT_ADVERTISING_OPTIONS); - } - NRF.setAdvertising(data, { showName: false, interval: 200 }); } @@ -203,7 +196,6 @@ Bangle.on('mag', function(newMag) { // On start: enable sensors and display main menu g.clear(); -NRF.setAdvertising({}, DEFAULT_ADVERTISING_OPTIONS); Bangle.setBarometerPower(isBarEnabled, APP_ID); Bangle.setGPSPower(isGpsEnabled, APP_ID); Bangle.setHRMPower(isHrmEnabled, APP_ID); From fe78e5045b012706a19479e73d6ea09555adfa6e Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Fri, 10 Dec 2021 14:59:26 -0500 Subject: [PATCH 04/10] Removed needless spaces --- apps/sensible/sensible.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index 91bbeaa02..e6d790c14 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -7,9 +7,9 @@ // Non-user-configurable constants const APP_ID = 'sensible'; const ESPRUINO_COMPANY_CODE = 0x0590; -const APP_ADVERTISING_DATA = [ 0x16, 0xff, 0x90, 0x05, 0x7b, 0x22, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0x3a, 0x22, 0x73, 0x65, 0x6e, - 0x73, 0x69, 0x62, 0x6c, 0x65, 0x22, 0x7d ]; +const APP_ADVERTISING_DATA = [ 0x16, 0xff, 0x90, 0x05, 0x7b, 0x6e, 0x61, 0x6d, + 0x65, 0x3a, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x62, + 0x6c, 0x65, 0x7d ]; // Global variables From 0fa290be68646223930440b5bbfef71ba581f88c Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Tue, 14 Dec 2021 16:57:59 -0500 Subject: [PATCH 05/10] Encode all barometer service data in a single function --- apps/sensible/sensible.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index e6d790c14..f7644e77a 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -105,7 +105,7 @@ function transmitUpdatedSensorData() { let data = [ APP_ADVERTISING_DATA ]; // Always advertise at least app name if(isNewBarData) { - data.push({ 0x2a6e: encodeTemperature(bar.temperature) }); + data.push(encodeBarServiceData()); isNewBarData = false; } @@ -118,11 +118,12 @@ function transmitUpdatedSensorData() { } -// Convert temperature to signed 16-bit integer byte array -// TODO: implement negative temperature as signed int -function encodeTemperature(temperature) { - return [ Math.round(bar.temperature * 100) & 0xff, - (Math.round(bar.temperature * 100) >> 8) & 0xff ]; +// Encode the bar service data to fit in a Bluetooth PDU +function encodeBarServiceData() { + // TODO: implement negative temperature as signed int + let encodedTemperature = [ Math.round(bar.temperature * 100) & 0xff, + (Math.round(bar.temperature * 100) >> 8) & 0xff ]; + return { 0x2a6e: encodedTemperature }; } From 3f5072b8748833eb4ed553e0f4042257977b59b0 Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Tue, 14 Dec 2021 16:58:32 -0500 Subject: [PATCH 06/10] Corrected length of app name advertising packet --- apps/sensible/sensible.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index f7644e77a..fb24828fe 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -7,7 +7,7 @@ // Non-user-configurable constants const APP_ID = 'sensible'; const ESPRUINO_COMPANY_CODE = 0x0590; -const APP_ADVERTISING_DATA = [ 0x16, 0xff, 0x90, 0x05, 0x7b, 0x6e, 0x61, 0x6d, +const APP_ADVERTISING_DATA = [ 0x12, 0xff, 0x90, 0x05, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x7d ]; From c0ac05488a2880d53f1475090efd023a7b28f081 Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Tue, 14 Dec 2021 17:59:36 -0500 Subject: [PATCH 07/10] Implemented barometer service data --- apps/sensible/sensible.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index fb24828fe..3c0667c4b 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -120,10 +120,28 @@ function transmitUpdatedSensorData() { // Encode the bar service data to fit in a Bluetooth PDU function encodeBarServiceData() { - // TODO: implement negative temperature as signed int - let encodedTemperature = [ Math.round(bar.temperature * 100) & 0xff, - (Math.round(bar.temperature * 100) >> 8) & 0xff ]; - return { 0x2a6e: encodedTemperature }; + let tEncoded = Math.round(bar.temperature * 100); + let pEncoded = Math.round(bar.pressure * 100); + let eEncoded = Math.round(bar.altitude * 100); + + if(bar.temperature < 0) { + tEncoded += 0x10000; + } + if(bar.altitude < 0) { + eEncoded += 0x1000000; + } + + let t = [ tEncoded & 0xff, (tEncoded >> 8) & 0xff ]; + let p = [ pEncoded & 0xff, (pEncoded >> 8) & 0xff, (pEncoded >> 16) & 0xff, + (pEncoded >> 24) & 0xff ]; + let e = [ eEncoded & 0xff, (eEncoded >> 8) & 0xff, (eEncoded >> 16) & 0xff ]; + + return [ + 0x02, 0x01, 0x06, // Flags + 0x05, 0x16, 0x6e, 0x2a, t[0], t[1], // Temperature + 0x07, 0x16, 0x6d, 0x2a, p[0], p[1], p[2], p[3], // Pressure + 0x06, 0x16, 0x6c, 0x2a, e[0], e[1], e[2] // Elevation + ]; } From cf9f724d365d769b7a92b16f4cc82266b1571255 Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Tue, 14 Dec 2021 20:41:16 -0500 Subject: [PATCH 08/10] Added location and speed service to transmit GPS data --- apps/sensible/sensible.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index 3c0667c4b..16534ff29 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -109,6 +109,11 @@ function transmitUpdatedSensorData() { isNewBarData = false; } + if(isNewGpsData && gps.lat && gps.lon) { + data.push(encodeGpsServiceData()); + isNewGpsData = false; + } + if(isNewHrmData) { data.push({ 0x2a37: [ 0, hrm.bpm ] }); isNewHrmData = false; @@ -145,6 +150,35 @@ function encodeBarServiceData() { } +// Encode the GPS service data using the Location and Speed characteristic +function encodeGpsServiceData() { + let latEncoded = Math.round(gps.lat * 10000000); + let lonEncoded = Math.round(gps.lon * 10000000); + let hEncoded = Math.round(gps.course * 100); + let sEncoded = Math.round(1000 * gps.speed / 36); + + if(gps.lat < 0) { + latEncoded += 0x100000000; + } + if(gps.lon < 0) { + lonEncoded += 0x100000000; + } + + let s = [ sEncoded & 0xff, (sEncoded >> 8) & 0xff ]; + let lat = [ latEncoded & 0xff, (latEncoded >> 8) & 0xff, + (latEncoded >> 16) & 0xff, (latEncoded >> 24) & 0xff ]; + let lon = [ lonEncoded & 0xff, (lonEncoded >> 8) & 0xff, + (lonEncoded >> 16) & 0xff, (lonEncoded >> 24) & 0xff ]; + let h = [ hEncoded & 0xff, (hEncoded >> 8) & 0xff ]; + + return [ + 0x02, 0x01, 0x06, // Flags + 0x11, 0x16, 0x67, 0x2a, 0x95, 0x02, s[0], s[1], lat[0], lat[1], lat[2], + lat[3], lon[0], lon[1], lon[2], lon[3], h[0], h[1] // Location and Speed + ]; +} + + // Update acceleration Bangle.on('accel', function(newAcc) { acc = newAcc; From c4d282b2f29dad71327c2fb3db8ea625f599074b Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Tue, 14 Dec 2021 21:09:18 -0500 Subject: [PATCH 09/10] Implemented magnetic flux density 3D service --- apps/sensible/sensible.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index 16534ff29..3da39998e 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -119,6 +119,11 @@ function transmitUpdatedSensorData() { isNewHrmData = false; } + if(isNewMagData) { + data.push(encodeMagServiceData()); + isNewMagData = false; + } + NRF.setAdvertising(data, { showName: false, interval: 200 }); } @@ -179,6 +184,33 @@ function encodeGpsServiceData() { } +// Encode the mag service data using the magnetic flux density 3D characteristic +function encodeMagServiceData() { + let xEncoded = mag.x; // TODO: units??? + let yEncoded = mag.y; + let zEncoded = mag.z; + + if(xEncoded < 0) { + xEncoded += 0x10000; + } + if(yEncoded < 0) { + yEncoded += 0x10000; + } + if(yEncoded < 0) { + yEncoded += 0x10000; + } + + let x = [ xEncoded & 0xff, (xEncoded >> 8) & 0xff ]; + let y = [ yEncoded & 0xff, (yEncoded >> 8) & 0xff ]; + let z = [ zEncoded & 0xff, (zEncoded >> 8) & 0xff ]; + + return [ + 0x02, 0x01, 0x06, // Flags + 0x09, 0x16, 0xa1, 0x2a, x[0], x[1], y[0], y[1], z[0], z[1] // Mag 3D + ]; +} + + // Update acceleration Bangle.on('accel', function(newAcc) { acc = newAcc; From 8d5107fe42db5cc2e3efbcf6aafdb39dfdeedc3e Mon Sep 17 00:00:00 2001 From: jeffyactive Date: Tue, 14 Dec 2021 21:16:02 -0500 Subject: [PATCH 10/10] Version bump to 0.04 --- apps.json | 2 +- apps/sensible/ChangeLog | 1 + apps/sensible/README.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps.json b/apps.json index c1f51f272..682c399c4 100644 --- a/apps.json +++ b/apps.json @@ -4649,7 +4649,7 @@ "id": "sensible", "name": "SensiBLE", "shortName": "SensiBLE", - "version": "0.03", + "version": "0.04", "description": "Collect, display and advertise real-time sensor data.", "icon": "sensible.png", "screenshots": [ diff --git a/apps/sensible/ChangeLog b/apps/sensible/ChangeLog index baa93f297..c50431f51 100644 --- a/apps/sensible/ChangeLog +++ b/apps/sensible/ChangeLog @@ -1,3 +1,4 @@ 0.01: New App! 0.02: Corrected variable initialisation 0.03: Advertise app name, added screenshots +0.04: Advertise bar, GPS, HRM and mag services diff --git a/apps/sensible/README.md b/apps/sensible/README.md index f79b61aea..fcff3b0f9 100644 --- a/apps/sensible/README.md +++ b/apps/sensible/README.md @@ -17,7 +17,7 @@ Currently implements: - Heart Rate Monitor - Magnetometer -in the menu display but NOT YET in Bluetooth Low Energy advertising (which will be implemented in a subsequent version). +in the menu display, and broadcasts all sensor data readings _except_ acceleration in Bluetooth Low Energy advertising packets as GATT characteristic services. ## Controls