CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
Rtc.cpp
Go to the documentation of this file.
1
10
11#include "cdc_hal/IRtc.h"
12#include "cdc_log.h"
13#include "nvs_flash.h"
14#include "nvs.h"
15#include "esp_system.h"
16#include "esp_timer.h"
17#include "esp_clk_tree.h"
18#include <sys/time.h>
19#include <cstring>
20
21static const char* TAG = "RTC";
22
23namespace cdc::hal {
24
28static constexpr const char* NVS_NAMESPACE = "rtc";
29static constexpr const char* NVS_KEY_TZ = "tz_offset";
30static constexpr const char* NVS_KEY_TS = "last_ts";
31
32class Esp32Rtc : public IRtc {
33public:
34 Esp32Rtc() = default;
35
36 // IService implementation
37 bool init() override;
38 bool start() override { state_ = core::ServiceState::STARTED; return true; }
39 void stop() override { state_ = core::ServiceState::STOPPED; }
40 core::ServiceState getState() const override { return state_; }
41 const char* getName() const override { return "rtc"; }
42
43 // IRtc implementation
44 void getTime(struct tm* timeinfo) const override;
45 void getTimeStr(char* buf, size_t bufLen) const override;
46 void getDateStr(char* buf, size_t bufLen) const override;
47 void setTime(int hour, int minute, int second) override;
48 void setDate(int year, int month, int day) override;
49 void setTimestamp(time_t timestamp) override;
50 time_t getTimestamp() const override;
51 bool isTimeSet() const override { return timeIsSet_; }
52 void markTimeSet() override { timeIsSet_ = true; }
53 void setTimezoneOffset(int8_t hours) override;
54 int8_t getTimezoneOffset() const override { return tzOffset_; }
55
56private:
57 void loadTimezoneFromNvs();
58 void applyTimezone();
59
61 bool timeIsSet_ = false;
62 int8_t tzOffset_ = 0; // Hours from UTC
63};
64
71 return state_ == core::ServiceState::INITIALIZED ||
73 }
74
75 LOG_I(TAG, "Initializing ESP32-S3 internal RTC");
76
77 esp_reset_reason_t reset_reason = esp_reset_reason();
78 int64_t rtc_us = esp_timer_get_time();
79 uint32_t rtc_clk_hz = 0;
80 esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_RTC_SLOW,
81 ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED,
82 &rtc_clk_hz);
83 LOG_W(TAG, "boot diag: reset=%d esp_timer=%lld us RTC_SLOW=%lu Hz",
84 static_cast<int>(reset_reason), (long long)rtc_us,
85 (unsigned long)rtc_clk_hz);
86
87 loadTimezoneFromNvs();
88 applyTimezone();
89
90 time_t now;
91 time(&now);
92 struct tm timeinfo;
93 localtime_r(&now, &timeinfo);
94
95 LOG_W(TAG, "boot RTC: %04d-%02d-%02d %02d:%02d (ts=%ld)",
96 timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
97 timeinfo.tm_hour, timeinfo.tm_min, (long)now);
98
99 if (timeinfo.tm_year >= (2024 - 1900)) {
100 timeIsSet_ = true;
101 LOG_I(TAG, "RTC time valid");
102 } else {
103 // Time not valid - user must sync via NTP or SET_TIME
104 // We do NOT restore from NVS - better no time than stale time
105 timeIsSet_ = false;
106 LOG_I(TAG, "RTC time not set - use NTP or SET_TIME command");
107 }
108
110 return true;
111}
112
118void Esp32Rtc::getTime(struct tm* timeinfo) const {
119 if (!timeinfo) return;
120 time_t now;
121 time(&now);
122 localtime_r(&now, timeinfo);
123}
124
131void Esp32Rtc::getTimeStr(char* buf, size_t bufLen) const {
132 if (!buf || bufLen < 6) return;
133 struct tm timeinfo;
134 getTime(&timeinfo);
135 strftime(buf, bufLen, "%H:%M", &timeinfo);
136}
137
144void Esp32Rtc::getDateStr(char* buf, size_t bufLen) const {
145 if (!buf || bufLen < 11) return;
146 struct tm timeinfo;
147 getTime(&timeinfo);
148 strftime(buf, bufLen, "%Y-%m-%d", &timeinfo);
149}
150
158void Esp32Rtc::setTime(int hour, int minute, int second) {
159 struct tm timeinfo;
160 getTime(&timeinfo);
161
162 // If year is invalid, set a default date
163 if (timeinfo.tm_year < (2024 - 1900)) {
164 LOG_W(TAG, "Year invalid (%d), setting default date 2025-01-01",
165 timeinfo.tm_year + 1900);
166 timeinfo.tm_year = 2025 - 1900;
167 timeinfo.tm_mon = 0; // January
168 timeinfo.tm_mday = 1;
169 }
170
171 timeinfo.tm_hour = hour;
172 timeinfo.tm_min = minute;
173 timeinfo.tm_sec = second;
174
175 time_t t = mktime(&timeinfo);
176 struct timeval tv = { .tv_sec = t, .tv_usec = 0 };
177 settimeofday(&tv, nullptr);
178
179 timeIsSet_ = true;
180
181 LOG_I(TAG, "Time set to %02d:%02d:%02d (date: %04d-%02d-%02d)",
182 hour, minute, second,
183 timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
184}
185
193void Esp32Rtc::setDate(int year, int month, int day) {
194 struct tm timeinfo;
195 getTime(&timeinfo);
196
197 timeinfo.tm_year = year - 1900;
198 timeinfo.tm_mon = month - 1;
199 timeinfo.tm_mday = day;
200
201 time_t t = mktime(&timeinfo);
202 struct timeval tv = { .tv_sec = t, .tv_usec = 0 };
203 settimeofday(&tv, nullptr);
204
205 timeIsSet_ = true;
206
207 LOG_I(TAG, "Date set to %04d-%02d-%02d", year, month, day);
208}
209
215void Esp32Rtc::setTimestamp(time_t timestamp) {
216 struct timeval tv = { .tv_sec = timestamp, .tv_usec = 0 };
217 settimeofday(&tv, nullptr);
218
219 timeIsSet_ = true;
220
221 struct tm timeinfo;
222 localtime_r(&timestamp, &timeinfo);
223 LOG_I(TAG, "Timestamp set: %04d-%02d-%02d %02d:%02d:%02d",
224 timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
225 timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
226}
227
233 time_t now;
234 time(&now);
235 return now;
236}
237
243void Esp32Rtc::setTimezoneOffset(int8_t hours) {
244 if (hours < -12 || hours > 14) {
245 LOG_W(TAG, "Invalid timezone offset: %d", hours);
246 return;
247 }
248
249 tzOffset_ = hours;
250 applyTimezone();
251
252 // Save to NVS
253 nvs_handle_t nvs;
254 if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs) == ESP_OK) {
255 nvs_set_i8(nvs, NVS_KEY_TZ, tzOffset_);
256 nvs_commit(nvs);
257 nvs_close(nvs);
258 }
259
260 LOG_I(TAG, "Timezone set to UTC%+d", hours);
261}
262
267void Esp32Rtc::loadTimezoneFromNvs() {
268 nvs_handle_t nvs;
269 if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs) == ESP_OK) {
270 int8_t tz = 0;
271 if (nvs_get_i8(nvs, NVS_KEY_TZ, &tz) == ESP_OK) {
272 tzOffset_ = tz;
273 }
274 nvs_close(nvs);
275 }
276}
277
282void Esp32Rtc::applyTimezone() {
283 // Set TZ environment variable
284 // Format: "UTC<offset>" where offset is negated (UTC-1 means TZ=UTC1)
285 char tz[16];
286 if (tzOffset_ == 0) {
287 snprintf(tz, sizeof(tz), "UTC0");
288 } else {
289 snprintf(tz, sizeof(tz), "UTC%+d", -tzOffset_);
290 }
291 setenv("TZ", tz, 1);
292 tzset();
293}
294
299
305 return &g_rtc;
306}
307
308} // namespace cdc::hal
static const char * TAG
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
time_t getTimestamp() const override
Returns current UNIX timestamp.
Definition Rtc.cpp:232
bool isTimeSet() const override
Definition Rtc.cpp:51
const char * getName() const override
Definition Rtc.cpp:41
void getDateStr(char *buf, size_t bufLen) const override
Formats current date as YYYY-MM-DD.
Definition Rtc.cpp:144
void stop() override
Definition Rtc.cpp:39
int8_t getTimezoneOffset() const override
Definition Rtc.cpp:54
bool start() override
Definition Rtc.cpp:38
void setTime(int hour, int minute, int second) override
Sets RTC time while preserving current date.
Definition Rtc.cpp:158
bool init() override
Initializes RTC service and timezone context.
Definition Rtc.cpp:69
void setTimezoneOffset(int8_t hours) override
Sets timezone offset and persists it.
Definition Rtc.cpp:243
void setTimestamp(time_t timestamp) override
Sets RTC from UNIX timestamp.
Definition Rtc.cpp:215
void getTimeStr(char *buf, size_t bufLen) const override
Formats current time as HH:MM.
Definition Rtc.cpp:131
void getTime(struct tm *timeinfo) const override
Reads current local time into tm.
Definition Rtc.cpp:118
void setDate(int year, int month, int day) override
Sets RTC date while preserving current time.
Definition Rtc.cpp:193
void markTimeSet() override
Definition Rtc.cpp:52
core::ServiceState getState() const override
Definition Rtc.cpp:40
static constexpr const char * NVS_KEY_TS
Definition Rtc.cpp:30
static Esp32Rtc g_rtc
Singleton RTC implementation instance.
Definition Rtc.cpp:298
static constexpr const char * NVS_NAMESPACE
NVS namespace and keys for display settings.
IRtc * getRtcInstance()
Returns the singleton RTC service instance.
Definition Rtc.cpp:304
static constexpr const char * NVS_KEY_TZ
Definition Rtc.cpp:29