# Import necessary modules
from machine import Pin, PWM, I2C # For GPIO pin control, PWM for servo, and I2C for OLED
from time import sleep, ticks_ms, ticks_diff # For delays and time tracking
from dht import DHT22 # Import DHT22 temperature and humidity sensor driver
import keypad # Import custom keypad handling library
import ssd1306 # Import SSD1306 OLED driver
# ========== Pin Definitions ==========
DHTPIN = 32 # DHT22 data pin connected to GPIO32
PIR_PIN = 33 # PIR motion sensor connected to GPIO33
ACTIVE_LED = 25 # Active status LED pin
SERVO_PIN = 14 # Servo motor control pin
GREEN_LED = 12 # Green LED pin (for success)
RED_LED = 13 # Red LED pin (for error)
BUZZER = 26 # Buzzer pin for sound alerts
# Unlock and lock positions for the servo motor (PWM duty cycle)
UNLOCK_POS = 40
LOCK_POS = 115
# ========== Setup Components ==========
dht_sensor = DHT22(Pin(DHTPIN)) # Initialize DHT22 sensor
pir = Pin(PIR_PIN, Pin.IN) # Set PIR sensor as input
active_led = Pin(ACTIVE_LED, Pin.OUT) # Set active LED as output
green_led = Pin(GREEN_LED, Pin.OUT) # Set green LED as output
red_led = Pin(RED_LED, Pin.OUT) # Set red LED as output
buzzer = Pin(BUZZER, Pin.OUT) # Set buzzer as output
# Initialize servo with PWM on given pin at 50Hz (standard for servos)
servo = PWM(Pin(SERVO_PIN), freq=50)
# Set up I2C communication for OLED screen
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000) # I2C0 with fast mode (400kHz)
oled = ssd1306.SSD1306_I2C(128, 64, i2c) # OLED display: 128x64 pixels
# ========== Keypad Setup ==========
KEYPAD_ROWS = [15, 2, 4, 5] # GPIO pins connected to keypad rows
KEYPAD_COLS = [18, 19, 27, 23] # GPIO pins connected to keypad columns
keys = [ # Define key layout
['1','2','3','A'],
['4','5','6','B'],
['7','8','9','C'],
['*','0','#','D']
]
kp = keypad.Keypad(KEYPAD_ROWS, KEYPAD_COLS, keys) # Initialize keypad object
# ========== Variables ==========
locked = True # Initial door lock status
entered_code = '' # Variable to store user-entered PIN
wait_for_code = False # Indicates if the system is waiting for code entry
last_key_time = 0 # Last time a key was pressed (for timing display)
display_real_code = False # Whether to display actual PIN or hide with asterisks
# ========== Functions ==========
# Function to move servo to a given position
def set_servo_position(pos):
servo.duty(pos)
# Function to clear OLED display
def clear_display():
oled.fill(0) # Fill screen with black (clear)
oled.show()
# Function to display two lines of text on OLED
def display_text(line1='', line2=''):
clear_display()
oled.text(line1, 0, 0) # Display first line at top
oled.text(line2, 0, 20) # Display second line below
oled.show()
# Function to update OLED with current lock status
def update_lock_status():
if locked:
display_text("Door Locked") # If locked, show "Door Locked"
else:
display_text("Door Unlocked") # Otherwise, show "Door Unlocked"
# Function to unlock door, activate success signals
def unlock_door():
global locked
locked = False
set_servo_position(UNLOCK_POS) # Move servo to unlock position
green_led.on() # Turn on green LED
buzzer.on() # Turn buzzer on
sleep(0.5) # Wait half second
buzzer.off() # Turn buzzer off
green_led.off() # Turn off green LED
update_lock_status() # Update OLED display
# Function to lock door, deactivate signals
def lock_door():
global locked
locked = True
set_servo_position(LOCK_POS) # Move servo to locked position
green_led.off() # Turn off green LED
red_led.off() # Turn off red LED
buzzer.off() # Make sure buzzer is off
update_lock_status() # Update OLED display
# Handle keypad button presses
def handle_keypad_input(key):
global entered_code, wait_for_code, locked, last_key_time, display_real_code
if locked:
if key == '#' and len(entered_code) > 0: # If # is pressed and code entered
if entered_code == '1234': # Correct PIN
unlock_door()
wait_for_code = False
sleep(5) # Door stays unlocked 5 seconds
lock_door()
else: # Incorrect PIN
display_text("Incorrect", "PIN!")
red_led.on()
buzzer.on()
sleep(1)
buzzer.off()
red_led.off()
update_lock_status()
wait_for_code = False
entered_code = '' # Reset entered code
elif key == 'C' and len(entered_code) > 0: # Clear last entered digit
entered_code = entered_code[:-1]
elif key not in ['#', 'C'] and len(entered_code) < 4: # Add digit if less than 4
entered_code += key
last_key_time = ticks_ms() # Update last key press time
display_real_code = False # Initially hide code
else: # If already unlocked
if key == '*': # Lock door manually
lock_door()
wait_for_code = False
sleep(2)
# Function to display temperature, humidity, and PIN entry on OLED
def show_temp_hum_and_pin(temp, hum, code):
oled.fill(0)
oled.text(f"{temp:.1f}C {hum:.1f}%", 0, 0) # Top: temp and humidity
oled.text("Enter PIN:", 0, 20) # Middle: instruction
if display_real_code: # Show real code if allowed
oled.text(code, 0, 40)
else: # Otherwise show masked '*'
oled.text('*' * len(code), 0, 40)
oled.show()
# ========== Main Loop ==========
def main_loop():
global wait_for_code, display_real_code
update_lock_status() # Show initial lock status
last_display = "" # Track last display to optimize screen updates
while True:
if pir.value() and not wait_for_code: # Detect motion
active_led.on()
display_text("Motion", "Detected")
sleep(1)
clear_display()
wait_for_code = True # Start asking for code after motion detected
if wait_for_code: # If waiting for PIN
try:
dht_sensor.measure() # Read temperature and humidity
temp = dht_sensor.temperature()
hum = dht_sensor.humidity()
# If some time passed after last key, show real PIN
if entered_code and ticks_diff(ticks_ms(), last_key_time) > 1000:
display_real_code = True
# Pack current screen info
new_display = (temp, hum, entered_code, display_real_code)
if new_display != last_display: # If something changed
show_temp_hum_and_pin(temp, hum, entered_code) # Update OLED
last_display = new_display
except OSError: # DHT22 reading failed
display_text("Sensor Error")
key = kp.get_key() # Check keypad
if key:
handle_keypad_input(key) # Process key press
last_display = "" # Force OLED update after key
else: # If not detecting motion
active_led.off()
display_text("System", "Sleeping")
sleep(1)
sleep(0.1) # Small delay to avoid spamming CPU
main_loop() # Start the main loop