Files
qdomyos-zwift/docs/10_Installation.md
2025-12-17 06:22:22 +01:00

15 KiB

Installation

QDomyos-Zwift can be installed from source on MacOs, Linux, Android and IOS.

Once you've installed QDomyos-Zwift, you can access the operation guide for more information.

These instructions build the app itself, not the test project.

On a Linux System (from source)

$ sudo apt update && sudo apt upgrade # this is very important on Raspberry Pi: you need the bluetooth firmware updated!
$ sudo apt install git qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtbase5-private-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-assistant libqt5networkauth5-dev libqt5websockets5-dev qml-module* libqt5texttospeech5-dev libqt5texttospeech5 libqt5location5-plugins qtlocation5-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 g++ make qtbase5-dev libqt5sql5 libqt5sql5-mysql libqt5sql5-psql
$ git clone https://github.com/cagnulein/qdomyos-zwift.git
$ cd qdomyos-zwift
$ git submodule update --init src/smtpclient/
$ git submodule update --init src/qmdnsengine/
$ git submodule update --init tst/googletest/
$ cd src
$ qmake qdomyos-zwift.pro
$ make -j4
$ sudo ./qdomyos-zwift

MacOs installation

You will need to (at a minimum) to install the xcode Command Line Tools (CLI) thanks to @richardwait https://developer.apple.com/download/more/?=xcode

Download and install https://download.qt.io/archive/qt/5.12/5.12.12/qt-opensource-mac-x64-5.12.12.dmg and simply run the qdomyos-zwift release for MacOs

On Raspberry Pi Zero W

raspi

This guide will walk you through steps to setup an autonomous, headless Raspberry Pi bridge.

Initial System Preparation

You can install a lightweight version of embedded OS to speed up your Raspberry booting time.

Prepare your SD Card

Get the latest Raspberry Pi Imager and install, on a SD card, Raspberry Pi OS Lite 64bit. Boot up the Raspberry Pi (default credentials are pi/raspberry)

Change default credentials

sudo raspi-config > Password

Setup Wifi

raspi-config

sudo raspi-config System Options > Wireless LAN Enter an SSID and your wifi password.

Your Raspberry will fetch a DHCP address at boot time, which can be painful :

  • The IP address might change at every boot
  • This process takes approximately 10 seconds at boot time.

It is recommended to set a fixed IP address

(optional) Set Fixed IP address

Edit /etc/dhcpcd.conf and insert the following content with your configuration.

# Example static IP configuration:
interface wlan0
static ip_address=192.168.1.99/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1 8.8.8.8 

Apply the changes sudo systemctl restart dhcpcd.service and ensure you have internet access.

Enable SSH access

You might want to access your Raspberry remotely while it is attached to your fitness equipment.

sudo raspi-config > Interface Options > SSH

Do not wait for network at boot

This option allows a faster boot. sudo raspi-config > System Options > Network at boot > No

Reboot and test connectivity

Reboot your Raspberry sudo reboot now

Congratulations !
Your Raspberry should be reachable from your local network via SSH.

QDOMYOS-ZWIFT installation

Qdomyos-zwift can be compiled from source (hard), or using a binary (easy). Only one is required.

Update your Raspberry (mandatory !)

Before installing qdomyos-zwift, let's ensure we have an up-to-date system.

sudo apt-get update
sudo apt-get upgrade

This operation takes a moment to complete.

Option 1. Install qdomyos-zwift from sources

sudo apt install git libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtbase5-private-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 qt5-assistant libqt5networkauth5-dev libqt5websockets5-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 qtlocation5-dev qtquickcontrols2-5-dev libqt5texttospeech5-dev libqt5texttospeech5 g++ make qtbase5-dev libqt5sql5 libqt5sql5-mysql libqt5sql5-psql
git clone https://github.com/cagnulein/qdomyos-zwift.git
cd qdomyos-zwift
git submodule update --init src/smtpclient/  
git submodule update --init src/qmdnsengine/  
git submodule update --init tst/googletest/
cd src
qmake qdomyos-zwift.pro    
make  

If you need GUI also do a

apt install qml-module*

Please note :

  • Don't build the application with -j4 option (this will fail)
  • Build operation is circa 45 minutes (subsequent builds are faster)

Option 2. Install qdomyos-zwift from binary

Ensure you're logged in to GitHub and download https://github.com/cagnulein/qdomyos-zwift/actions/runs/19521021942/artifacts/4622513957. Extract the zip file and copy the QZ binary to the Raspberry Pi Zero 2 W. If you get a 404 Not Found you might have to login to GitHub first.

Make it executable:

chmod +x qdomyos-zwift-64bit

Install required libraries and dependencies for headless mode:

sudo apt install libqt5charts5 libqt5multimedia5 libqt5bluetooth5 libqt5xml5t64 libqt5positioning5 libqt5networkauth5 libqt5websockets5 libqt5texttospeech5 libqt5sql5t64 

