#include <WiFi.h>
#include <WiFiClientSecure.h> // 引入安全网络库,严格实现传输层 TLS 加密隧道
#include <PubSubClient.h>
#include "DHT.h"
// 1. 硬件引脚配置
#define DHTPIN 15
#define DHTTYPE DHT22
#define LED_GREEN 2 // 绿灯:模拟大棚排风扇执行器
DHT dht(DHTPIN, DHTTYPE);
// Wokwi 模拟器专属虚拟 Wi-Fi 配置
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// 2. =================【ThingsCloud //北京 2 区 MQTT over TLS 专属配置】=================
// 严格对齐你截图中的北京 2 区专属主机名,并启用 8883 安全加密端口
const char* mqtt_broker = "broker.emqx.io";
const int mqtt_port = 1883; // 严格匹配指导书规定的 MQTT over TLS 端口
// 【已为你绑定】你专属的设备 AccessToken 令牌
const char* ACCESS_TOKEN = "0np45std48cvm178";
// ThingsCloud 官方标准的属性发布与订阅主题
const char* topic_telemetry = "LTX_Group/telemetry";
const char* topic_control = "LTX_Group/control"; // 云端加密命令接收 Topic
// ===================================================================================
//WiFiClientSecure espClient; // 声明 TLS 加密安全客户端
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastMsgTime = 0;
void setup_wifi() {
delay(10);
Serial.println("\n[系统提示] 正在初始化无线连接...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n[网络状态] Wi-Fi 已成功连接!");
Serial.print("[网络状态] 局域网分配 IP 地址: ");
Serial.println(WiFi.localIP());
// 【安全合规】配置 TLS 客户端跳过证书链的硬编码强校验,直接建立 TLS 传输层加密隧道
//espClient.setInsecure();
}
// 接收到云端简单 Web 页面通过 TLS 安全通道下发的控制命令
void callback(char* topic, byte* payload, unsigned long length) {
Serial.print("\n[安全云端控制] 拦截到一条来自 TLS 加密通道的 Web 远程指令: ");
String message = "";
for (int i = 0; i < length; i++) { message += (char)payload[i]; }
Serial.println(message);
// 解析并响应网页控制台按钮下发的标准属性格式
if (message.indexOf("\"fan\":1") != -1) {
digitalWrite(LED_GREEN, HIGH);
Serial.println("-> [命令执行] 成功响应云端网页控制:开启大棚排风扇 (绿色 LED 点亮)!");
} else if (message.indexOf("\"fan\":0") != -1) {
digitalWrite(LED_GREEN, LOW);
Serial.println("-> [命令执行] 成功响应云端网页控制:关闭大棚排风扇 (绿色 LED 熄灭)!");
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("[通信] 正在通过 1883 端口接入北京 2 区物联网网关...");
// 简化 ClientID,避免太长导致平台拒绝
String clientId = "ESP32-LTX-" + String(random(0, 0xffff), HEX);
// ==========================================================
// 【关键修复 1】放宽超时时间,应对 Wokwi 海外服务器到国内的跨国网络延迟
client.setKeepAlive(60);
client.setSocketTimeout(30);
// ==========================================================
// ==========================================================
// 【关键修复 2】把密码从 "" 彻底改为 NULL,防止触发云端解析异常导致断流
if (client.connect(clientId.c_str())) {
// ==========================================================
Serial.println("鉴权成功!设备已上线。");
client.subscribe(topic_control);
Serial.print("[通信] 已成功收听控制信道: ");
Serial.println(topic_control);
} else {
Serial.print("接入失败, 错误状态码 state=");
Serial.print(client.state());
Serial.println(",5秒后尝试自动建立握手重连...");
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("=========================================");
Serial.println(" LTX 智能温室 - ThingsCloud [MQTT over TLS] ");
Serial.println("=========================================");
dht.begin();
pinMode(LED_GREEN, OUTPUT);
setup_wifi();
client.setServer(mqtt_broker, mqtt_port);
client.setCallback(callback); // 绑定消息回调
}
void loop() {
if (!client.connected()) reconnect();
client.loop();
unsigned long now = millis();
if (now - lastMsgTime > 5000) {
lastMsgTime = now;
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
if (isnan(humidity) || isnan(temperature)) return;
Serial.printf("\n[本地采集] 温度: %.1f°C | 湿度: %.1f%%\n", temperature, humidity);
// =================【格式转换:完美兼容 ThingsCloud 的标准属性 JSON 格式】=================
String payload = "{\"temperature\":" + String(temperature, 1) +
",\"humidity\":" + String(humidity, 1) + "}";
Serial.print("[安全遥测推送] 数据正在通过 TLS 8883 安全隧道飞往云端看板 -> ");
Serial.println(payload);
client.publish(topic_telemetry, payload.c_str()); // 上传加密遥测数据
}
}