Skip to content

Commit 4ca750d

Browse files
committed
Add RTC and internet time sync
1 parent 1b46acf commit 4ca750d

1 file changed

Lines changed: 145 additions & 1 deletion

File tree

‎main/main.c‎

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <stdlib.h>
55
#include <string.h>
66
#include <sys/stat.h>
7+
#include <sys/time.h>
8+
#include <time.h>
79

810
#include "bsp/esp-bsp.h"
911
#include "esp_app_desc.h"
@@ -17,6 +19,7 @@
1719
#include "esp_netif.h"
1820
#include "esp_ota_ops.h"
1921
#include "esp_spiffs.h"
22+
#include "esp_sntp.h"
2023
#include "esp_system.h"
2124
#include "esp_wifi.h"
2225
#include "esp_crt_bundle.h"
@@ -56,6 +59,7 @@
5659
#define BATTERY_EMPTY_MV 6000
5760
#define BATTERY_FULL_MV 8230
5861
#define BATTERY_HISTORY_POINTS 60
62+
#define TIME_ZONE "CST6CDT,M3.2.0,M11.1.0"
5963

6064
typedef struct __attribute__((packed)) {
6165
char magic[4];
@@ -105,6 +109,15 @@ static const ebook_default_t ebook_defaults[] = {
105109
static lv_obj_t *content;
106110
static lv_obj_t *battery_label;
107111
static i2c_master_dev_handle_t battery_monitor;
112+
static i2c_master_dev_handle_t rtc;
113+
static lv_obj_t *time_label;
114+
static lv_obj_t *clock_time;
115+
static lv_obj_t *clock_date;
116+
static lv_obj_t *clock_status;
117+
static bool rtc_time_loaded;
118+
static bool sntp_started;
119+
static volatile bool internet_time_synced;
120+
static volatile bool rtc_sync_pending;
108121
static lv_obj_t *battery_metrics;
109122
static lv_obj_t *battery_chart;
110123
static lv_chart_series_t *battery_series;
@@ -224,9 +237,113 @@ static void show_settings(void);
224237
static void show_chat(void);
225238
static void show_browser(void);
226239
static void show_ebooks(void);
240+
static void show_clock(void);
227241
static void clear_content(void);
228242
static void browser_link_clicked(lv_event_t *event);
229243

244+
static uint8_t bcd(int value)
245+
{
246+
return (value / 10 << 4) | value % 10;
247+
}
248+
249+
static int unbcd(uint8_t value)
250+
{
251+
return (value >> 4) * 10 + (value & 0x0f);
252+
}
253+
254+
static void rtc_write_system_time(void)
255+
{
256+
if (!rtc) return;
257+
time_t now = time(NULL);
258+
struct tm utc;
259+
gmtime_r(&now, &utc);
260+
uint8_t values[] = {0x10, bcd(utc.tm_sec), bcd(utc.tm_min), bcd(utc.tm_hour),
261+
bcd(utc.tm_wday), bcd(utc.tm_mday), bcd(utc.tm_mon + 1), bcd(utc.tm_year % 100)};
262+
if (i2c_master_transmit(rtc, values, sizeof(values), 50) == ESP_OK) {
263+
uint8_t flag_reg = 0x1d;
264+
uint8_t flag;
265+
if (i2c_master_transmit_receive(rtc, &flag_reg, 1, &flag, 1, 50) == ESP_OK) {
266+
uint8_t clear_vlf[] = {0x1d, flag & ~0x02};
267+
i2c_master_transmit(rtc, clear_vlf, sizeof(clear_vlf), 50);
268+
}
269+
rtc_time_loaded = true;
270+
}
271+
}
272+
273+
static void time_synced(struct timeval *tv)
274+
{
275+
(void)tv;
276+
internet_time_synced = true;
277+
rtc_sync_pending = true;
278+
}
279+
280+
static void start_sntp(void)
281+
{
282+
if (sntp_started) return;
283+
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
284+
esp_sntp_setservername(0, "pool.ntp.org");
285+
esp_sntp_set_time_sync_notification_cb(time_synced);
286+
esp_sntp_init();
287+
sntp_started = true;
288+
}
289+
290+
static void clock_init(i2c_master_bus_handle_t bus)
291+
{
292+
i2c_device_config_t config = {
293+
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
294+
.device_address = 0x32,
295+
.scl_speed_hz = 400000,
296+
};
297+
if (i2c_master_bus_add_device(bus, &config, &rtc) != ESP_OK) rtc = NULL;
298+
299+
uint8_t reg = 0x10;
300+
uint8_t raw[14];
301+
if (rtc && i2c_master_transmit_receive(rtc, &reg, 1, raw, sizeof(raw), 50) == ESP_OK) {
302+
struct tm value = {
303+
.tm_sec = unbcd(raw[0] & 0x7f), .tm_min = unbcd(raw[1] & 0x7f),
304+
.tm_hour = unbcd(raw[2] & 0x3f), .tm_wday = unbcd(raw[3] & 0x7f),
305+
.tm_mday = unbcd(raw[4] & 0x3f), .tm_mon = unbcd(raw[5] & 0x1f) - 1,
306+
.tm_year = unbcd(raw[6]) + 100, .tm_isdst = 0,
307+
};
308+
if (!(raw[13] & 0x02) && value.tm_year >= 124 && value.tm_mon >= 0 && value.tm_mon < 12 &&
309+
value.tm_mday > 0 && value.tm_mday <= 31 && value.tm_hour < 24 && value.tm_min < 60 && value.tm_sec < 60) {
310+
setenv("TZ", "UTC0", 1);
311+
tzset();
312+
struct timeval tv = {.tv_sec = mktime(&value)};
313+
settimeofday(&tv, NULL);
314+
rtc_time_loaded = true;
315+
}
316+
}
317+
setenv("TZ", TIME_ZONE, 1);
318+
tzset();
319+
assert(unbcd(bcd(59)) == 59);
320+
}
321+
322+
static void clock_tick(lv_timer_t *timer)
323+
{
324+
(void)timer;
325+
if (rtc_sync_pending) {
326+
rtc_sync_pending = false;
327+
rtc_write_system_time();
328+
}
329+
time_t now = time(NULL);
330+
struct tm local;
331+
localtime_r(&now, &local);
332+
bool valid = local.tm_year >= 124;
333+
char text[40];
334+
if (valid) strftime(text, sizeof(text), "%I:%M %p", &local);
335+
else snprintf(text, sizeof(text), "--:--");
336+
if (time_label) lv_label_set_text(time_label, text);
337+
if (clock_time) lv_label_set_text(clock_time, text);
338+
if (clock_date) {
339+
if (valid) strftime(text, sizeof(text), "%A, %B %d, %Y", &local);
340+
else snprintf(text, sizeof(text), "Time not set");
341+
lv_label_set_text(clock_date, text);
342+
}
343+
if (clock_status) lv_label_set_text(clock_status, internet_time_synced ? "Synced from internet" :
344+
rtc_time_loaded ? "Running from hardware RTC" : "Connect to Wi-Fi to set the clock");
345+
}
346+
230347
static void battery_tick(lv_timer_t *timer)
231348
{
232349
(void)timer;
@@ -321,6 +438,7 @@ static void wifi_event(void *arg, esp_event_base_t base, int32_t id, void *data)
321438
wifi_retries = 0;
322439
wifi_connecting = false;
323440
wifi_connected = true;
441+
start_sntp();
324442
}
325443
}
326444

@@ -1308,6 +1426,9 @@ static void clear_content(void)
13081426
battery_metrics = NULL;
13091427
battery_chart = NULL;
13101428
battery_series = NULL;
1429+
clock_time = NULL;
1430+
clock_date = NULL;
1431+
clock_status = NULL;
13111432
lv_obj_clean(content);
13121433
lv_obj_set_flex_flow(content, LV_FLEX_FLOW_COLUMN);
13131434
lv_obj_set_flex_align(content, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
@@ -2165,6 +2286,23 @@ static void system_clicked(lv_event_t *event)
21652286
ota_timer = lv_timer_create(ota_tick, 250, NULL);
21662287
}
21672288

2289+
static void clock_clicked(lv_event_t *event)
2290+
{
2291+
(void)event;
2292+
show_clock();
2293+
}
2294+
2295+
static void show_clock(void)
2296+
{
2297+
clear_content();
2298+
clock_time = lv_label_create(content);
2299+
lv_obj_set_style_text_font(clock_time, &lv_font_montserrat_48, 0);
2300+
clock_date = lv_label_create(content);
2301+
lv_obj_set_style_text_font(clock_date, &lv_font_montserrat_28, 0);
2302+
clock_status = lv_label_create(content);
2303+
clock_tick(NULL);
2304+
}
2305+
21682306
static void show_launcher(void)
21692307
{
21702308
static int32_t columns[] = {
@@ -2183,6 +2321,7 @@ static void show_launcher(void)
21832321
app_icon(content, LV_SYMBOL_ENVELOPE, "AI Chat", 0xE91E63, chat_clicked, 2, 1);
21842322
app_icon(content, LV_SYMBOL_EYE_OPEN, "Browser", 0x3F51B5, browser_clicked, 0, 2);
21852323
app_icon(content, LV_SYMBOL_FILE, "Ebooks", 0x8D6E63, ebooks_clicked, 1, 2);
2324+
app_icon(content, LV_SYMBOL_LOOP, "Clock", 0x009688, clock_clicked, 2, 2);
21862325
}
21872326

21882327
static void confirm_running_ota(void)
@@ -2204,6 +2343,7 @@ void app_main(void)
22042343
vTaskDelay(pdMS_TO_TICKS(50));
22052344
bsp_set_charge_en(true);
22062345
battery_init(bsp_i2c_get_handle());
2346+
clock_init(bsp_i2c_get_handle());
22072347
vTaskDelay(pdMS_TO_TICKS(250));
22082348

22092349
wifi_ready = start_wifi();
@@ -2236,9 +2376,13 @@ void app_main(void)
22362376
lv_obj_set_style_text_font(brand, &lv_font_montserrat_28, 0);
22372377
lv_obj_align(brand, LV_ALIGN_CENTER, 0, 0);
22382378
battery_label = lv_label_create(header);
2239-
lv_obj_align(battery_label, LV_ALIGN_RIGHT_MID, -20, 0);
2379+
lv_obj_align(battery_label, LV_ALIGN_RIGHT_MID, -20, -18);
2380+
time_label = lv_label_create(header);
2381+
lv_obj_align(time_label, LV_ALIGN_RIGHT_MID, -20, 18);
22402382
battery_tick(NULL);
22412383
lv_timer_create(battery_tick, 5000, NULL);
2384+
clock_tick(NULL);
2385+
lv_timer_create(clock_tick, 1000, NULL);
22422386

22432387
content = lv_obj_create(screen);
22442388
lv_obj_set_size(content, 720, 1180);

0 commit comments

Comments
 (0)