06.M5 BasicとATOM Lite間でESP-NOW通信


06.M5 BasicとATOM Lite間でESP-NOW通信

ATOM LiteとM5Stack Basic各1台を使用し、ATOMのデータをBasicにESP-NOWで送信します。ESP-NOWはwifiの機能を使用しますが、ルーターを経由せずに機器同士で直接通信します。

必要ハード

・ATOM Lite x1
・M5Stack Basic x1

再インストール

PC(ディスクトップ i7 Windows11)を新規購入したので環境を再インストールしました。
https://docs.m5stack.com/en/quick_start/m5core/arduino に従って進みます。

USBドライバの CP210x_VCP_Windows をダウンロードし、
CP210xVCPInstaller_x64_v6.7.0.0.exe を実行。

Arduino-IDE 2.0.2

https://www.arduino.cc/en/software にて
Win10以降 64ビットをダウンロード、実行。

ボード

Arduino-IDE > 基本設定 > 追加のボードマネージャのURL:に
https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/arduino/package_m5stack_index.json
を貼付け > OK
Arduino-IDE > ツール > ボード > ボードマネージャ > "M5Stack" で検索 > M5Stack by M5Stack official 2.0.5-1.0 をインストール

接続

M5Stack-ATOM をPCにUSB接続

ボードの選択

Arduino-IDE > ツール > (上側の)ボード > M5Stack > M5Stack-ATOM
Arduino-IDE > ツール > (下側の)ボード > COM□

ATOMのライブラリ

Arduino-IDE > スケッチ > ライブラリをインクルード > ライブラリを管理... > "ATOM" で検索 > M5Atom by M5Stack 0.1.0 をインストール > 全てをインストール

気圧センサーBMP280

Arduino-IDE > スケッチ > ライブラリをインクルード > ライブラリを管理... > "BMP280" で検索 > Adafruit BMP280 Library by Adafruit 2.2.0 をインストール > 全てをインストール

温湿度センサーSHT30

Arduino-IDE > スケッチ > ライブラリをインクルード > ライブラリを管理... > "Adafruit SHT" で検索 > Adafruit SHT31 Library by Adafruit 2.6.6 をインストール > 全てをインストール

シリアルモニタの設定

CRおよびLF, 115200baud

その他のライブラリ

・M5Stack by M5Stack 0.4.3 で、全てをインストール
・M5Core-Ink by M5Stack 0.0.6
・LovyanGFX by lovyan03 0.4.18
・M5Stack_OnScreenKeyboard by lovyan03 0.3.4
 オンスクリーンキーボード
 https://github.com/lovyan03/M5Stack_OnScreenKeyboard

M5Stack Basicは、ボード = M5Stack-Core-ESP32 を
ATOM Liteは、ボード = M5Stack-ATOM を選択します。
その後、アップデートがあった場合は、"全てをインストール"しました。

ESP-NOW

・Espressif が開発し、ESP32等で利用できる通信プロトコルです。
・送受信は250byteまで。
・SoftAP + ステーション モードでは6台まで。

・一方向通信:(1→多, 多→1)
・双方向通信:(1←→1, 多←→多)
ができますが、ここでは、一方向ポイントツーポイント通信(1→1)で送信します。

送信機の判別:送信データに各ボード番号を入れ、受信側でどの送信機から来たかを判別します。
受信機の判別:送信側で受信機のMACアドレスを指定します。
ペアリング後は、ボードの1つが再起動しても自動的に接続して通信を継続します。送信側は、送りっぱなしで、受信側が起動していなくてもエラーは出ません。

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html?highlight=esp_now_register_recv_cb

ESP-NOWの解説

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

ボードの設定

Arduino-IDE > ツール にて

ATOM Lite

・ボード = "M5Stack-ATOM"
・Core Debug Level = None (/ Error / Warm / Info / Debug / Verbose)
・Erase All Flash Before Sketch Upload = Disabled (/ Enabled)
・Partition Scheme = Default (/ No OTA / Minimal SPIFFS)
・Upload Speed = 1500000(/ 750k / 500k / 250k / 115.2k)
・書き込み装置 = Esptool

M5Stack Basic

・ボード = "M5Stack-ATOM"
・Core Debug Level = None (/ Error / Warm / Info / Debug / Verbose)
・Erase All Flash Before Sketch Upload = Disabled (/ Enabled)
・Partition Scheme = Default (/ No OTA / Minimal SPIFFS)
・Upload Speed = 1500000(/ 750k / 500k / 250k / 115.2k)
・書き込み装置 = Esptool

MACアドレス

Media Access Control Addressの略で、ネット上の各デバイスを識別するハードウェア固有の識別子です。
例) 30:AE:A4:07:0D:64
MACアドレスはメーカーによって割り当てられますが、カスタムMACアドレスを与えることもできます。ただし、ボードがリセットされるたびに、元のMACアドレスに戻ります。

ATOMのMACアドレスを求める


// wifiに未接続で自分のMACアドレスをシリアルモニタに表示
// https://mobile.k05.biz/e/2018/12/esp32-mac-addr.html より
#include <M5Atom.h>     // ATOMを使用
uint8_t buf[6];         // アドレス表示用バッファ

void setup() {
  M5.begin(true, false, true);        // atom初期化(Serial,I2C?,Display)
  delay(1000);                        // 1秒待つ
  esp_read_mac(buf, ESP_MAC_WIFI_STA);// MACアドレス取得
  Serial.printf("%02X:%02X:%02X:%02X:%02X:%02X",
                buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); // 表示
}
void loop() {}

BasickのMACアドレスを求める


// wifiに未接続で自分のMACアドレスをシリアルモニタに表示
// https://mobile.k05.biz/e/2018/12/esp32-mac-addr.html より
#include   // M5Stack Basicを使用
uint8_t buf[6];       // アドレス表示用バッファ

void setup() {
  M5.begin();                           // M5stack初期化
  delay(1000);                          // 1秒待つ
  esp_read_mac(buf, ESP_MAC_WIFI_STA);  // MACアドレス取得
  Serial.printf("%02X:%02X:%02X:%02X:%02X:%02X",
                buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);  // 表示
}
void loop() {}
結果は、
Basic 2号機 8C:AA:B5:82:26:5C
ATOM 3号機 50:02:91:92:2F:E4
ATOM 4号機 50:02:91:91:FF:94
となりました。
今回は、Basicの2号機とATOMの3号機を使用します。送信側ATOMのスケッチに受信側BasicのMACアドレスを記入します。

スケッチ説明

データ構造体は送受信の設定は同一にします。構造体は、文字列(myData.a)、整数(myData.b)、浮動小数点(myData.c)、bool(myData.d)とします。今回のスケッチでは、整数のみ乱数で変化します。

受信側Basic

setup(){}では、
 esp_now_init()でESP-NOWを初期化します。
 esp_now_register_rcv_cb()でESP-NOWデータ受信時のコールバック関数(OnDataRecv)を登録します。これは、メッセージを受信時に実行される関数です。
loop(){}では、
 何もしません。
受信コールバック関数では、
 関数内で、メッセージを変数に保存して、その情報でタスクを実行します。実際は、送られてきたデータを画面に表示し、受信時に●を画面右上に表示します。

送信側ATOM

最初に、受信側のMACアドレスを設定しておきます。
setup(){}では、
 esp_now_init()でESP-NOWを初期化します。
 esp_now_register_send_cb()で送信後に実行するコールバック関数(OnDataSent)を登録します。
 esp_now_add_peer()で受信側のMACアドレスを追加します。
loop(){}では、
 送信データを設定し、esp_now_send()で送ります。
送信コールバック関数では、
 シリアルモニタに成功したかを表示します。また、送信成功時は緑が点灯、エラー時は赤が点灯します。受信しているかは見ていません。受信側が起動していなくても、送信します。

ATOMの消費電流

Vin=5V Iin=0.12A Win=0.6W
RT-USBVAC7QCにて測定しました。
https://route-r.co.jp/?p=3592

