嵌入式常用通信协议

参考:吃透 12种嵌入式通信协议,项目选型不用愁 - 深度Linux(公众号)


板级通信协议(3 种)

I2C:简洁高效的双线舞者

I²C(Inter-Integrated Circuit)是一种两线制串行通信协议,仅需两根信号线:时钟线 SCL 和 数据线 SDA。总线上可以挂载多个设备,每个设备都有唯一的地址。通信始终由主设备(Master) 主动发起并控制时钟,从设备(Slave)只能被动响应主设备的请求。主设备负责产生起始条件、停止条件、时钟脉冲以及决定数据传输方向(读或写);从设备在收到匹配的地址后才会参与通信。这种“主从架构”使得 I²C 非常适合电路板内部多个低速外设之间的连接。

I2C

一、理论知识总结

项目 说明
物理层 两线制:串行时钟线 SCL、串行数据线 SDA;开漏输出 + 上拉电阻,支持多主多从
起始条件 (S) SCL 高电平期间,SDA 发生 高→低 跳变
停止条件 (P) SCL 高电平期间,SDA 发生 低→高 跳变
数据传输 每个 SCL 脉冲传输 1 位,MSB 先传(MSB最高有效位,LSB最低有效位);SDA 仅在 SCL 低电平时变化,高电平时保持稳定
地址 + 读写位 7 位从机地址 + 1 位 R/W 位(0 = 写,1 = 读) → 共 8 位
应答 (ACK) 接收方在第 9 个 SCL 脉冲将 SDA 拉低(ACK)或保持高电平(NACK)
写操作 主设备每发完 1 字节,等待从设备 ACK;从设备每收完 1 字节,发送 ACK
读操作 主设备每收到 1 字节,需主动向从设备发送 ACK(希望继续读)或 NACK(结束读)
重复起始 (Sr) 不发送停止条件,直接再次发送起始信号,保持总线占用,用于改变读写方向
速率模式 标准模式 100 kbps,快速模式 400 kbps,高速模式 3.4 Mbps
优点 引脚少、布线简单、支持多主、时钟同步
缺点 速度较慢、通信距离短(<1m)、地址易冲突(需人工分配)
典型应用 传感器(SHT30、BMP280)、EEPROM(24LCxx)、RTC(DS1307)、OLED(SSD1306)

二、I2C 通信协议流程

2.1 标准写操作流程(主机向从机写数据)
sequenceDiagram
    participant M as 主机 (Master)
    participant S as 从机 (Slave)

    M->>S: 起始条件 (S)
    M->>S: 发送 7位地址 + 写位 (0)
    S-->>M: ACK (拉低SDA)
    M->>S: 发送数据字节 (8位)
    S-->>M: ACK
    Note over M,S: 可以重复发送多个数据字节
    M->>S: 停止条件 (P)
2.2 标准读操作流程(主机从从机读数据)
sequenceDiagram
    participant M as 主机 (Master)
    participant S as 从机 (Slave)

    M->>S: 起始条件 (S)
    M->>S: 发送 7位地址 + 读位 (1)
    S-->>M: ACK
    S-->>M: 发送数据字节1
    M-->>S: ACK (希望继续读)
    S-->>M: 发送数据字节2
    M-->>S: NACK (最后一个字节后不再读)
    M->>S: 停止条件 (P)
2.3 复合操作流程(写寄存器地址 + 读数据,典型传感器读取)
sequenceDiagram
    participant M as 主机 (MCU)
    participant S as 从机 (传感器)

    M->>S: 起始条件 (S)
    M->>S: 发送 7位地址 + 写位 (0)
    S-->>M: ACK
    M->>S: 发送寄存器地址 (8位)
    S-->>M: ACK
    M->>S: 重复起始条件 (Sr)
    M->>S: 发送 7位地址 + 读位 (1)
    S-->>M: ACK
    S-->>M: 发送数据字节 (8位)
    M-->>S: NACK (结束读取)
    M->>S: 停止条件 (P)

说明

  • 每个字节传输后必须跟一个应答位(ACK / NACK),由接收方控制 SDA。
  • 主机在读取最后一个字节前应发送 NACK,然后发送停止条件,以释放总线。
  • 重复起始(Sr)可以在不释放总线的情况下改变数据传输方向。

三、代码示例(MCU 通过 I2C 读取传感器数据)

以下示例展示了一个完整的 I2C 读取流程:
起始 → 发送从机地址 + 写 → 发送寄存器地址 → 重复起始 → 发送从机地址 + 读 → 读取一个字节 → NACK → 停止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 假设已经实现了底层 I2C 硬件操作函数:
// void I2C_Start(void);
// void I2C_Stop(void);
// void I2C_SendByte(uint8_t byte);
// uint8_t I2C_ReadByte(void);
// void I2C_WaitAck(void); // 等待从机 ACK(主机模式)
// void I2C_SendAck(void); // 主机发送 ACK(读取数据时)
// void I2C_SendNack(void); // 主机发送 NACK(读取最后一个字节时)

#define SENSOR_ADDR 0x40 // 7位从机地址
#define REG_TEMP 0x00 // 温度寄存器地址

uint8_t i2c_read_sensor_temp(void)
{
uint8_t temp;

// 1. 起始条件
I2C_Start();

// 2. 发送从机地址 + 写命令 (R/W=0)
I2C_SendByte((SENSOR_ADDR << 1) | 0);
I2C_WaitAck(); // 等待从机 ACK

// 3. 发送寄存器地址
I2C_SendByte(REG_TEMP);
I2C_WaitAck(); // 等待从机 ACK

// 4. 重复起始条件
I2C_Start();

// 5. 发送从机地址 + 读命令 (R/W=1)
I2C_SendByte((SENSOR_ADDR << 1) | 1);
I2C_WaitAck(); // 等待从机 ACK

// 6. 读取一个字节数据
temp = I2C_ReadByte();

// 7. 主机发送 NACK 表示不再读取更多数据
I2C_SendNack();

// 8. 停止条件
I2C_Stop();

return temp;
}

注意

  • 实际代码中,I2C_WaitAck() 应包含超时处理,防止总线卡死。
  • 7 位从机地址在发送时需左移 1 位,最低位填充 R/W 位。
  • 读取多个字节时,除了最后一个字节外,主机都应发送 ACK;最后一个字节后发送 NACK。

四、应用场景与注意事项

  • 典型应用

    • 温湿度传感器(SHT30、AHT20)
    • 气压传感器(BMP280)
    • 小容量 EEPROM(24LC02)
    • OLED 显示屏(SSD1306)
    • 实时时钟(DS1307、DS3231)
  • 常见坑点

    1. 忘记添加上拉电阻 → SDA/SCL 无法被拉高,通信失败。
    2. 地址错误(未左移或地址与实际不符) → 从机不应答。
    3. 读取最后一个字节后未发送 NACK 直接 STOP → 从机可能尝试发送下一字节,导致总线冲突。
    4. 时钟速度过高或电容负载过大 → 上升沿变缓,需降低速率或加强上拉。
  • 调试工具

    • 逻辑分析仪(如 Saleae、梦源)抓取波形,直观查看起始、停止、ACK/NACK。
    • I2C 总线扫描程序,快速检测挂载的从机地址。

SPI:高速全双工的串行先锋

SPI(Serial Peripheral Interface,串行外设接口)是一种四线制全双工同步串行通信协议。它包含四根信号线:SCLK(串行时钟,由主设备产生)、MOSI(主设备输出,从设备输入)、MISO(主设备输入,从设备输出)以及 SS/CS(从设备选择信号,通常低电平有效)。总线采用主从模式,一个主设备可连接多个从设备,主设备通过拉低对应从设备的片选线来选中目标设备。通信总是由主设备主动发起并产生时钟,数据在时钟边沿逐位传输,支持全双工(发送和接收同时进行)。SPI 没有应答机制,因此传输速度快但缺乏错误检测。

SPI

一、理论知识总结

项目 说明
物理层 四线制:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、SS/CS(片选,低电平有效);可选扩展线:WP、HOLD、INT
片选信号 主设备通过拉低目标从设备的 SS 线选中该从设备,任何时刻只有一个从设备被选中
时钟极性与相位 由 CPOL 和 CPHA 组合决定 4 种模式(Mode 0~3),适应不同设备要求
数据传输 每个时钟周期传输 1 位,数据帧通常为 8/16/32 位,支持 MSB 先传或 LSB 先传
全双工 主设备发送数据的同时,从设备也在返回数据;读操作时主设备需发送哑字节以产生时钟(哑字节:无意义字节,一般0x00或者0xFF)
无应答机制 发送方无法确认接收方是否正确接收数据,没有 ACK/NACK 信号
速率 可达几十 MHz 至上百 MHz,远高于 I2C
优点 高速、全双工、协议简单、扩展性好(多从机通过片选)
缺点 引脚较多(至少 4 根)、无错误检测、无应答、通信距离短(板级)
典型应用 Flash 存储器(W25Q128)、传感器(加速度计/陀螺仪)、TFT/OLED 显示屏、电机驱动器

二、SPI 通信协议流程

2.1 标准写操作流程(主设备向从设备发送数据)
sequenceDiagram
    participant M as 主设备 (Master)
    participant S as 从设备 (Slave)

    M->>S: 拉低 SS (选中从机)
    loop 每个字节
        M->>S: SCLK 时钟脉冲 + MOSI 发送数据位
        Note over M,S: 全双工:MISO 同时接收从机返回的位(通常忽略)
    end
    M->>S: 拉高 SS (释放从机)
2.2 标准读操作流程(主设备从从设备读取数据)
sequenceDiagram
    participant M as 主设备 (Master)
    participant S as 从设备 (Slave)

    M->>S: 拉低 SS (选中从机)
    loop 每个字节
        M->>S: SCLK 时钟脉冲 + MOSI 发送哑字节 (0xFF 或 0x00)
        S-->>M: MISO 返回数据位
    end
    M->>S: 拉高 SS (释放从机)
2.3 复合操作流程(发送命令+地址,然后读取数据,如读 Flash ID)
sequenceDiagram
    participant M as 主设备 (MCU)
    participant S as 从设备 (Flash)

    M->>S: 拉低 SS (选中 Flash)
    M->>S: 发送读 ID 命令 (0x90)
    M->>S: 发送 24 位地址 (0x000000)
    M->>S: 发送哑字节,同时接收返回的 ID
    Note over M,S: 主设备通过 MOSI 发哑字节提供时钟
从设备通过 MISO 返回数据 M->>S: 拉高 SS (结束通信)

说明

  • SPI 的所有读操作都隐含了“主设备发送哑字节以产生时钟”的过程。
  • 片选 SS 在整个传输过程中应保持低电平,传输结束后拉高。
  • 不同从设备可能要求不同的时钟模式(CPOL/CPHA)和位序(MSB/LSB),需根据数据手册配置。

三、代码示例(MCU 通过 SPI 读取 Flash 芯片 ID)

以下示例基于 HAL 库,展示了使用 SPI 读取 Flash(如 W25Q128)芯片 ID 的完整流程:
拉低片选 → 发送读 ID 命令 → 发送 24 位地址 → 发送哑字节并接收返回的 ID → 拉高片选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 假设 SPI 外设已初始化,并定义了片选引脚
// 底层发送/接收函数:
// void SPI_SendByte(uint8_t byte); // 发送一个字节,同时接收一个字节(通过全局变量或返回值)
// uint8_t SPI_ReceiveByte(void); // 发送哑字节并接收返回数据
// 或者统一函数:uint8_t SPI_Transceive(uint8_t tx_data);

#define CS_PIN GPIO_PIN_4
#define CS_PORT GPIOB

void spi_read_flash_id(void)
{
uint8_t cmd = 0x90; // 读取 JEDEC ID 命令(部分 Flash 用 0x9F 直接读,此处按用户示例)
uint8_t addr[3] = {0x00, 0x00, 0x00}; // 24 位地址
uint8_t id = 0;

// 1. 片选拉低,选中 Flash 设备
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET);

// 2. 发送读 ID 命令
SPI_SendByte(cmd);

// 3. 发送 24 位地址(有些 Flash 读 ID 不需要地址,此处保留用户逻辑)
SPI_SendByte(addr[0]);
SPI_SendByte(addr[1]);
SPI_SendByte(addr[2]);

// 4. 读取数据:发送哑字节,同时接收从设备返回的 ID
id = SPI_ReceiveByte(); // 内部会发送 0xFF 并接收 MISO 数据

// 5. 片选拉高,结束通信
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);

// 此时 id 即为读取到的 Flash ID
}

注意

  • 实际 Flash 芯片(如 W25Q128)的读 ID 命令常为 0x9F0x90,地址可能不需要或为 0。请以具体数据手册为准。
  • SPI_ReceiveByte() 内部通常实现为:发送哑字节(如 0xFF)的同时读取 MISO 引脚。
  • 不同 SPI 设备的时钟极性和相位配置不同,例如 W25Q128 支持 Mode 0 和 Mode 3。
  • 若使用硬件 SPI,需正确配置 CPOL、CPHA、数据位宽、位序等参数。

四、应用场景与注意事项

  • 典型应用

    • 存储设备:NOR Flash(W25Q128)、EEPROM(25LCxxx)
    • 传感器:加速度计(ADXL345)、陀螺仪(MPU6050,使用 SPI 模式)
    • 显示器:TFT 彩屏(ILI9341)、OLED(部分支持 SPI)
    • 工业控制:电机驱动器、数据采集模块、ADC(如 MCP3208)
  • 常见坑点

    1. 片选管理:必须在整个传输过程中保持低电平,传输完成后及时拉高,否则从机可能提前释放 MISO。
    2. 时钟极性/相位错误:导致数据在错误的边沿采样,表现为读回数据全错或乱码。
    3. 忽略全双工特性:读操作时必须发送哑字节以产生时钟,否则无法接收数据。
    4. 多从机时的引脚冲突:每个从设备需要独立的 SS 引脚,若从设备 MISO 不输出高阻态,需加外部逻辑或使用片选使能三态。
    5. 速率过高导致信号失真:长导线或高负载下需降低 SPI 时钟频率。
  • 调试工具

    • 逻辑分析仪抓取 MOSI、MISO、SCLK、SS 波形,检查时序和片选极性。
    • 使用简单的回环测试(MOSI 短接 MISO)验证 SPI 主机发送接收功能。

UART:灵活异步的串行纽带

UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)是一种异步串行通信协议,仅需两根数据线:TX(发送线)和 RX(接收线),外加一根共用地线 GND。与 SPI/I²C 不同,UART 没有独立的时钟线,通信双方必须事先约定相同的波特率(如 9600、115200 bps)来同步每一位数据。数据以“帧”为单位传输,每帧包含起始位、数据位、校验位(可选)和停止位。空闲时 TX/RX 线保持高电平,发送开始时会拉低一个位时间作为起始信号。UART 支持全双工通信(可同时收发),但属于点对点协议,一个接口只能连接一个设备。

UART

一、理论知识总结

项目 说明
物理层 三线制:TX(发送)、RX(接收)、GND(地);无时钟线,异步传输
波特率 vs 比特率 波特率(Baud)是每秒信号变化次数;当每个符号对应 1 bit 时,波特率数值等于比特率(bps)。常用范围:300 ~ 921600 bps,典型值:9600、115200
帧格式 起始位(1 位低电平)+ 数据位(5~8 位,通常 LSB 先发)+ 校验位(可选)+ 停止位(1、1.5 或 2 位高电平)
空闲电平 高电平(逻辑 1)
起始位 拉低电平(逻辑 0),持续 1 个位时间,通知接收方开始接收
数据位 低位(LSB)在前,高位(MSB)在后,常用 8 位
校验位 可选:奇校验(数据位+校验位中 1 的个数为奇数)、偶校验(偶数)、无校验
停止位 高电平,持续 1/1.5/2 个位时间,表示一帧结束
通信方式 点对点(一个 TX 只能接一个 RX),全双工
优点 实现简单、几乎全 MCU 支持、调试方便(printf 打印)、全双工
缺点 点对点(多设备需多接口)、依赖波特率同步(不匹配则错)、速度较慢
典型应用 MCU 与 PC 调试(USB 转 TTL)、GPS/蓝牙/WiFi 模块、单片机间短距离通信

二、UART 通信协议流程

2.1 单帧数据传输流程(主机发送一个字节)
sequenceDiagram
    participant T as 发送端 (TX)
    participant R as 接收端 (RX)

    Note over T,R: 空闲时 TX 为高电平
    T->>R: 起始位 (拉低,1位)
    T->>R: 数据位 D0 (LSB)
    T->>R: 数据位 D1
    T->>R: 数据位 D2~D6
    T->>R: 数据位 D7 (MSB)
    opt 启用校验
        T->>R: 校验位 (奇/偶)
    end
    T->>R: 停止位 (拉高,1/1.5/2位)
    Note over T,R: 帧结束,可立即发送下一帧起始位
2.2 连续多字节发送流程(发送字符串)
sequenceDiagram
    participant M as 主设备 (MCU, TX)
    participant S as 从设备 (RX)

    loop 每个字节
        M->>S: 起始位 (低)
        M->>S: 8位数据 (LSB first)
        opt 校验位
            M->>S: 校验位
        end
        M->>S: 停止位 (高)
    end
    Note over M,S: 前一帧停止位结束后可立即开始下一帧起始位
2.3 接收数据流程(接收端采样)
sequenceDiagram
    participant S as 发送端
    participant R as 接收端

    R->>R: 检测 RX 下降沿 (起始位)
    R->>R: 等待半个位时间,再按波特率采样每个位
    loop 数据位 (8次)
        R->>R: 采样 RX 电平,存入数据寄存器
    end
    opt 校验位
        R->>R: 采样校验位,对比计算结果
    end
    R->>R: 采样停止位 (应为高电平)
    Note over R: 一帧接收完成,数据可用

说明

  • 接收端通过检测起始位的下降沿来同步帧起始,之后按波特率周期采样。
  • 停止位至少需要 1 位时间的高电平,用于帧间隔。
  • 如果校验失败或停止位错误,通常置位错误标志。

三、代码示例(MCU 通过 UART 发送与接收数据)

以下示例基于寄存器操作(STM32 风格),展示发送单个字节、发送字符串、接收单个字节并回显的基本流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 假设 UART 外设已初始化,波特率、数据位、停止位等已配置
// 寄存器定义示例(STM32 USART1):
// #define UART ((USART_TypeDef *)USART1_BASE)
// UART->SR 状态寄存器,bit7 (TXE) 发送空,bit5 (RXNE) 接收非空

// 发送单个字符(阻塞)
void UART_SendByte(uint8_t ch)
{
while (!(UART->SR & 0x80)); // 等待发送数据寄存器空
UART->DR = ch; // 发送数据
}

// 发送字符串
void UART_SendString(const char *str)
{
while (*str)
{
UART_SendByte(*str++);
}
}

// 接收单个字符(阻塞)
uint8_t UART_ReceiveByte(void)
{
while (!(UART->SR & 0x20)); // 等待接收数据寄存器非空
return UART->DR; // 读取数据
}

// 主程序示例:发送字符串,然后接收一个字节并回显
void main(void)
{
UART_SendString("Hello UART\r\n"); // 发送字符串(带换行回车)
uint8_t data = UART_ReceiveByte(); // 等待并接收一个字节
UART_SendByte(data); // 将该字节回显出去
}

注意

  • 实际使用中,通常会使用中断或 DMA 方式收发,避免阻塞。
  • \r\n 是回车换行,使串口调试助手能正确换行显示。
  • 波特率不匹配会导致接收数据乱码,需确保双方一致(误差一般不超过 2%)。
  • 如果使用 printf 重定向,需实现 fputc 函数并调用 UART_SendByte

四、应用场景与注意事项

  • 典型应用

    • 调试输出:MCU 通过 USB 转 TTL 模块连接 PC,使用串口调试助手查看 printf 日志。
    • 无线模块:蓝牙(HC-05/HC-06)、WiFi(ESP8266)、GPS(NMEA 协议)等模块通常使用 UART 通信。
    • 单片机间通信:两个 MCU 的 TX/RX 交叉连接,实现短距离数据交换。
    • 智能小车/遥控:手机通过蓝牙串口模块发送控制指令给小车单片机。
  • 常见坑点

    1. 波特率不匹配:双方波特率必须相同,误差过大会导致数据错位或乱码。
    2. 电平不匹配:MCU 通常是 TTL 电平(0~3.3V/5V),PC 串口是 RS232 电平(±12V),直接连接会烧毁芯片,必须使用 MAX232 或 USB 转 TTL 模块。
    3. TX/RX 接反:发送端 TX 应接接收端 RX,交叉连接。
    4. 帧格式不一致:数据位、停止位、校验位需双方相同。
    5. 缺少共地:GND 必须相连,否则参考电位不同导致通信失败。
    6. 阻塞式接收:示例中的轮询方式会卡死程序,实际应使用中断或超时机制。
  • 调试工具

    • 串口调试助手(Windows:SSCOM、XCOM;Linux:minicom、cutecom)
    • USB 转 TTL 模块(CH340、CP2102、FT232)
    • 逻辑分析仪(抓取 TX/RX 波形,计算实际波特率)

总线通信协议(4 种)

CAN:工业与汽车领域的抗干扰强者

CAN(Controller Area Network,控制器局域网)是一种多主、差分信号、高可靠性的串行通信协议,由德国博世公司开发,广泛应用于汽车电子和工业自动化领域。它采用两根差分信号线 CAN_HCAN_L 来传输数据,通过双绞线和终端电阻(120Ω)增强抗干扰能力。CAN 总线上每个节点都可以主动发送数据,通过非破坏性总线仲裁机制(标识符优先级决定)自动解决冲突。协议内置强大的错误检测和处理能力(位错误、CRC 错误等),并定义了错误计数器及三种错误状态(主动错误、被动错误、总线关闭)。CAN 支持 1Mbps 速率(40m 内)和最长 10km 传输距离(低速下),节点数可达 110 个。

CAN

一、理论知识总结

项目 说明
物理层 差分信号:CAN_H 和 CAN_L;隐性(逻辑 1)时 CAN_H = CAN_L ≈ 2.5V;显性(逻辑 0)时 CAN_H ≈ 3.5V,CAN_L ≈ 1.5V,压差 2V-3V
终端电阻 总线两端需各接 120Ω 电阻,匹配阻抗,抑制信号反射
帧类型 数据帧、远程帧(请求数据)、错误帧、过载帧;数据帧最常用
数据帧结构 7 个部分:帧起始(SOF,1)、仲裁场(11/29位标识符+ RTR)、控制场(6位)、数据场(0-8字节)、CRC场(15位+界定符)、应答场(2位)、帧结束(EOF,7位)
总线仲裁 非破坏性:标识符数值越小,优先级越高;发送时逐位比较,显性(0)覆盖隐性(1),低优先级自动退出
错误处理 5 种错误检测(位、填充、CRC、格式、确认);节点包含发送/接收错误计数器,状态转换:主动错误 → 被动错误 → 总线关闭
传输速率与距离 最高 1Mbps(40m),速率越低距离越长,最长约 10km(5kbps 左右);标准 ISO 11898
节点数 最多 110 个(取决于收发器驱动能力)
优点 多主、非破坏性仲裁、高抗干扰(差分)、强大的错误处理、远距离、可连接多个节点
缺点 协议较复杂(需要控制器/收发器)、每帧有效负载小(最多 8 字节)、成本高于 I2C/SPI
典型应用 汽车电子(ECU、ABS、ESP、车身控制)、工业自动化(PLC、传感器、执行器)、医疗设备、航空航天

二、CAN 通信协议流程

2.1 标准数据帧发送流程(节点发送一帧数据)
sequenceDiagram
    participant NodeA as 节点A (发送)
    participant Bus as CAN 总线
    participant NodeB as 节点B (接收)

    Note over NodeA,Bus: 总线空闲时呈隐性(高电平)
    NodeA->>Bus: 帧起始 (SOF) — 显性
    NodeA->>Bus: 仲裁场 (标识符 + RTR)
    Bus-->>NodeA: 仲裁过程:若其他节点同时发送,ID值小的继续,大的退出
    NodeA->>Bus: 控制场 (IDE, R0, DLC)
    NodeA->>Bus: 数据场 (0~8 字节)
    NodeA->>Bus: CRC 场 (15位 + 界定符)
    NodeB-->>NodeA: 应答场 (ACK SLOT) — 接收节点拉显性表示正确接收
    NodeA->>Bus: 帧结束 (EOF) — 7位隐性
    Note over Bus: 总线恢复空闲
2.2 接收节点处理流程(接收一帧数据)
sequenceDiagram
    participant Bus as CAN 总线
    participant Node as 节点

    Bus->>Node: 检测到 SOF (显性)
    Node->>Node: 接收仲裁场,过滤器匹配标识符
    alt 标识符不匹配
        Node-->>Node: 停止接收,等待下一帧
    else 标识符匹配
        Node->>Node: 接收控制场、数据场、CRC场
        Node->>Node: 计算 CRC 并与接收值比较
        Node->>Node: 在应答槽位发送显性位 (ACK)
        Node->>Node: 接收帧结束,数据存入缓冲区
    end
