MicroPython: ESP-NOW с ESP32 — Управление несколькими платами (один-ко-многим)
В этом руководстве вы узнаете, как использовать ESP-NOW для управления несколькими платами ESP32 с одной основной платы-контроллера ESP32 (конфигурация «один-ко-многим»). Это похоже на пульт дистанционного управления, который отправляет команды нескольким устройствам. Мы покажем, как настроить одну плату ESP32 для беспроводного управления тремя другими платами с помощью протокола связи ESP-NOW — идеально для домашней автоматизации, сенсорных сетей или проектов с несколькими IoT-устройствами.
Используете Arduino IDE? Следуйте этому руководству: ESP-NOW с ESP32: отправка данных на несколько плат (один-ко-многим).
Предварительные требования — прошивка MicroPython
Для выполнения этого руководства вам нужна прошивка MicroPython, установленная на ваших платах ESP32 или ESP8266. Вам также понадобится IDE для написания и загрузки кода на плату. Мы рекомендуем использовать Thonny IDE:
Новичок в MicroPython? Ознакомьтесь с книгой: MicroPython Programming with ESP32 and ESP8266 eBook (2nd Edition)
Знакомство с ESP-NOW
ESP-NOW — это протокол беспроводной связи, разработанный Espressif, который позволяет нескольким платам ESP32 или ESP8266 обмениваться небольшими объёмами данных без использования Wi-Fi или Bluetooth. ESP-NOW не требует полного Wi-Fi-соединения (хотя контроллер Wi-Fi должен быть включён), что делает его идеальным для приложений с низким энергопотреблением и низкой задержкой, таких как сенсорные сети, пульты дистанционного управления или обмен данными между платами.
ESP-NOW использует модель связи без установления соединения, что означает, что устройства могут отправлять и получать данные без подключения к маршрутизатору или настройки точки доступа (в отличие от HTTP-связи между платами). Он поддерживает одноадресную (unicast — отправка данных на конкретное устройство по его MAC-адресу) и широковещательную (broadcast — отправка данных всем ближайшим устройствам с использованием широковещательного MAC-адреса) передачу сообщений.
Впервые работаете с ESP-NOW? Прочитайте наше руководство по началу работы: Начало работы с ESP-NOW (ESP32 с Arduino IDE).
Обзор проекта
В этом руководстве показано, как настроить плату ESP32 в качестве основного контроллера ESP-NOW, который отправляет команды нескольким платам ESP32.
Вот краткий обзор работы проекта:
Одна плата ESP32 будет работать как контроллер, отправляющий команды другим платам ESP32.
К контроллеру будут подключены три кнопки. Каждая кнопка управляет светодиодом одной из плат-приёмников.
Контроллер ESP32 отправляет сообщения конкретной плате, обращаясь к ней по MAC-адресу.
При нажатии кнопки для определённой платы текущее состояние светодиода переключается.
Платы-приёмники ESP32 будут прослушивать пакеты ESP-NOW. Когда они получают сообщение от контроллера, они управляют своим светодиодом в соответствии с полученной командой.
Необходимые компоненты
Для выполнения проекта из этого руководства вам понадобятся следующие компоненты:
4 платы ESP32 (минимум три)
3 кнопки
3 резистора 220 Ом (или аналогичные номиналы)
ESP32: Получение MAC-адреса платы
Для связи через ESP-NOW вам нужно знать MAC-адрес ваших плат. Чтобы получить MAC-адрес платы, скопируйте следующий код в Thonny IDE и запустите его на вашей плате.
# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/micropython-esp-now-esp32/
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
# Get MAC address (returns bytes)
mac = wlan.config('mac')
# Convert to human-readable format
mac_address = ':'.join('%02x' % b for b in mac)
print("MAC Address:", mac_address)
После запуска кода в консоли должен отобразиться MAC-адрес платы.
Получите MAC-адреса для всех ваших плат.
Например, в моём случае я получил:
Плата-отправитель: 24:0A:C4:31:38:5C
Плата-приёмник 1: DC:B8:15:81:2B:64
Плата-приёмник 2: 30:AE:A4:07:0D:64
Плата-приёмник 3: 30:AE:A4:F6:7D:4C
Подготовка платы-отправителя ESP32
Плата-отправитель будет подключена к трём кнопкам. Каждая кнопка будет управлять одним светодиодом на другой ESP32.
Схема подключения
Начните с подключения трёх кнопок к плате-отправителю ESP32. Мы подключим кнопки к GPIO 21, 22 и 23. Вы можете использовать любые другие подходящие GPIO (не забудьте проверить распиновку платы ESP32).
Мы будем использовать встроенные подтягивающие резисторы ESP32. Поэтому добавлять внешние резисторы в схему не нужно.
Код контроллера ESP32 ESP-NOW — отправитель
Скопируйте следующий код в Thonny IDE.
# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/micropython-esp-now-esp32-one-to-many/
import network
import aioespnow
import asyncio
from machine import Pin
# Initialize Wi-Fi in station mode
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.disconnect()
# Initialize AIOESPNow
e = aioespnow.AIOESPNow()
try:
e.active(True)
except OSError as err:
print("Failed to initialize AIOESPNow:", err)
raise
# Define pins for pushbuttons
button1_pin = Pin(21, Pin.IN, Pin.PULL_UP) # Button for Board 1
button2_pin = Pin(22, Pin.IN, Pin.PULL_UP) # Button for Board 2
button3_pin = Pin(23, Pin.IN, Pin.PULL_UP) # Button for Board 3
# Define MAC addresses for receiving boards
board1_mac = b'\xff\xff\xff\xff\xff\xff' # MAC address for Board 1
board2_mac = b'\xff\xff\xff\xff\xff\xff' # MAC address for Board 2
board3_mac = b'\xff\xff\xff\xff\xff\xff' # MAC address for Board 3
# Build dictionary with pin and MAC variables
boards = {
1: {"pin": button1_pin, "mac": board1_mac},
2: {"pin": button2_pin, "mac": board2_mac},
3: {"pin": button3_pin, "mac": board3_mac}
}
# Add peers
for board_num, info in boards.items():
try:
e.add_peer(info["mac"])
print(f"Added Board {board_num} peer: {info['mac']}")
except OSError as err:
print(f"Failed to add Board {board_num} peer: {err}")
raise
# Async function to monitor buttons and send toggle commands
async def monitor_buttons(e):
# Track LED states (True = ON, False = OFF)
led_states = {1: False, 2: False, 3: False}
while True:
for board_num, info in boards.items():
button = info["pin"]
if button.value() == 0: # Button pressed (active low)
led_states[board_num] = not led_states[board_num] # Toggle LED
command = "LED_ON" if led_states[board_num] else "LED_OFF"
if await e.asend(info["mac"], command, sync=True):
print(f"Board {board_num}: {command}")
else:
print(f"Board {board_num}: Failed to send")
await asyncio.sleep(0.3) # Debounce delay
await asyncio.sleep(0.1) # Short loop delay
# Main async function
async def main(e):
await monitor_buttons(e)
# Run the async program
try:
asyncio.run(main(e))
except KeyboardInterrupt:
print("Stopping...")
e.active(False)
sta.active(False)
Обязательно измените код, указав MAC-адреса ваших плат-приёмников.
Как работает код?
Рассмотрим, как работает код. Вы также можете перейти к следующему разделу.
Импорт библиотек
Начнём с импорта необходимых библиотек. Мы используем библиотеку aioespnow для ESP-NOW, которая является асинхронной версией библиотеки espnow.
import network
import aioespnow
import asyncio
from machine import Pin
Если вы новичок в асинхронном программировании, рекомендуем ознакомиться с этим руководством: MicroPython ESP32/ESP8266: Асинхронное программирование — запуск нескольких задач.
Инициализация Wi-Fi интерфейса и ESP-NOW
Для использования ESP-NOW сначала нужно инициализировать Wi-Fi интерфейс.
# Initialize Wi-Fi in station mode
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.disconnect()
Затем можно инициализировать протокол связи ESP-NOW.
# Initialize AIOESPNow
e = aioespnow.AIOESPNow()
try:
e.active(True)
except OSError as err:
print("Failed to initialize AIOESPNow:", err)
raise
Определение кнопок
Далее определяем пины для кнопок. Если вы используете другие GPIO для кнопок, измените соответствующим образом.
# Define pins for pushbuttons
button1_pin = Pin(21, Pin.IN, Pin.PULL_UP) # Button for Board 1
button2_pin = Pin(22, Pin.IN, Pin.PULL_UP) # Button for Board 2
button3_pin = Pin(23, Pin.IN, Pin.PULL_UP) # Button for Board 3
MAC-адреса плат-приёмников
Добавьте MAC-адреса плат-приёмников в следующие строки. MAC-адрес должен быть в формате bytes. Например:
30:AE:A4:F6:7D:4C превращается в
b'\x30\xae\xa4\xf6\x7d\x4c'
# Define MAC addresses for receiving boards
board1_mac = b'\x30\xae\xa4\x07\x0d\x64' # MAC address for Board 1
board2_mac = b'\x0c\xb8\x15\x81\x2b\x64' # MAC address for Board 2
board3_mac = b'\x30\xae\xa4\xf6\x7d\x4c' # MAC address for Board 3
Словарь плат
Мы создаём словарь, который связывает номер каждой платы с её кнопкой и MAC-адресом.
# Build dictionary with pin and MAC variables
boards = {
1: {"pin": button1_pin, "mac": board1_mac},
2: {"pin": button2_pin, "mac": board2_mac},
3: {"pin": button3_pin, "mac": board3_mac}
}
Добавление пиров
Затем мы добавляем платы-приёмники как пиры (peers).
# Add peers
for board_num, info in boards.items():
try:
e.add_peer(info["mac"])
print(f"Added Board {board_num} peer: {info['mac']}")
except OSError as err:
print(f"Failed to add Board {board_num} peer: {err}")
raise
Мониторинг кнопок и отправка сообщений
Функция monitor_buttons проверяет состояние каждой кнопки и отправляет сообщение соответствующей плате.
async def monitor_buttons(e):
Сначала мы создаём ещё один словарь для хранения состояния светодиода каждой платы.
led_states = {1: False, 2: False, 3: False}
Затем проверяем, была ли нажата кнопка.
for board_num, info in boards.items():
button = info["pin"]
if button.value() == 0: # Button pressed (active low)
led_states[board_num] = not led_states[board_num] # Toggle LED
command = "LED_ON" if led_states[board_num] else "LED_OFF"
if await e.asend(info["mac"], command, sync=True):
Поскольку мы используем кнопки в режиме active-low, когда значение кнопки равно 0, это означает, что она нажата.
if button.value() == 0: # Button pressed (active low)
Когда это происходит, мы переключаем состояние светодиода для платы, кнопка которой была нажата (в словаре led_states), и подготавливаем команду для отправки через ESP-NOW.
if button.value() == 0: # Button pressed (active low)
led_states[board_num] = not led_states[board_num] # Toggle LED
command = "LED_ON" if led_states[board_num] else "LED_OFF"
Наконец, мы вызываем метод asend() объекта espnow e для отправки сообщения соответствующей плате по её MAC-адресу.
if await e.asend(info["mac"], command, sync=True):
print(f"Board {board_num}: {command}")
else:
print(f"Board {board_num}: Failed to send")
await asyncio.sleep(0.3) # Debounce delay
Создание и запуск асинхронной задачи
Наконец, создаём асинхронную функцию main и запускаем асинхронную задачу.
# Main async function
async def main(e):
await monitor_buttons(e)
# Run the async program
try:
asyncio.run(main(e))
except KeyboardInterrupt:
print("Stopping...")
e.active(False)
sta.active(False)
Загрузка кода на плату-отправитель
После изменения кода с указанием MAC-адреса каждой платы-приёмника вы можете загрузить его на свою плату.
В Thonny IDE, при установленном соединении с платой, перейдите в File > Save as > MicroPython device.
Сохраните код с именем main.py, иначе он не будет работать. Если файл назван main.py, он автоматически запускается при перезагрузке или сбросе ESP32.
После загрузки кода на плату перезагрузите её, нажав кнопку RST или отключив и снова подключив питание. Код начнёт выполняться.
На данный момент код не будет работать, потому что мы ещё не подготовили платы-приёмники.
Подготовка плат-приёмников ESP32
Выполните эту процедуру для каждой из ваших плат-приёмников ESP32. В этом примере мы используем три платы-приёмника ESP32, но вы можете использовать только две (или даже одну), или более трёх.
Схема подключения
Подключите один светодиод к каждой из ваших плат-приёмников. Мы подключаем светодиод к GPIO 2, но вы можете подключить к любому другому GPIO.
Код приёмника ESP-NOW
Скопируйте следующий код в Thonny IDE и загрузите его на каждую из ваших плат-приёмников. Этот код будет прослушивать пакеты ESP-NOW и включать или выключать светодиод соответственно.
Важно: не забудьте изменить код, указав MAC-адрес платы-отправителя.
# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/micropython-esp-now-esp32-one-to-many/
import network
import aioespnow
import asyncio
from machine import Pin
# Initialize Wi-Fi in station mode
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.config(channel=1) # Match sender's channel
sta.disconnect()
# Initialize AIOESPNow
e = aioespnow.AIOESPNow()
try:
e.active(True)
except OSError as err:
print("Failed to initialize AIOESPNow:", err)
raise
# Sender's MAC address
sender_mac = b'\xff\xff\xff\xff\xff\xff' # You need to replace with actual sender MAC address
# Add sender as peer for more reliability
try:
e.add_peer(sender_mac)
print(f"Added sender peer: {sender_mac}")
except OSError as err:
print(f"Failed to add sender peer: {err}")
raise
# Initialize LED
led = Pin(2, Pin.OUT)
led.value(0)
# Async function to receive messages and control LED
async def receive_messages(e):
while True:
try:
async for mac, msg in e:
message = msg.decode()
print(f"Received from {mac.hex()}: {message}")
if message == "LED_ON":
led.value(1) # Turn LED on
print("LED turned ON")
elif message == "LED_OFF":
led.value(0) # Turn LED off
print("LED turned OFF")
except OSError as err:
print("Error:", err)
await asyncio.sleep(5)
# Main async function
async def main(e):
await receive_messages(e)
# Run the async program
try:
asyncio.run(main(e))
except KeyboardInterrupt:
print("Stopping receiver...")
led.value(0) # Turn off LED on exit
e.active(False)
sta.active(False)
Как работает код?
Продолжите чтение, чтобы узнать, как работает код, или перейдите к разделу демонстрации.
Импорт библиотек
Начнём с импорта необходимых библиотек.
import network
import aioespnow
import asyncio
from machine import Pin
Инициализация Wi-Fi интерфейса и ESP-NOW
Для использования ESP-NOW сначала нужно инициализировать Wi-Fi интерфейс.
# Initialize Wi-Fi in station mode
sta = network.WLAN(network.STA_IF)
sta.active(True)
sta.config(channel=1) # Match sender's channel
sta.disconnect()
Затем можно инициализировать протокол связи ESP-NOW.
# Initialize AIOESPNow
e = aioespnow.AIOESPNow()
try:
e.active(True)
except OSError as err:
print("Failed to initialize AIOESPNow:", err)
raise
Добавление отправителя как пира
Добавьте плату-отправитель как пир (peer). Не забудьте заменить на реальный MAC-адрес вашей платы-отправителя.
# Sender's MAC address
sender_mac = b'\x24\x0A\xC4\x31\x38\x5C' # Replace with actual sender MAC
# Add sender as peer for more reliability
try:
e.add_peer(sender_mac)
print(f"Added sender peer: {sender_mac}")
except OSError as err:
print(f"Failed to add sender peer: {err}")
raise
Инициализация светодиода
Инициализируем светодиод как выход на GPIO 2 и устанавливаем его начальное состояние в LOW.
# Initialize LED
led = Pin(2, Pin.OUT)
led.value(0)
Приём сообщений ESP-NOW и управление светодиодом
Асинхронная функция receive_messages() будет прослушивать сообщения ESP-NOW.
async def receive_messages(e):
while True:
try:
async for mac, msg in e:
message = msg.decode()
print(f"Received from {mac.hex()}: {message}")
Затем она включает или выключает светодиод в соответствии с полученным сообщением.
if message == "LED_ON":
led.value(1) # Turn LED on
print("LED turned ON")
elif message == "LED_OFF":
led.value(0) # Turn LED off
print("LED turned OFF")
Создание и запуск асинхронной задачи
Наконец, создаём асинхронную функцию main и запускаем асинхронную задачу.
# Main async function
async def main(e):
await receive_messages(e)
# Run the async program
try:
asyncio.run(main(e))
except KeyboardInterrupt:
print("Stopping receiver...")
led.value(0) # Turn off LED on exit
e.active(False)
sta.active(False)
Загрузка кода на платы-приёмники
После изменения кода с указанием MAC-адреса платы-отправителя вы можете загрузить предоставленный код на каждую из ваших плат-приёмников.
В Thonny IDE, при установленном соединении с платой, перейдите в File > Save as > MicroPython device.
Сохраните код с именем main.py, иначе он не будет работать. Если файл назван main.py, он автоматически запускается при перезагрузке или сбросе ESP32.
После загрузки кода на плату перезагрузите её, нажав кнопку RST или отключив и снова подключив питание. Код начнёт выполняться.
Демонстрация
Нажмите кнопки на плате-отправителе. Каждая кнопка будет переключать состояние светодиода конкретной платы. Отправитель ESP32 работает как пульт дистанционного управления для отправки индивидуальных команд каждой плате.
Вы можете посмотреть следующее видео, чтобы увидеть работу этого проекта.
Заключение
В этом руководстве вы узнали, как отправлять сообщения ESP-NOW от одного отправителя ESP32 нескольким платам. Это лишь один из множества примеров того, как можно использовать ESP-NOW в ваших IoT-проектах и проектах автоматизации.
Надеемся, что это руководство и проект были вам полезны. У нас есть другие руководства по ESP-NOW, которые могут вам понравиться: