WifiEspNow库函数详解

news2025/6/5 18:06:20

WifiEspNow库

项目地址https://github.com/yoursunny/WifiEspNow

WifiEspNow 是 ESP-NOW 的 Arduino 库,ESP-NOW 是乐鑫定义的无连接 WiFi 通信协议。 有关 ESP-NOW 工作原理及其限制的更多信息,请参阅 ESP-NOW 参考。

WifiEspNow是 ESP-IDF 中 ESP-NOW 函数的简单包装器。 在 ESP8266 上,它仅支持单播。 在 ESP32 上,它支持单播和多播。ESP-NOW支持多播,但WifiEspNow库中似乎并没有特殊函数来支持多播

WifiEspNowBroadcast 通过 ESP-NOW 实现广播。 每个设备通告一个特定的 WiFi SSID,并通过 BSSID 扫描发现彼此。 然后,消息通过 ESP-NOW 单播分别传输到每个对等体。

ESP - NOW 的伪广播机制是基于设备之间的对等连接。当设备调用 WifiEspNowBroadcast.begin() 初始化后,它会自动搜索同一通道上的其他 ESP - NOW 设备,并建立对等连接。这些连接会在后台自动管理,所以无需显式设置目标设备的 MAC 地址。所有设备都要使用相同的网络名称和通道,以此保证能相互通信。

ESP32 和 ESP8266 可以一起组网进行伪广播通信。因为伪广播是基于单播实现的,而 ESP8266 支持单播通信,ESP32 也支持单播通信,它们可以通过伪广播机制进行组网

单播和多播

在 ESP-NOW 通信中,单播(Unicast)多播(Multicast) 是两种不同的数据传输方式,它们在目标地址、通信模式和应用场景上有明显区别。

特性单播(Unicast)多播(Multicast)
目标地址单个接收方的 MAC 地址预定义的多播组 MAC 地址(例如以 01:00:5E 开头)
接收方数量1 个设备加入同一多播组的所有设备
可靠性高(有 ACK 确认)低(无 ACK 确认,不保证所有接收方收到)
效率每次发送到一个设备,开销较大单次发送到多个设备,效率高
应用场景一对一通信(如控制指令、私密数据)一对多通信(如系统广播、配置更新)
场景选择多播选择单播
同时向多个设备发送相同数据✅ 高效(一次发送)❌ 需多次发送,效率低
需要确认消息是否送达❌ 无 ACK 机制✅ 有 ACK 机制,可靠性高
系统配置更新✅ 简单直接❌ 需要逐个确认
私密数据传输❌ 所有组成员可见✅ 仅目标设备可见
网络负载✅ 低(单次传输)❌ 高(多次传输)

WifiEspNow 函数

初始化和终止函数

bool begin()

  • 功能:初始化 ESP - NOW。设置 ESP - NOW 角色、注册接收和发送回调函数。
  • 返回值:初始化成功返回 true,否则返回 false

void end()

  • 功能:停止 ESP - NOW,释放相关资源。
  • 返回值:无

密钥设置函数

bool setPrimaryKey(const uint8_t key[WIFIESPNOW_KEYLEN])

  • 功能:设置主加密密钥(KOK 或 PMK)。
  • 参数key 是一个长度为 WIFIESPNOW_KEYLEN(通常为 16 字节)的字节数组,表示加密密钥。
  • 返回值:设置成功返回 true,否则返回 false

加密密钥必须是 16 字节(128 位) 的随机字节数组。

python小工具

import secrets

# 生成一个长度为16的随机字节数组
key = secrets.token_bytes(16)

# 打印生成的密钥数组
print("static const uint8_t ENCRYPTION_KEY[WIFIESPNOW_KEYLEN]  = {", end="")
for i, byte in enumerate(key):
    if i < 15:
        print(f"0x{byte:02X}, ", end="")
    else:
        print(f"0x{byte:02X}", end="")
print("};")

在调用 WifiEspNow.begin() 之前设置密钥,确保所有设备使用相同的密钥

所有参与通信的设备必须使用相同的密钥,否则无法解密消息。

单播.ino

#include <WifiEspNow.h>


// 定义加密密钥(所有设备必须使用相同的密钥)
static const uint8_t ENCRYPTION_KEY[WIFIESPNOW_KEYLEN] = {
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
  0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};

