[RISC-V MCU 应用开发] ESP-NOW入门

[复制链接]
 楼主| gaoyang9992006 发表于 2023-8-17 22:20 | 显示全部楼层 |阅读模式
本帖最后由 gaoyang9992006 于 2023-8-17 22:27 编辑

@21小跑堂
ESP32-C3系列是RISC-V核心单片机中的一颗璀璨明珠,是一款性价比超高的WIFI单片机。
厂家提供了一种基于底层通信的协议ESP-NOW。无需无线路由器即可实现高速远距离通信。
ESP-NOW 是乐鑫定义的一种无线通信协议,能够在无路由器的情况下直接、快速、低功耗地控制智能设备。它能够与 Wi-Fi 和 Bluetooth LE 共存,支持乐鑫 ESP8266、ESP32、ESP32-S 和 ESP32-C 等多系列 SoC。ESP-NOW 广泛应用于智能家电、远程控制和传感器等领域。在 ESP-NOW 中,应用程序数据被封装在各个供应商的动作帧中,然后在无连接的情况下,从一个 Wi-Fi 设备传输到另一个 Wi-Fi 设备。
ESP-NOW 是基于数据链路层的无线通信协议,它将五层 OSI 上层协议精简为一层,数据传输时无需依次经过网络层、传输层、会话层、表示层、应用层等复杂的层级,也无需层层增加包头和解包,大大缓解了网络拥挤时因为丢包而导致的卡顿和延迟,拥有更高的响应速度。
4286464de2b93844e5.png
经过两天的学习,已经掌握了基本用法,现将学习笔记记录如下:
发送端,发送端作为STA模式运行,接收端是被发现设备,因此作为AP模式运行。
发送端代码:
  1. #include <esp_now.h>
  2. #include <WiFi.h>
  3. #include <esp_wifi.h>

  4. //定义开发板载LED
  5. #define LED4 12
  6. #define LED5 13
  7. //定义通信通道宏,指定在通道1通信
  8. #define CHANNEL 1

  9. // Global copy of slave
  10. esp_now_peer_info_t slave;

  11. esp_err_t addStatus;

  12. //发送回调函数,即发送完成后通过该函数返回相关的地址与状态
  13. void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
  14. {
  15.   for(int i = 0; i<6 ; i++ )
  16.   {
  17.     Serial.print("0x");Serial.print(*(mac_addr+i) , HEX);Serial.print("  ");
  18.   }
  19.   Serial.println();
  20.   if( status == ESP_NOW_SEND_SUCCESS )
  21.   {
  22.     Serial.println("ESP_NOW_SEND_SUCCESS"); //0是成功,1是失败
  23.     digitalWrite(LED4,!digitalRead(LED4));
  24.   }

  25. }

  26. void setup()
  27. {
  28.   pinMode( LED4 , OUTPUT );
  29.   pinMode( LED5 , OUTPUT );
  30.   digitalWrite(LED4,LOW);
  31.   digitalWrite(LED5,LOW);
  32.   Serial.begin(115200);
  33.   WiFi.mode(WIFI_STA);

  34.   esp_err_t erro = esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE);
  35.   Serial.println("ESP_NOW_DEMO");
  36.    Serial.print("esp_wifi_set_channel:");
  37.   switch (erro)
  38.   {
  39.     case ESP_OK:Serial.println("succeed");
  40.     break;
  41.     case ESP_ERR_WIFI_NOT_INIT:Serial.println("WiFi is not initialized by esp_wifi_init");
  42.     break;
  43.     case ESP_ERR_WIFI_IF:Serial.println("invalid interface");
  44.     break;
  45.     case ESP_ERR_INVALID_ARG:Serial.println("invalid argument");
  46.     break;   
  47.   }
  48.   Serial.print("STA MAC: "); Serial.println(WiFi.macAddress());
  49.   Serial.print("STA CHANNEL "); Serial.println(WiFi.channel());

  50.   //初始化ESP-NOW
  51.   if(esp_now_init() == ESP_OK)
  52.   {
  53.     Serial.println("ESPNow Init Success");
  54.   }
  55.   else
  56.   {
  57.     Serial.println("ESPNow Init Failed");
  58.     ESP.restart();
  59.   }
  60.   // Once ESP-Now is successfully Init, we will register for Send CB to
  61.   // get the status of Trasnmitted packet
  62.   esp_now_register_send_cb(OnDataSent);

  63.   int16_t scanResults = WiFi.scanNetworks(false,false,false,300,CHANNEL);
  64.   Serial.print("Found "); Serial.print(scanResults); Serial.println(" devices ");
  65.   for (int i = 0; i < scanResults; ++i)
  66.   {
  67.     // Print SSID and RSSI for each device found
  68.     String SSID = WiFi.SSID(i);
  69.     int32_t RSSI = WiFi.RSSI(i);
  70.     String BSSIDstr = WiFi.BSSIDstr(i);

  71.     Serial.print(i + 1);Serial.print(": ");
  72.     Serial.print(SSID);//热点名字
  73.     Serial.print(" (");Serial.print(RSSI);Serial.print(") ");//热点信号强度
  74.     Serial.print(" [");Serial.print(BSSIDstr);Serial.println("] ");//热点物理地址
  75.     Serial.println(BSSIDstr.c_str());
  76.     // Check if the current device starts with `Slave`
  77.     if (SSID.indexOf("Slave") == 0)
  78.     {
  79.       // SSID of interest
  80.       Serial.println("Found a Slave.");
  81.       Serial.print(i + 1); Serial.print(": "); Serial.print(SSID);
  82.       Serial.print(" ["); Serial.print(BSSIDstr); Serial.print("]");
  83.       Serial.print(" ("); Serial.print(RSSI); Serial.print(")"); Serial.println("");
  84.       // Get BSSID => Mac Address of the Slave
  85.       int mac[6];
  86.       if( 6== sscanf(BSSIDstr.c_str(), "%2x:%2x:%2x:%2x:%2x:%2x",&mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5] ))
  87.       {
  88.         Serial.print("<");
  89.         for( int i = 0 ; i<6 ; i++ )
  90.         {
  91.           slave.peer_addr[i] = (uint8_t)mac[i];
  92.           Serial.print(slave.peer_addr[i]);Serial.print(" ");
  93.         }
  94.         Serial.println(">");

  95.         slave.channel = CHANNEL; // pick a channel,选择一个信道
  96.         slave.encrypt = 0; // no encryption,不加密
  97.       }
  98.     }
  99.   }
  100.   addStatus = esp_now_add_peer( &slave );
  101. }

  102. uint8_t data = 0;

  103. void loop()
  104. {
  105.   // put your main code here, to run repeatedly:
  106.   data++;

  107.   const uint8_t *peer_addr = slave.peer_addr;
  108.   if(addStatus == ESP_OK)
  109.   {
  110.     esp_err_t result = esp_now_send(peer_addr, &data, sizeof(data));
  111.     Serial.print("Send Status: ");
  112.     if (result == ESP_OK)
  113.     {
  114.       Serial.println("Success");
  115.     }
  116.     else
  117.     {
  118.       Serial.println("Error");
  119.     }
  120.   }
  121.   delay(3000);
  122. }
