esp-now笔记

ESP NOW简介

介绍

  • 官网介绍

ESP-NOW 是由乐鑫开发的另一款协议,可以使多个设备在没有或不使用 Wi-Fi 的情况下进行通信。这种协议类似常见于无线鼠标中的低功耗 2.4GHz 无线连接——设备在进行通信之前要进行配对。配对之后,设备之间的连接是持续的、点对点的,并且不需要握手协议。

  • 启明云端:ESP-NOW的功能特性

一.简介

ESP-NOW是一种由乐鑫定义的无连接wi-fi通信,是由乐鑫开发的另一款协议,可以使多个设备在没有或不使用 Wi-Fi 的情况下进行通信。这种协议类似常见于无线鼠标中的低功耗 2.4GHz 无线连接——设备在进行通信之前要进行配对。配对之后,设备之间的连接是持续的、点对点的,并且不需要握手协议。

二.特点

1.无连接,设备配对简单,配对后可直接数据通信,距离授权,APP授权

2.协议精简O51上层5层协议精简为1层,降低延时,快速响应

3.加密安全

4.可以和wifi BLE共存

5.CPU,flash资源占用少

6.响应快,ms级

7.功耗低,协议精简,功耗降低

8.兼容性好,本地控制+远程控制,与路由器、热点共存

9.远距离快速稳定通信,可在视距500m稳定通信

10.支持单播、广播分组控制,同时支持上直设备

11.多形态控制 ,Button开关 ,Touch按键,LCD屏,语音,传感器控制等

image-20221207224249054

相关学习页

ESP-NOW乐鑫官方手册

ESP-NOW 官方QA

ESP32 Wi-Fi 10公里传输距离测试

获取本机mac地址

要通过 ESP-NOW 收发消息,需要知道接收板的 MAC 地址。每个开发板都有一个唯一的 MAC 地址,以下实例程序为打印mac地址。

示例程序01
#include <Arduino.h>
#ifdef ESP32
  #include <WiFi.h>
#else
  #include <ESP8266WiFi.h>
#endif

