2024-04-16 18:38:33 +00:00
|
|
|
let isMeasuring = false;
|
|
|
|
let currentHR = null;
|
|
|
|
let lcdTimeout;
|
|
|
|
let logData = [];
|
|
|
|
let bpmValues = [];
|
|
|
|
let lastLogTime = 0;
|
|
|
|
|
2024-04-06 02:57:04 +00:00
|
|
|
function startMeasure() {
|
2024-04-17 16:08:50 +00:00
|
|
|
logData = [];
|
2024-04-06 02:57:04 +00:00
|
|
|
isMeasuring = true;
|
|
|
|
Bangle.setLCDTimeout(0);
|
|
|
|
lcdTimeout = setTimeout(() => {
|
|
|
|
Bangle.setLCDTimeout(50);
|
|
|
|
}, 50000);
|
|
|
|
|
|
|
|
setTimeout(() => {
|
2024-04-16 18:38:33 +00:00
|
|
|
Bangle.setHRMPower(1); // starts HRM
|
2024-04-06 02:57:04 +00:00
|
|
|
Bangle.on('HRM', handleHeartRate);
|
2024-04-16 18:38:33 +00:00
|
|
|
Bangle.buzz(200, 10); // Buzz to indicate measurement start
|
2024-04-06 02:57:04 +00:00
|
|
|
drawScreen();
|
|
|
|
}, 500);
|
|
|
|
}
|
|
|
|
|
|
|
|
function stopMeasure() {
|
|
|
|
isMeasuring = false;
|
|
|
|
clearTimeout(lcdTimeout);
|
|
|
|
Bangle.setLCDTimeout(10);
|
|
|
|
Bangle.setHRMPower(0);
|
2024-04-16 18:38:33 +00:00
|
|
|
Bangle.removeAllListeners('HRM'); //stop HRM
|
|
|
|
saveDataToCSV(); // Save data to CSV when measurement stops
|
|
|
|
Bangle.buzz(200, 10); // Buzz to indicate measurement stop
|
2024-04-06 02:57:04 +00:00
|
|
|
drawScreen();
|
|
|
|
}
|
|
|
|
|
2024-04-16 18:38:33 +00:00
|
|
|
function handleHeartRate(hrm) {
|
2024-04-17 16:08:50 +00:00
|
|
|
if (isMeasuring && hrm.confidence > 85) {
|
2024-04-16 18:38:33 +00:00
|
|
|
let currentTime = Date.now();
|
|
|
|
let elaspedTime = currentTime - lastLogTime;
|
2024-04-16 22:15:54 +00:00
|
|
|
let nextLog = 3000 - (elaspedTime % 3000); // Calculate time to next log (3 seconds)
|
|
|
|
if (elaspedTime >= 3000) { // Check if it's time for the next log
|
|
|
|
lastLogTime = currentTime - (elaspedTime % 3000); // Set last log time to the previous 3-second boundary
|
2024-04-16 18:38:33 +00:00
|
|
|
let date = new Date(lastLogTime);
|
|
|
|
let dateStr = require("locale").date(date);
|
|
|
|
let timeStr = require("locale").time(date, 1);
|
|
|
|
let seconds = date.getSeconds();
|
|
|
|
let timestamp = `${dateStr} ${timeStr}:${seconds}`; // Concatenate date, time, and seconds
|
2024-04-17 16:08:50 +00:00
|
|
|
currentHR = hrm.bpm;
|
|
|
|
|
2024-04-16 18:38:33 +00:00
|
|
|
logData.push({ timestamp: timestamp, heartRate: currentHR });
|
|
|
|
bpmValues.push(currentHR); // Store heart rate for HRV calculation
|
|
|
|
if (bpmValues.length > 30) bpmValues.shift(); // Keep last 30 heart rate values
|
|
|
|
// Calculate and add SDNN (standard deviation of NN intervals) to the last log entry
|
|
|
|
logData[logData.length - 1].hrv = calcSDNN();
|
|
|
|
drawScreen();
|
|
|
|
}
|
|
|
|
// Schedule next measurement
|
|
|
|
setTimeout(() => {
|
|
|
|
handleHeartRate(hrm);
|
|
|
|
}, nextLog);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function calcSDNN() {
|
|
|
|
if (bpmValues.length < 5) return 0; // No calculation if insufficient data
|
|
|
|
|
|
|
|
// Calculate differences between adjacent heart rate values
|
|
|
|
const differences = [];
|
|
|
|
for (let i = 1; i < bpmValues.length; i++) {
|
|
|
|
differences.push(Math.abs(bpmValues[i] - bpmValues[i - 1]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate mean difference
|
|
|
|
const meanDifference = differences.reduce((acc, val) => acc + val, 0) / differences.length;
|
|
|
|
|
|
|
|
// Calculate squared differences from mean difference
|
|
|
|
const squaredDifferences = differences.map(diff => Math.pow(diff - meanDifference, 2));
|
|
|
|
|
|
|
|
// Calculate mean squared difference
|
|
|
|
const meanSquaredDifference = squaredDifferences.reduce((acc, val) => acc + val, 0) / squaredDifferences.length;
|
|
|
|
|
|
|
|
// Calculate SDNN (standard deviation of NN intervals)
|
|
|
|
const sdnn = Math.sqrt(meanSquaredDifference);
|
|
|
|
|
|
|
|
return sdnn;
|
|
|
|
}
|
|
|
|
|
2024-04-06 02:57:04 +00:00
|
|
|
function drawScreen(message) {
|
|
|
|
g.clear(); // Clear the display
|
|
|
|
|
|
|
|
// Set the background color
|
2024-04-16 20:48:04 +00:00
|
|
|
g.setColor('#95E7FF');
|
2024-04-06 02:57:04 +00:00
|
|
|
|
|
|
|
// Fill the entire display with the background color
|
|
|
|
g.fillRect(0, 0, g.getWidth(), g.getHeight());
|
|
|
|
|
|
|
|
// Set font and alignment for drawing text
|
|
|
|
g.setFontAlign(0, 0);
|
2024-04-16 20:48:04 +00:00
|
|
|
g.setFont('Vector', 15);
|
2024-04-06 02:57:04 +00:00
|
|
|
|
|
|
|
// Draw the title
|
|
|
|
g.setColor('#000000'); // Set text color to black
|
|
|
|
g.drawString('Heart Rate Monitor', g.getWidth() / 2, 10);
|
|
|
|
|
|
|
|
if (isMeasuring) {
|
|
|
|
// Draw measuring status
|
|
|
|
g.setFont('6x8', 2);
|
|
|
|
g.drawString('Measuring...', g.getWidth() / 2, g.getHeight() / 2 - 10);
|
|
|
|
|
2024-04-16 18:38:33 +00:00
|
|
|
// Draw current heart rate if available
|
|
|
|
g.setFont('6x8', 4);
|
|
|
|
if (currentHR !== null) {
|
|
|
|
g.drawString(currentHR.toString(), g.getWidth() / 2, g.getHeight() / 2 + 20);
|
2024-04-06 02:57:04 +00:00
|
|
|
g.setFont('6x8', 1.6);
|
|
|
|
g.drawString(' BPM', g.getWidth() / 2 + 42, g.getHeight() / 2 + 20);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw instructions
|
|
|
|
g.setFont('6x8', 1.5);
|
|
|
|
g.drawString('Press button to stop', g.getWidth() / 2, g.getHeight() / 2 + 42);
|
|
|
|
} else {
|
|
|
|
// Draw last heart rate
|
2024-04-16 18:38:33 +00:00
|
|
|
if (currentHR !== null && currentHR > 0) {
|
2024-04-17 16:08:50 +00:00
|
|
|
g.setFont('Vector', 12);
|
|
|
|
g.drawString('Last Heart Rate:', g.getWidth() / 2, g.getHeight() / 2 - 20);
|
2024-04-06 02:57:04 +00:00
|
|
|
g.setFont('6x8', 4);
|
2024-04-16 18:38:33 +00:00
|
|
|
g.drawString(currentHR.toString(), g.getWidth() / 2, g.getHeight() / 2 + 10);
|
2024-04-16 03:08:51 +00:00
|
|
|
g.setFont('6x8', 1.6);
|
|
|
|
g.drawString(' BPM', g.getWidth() / 2 + 42, g.getHeight() / 2 + 12);
|
2024-04-06 02:57:04 +00:00
|
|
|
} else {
|
|
|
|
g.setFont('6x8', 2);
|
2024-04-17 16:44:47 +00:00
|
|
|
g.drawString('No data', g.getWidth() / 2, g.getHeight() / 2 + 5);
|
2024-04-06 02:57:04 +00:00
|
|
|
g.setFont('6x8', 1);
|
2024-04-16 18:38:33 +00:00
|
|
|
g.drawString(message || 'Press button to start', g.getWidth() / 2, g.getHeight() / 2 + 30);
|
2024-04-06 02:57:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the display
|
|
|
|
g.flip();
|
|
|
|
}
|
|
|
|
|
|
|
|
function saveDataToCSV() {
|
2024-04-17 16:44:47 +00:00
|
|
|
let fileName = "heart_rate_data.csv";
|
|
|
|
let file = require("Storage").open(fileName, "a"); // Open the file for appending
|
|
|
|
|
|
|
|
// Check if the file is empty (i.e., newly created)
|
|
|
|
if (file.getLength() === 0) {
|
|
|
|
// Write the header if the file is empty
|
|
|
|
file.write("Timestamp,Heart Rate(bpm),HRV(ms)\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append the data
|
2024-04-16 18:38:33 +00:00
|
|
|
logData.forEach(entry => {
|
2024-04-17 16:44:47 +00:00
|
|
|
file.write(`${entry.timestamp},${entry.heartRate},${entry.hrv}\n`);
|
2024-04-06 02:57:04 +00:00
|
|
|
});
|
2024-04-16 20:48:04 +00:00
|
|
|
|
2024-04-06 02:57:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-16 18:38:33 +00:00
|
|
|
setWatch(function() {
|
2024-04-06 02:57:04 +00:00
|
|
|
if (!isMeasuring) {
|
|
|
|
startMeasure();
|
|
|
|
} else {
|
|
|
|
stopMeasure();
|
|
|
|
}
|
|
|
|
}, BTN1, { repeat: true, edge: 'rising' });
|
|
|
|
|
2024-04-17 14:57:45 +00:00
|
|
|
drawScreen();
|