🤖 Drive a Robot Car with Python · ขับหุ่นยนต์ด้วย Python · 用 Python 开机器人小车

Your code moves a real car. Lights, music and wheels, over Bluetooth. · โค้ดของคุณทำให้รถจริงเคลื่อนที่ ไฟ เสียงเพลง และล้อ ผ่านบลูทูธ · 你的代码让真的小车动起来。灯、音乐和轮子,都走蓝牙。

I am using: ฉันใช้: 我用的是:
$ python robot.py Looking for the robot... Found it: BBC micro:bit [pepat] Lights on. Playing a song. Driving forward. Now stopping. Done.

This is your program. It is about twenty lines. A real car moves on the floor. นี่คือโปรแกรมของคุณ ยาวประมาณยี่สิบบรรทัด รถจริงวิ่งบนพื้น 这就是你的程序。大概二十行。真的小车在地上跑。

🇬🇧 English

Most Python programs only change the screen. This one changes the room.

You send a very short message. The robot hears it. Then the wheels turn.

The message is tiny. A# means "go forward". 0# means "stop".

You do not need to know Bluetooth. One library does that work for you.

🇹🇭 ไทย

โปรแกรม Python ส่วนใหญ่เปลี่ยนแค่หน้าจอ แต่อันนี้เปลี่ยนทั้งห้อง

คุณส่งข้อความสั้นมาก หุ่นยนต์ได้ยิน แล้วล้อก็หมุน

ข้อความเล็กมาก A# แปลว่า "เดินหน้า" 0# แปลว่า "หยุด"

คุณไม่ต้องรู้เรื่องบลูทูธ มีไลบรารีทำงานนั้นให้คุณ

🇨🇳 中文

大多数 Python 程序只改变屏幕。这个程序改变整个房间。

你发一条很短的消息。机器人听到了。然后轮子就转。

消息很小。A# 是"往前走"。0# 是"停"。

你不用懂蓝牙。有一个库帮你做这件事。

🚗 Meet the robot · รู้จักหุ่นยนต์ · 认识这台机器人

front micro:bit motor motor eyes lights ball wheel

The car from above. This is a drawing, not a photo. รถมองจากด้านบน นี่คือภาพวาด ไม่ใช่ภาพถ่าย 从上面看这台小车。这是一张画,不是照片。

🇬🇧 English

The car has two wheels with motors. It has one small ball wheel at the back.

The brain is a micro:bit. It is a small computer for schools.

The micro:bit has a radio inside. That radio is Bluetooth.

Your laptop talks to that radio. So you do not need a cable.

🇹🇭 ไทย

รถมีล้อสองล้อที่มีมอเตอร์ ด้านหลังมีลูกกลมเล็ก ๆ หนึ่งลูก

สมองของรถคือ micro:bit เป็นคอมพิวเตอร์เล็ก ๆ สำหรับโรงเรียน

ใน micro:bit มีวิทยุอยู่ข้างใน วิทยุนั้นคือบลูทูธ

แล็ปท็อปของคุณคุยกับวิทยุนั้น คุณจึงไม่ต้องใช้สาย

🇨🇳 中文

小车有两个带马达的轮子。后面还有一个小圆球轮。

它的大脑是 micro:bit。这是给学校用的小电脑。

micro:bit 里面有一个无线电。那就是蓝牙。

你的电脑跟那个无线电说话。所以不用接线。

⚙️Two motors Go, turn and spin
มอเตอร์สองตัว · 两个马达
🌈Colour lights Red, green, blue and more
ไฟสี · 彩色灯
🎵A buzzer It plays notes
ลำโพงเล็ก · 蜂鸣器
📏Eyes for distance It sees walls in front
ตาวัดระยะ · 测距的眼睛
🛣️Line sensors It follows a black line
เซ็นเซอร์เส้น · 循线传感器
📡Bluetooth Your Python talks to it
บลูทูธ · 蓝牙

🧰 What you need first · สิ่งที่ต้องมีก่อน · 先要准备什么

🇬🇧 English

You need four things. Check them now.

1. Python on your computer. The Start Python page shows you how.

2. A computer with Bluetooth. Most laptops have it.

3. The car, with good batteries.

4. The Bluetooth program on the car. Your teacher puts it there once.

🇹🇭 ไทย

คุณต้องมีสี่อย่าง ตรวจดูตอนนี้เลย

1. Python บนเครื่องของคุณ หน้า เริ่มเขียน Python บอกวิธีไว้แล้ว

2. คอมพิวเตอร์ที่มีบลูทูธ แล็ปท็อปส่วนใหญ่มี

3. ตัวรถ พร้อมแบตเตอรี่ที่ยังดีอยู่

4. โปรแกรมบลูทูธบนตัวรถ ครูใส่ให้ครั้งเดียว

🇨🇳 中文

你需要四样东西。现在就检查一下。

1. 电脑上要有 Python。开始学 Python 那一页教你怎么装。

2. 一台有蓝牙的电脑。大部分笔记本都有。

3. 小车,还要有电的电池。

4. 车上要有蓝牙程序。老师装一次就行。

MAC Say yes to Bluetooth the first time. If you miss it: open System Settings. Go to Privacy and Security. Open Bluetooth. Turn on your Terminal app. ครั้งแรกให้กดอนุญาตบลูทูธ ถ้าพลาด ให้เปิด System Settings ไปที่ Privacy and Security เปิด Bluetooth แล้วเปิดสวิตช์ให้แอป Terminal 第一次要允许蓝牙。如果错过了:打开系统设置。进入隐私与安全性。点蓝牙。把你的终端 App 打开。
STOP Close the phone app first. The car talks to one thing at a time. ปิดแอปบนมือถือก่อน รถคุยกับอุปกรณ์ได้ทีละเครื่องเท่านั้น 先关掉手机 App。小车一次只能跟一个设备说话。

1 Get the library · ติดตั้งไลบรารี · 装一个库

🇬🇧 English

A library is code that other people wrote. You can use it for free.

