作者:Bird
在上一篇文章【Maker 電子學】GPS 接收器的原理與應用—PART6 中,我們將 u-blox NEO-7M GPS 接收器模組透過 USB 接到 PC 上,用 terminal 程式直接觀察在真實環境中收到的訊號、解出來的資料,我們也說明了 $GPGSA、$GPGGA、$GPGLL、$GPRMC 等幾個最重要的定位訊息。
這一回我們要試著將 GPS 接收器透過 UART 連接到像 Arduino 這樣的小系統上,來使用 GPS 的訊號。
連接 ESP8266
我們選用 ESP8266 來當作這次實驗的主控晶片。除了 Arduino 原生的 ATmega328p 之外,ESP8266 可能是 Arduino 平台上數一數二流行的晶片,它有 Wi-Fi 的功能,又有足夠大的 flash 和 RAM,處理器的速度也夠快,可以做很多 ATmega328p 做不到或做起來很吃力的事。
不過 ESP8266 最大的缺點就是 I/O 接腳太少,雖然晶片本身提供了 17 支的 GPIO,但 GPIO 0、GPIO 2、GPIO 15 用來控制啟動狀態,使用上有一些限制,而 GPIO 6-11 則用來連接存放程式碼的 SPI flash memory,其中 GPIO 7-10 是 data bits;如果你讓 SPI flash 跑在 4-bit 模式下,這四支腳都要用到;如果你把 SPI flash 設成 1-bit 模式,就可以空出 GPIO 8-10 三支腳出來當 GPIO 使用。
ESP8266 晶片本人有兩組硬體的 UART,但嚴格來說只有一組半。UART 0 有完整的 RxD/TxD 接腳,甚至還有硬體 flow control 用的 RTS/CTS 接腳,但 UART 1 只有一支 TxD 接腳,也就是說 UART 1 只能發不能收。
UART0 的 RxD/TxD 分別在 GPIO 3 和 GPIO 1 上,而 UART 1 的 TxD 則是 GPIO 2。在很多 ESP8266 的開發板上,會把 GPIO13 和 GPIO15 標示成 UART2,這裡其實有點小小的誤會。GPIO13 和 GPIO15 其實是 UART0 的另一組可以設定的接腳,也就是說當你用了 GPIO13/15,GPIO1/GPIO3 就不能當 UART 用了。

(圖片來源:Bird 提供)
當我們在 Arduino 裡面使用 Serial.begin() 來起始 serial 物件時,使用的就是預設在 GPIO1/3 上面的 serial port。如果要用第二組接腳 GPIO13/15,只要呼叫 Serial.swap(),就可以把 UART0 的接腳換到 GPIO13/15 上,但這時 GPIO1/3 上就沒有 UART 訊號了,而一般的 ESP8266 開發板像 NodeMCU 或是 WeMos D1,都是將 UART-USB 橋接晶片接在 GPIO1/3 上,以便透過 USB 可以燒錄 firmware,因此如果將 UART0 的接腳移走,我們雖然可以在別的接腳上使用 UART,但就失去了透過 USB 來觀察 UART0 輸出的方便。
我們想要將 GPS 模組的輸出連接到 ESP8266 上,又希望可以繼續用 Serial.print() 等方便的工具來觀察 ESP8266 的工作狀態,但 UART1 又只有 TxD,無法接收 GPS 的資料。
該怎麼辦呢?
軟體 Serial 界面
Arduino 裡有一個叫做 SoftwareSerial 的函式庫,可以在任意的 GPIO 接腳上用軟體方式模擬 serial 界面。原來的這個函式庫只能給 AVR 架構的 Arduino 硬體使用,在 ESP8266 上直接使用 SoftwareSerial 會遭遇一些奇奇怪怪的問題。幸好後來有一個叫做 EspSoftwareSerial 的函式庫,是專門移植給 ESP8266/ESP32 使用的 SoftwareSerial。
我們可以將 SoftwareSerial 出來的 UART 界面拿來連接 GPS 接收器,而將原來的硬體 UART 保留給 debug 界面。
SoftwareSerial 用起來很簡單:
SoftwareSerial SWSerial (rxPin, txPin); // 初始化 SoftwareSerial 物件
SoftwareSerial 物件繼承了 Serial 物件大部分的 property 跟 method,因此用起來的方法就跟 SErial 物件一樣。
我們先來寫個小程式:
這個程式很簡單,它不斷地從 SoftwareSerial 模擬的 UART 界面上將資料讀進來,然後送到硬體的 UART 0 上。我們在程式中宣告的軟體 UART 接腳是 RxD 用 GPIO 13,TxD 用 GPIO 15。
我們以 ESP8266 的開發板 NodeMCU 爲例,用四根線將 NodeMCU 跟 Neo-7m GPS 接收器模組連接起來:

(圖片來源:Bird 提供)

(圖片來源:Bird 提供)
如果連接正確,當我們把 NodeMCU 的 USB 插上電腦,NodeMCU 就會對 Neo-7m 模組供電,我們就會看到 Neo-7m 板子上的紅色 LED 亮起來。
接下來,在 Arduino 環境中將上面那組程式碼燒到 ESP8266 裡面之後,再用 Arduino 內建的 Serial Monitor 觀察 ESP8266 所連接的 serial port,應該就會看到來自 Neo-7m 模組的訊息,像這樣:

(圖片來源:Bird 提供)
嗯我們又看到了 99.99 的 DOP 了,因爲我做這個實驗的時候是在室內,GPS 接收器收不到任何訊號,所以就如我們上一回所聊的,$GPGSA 後面有一大排逗點,一顆有效的衛星編號都沒有。
從 UART 拿到 GPS 接收器送來的 NMEA 資料後,接下來就可以去 parse 它,拿出我們需要用的數據來使用了。就如我們上次聊過的,如果你只需要經緯度跟時間,就可以抓 $GPGLL;如果需要經緯度跟速度,就要抓 $GPRMC;如果想要知道現在使用的衛星編號、定位精度,就要抓 $GPGSA…。
NMEA-0183 的格式要用 Arduino 的字串處理功能來 parse 並不難,因爲它的格式很單純:又特定字串開頭標示不同的資訊,而每一行內的資訊都以逗點分隔各欄位,所以如果你不需要計算 checksum,其實可以很容易用 strtok() 之類的函數將字串拆解後就拿到所需的資訊。
當然,寫程式如果能站在巨人的肩膀上,開發的速度往往會更快。Arduino 上已經有很多前人寫好的 GPS NMEA parser 函式庫可以使用,常用的像是 MicroNMEA、TinyGPS 等,只要宣告好物件、將有 NMEA 資料流的 UART 餵給 parser,就可以輕鬆拿出所需的特定欄位。這個部分就留給讀者當作進一步研究的題目了。
最準的時鐘
在結束這個系列之前,我想再提一件事。除了定位之外,GPS 還有一個異常強大的功能:時鐘。
GPS 接收器上的時鐘在捕獲 GPS 衛星訊號之後,會與 GPS 衛星上的原子鐘同步,因此在持續有 GPS 訊號存在的狀況下,它所輸出的時間精度就跟衛星上的原子鐘一樣。GPS 上的原子鐘有極高的精確度,誤差大概在數千億甚至上兆分之一的等級,也就是三千多年才會有一秒的誤差。
你只要透過 GPS 接收器捕獲 GPS 衛星,你就有一個跟 GPS 衛星上的原子鐘一樣精確的時鐘。更棒的是,不管你在地球的哪個角落,只要收得到 GPS 衛星訊號,這個時鐘就是同步的。在手機通訊的技術進入 3G 時代之後,由於 CDMA 調變技術需要在不同的基地台之間有精確的時間同步,基地台之間需要一個一致的、同步的時脈訊號,因此在 3G 時代乃至於後來的 4G、5G 系統,基地台都使用 GPS 訊號來同步。
在 Neo-7m GPS 接收器模組的排針上有一個訊號叫做 PPS,它的意思是「pulse per second」,也就是一秒一次的方波。接收器捕獲 GPS 訊號後,PPS 上就會開始輸出這個一秒一次的方波,不過這個 PPS 訊號的精度跟定位精度一樣,都會受到電磁波傳遞延遲、訊號衰減等不確定因素的影響,它的精確度大概在十幾 ns 甚至數百 ns 左右,但它的不準確是以「jitter」的形式存在,長期來看它的準確度仍然會接近原子鐘本身的精確度。
小結
我們花了七次的篇幅,從 GPS 的技術、原理開始講起,順便聊了一些展頻通訊的技術,以及 GPS 的編碼、導航電文的格式等,一直到示範如何使用實際的 GPS 接收器。希望這一次關於 GPS 的系列文章對於讀者們往後使用 GPS 時,能有一些幫助。
(責任編輯:賴佩萱)
- 【Maker電子學】Flash 記憶體的原理與應用—PART22 - 2025/02/27
- 【Maker電子學】Flash 記憶體的原理與應用—PART21 - 2025/02/21
- 【Maker電子學】Flash 記憶體的原理與應用—PART20 - 2025/01/24
訂閱MakerPRO知識充電報
與40000位開發者一同掌握科技創新的技術資訊!