Введение в WebBluetooth

Опубликовано: 2022-03-10
Краткий обзор ↬ С Progressive Web Apps теперь вы можете использовать Интернет для создания полноценных приложений. Благодаря огромному количеству новых спецификаций и функций мы можем делать с Интернетом то, для чего раньше приходилось писать нативные приложения. Тем не менее, общение с аппаратными устройствами до сих пор было слишком далеко. Благодаря WebBluetooth теперь мы можем создавать PWA, которые могут управлять вашим светом, водить машину или даже управлять дроном.

С прогрессивными веб-приложениями Интернет все больше приближается к нативным приложениям. Тем не менее, с дополнительными преимуществами, присущими Интернету, такими как конфиденциальность и кросс-платформенная совместимость.

Сеть традиционно великолепно разговаривала с серверами в сети и, в частности, с серверами в Интернете. Теперь, когда Интернет движется в сторону приложений, нам также нужны те же возможности, что и в нативных приложениях.

Количество новых спецификаций и функций, которые были реализованы в браузерах за последние несколько лет, ошеломляет. У нас есть спецификации для работы с 3D, такие как WebGL и грядущий WebGPU. Мы можем транслировать и генерировать аудио, смотреть видео и использовать веб-камеру в качестве устройства ввода. Мы также можем запускать код почти с естественной скоростью, используя WebAssembly. Более того, несмотря на то, что первоначально Интернет был только сетевой средой, он перешел к офлайн-поддержке сервисными работниками.

Это здорово и все такое, но одна область была почти исключительной областью для нативных приложений: общение с устройствами. Это проблема, которую мы пытаемся решить в течение долгого времени, и, наверное, каждый когда-то сталкивался с ней. Сеть отлично подходит для общения с серверами, но не для общения с устройствами . Подумайте, например, о попытке настроить маршрутизатор в вашей сети. Скорее всего, вам пришлось ввести IP-адрес и использовать веб-интерфейс через обычное HTTP-соединение без какой-либо безопасности. Это просто плохой опыт и плохая безопасность. Кроме того, как узнать правильный IP-адрес?

Еще после прыжка! Продолжить чтение ниже ↓

HTTP также является первой проблемой, с которой мы сталкиваемся, когда пытаемся создать прогрессивное веб-приложение, которое пытается взаимодействовать с устройством. PWA — это только HTTPS, а локальные устройства всегда только HTTP. Вам нужен сертификат для HTTPS, а чтобы получить сертификат, вам нужен общедоступный сервер с доменным именем (я говорю об устройствах в нашей локальной сети, которые находятся вне досягаемости).

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

WebBluetooth — это новая спецификация, реализованная в Chrome и Samsung Internet, которая позволяет нам напрямую связываться с устройствами Bluetooth с низким энергопотреблением из браузера. Прогрессивные веб-приложения в сочетании с WebBluetooth обеспечивают безопасность и удобство веб-приложения с возможностью прямого взаимодействия с устройствами.

У Bluetooth довольно плохая репутация из-за ограниченного радиуса действия, плохого качества звука и проблем с сопряжением. Но почти все эти проблемы остались в прошлом. Bluetooth Low Energy — это современная спецификация, которая имеет мало общего со старыми спецификациями Bluetooth , за исключением использования того же частотного спектра. Ежедневно поставляется более 10 миллионов устройств с поддержкой Bluetooth. Это включает в себя компьютеры и телефоны, а также различные устройства, такие как датчики сердечного ритма и глюкозы, устройства IoT, такие как лампочки, и игрушки, такие как автомобили с дистанционным управлением и дроны.

Рекомендуемая литература : Понимание платформ на основе API: руководство для менеджеров по продуктам

Скучная теоретическая часть

Поскольку Bluetooth сам по себе не является веб-технологией, в нем используется некоторая лексика, которая может показаться нам незнакомой. Итак, давайте рассмотрим, как работает Bluetooth, и немного терминологии.

Каждое устройство Bluetooth является либо «центральным устройством», либо «периферийным устройством». Только центральные устройства могут инициировать связь и могут общаться только с периферийными устройствами. Примером центрального устройства может быть компьютер или мобильный телефон.

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

