From 266de9824a65db5fb270b665d797e928a6ff53ae Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Thu, 22 Oct 2020 19:04:47 +0200 Subject: [PATCH] gattc/*: DeviceCharacteristic Read() implementation Signed-off-by: deadprogram --- Makefile | 2 ++ adapter_nrf528xx.go | 11 +++++++++++ examples/discover/main.go | 12 ++++++++++++ gap_darwin.go | 9 +++++++-- gattc_darwin.go | 34 +++++++++++++++++++++++++++++++--- gattc_linux.go | 11 +++++++++++ gattc_sd.go | 36 ++++++++++++++++++++++++++++++++++++ 7 files changed, 110 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index ed2d1477..c01b6a12 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ smoketest-tinygo: @md5sum test.hex $(TINYGO) build -o test.uf2 -size=short -target=circuitplay-bluefruit ./examples/circuitplay @md5sum test.hex + $(TINYGO) build -o test.uf2 -size=short -target=circuitplay-bluefruit ./examples/discover + @md5sum test.hex $(TINYGO) build -o test.hex -size=short -target=pca10040-s132v6 ./examples/heartrate @md5sum test.hex $(TINYGO) build -o test.hex -size=short -target=reelboard-s140v7 ./examples/ledcolor diff --git a/adapter_nrf528xx.go b/adapter_nrf528xx.go index 2327996f..cbebd141 100644 --- a/adapter_nrf528xx.go +++ b/adapter_nrf528xx.go @@ -219,6 +219,17 @@ func handleEvent() { } } } + case C.BLE_GATTC_EVT_READ_RSP: + readEvent := gattcEvent.params.unionfield_read_rsp() + if debug { + println("evt: read response, data length", readEvent.len) + } + readingCharacteristic.handle_value.Set(readEvent.handle) + readingCharacteristic.offset = readEvent.offset + readingCharacteristic.length = readEvent.len + + // copy read event data into Go slice + copy(readingCharacteristic.value, (*[255]byte)(unsafe.Pointer(&readEvent.data[0]))[:readEvent.len:readEvent.len]) case C.BLE_GATTC_EVT_HVX: hvxEvent := gattcEvent.params.unionfield_hvx() switch hvxEvent._type { diff --git a/examples/discover/main.go b/examples/discover/main.go index 1d2a6165..db8c1e87 100644 --- a/examples/discover/main.go +++ b/examples/discover/main.go @@ -17,6 +17,8 @@ package main import ( + "strconv" + "tinygo.org/x/bluetooth" ) @@ -59,6 +61,9 @@ func main() { srvcs, err := device.DiscoverServices(nil) must("discover services", err) + // buffer to retrieve characteristic data + buf := make([]byte, 255) + for _, srvc := range srvcs { println("- service", srvc.UUID().String()) @@ -68,6 +73,13 @@ func main() { } for _, char := range chars { println("-- characteristic", char.UUID().String()) + n, err := char.Read(buf) + if err != nil { + println(" ", err.Error()) + } else { + println(" data bytes", strconv.Itoa(n)) + println(" value =", string(buf[:n])) + } } } diff --git a/gap_darwin.go b/gap_darwin.go index c062da03..19b4af8d 100644 --- a/gap_darwin.go +++ b/gap_darwin.go @@ -156,12 +156,17 @@ func (pd *peripheralDelegate) DidDiscoverCharacteristics(prph cbgo.Peripheral, s } // DidUpdateValueForCharacteristic is called when the characteristic for a Service -// for a Peripheral receives a notification with a new value. +// for a Peripheral receives a notification with a new value, +// or receives a value for a read request. func (pd *peripheralDelegate) DidUpdateValueForCharacteristic(prph cbgo.Peripheral, chr cbgo.Characteristic, err error) { uuid, _ := ParseUUID(chr.UUID().String()) if char, ok := pd.d.characteristics[uuid]; ok { - if char != nil && char.callback != nil { + if err == nil && char.callback != nil { go char.callback(chr.Value()) } + + if char.readChan != nil { + char.readChan <- err + } } } diff --git a/gattc_darwin.go b/gattc_darwin.go index 91cc06b7..c89f6573 100644 --- a/gattc_darwin.go +++ b/gattc_darwin.go @@ -92,9 +92,11 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter for _, dchar := range s.service.Characteristics() { uuid, _ := ParseUUID(dchar.UUID().String()) char := DeviceCharacteristic{ - uuidWrapper: uuid, - service: s, - characteristic: dchar, + deviceCharacteristic: &deviceCharacteristic{ + uuidWrapper: uuid, + service: s, + characteristic: dchar, + }, } chars = append(chars, char) s.device.characteristics[char.uuidWrapper] = &char @@ -108,12 +110,17 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter // DeviceCharacteristic is a BLE characteristic on a connected peripheral // device. type DeviceCharacteristic struct { + *deviceCharacteristic +} + +type deviceCharacteristic struct { uuidWrapper service *DeviceService characteristic cbgo.Characteristic callback func(buf []byte) + readChan chan error } // UUID returns the UUID for this DeviceCharacteristic. @@ -145,3 +152,24 @@ func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) err return nil } + +// Read reads the current characteristic value. +func (c *deviceCharacteristic) Read(data []byte) (n int, err error) { + c.readChan = make(chan error) + c.service.device.prph.ReadCharacteristic(c.characteristic) + + // wait for result + select { + case err := <-c.readChan: + c.readChan = nil + if err != nil { + return 0, err + } + case <-time.NewTimer(10 * time.Second).C: + c.readChan = nil + return 0, errors.New("timeout on Read()") + } + + copy(data, c.characteristic.Value()) + return len(c.characteristic.Value()), nil +} diff --git a/gattc_linux.go b/gattc_linux.go index 2b89bcd7..8a8c5dc3 100644 --- a/gattc_linux.go +++ b/gattc_linux.go @@ -231,3 +231,14 @@ func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) err }() return c.characteristic.StartNotify() } + +// Read reads the current characteristic value. +func (c *DeviceCharacteristic) Read(data []byte) (int, error) { + options := make(map[string]interface{}) + result, err := c.characteristic.ReadValue(options) + if err != nil { + return 0, err + } + copy(data, result) + return len(result), nil +} diff --git a/gattc_sd.go b/gattc_sd.go index 716ba861..cb5ad7a0 100644 --- a/gattc_sd.go +++ b/gattc_sd.go @@ -414,3 +414,39 @@ func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) err }) return makeError(errCode) } + +// A global used to pass information from the event handler back to the +// Read function below. +var readingCharacteristic struct { + handle_value volatile.Register16 + offset uint16 + length uint16 + value []byte +} + +// Read reads the current characteristic value up to MTU length. +// A future enhancement would be to be able to retrieve a longer +// value by making multiple calls. +func (c *DeviceCharacteristic) Read(data []byte) (n int, err error) { + // global will copy bytes from read operation into data slice + readingCharacteristic.value = data + + errCode := C.sd_ble_gattc_read(c.connectionHandle, c.valueHandle, 0) + if errCode != 0 { + return 0, Error(errCode) + } + + // wait for response with data + for readingCharacteristic.handle_value.Get() == 0 { + arm.Asm("wfe") + } + + // how much data was read into buffer + n = int(readingCharacteristic.length) + + // prepare for next read + readingCharacteristic.handle_value.Set(0) + readingCharacteristic.length = 0 + + return +}