// 接收方 MAC 地址(需要替换为实际接收方的 MAC 地址)
static uint8_t PEER_MAC[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

// 发送状态变量
WifiEspNowSendStatus lastSendStatus = WifiEspNowSendStatus::NONE;

// 接收回调函数
void onReceive(const uint8_t* mac, const uint8_t* data, size_t len, void* arg) {
  Serial.print("Received from: ");
  for (int i = 0; i < 6; i++) {
    Serial.printf("%02X:", mac[i]);
  }
  Serial.print(" Data: ");
  for (size_t i = 0; i < len; i++) {
    Serial.print((char)data[i]);
  }
  Serial.println();
}

// 发送消息函数
void sendMessage(const char* message) {
  size_t len = strlen(message);
  bool result = WifiEspNow.send(PEER_MAC, (const uint8_t*)message, len);
  
  if (result) {
    Serial.print("Message sent: ");
    Serial.println(message);
  } else {
    Serial.println("Failed to send message");
  }
  
  // 等待一段时间获取发送状态
  delay(100);
  lastSendStatus = WifiEspNow.getSendStatus();
  Serial.print("Send status: ");
  
  switch (lastSendStatus) {
    case WifiEspNowSendStatus::OK:
      Serial.println("SUCCESS");
      break;
    case WifiEspNowSendStatus::FAIL:
      Serial.println("FAILED");
      break;
    case WifiEspNowSendStatus::NONE:
      Serial.println("PENDING");
      break;
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("ESP-NOW Unicast Example (Encrypted)");

  // 设置为 STA 模式(单播通信推荐)
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  
  // 打印本机 MAC 地址
  Serial.print("My MAC address: ");
  Serial.println(WiFi.macAddress());

  // 设置加密密钥(必须在 begin() 之前调用)
  bool keySet = WifiEspNow.setPrimaryKey(ENCRYPTION_KEY);
  if (!keySet) {
    Serial.println("Failed to set encryption key!");
    ESP.restart();
  } else {
    Serial.println("Encryption key set successfully");
  }

  // 初始化 ESP-NOW
  if (!WifiEspNow.begin()) {
    Serial.println("ESP-NOW initialization failed");
    ESP.restart();
  }

  // 添加对等节点
  if (!WifiEspNow.addPeer(PEER_MAC)) {
    Serial.println("Failed to add peer");
    ESP.restart();
  } else {
    Serial.println("Peer added successfully");
  }

  // 设置接收回调
  WifiEspNow.onReceive(onReceive, nullptr);
  
  Serial.println("Ready to send/receive messages");
}

void loop() {
  // 每 5 秒发送一条消息
  static unsigned long lastSendTime = 0;
  if (millis() - lastSendTime > 5000) {
    sendMessage("Hello from encrypted ESP-NOW!");
    lastSendTime = millis();
  }
  
  // 处理其他任务
  delay(10);
}

伪广播.ino

#include <WifiEspNowBroadcast.h>


// 定义加密密钥(16 字节 = 128 位)
// 注意:所有设备必须使用相同的密钥!
static const uint8_t ENCRYPTION_KEY[WIFIESPNOW_KEYLEN] = {
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
  0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};

static const int BUTTON_PIN = 0;    // 默认使用 GPIO0(Flash 按钮)
static const int LED_PIN = 2;       // 默认使用 GPIO2(板载 LED)
int ledState = HIGH;                // LED 初始状态(HIGH 通常为熄灭)

/**
 * 接收消息回调函数
 * 当接收到伪广播消息时被调用
 */
void processRx(const uint8_t mac[WIFIESPNOW_ALEN], const uint8_t* buf, size_t count, void* arg) {
  // 打印发送方 MAC 地址
  Serial.printf("Message from %02X:%02X:%02X:%02X:%02X:%02X\n", 
                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  
  // 打印接收到的消息内容
  for (size_t i = 0; i < count; ++i) {
    Serial.print(static_cast<char>(buf[i]));
  }
  Serial.println();

  // 切换 LED 状态
  digitalWrite(LED_PIN, ledState);
  ledState = 1 - ledState;
}

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

  WiFi.persistent(false);

  // 初始化伪广播功能
  bool ok = WifiEspNowBroadcast.begin("ESPNOW", 3);
  if (!ok) {
    Serial.println("WifiEspNowBroadcast.begin() failed");
    ESP.restart();
  }

  // 设置加密密钥(使用 WifiEspNowBroadcast 类的方法)
  bool keySet = WifiEspNowBroadcast.setPrimaryKey(ENCRYPTION_KEY);
  if (!keySet) {
    Serial.println("Failed to set encryption key!");
    ESP.restart();
  } else {
    Serial.println("Encryption key set successfully");
  }

  // 设置接收回调函数
  WifiEspNowBroadcast.onReceive(processRx, nullptr);

  // 初始化按钮和 LED 引脚
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, ledState);

  // 打印设备 MAC 地址(用于识别不同设备)
  Serial.print("MAC address of this node is ");
  Serial.println(WiFi.softAPmacAddress());
  Serial.println("Press the button to send a message");
}

/**
 * 发送消息函数
 * 向所有已知的对等设备发送消息(伪广播)
 */
void sendMessage() {
  // 准备包含设备信息和时间戳的消息
  char msg[60];
  int len = snprintf(msg, sizeof(msg), "ESP-NOW encrypted broadcast from %s at %lu",
                     WiFi.softAPmacAddress().c_str(), millis());
  
  // 发送消息到所有已知对等设备
  WifiEspNowBroadcast.send(reinterpret_cast<const uint8_t*>(msg), len);

  // 打印发送信息和已知对等设备列表
  Serial.println("Sending encrypted message");
  Serial.println(msg);
  
  // 打印已知对等设备列表
  Serial.print("Known peers: ");
  const int MAX_PEERS = 20;
  WifiEspNowPeerInfo peers[MAX_PEERS];
  int nPeers = std::min(WifiEspNow.listPeers(peers, MAX_PEERS), MAX_PEERS);
  
  for (int i = 0; i < nPeers; ++i) {
    Serial.printf(" %02X:%02X:%02X:%02X:%02X:%02X", 
                  peers[i].mac[0], peers[i].mac[1],
                  peers[i].mac[2], peers[i].mac[3],
                  peers[i].mac[4], peers[i].mac[5]);
  }
  Serial.println();
}

void loop() {
  // 检测按钮是否按下(LOW 表示按下)
  if (digitalRead(BUTTON_PIN) == LOW) {
    // 发送消息
    sendMessage();

    // 等待按钮释放,避免重复触发
    while (digitalRead(BUTTON_PIN) == LOW);
  }

  // 处理伪广播库的循环逻辑(必须调用)
  WifiEspNowBroadcast.loop();
  
  // 短暂延时,避免过于频繁地扫描
  delay(10);
}

对等设备管理函数

int listPeers(WifiEspNowPeerInfo\* peers, int maxPeers) const

  • 功能:列出当前的对等设备信息。
  • 参数peers 为存储对等设备信息的缓冲区,maxPeers 为缓冲区大小。
  • 返回值:返回对等设备的总数,实际写入 peers 的数量为 std::min(retval, maxPeers)

bool hasPeer(const uint8_t mac[WIFIESPNOW_ALEN]) const

  • 功能:检查指定 MAC 地址的对等设备是否存在。
  • 参数mac 为对等设备的 MAC 地址。
  • 返回值:存在返回 true,否则返回 false

bool addPeer(const uint8_t mac[WIFIESPNOW_ALEN], int channel = 0, const uint8_t key[WIFIESPNOW_KEYLEN] = nullptr, int netif = ESP_IF_WIFI_AP)

  • 功能:添加一个对等设备或更改对等设备的通道。
  • 参数mac 为对等设备的 MAC 地址,channel 为对等设备的通道,key 为加密密钥,netif 为 WiFi 接口(仅 ESP32)。
  • 返回值:操作成功返回 true,否则返回 false

bool removePeer(const uint8_t mac[WIFIESPNOW_ALEN])

  • 功能:移除指定 MAC 地址的对等设备。
  • 参数mac 为对等设备的 MAC 地址。
  • 返回值:移除成功返回 true,否则返回 false

接收和发送函数

void onReceive(RxCallback cb, void\* arg)

  • 功能:设置接收回调函数。
  • 参数cb 为回调函数指针,arg 为传递给回调函数的参数。
  • 返回值:无

bool send(const uint8_t mac[WIFIESPNOW_ALEN], const uint8_t\* buf, size_t count)

  • 功能:发送消息。
  • 参数mac 为目标 MAC 地址,nullptr 表示所有对等设备;buf 为消息缓冲区,count 为消息大小。
  • 返回值:消息成功入队发送返回 true,否则返回 false

WifiEspNowSendStatus getSendStatus() const

  • 功能:获取最后一次发送消息的状态。
  • 返回值:返回发送状态,枚举类型 WifiEspNowSendStatus,可能值为 NONEOKFAIL
// 获取发送状态
        WifiEspNowSendStatus status = WifiEspNow.getSendStatus();

        switch (status) {
            case WifiEspNowSendStatus::OK:
                Serial.println("Message sent successfully");
                break;
            case WifiEspNowSendStatus::FAIL:
                Serial.println("Message sending failed");
                break;
            case WifiEspNowSendStatus::NONE:
                Serial.println("Message status unknown, still sending");
                break;

WifiEspNowBroadcast 函数

初始化和终止函数

bool begin(const char\* ssid, int channel = 1, int scanFreq = 15000)

  • 功能:初始化 ESP - NOW 并启动伪广播功能。

  • 参数ssid 为用于宣告和发现对等设备的 AP SSID,channel 指的是接入点(AP)所使用的 WiFi 通道。在 WiFi 网络里,不同的通道代表着不同的频段,设备要在相同的通道上才能实现相互通信。scanFreq 为扫描对等设备的频率(毫秒)。所有设备都要使用相同的网络名称和通道,以此保证能相互通信。

  • 返回值:初始化成功返回 true,否则返回 false

void end()

  • 功能:停止 ESP - NOW 和伪广播功能。
  • 返回值:无

扫描和循环函数

void loop()

  • 功能:在 Arduino 的 loop() 函数中调用,用于定期扫描对等设备。
  • 返回值:无

密钥设置函数

bool setKey(const uint8_t primary[WIFIESPNOW_KEYLEN], const uint8_t peer[WIFIESPNOW_KEYLEN] = nullptr)

  • 功能:设置加密密钥。
  • 参数primary 为主密钥,peer 为对等设备密钥,nullptr 表示禁用加密。
  • 返回值:设置成功返回 true,否则返回 false

使用 setKey() 的加密伪广播代码

#include <WifiEspNowBroadcast.h>
#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h>
#endif

// 定义主加密密钥(16 字节 = 128 位)
// 注意:所有设备必须使用相同的主密钥!
static const uint8_t PRIMARY_KEY[WIFIESPNOW_KEYLEN] = {
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
  0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};

// 可选:定义对等设备特定密钥(用于特定设备间的额外加密)
static const uint8_t PEER_KEY[WIFIESPNOW_KEYLEN] = {
  0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
  0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20
};

static const int BUTTON_PIN = 0;    // 默认使用 GPIO0(Flash 按钮)
static const int LED_PIN = 2;       // 默认使用 GPIO2(板载 LED)
int ledState = HIGH;                // LED 初始状态(HIGH 通常为熄灭)

/**
 * 接收消息回调函数
 * 当接收到伪广播消息时被调用
 */
void processRx(const uint8_t mac[WIFIESPNOW_ALEN], const uint8_t* buf, size_t count, void* arg) {
  // 打印发送方 MAC 地址
  Serial.printf("Message from %02X:%02X:%02X:%02X:%02X:%02X\n", 
                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  
  // 打印接收到的消息内容
  for (size_t i = 0; i < count; ++i) {
    Serial.print(static_cast<char>(buf[i]));
  }
  Serial.println();

  // 切换 LED 状态
  digitalWrite(LED_PIN, ledState);
  ledState = 1 - ledState;
}

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

  WiFi.persistent(false);

  // 初始化伪广播功能
  bool ok = WifiEspNowBroadcast.begin("ESPNOW", 3);
  if (!ok) {
    Serial.println("WifiEspNowBroadcast.begin() failed");
    ESP.restart();
  }

  // 设置加密密钥(使用 setKey 函数)
  // 第一个参数为主密钥,第二个参数为可选的对等设备特定密钥
  bool keySet = WifiEspNowBroadcast.setKey(PRIMARY_KEY, PEER_KEY);
  if (!keySet) {
    Serial.println("Failed to set encryption key!");
    ESP.restart();
  } else {
    Serial.println("Encryption key set successfully");
  }

  // 设置接收回调函数
  WifiEspNowBroadcast.onReceive(processRx, nullptr);

  // 初始化按钮和 LED 引脚
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, ledState);

  // 打印设备 MAC 地址(用于识别不同设备)
  Serial.print("MAC address of this node is ");
  Serial.println(WiFi.softAPmacAddress());
  Serial.println("Press the button to send a message");
}

/**
 * 发送消息函数
 * 向所有已知的对等设备发送消息(伪广播)
 */
void sendMessage() {
  // 准备包含设备信息和时间戳的消息
  char msg[60];
  int len = snprintf(msg, sizeof(msg), "ESP-NOW encrypted broadcast from %s at %lu",
                     WiFi.softAPmacAddress().c_str(), millis());
  
  // 发送消息到所有已知对等设备
  WifiEspNowBroadcast.send(reinterpret_cast<const uint8_t*>(msg), len);

  // 打印发送信息和已知对等设备列表
  Serial.println("Sending encrypted message");
  Serial.println(msg);
  
  // 打印已知对等设备列表
  Serial.print("Known peers: ");
  const int MAX_PEERS = 20;
  WifiEspNowPeerInfo peers[MAX_PEERS];
  int nPeers = std::min(WifiEspNow.listPeers(peers, MAX_PEERS), MAX_PEERS);
  
  for (int i = 0; i < nPeers; ++i) {
    Serial.printf(" %02X:%02X:%02X:%02X:%02X:%02X", 
                  peers[i].mac[0], peers[i].mac[1],
                  peers[i].mac[2], peers[i].mac[3],
                  peers[i].mac[4], peers[i].mac[5]);
  }
  Serial.println();
}

void loop() {
  // 检测按钮是否按下(LOW 表示按下)
  if (digitalRead(BUTTON_PIN) == LOW) {
    // 发送消息
    sendMessage();

    // 等待按钮释放,避免重复触发
    while (digitalRead(BUTTON_PIN) == LOW);
  }

  // 处理伪广播库的循环逻辑(必须调用)
  WifiEspNowBroadcast.loop();
  
  // 短暂延时,避免过于频繁地扫描
  delay(10);
}

密钥配置

  • 所有设备必须使用相同的主密钥 PRIMARY_KEY
  • 对等设备特定密钥 PEER_KEY 可以为 nullptr(如果不需要)或所有设备相同

接收和发送函数

void onReceive(WifiEspNowClass::RxCallback cb, void\* arg)

  • 功能:设置接收回调函数。
  • 参数cb 为回调函数指针,arg 为传递给回调函数的参数。
  • 返回值:无

bool send(const uint8_t\* buf, size_t count)

  • 功能:广播消息。
  • 参数buf 为消息缓冲区,count 为消息大小。
  • 返回值:消息成功入队发送返回 true,否则返回 false

setKey() 和 setPrimaryKey() 的区别

在 ESP-NOW 通信中,setKey()setPrimaryKey() 的区别涉及到加密的粒度应用场景

setPrimaryKey()

bool setPrimaryKey(const uint8_t key[WIFIESPNOW_KEYLEN]);
  • 功能:设置一个全局主密钥,所有对等设备必须使用相同的主密钥才能通信。
  • 应用范围:整个 ESP-NOW 网络的所有设备共享同一个密钥。
  • 使用场景:简单的加密通信,所有设备使用相同的安全级别。

setKey()

bool setKey(const uint8_t primary[WIFIESPNOW_KEYLEN], 
            const uint8_t peer[WIFIESPNOW_KEYLEN] = nullptr);
  • 功能:设置两个密钥
    1. 主密钥(Primary Key):与 setPrimaryKey() 类似,所有设备必须相同。
    2. 对等设备特定密钥(Peer Key):可选,用于特定设备间的额外加密。
  • 应用范围:主密钥用于全局加密,对等密钥用于特定设备对之间的通信。
  • 使用场景:需要更细粒度安全控制的场景(如不同设备组使用不同密钥)。

为什么需要两个密钥?

多层安全保护

  • 主密钥:确保整个网络的基本安全性,所有设备必须共享。
  • 对等密钥:为特定设备对提供额外的安全层,例如:
    • 管理员设备与普通设备之间使用不同的对等密钥。
    • 关键设备之间使用更高级别的加密。

灵活的安全策略

通过组合使用两个密钥,可以实现:

  • 分层安全:不同类型的设备使用不同的安全级别。
  • 设备身份验证:特定设备对之间使用专用密钥,防止中间人攻击。

应用场景对比

场景推荐方法说明
所有设备使用相同的加密密钥setPrimaryKey()简单、统一的加密,所有设备共享一个密钥。
不同设备组使用不同的安全级别setKey(主密钥, 组特定密钥)主密钥确保基本安全,组特定密钥区分不同设备组(如管理员组与普通用户组)。
特定设备对之间需要更高的安全性setKey(主密钥, 设备对密钥)主密钥用于全局通信,设备对密钥用于敏感通信(如控制指令)。

示例

获取MAC地址

#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h>
#endif

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

    // 设置WiFi模式为STA(站点模式)
    WiFi.mode(WIFI_STA);

    // 获取并打印MAC地址
    Serial.print("MAC address of this node is ");
    Serial.println(WiFi.macAddress());
}

