障害が起きたら何に通知する？M5stack編

## はじめに

あなたが管理するサービスに障害が発生したとき、それをどうやって通知していますか？
ほとんどの方は「アプリ・メール・SMS・コール」などスマホが第一候補ではないでしょうか。
**真夜中にスマホが鳴り響いたら起きる**なんて方も多いはずです。
しかし、スマホはさまざまなアプリから~~うっとおしい~~無関係な通知がきますし、専用端末を用意するのは面倒ですよね。

そんなわけで、スマホ以外で通知を受ける方法がなにかないかいろいろ考えてみました。
DIYでガジェットを作る選択肢もありますが、障害通知にそんな労力をかけたくないので、以下の基準で選んでいます。

1. **安価**にできること
2. 誰でも**簡単に再現**できて配布も可能なこと
3. 現実的に**人間が（危害を受けずに）起きれる**こと

そして考えついたのが、「[M5stack Basic](https://amzn.to/31NLQrC)（※Amazonアソシエイト）」を使った通知ガジェットです。
4000円程度となかなか安価！（ファイル入出力を扱いたい場合は別途microSDカードが必要です）

## 実装の概要

実装は以下のように、すごくシンプルです。
M5stack Basic上に簡易的なHTTPサーバをたて、そこに対してWebhookするという仕組みにしました。
通知を受けたM5stackが**指定されたMP3ファイルを再生する**ことで、無事すっきり起きることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184843/0e1e233e-36c2-3de4-8423-f0faca6269eb.png)

## 実装コード

M5stack向けのソースコードは以下のとおりです。

```c:app.ino
#include <M5Stack.h>
#include <WiFi.h>
#include <SD.h>

#include "AudioFileSourceSD.h"
#include "AudioFileSourceID3.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2S.h"

AudioGeneratorMP3 *mp3;
AudioFileSourceSD *file;
AudioOutputI2S *out;
AudioFileSourceID3 *id3;

// WiFiサーバのTCPポートを指定
WiFiServer server(10080);

void setup()
{
    // 初期化してフォントサイズを設定する
    M5.begin();
    M5.Power.begin();
    delay(100);
    M5.Lcd.setTextSize(2);
    M5.Lcd.clear();

    // 自宅WiFiに接続する
    M5.Lcd.print("Connecting to WiFi");
    WiFi.begin("SSID", "PASSWORD");
    while(WiFi.status() != WL_CONNECTED){
        M5.Lcd.print(".");
        delay(500);
    }
    M5.Lcd.println(".");
    M5.Lcd.print("connected: ");
    M5.Lcd.println(WiFi.localIP());

    // SDカードを準備する
    if(!SD.begin()){
        M5.Lcd.println("SD card is not present");
        return;
    }
    if(SD.cardType() == CARD_NONE){
        M5.Lcd.println("Can't read SD card");
        return;
    }

    // TCPサーバを起動する
    server.begin();
}

void loop(){
    WiFiClient client = server.available();
    // クライアント接続がない場合
    if(!client){
        M5.update();
        return;
    }
    String currentLine = "";
    while(client.connected()){
        if(!client.available()){
            continue;
        }
        char c = client.read();
        if (c == '\n') {
            // デフォルトでは403を返す
            if (currentLine.length() == 0) {
                client.println("HTTP/1.1 403 Forbidden");
                client.println("Connection: close");
                client.println();
                break;
            }
            else {
                currentLine = "";
            }
        }
        else if (c != '\r') {
            currentLine += c;
        }
        // 特定のURL（/hogehoge）にアクセスされた場合のみレスポンスして音を鳴らす
        if(currentLine.endsWith("GET /hogehoge")){
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type: application/son");
            client.println("Connection: close");
            client.println();
            client.print("{\"result\": true}");
            client.println();
            // MP3ファイルが存在する場合は鳴らす
            if(SD.exists("/test.mp3")){
                M5.Lcd.println("MP3");
                playMP3("/test.mp3");
            }
            // MP3ファイルが存在しない場合はビープ音を鳴らす
            else{
                playTone();
            }
            break;
        }
    }
    client.stop();
    M5.update();
}

// ファイル名を指定してMP3を再生する
// ※再生完了するクライアント接続が維持されるので注意すること！
void playMP3(char *filename){
  file = new AudioFileSourceSD(filename);
  id3 = new AudioFileSourceID3(file);
  out = new AudioOutputI2S(0, 1);
  out->SetOutputModeMono(true);
  out->SetGain(1.0);
  mp3 = new AudioGeneratorMP3();
  mp3->begin(id3, out);
  while(mp3->isRunning()){
    if (!mp3->loop()) mp3->stop();
  }
}

// ビープ音を鳴らす
void playTone(){
    M5.Speaker.beep();
}
```

## 実装コードのビルド方法

1. PCからデバイスにプログラム転送するため、まず[専用のUSBドライバ](https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers)をインストールしましょう。
2. コードのビルドおよび転送を行うため、「[Arduino IDE](https://www.arduino.cc/en/main/software)」をインストールしましょう。
3. セットアップ後、[公式マニュアルの手順](https://docs.m5stack.com/#/en/arduino/arduino_development?id=m5stack-library)に沿ってM5stack用のライブラリをインストールしましょう。
4. ArduinoIDEに以下の設定を行います。
    - 「ツール - ボードより「M5stack-Core-ESP32」を選択
    - 「ツール - シリアルポート」より「/dev/cu.SLAB_USBtoUART」を選択
5. あとはプログラムを書いてマイコンボードに書き込めば完成です！

Hello Worldまでの手順は以下の記事が参考になりました。
https://qiita.com/sugasaki/items/a763f238aa360e4df414

## おわりに

通知を止める手間や確実性など考えると第一候補としては難しいですよね。
しかし、想像力が広がる選択肢になるんじゃないかと思います。

今回のプログラムは通知を受けて音を鳴らすだけのシンプルな内容でしたが、ぜひ色々試して遊んでみてください。
M5stack Basicには３つのボタンがあるので、たとえば「ボタンAを押したらサーバを強制再起動する~~（たぶん寝ぼけて再起動しちゃう）~~」などのカスタマイズも可能です。
また、「[ObnizOS](https://obniz.io/ja/doc/reference/m5stack/)」を入れることで、JavaScriptでのコーディングもできます。