Skip to content

About

The original cross platform indoor cycling solution

Resources

Code of conduct

Contributing

Stars

26 stars

Watchers

5 watching

Forks

Latest commit

Β 

History

1,097 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MaximumTrainer

An open-source, high-performance indoor cycling training application built with the Qt framework (C++17). MaximumTrainer delivers structured interval workouts with real-time power, cadence, heart rate, and speed feedback, and controls smart trainers automatically via FTMS ERG mode. Browse and sync workouts from intervals.icu, or import your own .erg / .mrc / .zwo files.

πŸ“– Want to use the app? See the User Guide β€” pairing sensors, finding & building workouts, training, Studio mode, and more.

Download

Prebuilt binaries for each release live on the Releases page β€” you don't need to build from source unless you want to. Grab the asset for your platform from the latest release:

Platform Asset Notes
Linux MaximumTrainer-linux-x86_64.AppImage chmod +x it and run β€” self-contained, no install
Windows MaximumTrainer-windows-x64.msi (installer) or -windows-x64.zip (portable) Windows 10 1809+
macOS MaximumTrainer-macos.dmg Open and drag to Applications
Web (WASM) MaximumTrainer-wasm.zip Runs in a WebAssembly-capable browser

Prefer to compile it yourself? See Building below.

Technical Overview

Item Details
Language C++17 (β‰ˆ 95 % C++)
Framework Qt 6 (6.11+) on all platforms
Build file MaximumTrainer.pro (qmake)
Qt modules core Β· gui Β· widgets Β· network Β· bluetooth Β· webenginewidgets Β· printsupport Β· concurrent
Trainer protocol Bluetooth LE Fitness Machine Service (FTMS / 0x1826) for ERG resistance control
Sensor profiles Heart Rate (0x180D) Β· Cycling Speed & Cadence (0x1816) Β· Cycling Power (0x1818) Β· Moxy Muscle Oxygen (0xAAB0)
Workout formats .erg, .mrc, .zwo (imported and converted to the native XML format) Β· Intervals.icu calendar sync Β· export as .fit
Workout source Integrated intervals.icu for online workout plans

Using MaximumTrainer

For everything about using the app β€” pairing sensors, finding / importing / creating workouts, ERG vs Slope, the workout player (video Β· web Β· Retro Race game), Studio mode, activity uploads, and troubleshooting β€” see the User Guide.

No hardware? A built-in Simulation mode emits realistic drifting power / HR / cadence / speed and responds to ERG commands, so you can exercise the full workout flow β€” handy for development and CI β€” without any Bluetooth devices.

Screenshots

Workout library
Main window β€” workout library with power graphs and metrics
Workout editor
Workout editor β€” build custom intervals with Add / Repeat / Copy / Delete
Devices β€” pairing & trainer config
Devices tab β€” pair a sensor per role, toggle ERG control, and set dropout and battery options
Workout in progress
Workout player β€” video, interval graph, and live power, heart rate, and cadence
Studio mode β€” per-rider configuration
Studio mode configuration β€” per-rider name, FTP/LTHR, sensors, and ERG control for up to 8 riders

Building

All three platforms are built and tested automatically via GitHub Actions CI (see .github/workflows/build.yml). Use MaximumTrainer.pro with qmake and a standard C++ compiler.

Dependencies

Dependency Version Platform Notes
Qt 6.x (6.11+) all Core framework (incl. QtMultimedia, QtWebEngine, QtBluetooth)
QWT 6.3.0 all Plotting widgets (built from source against Qt 6)

Audio & media playback: both sound-effect feedback (interval beeps, etc., via QSoundEffect) and the embedded video + internet-radio player (QMediaPlayer / QVideoWidget / QAudioOutput) use Qt's own QtMultimedia.

Windows β€” Requirements

  • Windows 10 version 1809 or later is required (the minimum for Qt 6.11). Windows 7, 8, and 8.1 are not supported.
  • A Bluetooth 4.0+ adapter with a WDM-compatible driver.
  • No special permissions or manifest entries are needed.

Windows

Required tools: Qt 6.x (msvc2022_64, with the qtwebengine, qtconnectivity, qtmultimedia, qtwebchannel, qtpositioning modules), Visual Studio 2022+ (MSVC; Qt 6.11 ships msvc2022 builds only).

Environment variables β€” set before running qmake:

Variable Description Example
QTDIR Qt installation root for the target arch C:\Qt\6.11.3\msvc2022_64
QWT_INSTALL QWT installation root (built against Qt 6) C:/qwt

qmake invocation:

qmake MaximumTrainer.pro `
  "QWT_INSTALL=C:/qwt"

The Windows Kit libraries (Gdi32, User32) are resolved automatically by the MSVC linker.

Download links:

Linux

Install Qt 6 + dependencies, build QWT 6 from source (no Qt6 qwt apt package exists), then build the app:

sudo apt-get install -y \
  qt6-base-dev qt6-webengine-dev qt6-connectivity-dev \
  qt6-multimedia-dev qt6-webchannel-dev qt6-positioning-dev \
  cmake build-essential

# Build QWT 6.3.0 from source against Qt 6 (installs to /tmp/qwt6 here)
cd /tmp && curl -L -o qwt.tar.bz2 \
  "https://sourceforge.net/projects/qwt/files/qwt/6.3.0/qwt-6.3.0.tar.bz2/download"
tar xf qwt.tar.bz2 && cd qwt-6.3.0
qmake6 qwt.pro && make -j$(nproc)
make install INSTALL_ROOT=/tmp/qwt6-stage
mkdir -p /tmp/qwt6 && mv /tmp/qwt6-stage/usr/local/qwt-6.3.0/* /tmp/qwt6/

# Build MaximumTrainer
cd /path/to/MaximumTrainer_Redux
qmake6 MaximumTrainer.pro QWT_INSTALL=/tmp/qwt6
make -j$(nproc)
# Run (QWT in a non-standard prefix needs LD_LIBRARY_PATH):
LD_LIBRARY_PATH=/tmp/qwt6/lib ./build/release/MaximumTrainer

Wayland: QtWebEngine and some embedded native widgets are more stable under XWayland than the native Wayland QPA plugin, so on a Wayland session the app forces the xcb platform automatically. Set QT_QPA_PLATFORM yourself to override.

macOS

Uses Qt 6.11.3 with Clang. QWT is built from source (non-framework) against Qt 6.

macOS Bluetooth permission: The app's mac/Info.plist includes the NSBluetoothAlwaysUsageDescription key, which is required by macOS 10.15+ for any app that accesses BLE devices. The first time the app attempts to connect a sensor, macOS will prompt for Bluetooth permission. This permission can be reviewed or revoked in System Settings β†’ Privacy & Security β†’ Bluetooth.

# Install Qt 6.11.3 via install-qt-action or the Qt Installer, then:

# Build QWT 6.3.0 from source (non-framework, required for Qt 6)
curl -L -o /tmp/qwt.tar.bz2 "https://sourceforge.net/projects/qwt/files/qwt/6.3.0/qwt-6.3.0.tar.bz2/download"
tar -xjf /tmp/qwt.tar.bz2 -C /tmp && cd /tmp/qwt-6.3.0
sed -i.bak 's/QwtFramework//' qwtconfig.pri
qmake qwt.pro && make -j$(sysctl -n hw.logicalcpu) && sudo make install

# Build MaximumTrainer
cd /path/to/MaximumTrainer_Redux
qmake MaximumTrainer.pro \
  "QWT_INSTALL=/usr/local/qwt-6.3.0"
make

Testing

MaximumTrainer has a multi-tier test suite covering BLE parsing, service APIs, UI flows, and integration scenarios β€” all run headlessly in CI without real hardware.

Test tiers

Suite Location What is tested
BLE unit tests tests/btle/ BLE characteristic parsing (HR, CSC, Power, FTMS), SimulatorHub signal emission, trainer simulations (Elite, Wahoo KICKR, Garmin Tacx), battery level, interval summary
Service unit tests tests/strava/, tests/intervals_icu/ HTTP request construction, auth headers, URL patterns, null-manager guards for the Strava and Intervals.icu upload services
ZWO importer tests tests/intervals_icu/ Parsing of SteadyState, Ramp, IntervalsT, FreeRide, mixed, and malformed ZWO workout files
Credential store tests tests/credential_store/ Round-trip read/write, overwrite, remove, missing key, multi-service, WASM no-op
Config bootstrap tests tests/config_bootstrap/ First-run default configuration, legacy settings migration, unreadable settings store, credential load failures logged without values
Plan adherence tests tests/plan_adherence/ Completed/skipped/substituted entries, adherence %, encode-decode, change signals
Logger tests tests/logger/ Log level filtering, output format, file write enable/disable
Studio tests tests/studio/ Multi-hub simultaneous signals, user-ID propagation, FTP scaling
Login screen tests tests/integration/ OAuth dialog state, offline login path, Intervals.icu OAuth URL generation
Workout UI tests tests/integration/ Full ERG session driven by SimulatorHub through WorkoutDialog; interval advancement; data accumulation
Offline / BLE integration tests/integration/ BLE adapter smoke test, offline mode screenshot, runtime validation
Playwright (E2E) tests/playwright/ WASM asset loading, Web Bluetooth API mock, login screen, landing page

Running the BLE unit tests

cd tests/btle
qmake btle_tests.pro
make -j$(nproc)
../../build/tests/btle_tests -v2

The BLE suite has 51 test cases across HR parsing, CSC, Power, FTMS, trainer simulations, SimulatorHub, battery, and interval summary. See the CI run status for pass/fail counts on Linux, Windows, and macOS.

Log Files

MaximumTrainer writes diagnostic messages (network errors, BLE events, OAuth login steps) to a log file. File logging is enabled by default on first launch; you can adjust the level or disable it in Preferences β†’ Logging.

Platform Default log file path Fallback if the default folder is not writable
Windows %APPDATA%\MaximumTrainer\MaximumTrainer_Redux\MaximumTrainer.log
(e.g. C:\Users\YourName\AppData\Roaming\MaximumTrainer\MaximumTrainer_Redux\MaximumTrainer.log)
%TEMP%\MaximumTrainer.log
(e.g. C:\Users\YourName\AppData\Local\Temp\MaximumTrainer.log)
macOS ~/Library/Application Support/MaximumTrainer/MaximumTrainer_Redux/MaximumTrainer.log $TMPDIR/MaximumTrainer.log
(a per-user folder under /var/folders/…/T/; run echo $TMPDIR in Terminal to see it)
Linux ~/.local/share/MaximumTrainer/MaximumTrainer_Redux/MaximumTrainer.log
(or $XDG_DATA_HOME/MaximumTrainer/MaximumTrainer_Redux/MaximumTrainer.log when $XDG_DATA_HOME is set)
$TMPDIR/MaximumTrainer.log, which is /tmp/MaximumTrainer.log when $TMPDIR is not set
Web (WASM) No log file; messages go to the browser's developer console n/a

The log file is opened before the splash screen appears, and every startup step is logged as it begins and ends, so if the app hangs or crashes while starting, the last line of the log names the step that failed. If the default folder cannot be written, the log goes to the fallback file above and its first lines record the fallback. If neither can be written, messages go to stdout only.

If you chose a custom path in Preferences β†’ Logging, the app switches to it once the configuration has loaded. Anything logged before that point (the startup banner and the configuration steps) is still in the default file, so check both when the app fails early.

On Windows, the log starts with each graphics adapter and its driver version. A crash also logs:

  • the DLL and offset that faulted
  • the call stack, with function names and source lines for the app's own code (MaximumTrainer.pdb ships next to the exe)
  • the graphics-driver and third-party DLLs loaded in the process, such as overlays and antivirus hooks

It also writes MaximumTrainer-crash.dmp next to the log file. Attach both to the bug report.

If the app crashes while the embedded web engine starts (the last log line reads during GlobalVars: QWebEngineProfile init), the next launch starts it with GPU acceleration off ("safe graphics") and keeps doing so until the app is updated; the first launch of a new version tries the GPU again. That launch also saves the web engine's own log in MaximumTrainer-webengine.log next to the app log; attach it too. Set MT_WEBENGINE_SAFE_MODE=0 to turn safe graphics off again, MT_WEBENGINE_SAFE_MODE=1 to force it on, or launch with --safe-graphics to try it once. MT_WEBENGINE_LOG=1 saves the web engine's log on any launch.

The Windows build ships its own Visual C++ runtime (msvcp140.dll, vcruntime140*.dll) next to MaximumTrainer.exe, because Qt 6.11 needs version 14.40 or newer and an older copy in System32 makes the app crash at startup with a fault in MSVCP140.dll. The log's C++ runtime: line shows which copy was loaded.

Set the log level to Debug before reproducing an issue (or launch with --debug, or /debug on Windows), then attach the log file to your bug report. The Open log file button in the Logging settings page opens the file directly in your default text editor.

For full troubleshooting guidance see the User Guide β€” Log Files & Troubleshooting section.

Credits

The bundled Polarized 3x training plan is adapted from Robin Ingelbrecht's zwift-workouts (MIT license).

TODO

Project now going through new revisions, with plans to enhance as an open-source interval trainer for indoor cycling & indoor rowing. backlog and work items can be found here

About

The original cross platform indoor cycling solution

Resources

Code of conduct

Contributing

Stars

26 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages