from machine import Pin, Timer, lightsleep
import time
import esp32
DEBOUNCE_TIME = 0.05 # seconds
led_pin = Pin(13, Pin.OUT)
door_pin = Pin(12, Pin.IN, Pin.PULL_DOWN)
alarm_pin = Pin(14, Pin.OUT)
# Configure the wake-up source
esp32.wake_on_ext0(pin=door_pin, level=esp32.WAKEUP_ALL_LOW)
# Define GPIO pins for rows
rows = [
Pin(5, Pin.OUT),
Pin(18, Pin.OUT),
Pin(19, Pin.OUT),
Pin(21, Pin.OUT)
]
cols = [
Pin(22, Pin.IN, Pin.PULL_UP),
Pin(23, Pin.IN, Pin.PULL_UP),
Pin(25, Pin.IN, Pin.PULL_UP),
Pin(26, Pin.IN, Pin.PULL_UP)
]
# Define keypad layout
keys = [
['1', '2', '3', 'A'],
['4', '5', '6', 'B'],
['7', '8', '9', 'C'],
['*', '0', '#', 'D']
]
def scan_keypad():
for r_index, row_pin in enumerate(rows):
# set row off
row_pin.off()
# poll through col pins
for c_index, col_pin in enumerate(cols):
# key pressed
key = None
while col_pin.value() == 0:
key = keys[r_index][c_index]
# set row back on
if key:
pressed_key_pos = r_index, c_index
row_pin.on()
return keys[r_index][c_index]
row_pin.on()
return None
class DoorAlarm:
DISARM_CODE = "123456"
DISARM_KEY = 'D'
ARM_KEY = 'A'
# door states
UNKNOWN = -1
OPEN = 1
CLOSED = 0
# intervals (in ms)
DISARMED_BLINK_INTERVAL = 5_000 # how often should blink when state is DISARMED
KEY_PRESSED_LED_DURATION = 100 # how much the led stays on after a key is pressed
DOOR_DISARM_TIME = 20_000 # how much time it should pass before alarm goes on
def __init__(self):
# led
self._led_timer = Timer(0)
self._led_on_timer = Timer(1)
# door
door_pin.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=self.check_door)
self._door_state = self.UNKNOWN
# alarm
self._alarm_timer = Timer(2)
self._alarm_timer_started = False
alarm_pin.value(0)
# internal states
self._current_code = ""
def _arm(self):
led_pin.value(0)
if self._alarm_timer_started:
return
self._current_code = ""
print("armed")
led_pin.value(0)
self._led_timer.deinit()
self._led_on_timer.deinit()
lightsleep()
def _disarm(self):
print(self._current_code)
if self._current_code != self.DISARM_CODE:
print("invalid code")
self._trigger_warning()
self._current_code = ""
return
self._blink(self.DISARMED_BLINK_INTERVAL)
self._current_code = ""
self._alarm_timer.deinit()
self._alarm_timer_started = False
alarm_pin.value(0)
print("disarmed")
lightsleep()
def _clear(self):
self._current_code = ""
def _trigger_warning(self):
# only place where sleep is allowed
for i in range(5):
led_pin.value(0)
time.sleep(0.2)
led_pin.value(1)
led_pin.value(0)
def set_key(self, key: str):
if not key:
return
elif key is self.DISARM_KEY:
self._disarm()
else:
self._current_code += key
self._led_on(self.KEY_PRESSED_LED_DURATION)
def check_door(self, pin):
if pin.value() ^ 1 == self._door_state:
return
self._door_state = pin.value() ^ 1
if self._door_state is self.OPEN:
self.start_alarm_timer()
elif self._door_state is self.CLOSED:
self._arm()
def start_alarm_timer(self):
if not self._alarm_timer_started:
print("please enter code")
self._alarm_timer.init(mode=Timer.ONE_SHOT, period=self.DOOR_DISARM_TIME, callback=lambda *_: self._alarm())
self._alarm_timer_started = True
def _alarm(self):
print("Alarm goes on")
alarm_pin.value(1)
self._blink(500, 250)
def _blink(self, period: int, duration: int=300):
"""
period: interval in miliseconds to blink
duration: how much time in miliseconds the led should stay HIGH
"""
self._led_timer.init(mode=Timer.PERIODIC, period=period, callback=lambda *_: self._led_on(duration))
def _led_on(self, duration: int=300):
led_pin.value(1)
self._led_on_timer.init(mode=Timer.ONE_SHOT, period=duration, callback=lambda *_: led_pin.value(0))
def main():
door_alarm = DoorAlarm()
# check door position
door_alarm.check_door(door_pin)
while True:
pressed_key = scan_keypad()
door_alarm.set_key(pressed_key)
time.sleep(DEBOUNCE_TIME) # debounce and delay
if __name__ == '__main__':
main()