ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
rx8111.cpp
Go to the documentation of this file.
1#include "rx8111.h"
2#include "esphome/core/log.h"
3
4namespace esphome::rx8111 {
5
6ESPHOME_LOG_TAG(TAG, "rx8111");
7
8static constexpr uint8_t RX8111_REG_SEC = 0x10;
9static constexpr uint8_t RX8111_REG_FLAG = 0x1E;
10static constexpr uint8_t RX8111_REG_CONTROL = 0x1F;
11static constexpr uint8_t RX8111_FLAG_POR = 0x80;
12static constexpr uint8_t RX8111_FLAG_VLF = 0x02;
13static constexpr uint8_t RX8111_FLAG_XST = 0x01;
14static constexpr uint8_t RX8111_CTRL_STOP = 0x01;
15// FLAG bits are write-zero-to-clear; keep UF/TF/AF/EVF even if they latch during the write.
16static constexpr uint8_t RX8111_KEEP_EVENT_FLAGS = 0x3C;
17
18static constexpr uint8_t bcd_to_uint8(uint8_t bcd) { return (bcd >> 4) * 10 + (bcd & 0x0F); }
19static constexpr uint8_t uint8_to_bcd(uint8_t val) { return ((val / 10) << 4) | (val % 10); }
20
22
24
26 ESP_LOGCONFIG(TAG, "RX8111:");
27 LOG_I2C_DEVICE(this);
28 RealTimeClock::dump_config();
29}
30
32 uint8_t flags;
33 if (!this->read_byte(RX8111_REG_FLAG, &flags)) {
34 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
35 return;
36 }
37 if (flags & (RX8111_FLAG_VLF | RX8111_FLAG_XST | RX8111_FLAG_POR)) {
38 ESP_LOGW(TAG, "RTC time invalidated by power loss or oscillator stop; write trusted time");
39 this->status_set_warning(LOG_STR("RTC time invalidated; write trusted time"));
40 return;
41 }
42
43 uint8_t buf[7];
44 // I2C access freezes the RX8111 counter, yielding a coherent calendar snapshot.
45 if (!this->read_bytes(RX8111_REG_SEC, buf, sizeof(buf))) {
46 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
47 return;
48 }
49 static constexpr uint8_t CALENDAR_MASKS[7] = {0x7F, 0x7F, 0x3F, 0x00, 0x3F, 0x1F, 0xFF};
50 for (size_t i = 0; i < sizeof(buf); i++) {
51 if (i == 3)
52 continue; // Weekday uses one-hot encoding, not BCD.
53 buf[i] &= CALENDAR_MASKS[i];
54 if ((buf[i] & 0x0F) > 9 || (buf[i] >> 4) > 9) {
55 ESP_LOGW(TAG, "Invalid BCD in RTC time");
56 this->status_set_warning(LOG_STR("Invalid RTC time"));
57 return;
58 }
59 }
60 const uint8_t onehot = buf[3];
61 if (onehot == 0 || onehot > 0x40 || (onehot & (onehot - 1)) != 0) {
62 ESP_LOGW(TAG, "Invalid RTC weekday");
63 this->status_set_warning(LOG_STR("Invalid RTC time"));
64 return;
65 }
66 const uint8_t weekday = static_cast<uint8_t>(__builtin_ctz(onehot) + 1);
67 ESPTime rtc_time{
68 .second = bcd_to_uint8(buf[0]),
69 .minute = bcd_to_uint8(buf[1]),
70 .hour = bcd_to_uint8(buf[2]),
71 .day_of_week = weekday,
72 .day_of_month = bcd_to_uint8(buf[4]),
73 .month = bcd_to_uint8(buf[5]),
74 .year = static_cast<uint16_t>(bcd_to_uint8(buf[6]) + 2000),
75 .is_dst = false,
76 .timestamp = 0,
77 };
78 rtc_time.recalc_timestamp_utc(false);
79 if (!rtc_time.is_valid(true, false) || rtc_time.second >= 60) {
80 ESP_LOGW(TAG, "Invalid RTC time, not syncing to system clock");
81 this->status_set_warning(LOG_STR("Invalid RTC time"));
82 return;
83 }
84 if (ESPTime::from_epoch_utc(rtc_time.timestamp).day_of_week != weekday) {
85 ESP_LOGW(TAG, "RTC weekday does not match calendar date");
86 this->status_set_warning(LOG_STR("Invalid RTC time"));
87 return;
88 }
89
91 ESP_LOGD(TAG, "Read UTC time: %04d-%02d-%02d %02d:%02d:%02d", rtc_time.year, rtc_time.month, rtc_time.day_of_month,
92 rtc_time.hour, rtc_time.minute, rtc_time.second);
94}
95
98 if (!now.is_valid() || now.year > 2099) {
99 ESP_LOGE(TAG, "Invalid system time, not syncing to RTC");
100 return;
101 }
102 if (!this->set_stop_bit_(true)) {
103 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
104 return;
105 }
106
107 uint8_t buf[7];
108 buf[0] = uint8_to_bcd(now.second);
109 buf[1] = uint8_to_bcd(now.minute);
110 buf[2] = uint8_to_bcd(now.hour);
111 buf[3] = 1 << (now.day_of_week - 1);
112 buf[4] = uint8_to_bcd(now.day_of_month);
113 buf[5] = uint8_to_bcd(now.month);
114 buf[6] = uint8_to_bcd(static_cast<uint8_t>(now.year - 2000));
115 const bool written = this->write_bytes(RX8111_REG_SEC, buf, sizeof(buf));
116 // Always try to restart the counter, even after a failed calendar write.
117 const bool restarted = this->set_stop_bit_(false);
118 if (!written || !restarted) {
119 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
120 return;
121 }
122 if (!this->write_byte(RX8111_REG_FLAG, RX8111_KEEP_EVENT_FLAGS)) {
123 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
124 return;
125 }
126 this->status_clear_warning();
127 ESP_LOGD(TAG, "Wrote UTC time: %04d-%02d-%02d %02d:%02d:%02d", now.year, now.month, now.day_of_month, now.hour,
129}
130
132 uint8_t control;
133 if (!this->read_byte(RX8111_REG_CONTROL, &control))
134 return false;
135 if (stop) {
136 control |= RX8111_CTRL_STOP;
137 } else {
138 control &= ~RX8111_CTRL_STOP;
139 }
140 return this->write_byte(RX8111_REG_CONTROL, control);
141}
142
143} // namespace esphome::rx8111
void status_clear_warning()
Definition component.h:291
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) const
Definition i2c.h:251
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition i2c.h:217
ESPTime now()
Get the time in the currently defined timezone.
ESPTime utcnow()
Get the time without any time zone or DST corrections.
void synchronize_epoch_(uint32_t epoch)
Report a unix epoch as current time.
uint16_t flags
mopeka_std_values val[3]
ESPHOME_LOG_TAG(TAG, "rx8111")
int written
Definition helpers.h:1186
A more user-friendly version of struct tm from time.h.
Definition time.h:23
uint8_t minute
minutes after the hour [0-59]
Definition time.h:32
void recalc_timestamp_utc(bool use_day_of_year=true)
Recalculate the timestamp field from the other fields of this ESPTime instance (must be UTC).
Definition time.cpp:280
uint8_t second
seconds after the minute [0-60]
Definition time.h:30
uint8_t hour
hours since midnight [0-23]
Definition time.h:34
bool is_valid(bool check_day_of_week=true, bool check_day_of_year=true) const
Check if this ESPTime is valid (year >= 2019 and the requested fields are in range).
Definition time.h:82
static ESPTime from_epoch_utc(time_t epoch)
Convert an UTC epoch timestamp to a UTC time ESPTime instance.
Definition time.h:143
uint8_t day_of_month
day of the month [1-31]
Definition time.h:38
uint16_t year
year
Definition time.h:44
uint8_t month
month; january=1 [1-12]
Definition time.h:42
uint8_t day_of_week
day of the week; sunday=1 [1-7]
Definition time.h:36
uint16_t timestamp
Definition tt21100.cpp:2