Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий

в 14:57, , рубрики: bluetooth, corebluetooth, iOS, IoT, L2CAP, Raiffeisenbank, raiffeisenIT, usability, Блог компании Райффайзенбанк, высокая производительность, разработка под iOS

Недавно мы в команде придумали и реализовали функцию передачи денег по воздуху с помощью технологии Bluetooth LE. Я хочу рассказать вам, как мы это сделали и что Apple предоставляет нам из инструментов. Многие разработчики думают что Bluetooth — это сложно, ведь это достаточно низкоуровневый протокол, и по нему не так много специалистов. Но всё не так страшно, и на самом деле использовать эту функцию очень просто! А те функции, которые можно реализовать с помощью Bluetooth LE, безусловно, интересны и впоследствии позволят выделить ваше приложение среди конкурентов.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 1

Давайте сначала разберёмся, что это вообще за технология и в чём её отличие от классического Bluetooth.

Что такое Bluetooth LE?


Почему разработчики Bluetooth назвали эту технологию именно Low Energy? Ведь с каждой новой версией Bluetooth энергопотребление и без того многократно снижалось. Ответ кроется в этой батарейке.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 2

Её диаметр всего 2 см, а ёмкость около 220 мА*ч. Когда инженеры разрабатывали Bluetooth LE, они стремились к тому, чтобы устройство с такой батарейкой работало несколько лет. И у них это получилось! Bluetooth LE-устройства c таким элементом питания могут работать от года. Кто из вас еще по-старинке выключает Bluetooth на телефоне для экономии энергии, как это делали в 2000-м? Зря вы это делаете — экономия будет меньше 10 секунд работы телефона в день. А функциональность вы отключаете очень большую, такую как Handoff, AirDrop и другие.

Чего же инженеры добились, разработав Bluetooth LE? Они усовершенствовали классический протокол? Сделали его более энергоэфективным? Просто оптимизировали все процессы? Нет. Они полностью переделали архитектуру стека Bluetooth и добились того, что теперь, чтобы быть видимым для всех других устройств, необходимо меньше времени находиться в эфире и занимать канал. В свою очередь это позволило хорошо сэкономить на энергопотреблении. А с новой архитектурой теперь можно стандартизировать любое новое устройство, благодаря чему разработчики со всего мира могут коммуницировать с устройством, а значит, и с легкостью писать новые приложения для управления им. Кроме того, в архитектуру заложен принцип self-discovery: при подключении к устройству не нужно вводить никакие пин-коды, и если ваше приложение умеет общаться с этим устройством, подключение занимает считанные миллисекунды.

  • Меньше времени в эфире.
  • Меньше расход энергии.
  • Новая архитектура.
  • Уменьшено время подключения.

За счёт чего удалось инженерам сделать такой колоссальный скачок в энергоэффективности?

Частота осталась та же: 2,4 ГГц, не сертифицируемая и свободная для использования во многих странах. А вот задержка подключения стала меньше: 15-30 мс вместо 100 мс у классического Bluetooth. Расстояние работы осталось таким же — 100 м. Интервал передачи не сильно, но изменился — вместо 0,625 мс стало 3 мс.

Но не могло же из-за этого энергопотребление уменьшиться в десятки раз. Конечно же, что-то должно было пострадать. И это скорость: вместо 24 Мбит/с стало 0,27 Мбит/с. Вы, наверное, скажете, что это смешная скорость для 2018 года.

Где используется Bluetooth LE?


Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 3

Технология эта немолодая, впервые она появилась в iPhone 4s. И уже успела завоевать много сфер. Bluetooth LE используется во всех устройствах умного дома и в носимой электронике. Сейчас уже есть даже чипы размером с кофейное зерно.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 4

А как эта технология применяется в программном обеспечении?

Поскольку Apple была первой, кто встроил в своё устройство Bluetooth и начал её использовать, то к настоящему времени они достаточно хорошо продвинулись и встроили технологию в свою экосистему. И сейчас вы можете встретить эту технологию в таких сервисах, как AirDrop, Devices quick start, Share passwords, Handoff. И даже уведомления в часах сделаны через Bluetooth LE. Вдобавок, Apple выложила в открытый доступ документацию, как сделать так, чтобы на ваши собственные устройства приходили уведомления из всех приложений. Какие бывают роли устройств в рамках Bluetooth LE?

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 5

