mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[pca9554] Migrate to CachedGpioExpander to reduce I2C bus usage (#10571)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -342,7 +342,7 @@ esphome/components/ota/* @esphome/core | ||||
| esphome/components/output/* @esphome/core | ||||
| esphome/components/packet_transport/* @clydebarrow | ||||
| esphome/components/pca6416a/* @Mat931 | ||||
| esphome/components/pca9554/* @clydebarrow @hwstar | ||||
| esphome/components/pca9554/* @bdraco @clydebarrow @hwstar | ||||
| esphome/components/pcf85063/* @brogon | ||||
| esphome/components/pcf8563/* @KoenBreeman | ||||
| esphome/components/pi4ioe5v6408/* @jesserockz | ||||
|   | ||||
| @@ -11,7 +11,8 @@ from esphome.const import ( | ||||
|     CONF_OUTPUT, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@hwstar", "@clydebarrow"] | ||||
| CODEOWNERS = ["@hwstar", "@clydebarrow", "@bdraco"] | ||||
| AUTO_LOAD = ["gpio_expander"] | ||||
| DEPENDENCIES = ["i2c"] | ||||
| MULTI_CONF = True | ||||
| CONF_PIN_COUNT = "pin_count" | ||||
|   | ||||
| @@ -37,10 +37,9 @@ void PCA9554Component::setup() { | ||||
| } | ||||
|  | ||||
| void PCA9554Component::loop() { | ||||
|   // The read_inputs_() method will cache the input values from the chip. | ||||
|   this->read_inputs_(); | ||||
|   // Clear all the previously read flags. | ||||
|   this->was_previously_read_ = 0x00; | ||||
|   // Invalidate the cache at the start of each loop. | ||||
|   // The actual read will happen on demand when digital_read() is called | ||||
|   this->reset_pin_cache_(); | ||||
| } | ||||
|  | ||||
| void PCA9554Component::dump_config() { | ||||
| @@ -54,21 +53,17 @@ void PCA9554Component::dump_config() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool PCA9554Component::digital_read(uint8_t pin) { | ||||
|   // Note: We want to try and avoid doing any I2C bus read transactions here | ||||
|   // to conserve I2C bus bandwidth. So what we do is check to see if we | ||||
|   // have seen a read during the time esphome is running this loop. If we have, | ||||
|   // we do an I2C bus transaction to get the latest value. If we haven't | ||||
|   // we return a cached value which was read at the time loop() was called. | ||||
|   if (this->was_previously_read_ & (1 << pin)) | ||||
|     this->read_inputs_();  // Force a read of a new value | ||||
|   // Indicate we saw a read request for this pin in case a | ||||
|   // read happens later in the same loop. | ||||
|   this->was_previously_read_ |= (1 << pin); | ||||
| bool PCA9554Component::digital_read_hw(uint8_t pin) { | ||||
|   // Read all pins from hardware into input_mask_ | ||||
|   return this->read_inputs_();  // Return true if I2C read succeeded, false on error | ||||
| } | ||||
|  | ||||
| bool PCA9554Component::digital_read_cache(uint8_t pin) { | ||||
|   // Return the cached pin state from input_mask_ | ||||
|   return this->input_mask_ & (1 << pin); | ||||
| } | ||||
|  | ||||
| void PCA9554Component::digital_write(uint8_t pin, bool value) { | ||||
| void PCA9554Component::digital_write_hw(uint8_t pin, bool value) { | ||||
|   if (value) { | ||||
|     this->output_mask_ |= (1 << pin); | ||||
|   } else { | ||||
| @@ -127,8 +122,7 @@ bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) { | ||||
|  | ||||
| float PCA9554Component::get_setup_priority() const { return setup_priority::IO; } | ||||
|  | ||||
| // Run our loop() method very early in the loop, so that we cache read values before | ||||
| // before other components call our digital_read() method. | ||||
| // Run our loop() method early to invalidate cache before any other components access the pins | ||||
| float PCA9554Component::get_loop_priority() const { return 9.0f; }  // Just after WIFI | ||||
|  | ||||
| void PCA9554GPIOPin::setup() { pin_mode(flags_); } | ||||
|   | ||||
| @@ -3,22 +3,21 @@ | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
| #include "esphome/components/gpio_expander/cached_gpio.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace pca9554 { | ||||
|  | ||||
| class PCA9554Component : public Component, public i2c::I2CDevice { | ||||
| class PCA9554Component : public Component, | ||||
|                          public i2c::I2CDevice, | ||||
|                          public gpio_expander::CachedGpioExpander<uint16_t, 16> { | ||||
|  public: | ||||
|   PCA9554Component() = default; | ||||
|  | ||||
|   /// Check i2c availability and setup masks | ||||
|   void setup() override; | ||||
|   /// Poll for input changes periodically | ||||
|   /// Invalidate cache at start of each loop | ||||
|   void loop() override; | ||||
|   /// Helper function to read the value of a pin. | ||||
|   bool digital_read(uint8_t pin); | ||||
|   /// Helper function to write the value of a pin. | ||||
|   void digital_write(uint8_t pin, bool value); | ||||
|   /// Helper function to set the pin mode of a pin. | ||||
|   void pin_mode(uint8_t pin, gpio::Flags flags); | ||||
|  | ||||
| @@ -32,9 +31,13 @@ class PCA9554Component : public Component, public i2c::I2CDevice { | ||||
|  | ||||
|  protected: | ||||
|   bool read_inputs_(); | ||||
|  | ||||
|   bool write_register_(uint8_t reg, uint16_t value); | ||||
|  | ||||
|   // Virtual methods from CachedGpioExpander | ||||
|   bool digital_read_hw(uint8_t pin) override; | ||||
|   bool digital_read_cache(uint8_t pin) override; | ||||
|   void digital_write_hw(uint8_t pin, bool value) override; | ||||
|  | ||||
|   /// number of bits the expander has | ||||
|   size_t pin_count_{8}; | ||||
|   /// width of registers | ||||
| @@ -45,8 +48,6 @@ class PCA9554Component : public Component, public i2c::I2CDevice { | ||||
|   uint16_t output_mask_{0x00}; | ||||
|   /// The state of the actual input pin states - 1 means HIGH, 0 means LOW | ||||
|   uint16_t input_mask_{0x00}; | ||||
|   /// Flags to check if read previously during this loop | ||||
|   uint16_t was_previously_read_ = {0x00}; | ||||
|   /// Storage for last I2C error seen | ||||
|   esphome::i2c::ErrorCode last_error_; | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user