# MicroPython - Wokwi CrossFit Buzzer (single line) simulator
# Hardware (Wokwi diagram.json matches these pins):
# - TM1637 4-digit display: CLK=GPIO18, DIO=GPIO19
# - BUZZ button (athlete): GPIO15 to GND (internal PULL_UP)
# - GO button (master sim): GPIO5 to GND (internal PULL_UP)
# - LED (status): GPIO4
# - Piezo buzzer: GPIO2 (active-high)
#
# Usage on Wokwi:
# - Click the "GO" button to start the local chronometer.
# - Click the "BUZZ" button to freeze the time (simulates athlete finishing).
# - Press GO again to re-arm (RESET) when in BUZZED state.
#
# States: IDLE -> RUNNING -> BUZZED
# - IDLE : display 00:00, waiting for GO
# - RUNNING: chronometer running
# - BUZZED : display frozen until GO (reset) is pressed
import time
from machine import Pin
import tm1637
# Pins
PIN_CLK = 18
PIN_DIO = 19
PIN_BUZZ_BTN = 15
PIN_GO_BTN = 5
PIN_LED = 4
PIN_PIEZO = 2
# Display init
tm = tm1637.TM1637(clk=Pin(PIN_CLK), dio=Pin(PIN_DIO))
tm.brightness(7)
tm.show('0000')
tm.show_doublepoint(False)
# IO init
btn_buzz = Pin(PIN_BUZZ_BTN, Pin.IN, Pin.PULL_UP)
btn_go = Pin(PIN_GO_BTN, Pin.IN, Pin.PULL_UP)
led = Pin(PIN_LED, Pin.OUT)
piezo = Pin(PIN_PIEZO, Pin.OUT)
# Helpers
def beep(n=1, dur=60, gap=60):
for _ in range(n):
piezo.on(); time.sleep_ms(dur)
piezo.off(); time.sleep_ms(gap)
def mmss_from_ms(ms):
if ms < 0:
ms = 0
total_sec = ms // 1000
m = total_sec // 60
s = total_sec % 60
m = m % 100 # clamp to 99:59 for 4-digit display
return (m, s)
def show_mmss(m, s, running=False):
# e.g. m=1,s=23 -> "0123" with blinking colon if running
digits = f"{m:02d}{s:02d}"
tm.show(digits)
tm.show_doublepoint(running)
# Debounce
last_buzz = 1
last_go = 1
db_ms = 120
last_buzz_ts = 0
last_go_ts = 0
# State
state = "IDLE"
t0 = 0
frozen_ms = 0
def reset_to_idle():
global state, frozen_ms
state = "IDLE"
frozen_ms = 0
led.on()
show_mmss(0, 0, running=False)
reset_to_idle()
def handle_buttons():
global last_buzz, last_go, last_buzz_ts, last_go_ts
now = time.ticks_ms()
v_buzz = btn_buzz.value()
v_go = btn_go.value()
buzz_pressed = (v_buzz == 0 and last_buzz == 1 and time.ticks_diff(now, last_buzz_ts) > db_ms)
go_pressed = (v_go == 0 and last_go == 1 and time.ticks_diff(now, last_go_ts) > db_ms)
if buzz_pressed:
last_buzz_ts = now
if go_pressed:
last_go_ts = now
last_buzz = v_buzz
last_go = v_go
return buzz_pressed, go_pressed
def loop():
global state, t0, frozen_ms
while True:
buzz_pressed, go_pressed = handle_buttons()
if state == "IDLE":
if go_pressed:
# GO: start running
t0 = time.ticks_ms()
state = "RUNNING"
led.off()
beep(1, 100, 50)
elif state == "RUNNING":
# Update display
elapsed = time.ticks_diff(time.ticks_ms(), t0)
m, s = mmss_from_ms(elapsed)
show_mmss(m, s, running=True)
if buzz_pressed:
# Freeze
frozen_ms = elapsed
m, s = mmss_from_ms(frozen_ms)
show_mmss(m, s, running=False)
state = "BUZZED"
beep(2, 60, 40)
elif state == "BUZZED":
# Wait for GO as RESET to go back to IDLE
if go_pressed:
reset_to_idle()
beep(1, 50, 50)
time.sleep_ms(10)
loop()