Chapter 05 · Arduino 入门

串口通信

让 Arduino 与电脑对话:打印调试信息、接收命令、用绘图器看波形,以及用软串口接入更多设备。

课程进度42%

关键概念

串口 (UART / Serial)
Universal Asynchronous Receiver/Transmitter,通用异步收发器。一种最基础的一对一通信方式,只需 TX(发)、RX(收)、GND 三根线。数据一位一位地串行传输。Uno 的 D0(RX)/D1(TX) 通过板载 USB 芯片连到电脑,这就是上传程序和串口监视器用的通道。
波特率 (Baud Rate)
每秒传输的比特数(bps)。收发双方必须设成相同的波特率才能正确通信,就像两人说话得用同样的语速。常见值:9600(稳妥)、115200(快,ESP32 默认)。若串口监视器显示乱码,99% 是波特率没对上。
数据帧 (Frame)
UART 每次发一个字节,用"起始位 + 8 数据位 + 停止位"包裹(常写作 8N1:8 数据位、无校验、1 停止位)。起始位标志一帧开始,停止位标志结束,接收方据此对齐字节边界。Arduino 默认就是 8N1,一般无需改动。
Serial.begin(baud)
初始化串口并设波特率,必须在 setup() 里先调用。之后才能用 print/read。ESP32 等还可传更多参数配置引脚和帧格式。调用后可用 while(!Serial); 等待串口就绪(仅部分板如 Leonardo 需要)。
print vs println vs write
print() 把数据转成人类可读文本发出(数字 65 发成字符 "65");println() 同 print 但末尾加换行;write() 发原始字节(把 65 发成一个字节 0x41=字符 'A')。调试打印用 print/println,传二进制协议用 write。
Serial.available() / read()
available() 返回接收缓冲区里还有多少字节没读(>0 说明有数据来了);read() 取出并移除一个字节,无数据时返回 -1。接收数据的标准套路:if (Serial.available()) { char c = Serial.read(); ... }
串口绘图器 (Serial Plotter)
IDE 自带的可视化工具。把 Serial.println(数值) 输出的数字实时绘成折线图,多个用逗号/空格分隔的值会画成多条线。观察传感器波形、调 PID、看噪声时极其直观。注意:绘图器只认纯数字,混入文字会出错。
软串口 (SoftwareSerial)
Uno 只有一个硬件串口(D0/D1),被 USB 占用。要同时接蓝牙、GPS 等串口设备,可用 SoftwareSerial 库把普通数字引脚模拟成串口。缺点是软件模拟,高波特率下不稳定、占 CPU。Mega/ESP32 有多个硬串口则不需要它。

最基础:向电脑打印信息

串口打印是 Arduino 世界的"printf 调试法"——没有屏幕时,靠它了解程序在干什么。

// 每秒打印一次计数和一句话
int count = 0;

void setup() {
  Serial.begin(9600);          // 启动串口,波特率 9600
  Serial.println("Arduino 已启动");
}

void loop() {
  count++;
  Serial.print("第 ");      // print 不换行
  Serial.print(count);
  Serial.println(" 次循环");  // println 换行
  delay(1000);
}
打开串口监视器

上传后,点 IDE 右上角放大镜图标(或菜单 工具→串口监视器)。关键:把监视器右下角的波特率下拉框也调成 9600,与代码里的 Serial.begin(9600) 一致,否则显示乱码。每次打开监视器 Uno 会自动复位、程序从头运行。

UART 一个字节是怎么传的

发送字符 'A' (ASCII 65 = 0b01000001) 的波形,8N1 空闲 起始 D0 D1 D2 D3 D4 D5 D6 D7 停止 空闲 (高) 位(低) 1 0 0 0 0 0 1 0 位(高) ────┐ ┌──┐ ┌──┐ ┌──────── │ │ │ │ │ │ └─────┘ └────────┘ └──────────────┘ 低位先发 (LSB first) 一帧开始 一帧结束 波特率 9600 → 每一位约 104 微秒 收发双方按同一波特率采样,才能对齐这些位。

接收电脑发来的命令

反过来,电脑也能给 Arduino 发指令。下例:在监视器输入 1 开灯、0 关灯。

// 串口收命令控制 LED:发 '1' 开、'0' 关
const int ledPin = 13;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  Serial.println("发 1 开灯,发 0 关灯");
}

