スタックチャン(m5stack basic)に CO2 センサーをつないで、数値を計測。Ambient(IoTデータの可視化サービス)に値を送信して、簡単に推移を可視化してみた
ファームウェアには platform io を用いた。使用言語は C++。コードはほとんど ChatGPT とかいた
環境
- m5stack basic
- platformio: 6.11.1
- 依存ライブラリ
sensirion/Sensirion I2C SCD4x @ ^0.4.0
m5stack/M5Unified@^0.1.9
meganetaaan/M5Stack-Avatar@^0.9.0
ambientdatainc/Ambient ESP32 ESP8266 lib@^1.0.3
- 使用したCO2センサー: センシリオン社製の SCD40センサ
センサーから値を取得、送信
ほぼサンプルコードまま
- setup
wifi への接続と色々初期化。ambientの定数周りはサイトから取得しておく
#include <M5Unified.h>
#include <Ambient.h>
#include <Avatar.h>
#include <SensirionI2CScd4x.h>
SensirionI2CScd4x scd4x;
// wifi
const char* SSID = "xxxx";
const char* PASSWORD = "xxxx";
// for ambient
WiFiClient client;
Ambient ambient;
const unsigned channelId = 0;
const char* writeKey = "0xxx";
void co2_setup() {
WiFi.begin(SSID, PASSWORD);
unsigned long startTime = millis();
while (WiFi.status() != WL_CONNECTED) {
delay(100);
if (millis() - startTime > 10000) {
Serial.println("wifi not connected");
break;
}
}
uint16_t error;
char errorMessage[256];
ambient.begin(channelId, writeKey, &client);
scd4x.begin(Wire);
// stop potentially previously started measurement
error = scd4x.stopPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
}
// Start Measurement
error = scd4x.startPeriodicMeasurement();
if (error) {
Serial.print("Error trying to execute startPeriodicMeasurement(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
}
Serial.println("Waiting for first measurement... (5 sec)");
}
- loop
ambient の一日あたりのデータ登録数の上限が 3000 なので適当に間隔を開けて送信している
// 送信間隔
const int SEND_PEDIOD = 300;
void co2_loop() {
M5.update();
static unsigned long lastSendTime = 0;
unsigned long currentTime = millis();
uint16_t error;
char errorMessage[256];
// Read Measurement
uint16_t co2 = 0;
float temperature = 0.0f;
float humidity = 0.0f;
bool isDataReady = false;
error = scd4x.getDataReadyFlag(isDataReady);
if (error) {
M5.Lcd.print("Error trying to execute readMeasurement(): ");
errorToString(error, errorMessage, 256);
Serial.println(errorMessage);
return;
}
if (!isDataReady) {
return;
}
error = scd4x.readMeasurement(co2, temperature, humidity);
// ここに描画処理をかいている
// ...
if (currentTime - lastSendTime > SEND_PEDIOD) {
lastSendTime = currentTime;
ambient.set(1, temperature);
ambient.set(2, humidity);
ambient.set(3, co2);
ambient.send();
Serial.println("send to ambient");
};
}
可視化結果はこんな感じ。ダッシュボードの設定も ambient 上で簡単に設定できた
取得した値に応じて表情を変える
せっかくスタックチャンなので外気の状態によって表情を変化させてみた
co2 もしくは温度と湿度によって表情を変える
enum State {
HOT,
COLD,
NORMAL
};
// Define a function to determine the state based on temperature and humidity
State determineState(float temperature, float humidity) {
if (temperature > 25 && humidity > 60) {
return HOT;
} else if (temperature < 15) {
return COLD;
} else {
return NORMAL;
}
}
State currentState = determineState(temperature, humidity);
if (co2 < 1000) {
switch (currentState) {
case HOT:
avatar.setExpression(Expression::Angry);
break;
case COLD:
avatar.setExpression(Expression::Sad);
break;
case NORMAL:
avatar.setExpression(Expression::Happy);
break;
}
} else {
avatar.setExpression(Expression::Sleepy);
}
// display 表示
char c[100];
sprintf(c, "co2:%d\n temp:%.2f\n RH:%.0f", co2, temperature, humidity);
avatar.setSpeechText(c);
画面にはこんな感じで表示される。CO2 は 1000 ppm を上回ると眠気を感じるらしい
所感
関連のライブラリが充実しており、思った以上に簡単なコードで実現できた。用語周り(ファームウェア、I2Cなど)に馴染みがないため環境構築などには少し苦労した
せっかく躯体があるのに活かせてないため、動きの要素も追加したいと思った
参考
- m5stack のコード構造の基本
https://nouka-it.hatenablog.com/entry/2022/02/27/133549
- CO2センサー
https://massa4649.com/m5stack_co2unit/
- ambientの使い方
https://www.giardino-familiare.com/2021/08/08/m5stickcでガーデニングの水やりセンサを作る-その3-【/
- ambientのコードサンプル
- スタックチャンや環境構築のメモ