Skip to content

Add files via upload #3927

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions apps/smartrep/logic/bench_press.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@

let alpha = 0.2;
let ema = null;
let win = [];
let windowSize = 20;

let repCount = 0;
let state = "down";
let lastPeakT = 0, lastValleyT = 0;
let lastPeakV = 0, lastValleyV = 0;

let minRepTime = 300;
let minAmplitude = 15;
let tracking = false;

exports.start = function(onRep) {
repCount = 0;
ema = null;
state = "down";
lastPeakT = lastValleyT = (getTime() * 1000) | 0;
lastPeakV = lastValleyV = 0;
win = [];
tracking = true;

Bangle.setPollInterval(40);
Bangle.on("accel", function handler(a) {
let angle = Math.atan2(a.x, a.z) * 180 / Math.PI;
if (ema === null) ema = angle;
ema = alpha * angle + (1 - alpha) * ema;

win.push(ema);
if (win.length > windowSize) win.shift();
let mx = Math.max(...win);
let mn = Math.min(...win);
let mean = win.reduce((a, b) => a + b) / win.length;

let upTh = mean + 0.3 * (mx - mn);
let downTh = mean - 0.3 * (mx - mn);
let now = (getTime() * 1000) | 0;

if (state === "down" && ema > upTh && now - lastValleyT > minRepTime) {
state = "up";
lastPeakT = now;
lastPeakV = ema;
} else if (state === "up" && ema < downTh && now - lastPeakT > minRepTime) {
state = "down";
lastValleyT = now;
lastValleyV = ema;
let amp = Math.abs(lastPeakV - lastValleyV);
if (amp >= minAmplitude) {
repCount++;
onRep();
Bangle.buzz();
}
}
});
};

exports.stop = function() {
tracking = false;
Bangle.removeAllListeners("accel");
};
86 changes: 86 additions & 0 deletions apps/smartrep/logic/bicep_curl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// === Bicep Curl Rep Counter for Bangle.js 1 ===

// ==== Configuration ====
const POLLING_INTERVAL = 40; // ~25Hz sampling
const THRESHOLD_UP = 0.85; // Detected upward motion (from dataset)
const THRESHOLD_DOWN = 0.35; // Detected downward motion (from dataset)
const MIN_REP_INTERVAL = 500; // Minimum 0.5 sec between reps

// ==== State Variables ====
let repCount = 0;
let inCurl = false;
let lastRepTime = 0;
let tracking = false;

// ==== UI Functions ====
function drawStatus() {
g.clearRect(0, 40, 240, 120);
g.setFont("6x8", 3);
g.drawString("Reps: " + repCount, 20, 60);
g.setFont("6x8", 2);
g.drawString(inCurl ? "Up" : "Down", 100, 100);
}

function showStartScreen() {
g.clear();
g.setFont("6x8", 2);
g.drawString("Bicep Curl Tracker", 10, 10);
drawStatus();
}

function showStopScreen() {
g.clear();
g.setFont("6x8", 2);
g.drawString("Tracking Stopped", 20, 60);
}

// ==== Accelerometer Handler ====
function onAccel(data) {
let z = data.z;
let now = getTime() * 1000;

if (!inCurl && z > THRESHOLD_UP) {
inCurl = true;
}

if (inCurl && z < THRESHOLD_DOWN && (now - lastRepTime > MIN_REP_INTERVAL)) {
repCount++;
lastRepTime = now;
inCurl = false;
drawStatus();
}
}

// ==== Control Functions ====
function startTracking() {
if (tracking) return;
tracking = true;
repCount = 0;
inCurl = false;
showStartScreen();
Bangle.setPollInterval(POLLING_INTERVAL);
Bangle.on('accel', onAccel);
Bangle.setLCDPower(1);
}

function stopTracking() {
if (!tracking) return;
tracking = false;
Bangle.removeListener('accel', onAccel);
showStopScreen();
}

function resetReps() {
repCount = 0;
drawStatus();
}

// ==== Button Bindings ====
setWatch(startTracking, BTN1, { repeat: true, edge: "rising" });
setWatch(stopTracking, BTN2, { repeat: true, edge: "rising" });
setWatch(resetReps, BTN3, { repeat: true, edge: "rising" });

// ==== Init Display ====
g.clear();
g.setFont("6x8", 2);
g.drawString("Press BTN1 to Start", 10, 50);
40 changes: 40 additions & 0 deletions apps/smartrep/logic/cable_rows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
let pollingInterval = 40;
let thresholdPull = 0.85; // pulling motion
let thresholdRelease = 0.35; // return motion
let minInterval = 500; // 0.5s between rep

let repCount = 0;
let inPull = false;
let lastRepTime = 0;
let tracking = false;

exports.start = function(onRep) {
if (tracking) return;
tracking = true;
repCount = 0;
inPull = false;
lastRepTime = 0;

Bangle.setPollInterval(pollingInterval);
Bangle.on('accel', function handler(a) {
let y = a.y;
let now = getTime() * 1000;

if (!inPull && y > thresholdPull) inPull = true;

if (inPull && y < thresholdRelease && now - lastRepTime > minInterval) {
lastRepTime = now;
inPull = false;
repCount++;
onRep();
Bangle.buzz();
}
});
};

exports.stop = function() {
if (!tracking) return;
tracking = false;
Bangle.removeAllListeners('accel');
};

87 changes: 87 additions & 0 deletions apps/smartrep/logic/lateral_raise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// === Lateral Arm Raise Rep Counter for Bangle.js 1 ===
// Tracks up-down motion using x-axis and gives feedback on each rep.

// === Display Title ===
g.clear();
g.setFont("6x8", 2);
g.drawString("Lateral Raise Tracker", 10, 10);

// === Configuration ===
let pollingInterval = 40; // ~25Hz polling rate
let thresholdUp = 0.6; // X-axis threshold for upward motion
let thresholdDown = 0.2; // X-axis threshold for downward motion
let minInterval = 600; // Minimum time between reps (ms)

// === State Variables ===
let repCount = 0; // Number of reps counted
let inRaise = false; // Whether arm is currently rising
let lastRepTime = 0; // Last rep timestamp (ms)
let tracking = false; // Whether tracking is active

// === Draw Rep Status ===
function drawStatus() {
g.clearRect(0, 40, 240, 120);
g.setFont("6x8", 3);
g.drawString("Reps: " + repCount, 20, 60);
g.setFont("6x8", 2);
g.drawString(inRaise ? "Up" : "Down", 100, 100);
}

// === Feedback (Vibration + Optional Beep) ===
function giveFeedback() {
Bangle.buzz(200); // 200ms vibration
Bangle.beep(); // Optional sound
}

// === Accelerometer Handler ===
function onAccel(a) {
let x = a.x;
let now = getTime() * 1000; // Convert to ms

// Detect upward motion
if (!inRaise && x > thresholdUp) {
inRaise = true;
}

// Detect downward motion + validate full rep
if (inRaise && x < thresholdDown && (now - lastRepTime > minInterval)) {
repCount++;
lastRepTime = now;
inRaise = false;
drawStatus();
giveFeedback();
}
}

// === Start Tracking ===
function startTracking() {
if (tracking) return;
tracking = true;
repCount = 0;
inRaise = false;
g.clear();
g.setFont("6x8", 2);
g.drawString("Lateral Raise Tracker", 10, 10);
drawStatus();
Bangle.setPollInterval(pollingInterval);
Bangle.on('accel', onAccel);
Bangle.setLCDPower(1);
}

// === Stop Tracking ===
function stopTracking() {
if (!tracking) return;
tracking = false;
Bangle.removeListener('accel', onAccel);
g.clear();
g.setFont("6x8", 2);
g.drawString("Tracking Stopped", 20, 60);
}

// === Button Bindings ===
setWatch(startTracking, BTN1, { repeat: true, edge: "rising" });
setWatch(stopTracking, BTN2, { repeat: true, edge: "rising" });
setWatch(() => {
repCount = 0;
drawStatus();
}, BTN3, { repeat: true, edge: "rising" });
63 changes: 63 additions & 0 deletions apps/smartrep/logic/pushup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// === Push-Up Detector for Bangle.js 1 ===

// === Configuration ===
const POLLING_INTERVAL = 40; // Check accelerometer every 40ms (~25Hz)
const WINDOW_DURATION = 2000; // Analyze 2 seconds of motion data

// === Variables ===
let samples = []; // Stores recent accelerometer data

// === Show title ===
function showHeader() {
g.clear();
g.setFont("6x8", 2);
g.drawString("Push-Up Detector", 10, 10);
}

// === Show current state (push-up or rest) ===
function showPrediction(text) {
g.clearRect(0, 40, 240, 100);
g.setFont("6x8", 3);
g.drawString(text, 20, 50);
}

// === Extract average Z-axis acceleration ===
function computeFeatures(samples) {
let sumZ = samples.reduce((sum, s) => sum + s.z, 0);
let az_mean = sumZ / samples.length;
return { az_mean };
}

// === Classify motion as push-up or rest based on Z-axis average ===
function classify(features) {
return features.az_mean <= -0.34885 ? 1 : 0; // 1 = push-up, 0 = rest
}

// === Handle new accelerometer data ===
function onAccel(data) {
let t = getTime();
samples.push({ t: t, z: data.z });

// Keep only the last 2 seconds of samples
samples = samples.filter(s => t - s.t <= WINDOW_DURATION / 1000);

// Classify if enough data is collected
if (samples.length >= 10) {
let features = computeFeatures(samples);
let result = classify(features);
showPrediction(result === 1 ? "Push-Up!" : "Resting");
}
}

// === Start Tracking ===
showHeader();
Bangle.setPollInterval(POLLING_INTERVAL);
Bangle.on('accel', onAccel);

// === Stop Tracking on BTN1 ===
setWatch(() => {
Bangle.removeListener('accel', onAccel);
g.clear();
g.setFont("6x8", 2);
g.drawString("Stopped", 30, 60);
}, BTN1, { repeat: false, edge: "rising" });
Loading