If you are running Raspberry Pi Desktop OS, and you want to run the QZ UI, additonally add the qml libraries.

sudo apt install libqt5charts5 libqt5multimedia5 libqt5bluetooth5 libqt5xml5t64 libqt5positioning5 libqt5networkauth5 libqt5websockets5 libqt5texttospeech5 libqt5sql5t64 *qml*

Unblock Bluetooth (if using Bluetooth)

Unblock Bluetooth:

sudo rfkill unblock bluetooth

Troubleshooting Bluetooth not working: Errors:

Fri Nov 21 18:05:07 2025 1763708707500 Debug:   Bluez 5 detected.
qt.bluetooth.bluez: Aborting device discovery due to offline Bluetooth Adapter
Fri Nov 21 18:05:07 2025 1763708707540 Debug:   Aborting device discovery due to offline Bluetooth Adapter
^C"SIGINT"
Fri Nov 21 18:05:21 2025 1763708721033 Debug: devices/bluetooth.cpp virtual bool bluetooth::handleSignal(int) "SIGINT"

Check if Bluetooth is blocked/down:

$ rfkill list
0: hci0: Bluetooth
        Soft blocked: yes
        Hard blocked: no
1: phy0: Wireless LAN
        Soft blocked: no
        Hard blocked: no
$ hciconfig -a
hci0:   Type: Primary  Bus: UART
        BD Address: B8:27:EB:A2:85:70  ACL MTU: 1021:8  SCO MTU: 64:1
        DOWN
        RX bytes:3629 acl:0 sco:0 events:280 errors:0
        TX bytes:48392 acl:0 sco:0 commands:280 errors:0
        Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87
        Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
        Link policy: RSWITCH SNIFF
        Link mode: PERIPHERAL ACCEPT

Unblock Bluetooth:

sudo rfkill unblock bluetooth

Test your installation

It is now time to check everything's fine

sudo ./qdomyos-zwift-64bit -no-gui -heart-service

initial setup

Test your access from your fitness device.

Check logs to see if it's running:

journalctl -u qz.service -f

Update QZ config file

Running headless you need to update /root/.config/'Roberto Viola'/qDomyos-Zwift.conf with specific settings for your set up. If you already have it working on an iPhone/Android, follow this guide to deploy QZ with the UI, replicate the settings in the UI, check everything works, then take a copy of /root/.config/'Roberto Viola'/qDomyos-Zwift.conf to use with the headless deployment.

For my set up, I add:

Nordictrack C1650:

norditrack_s25_treadmill=true
proformtreadmillip=172.31.2.36

Zwift specific options (auto inclination not there yet in the Raspberry Pi version):

zwift_api_autoinclination=true
zwift_inclination_gain=1
zwift_inclination_offset=0
zwift_username=user@myemail.com
zwift_password=Password1

Check it works:

sudo ./qdomyos-zwift-64bit -no-gui -no-console -no-log

Automate QDOMYOS-ZWIFT at startup

You might want to have QDOMYOS-ZWIFT to start automatically at boot time.

Let's create a systemd service that we'll enable at boot sequence. Update ExecStart with the path and full name with commandline options for your qz binary. Update ExecStop with the full name of the binary.

sudo vi /lib/systemd/system/qz.service

[Unit]
Description=qdomyos-zwift service
After=multi-user.target

[Service]
Type=idle  
Restart=always
RestartSec=30
ExecStart=/home/pi/qdomyos-zwift/src/qdomyos-zwift -no-gui -no-log -heart-service
ExecStop=killall -9 qdomyos-zwift
User=root  

[Install]  
WantedBy=multi-user.target 

Once your file saved, you need to reload the active systemd configuration.
sudo systemctl daemon-reload

Test your service to check if everything's fine :

sudo systemctl start qz

service

Check if your system is stopping correctly : sudo systemctl stop qz

If everything is working as expected, enable your service at boot time :
sudo systemctl enable qz

Then reboot to check operations (sudo reboot)

(optional) Treadmill Auto-Detection and Service Management

This section provides a reliable way to manage the QZ service based on the treadmill's power state. Using a bluetoothctl-based Bash script, this solution ensures the QZ service starts when the treadmill is detected and stops when it is not.

  • Bluetooth Discovery: Monitors treadmill availability via bluetoothctl.
  • Service Control: Automatically starts and stops the QZ service.
  • Logging: Tracks treadmill status and actions in a log file.

Notes:

  • Ensure bluetoothctl is installed and working on your system.
  • Replace I_TL in the script with your treadmill's Bluetooth name. You can find your device name via bluetoothctl scan on
  • Adjust the sleep interval (sleep 30) in the script as needed for your use case.

Step 1: Save the following script as /root/qz-treadmill-monitor.sh:

#!/bin/bash

