虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

M5StickCで室温を監視しSlackへ通知する

f:id:toranoana-lab:20210922120818p:plain こんにちは。インフラ担当のサカガミです。

今回は室温を監視して Slack へ通知するシステムを作った話です。

きっかけ

弊社のシステムは基本的にクラウドかデータセンター内にあるオンプレミスのサーバを利用していますが、 一部のサーバは業務上の理由により事業所内に設置されています。 サーバが設置されている区画は常時エアコンにより冷却され、室温が25度程度に保たれています。

しかしながら、先日エアコンが不調により停止し、室温が28度まで上昇するという問題が発生しました。 当日は偶然私が出社していたため即時対応し事なきを得ましたが、 普段はリモートワークのためエアコンが故障しても気付くことができません。 遠隔地からでも室温を監視できるシステムの必要性を感じました。

室温を監視するにあたり通知機能のある温度計を探しましたが、 単体のスマホと連動させるタイプのものが多く 複数人のチームへ通知できるものを見つけることができませんでした。 また、普段サーバなどのアラート通知にも使っている Slack で確認できれば便利です。

そこで M5StickC と環境センサユニットを使用した 室温監視システムを構築しましたので紹介致します。

用意するもの

  1. M5StickC
  2. M5Stack 用環境センサユニット ver.2 (ENV II)
  3. USB Type-C ケーブルと充電器
  4. プログラムを書き込むための PC (今回は Windows 10 の PC を使用)

今回 ENV II ユニットを使用しましたが、後継の ENV III が発売され ENV II は販売終了 (EOL) となったようです。 ENV III でも温度センサーは同じ SHT30 のため同じように動作するかと思います。

M5StickC とは?

M5StickC とは、 M5Stack 社が販売しているマイコンモジュール M5Stack シリーズのひとつです。 m5stack.com

親指サイズの小さな筐体に Espressif 社の ESP32-PICO を搭載し、 CPU は最大 240 MHz、メモリは 520 KB となっています。 また、Wi-Fi や Bluetooth での無線接続機能を持っており、 Grove 端子による拡張性もあります。

Raspberry Pi のように高性能ではありませんが、 センサーで得た値を送信するのには十分です。

ハードウェアの構成

ハードウェアは以下のように M5StickC と環境センサユニットの Grove 端子を付属のケーブルで接続するだけです。

Grove 端子はマイコンとセンサーなどを4ピンのケーブルで接続する規格です。 はんだ付けもブレッドボードも不要でいろいろなセンサーを接続できるため、 私のような電子工作初心者でも簡単に扱うことができます。

M5StickC の開発環境

M5StickC ではプログラムの作成に C++, MicroPython, UIFlow が利用できます。 MicroPython はマイコン向けの Python、 UIFlow は GUI でブロックを組み合わせてプログラムが作成できるノーコード環境です。

今回は C++ を使用しました。 理由としては私が C++ を書けること、 M5StickC で検索すると出てくる情報が主に C++ であり サンプルプログラムが豊富なことです。 (これ大事)

C++ での開発には Arduino IDE を使用します。 Visual Studio Code と Microsoft 製の拡張機能 Arduino を使うと開発が楽になりますが、私の環境ではなぜかシリアルモニタが正常に動作しないため VSCode でプログラムを書いて Arduino IDE で M5StickC へ書き込むかたちをとっています。

ドライバーと Arduino IDE のインストール

まず初めに PC へドライバーと Arduino IDE をインストールします。

公式の m5-docs を参照するか、 以前私が書いた M5Stack ATOM Lite 用の開発環境構築 の ATOM Lite を M5StickC に読み替えてください。

スケッチ (プログラム) の作成

Arduino ではプログラムのことをスケッチと呼びます。 今回のスケッチでは以下の機能を実装しました。

  • 10秒ごとに温度を測定し、閾値を超えたら Slack へアラートメッセージを送信
  • 1時間に1回温度を記録し、1日1回レポートを Slack へ送信
  • 停電等によりバッテリーでの稼働に切り替わった場合 Slack へメッセージを送信

