Creation of main.py files

This commit is contained in:
bepis
2026-06-27 17:35:30 +10:00
parent e8ca51c5da
commit 8cebec6712
2 changed files with 384 additions and 0 deletions

192
main.py Normal file
View File

@@ -0,0 +1,192 @@
# base_station_main.py -- Trivia buzzer JUDGE (Raspberry Pi Pico W)
#
# Modes: "quizbowl" (live on reset) or "jeopardy" (stand-by until activated).
# Control button GP15 -> GND:
# quizbowl : press = reset / re-arm.
# jeopardy : press on STAND BY = ACTIVATE; press when ARMED/LOCKED = reset.
#
# Color LEDs: the base lights the winning team's color (blue or green),
# matching the buzzer NAMEs. Onboard LED: solid = someone buzzed.
import network, socket, select, time
from machine import Pin
SSID = "TriviaBuzzers"
PASSWORD = "buzzin123"
PORT = 5000
MODE = "jeopardy" # "jeopardy" or "quizbowl"
LOCK_TIMEOUT_MS = 10000
BATT_LOW_V = 3.60
BUTTON_PIN = 15 # control button GP15 -> GND
BLUE_LED_PIN = 16 # base's blue LED -> resistor -> GND
GREEN_LED_PIN = 17 # base's green LED -> resistor -> GND
led = Pin("LED", Pin.OUT) # onboard: solid = buzzed
button = Pin(BUTTON_PIN, Pin.IN, Pin.PULL_UP)
# winner NAME -> indicator LED on the base
TEAM_LEDS = {
"blue": Pin(BLUE_LED_PIN, Pin.OUT),
"green": Pin(GREEN_LED_PIN, Pin.OUT),
}
def set_team_led(name):
for k, p in TEAM_LEDS.items():
p.value(1 if k == name else 0)
def team_leds_off():
for p in TEAM_LEDS.values():
p.value(0)
# --- Wi-Fi access point ---
ap = network.WLAN(network.AP_IF)
ap.config(essid=SSID, password=PASSWORD)
ap.active(True)
while not ap.active():
time.sleep(0.1)
print("AP ready:", ap.ifconfig())
# --- TCP server ---
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("0.0.0.0", PORT))
srv.listen(8)
srv.setblocking(False)
poller = select.poll()
poller.register(srv, select.POLLIN)
clients = {}
phase = "standby"
winner = None
lock_time = 0
def send(sock, msg):
try:
sock.send(msg)
except OSError:
pass
def state_msg():
if phase == "locked":
return b"LOCKED " + winner.encode() + b"\n"
if phase == "standby":
return b"STANDBY\n"
return b"ARMED\n"
def broadcast(msg, exclude=None):
for c in list(clients.values()):
if c["sock"] is not exclude:
send(c["sock"], msg)
def battery_summary():
if not clients:
return
parts = []
for c in clients.values():
v = c.get("batt")
if v is None:
parts.append("%s ?" % c["name"])
else:
parts.append("%s %.2f%s" % (c["name"], v, "!" if v < BATT_LOW_V else ""))
print("Batteries:", " ".join(parts))
def go_armed():
global phase, winner
phase, winner = "armed", None
led.off()
team_leds_off()
broadcast(b"ARMED\n")
print("--- ARMED (buzz now) ---")
def go_standby():
global phase, winner
phase, winner = "standby", None
led.off()
team_leds_off()
broadcast(b"STANDBY\n")
print("--- STAND BY ---")
def start_round():
battery_summary()
if MODE == "jeopardy":
go_standby()
else:
go_armed()
def handle_line(fd, line):
global phase, winner, lock_time
c = clients.get(fd)
if not c:
return
if line.startswith(b"HELLO"):
parts = line.split()
if len(parts) > 1:
c["name"] = parts[1].decode()
send(c["sock"], state_msg())
elif line.startswith(b"BATT"):
parts = line.split()
if len(parts) > 1:
try:
v = float(parts[1])
except ValueError:
return
was = c.get("batt")
c["batt"] = v
if v < BATT_LOW_V and (was is None or was >= BATT_LOW_V):
print("[BATTERY LOW] %s = %.2f V" % (c["name"], v))
elif line.startswith(b"PRESS"):
if phase == "armed":
phase, winner = "locked", c["name"]
lock_time = time.ticks_ms()
led.on()
set_team_led(winner)
print("WINNER:", winner)
send(c["sock"], b"WIN\n")
broadcast(b"LOCKED " + winner.encode() + b"\n", exclude=c["sock"])
else:
send(c["sock"], state_msg())
start_round()
last_btn = 0
while True:
for sock, ev in poller.poll(20):
if sock is srv:
conn, addr = srv.accept()
conn.setblocking(False)
poller.register(conn, select.POLLIN)
clients[conn.fileno()] = {"sock": conn, "name": str(addr[0]),
"buf": b"", "batt": None}
send(conn, state_msg())
else:
fd = sock.fileno()
try:
data = sock.recv(64)
except OSError:
continue
if not data:
poller.unregister(sock)
clients.pop(fd, None)
sock.close()
continue
c = clients.get(fd)
if not c:
continue
c["buf"] += data
while b"\n" in c["buf"]:
line, c["buf"] = c["buf"].split(b"\n", 1)
handle_line(fd, line.strip())
if button.value() == 0 and time.ticks_diff(time.ticks_ms(), last_btn) > 300:
last_btn = time.ticks_ms()
if MODE == "jeopardy" and phase == "standby":
go_armed()
else:
start_round()
if phase == "locked" and LOCK_TIMEOUT_MS and time.ticks_diff(time.ticks_ms(), lock_time) > LOCK_TIMEOUT_MS:
print("auto-reset after timeout")
start_round()