2026年9月3日 星期四

用 micro:bit Python Editor 學習真正的 MicroPython:從 LED、按鍵到無線互動

用 micro:bit Python Editor 學習真正的 MicroPython:從 LED、按鍵到無線互動

當你已經用 MakeCode 讓 micro:bit 動起來,下一步可以改用 micro:bit Python Editor。它採用 MicroPython,程式結構更接近一般 Python;學生能保留硬體實作的即時回饋,同時學習 importwhile True、函式、序列輸出與模組化程式。

編輯器:micro:bit Python Editor v3
建議對象:具備 MakeCode 基礎、準備進入文字型 Python 程式設計的學生。

一、為什麼要學 micro:bit Python Editor?

學習階段你會學到什麼?
MakeCode 積木程式邏輯、事件、硬體輸入與輸出
MakeCode PythonPython 風格的硬體控制與程式結構
micro:bit Python EditorMicroPython 的 importwhile True、除錯主控台與模組
IDLE Python檔案、資料分析、API、AI 與完整應用程式

micro:bit Python Editor 有模擬器、語法提示、錯誤標示與序列主控台。先在模擬器測試,再傳送到實體 micro:bit,可以降低初次接觸文字程式的挫折感。

二、第一行程式:匯入 micro:bit 工具箱

from microbit import *

這行程式把 micro:bit 的 LED、按鍵、溫度、加速度、腳位與時間功能載入。目前的程式可以使用 displaybutton_asleep() 等名稱。

三、範例 1:讓 LED 顯示愛心

from microbit import *

display.show(Image.HEART)
sleep(1000)
display.clear()

練習:Image.HEART 改成 Image.HAPPYImage.SAD 或自己建立 5×5 圖案。

四、範例 2:按 A 顯示 A,按 B 顯示 B

from microbit import *

while True:
    if button_a.was_pressed():
        display.show('A')
    if button_b.was_pressed():
        display.show('B')

while True: 表示「永遠重複」。程式不是一直盯著按鍵不放,而是持續檢查是否發生「按過一次」的事件。

五、範例 3:搖一搖,顯示隨機點數

from microbit import *
from random import randint

while True:
    if accelerometer.was_gesture('shake'):
        number = randint(1, 6)
        display.show(str(number))
        sleep(800)

這個例子同時練習:感測器、條件判斷、變數、隨機數與字串轉換。

六、範例 4:溫度計與序列主控台

from microbit import *

while True:
    celsius = temperature()
    display.scroll(str(celsius) + 'C')
    print(celsius)
    sleep(2000)

display.scroll() 是給板子上的人看;print() 則會輸出到 Python Editor 的序列主控台,適合觀察資料是否合理、協助除錯。

七、範例 5:兩片 micro:bit 無線傳話

請用兩片 micro:bit,兩端都下載下列程式。按下 A 就送出訊息;接收端會顯示收到的內容。

from microbit import *
import radio

radio.on()
radio.config(group=23)

while True:
    if button_a.was_pressed():
        radio.send('Hello')
        display.show(Image.HAPPY)

    message = radio.receive()
    if message:
        display.scroll(message)

重點:兩片 micro:bit 的 group 必須相同,才像在同一個無線頻道。請改成不同組別測試,觀察訊息是否還能收到。

八、從 MakeCode Python 搬家時,不能直接複製的地方

MakeCode Pythonmicro:bit MicroPython
basic.show_number(51)display.show('5')display.scroll('51')
basic.forever(...)while True:
radio.send_value()radio.send()radio.receive()

兩者都是 Python,但 API 不同。若要控制 CuteBot、Joystick:bit 等擴充硬體,必須使用廠商提供、可在 MicroPython 使用的模組;MakeCode 的擴充功能不能直接複製過來。

九、課堂任務:微型智慧環境站

  1. 顯示目前溫度;當溫度高於自訂門檻時顯示警示圖案。
  2. 按 A 顯示目前溫度,按 B 顯示光線值。
  3. 每 5 秒用 Radio 傳送一行格式化資料,例如 T=28,L=120
  4. 另一片 micro:bit 收到資料後顯示圖示,並在序列主控台列印收到的原始訊息。

十、學習檢核

  • 我能說明 from microbit import * 的用途。
  • 我能用 while True 撰寫持續運作的程式。
  • 我能用序列主控台檢查 print() 輸出的資料。
  • 我能設定兩片 micro:bit 使用相同的 Radio 群組並互相傳訊息。
  • 我知道 MakeCode Python 與 MicroPython 的語法相似,但 API 不可直接混用。

參考資源:micro:bit Python Editor v3官方 Python Editor 指南

Python 程式實習:MakeCode Python 如何指揮 micro:bit 硬體?

Python 程式實習:MakeCode Python 如何指揮 micro:bit 硬體?

當我們寫下 radio.send_value("x", 35)cuteBot.motors(60, 60) 時,真正發生了什麼?本教材帶你理解:程式碼不是直接碰觸硬體,而是經過編譯、執行環境與硬體介面,最後才變成 LED、無線訊號、感測器讀值與馬達動作。

一、學習目標

  • 分辨 MakeCode Python、MicroPython 與電腦 IDLE Python 的差異。
  • 說明 MakeCode Python 從程式碼到 micro:bit 硬體動作的流程。
  • 理解事件驅動程式與一般由上而下執行程式的差別。
  • 能從 API 名稱推論其操作的硬體資源與通訊方式。

二、先釐清:它們不在同一個 Python 世界

環境主要用途程式在哪裡執行?
MakeCode Python以 Python 風格控制 micro:bit、Radio、馬達與感測器編譯後,在 micro:bit 上執行
IDLE Python檔案、資料分析、網頁、AI 與一般軟體開發電腦上的 CPython
MicroPython在微控制器上執行 Python 直譯器支援 MicroPython 韌體的微控制器

三者都有 ifforwhiledef 等共通語法;但可呼叫的函式庫、執行方式與目標硬體不同。MakeCode Python 的重點是「用接近 Python 的語法,使用 MakeCode 的硬體 API」。

三、從程式碼到硬體:中間經過了什麼?

MakeCode
Python
PXT 轉換
與編譯
.hex
機器碼
micro:bit
執行環境
LED、Radio、
腳位、I2C、馬達

你按下「下載」時,MakeCode 會將程式編譯成 micro:bit 的 ARM 處理器可執行的 .hex 檔。下載到開發板後,micro:bit 的執行環境才依序處理按鍵、時間、無線接收與硬體控制。

四、API 是硬體的翻譯字典

MakeCode Python它要做什麼?底層硬體資源
basic.show_number(51)在板子上顯示 515×5 LED 矩陣
radio.send_value("x", 35)送出 x 軸控制資料2.4 GHz 無線電
pins.digital_write_pin(...)輸出高、低電位GPIO 腳位
cuteBot.motors(60, 60)設定左右輪速度腳位或 I2C 與馬達驅動板

擴充功能的價值,是將複雜的電路與通訊細節包裝成可讀的 API。學生仍要理解:函式不是魔法,它是在請底層系統操作某項硬體資源。

五、MakeCode 為什麼常用事件?

在 IDLE 中,我們常把程式想成由上而下執行;但硬體系統必須隨時等候按鍵、無線資料或感測器變化,因此 MakeCode 常採用事件驅動。

def on_received_value(name, value):
    if name == "x":
        xValue = value

radio.on_received_value(on_received_value)

這段程式的意思不是「立刻執行一次後結束」,而是向執行環境登記:以後只要收到廣播鍵值,就呼叫這個函式。

相對地,basic.forever() 則適合放需要持續檢查的工作,例如馬達輸出、逾時判斷或感測距離。

六、程式實驗:觀察同一個概念在不同環境的差異

實驗 A:顯示與輸出

# MakeCode Python:對硬體說話
basic.show_number(51)

# IDLE Python:對終端機說話
print(51)

實驗 B:重複執行與逾時安全

def on_forever():
    if control.millis() - lastReceiveTime > 500:
        cuteBot.motors(0, 0)

basic.forever(on_forever)

請學生回答:這段程式為何要持續執行?若只檢查一次,斷線安全還有效嗎?

七、常見錯誤與除錯方法

現象可能原因檢查方式
IDLE 找不到 basic在錯誤環境執行 MakeCode API改回 MakeCode Python;或重寫為電腦版程式。
程式不能轉回積木某些 Python 寫法無法雙向轉換保留 Python 模式並檢查是否可編譯下載。
小車不動群組不同、未收到資料或安全條件成立先顯示群組、資料值與逾時計時。

八、挑戰任務:從硬體控制走向系統設計

  1. 為 Q霸小車新增一個「狀態回報」訊息,讓搖桿知道小車是否避障或斷線。
  2. 把遙控系統畫成資料流圖,標示程式、執行環境、無線傳輸與硬體輸出。
  3. 比較 MakeCode Python 與 IDLE Python:若要做「兒童館小車使用紀錄」應在哪一端執行?為什麼?
本課結論:MakeCode Python 讓我們用接近 Python 的方式撰寫硬體邏輯;編譯器與 micro:bit 執行環境則負責把這些指令變成真正的電氣訊號與裝置行為。理解這層關係,才能從「會寫積木」進階到「能設計可靠的智慧系統」。

網際網路應用:我的搖桿,只開我的小車!

網際網路應用:我的搖桿,只開我的小車!

今天我們用搖桿控制 Q霸小車,也用它認識網路。網路的工作就是:送訊息、收訊息、確認是不是傳給對的人。

今天完成後,你可以做到:
① 用搖桿開車 ② 讓小車不會撞到東西 ③ 讓你的搖桿不會開到別人的車

一、網路像傳話遊戲

生活中的角色小車系統裡是誰?
說話的人搖桿
傳話的路無線電波
聽話的人Q霸小車
回覆「收到」嗶一聲、震一下、顯示勾勾

二、開車四步驟

  1. 看同色號:搖桿和小車要有一樣的顏色與號碼。
  2. 等一下:看到勾勾、聽到嗶聲、感覺震動,表示配對成功。
  3. 推搖桿:往前推前進;往左、右推就轉彎。
  4. 遇到危險:按紅色 F,立刻停!

三、四個按鍵

C慢慢開
D正常開
E快快開
F立刻停

四、小車的三個安全規則

  1. 太久沒聽到搖桿:小車會自己停下來。
  2. 前面太近:小車會先慢慢走;再靠近就停下來。
  3. 按 F:小車一定先停,按 C、D 或 E 才能再開。

五、我們也會看懂一點程式

下面這段意思是:「如果太久沒收到訊息,小車停止。」

if control.millis() - lastControlTime > 500:
    cuteBot.motors(0, 0)

下面這段意思是:「第 2 組只聽第 2 組的搖桿。」

pairId = 2
radio.set_group(100 + pairId)

六、闖關任務

  1. 和同學同時開兩組小車,看看會不會互相影響。
  2. 讓小車靠近障礙物,觀察它什麼時候會慢下來、什麼時候會停。
  3. 行駛中按 F,確認小車能不能立刻停止。
  4. 用自己的話說出:為什麼搖桿和小車要同色同號?
記住:網路不是只有手機和電腦。搖桿、小車、感測器也可以互相傳訊息,這就是物聯網!

延伸閱讀:V1V2V3V4V5

網際網路應用:一支搖桿如何可靠地控制一台 Q霸小車?

網際網路應用:一支搖桿如何可靠地控制一台 Q霸小車?

「遙控車」看似只是硬體活動,其實是一個縮小版的網際網路系統:裝置收集輸入、封裝成訊息、透過無線網路傳送、接收端解讀資料並做出行動。本教材從 Joystick:bit × Q霸小車 V1~V5,認識網際網路應用最重要的可靠性、安全性與多人協作問題。

一、學習目標

  • 說明感測端、傳輸端、服務端與致動端的角色。
  • 理解廣播群組、鍵值訊息、回覆確認與逾時處理。
  • 以「安全優先」思維分析物聯網應用。
  • 設計多人共用場域的一對一裝置配對方案。

二、一台遙控車就是一個網路應用

系統角色本案例網路概念
輸入端Joystick:bit x、y 軸與四鍵資料產生者
傳輸端micro:bit Radio無線通道與群組
處理端小車端 Python 程式訊息解析、狀態機、規則
輸出端馬達、燈號、蜂鳴器服務回應與人機介面

三、V1~V5 對應的網路議題

  1. V1|訊息傳遞:搖桿將 x、y 值以鍵值訊息送出,小車依名稱接收。
  2. V2|可靠性:死區避免雜訊;超過 0.5 秒未收到訊息便停車,這就是逾時偵測。
  3. V3|邊緣運算:小車本地讀取超音波距離,快速做出降速或停止決策。
  4. V4|事件驅動:C、D、E、F 鍵不是持續輪詢,而是在按下事件發生時更新系統狀態。
  5. V5|識別與回覆:pairId 選擇專屬群組,並用 pair / ack 做握手確認。

四、資料封包設計:不要只傳數字

有意義的訊息必須包含「名稱」與「值」。例如 ("x", 35) 表示向右 35,("stop", 1) 表示緊急停止。這種設計比只傳一串數字更容易除錯與擴充。

# 搖桿端
radio.send_value("x", x)
radio.send_value("y", y)
radio.send_value("stop", 1)

# 小車端
if name == "x":
    xValue = value
elif name == "stop":
    emergencyStop = value == 1

討論:如果未來要加入「車燈」、「任務開始」與「低電量」,應如何命名訊息?請提出一份小組通訊協定表。

五、可靠性與安全性:先判斷不能做什麼

真正的網路應用不能假設每個封包都會到達。小車端的決策順序應為:

未配對/緊急停止 → 斷線停車 → 避障 → 一般控制
if emergencyStop:
    cuteBot.motors(0, 0)
elif control.millis() - lastControlTime > 500:
    cuteBot.motors(0, 0)
elif yValue > 0 and distance <= 15:
    cuteBot.motors(0, 0)
else:
    cuteBot.motors(leftSpeed, rightSpeed)

六、多人同時使用:廣播群組就像班級頻道

若所有設備都使用群組 51,一位學生的搖桿可能控制全班的小車。V5 將每一組設備設為專屬頻道:

pairId = 3
radio.set_group(100 + pairId)  # 第 3 組使用群組 103

radio.send_value("pair", pairId)
# 小車確認後:radio.send_value("ack", pairId)

這相當於網路上的「位址+連線確認」。雖然不是完整的 TCP/IP,卻已經具備辨識對象、建立關係與回覆確認的核心精神。

七、3 小時課堂任務

  1. 30 分鐘:畫出 V5 的資料流,標記每一種訊息與方向。
  2. 50 分鐘:完成兩組小車同時運作,驗證不互控。
  3. 40 分鐘:刻意關閉搖桿或遮住超音波,記錄安全機制的反應。
  4. 40 分鐘:設計一項新服務,例如車位預約、任務通知或低電量警示。
  5. 20 分鐘:用「問題、資料、規則、結果」分享設計。

八、延伸閱讀

V1 基礎遙控V2 可靠控制V3 安全避障V4 事件互動V5 配對與回饋

Python 程式實習:從搖桿遙控車學會事件、通訊與安全控制

Python 程式實習:從搖桿遙控車學會事件、通訊與安全控制

寫程式不只是讓硬體動起來,而是把使用者操作、資料傳輸、狀態判斷與安全處置串成可靠的系統。本教材以 Joystick:bit 遙控 Q霸小車的五個版本為案例,帶領大三學生由「可動」走向「可維運」。