void loop() {
    // 主循环可以留空,因为我们只需要在启动时获取一次MAC地址
}

单播

fa.ino

#include <WifiEspNow.h>

// 定义接收端的 MAC 地址
const uint8_t receiverMac[WIFIESPNOW_ALEN] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};


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

    // 初始化 ESP-NOW
    if (WifiEspNow.begin()) {
        Serial.println("ESP-NOW 初始化成功");
    } else {
        Serial.println("ESP-NOW 初始化失败");
        return;
    }

    // 添加接收端为对等设备
    if (WifiEspNow.addPeer(receiverMac)) {
        Serial.println("对等设备添加成功");
    } else {
        Serial.println("对等设备添加失败");
    }
}

void loop() {
    const char* message = "Hello, Receiver!";
    size_t messageLength = strlen(message);

    // 向接收端发送消息
    if (WifiEspNow.send(receiverMac, (const uint8_t*)message, messageLength)) {
        Serial.println("消息已发送");
    } else {
        Serial.println("消息发送失败");
    }

    delay(5000); // 每 5 秒发送一次消息
}

shou.ino

#include <WifiEspNow.h>

// 定义控制灯的引脚
const int ledPin = 2;

// 接收回调函数,当接收到消息时会被调用
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, size_t data_len) {
    Serial.print("Received data from MAC address: ");
    // 打印发送端的 MAC 地址
    for (int i = 0; i < WIFIESPNOW_ALEN; i++) {
        Serial.printf("%02X", mac_addr[i]);
        if (i < WIFIESPNOW_ALEN - 1) {
            Serial.print(":");
        }
    }
    Serial.print(", Data: ");
    // 打印接收到的数据
    for (size_t i = 0; i < data_len; i++) {
        Serial.print((char)data[i]);
    }
    Serial.println();

    // 将接收到的数据转换为字符串
    String receivedData = "";
    for (size_t i = 0; i < data_len; i++) {
        receivedData += (char)data[i];
    }

    // 根据接收到的数据控制灯的开关
    if (receivedData == "ON") {
        digitalWrite(ledPin, HIGH);
        Serial.println("Light turned on");
    } else if (receivedData == "OFF") {
        digitalWrite(ledPin, LOW);
        Serial.println("Light turned off");
    }
}

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

    // 初始化控制灯的引脚
    pinMode(ledPin, OUTPUT);
    digitalWrite(ledPin, LOW);

    // 初始化 ESP-NOW
    if (WifiEspNow.begin()) {
        Serial.println("ESP-NOW initialized successfully");
    } else {
        Serial.println("ESP-NOW initialization failed");
        return;
    }

    // 设置接收回调函数
    WifiEspNow.onReceive(OnDataRecv, nullptr);
}

