文前碎碎唸
前一陣子,因緣際會買到了這個觀望好久的 Badger 開發板後,便在思考除了原本名牌外的其他玩法。
雖然已經有成功嘗試將以前的網頁逃脫小遊戲移植上去,但因為一些技術限制,導致呈現的效果也不盡理想,於是便開始朝更輕量且明確的目的來開發。
而在我某天腦袋一抽(?)之後,終於想到一個簡單又實用的「社交計時器」可以玩玩看,而且在完成後丟上社群的反應,似乎也異常熱絡?於是我也決定,在這裡釋出開發過程以及程式碼供大家參考啦!
話在前頭
雖然是原程式碼的釋出,但有一點要注意的是,這個版本是針對「BadgerOS」對應的硬體為基礎做開發的,有使用到一些官方系統內的 Package,不一定適用於自行組製的版本,所以若要用於其他地方,需要再自行斟酌相容性來做修改。
開發準備
有了硬體與系統的基本概念後,接著就是開發環境的準備了,這裡使用的是官方推薦的 Python IDE Thonny 作為主要編輯器。

去官網下載安裝完啟動,要先到「執行」>「設定直譯器」中,調整為「MicroPython (Raspberry Pi Pico)」後,便可直接透過 USB 連接板子來開發與測試。


如果要直接操作檔案目錄,可以去「顯示」中勾選「檔案」選項,便能在左側開啟檔案樹瀏覽器。

如果發現沒有顯示任何東西,可以先確定右下角有沒有成功連接狀態。

接著上方按下紅色的「Stop」來停止 BadgeOS 的 UI 執行後,應該就可以看到了。
接著進入正式開發啦!
社交計時器
這次社交計時器的設計基礎,是設定好「預期的社交時間扣打」之後生成進度條,並隨著時間經過「遞減長度及百分比」來表示社交能量的消耗。
基礎
開啟新專案後,首先要先把所需的 Package 引用進來。
# Badger Package
import badger2040
import time
# Get Device Screen Size
DSW = badger2040.WIDTH # Screen Width
DSH = badger2040.HEIGHT # Screen Height
這裡主要的螢幕與按鈕控制,幾乎都是交給官方的「Badger2040」來處理,所以不用各別引入相關的資源。
設定畫面
接著就先來做不同 UI 繪製功能的編寫,第一步要做的是啟動後的基本設定介面,也就是社交時間扣打輸入區。
# Time Setting Var
mxt = [59,59,24] # Max Value
tim = [30,0,0] # Time Set S:M:H
cur = 0 # UI Manage Current Setting Target ID
# Fill Zero (MicroPy don't have fill zero function)
def filz(tx,ln):
txl = list(str(tx))
if len(txl) < ln:
res = [0]*(ln-len(txl))+txl
else:
res = txl
res = [str(i) for i in res]
return "".join(res)
# Time Setting Function
def strts(fclr=False):
display.set_pen(15) # Set Color White
display.clear() # Clear Display
display.set_font("bitmap8") # Set Font
spw = round(DSW/6) # Split Sreen Width into 6 Parts
# Display Title Bar
TITH = 20 # Title Height
TITS = 2 # Title Font Size
idx = 0 # Current Index
tit = ["SEC","MIN","HRS"] # Title Text
display.set_pen(0) # Set Color Black for Title Bar
# rectangle(Pending (X), Y, Width, Height)
display.rectangle(1, 1, DSW, TITH+2) # Draw Title Bar
display.set_pen(15) # Set Color White for Text
# loop and Draw Text on Title Bar
for tx in tit:
# Text Process
tx = filz(tx,2)
# Get Text Width
titw = display.measure_text(tx, TITS)
# Calculate Pending Size for Text
LPDN = (spw*2*idx)+(spw-(titw//2))
# Draw Text (Text,Pending(X),(Y),??,Size)
display.text(tx, LPDN, (TITH//2), DSW, TITS)
# Index Shift
idx+=1
# Display Number and Target Underline For Setting
display.set_pen(0)
TMH = 30 # Time Font Height
TMS = 3 # Time Font Size
idx = 0 # Index
rcty = round(DSH/3) # Underline Basic Y
# Loop and Draw Time / Underline
for tx in tim:
tx = filz(tx,2)
titw = display.measure_text(tx, TMS)
LPDN = (spw*2*idx)+(spw-(titw//2))
display.text(tx, LPDN, ((DSH//2)-(TMH//2)), DSW, TMS)
# If Index = Current Set Target Than Add Underline
if idx==cur:
rpnd = round(spw*2*cur)+(spw-(titw//2))
display.rectangle(rpnd, (rcty+TMH), titw, 5)
idx+=1
# (Optional) Full Clear Screen
if fclr:
display.set_update_speed(badger2040.UPDATE_NORMAL)
# Update Display and Set Refresh Reat to Turbo
# Turbo = Fast Refresh but lot of afterimages
display.update()
display.set_update_speed(badger2040.UPDATE_TURBO)
這一大段主要功能,就是先定義紀錄倒數時間的變數,並將目前記憶中的設定數值印出來,而在印到設定目標時,則在數字底下加上一個底線做標記,表示現在正在調整那個數值。

其中比較需要注意的是,在畫方形時,其 Y 軸是從「頂端」開始計算,而文字則是「中間」開始,另外顏色部分「set_pen(0)」為黑色「set_pen(15)」為白色,這些參數亦適用於之後的畫面設定。
接著定義繪製電量畫面的功能,基本組成是作為標題兼百分比顯示的動態文字,以及一個長方形計量條。
lstprc = 0 # Last Percentage
totsc = 0 # Total Time (in Sec)
secnt = 0 # Current Time Countdown (in Sec)
def drwbtr():
# Get Global Variables
global lstprc
global totsc
global secnt
display.set_pen(15)
display.clear()
display.set_pen(0)
display.set_font("bitmap8")
# Calculate Current Countdown Perctntage
prc = round((secnt/totsc)*100)
# Check Percentage Value
if prc > 0:
TITH = 30
TITS = 2
title = "Social Energy Left "+str(prc)+"%"
titw = display.measure_text(title, TITS)
display.text(title, round((DSW-titw)/2), (TITH // 2) + 1, DSW, TITS)
pnd = round(DSW/10) # Pending
# Energy bar Y
rcty = round(DSH/3)
# Energy bar Length (by Percentage)
rctw = round(((DSW-(2*pnd))/100)*prc)
display.rectangle(pnd, rcty, rctw, round(DSH/2))
# If any Change to Percentage than Update Screen
# Prevent Power Waste
if prc != lstprc:
lstprc = prc
display.update()
display.set_update_speed(badger2040.UPDATE_TURBO)
else:
# If Percentage is Zero than Display Title
TMH = 30
TMS = 3
spw = round(DSW/2)
rcty = round(DSH/2)
tx = "OUT OF ENERGY ;("
titw = display.measure_text(tx, TMS)
display.text(tx, round((DSW-titw)/2), rcty-((TMH // 2) + 1), DSW, TMS)
lstprc = prc
display.set_update_speed(badger2040.UPDATE_NORMAL)
display.update()
time.sleep(10) # Keep Title for 10 Seconds
return True
這段程式中,先定義紀錄先前設定時間計算後的秒數,以及目前倒數中的秒數,以做為計算目前剩餘百分比用,並且依照百分比量,去動態顯示百分比文字跟進度條長度。

而在最後百分比歸零時,則重設畫面並顯示 10 秒鐘的「OUT OF ENERGY」的標題。

這裡需要注意的項目與前一段相同,基本上就是每個元件的繪製位置以及配色,由於都是以顯示器尺寸為基準的座標來控制,所以如果要客製化,就必須花一些時間來耐心想像跟調整。
邏輯控制
以上基本的兩個 UI 界面都完成了,接著就是進到最核心的邏輯控制啦!
原則上這裡的控制邏輯,就是讓程式初始化後,直接進到一個無限迴圈中,即時接收來自各按鈕的輸入訊號,以及控制畫面的更新。
# Initialize Badger
display = badger2040.Badger2040()
display.led(128)
display.set_update_speed(badger2040.UPDATE_NORMAL)
display.set_thickness(2)
strts(True)
timgp = 1 # Time Gap Default 1 Second
timst = False # Flag for Time Set
# Main Loop
while True:
display.keepalive()
# If Time Not Set Than Goto Setting UI
if not timst:
# Flag for Buttons
chng = False
# Up/Down Button -> Time Value +/-
if display.pressed(badger2040.BUTTON_UP):
if tim[cur] < mxt[cur]:
tim[cur]+= 1
else:
tim[cur] = 0
chng = True
if display.pressed(badger2040.BUTTON_DOWN):
if tim[cur] > 0:
tim[cur]-= 1
else:
tim[cur] = mxt[cur]
chng = True
# A/C Button -> Time Target Left/Right
if display.pressed(badger2040.BUTTON_A):
if cur > 0:
cur-= 1
else:
cur = len(tim)-1
chng = True
if display.pressed(badger2040.BUTTON_C):
if cur < len(tim)-1:
cur+= 1
else:
cur = 0
chng = True
# B Button -> Start Countdown
if display.pressed(badger2040.BUTTON_B):
# Check if All Value Set Before Starting
sta = []
for v in tim:
sta.append(v==0)
if not all(sta):
totsc = tim[0]+tim[1]*60+tim[2]*3600
secnt = totsc
timst = True
# If Change Flag Than Update UI
if chng:
strts()
else:
# If Time Set and Start than Goto Energy Bar UI
drwbtr()
# Check if Countdown Second > 0 if Not Than Reset
if secnt>0:
secnt-=timgp
time.sleep(timgp)
else:
timst = False
strts(True)
這段迴圈中分兩個部分,透過「timst」這個 Flag 控制,如果還沒設定完啟動倒數,或是時間全部為 0 的話,便會一直維持在時間設定的介面,並持續接收按鈕訊號,來讓使用者進行調整。

而如果成功開始的話,就會自動進到能量條畫面,並持續更新數值直到歸零為止,之後再重置回到設定畫面,簡單、暴力但很明瞭對八。
備註:程式碼縮排複製可能會錯,使用時請再注意。
程式上傳
完成編輯後,接著就要來將程式 py 檔上傳到板子上執行拉,如果同樣使用 Badger 的朋友,其實只要把程式檔存到板子檔案目錄中的「examples」資料夾中。

接著在下一次啟動時就會出現在選單上了。

如果想要使用自己的圖示的話,只準備一張 52X52 的 PNG 檔,重新命名為「icon-專案名.png」就可以了,不過不知道為什麼我這個一直成功不了,其他圖示我都有做更換了。
以上就是這次的開發筆記與程式釋出啦。
結語
老實說,真的不確定這次的專案放上來,能夠幫助到多少,畢竟也只是娛樂性質,程式本身也有很多部分都是專為 Badger 2040 做處理,但這塊板子在台灣也不大好找,連線上購買途徑也大多缺貨。
所以需要詳細購買位置的,我也只能先說一下抱歉了,因為我也是剛好有人釋出二手才買到的。
另外,也因為只是一兩小時內磨出來的實驗性質專案,可能有部分資源使用的部分沒有進行最佳化的調整,像是 UI 設計、畫面更新頻率、電量消耗等,這些也是後續可以改進的地方。
總之,就當一次的開發紀錄吧,而且相信有能力自購零件組裝,還有自行開發程式的朋友們,應該總有辦法移植它的,尤其這個專案使用的,又是是相對簡單的 Micro Python 語言做開發,所以要移植到相容的開發板,如 Raspberry Pi Pico 上應該也容易。
畢竟這塊識別證的核心處理器,也是採用 Raspberry Pi 開發的架構,其餘需要改的大概就是功能按鈕的對應,以及 Eink 螢幕的驅動而已。
最後,雖然手邊這塊板子是較陽春的版本,不具有硬體通訊能力,在功能上還是有些屏障限制,但感覺應該還是有其他可玩性存在,就看之後的開發再來分享囉!
《全文。終了》