#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// ==================== WiFi & ThingsBoard 配置 ====================
const char* ssid = "Wokwi-GUEST"; // Wokwi 虚拟WiFi,不改
const char* password = ""; // 留空
const char* mqtt_server = "thingsboard.cloud";
const int mqtt_port = 1883;
const char* token = "g4n1eu7cgcqow9fhmppp"; // 替换成你的设备token
// ==================== 传感器引脚定义 ====================
#define DHTPIN 15
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
#define SOIL_MOISTURE_PIN 34 // 土壤湿度模拟输入
#define LIGHT_SENSOR_PIN 35 // 光照传感器模拟输入
#define PUMP_LED 26 // 水泵指示灯(模拟水泵)
#define LIGHT_LED 27 // 补光灯指示灯
// ==================== MQTT 对象 ====================
WiFiClient espClient;
PubSubClient client(espClient);
// 定时发送变量
unsigned long lastMsg = 0;
const long interval = 3000; // 每3秒发一次
void setup() {
Serial.begin(115200);
pinMode(PUMP_LED, OUTPUT);
pinMode(LIGHT_LED, OUTPUT);
digitalWrite(PUMP_LED, LOW);
digitalWrite(LIGHT_LED, LOW);
dht.begin();
// 连接WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
// 设置MQTT服务器和回调
client.setServer(mqtt_server, mqtt_port);
client.setCallback(mqttCallback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long now = millis();
if (now - lastMsg > interval) {
lastMsg = now;
publishTelemetry();
}
}
// MQTT 回调:处理 RPC 请求(来自ThingsBoard的控制按钮)
void mqttCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
payload[length] = '\0';
String message = String((char*)payload);
Serial.println(message);
// RPC 请求主题为 v1/devices/me/rpc/request/请求ID
String topicStr = String(topic);
if (topicStr.startsWith("v1/devices/me/rpc/request/")) {
// 提取请求ID
String requestId = topicStr.substring(strlen("v1/devices/me/rpc/request/"));
// 简单解析JSON(不用大型库,手动找方法名)
if (message.indexOf("\"method\":\"setPump\"") >= 0) {
if (message.indexOf("\"params\":true") >= 0) {
digitalWrite(PUMP_LED, HIGH);
Serial.println("Pump ON");
} else if (message.indexOf("\"params\":false") >= 0) {
digitalWrite(PUMP_LED, LOW);
Serial.println("Pump OFF");
}
}
if (message.indexOf("\"method\":\"setLight\"") >= 0) {
if (message.indexOf("\"params\":true") >= 0) {
digitalWrite(LIGHT_LED, HIGH);
Serial.println("Light ON");
} else if (message.indexOf("\"params\":false") >= 0) {
digitalWrite(LIGHT_LED, LOW);
Serial.println("Light OFF");
}
}
// 回复RPC(可选,不发也行)
// client.publish(("v1/devices/me/rpc/response/"+requestId).c_str(), "{\"result\":\"ok\"}");
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP32_Plant", token, NULL)) {
Serial.println("connected");
// 订阅RPC请求主题
client.subscribe("v1/devices/me/rpc/request/+");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
delay(5000);
}
}
}
void publishTelemetry() {
// 读取温湿度
float humidity = dht.readHumidity();
float temperature = dht.readTemperature(); // 默认摄氏度
// 检查读数是否有效
if (isnan(humidity) || isnan(temperature)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
// 读取土壤湿度(0-4095 映射到 0-100%)
int soilRaw = analogRead(SOIL_MOISTURE_PIN);
int soilMoisture = map(soilRaw, 0, 4095, 100, 0); // 假设值越大越湿(可调)
// 读取光照强度(0-4095 映射到 0-100%)
int lightRaw = analogRead(LIGHT_SENSOR_PIN);
//int lightLevel = map(lightRaw, 32, 4063, 100, 0);
// 光照映射(已校准:32=最亮100%, 4063=最暗0%)
int lightLevel = map(lightRaw, 32, 4063, 100, 0); // 注意:目标范围 100→0
lightLevel = constrain(lightLevel, 0, 100);
// 构造JSON负载
String payload = "{";
payload += "\"temperature\":" + String(temperature) + ",";
payload += "\"humidity\":" + String(humidity) + ",";
payload += "\"soil_moisture\":" + String(soilMoisture) + ",";
payload += "\"light_level\":" + String(lightLevel);
payload += "}";
Serial.println("Publishing: " + payload);
client.publish("v1/devices/me/telemetry", payload.c_str());
}