【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

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

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

 

M5StickCでESP-NOW その1

 

M5StickCでESP-NOW その2

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

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

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

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

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

 

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

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

 

 

 

 

●測定に使ったプログラム

 

#include <M5StickC.h>
#include <esp_now.h>
#include <WiFi.h>
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]);
  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");
  // 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();
  // ボタンを押したら送信
  if ( M5.BtnA.wasPressed() ) {
    uint8_t data[2] = {123, 234};
    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(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);}

コメントを残す

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