TCA6408 Example for DevTest
This commit is contained in:
@@ -5,6 +5,10 @@ This repository is for integration of DitroniX boards into Home Assistant using
|
||||
|
||||
The development and integration repository is work in progress as various projects evolve. It will also expand on route.
|
||||
|
||||
[](https://github.com/DitroniX/Home-Assistant-Dev/wiki)
|
||||
|
||||
[](https://github.com/DitroniX/Home-Assistant-Dev/discussions)
|
||||
|
||||
#### User contributions
|
||||
|
||||
User contributions to the HA integration is welcome and of course, acknowledgements would be included.
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
esphome:
|
||||
name: tca6408-node
|
||||
friendly_name: TCA6408 Node
|
||||
|
||||
esp32:
|
||||
board: esp32-c5-devkitc-1 # Change to your board (or esp32dev for S3/C3)
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
# Enable logging
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
# WiFi & API (Home Assistant)
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
|
||||
api:
|
||||
ota:
|
||||
|
||||
# ====================== I2C & TCA6408 ======================
|
||||
i2c:
|
||||
sda: GPIO2 # Adjust pins for your board
|
||||
scl: GPIO3
|
||||
scan: true
|
||||
frequency: 400kHz
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/DitroniX/Home-Assistant-Dev
|
||||
ref: main
|
||||
refresh: 0s #optional, how often to download fresh files from source, defaults to 1 day, use 0 to force updates
|
||||
components: [ TCA6408 ]
|
||||
|
||||
|
||||
tca6408:
|
||||
- id: tca6408_hub
|
||||
address: 0x20 # Common default (0x20 - 0x27 depending on ADDR pins) DitroniX boards sometimes use 0x21 as default to avoid conflicts
|
||||
interrupt_pin: GPIO6 # Recommended - connect to TCA6408 INT pin
|
||||
|
||||
# ====================== OUTPUTS (Switches) ======================
|
||||
switch:
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 0"
|
||||
id: tca_out0
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 0
|
||||
mode: OUTPUT
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 1 (Relay)"
|
||||
id: tca_out1
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 1
|
||||
mode: OUTPUT
|
||||
inverted: false
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 2"
|
||||
id: tca_out2
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 2
|
||||
mode: OUTPUT
|
||||
|
||||
# ====================== INPUTS (Binary Sensors) ======================
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
name: "TCA6408 Input 3 (Door Sensor)"
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 3
|
||||
mode: INPUT
|
||||
inverted: true # Use hardware polarity inversion if active-low
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Input 4 (Button)"
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 4
|
||||
mode: INPUT
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Input 5"
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 5
|
||||
mode: INPUT
|
||||
inverted: false
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Input 6"
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 6
|
||||
mode: INPUT
|
||||
|
||||
# Optional: Status LED or other outputs
|
||||
status_led:
|
||||
pin:
|
||||
number: GPIO2
|
||||
@@ -0,0 +1,121 @@
|
||||
esphome:
|
||||
name: tca6408-outputs
|
||||
friendly_name: TCA6408 Outputs
|
||||
|
||||
esp32:
|
||||
board: esp32-c5-devkitc-1
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
|
||||
api:
|
||||
ota:
|
||||
|
||||
# ====================== I2C & TCA6408 ======================
|
||||
i2c:
|
||||
sda: GPIO2 # Adjust pins for your board
|
||||
scl: GPIO3
|
||||
scan: true
|
||||
frequency: 400kHz
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: git
|
||||
url: https://github.com/DitroniX/Home-Assistant-Dev
|
||||
ref: main
|
||||
refresh: 0s #optional, how often to download fresh files from source, defaults to 1 day, use 0 to force updates
|
||||
components: [ TCA6408 ]
|
||||
|
||||
tca6408:
|
||||
- id: tca6408_hub
|
||||
address: 0x20 # Common default (0x20 - 0x27 depending on ADDR pins) DitroniX boards sometimes use 0x21 as default to avoid conflicts
|
||||
# interrupt_pin: GPIO6 # Optional for outputs only. Interrupt pin is commented out as not needed when using only outputs.
|
||||
|
||||
# ====================== OUTPUTS (Switches) ======================
|
||||
switch:
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 0"
|
||||
id: tca_out0
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 0
|
||||
mode: OUTPUT
|
||||
# inverted: false # Default behavior
|
||||
# inverted: true # Hardware/software inversion
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 1 (Relay)"
|
||||
id: tca_out1
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 1
|
||||
mode: OUTPUT
|
||||
# inverted: false # Default behavior
|
||||
# inverted: true # Hardware/software inversion
|
||||
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 2"
|
||||
id: tca_out2
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 2
|
||||
mode: OUTPUT
|
||||
# inverted: false # Default behavior
|
||||
# inverted: true # Hardware/software inversion
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 3"
|
||||
id: tca_out3
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 3
|
||||
mode: OUTPUT
|
||||
# inverted: false # Default behavior
|
||||
# inverted: true # Hardware/software inversion
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 4"
|
||||
id: tca_out4
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 4
|
||||
mode: OUTPUT
|
||||
# inverted: false # Default behavior
|
||||
# inverted: true # Hardware/software inversion
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 5"
|
||||
id: tca_out5
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 5
|
||||
mode: OUTPUT
|
||||
# inverted: false # Default behavior
|
||||
# inverted: true # Hardware/software inversion
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 6"
|
||||
id: tca_out6
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 6
|
||||
mode: OUTPUT
|
||||
# inverted: false # Default behavior
|
||||
# inverted: true # Hardware/software inversion
|
||||
|
||||
- platform: gpio
|
||||
name: "TCA6408 Output 7"
|
||||
id: tca_out7
|
||||
pin:
|
||||
tca6408: tca6408_hub
|
||||
number: 7
|
||||
mode: OUTPUT
|
||||
# inverted: false # Default behavior
|
||||
# inverted: true # Hardware/software inversion
|
||||
@@ -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))
|
||||
@@ -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
|
||||
@@ -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
@@ -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.
|
||||
[](https://github.com/DitroniX/Home-Assistant-Dev/wiki)
|
||||
|
||||
[](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)
|
||||
|
||||
Reference in New Issue
Block a user