commit
57f2bd7f0c
14
apps.json
14
apps.json
|
|
@ -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}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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=="))
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 826 B |
|
|
@ -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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -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>
|
||||||
Loading…
Reference in New Issue