iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
自我挑戰組

一塊板子,兩個腦袋:Arduino UNO Q 30 天實戰系列 第 25

Day 25|把前端 Web 操作送到後端 Python

  • 分享至 

  • xImage
  •  

Day 25|把前端 Web 操作送到後端 Python

瀏覽器已能接住點擊,今天要把這個操作送到 Python,再交給 MCU。

前一天完成 click 事件,每次點擊按鈕都能在瀏覽器 Console 留下紀錄,但命令仍停在前端。

今天加入 Socket.IO 與 WebUI,讓 JavaScript 發出 toggle_led,Python 收到後再呼叫 MCU 切換實體 LED。

Socket.IO 負責傳送瀏覽器事件

JavaScript 需要先建立 Socket.IO 連線,才能將前端事件送到 MPU,io() 會建立 socket 物件,後續使用 socket.emit() 發出具名事件。

事件名稱固定使用 "toggle_led",這個名稱會同時出現在 JavaScript 與 Python,字串若有大小寫、底線或拼字差異,兩端就無法配對。

在 HTML 載入 Socket.IO

assets/index.htmlapp.js 前加入 Socket.IO 程式庫。

  <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>  <!-- 本日新增 -->
  <script src="app.js"></script>
</body>

Socket.IO 必須先載入,app.js 才能使用 io(),若順序顛倒,瀏覽器會顯示找不到 io 的錯誤。

載入成功後,const socket = io(); 只建立一個通訊物件,不會自行送出切換命令,真正的事件仍要等 click callback 執行,這能將連線建立與使用者操作分成兩個清楚步驟。

HTML 的標題、狀態區與按鈕都維持 Day 24 版本,本日只增加通訊所需的 script。

JavaScript 發出 toggle_led

assets/app.js 改成以下內容。

const socket = io();  // 本日新增

const statusEl = document.getElementById("status");
const toggleBtn = document.getElementById("toggle-btn");

toggleBtn.addEventListener("click", () => {
  socket.emit("toggle_led", {});  // 本日新增
});

socket.emit("toggle_led", {}) 會送出事件名稱與一個空物件,本次切換不需要額外參數,因此資料內容保持 {}

空物件仍是一份明確的事件資料,Python 端會在 data 參數收到它,日後若操作需要附帶其他內容,可以在同一個位置增加欄位,但本專案的切換命令不需要指定目標狀態,因為真正切換規則由 MCU 管理。

點擊時不再直接修改 statusEl,也不反轉 class,瀏覽器只提出要求,畫面要等 MCU 完成切換並回報後才更新。

Python 建立 WebUI 接收入口

Day 17 的 python/main.py 加入 WebUI,並登記前一天已準備好的 on_toggle_led()

from arduino.app_utils import App, Bridge
from arduino.app_bricks.web_ui import WebUI

ui = WebUI()

def on_led_status(state: bool):
    label = "ON" if state else "OFF"
    message = {"status": label}
    print("LED 狀態:", message["status"])

def on_toggle_led(client_id, data):
    print("收到 Web 切換命令:", client_id, data)  # 本日新增
    Bridge.call("toggle_led")

Bridge.provide("led_status", on_led_status)
ui.on_message("toggle_led", on_toggle_led)  # 本日新增

App.run()

ui.on_message() 將 Web 事件名稱與 Python callback 連在一起,收到事件時,WebUI 會提供 client_iddata,再由函式呼叫 MCU。

ui = WebUI() 必須在登記事件前建立,否則 Python 沒有可用的 WebUI 物件,ui.on_message() 也無法執行,匯入、建立物件、定義函式、登記事件與 App.run() 的順序應保持清楚。

client_id 可用來辨認是哪一個瀏覽器連線送出操作,data 則是 JavaScript 傳來的空物件,本篇先將兩者印出,方便確認事件確實抵達 MPU。

驗證 Web 到 MCU 的控制鏈

按下 Run 並開啟管理頁面,點擊「切換 LED」,Python Console 應顯示收到 Web 命令,D2 LED 也應切換一次。

完整路徑為瀏覽器 click、socket.emit()、Python on_toggle_led()Bridge.call()、MCU on_toggle_led(),最後進入既有的 toggle_led()

若 Console 沒有訊息,先檢查 Socket.IO 是否成功載入,以及 JavaScript 與 Python 是否都使用 "toggle_led",若 Python 收到訊息但 LED 不動,再檢查 Bridge 與 MCU 登記。

可先保留 Python 的除錯輸出,確認 client_id{} 每次點擊都會出現一次,若瀏覽器連點五次,Python 也應收到五次事件,這能先驗證 Web 到 MPU,再繼續追蹤 Bridge。

畫面暫時仍維持原狀

MCU 切換後雖然會呼叫 led_status,Python 也能顯示 ON/OFF,但本日尚未將結果推送回瀏覽器,因此網頁可能仍顯示原本的 OFF。

這不是狀態錯誤,而是回傳路徑尚未完成,前端已能控制現場,卻還沒有接收真正結果,下一篇會補上 Python 到 Web 的主動更新。

本日驗收應分成兩段,先以瀏覽器 Console 確認 click callback 與 Socket.IO 沒有錯誤,再以 Python Console 與實體 LED 確認命令抵達 MPU 與 MCU,只有兩段都成立,才能判定 Web 控制鏈完成,而不能只因按鈕有 active 外觀就視為成功。

完成測試後,保留一筆 Web 命令與一次 LED 切換的對應紀錄,確認事件沒有遺失或重複送出,Python 每次都只呼叫 MCU 一次。

完成後應同時看到 Python 接收紀錄與實體 LED 變化,才能證明命令真的跨越兩層。

本篇完成內容

  • 使用 io() 建立 Socket.IO 連線。
  • JavaScript 發出 toggle_led 事件。
  • Python 以 ui.on_message() 接收 Web 命令。
  • 完成 Web → MPU → MCU 控制鏈。

現在 Web 操作已能一路送到 MCU 並切換實體 LED,但管理畫面還不知道真正結果,下一篇要讓 Python 主動把 MCU 回報推回瀏覽器。

下一篇|用 Python 主動更新網頁狀態


上一篇
Day 24|接住使用者互動操作事件
系列文
一塊板子,兩個腦袋:Arduino UNO Q 30 天實戰25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言