07.Basic等で3ヶ所の温度測定 ESP-NOW
07.Basic等で3ヶ所の温度測定 ESP-NOW
ATOM Lite 2台とM5Stack Basic 1台を使用し、3ヶ所の温湿度・気圧を測定し、ATOMのデータをBasicにESP-NOWで送信し表示します。
↓ 快晴のPM3
↓ その日のPM5
センサー精度は、温度:±0.2℃, 湿度:±2%, 気圧:±1hPa です。
必要ハード
・ATOM Lite x2・M5Stack Basic x1
・温湿度・気圧センサー ENV2 x3
ESP-NOW
前回に引き続きESP-NOWです。今回は、ESP32を使用したESP-NOW:複数のボードからデータを受信(多対1)
https://randomnerdtutorials.com/esp-now-many-to-one-esp32/
を参考にしています。
MACアドレスは前回調べました。以下の通りです。
Basic 2号機 8C:AA:B5:82:26:5C
居間(南側の室内)に置きます。
ATOM 3号機 50:02:91:92:2F:E4
倉庫(北側の室内)に置きます。
ATOM 4号機 50:02:91:91:FF:94
室外(仮 南側のベランダで野ざらし)に置きます。
スケッチ説明
データ配列boardsStruct[0]は、Basic 2号機
boardsStruct[1]は、board3 id=3 で ATOM 3号機
boardsStruct[2]は、board4 id=4 で ATOM 4号機
の測定データとします。
受信側Basic
前回からの変更点・センサのライブラリ読込み
・データ構造 (id,測定データ,配列)
・コールバック変数 (大幅変更)
・loop() 画面表示・表示間隔
等
・日本語サイズを32から36にしたら
"スケッチが大きすぎます。"
と表示されたので、
Arduino-IDE > ツール > Partition Scheme = Default → No OTA (Large APP)
に変更しました。
・受信は随時で、データ配列を更新します。
・表示間隔は10秒で、その時自分の測定値も表示します。
・画面右上に受信号機(3 or 4)を表示します。
・測定値の配列は受信したら書換えるので、途中から送信できなくなっても前のデータのままです。そこで、測定値を表示後に配列を0リセットします。
・気圧データが0の時は、受信できていないとして、その号機のデータを全て" -- "と表示します。
送信側ATOM
3号機 (前回からの変更点)・センサのライブラリ読込み
・データ構造 (id,測定データ)
・loop内のデータ作成(測定)内容
4号機 (3号機からの変更点)
・loop内の myData.id=3 → 4
倉庫と室外に置き、5秒ごとに測定と送信をして、送信成功すると緑点灯します。(受信できているかは判定しません)
動作の不具合の対処
Basic側表示
●何も表示しない (電源off) → 電源を確認。●数値が異常 (センサー断) → センサーを接続しBasicを再起動。
ATOM側表示
●" -- "と表示・(起動直後) → 10秒待つ
・ATOMのLEDは点滅 (通信が届かない) → 近くへ持ってくる。見晴らしの良い場所へ移動。
・ATOMのLEDは消灯 (電源off) → 電源を確認。
●数値が異常 (センサー断) → センサーを接続しBasicを再起動
スケッチ
Basic(受信)側
// 受信側 Basic ATOMから送信
// https://randomnerdtutorials.com/esp-now-many-to-one-esp32/ より変更
#include <M5Stack.h> // M5Stack Basicを使用
#include <esp_now.h> // ESP-NOW通信を使用
#include <WiFi.h> // wifiを使用
#include <Wire.h> // I2Cを使用
#define LGFX_AUTODETECT // LovyanGFX自動認識
#define LGFX_USE_V1 // LovyanGFX Ver1を使用
#include <LGFX_AUTODETECT.hpp> // クラス"LGFX"を用意
static LGFX lcd; // LGFXのインスタンスを作成
#include <Adafruit_SHT31.h> // 温湿度センサを使用
#include <Adafruit_BMP280.h> // 気圧センサを使用
Adafruit_SHT31 sht = Adafruit_SHT31(&Wire); // sht定義
Adafruit_BMP280 bme = Adafruit_BMP280(&Wire); // bmp定義だがbmeとする
int xx1 = 3, yy1 = 80, dxx1 = 93, dyy1 = 40; // タイトルの表示位置と表示刻み
typedef struct struct_message { // 受信データ構造体(max250byte)
int id; // myData.idは送信機No. (3-4)の2台
int tmp; // myData.tmpは10倍した温度(x0.1℃)
int hum; // myData.humは湿度(%)
int pre; // myData.preは気圧(hPa)
} struct_message;
struct_message myData; // 受信データを格納するmyDataというstruct_message変数を作成
struct_message board2; // ボード2(Basic 2号機)から読取った内容を保持する構造
struct_message board3; // ボード3(ATOM 3号機)から読取った内容を保持する構造
struct_message board4; // ボード4(ATOM 4号機)から読取った内容を保持する構造
// 各ボードにstruct_message変数を作成し、受信データを対応ボードに割当てる
struct_message boardsStruct[3] = { board2, board3, board4 }; // 全構造体を含む配列を作成 id=2,3,4
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *incomingData, int len) { // コールバック関数
lcd.setCursor(280, 0); // 表示位置(x,y)
lcd.setTextColor(WHITE, BLACK); // 黒地(前の字を消す)に白文字
char macStr[18]; // =2x6+5+1
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]); // ボードのMACアドレスを取得
//Serial.print(macStr); // 受信したMACアドレスを表示
memcpy(&myData, incomingData, sizeof(myData)); // 変数incomingDataの内容を変数myDataにコピー
// メモリコピー(コピー先ポインタ,コピー元ポインタ,コピーバイト数)
Serial.printf("%dbyte", len); // バイト数表示 整数4bytex4個=16
lcd.setCursor(280, 0); // 表示位置(x,y)
lcd.setTextColor(WHITE, BLACK); // 黒地(前の字を消す)に白文字
if (myData.id == 3) { // id=3なら
lcd.print("3"); // 受信時表示
Serial.print(" ATOM No.3="); // 表示
} else if (myData.id == 4) { // id=4なら
lcd.print("4"); // 受信時表示
Serial.print(" ATOM No.4="); // 表示
}
boardsStruct[myData.id - 2].tmp = myData.tmp; // 受信温度更新
boardsStruct[myData.id - 2].hum = myData.hum; // 受信湿度更新
boardsStruct[myData.id - 2].pre = myData.pre; // 受信気圧更新
Serial.printf(" %4.1f℃", (float)(boardsStruct[myData.id - 2].tmp) / 10.0); // 受信温度表示
Serial.printf("%3d%%", boardsStruct[myData.id - 2].hum); // 受信湿度表示
Serial.printf(" %4dhPa\n", boardsStruct[myData.id - 2].pre); // 受信気圧表示
delay(100); // 0.1秒待つ
lcd.setCursor(280, 0); // 表示位置(x,y)
lcd.print(" "); // 表示を消す
}
void LCDset() { // 初期画面設定関数
lcd.init(); // 画面初期化
lcd.setRotation(1); // 回転方向(0-3)
lcd.setBrightness(20); // バックライト輝度(0-255)
lcd.setColorDepth(24); // RGB888 24bit (16=RGB565 16bit)
lcd.setCursor(0, 0); // 表示位置(x,y)
lcd.setFont(&fonts::lgfxJapanGothic_36); // ゴシック(8,12,16,20,24,28,32,36,40)
lcd.setTextColor(GREEN, BLACK); // 色設定(文字色[,背景色])
}
void setup() {
M5.begin(); // M5stack初期化
Serial.begin(115200); // シリアルモニタ通信速度設定
LCDset(); // 初期画面設定関数へ
while (!bme.begin(0x76)) { // BMP280のアドレスで開始できない時
M5.lcd.println("エラー BMP280未接続"); // 表示
}
while (!sht.begin(0x44)) { // shtのアドレスで開始できない時
M5.lcd.println("エラー SHT3X未接続"); // 表示
}
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() { // 画面表示
boardsStruct[0].tmp = (int)(sht.readTemperature() * 10.0 + 0.5); // Basicのセンサー温度を読取る
boardsStruct[0].hum = (int)(sht.readHumidity() + 0.5); // Basicのセンサー湿度を読取る
boardsStruct[0].pre = (int)(bme.readPressure() / 100.0 + 0.5); // Basicのセンサー気圧を読取る
lcd.setTextColor(WHITE, BLACK); // 黒地(前の字を消す)に白文字
lcd.setCursor(xx1, yy1); // 表示位置(x,y)
lcd.print("居間"); // 表示
lcd.setCursor(xx1 + dxx1, yy1); // 表示位置(x,y)
lcd.print("倉庫"); // 表示
lcd.setCursor(xx1 + dxx1 * 2, yy1); // 表示位置(x,y)
lcd.print("室外"); // 表示
for (int i = 0; i < 3; i++) {
lcd.setCursor(xx1 + dxx1 * i, yy1 + dyy1); // 表示位置(x,y)
lcd.setTextColor(GREEN, BLACK); // 黒地(前の字を消す)に緑文字
if (boardsStruct[i].pre == 0) { // 0hPaなら
lcd.print(" -- "); // 表示
} else { // 0hPaでなければ
lcd.printf("%4.1f", (float)(boardsStruct[i].tmp / 10.0)); // 温度 文字サイズ1~7
}
boardsStruct[i].tmp = 0; // 表示後、データクリア
if (i == 2) { // もし最終号機だったら
lcd.setTextSize(0.75); // txt倍率
lcd.setCursor(xx1 + dxx1 * i + 85, yy1 + dyy1 + 5); // 表示位置(x,y)10 8
lcd.print("℃"); // 単位表示
lcd.setTextSize(1); // txt倍率を戻す
}
lcd.setCursor(xx1 + dxx1 * i, yy1 + dyy1 * 2); // 表示位置(x,y)
lcd.setTextColor(CYAN, BLACK); // 黒地にほぼ黄文字
if (boardsStruct[i].pre == 0) { // 0hPaなら
lcd.print(" -- "); // 表示
} else { // 0hPaでなければ
lcd.printf("%4d", boardsStruct[i].hum); // 湿度
}
boardsStruct[i].hum = 0; // 表示後、データクリア
if (i == 2) { // もし最終号機だったら
lcd.setTextSize(0.75); // txt倍率
lcd.setCursor(xx1 + dxx1 * i + 85, yy1 + dyy1 * 2 + 5); // 表示位置(x,y)
lcd.print("%"); // 単位表示
lcd.setTextSize(1); // txt倍率を戻す
}
lcd.setCursor(xx1 + dxx1 * i, yy1 + dyy1 * 3); // 気圧の表示位置(x,y)
lcd.setTextColor(PINK, BLACK); // 黒地にピンク色文字
if (boardsStruct[i].pre == 0) { // 0hPaなら
lcd.print(" -- "); // 表示
} else { // 0hPaでなければ
lcd.printf("%4d", boardsStruct[i].pre); // 気圧を表示
}
boardsStruct[i].pre = 0; // 表示後、データクリア
if (i == 2) { // もし最終号機だったら
lcd.setTextSize(0.86); // txt倍率
lcd.setCursor(xx1 + dxx1 * i + 85, yy1 + dyy1 * 3 + 5); // 表示位置(x,y)
lcd.print("hPa"); // 単位表示
lcd.setTextSize(1); // txt倍率を戻す
}
lcd.drawRoundRect(xx1 + dxx1 * i - 3, yy1 - 3, dxx1 - 14, dyy1 * 4, 10, WHITE); // 角丸の矩形罫線11
}
delay(10000); // 10秒待つ
}
* flash memory(2.0Mbyte)のうち、スケッチが65%使用。RAM(327kbyte)のうち、global変数が12%使用、local変数で287kbyte使用可能。(1000byte=1kbyteで計算)ATOM側
受信側のMACアドレスは各自のに修正してください。
// 送信側 ATOM (id=3 or 4) Basic 2号機へ温湿度・気圧送信
#include <M5Atom.h> // ATOM Lightを使用
#include <esp_now.h> // ESP-NOW通信を使用
#include <WiFi.h> // wifiを使用
#include <Adafruit_BMP280.h> // 気圧センサ
#include <Adafruit_SHT31.h> // 温湿度センサ
uint8_t broadcastAddress[] = { 0x8C, 0xAA, 0xB5, 0x82, 0x26, 0x5C }; // 相手(Basic)のMACアドレス
Adafruit_SHT31 sht = Adafruit_SHT31(&Wire); // 定義
Adafruit_BMP280 bme = Adafruit_BMP280(&Wire); // 定義
typedef struct struct_message { // 送信データ構造体
int id; // myData.idは送信機No. (3-4)の2台
int tmp; // myData.tmpは10倍した温度(x0.1℃)
int hum; // myData.humは湿度(%)
int pre; // myData.preは気圧(hPa)
} 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) { // コールバック関数
if (status == ESP_NOW_SEND_SUCCESS) { // 送信成功なら
Serial.print("パケット送信:成功 "); // 表示
} else { // さもなければ
Serial.print("パケット送信:失敗 "); // 表示
}
}
void setup() {
M5.begin(true, false, true); // 初期化(Serial,I2C?,Display)
Wire.begin(26, 32); // I2CでG26とG32を使用
while (!bme.begin(0x76)) { // BMP280(スレーブアドレス)を初期化できなければ
Serial.println("BMP280が未接続"); // 表示
}
while (!sht.begin(0x44)) { // SHT30を初期化できなければ
Serial.println("SHT30が未接続"); // 表示
}
M5.dis.setBrightness(100); // LEDの明るさ
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() {
myData.id = 4; // ATOM 3号機(id=3) or 4号機(id=4)
myData.tmp = (int)(sht.readTemperature() * 10.0 + 0.5); // センサー温度を読取る
myData.hum = (int)(sht.readHumidity() + 0.5); // センサー湿度を読取る
myData.pre = (int)(bme.readPressure() / 100.0 + 0.5); // センサー気圧を読取る
Serial.printf("id=%1d %3.1f℃ %3d%% %4dhPa ", myData.id, (float)(myData.tmp / 10.0), myData.hum, myData.pre);
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秒待つ
//delay(59990); // 計1分待つ
}
* flash memory(1.3Mbyte)のうち、スケッチが55%使用。RAM(327kbyte)のうち、global変数が11%使用、local変数で288kbyte使用可能。(1000byte=1kbyteで計算)