import math
import time
import gc
from machine import Pin, I2C
from i2c_lcd import I2cLcd
import ssd1306
# --- Keypad ---
class Keypad:
def __init__(self, rows, cols, keys):
self.rows = [Pin(p, Pin.OUT) for p in rows]
self.cols = [Pin(p, Pin.IN, Pin.PULL_UP) for p in cols] # PULL_UP
self.keys = keys
def get_key(self):
for r_idx, row in enumerate(self.rows):
row.value(0) # activo en bajo
for c_idx, col in enumerate(self.cols):
if not col.value(): # detecta 0 en lugar de 1
time.sleep_ms(20) # debounce
if not col.value(): # confirma
row.value(1)
return self.keys[r_idx][c_idx]
row.value(1)
return None
# --- Asistente Matemático ---
class AsistenteMatematico:
def __init__(self):
self.operaciones = {
'sin': math.sin, 'cos': math.cos, 'tan': math.tan,
'sqrt': math.sqrt, 'pi': math.pi, 'e': math.e, 'pow': pow
}
def resolver(self, expresion):
try:
expresion = expresion.replace('^', '**')
resultado = eval(expresion, {"__builtins__": {}}, self.operaciones)
return round(resultado, 4) if isinstance(resultado, float) else resultado
except ZeroDivisionError:
return "Err:Div/0"
except Exception:
return "Err:Sintaxis"
finally:
gc.collect()
# --- Hardware I2C compartido ---
i2c = I2C(0, sda=Pin(21), scl=Pin(22), freq=400000)
devices = i2c.scan()
print("I2C encontrados:", [hex(d) for d in devices])
# LCD1602 en 0x27
lcd = I2cLcd(i2c, 0x27, 2, 16)
lcd.backlight_on()
# OLED SSD1306 en 0x3C
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
# --- Historial (últimas 3 operaciones para el OLED) ---
historial = []
def agregar_historial(expresion, resultado):
entrada = f"{expresion}={resultado}"
historial.append(entrada)
if len(historial) > 3:
historial.pop(0)
# --- Funciones de display ---
def lcd_mostrar(linea1, linea2=""):
lcd.clear()
lcd.move_to(0, 0)
lcd.putstr(linea1[:16])
if linea2:
lcd.move_to(0, 1)
lcd.putstr(linea2[:16])
def oled_mostrar(estado, consulta="", resultado=""):
oled.fill(0)
# Título / estado
oled.text(estado[:16], 0, 0)
# Separador con fill_rect (barra de 1px de alto)
oled.fill_rect(0, 10, 128, 1, 1)
# Expresión actual
if consulta:
oled.text(consulta[:16], 0, 14)
# Resultado
if resultado:
oled.text(str(resultado)[:16], 0, 26)
# Separador historial
oled.fill_rect(0, 38, 128, 1, 1)
oled.text("Hist:", 0, 41)
for i, entrada in enumerate(historial[-2:]):
oled.text(entrada[:16], 0, 51 + i * 10)
oled.show()
def mostrar(linea1, linea2="", estado_oled="", consulta_oled="", resultado_oled=""):
print(f"LCD> {linea1} | {linea2}")
lcd_mostrar(linea1, linea2)
oled_mostrar(
estado_oled or linea1,
consulta_oled,
resultado_oled
)
# --- Teclado ---
FILAS = [18, 5, 17, 16]
COLUMNAS = [4, 0, 2, 15]
TECLAS = [
['1', '2', '3', '/'],
['4', '5', '6', '*'],
['7', '8', '9', '-'],
['C', '0', '=', '+']
]
teclado = Keypad(FILAS, COLUMNAS, TECLAS)
asistente = AsistenteMatematico()
# --- Pantalla de bienvenida ---
consulta_actual = ""
lcd_mostrar("Calculadora IA", "Listo!")
oled.fill(0)
oled.text("Calculadora IA", 0, 0)
oled.fill_rect(0, 10, 128, 1, 1) # <-- reemplaza hline
oled.text("Ingresa una", 0, 20)
oled.text("expresion...", 0, 32)
oled.fill_rect(0, 44, 128, 1, 1) # <-- reemplaza hline
oled.text("LCD + OLED", 19, 52)
oled.show()
time.sleep(2)
lcd_mostrar("")
oled_mostrar("Esperando...", "", "")
# --- Loop principal ---
while True:
tecla = teclado.get_key()
if tecla:
if tecla == '=':
if consulta_actual:
# Calculando...
lcd_mostrar(consulta_actual, "Calculando...")
oled_mostrar("Calculando...", consulta_actual, "")
time.sleep(0.3)
resultado = asistente.resolver(consulta_actual)
# Mostrar resultado en ambas pantallas
lcd_mostrar(consulta_actual, f"={resultado}")
oled_mostrar("Resultado:", consulta_actual, f"={resultado}")
# Guardar en historial
agregar_historial(consulta_actual, resultado)
# El resultado se convierte en nueva entrada
consulta_actual = str(resultado)
elif tecla == 'C':
consulta_actual = ""
lcd_mostrar("Borrado", "")
oled_mostrar("Borrado", "", "")
time.sleep(0.5)
lcd_mostrar("")
oled_mostrar("Esperando...", "", "")
else:
consulta_actual += tecla
lcd_mostrar(consulta_actual)
oled_mostrar("Entrada:", consulta_actual, "")
time.sleep(0.05)