Руководство по Bluetooth Low Energy (BLE) на ESP32

Основы ESP32: Bluetooth Low Energy (BLE)

Основы ESP32: Bluetooth Low Energy (BLE)

Bluetooth Low Energy (BLE) сегодня повсюду. Если вы включите сканер на своём телефоне и прогуляетесь по окрестностям, вы, без сомнения, найдёте десятки, если не сотни, BLE-устройств.

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

Основы Bluetooth Low Energy

Bluetooth Low Energy (BLE), иногда называемый «Bluetooth Smart», — это облегчённое подмножество классического Bluetooth, представленное как часть спецификации Bluetooth 4.0.

BLE предназначен для устройств, которые отправляют небольшие объёмы данных нечасто и работают от небольших батарей. Он использует тот же частотный диапазон, что и классический Bluetooth — диапазон 2,4 ГГц ISM (industrial, scientific, and medical), который не требует лицензии для использования. Диапазон ISM разделён на 40 каналов, и BLE-устройства переключаются между ними, чтобы избежать помех.

Для экономии энергии BLE имеет меньшую мощность передачи (от 0,01 до 10 мВт) по сравнению с классическим Bluetooth (до 100 мВт для класса 1 и 1 мВт для устройств класса 3). Данные передаются тем же способом (с использованием гауссовой частотной манипуляции — Gaussian Frequency Shift Keying), но скорость передачи данных ниже — максимум 1 Мбит/с по сравнению с максимумом 24 Мбит/с классического Bluetooth. BLE-устройства также могут переключаться между режимами ожидания и активным режимом гораздо быстрее, чем устройства с классическим Bluetooth, что позволяет экономить ещё больше энергии.

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

Bluetooth Low Energy

Профили Bluetooth

Профили Bluetooth — это дополнительные протоколы, которые строятся поверх базового стандарта Bluetooth. Они помогают определить тип данных, которые передаёт модуль Bluetooth. В то время как спецификации Bluetooth определяют, как работает технология, профили определяют, как она используется.

Профили, которые поддерживает устройство Bluetooth, определяют приложения, для которых оно предназначено. Например, Bluetooth-гарнитура для режима «свободные руки» использует профиль гарнитуры (HSP), тогда как беспроводная клавиатура использует профиль устройства с человеко-машинным интерфейсом (HID). Эти профили разрабатываются либо Bluetooth SIG (Bluetooth Special Interest Group), либо разработчиками периферийных устройств. Полный список официально принятых профилей можно посмотреть здесь.

Профили, с которыми вам следует ознакомиться, — это GAP и GATT, поскольку все стандартные профили BLE основаны на них. Итак, давайте познакомимся с ними поближе.

GAP (Generic Access Profile)

GAP управляет подключениями и рекламными объявлениями (advertising) в Bluetooth. GAP делает ваше устройство видимым для внешнего мира и определяет, как два устройства обнаруживают друг друга и устанавливают соединение.

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

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

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

Роли Generic Access Profile

Периферийное устройство передаёт рекламные пакеты (advertising packets) через заданные интервалы, чтобы информировать ближайшие центральные устройства о своём присутствии. Как только соединение между периферийным и центральным устройством установлено, процесс рекламирования прекращается, и в игру вступает GATT, обеспечивая двунаправленный обмен данными.

GATT (Generic ATTribute Profile)

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

Данные организованы иерархически в разделы, называемые сервисами (services), которые группируют концептуально связанные фрагменты пользовательских данных, называемые характеристиками (characteristics), как показано на иллюстрации ниже:

Generic ATTribute Profile

Сервисы (Services)

Сервис GATT — это набор концептуально связанных данных, называемых характеристиками. Каждый сервис может иметь одну или несколько характеристик и имеет собственный уникальный числовой идентификатор — UUID, который может быть 16-битным (для официально принятых BLE-сервисов) или 128-битным (для пользовательских сервисов). Подробнее об этом далее.

Например, рассмотрим сервис Heart Rate Service. Этот официально принятый сервис имеет 16-битный UUID 0x180D и содержит до 3 характеристик: Heart Rate Measurement, Body Sensor Location и Heart Rate Control Point. Больше сервисов, определённых Bluetooth SIG, можно найти здесь.

Характеристики (Characteristics)

Характеристика GATT — это группа информации, называемая атрибутами (Attributes). Атрибуты — это фактическая информация, передаваемая между BLE-устройствами. Типичная характеристика состоит из следующих атрибутов:

Характеристика GATT
  • Значение (Value): это фактические данные, хранящиеся в характеристике. Значение может быть любым типом данных, например числом, строкой или массивом байтов.

  • Дескриптор (Descriptor): предоставляет дополнительную информацию или параметры конфигурации для характеристики.

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

  • Дескриптор (Handle): 16-битное число, используемое для доступа к характеристике на серверном устройстве.

  • UUID: 128-битный универсально уникальный идентификатор, указывающий, что представляет собой характеристика.

  • Разрешения (Permissions): определяют, какие операции над характеристикой разрешены, например чтение, запись или уведомление.

UUID (Universally Unique Identifier)

