2020年12月10日木曜日

ド素人がM5StickCを用いたマルチ環境計を作ってみた Ver2.0

なんとかAmbientにデータを送って、グラフ化することには成功した(他に直さなきゃいけないところはいっぱいあるけど)のでVer2.0として記事化します。また、下には前回同様の注意文を。

※プログラミング素人が、教本すら読まずにネット上の知識のみで組み上げたものです。ガバガバでスパゲッティなコードだったり、変なところもあるかと思いますが温かい目で見てやってください。
※そのため説明が間違っている可能性が高いです。参考の一つになるかも怪しいのはあしからず。

~今回作ったもの~

M5StickCを使用したマルチ環境計(環境メーター?環境センサー?)
(測定・表示できるのは温度・湿度・気圧・CO2濃度です)

↑にAmbientのサービスを利用してデータを送信し、グラフ化しました。

使用したデバイス類

・M5StickC ・ENV Ⅱ HAT ・MH-Z19B ・Grove-メスピンヘッダケーブル
 前回と使用した部品に差異は無いので詳細な説明は省きます。
 あとは、長時間稼働させるためにモバイルバッテリーやUSB充電器、つなげるためのUSB-A to Cのケーブルは必要かな。

~Ambientとの接続、その前に~

 前回の最後に書いたコードをベースに、Ambientさんにデータを送信してグラフ化します。が、その前に私は一回もM5StickCをWiFiにつなげたことがないのでWiFiに接続するだけのコードできちんとうちのWiFiに接続できるかテストします。
 短い単純なコードなのでテストするだけならこれで十分です。ローカルIPアドレスも表示されるので、ルーター側からどれがどのデバイスだ、というのも特定可能になります。
下記コードの動いている様子

code:1 WiFi_TEST
#include <M5StickC.h>
#include <WiFi.h>

const char* ssid     = "YOUR SSID" // Define ssid SSIDを定義
const char* password = "YOUR PASSWORD"  // Define password パスワードを定義

WiFiServer server(80); //Connect to port80 at WiFiserver? サーバーのポート80に接続?

// Wifiに接続
void setup()
{
    M5.begin(); //Initialize M5StickC M5StickCを初期化
    delay(100); //Wait 100msecs 100ミリ秒待機
    Serial.begin(115200); //Communication begins at 115200bit/s? 115200bit/sでの通信開始?
    M5.Lcd.setRotation(3); //Set Lcd's Direction 画面を横向き(左上)に
    M5.Lcd.setCursor(0, 2, 2); //Set Cursor to (0,2) and Assign Fonts 2 カーソルを(0,2)へ、フォントは2を指定
    M5.Lcd.println("Connecting to"); //Print to Lcds in"" 画面に""内の文章を表示
    M5.Lcd.println(ssid); //Display the SSID on the screen 画面にSSIDを表示

    // wifi接続開始
    WiFi.begin(ssid, password); //Connect to WiFi with the specified SSID and password 指定したSSIDとパスワードでWiFiに接続

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        M5.Lcd.print("."); //Display "." every 500msecs while waiting for a WiFi connection WiFi接続待ちの間500ミリ秒ごとに"."を表示
    }

    //Display the Local IP once if the connection is complete 接続完了したらIP表示
    M5.Lcd.println("Successed!!");
    M5.Lcd.println("IP: ");
    M5.Lcd.println(WiFi.localIP());
  
    server.begin();

}


void loop(){

}
 

~Ambientとの接続、そして困難(でも普通に動くよ)~

 書いていたら思いの外開発ストーリーが長くなったので、手早く(と思ったら前説もまあまあ文章が長くなったけど)コードを先に表示します。
 まず今回のコードの特徴としては、下記参考元ページのコードと同様に、setup内で処理を完結させ、ディープスリープに移行して規定時間後に復帰して再度処理を行うという点があります。
 また、setup内に前回のloop内のコードをコピペしてあるということもあり、どこが起動関連でどこがデータ処理関連、Ambientへの送信関連なのかがわかるように改行してあります(あと元loopのコードの順番をデータ処理部とLCD表示部に分割した)。
 他には、下記参考元コードでディープスリープの処理はesp_deep_sleepで行われていましたが、M5StickC非公式日本語リファレンスさんを参考にウェイクアップタイマーの記述とディープスリープ開始の記述に分割しておきました。
 3000ミリ秒、つまり3秒間取得したデータが表示されている(と思ってたけどなんかディープスリープ中も表示されているっぽい??)ので、その場でどんな環境状態なのかを見ることができる時間を設けてあります。また、同じく3秒間WiFi接続成功後に接続したローカルIPアドレスを表示してあります。
接続確認表示
取得時データの表示(前回のと何ら変わってないけど)



code:2 MultiSensingSketchONLYAmbient
//Include Libraries ライブラリをインクルード
#include <M5StickC.h>
#include "SHT3X.h"
#include <Wire.h>
#include "Adafruit_Sensor.h"
#include <Adafruit_BMP280.h>
#include "MHZ19_uart.h"
#include "Ambient.h"

//Define Valiable 変数を定義
SHT3X sht3x;
Adafruit_BMP280 bme;
MHZ19_uart mhz19;

#define uS_TO_S_FACTOR 1000000 //マイクロ秒を秒に変換する定義?
#define TIME_TO_SLEEP 30 //30秒でESP32をスリープにする定義?

WiFiClient client;
Ambient ambient;

//Ssid and password of connecting WiFi 接続するWiFiのSSIDとパスワード
const char* ssid     = "YOUR SSID";
const char* password = "YOUR PASSWORD";

unsigned int channelID = YOUR ChannelID of Ambient; //ChannelID of Ambient AmbientのチャネルID
const char* writeKey = "YOUR WriteKey of Ambient"; //Writekey of Ambient Ambientのライトキー

//Calculate temperature, humidity and air pressure in floating point? 浮動小数点で温湿度・気圧を計算?
float tmp = 0.0;
float hum = 0.0;
float pressure = 0.0;

void setup() {
  // put your setup code here, to run once:
  M5.begin(); //Initialize M5StickC M5StickCを初期化
  delay(50); //Wait 50msecs 50ミリ秒待機

  mhz19.begin(32,33); //Initialize I2C of MHZ19 MHZ19のI2Cを初期化
  mhz19.setAutoCalibration(false); //AutoCalibration turn off 自動校正をしない
  delay(3000); //Wait 3000msecs 3000ミリ秒待機
  
  Wire.begin(0,26); //Initialize I2C of ENV2HAT ENV2HATのI2Cを初期化
  M5.Axp.ScreenBreath(10); //Decrease Lcd's Brightness 画面の輝度を下げる
  setCpuFrequencyMhz(160); //Set Cpu Frequency to 160MHz CPUの動作周波数を160MHzに
  M5.Lcd.fillScreen(BLACK); //Fill Screen Black 背景色を黒に
  M5.Lcd.setRotation(3); //Set Lcd's Direction 画面を横向き(左上)に
  M5.Lcd.setCursor(0, 2, 2); //Set Cursor to (0,2) and Assign Fonts 2 カーソルを(0,2)へ、フォントは2を指定
  M5.Lcd.println("Multi-Environmental-Sensor-System"); //Print to Lcds in"" 画面に””内の文章を表示
  
  if (!bme.begin(0x76)){  
      Serial.println("Could not find a valid BMP280 sensor, check wiring!");
      while (1);  //Error message if the BMP280 sensor was not detected? BMP280センサが検出されなかった場合のエラー表示?
  }

  WiFi.begin(ssid, password); //Starting connection of WiFi WiFi接続開始
  
  while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        M5.Lcd.print("."); //WiFi接続待ちの間500ミリ秒ごとに"."を表示
  }

  M5.Lcd.println("Successed!!");
  M5.Lcd.println("IP: "); //Print IPaddress IPアドレスを表示
  M5.Lcd.println(WiFi.localIP());

  ambient.begin(channelID, writeKey, &client); //Assign channelID and writekey and initialize ambient チャネルIDとライトキーを指定してAmbientの初期化
  
  delay(3000); //Wait 3000msecs 3000ミリ秒待機



  if(sht3x.get()==0){
    tmp = sht3x.cTemp;
    hum = sht3x.humidity; //Acquisition of temperature and humidity data from sht3x? sht3xからの温湿度データの取得?
  }

  float pressure = bme.readPressure(); //Calculating atmospheric pressure in floating point? 浮動小数点で大気圧を計算?
  int co2, temp; //Calculate CO2 concentration by integer? 整数でCO2濃度を計算?
  int i;
  unsigned int t; 
  co2 = mhz19.getPPM(); //Read datas from MH-Z19 MH-Z19からのデータ呼び出し?
  temp = mhz19.getTemperature();
  float vbat = M5.Axp.GetBatVoltage(); //Get the battery voltage of the M5StickC? M5StickCのバッテリー電圧を取得?

  M5.Lcd.fillScreen(BLACK); //Fill Screen Black(overwrite) 背景色を黒に上書き
  
  M5.Lcd.setCursor(0, 2, 1); //Set Cursor to (0,2) and Assign Fonts 1 カーソルを(0,2)へ、フォントは1を指定
  M5.Lcd.println("Multi-Environmental-Sensor-System"); //Print to Lcds in"" 画面に””内の文章を表示
  
  M5.Lcd.setCursor(0, 22, 2); //Set Cursor to (0,22) and Assign Fonts 2 カーソルを(0,22)へ、フォントは2を指定
  M5.Lcd.printf("Temp: %2.1fC Humi: %2.1f%%", tmp, hum); //Display of degrees Celsius and humidity in one decimal place 摂氏度および湿度を小数点以下1桁で表示

  M5.Lcd.setCursor(0, 42, 2); //Set Cursor to (0,42) and Assign Fonts 2 カーソルを(0,42)へ、フォントは2を指定
  M5.Lcd.printf("Pressure: %4.2fhPa", pressure / 100); //Atmospheric pressure in units of hPa to three decimal places 大気圧をhPaの単位で小数点以下3桁まで表示 
  
  delay(100); //Wait 100msecs 100ミリ秒待機

  if (!bme.begin(0x76)){  
      Serial.println("Could not find a valid BMP280 sensor, check wiring!");
      while (1); //Error message if the BMP280 sensor was not detected? BMP280センサが検出されなかった場合のエラー表示?
  }

  M5.Lcd.setCursor(0, 62, 2); //Set Cursor to (0,62) and Assign Fonts 2 カーソルを(0,62)へ、フォントは2を指定
  M5.Lcd.printf("CO2: %4dppm",co2); //CO2 concentration in integers in ppm CO2濃度をppmの単位で整数で表示
  
  delay(100); //Wait 100msecs 100ミリ秒待機
  
  M5.Lcd.setCursor(62, 10, 1); //Set Cursor to (62,10) and Assign Fonts 1 カーソルを(62,10)へ、フォントは1を指定
  M5.Lcd.printf("vbat: %4.2fV",vbat); //Battery voltage displayed in 2 decimal places バッテリー電圧を小数点以下2桁で表示

  //温湿度・気圧・CO2濃度・バッテリ電圧をAmbientに送信
  ambient.set(1, tmp);
  ambient.set(2, hum);
  ambient.set(3, co2);
  ambient.set(4, pressure / 100);
  ambient.set(5, vbat);

  ambient.send();

  delay(3000); //Wait 3000msecs 3000ミリ秒待機

  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); //Wakeup time from deep sleep ディープスリープからの復帰時間
  esp_deep_sleep_start(); //Start DEEP SLEEP MODE ディープスリープ開始

}

uint8_t setup_flag = 1;

void loop() {
  // put your main code here, to run repeatedly:
  
}
追記:上記コード内25行目、unsigned int channelID = YOUR ChannelID of Ambient;となっているところですが、ここは定数(数字)が直接入るため""(ダブルクォーテーション)は必要ありません。

 さて、今回のコードを作成するにあたって繰り広げられたストーリーなのですが、まずはAmbientの公式ページ上にM5StickCで小型環境センサ端末を作る、というクリティカルな記事がありましたのでそれを参考にコードを書いてみたんです…が、WiFiにつなげるコードとAmbientに送信するコードを追加しただけではうまく動かず。
 というか、WiFiにはつながってるしAmbientにはデータを送信できてるからそこに関しては問題なかったんですけど、30分ほどでフリーズして止まってしまうようになってしまいました。
 また、なぜかCO2濃度とバッテリ電圧の表示がAmbientに送信するタイミング(だと思われる)でチカチカ点滅するという謎の減少も発生。
 一体何が間違っているんだか皆目検討がつかないのでloop内で処理することを一旦諦め、参考元の通りsetup内でデータの読みとAmbientへの送信、ディープスリープへの移行を行うというコードになりました。
 また、上にも書きましたが、なぜか3秒間表示させる予定だった取得データが(おそらく)ディープスリープ時間の30秒間しっかり表示されていたり(それはそれで今の数値が見れるからいいのかもしれないけど)するんですけど、これもまったく原因がわかりません。というか書いてるコードの意味も一部しか理解できていないしね…。
 
 ちなみに、30秒ごとにデータを送信する理由としては、24時間稼働した際にAmbientさんの無料アカウントでの1日ごとのデータ量制限である3000件に引っかからない程度にデータ送信を細かくした結果です。もっと細かくしてもいいし、もっと長くしてもいいかもしれません。

 ただ、私の欲しかった就寝中の温湿度・CO2濃度のデータもグラフ化できたし、ひとまずは第二段階の完成…には至ったのかな?いや、至ったと思っておこう。

~次回以降の展望Ⅱ~

 一応Ambientに30秒ごとにデータを送信し、1日分の温湿度・CO2濃度・気圧(あと本体のバッテリ電圧)をグラフ化することには成功しました。
 次は、私のやる気と知識が増え次第、おいおいアップデートしていこうかと思います。
 例えば、でっかいスイッチが付いているのでそのスイッチを押したときのデータを画面に表示するとか、ディープスリープの挙動をもっとしっかりさせるとか。
 あとはせっかくWiFiにつなげているのでJSTから正確な時刻情報を取得して、今ダッサイ気取った題名の書いてある一行目に日付と時刻を表示させてやろうかな、とか考えています。
 どっちのコードが先に書けるかってところですかね。たぶん日付と時刻のほうが先になるかな?参考元のページのコードをコピペするだけで動きそうだし(動かないフラグ)。

~巻末の参考資料欄~

M5StickCで小型環境センサ端末を作る(上に貼ってあるリンクと同じ)
ESP32 Deep Sleep & Its Wake-up Sources(の、ESP32 Wake-up Source : Timer)

0 件のコメント:

コメントを投稿