void loop() {
    // 可以在这里添加其他的循环逻辑
    delay(100);
}

发送端,点击按钮发送ON

#include <WifiEspNow.h>
#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h>
#endif

// 定义接收方的 MAC 地址
static uint8_t PEER[] {0x02, 0x00, 0x00, 0x45, 0x53, 0x50};

// 定义按钮引脚
const int buttonPin = 4;

// 上一次按钮状态
int lastButtonState = HIGH;

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

  // 初始化按钮引脚为输入模式
  pinMode(buttonPin, INPUT_PULLUP);
    
    
  bool ok = WifiEspNow.begin();
  if (!ok) {
    Serial.println("WifiEspNow.begin() failed");
    ESP.restart();
  }

  ok = WifiEspNow.addPeer(PEER);
  if (!ok) {
    Serial.println("WifiEspNow.addPeer() failed");
    ESP.restart();
  }
}

void loop() {
  // 读取按钮状态
  int buttonState = digitalRead(buttonPin);

  // 检测按钮是否按下
  if (buttonState == LOW && lastButtonState == HIGH) {
    // 按钮按下,发送 "ON" 消息
    const char* msg = "ON";
    size_t len = strlen(msg);
    bool sent = WifiEspNow.send(PEER, reinterpret_cast<const uint8_t*>(msg), len);
    if (sent) {
      Serial.println("Message 'ON' sent successfully");
    } else {
      Serial.println("Failed to send message 'ON'");
    }
  }

  // 更新上一次按钮状态
  lastButtonState = buttonState;

  // 短暂延迟以避免抖动
  delay(50);
}

