parent
b711b50f88
commit
bfe2725483
|
|
@ -1,4 +1,4 @@
|
|||
# Banglexercise
|
||||
# BanglExercise
|
||||
|
||||
Can automatically track exercises while wearing the Bangle.js watch.
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue