# main.py — MicroPython + SH1107 類比時鐘 (128×64 模組)
import machine
import time
import math
from machine import Pin, I2C
# 載入 SH1107 驅動
import sh1107 # https://github.com/peter-l5/SH1107
# I2C 設定 (依你的板子腳位調整)
i2c = I2C(0, scl=Pin(17), sda=Pin(16))
WIDTH = 128
HEIGHT = 64
# 初始化 SH1107 (128x64, 預設 I2C address)
oled = sh1107.SH1107_I2C(WIDTH, HEIGHT, i2c)
# 畫面中心與時鐘半徑
center_x = WIDTH // 2
center_y = HEIGHT // 2 - 2
RADIUS = 28
# 畫線 (bresenham)
def draw_line(x0, y0, x1, y1, col=1):
dx = abs(x1 - x0)
dy = -abs(y1 - y0)
sx = 1 if x0 < x1 else -1
sy = 1 if y0 < y1 else -1
err = dx + dy
while True:
if 0 <= x0 < WIDTH and 0 <= y0 < HEIGHT:
oled.pixel(x0, y0, col)
if x0 == x1 and y0 == y1:
break
e2 = 2 * err
if e2 >= dy:
err += dy
x0 += sx
if e2 <= dx:
err += dx
y0 += sy
def polar_to_xy(angle_deg, r):
a = math.radians(angle_deg)
x = center_x + math.sin(a) * r
y = center_y - math.cos(a) * r
return int(round(x)), int(round(y))
def draw_circle(cx, cy, r, col=1):
steps = max(24, int(r*6))
for i in range(steps):
ang = i * (360.0 / steps)
x = cx + int(round(math.sin(math.radians(ang)) * r))
y = cy + int(round(-math.cos(math.radians(ang)) * r))
if 0 <= x < WIDTH and 0 <= y < HEIGHT:
oled.pixel(x, y, col)
def draw_filled_circle(cx, cy, r, col=1):
for dy in range(-r, r+1):
for dx in range(-r, r+1):
if dx*dx + dy*dy <= r*r:
x = cx + dx
y = cy + dy
if 0 <= x < WIDTH and 0 <= y < HEIGHT:
oled.pixel(x, y, col)
def draw_background():
draw_circle(center_x, center_y, RADIUS)
for i in range(60):
ang = i * 6
x2, y2 = polar_to_xy(ang, RADIUS - 1)
if 0 <= x2 < WIDTH and 0 <= y2 < HEIGHT:
oled.pixel(x2, y2, 1)
for i in range(12):
ang = i * 30
x_out, y_out = polar_to_xy(ang, RADIUS)
x_in, y_in = polar_to_xy(ang, RADIUS - 6)
draw_line(x_in, y_in, x_out, y_out, 1)
oled.text("12", center_x - 6, center_y - RADIUS + 2, 1)
oled.text("3", center_x + RADIUS - 8, center_y - 4, 1)
oled.text("6", center_x - 4, center_y + RADIUS - 10, 1)
oled.text("9", center_x - RADIUS + 2, center_y - 4, 1)
def draw_hand_bold(angle_deg, long_len, short_len, dot_size=2):
x_long, y_long = polar_to_xy(angle_deg, long_len)
x_short, y_short = polar_to_xy(angle_deg, short_len)
draw_line(center_x, center_y, x_short, y_short, 1)
draw_filled_circle(x_long, y_long, dot_size, 1)
draw_filled_circle(x_short, y_short, dot_size, 1)
off = dot_size + 1
a = math.radians(angle_deg + 90)
dx = int(round(math.sin(a) * off))
dy = int(round(-math.cos(a) * off))
draw_line(center_x + dx, center_y + dy, x_short + dx, y_short + dy, 1)
draw_line(center_x - dx, center_y - dy, x_short - dx, y_short - dy, 1)
draw_line(x_short + dx, y_short + dy, x_long + dx, y_long + dy, 1)
draw_line(x_short - dx, y_short - dy, x_long - dx, y_long - dy, 1)
def draw_hand_thin(angle_deg, long_len, short_len):
x_end, y_end = polar_to_xy(angle_deg, long_len)
x_back, y_back = polar_to_xy(angle_deg + 180, short_len)
draw_line(center_x, center_y, x_end, y_end, 1)
draw_filled_circle(x_back, y_back, 2, 1)
# 模擬時間 (亦可改為 RTC / NTP)
h, m, s = 10, 10, 45
def update_sim():
global h, m, s
s += 1
if s >= 60:
s = 0
m += 1
if m >= 60:
m = 0
h = (h + 1) % 12
while True:
oled.fill(0)
draw_background()
# 計算角度
sec_ang = s * 6
min_ang = m * 6 + s / 60 * 6
hour_ang = (h % 12) * 30 + m / 60 * 30
draw_hand_bold(int(min_ang), 20, 8, 2)
draw_hand_bold(int(hour_ang), 14, 8, 2)
draw_hand_thin(int(sec_ang), 24, 8)
draw_filled_circle(center_x, center_y, 3, 1)
oled.show()
time.sleep(1)
update_sim()