Hobby Science&Experiment

愛と工作の日々

趣味でやっている工作や勉強したことのメモ書きです。

ESP32-WROVER開発ボードでCO2センサー(MH-Z19B)を使う

こちらを使用させていただきました
github.com

RX,TXピンを変える必要がありました。

/*----------------------------------------------------------
    MH-Z19 CO2 sensor  SAMPLE
  ----------------------------------------------------------*/

#include <MHZ19_uart.h>

const int rx_pin = 32; //Serial rx pin no
const int tx_pin = 33; //Serial tx pin no

MHZ19_uart mhz19;

/*----------------------------------------------------------
    MH-Z19 CO2 sensor  setup
  ----------------------------------------------------------*/
void setup()
{
  Serial.begin(9600);
  setupMHZ();
}

/*----------------------------------------------------------
    MH-Z19 CO2 sensor  loop
  ----------------------------------------------------------*/
void loop()
{
  int co2ppm = mhz19.getCO2PPM();
  int temp = mhz19.getTemperature();

  Serial.print("co2: ");
  Serial.println(co2ppm);
  Serial.print("temp: ");
  Serial.println(temp);

  delay(5000);
}

void setupMHZ(){
  mhz19.begin(rx_pin, tx_pin);
  mhz19.setAutoCalibration(false);
  mhz19.setHardwareSerialNo(2);
  Serial.println("MH-Z19 is warming up now.");
  delay(3 * 1000); //
}

CO2センサー(MH-Z19B)をESP32のPWMで使用する

#define pwmPin 22 // Change this to the appropriate pin connected to the MH-Z19 PWM output

int prevVal = LOW;

long th, tl, h, l, ppm;

void setup() {
  Serial.begin(115200);
  pinMode(pwmPin, INPUT);
  Serial.println("MH-Z19 is warming up now.");
}

void loop() {
  int co2value = co2read();
  Serial.println("out:"+String(co2value));
  //delay(1000);

}

int co2read() {
  int maxAttempts = 5000;
  int attempts = 0;

  while (attempts < maxAttempts) {
    long tt = millis();
    int myVal = digitalRead(pwmPin);

    // Measure pulse width
    if (myVal == HIGH) {
      if (myVal != prevVal) {
        h = tt;
        tl = h - l;
        prevVal = myVal;
      }
    } else {
      if (myVal != prevVal) {
        l = tt;
        th = l - h;
        prevVal = myVal;
        ppm = 5000 * (th - 2) / (th + tl - 4);
        //Serial.println("CO2 PPM = " + String(ppm));
        return ppm;
      }
    }
    delay(10);
    attempts++;
  }

  // If maximum attempts reached, return an error code, e.g., -1
  return -1;
}

loop内のdelayを取ると挙動がおかしくなります。

M5StickC/ESP32-WROVERとCO2センサー(MH-Z19B)でCO2濃度測定

過去に同センサーとラズパイでCO2測定を行いましたが、今回はM5StickCでの測定となります。下記を参考にさせて頂きました。コードはヘッダ部分、文字サイズ以外のみ修正しそれ以外はそのまま使用させていただきました。
shoarai.com

// 追記:M5StickCで必要なヘッダをインクルードする。
#include <M5StickC.h>
#include <MHZ19_uart.h>

// 変更:接続するピンの番号を変更する。
const int rx_pin = 36;
const int tx_pin = 26;

MHZ19_uart mhz19;

void setup()
{
  // 追記:M5StickC Plusの初期化と、G36ピンを使うためにG25ピンをフローティングにする。
  M5.begin();
  pinMode(36, INPUT);
  gpio_pulldown_dis(GPIO_NUM_25);
  gpio_pullup_dis(GPIO_NUM_25);

  Serial.begin(9600);
  mhz19.begin(rx_pin, tx_pin);
  mhz19.setAutoCalibration(false);

  // 追記:デフォルトだと画面が見づらいので、横向き表示でフォントを大きくする。
  M5.Lcd.setRotation(3);
  M5.Lcd.setTextSize(3);

  // 変更:文字を画面に表示する。
  M5.Lcd.println("MH-Z19 is warming up now.");
  delay(10 * 1000);
}

void loop()
{
  int co2ppm = mhz19.getCO2PPM();
  int temp = mhz19.getTemperature();

  // 追記:画面の表示とカーソルを初期化することで、前回の文字の表示を消す。
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 0);

  // 変更:文字を画面に表示する。
  M5.Lcd.print("CO2: ");
  M5.Lcd.println(co2ppm);
  M5.Lcd.print("temp: ");
  M5.Lcd.println(temp);

  delay(5000);
}

OLEDは良いですね。
残念なのは引き出せるピンが少ない点です。

# ESP32の場合
ESP32-WROVER使用。16,17ピンではだめでした
github.com

/*----------------------------------------------------------
    MH-Z19 CO2 sensor  SAMPLE
  ----------------------------------------------------------*/

#include <MHZ19_uart.h>
const int rx_pin = 32; //Serial rx pin no
const int tx_pin = 33; //Serial tx pin no
MHZ19_uart mhz19;

/*----------------------------------------------------------
    MH-Z19 CO2 sensor  setup
  ----------------------------------------------------------*/
void setup()
{
  Serial.begin(9600);
  setupMHZ();
}

/*----------------------------------------------------------
    MH-Z19 CO2 sensor  loop
  ----------------------------------------------------------*/
void loop()
{
  int co2ppm = mhz19.getCO2PPM();
  int temp = mhz19.getTemperature();

  Serial.print("co2: ");
  Serial.println(co2ppm);
  Serial.print("temp: ");
  Serial.println(temp);

  delay(5000);
}

void setupMHZ(){
  mhz19.begin(rx_pin, tx_pin);
  mhz19.setAutoCalibration(false);
  mhz19.setHardwareSerialNo(2);
  Serial.println("MH-Z19 is warming up now.");
  delay(3 * 1000); //
}

ESP32開発ボードでSwitchbot温湿度計のデータを取得する

これまではラズパイで2台のSwitchbot温湿度計のデータを取得していたのですが(Switchbot温度計とラズパイで快適IoTライフ - 愛と工作の日々)、より安価で低消費電力のESP32開発ボードの使用を検討しました。開発ボードはESP32-DevKitC-VE ESP32-WROVER-E開発ボードを使用しました。
qiita.com
こちらの記事のコードをchatGPT(GPT-4)の協力の元モディファイしました。

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

int scanTime = 5; //In seconds
BLEScan* pBLEScan;

BLEAddress* addr01 = new BLEAddress("xx:xx:xx:xx:xx:xx");
BLEAddress* addr02 = new BLEAddress("xx:xx:xx:xx:xx:xx");
BLEUUID serviceUUID     = BLEUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b");
BLEUUID serviceDataUUID = BLEUUID("00000d00-0000-1000-8000-00805f9b34fb");

int globalBattery;
float globalTemperature;
int globalHumidity;
bool newDataAvailable = false;
BLEAddress* detectedAddress = nullptr;

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {

      if(advertisedDevice.getAddress().equals(*addr01) || advertisedDevice.getAddress().equals(*addr02)){
        // OK
      } else {
        return;
      }

      if(!advertisedDevice.haveServiceUUID()) return;
      if(!advertisedDevice.getServiceUUID().equals(serviceUUID)) return;

      if(!advertisedDevice.haveServiceData()) return;

      std::string s = advertisedDevice.getServiceData();

      const char* servicedata = s.c_str();
      int battery = servicedata[2] & 0b01111111;
      bool isTemperatureAboveFreezing = servicedata[4] & 0b10000000;
      float temperature = ( servicedata[3] & 0b00001111 ) / 10.0 + ( servicedata[4] & 0b01111111 );
      if (!isTemperatureAboveFreezing) {
          temperature = -temperature;
      }
      int humidity = servicedata[5] & 0b01111111;

      detectedAddress = new BLEAddress(advertisedDevice.getAddress());
      globalBattery = battery;
      globalTemperature = temperature;
      globalHumidity = humidity;
      newDataAvailable = true;
    }
};

void setup() {
  Serial.begin(115200);
  Serial.println("Scanning...");

  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), true);
  pBLEScan->setActiveScan(true);
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);
}

void loop() {
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
  pBLEScan->clearResults();

  if (newDataAvailable) {
    Serial.print("Address: ");
    Serial.println(detectedAddress->toString().c_str());

    Serial.print("Battery: ");
    Serial.print(globalBattery);
    Serial.println("%");

    Serial.print("Temperature: ");
    Serial.print(globalTemperature);
    Serial.println("°C");

    Serial.print("Humidity: ");
    Serial.print(globalHumidity);
    Serial.println("%");

    newDataAvailable = false;
    delete detectedAddress;
    detectedAddress = nullptr;
  }

  delay(2000);
}

このコードをアップロードすると、シリアルモニタに2つの温湿度計のデータが交互に表示されました。

EC2の定期起動・停止を利用したPythonプログラムの低コスト定期実行

前回EC2でPythonを定期実行できるようにしたものの、cronを定期実行するにはサーバーを起動しっぱなしにしないといけない。そうすると現時点は無料期間中とは言え、いずれ少なくない額の課金が発生する。
jakejake.hatenablog.com
というわけで、EC2サーバーの立ち上げ・たち下げ自体を定期実行してサーバー使用量を最小限にとどめる節約を試みた。
「EC2 定期実行」などで検索してめぼしい結果を試してみることにした。

主にこちらの手順をAmazon EventBridgeに置き換えてトレースした。
confrage.jp

Identity and Access Management (IAM)の設定はこちらが詳しかった。
agohack.com

EC2インスタンスを見ると、EventBridgeの実行で立ち上げ、立ち下げが出来ていることが確認できた。
後はこれをcron実行に切り替える。

cronは以下を参照。
docs.aws.amazon.com

  1. EC2立ち上げ:18:50 (UTC 09:50)
  2. EC2上のcronでPythonプログラム実行:18:53 (UTC 09:53)
  3. EC2立ち下げ:18:55 (UTC 09:55)

こんな感じで一回テストしてみると、インスタンスの状態がスケジュール通り切り替わり、cronも実行されているようだ。
良かった良かった。

EC2でPython3とCron動かした

ラズパイ不調につきRasbianでcron実行しているPythonプログラム達が滞るようになりました。
PythonでWeb漫画の更新をLine通知する - 愛と工作の日々
ゴミ出しの前日にLINEでアラートを出す【Python3】 - 愛と工作の日々
他にも何個か動かしていた分が生活で役立っていたので、これは何とかしないと。

そこでサーバーレスで何とか出来ないか検証してみました。
AWSもEC2もなんも知らんのですが、下記参照でcronまで動きました。

qiita.com
www.yukiyukiponsu.work

Python3.7も入ったから、自作プログラムも動くはず。

  • 追記

24時間サーバーが立ち上がりっぱなしでお金はかからないのだろうか。

aws.amazon.com
750時間 ÷ 24時間/1日 = 31.25日だから、一年間は無料で使えるってことか。

その後はインスタンスの状態に寄るのね、cronよりもインスタンスのstop/restartをスケジュール出来る方法のほうがよさそうだ。
qiita.com