Skip to content

Commit

Permalink
pushups : getting ready for release
Browse files Browse the repository at this point in the history
  • Loading branch information
frederic wagner committed Sep 6, 2024
1 parent 509064f commit 47d917c
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 33 deletions.
27 changes: 25 additions & 2 deletions apps/pushups/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
# Pushups

Train for push ups using the accelerometer. It should buzz everytime you go up and down.
Swipe the screen to set the countdown value.
Pushups is an exercising app with a twist : the accelerometer.

![Screenshot](shot_pushups.png)

I initially just wanted a pushups counter but i kind of got out of hand.

The accelerometer will work on the following exercises :

- pushups
- situps
- squats
- jumping jacks

For each of them it will try to detect two positions (for example up and down for pushups)
and buzz on each change. You can set up a target counter for each exercise.

Precision is not 100% but it's good for me and kind of increases my motivation.

Other activities are time based like

- plank
- rest


Define your training routine, set a duration and you're ready to go.


## Creator
Expand Down
121 changes: 90 additions & 31 deletions apps/pushups/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ const IMAGES = [
// number of movements or duration required for each activity
const DEFAULTS = [7, 10, 10, 30, 15, 30];

// detector sensitivity for each activity
// (less is more reactive but more sensitive to noise)
const COUNTS = [6, 10, 6, 6, 6, 5];

function default_config() {
return {duration: 10*60, routine: default_routine()};
}

function default_routine() {
let routine = [];
DEFAULTS.forEach((d, i) => {
Expand Down Expand Up @@ -94,11 +102,8 @@ const DETECTORS = [
];

class FitnessStatus {
constructor(duration) {
this.routine = require("Storage").readJSON("pushups.cfg", true);
if (this.routine === undefined) {
this.routine = default_routine();
}
constructor(config) {
this.routine = config.routine;
this.routine_step = 0;
this.current_status = 0;
this.buzzing = false;
Expand All @@ -108,7 +113,7 @@ class FitnessStatus {
this.remaining = this.routine[this.routine_step][1];
this.activity_start = getTime();
this.starting_time = this.activity_start;
this.duration = duration;
this.duration = config.duration;
this.completed = false;
}

Expand Down Expand Up @@ -202,6 +207,7 @@ class FitnessStatus {
}
let activity = this.routine[this.routine_step][0];
let detector = DETECTORS[activity];
let status = this;
if (detector === null) {
// it's time based
let activity_duration = getTime() - this.activity_start;
Expand All @@ -221,8 +227,7 @@ class FitnessStatus {
if (new_status != this.current_status) {
this.counts_in_opposite_status += 1;

// we consider 6 counts to smooth out noise
if (this.counts_in_opposite_status == 6) {
if (this.counts_in_opposite_status == COUNTS[activity]) {
this.current_status = 1 - this.current_status;
this.counts_in_opposite_status = 0;
if (this.current_status == 0) {
Expand All @@ -243,11 +248,9 @@ class FitnessStatus {
}
}

let status = new FitnessStatus(10 * 60);
// status.display();


function start_routine() {
function start_routine(config) {
let status = new FitnessStatus(config);

Bangle.accelWr(0x18,0b01110100); // off, +-8g // NOTE: this code is taken from 'accelrec' app
Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter
Expand Down Expand Up @@ -285,8 +288,8 @@ function start_routine() {
}


function edit_menu() {
let routine = status.routine;
function edit_menu(config) {
let routine = config.routine;

E.showScroller({
h : 60,
Expand All @@ -308,21 +311,20 @@ function edit_menu() {
select : function(idx) {
if (idx == routine.length + 1) {
E.showScroller();
require("Storage").writeJSON("pushups.cfg", routine);
start_routine();
set_duration(config);
} else if (idx == routine.length) {
E.showScroller();
add_activity();
add_activity(config);
} else {
E.showScroller();
set_counter(idx);
set_counter(config, idx);
}
}
});
}


function add_activity() {
function add_activity(config) {
E.showScroller({
h : 60,
c : IMAGES.length,
Expand All @@ -332,19 +334,19 @@ function add_activity() {
g.drawImage(img, r.x + r.w / 3, r.y + 10);
},
select : function(idx) {
let new_index = status.routine.length;
status.routine.push([idx, 10]);
let new_index = config.routine.length;
config.routine.push([idx, 10]);
E.showScroller();
set_counter(new_index);
set_counter(config, new_index);
}
});
}


function set_counter(index) {
function set_counter(config, index) {
let w = g.getWidth();
let h = g.getHeight();
let counter = status.routine[index][1];
let counter = config.routine[index][1];
function display() {
g.clear();
g.setFont("6x8:2")
Expand Down Expand Up @@ -378,18 +380,69 @@ function set_counter(index) {
});
Bangle.on("touch", function(button, xy) {
if (counter == 0) {
status.routine.splice(index, 1);
config.routine.splice(index, 1);
} else {
status.routine[index][1] = counter;
config.routine[index][1] = counter;
}
Bangle.removeAllListeners("touch");
Bangle.removeAllListeners("swipe");
edit_menu(config);
});

}


//TODO: factorize code with set_counter
function set_duration(config) {
let w = g.getWidth();
let h = g.getHeight();
let duration = config.duration;
let minutes = Math.floor(duration / 60);
function display() {
g.clear();
g.setColor(0);
g.setFont("6x8:2")
.setFontAlign(1, 0)
.drawString("+1", w, h/2);
g.setFontAlign(-1, 0)
.drawString("-1", 0, h/2);
g.setFontAlign(0, -1)
.drawString("+5", w/2, 0);
g.setFontAlign(0, 1)
.drawString("-5", w/2, h);
g.drawString("minutes", w/2, h-40);
g.setFont("Vector:64")
.setFontAlign(0, 0)
.drawString(""+minutes, w/2, h/2);
}
display();
Bangle.on("swipe", function (directionLR, directionUD) {
if (directionUD == -1) {
minutes += 5;
} else if (directionUD == 1) {
minutes -= 5;
} else if (directionLR == -1) {
minutes -= 1;
} else if (directionLR == 1) {
minutes += 1;
}
if (minutes < 1) {
minutes = 1;
}
display();
});
Bangle.on("touch", function(button, xy) {
Bangle.removeAllListeners("touch");
Bangle.removeAllListeners("swipe");
edit_menu();
config.duration = minutes * 60;
//TODO: don't write if no change
require("Storage").writeJSON("pushups.cfg", config);
start_routine(config);
});

}

function main_menu() {
function main_menu(config) {
let w = g.getWidth();
let h = g.getHeight();
g.clear();
Expand All @@ -409,18 +462,24 @@ function main_menu() {
.setFontAlign(0, 0)
.drawString("Edit", w/2, 3*h/4);
Bangle.removeAllListeners("touch");
edit_menu();
edit_menu(config);
} else if (xy.y < h/2-10) {
g.fillRect(10, 10, w-10, h/2-10);
g.setColor(1, 1, 1)
.setFont("Vector:32")
.setFontAlign(0, 0)
.drawString("Start", w/2, h/4);
Bangle.removeAllListeners("touch");
start_routine();
set_duration(config);
}
})
}


main_menu();
let config = require("Storage").readJSON("pushups.cfg", true);

if (config === undefined) {
config = default_config();
}

main_menu(config);
1 change: 1 addition & 0 deletions apps/pushups/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"type": "app",
"tags": "health",
"supports": ["BANGLEJS2"],
"screenshots": [{"url":"shot_pushups.png"}, {"url":"shot_squats.png"}, {"url":"shot_menu.png"}],
"readme": "README.md",
"storage": [
{"name":"pushups.app.js","url":"app.js"},
Expand Down
Binary file added apps/pushups/shot_menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/pushups/shot_pushups.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/pushups/shot_squats.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 47d917c

Please sign in to comment.