#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <DHT.h>
#include <Adafruit_Sensor.h>
#include <LiquidCrystal_I2C.h>
// Wi-Fi 配置
const char* ssid = "Wokwi-GUEST"; // 设置Wi-Fi名称
const char* password = ""; // 设置Wi-Fi密码
// MQTT 配置
const char* mqtt_broker = "2bf48bb4b30c487faffebbfd8260e850.s1.eu.hivemq.cloud"; // 设置MQTT服务器地址
const int mqtt_port = 8883; // 设置MQTT端口
const char* mqtt_username = "hyhyhy"; // 设置MQTT用户名
const char* mqtt_password = "Hy123123"; // 设置MQTT密码
const char* mqtt_client_id = "hy"; // 设置MQTT客户端ID
const char* mqtt_topic_publish = "smart_home/data/esp32_device_id"; // 设置MQTT发布主题
// 引脚定义
#define DHTPIN 4 // 定义DHT传感器连接的引脚
#define DHTTYPE DHT22 // 使用DHT22类型
#define LDR_PIN 34 // 光敏电阻连接的引脚
#define BUZZER_PIN 2 // 蜂鸣器连接的引脚
#define MQ2_PM25_PIN 35 // PM2.5传感器连接的引脚
#define MQ2_CO2_PIN 32 // CO2传感器连接的引脚
#define MODE_SELECT_PIN 15 // 模式选择开关连接的引脚
// 传感器对象
DHT dht(DHTPIN, DHTTYPE); // 创建DHT对象
WiFiClientSecure espClient; // 创建安全的Wi-Fi客户端
PubSubClient client(espClient); // 创建MQTT客户端
LiquidCrystal_I2C lcd(0x27, 16, 2); // 创建LCD对象
// 定时变量
unsigned long previousMillisLcdCycle = 0; // 记录上一次LCD切换的时间
const long intervalLcdCycle = 1000; // LCD切换间隔时间(毫秒)
int lcdDisplayMode = 0; // 当前LCD显示模式
float temperature = 0.0; // 存储温度值
float humidity = 0.0; // 存储湿度值
float lightLux = 0.0; // 存储光照强度
float pm2_5 = 0.0; // 存储PM2.5浓度
float co2_concentration = 0.0; // 存储CO2浓度
bool isRandomMode = true; // 是否为随机模拟模式
unsigned long previousMillisBuzzer = 0; // 记录上一次蜂鸣器状态变化的时间
const long intervalBuzzer = 500; // 蜂鸣器状态变化间隔时间(毫秒)
bool buzzerState = LOW; // 蜂鸣器当前状态
unsigned long previousMillisOutput = 0; // 记录上一次输出数据的时间
const long intervalOutput = 3000; // 输出数据间隔时间(毫秒)
bool currentAlertStatus = false; // 当前警报状态
// LDR计算参数
const float SERIES_RESISTOR = 10000.0; // 分压电阻阻值
const float ADC_MAX = 4095.0; // ADC最大值
const float ADC_REF_VOLTAGE = 3.3; // ADC参考电压
const float A = 12518931.0; // 经验拟合参数A
const float B = -1.405; // 经验拟合参数B
void setupWifi();
void reconnectMqtt();
void generateRandomSensorData();
void readActualSensors();
void displaySerialData();
bool checkAllAlertConditions();
void publishSensorDataToCloud();
void updateLcdFirstLine();
void updateLcdSecondLine();
void setup() {
Serial.begin(115200); // 初始化串口通信
Serial.println("智能家居信息采集系统启动..."); // 打印启动信息
dht.begin(); // 启动DHT传感器
Serial.println("DHT22传感器初始化完成."); // 打印DHT传感器初始化完成信息
pinMode(LDR_PIN, INPUT); // 设置光敏电阻引脚为输入
pinMode(MQ2_PM25_PIN, INPUT); // 设置PM2.5传感器引脚为输入
pinMode(MQ2_CO2_PIN, INPUT); // 设置CO2传感器引脚为输入
pinMode(BUZZER_PIN, OUTPUT); // 设置蜂鸣器引脚为输出
pinMode(MODE_SELECT_PIN, INPUT_PULLUP); // 设置模式选择引脚为上拉输入
digitalWrite(BUZZER_PIN, LOW); // 初始关闭蜂鸣器
Serial.println("蜂鸣器初始化完成."); // 打印蜂鸣器初始化完成信息
isRandomMode = (digitalRead(MODE_SELECT_PIN) == HIGH); // 读取模式选择开关状态
randomSeed(micros()); // 初始化随机数种子
Serial.printf("当前模式: %s\n", isRandomMode ? "随机模拟模式" : "手动调节模式"); // 打印当前模式
lcd.init(); // 初始化LCD
lcd.backlight(); // 开启LCD背光
lcd.clear(); // 清除LCD内容
lcd.setCursor(0, 0);
lcd.print("System Starting..."); // 显示系统启动信息
lcd.setCursor(0, 1);
lcd.print("Connecting WiFi..."); // 显示正在连接Wi-Fi
setupWifi(); // 连接Wi-Fi
client.setServer(mqtt_broker, mqtt_port); // 设置MQTT服务器
espClient.setInsecure(); // 忽略SSL证书验证
lcd.clear(); // 清除LCD内容
lcd.setCursor(0, 0);
lcd.print("WiFi Connected!"); // 显示Wi-Fi已连接
delay(1000); // 延迟1秒
lcd.clear(); // 清除LCD内容
lcd.setCursor(0, 0);
lcd.print("Connecting MQTT..."); // 显示正在连接MQTT
}
void loop() {
unsigned long currentMillis = millis(); // 获取当前时间(毫秒)
if (WiFi.status() != WL_CONNECTED) { // 检查Wi-Fi是否断开连接
setupWifi(); // 重新连接Wi-Fi
return; // 返回,避免继续执行后续操作
}
if (!client.connected()) { // 检查MQTT是否断开连接
reconnectMqtt(); // 重新连接MQTT
return; // 返回,避免继续执行后续操作
}
client.loop(); // 维持MQTT客户端循环
bool newModeState = (digitalRead(MODE_SELECT_PIN) == HIGH); // 读取模式选择开关状态
if (newModeState != isRandomMode) { // 如果模式发生变化
isRandomMode = newModeState; // 更新模式状态
Serial.printf("\n模式已切换为: %s\n", isRandomMode ? "随机模拟模式" : "手动调节模式"); // 打印模式切换信息
lcd.clear(); // 清除LCD内容
lcd.setCursor(0, 0);
lcd.print("Mode Switched!"); // 显示模式已切换
delay(1000); // 延迟1秒
digitalWrite(BUZZER_PIN, LOW); // 关闭蜂鸣器
buzzerState = LOW; // 重置蜂鸣器状态
previousMillisBuzzer = currentMillis; // 重置上一次蜂鸣器状态变化的时间
previousMillisOutput = currentMillis; // 重置上一次输出数据的时间
}
if (currentMillis - previousMillisOutput >= intervalOutput) { // 判断是否达到输出间隔时间
previousMillisOutput = currentMillis; // 更新上一次输出数据的时间
if (isRandomMode) { // 如果是随机模拟模式
generateRandomSensorData(); // 生成随机传感器数据
} else { // 如果是手动调节模式
readActualSensors(); // 读取实际传感器数据
}
currentAlertStatus = checkAllAlertConditions(); // 检查所有警报条件
displaySerialData(); // 在串口监视器中显示传感器数据
publishSensorDataToCloud(); // 将传感器数据发布到MQTT服务器
}
if (currentAlertStatus) { // 如果有警报触发
if (currentMillis - previousMillisBuzzer >= intervalBuzzer) { // 判断是否达到蜂鸣器状态变化间隔时间
previousMillisBuzzer = currentMillis; // 更新上一次蜂鸣器状态变化的时间
buzzerState = !buzzerState; // 反转蜂鸣器状态
digitalWrite(BUZZER_PIN, buzzerState); // 设置蜂鸣器状态
}
} else { // 如果没有警报
digitalWrite(BUZZER_PIN, LOW); // 确保蜂鸣器关闭
buzzerState = LOW; // 重置蜂鸣器状态
previousMillisBuzzer = currentMillis; // 重置上一次蜂鸣器状态变化的时间
}
updateLcdFirstLine(); // 更新LCD第一行显示
if (currentMillis - previousMillisLcdCycle >= intervalLcdCycle) { // 判断是否达到LCD切换间隔时间
previousMillisLcdCycle = currentMillis; // 更新上一次LCD切换的时间
lcdDisplayMode = (lcdDisplayMode + 1) % 5; // 循环更新LCD显示模式
}
updateLcdSecondLine(); // 更新LCD第二行显示
delay(10); // 短暂延迟
}
void setupWifi() {
Serial.print("尝试连接到 WiFi SSID: ");
Serial.println(ssid);
lcd.setCursor(0, 1);
lcd.print("Conn. WiFi...");
WiFi.begin(ssid, password); // 开始连接Wi-Fi
int retries = 0;
while (WiFi.status() != WL_CONNECTED) { // 等待直到Wi-Fi连接成功
delay(500);
Serial.print(".");
lcd.print(".");
retries++;
if (retries > 20) { // 如果超过最大重试次数
Serial.println("\nWiFi连接失败,请检查SSID和密码!"); // 打印错误信息
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi Failed!");
lcd.setCursor(0, 1);
lcd.print("Check Creds.");
delay(5000);
ESP.restart(); // 重启ESP32
}
}
Serial.println("\nWiFi已连接,IP地址: "); // 打印Wi-Fi连接成功信息
Serial.println(WiFi.localIP()); // 打印本地IP地址
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi Connected!");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(2000);
}
void reconnectMqtt() {
while (!client.connected()) { // 当MQTT未连接时循环
Serial.print("尝试连接MQTT Broker..."); // 打印连接尝试信息
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Conn. MQTT...");
if (client.connect(mqtt_client_id, mqtt_username, mqtt_password)) { // 尝试连接MQTT服务器
Serial.println("已连接到MQTT Broker。"); // 打印连接成功信息
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("MQTT Connected!");
delay(2000);
} else { // 如果连接失败
Serial.print("MQTT连接失败, rc="); // 打印错误信息
Serial.print(client.state());
Serial.println(" 1秒后重试...");
lcd.setCursor(0, 1);
lcd.print("Failed! rc:");
lcd.print(client.state());
delay(1000);
}
}
}
void generateRandomSensorData() {
temperature = random(230, 281) / 10.0; // 生成随机温度值
humidity = random(400, 501) / 10.0; // 生成随机湿度值
lightLux = random(2700, 3000)/10.0; // 生成随机光照强度值
pm2_5 = random(200, 260)/10.0; // 生成随机PM2.5浓度值
co2_concentration = random(3000, 3510)/10.0; // 生成随机CO2浓度值
}
void readActualSensors() {
humidity = dht.readHumidity(); // 读取湿度值
temperature = dht.readTemperature(); // 读取温度值
if (isnan(humidity) || isnan(temperature)) { // 如果读取失败
Serial.println("错误: 无法从DHT22传感器读取数据!"); // 打印错误信息
}
int adcValue = analogRead(LDR_PIN); // 读取光敏电阻ADC值
float voltage = adcValue / ADC_MAX * ADC_REF_VOLTAGE; // 计算电压
float ldrResistance = SERIES_RESISTOR * voltage / (ADC_REF_VOLTAGE - voltage); // 计算光敏电阻阻值
lightLux = A * pow(ldrResistance, B); // 根据经验公式计算光照强度
int raw_pm25_adc = analogRead(MQ2_PM25_PIN); // 读取PM2.5传感器ADC值
float pm25_raw = (float)raw_pm25_adc / 4095.0; // 归一化到0~1
pm2_5 = 20.0 + pm25_raw * (25.0 - 20.0); // 线性映射到20~25之间的浮点数
int raw_co2_adc = analogRead(MQ2_CO2_PIN); // 读取CO2传感器ADC值
float co2_raw = (float)raw_co2_adc / 4095.0; // 归一化到0~1
co2_concentration = 300.0 + co2_raw * (350.0 - 300.0); // 线性映射到300~350之间的浮点数
}
void displaySerialData() {
Serial.println("\n--- 传感器数据 ---"); // 打印传感器数据标题
Serial.printf("温度: %.1f °C\n", temperature); // 打印温度
Serial.printf("湿度: %.1f %%\n", humidity); // 打印湿度
Serial.printf("光照强度: %.1f lux\n", lightLux); // 打印光照强度
Serial.printf("PM2.5: %.1f ug/m³\n", pm2_5); // 打印PM2.5浓度
Serial.printf("CO2浓度: %.1f ppm\n", co2_concentration); // 打印CO2浓度
Serial.println("-----------------"); // 分隔线
Serial.println("--- 警报条件检查 ---"); // 打印警报条件检查标题
bool tempAlert = (temperature > 30.0); // 检查温度是否过高
bool humidAlert = (humidity > 80.0); // 检查湿度是否过高
bool pm25Alert = (pm2_5 > 75); // 检查PM2.5浓度是否过高
bool co2Alert = (co2_concentration > 1500); // 检查CO2浓度是否过高
bool lightAlert = (lightLux < 100); // 检查光照是否不足
Serial.printf(" 条件1: 温度 %.1f °C - %s\n", temperature, tempAlert ? "过高!" : "正常。"); // 打印温度检查结果
Serial.printf(" 条件2: 湿度 %.1f %% - %s\n", humidity, humidAlert ? "过高!" : "正常。"); // 打印湿度检查结果
Serial.printf(" 条件3: PM2.5 %.1f ug/m³ - %s\n", pm2_5, pm25Alert ? "过高!" : "正常。"); // 打印PM2.5检查结果
Serial.printf(" 条件4: CO2浓度 %.1f ppm - %s\n", co2_concentration, co2Alert ? "过高!" : "正常。"); // 打印CO2检查结果
Serial.printf(" 条件5: 光照强度 %.1f lux - %s\n", lightLux, lightAlert ? "不足!" : "正常。"); // 打印光照检查结果
if (currentAlertStatus) { // 如果有警报
Serial.println(" 最终警报状态: 有警报 (蜂鸣器闪烁中)"); // 打印警报状态
} else { // 如果没有警报
Serial.println(" 最终警报状态: 无警报 (蜂鸣器关闭)"); // 打印警报状态
}
Serial.println("---------------------"); // 分隔线
}
bool checkAllAlertConditions() {
bool alert = false; // 初始化警报状态为false
if (temperature > 30.0) alert = true; // 如果温度过高,设置警报为true
if (humidity > 80.0) alert = true; // 如果湿度过高,设置警报为true
if (pm2_5 > 75) alert = true; // 如果PM2.5浓度过高,设置警报为true
if (co2_concentration > 1500) alert = true; // 如果CO2浓度过高,设置警报为true
if (lightLux < 100) alert = true; // 如果光照不足,设置警报为true
return alert; // 返回警报状态
}
void publishSensorDataToCloud() {
StaticJsonDocument<256> doc; // 创建JSON文档
float temp_rounded = round(temperature * 10) / 10.0; // 四舍五入温度值到小数点后一位
float humi_rounded = round(humidity * 10) / 10.0; // 四舍五入湿度值到小数点后一位
float lux_rounded = round(lightLux * 10) / 10.0; // 四舍五入光照强度到小数点后一位
float pm25_rounded = round(pm2_5 * 10) / 10.0; // 四舍五入PM2.5浓度到小数点后一位
float co2_rounded = round(co2_concentration * 10) / 10.0; // 四舍五入CO2浓度到小数点后一位
doc["温度"] = temp_rounded; // 添加温度到JSON
doc["湿度"] = humi_rounded; // 添加湿度到JSON
doc["光照强度"] = lux_rounded; // 添加光照强度到JSON
doc["pm2.5"] = pm25_rounded; // 添加PM2.5浓度到JSON
doc["CO2"] = co2_rounded; // 添加CO2浓度到JSON
doc["蜂鸣器状态"] = currentAlertStatus ? "响" : "不响"; // 添加蜂鸣器状态到JSON
char jsonBuffer[256];
serializeJson(doc, jsonBuffer, sizeof(jsonBuffer)); // 序列化JSON文档
Serial.print("发布MQTT消息到主题 [");
Serial.print(mqtt_topic_publish);
Serial.print("]: ");
Serial.println(jsonBuffer);
if (client.publish(mqtt_topic_publish, jsonBuffer)) { // 发布MQTT消息
lcd.setCursor(0, 1);
lcd.print("Cloud: OK "); // 显示发布成功
} else {
lcd.setCursor(0, 1);
lcd.print("Cloud: Failed! "); // 显示发布失败
}
}
void updateLcdFirstLine() {
lcd.setCursor(0, 0);
lcd.print("Mode:"); // 显示模式
lcd.print(isRandomMode ? "RND" : "MAN"); // 显示随机或手动模式
lcd.print(" Alert:"); // 显示警报状态
lcd.print(currentAlertStatus ? "YES" : "NO "); // 显示有警报或无警报
}
void updateLcdSecondLine() {
lcd.setCursor(0, 1);
char buffer[17];
switch (lcdDisplayMode) {
case 0:
snprintf(buffer, sizeof(buffer), "Temp:%.1fC", temperature); // 显示温度
break;
case 1:
snprintf(buffer, sizeof(buffer), "Hum:%.1f%%", humidity); // 显示湿度
break;
case 2:
snprintf(buffer, sizeof(buffer), "PM2.5:%.1f", pm2_5); // 显示PM2.5浓度
break;
case 3:
snprintf(buffer, sizeof(buffer), "CO2:%.1f", co2_concentration); // 显示CO2浓度
break;
case 4:
snprintf(buffer, sizeof(buffer), "Light:%.1f", lightLux); // 显示光照强度
break;
}
lcd.print(" "); // 清除原有内容
lcd.setCursor(0, 1);
lcd.print(buffer); // 显示新内容
}