#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <DHT.h>
#include <RTClib.h>
#include <avr/power.h> // AVR 电源管理
#include <avr/sleep.h> // AVR 睡眠模式
// OLED 配置
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// DHT22 配置
#define DHT_PIN 2
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
// RTC 配置(DS1307)
RTC_DS1307 rtc;
// 引脚定义
#define MQ2_PIN A0 // MQ-2 模拟输入
#define BUZZER_PIN 9 // 蜂鸣器(PWM)
#define LED_PIN 10 // 报警灯
#define MODE_BTN 3 // 模式键(外部中断 INT1)
#define ADJUST_BTN 4 // 调整键
#define MUTE_BTN 5 // 消警键
// 全局变量
float tempThreshold = 30.0; // 温度阈值(℃)
float smokeThreshold = 500; // 烟雾阈值(模拟值)
float temp, hum, smoke; // 实时数据
bool alarmActive = false; // 报警状态
bool muteAlarm = false; // 消警标志
int settingMode = 0; // 0:正常 1:设温度 2:设烟雾
DateTime now; // 实时时间
// 休眠配置
const int SLEEP_THRESHOLD = 10000; // 10秒无操作休眠
unsigned long lastActivityTime = 0; // 最后活动时间(毫秒)
// 中断服务程序(空函数,仅用于唤醒)
void wakeUpISR() {}
void setup() {
Serial.begin(9600);
dht.begin();
// 初始化 DS1307
if (!rtc.begin()) {
Serial.println("DS1307 初始化失败!");
while (1);
}
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // 临时初始化时间(无电池时用)
rtc.writeSqwPinMode(DS1307_OFF);
// 初始化 OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED 初始化失败!");
while(1);
}
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
// 按键与输出引脚
pinMode(MODE_BTN, INPUT_PULLUP);
pinMode(ADJUST_BTN, INPUT_PULLUP);
pinMode(MUTE_BTN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
lastActivityTime = millis(); // 初始化活动时间
}
void loop() {
readSensors(); // 读取传感器
handleButtons(); // 处理按键
handleAlarm(); // 报警逻辑
updateDisplay(); // 更新显示
// 检测休眠条件:无报警、未消警、且超时
if (!alarmActive && !muteAlarm && (millis() - lastActivityTime > SLEEP_THRESHOLD)) {
enterSleepMode();
}
delay(200);
}
// 1. 读取传感器数据
void readSensors() {
// DHT22
temp = dht.readTemperature();
hum = dht.readHumidity();
if (isnan(temp) || isnan(hum)) {
Serial.println("DHT22 读取失败!");
return;
}
// MQ-2(10次采样平均)
int smokeValue = 0;
for (int i = 0; i < 10; i++) {
smokeValue += analogRead(MQ2_PIN);
delay(10);
}
smoke = smokeValue / 10.0;
// DS1307 时间
now = rtc.now();
}
// 2. 处理按键输入(新增 lastActivityTime 更新)
void handleButtons() {
// 消警键
if (digitalRead(MUTE_BTN) == LOW) {
delay(20); // 消抖
if (digitalRead(MUTE_BTN) == LOW) {
muteAlarm = !muteAlarm;
alarmActive = false;
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(LED_PIN, LOW);
lastActivityTime = millis(); // 更新活动时间
while (digitalRead(MUTE_BTN) == LOW);
}
}
// 模式键
if (digitalRead(MODE_BTN) == LOW) {
delay(20);
if (digitalRead(MODE_BTN) == LOW) {
settingMode = (settingMode + 1) % 3;
lastActivityTime = millis(); // 更新活动时间
while (digitalRead(MODE_BTN) == LOW);
}
}
// 调整键(设置模式下)
if (settingMode != 0 && digitalRead(ADJUST_BTN) == LOW) {
delay(20);
if (digitalRead(ADJUST_BTN) == LOW) {
if (settingMode == 1) { // 调温度
tempThreshold += 1.0;
if (tempThreshold > 50) tempThreshold = 20;
} else if (settingMode == 2) { // 调烟雾
smokeThreshold += 50;
if (smokeThreshold > 2000) smokeThreshold = 300;
}
lastActivityTime = millis(); // 更新活动时间
while (digitalRead(ADJUST_BTN) == LOW);
}
}
}
// 3. 报警逻辑(新增 lastActivityTime 更新)
void handleAlarm() {
if (muteAlarm) return;
if (temp > tempThreshold || smoke > smokeThreshold) {
alarmActive = true;
lastActivityTime = millis(); // 报警时更新活动时间
analogWrite(BUZZER_PIN, 128);
digitalWrite(LED_PIN, HIGH);
delay(200);
analogWrite(BUZZER_PIN, 0);
digitalWrite(LED_PIN, LOW);
delay(200);
} else {
alarmActive = false;
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(LED_PIN, LOW);
}
}
// 4. 更新 OLED 显示
void updateDisplay() {
display.clearDisplay();
// 时间
DateTime now = rtc.now();
display.setCursor(0, 15);
display.print("Time: ");
display.print(now.hour(), DEC);
display.print(':');
display.print(now.minute(), DEC);
display.print(':');
display.println(now.second(), DEC);
display.setCursor(0, 5);
display.print(now.year(), DEC);
display.print('/');
display.print(now.month(), DEC);
display.print('/');
display.println(now.day(), DEC);
// 温湿度显示
display.setCursor(0, 25);
display.print("Temp:");
display.print(temp, 1);
display.print("C Hum:");
display.print(hum, 1);
display.print("%");
// 烟雾
display.setCursor(0, 35);
display.print("Smoke: "); display.print(smoke);
display.print(" ("); display.print(smokeThreshold); display.println(")");
// 设置模式
if (settingMode == 1) {
display.setCursor(0, 45);
display.print("Set Temp: "); display.print(tempThreshold, 1);
} else if (settingMode == 2) {
display.setCursor(0, 45);
display.print("Set Smoke: "); display.print(smokeThreshold);
}
display.display();
}
// 5. 进入休眠模式(核心实现)
void enterSleepMode() {
// 1. 关闭外设
display.ssd1306_command(SSD1306_DISPLAYOFF); // 关闭 OLED 显示
delay(3000);
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(LED_PIN, LOW);
// 2. 配置睡眠模式(掉电模式,最低功耗)
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable(); // 使能睡眠
// 3. 配置外部中断唤醒(模式键,低电平触发)
attachInterrupt(digitalPinToInterrupt(MODE_BTN), wakeUpISR, LOW);
// 4. 关闭模块电源(ADC、SPI、I2C)
power_adc_disable(); // 关闭 ADC
power_spi_disable(); // 关闭 SPI
power_twi_disable(); // 关闭 I2C(TWI)
// 5. 进入睡眠(唤醒后从下一行继续执行)
sleep_mode();
// 6. 唤醒后恢复
sleep_disable(); // 关闭睡眠模式
power_all_enable(); // 恢复所有模块电源
detachInterrupt(digitalPinToInterrupt(MODE_BTN)); // 移除中断触发
// 7. 重新初始化 OLED(因 I2C 电源被关闭)
display.clearDisplay();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextColor(WHITE);
display.setTextSize(1);
display.display();
// 8. 更新活动时间
lastActivityTime = millis();
}