2.3 仲裁过程示意图(两个节点同时发送)
sequenceDiagram
    participant Node1 as 节点1 (ID=0x123)
    participant Node2 as 节点2 (ID=0x321)
    participant Bus as 总线

    Note over Node1,Bus: 同时发送 SOF
    Node1->>Bus: 发送 ID 位: 0 0 1 0 0 1 0 0 0 1 1
    Node2->>Bus: 发送 ID 位: 0 1 1 0 0 1 0 0 0 1 1
    Note over Bus: 第2位: 节点1发0(显性),节点2发1(隐性) → 总线为0
    Node2-->>Node2: 检测到总线与自己发送不同 → 失去仲裁,立即转为接收
    Node1-->>Node1: 继续发送剩余位,赢得总线使用权
    Node1->>Bus: 发送后续控制场、数据场...
    Note over Node2: 节点2稍后重新尝试发送

说明

  • 仲裁场中的 RTR 位(远程帧请求):数据帧为显性(0),远程帧为隐性(1),因此数据帧优先级高于远程帧。
  • 应答场:由接收节点在应答槽(ACK Slot)期间发送显性位表示正确接收;发送节点检测此显性位确认至少有一个节点收到。
  • 错误计数器:发送失败或接收错误时计数器增减,根据阈值进入不同错误状态。

三、代码示例(MCU 通过 CAN 发送标准数据帧)

以下示例基于 STM32 HAL 库,展示配置发送头并发送 4 字节数据到 ID 为 0x123 的节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 假设 CAN 外设已初始化(波特率、过滤器等已配置)
// CAN 发送头结构体配置

CAN_TxHeaderTypeDef tx_header;
uint8_t send_buf[4] = {0x11, 0x22, 0x33, 0x44};
uint32_t tx_mailbox;

// 1. 配置发送帧头
tx_header.StdId = 0x123; // 标准标识符 (11位)
tx_header.IDE = CAN_ID_STD; // 标准帧(扩展帧用 CAN_ID_EXT)
tx_header.RTR = CAN_RTR_DATA; // 数据帧(远程帧用 CAN_RTR_REMOTE)
tx_header.DLC = 4; // 数据长度码 (0~8)
// 可选:时间戳、发送类型等,通常默认

// 2. 调用 HAL 库发送函数
if (HAL_CAN_AddTxMessage(&hcan1, &tx_header, send_buf, &tx_mailbox) == HAL_OK)
{
// 发送成功,tx_mailbox 返回使用的邮箱编号 (0,1,2)
// 可通过 HAL_CAN_GetTxMailboxesFreeLevel 检查空闲邮箱
}
else
{
// 发送失败(总线错误、邮箱满等)
}

// 接收处理(通常使用中断或回调)
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
CAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[8];
if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK)
{
// 根据 rx_header.StdId 区分节点,处理 rx_data
}
}

注意

  • 使用前需调用 HAL_CAN_Start(&hcan1) 启动 CAN 外设,并配置过滤器(HAL_CAN_ConfigFilter),否则无法接收。
  • 发送时若总线忙,硬件会自动重试;无需软件重发。
  • 标准帧 ID 范围 00x7FF(11位),扩展帧 ID 范围 00x1FFFFFFF(29位)。
  • 发送数据长度 DLC 必须与实际数据数组长度一致,但驱动不会检查越界。

四、应用场景与注意事项

  • 典型应用

    • 汽车电子:发动机 ECU、ABS、ESP、仪表盘、车窗/车门控制模块等
    • 工业自动化:PLC 与分布式 I/O、伺服驱动器、传感器总线
    • 医疗设备:X光机、病床控制等
    • 航空航天:飞行数据采集、机舱控制
  • 常见坑点

    1. 终端电阻缺失或位置不对:总线两端必须各有一个 120Ω 电阻,否则信号反射导致通信故障。
    2. 波特率不一致:所有节点必须配置相同的位时序(同步段、传播段、相位段等),否则位采样错误。
    3. 标识符优先级规划:高优先级(小 ID)帧必须确保不会长期占用总线,导致低优先级帧饿死。
    4. 错误计数器卡死:当总线严重错误时,节点可能进入总线关闭状态,需要软件恢复或重新初始化。
    5. 过滤器配置错误:所有期望接收的 ID 必须通过验收过滤器,否则硬件会丢弃。
    6. 数据场最大 8 字节:无法一次传输大数据块,需拆包或多帧协议(如 CANopen、J1939)。
  • 调试工具

    • USB-CAN 分析仪(如 PCAN、ZLG CAN、CANable)
    • 示波器/逻辑分析仪(观察 CAN_H 和 CAN_L 差分波形,检查显隐性压差)
    • CAN 总线监视软件(PCAN-View、CANtact)查看总线负载和错误帧

LIN:低成本低速的局部小能手

LIN(Local Interconnect Network,本地互联网络)是一种基于 UART/SCI 的低成本单线串行通信协议,主要面向汽车电子中对速率和成本敏感的辅助子系统。LIN 总线采用单主多从结构,一个主节点最多可连接 16 个从节点,所有通信均由主节点发起(主节点发送帧头,从节点被动响应)。总线电平通常为 12V,最高传输速率 20kbps(受限于 EMC 要求)。LIN 协议简单,硬件成本低(比 CAN 降低 40%~60%),适合车窗、座椅、灯光、雨刷等低速控制场景。

LIN

一、理论知识总结

项目 说明
物理层 单线传输(LIN Bus),对地参考,通常 12V 电平;需上拉电阻(1kΩ)和总线电容(~220pF)
拓扑结构 单主多从,一个主节点 + 最多 16 个从节点
传输速率 最高 20kbps(典型值 19.2kbps),受 EMC 限制,传播时间可预测
帧结构 主机发送的帧头 + 从机发送的应答组成
帧头 同步间隔段(至少 13 位显性电平)+ 同步段(0x55)+ 受保护 ID 段(PID)
同步间隔 至少 13 位显性电平,标志帧开始;从机通过此间隔检测起始
同步段 固定字节 0x55(二进制 01010101b),从机以此校准波特率(允许使用精度较低的内部振荡器)
PID 段 6 位帧 ID(0~63)+ 2 位奇偶校验 = 8 位受保护 ID;用于标识帧类型和目的地
应答 由从机(发布节点)发送:数据段(1~8 字节)+ 校验和段
数据段 1~8 字节,携带信号或诊断消息;低字节先发(LSB)
校验和段 8 位校验和:传统校验和(仅数据)或增强校验和(PID + 数据),由主机指定
通信方式 主节点发送帧头 → 从节点根据 PID 决定接收、发送应答或忽略
优点 成本极低(单线、普通 UART 实现)、EMC 可控、确定性传输(时间可算)、协议简单
缺点 速率低(≤20kbps)、节点数少(≤16)、主从结构(从机不能主动发送)
典型应用 车窗升降、座椅调节、后视镜折叠、灯光控制、雨刷、天窗、空调面板等车身辅助功能

二、LIN 通信协议流程

2.1 完整的报文帧传输流程(主机查询从机数据)
sequenceDiagram
    participant Master as 主节点
    participant Slave as 从节点

    Master->>Slave: 同步间隔段 (≥13位显性)
    Master->>Slave: 同步段 (0x55)
    Master->>Slave: 受保护 ID (PID)
    Note over Slave: 解析 PID,判断自身是
发布者(发送数据)还是收听者(接收数据) alt 从节点是该帧的发布者 Slave->>Master: 应答:数据段 (1~8字节) Slave->>Master: 应答:校验和段 else 从节点是收听者 Slave-->>Slave: 接收数据(不应答) end Master->>Master: 可接收数据或忽略
2.2 主机发送帧头(无数据应答,如诊断命令)
sequenceDiagram
    participant Master as 主节点
    participant Slaves as 所有从节点

    Master->>Slaves: 同步间隔段 (13位显性)
    Master->>Slaves: 同步段 (0x55)
    Master->>Slaves: PID (例如诊断帧)
    Note over Slaves: 根据PID,部分从节点会
发送应答,其余忽略 Slaves->>Master: 应答数据 (若有)
2.3 从节点发送应答的细节(基于 UART 实现)
graph TD
    A[从节点检测到同步间隔] --> B[接收同步段0x55,校准波特率]
    B --> C[接收PID]
    C --> D{检查PID是否匹配
自己是否为发布者?} D -->|是| E[从节点发送数据段 1-8字节] E --> F[发送校验和] F --> G[结束帧] D -->|否| H[忽略后续应答部分(或不处理)] H --> G

说明

  • 同步间隔段 必须至少 13 位显性电平(逻辑 0),这是 LIN 与普通 UART 数据帧的明显区别。
  • 从节点通过检测同步间隔来帧同步,然后利用同步段 0x55 的上升/下降沿校准波特率,允许从节点使用低成本 RC 振荡器。
  • PID 段 中的奇偶校验公式:P0 = ID0 ⊕ ID1 ⊕ ID2 ⊕ ID4;P1 = ¬(ID1 ⊕ ID3 ⊕ ID4 ⊕ ID5)。
  • 校验和:传统模式仅对数据段求和取反;增强模式对 PID 和数据段求和取反(LIN 2.0 及以上常用增强型)。

三、代码示例(MCU 作为 LIN 主机发送帧头,查询从机数据)

以下示例基于 UART 模拟 LIN 主机发送帧头,然后切换到接收模式读取从机应答(假设从机 ID=0x02)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 假设 UART 已初始化(波特率 19200,8N1)
// 底层函数:
// void UART_SendByte(uint8_t data);
// uint8_t UART_ReceiveByte(void);
// void UART_SendBreak(void); // 发送至少13位显性电平(通过特殊寄存器或手动拉低TX)

#define LIN_PID(id, parity) (((id) & 0x3F) | ((parity) << 6))

// 计算 PID 的奇偶校验位 (P0, P1)
uint8_t LIN_ComputePID(uint8_t id) // id 范围 0~63
{
uint8_t p0 = ((id >> 0) ^ (id >> 1) ^ (id >> 2) ^ (id >> 4)) & 0x01;
uint8_t p1 = ~((id >> 1) ^ (id >> 3) ^ (id >> 4) ^ (id >> 5)) & 0x01;
return (id & 0x3F) | (p0 << 6) | (p1 << 7);
}

// LIN 主机发送帧头 (带超时等待从机应答)
uint8_t LIN_MasterRequest(uint8_t id, uint8_t *rx_data, uint8_t data_len)
{
uint8_t pid = LIN_ComputePID(id);
uint8_t checksum = 0;
uint8_t i;

// 1. 发送同步间隔 (至少13位显性)
UART_SendBreak(); // 实现方式:设置UART发送间隔段模式或手动拉低TX 13位时间

// 2. 发送同步段 0x55
UART_SendByte(0x55);

// 3. 发送 PID
UART_SendByte(pid);

// 4. 切换方向:等待从机应答 (假设从机发送 data_len 字节 + 校验和)
// 注意:可能需要延迟一段时间等待从机处理
for (i = 0; i < data_len; i++)
{
rx_data[i] = UART_ReceiveByte(); // 阻塞接收
checksum += rx_data[i];
}
uint8_t rx_checksum = UART_ReceiveByte();

// 5. 校验(增强型校验需包含 PID)
// 这里简单示例使用传统校验和(仅数据)
if ((checksum + rx_checksum) != 0xFF)
return 1; // 校验失败

return 0;
}

// 使用示例:主机查询 ID=2 的从机,读取4字节数据
void main(void)
{
uint8_t data[4];
if (LIN_MasterRequest(0x02, data, 4) == 0)
{
// 成功获取数据,处理 data[0]~data[3]
}
}

注意

  • 同步间隔的生成:大多数 MCU 的 UART 可配置发送间隔段(Break),直接调用 UART_SendBreak() 即可。若无硬件支持,可手动将 TX 引脚拉低超过 13 个位时间。
  • 接收超时:总线空闲时 RX 为高电平,如果从机未应答,UART_ReceiveByte 会一直等待,需添加超时机制。
  • 从机实现:从机需检测同步间隔(通过边沿计时),然后接收 0x55 校准波特率,再接收 PID 并与本地 ID 匹配,最后决定发送数据还是接收数据。
  • 波特率精度:从机内部 RC 振荡器误差容忍度可达 ±14% 以上(因同步段校准)。

