clock_main.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*
  2. * SPDX-FileCopyrightText: 2022 Helmut Pozimski <helmut@pozimski.eu>
  3. *
  4. * SPDX-License-Identifier: GPL-2.0-only
  5. */
  6. #include <time.h>
  7. #include <stdlib.h>
  8. #include <nvs_flash.h>
  9. #include <esp_sleep.h>
  10. #include <esp_system.h>
  11. #include "ds3231.h"
  12. #include "tm1637.h"
  13. #include "wifi.h"
  14. #include "configuration.h"
  15. #include <lwip/apps/sntp.h>
  16. #include <freertos/FreeRTOS.h>
  17. #include <freertos/event_groups.h>
  18. #define TIME_SYNCED_BIT BIT0
  19. #define SECONDS_TO_MICROSECONDS 1000000
  20. #define THREE_HOURS_IN_SECONDS 3 * 60 * 60
  21. #define HALF_HOUR_IN_SECONDS 30 * 60
  22. static EventGroupHandle_t time_sync_event_group;
  23. static uint8_t should_enable_display(uint8_t hour) {
  24. return hour < 23 && hour >= 6;
  25. }
  26. static void update_display(uint8_t hour, uint8_t minute) {
  27. tm1637_config display_config;
  28. uint8_t enable_display = should_enable_display(hour);
  29. tm1637_init(&display_config, TM1637_CLK_PIN, TM1637_DIO_PIN, true, 1);
  30. if (hour < 10) {
  31. tm1637_set_segment(&display_config, 10, 0, enable_display);
  32. tm1637_set_segment(&display_config, hour, 1, enable_display);
  33. } else {
  34. tm1637_set_segment(&display_config, hour / 10, 0, enable_display);
  35. tm1637_set_segment(&display_config, hour % 10, 1, enable_display);
  36. }
  37. if (minute < 10) {
  38. tm1637_set_segment(&display_config, 0, 2, enable_display);
  39. tm1637_set_segment(&display_config, minute, 3, enable_display);
  40. } else {
  41. tm1637_set_segment(&display_config, minute / 10, 2, enable_display);
  42. tm1637_set_segment(&display_config, minute % 10, 3, enable_display);
  43. }
  44. }
  45. static struct tm *convert_to_local(struct tm *timeinfo) {
  46. setenv("TZ", "GMT+0GMT+0", 1);
  47. tzset();
  48. time_t epoch_time = mktime(timeinfo);
  49. setenv("TZ", LOCAL_TIMEZONE, 1);
  50. tzset();
  51. return localtime(&epoch_time);
  52. }
  53. static void time_sync_callback(struct timeval *tv) {
  54. sntp_stop();
  55. wifi_stop();
  56. struct tm date_time;
  57. localtime_r(&tv->tv_sec, &date_time);
  58. ds3231_init(DS3231_SDA_PIN, DS3231_SCL_PIN);
  59. ds3231_write_date_time(date_time);
  60. struct tm *local_time = convert_to_local(&date_time);
  61. update_display(local_time->tm_hour, local_time->tm_min);
  62. xEventGroupSetBits(time_sync_event_group, TIME_SYNCED_BIT);
  63. }
  64. static void sync_time() {
  65. wifi_init();
  66. if (wifi_start()) {
  67. sntp_setoperatingmode(SNTP_OPMODE_POLL);
  68. sntp_setservername(0, "pool.ntp.org");
  69. sntp_init();
  70. time_sync_event_group = xEventGroupCreate();
  71. sntp_set_time_sync_notification_cb(&time_sync_callback);
  72. xEventGroupWaitBits(time_sync_event_group, TIME_SYNCED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
  73. vEventGroupDelete(time_sync_event_group);
  74. }
  75. }
  76. static void check_sync(struct tm *date_time) {
  77. if (date_time->tm_year == 0 || (date_time->tm_hour == 12 && date_time->tm_min == 0)) {
  78. esp_restart();
  79. } else {
  80. struct tm *local_time = convert_to_local(date_time);
  81. update_display(local_time->tm_hour, local_time->tm_min);
  82. }
  83. }
  84. static void display_time() {
  85. struct tm date_time;
  86. esp_err_t ret = ds3231_read_date_time(&date_time);
  87. if (ret == ESP_OK) {
  88. check_sync(&date_time);
  89. }
  90. }
  91. static uint16_t min(uint16_t a, uint16_t b) {
  92. return (a < b) ? a : b;
  93. }
  94. static uint16_t max(uint16_t a, uint16_t b) {
  95. return (a > b) ? a : b;
  96. }
  97. static uint64_t determine_sleep_time(struct tm *local_time) {
  98. uint16_t wakeup_time_sec;
  99. if (!should_enable_display(local_time->tm_hour)) {
  100. if (local_time->tm_hour == 5) {
  101. wakeup_time_sec = min((60 - local_time->tm_min) * 60
  102. + (60 - local_time->tm_sec), HALF_HOUR_IN_SECONDS);
  103. } else {
  104. wakeup_time_sec = HALF_HOUR_IN_SECONDS;
  105. }
  106. } else {
  107. wakeup_time_sec = 60 - local_time->tm_sec;
  108. }
  109. wakeup_time_sec = max(wakeup_time_sec, 1);
  110. return wakeup_time_sec * SECONDS_TO_MICROSECONDS;
  111. }
  112. static void sleep_until_next_update() {
  113. struct tm date_time;
  114. ds3231_read_date_time(&date_time);
  115. struct tm *local_time = convert_to_local(&date_time);
  116. esp_deep_sleep(determine_sleep_time(local_time));
  117. }
  118. void app_main() {
  119. ESP_ERROR_CHECK(nvs_flash_init());
  120. esp_reset_reason_t reset_reason = esp_reset_reason();
  121. if (reset_reason == ESP_RST_SW || reset_reason == ESP_RST_EXT) {
  122. sync_time();
  123. } else {
  124. ds3231_init(DS3231_SDA_PIN, DS3231_SCL_PIN);
  125. ds3231_disable_32khz_output();
  126. display_time();
  127. }
  128. sleep_until_next_update();
  129. }