PikaPikaLight

主に光モノ工作の忘備録

TWELITE CUEとM5Atomで玄関のカギの開閉状態監視システムを作った

初めに

外出先で「玄関のカギかけたっけ?」となる心配性な私。鍵の状態をスマホでチェックすることができたら超便利だと思う。
Qrio みたいなスマートロックを付けるという方法もあるが、開け閉めまでできなくてもいい(なんか不安だし。そんな私は心配性)
単純にカギかかっているかだけを確認できる仕組みが欲しい。
今年のゴールデンウィークもどこにも行けそうになくてヒマそうなので、自作してみた。

設計図

うちの玄関ドアのカギは2か所についているので2つモニターする必要あり。 f:id:PikaPikaLight:20210506074431p:plain

開発環境、アプリバージョン

windows10
ArduinoIDE v1.8
TWE Programmer v3.7.5
App_Wings_MONOSTICK_BLUE_L1304_V1-1-4.bin

TWELITEについて

モノをつなぐ無線マイコンモジュール TWELITE-トワイライト - MONO-WIRELESS.COM
TWELITEはIEEE802.15.4(2.4GHz)規格の無線マイコンで、手軽に複数の子機の無線網を構築できるのが特徴です。
自分的には5年ほど前、まだモノワイヤレス株式会社に分社化される前の東京コスモス電機の時代にTWELITEをいくつか使用したことがありました。その時はTWELITE 2525(ニコニコ)という、コイン電池で動いて加速度が取れるモジュールを3Dプリンタのフィラメントロールにつけて、3Dプリンタが途中で止まっていないかをロールの回転による加速度変化でモニターして、止まったらアラームを出すといったことをしたりしてました。

今回、鍵のモニターも2525に磁気センサつければできるなと漠然と考えていて、久しぶりにTWELITEのホームページを見たところ、すごく進化していて、新製品がいっぱいでした。
そして2525はすでに新規採用非推奨品となっていたのですが、なんと後継機TWELITE CUEは磁気センサを搭載しているではないですか。モノワイヤレスさん素晴らしいです。
加速度、磁石センサータグ TWELITE CUE-トワイライトキュー | MONO-WIRELESS.COM

材料

製品名 価格 用途
M5Atom Lite 1287円 Wi-Fiでインターネット接続
TWELITE CUE(Blue) 2481円 x2台 無線子機(磁気センサ)
TWELITE DIP(Blue) 1980円 無線親機
TWELITE R2 1500円 TWELITEの設定、アプリ変更に必要
  • 価格は2021/5/5現在のものです。

  • TWELITEには標準出力のBLUEと高出力タイプのREDがあります。値段的にREDのほうが1.5倍くらい高いです。

  • 自分はTWELITE DIP(旧製品の基板アンテナ型)とTWELITE R(2でなく旧型)とM5Atomを持っていたので、今回購入したのはTWELITE CUE2台だけです。

その他必要なもの

  • コイン型電池(CR2032) 2個:TWELITE CUE用
  • ブレッドボード:TWELITE DIPとM5Atom の接続用
  • マジックテープ:TWELITE CUEをドアに貼り付ける用

作り方

TWELITE CUEの設定

設定変更はインタラクティブモードで行います。 TWELITE CUEにTWELITE R2を接続し、TWELITE STAGE APPを使えばできるようです。その辺はホームページに詳しく記載されているので省略します。
自分の場合は旧製品であるTWELITE Rを使ったために、すんなりはいきませんでしたが、ちゃんと旧製品も使えるように詳細情報が記載されていました。モノワイヤレスさんのこういうところは信頼できてよいです。
USBアダプター TWELITE R-トワイライター - MONO-WIRELESS.COM
TWELITEプログラマというソフトを使うことで、TWELITE RでもCUEとの接続ができました。

肝心の設定内容ですが、変更するのは少なくとも4か所です。 f:id:PikaPikaLight:20210505193613p:plain
これが初期設定値ですが、変更するのは

a アプリケーションID
このIDが同じ端末同士で通信を行います。
初期値のままでもいいですが、もしもご近所でTWELITEを使っている人がいた場合、お互いに迷惑かけることになるので違う番号に変更したほうがいいでしょう。(もし近所で使っている人がいればお友達になりたいですが)
使える番号には制約があります。詳細はこちら
TWELITE APPS - インタラクティブモード - MONO-WIRELESS.COM

i 論理デバイスID
子機につけるナンバーです。同じセンサーの子機が複数あってもこの番号で個体識別できます。
今回は2台の子機を使うので、に設定しておきます。