UUID обеспечивают уникальную идентификацию сервисов и характеристик. Они играют важную роль в определении и доступе к данным на BLE-устройстве. В BLE существует два типа UUID:

  • 16-битный UUID: используется для официальных профилей, сервисов и характеристик BLE. Они стандартизированы Bluetooth SIG. Например, «Heart Rate Service» имеет стандартизированный 16-битный UUID 0x180D, а характеристика «Heart Rate Measurement» в рамках Heart Rate Service использует UUID 0x2A37.

  • 128-битный UUID: используется для пользовательских (vendor-specific) сервисов и характеристик. Если компания разрабатывает собственный сервис, не охваченный официальными BLE-сервисами, она использует уникальный 128-битный UUID. 128-битный UUID выглядит так: 4fafc201-1fb5-459e-8fcc-c5c9c331914b.

GATT-сервер и GATT-клиент

С точки зрения GATT, когда два устройства соединены, каждое из них находится в одной из двух ролей.

  • GATT-сервер — это устройство, которое содержит базу данных характеристик.

  • GATT-клиент — это устройство, которое считывает данные с GATT-сервера или записывает данные на него.

На следующем рисунке показана эта взаимосвязь в примере BLE-соединения, где периферийное устройство (ESP32) является GATT-сервером, а центральное устройство (смартфон) — GATT-клиентом.

GATT-сервер и GATT-клиент

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

Использование BLE на ESP32

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

Для ESP32 в библиотеке ESP32 BLE доступно несколько примеров скетчей. Эта библиотека устанавливается автоматически при установке ядра ESP32 в Arduino IDE.

Чтобы получить доступ к примерам скетчей, перейдите в File > Examples > ESP32 BLE Arduino.

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

Этот пример настраивает ESP32 как BLE-сервер с определённым сервисом и характеристикой. Затем он рекламирует этот сервис, делая его обнаруживаемым и доступным для BLE-клиентов, таких как ваш смартфон.

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/

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

void setup() {
  Serial.begin(115200);
  Serial.println("Starting BLE work!");

  BLEDevice::init("MyESP32");  // set the device name
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setValue("Hello World!");
  pService->start();
  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
}

Разбор кода

Давайте подробно разберём код BLE_server. Он начинается с подключения необходимых библиотек для работы с BLE на ESP32.

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

Далее определяются UUID для сервиса и характеристики. UUID (Universally Unique Identifiers) уникально идентифицируют сервисы и характеристики в BLE.

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

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

В функции setup() инициализируется последовательная связь со скоростью 115200 бод.

Serial.begin(115200);

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

// Create the BLE Device
BLEDevice::init("MyESP32");

После этого наше BLE-устройство настраивается как сервер.

BLEServer *pServer = BLEDevice::createServer();

Далее создаётся новый сервис для нашего сервера с ранее определённым UUID.

BLEService *pService = pServer->createService(SERVICE_UUID);

Затем для сервиса создаётся новая характеристика. Как вы можете видеть, свойства характеристики, в данном случае READ и WRITE, передаются в качестве аргументов вместе с CHARACTERISTIC_UUID.

BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                     CHARACTERISTIC_UUID,
                                     BLECharacteristic::PROPERTY_READ |
                                     BLECharacteristic::PROPERTY_WRITE
                                     );

После создания характеристики используйте метод setValue() для установки её значения. В данном случае значение установлено как текст «Hello World!». Вы можете изменить этот текст на что угодно; это могут быть показания датчика, состояние кнопки или что-либо ещё.

pCharacteristic->setValue("Hello World!");

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

pService->start();

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

BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();

Это простой пример настройки BLE-сервера, поэтому в loop() ничего не выполняется. Однако в зависимости от приложения вы можете добавить действия при подключении нового клиента (обратитесь к примеру BLE_notify для руководства).

void loop() {
  delay(2000);
}

Использование nRF Connect для тестирования

Для тестирования BLE-соединения вам нужно сопрячь ESP32 с вашим смартфоном. Вам также потребуется приложение для отладки Bluetooth, установленное на нём. Есть несколько доступных вариантов; одно из наших любимых — Nordic nRF Connect, которое доступно как для iOS, так и для Android-устройств. Это мощный инструмент, который позволяет сканировать и исследовать ваши BLE-устройства и обмениваться с ними данными.

  1. Перейдите в Google Play Store или Apple App Store и найдите «nRF Connect for Mobile». Установите приложение и откройте его.

Приложение nRF Connect for Mobile
  1. Убедитесь, что Bluetooth на вашем телефоне включён.

Включите Bluetooth на телефоне
  1. В приложении нажмите на кнопку «SCAN». Приложение начнёт сканирование ближайших BLE-устройств.

Сканирование ближайших BLE-устройств
  1. Появится список доступных устройств с их уровнями сигнала и другими деталями. Найдите «MyESP32» и нажмите кнопку «Connect» рядом с ним.

Подключение к ESP32 BLE
  1. Вы перейдёте в представление «Services». Там вы увидите список доступных сервисов. Нажмите «Unknown Service» — строка UUID должна совпадать с SERVICE_UUID в вашем примере кода.

Поиск SERVICE UUID
  1. Нажмите на сервис, чтобы увидеть связанные с ним характеристики. Рядом с каждой характеристикой будут два значка. Стрелка вниз позволяет прочитать характеристику, а стрелка вверх — записать в неё.

Нажмите стрелку вниз для чтения BLE-характеристики
  1. Нажмите на стрелку вниз, чтобы прочитать характеристику. Вы увидите UUID характеристики, свойства READ и WRITE, а также значение «Hello World!» — точно так, как указано в нашем коде.

Чтение значения BLE-характеристики