We use one library. Its name is bleak. It speaks Bluetooth for you.

Type this line in your terminal. Then press Enter.

🇹🇭 ไทย

ไลบรารีคือโค้ดที่คนอื่นเขียนไว้ คุณใช้ได้ฟรี

เราใช้ไลบรารีตัวเดียว ชื่อว่า bleak มันพูดบลูทูธแทนคุณ

พิมพ์บรรทัดนี้ในเทอร์มินัล แล้วกด Enter

🇨🇳 中文

库就是别人写好的代码。你可以免费用。

我们只用一个库,叫 bleak。它替你说蓝牙。

在终端里敲这一行,然后按 Enter

pythonpython3 -m pip install bleak
CHECK You should see "Successfully installed bleak". Then you are ready. คุณควรเห็นคำว่า "Successfully installed bleak" แล้วคุณก็พร้อม 你应该看到 "Successfully installed bleak"。那就可以了。

2 Find the robot · หาหุ่นยนต์ให้เจอ · 找到机器人

🇬🇧 English

First we look for the car. The car shouts its name every second.

Its name starts with BBC micro:bit. We look for that name.

Make a new file. Call it find.py. Put this code in it.

🇹🇭 ไทย

ก่อนอื่นเราหารถก่อน รถจะประกาศชื่อตัวเองทุกวินาที

ชื่อของมันขึ้นต้นด้วย BBC micro:bit เราหาชื่อนั้น

สร้างไฟล์ใหม่ ตั้งชื่อว่า find.py แล้วใส่โค้ดนี้ลงไป

🇨🇳 中文

先找车。小车每秒都在喊自己的名字。

它的名字以 BBC micro:bit 开头。我们就找这个名字。

新建一个文件,叫 find.py,把这段代码放进去。

find.py
import asyncio
from bleak import BleakScanner

async def main():
    print("Looking for the robot...")
    devices = await BleakScanner.discover(timeout=10)
    for d in devices:
        if d.name and "micro:bit" in d.name:
            print("Found it:", d.name)

asyncio.run(main())
pythonpython3 find.py
$ python find.py Looking for the robot... Found it: BBC micro:bit [pepat]

🇬🇧 English

The word async is new. It means "this can wait".

Bluetooth is slow. The program must wait for it.

await means "wait here, then go on".

Copy this shape for now. You will understand it later.

🇹🇭 ไทย

คำว่า async เป็นคำใหม่ แปลว่า "อันนี้รอได้"

บลูทูธช้า โปรแกรมต้องรอมัน

await แปลว่า "รอตรงนี้ แล้วค่อยไปต่อ"

ตอนนี้ลอกรูปแบบนี้ไปก่อน เดี๋ยวคุณจะเข้าใจเอง

🇨🇳 中文

async 是个新词。意思是"这里可以等"。

蓝牙很慢。程序必须等它。

await 的意思是"在这儿等一下,然后继续"。

先照着抄。以后你会明白的。

3 Turn on a light · เปิดไฟหนึ่งดวง · 点亮一盏灯

🇬🇧 English

Now we send our first command. We start with a light, not a wheel.

A light is safe. The car cannot run away.

G# makes the light red. M# turns it off.

The long number is an address. It is the robot's letter box.

🇹🇭 ไทย

ตอนนี้เราส่งคำสั่งแรก เริ่มที่ไฟก่อน ยังไม่ใช่ล้อ

ไฟปลอดภัย รถวิ่งหนีไม่ได้

G# ทำให้ไฟเป็นสีแดง M# ปิดไฟ

ตัวเลขยาว ๆ คือที่อยู่ มันคือตู้จดหมายของหุ่นยนต์

🇨🇳 中文

现在发第一条命令。先点灯,不动轮子。

灯很安全。小车跑不掉。

G# 把灯变红。M# 把灯关掉。

那串长长的数字是地址。就像机器人的信箱。

robot.py
import asyncio
from bleak import BleakScanner, BleakClient

BOX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"

async def main():
    robot = await BleakScanner.find_device_by_filter(
        lambda d, a: d.name and "micro:bit" in d.name)
    print("Found it:", robot.name)

    async with BleakClient(robot) as car:
        await car.write_gatt_char(BOX, b"G#")   # red
        await asyncio.sleep(2)
        await car.write_gatt_char(BOX, b"M#")   # off

asyncio.run(main())
FACE Watch the robot screen. It smiles when Python connects. It looks sad when Python leaves. ดูหน้าจอของหุ่นยนต์ มันยิ้มเมื่อ Python เชื่อมต่อ และหน้าเศร้าเมื่อ Python ออกไป 看机器人的屏幕。Python 连上时它会笑。Python 离开时它就变难过。
RUN IT The light turns red for two seconds. Then it goes off. ไฟจะเป็นสีแดงสองวินาที แล้วก็ดับ 灯会红两秒,然后灭掉。

🇬🇧 English

Look at b"G#". The letter b is important.

It means "send bytes, not letters". Radios send bytes.

async with opens the door and closes it again.

When the block ends, Python hangs up for you.

🇹🇭 ไทย

ดูที่ b"G#" ตัวอักษร b สำคัญมาก

มันแปลว่า "ส่งเป็นไบต์ ไม่ใช่ตัวอักษร" วิทยุส่งไบต์

async with เปิดประตูแล้วปิดให้เอง

พอจบบล็อก Python จะวางสายให้คุณ

🇨🇳 中文

b"G#"。前面那个 b 很重要。

它的意思是"发字节,不是字母"。无线电发的是字节。

async with 会开门,也会自己关门。

这段代码结束时,Python 帮你挂断。

4 Make one helper · สร้างตัวช่วยหนึ่งตัว · 写一个小帮手

🇬🇧 English

write_gatt_char is a long name. We must type it many times.

So we make a small function. We call it say.

Now we write await say(car, "G"). That is much shorter.

The robot needs a small rest. So we sleep for a moment.

🇹🇭 ไทย

write_gatt_char เป็นชื่อที่ยาว เราต้องพิมพ์หลายครั้ง

