#pragma once
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <time.h>
#include <esp_system.h>
// 引脚定义
#define BUTTON_PIN 15
#define LED1_PIN 19
#define LED2_PIN 18
#define DHTPIN 26
#define DHTTYPE DHT22
#define BUZZER_PIN 4
#define GAS_PIN 34
#define PIR_PIN 27
// 阈值定义
const float TEMP_THRESHOLD_C = 20.0;
const float TEMP_THRESHOLD_F = 68.0;
const float HUMIDITY_THRESHOLD = 70.0;
const int GAS_THRESHOLD = 3000;
const unsigned long MOTION_DELAY = 5000;
// WiFi & MQTT 配置
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqtt_server = "broker.hivemq.com";
// MQTT 主题
const char* mqtt_topic_pub = "IoT/Warehouse room1/sensor";
const char* mqtt_topic_time = "IoT/Warehouse room1/time";
const char* mqtt_topic_sub = "IoT/Warehouse room1/command";
const char* mqtt_topic_alarm = "IoT/Warehouse room1/alarm";
const char* mqtt_topic_lwt = "IoT/Warehouse room1/status";
const char* mqtt_topic_motion = "IoT/Warehouse room1/motion";
// 全局对象
WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 20, 4);
// 状态变量
bool led1State = false;
bool led2State = false;
unsigned long lastMsg = 0;
unsigned long lastDisplayChange = 0;
const long publishInterval = 3000;
const long displayInterval = 3000;
int displayState = 0;
char msg[100];
bool welcomeDisplayed = false;
bool isAlarm = false;
unsigned long alarmStartTime = 0;
const unsigned long ALARM_DURATION = 30000;
unsigned long lastBuzzerToggle = 0;
const long buzzerInterval = 500;
uint8_t dot[] = {0x0E, 0x0A, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00};
bool motionDetected = false;
unsigned long lastMotionTime = 0;
bool motionStateChanged = false;
int buttonState;
int lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
bool restartTriggered = false;
// WiFi 连接函数(增强重试)
void setup_wifi() {
Serial.println("Connecting to WiFi...");
WiFi.begin(ssid, password);
int wifiRetry = 0;
while (WiFi.status() != WL_CONNECTED && wifiRetry < 10) {
Serial.print(".");
delay(500);
wifiRetry++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected successfully!");
} else {
Serial.println("\nWiFi connection failed after retries! Restarting...");
esp_restart(); // 重试失败则重启
}
}
// 读取气体传感器
int readGasLevel() {
return analogRead(GAS_PIN);
}
// 处理人体红外传感器
void handlePIR() {
bool currentMotion = digitalRead(PIR_PIN) == HIGH;
if (currentMotion) {
lastMotionTime = millis();
if (!motionDetected) {
motionDetected = true;
motionStateChanged = true;
Serial.println("Motion detected!");
client.publish(mqtt_topic_motion, "detected");
}
} else {
if (motionDetected && (millis() - lastMotionTime > MOTION_DELAY)) {
motionDetected = false;
motionStateChanged = true;
Serial.println("Motion stopped");
client.publish(mqtt_topic_motion, "stopped");
}
}
}
// 初始化系统
void setup() {
Serial.begin(115200);
Serial.println("Starting setup()...");
// 引脚模式设置
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(GAS_PIN, INPUT);
pinMode(PIR_PIN, INPUT);
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, HIGH);
digitalWrite(BUZZER_PIN, LOW);
// LCD 初始化
lcd.init();
lcd.backlight();
lcd.createChar(0, dot);
lcd.clear();
lcd.print("Initializing...");
delay(1000);
// DHT 传感器初始化
dht.begin();
// WiFi 连接
setup_wifi();
// MQTT 客户端配置
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
client.setKeepAlive(120); // 延长心跳间隔
client.setSocketTimeout(5000); // 延长超时时间
// 时间同步
configTime(8 * 3600, 0, "pool.ntp.org");
struct tm timeinfo;
int timeRetry = 0;
while (!getLocalTime(&timeinfo) && timeRetry < 5) {
Serial.println("Waiting for time sync...");
delay(1000);
timeRetry++;
}
if (timeRetry < 5) {
Serial.println("Time synchronized successfully");
displayWelcome();
lastDisplayChange = millis();
lastMsg = 0;
} else {
Serial.println("Time sync failed after retries!");
}
Serial.println("setup() complete, entering loop()");
}
// 显示欢迎界面
void displayWelcome() {
if (!welcomeDisplayed) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Hi,Welcome!");
lcd.setCursor(0, 1);
lcd.print("Warehouse1,");
lcd.setCursor(0, 2);
lcd.print("By Group3!");
welcomeDisplayed = true;
delay(2000);
}
}
// 显示日期时间
void displayDateTime() {
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
char dtbuf[21];
strftime(dtbuf, sizeof(dtbuf), "%Y-%m-%d %H:%M:%S", &timeinfo);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("DateTime:");
lcd.setCursor(0, 1);
lcd.print(dtbuf);
unsigned long now = millis();
if (now - lastMsg >= publishInterval) {
lastMsg = now;
client.publish(mqtt_topic_time, dtbuf);
}
} else {
lcd.clear();
lcd.print("Time sync lost!");
}
}
// 显示传感器数据
void displaySensorData() {
unsigned long start = millis();
float h = dht.readHumidity();
float c = dht.readTemperature();
float f = dht.readTemperature(true);
int gas = readGasLevel();
// 超时保护
if (millis() - start > 1000) {
lcd.clear();
lcd.print("Sensor Timeout!");
return;
}
// 校验传感器数据
if (isnan(h) || isnan(c) || isnan(f)) {
Serial.println(F("Failed to read DHT!"));
lcd.clear();
lcd.print("Sensor Error!");
return;
}
// 串口打印
Serial.printf("Humidity: %.1f%% Temp: %.1f°C / %.1f°F Gas: %d Motion: %s\n",
h, c, f, gas, motionDetected ? "Yes" : "No");
// 报警判断
bool tempAlarm = c > TEMP_THRESHOLD_C;
bool humAlarm = h > HUMIDITY_THRESHOLD;
bool gasAlarm = gas > GAS_THRESHOLD;
if (tempAlarm || humAlarm || gasAlarm) {
isAlarm = true;
alarmStartTime = millis();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ALARM!");
lcd.setCursor(0, 1);
if (tempAlarm) lcd.print("Temp Over!");
else if (humAlarm) lcd.print("Humidity Over!");
else if (gasAlarm) lcd.print("Gas Over!");
lcd.setCursor(0, 2);
lcd.print(tempAlarm ? String(c) + "C > " + String(TEMP_THRESHOLD_C) + "C"
: humAlarm ? String(h) + "% > " + String(HUMIDITY_THRESHOLD) + "%"
: String(gas) + " > " + String(GAS_THRESHOLD));
lcd.setCursor(0, 3);
lcd.print("Motion: ");
lcd.print(motionDetected ? "Detected" : "None");
// 发布报警消息
char alarmMsg[100];
snprintf(alarmMsg, sizeof(alarmMsg), "Alarm: %s %.1f%s",
tempAlarm ? "Temp" : humAlarm ? "Humidity" : "Gas",
tempAlarm ? (float)c : humAlarm ? (float)h : (float)gas,
tempAlarm ? "C" : humAlarm ? "%" : "");
client.publish(mqtt_topic_alarm, alarmMsg);
} else {
isAlarm = false;
digitalWrite(BUZZER_PIN, LOW);
// 正常显示传感器数据
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Humidity: ");
lcd.print(h, 1);
lcd.print("%");
lcd.setCursor(0, 1);
lcd.print("Temperature: ");
lcd.print(c, 2);
lcd.write(0);
lcd.print("C");
lcd.setCursor(0, 2);
lcd.print("Gas Level: ");
lcd.print(gas);
lcd.setCursor(0, 3);
lcd.print("Motion: ");
lcd.print(motionDetected ? "Detected" : "None");
// 发布传感器数据
if (millis() - lastMsg >= publishInterval) {
lastMsg = millis();
snprintf(msg, sizeof(msg),
"{\"temp\": %.1f, \"hum\": %.1f, \"gas\": %d, \"motion\": %s}",
c, h, gas, motionDetected ? "true" : "false");
client.publish(mqtt_topic_pub, msg);
}
}
}
// MQTT 回调函数
void callback(char* topic, byte* payload, unsigned int length) {
payload[length] = 0;
String command = String((char*)payload);
command.toUpperCase();
Serial.print("Received MQTT: ");
Serial.println(command);
if (command == "RESTART") {
Serial.println("MQTT command: Restart");
blinkAndRestart();
}
}
// 蜂鸣器控制
void controlBuzzer(bool enable) {
static int buzzerPattern = 0;
unsigned long now = millis();
if (enable) {
if (now - lastBuzzerToggle >= buzzerInterval) {
lastBuzzerToggle = now;
buzzerPattern = (buzzerPattern + 1) % 4;
switch (buzzerPattern) {
case 0: tone(BUZZER_PIN, 1000); break;
case 1: tone(BUZZER_PIN, 1500); break;
case 2: tone(BUZZER_PIN, 2000); break;
case 3: noTone(BUZZER_PIN); break;
}
}
} else {
noTone(BUZZER_PIN);
}
}
// 重启流程
void blinkAndRestart() {
lcd.clear();
lcd.print("Restarting...");
for (int i = 0; i < 3; i++) {
digitalWrite(LED2_PIN, LOW);
delay(300);
digitalWrite(LED2_PIN, HIGH);
delay(300);
}
client.publish(mqtt_topic_lwt, "restarting", true);
delay(500);
esp_restart();
}
// 按键处理
void handleButton() {
int reading = digitalRead(BUTTON_PIN);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == LOW && !restartTriggered) {
restartTriggered = true;
Serial.println("Button pressed: Restart");
blinkAndRestart();
}
}
}
lastButtonState = reading;
}
// MQTT 重连逻辑(优化版)
void reconnect() {
if (client.connected()) return;
Serial.println("Connecting to MQTT...");
// 带遗嘱的连接
if (client.connect("ESP32ClientDevice", mqtt_topic_lwt, 0, true, "offline")) {
Serial.println("MQTT connected!");
client.publish(mqtt_topic_lwt, "online", true);
client.subscribe(mqtt_topic_sub);
delay(500); // 等待服务器处理
} else {
// 打印失败状态码
Serial.print("MQTT failed (code=");
Serial.print(client.state());
Serial.println("), retry in 5s...");
delay(5000); // 避免频繁重试
}
}
// 主循环
void loop() {
// 优先处理 MQTT 收发
client.loop();
// MQTT 重连逻辑
if (!client.connected()) {
reconnect();
}
// 按键处理
handleButton();
// 人体红外处理
handlePIR();
unsigned long now = millis();
// 报警逻辑
if (isAlarm) {
controlBuzzer(true);
// LED 闪烁
if (now - lastBuzzerToggle >= buzzerInterval/2) {
lastBuzzerToggle = now;
digitalWrite(LED1_PIN, !digitalRead(LED1_PIN));
}
// 报警时长限制
if (now - alarmStartTime > ALARM_DURATION) {
isAlarm = false;
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(LED1_PIN, led1State);
}
}
// 运动触发显示更新
if (motionStateChanged && !isAlarm && !restartTriggered) {
motionStateChanged = false;
switch (displayState) {
case 1: displayDateTime(); break;
case 2: displaySensorData(); break;
}
}
// 自动切换显示
if (now - lastDisplayChange >= displayInterval && !isAlarm && !restartTriggered) {
lastDisplayChange = now;
displayState = (displayState + 1) % 3;
switch (displayState) {
case 0: displayWelcome(); break;
case 1: displayDateTime(); break;
case 2: displaySensorData(); break;
}
}
}