#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <RTClib.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// 引脚定义
#define LIGHT_SENSOR A0 // 光照传感器引脚
#define PIR_SENSOR 2 // 人体红外传感器引脚
#define LED_PIN 3 // LED控制引脚
#define MODE_BUTTON 4 // 模式切换/时间设置按键
#define UP_BUTTON 5 // 上调整按键
#define DOWN_BUTTON 6 // 下调整按键
#define PAGE_BUTTON 7 // 页面切换按键
#define TEMP_SENSOR 8 // 温度传感器引脚
// OLED参数
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// RTC对象(实时时钟)
RTC_DS1307 rtc;
// 温度传感器对象
OneWire oneWire(TEMP_SENSOR);
DallasTemperature tempSensor(&oneWire);
// 全局变量 - 系统参数
int lightThreshold = 400; // 光照阈值(自动模式下开灯临界值)
int lightCritical = 800; // 光照临界值(强光下强制关灯值)
unsigned long lastMotionTime = 0; // 上次检测到移动的时间
bool isCriticalMode = false; // 是否处于临界值调整模式
bool isManualMode = false; // 是否处于手动模式
int ledBrightness = 0; // LED亮度(手动模式下有效)
unsigned int personCount = 0; // 累计通过人数
bool lastPirState = LOW; // 上一时刻PIR传感器状态
bool lastModeState = HIGH; // 上一时刻模式按键状态
float currentTemp = 0.0; // 当前温度值
int tempThreshold = 30; // 温度报警阈值(℃)
bool isTempAlarm = false; // 温度报警状态
unsigned long lastTempUpdate = 0; // 上次温度更新时间
unsigned long lastBlinkTime = 0; // 上次闪烁时间
bool blinkState = false; // 闪烁状态
bool isTempThresholdMode = false; // 是否处于温度阈值调整模式
// 全局变量 - 时间设置
bool isTimeSetting = false; // 时间设置模式标志
int timeSettingIndex = 0; // 当前设置的时间字段索引(0-5对应年-秒)
int timeValues[6] = {0}; // 存储当前时间值
const char* timeLabels[6] = {"年", "月", "日", "时", "分", "秒"}; // 时间字段标签
int timeMaxValues[6] = {2099, 12, 31, 23, 59, 59}; // 各时间字段最大值
int timeMinValues[6] = {2023, 1, 1, 0, 0, 0}; // 各时间字段最小值
// 全局变量 - 页面切换
#define PAGE_COUNT 3 // 总页面数量
int currentPage = 0; // 当前显示页面(0-主页面, 1-副页面, 2-温度页面)
bool lastPageState = HIGH; // 上一次页面按键状态
// 全局变量 - 实时状态
bool isPersonPresent = false; // 当前是否有人在场
bool isLightOn = false; // 灯泡是否亮起
void setup() {
// 初始化硬件引脚
pinMode(PIR_SENSOR, INPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(MODE_BUTTON, INPUT_PULLUP);
pinMode(UP_BUTTON, INPUT_PULLUP);
pinMode(DOWN_BUTTON, INPUT_PULLUP);
pinMode(PAGE_BUTTON, INPUT_PULLUP);
// 初始化温度传感器
tempSensor.begin();
// 初始化OLED显示
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
// 初始化RTC时钟
if (!rtc.begin()) {
display.setCursor(0, 0);
display.println("RTC初始化失败!");
display.display();
while (1); // 初始化失败则停滞
}
// 若RTC时间无效,设置为编译时的时间
if (!rtc.isrunning()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// 初始状态设置
digitalWrite(LED_PIN, LOW);
updateTimeValues(); // 同步RTC时间到变量
}
void loop() {
int lightValue = analogRead(LIGHT_SENSOR); // 读取光照值
bool currentPirState = digitalRead(PIR_SENSOR); // 读取PIR状态
// 更新温度数据(每2秒更新一次)
if (millis() - lastTempUpdate > 2000) {
tempSensor.requestTemperatures();
currentTemp = tempSensor.getTempCByIndex(0);
lastTempUpdate = millis();
// 检查温度报警
isTempAlarm = (currentTemp > tempThreshold);
}
// 温度报警闪烁处理
if (isTempAlarm && millis() - lastBlinkTime > 500) {
blinkState = !blinkState;
lastBlinkTime = millis();
}
// 人员通过检测(优化后的双沿触发逻辑)
detectPassing(currentPirState);
// 时间设置模式处理
if (isTimeSetting) {
handleTimeSetting(); // 处理时间调整
displayTimeSetting(); // 显示时间设置界面
} else {
// 常规模式处理
handleModeSwitch(); // 处理模式切换
handleButtonActions(); // 处理上下调整按键
handlePageSwitch(); // 处理页面切换
// 灯光控制逻辑(优化自动模式条件)
if (isManualMode) {
// 手动模式下,如果温度报警则闪烁,否则正常控制
if (isTempAlarm) {
analogWrite(LED_PIN, blinkState ? 255 : 0);
isLightOn = blinkState;
} else {
analogWrite(LED_PIN, ledBrightness);
isLightOn = (ledBrightness > 0);
}
} else {
// 自动模式下根据光照和人员状态控制灯光
if (isTempAlarm) {
// 温度报警时优先处理闪烁
analogWrite(LED_PIN, blinkState ? 255 : 0);
isLightOn = blinkState;
} else if (lightValue >= lightCritical) {
// 强光下强制关灯
analogWrite(LED_PIN, 0);
isLightOn = false;
} else if (lightValue < lightThreshold) {
// 光照不足时,仅当有人在场时开灯
if (isPersonPresent) {
analogWrite(LED_PIN, 255);
isLightOn = true;
lastMotionTime = millis(); // 记录最后移动时间
} else {
analogWrite(LED_PIN, 0);
isLightOn = false;
}
} else {
// 中等光照时关灯
analogWrite(LED_PIN, 0);
isLightOn = false;
}
}
// 根据当前页面显示对应数据
displayCurrentPage(lightValue, currentPirState);
}
delay(100); // 防止CPU占用过高
}
// 更新时间值数组(从RTC读取)
void updateTimeValues() {
DateTime now = rtc.now();
timeValues[0] = now.year();
timeValues[1] = now.month();
timeValues[2] = now.day();
timeValues[3] = now.hour();
timeValues[4] = now.minute();
timeValues[5] = now.second();
}
// 人员通过检测(优化后的双沿触发逻辑)
void detectPassing(bool currentState) {
static bool isInside = false; // 人员是否在检测区域内
// 上升沿:人员进入区域
if (currentState == HIGH && lastPirState == LOW) {
isInside = true;
isPersonPresent = true; // 标记有人在场
lastMotionTime = millis(); // 更新最后移动时间
}
// 下降沿:人员离开区域(触发有效计数)
else if (currentState == LOW && lastPirState == HIGH && isInside) {
personCount++; // 离开时计数+1
isInside = false;
isPersonPresent = false; // 离开后标记无人
}
// 持续高电平:人员持续在场
else if (currentState == HIGH) {
isPersonPresent = true;
lastMotionTime = millis(); // 更新最后移动时间
}
// 持续低电平:人员离开后标记为无人
else if (currentState == LOW) {
isPersonPresent = false;
}
lastPirState = currentState; // 更新上一状态
}
// 模式切换处理(自动/手动模式切换,长按进入时间设置)
void handleModeSwitch() {
bool currentModeState = digitalRead(MODE_BUTTON);
if (currentModeState != lastModeState) {
delay(20); // 按键防抖
currentModeState = digitalRead(MODE_BUTTON);
if (!currentModeState) { // 按键按下
unsigned long pressStart = millis();
// 检测长按(2秒进入时间设置)
while (!digitalRead(MODE_BUTTON)) {
if (millis() - pressStart > 2000) {
isTimeSetting = true;
updateTimeValues(); // 获取当前时间
return;
}
}
// 短按切换自动/手动模式
if (!isTimeSetting) {
isManualMode = !isManualMode;
if (isManualMode) {
ledBrightness = 0; // 手动模式初始亮度为0
isCriticalMode = false; // 退出临界值调整模式
isTempThresholdMode = false; // 退出温度阈值调整模式
}
}
}
lastModeState = currentModeState;
}
}
// 上下按键操作处理(调整参数)
void handleButtonActions() {
static bool isLongPressHandled = false;
bool upState = digitalRead(UP_BUTTON);
bool downState = digitalRead(DOWN_BUTTON);
if (!upState || !downState) {
delay(20); // 按键防抖
upState = digitalRead(UP_BUTTON);
downState = digitalRead(DOWN_BUTTON);
// 检测长按(500ms进入调整模式)
if ((!upState || !downState) && !isLongPressHandled) {
unsigned long pressStart = millis();
while (!digitalRead(UP_BUTTON) || !digitalRead(DOWN_BUTTON)) {
if (millis() - pressStart > 500) {
// 长按处理 - 切换调整模式
if (!isManualMode) {
if (!isCriticalMode && !isTempThresholdMode) {
isCriticalMode = true; // 进入光照临界值调整模式
} else if (isCriticalMode) {
isCriticalMode = false;
isTempThresholdMode = true; // 切换到温度阈值调整模式
} else {
isTempThresholdMode = false; // 退出所有调整模式
}
}
isLongPressHandled = true; // 标记长按已处理
break;
}
}
}
} else {
isLongPressHandled = false; // 按键释放后重置长按处理标志
}
// 手动模式下调整LED亮度
if (isManualMode) {
if (!upState && ledBrightness < 255) {
ledBrightness += 25; // 亮度递增
} else if (!downState && ledBrightness > 0) {
ledBrightness -= 25; // 亮度递减
}
} else {
// 温度阈值调整模式
if (isTempThresholdMode) {
if (!upState) tempThreshold = min(tempThreshold + 5, 60); // 温度阈值递增(最大60℃)
if (!downState) tempThreshold = max(tempThreshold - 5, 10); // 温度阈值递减(最小10℃)
}
// 光照临界值调整模式
else if (isCriticalMode) {
if (!upState) lightCritical = min(lightCritical + 50, 1023); // 临界值递增
if (!downState) lightCritical = max(lightCritical - 50, lightThreshold + 1); // 临界值递减
}
// 光照阈值调整模式(仅在非调整模式下才允许修改)
else if (!isCriticalMode && !isTempThresholdMode) {
if (!upState) lightThreshold = min(lightThreshold + 50, lightCritical - 1); // 阈值递增
if (!downState) lightThreshold = max(lightThreshold - 50, 0); // 阈值递减
}
}
}
// 页面切换处理
void handlePageSwitch() {
bool currentPageState = digitalRead(PAGE_BUTTON);
if (currentPageState != lastPageState) {
delay(20); // 按键防抖
currentPageState = digitalRead(PAGE_BUTTON);
if (!currentPageState) { // 页面按键按下
currentPage = (currentPage + 1) % PAGE_COUNT; // 循环切换页面
// 切换页面时退出所有调整模式
isCriticalMode = false;
isTempThresholdMode = false;
}
lastPageState = currentPageState;
}
}
// 时间设置处理(年月日时分秒)
void handleTimeSetting() {
static bool lastUpState = HIGH;
static bool lastDownState = HIGH;
static bool lastModeState = HIGH;
bool upState = digitalRead(UP_BUTTON);
bool downState = digitalRead(DOWN_BUTTON);
bool modeState = digitalRead(MODE_BUTTON);
// 按键防抖处理
if (upState != lastUpState) {
delay(20);
upState = digitalRead(UP_BUTTON);
}
if (downState != lastDownState) {
delay(20);
downState = digitalRead(DOWN_BUTTON);
}
if (modeState != lastModeState) {
delay(20);
modeState = digitalRead(MODE_BUTTON);
if (!modeState) { // 模式按键按下
unsigned long pressStart = millis();
// 长按保存时间设置并退出
while (!digitalRead(MODE_BUTTON)) {
if (millis() - pressStart > 2000) {
rtc.adjust(DateTime(
timeValues[0], // 年
timeValues[1], // 月
timeValues[2], // 日
timeValues[3], // 时
timeValues[4], // 分
timeValues[5] // 秒
));
isTimeSetting = false;
return;
}
}
// 短按切换设置字段(年月日时分秒循环)
timeSettingIndex = (timeSettingIndex + 1) % 6;
}
}
// 调整当前选中的时间字段
if (!upState && lastUpState) {
timeValues[timeSettingIndex] = min(timeValues[timeSettingIndex] + 1, timeMaxValues[timeSettingIndex]);
}
if (!downState && lastDownState) {
timeValues[timeSettingIndex] = max(timeValues[timeSettingIndex] - 1, timeMinValues[timeSettingIndex]);
}
lastUpState = upState;
lastDownState = downState;
lastModeState = modeState;
}
// 显示时间设置界面
void displayTimeSetting() {
display.clearDisplay();
display.setTextSize(0.5);
display.setCursor(0, 0);
display.println("时间设置 - 长按保存");
// 显示时间设置行(年月日 时分秒)
display.setTextSize(0.5);
display.setCursor(0, 16);
for (int i = 0; i < 6; i++) {
if (i == timeSettingIndex) {
display.setTextColor(BLACK, WHITE); // 反色显示当前选中项
} else {
display.setTextColor(WHITE);
}
// 格式化显示两位数字(不足10补0)
if (timeValues[i] < 10) display.print('0');
display.print(timeValues[i]);
if (i < 2) display.print('/'); // 年月日分隔符
else if (i < 5) display.print(':'); // 时分秒分隔符
display.setTextColor(WHITE); // 恢复默认颜色
}
// 显示当前设置的字段提示
display.setTextSize(0.5);
display.setCursor(0, 48);
display.print("当前设置: ");
display.println(timeLabels[timeSettingIndex]);
display.display();
}
// 根据当前页面显示对应数据
void displayCurrentPage(int lightValue, bool currentMotion) {
display.clearDisplay();
if (currentPage == 0) { // 主页面(显示时间、模式、阈值)
displayMainPage(lightValue);
} else if (currentPage == 1) { // 副页面(显示状态、计数、详细参数)
displaySubPage(lightValue, currentMotion);
} else { // 第三页面(显示温度信息)
displayTempPage();
}
display.display();
}
// 主页面显示函数(时间、模式、阈值)
void displayMainPage(int lightValue) {
// 大字体显示实时时间(时:分:秒)
DateTime now = rtc.now();
display.setTextSize(0.8);
display.setCursor(0, 0);
if (now.hour() < 10) display.print('0');
display.print(now.hour());
display.print(':');
if (now.minute() < 10) display.print('0');
display.print(now.minute());
display.print(':');
if (now.second() < 10) display.print('0');
display.println(now.second());
// 显示日期(年/月/日)
display.setTextSize(0.5);
display.setCursor(0, 12);
display.print(now.year());
display.print('/');
if (now.month() < 10) display.print('0');
display.print(now.month());
display.print('/');
if (now.day() < 10) display.print('0');
display.println(now.day());
// 显示工作模式(自动/手动)
display.setTextSize(0.6);
display.setCursor(0, 28);
if (isManualMode) {
display.print("MODE: MANUAL");
} else {
display.print("MODE: AUTO");
// 显示是否处于临界值调整模式
if (isCriticalMode) {
display.print(" (CRIT ADJ)");
} else if (isTempThresholdMode) {
display.print(" (TEMP ADJ)");
}
}
// 显示光照阈值和临界值
display.setTextSize(0.5);
display.setCursor(0, 40);
display.print("LIGHT THRESH: ");
display.print(lightThreshold);
display.setCursor(0, 50);
display.print("LIGHT CRIT: ");
display.println(lightCritical);
// 页面指示(显示当前页码)
display.setTextSize(0.4);
display.setCursor(100, 58);
display.print("PAGE 1/3");
}
// 副页面显示函数(状态、计数、详细参数)
void displaySubPage(int lightValue, bool currentMotion) {
// 显示工作模式和详细参数
display.setTextSize(0.6);
display.setCursor(0, 0);
if (isManualMode) {
display.print("MANUAL MODE");
display.setCursor(0, 12);
display.print("BRIGHT: ");
display.print((ledBrightness / 255.0) * 100, 0); // 亮度百分比
display.println("%");
} else {
display.print("AUTO MODE");
display.setCursor(0, 12);
display.print("LIGHT: ");
display.println(lightValue); // 实时光照值
}
// 显示人员 presence 状态
display.setTextSize(0.6);
display.setCursor(0, 28);
display.print("PRESENCE: ");
if (isPersonPresent) {
display.print("YES");
} else {
display.print("NO");
}
// 显示灯泡开关状态
display.setCursor(0, 40);
display.print("LIGHT: ");
if (isLightOn) {
display.print("ON");
} else {
display.print("OFF");
}
// 大字体显示累计通过人数
display.setTextSize(0.8);
display.setCursor(0, 50);
display.print("COUNT: ");
display.println(personCount);
// 页面指示(显示当前页码)
display.setTextSize(0.4);
display.setCursor(100, 58);
display.print("PAGE 2/3");
}
// 温度页面显示函数
void displayTempPage() {
// 显示温度调整状态
display.setTextSize(0.5);
display.setCursor(0, 0);
if (isTempThresholdMode) {
display.print("> TEMP THRESH ADJUST <");
} else {
display.print("TEMP MONITOR MODE");
}
// 大字体显示当前温度
display.setTextSize(1.5);
display.setCursor(0, 12);
display.print("CURRENT: ");
display.print(currentTemp, 1); // 显示1位小数
display.println(" C");
// 显示温度报警状态
display.setTextSize(0.8);
display.setCursor(0, 30);
display.print("ALARM THRESH: ");
if (isTempThresholdMode) {
display.setTextColor(WHITE); // 反色显示当前调整项
}
display.print(tempThreshold);
display.println(" C");
display.setTextColor(WHITE); // 恢复默认颜色
// 显示温度报警状态
display.setTextSize(0.6);
display.setCursor(0, 45);
display.print("ALARM: ");
if (isTempAlarm) {
display.setTextColor(WHITE); // 反色显示报警状态
display.print("ON ");
display.setTextColor(WHITE); // 恢复默认颜色
} else {
display.print("OFF");
}
// 页面指示(显示当前页码)
display.setTextSize(0.4);
display.setCursor(100, 58);
display.print("PAGE 3/3");
}