
啟動 App 後,確認 D2 依 ledState 初始值顯示熄滅,管理頁面開啟,Python Edge Service 保持待命。
按下 D9 一次,D2 應切換,Python 顯示 ON 或 OFF,Web 文字與顏色同步更新,持續按住時只能在剛按下的瞬間切換一次。
停止 Python 後再次操作 D9,LED 仍應正常切換,雖然 Console 與 Web 不再更新,但 MCU 的離線現場控制不能失效。
現場控制驗收通過,代表 GPIO、下降緣、去彈跳、ledState 與本地獨立性都仍然保留。

重新啟動完整 App,在 Web 點擊「切換 LED」,JavaScript 應發出 toggle_led,Python 收到後呼叫 MCU,D2 完成切換並回報狀態。
畫面只能在收到 led_update 後更新,不能在 click 時先自行反轉,LED、Python 與 Web 三者必須顯示相同結果。
連續操作五次,每次都應只產生一次 LED 變化與一次狀態更新,若出現重複事件,檢查 JavaScript 是否重複載入或監聽器是否登記兩次。
遠端控制驗收通過,代表 Socket.IO、WebUI、Bridge 下行與回程都已接通。
現場操作 ─→ MCU ─→ 實體輸出
↕
Bridge
↕
瀏覽器 ─→ MPU ─→ 網頁狀態
先使用 Web 切換一次,接著按 D9 一次,再持續以現場、遠端、現場、遠端的順序交替十次。
每一次操作後,實體 LED、Python Console、第一個瀏覽器與其他瀏覽器都應顯示相同 ON/OFF,不能有任何一層停留在上一個結果。
再以較快但仍屬正常使用的速度交替操作,確認事件名稱與資料格式沒有因連續操作而失去同步。
交替驗收通過,代表系統確實只有 MCU 的 ledState 一份答案,而不是三層各自反轉自己的狀態。
逐一檢查專案檔案。
sketch/sketch.ino 只負責現場 I/O、狀態與 Bridge。python/main.py 只負責 MPU、WebUI 與訊息轉送。assets/index.html 只負責頁面結構。assets/style.css 只負責外觀。assets/app.js 只負責瀏覽器事件與畫面更新。app.yaml 正確啟用 Web UI Brick。檔案內不應存在重複的 ledState,JavaScript click 中不應直接修改 ON/OFF,Python 也不應用自己的變數猜測 MCU 結果。
責任驗收通過,代表未來維護時可以依問題層級找到對應檔案,不必在同一支程式中混合硬體、服務與畫面。
還要檢查事件名稱與欄位名稱沒有多個版本,toggle_led、led_status、led_update 與 status 應在對應檔案中保持一致,這些字串就是跨層介面,任一處自行更名都可能讓整條路徑停止。
最終異常排查順序
若 Web 點擊沒有反應,依序檢查 DOM 元素、click、socket.emit()、ui.on_message()、Bridge.call() 與 MCU Bridge.provide()。
若 LED 已切換但頁面未更新,依序檢查 MCU led_status、Python Bridge.provide()、ui.send_message()、JavaScript socket.on() 與 data.status。
若現場按鈕不穩定,回到 MCU 的下降緣與去彈跳,不要在 Python 或 JavaScript 增加補償狀態,分層除錯才能維持唯一答案。
一路走到現在,恭喜你完成智慧路口 Edge Box 3.0,也學會如在一塊板子上同時控制兩個腦袋。
