ESP32:Примеры/Bluetooth Low Energy: уведомления

Материал из Онлайн справочника
Перейти к навигации Перейти к поиску

Перевод: Максим Кузьмин (Cubewriter) Перевел 364226 статей для сайта.</br>Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Pixel Art Mini Meow Animated.gif Черновик


Bluetooth Low Energy: уведомления

Рассмотрим скетч-пример, в котором ESP32-сервер после подключения клиента каждые 2 секунды отправляет ему уведомляющее сообщение. Это скетч-пример из ESP32-аддона для IDE Arduino.

Esp32 BLE notify 1.png

Примечание: ESP32 можно настроить так, чтобы она была и сервером, и клиентом.

Справочная информация

Bluetooth Low Energy (BLE) в ESP32

В этом Разделе мы изучим, что такое BLE (означает «Bluetooth low energy», что можно перевести как «Bluetooth с пониженным энергопотреблением») и как использовать его на практике. Чип ESP32 оснащен не только WiFi-компонентами, но и компонентами для передачи данных по Bluetooth и Bluetooth Low Energy (BLE).

В этом Разделе мы рассмотрим следующее:

  • Основы Bluetooth Low Energy (BLE)
  • Терминология BLE: UUID, сервис, характеристика и свойства
  • Взаимодействие между сервером и клиентом

Что такое Bluetooth?

Bluetooth – это беспроводной стандарт связи для обмена данными на коротком расстоянии. Как и WiFi, Bluetooth работает на частоте 2.4 ГГц.

Bluetooth communication.png

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

  • Передача аудиоданных в наушники или аудиосистему автомобиля
  • Коммуникация между периферийными устройствами и ПК
  • Передача данных между Bluetooth-устройствами

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

Что такое Bluetooth Low Energy (BLE)?

Bluetooth Low Energy (BLE) – это энергосберегающий вариант Bluetooth, и его главная область применения – это передача маленьких порций данных на короткие расстояния. Этот стандарт предназначен для очень маломощных проектов, питаемых от батареек-таблеток.

Ble communication.png

В отличие от Bluetooth, который включен постоянно, BLE-устройство постоянно находится в спящем режиме, кроме ситуаций, когда оно подключено к другим устройствам. Благодаря этому BLE-устройства потребляют очень мало питания. Эта функция крайне полезна для коммуникации типа M2M (англ. «machine-to-machine», т.е. «между машинами»), т.к. позволяет делать проекты из маленьких устройств, питаемых от батареек и работающих очень долгое время.

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

О других отличиях между Bluetooth и BLE можно почитать в этой статье или просто ознакомиться с ними в таблице ниже:

Bluetooth vs ble.png

Сервер и клиент Bluetooth

В стандарте Bluetooth Low Energy предусмотрено два типа устройств: сервер и клиент. В нашем примере ESP32 работает в качестве сервера, а смартфон – в качестве клиента.

Примечание: ESP32 (как и смартфон) может быть и сервером, и клиентом.

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

Ble server-client.png

Стандарт BLE предусматривает и другие типы подключения:

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

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

GATT

GATT расшифровывается как «generic attributes» («общие атрибуты»). Эта спецификация определяет иерархию данных, которую BLE-устройство демонстрирует другим BLE-устройствам, подключенным к нему. Другими словами, GATT определяет то, как два BLE-устройства отправляют и получают стандартные сообщения.

GATT hierarchy.png

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

Характеристики всегда находятся внутри сервисов, и это то место, где, собственно, хранятся данные во всей этой GATT-иерархии. Характеристика всегда содержит два атрибута: объявление характеристики (содержит метаданные о данных) и значение характеристики.

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

UUID

Каждый сервис, характеристика и дескриптор имеют собственный UUID (англ. «universally unique identifier», что переводится как «универсальный уникальный идентификатор»). UUID – это уникальное 128-битное (16-байтное) число вроде такого:

55072829-bc9e-4c53-938a-74a6d4c78776

Сокращенные UUID для всех сервисов, характеристик, профилей и т.д. можно найти на сайте Bluetooth SIG. К примеру, в неполной таблице ниже представлены сокращенные UUID для Bluetooth-сервисов. Полностью эту таблицу можно найти по этой ссылке.

Service UUID.png

Но если вашему приложению нужен собственный UUID, его можно сгенерировать при помощи этого UUID-генератора.

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

Сервис «Battery Service»

Для примера давайте рассмотрим сервис «Battery Service». Он используется в большинстве питаемых от батареи BLE-устройств с целью определения текущего уровня заряда батареи (в процентах). Если BLE-устройство было сделано как следует, оно должно использовать стандартизированный сервис под названием «Battery Service».

Bluetooth service characteristics battery level 1.PNG

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

Характеристика «Battery Level»

О характеристиках сервиса «Battery Service» можно узнать по этой ссылке. Нам же понадобится характеристика «Battery Level», возвращающая текущий уровень заряда батареи в диапазоне от 0 до 100 процентов.

Итак, если у вас есть BLE-устройство с батареей (то есть сервер с сервисом «Battery Service», рассылающий оповещения с характеристикой «Battery Level»), то клиент (то есть ваш смартфон, подключенный к этому BLE-устройству) может видеть уровень заряда батареи этого BLE-устройства, т.к. это широко используемый сервис со стандартной характеристикой.

Свойства характеристики «Battery Level»

Как мы уже говорили ранее, у одного сервиса может быть одна или несколько характеристик. Характеристика – это данные, передаваемые между клиентом и сервером. Характеристика может иметь разные свойства. К примеру, характеристика «Battery Level» предназначена только для чтения, и это обязательное требование для ее работы.

Вы также можете, если хотите, активировать свойство «Notify» (оно опционально), чтобы BLE-устройства, к примеру, сообщали об уровне заряда своей батареи каждые X секунд.

Bluetooth service characteristics battery level 2.PNG

В разных характеристиках могут быть разные свойства.

Структура Bluetooth-устройства

На рисунке ниже показано Bluetooth-устройство с сервисом «Battery Service», содержащим характеристику «Battery Level».

Bluetooth device overview.png

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

Более подробно о стандартных сервисах можно почитать на этой странице. Вы также можете создавать новые сервисы, которых в этом списке нет.

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

Настоятельно рекомендуем почитать эту статью о BLE на «Википедии» – там о BLE рассказано более подробно.

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

Как работает этот код

Теперь давайте рассмотрим этот код более подробно.

Он начинается с подключения библиотек, необходимых для работы с BLE.

1 #include <BLEDevice.h>
2 #include <BLEUtils.h>
3 #include <BLEScan.h>
4 #include <BLEAdvertisedDevice.h>

Следующая строчка создает указатель на «BLECharacteristic».

BLECharacteristic *pCharacteristic;

Далее создаем булеву переменную для хранения информации о том, подключен ли клиент к серверу или нет.

bool deviceConnected = false;

Переменная «value» будет использоваться для хранения значения характеристики.

1 uint8_t value = 0;

Затем нам нужно задать UUID для новых сервиса и характеристики.

1 #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
2 #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"

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

setup()

Пролистайте вниз до блока setup().

Здесь мы начинаем последовательную коммуникацию на скорости 115200 бод.

1 Serial.begin(115200);

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

1 // создаем BLE-устройство:
2 BLEDevice::init("MyESP32");

В следующей строчке делаем это BLE-устройство сервером.

BLEServer *pServer = BLEDevice::createServer();

Привязываем к этому серверу две функции обратного вызова.

pServer->setCallbacks(new MyServerCallbacks());

Первая функция при успешном подключении клиента будет менять значение переменной «deviceConnected» на «true», а вторая – при отключении клиента менять его на «false».

1 class MyServerCallbacks: public BLEServerCallbacks {
2 void onConnect(BLEServer* pServer) {
3  deviceConnected = true;
4 };
5 void onDisconnect(BLEServer* pServer) {
6  deviceConnected = false;
7 }
8 };

После этого создаем BLE-сервис для сервера с заданным выше UUID.

1 // создаем BLE-сервис:
2 BLEService *pService = pServer->createService(SERVICE_UUID);

Затем создаем характеристику для этого сервиса. Как видите, мы используем для этого заданный выше UUID, а также указываем в параметрах свойства, которыми обладает этот сервис. В данном случае это: READ, WRITE, NOTIFY и INDICATE.

1 // создаем BLE-характеристику:
2 pCharacteristic = pService->createCharacteristic(
3  CHARACTERISTIC_UUID,
4  BLECharacteristic::PROPERTY_READ |
5  BLECharacteristic::PROPERTY_WRITE |
6  BLECharacteristic::PROPERTY_NOTIFY |
7  BLECharacteristic::PROPERTY_INDICATE
8  );

Также создаем BLE-дескриптор для этой характеристики:

1 // создаем BLE-дескриптор:
2 pCharacteristic->addDescriptor(new BLE2902());

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

1 // запускаем сервис:
2 pService->start();
3 // начинаем рассылку оповещений:
4 pServer->getAdvertising()->start();

loop()

В блоке loop() проверяем, подключено ли устройство или нет. Если устройство подключено:

  • Оно напечатает в мониторе порта текущее значение в переменной «value»
  • Оно задаст новое значение характеристики
  • Оно отправит уведомление подключенному клиенту
  • Наконец, оно увеличит значение в переменной «value» на «1»
1 if (deviceConnected) {
2  Serial.printf("*** NOTIFY: %d ***\n", value);
3  pCharacteristic->setValue(&value, 1);
4  pCharacteristic->notify();
5  //pCharacteristic->indicate();
6  value++;
7 }

Этот процесс будет повторяться каждые 2 секунды. То есть при первом прохождении цикла loop() в переменной «value» будет задано значение «1», во втором – «2», в третьем – «3» и т.д.

delay(2000);

Тестируем скетч-пример «BLE_notify»

Убедитесь, что в IDE Arduino выбраны правильные плата и COM-порт, а потом загрузите код на ESP32. После этого откройте монитор порта на скорости 115200 бод.

Оставляем окно монитора порта открытым...

Подготавливаем смартфон

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

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

Итак, возьмите свой смартфон...