void setup()
{

  Serial.begin(115200);

  WiFi.mode(WIFI_MODE_STA);

  // 打印MAC地址
  uint8_t mac[6];
  WiFi.macAddress(mac);
  Serial.println();
  Serial.printf("const uint8_t mac_self[6] = {0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x};", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

void loop()
{
  delay(1000);
}

串口打印信息

image-20221206220354533

将打印的这一行信息复制到后续程序中

以下是我所使用的3块接收板mac地址速记信息

const uint8_t mac_self[6] = {0x60, 0x55, 0xf9, 0x77, 0x75, 0x60};//合宙esp32C3

const uint8_t mac_self[6] = {0x78, 0x21, 0x84, 0xc6, 0xd9, 0x8c};//esp32外置天线

const uint8_t mac_self[6] = {0x08, 0x3a, 0xf2, 0xb9, 0xa0, 0xe4};//esp32-旧CP2102

单向:一对多

image-20221207001335493

使用 ESP-NOW(一对多)将数据从一个 ESP32 发送到多个 ESP32 或 ESP8266 开发板。

本示例均使用ESP32开发板,esp8266需对程序做适配修改。

  • 一台 ESP32 充当发送方;
  • 多个 ESP32 或 ESP8266 板充当接收方
  • 如果消息成功下发,ESP32 发送方会收到一条确认消息。用以知道哪些板收到了消息,哪些板没有;

A接收方

将示例代码上传到接收方ESP32板中

示例程序02
#include <esp_now.h>
#include <WiFi.h>

//接收方用以接受数据的结构体 ***
//必须和发送方的结构体匹配 ***
typedef struct test_struct {
  int x;
  int y;
} test_struct;

//创建结构体 ***
test_struct 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("x: ");
  Serial.println(myData.x);
  Serial.print("y: ");
  Serial.println(myData.y);
  Serial.println();
}
 
void setup() {
  //初始化串口
  Serial.begin(115200);
  
  //设置wifi模式为 Wi-Fi Station
  WiFi.mode(WIFI_STA);

  //初始化 ESP-NOW,初始化失败则打印信息并退出
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  // 注册接受消息的回调函数
  // 用以获取接收到的数据
  esp_now_register_recv_cb(OnDataRecv);
}
 
void loop() {
  delay(1000);
}
  • 收到消息时打印的信息

image-20221206233233428

B发送方

以下代码通过 ESP-NOW 将不同的数据分别发送到指定的多个 ESP 板。或将相同数据一次性发送至所有接收方。

您应该使用接收方ESP板的 MAC 地址修改代码。还应该根据接收方ESP板的数量添加或删除代码行。

示例程序03
#include <esp_now.h>
#include <WiFi.h>

// 替换为esp接收方的MAC地址 ***
uint8_t mac_receive_1[] = {0x78, 0x21, 0x84, 0xc6, 0xd9, 0x8c}; // esp32外置天线
uint8_t mac_receive_2[] = {0x08, 0x3a, 0xf2, 0xb9, 0xa0, 0xe4}; // esp32-旧CP2102

// 发送方的结构体 ***
typedef struct test_struct
{
  int x;
  int y;
} test_struct;

// 创建结构体 ***
test_struct test_a;
test_struct test_b;

// 实例化espnow通讯的对等体
esp_now_peer_info_t peerInfo;

// 数据发送时的回调函数 ***
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
  char macStr[18];
  Serial.print("Packet to: ");
  //把接收方mac地址复制为字符串
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.print(macStr);
  Serial.print(" send status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

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

  WiFi.mode(WIFI_STA);

  // 初始化espnow,如果失败则打印错误信息并退出
  if (esp_now_init() != ESP_OK)
  {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // 注册消息发送的回调函数
  esp_now_register_send_cb(OnDataSent);

  // 配置对等体信息
  //    通讯信道
  peerInfo.channel = 0;
  //    是否加密通讯信息
  peerInfo.encrypt = false;
  // 添加第1个对等体
  memcpy(peerInfo.peer_addr, mac_receive_1, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK)
  {
    Serial.println("Failed to add peer");
    return;
  }
  // 添加第2个对等体
  memcpy(peerInfo.peer_addr, mac_receive_2, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK)
  {
    Serial.println("Failed to add peer");
    return;
  }
  // 添加第3个对等体(如有更多则复制以下几行并修改mac地址)
  /*
  memcpy(peerInfo.peer_addr, mac_receive_3, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK)
  {
    Serial.println("Failed to add peer");
    return;
  }
  */
}

void loop()
{

  test_a.x = random(0, 10);
  test_a.y = random(0, 10);

  test_b.x = random(50, 80);
  test_b.y = random(50, 80);

  // way 1: 发送数据给所有注册过的对等体
  // peer_addr参数为0,则为发送数据给所有注册过的对等体
  // esp_err_t result = esp_now_send(0, (uint8_t *)&test_a, sizeof(test_struct));

  // way 2: 发送数据给指定的对等体
  // peer_addr参数为指定的对等体的mac地址
  esp_err_t result_1 = esp_now_send(mac_receive_1, (uint8_t *)&test_a, sizeof(test_struct));
  esp_err_t result_2 = esp_now_send(mac_receive_2, (uint8_t *)&test_b, sizeof(test_struct));

  if (result_1 == ESP_OK)
  {
    Serial.println("Sent with success: result_1");
  }
  else
  {
    Serial.println("Error sending the data: result_1");
  }

  if (result_2 == ESP_OK)
  {
    Serial.println("Sent with success: result_2");
  }
  else
  {
    Serial.println("Error sending the data: result_2");
  }

  delay(2000);
}
  • 当2个接收方里一个正常,另外一个断电时

image-20221206232344233

image-20221206231521665

  • 将2个接收方正常供电时

image-20221206232311686

image-20221206231942916

单向:多对一

image-20221207001358568

通过 ESP-NOW 通信协议(多对一)接收来自多个 ESP32 开发板的数据

  • 发送方板收到一条确认消息,指示消息是否已成功传递;
  • ESP32 接收方接收来自所有发送方的消息,并识别发送消息的板卡;

A发送方

对于接收方来说,接收方可以通过其唯一的 MAC 地址来识别每个发送方。

但是,在接收端处理不同的MAC地址以识别哪个板发送了哪个消息可能会让人难以区分。

因此,为了方便起见,示例将使用唯一的编号(id) 从 1 开始。该 ID 将与其他变量一起发送到接收方。

示例程序04

需根据多个开发板序号改变结构体中的id

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

// 替换为esp接收方的MAC地址 ***
const uint8_t mac_receive[6] = {0x60, 0x55, 0xf9, 0x77, 0x75, 0x60};//合宙esp32C3


// 发送方的结构体 ***
// 必须与接收方结构体匹配
typedef struct struct_message {
    int id; // 每个发送方id必须是唯一的
    int x;
    int y;
} struct_message;

// 创建结构体 ***
struct_message myData;

// 实例化espnow通讯的对等体
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.mode(WIFI_STA);

  // 初始化espnow,如果失败则打印错误信息并退出
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // 注册消息发送的回调函数
  esp_now_register_send_cb(OnDataSent);
  
  // 配置对等体信息
  //    通讯信道
  peerInfo.channel = 0;
  //    是否加密通讯信息
  peerInfo.encrypt = false;
  
  // 添加第1个对等体    
  memcpy(peerInfo.peer_addr, mac_receive, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}
 
void loop() {
  // 赋值要发送的结构体数据
  myData.id = 1;
  myData.x = random(0,50);
  myData.y = random(0,50);

  // 通过 ESP-NOW 发送数据
  esp_err_t result = esp_now_send(mac_receive, (uint8_t *) &myData, sizeof(myData));
   
  if (result == ESP_OK) {
    Serial.println("Sent with success");
  }
  else {
    Serial.println("Error sending the data");
  }
  delay(10000);
}
  • 发送方串口信息

image-20221207193517278

B接收方

示例程序从多个esp板接收数据,并通过mac或数据中的id来区分

示例程序05
#include <esp_now.h>
#include <WiFi.h>

//接收方用以接受数据的结构体 ***
//必须和发送方的结构体匹配 ***
typedef struct struct_message
{
  int id;
  int x;
  int y;
} struct_message;

//创建结构体 ***
struct_message myData;

//创建不同的结构对象来分别保存每个板上的读数
struct_message board1;
struct_message board2;
struct_message board3;

// 创建一个包含所有结构的数组
struct_message boardsStruct[3] = {board1, board2, board3};

//接收到数据时执行的回调函数 ***
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *incomingData, int len)
{
  char macStr[18];
  Serial.print("Packet received from: ");
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.println(macStr);
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.printf("Board ID %u: %u bytes\n", myData.id, len);
  // 用传入的数据更新各自对应的结构体
  boardsStruct[myData.id - 1].x = myData.x;
  boardsStruct[myData.id - 1].y = myData.y;
  Serial.printf("x value: %d \n", boardsStruct[myData.id - 1].x);
  Serial.printf("y value: %d \n", boardsStruct[myData.id - 1].y);
  Serial.println();
}

void setup()
{
  //初始化串口
  Serial.begin(115200);

  //设置wifi模式为 Wi-Fi Station
  WiFi.mode(WIFI_STA);

  //初始化 ESP-NOW,初始化失败则打印信息并退出
  if (esp_now_init() != ESP_OK)
  {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // 注册接受消息的回调函数
  // 用以获取接收到的数据
  esp_now_register_recv_cb(OnDataRecv);
}

void loop()
{
  // 访问每个板对应的结构体变量
  // for (size_t i = 0; i < 3; i++)
  // {
  //   Serial.printf("Board %u: x = %d, y = %d \n", i + 1, boardsStruct[i].x, boardsStruct[i].y);
  // }
  


  delay(10000);
}
  • 当有2块发送方时

image-20221207192932032

  • 当只有1块发送方时

image-20221207193314747

双向:一对一 *

image-20221207001652111

将以下代码修改mac地址后分别上传到2块ESP开发板中。

其实esp-now通讯主要有4个点:

  • 数据发送回调函数 (用以判断对方是否成功收到消息或打印当前发送的目标地址等)
  • 数据接收回调函数 (用以对接收的数据做解析等)
  • 对等体配置 (用以配置通讯信道或内容是否加密等)
  • 发送数据 (Dapenson : ) )
示例程序06

需要修改程序中接收方(对方)的mac地址

程序功能为获取传感器(随机数)数值,发送给指定的对方接收对方发过来的传感器数值并串口打印

// espnow通讯主要有4个点:数据发送回调函数、数据接收回调函数、对等体配置、发送数据

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

// 函数声明
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status);
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len);
void getReadings();
void updateDisplay();