Broаdcaster. Отправляет сообщения всем, кто находится рядом, к этому устройству нельзя подключиться. По такому принципу работают iBeacons и навигация в помещениях.

Observer. Слушает, что происходит вокруг, и получает данные только от общедоступных сообщений. Соединения не создаёт.

А вот с Central и Peripheral интереснее. Почему их не назвали просто Server-Client? Логично же, судя по названию. А вот и нет.

Потому что Peripheral, на самом деле, выступает как сервер. Это периферийное устройство, которое потребляет меньше энергии и к которому подключается более мощный Central. Peripheral может извещать, что он находится рядом и какие у него есть службы. К нему может подключиться только одно устройство, и у Peripheral есть какие-то данные. А Central может сканировать эфир в поиске устройств, отправлять запросы на подключение, подключаться к любому количеству устройств, может читать, записывать и подписывать на данные у Peripheral.

Что же нам, как разработчикам, доступно в экосистеме Apple?

Что нам доступно?


iOS/Mac OS:

  • Peripheral и Central.
  • Фоновый режим.
  • Восстановление состояния.
  • Интервал подключения 15 мс.

watchOS/tvOS:

  • watchOS 4+/tvOS 9+.
  • Только Сentral.
  • Максимум два подключения.
  • Apple watch series 2+/ AppleTv 4+.
  • Отключение при переходе в фоновый режим.
  • Интервал подключения 30 мс.

Самое важно различие — интервал подключения. На что он влияет? Чтобы ответить на этот вопрос, сначала нужно разобраться, как работает протокол Bluetooth LE и почему такая небольшая разница в абсолютных значениях очень важна.

Как работает протокол


Как происходит процесс поиска и подключения?

Peripheral сообщает о своем присутствии с частотой advertisement-интервала, его пакет очень маленький и содержит всего несколько идентификаторов сервисов, которые предоставляет устройство, а также имя устройства. Интервал может быть достаточно большим и способен варьироваться в зависимости от текущего статуса устройства, режима энергосбережения и других настроек. Apple советует разработчикам внешних устройств привязывать длину интервала к акселерометру: увеличивать интервал, если устройством не пользуются, а когда оно активно — уменьшать, чтобы быстро находить устройство. Advertisement-интервал никак не коррелирует c интервалом подключения и определяется самим устройством в зависимости от энергопотребления и своих настроек. Нам он в экосистеме Apple недоступен и неизвестен, им полностью управляет система.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 6

После того, как мы нашли устройство, отправляем запрос на подключение, и вот тут на сцену выходит интервал подключения — время, через которое второе устройство может ответить на запрос. Но это при подключении, а что же происходит при чтении/записи?

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 7

Интервал подключения также фигурирует и при чтении данных — его уменьшение в 2 раза увеличивает скорость передачи данных. Но нужно понимать, что если оба устройства не поддерживают одинаковый интервал, то будет выбран максимальный из них.

Давайте рассмотрим, из чего состоит пакет с информацией, который передает Peripheral.

MTU (maximum transmission unit) такого пакета определяется в процессе подключения и варьируется от устройства к устройству и в зависимости от операционной системы. В протоколе версии 4.0 MTU был около 30, и размер полезных данных не превышал 20 байтов. В версии 4.2 всё поменялось, теперь можно передавать около 520 байтов. Но, к сожалению, эту версию протокола поддерживают только устройства младше IPhone 5s. Размер накладных расходов, независимо от размера MTU, составляет 7 байтов: сюда входят ATT и L2CAP заголовков. С записью, в целом, похожая ситуация.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 8

Есть только два режима: с ответом и без. Режим без ответа значительно ускоряет передачу данных, поскольку нет интервала ожидания перед следующей записью. Но этот режим доступен не всегда, не на всех устройствах и не на всех системах. Доступ к этому режиму записи может ограничить сама система, потому что он считается менее энергоэкономичным. В iOS eсть метод, в котором можно проверить перед записью, доступен ли такой режим.

Теперь давайте рассмотрим, из чего состоит протокол.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 9

Протокол состоит из 5 уровней. Слой приложения — эта ваша логика, описанная поверх CoreBluetooth. GATT (Generic Attributes Layer) служит для обмена сервисами и характеристиками, которые есть на устройствах. ATT (Attributes Layer) используется для управления вашими характеристиками и передачей ваших данных. L2CAP — низкоуровневый протокол обмена данными. Controller — это уже сам BT-чип.