t 送信間隔の設定
定期送信の送信間隔を秒単位で設定します。1〜4095の値で指定可能。
初期値は5なので5秒おきに通信されます。これはあくまで定期送信の話で、開閉センサーパルモードの場合、磁気センサの値に変化があった場合にも送信してくれます。なので定期送信は別になくてもよく、最大の4095秒(68分)でもいいのですが、 送信受信で取りこぼしが出る可能性を考えると60秒くらいが適当かと思います。(心配性アゲイン)

p センサ固有パラメータの設定
これは今回、開閉センサーパルモードを使うので、04000000を設定します。

設定変更後はコマンド Sで忘れずに保存しましょう。

TWELITE CUEの設置

f:id:PikaPikaLight:20210506075646p:plain

TWELITE DIPの設定

TWELITE DIPはまず親機用のアプリを書き込む必要があります。
書き込むのは親機・中継機アプリ(App_Wings)です。ホームページではMONOSTICK 用とありますが、TWELITE DIPに書き込んでも特に問題なく動きました。
TWELITE R2とTWELITE STAGE APPを使えば多分簡単にできることでしょう。

アプリを書き込んだらインタラクティブモードで設定変更をします
f:id:PikaPikaLight:20210505180033p:plain
これが初期設定値です。
変更するのは a アプリケーションIDだけです。CUEに設定した番号と同じものに変更します。

TWELITE DIPとM5AtomLiteの配線

Groveコネクタ使って配線できればよいのですが、TWELITEの電源は3.3VなのでGroveの5V電源は使えません。
M5Atomの背面のソケットピンを使うことになりますが、ブレッドボードを使うのが簡単です。(ワイヤーで結線してももちろんかまいません)
接続はわずか3ピンでOK。電源(3.3V)とGNDとTWELITEのUART TX→M5AtomのUART RXです。
f:id:PikaPikaLight:20210505204234p:plain f:id:PikaPikaLight:20210505204417p:plain

M5AtomLiteのプログラム

電文のデコード

まず受信電文を確認するために、単純に受信した値をそのままprintさせてみます。

#include "M5Atom.h"
void setup()
{
  M5.begin(true, false, true);
  Serial.begin(115200);
  Serial2.begin(115200, SERIAL_8N1, 21, 25);// RX=21 TX=25(TXは使わないので適当)
}
void loop()
{
  char c;
  if (Serial2.available() > 0 ) {
    c = Serial2.read();
    Serial.print(c);
  }
}

CUEに磁石をつけたり離したりして通信が来るのを確認します。
こんな電文が確認できました。

:800000004E0006810BFF090180810311300802099C1130010204A900000001026D42
:80000000480007810BFF090180810311300802099C1130010204A900000001005D59
:800000006C000B810BFF090180810311300802099C1130010204F70000000101B08F
:800000006F000C810BFF090180810311300802099C1130010204BA00000001818078

バイナリをアスキーにした電文が来ます。
電文の詳細はホームページに記載されています。パルアプリ - WINGS
必要な情報は3つ

  • 番号5 送信元の論理デバイスID

  • 番号d データ(子機の電池電圧(mV))

  • 番号n データ(磁気センサの状態 )
    0x00=近くに磁石がない
    0x01=N極が近い
    0x02=S極が近い
    0x80= 定期送信

    今回は磁気センサしか使っていないので、単純に:から始まる電文の文字数で必要なデータを取り出すことができます。
    余裕があれば拡張性がある作りにしたいところでしたが、今回はとりあえず決め打ちでプログラムしました。(とりあえずで作ったプログラムが直されることはほぼないが)

    IoTサービスへの考慮

    センサーからデータ受信するたびにIoTサービスへインターネット通信するのが最も簡単ですが、フリーで使えるIoTサービスはデータの更新間隔や一日のデータ数などに制約があることがほとんどです。
    とあるフリーのIoTサービスは1日3,000件(約30秒間隔)、データの送信は最低5秒あける必要があるとの条件でした。それを考えると単純に受信したら投げるという作りにはできません。その条件を満たす実装を考えます。
    基本的には1分程度に1回、定期的にその時の状態を送るでいいと思いますが、問題はカギが開き閉めされた時です。
    1分に1回だとその間の開き閉めを見逃す可能性があります。それは避けたい。 特にドアの上側のカギはメインで使われるので、ここが閉→開の動きをした場合は確実にデータアップしたい。
    そういった考えを盛り込んで最終的なプログラムをしました。

    コード

    Wi-Fi接続およびIoTサービスへのデータ送信に関してはあえて省いています。(あまりにプライベートなものなので) ここまで出来る人であればこの先のIoT化はできるでしょう。

