Fully functional ant+ to bluetooth bridge.

This commit is contained in:
Erik Brakkee 2026-05-26 18:21:49 +02:00
parent eb6fdd6b16
commit 29d7dc1632
4 changed files with 27 additions and 20 deletions

View File

@ -27,7 +27,8 @@ func resetAndWait(drv io.ReadWriter) error {
} }
} }
func listenForAntDevice(vendorId gousb.ID, productId gousb.ID, deviceNumber uint32) { func ListenForTrainer(vendorId gousb.ID, productId gousb.ID, deviceNumber uint32,
events chan<- Event) {
drv, err := usb.GetDevice(vendorId, productId) drv, err := usb.GetDevice(vendorId, productId)
if err != nil { if err != nil {
panic(err) panic(err)
@ -58,9 +59,18 @@ func listenForAntDevice(vendorId gousb.ID, productId gousb.ID, deviceNumber uint
switch msg.DeviceType() { switch msg.DeviceType() {
case ant.DEVICE_TYPE_POWER: case ant.DEVICE_TYPE_POWER:
// Check data page first
page := msg[4] & 0x7F // mask toggle bit
if page != 0x10 {
continue
}
cur := ant.PowerMessage(msg) cur := ant.PowerMessage(msg)
fmt.Printf("%v: power: %dW cadence: %d rpm\n", fmt.Printf("%v: power: %dW cadence: %d rpm\n",
msg.DeviceNumber(), cur.InstantaneousPower(), cur.InstantaneousCadence()) msg.DeviceNumber(), cur.InstantaneousPower(), cur.InstantaneousCadence())
events <- Event{
Power: cur.InstantaneousPower(),
Cadence: cur.InstantaneousCadence(),
}
} }
} }
} }

View File

@ -17,7 +17,7 @@ func must(err error) {
} }
} }
func ExposeBluetooth() { func ExposeBluetooth(events <-chan Event) {
must(adapter.Enable()) must(adapter.Enable())
var powerChar bluetooth.Characteristic var powerChar bluetooth.Characteristic
@ -49,14 +49,13 @@ func ExposeBluetooth() {
var lastCompletedRev float64 // time in 1/1024s of last completed revolution var lastCompletedRev float64 // time in 1/1024s of last completed revolution
lastUpdate := time.Now() lastUpdate := time.Now()
var elapsedTicks float64 var elapsedTicks float64
for { for event := range events {
now := time.Now() now := time.Now()
deltaT := now.Sub(lastUpdate).Seconds() deltaT := now.Sub(lastUpdate).Seconds()
lastUpdate = now lastUpdate = now
sec := now.Second() watts := event.Power
watts := uint16(100 + sec) rpm := float64(event.Cadence)
rpm := float64(50 + sec)
totalRevs += rpm / 60.0 * deltaT totalRevs += rpm / 60.0 * deltaT
elapsedTicks += deltaT * 1024.0 elapsedTicks += deltaT * 1024.0
@ -77,7 +76,5 @@ func ExposeBluetooth() {
powerChar.Write(payload) powerChar.Write(payload)
fmt.Printf("watts: %d rpm: %.0f completedRevs: %.0f lastRevTime: %.0f\n", fmt.Printf("watts: %d rpm: %.0f completedRevs: %.0f lastRevTime: %.0f\n",
watts, rpm, completedRevs, lastCompletedRev) watts, rpm, completedRevs, lastCompletedRev)
time.Sleep(250 * time.Millisecond)
} }
} }

6
cmd/bridge/event.go Normal file
View File

@ -0,0 +1,6 @@
package main
type Event struct {
Power uint16
Cadence uint8
}

View File

@ -1,26 +1,20 @@
package main package main
import ( import (
"fmt"
"os/exec" "os/exec"
"github.com/google/gousb" "github.com/google/gousb"
) )
func oldMain() { func main() {
exec.Command("hciconfig", "hci0", "down").Run()
exec.Command("hciconfig", "hci0", "up").Run()
var vendorId gousb.ID = 0x0fcf var vendorId gousb.ID = 0x0fcf
var productId gousb.ID = 0x1008 var productId gousb.ID = 0x1008
var deviceNumber uint32 = 3001 var deviceNumber uint32 = 3001
listenForAntDevice(vendorId, productId, deviceNumber) events := make(chan Event)
go ListenForTrainer(vendorId, productId, deviceNumber, events)
fmt.Println("channel closed") ExposeBluetooth(events)
}
func main() {
exec.Command("hciconfig", "hci0", "down").Run()
exec.Command("hciconfig", "hci0", "up").Run()
ExposeBluetooth()
} }