|
| 1 | +:mod:`bluetooth` --- low-level Bluetooth |
| 2 | +======================================== |
| 3 | + |
| 4 | +.. module:: bluetooth |
| 5 | + :synopsis: Low-level Bluetooth radio functionality |
| 6 | + |
| 7 | +This module provides an interface to a Bluetooth controller on a board. |
| 8 | +Currently this supports Bluetooth Low Energy (BLE) in Central, Peripheral, |
| 9 | +Broadcaster, and Observer roles. |
| 10 | + |
| 11 | +This API is intended to match the low-level Bluetooth protocol and provide |
| 12 | +building-blocks for higher-level abstractions such as specific device types. |
| 13 | + |
| 14 | +class BLE |
| 15 | +--------- |
| 16 | + |
| 17 | +Constructor |
| 18 | +----------- |
| 19 | + |
| 20 | +.. class:: BLE() |
| 21 | + |
| 22 | + Returns the singleton BLE object. |
| 23 | + |
| 24 | +Configuration |
| 25 | +------------- |
| 26 | + |
| 27 | +.. method:: BLE.active([active]) |
| 28 | + |
| 29 | + Optionally changes the active state of the BLE radio, and returns the |
| 30 | + current state. |
| 31 | + |
| 32 | + The radio must be made active before using any other methods on this class. |
| 33 | + |
| 34 | +.. method:: BLE.config(name) |
| 35 | + |
| 36 | + Queries a configuration value by *name*. Currently supported values are: |
| 37 | + |
| 38 | + - ``'mac'``: Returns the device MAC address. If a device has a fixed address |
| 39 | + (e.g. PYBD) then it will be returned. Otherwise (e.g. ESP32) a random |
| 40 | + address will be generated when the BLE interface is made active. |
| 41 | + |
| 42 | +Event Handling |
| 43 | +-------------- |
| 44 | + |
| 45 | +.. method:: BLE.irq(handler, trigger=0xffff) |
| 46 | + |
| 47 | + Registers a callback for events from the BLE stack. The *handler* takes two |
| 48 | + arguments, ``event`` (which will be one of the codes below) and ``data`` |
| 49 | + (which is an event-specific tuple of values). |
| 50 | + |
| 51 | + The optional *trigger* parameter allows you to set a mask of events that |
| 52 | + your program is interested in. The default is all events. |
| 53 | + |
| 54 | + An event handler showing all possible events:: |
| 55 | + |
| 56 | + def bt_irq(event, data): |
| 57 | + if event == _IRQ_CENTRAL_CONNECT: |
| 58 | + # A central has connected to this peripheral. |
| 59 | + conn_handle, addr_type, addr = data |
| 60 | + elif event == _IRQ_CENTRAL_DISCONNECT: |
| 61 | + # A central has disconnected from this peripheral. |
| 62 | + conn_handle, addr_type, addr = data |
| 63 | + elif event == _IRQ_GATTS_WRITE: |
| 64 | + # A central has written to this characteristic or descriptor. |
| 65 | + conn_handle, attr_handle = data |
| 66 | + elif event == _IRQ_GATTS_READ_REQUEST: |
| 67 | + # A central has issued a read. Note: this is a hard IRQ. |
| 68 | + # Return None to deny the read. |
| 69 | + conn_handle, attr_handle = data |
| 70 | + elif event == _IRQ_SCAN_RESULT: |
| 71 | + # A single scan result. |
| 72 | + addr_type, addr, connectable, rssi, adv_data = data |
| 73 | + elif event == _IRQ_SCAN_COMPLETE: |
| 74 | + # Scan duration finished or manually stopped. |
| 75 | + pass |
| 76 | + elif event == _IRQ_PERIPHERAL_CONNECT: |
| 77 | + # A successful gap_connect(). |
| 78 | + conn_handle, addr_type, addr = data |
| 79 | + elif event == _IRQ_PERIPHERAL_DISCONNECT: |
| 80 | + # Connected peripheral has disconnected. |
| 81 | + conn_handle, addr_type, addr = data |
| 82 | + elif event == _IRQ_GATTC_SERVICE_RESULT: |
| 83 | + # Called for each service found by gattc_discover_services(). |
| 84 | + conn_handle, start_handle, end_handle, uuid = data |
| 85 | + elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: |
| 86 | + # Called for each characteristic found by gattc_discover_services(). |
| 87 | + conn_handle, def_handle, value_handle, properties, uuid = data |
| 88 | + elif event == _IRQ_GATTC_DESCRIPTOR_RESULT: |
| 89 | + # Called for each descriptor found by gattc_discover_descriptors(). |
| 90 | + conn_handle, dsc_handle, uuid = data |
| 91 | + elif event == _IRQ_GATTC_READ_RESULT: |
| 92 | + # A gattc_read() has completed. |
| 93 | + conn_handle, value_handle, char_data = data |
| 94 | + elif event == _IRQ_GATTC_WRITE_STATUS: |
| 95 | + # A gattc_write() has completed. |
| 96 | + conn_handle, value_handle, status = data |
| 97 | + elif event == _IRQ_GATTC_NOTIFY: |
| 98 | + # A peripheral has sent a notify request. |
| 99 | + conn_handle, value_handle, notify_data = data |
| 100 | + elif event == _IRQ_GATTC_INDICATE: |
| 101 | + # A peripheral has sent an indicate request. |
| 102 | + conn_handle, value_handle, notify_data = data |
| 103 | + |
| 104 | +The event codes are:: |
| 105 | + |
| 106 | + from micropython import const |
| 107 | + _IRQ_CENTRAL_CONNECT = const(1 << 0) |
| 108 | + _IRQ_CENTRAL_DISCONNECT = const(1 << 1) |
| 109 | + _IRQ_GATTS_WRITE = const(1 << 2) |
| 110 | + _IRQ_GATTS_READ_REQUEST = const(1 << 3) |
| 111 | + _IRQ_SCAN_RESULT = const(1 << 4) |
| 112 | + _IRQ_SCAN_COMPLETE = const(1 << 5) |
| 113 | + _IRQ_PERIPHERAL_CONNECT = const(1 << 6) |
| 114 | + _IRQ_PERIPHERAL_DISCONNECT = const(1 << 7) |
| 115 | + _IRQ_GATTC_SERVICE_RESULT = const(1 << 8) |
| 116 | + _IRQ_GATTC_CHARACTERISTIC_RESULT = const(1 << 9) |
| 117 | + _IRQ_GATTC_DESCRIPTOR_RESULT = const(1 << 10) |
| 118 | + _IRQ_GATTC_READ_RESULT = const(1 << 11) |
| 119 | + _IRQ_GATTC_WRITE_STATUS = const(1 << 12) |
| 120 | + _IRQ_GATTC_NOTIFY = const(1 << 13) |
| 121 | + _IRQ_GATTC_INDICATE = const(1 << 14) |
| 122 | + |
| 123 | +In order to save space in the firmware, these constants are not included on the |
| 124 | +:mod:`bluetooth` module. Add the ones that you need from the list above to your |
| 125 | +program. |
| 126 | + |
| 127 | + |
| 128 | +Broadcaster Role (Advertiser) |
| 129 | +----------------------------- |
| 130 | + |
| 131 | +.. method:: BLE.gap_advertise(interval_us, adv_data=None, resp_data=None, connectable=True) |
| 132 | + |
| 133 | + Starts advertising at the specified interval (in **micro**\ seconds). This |
| 134 | + interval will be rounded down to the nearest 625us. To stop advertising, set |
| 135 | + *interval_us* to ``None``. |
| 136 | + |
| 137 | + *adv_data* and *resp_data* can be any type that implements the buffer |
| 138 | + protocol (e.g. ``bytes``, ``bytearray``, ``str``). *adv_data* is included |
| 139 | + in all broadcasts, and *resp_data* is send in reply to an active scan. |
| 140 | + |
| 141 | + |
| 142 | +Observer Role (Scanner) |
| 143 | +----------------------- |
| 144 | + |
| 145 | +.. method:: BLE.gap_scan(duration_ms, [interval_us], [window_us]) |
| 146 | + |
| 147 | + Run a scan operation lasting for the specified duration (in **milli**\ seconds). |
| 148 | + |
| 149 | + To scan indefinitely, set *duration_ms* to ``0``. |
| 150 | + |
| 151 | + To stop scanning, set *duration_ms* to ``None``. |
| 152 | + |
| 153 | + Use *interval_us* and *window_us* to optionally configure the duty cycle. |
| 154 | + The scanner will run for *window_us* **micro**\ seconds every *interval_us* |
| 155 | + **micro**\ seconds for a total of *duration_ms* **milli**\ seconds. The default |
| 156 | + interval and window are 1.28 seconds and 11.25 milliseconds respectively |
| 157 | + (background scanning). |
| 158 | + |
| 159 | + For each scan result, the ``_IRQ_SCAN_RESULT`` event will be raised. |
| 160 | + |
| 161 | + When scanning is stopped (either due to the duration finishing or when |
| 162 | + explicitly stopped), the ``_IRQ_SCAN_COMPLETE`` event will be raised. |
| 163 | + |
| 164 | + |
| 165 | +Peripheral Role (GATT Server) |
| 166 | +----------------------------- |
| 167 | + |
| 168 | +A BLE peripheral has a set of registered services. Each service may contain |
| 169 | +characteristics, which each have a value. Characteristics can also contain |
| 170 | +descriptors, which themselves have values. |
| 171 | + |
| 172 | +These values are stored locally and can be read from or written to by a remote |
| 173 | +central device. Additionally, a peripheral can "notify" its value to a connected |
| 174 | +central via its connection handle. |
| 175 | + |
| 176 | +.. method:: BLE.gatts_register_services(services_definition) |
| 177 | + |
| 178 | + Configures the peripheral with the specified services, replacing any |
| 179 | + existing services. |
| 180 | + |
| 181 | + *services_definition* is a list of **services**, where each **service** is a |
| 182 | + two-element tuple containing a UUID and a list of **characteristics**. |
| 183 | + |
| 184 | + Each **characteristic** is a two-or-three-element tuple containing a UUID, a |
| 185 | + **flags** value, and optionally a list of *descriptors*. |
| 186 | + |
| 187 | + Each **descriptor** is a two-element tuple containing a UUID and a **flags** |
| 188 | + value. |
| 189 | + |
| 190 | + The **flags** are a bitwise-OR combination of the |
| 191 | + :data:`bluetooth.FLAGS_READ`, :data:`bluetooth.FLAGS_WRITE` and |
| 192 | + :data:`bluetooth.FLAGS_NOTIFY` values defined below. |
| 193 | + |
| 194 | + The return value is a list (one element per service) of tuples (each element |
| 195 | + is a value handle). Characteristics and descriptor handles are flattened |
| 196 | + into the same tuple, in the order that they are defined. |
| 197 | + |
| 198 | + The following example registers two services (Heart Rate, and Nordic UART):: |
| 199 | + |
| 200 | + HR_UUID = bluetooth.UUID(0x180D) |
| 201 | + HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) |
| 202 | + HR_SERVICE = (HR_SERVICE, (HR_CHAR,),) |
| 203 | + UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E') |
| 204 | + UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) |
| 205 | + UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,) |
| 206 | + UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),) |
| 207 | + SERVICES = (HR_SERVICE, UART_SERVICE,) |
| 208 | + ( (hr,), (tx, rx,), ) = bt.gatts_register_services(SERVICES) |
| 209 | + |
| 210 | + The three value handles (``hr``, ``tx``, ``rx``) can be used with |
| 211 | + :meth:`gatts_read <BLE.gatts_read>`, :meth:`gatts_write <BLE.gatts_write>`, |
| 212 | + and :meth:`gatts_notify <BLE.gatts_notify>`. |
| 213 | + |
| 214 | + **Note:** Advertising must be stopped before registering services. |
| 215 | + |
| 216 | +.. method:: BLE.gatts_read(value_handle) |
| 217 | + |
| 218 | + Reads the local value for this handle (which has either been written by |
| 219 | + :meth:`gatts_write <BLE.gatts_write>` or by a remote central). |
| 220 | + |
| 221 | +.. method:: BLE.gatts_write(value_handle, data) |
| 222 | + |
| 223 | + Writes the local value for this handle, which can be read by a central. |
| 224 | + |
| 225 | +.. method:: BLE.gatts_notify(conn_handle, value_handle, [data]) |
| 226 | + |
| 227 | + Notifies a connected central that this value has changed and that it should |
| 228 | + issue a read of the current value from this peripheral. |
| 229 | + |
| 230 | + If *data* is specified, then the that value is sent to the central as part |
| 231 | + of the notification, avoiding the need for a separate read request. Note |
| 232 | + that this will not update the local value stored. |
| 233 | + |
| 234 | + |
| 235 | +Central Role (GATT Client) |
| 236 | +-------------------------- |
| 237 | + |
| 238 | +.. method:: BLE.gap_connect(addr_type, addr, scan_duration_ms=2000) |
| 239 | + |
| 240 | + Connect to a peripheral. |
| 241 | + |
| 242 | + On success, the ``_IRQ_PERIPHERAL_CONNECT`` event will be raised. |
| 243 | + |
| 244 | +.. method:: BLE.gap_disconnect(conn_handle) |
| 245 | + |
| 246 | + Disconnect the specified connection handle. |
| 247 | + |
| 248 | + On success, the ``_IRQ_PERIPHERAL_DISCONNECT`` event will be raised. |
| 249 | + |
| 250 | +.. method:: BLE.gattc_discover_services(conn_handle) |
| 251 | + |
| 252 | + Query a connected peripheral for its services. |
| 253 | + |
| 254 | + For each service discovered, the ``_IRQ_GATTC_SERVICE_RESULT`` event will be |
| 255 | + raised. |
| 256 | + |
| 257 | +.. method:: BLE.gattc_discover_characteristics(conn_handle, start_handle, end_handle) |
| 258 | + |
| 259 | + Query a connected peripheral for characteristics in the specified range. |
| 260 | + |
| 261 | + For each characteristic discovered, the ``_IRQ_GATTC_CHARACTERISTIC_RESULT`` |
| 262 | + event will be raised. |
| 263 | + |
| 264 | +.. method:: BLE.gattc_discover_descriptors(conn_handle, start_handle, end_handle) |
| 265 | + |
| 266 | + Query a connected peripheral for descriptors in the specified range. |
| 267 | + |
| 268 | + For each descriptor discovered, the ``_IRQ_GATTC_DESCRIPTOR_RESULT`` event |
| 269 | + will be raised. |
| 270 | + |
| 271 | +.. method:: BLE.gattc_read(conn_handle, value_handle) |
| 272 | + |
| 273 | + Issue a remote read to a connected peripheral for the specified |
| 274 | + characteristic or descriptor handle. |
| 275 | + |
| 276 | + On success, the ``_IRQ_GATTC_READ_RESULT`` event will be raised. |
| 277 | + |
| 278 | +.. method:: BLE.gattc_write(conn_handle, value_handle, data) |
| 279 | + |
| 280 | + Issue a remote write to a connected peripheral for the specified |
| 281 | + characteristic or descriptor handle. |
| 282 | + |
| 283 | + On success, the ``_IRQ_GATTC_WRITE_STATUS`` event will be raised. |
| 284 | + |
| 285 | + |
| 286 | +class UUID |
| 287 | +---------- |
| 288 | + |
| 289 | + |
| 290 | +Constructor |
| 291 | +----------- |
| 292 | + |
| 293 | +.. class:: UUID(value) |
| 294 | + |
| 295 | + Creates a UUID instance with the specified **value**. |
| 296 | + |
| 297 | + The **value** can be either: |
| 298 | + |
| 299 | + - A 16-bit integer. e.g. ``0x2908``. |
| 300 | + - A 128-bit UUID string. e.g. ``'6E400001-B5A3-F393-E0A9-E50E24DCCA9E'``. |
| 301 | + |
| 302 | + |
| 303 | +Constants |
| 304 | +--------- |
| 305 | + |
| 306 | +.. data:: bluetooth.FLAG_READ |
| 307 | + bluetooth.FLAG_WRITE |
| 308 | + bluetooth.FLAG_NOTIFY |
0 commit comments