发送端,点击按钮发送ON,再点击发送OFF

#include <WifiEspNow.h>
#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h>
#endif

// 定义接收方的 MAC 地址
static uint8_t PEER[] {0x02, 0x00, 0x00, 0x45, 0x53, 0x50};

// 定义按钮引脚
const int buttonPin = 4;

// 上一次按钮状态
int lastButtonState = HIGH;

// 消息状态,用于交替发送 "ON" 和 "OFF"
bool isOn = false;

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

  // 初始化按钮引脚为输入模式
  pinMode(buttonPin, INPUT_PULLUP);
    
  bool ok = WifiEspNow.begin();
  if (!ok) {
    Serial.println("WifiEspNow.begin() failed");
    ESP.restart();
  }

  ok = WifiEspNow.addPeer(PEER);
  if (!ok) {
    Serial.println("WifiEspNow.addPeer() failed");
    ESP.restart();
  }
}

void loop() {
  // 读取按钮状态
  int buttonState = digitalRead(buttonPin);

  // 检测按钮是否按下
  if (buttonState == LOW && lastButtonState == HIGH) {
    const char* msg;
    if (isOn) {
      msg = "OFF";
    } else {
      msg = "ON";
    }
    size_t len = strlen(msg);
    bool sent = WifiEspNow.send(PEER, reinterpret_cast<const uint8_t*>(msg), len);
    if (sent) {
      Serial.printf("Message '%s' sent successfully\n", msg);
    } else {
      Serial.printf("Failed to send message '%s'\n", msg);
    }
    // 切换消息状态
    isOn = !isOn;
  }

  // 更新上一次按钮状态
  lastButtonState = buttonState;

  // 短暂延迟以避免抖动
  delay(50);
}

