Осуществление разных способов связи между модулями esp32

image-loader.svg


Микроконтроллер 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 можно посмотреть в таблице ниже:

В стандарте 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 
#include 
#include 


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

zo51rompgpgmhtgugcno7w12cvc.jpeg

Теперь посмотрим на клиентскую часть.

Также подключаем необходимую библиотеку:

#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-порта клиента эта картина выглядит следующим образом:

iwnk4iskyoaoliauvnf3d5vukac.jpeg

Таким образом, используя энергоэффективный метод связи с использованием BLE, вы можете реализовывать свои интересные применения этого типа связи.

Поговорим теперь и о связи с использованием Wi-Fi


Здесь одно устройство также будет играть роль сервера, а другое — клиента.
Структура вкладок сервер выглядит следующим образом:

5vvinh0knwqycehxljvfivzkn28.jpeg

Структура вкладок клиента:

as0gvxgy80g7jrz9vfjohhvsghw.jpeg

Вообще говоря, схема работы выглядит примерно так: «я сам себе и дедушка, и любимый внук» :-)

5nuuqfjceagx7pntmhhnesf2ddw.jpeg

То есть: 1) запущена wi-fi сеть; 2) запущен сервер и на клиенте, и на точке доступа;

Подробнее рассмотрим код сервера на точке доступа


Для работы нам понадобятся следующие библиотеки:

#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include 


Конфигурируем точку доступа, а также определяем адрес сервера на клиенте, куда будем обращаться с точки доступа:

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 
#include 
#include 
#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-порта точки доступа:

ajg4nw3cr5bcqzy59scbr0wislc.jpeg

И клиента:

nmr92krnnvbde90unsgk9sxo6a0.jpeg

Библиотеку для работы wi-fi варианта можно скачать здесь: ESPAsyncWebServer
Все необходимые библиотеки для работы BLE-варианта устанавливаются автоматически при инсталляции поддержки esp32 в Arduino IDE.

Код сервера и клиента для BLE можно скачать здесь, для wi-fi варианта — тут.

Код для обоих вариантов полностью рабочий и протестирован. «Бери и используй!» :-)

Вот такими интересными способами вы можете наладить связь между 2-мя и более устройствами esp32. Успехов в проектировании!

НЛО прилетело и оставило здесь промокоды для читателей нашего блога:

— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.

— 20% на выделенные серверы AMD Ryzen и Intel Core — HABRFIRSTDEDIC.

© Habrahabr.ru