телефон посередине, разговаривающий с несколькими периферийными устройствами, такими как дрон, игрушка-робот, пульсометр и лампочка
Центральное устройство может взаимодействовать с несколькими периферийными устройствами. (Большой превью)

Центральное устройство может общаться с несколькими периферийными устройствами одновременно и может передавать сообщения, если захочет. Таким образом, монитор сердечного ритма не может общаться с вашими лампочками, однако вы можете написать программу, которая работает на центральном устройстве, которое получает ваш сердечный ритм и включает красный свет, если частота сердечных сокращений превышает определенный порог.

Когда мы говорим о WebBluetooth, мы имеем в виду особую часть спецификации Bluetooth под названием Generic Attribute Profile, которая имеет весьма очевидную аббревиатуру GATT. (Видимо, GAP уже был занят.)

В контексте ГАТТ речь идет уже не о центральных устройствах и периферийных устройствах, а о клиентах и ​​серверах. Ваши лампочки — это серверы. Это может показаться нелогичным, но на самом деле имеет смысл, если подумать. Лампочка предлагает услугу, то есть свет. Точно так же, как когда браузер подключается к серверу в Интернете, ваш телефон или компьютер является клиентом, который подключается к серверу GATT в лампочке.

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

иерархия сервисов и характеристик по сравнению с более привычными конструкциями из JavaScript — сервер подобен массиву объектов, сервис — объекту в этом массиве, характеристика — свойству этого объекта, и оба имеют значения
Упрощенная иерархия услуг и характеристик. (Большой превью)

В отличие от свойств объектов услуги и характеристики не идентифицируются строкой. Каждая услуга и характеристика имеют уникальный UUID длиной 16 или 128 бит. Официально 16-битный UUID зарезервирован для официальных стандартов, но почти никто не следует этому правилу. Наконец, каждое значение представляет собой массив байтов. В Bluetooth нет причудливых типов данных.

Пристальный взгляд на лампочку Bluetooth

Итак, давайте посмотрим на реальное Bluetooth-устройство: Mipow Playbulb Sphere. Вы можете использовать приложение, такое как BLE Scanner или nRF Connect, для подключения к устройству и просмотра всех услуг и характеристик. В этом случае я использую приложение BLE Scanner для iOS.

Первое, что вы видите при подключении к лампочке — это список сервисов. Есть несколько стандартизированных, таких как служба информации об устройстве и служба батареи. Но есть и специальные сервисы. Меня особенно интересует сервис с 16-битным UUID 0xff0f . Если открыть этот сервис, то можно увидеть длинный список характеристик. Я понятия не имею, что делает большинство этих характеристик, так как они идентифицируются только по UUID и потому, что они, к сожалению, являются частью пользовательского сервиса; они не стандартизированы, и производитель не предоставил никакой документации.

Первая характеристика с UUID 0xfffc кажется особенно интересной. Он имеет значение четыре байта. Если мы изменим значение этих байтов с 0x00000000 на 0x00ff0000 , лампочка станет красной. Изменение его на 0x0000ff00 делает лампочку зеленой, а 0x000000ff — синей. Это цвета RGB, и они точно соответствуют шестнадцатеричным цветам, которые мы используем в HTML и CSS.

Что делает этот первый байт? Что ж, если мы изменим значение на 0xff000000 , лампочка станет белой. Лампочка содержит четыре разных светодиода, и, изменяя значение каждого из четырех байтов, мы можем создать любой цвет, какой захотим.

API-интерфейс WebBluetooth

Это фантастика, что мы можем использовать собственное приложение для изменения цвета лампочки, но как мы можем сделать это из браузера? Оказывается, со знаниями о Bluetooth и GATT, которые мы только что узнали, это относительно просто благодаря API WebBluetooth. Чтобы изменить цвет лампочки, требуется всего пара строк JavaScript.

Давайте рассмотрим API WebBluetooth.

Подключение к устройству

Первое, что нам нужно сделать, это подключиться из браузера к устройству. Мы вызываем функцию navigator.bluetooth.requestDevice() и предоставляем функции объект конфигурации. Этот объект содержит информацию о том, какое устройство мы хотим использовать и какие службы должны быть доступны для нашего API.

