ESP32 NOW 通信介绍和一对一单向通信应用实例(一发一收)

ESP-NOW 是乐鑫开发的一种无连接通信协议,沒有握手协议(CHAP),具有短包传输的特点。该协议使多个设备能够在不使用Wi-Fi的情况下以简单的方式进行相互通讯,类似于低功耗 2.4GHz 无线连接,设备之间的配对是在它们通信之前就需要配对好在代码中。配对完成后,连接是安全的,持久的,点对点的。换句话说,如果你的一块ESP32板突然断电或重置,当它重新启动时,它就会自动连接到它的已经提前配对好的网路中以继续通信。

凌顺实验室(lingshunlab.com)介绍该通信协议(ESPNOW)特点及应注意之处,并演示ONE-WAY模式的ESPNOW通信代码。

ESP-NOW 协议功的能或特性

  1. 加密和未加密的单播通信;
  2. 混合加密和未加密对等设备;
  3. 最多可承载250 字节的有效载荷;
  4. 发送回调函数,可设置通知应用层传输成功或失败。

ESP-NOW 协议的限制

  1. 在有限的加密对等点:

    • Station模式最多支持10个设备的加密对等点网络通讯;
    • SoftAP或SoftAP+Station模式下最多支持6个设备;
  2. 多个未加密对等点:

    • 其总数应少于 20 个设备
    • 有效载荷限制为 250 字节。

凌顺实验室(lingshunlab.com)通过试验2个ESP32开发板在空旷区域中进行单向通讯,相隔30米仍然能很好低接收信息。

在开发过程中,注意以上特性,就能避免踩坑。


准备

2块 ESP32开发板

一块用于接受信号;
一块用于发送信号。

单向通信(one-way)示意图

esp32-now-introduce-and-one-way-communication-sketch-map_c

一块ESP32作为发送端 ,发送信号给另一块作为接收端的ESP32

实验效果

在串口中各自输出2块ESP32的数据,如下图:

esp32-now-introduce-and-one-way-communication-result_c

ESPNOW 发送端代码关键

1,加载需要的库

#include <esp_now.h>
#include <WiFi.h>

2,需要要知道接收数据的ESP32的MAC地址

// 定义接收端的mac地址,这里的地址请替换成接收端ESP32的MAC地址
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

获取ESP32 MAC地址的方法请参考:
https://lingshunlab.com/book/esp32/esp32-mac-address-get-and-set

3,需要定义与接收端一样的的数据结构体

// 发送数据的结构示例
// 在C中使用 typedef struct 定义一个结构体类型,名为struct_message
// 必须与接收方的结构相匹配一致
typedef struct struct_message {
  char a[40];
  int b;
  float c;
  bool d;
} struct_message;

4,初始化ESPNOW

// 设置设备WIFI模式为WIFI_STA
WiFi.mode(WIFI_STA);

esp_now_init();

5,配置并注册对等点(peer)

  // 配置对等(对等点)网络
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;

  // 添加对等(对等点)网络       
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }

6, 声明回调函数

// 当发送信息时,触发的回调函数
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");
}

  // 当ESPNOW初始化成功,我们将会注册一个回调函数(callback,CB)
  // 获得数据包的发送情况
  esp_now_register_send_cb(OnDataSent);

发送端(slave/sender)完整代码

// Welcome to lingshunlab.com
// 完整说明 :https://lingshunlab.com/book/esp32/esp32-now-introduce-and-one-way-communication

// 加载需要的库 
#include <esp_now.h>
#include <WiFi.h>

// 定义接收端的mac地址,这里的地址请替换成接收端ESP32的MAC地址
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

// 发送数据的结构示例
// 在C中使用 typedef struct 定义一个结构体类型,名为struct_message
// 必须与接收方的结构相匹配一致
typedef struct struct_message {
  char a[40];
  int b;
  float c;
  bool d;
} struct_message;

// 创建 结构为struct_message的myData变量
struct_message myData;

// 声明对等网络信息实体类变量
esp_now_peer_info_t peerInfo;

// 当发送信息时,触发的回调函数
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() {
  // 设置串口波特率
  Serial.begin(115200);

  // 设置设备WIFI模式为WIFI_STA
  WiFi.mode(WIFI_STA);

  // 初始化ESPNOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // 当ESPNOW初始化成功,我们将会注册一个回调函数(callback,CB)
  // 获得数据包的发送情况
  esp_now_register_send_cb(OnDataSent);

  // 配置对等(对等点)网络
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;

  // 添加对等(对等点)网络       
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}

void loop() {
  // 赋值需要发送的变量数据 
  strcpy(myData.a, "Welcome to Lingshunlab.com");
  myData.b = random(1,20);
  myData.c = 1.2;
  myData.d = false;

  // 通过ESPNOW发送信息
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));

  // 根据 result 返回结果判断是否发送成功
  if (result == ESP_OK) {// 当 发送成功 时
    Serial.println("Sent with success");
  }
  else { // 当 发送失败 时
    Serial.println("Error sending the data");
  }
  delay(2000);
}

ESPNOW 接收端代码关键

接收端的代码相对发送端会简单一些,少了一些步骤,当然这只是在One-Way模式下的代码。

1,加载需要的库

#include <esp_now.h>
#include <WiFi.h>

2,需要定义与接收端一样的的数据结构体

// 发送数据的结构示例
// 在C中使用 typedef struct 定义一个结构体类型,名为struct_message
// 必须与接收方的结构相匹配一致
typedef struct struct_message {
  char a[40];
  int b;
  float c;
  bool d;
} struct_message;

3,初始化ESPNOW

// 设置设备WIFI模式为WIFI_STA
WiFi.mode(WIFI_STA);

esp_now_init();

4,声明回调函数

  // 当ESPNOW初始化成功,我们将会注册一个回调函数(callback,CB)
  // 获得回收的包装信息
  esp_now_register_recv_cb(OnDataRecv);

接收端(master/receiver )完整代码

// Welcome to lingshunlab.com
// 完整说明 :https://lingshunlab.com/book/esp32/esp32-now-introduce-and-one-way-communication

// 加载需要的库 
#include <esp_now.h>
#include <WiFi.h>

// 接收数据的结构示例
// 在C中使用 typedef struct 定义一个结构体类型,名为struct_message
// 必须与发送方的结构相匹配一致
typedef struct struct_message {
    char a[40];
    int b;
    float c;
    bool d;
} struct_message;

// 创建 结构为struct_message的myData变量
struct_message myData;

// 当收到数据时将执行的回调函数
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() {
  // 设置串口波特率
  Serial.begin(115200);

  // 设置设备WIFI模式为WIFI_STA
  WiFi.mode(WIFI_STA);

  // 初始化ESPNOW
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // 当ESPNOW初始化成功,我们将会注册一个回调函数(callback,CB)
  // 获得回收的包装信息
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {

}

参考

https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/