from machine import Pin, I2C, ADC
from ssd1306 import SSD1306_I2C
import math
import time
# === Configuración del display y ADCs ===
WIDTH = 128
HEIGHT = 64
i2c = I2C(0, scl=Pin(5), sda=Pin(4))
oled = SSD1306_I2C(WIDTH, HEIGHT, i2c)
adc_x = ADC(26) # Control de rango X
adc_y = ADC(27) # Control de rango Y
# === Análisis Léxico ===
def es_numero(c):
return c.isdigit() or c == '.'
def es_letra(c):
return c.isalpha()
def lexer(expr):
tokens = []
i = 0
while i < len(expr):
c = expr[i]
if c in ' \t':
i += 1
elif c in '+-*/^(),':
tokens.append((c, c))
i += 1
elif es_numero(c):
num = ''
while i < len(expr) and es_numero(expr[i]):
num += expr[i]
i += 1
tokens.append(('NUM', float(num)))
elif es_letra(c):
ident = ''
while i < len(expr) and (es_letra(expr[i]) or expr[i].isdigit()):
ident += expr[i]
i += 1
tokens.append(('ID', ident))
else:
raise ValueError("Caracter inválido: " + c)
return tokens
# === Parser y Evaluador ===
class Parser:
def __init__(self, tokens, variables):
self.tokens = tokens
self.variables = variables
self.pos = 0
def actual(self):
return self.tokens[self.pos] if self.pos < len(self.tokens) else (None, None)
def avanzar(self):
self.pos += 1
def match(self, expected):
if self.actual()[0] == expected:
self.avanzar()
else:
raise ValueError("Se esperaba '{}'".format(expected))
def factor(self):
tok_type, tok_val = self.actual()
if tok_type == 'NUM':
self.avanzar()
return tok_val
elif tok_type == 'ID':
self.avanzar()
if self.actual()[0] == '(':
self.avanzar()
arg = self.expr()
self.match(')')
return self.evaluar_funcion(tok_val, arg)
else:
if tok_val not in self.variables:
raise ValueError("Variable no definida: " + tok_val)
return self.variables[tok_val]
elif tok_type == '(':
self.avanzar()
val = self.expr()
self.match(')')
return val
elif tok_type == '-':
self.avanzar()
return -self.factor()
else:
raise ValueError("Error de sintaxis en 'factor'")
def evaluar_funcion(self, nombre, arg):
funciones = {
'sin': math.sin,
'cos': math.cos,
'tan': math.tan,
'asin': math.asin,
'acos': math.acos,
'atan': math.atan,
'log': math.log10,
'ln': math.log,
'sqrt': math.sqrt,
'exp': math.exp
}
if nombre in funciones:
return funciones[nombre](arg)
else:
raise ValueError("Función no reconocida: " + nombre)
def potencia(self):
val = self.factor()
while self.actual()[0] == '^':
self.avanzar()
val = val ** self.factor()
return val
def termino(self):
val = self.potencia()
while self.actual()[0] in ('*', '/'):
op = self.actual()[0]
self.avanzar()
val = val * self.potencia() if op == '*' else val / self.potencia()
return val
def expr(self):
val = self.termino()
while self.actual()[0] in ('+', '-'):
op = self.actual()[0]
self.avanzar()
val = val + self.termino() if op == '+' else val - self.termino()
return val
# === Función para graficar ===
def graficar_expr(expr):
tokens = lexer(expr)
sclx = scly = 1
delta = 0.1
# Rango dinámico por potenciómetro
x_range = 5 + int(adc_x.read_u16() * 10 / 65535)
y_range = 1 + int(adc_y.read_u16() * 5 / 65535)
xmin = -x_range
xmax = x_range
ymin = -y_range
ymax = y_range
dx = (WIDTH * sclx) / (xmax - xmin)
dy = (HEIGHT * scly) / (ymax - ymin)
x0 = int(-xmin * dx / sclx)
y0 = int(HEIGHT * ymax / (ymax - ymin))
oled.fill(0)
oled.hline(0, y0, WIDTH, 1)
oled.vline(x0, 0, HEIGHT, 1)
x = xmin
while x < xmax:
variables = {'x': x}
parser = Parser(tokens, variables)
try:
y = parser.expr()
xp = int((x - xmin) * dx / sclx)
yp = int(HEIGHT - ((y - ymin) * dy / scly))
if 0 <= xp < WIDTH and 0 <= yp < HEIGHT:
oled.pixel(xp, yp, 1)
except:
pass
x += delta
oled.show()
# === Bucle principal ===
def main():
expr = input("Ingrese función de x (ej. sin(x), x^2+2*x): ")
while True:
graficar_expr(expr)
time.sleep(0.2)
# === Ejecutar ===
main()