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


日曜DIY。家人の外出や帰宅が何となく分かるように、M5StickC + ENV Hatで家のドアセンサーを作った(3000円前後)。

ハードウェアの調達

開発

開発環境

プログラム

#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分ほど本体温度や見た目に異状ないか要確認。