Вы, наверное, спросите, что такое GATT и как мы можем с ним работать?

GATT состоит из характеристики и сервисов. Характеристика — это объект, в котором хранятся ваши данные, словно переменная. А сервис — это группа, в которой находятся ваши характеристики, словно пространство имён. У сервиса есть название — UUID, вы сами его выбираете. Сервис может содержать в себе дочерний сервис.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 10

У характеристики тоже есть свой UUID — фактически, имя. Значение (Value) характеристики — это NSData, сюда вы можете записывать и хранить данные. Дескрипторы — это описание вашей характеристики, вы можете описать, какие данные вы ожидаете в этой характеристике, или что они означают. В протоколе Bluetooth есть много дескрипторов, но в Apple-системах пока доступно только два: человеческое описание и формат данных. Также есть уровни доступа (Permissions) для вашей характеристики:

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 11

Попробуем сами


У нас появилась идея сделать возможность передачи денег по воздуху, ничего не требуя от получателя. Представьте, вот ломаете голову над очень интересной задачей, пишете идеальный код, и тут коллега предлагает сходить за кофе. А вы так увлечены задачей, что не можете отлучиться, и просите его купить вам чашечку вкусного капучино. Он приносит вам кофе, и нужно вернуть ему деньги. Можно перевести по номеру телефона, работает отлично. Но вот неловкая ситуация — вы не знаете его номера. Ну вот так, три года работаете, а номерами не обменялись :)

Поэтому мы решили сделать возможность передачи денег тем, кто находится рядом, без ввода каких-либо пользовательских данных. Как в AirDrop. Просто выбрать пользователя и отправить нужную ему сумму. Давайте посмотрим, что нам для этого нужно.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 12

Отображение PUSH


Нам нужно, чтобы отправитель:

  1. Мог найти все устройства, которые находятся рядом и поддерживают наш сервис.
  2. Мог прочитать реквизиты.
  3. И мог отослать сообщение получателю о том, что успешно отправил ему деньги.

Получатель, в свою очередь, должен уметь сообщать окружающим отправителям, что у него есть сервис с нужными данными, и уметь получать сообщения от отправителя. Как происходит процесс перевода денег по реквизитам у нас в банке, думаю, описывать не стоит. А теперь попробуем это реализовать.

Для начала нужно придумать названия нашего сервиса и характеристик. Как я говорил, это UUID. Просто генерируем их и сохраняем на Peripheral и Central, чтобы на обоих устройствах были одинаковые.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 13

Вы вольны использовать любые UUID, кроме тех, которые оканчиваются вот так: XXXXXXXX-0000-1000-8000-00805F9B34FB, — они зарезервированы под разные компании. Вы сами можете купить себе такой номер и никто его использовать не будет. Это будет стоить $2500.

Далее нам нужно будет создать менеджеры: один для передачи денежных средств, другой для получения. Нужно просто указать делегатов. Передавать у нас будет Central, получать Peripheral. Мы создаем оба, потому что и отправителем, и получателем может быть одно лицо в разное время.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 14

Теперь нам нужно сделать возможность обнаружения получателя и записать в нашу характеристику реквизиты получателя.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 15

Для начала создадим сервис. Пропишем UUID и укажем, что он primary — то есть сервис является главным для этого устройства. Хороший пример: пульсомер, для которого главным сервисом будет текущее состояние пульса, а состояние батареи — это второстепенная информация.

Далее создаем две характеристики: одну для чтения реквизитов получателя, вторую для записи, чтобы получатель мог узнать об отправке денег. Регистрируем их в нашем сервисе, потом добавляем в менеджер, запускаем обнаружение и указываем UUID сервиса, чтобы все устройства, которые находятся рядом, могли узнать о нашем сервисе до подключения к нему. Эти данные помещаются в пакет, который отправляет Central в ходе вещания.

Получатель готов, приступим к отправителю. Запустим поиск и подключение.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 16

При включении менеджера мы запускаем поиск устройств с нашим сервисом. При нахождении мы их получаем в методе делегата и сразу подключаемся. Важно: нужно сохранять strong-ссылку на все Peripheral, с которыми вы работаете, иначе они «утекут».

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 17

После успешного подключения настраиваем делегат, который будет работать с данным устройством, и получаем от устройства нужный нам сервис.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 18

