import machine
import utime
from machine import Pin, I2C, PWM
from i2c_lcd import I2cLcd

# Define Servo
servo = PWM(Pin(2), freq=50)
led =Pin(15,Pin.OUT)
# I2C configuration for LCD
I2C_ADDR = 0x27
I2C_NUM_ROWS = 2
I2C_NUM_COLS = 16
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
lcd = I2cLcd(i2c, I2C_ADDR, I2C_NUM_ROWS, I2C_NUM_COLS)

# Define row and column pins
ROW_PINS = [13, 12, 14, 27]
COL_PINS = [26, 25, 33, 32]
# Initialize row pins as inputs with pull up
rows = [Pin(pin, Pin.IN,Pin.PULL_UP) for pin in ROW_PINS]
# Initialize column pins as outputs
cols = [Pin(pin, Pin.OUT) for pin in COL_PINS]
keys = [
    ['1', '2', '3', 'A'],
    ['4', '5', '6', 'B'],
    ['7', '8', '9', 'C'],
    ['*', '0', '#', 'D']
]

# Constants
CORRECT_CODE = '1729'
entered_code = ''
door_opened_time = None
DOOR_OPEN_DURATION = 5  # 5 seconds

# Servo Control Functions
def open_door():
    global door_opened_time
    servo.duty(40)  # Adjust duty for fully open position
    lcd.clear()
    lcd.putstr('Door Opened')
    door_opened_time = utime.time()  # Record the time the door was opened

def close_door():
    servo.duty(75)  # Adjust duty for fully closed position
    lcd.clear()
    lcd.putstr('Door Closed')
    utime.sleep(2)
    lcd.clear()
    lcd.putstr('Enter Code:')

def scan_keypad():
    for col_index, col in enumerate(cols):
        col.off()  # Set the current col pin low
        for row_index, row in enumerate(rows):
            if row.value()==0:# Check if the row pin is low
                col.on() # set the col pin to high
                return keys[row_index][col_index] # Return the key from the key map
        col.on()# set the col pin to high after scanning all rows
    return None # Return None if no key is pressed

# Initialize LCD
lcd.clear()
lcd.putstr('Enter Code:')

# Main Loop
while True:
    key = scan_keypad()
    if key:
       
        if key == 'D':
            close_door()
            door_opened_time = None  # Reset the door opened time
        elif len(entered_code) < 4:
            entered_code += key
            lcd.putstr('*')
        if len(entered_code) == 4:
            if entered_code == CORRECT_CODE:
                open_door()
            else:
                lcd.clear()
                lcd.putstr('Incorrect Code')
                utime.sleep(2)
                led.on()
                utime.sleep(2)
                lcd.clear()
                lcd.putstr('Enter Code:')
                led.off()
                utime.sleep(2)
            entered_code = ''
        utime.sleep(0.3)
    
    # Check if the door has been open for more than the allowed duration
    if door_opened_time is not None and (utime.time() - door_opened_time) > DOOR_OPEN_DURATION:
        close_door()
        door_opened_time = None  # Reset the door opened time