diff --git a/apps.json b/apps.json index 1c614c592..5ccbecd98 100644 --- a/apps.json +++ b/apps.json @@ -4526,5 +4526,22 @@ {"name":"93dub.app.js","url":"app.js"}, {"name":"93dub.img","url":"app-icon.js","evaluate":true} ] + }, + { + "id": "sensible", + "name": "SensiBLE", + "shortName": "SensiBLE", + "version": "0.01", + "description": "Collect, display and advertise real-time sensor data.", + "icon": "sensible.png", + "type": "app", + "tags": "tool,sensors", + "supports" : [ "BANGLEJS2" ], + "allow_emulator": true, + "readme": "README.md", + "storage": [ + { "name": "sensible.app.js", "url": "sensible.js" }, + { "name": "sensible.img", "url": "sensible-icon.js", "evaluate": true } + ] } ] diff --git a/apps/sensible/ChangeLog b/apps/sensible/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/sensible/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/sensible/README.md b/apps/sensible/README.md new file mode 100644 index 000000000..f79b61aea --- /dev/null +++ b/apps/sensible/README.md @@ -0,0 +1,35 @@ +# Sensible + +Collect all the sensor data from the Bangle.js 2, display the live readings in menu pages, and broadcast in Bluetooth Low Energy (BLE) advertising packets 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. Also convenient for testing individual sensors of the Bangle.js 2 via the menu interface. + + +## Features + +Currently implements: +- Accelerometer +- Barometer +- GPS +- Heart Rate Monitor +- Magnetometer + +in the menu display but NOT YET in Bluetooth Low Energy advertising (which will be implemented in a subsequent version). + + +## Controls + +Browse and control sensors using the standard Espruino menu interface. + + +## 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) diff --git a/apps/sensible/sensible-icon.js b/apps/sensible/sensible-icon.js new file mode 100644 index 000000000..f904fc7f3 --- /dev/null +++ b/apps/sensible/sensible-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkG/4AG+cilGIxGCkU/B44AGmQUBAAsjCyoYN+QWJAAMvCxsjLQXzG4gYIOIZwG+YLDCw34BRIkFx4JFHQRDElGCJYgOCFw5RCPQwJFGAg4BIoSRIDAQQEG4YLBHgYAGJQIjCJ4RGBDoU4SIqNDwYwDJAQEDFwSRGDAQfBFQgIDFwQtDRoowBAgQDEDYQzC7oACTogrEA4IfF/4WDDAY/Fx4CCEYQbB/oXF74TDCAYGBUoIDDCwowCUoIkBAYSABGwIDCLogADBIKMCAYRODLwRGGJAaMFPwghBnoXJHoJ8DF4Q5DC5HTKogVBgAAFpoXH6oQGAA1dC7/UC5sNC4/dCA0QAwsEC50BC40AC5FQC4sgMB4XFgUwC40FC4/QBwkD+B5HDA6oFh/xSREFqtVbogMEj/yVxkFMwRgEl//Y5sAqhgF///SA4AHghgDgQXBPBAAHrpICh4XBMBoADC4ReBAALxHABUBCwX/bI4AKgYXD+YXRn4XDSKCNDAAZ5QOoZhSLohhESRkBLopJQIo4YOCxYYCJQ0BCxoACmURCoMRkYOI")) \ No newline at end of file diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js new file mode 100644 index 000000000..eaebd5c2b --- /dev/null +++ b/apps/sensible/sensible.js @@ -0,0 +1,163 @@ +/** + * Copyright reelyActive 2021 + * We believe in an open Internet of Things + */ + + +// Non-user-configurable constants +const APP_ID = 'sensible'; + + +// Global variables +let acc, bar, hrm, mag; +let isAccMenu, isBarMenu, isGpsMenu, isHrmMenu, isMagMenu = false; +let barEnabled, gpsEnabled, hrmEnabled, magEnabled = true; + + +// Menus +let mainMenu = { + "": { "title": "-- SensiBLE --" }, + "Acceleration": function() { E.showMenu(accMenu); isAccMenu = true; }, + "Barometer": function() { E.showMenu(barMenu); isBarMenu = true; }, + "GPS": function() { E.showMenu(gpsMenu); isGpsMenu = true; }, + "Heart Rate": function() { E.showMenu(hrmMenu); isHrmMenu = true; }, + "Magnetometer": function() { E.showMenu(magMenu); isMagMenu = true; } +}; +let accMenu = { + "": { "title" : "- Acceleration -" }, + "State": { value: "On" }, + "x": { value: null }, + "y": { value: null }, + "z": { value: null }, + "<-": function() { E.showMenu(mainMenu); isAccMenu = false; }, +}; +let barMenu = { + "": { "title" : "- Barometer -" }, + "State": { + value: barEnabled, + format: v => v ? "On" : "Off", + onchange: v => { barEnabled = v; Bangle.setBarometerPower(v, APP_ID); } + }, + "Altitude": { value: null }, + "Press": { value: null }, + "Temp": { value: null }, + "<-": function() { E.showMenu(mainMenu); isBarMenu = false; }, +}; +let gpsMenu = { + "": { "title" : "- GPS -" }, + "State": { + value: gpsEnabled, + format: v => v ? "On" : "Off", + onchange: v => { gpsEnabled = v; Bangle.setGPSPower(v, APP_ID); } + }, + "Lat": { value: null }, + "Lon": { value: null }, + "Altitude": { value: null }, + "Satellites": { value: null }, + "HDOP": { value: null }, + "<-": function() { E.showMenu(mainMenu); isGpsMenu = false; }, +}; +let hrmMenu = { + "": { "title" : "- Heart Rate -" }, + "State": { + value: hrmEnabled, + format: v => v ? "On" : "Off", + onchange: v => { hrmEnabled = v; Bangle.setHRMPower(v, APP_ID); } + }, + "BPM": { value: null }, + "Confidence": { value: null }, + "<-": function() { E.showMenu(mainMenu); isHrmMenu = false; }, +}; +let magMenu = { + "": { "title" : "- Magnetometer -" }, + "State": { + value: magEnabled, + format: v => v ? "On" : "Off", + onchange: v => { magEnabled = v; Bangle.setCompassPower(v, APP_ID); } + }, + "x": { value: null }, + "y": { value: null }, + "z": { value: null }, + "Heading": { value: null }, + "<-": function() { E.showMenu(mainMenu); isMagMenu = false; }, +}; + + +// Update acceleration +Bangle.on('accel', function(newAcc) { + acc = newAcc; + + if(isAccMenu) { + accMenu.x.value = acc.x.toFixed(2); + accMenu.y.value = acc.y.toFixed(2); + accMenu.z.value = acc.z.toFixed(2); + E.showMenu(accMenu); + } +}); + +// Update barometer +Bangle.on('pressure', function(newBar) { + bar = newBar; + + if(isBarMenu) { + barMenu.Altitude.value = bar.altitude.toFixed(1) + 'm'; + barMenu.Press.value = bar.pressure.toFixed(1) + 'mbar'; + barMenu.Temp.value = bar.temperature.toFixed(1) + 'C'; + E.showMenu(barMenu); + } +}); + +// Update GPS +Bangle.on('GPS', function(newGps) { + gps = newGps; + + if(isGpsMenu) { + gpsMenu.Lat.value = gps.lat.toFixed(4); + gpsMenu.Lon.value = gps.lon.toFixed(4); + gpsMenu.Altitude.value = gps.alt + 'm'; + gpsMenu.Satellites.value = gps.satellites; + gpsMenu.HDOP.value = (gps.hdop * 5).toFixed(1) + 'm'; + E.showMenu(gpsMenu); + } +}); + +// Update heart rate monitor +Bangle.on('HRM', function(newHrm) { + hrm = newHrm; + + if(isHrmMenu) { + hrmMenu.BPM.value = hrm.bpm; + hrmMenu.Confidence.value = hrm.confidence + '%'; + E.showMenu(hrmMenu); + } +}); + +// Update magnetometer +Bangle.on('mag', function(newMag) { + mag = newMag; + + if(isMagMenu) { + magMenu.x.value = mag.x; + magMenu.y.value = mag.y; + magMenu.z.value = mag.z; + magMenu.Heading.value = mag.heading.toFixed(1); + E.showMenu(magMenu); + } +}); + + +// Special function to handle display switch on +//Bangle.on('lcdPower', (on) => { +// if(on) { +// E.showMenu(mainMenu); +// } +//}); + + +// On start: +g.clear(); +Bangle.setBarometerPower(true, APP_ID); +Bangle.setGPSPower(true, APP_ID); +Bangle.setHRMPower(true, APP_ID); +Bangle.setCompassPower(true, APP_ID); +E.showMenu(mainMenu); \ No newline at end of file diff --git a/apps/sensible/sensible.png b/apps/sensible/sensible.png new file mode 100644 index 000000000..d3e3dfbef Binary files /dev/null and b/apps/sensible/sensible.png differ