Last active
November 23, 2021 08:45
-
-
Save mizdra/955199c3ae4faad148c36ba0e6eba4e3 to your computer and use it in GitHub Desktop.
二酸化炭素濃度測定機 (M5StickC Plus + MH-Z19C)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// based https://gist.github.com/YujiSoftware/9274366a93f1ac7f9208bd4abf096527 | |
#include <M5StickCPlus.h> | |
#include "MHZ19.h" | |
#define RX_PIN 36 // Rx pin which the MHZ19 Tx pin is attached to | |
#define TX_PIN 0 // Tx pin which the MHZ19 Rx pin is attached to | |
#define BAUDRATE 9600 // Device to MH-Z19 Serial baudrate (should not be changed) | |
#define LCD_MODE_DIGIT 0 | |
#define LCD_MODE_GRAPH 1 | |
#define BRIGHTNESS 8 | |
#define ALERT_OK 0 | |
#define ALERT_WARNING 1 | |
#define ALERT_CRITICAL 2 | |
MHZ19 myMHZ19; // Constructor for library | |
HardwareSerial mySerial(1); // (ESP32 Example) create device to MH-Z19 serial | |
int lcdMode = LCD_MODE_DIGIT; | |
auto alertLevel = ALERT_OK; | |
unsigned long lastBeepOperationTime = 0; | |
// 最後に CO2 濃度を測定した時刻 | |
unsigned long lastMeasureTime = 0; | |
// CO2 濃度の測定間隔 | |
unsigned long measureInterval = 60 * 1000; | |
// 今回の B ボタンの押下でキャリブレーション済みかどうか。 | |
bool calibratedAtCurrentPressing = false; | |
// 画面幅 (240) 分だけ計測値をストックできるようにしておく | |
int history[240] = {}; | |
int historyPos = 0; | |
// ダブルバッファリング用のバッファを用意 | |
TFT_eSprite sprite(&M5.Lcd); | |
auto green = TFT_GREEN; | |
auto orange = TFT_ORANGE; | |
auto red = TFT_RED; | |
auto purple = TFT_PURPLE; | |
void setup() { | |
M5.begin(); | |
M5.Axp.ScreenBreath(BRIGHTNESS); | |
Serial.begin(9600); // Device to serial monitor feedback | |
mySerial.begin(BAUDRATE, SERIAL_8N1, RX_PIN, TX_PIN); // (ESP32 Example) device to MH-Z19 serial start | |
myMHZ19.begin(mySerial); // *Serial(Stream) refence must be passed to library begin(). | |
myMHZ19.autoCalibration(false); | |
// ビープ音の設定 | |
M5.Beep.begin(); | |
M5.Beep.setBeep(8000, 100); | |
digitalWrite(M5_LED, HIGH); | |
M5.Lcd.setRotation(3); | |
// ダブルバッファリング用のバッファを初期化 | |
sprite.createSprite(M5.Lcd.width(), M5.Lcd.height()); | |
render(); | |
pinMode(M5_LED, OUTPUT); | |
} | |
void loop() { | |
auto now = millis(); | |
M5.update(); | |
M5.Beep.update(); | |
// Aボタン: モードを切り替える | |
if ( M5.BtnA.wasPressed() ) { | |
lcdMode = (lcdMode + 1) % 2; | |
render(); | |
} | |
// Bボタン: 3秒間長押しされたらゼロキャリブレーションする | |
// NOTE: 4秒、5秒と押し続けた時に何度もゼロキャリブレーションされないよう、 | |
// calibratedAtCurrentPressing でフラグ管理している。 | |
if (!calibratedAtCurrentPressing && M5.BtnB.pressedFor(3000)) { | |
Serial.print("calibrateZero\n"); | |
M5.Beep.tone(2000); | |
delay(500); | |
M5.Beep.mute(); | |
myMHZ19.calibrateZero(); | |
calibratedAtCurrentPressing = true; | |
} | |
if (M5.BtnB.wasReleased()) { | |
calibratedAtCurrentPressing = false; | |
} | |
if (alertLevel >= ALERT_WARNING && now - lastBeepOperationTime >= 5 * 60 * 1000) { | |
lastBeepOperationTime = now; | |
M5.Beep.tone(150); | |
delay(100); | |
M5.Beep.mute(); | |
delay(100); | |
M5.Beep.tone(150); | |
delay(100); | |
M5.Beep.mute(); | |
} | |
if (now - lastMeasureTime >= measureInterval) { | |
/* note: getCO2() default is command "CO2 Unlimited". This returns the correct CO2 reading even | |
if below background CO2 levels or above range (useful to validate sensor). You can use the | |
usual documented command with getCO2(false) */ | |
int CO2 = myMHZ19.getCO2(); // Request CO2 (as ppm) | |
int8_t temp = myMHZ19.getTemperature(false, true); // Request Temperature (as Celsius) | |
Serial.print("CO2 (ppm): "); | |
Serial.print(CO2); | |
Serial.print(", Temperature (C): "); | |
Serial.println(temp); | |
alertLevel = CO2 >= 1500 ? ALERT_CRITICAL : CO2 >= 1200 ? ALERT_WARNING : ALERT_OK; | |
// 測定結果の表示 | |
historyPos = (historyPos + 1) % (sizeof(history) / sizeof(int)); | |
history[historyPos] = CO2; | |
render(); | |
lastMeasureTime = now; | |
} | |
} | |
void render() { | |
// Clear | |
int height = sprite.height(); | |
int width = sprite.width(); | |
sprite.fillRect(0, 0, width, height, BLACK); | |
switch (lcdMode) { | |
case LCD_MODE_DIGIT: | |
sprite.setTextSize(1); | |
sprite.drawString("CO2 [ppm]", 12, 0, 2); | |
sprite.setTextSize(2); | |
sprite.drawRightString((String)history[historyPos], width, 24, 7); | |
break; | |
case LCD_MODE_GRAPH: | |
int len = sizeof(history) / sizeof(int); | |
for (int i = 0; i < len; i++) { | |
auto value = history[(historyPos + 1 + i) % len]; | |
auto cuttedValue = max(0, value - 400); | |
auto y = min(height, (int)(cuttedValue * (height / 1200.0))); | |
auto color = value < 1000 ? green : value < 1200 ? orange : value < 1500 ? red : purple; | |
sprite.drawLine(i, height - y, i, height, color); | |
} | |
break; | |
} | |
sprite.pushSprite(0, 0); // バッファの内容を画面に反映 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://scrapbox.io/mizdra/%E4%BA%8C%E9%85%B8%E5%8C%96%E7%82%AD%E7%B4%A0%E6%BF%83%E5%BA%A6%E6%B8%AC%E5%AE%9A%E6%A9%9F%E3%82%92%E8%87%AA%E4%BD%9C%E3%81%99%E3%82%8B