一对多

ESP - NOW支持多播,但WifiEspNow库中似乎并没有特殊函数来支持多播

通过单播,依次向两个mac地址发送信息,实现多播效果

fa.ino

#include <WifiEspNow.h>

// 定义两个目标设备的 MAC 地址
const uint8_t macAddress1[WIFIESPNOW_ALEN] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
const uint8_t macAddress2[WIFIESPNOW_ALEN] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};


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

    // 初始化 ESP-NOW
    if (WifiEspNow.begin()) {
        Serial.println("ESP-NOW 初始化成功");
    } else {
        Serial.println("ESP-NOW 初始化失败");
        return;
    }

    // 添加两个对等设备
    if (WifiEspNow.addPeer(macAddress1) && WifiEspNow.addPeer(macAddress2)) {
        Serial.println("对等设备添加成功");
    } else {
        Serial.println("对等设备添加失败");
    }
}

void loop() {
    const char* message = "Hello, ESP-NOW!";
    size_t messageLength = strlen(message);

    // 向第一个 MAC 地址发送消息
    if (WifiEspNow.send(macAddress1, (const uint8_t*)message, messageLength)) {
        Serial.println("消息已发送到第一个设备");
    } else {
        Serial.println("消息发送到第一个设备失败");
    }

    // 向第二个 MAC 地址发送消息
    if (WifiEspNow.send(macAddress2, (const uint8_t*)message, messageLength)) {
        Serial.println("消息已发送到第二个设备");
    } else {
        Serial.println("消息发送到第二个设备失败");
    }

    delay(5000); // 每 5 秒发送一次消息
}

shou.ino

#include <WifiEspNow.h>

// 接收回调函数,当接收到消息时会被调用
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, size_t data_len) {
    Serial.print("Received data from MAC address: ");
    // 打印发送端的 MAC 地址
    for (int i = 0; i < WIFIESPNOW_ALEN; i++) {
        Serial.printf("%02X", mac_addr[i]);
        if (i < WIFIESPNOW_ALEN - 1) {
            Serial.print(":");
        }
    }
    Serial.print(", Data: ");
    // 打印接收到的数据
    for (size_t i = 0; i < data_len; i++) {
        Serial.print((char)data[i]);
    }
    Serial.println();
}

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

    // 初始化 ESP-NOW
    if (WifiEspNow.begin()) {
        Serial.println("ESP-NOW initialized successfully");
    } else {
        Serial.println("ESP-NOW initialization failed");
        return;
    }

    // 设置接收回调函数
    WifiEspNow.onReceive(OnDataRecv, nullptr);
}

void loop() {
    // 可以在这里添加其他的循环逻辑
    delay(100);
}

伪广播

// 实现 ESP-NOW 广播发送和接收消息,收到 'ON' 点亮 LED 的示例代码


#include <WifiEspNowBroadcast.h>
#if defined(ARDUINO_ARCH_ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h>
#endif

// 定义 LED 引脚
#ifdef ARDUINO_ARCH_ESP8266
const int LED_PIN = 2;
#else
const int LED_PIN = LED_BUILTIN;
#endif

// 处理接收到的消息的函数
void processRx(const uint8_t mac[WIFIESPNOW_ALEN], const uint8_t* buf, size_t count, void* arg) {
    Serial.print("Received message from ");
    for (int i = 0; i < WIFIESPNOW_ALEN; i++) {
        Serial.printf("%02X", mac[i]);
        if (i < WIFIESPNOW_ALEN - 1) {
            Serial.print(":");
        }
    }
    Serial.print(": ");
    for (size_t i = 0; i < count; i++) {
        Serial.print((char)buf[i]);
    }
    Serial.println();

    // 检查是否收到 "ON" 消息
    if (count == 2 && buf[0] == 'O' && buf[1] == 'N') {
        digitalWrite(LED_PIN, HIGH);
        Serial.println("Received 'ON', LED is turned on.");
    }
}

