time_helper.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /* time_helper.c
  2. *
  3. * Copyright (C) 2006-2024 wolfSSL Inc.
  4. *
  5. * This file is part of wolfSSL.
  6. *
  7. * wolfSSL is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * wolfSSL is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
  20. */
  21. /* common Espressif time_helper v5.6.3.002 */
  22. #include "sdkconfig.h"
  23. #include "time_helper.h"
  24. #include <esp_log.h>
  25. #include <esp_idf_version.h>
  26. #if defined(ESP_IDF_VERSION_MAJOR) && defined(ESP_IDF_VERSION_MINOR)
  27. #if (ESP_IDF_VERSION_MAJOR == 5) && (ESP_IDF_VERSION_MINOR >= 1)
  28. #define HAS_ESP_NETIF_SNTP 1
  29. #include <lwip/apps/sntp.h>
  30. #include <esp_netif_sntp.h>
  31. #else
  32. #include <string.h>
  33. #include <esp_sntp.h>
  34. #endif
  35. #else
  36. /* TODO Consider pre IDF v5? */
  37. #endif
  38. /* ESP-IDF uses a 64-bit signed integer to represent time_t starting from release v5.0
  39. * See: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/system_time.html#year-2036-and-2038-overflow-issues
  40. */
  41. const static char* TAG = "time_helper";
  42. /* see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html */
  43. #ifndef TIME_ZONE
  44. /*
  45. * PST represents Pacific Standard Time.
  46. * +8 specifies the offset from UTC (Coordinated Universal Time), indicating
  47. * that Pacific Time is UTC-8 during standard time.
  48. * PDT represents Pacific Daylight Time.
  49. * M3.2.0 indicates that Daylight Saving Time (DST) starts on the
  50. * second (2) Sunday (0) of March (3).
  51. * M11.1.0 indicates that DST ends on the first (1) Sunday (0) of November (11)
  52. */
  53. #define TIME_ZONE "PST+8PDT,M3.2.0,M11.1.0"
  54. #endif /* not defined: TIME_ZONE, so we are setting our own */
  55. #define NTP_RETRY_COUNT 10
  56. /* NELEMS(x) number of elements
  57. * To determine the number of elements in the array, we can divide the total
  58. * size of the array by the size of the array element.
  59. * See https://stackoverflow.com/questions/37538/how-do-i-determine-the-size-of-my-array-in-c
  60. **/
  61. #define NELEMS(x) ( (int)(sizeof(x) / sizeof((x)[0])) )
  62. /* See also CONFIG_LWIP_SNTP_MAX_SERVERS in sdkconfig */
  63. #define NTP_SERVER_LIST ( (char*[]) { \
  64. "pool.ntp.org", \
  65. "time.nist.gov", \
  66. "utcnist.colorado.edu" \
  67. } \
  68. )
  69. /* #define NTP_SERVER_COUNT using NELEMS:
  70. *
  71. * (int)(sizeof(NTP_SERVER_LIST) / sizeof(NTP_SERVER_LIST[0]))
  72. */
  73. #define NTP_SERVER_COUNT NELEMS(NTP_SERVER_LIST)
  74. #ifndef CONFIG_LWIP_SNTP_MAX_SERVERS
  75. /* We should find max value in sdkconfig, if not set it to our count:*/
  76. #define CONFIG_LWIP_SNTP_MAX_SERVERS NTP_SERVER_COUNT
  77. #endif
  78. char* ntpServerList[NTP_SERVER_COUNT] = NTP_SERVER_LIST;
  79. /* our NTP server list is global info */
  80. extern char* ntpServerList[NTP_SERVER_COUNT];
  81. /* Show the current date and time */
  82. int esp_show_current_datetime()
  83. {
  84. time_t now;
  85. char strftime_buf[64];
  86. struct tm timeinfo;
  87. time(&now);
  88. setenv("TZ", TIME_ZONE, 1);
  89. tzset();
  90. localtime_r(&now, &timeinfo);
  91. strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
  92. ESP_LOGI(TAG, "The current date/time is: %s", strftime_buf);
  93. return 0;
  94. }
  95. /* the worst-case scenario is a hard-coded date/time */
  96. int set_fixed_default_time(void)
  97. {
  98. /* ideally, we'd like to set time from network,
  99. * but let's set a default time, just in case */
  100. struct tm timeinfo = {
  101. .tm_year = 2023 - 1900,
  102. .tm_mon = 10,
  103. .tm_mday = 02,
  104. .tm_hour = 13,
  105. .tm_min = 01,
  106. .tm_sec = 05
  107. };
  108. struct timeval now;
  109. time_t interim_time;
  110. int ret = -1;
  111. /* set interim static time */
  112. interim_time = mktime(&timeinfo);
  113. ESP_LOGI(TAG, "Adjusting time from fixed value");
  114. now = (struct timeval){ .tv_sec = interim_time };
  115. ret = settimeofday(&now, NULL);
  116. return ret;
  117. }
  118. /* set_time_from_string(s)
  119. *
  120. * returns 0 = success if able to set the time from the provided string
  121. * error for any other value, typically -1 */
  122. int set_time_from_string(char* time_buffer)
  123. {
  124. /* expecting github default formatting: 'Thu Aug 31 12:41:45 2023 -0700' */
  125. const char *format = "%3s %3s %d %d:%d:%d %d %s";
  126. struct tm this_timeinfo;
  127. struct timeval now;
  128. time_t interim_time;
  129. char offset[6]; /* expecting trailing single quote, not used */
  130. char day_str[4];
  131. char month_str[4];
  132. int day, year, hour, minute, second;
  133. int quote_offset = 0;
  134. int ret = 0;
  135. /* we are expecting the string to be encapsulated in single quotes */
  136. if (*time_buffer == 0x27) {
  137. quote_offset = 1;
  138. }
  139. ret = sscanf(time_buffer + quote_offset,
  140. format,
  141. day_str, month_str,
  142. &day, &hour, &minute, &second, &year, &offset);
  143. if (ret == 8) {
  144. /* we found a match for all componets */
  145. const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  146. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  147. for (int i = 0; i < 12; i++) {
  148. if (strcmp(month_str, months[i]) == 0) {
  149. this_timeinfo.tm_mon = i;
  150. break;
  151. }
  152. }
  153. this_timeinfo.tm_mday = day;
  154. this_timeinfo.tm_hour = hour;
  155. this_timeinfo.tm_min = minute;
  156. this_timeinfo.tm_sec = second;
  157. this_timeinfo.tm_year = year - 1900; /* Number of years since 1900 */
  158. interim_time = mktime(&this_timeinfo);
  159. now = (struct timeval){ .tv_sec = interim_time };
  160. ret = settimeofday(&now, NULL);
  161. ESP_LOGI(TAG, "Time updated to %s", time_buffer);
  162. }
  163. else {
  164. ESP_LOGE(TAG, "Failed to convert \"%s\" to a tm date.", time_buffer);
  165. ESP_LOGI(TAG, "Trying fixed date that was hard-coded.");
  166. set_fixed_default_time();
  167. ret = -1;
  168. }
  169. return ret;
  170. }
  171. /* set time; returns 0 if succecssfully configured with NTP */
  172. int set_time(void)
  173. {
  174. #ifndef NTP_SERVER_COUNT
  175. ESP_LOGW(TAG, "Warning: no sntp server names defined. "
  176. "Setting to empty list");
  177. #define NTP_SERVER_COUNT 0
  178. #warning "NTP not properly configured"
  179. #endif /* not defined: NTP_SERVER_COUNT */
  180. #ifdef HAS_ESP_NETIF_SNTP
  181. #if CONFIG_LWIP_SNTP_MAX_SERVERS > 1
  182. esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(
  183. NTP_SERVER_COUNT,
  184. ESP_SNTP_SERVER_LIST(ntpServerList[0])
  185. );
  186. #else
  187. esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(ntpServerList[0]);
  188. #endif /* CONFIG_LWIP_SNTP_MAX_SERVERS > 1 */
  189. #endif /* HAS_ESP_NETIF_SNTP */
  190. int ret = 0;
  191. int i = 0; /* counter for time servers */
  192. ESP_LOGI(TAG, "Setting the time. Startup time:");
  193. esp_show_current_datetime();
  194. #ifdef LIBWOLFSSL_VERSION_GIT_HASH_DATE
  195. /* initialy set a default approximate time from recent git commit */
  196. ESP_LOGI(TAG, "Found git hash date, attempting to set system date.");
  197. set_time_from_string(LIBWOLFSSL_VERSION_GIT_HASH_DATE);
  198. esp_show_current_datetime();
  199. ret = -4;
  200. #else
  201. /* otherwise set a fixed time that was hard coded */
  202. set_fixed_default_time();
  203. ret = -3;
  204. #endif
  205. #ifdef CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH
  206. config.smooth_sync = true;
  207. #endif
  208. if (NTP_SERVER_COUNT) {
  209. /* next, let's setup NTP time servers
  210. *
  211. * see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/system_time.html#sntp-time-synchronization
  212. *
  213. * WARNING: do not set operating mode while SNTP client is running!
  214. */
  215. /* TODO Consider esp_sntp_setoperatingmode(SNTP_OPMODE_POLL); */
  216. sntp_setoperatingmode(SNTP_OPMODE_POLL);
  217. if (NTP_SERVER_COUNT > CONFIG_LWIP_SNTP_MAX_SERVERS) {
  218. ESP_LOGW(TAG, "WARNING: %d NTP Servers defined, but "
  219. "CONFIG_LWIP_SNTP_MAX_SERVERS = %d",
  220. NTP_SERVER_COUNT,CONFIG_LWIP_SNTP_MAX_SERVERS);
  221. }
  222. ESP_LOGI(TAG, "sntp_setservername:");
  223. for (i = 0; i < CONFIG_LWIP_SNTP_MAX_SERVERS; i++) {
  224. const char* thisServer = ntpServerList[i];
  225. if (strncmp(thisServer, "\x00", 1) == 0) {
  226. /* just in case we run out of NTP servers */
  227. break;
  228. }
  229. ESP_LOGI(TAG, "%s", thisServer);
  230. sntp_setservername(i, thisServer);
  231. }
  232. #ifdef HAS_ESP_NETIF_SNTP
  233. ret = esp_netif_sntp_init(&config);
  234. #else
  235. ESP_LOGW(TAG,"Warning: Consider upgrading ESP-IDF to take advantage "
  236. "of updated SNTP libraries");
  237. #endif
  238. if (ret == ESP_OK) {
  239. ESP_LOGV(TAG, "Successfully called esp_netif_sntp_init");
  240. }
  241. else {
  242. ESP_LOGE(TAG, "ERROR: esp_netif_sntp_init return = %d", ret);
  243. }
  244. sntp_init();
  245. switch (ret) {
  246. case ESP_ERR_INVALID_STATE:
  247. break;
  248. default:
  249. break;
  250. }
  251. ESP_LOGI(TAG, "sntp_init done.");
  252. }
  253. else {
  254. ESP_LOGW(TAG, "No sntp time servers found.");
  255. ret = -1;
  256. }
  257. return ret;
  258. }
  259. /* wait for NTP to actually set the time */
  260. int set_time_wait_for_ntp(void)
  261. {
  262. int ret = 0;
  263. #ifdef HAS_ESP_NETIF_SNTP
  264. int ntp_retry = 0;
  265. const int ntp_retry_count = NTP_RETRY_COUNT;
  266. ret = esp_netif_sntp_start();
  267. ret = esp_netif_sntp_sync_wait(500 / portTICK_PERIOD_MS);
  268. #endif /* HAS_ESP_NETIF_SNTP */
  269. esp_show_current_datetime();
  270. #ifdef HAS_ESP_NETIF_SNTP
  271. while (ret == ESP_ERR_TIMEOUT && (ntp_retry++ < ntp_retry_count)) {
  272. ret = esp_netif_sntp_sync_wait(1000 / portTICK_PERIOD_MS);
  273. ESP_LOGI(TAG, "Waiting for NTP to sync time... (%d/%d)",
  274. ntp_retry,
  275. ntp_retry_count);
  276. esp_show_current_datetime();
  277. }
  278. #endif /* HAS_ESP_NETIF_SNTP */
  279. #ifdef TIME_ZONE
  280. setenv("TZ", TIME_ZONE, 1);
  281. tzset();
  282. #endif
  283. if (ret == ESP_OK) {
  284. ESP_LOGI(TAG, "Successfuly set time via NTP servers.");
  285. }
  286. else {
  287. ESP_LOGW(TAG, "Warning: Failed to set time with NTP: "
  288. "result = 0x%0x: %s",
  289. ret, esp_err_to_name(ret));
  290. }
  291. return ret;
  292. }