เราจึงสร้างฟังก์ชันเล็ก ๆ ตั้งชื่อว่า say

ตอนนี้เราเขียน await say(car, "G") สั้นกว่ากันเยอะ

หุ่นยนต์ต้องพักนิดหนึ่ง เราจึงหยุดรอสักครู่

🇨🇳 中文

write_gatt_char 名字很长。我们要打很多次。

所以写一个小函数,叫 say

现在只要写 await say(car, "G")。短多了。

机器人需要一点点休息。所以我们等一下下。

Grey lines are already in your file. Add the white lines. บรรทัดสีเทาคือของที่มีอยู่แล้ว ให้เพิ่มบรรทัดสีขาว 灰色的行你已经有了。加上白色的行。

BOX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"


async def say(car, command):
    await car.write_gatt_char(BOX, command.encode() + b"#")
    await asyncio.sleep(0.05)


async def main():
    ...

🇬🇧 English

Why do we sleep for 0.05 seconds? The robot is small.

It reads one command at a time. Fast messages get lost.

This tiny rest fixes that. It is a real bug, and this is the fix.

🇹🇭 ไทย

ทำไมต้องหยุด 0.05 วินาที เพราะหุ่นยนต์ตัวเล็ก

มันอ่านคำสั่งได้ทีละคำสั่ง ถ้าส่งเร็วไป ข้อความจะหาย

การพักสั้น ๆ นี้แก้ปัญหาได้ นี่คือบั๊กจริง และนี่คือวิธีแก้

🇨🇳 中文

为什么要等 0.05 秒?因为机器人很小。

它一次只读一条命令。发太快,消息就丢了。

这一点点休息就能解决。这是真的 bug,这就是解法。

5 Make it move · ทำให้มันเคลื่อนที่ · 让它动起来

FLOOR Put the car on the floor now. It can fall off a table. วางรถบนพื้นตอนนี้เลย มันตกจากโต๊ะได้ 现在把小车放到地上。它会从桌上掉下去。

🇬🇧 English

Now the wheels. A is forward. 0 is stop.

Always write the stop line. A car with no stop keeps going.

It goes for one second. Then it stops.

🇹🇭 ไทย

ทีนี้มาที่ล้อ A คือเดินหน้า 0 คือหยุด

เขียนบรรทัดหยุดเสมอ รถที่ไม่มีคำสั่งหยุดจะวิ่งต่อไป

มันวิ่งหนึ่งวินาที แล้วก็หยุด

🇨🇳 中文

现在动轮子。A 是前进,0 是停。

一定要写停的那一行。没有停,车就一直跑。

它走一秒,然后停下来。

    async with BleakClient(robot) as car:
        await car.write_gatt_char(BOX, b"G#")   # red
        await asyncio.sleep(2)
        await car.write_gatt_char(BOX, b"M#")   # off

        await say(car, "A")          # forward
        await asyncio.sleep(1)
        await say(car, "0")          # stop

🇬🇧 English

Now try a circle. Send forward, then turn, again and again.

C turns left. Only one wheel moves.

So the car turns on the spot. The forward step makes it travel.

Change the two numbers. The circle gets bigger or smaller.

🇹🇭 ไทย

ลองวิ่งเป็นวงกลมดู ส่งเดินหน้า แล้วเลี้ยว สลับกันไปเรื่อย ๆ

C คือเลี้ยวซ้าย ล้อหมุนแค่ข้างเดียว

รถจึงหมุนอยู่กับที่ ส่วนก้าวเดินหน้าทำให้มันเคลื่อนที่ไป

ลองเปลี่ยนตัวเลขสองตัว วงกลมจะใหญ่ขึ้นหรือเล็กลง

🇨🇳 中文

试试转圈。发前进,再发转弯,一直重复。

C 是左转。只有一个轮子在动。

所以车在原地打转。前进那一步让它走出去。

改那两个数字,圈就变大或变小。

    async with BleakClient(robot) as car:
        for i in range(8):
            await say(car, "A")      # forward
            await asyncio.sleep(0.25)
            await say(car, "C")      # turn left
            await asyncio.sleep(0.40)
        await say(car, "0")          # stop

6 Play a song with lights · เล่นเพลงพร้อมไฟ · 放歌加灯光

🇬🇧 English

The car has a buzzer. The numbers 1 to 8 are notes.

1 is do. 2 is re. 3 is mi. And so on.

This song is Twinkle Twinkle Little Star.

Each note also picks a colour. So the lights follow the music.

🇹🇭 ไทย

รถมีลำโพงเล็ก ๆ ตัวเลข 1 ถึง 8 คือโน้ตดนตรี

1 คือ โด 2 คือ เร 3 คือ มี ไล่ไปเรื่อย ๆ

เพลงนี้คือเพลง Twinkle Twinkle Little Star

โน้ตแต่ละตัวเลือกสีด้วย ไฟจึงวิ่งตามเพลง

🇨🇳 中文

车上有蜂鸣器。数字 18 是音符。

1 是 do,2 是 re,3 是 mi,往下类推。

这首歌是《小星星》。

每个音符也挑一个颜色。所以灯跟着音乐走。

BOX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
SONG = "1155665443322 1"
COLOUR = {"1": "G", "2": "J", "3": "H", "4": "K",
          "5": "I", "6": "L", "7": "G", "8": "J"}


async def say(car, command):
    ...
    async with BleakClient(robot) as car:
        for note in SONG:
            if note == " ":
                await asyncio.sleep(0.4)
                continue
            await say(car, note)              # the sound
            await say(car, COLOUR[note])      # the light
            await asyncio.sleep(0.3)
            await say(car, "M")               # light off
            await asyncio.sleep(0.1)

🇬🇧 English

A string is a list of letters. So we can walk through the song.

for note in SONG takes one note each time.

A space means a rest. The music stops for a moment.

We turn the light off after each note. That makes it flash.

🇹🇭 ไทย

สตริงคือรายการของตัวอักษร เราจึงเดินไล่ไปในเพลงได้

for note in SONG หยิบโน้ตทีละตัว

ช่องว่างคือการพัก ดนตรีจะหยุดครู่หนึ่ง

เราปิดไฟหลังโน้ตแต่ละตัว ไฟจึงกะพริบ

🇨🇳 中文

字符串就是一串字母。所以我们能一个一个走过这首歌。

for note in SONG 每次取一个音符。

空格表示休止。音乐停一下。

每个音符之后我们把灯关掉。灯就会闪。

7 The whole program · โปรแกรมทั้งหมด · 完整的程序

🇬🇧 English

Here is everything in one file. Read it from the top.

try and finally are new. They keep you safe.

finally always runs. So the car always stops.

If your code breaks, the car still stops. That matters a lot.

🇹🇭 ไทย

นี่คือทั้งหมดในไฟล์เดียว อ่านจากบนลงล่าง

try กับ finally เป็นของใหม่ มันทำให้คุณปลอดภัย

finally ทำงานเสมอ รถจึงหยุดเสมอ

ถ้าโค้ดพัง รถก็ยังหยุดอยู่ดี เรื่องนี้สำคัญมาก

🇨🇳 中文

这里是一个文件里的全部内容。从头往下读。

tryfinally 是新的。它们保护你。

finally 一定会运行。所以车一定会停。

就算代码出错,车还是会停。这件事很重要。

robot.py
import asyncio
from bleak import BleakScanner, BleakClient

BOX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
SONG = "1155665443322 1"
COLOUR = {"1": "G", "2": "J", "3": "H", "4": "K",
          "5": "I", "6": "L", "7": "G", "8": "J"}

async def say(car, command):
    await car.write_gatt_char(BOX, command.encode() + b"#")
    await asyncio.sleep(0.05)

async def main():
    print("Looking for the robot...")
    robot = await BleakScanner.find_device_by_filter(
        lambda d, a: d.name and "micro:bit" in d.name)
    if robot is None:
        print("No robot. Is it on? Is the phone app closed?")
        return
    print("Found it:", robot.name)

    async with BleakClient(robot) as car:
        try:
            print("Playing a song.")
            for note in SONG:
                if note == " ":
                    await asyncio.sleep(0.4)
                    continue
                await say(car, note)
                await say(car, COLOUR[note])
                await asyncio.sleep(0.3)
                await say(car, "M")
                await asyncio.sleep(0.1)

            print("Driving in a circle.")
            for i in range(8):
                await say(car, "A")          # forward
                await asyncio.sleep(0.25)
                await say(car, "C")          # turn left
                await asyncio.sleep(0.40)
        finally:
            await say(car, "0")      # stop, always
            await say(car, "M")      # lights off
    print("Done.")

asyncio.run(main())
pythonpython3 robot.py

8 Draw a heart · วาดรูปหัวใจ · 画一颗心

🇬🇧 English

The micro:bit has 25 small lights. They sit in a 5 by 5 grid.

You can draw a picture on them. Let us draw a heart.

Each row is five places. # is a light that is on. . is off.

This part needs no robot. It draws in your terminal.

🇹🇭 ไทย

micro:bit มีไฟดวงเล็ก 25 ดวง เรียงเป็นตาราง 5 คูณ 5

คุณวาดรูปบนไฟพวกนี้ได้ มาวาดรูปหัวใจกัน

แต่ละแถวมีห้าช่อง # คือไฟติด . คือไฟดับ

ส่วนนี้ไม่ต้องใช้หุ่นยนต์ มันวาดในเทอร์มินัลของคุณ

🇨🇳 中文

micro:bit 有 25 个小灯。排成 5 乘 5 的格子。

你可以在上面画图。我们来画一颗心。

每一行有五个位置。# 是灯亮,. 是灯灭。

这部分不用机器人。它画在你的终端里。

heart.py
from faces import icon, render

heart = icon(".#.#.",
             "#####",
             "#####",
             ".###.",
             "..#..")

render(heart, "heart")
pythonpython3 heart.py
$ pythonpython3 heart.py heart .[] .[] . [][][][][] [][][][][] .[][][] . . .[] . . numbers: 10 31 31 14 4

🇬🇧 English

Your picture is five numbers. One number for each row.

The row ##### is five lights on. That number is 31.

Now change the dots. Make your own picture.

faces.py has more. It knows faces, A to Z, and 0 to 9.

🇹🇭 ไทย

รูปของคุณคือตัวเลขห้าตัว หนึ่งตัวต่อหนึ่งแถว

แถว ##### คือไฟติดห้าดวง ตัวเลขนั้นคือ 31

ลองเปลี่ยนจุดดู แล้ววาดรูปของคุณเอง

faces.py มีมากกว่านี้ มันรู้จักหน้ายิ้ม A ถึง Z และ 0 ถึง 9

🇨🇳 中文

你的图就是五个数字。一行一个数字。

##### 这一行是五个灯全亮。那个数字是 31。

现在改一改那些点。画你自己的图。

faces.py 里还有更多。它认识表情、A 到 Z、还有 0 到 9。

pythonpython3 faces.py HELLO
NOTE This draws on your screen, not on the car. You do not need the robot for this step. อันนี้วาดบนจอของคุณ ไม่ใช่บนรถ ขั้นนี้ไม่ต้องใช้หุ่นยนต์ 这是画在你的屏幕上,不是画在车上。这一步不用机器人。

9 Follow a black line · วิ่งตามเส้นสีดำ · 沿着黑线走

micro:bit sees black sees white the black line on your map

🇬🇧 English

The car has two small eyes under the front. They look down.

Black sends back little light. White sends back a lot.

So the car knows which side is on the line.

One command starts it. The car then drives by itself.

🇹🇭 ไทย

ใต้หน้ารถมีตาเล็ก ๆ สองดวง มันมองลงพื้น

สีดำสะท้อนแสงกลับมาน้อย สีขาวสะท้อนกลับมามาก

รถจึงรู้ว่าข้างไหนอยู่บนเส้น

คำสั่งเดียวก็เริ่มได้ แล้วรถจะวิ่งเอง

🇨🇳 中文

车头下面有两只小眼睛。它们往下看。

黑色反射回来的光少。白色反射回来的光多。

所以车知道哪一边在线上。

一条命令就能开始。然后车自己走。

MAP Put the car on the black line first. Use the map that came in the box. วางรถบนเส้นสีดำก่อน ใช้แผ่นแผนที่ที่มากับกล่อง 先把车放在黑线上。用盒子里那张地图。
    async with BleakClient(robot) as car:
        await say(car, "S")          # line mode on
        await asyncio.sleep(15)
        await say(car, "0")          # stop
pythonpython3 track.py

📁 Get the scripts · ดาวน์โหลดสคริปต์ · 下载脚本

🇬🇧 English

Here is every step as a whole file. You can read it or save it.

Click a card to save the file. Click a grey bar to read it here.

Put the file in one folder. Then run it with pythonpython3.

Start with find.py. Then light.py. Those two are safe.

🇹🇭 ไทย

นี่คือทุกขั้นตอนในรูปไฟล์เต็ม คุณอ่านหรือเซฟไว้ก็ได้

กดที่การ์ดเพื่อเซฟไฟล์ กดแถบสีเทาเพื่ออ่านตรงนี้

วางไฟล์ไว้ในโฟลเดอร์เดียว แล้วรันด้วย python

เริ่มที่ find.py แล้วต่อด้วย light.py สองอันนี้ปลอดภัย

🇨🇳 中文

这里是每一步的完整文件。你可以读,也可以存下来。

点卡片就能存文件。点灰色条就能在这里读。

把文件放在一个文件夹里。然后用 python 运行。

先跑 find.py,再跑 light.py。这两个是安全的。

🔍 find.py  Safe. It does not move.

Download find.py

"""Step 2 - Find the robot car.

This script only looks. It does not move the car.

Run it:
    Windows:  python find.py
    Mac:      python3 find.py

Install bleak first:
    Windows:  python -m pip install bleak
    Mac:      python3 -m pip install bleak

On a Mac, say yes when it asks for Bluetooth.
If you miss it: System Settings > Privacy and Security >
Bluetooth > turn on your Terminal app.
"""
import asyncio

from bleak import BleakScanner


async def main():
    print("Looking for the robot...")
    devices = await BleakScanner.discover(timeout=10)

    found = False
    for d in devices:
        if d.name and "micro:bit" in d.name:
            print("Found it:", d.name)
            found = True

    if not found:
        print("No robot.")
        print("Is the car on?")
        print("Is the phone app closed?")
        print("Are you near the car?")


asyncio.run(main())
🌈 light.py  Safe. It does not move.

Download light.py

"""Step 3 - Turn the lights on and off.

This script is safe. The car does not move.
Start here. If this works, your Bluetooth works.

Run it:
    Windows:  python light.py
    Mac:      python3 light.py

Install bleak first:
    Windows:  python -m pip install bleak
    Mac:      python3 -m pip install bleak

On a Mac, say yes when it asks for Bluetooth.
If you miss it: System Settings > Privacy and Security >
Bluetooth > turn on your Terminal app.
"""
import asyncio

from bleak import BleakClient, BleakScanner

# The robot's letter box. Messages go here.
BOX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"


async def say(car, command):
    """Send one command to the robot."""
    await car.write_gatt_char(BOX, command.encode() + b"#", response=False)
    await asyncio.sleep(0.05)


async def find_robot():
    """Look for the car and give it back."""
    return await BleakScanner.find_device_by_filter(
        lambda d, a: d.name and "micro:bit" in d.name)


async def main():
    print("Looking for the robot...")
    robot = await find_robot()
    if robot is None:
        print("No robot. Is it on? Is the phone app closed?")
        return
    print("Found it:", robot.name)

    async with BleakClient(robot) as car:
        for name, code in [("red", "G"), ("green", "H"), ("blue", "I")]:
            print("Light is", name)
            await say(car, code)
            await asyncio.sleep(1)
        await say(car, "M")
        print("Lights off.")

    print("Done.")


asyncio.run(main())
🎵 song.py  Safe. It does not move.

Download song.py

"""Step 6 - Play a song with flashing lights.

This script is safe. The car does not move.

The numbers are notes.
1=do 2=re 3=mi 4=fa 5=sol 6=la 7=si 8=high do.
A space is a rest.

Run it:
    Windows:  python song.py
    Mac:      python3 song.py

Install bleak first:
    Windows:  python -m pip install bleak
    Mac:      python3 -m pip install bleak

On a Mac, say yes when it asks for Bluetooth.
If you miss it: System Settings > Privacy and Security >
Bluetooth > turn on your Terminal app.
"""
import asyncio

from bleak import BleakClient, BleakScanner

BOX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"

# Twinkle Twinkle Little Star. Change this and make your own song.
SONG = "1155665443322 1"

# Each note gets a colour. So the lights follow the music.
COLOUR = {"1": "G", "2": "J", "3": "H", "4": "K",
          "5": "I", "6": "L", "7": "G", "8": "J"}

BEAT = 0.4          # how long one note lasts
ON = 0.7            # how much of the beat the light stays on


async def say(car, command):
    """Send one command to the robot."""
    await car.write_gatt_char(BOX, command.encode() + b"#", response=False)
    await asyncio.sleep(0.05)


async def find_robot():
    return await BleakScanner.find_device_by_filter(
        lambda d, a: d.name and "micro:bit" in d.name)


async def play(car, song):
    for note in song:
        if note == " ":
            await asyncio.sleep(BEAT)
            continue
        await say(car, note)              # the sound
        await say(car, COLOUR[note])      # the light
        await asyncio.sleep(BEAT * ON)
        await say(car, "M")               # light off
        await asyncio.sleep(BEAT * (1 - ON))


async def main():
    print("Looking for the robot...")
    robot = await find_robot()
    if robot is None:
        print("No robot. Is it on? Is the phone app closed?")
        return
    print("Found it:", robot.name)

    async with BleakClient(robot) as car:
        print("Playing a song.")
        try:
            await play(car, SONG)
        finally:
            await say(car, "M")
            print("Lights off.")

    print("Done.")


asyncio.run(main())
🚗 move.py  It MOVES. Floor only.

Download move.py

"""Step 5 - Drive the car with the keyboard.

WARNING: the car MOVES. Put it on the floor first.
It can fall off a table.

Type keys, then press Enter:
    w = forward     s = back
    a = turn left   d = turn right
    q = spin left   e = spin right
    x = quit

You can type more than one key. "wwd" goes forward twice, then turns.
Every key moves the car for a short time. Then the car stops by itself.
So the car can never run away from you.

Run it:
    Windows:  python move.py
    Mac:      python3 move.py

Install bleak first:
    Windows:  python -m pip install bleak
    Mac:      python3 -m pip install bleak

On a Mac, say yes when it asks for Bluetooth.
If you miss it: System Settings > Privacy and Security >
Bluetooth > turn on your Terminal app.
"""
import asyncio

from bleak import BleakClient, BleakScanner

BOX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"

# One key, one command.
KEYS = {
    "w": "A",   # forward
    "s": "B",   # back
    "a": "C",   # turn left
    "d": "D",   # turn right
    "q": "E",   # spin left
    "e": "F",   # spin right
}

# How long one key moves the car.
# Make it bigger for longer steps.
STEP = 0.5


async def say(car, command):
    """Send one command to the robot."""
    await car.write_gatt_char(BOX, command.encode() + b"#", response=False)
    await asyncio.sleep(0.05)


async def find_robot():
    return await BleakScanner.find_device_by_filter(
        lambda d, a: d.name and "micro:bit" in d.name)


async def drive(car):
    print()
    print("w = forward     s = back")
    print("a = turn left   d = turn right")
    print("q = spin left   e = spin right")
    print("x = quit")
    print()

    while True:
        # Wait for typing. to_thread keeps Bluetooth alive while we wait.
        line = await asyncio.to_thread(input, "> ")
        line = line.strip().lower()

        if line == "x":
            return

        for key in line:
            if key not in KEYS:
                print("  I do not know", key)
                continue
            await say(car, KEYS[key])       # go
            await asyncio.sleep(STEP)
            await say(car, "0")             # stop again


async def main():
    print("Looking for the robot...")
    robot = await find_robot()
    if robot is None:
        print("No robot. Is it on? Is the phone app closed?")
        return
    print("Found it:", robot.name)

    async with BleakClient(robot) as car:
        print("Put the car on the floor.")
        try:
            await drive(car)
        finally:
            # This always runs. So the car always stops.
            for i in range(3):
                await say(car, "0")
                await asyncio.sleep(0.15)
            print("Stopped.")

    print("Done.")


asyncio.run(main())
🤖 robot.py  It MOVES. Floor only.

Download robot.py

"""Step 7 - The whole program.

It finds the car. It plays a song with lights.
Then it drives a circle. Then it stops.

WARNING: the car MOVES. Put it on the floor first.

Run it:
    Windows:  python robot.py
    Mac:      python3 robot.py

Install bleak first:
    Windows:  python -m pip install bleak
    Mac:      python3 -m pip install bleak

On a Mac, say yes when it asks for Bluetooth.
If you miss it: System Settings > Privacy and Security >
Bluetooth > turn on your Terminal app.
"""
import asyncio

from bleak import BleakClient, BleakScanner

BOX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"

SONG = "1155665443322 1"
COLOUR = {"1": "G", "2": "J", "3": "H", "4": "K",
          "5": "I", "6": "L", "7": "G", "8": "J"}


async def say(car, command):
    """Send one command to the robot."""
    await car.write_gatt_char(BOX, command.encode() + b"#", response=False)
    await asyncio.sleep(0.05)


async def find_robot():
    return await BleakScanner.find_device_by_filter(
        lambda d, a: d.name and "micro:bit" in d.name)


async def main():
    print("Looking for the robot...")
    robot = await find_robot()
    if robot is None:
        print("No robot. Is it on? Is the phone app closed?")
        return
    print("Found it:", robot.name)

    async with BleakClient(robot) as car:
        try:
            print("Playing a song.")
            for note in SONG:
                if note == " ":
                    await asyncio.sleep(0.4)
                    continue
                await say(car, note)
                await say(car, COLOUR[note])
                await asyncio.sleep(0.3)
                await say(car, "M")
                await asyncio.sleep(0.1)

            print("Driving in a circle.")
            for i in range(8):
                await say(car, "A")          # forward
                await asyncio.sleep(0.25)
                await say(car, "C")          # turn left
                await asyncio.sleep(0.40)
        finally:
            # This always runs, even if something breaks.
            # So the car always stops.
            for i in range(3):
                await say(car, "0")
                await asyncio.sleep(0.15)
            await say(car, "M")
            print("Stopped.")

    print("Done.")


asyncio.run(main())
😀 faces.py  Safe. No robot needed.

Download faces.py

"""Faces and letters for a 5x5 grid of lights.

The micro:bit screen is 25 lights in a 5 by 5 grid. A picture for it is
five numbers, one for each row. Numbers are hard to read, so here you
draw each picture with dots and hashes instead.

This draws in your terminal. It needs no robot and no Bluetooth, so it
works on any computer.

    python faces.py                  # a smile
    python faces.py heart            # one picture
    python faces.py HELLO            # spell a word
    python faces.py font             # show everything

Run it:
    Windows:  python faces.py heart
    Mac:      python3 faces.py heart
"""
import sys


def icon(*rows):
    """Five strings of dots and hashes -> five numbers.

    One number for each row. Bit 4 is the LEFT light, bit 0 the right one.
    """
    out = bytearray()
    for row in rows:
        value = 0
        for i, ch in enumerate(row[:5]):
            if ch not in ". 0_":
                value |= 1 << (4 - i)
        out.append(value)
    return bytes(out)


FACES = {
    "smile": icon(".#.#.", ".#.#.", ".....", "#...#", ".###."),
    "sad":   icon(".#.#.", ".#.#.", ".....", ".###.", "#...#"),
    "wink":  icon(".#...", ".#.##", ".....", "#...#", ".###."),
    "flat":  icon(".#.#.", ".#.#.", ".....", "#####", "....."),
    "heart": icon(".#.#.", "#####", "#####", ".###.", "..#.."),
    "yes":   icon(".....", "....#", "...#.", "#.#..", ".#..."),
    "no":    icon("#...#", ".#.#.", "..#..", ".#.#.", "#...#"),
    "up":    icon("..#..", ".###.", "#.#.#", "..#..", "..#.."),
    "down":  icon("..#..", "..#..", "#.#.#", ".###.", "..#.."),
}

FONT = {
    " ": icon(".....", ".....", ".....", ".....", "....."),
    "A": icon(".###.", "#...#", "#####", "#...#", "#...#"),
    "B": icon("####.", "#...#", "####.", "#...#", "####."),
    "C": icon(".####", "#....", "#....", "#....", ".####"),
    "D": icon("####.", "#...#", "#...#", "#...#", "####."),
    "E": icon("#####", "#....", "####.", "#....", "#####"),
    "F": icon("#####", "#....", "####.", "#....", "#...."),
    "G": icon(".####", "#....", "#..##", "#...#", ".###."),
    "H": icon("#...#", "#...#", "#####", "#...#", "#...#"),
    "I": icon("#####", "..#..", "..#..", "..#..", "#####"),
    "J": icon("#####", "..#..", "..#..", "#.#..", ".##.."),
    "K": icon("#...#", "#..#.", "###..", "#..#.", "#...#"),
    "L": icon("#....", "#....", "#....", "#....", "#####"),
    "M": icon("#...#", "##.##", "#.#.#", "#...#", "#...#"),
    "N": icon("#...#", "##..#", "#.#.#", "#..##", "#...#"),
    "O": icon(".###.", "#...#", "#...#", "#...#", ".###."),
    "P": icon("####.", "#...#", "####.", "#....", "#...."),
    "Q": icon(".###.", "#...#", "#...#", "#..#.", ".##.#"),
    "R": icon("####.", "#...#", "####.", "#..#.", "#...#"),
    "S": icon(".####", "#....", ".###.", "....#", "####."),
    "T": icon("#####", "..#..", "..#..", "..#..", "..#.."),
    "U": icon("#...#", "#...#", "#...#", "#...#", ".###."),
    "V": icon("#...#", "#...#", "#...#", ".#.#.", "..#.."),
    "W": icon("#...#", "#...#", "#.#.#", "##.##", "#...#"),
    "X": icon("#...#", ".#.#.", "..#..", ".#.#.", "#...#"),
    "Y": icon("#...#", ".#.#.", "..#..", "..#..", "..#.."),
    "Z": icon("#####", "...#.", "..#..", ".#...", "#####"),
    "0": icon(".###.", "#..##", "#.#.#", "##..#", ".###."),
    "1": icon("..#..", ".##..", "..#..", "..#..", ".###."),
    "2": icon("###..", "...#.", "..#..", ".#...", "#####"),
    "3": icon("####.", "....#", "..##.", "....#", "####."),
    "4": icon("#...#", "#...#", "#####", "....#", "....#"),
    "5": icon("#####", "#....", "####.", "....#", "####."),
    "6": icon(".###.", "#....", "####.", "#...#", ".###."),
    "7": icon("#####", "....#", "...#.", "..#..", ".#..."),
    "8": icon(".###.", "#...#", ".###.", "#...#", ".###."),
    "9": icon(".###.", "#...#", ".####", "....#", ".###."),
    "!": icon("..#..", "..#..", "..#..", ".....", "..#.."),
    "?": icon(".###.", "#...#", "...#.", ".....", "..#.."),
}


def render(bitmap, label=""):
    """Print a picture in the terminal."""
    if label:
        print(f"  {label}")
    for row in bitmap:
        line = "".join("#" if (row >> (4 - i)) & 1 else "." for i in range(5))
        print("   ", line.replace("#", "[]").replace(".", " ."))
    print("    numbers:", " ".join(str(b) for b in bitmap))
    print()


def show(what):
    """Draw a face, a letter, or a whole word."""
    if what.lower() == "font":
        for name, bm in list(FACES.items()) + list(FONT.items()):
            render(bm, name)
        return

    if what.lower() in FACES:
        render(FACES[what.lower()], what.lower())
        return

    for ch in what.upper():
        bm = FONT.get(ch)
        if bm is None:
            print(f"  I have no picture for {ch!r}\n")
        else:
            render(bm, ch)


if __name__ == "__main__":
    show(" ".join(sys.argv[1:]) or "smile")
🛣️ track.py  It DRIVES ITSELF. Floor only.

Download track.py

"""Step 9 - the car follows a black line by itself.

WARNING: the car DRIVES ITSELF. Put it on the floor first.

PUT THE CAR ON THE BLACK LINE BEFORE YOU START.
If it does not start on the line, it just drives in circles
looking for one.

    python track.py

Run it:
    Windows:  python track.py
    Mac:      python3 track.py

Install bleak first:
    Windows:  python -m pip install bleak
    Mac:      python3 -m pip install bleak

This script only uses line mode (S#). Yahboom's sheet lists two more
modes, T# and U#. Do NOT use them. They drive the car and 0# does not
stop them - only the power switch does. Tested on a real car.
"""
import asyncio
import sys

from bleak import BleakClient, BleakScanner

BOX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"

SECONDS = 15


async def say(car, command):
    """Send one command to the robot."""
    await car.write_gatt_char(BOX, command.encode() + b"#", response=False)
    await asyncio.sleep(0.05)


async def find_robot():
    return await BleakScanner.find_device_by_filter(
        lambda d, a: d.name and "micro:bit" in d.name)


