Files
pico-time/main.py
T
gballan 7a653c424b
ci/woodpecker/push/woodpecker Pipeline was successful
Added option to allow user to choose 24-hour or 12-hour time
2026-09-07 20:37:14 -04:00

128 lines
3.7 KiB
Python

import framebuf
import network
import ntptime
import time
from machine import Pin, I2C
import ssd1306
# --- Configuration ---
try:
import config as Config
except ImportError:
print("ERROR: config.py not found.")
print("Create config.py on the device with your Wi-Fi and NTP settings.")
while True:
time.sleep(2)
# --- Hardware Setup ---
i2c = I2C(0, sda=Pin(16), scl=Pin(17), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
def display_message(line1, line2=""):
oled.fill(0)
oled.text(line1, 0, 16)
oled.text(line2, 0, 32)
oled.show()
# --- Wi-Fi Connection ---
display_message("Connecting to", "Wi-Fi...")
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(Config.WIFI_SSID, Config.WIFI_PASSWORD)
timeout = 20
while not wlan.isconnected() and timeout > 0:
time.sleep(1)
timeout -= 1
if not wlan.isconnected():
display_message("Wi-Fi Error", "Check settings")
raise RuntimeError("Failed to connect to Wi-Fi")
# --- NTP Synchronization ---
display_message("Syncing time...", "NTP server")
try:
ntptime.settime() # Sets the Pico internal RTC to UTC
except Exception as e:
display_message("NTP Failed", str(e))
time.sleep(3)
# Disconnect Wi-Fi to conserve power once time is synchronized
wlan.active(False)
def draw_scaled_text(display, text, x, y, scale=2):
"""
Renders text scaled up by an integer factor (scale=2 -> 16x16, scale=3 -> 24x24).
Uses a temporary 1-bit FrameBuffer to capture the default 8x8 font.
"""
width = len(text) * 8
height = 8
# 1-bit MONO_HLSB buffer: (width * height) / 8 bytes
buf = bytearray((width * height) // 8)
fb = framebuf.FrameBuffer(buf, width, height, framebuf.MONO_HLSB)
# Render string with built-in font into the scratch buffer
fb.fill(0)
fb.text(text, 0, 0, 1)
# Read each pixel and draw a (scale x scale) block on the OLED
for r in range(height):
for c in range(width):
if fb.pixel(c, r):
display.fill_rect(x + (c * scale), y + (r * scale), scale, scale, 1)
def calculate_x_spacing(text, scale=1):
"""
Calculates the amount of horizontal (x) spacing is needed to center a string.
"""
scaled_text = len(text) * scale
return (128 - (scaled_text * 8)) // 2
# --- Main Loop ---
DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
while True:
local_epoch = time.time() + (Config.UTC_OFFSET * 3600)
year, month, day, hour_24, minute, second, weekday, _ = time.localtime(local_epoch)
oled.fill(0)
# Draw 24-hour time
if Config.USE_24_HOUR:
# Time string (HH:MM:SS is 8 chars -> 8 chars * 8px * 2x = 128px wide)
time_str = f"{hour_24:02d}:{minute:02d}"
# Draw time at 2x scale (16px high, spans 128px across)
time_x = calculate_x_spacing(time_str, 2)
draw_scaled_text(oled, time_str, x=time_x, y=14, scale=2)
# Draw 12-hour time (AM/PM)
else:
# 1. Determine AM/PM and 12-hour format
period = "AM" if hour_24 < 12 else "PM"
hour_12 = hour_24 % 12
if hour_12 == 0:
hour_12 = 12
# Format time without leading zero (or use {:2d} for alignment)
time_str = f"{hour_12}:{minute:02d}"
# 2. Draw 2x scaled time
time_x = calculate_x_spacing(time_str, 2)
draw_scaled_text(oled, time_str, x=time_x, y=12, scale=2)
# 3. Draw AM/PM in standard 1x font (aligned with the bottom of the 16px digits)
oled.text(period, time_x + (len(time_str) * 16) + 4, 12 + 8)
# 4. Draw date centered horizontally underneath
date_str = f"{DAYS[weekday]} {year}-{month:02d}-{day:02d}"
oled.text(date_str, calculate_x_spacing(date_str), 42)
oled.show()
time.sleep(0.5)