| | |
| | | #include "system.h" |
| | | #include "config.h" |
| | | #include "app_task.h" |
| | | #include "parent_schedule.h" |
| | | #include "display/lvgl_display/jpg/jpeg_to_image.h" |
| | | |
| | | #include <esp_heap_caps.h> |
| | |
| | | static bool battery_charging_gpio_initialized; |
| | | static alarm_entry_t current_alarm1_entry; |
| | | static bool current_alarm1_valid; |
| | | static alarm_entry_t schedule_wake_entry; |
| | | static bool schedule_wake_valid; |
| | | static TaskHandle_t alarm_monitor_task_handle; |
| | | static TaskHandle_t alarm_power_off_restore_task_handle; |
| | | |
| | |
| | | static bool get_current_time(i2c_dev_t *i2c_dev, struct tm *timeinfo); |
| | | static int select_next_alarm_index(const alarm_store_t *store, const struct tm *now); |
| | | static int alarm_delta_seconds(const alarm_entry_t *entry, const struct tm *now); |
| | | static const alarm_entry_t *select_soonest_alarm(const alarm_store_t *store, const struct tm *now); |
| | | static void program_soonest_alarm(i2c_dev_t *i2c_dev, const struct tm *now); |
| | | static int mqtt_weekday_to_tm_wday(int day); |
| | | static uint8_t tm_wday_to_repeat_bit(int tm_wday); |
| | | static bool alarm_entry_equals(const alarm_entry_t *left, const alarm_entry_t *right); |
| | |
| | | if (!command || command[0] == '\0') { |
| | | return false; |
| | | } |
| | | if (Board_Is_Chat_Return_Command(command)) { |
| | | return false; |
| | | } |
| | | return strcmp(command, OFFLINE_REPEATER_WORD) == 0 || |
| | | strcmp(command, OFFLINE_START_REPEATER_WORD) == 0 || |
| | | strstr(command, OFFLINE_REPEATER_WORD) || |
| | | strstr(command, OFFLINE_START_REPEATER_WORD); |
| | | strstr(command, OFFLINE_START_REPEATER_WORD) || |
| | | strstr(command, "打开泛听"); |
| | | } |
| | | |
| | | bool Board_Handle_Offline_Command(const char *command) |
| | |
| | | esp_sntp_init(); |
| | | } |
| | | |
| | | void DS2312_Update_Alarm(i2c_dev_t *i2c_dev, struct tm *time) |
| | | static const alarm_entry_t *select_soonest_alarm(const alarm_store_t *store, const struct tm *now) |
| | | { |
| | | const alarm_entry_t *best = NULL; |
| | | int best_delta = SECONDS_PER_DAY * 8 + 1; |
| | | if (store && store->count > 0) { |
| | | int index = select_next_alarm_index(store, now); |
| | | if (index >= 0) { |
| | | best = &store->entries[index]; |
| | | best_delta = alarm_delta_seconds(best, now); |
| | | } |
| | | } |
| | | if (schedule_wake_valid) { |
| | | int delta = alarm_delta_seconds(&schedule_wake_entry, now); |
| | | if (delta < best_delta) { |
| | | best = &schedule_wake_entry; |
| | | } |
| | | } |
| | | return best; |
| | | } |
| | | |
| | | static void program_soonest_alarm(i2c_dev_t *i2c_dev, const struct tm *now) |
| | | { |
| | | alarm_store_t store; |
| | | if (load_alarm_store(&store) != ESP_OK) { |
| | | disable_alarm1(i2c_dev); |
| | | memset(&store, 0, sizeof(store)); |
| | | } |
| | | const alarm_entry_t *best = select_soonest_alarm(&store, now); |
| | | if (!best) { |
| | | if (current_alarm1_valid && !Board_Is_Power_Off()) { |
| | | disable_alarm1(i2c_dev); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | if (store.count == 0) { |
| | | disable_alarm1(i2c_dev); |
| | | ESP_LOGI(TAG, "alarm list is empty"); |
| | | if (!current_alarm1_valid) { |
| | | program_alarm1(i2c_dev, best); |
| | | return; |
| | | } |
| | | int next_delta = alarm_delta_seconds(best, now); |
| | | int current_delta = alarm_delta_seconds(¤t_alarm1_entry, now); |
| | | if (next_delta < current_delta || !alarm_entry_equals(best, ¤t_alarm1_entry)) { |
| | | program_alarm1(i2c_dev, best); |
| | | } |
| | | } |
| | | |
| | | void DS2312_Update_Alarm(i2c_dev_t *i2c_dev, struct tm *time) |
| | | { |
| | | struct tm now; |
| | | if (time) { |
| | | now = *time; |
| | |
| | | ESP_LOGW(TAG, "current time unavailable, skip alarm update"); |
| | | return; |
| | | } |
| | | |
| | | int index = select_next_alarm_index(&store, &now); |
| | | if (index < 0) { |
| | | disable_alarm1(i2c_dev); |
| | | return; |
| | | } |
| | | |
| | | program_alarm1(i2c_dev, &store.entries[index]); |
| | | program_soonest_alarm(i2c_dev, &now); |
| | | } |
| | | |
| | | void DS2312_Start_Alarm_Monitor(i2c_dev_t *i2c_dev) |
| | |
| | | alarm_monitor_task_handle = NULL; |
| | | ESP_LOGE(TAG, "create alarm monitor task failed"); |
| | | } |
| | | } |
| | | |
| | | bool Board_Get_Wall_Clock(struct tm *out) |
| | | { |
| | | if (!out) { |
| | | return false; |
| | | } |
| | | return get_current_time(ds2321_dev, out); |
| | | } |
| | | |
| | | esp_err_t DS2312_Set_Schedule_Wake(int hour, int min, int sec, uint8_t weekday_bit) |
| | | { |
| | | if (hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 59 || |
| | | (weekday_bit & ALARM_REPEAT_ALL) == 0) { |
| | | return ESP_ERR_INVALID_ARG; |
| | | } |
| | | memset(&schedule_wake_entry, 0, sizeof(schedule_wake_entry)); |
| | | strlcpy(schedule_wake_entry.id, "_sched", sizeof(schedule_wake_entry.id)); |
| | | schedule_wake_entry.hour = (uint8_t)hour; |
| | | schedule_wake_entry.min = (uint8_t)min; |
| | | schedule_wake_entry.sec = (uint8_t)sec; |
| | | schedule_wake_entry.flags = ALARM_ENTRY_ENABLED; |
| | | schedule_wake_entry.repeat_mask = (uint8_t)(weekday_bit & ALARM_REPEAT_ALL); |
| | | schedule_wake_valid = true; |
| | | DS2312_Update_Alarm(ds2321_dev, NULL); |
| | | ESP_LOGI(TAG, "schedule wake %02d:%02d:%02d mask=0x%02x", hour, min, sec, schedule_wake_entry.repeat_mask); |
| | | return ESP_OK; |
| | | } |
| | | |
| | | void DS2312_Clear_Schedule_Wake(void) |
| | | { |
| | | if (!schedule_wake_valid) { |
| | | return; |
| | | } |
| | | schedule_wake_valid = false; |
| | | memset(&schedule_wake_entry, 0, sizeof(schedule_wake_entry)); |
| | | DS2312_Update_Alarm(ds2321_dev, NULL); |
| | | } |
| | | |
| | | void DS2312_Add_Alarm_Time(i2c_dev_t *i2c_dev, int hour, int min, int sec) |
| | |
| | | while (true) { |
| | | vTaskDelay(pdMS_TO_TICKS(1000)); |
| | | |
| | | alarm_store_t store; |
| | | if (load_alarm_store(&store) != ESP_OK || store.count == 0) { |
| | | if (current_alarm1_valid) { |
| | | disable_alarm1(i2c_dev); |
| | | } |
| | | continue; |
| | | } |
| | | |
| | | struct tm now; |
| | | if (!get_current_time(i2c_dev, &now)) { |
| | | continue; |
| | |
| | | ESP_LOGI(TAG, "DS3231 alarm1 triggered"); |
| | | ds3231_clear_alarm_flags(i2c_dev, DS3231_ALARM_1); |
| | | current_alarm1_valid = false; |
| | | schedule_power_off_restore_from_alarm(); |
| | | } |
| | | |
| | | int next_index = select_next_alarm_index(&store, &now); |
| | | if (next_index < 0) { |
| | | if (current_alarm1_valid && !Board_Is_Power_Off()) { |
| | | disable_alarm1(i2c_dev); |
| | | if (Board_Schedule_ClaimDue()) { |
| | | if (Board_Is_Power_Off()) { |
| | | schedule_power_off_restore_from_alarm(); |
| | | } else { |
| | | Board_Schedule_OnAlarmFired(); |
| | | } |
| | | } |
| | | continue; |
| | | } else if (Board_Schedule_ClaimDue()) { |
| | | ESP_LOGI(TAG, "schedule wall clock due"); |
| | | if (Board_Is_Power_Off()) { |
| | | schedule_power_off_restore_from_alarm(); |
| | | } else { |
| | | Board_Schedule_OnAlarmFired(); |
| | | } |
| | | } |
| | | |
| | | alarm_entry_t *next_alarm = &store.entries[next_index]; |
| | | if (!current_alarm1_valid) { |
| | | program_alarm1(i2c_dev, next_alarm); |
| | | continue; |
| | | } |
| | | |
| | | int next_delta = alarm_delta_seconds(next_alarm, &now); |
| | | int current_delta = alarm_delta_seconds(¤t_alarm1_entry, &now); |
| | | if (next_delta < current_delta || !alarm_entry_equals(next_alarm, ¤t_alarm1_entry)) { |
| | | program_alarm1(i2c_dev, next_alarm); |
| | | } |
| | | Board_Schedule_OnLessonFilesUpdated(); |
| | | program_soonest_alarm(i2c_dev, &now); |
| | | } |
| | | } |
| | | |