ESP32 NOW:简化设备间Wi-Fi通信的新方式

介绍

ESP32 NOW 是 Espressif 推出的一种无需连接路由器的 Wi-Fi 通信方式。NOW 是 Neighbor Of WiFi(Wi-Fi 的邻居)的缩写,它允许 ESP32 设备之间直接通过 Wi-Fi 进行通信,而无需连接到传统的 Wi-Fi 路由器。这种通信方式非常适用于在没有现成网络基础设施的环境中进行设备之间的快速、直接通信。

以下是 ESP32 NOW 的一些主要特点和优势:

  1. 直连通信: ESP32 NOW 允许设备直接通过 Wi-Fi 进行通信,无需连接到路由器。这使得设备之间可以快速建立点对点的通信,而不需要中间设备。

  2. 低功耗: ESP32 NOW 被设计为能够在低功耗环境下工作,适用于需要长时间运行并希望最小化能耗的应用场景。

  3. 简化配置: 与连接到传统 Wi-Fi 路由器相比,ESP32 NOW 的配置更为简单,减少了设备连接的复杂性。

  4. 实时性: ESP32 NOW 提供了相对较低的延迟,适用于对通信实时性要求较高的应用。

  5. 适用范围: 由于它不依赖于现有的网络基础设施,ESP32 NOW 特别适用于一些特殊场景,比如在没有可用 Wi-Fi 路由器的工业环境或临时网络配置中使用。

在 ESP32 NOW 中,一个设备可以充当 “主”(Master)角色,而其他设备则可以充当 “从”(Slave)角色。主设备负责发起通信并传递数据,而从设备则接收数据。这种直连通信方式使得 ESP32 设备可以在不依赖于传统网络基础设施的情况下,实现简单而可靠的设备间通信。

基本方法


获取MAC地址

在使用NOW前我们需要提前获取MAC地址

#include "WiFi.h"
 
void setup(){
  Serial.begin(115200);
  WiFi.mode(WIFI_MODE_STA);
  Serial.print("MAC地址: ");
  Serial.println(WiFi.macAddress());
}
 
void loop(){

}

有些开发板可能没有串口转USB,也可以用tcp服务获取MAC地址

#include <Arduino.h>
#include <WiFi.h>
// ESPnow从机 MAC地址

// tcp服务端对象
  WiFiServer server(1133);
void setup()
{
  const char *ssid = "esp32_tcp";
  const char *password = "987654321";
  
  WiFi.softAP(ssid, password);
  server.begin();
}

void loop()
{
   // 循环检测有无客户端  有请求-能连接-有数据
  WiFiClient client = server.available(); // client指向发出请求的客户端对象
  if (client)
  {
   //当我们发送信息时返回MAC
    while (client.connected())
    {
      // 连接成功进入循环
      if (client.available())//检测有无数据可读
      {
        client.print(WiFi.macAddress()); // 回发数据给客户端
      }
    }
  }
  else
  {
    client.stop();
    delay(2000);
  }
}

主设备(Master)

在 Arduino 框架下使用 ESP32 NOW ,包括配置设备的主从角色、初始化 NOW 功能、以及使用相应的 API 进行数据传输。以下是一个基本的使用方法步骤:

  1. 包含必要的库:

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

    引入了 ESP32 的 Wi-Fi 库和 ESP-NOW 库。

  2. 定义 ESP32 NOW 的 Peer 信息:

    esp_now_peer_info_t peerInfo;
    

    创建一个结构体 peerInfo 来存储 ESP32 NOW Peer 设备的信息,包括 MAC 地址等。

  3. 初始化 ESP32 NOW:

    if (esp_now_init() != ESP_OK) {
      Serial.println("初始化 ESP32 NOW 失败");
      return;
    }
    

    使用 esp_now_init() 初始化 ESP32 NOW 功能,检查是否初始化成功。

  4. 配置为主设备:

    esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
    

    将 ESP32 设备配置为主设备,即控制器角色。

  5. 设置 Peer 设备的 MAC 地址:

    uint8_t peerMac[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
    memcpy(peerInfo.peer_addr, peerMac, 6);
    

    设置 Peer 设备的 MAC 地址,这个需要替换成实际设备的 MAC 地址。

  6. 添加 Peer 设备:

    if (esp_now_add_peer(&peerInfo) != ESP_OK) {
      Serial.println("添加 Peer 设备失败");
      return;
    }
    

    使用 esp_now_add_peer() 添加 Peer 设备到 NOW 网络中。

  7. 在循环中发送数据:

    esp_err_t result = esp_now_send(peerInfo.peer_addr, data, dataLength);
    

    使用 esp_now_send() 向 Peer 设备发送数据。在示例中,数据是一个包含 “Hello, ESP32 NOW!” 的字符串。

  8. 处理发送结果:

    if (result == ESP_OK) {
      Serial.println("数据发送成功");
    } else {
      Serial.println("数据发送失败");
    }
    

    根据发送结果输出相应的信息。

完整例程

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

// 定义 ESP32 NOW 的 Peer信息
esp_now_peer_info_t peerInfo;

void setup() {
  Serial.begin(115200);

  // 初始化 ESP32 NOW 功能
  if (esp_now_init() != ESP_OK) {
    Serial.println("初始化 ESP32 NOW 失败");
    return;
  }

  // 配置为主设备
  esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);

  // 设置 Peer 设备的 MAC 地址
  // 注意:需要替换成实际设备的 MAC 地址
  uint8_t peerMac[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
  memcpy(peerInfo.peer_addr, peerMac, 6);

  // 添加 Peer 设备
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("添加 Peer 设备失败");
    return;
  }
}

