2020年12月16日水曜日

とある病に関するクソ長つぶやきのまとめ

 はい、あまりに長すぎるツリーになっちゃったのでこっちで一つの記事にまとめます。


約半年治療をつづけて、気づいたことやら思ったことやら。もちろん他人にそのまま使えるかはわからないけど、参考にはできるんじゃないかな…?

まず、自問自答はだいたいマイナス方面にしか効かないこと。誰かと話しましょ。誰か、ほかの人となにか話を共有するだけで、気持ちは楽になりますね。

薬。SNRIのサインバルタをメインに飲んでますが、これら向精神薬の効果だけで大分不安は解消されました。私が軽いっていうのもあるかもしれないけど。
でも、向精神薬ってだいたい副作用に悩まされる。私の場合は主にアカシジア。万能なものは無いってことですね~。

”本当に”気分が沈んでしまっている人は、おとなしく数日休んで好きなことをするか寝るかして、メンタルを回復させましょう。 メンブレとか軽い気持ちでやってる人は一晩寝ればなおるんじゃない?ストロング缶をちぎって直飲みしたりワンカップを地面に叩きつけたりする倫理観の脳内回路してるんだから。

あと、”本当に”多動状態が止まらない人は何をどうしてもその状態は収まりません。薬を飲んでしばらく待つか、疲れきるほど動くまで。 たかだか曲を聞いて多動とまら~んwとか言ってる人は、座禅の一日体験修行でもして耐えられるようにするか、本当に止まらないのなら心療内科にかかってください。

あとは、ゆっくり喋る人っていますけど、その人ってそのスピードでしかしゃべれないんですね。 一番ひどかった治療直前は、いつも早口なはずの私が、まるで起きた直後のようにゆっくりしかしゃべることができなかったんです。
つまり、ゆっくりしかしゃべることができない人は、本当にそれ以上のスピードで話すことができないんだなーということに気づきました。わざととかじゃないんだねってこと。

ちなみに、もし実際に罹ってしまった場合、回復度合いのグラフは直線ではなくルートxのグラフのような感じであることに留意しましょう(形がわからなかったらググってね)。
最初は私も、不安感が解消されて、後数ヶ月もすれば回復できるかなーと思っていたのですが、プラス方面の思考はその後ゆっくりとしか回復しないです。未だに、トリガーであると思われる関連のことには触れられないでいます。
ほぼニート状態なくらい、好き勝手している、治療環境としては良い方であるのにも関わらず、です。

だからこそ、普段から感情を爆発させる機会を作ったり、キチゲを消化する機会を作ったり、誰かと喋りっからかしたり、何でもいいから精神を安定させる場を作ったほうがいいと思います。いざ罹ったら本当に時間がかかりますよ。

ちなみにあえて具体的な病名は出してないです。まあかんたんに察せると思うけど。

2020年12月10日木曜日

忙しい人のための:ド素人がM5StickCを用いたマルチ環境計を作ってみた コードへのポータルページ

 御託はいい!はやくコードをよこせ!!な人のために、コードごとのページとそれにつながるポータルページを作りました。


ENV Ⅱ HATの公式サンプルコードから地磁気取得部をはぶいただけのコードです。単純だよ!

NDIR方式のCO2濃度センサであるMH-Z19Bを単体で動作させるコードです。これはもっと単純。

私がやりたかったやつ。温湿度・CO2濃度・大気圧(とバッテリ電圧)が表示されます。ちょっと複雑かな?

M5Stack公式から出ているUNIT HEARTという心拍・SpO2センサを単体で動かすコードです。このご時世結構重要だと思うけどどうなんだろう。単純だけど理解が足りないから一部変な記述が混ざってるかも??あとここは簡潔にした記事にはしてません(そもそも元が簡潔)

CODE03を少しいじってAmbientにデータを送信してグラフ化するようにしたコード。ただし本体の画面に表示されるデータの更新頻度は低い(というか送信と一括処理してるから30秒ごと)

参考元様のコードをほぼそのままsetup関数内にぶちこんだだけの役に立たないコードです。

CODE05…というかVer2.0のコードに、CODE06のコードを調整しつつぶちこみ、またついでに一部(バッテリ電圧)の表示位置とフォントの大きさを微調整しました。そんだけっちゃあそんだけ。

CODE05 ~Ambientとの接続(問題未解決、でも普通に動くよ)~

 忙しい人のための最低限の分割したバージョンの記事Part4

~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;となっているところですが、ここは定数(数字)が直接入るため""(ダブルクォーテーション)は必要ありません。

CODE03 ~ENV Ⅱ HATとMH-Z19B、2センサの統合処理~

 忙しい人のための最低限の分割したバージョンの記事Part3

~2センサの統合処理~

 まずは完成形から。
マステでごちゃごちゃ留めてありますがこれが完成形です
ちなみに8の字というか∞風に線が走ってます

 まずENV Ⅱ HATについて、これはM5StickCのHAT端子のうち必要なGND/G26/G0/3V3端子を延長して接続しました。配線はそのまま延長しただけなので公式に準じています(5V端子じゃなくて3V3端子だったのは意外だった)。

 そして、色々と試行錯誤しながらなんとか完成したコードがこちらです。Multi-Environmental-Sensor-Systemとか変な名前つけちゃってますが無視してあげてください。
 あと、上2つのぶんに加えてバッテリー電圧をちょこんとシステム名の下に表示してあります。バッテリ容量が小さすぎで電圧表示のほうが管理しやすいかなと思って、%ではなく電圧を表示してあります。まあ大体電源つないであれば問題ないんだけどね。

code:3 MultiSensingSketch
/*注意 私の理解できたところのみ説明が書かれています
 説明がないということは…そういうことです
 また、英語はガバガバ英語だったりDeepLに頼った英語だったりします。自己満です。 */

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

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

//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 ENVHAT2 ENVHAT2のI2Cを初期化
  M5.Axp.ScreenBreath(10); //Decrease Lcd's Brightness 画面の輝度を下げる
  setCpuFrequencyMhz(80); //Set Cpu Frequency to 160MHz CPUの動作周波数を80MHzに
  M5.Lcd.fillScreen(BLACK); //Fill Screen Black 背景色を黒に
  M5.Lcd.setRotation(3); //Set Lcd's Direction 画面を横向き(左上)に
  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"); //Show sentences in "" on the screen 画面に””内の文章を表示
  
  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センサが検出されなかった場合のエラー表示?
  }

}

uint8_t setup_flag = 1;

void loop() {
  // put your main code here, to run repeatedly:
  if(sht3x.get()==0){
    tmp = sht3x.cTemp;
    hum = sht3x.humidity; //Acquisition of temperature and humidity data from sht3x? sht3xからの温湿度データの取得?
  }

  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桁で表示
  
  float pressure = bme.readPressure(); //Calculating atmospheric pressure in floating point? 浮動小数点で大気圧を計算?
  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センサが検出されなかった場合のエラー表示?
  }

  int co2,temp; //Calculate CO2 concentration by integer? 整数でCO2濃度を計算?
  int i;
  unsigned int t;
  co2 = mhz19.getPPM(); //Datas read from MH-Z19MH-Z19からのデータ呼び出し?
  temp = mhz19.getTemperature();
  Serial.printf("CO2=%d ppm / Temp=%d C\n",co2,temp);
  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ミリ秒待機

  float vbat = M5.Axp.GetBatVoltage(); //Get the battery voltage of the M5StickC? M5StickCのバッテリー電圧を取得
  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桁で表示

}

 一応これらのコードは私の環境上で動作したものとなります。なんならこの記事を書くまでに1ヶ月以上寝かせてしまったので3つ目のコードに至っては約1ヶ月間の稼働実績もあります。まだクラウドに接続してないからログは取れないけどね…

CODE01 ~ENV Ⅱ HATのサンプルコードの小改造~

 忙しい人のための最低限の分割したバージョンの記事Part1


~ENV Ⅱ HATのサンプルコードの小改造~

 下記のコードは、サンプルコードから地磁気を取得する部分を省き、また温度の後にCを付けて(℃はできないらしい?)、湿度を小数点以下一桁まで表示するようにしてその後ろに%を付けて、気圧をhPa表示にするという小改造を行ったものです。
 それ以外はまったくいじっていません。
下のコードで動いている様子

code:1 ENV II HAT KAI
/*
    note: need add library Adafruit_BMP280 from library manage
    Github: https://github.com/adafruit/Adafruit_BMP280_Library
*/

/*注意 私の理解できたところのみ説明が書かれています
 説明がないということは…そういうことです
 また、英語はガバガバ英語だったりDeepLに頼った英語だったりします。自己満です。 */

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

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

//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を初期化
  Wire.begin(0,26); //Initialize I2C of ENVHAT2 ENVHAT2のI2Cを初期化
  M5.Lcd.setRotation(3); //Set Lcd's Direction 画面を横向き(左上)に
  M5.Lcd.fillScreen(BLACK); //Fill Screen Black 背景色を黒に
  M5.Lcd.setCursor(0, 0, 2); //Set Cursor to (0,0) and Assign Fonts 2 カーソルを(0,0)へ、フォントは2を指定
  M5.Lcd.println("ENV TEST 2"); //Show sentences in "" on the screen 画面に””内の文章を表示

  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センサが検出されなかった場合のエラー表示?
  }
}
uint8_t setup_flag = 1;

void loop() {
  // put your main code here, to run repeatedly:
  if(sht3x.get()==0){
    tmp = sht3x.cTemp;
    hum = sht3x.humidity; //Acquisition of temperature and humidity data from sht3x? sht3xからの温湿度データの取得?
  }

  
  M5.Lcd.setCursor(0, 20, 2); //Set Cursor to (0,20) and Assign Fonts 2 カーソルを(0,20)へ、フォントは2を指定
  M5.Lcd.printf("Temp: %2.1fC Humi: %2.1f%%", tmp, hum); //Display of degrees Celsius and humidity in one decimal place 摂氏度および湿度を小数点以下1桁で表示
  
  float pressure = bme.readPressure();
  M5.Lcd.setCursor(0, 40, 2); //Set Cursor to (0,40) and Assign Fonts 2 カーソルを(0,40)へ、フォントは2を指定
  M5.Lcd.printf("pressure: %4.3fhPa", 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センサが検出されなかった場合のエラー表示?
  }
 }

CODE02 ~MH-Z19B~

 忙しい人のための最低限の分割したバージョンの記事Part2

~MH-Z19B~

 NDIR式のCO2センサの中では安価で定番な、MH-Z19B(リンク:Amazonの検索結果)です。Amazonの商品画像とはちょっと裏の基盤のシルク印刷が違ってるけど、なんとかわかったからヨシ。(TxとRxに"d"がついて、VinがV+、GNDがV-になってて、ADTとSRが消えてる)(ADTとSRは使わないからどうでもいいけど)
表面 こっちは変化なし

裏面 基盤の色とかシルクとかちょっと違う

 そして、ピンヘッダを必要な部分にはんだ付けして

表面はんだ付け後
裏面はんだ付け後

 GroveケーブルでM5StickC本体と接続
 ちなみに、センサ側のTxdにG32、RxdにG33を接続して、V+に5V、V-にGNDを接続しました(TxdとRxdの端子を逆にしてみたら-1ppmという値が出たのでこの通り配線しないといけないようです)。
ペイント3分クォリティのMH-Z19B簡易配線図


Grove-メスピンヘッダケーブル
接続後 ピンが緩かったのでこのあとテープで補強した


下記のコードで動いている様子 シンプルでしょ?

code:2 MH-Z19B TEST
/*注意 私の理解できたところのみ説明が書かれています
 説明がないということは…そういうことです
 また、英語はガバガバ英語だったりDeepLに頼った英語だったりします。自己満です。 */
 
//Include Libraries ライブラリをインクルード
#include <M5StickC.h>
#include "MHZ19_uart.h"

//Define Valiable 変数を定義
MHZ19_uart mhz19;

