Makato-kan が 2021年07月09日23時43分09秒 に編集
コメント無し
本文の変更
## はじめに: 前回作成した [お礼 いただいたM5StickCを使って二酸化炭素濃度測定](https://elchika.com/article/77f5ef6a-063a-4ac3-bd5a-50bc7f2488c7/)にM5ボタンを押した時WiFi接続しMQTTパブリッシュ機能を使ってデータ送信するようにしました。 ## 2021-7-9編集: このプログラムでは通常WiFiを使用していないのです。 色々な記事を見ていたらWiFi.mode(WIFI_OFF);というものがありWiFiの消費電流を少なくする効果あるそうなのでそれを 起動時とWiFi通信実施後に組み込んでみました。 また、バッテリーの電圧値を取得できたのでそれも組み込んでみました さらにCPU速度を通常の半分に設定変更しました。 他に、状態表示の出力はコメントしてありますので、エラー解析必要になったらコメント外して使います。 ## MQTT: 詳しいことはMQTTWikiにお任せして,MQTTとはデータを送信する事。そのデータを受信する事を決めたものらしいです。 そこで,ラズベリーパイをMQTTブローカーにして無線LAN親機経由でデータを受信しています。 M5StickCはラズベリーパイのMQTTブローカーにデータを送信して役目が終了です。 データ受信はラズベリーパイでMQTTサブスクライバーを起動して受信の都度データをファイルに追記するようにしています。 ちなみに受信時に時刻情報を追加しているのでM5StickCに時刻情報なくても大丈夫です。 作ったデータはカンマ区切りデータなので エクセルでグラフ化してもよいしグーグルチャートでグラフ化もできました。 恐れ入りますがMQTTのセットアップについては後日記載したいと思います。 試しにラズベリーパイを無線LAN親機にしてDHCPのサービスをすれば無線LAN親機なくてもデータ受信できましたがラズベリーパイのアンテナが貧弱なのか距離を伸ばす事できなかったのでデータ受信は無線LAN親機経由でやらせています。 ## ハードウェア: M5StickCにSGP30とDHT11を接続しています。接続は前の記事をご参考ください。 ## プログラム: 通常setup()に含まれるWiFi接続してIPアドレス取得する所はloop()の中で実施しています。 これはWiFi親機のない場所で起動した場合IPアドレス取得エラーになってしまい先に進まなくなるのでボタンを押したらIPアドレス取得してデータ送信するようにしています。 よってWiFi親機の範囲外でボタン押した時にはLED点灯したままになります。 またシリアル出力にはデータ以外のものは送信しないようにしたかったのでトラブル発生時はコメント外して,シリアル出力したものをみると良いです。 ```arduino:M5StickC+SGP30+DHT11接続し、画面表示とシリアル出力とMQTT送信する #include <M5StickC.h> #include <WiFi.h> #include <PubSubClient.h> #include <Wire.h> #include "Adafruit_SGP30.h" #include "DHT.h" #define DHTPIN 26 #define DHTTYPE DHT11 // DHT11 double vbat = 0.0; DHT dht(DHTPIN, DHTTYPE); #define DISP_BRIGHTNESS_MIN 9 //LCD画面の輝度は8以下にするとほぼ見えない uint8_t disp_brightness = DISP_BRIGHTNESS_MIN; Adafruit_SGP30 sgp; uint32_t getAbsoluteHumidity(float temperature, float humidity) { //approximation formula from Sensirion SGP30 Driver Integration chapter 3.15 const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3] const uint32_t absoluteHumidityScaled = static_cast<uint32_t>(1000.0f * absoluteHumidity); // [mg/m^3] return absoluteHumidityScaled; } #define inPin 37 //M5ボタンでMQTT送信する //#define GPIO10 MQTT送信時LEDを点灯する // WiFi 設定 const char wifi_ssid[] = "yourssid"; // yourssid→使うWiFiのSSIDに書き換える const char wifi_password[] = "yourwifissid"; // yourwifissid→使うWiFiのパスワードに書き換える //MQTT設定
const char mqtt_server[] = "192.168.10.106";
const char mqtt_server[] = "192.168.10.xxx"; //MQTTブローカーを作動しているPCのアドレス
const int mqtt_port = 1883; const char mqttUserName[] = "name"; // name→設定した名前に書き換える const char mqttPass[] = "pass"; // pass→設定されたmqttパスに書き換える const char clientID[] = "M5StickC-1"; #define dht11_topic "sensor/dht11-12" WiFiClient espClient; PubSubClient mqttClient(espClient); void setup() { WiFi.disconnect(); delay(1); WiFi.mode(WIFI_OFF); // WiFi.forceSleepBegin(); delay(1); pinMode(inPin, INPUT); pinMode(GPIO_NUM_10, OUTPUT); delay(10); digitalWrite(GPIO_NUM_10, HIGH); M5.Axp.ScreenBreath(disp_brightness); M5.begin(); M5.Lcd.setRotation(3); M5.Lcd.setTextFont(2); M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK); M5.Lcd.fillScreen(BLACK); while (!Serial) { delay(10); } // Wait for serial console to open! Serial.println("SGP30 test"); Wire.begin(32, 33); //I2C SGP30 dht.begin(); if (! sgp.begin()){ Serial.println("Sensor not found :("); while (1); } Serial.print("Found SGP30 serial #"); Serial.print(sgp.serialnumber[0], HEX); Serial.print(sgp.serialnumber[1], HEX); Serial.println(sgp.serialnumber[2], HEX); M5.Lcd.fillScreen(WHITE); delay(500); M5.Lcd.fillScreen(BLACK); delay(500); //CPU速度の設定 80以下にするとDHT11センサーデータ取得失敗するかもnanになるかも setCpuFrequencyMhz(160); } void setup_wifi() { delay(10); // Serial.print("Connecting to "); // Serial.println(wifi_ssid); WiFi.begin(wifi_ssid, wifi_password); delay(100); while (WiFi.status() != WL_CONNECTED) { delay(500); // Serial.println("Connecting to WiFi.."); } // Serial.println(WiFi.localIP()); } void mqtt_reConnect() { while (!mqttClient.connected()) { // Serial.println("Attempting MQTT connection...");トラブルあったらコメント外す // MQTT broker に接続する if (mqttClient.connect(clientID,mqttUserName,mqttPass)) { // Serial.print("Connected with Client ID: "); トラブルあったらコメント外す // Serial.print(clientID); // Serial.print(", Username: "); // Serial.print(mqttUserName); // Serial.print(" , Passwword: "); // Serial.println(mqttPass); } else { Serial.print("failed, rc= "); // http://pubsubclient.knolleary.net/api.html#state に state 一覧が書いてある Serial.print(mqttClient.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } int counter = 0; //シリアル送信用 int counter1 = 0; //ベースライン値送信用 int counter2 = 0; //画面表示用 void loop() { //本当はこれで良いのかわからないけどバッテリーの電圧測定をしてみる。 float vbat = M5.Axp.GetVbatData() * 1.1 / 1000; float h = dht.readHumidity(); // Read temperature as Celsius (false) float t = dht.readTemperature(); String datath = ""+String(t)+" "+String(h); sgp.setHumidity(getAbsoluteHumidity(t, h)); if (! sgp.IAQmeasure()) { Serial.println("Measurement failed"); return; } //シリアル送信は10秒に一回にした counter++; if (counter == 30) { counter = 0; Serial.print(datath); Serial.print(" ");Serial.print(sgp.TVOC); Serial.print(" "); Serial.print(sgp.eCO2); Serial.print(" "); Serial.println(vbat); } if (! sgp.IAQmeasureRaw()) { Serial.println("Raw Measurement failed"); return; } delay(50); //トラブル解析用にベースラインの値をシリアル出力できるようにしてあるが使っていない counter1++; if (counter1 == 60) { counter1 = 0; uint16_t TVOC_base, eCO2_base; if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) { Serial.println("Failed to get baseline readings"); return; } // Serial.print("****Baseline values: eCO2: 0x"); Serial.print(eCO2_base, HEX); トラブルあったらコメント外す // Serial.print(" & TVOC: 0x"); Serial.println(TVOC_base, HEX); } //M5ボタンを押したらWifi接続してMQTTデータ転送する // デジタルピン#37が'LOW'ボタンを押された時 //Serial.println(digitalRead(inPin)); if (digitalRead(inPin) == LOW) { digitalWrite(GPIO_NUM_10, LOW); setup_wifi(); mqttClient.setServer(mqtt_server, mqtt_port); delay(20); if (!mqttClient.connected()) { mqtt_reConnect(); } delay(10); if(!mqttClient.loop()) { delay(10); mqttClient.connect(clientID); } String datathTC = ""+String(t)+","+String(h)+","+String(sgp.TVOC)+","+String(sgp.eCO2)+","+String(vbat); delay(10); mqttClient.publish(dht11_topic, datathTC.c_str(), false); delay(10); digitalWrite(GPIO_NUM_10, HIGH); WiFi.disconnect(); delay(1); WiFi.mode(WIFI_OFF); delay(1); } //M5stick OLED に TVOC eCO2 温度 湿度を表示する 表示画面は少し暗くしている //データの更新は2.5秒位に一回にするためカウンタを使った counter2++; if (counter2 == 8) { counter2 = 0; M5.Axp.ScreenBreath(disp_brightness); M5.Lcd.fillScreen(BLACK); String str = " " + (String)sgp.TVOC + " [ppb] "; M5.Lcd.drawRightString(str,250,0,2); M5.Lcd.setCursor(0, 0, 2); M5.Lcd.println(" TVOC"); str = " " + (String)sgp.eCO2 + " [ppm] "; M5.Lcd.drawRightString(str,250,16,2); M5.Lcd.setCursor(0, 16, 2); M5.Lcd.println(" eCO2"); M5.Lcd.setCursor(0, 32, 2); M5.Lcd.println(" Temp"); str = " " + (String)t + " [C] "; M5.Lcd.drawRightString(str,250,16+16,2); M5.Lcd.setCursor(0, 32+16, 2); M5.Lcd.println(" Humi"); str = " " + (String)h + " [%] "; M5.Lcd.drawRightString(str,250,16+16+16,2); M5.Lcd.setCursor(0, 32+16+16, 2); M5.Lcd.println(" Volt"); str = " " + (String)vbat + " [V] "; M5.Lcd.drawRightString(str,250,16+16+16+16,2); } delay(300); } ``` ## 使い方: 起動すると画面に温度・湿度・eCO2・TVOCを表示します。 M5ボタンを押すとLEDが点灯してIPアドレス取得とデータ送信を行い、成功すればLED消灯します。 ## シリアルコンソール出力例: M5StickC initializing...OK SGP30 test Found SGP30 serial #01668xxx 25.80 76.00 7 400 4.16 25.90 76.00 11 400 4.16 26.00 76.00 6 400 4.16 25.80 75.00 5 400 4.16 26.00 76.00 2 400 4.16 ※ArduinoIDEのシリアルプロットを使えばグラフにしてくれます。 ※Gnuplotを使ってもグラフになります。 ※エクセルでもグラフにできます。 ## MQTTデータ受信コマンドと受信例: ラズベリーパイは設定が終えて,MQTTサブスクライバーコマンドでデータを受信する場合のコマンド例 mosquitto_sub -h 192.168.10.yyy -u USERNAME -p PASSWORD -t sensor/dht11-12 -F '@Y,@m,@d,"@H:@M",%p' 以下が受信例です。 ※時刻に””がついているのはグーグルチャートでグラフ化する時:のせいでエラーになったのでつけています。 2021,07,09,"07:03",24.20,83.00,50,500,3.83 2021,07,09,"19:30",24.10,79.00,222,593,3.88 2021,07,09,"22:59",25.50,77.00,22,426,4.16 2021,07,09,"23:01",25.30,77.00,9,412,4.16 ## ラズベリーパイにMQTTをインストールする: ラズベリーパイのコマンドラインで以下のコマンドを実行して インストールします。 ログイン名パスワード設定 などが必要ですがすっかり忘れているので思い出したら編集します。 $ sudo apt install mosquitto $ sudo apt install mosquitto-clients ## 最後に: DHT11はVCCとデータ線の間に10kΩ抵抗を入れて,VCCは3.3Vを使っています データ線は26番のピンに接続しております。