前言:ESP32 為什麼還要學 SPI 與 UART?
ESP32 可以透過許多不同方式與外部裝置交換資料。
例如:
GPIO
I²C
SPI
UART
Wi-Fi
Bluetooth
前面的 I²C 通訊非常適合:
LCD1602
OLED
RTC
BME280
MPU6050
BH1750
等裝置。
但是如果要連接:
SD Card
TFT LCD
RFID
高速 ADC
Ethernet Controller
通常會遇到:
SPI
而如果要連接:
GPS
指紋辨識模組
Barcode Scanner
GSM / LTE
Arduino UNO
另一塊 ESP32
Serial RFID Reader
就常使用:
UART
因此:
I²C
SPI
UART
可以說是微控制器最重要的三種硬體通訊介面。
這一章要一次掌握:
SPI
├── MOSI
├── MISO
├── SCK
└── CS
UART
├── TX
├── RX
├── Baud Rate
├── Serial
├── Serial1
├── Serial2
└── HardwareSerial
最後完成:
ESP32 與 Arduino UNO UART 雙向資料交換
一、什麼是 SPI?
SPI 全名:
Serial Peripheral Interface
是一種:
同步式序列通訊介面
SPI 通常由 Controller 控制一個或多個 Peripheral。
傳統教材也經常使用:
Master
Slave
這組術語。
基本架構:
SPI Device
▲
│
ESP32 ───────────┤
│ │
├── MOSI ───────┤
├── MISO ───────┤
├── SCK ───────┤
└── CS ───────┘
SPI 的重要特色是:
速度高
全雙工
硬體架構直接
適合大量資料
因此很適合 Display、Memory 與高速 Peripheral。
二、SPI 的四條重要訊號
SPI 最需要記住:
MOSI
MISO
SCK
CS
| 訊號 | 全名 | 主要功能 |
|---|---|---|
| MOSI | Master Out Slave In | Controller 傳資料給 Peripheral |
| MISO | Master In Slave Out | Peripheral 傳資料給 Controller |
| SCK | Serial Clock | SPI Clock |
| CS | Chip Select | 選擇目前通訊的裝置 |
三、MOSI 是什麼?
MOSI:
Master Out Slave In
當 ESP32 是 Controller:
ESP32
│
│ MOSI
▼
SPI Device
也就是 ESP32 傳送資料給裝置。
例如:
ESP32
↓
MOSI
↓
TFT LCD
可以傳送:
Command
Display Data
Pixel Data
Configuration
有些模組不一定直接標:
MOSI
也可能寫:
DIN
SDI
SDA
DATA IN
所以接線前應查看模組 Datasheet。
四、MISO 是什麼?
MISO:
Master In Slave Out
方向剛好相反。
SPI Device
│
│ MISO
▼
ESP32
例如:
SD Card
↓
MISO
↓
ESP32
ESP32 就可以從 SD Card 讀取資料。
某些只需要 ESP32 單向輸出的 Display Module,
可能沒有使用:
MISO
五、SCK 是什麼?
SCK:
Serial Clock
也常寫:
SCLK
CLK
因為 SPI 是:
同步通訊
Controller 會產生 Clock。
例如:
SCK
┌─┐ ┌─┐ ┌─┐ ┌─┐
───┘ └─┘ └─┘ └─┘ └──
MOSI / MISO 的資料傳輸會配合這個 Clock。
因此:
SPI
→ 有 Clock
UART
→ 沒有 Clock Line
這是兩者很重要的差異。
六、CS 是什麼?
CS:
Chip Select
也常看到:
SS
Slave Select
它的用途就是:
決定現在要和哪一個 SPI Device 通訊。
假設有:
SD Card
TFT
RFID
它們可以共用:
MOSI
MISO
SCK
但是:
SD Card → CS1
TFT → CS2
RFID → CS3
例如:
ESP32
│
├──────── MOSI ─────┬── SD
│ ├── TFT
│ └── RFID
│
├──────── MISO ─────┬── SD
│ ├── TFT
│ └── RFID
│
├──────── SCK ──────┬── SD
│ ├── TFT
│ └── RFID
│
├── CS1 ─────────────── SD
├── CS2 ─────────────── TFT
└── CS3 ─────────────── RFID
七、SPI 和 I²C 最大差異
I²C 使用:
SDA
SCL
Address
來辨識裝置。
SPI 通常使用:
MOSI
MISO
SCK
CS
其中:
CS
決定現在要操作哪個 Peripheral。
| 項目 | I²C | SPI |
|---|---|---|
| Clock | SCL | SCK |
| Data | SDA | MOSI + MISO |
| 裝置識別 | Address | CS |
| GPIO 數量 | 少 | 較多 |
| 多裝置 | 很方便 | 每裝置通常需要 CS |
| 速度 | 中等 | 通常較高 |
| Full Duplex | 否 | 可以 |
因此:
多個低速 Sensor
→ I²C
大量高速資料
→ SPI
八、原始 ESP32 常見 SPI Pin
對常見原始 ESP32 / ESP32-WROOM-32,
Arduino-ESP32 官方 SPI 範例中 VSPI 預設使用:
SCK → GPIO18
MISO → GPIO19
MOSI → GPIO23
SS → GPIO5
官方範例也列出 HSPI:
SCK → GPIO14
MISO → GPIO12
MOSI → GPIO13
SS → GPIO15
因此在許多 ESP32 教材會看到:
GPIO18
GPIO19
GPIO23
GPIO5
這組 SPI 配置。
但是:
不同 ESP32 SoC 與 Board 不一定使用完全相同的預設 SPI Pin。
九、ESP32 SPI Library
Arduino 使用:
#include <SPI.h>
最基本初始化:
SPI.begin();
也可以自行指定:
SPI.begin(
SCK,
MISO,
MOSI,
CS
);
例如:
SPI.begin(
18,
19,
23,
5
);
目前 Arduino-ESP32 官方 SPIClass 範例也支援自行指定這四個腳位。
十、SPI Transaction
SPI 裝置可能需要不同:
Frequency
Bit Order
SPI Mode
所以常使用:
SPI.beginTransaction(
SPISettings(
1000000,
MSBFIRST,
SPI_MODE0
)
);
這裡:
1000000
→ 1 MHz
MSBFIRST
→ 最高有效位元先傳
SPI_MODE0
→ SPI Mode 0
然後:
digitalWrite(
CS_PIN,
LOW
);
開始操作指定 Device。
十一、SPI.transfer()
資料傳輸可以使用:
uint8_t rxData =
SPI.transfer(
txData
);
SPI 是 Full Duplex,
所以在:
送出一個 Byte
的同時,
也可以:
接收一個 Byte
完整概念:
SPI.beginTransaction(
SPISettings(
1000000,
MSBFIRST,
SPI_MODE0
)
);
digitalWrite(
CS_PIN,
LOW
);
uint8_t result =
SPI.transfer(
0x55
);
digitalWrite(
CS_PIN,
HIGH
);
SPI.endTransaction();
Arduino-ESP32 官方 SPI 範例也是採用 beginTransaction() → CS LOW → transfer() → CS HIGH → endTransaction() 的結構。
十二、哪些模組常使用 SPI?
常見包括:
SD Card
ESP32
↓
SPI
↓
MicroSD
TFT LCD
例如:
ILI9341
ST7735
ST7789
RFID
例如:
MFRC522
常見模組多使用 SPI。
Ethernet
例如:
W5500
Flash Memory
SPI Flash
高速 ADC / DAC
因此 SPI 很適合:
高速 Peripheral。
十三、UART 是什麼?
UART 全名:
Universal Asynchronous Receiver / Transmitter
中文:
通用非同步收發器
UART 通常只需要:
TX
RX
GND
兩個裝置:
Device A TX
→
Device B RX
Device A RX
←
Device B TX
ESP32 Arduino 的 UART API 由 HardwareSerial 提供,支援 Full Duplex、可設定 Baud Rate、Data Bits、Parity、Stop Bits 等參數。
十四、UART 為什麼叫非同步?
SPI:
Data
+
Clock
UART 沒有 Clock Line。
因此兩端必須先約定:
Baud Rate
Data Bits
Parity
Stop Bits
例如:
9600
8N1
代表:
9600 baud
8 Data Bits
N
No Parity
1
1 Stop Bit
十五、TX 與 RX
TX:
Transmit
負責:
送資料
RX:
Receive
負責:
收資料
所以:
TX 必須接對方 RX。
例如:
ESP32 TX
─────────────→
Arduino RX
ESP32 RX
←─────────────
Arduino TX
不要寫成:
TX → TX
RX → RX
十六、UART 為什麼要共地?
除了:
TX
RX
還需要:
GND
例如:
ESP32 GND
────────────
Arduino GND
原因是 UART 的:
HIGH
LOW
必須有共同 Ground Reference。
如果沒有共地:
資料錯誤
亂碼
不穩定
完全無法通訊
都可能發生。
十七、ESP32 有幾組 UART?
目前 Arduino-ESP32 官方文件顯示 UART 數量依 SoC 不同。
例如:
| SoC | HP UART |
|---|---|
| ESP32 | 3 |
| ESP32-S2 | 2 |
| ESP32-S3 | 3 |
| ESP32-C3 | 2 |
| ESP32-C6 | 2 |
| ESP32-P4 | 5 |
Arduino Core 會根據實際 SoC 建立:
Serial
Serial1
Serial2
...
等 HardwareSerial Object。
所以:
「所有 ESP32 都一定有完全相同的 Serial2」是不精確的說法。
本章主實作以:
原始 ESP32 / ESP32-WROOM-32
為主要平台。
十八、Serial 是什麼?
大家最熟悉:
Serial.begin(
115200
);
通常就是:
UART0
Arduino-ESP32 官方目前說明:
Serial
Serial0
對應 UART0,
通常用於:
Programming
Debug
Serial Monitor
因此一般不建議把 Debug 用的:
Serial
又直接拿去長期連接 GPS 或 Arduino UNO。
十九、Serial1 是什麼?
額外 Hardware UART 可以透過:
Serial1
使用。
例如:
Serial1.begin(
9600,
SERIAL_8N1,
16,
17
);
目前 Arduino-ESP32 HardwareSerial::begin() 正式支援:
begin(
baud,
config,
rxPin,
txPin
);
因此可以自行指定 RX / TX GPIO。
二十、Serial2 是什麼?
在原始 ESP32 上有三個 HP UART,
所以常可以使用:
Serial
Serial1
Serial2
例如本章:
Serial2.begin(
9600,
SERIAL_8N1,
16,
17
);
這裡是明確指定:
RX = GPIO16
TX = GPIO17
所以我們不依賴 Serial2 的預設 Pin。
這一點在目前 Arduino-ESP32 3.x 很重要,因為新版 Core 某些 UART Default Pin 已經調整過;自行指定 Pin 可以讓教材更清楚。
二十一、HardwareSerial 是什麼?
Serial1、Serial2 本質上就是:
HardwareSerial
也可以自己建立物件:
HardwareSerial
unoSerial(2);
然後:
unoSerial.begin(
9600,
SERIAL_8N1,
16,
17
);
因此:
Serial2
和:
HardwareSerial unoSerial(2);
本質上都是在操作硬體 UART。
二十二、HardwareSerial::begin()
目前官方 API:
void begin(
unsigned long baud,
uint32_t config = SERIAL_8N1,
int8_t rxPin = -1,
int8_t txPin = -1,
...
);
例如:
Serial2.begin(
9600,
SERIAL_8N1,
16,
17
);
其中:
9600
→ Baud Rate
SERIAL_8N1
→ 8N1
16
→ RX
17
→ TX
二十三、Serial.available()
接收前先使用:
Serial2.available();
它會回傳:
RX Buffer 中目前可讀取的 Byte 數量。
例如:
while (
Serial2.available()
)
{
char c =
Serial2.read();
}
目前官方 API 也是這樣定義 available() 與 read()。
二十四、Serial.read()
取得一個 Byte:
int data =
Serial2.read();
如果是文字:
char c =
Serial2.read();
例如 GPS 傳:
$GPRMC,...
ESP32 可以一個 Character 一個 Character 接收。
二十五、Serial.print() / println()
傳送:
Serial2.print(
"TEMP:"
);
再:
Serial2.println(
27.5
);
對方就會收到:
TEMP:27.5
println() 特別方便建立:
一行一個 Message
的簡易通訊協定。
二十六、GPS 為什麼常用 UART?
很多 GPS Receiver 都會:
GPS TX
↓
ESP32 RX
送出 NMEA Data。
例如:
$GPGGA,...
$GPRMC,...
$GPGSV,...
基本架構:
GPS TX
→ ESP32 Serial1 RX
GPS RX
← ESP32 Serial1 TX
程式:
Serial1.begin(
9600,
SERIAL_8N1,
GPS_RX_PIN,
GPS_TX_PIN
);
然後:
while (
Serial1.available()
)
{
Serial.write(
Serial1.read()
);
}
這就是最簡單的:
GPS Serial Bridge。
二十七、RFID 都使用 UART 嗎?
不一定。
RFID 是:
Application / Device Category
不是通訊協定。
不同 RFID Module 可能使用:
SPI
UART
I²C
USB
例如常見:
MFRC522
多使用:
SPI(補充說明)
而某些 RFID Reader 則提供:
UART(補充說明)
因此接模組前一定要查看:
Datasheet
Pin Definition
Communication Interface
二十八、哪些模組常使用 UART?
常見:
GPS
Fingerprint Sensor
Barcode Scanner
QR Scanner
GSM
LTE
LoRa UART Module
RFID Reader
Arduino UNO
另一塊 ESP32
Raspberry Pi
所以 UART 是:
MCU-to-MCU 與 Serial Module 通訊的核心介面。
二十九、SPI 和 UART 如何選?
如果是:
SD Card
TFT
高速 ADC
MFRC522
通常優先考慮:
SPI(補充說明 2)
如果是:
GPS
GSM
Fingerprint
Arduino UNO
Serial Reader
通常優先考慮:
UART(補充說明 2)
三十、SPI、I²C、UART 一次比較
| 功能 | I²C | SPI | UART |
|---|---|---|---|
| Clock | SCL | SCK | 無 |
| Data | SDA | MOSI/MISO | TX/RX |
| 裝置選擇 | Address | CS | 通常點對點 |
| 多裝置 | 很方便 | 多 CS | 通常一對一 |
| GPIO | 少 | 較多 | 少 |
| Full Duplex | 否 | 可以 | 可以 |
| 高速資料 | 普通 | 很適合 | 依 Baud Rate |
| Sensor | 常見 | 常見 | 常見 |
| GPS | 少 | 少 | 很常見 |
| TFT | 少 | 很常見 | 少 |
| MCU ↔ MCU | 可以 | 可以 | 非常方便 |
最簡單可以記:
I²C
→ 多個低速周邊
SPI
→ 高速 Peripheral
UART
→ 點對點 Serial
三十一、實作:ESP32 與 Arduino UNO UART 雙向資料交換
接下來進入本章主實作:
ESP32 ↔ Arduino UNO
目標:
ESP32
↓
送出
ESP32:HELLO
Arduino UNO
↓
收到
↓
回覆
UNO:ACK
ESP32
↓
收到 ACK
形成:
Bidirectional UART Communication
三十二、為什麼 UNO 不直接使用 D0 / D1?
Arduino UNO:
D0 = RX
D1 = TX
同時連接板上的:
USB Serial
如果又接 ESP32,
可能干擾:
Upload
Serial Monitor
Debug
因此本章讓:
D0 / D1
繼續留給 PC。
ESP32 通訊則使用:
SoftwareSerial
三十三、Arduino UNO GPIO 規劃
設定:
D10
→ RX
D11
→ TX
程式:
SoftwareSerial espSerial(
10,
11
);
參數順序:
RX
TX
所以:
D10 = RX
D11 = TX
三十四、ESP32 GPIO 規劃
ESP32:
GPIO16
→ RX
GPIO17
→ TX
使用:
Serial2.begin(
9600,
SERIAL_8N1,
16,
17
);
因此:
Serial
→ PC Serial Monitor
Serial2
→ Arduino UNO
分工非常清楚。
三十五、ESP32 與 UNO 最大問題:3.3V / 5V
這是本實作最重要的硬體觀念。
Arduino UNO R3 的:
IOREF
是:
5V
官方 UNO R3 Datasheet 目前仍明確列出其 Digital Logic Reference 接到 5V。
ESP32 GPIO 則是:
3.3V Logic
因此:
UNO TX
→ 5V
不能不經處理就直接送入:
ESP32 RX
三十六、UNO TX → ESP32 RX 電阻分壓
教材可以使用:
Arduino D11 TX
│
1kΩ
│
├──── ESP32 GPIO16 RX
│
2kΩ
│
GND
理論:
Vout
=
5V × 2k
──────
1k+2k
≈ 3.33V
所以:
UNO TX 5V
↓
Voltage Divider
↓
約 3.3V
↓
ESP32 RX
正式產品則更推薦依完整訊號條件使用合適的:
Logic Level Translator
Buffer
三十七、ESP32 TX → UNO RX 呢?
另一方向:
ESP32 TX
3.3V
↓
Arduino UNO RX
在 UNO R3 / ATmega328P 一般數位輸入條件下,3.3V HIGH 通常可以被辨識,因此本章教材可以直接連接。
UNO 使用 ATmega328P,Arduino 官方資料顯示 UNO R3 邏輯系統工作於 5V。
因此實務重點是:
UNO TX → ESP32 RX 一定要處理 5V → 3.3V。
三十八、完整接線
ESP32 → UNO
ESP32 GPIO17 TX
│
└────────→ Arduino D10 RX
UNO → ESP32
Arduino D11 TX
│
1kΩ
│
├────────→ ESP32 GPIO16 RX
│
2kΩ
│
GND
Ground
Arduino GND
────────────
ESP32 GND
三十九、完整接線表
| Arduino UNO | ESP32 | 備註 |
|---|---|---|
| D10 RX | GPIO17 TX | ESP32 → UNO |
| D11 TX | GPIO16 RX | 需 5V → 3.3V 分壓 |
| GND | GND | 必須共地 |
注意:
TX → RX
RX ← TX
四十、Arduino UNO 完整程式
/*
Arduino UNO
<-> ESP32 UART
UNO:
D10 = RX
D11 = TX
D11 TX -> ESP32 RX
必須進行 5V -> 3.3V 電平處理
*/
#include <SoftwareSerial.h>
const uint8_t ESP_RX_PIN = 10;
const uint8_t ESP_TX_PIN = 11;
SoftwareSerial espSerial(
ESP_RX_PIN,
ESP_TX_PIN
);
String rxBuffer;
// =====================================
// Process ESP32 Message
// =====================================
void processMessage(
String message
)
{
message.trim();
Serial.print(
"From ESP32: "
);
Serial.println(
message
);
if (
message == "ESP32:HELLO"
)
{
espSerial.println(
"UNO:ACK"
);
Serial.println(
"Reply -> UNO:ACK"
);
}
else
{
espSerial.print(
"UNO:ECHO:"
);
espSerial.println(
message
);
}
}
// =====================================
// setup
// =====================================
void setup()
{
// PC Serial Monitor
Serial.begin(
115200
);
// ESP32 UART
espSerial.begin(
9600
);
Serial.println();
Serial.println(
"Arduino UNO UART Ready"
);
}
// =====================================
// loop
// =====================================
void loop()
{
while (
espSerial.available()
)
{
char c =
espSerial.read();
if (
c == '\n'
)
{
if (
rxBuffer.length() > 0
)
{
processMessage(
rxBuffer
);
rxBuffer = "";
}
}
else if (
c != '\r'
)
{
rxBuffer += c;
}
}
}
四十一、ESP32 完整程式
/*
ESP32
<-> Arduino UNO UART
ESP32:
GPIO16 = RX
GPIO17 = TX
Serial = USB Debug
Serial2 = Arduino UNO
適用:
原始 ESP32 / ESP32-WROOM-32
*/
const uint8_t UNO_RX_PIN = 16;
const uint8_t UNO_TX_PIN = 17;
const uint32_t UNO_BAUD =
9600;
const unsigned long
SEND_INTERVAL =
2000;
unsigned long
lastSendTime =
0;
String rxBuffer;
// =====================================
// Process UNO Message
// =====================================
void processMessage(
String message
)
{
message.trim();
Serial.print(
"From UNO: "
);
Serial.println(
message
);
if (
message == "UNO:ACK"
)
{
Serial.println(
"UART communication OK"
);
}
}
// =====================================
// setup
// =====================================
void setup()
{
// -----------------------------
// USB Debug
// -----------------------------
Serial.begin(
115200
);
delay(
500
);
Serial.println();
Serial.println(
"ESP32 <-> UNO UART"
);
// -----------------------------
// UART2
// -----------------------------
Serial2.begin(
UNO_BAUD,
SERIAL_8N1,
UNO_RX_PIN,
UNO_TX_PIN
);
Serial.println(
"Serial2 Ready"
);
Serial.println(
"RX = GPIO16"
);
Serial.println(
"TX = GPIO17"
);
}
// =====================================
// loop
// =====================================
void loop()
{
unsigned long currentTime =
millis();
// -----------------------------
// Send every 2 seconds
// -----------------------------
if (
currentTime
- lastSendTime
>= SEND_INTERVAL
)
{
lastSendTime =
currentTime;
Serial2.println(
"ESP32:HELLO"
);
Serial.println(
"To UNO: ESP32:HELLO"
);
}
// -----------------------------
// Receive from UNO
// -----------------------------
while (
Serial2.available()
)
{
char c =
Serial2.read();
if (
c == '\n'
)
{
if (
rxBuffer.length() > 0
)
{
processMessage(
rxBuffer
);
rxBuffer = "";
}
}
else if (
c != '\r'
)
{
rxBuffer += c;
}
}
}
目前 Arduino-ESP32 官方 HardwareSerial::begin() 的確支援 baud + config + rxPin + txPin 形式,因此這支程式不依賴 UART2 的預設 Pin。
四十二、ESP32 Serial Monitor 結果
可能看到:
ESP32 <-> UNO UART
Serial2 Ready
RX = GPIO16
TX = GPIO17
To UNO: ESP32:HELLO
From UNO: UNO:ACK
UART communication OK
To UNO: ESP32:HELLO
From UNO: UNO:ACK
UART communication OK
代表:
ESP32
↓
UNO
UNO
↓
ESP32
兩個方向都正常。
四十三、UNO Serial Monitor 結果
可能看到:
Arduino UNO UART Ready
From ESP32: ESP32:HELLO
Reply -> UNO:ACK
From ESP32: ESP32:HELLO
Reply -> UNO:ACK
這就完成:
ESP32 ↔ Arduino UNO 雙向資料交換。
四十四、為什麼使用 println()?
ESP32:
Serial2.println(
"ESP32:HELLO"
);
最後會帶 Line Ending。
UNO:
if (
c == '\n'
)
就知道:
一筆 Message 已經結束。
因此:
ESP32:HELLO\n
就是一個完整封包。
四十五、建立簡單 UART Protocol
真正專案可以定義:
COMMAND:VALUE
例如:
TEMP:27.5
HUM:63.2
LED:ON
LED:OFF
RELAY:1
MOTOR:128
BUTTON:1
ESP32:
Serial2.println(
"LED:ON"
);
UNO:
收到
LED:ON
↓
控制 LED
這就形成:
Application Protocol。
四十六、把 DHT Sensor Data 傳給 UNO
例如 ESP32 已取得:
float temperature =
27.5;
float humidity =
63.2;
可以:
Serial2.print(
"TEMP:"
);
Serial2.println(
temperature,
1
);
Serial2.print(
"HUM:"
);
Serial2.println(
humidity,
1
);
UNO 收到:
TEMP:27.5
HUM:63.2
之後就可以:
LCD 顯示
Relay 控制
資料儲存
四十七、ESP32 可以做 UART Gateway
這個架構非常實用。
例如:
Arduino UNO
├── Sensor
├── Keypad
├── Relay
└── Motor
│
▼
UART
│
▼
ESP32
│
▼
Wi-Fi
│
▼
MQTT / Web / Cloud
這樣:
UNO
→ 負責 I/O 與原本設備
ESP32
→ 負責網路與 Cloud
不必一次把舊 Arduino 專案全部改寫成 ESP32。
四十八、GPS + UNO 可以同時使用嗎?
在原始 ESP32 上,可以規劃:
Serial
→ PC Debug
Serial1
→ GPS
Serial2
→ Arduino UNO
例如:
GPS
↓
Serial1
↓
ESP32
↓
Serial2
↓
Arduino UNO
不過不同 ESP32 SoC 的 UART 數量不同,因此在 ESP32-C3、C6 等 Board 上應重新確認資源。
四十九、Serial Bridge Debug
UART Debug 最方便的方法之一:
while (
Serial2.available()
)
{
Serial.write(
Serial2.read()
);
}
這樣:
External Module
↓
Serial2
↓
Serial Monitor
原始資料直接顯示在 PC。
例如 GPS:
$GPRMC...
$GPGGA...
就能直接觀察。
五十、常見錯誤:TX 接 TX
錯誤:
ESP32 TX
→
UNO TX
正確:
ESP32 TX
→
UNO RX
以及:
UNO TX
→
ESP32 RX
記住:
TX 找對方的 RX。
五十一、常見錯誤:沒有共地
UART 兩端一定要有:
Common GND
例如:
ESP32 GND
↔
UNO GND
否則即使:
TX
RX
接對,
也可能無法可靠通訊。
五十二、常見錯誤:UNO 5V TX 直接接 ESP32
這是硬體風險。
UNO R3:
5V Logic
ESP32:
3.3V Logic
所以:
UNO TX
↓
Voltage Divider / Level Shifter
↓
ESP32 RX
應該列為標準接法。Arduino UNO R3 官方資料確認 IOREF 為 5V。
五十三、常見錯誤:兩邊 Baud Rate 不同
例如:
ESP32:
Serial2.begin(
9600,
...
);
UNO:
espSerial.begin(
115200
);
結果就會:
亂碼
Frame Error
資料錯誤
必須一致:
ESP32
9600
UNO
9600
五十四、常見錯誤:使用 UART0 接外部模組
UART0 通常是:
Serial Monitor
Programming
Debug
所以如果把 GPS 或 UNO 接在 UART0,
可能造成:
Upload 失敗
Log 混合
開機亂碼
外部模組干擾
原始 ESP32 有多個 UART 時,
優先把應用裝置放到:
Serial1
Serial2
會比較容易 Debug。官方也將 UART0 定位為通常用於 Programming / Debug 的 Serial Port。
五十五、常見錯誤:以為 Serial2 永遠固定 GPIO16 / 17
這一點現在尤其重要。
舊 ESP32 教材經常直接說:
RX2 = GPIO16
TX2 = GPIO17
但 Arduino-ESP32 3.x 曾調整部分 UART Default Pin Assignment。
因此本章不是依賴「預設」。
而是直接:
Serial2.begin(
9600,
SERIAL_8N1,
16,
17
);
明確要求:
RX = GPIO16
TX = GPIO17
這才是較穩定的教材寫法。
五十六、常見錯誤:所有 ESP32 都有三個 UART
不是。
目前官方顯示:
ESP32
→ 3 HP UART
ESP32-S2
→ 2
ESP32-S3
→ 3
ESP32-C3
→ 2
等差異。
所以使用:
Serial2
之前應先確認 Board / SoC。
五十七、常見錯誤:SPI CS 都固定 GPIO5
不是。
對原始 ESP32 VSPI 官方範例:
SS = GPIO5
只是常見預設。
實際專案:
SD_CS
TFT_CS
RFID_CS
可以依:
Board
Library
GPIO Availability
重新指定。
五十八、常見錯誤:MOSI / MISO 接反
常見模組標示:
MOSI
MISO
但也可能看到:
DIN
DOUT
SDI
SDO
一般:
ESP32 MOSI
→ Peripheral Data In
ESP32 MISO
← Peripheral Data Out
不要只依線的排列位置猜。
五十九、常見錯誤:多個 SPI Device 使用同一 CS
通常:
MOSI
MISO
SCK
可以共用,
但:
CS
要各自獨立。
例如:
SD_CS
TFT_CS
RFID_CS
才能逐一選擇 Device。
六十、ESP32 SPI / UART 實際應用
SPI(補充說明 3)
ESP32 + SD Card
ESP32 + TFT
ESP32 + MFRC522
ESP32 + W5500
ESP32 + SPI Flash
UART(補充說明 3)
ESP32 + GPS
ESP32 + Fingerprint
ESP32 + GSM
ESP32 + Barcode Scanner
ESP32 + Arduino UNO
ESP32 + Raspberry Pi
ESP32 + ESP32
六十一、FAQ:ESP32 SPI / UART 常見問題
Q1:ESP32 SPI MOSI 是哪一腳?
對常見原始 ESP32 VSPI 官方預設:
GPIO23
Q2:ESP32 SPI MISO 是哪一腳?
常見原始 ESP32 VSPI:
GPIO19
Q3:ESP32 SPI SCK 是哪一腳?
常見原始 ESP32 VSPI:
GPIO18
Q4:ESP32 SPI CS 是哪一腳?
官方原始 ESP32 VSPI 範例預設:
GPIO5
但實際 CS 可以依專案重新安排。
Q5:ESP32 有幾個 UART?
依 SoC 不同。
原始 ESP32:
3 個 HP UART
其他型號可能不同。
Q6:Serial、Serial1、Serial2 是什麼?
在具有對應 UART 的 ESP32 SoC:
Serial / Serial0
→ UART0
Serial1
→ 額外 UART
Serial2
→ 額外 UART
它們都是 HardwareSerial 物件。
Q7:ESP32 UART 可以指定 RX / TX 嗎?
可以。
例如:
Serial2.begin(
9600,
SERIAL_8N1,
16,
17
);
目前 Arduino-ESP32 官方 API 正式支援自訂 RX / TX Pin。
Q8:ESP32 可以與 Arduino UNO UART 通訊嗎?
可以。
但要注意:
UNO = 5V Logic
ESP32 = 3.3V Logic
特別是:
UNO TX
→ ESP32 RX
應做降壓或 Logic Level Conversion。UNO R3 官方 IOREF 為 5V。
Q9:UART 一定需要 GND 嗎?
一般 TTL UART 兩板直接連接:
要。
GND ↔ GND
是必要的共同 Voltage Reference。
Q10:GPS 通常使用什麼?
很多 GPS Module 使用:
UART(補充說明 4)
ESP32 可以透過:
Serial1
或其他可用 HardwareSerial 接收 GPS Data。
Q11:RFID 用 SPI 還是 UART?
看模組。
例如:
MFRC522
常用 SPI。
其他 RFID Reader 也可能提供 UART。
應依 Datasheet 決定。
Q12:SPI 與 UART 哪一個比較快?
通常 SPI 可以提供比一般 UART 更高的資料傳輸率,
但:
不能只看速度決定介面。
裝置本身支援什麼 Protocol 才是第一個條件。
六十二、本章學習重點整理
第一個重點:
SPI(補充說明 4)
主要 Signal:
MOSI
MISO
SCK
CS
第二個重點:
對常見原始 ESP32 VSPI:
SCK → GPIO18
MISO → GPIO19
MOSI → GPIO23
SS → GPIO5
這是官方目前範例的預設配置。
第三個重點:
SPI 適合:
SD
TFT
RFID
Memory
高速 Peripheral
第四個重點:
UART:
TX
RX
GND
其中:
TX → RX
RX ← TX
第五個重點:
ESP32 Arduino UART 使用:
Serial
Serial1
Serial2
HardwareSerial
而實際數量依 ESP32 SoC 不同。
第六個重點:
最推薦的初始化方式:
Serial2.begin(
9600,
SERIAL_8N1,
RX_PIN,
TX_PIN
);
明確指定:
Baud
Format
RX
TX
第七個重點:
ESP32 與 UNO:
ESP32 TX
→ UNO RX
UNO TX
→ Level Conversion
→ ESP32 RX
GND
↔ GND
六十三、本章總結
在第 10 章中,我們正式完成 ESP32 三大有線 Serial Interface 中另外兩個重要介面:
SPI(補充說明 5)
以及:
UART(補充說明 5)
首先學習 SPI:
MOSI
MISO
SCK
CS
並理解:
MOSI
→ ESP32 傳給 Peripheral
MISO
→ Peripheral 傳給 ESP32
SCK
→ Clock
CS
→ 選擇 Device
對常見原始 ESP32,Arduino-ESP32 官方目前 VSPI 範例預設為:
SCK GPIO18
MISO GPIO19
MOSI GPIO23
SS GPIO5
並提供另一組 HSPI Bus。
接著進入:
UART(補充說明 6)
掌握:
TX
RX
Baud Rate
8N1
Serial
Serial1
Serial2
HardwareSerial
原始 ESP32 有 3 個 HP UART,但不同 ESP32 SoC 的 UART 數量不同,因此程式應依實際 Board 規劃。
本章實作使用:
Serial2.begin(
9600,
SERIAL_8N1,
16,
17
);
明確指定 GPIO16 為 RX、GPIO17 為 TX,不依賴新版 Core 可能改變的 Default UART Pin。
最後完成:
ESP32 ↔ Arduino UNO UART 雙向資料交換
資料流程:
ESP32
│
│ Serial2
│
├── GPIO17 TX
│ │
│ ▼
│ UNO D10 RX
│
└── GPIO16 RX
▲
│
Voltage Divider
▲
│
UNO D11 TX
兩塊板再:
GND ↔ GND
共地。
ESP32 傳:
ESP32:HELLO
UNO 回:
UNO:ACK
就完成真正的:
MCU-to-MCU Bidirectional Communication
而這個架構還可以繼續擴充:
GPS
↓
Serial1
↓
ESP32
↓
Serial2
↓
Arduino UNO
甚至:
Arduino UNO
↓
UART
↓
ESP32
↓
Wi-Fi
↓
MQTT
↓
Cloud
變成:
ESP32 Serial Gateway
因此本章最值得記住三句話:
I²C 用 Address 選裝置,SPI 用 CS 選裝置,UART 則非常適合點對點 Serial Communication。
UART 接線永遠先確認 TX → RX、RX ← TX、GND 共地。
ESP32 是 3.3V Logic,而 Arduino UNO R3 是 5V Logic,所以 UNO TX → ESP32 RX 必須先處理電平。
掌握 SPI 與 UART 後,ESP32 就已經具備連接 GPS、RFID、SD Card、TFT、Arduino UNO 與大量外部硬體模組的基礎能力。