Время чтения: 15 минут | Технология: IoT Development | Платформы: ESP8266, ESP32
В предыдущих статьях мы заложили фундамент: разобрали Введение в ESP-NOW: Беспроводная связь на ESP8266, научились писать кроссплатформенный код в Arduino IDE и рассмотрели базовое управление светодиодом с кнопки.
Однако при переносе теории в практику разработчики часто сталкиваются с тремя критическими проблемами:
0 против ESP_OK) вызывают ошибки компиляции.
В этой статье мы публикуем полностью протестированную, исправленную и рабочую версию кроссплатформенного скетча. Этот код учитывает все аппаратные и программные нюансы, позволяя использовать ESP8266 и ESP32 в одной связке без единой ошибки.
A0:20:A6:1A:D3:86).Скопируйте этот код в файл main.ino в Arduino IDE. Ниже мы подробно разберем каждый блок.
/*
* Беспроводное управление LED по ESP-NOW
* КРОСС-ПЛАТФОРМЕННАЯ ВЕРСИЯ: Полная совместимость ESP32 и ESP8266
* ИСПРАВЛЕННАЯ ВЕРСИЯ: Совместима с Arduino ESP32 Core v2.x и v3.x
*/
// ==========================================
// 1. УПРАВЛЕНИЕ РЕЖИМОМ
// ==========================================
// Для прошивки ПРИЕМНИКА: Закомментируйте эту строку ( // #define IS_SENDER )
// Для прошивки ОТПРАВИТЕЛЯ: Раскомментируйте эту строку ( #define IS_SENDER )
//#define IS_SENDER
// MAC-адрес ПРИЕМНИКА.
uint8_t receiverMac[] = {0xA0, 0x20, 0xA6, 0x1A, 0xD3, 0x86}; // <-- ЗАМЕНИТЕ НА СВОЙ!
// ==========================================
// 2. ПОДКЛЮЧЕНИЕ БИБЛИОТЕК И ПИНОВ
// ==========================================
#ifdef ESP32
#include <WiFi.h>
#include <esp_wifi.h>
#include <esp_now.h>
#define BOARD_NAME "ESP32"
#define LED_PIN 2
#define BTN_PIN 4
#define LED_ON HIGH
#define LED_OFF LOW
#else
#include <ESP8266WiFi.h>
#include <espnow.h>
#define BOARD_NAME "ESP8266"
#define LED_PIN LED_BUILTIN
#define BTN_PIN 0
#define LED_ON LOW
#define LED_OFF HIGH
#endif
#include <string.h> // Для memcpy и memset
// Структура данных
typedef struct {
bool ledState;
} Message;
Message myData;
// Переменные для антидребезга кнопки
bool lastBtnState = HIGH;
bool stableBtnState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
// ==========================================
// 3. CALLBACK-ФУНКЦИИ
// ==========================================
#ifdef ESP32
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.println(status == ESP_NOW_SEND_SUCCESS ? " Доставлено" : " Ошибка");
}
// Функция приема данных (Совместимость с ядром ESP32 v3.x.x)
// Если у вас старое ядро (2.x.x), замените строку ниже на: void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len)
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
memcpy(&myData, incomingData, sizeof(myData));
digitalWrite(LED_PIN, myData.ledState ? LED_ON : LED_OFF);
Serial.println(myData.ledState ? " Прием: LED ON" : " Прием: LED OFF");
}
#else
// Функции для ESP8266
void OnDataSent(uint8_t *mac_addr, uint8_t status) {
Serial.println(status == 0 ? " Доставлено" : " Ошибка");
}
void OnDataRecv(uint8_t *mac, uint8_t *incomingData, uint8_t len) {
memcpy(&myData, incomingData, sizeof(myData));
digitalWrite(LED_PIN, myData.ledState ? LED_ON : LED_OFF);
Serial.println(myData.ledState ? " Прием: LED ON" : " Прием: LED OFF");
}
#endif
// ==========================================
// 4. НАСТРОЙКА (setup)
// ==========================================
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(BTN_PIN, INPUT_PULLUP);
digitalWrite(LED_PIN, LED_OFF);
// --- КРИТИЧЕСКИЙ БЛОК ДЛЯ ESP-NOW ---
WiFi.mode(WIFI_STA);
WiFi.disconnect();
#ifdef ESP8266
wifi_set_channel(1);
#else
esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
#endif
// ------------------------------------
Serial.println("=================================");
Serial.print("Запуск платы: "); Serial.println(BOARD_NAME);
Serial.print(" Мой MAC-адрес: "); Serial.println(WiFi.macAddress());
// --- ИСПРАВЛЕННАЯ ПРОВЕРКА ИНИЦИАЛИЗАЦИИ ---
// ESP32 использует esp_err_t (ESP_OK), а ESP8266 возвращает int (0)
#ifdef ESP32
if (esp_now_init() != ESP_OK) {
#else
if (esp_now_init() != 0) {
#endif
Serial.println(" Ошибка инициализации ESP-NOW!");
return; // Прерываем setup, если инициализация не удалась
}
Serial.println(" ESP-NOW инициализирован успешно.");
// -------------------------------------------
#ifdef IS_SENDER
Serial.println(" РЕЖИМ: ОТПРАВИТЕЛЬ");
#ifdef ESP32
esp_now_register_send_cb(OnDataSent);
esp_now_peer_info_t peerInfo;
memset(&peerInfo, 0, sizeof(peerInfo));
memcpy(peerInfo.peer_addr, receiverMac, 6);
peerInfo.channel = 1;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println(" Ошибка добавления пира!");
}
#else
// Настройка для ESP8266
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
esp_now_register_send_cb(OnDataSent);
esp_now_add_peer(receiverMac, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
#endif
#else
Serial.println(" РЕЖИМ: ПРИЕМНИК (Ожидание данных...)");
#ifdef ESP32
esp_now_register_recv_cb(OnDataRecv);
#else
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
esp_now_register_recv_cb(OnDataRecv);
#endif
#endif
Serial.println("=================================");
}
// ==========================================
// 5. ОСНОВНОЙ ЦИКЛ (loop)
// ==========================================
void loop() {
#ifdef IS_SENDER
bool currentBtnState = digitalRead(BTN_PIN);
if (currentBtnState != lastBtnState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (currentBtnState != stableBtnState) {
stableBtnState = currentBtnState;
if (stableBtnState == LOW) {
myData.ledState = !myData.ledState;
Serial.print(" Отправка: ");
Serial.println(myData.ledState ? "ON" : "OFF");
esp_now_send(receiverMac, (uint8_t *) &myData, sizeof(myData));
}
}
}
lastBtnState = currentBtnState;
#endif
}

#define IS_SENDERВместо того чтобы писать два разных скетча (один для передатчика, другой для приемника), мы используем директиву препроцессора.
#define IS_SENDER раскомментирована (без слешей // в начале), компилятор соберет код для платы-кнопки (передатчика). Функция loop() будет опрашивать кнопку и отправлять пакеты.//#define IS_SENDER), компилятор проигнорирует весь код отправки. Плата превратится в приемник, который просто ждет пакеты в фоновом режиме через callback-функции.ESP-NOW работает на физическом уровне Wi-Fi. Главное правило: оба устройства должны находиться на одном радиоканале. При включении ESP-платы могут автоматически выбрать разные каналы (например, одна на 1, другая на 6). В таком случае связь не установится, и вы будете видеть Ошибка при отправке.
В блоке
setup() мы принудительно отключаем платы от любых сетей и жестко задаем 1-й канал:
wifi_set_channel(1);esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE); При добавлении пира (приемника) на стороне ESP32 мы также указываем: peerInfo.channel = 1;. Это гарантирует 100% стабильность связи.Недавно вышло ядро ESP32 версии 3.0, которое сломало обратную совместимость многих старых скетчей ESP-NOW. Изменились типы данных в функциях обратного вызова (Callback).
В нашем коде для ESP32 используется современный формат:
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len)
Если по какой-то причине вы используете старую версию ядра (2.0.x) и получаете ошибку компиляции, просто уберите ключевые слова const в аргументах функции. Для ESP8266 формат остается неизменным годами (uint8_t *mac).
ESP32 и ESP8266 по-разному сообщают об успехе:
0 при успехе.ESP_OK. Блок #ifdef ESP32 ... #else ... #endif при проверке esp_now_init() позволяет коду компилироваться без ошибок на обеих платформах, выдавая понятные логи в Serial-порт. Аналогично, при добавлении пира на ESP32 используется функция memset(&peerInfo, 0, sizeof(peerInfo));. Она очищает оперативную память от "мусора" перед записью MAC-адреса. Без этого могут происходить случайные сбои шифрования или передачи.Обратите внимание на макросы:
#define LED_ON HIGH // Для ESP32 #define LED_ON LOW // Для ESP8266
Это избавляет вас от необходимости писать сложный код. Вы просто говорите микроконтроллеру digitalWrite(LED_PIN, LED_ON);, а макрос сам подставляет нужное значение напряжения в зависимости от выбранной платы.
Для работы вам понадобятся две платы (например, одна ESP32 и одна ESP8266, либо две одинаковых).
#define IS_SENDER закомментирована: //#define IS_SENDEREN (Reset) на плате. В мониторе порта появится текст:
=================================
Запуск платы: ESP32
Мой MAC-адрес: 3C:71:BF:F9:52:3C
ESP-NOW инициализирован успешно.
РЕЖИМ: ПРИЕМНИК (Ожидание данных...)
=================================
receiverMac и замените его значения на MAC-адрес, полученный на Шаге 1. Важно: замените двоеточия : на запятые , и добавьте префикс 0x к каждому байту. Было: A0:20:A6:1A:D3:86 Стало: uint8_t receiverMac[] = {0x3C, 0x71, 0xBF, 0xF9, 0x52, 0x3C};<br>#define IS_SENDER=================================
Запуск платы: ESP8266
Мой MAC-адрес: A0:20:A6:1A:D3:86
ESP-NOW инициализирован успешно.
РЕЖИМ: ОТПРАВИТЕЛЬ
=================================
FLASH, на ESP32 подключите кнопку между GPIO 4 и GND).Отправка: ON Доставлено Прием: LED ONOFF.
Используя этот универсальный шаблон, вы получаете промышленный стандарт разработки. Директивы препроцессора #ifdef берут на себя всю рутину по адаптации кода, жесткая фиксация Wi-Fi канала исключает потерю пакетов, а аппаратный антидребезг обеспечивает мгновенную и четкую реакцию системы. Этот код готов стать основой для ваших умных выключателей, пультов управления и систем сбора данных.
Дополнительные материалы:
Понравилась статья Беспроводное управление без роутера: Идеальный кроссплатформенный скетч ESP-NOW для ESP32 и ESP8266? Не забудь поделиться с друзьями в соц. сетях.
А также подписаться на наш канал на VK Видео, вступить в группу Вконтакте.
Спасибо за внимание!
Технологии начинаются с простого!
Фотографии к статье
Файлы для скачивания
| Код ESP8266 + ESP32.ino | 6 Kb | 20 | Скачать |
Статьи про ESP32, ESP8266
4 августа , 2026
Комментариев:0
Файлов для скачивания:1
Фото:6
Понравилась статья? Нажми
Читайте также
Мы в соц сетях
Комментарии