ESP-NOWで無線データ通信とマイコン間のタイミング同期ができるか検証測定してます。
前回は、母艦マイコン(M5StackBasic)からクランクマイコン(M5StickC)へ同期信号の
遅延時間を測定して1msec以内なのを確認したので、今回は、クランクデータ通信速度を測定してみた。今までXbeeで4msecはエラー0.5~1%もあってダメだった経緯があります。今回ESP-NOWを試してみて、ここ数年間XbeeやBlueToothを使ってきて速度が遅い双方向通信がやりにくいなど不便さがあったのですが、それが一挙に解決されました。2020年からは、データ無線通信はESP-NOWがメインになるかもしれません。
※2021年7月追記 1年後、ESP-NOWは1パケット250byteの制限があるので大容量高速通信に制限があるので、
現在シリアル460800bpsで通信しているMovingBase Base-Rover間で無線化ができないためUDPを使う検討をはじめました。
UDPめちゃ速いですが、データ落ちに対する補償手段がないので、信頼性確認が重要です。特にアンテナと環境が大事な感じですので、M5シリーズだとアンテナが内部で小さいので、ESP32-Wroom32のように大きな外だしアンテナがついてるほうが信頼性が高いです。
●速度実験測定
■方法:
クランクマイコンをM5StickCとして、送信します。
受信マイコンはM5StackBasicとして、1秒毎に同期信号をM5StickCへ送信します。
Data0:バイナリ整数4Byte=同期トリガーからのデータ番号
Data1:バイナリ整数4Byte=指定周期で回したmillis()値
※Data0は、同期信号を受信した瞬間にゼロリセットされます。
※無線通信データは短縮化するためにバイナリにして送信することにしました。
連続送信30分ログして、指定周期 8msecと4msecの場合で周期データの欠落個数の%を測定した。
■結果:
①M5Stickの送信データ99.99%以上はOKだった。
➁M5Stackの受信データ99.97%程度OKだった。
「Xbeeでは4msecを送信すると0.5%程度データ落ちが発生していたが、ESP-NOWだと一桁少なく0.03%程度しかデータ落ちしない。」
●ロードバイクパワーメーター用の無線システムが簡単になります。
ESP-NOWの実験結果より、パワーメーター2019で開発していた
Xbeeと外付けTCXOを使ったクランクデータ波形精度測定システムより
簡単でシンプルで精度がよいシステムの可能性がでてきました。
今までのアルゴリズムは、1つのCPUで、絶対時間精度をだしてサンプリングして
無線でデータ落ちしない速度で送信ログするシステムでしたが、
ESP-NOWでは、絶対時間精度は、求めずにクランクの1回転毎に全CPUの
同期をとることで、1回転内のわずかな時間誤差だけで、トルク波形の位相が管理する
というアルゴリズムにすることによって、大幅のクランクマイコンのデバイスを減らす
ことができる見込みを得ました。
●以後
ESP-NOWの制限として250byte以内と近距離無線があるが
ロードバイク用の無線方式としては、十分足りるので、
M5Stickを2個体制にして、M5Stack母艦で2個からデータ受信
できるか実験する準備をします。さらに、
ESP-NOWでロードバイクのクランクに実装して、実走行での実力を
調べてみることにする。
※使ったプログラム備忘録
参考にさせていただいたプログラムはLang-Ship様のここです。
①クランク用M5Stick ESP-NOWテストプログラム
//=======================Crank_ESP_Now_M5Stick_rev01========================================================= // M5StickC Crank Toruque Measuremnet System // 2020.4.30 Shinshu-Makers // //===================================================================================================== #include <M5StickC.h> #include <esp_now.h> #include <WiFi.h> //********************Genaral Parameters************************************************************ int j=0; int pushN=0; int value0,value1; uint8_t ti,ti_1; int RTC_Pin=0;//GP0 int NTP_Pin=26;//GP26 int t; int goZero=0; int sTime=0; uint8_t data[8]; //+++++++++++++++i_to_char+++++++++++++++++ // i=IntegerValueData,*d=Array pointer, n=Array start No void i_to_char(int i,uint8_t *d, int n) { t=millis(); d[n] = i & 0x000000ff; d[n+1] = (i & 0x0000ff00) >> 8; d[n+2] = (i & 0x00ff0000) >> 16; d[n+3] = (i & 0xff000000) >> 24; } //++++++++++++++++++++++++++++++++++++++++++ //=====================================ESP NOW Sub ================================================ esp_now_peer_info_t slave;// 送信コールバック 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]);}// 受信コールバック void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) { char macStr[18]; pushN++; //Serial.print(“*************Push********************”); //Serial.println(pushN); j=0;//reset Data couter} //============================================ESP-NOW Sub end========================================================================================== 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 ESP_error(esp_err_t result) } M5.update(); t=millis(); // if( pushN%2==1 )// 8msec data send i_to_char(j,data,0); Serial.print(value0); esp_err_t result = esp_now_send(slave.peer_addr, data, sizeof(data)); |
➁母艦M5Stack用プログラム
//=======================Crank_ESP_Now_M5Stack_Send_Rcv_rev01========================================================= // M5StickC Crank Toruque Measuremnet System // 2020.4.30 Shinshu-Makers // //=====================================================================================================#include <M5Stack.h> #include <esp_now.h> #include <WiFi.h>esp_now_peer_info_t slave; int value=0; int j=0; int ti,ti_1; int RTC_Pin=25;//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”);}// 受信コールバック void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) { char macStr[18]; digitalWrite(NTP_Pin,0); 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]);value=data[0]+data[1]*256+data[2]*256*256+data[3]*256*256; Serial.print(value); Serial.print(“, “); value=data[4]+data[5]*256+data[6]*256*256+data[7]*256*256; Serial.print(value); //} Serial.println(“”); digitalWrite(NTP_Pin,1); }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登録 // ESP-NOWコールバック登録 void loop() { M5.update(); ti=millis(); delay(2); } |