// 发送消息的函数
void sendMessage() {
    const char* msg = "hello";
    size_t len = strlen(msg);
    WifiEspNowBroadcast.send(reinterpret_cast<const uint8_t*>(msg), len);
    Serial.println("Message sent: hello");
    
      // 获取并打印已知对等设备列表
  const int MAX_PEERS = 20;
  WifiEspNowPeerInfo peers[MAX_PEERS];
  int nPeers = std::min(WifiEspNow.listPeers(peers, MAX_PEERS), MAX_PEERS);
  for (int i = 0; i < nPeers; ++i) {
    Serial.printf(" %02X:%02X:%02X:%02X:%02X:%02X", peers[i].mac[0], peers[i].mac[1],
                  peers[i].mac[2], peers[i].mac[3], peers[i].mac[4], peers[i].mac[5]);
  }
  Serial.println();
    
    
    
}

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

    WiFi.persistent(false); // 禁用 WiFi 配置持久化存储
    bool ok = WifiEspNowBroadcast.begin("ESPNOW", 3);
    if (!ok) {
        Serial.println("WifiEspNowBroadcast.begin() failed");
        ESP.restart();
    }

    WifiEspNowBroadcast.onReceive(processRx, nullptr);

    // 初始化 LED 引脚
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LOW);

    Serial.print("MAC address of this node is ");
    Serial.println(WiFi.softAPmacAddress());
}

void loop() {
    // 每5秒发送一次消息
    static unsigned long lastSendTime = 0;
    if (millis() - lastSendTime >= 5000) {
        sendMessage();
        lastSendTime = millis();
    }

    WifiEspNowBroadcast.loop();
    delay(10);
}    

伪广播认证

#include <WifiEspNowBroadcast.h>