適用課程:Python 程式實習、物聯網與智慧生活、嵌入式系統入門
建議時間:3 小時實作課+1 週小組延伸挑戰

一、學習目標

  1. 能以 Python 讀取搖桿資料,並透過無線廣播控制雙輪車。
  2. 能用變數、函式、條件判斷、迴圈與事件處理程式互動。
  3. 理解 x、y 軸混合為左右馬達速度的差速控制概念。
  4. 能加入死區、逾時停車、避障與緊急停止等安全機制。
  5. 能為多人共用場域設計一對一裝置配對與可維護的編號策略。

二、從 V1 到 V5:程式能力如何成長?

版本核心功能Python 觀念
V1搖桿讀值、廣播遙控變數、對應換算、無限迴圈
V2死區、三段速度、斷線停車條件判斷、狀態變數、時間差
V3超音波避障感測資料、優先順序、限制輸出
V4C、D、E、F 四鍵操作事件驅動、函式、介面設計
V5一對一配對、蜂鳴器、震動回饋通訊協定、握手、雙向狀態回傳

三、第一個關鍵:把搖桿數值變成可用的控制值

Joystick:bit 的 x、y 軸原始值範圍為 0~1023;馬達速度較適合使用 -100~100。先進行範圍對應,再做死區處理,才能避免搖桿已回正、小車卻慢慢滑動的問題。

x = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.X),
    0, 1023, -100, 100)
y = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.Y),
    0, 1023, -100, 100)

if x >= -10 and x <= 10:
    x = 0
if y >= -10 and y <= 10:
    y = 0

思考題:為什麼死區不能設得太小?又為什麼不能設得太大?請分別從感測器誤差與操控精準度說明。

四、第二個關鍵:差速控制不是左右各寫一個方向

y 作為前後速度、x 作為轉向量,左右輪可以用下面兩行程式同時完成前進、後退與轉彎:

leftSpeed = Math.constrain(yValue + xValue, -100, 100)
rightSpeed = Math.constrain(yValue - xValue, -100, 100)
cuteBot.motors(leftSpeed, rightSpeed)

其中 Math.constrain() 是防呆設計:斜向推搖桿時,加總可能超過 100;若不限制,行為會因硬體函式處理方式不同而不穩定。

五、第三個關鍵:安全程式要有優先順序

安全不是在最後補一個 if。小車端應明確定義決策順序:

未配對或緊急停止 → 斷線停車 → 超音波避障 → 一般搖桿控制
if emergencyStop:
    cuteBot.motors(0, 0)
elif control.millis() - lastControlTime > 500:
    cuteBot.motors(0, 0)
elif yValue > 0 and distance > 0 and distance <= 15:
    cuteBot.motors(0, 0)
elif yValue > 0 and distance <= 30:
    cuteBot.motors(leftSpeed * 0.4, rightSpeed * 0.4)
else:
    cuteBot.motors(leftSpeed, rightSpeed)

這是嵌入式系統常見的「安全優先」寫法。程式可讀性比把所有條件塞進一行更重要,因為日後需要維護、測試與除錯。

六、第四個關鍵:多人場域不能共用同一個廣播群組

若兒童館內十支搖桿都使用群組 51,任一孩子推動搖桿都可能讓十台車一起動。V5 的改善方式是以 pairId 建立專屬群組:

pairId = 1                 # 第 1 組設備
radio.set_group(100 + pairId)  # 使用群組 101

同組搖桿與小車的 pairId 必須一致;第 2 組改為 2,便使用群組 102。再以 pairack 訊息建立握手確認:

# 搖桿端:提出配對要求
radio.send_value("pair", pairId)

# 小車端:確認同一組後回覆
if name == "pair" and value == pairId:
    paired = True
    radio.send_value("ack", pairId)

七、第五個關鍵:回饋讓系統真正可用

無線系統不能只「送出去」。V5 讓小車將狀態回傳給搖桿,並以蜂鳴器與震動告訴使用者目前情況:

  • 配對成功:短音+震動一次。
  • 慢、中、快:分別以 1、2、3 次提示確認。
  • 前方接近障礙物:提示減速;距離過近:連續提示並停止。
  • F 緊急停止:低音與震動,直到按 C、D、E 之一才解除。
def feedback(times, tone):
    for index in range(times):
        music.play_tone(tone, music.beat(BeatFraction.SIXTEENTH))
        joystickbit.Vibration_Motor(80)
        basic.pause(60)

除錯提醒:Joystick:bit 的震動函式名稱為 Vibration_Motor,其中 VM 必須大寫。

八、實作任務:把 V5 變成可展示的智慧遙控車

  1. 完成基本搖桿遙控與差速轉彎。
  2. 加入死區、三段速度與 0.5 秒斷線停車。
  3. 加入超音波避障,設定停止距離與減速距離。
  4. 使用 C、D、E、F 建立速度與緊急停止事件。
  5. 完成一對一配對;兩組設備同時開機,驗證不會互相控制。
  6. 設計一項自己的狀態回饋,例如配對失敗、低電量或任務完成提示。

九、評量規準

面向檢核重點
功能正確可前後左右控制,速度切換與四鍵功能正常。
安全設計斷線、障礙物與緊急停止都能安全停車。
程式品質變數命名清楚、重複程式整理為函式、條件順序合理。
場域思考兩組以上設備可同時使用且不互相干擾。

十、延伸挑戰

將小車設計成兒童館任務車:小朋友先配對自己的車,依顏色指示完成巡遊、避障與停靠任務;系統記錄完成時間與安全停車次數。這會把 Python 從單純語法練習,轉化為可被真實使用者操作、觀察與改進的智慧系統。

延伸閱讀:本教材的五篇實作基礎

Joystick:bit 遙控 Q霸小車 V5:一對一配對、蜂鳴器與震動回饋

Joystick:bit 遙控 Q霸小車 V5:一對一配對、蜂鳴器與震動回饋

在兒童館同時使用多台 Q霸小車時,最需要避免的是「一支搖桿控制到別組小車」。本作品以每組設備專屬的 pairId 建立無線群組,讓每位小朋友只控制自己手上的搖桿所配對的小車;同時加入蜂鳴器、震動、超音波避障、斷線停車與緊急停止功能。

學習目標
學習 micro:bit 無線群組、一對一裝置配對、雙向訊息回傳、感測安全控制與觸覺回饋設計。

一、材料準備

  • Q霸小車(CuteBot)套件 × 1
  • Joystick:bit2 擴充搖桿 × 1
  • micro:bit × 2、電池盒與電池 × 2
  • Q霸小車超音波模組 × 1

二、兒童館多組設備的配對原則

每一組設備的搖桿與小車,都設定相同的 pairId。程式會將廣播群組設為 100 + pairId,因此各組不會互相干擾。

設備組別pairId廣播群組
紅色 011101
藍色 022102
綠色 033103

部署方式:將同色、同編號貼紙貼在搖桿與小車上,並在兩端程式都改成相同的 pairId 後再下載。建議館員事先完成燒錄,兒童不需要自行設定。

三、右側四鍵功能

按鍵功能回饋
C(P12)慢速 40%1 聲短音+震動 1 次
D(P13)一般速度 70%2 聲短音+震動 2 次
E(P14)快速 100%3 聲短音+震動 3 次
F(P15)緊急停止低音+長震動;按 C/D/E 才能解除

四、程式設計重點

  1. 搖桿每秒送出一次配對訊號,小車回傳 ack 確認;搖桿收到後以勾勾、短音和震動提示。
  2. 只有完成配對的小車才會接受 x、y 搖桿控制訊號。
  3. 小車前方 15~30 cm 時自動降速;15 cm 內自動停止。
  4. 若 0.5 秒沒有收到控制訊號,小車自動停止。

五、MakeCode Python 程式

請建立兩個專案。搖桿端加入 Joystick:bit 擴充功能,小車端加入 CuteBot 擴充功能。兩端皆使用 Radio。

(一)Joystick:bit 搖桿端

pairId = 1  # 同一組的小車也必須設為相同數字
x = 0
y = 0
speed = 70
paired = False
emergencyStop = False
lastPairRequest = -1000
lastStatus = -1

def feedback(times, tone):
    for index in range(times):
        music.play_tone(tone, music.beat(BeatFraction.SIXTEENTH))
        joystickbit.Vibration_Motor(80)
        basic.pause(60)

joystickbit.init_joystick_bit()
radio.set_group(100 + pairId)
basic.show_number(pairId)

def on_button_c():
    global speed, emergencyStop
    speed = 40
    emergencyStop = False
    radio.send_value("stop", 0)
    basic.show_number(1)
    feedback(1, 523)
joystickbit.on_button_event(joystickbit.JoystickBitPin.P12,
    joystickbit.ButtonType.down, on_button_c)

def on_button_d():
    global speed, emergencyStop
    speed = 70
    emergencyStop = False
    radio.send_value("stop", 0)
    basic.show_number(2)
    feedback(2, 659)
joystickbit.on_button_event(joystickbit.JoystickBitPin.P13,
    joystickbit.ButtonType.down, on_button_d)

def on_button_e():
    global speed, emergencyStop
    speed = 100
    emergencyStop = False
    radio.send_value("stop", 0)
    basic.show_number(3)
    feedback(3, 784)
joystickbit.on_button_event(joystickbit.JoystickBitPin.P14,
    joystickbit.ButtonType.down, on_button_e)

def on_button_f():
    global emergencyStop
    emergencyStop = True
    radio.send_value("stop", 1)
    basic.show_icon(IconNames.NO)
    feedback(1, 220)
joystickbit.on_button_event(joystickbit.JoystickBitPin.P15,
    joystickbit.ButtonType.down, on_button_f)

def on_received_value(name, value):
    global paired, lastStatus
    if name == "ack" and value == pairId:
        if not paired:
            paired = True
            basic.show_icon(IconNames.YES)
            feedback(1, 988)
    if name == "status" and value != lastStatus:
        lastStatus = value
        if value == 1:
            feedback(1, 440)
        elif value == 2:
            feedback(2, 330)
        elif value == 3:
            feedback(1, 220)

radio.on_received_value(on_received_value)

def on_forever():
    global x, y, lastPairRequest
    if control.millis() - lastPairRequest > 1000:
        radio.send_value("pair", pairId)
        lastPairRequest = control.millis()
    x = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.X),
        0, 1023, -100, 100)
    y = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.Y),
        0, 1023, -100, 100)
    if x >= -10 and x <= 10:
        x = 0
    if y >= -10 and y <= 10:
        y = 0
    if emergencyStop:
        radio.send_value("stop", 1)
    elif paired:
        radio.send_value("x", x * speed / 100)
        radio.send_value("y", y * speed / 100)
    basic.pause(50)

basic.forever(on_forever)

(二)Q霸小車端

pairId = 1  # 必須和配對搖桿相同
xValue = 0
yValue = 0
distance = 0
paired = False
emergencyStop = False
lastControlTime = -1000
lastStatus = -1

def send_status(newStatus):
    global lastStatus
    if newStatus != lastStatus:
        lastStatus = newStatus
        radio.send_value("status", newStatus)

radio.set_group(100 + pairId)
basic.show_number(pairId)
cuteBot.motors(0, 0)

def on_received_value(name, value):
    global xValue, yValue, paired, emergencyStop, lastControlTime
    if name == "pair" and value == pairId:
        paired = True
        radio.send_value("ack", pairId)
    elif paired and name == "x":
        xValue = value
        lastControlTime = control.millis()
    elif paired and name == "y":
        yValue = value
        lastControlTime = control.millis()
    elif paired and name == "stop":
        emergencyStop = value == 1
        lastControlTime = control.millis()

radio.on_received_value(on_received_value)

def on_forever():
    global distance
    if not paired:
        cuteBot.motors(0, 0)
    elif emergencyStop:
        cuteBot.motors(0, 0)
        basic.show_icon(IconNames.NO)
        send_status(3)
    elif control.millis() - lastControlTime > 500:
        cuteBot.motors(0, 0)
        basic.show_icon(IconNames.SAD)
        send_status(4)
    else:
        distance = cuteBot.ultrasonic(cuteBot.SonarUnit.Centimeters)
        leftSpeed = Math.constrain(yValue + xValue, -100, 100)
        rightSpeed = Math.constrain(yValue - xValue, -100, 100)
        if yValue > 0 and distance > 0 and distance <= 15:
            cuteBot.motors(0, 0)
            basic.show_icon(IconNames.NO)
            send_status(2)
        elif yValue > 0 and distance > 15 and distance <= 30:
            cuteBot.motors(leftSpeed * 0.4, rightSpeed * 0.4)
            basic.show_icon(IconNames.SMALL_DIAMOND)
            send_status(1)
        else:
            cuteBot.motors(leftSpeed, rightSpeed)
            basic.clear_screen()
            send_status(0)
    basic.pause(30)

basic.forever(on_forever)

六、測試步驟與除錯

  1. 先將兩端的 pairId 都設為 1,下載程式並開機。
  2. 搖桿顯示勾勾、短響並震動一次,表示已與同組小車完成配對。
  3. 按 C、D、E 測試三段速度;按 F 確認小車立即停止,並按 C、D 或 E 解除停止。
  4. 在前方放置障礙物:15~30 cm 會降速,15 cm 內會停止。
  5. 若小車前進方向相反,將小車端兩個 yValue > 0 改為 yValue < 0
  6. 若震動沒有反應,確認程式使用的是 joystickbit.Vibration_Motor(80),其中 VM 必須大寫。
館員提醒:為每組設備貼上相同顏色與編號,並將搖桿與小車的 pairId 預先設好。這是讓多位兒童同時操作時仍保持安全、清楚且可管理的關鍵。

使用 Joystick:bit 擴充搖桿遙控 Q霸小車 V4

使用 Joystick:bit 擴充搖桿遙控 Q霸小車 V4

本實作使用兩片 micro:bit 進行無線通訊:一片裝在 Joystick:bit2 擴充搖桿,負責讀取搖桿方向;另一片安裝在 Q霸小車(CuteBot) 上,接收訊號後控制左右輪馬達。V4 保留搖桿死區、斷線自動停車與超音波安全避障,並啟用右側四鍵,讓速度切換與緊急停止都可單手完成。

學習目標
了解 micro:bit 廣播通訊、類比搖桿讀值、超音波感測與雙輪差速控制,完成具備安全保護與速度切換功能的 Q霸小車。

一、材料準備

  • Q霸小車套件(CuteBot)× 1
  • Joystick:bit2 擴充搖桿 × 1
  • micro:bit × 2
  • 電池盒與電池 × 2

二、操作前準備

  1. 開啟 Microsoft MakeCode for micro:bit,分別建立「搖桿端」與「小車端」兩個專案。
  2. 在兩個專案中加入 Radio(廣播) 功能;搖桿端另加入 Joystick:bit 擴充功能,小車端加入 CuteBot 擴充功能。
  3. 兩端的廣播群組都設定為 51。群組號碼必須相同,才能互相通訊。
  4. 分別下載程式到兩片 micro:bit,再接上電池並開機。

三、搖桿端編程:死區、三段速度與訊號發送

