import board
import digitalio
import displayio
import terminalio
import rotaryio
import time
import busio
import adafruit_displayio_ssd1306
from adafruit_display_text import label
import pwmio
displayio.release_displays()
# --- OLED setup ---
i2c = busio.I2C(scl=board.GP27, sda=board.GP26)
display_bus = displayio.I2CDisplay(i2c, device_address=0x3C)
WIDTH = 128
HEIGHT = 64
BORDER = 5
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=WIDTH, height=HEIGHT, auto_refresh=False)
# Make the display context
splash = displayio.Group()
display.show(splash)
color_bitmap = displayio.Bitmap(WIDTH, HEIGHT, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0xFFFFFF # White
bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)
# --- Rotary encoder ---
encoder = rotaryio.IncrementalEncoder(board.GP1, board.GP2)
last_position = encoder.position
# --- Buttons ---
next_btn = digitalio.DigitalInOut(board.GP4)
next_btn.switch_to_input(pull=digitalio.Pull.UP)
stop_btn = digitalio.DigitalInOut(board.GP5)
stop_btn.switch_to_input(pull=digitalio.Pull.UP)
# --- Buzzer ---
buzzer = pwmio.PWMOut(board.GP6, variable_frequency=True)
OFF = 0
ON = 2**15
# --- Global variables ---
mode = 0
timer = 0
total_points = 0
points = 0
start_time = None
# --- Text functions ---
def showText (text, WIDTH=24):
while len(splash) > 1:
splash.pop()
text_area = label.Label(
terminalio.FONT, text=text, color=0x000000, x=WIDTH, y=HEIGHT // 2 - 1
)
splash.append(text_area)
display.refresh()
def showTime (seconds):
while len(splash) > 1:
splash.pop()
hours = seconds // 3600
minutes = (seconds % 3600) // 60
secs = round(seconds % 60)
text = f"{hours:02}:{minutes:02}:{secs:02}"
showText(text, 38)
# --- Stop function ---
def stopContest ():
global mode, start_time, points, total_points, timer
if(mode == 2 and points >= total_points):
showText("Well done!", 28)
else:
showText("Stopped :(", 28)
pitch = 400 + (points * 20)
buzzer.frequency = pitch
buzzer.duty_cycle = ON
time.sleep(1)
buzzer.duty_cycle = OFF
mode = timer = points = total_points = 0
start_time = None
# --- Main loop ---
while True:
pos = encoder.position
if pos != last_position:
if mode == 0:
timer = max(0, timer + (pos - last_position) * 5 * 60)
showTime(timer)
elif mode == 1:
total_points = max(0, total_points + (pos - last_position) * 10)
showText("Total points: " + str(total_points), 18)
elif mode == 2:
points = max(0, points + (pos - last_position))
showText("Points scored: " + str(points), 20)
time.sleep(.3)
last_position = pos
if not next_btn.value: # Next Button pressed
if(mode == 2): # Don't allow mode change if timer is running
break
mode += 1
if(mode == 1):
showText("Total points: " + str(total_points))
time.sleep(0.3)
if not stop_btn.value: # Stop Button pressed
stopContest()
time.sleep(0.3)
if mode == 0 and timer == 0: # Firmware just started
showText("Welcome! Set time.", 12)
time.sleep(0.1)
if mode == 2: # Timer running
if start_time is None:
start_time = time.monotonic()
elapsed = time.monotonic() - start_time
remaining = max(0, timer - elapsed)
if(remaining == 0):
stopContest()
else:
showTime(remaining)
time.sleep(1)
time.sleep(0.05)
Loading
ssd1306
ssd1306
Next button
Stop button