帰宅したら自動で解錠するスマートロックを自作する②~ボタンで解錠編~

前回はドアが閉まったら自動で鍵をかけるシステムを作りましたが、それだと開けることができないので結局鍵を出さないといけなくて面倒です。そこで今回は鍵の代わりにタクトスイッチを使った暗証番号的な方法で解錠する方法をご紹介します。

作りたいもの

前回のつづきです。

  1. ドアが閉まったら自動で施錠
  2. 暗証番号で解錠
  3. ICカードで解錠
  4. スマホを持って近づくだけで解錠

前回から追加で必要なもの

  • タクトスイッチ
押すと導通するふつうのタクトスイッチ

ドアの外側にスイッチをつける

外から押せる位置にスイッチをつけます。うちのドアの横のすき間から線を通せたので普通の導線を使っています。ドアのすき間が狭い場合はフラットケーブル等を使うと良いと思います。ESP8266側はGNDとデジタル入出力に繋ぎます。

方法を決める

ボタンを準備したら次はどんなパターンでボタンを押したら解錠するか決めます。

やり方はみなさんのオリジナリティを出せるところだと思います。例えばボタンを押す順番や、押す時間の長短などを組み合わせて秘密の暗号にします。ここでは長短の組み合わせをパスワードにしてみます。

長短の組み合わせ(モールス符号)をパスワードにしてみる

皆さんご存じモールス符号をパスワードにしてみます。例えば「OPEN」のモールス符号で「--- ・--・ ・ ―・」と押した時に解錠するようにしてみます。

新しく追加したのは関数secret_command()の部分です。ボタンを押したり離したりしたときに、digitalReadの値の立ち下がり・立ち上がりを検出して、ボタンを押していた時間で「・」か「―」を判定します。3秒以上入力がなかったら終了して、入力された符号とパスワードが一致していたら解錠します。

#include <Servo.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

// ルータ設定
IPAddress ip(192,168,100,102);
IPAddress gateway(192,168,100,1);
IPAddress subnet(255,255,255,0);

// ESP8266のピン番号
const int servo_pin = 2;
const int door_pin = 12;
const int button_pin = 14;

Servo myservo;             // サーボオブジェクトを生成
int door_sensor = LOW;
int door_sensor_last = LOW;
ESP8266WebServer server(80);  //サーバーオブジェクト


void door_open(){
  myservo.attach(servo_pin);
  delay(50);
  myservo.write(170);
  delay(500);
  myservo.write(90);
  delay(500);
  myservo.detach();
}

void door_close(){
  myservo.attach(servo_pin);
  delay(50);
  myservo.write(10);
  delay(500);
  myservo.write(90);
  delay(500);
  myservo.detach();
}

void handle_open(){
  door_open();
  server.send(200, "text/html", "opened");
}

void handle_close(){
  door_close();
  server.send(200, "text/html", "closed");
}

bool secret_command(){
  // HIGHがスイッチが押されていない状態
  // LOWがスイッチが押されている状態
  
  unsigned long pressed_time = millis();
  unsigned long released_time = millis();
  int now_state = LOW;
  int last_state = LOW;
  
  float button_lpf = 0.0;  // センサ信号にローパスフィルタ(LPF)をかけた値
  
  String key = "---.--..-.";  //モールス符号でO・P・E・N
  String input = "";
  while(millis() - released_time < 3000){

    button_lpf = 0.9 * button_lpf + 0.1 * digitalRead(button_pin);
    if( button_lpf > 0.8 ){
      now_state = HIGH;
    }else if( button_lpf < 0.2 ){
      now_state = LOW;
    }

    if( last_state == HIGH && now_state == LOW ){
      // ボタンが押されたとき、HIGH→LOW
      pressed_time = millis();
    }else if( last_state == LOW && now_state == HIGH){
      // ボタンが離されたとき、LOW→HIGH
      released_time = millis();

      // ボタンを押していた時間で「.」か「-」を追加
      input += (released_time - pressed_time < 300) ? "." : "-";
    }

    last_state = now_state;
    delay(1);
  }
  
  if(key == input){
    return true;
  }else{
    return false;
  }
  
}

void setup() 
{ 
  
  Serial.begin(115200);
  delay(200);

  //固定IPで運用するときの設定
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(WIFI_SSID, WIFI_PWD);

  // WiFiに接続するまで待つ
  Serial.println("");
  while(WiFi.status() != WL_CONNECTED){
    delay(1000);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.println("Connected!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  // Webサーバを設定
  server.on("/open", handle_open);
  server.on("/close", handle_close);
  server.begin();

  // 開閉センサの入力を内部プルアップにする
  pinMode(door_pin, INPUT_PULLUP);

  // サーボ変数をピンに割り当て
  myservo.attach(servo_pin);

  door_close();
} 

void loop() 
{ 
  // サーバとして待ち受ける
  server.handleClient();

  // 現在のドアの開閉を検知
  // LOW=閉
  // HIGH=開
  door_sensor = digitalRead(door_pin);

  // 「開」→「閉」になったタイミングでサーボモータを回す
  if(door_sensor == LOW && door_sensor_last == HIGH){
    delay(1000);
    door_close();
  }  
  door_sensor_last = door_sensor;

  // ボタンで解錠
  if(digitalRead(button_pin) == LOW){
    bool success = secret_command();
    
    if(success){
      door_open();
    }
  }
  
}

完成!

簡単ではありますがこれだけでパスワード解錠的な機能をつけることができました。他にもテンキーなどのデバイスを付けてパスワードと一致したら解錠とかもできると思います。もっといいやり方があるよ!という方はコメントで教えてください。

ちゃんとセキュリティを考えるなら、10回入力に失敗したら一定時間操作を受け付けなくなるとかも考えたほうが良いかもしれません。今回の例ならパスワードが10ケタなので、2の10乗=1024回試されたら破られます。もう少しパスワードは長いほうが良いかも。

←前回    次回→

コメントを残す

メールアドレスが公開されることはありません。