四、应用场景与注意事项

  • 典型应用

    • 汽车电子:车窗防夹模块、座椅位置记忆、后视镜折叠、车内氛围灯、雨量传感器、天窗控制
    • 小家电:智能空调面板与主控板通信、洗衣机显示面板
    • 工业:低成本传感器网络、执行器节点
  • 常见坑点

    1. 同步间隔长度不足:必须 > 13 位显性电平,否则从机可能无法识别帧开始。
    2. 从机地址冲突:每个从节点必须分配不同的帧 ID(0~63),且需设置好发布/收听关系。
    3. 波特率不匹配:虽然从机有同步段校准,但主机和从机的基础时钟误差应在可接受范围(通常 < 2%)。
    4. 校验和类型混淆:LIN 1.x 使用传统校验和(仅数据),LIN 2.x 使用增强校验和(包含 PID)。需根据从机规格配置。
    5. 电平转换:大多数 MCU 的 UART 是 3.3V/5V TTL 电平,而 LIN 总线是 12V,需要使用 LIN 收发器(如 TJA1020、MCP2003)转换。
    6. 总线负载电容:节点过多或线缆太长可能导致信号上升沿变缓,需控制总线电容 < 10nF。
  • 调试工具

    • USB 转 LIN 适配器(如 PLIN-USB、Baby-LIN)
    • 带 LIN 解码功能的示波器/逻辑分析仪
    • 串口助手(配合 LIN 收发器,使用 19200 波特率观察原始数据,但需人工解析帧结构)

RS485:远距离组网的可靠伙伴

RS485 是一种差分信号、半双工、多点通信的串行物理层标准,广泛应用于工业自动化、楼宇控制、安防监控等远距离、高抗干扰场景。它采用一对差分信号线 AB,通过电压差(A > B 时为逻辑 1,A < B 时为逻辑 0)传输数据。RS485 支持长达 1200m 的通信距离(低速下),速率最高可达 10Mbps(短距离),一条总线上最多可连接 32 个设备(标准负载,可通过中继器扩展)。与 RS232 相比,RS485 电平更低、不易损坏接口芯片,且与 TTL 电平兼容,方便与 MCU 的 UART 直接连接。由于是半双工,同一时刻只能有一个设备发送数据,需要由软件控制发送/接收方向切换。

RS485

一、理论知识总结

项目 说明
物理层 差分信号:A、B 两根线;逻辑 1:A > B 且压差 2V6V;逻辑 0:A < B 且压差 2V6V
传输方式 半双工(同一时刻数据只能单向传输)
通信距离 最长 1200m(速率越低距离越远);典型 9600bps 下可达千米
传输速率 最高 10Mbps(短距离,如 12m 内),随距离增加速率下降
节点数量 标准最多 32 个(单位负载),可接中继器或低阻抗驱动器扩展至数百个
接口电平 差分信号幅度约 ±2V~±6V,与 TTL 电平兼容(需收发器转换)
常用协议组合 RS485 仅定义物理层,常与 UART(异步串行)配合,上层可使用 Modbus、Profibus 等
终端电阻 长距离或多节点时,总线两端应各接 120Ω 电阻,防止信号反射
方向控制 半双工需要 GPIO 控制收发器(如 MAX485)的 DE(发送使能,DE=1)和 RE(接收使能,RE=0)引脚
优点 抗干扰强(差分信号)、远距离、高速、多点通信、成本低
缺点 半双工(需方向切换)、布线比 RS232 复杂(需双绞线)、终端电阻匹配要求
典型应用 工业 PLC 联网、智能电表、楼宇自动化(门禁/照明/空调)、安防监控(云台控制)、变频器通信

二、RS485 通信协议流程

2.1 半双工传输流程(主机发送 → 从机应答)
sequenceDiagram
    participant M as 主机
    participant Bus as RS485 总线
    participant S as 从机

    Note over M: 切换为发送模式 (DE=1, RE=1)
    M->>Bus: 发送数据帧 (通过UART)
    Note over M: 发送完毕,切换为接收模式 (DE=0, RE=0)
    Note over S: 从机一直处于接收模式
    Bus-->>S: 数据帧
    S-->>S: 解析数据并准备应答
    Note over S: 切换为发送模式 (DE=1)
    S->>Bus: 发送应答帧
    Note over S: 发送完毕,切换回接收模式
    Bus-->>M: 应答帧
2.2 多点通信拓扑(一主多从)
graph LR
    Master[主机 带方向控制] --> Bus[RS485总线 A B双绞线]
    Bus --> Slave1[从机1 始终接收]
    Bus --> Slave2[从机2 始终接收]
    Bus --> SlaveN[从机N 始终接收]
    Master -.->|发送时主动驱动总线| Bus
    Slave1 -.->|仅在应答时驱动总线| Bus
    Slave2 -.->|仅在应答时驱动总线| Bus
    SlaveN -.->|仅在应答时驱动总线| Bus

说明

  • 总线上同一时刻只能有一个设备发送,所有其他设备必须处于接收状态。
  • 主机通常默认处于接收模式,发送前切换为发送模式,发送完立即切回接收。
  • 从机平时一直处于接收模式,只有在被寻址并需要应答时才短暂切为发送模式。
  • 终端电阻应接在总线最远端的两端,而不是每个节点都接。

三、代码示例(MCU 通过 RS485 半双工发送数据)

以下示例基于 STM32 HAL 库的 UART,配合 MAX485 等收发器的方向控制引脚(DE/RE 通常并联,高电平发送,低电平接收)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 假设 UART 已初始化(例如 9600, 8N1)
// 定义方向控制引脚
#define DE_RE_PORT GPIOA
#define DE_RE_PIN GPIO_PIN_1

// 切换为发送模式
void RS485_SetTransmit(void)
{
HAL_GPIO_WritePin(DE_RE_PORT, DE_RE_PIN, GPIO_PIN_SET); // DE=1, RE=1 (发送模式)
}

// 切换为接收模式
void RS485_SetReceive(void)
{
HAL_GPIO_WritePin(DE_RE_PORT, DE_RE_PIN, GPIO_PIN_RESET); // DE=0, RE=0 (接收模式)
}

// 发送数据(阻塞方式)
void RS485_Send(uint8_t *buf, uint16_t len)
{
RS485_SetTransmit(); // 先切换为发送模式
for (uint16_t i = 0; i < len; i++)
{
while (!(UART->SR & 0x80)); // 等待发送数据寄存器空
UART->DR = buf[i]; // 发送一个字节
}
while (!(UART->SR & 0x40)); // 等待发送完成(TC标志)
RS485_SetReceive(); // 发送完毕,立即切回接收模式
}

// 接收数据(非阻塞示例,实际应用中常用中断)
uint8_t RS485_ReceiveByte(void)
{
while (!(UART->SR & 0x20)); // 等待接收数据寄存器非空
return UART->DR;
}

// 使用示例:主机发送命令并等待从机应答
void main(void)
{
uint8_t cmd[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x84, 0x0A}; // Modbus 读保持寄存器
uint8_t response[8];

RS485_Send(cmd, sizeof(cmd)); // 发送命令(自动切回接收模式)
// 延迟一段时间等待从机处理
for (int i = 0; i < 8; i++)
{
response[i] = RS485_ReceiveByte(); // 阻塞接收应答(实际应加超时)
}
// 处理应答数据...
}

注意

  • 发送完成后必须等待 UART 发送完成标志(TC),再切换为接收模式,否则最后一个字节可能丢失。
  • 实际产品中接收应采用中断或 DMA,并增加超时和错误处理。
  • 多从机系统中,每个从机必须有唯一地址,避免同时发送冲突。
  • 总线空闲时所有设备应处于接收模式(高阻态),否则会占用总线。

四、应用场景与注意事项

  • 典型应用

    • 工业自动化:PLC 与变频器、传感器、执行器之间的通信(常配合 Modbus RTU 协议)
    • 楼宇自控:门禁控制器、照明系统、空调末端联网
    • 安防监控:高速球机云台控制(Pelco-D 协议)、报警主机联动
    • 智能电表:集中器与电表之间的抄表总线
    • 数据采集:分布式数据采集模块(如研华 ADAM 模块)
  • 常见坑点

    1. 缺少终端电阻:长距离或多节点时,总线两端必须各接一个 120Ω 电阻,否则信号反射导致数据错误。
    2. 方向切换时机错误:发送完成但未等 TC 标志就切回接收,导致最后字节发送不完整。
    3. 多个节点同时发送:由于软件 bug 或逻辑错误,两个从机同时发送会短路总线(无仲裁机制),可能损坏收发器。
    4. 偏置电阻问题:总线空闲时 A/B 压差不确定,可能引起乱码;可加偏置电阻(如 560Ω 上拉 A,下拉 B)确保空闲时逻辑 1。
    5. 地线未连接:虽然 RS485 是差分信号,但收发器需共模电压参考,通常需要连接地线(GND)或使用隔离。
    6. 收发器死锁:某些情况下(如总线短路)收发器可能进入错误状态,需要硬件复位或重新上电。
  • 调试工具

    • USB 转 RS485 转换器(带方向自动切换功能,如宇泰 UT-890)
    • 串口调试助手 + Modbus 调试工具(如 ModScan)
    • 示波器/逻辑分析仪(差分探头或分别测量 A、B 对地波形)
    • RS485 总线分析仪(抓取总线数据并解析协议)

Modbus:工业设备的通用翻译官

Modbus 是一种工业自动化领域广泛使用的应用层通信协议,采用主从架构,主设备主动发起请求,从设备(地址 1~247)被动响应。它如同“通用翻译官”,使不同厂家的 PLC、传感器、驱动器等设备能够互联互通。Modbus 有多个变种,最常见的是 Modbus RTU(基于串行链路,如 RS-232/RS-485,二进制编码,需 CRC 校验)和 Modbus TCP(基于以太网,端口 502,利用 TCP 可靠校验,无 CRC)。协议核心是报文帧,包含从站地址、功能码、数据和错误校验。优点是简单、开放、低成本、互操作性好;缺点是无安全性、RTU 模式单主、数据模型简单。

MODBUS

一、理论知识总结

项目 说明
架构 主从式:一个主设备,多个从设备(地址 1~247,地址 0 为广播)
物理层 RTU:RS-232、RS-485 等串行链路;TCP:以太网(TCP/IP,端口 502)
编码方式 RTU:二进制,紧凑高效;TCP:嵌入 TCP 数据包,文本或二进制
帧定界 RTU:依赖时间间隔(至少 3.5 字符空闲);TCP:基于 TCP 连接和长度字段
错误校验 RTU:CRC-16-IBM(循环冗余校验);TCP:依赖 TCP 层的校验,不再需要 CRC
功能码(常用) 01 读线圈、02 读离散输入、03 读保持寄存器、04 读输入寄存器、05 写单线圈、06 写单寄存器、15 写多线圈、16 写多寄存器
数据模型 四种基本类型:线圈(DO)、离散输入(DI)、输入寄存器(AI)、保持寄存器(AO)
帧结构(RTU) 从站地址(1B) + 功能码(1B) + 数据(nB) + CRC(2B)
异常响应 功能码最高位置 1(如 0x83),后跟异常码(01 非法功能、02 非法地址、03 非法数据等)
优点 简单易开发、开放免费、部署成本低(尤其是 RS-485 + RTU)、设备互操作性好
缺点 无安全机制(无加密/认证)、RTU 模式单主(主设备可能成瓶颈)、数据模型简单、需预先知道数据映射
典型应用 工业过程控制(PLC/DCS/传感器)、楼宇自动化(HVAC/照明)、能源管理(电力监控/SCADA)

二、Modbus 通信协议流程

2.1 Modbus RTU 主站请求 – 从站响应(读保持寄存器)
sequenceDiagram
    participant M as 主站 (Master)
    participant S as 从站 (Slave)

    Note over M: 构建请求帧(地址+功能码+起始地址+数量+CRC)
    M->>S: 发送请求帧(至少间隔3.5字符空闲后)
    Note over S: 接收完整帧并校验CRC
    alt CRC正确
        S->>S: 执行功能码操作
        S-->>M: 响应帧(地址+功能码+字节数+数据+CRC)
    else CRC错误
        S-->>S: 丢弃帧,不响应
    end
2.2 Modbus RTU 请求帧结构(以读保持寄存器为例)
字段 值(示例) 说明
从站地址 0x01 目标从机地址
功能码 0x03 读保持寄存器
起始地址高字节 0x00
起始地址低字节 0x00 起始地址 = 0
寄存器数量高字节 0x00
寄存器数量低字节 0x02 读取 2 个寄存器
CRC 低字节 0x84
CRC 高字节 0x0B CRC = 0x0B84(低字节在前)

说明

  • RTU 帧之间需至少 3.5 个字符的静默时间作为帧定界。
  • CRC 校验采用多项式 0x8005,初始值 0xFFFF,结果低字节在前。
  • 对于 Modbus TCP,帧结构不同(无 CRC,增加 MBAP 报头),但功能码和数据部分类似。

三、代码示例(Modbus RTU 主站发送读保持寄存器请求)

以下示例使用 RS485 半双工发送一个标准的 Modbus RTU 请求:读取 1 号从站、起始地址 02 个保持寄存器(功能码 0x03)。CRC 已预计算(0x840B)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 假设 RS485_Send() 已实现(自动切换方向,发送完成后切回接收)
// Modbus RTU 请求帧数组
uint8_t modbus_req[] = {
0x01, // 从站地址
0x03, // 功能码: 读保持寄存器
0x00, // 起始地址高字节
0x00, // 起始地址低字节 (起始地址 0)
0x00, // 寄存器数量高字节
0x02, // 寄存器数量低字节 (读取 2 个寄存器)
0x84, // CRC 低字节
0x0B // CRC 高字节
};

// 发送请求
RS485_Send(modbus_req, sizeof(modbus_req));

// 之后切换到接收模式,等待从站响应(需实现超时)
// 响应帧格式:地址 + 功能码 + 字节数(2*N) + N个寄存器数据(高字节在前) + CRC

如果需要动态计算 CRC,可以使用以下函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdint.h>

uint16_t Modbus_CRC16(uint8_t *buf, uint16_t len)
{
uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < len; i++)
{
crc ^= buf[i];
for (uint8_t j = 0; j < 8; j++)
{
if (crc & 0x0001)
crc = (crc >> 1) ^ 0xA001;
else
crc >>= 1;
}
}
return crc; // 低字节在前
}

注意

  • 发送多字节数据时,CRC 需附加到帧末尾(低字节在前)。
  • 实际应用中应实现接收超时(如等待 200ms)和响应解析。
  • 对于 Modbus TCP,只需要去掉 CRC,并在帧前增加 7 字节 MBAP 报头,直接通过 socket 发送。

四、应用场景与注意事项

  • 典型应用

    • 工业过程控制:PLC 与变频器、智能仪表、伺服驱动器通信。
    • 楼宇自动化:HVAC 控制器、照明模块、门禁系统。
    • 能源管理:电力监控仪表、水处理 SCADA、光伏逆变器数据采集。
    • 远程终端:通过 RS-485 总线采集 RTU 现场数据。
  • 常见坑点

    1. RTU 帧间隔时间不满足:小于 3.5 字符时间会导致从站认为数据未完成,无响应。
    2. CRC 计算错误:字节顺序(高/低)或多项式不对,导致校验失败。
    3. 地址冲突:多个从站设为同一地址会导致总线冲突。
    4. 功能码不支持:请求的功能码超出从站能力,返回异常码 01。
    5. 寄存器地址越界:请求的起始地址+数量超出从站范围,返回异常码 02。
    6. RTU 模式下改变方向过快:主站发送完最后一个字节后立即切换为接收,可能丢失从站应答的第一个字节(需等待一个字节的延迟或发送完成中断)。
    7. Modbus TCP 防火墙阻塞:端口 502 需在防火墙中开放。
    8. 安全风险:生产环境应通过网关/VPN 隔离,避免直接暴露在公网。
  • 调试工具

    • Modbus 调试助手(ModScan、ModSim、QModMaster)
    • 串口监听工具(AccessPort、ComMonitor)配合 RS-485 转换器
    • Wireshark(分析 Modbus TCP 报文)
    • 逻辑分析仪/示波器(排查串行时序问题)

无线通信协议(5种)

蓝牙/BLE:短距离设备的无线桥梁

蓝牙(Bluetooth)是一种基于2.4GHz ISM频段的短距离无线通信技术,采用跳频扩频(FHSS) 技术实现抗干扰传输。蓝牙协议栈主要分为经典蓝牙(BR/EDR)低功耗蓝牙(BLE,Bluetooth Low Energy) 两大分支——BR/EDR 适用于音频流等较高带宽场景,BLE 从蓝牙 4.0 开始引入,专为电池供电的低功耗设备设计,功耗仅为经典蓝牙的 1%~10%,待机电流低至 0.01–0.5 mA,连接建立时间 ≤ 3 ms,典型通信距离 10–100 米。BLE 采用分层协议栈设计,通过广播(Advertising)和连接(Connection) 双模式通信——广播模式下,外围设备(Peripheral)在 3 个广播信道上周期性发送数据包,让中心设备(Central)发现自身存在;连接建立后,GATT 层定义的服务端(Server)与客户端(Client)通过读写(Read/Write)、通知(Notify,无确认)和指示(Indicate,需确认)等方式交换数据。BLE 具有超低功耗、快速连接、低成本等突出优势,广泛用于智能手环/手表、无线耳机、智能门锁、蓝牙灯泡等物联网和可穿戴设备。

拓展:BLE 的技术底座是经典蓝牙和低功耗蓝牙两条并存的技术路线(经典蓝牙侧重高带宽、连续传输,低功耗蓝牙侧重低功耗、间歇性通信),在此基础上,蓝牙 4.0 将两者整合为双模方案。蓝牙 5.0 及后续版本进一步引入了 2M PHY、扩展广播、LE Audio 等能力,将理论速率提升至 2 Mbps,广播包容量从 31 字节扩至 255 字节,并增加了长距离编码(Coded PHY)选项,实现了从“设备互联”到“场景化智能服务”的跨越式发展。

一、理论知识总结

项目 说明
频段与信道 2.4GHz ISM 频段(2402–2480 MHz),共 40 个 RF 信道(间隔 2 MHz):3 个广播信道(37/38/39)+ 37 个数据信道
调制技术 GFSK(高斯频移键控),BLE 4.0/4.2 为 1 Mbps 符号率;蓝牙 5.0 新增 2M PHY(2 Mbps)和 Coded PHY(125/500 kbps 长距离)
跳频技术 自适应跳频(AFH),在数据信道间快速跳变,避开干扰信道;广播信道固定,连接后在数据信道上跳频
拓扑结构 星形拓扑(经典 BLE 连接为一主多从),蓝牙 mesh 支持多跳网状网
角色划分 链路层:Master(主设备,发起连接)和 Slave(从设备,广播等待连接)
GAP 层:广播者(Advertiser)、观察者(Observer)、外围设备(Peripheral)、中心设备(Central)
GATT 层:Server(存储数据)和 Client(访问数据)
协议栈分层 PHY(物理层)→ LL(链路层)→ HCI(主机控制接口)→ L2CAP(逻辑链路控制与适配协议)→ ATT(属性协议)/SMP(安全管理器)→ GATT/GAP
广播与连接 广播设备(Advertiser)在 3 个广播信道循环发送广播包(ADV_IND);扫描设备(Scanner)监听广播包并发起连接请求(CONNECT_REQ)
连接参数 连接间隔(Connection Interval)7.5 ms–4 s,从机延迟(Slave Latency),监督超时(Supervision Timeout)
GATT 数据交换 Client 发起读写(Read/Write);Server 主动推送:Notify(无需确认)和 Indicate(需 Client 确认)
安全机制 LE Legacy Pairing(STK)和 LE Secure Connections(ECDH 生成 LTK),支持 Just Works、Passkey Entry、Numeric Comparison、OOB 四种配对方式;支持私有地址(RPA 定期更换)和 AES-128-CCM 加密
版本演进 蓝牙 4.0:引入 BLE,双模设计,速率 1 Mbps,连接建立 ≤ 3 ms;
蓝牙 4.2:LE Secure Connections,数据包长度扩展(MTU 可达 251 字节);
蓝牙 5.0:2M PHY(速率 2 Mbps)、LE Coded PHY(长距离)、扩展广播(255 字节)、广播信道扩展(最多 37 个辅助信道);
蓝牙 5.2:LE Audio、LC3 编码、多流音频
优点 超低功耗、快速连接、低成本、多设备支持、全球通用频段、良好生态
缺点 经典蓝牙与 BLE 部分不兼容、有效载荷小(连接态单包最多 20–251 字节)、同频段干扰(与 Wi-Fi 共存)、距离受限
典型应用 无线耳机/音箱(A2DP)、智能手环/手表(健康监测)、智能门锁/灯泡(蓝牙 Mesh)、防丢器/定位标签(Beacon)、医疗监护设备

二、BLE 通信协议流程

2.1 BLE 广播与连接建立流程(外围设备 → 中心设备)
sequenceDiagram
    participant P as 外围设备 Peripheral (从机)
    participant C as 中心设备 Central (主机)

    Note over P: 初始化 GAP 广播参数
    P->>P: 启动广播 (ADV_IND)
    loop 每个广播信道 (37/38/39)
        P->>C: 广播包 (包含设备名称、UUID等)
    end
    Note over C: 扫描到广播包
    C->>P: 发送连接请求 CONNECT_REQ
    P->>C: 确认连接
    Note over P,C: 连接建立,切换至数据信道
    P->>C: 连接成功后交换 MTU
    Note over P,C: 进入 GATT 数据通信阶段
2.2 GATT Server → Client 数据上报流程(Notify 方式)
sequenceDiagram
    participant S as GATT Server (设备端)
    participant C as GATT Client (手机端)

    Note over S,C: 已建立 BLE 连接
    C->>S: 使能 CCCD (客户端特征配置描述符)
    S-->>C: 写入成功响应
    loop 数据上报
        S-->>C: Notify (特征值变更,无确认)
        Note over S,C: Server 可连续发送多条 Notify
    end

说明

  • 广播包(Advertising Data)最大 31 字节,扫描响应包(Scan Response)同样为 31 字节,可携带设备名称、服务 UUID、发射功率等数据。
  • 蓝牙 5.0 及以上的扩展广播(Extended Advertising)可将广播数据容量提升至 255 字节,并支持更多辅助广播信道。
  • Notify(通知)无需客户端确认,适合频繁上报传感器的快速数据流;Indicate(指示)需要客户端确认,适合可靠性要求高的关键数据。
  • GATT Client 必须向特定特征对应的 CCCD(客户端特征配置描述符)写入 0x0001 来使能 Notify,服务器才能主动上报数据。
  • 连接参数(连接间隔等)需要在连接建立后进行协商,优化功耗和响应速度。

三、代码示例(BLE 从机特征值数据上报)

以下示例展示了 BLE 从机(GATT Server)通过 Notify 方式向已连接主机(GATT Client)上报传感器采集的温湿度数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 假设蓝牙协议栈已初始化,GATT 服务已注册
// 底层发送函数:atts_write_req(handle, data, len) 或类似的 GATT 通知 API
// 特征句柄 CHAR_HANDLE 在服务注册时获得

#define CHAR_HANDLE 0x0012 // 温湿度特征句柄(示例值)

// BLE 从机上报数据(Notify)
void BLE_SendNotify(uint16_t handle, uint8_t *data, uint16_t len)
{
// 调用协议栈提供的 GATT 通知函数
// 具体 API 因芯片平台而异,此处为示意
atts_write_req(handle, data, len);
}

// 主程序示例:上报温度 25℃、湿度 50%
void report_sensor_data(void)
{
uint8_t buf[2] = {25, 50}; // 第 0 字节温度,第 1 字节湿度

// 通过 Notify 上报数据
BLE_SendNotify(CHAR_HANDLE, buf, sizeof(buf));
}

注意

  • 实际开发中,不同厂商的 BLE 协议栈 API 有所差异。Nordic 使用 sd_ble_gatts_hvx(),TI 使用 GATT_Notification(),ESP32 使用 esp_ble_gatts_send_indicate() 并设置 need_confirm 参数为 false
  • Notify 数据长度受 MTU 限制(BLE 4.0/4.1 默认 23 字节有效载荷,BLE 4.2+ 可通过 MTU 交换扩展到 247 字节),长数据需拆包分次发送。
  • 发送 Notify 前必须确认 CCCD 已被客户端使能,否则服务器不应发送通知。
  • 使用 Notify 时无需等待对方确认,可连续发送多条数据;使用 Indicate 时需等待对方确认才能发送下一条。
  • 经典蓝牙(BR/EDR)与低功耗蓝牙(BLE)无法直接互通,双模设备可同时支持两种协议。

四、应用场景与注意事项

  • 典型应用

    • 无线音频:蓝牙耳机、蓝牙音箱(经典蓝牙 A2DP 协议,LE Audio 也开始普及)
    • 可穿戴设备:智能手环/手表(心率、步数同步到手机 App)
    • 智能家居:蓝牙 Mesh 灯控、智能门锁、温湿度传感器、蓝牙插座
    • 定位与信标:iBeacon 室内定位、防丢器、资产追踪标签
    • 医疗健康:血糖仪、血压计、体温计与手机数据同步
  • 常见坑点

    1. 经典蓝牙与 BLE 不互通:仅支持 BR/EDR 的设备和仅支持 BLE 的设备无法直接通信,需使用双模蓝牙设备作为桥梁。
    2. 广播通道干扰:Wi-Fi 在 2.4GHz 频段(尤其是信道 1、6、11)可能与蓝牙广播信道产生冲突,导致扫描困难。
    3. 未使能 CCCD 就发送 Notify:客户端未配置 CCCD(客户端特征配置描述符)的情况下发送通知,客户端将无法收到数据。
    4. 连接参数配置不当:连接间隔过短增加功耗,过长影响响应速度;从机延迟设置不当可能导致连接频繁断线。
    5. 配对绑定失败:配对过程中密钥交换异常(如 Just Works 模式下误操作),或绑定后设备地址变更导致无法重新连接。
    6. 广播数据超长:传统广播包最多 31 字节,超出部分需要依赖扫描响应包或使用蓝牙 5.0 的扩展广播功能。
    7. 未正确处理 MTU 交换:默认 MTU 为 23 字节,不进行 MTU 交换时无法传输较大数据包。
  • 调试工具

    • 手机端 nRF Connect / LightBlue 等 BLE 调试 App(扫描广播、连接、查看服务/特征、收发数据)
    • USB BLE 抓包器(如 Nordic BLE Sniffer、Ellisys、Frontline)配合 Wireshark 分析链路层和 ATT 协议
    • 蓝牙分析仪(专业级)和逻辑分析仪(抓取 HCI UART 日志)
    • 各芯片厂商提供的 SDK 调试工具(如 Nordic nRF Logger、TI BTool、ESP32 串口日志)

Wi-Fi:无处不在的无线宽带

Wi-Fi 是一种基于 IEEE 802.11 系列标准的短程无线网络技术,工作于 2.4 GHz5 GHz 频段(以及最新的 6 GHz 频段,Wi‑Fi 6E/7),采用 CSMA/CA(载波侦听多路访问/冲突避免) 机制实现多设备共享信道。Wi‑Fi 常采用基础设施模式:无线接入点(AP,Access Point)作为中心节点,多个站点(STA,Station)通过 AP 连接并接入互联网;也可支持 Ad‑Hoc 直连Wi‑Fi Direct 设备间直接通信。协议栈涵盖物理层(PHY)和介质访问控制层(MAC),利用无线电波传输数据。Wi‑Fi 具有覆盖范围广(典型室内 30–100 米)、传输速率高(从 11 Mbps 到 9.6 Gbps)、使用方便等优点,广泛用于家庭、办公、公共场所的无线互联,是智能家居和移动设备上网的核心技术。

一、理论知识总结

项目 说明
工作频段 2.4 GHz(2.400–2.4835 GHz,14 信道,互有重叠)、5 GHz(5.150–5.825 GHz,更多非重叠信道)、6 GHz(5.925–7.125 GHz,Wi‑Fi 6E/7)
信道带宽 20 MHz、40 MHz、80 MHz、160 MHz(通过信道绑定提升速率)
主要标准及速率 802.11b (11 Mbps)、802.11a/g (54 Mbps)、802.11n (最大 600 Mbps,支持 MIMO)、802.11ac (最大 6.93 Gbps,MU‑MIMO)、802.11ax (Wi‑Fi 6,最大 9.6 Gbps,OFDMA)、802.11be (Wi‑Fi 7,最大 46 Gbps)
多址接入技术 CSMA/CA(带冲突避免的载波侦听多路访问),采用 RTS/CTS 可选机制缓解隐藏节点问题
调制方式 BPSK、QPSK、16‑QAM、64‑QAM、256‑QAM(802.11ac)、1024‑QAM(802.11ax);OFDM/OFDMA(正交频分复用/多址)
拓扑结构 基础设施模式(AP + STA)、自组网(IBSS)、Wi‑Fi Direct(P2P)
安全协议 WEP(已淘汰)、WPA/WPA2(TKIP/CCMP,AES)、WPA3(SAE,更安全)
连接过程 扫描(主动/被动)→ 认证(Authentication)→ 关联(Association)→ 四次握手(生成加密密钥)→ DHCP 获取 IP
优点 覆盖范围大、速率高、使用方便、兼容性好、支持移动性
缺点 功耗较高(相对 BLE)、同频段干扰(2.4 GHz 尤其明显)、安全性依赖配置、信号受障碍物影响大
典型应用 家庭/企业无线上网、智能家居设备(摄像头、音箱、插座)、工业无线数据采集、公共场所热点、物联网

二、Wi‑Fi 通信协议流程

2.1 站点(STA)连接接入点(AP)并获取 IP
sequenceDiagram
    participant STA as 站点 (手机/设备)
    participant AP as 接入点 (路由器)

    Note over STA: 主动扫描 (Probe Request)
    STA->>AP: 探测请求 (Probe Request)
    AP-->>STA: 探测响应 (Probe Response,含 SSID、支持速率)
    STA->>AP: 认证请求 (Authentication)
    AP-->>STA: 认证响应 (成功)
    STA->>AP: 关联请求 (Association Request)
    AP-->>STA: 关联响应 (成功,分配 AID)
    STA->>AP: 四次握手 (4‑Way Handshake,生成加密密钥)
    AP-->>STA: 密钥协商完成
    STA->>AP: DHCP 发现 (Discover)
    AP-->>STA: DHCP 提供 (Offer)
    STA->>AP: DHCP 请求 (Request)
    AP-->>STA: DHCP 确认 (ACK,分配 IP)
    Note over STA,AP: 已连接并可收发 IP 数据包
2.2 CSMA/CA 基本数据传输流程(简化)
sequenceDiagram
    participant Sender as 发送站点
    participant Channel as 无线信道
    participant Receiver as 接收站点

    Sender->>Channel: 侦听信道是否空闲 (DIFS 时间)
    alt 信道忙
        Sender->>Sender: 随机退避 (Backoff)
    end
    Sender->>Receiver: 发送数据帧
    Receiver-->>Sender: 回复 ACK (短帧间间隔 SIFS 后)
    Note over Sender: 收到 ACK 表示传输成功

说明

  • 主动扫描:STA 发送 Probe Request,AP 回复 Probe Response。被动扫描:STA 侦听 AP 定期广播的 Beacon 帧。
  • 四次握手基于 802.1X(WPA/WPA2‑PSK 也使用类似流程)生成成对临时密钥(PTK)和组临时密钥(GTK)。
  • CSMA/CA 中的 DIFS 为分布式协调功能帧间间隔,SIFS 为短帧间间隔。RTS/CTS 机制可选用于减少冲突。
  • 实际 Wi‑Fi 数据传输还涉及分片、聚合(A‑MPDU)、节电模式等高级特性。

三、代码示例(ESP8266/ESP32 Wi‑Fi 连接路由器,TCP 客户端上报数据)

以下示例基于 ESP‑IDF 框架(ESP32),展示配置 Wi‑Fi 为 STA 模式,连接指定 SSID/密码,然后可作为 TCP 客户端发送数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"

// Wi‑Fi 连接函数
esp_err_t Wifi_Connect(const char *ssid, const char *pwd)
{
// 初始化 NVS(非易失性存储)
nvs_flash_init();
esp_netif_init();
esp_event_loop_create_default();

// 配置 Wi‑Fi 为 STA 模式
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);

wifi_config_t wifi_config = {
.sta = {
.ssid = "", // 需动态赋值
.password = "",
},
};
// 复制 SSID 和密码(注意安全截断)
strcpy((char *)wifi_config.sta.ssid, ssid);
strcpy((char *)wifi_config.sta.password, pwd);

esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
esp_wifi_start();

// 可选:等待连接成功(通过事件处理或轮询)
return ESP_OK;
}

// 示例调用:连接路由器并上报数据(伪代码)
void app_main(void)
{
Wifi_Connect("MySSID", "MyPassword");
// 等待 Wi‑Fi 连接成功并获取 IP(通常使用事件回调)
// 然后创建 TCP socket 向服务器发送数据
}

注意

  • 实际开发中需注册 Wi‑Fi 事件处理函数(如 esp_event_handler_register)来监控连接状态和 IP 获取。
  • 示例代码简化了初始化步骤,生产环境中应检查返回值并处理错误。
  • TCP 发送数据时建议使用非阻塞模式或 RTOS 任务,避免阻塞主循环。
  • 对于低功耗设备,可启用 Wi‑Fi 休眠模式(Modem‑sleep、Light‑sleep)以降低能耗。
  • 不同芯片厂商的 Wi‑Fi API 差异较大,但核心概念(扫描、认证、关联、DHCP)一致。

四、应用场景与注意事项

  • 典型应用

    • 家庭/企业网络:手机、电脑、智能电视、游戏机无线上网
    • 智能家居:Wi‑Fi 插座、智能音箱(如 Amazon Echo)、扫地机器人、摄像头
    • 工业物联网:无线传感器数据采集、AGV 小车通信
    • 公共热点:商场、机场、咖啡馆提供的免费 Wi‑Fi
    • 无线打印:打印机通过 Wi‑Fi 与电脑/手机连接
  • 常见坑点

    1. 频段选择:2.4 GHz 覆盖好但干扰多(微波炉、蓝牙、邻居 Wi‑Fi),5 GHz 速度快但穿墙差;支持双频的设备可优先连接 5 GHz。
    2. 信道重叠:2.4 GHz 中只有 1、6、11 三个完全非重叠信道,部署 AP 时应错开信道。
    3. 隐藏节点问题:在复杂环境中,RTS/CTS 机制有助于减少碰撞。
    4. 安全配置:避免使用 WEP 或开放式网络,至少使用 WPA2‑PSK(AES),推荐 WPA3。
    5. 连接过程超时:环境信号弱或 AP 负载高时,认证/关联可能失败,需实现重试机制。
    6. IP 地址获取失败:DHCP 超时或地址池耗尽,可配置静态 IP 作为备用。
    7. 功耗过高:对于电池供电设备,Wi‑Fi 的功耗显著高于 BLE,应合理使用休眠模式并降低发包频率。
    8. 固件/驱动兼容性:不同 Wi‑Fi 芯片对某些高级特性(如 MIMO、OFDMA)的支持程度不同,需查阅数据手册。
  • 调试工具

    • Wi‑Fi 抓包:Wireshark + 支持监听模式的无线网卡(如 Atheros 芯片)分析 802.11 帧。
    • 频谱分析仪:查看 2.4/5 GHz 频段的干扰情况(或使用手机 App Wi‑Fi 分析仪)。
    • ping 工具:测试连通性和延迟;iperf3 测试吞吐量。
    • 芯片厂商日志:ESP32 通过串口输出 Wi‑Fi 事件和连接状态;Linux 下使用 iwconfigiw dev wlan0 scan

ZigBee:低功耗组网的智能之选

(这个可能有坑,但实际上所有的都会有坑,项目做的不大不小了就用”工业认证”的协议比较好)

ZigBee 是一种基于 IEEE 802.15.4 标准的低功耗、低数据速率无线网络技术,主要面向自动控制和远程监控领域。它工作于 2.4 GHz(全球通用)、915 MHz(美国)和 868 MHz(欧洲)频段,采用 DSSS(直接序列扩频)跳频技术(部分频段)以增强抗干扰能力。ZigBee 支持星型、树型和网状(Mesh) 等多种网络拓扑,具备自组织、自愈能力——网络中设备可自动建立路由,单个节点故障时仍能维持通信。其协议栈建立在 IEEE 802.15.4 的物理层(PHY)和介质访问控制层(MAC)之上,由 ZigBee 联盟定义网络层(NWK)、应用层(APL)和安全服务。ZigBee 的特点是超低功耗(电池供电可运行数年)、低成本高安全性(AES‑128 加密)、网络容量大(单个协调器可连接数万个节点),广泛用于智能家居、工业监控、医疗护理和楼宇自动化等场景。

补充:ZigBee 网络中设备角色分为协调器(Coordinator)——负责组建网络、分配地址;路由器(Router)——允许子设备加入并转发数据;终端设备(End Device)——低功耗节点,通常睡眠唤醒发送数据。网络层支持簇树(Cluster Tree)和网状路由(AODV 路由算法),确保数据传输的可靠性。

一、理论知识总结

项目 说明
基础标准 IEEE 802.15.4(物理层和 MAC 层)
工作频段与速率 2.4 GHz(16 信道,250 kbps)、915 MHz(10 信道,40 kbps)、868 MHz(1 信道,20 kbps)
调制与扩频 2.4 GHz:O‑QPSK + DSSS(半正弦脉冲整形);915/868 MHz:BPSK + DSSS
网络拓扑 星型(Star)、树型(Cluster Tree)、网状(Mesh,支持多跳)
设备角色 协调器(ZC,每个网络唯一)、路由器(ZR,可转发)、终端设备(ZED,低功耗)
网络容量 理论最大节点数 2^16 – 1(65535),实际受限于内存和通信效率
路由协议 网络层采用 AODV(按需距离矢量路由)或 Cluster Tree 路由
安全机制 AES‑128 加密(链路层和应用层),支持密钥建立与更新
低功耗技术 终端设备可周期性睡眠(Beacon 模式或非 Beacon 模式),通过轮询(Data Request)从父节点接收数据
优点 低功耗(电池续航数年)、低成本、自组织/自愈网状网、高安全性、大网络容量
缺点 数据速率低(最大 250 kbps)、通信距离较短(室内约 10–30 米,可多跳扩展)、组网配置较复杂
典型应用 智能家居(灯光、插座、窗帘、安防传感器)、工业监控(温湿度、振动、压力监测)、医疗护理(患者定位、体征监测)、楼宇自动化

二、ZigBee 通信协议流程

2.1 协调器组建网络与终端设备入网
sequenceDiagram
    participant Coord as 协调器 (Coordinator)
    participant Router as 路由器 (Router)
    participant EndDev as 终端设备 (End Device)

    Note over Coord: 扫描信道,选择空闲信道
    Coord->>Coord: 启动网络(设置 PAN ID 和短地址 0x0000)
    loop 周期性发送 Beacon
        Coord->>Router: 信标帧 (Beacon,含网络参数)
    end
    Router->>Coord: 关联请求 (Association Request)
    Coord-->>Router: 关联响应 (分配短地址)
    Router->>Coord: 数据请求 (Data Request)
    Coord-->>Router: ACK 及确认
    Note over EndDev: 扫描找到路由器/协调器
    EndDev->>Router: 关联请求
    Router-->>EndDev: 关联响应 (分配短地址)
    EndDev->>Router: 数据请求 (周期唤醒)
    Router-->>EndDev: 缓存数据或 ACK
2.2 终端设备向协调器上报传感器数据(网状网)
sequenceDiagram
    participant EndDev as 终端设备 (传感器)
    participant Router as 路由器 (中继)
    participant Coord as 协调器

    EndDev->>EndDev: 采集数据,准备发送
    EndDev->>Router: 发送数据帧 (使用 APS 或 AF 帧)
    Router->>Router: 查询路由表
    Router->>Coord: 转发数据 (多跳)
    Coord-->>Router: 应答 (ACK)
    Router-->>EndDev: 应答 (ACK)
    Note over EndDev: 可进入休眠

说明

  • 网络启动时,协调器先进行能量检测(ED)和主动扫描,选择干扰最小的信道,设置 PAN ID(个人局域网标识符)。
  • 设备入网流程:子设备发送关联请求 → 父设备回复关联响应(分配 16 位短地址)→ 子设备发送数据请求确认。
  • 终端设备为了省电,大部分时间处于休眠状态,定期唤醒(例如每几秒)发送数据请求,查询父节点是否有发给自己的数据。
  • 网状网中使用 AODV 路由:源节点广播 RREQ(路由请求),目的节点回复 RREP(路由应答),中间节点学习路由表。
  • ZigBee 应用层通过端点(Endpoint)、簇(Cluster)和属性(Attribute)定义设备功能,如 ZCL(ZigBee 集群库)规范。

三、代码示例(ZigBee 终端节点向协调器周期性上传传感器数据)

以下示例基于 TI Z‑Stack 或类似的 ZigBee 协议栈 API,展示终端设备通过 AF_DataRequest 向协调器(地址 0x0000)发送传感器数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 假设已定义端点号和簇 ID
#define ENDPOINT 10
#define CLUSTER_ID 0x0001 // 自定义传感器数据簇

// 目标地址:协调器短地址为 0x0000
uint16_t dst_addr = 0x0000;

// 发送数据函数
void ZB_SendData(uint16_t dst_addr, uint8_t *dat, uint8_t len)
{
afAddrType_t dst;
dst.addr.shortAddr = dst_addr; // 目标短地址
dst.addrMode = afAddr16Bit; // 16 位短地址模式
dst.endpoint = ENDPOINT; // 目标端点
dst.panId = 0xFFFF; // 使用当前 PAN ID

// 调用 AF 数据请求
AF_DataRequest(&dst, // 目标地址
(endPointDesc_t *)&epDesc, // 源端点描述符(需提前定义)
CLUSTER_ID,
len,
dat,
NULL, // 事务序号(可选)
0, // 选项(如 AF_ACK_REQUEST 请求应答)
AF_DEFAULT_RADIUS); // 跳数半径
}

// 主函数中周期性调用
void main_loop(void)
{
uint8_t sensor_data[2] = {temp, humi};
ZB_SendData(0x0000, sensor_data, sizeof(sensor_data));
// 延时或进入休眠
}

注意

  • 实际开发中需先在应用层注册端点描述符(endPointDesc_t),并指定输入/输出簇列表。
  • 若发送时使用 AF_ACK_REQUEST 选项,接收方需在应用层发送 APS ACK,否则仅 MAC 层 ACK。
  • 终端设备在休眠前应确保数据已成功发送并收到 MAC ACK(或 APS ACK)。
  • 不同厂商的 ZigBee 协议栈 API 略有差异(如 Silicon Labs EmberZNet、TI Z‑Stack、Microchip MiWi),但基本概念一致。
  • 协调器收到数据后通常通过串口或用户回调处理数据。

四、应用场景与注意事项

  • 典型应用

    • 智能家居:ZigBee 智能灯泡、智能插座、门窗传感器、温湿度传感器、烟雾报警器(与网关配合)
    • 工业监控:无线传感器网络(压力、振动、流量监测)、设备状态采集
    • 楼宇自动化:照明控制、窗帘控制、HVAC(暖通空调)调节
    • 医疗健康:患者定位、输液监测、老人/婴儿看护
    • 农业物联网:土壤湿度监测、温室控制
  • 常见坑点

    1. 信道选择冲突:2.4 GHz 频段与 Wi‑Fi、蓝牙共存,需选择相对干净的信道(例如 ZigBee 建议避开 Wi‑Fi 的 1、6、11 信道主载波)。
    2. 终端设备父节点失效:若终端设备的父节点(路由器/协调器)断电,终端需重新扫描并加入其他父节点,应用层应处理重入网逻辑。
    3. 数据发送失败:由于冲突或路由未建立,首次发送可能失败;协议栈通常自动重试,但应设置超时和重传策略。
    4. 网络深度过大:网状网最大跳数受限(通常不超过 30 跳),跳数过多会增加延迟和丢包率。
    5. 绑定表配置错误:设备间通信(如开关控制灯)需要配置绑定(Binding)表,若未正确建立绑定则数据无法到达目标。
    6. 安全性配置不一致:网络密钥(Network Key)必须相同才能加入网络,链路密钥(Link Key)用于端到端加密。
    7. 休眠周期与数据延迟:终端设备休眠时间过长会导致上行数据延迟大或下行控制指令无法及时送达,需平衡功耗与实时性。
    8. 设备地址冲突:虽然 ZigBee 使用 16 位短地址且由父节点分配,但在网络中应避免手动干预导致重复。
  • 调试工具

    • ZigBee 抓包器:如 TI CC2531 USB Dongle 配合 Ubiqua 或 Wireshark 分析 802.15.4 帧
    • 厂商调试工具:TI Z‑Tool、Silicon Labs Simplicity Studio、Digi XCTU
    • 网络分析:监听网络加入、路由发现和 APS 层通信
    • 串口助手:查看协调器/路由器打印的入网信息及数据

LoRa:远距离低功耗的通信之星

LoRa(Long Range Radio,远距离无线电)是一种基于扩频调制技术的低功耗广域网络(LPWAN)无线通信技术。它工作在 ISM 频段(433 MHz、868 MHz、915 MHz 等),通过将信号扩展至较宽的频带,实现了远距离(城镇 2–5 km,郊区可达 15 km)低功耗的平衡。LoRa 采用 Chirp 扩频(CSS) 调制,并支持扩频因子(SF)带宽(BW)编码率(CR) 等可调参数,让用户可根据距离、速率、功耗需求灵活配置。LoRa 本身仅定义物理层(PHY),通常与 LoRaWAN 协议配合形成网络层和应用层。LoRa 具有传输距离远、功耗极低、抗干扰强、成本低等优点,广泛用于智能抄表、环境监测、智慧农业、资产追踪等物联网场景。

补充:LoRaWAN 采用星型拓扑,节点与网关直接通信,网关将数据转发至网络服务器,支持双向通信且针对低功耗进行了优化(如 Class A/B/C 三种工作模式)。

一、理论知识总结

项目 说明
调制技术 Chirp 扩频(CSS),线性调频脉冲;抗多径衰落,接收灵敏度可达 –148 dBm
工作频段 主要 ISM 频段:433 MHz(中国/欧洲)、868 MHz(欧洲)、915 MHz(美国/澳大利亚),部分区域开放 470–510 MHz
扩频因子(SF) 6–12,SF 越高,通信距离越远、空中时间越长、功耗越高;典型值 SF7(高速)、SF12(最远距离)
带宽(BW) 125 kHz、250 kHz、500 kHz;窄带提高灵敏度,宽带提高数据速率
编码率(CR) 4/5、4/6、4/7、4/8,前向纠错(FEC)强度;CR 越高,可靠性越强,有效负载减少
数据速率 取决于 SF、BW、CR,范围约 0.3 kbps – 27 kbps(典型 0.3–5 kbps 用于远距离)
通信距离 城镇 2–5 km,郊区 10–15 km,视距可达 20 km 以上
发射电流 典型 20–120 mA(+20 dBm 发射时),接收电流 5–10 mA,休眠电流 < 1 µA
网络拓扑(LoRaWAN) 星型:节点 → 网关 → 网络服务器 → 应用服务器
节点类型 Class A(双向,下行需上行后)、Class B(定时下行窗口)、Class C(持续接收下行)
安全 LoRaWAN 使用 AES‑128 加密(网络会话密钥和应用会话密钥)
优点 超远距离、超低功耗、穿透力强、抗干扰、低成本、易于部署
缺点 低数据速率(不适合音频/视频)、占空比受限(ISM 频段有法规限制)、需网关支撑网络
典型应用 智能电表/水表/气表、环境监测(空气、水质)、农业灌溉、畜牧定位、智慧城市停车/垃圾桶监测

二、LoRa 通信协议流程

2.1 LoRa 节点向网关发送数据(单跳星型网络)
sequenceDiagram
    participant Node as LoRa 节点
    participant Gateway as LoRa 网关
    participant NS as 网络服务器

    Node->>Node: 采集传感器数据
    Node->>Node: 使用 SF/BW/CR 参数打包(带 CRC)
    Node->>Gateway: 发送上行数据包 (LoRa 调制)
    Note over Gateway: 解调、解扩,校验 CRC
    Gateway->>NS: 通过 TCP/UDP 转发(Packet Forwarder 协议)
    NS-->>Node: 可选下行数据(仅在节点接收窗口)
2.2 LoRaWAN Class A 节点上行 → 下行接收窗口
sequenceDiagram
    participant Node as Class A 节点
    participant Gateway as 网关

    Node->>Node: 进入发送状态
    Node->>Gateway: 发送上行帧(TX)
    Note over Node: 结束发送后延时 RECV_DELAY_1
    Node->>Gateway: 打开 RX1 窗口(相同频段)
    alt 服务器有下行数据
        Gateway->>Node: 在下行窗口发送下行帧
    else 无下行数据
        Node->>Node: 无接收
    end
    Note over Node: 若 RX1 未收到,延时后打开 RX2 窗口
    Node->>Gateway: RX2 窗口(固定频率/速率)
    Node->>Node: 进入休眠(最低功耗)

说明

  • LoRa 物理层中,一个数据包通常包含前导码(Preamble)、物理头(PHDR)、物理头 CRC(PHDR_CRC)和有效负载(Payload)。
  • 扩频因子(SF)与通信距离呈正相关,与数据速率呈反相关。例如 SF12 的空中时间是 SF7 的 2^5=32 倍。
  • LoRaWAN 中上行数据携带设备地址(DevAddr)、帧计数器(FCnt)和 MIC(消息完整性校验),防止重放攻击。
  • 网关与网络服务器之间使用 Semtech UDP 协议(或称 Packet Forwarder 协议)或 MQTT 传输 JSON 格式的数据。

三、代码示例(LoRa 模块发送传感器数据包,配置 433 MHz、SF12)

以下示例基于 Semtech SX126x/SX127x 系列 LoRa 芯片驱动,展示初始化频点、扩频因子,并发送一包数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 假设 LoRa 硬件驱动已初始化(SPI 接口)
// 底层函数:LoRa_WriteReg, LoRa_ReadReg, LoRa_SetTx 等

