mirror of https://github.com/espruino/BangleApps
Delete apps/phystrax directory
parent
b7e40d446f
commit
6d5bcd72a0
|
@ -1,8 +0,0 @@
|
|||
# CapstoneWearable
|
||||
Research has shown that active learning approaches in classrooms, which emphasize the construction of knowledge and reflection on the learning process, significantly enhance the student learning experience and lead to better success in the classroom. The Active Learning Initiative at the University of Georgia focuses on instructor development, student engagement, and classroom enhancement.
|
||||
|
||||
This project focuses specifically on classroom enhancement to create a device that will enhance the learning experience of students in physiology classes by allowing them to learn about and analyze their physiological patterns. The project must adhere to the constraints of a one-hundred dollar per watch budget, the ability to be scaled to a classroom of three-hundred twenty students, and provide data with at least 80% accuracy. By giving students access to their heart rate (HR) and heart rate variability (HRV) data, the goal is that students can better understand the function of the heart as well as the physiological conditions that can be affected by the heart. The original concept was to develop a wrist-wearable, screen-less device featuring sensors that detect a user’s heart rate by emitting LEDs that detect the expansion of blood vessels in the wrist and the quantity of light transmitted in the vascular bed to obtain heart rate and blood oxygen data and the heart rate variability calculated using an algorithm that takes the root-mean-square of interbeat intervals between successive heartbeats.
|
||||
|
||||
After prototyping this custom device and testing its efficacy, the accuracy of the data produced by the sensor was not within the accuracy criteria, and the decision was made to pivot to an existing on-market device coupled with custom software to meet the needs of the client. The analysis software created for the device allows the students to analyze their physiological data and permits for their instructor to access their data anonymously. The data, coupled with survey data entered by the students daily, provides insight into the effect of variables such as sleep, exercise, and caffeine intake to analyze the effects that these have on their physiological measurements. To evaluate the device's efficacy in achieving the goal of 80% accuracy, the wrist wearable device was tested on several different skin tones doing various activities and measuring data over varying intervals and then compared against the data of Apple Watches.
|
||||
|
||||
This project resulted in the creation of a custom data analysis software for the use of students on a Bangle.js smartwatch device that can be continually developed in the future to reach the goal of an inexpensive, easy-to-use device for students to use in active learning classrooms to enhance their understanding of physiology by analyzing their own physiological data in class.
|
|
@ -1 +0,0 @@
|
|||
require("heatshrink").decompress(atob("mUywZC/AB9JkmQA4mSpMgCAsCCIILBCIoIBoARIEwYRDAQI1FBYdIgESBAoRChIIEAQIsFIIcBCIwCIwEACJ5fCBAkgChRTGMQw4FQApQLBYtAggRJpA+MARZKLRLDdHKBUEZY4CGyEJkEQcwOQX5hKBAQI7BBw9BGoUAEYJKBAoIjHUIUggAmCTAKGDFgQOCAAMCgIgBYQIFBBYawCR4IABGoMCOIOQhEEBYaPDXIhxBiQjCOgQCBHYJTDyA1BwAmCOIwpEBwIsBEwNANARcEEAIFCKYJNBAoLsHRgxHCZAxiFBAb+JAALRFI4puDAARlFUIRuGHA+CCIy/DHAwCHCIgyIQAwADTAYRNTA6SGAAo4ICJCkMAAzdBUhIAGL4tICJSGFCJsAhLIHABQRRQwLaFABbaGAFAA=="))
|
|
@ -1,112 +0,0 @@
|
|||
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;
|
||||
var date = new Date();
|
||||
var dateStr = require("locale").date(date);
|
||||
var timeStr = require("locale").time(date, 1);
|
||||
var seconds = ('0' + date.getSeconds()).slice(-2); // Get seconds and pad with leading zero if necessary
|
||||
var 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();
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB |
|
@ -1,65 +0,0 @@
|
|||
<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(`heart_rate_data.csv`, data => {
|
||||
csvData = data.trim();
|
||||
// Remove loading window
|
||||
Util.hideModal();
|
||||
// If no data, report it and exit
|
||||
if (data.length === 0) {
|
||||
dataElement.innerHTML = "<b>There is no data</b>";
|
||||
return;
|
||||
}
|
||||
// Otherwise parse the data and output it as a table
|
||||
dataElement.innerHTML = `<table>
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
<th>Heart Rate</th>
|
||||
</tr>` + data.trim().split("\n").map(l => {
|
||||
l = l.split(",");
|
||||
return `<tr>
|
||||
<td>${l[0]}</td>
|
||||
<td>${l[1]}</td>
|
||||
</tr>`;
|
||||
}).join("\n") + "</table>";
|
||||
});
|
||||
}
|
||||
|
||||
// Call a utility function to save the data
|
||||
document.getElementById("btnSave").addEventListener("click", function() {
|
||||
Util.saveCSV("heart_rate_data", csvData);
|
||||
});
|
||||
|
||||
// Or delete the file
|
||||
document.getElementById("btnDelete").addEventListener("click", function() {
|
||||
Util.showModal("Deleting...");
|
||||
Util.eraseStorageFile("heart_rate_data.csv", function() {
|
||||
Util.hideModal();
|
||||
getData();
|
||||
});
|
||||
});
|
||||
|
||||
// Called when app starts
|
||||
function onInit() {
|
||||
getData();
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,15 +0,0 @@
|
|||
{ "id": "phystrax",
|
||||
"name": "PhysTrax",
|
||||
"shortName":"PhysTrax",
|
||||
"version":"0.01",
|
||||
"description": "Tracking physiological measurements to support active learning in classrooms",
|
||||
"icon": "app.png",
|
||||
"tags": "health",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"interface": "interface.html",
|
||||
"storage": [
|
||||
{"name":"phystrax.app.js","url":"app.js"},
|
||||
{"name":"app-icon.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue