bluetooth

package module
v0.0.0-...-2d90971 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 3, 2020 License: BSD-3-Clause Imports: 11 Imported by: 0

README

Go Bluetooth

PkgGoDev CircleCI

This package provides a cross-platform Bluetooth Low Energy module for Go that can be used on operating systems such as Linux, macOS, and Windows.

It can also be used running "bare metal" on microcontrollers such as those produced by Nordic Semiconductor.

This example scans for peripheral devices and then displays information about them as they are discovered:

package main

import (
	"github.com/tinygo-org/bluetooth"
)

var adapter = bluetooth.DefaultAdapter

func main() {
	// Enable BLE interface.
	must("enable BLE stack", adapter.Enable())

	// Start scanning.
	println("scanning...")
	err := adapter.Scan(func(adapter *bluetooth.Adapter, device bluetooth.ScanResult) {
		println("found device:", device.Address.String(), device.RSSI, device.LocalName())
	})
	must("start scan", err)
}

func must(action string, err error) {
	if err != nil {
		panic("failed to " + action + ": " + err.Error())
	}
}

Current support

Linux macOS Windows Nordic Semi
API used BlueZ CoreBluetooth WinRT SoftDevice
Scanning
Connect to peripheral
Write peripheral characteristics
Receive notifications
Advertisement
Local services
Local characteristics
Send notifications

Linux

The current support for Linux uses BlueZ via the D-Bus interface. It should work with most distros that support BlueZ such as Ubuntu, Debian, Fedora, and Arch Linux, among others. Linux can be used both as a BLE central as well as BLE peripheral. You need to have a fairly recent version of BlueZ, for example v5.48 is the latest released version for Ubuntu/Debian.

macOS

The current macOS support uses the CoreBluetooth libraries provided by macOS. As a result, it should work with most versions of macOS, although it will require compiling using whatever specific version of XCode is required by your version of the operating system. The macOS support only can only act as a BLE central at this time, with some additional development support needed for full functionality.

Windows

The Windows support is still experimental, and needs additional development to be useful.

Nordic Semiconductor

As you can see above, there is bare metal support for several chips from Nordic Semiconductors.

These chips are supported through TinyGo.

This support also requires firmware provided by Nordic Semi known as the "SoftDevice". The SoftDevice is a binary blob that implements the BLE stack. There are other (open source) BLE stacks, but the SoftDevices are pretty solid and have all the qualifications you might need. Other BLE stacks might be added in the future.

At the moment the following chips are supported:

  • nRF52832 with the S132 SoftDevice (version 6).
  • nRF52840 with the S140 SoftDevice (version 6 and 7).
  • nRF51822 with the S110 SoftDevice (version 8). This SoftDevice does not support all features (e.g. scanning).
Adafruit "Bluefruit" boards

The support for boards created by Adafruit already have the Nordic Semi SoftDevice firmware pre-installed. You can use TinyGo with this package without any additional steps required. Supported boards include:

Flashing the SoftDevice

Other boards that use supported chips from Nordic Semi that do not already have the SoftDevice firmware must have it installed on the board in order to use this package.

Flashing the SoftDevice can be tricky. If you have nrfjprog installed, you can erase the flash and flash the new BLE firmware using the following commands. Replace the path to the hex file with the correct SoftDevice, for example s132_nrf52_6.1.1/s132_nrf52_6.1.1_softdevice.hex for S132 version 6.

nrfjprog -f nrf52 --eraseall
nrfjprog -f nrf52 --program path/to/softdevice.hex

After that, don't reset the board but instead flash a new program to it. For example, you can flash the Heart Rate Sensor example using tinygo (modify the -target flag as needed for your board):

tinygo flash -target=pca10040-s132v6 ./examples/heartrate

Flashing will normally reset the board.

For boards that use the CMSIS-DAP interface (such as the BBC micro:bit), this works a bit different. Flashing the SoftDevice is done by simply copying the .hex file to the device, for example (on Linux):

cp path/to/softdevice.hex /media/yourusername/MICROBIT/

Flashing will then need to be done a bit differently, using the CMSIS-DAP interface instead of the mass-storage interface normally used by TinyGo:

tinygo flash -target=microbit-s110v8 -programmer=cmsis-dap ./examples/heartrate

API stability

The API is not stable! Because many features are not yet implemented and some platforms (e.g. Windows and macOS) are not yet fully supported, it's hard to say what a good API will be. Therefore, if you want stability you should pick a particular git commit and use that. Go modules can be useful for this purpose.

