PCA9535 / PCA9555 I/O Expander
The PCA9535 (and the register-identical PCA9555) is a 16-bit I2C GPIO expander with two 8-bit ports. Each pin can be configured as an input or an output, with optional input polarity inversion. The PCA9535 additionally provides an open-drain interrupt output.
The espp::Pca9535 component allows the user to read the input pins, drive the output pins, and configure per-port direction and polarity over I2C.
API Reference
Header File
Classes
-
class Pca9535 : public espp::BasePeripheral<>
Class for communicating with and controlling a PCA9535 / PCA9555 16-bit I2C GPIO expander. The PCA9535 and PCA9555 share an identical register map; the PCA9535 additionally provides an open-drain interrupt output, while the PCA9555 is otherwise identical. A single driver therefore covers both parts.
The device exposes two 8-bit ports (Port 0 and Port 1), each of which can be independently configured for input or output, and each of which supports input polarity inversion.
PCA9535 Example
// make the I2C that we'll use to communicate espp::I2c i2c({ .port = I2C_NUM_0, .sda_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO, .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO, }); std::error_code ec; auto pca9535_device = i2c.add_device<uint8_t>({.device_address = espp::Pca9535::DEFAULT_ADDRESS, .timeout_ms = static_cast<int>(i2c.config().timeout_ms), .scl_speed_hz = i2c.config().clk_speed, .log_level = espp::Logger::Verbosity::WARN}, ec); if (!pca9535_device) { fmt::print("PCA9535 I2C device initialization failed: {}\n", ec.message()); return; } // now make the pca9535 which handles GPIO. Port 0 is all inputs, and Port 1 // is all outputs. espp::Pca9535 pca9535({.port_0_direction_mask = 0xFF, // all inputs on port 0 .port_1_direction_mask = 0x00, // all outputs on port 1 .write = espp::make_i2c_addressed_write(pca9535_device), .read_register = espp::make_i2c_addressed_read_register(pca9535_device), .log_level = espp::Logger::Verbosity::WARN}); // and finally, make the task to periodically poll the pca9535 and print the // state. NOTE: the Pca9535 does not internally manage its own state update, // so whatever rate we use here is the rate at which the state will update. auto task_fn = [&pca9535](std::mutex &m, std::condition_variable &cv) { static auto start = std::chrono::high_resolution_clock::now(); static uint8_t output = 0; auto now = std::chrono::high_resolution_clock::now(); auto seconds = std::chrono::duration<float>(now - start).count(); std::error_code ec; // read the inputs on port 0 auto p0_pins = pca9535.get_pins(espp::Pca9535::Port::PORT0, ec); if (ec) { fmt::print("get_pins failed: {}\n", ec.message()); } else { // toggle the output on port 1 output = ~output; pca9535.set_pins(espp::Pca9535::Port::PORT1, output, ec); if (ec) { fmt::print("set_pins failed: {}\n", ec.message()); } else { fmt::print("{:.3f}, {:#x}, {:#x}\n", seconds, p0_pins, output); } } // Always sleep (even after an error) so a persistently-failing device does // not spin this task in a tight loop. Sleeping this way also lets the sleep // exit early when the task is being stopped / destroyed. { std::unique_lock<std::mutex> lk(m); cv.wait_for(lk, 500ms); } // don't want to stop the task return false; }; auto task = espp::Task({.callback = task_fn, .task_config = { .name = "Pca9535 Task", .stack_size_bytes = 5 * 1024, }, .log_level = espp::Logger::Verbosity::WARN}); fmt::print("%time(s), port_0 pins, port_1 output\n"); task.start();
Public Types
-
enum class Port
The two GPIO ports the PCA9535 / PCA9555 has.
Values:
-
enumerator PORT0
Port 0.
-
enumerator PORT1
Port 1.
-
enumerator PORT0
-
typedef std::function<bool(uint8_t)> probe_fn
Function to probe the peripheral
- Param address:
The address to probe
- Return:
True if the peripheral is found at the given address
Public Functions
-
inline explicit Pca9535(const Config &config)
Construct the Pca9535 and configure it.
- Parameters:
config – Config structure for configuring the PCA9535 / PCA9555
-
inline void initialize(std::error_code &ec)
Initialize the device.
Writes the direction (Config) registers and the polarity inversion registers for both ports from the configured masks.
- Parameters:
ec – Error code to set if there is an error.
-
inline uint8_t get_pins(Port port, std::error_code &ec)
Read the pin values on the provided port.
- Parameters:
port – The Port for which to read the pins.
ec – Error code to set if there is an error.
- Returns:
The pin values as an 8 bit mask.
-
inline uint16_t get_pins(std::error_code &ec)
Read the pin values on both Port 0 and Port 1.
- Parameters:
ec – Error code to set if an error occurs.
- Returns:
The pin values as a 16 bit mask (Port 0 low byte, Port 1 high byte).
-
inline void set_pins(Port port, uint8_t output, std::error_code &ec)
Set the pin values on the provided port.
- Parameters:
port – The Port for which to set the pin outputs.
output – The pin values as an 8 bit mask to set.
ec – Error code to set if there is an error.
-
inline uint8_t get_output(Port port, std::error_code &ec)
Read back the output register for the provided port.
- Parameters:
port – The Port for which to read the output register.
ec – Error code to set if there is an error.
- Returns:
The output register value as an 8 bit mask.
-
inline void set_direction(Port port, uint8_t mask, std::error_code &ec)
Set the i/o direction for the pins according to mask.
Note
For the PCA9535 / PCA9555 the direction (Config) register uses the convention 1 = input, 0 = output. This differs from some other expanders, so take care when porting masks between drivers.
- Parameters:
port – The port associated with the provided pin mask.
mask – The mask indicating direction (1 = input, 0 = output).
ec – Error code to set if there is an error.
-
inline uint8_t get_direction(Port port, std::error_code &ec)
Read the direction (configuration) register for a port.
- Parameters:
port – The Port whose direction register to read.
ec – The error code, set on failure.
- Returns:
The direction mask (1 = input, 0 = output). Useful for a read-modify-write when only some pins should change direction.
-
inline void set_polarity_inversion(Port port, uint8_t mask, std::error_code &ec)
Set the input polarity inversion for the pins according to mask.
- Parameters:
port – The port associated with the provided polarity mask.
mask – Polarity mask for the pins, 1 -> invert the input pin value.
ec – Error code to set if there is an error.
-
inline bool probe(std::error_code &ec) const
Probe the peripheral
Note
This function is thread safe
Note
If the probe function is not set, this function will return false and set the error code to operation_not_supported
Note
This function is only available if UseAddress is true
- Parameters:
ec – The error code to set if there is an error
- Returns:
True if the peripheral is found
-
inline void set_address(uint8_t address)
Set the address of the peripheral
Note
This function is thread safe
Note
This function is only available if UseAddress is true
- Parameters:
address – The address of the peripheral
-
inline void set_probe(const probe_fn &probe)
Set the probe function
Note
This function is thread safe
Note
This should rarely be used, as the probe function is usually set in the constructor. If you need to change the probe function, consider using the set_config function instead.
Note
This function is only available if UseAddress is true
- Parameters:
probe – The probe function
-
inline void set_write(const write_fn &write)
Set the write function
Note
This function is thread safe
Note
This should rarely be used, as the write function is usually set in the constructor. If you need to change the write function, consider using the set_config function instead.
- Parameters:
write – The write function
-
inline void set_read(const read_fn &read)
Set the read function
Note
This function is thread safe
Note
This should rarely be used, as the read function is usually set in the constructor. If you need to change the read function, consider using the set_config function instead.
- Parameters:
read – The read function
-
inline void set_read_register(const read_register_fn &read_register)
Set the read register function
Note
This function is thread safe
Note
This should rarely be used, as the read register function is usually set in the constructor. If you need to change the read register function, consider using the set_config function instead.
- Parameters:
read_register – The read register function
-
inline void set_write_then_read(const write_then_read_fn &write_then_read)
Set the write then read function
Note
This function is thread safe
Note
This should rarely be used, as the write then read function is usually set in the constructor. If you need to change the write then
- Parameters:
write_then_read – The write then read function
-
inline void set_separate_write_then_read_delay(const std::chrono::milliseconds &delay)
Set the delay between the write and read operations in write_then_read
Note
This function is thread safe
Note
This should rarely be used, as the delay is usually set in the constructor. If you need to change the delay, consider using the set_config function instead.
Note
This delay is only used if the write_then_read function is not set to a custom function and the write and read functions are separate functions.
- Parameters:
delay – The delay between the write and read operations in write_then_read
-
inline void set_config(const Config &config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters:
config – The configuration for the peripheral
-
inline void set_config(Config &&config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters:
config – The configuration for the peripheral
-
inline const Config &config() const
Get the configuration for the peripheral
- Returns:
The configuration for the peripheral
-
inline uint8_t address() const
Get the address of the peripheral
- Returns:
The address of the peripheral
-
inline const std::string &get_name() const
Get the name of the component
Note
This is the tag of the logger
- Returns:
A const reference to the name of the component
-
inline void set_log_tag(const std::string_view &tag)
Set the tag for the logger
- Parameters:
tag – The tag to use for the logger
-
inline espp::Logger::Verbosity get_log_level() const
Get the log level for the logger
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- Parameters:
level – The verbosity level to use for the logger
-
inline void set_log_verbosity(espp::Logger::Verbosity level)
Set the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls set_log_level
- Parameters:
level – The verbosity level to use for the logger
-
inline espp::Logger::Verbosity get_log_verbosity() const
Get the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls get_log_level
- Returns:
The verbosity level of the logger
-
inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
Set the rate limit for the logger
See also
Note
Only calls to the logger that have _rate_limit suffix will be rate limited
- Parameters:
rate_limit – The rate limit to use for the logger
Public Static Attributes
-
static constexpr uint8_t DEFAULT_ADDRESS = 0x20
Default I2C address (A2..A0 = 000). Valid range is 0x20 - 0x27.
-
struct Config
Configuration information for the Pca9535.
Public Members
-
uint8_t device_address = DEFAULT_ADDRESS
I2C address of this device.
-
uint8_t port_0_direction_mask = 0xFF
Direction mask (1 = input, 0 = output) for port 0. Default all inputs.
-
uint8_t port_0_polarity_mask = 0x00
Polarity inversion mask (1 = inverted) for port 0 inputs.
-
uint8_t port_1_direction_mask = 0xFF
Direction mask (1 = input, 0 = output) for port 1. Default all inputs.
-
uint8_t port_1_polarity_mask = 0x00
Polarity inversion mask (1 = inverted) for port 1 inputs.
-
BasePeripheral::write_fn write
Function to write to the device.
-
BasePeripheral::read_register_fn read_register
Function to read bytes at a register address from the device.
-
bool auto_init = true
True if the device should be initialized on construction.
-
uint8_t device_address = DEFAULT_ADDRESS
-
enum class Port