// 接收方(对方)的mac地址
// const uint8_t mac_target[6] = {0x78, 0x21, 0x84, 0xc6, 0xd9, 0x8c}; // esp32外置天线
const uint8_t mac_target[6] = {0x60, 0x55, 0xf9, 0x77, 0x75, 0x60};//合宙esp32C3

// 定义变量存储传感器读数
float temperature;
float humidity;
float pressure;

//定义变量存储接收到的传感器读数
float incomingTemp;
float incomingHum;
float incomingPres;

// 用于存储数据发送是否成功的变量
String success;

// 发送方或接收方的结构体 ***
// 必须与接收方结构体匹配,是消息传递的载体
typedef struct struct_message
{
  float temp;
  float hum;
  float pres;
} struct_message;

// 创建一个struct_message类型的变量sensor_fake,用以存储传感器读数
// Create a struct_message called sensor_fake to hold sensor readings
struct_message sensor_fake;

// 创建一个struct_message类型的变量incomingReadings,用以存储接收到的传感器读数
// Create a struct_message to hold incoming sensor readings
struct_message incomingReadings;

// 实例化espnow通讯的对等体
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");
  if (status == 0)
  {
    success = "Delivery Success";
  }
  else
  {
    success = "Delivery Fail";
  }
}

// 数据接收时的回调函数 ***
// Callback when data is received
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len)
{
  memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));
  Serial.print("Bytes received: ");
  Serial.println(len);
  incomingTemp = incomingReadings.temp;
  incomingHum = incomingReadings.hum;
  incomingPres = incomingReadings.pres;
}

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

  WiFi.mode(WIFI_STA);

  // 初始化espnow,如果失败则打印错误信息并退出
  if (esp_now_init() != ESP_OK)
  {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // 注册消息发送的回调函数
  esp_now_register_send_cb(OnDataSent);

  // 配置对等体信息
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  // 添加消息对等点
  memcpy(peerInfo.peer_addr, mac_target, 6);
  if (esp_now_add_peer(&peerInfo) != ESP_OK)
  {
    Serial.println("Failed to add peer");
    return;
  }

  // 注册接受消息的回调函数
  // 用以获取接收到的数据
  esp_now_register_recv_cb(OnDataRecv);
}

