基礎介紹:

ESP-NOW,ESP-NOW是一個沒有握手協議(CHAP)的一種通訊方式,以現有的WIFI協議衍生出類似UDP的溝通方式,頻段為2.4GHz,跟現有主流的有線或無線的通訊相比還有一個優勢

是在正常情況下不會產生競爭危害(race hazard)及具備訊息佇列(Message queue)。
 

何謂CHAP:

握手協議是一個用來驗證用戶或網絡提供者的協議。通過三次握手周期性的校驗對端的身份,可在初始鏈路建立時完成時,在鏈路建立之後重複進行。

以下這個例子說明,如果 A 要向 B 進行驗證,所需進行的步驟:
 在連線建立之後,使用者 A 會發出一個「challenge」信息給使用者 B。
 當 B 在收到這個訊息後,會使用 hash function,像是MD5(哈希函數) 來計算出雜湊值。
 之後 B 會將這個雜湊值送回去給 A。
 A 在收到之後,也會使用自身的 hash function 將原本的「challenge」信息運算,得  到一組雜湊值。
 此時 A 就可以比較:自己算出來的這組與收到(從B來)的雜湊值是否相同,如果相同,   則通過驗證。
 之後 B 也可以彷照上述方法,向 A 進行驗證。

何謂UDP :

在TCP/IP模型中,UDP為網路層以上和應用層以下提供了一個簡單的介面。UDP只提供資料的不可靠傳遞,它一旦把應用程式發給網路層的資料傳送出去,就不保留資料備份(俗稱暴力傳輸)。 UDP適用於不需要或在程式中執行錯誤檢查和糾正的應用,它避免了協定棧中此類處理的開銷。對時間有較高要求的應用程式通常使用UDP,因為丟棄封包比等待或重傳導致延遲更可取

何謂競爭危害(race hazard) :

競爭危害(race hazard)又名競態條件、競爭條件(race condition),它旨在描述一個系統或者進程的輸出依賴於不受控制的事件出現順序或者出現時機。此詞源自於兩個訊號試著彼此競爭,來影響誰先輸出。 舉例來說,如果電腦中的兩個行程同時試圖修改一個共享記憶體的內容,在沒有並行控制的情況下,最後的結果依賴於兩個行程的執行順序與時機。而且如果發生了並行存取衝突,則最後的結果是不正確的。

何謂訊息佇列(Message queue) :

訊息佇列提供了非同步的通信協定,每一個貯列中的紀錄包含詳細說明的資料,包含發生的時間,輸入裝置的種類,以及特定的輸入參數,也就是說:訊息的傳送者和接收者不需要同時與訊息佇列互動。訊息會儲存在佇列中,直到接收者取回它 訊息佇列本身是非同步的,它允許接收者在訊息傳送很長時間後再取回訊息,這和大多數通信協定是不同的。例如HTTP協定(HTTP/2之前)是同步的,因為客戶端在發出請求後必須等待伺服器回應。然而,很多情況下我們需要非同步的通信協定。比如,一個行程通知另一個行程發生了一個事件,但不需要等待回應。但訊息佇列的非同步特點,也造成了一個缺點,就是接收者必須輪詢訊息佇列,才能收到最近的訊息。

何謂ESP-NOW:

ESP-NOW 是樂鑫開發的一種協議,它可以讓多個設備在不使用 Wi-Fi 的情況下相互通信。該協議類似於低功耗 2.4GHz 無線連接 (...)。在設備之間進行通信之前需要進行配對。配對完成後,連接是安全的點對點連接,不需要握手 ESP-NOW 技術也有以下限制:

1.有限的加密對等點。Station模式最多支持10個加密peer;SoftAP或     SoftAP+ Station模式下最多6個;

2.支持多個未加密對等體,但其總數應小於20,包括加密對等體;

3.有效負載限制為  250 bytes

 

image

 

實驗所需:

ESP32 兩個以上

 

以下是針對多對一的情況來進行範例講解(因為這個比較容易用到)

實驗步驟:

首先先取得接收端的ESP32晶片的MACDRESS

以下是程式碼:

#include <WiFi.h>

void setup() {
  Serial.begin(115200);
  Serial.println();
#ifdef ESP8266
  Serial.print("ESP8266 Board MAC Address:  ");
  Serial.println(WiFi.macAddress());
#elif defined ESP32
  WiFi.mode(WIFI_MODE_STA);
  Serial.print("ESP32 Board MAC Address:  ");
  Serial.println(WiFi.macAddress());
#endif
}

void loop() {

}

 

把序列副視窗看到的MAC記錄下來,待會會用到

 

接下來是接收端的CODE:

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

String hallVal2;

// 数据接收回调函数
void OnDataRecv2(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&hallVal2, incomingData, sizeof(hallVal2));
  //
  Serial.print("hallVal2: ");
  Serial.println(hallVal2);
  //
}

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

  // 初始化 ESP-NOW
  WiFi.mode(WIFI_STA);
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
}

void loop() {
// 设置接收数据回调函数
  esp_now_register_recv_cb(OnDataRecv2);
  delay(500);
}

接下來是發送端的CODE:

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

int hallVal2=1;
String SSD;

// 接收设备的 MAC 地址
uint8_t broadcastAddress[] = {0x24, 0x0A, 0xC4, 0x9F, 0x43, 0x7C};  //這是我接收端的MAC,請改成你自己MAC的位址

// 数据发送回调函数
void OnDataSent2(const uint8_t *mac_addr, esp_now_send_status_t status) {
  char macStr[18];
  Serial.print("Packet to: ");
  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);
  Serial.print("Send status: ");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? SSD ="Delivery Success" : SSD ="Delivery Fail");
  //
  if(SSD == "Delivery Success")
  {
    Serial.println("OK");
    hallVal2= hallVal2 + 1; // 如果通道成功傳輸後才+1,失敗的話就會繼續傳上一筆的資料
  }else
  {
    Serial.println("NO");
   }
  //
  Serial.println();
}

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

  // 初始化 ESP-NOW
  WiFi.mode(WIFI_STA);
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // 设置发送数据回调函数
  esp_now_register_send_cb(OnDataSent2);

  // 绑定数据接收端
  esp_now_peer_info_t peerInfo;
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;

  // 检查设备是否配对成功
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println("Failed to add peer");
    return;
  }
}

void loop() {

  String mes = "E" + String(hallVal2);  //如果你有很多片ESP要發送的話建議每一片都用不一樣的字母,這樣才能辨識出哪一筆資料是誰的
  Serial.println(mes);

  // 发送数据
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &mes, sizeof(mes));

  // 检查数据是否发送成功
  if (result == ESP_OK) {
    Serial.println("Sent with success");
  }
  else {
    Serial.println("Error sending the data");
  }
  delay(500);
  
}

 

成功之後你會看到類似以下的東西,但因為我後來有把接收端改成JSON格式發送所以才會變成下圖這樣

而上面的是基礎範例,所以格式不會跟下圖一樣,如果你照我的做法做看到不同的顯示請不要多想

image

下面是國外對ESP-NOW的說明,會比我說的更詳細,我是針對比較常用到的架構來做為範例內容可能不太完善還請見諒

https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/

 

 

 


 

arrow
arrow
    創作者介紹
    創作者 凶王 的頭像
    凶王

    凶王的部落

    凶王 發表在 痞客邦 留言(1) 人氣()