3. 农业传感器原理与接口实践
农业远程监控系统的“感知层”由若干传感器构成,它们把土壤水分、空气温湿度、光照强度这些物理量转换成ESP32能够读取的电信号。本节聚焦三类最常用的农业传感器:电阻式土壤湿度传感器、DHT22数字温湿度传感器和光照传感器(以光敏电阻模块与BH1750数字光照传感器作对比),从电气特性、输出信号形式出发,完成与ESP32的硬件连接和基础采集代码。理解“信号是什么形态”是接口设计的前提——模拟量要走ADC,单总线数字量要走精确时序,I²C数字量要走总线协议,三者的接线与代码结构完全不同。
三类传感器的电气特性与输出信号
电阻式土壤湿度传感器通常由两片电极构成探头,土壤含水量越高,电极间导电性越好、等效电阻越小。常见模块(如YL-69配合LM393比较器)输出两路信号:AO为模拟电压,DO为比较器输出的数字高低电平。以3.3V供电时,干燥空气中AO输出接近供电电压,插入湿润土壤后AO电压下降。需要注意,这类传感器长期通电会产生电化学腐蚀,实践中建议“测量时供电、测完断电”,或选用电容式土壤湿度传感器(输出0~3.0V模拟电压,抗腐蚀性更好)。ESP32的ADC为12位,理论量程0~4095,但实际存在非线性,且Wi-Fi发射时ADC2通道会被占用,因此土壤湿度应接在ADC1通道(GPIO32~GPIO39)。
DHT22(AM2302)是数字式温湿度传感器,供电3.3~5.5V,湿度量程0~100%RH、精度约±2%RH,温度量程-40~80℃、精度约±0.5℃。它采用单总线协议:主机拉低总线至少1ms作为起始信号,随后释放总线,DHT22应答后连续输出40位数据——湿度高8位、湿度低8位、温度高8位、温度低8位、8位校验和。每一位以50μs低电平开始,高电平持续26~28μs表示“0”,持续70μs表示“1”。这意味着读取时序对微秒级延时敏感,且两次读取间隔不得小于2秒。数据引脚需接4.7kΩ~10kΩ上拉电阻,ESP32侧任选一个普通GPIO即可。
光照检测有两种典型方案。光敏电阻模块输出模拟电压,光照越强电阻越小,分压点电压随光照变化,适合做“白天/夜晚”或“遮阳/补光”的阈值判断,但线性度和一致性较差。BH1750是I²C数字光照传感器,直接输出以勒克斯(lx)为单位的光照度,量程1~65535lx,地址由ADDR引脚决定(接地为0x23,接VCC为0x5C),适合需要定量数据的场景。农业大棚中,光照度直接关联光合作用速率,定量值比“亮/暗”更有决策价值。
硬件连接与供电注意事项
接线前先统一电平与供电。ESP32开发板的3.3V引脚可同时为DHT22和BH1750供电,但土壤湿度模块和光敏电阻模块的加热/分压电路耗流较大,若同时挂载多个模块,建议用外部5V电源经AMS1117稳压到3.3V单独供电,并与ESP32共地。所有模拟输出引脚不得超过3.3V,否则需加分压电阻。DHT22数据线接GPIO4并加4.7kΩ上拉到3.3V;BH1750的SDA接GPIO21、SCL接GPIO22,两根线上各加4.7kΩ上拉(多数模块已板载)。土壤湿度AO接GPIO34,光敏电阻AO接GPIO35,二者都属于ADC1,避开Wi-Fi冲突。
基础采集代码
下面给出一个可编译的Arduino框架示例,把三类传感器的读取封装成独立函数,主循环按不同周期调度:土壤湿度每2秒采一次并做多次平均,DHT22严格保持2秒以上间隔,BH1750每1秒读一次。代码中的阈值和换算系数属于示例值,实际使用前应通过标定替换。
#include <DHT.h>
#include <Wire.h>
#include <BH1750.h>
#define SOIL_PIN 34
#define LIGHT_PIN 35
#define DHT_PIN 4
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
BH1750 bh1750(0x23);
// 示例标定值:需按实际探头在空气/水中测得的ADC值替换
const int SOIL_DRY = 3200; // 干燥空气 ADC 读数(示例)
const int SOIL_WET = 1300; // 浸水 ADC 读数(示例)
int readSoilPercent() {
long sum = 0;
for (int i = 0; i < 8; i++) { sum += analogRead(SOIL_PIN); delay(5); }
int raw = sum / 8;
int pct = map(raw, SOIL_DRY, SOIL_WET, 0, 100);
return constrain(pct, 0, 100);
}
void setup() {
Serial.begin(115200);
analogReadResolution(12);
dht.begin();
Wire.begin(21, 22);
bh1750.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
}
void loop() {
int soil = readSoilPercent();
float t = dht.readTemperature();
float h = dht.readHumidity();
float lux = bh1750.readLightLevel();
if (isnan(t) || isnan(h)) {
Serial.println("DHT22 读取失败,检查上拉与接线");
} else {
Serial.printf("土壤:%d%% 温度:%.1fC 湿度:%.1f%% 光照:%.0flx\n",
soil, t, h, lux);
}
delay(2000); // 满足 DHT22 最小采样间隔
}
代码中有几个容易踩坑的点。第一,analogReadResolution(12)必须在读取前调用,否则默认可能是10位。第二,map()函数的两个端点顺序决定了输出方向:本例中干燥读数大、湿润读数小,所以把SOIL_DRY放在第一个参数位置,输出才会“越湿百分比越大”。第三,DHT22读取失败返回NaN,必须用isnan()判断,不能直接参与运算。第四,BH1750的CONTINUOUS_HIGH_RES_MODE分辨率1lx、测量周期约120ms,适合连续采集。
标定与结果判读
土壤湿度的百分比没有绝对标准,必须现场标定。做法是:把探头置于干燥空气中静置,串口记录稳定后的ADC读数作为“干端”;再把探头插入水中(不要淹没电路部分),记录稳定读数作为“湿端”。把这两个值填入SOIL_DRY和SOIL_WET,百分比才有意义。判断采集是否正常,可以看三条依据:数值是否在合理区间内波动(土壤湿度0~100%、温度与室温接近、光照白天明显高于夜晚);连续读取是否稳定(同一环境下波动不超过几个百分点);异常时是否有明确报错(DHT22返回NaN、BH1750返回负值说明总线通信失败)。若DHT22持续失败,优先检查上拉电阻和GPIO是否被其他功能占用;若BH1750读数为负,用I²C扫描确认地址是否为0x23。
完成本节后,读者应能根据传感器输出信号类型选择正确的ESP32引脚与读取方式,独立完成接线、编写采集代码,并通过串口输出判断数据是否可信。这套“感知层”采集能力,是后续数据上传、阈值报警与远程控制的基础。