You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

324 lines
13 KiB

:mod:`bluetooth` --- low-level Bluetooth
========================================
.. module:: bluetooth
:synopsis: Low-level Bluetooth radio functionality
This module provides an interface to a Bluetooth controller on a board.
Currently this supports Bluetooth Low Energy (BLE) in Central, Peripheral,
Broadcaster, and Observer roles, and a device may operate in multiple
roles concurrently.
This API is intended to match the low-level Bluetooth protocol and provide
building-blocks for higher-level abstractions such as specific device types.
class BLE
---------
Constructor
-----------
.. class:: BLE()
Returns the singleton BLE object.
Configuration
-------------
.. method:: BLE.active([active])
Optionally changes the active state of the BLE radio, and returns the
current state.
The radio must be made active before using any other methods on this class.
.. method:: BLE.config(name)
Queries a configuration value by *name*. Currently supported values are:
- ``'mac'``: Returns the device MAC address. If a device has a fixed address
(e.g. PYBD) then it will be returned. Otherwise (e.g. ESP32) a random
address will be generated when the BLE interface is made active.
Event Handling
--------------
.. method:: BLE.irq(handler, trigger=0xffff)
Registers a callback for events from the BLE stack. The *handler* takes two
arguments, ``event`` (which will be one of the codes below) and ``data``
(which is an event-specific tuple of values).
The optional *trigger* parameter allows you to set a mask of events that
your program is interested in. The default is all events.
An event handler showing all possible events::
def bt_irq(event, data):
if event == _IRQ_CENTRAL_CONNECT:
# A central has connected to this peripheral.
conn_handle, addr_type, addr = data
elif event == _IRQ_CENTRAL_DISCONNECT:
# A central has disconnected from this peripheral.
conn_handle, addr_type, addr = data
elif event == _IRQ_GATTS_WRITE:
# A central has written to this characteristic or descriptor.
conn_handle, attr_handle = data
elif event == _IRQ_GATTS_READ_REQUEST:
# A central has issued a read. Note: this is a hard IRQ.
# Return None to deny the read.
# Note: This event is not supported on ESP32.
conn_handle, attr_handle = data
elif event == _IRQ_SCAN_RESULT:
# A single scan result.
addr_type, addr, connectable, rssi, adv_data = data
elif event == _IRQ_SCAN_COMPLETE:
# Scan duration finished or manually stopped.
pass
elif event == _IRQ_PERIPHERAL_CONNECT:
# A successful gap_connect().
conn_handle, addr_type, addr = data
elif event == _IRQ_PERIPHERAL_DISCONNECT:
# Connected peripheral has disconnected.
conn_handle, addr_type, addr = data
elif event == _IRQ_GATTC_SERVICE_RESULT:
# Called for each service found by gattc_discover_services().
conn_handle, start_handle, end_handle, uuid = data
elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
# Called for each characteristic found by gattc_discover_services().
conn_handle, def_handle, value_handle, properties, uuid = data
elif event == _IRQ_GATTC_DESCRIPTOR_RESULT:
# Called for each descriptor found by gattc_discover_descriptors().
conn_handle, dsc_handle, uuid = data
elif event == _IRQ_GATTC_READ_RESULT:
# A gattc_read() has completed.
conn_handle, value_handle, char_data = data
elif event == _IRQ_GATTC_WRITE_STATUS:
# A gattc_write() has completed.
conn_handle, value_handle, status = data
elif event == _IRQ_GATTC_NOTIFY:
# A peripheral has sent a notify request.
conn_handle, value_handle, notify_data = data
elif event == _IRQ_GATTC_INDICATE:
# A peripheral has sent an indicate request.
conn_handle, value_handle, notify_data = data
The event codes are::
from micropython import const
_IRQ_CENTRAL_CONNECT = const(1 << 0)
_IRQ_CENTRAL_DISCONNECT = const(1 << 1)
_IRQ_GATTS_WRITE = const(1 << 2)
_IRQ_GATTS_READ_REQUEST = const(1 << 3)
_IRQ_SCAN_RESULT = const(1 << 4)
_IRQ_SCAN_COMPLETE = const(1 << 5)
_IRQ_PERIPHERAL_CONNECT = const(1 << 6)
_IRQ_PERIPHERAL_DISCONNECT = const(1 << 7)
_IRQ_GATTC_SERVICE_RESULT = const(1 << 8)
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9)
_IRQ_GATTC_DESCRIPTOR_RESULT = const(1 << 10)
_IRQ_GATTC_READ_RESULT = const(1 << 11)
_IRQ_GATTC_WRITE_STATUS = const(1 << 12)
_IRQ_GATTC_NOTIFY = const(1 << 13)
_IRQ_GATTC_INDICATE = const(1 << 14)
In order to save space in the firmware, these constants are not included on the
:mod:`bluetooth` module. Add the ones that you need from the list above to your
program.
Broadcaster Role (Advertiser)
-----------------------------
.. method:: BLE.gap_advertise(interval_us, adv_data=None, resp_data=None, connectable=True)
Starts advertising at the specified interval (in **micro**\ seconds). This
interval will be rounded down to the nearest 625us. To stop advertising, set
*interval_us* to ``None``.
*adv_data* and *resp_data* can be any type that implements the buffer
protocol (e.g. ``bytes``, ``bytearray``, ``str``). *adv_data* is included
in all broadcasts, and *resp_data* is send in reply to an active scan.
Note: if *adv_data* (or *resp_data*) is ``None``, then the data passed
to the previous call to ``gap_advertise`` will be re-used. This allows a
broadcaster to resume advertising with just ``gap_advertise(interval_us)``.
To clear the advertising payload pass an empty ``bytes``, i.e. ``b''``.
Observer Role (Scanner)
-----------------------
.. method:: BLE.gap_scan(duration_ms, [interval_us], [window_us])
Run a scan operation lasting for the specified duration (in **milli**\ seconds).
To scan indefinitely, set *duration_ms* to ``0``.
To stop scanning, set *duration_ms* to ``None``.
Use *interval_us* and *window_us* to optionally configure the duty cycle.
The scanner will run for *window_us* **micro**\ seconds every *interval_us*
**micro**\ seconds for a total of *duration_ms* **milli**\ seconds. The default
interval and window are 1.28 seconds and 11.25 milliseconds respectively
(background scanning).
For each scan result, the ``_IRQ_SCAN_RESULT`` event will be raised.
When scanning is stopped (either due to the duration finishing or when
explicitly stopped), the ``_IRQ_SCAN_COMPLETE`` event will be raised.
Peripheral Role (GATT Server)
-----------------------------
A BLE peripheral has a set of registered services. Each service may contain
characteristics, which each have a value. Characteristics can also contain
descriptors, which themselves have values.
These values are stored locally, and are accessed by their "value handle" which
is generated during service registration. They can also be read from or written
to by a remote central device. Additionally, a peripheral can "notify" a
characteristic to a connected central via a connection handle.
Characteristics and descriptors have a default maximum size of 20 bytes.
Anything written to them by a central will be truncated to this length. However,
any local write will increase the maximum size, so if you want to allow larger
writes from a central to a given characteristic, use
:meth:`gatts_write<BLE.gatts_write>` after registration. e.g.
``gatts_write(char_handle, bytes(100))``.
.. method:: BLE.gatts_register_services(services_definition)
Configures the peripheral with the specified services, replacing any
existing services.
*services_definition* is a list of **services**, where each **service** is a
two-element tuple containing a UUID and a list of **characteristics**.
Each **characteristic** is a two-or-three-element tuple containing a UUID, a
**flags** value, and optionally a list of *descriptors*.
Each **descriptor** is a two-element tuple containing a UUID and a **flags**
value.
The **flags** are a bitwise-OR combination of the
:data:`bluetooth.FLAGS_READ`, :data:`bluetooth.FLAGS_WRITE` and
:data:`bluetooth.FLAGS_NOTIFY` values defined below.
The return value is a list (one element per service) of tuples (each element
is a value handle). Characteristics and descriptor handles are flattened
into the same tuple, in the order that they are defined.
The following example registers two services (Heart Rate, and Nordic UART)::
HR_UUID = bluetooth.UUID(0x180D)
HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
HR_SERVICE = (HR_SERVICE, (HR_CHAR,),)
UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,)
UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),)
SERVICES = (HR_SERVICE, UART_SERVICE,)
( (hr,), (tx, rx,), ) = bt.gatts_register_services(SERVICES)
The three value handles (``hr``, ``tx``, ``rx``) can be used with
:meth:`gatts_read <BLE.gatts_read>`, :meth:`gatts_write <BLE.gatts_write>`,
and :meth:`gatts_notify <BLE.gatts_notify>`.
**Note:** Advertising must be stopped before registering services.
.. method:: BLE.gatts_read(value_handle)
Reads the local value for this handle (which has either been written by
:meth:`gatts_write <BLE.gatts_write>` or by a remote central).
.. method:: BLE.gatts_write(value_handle, data)
Writes the local value for this handle, which can be read by a central.
.. method:: BLE.gatts_notify(conn_handle, value_handle, [data])
Notifies a connected central that this value has changed and that it should
issue a read of the current value from this peripheral.
If *data* is specified, then the that value is sent to the central as part
of the notification, avoiding the need for a separate read request. Note
that this will not update the local value stored.
Central Role (GATT Client)
--------------------------
.. method:: BLE.gap_connect(addr_type, addr, scan_duration_ms=2000)
Connect to a peripheral.
On success, the ``_IRQ_PERIPHERAL_CONNECT`` event will be raised.
.. method:: BLE.gap_disconnect(conn_handle)
Disconnect the specified connection handle.
On success, the ``_IRQ_PERIPHERAL_DISCONNECT`` event will be raised.
.. method:: BLE.gattc_discover_services(conn_handle)
Query a connected peripheral for its services.
For each service discovered, the ``_IRQ_GATTC_SERVICE_RESULT`` event will be
raised.
.. method:: BLE.gattc_discover_characteristics(conn_handle, start_handle, end_handle)
Query a connected peripheral for characteristics in the specified range.
For each characteristic discovered, the ``_IRQ_GATTC_CHARACTERISTIC_RESULT``
event will be raised.
.. method:: BLE.gattc_discover_descriptors(conn_handle, start_handle, end_handle)
Query a connected peripheral for descriptors in the specified range.
For each descriptor discovered, the ``_IRQ_GATTC_DESCRIPTOR_RESULT`` event
will be raised.
.. method:: BLE.gattc_read(conn_handle, value_handle)
Issue a remote read to a connected peripheral for the specified
characteristic or descriptor handle.
On success, the ``_IRQ_GATTC_READ_RESULT`` event will be raised.
.. method:: BLE.gattc_write(conn_handle, value_handle, data)
Issue a remote write to a connected peripheral for the specified
characteristic or descriptor handle.
On success, the ``_IRQ_GATTC_WRITE_STATUS`` event will be raised.
class UUID
----------
Constructor
-----------
.. class:: UUID(value)
Creates a UUID instance with the specified **value**.
The **value** can be either:
- A 16-bit integer. e.g. ``0x2908``.
- A 128-bit UUID string. e.g. ``'6E400001-B5A3-F393-E0A9-E50E24DCCA9E'``.
Constants
---------
.. data:: bluetooth.FLAG_READ
bluetooth.FLAG_WRITE
bluetooth.FLAG_NOTIFY