Микроконтроллер esp32 примечателен многим, однако его наиболее известной характеристикой (которая, кстати, вполне себе «перевернула» рынок в своё время) является встроенная возможность осуществления связи по bluetooth и wi-fi. Эти способы коммуникации позволяют микроконтроллеру осуществлять как скоростную связь с устройствами, так и энергосберегающую. Именно об этих способах мы и поговорим в этой статье.

Достаточно часто перед использующими этот микроконтроллер встаёт задача связи его с другими аналогичными устройствами. Поэтому мне показалось, что будет достаточно полезным рассмотреть 2 достаточно распространённых способа коммуникации устройств. Начнём мы с энергоэффективного протокола BLE.

Что такое Bluetooth? Это беспроводной стандарт связи для обмена данными на коротком расстоянии. Как и WiFi, Bluetooth работает на частоте 2.4 ГГц.

image
Источник картинки: wikihandbk.com

Bluetooth применяется во множестве разных ситуаций, где требуется беспроводное управление и передача данных. Например:

  • Передача аудиоданных в наушники или аудиосистему автомобиля
  • Коммуникация между периферийными устройствами и ПК
  • Передача данных между Bluetooth-устройствами

Другими словами, Bluetooth используется в ситуациях, когда для передачи данных между устройствами требуется непрерывное сквозное («точка-точка») подключение.

Что такое Bluetooth Low Energy (BLE)? – это энергосберегающий вариант Bluetooth, и его главная область применения – это передача маленьких порций данных на короткие расстояния. Этот стандарт предназначен для очень маломощных проектов, питаемых от батареек-таблеток.

image
Источник картинки: 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 дБм)
Сетевая топология * “Точка-точка” (включая “пикосеть”)
* Вещание
* Ячеистая сеть
“Точка-точка” (включая “пикосеть”)
В стандарте Bluetooth Low Energy предусмотрено два типа устройств: сервер и клиент.
Сервер оповещает о своём существовании, чтобы его могли найти другие устройства, а также содержит данные для считывания клиентом. Клиент сканирует близлежащие устройства и, найдя искомый сервер, подключается к нему и начинает прослушивать входящие данные. Этот тип коммуникации называют «сквозным» или «точка-точка».

Стандарт BLE предусматривает и другие типы подключения:

  • Режим вещания. Сервер передаёт данные нескольким клиентам, подключённым к нему.
  • Ячеистая сеть. Все устройства подключены друг к другу. Это тип соединения, когда в сети несколько устройств и у каждого из них по несколько подключений.

Теперь давайте рассмотрим несколько важных терминов, касающихся BLE.

GATT — расшифровывается как «generic attributes» («общие атрибуты»). Эта спецификация определяет иерархию данных, которую BLE-устройство демонстрирует другим BLE-устройствам, подключённым к нему. Другими словами, GATT определяет то, как два BLE-устройства отправляют и получают стандартные сообщения.

image
Источник картинки: 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)


  1. Armleo
    27.12.2021 11:14

    Статья не плохая, поставил плюс. На будущее пишите больше + рассказывайте больше о том, что происходит под капотом. А то непонятно например чем Wifi хуже BLE, либо что может обеспечить большую скорость итд итп.


    1. ainu
      27.12.2021 14:59

      Скорость: WiFi > Bluetooth > BLE
      Энергоэкономия: WiFi < Bluetooth < BLE

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

      Другое дело примеров кода для Bluetooth-сервера на ESP32 (то есть он подключает к себе устройства, а не сам ведет себя как устройство) раз два и обчелся — недокументированный код подключения клавиатуры и PS3 геймпада. Документации вообще нет. В общем, в это дело лезть сложно.


      1. mctMaks
        27.12.2021 16:08

        Bluetooth > BLE

        верно только для EDR, в котором 3 мегабита разрешено. для BLE уже 2 мегабита скорость разрешили.

        Если раз в сутки проснуться и скачать новый прогноз погоды, то WiFi.

        Тут скорее без разницы, по энергии скорее всего в среднем одинаково будет. Вопрос только в организации доступа к сети.

        А вот для синхронизации часов с высокой точностью это уже строго WiFi.


  1. ainu
    27.12.2021 14:45
    +3

    Интересно, что в статье под названием «Осуществление разных способов связи между модулями esp32» нет слова «ESP-NOW». Технология позволяет соединять между собой esp32 и один-на-один, и сеткой, и вокруг одного устройства, и в одну сторону, и всяк-повсяк.


    1. ainu
      27.12.2021 14:54
      +1

      В двух словах: это 2.4 гигагерц протокол, который работает НЕ поверх Wifi протокола, но вместо этого использует Wifi-оборудование (радио, антенну и прочее).

      Самый ближайший аналог — 2.4 ГГц свисток для беспроводной мышки — совсем не вайфай, хотя тоже радиоволны.

      Если говорить про энергоэффективность, ESPNOW ~ 120mA в момент соединения BLE ~ 100mA, но для BLE нужно больше времени.

      То есть для вай-фай кнопки вида проснулись-отправили-уснули, ESPNOW вообще впереди планеты всей (я уже молчу про вайфай и блютус), ну и например он ещё может работать как передатчик для рации или подобного в одну сторону, причем бьёт примерно на 500метров.

      Из минусов — только ESP девайсы между собой соединять, из андроид-телефона или ноутбука или Wi-Fi роутера к этому делу не подключиться. Но и статья не об этом, естественно.


      1. 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.


    1. kharlashkin
      27.12.2021 23:51

      Также непонятно почему используют http сервер и клиент, тот же mqtt (канальный протокол TCP) и mqtt-sn (UDP) гораздо более заточенные под IoT.


      1. boojum
        28.12.2021 09:49

        А в чем преимущество mqtt в данном случае?


  1. 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-репозиторий?


    1. kot_review Автор
      28.12.2021 11:09

      Сорри, ссылка видимо битая была, поправил.


      1. alstutor
        28.12.2021 11:50

        Спасибо!


  1. dernuss
    28.12.2021 14:28

    Ещё интересна дальность разных типов связи на esp32


    1. FGV
      28.12.2021 14:57

      как то проверял, в городе, получил:

      1 по ble есп32-смартфон ~40м

      2 по wifi есп32-смартфон ~80м


      1. dernuss
        28.12.2021 15:03

        А есп - есп?


        1. FGV
          28.12.2021 15:24

          Не замерял, так как не было надобности.