Display & Manually Set RTC (DS3231) Time in ESPHome + SNTP Sync Every 12 Hours – Need Help with Web Interface

Hey everyone! :wave:

I’ve built a custom ESPHome component for the DS3231 RTC. It regularly reads and logs the RTC time.

:white_check_mark: 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.

:jigsaw: Custom Component (Core Snippet):


:wrench: 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 {};



:question: 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! :raised_hands: Really appreciate any pointers, code samples, or resources.

I do not know if this is possible in ESPHome yet, but ESP devices can be made into Stratum 1 servers with little effort.