ubluetooth — low-level Bluetooth¶
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, as well as GATT Server and Client. 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.
This module is still under development and its classes, functions, methods and constants are subject to change.
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.
config(*, param=value, ...)
Get or set configuration values of the BLE interface. To get a value the parameter name should be quoted as a string, and just one parameter is queried at a time. To set values use the keyword syntax, and one ore more parameter can be set at a time.
Currently supported values are:
'mac': The current address in use, depending on the current address mode. This returns a tuple of
gatts_writefor details about address type.
This may only be queried while the interface is currently active.
'addr_mode': Sets the address mode. Values can be:
0x00 - PUBLIC - Use the controller’s public address.
0x01 - RANDOM - Use a generated static address.
0x02 - RPA - Use resolvable private addresses.
0x03 - NRPA - Use non-resolvable private addresses.
By default the interface mode will use a PUBLIC address if available, otherwise it will use a RANDOM address.
'gap_name': Get/set the GAP device name used by service 0x1800, characteristic 0x2a00. This can be set at any time and changed multiple times.
'rxbuf': Get/set the size in bytes of the internal buffer used to store incoming events. This buffer is global to the entire BLE driver and so handles incoming data for all events, including all characteristics. Increasing this allows better handling of bursty incoming data (for example scan results) and the ability to receive larger characteristic values.
'mtu': Get/set the MTU that will be used during an MTU exchange. The resulting MTU will be the minimum of this and the remote device’s MTU. MTU exchange will not happen automatically (unless the remote device initiates it), and must be manually initiated with
gattc_exchange_mtu. Use the
_IRQ_MTU_EXCHANGEDevent to discover the MTU for a given connection.
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).
Note: As an optimisation to prevent unnecessary allocations, the
uuidentries in the tuples are read-only memoryview instances pointing to ubluetooth’s internal ringbuffer, and are only valid during the invocation of the IRQ handler function. If your program needs to save one of these values to access after the IRQ handler has returned (e.g. by saving it in a class instance or global variable), then it needs to take a copy of the data, either by using
bluetooth.UUID(), like this:
connected_addr = bytes(addr) # equivalently: adv_data, char_data, or notify_data matched_uuid = bluetooth.UUID(uuid)
For example, the IRQ handler for a scan result might inspect the
adv_datato decide if it’s the correct device, and only then copy the address data to be used elsewhere in the program. And to print data from within the IRQ handler,
print(bytes(addr))will be needed.
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 client has written to this characteristic or descriptor. conn_handle, attr_handle = data elif event == _IRQ_GATTS_READ_REQUEST: # A client 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, adv_type, rssi, adv_data = data elif event == _IRQ_SCAN_DONE: # 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_SERVICE_DONE: # Called once service discovery is complete. # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, status = 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_CHARACTERISTIC_DONE: # Called once service discovery is complete. # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, status = 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_DESCRIPTOR_DONE: # Called once service discovery is complete. # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, status = data elif event == _IRQ_GATTC_READ_RESULT: # A gattc_read() has completed. conn_handle, value_handle, char_data = data elif event == _IRQ_GATTC_READ_DONE: # A gattc_read() has completed. # Note: The value_handle will be zero on btstack (but present on NimBLE). # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, value_handle, status = data elif event == _IRQ_GATTC_WRITE_DONE: # A gattc_write() has completed. # Note: The value_handle will be zero on btstack (but present on NimBLE). # Note: Status will be zero on success, implementation-specific value otherwise. conn_handle, value_handle, status = data elif event == _IRQ_GATTC_NOTIFY: # A server has sent a notify request. conn_handle, value_handle, notify_data = data elif event == _IRQ_GATTC_INDICATE: # A server has sent an indicate request. conn_handle, value_handle, notify_data = data elif event == _IRQ_GATTS_INDICATE_DONE: # A client has acknowledged the indication. # Note: Status will be zero on successful acknowledgment, implementation-specific value otherwise. conn_handle, value_handle, status = data elif event == _IRQ_MTU_EXCHANGED: # MTU exchange complete (either initiated by us or the remote device). conn_handle, mtu = data
The event codes are:
from micropython import const _IRQ_CENTRAL_CONNECT = const(1) _IRQ_CENTRAL_DISCONNECT = const(2) _IRQ_GATTS_WRITE = const(3) _IRQ_GATTS_READ_REQUEST = const(4) _IRQ_SCAN_RESULT = const(5) _IRQ_SCAN_DONE = const(6) _IRQ_PERIPHERAL_CONNECT = const(7) _IRQ_PERIPHERAL_DISCONNECT = const(8) _IRQ_GATTC_SERVICE_RESULT = const(9) _IRQ_GATTC_SERVICE_DONE = const(10) _IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) _IRQ_GATTC_CHARACTERISTIC_DONE = const(12) _IRQ_GATTC_DESCRIPTOR_RESULT = const(13) _IRQ_GATTC_DESCRIPTOR_DONE = const(14) _IRQ_GATTC_READ_RESULT = const(15) _IRQ_GATTC_READ_DONE = const(16) _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) _IRQ_GATTS_INDICATE_DONE = const(20) _IRQ_MTU_EXCHANGED = const(21)
In order to save space in the firmware, these constants are not included on the
ubluetooth module. Add the ones that you need from the list above to your
Broadcaster Role (Advertiser)¶
gap_advertise(interval_us, adv_data=None, *, resp_data=None, connectable=True)¶
Starts advertising at the specified interval (in microseconds). This interval will be rounded down to the nearest 625us. To stop advertising, set interval_us to
adv_data and resp_data can be any type that implements the buffer protocol (e.g.
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_advertisewill be re-used. This allows a broadcaster to resume advertising with just
gap_advertise(interval_us). To clear the advertising payload pass an empty
Observer Role (Scanner)¶
gap_scan(duration_ms, interval_us=1280000, window_us=11250, active=False, /)¶
Run a scan operation lasting for the specified duration (in milliseconds).
To scan indefinitely, set duration_ms to
To stop scanning, set duration_ms to
Use interval_us and window_us to optionally configure the duty cycle. The scanner will run for window_us microseconds every interval_us microseconds for a total of duration_ms milliseconds. The default interval and window are 1.28 seconds and 11.25 milliseconds respectively (background scanning).
For each scan result the
_IRQ_SCAN_RESULTevent will be raised, with event data
(addr_type, addr, adv_type, rssi, adv_data).
addr_typevalues indicate public or random addresses:
0x00 - PUBLIC
0x01 - RANDOM (either static, RPA, or NRPA, the type is encoded in the address itself)
adv_typevalues correspond to the Bluetooth Specification:
0x00 - ADV_IND - connectable and scannable undirected advertising
0x01 - ADV_DIRECT_IND - connectable directed advertising
0x02 - ADV_SCAN_IND - scannable undirected advertising
0x03 - ADV_NONCONN_IND - non-connectable undirected advertising
0x04 - SCAN_RSP - scan response
activecan be set
Trueif you want to receive scan responses in the results.
When scanning is stopped (either due to the duration finishing or when explicitly stopped), the
_IRQ_SCAN_DONEevent will be raised.
A central device can connect to peripherals that it has discovered using the observer role (see
gap_scan) or with a known address.
A peripheral device is expected to send connectable advertisements (see
gap_advertise). It will usually be acting as a GATT
server, having first registered services and characteristics using
When a central connects, the
_IRQ_CENTRAL_CONNECT event will be raised.
Central & Peripheral Roles¶
Disconnect the specified connection handle. This can either be a central that has connected to this device (if acting as a peripheral) or a peripheral that was previously connected to by this device (if acting as a central).
On success, the
_IRQ_CENTRAL_DISCONNECTevent will be raised.
Falseif the connection handle wasn’t connected, and
A GATT server 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 client device. Additionally, a server can “notify” a characteristic to a connected client via a connection handle.
A device in either central or peripheral roles may function as a GATT server, however in most cases it will be more common for a peripheral device to act as the server.
Characteristics and descriptors have a default maximum size of 20 bytes.
Anything written to them by a client 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 client to a given characteristic, use
gatts_write after registration. e.g.
Configures the server 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 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_UUID, (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)
Note: Advertising must be stopped before registering services.
Reads the local value for this handle (which has either been written by
gatts_writeor by a remote client).
gatts_write(value_handle, data, /)¶
Writes the local value for this handle, which can be read by a client.
gatts_notify(conn_handle, value_handle, data=None, /)¶
Sends a notification request to a connected client.
If data is not
None, then that value is sent to the client as part of the notification. The local value will not be modified.
Otherwise, if data is
None, then the current local value (as set with
gatts_write) will be sent.
gatts_indicate(conn_handle, value_handle, /)¶
Sends an indication request to a connected client.
Note: This does not currently support sending a custom value, it will always send the current local value (as set with
On acknowledgment (or failure, e.g. timeout), the
_IRQ_GATTS_INDICATE_DONEevent will be raised.
gatts_set_buffer(value_handle, len, append=False, /)¶
Sets the internal buffer size for a value in bytes. This will limit the largest possible write that can be received. The default is 20.
Setting append to
Truewill make all remote writes append to, rather than replace, the current value. At most len bytes can be buffered in this way. When you use
gatts_read, the value will be cleared after reading. This feature is useful when implementing something like the Nordic UART Service.
A GATT client can discover and read/write characteristics on a remote GATT server.
It is more common for a central role device to act as the GATT client, however it’s also possible for a peripheral to act as a client in order to discover information about the central that has connected to it (e.g. to read the device name from the device information service).
gattc_discover_services(conn_handle, uuid=None, /)¶
Query a connected server for its services.
Optionally specify a service uuid to query for that service only.
For each service discovered, the
_IRQ_GATTC_SERVICE_RESULTevent will be raised, followed by
gattc_discover_characteristics(conn_handle, start_handle, end_handle, uuid=None, /)¶
Query a connected server for characteristics in the specified range.
Optionally specify a characteristic uuid to query for that characteristic only.
You can use
end_handle=0xffffto search for a characteristic in any service.
For each characteristic discovered, the
_IRQ_GATTC_CHARACTERISTIC_RESULTevent will be raised, followed by
gattc_discover_descriptors(conn_handle, start_handle, end_handle, /)¶
Query a connected server for descriptors in the specified range.
For each descriptor discovered, the
_IRQ_GATTC_DESCRIPTOR_RESULTevent will be raised, followed by
gattc_read(conn_handle, value_handle, /)¶
Issue a remote read to a connected server for the specified characteristic or descriptor handle.
When a value is available, the
_IRQ_GATTC_READ_RESULTevent will be raised. Additionally, the
_IRQ_GATTC_READ_DONEwill be raised.
gattc_write(conn_handle, value_handle, data, mode=0, /)¶
Issue a remote write to a connected server for the specified characteristic or descriptor handle.
The argument mode specifies the write behaviour, with the currently supported values being:
mode=0(default) is a write-without-response: the write will be sent to the remote server but no confirmation will be returned, and no event will be raised.
mode=1is a write-with-response: the remote server is requested to send a response/acknowledgement that it received the data.
If a response is received from the remote server the
_IRQ_GATTC_WRITE_DONEevent will be raised.
Initiate MTU exchange with a connected server, using the preferred MTU set using
_IRQ_MTU_EXCHANGEDevent will be raised when MTU exchange completes.
Note: MTU exchange is typically initiated by the central. When using the BlueKitchen stack in the central role, it does not support a remote peripheral initiating the MTU exchange. NimBLE works for both roles.
Creates a UUID instance with the specified value.
The value can be either:
A 16-bit integer. e.g.
A 128-bit UUID string. e.g.