diff --git a/apps/phystrax/app-icon.js b/apps/phystrax/app-icon.js new file mode 100644 index 000000000..a535bf5e3 --- /dev/null +++ b/apps/phystrax/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwZC/AH4ABgVJkmAA4cEyVJCIwIBkmSAwUBAoIIBCAgaCBYNIA4IFCFgwIDF4Q7CBAWQFg4CBkESCIg+DhIRFAQ9ACKYOLMRWf5IRQ/IRP/4RlzwRKyf5k4RC/xcFCISPBKwMn+YRB/4RFUImf/4RCEwIRIa4P/AAPz/YRDHwLXEgP//1P/+T/8vJQIZCCIkAn/yCIOSv8/2YQCCIOQCIY+CCIYmB8g1CCIkECIM8CII4CLIeACIcAMQd//mSvYRDCAkAiQRFWYcgCIsCCJIQFbQl/8jCGAAq2ByVPCIiwCAAq2BCILCECA6SEAQaMEAAqSCRhIAFCIoQKSQiMHQBJ6IQBB6IQBAQNNwRoLAH4AoA=")) \ No newline at end of file diff --git a/apps/phystrax/app.js b/apps/phystrax/app.js new file mode 100644 index 000000000..663652c14 --- /dev/null +++ b/apps/phystrax/app.js @@ -0,0 +1,114 @@ +let isMeasuring = false; +let currentHeartRate = null; +let lcdTimeout; +let logData = []; + +function startMeasure() { + isMeasuring = true; + Bangle.setLCDTimeout(0); + lcdTimeout = setTimeout(() => { + Bangle.setLCDTimeout(50); + }, 50000); + + setTimeout(() => { + Bangle.setHRMPower(1); + Bangle.on('HRM', handleHeartRate); + Bangle.beep(400, 1000); // Buzz to indicate measurement start + drawScreen(); + }, 500); +} + +function stopMeasure() { + isMeasuring = false; + clearTimeout(lcdTimeout); + Bangle.setLCDTimeout(10); + Bangle.setHRMPower(0); + Bangle.removeAllListeners('HRM'); + saveDataToCSV(); // Save data to CSV when measurement stops + Bangle.beep(400, 800); // Buzz to indicate measurement stop + drawScreen(); +} + +function handleHeartRate(hrm) { + if (hrm.confidence > 90) { + currentHeartRate = hrm.bpm; + let date = new Date(); + 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 + logData.push({ timestamp: timestamp, heartRate: currentHeartRate }); + drawScreen(); + } +} + + +function drawScreen(message) { + g.clear(); // Clear the display + + // Set the background color + g.setColor('#95E7FF'); + + // 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); + g.setFont('Vector', 15); + + // 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); + + // Draw current heart rate if available + g.setFont('6x8', 4); + if (currentHeartRate !== null) { + g.drawString(currentHeartRate.toString(), g.getWidth() / 2, g.getHeight() / 2 + 20); + 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 + g.setFont('Vector', 12); + g.drawString('Last Heart Rate:', g.getWidth() / 2, g.getHeight() / 2 - 20); + if (currentHeartRate !== null && currentHeartRate > 0) { + g.setFont('6x8', 4); + g.drawString(currentHeartRate.toString(), g.getWidth() / 2, g.getHeight() / 2 + 10); + } else { + g.setFont('6x8', 2); + g.drawString('No data', g.getWidth() / 2, g.getHeight() / 2 + 10); + g.setFont('6x8', 1); + g.drawString(message || 'Press button to start', g.getWidth() / 2, g.getHeight() / 2 + 30); + } + } + + // Update the display + g.flip(); +} + +function saveDataToCSV() { + let csvContent = "Timestamp,Heart Rate\n"; + logData.forEach(entry => { + csvContent += `${entry.timestamp},${entry.heartRate}\n`; + }); + require("Storage").write("heart_rate_data.csv", csvContent); +} + +setWatch(function() { + if (!isMeasuring) { + startMeasure(); + } else { + stopMeasure(); + } +}, BTN1, { repeat: true, edge: 'rising' }); + +drawScreen(); diff --git a/apps/phystrax/app.png b/apps/phystrax/app.png new file mode 100644 index 000000000..b9c10ca46 Binary files /dev/null and b/apps/phystrax/app.png differ diff --git a/apps/phystrax/interface.html b/apps/phystrax/interface.html new file mode 100644 index 000000000..d2d3a89cc --- /dev/null +++ b/apps/phystrax/interface.html @@ -0,0 +1,64 @@ + +
+ + + + + + + + + + + diff --git a/apps/phystrax/metadata.json b/apps/phystrax/metadata.json new file mode 100644 index 000000000..e4198919b --- /dev/null +++ b/apps/phystrax/metadata.json @@ -0,0 +1,14 @@ +{ "id": "phystrax", + "name": "PhysTrax", + "shortName":"PhysTrax", + "icon": "app.png", + "version":"0.01", + "description": "Tracking physiological measurements to support active learning in classrooms", + "tags": "health", + "interface": "interface.html", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"phystrax.app.js","url":"app.js"}, + {"name":"phystrax.img","url":"app-icon.js","evaluate":true} + ] +} \ No newline at end of file