Smarten Your Home From The Entrance: MKR Keylock

Smarten Your Home From The Entrance: MKR Keylock © GPL3+

The entrance is one of the main devices in your house, yet it uses pretty old tech. Now you can upgrade your door to the IoT century!

  • 9 views
  • 0 comments
  • 0 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)
Pro dmm box 600  26129.1449785766.500.659
Digilent Mastech MS8217 Autorange Digital Multimeter
Electrician Scissors
Screwdriver set

Apps and online services

About this project

Story

Entrance doors are architectural elements we get through multiple times a day and still they use a very old technology: the key. As anyone, we were exhausted by the worries of taking care of a key, the difficulties of getting the keyhole right at night - not to say to allow one of our guests in while we were away (think of an airbnb house! we'll explore that soon!).

Fear no more, we managed to find a Solution! (and it's a cool one)

Overview

In order to smarten our door, we have evaluated different kinds of authentication technologies and we thought that using a alphanumeric passcode is the best match of simplicity and security. Developing our project around the Arduino MKR WiFi 1010 enables WiFi and BLE connectivity, allowing us to connect it to our MQTT broker and integrate it in our favourite home automation software.

Setup

The first step is to modify your door by installing an electronic door lock. There are several models on the market ranging from very cheap to very expensive, choose in base of your needs. We had a door with a Keyless lock and we modified the electronics to fit our requirements. (exposing the opener contact)

The second step is to locate an accessible and permanent location for the keypad and the electronics. You can mount them together or not, depending on your situation. In our case, we had a ringbell box near the door, so we used it.

Then we start prototyping on a breadboard with some jumper wires. We hooked up the keypad all on one side and the outputs on the other to keep things tidy. Depending on your lock model, you could have to use octocouplers or relays in order to control the door. In our case the Arduino is powered by the lock circuitry and the lock is 3.3v compatible, so there is no problem hooking it up directly.

To test the circuit we've released a simple code that interfaces the keypad and the lock, without all the network mess. This is pretty straightforward and is fast to check if your circuit is configured correctly.

As you can see in the code below, you can configure the matrix layout and pins, a door opener contact and a ring output; there is also a buzzer pin to give a sound feedback to the user: change the code according to your setup.

// Officine Innesto - Alberto Perro 2019
// keypad settings
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = { //keypad mapping
 {'1','2','3','A'},
 {'4','5','6','B'},
 {'7','8','9','C'},
 {'*','0','#','D'}
};
byte rowPins[ROWS] = {1,0,A6,A5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5,4,3,2}; //connect to the column pinouts of the keypad
Keypad pad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
//lock definitions
const int doorOpener = 7; //opener contact
const int doorRing = 9;   //ring contact
const int buzzer = 12;    //buzzer pin
char master[] = {'2', '6', '7', '8'}; //master key

The firmware accepts the Master Code (you'll see why it's called Master later) which can be an infinite-long char array. The code supports just numbers since we've used the other buttons as special functions.

A brief explanation of the firmware: Enter the digits and press # to confirm, if the code is right or wrong it will make a sound; if you entered the wrong digit, press C to cancel the code otherwise it will self-reset after 10 seconds. To drive the ring output press the D key. You can implement your own variation now, using the remaining keys!

// Officine Innesto - Alberto Perro 2019
// keypad settings
if(key){                         
   timer = millis();
   Serial.println(key);
   if(key=='D'){               //key to ringbell
     Serial.println("RING");
     digitalWrite(doorRing, LOW);
     tone(buzzer,784,500);
     delay(500);
     tone(buzzer,659,500);
     digitalWrite(doorRing, HIGH);
     resetKeys();
   }else if(key=='C'){         //key to reset
     resetKeys();
   }else if(key=='#'){         //key to open
     checkKey()?openTheDoor():tone(buzzer,500,500);
     resetKeys();
   }else if(key!='A'&& key!='B'){ // buffer manager
     temp[keyCount]=key;
     keyCount++;
 }
}

Since this project is intended for a semi-permanent installation and should be manteinance-free, we built a proto-board to keep the things safe, compact and tidy. This can take up to one hour and a good amount of soldering and design skills; if you're not familiar with the tools, this is not a project to start with. By the way, the result is great and the time making it is well spent.

Software

Here it goes the real stuff you've been waiting for: the complete firmware.
After having double checked that your circuit is fully working using the first code, you can now transfer the configuration stuff into the new one. As you can see, it also ask for Wi-Fi network credentials and an MQTT broker configuration.

New firmware enables remote control of the door lock: it supports an open command by publishing ON in the /open topic and a guest code, maximum 10 digits long, that can be set by publishing the code in the /code topic. This code will not be saved on the device, so if the power goes out, it will reset and accept the hardcoded master key only; to keep it you can set the retain flag in your MQTT message and the broker will send it every time the device subscribes to the topic, however this procedure is less secure because if someone subscribes to all topics of your MQTT broker, it will get the code, too.

The lock also publishes infos in some topics: whenever the door has been opened by digiting the code or by MQTT, it will send a YES on the /open topic. If you don't have a ringbell contact handy, the lock will publish a YES under /ringbell/pressed topic. Another handy feature is an OK message under /alive/status topic every 10 seconds, so you will know if anything has gone wrong.

Home Assistant Integration

As an example of integration, we've added the door lock to our Home Assistant istance; this way you can have a simple user interface to open the door and to set the guest code.

First, we need to add a switch and a text field. To do so, edit the configuration.yaml and add the following lines.

//text field for the guest code
input_text:
 guest_code:
   name: "Guest Code"
   icon: mdi:login 
//door opener switch
switch:
 - platform: mqtt
   name: "Open the door"
   state_topic: "door_lock/door_lock/status"
   command_topic: "door_lock/door_lock/open"
   optimistic: false
   icon: mdi:lock-open

Reload Home Assistant and you should see them in the dashboard.
Now we need to write the automation that publish the input code via MQTT. Open your automation.yaml file and edit.

 - id: guest_code
   alias: guest_code
   trigger:
    - platform: state
      entity_id: input_text.guest_code
   action:
    - service: mqtt.publish
      data_template:
        topic: 'door_lock/door_lock/code'
        payload_template: '{{states.input_text.guest_code.state}}'

By using this automation, Home Assistant checks when the text field is updated and publishes the input text on the desired MQTT topic. To make this happen, just reload automations in Settings > General, reload the page and try it out!

This tutorial has finished, now it's up to you to improve it and add your own custom features: happy making!

Code

Simple keypadArduino
This is the simple keypad sketch. Use it to debug your connections.
/* Simple Keypad using Arduino MKR WiFi1010
 * -----------------------------------------
 * This sketch provide a simple keypad lock using an electronic lock and a keypad.
 * Mapping:
 * 7 --> CONTACT TO OPENER (ACTIVE HIGH)
 * 9 --> CONTACT TO RINGBELL (ACTIVE HIGH)
 * -----------------------------------------
 * Materials:
 * Arduino MKR WiFi1010
 * 4x4 Keypad
 * Keyless or other electronic locks
 * Ringbell
 * -----------------------------------------
 * Created by Alberto Perro
 * Officine Innesto 2018
 */
 
#include <Keypad.h>

//keypad settings
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = { //keypad mapping
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {1,0,A6,A5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5,4,3,2}; //connect to the column pinouts of the keypad
Keypad pad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

//lock definitions
const int doorOpener = 7; //opener contact
const int doorRing = 9;   //ring contact
const int buzzer = 12;    //buzzer pin
char master[] = {'2', '6', '7', '8'}; //master key
//variables
unsigned long timer;
char temp[10];
int keyCount = 0;

void setup(){
  Serial.begin(9600);
  //while (!Serial);
  pinMode (buzzer, OUTPUT);
  pinMode (doorOpener, OUTPUT);
  pinMode (doorRing, OUTPUT);
  digitalWrite(doorOpener, HIGH);
  digitalWrite(doorRing, HIGH);
}

void loop(){
  if ((millis() - timer) > 10000) { // reset key buffer after 10s
    timer = millis();
    Serial.println("Reset keys");
    resetKeys();
  }
  char key = pad.getKey(); //read keys
  if (key){ //key selection
    timer = millis();
    Serial.println(key);
    if(key=='D'){               //key to ringbell
      Serial.println("RING");
      digitalWrite(doorRing, LOW);
      tone(buzzer,784,500);
      delay(500);
      tone(buzzer,659,500);
      digitalWrite(doorRing, HIGH);
      resetKeys();
    }else if(key=='C'){         //key to reset
      resetKeys();
    }else if(key=='#'){         //key to open
      checkKey()?openTheDoor():tone(buzzer,500,500);
      resetKeys();
    }else if(key!='A'&& key!='B'){ // buffer manager
      temp[keyCount]=key;
      keyCount++;
  }
}
}
// key check method
bool checkKey(){
  bool check = true;
  Serial.println("check Master key");
  for(int i =0;i<sizeof(master)/sizeof(char);i++){
    if(master[i]!=temp[i]) check=false;
  }
  return check;
}
// key reset method
void resetKeys(){
  keyCount=0;
  temp[4]={0};
}
// open the door routine
void openTheDoor() {
  digitalWrite(doorOpener, LOW);
  delay(100);
  digitalWrite(doorOpener, HIGH);
  tone(buzzer,784,300);
  delay(200);
  tone(buzzer,1047,300);
}
WiFi KeypadArduino
This is the full firmware, implementing MQTT over WiFi.
/* WiFi-enabled Keypad using Arduino MKR WiFi1010
 * -----------------------------------------
 * This sketch provide a simple keypad lock using an electronic lock and a keypad
 * It also has MQTT implementing opening and setting a guest code.
 * Mapping:
 * 7 --> CONTACT TO OPENER (ACTIVE HIGH)
 * 9 --> CONTACT TO RINGBELL (ACTIVE HIGH)
 * -----------------------------------------
 * Materials:
 * Arduino MKR WiFi1010
 * 4x4 Keypad
 * Keyless or other electronic locks
 * Ringbell
 * -----------------------------------------
 * Created by Alberto Perro
 * Officine Innesto 2018
 */
#include <Keypad.h>
#include <MQTTClient.h>
#include <WiFiNINA.h>
//keypad settings
const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {1,0,A6,A5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5,4,3,2}; //connect to the column pinouts of the keypad
Keypad pad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
// Network settings
char ssid[] = "YOUR SSID";     //  your network SSID (name)
char pass[] = "YOUR PASSWORD";  // your network password

// MQTT settings
char MQTTClient_id[] = "door_lock";
char MQTTClient_topic[] = "door_lock";
char MQTTBroker_ip[] = "YOUR BROKER IP";

// Client settings
WiFiClient net;
MQTTClient client;

//lock definitions
const int doorOpener = 7; // DOOR CONTACT
const int doorRing = 9;   // RINGBELL CONTACT
const int buzzer = 12;    // BUZZER PIN
unsigned long timer;
char master[] = {'0', '0', '0', '0'}; // MASTER KEY
char guest[10];
char guestLength = 0;
char temp[10];
int keyCount = 0;
bool toOpen = false;

void setup(){
  Serial.begin(9600);
  pinMode (buzzer, OUTPUT);
  pinMode (doorOpener, OUTPUT);
  pinMode (doorRing, OUTPUT);
  digitalWrite(doorOpener, HIGH);
  digitalWrite(doorRing, HIGH);
  Serial.print("Attempting to connect to WPA SSID: ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
  Serial.println("You're connected to the network");
  client.begin(MQTTBroker_ip, net);
  client.onMessage(messageReceived);
  if(client.connect(MQTTClient_id)){
    client.subscribe("door_lock/" + String(MQTTClient_topic) + "/code");
    client.subscribe("door_lock/" + String(MQTTClient_topic) + "/open");
    Serial.println("\nMQTT connected!");
  }
}

void loop(){
  if (!client.connected()) { // check net and MQTT connection
    Serial.println("reconnection");
    if(client.connect(MQTTClient_id)){
      client.subscribe("door_lock/" + String(MQTTClient_topic) + "/code");
      client.subscribe("door_lock/" + String(MQTTClient_topic) + "/open");
      Serial.println("\nMQTT connected!");
    }
  }
  client.loop();
  if ((millis() - timer) > 10000) { //send alive message
    timer = millis();
    client.publish("door_lock/" + String(MQTTClient_topic) + "/alive/status", "OK");
    Serial.println("keep it alive");
    resetKeys();
  }
  char key = pad.getKey();
  if (key){           //key functions 
    timer = millis();
    Serial.println(key);
    if(key=='D'){ // play ringbell
      client.publish("door_lock/" + String(MQTTClient_topic) + "/ringbell/pressed", "YES");
      Serial.println("RING");
      digitalWrite(doorRing, LOW);
      tone(buzzer,784,500);
      delay(500);
      tone(buzzer,659,500);
      digitalWrite(doorRing, HIGH);
      resetKeys();
    }else if(key=='C'){ // delete code
      resetKeys();
    }else if(key=='#'){ // enter code
      checkKey()?openTheDoor():tone(buzzer,500,500);
      resetKeys();
    }else if(key!='A'&& key!='B'){
      temp[keyCount]=key;
      keyCount++;
  }
}
if(toOpen) openTheDoor();
}
//check keys method
bool checkKey(){
  bool check = true;
  Serial.println("check Master key");
  for(int i =0;i<sizeof(master)/sizeof(char);i++){
    if(master[i]!=temp[i]) check=false;
  }
  if(check){return true;}else{check=true;}
  Serial.println("check Guest key");
  for(int i =0;i<guestLength;i++){
    if(guest[i]!=temp[i]) check=false;
  }
  
  return check;
}
//reset key buffer method
void resetKeys(){
  keyCount=0;
  temp[4]={0};
}
//MQTT callback method
void messageReceived(String &topic, String &payload) {
  client.publish("received_Message", "OK");
  Serial.print("incoming: ");
  Serial.print(topic);
  Serial.print(" - ");
  Serial.print(payload);
  Serial.println();
  if (topic == "door_lock/" + String(MQTTClient_topic) + "/open") { // 
    if (payload == "ON") toOpen = true; 
  }
  if (topic == "door_lock/" + String(MQTTClient_topic) + "/code") { // GUEST CODE TOPIC
    //Serial.println(payload.length());
    payload.toCharArray(guest,11);
    guestLength=payload.length();
    Serial.print("Got Guest Code:");
    for(int i=0;i<guestLength;i++) Serial.print(guest[i]);
    Serial.println();
  }
}

//door open method
void openTheDoor() {
  digitalWrite(doorOpener, LOW);
  delay(100);
  digitalWrite(doorOpener, HIGH);
  client.publish("door_lock/" + String(MQTTClient_topic) + "/open", "YES");
  tone(buzzer,784,300);
  delay(200);
  tone(buzzer,1047,300);
}

    

Schematics

Circuit Diagram
This is the circuit diagram of the project. Follow it to connect the peripherals.
Test f7cqsnoxjc

Comments

Add projectSign up / Login