Implement Motion Service

This commit is contained in:
Elara 2021-10-22 12:59:51 -07:00
parent 1dde7f9b07
commit 45baea1048

View File

@ -17,6 +17,8 @@ const BTName = "InfiniTime"
const ( const (
NewAlertChar = "00002a46-0000-1000-8000-00805f9b34fb" NewAlertChar = "00002a46-0000-1000-8000-00805f9b34fb"
NotifEventChar = "00020001-78fc-48fe-8e23-433b3a1942d0" NotifEventChar = "00020001-78fc-48fe-8e23-433b3a1942d0"
StepCountChar = "00030001-78fc-48fe-8e23-433b3a1942d0"
MotionValChar = "00030002-78fc-48fe-8e23-433b3a1942d0"
FirmwareVerChar = "00002a26-0000-1000-8000-00805f9b34fb" FirmwareVerChar = "00002a26-0000-1000-8000-00805f9b34fb"
CurrentTimeChar = "00002a2b-0000-1000-8000-00805f9b34fb" CurrentTimeChar = "00002a2b-0000-1000-8000-00805f9b34fb"
BatteryLvlChar = "00002a19-0000-1000-8000-00805f9b34fb" BatteryLvlChar = "00002a19-0000-1000-8000-00805f9b34fb"
@ -28,6 +30,8 @@ type Device struct {
device *device.Device1 device *device.Device1
newAlertChar *gatt.GattCharacteristic1 newAlertChar *gatt.GattCharacteristic1
notifEventChar *gatt.GattCharacteristic1 notifEventChar *gatt.GattCharacteristic1
stepCountChar *gatt.GattCharacteristic1
motionValChar *gatt.GattCharacteristic1
fwVersionChar *gatt.GattCharacteristic1 fwVersionChar *gatt.GattCharacteristic1
currentTimeChar *gatt.GattCharacteristic1 currentTimeChar *gatt.GattCharacteristic1
battLevelChar *gatt.GattCharacteristic1 battLevelChar *gatt.GattCharacteristic1
@ -277,6 +281,10 @@ func (i *Device) resolveChars() error {
i.newAlertChar = char i.newAlertChar = char
case NotifEventChar: case NotifEventChar:
i.notifEventChar = char i.notifEventChar = char
case StepCountChar:
i.stepCountChar = char
case MotionValChar:
i.motionValChar = char
case FirmwareVerChar: case FirmwareVerChar:
i.fwVersionChar = char i.fwVersionChar = char
case CurrentTimeChar: case CurrentTimeChar:
@ -330,6 +338,40 @@ func (i *Device) BatteryLevel() (uint8, error) {
return uint8(battLevel[0]), nil return uint8(battLevel[0]), nil
} }
func (i *Device) StepCount() (uint32, error) {
if !i.device.Properties.Connected {
return 0, nil
}
stepCountData, err := i.stepCountChar.ReadValue(nil)
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint32(stepCountData), nil
}
type MotionValues struct {
X int16
Y int16
Z int16
}
func (i *Device) Motion() (MotionValues, error) {
out := MotionValues{}
if !i.device.Properties.Connected {
return out, nil
}
motionVals, err := i.motionValChar.ReadValue(nil)
if err != nil {
return out, nil
}
motionValReader := bytes.NewReader(motionVals)
err = binary.Read(motionValReader, binary.LittleEndian, &out)
if err != nil {
return out, err
}
return out, nil
}
func (i *Device) HeartRate() (uint8, error) { func (i *Device) HeartRate() (uint8, error) {
if !i.device.Properties.Connected { if !i.device.Properties.Connected {
return 0, nil return 0, nil
@ -407,6 +449,100 @@ func (i *Device) WatchBatteryLevel() (<-chan uint8, error) {
return out, nil return out, nil
} }
func (i *Device) WatchStepCount() (<-chan uint32, func(), error) {
if !i.device.Properties.Connected {
return make(<-chan uint32), nil, nil
}
// Start notifications on step count characteristic
err := i.stepCountChar.StartNotify()
if err != nil {
return nil, nil, err
}
// Watch properties of step count characteristic
ch, err := i.stepCountChar.WatchProperties()
if err != nil {
return nil, nil, err
}
out := make(chan uint32, 2)
currentStepCount, err := i.StepCount()
if err != nil {
return nil, nil, err
}
out <- currentStepCount
cancel, done := cancelFunc()
go func() {
// For every event
for event := range ch {
select {
case <-done:
close(out)
close(done)
i.stepCountChar.StopNotify()
return
default:
// If value changed
if event.Name == "Value" {
// Send step count to channel
out <- binary.LittleEndian.Uint32(event.Value.([]byte))
}
}
}
}()
return out, cancel, nil
}
func (i *Device) WatchMotion() (<-chan MotionValues, func(), error) {
if !i.device.Properties.Connected {
return make(<-chan MotionValues), nil, nil
}
// Start notifications on motion characteristic
err := i.motionValChar.StartNotify()
if err != nil {
return nil, nil, err
}
// Watch properties of motion characteristic
ch, err := i.motionValChar.WatchProperties()
if err != nil {
return nil, nil, err
}
out := make(chan MotionValues, 2)
motionVals, err := i.Motion()
if err != nil {
return nil, nil, err
}
out <- motionVals
cancel, done := cancelFunc()
go func() {
// For every event
for event := range ch {
select {
case <-done:
close(out)
close(done)
i.motionValChar.StopNotify()
return
default:
// If value changed
if event.Name == "Value" {
// Read binary into MotionValues struct
binary.Read(bytes.NewReader(event.Value.([]byte)), binary.LittleEndian, &motionVals)
// Send step count to channel
out <- motionVals
}
}
}
}()
return out, cancel, nil
}
func cancelFunc() (func(), chan struct{}) {
done := make(chan struct{}, 1)
return func() {
done <- struct{}{}
}, done
}
// SetTime sets the watch's time using the Current Time Service // SetTime sets the watch's time using the Current Time Service
func (i *Device) SetTime(t time.Time) error { func (i *Device) SetTime(t time.Time) error {
if !i.device.Properties.Connected { if !i.device.Properties.Connected {