Embedded firmware · ATmega328 · C++/Arduino

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.

MCU: ATmega328 (Uno/Nano) Display: TM1637 4-digit I/O: 4 relays · 4 buttons · 1 vibration motor Loop cadence: 10 ms github.com/amd858/startrac_walk_intervals ↗
01 · What it does

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.

02 · Signal flow

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.

Signal flow diagram External buttons feed the ATmega328. The controller drives four relays into the treadmill, a vibration motor, TM1637 display, and telemetry output. Start / Stop buttons Wait / Extreme buttons DIP config bits ATmega328 10ms main loop 4× relay → treadmill panel Vibration motor (fake step) TM1637 4-digit display SoftwareSerial TX telemetry + / − / start / stop, unmodified

Dashed edge = wired but not currently read in software (see Notes & quirks).

03 · Hardware map

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.

PinSymbolModeFunction
D2START_BUTTON_PININPUTExternal start button — read inverted (!digitalRead)
D3STOP_BUTTON_PININPUTExternal stop button — same convention
D4TREADMILL_PLUSOUTPUTRelay across treadmill's "+" speed button
D5TREADMILL_STOPOUTPUTRelay across treadmill's "stop" button
D6TREADMILL_STARTOUTPUTRelay across treadmill's "start" button
D7MOTOR_PIN_2OUTPUTVibration/linear motor — fake pedometer steps
D8FAN_CLOCK_ENABLE_PINOUTPUTCooling-fan timer enable while session active
D9BUZZEROUTPUTConfigured but unused in this revision — reserved
D10TREADMILL_MINUSOUTPUTRelay across treadmill's "−" speed button
D11WAIT_BUTTON_PININPUT_PULLUP"I'm ready" button, sampled in final ~7s of rest
D12EXTREME_MODE_BUTTON_PININPUT_PULLUPCycles speed tier (walking) or interval preset (stopped)
D13READY_LEDOUTPUTLit during final seconds of rest phase
A0myTxSerial (TX)SoftwareSerialTX-only telemetry @ 115200 baud
A1–A3DIP_BIT_1/2/3INPUT_PULLUPDIP config bits — wired, not currently read
A4DIOTM1637Display data line
A5CLKTM1637Display clock line
04 · Control flow

State machines

Two state machines run every loop pass. Debounce sub-states are shown under each diagram.

treadmill_state — workout controller

Treadmill workout state machine STOP CONSTANT_SPEED HIIT_REST HIIT_HIGH start ×3 start ×3 rest timer elapses interval timer elapses stop button ×3, from any active state → STOP
Enum valueMeaning
STOPIdle; belt off, display shows next interval preset duration
BOUNCING_BEFORE_CONSTANT_SPEED3-tick debounce gate before committing to ramp-up
CONSTANT_SPEEDBelt at steady walking pace; step counting and speed-tier cycling active
BOUNCING_BEFORE_STOP3-tick debounce gate before committing to full stop
BOUNCING_BEFORE_HIIT_REST3-tick debounce gate before entering interval program
HIIT_RESTRecovery pace; countdown shown, ready-LED signals late in phase
HIIT_HIGHInterval 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.

StateBehaviorNext state
MOTOR_STOPNo pulses; waiting for treadmill to enter CONSTANT_SPEED or HIIT_HIGHMOTOR_ACTIVE
MOTOR_ACTIVEPulse is ON (for SLOW_STEP_TICKS or FAST_STEP_TICKS ms)IDLE
IDLEPulse is OFF; decrement step counter, prepare next pulseMOTOR_ACTIVE or PAUSE_STEPS
PAUSE_STEPSMotor requested to pause (e.g., during rest phase)PAUSED
PAUSEDPaused state; pending step count preserved, awaiting resume signalMOTOR_ACTIVE
05 · Function reference

Function reference

Speed ramping — virtual button presses

set_treadmill_speed(target_speed)

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.

Streams every intermediate speed value out over SoftwareSerial for external logging
jump_up_one()

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.

jump_down_one()

Single press of the − button. Same timing discipline as jump_up_one.

next_odd_ten(current, target)

Computes a safe intermediate checkpoint speed—always an odd multiple of ten—to prevent overshooting the target during long-press batching.

Interval scheduling

treadmillProcessing()

Top-level state machine. Owns treadmill_state, countdown timers, and all speed transitions. Called once per 10ms main loop tick.

displayTime(remaining_seconds)

Packs remaining interval time (mm:ss) into the TM1637's colon format for display on the 4-digit module.

print_countdown()

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

fakeStepsProcessing()

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.

Preserves pending step count across pause/resume cycles

Live overrides & timing

extreme_mode_button_processing()

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.

aditional_wait_calculator()

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.

06 · Tuning constants

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.

07 · Auxiliary modules

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.

08 · Revision history

Revision history

Complete firmware snapshots kept in old firmware/ directory — useful for diffing behavior or tuning speed tables across revisions.

03-04-2026
Initial commit — treadmill state machine, relay control, HIIT scheduler
05-06-2026
Added fake-step motor — pedometer simulation with cadence matching
23-06-2026
Display integration — TM1637 countdown and speed-tier display
26-06-2026
Extreme mode button — live speed tier / interval preset cycling
30-06-2026
Wait button & rest extension — adaptive recovery timing
01-07-2026
SoftwareSerial telemetry — speed change logging at 115200 baud
02-07-2026
Current revision — ready-LED during final rest seconds, motor pause logic
09 · Notes & known quirks

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.

See setup() pinMode declarations

BUZZER not used: D9 is configured but never driven in this revision. Reserved for audio feedback or alarm in future versions.

See 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.

See treadmillProcessing() button read logic

Motor 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.

See fakeStepsProcessing(), PAUSE_STEPS state

No 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.

See jump_up_one(), jump_down_one()