feat: make --device flag optional with 'any' default

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.
This commit is contained in:
Erik Brakkee 2026-05-26 23:06:05 +00:00
parent 19e4e78c75
commit 7f52f0ff68
4 changed files with 43 additions and 28 deletions

View File

@ -119,13 +119,13 @@ The binary is built to `bin/bridge`.
## Usage
```bash
# Use defaults (device 3001)
# Listen to all trainers (default)
sudo ./bin/bridge
# Custom trainer device
# Filter for a specific trainer
sudo ./bin/bridge --device 0xbb9
# Custom vendor/product IDs
# Custom vendor/product IDs with specific device
sudo ./bin/bridge --vendor 0x0fcf --product 0x1008 --device 3001
```
@ -135,14 +135,14 @@ sudo ./bin/bridge --vendor 0x0fcf --product 0x1008 --device 3001
|------|---------|-------------|
| `--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`) |
| `--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
Most ANT+ cycling power trainers use device number `3001` (decimal) or `0xbb9` (hex). If that works, you can use the
bridge with default settings.
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.
@ -179,11 +179,14 @@ Your trainer's power and cadence data should now be available in the app.
```
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
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
@ -191,9 +194,14 @@ 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
- `usb: vendor: ...` / `usb: product: ...` — USB dongle configuration
- `ant: device: ...` — ANT+ device filter (or `any` if listening to all devices)
- `ant: device opened` — USB dongle opened successfully
- `bt: BLE advertising...` — BLE service is ready and visible to other devices
- `ant: scan mode started` / `ant: listening...` — ANT+ receiver is active
- `ant: <id>: power: ...` — Power/cadence data with transmitting device ID
- `bt: watts: ...` — Power/cadence data being broadcast over BLE
- `ant: received msg from ignored device ...` — Message from non-matching device (when `--device` is specified)
- `hciconfig ... failed` — Bluetooth adapter reset warnings (usually harmless)
## Development
@ -230,10 +238,10 @@ make clean # Remove build artifacts
- 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)
- 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

BIN
bridge

Binary file not shown.

View File

@ -56,8 +56,8 @@ func ListenForTrainer(vendorId uint16, productId uint16, deviceNumber uint32,
logMsg("ant: listening...")
for msg := range msgs {
if msg.DeviceNumber() != deviceNumber {
logPrintf("Received msg from ignored ant+ device %v\n", msg.DeviceNumber())
if deviceNumber != 0 && msg.DeviceNumber() != deviceNumber {
logPrintf("ant: received msg from ignored device %v\n", msg.DeviceNumber())
continue
}
//fmt.Printf("raw msg: device type=0x%02x device number=%d content=%x\n",

View File

@ -52,19 +52,26 @@ This allows these trainers to be used with modern devices that don't support ANT
if err != nil {
return fmt.Errorf("invalid --product %q: %w", c.product, err)
}
deviceNumber, err := parseHex(c.device, 32)
if err != nil {
return fmt.Errorf("invalid --device %q: %w", c.device, err)
c.vendorID = uint64(vendorID)
c.productID = uint64(productID)
if c.device != "" {
deviceNumber, err := parseHex(c.device, 32)
if err != nil {
return fmt.Errorf("invalid --device %q: %w", c.device, err)
}
c.deviceNumber = uint32(deviceNumber)
}
c.vendorID = vendorID
c.productID = productID
c.deviceNumber = uint32(deviceNumber)
deviceNumber := c.deviceNumber
logMsgf("ANT Bridge %s built %s", Version, BuildTime)
logMsgf("usb: vendor: 0x%04x/%d", vendorID, vendorID)
logMsgf("usb: product: 0x%04x/%d", productID, productID)
logMsgf("ant: device: 0x%03x/%d", deviceNumber, deviceNumber)
if deviceNumber == 0 {
logMsgf("ant: device: any")
} else {
logMsgf("ant: device: 0x%03x/%d", deviceNumber, deviceNumber)
}
if err := exec.Command("hciconfig", "hci0", "down").Run(); err != nil {
logPrintf("bt: hciconfig down failed: %v\n", err)
@ -83,7 +90,7 @@ This allows these trainers to be used with modern devices that don't support ANT
rootCmd.Flags().String("vendor", "0x0fcf", "USB vendor ID (hex, e.g. 0x0fcf)")
rootCmd.Flags().String("product", "0x1008", "USB product ID (hex, e.g. 0x1008)")
rootCmd.Flags().String("device", "3001", "ANT+ device number (hex or decimal, e.g. 3001 or 0xbb9)")
rootCmd.Flags().String("device", "", "ANT+ device number (hex or decimal). Use 0 or omit to listen to all devices")
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}