misoのアイコン画像
miso 2024年06月22日作成 (2024年06月23日更新) © MIT
製作品 製作品 閲覧数 154
miso 2024年06月22日作成 (2024年06月23日更新) © MIT 製作品 製作品 閲覧数 154

M5AtomS3とENV4センサーで作る、パーソナル環境モニター

M5AtomS3とENV4センサーで作る、パーソナル環境モニター

今回は、コンパクトで高性能なM5AtomS3と精密なENV4センサーを使って、自作の環境モニターを作る方法をご紹介します。温度、湿度、気圧を測定し、視覚的に分かりやすく表示する、実用的なガジェットを一緒に作りましょう。

プロジェクトの始まり

このプロジェクトは、ある暑い夏の日の経験から生まれました。室内環境をより快適に保つために、正確な環境データが必要だと感じたのがきっかけです。

「環境をリアルタイムでモニターして、最適な状態を維持できれば...」

そんな思いから、M5AtomS3とENV4センサーを使ったプロジェクトを始めることにしました。

必要な機材とソフトウェア

  • M5AtomS3
  • ENV4センサー
  • Arduino IDE 2.3.2
  • 必要なライブラリ(M5AtomS3, M5UnitENV)

プログラムの

Arduino IDEを使って、以下の機能を持つプログラムを作成します。

#include "M5AtomS3.h"
#include "M5UnitENV.h"
#include <SPI.h>

SHT4X sht4;
BMP280 bmp;

#define GRAPH_HEIGHT 85
#define GRAPH_WIDTH 128
#define GRAPH_LEFT 25
#define TEMP_MIN 0
#define TEMP_MAX 40

const uint32_t BACKGROUND_COLOR = M5.Lcd.color565(10, 20, 30);  // Dark blue-gray
const uint32_t TEXT_COLOR = M5.Lcd.color565(200, 200, 200);  // Light gray
const uint32_t GRID_COLOR = M5.Lcd.color565(40, 60, 80);  // Lighter blue-gray
const uint32_t TEMP_COLOR = M5.Lcd.color565(255, 100, 100);  // Soft red

void setup() {
    M5.begin();  // Initialize AtomS3
    Serial.begin(115200);

    if (!sht4.begin(&Wire, SHT40_I2C_ADDR_44, 2, 1, 400000U)) {
        Serial.println("Couldn't find SHT4x");
        while (1) delay(1);
    }

    sht4.setPrecision(SHT4X_HIGH_PRECISION);
    sht4.setHeater(SHT4X_NO_HEATER);

    if (!bmp.begin(&Wire, BMP280_I2C_ADDR, 2, 1, 400000U)) {
        Serial.println("Couldn't find BMP280");
        while (1) delay(1);
    }
    
    bmp.setSampling(BMP280::MODE_NORMAL,
                    BMP280::SAMPLING_X2,
                    BMP280::SAMPLING_X16,
                    BMP280::FILTER_X16,
                    BMP280::STANDBY_MS_500);

    drawBackground();
}

void drawBackground() {
    M5.Lcd.fillScreen(BACKGROUND_COLOR);
    
    // Draw vertical grid lines
    for (int x = GRAPH_LEFT; x < GRAPH_WIDTH; x += 25) {
        M5.Lcd.drawLine(x, 0, x, GRAPH_HEIGHT, GRID_COLOR);
    }
    
    // Draw horizontal grid lines and labels
    M5.Lcd.setTextSize(1);
    M5.Lcd.setTextColor(TEXT_COLOR);
    for (int i = 0; i <= 4; i++) {
        int y = map(i * 10, TEMP_MIN, TEMP_MAX, GRAPH_HEIGHT - 5, 5);
        M5.Lcd.drawLine(GRAPH_LEFT, y, GRAPH_WIDTH, y, GRID_COLOR);
        M5.Lcd.setCursor(2, y - 3);
        M5.Lcd.printf("%2d", i * 10);
    }
}

void drawGraph(float temperature) {
    static int x = GRAPH_LEFT;
    static int lastTempY = -1;
    
    int tempY = map(temperature, TEMP_MIN, TEMP_MAX, GRAPH_HEIGHT - 5, 5);
    
    if (x == GRAPH_LEFT) {
        drawBackground();
        lastTempY = tempY;
    }
    
    if (lastTempY != -1) {
        M5.Lcd.drawLine(x-1, lastTempY, x, tempY, TEMP_COLOR);
    }
    
    lastTempY = tempY;
    
    x++;
    if (x >= GRAPH_WIDTH) x = GRAPH_LEFT;
}

float calculateDiscomfortIndex(float temperature, float humidity) {
    return 0.81 * temperature + 0.01 * humidity * (0.99 * temperature - 14.3) + 46.3;
}

void drawDiscomfortIcon(float di, int x, int y) {
    uint32_t color;
    if (di < 70) color = M5.Lcd.color565(0, 255, 0);       // Green (Comfortable)
    else if (di < 75) color = M5.Lcd.color565(255, 255, 0); // Yellow (Slightly uncomfortable)
    else if (di < 80) color = M5.Lcd.color565(255, 128, 0); // Orange (Uncomfortable)
    else color = M5.Lcd.color565(255, 0, 0);               // Red (Very uncomfortable)

    // Draw face outline
    M5.Lcd.drawRect(x, y, 16, 16, color);
    
    // Draw eyes
    M5.Lcd.drawPixel(x+4, y+4, color);
    M5.Lcd.drawPixel(x+11, y+4, color);

    // Draw mouth based on discomfort index
    if (di < 70) {
        // Very happy
        M5.Lcd.drawLine(x+4, y+11, x+11, y+11, color);
        M5.Lcd.drawPixel(x+3, y+10, color);
        M5.Lcd.drawPixel(x+12, y+10, color);
        M5.Lcd.drawPixel(x+4, y+12, color);
        M5.Lcd.drawPixel(x+11, y+12, color);
    } else if (di < 75) {
        // Happy
        M5.Lcd.drawLine(x+4, y+11, x+11, y+11, color);
        M5.Lcd.drawPixel(x+3, y+10, color);
        M5.Lcd.drawPixel(x+12, y+10, color);
    } else if (di < 80) {
        // Neutral
        M5.Lcd.drawLine(x+4, y+11, x+11, y+11, color);
    } else if (di < 85) {
        // Unhappy
        M5.Lcd.drawLine(x+4, y+12, x+11, y+12, color);
        M5.Lcd.drawPixel(x+3, y+13, color);
        M5.Lcd.drawPixel(x+12, y+13, color);
    } else {
        // Very unhappy
        M5.Lcd.drawLine(x+4, y+13, x+11, y+13, color);
        M5.Lcd.drawPixel(x+3, y+12, color);
        M5.Lcd.drawPixel(x+12, y+12, color);
        M5.Lcd.drawPixel(x+4, y+14, color);
        M5.Lcd.drawPixel(x+11, y+14, color);
    }

    // Display discomfort index value
    M5.Lcd.setTextSize(1);
    M5.Lcd.setTextColor(color);
    M5.Lcd.setCursor(x, y - 10);
    M5.Lcd.printf("%.0f", di);
}

void displayValues(float temperature, float humidity, float pressure) {
    M5.Lcd.fillRect(0, GRAPH_HEIGHT, GRAPH_WIDTH, 128 - GRAPH_HEIGHT, BACKGROUND_COLOR);
    M5.Lcd.setTextSize(1);
    M5.Lcd.setTextColor(TEXT_COLOR);
    
    M5.Lcd.setCursor(2, GRAPH_HEIGHT + 5);
    M5.Lcd.printf("Temp: %.1f C", temperature);
    
    M5.Lcd.setCursor(2, GRAPH_HEIGHT + 20);
    M5.Lcd.printf("Humi: %.1f %%", humidity);
    
    M5.Lcd.setCursor(2, GRAPH_HEIGHT + 35);
    M5.Lcd.printf("Pres: %.1f hPa", pressure);

    float di = calculateDiscomfortIndex(temperature, humidity);
    drawDiscomfortIcon(di, 100, GRAPH_HEIGHT + 15);
}

void loop() {
    M5.update();  // Update button state

    float temperature = 0;
    float humidity = 0;
    float pressure = 0;

    if (sht4.update()) {
        temperature = sht4.cTemp;
        humidity = sht4.humidity;
        
        drawGraph(temperature);
    }

    if (bmp.update()) {
        pressure = bmp.pressure / 100.0;  // Convert Pa to hPa
    }

    displayValues(temperature, humidity, pressure);

    // Output data for serial plotter with custom labels
    Serial.print("Temp:");
    Serial.print(temperature);
    Serial.print(",");
    Serial.print("Humi:");
    Serial.print(humidity);
    Serial.print(",");
    Serial.print("Pres:");
    Serial.println(pressure);

    delay(1000);
}

グラフ表示機能

温度の変化を視覚的に捉えるため、リアルタイムでグラフを描画する機能を実装しました。

void drawGraph(float temperature) {
    // ... (drawGraph関数の中身)
}

この機能により、温度変化のトレンドが一目で分かります。

不快指数の視覚化

数値だけでなく、直感的に理解できるよう、不快指数を表情アイコンで表現しました。

void drawDiscomfortIcon(float di, int x, int y) {
    // ... (drawDiscomfortIcon関数の中身)
}

これにより、環境の快適さが一目で判断できます。

データ分析のためのシリアル出力

Serial.print("Temp:");
Serial.print(temperature);
// ... (以下、シリアル出力の部分)

このコードで、Arduino上でデータをシリアルプロッタを使って可視化し、直感的な分析が可能になります。

キャプションを入力できます

実際の動作

キャプションを入力できます

  1. M5AtomS3の画面に温度のグラフがリアルタイムで描画されます。
  2. 画面下部に現在の温度、湿度、気圧が表示されます。
  3. 右下に不快指数を表すアイコンが表示されます。
  4. PCに接続すると、詳細なデータがグラフで確認できます。

デスクトップアプリで温度等を表示できるようにしてみた

シリアルからの内容を表示してくれるデスクトップアプリも、作例として作ってみた。
マウスオーバーすると詳細表示されるにしてみた、不快指数も表示してくれる。

キャプションを入力できます

いちおう右クリックするとメニューが表示されて、自動接続設定や自動スタートアップを設定できるように作り込んであったりする。

キャプションを入力できます

まとめ

M5AtomS3とENV4センサー、そして適切なプログラミングにより、私たちの生活をより快適にするツールを作ることができました。

このプロジェクトを通じて、IoTデバイスの可能性と、身の回りの環境をより良く理解することの重要性を実感しました。

皆さんも、自分専用の環境モニターを作ってみませんか?きっと、新しい気づきや発見があるはずです。

さあ、あなたのIoTプロジェクト、始めましょう!

misoのアイコン画像
寝るのが趣味
ログインしてコメントを投稿する