Русский
Русский
English
Статистика
Реклама

Bluetooth

Security Week 39 две уязвимости в протоколе Bluetooth

21.09.2020 18:14:10 | Автор: admin


За последние две недели стало известно сразу о двух уязвимостях в стандарте беспроводной связи Bluetooth. Сначала 9 сентября организация Bluetooth SIG распространила предупреждение о семействе атак BLURtooth. В теории уязвимость в спецификациях Bluetooth 4.2 и 5.0 позволяет организовать MitM-атаку. На практике для этого требуется совпадение множества условий, например подключение (с ограниченными правами) к целевому устройству.

Уязвимость обнаружена на стыке двух вариантов соединения Bluetooth традиционного Basic Rate / Enhanced Data Rate и энергоэффективного Bluetooth Low Energy. Чтобы не авторизоваться дважды по разным протоколам, в устройствах, поддерживающих и BR/EDR, и BLE, генерируются общие ключи длительного срока действия. Спецификация позволяет перезаписывать ключи, если требуется более надежный режим передачи данных. Но в результате с устройством можно установить связь либо вовсе без должной авторизации, либо защита соединения легко взламывается.

Вторую уязвимость, получившую название BLESA, обнаружили в спецификации Bluetooth Low Energy. Она позволяет подключаться к другим устройствам без авторизации, имитируя процедуру восстановления соединения после разрыва связи.

На практике это выглядит так: к смартфону подключено устройство (например, фитнес-трекер), затем соединение с ним обрывают и подключают к смартфону устройство атакующего. Дополнительная авторизация не требуется, а перехватить идентификатор легитимного устройства легко, так как эти данные передаются открытым текстом. Простота повторного подключения устройства обернулась прорехой в безопасности.


Источники
BLURTooth:
сообщение CERT Университета Карнеги Меллона;
бюллетень Bluetooth SIG;
новость на сайте Bleeping Computer;
новость на Хабре.

BLESA:
новость;
исследовательская работа;
новость на Хабре с PoC-видео.

Информация об этих атаках раскрыта совершенно по-разному. О BLESA опубликована научная работа с подробным описанием процесса атаки. Про BLURtooth лишь два коротких сообщения без деталей. Возможно, это связано с тем, что для BLESA уже выпустили патч (как минимум для устройств Apple), а также готовятся заплатки для Android и универсального стека Bluez.

BLURtooth пока что проблема без решения. Хотя есть у этих атак и общее: невысокая вероятность использования на практике из-за необходимости находиться рядом с жертвой и сомнительных (как минимум неисследованных) перспектив с точки зрения кражи данных.

Обе уязвимости в будущем могут стать этапом более серьезной атаки на IoT-устройства, тем более что обновить стек Bluetooth получится далеко не везде.

Что еще произошло
Специалисты Лаборатории Касперского опубликовали отчет о развитии угроз во II квартале 2020 года. Из интересного: рост числа вредоносных атак игровой тематики, в частности фишинг и распространение вредоносного ПО, связанного с платформой Steam.

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

Другое исследование Лаборатории Касперского посвящено уязвимости нулевого дня в Internet Explorer 11. В паре с другой дырой, не столь опасной самой по себе, баг в браузере обеспечивал полный контроль над целевой системой.

Печальный, но предсказуемый случай: в Германии атака трояна-шифровальщика на инфраструктуру больницы привела к смерти пациента.

В среду 16 сентября разработчики обновили CMS Drupal, в том числе исправили критическую XSS-уязвимость.

Интересный случай произошел с WordPress-плагином Discount Rules for WooCommerce. Две серьезных уязвимости пропатчили лишь с третьей попытки.



Издание Bleeping Computer сообщает о фишинговой атаке, которая маскируется под тренинг по информационной безопасности.

Компания Google вводит запрет на ПО типа stalkerware в Google Play. Точнее, нельзя наблюдать за пользователем скрытно: если такие функции есть, пользователя надо предупредить, что за его перемещениями и действиями будут наблюдать.

Эксплойт для уязвимости Zerologon в Windows появился в публичном доступе. Патч для этой дыры выпустили еще в августе этого года.
Подробнее..

Security Week 43 новые уязвимости в Bluetooth для ПК и автомобилей

19.10.2020 18:20:51 | Автор: admin
Прошедшая неделя добавила к списку потенциально опасных уязвимостей в Bluetooth еще один баг на этот раз не в самом протоколе, а в его реализации для Linux, в стеке BlueZ. Исследователь Энди Нгуен из компании Google назвал новую проблему BleedingTooth. Пока описание уязвимости состоит из одного твита и PoC-видео. Ключевое слово в коротком сообщении без подробностей zero click. Если на вашем устройстве под управлением Linux будет включен Bluetooth-модуль и атакующий подберется к вам достаточно близко, он сможет выполнить произвольный код с высшими привилегиями. Действий со стороны жертвы вроде подтверждения соединения не требуется.



Наличие уязвимости подтвердила компания Intel в своем бюллетене (также без технических деталей). Строго говоря, атака на Bluez складывается из трех уязвимостей с высшим рейтингом опасности в 8,3 балла по шкале CVSS v3.

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


Картинка в начале статьи взята из другого свежего исследования. Хакер, известный как Kevin2600, роняет мультимедийный софт в автомобиле Audi 2014 года, просто меняя имя подключенного по Bluetooth смартфона на %x%x%x%x%x. Эта строка вызывает утечку памяти в софте и последующий сбой.


Эту проблему вообще с трудом можно назвать уязвимостью. Интересно, что она не первая. Ранее точно такой же сбой обнаружили в мультимедийной системе Mercedes Benz, а три года назад в BMW.

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

Что еще произошло


Компания Microsoft закрыла с помощью внеочередного патча две серьезные уязвимости, одна из них в наборе кодеков Windows. Обе проблемы позволяют получить полный контроль над системой.

В этом обзоре инцидента с трояном-шифровальщиком (атака на бизнес, требование выкупа в размере 6 млн долларов) приводится интересный почасовой анализ развития атаки. Вся операция от первого запуска вредоносного кода до полного контроля над инфраструктурой компании заняла 29 часов.

В прошлый понедельник, 12 октября, компания Microsoft сообщила о захвате контроля над ботнетом Trickbot. Помимо прочего, управление серверами шло через суд, где Microsoft привела аргумент о нарушении своих авторских прав.

Издание Ars Technica пишет о бэкдоре в детских умных часах Xplora X4. Для удаленного управления устройством (включая встроенную камеру), впрочем, требуется знать ключ, уникальный для каждого устройства, а также номер мобильного телефона.

Новое исследование описывает знакомый способ извлечения прибыли из взломанных IoT-девайсов: на базе ботнета из 9000 взломанных устройств поднимается анонимный прокси-сервер, доступ к которому продается за деньги.

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

Security Week 49 взлом Tesla через Bluetooth

30.11.2020 18:20:43 | Автор: admin
Исследователь Леннерт Вютерс (Lennert Wouters) из Лёвенского католического университета нашел красивый способ угона Tesla Model X через переписывание прошивки фирменного ключа к автомобилю. На фото ниже показано устройство для взлома, состоящего из батареи, компьютера Raspberry Pi и бортового компьютера Tesla, купленного на разборке. Себестоимость комплекта около 300 долларов. Для успешной атаки (достаточно нетривиальной) придется приблизиться к владельцу автомобиля с ключом в кармане, а также заранее переписать VIN.


По словам исследователя, проблема заключается в некорректной реализации достаточно защищенного протокола связи между брелком и бортовым компьютером Model X. Прежде всего, прошивку ключа можно обновлять по Bluetooth, причем валидность кода никак не проверяется. Правда, и сам режим обновления прошивки нестандартный, и для его активации Вютерсу как раз и понадобился подменный бортовой модуль Tesla.



Обычно модуль Bluetooth активируется в ключе Tesla только после смены батарейки. Вютерс обнаружил, что беспроводную связь также можно включить из автомобиля за это отвечает устройство Body Control Module. Такое удалось найти на eBay: там они продаются по 50100 долларов. Следующая ошибка реализации протокол связи, в котором подлинность сигнала от Body Control Module удостоверяется кодом на основе пяти последних цифр VIN-номера. А вот его можно просто подсмотреть: он, как у почти всех автомобилей, виден под лобовым стеклом.

Следующий этап: после подключения к брелку нужно переписать его прошивку. Она, в свою очередь, позволяет вытащить секретный ключ из аппаратного хранилища. Этот ключ позволяет атакующему открыть автомобиль. Но и только. Завести машину и уехать не получится. Для этого нужно, уже находясь в автомобиле, подключиться к CAN-шине и заставить встроенный Body Control Module прописать брелок злоумышленника как доверенный.

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

Описанный сценарий крайне далек от ночного кошмара для производителя постоянно подключенного к сети автомобиля полной компрометации систем безопасности в удаленном режиме. Здесь и за владельцем последить придется, и в машине поковыряться перед угоном. Зато та самая компьютеризация Tesla, и конкретно возможность обновления прошивки ключа по воздуху, пошла на пользу при исправлении багов. В похожей ситуации другому владельцу пришлось бы добираться до сервиса, чтобы перепрошить брелок. Здесь же уязвимость закрылась просто: обновление прилетело по воздуху в автомобиль, а от него уже автоматически были прошиты брелки.

Что еще произошло:

В среду произошел серьезный сбой в облачной инфраструктуре Amazon Web Services. В течение нескольких часов был недоступен кластер US-EAST-1, что привело к многочисленным перебоям в работе сетевых сервисов, включая IoT-устройства, такие как пылесосы Roomba и умные звонки Amazon Ring. В подробном описании инцидента раскрывается причина: были случайно превышены ограничения ОС по количеству потоков выполнения. Перезагружать пострадавшие серверы пришлось вручную.

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

Еще одну критическую уязвимость в системе менеджмента мобильных устройств MobileIron активно эксплуатируют злоумышленники.
Подробнее..

Пиринговые мессенджеры враг государства?

24.05.2021 12:21:25 | Автор: admin


В случае полного отключения интернета одна из главных проблем общение с товарищами и родственниками. Опыт Гонконга показывает, что для этого хорошо подходят децентрализованные P2P-мессенджеры, которые работают без интернета, используя mesh-сеть по протоколам Wi-Fi Direct, Bluetooth, Apple Multipeer Connectivity Framework, ANT+, LoRa и др.

Для эффективной коммуникации приложение нужно скачать максимальному количеству человек до начала блокады интернета. Иначе придётся искать файлы после блокады. Человек с нужными файлами станет настоящим авторитетом в офисе или в классе (как это было в Беларуси в августе 2020 года за файлами Psiphon люди реально приезжали из других микрорайонов города).

Вообще, вся история сетей wireless mesh намекает на то, что эта технология крайне не нравится правоохранительным органам.

Mesh-сети


Приложения типа FireChat создают mesh-сеть, используя Bluetooth и прямые подключения через Wi-Fi. Они обеспечивают обмен сообщениями и фотографиями в офлайне между устройствами, находящимися друг от друга на расстоянии примерно до 60 метров.

Все пользователи образуют единую сеть, которая расширяется с ростом количества устройств. На пути к получателю (получателям) сообщения передаются от одного устройства к другому.

Другими словами, вам достаточно приблизиться на 60 метров к ближайшему пользователю и вы можете отправить сообщение кому угодно. Каждый юзер нода в сети. Без интернета.

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

Если какой-нибудь узел mesh-сети вдруг выйдет в интернет, то сообщения пакетом от всех пользователей уйдут получателям по всему миру.

Особенно удобно, если каналы или комнаты в мессенджере создаются автоматически и объединяют людей, которые находятся рядом с друг другом (многоквартирный дом, стадион, концертный зал).

Для передачи текстовых сообщений можно приспособить практически любую mesh-сеть, даже сеть из геометок Apple AirTag, которые вообще-то предназначены не для общения людей, а для поиска утерянных вещей. В мае 2021 года хакерам удалось расшифровать трафик AirTag и передать по сети Apple Find My произвольный текст под видом оригинальных зашифрованных сообщений с GPS-координатами. Скорость передачи составила 3байта в секунду. Задержка в сети от 1 до 60 минут.



Подобные mesh-сети есть также у Samsung (Smart Things) и Amazon (Sidewalk) на протоколе LoRa. В принципе, можно использовать эту инфраструктуру для пирингового обмена сообщениями, а также для съёма данных с устройств вне зоны доступа в интернет.

Трагедия FireСhat


FireChat проприетарное приложение от американской компании Open Garden. Эта фирма прекратила дальнейшую разработку, не открыв исходники.

Последняя версия приложения: 9.0.14 (копия на 4pda) вышла полтора года назад (примечание: доступ к сайту 4pda затруднён с территории РФ).


FireChat

Первая версия FireChat появилась в марте 2014 года под iOS, в апреле под Android. Среди сооснователей Open Garden и разработчиков программы Станислав Шалунов и Грег Хазел из компании BitTorrent, где они делали торрент-клиент uTorrent (200 млн пользователей). Там и познакомились.

Вероятно, предприниматели рассчитывали, что FireСhat станет настолько же успешным, как файлоообменные P2P-приложения. Если представить себе эту картину, то весь мир может объединиться в mesh-сеть, а интернет становится практически не нужен! Даже хостинг сайтов теоретически можно размазать в распределённой сети. Такая фантазия.

Очень быстро FireСhat приобрёл популярность в Ираке после того, как местное правительство ввело ограничения на использование интернета, а затем то же самое произошло в Гонконге, во время массовых протестов 2014 года.


Главной проблемой FireСhat во время массовых протестов в Гонконге стала безопасность. Сама архитектура открытой mesh-сети предполагает, что все пользователи приложения светятся как радиомаячки на расстоянии 60 метров, а то и больше. Так что полиции отловить их было очень просто. Наличие программы на телефоне однозначно доказывало вину. Можно предположить, что сотни или тысячи пользователей были арестованы благодаря FireСhat. К тому же, в приложении не использовалось шифрование, так что никакие сообщения не были действительно приватными.

Возможно, критика от специалистов по ИБ повлияла на решение американской компании Open Garden прекратить разработку и не публиковать исходники. Ну или ребята увидели, что всемирная популярность торрентов им не светит, а развитие программы пошло совсем не по тому пути.

Когда появился FireСhat, это была единственная программа такого рода, которая позволяла пользователям создавать mеsh-сети в офлайновом режиме (без интернета) и обмениваться сообщениями1.

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


Фото из статьи FireChat мессенджер, на котором работают протесты в Гонконге, The Guardian

Тем более обидно, если FireСhat создавали специально для Гонконга, как разработку аналогичного приложения Commotion Wireless в 2011 году профинансировал госдепартамент США в преддверии Арабской весны. Хотели как лучше, а получилось как всегда

После FireСhat фирма Open Garden торговала электронными сим-картами (eSIM), продвигала свою криптовалюту, но в последние годы про неё ничего не слышно.

Вообще, приложение FireСhat было не очень качественным. Во-первых, закрытый код. Во-вторых, для регистрации обязательно (!) был нужен доступ в интернет, хотя потом оно работало в офлайне. Короче, странная программа. Может и хорошо, что разработчики её похоронили.

Рация Zello


Близкой по логике функционирования является интернет-рация Zello, которая заблокирована в Российской Федерации в апреле 2017 года.

Радио работает по принципу уоки-токи или push-to-talk. Основное преимущество возможность слушать голосовые сообщения по громкой связи в реал-тайме, вообще не притрагиваясь к смартфону и в то же время быть на связи с большим количеством пользователей.

Хотя здесь передаётся не текст/картинки, а звук, но принцип работы такой же через mesh-сеть, без интернета. Близкие друг к другу пользователи соединяются друг с другом напрямую и передают сообщения дальше, по цепочке. Что важно, здесь используется сквозное шифрование (E2E).

В России приложение получило особую популярность среди дальнобойщиков и других автомобилистов, поскольку очень удобно (и приятно) беседовать с попутчиками, которые едут по той же трассе. Они подскажут о радаре в кустах, о ДТП на дороге и т.д. Репрессии со стороны государства начались после массовых протестов дальнобойщиков в связи с введением обязательной платы миллиардеру Игорю Ротенбергу за проезд тяжёлых грузовиков по дорогам России (система Платон). Координация протестов осуществлялась через Zello, поэтому программу решили заблокировать.

Это был первый прецедент, когда власти попытались сломать мессенджер, работающий по VPN. Понадобился целый год, чтобы заблокировать свыше 4тыс. IP-адресов из облака AWS, что не принесло успеха. Тогда РКН пошёл на шантаж компании Amazon, угрожая заблокировать 26 подсетей AWS, в сумме 13,5млн адресов.

Компания Amazon оказалась не готова к войне и отказалась предоставлять услуги Zello. Лишившийся облачной инфраструктуры сервис было легко заблокировать.

После успеха с блокировкой облачной VPN-инфраструктуры Zello, в 2018 году власти решили, что смогут успешно заблокировать такую же облачную инфраструктуру мессенджера Telegram. Но здесь нашла коса на камень: Павел Дуров инвестировал миллионы долларов в покупку всё новых и новых инстансов AWS, его брат Николай с коллегами запрограммировал систему обхода блокировок через прокси, а компании Amazon и Google выдержали блокировку своих подсетей в РФ. В итоге властям пришлось заблокировать 18 миллионов IP-адресов AWS и Google Cloud, что нарушило работу крупных ритейл-компаний, банков из топ-20, частных клиник и тысяч бизнесов в России. После двух лет выматывающей борьбы государство сдалось: руководителя Роскомнадзора уволили, Telegram разблокировали, а IP-адреса облачных сервисов удалили из чёрного списка.

Криптомессенджер Briar


Четыре года назад, в июле 2017 года, вышла первая публичная бета-версия мессенджера Briar, который работает через Tor или в офлайне, через пиринг. Его разработка продолжалась аж три года. Но продукт вышел на редкость качественный.

Это уже честный опенсорс. Приложение можно собрать из исходников (пошаговая инструкция для Android Studio). Оно доступно в каталогах приложений (например, Google Play) и отлично поддерживается: последняя версия 1.2.20 от 2апреля 2021года. Реализована стойкая криптография, сквозное шифрование.


По умолчанию мессенджер работает через интернет по протоколу Tor (луковичная маршрутизация). В этом случае программа создаёт на устройстве пользователя скрытый сервис Tor и соединяется со скрытыми сервисами Tor других людей из списка контактов. В случае отсутствия интернета она переходит на peer-to-peer коммуникации через Wi-Fi Direct, локальную сеть или Bluetooth.

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

Поддерживает передачу сообщений (но не файлов), приватные группы, блоги/статусы и импорт RSS. Есть несколько полезных функций, таких как блокировка приложения (по PIN-коду).

Система относительно защищена от посторонних: добавление новых контактов производится по QR-коду с другого телефона (подразумевается физическое присутствие лица) или через обмен контактами, или по уникальной ссылке. Таким образом, в контакты не должен попасть человек, с которым никто не знаком.



Briar Project некоммерческий проект, который сейчас ведут шесть добровольцев. Код на 97,7% написан на Java (+немного Kotlin, Python и Ruby) и поставляется под GPLv3. Вскоре после первого релиза код прошёл независимый аудит безопасности от компании Cure53. Она известна своим аудитом проектов SecureDrop, Cryptocat и Dovecot. Так что Briar действительно надёжный вариант для пиринговых коммуникаций.

Единственный минус, что приложение выпущено только в версии под Android. Официальной версии для iOS нет и не будет, потому что эта платформа накладывает жёсткие ограничения на разработчиков, не позволяющие реализовать некоторые идеи P2P, и является закрытым проприетарным продуктом.

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


Matrix, Riot, Element


Официальный клиент Element (бывший Riot) для относительно децентрализованной сети Matrix не поддерживает пиринговые коммуникации в офлайн-режиме и формирование mesh-сети.

В последнее время этот клиент получил некоторую известность. Сейчас количество пользователей Matrix/Element сравнимо с Briar или больше. У него есть сквозное шифрование, мосты в IRC, Slack, Telegram, Jitsi Meet и др. Но именно пиринга в офлайне нет.

См. также:


Secure Scuttlebutt p2p социальная сеть, работающая и в офлайне (есть клиент Manyverse для Android и iOS)

P. S. Кроме FireСhat, протестующие в Гонконге использовали малоизвестное мексиканское приложение Bridgefy, которое тоже умеет формировать mesh-сеть и передавать сообщения в офлайне. В октябре 2020 года приложение перешло на криптографический протокол Signal.

1 На самом деле ещё раньше были другие приложения, такие как Serval Mesh от проекта Serval Project или древний Commotion Wireless от Open Technology Initiative (с финансированием госдепа), но все эти разработки давно прекращены [вернуться]




На правах рекламы


Мощные виртуальные серверы на базе процессоров AMD EPYC для любых целей, в том числе для установки VPN.

Присоединяйтесь к нашему чату в Telegram.

Подробнее..

Как я делаю цифровую минигитару

06.09.2020 16:13:36 | Автор: admin
image

В этой статье я постараюсь в общих чертах описать путь создания девайса от идеи до реализации юзабельного прототипа.

Меня зовут Дмитрий Дударев. Я занимаюсь разработкой электроники и очень люблю создавать различные портативные девайсы. Еще я люблю музыку. Полгода назад я взял у друга акустическую гитару чтобы попытаться научиться на ней играть по урокам из ютуба и табулатурам. Было тяжело. То ли я неправильно что-то делал, то ли плохо старался, то ли в обществе моих предков мелкая моторика вредила размножению. В любом случае, ничего кроме звуков дребезжащих струн у меня не выходило. Мое негодование усиливала постоянная расстройка струн. Да и окружающим тысячный раз слушать мою кривую Nothing else matters удовольствия не доставляло.

Но в этих муках про главное правило электронщика я не забыл. Если что-то существует, значит туда можно вставить микроконтроллер. Или, хотя бы, сделать портативную электронную модификацию.


Идея


В голове начала вырисовываться структура цифровой гитары.

Первым делом я составил список требований к девайсу:

  1. Устройство должно имитировать гитару с 6-ю струнами и 12-ю ладами на грифе
  2. Должно быть компактным, в идеале складным, чтобы можно было брать его с собой куда угодно
  3. Должно подключаться ко всем популярным осям Android, IOS, Windows, Linux, MacOS и определяться там как MIDI устройство без каких-либо драйверов
  4. Работа от аккумулятора
  5. Подключение должно производиться без проводов (но раз уж там будет USB разъем для зарядки, то и по проводу пусть тоже подключается)
  6. Возможность сразу начать играть, без необходимости в долгих тренировках по адаптации кистевых связок
  7. На каждой струне и каждом элементе грифа должно быть по светодиоду, чтобы можно было запустить табулатуру мелодии, и гитара сама показывала куда нужно прикладывать руки
  8. Возможность использования основных техник игры на гитаре: hummer on, pull off, slide, vibrato
  9. Задержка передачи midi команд не более 10мс
  10. Все должно собираться из подручных материалов без сложных техпроцессов и дорогой электроники

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

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

Аналоги


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



Современные компактные midi гитары тоже существуют, но рассчитаны на более профессиональную целевую аудиторию, дорогие и, самое главное, без режима жми на лампочки. Есть даже экзотические варианты с айпадом вместо струн или напоминающее моллюска.



Значит, можно приступать.

Proof of concept


Начать решил с проверкой концепции. Минимальный прототип на скорую руку.
Для начала нужно определиться с элементной базой.

Контроллер

В своих проектах я чаще всего использую STM32. Они мощные, дешевые, доступные. Выбрал STM32F042. В нем есть USB (причем, со специальным внутренним генератором на 48МГц чтобы не вешать внешний кварц), 32-битное ядро, и вся необходимая периферия. И все это при стоимости меньше бакса.

Беспроводное подключение решил оставить на следующую итерацию.

Струны на дэке

В качестве струн решил напечатать пластиковые язычки, закрепить их на потенциометрах с пружинками и измерять углы отклонения.

Замоделил в солиде и напечатал для оценки эргономики.





Получилось довольно приятно на ощупь. Должно работать.

Сенсоры на грифе

На гитаре предполагаются 6 струн и 12 ладов. Суммарно это 72 сенсора на грифе и еще 6 на дэке. Можно было бы использовать на каждый элемент по тактовой кнопке, но, во-первых, они щелкают, во-вторых, не получится реализовать техники вроде slide или vibrato. Хотелось бы еще и усилие нажатия определять.

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



В продаже их найти в нужном количестве оказалось очень сложно, да и стоят немало. Пришлось заказать на али.

АЦП

Для считывания состояния каждого датчика нужен ацп. В STM-ке их всего несколько штук, так что для опроса 78 датчиков понадобится что-то еще. Можно было бы использовать внешние многоканальные ацп микросхемы, но они слишком дорогие. Поэтому я решил поставить 5 дешевых 16-канальных аналоговых мультиплексора CD74HC4067 и подключить к каждому по одному каналу ацп STM-ки.

Плата

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







Прежде чем заказывать печать платы, решил дождаться тензорезисторов. И, как оказалось, не зря.

Из 80-ти датчиков рабочими оказались только несколько, и то с разными параметрами.



От изображения на сайте продавца они отличаются заметно в худшую сторону.

И чего я ожидал, покупая электронику на али?..

И тут меня осенило.

Можно ведь применить другой метод детектирования измерение емкости как в датчиках прикосновения. Это гораздо дешевле и доступнее. А если правильно спроектировать механику, то можно и усилие определять.

Что ж. Удаляю все что было сделано.



Начинаю сначала


В новой версии минималистичного proof of concept-а в качестве сенсорных элементов я выбрал напиленные из 4мм медного прутка цилиндрики, припаянные к плате.

Теперь нужно придумать как измерять 78 емкостей.

Опрос сенсоров

Немного погуглив, выяснил, что существует множество микросхем контроллеров сенсорных клавиатур. Среди них удалось найти дешевый 12-канальный измеритель емкости общего назначения. Он измеряет емкость в масштабах единиц пикофарад, чего должно быть достаточно для схемы измерения усилия, которую я планирую реализовать в следующих модификациях.
Дополнительно на всякий случай повесил на каждый элемент грифа по посадочному месту для кнопки или чего-то подобного. И сделал вырезы в плате, чтобы можно было не только прикоснуться к цилиндрику, но и прожать его внутрь. Можно будет поэкспериментировать с разными техниками игры.

Микросхема подключается по I2C интерфейсу и имеет 2 конфигурационных пина, задающих адрес. Соответственно, на одну шину можно повесить максимум 4 микросхемы. А мне нужно 12. Не проблема, распределяю их на три группы и подключаю к шине STM-ки через мультиплексор.

Платы





На этот раз плату удалось заказать и даже дождаться ее изготовления.

После запайки комплектухи, медных цилиндриков и потенциометров понял, что конструкция с пластиковыми струнами получается слишком сложной. Поэтому решил пока что повесить на дэку такие же сенсорные цилиндрики, но подлиннее. Чтобы не переделывать всю плату, сделал небольшую платку-накладку и подключил ее ко второй I2C шине STM-ки.



Железяка готова. Следующая задача заставить ее играть.

Софт


План следующий:

  1. Скачать виртуальный синтезатор, который может работать с MIDI устройствами и издавать гитарные звуки.
  2. Написать прошивку, которая будет опрашивать сенсоры и передавать результаты в комп через USB custom HID интерфейс около 100 раз в секунду.
  3. Написать программу на питоне, которая будет принимать эти данные, эмулировать виртуальное MIDI устройство, генерировать MIDI пакеты и отправлять их на виртуальный синтезатор.

Разбираться как сразу прикинуться MIDI устройством решил чуть позже.

Чем воспроизводить звук?

Виртуальных синтезаторов под винду с поддержкой MIDI оказалось довольно много. Я попробовал Ableton live, RealGuitar, FL studio, Kontakt. Остановился на RealGuitar из-за простоты и заточенности именно под гитару. Он даже умеет имитировать несовершенства человеческой игры скольжение пальцев по струнам, рандомизированные параметры извлечения нот.



Подключение к виртуальному синтезатору

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



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



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

Оказалось, что из 72 элементов на 12-ти ладах всего 37 уникальных нот. Они расположены по определенной структуре, так что удалось вместо построения большой таблицы вывести простое уравнение, которое по номеру сенсора выдает номер соответствующей ноты.



Тест

Похоже, все готово для первого теста. Пилить прутки и паять все 12 ладов мне было лень, поэтому ограничился 8-ю. Момент истины:


ITS ALIVE! Жизнеспособность концепта подтверждена. Счастью не было предела! Но нельзя расслабляться.

Следующий этап добавление светодиодов, акселерометра, вибромотора, аккумулятора, беспроводной связи, корпуса и возможности работы без драйверов или программ эмуляции midi на всех популярных платформах.

Светодиоды