В следующем примере мы фильтруем имя устройства, так как хотим видеть только те устройства, которые содержат префикс PLAYBULB в имени. Мы также указываем 0xff0f как службу, которую мы хотим использовать. Поскольку requestDevice() возвращает обещание, мы можем дождаться результата.

 let device = await navigator.bluetooth.requestDevice({ filters: [ { namePrefix: 'PLAYBULB' } ], optionalServices: [ 0xff0f ] });

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

браузер Chrome с окном, которое нужно использовать пользователю для подключения к устройству, с видимой лампочкой в ​​списке устройств
Пользователь должен вручную подключиться, выбрав устройство. (Большой превью)

Получив доступ к устройству, мы можем подключиться к серверу GATT, вызвав функцию connect() в gatt устройства, и дождаться результата.

 let server = await device.gatt.connect();

Когда у нас есть сервер, мы можем вызвать getPrimaryService() на сервере с UUID службы, которую мы хотим использовать в качестве параметра, и дождаться результата.

 let service = await server.getPrimaryService(0xff0f);

Затем вызовите getCharacteristic() для службы с UUID характеристики в качестве параметра и снова дождитесь результата.

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

 let characteristic = await service.getCharacteristic(0xfffc);

Запись данных

Чтобы записать данные, мы можем вызвать функцию writeValue() для характеристики со значением, которое мы хотим записать как ArrayBuffer, который является методом хранения двоичных данных. Причина, по которой мы не можем использовать обычный массив, заключается в том, что обычные массивы могут содержать данные различных типов и даже могут иметь пустые дыры.

Поскольку мы не можем создавать или изменять ArrayBuffer напрямую, вместо этого мы используем «типизированный массив». Каждый элемент типизированного массива всегда имеет один и тот же тип и не имеет пробелов. В нашем случае мы собираемся использовать Uint8Array без знака, поэтому он не может содержать отрицательных чисел; целое число, поэтому оно не может содержать дроби; и это 8 бит и может содержать только значения от 0 до 255. Другими словами: массив байтов.

 characteristic.writeValue( new Uint8Array([ 0, r, g, b ]) );

Мы уже знаем, как работает эта конкретная лампочка. Мы должны предоставить четыре байта, по одному для каждого светодиода. Каждый байт имеет значение от 0 до 255, и в этом случае мы хотим использовать только красный, зеленый и синий светодиоды, поэтому мы оставляем белый светодиод выключенным, используя значение 0.

Чтение данных

Чтобы прочитать текущий цвет лампочки, мы можем использовать readValue() и дождаться результата.

 let value = await characteristic.readValue(); let r = value.getUint8(1); let g = value.getUint8(2); let b = value.getUint8(3);

Значение, которое мы возвращаем, — это DataView ArrayBuffer, и оно предлагает способ получить данные из ArrayBuffer. В нашем случае мы можем использовать getUint8() с индексом в качестве параметра для извлечения отдельных байтов из массива.

Получение уведомлений об изменениях

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

 characteristic.addEventListener( 'characteristicvaluechanged', e => { let r = e.target.value.getUint8(1); let g = e.target.value.getUint8(2); let b = e.target.value.getUint8(3); } ); characteristic.startNotifications();

Чтобы получить обратный вызов всякий раз, когда значение изменяется, мы должны вызвать addEventListener() для характеристики с параметром characteristicvaluechanged и функцией обратного вызова. Всякий раз, когда значение изменяется, функция обратного вызова будет вызываться с объектом события в качестве параметра, и мы можем получить данные из свойства значения цели события. И, наконец, снова извлеките отдельные байты из DataView ArrayBuffer.

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

Заключение

Теперь мы прошли более 90% API WebBluetooth. С помощью всего нескольких вызовов функций и отправки 4 байтов вы можете создать веб-приложение, которое будет управлять цветами ваших лампочек. Если вы добавите еще несколько строк, вы даже сможете управлять игрушечной машинкой или управлять дроном. Поскольку на рынке появляется все больше и больше устройств Bluetooth, возможности безграничны.

Дополнительные ресурсы

  • Bluetooth.качает! Демо | (Исходный код на GitHub)
  • «Спецификация Web Bluetooth», группа сообщества Web Bluetooth.
  • Open GATT Registry Неофициальный сборник документации по службам Generic Attribute для устройств Bluetooth с низким энергопотреблением.