from machine import Pin, I2C, ADC, Timer
import utime
import math
import micropython
from ssd1306 import SSD1306_I2C
micropython.alloc_emergency_exception_buf(100)
# ============================================================
# CONFIGURACIÓN DE HARDWARE (Pico W + Red R-2R Ph.D. German)
# ============================================================
pines = [2, 3, 4, 5, 6, 7, 8, 9, 10]
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=400000)
oled = SSD1306_I2C(128, 64, i2c)
vsys_adc = ADC(Pin(26))
btn_set = Pin(14, Pin.IN, Pin.PULL_UP)
btn_rst = Pin(15, Pin.IN, Pin.PULL_UP)
# ============================================================
# ESTADO GLOBAL DEL SISTEMA
# ============================================================
segundos = 0
minutos = 0
horas = 0
modo_actual = 0
NUM_MODOS = 4
DEBOUNCE_MS = 200
ultima_irq_set = 0
ultima_irq_rst = 0
NOMBRES_MODOS = [
"Problema 1",
"Problema 2",
"Problema 3",
"Problema 4",
]
# ============================================================
# FUNCIONES BASE
# ============================================================
def GPIO_SALP(valor, listagpio):
pines_gpio = [Pin(pin, Pin.OUT) for pin in listagpio]
ii = 1
saltxt = ""
for i in range(8):
if valor & ii:
pines_gpio[i].value(1)
saltxt = saltxt + "1"
else:
pines_gpio[i].value(0)
saltxt = saltxt + "0"
ii = ii << 1
return saltxt
# ============================================================
# INTERFAZ OLED - BATERÍA
# ============================================================
def leer_vsys_pct():
raw = vsys_adc.read_u16()
v_pot = (raw / 65535) * 3.3
vreal = 3.0 + (v_pot / 3.3) * (4.2 - 3.0)
pct = int((vreal - 3.0) / (4.2 - 3.0) * 100)
return max(0, min(100, pct))
def dibujar_icono_bat(x, y, pct):
oled.rect(x, y, 16, 8, 1)
oled.fill_rect(x+16, y+2, 2, 4, 1)
ancho = int(13 * pct / 100)
if ancho > 0:
oled.fill_rect(x+2, y+2, ancho, 4, 1)
def dibujar_barra():
oled.fill_rect(0, 0, 128, 15, 0)
t = "{:02d}:{:02d}:{:02d}".format(horas, minutos, segundos)
oled.text(t, 0, 3)
pct = leer_vsys_pct()
dibujar_icono_bat(110, 3, pct)
oled.text("{:3d}%".format(pct), 76, 3)
oled.hline(0, 14, 128, 1)
# ============================================================
# FUNCIÓN AUXILIAR: evalúa la serie en un punto x
# ============================================================
def calcular_fx(x, w0, a0, nmax, modo):
sum_val = 0.0
if modo == 0:
for n in range(1, nmax + 1):
an = (2.0 / (math.pi * n ** 2)) * (1.0 - math.cos(n * math.pi / 2.0))
bn = (2.0 / (n * math.pi)) * math.sin(n * math.pi / 2.0)
sum_val += an * math.cos(n * w0 * x) + bn * math.sin(n * w0 * x)
elif modo == 1:
for n in range(1, nmax + 1):
an = (-2.0 / (n * math.pi)) * math.sin(n * math.pi / 2.0)
bn = ((2.0 / (n * math.pi)) * (1.0 - math.cos(n * math.pi / 2.0))
+ (2.0 / (n * math.pi)) * math.sin(n * math.pi / 2.0))
sum_val += an * math.cos(n * w0 * x) + bn * math.sin(n * w0 * x)
elif modo == 2:
for n in range(1, nmax + 1):
an = (2.0 / (n * math.pi)) * math.sin(n * math.pi / 2.0)
sum_val += an * math.cos(n * w0 * x)
elif modo == 3:
for n in range(1, nmax + 1):
factor = 1.0 - math.exp(-0.5) * ((-1) ** n)
denominador = 1.0 + 4.0 * (math.pi ** 2) * (n ** 2)
an = (2.0 * factor) / denominador
bn = (4.0 * math.pi * n * factor) / denominador
sum_val += an * math.cos(n * w0 * x) + bn * math.sin(n * w0 * x)
return (a0 / 2.0) + sum_val
# ============================================================
# FOURIER - SIN append, DOS PASADAS, UN SOLO oled.show()
# ============================================================
def ProcesarFourier(nmax, modo):
# Parámetros de dominio y DC propios de cada problema
if modo == 0:
xmin = -2.0 * math.pi
xmax = 2.0 * math.pi
delta = 0.1
T = 4.0 * math.pi
a0 = (1.0 + math.pi) / 2.0
elif modo == 1:
xmin = -2.0
xmax = 2.0
delta = 0.05
T = 4.0
a0 = -0.5
elif modo == 2:
xmin = -0.5
xmax = 0.5
delta = 0.01
T = 1.0
a0 = 1.0
elif modo == 3:
xmin = -0.5
xmax = 0.5
delta = 0.01
T = 1.0
a0 = 2.0 * (1.0 - math.exp(-0.5))
w0 = 2.0 * math.pi / T
# --- PASADA 1: min / max ---
mn = 1e9
mx = -1e9
x = xmin
while x <= xmax:
fx = calcular_fx(x, w0, a0, nmax, modo)
if fx < mn: mn = fx
if fx > mx: mx = fx
x += delta
rng = mx - mn if mx != mn else 1.0
# --- Preparar buffer OLED (sin show todavía) ---
oled.fill_rect(0, 16, 128, 48, 0) # borra zona de onda
oled.text(NOMBRES_MODOS[modo], 0, 17) # nombre del modo
# --- PASADA 2: DAC + plotter + píxeles en buffer ---
x = xmin
columna_px = 0
while x <= xmax:
fx = calcular_fx(x, w0, a0, nmax, modo)
# DAC R-2R
val_dac = int(((fx - mn) / rng) * 255)
GPIO_SALP(val_dac, pines)
# Plotter serial de Wokwi (siempre activo)
print(fx)
# Píxel en buffer OLED (zona baja filas 29-63)
py = 63 - int(((fx - mn) / rng) * 34)
py = max(29, min(63, py))
if columna_px < 128:
oled.pixel(columna_px, py, 1)
columna_px += 1
x += delta
# --- UN SOLO oled.show(): barra + onda juntas ---
dibujar_barra() # escribe en buffer, sin show
oled.show() # transfiere TODO el buffer de una vez
# ============================================================
# ISRs
# ============================================================
def isr_timer(timer):
global segundos, minutos, horas
segundos += 1
if segundos >= 60:
segundos = 0
minutos += 1
if minutos >= 60:
minutos = 0
horas += 1
if horas >= 24:
horas = 0
def isr_btn_set(pin):
global modo_actual, ultima_irq_set
ahora = utime.ticks_ms()
if utime.ticks_diff(ahora, ultima_irq_set) > DEBOUNCE_MS:
ultima_irq_set = ahora
modo_actual = (modo_actual + 1) % NUM_MODOS
def isr_btn_rst(pin):
global modo_actual, ultima_irq_rst
ahora = utime.ticks_ms()
if utime.ticks_diff(ahora, ultima_irq_rst) > DEBOUNCE_MS:
ultima_irq_rst = ahora
modo_actual = (modo_actual - 1) % NUM_MODOS
# ============================================================
# INICIALIZACIÓN
# ============================================================
timer_hw = Timer()
timer_hw.init(period=1000, mode=Timer.PERIODIC, callback=isr_timer)
btn_set.irq(trigger=Pin.IRQ_FALLING, handler=isr_btn_set)
btn_rst.irq(trigger=Pin.IRQ_FALLING, handler=isr_btn_rst)
oled.fill(0)
oled.text("Lab 12", 0, 0)
oled.text("Estudiante", 0, 10)
oled.text(" Nina Diego y", 10, 35)
oled.text(" Quispe Coronel ", 10, 50)
oled.show()
utime.sleep_ms(2000)
oled.fill(0)
n_armonicos = 15
# ============================================================
# BUCLE PRINCIPAL - ProcesarFourier siempre activo
# ============================================================
try:
while True:
ProcesarFourier(n_armonicos, modo_actual)
except KeyboardInterrupt:
timer_hw.deinit()
oled.fill(0)
oled.text("Detenido.", 20, 28)
oled.show()
print("Sistema detenido de forma segura.")