adding extra features to matrix clock project

new features for matrix clock project
* "blinking eyes" every 30 seconds
* scrolling text during alarm
* scrolling text to denote if alarm is enabled
This commit is contained in:
Liz 2025-06-06 12:39:18 -04:00
parent ec541811cc
commit f2ede9c176

View file

@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
# SPDX-License-Identifier: MIT
'''LED Matrix Alarm Clock'''
'''LED Matrix Alarm Clock with Scrolling Wake Up Text and Winking Eyes'''
import os
import ssl
import time
@ -21,20 +21,22 @@ from rainbowio import colorwheel
from adafruit_seesaw import digitalio, rotaryio, seesaw
from adafruit_debouncer import Button
timezone = -4 # your timezone offset
alarm_hour = 12 # hour is 24 hour for alarm to denote am/pm
alarm_min = 00 # minutes
alarm_volume = 1 # float 0.0 to 1.0
hour_12 = True # 12 hour or 24 hour time
# Configuration
timezone = -4
alarm_hour = 11
alarm_min = 36
alarm_volume = .2
hour_12 = True
no_alarm_plz = False
BRIGHTNESS = 128 # led brightness (0-255)
BRIGHTNESS_DAY = 200
BRIGHTNESS_NIGHT = 50
# I2S pins for Audio BFF
DATA = board.A0
LRCLK = board.A1
BCLK = board.A2
LRCLK = board.A2
BCLK = board.A3
# connect to WIFI
# Connect to WIFI
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}")
@ -42,345 +44,425 @@ context = ssl.create_default_context()
pool = socketpool.SocketPool(wifi.radio)
ntp = adafruit_ntp.NTP(pool, tz_offset=timezone, cache_seconds=3600)
# Initialize I2C
# Initialize I2C and displays
i2c = board.STEMMA_I2C()
# Initialize both matrix displays
matrix1 = Adafruit_RGBMatrixQT(i2c, address=0x30, allocate=adafruit_is31fl3741.PREFER_BUFFER)
matrix2 = Adafruit_RGBMatrixQT(i2c, address=0x31, allocate=adafruit_is31fl3741.PREFER_BUFFER)
matrix1.global_current = 0x05
matrix2.global_current = 0x05
matrix1.set_led_scaling(BRIGHTNESS)
matrix2.set_led_scaling(BRIGHTNESS)
matrix1.enable = True
matrix2.enable = True
matrix1.fill(0x000000)
matrix2.fill(0x000000)
matrix1.show()
matrix2.show()
# Configure displays
for m in [matrix1, matrix2]:
m.global_current = 0x05
m.set_led_scaling(BRIGHTNESS_DAY)
m.enable = True
m.fill(0x000000)
m.show()
# Audio setup
audio = audiobusio.I2SOut(BCLK, LRCLK, DATA)
wavs = []
for filename in os.listdir('/'):
if filename.lower().endswith('.wav') and not filename.startswith('.'):
wavs.append("/"+filename)
wavs = ["/"+f for f in os.listdir('/') if f.lower().endswith('.wav') and not f.startswith('.')]
mixer = audiomixer.Mixer(voice_count=1, sample_rate=22050, channel_count=1,
bits_per_sample=16, samples_signed=True, buffer_size=32768)
mixer.voice[0].level = alarm_volume
wav_filename = wavs[random.randint(0, (len(wavs))-1)]
wav_file = open(wav_filename, "rb")
audio.play(mixer)
def open_audio():
n = wavs[random.randint(0, (len(wavs))-1)]
f = open(n, "rb")
w = audiocore.WaveFile(f)
return w
"""Open a random WAV file"""
filename = random.choice(wavs)
return audiocore.WaveFile(open(filename, "rb"))
def update_brightness(hour_24):
"""Update LED brightness based on time of day"""
brightness = BRIGHTNESS_NIGHT if (hour_24 >= 20 or hour_24 < 7) else BRIGHTNESS_DAY
matrix1.set_led_scaling(brightness)
matrix2.set_led_scaling(brightness)
return brightness
# Seesaw setup for encoder and button
seesaw = seesaw.Seesaw(i2c, addr=0x36)
seesaw.pin_mode(24, seesaw.INPUT_PULLUP)
ss_pin = digitalio.DigitalIO(seesaw, 24)
button = Button(ss_pin, long_duration_ms=1000)
button_held = False
button = Button(digitalio.DigitalIO(seesaw, 24), long_duration_ms=1000)
encoder = rotaryio.IncrementalEncoder(seesaw)
last_position = 0
# Simple 5x7 font bitmap patterns for digits 0-9
# Font definitions
FONT_5X7 = {
'0': [
0b01110,
0b10001,
0b10011,
0b10101,
0b11001,
0b10001,
0b01110
],
'1': [
0b00100,
0b01100,
0b00100,
0b00100,
0b00100,
0b00100,
0b01110
],
'2': [
0b01110,
0b10001,
0b00001,
0b00010,
0b00100,
0b01000,
0b11111
],
'3': [
0b11111,
0b00010,
0b00100,
0b00010,
0b00001,
0b10001,
0b01110
],
'4': [
0b00010,
0b00110,
0b01010,
0b10010,
0b11111,
0b00010,
0b00010
],
'5': [
0b11111,
0b10000,
0b11110,
0b00001,
0b00001,
0b10001,
0b01110
],
'6': [
0b00110,
0b01000,
0b10000,
0b11110,
0b10001,
0b10001,
0b01110
],
'7': [
0b11111,
0b00001,
0b00010,
0b00100,
0b01000,
0b01000,
0b01000
],
'8': [
0b01110,
0b10001,
0b10001,
0b01110,
0b10001,
0b10001,
0b01110
],
'9': [
0b01110,
0b10001,
0b10001,
0b01111,
0b00001,
0b00010,
0b01100
],
' ': [
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000
]
'0': [0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110],
'1': [0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110],
'2': [0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111],
'3': [0b11111, 0b00010, 0b00100, 0b00010, 0b00001, 0b10001, 0b01110],
'4': [0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010],
'5': [0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110],
'6': [0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110],
'7': [0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000],
'8': [0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110],
'9': [0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100],
' ': [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000],
'W': [0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b11011, 0b10001],
'A': [0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001],
'K': [0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001],
'E': [0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111],
'U': [0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110],
'P': [0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000],
'O': [0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110],
'N': [0b10001, 0b11001, 0b10101, 0b10101, 0b10011, 0b10001, 0b10001],
'F': [0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000]
}
def draw_pixel_flipped(matrix, x, y, color):
"""Draw a pixel with 180-degree rotation"""
flipped_x = 12 - x
flipped_y = 8 - y
if 0 <= flipped_x < 13 and 0 <= flipped_y < 9:
matrix.pixel(flipped_x, flipped_y, color)
# Eye patterns
EYE_OPEN = [0b10101, 0b01110, 0b10001, 0b10101, 0b10001, 0b01110, 0b00000]
EYE_CLOSED = [0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000]
def draw_char(matrix, char, x, y, color):
"""Draw a character at position x,y on the specified matrix (flipped)"""
if char in FONT_5X7:
bitmap = FONT_5X7[char]
class Display:
"""Handle all display operations"""
def __init__(self, m1, m2):
self.matrix1 = m1
self.matrix2 = m2
def clear(self):
"""Clear both displays"""
self.matrix1.fill(0x000000)
self.matrix2.fill(0x000000)
def show(self):
"""Update both displays"""
self.matrix1.show()
self.matrix2.show()
def pixel(self, matrix, x, y, color):
"""Draw a pixel with 180-degree rotation"""
fx, fy = 12 - x, 8 - y
if 0 <= fx < 13 and 0 <= fy < 9:
matrix.pixel(fx, fy, color)
def draw_char(self, matrix, char, x, y, color):
"""Draw a character at position x,y"""
if char.upper() in FONT_5X7:
bitmap = FONT_5X7[char.upper()]
for row in range(7):
for col in range(5):
if bitmap[row] & (1 << (4 - col)):
draw_pixel_flipped(matrix, x + col, y + row, color)
self.pixel(matrix, x + col, y + row, color)
def draw_colon_split(y, color):
"""Draw a split colon with 2x2 dots between the displays"""
# Top dot - left half on matrix1, right half on matrix2
draw_pixel_flipped(matrix1, 12, y+1, color) # Top-left
draw_pixel_flipped(matrix1, 12, y + 2, color) # Bottom-left
draw_pixel_flipped(matrix2, 0, y+1, color) # Top-right
draw_pixel_flipped(matrix2, 0, y + 2, color) # Bottom-right
def draw_colon(self, y, color, is_pm=False):
"""Draw colon split between displays with optional PM indicator"""
# Two dots for the colon
for dy in [(1, 2), (4, 5)]:
for offset in dy:
self.pixel(self.matrix1, 12, y + offset, color)
self.pixel(self.matrix2, 0, y + offset, color)
# PM indicator dot
if is_pm:
self.pixel(self.matrix1, 12, y + 6, color)
self.pixel(self.matrix2, 0, y + 6, color)
# Bottom dot - left half on matrix1, right half on matrix2
draw_pixel_flipped(matrix1, 12, y + 4, color) # Top-left
draw_pixel_flipped(matrix1, 12, y + 5, color) # Bottom-left
draw_pixel_flipped(matrix2, 0, y + 4, color) # Top-right
draw_pixel_flipped(matrix2, 0, y + 5, color) # Bottom-right
def draw_time(self, time_str, color, is_pm=False):
"""Draw time display across both matrices"""
self.clear()
y = 1
# Draw digits
if len(time_str) >= 5:
self.draw_char(self.matrix1, time_str[0], 0, y, color)
self.draw_char(self.matrix1, time_str[1], 6, y, color)
self.draw_colon(y, color, is_pm)
self.draw_char(self.matrix2, time_str[3], 2, y, color)
self.draw_char(self.matrix2, time_str[4], 8, y, color)
self.show()
def draw_text(text, color=0xFFFFFF):
"""Draw text across both matrices with proper spacing"""
# Clear both displays
matrix1.fill(0x000000)
matrix2.fill(0x000000)
def draw_scrolling_text(self, text, offset, color):
"""Draw scrolling text across both matrices"""
self.clear()
char_width = 6
total_width = 26
# Calculate position for smooth scrolling
y = 1
for i, char in enumerate(text):
# Start from right edge and move left
char_x = total_width - offset + (i * char_width)
# Draw character if any part is visible
if -6 < char_x < total_width:
if char_x < 13: # On matrix1
self.draw_char(self.matrix1, char, char_x, y, color)
else: # On matrix2
self.draw_char(self.matrix2, char, char_x - 13, y, color)
self.show()
# For "12:00" layout with spacing:
# "1" at x=0 on matrix1 (5 pixels wide)
# "2" at x=6 on matrix1 (5 pixels wide, leaving 1-2 pixels space before colon)
# ":" split between matrix1 and matrix2
# "0" at x=2 on matrix2 (leaving 1-2 pixels space after colon)
# "0" at x=8 on matrix2 (5 pixels wide)
def draw_eye(self, matrix, pattern, color):
"""Draw eye pattern centered on matrix"""
x, y = 4, 1 # Center position
for row in range(7):
for col in range(5):
if pattern[row] & (1 << (4 - col)):
self.pixel(matrix, x + col, y + row, color)
y = 1 # Vertical position
def wink_animation(self, color):
"""Perform winking animation"""
# Sequence: open -> left wink -> open -> right wink -> open
sequences = [
(EYE_OPEN, EYE_OPEN),
(EYE_CLOSED, EYE_OPEN),
(EYE_OPEN, EYE_OPEN),
(EYE_OPEN, EYE_CLOSED),
(EYE_OPEN, EYE_OPEN)
]
for left_eye, right_eye in sequences:
self.clear()
self.draw_eye(self.matrix1, left_eye, color)
self.draw_eye(self.matrix2, right_eye, color)
self.show()
time.sleep(0.3)
# Draw first two digits on matrix1
if len(text) >= 2:
draw_char(matrix1, text[0], 0, y, color) # First digit at x=0
draw_char(matrix1, text[1], 6, y, color) # Second digit at x=6 (leaves space for colon)
def blink_time(self, time_str, color, is_pm=False, count=3):
"""Blink time display for mode changes"""
for _ in range(count):
self.clear()
self.show()
time.sleep(0.2)
self.draw_time(time_str, color, is_pm)
time.sleep(0.2)
# Draw the colon split between displays
if len(text) >= 3 and text[2] == ':':
draw_colon_split(y, color)
# Initialize display handler
display = Display(matrix1, matrix2)
# Draw last two digits on matrix2
if len(text) >= 5:
draw_char(matrix2, text[3], 2, y, color) # Third digit at x=2 (leaves space after colon)
draw_char(matrix2, text[4], 8, y, color) # Fourth digit at x=8
# State variables
class State:
"""Track all state variables"""
def __init__(self):
self.color_value = 0
self.color = colorwheel(0)
self.is_pm = False
self.alarm_is_pm = False
self.time_str = "00:00"
self.set_alarm = 0
self.active_alarm = False
self.alarm_str = f"{alarm_hour:02}:{alarm_min:02}"
self.current_brightness = BRIGHTNESS_DAY
# Timers
self.refresh_timer = Timer(3600000) # 1 hour
self.clock_timer = Timer(1000) # 1 second
self.wink_timer = Timer(30000) # 30 seconds
self.scroll_timer = Timer(80) # Scroll speed
self.blink_timer = Timer(500) # Blink speed
self.alarm_status_timer = Timer(100) # Status scroll
# Display state
self.scroll_offset = 0
self.blink_state = True
self.showing_status = False
self.status_start_time = 0
self.alarm_start_time = 0
# Time tracking
self.first_run = True
self.seconds = 0
self.mins = 0
self.am_pm_hour = 0
# Update both displays
matrix1.show()
matrix2.show()
print("updated matrices")
class Timer:
"""Simple timer helper"""
def __init__(self, interval):
self.interval = interval
self.last_tick = ticks_ms()
refresh_clock = ticks_ms()
refresh_timer = 3600 * 1000
clock_clock = ticks_ms()
clock_timer = 1000
first_run = True
new_time = False
color_value = 0
COLOR = colorwheel(0)
time_str = "00:00"
set_alarm = 0
active_alarm = False
alarm = f"{alarm_hour:02}:{alarm_min:02}"
def check(self):
"""Check if timer has elapsed"""
if ticks_diff(ticks_ms(), self.last_tick) >= self.interval:
self.last_tick = ticks_add(self.last_tick, self.interval)
return True
return False
while True:
def reset(self):
"""Reset timer"""
self.last_tick = ticks_ms()
button.update()
if button.long_press:
# long press to set alarm & turn off alarm
if set_alarm == 0 and not active_alarm:
set_alarm = 1
draw_text(f"{alarm_hour:02}: ", COLOR)
if active_alarm:
mixer.voice[0].stop()
active_alarm = False
BRIGHTNESS = 128
matrix1.set_led_scaling(BRIGHTNESS)
matrix2.set_led_scaling(BRIGHTNESS)
if button.short_count == 1:
# short press to set hour and minute
set_alarm = (set_alarm + 1) % 3
if set_alarm == 0:
draw_text(time_str, COLOR)
elif set_alarm == 2:
draw_text(f" :{alarm_min:02}", COLOR)
if button.short_count == 3:
no_alarm_plz = not no_alarm_plz
print(f"alarms off? {no_alarm_plz}")
# Initialize state
state = State()
position = -encoder.position
if position != last_position:
if position > last_position:
# when setting alarm, rotate through hours/minutes
# when not, change color for LEDs
if set_alarm == 0:
color_value = (color_value + 5) % 255
elif set_alarm == 1:
alarm_hour = (alarm_hour + 1) % 24
elif set_alarm == 2:
alarm_min = (alarm_min + 1) % 60
def format_time_display(hour_24, minute, use_12hr=True):
"""Format time for display with AM/PM detection"""
if use_12hr:
hour = hour_24 % 12
if hour == 0:
hour = 12
is_pm = hour_24 >= 12
else:
if set_alarm == 0:
color_value = (color_value - 5) % 255
elif set_alarm == 1:
alarm_hour = (alarm_hour - 1) % 24
elif set_alarm == 2:
alarm_min = (alarm_min - 1) % 60
alarm = f"{alarm_hour:02}:{alarm_min:02}"
COLOR = colorwheel(color_value)
if set_alarm == 0:
draw_text(time_str, COLOR)
elif set_alarm == 1:
draw_text(f"{alarm_hour:02}: ", COLOR)
elif set_alarm == 2:
draw_text(f" :{alarm_min:02}", COLOR)
last_position = position
hour = hour_24
is_pm = False
return f"{hour:02}:{minute:02}", is_pm
# resync with NTP time server every hour
if set_alarm == 0:
if ticks_diff(ticks_ms(), refresh_clock) >= refresh_timer or first_run:
def sync_time():
"""Sync with NTP server"""
try:
print("Getting time from internet!")
now = ntp.datetime
print(now)
total_seconds = time.mktime(now)
first_run = False
am_pm_hour = now.tm_hour
if hour_12:
hours = am_pm_hour % 12
if hours == 0:
hours = 12
else:
hours = am_pm_hour
time_str = f"{hours:02}:{now.tm_min:02}"
print(time_str)
mins = now.tm_min
seconds = now.tm_sec
draw_text(time_str, COLOR)
refresh_clock = ticks_add(refresh_clock, refresh_timer)
state.am_pm_hour = now.tm_hour
state.mins = now.tm_min
state.seconds = now.tm_sec
state.time_str, state.is_pm = format_time_display(state.am_pm_hour, state.mins, hour_12)
update_brightness(state.am_pm_hour)
if not state.active_alarm and not state.showing_status:
display.draw_time(state.time_str, state.color, state.is_pm)
print(f"Time: {state.time_str}")
state.first_run = False
return True
except Exception as e: # pylint: disable=broad-except
print("Some error occured, retrying! -", e)
print(f"Error syncing time: {e}")
return False
# Main loop
while True:
button.update()
# Handle button presses
if button.long_press:
if state.set_alarm == 0 and not state.active_alarm:
# Enter alarm setting mode
state.blink_timer.reset()
state.set_alarm = 1
state.alarm_is_pm = alarm_hour >= 12 if hour_12 else False
hour_str, _ = format_time_display(alarm_hour, 0, hour_12)
display.blink_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm)
# Draw the alarm hour after blinking to keep it displayed
display.draw_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm)
elif state.active_alarm:
# Stop alarm
mixer.voice[0].stop()
state.active_alarm = False
update_brightness(state.am_pm_hour)
state.scroll_offset = 0
# Immediately redraw the current time
display.draw_time(state.time_str, state.color, state.is_pm)
print("Alarm silenced")
if button.short_count == 1: # Changed from == 1 to >= 1 for better detection
# Cycle through alarm setting modes
state.set_alarm = (state.set_alarm + 1) % 3
if state.set_alarm == 0:
# Exiting alarm setting mode - redraw current time
state.wink_timer.reset()
display.draw_time(state.time_str, state.color, state.is_pm)
elif state.set_alarm == 1:
# Entering hour setting
hour_str, _ = format_time_display(alarm_hour, 0, hour_12)
display.draw_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm)
# Reset timer to prevent immediate blinking
elif state.set_alarm == 2:
# Entering minute setting
display.blink_time(f" :{alarm_min:02}", state.color, state.alarm_is_pm)
# Draw the minutes after blinking to keep them displayed
display.draw_time(f" :{alarm_min:02}", state.color, state.alarm_is_pm)
# Reset timer to prevent immediate blinking
if button.short_count == 3: # Changed for better detection
# Toggle alarm on/off
no_alarm_plz = not no_alarm_plz
print(f"Alarm disabled: {no_alarm_plz}")
state.showing_status = True
state.status_start_time = ticks_ms()
state.scroll_offset = 0
# Handle encoder (your existing code)
position = -encoder.position
if position != last_position:
delta = 1 if position > last_position else -1
if state.set_alarm == 0:
# Change color
state.color_value = (state.color_value + delta * 5) % 255
state.color = colorwheel(state.color_value)
display.draw_time(state.time_str, state.color, state.is_pm)
elif state.set_alarm == 1:
# Change hour
alarm_hour = (alarm_hour + delta) % 24
state.alarm_is_pm = alarm_hour >= 12 if hour_12 else False
hour_str, _ = format_time_display(alarm_hour, 0, hour_12)
display.draw_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm)
elif state.set_alarm == 2:
# Change minute
alarm_min = (alarm_min + delta) % 60
display.draw_time(f" :{alarm_min:02}", state.color, state.alarm_is_pm)
state.alarm_str = f"{alarm_hour:02}:{alarm_min:02}"
last_position = position
# Handle alarm status display
if state.showing_status:
if state.alarm_status_timer.check():
status_text = "OFF " if no_alarm_plz else "ON "
display.draw_scrolling_text(status_text, state.scroll_offset, state.color)
text_width = 4*6 if no_alarm_plz else 3*6
state.scroll_offset += 1
# Reset when text has completely scrolled off
if state.scroll_offset > text_width + 18:
state.scroll_offset = 0
state.showing_status = False
if state.set_alarm == 0 and not state.active_alarm:
display.draw_time(state.time_str, state.color, state.is_pm)
# Handle active alarm scrolling
if state.active_alarm:
# Auto-silence alarm after 1 minute
if ticks_diff(ticks_ms(), state.alarm_start_time) >= 60000:
mixer.voice[0].stop()
state.active_alarm = False
update_brightness(state.am_pm_hour)
state.scroll_offset = 0
display.draw_time(state.time_str, state.color, state.is_pm)
print("Alarm auto-silenced")
elif state.scroll_timer.check():
display.draw_scrolling_text("WAKE UP ", state.scroll_offset, state.color)
text_width = 8 * 6 # "WAKE UP " is 8 characters
state.scroll_offset += 1
# Reset when text has completely scrolled off
if state.scroll_offset > text_width + 26:
state.scroll_offset = 0
# Handle alarm setting mode blinking
elif state.set_alarm > 0:
# Only blink if enough time has passed since mode change
if state.blink_timer.check():
state.blink_state = not state.blink_state
if state.blink_state:
# Redraw during the "on" part of blink
if state.set_alarm == 1:
hour_str, _ = format_time_display(alarm_hour, 0, hour_12)
display.draw_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm)
else:
display.draw_time(f" :{alarm_min:02}", state.color, state.alarm_is_pm)
else:
# Only clear display during the "off" part of blink
display.clear()
display.show()
# Normal mode operations
else: # state.set_alarm == 0
# Winking animation
if not state.active_alarm and not state.showing_status and state.wink_timer.check():
print("Winking!")
display.wink_animation(state.color)
display.draw_time(state.time_str, state.color, state.is_pm)
# Time sync
if state.refresh_timer.check() or state.first_run:
if not sync_time():
time.sleep(10)
microcontroller.reset()
# keep time locally between NTP server syncs
if ticks_diff(ticks_ms(), clock_clock) >= clock_timer:
seconds += 1
# print(seconds)
if seconds > 59:
mins += 1
seconds = 0
new_time = True
if mins > 59:
am_pm_hour += 1
mins = 0
new_time = True
if hour_12:
hours = am_pm_hour % 12
if hours == 0:
hours = 12
else:
hours = am_pm_hour
if new_time:
time_str = f"{hours:02}:{mins:02}"
new_time = False
print(time_str)
draw_text(time_str, COLOR)
if f"{am_pm_hour:02}:{mins:02}" == alarm and not no_alarm_plz:
print("alarm!")
# grab a new wav file from the wavs list
# Local timekeeping
if state.clock_timer.check():
state.seconds += 1
if state.seconds > 59:
state.seconds = 0
state.mins += 1
if state.mins > 59:
state.mins = 0
state.am_pm_hour = (state.am_pm_hour + 1) % 24
update_brightness(state.am_pm_hour)
# Update display
state.time_str, state.is_pm = format_time_display(state.am_pm_hour,
state.mins, hour_12)
if not state.active_alarm and not state.showing_status:
display.draw_time(state.time_str, state.color, state.is_pm)
# Check alarm
if f"{state.am_pm_hour:02}:{state.mins:02}" == state.alarm_str and not no_alarm_plz:
print("ALARM!")
wave = open_audio()
mixer.voice[0].play(wave, loop=True)
active_alarm = True
if active_alarm:
# blink the clock characters
if BRIGHTNESS:
BRIGHTNESS = 0
else:
BRIGHTNESS = 128
matrix1.set_led_scaling(BRIGHTNESS)
matrix2.set_led_scaling(BRIGHTNESS)
clock_clock = ticks_add(clock_clock, clock_timer)
state.active_alarm = True
state.alarm_start_time = ticks_ms()
state.scroll_offset = 0