左右クランクの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シンプルで超速いですが、アンテナ状態など無線アナログばらつきをくらいそうです。
●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/
#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);} |