TCA6408 Example for DevTest

This commit is contained in:
Dave Williams | DitroniX | G8PUO
2026-06-13 08:19:00 +01:00
parent 94a348d55e
commit 7e658807e8
7 changed files with 504 additions and 1 deletions
+34
View File
@@ -0,0 +1,34 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, gpio
from esphome.const import CONF_ID, CONF_ADDRESS, CONF_INTERRUPT_PIN
DEPENDENCIES = ["i2c"]
tca6408_ns = cg.esphome_ns.namespace("tca6408")
TCA6408Component = tca6408_ns.class_("TCA6408Component", cg.Component, i2c.I2CDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(TCA6408Component),
cv.Optional(CONF_ADDRESS, default=0x20): cv.i2c_address,
cv.Optional(CONF_INTERRUPT_PIN): cv.All(
gpio.validate_gpio_pin("internal"),
),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema())
)
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_address(config[CONF_ADDRESS]))
if CONF_INTERRUPT_PIN in config:
pin = await gpio.register_gpio_pin(var, config[CONF_INTERRUPT_PIN])
cg.add(var.set_interrupt_pin(pin))
+165
View File
@@ -0,0 +1,165 @@
#include "tca6408.h"
#include "esphome/core/log.h"
namespace esphome {
namespace tca6408 {
static const char *const TAG = "tca6408";
static const uint8_t TCA6408_INPUT_PORT = 0x00;
static const uint8_t TCA6408_OUTPUT_PORT = 0x01;
static const uint8_t TCA6408_POLARITY_PORT = 0x02;
static const uint8_t TCA6408_CONFIG_PORT = 0x03;
void TCA6408Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up TCA6408...");
if (this->write_register(TCA6408_POLARITY_PORT, 0x00) != i2c::ERROR_OK ||
this->write_register(TCA6408_CONFIG_PORT, this->mode_mask_) != i2c::ERROR_OK ||
!this->read_gpio_outputs_()) {
this->mark_failed();
return;
}
if (this->interrupt_pin_ != nullptr) {
this->interrupt_pin_->setup();
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
this->interrupt_pin_->attach_interrupt(&TCA6408Component::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
this->set_invalidate_on_read_(false);
ESP_LOGCONFIG(TAG, " Interrupt mode enabled");
} else {
this->enable_loop();
ESP_LOGCONFIG(TAG, " Polling mode enabled");
}
}
void IRAM_ATTR TCA6408Component::gpio_intr(TCA6408Component *arg) {
arg->interrupt_triggered_ = true;
arg->enable_loop_soon_any_context();
}
void TCA6408Component::loop() {
if (this->interrupt_triggered_) {
this->interrupt_triggered_ = false;
this->digital_read_hw(0);
this->reset_pin_cache_();
if (this->interrupt_pin_ && this->interrupt_pin_->digital_read()) {
this->disable_loop();
}
}
}
void TCA6408Component::dump_config() {
ESP_LOGCONFIG(TAG, "TCA6408:");
LOG_I2C_DEVICE(this);
ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
}
bool TCA6408Component::read_gpio_outputs_() {
if (this->is_failed()) return false;
uint8_t data;
if (this->read_register(TCA6408_OUTPUT_PORT, &data) != i2c::ERROR_OK) {
this->status_set_warning();
return false;
}
this->output_mask_ = data;
this->status_clear_warning();
return true;
}
bool TCA6408Component::digital_read_hw(uint8_t pin) {
if (this->is_failed()) return false;
uint8_t data = 0;
if (this->read_register(TCA6408_INPUT_PORT, &data) != i2c::ERROR_OK) {
this->status_set_warning();
return false;
}
this->input_mask_ = data;
this->status_clear_warning();
return true;
}
void TCA6408Component::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_register(TCA6408_OUTPUT_PORT, this->output_mask_) != i2c::ERROR_OK) {
this->status_set_warning();
} else {
this->status_clear_warning();
}
}
void TCA6408Component::pin_mode(uint8_t pin, gpio::Flags flags) {
if (flags & gpio::FLAG_OUTPUT) {
this->mode_mask_ &= ~(1 << pin);
} else {
this->mode_mask_ |= (1 << pin);
if (this->interrupt_pin_ == nullptr) {
this->enable_loop();
}
}
this->write_register(TCA6408_CONFIG_PORT, this->mode_mask_);
}
bool TCA6408Component::digital_read_cache(uint8_t pin) {
bool value = (this->input_mask_ & (1 << pin)) != 0;
if (this->polarity_mask_ & (1 << pin)) {
value = !value;
}
return value;
}
void TCA6408Component::set_polarity_inversion(uint8_t pin, bool inverted) {
if (inverted) {
this->polarity_mask_ |= (1 << pin);
} else {
this->polarity_mask_ &= ~(1 << pin);
}
this->write_register(TCA6408_POLARITY_PORT, this->polarity_mask_);
}
// ==================== TCA6408GPIOPin ====================
void TCA6408GPIOPin::setup() {
this->pin_mode(this->flags_);
if (this->inverted_) {
this->parent_->set_polarity_inversion(this->pin_, true);
}
}
void TCA6408GPIOPin::set_inverted(bool inverted) {
this->inverted_ = inverted;
if (this->parent_ != nullptr) {
this->parent_->set_polarity_inversion(this->pin_, inverted);
}
}
void TCA6408GPIOPin::pin_mode(gpio::Flags flags) {
this->flags_ = flags;
this->parent_->pin_mode(this->pin_, flags);
}
bool TCA6408GPIOPin::digital_read() {
return this->parent_->digital_read(this->pin_);
}
void TCA6408GPIOPin::digital_write(bool value) {
this->parent_->digital_write(this->pin_, value);
}
size_t TCA6408GPIOPin::dump_summary(char *buffer, size_t len) const {
return snprintf(buffer, len, "%u via TCA6408", this->pin_);
}
} // namespace tca6408
} // namespace esphome
+62
View File
@@ -0,0 +1,62 @@
#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 tca6408 {
class TCA6408Component : public Component,
public i2c::I2CDevice,
public gpio_expander::CachedGpioExpander<uint8_t, 8> {
public:
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::IO; }
void set_address(uint8_t address) { this->address_ = address; }
void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; }
void set_polarity_inversion(uint8_t pin, bool inverted);
protected:
static void IRAM_ATTR gpio_intr(TCA6408Component *arg);
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;
void pin_mode(uint8_t pin, gpio::Flags flags) override;
uint8_t mode_mask_{0xFF}; // 1 = input, 0 = output
uint8_t output_mask_{0x00};
uint8_t input_mask_{0x00};
uint8_t polarity_mask_{0x00}; // 1 = inverted
InternalGPIOPin *interrupt_pin_{nullptr};
bool interrupt_triggered_{false};
};
class TCA6408GPIOPin : public gpio::GPIOPin, public Parented<TCA6408Component> {
public:
void setup() override;
void pin_mode(gpio::Flags flags) override;
bool digital_read() override;
void digital_write(bool value) override;
size_t dump_summary(char *buffer, size_t len) const override;
void set_pin(uint8_t pin) { pin_ = pin; }
void set_inverted(bool inverted);
void set_flags(gpio::Flags flags) { flags_ = flags; }
gpio::Flags get_flags() const override { return flags_; }
protected:
uint8_t pin_{0};
bool inverted_{false};
gpio::Flags flags_{0};
};
} // namespace tca6408
} // namespace esphome
+13 -1
View File
@@ -1,4 +1,16 @@
## Home Assistant - ESPHome Components
These ESPHome Components are under development and are subject to change as feedback is provided from the community.
The components have been created in order to assist users of DitroniX boards and ESPHome development. Hopefully these will eventually be incorporated within HA/ESPHome.
[![DitroniX Wiki Pages](https://github.com/DitroniX/Home-Assistant-Dev/blob/main/Datasheets%20and%20Information/GitHub%20WiKi.png?raw=true)](https://github.com/DitroniX/Home-Assistant-Dev/wiki)
[![DitroniX Discussions](https://github.com/DitroniX/DitroniX/blob/main/Files/GitHub%20Discussions.png?raw=true)](https://github.com/DitroniX/Home-Assistant-Dev/discussions)
- [**ATM90E36**](https://github.com/DitroniX/Home-Assistant-Dev/tree/main/components/ATM90E36 "ATM90E36")
- This is for use on the [EPEM ATM90E36](https://github.com/DitroniX/EPEM-Ethernet-Power-Energy-Monitor) Ethernet Power Energy Monitor
- This is for use on the ATM90E36 based Mains Energy Monitors
- [EPEM](https://github.com/DitroniX/EPEM-Ethernet-Power-Energy-Monitor)
- [IPEM Plus](https://github.com/DitroniX/IPEM-Plus-ESP32-C5-LoRa-LoRaWAN-IoT-Mains-Power-Energy-Monitor)
- [**TCA6408**](https://github.com/DitroniX/Home-Assistant-Dev/tree/main/components/TCA6408"TCA6408")
- 260612 - This is a test component and is work in progress
- [WREN](https://github.com/DitroniX/WREN-Recliner-Linear-Motor-Controller-IoT-Home-Automation)