Начало работы с ESP-NOW (ESP32 с Arduino IDE)
Узнайте, как использовать ESP-NOW для обмена данными между платами ESP32, запрограммированными в Arduino IDE. ESP-NOW — это протокол связи без установления соединения, разработанный Espressif, который обеспечивает передачу коротких пакетов. Этот протокол позволяет нескольким устройствам легко общаться друг с другом.
У нас есть другие руководства по ESP-NOW с ESP32:
ESP-NOW с ESP32: отправка данных на несколько плат (один-ко-многим)
ESP-NOW с ESP32: получение данных от нескольких плат (многие-к-одному)
ESP32: веб-сервер ESP-NOW с панелью датчиков (ESP-NOW + Wi-Fi)
Arduino IDE
Мы будем программировать плату ESP32 с помощью Arduino IDE, поэтому перед началом работы с этим руководством у вас должно быть установлено дополнение ESP32 в Arduino IDE. Следуйте следующему руководству:
Примечание: у нас есть аналогичное руководство для платы ESP8266 NodeMCU: Начало работы с ESP-NOW (ESP8266 NodeMCU с Arduino IDE)
Знакомство с ESP-NOW
Для видеовведения в протокол ESP-NOW посмотрите следующее видео (попробуйте проект из этого видео):
Цитируя сайт Espressif, ESP-NOW — это «протокол, разработанный Espressif, который позволяет нескольким устройствам общаться друг с другом без использования Wi-Fi. Протокол похож на низкомощное беспроводное соединение на частоте 2,4 ГГц (…). Перед началом связи требуется сопряжение устройств. После сопряжения соединение безопасно и работает по принципу точка-точка, без необходимости рукопожатия».
Это означает, что после сопряжения устройств друг с другом соединение является постоянным. Другими словами, если одна из ваших плат внезапно потеряет питание или перезагрузится, при перезапуске она автоматически подключится к своему пиру и продолжит связь.
ESP-NOW поддерживает следующие функции:
Зашифрованная и незашифрованная одноадресная связь;
Смешанные зашифрованные и незашифрованные пиринговые устройства;
Полезная нагрузка до 250 байт может быть передана;
Функция обратного вызова при отправке, которая может быть настроена для информирования прикладного уровня об успешной или неудачной передаче.
Технология ESP-NOW также имеет следующие ограничения:
Ограниченное количество зашифрованных пиров. Поддерживается не более 10 зашифрованных пиров в режиме Station; не более 6 в режиме SoftAP или SoftAP + Station;
Поддерживается несколько незашифрованных пиров, однако их общее количество должно быть менее 20, включая зашифрованные пиры;
Полезная нагрузка ограничена 250 байтами.
Простыми словами, ESP-NOW — это быстрый протокол связи, который можно использовать для обмена небольшими сообщениями (до 250 байт) между платами ESP32.
ESP-NOW очень универсален, и вы можете организовать одностороннюю или двустороннюю связь в различных конфигурациях.
Односторонняя связь ESP-NOW
Например, при односторонней связи возможны следующие сценарии:
Одна плата ESP32 отправляет данные другой плате ESP32
Эта конфигурация очень проста в реализации и отлично подходит для отправки данных с одной платы на другую, например, показаний датчиков или команд включения/выключения для управления GPIO.
«Мастер» ESP32 отправляет данные нескольким «ведомым» ESP32 (подробнее)
Одна плата ESP32 отправляет одинаковые или разные команды различным платам ESP32. Эта конфигурация идеальна для создания чего-то вроде пульта дистанционного управления. У вас может быть несколько плат ESP32 по всему дому, управляемых одной основной платой ESP32.
Один «ведомый» ESP32 получает данные от нескольких «мастеров» (подробнее)
Эта конфигурация идеальна, если вы хотите собирать данные с нескольких сенсорных узлов на одну плату ESP32. Её можно настроить как веб-сервер для отображения данных со всех остальных плат, например.
Примечание: в документации ESP-NOW нет такого понятия, как «отправитель/мастер» и «получатель/ведомый». Каждая плата может быть отправителем или получателем. Однако для наглядности мы будем использовать термины «отправитель» и «получатель» или «мастер» и «ведомый».
Двусторонняя связь ESP-NOW
С ESP-NOW каждая плата может быть одновременно отправителем и получателем. Таким образом, вы можете установить двустороннюю связь между платами.
Например, вы можете организовать связь между двумя платами.
Узнайте, как: обмениваться показаниями датчиков с помощью двусторонней связи ESP-NOW.
Вы можете добавить больше плат в эту конфигурацию и получить что-то похожее на сеть (все платы ESP32 общаются друг с другом).
Подводя итог, ESP-NOW идеально подходит для построения сети, в которой несколько плат ESP32 могут обмениваться данными друг с другом.
ESP32: получение MAC-адреса платы
Для связи через ESP-NOW вам нужно знать MAC-адрес ESP32-приёмника. Именно так вы определяете, какому устройству будут отправлены данные.
Каждая ESP32 имеет уникальный MAC-адрес, и именно так мы идентифицируем каждую плату для отправки данных через ESP-NOW (узнайте, как получить и изменить MAC-адрес ESP32).
Чтобы получить MAC-адрес вашей платы, загрузите следующий код.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/get-change-esp32-esp8266-mac-address-arduino/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <WiFi.h>
#include <esp_wifi.h>
void readMacAddress(){
uint8_t baseMac[6];
esp_err_t ret = esp_wifi_get_mac(WIFI_IF_STA, baseMac);
if (ret == ESP_OK) {
Serial.printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
baseMac[0], baseMac[1], baseMac[2],
baseMac[3], baseMac[4], baseMac[5]);
} else {
Serial.println("Failed to read MAC address");
}
}
void setup(){
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.STA.begin();
Serial.print("[DEFAULT] ESP32 Board MAC Address: ");
readMacAddress();
}
void loop(){
}
После загрузки кода откройте Serial Monitor на скорости 115200 бод и нажмите кнопку RST/EN на ESP32. MAC-адрес должен отобразиться следующим образом:
Сохраните MAC-адрес вашей платы, потому что он понадобится вам для отправки данных на нужную плату через ESP-NOW.
Односторонняя связь ESP-NOW точка-точка
Чтобы начать работу с беспроводной связью ESP-NOW, мы создадим простой проект, показывающий, как отправить сообщение с одной ESP32 на другую. Одна ESP32 будет «отправителем», а другая ESP32 — «получателем».
Мы будем отправлять структуру, содержащую переменные типов char, int, float и boolean. Затем вы сможете изменить структуру для отправки любых типов переменных, подходящих для вашего проекта (например, показаний датчиков или логических переменных для включения/выключения чего-либо).
Для лучшего понимания мы будем называть ESP32 #1 «отправителем», а ESP32 #2 — «получателем».
Вот что должно быть включено в скетч отправителя:
Инициализация ESP-NOW;
Регистрация функции обратного вызова при отправке данных — функция OnDataSent будет выполнена при отправке сообщения. Она может сообщить нам, было ли сообщение успешно доставлено или нет;
Добавление пирингового устройства (получателя). Для этого вам нужно знать MAC-адрес получателя;
Отправка сообщения пиринговому устройству.
На стороне получателя скетч должен включать:
Инициализация ESP-NOW;
Регистрация функции обратного вызова при получении данных (OnDataRecv). Это функция, которая будет выполнена при получении сообщения.
Внутри этой функции обратного вызова сохранение сообщения в переменную для выполнения любых задач с полученной информацией.
ESP-NOW работает с функциями обратного вызова, которые вызываются, когда устройство получает сообщение или когда сообщение отправлено (вы получаете информацию, было ли сообщение успешно доставлено или произошла ошибка).
Полезные функции ESP-NOW
Вот краткое описание наиболее важных функций ESP-NOW:
Название функции и описание |
|---|
esp_now_init() — Инициализирует ESP-NOW. Перед инициализацией ESP-NOW необходимо инициализировать Wi-Fi. |
esp_now_add_peer() — Вызовите эту функцию для сопряжения с устройством, передав в качестве аргумента MAC-адрес пира. |
esp_now_send() — Отправка данных через ESP-NOW. |
esp_now_register_send_cb() — Регистрация функции обратного вызова, которая срабатывает при отправке данных. При отправке сообщения вызывается функция — эта функция возвращает информацию об успешности доставки. |
esp_now_register_recv_cb() — Регистрация функции обратного вызова, которая срабатывает при получении данных. При получении данных через ESP-NOW вызывается функция. |
Для получения дополнительной информации об этих функциях прочитайте документацию ESP-NOW на Read the Docs.
Скетч отправителя ESP32 (ESP-NOW)
Вот код для платы ESP32-отправителя. Скопируйте код в Arduino IDE, но пока не загружайте его. Вам нужно внести несколько изменений, чтобы он заработал.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <esp_now.h>
#include <WiFi.h>
// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
char a[32];
int b;
float c;
bool d;
} struct_message;
// Create a struct_message called myData
struct_message myData;
esp_now_peer_info_t peerInfo;
// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.print("\r\nLast Packet Send Status:\t");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
void setup() {
// Init Serial Monitor
Serial.begin(115200);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for Send CB to
// get the status of Trasnmitted packet
esp_now_register_send_cb(esp_now_send_cb_t(OnDataSent));
// Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
}
void loop() {
// Set values to send
strcpy(myData.a, "THIS IS A CHAR");
myData.b = random(1,20);
myData.c = 1.2;
myData.d = false;
// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
if (result == ESP_OK) {
Serial.println("Sent with success");
}
else {
Serial.println("Error sending the data");
}
delay(2000);
}
Как работает код
Сначала подключите библиотеки esp_now.h и WiFi.h.
#include <esp_now.h>
#include <WiFi.h>
В следующей строке вы должны вставить MAC-адрес ESP32-получателя.
uint8_t broadcastAddress[] = {0x30, 0xAE, 0xA4, 0x07, 0x0D, 0x64};
В нашем случае MAC-адрес получателя: 30:AE:A4:07:0D:64, но вам нужно заменить эту переменную вашим собственным MAC-адресом.
Затем создайте структуру, содержащую типы данных, которые мы хотим отправить. Мы назвали эту структуру struct_message, и она содержит 4 переменные разных типов. Вы можете изменить её для отправки других типов переменных.
typedef struct struct_message {
char a[32];
int b;
float c;
bool d;
} struct_message;
Затем создайте новую переменную типа struct_message с именем myData, которая будет хранить значения переменных.
struct_message myData;
Создайте переменную типа esp_now_peer_info_t для хранения информации о пире.
esp_now_peer_info_t peerInfo;
Далее определите функцию OnDataSent(). Это функция обратного вызова, которая будет выполнена при отправке сообщения. В данном случае эта функция просто выводит информацию о том, было ли сообщение успешно доставлено или нет.
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.print("\r\nLast Packet Send Status:\t");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
В setup() инициализируйте Serial Monitor для целей отладки:
Serial.begin(115200);
Установите устройство в режим Wi-Fi станции:
WiFi.mode(WIFI_STA);
Инициализируйте ESP-NOW:
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
После успешной инициализации ESP-NOW зарегистрируйте функцию обратного вызова, которая будет вызвана при отправке сообщения. В данном случае мы регистрируем ранее созданную функцию OnDataSent().
esp_now_register_send_cb(OnDataSent);
После этого нам нужно выполнить сопряжение с другим устройством ESP-NOW для отправки данных. Это то, что мы делаем в следующих строках:
//Register peer
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
//Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
В loop() мы будем отправлять сообщение через ESP-NOW каждые 2 секунды (вы можете изменить этот интервал).
Сначала мы устанавливаем значения переменных следующим образом:
strcpy(myData.a, "THIS IS A CHAR");
myData.b = random(1,20);
myData.c = 1.2;
myData.d = false;
Помните, что myData — это структура. Здесь мы присваиваем значения, которые хотим отправить внутри структуры. Например, первая строка присваивает char, вторая строка — случайное число Int, далее Float и Boolean переменная.
Мы создали такую структуру, чтобы показать вам, как отправлять наиболее распространённые типы переменных. Вы можете изменить структуру для отправки других типов данных.
Наконец, отправьте сообщение следующим образом:
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
Проверьте, было ли сообщение успешно отправлено:
if (result == ESP_OK) {
Serial.println("Sent with success");
}
else {
Serial.println("Error sending the data");
}
Цикл loop() выполняется каждые 2000 миллисекунд (2 секунды).
delay(2000);
Скетч получателя ESP32 (ESP-NOW)
Загрузите следующий код на вашу плату ESP32-получатель.
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp-now-esp32-arduino-ide/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <esp_now.h>
#include <WiFi.h>
// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
char a[32];
int b;
float c;
bool d;
} struct_message;
// Create a struct_message called myData
struct_message myData;
// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
memcpy(&myData, incomingData, sizeof(myData));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Char: ");
Serial.println(myData.a);
Serial.print("Int: ");
Serial.println(myData.b);
Serial.print("Float: ");
Serial.println(myData.c);
Serial.print("Bool: ");
Serial.println(myData.d);
Serial.println();
}
void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));
}
void loop() {
}
Как работает код
Аналогично отправителю, начните с подключения библиотек:
#include <esp_now.h>
#include <WiFi.h>
Создайте структуру для получения данных. Эта структура должна совпадать с определённой в скетче отправителя.
typedef struct struct_message {
char a[32];
int b;
float c;
bool d;
} struct_message;
Создайте переменную struct_message с именем myData.
struct_message myData;
Создайте функцию обратного вызова, которая будет вызвана, когда ESP32 получит данные через ESP-NOW. Функция называется OnDataRecv() и должна принимать несколько параметров следующим образом:
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
Мы копируем содержимое переменной incomingData в переменную myData.
memcpy(&myData, incomingData, sizeof(myData));
Теперь структура myData содержит несколько переменных с значениями, отправленными ESP32-отправителем. Чтобы получить доступ к переменной a, например, нам просто нужно обратиться к myData.a.
В этом примере мы просто выводим полученные данные, но в практическом приложении вы можете отобразить данные на дисплее, например.
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Char: ");
Serial.println(myData.a);
Serial.print("Int: ");
Serial.println(myData.b);
Serial.print("Float: ");
Serial.println(myData.c);
Serial.print("Bool: ");
Serial.println(myData.d);
Serial.println();
В setup() инициализируйте Serial Monitor.
Serial.begin(115200);
Установите устройство в режим Wi-Fi станции.
WiFi.mode(WIFI_STA);
Инициализируйте ESP-NOW:
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
Зарегистрируйте функцию обратного вызова, которая будет вызвана при получении данных. В данном случае мы регистрируем ранее созданную функцию OnDataRecv().
esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));
Тестирование связи ESP-NOW
Загрузите скетч отправителя на плату ESP32-отправитель, а скетч получателя — на плату ESP32-получатель.
Теперь откройте два окна Arduino IDE. Одно для получателя, другое для отправителя. Откройте Serial Monitor для каждой платы. Это должен быть разный COM-порт для каждой платы.
Вот что вы должны увидеть на стороне отправителя.
А вот что вы должны увидеть на стороне получателя. Обратите внимание, что переменная Int меняется между каждым полученным сообщением (потому что на стороне отправителя мы установили случайное число).
Мы протестировали дальность связи между двумя платами и смогли получить стабильную связь на расстоянии до 220 метров (приблизительно 722 фута) на открытой местности. В этом эксперименте встроенные антенны обеих ESP32 были направлены друг на друга.
Заключение
Мы постарались сделать наши примеры максимально простыми, чтобы вы лучше поняли, как всё работает. Существуют и другие функции, связанные с ESP-NOW, которые могут быть полезны в ваших проектах, такие как: управление пирами, удаление пиров, сканирование ведомых устройств и т.д. Для полного примера в Arduino IDE вы можете перейти в File > Examples > ESP32 > ESPNow и выбрать один из примеров скетчей.
Мы надеемся, что это введение в ESP-NOW было полезным для вас. В качестве простого примера для начала мы показали, как отправить данные в виде структуры с одной ESP32 на другую. Идея состоит в том, чтобы заменить значения структуры показаниями датчиков или состояниями GPIO, например.
Кроме того, с ESP-NOW каждая плата может одновременно быть отправителем и получателем. Одна плата может отправлять данные на несколько плат, а также получать данные от нескольких плат.
У нас также есть руководство по ESP-NOW с ESP8266: Начало работы с ESP-NOW (ESP8266 NodeMCU с Arduino IDE).
Для изучения дополнительных материалов о плате ESP32 обязательно ознакомьтесь с нашими ресурсами:
Спасибо за чтение.
Источник: Getting Started with ESP-NOW (ESP32 with Arduino IDE)