搖桿的原始讀值介於 0~1023。本程式利用「對應」積木將數值換算為 -100~100,並先判斷搖桿是否落在中央的死區範圍,再依速度模式縮放輸出值,最後以廣播鍵值送出 xy

  • x 軸:控制左右轉向。
  • y 軸:控制前進與後退。
  • 死區 -10~10:數值位於此區間時歸零,避免小車自己緩慢移動。
  • 三段速度:按右側 C 鍵為慢速 40%、D 鍵為一般 70%、E 鍵為快速 100%。F 鍵為緊急停止。
  • 廣播群組 51:作為搖桿與小車共用的無線頻道。
圖1 Joystick:bit 搖桿端積木程式(可依下列 V2 邏輯補強)

搖桿端 V2 積木邏輯

  1. 建立變數:xyspeed
  2. 啟動時設定 speed = 70,廣播群組設為 51。
  3. 按下 A:設定 speed = 40 並顯示 1;按下 B:設定 speed = 70 並顯示 2;按下 A+B:設定 speed = 100 並顯示 3。
  4. 重複無限次:讀取並對應 x、y 軸;若 x 或 y 介於 -10~10,將該值設為 0;最後分別將 x、y 乘以 speed ÷ 100 後廣播。

四、Q霸小車端編程:超音波安全避障、斷線停車與馬達控制

小車端收到廣播鍵值後,依名稱將數值分別存入 xValueyValue,同時記錄最後一次收到訊號的時間。若超過 500 毫秒沒有收到新訊號,便立刻停止兩輪馬達;向前行駛時,超音波感測器會依前方距離自動降速或停車。

左輪速度 = yValue + xValue
右輪速度 = yValue − xValue

當搖桿向前推時,兩輪同向前轉;向右推時,左輪速度增加、右輪速度降低,小車便向右轉。向左、向後的原理相同。斷線停車能避免電池沒電、搖桿關機或超出廣播距離時,小車仍維持原方向行駛。

圖2 Q霸小車端積木程式

小車端 V3 積木邏輯

  1. 建立變數:xValueyValuelastReceiveTime
  2. 啟動時將廣播群組設為 51,並讓兩輪馬達停止。
  3. 當收到廣播鍵值:名稱為 x 時更新 xValue;名稱為 y 時更新 yValue;每次收到訊號都將 lastReceiveTime 設為「運行時間(毫秒)」。
  4. 重複無限次:若「運行時間 − lastReceiveTime」大於 500,設定左右輪速度都為 0;否則讀取超音波距離。
  5. 前方距離為 15~30 cm 時,向前行駛自動降為 40% 速度;距離小於等於 15 cm 時,向前行駛立即停止。

五、MakeCode Python 程式碼

若要使用 Python 模式,請在 MakeCode 先加入 Joystick:bit 與 CuteBot 擴充功能,再分別建立兩個專案並貼上以下程式。

(一)Joystick:bit 搖桿端

x = 0
y = 0
speed = 70

radio.set_group(51)
basic.show_number(2)  # 開機預設為一般速度

def on_button_pressed_a():
    global speed
    speed = 40
    basic.show_number(1)  # 慢速
input.on_button_pressed(Button.A, on_button_pressed_a)

def on_button_pressed_b():
    global speed
    speed = 70
    basic.show_number(2)  # 一般速度
input.on_button_pressed(Button.B, on_button_pressed_b)

def on_button_pressed_ab():
    global speed
    speed = 100
    basic.show_number(3)  # 快速
input.on_button_pressed(Button.AB, on_button_pressed_ab)

def on_forever():
    global x, y

    x = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.X),
        0, 1023, -100, 100)
    y = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.Y),
        0, 1023, -100, 100)

    # 搖桿死區:回到中央時停止
    if x >= -10 and x <= 10:
        x = 0
    if y >= -10 and y <= 10:
        y = 0

    # 依速度模式縮放輸出值
    x = x * speed / 100
    y = y * speed / 100

    radio.send_value("x", x)
    radio.send_value("y", y)
    basic.pause(50)

basic.forever(on_forever)

(二)Q霸小車端:V3 超音波安全避障

xValue = 0
yValue = 0
lastReceiveTime = -1000
distance = 0

radio.set_group(51)
basic.show_number(51)
cuteBot.motors(0, 0)

def on_received_value(name, value):
    global xValue, yValue, lastReceiveTime
    if name == "x":
        xValue = value
    elif name == "y":
        yValue = value
    lastReceiveTime = control.millis()

radio.on_received_value(on_received_value)

def on_forever():
    global distance
    # 優先保護 1:逾 0.5 秒未收到訊號,立即停車
    if control.millis() - lastReceiveTime > 500:
        cuteBot.motors(0, 0)
    else:
        distance = cuteBot.ultrasonic(cuteBot.SonarUnit.Centimeters)
        leftSpeed = Math.constrain(yValue + xValue, -100, 100)
        rightSpeed = Math.constrain(yValue - xValue, -100, 100)

        # 優先保護 2:僅在向前時啟用前方避障
        if yValue > 0 and distance > 0 and distance <= 15:
            cuteBot.motors(0, 0)
            basic.show_icon(IconNames.NO)
        elif yValue > 0 and distance > 15 and distance <= 30:
            cuteBot.motors(leftSpeed * 0.4, rightSpeed * 0.4)
            basic.show_icon(IconNames.SMALL_DIAMOND)
        else:
            cuteBot.motors(leftSpeed, rightSpeed)
            basic.clear_screen()
    basic.pause(30)

basic.forever(on_forever)

測試提示:本程式假設 yValue > 0 是前進。如果實測發現前進方向相反,請把小車端的兩個 yValue > 0 改為 yValue < 0

六、V4 四鍵駕駛面板 Python 程式

以下程式為 V4 正式測試版。右側按鍵 C、D、E 用於速度選擇,F 為緊急停止;按下 F 後,必須按 C、D 或 E 才能重新啟動。

按鍵功能
C(P12)慢速 40%
D(P13)一般 70%
E(P14)快速 100%
F(P15)緊急停止

(一)Joystick:bit 搖桿端 V4

x = 0
y = 0
speed = 70
emergencyStop = False

joystickbit.init_joystick_bit()
radio.set_group(51)
basic.show_number(2)

def on_button_c():
    global speed, emergencyStop
    speed = 40
    emergencyStop = False
    radio.send_value("stop", 0)
    basic.show_number(1)
joystickbit.on_button_event(joystickbit.JoystickBitPin.P12,
    joystickbit.ButtonType.down, on_button_c)

def on_button_d():
    global speed, emergencyStop
    speed = 70
    emergencyStop = False
    radio.send_value("stop", 0)
    basic.show_number(2)
joystickbit.on_button_event(joystickbit.JoystickBitPin.P13,
    joystickbit.ButtonType.down, on_button_d)

def on_button_e():
    global speed, emergencyStop
    speed = 100
    emergencyStop = False
    radio.send_value("stop", 0)
    basic.show_number(3)
joystickbit.on_button_event(joystickbit.JoystickBitPin.P14,
    joystickbit.ButtonType.down, on_button_e)

def on_button_f():
    global emergencyStop
    emergencyStop = True
    radio.send_value("stop", 1)
    basic.show_icon(IconNames.NO)
joystickbit.on_button_event(joystickbit.JoystickBitPin.P15,
    joystickbit.ButtonType.down, on_button_f)

def on_forever():
    global x, y
    x = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.X),
        0, 1023, -100, 100)
    y = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.Y),
        0, 1023, -100, 100)
    if x >= -10 and x <= 10:
        x = 0
    if y >= -10 and y <= 10:
        y = 0
    if emergencyStop:
        radio.send_value("stop", 1)
    else:
        radio.send_value("x", x * speed / 100)
        radio.send_value("y", y * speed / 100)
    basic.pause(50)

basic.forever(on_forever)

(二)Q霸小車端 V4

xValue = 0
yValue = 0
lastReceiveTime = -1000
distance = 0
emergencyStop = False

radio.set_group(51)
basic.show_number(51)
cuteBot.motors(0, 0)

def on_received_value(name, value):
    global xValue, yValue, lastReceiveTime, emergencyStop
    lastReceiveTime = control.millis()
    if name == "x":
        xValue = value
    elif name == "y":
        yValue = value
    elif name == "stop":
        emergencyStop = value == 1

radio.on_received_value(on_received_value)

def on_forever():
    global distance
    # 緊急停止 → 斷線停車 → 超音波避障 → 搖桿控制
    if emergencyStop:
        cuteBot.motors(0, 0)
        basic.show_icon(IconNames.NO)
    elif control.millis() - lastReceiveTime > 500:
        cuteBot.motors(0, 0)
        basic.show_icon(IconNames.SAD)
    else:
        distance = cuteBot.ultrasonic(cuteBot.SonarUnit.Centimeters)
        leftSpeed = Math.constrain(yValue + xValue, -100, 100)
        rightSpeed = Math.constrain(yValue - xValue, -100, 100)
        if yValue > 0 and distance > 0 and distance <= 15:
            cuteBot.motors(0, 0)
            basic.show_icon(IconNames.NO)
        elif yValue > 0 and distance > 15 and distance <= 30:
            cuteBot.motors(leftSpeed * 0.4, rightSpeed * 0.4)
            basic.show_icon(IconNames.SMALL_DIAMOND)
        else:
            cuteBot.motors(leftSpeed, rightSpeed)
            basic.clear_screen()
    basic.pause(30)

basic.forever(on_forever)

測試提示:如果實測前進方向是 yValue < 0,請把小車端兩個 yValue > 0 改為 yValue < 0

七、完成後的操作方式

圖3 Q霸小車與 Joystick:bit 擴充搖桿實體外觀
搖桿操作小車動作
向前推前進
向後拉後退
向左推左轉
向右推右轉
回到中央停止(死區保護)
按 A/B/A+B切換慢速/一般/快速模式
搖桿關機或超出距離0.5 秒後自動停車

八、除錯小提醒

  1. 小車沒有反應:先確認兩端廣播群組是否都為 51,以及兩片 micro:bit 是否都有供電。
  2. 前後方向相反:將 y 軸對應範圍由 -100~100 改為 100~-100,或將馬達速度乘以 -1。
  3. 左右方向相反:將 x 軸對應範圍反向,或在小車端交換加、減的運算方向。
  4. 停下仍會緩慢移動:先確認死區已設定為 -10~10;若仍有誤差,可加大為 -15~15。
  5. 小車經常自動停車:確認兩端群組號相同、電池電力充足,並將斷線判斷時間由 500 毫秒調整為 800 毫秒。

九、延伸挑戰

  • 按下搖桿按鍵時,讓小車發出聲音或亮起車燈。
  • 加入超音波避障:前方太近時,即使搖桿要求前進也自動停車。
  • 將小車改造成校園導覽車、農場巡檢車或智慧物流車,設計任務關卡。
教學重點:這個作品不是只讓小車「動起來」,而是把感測輸入、無線傳輸與馬達輸出串成完整的智慧控制系統。學生可從中理解物聯網裝置如何把人的操作,轉換成真實世界的行動。

使用 Joystick:bit 擴充搖桿遙控 Q霸小車 V3

使用 Joystick:bit 擴充搖桿遙控 Q霸小車 V3

本實作使用兩片 micro:bit 進行無線通訊:一片裝在 Joystick:bit2 擴充搖桿,負責讀取搖桿方向;另一片安裝在 Q霸小車(CuteBot) 上,接收訊號後控制左右輪馬達。V3 保留搖桿死區、斷線自動停車與三段速度,再加入超音波安全避障,讓小車接近前方障礙物時能自動降速或停止。

學習目標
了解 micro:bit 廣播通訊、類比搖桿讀值、超音波感測與雙輪差速控制,完成具備安全保護與速度切換功能的 Q霸小車。

一、材料準備

  • Q霸小車套件(CuteBot)× 1
  • Joystick:bit2 擴充搖桿 × 1
  • micro:bit × 2
  • 電池盒與電池 × 2

二、操作前準備

  1. 開啟 Microsoft MakeCode for micro:bit,分別建立「搖桿端」與「小車端」兩個專案。
  2. 在兩個專案中加入 Radio(廣播) 功能;搖桿端另加入 Joystick:bit 擴充功能,小車端加入 CuteBot 擴充功能。
  3. 兩端的廣播群組都設定為 51。群組號碼必須相同,才能互相通訊。
  4. 分別下載程式到兩片 micro:bit,再接上電池並開機。

三、搖桿端編程:死區、三段速度與訊號發送

搖桿的原始讀值介於 0~1023。本程式利用「對應」積木將數值換算為 -100~100,並先判斷搖桿是否落在中央的死區範圍,再依速度模式縮放輸出值,最後以廣播鍵值送出 xy

  • x 軸:控制左右轉向。
  • y 軸:控制前進與後退。
  • 死區 -10~10:數值位於此區間時歸零,避免小車自己緩慢移動。
  • 三段速度:按 A 鍵為慢速 40%、按 B 鍵為一般 70%、同時按 A+B 為快速 100%。
  • 廣播群組 51:作為搖桿與小車共用的無線頻道。
圖1 Joystick:bit 搖桿端積木程式(可依下列 V2 邏輯補強)

搖桿端 V2 積木邏輯

  1. 建立變數:xyspeed
  2. 啟動時設定 speed = 70,廣播群組設為 51。
  3. 按下 A:設定 speed = 40 並顯示 1;按下 B:設定 speed = 70 並顯示 2;按下 A+B:設定 speed = 100 並顯示 3。
  4. 重複無限次:讀取並對應 x、y 軸;若 x 或 y 介於 -10~10,將該值設為 0;最後分別將 x、y 乘以 speed ÷ 100 後廣播。

四、Q霸小車端編程:超音波安全避障、斷線停車與馬達控制

小車端收到廣播鍵值後,依名稱將數值分別存入 xValueyValue,同時記錄最後一次收到訊號的時間。若超過 500 毫秒沒有收到新訊號,便立刻停止兩輪馬達;向前行駛時,超音波感測器會依前方距離自動降速或停車。

左輪速度 = yValue + xValue
右輪速度 = yValue − xValue

當搖桿向前推時,兩輪同向前轉;向右推時,左輪速度增加、右輪速度降低,小車便向右轉。向左、向後的原理相同。斷線停車能避免電池沒電、搖桿關機或超出廣播距離時,小車仍維持原方向行駛。

圖2 Q霸小車端積木程式

小車端 V3 積木邏輯

  1. 建立變數:xValueyValuelastReceiveTime
  2. 啟動時將廣播群組設為 51,並讓兩輪馬達停止。
  3. 當收到廣播鍵值:名稱為 x 時更新 xValue;名稱為 y 時更新 yValue;每次收到訊號都將 lastReceiveTime 設為「運行時間(毫秒)」。
  4. 重複無限次:若「運行時間 − lastReceiveTime」大於 500,設定左右輪速度都為 0;否則讀取超音波距離。
  5. 前方距離為 15~30 cm 時,向前行駛自動降為 40% 速度;距離小於等於 15 cm 時,向前行駛立即停止。

五、MakeCode Python 程式碼

若要使用 Python 模式,請在 MakeCode 先加入 Joystick:bit 與 CuteBot 擴充功能,再分別建立兩個專案並貼上以下程式。

(一)Joystick:bit 搖桿端

x = 0
y = 0
speed = 70

radio.set_group(51)
basic.show_number(2)  # 開機預設為一般速度

def on_button_pressed_a():
    global speed
    speed = 40
    basic.show_number(1)  # 慢速
