diff --git a/apps.json b/apps.json index 41151683d..1b5f8a868 100644 --- a/apps.json +++ b/apps.json @@ -2536,5 +2536,19 @@ {"name":"edisonsball.app.js","url":"app.js"}, {"name":"edisonsball.img","url":"app-icon.js","evaluate":true} ] +}, +{ "id": "hrrawexp", + "name": "HRM Data Exporter", + "shortName":"HRM Data Exporter", + "icon": "app-icon.png", + "version":"0.01", + "description": "export raw hrm signal data to a csv file", + "tags": "", + "readme": "README.md", + "interface": "interface.html", + "storage": [ + {"name":"hrrawexp.app.js","url":"app.js"}, + {"name":"hrrawexp.img","url":"app-icon.js","evaluate":true} + ] } ] diff --git a/apps/hrrawexp/README.md b/apps/hrrawexp/README.md new file mode 100644 index 000000000..abf2d3d7c --- /dev/null +++ b/apps/hrrawexp/README.md @@ -0,0 +1,13 @@ +Extract hrm raw signal data to CSV file +======================================= + +Simple app that will run the heart rate monitor for a defined period of time you set at the start. + +-The app creates a csv file (it's actually just 1 column) and you can download this via My Apps in the App Loader. + +-The max time value is 60 minutes. + +-The first item holds the data/time when the readings were taken and the file is reset each time the app is run. + +-The hrm sensor is sampled @50Hz and this app does not do any processing on it other than clip overly high/extreme values, the array is written as-is. There is an example Python script that can process this signal, smooth it and also extract a myriad of heart rate variability metrics using the hrvanalysis library: +https://github.com/jabituyaben/BangleJS-HRM-Signal-Processing diff --git a/apps/hrrawexp/app-icon.js b/apps/hrrawexp/app-icon.js new file mode 100644 index 000000000..01718675e --- /dev/null +++ b/apps/hrrawexp/app-icon.js @@ -0,0 +1 @@ +E.toArrayBuffer(atob("MDCBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAP4AAD/8A//AAH//D//gAP//n//gAf/////AA/////+AA/////8MA/////4cB/////w+B/////B+B////+D+B////8H+B////4P+B////w/+B/+P/h/+B/+H/D/+A/+D+H/8A//h8P/8A//w4f/8Af/4A//4Af/8B//4AP/+D//wAP//H//wAH/////gAD/////AAB////+AAA////+AAAf///8AAAP///wAAAH///gAAAD///AAAAA//+AAAAAf/4AAAAAH/gAAAAAB+AAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")) diff --git a/apps/hrrawexp/app-icon.png b/apps/hrrawexp/app-icon.png new file mode 100644 index 000000000..b120cdb42 Binary files /dev/null and b/apps/hrrawexp/app-icon.png differ diff --git a/apps/hrrawexp/app.js b/apps/hrrawexp/app.js new file mode 100644 index 000000000..f78d3fbf6 --- /dev/null +++ b/apps/hrrawexp/app.js @@ -0,0 +1,97 @@ +var counter = 1; +var logging_started; +var interval; +var value; + +var file = require("Storage").open("hrm_log.csv", "w"); +file.write(""); + +file = require("Storage").open("hrm_log.csv", "a"); + +function update_timer() { + g.clear(); + g.setColor("#00ff7f"); + g.setFont("6x8", 4); + g.setFontAlign(0, 0); // center font + + g.drawString(counter, 120, 120); + g.setFont("6x8", 2); + g.setFontAlign(-1, -1); + g.drawString("-", 220, 200); + g.drawString("+", 220, 40); + g.drawString("GO", 210, 120); + + g.setColor("#ffffff"); + g.setFontAlign(0, 0); // center font + g.drawString("Timer (minutes)", 120, 90); + + g.setFont("6x8", 4); // bitmap font, 8x magnified + + if (!logging_started) + g.flip(); +} + +function btn1Pressed() { + if (!logging_started) { + if (counter < 60) + counter += 1; + else + counter = 1; + update_timer(); + } +} + +function btn3Pressed() { + if (!logging_started) { + if (counter > 1) + counter -= 1; + else + counter = 60; + update_timer(); + } +} + +function btn2Pressed() { + launchtime = 0 | getTime(); + file.write(launchtime + "," + "\n"); + logging_started = true; + counter = counter * 60; + interval = setInterval(countDown, 1000); + Bangle.setHRMPower(1); +} + +function fmtMSS(e) { + var m = Math.floor(e % 3600 / 60).toString().padStart(2, '0'), + s = Math.floor(e % 60).toString().padStart(2, '0'); + return m + ':' + s; +} + +function countDown() { + g.clear(); + counter--; + if (counter == 0) { + Bangle.setHRMPower(0); + clearInterval(interval); + g.drawString("Finished", g.getWidth() / 2, g.getHeight() / 2); + Bangle.buzz(500, 1); + } + else + g.drawString(fmtMSS(counter), g.getWidth() / 2, g.getHeight() / 2); +} + +update_timer(); + +setWatch(btn1Pressed, BTN1, { repeat: true }); +setWatch(btn2Pressed, BTN2, { repeat: true }); +setWatch(btn3Pressed, BTN3, { repeat: true }); + +Bangle.on('HRM', function (hrm) { + for (let i = 0; i < hrm.raw.length; i++) { + value = hrm.raw[i]; + if (value < -2) + value = -2; + if (value > 6) + value = 6; + file.write(value + "," + "\n"); + } +}); diff --git a/apps/hrrawexp/interface.html b/apps/hrrawexp/interface.html new file mode 100644 index 000000000..703341415 --- /dev/null +++ b/apps/hrrawexp/interface.html @@ -0,0 +1,54 @@ + +
+ + + + + + + + + + +