#include "pcf85063.h" #include "esphome/core/log.h" // Datasheet: // - https://datasheets.maximintegrated.com/en/ds/DS1307.pdf namespace esphome { namespace pcf85063 { static const char *const TAG = "pcf85063"; void PCF85063Component::setup() { ESP_LOGCONFIG(TAG, "Setting up PCF85063..."); if (!this->read_rtc_()) { this->mark_failed(); } } void PCF85063Component::update() { this->read_time(); } void PCF85063Component::dump_config() { ESP_LOGCONFIG(TAG, "PCF85063:"); LOG_I2C_DEVICE(this); if (this->is_failed()) { ESP_LOGE(TAG, "Communication with PCF85063 failed!"); } ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); } float PCF85063Component::get_setup_priority() const { return setup_priority::DATA; } void PCF85063Component::read_time() { if (!this->read_rtc_()) { return; } if (pcf85063_.reg.osc_stop) { ESP_LOGW(TAG, "RTC halted, not syncing to system clock."); return; } ESPTime rtc_time{ .second = uint8_t(pcf85063_.reg.second + 10 * pcf85063_.reg.second_10), .minute = uint8_t(pcf85063_.reg.minute + 10u * pcf85063_.reg.minute_10), .hour = uint8_t(pcf85063_.reg.hour + 10u * pcf85063_.reg.hour_10), .day_of_week = uint8_t(pcf85063_.reg.weekday), .day_of_month = uint8_t(pcf85063_.reg.day + 10u * pcf85063_.reg.day_10), .day_of_year = 1, // ignored by recalc_timestamp_utc(false) .month = uint8_t(pcf85063_.reg.month + 10u * pcf85063_.reg.month_10), .year = uint16_t(pcf85063_.reg.year + 10u * pcf85063_.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 PCF85063Component::write_time() { auto now = time::RealTimeClock::utcnow(); if (!now.is_valid()) { ESP_LOGE(TAG, "Invalid system time, not syncing to RTC."); return; } pcf85063_.reg.year = (now.year - 2000) % 10; pcf85063_.reg.year_10 = (now.year - 2000) / 10 % 10; pcf85063_.reg.month = now.month % 10; pcf85063_.reg.month_10 = now.month / 10; pcf85063_.reg.day = now.day_of_month % 10; pcf85063_.reg.day_10 = now.day_of_month / 10; pcf85063_.reg.weekday = now.day_of_week; pcf85063_.reg.hour = now.hour % 10; pcf85063_.reg.hour_10 = now.hour / 10; pcf85063_.reg.minute = now.minute % 10; pcf85063_.reg.minute_10 = now.minute / 10; pcf85063_.reg.second = now.second % 10; pcf85063_.reg.second_10 = now.second / 10; pcf85063_.reg.osc_stop = false; this->write_rtc_(); } void PCF85063Component::write_nvram(uint8_t data) { pcf85063_.reg.nvram = data; this->write_bytes(0x03, &pcf85063_.raw[0x03], 1); } uint8_t PCF85063Component::read_nvram() { this->read_bytes(0x03, &pcf85063_.raw[0x03], 1); return pcf85063_.reg.nvram; } /* TIMER_CLOCK_MINUTE 60 15300 1m 4h15m TIMER_CLOCK_SECOND 1 255 1s 4m15s */ bool PCF85063Component::write_timer_interval(uint16_t interval_seconds) { if (interval_seconds < 256) { pcf85063_.reg.timer_clock_frequency = TIMER_CLOCK_SECOND; pcf85063_.reg.timer_value = interval_seconds; pcf85063_.reg.timer_enable = true; return this->write_timer_(); } else if (interval_seconds > 15300) { ESP_LOGE(TAG, "Specified interval is longer than max allowed, clamping to 4h15m."); interval_seconds = 15300; } div_t dm = div(interval_seconds, 60); if (dm.rem) { ESP_LOGI(TAG, "Interval out of seconds range, rounding down to closest whole minute."); } pcf85063_.reg.timer_clock_frequency = TIMER_CLOCK_MINUTE; pcf85063_.reg.timer_value = dm.quot; pcf85063_.reg.timer_enable = true; return this->write_timer_(); } void PCF85063Component::set_timer_interrupt_enable_(bool state) { pcf85063_.reg.timer_interrupt_enable = state; } void PCF85063Component::set_timer_interrupt_mode_(PCF85063ATimerInterruptMode_t mode) { pcf85063_.reg.timer_interrupt_mode = mode; } bool PCF85063Component::write_timer_() { if (!this->write_bytes(0x10, &this->pcf85063_.raw[0x10], 2)) { ESP_LOGE(TAG, "Can't write I2C data."); return false; } ESP_LOGD(TAG, "Write timer %s %0u CLOCK=%0u IR=%s %0u", ONOFF(pcf85063_.reg.timer_enable), pcf85063_.reg.timer_value, pcf85063_.reg.timer_clock_frequency, ONOFF(pcf85063_.reg.timer_interrupt_enable), pcf85063_.reg.timer_interrupt_mode); return true; } bool PCF85063Component::read_rtc_() { if (!this->read_bytes(0, this->pcf85063_.raw, sizeof(this->pcf85063_.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 OSC:%s CLKOUT:%0u", pcf85063_.reg.hour_10, pcf85063_.reg.hour, pcf85063_.reg.minute_10, pcf85063_.reg.minute, pcf85063_.reg.second_10, pcf85063_.reg.second, pcf85063_.reg.year_10, pcf85063_.reg.year, pcf85063_.reg.month_10, pcf85063_.reg.month, pcf85063_.reg.day_10, pcf85063_.reg.day, ONOFF(!pcf85063_.reg.osc_stop), pcf85063_.reg.clkout_control); return true; } bool PCF85063Component::write_rtc_() { if (!this->write_bytes(0, this->pcf85063_.raw, sizeof(this->pcf85063_.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 OSC:%s CLKOUT:%0u", pcf85063_.reg.hour_10, pcf85063_.reg.hour, pcf85063_.reg.minute_10, pcf85063_.reg.minute, pcf85063_.reg.second_10, pcf85063_.reg.second, pcf85063_.reg.year_10, pcf85063_.reg.year, pcf85063_.reg.month_10, pcf85063_.reg.month, pcf85063_.reg.day_10, pcf85063_.reg.day, ONOFF(!pcf85063_.reg.osc_stop), pcf85063_.reg.clkout_control); return true; } } // namespace pcf85063 } // namespace esphome