MicroPython:Библиотеки/ubluetooth
Модуль ubluetooth – низкоуровневый Bluetooth[1]
В этом модуле реализован интерфейс для Bluetooth-контроллера платы. В данный момент поддерживается BLE (Bluetooth Low Energy) в режимах Central (центральный узел), Peripheral (периферия), Broadcaster (объявитель) и Observer (наблюдатель), а также режимы GATT-сервера и GATT-клиента. Кроме того, устройство может одновременно работать в нескольких режимах.
Эти API созданы с расчетом на соответствие низкоуровневому Bluetooth-протоколу, а также служат строительными кирпичиками для высокоуровневых абстракций вроде специфических типов устройств. Примечание: Этот модуль по-прежнему в разработке, поэтому его классы, функции, методы и константы в будущем, возможно, претерпят некоторые изменения.
Класс BLE
Конструктор
Настройки
- BLE.active([active]) – возвращает текущее состояние BLE-связи. Опционально меняет ее состояние.
Если вы хотите использовать на созданном классе какие-то другие методы, Bluetooth-связь должна оставаться активной.
- BLE.config('param') и BLE.config(param=value, ...) – задает или считывает настройки BLE-интерфейса. Чтобы прочесть значение параметра, его название должно быть строкой в кавычках, и одновременно можно запрашивать только один параметр. Чтобы задавать значения, используйте синтаксис именованных аргументов, и в этом случае одновременно можно задать более одного параметра.
Сейчас поддерживаются следующие значения:
- 'mac' – адрес, используемый в данный момент (в зависимости от используемого адресного режима). Возвращает кортеж типа (addr_type, addr).
Более подробно об этом типе адресов читайте ниже, в описании к функции gatts_write(). Этот параметр можно запрашивать, только если интерфейс активен.
- 'addr_mode' – задает адресный режим. Здесь могут быть следующие значения:
- 0x00 – публичный; использует публичный адрес контроллера.
- 0x01 – случайный; использует сгенерированный статический адрес.
- 0x02 – RPA (resolvable private address); определяемый частный адрес.
- 0x03 - NRPA (non-resolvable private address); неопределяемый частный адрес.
По умолчанию здесь будет задан режим публичного адреса (т.е. «0x00»), но при его наличии. В противном случае будет задан режим случайного адреса.
- 'gap_name' – задает или считывает GAP-название устройства, используемого сервисом 0x1800 с характеристикой 0x2a00. Его можно задать в любой момент и менять сколько угодно раз.
- 'rxbuf' – задает или считывает размер внутреннего буфера (в байтах), используемого для хранения входящих событий. Этот буфер глобален для всего BLE-драйвера и потому обрабатывает входящие данные для всех событий, включая все характеристики. Увеличение этого буфера улучшает управляемость резкими всплесками входящих данных (например, результатами сканирования) и способностью принимать большие значения характеристик.
Управление событиями
- BLE.irq(handler) – регистрирует функцию обратного вызова для событий из BLE-стека. Функция обратного вызова handler принимает два аргумента: event (здесь может быть один из кодов ниже) и data (это специфичный для события кортеж данных).
Примечание: Элементы addr, adv_data, char_data, notify_data и uuid в кортеже – это указатели на данные, управляемые модулем ubluetooth (т.е. один и тот же экземпляр будет повторно использоваться при всех вызовах обработчика событий). Если ваша программа хочет воспользоваться этими данными вне обработчика, то должна сначала скопировать их – например, при помощи bytes(addr) или bluetooth.UUID(uuid). В обработчике событий хранятся варианты действий для всех возможных событий.
def bt_irq(event, data):
if event == _IRQ_CENTRAL_CONNECT:
# Центральный узел подключился
# к этому периферийному устройству.
conn_handle, addr_type, addr = data
elif event == _IRQ_CENTRAL_DISCONNECT:
# Центральный узел отключился
# от этого периферийного устройства.
conn_handle, addr_type, addr = data
elif event == _IRQ_GATTS_WRITE:
# Клиент выполнил запись
# в эту характеристику или дескриптор.
conn_handle, attr_handle = data
elif event == _IRQ_GATTS_READ_REQUEST:
# Клиент выполнил операцию чтения.
# Примечание: это аппаратное прерывание.
# Верните «None», чтобы отклонить операцию чтения.
# Примечание: это событие не поддерживается на ESP32.
conn_handle, attr_handle = data
elif event == _IRQ_SCAN_RESULT:
# Один результат сканирования.
addr_type, addr, adv_type, rssi, adv_data = data
elif event == _IRQ_SCAN_DONE:
# Сканирование закончилось
# или было установлено вручную.
pass
elif event == _IRQ_PERIPHERAL_CONNECT:
# Успешный вызов gap_connect().
conn_handle, addr_type, addr = data
elif event == _IRQ_PERIPHERAL_DISCONNECT:
# Подключенное периферийное устройство было отключено.
conn_handle, addr_type, addr = data
elif event == _IRQ_GATTC_SERVICE_RESULT:
# Вызывается каждый раз,
# когда gattc_discover_services() находит новый сервис.
conn_handle, start_handle, end_handle, uuid = data
elif event == _IRQ_GATTC_SERVICE_DONE:
# Вызывается каждый раз при обнаружении сервиса.
# Примечание: при успешном вызове статус будет «0»,
# в противном случае будет значение,
# зависящее от используемой реализации.
conn_handle, status = data
elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
# Вызывается каждый раз, когда gattc_discover_services()
# находит новую характеристику.
conn_handle, def_handle, value_handle, properties, uuid = data
elif event == _IRQ_GATTC_CHARACTERISTIC_DONE:
# Вызывается каждый раз при обнаружении сервиса.
# Примечание: при успешном вызове статус будет «0»,
# в противном случае будет значение,
# зависящее от используемой реализации.
conn_handle, status = data
elif event == _IRQ_GATTC_DESCRIPTOR_RESULT:
# Вызывается каждый раз,
# когда gattc_discover_descriptors()
# находит новый дескриптор.
conn_handle, dsc_handle, uuid = data
elif event == _IRQ_GATTC_DESCRIPTOR_DONE:
# Вызывается каждый раз при обнаружении сервиса.
# Примечание: при успешном вызове статус будет «0»,
# в противном случае будет значение,
# зависящее от используемой реализации.
conn_handle, status = data
elif event == _IRQ_GATTC_READ_RESULT:
# Была выполнена функция gattc_read().
conn_handle, value_handle, char_data = data
elif event == _IRQ_GATTC_READ_DONE:
# Была выполнена функция gattc_read().
# Примечание: в btstack «value_handle» будет «0»,
# но в NimBLE оно будет присутствовать.
# Примечание: при успешном вызове статус будет «0»,
# в противном случае будет значение,
# зависящее от используемой реализации.
conn_handle, value_handle, status = data
elif event == _IRQ_GATTC_WRITE_DONE:
# Была выполнена функция gattc_write().
# Примечание: в btstack «value_handle» будет «0»,
# но в NimBLE оно будет присутствовать.
# Примечание: при успешном вызове статус будет «0»,
# в противном случае будет значение,
# зависящее от используемой реализации.
conn_handle, value_handle, status = data
elif event == _IRQ_GATTC_NOTIFY:
# Сервер отправил запрос на уведомление.
conn_handle, value_handle, notify_data = data
elif event == _IRQ_GATTC_INDICATE:
# Сервер отправил запрос на индикацию.
conn_handle, value_handle, notify_data = data
elif event == _IRQ_GATTS_INDICATE_DONE:
# Клиент подтвердил индикацию.
# Примечание: при успешном подтверждении
# статус будет «0»,
# в противном случае будет значение,
# зависящее от используемой реализации.
conn_handle, value_handle, status = data
А вот коды этих событий:
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)
В целях сделать прошивку максимально компактной эти константы не включены в модуль ubluetooth. Поэтому если вам нужны какие-то константы из списка выше, просто добавьте их в свою программу.
Режим объявителя (рассылка объявлений)
- BLE.gap_advertise(interval_us, adv_data=None, resp_data=None, connectable=True) – начинает рассылку объявлений с заданным интервалом (в микросекундах). Заданный интервал будет округлен до ближайших 625 мкс. Чтобы остановить рассылку объявлений, задайте None в аргументе interval_us.
Значения в аргументах adv_data и resp_data могут быть любого типа, но в нем должен быть реализован буферный протокол (например, bytes, bytearray, str). Аргумент adv_data включен во все объявления, а resp_data отправляется только в ответ на активное сканирование. Примечание: Если в adv_data или resp_data задано None, то будут повторно использованы данные, которые были переданы методу gap_advertise(), когда его вызывали в прошлый раз. Это позволяет объявителю возобновить рассылку объявлений, просто запустив gap_advertise(interval_us). Чтобы стереть полезную «объявленческую» нагрузку, просто передайте методу пустой объект bytes, т.е. b.
Режим наблюдателя (сканирование)
- BLE.gap_scan(duration_ms[, interval_us][, window_us][, active]) – запускает операцию сканирования, длящуюся заданное количество времени (в миллисекундах).
Чтобы сканирование выполнялось бесконечно, задайте в duration_ms значение «0». Чтобы остановить сканирование, задайте в duration_ms значение None. Опционально можно настроить коэффициент заполнения – для этого используйте аргументы interval_us и window_us. Сканирование будет длиться window_us микросекунд каждые interval_us микросекунд на протяжении duration_ms миллисекунд. По умолчанию в аргументах interval_us и window_us задано соответственно 1.28 секунд и 11.25 миллисекунд (фоновое сканирование). С каждым результатом сканирования будет вызвано событие _IRQ_SCAN_RESULT, а данные события будут иметь формат (addr_type, addr, adv_type, rssi, adv_data). В элементе addr_type задается тип адреса – публичный или случайный:
- 0x00 – публичный.
- 0x01 – случайный (либо статичный RPA, либо NRPA; тип зашифрован в самом адресе).
В элементе adv_type задается тип пакетов объявления:
- 0x00 - ADV_IND – ненаправленные пакеты, рассылаемые присоединяемыми и сканируемыми устройствами.
- 0x01 - ADV_DIRECT_IND – направленные пакеты, рассылаемые присоединяемыми устройствами.
- 0x02 - ADV_SCAN_IND – ненаправленные пакеты, рассылаемые сканируемыми устройствами.
- 0x03 - ADV_NONCONN_IND – ненаправленные пакеты, рассылаемые неприсоединяемыми устройствами.
- 0x04 - SCAN_RSP – пакеты с ответом на сканирование.
Если вы хотите получать в результатах ответы на сканирование, задайте True в аргументе active. Когда сканирование будет остановлено (будь то явная остановка или истечение таймаута), будет возбуждено исключение _IRQ_SCAN_DONE.
Режим центрального устройства
Центральное устройство может подключаться к периферийным устройствам, которые оно нашло в режиме наблюдателя (см. функцию gp_scan()), или к устройствам с известным адресом.
- BLE.gap_connect(addr_type, addr, scan_duration_ms=2000, /) – подключает центральное устройство в периферийному.
Более подробно о типах адресов читайте в описании функции gap_scan(). При успешном подключении будет запущено событие _IRQ_PERIPHERAL_CONNECT.
Режим периферийного устройства
Периферийное устройство обычно рассылает пакеты, сообщающие о том, что оно является присоединяемым устройством (см. функцию gap_advertise()). Обычно оно функционирует как GATT-сервер, но при условии, что у него есть сервисы и характеристики, зарегистрированные с помощью gatts_register_services(). При подключении центрального устройства будет запущено событие _IRQ_CENTRAL_CONNECT.
Режимы центрального и периферийного устройства
- BLE.gap_disconnect(conn_handle, /) – отключает устройство от заданного указателя соединения conn_handle. Это может быть либо центральное устройство, подключенное к этому устройству (если оно работает как периферийное), либо периферийное устройство, которое ранее было подключено к этому устройству (если оно работает как центральное).
При успешном отключении будут запущены события _IRQ_PERIPHERAL_DISCONNECT или _IRQ_CENTRAL_DISCONNECT. Если подключения к conn_handle не было, возвращает False. В противном случае возвращает True.
Режим GATT-сервера
У GATT-сервера есть набор зарегистрированных сервисов. Каждый сервис может содержать характеристики, каждое из которых имеет некоторое значение. Кроме того, характеристики могут содержать дескрипторы, которые тоже могут содержать значения. Эти значения хранятся локально, и доступ к ним осуществляется по их «указателю» (аргумент value_handle в описаниях ниже), который генерируется во время регистрации сервиса. Кроме того, удаленное клиентское устройство может считывать или записывать эти значения. Также сервер может «уведомлять» характеристику клиента, к которому он подключен, при помощи так называемого «указателя соединения» (аргумент conn_handle в описаниях ниже). GATT-сервером может быть устройство, работающее и в периферийном, и в центральном режиме, но в большинстве случаев это скорее периферийное серверное устройство. По умолчанию максимальный размер характеристик и дескрипторов составляет 20 байт. Любые данные, которые клиент попытается в них записать, будет обрезано до этого размера. Впрочем, любая локальная запись увеличит этот порог, поэтому если вам нужно, чтобы клиент записал в заданную характеристику более крупные данные, после регистрации используйте gatts_write() – например, gatts_write(char_handle, bytes(100)).
- BLE.gatts_register_services(services_definition) – настраивает сервер при помощи заданных сервисов, заменяя все существующие сервисы.
Аргумент services_definition – это список сервисов, где каждый сервис – это 2-элементный кортеж, содержащий UUID и список характеристик. Каждая характеристика – это 2- или 3-элементный кортеж, содержащий UUID, флаговое значение и опционально список дескрипторов. Каждый дескриптор – это 2-элементный кортеж, содержащий UUID и флаговое значение. Флаговое значение – это комбинация значений ubluetooth.FLAG_READ, ubluetooth.FLAG_WRITE и ubluetooth.FLAG_NOTIFY (см. ниже), заданных по принципу побитового ИЛИ. Возвращаемое значение – это список (один элемент – один сервис) кортежей (один элемент – один указатель значения). Указатели характеристик и дескрипторов помещены в один и тот же кортеж в том порядке, в котором они были заданы. Фрагмент кода ниже регистрирует два сервиса (Heart Rate и 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) Три указателя значения (hr, tx, rx) можно использовать в методах gatts_read, gatts_write, gatts_notify и gatts_indicate. Примечание: Рассылку объявлений можно остановить до регистрации сервисов.
- BLE.gatts_read(value_handle) – считывает локальное значение для заданного указателя значения value_handle, которое могло быть записано либо gatts_write(), либо удаленным клиентом.
- BLE.gatts_write(value_handle, data) – записывает локальное значение для указателя значения value_handle, которое затем может быть прочитано клиентом.
- BLE.gatts_notify(conn_handle, value_handle[, data]) – отправляет подключенному клиенту запрос на уведомление.
Если в аргументе data задано не None, его значение будет отправлено клиенту как часть уведомления. Локальное значение изменено не будет. Если же аргументе data будет None, будет отправлено текущее локальное значение – то, что было задано методом gatts_write().
- BLE.gatts_indicate(conn_handle, value_handle) – отправляет подключенному клиенту запрос на индикацию.
Примечание: Отправка произвольного значения в данный момент не поддерживается. Метод всегда будет отправлять текущее локальное значение – то, что было задано методом gatts_write(). При подтверждении (или отклонении запроса – например, из-за истечения таймаута) будет запущено событие _IRQ_GATTS_INDICATE_DONE.
- BLE.gatts_set_buffer(value_handle, len, append=False, /) – задает размер внутреннего буфера для данных (в байтах). По сути, это ограничение на размер записи. По умолчанию это 20 байт.
Ограничение на количество записываемых байтов задается в аргументе len. Если задать True в аргументе append, все удаленные записи будут добавляться к текущему значению, а не заменять его. Если вы используете gatts_read(), значение будет очищено после считывания. Эта функция может быть полезна при реализации сервисов вроде Nordic UART Service.
Режим GATT-клиента
GATT-клиент может находить и считывать/записывать характеристики с удаленного GATT-сервера. Для устройства в центральном режиме более характерна роль GATT-клиента, но периферийное устройство тоже может быть клиентом, чтобы найти информацию о центральном устройстве, которое к нему подключено (например, чтобы прочесть название устройства с его информационного сервиса).
- BLE.gattc_discover_services(conn_handle[, uuid]) – запрашивает у подключенного сервера его сервисы.
Опционально можно задать uuid сервиса, чтобы запросить именно этот сервис. Для каждого найденного сервиса будет запущено событие _IRQ_GATTC_SERVICE_RESULT, а потом по завершении запроса будет запущено событие _IRQ_GATTC_SERVICE_DONE.
- BLE.gattc_discover_characteristics(conn_handle, start_handle, end_handle[, uuid]) – запрашивает у подключенного сервера характеристики заданного диапазона.
Опционально можно задать uuid характеристики, чтобы запросить именно эту характеристику. Чтобы искать характеристику во всех сервисах, можно задать диапазон start_handle=1, end_handle=0xffff. Для каждой найденной характеристики будет запущено событие _IRQ_GATTC_CHARACTERISTIC_RESULT, а потом по завершении запроса будет запущено событие _IRQ_GATTC_CHARACTERISTIC_DONE.
- BLE.gattc_discover_descriptors(conn_handle, start_handle, end_handle) – запрашивает у подключенного сервера дескрипторы, находящиеся в заданном диапазоне.
Для каждого найденного дескриптора будет запущено событие _IRQ_GATTC_DESCRIPTOR_RESULT, а потом по завершении запроса будет запущено событие _IRQ_GATTC_DESCRIPTOR_DONE.
- BLE.gattc_read(conn_handle, value_handle) – выполняет удаленную операцию чтения с подключенного сервера по заданному указателю характеристики или дескриптора.
Если по заданному указателю есть какое-либо значение, будет запущено событие _IRQ_GATTC_READ_RESULT. Также будет запущено событие _IRQ_GATTC_READ_DONE.
- BLE.gattc_write(conn_handle, value_handle, data, mode=0, /) – выполняет удаленную операцию записи на подключенный сервер по заданному указателю характеристики или дескриптора.
В аргументе mode задается режим операции записи. В данный момент для него поддерживаются следующие значения:
- mode=0 (по умолчанию) – запись без ответа. Операция записи будет отправлена удаленному серверу, но подтверждения возвращено не будет. Кроме того, не будет запущено никакого события.
- mode=1 – запись с ответом. Удаленному серверу будет отправлен запрос на отправку ответа/подтверждения того, что данные получены.
При получении ответа от удаленного сервера будет запущено событие _IRQ_GATTC_WRITE_DONE.
Класс UUID
Конструктор
- Класс ubluetooth.UUID(value) – создает экземпляр UUID с заданным значением value. В аргументе value может быть:
- 16-битное целое число (например, 0x2908).
- 128-битная UUID-строка (например, '6E400001-B5A3-F393-E0A9-E50E24DCCA9E').
Константы
- ubluetooth.FLAG_READ
- ubluetooth.FLAG_WRITE
- ubluetooth.FLAG_NOTIFY
<syntaxhighlight lang="python" enclose="div">