2026年8月25日 星期二

[水井村USR] 設備心跳+線上狀態+雲端健康監控版:讓故事機自己回報「我還正常運作」

樹藝AI故事機|V1.0-12

設備心跳+線上狀態+雲端健康監控版:讓故事機自己回報「我還正常運作」

V1.0-11已完成設備身分、Django事件同步與Flash離線佇列;V1.0-12再向前一步,加入低優先權設備心跳,讓管理者從雲端儀表板確認每台故事機是ONLINE或OFFLINE,以及目前狀態與韌體版本。

60秒自動心跳週期
HTTP 200Django更新成功
ONLINE雲端即時判定

一、為什麼故事機需要「心跳」?

故事播放正常,不代表管理者隨時知道設備是否健康。故事機部署在社區、展場或兒童館後,可能遇到斷電、分享器失效、Wi-Fi中斷、程式重啟或設備長時間沒有回報等狀況。

若沒有心跳,雲端只能看到「最後一筆故事事件」,卻無法判斷設備是安靜待機,還是已經離線。V1.0-12因此讓HUB 8735 Ultra每隔60秒主動向Django報到。

1故事機待機
2檢查STA與時間
3確認事件佇列為0
4送出設備心跳
5Django更新ONLINE

二、V1.0-12心跳包含哪些資訊?

心跳不是故事事件,不需要永久保存在Flash。它是一份設備當下的健康摘要:

{
  "device_id": "SHUIJING-002",
  "state": "IDLE",
  "firmware": "V1.0-12",
  "rssi": -24,
  "uptime": 3600,
  "volume": 16,
  "queue_count": 0,
  "current_track": 0,
  "jq_recoveries": 0,
  "last_cloud_http": 201,
  "last_error": ""
}
欄位用途管理意義
device_id設備唯一編號區分不同展場與故事機
stateIDLE、PLAYING或ERROR知道設備正在待機、播放或異常
firmware韌體版本確認設備是否完成升級
rssiWi-Fi訊號強度找出連線不穩的場域
queue_count尚未同步事件數判斷Django或網路是否阻塞
jq_recoveries播放器復原次數觀察JQ6500長期穩定性

三、心跳不能影響故事播放

展場系統最重要的仍是使用者操作。因此V1.0-12把心跳放在最低優先層級:

  1. 實體按鈕與停止操作。
  2. JQ6500播放與BUSY狀態機。
  3. 手機Web控制。
  4. Flash離線事件補送。
  5. 設備心跳。
設計原則:只要播放器不是IDLE、BUSY仍在播放、離線事件尚未補完,或使用者突然按下按鈕,心跳就延後或取消。心跳失敗也不寫入Flash,避免累積大量沒有長期保存價值的資料。

四、開機測試:先送重要事件,再送心跳

V1.0-12沿用V1.0-11的Flash資料結構,開機後成功恢復音量、STA設定、事件序號與佇列:

[FLASH] V1.0-11 data restored
[FLASH] Volume = 16
[FLASH] Boot count = 54
[CLOUD] Device ID: SHUIJING-002
[CLOUD] Offline queue: 0

接著建立本次開機事件:

[CLOUD QUEUE] + SHUIJING-002-0000000016 | DEVICE_BOOT | pending=1

STA與NTP完成後,系統先把重要的DEVICE_BOOT送到Django:

[TIME] Synced by NTP | 2026-08-25 10:44:06
[MODE] STA online service ready
[CLOUD] POST SHUIJING-002-0000000016 | pending=1
[CLOUD] Accepted HTTP 201 | remaining=0

只有在事件佇列回到0以後,才開始傳送心跳:

[HEARTBEAT] POST | state=IDLE | queue=0
[HEARTBEAT] Accepted HTTP 200
這個順序非常重要:HTTP 201代表開機事件已建立;HTTP 200代表設備健康資料已更新。事件資料不會被頻繁心跳搶在前面。

五、連續心跳實測

序列監控顯示,故事機約每分鐘傳送一次心跳,而且每次均獲得Django的HTTP 200:

HUB 8735 Ultra V1.0-12每分鐘設備心跳均獲得HTTP 200
圖1:10:45至10:49連續心跳測試,每分鐘均成功回傳HTTP 200。
10:46:07 [HEARTBEAT] POST | state=IDLE | queue=0
10:46:09 [HEARTBEAT] Accepted HTTP 200
10:47:07 [HEARTBEAT] POST | state=IDLE | queue=0
10:47:09 [HEARTBEAT] Accepted HTTP 200
10:48:07 [HEARTBEAT] POST | state=IDLE | queue=0
10:48:09 [HEARTBEAT] Accepted HTTP 200
10:49:07 [HEARTBEAT] POST | state=IDLE | queue=0
10:49:09 [HEARTBEAT] Accepted HTTP 200

從紀錄可觀察到每次HTTPS請求約在兩秒內完成;下一次心跳不會緊接著大量重複送出,而是依照週期執行。

六、Django儀表板成功判斷ONLINE

Django以設備最後收到心跳的時間判斷線上狀態。實測畫面中,舊設備SHUIJING-001因長時間沒有心跳而顯示OFFLINE;目前測試設備SHUIJING-002則顯示ONLINE,並正確呈現IDLE / V1.0-12

Django水井三寶儀表板顯示SHUIJING-002為ONLINE且韌體為V1.0-12
圖2:雲端儀表板的裝置健康區,能同時比較不同設備的ONLINE/OFFLINE狀態。

儀表板實測結果

  • SHUIJING-001:OFFLINE,顯示舊韌體資訊。
  • SHUIJING-002:ONLINE,狀態IDLE,韌體V1.0-12。
  • 故事啟動:6次。
  • 完整播放:6次。
  • 播放完成率:100%。
  • 互動來源:實體按鈕5次、Edge Web 1次。

七、Django如何判斷設備是否在線?

API收到心跳後更新last_seenlast_statefirmware_versionlast_rssi。儀表板再依最後回報時間判定設備狀態:

online_cutoff = timezone.now() - timedelta(minutes=10)

online = bool(
    device.last_seen and
    device.last_seen >= online_cutoff
)

正式展場可依網路品質調整門檻。若心跳每60秒一次,常見做法是:

最後心跳距今建議狀態管理判讀
2分鐘內ONLINE設備正常
2至5分鐘WARNING可能網路不穩
超過5分鐘OFFLINE需要遠端或現場檢查

八、HTTP狀態碼如何判讀?

紀錄意義處理方式
HTTP 200心跳更新成功設備維持ONLINE
HTTP 201事件建立成功從Flash佇列移除該事件
HTTP 400JSON或欄位不合API規則查看Django回應本文
HTTP 401設備驗證失敗檢查Device ID、API Key與啟用狀態
-1/-2連線或等待逾時檢查STA、DNS、TLS與伺服器
-3心跳被現場操作取消正常設計,稍後自動再試

九、V1.0-12驗收結果

  • V1.0-11的Flash設定與統計成功延續。
  • STA連線與NTP自動校時正常。
  • DEVICE_BOOT取得HTTP 201並從佇列移除。
  • 事件佇列為0後才開始傳送心跳。
  • 連續多次心跳均取得HTTP 200。
  • Django正確顯示SHUIJING-002為ONLINE。
  • Django正確顯示IDLE與V1.0-12。
  • 不同設備可以同時顯示ONLINE與OFFLINE。
  • 心跳不寫入Flash,不增加Flash耗損。
  • 按鈕、播放、停止、Web與事件補送仍具有較高優先權。

十、從「會播放」走向「可維護」

早期的故事機只要按下按鈕會播放,就可以稱為功能完成。但真正進入社區與展場後,管理者需要知道:設備是否在線、使用哪一版韌體、Wi-Fi訊號是否穩定、離線事件是否堆積,以及播放器是否經常復原。

V1.0-12加入的不是一個華麗功能,而是一項非常重要的營運能力:讓設備主動說明自己的健康狀況。這也是IoT系統從作品原型走向可管理、可維護及可擴充服務的重要分界。

沒有留言:

張貼留言