環境データを収集する①~ESP-NOWでセンサ情報を収集する~
こういうシステムを作って、自宅の環境情報を簡単に収集・保存・閲覧できるようにしたいと思います。
まずは、複数のマイコンモジュールで別々に取得したセンサ情報を、ワイヤレスで1台のマイコンモジュールが収集する、システムの基本構成を作成します。
ESP-NOW
ESP32マイコンを搭載しているM5Stackシリーズでは、ESP-NOWという通信プロトコルが使用可能です。ESP-NOWを用いることで、アクセスポイントなしに高速にデータを送受信できます。
今回は、M5StackシリーズをESP-NOWで接続してセンサ情報を収集することにします。
データ送信時の注意
ESP-NOWでのデータ送信はuint8_t型の変数を格納したリストしか送ることができません。つまり、データは符号なし8ビットで表現できる0~255までの値しか一度に送信できません。
0~255の整数以外の値を送信する場合は、少し工夫が必要です。
スケッチ例
mametarou963さんの記事を参考にさせて頂きました。
https://elchika.com/article/748b00ae-c320-406c-9bc6-8566090b882e/
センサ側
以下のスケッチ例は、ATOM Liteに温湿度気圧センサユニットとTVOC/eCO2 ガスセンサユニットを接続した場合です。
////////////////////////////////////////////////////////////////////////////////
// ライブラリ読み込み
////////////////////////////////////////////////////////////////////////////////
#include <EEPROM.h>
#include <esp_now.h>
#include <WiFi.h>
#include <M5Atom.h>
#include <M5_ENV.h>
#include <Adafruit_SGP30.h>
////////////////////////////////////////////////////////////////////////////////
// グローバル変数、定数
////////////////////////////////////////////////////////////////////////////////
// peer設定
esp_now_peer_info_t slave;
// EEPROM
#define EEPROM_SIZE 64
#define DEVICE_NUMBER_ADDRESS 1
// デバイス番号
int deviceNumber = 1;
// ENV III
SHT3X sht30;
QMP6988 qmp6988;
// SGP30
Adafruit_SGP30 sgp;
// 温度、湿度、気圧
float tmp = 0.0;
float hum = 0.0;
float pressure = 0.0;
// ループカウント
uint16_t count = 0;
////////////////////////////////////////////////////////////////////////////////
// LEDの色
////////////////////////////////////////////////////////////////////////////////
CRGB dispColor(uint8_t g, uint8_t r, uint8_t b) {
return (CRGB)((g << 16) | (r << 8) | b);
}
////////////////////////////////////////////////////////////////////////////////
// デバイス番号を読み込む
////////////////////////////////////////////////////////////////////////////////
void readDeviceNumber() {
deviceNumber = EEPROM.readInt(DEVICE_NUMBER_ADDRESS);
if ((deviceNumber < DEVICE_NUMBER_MIN) || (deviceNumber > DEVICE_NUMBER_MAX)) {
deviceNumber = DEVICE_NUMBER_MIN;
}
}
////////////////////////////////////////////////////////////////////////////////
// デバイス番号をカウントアップする
////////////////////////////////////////////////////////////////////////////////
void countUpDeviceNumber() {
deviceNumber = deviceNumber + 1;
if(deviceNumber > DEVICE_NUMBER_MAX) {
deviceNumber = DEVICE_NUMBER_MIN;
}
EEPROM.writeInt(DEVICE_NUMBER_ADDRESS, deviceNumber);
EEPROM.commit();
}
////////////////////////////////////////////////////////////////////////////////
// ESP-NOW送信コールバック
////////////////////////////////////////////////////////////////////////////////
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 setup() {
// シリアルの初期設定
Serial.begin(115200);
// 動作周波数 240MHz → 80MHz
setCpuFrequencyMhz(80);
// EEPROMの初期化
if (!EEPROM.begin(EEPROM_SIZE)) {
Serial.println("Failed to initialise EEPROM");
Serial.println("Restarting...");
delay(1000);
ESP.restart();
}
// m5stickcの初期設定
M5.begin(false, true, true); // UART:false, I2C:true, 本体LED:true
Wire.begin(26, 32);
// deviceNumberの読み出し
readDeviceNumber();
// SGP30の初期設定
while (! sgp.begin()) {
Serial.println("Could not find a valid SGP30 sensor, check wiring!");
}
// ESP-NOW初期化
WiFi.mode(WIFI_STA);
WiFi.disconnect();
if (esp_now_init() == ESP_OK) {
Serial.println("ESPNow Init Success");
} else {
Serial.println("ESPNow Init Failed");
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);
// QMP6988初期化
qmp6988.init();
}
////////////////////////////////////////////////////////////////////////////////
// メインループ
////////////////////////////////////////////////////////////////////////////////
void loop() {
M5.update();
// 温湿度の取得
if (sht30.get() == 0) { // Obtain the data of shT30.
tmp = sht30.cTemp; // Store the temperature obtained from shT30.
hum = sht30.humidity; // Store the humidity obtained from the SHT30.
} else {
tmp = 0, hum = 0;
}
// 圧力の取得
pressure = qmp6988.calcPressure() * 0.01;
// TVOC、eCO2の取得
sgp.IAQmeasure();
// 表示の更新
uint8_t tmpUpper = (uint8_t)tmp;
uint8_t tmpLower = (uint8_t)((tmp - (float)tmpUpper) * 100);
uint8_t humUpper = (uint8_t)hum;
uint8_t humLower = (uint8_t)((hum - (float)humUpper) * 100);
uint8_t pressureUpper1 = (uint8_t)(((int)pressure) / 100);
uint8_t pressureUpper2 = (uint8_t)(((int)pressure) % 100);
uint8_t pressureLower = (uint8_t)((pressure - (int)pressure) * 100);
uint8_t tvocUpper = (uint8_t)(sgp.TVOC / 100);
uint8_t tvocLower = (uint8_t)(sgp.TVOC % 100);
uint8_t eco2Upper = (uint8_t)(sgp.eCO2 / 100);
uint8_t eco2Lower = (uint8_t)(sgp.eCO2 % 100);
uint8_t data[30] = {
deviceNumber,
tmpUpper,
tmpLower,
humUpper,
humLower,
pressureUpper1,
pressureUpper2,
pressureLower,
tvocUpper,
tvocLower,
eco2Upper,
eco2Lower,
};
esp_err_t result = esp_now_send(slave.peer_addr, data, sizeof(data));
// ボタン操作
if (M5.Btn.wasPressed()) {
Serial.println("Button pressed");
countUpDeviceNumber();
}
// 通常時のLED
if (deviceNumber == 1) M5.dis.drawpix(0, 0x0f0000); // RED
if (deviceNumber == 2) M5.dis.drawpix(0, 0x0f0f00); // YELLOW
if (deviceNumber == 3) M5.dis.drawpix(0, 0x000f00); // GREEN
if (deviceNumber == 4) M5.dis.drawpix(0, 0x000f0f); // CYAN
// 5秒稼働、55秒ディープスリープ
count++;
if (count > 5) {
esp_sleep_enable_timer_wakeup(55000000);
esp_deep_sleep_start();
count = 0;
}
delay(1000);
}
ATOM LiteのLEDボタンを押すことで、デバイス番号(1~4)を切り替えることができます。
受信側
デバイス番号1~4のセンサデータをM5Stack Core2のLCDに表示します。
////////////////////////////////////////////////////////////////////////////////
// ライブラリ読み込み
////////////////////////////////////////////////////////////////////////////////
#include <M5Core2.h>
#include <esp_now.h>
#include <WiFi.h>
////////////////////////////////////////////////////////////////////////////////
// グローバル変数、定数
////////////////////////////////////////////////////////////////////////////////
// peer設定
esp_now_peer_info_t slave;
// デバイス番号
#define DEVICE_NUMBER_MIN 1
#define DEVICE_NUMBER_MAX 4
struct deviceInfo {
double tmp;
double humi;
double pressure;
int tvoc;
int eco2;
};
struct deviceInfo devInfo[DEVICE_NUMBER_MAX] = {0};
////////////////////////////////////////////////////////////////////////////////
// esp-now 受信コールバック
////////////////////////////////////////////////////////////////////////////////
void OnDataRecv(const uint8_t *mac_addr, const uint8_t *data, int data_len) {
char macStr[28];
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\n", macStr);
int deviceNumber = data[0];
double tempValue = (double)data[1] + ((double)data[2] * 0.01);
double humiValue = (double)data[3] + ((double)data[4] * 0.01);
double pressureValue = ((double)data[5] * 100) + (double)data[6] + ((double)data[7] * 0.01);
int tvocValue = (data[8] * 100) + data[9];
int eco2Value = (data[10] * 100) + data[11];
// 画面表示用/データ送信用のデータ更新
if((deviceNumber >= DEVICE_NUMBER_MIN) && (deviceNumber <= DEVICE_NUMBER_MAX)) {
devInfo[deviceNumber-1].tmp = tempValue;
devInfo[deviceNumber-1].humi = humiValue;
devInfo[deviceNumber-1].pressure = pressureValue;
devInfo[deviceNumber-1].tvoc = tvocValue;
devInfo[deviceNumber-1].eco2 = eco2Value;
}
}
////////////////////////////////////////////////////////////////////////////////
// 初期設定
////////////////////////////////////////////////////////////////////////////////
void setup() {
// serial
Serial.begin(115200);
// 動作周波数 240MHz → 80MHz
setCpuFrequencyMhz(80);
// M5Stack
M5.begin();
M5.Lcd.setTextSize(2); // テキストサイズ指定
M5.Lcd.setCursor(0, 0); // カーソル位置を設定
// 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_recv_cb(OnDataRecv);
}
////////////////////////////////////////////////////////////////////////////////
// メインループ
////////////////////////////////////////////////////////////////////////////////
void loop() {
M5.update();
M5.Lcd.setCursor(0, 0);
M5.Lcd.setTextColor(WHITE, BLACK);
int i = 0;
int error_flag = 0;
for (i = 0; i < DEVICE_NUMBER_MAX; i=i+2) {
M5.Lcd.setTextColor(WHITE, BLACK);
M5.Lcd.printf("DvNo| %d | %d\n", i+1, i+2);
M5.Lcd.printf("----+----------+----------\n");
// 温度
M5.Lcd.printf("Temp| ");
M5.Lcd.printf("%4.1f C", devInfo[i].tmp);
M5.Lcd.printf(" | ");
M5.Lcd.printf("%4.1f C\n", devInfo[i+1].tmp);
// 湿度
M5.Lcd.printf("Humi| ");
M5.Lcd.printf("%4.1f %%", devInfo[i].humi);
M5.Lcd.printf(" | ");
M5.Lcd.printf("%4.1f %%\n", devInfo[i+1].humi);
// 圧力
M5.Lcd.printf("Pres|");
M5.Lcd.printf("%6.1f hPa", devInfo[i].pressure);
M5.Lcd.printf("|");
M5.Lcd.printf("%6.1f hPa\n", devInfo[i+1].pressure);
// TVOC
M5.Lcd.printf("TVOC| ");
M5.Lcd.printf("%5d ppb", devInfo[i].tvoc);
M5.Lcd.printf("| ");
M5.Lcd.printf("%5d ppb\n", devInfo[i+1].tvoc);
// eCO2
M5.Lcd.printf("eCO2| ");
M5.Lcd.printf("%5d ppm", devInfo[i].eco2);
M5.Lcd.printf("| ");
M5.Lcd.printf("%5d ppm\n", devInfo[i+1].eco2);
//
M5.Lcd.printf("\n");
}
delay(1000);
}
受信結果
以下は、デバイス番号1と2にそれぞれ設定したATOM Liteから、温湿度気圧センサユニットとTVOC/eCO2 ガスセンサユニットのデータを取得したときの、受信側のM5Stack Core2のLCDに表示された内容です。
これでワイヤレスでセンサ情報を収集できるようになりました。
この記事が気に入ったらサポートをしてみませんか?