#include "rtc_ds3231.h" #include "config.h" #include #include #include static constexpr uint8_t DS3231_ADDR = 0x68; static uint8_t bcd_to_dec(uint8_t val) { return static_cast((val >> 4) * 10 + (val & 0x0F)); } static uint8_t dec_to_bcd(uint8_t val) { return static_cast(((val / 10) << 4) | (val % 10)); } static time_t timegm_fallback(struct tm *tm_utc) { if (!tm_utc) { return static_cast(-1); } const char *old_tz = getenv("TZ"); // getenv() may return a pointer into mutable storage that becomes invalid after setenv(). std::string old_tz_copy = old_tz ? old_tz : ""; setenv("TZ", "UTC0", 1); tzset(); time_t t = mktime(tm_utc); if (!old_tz_copy.empty()) { setenv("TZ", old_tz_copy.c_str(), 1); } else { unsetenv("TZ"); } tzset(); return t; } static bool read_registers(uint8_t start_reg, uint8_t *out, size_t len) { if (!out || len == 0) { return false; } Wire.beginTransmission(DS3231_ADDR); Wire.write(start_reg); if (Wire.endTransmission(false) != 0) { return false; } size_t read = Wire.requestFrom(DS3231_ADDR, static_cast(len)); if (read != len) { return false; } for (size_t i = 0; i < len; ++i) { out[i] = Wire.read(); } return true; } static bool write_registers(uint8_t start_reg, const uint8_t *data, size_t len) { if (!data || len == 0) { return false; } Wire.beginTransmission(DS3231_ADDR); Wire.write(start_reg); for (size_t i = 0; i < len; ++i) { Wire.write(data[i]); } return Wire.endTransmission() == 0; } bool rtc_ds3231_init() { Wire.begin(PIN_OLED_SDA, PIN_OLED_SCL); Wire.setClock(100000); return rtc_ds3231_is_present(); } bool rtc_ds3231_is_present() { Wire.beginTransmission(DS3231_ADDR); return Wire.endTransmission() == 0; } bool rtc_ds3231_read_epoch(uint32_t &epoch_utc) { uint8_t regs[7] = {}; if (!read_registers(0x00, regs, sizeof(regs))) { return false; } uint8_t sec = bcd_to_dec(regs[0] & 0x7F); uint8_t min = bcd_to_dec(regs[1] & 0x7F); uint8_t hour = bcd_to_dec(regs[2] & 0x3F); uint8_t day = bcd_to_dec(regs[4] & 0x3F); uint8_t month = bcd_to_dec(regs[5] & 0x1F); uint16_t year = 2000 + bcd_to_dec(regs[6]); struct tm tm_utc = {}; tm_utc.tm_sec = sec; tm_utc.tm_min = min; tm_utc.tm_hour = hour; tm_utc.tm_mday = day; tm_utc.tm_mon = month - 1; tm_utc.tm_year = year - 1900; tm_utc.tm_isdst = 0; time_t t = timegm_fallback(&tm_utc); if (t <= 0) { return false; } epoch_utc = static_cast(t); return true; } bool rtc_ds3231_set_epoch(uint32_t epoch_utc) { time_t t = static_cast(epoch_utc); struct tm tm_utc = {}; if (!gmtime_r(&t, &tm_utc)) { return false; } uint8_t regs[7] = {}; regs[0] = dec_to_bcd(static_cast(tm_utc.tm_sec)); regs[1] = dec_to_bcd(static_cast(tm_utc.tm_min)); regs[2] = dec_to_bcd(static_cast(tm_utc.tm_hour)); regs[3] = dec_to_bcd(static_cast(tm_utc.tm_wday + 1)); regs[4] = dec_to_bcd(static_cast(tm_utc.tm_mday)); regs[5] = dec_to_bcd(static_cast(tm_utc.tm_mon + 1)); regs[6] = dec_to_bcd(static_cast((tm_utc.tm_year + 1900) - 2000)); return write_registers(0x00, regs, sizeof(regs)); }