async def main(seconds):
    print("Looking for the robot...")
    robot = await find_robot()
    if robot is None:
        print("No robot. Is it on? Is the phone app closed?")
        return
    print("Found it:", robot.name)

    async with BleakClient(robot) as car:
        print()
        print("Put the car ON the black line now.")
        print("It will follow the line by itself.")
        for n in [5, 4, 3, 2, 1]:
            print("  starting in", n)
            await asyncio.sleep(1)

        try:
            await say(car, "S")            # line mode on
            print("  following the line for", seconds, "seconds...")
            await asyncio.sleep(seconds)
        finally:
            # Send the stop more than once. One lost message would
            # leave the car driving.
            for i in range(3):
                await say(car, "0")
                await asyncio.sleep(0.15)
            print("Stopped.")

    print("Done.")


asyncio.run(main(float(sys.argv[1]) if len(sys.argv) > 1 else SECONDS))
🙂 smile.py  Safe. It does not move.

Download smile.py

"""Make the robot smile.

Yahboom's firmware draws a happy face while Bluetooth is connected,
and a sad face when it disconnects. So to make the bot smile, we
connect and simply hold the connection open.

    python smile.py        # smile for 30 seconds
    python smile.py 120    # smile for 2 minutes

Run it:
    Windows:  python smile.py
    Mac:      python3 smile.py

Install bleak first:
    Windows:  python -m pip install bleak
    Mac:      python3 -m pip install bleak
"""
import asyncio
import sys

from bleak import BleakClient, BleakScanner


async def main(seconds):
    print("Looking for the robot...")
    robot = await BleakScanner.find_device_by_filter(
        lambda d, a: d.name and "micro:bit" in d.name)
    if robot is None:
        print("No robot. Is it on? Is the phone app closed?")
        return
    print("Found it:", robot.name)

    async with BleakClient(robot) as car:
        print(f"Connected - the robot is smiling for {seconds}s.")
        try:
            await asyncio.sleep(seconds)
        except KeyboardInterrupt:
            pass
    print("Disconnected - it goes sad again.")


asyncio.run(main(float(sys.argv[1]) if len(sys.argv) > 1 else 30))

📜 All the commands · คำสั่งทั้งหมด · 全部命令

🇬🇧 English

Every command is one or two letters. Then a # at the end.

The # tells the robot "the message ends here".

Try them. Change one letter and run it again.

🇹🇭 ไทย

ทุกคำสั่งมีหนึ่งหรือสองตัวอักษร แล้วปิดท้ายด้วย #

# บอกหุ่นยนต์ว่า "ข้อความจบตรงนี้"

ลองเล่นดู เปลี่ยนตัวอักษรหนึ่งตัว แล้วรันใหม่

🇨🇳 中文

每条命令是一两个字母,最后加一个 #

# 告诉机器人"消息到这里结束"。

试试看。改一个字母,再跑一次。

What it does · ทำอะไร · 做什么Command
Go forward · เดินหน้า · 前进A#
Go back · ถอยหลัง · 后退B#
Turn left · เลี้ยวซ้าย · 左转C#
Turn right · เลี้ยวขวา · 右转D#
Spin left · หมุนซ้าย · 原地左转E#
Spin right · หมุนขวา · 原地右转F#
Stop · หยุด · 停0#
Red light · ไฟแดง · 红灯G#
Green light · ไฟเขียว · 绿灯H#
Blue light · ไฟน้ำเงิน · 蓝灯I#
Yellow light · ไฟเหลือง · 黄灯J#
Lights off · ปิดไฟ · 关灯M#
Notes do to high do · โน้ต โด ถึง โดสูง · do 到高音 do1#8#
Follow a black line · วิ่งตามเส้นดำ · 循黑线走S#
Avoid walls · หลบสิ่งกีดขวาง · 避开障碍T#

Yahboom made this list. You can download their sheet. Yahboom เป็นคนทำรายการนี้ คุณดาวน์โหลดเอกสารของเขาได้ 这个表是 Yahboom 做的。你可以下载他们的文件

DANGER S# is safe. 0# stops it. Do NOT use T# or U#. 0# does not stop those. Only the power switch does. S# ปลอดภัย 0# หยุดได้ อย่าใช้ T# หรือ U# 0# หยุดสองอันนั้นไม่ได้ ต้องปิดสวิตช์เท่านั้น S# 是安全的,0# 能停。不要用 T#U#0# 停不了它们。只有电源开关能停。

🔧 When it does not work · เมื่อมันไม่ทำงาน · 跑不起来的时候

You see thisDo this
No module named bleak Run pythonpython3 -m pip install bleak again.
ติดตั้ง bleak อีกครั้ง · 再装一次 bleak
"No robot" every time Turn the car on. Close the phone app. Sit near the car.
เปิดรถ ปิดแอป นั่งใกล้รถ · 开车、关 App、坐近点
It finds the car, then fails The phone still holds it. Turn the phone Bluetooth off.
มือถือยังจับอยู่ ปิดบลูทูธมือถือ · 手机还占着,关手机蓝牙
Lights work, wheels do not The batteries are low. Charge them.
แบตอ่อน ชาร์จก่อน · 电池没电了,去充电
The car will not stop Turn the car off by hand. Then add finally.
ปิดสวิตช์รถ แล้วใส่ finally · 手动关车,再加 finally
Some commands are ignored You send too fast. Make the sleep bigger.
ส่งเร็วไป เพิ่มเวลาพัก · 发太快了,把 sleep 调大

🚀 Make it your own · ทำให้เป็นของคุณเอง · 做成你自己的

Try thisHowLevel
A new songChange SONG. Use numbers 1 to 8.easy
A square, not a circleUse a longer forward, then E to spin.easy
Faster flashingMake the sleep numbers smaller.easy
One key for a whole dancePut a list of moves under one key in move.py.harder
Dance to your own songPut the note list and the move list side by side.harder
Stop before a wallRead the distance the car sends back.harder