Для подключения 84 светодиодов я выбрал самый простой пусть daisy chain из 14-ти 8-битных сдвиговых регистров. Их удобно подключить к SPI MOSI выводу STM-ки и слать по DMA массив данных без участия ядра.

Акселерометр

Особых требований к акселерометру у меня не было, поэтому взял самый простой LIS3D. С его помощью гитара будет определять свой наклон относительно горизонта, что позволит модулировать различные звуковые фильтры во время игры движениями рук.

Беспровод

Для беспроводной передачи данных решил поставить ESP32. Оно поддерживает различные протоколы Bluetooth и WI-FI, будет с чем поэкспериментировать (на тот момент я еще не знал, что в моем случае существует только один правильный способ подключения).

Корпус

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

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

Начинаю работу


Было проделано много работы по экспериментам с различными конструкциями тактильных элементов грифа и рассеивателями для светодиодов. Хотелось, чтобы равномерно светилась вся поверхность элемента, при этом сохранялась возможность детектирования прикосновения и нажатия на кнопки.



Я обратился к другу, который профессионально занимается пром дизайном. Мы придумали конструкцию узла сгибания гитары, после чего он спроектировал и напечатал прототип корпуса.



Вроде, все продумано, можно начинать разводку платы.











MIDI устройство

В новой версии, в первую очередь я хотел, чтобы при подключении по USB, гитара определялась как MIDI устройство без всяких лишних программ.

Оказалось, сделать это не так сложно, все спецификации есть на официальном сайте usb.org. Но все алгоритмы, которые выполнялись на стороне питонского приложения, пришлось переписывать на C в контроллер.

Я был удивлен, что оно сразу заработало на всех устройствах. Windows 10, MacOS, Debian 9, Android (через USB переходник). Достаточно просто воткнуть провод и в системе появляется MIDI устройство с названием Sensy и распознается всеми синтезаторами. С айфоном пока протестировать не удалось т.к. нет переходника. Но должно работать так же.



Беспроводной интерфейс

Следующая задача организовать работу без проводов.

Погуглить сразу я поленился, поэтому потратил несколько дней на тестирование различных беспроводных интерфейсов. BLE я отмёл сразу, т.к. в моей голове Low energy прочно ассоциировалось с низкой частотой передачи пакетов. Пробовал WI-FI в режиме клиента, WI-FI в режиме точки доступа, Bluetooth в режиме SPP и т.д. Везде была одна и та же проблема огромная задержка (больше 100мс на глаз) и неравномерность прихода пакетов во времени. Это делало игру невозможной.

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

Но тут я случайно наткнулся на спецификации новых версий протокола BLE и увидел, что минимальный connection interval там 7.5мс, что отлично вписывается в мои требования.

Более того, оказалось, что существует протокол BLE MIDI, который поддерживается всеми новыми операционками и работает без всяких драйверов прямо как по USB MIDI.

Единственной проблемой оказалось то, что такой низкий connection interval и вообще BLE MIDI поддерживаются только относительно новыми платформами. Подробности еще предстоит выяснить, но тесты с доступными мне девайсами прошли успешно.

На некоторых новых айфонах даже имеется предустановленный виртуальный синтезатор Garage Band, способный издавать качественные гитарные звуки (если нет, можно скачать в App Store бесплатно).

Прошивка

Написав весь минимальный необходимый функционал, я уперся ровно в размер флэша STM-ки. Свободными осталось всего 168 байт. Очевидно, кремниевые боги мне благоволили, значит иду в правильном направлении.



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

С USB я уже поразвлекался, поэтому можно весь код, связанный с ним, закомментить и освободить память. Тестировать можно и по беспроводу:


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



Какие минусы у этой конструкции?

  1. На сенсорах нигде не измеряется усилие нажатия. Это влечет за собой три проблемы:
    • Постоянно происходят случайные задевания соседних струн как на дэке, так и на грифе. Это делает игру очень сложной.
    • Все играемые ноты извлекаются с одинаковой громкостью. Большинство подопытных этого не замечают, но хотелось бы более приближенной к настоящей гитаре игры
    • Невозможность использовать техники hammer on, pull off и vibrato
  2. Светодиоды одноцветные. Это ограничивает наглядность при игре по табулатурам. Хочется иметь возможность разными цветами указывать на различные приемы игры.
  3. Форма корпуса не подходит для левшей. С точки зрения софта я уже реализовал инверсию струн по акселерометру. Но механический лепесток, необходимый для удержания гитары рукой во время игры, поворачивается только в сторону, удобную правшам.
  4. Отсутствие упора для ноги. Сейчас, при игре сидя, нижняя струна почти касается ноги, а это неудобно.
  5. Сустав сгибания гитары требует осмысления и доработки. Возможно, он недостаточно надежен и стабилен.

Время переходить к разработке следующей версии.


Переезжаю на контроллер серии STM32F07. На нем уже 128КБ флэша этого хватит на любой функционал. И даже на пасхалки останется.

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

Конечно, будут реализованы и три главных нововведения:

  • светодиоды теперь RGB,
  • на каждом сенсоре грифа будет измерение усилия (тактовые кнопки больше не нужны),
  • струны на дэке станут подвижными.

На данный момент плата дэки выглядит так (футпринт ESP на всякий случай оставил):


Проект называется Sensy и сейчас находится в активной разработке. Уже есть полная уверенность в том, что весь задуманный функционал будет реализован, поэтому было принято решение о дальнейшем развитии.

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

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

Кому интересно следить за новостями проекта оставляйте почту в форме на сайте и подписывайтесь на соцсети.

Очень надеюсь на обратную связь от Хабрасообщества с комментариями и предложениями!

Забавный эпизод из процесса разработки
Сижу отлаживаю NRF52, пытаюсь вывести данные через UART. Ничего не выходит. Проверял код, пайку, даже перепаивал чип, ничего не помогает.

И тут случайно нестандартным способом перезагружаю плату в терминал приходит буква N в ascii. Это соответствует числу 0x4E, которое я не отправлял. Перезагружаю еще раз приходит буква O. Странно. Может быть проблема с кварцевым резонатором и сбился baud rate? Меняю частоту в терминале, перезагружаю плату опять приходит N. С каждой новой перезагрузкой приходит по новой букве, которые в итоге составляют повторяющуюся по кругу фразу NON GENUINE DEVICE FOUND.

Что эта NRF-ка себе позволяет? Прошивку я обнулял. Как она после перезагрузки вообще помнит, что отправлялось в предыдущий раз? Это было похоже на какой-то спиритический сеанс. Может, я и есть тот самый NON GENUINE DEVICE?

Залез в гугл, выяснил, что производители ftdi микросхем, которые стоят в USB-UART донглах, придумали способ бороться с китайскими подделками. Виндовый драйвер проверяет оригинальность микросхемы и на лету подменяет приходящие данные на эту фразу в случае, если она поддельная. Очевидно, мой донгл оказался подделкой и переход на другой решил эту проблему.

Снова спасибо китайцам.

Спасибо за внимание!
Подробнее..

Recovery mode Моя Яндекс.Станция Мини умеет выводить два звука одновременно, а ваша?

07.09.2020 04:21:52 | Автор: admin
Совсем недавно приобрел Яндекс.Станцию Мини. Если, кто не знает, это маленькая умная колонка, управляемая голосом и жестами. Внутри голосовой помощник Алиса: она включает музыку, отвечает на вопросы и выполняет поручения. Приобреталась как умный радиоприемник на кухню, последующего создания умного дома со своими навыками.

После спаривания с операционными системами Winodows 7, 10, Ubuntu 16.04, вдоволь поигравшись с ее возможностями, ознакомился с официальной документацией.

https://yandex.ru/support/station-mini/speaker.html
Использовать Станцию Мини как музыкальную колонку
На Станцию Мини можно транслировать музыку с компьютера, планшета или смартфона через Bluetooth как на обычную беспроводную колонку:
Скажите: Алиса, включи Bluetooth или нажмите кнопку отключения микрофонов и удерживайте ее пять секунд, пока подсветка Станции Мини не замигает.
Включите Bluetooth на компьютере, планшете или смартфоне и запустите поиск устройств Bluetooth.
В списке выберите Станцию Мини и включите музыку.
Пока Станция Мини играет музыку через Bluetooth, Алиса вас не слышит. Чтобы выйти из режима трансляции, разорвите соединение на стороне вашего компьютера, смартфона или планшета.







После прочтения очень сильно расстроился. С одной стороны потрясающие возможности речевого управления, создания навыков, умного дома. С другой стороны, используя возможности зарядки от USB 3.0 порта ноутбука, получаем пульт голосового управления с радиусом дальнобойности хорошей точки Wi-Fi, без особых ухищрений до 100 метров!

Решил проверить кодовую фразу на колонке, спаренной по Bluetooth с компьютером. Алиса, включи радио Маяк. И радио запело И параллельно шел звук с компьютера. В итоге,
официальная документация опровергнута, устройство с августа 2020 получило новые возможности. Для двух потоков рекомендуется командовать одним потоком как обычно, командами Алисы, потоком по Bluetooth можно управлять регуляторами громкости компьютера, так же задействовав беспроводную клавиатуру с клавишами управления, либо, если есть, отдельными кнопками управления громкости ноубука с торца устройства.

У кого еще получится провести такой эксперимент?
Подробнее..

И снова CSR8645, или надоело чинить провода

01.02.2021 18:10:50 | Автор: admin

Предыстория

В далеком-недалеком 2019, в очередной раз повредив провод от своих проводных наушников, чинить/менять его в 100 и 1 раз стало уже лень. И как обычно в таких случаях, прошелся по сайтам магазинов в поисках новых.

Тем бы история и закончилась, но так случилось, что один мой хороший знакомый предложил в тот момент перебраться в мир беспроводного аудио. Да и не просто так перебраться, а в стиле DIY, что показалось все-таки интересным. Ну а время спустя, одним долгим зимним вечером появилась идея оформить итоги этого всего в более-менее подробный пост.

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

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

Но обо всем по порядку. Как уже понятно из названия (для тех кто с данной темой сталкивался), история будет об аудиомодуле Bluetooth модели CSR8645, производства Qualcomm.

Данный чип часто используется в беспроводной передаче звука. Он не относится к "премиум" классу моделей, но и не является бюджетным. Иначе говоря, есть чипы и лучше, есть и хуже (до неприличия хуже). Автором именно CSR8645 расценен как оптимальный для своих опытов.

Требования и ожидания

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

И вот когда окончательно решился на эксперимент с беспроводными (что произошло не сразу, так как некоторое время ушло на изучение вопроса и обдумывание тех или других аспектов и проблем), сформировался более-менее определенный список требований к результату:

  • качество звука не хуже чем у проводных

  • отсутствие ощутимой задержки звука/лагов (может быть важно если захочется игры поиграть)

  • большое время работы без подзарядки

  • приемлемый вес

  • наушники не должны являться "вкладышами", или подобными

  • наушники не должны вызывать дискомфорта или других проблем при работе в них в течении 1-2 суток непрерывно

  • должна быть совместимость с Windows

Требованиям к качеству звука, отвечает модуль с поддержкой AptX (и большим набором применяемых в нем кодеков, где-то даже была отдельная статья о них). AptX LL был бы вероятно еще лучше, так как задержка звука в нем еще ниже, но почитав о том что далеко не все устройства его поддерживают, решено оставить это на будущее.

Для полноценной работы с Windows, понадобился сторонний Bluetooth-Stack (выбор автора после тестирования нескольких из них BlueSoleil, хоть и у него хватает недостатков). В Linux впринципе, дела с программной поддержкой должны обстоять получше насколько знаю, но лично опыты не проводились.

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

Выбор комплектующих

Аудиомодуль (приемник)

CSR8645. В продаже он нашелся в двух вариациях: "чистый" модуль, и уже распаянный на плате с 5-ваттным усилителем.

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

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

Данный модуль работает только в качестве приемника (существуют и модели-передатчики).

CSR8545CSR8545Пример CSR8645 с усилителемПример CSR8645 с усилителем

Копировать все характеристики смысла нет, упомяну разве что физические размеры модуля, так как в первую очередь по ним становится ясно получится его разместить в наушники, или нет.

Так, сам по себе модуль имеет размеры 26.2x13.5 мм. Тогда как размеры вариантов с усилителем от 29x29 мм. Существует несколько вариантов готовых плат с усилителем.

Передатчик

В качестве источника пригоден любой Bluetooth-адаптер, поддерживающий Bluetooth 2.0 и выше.

Автор использовал первый-попавшийся дешевый внешний адаптер к ПК, на основе чипа CSR8510 A10. Выбор внешнего адаптера имеет кое-какие преимущества, к примеру можно его немного переделать (об этом тоже позднее).

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

Наушники

Решил переделывать свои старые проводные. О них говорить особо нечего, недорогие и ничем не примечательные особо, мониторные наушники Ergo VD-350. Ценны для автора разве что тем, что были однажды слегка переделаны для улучшения и упрочнения конструкции, и благодаря этому, как только пожелают то могут спокойно упасть на бетон без последствий. Как еще оказалось потом, их конструкция крайне удобна для монтирования данного модуля под боковую черную крышку.

В принципе, подходят какие-угодно с импедансом до 32 Ом, и дешевые и дорогущие (если только не жалко будет к дорогущим лезть с паяльником). Главное, чтобы была возможность в том или другом месте разместить аккумулятор и сам модуль с проводами.

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

Аккумулятор

Выбор аккумулятора определяется размерами, весом, требуемым напряжением питания модуля (3.3-4.2V) и пожеланиями к продолжительности автономной работы.

Если использовать уже распаянный модуль (как упоминалось выше), то возможно питание и от больших напряжений (до 12V, зависимо от вариации платы), поскольку на таких платах обычно находится микросхема-стабилизатор, выдающая на выходе те же 3.6 или 3.3V.

Для использования отлично подходят литий-полимерные аккумуляторы (Li-Po) с напряжением 3.7V.

Но автора и они не устроили. Не хотелось устанавливать аккумулятор внутри устройства, а для наружной установки форма Li-Po батарей показалась хуже. Есть смысл ставить аккумулятор внутрь устройства, если планируется делать встроенную подзарядку, то есть без использования внешних зарядных устройств.

В итоге понравилась идея "добавить себе в наушники немного Теслы". И выбор пал на аккумуляторы типа 18650, которые используются в электромобилях, в батареях ноутбуков, фонарях да и много где еще. Являются литий-ионными по технологии, а так же обладают большой емкостью (кроме дешевых моделей сомнительных производителей).

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

На деле нет принципиальной разницы какой тип аккумуляторов использовать. И те и другие в данном случае (из-за малой нагрузки от модуля по току) достаточно безопасны чтобы носить их при себе, и способны выдать нужные характеристики, так что выбор отчасти субъективный.

Прочее

Тонкие провода (автор использовал выдранные с какой-то электроники, и парочку более мощных, выдранных со старого компьютерного блока питания).

Микропереключатель типа On/Off (для включения/выключения собственно модуля)

Тактовые кнопки SMD (со старого mp3-плеера удалось достать хорошие экземпляры)

Шлейф для кнопок (отрезан кусочек старого IDE-шлейфа. можно и проводами обойтись)

Отсек для 18650 аккумуляторов (одиночный)

Зарядное устройство для 18650 аккумуляторов (подойдет и простейшая схемка контроля заряда, и что-то навороченное)

Реализация

И вот когда все элементы были заказаны/выдраны откуда можно, пришло время самого интересного.

Схему подключения чистого модуля приводить не стану, так как не использовалась в этой всей истории по вышеописанным причинам. Поэтому сразу перейдем к варианту с "полуфабрикатом".

Само подключение получилось примерно по принципу "ломать не строить".

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

С интересующих нас элементов, здесь присутствуют 2 микросхемы аудиоусилителя (подсвечены красным), обвязка этих микросхем, состоящая из SMD-резисторов (желтая подсветка) и конденсаторов (синяя). А так же микросхема-стабилизатор входного напряжения (белая).

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

Суть переделки состоит в том, чтобы исключить микросхемы усилителей, тем самым подключив выводы LP, LN, RP, RN напрямую к соответствующим выводам CSR8645.

Для этого, нужно выпаять все указанные элементы (кроме белого стабилизатора питания, о нем ниже), и установить вместо них проволочные перемычки, как показано на этой схеме:

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

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

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

Дальнейшие шаги выглядели вот так (схема в которой отсутствует микрофон и используются 3 кнопки управления из 5 возможных):

Итоговая псевдо-схемаИтоговая псевдо-схема

К сожалению фото изнутри приложить нет возможности, пришлось бы все разбирать ради этого.

Выводы LP, LN контакты для левого динамика. RP, RN для правого соответственно.

Активация кнопок происходит замыканием контакта COM на нужный вывод. В данном случае на плате уже установлены резисторы, поэтому никаких дополнительных элементов для работы кнопок не требуется (в отличие от использования чистого модуля).

Для уменьшения количества проводов и упрощения, можно обойтись и вовсе без кнопок, но рекомендую по крайней мере одну подключить (вывод PLY, отвечающий за команду Play/Pause удаленного управления), так как на ней завязаны некоторые сервисные функции, вроде перезапуска сопряжения. Хотя и они не обязательны и все будет работать и так (устройство автоматически сопрягается каждый раз при включении).

Результат

В результате, получились довольно монструозные наушники, которые тем не менее, вполне радуют при домашнем использовании и покрывают все перечисленные (и некоторые не перечисленные) требования к ним.

Обозначения:

  1. Аккумулятор

  2. Выключатель наушников

  3. Независимый выключатель светодиода

  4. Дополнительный белый светодиод

  5. Резисторы светодиода на минусе питания

  6. Внешняя антенна

  7. Экранированный антенный кабель

  8. Провода питания модуля

  9. Сигнальный светодиод модуля

  10. Кнопка управления Previous Track

  11. Кнопка Play/Pause

  12. Кнопка Next Track

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

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

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

Отсек аккумулятора крепится к рамке наушников заклепкой. Поскольку внутри этой рамки находится пластина из закаленной стали, в целом все достаточно надежно.

Еще некоторые характеристики:

  • Вес наушников (с аккумулятором): 220 грамм

  • Вес аккумулятора: 50-80 грамм

  • Продолжительность работы 24/24, без переходов в режим ожидания: ~4-5 суток

  • Продолжительность работы 16/24: до 10 суток

Данные о продолжительности работы актуальны для используемых к настоящему моменту аккумуляторов (2000 и 2200 mAh), есть возможность использовать и более емкие. Используются 2 аккумулятора по очереди, чтобы в случае сигнала о разряде, быстро сменить на уже заряженный.

Возможные альтернативы

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

Поэтому рассматривались еще некоторые варианты:

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

  • На основе обычного FM передатчика и приемника (отброшен из-за отсутствия какой-либо защиты от перехвата эфира, или высокой трудоемкости в случае реализации чего-то своего шифрованного).

  • На основе неких чудных китайских модулей, работающих тоже в 2.4ГГц-диапазоне (и по заявлению производителя, не конфликтующих с Wifi и Bluetooth). Этот вариант не отброшен и сейчас, поскольку представляет интерес, в основном из-за большого радиуса работы данных модулей и отсутствия необходимости в программной поддержке (они по сути просто подключаются к аудиовыходу звуковой карты и работают как есть)

Дополнительно

Радиус работы

В абсолютном большинстве случаев стандартной дальности работы Bluetooth 2.0 (до 10 метров) более чем достаточно. В пределах 1-2 комнат может работать без помех.

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

В итоге, проводились эксперименты с добавлением внешних антенн.

Поначалу самодельных, но прирост дальности от них был порядка 10-15% всего.

Позднее нашел парочку антенн от старого радиотелефона, но и с ними ничего лучше тех же +10-15% не получилось. Разве что внешний вид получше, чем у самодельных проводков.

Как-нибудь в другой раз хочется попробовать специальные антенны под 2.4ГГц-диапазон (наподобие тех что применяются в мощных передатчиках Wifi-роутеров), но есть подозрение что и они не особо помогут.

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

Очень интересно, если у кого-то есть результаты в этом направлении, или схемы/идеи применимые к BT-передатчикам.

Изменение прошивки/конфигурации модуля

Есть возможность с использованием фирменной китайской утилиты и программатора более тонко настроить плату под себя. Включить-выключить те или другие функции и поддерживаемые кодеки, задать "нескучное" имя устройства и сервисные звуки, окирпичить модуль, и многое другое.

Интереса ради, прежде находил как все это сделать, но так и не появилось особой необходимости (да и не было подходящего программатора, чтобы попробовать без необходимости), потому ссылки не сохранились.

Добавление микрофона

На плате присутствуют выводы для присоединения микрофона.

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

Объединенный аудиовыход

Если требуется объединить минуса аудиоканалов (например, чтобы сделать вывод через распространенный jack с общим минусом), нужна дополнительная развязка аудиовыхода, либо подключение к внешнему усилителю, имеющему на входе раздельные каналы, а на выходе объединенные.

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

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

Ссылки

CSR8645 с усилителем на AliExpress (детали, фото, схемы)

На сайте производителя

Документация на примере похожей платы

Подробная статья касательно A2DP, кодеков и прочего

Одна из подобных историй

tl;dr

На этом вроде бы все. Надеюсь статья окажется интересной, по крайней мере непрофессиональным любителям время от времени поиграть с электроникой (к каким сам автор относится).

Подробнее..

Забытые корни популярных иконок

05.02.2021 12:22:10 | Автор: admin


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

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

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

Медиакнопки без автора


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

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

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


Контрольная панель немецкого магнитофона AEG FT4, который производился с 1939 по 1941 год. Никаких иконок, только краткие подписи. Фотография Музея магнитной аудиозаписи в Остине, штат Техас

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


Панель управления магнитофона Ampex AG-440, модель 1967 года. Профессиональный сегмент и назначение только для внутреннего рынка США избавляли от необходимости переводить надписи. Фотография сайта Audiofanzine

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


Швейцарский магнитофон Revox F36 1962 года. Возможно, из-за двуязычности страны происхождения производитель задумался нанести на корпус графические символы, а не надписи текстом. Фотография сайта Reverb

К семидесятым глобализация и завоевание мирового рынка наконец вынудили подумать про единый стандарт. В 1973 году Международная электротехническая комиссия выпустила документ IEC 417. Стандарт фиксировал графические символы, многие из которых широко использовались и до этого.

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

Попытки есть. Несколько сайтов ошибочно утверждают, что иконки придумал несуществующий шведский дизайнер Филип Ульссон (Philip Olsson) во время стажировки в Японии по окончании его обучения в Королевском технологическом институте. На деле это обрывок информации из Википедии, который в 2012 году добавил викивандал под именем Phlopydisk. Судя по его аккаунтам в Сети, Филип зачем-то вставил своё имя в статью.

Треугольник воспроизведения указывает направо. Туда же движется лента при проигрывании звука. Если речь идёт про катушечный магнитофон с автореверсом, то кнопок воспроизведения иногда две. На магнитной ленте в этом случае 2 (моно) или 4 (стерео) дорожки. В дизайне укоренился лишь один вариант кнопки проигрывания: указывающая слева направо.


Продвинутый бобинник Pioneer RT-909 продавался уже в закат эпохи ленты, с 1978 года. Фотография Walkman Archive

Кнопка паузы похожа на символ цезуры ||, который ставят в тексте для указания паузы. Также она напоминает символ последний в японском слове (кугири), означающем место для отдыха или знак пунктуации.

На форумах Straight Dope обнаружился анонимный пользователь, который утверждает, что в шестидесятых работал в Ampex, где сократил квадратик кнопки остановки удалением верхней и нижней грани иначе получился бы знак равенства. Но никаких иных подтверждений этому нет.

Цвет кружка кнопки записи был продиктован традицией: красной индикацией обозначали прямой эфир.


Кнопки управления Grundig TK46, магнитофон производили с 1962 по 1964 год. Уже здесь используется красный кружок, хотя в остальном корпус обильно испещрён английскими глаголами. Модель для рынка Германии производилась с надписями на немецком. Фотография Relics & Rarities

Символы из стандарта IEC 417 (также известен как IEC 60417:1973) постепенно распространились по всему миру. На это ушло не одно десятилетие, и часто рядом соседствовали значки и буквы. Но простота значков победила.

А нарисовать интуитивные пиктограммы не так-то просто. В том же документе IEC 417 у привычных треугольников, квадрата и кружка есть аналог, который изображает действия с лентой и сами катушки. К этому аналогу прибегала, к примеру, советская техника: вместо значка 5107B на кнопке воспроизведения стоял похожий на очки 5096, вместо двух паралелльных вертикальных полосок 5111B магическая руна 5111A и так далее.


Советский кассетный магнитофон Квазар-303 выпускался с 1985 года, когда на Западе уже устоялись простые треугольники, квадраты и круги для пиктограмм. Фотография Виртуального музея отечественной радиотехники XX века

Скандинавский след


Символом в macOS обозначают клавишу Command. Как и для многих других особенностей интерфейса Apple, история этого значка восходит к дизайнеру Сьюзен Кэр.

В августе 1983 года команда разработки программного обеспечения Apple Macintosh заметила, что нужна специальная клавиша для вызова команд из строки меню. Разработчики решили поставить иконку компании небольшое яблочко. Так уже поступили с клавиатурой Lisa.


Клавиатура компьютера Apple Lisa. Изображение из архива Bitsavers

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

Внезапно взбунтовался руководивший работами Стив Джобс. Возможно, он увидел программу MacDraw, в которой пунктов меню было по-настоящему много. Он ворвался в здание офиса и потребовал прекратить упоминать логотип Apple всуе.

На все увещевания, что нужно показывать хоть какой-то символ, Джобс заявил: смените иконку. Поскольку это касалось и дедлайнов сборки физической клавиатуры, и сроков печати руководства пользователя, на поиски замены оставалось всего несколько дней.

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

Кэр дошла до шведского символа достопримечательности, которым помечают представляющие культурный интерес места. Этот символ появился в пятидесятых годах в Финляндии и быстро распространился по остальным странам Скандинавии.

Квадрат с петлями в разрешении 1616 получил одобрение всего отдела. Спустя 37 лет на каждом Маке до сих пор есть языческий символ, который в позапрошлом тысячелетии рисовали для отпугивания злых духов.


Клавиатура Apple M0110 от оригинального Macintosh. Фотография Deksthority

Сам Джобс покинул Apple в 1985 году и основал собственную компанию NeXT Computer. На клавиатурах NeXT командная клавиша помечена зелёной надписью Command.

Уже в 1986 году в Apple IIGS на клавишу рядом к символу достопримечательности добавили логотип компании. Так сделали для сохранения совместимости с предыдущими поколениями Apple II. В 2007 году логотип компании вновь исчез. На тот момент NeXT уже как десять лет вошла в состав Apple, а Джобс вновь влиял на любые процессы.

Другой символ протонордической культуры, который жёстко обосновался на наших устройствах это Bluetooth. Когда-то на сайте протокола даже была специальная страница, объяснявшая выбор имени и символики.



Составная руна в логотипе стандарта передачи данных по беспроводной связи комбинирует символ младшего футарка хагалаз () и беркану (). Вместе они образуют инициалы HB [Harald Bltand] короля Дании и Норвегии Харальда I Гормссона Синезубого, в честь которого и назвали стандарт. Как и связывающий устройства враждующих производителей протокол Bluetooth, этот правитель X века объединил соперничающие княжества Скандинавии.

А вот прозвище Синезубый Харальдс получил, вероятно, не за синие, а потемневшие зубы. Но логотип Bluetooth толкует цвет буквально.

Новейшие потеряшки


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

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

Автор трёх полосок Норм Кокс, один из разработчиков первого в мире графического интерфейса Xerox Star. Как рассказывает создатель элемента, в своей работе он руководствовался ограничениями дисплеев. Требовалась хорошо различимая и запоминающаяся пиктограмма, которая будет имитировать вид открываемого списка элементов. А в квадрате 1616 пикселей с градациями серого особо не разгуляться.


Скриншот из видеоролика. Видео смонтировано в 1990 году, но дата указывает на внутреннюю демонстрацию Xerox 1981 года. Слово гамбургер не звучит: элемент называют кнопкой меню

Как признаётся Кокс, один из самых незначительных и слабо обдуманных элементов системы Star получил наибольшее наследие. На эту кнопку даже хотели поставить указывающую вниз стрелку, значок + или подошёл бы любой символ, означавшие разное, дополнительное.

Изначальное название иконки было другим: в шутку клиентам сотрудники Xerox рассказывали, что это решётка вентилятора, которая охлаждает элементы интерфейса.

Отследить дальнейшее распространение иконки несложно. Гамбургер появился на самих смартфонах 17 июня 2009 года в приложении голосовых заметок в iOS.



Возможный кандидат среди сторонних приложений Tweetie, первый клиент Twitter руки известного разработчика iOS Лорена Брихтера. Tweetie вышел в 2008 году, и на тот момент Брихтер работал в Apple

