Hi all,
Similar to Roby, I also am programming the older TrueForm Runner. My display didn't work, the arduino board was fried when i bought it used, so I had to replace it with a new one. In case anyone else is in the same spot, I'll add my code that reprograms the display for this treadmill.
The treadmill is manual, but comes with a sensor that connects to pin 2 on the adurino. There's 4 LCD displays on it that I needed to program around the sensor based on revolutions / second. All displayed info (pace, speed, distance) is in km.
Previous thread:
@robybrisson in case you are still working on this.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>
Adafruit_7segment disp1; // 0x70
Adafruit_7segment disp2; // 0x71
Adafruit_7segment disp3; // 0x72
Adafruit_7segment disp4; // 0x74
// Physical order: 3,2,4,1 => pace, speed, distance, time
Adafruit_7segment* PACE = &disp3;
Adafruit_7segment* SPEED = &disp2;
Adafruit_7segment* DISTANCE = &disp4;
Adafruit_7segment* TIMEHUD = &disp1;
const byte SENSOR_PIN = 2; // interrupt-capable
const float CM_PER_REV = 15.34f;
const float M_PER_REV = CM_PER_REV / 100.0f;
// --- ISR counters ---
volatile unsigned long revCountISR = 0;
volatile unsigned long lastPulseUs = 0;
// rolling window: 5 s, 10 Hz -> 50 buckets
const uint16_t SAMPLE_MS = 100;
const uint8_t WIN_SECONDS = 5;
const uint8_t BUCKETS = (WIN_SECONDS * 1000) / SAMPLE_MS;
unsigned long lastUpdateMs = 0;
unsigned long startMs = 0;
unsigned long prevTotal = 0;
uint16_t buckets[BUCKETS];
uint8_t idx = 0;
uint16_t sumWin = 0;
// --- ISR: software debounce using micros() ---
void onRise() {
unsigned long t = micros();
if ((t - lastPulseUs) >= 2000) { // ignore pulses <2 ms apart
revCountISR++;
lastPulseUs = t;
}
}
void beginDisp(Adafruit_7segment& d, uint8_t addr) {
d.begin(addr);
d.setBrightness(10);
d.clear();
d.writeDisplay();
}
void showMMSS(Adafruit_7segment* d, unsigned long secs) {
unsigned int mm = secs / 60;
unsigned int ss = secs % 60;
if (mm > 99) mm = 99;
d->clear();
d->print(mm * 100 + ss);
d->drawColon(true);
d->writeDisplay();
}
void showInt(Adafruit_7segment* d, long v) {
if (v > 9999) v = 9999;
d->clear();
d->print(v);
d->writeDisplay();
}
void showFloat(Adafruit_7segment* d, float v, uint8_t decimals) {
d->clear();
d->print(v, decimals); // Adafruit_7segment handles decimal point
d->writeDisplay();
}
void setup() {
Wire.begin();
beginDisp(disp1, 0x70);
beginDisp(disp2, 0x71);
beginDisp(disp3, 0x72);
beginDisp(disp4, 0x74);
// mapping check
PACE->print(1); PACE->writeDisplay();
SPEED->print(2); SPEED->writeDisplay();
DISTANCE->print(3); DISTANCE->writeDisplay();
TIMEHUD->print(4); TIMEHUD->writeDisplay();
delay(300);
PACE->clear(); PACE->writeDisplay();
SPEED->clear(); SPEED->writeDisplay();
DISTANCE->clear(); DISTANCE->writeDisplay();
TIMEHUD->clear(); TIMEHUD->writeDisplay();
pinMode(SENSOR_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(SENSOR_PIN), onRise, RISING);
memset(buckets, 0, sizeof(buckets));
startMs = millis();
lastUpdateMs = startMs;
}
void loop() {
unsigned long now = millis();
// update at 10 Hz
if (now - lastUpdateMs >= SAMPLE_MS) {
lastUpdateMs += SAMPLE_MS;
// atomic read
noInterrupts();
unsigned long total = revCountISR;
interrupts();
// bucket update
unsigned long delta = total - prevTotal;
prevTotal = total;
sumWin -= buckets[idx];
buckets[idx] = (delta > 65535UL) ? 65535 : (uint16_t)delta;
sumWin += buckets[idx];
idx = (idx + 1) % BUCKETS;
// elapsed time
unsigned long elapsedSec = (now - startMs) / 1000UL;
// total distance (km)
float metersTotal = total * M_PER_REV;
float kmTotal = metersTotal / 1000.0f;
// window metrics
float metersWin = sumWin * M_PER_REV;
float speedKmh = (metersWin * 3.6f) / (float)WIN_SECONDS; // FIXED
unsigned long paceSecPerKm = 0; // min/km as seconds
if (speedKmh > 0.0001f) {
float secPerKm = 3600.0f / speedKmh; // 3600 sec / (km/h)
paceSecPerKm = (unsigned long)(secPerKm + 0.5f);
}
// render
showMMSS(TIMEHUD, elapsedSec);
// distance in km
if (kmTotal < 10.0f) showFloat(DISTANCE, kmTotal, 3);
else if (kmTotal < 100.0f) showFloat(DISTANCE, kmTotal, 1);
else showInt(DISTANCE, (long)(kmTotal + 0.5f));
// speed in km/h
if (speedKmh < 0.05f) {
showInt(SPEED, 0);
} else if (speedKmh < 100.0f) {
showFloat(SPEED, speedKmh, 1); // e.g., 8.1
} else {
showInt(SPEED, (long)(speedKmh + 0.5f)); // 100+
}
// pace in min:sec per km
if (paceSecPerKm == 0) {
showInt(PACE, 0);
} else {
showMMSS(PACE, paceSecPerKm);
}
}
}