#include "M5Atom.h"

#define SENSOR_NUM   2
#define OPEN    1
#define CLOSE   0
uint8_t door_lock[ SENSOR_NUM ] = {0};
uint16_t batt_vol[ SENSOR_NUM ] = {0};

unsigned long send_time = 0;
uint8_t door1_before = CLOSE;

void setup() {
  M5.begin(true, false, true);
  Serial.begin(115200);
  Serial2.begin(115200, SERIAL_8N1, 21, 25);// RX=21   TX=25
  send_time = millis();
}

void loop() {
  char str[70]; //受信用配列
  int str_len;
  unsigned long now_time;

  if (Serial2.available() > 0 ) {
    str_len = Serial2RecvStr(str);
    if (str_len != 69) {
      return;
    }

    //Serial.println(str);
    char id[] = {str[23], str[24], '\0'};
    char bat[] = {str[39], str[40], str[41], str[42], '\0'};
    char door[] = {str[64], '\0'};
    int id_num = atoi(id);
    int bat_mv = strtol(bat, 0, 16);
    int d_num = atoi(door);

    switch (id_num) {
      case 1:
        if (d_num == 0 ) {
          door_lock[0] = 0; //door close
          door1_before = CLOSE;
          M5.dis.drawpix(0, 0x000000);
        } else {
          door_lock[0] = 1; //door open
          M5.dis.drawpix(0, 0x00f000);//開いている場合赤LED点灯
          if (door1_before == CLOSE) {
            while (1) {
              now_time = millis();
              if (send_time > now_time) { //飽和対策
                send_time = 5000;
              }
              if (now_time - send_time > 5000) {
                send_mes();
                break;
              }
            }
          }
          door1_before = OPEN;
        }
        batt_vol[0] = bat_mv;
        break;
      case 2:
        if (d_num == 0 ) {
          door_lock[1] = 0; //door close
        } else {
          door_lock[1] = 1; //door open
        }
        batt_vol[1] = bat_mv;
        break;
    }
  }
  now_time = millis();
  if (send_time > now_time) { //飽和対策
    send_time = 60000;
  }
  if (now_time - send_time > 60000) {
    send_mes();
  }
}

void send_mes(void) {
  Serial.print("door1 = ");
  Serial.println(door_lock[0]);
  Serial.print("bat1 = ");
  Serial.println(batt_vol[0]);
  Serial.print("door2 = ");
  Serial.println(door_lock[1]);
  Serial.print("bat2 = ");
  Serial.println(batt_vol[1]);

/*
 ここにIoTサービスへのデータ送信を記述すればよい
*/

  send_time = millis();
}
int Serial2RecvStr(char *buf)
{
  int i = 0;
  char c;
  if (Serial2.available() > 0 ) {
    c = Serial2.read();

    if (i == 0 ) {
      if (c == ':') {
        buf[i] = c;
        i++;
      } else
        return 0;
    }

    while (1) {
      if (Serial2.available() > 0 ) {
        c = Serial2.read();
        if ( (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ) {
          buf[i] = c;
          i++;
        } else {
          return 0; // ERROR
        }
        if (i == 69) {
          buf[i] = '\0';
          return i;
        }
      }
    }
  }
}

動作の様子

TWELITE DIPとM5AtomLiteの設置

電源はM5AtomのUSB端子で給電しますのでUSB電源ポートが必要です。
自分は最初、ドアから距離5mほど高さ2mほどにあるWi-Fiルータ(通称:カニルータ)のそばに置いてみたのですが、下側のカギにつけたセンサからの無線が不安定でした。上側のカギは問題なし。無線機の特性として地面に近いところに設置したものは通信距離が悪くなるので、多分そのせいだと思います。設置場所を変えてドアの近くに設置したら両方とも問題なく受信できました。Wi-Fiカニルータのおかげもあって余裕でつながりました。
ブレッドボードに挿してあるだけなので、テープで固定してタッパ的なケースに入れて完成としました。

最後に

今回はカギの状態だけを見ていますが、M5AtomのGroveコネクタも空いていることなので、何か他のセンサを追加して本格的なIoTをしてみるのも面白いと思っています。
今回のプログラミングはいわゆるゲートウェイを作るもので普段なじみがなく、我ながら不細工なコードだと思いますが、目標の期間で動くものができたのでよしとします。
TWELITE CUEの電池寿命はホームページページによると「1日に200回の開閉を行なった場合、約4年です。」とありますが、実際にどのくらい持つのか楽しみです。
稼働日:2021/5/4