Поиски гамбургера шли по пути упрощения. Facebook в 2008 году добавила иконку с сеткой квадратиков 23, а через год её поменяла на 33. В 2010 году в интерфейсе приложения девять квадратиков сменили на три полоски.


Интерфейс приложения Facebook для iOS, 2008 год

Растущий темп современности стирает историю не только у изобретения 40-летней давности. В веб-интерфейсах нет стандартизированной иконки расшарки контента, хотя кандидаты находятся. Заметное распространение получили три точки, соединённые линиями, будто иллюстрирующие граф.



Её создатель веб-разработчик Алекс Кинг, создатель плагина ShareThis для WordPress. В ноябре 2006 года Кинг задался вопросом, нужна ли дополнению специальная иконка. 4 декабря 2006 года веб-разработчик публикует первый вариант, который затем лишь незначительно модифицирует утолщает рёбра графа, чтобы их было лучше видно в маленьком размере.



Кинг удачно предугадал необходимость в новом символе. К концу 2006 года Алекс открыл веб-сайт Share Icon Project, в котором призывал пользоваться этим логотипом для указания действия расшарки, и выложил архив с изображениями. Алекс объявил, что лицензирует иконку сразу под 4 свободными лицензиями: GPL, LGPL, BSD и Creative Commons Attribution 2.5.

Пиктограмма получила распространение: меньше чем через год ей начал пользоваться Google в своём интерфейсе. В сентябре 2007 года Алекс Кинг объявил, что продаёт логотипы ShareThis одноимённой компании виджетов соцсетей.

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



Без истории развития стандарта логотип Ethernet понять тяжело: это какие-то квадратики, которые почему-то общаются по одному кабелю. Реальная витая пара идёт от устройства к устройству.


Порт Ethernet и его логотип

Но всё встанет на свои места, если вспомнить, что в 10BASE5, первом стандарте Ethernet, к общему коаксиальному кабелю вампирчиками подключались до 100 узлов.


В логотипе наверняка есть что-то от набросков автора стандарта Ethernet Роберта Меткалфа

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

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

Хотя дизайнеры возмущаются оторванностью символа от эпохи, а дети видят на пиктограмме торговый автомат, визуальная метафора для сохранения данных останется прежней. Когда стандарт принят глобально, шансы ухода от него минимальны. Если бы это было неверно, мы бы давно ушли на раскладку Дворака и называли спам нежелательной перепиской.



На правах рекламы


Эпично! Мощные серверы на базе новейших процессоров AMD EPYC для размещения проектов любой сложности, от корпоративных сетей и игровых проектов до лендингов и VPN.

Подробнее..

Перевод Bluetooth Low Energy подробный гайд для начинающих

10.12.2020 12:12:32 | Автор: admin

Создание кастомного сервиса и тем более клиента Bluetooth Low Energy прогулка по граблям с завязанными глазами. По крайне мере так было для меня 4 года назад, когда я только начинал работать с BLE-устройствами. Сейчас почти каждый мой проект предусматривает использование этого протокола, поэтому в свое время пришлось в нем долго и мучительно разбираться.

Разложить все по полкам помогла книга Мохаммада Афане "Intro to Bluetooth Low Energy" и серия постов на Novel Bits. Лично для меня эта книга стала настоящим открытием. Изначально я делал ее перевод на русский для своих коллег, не имеющим опыт работы с BLE. С согласия автора (огромное ему спасибо) решил опубликовать свою работу здесь. Надеюсь, перевод окажется полезным.

Это первая часть перевода (всего их будет 5), которая рассказывает, что такое BLE, ее возможности и отличия от Bluetooth Classic и описывает архитектуру протокола.

Об авторе

Мохаммад Афане занимается разработкой встроенного программного обеспечения и прошивок с 2006 года. Он работал и консультировал множество крупных компаний, включая такие как Allegion (Schlage locks), Motorola, Technicolor, Audiovox, и Denon & Marantz Group. На протяжении всей своей карьеры он работал над множеством проектов Интернета Вещей, включая: беспроводные электронные дверные замки, спутниковые приемники, беспроводные дверные замки и т.д.

В июле 2015 года он принял решение прекратить работу на полную ставку для того, чтобы основать собственную компанию Novel Bits, LLC, где он делится своими знаниями и опытом на своем web-сайте, локальных тренингах и в электронных книгах, посвященных разработке приложений с поддержкой Bluetooth Low Energy.

Вы можете связаться с Мохаммадом по его электронной почте: mohammad@novelbits.io или через профиль на LinkedIn.

Базовые понятия Bluetooth Low Energy

1. Что такое Bluetooth Low Energy?

Bluetooth был задуман как технология связи ближнего диапазона, призванная заменить провода в таких устройствах, как компьютерные мыши, клавиатуры или персональные компьютеры. Если у вас есть современный автомобиль или смартфон, то скорее всего вы использовали Bluetooth хотя бы раз в своей жизни. Он повсюду: в громкоговорителях и колонках, беспроводных наушниках, автомобилях, носимых устройствах и даже в шлёпанцах!

Первая официальная версия стандарта была выпущена компанией Ericsson в 1994 году. Разработчики назвали свое изобретение в честь короля Дании Харальда Гормссона по прозвищу Синезубый, объединившего в 10 веке враждовавшие датские племена в единое королевство.

В настоящее время существует два типа устройств с поддержкой Bluetooth:

  • Bluetooth Classic (BR/EDR), используется в беспроводных громкоговорителях, автомобильных информационно-развлекательных системах и наушниках;

  • Bluetooth Low Energy (BLE), т.е. Bluetooth с низким энергопотреблением, который появился в версии стандарта Bluetooth 4.0. Он чаще всего применяется в приложениях, чувствительных к энергопотреблению (например в устройствах с батарейным питанием) или в устройствах, передающих небольшие объемы данных с большими перерывами между передачами (например, разнообразные сенсоры параметров окружающей среды или управляющие устройства, такие как беспроводные выключатели).

Эти два типа устройств несовместимы друг с другом, даже если они выпущены под одним брендом или спецификацией. Устройства с поддержкой Bluetooth Classic не могут напрямую связываться с устройствами, использующими BLE. Это причина, по которой некоторые устройства, такие как смартфоны, выполняются с поддержкой обоих типов соединения (так называемые Dual mode Bluetooth devices), что позволяет им обмениваться информацией с обоими типами устройств.

Рис.1: Типы Bluetooth-устройствРис.1: Типы Bluetooth-устройств

Несколько важных замечаний о BLE:

  • Официальная спецификация Bluetooth сочетает оба типа Bluetooth (Classic и BLE), что иногда затрудняет поиск документации, специфичной для BLE;

  • BLE был введен в версии 4.0 спецификации стандарта Bluetooth, выпущенной в 2010 году;

  • BLE иногда называют Bluetooth Smart, BTLE или Bluetooth 4.0, что является ошибкой, так как эта версия в действительности включает оба типа Bluetooth;

  • Bluetooth Classic и BLE работают в одном и том же частотном диапазоне 2.4 ГГц, ISM-диапазон.

Поскольку во многих устройствах Интернета Вещей (IoT) используются небольшие устройства и датчики, BLE стал наиболее часто используемым протоколом связи (в сравнении с Bluetooth Classic) в приложениях Интернета Вещей. В декабре 2016 года группа компаний Bluetooth Special Interest Group (SIG), регулирующая развитие стандарта, выпустила Bluetooth версии 5.0 (для простоты маркетинга была убрана точка из названия, так что официально он называется Bluetooth 5). Большинство улучшений и новых функций, представленных в этой версии, были ориентированы на BLE, а не на Bluetooth Classic.

Вы также могли слышать о другом термине, связанном с Bluetooth Bluetooth Mesh. Bluetooth Mesh был выпущен в июле 2017 года и основан на BLE. Для работы ему требуется полный стек BLE (ПО, которое действует как интерфейс для другого программного или аппаратного обеспечения), но он не является частью основной спецификации Bluetooth. Мы рассмотрим более подробно эту технологию в отдельной главе.

Подводя итог, посмотрим на диаграмму, показывающую прогресс BLE за прошедшие годы с начала его появления:

Рис.2: История BLEРис.2: История BLE

Технические факты о BLE

Некоторые из наиболее важных технических фактов о BLE включают в себя:

  • Используемый частотный диапазон 2.400 - 2.4835 ГГц.

  • Весь частотный диапазон поделен на 40 каналов по 2 МГц каждый.

  • Максимальная скорость передачи данных по радиоканалу (начиная с Bluetooth версии 5) 2Мбит/с.

  • Дальность передачи сильно зависит от физического окружения, а также используемого режима передачи. Например, в режиме большой дальности передачи дальность связи будет выше, а скорость передачи ниже, чем в высокоскоростном режиме. Типичная дальность передачи: 10-30 метров.

  • Потребление электроэнергии также может изменяться в широких пределах. Оно зависит от реализации устройства, различных параметров протокола и используемого чипсета. Типичное потребление BLE-трансивера во время передачи данных как правило не превышает 15 мА.

  • Обеспечение безопасности не обязательно при обмене данными через BLE и зависит от устройства и реализации приложения разработчиком. Другими словами, существует несколько возможных для реализации уровней обеспечения безопасности.

  • Для всех операций, связанных с шифрованием, BLE использует алгоритм AES-CCM с длиной ключа 128 бит.

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

  • Версии Bluetooth (в части BLE) являются обратно совместимыми. Тем не менее возможности связи будут ограничены функциями более старой версии. Например, устройство с поддержкой Bluetooth 5 LE может установить связь с устройством с поддержкой Bluetooth 4.1 LE, но возможности, появившиеся в версии 4.2 и более новых, будут недоступны. В то же время они смогут использовать возможности подключения, рассылки и приема широковещательных пакетов, обнаруживать сервисы и характеристики, а также читать и записывать их независимо от поддерживаемой ими версии стандарта, так как эти возможности доступны во всех версиях Bluetooth.

Сравнение Bluetooth Classic и BLE

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

Некоторые из упомянутых различий представлены в этой таблице:

Таблица 1. Сравнение Bluetooth Classic и BLE

Bluetooth Classic

BLE

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

Используется в сенсорах, управлении устройствами и приложениях, не требующих передачи больших объемов данных

Не оптимизирован для низкого энергопотребления, но поддерживает большую скорость передачи (максимум 3 МБит/с, в то время как BLE 5 имеет максимум 2 МБит/с)

Предназначен для применения в малопотребляющих устройствах с большими интервалами между передачей данных

Использует 79 радиоканалов

Использует 40 радиоканалов

Обнаружение происходит на 32 каналах

Обнаружение происходит на 3 каналах, что приводит к более быстрому обнаружению и установке соединения по сравнению с Bluetooth Classic

С момента официального выпуска в 2010 году BLE прошел череду ревизий и изменений. Наиболее важное изменение произошло в декабре 2016 года с внедрением Bluetooth 5, который привнес множество важных улучшений в спецификацию стандарта, большинство из которых касалось BLE. Эти улучшения позволили удвоить скорость передачи, в 4 раза увеличить дальность передачи и в 8 раз увеличить размер широковещательного пакета.

Возможности и ограничения BLE

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

4.1. Ограничения BLE

Пропускная способность

Пропускная способность BLE ограничена физической пропускной способностью радиоканала, т.е. скоростью, с которой данные передаются по радиоканалу. Пропускная способность зависит от используемой версии Bluetooth. Для Bluetooth 4.2 и более ранних, доступна только пропускная способность в 1 Мбит/с. В Bluetooth 5 и более поздних версиях пропускная способность зависит от выбранного режима PHY (Physical Layer, рассматривается в разделе физического уровня). Она может составлять 1 Мбит/с как в более ранних версиях или 2 Мбит/с при использовании высокоскоростной передачи. При использовании функции дальней связи пропускная способность ограничена значениями 500 или 125 Мбит/с. Мы обсудим это более подробно в главе, посвященной Bluetooth 5.

Скорость передачи с точки зрения конечного пользователя всегда будет ниже скорости передачи по радиоканалу в силу следующих факторов:

  • Промежутки между пакетами данных: спецификация Bluetooth определяет зазор в 150 микросекунд между передаваемыми пакетами как требование для соблюдения спецификации. В этот промежуток времени невозможна передача данных между устройствами.

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

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

  • Переотправка пакетов данных: в случае потери пакета или перекрестных помех от находящихся поблизости устройств, потерянные или поврежденные данные отправляются заново.

Дальность передачи

BLE был разработан для применения на коротких расстояниях, и, следовательно, его диапазон действия ограничен. Вот некоторые факторы, ограничивающие дальность передачи при помощи BLE:

  • На передачу в ISM-диапазоне 2.4 ГГц сильно влияют окружающие нас препятствия, такие как металлические предметы, бетонные стены, вода и человеческие тела.

  • Диаграмма направленности и коэффициент усиления антенны.

  • Корпус устройства, в котором находится антенна, также ухудшает характеристики антенны.

  • Ориентация устройства в пространстве, от которого зависит ориентация антенны, например в смартфонах.

Потребность в шлюзе для интернет-соединения

Для передачи данных с устройства, поддерживающего только BLE-соединение, необходимо другое устройство с поддержкой как BLE, так и IP-соединения. Именно оно будет получать данные и отправлять их в интернет.

4.2 Преимущества BLE

Даже с учетом представленных выше ограничений BLE имеет некоторые существенные преимущества перед другими аналогичными технологиями передачи данных для IoT.

Вот некоторые из них:

  • Меньшее энергопотребление;

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

  • Бесплатный доступ к официальным спецификациям;

Чтобы получить доступ к спецификациям большинства других протоколов вы должны стать членом официальной группы или консорциума по этому стандарту. Стать членом можно за внушительную сумму (от 7500 до 35000 долларов в год). В случае с BLE, спецификации для основных версий (4.0, 4.1, 4.2, 5) доступны для загрузки с сайта Bluetooth абсолютно бесплатно.

  • Низкая цена модулей и чипсетов по сравнению с другими технологиями;

  • Наконец, не менее важный фактор наличие в большинстве смартфонов на рынке. Возможно, это наибольшее преимущество BLE перед такими технологиями как ZigBee, Z-Wave и Thread.

4.3 Наиболее подходящие области применения BLE

Исходя из ограничений и преимуществ, указанных выше, существуют варианты использования, где BLE раскрывается наиболее полно:

  • Малый объем передаваемых данных;

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

  • Настройка устройств;

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

Например, некоторые устройства с поддержкой WiFi добавляют BLE как вспомогательный протокол вместо использования таких технологий как WiFi Direct. Это технология, которая позволяет двум устройствам с поддержкой WiFi соединяться напрямую, минуя роутер. Вы можете узнать подробнее о ней на Википедии или здесь.

  • Использование смартфона в качестве интерфейса;

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

  • Персональные и носимые устройства;

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

  • Устройства без возможности установления соединения.

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

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

  • Потоковая передача видео;

  • Трансляция высококачественного звука (прим.: стала возможна в BLE 5.2);

  • Передача больших объемов данных в течении длительного времени в тех случаях, когда важно сокращение энергопотребления.

Архитектура BLE

Рисунок ниже иллюстрирует различные уровни, присущие архитектуре BLE. Три главных блока в этой архитектуре приложение, хост и контроллер.

Рис.3: Архитектура BLEРис.3: Архитектура BLE

В этой книге мы сфокусируемся на верхних уровнях архитектуры, кратко ознакомившись с нижними уровнями в этой главе. Подробное описание верхних уровней GAP (Generic Access Profile), GATT (Generic Attribute Profile) и Security Manager вынесем в отдельные главы.

Прикладной уровень

Прикладной уровень зависит от варианта использования девайса/приложения и относится к реализации на основе общего профиля доступа (GAP) и общего профиля атрибутов (GATT) он отвечает за то, как ваше приложение обрабатывает данные, полученные от других устройств и отправленные на них, а также управляющую логику.

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

Хост-уровень

Хост включает следующие уровни:

  • Общий профиль доступа (GAP, Generic Access Profile);

  • Общий профиль атрибутов (GATT, Generic Attribute Profile);

  • Протокол атрибутов (ATT, Attribute Protocol);

  • Менеджер безопасности (SM, Security Manager);

  • Протокол управления и адаптации логических связей (L2CAP, Logical Link Control and Adaptation Protocol);

  • Интерфейс хост-контроллера (HCI, Host Controller Interface), зона ответственности хоста.

Контроллер

Контроллер включает следующие уровни:

  • Физический уровень (PHY, Physical Layer);

  • Слой связи (Link Layer);

  • Режим прямого тестирования (DTM, Direct Test Mode);

  • Интерфейс хост-контроллера (HCI, Host Controller Interface), зона ответственности контроллера.

Уровни архитектуры BLE

Физический уровень (PHY)

PHY относится к части оборудования, ответственного за прием, передачу, модуляцию и демодуляцию сигнала. BLE работает в ISM-диапазоне (2.4 ГГЦ), который разделен на 40 каналов по 2 Мгц, как показано на рисунке ниже:

Рис.4: Частотный спектр и радиоканалы в BLEРис.4: Частотный спектр и радиоканалы в BLE

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

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

Вот некоторые другие важные технические детали, касающиеся физического уровня передачи BLE:

  • Он использует скачкообразную перестройку несущей частоты (FHSS, Frequency Hopping Spread Spectrum), что позволяет двум взаимодействующим устройствам переключаться на случайные предварительно согласованные частоты для обмена данными. Это значительно повышает надежность и позволяет устройствам избегать перегруженных каналов.

  • Мощность передачи может быть:

Не более: 100 мВт (+20 дБм) для версии 5 и более новых, 10 мВт (+10 дБм) для версии 4.2 и более старых;

Не менее: 0.01 мВт (-20 дБм).

  • В старых версиях Bluetooth (4.0, 4.1 и 4.2) была доступна только одна скорость передачи 1 Мбит/с. Физический уровень радио (PHY) в этом случае называется 1M PHY и является обязательным во всех версиях, включая Bluetooth 5. В Bluetooth 5 были также введены два новых дополнительных PHY:

    • 2 Мбит/с PHY, используемый для удвоения скорости передачи по сравнению с более ранними версиями Bluetooth.

    • Зашифрованный PHY, используемый для связи на дальних расстояниях.

Мы рассмотрим эти два новых PHY и концепцию кодирования в главе, посвященной Bluetooth 5.

Канальный уровень

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

Существует три основных состояния, в которых может находиться устройство с BLE:

  • Широковещательное состояние (Advertising);

  • Состояние сканирования (Scanning);

  • Подключенное состояние.

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

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

Рис.5: Состояния канального уровняРис.5: Состояния канального уровня
  • Standby: состояние по умолчанию, когда радио не передает и не принимает никаких данных.

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

  • Scanning: состояние, в котором устройство ищет устройства, посылающие широковещательные пакеты.

  • Initiating: состояние, в котором начинается процесс установки соединения с устройством, находящимся в состоянии advertising.

  • Connected: Состояние, в котором одно устройство установило соединение с другим и регулярно обменивается с ним информацией. В подключенном состоянии устройство, которое находилось в состоянии scanning и инициировало соединение, называется ведущим. Устройство, которое рассылало широковещательные пакеты, называется ведомым.

Мы рассмотрим эти состояния более подробно в последующих главах.

Bluetooth адрес:

Bluetooth-устройства идентифицируются посредством 48-битного адреса, похожего на MAC-адрес. Существуют два основных типа адресов: публичный и случайный.

Публичный адрес:

Это фиксированный адрес, запрограммированный на фабрике. Он не может быть изменен и должен быть зарегистрирован в IEEE (также, как и MAC-адреса устройств с поддержкой WiFi или Ethernet).

Случайный адрес:

Так как у производителей есть возможность выбирать, какой тип адреса использовать (публичный или случайный), случайные адреса встречаются более часто, так как они не требуют регистрации в Институте инженеров электротехники и электроники. Случайный адрес программируется на устройстве или генерируется в ходе выполнения программы. Он может относиться к одному из следующих подтипов:

  • Статический адрес

    • Используется в качестве замены публичного адреса;

    • Может быть заново сгенерирован при загрузке кода или оставаться постоянным в течение всего срока службы;

    • Не может изменяться при включении или выключении.

  • Частный адрес включает в себя следующие подтипы:

    Неразрешимый частный адрес:

    • Случайный, генерируется на определенный промежуток времени;

    • Широко не используется.

    Разрешимый частный адрес:

    • Используется для обеспечения безопасности;

    • Генерируется с использованием ключа (IRK, Identity Resolving Key) и случайного числа;

    • Периодически меняется (даже во время соединения);

    • Используется для защиты от отслеживания злоумышленниками;

    • Доверенные устройства (связанные, описанные в главе, посвященной безопасности) могут расшифровать адрес, используя предварительно сохраненный ключ.

Режим прямого тестирования

Режим прямого тестирования (DTM, Direct Test Mode) используется исключительно для проведения испытаний радиочасти во время производства или сертификационных испытаний. Он не относится напрямую к теме нашей книги, поэтому мы оставим его без подробного рассмотрения.

Уровень интерфейса хост-контроллера (HCI)

Интерфейс хост-контроллера это стандартный протокол, определенный спецификацией Bluetooth, который позволяет уровню хоста коммуницировать с уровнем контроллера. Эти уровни могут быть реализованы на двух раздельных микросхемах или существовать на одной. В этом смысле он также обеспечивает взаимодействие между микросхемами, поэтому разработчик устройства может выбрать два сертифицированных Bluetooth-устройства, контроллер и хост, и быть на 100% уверенным в том, что они совместимы друг с другом в плане связи между уровнями хоста и контроллера.

В случае, когда хост и контроллер находятся на разных микросхемах, связь между ними может быть реализована посредством трех официально поддерживаемых физических интерфейсов: UART, USB или SDIO (Secure Digital Input Output). В случае, когда хост и контроллер находятся на одной и той же микросхеме, интерфейс хост-контроллера будет логическим интерфейсом.

Задача интерфейса хост-контроллера состоит в передаче команд от хоста контроллеру и передаче информации и событий от контроллера к хосту. На рисунке ниже приведен пример обмена командами и событиями между уровнями хоста и контроллера.

Рис.6: Пример пакетов интерфейса хост-контроллераРис.6: Пример пакетов интерфейса хост-контроллера

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

Уровень протокола управления и адаптации логического канала (L2CAP)

Протокол L2CAP предоставляет услуги по работе с данными, как ориентированные на соединения, так и без ориентации на них, протоколам более высокого уровня с возможностями мультиплексирования и обеспечения операций по сегментации и обратной сборке. Он заимствован из стандарта Bluetooth Classic и в случае BLE выполняет следующие задачи:

  • Принимает несколько протоколов с верхних уровней и помещает их в стандартные пакеты BLE, которые передаются на нижние уровни под ним.

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

В случае BLE уровень L2CAP управляет двумя основными протоколами: протоколом атрибутов (ATT, рассмотрен в главе, посвященной GATT) и протоколу управления безопасностью (SMP, рассмотрен в главе, посвященной безопасности).

Слои верхнего уровня

Протокол атрибутов (АТТ), общий профиль атрибутов (GATT), менеджер безопасности (SM) и общий профиль доступа (GAP) будут подробно рассмотрены в следующих главах.


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

Подробнее..

Перевод Bluetooth Low Energy подробный гайд для начинающих. Часть 2

17.12.2020 16:16:33 | Автор: admin

Это вторая часть перевода книги Мохаммада Афане Intro to Bluetooth Low Energy. В представленных главах мы поговорим о типах устройств и об адвертайзинге, методе, с помощью которого периферийные устройства сообщают о своем присутствии.Первая часть здесь.

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

2. Периферийные и центральные устройства BLE

Существуют несколько важных определений, с которыми вы будете постоянно сталкиваться при изучении BLE. Два наиболее важных касаются ролей устройства: BLE central и BLE peripheral.

Рассмотрим их более детально.

2.1 Периферийные устройства

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

Другой связанный термин BLE-передатчик, устройство, которое также рассылает широковещательные пакеты, но имеет одно отличие от периферийного устройства:оно не разрешает другим устройствам устанавливать с ним соединение. С другой стороны, устройство-наблюдатель только обнаруживает устройства, производящие процедуру адвертайзинга, но не имеет возможности инициировать соединение с ними.

Типичный пример устройства, реализующего роль передатчика маячок (beacon). Маячки это устройства, которые передают информацию без возможности установить с ними соединение. Они очень популярны в двух сферах: розничная торговля и определение местоположения внутри помещений.

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

Отличить маячок от периферийного устройства можно по типу широковещательных пакетов, которые он передает. Существуют различные типы пакетов: некоторые указывают на возможность установить соединение, а другие просто указывают на наличие маячка в этом месте. Когда центральное BLE-устройство обнаруживает широковещательные пакеты другого устройства с BLE (будь оно маячком или периферийным), оно знает, может ли оно начинать процедуру установки соединения или нет в зависимости от типа принятых широковещательных пакетов.

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

2.2 Центральные устройства

Центральное устройство устройство, которое обнаруживает периферийные устройства и считывает передаваемую ими информацию. Оно также может устанавливать соединение с одним или несколькими устройствами одновременно.

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

2.3 Сравнение типов устройств

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

Передатчик

Периферийное устройство

Наблюдатель

Центральное устройство

Не требует наличия приемника

Требует наличия как приемника, так и передатчика

Не требует наличия передатчика

Требует наличия как приемника, так и передатчика

Не поддерживает двунаправленный обмен данными

Поддерживает двунаправленный обмен данными

Не поддерживает двунаправленный обмен данными

Поддерживает двунаправленный обмен данными

Упрощенная схема, уменьшенный размер программного стека BLE

Требует полного программного стека BLE

Упрощенная схема, уменьшенный размер программного стека BLE

Требует полного программного стека BLE

Табл. 1: Сравнение типов устройств

2.4 Энергопотребление и требования к вычислительной мощности

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

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

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

2.5 Многоролевые устройства BLE

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

Рис. 1: Смартфон в качестве многоролевого устройстваРис. 1: Смартфон в качестве многоролевого устройства

2.6 Роль смартфонов в BLE

Одним из наиболее значимых преимуществ BLE перед другими похожими малопотребляющими технологиями, такими как ZigBee, Z-Wave, Thread и др.,) является его наличие в большинстве смартфонов, представленных на рынке. Практически все смартфоны уже имели на борту Bluetooth Classic с самых ранних дней, и большинство производителей чипсетов Bluetooth теперь внедряют в свои чипы поддержку и BLE, и Bluetooth Classic. В результате в настоящее время подавляющее большинство смартфонов поддерживает BLE.

Для смартфона возможность взаимодействовать с устройствами BLE дает пару существенных преимуществ:

  • Смартфоны предоставляют пользователям привычный интерфейс. Использование мобильного приложения для взаимодействия с BLE-устройством зачастую оказывается удобнее непосредственного взаимодействия с этим устройством.

  • Смартфоны, как правило, постоянно подключены к Интернету. Это означает, что данные, полученные с BLE-устройства, могут быть переданы в облако и сохранены для последующего анализа и обработки.

Сложности, связанные с BLE на смартфонах

В настоящий момент существуют две основные мобильные операционные системы: Android и iOS. Android представил встроенную поддержку BLE API в версии Android 4.3 (выпущена в июле 2012 года), в то время как iOS сделал то же самое немного раньше в октябре 2011 года.

Важно отметить, что многое зависит от возможностей аппаратного обеспечения операционной системы. В случае с iOS, поддержку BLE имеют все устройства, начиная с iPhone 4s. Ситуация с Android гораздо сложнее. Эта операционная система работает на устройствах разных производителей с разной аппаратной конфигурацией, поэтому нет простого способа определить, какие устройства первыми начали поддерживать BLE. Эта проблема фрагментации Android представляет большую проблему при разработке приложений, использующих BLE, которые должны работать одинаково на всех существующих Android-устройствах.

3. Рассылка и сканирование пакетов адвертайзинга

3.1 Общий профиль доступа (GAP)

Общий профиль доступа предоставляет фреймворк, который определяет способы взаимодействия BLE-устройств друг с другом. Он включает в себя следующие аспекты:

  • Режимы и роли устройств;

  • Обнаружение устройств: рассылка пакетов адвертайзинга, сканирование, параметры рассылки и сканирования, содержимое пакетов;

  • Установка соединения: инициация, подтверждение, параметры соединения;

  • Обеспечение безопасности

Реализация этого фреймворка является обязательной согласно официальной спецификации, и это то, что позволяет устройствам BLE обмениваться данными и взаимодействовать друг с другом.

Мы кратко осветили состояния сканирования и рассылки пакетов адвертайзинга BLE-устройств и упомянули, что периферийное устройство всегда запускается в состоянии рассылки пакетов адвертайзинга, даже если оно предназначено для работы в подключенном состоянии большую часть времени. Чтобы два устройства могли обнаружить друг друга, одно из них должно рассылать пакеты адвертайзинга, а другое сканировать первичные широковещательные каналы (радиоканалы 37, 38, 39) в поисках пакетов адвертайзинга, отправленных периферийным устройством.

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

3.2 Рассылка пакетов адвертайзинга

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

В BLE существуют 40 радиоканалов, разнесенных на 2 МГц (от центра до центра), как показано на рисунке ниже. Три канала называются каналами первичного адвертайзинга, в то время как оставшиеся 37 каналов используются для вторичного адвертайзинга, а также для передачи пакетов данных во время соединения.

Рис. 8: Радиоканалы в BLEРис. 8: Радиоканалы в BLE

Замечание: Так как устройство посылает пакеты адвертайзинга на одном из этих каналов и, как правило, постоянно переключается между ними, они (каналы) разнесены далеко по спектру друг от друга для того, чтобы избежать перекрестных помех между устройствами, вещающими на разных каналах. Также, расположение этих каналов на спектре выбрано таким, чтобы избежать помех от наиболее часто используемых Wi-Fi каналов.

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

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

Примечание: длина первичного пакета адвертайзинга ограничена 31 байтами. Длина вторичного пакета адвертайзинга может составлять до 254 байт.

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

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

Рис. 9: Устройства, имеющие и не имеющие возможность подключенияРис. 9: Устройства, имеющие и не имеющие возможность подключения

3.3 Состояние сканирования

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

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

Рис. 10: Пассивное и активное сканированиеРис. 10: Пассивное и активное сканирование

3.4 События адвертайзинга

Событие адвертайзинга состоит из нескольких пакетов, отправленных по всем или нескольким из трех каналов первичного адвертайзинга (37, 38 и 39). Существует семь типов событий адвертайзинга (их можно рассматривать как различные типы пакетов):

  • Подключаемое и сканируемое ненаправленное событие.

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

  • Подключаемое ненаправленное событие.

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

  • Подключаемое направленное событие.

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

  • Неподключаемое и несканируемое ненаправленное событие.

Позволяет всем устройствам принимать пакеты. В то же время отклоняет все запросы сканирования и попытки установить соединение.

  • Неподключаемое и несканируемое направленное событие.

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

  • Сканируемое ненаправленное событие.

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

  • Сканируемое направленное событие.

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

3.5 Параметры адвертайзинга

Под параметрами адвертайзинга понимают:

  • Интервал адвертайзинга.

Наиболее важный параметр из относящихся к адвертайзингу это интервал адвертайзинга. Значение этого параметра может дискретно изменяться в пределах от 20 миллисекунд до 10.24 секунд, с шагом в 625 микросекунд. Интервал адвертайзинга оказывает большое влияние на продолжительность работы от батареи, поэтому выбору его значения следует уделить самое пристальное внимание. Рекомендуется выбирать наибольший интервал адвертайзинга, позволяющий соблюсти баланс между скоростью обнаружения и энергопотреблением.

  • Данные адвертайзинга и ответа на сканирование.

Давайте посмотрим на формат пакета адвертайзинга и составляющие его поля. Стоит отметить, что пакет ответа на сканирование использует такой же формат.

Рис. 11: Формат пакета адвертайзинга (из спецификации стандарта Bluetooth 5)Рис. 11: Формат пакета адвертайзинга (из спецификации стандарта Bluetooth 5)

Данные адвертайзинга используют формат, аналогичный формату TLV (Type-Length-Value, Тип-Длина-Значение), используемому для передачи данных. Отличие состоит в том, что в пакетах адвертайзинга длина данных следует перед их типом. Данные адвертайзинга входят в состав протокольных данных (PDU, Protocol Data Unit) BLE-пакета и включает в себя:

  • Длину: длину данных, которые следуют за самим значением длины, включая тип данных и непосредственно данные.

  • Тип данных адвертайзинга: тип данных адвертайзинга, содержащихся в этой структуре TLV.

  • Данные адвертайзинга: непосредственно данные.

Типы данных адвертайзинга определены в дополнении спецификации Bluetooth (не в основном документе).

Ниже приведены одни из наиболее часто встречающихся типов данных:

  • Local Name: имя устройства, считываемое при его обнаружении другими устройствами, производящими процедуру сканирования.

  • Tx Power Level: Мощность передачи, измеряемая в дБм.

  • Flags: множество однобитных логических флагов (переменные, которые могут принимать одно из двух значений, истина [1] или ложь [0], включающее в себя:

    • Limited Discoverable Mode (ограниченный режим обнаружения);

    • General Discoverable Mode (общий режим обнаружения);

    • BR/EDR Not Supported (возможность поддержки классического протокола Bluetooth);

    • Возможность одновременной поддержки классического и Low Energy Bluetooth на одном устройстве со стороны контроллера;

    • Возможность одновременной поддержки классического и Low Energy Bluetooth на одном устройстве со стороны хоста.

Примечание: понятия BR (Basic Rate, базовая пропускная способность) и EDR (Enhanced Data Rate, расширенная пропускная способность) относятся к Bluetooth Classic.

  • Service Solicitation: список из одного или нескольких UUID, показывающий, какие сервисы поддерживаются и представлены GATT-сервером устройства. Это помогает центральному устройству узнать о поддерживаемых периферийным устройством сервисах до установления соединения.

  • Appearance: вид, определяет тип устройства в соответствии со спецификацией стандарта. Включает в себя такие виды как телефон, измеритель сердечного ритма, брелок для ключей и множество других.

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

3.6. Параметры сканирования

Существует три основных параметра сканирования:

  • Scan Type (тип сканирования): пассивное или активное.

  • Scan Window (окно сканирования): определяет, длительность сканирования.

  • Scan Interval (интервал сканирования): определяет частоту повторения сканирования.

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

Рис. 12: Параметры сканированияРис. 12: Параметры сканирования

__________________________________

А что дальше?

В следующей статье мы рассмотрим вопросы, связанные с соединениями, а также разберемся с сервисами, характеристиками и способами работы с ними.

Подробнее..

Подключаем новый Xiaomi Gateway 3 к Home Assistant без паяльника и смс

18.09.2020 10:20:14 | Автор: admin

Новый хаб от Xiaomi с поддержкой технологий Zigbee 3, Bluetooth Mesh, HomeKit и его подключение к достаточно популярной системе умного дома Home Assistant, интересует?



Введение


Устройства умного дома можно встретить на разных беспроводных протоколах.


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


Wi-Fi


Чаще всего новички выбирают устройства на технологии Wi-Fi. Ведь Wi-Fi роутер сегодня есть у всех. Умным устройством можно пользоваться сразу после покупки. Но тут есть нюанс: в количестве устройств слабость Wi-Fi. Роутеры от провайдеров в большинстве своём тот ещё хлам, способный справиться с 1-2 десятками устройств. И пять новых умных лампочек могут быть проблемой для всей сети.


Здесь выходом будет хороший двухдиапазонный роутер. Весь умный дом можно повесить на диапазон 2.4 ГГц, а мультимедиа-устройства (смартфоны, ноутбуки, телевизоры, колонки) на 5 ГГц.


Bluetooth


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


В таких случаях производители рекомендуют покупать BLE Gateway. Это устройство, которое будет посредником между Bluetooth и серверами производителя. Здесь уже далеко не все пользователи соглашаются на дополнительные траты и лишнюю железку в доме.


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


Zigbee


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


Дополнительную проблему составляет, что каждый такой Gateway поддерживает только дочерние устройства своего производителя. Купив устройства Philips Hue, IKEA, Sonoff, Xiaomi и Tuya, вы, скорее всего, должны будете докупить пять Gateway соответственно.


Эта технология заслуживает внимания по следующим причинам:


  • Беспроводные датчики довольно маленькие и в последнее время не такие и дорогие: 500 рублей за простейший датчик или кнопку это реальность, Xiaomi и AliExpress сделали своё дело.
  • Беспроводные датчики могут продержаться на одной батарее несколько лет, без шуток лет!
  • В количестве Zigbee устройств их сила: технология поддерживает Mesh, проводные устройства, скорее всего, будут ретрансляторами сигнала между Gateway и удалёнными датчиками, заодно снимая нагрузку с самого Gateway.
  • Технология поддерживает прямое управление в обход Gateway, можно связать кнопку и лампочку. В случае выхода Gateway из строя управление светом продолжит работать. Правда далеко не все кнопки это умеют.

Xiaomi


Фирма Xiaomi сделала многое для продвижения технологии Zigbee в альтернативных системах умного дома. Их старенький Xiaomi Gateway 2 (DGNWG02LM, lumi.gateway.v3) имел на борту "режим разработчика", который открывал локальный протокол доступа к управлению Zigbee устройствами этого шлюза. Интеграции этого протокола есть в множестве open source систем.


В евро-версии этого шлюза Xiaomi Gateway EU (DGNWG05LM, lumi.gateway.mieu01), а также в обновлённой версии Xiaomi Gateway 3 (ZNDMWG03LM, lumi.gateway.mgl03) этого протокола нет.


Обновлённая версия шлюза получила новый чип на Zigbee 3 (EFR32MG1B), а также поддержку технологии Bluetooth Mesh и HomeKit. В HomeKit поддерживаются не все устройства, будьте внимательны.


Xiaomi Gateway 3


В отличие от всех остальных шлюзов, обновлённая версия имеет уникальную особенность: на ней программно можно открыть Telnet-доступ. Доступ открывается только при наличии Mi Home токена, так что всё вполне секьюрно.


В этом шлюзе стоит чип серии EFR32 от фирмы Silicon Labs. Те в свою очередь поставляют вместе с чипом набор SDK. В составе SDK есть MQTT-транспорт, обеспечивающий доступ к Zigbee проколу из любого ПО, установленного как на шлюзе, так и за его пределами.


По умолчанию MQTT-брокер не доступен извне, но у нас ведь теперь есть Telnet!


В брокере есть два корневых топика: это "сырые" данные Zigbee и обработанные данные от Xiaomi. Я решил взять за основу обработанные данные. Там атрибуты устройств хоть и описаны псевдокодами, но в них всё же проще разобраться человеку, ничего не понимающему в Zigbee.


В итоге получился такой вот компонент для Home Assistant XiaomiGateway3.


Он автоматически включает Telnet и публичный MQTT, используя токен Mi Home.


Сейчас токен нужно получать нехитрым образом (инструкция в readme). Но в будущем я планирую добавить получение токена с серверов Xiaomi, используя аккаунт Mi Home. Ведь недавно в сети появилась рабочая реализация авторизации в их облаке.


Сейчас компонент получает список устройств и последние значения их атрибутов с Хаба. Но в дальнейшем я планирую добавить получение списка устройств из облака. Там есть пользовательские названия всех устройств.


BLE Gateway


С этим пришлось повозиться. Работа с Bluetooth-устройствами не отражается в MQTT. Зато все данные отражаются в консоли. Поэтому компонент подключается к хабу через Telnet отдельным потоком, перезапускает утилиту работы с Bluetooth и читает её вывод в реальном времени. Это самый стабильный способ, что я нашёл. В syslog данные от этой утилиты попадают с перебоями. Моих знаний Linux не хватает, чтоб понять, почему так происходит.


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


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


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


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


Поддержка Bluetooth Mesh ламп пока в разработке. Работа с ними сильно отличается от BLE-устройств.


Планы


Грандиозные.


Нужно отладить работу хаба со всем списком официально поддерживаемых Zigbee-устройств. Добавить возможность настройки "тонких" параметров:


  • настройки хаба: например, мощность (и её можно сильно увеличить!),
  • изменение настроек дочерних устройств: режим логических кнопок на выключателях, чувствительность датчика вибрации и многое другое.

Нужно добавить поддержку облака для получения токена хаба и полного списка Zigbee и Bluetooth-устройств.


Нужно добавить поддержку Bluetooth Mesh ламп.


И самое главное добавить поддержку устройств других производителей. Да, это возможно. Мне удалось подключить все сторонние устройства, что у меня были, и управлять ими. Такие устройства не отображаются в Mi Home и HomeKit. Но управлять ими можно с помощью "сырых" Zigbee-команд.


Для понимания полного масштаба проблемы такой поддержки загляните в исходники замечательного проекта zigbee2mqtt: devices, fromZigbee, toZigbee.


Почти каждое устройство требует свой собственный обработчик. В случае с Xiaomi Gateway 3 и официально поддерживаемыми устройствами роль такого обработчика выполняет софт хаба.


Другие мои разработки можно найти на GitHub. Среди русскоговорящей аудитории наиболее популярный проект YandexStation. Глобально очень хорошо себя зарекомендовал SonoffLAN. Но, думаю, XiaomiGateway3 его легко обгонит. За развитием этого и других моих проектов можно следить на моём канале Telegram.

Подробнее..

Плюсы интеграции Xiaomi Gateway 3 в Home Assistant

30.12.2020 14:14:51 | Автор: admin

Первая версия компонента XiaomiGateway3 для Home Assistant вышла 4 месяца назад, и с тех пор много всего изменилось. В прошлой статье я писал про создание компонента. А в этой статье расскажу, почему это решение так заинтересовало сотни пользователей.

Прошивка шлюза

Эта модель шлюза действительно получилась очень удачной. Иначе как можно объяснить, что компания Xiaomi уже два раза успела обновить прошивку шлюза на своих заводах. Это единственная модель шлюза, у которой прошивка теперь обновляется настолько оперативно на производстве.

В сентябре вышла первая версия компонента. В октябре, после долгих задержек, покупателям начали приходить шлюзы уже с новой прошивкой. В ней Telnet был закрыт паролем. В ноябре прошивка на заводах снова обновилась, и Telnet в ней совсем пропал. Определить проблемный шлюз можно по дате производства на коробке 2020.10 и выше.

В миг шлюз превратился из простого Plug and Play устройства в устройство, которое нужно обязательно вскрыть и прошить для интеграции в альтернативные системы умного дома.

Но шлюз хорош не только своим современным Zigbee-чипом и наличием чипа Bluetooth. Выбранный компанией-производителем SoC от Realtek позволяет в любой момент записать на шлюз любую прошивку, подключив всего три провода UART.

Если вы не любите паять, можно воспользоваться специальной прищепкой с контактами. Есть даже удачные примеры прошивки с бельевой прищепкой. Главное помнить, что обрыв контактов во время прошивки или UART на 5 В вместо 3.3 В вполне могут спалить микросхемы шлюза.

В развитии этого хаба участвует довольно много крутых людей. @serrj-sv собрал скрипт под Windows, который может прошить шлюз в полуавтоматическом режиме. А @zvldz собрал альтернативную версию прошивки, которая на 99% соответствует оригинальной. В ней поправлено недоразумение с закрытым Telnet и ещё пара мелочей. По особенностям прошивки и другим вопросам можно писать в этот чат Telegram.

Все полезные ссылки можно найти в вики проекта.

Поддержка Mi Home

Многие гики не любят облака и стараются с ними не связываться. Идеология облаков нарушает и главный девиз Home Assistant: конфиденциальность прежде всего (privacy first).

С другой стороны, если при данном подходе сохраняется полноценное локальное управление (local control, вторая часть девиза Home Assistant), то ничего страшного в облаках нет.

Экосистемы производителей добавляют устройствам больше свободы и возможностей управления, включая внешний доступ из коробки и прямое подключение к популярным голосовым ассистентам вроде Яндекс.Алисы и Google Assistant.

Также с этим подходом новые пользователи могут плавно осваивать Open Source системы умного дома: если что-то не получается, можно легко продолжить пользоваться устройством в экосистеме производителя.

Да, где-то на китайском сервере будет хранится информация, включен ли у вас в туалете свет. Так ли много на вас компромата у лампочки? Особенно если сравнивать с данными в вашем браузере или смартфоне. А полный доступ к прошивке шлюза позволяет пытливому уму легко отследить, какая именно информация отправляется на родину.

Поддержка Zigbee устройств Xiaomi

Компонент поддерживает:

  • все популярные устройства: выключатели, кнопки, розетки, реле, лампочки, датчики движения, температуры, открытия, протечки, вибрации, газа, дыма, шторы и замки;

  • редкие устройства вроде термостата Aqara Thermostat S2 (KTWKQ03ES) такой термостат пока не поддерживается даже в zigbee2mqtt;

  • самые свежие устройства вроде новых: реле Aqara Relay T1 и высокоточный датчик присутствия Aqara Hight Precision Motion Sensor (RTCGQ13LM).

Альтернатива: разнообразные DIY и коммерческие Zigbee-стики и DIY-хабы. Вот довольно большое русскоязычное сообщество в Telegram, где могут ответить на ваши вопросы по поводу Zigbee.

Поддержка BLE-датчиков Xiaomi

Компонент поддерживает:

  • все популярные датчики: разнообразные датчики температуры с экраном, датчик ухода за растениями, фумигатор, ночник и умный кубик Рубика;

  • редкие устройства вроде сейфа Xiaomi Safe Box (BGX-5/X1-3001) да, есть и такое устройство;

  • самые свежие датчики вроде новых датчиков двери, протечки и движения на технологии BLE;

  • различные дверные замки экосистемы Xiaomi.

Компонент не поддерживает не BLE устройства вроде чайника и самоката Xiaomi.

Альтернатива: шлюз на основе ESP32 или встроенный Bluetooth на сервере умного дома. Обычно этими способами поддерживаются только популярные датчики температуры альтернативной поддержки дверных замков я не встречал.

Поддержка Bluetooth Mesh-ламп

Компонент поддерживает новые Mesh-лампы экосистемы Xiaomi под брендами MiJia и Yeelight. И один китайский пользователь уже второй месяц пытается добавить поддержку Mesh-выключателей. А я никак не найду время рассмотреть его pull request.

Новые лампы очень выгодно отличаются ценой и функциями от аналогов на технологиях Wi-Fi и Zigbee, поэтому к ним стоит присмотреться. Об одной из таких ламп я рассказываю в своей статье про адаптивное освещение.

Альтернатива: на ум приходит только новый хаб Yeelight и подключение его к Home Assistant через протокол HomeKit. Open Source проекты с поддержкой Mesh-ламп я не встречал.

Поддержка Zigbee Home Automation

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

Я добавил в него режим, в котором Home Assistant напрямую подключается к Zigbee-чипу хаба через интеграцию Zigbee Home Automation.

Конечно, у подхода есть минусы:

  • Zigbee-чип перестаёт работать с Mi Home и начинает работать только с Home Assistant;

  • по количеству поддерживаемых устройств ZHA сильно уступает проекту zigbee2mqtt.

Но есть и плюсы:

  • в Китае не узнают, включен ли у вас в туалете свет;

  • в любой момент можно вернуть хаб в обычный режим работы c Mi Home без последствий для родной прошивки хаба;

  • BLE-датчики и Mesh-лампы продолжают работать в этом режиме;

  • команда Home Assistant активно развивает проект ZHA в рамках своей основной работы.

Альтернатива: шлюз Sonoff ZBBridge, прошитый Tasmota.

Поддержка сторонних устройств в Mi Home

В некоторых случаях для поддержки сторонних Zigbee-устройств режим ZHA включать не обязательно.

Изучая проблему, почему лампы IKEA E27 из российских магазинов не подключаются к хабам Xiaomi, я пришел к выводу, что в хабах зашита поддержка лишь семи моделей ламп, хотя проект zigbee2mqtt поддерживает более 30 моделей ИКЕА.

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

И самое интересное в данном способе то, что устройства работают и управляются в Mi Home без участия Home Assistant. И могут участвовать в автоматизациях.

Конечно, не стоит рассчитывать, что таким образом заработают любые устройства. Например, у меня не получилось заставить работать лампочки и датчик Philips Hue, а также кнопку Sonoff. А умные розетки, добавленные таким образом, не показывают энергопотребление.

Интеграция с облаком

Компонент поддерживает опциональную интеграцию с облаком. При этом компонентом можно пользоваться и без интеграции с облаком, просто добавив шлюз по IP-адресу и токену Mi Home.

Но если вы авторизуетесь в облаке Xiaomi, все данные о шлюзе загрузятся автоматически. Кроме адреса и токена шлюза из облака загрузятся все имена ваших Zigbee, BLE и Mesh-устройств. Вам не придётся снова заполнять их в Home Assistant, выясняя, что за устройство скрывается за именем 0x00158D0007396A5D.

Получение токенов любых Xiaomi устройств

Помимо получения данных о шлюзе и его устройствах - облачная интеграция позволяет получить Mi Home токены от любых Wi-Fi устройств в вашем аккаунте. Если ваш аккаунт использует одновременно разные сервера (например, европейский и китайский), это тоже поддерживается.

Функция получения токенов работает даже если у вас нет шлюза Xiaomi Gateway 3.

Расширенные настройки устройств

Один из самых популярных Zigbee-датчиков в экосистеме Xiaomi это датчик движения. Ранее я пользовался вторым шлюзом Xiaomi и писал автоматизации для этого датчика в Node-RED.

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

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

Логика работы показана на картинке

Мониторинг работы устройств

Радиосвязь всегда менее надёжнее провода. Сигнал от датчика может по разным причинам не дойти в центр. Zigbee и Bluetooth работают на той же частоте, что Wi-Fi и микроволновки.

Для обнаружения возможных проблем в компоненте есть опциональная статистика по каждому Zigbee и BLE устройству.

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

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

Именно этот показатель помог мне выпустить две заплатки и уменьшить количество пропусков срабатывания у популярного датчика движения Aqara Motion Sensor. Одна заплатка попала в компонент Home Assistant, а вторая в проект zigbee2mqtt.

Перспективы развития

За четыре месяца компонент оброс огромным количеством функционала и успел получить более 400 звёзд на GitHub. Но мысли по дальнейшему развитию и не думают кончаться.

Ещё остаётся добавить корректную работу с Bluetooth-устройствами при использовании нескольких хабов на одном сервере Home Assistant. Дело в том, что BLE датчики и Mesh-лампы не привязаны к какому-либо одному хабу. Все хабы могут получать данные с окружающих сенсоров и управлять окружающими лампами. Кстати огромный плюс в сравнении с технологией Zigbee.

Так же в планах добавить настройку параметров дочерних устройств - режим interlock в реле Aqara (переключатель пропал в последних версиях приложения Mi Home), чувствительность и задержки между срабатываниями нового датчика присутствия Aqara и многие другие.

Отдельным направление развития остаётся поддержка конвертеров проекта zigbee2mqtt. Если его удастся довести до релиза, пользователь сможет выбрать, как именно подключать имеющиеся у него устройства:

  • оригинальное ПО Xiaomi - готово

  • интеграция Zigbee Home Automation - готово

  • конвертеры zigbee2mqtt - есть рабочий прототип

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

На моём GitHub можно найти ссылки на другие компоненты и статьи. А за их развитием можно следить на моём канале в Telegram.

Подробнее..

Делаем из старого усилителя многофункциональный медиа сервер с помощью Raspberry pi

08.01.2021 18:05:33 | Автор: admin

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

Правда, в них отсутствует много всяких цифровых штучек. Разные flac и тому подобные форматы. Управление проигрыванием, музыкой и фильмами через телефон или компьютер. Возможность запустить музыку с Youtube, Last.fm или выбрать интернет радио. Удаленное проигрывание медиа через DLNA. Или просто возможность подключить ваш компьютер через Bluetooth и выводить весь звук через большие колонки. Или что там еще напридумывают нам в будущем для облегчения нашей аудиофильской жизни.

Но что нам мешает использовать все эти технологии в старой технике? Да и почему обязательно в старой У вас есть RCA, miniJack 3.5 или S/PDIF разъем на вашей магнитоле? Тогда мы идем к вам и И делаем из старого усилителя многофункциональный медиа сервер с помощью Raspberry Pi. Подключаем нашу Raspberry Pi через RCA и обновляем нашу технику до "острия технологической атаки". Не обязательно использовать RCA. Найдите аналоговый или цифровой вход на своей технике и выберите нужную комплектацию вашей Raspberry Pi. Я буду рассматривать вопрос сугубо с практической точки зрения. Как настроить все быстро на Raspberry Pi? Ведь статей профессионалов об особенностях работы той или иной технологии достаточно, чтобы не останавливаться на этом. Я возьму в качестве примера Raspberry Pi и плату для цифровой обработки звука на основе чипа PCM 5102 A.

Существует множество различных вариантов музыкальных DAC систем на любой вкус и цвет. Выбор только за вами. Вот некоторые из них для Raspberry Pi. DAC на русском ЦАП цифро-аналоговый преобразователь. Я возьму один из самых недорогих аналогов данного устройства. Судя по различным отзывам на веб просторах, очень даже неплохо себя зарекомендовавшую PCM 5102 A . На ней 6 контактов, и мы с легкостью подключим ее к нашей Raspberry Pi через GPIO разъем. Причем, подойдет любая серия нашей Малинки. Ведь для проигрывания музыки производственных мощностей хватит и самой младшей из них Raspberry Pi Zero. Хотя, без точных входных данных для любой поставленной задачи перед нашей Raspberry Pi, все относительно. И подобное утверждение является оценочным мнением автора. Далее для подключения к GPIO нашей платы используем следующую схему:

BCK -> Pin 12 (GPIO18) DATA(SCLK) -> Pin 40 (GPIO21)LRCK -> Pin 35 (GPIO19) GND -> Pin 39 (GND)GND -> Pin 34 (GND) VCC -> Pin 2 (5v) 

И нам потребуется подключить драйвер устройства в config.txt :

sudo nano /boot/config.txt

меняем на:

dtoverlay=hifiberry-dac #dtparam=audio=on 

Перезагрузимся у убедимся, что в журналах ядра все ок:

dmesg |grep hifi

И посмотрим нашу карточку через листинг доступных устройств alsa:

aplay -l

**** List of PLAYBACK Hardware Devices **** card 0: sndrpihifiberry [sndrpihifiberry_dac], device 0: HifiBerry DAC HiFi pcm5102a-hifi-0 [HifiBerry DAC HiFi pcm5102a-hifi-0] Subdevices: 1/1 Subdevice #0: subdevice #0

Если что-то пошло не так, включаем режим отладки:

sudo nano /boot/config.txt dtdebug=1

И смотрим, что происходит:

sudo dmesg sudo vcdbg log msg

И, по идее, запустив плеер, допустим, mplayer с каким-нибудь mp3, можно услышать музыку через канал на вашей аппаратуре, к которой вы подключились.

Подключаем Bluetooth

Технология Bluetooth не устаревает и активно развивается. Bluetooth передатчики постоянно обновляют версию. Слушать музыку через нее в HiEnd качестве можно. Она рождена для использования радио канала с большой загруженностью радио эфира. Это немаловажно при использовании ее в городе для передачи медиа контента. Правда, при использовании встроенного передатчика на Малинке не все гладко. И помехи при прослушивании музыки через нее могут быть слышны. Как вариант использовать внешнюю USB Bluetooth антенну. Она позволит избежать потерь, что, в общем-то, отдельная тема, требующая изучения. И тем не менее, достаточно просто и быстро через Bluetooth пробрасывать звук. Скажем, с компьютера на Raspberry Pi и далее через усилитель на колонки. Или с телефона подключаться в Raspberry и слушать музыку. Поэтому, запускаем Bluetooth на Малине. Для запуска нам понадобятся три компонента

Bluetooth сервер

Pulse-audio

Bluetooth клиент

Это не обязательный набор, но, как показала практика, все три компонента позволяют быстро и прозрачно запускать Bluetooth на Raspberry Pi. В результате, мы должны получить три работающие службы:

bluetooth.service

bt-agent.service

pulseaudio

Первая используется как серверная часть, Bt-agent как блютуз клиент для обработки входящих запросов по авторизации. Это удобно для автоматизации процессов подключения без заморочек ручного подключения к нашему каналу. И pulse аудио как сервер, обрабатывающий звук, и как прозрачная прослойка для автоматического перенаправления его между устройствами. Установим необходимые пакеты: sudo apt-get install pulseaudio pulseaudio-module-bluetooth bluez-tools

Bключим в группы pulse-access bluetooth пользователя pi:

sudo usermod -a -G bluetooth pi

sudo usermod -a -G pulse-access pi

sudo adduser pi pulse-access

Запускаем при старте аудио сервер pulseaudio:

systemctl --user enable pulseaudio

Для работы нашей Малины в режиме блютуз с постоянным обнаружением и с профилем A2DP приемника поправим конфигурацию:

sudo nano /etc/bluetooth/main.conf

и в нем:

  Class = 0x41C  DiscoverableTimeout = 0 A2DP

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

И Class = 0x41C в соответствии с этим ресурсом соответствует параметрам:

Major Device Class -> Audio/Video

Minor device class -> Portable Audio

Это те параметры, которые будут видны сканирующему Bluetooth устройству, чтобы разобраться, какие сервисы доступны через это устройство. В общем, можно не заморачиваться этими параметрами и выставить, например, Class = 0x20043C Что просто будет соответствовать большему количеству сервисов. Если вас вдруг не устраивает звук, тогда переходим к настройкам звука pulseaudio:

sudo nano /etc/pulse/daemon.conf

и смотрим "основные" настройки типа:

; resample-method = speex-float-1 ; default-sample-format = s16le; default-sample-rate = 44100 ; alternate-sample-rate = 48000

В качестве мануала по pulseaudio есть неплохой ресурс.

Кто-то рекомендует resample-metod=ffmpeg или resample-metod=speex-float-9, решать вам, если услышите разницу. Выставляем нужные параметры и убираем ";" чтобы раскомментировать строчку.

После изменений перезапускаем pulseaudio:

pulseaudio -k && pulseaudio --start

И не забываем посмотреть, сколько процессорного времени отъест pulseaudio при ваших HI-FI настройках:

htop

По загрузке процессора 3-ей Raspberry Pi с использованием ЦАП (DAC) аудио платы на основе микросхемы PCM5102 файл flac 24bit читается с загрузкой около :

10-15% для resample-metod=speex-float-1

30% для resample-metod=ffmpeg

60% для resample-metod=speex-float-9

Ну и напоследок, посмотрим теперь в сторону передачи звука через WiFi с помощью технологии DLNA.

MiniDLNA сервер

Если у вас на вашей Raspberry Pi находится медиа сервер, то для того чтобы ваша коллекция музыки была видна на других устройствах, поддерживающих DLNA, вам необходимо установить DLNA server. Здесь все просто: sudo apt-get install minidlna Рекомендую ознакомиться с возможностями Minidlna здесь.

В файле конфигурации правим папку с нашей медиатекой:

sudo nano /etc/minidlna.conf

И там указываем правильно папку:

media_dir=/home/pi/Music

Права на папку должны быть следующими:

sudo chmod -R 755 папка

Проверяем командой:

sudo -u minidlna ls -l папка

Cтартуем сервис:

sudo sevice minidlna start

И теперь в нашем проводнике в закладке сеть должно быть что-то вроде:

И, кликая на эту ссылку, открываем в браузере статус нашего сервиса:

А проиграть файлы можно скажем через "проигрыватель windows media":

Или запустить их на андроид с помощью "HI-FI Cast" приложения:

Если задача обратная и мы хотим воспроизводить музыку и фильмы, просматривать фото, которые находятся где-то удаленно на сервере через нашу Raspberry Pi, то нам потребуется установить на нее dlna render. С устройства из приложения, позволяющего находить наш raspberry media render, мы будем перенаправлять медиа контент на Raspberry Pi.

Я нашел два рендера прекрасно работающих на Raspberry Pi. У каждой из них есть свои плюсы.

DLNA Render Gmediarenderer

Проект развивается и полон последователей. Из основных возможностей стоит отметить что, этот рендер переключает устройства вывода, настраивает громкость и имеет уникальный идентификатор. Это позволит настроить в сети несколько подобных устройств. Есть более подробная статья про использование всех возможностей DLNA/UPnP как распределенной структуры устройств, взаимодействующих по этим протоколам и конкретно этого рендера. Инсталляция не должна вызвать сложностей. Проинсталлируем необходимые пакеты и gstreamer как движок для нашего рендера:

sudo apt-get install autoconf automake libtool gitsudo apt-get install libupnp-dev libgstreamer1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly  gstreamer1.0-libavsudo apt-get install gstreamer1.0-alsa sudo apt-get install gstreamer1.0-pulseaudio

Возьмем копию проекта и соберем его:

git clone https://github.com/hzeller/gmrender-resurrect.git cd gmrender-resurrect ./autogen.sh ./configure make sudo make install

и запускаем:

gmediarenderer

Найти и управлять нашим сетевым проигрывателем через Android устройство удаленно позволит программа типа DLNA Controller. Воспользуйтесь одной из подобных программ для этого: HiFi Cast, Airpincast, Bubbleupnpcast

Rygel DLNA Рендер

https://wiki.gnome.org/Projects/Rygel

Проект представляет из себя не только dlna рендер. Это полноценный медиа сервер UpnPMedia Server, который позволит и расшаривать и перенаправлять музыку видео и фото на любое UPnP/DLNA поддерживающее устройство. Из заявленных возможностей есть конвертация на лету записи в тот формат, который будет поддерживаться устройством воспроизведения. Взаимодействие со сторонними media плеерами такими как Totem, Rhythmbox, VLC. Удаленные запросы UPnP конвертируются в MPRIS запросы и позволяют взаимодействовать с этими media проигрывателями. Пример такого взаимодействия Rygel и VLC рассказан на странице David Wiencer.

Суть в том, что мы запускаем VLC плеер. Устанавливаем в конфигурационном файле mpris поддержку. И при работающем Rygel приложении vlc воспроизводит медиа контент, который мы посылаем ему через DLNA Rygel.

Проинсталлируем Rygel и VLC:

sudo apt-get install rygel vlc rygel-playbin

Если у вас еще не стоят gstreamer библиотеки:

sudo apt-get install libupnp-dev libgstreamer1.0-dev  gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly  gstreamer1.0-libav 

Убедимся, что в конфигурации включена поддержка MPRIS:

nano ~/.config/rygel.conf

[general]upnp-enabled=trueenable-transcoding=false[Tracker]enabled=false[MediaExport]enabled=false[Playbin]enabled=false[GstLaunch]enabled=false[MPRIS]enabled=true[External]enabled=false

И далее, создаем скрипт David Wiesner, который запускает сначала VLC затем Rygel:

sudo nano rygel-vlc.sh

И в нем:

#!/bin/bashvlcCall="vlc  intf dummy  fullscreen  no-osd"function cleanup(){for pid in $(pgrep -f "$vlcCall"); dokill -9 $piddonekillall rygel}function waitCpuDecrease(){pid=$1lastCpu="0.0"while true; docpu=$(ps S -p $pid -o pcpu=)sleep 0.2[ $(bc <<< "$cpu < $lastCpu") == 1 ] && breaklastCpu=$cpudone}killall rygel and vlccleanuplaunch vlc in background$vlcCall &wait until vlc has done most stuffwaitCpuDecrease $!start rygelrygel

Запустим его в фоновом режиме:

sudo ./rygel-vlc.sh &

И проверим наш DLNA render также через Andriod с помощью: HiFi Cast, Airpincast, Bubbleupnpcast.

И если вы не готовы экспериментировать с Linux , то в качестве собранных, готовых к работе "из коробки" Raspberry Pi с вышеописанным функционалом мы можем вам предложить наборы ViaMyBox "Музыка флер"!

Дистрибутив и код вы можете найти здесь.

https://github.com/viatc/viamybox

Надеюсь со временем, мы увидим и рассмотрим новые интересные проекты для Raspberry Pi на эту тему!

Подробнее..

Hi-перевод Обзор беспроводных наушников Bowers amp Wilkins PI7

07.06.2021 14:15:24 | Автор: admin

Портал о Hi-Fi, Hi-End технике(и не только) - hifiNews.ru подготовил перевод теста новых наушников британской компании Bowers & Wilkins (B&W).

Bowers & Wilkins PI7Bowers & Wilkins PI7

Bowers & Wilkins PI7представляют собой полностью беспроводные наушники, которые являются более дорогой из двух новых моделей данного типа, представленных в каталоге компании.

Надо заметить, что при разработке своих наушников B&W применят подход, отличный от компании Focal. Стоимость ее моделей никогда не превышает психологический барьер в $1000, в то время как модели Focal часто стоят дороже. Кроме того, если Focal придерживается последовательной концепции в дизайне своих наушников, подход Bowers & Wilkins более гибкий. И поскольку в настоящее время беспроводные модели находятся на пике популярности, эта британская компания решила выпустить и такую модель.

Однако стоит заметить, что Bowers & Wilkins никогда не принимает поспешных решений. Ее наушники создаются в результате серьезных исследований, и имеют интересные технические решения, а также впечатляющий дизайн. Продолжат ли PI7 эту хорошую традицию компании? Пришло время нам это узнать.

Технические характеристики и дизайн

PI7 - это первая полностью беспроводная модель от Bowers and Wilkins, но компания уже несколько лет выпускает наушники, поэтому неудивительно, что в PI7 нашли свое отражение несколько уже имеющихся у нее решений. Разумеется, здесь есть и несколько изменений в конструкции, связанных с тем, что данные наушники полностью беспроводные. Среди уже знакомого нам в PI7 9,2-миллиметровые динамические излучатели, аналогичные тем, которые мы видели в предыдущих разработках компании.

В PI7 к ним добавлен балансный излучатель, который работает на высоких частотах. Подобное мы тоже видели, в модели PI3, и это позволяет теоретически сочетать мощные басы от динамического излучателя с аккуратными верхами балансного. Ключевой особенностью PI7 является то, что каждый излучатель в них подключен к собственному усилителю. Заявленная частотная характеристика наушников составляет 10 Гц - 20 кГц.

Bowers & Wilkins утверждает, что до этого момента она воздерживалась от выпуска такой модели потому, что ее инженеров не удовлетворяло качество звука при использовании Bluetooth (интересный аргумент для компании с довольно широким ассортиментом беспроводных наушников, ну да ладно), а PI7 имеет полный из доступных на сегодня набор улучшений данной технологии. Наушники поддерживают все разновидности кодеков aptX, известных на данный момент: обычный, HD, с низкой задержкой LLC и адаптивный. Они также имеют поддержку AAC и, разумеется, стандартного кодека SBC. Дополнительно, имеется технология BLE, информирующая вас об уровне заряда аккумулятора, и функция управления через приложение. Разумеется, использование всего этого богатства будет возможным только в случае соответствующей поддержки и со стороны передающего устройства.

Для кодека aptX это означает, что, по крайней мере, теоретически, передачу аудио с параметрами 24 бит/48 кГц можно будет осуществлять. Наушники связываются между собой с использованием сигнала с такими же параметрами. Лично я считаю, что все это не слишком целесообразно. Если вы хотите слушать записи High resolution в мобильных условиях, то, скорее всего, предпочтете традиционное проводное соединение. Но, по крайней мере, вы можете быть уверенным, что сигнал на наушники приходит без дополнительного сжатия.

Из других положительных моментов следует отметить, что PI7 оснащены фирменной системой шумоподавления Bowers & Wilkins. PI7 имеют шесть микрофонов для работы адаптивного шумоподавления (настраиваемого в приложении), с передачей голоса, что было сделано и другими компаниями, но ни одна из них не приблизилась к Bowers. Есть несколько настроек для этой системы в зависимости от окружающей обстановки (лично мне такое не нужно, но, возможно, кому-то пригодится), а также возможность выключить всю дополнительную обработку. Можно с уверенностью сказать, что это наиболее универсальная на сегодня система шумоподавления на рынке, и поместить ее в пару вкладышей настоящий подвиг.

Учитывая, сколько в каждом наушнике находится излучателей, усилителей, микрофонов и тому подобного, места для размещения аккумуляторов там невелико. Bowers & Wilkins указывает продолжительность автономной работы PI7 в четыре часа, и я думаю, что это реалистичный показатель, хотя он может зависеть от выбранного кодека Bluetooth, а также от громкости прослушивания. Как и во многих подобных моделях, на помощь здесь приходит чехол-зарядник. Он обеспечивает четыре цикла заряда вкладышей, и может дать два часа прослушивания музыки после 15-минутной зарядки. Возможно, это не то устройство, которое я взял бы с собой в рейс в Новую Зеландию, но его хватит на неделю обычных поездок.

Это все хорошо, но есть в данных наушниках и некоторые особенности, которые я пока не встречал у конкурентов. Одна из них то, на что еще способен чехол PI7. В его основании находится разъем USB-C, который не только заряжает внутреннюю батарею, но и позволяет передавать аудио. Вы можете подключить его через специальный кабель к аналоговому выходу устройства (разъем 3,5 мм), сигнал с которого будет оцифрован, и передан на вкладыши по Bluetooth с кодеком aptX HD. Это значительно расширяет возможности использования PI7 и выгодно отличает их от конкурирующих моделей, у которых нет подобной функции.

Кроме того, Bower & Wilkins удалось сделать беспроводные наушники очень привлекательными и удобными в использовании. Полированные металлические секции вкладышей выглядят превосходно, а общая форма и размер PI7 достаточно компактны, чтобы они слишком торчали из вашего уха. При этом наушники B&W сидят в ушах не только комфортно, но и надежно, по крайней мере, для обычных пользователей, не увлекающихся экстремальными видами спорта.

Как проходило тестирование PI7?

Большая часть тестирования PI7 производилась с помощью Oppo Find X2 Neo, который имеет поддержку aptX Adaptive и HD, для качественного воспроизведения музыки. Часть тестирования также проводилось с помощьюAstell & Kern Kann. Затем iPad Pro был использован для тестирования качества звука с AAC, а ноутбук Lenovo T15P обеспечил возможность тестирования через USB и 3,5-мм подключения. Музыкальный материал почти полностью был с сервисов Qobuz и Deezer, также использовалось некоторое количество видеороликов.

Качество звучания

PI7 продемонстрировали звучание, которое я уже отмечал для модели Signature, но в миниатюре. Сопряжение с Oppo сработало сразу, а подсказки и сообщения, выводимые приложением, значительно упрощают процесс подключения. После установки соединения PI7 работали стабильно и безупречно переподключались 19 раз из 20.

Надо заметить, что Oppo и PI7, работающие вместе, очень чувствительны к определенным типам помех. Например, мониторы дверных датчиков в супермаркетах заставят два устройства либо разорвать соединение, либо пропустить трек. Впрочем, это происходило не во всех магазинах, так что серьезной претензии здесь нет. Но принять во внимание этот факт стоит.

За исключением этого, PI7 сразу произвели на меня сильное впечатление. Во-первых, в эту модель были перенесены все особенности, которыми Bowers & Wilkins наделила свои полноразмерные наушники. Чувствительность сенсоров управления PI7 на обоих вкладышах и работа системы шумоподавления были выше всяких похвал. Качество звука при разговоре по телефону приемлемое, если шум ветра не слишком высок, но это относится ко всем моделям данного класса. На практике PI7 ничем не уступает ни одному из конкурентов, которые я тестировал.

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

Typhoons от Royal Blood, возможно, не самая Hi-Fi-запись, однако ее насыщенный, взрывной звук способен прижать вас к креслу. PI7 удается поддерживать порядок среди этого музыкального хаоса, не теряя при этом его напора. Даже работая на высоких уровнях громкости, PI7 невозмутимы. Замечательно то, что контролируемость звука не уменьшает удовольствия от прослушивания музыки. Мощная начальная композиция Trouble's Coming по-прежнему остается в полной мере бунтарской, хотя и чуть-чуть управляемой.

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

Переключение на AAC через iPad действительно частично снижает качество звука, потому что у этого кодека более низкий битрейт. Если на Oppo музыка с сервисов Qobuz и (с потерями) Deezer звучит по-разному, то на iPad эта разница теряется. Это все еще хороший звук, но, очевидно, Apple оставила в нем какой-то запас, что бы полностью реализовать в собственном AirPod Pro, и, тем самым, дать им определенные преимущества. Однако в случае мобильного использования звук все равно вполне хорош.

А как насчет системы подключения через чехол? Она работает и работает хорошо. USB-соединение практически не имеет преимуществ по сравнению с Oppo, поэтому использование PI7 с ноутбуком становится бесполезным занятием. Подключение через аналоговый выход шумнее, чем другие варианты, и несколько снижает качество звука, но это все еще уникальная функция, изучением которой, я не сомневаюсь, сейчас вплотную занимаются конкуренты. Насколько она нужна именно вам отдельный вопрос, но подобное, на мой взгляд, сейчас есть только у PI7.

Заключение

В основе любых дебатов о ценности PI7 для многих пользователей - оправдывают ли они стоимость на $150 фунтов выше, чем у AirPod Pro.

стоимость в России на 05.2021 ~ 29 990

На этот вопрос есть два ответа: один совершенно очевидный, а другой - более тонкий. Если вы не являетесь пользователем iPhone и у вас есть достойная реализация aptX, то качество звука PI7 за счет более совершенного кодека будет выше. Если вы пользователь iOS, решение больше зависит от личных предпочтений и от того, есть ли у вас дополнительные $150, но я думаю, что PI7 все же будут лучше. Качество реализации шумоподавления, качество сборки и уровень комфорта, предлагаемые здесь, в значительной степени являются лучшими в данном классе. Bowers & Wilkins не спешила выпускать полностью беспроводные наушники, но результат впечатляет и на сегодня PI7 являются бесспорной самой выгодной покупкой.

Вердикт

Понравилось
Превосходное качество звука, особенно с высококачественными кодеками Bluetooth
Отличное шумоподавление
Удобные и качественно сделанные

Не понравилось
Небольшое время автономной работы вкладышей
Звучание с использованием кодека AAC не впечатляет
Возможны конфликты с внешним оборудованием
Высокая стоимость


Оценки:

Качество сборки: 9
Легкость использования: 9
Качество звука: 9
Дизайн: 9
Чувствительность: 9
Вердикт: 9

Основные технические характеристики Bowers & Wilkins PI7

Тип:

вкладыши, полностью беспроводные, с активным подавлением шумов

Излучатели:

9,2 мм динамический + арматурный

Частотный диапазон:

10 Гц - 20 кГц

Чехол-зарядник в комплекте:

да

Заменяемые внутриушные вставки:

да

Соединение:

Bluetooth SBC, AAC, aptX, aptX HD, LLC, Adaptive

При подготовке обзора использовались материалы с www.avforums.com (перевод с английского - hifiNews.RU)


Подробнее..

Перевод Android Bluetooth Low Energy (BLE) готовим правильно, часть 1

07.01.2021 20:09:56 | Автор: admin

В последний год я разрабатывал Bluetooth Low Energy (BLE) приложения под iOS и это оказалось довольно простым. Далее было портирование их на Android насколько это могло быть сложным?

Могу точно сказать это было сложней, чем представлял, мне пришлось приложить немало усилий для стабильной работы под Android. Я изучил много статей в свободном доступе, некоторые оказались ошибочными, многие были очень полезными и помогли в деле. В этой серии статей я хочу описать свои выводы, чтобы вы не тратили уйму времени на поиски как я.

Особенности работы BLE под Android

  • Google документация по BLE очень общая, в некоторых случаях нет важной информации или она устарела, примеры приложений не показывают, как правильно использовать BLE. Я обнаружил лишь несколько источников, как правильно сделать BLE.Презентация Stuart Kentдает замечательный материал для старта. Для некоторых продвинутых тем есть хорошая статьяNordic.

  • Android BLE API это низкоуровневые операции, в реальных приложениях нужно использовать несколько слоев абстракции (как например сделано из коробки в iOS-CoreBluetooth). Обычно нужно самостоятельно сделать: очередь команд, bonding, обслуживание соединений, обработка ошибок и багов, мультипоточный доступ . Самые известные библиотеки:SweetBlue,RxAndroidBleиNordic. На мой взгляд самая легкая для изучения - Nordic,см. детали тут.

  • Производители делают изменения в Android BLE стекеили полностью заменяют на свою реализацию. И надо учитывать разницу поведения для разных устройств в приложении. То что прекрасно работает на одном телефоне, может не работать на других! В целом не все так плохо, например реализация Samsung сделана лучше собственной реализации от Google!

  • В Android есть несколько известных (и неизвестных) баговкоторые должны быть обработаны, особенно в версиях 4,5 и 6. Более поздние версии работают намного лучше, но тоже имеют определенные проблемы, такие как случайные сбои соединения с ошибкой 133. Подробнее об этом ниже.

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

Сканирование устройств

Перед подключением к устройству вам нужно его просканировать. Это делается при помощи классаBluetoothLeScanner:

BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();BluetoothLeScanner scanner = adapter.getBluetoothLeScanner();if (scanner != null) {    scanner.startScan(filters, scanSettings, scanCallback);    Log.d(TAG, "scan started");}  else {    Log.e(TAG, "could not get scanner object");}

Сканер пытается найти устройства в соответствии снастройками filtersиscanSettings, при обнаружении устройства вызываетсяscanCallback:

private final ScanCallback scanCallback = new ScanCallback() {    @Override    public void onScanResult(int callbackType, ScanResult result) {        BluetoothDevice device = result.getDevice();        // ...do whatever you want with this found device    }    @Override    public void onBatchScanResults(List<ScanResult> results) {        // Ignore for now    }    @Override    public void onScanFailed(int errorCode) {        // Ignore for now    }};

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

  • Advertisement data- массив байтов с информацией об устройстве, для большинства устройств это имя и UUID сервисов, можно задать вfiltersимя устройства и UUID сервисов для поиска конкретных устройств.

  • RSSI уровень- уровень сигнала (насколько близко устройство).

  • дополнительные данные, см. документацию поScanResultздесь.

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

Настраиваем фильтр для сканирования

Вообще можно передать null вместо фильтров и получить все ближайшие устройства, иногда это полезно, но чаще требуются устройства с определенным именем или набором UUID сервисов.

Сканирование устройств по UUID сервиса

Используется если вам необходимо найти устройства определенной категории, например мониторы артериального давления со стандартным сервисным UUID: 1810. При сканировании устройство может содержать вAdvertisement dataUUID сервис, который характеризует это устройство. На самом деле эти данные ненадежные, фактически сервисы могут не поддерживаться, или подделыватьсяAdvertisement dataданные, в общем тут есть творческий момент.

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

Пример сканирования службы с артериальным давлением:

UUID BLP_SERVICE_UUID = UUID.fromString("00001810-0000-1000-8000-00805f9b34fb");UUID[] serviceUUIDs = new UUID[]{BLP_SERVICE_UUID};List<ScanFilter> filters = null;if(serviceUUIDs != null) {    filters = new ArrayList<>();    for (UUID serviceUUID : serviceUUIDs) {        ScanFilter filter = new ScanFilter.Builder()                .setServiceUuid(new ParcelUuid(serviceUUID))                .build();        filters.add(filter);    }}scanner.startScan(filters, scanSettings, scanCallback);

Обратите внимание на короткий UUID (например1810), он называется16-bit UUIDи является частью длинного128-bit UUID(в данном случае00001810-000000-1000-8000-000-00805f9b34fb). Короткий UUID это BASE_PART длинного UUID, см. спецификациюздесь.

Сканирование устройств по имени

Поиск устройств использует точное совпадение имени устройства, обычно это применяется в двух случаях:

  • поиск конкретного устройства

  • поиск конкретной модели устройства, например, мой нагрудный напульсник Polar H7 определяется как Polar H7 391BBB014, первая часть - Polar H7 общая для всех таких устройств этой модели, а последняя часть 391BBB014 - уникальный серийный номер. Это очень распространенная практика. Если вы хотите найти все устройства Polar H7, то фильтр по имени вам не поможет, придется искать подстроку у всех отсканированных устройств вScanResult. Пример с поискомточнопо имени:

String[] names = new String[]{"Polar H7 391BB014"};List<ScanFilter> filters = null;if(names != null) {    filters = new ArrayList<>();    for (String name : names) {        ScanFilter filter = new ScanFilter.Builder()                .setDeviceName(name)                .build();        filters.add(filter);    }}scanner.startScan(filters, scanSettings, scanCallback);

Сканирование устройств по MAC-адресам.

Обычно применяется дляпереподключенияк уже известным устройствам. Обычно мы не знаем MAC-адрес девайса, если не сканировали его раньше, иногда адрес печатается на коробке или на корпусе самого устройства, особенно это касается медицинских приборов. Существует другой способ повторного подключения, но в некоторых случаях придется еще раз сканировать устройство, например при очистке кеша Bluetooth.

String[] peripheralAddresses = new String[]{"01:0A:5C:7D:D0:1A"};// Build filters listList<ScanFilter> filters = null;if (peripheralAddresses != null) {    filters = new ArrayList<>();    for (String address : peripheralAddresses) {        ScanFilter filter = new ScanFilter.Builder()                .setDeviceAddress(address)                .build();        filters.add(filter);    }}scanner.startScan(filters, scanSettings, scanByServiceUUIDCallback);

Вероятно вы уже поняли, что можно комбинировать в фильтре UUID, имя и MAC-адрес устройства. Выглядит неплохо, но на практике я не применял такое. Хотя может быть вам это пригодится.

Настройка ScanSettings

ScanSettings объясняют Android как сканировать устройства. Там есть ряд настроек, которые можно задать, ниже полный пример:

ScanSettings scanSettings = new ScanSettings.Builder()        .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)        .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)        .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)        .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)        .setReportDelay(0L)        .build();

