以 Lisp 與 M5Stack 建構以隱私為先的數位相框

在大多數消費電子產品都需要雲端訂閱並交出個人資料的時代,對於一種簡單、私密的家庭回憶分享方式的需求竟然相當罕見。商業數位相框通常價格高昂,且必須註冊帳號,這可能導致私人照片洩漏至網路,或被用來訓練 AI 模型。

為了解決這個問題,開發者 chrisjj 使用 M5Stack Tab5 與 uLisp 製作了一個自訂的數位相框。透過將邏輯移至基於 Lisp 的韌體,並將影像託管於私人伺服器,該專案在高解析度顯示與完整資料主權之間取得了平衡。

硬體與顯示規格

本專案的核心是 M5Stack Tab5,其配備 1280 x 720 的高解析度顯示螢幕。此螢幕提供寬螢幕 16:9 的長寬比與優秀的色彩還原,使其成為昂貴商業相框的可行替代方案。

為了處理影像,作者使用了 uLisp 的自訂 GIF 解碼/編碼擴充套件。由於硬體具有特定的記憶體與處理能力限制,照片被格式化為 256 色 GIF,並使用 75% 擴散抖動。此流程通常透過 Photoshop 或線上轉換工具完成,能將一般照片大小維持在約 500KB 左右,同時保有幾乎與原圖無異的視覺品質。

系統架構

相框作為一個網路客戶端,從遠端網站取得影像清單並循環播放。此架構讓擁有者能遠端更新相簿,並授予朋友與家人上傳權限,無需複雜的應用程式生態系統。

後端

影像透過位於 www.wackyimage.com 的簡易 HTTP 內容管理系統 (CMS) 提供。網站包含一個受保護的頁面,內有影像標籤清單:

<body>
<p>Insert photos here! GIF format, image size 1280 x 720.</p>
<p><img src="/pictures/k58/sleepycat.gif" alt="SleepyCat.gif" /></p>
<p><img src="/pictures/k58/wedding.gif" alt="Wedding.gif" /></p>
</body>

Lisp 實作

韌體以 uLisp 撰寫,遵循線性執行流程:連線、收集與顯示。

1. 網路連線

裝置使用 wifi-connect 函式初始化與本地 Wi‑Fi 網路的連線,之後延遲 5 秒以確保握手完成:

(wifi-connect "Mynetwork" "mypassword")
(delay 5000)

2. 取得影像清單

collect-pics-list 函式負責 HTTP 請求。它使用登入 Cookie 以提升安全性,並透過輔助函式 skip-headers 跳過 HTTP 回應標頭,直接取得 HTML 主體。

(defun collect-pics-list nil
  (with-client (s "www.wackyimage.com" 80)
    (format s "GET /show?K5A HTTP/1.0~a~%" #\Return)
    (format s "Host: www.wackyimage.com~a~%" #\Return)
    (format s "Cookie: LOGIN=WI1NDlkNmE4YzBmZmE4MTQyYzNjODk2YzRkZDEwMiIp~a~%" #\Return)
    (format s "Connection: close~a~%" #\Return)
    (format s "~a~%" #\Return)
    (skip-headers s)
    (collect-pics s))))

3. 解析與顯示

collect-pics 函式掃描 HTML 中特定的目錄模式(/k58/),並擷取檔名。清單完成後,display-pic 會取得單一 GIF 檔案,並將資料流傳遞給 decode-gif 函式,以在 M5Stack 螢幕上渲染。

主執行迴圈

整個流程被包裹在 photo-frame 函式中,負責管理輪播邏輯。它會追蹤可用圖片的數量,以避免存取不存在的索引,並使用可設定的 *minutes* 變數來控制切換速度。

(defun photo-frame ()
  (wifi-connect "Mynetwork" "mypassword")
  (delay 5000)
  (let ((nextpic 0) (lastpics 0))
    (loop
     (let* ((pics (collect-pics-list))
            (npics (length pics)))
       (when (> npics lastpics)
         (setq nextpic lastpics)
         (setq lastpics npics))
       (display-pic (nth (mod nextpic npics) pics)))
     (incf nextpic)
     (delay (* 1000 60 *minutes*)))))

為確保裝置能作為獨立設備運作,作者透過 (save-image 'photo-frame) 使用 uLisp 的 resetautorun 功能,使得開機時自動觸發迴圈。

結論

結合 Lisp 的彈性與平價的 M5Stack 硬體,這個專案展示了不依賴侵入性雲端服務亦能打造功能完整、高解析度的家用設備。它將簡易的微控制器轉變為可動態、遠端更新的相簿,同時對個人資料保持嚴格的掌控。

Sources