M5StickC をドアセンサー(&開閉通知)にする 

日曜DIY。家人の外出や帰宅が何となく分かるように、M5StickC + ENV Hatで家のドアセンサーを作った(3000円前後)。
![m5stickc.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/385786/74a7ecee-0d06-21ba-8f73-e3cfffa61ed6.jpeg)

# ハードウェアの調達
  - [M5 StickC](https://www.switch-science.com/catalog/5517/) (2000円前後)
  - [M5 StickC Env Hat](https://www.switch-science.com/catalog/5755/) (500円前後)
  - USB電源 (USBケーブルはStickC同梱のものを使用)
  - 磁石、[ひっつき虫](https://www.kokuyo-st.co.jp/stationery/hittsuki/)

# 開発
## 開発環境
- 色々サイトを参考にしながら試行錯誤したため曖昧だが、、、主には下記
  - ESP32(StickCの内部コア)とUSB-シリアル通信するためのドライバインストール
  - [Arduino IDE](https://www.arduino.cc/en/Main/software)環境を構築
  - ソースで(includeしている)ライブラリのインストール
  - センサ関連コードは[ENV Hatのgithubから](https://github.com/m5stack/M5StickC/tree/master/examples/Hat/ENV)
- 参考サイト
  - https://raspberrypi.mongonta.com/howto-start-m5stack-arduinoide/
  - https://ambidata.io/samples/m5stack/m5sitckc/

## プログラム

```C
#include <M5StickC.h>
#include <Wire.h>
#include "bmm150.h"
#include "bmm150_defs.h"
#include <WiFi.h>
#include <WiFiClientSecure.h>

const char* wifi_ap_ssid = "XXXXXXXX";
const char* wifi_ap_pass = "XXXXXXXX";

String   door_state     = "close";
uint16_t door_thr_close =  1600;
uint16_t door_thr_open  =   800;
uint16_t door_ignore    = 60000;
uint32_t door_lastpost  = -door_ignore;

BMM150 bmm = BMM150();

void setup() {
  Serial.begin(115200);
  Serial.println("start debugging");
  Wire.begin(0,26);
  M5.begin();
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  pinMode(M5_BUTTON_HOME, INPUT);
  initdev();
  M5.Lcd.fillScreen(BLACK);
}

void initdev(){
  M5.Lcd.setCursor(0, 0, 2);
  if(bmm.initialize() == BMM150_E_ID_NOT_CONFORM){ while(1); }
  M5.Lcd.printf("Mag sensor OK.\n");

  WiFi.begin(wifi_ap_ssid, wifi_ap_pass);
  while (WiFi.status() != WL_CONNECTED){ delay(500); }
  IPAddress ip = WiFi.localIP();
  M5.Lcd.printf("Wifi: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
  delay(2000);
}

uint8_t setup_flag = 1;
void loop() {
  bmm.read_mag_data();
  int magval = (int)(sqrt(pow(bmm.raw_mag_data.raw_datax, 2) + pow(bmm.raw_mag_data.raw_datay, 2) + pow(bmm.raw_mag_data.raw_dataz, 2)));
  String state = "-----";
  if (door_thr_close < magval){ state = "close"; }
  if (magval < door_thr_open ){ state = "open"; }

  M5.Lcd.setCursor(0, 0, 2);
  M5.Lcd.printf("%06d [%s]  \n", magval, state);
  if ((state != "-----") && ((millis()-door_lastpost) > door_ignore) && (door_state != state)){
    door_state = state;
    door_lastpost = millis();
    String r = https_Web_Get("example.com", "/update?1=door&2=" + state + "&3=" + String(magval));
    M5.Lcd.printf("send: %s \n", state);
    M5.Lcd.println(r);
  }
  delay(200);

  if(!setup_flag){
    setup_flag = 1;
    initdev();
  }
  if(digitalRead(M5_BUTTON_HOME) == LOW){
    setup_flag = 0;
    while(digitalRead(M5_BUTTON_HOME) == LOW);
  }
  return;
}

String https_Web_Get(const char* host, String target_page){
  String res;
  WiFiClientSecure https_client;
  if (https_client.connect(host, 443)){
    String s = "GET https://" + String(host) + target_page + " HTTP/1.1\r\nHost: " + String(host) + "\r\n";
    s += "User-Agent: ESP32\r\nConnection: close\r\n\r\n\0";
    https_client.print(s);
    https_client.flush();
  }
  if(https_client){
    while(https_client.connected()){
      String r = https_client.readStringUntil('\n');
      if(0 <= r.indexOf("HTTP")){res = r;}
      Serial.println(r);
      if (r == "\r") break;
    }
    while (https_client.available()){ https_client.read(); }
    https_client.stop();
  }
  return res;
}
```

- 上記では磁力の閾値は800以下でclose、1600以上でopenとしてある。
- チャタリング防止のため、状態変化通知後60秒間は次の通知を行わないようにしている。
- 通知先のAPIは当初IFTTTからのEmailとしていたが、通知が遅いことがあったため、自作+Slackにした。
- 通信はhttpsだが、ライブラリではサーバ証明書の検証まではしていないようなので、その点留意。
- 機能加除、各種デバッグ行いたい場合は、SerialかDisplayにて適宜printfデバッグ。
- ENV HATは気温/湿度/気圧のデータも取れるほか、StickC本体にはマイクもあるため、ドアが開いたら録音、など様々な拡張の妄想が広がります。StickVだとさらに。

# 設置と調整
- ドア側にマグネットを付ける
- 閾値を環境に合わせて調整
- 当初USB電源に安価なものを使ったら、過電流で熱くなり液晶がダメになって1台つぶしてしまった。USB電源接続後は3分ほど本体温度や見た目に異状ないか要確認。