Микроконтроллер esp32 примечателен многим, однако его наиболее известной характеристикой (которая, кстати, вполне себе «перевернула» рынок в своё время) является встроенная возможность осуществления связи по bluetooth и wi-fi. Эти способы коммуникации позволяют микроконтроллеру осуществлять как скоростную связь с устройствами, так и энергосберегающую. Именно об этих способах мы и поговорим в этой статье.
Достаточно часто перед использующими этот микроконтроллер встаёт задача связи его с другими аналогичными устройствами. Поэтому мне показалось, что будет достаточно полезным рассмотреть 2 достаточно распространённых способа коммуникации устройств. Начнём мы с энергоэффективного протокола BLE.
Что такое Bluetooth? Это беспроводной стандарт связи для обмена данными на коротком расстоянии. Как и WiFi, Bluetooth работает на частоте 2.4 ГГц.
Источник картинки: wikihandbk.com
Bluetooth применяется во множестве разных ситуаций, где требуется беспроводное управление и передача данных. Например:
- Передача аудиоданных в наушники или аудиосистему автомобиля
- Коммуникация между периферийными устройствами и ПК
- Передача данных между Bluetooth-устройствами
Другими словами, Bluetooth используется в ситуациях, когда для передачи данных между устройствами требуется непрерывное сквозное («точка-точка») подключение.
Что такое Bluetooth Low Energy (BLE)? – это энергосберегающий вариант Bluetooth, и его главная область применения – это передача маленьких порций данных на короткие расстояния. Этот стандарт предназначен для очень маломощных проектов, питаемых от батареек-таблеток.
Источник картинки: wikihandbk.com
В отличие от Bluetooth, который включён постоянно, BLE-устройство постоянно находится в спящем режиме, кроме ситуаций, когда оно подключено к другим устройствам. Благодаря этому BLE-устройства потребляют очень мало питания. Эта функция крайне полезна для коммуникации типа M2M (англ. «machine-to-machine», т.е. «между машинами»), т.к. позволяет делать проекты из маленьких устройств, питаемых от батареек и работающих очень долгое время.
Из-за этого стандарт BLE идеален для проектов, где требуется периодический обмен небольшими порциями данных – например, в медицине, фитнесе, отслеживании объектов, навигации, безопасности и домашней автоматизации.
О других различиях между Bluetooth и BLE можно посмотреть в таблице ниже:
Bluetooh Low Energy (BLE) | Bluetooh: Базовый режим (Base Rate, BR) / Дополнительный режим (Enhanced Date Rate, EDR) |
|
Оптимизирован для: | Передача данных “короткими очередями” | Постоянный поток данных |
Диапазон частот | ISM-диапазон 2.4 ГГц (2.402 — 2.480 ГГц) |
ISM-диапазон 2.4 ГГц (2.402 — 2.480 ГГц) |
Каналы | 40 каналов с диапазоном 2 МГц (3 канала для оповещений, 37 каналов для данных) |
79 каналов с диапазоном 1 МГц |
Использование каналов |
Псевдослучайная перестройка рабочей частоты (FHSS) |
Псевдослучайная перестройка рабочей частоты (FHSS) |
Модуляция | GFSK | GFSK, π/4 DQPSK, 8DPSK |
Энергопотребление | Примерно от 0.01х до 0.5х относительно опорной величины (зависит от способа применения) |
1 (опорная величина) |
Скорость передачи данных | * LE2М РНУ: 2 Мбит/с * LE1М РНУ: 1 Мбит/с * LE Coded РНУ (S=2): 500 Кбит/с * LE Coded РНУ (s=8): 125 Кбит/с |
* EDR РНУ (8DPSK): 3 Мбит/с * EDR РНУ (п/4 DQPSK): 2 Мбит/с * BR PHY (GFSK): 1 Мбит/с |
Макс. мощность | * Класс 1: 100 мВТ (+20 дБм) * Класс 1.5: 10 мВт (+10 дБм) * Класс 2: 2.5 мВт (+4 дБм) * Класс 3: 1 мВт (0 дБм) |
* Класс 1: 100 мВт (+20 дБм) * Класс 2: 2.5 мВт (+4 дм) * Класс 3: 1 мВт (0 дБм) |
Сетевая топология | * “Точка-точка” (включая “пикосеть”) * Вещание * Ячеистая сеть |
“Точка-точка” (включая “пикосеть”) |
Сервер оповещает о своём существовании, чтобы его могли найти другие устройства, а также содержит данные для считывания клиентом. Клиент сканирует близлежащие устройства и, найдя искомый сервер, подключается к нему и начинает прослушивать входящие данные. Этот тип коммуникации называют «сквозным» или «точка-точка».
Стандарт BLE предусматривает и другие типы подключения:
- Режим вещания. Сервер передаёт данные нескольким клиентам, подключённым к нему.
- Ячеистая сеть. Все устройства подключены друг к другу. Это тип соединения, когда в сети несколько устройств и у каждого из них по несколько подключений.
Теперь давайте рассмотрим несколько важных терминов, касающихся BLE.
GATT — расшифровывается как «generic attributes» («общие атрибуты»). Эта спецификация определяет иерархию данных, которую BLE-устройство демонстрирует другим BLE-устройствам, подключённым к нему. Другими словами, GATT определяет то, как два BLE-устройства отправляют и получают стандартные сообщения.
Источник картинки: wikihandbk.com
На верхнем уровне этой иерархии – профиль, состоящий из одного или более сервисов. Каждый сервис содержит как минимум одну характеристику, а также может отсылать к другим сервисам.
Характеристики всегда находятся внутри сервисов, и это то место, где, собственно, хранятся данные во всей этой GATT-иерархии. Характеристика имеет уникальный идентификатор (UID), значение и свойства.
Кроме того, после значения характеристики могут идти дескрипторы, которые дополняют метаданные, содержащиеся в объявлении характеристики.
Каждый сервис, характеристика и дескриптор имеют собственный UUID (англ. «universally unique identifier», что переводится как «универсальный уникальный идентификатор»). UUID – это уникальное 128-битное (16-байтное) число вроде такого:
55072829-bc9e-4c53-938a-74a6d4c78776
Сокращённые UUID для всех сервисов, характеристик, профилей и т.д. можно найти на сайте Bluetooth SIG.
Но если вашему приложению нужен собственный UUID, его можно сгенерировать при помощи этого UUID-генератора.
Итак, теперь, когда мы немного ознакомились с теоретической стороной вопроса, посмотрим, как это можно было бы реализовать на базе esp32.
Так как мы осуществляем связь двух модулей esp32 между собой — нам понадобится серверный и клиентский код.
Посмотрим общий случай такой реализации и начнём с серверной части:
Сначала мы подключаем необходимые библиотеки:
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
Генерируем с помощью сервиса, указанного выше, уникальные UUID:
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
Создаём BLE-устройство:
BLEDevice::init("Eto moye imya");
Создаём BLE-сервер:
BLEServer *pServer = BLEDevice::createServer();
Создаём сервис и характеристику с указанными UUID:
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
После чего устанавливаем начальное значение характеристики, запускаем сервис и начинаем широковещательную рассылку уведомлений:
pCharacteristic->setValue("Всем привет!");
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println("Характеристика установлена! Теперь вы можете прочитать её на телефоне!");
После прошивки программы в микроконтроллер в мониторе COM-порта появляются приветственные сообщения:
Теперь посмотрим на клиентскую часть.
Также подключаем необходимую библиотеку:
#include "BLEDevice.h"
Далее указываем UUID сервиса и характеристики (должны совпадать с таковыми на сервере):
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
Так как клиентская часть содержит достаточно большое количество служебного кода, в разы превышающего серверный код, имеет смысл остановиться на главном: после осуществления поиска и успешного соединения с сервером клиент обновляет характеристику сервиса на удалённом сервере:
if (connected) {
String newValue = "Time since boot: " + String(millis()/1000);
Serial.println("Setting new characteristic value to \"" + newValue + "\"");
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
}else if(doScan){
BLEDevice::getScan()->start(0);
}
И в мониторе COM-порта клиента эта картина выглядит следующим образом:
Таким образом, используя энергоэффективный метод связи с использованием BLE, вы можете реализовывать свои интересные применения этого типа связи.
Поговорим теперь и о связи с использованием Wi-Fi
Здесь одно устройство также будет играть роль сервера, а другое — клиента.
Структура вкладок сервер выглядит следующим образом:
Структура вкладок клиента:
Вообще говоря, схема работы выглядит примерно так: «я сам себе и дедушка, и любимый внук» :-)
То есть: 1) запущена wi-fi сеть; 2) запущен сервер и на клиенте, и на точке доступа;
Подробнее рассмотрим код сервера на точке доступа
Для работы нам понадобятся следующие библиотеки:
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include <HTTPClient.h>
Конфигурируем точку доступа, а также определяем адрес сервера на клиенте, куда будем обращаться с точки доступа:
const char* ssid = "ESP32-Access-Point";
const char* password = "123456789";
const char* SlaveServerName = "http://192.168.4.2/post";
Запускаем сервер на 80 порту:
AsyncWebServer server(80);
void setup() {
Serial.begin(115200);
Serial.println();
Serial.print("Setting AP (Access Point)…");
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
Server ();//запустили сервер для входящих POST-запросов
}
Функция
Server ()
, вызывающаяся в последней строке (выше), служит для прослушивания входящих POST-запросов и реализована во вкладке server
:void Server ()
{
server.on(
"/post",
HTTP_POST,
[](AsyncWebServerRequest * request){},
NULL,
[](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) {
// IsReadyForRead = false;
for (size_t i = 0; i < len; i++) {
Serial.write(data[i]);
}
Serial.println();
request->send(200);
});
server.begin();
}
Ну и в методе
loop ()
— мы вызываем тестовую функцию TestSender
, которая отсылает сообщение клиенту:void loop() {TestSender ();}
void TestSender ()
{
WiFiClient client;
HTTPClient http;
// Your Domain name with URL path or IP address with path
http.begin(client, SlaveServerName);
// отсылаем то, что получили
// If you need an HTTP request with a content type: text/plain
http.addHeader("Content-Type", "text/plain");
int httpResponseCode = http.POST("Привет!");
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
// Освобождаем ресурсы
http.end();
}
Теперь рассмотрим код клиента точки доступа
Подключаем необходимые библиотеки:
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include "ESPAsyncWebServer.h"
Задаём логин и пароль точки доступа, к которой будем подключаться:
const char* ssid = "ESP32-Access-Point";
const char* password = "123456789";
Определяем адрес для обращений на сервере (у точки доступа):
const char* MainServerName = "http://192.168.4.1/post";
Подключаемся к точке доступа и запускаем сервер для прослушки входящих POST-запросов:
AsyncWebServer server(80);
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("Connecting");
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
Server ();//запустили сервер для входящих POST-запросов
}
Функция
Server ()
— в последней строке (выше) полностью аналогична таковой у точки доступа и служит для прослушивания POST-запросов:void Server ()
{
server.on(
"/post",
HTTP_POST,
[](AsyncWebServerRequest * request){},
NULL,
[](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) {
// IsReadyForRead = false;
for (size_t i = 0; i < len; i++) {
Serial.write(data[i]);
}
Serial.println();
request->send(200);
});
server.begin();
}
Ну и в методе
loop ()
— мы вызываем тестовую функцию TestSender ()
, которая отсылает сообщение на POST-сервер, запущенный на точке доступа:
void loop() {TestSender ();}
void TestSender ()
{
WiFiClient client;
HTTPClient http;
http.begin(client, MainServerName);
// отсылаем то, что получили
// If you need an HTTP request with a content type: text/plain
http.addHeader("Content-Type", "text/plain");
int httpResponseCode = http.POST("И тебе привет!");
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
// Освобождаем ресурсы
http.end();
}
После включения обоих модулей esp32 вот так эта картина выглядит в мониторе COM-порта точки доступа:
И клиента:
Библиотеку для работы wi-fi варианта можно скачать здесь: ESPAsyncWebServer
Все необходимые библиотеки для работы BLE-варианта устанавливаются автоматически при инсталляции поддержки esp32 в Arduino IDE.
Код сервера и клиента для BLE можно скачать здесь, для wi-fi варианта — тут.
Код для обоих вариантов полностью рабочий и протестирован. «Бери и используй!» :-)
Вот такими интересными способами вы можете наладить связь между 2-мя и более устройствами esp32. Успехов в проектировании!
НЛО прилетело и оставило здесь промокоды для читателей нашего блога:
— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.
— 20% на выделенные серверы AMD Ryzen и Intel Core — HABRFIRSTDEDIC.
Комментарии (15)
ainu
27.12.2021 14:45+3Интересно, что в статье под названием «Осуществление разных способов связи между модулями esp32» нет слова «ESP-NOW». Технология позволяет соединять между собой esp32 и один-на-один, и сеткой, и вокруг одного устройства, и в одну сторону, и всяк-повсяк.
ainu
27.12.2021 14:54+1В двух словах: это 2.4 гигагерц протокол, который работает НЕ поверх Wifi протокола, но вместо этого использует Wifi-оборудование (радио, антенну и прочее).
Самый ближайший аналог — 2.4 ГГц свисток для беспроводной мышки — совсем не вайфай, хотя тоже радиоволны.
Если говорить про энергоэффективность, ESPNOW ~ 120mA в момент соединения BLE ~ 100mA, но для BLE нужно больше времени.
То есть для вай-фай кнопки вида проснулись-отправили-уснули, ESPNOW вообще впереди планеты всей (я уже молчу про вайфай и блютус), ну и например он ещё может работать как передатчик для рации или подобного в одну сторону, причем бьёт примерно на 500метров.
Из минусов — только ESP девайсы между собой соединять, из андроид-телефона или ноутбука или Wi-Fi роутера к этому делу не подключиться. Но и статья не об этом, естественно.FGV
27.12.2021 15:50+2…НЕ поверх Wifi протокола…
Китайцы они такие, в https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html пишут немного не так:
ESP-NOW is a kind of connectionless Wi-Fi communication protocol that is defined by Espressif.
kharlashkin
27.12.2021 23:51Также непонятно почему используют http сервер и клиент, тот же mqtt (канальный протокол TCP) и mqtt-sn (UDP) гораздо более заточенные под IoT.
alstutor
28.12.2021 08:32А это только у меня выдает "Hmm. We’re having trouble finding that site. We can’t connect to the server at https." для BLE варианта? WiFi-то скачался...
И, может быть, лучше оформить как git-репозиторий?
Armleo
Статья не плохая, поставил плюс. На будущее пишите больше + рассказывайте больше о том, что происходит под капотом. А то непонятно например чем Wifi хуже BLE, либо что может обеспечить большую скорость итд итп.
ainu
Скорость: WiFi > Bluetooth > BLE
Энергоэкономия: WiFi < Bluetooth < BLE
Если нужно отправить сигнал из кнопки (беспроводной выключатель), лучше BLE. Если нужно отправить веб-интерфейс или видеосигнал, то WiFi. Если раз в сутки проснуться и скачать новый прогноз погоды, то WiFi. Чем меньше байт нужно для отправки, тем лучше BLE/Bluetooth.
Другое дело примеров кода для Bluetooth-сервера на ESP32 (то есть он подключает к себе устройства, а не сам ведет себя как устройство) раз два и обчелся — недокументированный код подключения клавиатуры и PS3 геймпада. Документации вообще нет. В общем, в это дело лезть сложно.
mctMaks
верно только для EDR, в котором 3 мегабита разрешено. для BLE уже 2 мегабита скорость разрешили.
Тут скорее без разницы, по энергии скорее всего в среднем одинаково будет. Вопрос только в организации доступа к сети.
А вот для синхронизации часов с высокой точностью это уже строго WiFi.