ESPHome 2025.5.0
Loading...
Searching...
No Matches
pca9554.cpp
Go to the documentation of this file.
1#include "pca9554.h"
2#include "esphome/core/log.h"
3
4namespace esphome {
5namespace pca9554 {
6
7// for 16 bit expanders, these addresses will be doubled.
8const uint8_t INPUT_REG = 0;
9const uint8_t OUTPUT_REG = 1;
10const uint8_t INVERT_REG = 2;
11const uint8_t CONFIG_REG = 3;
12
13static const char *const TAG = "pca9554";
14
16 ESP_LOGCONFIG(TAG, "Setting up PCA9554/PCA9554A...");
17 this->reg_width_ = (this->pin_count_ + 7) / 8;
18 // Test to see if device exists
19 if (!this->read_inputs_()) {
20 ESP_LOGE(TAG, "PCA95xx not detected at 0x%02X", this->address_);
21 this->mark_failed();
22 return;
23 }
24
25 // No polarity inversion
26 this->write_register_(INVERT_REG, 0);
27 // All inputs at initialization
28 this->config_mask_ = 0;
29 // Invert mask as the part sees a 1 as an input
30 this->write_register_(CONFIG_REG, ~this->config_mask_);
31 // All outputs low
32 this->output_mask_ = 0;
33 this->write_register_(OUTPUT_REG, this->output_mask_);
34 // Read the inputs
35 this->read_inputs_();
36 ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(),
37 this->status_has_error());
38}
39
41 // The read_inputs_() method will cache the input values from the chip.
42 this->read_inputs_();
43 // Clear all the previously read flags.
44 this->was_previously_read_ = 0x00;
45}
46
48 ESP_LOGCONFIG(TAG, "PCA9554:");
49 ESP_LOGCONFIG(TAG, " I/O Pins: %d", this->pin_count_);
50 LOG_I2C_DEVICE(this)
51 if (this->is_failed()) {
52 ESP_LOGE(TAG, "Communication with PCA9554 failed!");
53 }
54}
55
57 // Note: We want to try and avoid doing any I2C bus read transactions here
58 // to conserve I2C bus bandwidth. So what we do is check to see if we
59 // have seen a read during the time esphome is running this loop. If we have,
60 // we do an I2C bus transaction to get the latest value. If we haven't
61 // we return a cached value which was read at the time loop() was called.
62 if (this->was_previously_read_ & (1 << pin))
63 this->read_inputs_(); // Force a read of a new value
64 // Indicate we saw a read request for this pin in case a
65 // read happens later in the same loop.
66 this->was_previously_read_ |= (1 << pin);
67 return this->input_mask_ & (1 << pin);
68}
69
70void PCA9554Component::digital_write(uint8_t pin, bool value) {
71 if (value) {
72 this->output_mask_ |= (1 << pin);
73 } else {
74 this->output_mask_ &= ~(1 << pin);
75 }
76 this->write_register_(OUTPUT_REG, this->output_mask_);
77}
78
79void PCA9554Component::pin_mode(uint8_t pin, gpio::Flags flags) {
80 if (flags == gpio::FLAG_INPUT) {
81 // Clear mode mask bit
82 this->config_mask_ &= ~(1 << pin);
83 } else if (flags == gpio::FLAG_OUTPUT) {
84 // Set mode mask bit
85 this->config_mask_ |= 1 << pin;
86 }
87 this->write_register_(CONFIG_REG, ~this->config_mask_);
88}
89
91 uint8_t inputs[2];
92
93 if (this->is_failed()) {
94 ESP_LOGD(TAG, "Device marked failed");
95 return false;
96 }
97
98 this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_, true);
99 if (this->last_error_ != i2c::ERROR_OK) {
100 this->status_set_warning();
101 ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
102 return false;
103 }
104 this->status_clear_warning();
105 this->input_mask_ = inputs[0];
106 if (this->reg_width_ == 2) {
107 this->input_mask_ |= inputs[1] << 8;
108 }
109 return true;
110}
111
112bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) {
113 uint8_t outputs[2];
114 outputs[0] = (uint8_t) value;
115 outputs[1] = (uint8_t) (value >> 8);
116 this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_, true);
117 if (this->last_error_ != i2c::ERROR_OK) {
118 this->status_set_warning();
119 ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
120 return false;
121 }
122
123 this->status_clear_warning();
124 return true;
125}
126
128
129// Run our loop() method very early in the loop, so that we cache read values before
130// before other components call our digital_read() method.
131float PCA9554Component::get_loop_priority() const { return 9.0f; } // Just after WIFI
132
134void PCA9554GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
135bool PCA9554GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
136void PCA9554GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
137std::string PCA9554GPIOPin::dump_summary() const {
138 char buffer[32];
139 snprintf(buffer, sizeof(buffer), "%u via PCA9554", pin_);
140 return buffer;
141}
142
143} // namespace pca9554
144} // namespace esphome
virtual void mark_failed()
Mark this component as failed.
bool is_failed() const
bool status_has_warning() const
bool status_has_error() const
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop=true)
writes an array of bytes to a specific register in the I²C device
Definition i2c.cpp:25
uint8_t address_
store the address of the device on the bus
Definition i2c.h:273
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop=true)
reads an array of bytes from a specific register in the I²C device
Definition i2c.cpp:10
float get_setup_priority() const override
Definition pca9554.cpp:127
size_t pin_count_
number of bits the expander has
Definition pca9554.h:39
bool write_register_(uint8_t reg, uint16_t value)
Definition pca9554.cpp:112
uint16_t was_previously_read_
Flags to check if read previously during this loop.
Definition pca9554.h:49
void digital_write(uint8_t pin, bool value)
Helper function to write the value of a pin.
Definition pca9554.cpp:70
uint16_t output_mask_
The mask to write as output state - 1 means HIGH, 0 means LOW.
Definition pca9554.h:45
size_t reg_width_
width of registers
Definition pca9554.h:41
float get_loop_priority() const override
Definition pca9554.cpp:131
uint16_t input_mask_
The state of the actual input pin states - 1 means HIGH, 0 means LOW.
Definition pca9554.h:47
void pin_mode(uint8_t pin, gpio::Flags flags)
Helper function to set the pin mode of a pin.
Definition pca9554.cpp:79
uint16_t config_mask_
Mask for the pin config - 1 means OUTPUT, 0 means INPUT.
Definition pca9554.h:43
void loop() override
Poll for input changes periodically.
Definition pca9554.cpp:40
void setup() override
Check i2c availability and setup masks.
Definition pca9554.cpp:15
bool digital_read(uint8_t pin)
Helper function to read the value of a pin.
Definition pca9554.cpp:56
esphome::i2c::ErrorCode last_error_
Storage for last I2C error seen.
Definition pca9554.h:51
void pin_mode(gpio::Flags flags) override
Definition pca9554.cpp:134
std::string dump_summary() const override
Definition pca9554.cpp:137
PCA9554Component * parent_
Definition pca9554.h:71
void digital_write(bool value) override
Definition pca9554.cpp:136
@ FLAG_OUTPUT
Definition gpio.h:19
@ FLAG_INPUT
Definition gpio.h:18
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:13
const uint8_t INPUT_REG
Definition pca9554.cpp:8
const uint8_t INVERT_REG
Definition pca9554.cpp:10
const uint8_t CONFIG_REG
Definition pca9554.cpp:11
const uint8_t OUTPUT_REG
Definition pca9554.cpp:9
const float IO
For components that represent GPIO pins like PCF8573.
Definition component.cpp:17
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7