# 使用ESP-NOW协议

## 1. 功能简介

ESP-NOW 是由乐鑫开发的另一款无线通信协议，可以使多个设备在没有或不使用 Wi-Fi 的情况下进行通信。这种协议类似常见于无线鼠标中的低功耗 2.4GHz 无线连接——设备在进行通信之前要进行配对。配对之后，设备之间的连接是持续的、点对点的，并且不需要握手协议。它是一种短数据传输、无连接的快速通信技术，可以让低功耗控制器直接控制所有智能设备而无需连接路由器，适用于智能灯、遥控控制、传感器数据回传等场景。

使用了 ESP-NOW 通信之后，如果某一个设备突然断电之后，只要它一旦重启，就是自动连接到对应的节点中重新进行通信。

ESP-NOW 的通信模式支持如下：

* 一对一通信
* 一对多通信
* 多对一通信
* 多对多通信

ESP-NOW 支持如下特性：

* 单播包加密或单播包不加密通信；
* 加密配对设备和非加密配对设备混合使用；
* 可携带最长为 250 字节的有效 payload 数据；
* 支持设置发送回调函数以通知应用层帧发送失败或成功。

同时，ESP-NOW 也存在一些限制：

* 暂时不支持广播包；
* 加密配对设备有以下限制：
  * Station 模式下最多支持10 个加密配对设备；
  * SoftAP 或 SoftAP + Station 混合模式下最多支持 6 个加密配对设备；
  * 非加密配对设备支持若干，与加密设备总数和不超过 20 个；
* 有效 payload 限制为 250 字节。

Petoi群控完全可以采用ESP8266模块的ESP-NOW通信功能。

## 2. 准备工作

### 2.1 硬件准备

本案例准备2只Bittle（配备WiFi模块），一台连接有ESP模块的计算机。

<figure><img src="/files/cOizKeyAcRIbsv0mzZiB" alt=""><figcaption></figcaption></figure>

图中模块的程序烧录和MAC地址的获取见下文。

### 2.2 软件准备

电脑安装Thonny，方便进行8266模块的MicroPython的调试。 使用ESP-NOW协议时，需要特殊的MicroPython固件（见[Github](https://github.com/glenn20/micropython-espnow-images)）。因为普通版本的8266-MicroPython固件会提示找不到库。

打开Thonny并使用USB上载器连接ESP8266模块，在shelll界面中输入：

```python
import espnow
```

如果出现错误提示`如“找不到espnow”模块`，表示固件烧录有问题；若没有任何提示，表示固件烧录正常。

{% hint style="info" %}
如果烧录完ESP-NOW固件后，在shelll界面未出现Python标志性&#x7684;**`>>>`**&#x7B26;号，表示固件烧录失败。可以尝试使用Flash烧录工具 [NodeMCU-PyFlasher.exe](https://github.com/marcelstoer/nodemcu-pyflasher/releases/tag/v5.0.0)，烧录配置如下图所示：![](/files/6jz3YD29vTx6TJDFXRcH)
{% endhint %}

## 3. 代码简介

群控代码分为3部分：

* 查询模块的MAC地址
* 发射端程序
* 接收端程序

### 3.1 查询模块的MAC地址

MAC地址是一个用来确认网络设备位置的位址，由OSI网络模型第二层（数据链路层）负责。 MAC地址也叫物理地址、硬件地址，由网络设备制造商生产时烧录在网卡的非易失存储器（如EEPROM）中。

MAC地址的长度为48位(6个字节)，通常表示为12个16进制数。 其中前3个字节代表网络硬件制造商的编号，它由IEEE(电气与电子工程师协会)分配， 而后3个字节代表该制造商所制造的某个网络产品(如网卡)的系列号。 只要不更改自己的MAC地址，MAC地址在世界是唯一的。形象地说，MAC地址就如同身份证上的身份证号码，具有唯一性。

ESPNOW最简单的使用方式是通过MAC地址发送。我们使用一个小的程序来查询模块的MAC地址。

```python
import ubinascii
import network

wlan_sta = network.WLAN(network.STA_IF)
wlan_sta.active(True)
wlan_mac = wlan_sta.config('mac')
print(ubinascii.hexlify(wlan_mac).decode())
```

在Thonny中运行后，在终端打印出MAC地址。此时可以用一个不干胶贴写上模块的MAC地址并贴在模块上。

### 3.2 发射端程序

发射端程序由以下几个部分组成：

* 启用模块的WiFi功能
* 配置ESP-NOW协议并启用
* 添加需要通信的节点（peer）
* 发送消息

具体程序代码如下：

```python
import network
import espnow
import time

sta = network.WLAN(network.STA_IF)    # Enable station mode for ESP
sta.active(True)
sta.disconnect()        # Disconnect from last connected WiFi SSID

e = espnow.ESPNow()     # Enable ESP-NOW
e.active(True)

peer1 = b'\xe8\x68\xe7\x4e\xbb\x19'   # MAC address of peer1's wifi interface
e.add_peer(peer1)                     # add peer1 (receiver1)

peer2 = b'\x60\x01\x94\x5a\x9c\xf0'   # MAC address of peer2's wifi interface
e.add_peer(peer2)                     # add peer2 (receiver2)

print("Starting...")            # Send to all peers

e.send(peer1, "walk", True)     # send commands to pear 1
e.send(peer2, "walk", True)     # send commands to pear 2
time.sleep_ms(2000)
e.send(peer1, "walk", True)
e.send(peer2, "back", True)
time.sleep_ms(2000)
```

### 3.3 接收端程序

接收端程序主要由以下几个部分组成

* 启用模块的WiFi功能
* 配置ESP-NOW协议并启用
* 添加需要通信的节点（peer）
* 接收消息并解码，通过串口给NyBoard发送指令

具体程序代码如下：

```python
import network
import espnow
from machine import UART

def espnow_rx():
    #config UART
    uart = UART(0, baudrate=115200)

    # A WLAN interface must be active to send()/recv()
    sta = network.WLAN(network.STA_IF)
    sta.active(True)
    sta.disconnect()                # Disconnect from last connected WiFi SSID

    e = espnow.ESPNow()                  # Enable ESP-NOW
    e.active(True)

    peer = b'\x5c\xcf\x7f\xf0\x06\xda'   # MAC address of peer's wifi interface
    e.add_peer(peer)                     # Sender's MAC registration

    while True:
        host, msg = e.recv()
        if msg:                          # wait for message
            if msg == b'walk':           # decode message and translate
                uart.write("kwkF")       # to the NyBoard's command
            elif msg == b'back':
                uart.write('kbk')
            elif msg == b'stop':
                uart.write('d')

if __name__ == "__main__":
    espnow_rx()
    
```

此代码封装于一个名为`espnow_rx()`的函数中是为了方便上电后自动启动程序。

实现上电自动启动的方法有以下两种：

* 将代码文件改名为`main.py`；
* `修改boot.py`文件；

&#x20;对于新手而言，我们更推荐第一种方法。

### 3.4 通信-指令转换程序

将串口命令转换写在接收端会使程序过于复杂且不易维护。我们可以新建一个函数在其中进行指令转换并输出命令。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.petoi.com/chinese/api/micropython-kong-zhi-qi/shi-yong-espnow-xie-yi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
