National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 80 24°C PM2.5 21
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 11

UART 序列通訊

鮑率、收送資料與 GPS 這類序列模組
Interaction 25 minute(s)

Learning Objectives

  • 說明 UART 的接線方式(TX 接 RX、共地),以及兩端鮑率必須相同的原因
  • 由鮑率估算傳送一段資料需要多少時間
  • 用 machine.UART 送出資料,並用 any()、readline() 在不卡住程式的情況下接收
  • 解析 GPS 模組送來的 NMEA 語句,取出衛星數與經緯度

一個位元接一個位元地傳

上一課的 I2C 有一條時脈線,控制端用時脈告訴大家「現在讀這個位元」。UART(Universal Asynchronous Receiver-Transmitter) 沒有時脈線,是非同步的:雙方事先約定好速度,各自計時。GPS 模組、藍牙序列模組、指紋辨識模組, 還有你用 USB 線連到電腦的 REPL,都是 UART。

這一課要回答:

  1. UART 怎麼接線?兩端要約定什麼?
  2. MicroPython 怎麼送出與接收資料,而且不讓程式卡住?
  3. GPS 模組一直送來的一行行文字,要怎麼變成經緯度?

接線:交叉接、要共地

每一端都有 TX(傳送)與 RX(接收)。一端的 TX 要接到另一端的 RX,兩條線交叉:

ESP32               GPS 模組
GPIO17 (TX) ──────▶ RX
GPIO16 (RX) ◀────── TX
GND ─────────────── GND
3.3V ────────────── VCC(依模組規格)

GND 一定要接,雙方的電壓才有共同的參考點。另外要確認模組的訊號電壓:ESP32 的腳位是 3.3V, 接 5V 訊號的模組前要先看規格,必要時加電位轉換。

鮑率與資料框

鮑率(baud rate)是每秒傳幾個位元,雙方必須設成一樣,常見的有 9600、115200。 最常見的格式叫 8N1:每個位元組前面加一個起始位元、後面加一個停止位元,8 個資料位元、不用同位檢查, 所以每傳一個位元組要 10 個位元:

閒置(高) │起始│ b0 b1 b2 b3 b4 b5 b6 b7 │停止│ 閒置…
          0    ← 8 個資料位元,低位在前 →   1

9600 鮑率每秒 9600 個位元,也就是每秒最多 960 個位元組。鮑率設錯的話,接收端在錯的時間點讀位元, 收到的就是一堆亂碼。

ESP32 的三組 UART

ESP32 有三組硬體 UART:UART0、UART1、UART2。依 MicroPython 文件,它們的預設腳位是:

UART0 UART1 UART2
TX GPIO1 GPIO10 GPIO17
RX GPIO3 GPIO9 GPIO16
  • UART0 通常接到開發板的 USB 轉序列晶片,給 REPL 與上傳程式用,不要拿來接模組。
  • UART1 的預設腳位常和開發板的快閃記憶體衝突,所以建立時最好明確寫出 tx=、rx=。
  • 這一課用 UART2(TX=GPIO17、RX=GPIO16)。GPIO34~39 只能輸入,只能當 RX。

MicroPython 的 UART 會把收到的位元組先放進接收緩衝區,程式之後再讀:

函式 作用
uart.write(資料) 送出字串或 bytes,回傳送出的位元組數
uart.any() 緩衝區裡有幾個位元組(0 表示還沒有資料)
uart.read() 讀出緩衝區全部的資料(bytes);沒有資料時回傳 None
uart.readline() 讀到換行 \n 為止(包含換行);沒有資料時回傳 None

收到的是 bytes,要當字串處理時用 .decode() 轉換。

逐步看:送出指令、讀回應答

很多序列模組用「AT 指令」設定:送出 AT 加換行,模組回 OK。模擬中的模組在 0.12 秒時回應。 注意第 7 行之後時間前進了約 4 毫秒(送 4 個位元組要花的時間),以及 reply 是 bytes 而不是字串。

逐步看:一行一行接收 GPS 資料

GPS 模組開機後會自己一直送資料,通常每秒一組。程式每 0.25 秒看一次緩衝區,有資料才讀一行, 沒有就去做別的事。這是第 07 課「不讓程式卡住」的寫法。

NMEA 語句:GPS 說的話

