from machine import Pin, I2C, PWM
import time
# ==================================================
# LCD 16x2 I2C
# SDA = GP0
# SCL = GP1
# ==================================================
i2c = I2C(
0,
sda=Pin(0),
scl=Pin(1),
freq=100000
)
LCD_ADDR = 0x27
BL = 0x08
EN = 0x04
RS = 0x01
def expander_write(data):
i2c.writeto(
LCD_ADDR,
bytes([data | BL])
)
def pulse_enable(data):
expander_write(data | EN)
time.sleep_us(50)
expander_write(data & ~EN)
time.sleep_us(50)
def send4bits(data):
expander_write(data)
pulse_enable(data)
def lcd_send(value, mode):
send4bits((value & 0xF0) | mode)
send4bits(((value << 4) & 0xF0) | mode)
def lcd_cmd(command):
lcd_send(command, 0)
def lcd_data(data):
lcd_send(data, RS)
def lcd_clear():
lcd_cmd(0x01)
time.sleep_ms(5)
def lcd_init():
time.sleep_ms(50)
send4bits(0x30)
time.sleep_ms(5)
send4bits(0x30)
time.sleep_ms(1)
send4bits(0x30)
send4bits(0x20)
lcd_cmd(0x28)
lcd_cmd(0x0C)
lcd_cmd(0x06)
lcd_clear()
def lcd_line(linea):
if linea == 0:
lcd_cmd(0x80)
else:
lcd_cmd(0xC0)
def lcd_print(texto):
texto = str(texto)
for caracter in texto:
lcd_data(ord(caracter))
def completar_linea(texto):
texto = str(texto)
if len(texto) > 16:
texto = texto[:16]
cantidad_espacios = 16 - len(texto)
return texto + (" " * cantidad_espacios)
def lcd_show(linea1, linea2=""):
lcd_clear()
lcd_line(0)
lcd_print(completar_linea(linea1))
lcd_line(1)
lcd_print(completar_linea(linea2))
# ==================================================
# TECLADO MATRICIAL 4x4
#
# R1 = GP2
# R2 = GP3
# R3 = GP4
# R4 = GP5
#
# C1 = GP6
# C2 = GP7
# C3 = GP8
# C4 = GP9
# ==================================================
teclas = [
["1", "2", "3", "A"],
["4", "5", "6", "B"],
["7", "8", "9", "C"],
["*", "0", "#", "D"]
]
filas = [
Pin(2, Pin.OUT),
Pin(3, Pin.OUT),
Pin(4, Pin.OUT),
Pin(5, Pin.OUT)
]
columnas = [
Pin(6, Pin.IN, Pin.PULL_UP),
Pin(7, Pin.IN, Pin.PULL_UP),
Pin(8, Pin.IN, Pin.PULL_UP),
Pin(9, Pin.IN, Pin.PULL_UP)
]
# Todas las filas empiezan en nivel alto
for fila in filas:
fila.value(1)
def leer_tecla():
for numero_fila in range(4):
# Colocar todas las filas en nivel alto
for fila in filas:
fila.value(1)
# Activar solamente la fila que se está revisando
filas[numero_fila].value(0)
time.sleep_us(200)
for numero_columna in range(4):
# Con PULL_UP, una tecla presionada genera nivel 0
if columnas[numero_columna].value() == 0:
# Antirrebote
time.sleep_ms(30)
# Confirmar que la tecla continúa presionada
if columnas[numero_columna].value() == 0:
tecla = teclas[numero_fila][numero_columna]
# Esperar hasta que el usuario suelte la tecla
while columnas[numero_columna].value() == 0:
time.sleep_ms(10)
filas[numero_fila].value(1)
# Evitar una segunda lectura accidental
time.sleep_ms(80)
print("Tecla detectada:", tecla)
return tecla
filas[numero_fila].value(1)
return None
# ==================================================
# LEDS
#
# Verde 1 = GP16
# Verde 2 = GP17
# Rojo 1 = GP18
# Rojo 2 = GP19
# ==================================================
led_verde_1 = Pin(16, Pin.OUT)
led_verde_2 = Pin(17, Pin.OUT)
led_rojo_1 = Pin(18, Pin.OUT)
led_rojo_2 = Pin(19, Pin.OUT)
def apagar_leds():
led_verde_1.value(0)
led_verde_2.value(0)
led_rojo_1.value(0)
led_rojo_2.value(0)
def encender_verdes():
led_verde_1.value(1)
led_verde_2.value(1)
led_rojo_1.value(0)
led_rojo_2.value(0)
def encender_rojos():
led_verde_1.value(0)
led_verde_2.value(0)
led_rojo_1.value(1)
led_rojo_2.value(1)
# ==================================================
# SERVOMOTOR
# Señal = GP14
# ==================================================
servo = PWM(Pin(14))
servo.freq(50)
def mover_servo(angulo):
if angulo < 0:
angulo = 0
if angulo > 180:
angulo = 180
# Pulso aproximado de 500 us a 2500 us
pulso_us = 500 + (angulo * 2000 / 180)
# Periodo de 20 000 us para una frecuencia de 50 Hz
duty = int(pulso_us * 65535 / 20000)
servo.duty_u16(duty)
print("Servo en:", angulo, "grados")
# ==================================================
# CONFIGURACIÓN
# ==================================================
clave_correcta = "#*B1"
# Verificar que la pantalla sea detectada
dispositivos = i2c.scan()
print("Dispositivos I2C encontrados:", dispositivos)
if LCD_ADDR not in dispositivos:
raise Exception(
"No se detecta la pantalla LCD I2C en 0x27"
)
# ==================================================
# INICIO DEL SISTEMA
# ==================================================
lcd_init()
apagar_leds()
# Posición inicial del servomotor
mover_servo(45)
lcd_show(
"SISTEMA DE",
"ACCESO"
)
time.sleep(2)
# ==================================================
# PROGRAMA PRINCIPAL
# ==================================================
while True:
clave_ingresada = ""
apagar_leds()
lcd_show(
"INGRESAR CLAVE",
""
)
# Esperar cuatro caracteres
while len(clave_ingresada) < 4:
tecla = leer_tecla()
if tecla is not None:
clave_ingresada = clave_ingresada + tecla
print(
"Clave ingresada:",
clave_ingresada
)
# Mostrar asteriscos en la segunda línea
texto_oculto = "*" * len(clave_ingresada)
lcd_line(1)
lcd_print(
completar_linea(texto_oculto)
)
time.sleep_ms(5)
# ==============================================
# CLAVE CORRECTA
# ==============================================
if clave_ingresada == clave_correcta:
lcd_show(
"Clave Correcta",
"ACCESO PERMITIDO"
)
encender_verdes()
mover_servo(120)
# ==============================================
# CLAVE INCORRECTA
# ==============================================
else:
lcd_show(
"Clave Incorrecta",
"ACCESO DENEGADO"
)
encender_rojos()
mover_servo(45)
# Mantener el resultado durante 3 segundos
time.sleep(3)
apagar_leds()
# El ciclo vuelve a mostrar INGRESAR CLAVE