測試完 GFX 函式庫就可以來改寫下面這篇文章所展示的 SSD1306 指針時鐘了 :
# Micropython NTP指針時鐘 OLED SSD1306
關於 GFX 函式庫用法參考前一篇測試 :
# MicroPython 學習筆記 : 使用 Adafruit 的 GFX 函式庫繪圖
本測試需要用到 SSD1306 驅動程式 ssd1306.py 與 GFX 模組 gfx.py, 可在 GitHub 下載 :
另外連線 WiFi 與查詢 NTP 伺服器會用到 xtools.py 函式庫, 下載網址如下 :
# https://github.com/tony1966/tony1966.github.io/blob/master/test/MicroPython/ESP8266_lib/xtools.py (ESP8266)
連線 WiFi 的 SSID 與密碼放在 config.py 中的 SSID 與 PASSWORD 變數中 :
# config.py
SSID="YOUR SSID"
PASSWORD="YOUR PASSWORD"
將 config.py, xtools.py, ssd1306.py, 與 gfx.py 這四個模組上傳到開發板, 匯入 config 與 xtools 後呼叫 xtools.connect_wifi() 連上網路 :
MicroPython v1.23.0 on 2024-06-02; Generic ESP32 module with ESP32
Type "help()" for more information.
>>> import config
>>> import xtools
>>> ip=xtools.connect_wifi(led=5)
Connecting to network...
network config: ('192.168.50.156', '255.255.255.0', '192.168.50.1', '192.168.50.1')
>>> from machine import RTC
>>> dt=RTC().datetime()
>>> dt
(2024, 11, 3, 6, 7, 33, 9, 988798)
元組的索引 4, 5, 6 分別為時分秒, 就是顯示指針時鐘所需的時間資料.
接下來匯入 machine.PIN 與 machine.I2C 類別來建立 I2C 通訊, 匯入 ssd1306 模組與 gfx.GFX 類別來繪圖, 匯入 time 用來延遲時間 :
>>> from gfx import GFX
>>> from machine import I2C, Pin
>>> import ssd1306
>>> import time
然後建立 SSD1306_I2C 與 GFX 物件 :
>>> i2c=I2C(0, scl=Pin(22), sda=Pin(21)) # ESP32 I2C
>>> oled=ssd1306.SSD1306_I2C(128, 64, i2c) # 0.96 吋解析度 128*64
>>> gfx=GFX(oled.width, oled.height, oled.pixel)
首先指針時鐘的圓心 :
>>> gfx.circle(31, 31, 1, 1)
>>> oled.show()
接著繪製外框, 半徑取 29px :
>>> gfx.circle(31, 31, 29, 1)
>>> oled.show()
結果如下 :
然後繪製小時刻度 :
>>> for i in range(1,13):
angle=i * 30
gfx.line(31+math.trunc(27*math.sin(math.radians(angle))),
31-math.trunc(27*math.cos(math.radians(angle))),
31+math.trunc(29*math.sin(math.radians(angle))),
31-math.trunc(29*math.cos(math.radians(angle))), 1)
oled.show()
結果如下 :
最後是用一個無限迴圈每隔約 1 秒鐘讀取 RTC 時鐘, 取得目前時間的時分秒資料, 然後繪製時針與分針線條 :
>>> while True:
dt=RTC().datetime()
time.sleep_ms(950) # 延遲 0.95 秒
gfx.circle(31, 31, 29, 1) # 繪製時鐘外框
h_angle=dt[4] % 12 * 30 # 計算時針角度
gfx.line(31, 31,
31+math.trunc(15*math.sin(math.radians(h_angle))),
31-math.trunc(15*math.cos(math.radians(h_angle))), 1)
oled.show() # 繪製時針
m_angle=dt[5] * 360 / 60 # 計算分針角度
gfx.line(31, 31,
31+math.trunc(25*math.sin(math.radians(m_angle))),
31-math.trunc(25*math.cos(math.radians(m_angle))), 1)
oled.show() # 繪製分針
gfx.fill_rect(53, 56, 17, 10, 0) # 繪製填滿的長方形框
oled.text(str(dt[6]), 53, 56, 1) # 繪製秒數 (文字)
oled.show()
if dt[6] == 59: # 59 秒時
gfx.fill_circle(31, 31, 25, 0) # 填充圓
gfx.circle(31, 31, 1, 1) # 繪製時鐘圓心
gfx.circle(31, 31, 29, 1) # 繪製時鐘外框
xtools.tw_now() # 同步 NTP
我在原始程式末尾添加了一行 tw_now(), 此函式會查詢 NTP 伺服器, 成功的話會更新 RTC 時鐘, 從而保證 RTC 時間的準確, 如果沒有同步, 時間一久 RTC 時鐘會漂移而不準. 結果如下 :
完整程式碼如下 :
# ssd1306_clock_1.py
import config
import xtools
from gfx import GFX
from machine import I2C, Pin, RTC
import ssd1306
import time
import math
ip=xtools.connect_wifi(led=5)
if ip:
# 建立 SSD1306_I2C 與 GFX 物件
i2c=I2C(0, scl=Pin(22), sda=Pin(21)) # ESP32 I2C
oled=ssd1306.SSD1306_I2C(128, 64, i2c) # 0.96 吋解析度 128*64
gfx=GFX(oled.width, oled.height, oled.pixel)
oled.fill(0) # 清除螢幕
oled.show()
# 繪製圓心與圓形外框
gfx.circle(31, 31, 1, 1)
oled.show()
gfx.circle(31, 31, 29, 1)
oled.show()
# 繪製小時刻度
for i in range(1,13):
angle=i * 30
gfx.line(31+math.trunc(27*math.sin(math.radians(angle))),
31-math.trunc(27*math.cos(math.radians(angle))),
31+math.trunc(29*math.sin(math.radians(angle))),
31-math.trunc(29*math.cos(math.radians(angle))), 1)
oled.show()
# 繪製時針與分針
while True:
dt=RTC().datetime()
time.sleep_ms(950) # 延遲 0.95 秒
gfx.circle(31, 31, 29, 1) # 繪製時鐘外框
h_angle=dt[4] % 12 * 30 # 計算時針角度
gfx.line(31, 31,
31+math.trunc(15*math.sin(math.radians(h_angle))),
31-math.trunc(15*math.cos(math.radians(h_angle))), 1)
oled.show() # 繪製時針
m_angle=dt[5] * 360 / 60 # 計算分針角度
gfx.line(31, 31,
31+math.trunc(25*math.sin(math.radians(m_angle))),
31-math.trunc(25*math.cos(math.radians(m_angle))), 1)
oled.show() # 繪製分針
gfx.fill_rect(53, 56, 17, 10, 0) # 繪製填滿的長方形框
oled.text(str(dt[6]), 53, 56, 1) # 繪製秒數 (文字)
oled.show()
if dt[6] == 59: # 59 秒時
gfx.fill_circle(31, 31, 25, 0) # 填充圓
gfx.circle(31, 31, 1, 1) # 繪製時鐘圓心
gfx.circle(31, 31, 29, 1) # 繪製時鐘外框
xtools.tw_now() # 同步 NTP
else:
print('無法連線 WiFi 網路!')
但我觀察發現, 時針的位置似乎沒有伴隨分針做適當的移動, 例如已經 12:45 了, 時針還是指到 12 點, 正確位置應該是較靠近 1 點才對, 它要到 13:00 才會突然從 12 點突然指向 13 點, 我詢問 ChatGPT 得到的修改建議是將 h_angle 變數改成如下 :
h_angle=(dt[4] % 12 + dt[5] / 60) * 30 # 計算時針角度
加入目前的分鐘數 dt[5] / 60 作為調整項, 這樣時針的位置就正確了. 另外, 12 個小時刻度都是相同, 我想將 3, 6, 9, 12 這 4 個小時刻度改成 2x2 px 的方點, ChatGPT 建議將刻度迴圈修改為如下 :
# 繪製小時刻度
for i in range(1, 13):
angle=i * 30
x=31+math.trunc(27*math.sin(math.radians(angle)))
y=31-math.trunc(27*math.cos(math.radians(angle)))
# 判斷是否為 3, 6, 9 或 12 點的刻度
if i in [3, 6, 9, 12]:
gfx.fill_rect(x-1, y-1, 2, 2, 1) # 繪製 2x2 像素的粗圓點
else:
gfx.line(x, y,
31+math.trunc(29 * math.sin(math.radians(angle))),
31-math.trunc(29 * math.cos(math.radians(angle))), 1)
oled.show()
黃底色部分就是 3, 6, 9, 12 這 4 個小時刻度改用 fill_rect() 繪製 2x2 矩形, 結果如下 :
這樣子小時刻度辨識度就比較高了. 完整程式碼如下 :
# ssd1306_clock_2.py
import config
import xtools
from gfx import GFX
from machine import I2C, Pin, RTC
import ssd1306
import time
import math
ip=xtools.connect_wifi(led=5)
if ip:
# 建立 SSD1306_I2C 與 GFX 物件
i2c=I2C(0, scl=Pin(22), sda=Pin(21)) # ESP32 I2C
oled=ssd1306.SSD1306_I2C(128, 64, i2c) # 0.96 吋解析度 128*64
gfx=GFX(oled.width, oled.height, oled.pixel)
oled.fill(0) # 清除螢幕
oled.show()
# 繪製圓心與圓形外框
gfx.circle(31, 31, 1, 1)
oled.show()
gfx.circle(31, 31, 29, 1)
oled.show()
# 繪製小時刻度
for i in range(1, 13):
angle=i * 30
x=31+math.trunc(27*math.sin(math.radians(angle)))
y=31-math.trunc(27*math.cos(math.radians(angle)))
# 判斷是否為 3, 6, 9 或 12 點的刻度
if i in [3, 6, 9, 12]:
gfx.fill_rect(x-1, y-1, 2, 2, 1) # 繪製 2x2 像素的粗圓點
else:
gfx.line(x, y,
31+math.trunc(29 * math.sin(math.radians(angle))),
31-math.trunc(29 * math.cos(math.radians(angle))), 1)
oled.show()
# 繪製時針與分針
while True:
dt=RTC().datetime()
time.sleep_ms(950) # 延遲 0.95 秒
gfx.circle(31, 31, 29, 1) # 繪製時鐘外框
h_angle=(dt[4] % 12 + dt[5] / 60) * 30 # 計算時針角度
gfx.line(31, 31,
31+math.trunc(15*math.sin(math.radians(h_angle))),
31-math.trunc(15*math.cos(math.radians(h_angle))), 1)
oled.show() # 繪製時針
m_angle=dt[5] * 360 / 60 # 計算分針角度
gfx.line(31, 31,
31+math.trunc(25*math.sin(math.radians(m_angle))),
31-math.trunc(25*math.cos(math.radians(m_angle))), 1)
oled.show() # 繪製分針
gfx.fill_rect(53, 56, 17, 10, 0) # 繪製填滿的長方形框
oled.text(str(dt[6]), 53, 56, 1) # 繪製秒數 (文字)
oled.show()
if dt[6] == 59: # 59 秒時
gfx.fill_circle(31, 31, 25, 0) # 填充圓
gfx.circle(31, 31, 1, 1) # 繪製時鐘圓心
gfx.circle(31, 31, 29, 1) # 繪製時鐘外框
xtools.tw_now() # 同步 NTP
else:
print('無法連線 WiFi 網路!')
沒有留言:
張貼留言