input.on_button_pressed(Button.A, on_button_pressed_a)

def on_button_pressed_b():
    global speed
    speed = 70
    basic.show_number(2)  # 一般速度
input.on_button_pressed(Button.B, on_button_pressed_b)

def on_button_pressed_ab():
    global speed
    speed = 100
    basic.show_number(3)  # 快速
input.on_button_pressed(Button.AB, on_button_pressed_ab)

def on_forever():
    global x, y

    x = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.X),
        0, 1023, -100, 100)
    y = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.Y),
        0, 1023, -100, 100)

    # 搖桿死區:回到中央時停止
    if x >= -10 and x <= 10:
        x = 0
    if y >= -10 and y <= 10:
        y = 0

    # 依速度模式縮放輸出值
    x = x * speed / 100
    y = y * speed / 100

    radio.send_value("x", x)
    radio.send_value("y", y)
    basic.pause(50)

basic.forever(on_forever)

(二)Q霸小車端:V3 超音波安全避障

xValue = 0
yValue = 0
lastReceiveTime = -1000
distance = 0

radio.set_group(51)
basic.show_number(51)
cuteBot.motors(0, 0)

def on_received_value(name, value):
    global xValue, yValue, lastReceiveTime
    if name == "x":
        xValue = value
    elif name == "y":
        yValue = value
    lastReceiveTime = control.millis()

radio.on_received_value(on_received_value)

def on_forever():
    global distance
    # 優先保護 1:逾 0.5 秒未收到訊號,立即停車
    if control.millis() - lastReceiveTime > 500:
        cuteBot.motors(0, 0)
    else:
        distance = cuteBot.ultrasonic(cuteBot.SonarUnit.Centimeters)
        leftSpeed = Math.constrain(yValue + xValue, -100, 100)
        rightSpeed = Math.constrain(yValue - xValue, -100, 100)

        # 優先保護 2:僅在向前時啟用前方避障
        if yValue > 0 and distance > 0 and distance <= 15:
            cuteBot.motors(0, 0)
            basic.show_icon(IconNames.NO)
        elif yValue > 0 and distance > 15 and distance <= 30:
            cuteBot.motors(leftSpeed * 0.4, rightSpeed * 0.4)
            basic.show_icon(IconNames.SMALL_DIAMOND)
        else:
            cuteBot.motors(leftSpeed, rightSpeed)
            basic.clear_screen()
    basic.pause(30)

basic.forever(on_forever)

測試提示:本程式假設 yValue > 0 是前進。如果實測發現前進方向相反,請把小車端的兩個 yValue > 0 改為 yValue < 0

六、完成後的操作方式

圖3 Q霸小車與 Joystick:bit 擴充搖桿實體外觀
搖桿操作小車動作
向前推前進
向後拉後退
向左推左轉
向右推右轉
回到中央停止(死區保護)
按 A/B/A+B切換慢速/一般/快速模式
搖桿關機或超出距離0.5 秒後自動停車

七、除錯小提醒

  1. 小車沒有反應:先確認兩端廣播群組是否都為 51,以及兩片 micro:bit 是否都有供電。
  2. 前後方向相反:將 y 軸對應範圍由 -100~100 改為 100~-100,或將馬達速度乘以 -1。
  3. 左右方向相反:將 x 軸對應範圍反向,或在小車端交換加、減的運算方向。
  4. 停下仍會緩慢移動:先確認死區已設定為 -10~10;若仍有誤差,可加大為 -15~15。
  5. 小車經常自動停車:確認兩端群組號相同、電池電力充足,並將斷線判斷時間由 500 毫秒調整為 800 毫秒。

八、延伸挑戰

  • 按下搖桿按鍵時,讓小車發出聲音或亮起車燈。
  • 加入超音波避障:前方太近時,即使搖桿要求前進也自動停車。
  • 將小車改造成校園導覽車、農場巡檢車或智慧物流車,設計任務關卡。
教學重點:這個作品不是只讓小車「動起來」,而是把感測輸入、無線傳輸與馬達輸出串成完整的智慧控制系統。學生可從中理解物聯網裝置如何把人的操作,轉換成真實世界的行動。

使用 Joystick:bit 擴充搖桿遙控 Q霸小車 V2

使用 Joystick:bit 擴充搖桿遙控 Q霸小車 V2

本實作使用兩片 micro:bit 進行無線通訊:一片裝在 Joystick:bit2 擴充搖桿,負責讀取搖桿方向;另一片安裝在 Q霸小車(CuteBot) 上,接收訊號後控制左右輪馬達。V2 再加入搖桿死區、斷線自動停車與三段速度,讓小車操作更穩定也更安全。

學習目標
了解 micro:bit 廣播通訊、類比搖桿讀值與雙輪差速控制,完成具備安全保護與速度切換功能的 Q霸小車。

一、材料準備

  • Q霸小車套件(CuteBot)× 1
  • Joystick:bit2 擴充搖桿 × 1
  • micro:bit × 2
  • 電池盒與電池 × 2

二、操作前準備

  1. 開啟 Microsoft MakeCode for micro:bit,分別建立「搖桿端」與「小車端」兩個專案。
  2. 在兩個專案中加入 Radio(廣播) 功能;搖桿端另加入 Joystick:bit 擴充功能,小車端加入 CuteBot 擴充功能。
  3. 兩端的廣播群組都設定為 51。群組號碼必須相同,才能互相通訊。
  4. 分別下載程式到兩片 micro:bit,再接上電池並開機。

三、搖桿端編程:死區、三段速度與訊號發送

搖桿的原始讀值介於 0~1023。本程式利用「對應」積木將數值換算為 -100~100,並先判斷搖桿是否落在中央的死區範圍,再依速度模式縮放輸出值,最後以廣播鍵值送出 xy

  • x 軸:控制左右轉向。
  • y 軸:控制前進與後退。
  • 死區 -10~10:數值位於此區間時歸零,避免小車自己緩慢移動。
  • 三段速度:按 A 鍵為慢速 40%、按 B 鍵為一般 70%、同時按 A+B 為快速 100%。
  • 廣播群組 51:作為搖桿與小車共用的無線頻道。
圖1 Joystick:bit 搖桿端積木程式(可依下列 V2 邏輯補強)

搖桿端 V2 積木邏輯

  1. 建立變數:xyspeed
  2. 啟動時設定 speed = 70,廣播群組設為 51。
  3. 按下 A:設定 speed = 40 並顯示 1;按下 B:設定 speed = 70 並顯示 2;按下 A+B:設定 speed = 100 並顯示 3。
  4. 重複無限次:讀取並對應 x、y 軸;若 x 或 y 介於 -10~10,將該值設為 0;最後分別將 x、y 乘以 speed ÷ 100 後廣播。

四、Q霸小車端編程:斷線自動停車與馬達控制

小車端收到廣播鍵值後,依名稱將數值分別存入 xValueyValue,同時記錄最後一次收到訊號的時間。若超過 500 毫秒沒有收到新訊號,便立刻停止兩輪馬達。

左輪速度 = yValue + xValue
右輪速度 = yValue − xValue

當搖桿向前推時,兩輪同向前轉;向右推時,左輪速度增加、右輪速度降低,小車便向右轉。向左、向後的原理相同。斷線停車能避免電池沒電、搖桿關機或超出廣播距離時,小車仍維持原方向行駛。

圖2 Q霸小車端積木程式

小車端 V2 積木邏輯

  1. 建立變數:xValueyValuelastReceiveTime
  2. 啟動時將廣播群組設為 51,並讓兩輪馬達停止。
  3. 當收到廣播鍵值:名稱為 x 時更新 xValue;名稱為 y 時更新 yValue;每次收到訊號都將 lastReceiveTime 設為「運行時間(毫秒)」。
  4. 重複無限次:若「運行時間 − lastReceiveTime」大於 500,設定左右輪速度都為 0;否則以 yValue + xValueyValue − xValue 控制左右輪。

五、MakeCode Python 程式碼

若要使用 Python 模式,請在 MakeCode 先加入 Joystick:bit 與 CuteBot 擴充功能,再分別建立兩個專案並貼上以下程式。

(一)Joystick:bit 搖桿端

x = 0
y = 0
speed = 70

radio.set_group(51)
basic.show_number(2)  # 開機預設為一般速度

def on_button_pressed_a():
    global speed
    speed = 40
    basic.show_number(1)  # 慢速
input.on_button_pressed(Button.A, on_button_pressed_a)

def on_button_pressed_b():
    global speed
    speed = 70
    basic.show_number(2)  # 一般速度
input.on_button_pressed(Button.B, on_button_pressed_b)

def on_button_pressed_ab():
    global speed
    speed = 100
    basic.show_number(3)  # 快速
input.on_button_pressed(Button.AB, on_button_pressed_ab)

def on_forever():
    global x, y

    x = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.X),
        0, 1023, -100, 100)
    y = Math.map(joystickbit.get_rocker_value(joystickbit.rockerType.Y),
        0, 1023, -100, 100)

    # 搖桿死區:回到中央時停止
    if x >= -10 and x <= 10:
        x = 0
    if y >= -10 and y <= 10:
        y = 0

    # 依速度模式縮放輸出值
    x = x * speed / 100
    y = y * speed / 100

    radio.send_value("x", x)
    radio.send_value("y", y)
    basic.pause(50)

basic.forever(on_forever)

(二)Q霸小車端

xValue = 0
yValue = 0
lastReceiveTime = -1000

radio.set_group(51)
basic.show_number(51)
cuteBot.motors(0, 0)

def on_received_value(name, value):
    global xValue, yValue, lastReceiveTime

    if name == "x":
        xValue = value
    elif name == "y":
        yValue = value

    # 每收到一筆資料就刷新最後接收時間
    lastReceiveTime = control.millis()

radio.on_received_value(on_received_value)

def on_forever():
    # 逾 0.5 秒未收到訊號,立即停車
    if control.millis() - lastReceiveTime > 500:
        cuteBot.motors(0, 0)
    else:
        leftSpeed = Math.constrain(yValue + xValue, -100, 100)
        rightSpeed = Math.constrain(yValue - xValue, -100, 100)
        cuteBot.motors(leftSpeed, rightSpeed)

basic.forever(on_forever)

測試提示:如果前後方向相反,將搖桿端 y 軸的對應範圍由 -100, 100 改為 100, -100;若左右相反,則反轉 x 軸範圍。

六、完成後的操作方式

圖3 Q霸小車與 Joystick:bit 擴充搖桿實體外觀
搖桿操作小車動作
向前推前進
向後拉後退
向左推左轉
向右推右轉
回到中央停止(死區保護)
按 A/B/A+B切換慢速/一般/快速模式
搖桿關機或超出距離0.5 秒後自動停車

七、除錯小提醒

  1. 小車沒有反應:先確認兩端廣播群組是否都為 51,以及兩片 micro:bit 是否都有供電。
  2. 前後方向相反:將 y 軸對應範圍由 -100~100 改為 100~-100,或將馬達速度乘以 -1。
  3. 左右方向相反:將 x 軸對應範圍反向,或在小車端交換加、減的運算方向。
  4. 停下仍會緩慢移動:先確認死區已設定為 -10~10;若仍有誤差,可加大為 -15~15。
  5. 小車經常自動停車:確認兩端群組號相同、電池電力充足,並將斷線判斷時間由 500 毫秒調整為 800 毫秒。

八、延伸挑戰

  • 按下搖桿按鍵時,讓小車發出聲音或亮起車燈。
  • 加入超音波避障:前方太近時,即使搖桿要求前進也自動停車。
  • 將小車改造成校園導覽車、農場巡檢車或智慧物流車,設計任務關卡。
教學重點:這個作品不是只讓小車「動起來」,而是把感測輸入、無線傳輸與馬達輸出串成完整的智慧控制系統。學生可從中理解物聯網裝置如何把人的操作,轉換成真實世界的行動。

使用 Joystick:bit 擴充搖桿遙控 Q霸小車

使用 Joystick:bit 擴充搖桿遙控 Q霸小車

本實作使用兩片 micro:bit 進行無線通訊:一片裝在 Joystick:bit2 擴充搖桿,負責讀取搖桿方向;另一片安裝在 Q霸小車(CuteBot) 上,接收訊號後控制左右輪馬達。透過 x、y 兩軸數值混合,即可讓小車前進、後退與左右轉彎。

學習目標
了解 micro:bit 廣播通訊、類比搖桿讀值與雙輪差速控制,完成一台可用手持搖桿遙控的 Q霸小車。

一、材料準備

  • Q霸小車套件(CuteBot)× 1
  • Joystick:bit2 擴充搖桿 × 1
  • micro:bit × 2
  • 電池盒與電池 × 2

二、操作前準備

  1. 開啟 Microsoft MakeCode for micro:bit,分別建立「搖桿端」與「小車端」兩個專案。
  2. 在兩個專案中加入 Radio(廣播) 功能;搖桿端另加入 Joystick:bit 擴充功能,小車端加入 CuteBot 擴充功能。
  3. 兩端的廣播群組都設定為 51。群組號碼必須相同,才能互相通訊。
  4. 分別下載程式到兩片 micro:bit,再接上電池並開機。

三、搖桿端編程:讀取方向並發送 x、y 值

搖桿的原始讀值介於 0~1023。本程式利用「對應」積木將數值換算為 -100~100,讓它能直接作為馬達速度使用;接著以廣播鍵值分別送出 xy

  • x 軸:控制左右轉向。
  • y 軸:控制前進與後退。
  • 廣播群組 51:作為搖桿與小車共用的無線頻道。

圖1 Joystick:bit 搖桿端積木程式

四、Q霸小車端編程:接收數值並控制馬達

小車端收到廣播鍵值後,依名稱將數值分別存入 xValueyValue。在「重複無限次」中,利用下列差速控制公式設定馬達速度:

左輪速度 = yValue + xValue
右輪速度 = yValue − xValue

當搖桿向前推時,兩輪同向前轉;向右推時,左輪速度增加、右輪速度降低,小車便向右轉。向左、向後的原理相同。

圖2 Q霸小車端積木程式

五、完成後的操作方式



圖3 Q霸小車與 Joystick:bit 擴充搖桿實體外觀
搖桿操作小車動作
向前推前進
向後拉後退
向左推左轉
向右推右轉
回到中央停止或接近停止

六、除錯小提醒

  1. 小車沒有反應:先確認兩端廣播群組是否都為 51,以及兩片 micro:bit 是否都有供電。
  2. 前後方向相反:將 y 軸對應範圍由 -100~100 改為 100~-100,或將馬達速度乘以 -1。
  3. 左右方向相反:將 x 軸對應範圍反向,或在小車端交換加、減的運算方向。
  4. 停下仍會緩慢移動:這是搖桿中心值的誤差。可設定小範圍死區,例如 x、y 介於 -10~10 時改為 0。

七、延伸挑戰

  • 按下搖桿按鍵時,讓小車發出聲音或亮起車燈。
  • 加入超音波避障:前方太近時,即使搖桿要求前進也自動停車。
  • 將小車改造成校園導覽車、農場巡檢車或智慧物流車,設計任務關卡。
教學重點:這個作品不是只讓小車「動起來」,而是把感測輸入、無線傳輸與馬達輸出串成完整的智慧控制系統。學生可從中理解物聯網裝置如何把人的操作,轉換成真實世界的行動。