from machine import Pin, I2C
import time
# =====================================================
# CONFIGURACION GENERAL
# =====================================================
# Pantalla LCD I2C
PIN_SDA = 0 # GP0
PIN_SCL = 1 # GP1
# LEDs
PIN_LED_VERDE = 10
PIN_LED_ROJO = 11
# Clave correcta de acceso
CLAVE_CORRECTA = "#*A2"
LONGITUD_CLAVE = 4
# Tiempo que permanece visible el resultado
DURACION_RESULTADO_MS = 1500
# =====================================================
# CONFIGURACION DE LOS LEDS
# =====================================================
led_verde = Pin(PIN_LED_VERDE, Pin.OUT)
led_rojo = Pin(PIN_LED_ROJO, Pin.OUT)
led_verde.value(0)
led_rojo.value(0)
# =====================================================
# CONFIGURACION DEL BUS I2C
# =====================================================
i2c = I2C(
0,
sda=Pin(PIN_SDA),
scl=Pin(PIN_SCL),
freq=100000
)
print()
print("================================")
print("BUSCANDO PANTALLA LCD I2C")
print("================================")
dispositivos_i2c = i2c.scan()
print("Dispositivos encontrados:", dispositivos_i2c)
if 0x27 in dispositivos_i2c:
DIRECCION_LCD = 0x27
elif 0x3F in dispositivos_i2c:
DIRECCION_LCD = 0x3F
elif len(dispositivos_i2c) > 0:
DIRECCION_LCD = dispositivos_i2c[0]
else:
print()
print("ERROR: No se encontro la pantalla LCD.")
print("Revise las conexiones:")
print("LCD SDA -> GP0")
print("LCD SCL -> GP1")
print("LCD GND -> GND")
print("LCD VCC -> Alimentacion")
print()
# El LED rojo parpadea cuando no encuentra el LCD
while True:
led_rojo.toggle()
time.sleep_ms(300)
print("LCD encontrada en:", hex(DIRECCION_LCD))
# =====================================================
# CONTROLADOR LCD 1602 I2C
# =====================================================
class LCD1602_I2C:
LCD_CLEAR = 0x01
LCD_HOME = 0x02
LCD_ENTRY_MODE = 0x04
LCD_DISPLAY_CONTROL = 0x08
LCD_FUNCTION_SET = 0x20
LCD_SET_DDRAM = 0x80
LCD_ENTRY_LEFT = 0x02
LCD_DISPLAY_ON = 0x04
LCD_CURSOR_OFF = 0x00
LCD_BLINK_OFF = 0x00
LCD_2_LINE = 0x08
LCD_4_BIT = 0x00
LCD_5X8_DOTS = 0x00
RS = 0x01
ENABLE = 0x04
BACKLIGHT = 0x08
def __init__(self, bus_i2c, direccion, columnas=16, filas=2):
self.i2c = bus_i2c
self.direccion = direccion
self.columnas = columnas
self.filas = filas
self.backlight = self.BACKLIGHT
time.sleep_ms(50)
# Inicializacion de la pantalla en modo de 4 bits
self._escribir_4_bits(0x30)
time.sleep_ms(5)
self._escribir_4_bits(0x30)
time.sleep_ms(1)
self._escribir_4_bits(0x30)
time.sleep_ms(1)
self._escribir_4_bits(0x20)
time.sleep_ms(1)
# Configuracion para LCD de 2 filas
self.comando(
self.LCD_FUNCTION_SET
| self.LCD_2_LINE
| self.LCD_4_BIT
| self.LCD_5X8_DOTS
)
# Encender pantalla y apagar cursor
self.comando(
self.LCD_DISPLAY_CONTROL
| self.LCD_DISPLAY_ON
| self.LCD_CURSOR_OFF
| self.LCD_BLINK_OFF
)
self.limpiar()
# Escritura de izquierda a derecha
self.comando(
self.LCD_ENTRY_MODE
| self.LCD_ENTRY_LEFT
)
def _escribir_i2c(self, dato):
self.i2c.writeto(
self.direccion,
bytes([dato | self.backlight])
)
def _pulso_enable(self, dato):
self._escribir_i2c(dato | self.ENABLE)
time.sleep_us(1)
self._escribir_i2c(dato & ~self.ENABLE)
time.sleep_us(50)
def _escribir_4_bits(self, dato):
self._escribir_i2c(dato)
self._pulso_enable(dato)
def enviar(self, dato, modo):
parte_alta = dato & 0xF0
parte_baja = (dato << 4) & 0xF0
self._escribir_4_bits(parte_alta | modo)
self._escribir_4_bits(parte_baja | modo)
def comando(self, dato):
self.enviar(dato, 0)
def escribir_caracter(self, caracter):
self.enviar(ord(caracter), self.RS)
def escribir(self, texto):
for caracter in texto:
self.escribir_caracter(caracter)
def limpiar(self):
self.comando(self.LCD_CLEAR)
time.sleep_ms(2)
def posicion(self, columna, fila):
direcciones = [0x00, 0x40, 0x14, 0x54]
if fila >= self.filas:
fila = self.filas - 1
direccion = direcciones[fila] + columna
self.comando(self.LCD_SET_DDRAM | direccion)
def encender_luz(self):
self.backlight = self.BACKLIGHT
self._escribir_i2c(0)
def apagar_luz(self):
self.backlight = 0
self._escribir_i2c(0)
# Crear la pantalla LCD
lcd = LCD1602_I2C(
i2c,
DIRECCION_LCD,
columnas=16,
filas=2
)
lcd.encender_luz()
# =====================================================
# CONFIGURACION DEL TECLADO MATRICIAL 4x4
# =====================================================
# Filas:
# R1 -> GP2
# R2 -> GP3
# R3 -> GP4
# R4 -> GP5
PINES_FILAS = [2, 3, 4, 5]
# Columnas:
# C1 -> GP6
# C2 -> GP7
# C3 -> GP8
# C4 -> GP9
PINES_COLUMNAS = [6, 7, 8, 9]
TECLAS = [
["1", "2", "3", "A"],
["4", "5", "6", "B"],
["7", "8", "9", "C"],
["*", "0", "#", "D"]
]
filas = []
for numero_pin in PINES_FILAS:
pin = Pin(numero_pin, Pin.OUT)
pin.value(0)
filas.append(pin)
columnas = []
for numero_pin in PINES_COLUMNAS:
pin = Pin(numero_pin, Pin.IN, Pin.PULL_DOWN)
columnas.append(pin)
# =====================================================
# VARIABLES DEL SISTEMA
# =====================================================
clave_ingresada = ""
mostrando_resultado = False
tiempo_inicio_resultado = 0
tecla_anterior = None
tecla_estable = None
tiempo_ultimo_cambio = time.ticks_ms()
# Tiempo para eliminar rebotes del teclado
TIEMPO_REBOTE_MS = 30
# =====================================================
# LEER TECLADO MATRICIAL
# =====================================================
def leer_tecla_directa():
# Apagar todas las filas
for fila in filas:
fila.value(0)
# Revisar las cuatro filas
for indice_fila in range(4):
filas[indice_fila].value(1)
time.sleep_us(100)
# Revisar las cuatro columnas
for indice_columna in range(4):
if columnas[indice_columna].value() == 1:
tecla = TECLAS[indice_fila][indice_columna]
filas[indice_fila].value(0)
return tecla
filas[indice_fila].value(0)
return None
def obtener_tecla_nueva():
global tecla_anterior
global tecla_estable
global tiempo_ultimo_cambio
tecla_actual = leer_tecla_directa()
tiempo_actual = time.ticks_ms()
# Detectar cambio de tecla
if tecla_actual != tecla_anterior:
tecla_anterior = tecla_actual
tiempo_ultimo_cambio = tiempo_actual
# Confirmar que la tecla permanezca estable
if time.ticks_diff(
tiempo_actual,
tiempo_ultimo_cambio
) >= TIEMPO_REBOTE_MS:
if tecla_actual != tecla_estable:
tecla_estable = tecla_actual
# Registrar solamente cuando se presiona
if tecla_estable is not None:
return tecla_estable
return None
# =====================================================
# MOSTRAR PANTALLA PRINCIPAL
# =====================================================
def mostrar_solicitud_clave():
global clave_ingresada
global mostrando_resultado
clave_ingresada = ""
mostrando_resultado = False
led_verde.value(0)
led_rojo.value(0)
lcd.limpiar()
lcd.posicion(1, 0)
lcd.escribir("INGRESAR CLAVE")
lcd.posicion(0, 1)
lcd.escribir("Clave: ")
print()
print("================================")
print("SISTEMA LISTO")
print("Ingrese una clave de 4 caracteres")
print("Presione D para borrar")
print("================================")
# =====================================================
# LIMPIAR LA ZONA DE LA CLAVE
# =====================================================
def limpiar_zona_clave():
lcd.posicion(7, 1)
lcd.escribir(" ")
# =====================================================
# BORRAR TODA LA CLAVE
# =====================================================
def borrar_clave():
global clave_ingresada
clave_ingresada = ""
limpiar_zona_clave()
lcd.posicion(7, 1)
print("Clave borrada")
# =====================================================
# COMPROBAR LA CLAVE
# =====================================================
def comprobar_clave():
global mostrando_resultado
global tiempo_inicio_resultado
lcd.limpiar()
if clave_ingresada == CLAVE_CORRECTA:
lcd.posicion(1, 0)
lcd.escribir("CLAVE CORRECTA")
lcd.posicion(1, 1)
lcd.escribir("ACCESO PERMIT.")
led_verde.value(1)
led_rojo.value(0)
print("CLAVE CORRECTA")
print("ACCESO PERMITIDO")
else:
lcd.posicion(0, 0)
lcd.escribir("CLAVE INCORRECTA")
lcd.posicion(2, 1)
lcd.escribir("ACCESO NEGADO")
led_verde.value(0)
led_rojo.value(1)
print("CLAVE INCORRECTA")
print("ACCESO NEGADO")
mostrando_resultado = True
tiempo_inicio_resultado = time.ticks_ms()
# =====================================================
# PROCESAR TECLA PRESIONADA
# =====================================================
def procesar_tecla(tecla):
global clave_ingresada
print("Tecla presionada:", tecla)
# La tecla D borra toda la clave
if tecla == "D":
borrar_clave()
return
# Todas estas teclas pueden formar parte de una clave
teclas_validas = "0123456789ABC*#"
if tecla not in teclas_validas:
print("Tecla ignorada")
return
# No permitir mas de cuatro caracteres
if len(clave_ingresada) >= LONGITUD_CLAVE:
return
# Posicion donde aparecerá el carácter
posicion_caracter = 7 + len(clave_ingresada)
# Mostrar inmediatamente el carácter en la pantalla
lcd.posicion(posicion_caracter, 1)
lcd.escribir(tecla)
# Guardar el carácter
clave_ingresada += tecla
print("Clave ingresada:", clave_ingresada)
# Comprobar automáticamente al ingresar 4 caracteres
if len(clave_ingresada) == LONGITUD_CLAVE:
comprobar_clave()
# =====================================================
# INICIO DEL PROGRAMA
# =====================================================
print()
print("INICIANDO SISTEMA DE ACCESO...")
mostrar_solicitud_clave()
# =====================================================
# BUCLE PRINCIPAL
# =====================================================
while True:
if mostrando_resultado:
tiempo_actual = time.ticks_ms()
if time.ticks_diff(
tiempo_actual,
tiempo_inicio_resultado
) >= DURACION_RESULTADO_MS:
mostrar_solicitud_clave()
else:
tecla_presionada = obtener_tecla_nueva()
if tecla_presionada is not None:
procesar_tecla(tecla_presionada)
# Pausa pequeña para estabilidad
time.sleep_ms(5)