ScanMode

Безусловно, это самый важный параметр. Определяет метод и время сканирования в Bluetooth стеке. Такая операция требует много энергии и необходим контроль над этим процессом, чтобы не разрядить батарею телефона быстро. Есть 4 режима работы, в соответствии с руководствомNordics и официальной документацией:

  1. SCAN_MODE_LOW_POWER. В этом режиме Android сканирует 0.5с, потом делает паузу на 4.5с. Поиск может занять относительно длительное время, зависит от того насколько часто устройство посылает пакет advertisement данных.

  2. SCAN_MODE_BALANCED. Время сканирования: 2с, время паузы: 3с, компромиссный режим работы.

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

  4. SCAN_MODE_OPPORTUNISTIC. Результаты будут получены, если сканирование выполняется другими приложениями! Строго говоря, это вообще не гарантирует, что обнаружится ваше устройство. Стек Android использует этот режим в случае долгого сканирования, для понижения качества результатов (см. ниже Непрерывное сканирование).

Callback Type

Эта настройка контролирует как будет вызываться callback соScanResultв соответствии с заданными фильтрами, есть 3 варианта:

  1. CALLBACK_TYPE_ALL_MATCHES. Callback будет вызывать каждый раз, при получении advertisement пакета от устройств. На практике - каждые 200-500мс будет срабатывать сallback, в зависимости от частоты отправки advertisement пакетов устройствами.

  2. CALLBACK_TYPE_FIRST_MATCH. Callback сработает один раз для устройства, даже если оно далее будет снова посылать advertisement пакеты.

  3. CALLBACK_TYPE_MATCH_LOST. Callback будет вызван, если получен первый advertisement пакет от устройства и дальнейшие advertisement пакеты не обнаружены. Немного странное поведение.

В практике обычно используются настройкаCALLBACK_TYPE_ALL_MATCHESилиCALLBACK_TYPE_FIRST_MATCH. Правильный тип зависит от конкретного случая. Если не знаете - используйтеCALLBACK_TYPE_ALL_MATCHES, это дает больше контроля при получении callback, если вы останавливаете сканирование после получения нужных результатов - фактически этоCALLBACK_TYPE_FIRST_MATCH.

Match mode

Настройка того, как Android определяет совпадения.

  1. MATCH_MODE_AGGRESSIVE. Агрессивность обуславливается поиском минимального количества advertisement пакетов и устройств даже со слабым сигналом.

  2. MATCH_MODE_STICKY. В противоположность, этот режим требует большего количества advertisement пакетов и хорошего уровня сигнала от устройств.

Я не тестировал эти настройки подробно, но я в основном используюMATCH_MODE_AGGRESSIVE, это помогает быстрее найти устройства.

Number of matches

Параметр определяет сколько advertisement данных необходимо для совпадения.

  1. MATCH_NUM_ONE_ADVERTISEMENT. Одного пакета достаточно.

  2. MATCH_NUM_FEW_ADVERTISEMENT. Несколько пакетов нужно для соответствия.

  3. MATCH_NUM_MAX_ADVERTISEMENT. Максимальное количество advertisement данных, которые устройство может обработать за один временной кадр.

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

Report delay

Задержка для вызова сallback в миллисекундах. Если она больше нуля, Android будет собирать результаты в течение этого времени и вышлет их сразу все в обработчикеonBatchScanResults. Важно понимать чтоonScanResultне будет вызываться. Обычно применяется, когда есть несколько устройств одного типа и мы хотим дать пользователю выбрать одно из них. Единственная проблема здесь - предоставить информацию пользователю для выбора, это должен быть не только MAC-адрес (например имя устройства).

Важно: естьизвестный багдля Samsung S6 / Samsung S6 Edge, когда все результаты сканирования имеют один и тот же RSSI (уровень сигнала) при задержке больше нуля.

Кеширование Android Bluetooth стека

В результате процесса сканирования вы получаете список BLE устройств и при этом данные устройств кешируются в Bluetooth стеке. Там хранится основная информация: имя, MAC-адрес, тип адреса (публичный, случайный), тип устройства (Classic, Dual, BLE) и т.д. Android нужны эти данные, чтобы подключится к устройству быстрее. Он кеширует все устройства, которые видит при сканировании. Для каждого из них записывается небольшой файл с данными. Когда вы пытаетесь подключиться к устройству, стек Android ищет соответствующий файл, чтобы прочитать данные для подключения. Важный момент - одного MAC-адреса недостаточно для успешного подключения к устройству!

Очистка кеша

Bluetooth кеш, как и любой другой, не существует вечно, есть 3 ситуации, когда он очищается:

  1. Выключение и включение системного переключателя Bluetooth

  2. Перезагрузка телефона

  3. Очистка данных приложения (в ручном режиме в настройках телефона)

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

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

// Get device object for a mac addressBluetoothDevice device = bluetoothAdapter.getRemoteDevice(peripheralAddress)// Check if the peripheral is cached or notint deviceType = device.getType();if(deviceType == BluetoothDevice.DEVICE_TYPE_UNKNOWN) {    // The peripheral is not cached} else {    // The peripheral is cached}

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

Непрерывное сканирование?

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

Плохая новость в том, что Google в последнее время ограничивает (неофициально) непрерывное сканирование:

Непрерывное сканирование в фоне

Google значительно усложнил сканирование на переднем плане. Для фонового режима вы столкнетесь с еще большими трудностями! Новые версии Android имеют лимиты на работу служб в фоновом режиме, обычно после 10 минут работы, фоновый сервис прекращает свою работу принудительно. Посмотрите возможные решения этой проблемы:

Проверка разрешений (permissions)

Есть еще несколько важных моментов, прежде чем мы закончим статью. Для начала сканирования нужны системные разрешения (permissions):

<uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Убедитесь, что все разрешения одобрены, или запросите их у пользователя. РазрешениеACCESS_COARSE_LOCATIONGoogle считает опасным и для него требуется обязательное согласие пользователя.

private boolean hasPermissions() {    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {        if (getApplicationContext().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {            requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION }, ACCESS_COARSE_LOCATION_REQUEST);            return false;        }    }    return true;}

Прим. переводчика, в моем проекте для корректной работы с BLE потребовалось еще 2 разрешения:ACCESS_FINE_LOCATION(для API<23) иACCESS_BACKGROUND_LOCATIONобсуждение на Stackoverflow.

В итоге полный список разрешений включая версию Android10:

<uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

После получения всех нужный разрешений, нужно проверить включен Bluetooth, если нет - используйтеIntentдля запуска запроса на включение:

BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if (!bluetoothAdapter.isEnabled()) {    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);}

Заключение

Мы научились запускать сканирование BLE устройств с учетом жизненного цикла Activity (Fragment / Service), использовать фильтры и различные настройки сканирования, также узнали все нужные разрешения (permissions) для удачного запуска сканирования и особенности работы Android-Bluetooth кеша. В следующей статье мы погрузимся глубже в процесс подключения и отключения к устройствам.

Спасибо!

Подробнее..

Перевод Android Bluetooth Low Energy (BLE) готовим правильно, часть 2 (connectingdisconnecting)

15.01.2021 20:21:00 | Автор: admin
Часть 2Часть 2

Содержание

Часть #1 (scanning).

Часть #2 (connecting/disconnecting), вы здесь.

Впредыдущей статьемы подробно рассмотрели сканирование устройств. Эта статья - оподключении,отключениииобнаружении сервисов(discovering services).

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

После удачного сканирования, вы должны подключиться к устройству, вызывая методconnectGatt(). В результате мы получаем объект BluetoothGatt, который будет использоваться для всехGATT операций, такие как чтение и запись характеристик. Однако будьте внимательны, есть две версии методаconnectGatt(). Поздние версии Android имеют еще несколько вариантов, но нам нужна совместимость с Android-6 поэтому мы рассматриваем только эти две:

BluetoothGatt connectGatt(Context context, boolean autoConnect,        BluetoothGattCallback callback)BluetoothGatt connectGatt(Context context, boolean autoConnect,        BluetoothGattCallback callback, int transport)

Внутренняя реализация первой версии это фактически вызов второй версии с аргументомtransport = TRANSPORT_AUTO. Для подключения BLE устройств такой вариант не подходит.TRANSPORT_AUTOиспользуется для устройств с поддержкой и BLE и классического Bluetooth протоколов. Это значит, что Android будет сам выбирать протокол подключения. Этот момент практически нигде не описан и может привести к непредсказуемым результатам, много людей сталкивались с такой проблемой. Вот почему вы должны использовать вторую версиюconnectGatt()сtransport = TRANSPORT_LE:

BluetoothGatt gatt = device.connectGatt(context, false,     bluetoothGattCallback, TRANSPORT_LE);

Первый аргумент contextприложения. Второй аргумент флагautoconnect, говорит подключаться немедленно (false) или нет (true). При немедленном подключении (false) Android будет пытаться соединиться в течение 30 секунд (на большинстве смартфонов), по истечении этого времени придет статус соединенияstatus_code = 133. Это не официальная ошибка для таймаута соединения. В исходниках Android код фигурирует какGATT_ERROR. К сожалению, эта ошибка появляется и в других случаях. Имейте ввиду, сautoconnect = falseAndroid делает соединение только с одним устройством в одно и то же время (это значит если у вас несколько устройств - подключайте их последовательно, а не паралелльно). Третий аргумент функция обратного вызоваBluetoothGattCallback(callback) для конкретного устройства. Этот колбек используется для всех связанных с устройством операциях, такие как чтение и запись. Мы рассмотрим это более детально в следующей статье.

Autoconnect = true

Если вы установитеautoconnect = true, Android будет подключаться самостоятельно к устройству всякий раз, когда оно будет обнаружено. Внутри это работает так: Bluetooth стек сканирует сохраненные устройства и когда увидит одно из них подключается к нему. Это довольно удобно, если вы хотите подключиться к конкретному устройству, когда оно становится доступным. Фактически, это предпочтительный способ для переподключения. Вы просто создаетеBluetoothDeviceобъект и вызываетеconnectGattсautoconnect = true.

BluetoothDevice device = bluetoothAdapter.getRemoteDevice("12:34:56:AA:BB:CC");BluetoothGatt gatt = device.connectGatt(context, true, bluetoothGattCallback, TRANSPORT_LE);

Обратите внимание, этот подход работает только, если устройство есть в Bluetooth кеше или устройство было уже сопряжено (bonding). Посмотрите моюпредыдущую статью, где подробно объясняется работа с Bluetooth кешем. При перезагрузке смартфона или выключении/включении Bluetooth (а также Airplane режима) кеш очистится, это надо проверять перед подключением сautoconnect = true, что действительно раздражает.

Autoconnect работает только с закешированными и сопряженными (bonded) устройствами!

Для того, чтобы узнать, закешировано устройство или нет, можно использовать небольшой трюк. После создания объектаBluetoothDevice, вызовите у негоgetType, если результат TYPE_UNKNOWN, значит устройство не закешировано. В этом случае, необходимо просканировать устройство с этим мак-адресом (используя не агрессивный метод сканирования) и после этого можно использовать автоподключение снова.

Android-6 и ниже имеет известный баг, в котором возникает гонка состояний и автоматическое подключение становится обычным (autoconnect = false). К счастью, умные ребята из Polidea нашлирешение для этого. Настоятельно рекомендуется использовать его, если думаете использовать автоподключение.

Преимущества:

  • работает достаточно хорошо на современных версиях Android (прим. переводчика - от Android-8 и выше).

  • возможность подключаться к нескольким устройствам одновременно;

Недостатки:

  • работает медленнее, если сравнивать сканирование в агрессивном режиме + подключение сautoconnect = false. Потому что Android в этом случае сканирует в режимеSCAN_MODE_LOW_POWER, экономя энергию.

Изменения статуса подключения

После вызоваconnectGatt(), Bluetooth стек присылает результат в колбекonConnectionStateChange, он вызывается при любом изменении соединения.

Работа с этим колбеком достаточно нетривиальная вещь. Большинство простых примеров из сети выглядит так (не обольщайтесь):

public void onConnectionStateChange(final BluetoothGatt gatt,                                     final int status,                                     final int newState) {    if (newState == BluetoothProfile.STATE_CONNECTED) {        gatt.discoverServices();    } else {        gatt.close();    }}

Этот код обрабатывает только аргументnewStateи полностью игнорируетstatus. В многих случаях это работает и кажется безошибочным. Действительно, после подключения, следующее что нужно сделать это вызватьdiscoverServices(). А в случае отключения - необходимо сделать вызовclose(), чтобы Android освободил все связанные ресурсы в стеке Bluetooth. Эти два момента очень важные для стабильной работы BLE под Android, давайте их обсудим прямо сейчас!

При вызовеconnectGatt(), Bluetooth стек регистрирует внутри себя интерфейс для нового клиента (client interface: clientIf).

Возможно вы заметили такие логи в LogCat:

D/BluetoothGatt: connect() - device: B0:49:5F:01:20:XX, auto: falseD/BluetoothGatt: registerApp()D/BluetoothGatt: registerApp()  UUID=0e47c0cf-ef134afb-9f548cf3e9e808d5D/BluetoothGatt: onClientRegistered()  status=0 clientIf=6

Здесь видно, что клиент6был зарегистрирован после вызоваconnectGatt(). Максимальное количество клиентов (подключения) у Android равно 30 (константаGATT_MAX_APPSв исходниках), при достижении которого Android не будет подключаться к устройствам вообще и вы будете получать постоянно ошибку подключения. Достаточно странно, но сразу после загрузки Android уже имеет 5 или 6 таких подключенных клиентов, предполагаю, что Android использует их для внутренних нужд. Таким образом, если вы не вызываете методclose(), то счетчик клиентов увеличивается каждый раз при вызовеconnectGatt(). Когда вы вызываетеclose(), Bluetooth стек удаляет ваш колбек, счетчик клиентов уменьшается на единицу и освобождает ресурсы клиента.

D/BluetoothGatt: close()D/BluetoothGatt: unregisterApp()  mClientIf=6

Важно всегда вызыватьclose()после отключения! А сейчас обсудим основные случаи дисконнекта устройств.

Состояние подключения (newState)

ПеременнаяnewStateсодержит новое состояние подключения и может иметь 4 значения:

  • STATE_CONNECTED

  • STATE_DISCONNECTED

  • STATE_CONNECTING

  • STATE_DISCONNECTING

Значения говорят сами за себя. Хотя состоянияSTATE_CONNECTING,STATE_DISCONNECTINGесть в документации, на практике я их не встречал. Так что, в принципе, можно не обрабатывать их, но для уверенности, я предлагаю их явно учитывать (прим. переводчика - и это лучше, чем не обрабатывать их), вызываяclose()только в том случае если устройство действительно отключено.

Статус подключения (status)

В примере выше, переменная статусаstatusполностью игнорировалась, но в действительности обрабатывать ее важно. Эта переменная, по сути, является кодом ошибки. Вы можете получитьGATT_SUCCESSв результате как подключения, так и контролируемого отключения. Таким образом, мы можем по-разному обрабатывать контролируемое или внезапное отключение устройства. Если вы получили значение отличное отGATT_SUCCESS, значит что-то пошло не так и вstatusбудет указана причина. К сожалению, объектBluetoothGattдает очень мало кодов ошибок, все они описаныздесь. Чаще всего вы будете встречаться с кодом 133 (GATT_ERROR). Который не имеет точного описания, и просто говорит произошла какая-то ошибка. Не очень информативно, подробнее обGATT_ERRORпозже.

Теперь мы знаем, что обозначают переменныеnewStateиstatus, давайте улучшим наш колбекonConnectionStateChange:

