Dumb generic keypad deadbolt locks made smart via BLE/Esphome

Hi everyone!

I’ve been a lurker here since I installed home assistant for my own home a year ago and wanted to find some way to give back to the community. This first post of mine is dedicated to you lovely people about automating your doorlock :).

Before we continue, I want you to know that
A - you realize off the bat that this is a horrible idea
B - your house’s security can be compromised by some guy with a phone
C - you could burn down your house
D - you may burn your fingers soldering
E - the soldering part is hard and I’d admit that this can be artistic soldering at times
F - you could already buy $150 door locks that are more secure than this and have IOT

I AM NOT RESPONSIBLE FOR ANYTHING YOU DO OR THAT HAPPENS TO YOU


Here’s how to automate your deadbolt lock.
I purchased some generic non-IOT keypad locks to lock my home

  1. orangeIOT lock example
  2. zomoss lock example

*Note that the most identical part is the right part. It comes in rectangle sizes too.
Weird sounding brands like:
Zomoss ← I bought this basically, has a rectangle back (2)
HuTools
OrangeIot ← I got this too. has a round back (1)
Hugolog
or simply named “Keypad Digital Deadbolt Doorlock”
They usually cost ~USD$25-$50.
These locks have exposed test pads and run on 4 AA batteries.

My aims

  • I want the lock to be low powered.
  • I want Home Assistant to control the lock.

I decided

  1. To have a Bluetooth chip reporting the lock’s lock/unlock state.
  2. (Optional) Bluetooth chip lock controlling unlocking/locking.
  3. ESP32 relaying Bluetooth communication back to Home Assistant.

Things I’ve tried
I. Esp8266 D1Mini lock which sucked so much battery power, I had to replace it every other day.
II. Esp8266 D1Mini lock with a wire running to the lock. It was stupid.
III. Running the circuit on a separate 3V battery.
Things I’ve NOT tried
A. Replacing bluetooth with some dev zigbee/zwave board
B. Replacing the sketchy H-Bridge I made out of 2N2222A331 with a L293D motor control.
C. Adding a door contact sensor, then your lock would only lock when the door is shut.
D. Direct Bluetooth to some other interface. (if you don’t use Home Assistant or EspHome)
E. Using NRF52 dev boards and whatnot instead of the only NRF51 board I had.
F. Using Tasmota. (I don’t think it has BLE interfaces)


Heres a BOM(Bill Of Materials) to do this. All in USD.

  1. The lock I mentioned above ($50 since I was desperate, you can get it for $25)
  2. An ESP32($10) - For Bluetooth
  3. An NRF51822/NRF52($4/$10) - For Bluetooth Low Energy
  4. A cheapo ST-LINK V2 ($5) - For programming the NRF
  5. A PC with Arduino on it + a phone with bluetooth and nRF Connect app on it
  6. Soldering Iron + Solder + Flux + Whatever that sticks two metals together
  7. Electronic Components
    a. A breadboard + wires + some male pins
    b. 1 470Ohm Resistor (To fix the issue with NRF51 pulling too much current)
    c. 4 2N2222A331 (If you want motor control from a sketchy H-Bridge circuit)
    d. 2 330Ohm Resistors (If you want motor control from a sketchy H-Bridge circuit)
    e. 4 Screw terminals (For disconnecting ease)
    Swap c&d for a L293D motor control

Once you are in that state of mind and have all the materials ready, here are the steps


/// 1. The lock

Lets investigate the back of the lock, to see which pads/pin can be tapped into.


Here you can see a few things you have access to:

  • The 2 motor pins are somewhat exposed (4.5V +/-).
  • The 2 battery connections are exposed (6V +/-).
  • The INTERFACE has multiple test pins.
    • GND: The ground pin.
    • HALL: Which runs 3.3V when locked and 0V when unlocked.
    • 3.3V: Its next to the MOTOR2 pin and has no testpad.

In total you have 2/3/7 pins you would be interested in.
Your options are:
2 Pins. HALL & GND. Wire in a separate 2 AAA/AA 1.5V batteries via a hole.
3 Pins. 3.3, HALL, GND. If you want to run it off the internal circuit, you need to reach the 3.3V.
7. If you want motor control on top of the rest, you need all the pins I’ve listed.
If your lock looks different, you either trail and error pads/pins or buy another lock.

