【PowerMeter2020】CPU間無線同期にESP-NOW学習開始<導入簡単で低遅延>

左右クランクのCPU(M5StickC)とサドル下の母艦CPU(M5StackBasic)と3個の計測タイミングを合わせて、周期ズレを極小まで減少させる検討中。

●判ってきたこと
①M5StickのRTC精度が悪い、NTP精度はよいが、msec間隔でNTP補正はむずかしいので
NTPをmsecオーダーのサンプリングのタイミング補正には使えない。
➁BlueToothで左右クランクからデータを送信しても、スマホでは、1個しか受信できないので、クランクからのBlueToothデータ送信は不便なので使えない。

③Data送信は、無線で行うが、左右クランクデータを母艦CPU1個でどうやって受信できるかが課題で2通りの方式
受信A:ESP-NOWのみ:左右のESP-NOWからの信号を母艦で取りこぼしなく受信できるか
受信B:Xbee2個で受信して、2ポートのUARTを取りこぼしなく受信できるか

今回本記事では、ESP-NOWの同期遅延時間測定だけにして、データ通信能力は次回の測定のまわす。

●新たな同期方式
①クランク周期毎で各CPUのタイミングをスタートリセットすれば精度がでる
サドル下母艦で、クランクの上死点下死点センシングして、そのタイミングを
左右クランクのM5stickで割り込み受信してサンプリングをスタートさせて
次の上死点タイミングでリセットする方式で3つのCPUの同期をとる方式

 

 

 

●ESP-NOWを試す
https://www.espressif.com/en/products/software/esp-now/overview

 

API Reference:
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html

■ESP-NOWの解説記事
資料:https://www.espressif.com/en/products/software/esp-now/resources

LANG-SHIP様の解説記事とサンプルプログラムを使わせていただきました。

 

https://lang-ship.com/blog/work/m5stickc-esp-now-1/

 

https://lang-ship.com/blog/work/m5stickc-esp-now-2/

●理解したESP-NOWのメリット
Espressiffの独自通信規格ですのでESP32のみです。
⓪インターネット接続が不要なWiFi通信なので無線ルーターが不要。

①TCP/IPなどの知識がなくてもMACアドレス指定だけESP32間無線通信が簡単。

➁ブロードキャスト送信ができるMACアドレスFF:FF:FF:FF:FF:FFだけでよい。

③Slave Mastere区別がないので同一通信プログラムで済む。

④レスポンスが速いが、TCP/IPのようなデータのチャックがない。

※2021年7月追記 ESP-NOW使い初めて1年経過して、ESP-NOWは1パケット250byte制限があるので、大容量高速無線通信が必要になったのでUDPに挑戦してます触り初めですが、UDPシンプルで超速いですが、アンテナ状態など無線アナログばらつきをくらいそうです。

【M5】WiFi UDPを初めていじってみた<めっちゃ速い>

 

 

 

●ESP-NOW動作
M5Stackを母艦CPU、M5StickCをクランク用CPUとして、両者に上記サンプルプログラムをコンパイルします。

①同期遅延時間の測定のために、GPIO26に信号をだしてオシロでみました。
黄色:M5Stackのボタンを押すと送信信号がでてLOWエッジでます。
青色:M5Stickが上記信号を受信してLOWエッジがでます。
遅延時間は920μsecで1msec以下なので本測定では許容範囲となります。

 

 

 

 

●測定に使ったプログラム
こちらのブログ記事をそのままコピペさせていただきました。作者のLANGSHIP様へ感謝です。

https://lang-ship.com/blog/work/m5stickc-esp-now-1/

GISTに使ったソースあります。

#include <M5Stack.h>
#include <esp_now.h>
#include <WiFi.h>esp_now_peer_info_t slave;
int j=0;
uint8_t ti,ti_1;
int RTC_Pin=0;//GP0
int NTP_Pin=26;//GP26
// 送信コールバック
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
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 Sent to: “);
Serial.println(macStr);
Serial.print(“Last Packet Send Status: “);
Serial.println(status == ESP_NOW_SEND_SUCCESS ? “Delivery Success” : “Delivery Fail”);// 画面にも描画
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0, 0);
M5.Lcd.print(“Last Packet Sent to: \n “);
M5.Lcd.println(macStr);
M5.Lcd.print(“Last Packet Send Status: \n “);
M5.Lcd.println(status == ESP_NOW_SEND_SUCCESS ? “Delivery Success” : “Delivery Fail”);
}// 受信コールバック
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.printf(“Last Packet Recv from: %s\n”, macStr);
//Serial.printf(“Last Packet Recv Data(%d): “, data_len);
for ( int i = 0 ; i < data_len ; i++ ) {
Serial.print(data[i]);
Serial.print(” “);
}
Serial.println(“”);// 画面にも描画
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0, 0);
M5.Lcd.print(“Last Packet Recv from: \n “);
M5.Lcd.println(macStr);
M5.Lcd.printf(“Last Packet Recv Data(%d): \n “, data_len);
for ( int i = 0 ; i < data_len ; i++ ) {
M5.Lcd.print(data[i]);
M5.Lcd.print(” “);
}
}void setup() {
M5.begin();
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setRotation(3);
M5.Lcd.print(“ESP-NOW Test\n”);
// Ext Signal Pin
pinMode(RTC_Pin, OUTPUT);
pinMode(NTP_Pin, OUTPUT);
// ESP-NOW初期化
WiFi.mode(WIFI_STA);
WiFi.disconnect();
if (esp_now_init() == ESP_OK) {
Serial.println(“ESPNow Init Success”);
M5.Lcd.print(“ESPNow Init Success\n”);
} else {
Serial.println(“ESPNow Init Failed”);
M5.Lcd.print(“ESPNow Init Failed\n”);
ESP.restart();
}// マルチキャスト用Slave登録
memset(&slave, 0, sizeof(slave));
for (int i = 0; i < 6; ++i) {
slave.peer_addr[i] = (uint8_t)0xff;
}
esp_err_t addStatus = esp_now_add_peer(&slave);
if (addStatus == ESP_OK) {
// Pair success
Serial.println(“Pair success”);
}// ESP-NOWコールバック登録
esp_now_register_send_cb(OnDataSent);
esp_now_register_recv_cb(OnDataRecv);
}void loop() {M5.update();
j++;
// ボタンを押したら送信
if ( M5.BtnA.wasPressed() ) {
digitalWrite(NTP_Pin,0);ti_1=ti;
ti=millis();uint8_t data[2] = {j, ti};
esp_err_t result = esp_now_send(slave.peer_addr, data, sizeof(data));
Serial.print(“Send Status: “);
if (result == ESP_OK) {
Serial.println(“Success”);
} else if (result == ESP_ERR_ESPNOW_NOT_INIT) {
Serial.println(“ESPNOW not Init.”);
} else if (result == ESP_ERR_ESPNOW_ARG) {
Serial.println(“Invalid Argument”);
} else if (result == ESP_ERR_ESPNOW_INTERNAL) {
Serial.println(“Internal Error”);
} else if (result == ESP_ERR_ESPNOW_NO_MEM) {
Serial.println(“ESP_ERR_ESPNOW_NO_MEM”);
} else if (result == ESP_ERR_ESPNOW_NOT_FOUND) {
Serial.println(“Peer not found.”);
} else {
Serial.println(“Not sure what happened”);
}
delay(5);
digitalWrite(NTP_Pin,1);
}
delay(10);}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です