2. 传感器数据采集:温湿度传感器(DHT22)、光照传感器(BH1750)、风速风向传感器、雨量传感器,通过ESP32采集数据
好,咱们进入实战的第二关。
传感器选好了,板子也定了,接下来就是真刀真枪地让ESP32把数据读上来。这一章我会带着你,把四个典型的农业传感器挨个搞定。DHT22、BH1750、风速风向、雨量传感器,一个都不能少。
我个人习惯,先把每个传感器的脾气摸透,再写代码。你想想看,传感器就像人一样,有的用单总线通信,有的走I2C,有的就是个简单的开关量。不了解清楚,代码写得再漂亮也白搭。
2.1 温湿度传感器 DHT22
DHT22,也叫AM2302。精度比DHT11高不少,温度±0.5°C,湿度±2%RH。农业大棚里用,完全够用。
它用的是单总线协议,说白了就是一根数据线既发命令又收数据。时序要求比较严格,我建议直接用现成的库,别自己折腾时序——我在项目中吃过这个亏,自己写时序,结果不同批次的DHT22时序有细微差异,折腾了两天才发现是库的问题。
- 供电电压:3.3V ~ 5.5V
- 测量范围:温度 -40~80°C,湿度 0~100%RH
- 采样周期:≥2秒
接线很简单:VCC接3.3V,GND接地,DATA接一个GPIO(比如GPIO4)。注意,DATA引脚需要上拉一个4.7kΩ电阻到VCC。嗯,这里要注意,有些开发板内部有上拉,但为了稳定,我建议还是外接一个。
#include <DHT.h>
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200);
dht.begin();
}
void loop() {
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("读取DHT22失败!");
return;
}
Serial.print("湿度: ");
Serial.print(h);
Serial.print(" %\t");
Serial.print("温度: ");
Serial.print(t);
Serial.println(" *C");
delay(2000);
}
2.2 光照传感器 BH1750
BH1750是个数字光照传感器,直接输出lux值。它用I2C通信,地址默认是0x23(ADDR引脚接地),也可以改成0x5C(ADDR接VCC)。
我特别喜欢这个传感器,因为它不需要校准,拿来就用。不像某些模拟光照传感器,还得自己算公式,麻烦得很。
- VCC → 3.3V
- GND → GND
- SCL → GPIO22 (ESP32默认I2C时钟)
- SDA → GPIO21 (ESP32默认I2C数据)
- ADDR → GND (地址0x23)
#include <Wire.h>
#include <BH1750.h>
BH1750 lightMeter;
void setup() {
Serial.begin(115200);
Wire.begin();
lightMeter.begin();
}
void loop() {
float lux = lightMeter.readLightLevel();
Serial.print("光照强度: ");
Serial.print(lux);
Serial.println(" lx");
delay(1000);
}
这里有个小细节:BH1750有几种测量模式,连续高分辨率模式最常用,精度1lx。如果你需要快速响应,可以用连续低分辨率模式,精度4lx,但测量时间从120ms降到16ms。农业场景下,我建议用高分辨率模式,数据更平滑。
2.3 风速风向传感器
风速风向传感器,市面上常见的有两种:一种是模拟输出的,一种是脉冲输出的。
模拟输出的风速传感器,输出0-5V电压,对应0-30m/s。风向传感器通常是8个或16个方向,也是模拟电压输出。ESP32的ADC是0-3.3V,所以需要分压电阻或者用ADS1115外部ADC。
脉冲输出的风速传感器,每转一圈输出一个脉冲。比如三杯风速计,每转一圈对应一个脉冲频率。这种传感器,用ESP32的脉冲计数功能(PCNT)或者外部中断来测量频率,再换算成风速。
// 脉冲式风速传感器示例
// 假设每转一圈产生一个脉冲,风速 = 频率 * 0.1 (m/s)
volatile unsigned long pulseCount = 0;
unsigned long lastTime = 0;
void IRAM_ATTR pulseCounter() {
pulseCount++;
}
void setup() {
Serial.begin(115200);
pinMode(5, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(5), pulseCounter, RISING);
}
void loop() {
if (millis() - lastTime >= 1000) {
noInterrupts();
unsigned long count = pulseCount;
pulseCount = 0;
interrupts();
float freq = count; // 每秒脉冲数
float windSpeed = freq * 0.1; // 换算系数根据传感器手册调整
Serial.print("风速: ");
Serial.print(windSpeed);
Serial.println(" m/s");
lastTime = millis();
}
}
2.4 雨量传感器
雨量传感器,农业上常用的是翻斗式雨量计。原理很简单:雨水积满一个小斗,翻倒一次,产生一个脉冲。每个脉冲代表0.5mm或1mm的降雨量。
还有一种电容式雨量传感器,输出模拟电压,但精度不如翻斗式。我个人更推荐翻斗式,机械结构简单,不容易坏。
- 翻斗式雨量计:两根线,一根接GPIO,一根接GND
- 内部是干簧管,需要上拉电阻到3.3V
- 每个脉冲代表0.5mm降雨量(具体看传感器规格)
// 翻斗式雨量计示例
// 每个脉冲代表0.5mm降雨量
volatile unsigned long rainCount = 0;
unsigned long lastRainTime = 0;
void IRAM_ATTR rainDetected() {
// 去抖处理,防止机械抖动
if (millis() - lastRainTime > 100) {
rainCount++;
lastRainTime = millis();
}
}
void setup() {
Serial.begin(115200);
pinMode(14, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(14), rainDetected, FALLING);
}
void loop() {
// 每10秒上报一次累计雨量
static unsigned long lastReport = 0;
if (millis() - lastReport >= 10000) {
noInterrupts();
unsigned long count = rainCount;
rainCount = 0;
interrupts();
float rainfall = count * 0.5; // 单位mm
Serial.print("10秒内降雨量: ");
Serial.print(rainfall);
Serial.println(" mm");
lastReport = millis();
}
}
2.5 多传感器整合采集
四个传感器都单独调通了,接下来就是整合。我建议用定时器,每2秒采集一次DHT22和BH1750,每1秒采集一次风速,雨量用中断累计,每10分钟上报一次累计值。
// 整合采集框架
void collectAllSensors() {
// 1. 采集温湿度
float temp = dht.readTemperature();
float hum = dht.readHumidity();
// 2. 采集光照
float lux = lightMeter.readLightLevel();
// 3. 采集风速(从中断累计值计算)
float windSpeed = getWindSpeed();
// 4. 雨量累计(从中断累计值计算)
float rainfall = getRainfall();
// 5. 打包成JSON或结构体,准备上传
// 这部分下一章会详细讲
}
这里有个坑:DHT22的读取间隔必须大于2秒,否则会出错。BH1750连续模式没问题,但如果你用单次模式,也要注意间隔。我建议把DHT22放在主循环里,每2.5秒读一次,其他传感器可以更频繁。
好了,四个传感器的数据采集,咱们都过了一遍。下一章,我会讲怎么把这些数据打包成标准格式,通过WiFi或者4G模块上传到云端。到时候,你就能在手机或电脑上实时看到大棚里的环境数据了。
记住,传感器采集是物联网项目的地基。地基不稳,上层建筑再漂亮也没用。多花点时间在传感器调试上,后面会省很多事。