#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> // 用于重启功能
// 新增按钮和LED引脚定义
#define BUTTON_PIN 15 // 按钮引脚,可根据实际情况修改
#define LED1_PIN 19 // 主LED引脚 (重启时不闪烁)
#define LED2_PIN 18 // 辅助LED引脚 (重启时闪烁)
// 传感器与引脚定义
#define DHTPIN 26
#define DHTTYPE DHT22
#define BUZZER_PIN 4 // 蜂鸣器引脚
// 报警阈值设置
const float TEMP_THRESHOLD_C = 40.0; // 温度阈值(摄氏度)
const float TEMP_THRESHOLD_F = 135.0; // 温度阈值(华氏度)
const float HUMIDITY_THRESHOLD = 45.0; // 湿度阈值
// WiFi 配置
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqtt_server = "broker.hivemq.com";
// MQTT 主题
const char* mqtt_topic_pub = "IoT/WXP/gp/sensor"; // 温湿度发布
const char* mqtt_topic_time = "IoT/WXP/gp/time"; // 时间发布
const char* mqtt_topic_sub = "IoT/WXP/gp/led"; // LED 控制订阅
const char* mqtt_topic_alarm = "IoT/WXP/gp/alarm"; // 报警消息
const char* mqtt_topic_lwt = "IoT/WXP/gp/status"; // 设备状态(遗嘱消息)
// 对象创建
WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 20, 4);
// 全局变量
bool led1State = false; // LED1状态
bool led2State = false; // LED2状态
unsigned long lastMsg = 0; // MQTT 发布计时器
unsigned long lastDisplayChange = 0;
const long publishInterval = 3000; // 3秒发布一次数据
const long displayInterval = 3000; // 显示切换间隔
int displayState = 0; // 显示状态(0:欢迎, 1:时间, 2:温湿度)
char msg[100]; // MQTT 消息缓冲区
bool welcomeDisplayed = false;
// 报警配置
bool isAlarm = false;
unsigned long alarmStartTime = 0;
const unsigned long ALARM_DURATION = 30000; // 报警持续30秒
unsigned long lastBuzzerToggle = 0;
const long buzzerInterval = 500; // 蜂鸣器闪烁间隔
// 自定义℃符号
uint8_t dot[] = {0x0E, 0x0A, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00};
// 按钮相关变量
int buttonState; // 当前按钮状态
int lastButtonState = HIGH; // 上一次按钮状态
unsigned long lastDebounceTime = 0; // 上次抖动的时间
unsigned long debounceDelay = 50; // 抖动延迟时间(毫秒)
int buttonPressCount = 0; // 按钮按下计数
bool isBlinking = false; // LED是否正在闪烁
unsigned long blinkStartTime = 0; // 闪烁开始时间
int blinkCount = 0; // 闪烁次数
// WiFi 连接
void setup_wifi() {
Serial.println("Connecting to WiFi...");
WiFi.begin(ssid, password);
// 等待WiFi连接
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(250);
}
Serial.println("\nWiFi connected successfully!");
}
// MQTT 回调(LED 控制)
void callback(char* topic, byte* payload, unsigned int length) {
payload[length] = 0;
String command = String((char*)payload);
command.toUpperCase();
Serial.print("Received MQTT command: ");
Serial.println(command);
if (command == "ON") {
digitalWrite(LED1_PIN, HIGH);
digitalWrite(LED2_PIN, HIGH);
led1State = true;
led2State = true;
Serial.println("LEDs turned ON via MQTT");
} else if (command == "OFF") {
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, LOW);
led1State = false;
led2State = false;
Serial.println("LEDs turned OFF via MQTT");
}
}
// MQTT 重连
void reconnect() {
static unsigned long lastReconnectAttempt = 0;
const long reconnectInterval = 7000; // 7秒重连间隔
if (millis() - lastReconnectAttempt < reconnectInterval) return;
lastReconnectAttempt = millis();
Serial.println("Connecting to MQTT...");
if (client.connect("ESP32ClientDevice", mqtt_topic_lwt, 0, true, "offline")) {
Serial.println("MQTT connected successfully");
client.publish(mqtt_topic_lwt, "online", true);
client.subscribe(mqtt_topic_sub);
client.setKeepAlive(60);
client.setSocketTimeout(30);
} else {
Serial.print("MQTT connection failed, status=");
Serial.print(client.state());
Serial.println(", will retry....");
}
}
// 蜂鸣器控制
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; // 1kHz
case 1: tone(BUZZER_PIN, 1500); break; // 1.5kHz
case 2: tone(BUZZER_PIN, 2000); break; // 2kHz
case 3: noTone(BUZZER_PIN); break; // silent
}
}
} else {
noTone(BUZZER_PIN); // stop any tone
}
}
// 欢迎信息显示
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 Group007!");
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);
}
}
}
// 温湿度显示与发布
void displayTempHumidity() {
unsigned long start = millis();
float h = dht.readHumidity();
float c = dht.readTemperature();
float f = dht.readTemperature(true);
if (millis() - start > 1000) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sensor Timeout!");
return;
}
if (isnan(h) || isnan(c) || isnan(f)) {
Serial.println(F("Failed to read from DHT sensor!"));
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sensor Error!");
return;
}
Serial.printf("Humidity: %.1f%% Temp: %.1f°C / %.1f°F\n", h, c, f);
bool tempAlarm = c > TEMP_THRESHOLD_C || f > TEMP_THRESHOLD_F;
bool humAlarm = h > HUMIDITY_THRESHOLD;
if (tempAlarm || humAlarm) {
isAlarm = true;
alarmStartTime = millis();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ALARM!");
lcd.setCursor(0, 1);
if (tempAlarm) lcd.print("Temp Over!");
else lcd.print("Humidity Over!");
lcd.setCursor(0, 2);
lcd.print(tempAlarm ? String(c) + "C > " + String(TEMP_THRESHOLD_C) + "C"
: String(h) + "% > " + String(HUMIDITY_THRESHOLD) + "%");
char alarmMsg[100];
snprintf(alarmMsg, sizeof(alarmMsg), "Alarm: %s %.1f%s",
tempAlarm ? "Temp" : "Humidity",
tempAlarm ? c : h,
tempAlarm ? "C" : "%");
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("Temperature: ");
lcd.print(f, 1);
lcd.write(0);
lcd.print("F");
unsigned long now = millis();
if (now - lastMsg >= publishInterval) {
lastMsg = now;
snprintf(msg, sizeof(msg), "{\"temp\": %.1f, \"hum\": %.1f}", c, h);
client.publish(mqtt_topic_pub, msg);
}
}
}
// 新增:LED2(重启指示灯)闪烁函数
void blinkRestartLED(int times) {
bool originalState = led2State; // 保存LED2原始状态
for (int i = 0; i < times; i++) {
digitalWrite(LED2_PIN, HIGH);
delay(300);
digitalWrite(LED2_PIN, LOW);
delay(300);
}
// 恢复LED2原始状态
digitalWrite(LED2_PIN, originalState ? HIGH : LOW);
led2State = originalState;
}
// 新增:处理按钮功能
void handleButton() {
int reading = digitalRead(BUTTON_PIN);
// 检测按钮状态变化,考虑抖动
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
// 如果按钮状态稳定超过抖动延迟
if ((millis() - lastDebounceTime) > debounceDelay) {
// 如果状态确实改变
if (reading != buttonState) {
buttonState = reading;
// 按钮被按下 (LOW表示按下,因为使用了内部上拉)
if (buttonState == LOW) {
buttonPressCount++;
Serial.print("Button pressed, count: ");
Serial.println(buttonPressCount);
// 第一步:按下一次,LED2(重启指示灯)闪烁3次
if (buttonPressCount == 1) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Prepare to restart");
Serial.println("LED2 will blink 3 times");
blinkRestartLED(3);
// 显示按钮功能提示
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Press again to");
lcd.setCursor(0, 1);
lcd.print("restart device");
delay(2000);
}
// 第二步:再按一次,重启设备
else if (buttonPressCount == 2) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Restarting...");
Serial.println("Restarting device...");
client.publish(mqtt_topic_lwt, "restarting", true);
delay(1000);
esp_restart(); // 重启ESP32
}
}
}
}
lastButtonState = reading;
}
void setup() {
Serial.begin(115200);
Serial.println("Starting setup()...");
// 初始化按钮和LED引脚
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
// 初始状态为LOW
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
lcd.init();
lcd.backlight();
lcd.createChar(0, dot);
lcd.clear();
lcd.print("Initializing...");
delay(1000);
dht.begin();
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
configTime(8 * 3600, 0, "pool.ntp.org");
struct tm timeinfo;
while (!getLocalTime(&timeinfo)) {
Serial.println("Waiting for time sync...");
delay(1000);
}
Serial.println("Time synchronized successfully");
// 显示按钮功能提示
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Button Func:");
lcd.setCursor(0, 1);
lcd.print("1.Blink LED");
lcd.setCursor(0, 2);
lcd.print("2.Restart");
delay(2000);
displayWelcome();
lastDisplayChange = millis();
lastMsg = 0;
Serial.println("setup() complete, entering loop()");
}
void loop() {
if (!client.connected()) reconnect();
client.loop();
// 处理按钮逻辑
handleButton();
unsigned long now = millis();
// 报警处理
if (isAlarm) {
controlBuzzer(true); // 激活蜂鸣器
// 报警时LED1和LED2同步闪烁
if (now - lastBuzzerToggle >= buzzerInterval/2) {
lastBuzzerToggle = now;
digitalWrite(LED1_PIN, !digitalRead(LED1_PIN));
digitalWrite(LED2_PIN, !digitalRead(LED2_PIN));
}
if (now - alarmStartTime > ALARM_DURATION) {
isAlarm = false;
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(LED1_PIN, led1State);
digitalWrite(LED2_PIN, led2State);
}
}
// 显示切换逻辑(报警或按钮操作时暂停切换)
if (now - lastDisplayChange >= displayInterval && !isAlarm && buttonPressCount == 0) {
lastDisplayChange = now;
displayState = (displayState + 1) % 3;
switch (displayState) {
case 0: displayWelcome(); break;
case 1: displayDateTime(); break;
case 2: displayTempHumidity(); break;
}
}
}