void loop()
{
  getReadings();

  // 赋值要发送的结构体数据
  sensor_fake.temp = temperature;
  sensor_fake.hum = humidity;
  sensor_fake.pres = pressure;

  // 通过 ESP-NOW 发送数据
  esp_err_t result = esp_now_send(mac_target, (uint8_t *)&sensor_fake, sizeof(sensor_fake));

  if (result == ESP_OK)
  {
    Serial.println("Sent with success");
  }
  else
  {
    Serial.println("Error sending the data");
  }
  updateDisplay();
  delay(10000);
}

// 从传感器获取数据
void getReadings()
{
  temperature = random(20, 30);
  humidity = random(50, 60);
  pressure = random(1000, 1100);
}

void updateDisplay()
{
  // 将数据更新显示在串口
  Serial.println("INCOMING READINGS");
  Serial.print("Temperature: ");
  Serial.print(incomingReadings.temp);
  Serial.println(" ºC");
  Serial.print("Humidity: ");
  Serial.print(incomingReadings.hum);
  Serial.println(" %");
  Serial.print("Pressure: ");
  Serial.print(incomingReadings.pres);
  Serial.println(" hPa");
  Serial.println();
}

  • 效果演示

image-20221207204834275

左右分别为2块开发板串口的打印信息,分别将对方的传感器数值显示出来

双向:多对多

image-20221207001858274

理解了以上的,便可以在双向一对一的基础上写出多对多的程序,通过指定对方mac地址,定向的发送双方都通用的结构体数据,

在接收回调函数中写入结构体解析类的函数。

多块开发板之间可以通过mac地址,或在自定义的通用结构体中定义身份区分的唯一id值,即自定义数据通讯协议。

广播模式 *

image-20221207002714484

由于使用广播模式,因此我们不需要知道对方其他ESP板的MAC地址,因此每个板都可以运行相同的代码。

程序功能:通过按下板载按钮,改变板载LED灯,同时将该状态相互广播出去,实现任一开发板按键控制所有开发板的LED

示例程序07
  • 前面的示例程序都使用结构体来作为数据通讯的载体,因此本示例使用msg的方式直接发送数据
  • 广播函数void broadcast(const String &message)通过发送特殊的 MAC 地址 FF:FF:FF:FF:FF:FF 创建广播消息。每个对等方都会回复它的 MAC 地址,这些地址可用于发送数据。
#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>

// 函数声明
void formatMacAddress(const uint8_t *macAddr, char *buffer, int maxLength);
void receiveCallback(const uint8_t *macAddr, const uint8_t *data, int dataLen);
void sentCallback(const uint8_t *macAddr, esp_now_send_status_t status);
void sentCallback(const uint8_t *macAddr, esp_now_send_status_t status);
void broadcast(const String &message);

// 定义LED和按键状态布尔值
bool buttonDown = false;
bool ledOn = false;

// 宏定义LED和按键引脚,根据自己的开发板原理图修改
#ifdef ESP32C3
  // 适配合宙esp32C3
  #define STATUS_LED 12
  #define STATUS_BUTTON 9