该例子,通过主动发现存在的指定通道的热点,然后逐个判断是否是想要的那些,然后找到目标后,对该目标发送一个变量,根据需要可以将发送内容改成别的内容,可以是字符串,可以是结构体。。。
下面是接收端代码
  1. #include <esp_now.h>
  2. #include <WiFi.h>
  3. #include <esp_wifi.h>

  4. //定义开发板载LED
  5. #define LED4 12
  6. #define LED5 13
  7. //定义通信通道宏,指定在通道1通信
  8. #define CHANNEL 1

  9. // callback when data is recv from Master,回调返回的变量为发送设备的MAC地址,传输来的数据,数据包的长度
  10. void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len)
  11. {
  12.   char macStr[18];
  13.   snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
  14.            mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  15.   Serial.print("Last Packet Recv from: "); Serial.println(macStr);
  16.   Serial.print("Last Packet Recv Data: "); Serial.println(*data);
  17.   Serial.print("Last Packet Recv len:"); Serial.println(data_len);
  18.   Serial.println("");
  19.   
  20.   digitalWrite(LED4,!digitalRead(LED4));

  21.   if(*data%2 == 0)
  22.   {
  23.     digitalWrite(LED5,!digitalRead(LED5));
  24.   }
  25.   
  26. }

  27. void setup()
  28. {
  29.   // put your setup code here, to run once:
  30.   pinMode( LED4 , OUTPUT );
  31.   pinMode( LED5 , OUTPUT );
  32.   digitalWrite(LED4,LOW);
  33.   digitalWrite(LED5,LOW);

  34.   Serial.begin(115200);
  35.   WiFi.mode(WIFI_AP);
  36.   const char *SSID = "Slave_A";
  37.   bool result = WiFi.softAP(SSID,"Slave_A_Password",CHANNEL,0);
  38.   if(!result)
  39.   {
  40.     Serial.println("Ap Config failed.");
  41.   }
  42.   else
  43.   {
  44.     Serial.println("AP Config Success. Broadcasting with AP: " + String(SSID));
  45.     Serial.print("AP CHANNEL "); Serial.println(WiFi.channel());
  46.   }

  47.   // This is the mac address of the Slave in AP Mode
  48.   Serial.print("AP MAC: "); Serial.println(WiFi.softAPmacAddress());

  49.   WiFi.disconnect();
  50.   //初始化ESP-NOW
  51.   if(esp_now_init() == ESP_OK)
  52.   {
  53.     Serial.println("ESPNow Init Success");
  54.   }
  55.   else
  56.   {
  57.     Serial.println("ESPNow Init Failed");
  58.     ESP.restart();
  59.   }
  60.   // Once ESPNow is successfully Init, we will register for recv CB to
  61.   // get recv packer info.
  62.   esp_now_register_recv_cb(OnDataRecv);
  63. }

  64. void loop()
  65. {
  66.   // put your main code here, to run repeatedly:

  67. }
