第10章 ESP32 SPI 與 UART 教學:Serial、GPS、RFID 與 Arduino UNO 通訊

發布日期: |更新日期:
文章主圖:第10章 ESP32 SPI 與 UART 教學:Serial、GPS、RFID 與 Arduino UNO 通訊

前言: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
SPI 主要訊號與功能
訊號 全名 主要功能
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 通訊比較
項目 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 不同。

例如:

ESP32 系列 SoC HP UART 數量
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 通訊介面比較
功能 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 UART 接線表
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 與大量外部硬體模組的基礎能力。

下一步學習

不要停在這一篇,接著把內容做成自己的實作

目前主題:Arduino。從完整學習路線、免費資源、會員下載,再接到實作所需材料。

同系列:ESP32 |  已是會員?直接前往 Getfile
相關資源與會員文章下載
文章可免費閱讀;下載完整 DOCX 或 PDF 需登入免費基本會員。
查看相關資源 會員免費下載 DOCX 會員免費 PDF/列印

← 回文章列表