4. 通信协议基础:I2C、SPI、UART、Modbus在传感器数据读取中的应用
做温室物联网,说白了就是跟传感器打交道。而传感器怎么把数据交给你?靠的就是通信协议。我见过不少新手,上来就怼代码,结果数据读出来全是乱码,折腾半天才发现是协议没配对。
今天咱们就把温室里最常用的四种协议——I2C、SPI、UART、Modbus——掰开揉碎了讲清楚。每种协议怎么接线、怎么读数据、有什么坑,我都会结合项目经验给你说明白。
4.1 I2C:两线制,适合短距离多传感器
I2C协议,我习惯叫它「两根线走天下」。一根时钟线SCL,一根数据线SDA,就能挂载几十个传感器。温室里的温湿度传感器(比如SHT30、BME280)大多用这个。
工作原理很简单:主机(比如你的ESP32)通过SCL发出时钟脉冲,SDA线上按位传输数据。每个从机(传感器)都有一个唯一的7位地址,主机点名,从机应答。
关键参数:
- 标准模式:100kHz
- 快速模式:400kHz
- 高速模式:3.4MHz(温室里基本用不到)
- 最大总线电容:400pF(线太长会出问题)
我在项目中遇到过一个问题:温室里装了8个SHT30,用I2C总线连在一起,结果数据偶尔跳变。查了半天,发现是总线长度超过了2米,信号反射导致的。后来加了上拉电阻(4.7kΩ),问题解决。
避坑指南:我曾经因为忘记加上拉电阻,折腾了一整天。I2C总线必须要有上拉电阻,一般4.7kΩ到10kΩ。没有它,SDA和SCL拉不高,数据根本读不出来。
代码示例:读取SHT30温湿度
#include <Wire.h>
#include <Adafruit_SHT31.h>
Adafruit_SHT31 sht31 = Adafruit_SHT31();
void setup() {
Serial.begin(115200);
Wire.begin(); // 默认引脚,ESP32是GPIO21(SDA), GPIO22(SCL)
if (!sht31.begin(0x44)) { // SHT30默认地址0x44
Serial.println("找不到SHT30!检查接线");
while (1);
}
}
void loop() {
float t = sht31.readTemperature();
float h = sht31.readHumidity();
if (!isnan(t)) {
Serial.print("温度: "); Serial.print(t);
Serial.print(" °C, 湿度: "); Serial.print(h); Serial.println(" %");
} else {
Serial.println("读取失败");
}
delay(2000);
}
4.2 SPI:四线制,速度快,适合高数据量
SPI协议,说白了就是「快」。四根线:SCK(时钟)、MOSI(主机输出从机输入)、MISO(主机输入从机输出)、CS(片选)。温室里如果要用高精度的光照传感器(比如TSL2591)或者ADC芯片,SPI是首选。
为什么快?因为它是全双工的——发送和接收可以同时进行。而且没有地址的概念,每个从机单独用一根CS线控制。
| 特性 | I2C | SPI |
|---|---|---|
| 线数 | 2根 | 4根(最少) |
| 速度 | 最高3.4MHz | 可达几十MHz |
| 从机数量 | 理论127个 | 受限于GPIO数量 |
| 距离 | 1-2米 | 1米以内 |
| 典型传感器 | SHT30, BME280 | TSL2591, MAX31865 |
我的经验:SPI的时钟极性(CPOL)和时钟相位(CPHA)一定要配对。我见过有人把模式0和模式3搞混,数据读出来全是0x00。查手册,看传感器要求的是哪种模式,别想当然。
代码示例:读取TSL2591光照传感器
#include <SPI.h>
#define CS_PIN 5 // 片选引脚
void setup() {
Serial.begin(115200);
SPI.begin();
pinMode(CS_PIN, OUTPUT);
digitalWrite(CS_PIN, HIGH);
// 初始化传感器(简化版)
digitalWrite(CS_PIN, LOW);
SPI.transfer(0x80 | 0x00); // 写命令
SPI.transfer(0x03); // 使能传感器
digitalWrite(CS_PIN, HIGH);
}
void loop() {
uint16_t ch0, ch1;
digitalWrite(CS_PIN, LOW);
SPI.transfer(0x80 | 0x14); // 读通道0
ch0 = SPI.transfer(0x00) << 8;
ch0 |= SPI.transfer(0x00);
digitalWrite(CS_PIN, HIGH);
Serial.print("可见光+红外: "); Serial.println(ch0);
delay(1000);
}
4.3 UART:串口通信,简单可靠
UART,大家更习惯叫它「串口」。就两根线:TX(发送)和RX(接收)。温室里很多工业级的传感器(比如CO2传感器、风速传感器)都用这个。
为什么?因为简单。不需要时钟线,双方约定好波特率就行。我常用的波特率是9600和115200。
UART通信要点:
- 波特率必须一致(差一点都会乱码)
- TX接RX,RX接TX(交叉连接)
- 数据格式:1位起始位 + 8位数据位 + 1位停止位(最常见)
- 距离:RS232电平3-5米,TTL电平1米以内
我记得有一次在温室里调试CO2传感器,数据读出来全是乱码。检查了半天,发现是波特率设成了9600,但传感器默认是19200。改过来就好了。嗯,这种低级错误我也犯过。
代码示例:读取MH-Z19B CO2传感器
#include <SoftwareSerial.h>
SoftwareSerial co2Serial(16, 17); // RX, TX
byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
void setup() {
Serial.begin(115200);
co2Serial.begin(9600); // MH-Z19B默认波特率9600
}
void loop() {
co2Serial.write(cmd, 9);
delay(100);
byte response[9];
if (co2Serial.available() >= 9) {
co2Serial.readBytes(response, 9);
if (response[0] == 0xFF && response[1] == 0x86) {
int co2 = response[2] * 256 + response[3];
Serial.print("CO2浓度: "); Serial.print(co2); Serial.println(" ppm");
}
}
delay(5000);
}
4.4 Modbus:工业标准,远距离传输
Modbus协议,说白了就是工业界的「普通话」。温室大棚面积大,传感器分布广,用Modbus RTU(RS485)可以传几百米。我做过一个项目,大棚长200米,用Modbus挂了30多个传感器,一点问题没有。
Modbus有两种常见形式:
- Modbus RTU:基于RS485物理层,半双工,距离可达1200米
- Modbus TCP:基于以太网,适合已经布好网线的场景
温室里最常用的是Modbus RTU。每个传感器有一个地址(1-247),主机轮询读取。数据格式是:地址 + 功能码 + 数据 + CRC校验。
避坑指南:我曾经因为RS485的A/B线接反,折腾了三个小时。记住:A接A,B接B。如果数据读不出来,先交换一下A/B试试。另外,总线两端必须加120Ω终端电阻,否则信号反射会导致数据错误。
代码示例:读取Modbus温湿度传感器
#include <ModbusMaster.h>
ModbusMaster node;
void setup() {
Serial.begin(115200);
Serial2.begin(9600, SERIAL_8N1, 16, 17); // RS485模块接在Serial2
node.begin(1, Serial2); // 传感器地址为1
}
void loop() {
uint8_t result;
uint16_t data[2];
result = node.readHoldingRegisters(0x0000, 2); // 读2个寄存器
if (result == node.ku8MBSuccess) {
float temperature = node.getResponseBuffer(0) / 10.0;
float humidity = node.getResponseBuffer(1) / 10.0;
Serial.print("温度: "); Serial.print(temperature);
Serial.print(" °C, 湿度: "); Serial.print(humidity); Serial.println(" %");
} else {
Serial.print("Modbus错误: 0x");
Serial.println(result, HEX);
}
delay(2000);
}
4.5 如何选择?我的建议
你想想看,温室里传感器那么多,到底用哪种协议?我一般这么选:
- 传感器在同一个控制箱内(1米以内):用I2C,简单省线
- 需要高速读取(比如光照传感器):用SPI,速度快
- 单个传感器,距离不远:用UART,调试方便
- 传感器分布在大棚各处(几十米到几百米):用Modbus RTU,抗干扰能力强
我的习惯:同一个项目里尽量统一协议。比如全部用I2C,或者全部用Modbus。混用的话,代码会变得很乱,调试也麻烦。除非有特殊需求,否则别给自己找事。
好了,这四种协议在温室里的应用就讲到这里。下一章咱们聊聊传感器数据融合的具体算法,怎么把多个传感器的数据揉在一起,得到更准确的结果。