Now the hard part:
To solder these parts, you will need to pull off some of the rubbery silicone off the connectors. You can scrape it off with your fingernails or use a small blade. Once exposed

  1. For the pads, scratch the pads a little, and melt some solder onto those pads first. Then bring a wire close to it and push it down on the pads with the soldering iron to fuse them together permanently.
  2. For the others, scratch the pins/legs a little, melt some solder on them and then bring a wire close to it and push it down the same. You may need a little more solder than usual to make it stick.

Wiggle the wires around a bit to see that they don’t move. Then maybe hot glue those parts down.
YOU CAN ACCIDENTALLY SHORT YOUR CIRCUITS AND CREATE FIRE, SO MAKE SURE ALL CONNECTIONS ARE STRONG.

End results:


/// 2. The circuit

In essence:

  • Expose the bluetooth programming pins (3.3V, GND, SWDIO, SWDCLK) for your ST-LINK V2
  • Expose the bluetooth GPIO pins (9/10/13 or whatever you want) + the same power pins(3.3V, GND)
  • Create a H-Bridge motor driver
  • Use one GPIO pin for sensing the state
  • Use two GPIOs for for driving the H-Bridge (forward, reverse)
  • Connect all grounds together (yes the bluetooth ground, sense ground and 6V/motor ground to create a common ground) I missed that in the diagram

I am not an electrical engineer, nor do I have any idea what I was doing, but I did take classes.

Here is my end result

Here’s some notes

  • I purchased a small form WT51822-S4AT off ebay and I regret soldering such a tiny piece. Do yourself a favor and get a normal breakout NRF51/NRF52 please. This was terrible.
  • My idea was to tap the exposed 6V to drive the 4.5V motor, which I proceeded to wing it.
  • I made it work with 4 2N2222A331s which took a lot of trail and error.
  • The bottom row are screw terminals. It is for easy disconnection.
  • I made it fit in the empty space in the top part of the lock.

When put together, it looks like this.


/// 3. The Bluetooth Low Energy(BLE) Programming

I am unfortunately a software engineer, with questionable programming skills.

To explain briefly, NRF51/NRF52 are a set of programmable BLE boards from Nordic Semiconductors which you can purchase to program GPIO pins on. It’s like an Arduino but with some proprietary bluetooth stack. It’s also said to be pretty low powered: ESP8266 takes about 60-200mA while BLE goes down to 0.04mA even while transmitting!

Usually you have to download the Nordic Software to program these boards, but some people wrote their own stack to program these boards via Arduino. https://github.com/sandeepmistry/arduino-BLEPeripheral SandeepMistry (thanks for this!) did the BLEPeripheral library so that you could use it in Arduino itself.

The steps in Arduino are:

  1. Install BLEPeripherals (I recommend checking out jeanmatthieud’s branch and installing it in arduino sketchbook folder as it contains some low power GPIOTE stuff which I’ll explain later)
  2. Flash SoftDevice Bootloader (v110 or v130 either are fine I think) “Tools → nRF5 Flash SoftDevice”
  3. Include <SPI.h> & <BLEPeripheral.h>, write program and Flash via “Sketch → Upload Using Programmer”

I came across jeanmatthieud’s BLE sensor with the nRF51822 chip write up (cheers for this!) which was his journey on trying to get nrf51 running on a coin cell battery. I followed this word for word and instead of installing SandeepMistry main branch, I checked out his branch instead to use his two nice functions attachInterruptLowAccuracy and detachInterruptLowAccuracy which listens out for GPIOTE(General purpose input/output tasks and events) on low power. If you don’t, you will have to resort to using attachInterrupt instead. It lets the program sleep and wake only on GPIO changes. I did measure my bluetooth current consumption before and after this and it was huge. 1mA vs 0.08mA (I think it could go lower, but this is fine).

Why was I so obsessed with this? Well, if you ran this on two AA rechargeable batteries, 800mAH/0.08mA would last you 10000hours which equals to way more than 1.5 years! I was very happy with this rather than running 5V wires from some phone charger in an outlet.

Here is my program. Inspired by sandeepmistry and jeanmatthieud.

