/* * SPDX-FileCopyrightText: 2022 Helmut Pozimski * * SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include "ds3231.h" #include "tm1637.h" #include "wifi.h" #include "configuration.h" #include #include #include #define TIME_SYNCED_BIT BIT0 #define SECONDS_TO_MICROSECONDS 1000000 #define THREE_HOURS_IN_SECONDS 3 * 60 * 60 #define HALF_HOUR_IN_SECONDS 30 * 60 static EventGroupHandle_t time_sync_event_group; static uint8_t should_enable_display(uint8_t hour) { return hour < 23 && hour >= 6; } static void update_display(uint8_t hour, uint8_t minute) { tm1637_config display_config; uint8_t enable_display = should_enable_display(hour); tm1637_init(&display_config, TM1637_CLK_PIN, TM1637_DIO_PIN, true, 1); if (hour < 10) { tm1637_set_segment(&display_config, 10, 0, enable_display); tm1637_set_segment(&display_config, hour, 1, enable_display); } else { tm1637_set_segment(&display_config, hour / 10, 0, enable_display); tm1637_set_segment(&display_config, hour % 10, 1, enable_display); } if (minute < 10) { tm1637_set_segment(&display_config, 0, 2, enable_display); tm1637_set_segment(&display_config, minute, 3, enable_display); } else { tm1637_set_segment(&display_config, minute / 10, 2, enable_display); tm1637_set_segment(&display_config, minute % 10, 3, enable_display); } } static struct tm *convert_to_local(struct tm *timeinfo) { setenv("TZ", "GMT+0GMT+0", 1); tzset(); time_t epoch_time = mktime(timeinfo); setenv("TZ", LOCAL_TIMEZONE, 1); tzset(); return localtime(&epoch_time); } static void time_sync_callback(struct timeval *tv) { sntp_stop(); wifi_stop(); struct tm date_time; localtime_r(&tv->tv_sec, &date_time); ds3231_init(DS3231_SDA_PIN, DS3231_SCL_PIN); ds3231_write_date_time(date_time); struct tm *local_time = convert_to_local(&date_time); update_display(local_time->tm_hour, local_time->tm_min); xEventGroupSetBits(time_sync_event_group, TIME_SYNCED_BIT); } static void sync_time() { wifi_init(); if (wifi_start()) { sntp_setoperatingmode(SNTP_OPMODE_POLL); sntp_setservername(0, "pool.ntp.org"); sntp_init(); time_sync_event_group = xEventGroupCreate(); sntp_set_time_sync_notification_cb(&time_sync_callback); xEventGroupWaitBits(time_sync_event_group, TIME_SYNCED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); vEventGroupDelete(time_sync_event_group); } } static void check_sync(struct tm *date_time) { if (date_time->tm_year == 0 || (date_time->tm_hour == 12 && date_time->tm_min == 0)) { esp_restart(); } else { struct tm *local_time = convert_to_local(date_time); update_display(local_time->tm_hour, local_time->tm_min); } } static void display_time() { struct tm date_time; esp_err_t ret = ds3231_read_date_time(&date_time); if (ret == ESP_OK) { check_sync(&date_time); } } static uint16_t min(uint16_t a, uint16_t b) { return (a < b) ? a : b; } static uint16_t max(uint16_t a, uint16_t b) { return (a > b) ? a : b; } static uint64_t determine_sleep_time(struct tm *local_time) { uint16_t wakeup_time_sec; if (!should_enable_display(local_time->tm_hour)) { if (local_time->tm_hour == 5) { wakeup_time_sec = min((60 - local_time->tm_min) * 60 + (60 - local_time->tm_sec), HALF_HOUR_IN_SECONDS); } else { wakeup_time_sec = HALF_HOUR_IN_SECONDS; } } else { wakeup_time_sec = 60 - local_time->tm_sec; } wakeup_time_sec = max(wakeup_time_sec, 1); return wakeup_time_sec * SECONDS_TO_MICROSECONDS; } static void sleep_until_next_update() { struct tm date_time; ds3231_read_date_time(&date_time); struct tm *local_time = convert_to_local(&date_time); esp_deep_sleep(determine_sleep_time(local_time)); } void app_main() { ESP_ERROR_CHECK(nvs_flash_init()); esp_reset_reason_t reset_reason = esp_reset_reason(); if (reset_reason == ESP_RST_SW || reset_reason == ESP_RST_EXT) { sync_time(); } else { ds3231_init(DS3231_SDA_PIN, DS3231_SCL_PIN); ds3231_disable_32khz_output(); display_time(); } sleep_until_next_update(); }