// 合法设备的 MAC 地址列表
uint8_t allowedMacs[][WIFIESPNOW_ALEN] = {
    {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
    // 可以添加更多合法 MAC 地址
};
int numAllowedMacs = sizeof(allowedMacs) / sizeof(allowedMacs[0]);

bool isMacAllowed(const uint8_t mac[WIFIESPNOW_ALEN]) {
    for (int i = 0; i < numAllowedMacs; i++) {
        if (memcmp(mac, allowedMacs[i], WIFIESPNOW_ALEN) == 0) {
            return true;
        }
    }
    return false;
}

void processRx(const uint8_t mac[WIFIESPNOW_ALEN], const uint8_t* buf, size_t count, void* arg) {
    if (isMacAllowed(mac)) {
        // 处理合法设备发送的消息
        for (size_t i = 0; i < count; i++) {
            Serial.print((char)buf[i]);
        }
        Serial.println();
    } else {
        // 忽略非法设备发送的消息
        Serial.println("Received message from unauthorized device");
    }
}

void setup() {
    Serial.begin(115200);
    
    WiFi.persistent(false);
    bool ok = WifiEspNowBroadcast.begin("ESPNOW", 3);
    if (!ok) {
        Serial.println("WifiEspNowBroadcast.begin() failed");
        ESP.restart();
    }
    WifiEspNowBroadcast.onReceive(processRx, nullptr);
}

void loop() {
    WifiEspNowBroadcast.loop();
    delay(10);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2397601.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis的安装与使用

网址&#xff1a;Spring Data Redis 安装包&#xff1a;Releases tporadowski/redis GitHub 解压后 在安装目录中打开cmd 打开服务&#xff08;注意&#xff1a;每次客户端连接都有先打开服务&#xff01;&#xff01;&#xff01;&#xff09; 按ctrlC退出服务 客户端连接…

2024年数维杯国际大学生数学建模挑战赛C题时间信号脉冲定时噪声抑制与大气时延抑制模型解题全过程论文及程序

2024年数维杯国际大学生数学建模挑战赛 C题 时间信号脉冲定时噪声抑制与大气时延抑制模型 原题再现&#xff1a; 脉冲星是一种快速旋转的中子星&#xff0c;具有连续稳定的旋转&#xff0c;因此被称为“宇宙灯塔”。脉冲星的空间观测在深空航天器导航和时间标准维护中发挥着至…

C# 控制台程序获取用户输入数据验证 不合规返回重新提示输入

在 C# 控制台程序中实现输入验证并循环重试&#xff0c;可以通过以下方式实现高效且用户友好的交互。以下是包含多种验证场景的完整解决方案&#xff1a; 一、通用输入验证框架 public static T GetValidInput<T>(string prompt, Func<string, (bool IsValid, T Val…

TDengine 运维——巡检工具(安装前检查)

简介 本文档旨在介绍 TDengine 安装部署前后配套的巡检工具。 相关工具的功能简介&#xff1a; 工具名称功能简介安装前检查部署前对 TDengine 安装部署的依赖要素进行安装前检查安装前预配置部署前对 TDengine 安装部署的依赖要素进行安装前预配置安装部署指定环境安装部署…

【Linux】权限chmod命令+Linux终端常用快捷键

目录 linux中权限表示形式 解析标识符 权限的数字序号 添加权限命令chmod 使用数字表示法设置权限 使用符号表示法设置权限 linux终端常用快捷键 &#x1f525;个人主页 &#x1f525; &#x1f608;所属专栏&#x1f608; 在 Linux 系统里&#xff0c;权限管理是保障系…

Java八股文智能体——Agent提示词(Prompt)

这个智能体能够为正在学习Java八股文的同学提供切实帮助&#xff1a;不仅可以帮你优化答案表述&#xff0c;还能直接解答八股文相关问题——它会以面试者的视角&#xff0c;给出贴合求职场景的专业回答。 将以下内容发送给任何一个LLM&#xff0c;他会按照你提示词的内容&…

Go语言的context

Golang context 实现原理 本篇文章是基于小徐先生的文章的修改和个人注解&#xff0c;要查看原文可以点击上述的链接查看 目前我这篇文章的go语言版本是1.24.1 context上下文 context被当作第一个参数&#xff08;官方建议&#xff09;&#xff0c;并且不断的传递下去&…

数据库原理 试卷

以下是某高校教学管理系统的毕业论文指导ER图&#xff0c;数据信息&#xff1a;一名教师指导多名学生&#xff0c;一名学生只能选择一名教师&#xff0c;试分析完成以下各题&#xff0c;如用SQL命令完成的&#xff0c;在SQL Server2008验证后把答案写在题目的下方。 图1 毕业论…

【Qt开发】对话框

目录 1&#xff0c;对话框的介绍 2&#xff0c;Qt内置对话框 2-1&#xff0c;消息对话框QMessageBox 2-2&#xff0c;颜色对话框QColorDialog 2-3&#xff0c;文件对话框QFileDialog 2-4&#xff0c;字体对话框QFontDialog 2-5&#xff0c;输入对话框QInputDialog 1&…

2025年渗透测试面试题总结-匿名[校招]攻防研究员(应用安全)(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 匿名[校招]攻防研究员(应用安全) 基础部分 1. HTTP状态码 2. HTTP请求方法及作用 3. 网络分层及协议 OW…

碰一碰发视频系统--基于H5场景开发

#碰一碰发视频# 旨在构建一个基于移动网页&#xff08;H5&#xff09;的视频“碰传”交互系统&#xff0c;提供类似华为/苹果设备 NFC 轻碰分享的便捷体验。其核心技术依赖于移动端可用的近场通信&#xff08;NFC 或 H5 相关 API&#xff09;和可靠的媒体数据传输方案。实现细节…

MagicAnimate 论文解读:引入时间一致性的视频人物动画生成方法

1. 前言/动机 问题&#xff1a;现有动画生成方法缺乏对时间信息的建模&#xff0c;常常出现时间一致性差的问题 描述&#xff1a; 现有的动画生成方法通常采用帧变形&#xff08;frame-warping&#xff09;技术&#xff0c;将参考图像变形以匹配目标动作。尽管这类方法能生成较…

数据结构:递归(Recursion)

目录 示例1&#xff1a;先打印&#xff0c;再递归 示例2&#xff1a;先递归&#xff0c;再打印 递归的两个阶段 递归是如何使用栈内存 复杂度分析 递归中的静态变量 内存结构图解 递归&#xff1a;函数调用自己 必须有判断条件来使递归继续或停止 我们现在通过这两个示…

Cesium快速入门到精通系列教程一:打造第一个Cesium应用

一、打造第一个Cesium应用 1、官方渠道下载Cesium&#xff08;可选择历史版本&#xff09; ​​GitHub Releases页面​​&#xff1a;https://github.com/CesiumGS/cesium/releases 访问 Cesium GitHub Releases&#xff0c;此处列出了所有正式发布的版本。 通过标签&#…

力扣题解106:从中序与后序遍历序列构造二叉树

一、题目内容 题目要求根据二叉树的中序遍历序列和后序遍历序列来重建二叉树。具体来说&#xff0c;我们需要利用中序遍历序列和后序遍历序列的特点&#xff0c;通过递归的方法逐步构建出完整的二叉树。 中序遍历序列的特点是&#xff1a;左子树 -> 根节点 -> 右子树。后…

学习STC51单片机25(芯片为STC89C52RCRC)

每日一言 生活就像弹簧&#xff0c;你弱它就强&#xff0c;你强它就弱&#xff0c;别轻易认输。 ESP8266作为路由器模式&#xff08;AP模式&#xff09;也就是在局域网内可以有服务器的作用 那么我们需要将pc作为设备进行连接ESP的发射出来的WIFE 叫做这个AI啥的 也有可能叫做…

宁夏农业科技:创新引领,赋能现代农业新篇章

在广袤的宁夏大地上&#xff0c;农业科技如同一股强劲的春风&#xff0c;吹拂着每一寸土地&#xff0c;为宁夏的农业发展注入了新的活力与希望。近年来&#xff0c;宁夏农业科技以其独特的创新力和实践力&#xff0c;不断推动着现代农业的转型升级&#xff0c;让这片古老的土地…

Accelerate 2025北亚巡展正式启航!AI智御全球·引领安全新时代

近日&#xff0c;网络安全行业年度盛会Accelerate 2025北亚巡展正式在深圳启航&#xff01;智库专家、产业领袖及Fortinet高管、产品技术团队和300余位行业客户齐聚一堂&#xff0c;围绕“AI智御全球引领安全新时代”主题&#xff0c;共同探讨AI时代网络安全新范式。大会聚焦三…

005学生心理咨询评估系统技术解析:搭建科学心理评估平台

学生心理咨询评估系统技术解析&#xff1a;搭建科学心理评估平台 在心理健康教育日益受重视的当下&#xff0c;学生心理咨询评估系统成为了解学生心理状态的重要工具。该系统涵盖试卷管理、试题管理等核心模块&#xff0c;面向管理员和用户两类角色&#xff0c;通过前台展示与…

贪心算法应用:多重背包启发式问题详解

贪心算法应用&#xff1a;多重背包启发式问题详解 多重背包问题是经典的组合优化问题&#xff0c;也是贪心算法的重要应用场景。本文将全面深入地探讨Java中如何利用贪心算法解决多重背包问题。 多重背包问题定义 **多重背包问题(Multiple Knapsack Problem)**是背包问题的变…