// Import libraries (BLEPeripheral depends on SPI)
#include <SPI.h>
#include <BLEPeripheral.h>

// GPIO pins
#define SENSE_PIN 9 // The input pin which is going to sense 3.3V/0V state of the lock
#define CLOCKWISE_PIN 10 // The output pin which turns the lock clockwise.
#define ANTI_CLOCKWISE_PIN 13 // The output pin which turns the lock anti-clockwise.

// Create peripheral
BLEPeripheral blePeripheral = BLEPeripheral();

// Create service
BLEService bluetoothService = BLEService("12340000abc1234fabc15678abcdef09");

// Create characteristics (rando characteristic numbers for them)
BLECharCharacteristic stateCharacteristic("12340012abc1234fabc15678abcdef09", BLERead | BLENotify | BLEWrite);
BLECharCharacteristic motorClockwiseCharacteristic("12340014abc1234fabc15678abcdef09", BLERead | BLENotify | BLEWrite);
BLECharCharacteristic motorAnticlockwiseCharacteristic("12340016abc1234fabc15678abcdef09", BLERead | BLENotify | BLEWrite);

// Volatile interrupt values
volatile bool g_sensorValueChanged = false;
volatile uint32_t g_lastSensorValueChanged = 0;
volatile char g_sensorValue = 0;

// Callback for the attach/detatchInterruptLowAccuracy.
void sensorValueChanged()
{
  uint32_t diff = millis() - g_lastSensorValueChanged;
  if (diff > 600 || diff < 0) // Debounce value
  {
    g_sensorValueChanged = true;
  }
  g_lastSensorValueChanged = millis();
}

// This is to do a pin read, and then set the interrupt to flip.
// The interrupts only work on FALLING and RISING and not values like CHANGE, so I did a flip-flop here instead.
void pinInterruptRead() {
  if (g_sensorValueChanged) {
    g_sensorValue = digitalRead(SENSE_PIN);
    if (g_sensorValue) {
      stateCharacteristic.setValue(0x01);
      detachInterruptLowAccuracy(SENSE_PIN);
      attachInterruptLowAccuracy(SENSE_PIN, sensorValueChanged, FALLING);
    } else {
      stateCharacteristic.setValue(0x02);
      detachInterruptLowAccuracy(SENSE_PIN);
      attachInterruptLowAccuracy(SENSE_PIN, sensorValueChanged, RISING);
    }
    g_sensorValueChanged = false;
  }
}

// When a motorCharacteristic is changed, the intention is to run the motor.
// The motor will basically turn 1s,detect x5 and then turn the other way for 1.5s.
// It will read the pin state to update the state of the lock.
// Flip the digitalRead(SENSE_PIN) to ! if you have your lock installed the other way.
void motorCharacteristicWritten(BLECentral& central, BLECharacteristic& characteristic) {
  g_sensorValueChanged = true;
  pinInterruptRead();
  if (motorClockwiseCharacteristic.value() == 0x01 && !digitalRead(SENSE_PIN)) {
    stateCharacteristic.setValue(0x04);
    for (int i = 0; i < 5; i++) {
      digitalWrite(CLOCKWISE_PIN, HIGH);
      delay(1000);
      digitalWrite(CLOCKWISE_PIN, LOW);
      if (digitalRead(SENSE_PIN)) {
        break;
      }
    }
    digitalWrite(ANTI_CLOCKWISE_PIN, HIGH);
    delay(1500);
    digitalWrite(ANTI_CLOCKWISE_PIN, LOW);
    motorClockwiseCharacteristic.setValue(0x00); // clear characteristic when done.
    g_sensorValueChanged = true;
    pinInterruptRead(); // re-read pin just in case
  } else if (motorAnticlockwiseCharacteristic.value() == 0x01 && digitalRead(SENSE_PIN)) {
    stateCharacteristic.setValue(0x05);
    for (int i = 0; i < 5; i++) {
      digitalWrite(ANTI_CLOCKWISE_PIN, HIGH);
      delay(1000);
      digitalWrite(ANTI_CLOCKWISE_PIN, LOW);
      if (!digitalRead(SENSE_PIN)) {
        break;
      }
    }
    digitalWrite(CLOCKWISE_PIN, HIGH);
    delay(1500);
    digitalWrite(CLOCKWISE_PIN, LOW);
    motorAnticlockwiseCharacteristic.setValue(0x00); // clear characteristic when done.
    g_sensorValueChanged = true;
    pinInterruptRead(); // re-read pin just in case
  }
}