void loop() {
  // 示例:发送数据
  String message = "Hello, ESP32 NOW!";
  const uint8_t* data = (const uint8_t*)message.c_str();//将String字符串转换为c风格的字符串
  int dataLength = message.length() + 1; // 计算长度包括 null 终止符

  // 向 Peer 设备发送数据
  esp_err_t result = esp_now_send(peerInfo.peer_addr, data, dataLength);

  if (result == ESP_OK) {
    Serial.println("数据发送成功");
  } else {
    Serial.println("数据发送失败");
  }

  delay(5000); // 每隔5秒发送一次数据
}

通过这样的处理,我们将字符串消息转换为适合 ESP32 NOW 发送的字节数组,并且在发送时可以使用 dataLength 来指定要发送的数据的长度。在接收端,可以根据数据的长度来正确解析接收到的字节数组。

从设备(Slave)

处理从设备接收数据涉及到设置从设备的角色、注册接收回调函数以及在回调函数中处理接收到的数据。下面是从设备的示例,展示了如何设置从设备角色以及处理接收到的数据:

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

// 定义 ESP32 NOW 的 Peer 信息
esp_now_peer_info_t peerInfo;

// 回调函数,用于处理接收到的数据
void onDataReceived(const uint8_t *mac, const uint8_t *data, int dataLength) {
  Serial.print("从设备接收到数据: ");
  for (int i = 0; i < dataLength; i++) {
    Serial.print((char)data[i]);
  }
  Serial.println();
}

void setup() {
  Serial.begin(115200);

  // 初始化 ESP32 NOW 功能
  if (esp_now_init() != ESP_OK) {
    Serial.println("初始化 ESP32 NOW 失败");
    return;
  }

  // 配置为从设备
  esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);

  // 设置 Peer 设备的 MAC 地址
  // 注意:需要替换成实际设备的 MAC 地址
  uint8_t peerMac[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC};
  memcpy(peerInfo.peer_addr, peerMac, 6);

  // 添加 Peer 设备
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("添加 Peer 设备失败");
    return;
  }

  // 注册接收回调函数
  esp_now_register_recv_cb(onDataReceived);
}

void loop() {
  // 从设备不发送数据,主设备发送数据
  delay(5000);
}

在这个示例中,主要的修改包括:

  1. 配置为从设备:

    esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
    

    将 ESP32 设备配置为从设备,即被控制器角色。

  2. 注册接收回调函数:

    esp_now_register_recv_cb(onDataReceived);
    

    使用 esp_now_register_recv_cb() 注册一个回调函数,当从设备接收到数据时,该函数会被调用。

  3. 接收回调函数的实现:

    void onDataReceived(const uint8_t *mac, const uint8_t *data, int dataLength) {
      Serial.print("从设备接收到数据: ");
      for (int i = 0; i < dataLength; i++) {
        Serial.print((char)data[i]);
      }
      Serial.println();
    }
    

    onDataReceived 是一个自定义的回调函数,用于处理接收到的数据。在这个简单的例子中,它将接收到的数据打印到串口。

常用API


ESP32 NOW 的一些核心 API,用于配置、发送和接收数据:

  1. 初始化 ESP32 NOW:

    esp_now_init();
    
    • 用于初始化 ESP32 NOW 功能。
    • 返回 ESP_OK 表示初始化成功。
  2. 设置设备角色:

    esp_now_set_self_role(role);
    
    • 用于设置设备的角色,包括 ESP_NOW_ROLE_CONTROLLER(主设备)和 ESP_NOW_ROLE_SLAVE(从设备)。
    • 返回 ESP_OK 表示设置成功。
  3. 定义 Peer 设备信息:

    esp_now_peer_info_t peerInfo;
    
    • 用于定义 Peer 设备的信息结构体。
  4. 添加 Peer 设备:

    esp_now_add_peer(&peerInfo);
    
    • 用于将 Peer 设备添加到 NOW 网络中。
    • 返回 ESP_OK 表示添加成功。
  5. 注册接收回调函数:

    esp_now_register_recv_cb(onDataReceived);
    
    • 用于注册一个回调函数,当设备接收到数据时调用该函数。
  6. 发送数据:

    esp_now_send(peerMac, data, dataLength);
    
    • 用于向指定的 Peer 设备发送数据。
    • peerMac 是目标设备的 MAC 地址,data 是要发送的数据,dataLength 是数据长度。
    • 返回 ESP_OK 表示发送成功。
  7. 接收回调函数的实现:

    void onDataReceived(const uint8_t *mac, const uint8_t *data, int dataLength);
    
    • 用户自定义的回调函数,用于处理接收到的数据。
    • mac 是发送数据的设备的 MAC 地址,data 是接收到的数据,dataLength 是数据长度。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