Для нашего теста мы воспользуемся бесплатным приложением nRF Connect for Mobile от Nordic – оно есть и для Android (Google Play Store), и для iOS (App Store). Зайдите в один из этих магазинов приложений и вбейте в поиске «nRF Connect for Mobile». Установите приложение и откройте его.

NRf connect for mobile 1.jpg

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

Настроив BLE в смартфоне и запустив на ESP32 скетч «BLE_notify», откройте приложение nRF Connect for Mobile и нажмите на кнопку сканирования ближайших устройств. Поиск должен показать ESP32 с названием «MyESP32».

NRf connect for mobile 2.jpg

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

NRf connect for mobile 3.jpg

У этой характеристики должно быть 4 свойства: INDICATE, NOTIFY, READ и WRITE. Четыре кнопки, обведенные красной рамкой на скриншоте ниже, позволяют:

  1. считывать текущее значение характеристики;
  2. записывать новое значение характеристики;
  3. включать/выключать свойство «Notify»;
  4. включать/выключать свойство «Indicate».
NRf connect for mobile 4.jpg

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

Кроме того, в приложении будет показано сообщение, которое ESP32 печатает в мониторе порта. Это значит, что все работает как надо.

Примечание: Значение, получаемое приложением, имеет шестнадцатеричный формат.

Ble esp32 hexadecimal values.png

Теперь можно выключить Bluetooth в смартфоне и закрыть смартфонное приложение.

Необходимое оборудование

  • Плата ESP32 - 1шт.;

Схема

Для данного примера нужна только плата.

Esp32 board exm 1.png

Код

 1 /*
 2     Видео: https://www.youtube.com/watch?v=oCMOYS71NIU
 3     Основан на скетче-примере Нила Колбана для IDF:
 4     https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
 5     Порт в ESP32-аддон для IDE Arduino - Эвандро Коперчини
 6    
 7     Создаем BLE-сервер, который после подключения клиента
 8     начнет отправлять ему периодические уведомляющие сообщения.
 9  
10     Сервис оповещает о себе при помощи следующего UUID:
11     4fafc201-1fb5-459e-8fcc-c5c9c331914b
12     UUID характеристики: beb5483e-36e1-4688-b7f5-ea07361b26a8
13 
14     Шаги создания BLE-сервера таковы:
15      1.	Создание BLE-сервера
16      2.	Создание BLE-сервиса
17      3.	Создание BLE-характеристики в BLE-сервисе
18      4.	Создание BLE-дескриптора в BLE-характеристике
19      5.	Запуск сервера
20      6.	Запуск рассылки оповещений (advertising)
21     
22     В результате устройство, выступающее сервером, 
23     запустит фоновую задачу по рассылке уведомляющих сообщений
24     каждые несколько секунд.
25 */
26 
27 #include <BLEDevice.h>
28 #include <BLEServer.h>
29 #include <BLEUtils.h>
30 #include <BLE2902.h>
31 
32 BLECharacteristic *pCharacteristic;
33 bool deviceConnected = false;
34 uint8_t value = 0;
35 
36 // Сайт для генерирования UUID:
37 // https://www.uuidgenerator.net/
38 
39 #define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
40 #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
41 
42 
43 class MyServerCallbacks: public BLEServerCallbacks {
44     void onConnect(BLEServer* pServer) {
45       deviceConnected = true;
46     };
47 
48     void onDisconnect(BLEServer* pServer) {
49       deviceConnected = false;
50     }
51 };
52 
53 
54 
55 void setup() {
56   Serial.begin(115200);
57 
58   // создаем BLE-устройство:
59   BLEDevice::init("MyESP32");
60 
61   // Создаем BLE-сервер:
62   BLEServer *pServer = BLEDevice::createServer();
63   pServer->setCallbacks(new MyServerCallbacks());
64 
65   // Создаем BLE-сервис:
66   BLEService *pService = pServer->createService(SERVICE_UUID);
67 
68   // Создаем BLE-характеристику:
69   pCharacteristic = pService->createCharacteristic(
70                       CHARACTERISTIC_UUID,
71                       BLECharacteristic::PROPERTY_READ   |
72                       BLECharacteristic::PROPERTY_WRITE  |
73                       BLECharacteristic::PROPERTY_NOTIFY |
74                       BLECharacteristic::PROPERTY_INDICATE
75                     );
76 
77   // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
78   // создаем BLE-дескриптор:
79   pCharacteristic->addDescriptor(new BLE2902());
80 
81   // запускаем сервис:
82   pService->start();
83 
84   // запускаем оповещения (advertising):
85   pServer->getAdvertising()->start();
86   Serial.println("Waiting a client connection to notify...");  //  "Ждем подключения клиента, чтобы отправить ему уведомление..."
87 }
88 
89 void loop() {
90 
91   if (deviceConnected) {
92     Serial.printf("*** NOTIFY: %d ***\n", value);
93     pCharacteristic->setValue(&value, 1);
94     pCharacteristic->notify();
95     //pCharacteristic->indicate();
96     value++;
97   }
98   delay(2000);
99 }

См.также

Внешние ссылки