打印
[复制链接]
1676|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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 上层协议精简为一层,数据传输时无需依次经过网络层、传输层、会话层、表示层、应用层等复杂的层级,也无需层层增加包头和解包,大大缓解了网络拥挤时因为丢包而导致的卡顿和延迟,拥有更高的响应速度。

经过两天的学习,已经掌握了基本用法,现将学习笔记记录如下:
发送端,发送端作为STA模式运行,接收端是被发现设备,因此作为AP模式运行。
发送端代码:
#include <esp_now.h>
#include <WiFi.h>
#include <esp_wifi.h>

//定义开发板载LED
#define LED4 12
#define LED5 13
//定义通信通道宏,指定在通道1通信
#define CHANNEL 1

// Global copy of slave
esp_now_peer_info_t slave;

esp_err_t addStatus;

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

}

void setup()
{
  pinMode( LED4 , OUTPUT );
  pinMode( LED5 , OUTPUT );
  digitalWrite(LED4,LOW);
  digitalWrite(LED5,LOW);
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);

  esp_err_t erro = esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE);
  Serial.println("ESP_NOW_DEMO");
   Serial.print("esp_wifi_set_channel:");
  switch (erro)
  {
    case ESP_OK:Serial.println("succeed");
    break;
    case ESP_ERR_WIFI_NOT_INIT:Serial.println("WiFi is not initialized by esp_wifi_init");
    break;
    case ESP_ERR_WIFI_IF:Serial.println("invalid interface");
    break;
    case ESP_ERR_INVALID_ARG:Serial.println("invalid argument");
    break;   
  }
  Serial.print("STA MAC: "); Serial.println(WiFi.macAddress());
  Serial.print("STA CHANNEL "); Serial.println(WiFi.channel());

  //初始化ESP-NOW
  if(esp_now_init() == ESP_OK)
  {
    Serial.println("ESPNow Init Success");
  }
  else
  {
    Serial.println("ESPNow Init Failed");
    ESP.restart();
  }
  // Once ESP-Now is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_register_send_cb(OnDataSent);

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

    Serial.print(i + 1);Serial.print(": ");
    Serial.print(SSID);//热点名字
    Serial.print(" (");Serial.print(RSSI);Serial.print(") ");//热点信号强度
    Serial.print(" [");Serial.print(BSSIDstr);Serial.println("] ");//热点物理地址
    Serial.println(BSSIDstr.c_str());
    // Check if the current device starts with `Slave`
    if (SSID.indexOf("Slave") == 0)
    {
      // SSID of interest
      Serial.println("Found a Slave.");
      Serial.print(i + 1); Serial.print(": "); Serial.print(SSID);
      Serial.print(" ["); Serial.print(BSSIDstr); Serial.print("]");
      Serial.print(" ("); Serial.print(RSSI); Serial.print(")"); Serial.println("");
      // Get BSSID => Mac Address of the Slave
      int mac[6];
      if( 6== sscanf(BSSIDstr.c_str(), "%2x:%2x:%2x:%2x:%2x:%2x",&mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5] ))
      {
        Serial.print("<");
        for( int i = 0 ; i<6 ; i++ )
        {
          slave.peer_addr[i] = (uint8_t)mac[i];
          Serial.print(slave.peer_addr[i]);Serial.print(" ");
        }
        Serial.println(">");

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

uint8_t data = 0;

void loop()
{
  // put your main code here, to run repeatedly:
  data++;

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

//定义开发板载LED
#define LED4 12
#define LED5 13
//定义通信通道宏,指定在通道1通信
#define CHANNEL 1

// callback when data is recv from Master,回调返回的变量为发送设备的MAC地址,传输来的数据,数据包的长度
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len)
{
  char macStr[18];
  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("Last Packet Recv from: "); Serial.println(macStr);
  Serial.print("Last Packet Recv Data: "); Serial.println(*data);
  Serial.print("Last Packet Recv len:"); Serial.println(data_len);
  Serial.println("");
  
  digitalWrite(LED4,!digitalRead(LED4));

  if(*data%2 == 0)
  {
    digitalWrite(LED5,!digitalRead(LED5));
  }
  
}

void setup()
{
  // put your setup code here, to run once:
  pinMode( LED4 , OUTPUT );
  pinMode( LED5 , OUTPUT );
  digitalWrite(LED4,LOW);
  digitalWrite(LED5,LOW);

  Serial.begin(115200);
  WiFi.mode(WIFI_AP);
  const char *SSID = "Slave_A";
  bool result = WiFi.softAP(SSID,"Slave_A_Password",CHANNEL,0);
  if(!result)
  {
    Serial.println("Ap Config failed.");
  }
  else
  {
    Serial.println("AP Config Success. Broadcasting with AP: " + String(SSID));
    Serial.print("AP CHANNEL "); Serial.println(WiFi.channel());
  }

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

  WiFi.disconnect();
  //初始化ESP-NOW
  if(esp_now_init() == ESP_OK)
  {
    Serial.println("ESPNow Init Success");
  }
  else
  {
    Serial.println("ESPNow Init Failed");
    ESP.restart();
  }
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info.
  esp_now_register_recv_cb(OnDataRecv);
}

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

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

使用特权

评论回复
评论
gaoyang9992006 2023-8-18 11:03 回复TA
@gaoyang9992006 :通过MAC地址和对应的信道让发送函数发送数据 
gaoyang9992006 2023-8-18 09:38 回复TA
技术总结:发送端查找接收端的热点,并把符合要求的热点的MAC找到,通过MAC地址和信号让发送函数发送数据;接收端监听对应信道发送给自己MAC地址的消息,并返回发送者的MAC与数据。 
沙发
gaoyang9992006|  楼主 | 2023-8-17 22:25 | 只看该作者
本帖最后由 gaoyang9992006 于 2023-8-17 22:26 编辑


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

Last Packet Recv from: 60:55:f9:74:15:c0
Last Packet Recv Data: 2
Last Packet Recv len:1

Last Packet Recv from: 60:55:f9:74:15:c0
Last Packet Recv Data: 3
Last Packet Recv len:1

Last Packet Recv from: 60:55:f9:74:15:c0
Last Packet Recv Data: 4
Last Packet Recv len:1

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



使用特权

评论回复
评论
gaoyang9992006 2023-8-18 09:39 回复TA
技术总结:发送端查找接收端的热点,并把符合要求的热点的MAC找到,通过MAC地址和信道让发送函数发送数据;接收端监听对应信道发送给自己MAC地址的消息,并返回发送者的MAC与数据。 
板凳
598330983| | 2023-8-17 23:24 | 只看该作者
mark学一下

使用特权

评论回复
地板
WENHX| | 2023-8-18 09:20 | 只看该作者
这个要好好学,谢了

使用特权

评论回复
5
laocuo1142| | 2023-8-18 09:52 | 只看该作者
mark学一下

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:西安公路研究院南京院
简介:主要工作从事监控网络与通信网络设计,以及从事基于嵌入式的通信与控制设备研发。擅长单片机嵌入式系统物联网设备开发,音频功放电路开发。

1961

主题

15933

帖子

208

粉丝