GPS 模組送出的文字遵守 NMEA 0183 格式:每一句以 $ 開頭、欄位用逗號分隔、* 後面是兩位十六進位的檢查碼, 最後以 \r\n 結束,一句最長 82 個字元。最常用的一種是 GGA(定位資料):

$GPGGA,023045.00,2458.0870,N,12111.4820,E,1,08,0.9,150.2,M,17.5,M,,*60
  [0]     [1]       [2]    [3]   [4]    [5][6][7] [8]  [9]
索引 內容 這一句的值
0 語句種類(GP=GPS、GGA) $GPGGA
1 UTC 時間 hhmmss.ss 02:30:45(臺灣時間要加 8 小時)
2、3 緯度 ddmm.mmmm、N/S 24 度 58.0870 分,北緯
4、5 經度 dddmm.mmmm、E/W 121 度 11.4820 分,東經
6 定位品質(0=尚未定位,1=GPS 定位) 1
7 使用的衛星數 08
9 海拔(公尺) 150.2

度分換算成度:2458.0870 是 24 度 58.0870 分,1 度=60 分,所以是 24 + 58.0870 ÷ 60 ≈ 24.9681 度。 地圖服務常用的就是這種十進位度數。

檢查碼:把 $ 和 * 之間的每個字元依序做 XOR(^),結果應該等於 * 後面的十六進位數字。 不相等就表示傳輸過程中有字元錯了,這一句要丟掉。

依 Waveshare 的模組說明,常見的 u-blox NEO-6M 模組預設輸出 NMEA、鮑率 9600。

逐步看:解析 GGA,算出經緯度

收到一句之後:轉成字串 → 用逗號切成串列 → 檢查是不是 GGA、有沒有定位 → 換算經緯度。注意 to_degrees 怎麼用 // 把度與分拆開。

互動練習:從 GPS 資料取出位置

模擬的 GPS 模組每秒送一句 GGA,前兩句還沒定位,第三句開始有位置。依序完成任務:

  1. 先直接執行,看收到的原始資料。
  2. 改程式,每收到一句就印出 衛星 8 顆 這樣的格式(int(f[7]))。
  3. 有定位時(定位品質不是 "0"),印出 緯度 24.9681 這樣的格式(四位小數)。還沒定位的句子要跳過,程式不能出錯。
  4. 回答一個問題。

小測驗:接線與鮑率

1.

ESP32 和 GPS 模組用 UART 連接,下面哪一種接法正確?

2.

鮑率 9600、格式 8N1(每個位元組 10 個位元),送出 96 個位元組大約要多久?

3.

uart.any() 回傳 0 時呼叫 uart.readline(),會得到什麼?

第 1 次作答

小測驗:解析 NMEA

1.

下面的程式會印出什麼?

line = "$GPGGA,023045.00,,,,,0,03,99.99,,,,,,*64"
f = line.split(",")
print(len(f), f[6], int(f[7]))
2.

下面的程式會印出什麼?(緯度的度分換算)

v = float("2230.6000")
deg = int(v // 100)
print(round(deg + (v - deg * 100) / 60, 4))
3.

下面的程式計算一句 NMEA 的檢查碼,會印出什麼?

body = "GPS"
c = 0
for ch in body:
    c ^= ord(ch)
print("%02X" % c)
第 1 次作答

重點整理

  • UART 沒有時脈線,雙方要設定相同的鮑率;8N1 每個位元組 10 個位元,9600 鮑率每秒約 960 個位元組。
  • 接線:TX 接對方的 RX、RX 接對方的 TX,GND 一定要接;注意訊號電壓是 3.3V。
  • ESP32 有三組 UART,UART0 留給 REPL;這一課用 UART(2, baudrate=9600, tx=17, rx=16)。
  • write() 送出;any() 先看緩衝區有沒有資料,再 read()/readline(),收到的是 bytes,用 decode() 轉字串。
  • NMEA 語句用逗號分欄位、* 後面是 XOR 檢查碼;GGA 的緯度 ddmm.mmmm 要換算成十進位度數,沒定位時欄位是空的。

下一課讓 ESP32 連上 Wi-Fi,開始和網路上的服務交換資料。

這一課完成了!
Next lesson:連上 Wi-Fi
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。