Some things that will probably change:

  • Add options to the Scan method, for example to filter on UUID.
  • Extra options to the Enable function, to request particular features (such as the number of peripheral connections supported).

This package will probably remain unstable until the following has been implemented:

  • Scan filters. For example, to filter on service UUID.
  • Bonding and private addresses.
  • Usable support on at least two desktop operating systems.
  • Maybe some Bluetooth Classic support, such as A2DP.

Contributing

Your contributions are welcome!

Please take a look at our CONTRIBUTING.md document for details.

License

This project is licensed under the BSD 3-clause license, see the LICENSE file for details.

The SoftDevices from Nordic are licensed under a different license, check the license file in the SoftDevice source directory.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultAdapter = &Adapter{}

DefaultAdapter is the default adapter on the system. On Linux, it is the first adapter available.

Make sure to call Enable() before using it to initialize the adapter.

Functions

This section is empty.

Types

type Adapter

type Adapter struct {
	// contains filtered or unexported fields
}

func (*Adapter) AddService

func (a *Adapter) AddService(s *Service) error

AddService creates a new service with the characteristics listed in the Service struct.

func (*Adapter) Connect

func (a *Adapter) Connect(address Addresser, params ConnectionParams) (*Device, error)

Connect starts a connection attempt to the given peripheral device address.

On Linux and Windows, the IsRandom part of the address is ignored.

func (*Adapter) DefaultAdvertisement

func (a *Adapter) DefaultAdvertisement() *Advertisement

DefaultAdvertisement returns the default advertisement instance but does not configure it.

func (*Adapter) Enable

func (a *Adapter) Enable() (err error)

Enable configures the BLE stack. It must be called before any Bluetooth-related calls (unless otherwise indicated).

func (*Adapter) Scan

func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) error

Scan starts a BLE scan. It is stopped by a call to StopScan. A common pattern is to cancel the scan when a particular device has been found.

On Linux with BlueZ, incoming packets cannot be observed directly. Instead, existing devices are watched for property changes. This closely simulates the behavior as if the actual packets were observed, but it has flaws: it is possible some events are missed and perhaps even possible that some events are duplicated.

func (*Adapter) StopScan

func (a *Adapter) StopScan() error

StopScan stops any in-progress scan. It can be called from within a Scan callback to stop the current scan. If no scan is in progress, an error will be returned.

type Address

type Address struct {
	MACAddress
}

Address contains a Bluetooth MAC address.

type Addresser

type Addresser interface {
	// String of the address
	String() string

	// Set the address
	Set(val interface{})

	// Is this address a random address?
	// Bluetooth addresses are roughly split in two kinds: public
	// (IEEE-assigned) addresses and random (not IEEE assigned) addresses.
	// "Random" here doesn't mean it is exactly random but at least it looks
	// random. Sometimes, it contains a hash.
	// For more information:
	// https://www.novelbits.io/bluetooth-address-privacy-ble/
	// Set the address
	SetRandom(bool)
	IsRandom() bool
}

Addresser contains a Bluetooth address, which is a MAC address plus some extra information.

type Advertisement struct {
	// contains filtered or unexported fields
}

Advertisement encapsulates a single advertisement instance.

func (*Advertisement) Configure

func (a *Advertisement) Configure(options AdvertisementOptions) error

Configure this advertisement.

On Linux with BlueZ, it is not possible to set the advertisement interval.

func (*Advertisement) Start

func (a *Advertisement) Start() error

Start advertisement. May only be called after it has been configured.

type AdvertisementFields

type AdvertisementFields struct {
	// The LocalName part of the advertisement (either the complete local name
	// or the shortened local name).
	LocalName string

	// ServiceUUIDs are the services (16-bit or 128-bit) that are broadcast as
	// part of the advertisement packet, in data types such as "complete list of
	// 128-bit UUIDs".
	ServiceUUIDs []UUID
}

AdvertisementFields contains advertisement fields in structured form.

type AdvertisementOptions

type AdvertisementOptions struct {
	// The (complete) local name that will be advertised. Optional, omitted if
	// this is a zero-length string.
	LocalName string

	// ServiceUUIDs are the services (16-bit or 128-bit) that are broadcast as
	// part of the advertisement packet, in data types such as "complete list of
	// 128-bit UUIDs".
	ServiceUUIDs []UUID

	// Interval in BLE-specific units. Create an interval by using NewDuration.
	Interval Duration
}

AdvertisementOptions configures an advertisement instance. More options may be added over time.

type AdvertisementPayload

type AdvertisementPayload interface {
	// LocalName is the (complete or shortened) local name of the device.
	// Please note that many devices do not broadcast a local name, but may
	// broadcast other data (e.g. manufacturer data or service UUIDs) with which
	// they may be identified.
	LocalName() string

	// HasServiceUUID returns true whether the given UUID is present in the
	// advertisement payload as a Service Class UUID. It checks both 16-bit
	// UUIDs and 128-bit UUIDs.
	HasServiceUUID(UUID) bool

	// Bytes returns the raw advertisement packet, if available. It returns nil
	// if this data is not available.
	Bytes() []byte
}

AdvertisementPayload contains information obtained during a scan (see ScanResult). It is provided as an interface as there are two possible implementations: an implementation that works with raw data (usually on low-level BLE stacks) and an implementation that works with structured data.

type Characteristic

type Characteristic struct {
	// contains filtered or unexported fields
}

Characteristic is a single characteristic in a service. It has an UUID and a value.

func (*Characteristic) Write

func (c *Characteristic) Write(p []byte) (n int, err error)

Write replaces the characteristic value with a new value.

type CharacteristicConfig

type CharacteristicConfig struct {
	Handle *Characteristic
	UUID
	Value      []byte
	Flags      CharacteristicPermissions
	WriteEvent func(client Connection, offset int, value []byte)
}

CharacteristicConfig contains some parameters for the configuration of a single characteristic.

The Handle field may be nil. If it is set, it points to a characteristic handle that can be used to access the characteristic at a later time.

type CharacteristicPermissions

type CharacteristicPermissions uint8

CharacteristicPermissions lists a number of basic permissions/capabilities that clients have regarding this characteristic. For example, if you want to allow clients to read the value of this characteristic (a common scenario), set the Read permission.

const (
	CharacteristicBroadcastPermission CharacteristicPermissions = 1 << iota
	CharacteristicReadPermission
	CharacteristicWriteWithoutResponsePermission
	CharacteristicWritePermission
	CharacteristicNotifyPermission
	CharacteristicIndicatePermission
)

Characteristic permission bitfields.

func (CharacteristicPermissions) Broadcast

func (p CharacteristicPermissions) Broadcast() bool

Broadcast returns whether broadcasting of the value is permitted.

func (CharacteristicPermissions) Read

Read returns whether reading of the value is permitted.

func (CharacteristicPermissions) Write

func (p CharacteristicPermissions) Write() bool

Write returns whether writing of the value with Write Request is permitted.

func (CharacteristicPermissions) WriteWithoutResponse

func (p CharacteristicPermissions) WriteWithoutResponse() bool

WriteWithoutResponse returns whether writing of the value with Write Command is permitted.

type Connection

type Connection uint16

Connection is a numeric identifier that indicates a connection handle.

type ConnectionParams

type ConnectionParams struct {
	// The timeout for the connection attempt. Not used during the rest of the
	// connection. If no duration is specified, a default timeout will be used.
	ConnectionTimeout Duration

	// Minimum and maximum connection interval. The shorter the interval, the
	// faster data can travel between both devices but also the more power they
	// will draw. If no intervals are specified, a default connection interval
	// will be used.
	MinInterval Duration
	MaxInterval Duration
}

ConnectionParams are used when connecting to a peripherals.

type Device

type Device struct {
	// contains filtered or unexported fields
}

Device is a connection to a remote peripheral.

func (*Device) DiscoverServices

func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error)

DiscoverServices starts a service discovery procedure. Pass a list of service UUIDs you are interested in to this function. Either a slice of all services is returned (of the same length as the requested UUIDs and in the same order), or if some services could not be discovered an error is returned.

Passing a nil slice of UUIDs will return a complete list of services.

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.

type DeviceCharacteristic

type DeviceCharacteristic struct {
	UUID
	// contains filtered or unexported fields
}

DeviceCharacteristic is a BLE characteristic on a connected peripheral device.

func (DeviceCharacteristic) EnableNotifications

func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error

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 (DeviceCharacteristic) WriteWithoutResponse

func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error)

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).

type DeviceService

type DeviceService struct {
	UUID
	// contains filtered or unexported fields
}

DeviceService is a BLE service on a connected peripheral device.

func (*DeviceService) DiscoverCharacteristics

func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error)

DiscoverCharacteristics discovers characteristics in this service. Pass a list of characteristic UUIDs you are interested in to this function. Either a list of all requested services is returned, or if some services could not be discovered an error is returned. If there is no error, the characteristics slice has the same length as the UUID slice with characteristics in the same order in the slice as in the requested UUID list.

