ESPHome 2025.6.2
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, "Running setup");
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,
49 "PCA9554:\n"
50 " I/O Pins: %d",
51 this->pin_count_);
52 LOG_I2C_DEVICE(this)
53 if (this->is_failed()) {
54 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
55 }
56}
57
59 // Note: We want to try and avoid doing any I2C bus read transactions here
60 // to conserve I2C bus bandwidth. So what we do is check to see if we
61 // have seen a read during the time esphome is running this loop. If we have,
62 // we do an I2C bus transaction to get the latest value. If we haven't
63 // we return a cached value which was read at the time loop() was called.
64 if (this->was_previously_read_ & (1 << pin))
65 this->read_inputs_(); // Force a read of a new value
66 // Indicate we saw a read request for this pin in case a
67 // read happens later in the same loop.
68 this->was_previously_read_ |= (1 << pin);
69 return this->input_mask_ & (1 << pin);
70}
71
72void PCA9554Component::digital_write(uint8_t pin, bool value) {
73 if (value) {
74 this->output_mask_ |= (1 << pin);
75 } else {
76 this->output_mask_ &= ~(1 << pin);
77 }
78 this->write_register_(OUTPUT_REG, this->output_mask_);
79}
80
81void PCA9554Component::pin_mode(uint8_t pin, gpio::Flags flags) {
82 if (flags == gpio::FLAG_INPUT) {
83 // Clear mode mask bit
84 this->config_mask_ &= ~(1 << pin);
85 } else if (flags == gpio::FLAG_OUTPUT) {
86 // Set mode mask bit
87 this->config_mask_ |= 1 << pin;
88 }
89 this->write_register_(CONFIG_REG, ~this->config_mask_);
90}
91
93 uint8_t inputs[2];
94
95 if (this->is_failed()) {
96 ESP_LOGD(TAG, "Device marked failed");
97 return false;
98 }
99
100 this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_, true);
101 if (this->last_error_ != i2c::ERROR_OK) {
102 this->status_set_warning();
103 ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_);
104 return false;
105 }
106 this->status_clear_warning();
107 this->input_mask_ = inputs[0];
108 if (this->reg_width_ == 2) {
109 this->input_mask_ |= inputs[1] << 8;
110 }
111 return true;
112}
113
114bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) {
115 uint8_t outputs[2];
116 outputs[0] = (uint8_t) value;
117 outputs[1] = (uint8_t) (value >> 8);
118 this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_, true);
119 if (this->last_error_ != i2c::ERROR_OK) {
120 this->status_set_warning();
121 ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_);
122 return false;
123 }
124
125 this->status_clear_warning();
126 return true;
127}
128
130
131// Run our loop() method very early in the loop, so that we cache read values before
132// before other components call our digital_read() method.
133float PCA9554Component::get_loop_priority() const { return 9.0f; } // Just after WIFI
134
136void PCA9554GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
137bool PCA9554GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
138void PCA9554GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
139std::string PCA9554GPIOPin::dump_summary() const {
140 char buffer[32];
141 snprintf(buffer, sizeof(buffer), "%u via PCA9554", pin_);
142 return buffer;
143}
144
145} // namespace pca9554
146} // 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:129
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:114
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:72
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:133
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:81
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:58
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:136
std::string dump_summary() const override
Definition pca9554.cpp:139
PCA9554Component * parent_
Definition pca9554.h:71
void digital_write(bool value) override
Definition pca9554.cpp:138
@ 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:18
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7