from machine import Pin, PWM ,I2C
from time import sleep
from i2c_lcd import I2cLcd
from keypad import Keypad
from safe_state import SafeState

# Locking mechanism definitions

SERVO_LOCK_POS = 20
SERVO_UNLOCK_POS = 90
servo_pin = Pin(6, Pin.OUT)
pwm = PWM(servo_pin)
pwm.freq(50)



# Creates a function for mapping the 0 to 180 degrees to 20 to 120 pwm duty values
def map(x, in_min, in_max, out_min, out_max):
    return int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)

# Creates another function for turning the servo according to input angle
def servo(pwm_pin, angle):
    pwm.duty_u16(map(angle, 0, 180, 20, 120))


# Display
i2c = I2C(0, sda=Pin(0), scl=Pin(21), freq=400000)
DEFAULT_I2C_ADDR = i2c.scan()[0]
lcd = I2cLcd(i2c, DEFAULT_I2C_ADDR, 2, 16)
# Keypad setup
keypad_rows = [5, 4, 3, 2]
keypad_cols = [28, 27, 26, 22]  # Analog pins on Raspberry Pi Pico
keypad = Keypad(keypad_rows, keypad_cols, ['1', '2', '3', 'A', '4', '5', '6', 'B', '7', '8', '9', 'C', '*', '0', '#', 'D'])

# SafeState stores the secret code in EEPROM
safe_state = SafeState()

def lock():
    pwm.duty_u16(SERVO_LOCK_POS)
    safe_state.lock()

def unlock():
    pwm.duty_u16(SERVO_UNLOCK_POS)

def show_startup_message():
    lcd.clear()
    lcd.move_to(0,0)
    lcd.putstr("Welcome!")
    sleep(1)

def input_secret_code():
    lcd.clear()
    lcd.putstr("[____]")
    code = ''
    while len(code) < 4:
        key = keypad.get_key()
        if key and key.isdigit():
            lcd.putstr('*')
            code += key
    return code

def show_wait_screen(delay_millis):
    lcd.clear()
    lcd.putstr("[..........]")
    sleep(delay_millis / 1000)

def set_new_code():
    lcd.clear()
    lcd.putstr("Enter new code:")
    new_code = input_secret_code()

    lcd.clear()
    lcd.putstr("Confirm new code")
    confirm_code = input_secret_code()

    if new_code == confirm_code:
        safe_state.set_code(new_code)
        return True
    else:
        lcd.clear()
        lcd.putstr("Code mismatch\nSafe not locked!")
        sleep(2)
        return False

def show_unlock_message():
    lcd.clear()
    lcd.putstr("Unlocked!")
    sleep(1)

def safe_unlocked_logic():
    lcd.clear()
    lcd.move_to(0,0)
    lcd.putstr('# to unlock')

    if safe_state.has_code():
        lcd.clear()
        lcd.move_to(0, 1)
        lcd.putstr("A = new code")
        new_code_needed = False
    else:
        new_code_needed = True

    key = ''
    while key != 'A' and key != '#':
        key = keypad.get_key()

    ready_to_lock = True
    if key == 'A' or new_code_needed:
        ready_to_lock = set_new_code()

    if ready_to_lock:
        lcd.clear()
        safe_state.lock()
        lock()
        show_wait_screen(100)

def safe_locked_logic():
    lcd.clear()
    lcd.putstr(" Safe Locked! ")

    user_code = input_secret_code()
    unlocked_successfully = safe_state.unlock(user_code)
    show_wait_screen(200)

    if unlocked_successfully:
        show_unlock_message()
        unlock()
    else:
        lcd.clear()
        lcd.putstr("Access Denied!")
        show_wait_screen(1000)

def setup():
    pwm.freq(50)  # Set PWM frequency
    pwm.duty_u16(SERVO_UNLOCK_POS)  # Make sure the servo is initially unlocked
    show_startup_message()

def loop():
    if safe_state.locked():
        safe_locked_logic()
    else:
        safe_unlocked_logic()

if __name__ == "__main__":
    setup()
    while True:
        loop()









BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT