windows: add characteristic read, write and notify operations
This commit is contained in:
parent
1b30ec4619
commit
e843ce91a6
2 changed files with 193 additions and 0 deletions
2
Makefile
2
Makefile
|
@ -43,6 +43,8 @@ smoketest-linux:
|
|||
smoketest-windows:
|
||||
# Test on Windows.
|
||||
GOOS=windows CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -o /tmp/go-build-discard ./examples/scanner
|
||||
GOOS=windows CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -o /tmp/go-build-discard ./examples/discover
|
||||
GOOS=windows CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc go build -o /tmp/go-build-discard ./examples/heartrate-monitor
|
||||
|
||||
smoketest-macos:
|
||||
# Test on macos.
|
||||
|
|
191
gattc_windows.go
191
gattc_windows.go
|
@ -1,11 +1,26 @@
|
|||
package bluetooth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/saltosystems/winrt-go"
|
||||
"github.com/saltosystems/winrt-go/windows/devices/bluetooth"
|
||||
"github.com/saltosystems/winrt-go/windows/devices/bluetooth/genericattributeprofile"
|
||||
"github.com/saltosystems/winrt-go/windows/foundation"
|
||||
"github.com/saltosystems/winrt-go/windows/storage/streams"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoWrite = errors.New("bluetooth: write not supported")
|
||||
errNoWriteWithoutResponse = errors.New("bluetooth: write without response not supported")
|
||||
errWriteFailed = errors.New("bluetooth: write failed")
|
||||
errNoRead = errors.New("bluetooth: read not supported")
|
||||
errNoNotify = errors.New("bluetooth: notify not supported")
|
||||
errEnableNotificationsFailed = errors.New("bluetooth: enable notifications failed")
|
||||
)
|
||||
|
||||
// DiscoverServices starts a service discovery procedure. Pass a list of service
|
||||
|
@ -225,3 +240,179 @@ func (c *DeviceCharacteristic) UUID() UUID {
|
|||
func (c *DeviceCharacteristic) Properties() uint32 {
|
||||
return uint32(c.properties)
|
||||
}
|
||||
|
||||
// Write replaces the characteristic value with a new value. The
|
||||
// call will return after all data has been written.
|
||||
func (c DeviceCharacteristic) Write(p []byte) (n int, err error) {
|
||||
if c.properties&genericattributeprofile.GattCharacteristicPropertiesWrite == 0 {
|
||||
return 0, errNoWrite
|
||||
}
|
||||
|
||||
return c.write(p, genericattributeprofile.GattWriteOptionWriteWithResponse)
|
||||
}
|
||||
|
||||
// WriteWithoutResponse replaces the characteristic value with a new value. The
|
||||
// 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
|
||||
// "write command" (as opposed to a write request).
|
||||
func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) {
|
||||
if c.properties&genericattributeprofile.GattCharacteristicPropertiesWriteWithoutResponse == 0 {
|
||||
return 0, errNoWriteWithoutResponse
|
||||
}
|
||||
return c.write(p, genericattributeprofile.GattWriteOptionWriteWithoutResponse)
|
||||
}
|
||||
|
||||
func (c DeviceCharacteristic) write(p []byte, mode genericattributeprofile.GattWriteOption) (n int, err error) {
|
||||
// Convert data to buffer
|
||||
writer, err := streams.NewDataWriter()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer writer.Release()
|
||||
|
||||
// Add bytes to writer
|
||||
if err := writer.WriteBytes(uint32(len(p)), p); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := writer.DetachBuffer()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// IAsyncOperation<GattCommunicationStatus>
|
||||
asyncOp, err := c.characteristic.WriteValueWithOptionAsync(value, mode)
|
||||
|
||||
if err := awaitAsyncOperation(asyncOp, genericattributeprofile.SignatureGattCommunicationStatus); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
res, err := asyncOp.GetResults()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
status := genericattributeprofile.GattCommunicationStatus(uintptr(res))
|
||||
|
||||
// Is the status success?
|
||||
if status != genericattributeprofile.GattCommunicationStatusSuccess {
|
||||
return 0, errWriteFailed
|
||||
}
|
||||
|
||||
// Success
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Read reads the current characteristic value.
|
||||
func (c *DeviceCharacteristic) Read(data []byte) (int, error) {
|
||||
if c.properties&genericattributeprofile.GattCharacteristicPropertiesRead == 0 {
|
||||
return 0, errNoRead
|
||||
}
|
||||
|
||||
readOp, err := c.characteristic.ReadValueAsync()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// IAsyncOperation<GattReadResult>
|
||||
if err := awaitAsyncOperation(readOp, genericattributeprofile.SignatureGattReadResult); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
res, err := readOp.GetResults()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
result := (*genericattributeprofile.GattReadResult)(res)
|
||||
|
||||
buffer, err := result.GetValue()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
datareader, err := streams.FromBuffer(buffer)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
bufferlen, err := buffer.GetLength()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
readBuffer, err := datareader.ReadBytes(bufferlen)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
copy(data, readBuffer)
|
||||
return len(readBuffer), nil
|
||||
}
|
||||
|
||||
// EnableNotifications enables notifications in the Client Characteristic
|
||||
// Configuration Descriptor (CCCD). This means that most peripherals will send a
|
||||
// notification with a new value every time the value of the characteristic
|
||||
// changes.
|
||||
func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error {
|
||||
if c.properties&genericattributeprofile.GattCharacteristicPropertiesNotify == 0 {
|
||||
return errNoNotify
|
||||
}
|
||||
|
||||
// listen value changed event
|
||||
// TypedEventHandler<GattCharacteristic,GattValueChangedEventArgs>
|
||||
guid := winrt.ParameterizedInstanceGUID(foundation.GUIDTypedEventHandler, genericattributeprofile.SignatureGattCharacteristic, genericattributeprofile.SignatureGattValueChangedEventArgs)
|
||||
valueChangedEventHandler := foundation.NewTypedEventHandler(ole.NewGUID(guid), func(instance *foundation.TypedEventHandler, sender, args unsafe.Pointer) {
|
||||
valueChangedEvent := (*genericattributeprofile.GattValueChangedEventArgs)(args)
|
||||
|
||||
buf, err := valueChangedEvent.GetCharacteristicValue()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
reader, err := streams.FromBuffer(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer reader.Release()
|
||||
|
||||
buflen, err := buf.GetLength()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := reader.ReadBytes(buflen)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
callback(data)
|
||||
})
|
||||
_, err := c.characteristic.AddValueChanged(valueChangedEventHandler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeOp, err := c.characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(genericattributeprofile.GattClientCharacteristicConfigurationDescriptorValueNotify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// IAsyncOperation<GattCommunicationStatus>
|
||||
if err := awaitAsyncOperation(writeOp, genericattributeprofile.SignatureGattCommunicationStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := writeOp.GetResults()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := genericattributeprofile.GattCommunicationStatus(uintptr(res))
|
||||
|
||||
if result != genericattributeprofile.GattCommunicationStatusSuccess {
|
||||
return errEnableNotificationsFailed
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue