mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	DS1307 real time clock component (#1441)
* initial support for DS1307 real time clock * add simple test, make sync functions public * cleanup lint * add sync to/from rtc actions * changes action names * Update esphome/components/ds1307/ds1307.cpp Co-authored-by: Guillermo Ruffino <glm.net@gmail.com> * Update esphome/components/ds1307/time.py Co-authored-by: Guillermo Ruffino <glm.net@gmail.com> * fix suggested change Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
This commit is contained in:
		
				
					committed by
					
						 Jesse Hills
						Jesse Hills
					
				
			
			
				
	
			
			
			
						parent
						
							585bb6dac8
						
					
				
				
					commit
					dcd3d2084d
				
			| @@ -29,6 +29,7 @@ esphome/components/ct_clamp/* @jesserockz | |||||||
| esphome/components/debug/* @OttoWinter | esphome/components/debug/* @OttoWinter | ||||||
| esphome/components/dfplayer/* @glmnet | esphome/components/dfplayer/* @glmnet | ||||||
| esphome/components/dht/* @OttoWinter | esphome/components/dht/* @OttoWinter | ||||||
|  | esphome/components/ds1307/* @badbadc0ffee | ||||||
| esphome/components/exposure_notifications/* @OttoWinter | esphome/components/exposure_notifications/* @OttoWinter | ||||||
| esphome/components/ezo/* @ssieb | esphome/components/ezo/* @ssieb | ||||||
| esphome/components/fastled_base/* @OttoWinter | esphome/components/fastled_base/* @OttoWinter | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								esphome/components/ds1307/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/ds1307/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										104
									
								
								esphome/components/ds1307/ds1307.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								esphome/components/ds1307/ds1307.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | |||||||
|  | #include "ds1307.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | // Datasheet: | ||||||
|  | // - https://datasheets.maximintegrated.com/en/ds/DS1307.pdf | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ds1307 { | ||||||
|  |  | ||||||
|  | static const char *TAG = "ds1307"; | ||||||
|  |  | ||||||
|  | void DS1307Component::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up DS1307..."); | ||||||
|  |   if (!this->read_rtc_()) { | ||||||
|  |     this->mark_failed(); | ||||||
|  |   } | ||||||
|  |   this->set_interval(15 * 60 * 1000, [&]() { this->read(); }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void DS1307Component::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "DS1307:"); | ||||||
|  |   LOG_I2C_DEVICE(this); | ||||||
|  |   if (this->is_failed()) { | ||||||
|  |     ESP_LOGE(TAG, "Communication with DS1307 failed!"); | ||||||
|  |   } | ||||||
|  |   ESP_LOGCONFIG(TAG, "  Timezone: '%s'", this->timezone_.c_str()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float DS1307Component::get_setup_priority() const { return setup_priority::DATA; } | ||||||
|  |  | ||||||
|  | void DS1307Component::read() { | ||||||
|  |   if (!this->read_rtc_()) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   if (ds1307_.reg.ch) { | ||||||
|  |     ESP_LOGW(TAG, "RTC halted, not syncing to system clock."); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   time::ESPTime rtc_time{.second = uint8_t(ds1307_.reg.second + 10 * ds1307_.reg.second_10), | ||||||
|  |                          .minute = uint8_t(ds1307_.reg.minute + 10u * ds1307_.reg.minute_10), | ||||||
|  |                          .hour = uint8_t(ds1307_.reg.hour + 10u * ds1307_.reg.hour_10), | ||||||
|  |                          .day_of_week = uint8_t(ds1307_.reg.weekday), | ||||||
|  |                          .day_of_month = uint8_t(ds1307_.reg.day + 10u * ds1307_.reg.day_10), | ||||||
|  |                          .day_of_year = 1,  // ignored by recalc_timestamp_utc(false) | ||||||
|  |                          .month = uint8_t(ds1307_.reg.month + 10u * ds1307_.reg.month_10), | ||||||
|  |                          .year = uint16_t(ds1307_.reg.year + 10u * ds1307_.reg.year_10 + 2000)}; | ||||||
|  |   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 DS1307Component::write() { | ||||||
|  |   auto now = time::RealTimeClock::utcnow(); | ||||||
|  |   if (!now.is_valid()) { | ||||||
|  |     ESP_LOGE(TAG, "Invalid system time, not syncing to RTC."); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   ds1307_.reg.year = (now.year - 2000) % 10; | ||||||
|  |   ds1307_.reg.year_10 = (now.year - 2000) / 10 % 10; | ||||||
|  |   ds1307_.reg.month = now.month % 10; | ||||||
|  |   ds1307_.reg.month_10 = now.month / 10; | ||||||
|  |   ds1307_.reg.day = now.day_of_month % 10; | ||||||
|  |   ds1307_.reg.day_10 = now.day_of_month / 10; | ||||||
|  |   ds1307_.reg.weekday = now.day_of_week; | ||||||
|  |   ds1307_.reg.hour = now.hour % 10; | ||||||
|  |   ds1307_.reg.hour_10 = now.hour / 10; | ||||||
|  |   ds1307_.reg.minute = now.minute % 10; | ||||||
|  |   ds1307_.reg.minute_10 = now.minute / 10; | ||||||
|  |   ds1307_.reg.second = now.second % 10; | ||||||
|  |   ds1307_.reg.second_10 = now.second / 10; | ||||||
|  |   ds1307_.reg.ch = false; | ||||||
|  |  | ||||||
|  |   this->write_rtc_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool DS1307Component::read_rtc_() { | ||||||
|  |   if (!this->read_bytes(0, this->ds1307_.raw, sizeof(this->ds1307_.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  CH:%s RS:%0u SQWE:%s OUT:%s", ds1307_.reg.hour_10, | ||||||
|  |            ds1307_.reg.hour, ds1307_.reg.minute_10, ds1307_.reg.minute, ds1307_.reg.second_10, ds1307_.reg.second, | ||||||
|  |            ds1307_.reg.year_10, ds1307_.reg.year, ds1307_.reg.month_10, ds1307_.reg.month, ds1307_.reg.day_10, | ||||||
|  |            ds1307_.reg.day, ONOFF(ds1307_.reg.ch), ds1307_.reg.rs, ONOFF(ds1307_.reg.sqwe), ONOFF(ds1307_.reg.out)); | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool DS1307Component::write_rtc_() { | ||||||
|  |   if (!this->write_bytes(0, this->ds1307_.raw, sizeof(this->ds1307_.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  CH:%s RS:%0u SQWE:%s OUT:%s", ds1307_.reg.hour_10, | ||||||
|  |            ds1307_.reg.hour, ds1307_.reg.minute_10, ds1307_.reg.minute, ds1307_.reg.second_10, ds1307_.reg.second, | ||||||
|  |            ds1307_.reg.year_10, ds1307_.reg.year, ds1307_.reg.month_10, ds1307_.reg.month, ds1307_.reg.day_10, | ||||||
|  |            ds1307_.reg.day, ONOFF(ds1307_.reg.ch), ds1307_.reg.rs, ONOFF(ds1307_.reg.sqwe), ONOFF(ds1307_.reg.out)); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | }  // namespace ds1307 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										69
									
								
								esphome/components/ds1307/ds1307.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								esphome/components/ds1307/ds1307.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/i2c/i2c.h" | ||||||
|  | #include "esphome/components/time/real_time_clock.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ds1307 { | ||||||
|  |  | ||||||
|  | class DS1307Component : public time::RealTimeClock, public i2c::I2CDevice { | ||||||
|  |  public: | ||||||
|  |   void setup() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override; | ||||||
|  |   void read(); | ||||||
|  |   void write(); | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool read_rtc_(); | ||||||
|  |   bool write_rtc_(); | ||||||
|  |   union DS1307Reg { | ||||||
|  |     struct { | ||||||
|  |       uint8_t second : 4; | ||||||
|  |       uint8_t second_10 : 3; | ||||||
|  |       bool ch : 1; | ||||||
|  |  | ||||||
|  |       uint8_t minute : 4; | ||||||
|  |       uint8_t minute_10 : 3; | ||||||
|  |       uint8_t unused_1 : 1; | ||||||
|  |  | ||||||
|  |       uint8_t hour : 4; | ||||||
|  |       uint8_t hour_10 : 2; | ||||||
|  |       uint8_t unused_2 : 2; | ||||||
|  |  | ||||||
|  |       uint8_t weekday : 3; | ||||||
|  |       uint8_t unused_3 : 5; | ||||||
|  |  | ||||||
|  |       uint8_t day : 4; | ||||||
|  |       uint8_t day_10 : 2; | ||||||
|  |       uint8_t unused_4 : 2; | ||||||
|  |  | ||||||
|  |       uint8_t month : 4; | ||||||
|  |       uint8_t month_10 : 1; | ||||||
|  |       uint8_t unused_5 : 3; | ||||||
|  |  | ||||||
|  |       uint8_t year : 4; | ||||||
|  |       uint8_t year_10 : 4; | ||||||
|  |  | ||||||
|  |       uint8_t rs : 2; | ||||||
|  |       uint8_t unused_6 : 2; | ||||||
|  |       bool sqwe : 1; | ||||||
|  |       uint8_t unused_7 : 2; | ||||||
|  |       bool out : 1; | ||||||
|  |     } reg; | ||||||
|  |     mutable uint8_t raw[sizeof(reg)]; | ||||||
|  |   } ds1307_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class WriteAction : public Action<Ts...>, public Parented<DS1307Component> { | ||||||
|  |  public: | ||||||
|  |   void play(Ts... x) override { this->parent_->write(); } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template<typename... Ts> class ReadAction : public Action<Ts...>, public Parented<DS1307Component> { | ||||||
|  |  public: | ||||||
|  |   void play(Ts... x) override { this->parent_->read(); } | ||||||
|  | }; | ||||||
|  | }  // namespace ds1307 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										44
									
								
								esphome/components/ds1307/time.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/ds1307/time.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | import esphome.config_validation as cv | ||||||
|  | import esphome.codegen as cg | ||||||
|  | from esphome import automation | ||||||
|  | from esphome.components import i2c, time | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CODEOWNERS = ['@badbadc0ffee'] | ||||||
|  | DEPENDENCIES = ['i2c'] | ||||||
|  | ds1307_ns = cg.esphome_ns.namespace('ds1307') | ||||||
|  | DS1307Component = ds1307_ns.class_('DS1307Component', time.RealTimeClock, i2c.I2CDevice) | ||||||
|  | WriteAction = ds1307_ns.class_('WriteAction', automation.Action) | ||||||
|  | ReadAction = ds1307_ns.class_('ReadAction', automation.Action) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = time.TIME_SCHEMA.extend({ | ||||||
|  |     cv.GenerateID(): cv.declare_id(DS1307Component), | ||||||
|  | }).extend(i2c.i2c_device_schema(0x68)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action('ds1307.write', WriteAction, cv.Schema({ | ||||||
|  |     cv.GenerateID(): cv.use_id(DS1307Component), | ||||||
|  | })) | ||||||
|  | def ds1307_write_to_code(config, action_id, template_arg, args): | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|  |     yield cg.register_parented(var, config[CONF_ID]) | ||||||
|  |     yield var | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @automation.register_action('ds1307.read', ReadAction, automation.maybe_simple_id({ | ||||||
|  |     cv.GenerateID(): cv.use_id(DS1307Component), | ||||||
|  | })) | ||||||
|  | def ds1307_read_to_code(config, action_id, template_arg, args): | ||||||
|  |     var = cg.new_Pvariable(action_id, template_arg) | ||||||
|  |     yield cg.register_parented(var, config[CONF_ID]) | ||||||
|  |     yield var | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |  | ||||||
|  |     yield cg.register_component(var, config) | ||||||
|  |     yield i2c.register_i2c_device(var, config) | ||||||
|  |     yield time.register_time(var, config) | ||||||
| @@ -1837,6 +1837,20 @@ time: | |||||||
|       then: |       then: | ||||||
|         - lambda: 'ESP_LOGD("main", "time");' |         - lambda: 'ESP_LOGD("main", "time");' | ||||||
|   - platform: gps |   - platform: gps | ||||||
|  |     on_time: | ||||||
|  |       seconds: 0 | ||||||
|  |       minutes: /15 | ||||||
|  |       then: | ||||||
|  |         ds1307.write: | ||||||
|  |           id: ds1307_time | ||||||
|  |   - platform: ds1307 | ||||||
|  |     id: ds1307_time | ||||||
|  |     on_time: | ||||||
|  |       seconds: 0 | ||||||
|  |       then: | ||||||
|  |           ds1307.read | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| cover: | cover: | ||||||
|   - platform: template |   - platform: template | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user