Hey everyone!
I’ve built a custom ESPHome component for the DS3231 RTC. It regularly reads and logs the RTC time.
What I Have:
- On boot, syncs time from SNTP (if valid)
- Logs RTC time every 10 seconds.
- Plan to auto-sync via SNTP every 12 hours.
- Need to manually set RTC time via a text input on a web page if SNTP isn’t available.
Custom Component (Core Snippet):
YAML Snippet
time:
- platform: sntp
id: sntp_time
web_server:
port: 80
custom_component:
- lambda: |-
class DS3231Component : public PollingComponent {
public:
DS3231Component(esphome::time::RealTimeClock *rtc) : PollingComponent(10000), rtc_(rtc) {}
void setup() override {
if (!Wire.requestFrom(0x68, 1)) {
ESP_LOGE("DS3231", "RTC not found at I2C address 0x68");
return;
}
ESP_LOGI("DS3231", "RTC found at I2C address 0x68");
auto now = this->rtc_->now();
if (now.is_valid()) {
set_time(now.year, now.month, now.day_of_month, now.hour, now.minute, now.second, now.day_of_week);
ESP_LOGI("DS3231", "RTC time set to %04d-%02d-%02d %02d:%02d:%02d",
now.year, now.month, now.day_of_month, now.hour, now.minute, now.second);
} else {
ESP_LOGE("DS3231", "SNTP time is not valid; cannot set RTC");
}
}
void update() override {
auto time = get_time();
if (time.year > 0) {
ESP_LOGI("DS3231", "Current RTC Time: %04d-%02d-%02d %02d:%02d:%02d",
time.year, time.month, time.day, time.hour, time.minute, time.second);
} else {
ESP_LOGE("DS3231", "Failed to read time from RTC");
}
}
void set_time(int year, int month, int day, int hour, int minute, int second, int day_of_week) {
Wire.beginTransmission(0x68);
Wire.write(0); // Start at register 0
Wire.write(dec_to_bcd(second)); // Seconds
Wire.write(dec_to_bcd(minute)); // Minutes
Wire.write(dec_to_bcd(hour)); // Hours
Wire.write(dec_to_bcd(day_of_week)); // Day of the week
Wire.write(dec_to_bcd(day)); // Day of the month
Wire.write(dec_to_bcd(month)); // Month
Wire.write(dec_to_bcd(year - 2000)); // Year
Wire.endTransmission();
}
struct Time {
int year;
int month;
int day;
int hour;
int minute;
int second;
int day_of_week;
};
Time get_time() {
Wire.beginTransmission(0x68);
Wire.write(0); // Start at register 0
Wire.endTransmission();
if (Wire.requestFrom(0x68, 7) != 7) {
ESP_LOGE("DS3231", "Failed to read time registers");
return Time{0, 0, 0, 0, 0, 0, 0};
}
uint8_t second = bcd_to_dec(Wire.read());
uint8_t minute = bcd_to_dec(Wire.read());
uint8_t hour = bcd_to_dec(Wire.read());
uint8_t day_of_week = bcd_to_dec(Wire.read());
uint8_t day = bcd_to_dec(Wire.read());
uint8_t month = bcd_to_dec(Wire.read());
uint16_t year = bcd_to_dec(Wire.read()) + 2000;
return Time{year, month, day, hour, minute, second, day_of_week};
}
private:
esphome::time::RealTimeClock *rtc_;
uint8_t dec_to_bcd(int val) {
return ((val / 10 * 16) + (val % 10));
}
int bcd_to_dec(uint8_t val) {
return ((val / 16 * 10) + (val % 16));
}
};
auto my_rtc = new DS3231Component(id(sntp_time));
App.register_component(my_rtc);
return {};
Questions:
- Any ESPHome-native solutions for handling SNTP fallback and manual entry?
- Has anyone built a manual time input interface (e.g., web form) to set RTC time directly on an ESP device?
- Are there simpler alternatives to achieve manual time entry + RTC setting inside ESPHome?
- How to auto-sync via SNTP every 12 hours, if sntp time is valid and available.
Thanks in advance! Really appreciate any pointers, code samples, or resources.