通过接收回调函数可以获取到发送者的MAC地址,以及发送的数据和数据长度。
该例子中根据接收的内容控制两个LED的亮灭。用于远距离测试时候作为指示标志。
本人经过接收器插入一个移动电源,在楼内走动,发现穿墙能力非常的强大,值的学习。

评论

[url=home.php?mod=space&uid=724503]@gaoyang9992006[/url] :通过MAC地址和对应的信道让发送函数发送数据  发表于 2023-8-18 11:03
技术总结:发送端查找接收端的热点,并把符合要求的热点的MAC找到,通过MAC地址和信号让发送函数发送数据;接收端监听对应信道发送给自己MAC地址的消息,并返回发送者的MAC与数据。  发表于 2023-8-18 09:38
 楼主| gaoyang9992006 发表于 2023-8-17 22:25 | 显示全部楼层
本帖最后由 gaoyang9992006 于 2023-8-17 22:26 编辑

2591064de2db07bb1a.png
该图是发送和接收设备串口打印的消息。
  1. ESP_NOW_DEMO
  2. esp_wifi_set_channel:succeed
  3. STA MAC: 60:55:F9:74:15:C0
  4. STA CHANNEL 1
  5. ESPNow Init Success
  6. Found 6 devices
  7. 1: Slave_A (-28)  [D4:F9:8D:3E:17:BD]
  8. D4:F9:8D:3E:17:BD
  9. Found a Slave.
  10. 1: Slave_A [D4:F9:8D:3E:17:BD] (-28)
  11. <212 249 141 62 23 189 >
  12. 2: WXA (-62)  [4C:C6:4C:B6:CD:EA]
  13. 4C:C6:4C:B6:CD:EA
  14. 3: DIRECT-5C-HP Laser 136nw (-68)  [52:81:40:D9:31:5C]
  15. 52:81:40:D9:31:5C
  16. 4: XGY-NJY (-74)  [98:F1:81:1A:C2:90]
  17. 98:F1:81:1A:C2:90
  18. 5: hw_manage_5be0 (-79)  [F8:98:EF:E6:5B:EB]
  19. F8:98:EF:E6:5B:EB
  20. 6: ChinaNet-7uLj (-90)  [38:88:1E:B5:E5:90]
  21. 38:88:1E:B5:E5:90
  22. Send Status: Success
  23. 0xD4  0xF9  0x8D  0x3E  0x17  0xBD  
  24. ESP_NOW_SEND_SUCCESS
  25. Send Status: Success
  26. 0xD4  0xF9  0x8D  0x3E  0x17  0xBD  
  27. ESP_NOW_SEND_SUCCESS
  1. Last Packet Recv from: 60:55:f9:74:15:c0
  2. Last Packet Recv Data: 1
  3. Last Packet Recv len:1

  4. Last Packet Recv from: 60:55:f9:74:15:c0
  5. Last Packet Recv Data: 2
  6. Last Packet Recv len:1

  7. Last Packet Recv from: 60:55:f9:74:15:c0
  8. Last Packet Recv Data: 3
  9. Last Packet Recv len:1

  10. Last Packet Recv from: 60:55:f9:74:15:c0
  11. Last Packet Recv Data: 4
  12. Last Packet Recv len:1

  13. Last Packet Recv from: 60:55:f9:74:15:c0
  14. Last Packet Recv Data: 5
  15. Last Packet Recv len:1



评论

技术总结:发送端查找接收端的热点,并把符合要求的热点的MAC找到,通过MAC地址和信道让发送函数发送数据;接收端监听对应信道发送给自己MAC地址的消息,并返回发送者的MAC与数据。  发表于 2023-8-18 09:39
598330983 发表于 2023-8-17 23:24 来自手机 | 显示全部楼层
mark学一下
WENHX 发表于 2023-8-18 09:20 | 显示全部楼层
这个要好好学,谢了
laocuo1142 发表于 2023-8-18 09:52 | 显示全部楼层
mark学一下
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:如果你觉得我的分享或者答复还可以,请给我点赞,谢谢。

2046

主题

16356

帖子

221

粉丝
快速回复 在线客服 返回列表 返回顶部