public void onConnectionStateChange(final BluetoothGatt gatt,                                     final int status,                                     final int newState) {if(status == GATT_SUCCESS) {    if (newState == BluetoothProfile.STATE_CONNECTED) {        // Мы подключились, можно запускать обнаружение сервисов        gatt.discoverServices();    } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {        // Мы успешно отключились (контролируемое отключение)        gatt.close();    } else {        // мы или подключаемся или отключаемся, просто игнорируем эти статусы    }} else {   // Произошла ошибка... разбираемся, что случилось!   ...   gatt.close();} 

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

Состояние bonding (bondState)

Последний параметр, который необходимо учитывать в колбекеonConnectionStateChange этоbondState, состояние сопряжения (bonding) с устройством. Мы получаем этот параметр так:

int bondstate = device.getBondState();

Состояние bonding может иметь одно из трех значенийBOND_NONE,BOND_BONDINGorBOND_BONDED. Каждое из них влияет на то, как обрабатывать подключение.

  • BOND_NONE, нет проблем, можно вызыватьdiscoverServices();

  • BOND_BONDING, устройство в процессе сопряжения, нельзя вызыватьdiscoverServices(), так как Bluetooth стек в работе и запускdiscoverServices()может прервать сопряжение и вызвать ошибку соединения.discoverServices()вызываем только после того, как пройдет сопряжение (bonding);

  • BOND_BONDED, для Android-8 и выше, можно запускатьdiscoverServices()без задержки. Для версий 7 и ниже может потребоваться задержка перед вызовом. Если ваше устройство имеетService Changed Characteristic, то Bluetooth стек в этот момент еще обрабатывает их и запускdiscoverServices()без задержки может вызвать ошибку соединения. Добавьте 1000-1500мс задержки, конкретное значение зависит от количества характеристик на устройстве. Используйте задержку всегда, если вы не знаете сколькоService Changed Characteristicимеет устройство.

Теперь мы можем учитывать состояниеbondStateвместе сstatusиnewState:

if (status == GATT_SUCCESS) {    if (newState == BluetoothProfile.STATE_CONNECTED) {        int bondstate = device.getBondState();        // Обрабатываем bondState        if(bondstate == BOND_NONE || bondstate == BOND_BONDED) {            // Подключились к устройству, вызываем discoverServices с задержкой            int delayWhenBonded = 0;            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {                delayWhenBonded = 1000;            }            final int delay = bondstate == BOND_BONDED ? delayWhenBonded : 0;            discoverServicesRunnable = new Runnable() {                @Override                public void run() {                    Log.d(TAG, String.format(Locale.ENGLISH, "discovering services of '%s' with delay of %d ms", getName(), delay));                    boolean result = gatt.discoverServices();                    if (!result) {                        Log.e(TAG, "discoverServices failed to start");                    }                    discoverServicesRunnable = null;                }            };            bleHandler.postDelayed(discoverServicesRunnable, delay);        } else if (bondstate == BOND_BONDING) {            // Bonding в процессе, ждем когда закончится            Log.i(TAG, "waiting for bonding to complete");        }....

Обработка ошибок

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

  • Устройство отключилось намеренно. Например, все данные были переданы и больше ему нечего делать. Вы получите статус - 19 (GATT_CONN_TERMINATE_PEER_USER);

  • Истекло время ожидания соединения и устройство отключилось само. В этом случае придет статус - 8 (GATT_CONN_TIMEOUT);

  • Низкоуровневая ошибка соединения, которая привела к отключению. Обычно это статус - 133 (GATT_ERROR) или более конкретный код, если повезет;

  • Bluetooth стек не смог подключится ни разу. Здесь также получим статус - 133 (GATT_ERROR);

  • Соединение было потеряно в процессеbondingилиdiscoverServices. Необходимо выяснить причину и возможно повторить попытку подключения.

Первые два случая абсолютно нормальные явления и все что нужно сделать - это вызыватьclose()и подчистить ссылки на объектBluetoothGatt, если необходимо. В остальных случаях, либо ваш код, либо устройство, что-то делает не так. Вы возможно захотите уведомить UI или другие части приложения о проблеме, повторить подключение или еще каким-то образом отреагировать на ситуацию. Взгляните как я сделал это вмоей библиотеке.

Статус 133 при подключении (connecting)

Статус - 133 часто встречается при попытках подключиться к устройству, особенно во время разработки. Этот статус может иметь множество причин, некоторые из них можно контролировать:

  • Убедитесь, что вы всегда вызываетеclose()при отключении. Если этого не сделать, в следующий раз при подключении вы точно получитеstatus=133;

  • Всегда используйтеTRANSPORT_LEв вызовеconnectGatt();

  • Перезагрузите смартфон. Возможно Bluetooth стек выбрал лимит по клиентским подключениям или есть внутренняя проблема. (Прим. переводчика: я сначала выключал/включал Bluetooth, потом Airplane режим и если не помогало - перезагружал);

  • Проверьте что устройство посылает advertising пакеты. ВызовconnectGatt()сautoconnect = falseимеет таймаут 30 секунд, после чего присылает ошибкуstatus=133;

  • Замените/зарядите батарею на устройстве. Обычно устройства работают нестабильно при низком заряде;

Если вы попробовали все способы выше и все еще получаете статус 133, необходимо простоповторить подключение! Это одна из Android ошибок, которую мне так и не удалось понять или решить. Иногда вы получаете 133 при подключении к устройству, но если вызыватьclose()и переподключиться, то все работает без проблем! Есть подозрение, что проблема в кеше Android и вызовclose()сбрасывает его состояние для конкретного устройства. Если кто-нибудь поймет, как решить эту проблему дайте мне знать!

Отключение по запросу (disconnect)

Для отключения устройства вам необходимо сделать шаги:

  • вызватьdisconnect();

  • подождать обновления статуса вonConnectionStateChange;

  • вызватьclose();

  • освободить связанные с объектом gatt ресурсы;

Командаdisconnect()фактически разрывает соединение с устройством и обновляет внутреннее состояние Bluetooth стека. Затем вызывается колбекonConnectionStateChangeс новым состоянием disconnected.

Вызовclose()удаляет вашBluetoothGattCallbackи освобождает клиента в Bluetooth стеке.

Наконец, удалениеBluetoothGattосвободит все связанные с подключением ресурсы.

Отключение неправильно

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

  • вызватьdisconnect()

  • сразувызватьclose()

Это будет работать более-менее. Да устройство отключится, но вы никогда не получите вызов колбека с состоянием disconnected. Дело в том, чтоdisconnect()операция асинхронная (не блокирует поток и имеет свое время выполнения), аclose()немедленно удаляет коллбек! Получается, когда Android будет готов вызвать колбек, его уже не будет.

Иногда в примерах не вызываютdisconnect(), а толькоclose(). Это приведет к отключению устройства, но это неправильный способ, посколькуdisconnect()отключает активное соединение и отменяет ожидающее автоматическое подключение (вызов сautoconnect = true). Поэтому, если вы вызываете толькоclose(), любое ожидающее автоподключение может привести к новому подключению.

Отмена попытки подключения

Если вы хотите отменить подключение послеconnectGatt(), вам нужно вызватьdisconnect(). Так как в этому моменту вы еще не подключены, колбекonConnectionStateChangeне сработает! Просто подождите некоторое время послеdisconnect()и после этого вызывайтеclose()(прим. переводчика: обычно это 50-100мс).

При удачной отмене вы увидите примерно такое в логах:

D/BluetoothGatt: cancelOpen()  device: CF:A9:BA:D9:62:9E

Скорее всего, вы никогда не отмените соединение, для параметраautoconnect = false. Часто это делается для подключений сautoconnect = true. Например, когда приложение на переднем плане вы подключаетесь к вашим устройствам и отключаетесь от них, если приложение переходит в фон.

Прим. переводчика: но это не значит что дляautoconnect = falseне надо проводить такую отмену!

Обнаружение сервисов (discovering services)

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

Первым делом проверим, есть ли какие ошибки после обнаружения сервисов:

// Проверяем есть ли ошибки? Если да - отключаемсяif (status == GATT_INTERNAL_ERROR) {    Log.e(TAG, "Service discovery failed");    disconnect();    return;}

Если есть ошибки (обычно этоGATT_INTERNAL_ERRORсо значением 129), делаем отключение устройства, что-то исправить здесь невозможно (нет специальных технических способов для этого). Вы просто отключаете устройство и повторно пробуете подключиться.

Если все прошло удачно, вы получите список сервисов:

final List<BluetoothGattService> services = gatt.getServices();Log.i(TAG, String.format(Locale.ENGLISH,"discovered %d services for '%s'", services.size(), getName()));// Работа со списком сервисов (если требуется)...

Кеширование сервисов.

Bluetooth стек кеширует найденные на устройстве сервисы, характеристики и дескрипторы. Первое подключение вызывает реальное обнаружение сервисов, все последующие возвращаются кешированные версии. Это соответствует стандарту Bluetooth. Обычно это нормально и сокращает время соединения с устройством. Однако в некоторых случаях, может потребоваться очистить кеш, чтобы снова обнаружить их с устройства при следующем соединении. Типичный сценарий: обновление прошивки, в которой изменяется набор сервисов, характеристик, дескрипторов. Есть скрытый метод очистки кеша и добраться до него нам поможет механизм рефлексии Java:

private boolean clearServicesCache() {    boolean result = false;    try {        Method refreshMethod = bluetoothGatt.getClass().getMethod("refresh");        if(refreshMethod != null) {            result = (boolean) refreshMethod.invoke(bluetoothGatt);        }    } catch (Exception e) {        Log.e(TAG, "ERROR: Could not invoke refresh method");    }    return result;}

Этот метод асинхронный, дайте ему некоторое время для завершения!

Странные штуки в подключении/отключении

Хотя операции подключения и отключения выглядят просто, есть некоторые особенности, которые нужно знать.

  • Случайная ошибка 133 при подключении, выше мы разобрались как с ней работать;

  • Периодическое зависание подключения, не срабатывает таймаут и не вызывается колбекonConnectionStateChange. Это случается не часто, но я видел такие случае при низком уровне батареи или когда устройство находится на границе доступности по расстоянию Bluetooth. Скорее всего общение с устройством происходит, но затем прерывается и зависает. Мой обходной путь использовать свой таймер подключения и в случае таймаута закрывать соединение и отключаться;

  • Некоторые смартфоны имеют проблему с подключением во время сканирования. Например, Huawei P8 Lite один из таких. Останавливаем сканнер перед любым подключением (Прим. переводчика: это правило соблюдаем строго!);

  • Все вызовы подключения/отключения асинхронные. То есть неблокирующие, но при этом им нужно время, чтобы выполнится до конца. Избегайте быстрый запуск их друг за другом (Прим. переводчика: я обычно использую задержку 50-100мс между вызовами).

Следующая статья: чтение и запись характеристик.

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

Не терпится поработать с BLE? Попробуйтемою библиотеку Blessed for Android. Она использует все подходы из этой серии статей и упрощает работу с BLE в вашем приложении.

Подробнее..

Перевод Android Bluetooth Energy (BLE) готовим правильно, часть 3 (readwrite)

22.01.2021 20:15:37 | Автор: admin

Содержание

Часть #1 (scanning)

Часть #2 (connecting/disconnecting)

Часть #3 (read/write), вы здесь

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

Чтение и запись характеристик

Многие разработчики, которые начинают работать с BLE на Android, сталкиваются с проблемами чтения/записи BLE характеристик. НаStackoverflowполно людей, предлагающих просто использовать задержки Большинство таких советов неверные.

Есть две основные причины проблем:

  • Операции чтения/записи асинхронные. Это значит, что вызов метода вернется немедленно, но результат вызова вы получите немного позже в соответствующих колбеках. НапримерonCharacteristicRead()илиonCharacteristicWrite().

  • Одновременно может быть запущена только одна операция. Нужно дождаться выполнения текущей операции, и затем, запускать следующую. В исходном кодеBluetoothGattесть блокирующая переменная, которая при запуске операции устанавливается и при вызове колбека сбрасывается. Google забыла про это упомянуть в документации (Прим. переводчика: речь идет оmDeviceBusyиmDeviceBusyLockздесь).

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

Ниже кусок кодаBluetoothGatt.javaс блокировкой переменнойmDeviceBusy, перед чтением характеристики:

public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) {    if ((characteristic.getProperties()             & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {        return false;    }    if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid());    if (mService == null || mClientIf == 0) return false;    BluetoothGattService service = characteristic.getService();    if (service == null) return false;    BluetoothDevice device = service.getDevice();    if (device == null) return false;    synchronized (mDeviceBusy) {        if (mDeviceBusy) return false;        mDeviceBusy = true;    }    try {        mService.readCharacteristic(mClientIf, device.getAddress(),                characteristic.getInstanceId(), AUTHENTICATION_NONE);    } catch (RemoteException e) {        Log.e(TAG, "", e);        mDeviceBusy = false;        return false;    }    return true;}

Когда приходит результат чтения/записи, переменнаяmDeviceBusyсбрасывается в false снова:

public void onCharacteristicRead(String address,                                 int status,                                 int handle,                                 byte[] value) {    if (VDBG) {        Log.d(TAG, "onCharacteristicRead() - Device=" + address                + " handle=" + handle + " Status=" + status);    }    if (!address.equals(mDevice.getAddress())) {        return;    }    synchronized (mDeviceBusy) {        mDeviceBusy = false;    }....

Используем очередь

Выполнять чтение/запись по одной операции за раз неудобно, но любое сложное приложение должно это учитывать. Решение этой проблемы - использованиеочереди команд. Все BLE библиотеки, которые я ранее упоминал, так или иначе реализуют очередь. Это одна из лучших практик! Идея простая каждая команда сначала добавляется в очередь. Затем команда забирается из очереди на исполнение, после результата, команда помечается как завершенная и, удаляется из очереди. Запускать команды можно в любое время, но они выполняются точно в том порядке, в котором поступают в очередь. Это очень упрощает разработку под BLE. В iOS аналогично работает фреймворкCoreBluetooth(Прим. переводчика: который намного удобнее, чем реализация Bluetooth стека в Android).

Очередь создается для каждого объектаBluetoothGatt. К счастью, Android сможет обрабатывать очереди от нескольких объектовBluetoothGatt, вам не нужно об этом беспокоиться (Прим. переводчика: у меня это не сработало, я использовал глобальную очередь команд для всех устройств). Есть много способов создать очередь, мы будем использовать простую очередьQueueсRunnableдля каждой команды и переменнойcommandQueueBusyдля отслеживания работы команды:

private Queue<Runnable> commandQueue;private boolean commandQueueBusy;

Мы добавляем новый экземплярRunnableв очередь при выполнении команды. Ниже пример чтения характеристики (readCharacteristic):

public boolean readCharacteristic(final BluetoothGattCharacteristic characteristic) {    if(bluetoothGatt == null) {        Log.e(TAG, "ERROR: Gatt is 'null', ignoring read request");        return false;    }    // Check if characteristic is valid    if(characteristic == null) {        Log.e(TAG, "ERROR: Characteristic is 'null', ignoring read request");        return false;    }    // Check if this characteristic actually has READ property    if((characteristic.getProperties() & PROPERTY_READ) == 0 ) {        Log.e(TAG, "ERROR: Characteristic cannot be read");        return false;    }    // Enqueue the read command now that all checks have been passed    boolean result = commandQueue.add(new Runnable() {        @Override        public void run() {            if(!bluetoothGatt.readCharacteristic(characteristic)) {                Log.e(TAG, String.format("ERROR: readCharacteristic failed for characteristic: %s", characteristic.getUuid()));                completedCommand();            } else {                Log.d(TAG, String.format("reading characteristic <%s>", characteristic.getUuid()));                nrTries++;            }        }    });    if(result) {        nextCommand();    } else {        Log.e(TAG, "ERROR: Could not enqueue read characteristic command");    }    return result;}

В этом методе, сначала проверяем все ли готово для выполнения (наличие и тип характеристики) и логгируем ошибки, если они есть. ВнутриRunnable, фактически вызывается методreadCharacteristic(), который выдает команду на устройство. Мы также отслеживаем сколько было попыток, чтобы сделать повтор в случае ошибки (Прим. переводчика: это лучшая тактика, чтобы добиться стабильной работы с устройством). Если чтение характеристики возвращаетfalse, мы логгируем ошибку, завершаем команду, чтобы можно было запустить следующую. Наконец вызываетсяnextCommand(), чтобы запустить следующую команду из очереди:

private void nextCommand() {    // If there is still a command being executed then bail out    if(commandQueueBusy) {        return;    }    // Check if we still have a valid gatt object    if (bluetoothGatt == null) {        Log.e(TAG, String.format("ERROR: GATT is 'null' for peripheral '%s', clearing command queue", getAddress()));        commandQueue.clear();        commandQueueBusy = false;        return;    }    // Execute the next command in the queue    if (commandQueue.size() > 0) {        final Runnable bluetoothCommand = commandQueue.peek();        commandQueueBusy = true;        nrTries = 0;        bleHandler.post(new Runnable() {            @Override            public void run() {                    try {                        bluetoothCommand.run();                    } catch (Exception ex) {                        Log.e(TAG, String.format("ERROR: Command exception for device '%s'", getName()), ex);                    }            }        });    }}

Обратите внимание, мы используем методpeek()для получения объектаRunnableиз очереди, чтобы можно было повторить запуск позже. Этот метод не удаляет объект из очереди.

Результат чтения будет отправлен в ваш колбек:

@Overridepublic void onCharacteristicRead(BluetoothGatt gatt,                                 final BluetoothGattCharacteristic characteristic,                                 int status) {    // Perform some checks on the status field    if (status != GATT_SUCCESS) {        Log.e(TAG, String.format(Locale.ENGLISH,"ERROR: Read failed for characteristic: %s, status %d", characteristic.getUuid(), status));        completedCommand();        return;    }    // Characteristic has been read so processes it       ...    // We done, complete the command    completedCommand();}

Мы завершаем командуcompletedCommand()после обработки нового значения. Это помогает избежать одновременный вызов другой команды и состояния гонки.

Теперь мы готовы завершить команду, убираемRunnableиз очереди через вызовpoll()и запускаем следующую из очереди:

private void completedCommand() {    commandQueueBusy = false;    isRetrying = false;    commandQueue.poll();    nextCommand();}

В некоторых случаях (ошибка, неожиданное значение), вам нужно будет повторить команду. Сделать это просто, так как объектRunnableостается в очереди до вызоваcompletedCommand(). Чтобы не уйти в бесконечное повторение проверяем лимит на повторы:

private void retryCommand() {    commandQueueBusy = false;    Runnable currentCommand = commandQueue.peek();    if(currentCommand != null) {        if (nrTries >= MAX_TRIES) {            // Max retries reached, give up on this one and proceed            Log.v(TAG, "Max number of tries reached");            commandQueue.poll();        } else {            isRetrying = true;        }    }    nextCommand();}

Запись характеристик

Чтение характеристики достаточно простая операция, а запись требует дополнительных пояснений. Для выполнения записи нужно предоставитьхарактеристику,массив байтовитип записи. Существует несколько типов записи, важные для нас это:

  • WRITE_TYPE_DEFAULT(вы получите ответ от устройства, например, код завершения);

  • WRITE_TYPE_NO_RESPONSE(никакого ответа от устройства не будет).

Использовать тот или иной тип зависит от вашего устройства и характеристики (иногда она поддерживает оба типа записи, иногда только один конкретный тип).

В Android каждая характеристика имеет дефолтный тип записи, который определяется при ее создании. Ниже фрагмент кода из исходников Android, где определяется тип:

...if ((mProperties & PROPERTY_WRITE_NO_RESPONSE) != 0) {    mWriteType = WRITE_TYPE_NO_RESPONSE;} else {    mWriteType = WRITE_TYPE_DEFAULT;}...

Как вы видите, это работает нормально, если характеристика поддерживает только один их двух типов записи. Если характеристика поддерживает оба типа, то значение по умолчанию будетWRITE_TYPE_NO_RESPONSE. Имейте это ввиду!

Перед записью можно проверить характеристику, поддерживает ли она нужный тип записи:

// Check if this characteristic actually supports this writeTypeint writeProperty;switch (writeType) {    case WRITE_TYPE_DEFAULT: writeProperty = PROPERTY_WRITE; break;    case WRITE_TYPE_NO_RESPONSE : writeProperty = PROPERTY_WRITE_NO_RESPONSE; break;    case WRITE_TYPE_SIGNED : writeProperty = PROPERTY_SIGNED_WRITE; break;    default: writeProperty = 0; break;}if((characteristic.getProperties() & writeProperty) == 0 ) {    Log.e(TAG, String.format(Locale.ENGLISH,"ERROR: Characteristic <%s> does not support writeType '%s'", characteristic.getUuid(), writeTypeToString(writeType)));    return false;}

Я рекомендую всегда явно указывать тип записи и не полагаться на дефолтные настройки выбранные Android!

Итак, запись массива байтовbytesToWriteв характеристику выглядит так:

characteristic.setValue(bytesToWrite);characteristic.setWriteType(writeType);if (!bluetoothGatt.writeCharacteristic(characteristic)) {    Log.e(TAG, String.format("ERROR: writeCharacteristic failed for characteristic: %s", characteristic.getUuid()));    completedCommand();} else {    Log.d(TAG, String.format("writing <%s> to characteristic <%s>", bytes2String(bytesToWrite), characteristic.getUuid()));    nrTries++;}

Включение/выключение уведомлений

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

Для включения уведомлений нужно сделать две вещи в Android:

  1. вызватьsetCharacteristicNotification. Bluetooth стек будет ожидать уведомления для этой характеристики.

  2. записать1или2какunsigned int16в дескриптор конфигурации характеристик (Client Characteristic Configuration, сокращенно - ССС). Дескриптор CCC имеет короткий UUID2902.

Почему1или2? Потому что под капотом Bluetooth стека есть Уведомление и Индикация. Полученное Уведомление не подтверждаются стеком Bluetooth, а Индикация наоборот подтверждается стеком. При использовании Индикации, устройство будет точно знать, что данные получены и может их, например, удалить из локального хранилища. С точки зрения Android приложения нет разницы: в обоих случаях вы просто получите массив байтов и Bluetooth стек уведомит устройство об этом, если вы используете Индикацию. Итак,1включает уведомления,2 индикацию. Чтобы выключить их, записываем0. Вы должны самостоятельно определить, что записать в дескриптор CCC.

В iOS методsetNotify()делает всю работу за вас. Ниже пример, как сделать тоже самое на Android, там сначала идут проверки входных параметров, определяется что записать в дескриптор и, наконец команда отправляется в очередь:

private final String CCC_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb";public boolean setNotify(BluetoothGattCharacteristic characteristic,                         final boolean enable) {    // Check if characteristic is valid    if(characteristic == null) {        Log.e(TAG, "ERROR: Characteristic is 'null', ignoring setNotify request");        return false;    }    // Get the CCC Descriptor for the characteristic    final BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(CCC_DESCRIPTOR_UUID));    if(descriptor == null) {        Log.e(TAG, String.format("ERROR: Could not get CCC descriptor for characteristic %s", characteristic.getUuid()));        return false;    }    // Check if characteristic has NOTIFY or INDICATE properties and set the correct byte value to be written    byte[] value;    int properties = characteristic.getProperties();    if ((properties & PROPERTY_NOTIFY) > 0) {        value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;    } else if ((properties & PROPERTY_INDICATE) > 0) {        value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;    } else {        Log.e(TAG, String.format("ERROR: Characteristic %s does not have notify or indicate property", characteristic.getUuid()));        return false;    }    final byte[] finalValue = enable ? value : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;    // Queue Runnable to turn on/off the notification now that all checks have been passed    boolean result = commandQueue.add(new Runnable() {        @Override        public void run() {            // First set notification for Gatt object  if(!bluetoothGatt.setCharacteristicNotification(descriptor.getCharacteristic(), enable)) {                Log.e(TAG, String.format("ERROR: setCharacteristicNotification failed for descriptor: %s", descriptor.getUuid()));            }            // Then write to descriptor            descriptor.setValue(finalValue);            boolean result;            result = bluetoothGatt.writeDescriptor(descriptor);            if(!result) {                Log.e(TAG, String.format("ERROR: writeDescriptor failed for descriptor: %s", descriptor.getUuid()));                completedCommand();            } else {                nrTries++;            }        }    });    if(result) {        nextCommand();    } else {        Log.e(TAG, "ERROR: Could not enqueue write command");    }    return result;}

Результат записи в CCC дескриптор обрабатывается в колбекеonDescriptorWrite. Здесь вы должны отличить запись в CCC от записей в другие дескрипторы. Во время обработки колбека, мы также должны хранить, какие в данный момент характеристики уведомляются.

@Overridepublic void onDescriptorWrite(BluetoothGatt gatt,                                 final BluetoothGattDescriptor descriptor,                                 final int status) {    // Do some checks first    final BluetoothGattCharacteristic parentCharacteristic = descriptor.getCharacteristic();    if(status!= GATT_SUCCESS) {        Log.e(TAG, String.format("ERROR: Write descriptor failed value <%s>, device: %s, characteristic: %s", bytes2String(currentWriteBytes), getAddress(), parentCharacteristic.getUuid()));    }    // Check if this was the Client Configuration Descriptor  if(descriptor.getUuid().equals(UUID.fromString(CCC_DESCRIPTOR_UUID))) {        if(status==GATT_SUCCESS) {            // Check if we were turning notify on or off            byte[] value = descriptor.getValue();            if (value != null) {                if (value[0] != 0) {                    // Notify set to on, add it to the set of notifying characteristics          notifyingCharacteristics.add(parentCharacteristic.getUuid());                    }                } else {                    // Notify was turned off, so remove it from the set of notifying characteristics               notifyingCharacteristics.remove(parentCharacteristic.getUuid());                }            }        }        // This was a setNotify operation        ....    } else {        // This was a normal descriptor write....        ...        });    }    completedCommand();}

Чтобы узнать из какой характеристики пришло уведомление используйте методisNotifying():

public boolean isNotifying(BluetoothGattCharacteristic characteristic) {    return notifyingCharacteristics.contains(characteristic.getUuid());}

Лимиты на установку уведомлений

К сожалению, нельзя включить столько уведомлений, сколько хочешь. Начиная с Android-5 лимит равен 15. В более старых версиях он был равен 7 или даже 4. Большинство смартфонов поддерживают 15 уведомлений. Не забывайте отключать их, если они вам больше не нужны, чтобы не исчерпать лимит.

Проблемы с потоками

Итак, мы научились читать/писать характеристики, включать/выключать уведомления, а значит готовы использовать это в реальном проекте. Я думаю, что устройства BLE можно разделить на две категории:

  • Простые устройства. Например, термометр, который использует официальный Bluetooth Health Thermometer сервис. Такие устройства легко использовать, вы просто включаете уведомления и данные начинают поступать. Здесь мы используем только операции чтения характеристики, запись не нужна;

  • Сложные устройства. Это может быть любое устройство, но обычно все они используют свой внутренний протокол обмена данными. Часто эти протоколы не спроектированы под BLE, а просто транслируют внутренний последовательный протокол в BLE, где одна характеристика используется для отправки данных, а другая для приема. Сложность в том, что вам требуется знать большое количество команд для работы с устройством: авторизация, обновление пользовательских параметров, параметров самого устройства, получение сохраненных данных и т.д.

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

Типичная проблема с потоками выглядит так:

  • приходит уведомление

  • вы отправляете событие в свою собственную очередь для обработки

  • запускается обработку полученных данных

  • в это время приходит новое уведомление и перезаписывает предыдущее значение вBluetoothGattCharacteristic

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

Причины такого поведения:

  • как только сообщение доставлено, Android будет отправлять следующее(если оно есть). Посколько обработка данных отправляется в другой поток, текущий освобождается и Android продолжит доставку уведомлений;

  • Androidпереиспользует BluetoothGattCharacteristic объекты внутри. Они создаются в время обнаружения сервисов (services discovering) и после этогопереспользуютсямногократно. Таким образом, когда приходит уведомления Android сохраняет значение в объектBluetoothGattCharacteristic. Если характеристика в этот момент обрабатывается в другом потоке мы получим гонку состояний (race condition) и результат будет непредсказуемым.

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

Ниже пример, который использует такую тактику:

@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt,                                     final BluetoothGattCharacteristic characteristic) {    // Copy the byte array so we have a threadsafe copy    final byte[] value = new byte[characteristic.getValue().length];    System.arraycopy(        characteristic.getValue(),         0, value, 0,         characteristic.getValue().length);    // Characteristic has new value so pass it on for processing    bleHandler.post(new Runnable() {        @Override        public void run() {                     myProcessor.onCharacteristicUpdate(BluetoothPeripheral.this, value, characteristic);        }    });}

Другие рекомендации по работе с потоками

Есть несколько дополнительных рекомендаций по работе с BLE на Android. Поскольку стек BLE в основном асинхронный, у нас есть мульти-поточная обработка задач.

Android использует потоки:

  • При сканировании (результаты приходят вmainпоток);

  • Вызове колбековBluetoothGattCallback(выполняются в потокахBinder);

Обработка результатов сканирования на main потоке не будет проблемой. Но с потоками Binder все немного сложнее. При вызове колбека на потоке Binder, Android не будет отправлять новые данные пока не закончится обработка текущих, то есть поток Binder блокируется пока ваш код не завершится. Следует избегать тяжелых операций в колбеках, никакихsleep()или что-то подобное. Кроме того, никаких новых вызовов в объектеBluetoothGatt, пока вы находитесь в потоке Binder, хотя большинство методов асинхронные.

Я рекомендую следующее:

  • Всегда выполняйте вызовыBluetoothGattCallbackв отдельном потоке, возможно даже из потока пользовательского интерфейса (Прим. переводчика: работать на main потоке - плохая идея, если у вас есть активный обмен с устройством, обязательно будут залипания UI, не делайте так);

  • Освобождайте потокиBinderкак можно быстрее иникогдане блокируйте их;

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

Объявление объекта:

Handler bleHandler = new Handler();

Если хотите запуститьHandlerнаmainпотоке:

Handler bleHandler = new Handler(Looper.getMainLooper());

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

Следующая статья: сопряжение (bonding)

В этой статье мы разобрались с чтением и записью характеристик, включением и выключением уведомлений/нотификаций. В следующей статье, мы детально изучим процесс спряжения с устройством (bonding).

Не терпится поработать с BLE? Попробуйтемою библиотеку Blessed for Android. Она использует все подходы из этой серии статей и упрощает работу с BLE в вашем приложении.

Подробнее..

Перевод Android Bluetooth Low Energy (BLE) готовим правильно, часть 4 (bonding)

28.01.2021 18:09:16 | Автор: admin

Содержание

Часть #1 (scanning)

Часть #2 (connecting/disconnecting)

Часть #3 (read/write)

Часть #4 (bonding), вы здесь

Впредыдущей статьемы разобрались с операциями чтения/записи, включения/выключения нотификаций и организации очереди команд. В этой статье мы поговорим осопряжении устройств(Прим. переводчика далее я буду использовать термин bonding).

Bonding

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

Тема bonding плохо описана в документации Google, полностью непонятно, как приложение должно работать с bonding. Первое на что вы обратите внимание это методcreateBond(). Что интересно, в iOS такого метода нет вообще и фреймворкCoreBluetoothделает все за вас! Тогда зачем вызыватьcreateBond()? Кажется немного странным, вам заранее надо знать, какие устройства требуют bonding, а какие нет. Протокол Bluetooth был спроектирован так, что обычно устройства явно говорят им требуется или нет bonding. Я копнул немного глубже и поэкспериментировал. Чтобы разобраться с этим, ушло некоторое время, но в конце концов, все оказалось просто.

Принципы работы с bonding:

  • Пусть Android сам работает с bonding.Android сделает bonding за вас, когда устройство скажет, что нужен bonding, или во время операции чтения/записи зашифрованной характеристики. В большинстве случаев не надо вызыватьcreateBond()самостоятельно (Прим. переводчика: мне пришлось это делать самостоятельно, из-за особенностей прошивки устройства. Кроме того, Samsung работает по-другому, чем другие вендоры);

  • Нельзя запускать другие операции, в процессе работы bonding.Если вы будете запускать обнаружение сервисов или читать/писать характеристики, это приведет к ошибками и сбросу соединения. Просто дождитесь пока Android выполнит bonding;

  • Продолжайте очередь операций после завершения bonding.Как только операция bonding завершилась, продолжайте выполнение операций из очереди;

  • Если вы знаете, что делаете, и это необходимовы можете вызватьcreateBond()для запуска bonding с устройством самостоятельно. Но это должно быть исключением.

Что вызывает bonding?

Есть три причины, по которым запускается процесс bonding:

  1. При соединении с устройством, оно сигнализирует, что требуется bonding, до любых других операций;

  2. Характеристика может быть зашифрована для чтения или записи.При попытке прочитать или записать такую характеристику, запустится bonding. Если он пройдет удачно чтение/запись также выполнится, в случае ошибки bonding чтение/запись выполнится с ошибкойINSUFFICIENT_AUTHENTICATION. Такая же ошибка есть в iOS.

  3. Вызапускаете процесс bonding самостоятельночерез вызовcreateBond(). Если этого требует ваше устройство, оно вероятно не будет совместимо с iOS, так как там нет аналогичного метода. Но формально в протоколе Bluetooth такое возможно.

Давайте обсудим каждый случай.

Bonding во время подключения

Если устройство требует bonding сразу после подключения, то при вызове колбекаonConnectionStateChangeсостояние bonding будетBOND_BONDING. Это означает что идет процесс bonding ивы не должны ничего делать в этот момент, например вызыватьdiscoverServices(), до тех пор пока процесс bonding не закончится! Иначе возможны неожиданные дисконнекты или ошибки обнаружения сервисов. Поэтому следует специально обрабатывать эту ситуацию вonConnectionStateChanged:

// Take action depending on the bond stateif(bondstate == BOND_NONE || bondstate == BOND_BONDED) {    // Connected to device, now proceed to discover it's services    ... } else if (bondstate == BOND_BONDING) {    // Bonding process has already started let it complete    Log.i(TAG, "waiting for bonding to complete");}

Чтобы следить, как идет процесс bonding, необходимо зарегистрировать колбекBroadcastReceiverдля интентаACTION_BOND_STATE_CHANGEDдо вызоваconnectGatt. Этот колбек будет вызываться несколько раз в процессе bonding.

context.registerReceiver(bondStateReceiver,                         new IntentFilter(ACTION_BOND_STATE_CHANGED));private final BroadcastReceiver bondStateReceiver = new BroadcastReceiver() {    @Override    public void onReceive(Context context, Intent intent) {        final String action = intent.getAction();        final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);        // Ignore updates for other devices        if (bluetoothGatt == null || !device.getAddress().equals(bluetoothGatt.getDevice().getAddress()))            return;        // Check if action is valid        if(action == null) return;        // Take action depending on new bond state        if (action.equals(ACTION_BOND_STATE_CHANGED)) {            final int bondState = intent.getIntExtra(EXTRA_BOND_STATE, ERROR);            final int previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);            switch (bondState) {                case BOND_BONDING:                    // Bonding started                    ...                    break;                case BOND_BONDED:                    // Bonding succeeded                    ...                    break;                case BOND_NONE:                    // Oh oh                    ...                    break;            }        }    }};

После завершения bonding, мы запускаем обнаружение сервисов (service discovery), если они еще не обнаружены, это можно проверить:

case BOND_BONDED:    // Bonding succeeded    Log.d(TAG, "bonded");    // Check if there are services    if(bluetoothGatt.getServices().isEmpty()) {        // No services discovered yet        bleHandler.post(new Runnable() {            @Override            public void run() {                Log.d(TAG, String.format("discovering services of '%s'", getName()));                boolean result = bluetoothGatt.discoverServices();                if (!result) {                    Log.e(TAG, "discoverServices failed to start");                }            }        });    }

Вот и все, что касается особенностей bonding при подключении.

Bonding при чтении/записи зашифрованных характеристик

Если bonding стартует при чтении/записи зашифрованной характеристики, то самая первая операция чтения/записи окончится с ошибкойGATT_INSUFFICIENT_AUTHENTICATION. На версиях Android-6, 7 вы получите эту ошибку вonCharacteristicRead/onCharacteristicWrite, при этом процесс bonding уже будет запущен внутри Android. С версии Android-8 ошибки не будет и Android самостоятельно повторит операцию после завершения bonding. Получается на Android-6, 7 надо повторить операцию чтения/записи самостоятельно. Итак, вам надо поймать ошибку и сделать повтор операции после bonding.

При получении такой ошибки, не продолжайте запуск операций:

public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) {    // Perform some checks on the status field    if (status != GATT_SUCCESS) {        if (status == GATT_INSUFFICIENT_AUTHENTICATION ) {            // Characteristic encrypted and needs bonding,            // So retry operation after bonding completes            // This only happens on Android 5/6/7            Log.w(TAG, "read needs bonding, bonding in progress");            return;        } else {            Log.e(TAG, String.format(Locale.ENGLISH,"ERROR: Read failed for characteristic: %s, status %d", characteristic.getUuid(), status));            completedCommand();            return;        }    }...

После bonding проверяем, есть ли операция в процессе выполнения и повторяем ее:

case BOND_BONDED:    // Bonding succeeded    Log.d(TAG, "bonded");    // Check if there are services    ...    // If bonding was triggered by a read/write, we must retry it    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {        if (commandQueueBusy && !manuallyBonding) {            bleHandler.postDelayed(new Runnable() {                @Override                public void run() {                    Log.d(TAG, "retrying command after bonding");                    retryCommand();                }            }, 50);        }    }

Запуск bonding самостоятельно

Как я говорил выше, лучше не вызыватьcreateBondсамостоятельно, хотя сделать это, конечно можно. Спросите себя, это действительно необходимо? На iOS нет эквивалента методаcreateBond(), если этот метод единственный способ сделать bonding для вашего устройства, то скорее всего оно несовместимо с iOS. Это прямо указывается в документации iOS. Я перепробовал несколько десятков BLE устройств, и только в единственном случае я вызывалcreateBond()самостоятельно из-за исключительных обстоятельств.

При вызовеcreateBondсамостоятельно, также нельзя ничего делать, пока bonding не завершится и требуется регистрировать колбекBroadcastReceiverдля отслеживания процесса. Если устройство уже сопряжено (bonding завершился), тоcreateBond()вызовет ошибку, надо проверить состояние bonding перед вызовом.

Еще одна причина запускатьcreateBond()самостоятельно упростить повторное подключение. ОбъектBluetoothDeviceможно получить при помощи MAC-адреса, если устройство закешировано или сопряжено (bonding). Таким образом вам не придется снова сканировать устройство Может пригодиться! (Прим. переводчика: я как раз работал с таким вариантом подключения, его требовалось сделать полностью детерминированным, разбитым на подфазы, для точного понимания что происходит).

Удаление bonding

Как пользователь Android, я могу увидеть список сопряженных устройств в Bluetooth настройках. Там можно удалить устройство, bonding также будет удален.

Требуется некоторое время на удаление устройства.

Достаточно странно, что нет официального способа удалить bonding устройства программно. Это можно сделать, используя скрытый методremoveBond(), доступный через механизм рефлексии в Java:

try {    Method method = device.getClass().getMethod("removeBond", (Class[]) null);    result = (boolean) method.invoke(device, (Object[]) null);    if (result) {        Log.i(TAG, "Successfully removed bond");    }    return result;} catch (Exception e) {    Log.e(TAG, "ERROR: could not remove bond");    e.printStackTrace();    return false;}

Потеря bonding

Большинство BLE устройств поддерживают bonding только с одним смартфоном. Типичный сценарий, когда мы теряем bonding такой:

  • Смартфон А делает bonding с устройством Х

  • Смартфон B делает bonding с устройством Х

  • Смартфон А переподключается к устройству Х, и теперь bonding потерян.

При реконнекте смартфон А получит состояние bondingBOND_NONEв колбекеBroadcastReceiver. Сравнивайте предыдущее состояние bonding, чтобы понять была потеря или нет:

case BOND_NONE:    if(previousBondState == BOND_BONDING) {       // Bonding failed       ...    } else {       // Bond lost       ...    }    disconnect();    break;

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

Существует мелкий баг, о котором следует знать. При потере bonding, кажется нужнаодна секундадля того, чтобы Bluetooth стек обновил свое внутреннее состояние. Если сделать реконнект сразу после потери bonding, Android может сказать, что устройство все еще сопряжено, но на самом деле это будет не так. Сделайте задержку в одну секунду перед переподключением.

Pairing попап

Прим. переводчика: не нашел толковой замены слова pairing, спаривание - звучит неблагозвучно здесь.

Когда Android запускает процесс bonding, может появится всплывающее окно. Я говорю может, потому что некоторые вендоры используют свою логику показа этого попапа (Прим. переводчика: на моем Samsung-S9, после обновления до Android-10, это попап стал появляться всегда, при коннекте любого нового устройства, до этого обновления, такого не было). На смартфонах Google (или других вендоров, где код Android в этой части не изменялся), всплывающий попап появляется только при определенный условиях.

Pairing попап появляется на переднем фоне если:

  • Устройство недавно было в режиме обнаружения;

  • Устройство было обнаружено недавно;

  • Устройство недавно было выбрано в сборщике устройств;

  • Экран настроек Bluetooth виден.

Значение недавно означаетв течение последних 60 секунд. Условия выглядят непонятными, поэтому лучше посмотреть наисходный код. Если все эти условия не выполняются, то вместо попапа появится уведомление, которое большинство пользователей не замечает. Но если они заметят и нажмут на него, всплывающее окно сбивает с толку своей опцией доступа к контактам. Ужасный UI по-моему! Некоторые производители (справедливо) решили исправить такое поведение! На устройствах Samsung всплывающее окно-подтверждение (для подключений в режиме JustWorks) вообще не отображается, а всплывающие окна всегда появляются на переднем плане. При этом всплывающее окно открывается только при вводе PIN-кода или кодовой фразы. Никаких доступов к контактам и всегда передний план. Так намного лучше!

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

public void startPairingPopupHack() {    String manufacturer = Build.MANUFACTURER;    if(!manufacturer.equals("samsung")) {        bluetoothAdapter.startDiscovery();        callBackHandler.postDelayed(new Runnable() {            @Override            public void run() {                Log.d(TAG, "popup hack completed");                bluetoothAdapter.cancelDiscovery();            }        }, 1000);    }}

Важный момент здесь вы не должны запускать никакие BLE операции пока попап на экране. Подождите ответа от пользователя.

Если учтете все эти моменты, bonding будет работать как часики!

Подведение итогов

На этом мы завершаем цикл статей о BLE в Android (Прим. переводчика: я готовлю отдельную статью-заключение, где опишу свои подходы к работе с BLE устройствами на Android, небольшие ньюансы и решения для стабильной продолжительной работы с устройствами). Надеюсь эта информация будет полезной вам и сделает работу с BLE комфортнее. Чем больше знаешь про BLE, тем лучше работает ваше приложение. Успехов!

Не терпится поработать с BLE? Попробуйтемою библиотеку Blessed for Android. Она использует все подходы из этой серии статей и упрощает работу с BLE в вашем приложении.

Подробнее..

Подключаем нагрудный датчик пульса по Bluetooth на Swift

02.04.2021 14:12:19 | Автор: admin

С чего все началось?

Около года назад я приобрел данный девайс для контроля частоты сердечных сокращений (далее - ЧСС) во время тренировок. Датчик отлично подсоединяется к телефону, умным часам по Bluetooth, но обычно, фитнесс-приложения, анализирующие подобного рода данные требуют либо подписки, либо нагружены излишне сложными аналитиками, которые мне, как рядовому пользователю не очень интересны. Поэтому у меня родилась идея написать свое приложение для контроля ЧСС во время тренировок для IOS на Swift.

Немного теории о технологии Bluetooth LE

Bluetooth Low Energy - очень популярный и распространённый протокол обмена данными, который мы используем повсеместно и который становится все популярнее с каждым днем. У меня даже чайник на кухне управляется дистанционно через BLE. Low energy, кстати, гораздо сниженное энергопотребление в отличие от "голого" Bluetooth, настолько сниженное, что устройство готово общаться по данному протоколу на одной батарейке несколько месяцев, а то и лет.

Конечно, цитировать и переписывать спецификацию протокола BLE 5.2 нет никакого смысла, поэтому ограничимся основными понятиями.

Центральное и периферийное устройство

В зависимости от использования и назначения, устройство Bluetooth может быть:

  • Центральным (главным) - получает данные от периферийного устройства (наш телефон)

  • Периферийным - устройство, которое отправляет данные на центральное устройство (датчик ЧСС)

Рекламные пакеты данных протокола

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

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

  • Сервиса (услуг) - набор данных, описывающих функции устройства. В нашем случае мы увидим службу получения ЧСС.

  • Характеристик - дополнительных описаний сервисов устройства. Например характеристика изменения сердечного ритма в секунду, а также положения датчика на теле.

Переходя к абстракциям, сервисом является некий шкаф, в котором много ящиков - характеристик. Причем каждый сервис уникален и представлен идентификатором UUID, который может быть 16-битным или 128-битным, в зависимости от типов сервисов.

Перейдем к написанию кода

Создадим проект в Xcode с одноимённым названием, после чего добавим несколько необходимых Label в Main.storyboard и перетянем outlets этих labels во View Controller, закрепим их с помощью constraints, а также скроем их для первоначального изображения в методе viewDidLoad, как я сделал это на изображении:

Я создал outlets для текстовых значений "121" и "грудь", другие же текстовые значения просто закрепил на view, так как изменений в них делать мы не планируем.

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

В файле Info.plist проекта необходимо добавить свойство: Bluetooth Always Usage Description и прикрепить к нему описание, чтобы уведомить пользователя об использовании данных по Bluetooth при первом запуске приложения. Если данное свойство не добавить в список, то приложение "упадет" с одноименной ошибкой. Не забывайте про это!

Подключаем библиотеку Bluetooth

Тут все просто, для подключения библиотеки воспользуемся следующей строчкой:

import CoreBluetooth

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

Для начала создадим переменную центрального (главного) устройства в проекте рядом с объявлением других переменных:

var centralManager: CBCentralManager!

Теперь, чтобы получить доступ к методам необходимо назначить ViewController делегатом, но предварительно подпишем его под протокол CBCentralManagerDelegate. Сделать это предлагаю в extension ViewController, так будет рациональнее.

extension ViewController: CBCentralManagerDelegate {}

Xcode на такое пользовательское действие отреагирует ошибкой: "Type 'ViewController' does not conform to protocol 'CBCentralManagerDelegate'", оповещая, что данный протокол требует обязательную реализацию метода: "func centralManagerDidUpdateState(_ central: CBCentralManager)". Нажмем "fix", добавив этот метод в проект. Данный метод нужен для автоматической проверки состояния центрального менеджера, которого мы создали ранее.

Чтобы отобразить все состояния центрального менеджера, в теле метода "func centralManagerDidUpdateState(_ central: CBCentralManager)" напишем:

 func centralManagerDidUpdateState(_ central: CBCentralManager) {        switch central.state {        }

Xcode автоматически предложит вставить все возможные состояния данного условия, соглашаемся с ним. А в каждом из состояний напишем функцию print("это состояние"):

   extension ViewController: CBCentralManagerDelegate {    func centralManagerDidUpdateState(_ central: CBCentralManager) {        switch central.state {        case .unknown:            print ("central.state is unknown")        case .resetting:            print ("central.state is resetting")        case .unsupported:            print ("central.state is unsupported")        case .unauthorized:            print ("central.state is unauthorized")        case .poweredOff:            print ("central.state is poweredOff")        case .poweredOn:            print ("central.state is poweredOn")        @unknown default:            break        }    }}

Теперь нам осталось проинициализировать переменную "centralManager" и задать ей делегирование. Сделаем это в методе "viewDidLoad", а в качестве параметра очереди напишем "nil", определяя всю работу про Bluetooth в главной очереди.

override func viewDidLoad() {        super.viewDidLoad()        centralManager = CBCentralManager(delegate: self, queue: nil)        heartRateLabel.isHidden = true        bodyLocationLabel.isHidden = true    }

Собираем проект, запускаем на устройстве с включенным Bluetooth, видим системный запрос за его использование, соглашаемся и получаем в консоль заветное сообщение "central.state is poweredOn", которое сигнализирует нам о том, что центральный менеджер готов к работе. Если выключить Bluetooth на телефоне, то в консоли появится логичное "central.state is poweredOff".

Поиск Bluetooth устройств

Центральный менеджер ждет дальнейших указаний, и сейчас он их получит. Для этого в методе "centralManagerDidUpdateState" в случае ".poweredOn" после метода "print" пишем:

centralManager.scanForPeripherals(withServices: nil)

Менеджер начнет сканировать все доступные вокруг устройства, а чтобы мы смогли увидеть их в консоли приложения, необходимо реализовать метод делегата в extension ViewController ниже метода "centralManagerDidUpdateState" следующим образом:

 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {        print(peripheral)    }

Запускаем приложение... И теперь в консоли мы можем увидеть множество всех доступных для подключения устройств. Замечательно! Одним из них и является необходимый для подключения пульсометр. Но чтобы упростить поиск пульсометра, можно воспользоваться некоторой хитростью, которую я сейчас покажу.

Идентификатор служб UUID

Я ранее упомянул наличие данного идентификатора в протоколе Bluetooth как уникальную характеристику для различных устройств, поэтому могу сказать вам, что пульсометры обладают таким уникальным UUID для своей непосредственной службы измерения ЧСС. Список всех UUID можно также найти в спецификации, из которой я нашел нужный: "0x180D". Добавим новую константу в проект над объявленными ранее outlets:

let heartRateUUID = CBUUID(string: "0x180D")

Также обновим метод "centralManager.scanForPeripherals(withServices: nil)" добавив в него вышенаписанный идентификатор пульсометра:

case .poweredOn:            print ("central.state is poweredOn")            centralManager.scanForPeripherals(withServices: [heartRateUUID] )

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

<CBPeripheral: 0x280214000, identifier = D5A5CD3E-33AC-7245-4294-4FFB9B986DFC, name = COOSPO H6 0062870, state = disconnected>

Теперь необходимо создать переменную в проекте, с которой мы сможем связать данное устройство, для этого рядом с "var centralManager: CBCentralManager!" напишем:

var heartRatePeripheral: CBPeripheral!

А в методе "didDiscover peripheral" свяжем найденное устройство с вышеобъявленной переменной и прекратим поиск новых устройств с помощью метода:

 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {        print(peripheral)        heartRatePeripheral = peripheral        centralManager.stopScan()    }

Подключаемся к пульсометру

Для этого напишем под строкой "centralManager.stopScan()":

centralManager.connect(heartRatePeripheral, options: nil)

Нам уже удалось подключиться к пульсометру, но чтобы это действительно увидеть, необходимо реализовать еще один метод делегата "didConnect peripheral" ниже метода "didDiscover peripheral", который автоматически вызывается при подключении нового устройства:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {        print("Соединение установлено")    }

Собираем проект, запускаем на устройстве и видим в консоле заветное "Соединение установлено". Хороший результат, теперь двигаемся дальше.

Получаем список сервисов с пульсометра

После того, как соединение установлено, необходимо сделать запрос об услугах (сервисах), которые данный пульсометр готов предоставить. Для этого после установки соединения вызовем метод "heartRatePeripheral.discoverServices()" в методе "didConnect", который примет следующий вид:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {        print("Соединение установлено")        heartRatePeripheral.discoverServices(nil)    }

Запрос на получение сервисов сделан, а чтобы их увидеть и начать с ними работать, необходимо расширить класс протоколом "CBPeripheralDelegate" в самом низу нашего проекта и вызвать метод "peripheral(_:didDiscoverServices:)" следующим образом:

extension ViewController: CBPeripheralDelegate {        func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {        guard let services = peripheral.services else { return }        for service in services {            print(service)        }    }}

Метод получает сервисы, сообщает об этом центральному менеджеру и выводит их в консоль. В данный момент консоль будет пуста, так как необходимо делегировать данный протокол периферийному устройству "heartRatePeripheral". Сделаем это после инициализации периферийного устройства в следующем методе:

  func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {        print(peripheral)        heartRatePeripheral = peripheral                heartRatePeripheral.delegate = self                centralManager.stopScan()        centralManager.connect(heartRatePeripheral, options: nil)    }

Отлично, делегат обьявлен, метод получения сервисов написан, запустим программу на телефоне и получим в консоль список служб пульсометра:

<CBService: 0x2824b4340, isPrimary = YES, UUID = Heart Rate>

<CBService: 0x2824b4240, isPrimary = YES, UUID = Battery>

<CBService: 0x2824b4280, isPrimary = YES, UUID = Device Information>

<CBService: 0x2824b4200, isPrimary = YES, UUID = 8FC3FD00-F21D-11E3-976C-0002A5D5C51B>

Не все сервисы нам интересны и оставить необходимо лишь первый. Для этого можно провести так называемую фильтрацию с помощью идентификатора UUID в методе "heartRatePeripheral.discoverServices()"

heartRatePeripheral.discoverServices([heartRateUUID])

Вот теперь список служб отобразится в виде "<CBService: 0x2824b4340, isPrimary = YES, UUID = Heart Rate>", из которой мы сможем извлечь нужные нам характеристики - ящики ( шкафа мы уже получили).

Достаем характеристики из шкафа

Шкаф-сервис нам известен, осталось посмотреть, что он предлагает и получить это. Сделаем запрос на получение характеристик, для этого в теле метода "didDiscoverServices - peripheral" реализуем метод - поиск:

extension ViewController: CBPeripheralDelegate {        func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {        guard let services = peripheral.services else { return }        for service in services {            peripheral.discoverCharacteristics(nil, for: service)        }    }}

Теперь доступный сервис будет посылать свои характеристики, а увидеть мы их сможем в самостоятельном методе делегата "CBPeripheralDelegate" под названием "didDiscoverCharacteristicsFor". Реализуем его и выведем в консоль все доступные характеристики:

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {        guard let characteristics = service.characteristics else { return }        for characteristic in characteristics {            print(characteristic)        }    }

Запускаем программу, видим, что характеристики получены, а консоль заполнилась следующими строками:

<CBCharacteristic: 0x28024c120, UUID = 2A37, properties = 0x10, value = {length = 2, bytes = 0x0469}, notifying = NO>

<CBCharacteristic: 0x28024c180, UUID = 2A38, properties = 0x2, value = {length = 1, bytes = 0x01}, notifying = NO>

Видно, что у данной службы две характеристики, имеющие два уникальных идентификатора. Из спецификации на Bluetooth узнаем, что UUID = 2A37 отвечает за измерение ЧСС, а UUID = 2A38 за положение датчика на теле. Положение датчика на теле не самая интересная характеристика в данной теме, но будет полезно считать и ее.

Для удобства добавим в проект два уникальных идентификатора данных характеристик следующим образом:

 let heartRateUUID = CBUUID(string: "0x180D") let heartRateCharacteristicCBUUID = CBUUID(string: "2A37") let bodyLocationCharacteristicCBUUID = CBUUID(string: "2A38")

Характеристики отличаются друг от друга типами свойств. Например, характеристика ЧСС имеет свойство ".notify" т.е. она уведомляет об изменении значения ЧСС, а характеристика положения на теле имеет свойство ".read", т.е. может быть считана напрямую. Данное пояснение необходимо, чтобы правильно получить значения из них.

Положение пульсометра на теле

Характеристика выведена консоль, теперь нужно лишь реализовать метода считывая значений из нее. Для этого напишем запрос на чтение значений "peripheral.readValue(for: characteristic)"

 func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {        guard let characteristics = service.characteristics else { return }        for characteristic in characteristics {            peripheral.readValue(for: characteristic)        }    }

Запрос написан, как вы догадываетесь, нужно реализовать еще один метод "peripheral(_:didUpdateValueFor:error:)" делегата "CBPeripheralDelegate", который будет в асинхронном режиме получать ответ с данного запроса, причем в данном методе напишем конструкцию "switch - case", чтобы была возможность разделить характеристики по уникальному идентификатору:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,                error: Error?) {  switch characteristic.uuid {    case bodySensorLocationCharacteristicCBUUID:      print(characteristic.value ?? "no value")    default:      print("Unhandled Characteristic UUID: \(characteristic.uuid)")  }}

В консоли после выполнения данной программы появится строка "1 bytes". Это нужный результат, потому что мы пытались вывести объект типа "data".

Чтобы "распасить" данный байт, необходимо снова прибегнуть к спецификации, из которой мы сможем понять, как данный байт заполнен. Ускоряя результат, реализуем ниже функцию получения строки положения на теле датчика ЧСС из этого байта:

      private func bodyLocation(from characteristic: CBCharacteristic) -> String {        guard let characteristicData = characteristic.value,              let byte = characteristicData.first else { return "Error" }        switch byte {        case 0: return "Другое"        case 1: return "Грудь"        case 2: return "Запястье"        case 3: return "Палец"        case 4: return "Ладонь"        case 5: return "Мочка уха"        case 6: return "Нога"        default:            return "Резерв"        }    }

И теперь вызовем данную функцию в методе "didUpdateValueFor characteristic", одновременно выводя результат на экран телефона (не забудем показать скрытый label для положения датчика):

   func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,                    error: Error?) {                switch characteristic.uuid {                case bodyLocationCharacteristicCBUUID:            let bodySensorLocation = bodyLocation(from: characteristic)            bodyLocationLabel.text = bodySensorLocation            bodyLocationLabel.isHidden = false                  default:          print("Unhandled Characteristic UUID: \(characteristic.uuid)")      }            }

Ура! Характеристика успешно получена, прочитана и выведена на экран!

Не совсем ясно, где еще можно носить данный пульсометр, поэтому существует данная характеристика :)


Получение ЧСС и вывод на экран пользователя

Осталось совсем немного, и теперь нужно получить значения из характеристики ЧСС. Как мы помним, у нее тип значения ".notify", поэтому нам нужно как бы "подписаться на нее", чтобы она присылала обновленные значения ЧСС. Для этого нужно выполнить метод "peripheral.setNotifyValue(true, for: characteristic)" в функции "didDiscoverCharacteristicsFor service:

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {        guard let characteristics = service.characteristics else { return }        for characteristic in characteristics {            peripheral.readValue(for: characteristic)            peripheral.setNotifyValue(true, for: characteristic)        }    }

Если запустить приложение, то в консоли появятся стоки:

Unhandled Characteristic UUID: 2A37

Unhandled Characteristic UUID: 2A37

Unhandled Characteristic UUID: 2A37

Именно в этой характеристики и лежат данные о ЧСС. Теперь необходимо провернуть такую же развертку этих данных, обращаясь к спецификации. В некоторых моделях данные могут быть представлены либо 1 либо 2 байтами. Чтобы не получить конфуз, реализуем метод для "парсинга" этих данных в нужном порядке в протоколе "CBPeripheralDelegate".

  private func heartRate(from characteristic: CBCharacteristic) -> Int {        guard let characteristicData = characteristic.value else { return -1 }        let byteArray = [UInt8](characteristicData)                let firstBitValue = byteArray[0] & 0x01        if firstBitValue == 0 {            return Int(byteArray[1])        } else {            return (Int(byteArray[1]) << 8) + Int(byteArray[2])        }    }

И, наконец, добавим еще один case в методе "peripheral(_:didUpdateValueFor:error:)", в котором получим ЧСС, а также обновим и покажем label пользовательского интерфейса:

   func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,                    error: Error?) {                switch characteristic.uuid {                case bodyLocationCharacteristicCBUUID:            let bodySensorLocation = bodyLocation(from: characteristic)            bodyLocationLabel.text = bodySensorLocation            bodyLocationLabel.isHidden = false                    case heartRateCharacteristicCBUUID:            let bpm = heartRate(from: characteristic)            heartRateLabel.text = String(bpm)            heartRateLabel.isHidden = false                    default:          print("Unhandled Characteristic UUID: \(characteristic.uuid)")      }    }

Поздравляю!

Теперь данные с пульсометра выводятся на экран телефона. Я даже слегка нервничаю :)


Итоги

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

Всем успехов и спасибо!

Подробнее..

Назад к BLE или способ автоматизировать рутинные операции

21.10.2020 18:15:40 | Автор: admin

Кадр из фильма Назад в будущее (1985 год)

Стандарт Bluetooth 5.0 вышел в 2016 году, 2019-м появилась версия 5.2. За последнее время Apple провела две конференции WWDC 2017, WWDC 2019 посвященных CoreBluetooth. Активно развивается технология построения mesh сетей. Все стало еще лучше, быстрее и эффективнее. Интерес к этому направлению только растет. Выстроены целые системы управления на этой технологии.
Мы же задались целью автоматизировать рутинные операции и повысить безопасность доступа пользователей на свое рабочее место. В статье разберем, что было решено предложить пользователям, поговорим немного о технологии BLE (хотя, как тут кратко?) на примере небольшого проекта, который запускается на двух смартфонах и позволяет передавать данные в обе стороны, ну а в конце познакомлю с нашим приложением GM MOBILE ASSISTANT.



Я работаю swift-разработчиком в компании Гетмобит. Мы создаем ИТ-инфраструктуру а-персональных рабочих мест, которая позволит изменить традиционное восприятие рабочей среды, станет более гибкой и независимой от локации, где много внимания уделяется вопросам обеспечения безопасности. Наша экосистема называется GM SMART SYSTEM, ее ключевой элемент устройство GM-Box, сочетающее тонкий клиент и VOIP телефонию. Более подробно, с нашей инфраструктурой, можно ознакомиться в статьях моего коллеги.



Что нам захотелось улучшить?


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

Мы решили избавиться от этих проблем, сделав доступной авторизацию через мобильный телефон. Решение основано на том, что пользователь однажды ввел свои учетные данные в приложении на смартфоне и может забыть про ручной ввод. Мобильный телефон есть у каждого пользователя, приложение доступно в AppStore и Google Play и является частью GM SMART SYSTEM, т.е. работает из коробки, не требует затрат на поддержку. Данные хранятся в защищенной области, доступ к приложению закрыт пин-кодом или биометрией, передаваемые данные шифруются. Мы понимаем, что любую систему можно взломать, но всегда встает вопрос целесообразности.



Работа над решением


Определив смартфон, как средство для авторизации в нашей инфраструктуре, встал вопрос выбора кроссплатформенного решения iOS/Android. Не во всех компаниях разрешено использование WI-FI, USB API не доступно для iOS без дополнительной головной боли, Bluetooth порезан до BLE опять же в iOS. Была даже идея использовать звук но это отдельная история. По итогу, из всего того, что можно, наиболее практичным показался BLE. Пошли изучать матчасть.



Исследования


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

Технология BLE реализует клиент-серверную модель, является самостоятельным сегментом Bluetooth, рассчитана на низкое энергопотребление и относительно небольшую пропускную способность передачи данных. Вполне достаточно для обмена небольшими пакетами.

На схеме пунктиром выделены несколько Peripheral (GATT server), которые ожидают входящие подключения и что бы заявить о себе в радиоэфире, после инициализации, Peripheral, запускает процесс вещания Advertising пакетов с заданной частотой, чем чаще, тем выше вероятность обнаружения. Central (GATT client) запускает процесс поиска (как правило, запускается на несколько секунд) и пытается найти все устройства в радиусе 10-1500 (Bluetooth 5) метров. Дистанция, конечно, сильно зависит от преград и помех. После обнаружения получаем возможность подключиться, прочитать профиль устройства, подписаться на характеристики (тип NOTIFY), передать данные (WRITE). Любой атрибут имеет уникальный uuid идентификатор.





GATT (Generic Attribute Profile) профиль является общей спецификацией для отправки и получения коротких фрагментов данных, строится на основе протокола атрибутов АТТ. Intro to Bluetooth Generic Attribute Profile.
ATT (Attribute Protocol) протокол атрибутов, оптимизированный для работы на BLE-устройствах. Использует настолько мало байт, насколько это возможно. Каждый атрибут идентифицируется уникальным универсальным идентификатором UUID.
Service совокупность характеристик.
Characteristic по сути, это канал для передачи данных, ограниченного заданной функциональностью:

  • READ после подключения можно прочесть предварительно заданное значение.
  • WRITE клиент использует характеристику такого типа для передачи данных на сервер, однонаправленный канал.
  • NOTIFY используется для асинхронного приема данных от сервера, клиент должен подписаться на получение.
  • INDICATE используется для получения данных, не требует подписки, но необходимо запросить значение.

Descriptor используется для описания характеристики, необязательный атрибут.
UUID стандартизированный 128-битный строковый идентификатор, используемый для однозначной идентификации информации. Может быть задан в сокращенном виде, например 180A информация об устройстве. Обнаружив сервис с таким идентификатором, мы должны предположить, что имеющиеся характеристики будут выдавать нам информацию об этом устройстве. Подробнее в этой статье. Можно воспользоваться генератором uuid.
Adveritising короткие пакеты, необходимы для обнаружения BLE Peripherals. Частота появления в радиоэфире зависит от выставленного значения, минимально от нескольких мс. Дополнения в bluetooth 5.
Перечень зарезервированных профилей. Перечень зарезервированных
16 бит идентификаторов.


Пробуем на практике


Чтобы лучше понять приведенную выше схему, написал небольшое приложение. В проекте используется Core Bluetooth фреймворк. Запускать нужно на 2-х телефонах. На первом выбираем GATT сервер, на втором GATT клиент. После подключения можно пересылать текстовые сообщения. Ниже, разберем по порядку ключевые моменты создания профилей.


Настройка проекта


После создания нового проекта в Xcode добавляем ключ в info.plist

<key>NSBluetoothAlwaysUsageDescription</key><string> Описание для чего будет использоваться bluetooth в приложении</string>


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



Важным моментом, особенно для профиля peripheral, является работа в фоновом режиме, если его не включить, то приложение не будет отправлять Advertising пакеты при сворачивании или выключенном экране. Для активации нужно добавить Background Modes и выбрать два пункта, так как мы хотим работать с обоими профилями.




Создаем профиль GATT сервер


Для управления профилем нам потребуются два класса: CBPeripheralManager, CBPeripheralManagerDelegate. Атрибуты задаются с помощью: CBMutableService, CBMutableCharacteristic, CBMutableDescriptor. Причем созданные атрибуты вкладываются один в другой по цепочке дескриптор, характеристика, сервис.



В начале создадим UUID для каждого атрибута планируемого профиля.



static let primaryServiceUUID =CBUUID(string: "bf52e2d6-ff52-43cb-99a0-872fa3dde94f")static let readCharacteristicUUID =CBUUID(string: "f438775d-e605-42b8-abe7-6e31ed52ea87")static let transferCharacteristicUUID =CBUUID(string: "a82ae020-e171-42f8-a31c-5f612926f041")

Затем нужно создать две характеристики. Первая рассчитана только на передачу некоторой предварительно подготовленной информации. Central сможет считать данные после подключения.



var readCharacteristic =CBMutableCharacteristic(type: UUIDs.readCharacteristicUUID,properties:[.read],value:"some identificator".data(using: .utf8),permissions:[.readable])

Вторая будет выполнять роль канала передачи данных. Свойство .notify ставиться, чтобы можно было отправлять уведомления, но для получения Central необходимо подписаться на них. Второе свойство .writeWithoutResponse означает, что запись в характеристику осуществляется без подтверждения.



var transferCharacteristic =CBMutableCharacteristic(type: UUIDs.transferCharacteristicUUID,properties:[.notify, .writeWithoutResponse],value:nil,permissions:[.readable, .writeable])

Теперь создаем сервис. Ставим primary=true, чтобы он был добавлен в перечень сервисов в advertising пакете.



var primaryService = CBMutableService(type:UUIDs.primaryServiceUUID, primary:true)

Добавляем характеристики.



primaryService.characteristics = [readCharacteristic, transferCharacteristic]

Созданные атрибуты передаем в CBPeripheralManager.



manager = CBPeripheralManager(delegate: self, queue: nil)for service in gattProfile.services {manager.add(service)}

При создании CBPeripheralManager необходимо указать делегат CBPeripheralManagerDelegate. Если после инициализации peripheral manager функция



(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral

возвращает состояние peripheral.state = .poweredOn, значит manager готов к работе.

GATT профиль заполнен. Осталось собрать Advertising пакет и можно запускать передачу в радиоэфир. Структура пакета это массив [String:Any] значений. Основной параметр, который нам нужно задать идентификатор primary сервиса.



struct Advertisment {var value = [CBAdvertisementDataServiceUUIDsKey:[UUIDs.primaryServiceUUID]]}


Можно было бы еще добавить параметр CBAdvertisementDataLocalNameKey, но в данном случае это не имеет значения. Наши данные для advertising пакета объединяются с advertising данными смартфона. Этот параметр перезаписывается и как правило localName мы видим как iPhone 7.



Для запуска Advertising достаточно выполнить команду.

manager.startAdvertising(Advertisment().value)


Создаем профиль GATT клиент


Для реализации клиентского профиля нам потребуются классы: CBCentralManager, CBCentralManagerDelegate, CBPeripheralDelegate. Последний нужен для работы с найденным GATT сервером, т.к. получаем экземпляр CBPeripheral.
Для того, чтобы найти сервер нам нужно создать экземпляр менеджера CBCentralManager и запустить поиск.



var manager = CBCentralManager(delegate: self, queue: nil)manager.scanForPeripherals(withServices: [UUIDs.primaryServiceUUID],options: managerOptions)

В качестве параметра withServices: передаем массив uuids по этим идентификаторам будет осуществляться автоматическая фильтрация, будут возвращены только те peripherals, идентификаторы которых заданы в массиве. Передавая nil можно обнаружить все активные BLE устройства поблизости. В нашем случае был создан только один сервис, его uuid и передаем в качестве параметра. Параметр options: не обязателен, я передаю туда:



private let managerOptions = [CBCentralManagerScanOptionAllowDuplicatesKey:false,CBCentralManagerOptionShowPowerAlertKey:false]

Первый параметр означает, что за время поиска мне вернется только первый обнаруженный Advertising пакет от конкретного устройства, остальные будут игнорироваться. Второй параметр означает, что выключаю системный диалог с предупреждением о состоянии bluetooth. Состояние будет приходить, а вот диалог не отобразится.



Если устройство с указанным uuid сервисом было обнаружено, то метод didDiscoverPeripheral:



- (void)centralManager:(CBCentralManager *)centraldidDiscoverPeripheral:(CBPeripheral *)peripheraladvertisementData:(NSDictionary<NSString *, id> *)advertisementDataRSSI:(NSNumber *)RSSI;

вернет объект (CBPeripheral *)peripheral. Обязательно нужно сохранить на него ссылку.



var device:CBPeripheral = peripheral 


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



manager.connect(device, options: nil) 

Подключением и отключением управляет CBCentralManager. Отключиться можно командой:


 manager.cancelPeripheralConnection(cancelingDevice) 


После успешного подключения делаем запрос на сервисы:



device?.discoverServices(nil) 

В качестве параметра можем передать массив uuids сервисов, которые хотели бы прочитать. В данном случае стоит nil значит peripheral вернет все имеющиеся.


Чтение характеристик происходит схожим образом, только отправить запрос нужно индивидуального для каждого сервиса. Метод didDiscoverServices возвращает массив найденных сервисов и дальше, выбрав нужный делаем запрос на характеристики. Я не стал запрашивать для всех, т.к. у iPhone параллельно запущено еще несколько служебных, для понимания процесса это только запутает.



guard let services = device?.services else { return }let filterServices = services.filter { (service) -> Bool inservice.uuid == UUIDs.primaryServiceUUID}for service in filterServices {device?.discoverCharacteristics(nil, for: service)}

Найденные характеристики возвращает метод didDiscoverCharacteristicsFor service:, т.е. последовательно для каждого сервиса. Необходимо подписаться на характеристику c идентификатором transferCharacteristicUUID.



if (characteristic.uuid == UUIDs.transferCharacteristicUUID) {device?.setNotifyValue(true, for: characteristic)}

Теперь можно передавать данные между созданными профилями GATT клиент/сервер.



Клиент отправляет данные таким образом:



device?.writeValue(text.data(using: .utf8)!,for: transferCharacteristic,type: .withoutResponse)

Принимает методом делегата CBPeripheralDelegate:



- (void)peripheral:(CBPeripheral *)peripheraldidUpdateValueForCharacteristic:(CBCharacteristic *)characteristicerror:(nullable NSError *)error;

Подробнее можно посмотреть в коде репозитория, ссылка на проект в начале раздела.



MTU. Особенности передачи данных


Передавая данные между Central и Peripheral, важно понимать, что есть ограничение на одну порцию данных. За это отвечает параметр MTU. Перед отправкой нужно определить это значение. Central делает это так:

let mtu = device?.maximumWriteValueLength (for: .withoutResponse)

Если общий объем превышает MTU, то нужно разбить данные на соответствующие порции.
Для Peripheral MTU определяется так:

let mtu = connectedCentral?.maximumUpdateValueLength

Ссылку на подключенный Central нужно сохранить предварительно, когда делегат CBPeripheralManagerDelegate обрабатывает запрос подписи на характеристику.

- (void)peripheralManager:(CBPeripheralManager *)peripheralcentral:(CBCentral *)centraldidSubscribeToCharacteristic:(CBCharacteristic *)characteristic;

Передача данных между Central и Peripheral осуществляется без проверок, достаточно прогнать в цикле несколько раз запись в характеристику и все. Когда передаем наоборот от Peripheral к Central, то алгоритм гораздо хитрее. Вначале нужно разделить имеющиеся данные на порции, затем начать отправку. Причем функция updateValue может вернуть false.

func updateValue(_ value: Data,for characteristic: CBMutableCharacteristic,onSubscribedCentrals centrals: [CBCentral]?) -> Bool

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

- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral;

В приложении есть полный алгоритм передачи данных между Central и Peripheral. По нажатию кнопки Send long text отправляется строка чуть больше 4000 байт.

Что в итоге получилось


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

Теперь предлагаю пройтись по основным возможностям нашего приложения.



Поиск устройств


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

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

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


На практике мы убедились, что вызов метода:

manager.scanForPeripherals(withServices: [UUIDs.primaryServiceUUID],options: managerOptions)

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

Подключение к GM-Box


Если приложение только установлено, то после ввода идентификатора GM-Box, необходимо ввести свои учетные данные. С этого же экрана происходит переподключение, если данные были введены некорректно и проверка прошла с ошибкой.


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

Завершение сессии


В любой момент пользователь может завершить сессию. Стандартный способ вызвать диалог и подтвердить завершение.


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

Защита приложения


Функционал не требует защищать доступ к приложению в обязательном порядке. По желанию, пользователь может сам установить ПИН-код, либо выбрать вход используя биометрию. После 10-ти попыток неверно введенного значения ПИН-кода, учетные данные стираются.




Послесловие


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



Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru