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

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

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


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

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

Примечание

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 применяется во множестве разных ситуаций, где требуются беспроводные управление и передача данных. Например:

  • Передача аудиоданных в наушники или аудиосистему автомобиля
  • Коммуникация между периферийными устройствами и ПК
  • Передача данных между 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 работает в качестве сервера, а смартфон – в качестве клиента.

Примечание

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-функционал, можно почитать в его характеристиках.

Примечание

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

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

Для нашего теста мы воспользуемся бесплатным приложением 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. Четыре кнопки, обведенные красной рамкой на скриншоте ниже, позволяют:

  1. считывать текущее значение характеристики;
  2. записывать новое значение характеристики;
  3. включать/выключать свойство «Notify»;
  4. включать/выключать свойство «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);
}

См.также

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