Add Linux support
Very much experimental, no BLE service support yet and BLE LocalName in advertisement packet doesn't seem to work. So rather useless at the moment.
This commit is contained in:
parent
6e4cf6f8e0
commit
e453c4d3f9
7 changed files with 138 additions and 11 deletions
39
adapter_linux.go
Normal file
39
adapter_linux.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// +build !baremetal
|
||||||
|
|
||||||
|
package bluetooth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/muka/go-bluetooth/api"
|
||||||
|
"github.com/muka/go-bluetooth/bluez/profile/adapter"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Adapter struct {
|
||||||
|
adapter *adapter.Adapter1
|
||||||
|
id string
|
||||||
|
handler func(Event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultAdapter returns the default adapter on the current system. On Linux,
|
||||||
|
// it will return the first adapter available.
|
||||||
|
func DefaultAdapter() (*Adapter, error) {
|
||||||
|
adapter, err := api.GetDefaultAdapter()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
adapterID, err := adapter.GetAdapterID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Adapter{
|
||||||
|
adapter: adapter,
|
||||||
|
id: adapterID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable configures the BLE stack. It must be called before any
|
||||||
|
// Bluetooth-related calls (unless otherwise indicated).
|
||||||
|
//
|
||||||
|
// The Linux implementation is a no-op.
|
||||||
|
func (a *Adapter) Enable() error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -62,9 +62,15 @@ type Adapter struct {
|
||||||
charWriteHandlers []charWriteHandler
|
charWriteHandlers []charWriteHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultAdapter is an adapter to the default Bluetooth stack on a given
|
// defaultAdapter is an adapter to the default Bluetooth stack on a given
|
||||||
// target.
|
// target.
|
||||||
var DefaultAdapter = &Adapter{isDefault: true}
|
var defaultAdapter = Adapter{isDefault: true}
|
||||||
|
|
||||||
|
// DefaultAdapter returns the default adapter on the current system. On Nordic
|
||||||
|
// chips, it will return the SoftDevice interface.
|
||||||
|
func DefaultAdapter() (*Adapter, error) {
|
||||||
|
return &defaultAdapter, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Enable configures the BLE stack. It must be called before any
|
// Enable configures the BLE stack. It must be called before any
|
||||||
// Bluetooth-related calls (unless otherwise indicated).
|
// Bluetooth-related calls (unless otherwise indicated).
|
||||||
|
@ -108,7 +114,7 @@ func (a *Adapter) Enable() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleEvent() {
|
func handleEvent() {
|
||||||
handler := DefaultAdapter.handler
|
handler := defaultAdapter.handler
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -131,7 +137,7 @@ func handleEvent() {
|
||||||
writeEvent := gattsEvent.params.unionfield_write()
|
writeEvent := gattsEvent.params.unionfield_write()
|
||||||
len := writeEvent.len - writeEvent.offset
|
len := writeEvent.len - writeEvent.offset
|
||||||
data := (*[255]byte)(unsafe.Pointer(&writeEvent.data[0]))[:len:len]
|
data := (*[255]byte)(unsafe.Pointer(&writeEvent.data[0]))[:len:len]
|
||||||
handler := DefaultAdapter.getCharWriteHandler(writeEvent.handle)
|
handler := defaultAdapter.getCharWriteHandler(writeEvent.handle)
|
||||||
if handler != nil {
|
if handler != nil {
|
||||||
handler.callback(Connection(gattsEvent.conn_handle), int(writeEvent.offset), data)
|
handler.callback(Connection(gattsEvent.conn_handle), int(writeEvent.offset), data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ import (
|
||||||
var advPayload = []byte("\x02\x01\x06" + "\x07\x09TinyGo")
|
var advPayload = []byte("\x02\x01\x06" + "\x07\x09TinyGo")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
adapter := bluetooth.DefaultAdapter
|
adapter, err := bluetooth.DefaultAdapter()
|
||||||
|
must("get default adapter", err)
|
||||||
must("enable SD", adapter.Enable())
|
must("enable SD", adapter.Enable())
|
||||||
adv := adapter.NewAdvertisement()
|
adv := adapter.NewAdvertisement()
|
||||||
options := &bluetooth.AdvertiseOptions{
|
options := &bluetooth.AdvertiseOptions{
|
||||||
|
|
|
@ -11,7 +11,8 @@ var advPayload = []byte("\x02\x01\x06" + "\x07\x09TinyGo")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println("starting")
|
println("starting")
|
||||||
adapter := bluetooth.DefaultAdapter
|
adapter, err := bluetooth.DefaultAdapter()
|
||||||
|
must("get default adapter", err)
|
||||||
adapter.SetEventHandler(handleBluetoothEvents)
|
adapter.SetEventHandler(handleBluetoothEvents)
|
||||||
must("enable SD", adapter.Enable())
|
must("enable SD", adapter.Enable())
|
||||||
adv := adapter.NewAdvertisement()
|
adv := adapter.NewAdvertisement()
|
||||||
|
|
5
gap.go
5
gap.go
|
@ -1,10 +1,5 @@
|
||||||
package bluetooth
|
package bluetooth
|
||||||
|
|
||||||
// Advertisement encapsulates a single advertisement instance.
|
|
||||||
type Advertisement struct {
|
|
||||||
handle uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdvertiseOptions configures everything related to BLE advertisements.
|
// AdvertiseOptions configures everything related to BLE advertisements.
|
||||||
type AdvertiseOptions struct {
|
type AdvertiseOptions struct {
|
||||||
Interval AdvertiseInterval
|
Interval AdvertiseInterval
|
||||||
|
|
80
gap_linux.go
Normal file
80
gap_linux.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
// +build !baremetal
|
||||||
|
|
||||||
|
package bluetooth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/muka/go-bluetooth/api"
|
||||||
|
"github.com/muka/go-bluetooth/bluez/profile/advertising"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrMalformedAdvertisement = errors.New("bluetooth: malformed advertisement packet")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Advertisement encapsulates a single advertisement instance.
|
||||||
|
type Advertisement struct {
|
||||||
|
adapter *Adapter
|
||||||
|
advertisement *api.Advertisement
|
||||||
|
properties *advertising.LEAdvertisement1Properties
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAdvertisement creates a new advertisement instance but does not configure
|
||||||
|
// it.
|
||||||
|
func (a *Adapter) NewAdvertisement() *Advertisement {
|
||||||
|
return &Advertisement{
|
||||||
|
adapter: a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure this advertisement.
|
||||||
|
func (a *Advertisement) Configure(broadcastData, scanResponseData []byte, options *AdvertiseOptions) error {
|
||||||
|
if a.advertisement != nil {
|
||||||
|
panic("todo: configure advertisement a second time")
|
||||||
|
}
|
||||||
|
if scanResponseData != nil {
|
||||||
|
panic("todo: scan response data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick-and-dirty advertisement packet parser.
|
||||||
|
a.properties = &advertising.LEAdvertisement1Properties{
|
||||||
|
Type: advertising.AdvertisementTypeBroadcast,
|
||||||
|
Timeout: 1<<16 - 1,
|
||||||
|
}
|
||||||
|
for len(broadcastData) != 0 {
|
||||||
|
if len(broadcastData) < 2 {
|
||||||
|
return ErrMalformedAdvertisement
|
||||||
|
}
|
||||||
|
fieldLength := broadcastData[0]
|
||||||
|
fieldType := broadcastData[1]
|
||||||
|
fieldValue := broadcastData[2 : fieldLength+1]
|
||||||
|
if int(fieldLength) > len(broadcastData) {
|
||||||
|
return ErrMalformedAdvertisement
|
||||||
|
}
|
||||||
|
switch fieldType {
|
||||||
|
case 1:
|
||||||
|
// BLE advertisement flags. Ignore.
|
||||||
|
case 9:
|
||||||
|
// Complete Local Name.
|
||||||
|
a.properties.LocalName = string(fieldValue)
|
||||||
|
default:
|
||||||
|
return ErrMalformedAdvertisement
|
||||||
|
}
|
||||||
|
broadcastData = broadcastData[fieldLength+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start advertisement. May only be called after it has been configured.
|
||||||
|
func (a *Advertisement) Start() error {
|
||||||
|
if a.advertisement != nil {
|
||||||
|
panic("todo: start advertisement a second time")
|
||||||
|
}
|
||||||
|
_, err := api.ExposeAdvertisement(a.adapter.id, a.properties, uint32(a.properties.Timeout))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -11,6 +11,11 @@ package bluetooth
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
// Advertisement encapsulates a single advertisement instance.
|
||||||
|
type Advertisement struct {
|
||||||
|
handle uint8
|
||||||
|
}
|
||||||
|
|
||||||
// NewAdvertisement creates a new advertisement instance but does not configure
|
// NewAdvertisement creates a new advertisement instance but does not configure
|
||||||
// it. It can be called before the SoftDevice has been initialized.
|
// it. It can be called before the SoftDevice has been initialized.
|
||||||
func (a *Adapter) NewAdvertisement() *Advertisement {
|
func (a *Adapter) NewAdvertisement() *Advertisement {
|
||||||
|
|
Loading…
Reference in a new issue