初期設定

Arduino のプログラムでは起動時に setup() が呼び出され そこで初期設定を行ったあと loop() が無限ループするかたちとなっています。

今回のプログラムの setup() は以下のとおりです。

void setup() {
    M5.begin();
    M5.Lcd.setRotation(1);
    Wire.begin(32, 33);

    // 画面の輝度を下げる
    M5.Axp.ScreenBreath(8);
    // CPUの動作周波数を80MHzに設定 (80MHz未満ではWi-Fi使用不可)
    setCpuFrequencyMhz(80);

    delay(100);

    // 無線LANへ接続
    connectWiFi(WIFI_SSID, WIFI_PASSPHRASE);
    // NTPの設定
    configTime(JST, 0, NTP_SERVER);

    // 温度を取得
    getTemp();

    // 起動メッセージを送信
    std::ostringstream message;
    message << "起動しました。アラートの閾値は"
            << std::fixed << std::setprecision(2) << TEMP_THRESHOLD
            << "度、現在の温度は"
            << std::fixed << std::setprecision(2) << temp
            << "度です。";
    postMessage(message.str().c_str());
}

M5StickC の Grove 端子は G32, G33 ですので Wire.begin(32, 33); で設定しています。

画面の輝度や CPU の周波数設定は消費電力を下げるためのものです。 常時稼働する予定のため無駄に電力を消費しないようにしています。

また、無線 LAN への接続や NTP での時刻合わせの設定なども行っています。

最後の postMessage() は Slack へメッセージを送信する関数です。 こちらは後ほど説明します。

無限ループ部分

// 温度を測定する間隔(秒)
const uint32_t INTERVAL = 10;

void loop() {
    (中略)

    // 1日1回温度を送信
    sendDailyMessage();
    // 現在の温度が閾値を超えていればメッセージを送信
    sendAlertMessage();
    // バッテリーの状態を送信
    sendBatteryMessage();

    delay(INTERVAL * 1000);
}

loop() では温度の送信を行う関数などを呼び出したあと delay() で10秒の待ち時間を入れています。

温度の取得

温度の取得は M5Stack 公式のスケッチ例 どおり SHT3X を使用しています。

// デジタル温湿度センサーSHT30
SHT3X sht30;
// 温度
float temp = 0.0;

// 温度を取得
void getTemp() {
    if (sht30.get() == 0) {
        temp = sht30.cTemp;
        Serial.printf("Temperature: %2.2f*C\r\n", temp);
    }
    else {
        Serial.println("sht30.get() failed");
    }
}

Slack へのメッセージ投稿

Slack へのメッセージの投稿は Incoming Webhook を使用します。 api.slack.com

サーバへ HTTPS で接続する必要があるため Espressif 公式のスケッチ例 を参考に WiFiClientSecure を使用し接続しています。

Espressif のスケッチ例では証明書をソースコードに埋め込んでありますが、 今回のプログラムでは証明書を検証せずに接続しています。 その際、ボードマネージャで表示される M5Stack のバージョンが 1.0.8 の場合は client.setInsecure() を呼び出す必要があります。

1.0.9 の場合は不要です。1.0.9 では WiFiClientSecure に setInsecure() が存在しないため、呼び出そうとするとコンパイルエラーとなります。

サーバに接続できたら HTTP リクエストのヘッダーと本文の JSON を作成し送信します。

