// 适配Wokwi ESP32-C3仿真的供应链风险追踪系统代码
// 支持DHT11温湿度采集、虚拟GPS解析、MQTT上传(腾讯云/仿真)
#include <DHT.h>
#include <TinyGPS++.h>
#include <PubSubClient.h>
#include <SoftwareSerial.h>
#include <WiFi.h>
// ===================== 1. 硬件引脚配置(Wokwi仿真固定) =====================
#define DHTPIN 4 // DHT11接GPIO4
#define DHTTYPE DHT11
// Wokwi虚拟GPS模块串口(RX=17, TX=16)
SoftwareSerial gpsSerial(17, 16);
TinyGPSPlus gps;
DHT dht(DHTPIN, DHTTYPE);
// ===================== 2. Wokwi仿真配置(必改!) =====================
// 1. WiFi配置(Wokwi支持虚拟WiFi,填任意有效值即可)
const char* ssid = "Wokwi-Fi"; // Wokwi虚拟WiFi名称(固定)
const char* password = "password"; // Wokwi虚拟WiFi密码(固定)
// 2. 腾讯云MQTT配置(真实上传需改,纯仿真可保留默认)
const char* mqttServer = "iotcloud-mqtt.gz.tencentdevices.com"; // 腾讯云MQTT服务器
const int mqttPort = 1883;
const char* clientId = "your-device-name"; // 替换为你的DeviceName
const char* mqttUser = "your-product-key&your-device-name"; // 替换为你的ProductKey&DeviceName
const char* mqttPwd = "your-mqtt-password"; // 替换为你的MQTT密码
// 3. MQTT Topic(替换为你的腾讯云Topic)
#define TOPIC_TEMP_HUMI "your-product-key/your-device-name/data/temperature_humidity"
#define TOPIC_GPS "your-product-key/your-device-name/data/gps"
// ===================== 3. 全局变量 =====================
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastCollectTime = 0; // 分钟级采集计时
// Wokwi虚拟GPS模拟数据(备用,若虚拟模块无输出时启用)
const char* fakeGpsData = "$GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62";
// ===================== 4. 函数声明 =====================
void connectWiFi();
void reconnectMQTT();
void collectAndUploadData();
void simulateGpsData(); // Wokwi虚拟GPS补全函数
// ===================== 5. 初始化函数 =====================
void setup() {
// 初始化串口(Wokwi仿真波特率115200)
Serial.begin(115200);
gpsSerial.begin(9600);
// 初始化传感器
dht.begin();
delay(1000); // Wokwi仿真启动延迟
// 打印Wokwi仿真提示
Serial.println("===== Wokwi ESP32-C3仿真启动 =====");
Serial.println("正在连接虚拟WiFi...");
// 连接虚拟WiFi
connectWiFi();
// 配置MQTT
client.setServer(mqttServer, mqttPort);
// MQTT回调函数(接收云端指令)
client.setCallback([](char* topic, byte* payload, unsigned int length) {
Serial.print("收到云端消息 [");
Serial.print(topic);
Serial.print("]: ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
});
Serial.println("初始化完成,等待分钟级采集...");
Serial.println("=====================================");
}
// ===================== 6. 主循环函数 =====================
void loop() {
// MQTT断线重连
if (!client.connected()) {
reconnectMQTT();
}
client.loop();
// 分钟级采集(60秒/次,Wokwi测试可改10秒:10000)
if (millis() - lastCollectTime >= 60000) {
lastCollectTime = millis();
collectAndUploadData();
}
// Wokwi虚拟GPS数据补充(防止无输出)
simulateGpsData();
// 解析GPS数据
while (gpsSerial.available() > 0) {
gps.encode(gpsSerial.read());
}
}
// ===================== 7. Wokwi专属函数 =====================
// 连接Wokwi虚拟WiFi
void connectWiFi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n✅ WiFi连接成功!");
Serial.print("IP地址:");
Serial.println(WiFi.localIP());
}
// MQTT重连(适配Wokwi网络仿真)
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("🔌 连接腾讯云MQTT...");
// Wokwi仿真模式:若无需真实上传,跳过认证(注释下方真实连接,启用仿真连接)
// -------- 真实MQTT连接(需改配置) --------
if (client.connect(clientId, mqttUser, mqttPwd)) {
Serial.println("成功!");
}
// -------- 纯仿真MQTT连接(无需改配置) --------
// if (client.connect("wokwi-sim-device")) {
// Serial.println("仿真模式成功!");
// }
// -------- 连接失败处理 --------
else {
Serial.print("失败(错误码:");
Serial.print(client.state());
Serial.println("),5秒后重试...");
delay(5000);
}
}
}
// 数据采集&上传(Wokwi适配)
void collectAndUploadData() {
Serial.println("\n📊 开始本次数据采集 =====");
// 1. 采集温湿度(Wokwi仿真DHT11数据)
float temp = dht.readTemperature();
float humi = dht.readHumidity();
// 过滤异常值
if (isnan(temp) || isnan(humi)) {
Serial.println("❌ DHT11读取失败,使用仿真数据");
temp = random(20, 35) + 0.5; // 仿真温度
humi = random(30, 70) + 0.5; // 仿真湿度
}
// 2. 采集GPS数据(Wokwi虚拟GPS)
float lat = gps.location.isValid() ? gps.location.lat() : 30.123456;
float lng = gps.location.isValid() ? gps.location.lng() : 120.654321;
if (!gps.location.isValid()) {
Serial.println("⚠️ GPS无有效数据,使用仿真坐标");
}
// 3. 风险检测(温湿度超标)
bool tempAbnormal = temp > 30.0;
bool humiAbnormal = humi < 40.0;
bool risk = tempAbnormal || humiAbnormal;
// 4. 打印采集结果
Serial.print("温度:");
Serial.print(temp, 1);
Serial.print("℃ ");
if (tempAbnormal) Serial.print("【高温异常】");
Serial.println();
Serial.print("湿度:");
Serial.print(humi, 1);
Serial.print("%RH ");
if (humiAbnormal) Serial.print("【低湿异常】");
Serial.println();
Serial.print("GPS坐标:");
Serial.print(lat, 6);
Serial.print(", ");
Serial.println(lng, 6);
if (risk) Serial.println("🚨 检测到风险,触发传导分析!");
// 5. 封装JSON数据
char tempHumiPayload[120];
char gpsPayload[120];
sprintf(tempHumiPayload,
"{\"time\":%lu,\"temp\":%.1f,\"humi\":%.1f,\"risk\":%s}",
millis()/1000, temp, humi, risk ? "true" : "false");
sprintf(gpsPayload,
"{\"time\":%lu,\"lat\":%.6f,\"lng\":%.6f,\"valid\":%s}",
millis()/1000, lat, lng, gps.location.isValid() ? "true" : "false");
// 6. MQTT上传
if (client.connected()) {
// 上传温湿度
if (client.publish(TOPIC_TEMP_HUMI, tempHumiPayload)) {
Serial.println("✅ 温湿度数据上传成功");
} else {
Serial.println("❌ 温湿度数据上传失败");
}
// 上传GPS
if (client.publish(TOPIC_GPS, gpsPayload)) {
Serial.println("✅ GPS数据上传成功");
} else {
Serial.println("❌ GPS数据上传失败");
}
} else {
Serial.println("⚠️ MQTT未连接,数据暂存本地");
}
Serial.println("==================== 采集结束 📤");
}
// Wokwi虚拟GPS数据补充(防止模块无输出)
void simulateGpsData() {
static unsigned long lastGpsTime = 0;
if (millis() - lastGpsTime >= 5000) {
lastGpsTime = millis();
// 向虚拟GPS串口写入仿真数据
gpsSerial.println(fakeGpsData);
}
}