Ant+ to bluetooth bridge
Go to file
2026-05-27 00:36:06 +02:00
.opencode refactor: remove init() and package-level rootCmd, add error logging 2026-05-27 00:36:06 +02:00
cmd/bridge refactor: replace magic hex constants with named constants 2026-05-27 00:36:06 +02:00
go.mod feat: add CLI with Cobra and timestamped logging 2026-05-27 00:36:06 +02:00
go.sum feat: add CLI with Cobra and timestamped logging 2026-05-27 00:36:06 +02:00
LICENSE Initial commit 2026-05-25 11:00:13 +02:00
Makefile removed unnecessary download. 2026-05-27 00:36:06 +02:00
out refactor: remove init() and package-level rootCmd, add error logging 2026-05-27 00:36:06 +02:00
README.md some minor doc updates. 2026-05-27 00:36:06 +02:00
setup.md docs: add README with setup instructions and tested hardware 2026-05-27 00:36:06 +02:00

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

  1. Set up the USB dongle — Follow the Setup instructions to prevent the usbserial driver from claiming it
  2. Build:
    make build
    
  3. Run (requires root):
    sudo ./bin/bridge
    
  4. 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

  1. Opens a USB ANT+ dongle and listens for broadcast messages from a specific trainer device number.
  2. Parses power (Watts) and cadence (RPM) from the trainer's ANT+ data.
  3. 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, product 0x1008)
  • An ANT+ cycling power trainer

Software

  • Linux
  • BlueZ 5.50+ (for Bluetooth management)
  • D-Bus (for BlueZ communication)
  • hciconfig (usually part of the bluez-utils or bluez-tools package)

Note: This program must be run as root to access the USB dongle.

Tested hardware

  • USB dongle: ANT USBStick2 (Dynastream Innovations, vendor 0x0fcf, product 0x1008)

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

# Use defaults (device 3001)
sudo ./bin/bridge

# Custom trainer device
sudo ./bin/bridge --device 0xbb9

# Custom vendor/product IDs
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 3001 ANT+ device number (hex or decimal, e.g. 3001 or 0xbb9)
--help Show help
--version Show version info

Finding your trainer's ANT+ device number

Most ANT+ cycling power trainers use device number 3001 (decimal) or 0xbb9 (hex). If that works, you can use the bridge with default settings.

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

  1. Run the bridge: sudo ./bin/bridge
  2. On your computer or phone, scan for BLE devices
  3. Connect to the device named LinuxAntBtBridge
  4. 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 vendor: 0x0fcf/4047
2026-05-26 11:00:34 product: 0x1008/4104
2026-05-26 11:00:34 device: 0xbb9/3001
2026-05-26 11:00:34 BLE advertising...
2026-05-26 11:00:34 bt: watts: 150  rpm: 80  completedRevs: 1234  lastRevTime: 4890

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 time
  • vendor: ... / product: ... / device: ... — Configuration being used
  • BLE advertising... — BLE service is ready and visible to other devices
  • bt: watts: ... — Power and cadence data being broadcast
  • 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 usbserial driver 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
  • Make sure the dongle is close enough to the trainer (within 1-2 meters)
  • Verify the device number is correct (try --device 3001)
  • Check the log output for the listening... message
  • Try a different device number if available

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 characteristic messages