void postMessage(const char* message) {
    WiFiClientSecure client;

    // HTTPS接続時に証明書による検証を行わない。
    // ボードマネージャで表示されるM5Stackのバージョンが1.0.8の場合は
    // client.setInsecure()を呼び出す必要がある。
    // 1.0.8の場合は以下のコメントをはずすこと。
    // 1.0.9の場合は不要。(setInsecureが存在しないためコンパイルエラーとなる)
    // client.setInsecure();

    Serial.println("\nStarting connection to server...");
    if (!client.connect(WEBHOOK_HOST, WEBHOOK_PORT)) {
        Serial.println("Connection failed!");
    }
    else {
        Serial.println("Connected to server!");

        // リクエストを作成
        std::ostringstream payload, request;
        payload << "payload={\"text\": \"" << message << "\"}";
        request << "POST " << WEBHOOK_PATH << " HTTP/1.1\r\n"
                << "Host: " << WEBHOOK_HOST << "\r\n"
                << "User-Agent: WiFiClientSecure\r\n"
                << "Content-Type: application/x-www-form-urlencoded\r\n"
                << "Content-Length: " << payload.str().length() << "\r\n\r\n"
                << payload.str();

        Serial.println(request.str().c_str());

        // リクエストを送信
        client.print(request.str().c_str());
        client.println();

        while (client.connected()) {
            String line = client.readStringUntil('\n');
            if (line == "\r") {
                Serial.println("headers received");
                break;
            }
        }

        client.stop();
    }
}

バッテリーでの稼働に切り替わった場合の検出

M5StickC は 80 mAh のバッテリーを搭載しており 電源に接続されていなくてもしばらくは動きます。 せっかく設置するので停電時に通報する機能もつけておきます。

M5.Axp.GetBatCurrent() でバッテリーの充放電電流を取得できます。 充電時はプラス、放電時はマイナスですので、 マイナスの場合はアラートメッセージを Slack で送信します。

当然ですが、通信経路となる Wi-Fi のアクセスポイントやネットワークスイッチも UPS 等で電源が供給されていないと停電時にメッセージが送れません。

void sendBatteryMessage() {
    // バッテリー稼働での警告を送信済みならtrue
    static bool battery_sent = false;

    // バッテリーの充放電電流を取得
    float bat_current = M5.Axp.GetBatCurrent();

    if (bat_current < 0) {
        if (!battery_sent) {
            postMessage("<!here> バッテリーでの稼働に切り替わりました。電源を確認してください。");
            battery_sent = true;
        }
    }
    else {
        if (battery_sent) {
            postMessage("電源に接続されました。");
            battery_sent = false;
        }
    }
}

ソースコード

全体のソースコードは GitHub で公開しています。 github.com

動作確認

実際に設置し動作を確認します。 設置場所はサーバの排熱やエアコンの冷気が当たらない、 近くの作業台の上にしました。

f:id:toranoana-lab:20210910125247p:plain

起動時の Slack 通知です。 今回閾値は27度に設定しています。

f:id:toranoana-lab:20210910125317p:plain

バッテリー稼働時と室温上昇時の通知です。 テストのため電源からはずして外へ持ち出しました。夏場なので外は暑いです。 閾値を超えたところでアラートが送信されました。

f:id:toranoana-lab:20210910125344p:plain

1日1回の定期レポートも想定通り動作しています。

あとがき

以上のように3,500円程度の出費で室温をモニタリングできるようになりました。 リモートワーク中でも Slack で状態を知ることができるのは便利です。

今回は行いませんでしたが、 定期レポートは視覚的に分かりづらいため Ambient – IoTデータ可視化サービス や社内サーバなどにデータを投げてグラフ化するのもいいかもしれません。

今後も IoT を活用し課題を解決していきたいと思います。

P.S.

採用情報

■募集職種
yumenosora.co.jp

インフラエンジニアの採用はこちら yumenosora.co.jp

カジュアル面談も随時開催中です

■お申し込みはこちら!
news.toranoana.jp

■ToraLab.fmスタートしました!

メンバーによるPodcastを配信中!
是非スキマ時間に聞いて頂けると嬉しいです。
anchor.fm

■Twitterもフォローしてくださいね!

ツイッターでも随時情報発信をしています
twitter.com