ESP32:Примеры/Bluetooth Low Energy: уведомления
Bluetooth Low Energy: уведомления
Рассмотрим скетч-пример, в котором ESP32-сервер после подключения клиента каждые 2 секунды отправляет ему уведомляющее сообщение. Это скетч-пример из ESP32-аддона для IDE Arduino.
Справочная информация
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 применяется во множестве разных ситуаций, где требуются беспроводные управление и передача данных. Например:
- Передача аудиоданных в наушники или аудиосистему автомобиля
- Коммуникация между периферийными устройствами и ПК
- Передача данных между Bluetooth-устройствами
Другими словами, Bluetooth используется в ситуациях, когда для передачи данных между устройствами требуется непрерывное сквозное («точка-точка») подключение.
Что такое Bluetooth Low Energy (BLE)?
Bluetooth Low Energy (BLE) – это энергосберегающий вариант Bluetooth, и его главная область применения – это передача маленьких порций данных на короткие расстояния. Этот стандарт предназначен для очень маломощных проектов, питаемых от батареек-таблеток.
В отличие от Bluetooth, который включен постоянно, BLE-устройство постоянно находится в спящем режиме, кроме ситуаций, когда оно подключено к другим устройствам. Благодаря этому BLE-устройства потребляют очень мало питания. Эта функция крайне полезна для коммуникации типа M2M (англ. «machine-to-machine», т.е. «между машинами»), т.к. позволяет делать проекты из маленьких устройств, питаемых от батареек и работающих очень долгое время.
Благодаря этому стандарт BLE идеален для проектов, где требуется периодический обмен небольшими порциями данных – например, в медицине, фитнесе, отслеживании объектов, навигации, безопасности и домашней автоматизации.
О других отличиях между Bluetooth и BLE можно почитать в этой статье или просто ознакомиться с ними в таблице ниже:
Сервер и клиент Bluetooth
В стандарте Bluetooth Low Energy предусмотрено два типа устройств: сервер и клиент. В нашем примере ESP32 работает в качестве сервера, а смартфон – в качестве клиента.
Сервер оповещает о своем существовании, чтобы его могли найти другие устройства, а также содержит данные для считывания клиентом. Клиент сканирует близлежащие устройства и, найдя искомый сервер, подключается к нему и начинает прослушивать входящие данные. Этот тип коммуникации называют «сквозным» или «точка-точка».
Стандарт BLE предусматривает и другие типы подключения:
- Режим вещания. Сервер передает данные нескольким клиентам, подключенным к нему.
- Ячеистая сеть. Все устройства подключены друг к другу. Это тип соединения, когда в сети несколько устройств, и у каждого из них по несколько подключений.
Теперь давайте рассмотрим несколько важных терминов, касающихся BLE.
GATT
GATT расшифровывается как «generic attributes» («общие атрибуты»). Эта спецификация определяет иерархию данных, которую BLE-устройство демонстрирует другим BLE-устройствам, подключенным к нему. Другими словами, GATT определяет то, как два BLE-устройства отправляют и получают стандартные сообщения.
На верхнем уровне этой иерархии – профиль, состоящий из одного или более сервисов. Каждый сервис содержит как минимум одну характеристику, а также может отсылать к другим сервисам.
Характеристики всегда находятся внутри сервисов, и это то место, где, собственно, хранятся данные во всей этой GATT-иерархии. Характеристика всегда содержит два атрибута: объявление характеристики (содержит метаданные о данных) и значение характеристики.
Кроме того, после значения характеристики могут идти дескрипторы, которые дополняют метаданные, содержащиеся в объявлении характеристики.
UUID
Каждый сервис, характеристика и дескриптор имеют собственный UUID (англ. «universally unique identifier», что переводится как «универсальный уникальный идентификатор»). UUID – это уникальное 128-битное (16-байтное) число вроде такого:
55072829-bc9e-4c53-938a-74a6d4c78776
Сокращенные UUID для всех сервисов, характеристик, профилей и т.д. можно найти на сайте Bluetooth SIG. К примеру, в неполной таблице ниже представлены сокращенные UUID для Bluetooth-сервисов. Полностью эту таблицу можно найти по этой ссылке.
Но если вашему приложению нужен собственный UUID, его можно сгенерировать при помощи этого UUID-генератора.
Итак, если вкратце, в UUID хранится уникальная идентифицирующая информация. С его помощью, к примеру, можно идентифицировать какой-либо сервис, предлагаемый Bluetooth-устройством.
Сервис «Battery Service»
Для примера давайте рассмотрим сервис «Battery Service». Он используется в большинстве питаемых от батареи BLE-устройств с целью определения текущего уровня заряда батареи (в процентах). Если BLE-устройство было сделано как следует, оно должно использовать стандартизированный сервис под названием «Battery Service».
Этот сервис позволяет приложениям, которые подключены к 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-устройства
На рисунке ниже показано Bluetooth-устройство с сервисом «Battery Service», содержащим характеристику «Battery Level».
Понимать эту иерархию очень важно, т.к. это упростит понимание того, как использовать BLE и создавать собственные проекты, использующие эту функцию.
Более подробно о стандартных сервисах можно почитать на этой странице. Вы также можете создавать новые сервисы, которых в этом списке нет.
Это лишь базовая теория о BLE-устройствах, взаимодействии между клиентом и сервером, а также о некоторых стандартах, о которых нужно знать, чтобы работать с Bluetooth Low Energy. Стоит отметить, что мы затронули тему BLE поверхностно, и впереди нас ждет еще много интересного. Мы лишь рассмотрели несколько функций, имеющих отношение к нашим проектам.
Настоятельно рекомендуем почитать эту статью о BLE на «Википедии» – там о BLE рассказано более подробно.
Также советуем добавить в закладки сайт bluetooth.com, т.к. он содержит информацию о стандартных сервисах и UUID, которые вы будете использовать в BLE-устройствах и приложениях.
Как работает этот код
Теперь давайте рассмотрим этот код более подробно.
Он начинается с подключения библиотек, необходимых для работы с BLE.
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
Следующая строчка создает указатель на «BLECharacteristic».
BLECharacteristic *pCharacteristic;
Далее создаем булеву переменную для хранения информации о том, подключен ли клиент к серверу или нет.
bool deviceConnected = false;
Переменная «value» будет использоваться для хранения значения характеристики.
uint8_t value = 0;
Затем нам нужно задать UUID для новых сервиса и характеристики.
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
Вы можете оставить здесь старые UUID или воспользоваться UUID-генератором uuidgenerator.net, который позволяет создавать случайные UUID для своих сервисов и характеристик.
setup()
Пролистайте вниз до блока setup().
Здесь мы начинаем последовательную коммуникацию на скорости 115200 бод.
Serial.begin(115200);
Затем создаем BLE-устройство под названием «MyESP32». Вы, впрочем, если хотите, можете задать ему любое другое название.
// создаем BLE-устройство:
BLEDevice::init("MyESP32");
В следующей строчке делаем это BLE-устройство сервером.
BLEServer *pServer = BLEDevice::createServer();
Привязываем к этому серверу две функции обратного вызова.
pServer->setCallbacks(new MyServerCallbacks());
Первая функция при успешном подключении клиента будет менять значение переменной «deviceConnected» на «true», а вторая – при отключении клиента менять его на «false».
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
После этого создаем BLE-сервис для сервера с заданным выше UUID.
// создаем BLE-сервис:
BLEService *pService = pServer->createService(SERVICE_UUID);
Затем создаем характеристику для этого сервиса. Как видите, мы используем для этого заданный выше UUID, а также указываем в параметрах свойства, которыми обладает этот сервис. В данном случае это: READ, WRITE, NOTIFY и INDICATE.
// создаем BLE-характеристику:
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
Также создаем BLE-дескриптор для этой характеристики:
// создаем BLE-дескриптор:
pCharacteristic->addDescriptor(new BLE2902());
Наконец, запускаем сервис и делаем так, чтобы он начал рассылать оповещения, чтобы другие BLE-устройства могли его просканировать и найти.
// запускаем сервис:
pService->start();
// начинаем рассылку оповещений:
pServer->getAdvertising()->start();
loop()
В блоке loop() проверяем, подключено ли устройство или нет. Если устройство подключено:
- Оно напечатает в мониторе порта текущее значение в переменной «value»
- Оно задаст новое значение характеристики
- Оно отправит уведомление подключенному клиенту
- Наконец, оно увеличит значение в переменной «value» на «1»
if (deviceConnected) {
Serial.printf("*** NOTIFY: %d ***\n", value);
pCharacteristic->setValue(&value, 1);
pCharacteristic->notify();
//pCharacteristic->indicate();
value++;
}
Этот процесс будет повторяться каждые 2 секунды. То есть при первом прохождении цикла loop() в переменной «value» будет задано значение «1», во втором – «2», в третьем – «3» и т.д.
delay(2000);
Тестируем скетч-пример «BLE_notify»
Убедитесь, что в IDE Arduino выбраны правильные плата и COM-порт, а потом загрузите код на ESP32. После этого откройте монитор порта на скорости 115200 бод.
Оставляем окно монитора порта открытым...
Подготавливаем смартфон
BLE-функционал есть у большинства современных смартфонов. Для нашего проекта должен подойти любой современный смартфон. О том, есть ли в вашем смартфоне BLE-функционал, можно почитать в его характеристиках.
Итак, возьмите свой смартфон...
Для нашего теста мы воспользуемся бесплатным приложением nRF Connect for Mobile от Nordic – оно есть и для Android (Google Play Store), и для iOS (App Store). Зайдите в один из этих магазинов приложений и вбейте в поиске «nRF Connect for Mobile». Установите приложение и откройте его.
Не забудьте зайти в настройки Bluetooth в своем смартфоне и включить Bluetooth. Заодно можно включить видимость для других устройств, что пригодится при тестировании других скетчей.
Настроив BLE в смартфоне и запустив на ESP32 скетч «BLE_notify», откройте приложение nRF Connect for Mobile и нажмите на кнопку сканирования ближайших устройств. Поиск должен показать ESP32 с названием «MyESP32».
Кликните по кнопке «Connect». Как видно на скриншоте ниже, у ESP32 есть сервис с UUID, который мы задали ранее. Если вы нажмете на этот сервис, раскроется его меню, в котором будет информация о характеристике, чей UUID мы тоже задали ранее.
У этой характеристики должно быть 4 свойства: INDICATE, NOTIFY, READ и WRITE. Четыре кнопки, обведенные красной рамкой на скриншоте ниже, позволяют:
- считывать текущее значение характеристики;
- записывать новое значение характеристики;
- включать/выключать свойство «Notify»;
- включать/выключать свойство «Indicate».
В этом примере мы оставим свойство «Notify» включенным, в результате чего смартфон будет получать новое значение характеристики каждые 2 секунды.
Кроме того, в приложении будет показано сообщение, которое ESP32 печатает в мониторе порта. Это значит, что все работает как надо.
Теперь можно выключить Bluetooth в смартфоне и закрыть смартфонное приложение.
Необходимое оборудование
- Плата ESP32 - 1шт.;
Схема
Для данного примера нужна только плата.
Код
/*
Видео: https://www.youtube.com/watch?v=oCMOYS71NIU
Основан на скетче-примере Нила Колбана для IDF:
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Порт в ESP32-аддон для IDE Arduino - Эвандро Коперчини
Создаем BLE-сервер, который после подключения клиента
начнет отправлять ему периодические уведомляющие сообщения.
Сервис оповещает о себе при помощи следующего UUID:
4fafc201-1fb5-459e-8fcc-c5c9c331914b
UUID характеристики: beb5483e-36e1-4688-b7f5-ea07361b26a8
Шаги создания BLE-сервера таковы:
1. Создание BLE-сервера
2. Создание BLE-сервиса
3. Создание BLE-характеристики в BLE-сервисе
4. Создание BLE-дескриптора в BLE-характеристике
5. Запуск сервера
6. Запуск рассылки оповещений (advertising)
В результате устройство, выступающее сервером,
запустит фоновую задачу по рассылке уведомляющих сообщений
каждые несколько секунд.
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
uint8_t value = 0;
// Сайт для генерирования UUID:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
void setup() {
Serial.begin(115200);
// создаем BLE-устройство:
BLEDevice::init("MyESP32");
// Создаем BLE-сервер:
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Создаем BLE-сервис:
BLEService *pService = pServer->createService(SERVICE_UUID);
// Создаем BLE-характеристику:
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// создаем BLE-дескриптор:
pCharacteristic->addDescriptor(new BLE2902());
// запускаем сервис:
pService->start();
// запускаем оповещения (advertising):
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify..."); // "Ждем подключения клиента, чтобы отправить ему уведомление..."
}
void loop() {
if (deviceConnected) {
Serial.printf("*** NOTIFY: %d ***\n", value);
pCharacteristic->setValue(&value, 1);
pCharacteristic->notify();
//pCharacteristic->indicate();
value++;
}
delay(2000);
}
См.также
Внешние ссылки