void setup() {
  //Serial.begin(9600); // This is for debugging printf out to serial. This takes 10mA and is only meant for debugging.

  // Set pin states.
  pinMode(SENSE_PIN, INPUT);
  pinMode(CLOCKWISE_PIN, OUTPUT);
  pinMode(ANTI_CLOCKWISE_PIN, OUTPUT);

  // Set device and local name. Name it some generic name as everyone can see this.
  blePeripheral.setDeviceName("GENERICBLE");
  blePeripheral.setLocalName("GENERICBLE");

  // Start all the Service and Attributes.
  blePeripheral.setAdvertisedServiceUuid(bluetoothService.uuid());
  blePeripheral.addAttribute(bluetoothService);
  blePeripheral.addAttribute(stateCharacteristic);
  blePeripheral.addAttribute(motorClockwiseCharacteristic);
  blePeripheral.addAttribute(motorAnticlockwiseCharacteristic);

  // Set advertising and connection interval.
  blePeripheral.setAdvertisingInterval(320);
  blePeripheral.setConnectionInterval(200,250);
  
  // Assign event handlers for motor characteristic changes
  motorClockwiseCharacteristic.setEventHandler(BLEWritten, motorCharacteristicWritten);
  motorAnticlockwiseCharacteristic.setEventHandler(BLEWritten, motorCharacteristicWritten);
  
  // Begin ble initialization
  blePeripheral.begin();

  sd_power_mode_set(NRF_POWER_MODE_LOWPWR); // Set NRF to low power mode.
  g_sensorValueChanged = true; // To trigger inital pin read
  pinInterruptRead(); // Attach initial GPIO interrupt.
}

void loop() {
  // Poll peripheral
  sd_app_evt_wait(); // Sleeps until an interrupt is called.
  sd_nvic_ClearPendingIRQ(SWI2_IRQn); // Clear pending interrupts.
  pinInterruptRead(); // Read and reassign the interrupt.
  blePeripheral.poll(); // Generic BLE polling.
}

Some notes:

  • I can write the code better, but I’m quite the shit software engineer
  • The program has one “output” BLE Characteristic and two “input” BLE Characteristics
  • The output BLE Characteristic is the lock state
  • The two input BLE Characteristic is to lock and unlock the lock via the motor
  • The (attach/detach)InterruptLowAccuracy is for the lock state and flip flops
  • The callback setEventHandler is for both locking and unlocking

/// 4. The ESP32

Thanks to the Esphome, you basically install the ble_tracker that they have.
If you have other ways to interface in a bluetooth lock directly, go ahead.

esp32_ble_tracker:

ble_client:
  - mac_address: AB:CD:EF:12:34:56 #Find this from the nRF Connect App on your phone or some bluetooth sniffer
    id: myBleClient
    
sensor:
  - platform: ble_client
    ble_client_id: myBleClient
    name: "mylockstate"
    service_uuid: '12340000-abc1-234f-abc1-5678abcdef09' # The service ID.
    characteristic_uuid: '12340012-abc1-234f-abc1-5678abcdef09' # The sense pin characteristic.
    icon: 'mdi:lock'
    notify: true
    id: lockstate
    
output:
  - platform: ble_client
    ble_client_id: myBleClient
    service_uuid: '12340000-abc1-234f-abc1-5678abcdef09' # The service ID.
    characteristic_uuid: '12340014-abc1-234f-abc1-5678abcdef09' # The lock characteristic.
    id: tolock
  - platform: ble_client
    ble_client_id: myBleClient
    service_uuid: '12340000-abc1-234f-abc1-5678abcdef09' # The service ID.
    characteristic_uuid: '12340016-abc1-234f-abc1-5678abcdef09' # The unlock characteristic.
    id: tounlock