// 设置工作频率(单位:Hz)
void LORA_SetFreq(uint32_t freq)
{
uint64_t frf = ((uint64_t)freq << 25) / 32000000; // 根据晶振计算
LoRa_WriteReg(REG_FRF_MSB, (frf >> 16) & 0xFF);
LoRa_WriteReg(REG_FRF_MID, (frf >> 8) & 0xFF);
LoRa_WriteReg(REG_FRF_LSB, frf & 0xFF);
}

// 设置扩频因子(6-12)
void LORA_SetSF(uint8_t sf)
{
if (sf < 6) sf = 6;
if (sf > 12) sf = 12;
LoRa_WriteReg(REG_MODEM_CONFIG2, (LoRa_ReadReg(REG_MODEM_CONFIG2) & 0x0F) | ((sf << 4) & 0xF0));
}

// 发送数据包
void LoRa_Send(uint8_t *buf, uint8_t len)
{
// 配置 LoRa 模式
LORA_SetFreq(433000000); // 设置 433 MHz
LORA_SetSF(12); // 扩频因子 12(最远距离)
// 可选设置:带宽 125 kHz,编码率 4/5,输出功率 20 dBm

// 清空 FIFO,写入数据
LoRa_WriteReg(REG_FIFO_TX_BASE_ADDR, 0);
LoRa_WriteReg(REG_FIFO_ADDR_PTR, 0);
for (uint8_t i = 0; i < len; i++)
LoRa_WriteReg(REG_FIFO_DATA, buf[i]);
LoRa_WriteReg(REG_PAYLOAD_LENGTH, len);

// 启动发送
LoRa_WriteReg(REG_OP_MODE, MODE_TX);
// 等待发送完成中断(示例省略中断处理,实际应轮询或使用 IRQ)
while (!(LoRa_ReadReg(REG_IRQ_FLAGS) & IRQ_TX_DONE));
LoRa_WriteReg(REG_IRQ_FLAGS, IRQ_TX_DONE); // 清除标志
}

// 示例:采集温湿度并发送
void report_sensor(void)
{
uint8_t data[5];
data[0] = 0x01; // 节点地址
data[1] = 25; // 温度 25℃
data[2] = 50; // 湿度 50%
data[3] = 0x00; // 保留
data[4] = 0x00; // CRC 可由上层计算
LoRa_Send(data, sizeof(data));
}

注意

  • 实际应用中,建议使用成熟的 LoRaWAN 协议栈(如 Semtech 的 LoRaMac-node、IBM 的 LMiC),避免自行实现帧格式和重传机制。
  • 配置扩频因子、带宽、编码率时需满足 LoRaWAN 区域规范(如 EU868 要求 SF12 BW125 CR4/5 等)。
  • 空中时间随 SF 增加而迅速增加,需注意占空比限制(如欧洲 868 MHz 频段 1% 占空比)。
  • LoRa 发送前建议进行信道活动检测(CAD)以避免冲突。
  • 发送完成后应切换至接收模式(如 Class A 节点打开 RX 窗口)或休眠模式以节省功耗。

四、应用场景与注意事项

  • 典型应用

    • 智能抄表:电表、水表、燃气表数据远传(无需人工读数)
    • 环境监测:空气质量(PM2.5/PM10)、气象站(风速/风向/雨量)、水质监测
    • 智慧农业:土壤湿度传感器、自动灌溉阀、牲畜定位项圈
    • 资产追踪:集装箱、冷链物流温度跟踪
    • 智慧城市:智能停车地磁检测、垃圾桶满溢监测、路灯控制
  • 常见坑点

    1. 扩频因子与数据率平衡:SF12 可提高距离但大幅降低速率和增加功耗,应根据实际覆盖要求选择合适 SF。
    2. 占空比限制:ISM 频段通常规定设备发射时间占空比 ≤1%(例如 868 MHz),需计算空中时间,否则违反法规。
    3. 前导码长度:默认前导码 8 个符号,长距离或密集干扰时可适当增加前导码提高唤醒可靠性。
    4. 接收灵敏度与干扰:虽然 LoRa 抗干扰能力强,但强干扰仍可能导致丢包;可使用 CAD 检测信道空闲后再发送。
    5. 网关部署位置:为了覆盖广阔区域,网关需架设于高处(建筑物顶、铁塔),减少遮挡。
    6. 双向通信延时:Class A 节点只能在发送后短暂打开接收窗口,下行指令需等待节点下次上行,实时性较差;Class C 持续监听但功耗高。
    7. 频点规划:同一区域多个网络需使用不同频点或不同扩频因子,避免同频干扰。
    8. 数据加密:虽然 LoRaWAN 提供了 AES‑128 加密,但自行设计私有协议时需确保加密和完整性校验。
  • 调试工具

    • 频谱分析仪:查看 433/868/915 MHz 频段干扰情况
    • LoRa 调试助手:如 Semtech 的 LoRaWAN 网络服务器模拟器、ChirpStack 网关桥接
    • SX126x/SX127x 评估板配合 PC 端 GUI(如 Semtech LoRa Calculator)计算空中时间
    • USB 转 LoRa 模块(如 Dragino、RisingHF)配合串口助手进行收发测试

MQTT:物联网通信的轻量使者

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是一种基于 TCP/IP 的轻量级发布/订阅通信协议,专为硬件资源受限、网络带宽低且不稳定的物联网场景设计。它采用发布者(Publisher)、代理(Broker)、订阅者(Subscriber) 三方模型:发布者将消息发送到代理的某个主题(Topic),订阅者通过订阅主题接收消息,实现设备间解耦。MQTT 固定报头仅 1 字节,可选的 2 字节心跳报文使开销极小;支持 三种 QoS 等级(最多一次、至少一次、恰好一次)以适应不同可靠性需求。由于协议简洁、易于实现,MQTT 已成为物联网数据上传、远程控制、设备状态同步的主流协议之一,广泛应用于智能家居、工业物联网、车联网、移动消息推送等领域。

一、理论知识总结

项目 说明
协议架构 发布/订阅模型,三个角色:发布者、代理(Broker)、订阅者
传输层 基于 TCP/IP(默认端口 1883 for 非加密,8883 for TLS)
协议开销 最小固定报头 2 字节(控制报文类型 + 标志位 + 剩余长度),可变头+有效负载可选
主题(Topic) UTF‑8 字符串,支持层级分隔符 /(如 home/livingroom/temp),通配符 +(单层)和 #(多层)
QoS 等级 QoS 0(最多一次):消息可能丢失,无确认
QoS 1(至少一次):确认接收,可能重复
QoS 2(恰好一次):两次握手,保证不重复不丢失
会话(Session) 持久会话:代理存储离线期间的 QoS 1/2 消息,客户端重连后接收
保留消息(Retain) 代理为每个主题保留最后一条消息,新订阅者立即收到
遗嘱消息(Will) 客户端异常断开时,代理自动发布预设的遗嘱消息
心跳(Keep Alive) 客户端定期发送 PINGREQ,代理回复 PINGRESP,维持连接活跃
安全 支持用户名/密码认证,TLS/SSL 加密传输
优点 轻量、低带宽、低功耗、解耦、灵活的主题过滤、多种 QoS、持久会话
缺点 依赖 TCP(不适合极低带宽/高丢包网络);代理可能成为单点瓶颈;主题命名无标准规范
典型应用 物联网传感器数据上报、远程设备控制(智能灯泡/插座)、消息推送(即时通讯)、车联网(T‑Box 与云端通信)

二、MQTT 通信协议流程

2.1 发布者 → 代理 → 订阅者(QoS 1 示例)
sequenceDiagram
    participant Pub as 发布者
    participant Broker as MQTT 代理
    participant Sub as 订阅者

    Sub->>Broker: 订阅主题 (SUBSCRIBE, topic=home/+/temp)
    Broker-->>Sub: SUBACK (确认订阅)
    Pub->>Broker: 发布消息 (PUBLISH, topic=home/livingroom/temp, payload=25.6, QoS=1)
    Broker-->>Pub: PUBACK (确认收到)
    Broker->>Sub: 转发消息 (PUBLISH, topic=home/livingroom/temp, payload=25.6, QoS=1)
    Sub-->>Broker: PUBACK (确认接收)
2.2 客户端连接并发布数据(含心跳)
sequenceDiagram
    participant Client as MQTT 客户端
    participant Broker as 代理

    Client->>Broker: CONNECT (ClientId, KeepAlive=60, CleanSession)
    Broker-->>Client: CONNACK (成功)
    loop 每隔 KeepAlive/2 秒
        Client->>Broker: PINGREQ
        Broker-->>Client: PINGRESP
    end
    Client->>Broker: PUBLISH (topic=dev/temp, payload=24, QoS=0)
    Client->>Broker: DISCONNECT

说明

  • 订阅和发布使用相同的主题格式,通配符只能在订阅时使用,发布时不能包含 +#
  • QoS 1 需要 PUBACK 确认;QoS 2 需要 PUBREC、PUBREL、PUBCOMP 四次交互。
  • 代理可以配置为内存或持久化存储消息,支持离线客户端(QoS 1/2 + 持久会话)。
  • 心跳机制用于探测连接是否存活,若未收到 PINGRESP,客户端可重连。

三、代码示例(MQTT 客户端向主题发布温湿度数据,QoS 1)

以下示例使用 paho.mqtt 嵌入式 C 库(抽象 API),展示发布一包传感器数据到主题 dev/temp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 假设 mqtt_client 已初始化并成功连接到代理
// 函数原型: mqtt_client_publish(mqtt_client_t* client, const char* topic, uint8_t* payload, uint16_t len, int qos)

void MQTT_PublishData(char *topic, uint8_t *payload, uint16_t len)
{
// 发布数据,QoS = 1,需要服务器确认
mqtt_client_publish(&mqtt_client, topic, payload, len, MQTT_QOS_1);
}

// 调用示例:上报温度 25°C,湿度 50%
void report_sensor(void)
{
char data[16];
snprintf(data, sizeof(data), "{\"temp\":25,\"humi\":50}");
MQTT_PublishData("dev/temp", (uint8_t*)data, strlen(data));
}

注意

  • 实际嵌入式中常用 paho MQTT C 客户端、FreeRTOS+MQTT 或 ESP‑IDF 的 MQTT 组件。
  • 发布前应确保连接有效,否则需重连后再发布。
  • 对于 QoS 1/2 消息,客户端应保留未确认的报文,在重连后重新发送(持久会话)。
  • 主题命名建议采用逆域名或分层结构,如 公司/产品/设备/数据类型
  • 对于低功耗设备,可设置较长心跳间隔(如 240 秒),并在完成数据上报后立即断开连接以节省电池。

四、应用场景与注意事项

  • 典型应用

    • 物联网传感器数据上报:温湿度、空气质量、水表等设备定时上传数据到云平台
    • 远程设备控制:手机 App 通过 MQTT 发送控制指令(开灯、调温、锁门)
    • 智能家居场景:多个设备共享状态(如网关同步所有灯泡状态)
    • 车联网:车辆 T‑Box 上报 GPS 位置、发动机数据,接收远程控制指令
    • 即时消息推送:移动端通知、聊天室消息
  • 常见坑点

    1. QoS 选择不当:对于非关键数据使用 QoS 0 可降低开销,但丢失无感知;关键控制指令使用 QoS 1 或 2,注意 QoS 2 会增加延迟。
    2. 心跳超时导致掉线:网络闪断时若未收到 PINGRESP,客户端应主动重连,避免长期占用代理资源。
    3. 主题通配符过度使用:大量订阅 # 会导致代理转发所有消息,增加负载。
    4. 会话清理配置错误:CleanSession = 1 会删除离线消息,设备重连后丢失;CleanSession = 0 可恢复会话但需管理存储。
    5. 负载过大:当消息速率过高,代理或客户端的接收缓冲区溢出可能导致丢包;应使用流量控制或增加缓冲区。
    6. 安全配置缺失:生产环境必须启用用户名/密码认证,使用 TLS 加密通信,避免敏感数据泄露。
    7. 遗嘱消息未利用:设备异常断连时,可利用遗嘱消息通知其他设备(如“设备离线”),提高系统健壮性。
    8. 保留消息滥用:每个主题仅需保留最后一条重要状态消息,滥用保留消息会浪费代理存储。
  • 调试工具

    • MQTT 代理软件:Mosquitto、EMQX、HiveMQ(可开启日志)
    • 客户端调试工具:MQTT.fx、MQTT Explorer、Mosquitto_pub/sub 命令行
    • 抓包分析:Wireshark 过滤 tcp.port == 1883 查看 MQTT 报文
    • 嵌入式调试:串口打印 MQTT 事件和错误码