Merge pull request #624 from jabituyaben/master

HRM raw data export app
master
Gordon Williams 2021-01-08 08:17:07 +00:00 committed by GitHub
commit 57f2bd7f0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 179 additions and 0 deletions

View File

@ -2536,5 +2536,19 @@
{"name":"edisonsball.app.js","url":"app.js"}, {"name":"edisonsball.app.js","url":"app.js"},
{"name":"edisonsball.img","url":"app-icon.js","evaluate":true} {"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}
]
} }
] ]

13
apps/hrrawexp/README.md Normal file
View File

@ -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

View File

@ -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=="))

BIN
apps/hrrawexp/app-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

97
apps/hrrawexp/app.js Normal file
View File

@ -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");
}
});

View File

@ -0,0 +1,54 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
</head>
<body>
<div id="data"></div>
<button class="btn btn-default" id="btnSave">Save</button>
<button class="btn btn-default" id="btnDelete">Delete</button>
<script src="../../core/lib/interface.js"></script>
<script>
var dataElement = document.getElementById("data");
var csvData = "";
function getData() {
// show loading window
Util.showModal("Loading...");
// get the data
dataElement.innerHTML = "";
Util.readStorageFile(`hrm_log.csv`,data=>{
csvData = data.trim();
// remove window
Util.hideModal();
// If no data, report it and exit
if (data.length==0) {
dataElement.innerHTML = "<b>No data found</b>";
return;
}
else{
dataElement.innerHTML = "<b>data file found</b>";
return;
}
});
}
// You can call a utility function to save the data
document.getElementById("btnSave").addEventListener("click", function() {
Util.saveCSV("HRM_data", csvData);
});
// Or you can also delete the file
document.getElementById("btnDelete").addEventListener("click", function() {
Util.showModal("Deleting...");
Util.eraseStorageFile("hrm_log.csv", function() {
Util.hideModal();
getData();
});
});
// Called when app starts
function onInit() {
getData();
}
</script>
</body>
</html>