void setup() {
  M5.begin(); //Initialize M5StickC M5StickCを初期化
  delay(50); //Wait 50msecs 50ミリ秒待機

  mhz19.begin(32,33); //Initialize I2C of MHZ19 MHZ19のI2Cを初期化
  mhz19.setAutoCalibration(false); //MHZ19's autoCalibration turn off MHZ19の自動校正をしない
  delay(3000); //Wait 3000msecs 3000ミリ秒待機
  M5.Lcd.setRotation(3); //Set Lcd's Direction 画面を横向き(左上)に
  M5.Axp.ScreenBreath(10); //Decrease Lcd's Brightness to 10 画面の輝度を10に下げる
  setCpuFrequencyMhz(80); //Set Cpu Frequency to 80MHz CPUの動作周波数を80MHzにする
  M5.Lcd.fillScreen(BLACK); //Fill Screen Black 背景色を黒に
  M5.Lcd.setCursor(0,0,4); //Set Cursor to (0,0) and Assign Fonts 4 カーソルを(0,0)へ、フォントは4を指定
}

void loop() {
  int co2,temp;
  int i;
  unsigned int t;
  co2 = mhz19.getPPM();
  temp = mhz19.getTemperature();
  Serial.printf("CO2=%d ppm / Temp=%d C\n",co2,temp);
  M5.Lcd.fillScreen(BLACK); //Fill Screen Black 背景色を黒に
  M5.Lcd.setCursor(0,20,4); //Set Cursor to (0,0) and Assign Fonts 4 カーソルを(0,0)へ、フォントは4を指定
  M5.Lcd.printf("%4dppm",co2); //CO2 concentration in integers in ppm CO2濃度をppmの単位で整数で表示

  
}

ド素人が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)

2020年12月7日月曜日

ド素人がM5StickCを用いたマルチ環境計を作ってみた 番外編:心拍・SpO2センサ

 Part1で番外にする~とか書いていた心拍・SpO2センサ編です。
 とはいっても、サンプルコードを探してきてちょっといじった(かどうかも不確か)だけのものです。だから番外にしてるんだけどね。
 
 さて、今回採用したセンサはM5StickC…というかM5Stack公式のセンサキット、UNIT HEARTです。MAX30100センサを採用し、赤外線LEDと光センサを用いて赤血球中の酸素濃度を測定する…らしいです。スイッチサイエンスさんの説明書きまんまです。

UNIT HEART 公式より

 ただ、今回も一筋縄では行かず。なんか調べたコードが生データの表示しかしないやつだったりしたのです。最終的にはなんとかなった(と思う)けど。
 (あと、一枚も写真撮ってなかったからこの記事書きながら撮る羽目になった)

 とりあえず動作中の写真をば。
 HRはHeartRate、つまり心拍数を表し、SpO2は血中酸素濃度を表しています。
 心拍数の正常値は60前後で、SpO2の正常値はなんと97%以上だそうです。もし95%以下なら何かしらの疾患、特に呼吸器系の疾患が疑われるようです。90%を下回るような方は酸素吸入が必要なレベルだそうです。このご時世、ある程度の頻度で測っておくといいかもしれませんね。目安にはなりますから。
動作中(環境計のGroveコネクタを付け替えただけ)

赤色LEDが発光してます
指をのせたところです

 指をのせてから10秒ほど経つと、ある程度正確な値が表示されます。ただし、ご覧の通り医療機関等で使用されているようなパルスオキシメーターとは似ても似つかないため、少しでも指がずれたり、当て方が悪かったりするとおかしな値が帰ってきます。ガチな人は指を固定するためのエンクロージャーを作ったりしているようです。

 今回のコードです…が、ほぼほぼ「M5Stack Basicで通信機能付き心拍・血中酸素モニタを作る(MAX30100) - Qiita」さんのコードまんまです。M5Stack用になっているところをM5StickC用に直したり、一部の表示を削って単純に文字情報のみを表示するようにカスタマイズしただけのものになります。
 一応書いておきますが、Arduino IDEのライブラリの管理からMAX30100_PulseOximeter.hというライブラリをインストールしないと動きません。
 また、UNIT HEARTを接続せずにM5StickCを起動した場合にはFAILEDと表示されるようになってます。
 それ以外はちんぷんかんぷんなコードなので、今回は説明なしです。ついてる説明はもともとあったものです。私はほぼほぼ何をやっているのかわかっていません…

MAX30100_SIMPLE_HRandPulseOximeter
#include <M5StickC.h>
#include <Wire.h>
#include "MAX30100_PulseOximeter.h"

#define REPORTING_PERIOD_MS     1000

PulseOximeter pox;
uint8_t Heart_rate = 0;
uint8_t Spo2 = 0;
uint32_t tsLastReport = 0;

// Callback (registered below) fired when a pulse is detected
void onBeatDetected()
{
  printHRandSPO2(true);
}

void setup()
{
  Serial.begin(115200); // to PC via USB

  M5.begin();
  M5.Lcd.setRotation(3);

  // Initialize sensor
  if (!pox.begin()) {
    M5.Lcd.println("FAILED");
    for(;;);
   } else {
    M5.Lcd.println("SUCCESS");
  }

  //LED Configuration
  pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);
  //Register a callback for the beat detection
  pox.setOnBeatDetectedCallback(onBeatDetected);
}

void loop()
{
  M5.update();  //update button state
  pox.update(); //update pulse oximeter

  if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
    Heart_rate = (int)pox.getHeartRate();
    Spo2 = pox.getSpO2();
    printHRandSPO2(false);
    tsLastReport = millis();
  }
}

void printHRandSPO2(bool beat)
{
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0,20,4);
  M5.Lcd.print("HR:   "); M5.Lcd.println(Heart_rate);
  M5.Lcd.print("SpO2: "); M5.Lcd.println(Spo2);  
}
多分これいらないコード消しきれてないかもな…

~巻末の参考資料~

2020年12月5日土曜日

ド素人がM5StickCを用いたマルチ環境計を作ってみた Ver1.1 (CO2測定関連のプチコラム追加)

とりあえずVer.1としては完成したので、作り方を書き連ねていこうと思います~

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

※最下部のマルチ環境コードに訂正、M5StickC非公式日本語リファレンスさんによると、バッテリ電圧の取得はdouble vbat~の関数よりもfloat vbat = M5.Axp.GetBatVoltage();で取得したほうが良いそうです。よって該当部分を修正しました。主たる動作に変更はありません。
※また、番外編の心拍・SpO2センサ編へのリンクを貼りました。よろしければ見ていってください。
※あと、MH-Z19Bの下欄に調子乗ったこと書きました。CO2測定の云々を書いてあります。
※最新版→ド素人がM5StickCを用いたマルチ環境計を作ってみた Ver3.0(Ambientとの接続優先で30秒おきのデータ更新となります)

~今回作ったもの~

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

使用したデバイス類

・M5StickC(ESP32搭載小型コンピュータ)
・ENV Ⅱ HAT(M5StickC公式の環境センサキット、温湿度・気圧・地磁気を測定可能)
・MH-Z19B(汎用のNDIR-非分散型赤外線吸収法-方式CO2センサ)
・Grove-メスピンヘッダケーブル(一部要加工)

~まずは~

 開発環境として、Arduino IDEをインストール。あといらないらしいけどCP210xというM5StickC用のドライバもインストールしました。
 そこからM5StickCのセットアップをするのですが、ド素人のわたしはそこでも苦戦しました。
 そのあたりを少し書き連ねさせていただきます。

 まず、Tools内のBoardsは、ESP32 Pico KitでもM5Stick-Cでも大丈夫でした。ただし前者のUpload Speedは115200が最大、後者は1500000が最大になります。地味に書き込み速度って変化を感じますよ。
 あと、ポート番号は自分のものを調べて覚えておきましょう。たぶんそう変わらないので一回確認すれば十分だと思います。

 あと、ExamplesのFactoryTestをそのまま書き込もうとすると、FastLED.hというライブラリが無いよと怒られるのでライブラリをインストールしましょう。
 さらに、”スケッチが大きすぎます”と怒られたのでToolsのPartition SchemeをNo OTA(Large APP)にして解決しました。もしかしたら一個下のLarge APPs with OTAでも良かったかもしれません。

 次にHello Worldを表示。ここまでできたらチュートリアルは完了って感じ…でいいんだよね?
Hello World 文字サイズ変更と画面の向きの変更はした

 さてここまででもいくつかのサイトを参考にさせていただいたのですが、そちらに関しては保存するのを忘れていたためなんとなくしか覚えていません…ここ(リンク:MSR合同会社さん)は残してあったんですが、他のエラーにあたったときはその場で調べてそのまま消してしまいまして…。あと公式のページは参考にしたかな??

~ENV Ⅱ HATのサンプルコードと小改造~

 M5Stack公式サイトから、ENV Ⅱ HATのドキュメントを探し、GithubにあるサンプルコードをそのままArduino IDEにコピペして書き込み。ちなみに1こずつタブ?に分けておかないと動きませんでした。(そういうことすら知らない)
サンプルコードまんまの状態
 下記のコードは、サンプルコードから地磁気を取得する部分を省き、また温度の後にCを付けて(℃はできないらしい?)、湿度を小数点以下一桁まで表示するようにしてその後ろに%を付けて、気圧をhPa表示にするという小改造を行ったものです。
 それ以外はまったくいじっていません。
下のコードで動いている様子

code:1 ENV II HAT KAI
/*
    note: need add library Adafruit_BMP280 from library manage
    Github: https://github.com/adafruit/Adafruit_BMP280_Library
*/

/*注意 私の理解できたところのみ説明が書かれています
 説明がないということは…そういうことです
 また、英語はガバガバ英語だったりDeepLに頼った英語だったりします。自己満です。 */

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

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

//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を初期化
  Wire.begin(0,26); //Initialize I2C of ENVHAT2 ENVHAT2のI2Cを初期化
  M5.Lcd.setRotation(3); //Set Lcd's Direction 画面を横向き(左上)に
  M5.Lcd.fillScreen(BLACK); //Fill Screen Black 背景色を黒に
  M5.Lcd.setCursor(0, 0, 2); //Set Cursor to (0,0) and Assign Fonts 2 カーソルを(0,0)へ、フォントは2を指定
  M5.Lcd.println("ENV TEST 2"); //Show sentences in "" on the screen 画面に””内の文章を表示

  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センサが検出されなかった場合のエラー表示?
  }
}
uint8_t setup_flag = 1;

void loop() {
  // put your main code here, to run repeatedly:
  if(sht3x.get()==0){
    tmp = sht3x.cTemp;
    hum = sht3x.humidity; //Acquisition of temperature and humidity data from sht3x? sht3xからの温湿度データの取得?
  }

  
  M5.Lcd.setCursor(0, 20, 2); //Set Cursor to (0,20) and Assign Fonts 2 カーソルを(0,20)へ、フォントは2を指定
  M5.Lcd.printf("Temp: %2.1fC Humi: %2.1f%%", tmp, hum); //Display of degrees Celsius and humidity in one decimal place 摂氏度および湿度を小数点以下1桁で表示
  
  float pressure = bme.readPressure();
  M5.Lcd.setCursor(0, 40, 2); //Set Cursor to (0,40) and Assign Fonts 2 カーソルを(0,40)へ、フォントは2を指定
  M5.Lcd.printf("pressure: %4.3fhPa", 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センサが検出されなかった場合のエラー表示?
  }
 }


~MH-Z19B~

 ここまでは公式にドキュメントだったりコードだったりが用意されていたので実装もかんたんだったんですが、次は汎用の規格を使ったものになります。まあ作例いっぱいあるから参考にすれば私ですらできたので大丈夫じゃないかな…
 ということで、NDIR式のCO2センサの中では安価で定番な、MH-Z19B(リンク:Amazonの検索結果)です。Amazonの商品画像とはちょっと裏の基盤のシルク印刷が違ってるけど、なんとかわかったからヨシ。(TxとRxに"d"がついて、VinがV+、GNDがV-になってて、ADTとSRが消えてる)(ADTとSRは使わないからどうでもいいけど)
表面 こっちは変化なし

裏面 基盤の色とかシルクとかちょっと違う

 そして、ピンヘッダを必要な部分にはんだ付けして

表面はんだ付け後
裏面はんだ付け後

 GroveケーブルでM5StickC本体と接続
 ちなみに、センサ側のTxdにG32、RxdにG33を接続して、V+に5V、V-にGNDを接続しました(TxdとRxdの端子を逆にしてみたら-1ppmという値が出たのでこの通り配線しないといけないようです)。
ペイント3分クォリティのMH-Z19B簡易配線図


Grove-メスピンヘッダケーブル
接続後 ピンが緩かったのでこのあとテープで補強した


下記のコードで動いている様子 シンプルでしょ?

code:2 MH-Z19B TEST
/*注意 私の理解できたところのみ説明が書かれています
 説明がないということは…そういうことです
 また、英語はガバガバ英語だったりDeepLに頼った英語だったりします。自己満です。 */
 
//Include Libraries ライブラリをインクルード
#include <M5StickC.h>
#include "MHZ19_uart.h"

//Define Valiable 変数を定義
MHZ19_uart mhz19;

void setup() {
  M5.begin(); //Initialize M5StickC M5StickCを初期化
  delay(50); //Wait 50msecs 50ミリ秒待機

  mhz19.begin(32,33); //Initialize I2C of MHZ19 MHZ19のI2Cを初期化
  mhz19.setAutoCalibration(false); //MHZ19's autoCalibration turn off MHZ19の自動校正をしない
  delay(3000); //Wait 3000msecs 3000ミリ秒待機
  M5.Lcd.setRotation(3); //Set Lcd's Direction 画面を横向き(左上)に
  M5.Axp.ScreenBreath(10); //Decrease Lcd's Brightness to 10 画面の輝度を10に下げる
  setCpuFrequencyMhz(80); //Set Cpu Frequency to 80MHz CPUの動作周波数を80MHzにする
  M5.Lcd.fillScreen(BLACK); //Fill Screen Black 背景色を黒に
  M5.Lcd.setCursor(0,0,4); //Set Cursor to (0,0) and Assign Fonts 4 カーソルを(0,0)へ、フォントは4を指定
}

void loop() {
  int co2,temp;
  int i;
  unsigned int t;
  co2 = mhz19.getPPM();
  temp = mhz19.getTemperature();
  Serial.printf("CO2=%d ppm / Temp=%d C\n",co2,temp);
  M5.Lcd.fillScreen(BLACK); //Fill Screen Black 背景色を黒に
  M5.Lcd.setCursor(0,20,4); //Set Cursor to (0,0) and Assign Fonts 4 カーソルを(0,0)へ、フォントは4を指定
  M5.Lcd.printf("%4dppm",co2); //CO2 concentration in integers in ppm CO2濃度をppmの単位で整数で表示

  
}
ここの参考文献は末尾※1

-プチコラム-

 今回のセンサで採用されているNDIR、非分散赤外線吸収法によるCO2測定について。
 まず、二酸化炭素は三分子原子で、対称・非対称・変角の3つの分子運動をしています。そのうち非対称運動が赤外線吸収を示します。
 その振動数が2349cm^-1(カイザー、cmで測った光の波長の逆数、つまりλ=約425μm)と特異な数値を示すため、単一波長の吸収のみ検出できる非分散方式での測定が可能で、単純な構造のためある程度の小型化と安価にすることができるのです。
 あと、その赤外線を吸収する、というところから想像できるかもしれませんが、CO2が温室効果ガスとして取り上げられているのも、CO2が赤外線を吸収し大気を暖めるためです。

 ちなみに、同じ赤外線吸収法による測定として、FT-IR(フーリエ変換赤外分光法)というものがあります。こちらは装置が大掛かりで高価だったりしますが、様々な成分を検出することができます。


~2センサの統合処理~

 さてここまで単体動作はなんとかなったのですが、私が作りたいのはマルチな環境計。温度と湿度、換気の目安ともなるCO2濃度(気圧はおまけ)を同時に表示させたかったのです。
 ここでプログラミングの知識がある人ならかんたんに上2つのコードを難なくまとめて統合したコードを書いちゃうんでしょうけど、もちろん私は苦戦しました。
 まずGPIOピンの配列がどうなってるのというところからスタートし、番外に乗っける予定の心拍・SpO2センサをついでに載っけようとしたり、(わかってる人にとっては当たり前なのかもしれないけど)同じGPIOピンを両方のセンサにつなげて動かねーってなったりと、色々と紆余曲折を経てとりあえず完成させました(とりあえず、というのはインターネットに接続するアップデートを行う予定があるから)。

 なにはともあれ完成形から。
マステでごちゃごちゃ留めてありますがこれが完成形です
ちなみに8の字というか∞風に線が走ってます

 ENV Ⅱ HATについて、これはM5StickCのHAT端子のうち必要なGND/G26/G0/3V3端子を延長して接続しました。配線はそのまま延長しただけなので公式に準じています(5V端子じゃなくて3V3端子だったのは意外だった)。

 そして、色々と試行錯誤しながらなんとか完成したコードがこちらです。Multi-Environmental-Sensor-Systemとか変な名前つけちゃってますが無視してあげてください。
 あと、上2つのぶんに加えてバッテリー電圧をちょこんとシステム名の下に表示してあります。バッテリ容量が小さすぎで電圧表示のほうが管理しやすいかなと思って、%ではなく電圧を表示してあります。まあ大体電源つないであれば問題ないんだけどね。

code:3 MultiSensingSketch
/*注意 私の理解できたところのみ説明が書かれています
 説明がないということは…そういうことです
 また、英語はガバガバ英語だったりDeepLに頼った英語だったりします。自己満です。 */

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

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

//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 ENVHAT2 ENVHAT2のI2Cを初期化
  M5.Axp.ScreenBreath(10); //Decrease Lcd's Brightness 画面の輝度を下げる
  setCpuFrequencyMhz(80); //Set Cpu Frequency to 160MHz CPUの動作周波数を80MHzに
  M5.Lcd.fillScreen(BLACK); //Fill Screen Black 背景色を黒に
  M5.Lcd.setRotation(3); //Set Lcd's Direction 画面を横向き(左上)に
  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"); //Show sentences in "" on the screen 画面に””内の文章を表示
  
  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センサが検出されなかった場合のエラー表示?
  }

}

uint8_t setup_flag = 1;

void loop() {
  // put your main code here, to run repeatedly:
  if(sht3x.get()==0){
    tmp = sht3x.cTemp;
    hum = sht3x.humidity; //Acquisition of temperature and humidity data from sht3x? sht3xからの温湿度データの取得?
  }

  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桁で表示
  
  float pressure = bme.readPressure(); //Calculating atmospheric pressure in floating point? 浮動小数点で大気圧を計算?
  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センサが検出されなかった場合のエラー表示?
  }

  int co2,temp; //Calculate CO2 concentration by integer? 整数でCO2濃度を計算?
  int i;
  unsigned int t;
  co2 = mhz19.getPPM(); //Datas read from MH-Z19MH-Z19からのデータ呼び出し?
  temp = mhz19.getTemperature();
  Serial.printf("CO2=%d ppm / Temp=%d C\n",co2,temp);
  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ミリ秒待機

  float vbat = M5.Axp.GetBatVoltage(); //Get the battery voltage of the M5StickC? M5StickCのバッテリー電圧を取得
  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桁で表示

}

 一応これらのコードは私の環境上で動作したものとなります。なんならこの記事を書くまでに1ヶ月以上寝かせてしまったので3つ目のコードに至っては約1ヶ月間の稼働実績もあります。まだクラウドに接続してないからログは取れないけどね…

~次回以降の展望~

 とりあえず、Ambientさんのサービスを利用してグラフ化はしてみたいと思ってます。
 あとはM5StickC自体をWebサーバー化して、ブラウザ経由で測定値を見れるようになったらなぁ、どっちも実装できたらなぁと画策しています。どっちもっていうのが成功しなさそうだけど。


~巻末の参考資料欄~

 ・電源管理(AXP192)(の、バッテリー電圧取得 GetBatVoltage())

※1
CO2測定(MH-Z19B・M5StickCPlus) - Qiita ←たぶん一番参考にしたのはここかな…
(Githubのライブラリは、どっちをメインで使ったか忘れたので2つ載せました。実際使用しているライブラリは片方のはずなのでどっちかは要らないです)
(リンク禁止のところなどがあれば教えて下さい…)