from machine import Pin
import time
# ====== CONFIGURA AQUÍ ======
RED_PIN = 5 # ROJO (GP5)
YELLOW_PIN= 9 # AMARILLO (GP9)
GREEN_PIN = 13 # VERDE (GP13)
BUTTON_PIN= 0 # Botón (GP0)
USE_PULL_UP = False # ahora usamos pull-up interno (botón a GND al presionar)
TOTAL_RUNTIME_MS = 30000 # 30 s
# Duraciones (ms)
GREEN_STEADY_MS = 4000
GREEN_BLINK_MS = 1000
GREEN_BLINK_INT_MS = 200
YELLOW_TOTAL_MS = 2500
YELLOW_BLINK_INT_MS = 250 # (ya no se usa para parpadeo, lo dejo por compatibilidad)
RED_MS = 5000
DEBOUNCE_MS = 40
# ============================
# Pines
red = Pin(RED_PIN, Pin.OUT)
yellow = Pin(YELLOW_PIN, Pin.OUT)
green = Pin(GREEN_PIN, Pin.OUT)
# botón: si USE_PULL_UP True usamos pull interno (botón conecta a GND al presionar)
if USE_PULL_UP:
button = Pin(BUTTON_PIN, Pin.IN, Pin.PULL_UP)
else:
button = Pin(BUTTON_PIN, Pin.IN)
PRESSED_LEVEL = 0 # activo en LOW (con pull-up, al presionar va a GND)
def all_off():
red.off(); yellow.off(); green.off()
def is_pressed():
return button.value() == PRESSED_LEVEL
def wait_press_release():
# Espera a que se presione y suelte (con debounce)
while not is_pressed():
time.sleep_ms(10)
time.sleep_ms(DEBOUNCE_MS)
if is_pressed():
while is_pressed():
time.sleep_ms(10)
time.sleep_ms(DEBOUNCE_MS)
return True
return False
def sleep_capped(ms):
# duerme pero permite salir si se presiona el botón (cancelar)
if ms <= 0:
return False
end = time.ticks_add(time.ticks_ms(), ms)
while time.ticks_diff(end, time.ticks_ms()) > 0:
if is_pressed(): # abortar si se pulsa
return False
time.sleep_ms(10)
return True
def blink(pin, interval_ms, duration_ms):
# parpadea durante duration_ms; devuelve True si completó, False si fue abortado
if duration_ms <= 0 or interval_ms <= 0:
pin.off()
return False
end = time.ticks_add(time.ticks_ms(), duration_ms)
state = False
while time.ticks_diff(end, time.ticks_ms()) > 0:
if is_pressed():
pin.off()
return False
state = not state
if state:
pin.on()
else:
pin.off()
time.sleep_ms(interval_ms)
pin.off()
return True
def run_for_30s():
start = time.ticks_ms()
aborted = False
while time.ticks_diff(time.ticks_ms(), start) < TOTAL_RUNTIME_MS:
remaining = TOTAL_RUNTIME_MS - time.ticks_diff(time.ticks_ms(), start)
# GREEN steady
dur = min(GREEN_STEADY_MS, remaining)
if dur > 0:
green.on(); yellow.off(); red.off()
if not sleep_capped(dur):
aborted = True
break
# GREEN blink
remaining = TOTAL_RUNTIME_MS - time.ticks_diff(time.ticks_ms(), start)
dur = min(GREEN_BLINK_MS, remaining)
if dur > 0:
if not blink(green, GREEN_BLINK_INT_MS, dur):
aborted = True
break
# YELLOW: ahora **NO** parpadea — permanece encendida el tiempo total
remaining = TOTAL_RUNTIME_MS - time.ticks_diff(time.ticks_ms(), start)
dur = min(YELLOW_TOTAL_MS, remaining)
if dur > 0:
yellow.on(); green.off(); red.off()
if not sleep_capped(dur):
yellow.off()
aborted = True
break
yellow.off()
# RED
remaining = TOTAL_RUNTIME_MS - time.ticks_diff(time.ticks_ms(), start)
dur = min(RED_MS, remaining)
if dur > 0:
red.on(); green.off(); yellow.off()
if not sleep_capped(dur):
red.off()
aborted = True
break
red.off()
all_off()
return not aborted # True si completó, False si fue abortado
# ===== MAIN =====
all_off()
print("Modo botón: usando pull-up interno (botón a GND).")
print("Listo: presiona el botón para correr 30 s.")
while True:
if wait_press_release():
print("Botón detectado → iniciando por 30 s")
completed = run_for_30s()
if completed:
print("Fin normal de 30 s. Esperando nuevo toque.")
else:
print("Secuencia abortada por botón. Esperando nuevo toque.")
time.sleep_ms(20)