購入は、秋月電子 2680円
https://akizukidenshi.com/catalog/g/gM-16929/

参考

ピア(peer)

現在接続している相手のPCや通信機器のことです。広義には、ある機器が通信を行う際にその相手方となる機器などのこと全般を指してピアと言います。接続中の2台の機器の組のことを指す場合もあります。また、WebサーバとWebブラウザのように、同じプロトコルで通信しているプログラム同士、および、一方から見た相手方のことを指すこともあります。狭義には、サーバとクライアントのような役割分担や上下関係がなく、機能や地位が対等な機器同士が相互に通信する接続形態における個々の通信主体のことをピアと言います。例えば、大手通信事業者のネットワーク同士が対等な関係に接続することをピアリング(peering)、対等な端末同士が直に接続しあってネットワークを形成する通信方式をピアツーピア(P2P, Peer to Peer)と言います。

ペイロード(payload)

送受信されるデータの伝送単位のうち、宛先などの制御情報を除いた、相手に送り届けようとしている正味のデータ本体のことです。データ自体のことを指す場合と、データ長や最大長を意味する場合があります。パケットやデータグラムの多くは、ヘッダ部、続けてペイロード(ボディとも)、必要に応じて末尾にパディング(特定長さに合わせるための詰め物となる無意味なデータ)の構成になっています。

スケッチ

Basic側


// 受信側 Basic  ATOMから送信
//https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/ を修正
#include <M5Stack.h>            // M5Stack Basicを使用
#include <esp_now.h>            // ESP-NOW通信を使用
#include <WiFi.h>               // wifiを使用
#define LGFX_AUTODETECT         // LovyanGFX自動認識
#define LGFX_USE_V1             // LovyanGFX Ver1を使用
#include <LGFX_AUTODETECT.hpp>  // クラス"LGFX"を用意
static LGFX lcd;                // LGFXのインスタンスを作成

typedef struct struct_message {  // 受信データ構造体(max250byte)
  char a[32];                    // myData.aは文字列 32文字まで
  int b;                         // myData.bは整数
  float c;                       // myData.cは浮動小数点
  bool d;                        // myData.dはtrue or false, booleanと同じ
} struct_message;
struct_message myData;  // struct_messageをmyDataとする

void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) {  // コールバック関数
  lcd.setCursor(280, 0);                                                     // 表示位置(x,y)
  lcd.print("●");                                                            // 受信時表示
  memcpy(&myData, incomingData, sizeof(myData));
  // メモリコピー(コピー先ポインタ,コピー元ポインタ,コピーバイト数)
  lcd.setCursor(0, 0);                        // 表示位置(x,y)
  lcd.printf("受信  :%02d byte\n\n", len);    // 受信バイト数
  lcd.printf("文字列:%s\n\n", myData.a);      // 文字列を表示
  lcd.printf("整数  :%02d\n\n", myData.b);    // 整数を表示
  lcd.printf("Float :%05.2f\n\n", myData.c);  // 浮動小数点を表示
  lcd.printf("ブール:%d", myData.d);          // true or falseを表示
  Serial.printf("%02d ", myData.b);           // 整数はシリアルモニタにも表示
  delay(20);                                  // 20mS待つ
  lcd.setCursor(280, 0);                      // 表示位置(x,y)
  lcd.print(" ");                            // 表示を消す
}

void LCDset() {                             // 初期画面設定関数
  lcd.init();                               // 画面初期化
  lcd.setRotation(1);                       // 回転方向(0-3)
  lcd.setBrightness(128);                   // バックライト輝度(0-255)
  lcd.setColorDepth(24);                    // RGB888 24bit (16=RGB565 16bit)
  lcd.setCursor(0, 0);                      // 表示位置(x,y)
  lcd.setFont(&fonts::lgfxJapanGothic_24);  // ゴシック(8,12,16,20,24,28,32,36,40)
  lcd.setTextColor(GREEN, BLACK);           // 色設定(文字色[,背景色])
}

