#include <Ds1302.h>
#include <I2CKeyPad.h>
#include <LiquidCrystal_I2C.h>
// 硬件引脚定义
const int RST_PIN = 5; // DS1302 RST
const int DAT_PIN = 6; // DS1302 DAT
const int CLK_PIN = 7; // DS1302 CLK
const int BUZZER_PIN = 8; // 有源蜂鸣器
// 模块初始化
Ds1302 rtc(RST_PIN, CLK_PIN, DAT_PIN);
I2CKeyPad keyPad(0x21);
char keypad_layout[19] = "123A456B789C*0#DNF";
LiquidCrystal_I2C lcd(0x27, 16, 2);
// 全局变量
unsigned long lastKeyPress = 0;
const unsigned long keyDelay = 200;
// 停车计时相关变量
bool settingParking = false;
uint8_t parkingDuration = 0; // 用户设置的停车分钟数
Ds1302::DateTime parkingEndTime; // 计算出的结束时间
bool parkingAlarmActive = false; // 报警触发标志
bool timerActive = false; // 计时器激活标志
void setInitTime()
{
Ds1302::DateTime dt = {
.year = 25,
.month = Ds1302::MONTH_MAR,
.day = 30,
.hour = 1,
.minute = 26,
.second = 30,
.dow = Ds1302::DOW_SUN
};
rtc.setDateTime(&dt);
}
void setup() {
Serial.begin(9600);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
if (!keyPad.begin()) {
Serial.println("Keypad init failed!");
while(1);
}
keyPad.loadKeyMap(keypad_layout);
lcd.init();
lcd.backlight();
lcd.print("Parking Timer");
delay(2000);
lcd.clear();
rtc.init();
// 设置初始时间
setInitTime();
}
void loop() {
Ds1302::DateTime now;
rtc.getDateTime(&now);
// 显示实时信息
displayInfo(now);
// 处理键盘输入
checkKeypad(now);
// 检查停车计时结束(只有当计时器激活时才检查)
if (timerActive && !parkingAlarmActive) {
if (isTimeReached(now, parkingEndTime)) {
parkingAlarmActive = true;
triggerAlarm("PARKING TIME UP!");
}
}
delay(100);
}
// 改进的时间到达判断函数
bool isTimeReached(const Ds1302::DateTime &now, const Ds1302::DateTime &endTime) {
if (now.hour > endTime.hour) return true;
if (now.hour == endTime.hour && now.minute >= endTime.minute) return true;
return false;
}
// 计算停车结束时间(修正版)
void calculateEndTime(const Ds1302::DateTime &startTime, uint8_t duration) {
parkingEndTime = startTime;
// 直接计算总分钟数再转换
uint32_t totalMinutes = startTime.hour * 60 + startTime.minute + duration;
parkingEndTime.hour = (totalMinutes / 60) % 24;
parkingEndTime.minute = totalMinutes % 60;
Serial.print("End Time Calculated: ");
Serial.print(parkingEndTime.hour);
Serial.print(":");
Serial.println(parkingEndTime.minute);
timerActive = true; // 激活计时器
}
// 信息显示函数
void displayInfo(const Ds1302::DateTime &t) {
// 第一行:当前时间(HH:MM:SS)
lcd.setCursor(0, 0);
print2Digits(t.hour); lcd.print(":");
print2Digits(t.minute); lcd.print(":");
print2Digits(t.second);
// 第二行:停车状态
lcd.setCursor(0, 1);
if (settingParking) {
lcd.print("Set Minutes:");
lcd.print(parkingDuration);
}
else if (parkingAlarmActive) {
lcd.print("!! TIME UP !!");
}
else if (timerActive) {
lcd.print("Remaining:");
int remaining = calculateRemainingMinutes(t, parkingEndTime);
print2Digits(remaining / 60); // 小时
lcd.print(":");
print2Digits(remaining % 60); // 分钟
}
else {
lcd.print("Press D to park");
}
}
// 计算剩余分钟数(修正版)
int calculateRemainingMinutes(const Ds1302::DateTime &now, const Ds1302::DateTime &endTime) {
int nowMinutes = now.hour * 60 + now.minute;
int endMinutes = endTime.hour * 60 + endTime.minute;
if (endMinutes >= nowMinutes) {
return endMinutes - nowMinutes;
} else {
// 处理跨日情况
return (24 * 60 - nowMinutes) + endMinutes;
}
}
// 两位数显示辅助函数
void print2Digits(int num) {
if(num < 10) lcd.print('0');
lcd.print(num);
}
// 报警触发函数
void triggerAlarm(const char* msg) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(msg);
lcd.setCursor(0, 1);
lcd.print("Press any key");
while (parkingAlarmActive) {
// 急促报警音(滴滴-滴滴)
for(int i=0; i<2; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
delay(100);
}
delay(800);
// 按键停止
if (keyPad.isPressed() && (millis() - lastKeyPress) > keyDelay) {
parkingAlarmActive = false;
timerActive = false; // 停止计时器
digitalWrite(BUZZER_PIN, LOW);
lcd.clear();
return;
}
}
}
// 键盘处理函数
void checkKeypad(const Ds1302::DateTime &t) {
if (keyPad.isPressed() && (millis() - lastKeyPress) > keyDelay) {
lastKeyPress = millis();
char ch = keyPad.getChar();
// 停止报警(如果正在响)
if (parkingAlarmActive) {
parkingAlarmActive = false;
timerActive = false;
digitalWrite(BUZZER_PIN, LOW);
lcd.clear();
return;
}
switch(ch) {
case 'D': // 进入停车时间设置
if (!settingParking) {
settingParking = true;
parkingDuration = 0;
lcd.clear();
}
break;
case 'B': // 确认设置
if (settingParking && parkingDuration > 0) {
settingParking = false;
calculateEndTime(t, parkingDuration);
lcd.clear();
}
break;
case 'C': // 取消设置
if (settingParking) {
settingParking = false;
parkingDuration = 0;
lcd.clear();
}
break;
default: // 数字输入
if (settingParking && ch >= '0' && ch <= '9') {
parkingDuration = parkingDuration * 10 + (ch - '0');
if (parkingDuration > 999) parkingDuration = 0;
}
break;
}
}
}