Files
2025-10-06 18:27:50 +02:00

169 lines
4.8 KiB
C++

#include "hardware_rtc.hpp"
#include <logger.hpp>
HardwareRTC::HardwareRTC(TwoWire& wire): wire(wire), initialized(false) {}
HardwareRTC::~HardwareRTC() {}
void HardwareRTC::begin() {
if(wire.getClock() == 0) {
wire.begin();
wire.setClock(100000); // Set to 100kHz
}
initialized = true;
LOG_DEBUG("RTC initialized");
setSystemTime(0);
}
bool HardwareRTC::setTime(time_t unixTimestamp) {
if (!initialized) {
LOG_WARN("RTC not initialized");
return false;
}
// Use time_t, which on ESP32 is 64-bit and safe for this conversion
time_t t = unixTimestamp;
struct tm timeStruct;
gmtime_r(&t, &timeStruct);
// Check if the year is within the DS1307's valid range (2000-2099)
if (timeStruct.tm_year < 100 || timeStruct.tm_year > 199) {
// Year is out of the 2000-2099 range that the DS1307 can store.
LOG_ERROR("RTC setTime: Year %d is out of range (2000-2099)", timeStruct.tm_year + 1900);
return false;
}
uint8_t buffer[7];
buffer[0] = decToBcd(timeStruct.tm_sec);
buffer[1] = decToBcd(timeStruct.tm_min);
buffer[2] = decToBcd(timeStruct.tm_hour);
buffer[3] = decToBcd(timeStruct.tm_wday + 1);
buffer[4] = decToBcd(timeStruct.tm_mday);
buffer[5] = decToBcd(timeStruct.tm_mon + 1);
// The DS1307 only stores years 00-99. We handle this by taking the year since 2000.
buffer[6] = decToBcd(timeStruct.tm_year - 100);
writeRegisters(buffer, 7);
// set system time to match RTC
setSystemTime(0);
LOG_DEBUG("RTC setTime: %s", toDateString(unixTimestamp).c_str());
return true;
}
time_t HardwareRTC::getTime() {
if (!initialized) return 0;
uint8_t buffer[7];
readRegisters(buffer, 7);
struct tm timeStruct = {0}; // Important: Initialize the struct to zero
timeStruct.tm_sec = bcdToDec(buffer[0] & 0x7F);
timeStruct.tm_min = bcdToDec(buffer[1]);
timeStruct.tm_hour = bcdToDec(buffer[2] & 0x3F);
timeStruct.tm_wday = bcdToDec(buffer[3]) - 1;
timeStruct.tm_mday = bcdToDec(buffer[4]);
timeStruct.tm_mon = bcdToDec(buffer[5]) - 1;
// Assume all 2-digit years from the RTC are in the 21st century (2000-2099)
timeStruct.tm_year = bcdToDec(buffer[6]) + 100; // Years since 1900
// mktime converts a local time struct to a time_t.
// Since the RTC stores time without timezone info, we treat it as UTC.
// timegm is the correct function for this, but mktime is often used
// on embedded systems with the timezone set to UTC.
time_t t = mktime(&timeStruct);
LOG_DEBUG("RTC getTime: %s", toDateString(t).c_str());
// Cast the 64-bit time_t to uint64_t for the return type
return static_cast<time_t>(t);
}
void HardwareRTC::setSystemTime(int timezoneOffsetHours) {
if (!initialized) {
LOG_WARN("RTC not initialized");
return;
}
uint64_t unixTime = getTime();
if (unixTime == 0) return;
unixTime += timezoneOffsetHours * 3600;
LOG_DEBUG("RTC setSystemTime: %s", toDateString(unixTime).c_str());
struct timeval tv;
// The tv_sec field is of type time_t, which is 64-bit on ESP32
tv.tv_sec = unixTime;
tv.tv_usec = 0;
settimeofday(&tv, NULL);
}
bool HardwareRTC::isRunning() {
if (!initialized) return false;
uint8_t buffer[1];
readRegisters(buffer, 1);
return !(buffer[0] & 0x80); // CH bit is 0 when running
}
void HardwareRTC::setTimezone(const char* timezone) {
setenv("TZ", timezone, 1);
tzset();
}
void HardwareRTC::update()
{
static uint32_t nextSync = 0;
time_t now = millis();
if (now >= nextSync) {
if(!isRunning()) {
LOG_WARN("RTC is not running, skipping update");
return;
}
setSystemTime(0);
nextSync = now + (15UL * 60UL * 1000UL); // every 15 minutes
LOG_DEBUG("RTC update: System time synchronized with RTC");
}
}
String HardwareRTC::toDateString(time_t timestamp) {
struct tm* timeinfo = gmtime(&timestamp);
char buffer[40];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);
return String(buffer);
}
uint8_t HardwareRTC::bcdToDec(uint8_t bcd) {
return ((bcd >> 4) * 10) + (bcd & 0x0F);
}
uint8_t HardwareRTC::decToBcd(uint8_t dec) {
return ((dec / 10) << 4) | (dec % 10);
}
void HardwareRTC::readRegisters(uint8_t* buffer, uint8_t length) {
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(0x00); // start at register 0
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, length);
for (uint8_t i = 0; i < length; i++) {
buffer[i] = Wire.read();
}
}
void HardwareRTC::writeRegisters(uint8_t* buffer, uint8_t length) {
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(0x00); // start at register 0
for (uint8_t i = 0; i < length; i++) {
Wire.write(buffer[i]);
}
Wire.endTransmission();
}