package main import ( "log" "time" "dim13.org/anki" "github.com/currantlabs/gatt" ) func onStateChange(d gatt.Device, s gatt.State) { sid := gatt.MustParseUUID(anki.ServiceUUID) log.Println("State", s) switch s { case gatt.StatePoweredOn: log.Println("Scan...") d.Scan([]gatt.UUID{sid}, false) default: d.StopScanning() } } func onDiscover(p gatt.Peripheral, a *gatt.Advertisement, rssi int) { //defer p.Device().StopScanning() sid := gatt.MustParseUUID(anki.ServiceUUID) for _, s := range a.Services { log.Println("Found", p.ID()) if s.Equal(sid) { log.Println("Connect", p.ID()) p.Device().Connect(p) } } } func onRead(w gatt.ResponseWriter, r *gatt.ReadRequest) { log.Println("READ", r) } func onConnect(p gatt.Peripheral, err error) { ss, err := p.DiscoverServices(nil) if err != nil { log.Println(err) return } for _, s := range ss { cs, err := p.DiscoverCharacteristics(nil, s) if err != nil { log.Println("Discover Service", err) return } for _, c := range cs { /* ds, err := p.DiscoverDescriptors(nil, c) if err != nil { log.Println("Discover Descriptors", err) return } for _, d := range ds { log.Println("Descriptor", d.UUID()) } */ //log.Println("Properties", c.Properties()) if (c.Properties() & (gatt.CharNotify | gatt.CharIndicate)) != 0 { if err := p.SetNotifyValue(c, onNotify); err != nil { log.Println("Set notify", err) return } } if (c.Properties() & (gatt.CharWrite | gatt.CharWriteNR)) != 0 { //p.WriteCharacteristic(c, []byte{0x03, 0x90, 0x01, 0x00}, true) // sdk ? //p.WriteCharacteristic(c, []byte{0x01, 0x8c}, true) // reset stats ? go func(c *gatt.Characteristic) { t := time.NewTicker(time.Second * 10) defer t.Stop() for range t.C { //log.Println("Ping") p.WriteCharacteristic(c, []byte{0x01, 0x16}, true) // ping time.Sleep(10 * time.Second) } }(c) //p.WriteCharacteristic(c, []byte{0x01, 0x18}, true) // version //p.WriteCharacteristic(c, []byte{0x01, 0x1a}, true) // battery lc := &anki.VehicleLightConfig{} lm := &anki.VehicleMsgLightsPattern{} lc.Set(anki.LightFrontL, anki.EffectSteady, 10, 0, 0) lm.Set(*lc) lc.Set(anki.LightFrontR, anki.EffectSteady, 10, 0, 0) lm.Set(*lc) lc.Set(anki.LightTail, anki.EffectSteady, 5, 0, 0) lm.Set(*lc) lb := anki.Encode(lm) p.WriteCharacteristic(c, lb, true) go func(c *gatt.Characteristic) { for i := 1; i <= 5; i++ { time.Sleep(5 * time.Second) log.Println("Set speed") ss := &anki.VehicleMsgSetSpeed{} ss.Set(anki.Speed(100*i), 1000) b := anki.Encode(ss) p.WriteCharacteristic(c, b, true) } }(c) go func(c *gatt.Characteristic) { return // XXX disable for now time.Sleep(15 * time.Second) log.Println("Change lane") cl := &anki.VehicleMsgChangeLane{} cl.Set(100, 100, 0.0) b := anki.Encode(cl) p.WriteCharacteristic(c, b, true) }(c) } } } } func onNotify(c *gatt.Characteristic, b []byte, err error) { if err != nil { log.Println(c.UUID(), err) } id, payload := anki.SplitMsg(b) switch id { case anki.VehicleMsgV2CPingResponse: // ignore case anki.VehicleMsgV2CCollisionDetected: v := anki.VehicleMsgCollisionDetected{} anki.Decode(b, &v) log.Printf("%+v", v) case anki.VehicleMsgV2CCycleOvertime: v := anki.VehicleMsgCycleOvertime{} anki.Decode(b, &v) log.Printf("%+v", v) case anki.VehicleMsgV2CBatteryLevelResponse: v := anki.VehicleMsgBatteryLevelResponse{} anki.Decode(b, &v) log.Printf("%+v", v) case anki.VehicleMsgV2CVersionResponse: v := anki.VehicleMsgVersionResponse{} anki.Decode(b, &v) log.Printf("%+v", v) case anki.VehicleMsgV2CSpeedUpdate: v := anki.VehicleMsgSpeedUpdate{} anki.Decode(b, &v) log.Printf("%+v", v) case anki.VehicleMsgV2CLaneChangeUpdate: v := anki.VehicleMsgLaneChangeUpdate{} anki.Decode(b, &v) log.Printf("%+v", v) case anki.VehicleMsgV2CStatusUpdate: v := anki.VehicleMsgStatusUpdate{} anki.Decode(b, &v) log.Printf("%+v", v) case anki.VehicleMsgV2CLocalizationTransitionUpdate: v := anki.VehicleMsgLocalizationTransitionUpdate{} anki.Decode(b, &v) log.Printf("%+v", v) case anki.VehicleMsgV2CLocalizationPositionUpdate: v := anki.VehicleLocalizationPositionUpdate{} anki.Decode(b, &v) log.Printf("%+v", v) default: log.Printf("ID: %v | % X\n", id, payload) } } func onDisconnect(p gatt.Peripheral, err error) { log.Println("Disconnect", p.ID()) p.Device().CancelConnection(p) } func main() { d, err := gatt.NewDevice() if err != nil { log.Fatal(err) } d.Handle( gatt.PeripheralDiscovered(onDiscover), gatt.PeripheralConnected(onConnect), gatt.PeripheralDisconnected(onDisconnect), ) d.Init(onStateChange) select {} }