StarTrac Walk Intervals
Firmware that turns a StarTrac treadmill into a self-driving HIIT walking controller — it physically actuates the treadmill's own speed buttons, injects fake pedometer steps timed to belt cadence, and runs a full rest/high interval scheduler on a 4-digit display, without touching the treadmill's internal electronics.
Overview
The treadmill has its own control board with physical + / − / start / stop buttons. Rather than reverse-engineering or replacing that board, this firmware sits alongside it: four relay outputs press those same buttons on the controller's behalf, so the stock treadmill logic and safety behavior are untouched. An external start/stop button pair and a small control panel (wait, extreme-mode) let the user drive full workouts hands-off.
Two independent finite-state machines run in the main loop, sampled every 10 ms: treadmillProcessing owns the workout itself — idle, ramp-up, steady walk, and alternating high/rest HIIT intervals — while fakeStepsProcessing drives a vibration motor to simulate footfalls for a pedometer strapped to the deck, throttled to the belt's real pace and automatically silenced during high-intensity intervals so step counts stay believable.
A TM1637 4-digit display shows either the interval countdown or the day's simulated step count, and a second MCU pinned to a SoftwareSerial line streams every commanded speed change out as plain-text telemetry for external logging.
Signal architecture
The controller is the only piece of "smart" hardware in the loop — everything downstream of it is either a dumb relay contact or a passive display.
Dashed edge = wired but not currently read in software (see Notes & quirks).
Pin reference
All pins are configured in setup(). Two logic conventions coexist: the external start/stop pair is plain INPUT (board-level pull assumed), while wait and extreme-mode buttons use INPUT_PULLUP and read active-low.
| Pin | Symbol | Mode | Function |
|---|---|---|---|
| D2 | START_BUTTON_PIN | INPUT | External start button — read inverted (!digitalRead) |
| D3 | STOP_BUTTON_PIN | INPUT | External stop button — same convention |
| D4 | TREADMILL_PLUS | OUTPUT | Relay across treadmill's "+" speed button |
| D5 | TREADMILL_STOP | OUTPUT | Relay across treadmill's "stop" button |
| D6 | TREADMILL_START | OUTPUT | Relay across treadmill's "start" button |
| D7 | MOTOR_PIN_2 | OUTPUT | Vibration/linear motor — fake pedometer steps |
| D8 | FAN_CLOCK_ENABLE_PIN | OUTPUT | Cooling-fan timer enable while session active |
| D9 | BUZZER | OUTPUT | Configured but unused in this revision — reserved |
| D10 | TREADMILL_MINUS | OUTPUT | Relay across treadmill's "−" speed button |
| D11 | WAIT_BUTTON_PIN | INPUT_PULLUP | "I'm ready" button, sampled in final ~7s of rest |
| D12 | EXTREME_MODE_BUTTON_PIN | INPUT_PULLUP | Cycles speed tier (walking) or interval preset (stopped) |
| D13 | READY_LED | OUTPUT | Lit during final seconds of rest phase |
| A0 | myTxSerial (TX) | SoftwareSerial | TX-only telemetry @ 115200 baud |
| A1–A3 | DIP_BIT_1/2/3 | INPUT_PULLUP | DIP config bits — wired, not currently read |
| A4 | DIO | TM1637 | Display data line |
| A5 | CLK | TM1637 | Display clock line |
State machines
Two state machines run every loop pass. Debounce sub-states are shown under each diagram.
treadmill_state — workout controller
| Enum value | Meaning |
|---|---|
| STOP | Idle; belt off, display shows next interval preset duration |
| BOUNCING_BEFORE_CONSTANT_SPEED | 3-tick debounce gate before committing to ramp-up |
| CONSTANT_SPEED | Belt at steady walking pace; step counting and speed-tier cycling active |
| BOUNCING_BEFORE_STOP | 3-tick debounce gate before committing to full stop |
| BOUNCING_BEFORE_HIIT_REST | 3-tick debounce gate before entering interval program |
| HIIT_REST | Recovery pace; countdown shown, ready-LED signals late in phase |
| HIIT_HIGH | Interval pace; countdown shown, fake-step injection paused |
fake_steps_machine_state — pedometer injection
A 5-state motor control machine that generates periodic pulses to simulate footfalls on the treadmill's deck, with automatic pausing during high-intensity phases.
| State | Behavior | Next state |
|---|---|---|
| MOTOR_STOP | No pulses; waiting for treadmill to enter CONSTANT_SPEED or HIIT_HIGH | MOTOR_ACTIVE |
| MOTOR_ACTIVE | Pulse is ON (for SLOW_STEP_TICKS or FAST_STEP_TICKS ms) | IDLE |
| IDLE | Pulse is OFF; decrement step counter, prepare next pulse | MOTOR_ACTIVE or PAUSE_STEPS |
| PAUSE_STEPS | Motor requested to pause (e.g., during rest phase) | PAUSED |
| PAUSED | Paused state; pending step count preserved, awaiting resume signal | MOTOR_ACTIVE |
Function reference
Speed ramping — virtual button presses
Ramps the belt from the current speed to target_speed using only the stock +/− buttons. Uses single presses for fine adjustments (up to 3), then switches to long-presses batched by next_odd_ten to avoid overshoot.
Single press of the + button with bounded delays: on_time ms hold, off_time ms release. Tunes to treadmill response time; start at 120ms/190ms.
Single press of the − button. Same timing discipline as jump_up_one.
Computes a safe intermediate checkpoint speed—always an odd multiple of ten—to prevent overshooting the target during long-press batching.
Interval scheduling
Top-level state machine. Owns treadmill_state, countdown timers, and all speed transitions. Called once per 10ms main loop tick.
Packs remaining interval time (mm:ss) into the TM1637's colon format for display on the 4-digit module.
Compresses long countdowns (>90s) into a coarser step interval so the display doesn't flicker every second. Shows active speed tier digit alongside countdown.
Fake step injection
Motor pulse generator state machine. Fires MOTOR_PIN_2 for a fixed on/off window per step, timed to belt cadence. Automatically pauses during high-intensity phases.
Live overrides & timing
Debounced press/release state machine. In CONSTANT_SPEED, cycles extreme_speed_multiplier and immediately calls set_treadmill_speed. In STOP, cycles interval_index into duration tables.
Extends the rest phase in real time if the wait button is held during the final ~5 seconds of rest. Lights READY_LED during the last ~7 seconds to signal imminent end.
Tuning constants
All user-configurable parameters defined at the top of the sketch. Adjust these for your treadmill's responsiveness and workout profile.
Relay timing (milliseconds)
int on_time = 120; // Button press hold int off_time = 190; // Button release
Tune if: Treadmill misses button presses → increase both. Over-responds → decrease both.
Motor step timing (ticks per step)
#define SLOW_STEP_TICKS 88 #define FAST_STEP_TICKS 53
Tune if: Pedometer reads too high/low → adjust ticks inversely.
Speed tables
const int CONSTANT_SPEEDS_ARRAY[5] = { 25, 32, 40, 80, 100 }; // Mapped to: 2.5, 3.2, 4.0, 8.0, 10.0 km/h const int HIIT_HIGH_SPEEDS_ARRAY[8] = { 54, 80, 90, 100, 110, 120, 130, 140 }; // Mapped to: 5.4, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0 km/h
Tune if: Want different walking speeds or interval intensities → edit array values.
Interval duration tables (seconds)
const int HIIT_high_interval_array[10] = { 60, 30, 150, 240, 300, 360, 420, 480, 540, 600 }; const int HIIT_rest_interval_array[4] = { 60, 64, 120, 180 }; // Extreme-mode button cycles through these presets
Tune if: Want custom workout schedules → edit array values and sizes.
Auxiliary modules
Cooling fan control (ESP-NOW wireless)
Optional wireless control for a remote cooling fan via ESP8266-based ESP-NOW protocol:
- Basic_Master: Monitors the cooling-fan trigger pin, sends state over ESP-NOW
- slave_fan: Receives signal, drives physical fan relay
No WiFi router needed — pure point-to-point RF. Useful if the fan is mounted remotely or isolation is required between circuits.
Motor tuning bench sketch
watch_linear_motor/ contains a standalone sketch for tuning the fake-step vibration-motor timing in isolation. Later folded into fakeStepsProcessing() above.
Revision history
Complete firmware snapshots kept in old firmware/ directory — useful for diffing behavior or tuning speed tables across revisions.
Notes & known quirks
DIP switch bits wired but unread: DIP_BIT_1/2/3 (A1–A3) are wired to the control panel but not currently read in firmware logic. Reserved for future tuning profiles.
setup() pinMode declarationsBUZZER not used: D9 is configured but never driven in this revision. Reserved for audio feedback or alarm in future versions.
setup()External start/stop buttons are active-high: Contrast with WAIT_BUTTON_PIN and EXTREME_MODE_BUTTON_PIN, which use internal pullups and read active-low.
treadmillProcessing() button read logicMotor pulses pause during HIIT phases: Step injection automatically stops (without losing pending count) whenever the treadmill enters a high-intensity interval, so injected steps never overlap user's active motion.
fakeStepsProcessing(), PAUSE_STEPS stateNo blocking delays in main state machines: All timing is 10ms-tick-based. Only the low-level relay actuation functions use bounded delay() calls to match real button press timing.
jump_up_one(), jump_down_one()