mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Add I2CMultiplexer in generel and the TCA9548A in special (#1410)
* Added I2CMultiplexer in generel and the TCA9548A in special * cleanup * tidy * tidy * tidy * tidy * Update CODEOWNERS * Update CODEOWNERS * added CODEOWNERS * Fix CODEOWNERS * protected function * fixed scan * fixed style * added to test1.yaml * Update esphome/components/tca9548a/__init__.py * Update esphome/components/i2c/__init__.py Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> * Update esphome/components/i2c/i2c.cpp Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> * Update esphome/components/i2c/__init__.py * Update esphome/components/i2c/__init__.py Co-authored-by: Guillermo Ruffino <glm.net@gmail.com> * Update esphome/components/i2c/i2c.cpp Co-authored-by: Guillermo Ruffino <glm.net@gmail.com> * added define statements for I2C Multiplexer * fix * try to tidy * bug fix * tidy * override fix * only change channel if different * tidy * added test * testfix * added defines * tidy * fix dep * like recommended Co-authored-by: Andreas Hergert <andreas.hergert@otrs.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Guillermo Ruffino <glm.net@gmail.com>
This commit is contained in:
		| @@ -95,6 +95,7 @@ esphome/components/st7789v/* @kbx81 | |||||||
| esphome/components/substitutions/* @esphome/core | esphome/components/substitutions/* @esphome/core | ||||||
| esphome/components/sun/* @OttoWinter | esphome/components/sun/* @OttoWinter | ||||||
| esphome/components/switch/* @esphome/core | esphome/components/switch/* @esphome/core | ||||||
|  | esphome/components/tca9548a/* @andreashergert1984 | ||||||
| esphome/components/tcl112/* @glmnet | esphome/components/tcl112/* @glmnet | ||||||
| esphome/components/teleinfo/* @0hax | esphome/components/teleinfo/* @0hax | ||||||
| esphome/components/thermostat/* @kbx81 | esphome/components/thermostat/* @kbx81 | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ import esphome.codegen as cg | |||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome import pins | from esphome import pins | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|  |     CONF_CHANNEL, | ||||||
|     CONF_FREQUENCY, |     CONF_FREQUENCY, | ||||||
|     CONF_ID, |     CONF_ID, | ||||||
|     CONF_SCAN, |     CONF_SCAN, | ||||||
| @@ -9,6 +10,7 @@ from esphome.const import ( | |||||||
|     CONF_SDA, |     CONF_SDA, | ||||||
|     CONF_ADDRESS, |     CONF_ADDRESS, | ||||||
|     CONF_I2C_ID, |     CONF_I2C_ID, | ||||||
|  |     CONF_MULTIPLEXER, | ||||||
| ) | ) | ||||||
| from esphome.core import coroutine, coroutine_with_priority | from esphome.core import coroutine, coroutine_with_priority | ||||||
|  |  | ||||||
| @@ -16,6 +18,7 @@ CODEOWNERS = ["@esphome/core"] | |||||||
| i2c_ns = cg.esphome_ns.namespace("i2c") | i2c_ns = cg.esphome_ns.namespace("i2c") | ||||||
| I2CComponent = i2c_ns.class_("I2CComponent", cg.Component) | I2CComponent = i2c_ns.class_("I2CComponent", cg.Component) | ||||||
| I2CDevice = i2c_ns.class_("I2CDevice") | I2CDevice = i2c_ns.class_("I2CDevice") | ||||||
|  | I2CMultiplexer = i2c_ns.class_("I2CMultiplexer", I2CDevice) | ||||||
|  |  | ||||||
| MULTI_CONF = True | MULTI_CONF = True | ||||||
| CONFIG_SCHEMA = cv.Schema( | CONFIG_SCHEMA = cv.Schema( | ||||||
| @@ -30,6 +33,13 @@ CONFIG_SCHEMA = cv.Schema( | |||||||
|     } |     } | ||||||
| ).extend(cv.COMPONENT_SCHEMA) | ).extend(cv.COMPONENT_SCHEMA) | ||||||
|  |  | ||||||
|  | I2CMULTIPLEXER_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.Required(CONF_ID): cv.use_id(I2CMultiplexer), | ||||||
|  |         cv.Required(CONF_CHANNEL): cv.uint8_t, | ||||||
|  |     } | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @coroutine_with_priority(1.0) | @coroutine_with_priority(1.0) | ||||||
| def to_code(config): | def to_code(config): | ||||||
| @@ -53,6 +63,7 @@ def i2c_device_schema(default_address): | |||||||
|     """ |     """ | ||||||
|     schema = { |     schema = { | ||||||
|         cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CComponent), |         cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CComponent), | ||||||
|  |         cv.Optional(CONF_MULTIPLEXER): I2CMULTIPLEXER_SCHEMA, | ||||||
|     } |     } | ||||||
|     if default_address is None: |     if default_address is None: | ||||||
|         schema[cv.Required(CONF_ADDRESS)] = cv.i2c_address |         schema[cv.Required(CONF_ADDRESS)] = cv.i2c_address | ||||||
| @@ -72,3 +83,8 @@ def register_i2c_device(var, config): | |||||||
|     parent = yield cg.get_variable(config[CONF_I2C_ID]) |     parent = yield cg.get_variable(config[CONF_I2C_ID]) | ||||||
|     cg.add(var.set_i2c_parent(parent)) |     cg.add(var.set_i2c_parent(parent)) | ||||||
|     cg.add(var.set_i2c_address(config[CONF_ADDRESS])) |     cg.add(var.set_i2c_address(config[CONF_ADDRESS])) | ||||||
|  |     if CONF_MULTIPLEXER in config: | ||||||
|  |         multiplexer = yield cg.get_variable(config[CONF_MULTIPLEXER][CONF_ID]) | ||||||
|  |         cg.add( | ||||||
|  |             var.set_i2c_multiplexer(multiplexer, config[CONF_MULTIPLEXER][CONF_CHANNEL]) | ||||||
|  |         ) | ||||||
|   | |||||||
| @@ -178,28 +178,67 @@ bool I2CComponent::write_byte_16(uint8_t address, uint8_t a_register, uint16_t d | |||||||
| } | } | ||||||
|  |  | ||||||
| void I2CDevice::set_i2c_address(uint8_t address) { this->address_ = address; } | void I2CDevice::set_i2c_address(uint8_t address) { this->address_ = address; } | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  | void I2CDevice::set_i2c_multiplexer(I2CMultiplexer *multiplexer, uint8_t channel) { | ||||||
|  |   ESP_LOGVV(TAG, "    Setting Multiplexer %p for channel %d", multiplexer, channel); | ||||||
|  |   this->multiplexer_ = multiplexer; | ||||||
|  |   this->channel_ = channel; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void I2CDevice::check_multiplexer_() { | ||||||
|  |   if (this->multiplexer_ != nullptr) { | ||||||
|  |     ESP_LOGVV(TAG, "Multiplexer setting channel to %d", this->channel_); | ||||||
|  |     this->multiplexer_->set_channel(this->channel_); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| bool I2CDevice::read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) {  // NOLINT | bool I2CDevice::read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) {  // NOLINT | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   this->check_multiplexer_(); | ||||||
|  | #endif | ||||||
|   return this->parent_->read_bytes(this->address_, a_register, data, len, conversion); |   return this->parent_->read_bytes(this->address_, a_register, data, len, conversion); | ||||||
| } | } | ||||||
| bool I2CDevice::read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion) {  // NOLINT | bool I2CDevice::read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion) {  // NOLINT | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   this->check_multiplexer_(); | ||||||
|  | #endif | ||||||
|   return this->parent_->read_byte(this->address_, a_register, data, conversion); |   return this->parent_->read_byte(this->address_, a_register, data, conversion); | ||||||
| } | } | ||||||
| bool I2CDevice::write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) {  // NOLINT | bool I2CDevice::write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) {  // NOLINT | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   this->check_multiplexer_(); | ||||||
|  | #endif | ||||||
|   return this->parent_->write_bytes(this->address_, a_register, data, len); |   return this->parent_->write_bytes(this->address_, a_register, data, len); | ||||||
| } | } | ||||||
| bool I2CDevice::write_byte(uint8_t a_register, uint8_t data) {  // NOLINT | bool I2CDevice::write_byte(uint8_t a_register, uint8_t data) {  // NOLINT | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   this->check_multiplexer_(); | ||||||
|  | #endif | ||||||
|   return this->parent_->write_byte(this->address_, a_register, data); |   return this->parent_->write_byte(this->address_, a_register, data); | ||||||
| } | } | ||||||
| bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion) {  // NOLINT | bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion) {  // NOLINT | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   this->check_multiplexer_(); | ||||||
|  | #endif | ||||||
|   return this->parent_->read_bytes_16(this->address_, a_register, data, len, conversion); |   return this->parent_->read_bytes_16(this->address_, a_register, data, len, conversion); | ||||||
| } | } | ||||||
| bool I2CDevice::read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion) {  // NOLINT | bool I2CDevice::read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion) {  // NOLINT | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   this->check_multiplexer_(); | ||||||
|  | #endif | ||||||
|   return this->parent_->read_byte_16(this->address_, a_register, data, conversion); |   return this->parent_->read_byte_16(this->address_, a_register, data, conversion); | ||||||
| } | } | ||||||
| bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) {  // NOLINT | bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) {  // NOLINT | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   this->check_multiplexer_(); | ||||||
|  | #endif | ||||||
|   return this->parent_->write_bytes_16(this->address_, a_register, data, len); |   return this->parent_->write_bytes_16(this->address_, a_register, data, len); | ||||||
| } | } | ||||||
| bool I2CDevice::write_byte_16(uint8_t a_register, uint16_t data) {  // NOLINT | bool I2CDevice::write_byte_16(uint8_t a_register, uint16_t data) {  // NOLINT | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   this->check_multiplexer_(); | ||||||
|  | #endif | ||||||
|   return this->parent_->write_byte_16(this->address_, a_register, data); |   return this->parent_->write_byte_16(this->address_, a_register, data); | ||||||
| } | } | ||||||
| void I2CDevice::set_i2c_parent(I2CComponent *parent) { this->parent_ = parent; } | void I2CDevice::set_i2c_parent(I2CComponent *parent) { this->parent_ = parent; } | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <Wire.h> | #include <Wire.h> | ||||||
|  | #include "esphome/core/defines.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
| @@ -135,7 +136,7 @@ extern uint8_t next_i2c_bus_num_; | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| class I2CDevice; | class I2CDevice; | ||||||
|  | class I2CMultiplexer; | ||||||
| class I2CRegister { | class I2CRegister { | ||||||
|  public: |  public: | ||||||
|   I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} |   I2CRegister(I2CDevice *parent, uint8_t a_register) : parent_(parent), register_(a_register) {} | ||||||
| @@ -167,7 +168,10 @@ class I2CDevice { | |||||||
|  |  | ||||||
|   /// Manually set the i2c address of this device. |   /// Manually set the i2c address of this device. | ||||||
|   void set_i2c_address(uint8_t address); |   void set_i2c_address(uint8_t address); | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   /// Manually set the i2c multiplexer of this device. | ||||||
|  |   void set_i2c_multiplexer(I2CMultiplexer *multiplexer, uint8_t channel); | ||||||
|  | #endif | ||||||
|   /// Manually set the parent i2c bus for this device. |   /// Manually set the parent i2c bus for this device. | ||||||
|   void set_i2c_parent(I2CComponent *parent); |   void set_i2c_parent(I2CComponent *parent); | ||||||
|  |  | ||||||
| @@ -280,9 +284,19 @@ class I2CDevice { | |||||||
|   bool write_byte_16(uint8_t a_register, uint16_t data); |   bool write_byte_16(uint8_t a_register, uint16_t data); | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|  |   // Checks for multiplexer set and set channel | ||||||
|  |   void check_multiplexer_(); | ||||||
|   uint8_t address_{0x00}; |   uint8_t address_{0x00}; | ||||||
|   I2CComponent *parent_{nullptr}; |   I2CComponent *parent_{nullptr}; | ||||||
|  | #ifdef USE_I2C_MULTIPLEXER | ||||||
|  |   I2CMultiplexer *multiplexer_{nullptr}; | ||||||
|  |   uint8_t channel_; | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  | class I2CMultiplexer : public I2CDevice { | ||||||
|  |  public: | ||||||
|  |   I2CMultiplexer() = default; | ||||||
|  |   virtual void set_channel(uint8_t channelno); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace i2c | }  // namespace i2c | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								esphome/components/tca9548a/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								esphome/components/tca9548a/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import i2c | ||||||
|  | from esphome.const import CONF_ID, CONF_SCAN | ||||||
|  |  | ||||||
|  | CODEOWNERS = ["@andreashergert1984"] | ||||||
|  |  | ||||||
|  | DEPENDENCIES = ["i2c"] | ||||||
|  |  | ||||||
|  | tca9548a_ns = cg.esphome_ns.namespace("tca9548a") | ||||||
|  | TCA9548AComponent = tca9548a_ns.class_( | ||||||
|  |     "TCA9548AComponent", cg.PollingComponent, i2c.I2CMultiplexer | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | MULTI_CONF = True | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.Schema( | ||||||
|  |     { | ||||||
|  |         cv.GenerateID(): cv.declare_id(TCA9548AComponent), | ||||||
|  |         cv.Optional(CONF_SCAN, default=True): cv.boolean, | ||||||
|  |     } | ||||||
|  | ).extend(i2c.i2c_device_schema(0x70)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     cg.add_define("USE_I2C_MULTIPLEXER") | ||||||
|  |     yield cg.register_component(var, config) | ||||||
|  |     yield i2c.register_i2c_device(var, config) | ||||||
|  |     cg.add(var.set_scan(config[CONF_SCAN])) | ||||||
							
								
								
									
										41
									
								
								esphome/components/tca9548a/tca9548a.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								esphome/components/tca9548a/tca9548a.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | #include "tca9548a.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace tca9548a { | ||||||
|  |  | ||||||
|  | static const char *TAG = "tca9548a"; | ||||||
|  |  | ||||||
|  | void TCA9548AComponent::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up TCA9548A..."); | ||||||
|  |   uint8_t status = 0; | ||||||
|  |   if (!this->read_byte(0x00, &status)) { | ||||||
|  |     ESP_LOGI(TAG, "TCA9548A failed"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   // out of range to make sure on first set_channel a new one will be set | ||||||
|  |   this->current_channelno_ = 8; | ||||||
|  |   ESP_LOGCONFIG(TAG, "Channels currently open: %d", status); | ||||||
|  | } | ||||||
|  | void TCA9548AComponent::dump_config() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "TCA9548A:"); | ||||||
|  |   LOG_I2C_DEVICE(this); | ||||||
|  |   if (this->scan_) { | ||||||
|  |     for (uint8_t i = 0; i < 8; i++) { | ||||||
|  |       ESP_LOGCONFIG(TAG, "Activating channel: %d", i); | ||||||
|  |       this->set_channel(i); | ||||||
|  |       this->parent_->dump_config(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void TCA9548AComponent::set_channel(uint8_t channelno) { | ||||||
|  |   if (this->current_channelno_ != channelno) { | ||||||
|  |     this->current_channelno_ = channelno; | ||||||
|  |     uint8_t channelbyte = 1 << channelno; | ||||||
|  |     this->write_byte(0x70, channelbyte); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace tca9548a | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										22
									
								
								esphome/components/tca9548a/tca9548a.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								esphome/components/tca9548a/tca9548a.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/i2c/i2c.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace tca9548a { | ||||||
|  |  | ||||||
|  | class TCA9548AComponent : public Component, public i2c::I2CMultiplexer { | ||||||
|  |  public: | ||||||
|  |   void set_scan(bool scan) { scan_ = scan; } | ||||||
|  |   void setup() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   void update(); | ||||||
|  |   void set_channel(uint8_t channelno) override; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   bool scan_; | ||||||
|  |   uint8_t current_channelno_; | ||||||
|  | }; | ||||||
|  | }  // namespace tca9548a | ||||||
|  | }  // namespace esphome | ||||||
| @@ -1993,6 +1993,18 @@ cover: | |||||||
|  |  | ||||||
| debug: | debug: | ||||||
|  |  | ||||||
|  | tca9548a: | ||||||
|  |   - address: 0x70 | ||||||
|  |     id: multiplex0 | ||||||
|  |     scan: True | ||||||
|  |   - address: 0x71 | ||||||
|  |     id: multiplex1 | ||||||
|  |     scan: True | ||||||
|  |     multiplexer: | ||||||
|  |       id: multiplex0 | ||||||
|  |       channel: 0 | ||||||
|  |      | ||||||
|  |  | ||||||
| pcf8574: | pcf8574: | ||||||
|   - id: 'pcf8574_hub' |   - id: 'pcf8574_hub' | ||||||
|     address: 0x21 |     address: 0x21 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user