Bluetooth® Low Energy с Arduino
Узнайте, как работает Bluetooth® Low Energy, и научитесь создавать BLE-проекты на Arduino с помощью библиотеки ArduinoBLE.
Введение
Bluetooth® Low Energy (сокращённо Bluetooth® LE или BLE) — это беспроводная технология для обмена данными на коротких расстояниях между электронными устройствами. Она появилась как ответ на запрос рынка: нужна была связь, которая почти не расходует энергию, — особенно в устройствах, где каждый миллиампер на счету.
В отличие от классического Bluetooth®, рассчитанного на непрерывную передачу данных с относительно высокой скоростью, Bluetooth® LE жертвует скоростью ради экономии энергии. Именно поэтому его используют в фитнес-браслетах, медицинских датчиках, умных сенсорах и устройствах IoT.
Цель этой статьи — объяснить базовые концепции Bluetooth® Low Energy и показать, как применять библиотеку ArduinoBLE для создания BLE-проектов на совместимых платах Arduino.
Поддерживаемые платы
Bluetooth® LE работает на следующих платах Arduino:
- MKR WiFi 1010
- UNO R4 WiFi
- Nano 33 BLE
- Nano 33 BLE Sense
- Nano 33 BLE Sense Rev2
- Nano 33 IoT
- Nano ESP32
- Nano RP2040 Connect
- Portenta H7
- Portenta H7 Lite Connected
- Portenta Machine Control
- Nicla Voice
- Opta WiFi
- GIGA R1 WiFi
Чтобы повторить примеры из этой статьи, вам понадобится совместимая плата и библиотека ArduinoBLE library
Немного истории: Харальд Синезубый
Название «Bluetooth®» восходит к датскому королю X века Харальду Синезубому (Harald Blåtand), правившему примерно с 958 по 986 год. Он прославился тем, что объединил разрозненные датские племена и принял христианство, что оказало огромное влияние на историю Дании. Прозвище «Blåtand» (буквально — «синий зуб») связывают с его потемневшим или мёртвым зубом.
Аналогия очевидна: как Харальд объединил племена, так и протокол Bluetooth® призван объединить разные устройства и стандарты связи.
Технические характеристики
Рассмотрим ключевые концепции и технические параметры Bluetooth® Low Energy.
Частотный диапазон
Bluetooth® LE работает в диапазоне 2,4 GHz ISM (промышленный, научный и медицинский диапазон), который также используется Wi-Fi, микроволновыми печами и другими беспроводными технологиями. Этот диапазон делится на несколько каналов, по которым устройства и обмениваются данными.
Дальность связи
В типичных условиях Bluetooth® LE обеспечивает связь на расстоянии до 50 метров (около 164 футов) при прямой видимости. На практике дальность зависит от нескольких факторов:
Препятствия. Стены, мебель и другие объекты заметно сокращают дальность. Особенно плохо сигнал проходит через толстые бетонные стены.
Помехи. Диапазон 2,4 GHz делят между собой множество устройств. Помехи от соседнего Wi-Fi или другого BLE-устройства могут снизить надёжность соединения.
Конструкция антенны. Качество и форм-фактор антенны напрямую влияют на зону покрытия. Хорошо спроектированная антенна — это заметно лучший сигнал.
Ориентация устройств. Прямая видимость между устройствами даёт максимальную дальность; любые препятствия на пути сигнала её уменьшают.
Центральное и периферийное устройство
В BLE каждое устройство выполняет одну из двух ролей.
Центральное устройство (Central). Как правило, это более мощное устройство — с быстрым процессором, достаточным объёмом памяти и ёмкой батареей. Именно центральное устройство инициирует подключение к периферийным. Например, ваш смартфон выступает центральным устройством, когда подключается к фитнес-браслету или плате Arduino.
Периферийное устройство (Peripheral). Периферийные устройства, как правило, ресурсно ограничены (например, плата Arduino по сравнению со смартфоном). Они периодически рассылают рекламные пакеты, оповещая о своём присутствии. В отличие от классического Bluetooth®, BLE-устройства не поддерживают постоянное соединение с центральным устройством — это экономит энергию.
Роли Bluetooth® LE: периферийные устройства подключаются к центральному устройству
Режимы рекламы и соединения
Режим рекламы (Advertising Mode). В этом режиме периферийное устройство периодически рассылает рекламные пакеты с информацией о себе: идентификатором, доступными сервисами и характеристиками. Центральные устройства сканируют эфир и обнаруживают такие пакеты. Пока соединение не установлено, периферия находится в энергосберегающем состоянии — она просто «ждёт» подключения.
Режим соединения (Connection Mode). Этот режим активируется после того, как центральное устройство успешно подключилось к периферийному. Теперь устройства могут обмениваться данными в обоих направлениях: центральное устройство читает данные с периферии и записывает данные на неё.
Режимы Bluetooth® LE: анимация показывает попытку подключения центрального устройства к недоступному периферийному
Сервисы и характеристики
Сервисы и характеристики — это основные строительные блоки BLE, определяющие, как организованы и передаются данные.
Сервисы (Services)
Сервис — это логическая группа связанных данных или функций, которые предоставляет периферийное устройство. Представьте метеосенсор: у него может быть сервис «Weather Data» с измерениями температуры, влажности и скорости ветра. Другой сервис — «Energy Information» — содержит данные об уровне заряда батареи и потреблении энергии.
Характеристики (Characteristics)
Внутри каждого сервиса находятся характеристики — отдельные точки данных. В сервисе «Weather Data» это могут быть «Temperature», «Humidity» и «Wind Speed». Характеристики непрерывно обновляются по мере поступления новых измерений.
Уникальные универсальные идентификаторы (UUIDs)
Чтобы различать сервисы и характеристики, BLE использует UUID — 128-битный идентификатор, уникальный для каждого сервиса или характеристики. Именно по UUID центральное устройство находит нужные данные на периферии.
UUIDs играют ключевую роль в BLE-коммуникации: они исключают путаницу и позволяют точно обращаться к нужным данным. UUIDs — это своего рода ключи, открывающие доступ к конкретным данным периферийного устройства.
Профили (Profiles)
Bluetooth® LE профили — это заранее определённые наборы сервисов и характеристик, стандартизирующие взаимодействие устройств. Каждый профиль ориентирован на конкретную задачу и гарантирует совместимость устройств разных производителей.
Наиболее распространённые стандартные профили:
- Battery Service — информация об уровне заряда батареи. Центральное устройство читает характеристику Battery Level, чтобы знать, сколько заряда осталось у наушников или смарт-часов.
- Heart Rate Service — данные о пульсе в реальном времени. Используется в фитнес-приложениях и медицинских мониторах.
- Generic Access Profile (GAP) — определяет роли устройств и процедуры обнаружения и установки соединения. Это фундамент всей BLE-коммуникации.
Подробнее о профилях Bluetooth® можно прочитать here.
Помимо стандартных профилей, разработчики могут создавать собственные — с уникальными сервисами и характеристиками под конкретный проект. Это открывает широкие возможности: от мониторинга здоровья до умного дома.
Bluetooth® LE против Bluetooth® Classic
Bluetooth® LE и Bluetooth® Classic — это принципиально разные технологии. Классический Bluetooth® работает как последовательный порт (UART) и рассчитан на точечное соединение с непрерывной передачей данных.
Основные отличия:
Энергопотребление
- Bluetooth® Classic: потребляет больше энергии из-за непрерывного соединения. Не подходит для устройств с маленькой батареей.
- Bluetooth® LE: оптимизирован для минимального потребления. Устройства на BLE могут работать месяцами от небольшой батарейки или даже от энергосбора.
Скорость передачи данных
- Bluetooth® Classic: высокая скорость — подходит для стриминга аудио и передачи файлов.
- Bluetooth® LE: скорость ниже, зато потребление минимальное. Идеален для редкой отправки небольших порций данных: показаний сенсоров, команд управления.
Тип соединения
- Bluetooth® Classic: постоянное соединение с относительно высоким потреблением.
- Bluetooth® LE: два режима — реклама и соединение. В режиме рекламы периферия периодически сигнализирует о себе, не поддерживая постоянного соединения и экономя энергию. Соединение устанавливается только по запросу.
Библиотека ArduinoBLE
ArduinoBLE Library — главная библиотека для работы с Bluetooth® Low Energy на совместимых платах Arduino. Прежде всего установите библиотеку ArduinoBLE. Как это сделать, описано в руководстве instructions.
Ниже — обзор наиболее важных методов библиотеки.
BLE.begin()— инициализация библиотеки
Методы центрального устройства
BLE.scan()— начать сканирование периферийных устройствBLE.available()— проверить наличие доступных периферийных устройствBLE.scanForUuid("UUID")— сканировать периферийные устройства с конкретным UUIDbleDevice.address()— получить адрес периферийного устройстваbleDevice.localName()— получить имя периферийного устройстваbleDevice.advertisedServiceUuid()— получить рекламируемый сервис периферийного устройстваbleDevice.connect()— подключиться к BLE-устройствуBLE.connected()— проверить успешность подключенияbleDevice.discoverAttributes()— обнаружить атрибуты периферийного устройстваBLE.stopScan()— остановить сканированиеBLE.disconnect()— отключиться от периферийного устройства
Методы периферийного устройства
BLE.setLocalName(name)— задать имя устройстваBLEService newService(service)— создать объект сервиса
Подробнее о стандартных сервисах — в Assigned Numbers document.
BLEUnsignedCharCharacteristic customCharacteristic("UUID_here", Properties_here);— создать объект характеристикиBLE.setAdvertisedService(bleService)— задать рекламируемый сервисbleService.addCharacteristic(bleCharacteristic)— добавить характеристику в сервисbleCharacteristic.writeValue()— записать значение характеристикиBLE.advertise()— начать рекламу
Здесь перечислены только самые важные методы. Полный список с описанием каждого метода — в ArduinoBLE reference.
Примеры
Ниже приведены примеры: передача данных между двумя платами Arduino и управление платой со смартфона через приложение.
Дистанционное управление светодиодом
Для этого примера понадобятся две платы Arduino с поддержкой Bluetooth® LE. К одной подключается кнопка, другая управляет светодиодом. При нажатии кнопки первая плата рекламирует соответствующее значение (HIGH/LOW), которое принимает вторая плата и меняет состояние светодиода.
Центральное устройство
Этот скетч сканирует BLE-периферию с определённым UUID (в нашем случае — другую плату Arduino), подключается к ней и позволяет управлять встроенным светодиодом с помощью кнопки, подключённой к пину 4.
- В схеме ниже используется [Arduino UNO R4 WiFi](). Вы можете взять любую совместимую плату из [этого списка]().
Схема подключения: Arduino UNO R4 WiFi с кнопкой на макетной плате (центральное устройство)
Загрузите следующий скетч:
#include <ArduinoBLE.h>
// переменные для кнопки
const int buttonPin = 4;
int oldButtonState = LOW;
void setup() {
Serial.begin(9600);
while (!Serial);
// настройка пина кнопки как входа
pinMode(buttonPin, INPUT);
// инициализация аппаратной части Bluetooth® Low Energy
BLE.begin();
Serial.println("Bluetooth® Low Energy Central - LED control");
// начало сканирования периферийных устройств
BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
}
void loop() {
// проверка обнаружения периферийного устройства
BLEDevice peripheral = BLE.available();
if (peripheral) {
// периферийное устройство обнаружено, вывод адреса, локального имени и рекламируемого сервиса
Serial.print("Found ");
Serial.print(peripheral.address());
Serial.print(" '");
Serial.print(peripheral.localName());
Serial.print("' ");
Serial.print(peripheral.advertisedServiceUuid());
Serial.println();
if (peripheral.localName() != "LED") {
return;
}
// остановка сканирования
BLE.stopScan();
controlLed(peripheral);
// периферийное устройство отключилось, возобновление сканирования
BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214");
}
}
void controlLed(BLEDevice peripheral) {
// подключение к периферийному устройству
Serial.println("Connecting ...");
if (peripheral.connect()) {
Serial.println("Connected");
} else {
Serial.println("Failed to connect!");
return;
}
// обнаружение атрибутов периферийного устройства
Serial.println("Discovering attributes ...");
if (peripheral.discoverAttributes()) {
Serial.println("Attributes discovered");
} else {
Serial.println("Attribute discovery failed!");
peripheral.disconnect();
return;
}
// получение характеристики LED
BLECharacteristic ledCharacteristic = peripheral.characteristic("19b10001-e8f2-537e-4f6c-d104768a1214");
if (!ledCharacteristic) {
Serial.println("Peripheral does not have LED characteristic!");
peripheral.disconnect();
return;
} else if (!ledCharacteristic.canWrite()) {
Serial.println("Peripheral does not have a writable LED characteristic!");
peripheral.disconnect();
return;
}
while (peripheral.connected()) {
// пока периферийное устройство подключено
// чтение пина кнопки
int buttonState = digitalRead(buttonPin);
if (oldButtonState != buttonState) {
// состояние кнопки изменилось
oldButtonState = buttonState;
if (buttonState) {
Serial.println("button pressed");
// кнопка нажата, запись 0x01 для включения LED
ledCharacteristic.writeValue((byte)0x01);
} else {
Serial.println("button released");
// кнопка отпущена, запись 0x00 для выключения LED
ledCharacteristic.writeValue((byte)0x00);
}
}
}
Serial.println("Peripheral disconnected");
}
Периферийное устройство
Этот скетч настраивает плату Arduino как периферийное устройство с нужным UUID и рекламирует характеристику встроенного светодиода. Дополнительных компонентов подключать не нужно — используется только LED_BUILTIN.
LED_BUILTINработает на всех платах Arduino.
#include <ArduinoBLE.h>
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // Bluetooth® Low Energy сервис LED
// Bluetooth® Low Energy характеристика переключения LED — пользовательский 128-битный UUID, доступен для чтения и записи центральным устройством
BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
const int ledPin = LED_BUILTIN; // пин для LED
void setup() {
Serial.begin(9600);
while (!Serial);
// установка пина LED в режим выхода
pinMode(ledPin, OUTPUT);
// начало инициализации
if (!BLE.begin()) {
Serial.println("starting Bluetooth® Low Energy module failed!");
while (1);
}
// установка рекламируемого локального имени и UUID сервиса:
BLE.setLocalName("LED");
BLE.setAdvertisedService(ledService);
// добавление характеристики в сервис
ledService.addCharacteristic(switchCharacteristic);
// добавление сервиса
BLE.addService(ledService);
// установка начального значения характеристики:
switchCharacteristic.writeValue(0);
// начало рекламирования
BLE.advertise();
Serial.println("BLE LED Peripheral");
}
void loop() {
// ожидание подключения Bluetooth® Low Energy периферийных устройств:
BLEDevice central = BLE.central();
// если центральное устройство подключено к периферийному:
if (central) {
Serial.print("Connected to central: ");
// вывод MAC-адреса центрального устройства:
Serial.println(central.address());
// пока центральное устройство подключено к периферийному:
while (central.connected()) {
// если удалённое устройство записало значение в характеристику,
// используем значение для управления LED:
if (switchCharacteristic.written()) {
if (switchCharacteristic.value()) { // любое значение, кроме 0,
Serial.println("LED on");
digitalWrite(ledPin, HIGH); // включит LED
} else { // значение 0
Serial.println(F("LED off"));
digitalWrite(ledPin, LOW); // выключит LED
}
}
}
// при отключении центрального устройства выводим сообщение:
Serial.print(F("Disconnected from central: "));
Serial.println(central.address());
}
}
Практические замечания
- Убедитесь, что UUID в скетчах центрального и периферийного устройства совпадают — иначе соединение не установится.
- Если плата не обнаруживается при сканировании, проверьте, загружен ли скетч периферии и включена ли реклама (
BLE.advertise()). - При отладке удобно использовать Serial Monitor: добавьте вывод адреса и имени найденного устройства через
bleDevice.address()иbleDevice.localName().
Управление Arduino со смартфона
Этот пример позволяет управлять встроенным светодиодом платы Arduino прямо со смартфона. Рекомендуем использовать приложение LightBlue, доступное для Android и iOS . Загрузите скетч и следуйте шагам, показанным на изображении ниже.
Шаг 1 — Загрузите скетч
#include <ArduinoBLE.h>
BLEService newService("180A"); // создание сервиса
BLEUnsignedCharCharacteristic randomReading("2A58", BLERead | BLENotify); // создание характеристики аналогового значения
BLEByteCharacteristic switchChar("2A57", BLERead | BLEWrite); // создание характеристики LED
const int ledPin = 2;
long previousMillis = 0;
void setup() {
Serial.begin(9600); // инициализация последовательного соединения
while (!Serial); //программа запускается при открытии монитора порта
pinMode(LED_BUILTIN, OUTPUT); // инициализация встроенного пина LED для индикации подключения центрального устройства
pinMode(ledPin, OUTPUT); // инициализация встроенного пина LED для индикации подключения центрального устройства
//инициализация библиотеки ArduinoBLE
if (!BLE.begin()) {
Serial.println("starting Bluetooth® Low Energy failed!");
while (1);
}
BLE.setLocalName("<My Board Name>"); //установка имени, отображаемого при сканировании Bluetooth® устройств
BLE.setAdvertisedService(newService);
newService.addCharacteristic(switchChar); //добавление характеристик в сервис
newService.addCharacteristic(randomReading);
BLE.addService(newService); // добавление сервиса
switchChar.writeValue(0); //установка начальных значений характеристик
randomReading.writeValue(0);
BLE.advertise(); //начало рекламирования сервиса
Serial.println(" Bluetooth® device active, waiting for connections...");
}
void loop() {
BLEDevice central = BLE.central(); // ожидание центрального Bluetooth® Low Energy устройства
if (central) { // если центральное устройство подключено к периферийному
Serial.print("Connected to central: ");
Serial.println(central.address()); // вывод BT-адреса центрального устройства
digitalWrite(LED_BUILTIN, HIGH); // включение LED для индикации подключения
// проверка уровня заряда батареи каждые 200 мс
// пока центральное устройство подключено:
while (central.connected()) {
long currentMillis = millis();
if (currentMillis - previousMillis >= 200) { // если прошло 200 мс, проверяем уровень заряда батареи
previousMillis = currentMillis;
int randomValue = analogRead(A1);
randomReading.writeValue(randomValue);
if (switchChar.written()) {
if (switchChar.value()) { // любое значение, кроме 0,
Serial.println("LED on");
digitalWrite(ledPin, HIGH); // включит LED
} else { // значение 0
Serial.println(F("LED off"));
digitalWrite(ledPin, LOW); // выключит LED
}
}
}
}
digitalWrite(LED_BUILTIN, LOW); // при отключении центрального устройства выключаем LED
Serial.print("Disconnected from central: ");
Serial.println(central.address());
}
}
Шаг 2 — Запустите приложение LightBlue
Управление платой Arduino через приложение LightBlue®: выбор устройства (Select Device), чтение (Read Value) и запись значений (Write Value) цифровых и аналоговых характеристик
Типичные ошибки
- Библиотека не установлена. Если при компиляции появляется ошибка
ArduinoBLE.h: No such file or directory— откройте менеджер библиотек и установитеArduinoBLE. - Неверная плата. Убедитесь, что выбрана совместимая плата. На платах без BLE-модуля библиотека не работает.
- UUID не совпадают. Центральное устройство ищет периферию по UUID сервиса. Если UUID в скетчах разные — устройства друг друга не найдут.
- Устройство не появляется в приложении. Проверьте, вызван ли
BLE.advertise()в скетче периферии. Попробуйте перезагрузить плату. - Соединение обрывается. Убедитесь, что расстояние между устройствами не превышает нескольких метров и нет сильных источников помех (роутер Wi-Fi рядом с платой).
Итоги
В этой статье мы разобрали основы Bluetooth® Low Energy: как работают роли центрального и периферийного устройства, что такое сервисы, характеристики и UUID, чем BLE отличается от классического Bluetooth®. На практике мы показали, как передавать данные между двумя платами Arduino и как управлять платой со смартфона с помощью библиотеки ArduinoBLE.