openshell-esphome-components/components/pcf85063/pcf85063.cpp

201 lines
6.7 KiB
C++

#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_();
}
#define PCF85063_READ_REG(reg, len) \
if (!this->read_bytes(reg, &this->pcf85063_.raw[reg], len)) { \
ESP_LOGE(TAG, "Can't read I2C data."); \
return false; \
}
#define PCF85063_WRITE_REG(reg, len) \
if (!this->write_bytes(reg, &this->pcf85063_.raw[reg], len)) { \
ESP_LOGE(TAG, "Can't write I2C data."); \
return false; \
}
bool PCF85063Component::clear_timer_flag() {
PCF85063_READ_REG(0x01, 1);
this->pcf85063_.reg.timer_flag = 0;
PCF85063_WRITE_REG(0x01, 1);
return true;
}
bool PCF85063Component::stop_timer() {
PCF85063_READ_REG(0x11, 1);
this->pcf85063_.reg.timer_enable = false;
PCF85063_WRITE_REG(0x11, 1);
return true;
}
bool PCF85063Component::clear_alarm_flag() {
PCF85063_READ_REG(0x01, 1);
this->pcf85063_.reg.alarm_flag = 0;
PCF85063_WRITE_REG(0x01, 1);
return true;
}
bool PCF85063Component::write_clockout_frequency(uint8_t freq) {
PCF85063_READ_REG(0x01, 1);
this->pcf85063_.reg.clkout_control = freq & 0b111;
PCF85063_WRITE_REG(0x01, 1);
return true;
}
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_() {
PCF85063_WRITE_REG(0x10, 2);
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