Passing a nil slice of UUIDs will return a complete list of characteristics.

type Duration

type Duration uint16

Duration is the unit of time used in BLE, in 0.625µs units. This unit of time is used throughout the BLE stack.

func NewDuration

func NewDuration(interval time.Duration) Duration

NewDuration returns a new Duration, in units of 0.625µs. It is used both for advertisement intervals and for connection parameters.

type MAC

type MAC [6]byte

MAC represents a MAC address, in little endian format.

func ParseMAC

func ParseMAC(s string) (mac MAC, err error)

ParseMAC parses the given MAC address, which must be in 11:22:33:AA:BB:CC format. If it cannot be parsed, an error is returned.

func (MAC) String

func (mac MAC) String() string

String returns a human-readable version of this MAC address, such as 11:22:33:AA:BB:CC.

type MACAddress

type MACAddress struct {
	// MAC address of the Bluetooth device.
	MAC
	// contains filtered or unexported fields
}

MACAddress contains a Bluetooth address which is a MAC address.

func (MACAddress) IsRandom

func (mac MACAddress) IsRandom() bool

IsRandom if the address is randomly created.

func (MACAddress) Set

func (mac MACAddress) Set(val interface{})

Set the address

func (MACAddress) SetRandom

func (mac MACAddress) SetRandom(val bool)

SetRandom if is a random address.

type ScanResult

type ScanResult struct {
	// Bluetooth address of the scanned device.
	Address Addresser

	// RSSI the last time a packet from this device has been received.
	RSSI int16

	// The data obtained from the advertisement data, which may contain many
	// different properties.
	// Warning: this data may only stay valid until the next event arrives. If
	// you need any of the fields to stay alive until after the callback
	// returns, copy them.
	AdvertisementPayload
}

ScanResult contains information from when an advertisement packet was received. It is passed as a parameter to the callback of the Scan method.

type Service

type Service struct {
	UUID
	Characteristics []CharacteristicConfig
	// contains filtered or unexported fields
}

Service is a GATT service to be used in AddService.

type UUID

type UUID [4]uint32

UUID is a single UUID as used in the Bluetooth stack. It is represented as a [4]uint32 instead of a [16]byte for efficiency.

func New16BitUUID

func New16BitUUID(shortUUID uint16) UUID

New16BitUUID returns a new 128-bit UUID based on a 16-bit UUID.

Note: only use registered UUIDs. See https://www.bluetooth.com/specifications/gatt/services/ for a list.

func NewUUID

func NewUUID(uuid [16]byte) UUID

NewUUID returns a new UUID based on the 128-bit (or 16-byte) input.

func ParseUUID

func ParseUUID(s string) (uuid UUID, err error)

ParseUUID parses the given UUID, which must be in 00001234-0000-1000-8000-00805f9b34fb format. This means that it cannot (yet) parse 16-bit UUIDs unless they are serialized as a 128-bit UUID. If the UUID cannot be parsed, an error is returned. It will always successfully parse UUIDs generated by UUID.String().

func (UUID) Bytes

func (uuid UUID) Bytes() [16]byte

Bytes returns a 16-byte array containing the raw UUID.

func (UUID) Get16Bit

func (uuid UUID) Get16Bit() uint16

Get16Bit returns the 16-bit version of this UUID. This is only valid if it actually is a 16-bit UUID, see Is16Bit.

func (UUID) Is16Bit

func (uuid UUID) Is16Bit() bool

Is16Bit returns whether this UUID is a 16-bit BLE UUID.

func (UUID) Is32Bit

func (uuid UUID) Is32Bit() bool

Is32Bit returns whether this UUID is a 32-bit or 16-bit BLE UUID.

func (UUID) Replace16BitComponent

func (uuid UUID) Replace16BitComponent(component uint16) UUID

Replace16BitComponent returns a new UUID where bits 16..32 have been replaced with the bits given in the argument. These bits are the same bits that vary in the 16-bit compressed UUID form.

This is especially useful for the Nordic SoftDevice, because it is able to store custom UUIDs more efficiently when only these bits vary between them.

func (UUID) String

func (uuid UUID) String() string

String returns a human-readable version of this UUID, such as 00001234-0000-1000-8000-00805f9b34fb.

Directories

Path Synopsis
examples
Package rawterm provides some sort of raw terminal interface, both on hosted systems and baremetal.
Package rawterm provides some sort of raw terminal interface, both on hosted systems and baremetal.
Package winbt provides a thin layer over the WinRT Bluetooth interfaces.
Package winbt provides a thin layer over the WinRT Bluetooth interfaces.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL