Edge Service 已能持續待命,今天要讓 MPU 第一次向 MCU 下達切換命令。
前一天加入 App.run() 後,Python 不再執行完就離開,MPU 已經有能力長時間等待事件。
今天要建立第一條橋,讓 Python 透過 Bridge 呼叫 MCU 端既有的 toggle_led(),先完成由 MPU 往 MCU 的下行控制路徑。
Bridge 的兩端使用相同字串辨認功能,MPU 呼叫 "toggle_led",MCU 也必須以 "toggle_led" 登記可被呼叫的函式,字串內容若有大小寫或底線差異,兩端便無法對上。
MPU 只提出切換要求,不直接傳送 HIGH 或 LOW,也不自行修改 ledState,真正的狀態反轉與 D2 輸出仍由 MCU 的 toggle_led() 完成。
這種分工讓 Bridge 傳送的是「要做什麼」,而不是要求 MPU 越過 MCU 直接操作腳位,未來不論命令來自測試程式或網頁,MCU 都會沿用相同的狀態規則,現場按鈕與上層服務不會各自建立一套燈號控制方式。
在 sketch/sketch.ino 加入 Router Bridge 標頭,並建立 on_toggle_led(),完整 MCU 程式如下。
// 本日新增
#include <Arduino_RouterBridge.h>
const int LED_PIN = 2;
const int BTN_PIN = 9;
bool ledState = false;
bool lastBtn = HIGH;
void toggle_led() {
ledState = !ledState;
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
}
void on_toggle_led() {
toggle_led();
}
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BTN_PIN, INPUT_PULLUP);
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
Bridge.begin();
Bridge.provide("toggle_led", on_toggle_led);
}
void loop() {
bool btn = digitalRead(BTN_PIN);
if (lastBtn == HIGH && btn == LOW) {
toggle_led();
delay(50);
}
lastBtn = btn;
}
Bridge.begin() 啟動 MCU 端橋接功能,Bridge.provide() 將字串名稱與實際函式連在一起,收到呼叫後執行的是 on_toggle_led(),再由它使用既有的 toggle_led()。
Bridge.provide() 的作用只是登記,不會在開機時自行切換 LED,真正動作仍要等 MPU 呼叫同名功能,這也是為何它放在 setup(),MCU 啟動時只完成一次準備,後續呼叫則由 Bridge 送到已登記的入口。
on_toggle_led() 看起來只有一行,但它把對外可呼叫的介面與內部狀態函式分開,未來若 Bridge 入口需要增加檢查,可以修改包裝函式,而不必改動 D9 按鈕同樣使用的 toggle_led()。
Python 端同時匯入 App 與 Bridge,保留 Day 12 的 callback 名稱,並在服務啟動前暫時呼叫一次 on_toggle_led()。
# 本日新增
from arduino.app_utils import App, Bridge
service_name = "Intersection Edge Service"
service_ready = True
def on_led_status(state: bool):
pass
def on_toggle_led(client_id, data):
Bridge.call("toggle_led")
print("已要求 MCU 切換 LED")
print("Service ready:", service_name)
on_toggle_led(None, None)
App.run()
client_id 與 data 目前尚未由 Web 傳入,因此測試時使用 None 代替,函式內真正重要的是 Bridge.call("toggle_led"),它會尋找 MCU 登記的同名入口。
這一行啟動測試呼叫只是為了在尚未建立網頁時驗證通道,程式每次重新啟動都會切換一次 LED,測試時要先記住目前燈號,避免把正常的一次切換誤認為自行亂跳,後續階段驗收完成後會將這個暫時呼叫移除。
按下 Run 後,Python Console 應顯示已送出切換要求,D2 LED 也應改變一次狀態,完整路徑是 MPU 呼叫 Bridge、MCU 執行 on_toggle_led(),最後共用 toggle_led() 更新 ledState 與硬體輸出。
接著按下 D9,LED 仍應正常切換,這代表遠端命令與現場按鈕共用同一個狀態入口,新增 Bridge 並沒有繞過 MCU 的管理規則。
若 Console 顯示已送出命令但 LED 沒有動作,先比對兩端是否都使用完全相同的 toggle_led 字串,再確認 MCU 已執行 Bridge.begin() 與 Bridge.provide(),不要先去修改 LED 腳位或建立第二個狀態變數,問題應集中在橋接登記與名稱對應。
為了看清楚 Bridge 是否只觸發一次,可先重新啟動 App,確認 ledState 初始為 false、D2 維持熄滅,再觀察 Python 啟動後是否只亮起一次,之後按下 D9 應再熄滅,這個順序能分辨啟動命令、現場操作與按住跳動三種不同現象,也能進一步確認兩個入口確實共用同一份狀態。
Python 現在可以送出命令,但 Console 顯示的只是「已要求切換」,不能證明 MCU 一定完成動作,若硬體、名稱或 Bridge 設定有問題,MPU 不應自行假設 LED 已經改變。
現在 MPU 已能把切換要求送到 MCU,但它只知道命令送出,還不知道硬體是否真的完成動作,下一篇要讓 MCU 主動回報真正的 ledState。
下一篇|由 MCU 主動回報狀態