void loop() {
  // 缓冲区里有数据才读
  if (Serial.available() > 0) {
    char cmd = Serial.read();   // 读一个字符

    if (cmd == '1') {
      digitalWrite(ledPin, HIGH);
      Serial.println("LED 已开");
    } else if (cmd == '0') {
      digitalWrite(ledPin, LOW);
      Serial.println("LED 已关");
    }
  }
}
注意字符 '1' 不等于数字 1

从串口读来的是 字符。用户敲的 "1" 是字符 '1'(ASCII 值 49),不是整数 1。所以判断要写 cmd == '1'。若要把输入的一串数字转成整数,用 Serial.parseInt();读整行文本用 Serial.readStringUntil('\n')。别忘了把监视器的行尾设置调成"换行符(NL)"配合 readStringUntil。

读取整行命令

// 读一整行文本命令,例如输入 "led on"
void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available()) {
    // 读到换行符为止,得到一整行
    String line = Serial.readStringUntil('\n');
    line.trim();   // 去掉首尾空白和回车

    if (line == "led on") {
      digitalWrite(13, HIGH);
    } else if (line == "led off") {
      digitalWrite(13, LOW);
    } else {
      Serial.print("未知命令: ");
      Serial.println(line);
    }
  }
}

用串口绘图器看波形

把电位器接 A0,打印读数,用绘图器观察旋钮变化的曲线:

// 供串口绘图器用:只 println 纯数字
void setup() {
  Serial.begin(9600);
}

void loop() {
  int v = analogRead(A0);
  Serial.println(v);   // 绘图器会把它画成折线
  delay(50);

  // 多条曲线:用逗号分隔多个值
  // Serial.print(v1); Serial.print(","); Serial.println(v2);
}
监视器 vs 绘图器

同一份 println 输出,串口监视器显示成文字数字流,绘图器(工具→串口绘图器)画成折线图。二者不能同时打开(都占用串口)。绘图器只接受数字;如果你的输出里混了 "raw=" 这类文字,绘图会报错或画不出——给绘图器的代码要输出纯数字。

软串口:接入蓝牙/GPS 等模块

Uno 的硬件串口被 USB 占着,想同时连蓝牙(如 HC-05)又想用监视器调试,就用软串口把 D10/D11 变成第二个串口。

#include <SoftwareSerial.h>

// 软串口:RX=D10, TX=D11(接蓝牙模块时交叉连:模块 TX→D10, 模块 RX→D11)
SoftwareSerial btSerial(10, 11);

void setup() {
  Serial.begin(9600);      // 硬串口:和电脑调试
  btSerial.begin(9600);    // 软串口:和蓝牙模块
  Serial.println("软串口就绪");
}

void loop() {
  // 蓝牙 → 转发到电脑监视器
  if (btSerial.available()) {
    Serial.write(btSerial.read());
  }
  // 电脑 → 转发到蓝牙
  if (Serial.available()) {
    btSerial.write(Serial.read());
  }
}
TX/RX 必须交叉接 + 电平匹配

串口接线口诀:我的 TX 接你的 RX(交叉)。发送脚连对方接收脚才能通。另外 5V 的 Uno 直连 3.3V 模块的 RX 可能损坏模块,需用电阻分压或电平转换。软串口在 57600 以上波特率容易丢数据,蓝牙模块建议设 9600。

本章小结

串口(UART)是最基础的通信方式,只需 TX/RX/GND 三线,数据一位位串行传输。收发双方波特率必须一致(常用 9600、115200),否则乱码——这是新手第一大坑,记得把串口监视器右下角的波特率也调成和 Serial.begin() 相同。发送用 print(转文本)、println(加换行)、write(发原始字节);接收标准套路是 if(Serial.available()) { c = Serial.read(); },注意读到的是字符('1' 是 ASCII 49 而非整数 1),读整数用 parseInt()、读整行用 readStringUntil('\n')。串口绘图器能把纯数字输出实时画成折线,适合看波形,但不能混文字、不能与监视器同开。Uno 只有一个硬串口(被 USB 占用),要再接蓝牙/GPS 用 SoftwareSerial 把普通引脚模拟成串口(高波特率不稳)。接线口诀:TX 接对方 RX 交叉连,5V 与 3.3V 之间要做电平匹配。

⚒️ 配套开发者工具箱 · 17 款免费在线工具