#elif ESP32
  // 适配esp32
  #define STATUS_LED 2
  #define STATUS_BUTTON 0
#endif

// 格式化MAC地址
// Formats MAC Address
void formatMacAddress(const uint8_t *macAddr, char *buffer, int maxLength)
{
  snprintf(buffer, maxLength, "%02x:%02x:%02x:%02x:%02x:%02x", macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);
}

//  接收到数据时的回调函数
// Called when data is received
void receiveCallback(const uint8_t *macAddr, const uint8_t *data, int dataLen)
{
  // 消息最长250个字符,加上一个空字符
  // Only allow a maximum of 250 characters in the message + a null terminating byte
  char buffer[ESP_NOW_MAX_DATA_LEN + 1];
  int msgLen = min(ESP_NOW_MAX_DATA_LEN, dataLen);
  strncpy(buffer, (const char *)data, msgLen);

  //确保以空字符结尾
  // Make sure we are null terminated
  buffer[msgLen] = 0;

  // 格式化MAC地址
  // Format the MAC address
  char macStr[18];
  formatMacAddress(macAddr, macStr, 18);

  // 发送调试日志消息到串口
  // Send Debug log message to the serial port
  Serial.printf("Received message from: %s - %s\n", macStr, buffer);

  // Dapenson 以下便可添加数据处理或逻辑处理代码
  // 比较2个字符串是否相等,相等则返回0
  // 如果消息是“on”,则打开LED
  if (strcmp("on", buffer) == 0)
  {
    ledOn = true;
  }
  else
  {
    ledOn = false;
  }
  digitalWrite(STATUS_LED, ledOn);
}

// 消息发送后的回调函数,用以判断对方是否成功收到消息等
// Called when data is sent
void sentCallback(const uint8_t *macAddr, esp_now_send_status_t status)
{
  char macStr[18];
  formatMacAddress(macAddr, macStr, 18);
  Serial.print("Last Packet Sent to: ");
  Serial.println(macStr);
  Serial.print("Last Packet Send Status: ");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

// 广播消息 ***
// Emulates a broadcast
void broadcast(const String &message)
{
  // 将消息广播到每个在范围内的设备
  // Broadcast a message to every device in range
  uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
  esp_now_peer_info_t peerInfo = {};
  // 把broadcastAddress添加为消息对等体
  memcpy(&peerInfo.peer_addr, broadcastAddress, 6);
  if (!esp_now_is_peer_exist(broadcastAddress))
  {
    esp_now_add_peer(&peerInfo);
  }

  // 发送消息给所有范围内的设备
  esp_err_t result = esp_now_send(broadcastAddress, (const uint8_t *)message.c_str(), message.length());

  // 将发送结果打印到串口
  // Print results to serial monitor
  Serial.printf(esp_err_to_name(result));
  Serial.println();
}

void setup()
{

  Serial.begin(115200);
  delay(1000);

  WiFi.mode(WIFI_STA);
  Serial.println("ESP-NOW Broadcast Demo");

  // 初始化espnow,如果失败则打印错误信息并退出重启
  if (esp_now_init() == ESP_OK)
  {
    Serial.println("ESP-NOW Init Success");
    // 注册接受消息的回调函数
    esp_now_register_recv_cb(receiveCallback);
    // 注册消息发送的回调函数
    esp_now_register_send_cb(sentCallback);
  }
  else
  {
    Serial.println("ESP-NOW Init Failed");
    delay(3000);
    // 重启esp设备
    ESP.restart();
  }

  pinMode(STATUS_LED, OUTPUT);
}
void loop()
{
  if (digitalRead(STATUS_BUTTON))
  {
    // 检测从低到高的转换
    // Detect the transition from low to high
    if (!buttonDown)
    {
      buttonDown = true;
      
      // 翻转LED状态
      // Toggle the LED state
      ledOn = !ledOn;
      digitalWrite(STATUS_LED, ledOn);
      
      // 发送消息给所有范围内的设备,用以同步LED状态
      // Send a message to all devices
      if (ledOn)
      {
        broadcast("on");
      }
      else
      {
        broadcast("off");
      }
    }
    
    // 延时以避免抖动
    // Delay to avoid bouncing
    delay(500);
  }
  else
  {
    // 重置按钮状态
    // Reset the button state
    buttonDown = false;
  }
}
  • 效果演示

image-20221207222532022

  • 实际按键效果视频

https://www.bilibili.com/video/BV1ZP4y1Q78G/

posted @ 2022-12-07 22:54  Dapenson  阅读(2666)  评论(0编辑  收藏  举报