Files
petio/src/timer_manager.cpp

580 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "timer_manager.h"
TimerManager::TimerManager() {
timerCount = 0;
timeManager = nullptr;
lastStateSave = 0;
}
void TimerManager::begin(TimeManager* tm) {
timeManager = tm;
// 初始化EEPROM
EEPROM.begin(EEPROM_SIZE);
// 初始化所有可用引脚为输出模式
for (int i = 0; i < AVAILABLE_PINS_COUNT; i++) {
pinMode(AVAILABLE_PINS[i], OUTPUT);
digitalWrite(AVAILABLE_PINS[i], LOW);
// 为PWM引脚设置频率
analogWriteFreq(PWM_FREQUENCY);
analogWriteResolution(PWM_RESOLUTION);
}
loadTimers();
Serial.println("Timer Manager 初始化完成,已加载 " + String(timerCount) + " 个定时器");
// 输出恢复的状态信息
int activeCount = 0;
for (int i = 0; i < timerCount; i++) {
if (timers[i].isActive) {
activeCount++;
}
}
if (activeCount > 0) {
Serial.println("已恢复 " + String(activeCount) + " 个活跃定时器状态");
}
}
void TimerManager::update() {
if (!timeManager) return;
unsigned long currentTime = millis();
int currentHour = timeManager->getCurrentHour();
int currentMinute = timeManager->getCurrentMinute();
unsigned long currentDay = timeManager->getCurrentDay();
bool stateChanged = false;
for (int i = 0; i < timerCount; i++) {
if (!timers[i].enabled) continue;
// 检查是否到达触发时间
bool shouldTrigger = false;
if (!timers[i].isActive &&
timers[i].hour == currentHour &&
timers[i].minute == currentMinute) {
if (timers[i].repeatDaily) {
// 每天重复:检查今天是否已经触发过
if (timers[i].lastTriggerDay != currentDay) {
shouldTrigger = true;
timers[i].lastTriggerDay = currentDay;
}
} else {
// 单次触发
shouldTrigger = true;
}
}
if (shouldTrigger) {
timers[i].isActive = true;
timers[i].startTime = currentTime;
// 保存真实时间戳(如果可用)
if (timeManager->isTimeValid()) {
timers[i].realStartTime = timeManager->getEpochTime();
} else {
timers[i].realStartTime = 0;
}
setPin(timers[i].pin, HIGH, timers[i].isPWM ? timers[i].pwmValue : 0);
String modeStr = timers[i].isPWM ? " PWM(" + String(timers[i].pwmValue) + ")" : "";
Serial.println("定时器 " + String(i) + " 激活,引脚 " + String(timers[i].pin) + " 开启" + modeStr +
(timers[i].repeatDaily ? " (每天重复)" : " (单次)"));
stateChanged = true;
// 如果是单次定时器,触发后自动禁用
if (!timers[i].repeatDaily) {
timers[i].enabled = false;
saveTimers(); // 立即保存配置更改
}
}
// 检查是否需要关闭
if (timers[i].isActive &&
currentTime - timers[i].startTime >= (unsigned long)(timers[i].duration * 1000.0)) {
timers[i].isActive = false;
timers[i].realStartTime = 0; // 清理真实时间戳
setPin(timers[i].pin, LOW, 0);
Serial.println("定时器 " + String(i) + " 完成,引脚 " + String(timers[i].pin) + " 关闭");
stateChanged = true;
}
}
// 定期保存状态,或者有状态变化时立即保存
if (stateChanged || (currentTime - lastStateSave >= STATE_SAVE_INTERVAL)) {
saveTimerStates();
lastStateSave = currentTime;
}
}
bool TimerManager::addTimer(int pin, int hour, int minute, float duration, bool repeatDaily, bool isPWM, int pwmValue) {
if (timerCount >= MAX_TIMERS) {
return false;
}
// 验证引脚是否可用
bool pinValid = false;
for (int i = 0; i < AVAILABLE_PINS_COUNT; i++) {
if (AVAILABLE_PINS[i] == pin) {
pinValid = true;
break;
}
}
if (!pinValid) return false;
// 验证时间
if (hour < 0 || hour > 23 || minute < 0 || minute > 59 || duration <= 0.0) {
return false;
}
// 验证PWM值
if (isPWM && (pwmValue < 0 || pwmValue > PWM_MAX_VALUE)) {
return false;
}
timers[timerCount].enabled = true;
timers[timerCount].pin = pin;
timers[timerCount].hour = hour;
timers[timerCount].minute = minute;
timers[timerCount].duration = duration;
timers[timerCount].repeatDaily = repeatDaily;
timers[timerCount].isActive = false;
timers[timerCount].startTime = 0;
timers[timerCount].lastTriggerDay = 0; // 初始化为0
timers[timerCount].isPWM = isPWM;
timers[timerCount].pwmValue = isPWM ? pwmValue : 0;
timers[timerCount].realStartTime = 0; // 初始化真实时间戳
timerCount++;
saveTimers();
String modeStr = isPWM ? " PWM模式, 值=" + String(pwmValue) : " 数字模式";
Serial.println("添加定时器:引脚 " + String(pin) + ", 时间 " + String(hour) + ":" + String(minute) +
", 持续 " + String(duration) + "" + modeStr + (repeatDaily ? " (每天重复)" : " (单次)"));
return true;
}
bool TimerManager::removeTimer(int index) {
if (index < 0 || index >= timerCount) {
return false;
}
// 如果定时器正在运行,先关闭引脚
if (timers[index].isActive) {
setPin(timers[index].pin, LOW, 0);
}
// 移动数组元素
for (int i = index; i < timerCount - 1; i++) {
timers[i] = timers[i + 1];
}
timerCount--;
saveTimers();
Serial.println("删除定时器 " + String(index));
return true;
}
bool TimerManager::updateTimer(int index, int pin, int hour, int minute, float duration, bool enabled, bool repeatDaily, bool isPWM, int pwmValue) {
if (index < 0 || index >= timerCount) {
return false;
}
// 验证引脚
bool pinValid = false;
for (int i = 0; i < AVAILABLE_PINS_COUNT; i++) {
if (AVAILABLE_PINS[i] == pin) {
pinValid = true;
break;
}
}
if (!pinValid) return false;
// 验证时间
if (hour < 0 || hour > 23 || minute < 0 || minute > 59 || duration <= 0.0) {
return false;
}
// 验证PWM值
if (isPWM && (pwmValue < 0 || pwmValue > PWM_MAX_VALUE)) {
return false;
}
// 如果定时器正在运行且引脚发生变化,先关闭旧引脚
if (timers[index].isActive && timers[index].pin != pin) {
setPin(timers[index].pin, LOW, 0);
timers[index].isActive = false;
}
timers[index].enabled = enabled;
timers[index].pin = pin;
timers[index].hour = hour;
timers[index].minute = minute;
timers[index].duration = duration;
timers[index].repeatDaily = repeatDaily;
timers[index].isPWM = isPWM;
timers[index].pwmValue = isPWM ? pwmValue : 0;
// 如果修改了重复设置,重置触发状态
timers[index].lastTriggerDay = 0;
saveTimers();
Serial.println("更新定时器 " + String(index));
return true;
}
String TimerManager::getTimersJSON() {
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
for (int i = 0; i < timerCount; i++) {
JsonObject timer = array.add<JsonObject>();
timer["index"] = i;
timer["enabled"] = timers[i].enabled;
timer["pin"] = timers[i].pin;
timer["hour"] = timers[i].hour;
timer["minute"] = timers[i].minute;
timer["duration"] = timers[i].duration;
timer["repeatDaily"] = timers[i].repeatDaily;
timer["isActive"] = timers[i].isActive;
timer["isPWM"] = timers[i].isPWM;
timer["pwmValue"] = timers[i].pwmValue;
}
String result;
serializeJson(doc, result);
return result;
}
String TimerManager::getAvailablePinsJSON() {
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
for (int i = 0; i < AVAILABLE_PINS_COUNT; i++) {
JsonObject pin = array.add<JsonObject>();
pin["pin"] = AVAILABLE_PINS[i];
pin["state"] = digitalRead(AVAILABLE_PINS[i]);
// 检查是否被定时器占用
bool inUse = false;
for (int j = 0; j < timerCount; j++) {
if (timers[j].pin == AVAILABLE_PINS[i] && timers[j].isActive) {
inUse = true;
break;
}
}
pin["inUse"] = inUse;
}
String result;
serializeJson(doc, result);
return result;
}
void TimerManager::executeManualControl(int pin, float duration, bool isPWM, int pwmValue) {
// 验证引脚
bool pinValid = false;
for (int i = 0; i < AVAILABLE_PINS_COUNT; i++) {
if (AVAILABLE_PINS[i] == pin) {
pinValid = true;
break;
}
}
if (!pinValid) return;
// 验证PWM值
if (isPWM && (pwmValue < 0 || pwmValue > PWM_MAX_VALUE)) {
return;
}
if (duration > 0.0) {
// 开启引脚指定时间
setPin(pin, HIGH, isPWM ? pwmValue : 0);
String modeStr = isPWM ? " PWM模式, 值=" + String(pwmValue) : " 数字模式";
Serial.println("手动控制:引脚 " + String(pin) + " 开启 " + String(duration) + "" + modeStr);
// 这里应该使用定时器来关闭,简化处理
delay((unsigned long)(duration * 1000.0));
setPin(pin, LOW, 0);
Serial.println("手动控制:引脚 " + String(pin) + " 关闭");
} else {
// 切换状态
bool currentState = digitalRead(pin);
if (isPWM) {
// PWM模式切换
if (currentState) {
setPin(pin, LOW, 0);
Serial.println("手动控制:引脚 " + String(pin) + " PWM关闭");
} else {
setPin(pin, HIGH, pwmValue);
Serial.println("手动控制:引脚 " + String(pin) + " PWM开启, 值=" + String(pwmValue));
}
} else {
// 数字模式切换
setPin(pin, !currentState, 0);
Serial.println("手动控制:引脚 " + String(pin) + " 数字切换到 " + String(!currentState));
}
}
}
void TimerManager::saveTimers() {
int addr = TIMER_CONFIG_ADDR;
// 保存定时器数量
EEPROM.write(addr++, timerCount);
// 保存每个定时器
for (int i = 0; i < timerCount; i++) {
EEPROM.write(addr++, timers[i].enabled ? 1 : 0);
EEPROM.write(addr++, timers[i].pin);
EEPROM.write(addr++, timers[i].hour);
EEPROM.write(addr++, timers[i].minute);
// 保存float类型的duration4字节
union {
float f;
uint8_t bytes[4];
} durationConverter;
durationConverter.f = timers[i].duration;
EEPROM.write(addr++, durationConverter.bytes[0]);
EEPROM.write(addr++, durationConverter.bytes[1]);
EEPROM.write(addr++, durationConverter.bytes[2]);
EEPROM.write(addr++, durationConverter.bytes[3]);
EEPROM.write(addr++, timers[i].repeatDaily ? 1 : 0);
EEPROM.write(addr++, timers[i].isPWM ? 1 : 0);
EEPROM.write(addr++, (timers[i].pwmValue >> 8) & 0xFF);
EEPROM.write(addr++, timers[i].pwmValue & 0xFF);
// 保存运行时状态
EEPROM.write(addr++, timers[i].isActive ? 1 : 0);
// 保存开始时间4字节
unsigned long startTime = timers[i].startTime;
EEPROM.write(addr++, (startTime >> 24) & 0xFF);
EEPROM.write(addr++, (startTime >> 16) & 0xFF);
EEPROM.write(addr++, (startTime >> 8) & 0xFF);
EEPROM.write(addr++, startTime & 0xFF);
// 保存上次触发天数4字节
unsigned long lastTriggerDay = timers[i].lastTriggerDay;
EEPROM.write(addr++, (lastTriggerDay >> 24) & 0xFF);
EEPROM.write(addr++, (lastTriggerDay >> 16) & 0xFF);
EEPROM.write(addr++, (lastTriggerDay >> 8) & 0xFF);
EEPROM.write(addr++, lastTriggerDay & 0xFF);
// 保存真实开始时间4字节
unsigned long realStartTime = timers[i].realStartTime;
EEPROM.write(addr++, (realStartTime >> 24) & 0xFF);
EEPROM.write(addr++, (realStartTime >> 16) & 0xFF);
EEPROM.write(addr++, (realStartTime >> 8) & 0xFF);
EEPROM.write(addr++, realStartTime & 0xFF);
}
EEPROM.commit();
}
void TimerManager::loadTimers() {
int addr = TIMER_CONFIG_ADDR;
// 读取定时器数量
timerCount = EEPROM.read(addr++);
if (timerCount > MAX_TIMERS) timerCount = 0;
// 读取每个定时器
for (int i = 0; i < timerCount; i++) {
timers[i].enabled = EEPROM.read(addr++) == 1;
timers[i].pin = EEPROM.read(addr++);
timers[i].hour = EEPROM.read(addr++);
timers[i].minute = EEPROM.read(addr++);
// 读取float类型的duration4字节
union {
float f;
uint8_t bytes[4];
} durationConverter;
durationConverter.bytes[0] = EEPROM.read(addr++);
durationConverter.bytes[1] = EEPROM.read(addr++);
durationConverter.bytes[2] = EEPROM.read(addr++);
durationConverter.bytes[3] = EEPROM.read(addr++);
timers[i].duration = durationConverter.f;
timers[i].repeatDaily = EEPROM.read(addr++) == 1;
// 检查是否有PWM数据向后兼容
if (addr < EEPROM_SIZE - 20) { // 更新了字节数计算duration现在是4字节而不是2字节
timers[i].isPWM = EEPROM.read(addr++) == 1;
int pwmHigh = EEPROM.read(addr++);
int pwmLow = EEPROM.read(addr++);
timers[i].pwmValue = (pwmHigh << 8) | pwmLow;
// 读取运行时状态
timers[i].isActive = EEPROM.read(addr++) == 1;
// 读取开始时间4字节
unsigned long startTime = 0;
startTime |= ((unsigned long)EEPROM.read(addr++)) << 24;
startTime |= ((unsigned long)EEPROM.read(addr++)) << 16;
startTime |= ((unsigned long)EEPROM.read(addr++)) << 8;
startTime |= (unsigned long)EEPROM.read(addr++);
timers[i].startTime = startTime;
// 读取上次触发天数4字节
unsigned long lastTriggerDay = 0;
lastTriggerDay |= ((unsigned long)EEPROM.read(addr++)) << 24;
lastTriggerDay |= ((unsigned long)EEPROM.read(addr++)) << 16;
lastTriggerDay |= ((unsigned long)EEPROM.read(addr++)) << 8;
lastTriggerDay |= (unsigned long)EEPROM.read(addr++);
timers[i].lastTriggerDay = lastTriggerDay;
// 读取真实开始时间4字节
unsigned long realStartTime = 0;
realStartTime |= ((unsigned long)EEPROM.read(addr++)) << 24;
realStartTime |= ((unsigned long)EEPROM.read(addr++)) << 16;
realStartTime |= ((unsigned long)EEPROM.read(addr++)) << 8;
realStartTime |= (unsigned long)EEPROM.read(addr++);
timers[i].realStartTime = realStartTime;
} else {
// 旧数据没有PWM和运行时状态信息设为默认值
timers[i].isPWM = false;
timers[i].pwmValue = 0;
timers[i].isActive = false;
timers[i].startTime = 0;
timers[i].lastTriggerDay = 0;
timers[i].realStartTime = 0;
}
// 恢复活跃定时器的引脚状态
if (timers[i].isActive) {
// 使用真实时间检查定时器是否应该仍然活跃
if (timeManager && timeManager->isTimeValid() && timers[i].realStartTime > 0) {
// 需要添加获取当前时间戳的方法到TimeManager
// 暂时使用millis()逻辑,但添加更好的时间处理
unsigned long currentTime = millis();
// 重置开始时间为当前时间减去已经运行的时间
// 这样可以在重启后继续正确计时
if (timers[i].startTime > currentTime) {
// millis() 已重置,重新计算开始时间
timers[i].startTime = currentTime;
Serial.println("重启后调整定时器 " + String(i) + " 开始时间");
}
unsigned long elapsedTime = currentTime - timers[i].startTime;
// 检查定时器是否已经超时
if (elapsedTime >= (unsigned long)(timers[i].duration * 1000.0)) {
// 定时器已经超时,关闭它
timers[i].isActive = false;
timers[i].realStartTime = 0;
setPin(timers[i].pin, LOW, 0);
Serial.println("重启后发现定时器 " + String(i) + " 已超时,关闭引脚 " + String(timers[i].pin));
} else {
// 定时器仍然有效,恢复引脚状态
setPin(timers[i].pin, HIGH, timers[i].isPWM ? timers[i].pwmValue : 0);
String modeStr = timers[i].isPWM ? " PWM(" + String(timers[i].pwmValue) + ")" : "";
unsigned long remainingTime = (unsigned long)(timers[i].duration * 1000.0) - elapsedTime;
Serial.println("恢复定时器 " + String(i) + " 状态,引脚 " + String(timers[i].pin) + " 开启" + modeStr +
",剩余时间: " + String(remainingTime / 1000) + "");
}
} else {
// 没有有效的时间或者是旧格式数据,保守处理
unsigned long currentTime = millis();
timers[i].startTime = currentTime;
setPin(timers[i].pin, HIGH, timers[i].isPWM ? timers[i].pwmValue : 0);
String modeStr = timers[i].isPWM ? " PWM(" + String(timers[i].pwmValue) + ")" : "";
Serial.println("恢复定时器 " + String(i) + " 状态,引脚 " + String(timers[i].pin) + " 开启" + modeStr +
"(重启后重新计时)");
}
}
}
}
bool TimerManager::hasValidTime() {
return timeManager && timeManager->isTimeValid();
}
void TimerManager::saveTimerStates() {
// 只保存运行时状态,不保存完整配置
// 这样可以减少EEPROM写入次数延长寿命
int addr = TIMER_CONFIG_ADDR + 1; // 跳过定时器数量
for (int i = 0; i < timerCount; i++) {
// 跳过基本配置部分 (12字节: enabled(1) + pin(1) + hour(1) + minute(1) + duration(4) + repeatDaily(1) + isPWM(1) + pwmValue(2))
addr += 12;
// 更新运行时状态部分 (13字节)
EEPROM.write(addr++, timers[i].isActive ? 1 : 0);
// 更新开始时间4字节
unsigned long startTime = timers[i].startTime;
EEPROM.write(addr++, (startTime >> 24) & 0xFF);
EEPROM.write(addr++, (startTime >> 16) & 0xFF);
EEPROM.write(addr++, (startTime >> 8) & 0xFF);
EEPROM.write(addr++, startTime & 0xFF);
// 更新上次触发天数4字节
unsigned long lastTriggerDay = timers[i].lastTriggerDay;
EEPROM.write(addr++, (lastTriggerDay >> 24) & 0xFF);
EEPROM.write(addr++, (lastTriggerDay >> 16) & 0xFF);
EEPROM.write(addr++, (lastTriggerDay >> 8) & 0xFF);
EEPROM.write(addr++, lastTriggerDay & 0xFF);
// 更新真实开始时间4字节
unsigned long realStartTime = timers[i].realStartTime;
EEPROM.write(addr++, (realStartTime >> 24) & 0xFF);
EEPROM.write(addr++, (realStartTime >> 16) & 0xFF);
EEPROM.write(addr++, (realStartTime >> 8) & 0xFF);
EEPROM.write(addr++, realStartTime & 0xFF);
}
EEPROM.commit();
}
void TimerManager::clearAllTimers() {
// 关闭所有激活的引脚
for (int i = 0; i < timerCount; i++) {
if (timers[i].isActive) {
setPin(timers[i].pin, LOW, 0);
}
}
timerCount = 0;
saveTimers();
Serial.println("所有定时器已清除");
}
int TimerManager::getTimerCount() {
return timerCount;
}
TimerConfig TimerManager::getTimer(int index) {
if (index >= 0 && index < timerCount) {
return timers[index];
}
TimerConfig empty = {false, 0, 0, 0, 0.0, false, false, 0, 0UL, false, 0, 0UL};
return empty;
}
void TimerManager::setPin(int pin, bool state, int pwmValue) {
if (pwmValue > 0 && state) {
// PWM 模式
analogWrite(pin, pwmValue);
} else {
// 数字模式
digitalWrite(pin, state ? HIGH : LOW);
}
}