When --device is omitted or set to 0, the bridge listens to all broadcasting ANT+ trainers instead of filtering by a specific device. The actual transmitting device ID is shown in data logs, allowing users to discover and then filter specific trainers. Also fix vendorID/productID not being assigned to config struct which caused ListenForTrainer to receive 0 for both values.
8.3 KiB
ANT+ to Bluetooth LE Bridge
Bridge an ANT+ cycling power trainer over Bluetooth Low Energy, allowing modern devices that don't support ANT+ to receive power and cadence data.
Quick Start
- Set up the USB dongle — Follow the Setup instructions to prevent the
usbserialdriver from claiming it - Build:
make build - Run (requires root):
sudo ./bin/bridge - Connect — On your computer or phone, scan for BLE devices and connect to
LinuxAntBtBridge.
That's it. Your trainer's power and cadence data should now be available over BLE.
Why this exists
Modern training apps and devices (phone apps, many power meter displays) support Bluetooth LE but lack ANT+ radios. Previously, hardware-based Bluetooth-to-ANT+ bridges were available for this purpose, but these have now been discontinued. This project provides a simple, software-based alternative using a standard USB ANT+ dongle. This is useful if the hardware device breaks and cannot be replaced when the trainer is still functioning perfectly.
This bridge listens to ANT+ data from a trainer and re-broadcasts it as a standard Cycling Power Profile service over BLE.
How it works
- Opens a USB ANT+ dongle and listens for broadcast messages from a specific trainer device number.
- Parses power (Watts) and cadence (RPM) from the trainer's ANT+ data.
- Exposes a standard Cycling Power Profile BLE service via the system's Bluetooth adapter, updating values as new ANT+ messages arrive.
Requirements
Hardware
- A Bluetooth adapter (built-in or USB)
- An ANT+ USB dongle (tested: ANT USBStick2, vendor
0x0fcf, product0x1008) - An ANT+ cycling power trainer
Software
- Linux: tested on Debian 13.
- BlueZ 5.50+ (for Bluetooth management)
- D-Bus (for BlueZ communication)
hciconfig(usually part of thebluez-utilsorbluez-toolspackage)
Note: This program must be run as root to access the USB dongle.
Tested hardware
- USB dongle: ANT USBStick2 (Dynastream Innovations, vendor
0x0fcf, product0x1008)
Setup
The Linux usbserial driver must be prevented from claiming the ANT+ dongle.
Find your dongle's USB IDs
If you're not using the tested dongle, find your dongle's vendor and product IDs:
lsusb
Look for a line with "ANT" in the description. Note the vendor ID (e.g., 0fcf) and product ID (e.g., 1008).
Create the udev rule
Create the following udev rule to prevent usbserial from claiming the dongle:
sudo tee /etc/udev/rules.d/99-ant-usb.rules > /dev/null <<EOF
SUBSYSTEM=="usb", ATTRS{idVendor}=="0fcf", ATTRS{idProduct}=="1008", ENV{MODALIAS}="ignore"
EOF
Replace 0fcf and 1008 with your dongle's actual vendor and product IDs if different.
Reload the rules
sudo modprobe -r usb_serial_simple
sudo udevadm control --reload-rules
sudo udevadm trigger
Verify
Reconnect the dongle and check dmesg:
dmesg | tail -5
Expected output (no usbserial messages):
usb 3-1.2.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 3-1.2.1: Product: ANT USBStick2
usb 3-1.2.1: Manufacturer: Dynastream Innovations
If you see a line like usb_serial converter now attached to ttyUSB0, the udev rule did not apply.
Installation
make build
The binary is built to bin/bridge.
Usage
# Listen to all trainers (default)
sudo ./bin/bridge
# Filter for a specific trainer
sudo ./bin/bridge --device 0xbb9
# Custom vendor/product IDs with specific device
sudo ./bin/bridge --vendor 0x0fcf --product 0x1008 --device 3001
CLI Flags
| Flag | Default | Description |
|---|---|---|
--vendor |
0x0fcf |
USB vendor ID (hex or decimal, e.g. 0x0fcf or 4047) |
--product |
0x1008 |
USB product ID (hex or decimal) |
--device |
0 (any) |
ANT+ device number (hex or decimal). Use 0 or omit to listen to all devices |
--help |
Show help | |
--version |
Show version info |
Finding your trainer's ANT+ device number
The bridge listens to all broadcasting devices by default. When multiple ANT+ trainers are nearby, each will appear in the
log output as ant: <device_id>: power: .... Use --device <id> to filter for a specific trainer.
If you're unsure of your trainer's device number, you can discover it using another ANT+ device (e.g., a Garmin Edge head unit). Connect the Garmin to your trainer and look for the device number in the ANT+ sensor list.
You can also try common device numbers for your trainer brand — most manufacturers use predictable numbers for their devices.
Connecting
The bridge exposes a standard Cycling Power Profile service over BLE. Any app that supports this profile will work — no custom app required.
Tested with:
- Zwift
May also work with other Cycling Power Profile apps:
- TrainingPeaks
- Wahoo SYSTM
- TrainerRoad
- Rouvy
- Kinomap
- GoldenCheetah
Steps to connect
- Run the bridge:
sudo ./bin/bridge - On your computer or phone, scan for BLE devices
- Connect to the device named
LinuxAntBtBridge - Start training in your app
Your trainer's power and cadence data should now be available in the app.
Output
2026-05-26 11:00:34 ANT Bridge abc1234 built 2026-05-26T11:00:00
2026-05-26 11:00:34 usb: vendor: 0x0fcf/4047
2026-05-26 11:00:34 usb: product: 0x1008/4104
2026-05-26 11:00:34 ant: device: any
2026-05-26 11:00:34 ant: device opened
2026-05-26 11:00:34 bt: BLE advertising...
2026-05-26 11:00:34 ant: scan mode started
2026-05-26 11:00:34 ant: listening...
2026-05-26 11:00:35 ant: 3001: power: 150W cadence: 80 rpm
The bridge logs a lot of output during normal operation — this is expected and helpful for debugging during this early stage of development.
Key log lines:
ANT Bridge ...— Startup message with version and build timeusb: vendor: .../usb: product: ...— USB dongle configurationant: device: ...— ANT+ device filter (oranyif listening to all devices)ant: device opened— USB dongle opened successfullybt: BLE advertising...— BLE service is ready and visible to other devicesant: scan mode started/ant: listening...— ANT+ receiver is activeant: <id>: power: ...— Power/cadence data with transmitting device IDbt: watts: ...— Power/cadence data being broadcast over BLEant: received msg from ignored device ...— Message from non-matching device (when--deviceis specified)hciconfig ... failed— Bluetooth adapter reset warnings (usually harmless)
Development
make build # Build with ldflags (version, build time)
make clean # Remove build artifacts
Known issues
- Verbose logging: The bridge produces a lot of log output during normal operation. This is expected and helpful for debugging during this early stage of development.
- Wattage drops: The wattage sometimes drops briefly to 0 but then quickly recovers. If this happens frequently, please report it with your log output.
Troubleshooting
No BLE devices found
- Check that the Bluetooth adapter is enabled:
rfkill list bluetooth - Check that the bridge is advertising: look for
BLE advertising...in the log output - Make sure no other app is using the Bluetooth adapter
ANT+ dongle not found
- Check that the dongle is plugged in:
lsusb | grep 0fcf - Check that the
usbserialdriver is not claiming the dongle:lsmod | grep usb_serial_simple - Verify the udev rule was applied (see Setup)
- Make sure you're running with
sudo
No data from trainer
- Make sure the trainer is turned on and broadcasting
- Use another device (e.g. Garmin Edge) to verify it is broadcasting. This also provides a way to get the device id.
- The bridge listens to all devices by default. If multiple trainers are broadcasting, use
--device <id>to filter for a specific one - Make sure the dongle is close enough to the trainer (within 1-2 meters)
- Check the log output for the
listening...message
BLE connection drops
- Make sure the bridge is within range of the receiving device (within 5-10 meters)
- Check for Bluetooth interference (other BLE devices, Wi-Fi, etc.)
- Check the log output for
failed to write power characteristicmessages