11.6. Сервисы и характеристики¶
После того как GAP установил между двумя устройствами открытое соединение, вышестоящий уровень – Generic Attribute Profile, GATT – должен придать смысл байтам, текущим через это соединение. Подход BLE здесь необычен. Если TCP предоставляет необработанный поток байтов и оставляет приложению самому придумывать собственный формат кадров, то GATT предоставляет небольшую базу данных ключ/значение, которую одна сторона размещает у себя, а другая читает, записывает или подписывается на неё.
Именно над этой базой данных проектировщики приложений проводят большую часть своего времени, посвящённого BLE. То, что камера публикует для телефона, то, что она отслеживает на удалённом датчике, то, как Bluetooth-клавиатура сообщает хосту, какая клавиша была нажата – всё это значения характеристик в какой-то базе данных GATT.
11.6.1. Две оси ролей, а не одна¶
Частый источник путаницы: периферийное устройство / центральное устройство и сервер / клиент – это две независимые оси, а не синонимы.
Периферийное устройство (peripheral) и центральное устройство (central) – это роли GAP, задаваемые при установке соединения. Периферийное устройство выполняет рекламу и к нему подключаются; центральное устройство сканирует и инициирует соединение. Это определяется в момент установления связи и не меняется.
Сервер и клиент – это роли GATT, задаваемые для каждой операции над характеристикой. Сервер размещает характеристику; клиент читает, записывает или подписывается на неё.
Эти две оси спецификацией разделены. Периферийное устройство обычно является сервером (нагрудный датчик пульса публикует свои показания), а центральное устройство обычно является клиентом (телефон их читает), но BLE допускает любую комбинацию – периферийное устройство может обнаружить характеристику на центральном устройстве, к которому оно только что подключилось, или одно соединение может размещать сервисы сразу с обеих сторон.
Большинство приложений для камер придерживаются традиционного сочетания (периферийное устройство + сервер либо центральное устройство + клиент), поэтому в оставшейся части этого раздела они рассматриваются как одна ось, когда речь идёт о традиционном случае. Когда различие имеет значение, оба термина приводятся явно.
11.6.2. Внутри базы данных¶
База данных GATT – это дерево. Листья несут фактические байты. Ветви группируют связанные листья в понятные человеку единицы.
База данных GATT. Сервисы группируют характеристики; характеристики несут байты приложения; дескрипторы несут метаданные о характеристике.¶
Существует три вида узлов:
Сервис – это логическая группа связанных значений. Bluetooth SIG публикует стандартные определения сервисов для распространённых сценариев использования – Battery Service для уровня заряда батареи, Environmental Sensing для температуры / влажности / давления, Heart Rate для мониторов пульса – так что универсальное приложение на телефоне может распознать сервис, который оно никогда раньше не встречало. Приложение также вправе определять собственные сервисы для того, что SIG не стандартизировал.
Характеристика – это одно именованное значение внутри сервиса. У сервиса Battery есть единственная характеристика – Battery Level, однобайтовый процент. У Environmental Sensing есть отдельные характеристики для температуры, влажности, давления и так далее. Характеристика – это единица операций GATT: вы читаете характеристику, вы записываете характеристику, вы подписываетесь на характеристику.
Дескриптор – это метаданные, прикреплённые к характеристике. Некоторые дескрипторы стандартизированы – Client Characteristic Configuration Descriptor (CCCD) самый известный из них, потому что запись в него – это то, как клиент сообщает серверу «отправляй мне уведомления по этой характеристике». Другие определяются пользователем и несут такие вещи, как формат представления или расширенные свойства.
Сервер GATT (как правило, периферийное устройство) объявляет свою базу данных один раз при запуске, и во время работы база данных не меняется. Клиент GATT (как правило, центральное устройство) обнаруживает содержимое базы данных после подключения – обходит дерево, читает UUID найденных сервисов, а затем характеристик внутри каждого из них.
11.6.3. UUID¶
У каждого сервиса, характеристики и дескриптора есть UUID (Universally Unique IDentifier), который идентифицирует, что это за сущность. UUID бывают трёх размеров:
16-битные. Зарезервированы для стандартов, определяемых Bluetooth SIG. Battery Service – это
0x180F. Battery Level (характеристика) – это0x2A19. Полный список опубликован на сайте назначенных номеров Bluetooth SIG по адресу https://www.bluetooth.com/specifications/assigned-numbers/.32-битные. Редко используемый промежуточный вариант.
128-битные. То, что используют все остальные – поставщик или приложение генерирует один случайным образом и применяет его для своего пользовательского сервиса или характеристики. Сюда относятся камеры, определяющие собственный протокол.
Класс bluetooth.UUID принимает любой из трёх размеров:
import bluetooth
BATTERY_SERVICE = bluetooth.UUID(0x180F)
CUSTOM_SERVICE = bluetooth.UUID("12345678-1234-5678-9abc-def012345678")
16-битный UUID кодируется в небольшую рекламную полезную нагрузку, и это одна из причин, по которой стандартные сервисы предпочтительнее, когда таковой существует – нагрудный датчик пульса, который рекламирует 0x180D (Heart Rate), стоит два байта; пользовательский UUID стоит шестнадцать. Для приложений, которым не нужна стандартная совместимость, правильным ответом будет сгенерированный 128-битный UUID.
11.6.4. Что дают сервисы, стандартизированные SIG¶
Аргумент в пользу использования стандартного сервиса прост: существующие приложения уже умеют с ним работать. Устройство, которое рекламирует сервис Heart Rate (0x180D) и предоставляет характеристику Heart Rate Measurement (0x2A37), работает с любым фитнес-приложением на планете без написания кем-либо нового кода. Устройству, которое заново реализует те же данные с пользовательскими UUID, требуется собственное приложение-компаньон и собственный документ протокола.
Стандарты не даются бесплатно. Расположение байтов внутри каждой характеристики специфицировано – SIG решил, что Heart Rate Measurement – это однобайтовое поле флагов, за которым следует либо 8-битное, либо 16-битное значение пульса, за которым опционально следуют R-R интервалы – и соответствующее стандарту устройство обязано следовать этой схеме. Пользовательские сервисы свободны от этого ограничения.
Прагматичный ответ для камер: используйте стандартный сервис, когда таковой существует для типа данных, которые у вас есть (Battery Service, Environmental Sensing), и определяйте пользовательский с 128-битным UUID для всего, что специфично для вашего приложения.
11.6.5. Объекты на стороне сервера и на стороне клиента¶
Для одних и тех же концептуальных строительных блоков (сервис, характеристика, дескриптор) каждая библиотека GATT предоставляет два параллельных набора объектов:
Объекты на стороне сервера – то, что периферийное устройство объявляет как размещаемое у себя. В
aioble:aioble.Service,aioble.Characteristic,aioble.Descriptor. Они создаются до начала рекламы и регистрируются с помощьюaioble.register_services().Объекты на стороне клиента – то, что центральное устройство обнаруживает на одноранговом устройстве после подключения. В
aioble:aioble.ClientService,aioble.ClientCharacteristic,aioble.ClientDescriptor. По ним проходят черезaioble.DeviceConnection.service()/services()после подключения.