09-智慧农业传感器采集:温湿度、土壤EC、PH、光照、CO2数据解析
搞智慧农业的朋友应该都有同感:大棚里一排排传感器,天天吐数据,可你拿到的是一串十六进制——01 03 02 01 2C B9 B2,这玩意儿到底代表几度?几克?几 lux?
别急,这篇就把智慧农业最常见的传感器挨个盘一遍,从寄存器映射到数据解析,把"原始值 → 工程量"的换算公式和代码全给你,让你从"读报文"进化到"看得懂数据"。
一、先认识智慧农业的"感觉器官"
大棚环境监控,无外乎"空气、土壤、光照、气象"四大类:
| 传感器 | 测量物理量 | 典型量程 | 常见单位 | 通讯方式 |
|---|---|---|---|---|
| 空气温湿度 | 温度/相对湿度 | -40 | ℃ / %RH | RS485 Modbus |
| 土壤温湿度 | 土壤温度/体积含水率 | -40 | ℃ / % | RS485 Modbus |
| 土壤EC | 电导率(肥力) | 0~10000 | uS/cm | RS485 Modbus |
| 土壤PH | 酸碱度 | 0~14 | pH | RS485 Modbus |
| 光照强度 | 光合有效辐射 | 0~200000 | Lux | RS485 Modbus |
| CO2浓度 | 二氧化碳 | 0~5000 | ppm | RS485 Modbus |
| 风速风向 | 风速/风向角 | 0 | m/s / ° | RS485 Modbus |
| 雨量 | 降雨量 | 0~9999 | mm | RS485 Modbus |
看出规律没?RS485 + Modbus 几乎是农业传感器的"普通话"。原因很简单:成本低、抗干扰、一根双绞线可以挂几十个设备,大棚布线友好。
二、寄存器映射:传感器怎么"开口说话"
每个传感器相当于一个小 PLC,内部有寄存器。Modbus 主站(通常是边缘网关或 PLC)通过 功能码03 读保持寄存器 或 04 读输入寄存器 去问它。
举个例子,某品牌"空气温湿度二合一"传感器的寄存器表:
| 寄存器地址(十进制) | 寄存器内容 | 数据类型 | 说明 |
|---|---|---|---|
| 0x0000 | 空气温度 | int16 | 有符号,缩放0.01,单位℃ |
| 0x0001 | 空气湿度 | uint16 | 无符号,缩放0.01,单位%RH |
| 0x0002 | 露点温度 | int16 | 有符号,缩放0.01,单位℃ |
| 0x0003 | 校验值 | uint16 | 设备状态字 |
关键点:几乎所有农业传感器都是"整数存储 + 缩放系数"的模式。因为 Modbus 寄存器是 16 位整数,没法直接装小数,厂商就用"乘个系数"把小数变成整数存进去,你在解析时再除回来。
这就是新手最常踩的坑:寄存器读回来
0x012C,直接当成 300 用,结果大棚温度 300℃ 把植物都烤没了。实际上要除以 100,是 3.00℃。凡事先看文档里的缩放系数!
三、数据解析实战:从原始值到工程量
1. 温湿度(有符号16位,缩放0.01)
假设读温度寄存器返回 0xFF6C:
0xFF6C = 65452(无符号视角)
有符号解析:0xFF6C 首位为1,负数。
补码转真值:65452 - 65536 = -84
工程量 = -84 × 0.01 = -0.84℃
Python 实现:
def parse_int16(raw: int) -> int:
"""把寄存器原始值转成有符号16位整数"""
if raw >= 0x8000: # 最高位为1表示负数
return raw - 0x10000
return raw
temp_raw = 0xFF6C
temp_c = parse_int16(temp_raw) * 0.01
print(f"温度: {temp_c:.2f} ℃") # 输出: -0.84 ℃
湿度一般是无符号,直接除系数:
hum_raw = 0x1F40 # 8000
hum_rh = hum_raw * 0.01 # 80.00 %RH
2. 土壤EC(uS/cm 换算)
EC(Electrical Conductivity)电导率,反映土壤里可溶性盐离子浓度,是判断"该不该施肥"的核心指标。常见寄存器存的就是 uS/cm 原始值,缩放系数 1(即无需缩放),有些老款传感器按 0.1 缩放。
换算公式(uS/cm → mS/cm,业界更爱用 mS/cm):
EC(mS/cm) = EC(uS/cm) / 1000
ec_raw = 0x08FC # 2300
ec_us = ec_raw * 1.0 # 2300 uS/cm
ec_ms = ec_us / 1000.0 # 2.3 mS/cm
print(f"土壤电导率: {ec_ms:.2f} mS/cm")
农业经验值参考:蔬菜大田适宜 EC 大约 1.0~2.5 mS/cm,超过 3 说明盐分过高,要控制施肥了。
3. PH(0.01 精度)
PH 寄存器通常是 uint16,缩放 0.01。即 0x0341 = 833 → PH 8.33。
ph_raw = 0x0341 # 833
ph_val = ph_raw * 0.01 # 8.33
注意:有些廉价 PH 传感器用 0.1 缩放,0x0321=801 → 8.0。同型号看文档,不同厂商别硬套同一个系数。
4. 光照强度(Lux)
光照传感器量程差异巨大,低配 020000 Lux,高配 0200000 Lux。寄存器存 uint32 的也不少见(跨两个寄存器),读取时注意高位寄存器在前。
# 两个寄存器组成 uint32,高位在前
hi_raw = 0x0003 # 寄存器0
lo_raw = 0xD090 # 寄存器1
lux = (hi_raw << 16) | lo_raw # 250000
print(f"光照强度: {lux} Lux")
光照单位换算别踩坑:
Lux(照度)和umol/(m²·s)(光合有效辐射 PAR)是两个东西,转换系数约 0.0185(依光源光谱而定),直接拿 Lux 当 PAR 算光补偿点会得出离谱结论。
5. CO2(ppm)
CO2 传感器一般 05000 ppm,uint16 直接存储,缩放 1。大棚 CO2 浓度 350800 ppm 属于正常范围,光合作用最适浓度 1000~1500 ppm。
co2_raw = 0x03E8 # 1000
co2_ppm = co2_raw # 1000 ppm
四、通用解析代码模板
把上面逻辑收拢成一个通用函数,覆盖 "有符号/无符号 + 缩放系数" 两种最常见的组合:
def reg_to_eng(raw: int, scale: float = 1.0,
signed: bool = False, signed_bits: int = 16) -> float:
"""
寄存器原始值 -> 工程量
raw: 寄存器读回的整数
scale: 缩放系数,如 0.01
signed: 是否有符号
"""
val = raw
if signed and raw >= (1 << (signed_bits - 1)):
val = raw - (1 << signed_bits)
return val * scale
# 例子
print(reg_to_eng(0xFF6C, 0.01, signed=True)) # -0.84
print(reg_to_eng(0x1F40, 0.01)) # 80.0
C 语言版本(嵌入式网关常用):
#include <stdint.h>
#include <stdio.h>
// 16位有符号
int16_t raw_int16(uint16_t raw) {
return (int16_t)raw; // C里强转即可完成补码解释
}
// 32位浮点:两个寄存器拼成IEEE754
float regs_to_float(uint16_t hi, uint16_t lo) {
uint32_t bits = ((uint32_t)hi << 16) | lo;
float f;
memcpy(&f, &bits, sizeof(f));
return f;
}
int main(void) {
printf("%.2f\n", raw_int16(0xFF6C) * 0.01f); // -0.84
return 0;
}
五、异常值识别:别让"假数据"毁了自动控制
传感器解析出来是一回事,值可不可信是另一回事。农业生产系统里,一个假数据可能导致自动灌溉误动作——半夜给作物淹了水。几个必须做的检查:
1. 超量程检查
物理世界给传感器返回值设了边界,超了就基本可判定异常:
| 物理量 | 合理范围 | 可疑值 |
|---|---|---|
| 空气温度 | -40~80℃ | < -50 或 > 100 |
| 湿度 | 0~100%RH | > 100 |
| PH | 0~14 | < 0 或 > 14 |
| CO2 | 350~5000 ppm | < 300 或 > 10000 |
| 光照 | 0~200000 Lux | 负数(不可能) |
2. 特征值识别传感器故障
很多厂商在故障时返回特殊值:
0x8000/0xFFFF:很多国产传感器读不到物理量时回0xFFFF(-1),解析后是离谱负数,注意识别;- 数据长时间恒定不变:湿度传感器探头进水、结露后可能"死数据",连续 10 次轮询值完全相同且超出物理合理范围,标记为故障;
- EC 突然跳 0 且持续:探头可能开路,或没插入土壤。
3. 变化速率判断(工程级兜底)
温度两分钟内从 20℃ 变到 60℃,这在温室里物理上不可能(除非着火)。加一阶差分限幅滤波:
PREV = None
MAX_DELTA = {"temp": 5.0, "hum": 15.0} # 每轮询周期的最大合理变化量
def sanity_check(name: str, val: float):
global PREV
if PREV is None:
PREV = val
return True
if abs(val - PREV) > MAX_DELTA[name]:
print(f"[告警] {name} 变化过快: {PREV} -> {val}")
return False
PREV = val
return True
六、小结
一句话总结解析套路:读懂寄存器表 → 注意缩放系数 → 注意有符号 → 补上合理性校验。温湿度、EC、PH、光照、CO2 这五类数据在智慧农业里占比 80% 以上,把这套公式记熟,再遇到陌生传感器也就是"查文档换系数"的事。数据能可靠上来了,下一步自然是让设备"动起来"——控制执行器,那是另一个方向的实战。