Мы успешно подключились к получателю, теперь нужно прочитать его реквизиты.

Мы после подключения уже запросили все сервисы с устройства. И после их получения будет вызван метод делегата, в котором будут перечислены все сервисы, доступные на данном устройстве. Находим нужный и запрашиваем его характеристики. Результат можно будет найти по UUID в методе делегата, в котором хранятся данные для перевода. Пробуем их прочитать, и получим искомое опять в методе делегата. Все сервисы, характеристики и их значения кешируются системой, так что запрашивать их впоследствии каждый раз необязательно.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 19

Всё, мы отправили деньги за кофе, самое время показать получателю красивое уведомление, чтобы он ждал рубли на своем счёте. Для этого нужно реализовать процесс отправки сообщения.

У отправителя достаем нужную нам характеристику, в данном случае мы её взяли из сохраненного значения. Но до этого вам её нужно получить с устройства, как мы делали до этого. А дальше просто записываете данные в нужную характеристику.

После этого на другом устройстве получаем в методе делегата запрос на запись. Тут вы можете прочитать данные, которые вам отправляют, ответить на какую-либо ошибкой, например, нет доступа, или данной характеристики не существует. Всё будет работать, но только если оба устройства включены и приложения активны. А нам нужно, чтобы работало и в фоне!

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 20

Apple позволяет использовать Bluetooth в фоне. Для этого нужно в info.plist указать ключ, в каком режиме мы хотим использовать, в Peripheral или Central.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 21

Далее в менеджере нужно указать ключ восстановления и создать метод делегата. Теперь нам доступен и фоновый режим. Если приложение заснёт или будет выгружено из памяти, то при нахождении нужного Peripheral или при подключении Central оно проснётся, а менеджер восстановится с вашим ключом.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 22