# This creates the lock for you.
lock:
  - platform: template
    name: "Template Lock"
    lambda: |-
      if (id(lockstate).state == 0x05) {
        return LOCK_STATE_UNLOCKING;
      } else if (id(lockstate).state == 0x04) {
        return LOCK_STATE_LOCKING;
      } else if (id(lockstate).state == 0x03) {
        return LOCK_STATE_JAMMED;
      } else if (id(lockstate).state == 0x02) {
        return LOCK_STATE_UNLOCKED;
      } else if (id(lockstate).state == 0x01) {
        return LOCK_STATE_LOCKED;
      } else {
        return LOCK_STATE_NONE;
      }
    lock_action:
      - output.turn_on: tolock
    unlock_action:
      - output.turn_on: tounlock

/// 5. Home Assistant

  1. Entity Card to show the lock state.
  - type: custom:mushroom-entity-card
    entity: sensor.lockstate
  1. Mushroom Lock Card to show the lock state and locking/unlocking function.
  - type: custom:mushroom-lock-card
    entity: lock.template_lock

mushroomlock


/// Security Concerns

One major thing you may realized already: THIS IS CRAZY, YOU MIGHT AS WELL LEAVE YOUR DOOR OPEN.
All I have to do to hack your lock is to go up to it, use my nRF Connect App and send 12340014-abc1-234f-abc1-5678abcdef09 or 12340016-abc1-234f-abc1-5678abcdef09 a 0x01 char code to unlock your door.

Yes. I know. Hence the warnings at the top.
So here are some suggestions

  • let only your esp32 pair with it, in which my code does that and cuts advertising once connected
  • hardcode a special char to unlock it like 0xthisIsMyPass
  • obscure it by exposing a lot of dummy characteristics
  • only implement the lock sensing by disconnecting the power to motors

At this point, it is security by obscurity, and I’m open to suggestions.


/// Finale. My thoughts

  • There is WAY too much investment of time here. Sorry if it was a long read.
  • Realistically, I may not reply to everyone or help with code/electronics when requested. Sorry in advance.
  • I think this is cheap ~USD$(30-75) for a lock and a bit cheaper if you do more of it or found it 2nd hand.
  • I like that I have full control over my lock.
  • I hate that the security on this is almost like a door locked with a Cheetos stick.
  • This was a lot to learn, but I’m glad it works. (Soldering, BLE Coding, Esphome, HomeAssistant)
  • Hopefully someone else gets ideas out of this (a ble door/window sensor? a soil moisture detector?)
  • I would like some suggestions to make this lock better.
  • I hope this lock may make someone’s home a little safer.

Cheers!

Shout outs to

  • Home Assistant (Mushroom Cards too!)
  • Esphome/Tasmota (The BLE Functionality is chef kiss)
  • BLE Programming (sandeepmistry for BLE Peripheral, jeanmatthieud for Low Power GPIOTE callbacks)
  • Maybe the lock people for exposing test pads.
5 Likes

Reserved 1

Reserved 2

Reserved 3

Have you found a better way of adding esp home to other keypad deadbolt locks ?

Like this one Smart Door Lock, Geek 4-in-1 IP65 Waterproof Anti-Fog Keyless Entry Deadbolt Door Locks with Keypad Remote Control with APP, IC Card, Mechanical Key Low Temperature Start Front Door External Lock https://a.co/d/he88u1W

1 Like

Unfortunately, no.

This hardware hack is an injection of a bluetooth chip onto a dumb keypad lock, not a smart one. My hardware hack only does 2 things: sense the lock state and run motors. It doesn’t even hook onto the lock’s logic at all which has its own limitations.

I took a look at your link and it seems to be a smart lock with full app features costing about $100. The connecting wire is different and it includes a lot of features over what I have (one-time passwords, voice control). I don’t think this hack will work on it unfortunately although it could if you could find the relevant pins on those locks.

It was a good read, thanks.

How do you actually open the lock? Using your phone by clicking unlock in home assistant?

Step 5 is how the UI interface will be like and is unlocked by clicking the unlock button in home assistant. The lock itself works just fine too (manual keypad unlock/key unlock).

Security wise, I think the most you could do here would be to hide it behind a key and unlocking it with a numeric keypad on home assistant, but that slows down the unlocking. Since you sign in with your user account, you would limit the use of the unlock/lock to your accounts anyway.