configs/components/ds3231/ds3231.cpp

113 lines
4.4 KiB
C++

#include "ds3231.h"
#include "esphome/core/log.h"
// Datasheet:
// - https://datasheets.maximintegrated.com/en/ds/DS3231.pdf
namespace esphome {
namespace ds3231 {
static const char *const TAG = "ds3231";
void DS3231Component::setup() {
if (!this->read_rtc_()) {
this->mark_failed();
}
}
void DS3231Component::update() { this->read_time(); }
void DS3231Component::dump_config() {
ESP_LOGCONFIG(TAG, "DS3231:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
// ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
if (!this->read_bytes(0, this->ds3231_.raw, sizeof(this->ds3231_.raw))) {
ESP_LOGE(TAG, "Can't read I2C data.");
}
}
float DS3231Component::get_setup_priority() const { return setup_priority::DATA; }
void DS3231Component::read_time() {
if (!this->read_rtc_()) {
return;
}
if (ds3231_.reg.ch) {
ESP_LOGW(TAG, "RTC halted, not syncing to system clock.");
return;
}
ESPTime rtc_time{
.second = uint8_t(ds3231_.reg.second + 10 * ds3231_.reg.second_10),
.minute = uint8_t(ds3231_.reg.minute + 10u * ds3231_.reg.minute_10),
.hour = uint8_t(ds3231_.reg.hour + 10u * ds3231_.reg.hour_10),
.day_of_week = uint8_t(ds3231_.reg.weekday),
.day_of_month = uint8_t(ds3231_.reg.day + 10u * ds3231_.reg.day_10),
.day_of_year = 1, // ignored by recalc_timestamp_utc(false)
.month = uint8_t(ds3231_.reg.month + 10u * ds3231_.reg.month_10),
.year = uint16_t(ds3231_.reg.year + 10u * ds3231_.reg.year_10 + 2000),
.is_dst = false, // not used
.timestamp = 0 // overwritten by recalc_timestamp_utc(false)
};
rtc_time.recalc_timestamp_utc(false);
if (!rtc_time.is_valid()) {
ESP_LOGE(TAG, "Invalid RTC time, not syncing to system clock.");
return;
}
time::RealTimeClock::synchronize_epoch_(rtc_time.timestamp);
}
void DS3231Component::write_time() {
auto now = time::RealTimeClock::utcnow();
if (!now.is_valid()) {
ESP_LOGE(TAG, "Invalid system time, not syncing to RTC.");
return;
}
ds3231_.reg.year = (now.year - 2000) % 10;
ds3231_.reg.year_10 = (now.year - 2000) / 10 % 10;
ds3231_.reg.month = now.month % 10;
ds3231_.reg.month_10 = now.month / 10;
ds3231_.reg.day = now.day_of_month % 10;
ds3231_.reg.day_10 = now.day_of_month / 10;
ds3231_.reg.weekday = now.day_of_week;
ds3231_.reg.hour = now.hour % 10;
ds3231_.reg.hour_10 = now.hour / 10;
ds3231_.reg.minute = now.minute % 10;
ds3231_.reg.minute_10 = now.minute / 10;
ds3231_.reg.second = now.second % 10;
ds3231_.reg.second_10 = now.second / 10;
ds3231_.reg.ch = false;
// ESPHOME emits warning about synchronous mode; must check datasheet to see if this is a problem for us. Datasheet pg. 6 says "The DS3231 is designed to operate in a synchronous mode, where the timekeeping registers are updated at the end of each second. This ensures that the timekeeping registers always contain valid data and eliminates the need for software to handle potential timing issues when reading or writing the registers."
this->write_rtc_();
}
bool DS3231Component::read_rtc_() {
if (!this->read_bytes(0, this->ds3231_.raw, sizeof(this->ds3231_.raw))) {
ESP_LOGE(TAG, "Can't read I2C data.");
return false;
}
ESP_LOGD(TAG, "Read %0u%0u:%0u%0u:%0u%0u 20%0u%0u-%0u%0u-%0u%0u CH:%s RS:%0u SQWE:%s OUT:%s", ds3231_.reg.hour_10,
ds3231_.reg.hour, ds3231_.reg.minute_10, ds3231_.reg.minute, ds3231_.reg.second_10, ds3231_.reg.second,
ds3231_.reg.year_10, ds3231_.reg.year, ds3231_.reg.month_10, ds3231_.reg.month, ds3231_.reg.day_10,
ds3231_.reg.day, ONOFF(ds3231_.reg.ch), ds3231_.reg.rs, ONOFF(ds3231_.reg.sqwe), ONOFF(ds3231_.reg.out));
return true;
}
bool DS3231Component::write_rtc_() {
if (!this->write_bytes(0, this->ds3231_.raw, sizeof(this->ds3231_.raw))) {
ESP_LOGE(TAG, "Can't write I2C data.");
return false;
}
ESP_LOGD(TAG, "Write %0u%0u:%0u%0u:%0u%0u 20%0u%0u-%0u%0u-%0u%0u CH:%s RS:%0u SQWE:%s OUT:%s", ds3231_.reg.hour_10,
ds3231_.reg.hour, ds3231_.reg.minute_10, ds3231_.reg.minute, ds3231_.reg.second_10, ds3231_.reg.second,
ds3231_.reg.year_10, ds3231_.reg.year, ds3231_.reg.month_10, ds3231_.reg.month, ds3231_.reg.day_10,
ds3231_.reg.day, ONOFF(ds3231_.reg.ch), ds3231_.reg.rs, ONOFF(ds3231_.reg.sqwe), ONOFF(ds3231_.reg.out));
return true;
}
} // namespace ds3231
} // namespace esphome