LOG_FILE="/var/log/qz-treadmill-monitor.log"
TARGET_DEVICE="I_TL"
SCAN_INTERVAL=30  # Time in seconds between checks
SERVICE_NAME="qz"
DEBUG_LOG_DIR="/var/log"  # Directory where QZ debug logs are stored
ERROR_MESSAGE="BTLE stateChanged InvalidService"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}

is_service_running() {
    systemctl is-active --quiet "$SERVICE_NAME"
    return $?
}

scan_for_device() {
    log "Starting Bluetooth scan for $TARGET_DEVICE..."

    # Run bluetoothctl scan in the background and capture output
    bluetoothctl scan on &>/dev/null &
    SCAN_PID=$!

    # Allow some time for devices to appear
    sleep 5

    # Check if the target device appears in the list
    bluetoothctl devices | grep -q "$TARGET_DEVICE"
    DEVICE_FOUND=$?

    # Stop scanning
    kill "$SCAN_PID"
    bluetoothctl scan off &>/dev/null

    if [ $DEVICE_FOUND -eq 0 ]; then
        log "Device '$TARGET_DEVICE' found."
        return 0
    else
        log "Device '$TARGET_DEVICE' not found."
        return 1
    fi
}

restart_qz_on_error() {
    # Get the current date
    CURRENT_DATE=$(date '+%a_%b_%d')
    
    # Find the latest QZ debug log file for today
    LATEST_LOG=$(ls -t "$DEBUG_LOG_DIR"/debug-"$CURRENT_DATE"_*.log 2>/dev/null | head -n 1)
    
    if [ -z "$LATEST_LOG" ]; then
        log "No QZ debug log found for today."
        return 0
    fi

    log "Checking latest log file: $LATEST_LOG for errors..."

    # Search the latest log for the error message
    if grep -q "$ERROR_MESSAGE" "$LATEST_LOG"; then
        log "***** Error detected in QZ log: $ERROR_MESSAGE *****"
        log "Restarting QZ service..."
        systemctl restart "$SERVICE_NAME"
    else
        log "No errors detected in $LATEST_LOG."
    fi
}

manage_service() {
    local device_found=$1
    if $device_found; then
        if ! is_service_running; then
            log "***** Starting QZ service... *****"
            systemctl start "$SERVICE_NAME"
        else
            log "QZ service is already running."
            restart_qz_on_error  # Check the log for errors when QZ is already running
        fi
    else
        if is_service_running; then
            log "***** Stopping QZ service... *****"
            systemctl stop "$SERVICE_NAME"
        else
            log "QZ service is already stopped."
        fi
    fi
}

while true; do
    log "Checking for treadmill status..."
    if scan_for_device; then
        manage_service true
    else
        manage_service false
    fi
    log "Waiting for $SCAN_INTERVAL seconds before next check..."
    sleep "$SCAN_INTERVAL"
done

Step2: To ensure the script runs continuously, create a systemd service file at /etc/systemd/system/qz-treadmill-monitor.service

[Unit]
Description=QZ Treadmill Monitor Service
After=bluetooth.service

[Service]
Type=simple
ExecStart=/root/qz-treadmill-monitor.sh
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target

Step 3: Enable and Start the Service

sudo systemctl daemon-reload
sudo systemctl enable qz-treadmill-monitor
sudo systemctl start qz-treadmill-monitor

Monitor logs are written to /var/log/qz-treadmill-monitor.log. Use the following command to check logs in real-time:

sudo tail -f /var/log/qz-treadmill-monitor.log

(optional) Enable overlay FS

Once that everything is working as expected, and if you dedicate your Raspberry Pi to this usage, you might want to enable the read-only overlay FS.

By enabling the overlay read-only system, your SD card will be read-only only and every file written will be to RAM. Then at each reboot the RAM is erased and you'll revert to the initial status of the overlay file-system.

Why doing that ?

  • Your SD card will not suffer any write operation and will life longer.
  • The risk of corruption of your FS is reduced (in case of unproper shutdown in example)
  • This operation is reversible.

ATTENTION
After enabling the overlay filesystem any change on your SDCard will become a temporary change.
In the case you need to update qdomyos-zwift (in example), you'll need to disable the overlay filesystem first.

sudo raspi-config > Performance > Overlay File system

"Would you like the overlay filesystem to be enabled ?" YES.

"Would you like the boot partition to be read-only ?" YES.

Reboot immediately.

Other tricks

I use some 3m magic scratches to attach my Raspberry to my bike. I use the USB port from the bike console (always powered as long as the bike is plugged to main), maximum power is 500mA and this is enough for the Raspberry.

You can easily remove the Raspberry Pi from the bike if required.

Trouobleshooting QZ on RPI

Run qz as root

For Zwift, check Zwift detects QZ. Check bluetooth

If Zwift isn't detecting speed from your exercise device, double check your .conf is correct. If you're not sure, Check the setup works using iPhone/Android phone, then replicate the settings by using Raspberry Pi Desktop OS and qz -qml to view the QZ UI. Change settings to match working iPhone/Android.