ESP32 + 传感器:手把手教你做土壤监测终端
ESP32 + 传感器:手把手教你做土壤监测终端
上一篇给了硬件清单,这篇直接上代码。从 GPIO 初始化到传感器读数,再到 MQTT 上发,最后低功耗优化——每一行都有注释,复制到 Arduino IDE 里就能跑。
前置准备
软件环境:
- Arduino IDE 2.x(装好 ESP32 开发板支持)
- 库:
WiFiPubSubClientDHT sensor libraryOneWireDallasTemperatureBH1750ModbusMaster
硬件接线: 按上一篇的引脚表接好,USB 线插电脑。
第一步:基础采集——五个传感器一把梭
#include <WiFi.h>
#include <DHT.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <BH1750.h>
#include <Wire.h>
#include <ModbusMaster.h>
// ==================== 引脚定义 ====================
#define DHT_PIN 25
#define ONE_WIRE_PIN 4
#define RS485_DE_RE 18 // MAX485 方向控制引脚
#define SOIL_SENSOR_1 32 // ADC1 读模拟量土壤湿度(备选方案)
// ==================== 传感器对象 ====================
DHT dht(DHT_PIN, DHT22);
OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature soilTempSensor(&oneWire);
BH1750 lightMeter;
ModbusMaster modbus;
// ==================== 数据结构 ====================
struct SensorData {
float airTemp; // 空气温度 ℃
float airHumidity; // 空气湿度 %
float soilTemp; // 土壤温度 ℃
float soilMoisture; // 土壤湿度 %(RS485 读数)
float lightLux; // 光照强度 Lux
};
// ==================== 初始化 ====================
void setup() {
Serial.begin(115200);
// DHT22
dht.begin();
// DS18B20 土壤温度
soilTempSensor.begin();
// BH1750 光照
Wire.begin(21, 22); // SDA=21, SCL=22
lightMeter.begin();
// RS485 (Serial2: RX=16, TX=17)
Serial2.begin(9600, SERIAL_8N1, 16, 17);
modbus.begin(1, Serial2); // 从机地址 = 1
pinMode(RS485_DE_RE, OUTPUT);
Serial.println("=== 土壤监测终端启动 ===");
}
// ==================== RS485 读土壤湿度 ====================
float readSoilMoistureRS485() {
// 切换到发送模式
digitalWrite(RS485_DE_RE, HIGH);
delay(10);
// 读保持寄存器 0x0000(湿度值),读 1 个寄存器
uint8_t result = modbus.readHoldingRegisters(0x0000, 1);
// 切回接收模式
digitalWrite(RS485_DE_RE, LOW);
if (result == modbus.ku8MBSuccess) {
return modbus.getResponseBuffer(0) / 10.0; // 传感器返回 0-1000,除以 10
}
return -1.0; // 读取失败
}
// ==================== 读土壤温度(DS18B20) ====================
float readSoilTemp() {
soilTempSensor.requestTemperatures();
float temp = soilTempSensor.getTempCByIndex(0);
// DS18B20 在 -127 时表示读取异常
if (temp == DEVICE_DISCONNECTED_C) return -127;
return temp;
}
// ==================== 采集全部数据 ====================
SensorData collectAll() {
SensorData data;
// DHT22 空气温湿度
data.airTemp = dht.readTemperature();
data.airHumidity = dht.readHumidity();
// DS18B20 土壤温度
data.soilTemp = readSoilTemp();
// RS485 土壤湿度
data.soilMoisture = readSoilMoistureRS485();
// BH1750 光照
data.lightLux = lightMeter.readLightLevel();
return data;
}
void loop() {
SensorData data = collectAll();
Serial.println("════════════════════════════");
Serial.printf("空气温度: %.1f ℃\n", data.airTemp);
Serial.printf("空气湿度: %.1f %%\n", data.airHumidity);
Serial.printf("土壤温度: %.1f ℃\n", data.soilTemp);
Serial.printf("土壤湿度: %.1f %%\n", data.soilMoisture);
Serial.printf("光照强度: %.0f Lux\n", data.lightLux);
delay(5000); // 调试阶段 5s 一次,实际部署改 5min
}
这段代码用 Arduino IDE 编译下载到 ESP32,打开串口监视器(115200 波特率),你应该能看到每隔 5 秒打印一组传感器读数。
第二步:接上 MQTT,数据上云端
光串口打印没意义,数据得上云。MQTT 部分只需要在 setup() 里连 WiFi + 连 EMQX,在 loop() 里把采集到的数据 publish 出去。
#include <PubSubClient.h>
// ==================== WiFi & MQTT 配置 ====================
const char* WIFI_SSID = "你的WiFi名";
const char* WIFI_PASS = "你的WiFi密码";
const char* MQTT_BROKER = "你的服务器IP";
const int MQTT_PORT = 1883;
const char* MQTT_CLIENT_ID = "esp32_greenhouse_01";
const char* MQTT_TOPIC = "farm/greenhouse/01/data";
WiFiClient espClient;
PubSubClient mqtt(espClient);
// 连接 WiFi
void connectWiFi() {
WiFi.begin(WIFI_SSID, WIFI_PASS);
Serial.print("连接 WiFi");
int retry = 0;
while (WiFi.status() != WL_CONNECTED && retry < 40) {
delay(500);
Serial.print(".");
retry++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi 已连接");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
}
// 连接 MQTT
void connectMQTT() {
while (!mqtt.connected()) {
Serial.print("连接 MQTT...");
if (mqtt.connect(MQTT_CLIENT_ID)) {
Serial.println("成功");
} else {
Serial.print("失败, rc=");
Serial.print(mqtt.state());
Serial.println(" 5 秒后重试");
delay(5000);
}
}
}
// 发送 JSON 数据(不用 ArduinoJson 库也行,手动拼)
void publishData(SensorData data) {
char payload[256];
snprintf(payload, sizeof(payload),
"{\"t\":\"%.1f\",\"h\":\"%.1f\",\"st\":\"%.1f\",\"sm\":\"%.1f\",\"l\":\"%.0f\"}",
data.airTemp, data.airHumidity, data.soilTemp, data.soilMoisture, data.lightLux
);
if (mqtt.publish(MQTT_TOPIC, payload)) {
Serial.println("✅ 数据已上发");
} else {
Serial.println("❌ 发送失败");
}
}
void setup() {
Serial.begin(115200);
// ... 传感器初始化(同上,省略)...
connectWiFi();
mqtt.setServer(MQTT_BROKER, MQTT_PORT);
connectMQTT();
}
void loop() {
if (!mqtt.connected()) connectMQTT();
mqtt.loop();
SensorData data = collectAll();
publishData(data);
delay(300000); // 5 分钟上报一次
}
到这里,你的 ESP32 已经能做到:每 5 分钟采集一组数据 → JSON 格式化 → MQTT 上发到 EMQX。
第三步:低功耗优化——一节电池用半年
ESP32 全速跑起来大约 150-200mA,18650 一节 3400mAh 撑不到 24 小时。必须做低功耗优化。
思路
ESP32 大部分时间在睡觉(深度睡眠模式,电流 ~10μA),定时醒来(RTC 闹钟),花 5 秒采数据 + 连 WiFi + 发 MQTT,然后倒头接着睡。
时间线:
┌───── 睡眠(295s) ─────┬─ 唤醒(5s) ─┐
│ 约 10μA │ WiFi+采集+发送 │
└───────────────────────┴──────────────┘
平均电流 ≈ (295*0.01 + 5*150) / 300 = 2.5mA
一节 3400mAh → 约 56 天
加太阳能板 + TP4056 充电,白天充电晚上耗电,基本无限续航。
#define uS_TO_S_FACTOR 1000000ULL
#define SLEEP_SECONDS 300 // 5 分钟
void setup() {
// ... 前面初始化 ...
// 采集一次并上报
SensorData data = collectAll();
publishData(data);
// 断开 WiFi(省电)
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
// 配置唤醒源:定时器
esp_sleep_enable_timer_wakeup(SLEEP_SECONDS * uS_TO_S_FACTOR);
Serial.println("进入深度睡眠...");
esp_deep_sleep_start();
}
void loop() {
// 深度睡眠模式下永远不会执行到这里
// setup() 执行完后 ESP32 自动关机,RTC 闹钟触发后重启 → 再次进 setup()
}
注意: 用了深度睡眠后,setup() 就是你的主循环——每次醒来跑一遍,跑完就睡。loop() 成了摆设。
进一步省电技巧
- 去掉串口打印:
Serial.begin()和Serial.print()在部署时全删掉,能省 5-10mA - WiFi 快速连接:提前存 WiFi 信道,连接时间从 3 秒降到 1 秒
- 静态 IP:DHCP 握手省 0.5 秒
- MQTT QoS 0:不等待服务器确认,发完就走
优化后平均电流可以压到 1.5mA 以下,一节 18650 撑 90+ 天。
常见问题
Q: RS485 读数总是 -1(失败)?
A: 检查 DE/RE 引脚电平——发送时拉高、接收时拉低。时序:拉高后要 delay(10) 再发指令。另外确认 MAX485 的 RO/DI 交叉接对。
Q: DHT22 偶尔返回 NaN?
A: DHT22 的单总线时序很敏感,读取间隔不能短于 2 秒。如果连续读太快,加 delay(2500) 或者换 DHT22 库里带超时处理的版本。
Q: 太阳能板阴天能充进去吗?
A: 能,但很慢。5V 1W 的多晶硅在阴天输出只有 0.1-0.3W。如果你在四川、贵州这种阴天多的地区,建议上 2W 的板子或者两片并联。
下一篇:《MQTT 物联网通信:100 个传感器的数据怎么传到云端?》——部署 EMQX、设计 topic 规范、设备上下线监控、断网重连机制。
更多推荐
所有评论(0)