void setup() {
  M5.begin();                                // M5stack初期化
  LCDset();                                  // 初期画面設定関数へ
  Serial.println();                          // シリアルモニタ 改行
  WiFi.mode(WIFI_STA);                       // Wi-Fiステーション(子機)に設定
  if (esp_now_init() != ESP_OK) {            // ESP-NOWの初期化が成功しなかったら
    Serial.println("エラー ESP-NOW初期化");  // 表示
    return;                                  //
  }
  esp_now_register_recv_cb(OnDataRecv);  // ESPNOWデータ受信のコールバック関数を登録
}

void loop() {}
* flash memory(1.3Mbyte)のうち、スケッチが88%使用。RAM(327kbyte)のうち、global変数が12%使用、local変数で287kbyte使用可能。(1000byte=1kbyteで計算)

ATOM側

MACアドレスは各自のアドレスを記入してください。

// 送信側 ATOM   Basic 2号機へ乱数等送信
// https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/ を修正
#include <esp_now.h>                                                  // ESP-NOW通信を使用
#include <WiFi.h>                                                     // wifiを使用
#include <M5Atom.h>                                                   // ATOM Lightを使用
uint8_t broadcastAddress[] = { 0x8C, 0xAA, 0xB5, 0x82, 0x26, 0x5C };  // 相手(Basic)のMACアドレス

typedef struct struct_message {  // 送信データ構造体
  char a[32];                    // myData.aは文字列 32文字まで
  int b;                         // myData.bは整数
  float c;                       // myData.cは浮動小数点
  bool d;                        // myData.dはtrue or false, booleanと同じ
} struct_message;
struct_message myData;         // struct_messageをmyDataとする
esp_now_peer_info_t peerInfo;  // 定義

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {  // コールバック関数
  Serial.print("\r\nパケット送信:");                                      // 表示
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "成功" : "失敗");       // statusが成功か表示
}

void setup() {
  M5.begin(true, false, true);               // 初期化(Serial,I2C?,Display)
  M5.dis.setBrightness(100);                 // LRDの明るさ 書かないとエラーになる
  Serial.println();                          // シリアルモニタ 改行
  WiFi.mode(WIFI_STA);                       // Wi-Fiステーション(子機)に設定
  if (esp_now_init() != ESP_OK) {            // ESP-NOWの初期化が成功しなかったら
    Serial.println("エラー ESP-NOW初期化");  // 表示
    return;
  }
  esp_now_register_send_cb(OnDataSent);  // 送信後のコールバック関数を登録
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  // メモリコピー(コピー先ポインタ,コピー元ポインタ,コピーバイト数)
  peerInfo.channel = 0;                         // デバイスのチャネル(0-14)
  peerInfo.encrypt = false;                     // 暗号化しない
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {  // デバイスを追加できなかったら
    Serial.println("エラー デバイス追加");      // 表示
    return;
  }
}

void loop() {
  strcpy(myData.a, "これは、文字列です");  // 文字列 送信値の設定
  myData.b = random(1, 20);                // 1~19の乱数 整数
  myData.c = 1.2;                          // 浮動小数点
  myData.d = false;                        // 真偽値
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&myData, sizeof(myData));
  // ESP-NOW送信(相手のMACアドレス,データ,データ長さ)
  if (result == ESP_OK) {          // 戻り値が成功なら
    Serial.println("送信成功");    // シリアルモニタに表示
    M5.dis.drawpix(0, 0x00ff00);   // 緑
  } else {                         // 戻り値が成功でなければ
    Serial.println("送信エラー");  // シリアルモニタに表示
    M5.dis.drawpix(0, 0xff0000);   // 赤
  }
  delay(10);       // 10mS待つ
  M5.dis.clear();  // 消灯
  delay(4990);     // 4.99秒待つ
}
* flash memory(1.3Mbyte)のうち、スケッチが54%使用。RAM(327kbyte)のうち、global変数が12%使用、local変数で287kbyte使用可能。(1000byte=1kbyteで計算)