お盆休みの電子工作
お盆休みがあり、じっくり電子工作に没頭しました。
M5Stack Core2を以前に買ったままで休眠状態だったので、起こしてやろう思い、M5Stack Core2とM5Stack用温湿度気圧センサユニット Ver.3(ENV Ⅲ)で、温湿度気圧を測定。
次に、そのデータをMQTTでパソコンに送信。受信は、node-redを利用しましました。 ネットの記事を参照しながら、作成しました。
概要
M5Stack Core2の設定
画面には、温湿度気圧、取得時間を表示
バッテリーの残量を表示したけど、おかしい。調べてみようと思ってます。
SDカードにデータを保存可能
PubSubClientを利用して、Node-Redにデータを送信
コード
#include <M5Core2.h>
#include "M5_ENV.h"
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <WiFi.h>
#include <time.h>
//ファイル保存するかどうか。ファイルに保存するならここを true にする
#define FILEWRITE false
// 保存するファイル名
const char *fname = "/env_log.csv";
// ループのウェイト、何秒待つかをミリ秒で指定
const long DELAY = 10000; // ミリ秒
// Wi-FiのSSID
char *ssid = "******";
// Wi-Fiのパスワード
char *password = "******";
// MQTTの接続先(ラズパイ)のIP
const char* endpoint = "***.***.***.***";
// MQTTのポート
const int port = 1883;
// デバイスID
char* deviceID = "M5StackCore2";
// メッセージを知らせるトピック
char* pubTopic = "/pub/M5StackCore2";
// メッセージを待つトピック
char* subTopic = "/sub/M5StackCore2";
//ENV3
SHT3X sht30;
QMP6988 qmp6988;
//温度・湿度・圧力
float tmp = 0.0;
float hum = 0.0;
float pressure = 0.0;
// === 関数プロトタイプ宣言
uint8_t getBattery(uint16_t, uint16_t);
// Time
char ntpServer[] = "ntp.jst.mfeed.ad.jp"; // ntpサーバ
const long gmtOffset_sec = 9 * 3600;
const int daylightOffset_sec = 0;
struct tm timeinfo;
String dateStr;
String timeStr;
void getTimeFromNTP(){
// NTPサーバと時刻を同期
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
while (!getLocalTime(&timeinfo)) {
delay(1000);
}
}
File file;
// ================================================================== //
// バッテリ残量取得
// 戻り値 : uint8_t 0 ~ 100(%)
// ================================================================== //
uint8_t getBattery() {
uint8_t vat = 0xFF;
Wire.beginTransmission(0x75);
Wire.write(0x78); // 0x78 バッテリ残量取得レジスタアドレスオフセット
Wire.endTransmission(false);
if (Wire.requestFrom(0x75, 1)) {
vat = Wire.read() & 0xF0; // 下位4ビット 0 マスク
if (vat == 0xF0) vat = 0;
else if (vat == 0xE0) vat = 25;
else if (vat == 0xC0) vat = 50;
else if (vat == 0x80) vat = 75;
else if (vat == 0x00) vat = 100;
else vat = 0xFF;
} else vat = 0xFF;
return vat;
}
void writeData(char *paramStr) {
// SDカードへの書き込み処理(ファイル追加モード)
// SD.beginはM5.begin内で処理されているので不要
file = SD.open(fname, FILE_APPEND);
file.println(dateStr + "," + timeStr + "," + paramStr);
file.close();
}
////////////////////////////////////////////////////////////////////////////////
WiFiClient httpsClient;
PubSubClient mqttClient(httpsClient);
// シリアル通信ブロックの初期化
void setup_serial(){
Serial.begin(9600);
while (!Serial) continue;
}
// Wi-Fiブロックの初期化と開始
void setup_wifi(){
Serial.println("Connecting to ");
Serial.print(ssid);
// WiFi接続性改善のため、いったん切断
WiFi.disconnect( true, true ); //WiFi OFF, eraseAP=true
delay(500);
// WiFi開始
WiFi.begin(ssid, password);
// Wi-Fi接続待ち
while (WiFi.status() != WL_CONNECTED) {
delay(100);
M5.Lcd.print(".");
}
// WiFi接続成功メッセージの表示
Serial.println("\nWiFi Connected.");
M5.Lcd.setCursor(10, 40);
M5.Lcd.setTextSize(2);
M5.Lcd.println("WiFi Connected.");
// M5StackのIPアドレスを表示
M5.Lcd.print("IP address: ");
M5.Lcd.println(WiFi.localIP());
}
void getTime(){
// 時刻の取得と表示
getLocalTime(&timeinfo);
dateStr = (String)(timeinfo.tm_year + 1900)
+ "/" + (String)(timeinfo.tm_mon + 1)
+ "/" + (String)timeinfo.tm_mday;
timeStr = (String)timeinfo.tm_hour
+ ":" + (String)timeinfo.tm_min
+ ":" + (String)timeinfo.tm_sec;
//M5.Lcd.setTextColor(WHITE,BLACK);
//M5.Lcd.setCursor(0, 200, 1);
M5.Lcd.println(dateStr + " ");
M5.Lcd.println(timeStr + " ");
}
//MQTT接続処理
void connectMQTT(){
// MQTT接続待ち
while (!mqttClient.connected()) {
if (mqttClient.connect(deviceID)) {
// 所定のデバイスと接続した
Serial.println("Connected.");
int qos = 0;
mqttClient.subscribe(subTopic, qos);
Serial.println("Subscribed.");
} else {
// 所定のデバイスと接続失敗 ⇒ 再トライする
Serial.print("Failed. Error state=");
Serial.print(mqttClient.state());
// Wait 5 seconds before retrying
delay(5000);
}
}
}
// MQTT接続管理
void mqttLoop(){
// MQTT接続状態を監視し、切断されていたら、再接続を試みる
if (!mqttClient.connected()) {
connectMQTT();
}
// MQTTクライアント側の定期処理の実行
mqttClient.loop();
}
//センサーデータ取得
void readData(){
//温度・湿度
if (sht30.get()==0){
// 温度の取得
tmp = sht30.cTemp;
// 湿度の取得
hum = sht30.humidity;
}else{
tmp=0;
hum=0;
}
// 気圧の取得[hPa = Pa * 0.01]
pressure = qmp6988.calcPressure() * 0.01;
}
// M5Stackの初期化
void setup() {
// M5Stack objectの初期化
M5.begin();
// M5.Power.begin();
// M5.IMU.Init();
setup_serial();
// 画面初期設定
M5.Lcd.fillScreen(TFT_NAVY);
M5.Lcd.setCursor(10, 10);
M5.Lcd.setTextSize(2);
M5.Lcd.printf("START");
// Wi-Fi処理の開始
setup_wifi();
// MQTTクライアントの初期化
mqttClient.setServer(endpoint, port);
// MQTT接続開始
connectMQTT();
qmp6988.init();//気圧初期化
// timeSet
getTimeFromNTP();
}
// M5Stackの定期処理
void loop() {
StaticJsonDocument<500> doc;
char pubMessage[256];
uint8_t batt = getBattery();
//センサーデータ読込
readData();
// 温度、湿度、気圧をシリアル通信で送信
Serial.printf("Temperatura: %2.2f*C Humedad: %0.2f%% Pressure: %0.2fPa\r\n", tmp, hum, pressure);
//LCD表示クリア&色設定
M5.Lcd.setCursor(0, 0); // カーソル
M5.Lcd.setTextColor(WHITE, BLACK); // 色
M5.Lcd.setTextSize(4); // 文字サイズ
// 時刻表示
getTime();
M5.Lcd.printf("--------\n");
// 温度、湿度、気圧、バッテリー情報
M5.Lcd.printf("Temp:%2.1f \nHumi:%2.0f%% \nPres:%2.0fhPa \nBatt: %d%%", tmp, hum, pressure,batt);
// MQTT接続監視
mqttLoop();
// メッセージの作成
sprintf(pubMessage, "{\"tmp\": %5.1f,\"hum\": %5.1f,\"pressure\": %5.1f}",tmp, hum, pressure);
// JSONメッセージの作成
JsonArray idValues = doc.createNestedArray("ID");
idValues.add(deviceID);
JsonArray tmpValues = doc.createNestedArray("tmp");
tmpValues.add(tmp);
JsonArray humValues = doc.createNestedArray("hum");
humValues.add(hum);
JsonArray pressureValues = doc.createNestedArray("pressure");
pressureValues.add(pressure);
serializeJson(doc, pubMessage);
// メッセージの画面表示
Serial.print("topic= ");
Serial.println(pubTopic);
Serial.print("Message= ");
Serial.println(pubMessage);
M5.Lcd.clear();
M5.Lcd.setCursor(0, 0); // カーソル
M5.Lcd.setTextColor(GREENYELLOW, BLACK); // 色
M5.Lcd.setTextSize(3); // 文字サイズ
// 温度、湿度、気圧、バッテリー情報
M5.Lcd.printf("Temp:%2.1f \nHumi:%2.0f%% \nPres:%2.0fhPa \nBatt: %d%%", tmp, hum, pressure,batt);
M5.Lcd.setCursor(0, 100); // カーソル
M5.Lcd.setTextColor(YELLOW, BLACK); // 色
M5.Lcd.setTextSize(2); // 文字サイズ
getTime();
M5.Lcd.printf("");
M5.Lcd.setTextColor(WHITE, BLACK); // 色
M5.Lcd.printf(pubMessage);
// メッセージのPublish
mqttClient.publish(pubTopic, pubMessage);
Serial.println("Published.");
Serial.println("");
#if FILEWRITE
// ファイル出力
char buff[128];
sprintf(buff,"%2.1f ,%2.0f% ,%2.0f, %d%", tmp, hum, pressure,batt);
writeData(buff);
#endif
// ボタンイベント処理
//Aボタンを押したときに明るくするテスト。今はDELAYの切り替わりのタイミングでしか動作しない
if (M5.BtnA.wasPressed()) {
M5.Lcd.setBrightness(100);
} else {
M5.Lcd.setBrightness(10);
}
M5.update();
delay(DELAY);
}
Node-Red設定
Node Redのフロー図
ブロガーとMQTT設定
データからダッシュボード作成
データのデータベース登録
5M5Core2側で指定した、ポート・トピックを設定
Node-Redでデータ受信
JSON形式で送信されてくる。データをデバッグノードで確認する。
JSON形式のデータからchangeノードを利用し、温度、湿度、気圧データに分解
chartノードに出力する。
SQLite3へデータを登録
SQLite3にテーブル準備
データベースにに保存先.DB名を入力する。
Injectノードのmsg.topicにSQL文を設定
--msg.topicにSQL(登録)
CREATE TABLE readings(
id INTEGER PRIMARY KEY AUTOINCREMENT, datetime timestamp,
temperature NUMERIC, humidity NUMERIC, pressure NUMERIC)
--msg.topicにSQL(選択)
SELECT * FROM readings order by datetime desc
--msg.topicにSQL(削除)DROP TABLE readings
まとめ(乾燥)
M5core2を利用し、温度・湿度・気圧データを利用し、データを可視化・保存するところまでを、自前行ってみた。少しずつ、ホームIOTを進めていきたいと思っていいます。
コロナで外に出られないので、じっくりできることができた。
データ取得・可視化は,Raspberry pi + BeebotteやAmbientを利用して可視化、データ保存は試したことがあった。
自前でデータ取得、保存、可視化までを試してみたかったので、とりあえず、目標は達成できた。
M5Stackのプログラム作成は、Visual Studio Code & PlatformIOで試してみました。
最近は、便利なツールがあり、電子工作もいろいろやりたくなります。
それと、Node-Redは、すごいですね。いろいろな連携が可能で、Rasberry pi やUbuntuで利用できるので、すごく便利です。
Windows 7で利用していたPCにUbuntuを入れて、サーバとして利用しています。
すこしずつ、ためしたら記事にして残したいと思います。