mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
[pi4ioe5v6408] Add new IO Expander (#8888)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
parent
2289073a1e
commit
53e9ffe656
@ -332,6 +332,7 @@ esphome/components/pca6416a/* @Mat931
|
|||||||
esphome/components/pca9554/* @clydebarrow @hwstar
|
esphome/components/pca9554/* @clydebarrow @hwstar
|
||||||
esphome/components/pcf85063/* @brogon
|
esphome/components/pcf85063/* @brogon
|
||||||
esphome/components/pcf8563/* @KoenBreeman
|
esphome/components/pcf8563/* @KoenBreeman
|
||||||
|
esphome/components/pi4ioe5v6408/* @jesserockz
|
||||||
esphome/components/pid/* @OttoWinter
|
esphome/components/pid/* @OttoWinter
|
||||||
esphome/components/pipsolar/* @andreashergert1984
|
esphome/components/pipsolar/* @andreashergert1984
|
||||||
esphome/components/pm1006/* @habbie
|
esphome/components/pm1006/* @habbie
|
||||||
|
84
esphome/components/pi4ioe5v6408/__init__.py
Normal file
84
esphome/components/pi4ioe5v6408/__init__.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_INPUT,
|
||||||
|
CONF_INVERTED,
|
||||||
|
CONF_MODE,
|
||||||
|
CONF_NUMBER,
|
||||||
|
CONF_OUTPUT,
|
||||||
|
CONF_PULLDOWN,
|
||||||
|
CONF_PULLUP,
|
||||||
|
CONF_RESET,
|
||||||
|
)
|
||||||
|
|
||||||
|
AUTO_LOAD = ["gpio_expander"]
|
||||||
|
CODEOWNERS = ["@jesserockz"]
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
|
||||||
|
pi4ioe5v6408_ns = cg.esphome_ns.namespace("pi4ioe5v6408")
|
||||||
|
PI4IOE5V6408Component = pi4ioe5v6408_ns.class_(
|
||||||
|
"PI4IOE5V6408Component", cg.Component, i2c.I2CDevice
|
||||||
|
)
|
||||||
|
PI4IOE5V6408GPIOPin = pi4ioe5v6408_ns.class_("PI4IOE5V6408GPIOPin", cg.GPIOPin)
|
||||||
|
|
||||||
|
CONF_PI4IOE5V6408 = "pi4ioe5v6408"
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.declare_id(PI4IOE5V6408Component),
|
||||||
|
cv.Optional(CONF_RESET, default=True): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(i2c.i2c_device_schema(0x43))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
cg.add(var.set_reset(config[CONF_RESET]))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_mode(value):
|
||||||
|
if not (value[CONF_INPUT] or value[CONF_OUTPUT]):
|
||||||
|
raise cv.Invalid("Mode must be either input or output")
|
||||||
|
if value[CONF_INPUT] and value[CONF_OUTPUT]:
|
||||||
|
raise cv.Invalid("Mode must be either input or output")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
PI4IOE5V6408_PIN_SCHEMA = pins.gpio_base_schema(
|
||||||
|
PI4IOE5V6408GPIOPin,
|
||||||
|
cv.int_range(min=0, max=7),
|
||||||
|
modes=[
|
||||||
|
CONF_INPUT,
|
||||||
|
CONF_OUTPUT,
|
||||||
|
CONF_PULLUP,
|
||||||
|
CONF_PULLDOWN,
|
||||||
|
],
|
||||||
|
mode_validator=validate_mode,
|
||||||
|
).extend(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_PI4IOE5V6408): cv.use_id(PI4IOE5V6408Component),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pins.PIN_SCHEMA_REGISTRY.register(CONF_PI4IOE5V6408, PI4IOE5V6408_PIN_SCHEMA)
|
||||||
|
async def pi4ioe5v6408_pin_schema(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_parented(var, config[CONF_PI4IOE5V6408])
|
||||||
|
|
||||||
|
cg.add(var.set_pin(config[CONF_NUMBER]))
|
||||||
|
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||||
|
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||||
|
return var
|
171
esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp
Normal file
171
esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
#include "pi4ioe5v6408.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pi4ioe5v6408 {
|
||||||
|
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_DEVICE_ID = 0x01;
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_IO_DIR = 0x03;
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_OUT_SET = 0x05;
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_OUT_HIGH_IMPEDENCE = 0x07;
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_IN_DEFAULT_STATE = 0x09;
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_PULL_ENABLE = 0x0B;
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_PULL_SELECT = 0x0D;
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_IN_STATE = 0x0F;
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_INTERRUPT_ENABLE_MASK = 0x11;
|
||||||
|
static const uint8_t PI4IOE5V6408_REGISTER_INTERRUPT_STATUS = 0x13;
|
||||||
|
|
||||||
|
static const char *const TAG = "pi4ioe5v6408";
|
||||||
|
|
||||||
|
void PI4IOE5V6408Component::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Running setup");
|
||||||
|
if (this->reset_) {
|
||||||
|
this->reg(PI4IOE5V6408_REGISTER_DEVICE_ID) |= 0b00000001;
|
||||||
|
this->reg(PI4IOE5V6408_REGISTER_OUT_HIGH_IMPEDENCE) = 0b00000000;
|
||||||
|
} else {
|
||||||
|
if (!this->read_gpio_modes_()) {
|
||||||
|
this->mark_failed();
|
||||||
|
ESP_LOGE(TAG, "Failed to read GPIO modes");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->read_gpio_outputs_()) {
|
||||||
|
this->mark_failed();
|
||||||
|
ESP_LOGE(TAG, "Failed to read GPIO outputs");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void PI4IOE5V6408Component::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "PI4IOE5V6408:");
|
||||||
|
LOG_I2C_DEVICE(this)
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void PI4IOE5V6408Component::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||||
|
if (flags & gpio::FLAG_OUTPUT) {
|
||||||
|
// Set mode mask bit
|
||||||
|
this->mode_mask_ |= 1 << pin;
|
||||||
|
} else if (flags & gpio::FLAG_INPUT) {
|
||||||
|
// Clear mode mask bit
|
||||||
|
this->mode_mask_ &= ~(1 << pin);
|
||||||
|
if (flags & gpio::FLAG_PULLUP) {
|
||||||
|
this->pull_up_down_mask_ |= 1 << pin;
|
||||||
|
this->pull_enable_mask_ |= 1 << pin;
|
||||||
|
} else if (flags & gpio::FLAG_PULLDOWN) {
|
||||||
|
this->pull_up_down_mask_ &= ~(1 << pin);
|
||||||
|
this->pull_enable_mask_ |= 1 << pin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Write GPIO to enable input mode
|
||||||
|
this->write_gpio_modes_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PI4IOE5V6408Component::loop() { this->reset_pin_cache_(); }
|
||||||
|
|
||||||
|
bool PI4IOE5V6408Component::read_gpio_outputs_() {
|
||||||
|
if (this->is_failed())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8_t data;
|
||||||
|
if (!this->read_byte(PI4IOE5V6408_REGISTER_OUT_SET, &data)) {
|
||||||
|
this->status_set_warning("Failed to read output register");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->output_mask_ = data;
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PI4IOE5V6408Component::read_gpio_modes_() {
|
||||||
|
if (this->is_failed())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8_t data;
|
||||||
|
if (!this->read_byte(PI4IOE5V6408_REGISTER_IO_DIR, &data)) {
|
||||||
|
this->status_set_warning("Failed to read GPIO modes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||||
|
ESP_LOGV(TAG, "Read GPIO modes: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(data));
|
||||||
|
#endif
|
||||||
|
this->mode_mask_ = data;
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PI4IOE5V6408Component::digital_read_hw(uint8_t pin) {
|
||||||
|
if (this->is_failed())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8_t data;
|
||||||
|
if (!this->read_byte(PI4IOE5V6408_REGISTER_IN_STATE, &data)) {
|
||||||
|
this->status_set_warning("Failed to read GPIO state");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->input_mask_ = data;
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PI4IOE5V6408Component::digital_write_hw(uint8_t pin, bool value) {
|
||||||
|
if (this->is_failed())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
this->output_mask_ |= (1 << pin);
|
||||||
|
} else {
|
||||||
|
this->output_mask_ &= ~(1 << pin);
|
||||||
|
}
|
||||||
|
if (!this->write_byte(PI4IOE5V6408_REGISTER_OUT_SET, this->output_mask_)) {
|
||||||
|
this->status_set_warning("Failed to write output register");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||||
|
ESP_LOGV(TAG, "Wrote GPIO output: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(this->output_mask_));
|
||||||
|
#endif
|
||||||
|
this->status_clear_warning();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PI4IOE5V6408Component::write_gpio_modes_() {
|
||||||
|
if (this->is_failed())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!this->write_byte(PI4IOE5V6408_REGISTER_IO_DIR, this->mode_mask_)) {
|
||||||
|
this->status_set_warning("Failed to write GPIO modes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this->write_byte(PI4IOE5V6408_REGISTER_PULL_SELECT, this->pull_up_down_mask_)) {
|
||||||
|
this->status_set_warning("Failed to write GPIO pullup/pulldown");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this->write_byte(PI4IOE5V6408_REGISTER_PULL_ENABLE, this->pull_enable_mask_)) {
|
||||||
|
this->status_set_warning("Failed to write GPIO pull enable");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||||
|
ESP_LOGV(TAG,
|
||||||
|
"Wrote GPIO modes: 0b" BYTE_TO_BINARY_PATTERN "\n"
|
||||||
|
"Wrote GPIO pullup/pulldown: 0b" BYTE_TO_BINARY_PATTERN "\n"
|
||||||
|
"Wrote GPIO pull enable: 0b" BYTE_TO_BINARY_PATTERN,
|
||||||
|
BYTE_TO_BINARY(this->mode_mask_), BYTE_TO_BINARY(this->pull_up_down_mask_),
|
||||||
|
BYTE_TO_BINARY(this->pull_enable_mask_));
|
||||||
|
#endif
|
||||||
|
this->status_clear_warning();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PI4IOE5V6408Component::digital_read_cache(uint8_t pin) { return (this->input_mask_ & (1 << pin)); }
|
||||||
|
|
||||||
|
float PI4IOE5V6408Component::get_setup_priority() const { return setup_priority::IO; }
|
||||||
|
|
||||||
|
void PI4IOE5V6408GPIOPin::setup() { this->pin_mode(this->flags_); }
|
||||||
|
void PI4IOE5V6408GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
|
||||||
|
bool PI4IOE5V6408GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||||
|
void PI4IOE5V6408GPIOPin::digital_write(bool value) {
|
||||||
|
this->parent_->digital_write(this->pin_, value != this->inverted_);
|
||||||
|
}
|
||||||
|
std::string PI4IOE5V6408GPIOPin::dump_summary() const { return str_sprintf("%u via PI4IOE5V6408", this->pin_); }
|
||||||
|
|
||||||
|
} // namespace pi4ioe5v6408
|
||||||
|
} // namespace esphome
|
70
esphome/components/pi4ioe5v6408/pi4ioe5v6408.h
Normal file
70
esphome/components/pi4ioe5v6408/pi4ioe5v6408.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/gpio_expander/cached_gpio.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace pi4ioe5v6408 {
|
||||||
|
class PI4IOE5V6408Component : public Component,
|
||||||
|
public i2c::I2CDevice,
|
||||||
|
public gpio_expander::CachedGpioExpander<uint8_t, 8> {
|
||||||
|
public:
|
||||||
|
PI4IOE5V6408Component() = default;
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
void dump_config() override;
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
|
/// Indicate if the component should reset the state during setup
|
||||||
|
void set_reset(bool reset) { this->reset_ = reset; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// Mask for the pin mode - 1 means output, 0 means input
|
||||||
|
uint8_t mode_mask_{0x00};
|
||||||
|
/// The mask to write as output state - 1 means HIGH, 0 means LOW
|
||||||
|
uint8_t output_mask_{0x00};
|
||||||
|
/// The state read in digital_read_hw - 1 means HIGH, 0 means LOW
|
||||||
|
uint8_t input_mask_{0x00};
|
||||||
|
/// The mask to write as input buffer state - 1 means enabled, 0 means disabled
|
||||||
|
uint8_t pull_enable_mask_{0x00};
|
||||||
|
/// The mask to write as pullup state - 1 means pullup, 0 means pulldown
|
||||||
|
uint8_t pull_up_down_mask_{0x00};
|
||||||
|
|
||||||
|
bool reset_{true};
|
||||||
|
|
||||||
|
bool read_gpio_modes_();
|
||||||
|
bool write_gpio_modes_();
|
||||||
|
bool read_gpio_outputs_();
|
||||||
|
};
|
||||||
|
|
||||||
|
class PI4IOE5V6408GPIOPin : public GPIOPin, public Parented<PI4IOE5V6408Component> {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void pin_mode(gpio::Flags flags) override;
|
||||||
|
bool digital_read() override;
|
||||||
|
void digital_write(bool value) override;
|
||||||
|
std::string dump_summary() const override;
|
||||||
|
|
||||||
|
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||||
|
void set_inverted(bool inverted) { this->inverted_ = inverted; }
|
||||||
|
void set_flags(gpio::Flags flags) { this->flags_ = flags; }
|
||||||
|
|
||||||
|
gpio::Flags get_flags() const override { return this->flags_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t pin_;
|
||||||
|
bool inverted_;
|
||||||
|
gpio::Flags flags_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pi4ioe5v6408
|
||||||
|
} // namespace esphome
|
22
tests/components/pi4ioe5v6408/common.yaml
Normal file
22
tests/components/pi4ioe5v6408/common.yaml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
i2c:
|
||||||
|
id: i2c_pi4ioe5v6408
|
||||||
|
sda: ${i2c_sda}
|
||||||
|
scl: ${i2c_scl}
|
||||||
|
|
||||||
|
pi4ioe5v6408:
|
||||||
|
id: pi4ioe1
|
||||||
|
address: 0x44
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: gpio
|
||||||
|
id: switch1
|
||||||
|
pin:
|
||||||
|
pi4ioe5v6408: pi4ioe1
|
||||||
|
number: 0
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
id: sensor1
|
||||||
|
pin:
|
||||||
|
pi4ioe5v6408: pi4ioe1
|
||||||
|
number: 1
|
5
tests/components/pi4ioe5v6408/test.esp32-ard.yaml
Normal file
5
tests/components/pi4ioe5v6408/test.esp32-ard.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
i2c_sda: GPIO21
|
||||||
|
i2c_scl: GPIO22
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
5
tests/components/pi4ioe5v6408/test.esp32-idf.yaml
Normal file
5
tests/components/pi4ioe5v6408/test.esp32-idf.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
i2c_sda: GPIO21
|
||||||
|
i2c_scl: GPIO22
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
5
tests/components/pi4ioe5v6408/test.rp2040-ard.yaml
Normal file
5
tests/components/pi4ioe5v6408/test.rp2040-ard.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
i2c_sda: GPIO4
|
||||||
|
i2c_scl: GPIO5
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
Loading…
x
Reference in New Issue
Block a user