Show heart rate during training

Alot of code cleanup and improvements
master
Marco Heiming 2022-01-10 19:02:39 +01:00
parent b711b50f88
commit bfe2725483
2 changed files with 151 additions and 120 deletions

View File

@ -1,4 +1,4 @@
# Banglexercise
# BanglExercise
Can automatically track exercises while wearing the Bangle.js watch.

View File

@ -1,15 +1,9 @@
const Layout = require("Layout");
const heatshrink = require('heatshrink');
let tStart;
let historyY = [];
let historyZ = [];
const avgSize = 10;
const thresholdY = 2500;
const thresholdPushUpTime = 1400; // mininmal time between two push ups
let tStart;
let avgY;
let avgZ;
let historyAvgY = [];
let historyAvgZ = [];
let historySlopeY = [];
@ -17,103 +11,122 @@ let historySlopeZ = [];
let lastZeroPassType;
let lastZeroPassTime = 0;
let lastPushUpCmpltTime = 0;
let exerciseType = "";
let pushUpCounter = 0;
let lastExerciseCmpltTime = 0;
let exerciseType = {
"name": ""
};
const exerciseTypes = [{
"id": "pushup",
"name": "Push ups",
"useYaxe": true,
"useZaxe": false
} // add other exercises here
];
let exerciseCounter = 0;
let layout;
let recordActive = false;
/**
* Thresholds
*/
const avgSize = 6;
const pushUpThresholdY = 2500;
const pushUpThresholdTime = 1400; // mininmal time between two push ups
function showMenu() {
let hrtValue;
function showMainMenu() {
let menu;
if (pushUpCounter == 0) {
menu = {
"": {
title: "Banglexercise"
},
"Start push ups": function() {
exerciseType = "push ups";
E.showMenu();
startRecording();
}
menu = {
"": {
title: "BanglExercise"
}
};
exerciseTypes.forEach(function(et) {
menu["Do " + et.name] = function() {
exerciseType = et;
E.showMenu();
startRecording();
};
} else {
menu = {
"": {
title: "Banglexercise"
},
"Last:": {
value: pushUpCounter + " push ups"
},
"Start push ups": function() {
exerciseType = "push ups";
E.showMenu();
startRecording();
}
});
if (exerciseCounter > 0) {
menu["----"] = {};
menu["Last:"] = {
value: exerciseCounter + " " + exerciseType.name
};
}
E.showMenu(menu);
}
function accelHandler(accel) {
if (!exerciseType) return;
const t = Math.round(new Date().getTime()); // time in ms
const y = accel.y * 8192;
const z = accel.z * 8192;
const y = exerciseType.useYaxe ? accel.y * 8192 : 0;
const z = exerciseType.useZaxe ? accel.z * 8192 : 0;
//console.log(t, y, z);
while (historyY.length > avgSize)
historyY.shift();
historyY.push(y);
if (exerciseType.useYaxe) {
while (historyY.length > avgSize)
historyY.shift();
if (historyY.length > avgSize / 2)
avgY = E.sum(historyY) / historyY.length;
historyY.push(y);
while (historyZ.length > avgSize)
historyZ.shift();
historyZ.push(z);
if (historyZ.length > avgSize / 2)
avgZ = E.sum(historyZ) / historyZ.length;
if (avgY) {
//console.log(avgY, avgZ);
historyAvgY.push([t, avgY]);
historyAvgZ.push([t, avgZ]);
if (historyY.length > avgSize / 2) {
const avgY = E.sum(historyY) / historyY.length;
historyAvgY.push([t, avgY]);
}
}
let mY;
let mZ;
// slope for Y
let l = historyAvgY.length;
if (l > 1) {
const p1 = historyAvgY[l - 2];
const p2 = historyAvgY[l - 1];
mY = (p2[1] - p1[1]) / (p2[0] / 1000 - p1[0] / 1000);
if (Math.abs(mY) >= thresholdY) {
historyAvgY.shift();
historySlopeY.push([t, mY]);
//console.log(t, Math.abs(mY));
if (exerciseType.useYaxe) {
while (historyZ.length > avgSize)
historyZ.shift();
const lMY = historySlopeY.length;
if (lMY > 1) {
const pMY1 = historySlopeY[lMY - 2][1];
const pMY2 = historySlopeY[lMY - 1][1];
isValidPushUp(pMY1, pMY2, t);
historyZ.push(z);
if (historyZ.length > avgSize / 2) {
const avgZ = E.sum(historyZ) / historyZ.length;
historyAvgZ.push([t, avgZ]);
}
}
// slope for Y
if (exerciseType.useYaxe) {
let l = historyAvgY.length;
if (l > 1) {
const p1 = historyAvgY[l - 2];
const p2 = historyAvgY[l - 1];
const mY = (p2[1] - p1[1]) / (p2[0] / 1000 - p1[0] / 1000);
if (Math.abs(mY) >= pushUpThresholdY) {
historyAvgY.shift();
historySlopeY.push([t, mY]);
//console.log(t, Math.abs(mY));
const lMY = historySlopeY.length;
if (lMY > 1) {
const pMY1 = historySlopeY[lMY - 2][1];
const pMY2 = historySlopeY[lMY - 1][1];
isValidPushUp(pMY1, pMY2, t);
}
}
}
}
// slope for Z
l = historyAvgZ.length;
if (l > 1) {
const p1 = historyAvgZ[l - 2];
const p2 = historyAvgZ[l - 1];
mZ = (p2[1] - p1[1]) / (p2[0] - p1[0]);
historyAvgZ.shift();
historySlopeZ.push([p2[0] - p1[0], mZ]);
if (exerciseType.useZaxe) {
l = historyAvgZ.length;
if (l > 1) {
const p1 = historyAvgZ[l - 2];
const p2 = historyAvgZ[l - 1];
const mZ = (p2[1] - p1[1]) / (p2[0] - p1[0]);
historyAvgZ.shift();
historySlopeZ.push([p2[0] - p1[0], mZ]);
}
}
}
@ -123,8 +136,8 @@ function isValidPushUp(p1, p2, t) {
if (lastZeroPassType == "-+") {
console.log(t, "Push up half complete...");
layout.progress.label = "...";
layout.render(layout.progress);
layout.progress.label = "*";
layout.render();
}
lastZeroPassType = "+-";
@ -134,20 +147,19 @@ function isValidPushUp(p1, p2, t) {
if (lastZeroPassType == "+-") {
// potential complete push up. Let's check the time difference...
const tDiffLastPushUp = t - lastPushUpCmpltTime;
const tDiffLastPushUp = t - lastExerciseCmpltTime;
const tDiffStart = t - tStart;
console.log(t, "Push up maybe complete?", Math.round(tDiffLastPushUp), Math.round(tDiffStart));
if ((lastPushUpCmpltTime <= 0 && tDiffStart >= thresholdPushUpTime) || tDiffLastPushUp >= thresholdPushUpTime) {
if ((lastExerciseCmpltTime <= 0 && tDiffStart >= pushUpThresholdTime) || tDiffLastPushUp >= pushUpThresholdTime) {
console.log(t, "Push up complete!!!");
lastPushUpCmpltTime = t;
pushUpCounter++;
lastExerciseCmpltTime = t;
exerciseCounter++;
layout.count.label = pushUpCounter;
layout.render(layout.count);
layout.count.label = exerciseCounter;
layout.progress.label = "";
layout.render(layout.progress);
layout.render();
Bangle.buzz(100, 0.3); // TODO make configurable
} else {
@ -160,19 +172,6 @@ function isValidPushUp(p1, p2, t) {
}
}
/*
function calcPushUps() {
const l = historySlopeY.length;
for (let i = 1; i < l; i++) {
const p1 = historySlopeY[i - 1][1];
const p2 = historySlopeY[i][1];
const t = historySlopeY[i][0];
isValidPushUp(p1, p2, t);
}
}
*/
function reset() {
historyY = [];
historyZ = [];
@ -183,8 +182,9 @@ function reset() {
lastZeroPassType = "";
lastZeroPassTime = 0;
lastPushUpCmpltTime = 0;
pushUpCounter = 0;
lastExerciseCmpltTime = 0;
exerciseCounter = 0;
tStart = 0;
}
@ -192,29 +192,54 @@ function startRecording() {
if (recordActive) return;
g.clear(1);
reset();
Bangle.setHRMPower(1, "banglexercise");
if (!hrtValue) hrtValue = "...";
layout = new Layout({
type: "v",
c: [{
type: "txt",
id: "type",
font: "6x8:2",
label: exerciseType,
label: exerciseType.name,
pad: 5
},
{
type: "txt",
id: "count",
font: "6x8:9",
label: pushUpCounter,
pad: 5,
bgCol: g.theme.bg
type: "h",
c: [{
type: "txt",
id: "count",
font: "6x8:10",
label: exerciseCounter,
pad: 5,
bgCol: g.theme.bg
},
{
type: "txt",
id: "progress",
font: "6x8:2",
label: "",
pad: 5
},
]
},
{
type: "txt",
id: "progress",
font: "6x8:2",
label: "",
pad: 5
type: "h",
c: [{
type: "img",
pad: 4,
src: function() {
return heatshrink.decompress(atob("h0OwYOLkmQhMkgACByVJgESpIFBpEEBAIFBCgIFCCgsABwcAgQOCAAMSpAwDyBNM"));
}
},
{
type: "txt",
id: "hrtRate",
font: "6x8:2",
label: hrtValue,
pad: 5
},
]
},
{
type: "txt",
@ -234,7 +259,8 @@ function startRecording() {
stopRecording();
}
}
]
],
lazy: true
});
layout.render();
@ -248,12 +274,17 @@ function startRecording() {
function stopRecording() {
if (!recordActive) return;
g.clear(1);
Bangle.setHRMPower(0, "banglexercise");
Bangle.removeListener('accel', accelHandler);
showMenu();
console.log("Found " + pushUpCounter + " push ups!");
showMainMenu();
recordActive = false;
}
Bangle.on('HRM', function(hrm) {
hrtValue = hrm.bpm;
});
g.clear(1);
Bangle.drawWidgets();
showMenu();
showMainMenu();