目的
家を出た後に玄関の鍵かけたっけ・・と心配にならないような仕組みを作る
(ただし、いくつか課題があり、題名にも書いてあるように試作レベル。
具体的な課題については最後のまとめで触れる)
環境
-
実際の設置画像1
※参考にセンサー類の詳細を載せておく
- ENV II Hat
- HC-SR501
作業メモ
※ソースコードは最後にこちらでまとめて紹介します
各種センサー類の設定("環境"の図の①~③)
参考サイト
https://lang-ship.com/blog/work/esp32-deep-sleep/
https://github.com/m5stack/M5-ProductExampleCodes/tree/master/Hat/ENVII_HAT/Arduino/ENVII_HAT
https://qiita.com/uniqode/items/1a4fff1e06c8d9958ef2
- 人感センサーの設定
HC-SR501のデータシートなどを参考にして、
M5StickCのVout/GND/G33(GPIO端子)を人感センサー側と接続。
- 人が来たことを検知(人感センサー)してDeep Sleep → wakeup処理
人が近づくと人感センサーからM5StickCのG33(GPIO端子)へHighが入力される構成。
よって参考サイト を参考に、
G33(GPIO端子)へのHigh入力をトリガーにDeep Sleep → wakeupする処理を追加。
また、ある程度の時間が経ったら、再度Deep Sleepに戻るようにしている。
- wakeup中に磁界センサーの値を取得し、その値から鍵の状態を判断
ENV II HatのサンプルコードとENV II Hatの磁界センサー使い方で参考にしたサイトを参考に、
磁界センサーの値を取得。
[取得した値]が[閾値]より大きい or 小さい
を確認することで鍵のUNLOCK/LOCKを判断する
IFTTT/LINE Notifyとの連携("環境"の図の④、⑤)
参考サイト
https://mimimopu.com/raspberry-pi_line/
https://mag.switch-science.com/2016/09/29/ifttt_line-channel/
-
IFTTTとLINE Notifyを連携
ここは参考サイトの手順通りにやればできるはず。
今回はメッセージとして送信するValueはValue1しか使っていない。 -
M5StickC → IFTTT → LINE Notifyへ鍵状態を通知
鍵の状態に変化があったら(ex.UNLOCK → LOCKなど)、LINEへ鍵状態を通知する仕組み。
こちらのサイトを参考に、M5StickC → IFTTTへトリガーとメッセージ(鍵状態)を送信。
そのトリガーが来ると、先に連携したIFTTT → LINE Notifyへのメッセージ(鍵状態)の送信が発生。
ソースコード
設定しないと動かない箇所(WLAN設定2、IFTTTトリガーなど)があるので注意
#include <M5StickC.h>
#include "SHT3X.h"
#include <Wire.h>
#include "Adafruit_Sensor.h"
#include "bmm150.h"
#include "bmm150_defs.h"
#include <WiFi.h>
/*---------------------------------------------
Global Data
※Globalにする必要がないデータも多数あるが、
変数の説明のため全てGlobalにまとめた
-----------------------------------------------*/
/* ---- 【要設定】環境依存のパラメータ ---- */
// 磁界センサーの閾値、ドア状態(OPEN/CLOSE)判定用
#define THRESHOLD_VALUE 500
// IFTTT接続用のWLAN設定
// !!!!! 設定しないと動かない !!!!!
#define WLAN_SSID "*****"
#define WLAN_PASS "*****"
// ドア状態の変更を反映させるまでの猶予回数
// ex. CHATTERING_NUM 3 -> 3loop連続"OPEN","OPEN","OPEN"となったらドア状態をOPENに変更する
#define CHATTERING_NUM 3
// IFTTTのトリガー設定
// !!!!! 設定しないと動かない !!!!!
char header[100] = "POST /trigger/****/with/key/**** HTTP/1.1\r\n";
// wakeup → 再度Deep Sleepするまでの時間設定
// 最小時間[ms] = DEEPSLEEP_LOOP * LOOP_DELAY
// この時間を出かける際に玄関から出る → 鍵をかける時間を目安に設定
#define DEEPSLEEP_LOOP 50
#define LOOP_DELAY 200
/* ---- ここまで ---- */
/* ---- [3.磁界センサーからUNLOCK/LOCKを判断]用 ---- */
// 磁界センサー利用
BMM150 bmm = BMM150();
// ドアの状態定義
#define OPEN_STATE 0
#define CLOSE_STATE 1
// Deep Sleep時に値保持のためRTC_DATA_ATTRにて変数定義
RTC_DATA_ATTR int pre_mag_state = OPEN_STATE;
RTC_DATA_ATTR int mag_state = OPEN_STATE;
int magval = 0;
/* ---- ここまで ---- */
/* ---- [4.IFTTTへ鍵の状態(UNLOCK/LOCK)を送信]用 ---- */
// IFTTTへのデータ送信
#define IFTTT_MAX_SIZE_STRING 512
const char *door_state[2] = { "UNLOCK", "LOCK" };
char str[IFTTT_MAX_SIZE_STRING] = {0};
const char* host = "maker.ifttt.com";
char contentLen[50] = {0};
const char* contentType = "Content-Type: application/json\r\n\r\n";
char valueData [150] = {0};
WiFiClient client;
int chattering_count = 0;
/* ---- ここまで ---- */
/* ---- [2.Deep Sleep → wakeup]用 ---- */
// Deep Sleep設定
int deepsleep_count = 0;
/* ---- ここまで ---- */
void setup() {
M5.begin();
Wire.begin(0,26);
if(bmm.initialize() == BMM150_E_ID_NOT_CONFORM) {
#if 0
// for debug
Serial.println("Chip ID can not read!");
#endif
while(1);
}
else {
#if 0
// for debug
Serial.println("Initialize done!");
#endif
}
// WiFi Setup
WiFi.begin(WLAN_SSID, WLAN_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
#if 0
// for debug
Serial.println("connection failed");
#endif
return;
}
}
void loop() {
// 磁界データ取得
bmm.read_mag_data();
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)));
// ドアopen/close状態更新
// CLOSE = 閾値より小さい/OPEN = 閾値以上
if( magval < THRESHOLD_VALUE ){
mag_state = CLOSE_STATE;
}
else{
mag_state = OPEN_STATE;
}
// 状態変化がある場合、LINE通知
if( pre_mag_state != mag_state ){
if( chattering_count < CHATTERING_NUM ){
chattering_count++;
#if 0
// for debug
M5.Lcd.setCursor(0, 0, 2);
M5.Lcd.printf("chattering_count: %d", chattering_count);
Serial.print("chattering_count: ");
Serial.println(chattering_count);
#endif
}
else{
sprintf(valueData, "{\"value1\":\"%s\"}\r\n", door_state[mag_state]);
sprintf(contentLen, "Content-Length: %d\r\n", strlen(valueData));
sprintf(str, "%sHost: %s\r\n%s%s%s", header, host, contentLen, contentType, valueData);
client.print(str);
delay(1000);
pre_mag_state = mag_state;
chattering_count = 0;
}
}
// 状態変化がない場合はcountを初期化
else{
chattering_count = 0;
}
// Deep Sleepするまでのloopカウント
if( deepsleep_count < DEEPSLEEP_LOOP ){
deepsleep_count++;
}
// 人を検知するまでDeep Sleep
else{
// GPIO33がHIGHになったら起動
pinMode(GPIO_NUM_33, INPUT_PULLDOWN);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, HIGH);
// #if 0
// for debug
M5.Lcd.printf("Deep Sleep.");
// #endif
deepsleep_count = 0;
// ディープスリープ
esp_deep_sleep_start();
}
delay(LOOP_DELAY);
#if 0
// for debug
M5.Lcd.setCursor(0, 20, 2);
M5.Lcd.printf("magval: %05d", magval);
Serial.print("magval: ");
Serial.println(magval);
M5.Lcd.setCursor(0, 40, 2);
M5.Lcd.printf("mag_state: %s", mag_state);
Serial.print("mag_state: ");
Serial.println(mag_state);
#endif
}
まとめ
目的を達成!(試作という意味で。まだまだ課題は残っている)
家を出た後に玄関の鍵かけたっけ・・と心配にならないような仕組みを作る
課題
- 精度のいいUNLOCK/LOCKの判断方法確立
今回は磁界センサーで判断しているが、磁石やM5StickCの位置がずれると精度が落ちる。
(実際試した際も反応が悪く鍵状態が通知されないことがたまにあった)
そのため、別の方法での検知や磁界センサーの使い方見直し3といった検討が必要。
- システムの長期稼働方法検討
M5StickCはDeep Sleepさせているとはいえ、長期稼働を考えると電源供給をしないといけない。
ちゃんとした運用をするならば、ここの検討は避けられない。