Compare commits
3 Commits
release
...
read-chara
Author | SHA1 | Date | |
---|---|---|---|
|
97f17e93f1 | ||
|
47770f6c59 | ||
|
3e31cedee9 |
@ -219,6 +219,16 @@ 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
|
||||||
|
// Create a Go slice from the data.
|
||||||
|
readingCharacteristic.value = (*[255]byte)(unsafe.Pointer(&readEvent.data[0]))[:readEvent.len:readEvent.len]
|
||||||
|
|
||||||
case C.BLE_GATTC_EVT_HVX:
|
case C.BLE_GATTC_EVT_HVX:
|
||||||
hvxEvent := gattcEvent.params.unionfield_hvx()
|
hvxEvent := gattcEvent.params.unionfield_hvx()
|
||||||
switch hvxEvent._type {
|
switch hvxEvent._type {
|
||||||
|
@ -68,6 +68,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
for _, char := range chars {
|
for _, char := range chars {
|
||||||
println("-- characteristic", char.UUID().String())
|
println("-- characteristic", char.UUID().String())
|
||||||
|
val, err := char.Read()
|
||||||
|
if err != nil {
|
||||||
|
println("---", err.Error())
|
||||||
|
} else {
|
||||||
|
println("--- value =", string(val))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,5 +162,9 @@ func (pd *peripheralDelegate) DidUpdateValueForCharacteristic(prph cbgo.Peripher
|
|||||||
if char != nil && char.callback != nil {
|
if char != nil && char.callback != nil {
|
||||||
go char.callback(chr.Value())
|
go char.callback(chr.Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if char.readChan != nil {
|
||||||
|
char.readChan <- nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
//
|
//
|
||||||
// Passing a nil slice of UUIDs will return a complete list of
|
// Passing a nil slice of UUIDs will return a complete list of
|
||||||
// services.
|
// services.
|
||||||
func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
func (d *Device) DiscoverServices(uuids []UUID) ([]*DeviceService, error) {
|
||||||
cbuuids := []cbgo.UUID{}
|
cbuuids := []cbgo.UUID{}
|
||||||
for _, u := range uuids {
|
for _, u := range uuids {
|
||||||
uuid, _ := cbgo.ParseUUID(u.String())
|
uuid, _ := cbgo.ParseUUID(u.String())
|
||||||
@ -29,16 +29,16 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
|||||||
// wait on channel for service discovery
|
// wait on channel for service discovery
|
||||||
select {
|
select {
|
||||||
case <-d.servicesChan:
|
case <-d.servicesChan:
|
||||||
svcs := []DeviceService{}
|
svcs := []*DeviceService{}
|
||||||
for _, dsvc := range d.prph.Services() {
|
for _, dsvc := range d.prph.Services() {
|
||||||
uuid, _ := ParseUUID(dsvc.UUID().String())
|
uuid, _ := ParseUUID(dsvc.UUID().String())
|
||||||
svc := DeviceService{
|
svc := &DeviceService{
|
||||||
uuidWrapper: uuid,
|
uuidWrapper: uuid,
|
||||||
device: d,
|
device: d,
|
||||||
service: dsvc,
|
service: dsvc,
|
||||||
}
|
}
|
||||||
svcs = append(svcs, svc)
|
svcs = append(svcs, svc)
|
||||||
d.services[svc.uuidWrapper] = &svc
|
d.services[svc.uuidWrapper] = svc
|
||||||
}
|
}
|
||||||
return svcs, nil
|
return svcs, nil
|
||||||
case <-time.NewTimer(10 * time.Second).C:
|
case <-time.NewTimer(10 * time.Second).C:
|
||||||
@ -73,7 +73,7 @@ func (s *DeviceService) UUID() UUID {
|
|||||||
//
|
//
|
||||||
// Passing a nil slice of UUIDs will return a complete list of
|
// Passing a nil slice of UUIDs will return a complete list of
|
||||||
// characteristics.
|
// characteristics.
|
||||||
func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error) {
|
func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]*DeviceCharacteristic, error) {
|
||||||
cbuuids := []cbgo.UUID{}
|
cbuuids := []cbgo.UUID{}
|
||||||
for _, u := range uuids {
|
for _, u := range uuids {
|
||||||
uuid, _ := cbgo.ParseUUID(u.String())
|
uuid, _ := cbgo.ParseUUID(u.String())
|
||||||
@ -88,16 +88,16 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
|||||||
// wait on channel for characteristic discovery
|
// wait on channel for characteristic discovery
|
||||||
select {
|
select {
|
||||||
case <-s.device.charsChan:
|
case <-s.device.charsChan:
|
||||||
chars := []DeviceCharacteristic{}
|
chars := []*DeviceCharacteristic{}
|
||||||
for _, dchar := range s.service.Characteristics() {
|
for _, dchar := range s.service.Characteristics() {
|
||||||
uuid, _ := ParseUUID(dchar.UUID().String())
|
uuid, _ := ParseUUID(dchar.UUID().String())
|
||||||
char := DeviceCharacteristic{
|
char := &DeviceCharacteristic{
|
||||||
uuidWrapper: uuid,
|
uuidWrapper: uuid,
|
||||||
service: s,
|
service: s,
|
||||||
characteristic: dchar,
|
characteristic: dchar,
|
||||||
}
|
}
|
||||||
chars = append(chars, char)
|
chars = append(chars, char)
|
||||||
s.device.characteristics[char.uuidWrapper] = &char
|
s.device.characteristics[char.uuidWrapper] = char
|
||||||
}
|
}
|
||||||
return chars, nil
|
return chars, nil
|
||||||
case <-time.NewTimer(10 * time.Second).C:
|
case <-time.NewTimer(10 * time.Second).C:
|
||||||
@ -114,6 +114,7 @@ type DeviceCharacteristic struct {
|
|||||||
|
|
||||||
characteristic cbgo.Characteristic
|
characteristic cbgo.Characteristic
|
||||||
callback func(buf []byte)
|
callback func(buf []byte)
|
||||||
|
readChan chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UUID returns the UUID for this DeviceCharacteristic.
|
// UUID returns the UUID for this DeviceCharacteristic.
|
||||||
@ -125,7 +126,7 @@ func (c *DeviceCharacteristic) UUID() UUID {
|
|||||||
// call will return before all data has been written. A limited number of such
|
// call will return before all data has been written. A limited number of such
|
||||||
// writes can be in flight at any given time. This call is also known as a
|
// writes can be in flight at any given time. This call is also known as a
|
||||||
// "write command" (as opposed to a write request).
|
// "write command" (as opposed to a write request).
|
||||||
func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) {
|
func (c *DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) {
|
||||||
c.service.device.prph.WriteCharacteristic(p, c.characteristic, false)
|
c.service.device.prph.WriteCharacteristic(p, c.characteristic, false)
|
||||||
|
|
||||||
return len(p), nil
|
return len(p), nil
|
||||||
@ -135,7 +136,7 @@ func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error)
|
|||||||
// Configuration Descriptor (CCCD). This means that most peripherals will send a
|
// Configuration Descriptor (CCCD). This means that most peripherals will send a
|
||||||
// notification with a new value every time the value of the characteristic
|
// notification with a new value every time the value of the characteristic
|
||||||
// changes.
|
// changes.
|
||||||
func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error {
|
func (c *DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error {
|
||||||
if callback == nil {
|
if callback == nil {
|
||||||
return errors.New("must provide a callback for EnableNotifications")
|
return errors.New("must provide a callback for EnableNotifications")
|
||||||
}
|
}
|
||||||
@ -145,3 +146,20 @@ func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) err
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read reads the current characteristic value.
|
||||||
|
func (c *DeviceCharacteristic) Read() (data []byte, err error) {
|
||||||
|
c.readChan = make(chan error)
|
||||||
|
c.service.device.prph.ReadCharacteristic(c.characteristic)
|
||||||
|
|
||||||
|
// wait for result
|
||||||
|
select {
|
||||||
|
case <-c.readChan:
|
||||||
|
c.readChan = nil
|
||||||
|
case <-time.NewTimer(10 * time.Second).C:
|
||||||
|
c.readChan = nil
|
||||||
|
return nil, errors.New("timeout on Read()")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.characteristic.Value(), nil
|
||||||
|
}
|
||||||
|
@ -37,7 +37,7 @@ func (s *DeviceService) UUID() UUID {
|
|||||||
//
|
//
|
||||||
// On Linux with BlueZ, this just waits for the ServicesResolved signal (if
|
// On Linux with BlueZ, this just waits for the ServicesResolved signal (if
|
||||||
// services haven't been resolved yet) and uses this list of cached services.
|
// services haven't been resolved yet) and uses this list of cached services.
|
||||||
func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
func (d *Device) DiscoverServices(uuids []UUID) ([]*DeviceService, error) {
|
||||||
for {
|
for {
|
||||||
resolved, err := d.device.GetServicesResolved()
|
resolved, err := d.device.GetServicesResolved()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -50,7 +50,7 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
|||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
services := []DeviceService{}
|
services := []*DeviceService{}
|
||||||
uuidServices := make(map[string]string)
|
uuidServices := make(map[string]string)
|
||||||
servicesFound := 0
|
servicesFound := 0
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uuid, _ := ParseUUID(service.Properties.UUID)
|
uuid, _ := ParseUUID(service.Properties.UUID)
|
||||||
ds := DeviceService{uuidWrapper: uuid,
|
ds := &DeviceService{uuidWrapper: uuid,
|
||||||
service: service,
|
service: service,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,8 +136,8 @@ func (c *DeviceCharacteristic) UUID() UUID {
|
|||||||
//
|
//
|
||||||
// Passing a nil slice of UUIDs will return a complete
|
// Passing a nil slice of UUIDs will return a complete
|
||||||
// list of characteristics.
|
// list of characteristics.
|
||||||
func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error) {
|
func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]*DeviceCharacteristic, error) {
|
||||||
chars := []DeviceCharacteristic{}
|
chars := []*DeviceCharacteristic{}
|
||||||
uuidChars := make(map[string]string)
|
uuidChars := make(map[string]string)
|
||||||
characteristicsFound := 0
|
characteristicsFound := 0
|
||||||
|
|
||||||
@ -185,7 +185,8 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
|||||||
}
|
}
|
||||||
|
|
||||||
uuid, _ := ParseUUID(char.Properties.UUID)
|
uuid, _ := ParseUUID(char.Properties.UUID)
|
||||||
dc := DeviceCharacteristic{uuidWrapper: uuid,
|
dc := &DeviceCharacteristic{
|
||||||
|
uuidWrapper: uuid,
|
||||||
characteristic: char,
|
characteristic: char,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,7 +206,7 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
|||||||
// call will return before all data has been written. A limited number of such
|
// call will return before all data has been written. A limited number of such
|
||||||
// writes can be in flight at any given time. This call is also known as a
|
// writes can be in flight at any given time. This call is also known as a
|
||||||
// "write command" (as opposed to a write request).
|
// "write command" (as opposed to a write request).
|
||||||
func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) {
|
func (c *DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) {
|
||||||
err = c.characteristic.WriteValue(p, nil)
|
err = c.characteristic.WriteValue(p, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -217,7 +218,7 @@ func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error)
|
|||||||
// Configuration Descriptor (CCCD). This means that most peripherals will send a
|
// Configuration Descriptor (CCCD). This means that most peripherals will send a
|
||||||
// notification with a new value every time the value of the characteristic
|
// notification with a new value every time the value of the characteristic
|
||||||
// changes.
|
// changes.
|
||||||
func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error {
|
func (c *DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error {
|
||||||
ch, err := c.characteristic.WatchProperties()
|
ch, err := c.characteristic.WatchProperties()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -231,3 +232,9 @@ func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) err
|
|||||||
}()
|
}()
|
||||||
return c.characteristic.StartNotify()
|
return c.characteristic.StartNotify()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read reads the current characteristic value.
|
||||||
|
func (c *DeviceCharacteristic) Read() ([]byte, error) {
|
||||||
|
options := make(map[string]interface{})
|
||||||
|
return c.characteristic.ReadValue(options)
|
||||||
|
}
|
||||||
|
48
gattc_sd.go
48
gattc_sd.go
@ -62,7 +62,7 @@ func (s *DeviceService) UUID() UUID {
|
|||||||
//
|
//
|
||||||
// On the Nordic SoftDevice, only one service discovery procedure may be done at
|
// On the Nordic SoftDevice, only one service discovery procedure may be done at
|
||||||
// a time.
|
// a time.
|
||||||
func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
func (d *Device) DiscoverServices(uuids []UUID) ([]*DeviceService, error) {
|
||||||
if discoveringService.state.Get() != 0 {
|
if discoveringService.state.Get() != 0 {
|
||||||
// Not concurrency safe, but should catch most concurrency misuses.
|
// Not concurrency safe, but should catch most concurrency misuses.
|
||||||
return nil, errAlreadyDiscovering
|
return nil, errAlreadyDiscovering
|
||||||
@ -72,7 +72,7 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
|||||||
if len(uuids) > 0 {
|
if len(uuids) > 0 {
|
||||||
sz = len(uuids)
|
sz = len(uuids)
|
||||||
}
|
}
|
||||||
services := make([]DeviceService, 0, sz)
|
services := make([]*DeviceService, 0, sz)
|
||||||
|
|
||||||
var shortUUIDs []C.ble_uuid_t
|
var shortUUIDs []C.ble_uuid_t
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store the discovered service.
|
// Store the discovered service.
|
||||||
svc := DeviceService{
|
svc := &DeviceService{
|
||||||
uuid: suuid,
|
uuid: suuid,
|
||||||
connectionHandle: d.connectionHandle,
|
connectionHandle: d.connectionHandle,
|
||||||
startHandle: startHandle,
|
startHandle: startHandle,
|
||||||
@ -191,7 +191,7 @@ var discoveringCharacteristic struct {
|
|||||||
//
|
//
|
||||||
// Passing a nil slice of UUIDs will return a complete
|
// Passing a nil slice of UUIDs will return a complete
|
||||||
// list of characteristics.
|
// list of characteristics.
|
||||||
func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error) {
|
func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]*DeviceCharacteristic, error) {
|
||||||
if discoveringCharacteristic.handle_value.Get() != 0 {
|
if discoveringCharacteristic.handle_value.Get() != 0 {
|
||||||
return nil, errAlreadyDiscovering
|
return nil, errAlreadyDiscovering
|
||||||
}
|
}
|
||||||
@ -200,7 +200,7 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
|||||||
if len(uuids) > 0 {
|
if len(uuids) > 0 {
|
||||||
sz = len(uuids)
|
sz = len(uuids)
|
||||||
}
|
}
|
||||||
characteristics := make([]DeviceCharacteristic, 0, sz)
|
characteristics := make([]*DeviceCharacteristic, 0, sz)
|
||||||
|
|
||||||
var shortUUIDs []C.ble_uuid_t
|
var shortUUIDs []C.ble_uuid_t
|
||||||
|
|
||||||
@ -276,7 +276,7 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
|||||||
permissions |= CharacteristicIndicatePermission
|
permissions |= CharacteristicIndicatePermission
|
||||||
}
|
}
|
||||||
|
|
||||||
dc := DeviceCharacteristic{uuid: discoveringCharacteristic.uuid}
|
dc := &DeviceCharacteristic{uuid: discoveringCharacteristic.uuid}
|
||||||
dc.permissions = permissions
|
dc.permissions = permissions
|
||||||
dc.valueHandle = foundCharacteristicHandle
|
dc.valueHandle = foundCharacteristicHandle
|
||||||
|
|
||||||
@ -319,7 +319,7 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
|||||||
// call will return before all data has been written. A limited number of such
|
// call will return before all data has been written. A limited number of such
|
||||||
// writes can be in flight at any given time. This call is also known as a
|
// writes can be in flight at any given time. This call is also known as a
|
||||||
// "write command" (as opposed to a write request).
|
// "write command" (as opposed to a write request).
|
||||||
func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) {
|
func (c *DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) {
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@ -337,6 +337,38 @@ func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error)
|
|||||||
return len(p), nil
|
return len(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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() ([]byte, error) {
|
||||||
|
errCode := C.sd_ble_gattc_read(c.connectionHandle, c.valueHandle, 0)
|
||||||
|
if errCode != 0 {
|
||||||
|
return nil, Error(errCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for response with data
|
||||||
|
for readingCharacteristic.handle_value.Get() == 0 {
|
||||||
|
arm.Asm("wfe")
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy data since value is slice to unsafe pointer.
|
||||||
|
data := make([]byte, len(readingCharacteristic.value))
|
||||||
|
copy(data, readingCharacteristic.value)
|
||||||
|
|
||||||
|
// prepare for next read
|
||||||
|
readingCharacteristic.handle_value.Set(0)
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
type gattcNotificationCallback struct {
|
type gattcNotificationCallback struct {
|
||||||
connectionHandle uint16
|
connectionHandle uint16
|
||||||
valueHandle uint16 // may be 0 if the slot is empty
|
valueHandle uint16 // may be 0 if the slot is empty
|
||||||
@ -356,7 +388,7 @@ var gattcNotificationCallbacks []gattcNotificationCallback
|
|||||||
// Warning: when using the SoftDevice, the callback is called from an interrupt
|
// Warning: when using the SoftDevice, the callback is called from an interrupt
|
||||||
// which means there are various limitations (such as not being able to allocate
|
// which means there are various limitations (such as not being able to allocate
|
||||||
// heap memory).
|
// heap memory).
|
||||||
func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error {
|
func (c *DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error {
|
||||||
if c.permissions&CharacteristicNotifyPermission == 0 {
|
if c.permissions&CharacteristicNotifyPermission == 0 {
|
||||||
return errNoNotify
|
return errNoNotify
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user