Всё отлично, уже готовы релизиться. Но тут к нам прибегают дизайнеры и говорят: «Хотим вставить фотографии пользователей, чтобы им было легче находить друг друга». Что же делать? У нас в характеристику можно записать всего какие-то 500 байтов, а на каких-то устройствах вообще 20 :(

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 23

Спустимся глубже


Чтобы решить эту проблему, нам пришлось спуститься глубже.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 24

Сейчас мы общались устройствами на уровне GATT/ATT. Но в iOS 11 у нас есть доступ к протоколу L2CAP. Однако в этом случае придётся самостоятельно позаботиться о передаче данных. Пакеты отправляются с MTU 2 Кб, не нужно ни во что перекодировать, применяется обычный NSStream. Скорость передачи данных до 394 Килобит/с., по заверению Apple.

Допустим, вы передаёте какие-либо данные вашего сервиса от Peripheral к Central в виде обычных характеристик. И понадобилось открыть канал. Вы открываете его на Peripheral, в ответ получаете PSM — это номер канала, к которому можно подключиться, и нужно с помощью тех же характеристик передать его Central. Номер динамический, система сама выбирает, какой PSM открыть в данный момент. После передачи можно уже на Сentral подключиться к Peripheral и обмениваться данными в удобном для вас формате. Давайте рассмотрим, как это сделать.

Для начала на Peripheral открываем порт с шифрованием. Можно делать и без шифрования, тогда это немного ускорит передачу.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 25

Далее мы в методе делегата получаем PSM и отправляем на другое устройство.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 26

После подключения другого устройства у нас вызовется метод, в котором из канала мы можем достать нужные нам для передачи NSStream.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 27

С Central еще проще, мы просто подключаемся к каналу с нужным номером…

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 28

… и после этого получаем нужные нам стримы. В них вы можете передавать абсолютно любые данные любого размера, и строить поверх L2CAP свой протокол. Так мы и реализовали у себя передачу фотографий получателя.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 29

Но есть подводные камни, куда же без них.

Подводные камни


Давайте рассмотрим подводные камни при работе в фоновом режиме. Поскольку вам доступны роли Peripheral и Central, вы можете подумать. что в фоне можете определять, какие устройства рядом находятся в фоновом режиме, а какие в активном. В теории так и должно было быть, но Apple ввела ограничение: телефоны, которые находятся в фоновом режиме, будь то Central или Peripheral, не доступны для других телефонов, которые тоже находятся в фоновом режиме. Также телефоны, которые находятся в фоновом режиме, не видны с неiOS-устройств. Давайте рассмотрим почему так происходит.

Когда ваше устройство активно, оно посылает обычный broadcast-пакет, в котором может быть имя устройства и список сервисов. которые предоставляет это устройство. И overflow данные — всё что не поместилось.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 30

Когда же устройство переходит в фоновый режим, оно не передает название, а список поддерживаемых сервисов переносит в overflow-данные. Если приложение активно, то при сканировании с iOS-устройства оно читает эти данные, а при переходе в фон — игнорирует. Поэтому при переходе в фон вы не сможете видеть приложения, которые также находятся в фоне. Остальные операционные системы Apple всегда игнорируют overflow-данные, поэтому если вы будете искать устройства, поддерживающие ваш сервис, то получите пустой массив. А если подключиться к каждому устройству, которое находится рядом, и запросить поддерживаемые сервисы, то в списке, возможно, будет ваш сервис, и вы сможете с ним работать.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 31

Дальше мы уже готовились передавать в тестирование, правили мелкие недочёты, занимались оптимизацией. И вдруг в какой-то момент мы стали получать в консоли эту ошибку:

CoreBluetooth[WARNING] Unknown error: 124

Самое плохое было в том, что никакой метод делегата не вызывался, мы даже не могли никак обыграть эту ошибку для пользователя. Просто сообщение в лог — и тишина, всё замирало. Никаких особых изменений не вносилось, поэтому мы начали откатываться по коммитам. И обнаружили, что однажды оптимизировали код и переделали способ записи данных. Проблема крылась в том, что не все клиенты были обновлены, поэтому возникала такая ошибка.

.write != .writeWithoutResponse

Мы, счастливые, что всё исправили, побежали скорее передавать в тестирование, а они нам почти сразу возвращают: «Ваши модные фоточки не работают. Они все недогруженные приходят». Мы начали пробовать, и правда, иногда, на разных устройствах, в разное время приходят битые фотографии. Начали искать причину.

И тут снова увидели прежнюю ошибку. Сразу подумали, что дело в разных версиях. Но после полного удаления старой версии со всех тестовых устройств ошибка всё равно воспроизводилась. Мы взгрустнули…

CoreBluetooth[WARNING] Unknown error: 722
CoreBluetooth[WARNING] Unknown error: 249
CoreBluetooth[WARNING] Unknown error: 312

Начали искать инструмент для отладки. Первое, что нам попалось, это Apple Bluetooth Explorer. Мощная программа, много всего умеет, но вот для отладки протокола Bluetooth LE одна маленькая вкладка с поиском устройств и получением характеристик. А нам-то нужно было анализировать L2CAP.

Потом нашли LightBlue Explorer. Оказалась вполне приличная программа, правда, с дизайном из iOS 7. Может делать то же самое, что и Bluetooth Explorer, а еще умеет подписываться на характеристики. И работает стабильнее. Всё хорошо, но опять без L2CAP.

И тут нам вспомнился всем известный сниффер WireShark.

Оказалось, он знаком с Bluetooth LE: может читать L2CAP, но только под Windows. Хотя это не страшно, что мы, не найдем винду, что ли. Самый большой минус — программа работает только с определенным устройством. То есть нужно было найти где-то устройство в официальном магазине. А вы сами понимаете, в большой компании вряд ли одобрят покупку непонятного устройства на барахолке. Мы даже начали просматривать зарубежные онлайн-магазины.

Но тут обнаружили в Additional Xcode Tools программу PacketLogger. Она позволяет смотреть траффик, которой идет на OS X-устройстве. А почему бы не переписать наш MoneyDrop под OS X? Он у нас уже был отдельной библиотеки. Мы просто заменили UIImage на NSImage, всё завелось само через 10 минут.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 32

Наконец-то мы могли читать пакеты, которыми обмениваются устройства. Сразу стало понятно, что в момент передачи данных по L2CAP записывалась одна из характеристик. А из-за того, что канал был полностью занят передачей фотографии, iOS игнорировала запись, а отправитель после игнора обрывал канал. После исправления проблем с передачей фотографии не было.

Bluetooth LE не так уж и страшен, или Как улучшить пользовательский опыт без особых усилий - 33

На этом всё, спасибо за прочтение :)

Полезные ссылки


WWDC/CoreBluetooth:

Bluetooth

YouTube

  • Arrow Electronics → Bluetooth Low Energy Series

Автор: eantropov

Источник

* - обязательные к заполнению поля