diff --git a/CODEOWNERS b/CODEOWNERS index 0f22c8ace9..4fdfb21569 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -46,8 +46,13 @@ esphome/components/ledc/* @OttoWinter esphome/components/light/* @esphome/core esphome/components/logger/* @esphome/core esphome/components/max7219digit/* @rspaargaren -esphome/components/mcp23s08/* @SenexCrenshaw -esphome/components/mcp23s17/* @SenexCrenshaw +esphome/components/mcp23008/* @jesserockz +esphome/components/mcp23017/* @jesserockz +esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz +esphome/components/mcp23s17/* @SenexCrenshaw @jesserockz +esphome/components/mcp23x08_base/* @jesserockz +esphome/components/mcp23x17_base/* @jesserockz +esphome/components/mcp23xxx_base/* @jesserockz esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp9808/* @k7hpn esphome/components/network/* @esphome/core diff --git a/esphome/components/mcp23008/__init__.py b/esphome/components/mcp23008/__init__.py index 4736765e64..ae43e9ff0a 100644 --- a/esphome/components/mcp23008/__init__.py +++ b/esphome/components/mcp23008/__init__.py @@ -1,76 +1,28 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.components import i2c -from esphome.const import ( - CONF_ID, - CONF_NUMBER, - CONF_MODE, - CONF_INVERTED, - CONF_OPEN_DRAIN_INTERRUPT, -) +from esphome.components import i2c, mcp23x08_base, mcp23xxx_base +from esphome.const import CONF_ID +AUTO_LOAD = ["mcp23x08_base"] +CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["i2c"] MULTI_CONF = True mcp23008_ns = cg.esphome_ns.namespace("mcp23008") -MCP23008GPIOMode = mcp23008_ns.enum("MCP23008GPIOMode") -MCP23008_GPIO_MODES = { - "INPUT": MCP23008GPIOMode.MCP23008_INPUT, - "INPUT_PULLUP": MCP23008GPIOMode.MCP23008_INPUT_PULLUP, - "OUTPUT": MCP23008GPIOMode.MCP23008_OUTPUT, -} -MCP23008 = mcp23008_ns.class_("MCP23008", cg.Component, i2c.I2CDevice) -MCP23008GPIOPin = mcp23008_ns.class_("MCP23008GPIOPin", cg.GPIOPin) +MCP23008 = mcp23008_ns.class_("MCP23008", mcp23x08_base.MCP23X08Base, i2c.I2CDevice) CONFIG_SCHEMA = ( cv.Schema( { cv.Required(CONF_ID): cv.declare_id(MCP23008), - cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, } ) - .extend(cv.COMPONENT_SCHEMA) + .extend(mcp23xxx_base.MCP23XXX_CONFIG_SCHEMA) .extend(i2c.i2c_device_schema(0x20)) ) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - yield cg.register_component(var, config) + var = yield mcp23xxx_base.register_mcp23xxx(config) yield i2c.register_i2c_device(var, config) - cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) - - -CONF_MCP23008 = "mcp23008" -MCP23008_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23008): cv.use_id(MCP23008), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - MCP23008_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -MCP23008_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23008): cv.use_id(MCP23008), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23008_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - - -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23008, (MCP23008_OUTPUT_PIN_SCHEMA, MCP23008_INPUT_PIN_SCHEMA) -) -def mcp23008_pin_to_code(config): - parent = yield cg.get_variable(config[CONF_MCP23008]) - yield MCP23008GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) diff --git a/esphome/components/mcp23008/mcp23008.cpp b/esphome/components/mcp23008/mcp23008.cpp index 22c7322458..eb66bfc7d7 100644 --- a/esphome/components/mcp23008/mcp23008.cpp +++ b/esphome/components/mcp23008/mcp23008.cpp @@ -9,85 +9,32 @@ static const char *TAG = "mcp23008"; void MCP23008::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP23008..."); uint8_t iocon; - if (!this->read_reg_(MCP23008_IOCON, &iocon)) { + if (!this->read_reg(mcp23x08_base::MCP23X08_IOCON, &iocon)) { this->mark_failed(); return; } if (this->open_drain_ints_) { // enable open-drain interrupt pins, 3.3V-safe - this->write_reg_(MCP23008_IOCON, 0x04); + this->write_reg(mcp23x08_base::MCP23X08_IOCON, 0x04); } } -bool MCP23008::digital_read(uint8_t pin) { - uint8_t bit = pin % 8; - uint8_t reg_addr = MCP23008_GPIO; - uint8_t value = 0; - this->read_reg_(reg_addr, &value); - return value & (1 << bit); -} -void MCP23008::digital_write(uint8_t pin, bool value) { - uint8_t reg_addr = MCP23008_OLAT; - this->update_reg_(pin, value, reg_addr); -} -void MCP23008::pin_mode(uint8_t pin, uint8_t mode) { - uint8_t iodir = MCP23008_IODIR; - uint8_t gppu = MCP23008_GPPU; - switch (mode) { - case MCP23008_INPUT: - this->update_reg_(pin, true, iodir); - break; - case MCP23008_INPUT_PULLUP: - this->update_reg_(pin, true, iodir); - this->update_reg_(pin, true, gppu); - break; - case MCP23008_OUTPUT: - this->update_reg_(pin, false, iodir); - break; - default: - break; - } -} -float MCP23008::get_setup_priority() const { return setup_priority::HARDWARE; } -bool MCP23008::read_reg_(uint8_t reg, uint8_t *value) { + +void MCP23008::dump_config() { ESP_LOGCONFIG(TAG, "MCP23008:"); } + +bool MCP23008::read_reg(uint8_t reg, uint8_t *value) { if (this->is_failed()) return false; return this->read_byte(reg, value); } -bool MCP23008::write_reg_(uint8_t reg, uint8_t value) { + +bool MCP23008::write_reg(uint8_t reg, uint8_t value) { if (this->is_failed()) return false; return this->write_byte(reg, value); } -void MCP23008::update_reg_(uint8_t pin, bool pin_value, uint8_t reg_addr) { - uint8_t bit = pin % 8; - uint8_t reg_value = 0; - if (reg_addr == MCP23008_OLAT) { - reg_value = this->olat_; - } else { - this->read_reg_(reg_addr, ®_value); - } - - if (pin_value) - reg_value |= 1 << bit; - else - reg_value &= ~(1 << bit); - - this->write_reg_(reg_addr, reg_value); - - if (reg_addr == MCP23008_OLAT) { - this->olat_ = reg_value; - } -} - -MCP23008GPIOPin::MCP23008GPIOPin(MCP23008 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23008GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23008GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -bool MCP23008GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } -void MCP23008GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } } // namespace mcp23008 } // namespace esphome diff --git a/esphome/components/mcp23008/mcp23008.h b/esphome/components/mcp23008/mcp23008.h index e30a924dde..42c8e497fa 100644 --- a/esphome/components/mcp23008/mcp23008.h +++ b/esphome/components/mcp23008/mcp23008.h @@ -1,71 +1,23 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/components/mcp23x08_base/mcp23x08_base.h" #include "esphome/core/esphal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { namespace mcp23008 { -/// Modes for MCP23008 pins -enum MCP23008GPIOMode : uint8_t { - MCP23008_INPUT = INPUT, // 0x00 - MCP23008_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23008_OUTPUT = OUTPUT // 0x01 -}; - -enum MCP23008GPIORegisters { - // A side - MCP23008_IODIR = 0x00, - MCP23008_IPOL = 0x01, - MCP23008_GPINTEN = 0x02, - MCP23008_DEFVAL = 0x03, - MCP23008_INTCON = 0x04, - MCP23008_IOCON = 0x05, - MCP23008_GPPU = 0x06, - MCP23008_INTF = 0x07, - MCP23008_INTCAP = 0x08, - MCP23008_GPIO = 0x09, - MCP23008_OLAT = 0x0A, -}; - -class MCP23008 : public Component, public i2c::I2CDevice { +class MCP23008 : public mcp23x08_base::MCP23X08Base, public i2c::I2CDevice { public: MCP23008() = default; void setup() override; - - bool digital_read(uint8_t pin); - void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); - - void set_open_drain_ints(const bool value) { open_drain_ints_ = value; } - - float get_setup_priority() const override; + void dump_config() override; protected: - // read a given register - bool read_reg_(uint8_t reg, uint8_t *value); - // write a value to a given register - bool write_reg_(uint8_t reg, uint8_t value); - // update registers with given pin value. - void update_reg_(uint8_t pin, bool pin_value, uint8_t reg_a); - - uint8_t olat_{0x00}; - bool open_drain_ints_; -}; - -class MCP23008GPIOPin : public GPIOPin { - public: - MCP23008GPIOPin(MCP23008 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - - void setup() override; - void pin_mode(uint8_t mode) override; - bool digital_read() override; - void digital_write(bool value) override; - - protected: - MCP23008 *parent_; + bool read_reg(uint8_t reg, uint8_t *value) override; + bool write_reg(uint8_t reg, uint8_t value) override; }; } // namespace mcp23008 diff --git a/esphome/components/mcp23017/__init__.py b/esphome/components/mcp23017/__init__.py index dae7a6fead..a86133e232 100644 --- a/esphome/components/mcp23017/__init__.py +++ b/esphome/components/mcp23017/__init__.py @@ -1,76 +1,28 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.components import i2c -from esphome.const import ( - CONF_ID, - CONF_NUMBER, - CONF_MODE, - CONF_INVERTED, - CONF_OPEN_DRAIN_INTERRUPT, -) +from esphome.components import i2c, mcp23x17_base, mcp23xxx_base +from esphome.const import CONF_ID +AUTO_LOAD = ["mcp23x17_base"] +CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["i2c"] MULTI_CONF = True mcp23017_ns = cg.esphome_ns.namespace("mcp23017") -MCP23017GPIOMode = mcp23017_ns.enum("MCP23017GPIOMode") -MCP23017_GPIO_MODES = { - "INPUT": MCP23017GPIOMode.MCP23017_INPUT, - "INPUT_PULLUP": MCP23017GPIOMode.MCP23017_INPUT_PULLUP, - "OUTPUT": MCP23017GPIOMode.MCP23017_OUTPUT, -} -MCP23017 = mcp23017_ns.class_("MCP23017", cg.Component, i2c.I2CDevice) -MCP23017GPIOPin = mcp23017_ns.class_("MCP23017GPIOPin", cg.GPIOPin) +MCP23017 = mcp23017_ns.class_("MCP23017", mcp23x17_base.MCP23X17Base, i2c.I2CDevice) CONFIG_SCHEMA = ( cv.Schema( { cv.Required(CONF_ID): cv.declare_id(MCP23017), - cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, } ) - .extend(cv.COMPONENT_SCHEMA) + .extend(mcp23xxx_base.MCP23XXX_CONFIG_SCHEMA) .extend(i2c.i2c_device_schema(0x20)) ) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - yield cg.register_component(var, config) + var = yield mcp23xxx_base.register_mcp23xxx(config) yield i2c.register_i2c_device(var, config) - cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) - - -CONF_MCP23017 = "mcp23017" -MCP23017_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23017): cv.use_id(MCP23017), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - MCP23017_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -MCP23017_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23017): cv.use_id(MCP23017), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23017_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - - -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23017, (MCP23017_OUTPUT_PIN_SCHEMA, MCP23017_INPUT_PIN_SCHEMA) -) -def mcp23017_pin_to_code(config): - parent = yield cg.get_variable(config[CONF_MCP23017]) - yield MCP23017GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) diff --git a/esphome/components/mcp23017/mcp23017.cpp b/esphome/components/mcp23017/mcp23017.cpp index ec972668ef..0523b14d5a 100644 --- a/esphome/components/mcp23017/mcp23017.cpp +++ b/esphome/components/mcp23017/mcp23017.cpp @@ -9,90 +9,32 @@ static const char *TAG = "mcp23017"; void MCP23017::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP23017..."); uint8_t iocon; - if (!this->read_reg_(MCP23017_IOCONA, &iocon)) { + if (!this->read_reg(mcp23x17_base::MCP23X17_IOCONA, &iocon)) { this->mark_failed(); return; } if (this->open_drain_ints_) { // enable open-drain interrupt pins, 3.3V-safe - this->write_reg_(MCP23017_IOCONA, 0x04); - this->write_reg_(MCP23017_IOCONB, 0x04); + this->write_reg(mcp23x17_base::MCP23X17_IOCONA, 0x04); + this->write_reg(mcp23x17_base::MCP23X17_IOCONB, 0x04); } } -bool MCP23017::digital_read(uint8_t pin) { - uint8_t bit = pin % 8; - uint8_t reg_addr = pin < 8 ? MCP23017_GPIOA : MCP23017_GPIOB; - uint8_t value = 0; - this->read_reg_(reg_addr, &value); - return value & (1 << bit); -} -void MCP23017::digital_write(uint8_t pin, bool value) { - uint8_t reg_addr = pin < 8 ? MCP23017_OLATA : MCP23017_OLATB; - this->update_reg_(pin, value, reg_addr); -} -void MCP23017::pin_mode(uint8_t pin, uint8_t mode) { - uint8_t iodir = pin < 8 ? MCP23017_IODIRA : MCP23017_IODIRB; - uint8_t gppu = pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB; - switch (mode) { - case MCP23017_INPUT: - this->update_reg_(pin, true, iodir); - break; - case MCP23017_INPUT_PULLUP: - this->update_reg_(pin, true, iodir); - this->update_reg_(pin, true, gppu); - break; - case MCP23017_OUTPUT: - this->update_reg_(pin, false, iodir); - break; - default: - break; - } -} -float MCP23017::get_setup_priority() const { return setup_priority::IO; } -bool MCP23017::read_reg_(uint8_t reg, uint8_t *value) { + +void MCP23017::dump_config() { ESP_LOGCONFIG(TAG, "MCP23017:"); } + +bool MCP23017::read_reg(uint8_t reg, uint8_t *value) { if (this->is_failed()) return false; return this->read_byte(reg, value); } -bool MCP23017::write_reg_(uint8_t reg, uint8_t value) { +bool MCP23017::write_reg(uint8_t reg, uint8_t value) { if (this->is_failed()) return false; return this->write_byte(reg, value); } -void MCP23017::update_reg_(uint8_t pin, bool pin_value, uint8_t reg_addr) { - uint8_t bit = pin % 8; - uint8_t reg_value = 0; - if (reg_addr == MCP23017_OLATA) { - reg_value = this->olat_a_; - } else if (reg_addr == MCP23017_OLATB) { - reg_value = this->olat_b_; - } else { - this->read_reg_(reg_addr, ®_value); - } - - if (pin_value) - reg_value |= 1 << bit; - else - reg_value &= ~(1 << bit); - - this->write_reg_(reg_addr, reg_value); - - if (reg_addr == MCP23017_OLATA) { - this->olat_a_ = reg_value; - } else if (reg_addr == MCP23017_OLATB) { - this->olat_b_ = reg_value; - } -} - -MCP23017GPIOPin::MCP23017GPIOPin(MCP23017 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23017GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23017GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -bool MCP23017GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } -void MCP23017GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } } // namespace mcp23017 } // namespace esphome diff --git a/esphome/components/mcp23017/mcp23017.h b/esphome/components/mcp23017/mcp23017.h index 5656dcc58d..fd9086a492 100644 --- a/esphome/components/mcp23017/mcp23017.h +++ b/esphome/components/mcp23017/mcp23017.h @@ -1,84 +1,23 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/components/mcp23x17_base/mcp23x17_base.h" #include "esphome/core/esphal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { namespace mcp23017 { -/// Modes for MCP23017 pins -enum MCP23017GPIOMode : uint8_t { - MCP23017_INPUT = INPUT, // 0x00 - MCP23017_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23017_OUTPUT = OUTPUT // 0x01 -}; - -enum MCP23017GPIORegisters { - // A side - MCP23017_IODIRA = 0x00, - MCP23017_IPOLA = 0x02, - MCP23017_GPINTENA = 0x04, - MCP23017_DEFVALA = 0x06, - MCP23017_INTCONA = 0x08, - MCP23017_IOCONA = 0x0A, - MCP23017_GPPUA = 0x0C, - MCP23017_INTFA = 0x0E, - MCP23017_INTCAPA = 0x10, - MCP23017_GPIOA = 0x12, - MCP23017_OLATA = 0x14, - // B side - MCP23017_IODIRB = 0x01, - MCP23017_IPOLB = 0x03, - MCP23017_GPINTENB = 0x05, - MCP23017_DEFVALB = 0x07, - MCP23017_INTCONB = 0x09, - MCP23017_IOCONB = 0x0B, - MCP23017_GPPUB = 0x0D, - MCP23017_INTFB = 0x0F, - MCP23017_INTCAPB = 0x11, - MCP23017_GPIOB = 0x13, - MCP23017_OLATB = 0x15, -}; - -class MCP23017 : public Component, public i2c::I2CDevice { +class MCP23017 : public mcp23x17_base::MCP23X17Base, public i2c::I2CDevice { public: MCP23017() = default; void setup() override; - - bool digital_read(uint8_t pin); - void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); - - void set_open_drain_ints(const bool value) { open_drain_ints_ = value; } - - float get_setup_priority() const override; + void dump_config() override; protected: - // read a given register - bool read_reg_(uint8_t reg, uint8_t *value); - // write a value to a given register - bool write_reg_(uint8_t reg, uint8_t value); - // update registers with given pin value. - void update_reg_(uint8_t pin, bool pin_value, uint8_t reg_a); - - uint8_t olat_a_{0x00}; - uint8_t olat_b_{0x00}; - bool open_drain_ints_; -}; - -class MCP23017GPIOPin : public GPIOPin { - public: - MCP23017GPIOPin(MCP23017 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - - void setup() override; - void pin_mode(uint8_t mode) override; - bool digital_read() override; - void digital_write(bool value) override; - - protected: - MCP23017 *parent_; + bool read_reg(uint8_t reg, uint8_t *value) override; + bool write_reg(uint8_t reg, uint8_t value) override; }; } // namespace mcp23017 diff --git a/esphome/components/mcp23s08/__init__.py b/esphome/components/mcp23s08/__init__.py index b0e047b6ba..cfeb65de9b 100644 --- a/esphome/components/mcp23s08/__init__.py +++ b/esphome/components/mcp23s08/__init__.py @@ -1,26 +1,18 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.components import spi -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED - -CODEOWNERS = ["@SenexCrenshaw"] +from esphome.components import spi, mcp23x08_base, mcp23xxx_base +from esphome.const import CONF_ID +AUTO_LOAD = ["mcp23x08_base"] +CODEOWNERS = ["@SenexCrenshaw", "@jesserockz"] DEPENDENCIES = ["spi"] MULTI_CONF = True CONF_DEVICEADDRESS = "deviceaddress" mcp23S08_ns = cg.esphome_ns.namespace("mcp23s08") -mcp23S08GPIOMode = mcp23S08_ns.enum("MCP23S08GPIOMode") -mcp23S08_GPIO_MODES = { - "INPUT": mcp23S08GPIOMode.MCP23S08_INPUT, - "INPUT_PULLUP": mcp23S08GPIOMode.MCP23S08_INPUT_PULLUP, - "OUTPUT": mcp23S08GPIOMode.MCP23S08_OUTPUT, -} -mcp23S08 = mcp23S08_ns.class_("MCP23S08", cg.Component, spi.SPIDevice) -mcp23S08GPIOPin = mcp23S08_ns.class_("MCP23S08GPIOPin", cg.GPIOPin) +mcp23S08 = mcp23S08_ns.class_("MCP23S08", mcp23x08_base.MCP23X08Base, spi.SPIDevice) CONFIG_SCHEMA = ( cv.Schema( @@ -29,47 +21,12 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_DEVICEADDRESS, default=0): cv.uint8_t, } ) - .extend(cv.COMPONENT_SCHEMA) + .extend(mcp23xxx_base.MCP23XXX_CONFIG_SCHEMA) .extend(spi.spi_device_schema()) ) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = yield mcp23xxx_base.register_mcp23xxx(config) cg.add(var.set_device_address(config[CONF_DEVICEADDRESS])) - yield cg.register_component(var, config) yield spi.register_spi_device(var, config) - - -CONF_MCP23S08 = "mcp23s08" - -mcp23S08_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.GenerateID(CONF_MCP23S08): cv.use_id(mcp23S08), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - mcp23S08_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -mcp23S08_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.GenerateID(CONF_MCP23S08): cv.use_id(mcp23S08), - cv.Required(CONF_NUMBER): cv.int_range(0, 7), - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - mcp23S08_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - - -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23S08, (mcp23S08_OUTPUT_PIN_SCHEMA, mcp23S08_INPUT_PIN_SCHEMA) -) -def mcp23S08_pin_to_code(config): - parent = yield cg.get_variable(config[CONF_MCP23S08]) - yield mcp23S08GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) diff --git a/esphome/components/mcp23s08/mcp23s08.cpp b/esphome/components/mcp23s08/mcp23s08.cpp index 07e1808485..3bb03cb225 100644 --- a/esphome/components/mcp23s08/mcp23s08.cpp +++ b/esphome/components/mcp23s08/mcp23s08.cpp @@ -15,14 +15,18 @@ void MCP23S08::set_device_address(uint8_t device_addr) { void MCP23S08::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP23S08..."); this->spi_setup(); - this->enable(); - this->transfer_byte(MCP23S08_IODIR); - this->transfer_byte(0xFF); - for (uint8_t i = 0; i < MCP23S08_OLAT; i++) { - this->transfer_byte(0x00); - } + this->enable(); + uint8_t cmd = 0b01000000; + this->transfer_byte(cmd); + this->transfer_byte(mcp23x08_base::MCP23X08_IOCON); + this->transfer_byte(0b00011000); // Enable HAEN pins for addressing this->disable(); + + if (this->open_drain_ints_) { + // enable open-drain interrupt pins, 3.3V-safe + this->write_reg(mcp23x08_base::MCP23X08_IOCON, 0x04); + } } void MCP23S08::dump_config() { @@ -30,76 +34,6 @@ void MCP23S08::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); } -float MCP23S08::get_setup_priority() const { return setup_priority::HARDWARE; } - -bool MCP23S08::digital_read(uint8_t pin) { - if (pin > 7) { - return false; - } - uint8_t bit = pin % 8; - uint8_t reg_addr = MCP23S08_GPIO; - uint8_t value = 0; - this->read_reg(reg_addr, &value); - return value & (1 << bit); -} - -void MCP23S08::digital_write(uint8_t pin, bool value) { - if (pin > 7) { - return; - } - uint8_t reg_addr = MCP23S08_OLAT; - this->update_reg(pin, value, reg_addr); -} - -void MCP23S08::pin_mode(uint8_t pin, uint8_t mode) { - uint8_t iodir = MCP23S08_IODIR; - uint8_t gppu = MCP23S08_GPPU; - switch (mode) { - case MCP23S08_INPUT: - this->update_reg(pin, true, iodir); - break; - case MCP23S08_INPUT_PULLUP: - this->update_reg(pin, true, iodir); - this->update_reg(pin, true, gppu); - break; - case MCP23S08_OUTPUT: - this->update_reg(pin, false, iodir); - break; - default: - break; - } -} - -void MCP23S08::update_reg(uint8_t pin, bool pin_value, uint8_t reg_addr) { - uint8_t bit = pin % 8; - uint8_t reg_value = 0; - if (reg_addr == MCP23S08_OLAT) { - reg_value = this->olat_; - } else { - this->read_reg(reg_addr, ®_value); - } - - if (pin_value) - reg_value |= 1 << bit; - else - reg_value &= ~(1 << bit); - - this->write_reg(reg_addr, reg_value); - - if (reg_addr == MCP23S08_OLAT) { - this->olat_ = reg_value; - } -} - -bool MCP23S08::write_reg(uint8_t reg, uint8_t value) { - this->enable(); - this->transfer_byte(this->device_opcode_); - this->transfer_byte(reg); - this->transfer_byte(value); - this->disable(); - return true; -} - bool MCP23S08::read_reg(uint8_t reg, uint8_t *value) { uint8_t data; this->enable(); @@ -110,12 +44,14 @@ bool MCP23S08::read_reg(uint8_t reg, uint8_t *value) { return true; } -MCP23S08GPIOPin::MCP23S08GPIOPin(MCP23S08 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23S08GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23S08GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -bool MCP23S08GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } -void MCP23S08GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +bool MCP23S08::write_reg(uint8_t reg, uint8_t value) { + this->enable(); + this->transfer_byte(this->device_opcode_); + this->transfer_byte(reg); + this->transfer_byte(value); + this->disable(); + return true; +} } // namespace mcp23s08 } // namespace esphome diff --git a/esphome/components/mcp23s08/mcp23s08.h b/esphome/components/mcp23s08/mcp23s08.h index a90f89ba23..4ca02c54fc 100644 --- a/esphome/components/mcp23s08/mcp23s08.h +++ b/esphome/components/mcp23s08/mcp23s08.h @@ -1,35 +1,14 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/components/mcp23x08_base/mcp23x08_base.h" #include "esphome/core/esphal.h" #include "esphome/components/spi/spi.h" namespace esphome { namespace mcp23s08 { -/// Modes for MCP23S08 pins -enum MCP23S08GPIOMode : uint8_t { - MCP23S08_INPUT = INPUT, // 0x00 - MCP23S08_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23S08_OUTPUT = OUTPUT // 0x01 -}; - -enum MCP23S08GPIORegisters { - // A side - MCP23S08_IODIR = 0x00, - MCP23S08_IPOL = 0x01, - MCP23S08_GPINTEN = 0x02, - MCP23S08_DEFVAL = 0x03, - MCP23S08_INTCON = 0x04, - MCP23S08_IOCON = 0x05, - MCP23S08_GPPU = 0x06, - MCP23S08_INTF = 0x07, - MCP23S08_INTCAP = 0x08, - MCP23S08_GPIO = 0x09, - MCP23S08_OLAT = 0x0A, -}; - -class MCP23S08 : public Component, +class MCP23S08 : public mcp23x08_base::MCP23X08Base, public spi::SPIDevice { public: @@ -37,37 +16,14 @@ class MCP23S08 : public Component, void setup() override; void dump_config() override; - bool digital_read(uint8_t pin); - void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); void set_device_address(uint8_t device_addr); - float get_setup_priority() const override; - - // read a given register - bool read_reg(uint8_t reg, uint8_t *value); - // write a value to a given register - bool write_reg(uint8_t reg, uint8_t value); - // update registers with given pin value. - void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a); - protected: + bool read_reg(uint8_t reg, uint8_t *value) override; + bool write_reg(uint8_t reg, uint8_t value) override; + uint8_t device_opcode_ = 0x40; - uint8_t olat_{0x00}; -}; - -class MCP23S08GPIOPin : public GPIOPin { - public: - MCP23S08GPIOPin(MCP23S08 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - - void setup() override; - void pin_mode(uint8_t mode) override; - bool digital_read() override; - void digital_write(bool value) override; - - protected: - MCP23S08 *parent_; }; } // namespace mcp23s08 diff --git a/esphome/components/mcp23s17/__init__.py b/esphome/components/mcp23s17/__init__.py index 85f22874a6..c38bd2617a 100644 --- a/esphome/components/mcp23s17/__init__.py +++ b/esphome/components/mcp23s17/__init__.py @@ -1,26 +1,18 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.components import spi -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED - -CODEOWNERS = ["@SenexCrenshaw"] +from esphome.components import spi, mcp23x17_base, mcp23xxx_base +from esphome.const import CONF_ID +AUTO_LOAD = ["mcp23x17_base"] +CODEOWNERS = ["@SenexCrenshaw", "@jesserockz"] DEPENDENCIES = ["spi"] MULTI_CONF = True CONF_DEVICEADDRESS = "deviceaddress" mcp23S17_ns = cg.esphome_ns.namespace("mcp23s17") -mcp23S17GPIOMode = mcp23S17_ns.enum("MCP23S17GPIOMode") -mcp23S17_GPIO_MODES = { - "INPUT": mcp23S17GPIOMode.MCP23S17_INPUT, - "INPUT_PULLUP": mcp23S17GPIOMode.MCP23S17_INPUT_PULLUP, - "OUTPUT": mcp23S17GPIOMode.MCP23S17_OUTPUT, -} -mcp23S17 = mcp23S17_ns.class_("MCP23S17", cg.Component, spi.SPIDevice) -mcp23S17GPIOPin = mcp23S17_ns.class_("MCP23S17GPIOPin", cg.GPIOPin) +mcp23S17 = mcp23S17_ns.class_("MCP23S17", mcp23x17_base.MCP23X17Base, spi.SPIDevice) CONFIG_SCHEMA = ( cv.Schema( @@ -29,47 +21,12 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_DEVICEADDRESS, default=0): cv.uint8_t, } ) - .extend(cv.COMPONENT_SCHEMA) + .extend(mcp23xxx_base.MCP23XXX_CONFIG_SCHEMA) .extend(spi.spi_device_schema()) ) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = yield mcp23xxx_base.register_mcp23xxx(config) cg.add(var.set_device_address(config[CONF_DEVICEADDRESS])) - yield cg.register_component(var, config) yield spi.register_spi_device(var, config) - - -CONF_MCP23S17 = "mcp23s17" - -mcp23S17_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.GenerateID(CONF_MCP23S17): cv.use_id(mcp23S17), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - mcp23S17_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -mcp23S17_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23S17): cv.use_id(mcp23S17), - cv.Required(CONF_NUMBER): cv.int_range(0, 15), - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - mcp23S17_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - - -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23S17, (mcp23S17_OUTPUT_PIN_SCHEMA, mcp23S17_INPUT_PIN_SCHEMA) -) -def mcp23S17_pin_to_code(config): - parent = yield cg.get_variable(config[CONF_MCP23S17]) - yield mcp23S17GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) diff --git a/esphome/components/mcp23s17/mcp23s17.cpp b/esphome/components/mcp23s17/mcp23s17.cpp index 30e4f63953..7c2cfcd526 100644 --- a/esphome/components/mcp23s17/mcp23s17.cpp +++ b/esphome/components/mcp23s17/mcp23s17.cpp @@ -19,17 +19,15 @@ void MCP23S17::setup() { this->enable(); uint8_t cmd = 0b01000000; this->transfer_byte(cmd); - this->transfer_byte(0x18); - this->transfer_byte(0x0A); - this->transfer_byte(this->device_opcode_); - this->transfer_byte(0); - this->transfer_byte(0xFF); - this->transfer_byte(0xFF); - - for (uint8_t i = 0; i < 20; i++) { - this->transfer_byte(0); - } + this->transfer_byte(mcp23x17_base::MCP23X17_IOCONA); + this->transfer_byte(0b00011000); // Enable HAEN pins for addressing this->disable(); + + if (this->open_drain_ints_) { + // enable open-drain interrupt pins, 3.3V-safe + this->write_reg(mcp23x17_base::MCP23X17_IOCONA, 0x04); + this->write_reg(mcp23x17_base::MCP23X17_IOCONB, 0x04); + } } void MCP23S17::dump_config() { @@ -37,65 +35,6 @@ void MCP23S17::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); } -float MCP23S17::get_setup_priority() const { return setup_priority::HARDWARE; } - -bool MCP23S17::digital_read(uint8_t pin) { - uint8_t bit = pin % 8; - uint8_t reg_addr = pin < 8 ? MCP23S17_GPIOA : MCP23S17_GPIOB; - uint8_t value = 0; - this->read_reg(reg_addr, &value); - return value & (1 << bit); -} - -void MCP23S17::digital_write(uint8_t pin, bool value) { - uint8_t reg_addr = pin < 8 ? MCP23S17_OLATA : MCP23S17_OLATB; - this->update_reg(pin, value, reg_addr); -} - -void MCP23S17::pin_mode(uint8_t pin, uint8_t mode) { - uint8_t iodir = pin < 8 ? MCP23S17_IODIRA : MCP23S17_IODIRB; - uint8_t gppu = pin < 8 ? MCP23S17_GPPUA : MCP23S17_GPPUB; - switch (mode) { - case MCP23S17_INPUT: - this->update_reg(pin, true, iodir); - break; - case MCP23S17_INPUT_PULLUP: - this->update_reg(pin, true, iodir); - this->update_reg(pin, true, gppu); - break; - case MCP23S17_OUTPUT: - this->update_reg(pin, false, iodir); - break; - default: - break; - } -} - -void MCP23S17::update_reg(uint8_t pin, bool pin_value, uint8_t reg_addr) { - uint8_t bit = pin % 8; - uint8_t reg_value = 0; - if (reg_addr == MCP23S17_OLATA) { - reg_value = this->olat_a_; - } else if (reg_addr == MCP23S17_OLATB) { - reg_value = this->olat_b_; - } else { - this->read_reg(reg_addr, ®_value); - } - - if (pin_value) - reg_value |= 1 << bit; - else - reg_value &= ~(1 << bit); - - this->write_reg(reg_addr, reg_value); - - if (reg_addr == MCP23S17_OLATA) { - this->olat_a_ = reg_value; - } else if (reg_addr == MCP23S17_OLATB) { - this->olat_b_ = reg_value; - } -} - bool MCP23S17::read_reg(uint8_t reg, uint8_t *value) { this->enable(); this->transfer_byte(this->device_opcode_ | 1); @@ -115,12 +54,5 @@ bool MCP23S17::write_reg(uint8_t reg, uint8_t value) { return true; } -MCP23S17GPIOPin::MCP23S17GPIOPin(MCP23S17 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23S17GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23S17GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -bool MCP23S17GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } -void MCP23S17GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } - } // namespace mcp23s17 } // namespace esphome diff --git a/esphome/components/mcp23s17/mcp23s17.h b/esphome/components/mcp23s17/mcp23s17.h index 8e27ff447f..1ced144c23 100644 --- a/esphome/components/mcp23s17/mcp23s17.h +++ b/esphome/components/mcp23s17/mcp23s17.h @@ -1,47 +1,14 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/components/mcp23x17_base/mcp23x17_base.h" #include "esphome/core/esphal.h" #include "esphome/components/spi/spi.h" namespace esphome { namespace mcp23s17 { -/// Modes for MCP23S17 pins -enum MCP23S17GPIOMode : uint8_t { - MCP23S17_INPUT = INPUT, // 0x00 - MCP23S17_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23S17_OUTPUT = OUTPUT // 0x01 -}; - -enum MCP23S17GPIORegisters { - // A side - MCP23S17_IODIRA = 0x00, - MCP23S17_IPOLA = 0x02, - MCP23S17_GPINTENA = 0x04, - MCP23S17_DEFVALA = 0x06, - MCP23S17_INTCONA = 0x08, - MCP23S17_IOCONA = 0x0A, - MCP23S17_GPPUA = 0x0C, - MCP23S17_INTFA = 0x0E, - MCP23S17_INTCAPA = 0x10, - MCP23S17_GPIOA = 0x12, - MCP23S17_OLATA = 0x14, - // B side - MCP23S17_IODIRB = 0x01, - MCP23S17_IPOLB = 0x03, - MCP23S17_GPINTENB = 0x05, - MCP23S17_DEFVALB = 0x07, - MCP23S17_INTCONB = 0x09, - MCP23S17_IOCONB = 0x0B, - MCP23S17_GPPUB = 0x0D, - MCP23S17_INTFB = 0x0F, - MCP23S17_INTCAPB = 0x11, - MCP23S17_GPIOB = 0x13, - MCP23S17_OLATB = 0x15, -}; - -class MCP23S17 : public Component, +class MCP23S17 : public mcp23x17_base::MCP23X17Base, public spi::SPIDevice { public: @@ -51,36 +18,11 @@ class MCP23S17 : public Component, void dump_config() override; void set_device_address(uint8_t device_addr); - bool digital_read(uint8_t pin); - void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); - - float get_setup_priority() const override; - - // read a given register - bool read_reg(uint8_t reg, uint8_t *value); - // write a value to a given register - bool write_reg(uint8_t reg, uint8_t value); - // update registers with given pin value. - void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a); - protected: + bool read_reg(uint8_t reg, uint8_t *value) override; + bool write_reg(uint8_t reg, uint8_t value) override; + uint8_t device_opcode_ = 0x40; - uint8_t olat_a_{0x00}; - uint8_t olat_b_{0x00}; -}; - -class MCP23S17GPIOPin : public GPIOPin { - public: - MCP23S17GPIOPin(MCP23S17 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - - void setup() override; - void pin_mode(uint8_t mode) override; - bool digital_read() override; - void digital_write(bool value) override; - - protected: - MCP23S17 *parent_; }; } // namespace mcp23s17 diff --git a/esphome/components/mcp23x08_base/__init__.py b/esphome/components/mcp23x08_base/__init__.py new file mode 100644 index 0000000000..ba44917202 --- /dev/null +++ b/esphome/components/mcp23x08_base/__init__.py @@ -0,0 +1,8 @@ +import esphome.codegen as cg +from esphome.components import mcp23xxx_base + +AUTO_LOAD = ["mcp23xxx_base"] +CODEOWNERS = ["@jesserockz"] + +mcp23x08_base_ns = cg.esphome_ns.namespace("mcp23x08_base") +MCP23X08Base = mcp23x08_base_ns.class_("MCP23X08Base", mcp23xxx_base.MCP23XXXBase) diff --git a/esphome/components/mcp23x08_base/mcp23x08_base.cpp b/esphome/components/mcp23x08_base/mcp23x08_base.cpp new file mode 100644 index 0000000000..c14e0020dd --- /dev/null +++ b/esphome/components/mcp23x08_base/mcp23x08_base.cpp @@ -0,0 +1,89 @@ +#include "mcp23x08_base.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp23x08_base { + +static const char *TAG = "mcp23x08_base"; + +bool MCP23X08Base::digital_read(uint8_t pin) { + uint8_t bit = pin % 8; + uint8_t reg_addr = mcp23x08_base::MCP23X08_GPIO; + uint8_t value = 0; + this->read_reg(reg_addr, &value); + return value & (1 << bit); +} + +void MCP23X08Base::digital_write(uint8_t pin, bool value) { + uint8_t reg_addr = mcp23x08_base::MCP23X08_OLAT; + this->update_reg(pin, value, reg_addr); +} + +void MCP23X08Base::pin_mode(uint8_t pin, uint8_t mode) { + uint8_t iodir = mcp23x08_base::MCP23X08_IODIR; + uint8_t gppu = mcp23x08_base::MCP23X08_GPPU; + switch (mode) { + case mcp23xxx_base::MCP23XXX_INPUT: + this->update_reg(pin, true, iodir); + break; + case mcp23xxx_base::MCP23XXX_INPUT_PULLUP: + this->update_reg(pin, true, iodir); + this->update_reg(pin, true, gppu); + break; + case mcp23xxx_base::MCP23XXX_OUTPUT: + this->update_reg(pin, false, iodir); + break; + default: + break; + } +} + +void MCP23X08Base::pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) { + uint8_t gpinten = mcp23x08_base::MCP23X08_GPINTEN; + uint8_t intcon = mcp23x08_base::MCP23X08_INTCON; + uint8_t defval = mcp23x08_base::MCP23X08_DEFVAL; + + switch (interrupt_mode) { + case mcp23xxx_base::MCP23XXX_CHANGE: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, false, intcon); + break; + case mcp23xxx_base::MCP23XXX_RISING: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, true, intcon); + this->update_reg(pin, true, defval); + break; + case mcp23xxx_base::MCP23XXX_FALLING: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, true, intcon); + this->update_reg(pin, false, defval); + break; + case mcp23xxx_base::MCP23XXX_NO_INTERRUPT: + this->update_reg(pin, false, gpinten); + break; + } +} + +void MCP23X08Base::update_reg(uint8_t pin, bool pin_value, uint8_t reg_addr) { + uint8_t bit = pin % 8; + uint8_t reg_value = 0; + if (reg_addr == mcp23x08_base::MCP23X08_OLAT) { + reg_value = this->olat_; + } else { + this->read_reg(reg_addr, ®_value); + } + + if (pin_value) + reg_value |= 1 << bit; + else + reg_value &= ~(1 << bit); + + this->write_reg(reg_addr, reg_value); + + if (reg_addr == mcp23x08_base::MCP23X08_OLAT) { + this->olat_ = reg_value; + } +} + +} // namespace mcp23x08_base +} // namespace esphome diff --git a/esphome/components/mcp23x08_base/mcp23x08_base.h b/esphome/components/mcp23x08_base/mcp23x08_base.h new file mode 100644 index 0000000000..5e2c1a047f --- /dev/null +++ b/esphome/components/mcp23x08_base/mcp23x08_base.h @@ -0,0 +1,39 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" +#include "esphome/core/esphal.h" + +namespace esphome { +namespace mcp23x08_base { + +enum MCP23S08GPIORegisters { + // A side + MCP23X08_IODIR = 0x00, + MCP23X08_IPOL = 0x01, + MCP23X08_GPINTEN = 0x02, + MCP23X08_DEFVAL = 0x03, + MCP23X08_INTCON = 0x04, + MCP23X08_IOCON = 0x05, + MCP23X08_GPPU = 0x06, + MCP23X08_INTF = 0x07, + MCP23X08_INTCAP = 0x08, + MCP23X08_GPIO = 0x09, + MCP23X08_OLAT = 0x0A, +}; + +class MCP23X08Base : public mcp23xxx_base::MCP23XXXBase { + public: + bool digital_read(uint8_t pin) override; + void digital_write(uint8_t pin, bool value) override; + void pin_mode(uint8_t pin, uint8_t mode) override; + void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; + + protected: + void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a) override; + + uint8_t olat_{0x00}; +}; + +} // namespace mcp23x08_base +} // namespace esphome diff --git a/esphome/components/mcp23x17_base/__init__.py b/esphome/components/mcp23x17_base/__init__.py new file mode 100644 index 0000000000..97e0b3823d --- /dev/null +++ b/esphome/components/mcp23x17_base/__init__.py @@ -0,0 +1,8 @@ +import esphome.codegen as cg +from esphome.components import mcp23xxx_base + +AUTO_LOAD = ["mcp23xxx_base"] +CODEOWNERS = ["@jesserockz"] + +mcp23x17_base_ns = cg.esphome_ns.namespace("mcp23x17_base") +MCP23X17Base = mcp23x17_base_ns.class_("MCP23X17Base", mcp23xxx_base.MCP23XXXBase) diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.cpp b/esphome/components/mcp23x17_base/mcp23x17_base.cpp new file mode 100644 index 0000000000..18f3ba7c6d --- /dev/null +++ b/esphome/components/mcp23x17_base/mcp23x17_base.cpp @@ -0,0 +1,93 @@ +#include "mcp23x17_base.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp23x17_base { + +static const char *TAG = "mcp23x17_base"; + +bool MCP23X17Base::digital_read(uint8_t pin) { + uint8_t bit = pin % 8; + uint8_t reg_addr = pin < 8 ? mcp23x17_base::MCP23X17_GPIOA : mcp23x17_base::MCP23X17_GPIOB; + uint8_t value = 0; + this->read_reg(reg_addr, &value); + return value & (1 << bit); +} + +void MCP23X17Base::digital_write(uint8_t pin, bool value) { + uint8_t reg_addr = pin < 8 ? mcp23x17_base::MCP23X17_OLATA : mcp23x17_base::MCP23X17_OLATB; + this->update_reg(pin, value, reg_addr); +} + +void MCP23X17Base::pin_mode(uint8_t pin, uint8_t mode) { + uint8_t iodir = pin < 8 ? mcp23x17_base::MCP23X17_IODIRA : mcp23x17_base::MCP23X17_IODIRB; + uint8_t gppu = pin < 8 ? mcp23x17_base::MCP23X17_GPPUA : mcp23x17_base::MCP23X17_GPPUB; + switch (mode) { + case mcp23xxx_base::MCP23XXX_INPUT: + this->update_reg(pin, true, iodir); + break; + case mcp23xxx_base::MCP23XXX_INPUT_PULLUP: + this->update_reg(pin, true, iodir); + this->update_reg(pin, true, gppu); + break; + case mcp23xxx_base::MCP23XXX_OUTPUT: + this->update_reg(pin, false, iodir); + break; + default: + break; + } +} + +void MCP23X17Base::pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) { + uint8_t gpinten = pin < 8 ? mcp23x17_base::MCP23X17_GPINTENA : mcp23x17_base::MCP23X17_GPINTENB; + uint8_t intcon = pin < 8 ? mcp23x17_base::MCP23X17_INTCONA : mcp23x17_base::MCP23X17_INTCONB; + uint8_t defval = pin < 8 ? mcp23x17_base::MCP23X17_DEFVALA : mcp23x17_base::MCP23X17_DEFVALB; + + switch (interrupt_mode) { + case mcp23xxx_base::MCP23XXX_CHANGE: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, false, intcon); + break; + case mcp23xxx_base::MCP23XXX_RISING: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, true, intcon); + this->update_reg(pin, true, defval); + break; + case mcp23xxx_base::MCP23XXX_FALLING: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, true, intcon); + this->update_reg(pin, false, defval); + break; + case mcp23xxx_base::MCP23XXX_NO_INTERRUPT: + this->update_reg(pin, false, gpinten); + break; + } +} + +void MCP23X17Base::update_reg(uint8_t pin, bool pin_value, uint8_t reg_addr) { + uint8_t bit = pin % 8; + uint8_t reg_value = 0; + if (reg_addr == mcp23x17_base::MCP23X17_OLATA) { + reg_value = this->olat_a_; + } else if (reg_addr == mcp23x17_base::MCP23X17_OLATB) { + reg_value = this->olat_b_; + } else { + this->read_reg(reg_addr, ®_value); + } + + if (pin_value) + reg_value |= 1 << bit; + else + reg_value &= ~(1 << bit); + + this->write_reg(reg_addr, reg_value); + + if (reg_addr == mcp23x17_base::MCP23X17_OLATA) { + this->olat_a_ = reg_value; + } else if (reg_addr == mcp23x17_base::MCP23X17_OLATB) { + this->olat_b_ = reg_value; + } +} + +} // namespace mcp23x17_base +} // namespace esphome diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.h b/esphome/components/mcp23x17_base/mcp23x17_base.h new file mode 100644 index 0000000000..1bbcb97041 --- /dev/null +++ b/esphome/components/mcp23x17_base/mcp23x17_base.h @@ -0,0 +1,52 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" +#include "esphome/core/esphal.h" + +namespace esphome { +namespace mcp23x17_base { + +enum MCP23X17GPIORegisters { + // A side + MCP23X17_IODIRA = 0x00, + MCP23X17_IPOLA = 0x02, + MCP23X17_GPINTENA = 0x04, + MCP23X17_DEFVALA = 0x06, + MCP23X17_INTCONA = 0x08, + MCP23X17_IOCONA = 0x0A, + MCP23X17_GPPUA = 0x0C, + MCP23X17_INTFA = 0x0E, + MCP23X17_INTCAPA = 0x10, + MCP23X17_GPIOA = 0x12, + MCP23X17_OLATA = 0x14, + // B side + MCP23X17_IODIRB = 0x01, + MCP23X17_IPOLB = 0x03, + MCP23X17_GPINTENB = 0x05, + MCP23X17_DEFVALB = 0x07, + MCP23X17_INTCONB = 0x09, + MCP23X17_IOCONB = 0x0B, + MCP23X17_GPPUB = 0x0D, + MCP23X17_INTFB = 0x0F, + MCP23X17_INTCAPB = 0x11, + MCP23X17_GPIOB = 0x13, + MCP23X17_OLATB = 0x15, +}; + +class MCP23X17Base : public mcp23xxx_base::MCP23XXXBase { + public: + bool digital_read(uint8_t pin) override; + void digital_write(uint8_t pin, bool value) override; + void pin_mode(uint8_t pin, uint8_t mode) override; + void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; + + protected: + void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a) override; + + uint8_t olat_a_{0x00}; + uint8_t olat_b_{0x00}; +}; + +} // namespace mcp23x17_base +} // namespace esphome diff --git a/esphome/components/mcp23xxx_base/__init__.py b/esphome/components/mcp23xxx_base/__init__.py new file mode 100644 index 0000000000..f8ab224193 --- /dev/null +++ b/esphome/components/mcp23xxx_base/__init__.py @@ -0,0 +1,118 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.const import ( + CONF_ID, + CONF_NUMBER, + CONF_MODE, + CONF_INVERTED, + CONF_INTERRUPT, + CONF_OPEN_DRAIN_INTERRUPT, +) +from esphome.core import coroutine + +CODEOWNERS = ["@jesserockz"] + +mcp23xxx_base_ns = cg.esphome_ns.namespace("mcp23xxx_base") +MCP23XXXBase = mcp23xxx_base_ns.class_("MCP23XXXBase", cg.Component) +MCP23XXXGPIOPin = mcp23xxx_base_ns.class_("MCP23XXXGPIOPin", cg.GPIOPin) +MCP23XXXGPIOMode = mcp23xxx_base_ns.enum("MCP23XXXGPIOMode") +MCP23XXXInterruptMode = mcp23xxx_base_ns.enum("MCP23XXXInterruptMode") + +MCP23XXX_INTERRUPT_MODES = { + "NO_INTERRUPT": MCP23XXXInterruptMode.MCP23XXX_NO_INTERRUPT, + "CHANGE": MCP23XXXInterruptMode.MCP23XXX_CHANGE, + "RISING": MCP23XXXInterruptMode.MCP23XXX_RISING, + "FALLING": MCP23XXXInterruptMode.MCP23XXX_FALLING, +} + +MCP23XXX_GPIO_MODES = { + "INPUT": MCP23XXXGPIOMode.MCP23XXX_INPUT, + "INPUT_PULLUP": MCP23XXXGPIOMode.MCP23XXX_INPUT_PULLUP, + "OUTPUT": MCP23XXXGPIOMode.MCP23XXX_OUTPUT, +} + +MCP23XXX_CONFIG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) + + +@coroutine +def register_mcp23xxx(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) + return var + + +CONF_MCP23XXX = "mcp23xxx" +MCP23XXX_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( + MCP23XXX_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( + MCP23XXX_INTERRUPT_MODES, upper=True + ), + } +) +MCP23XXX_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + MCP23XXX_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( + MCP23XXX_INTERRUPT_MODES, upper=True + ), + } +) + + +@pins.PIN_SCHEMA_REGISTRY.register( + CONF_MCP23XXX, (MCP23XXX_OUTPUT_PIN_SCHEMA, MCP23XXX_INPUT_PIN_SCHEMA) +) +def mcp23xxx_pin_to_code(config): + parent = yield cg.get_variable(config[CONF_MCP23XXX]) + yield MCP23XXXGPIOPin.new( + parent, + config[CONF_NUMBER], + config[CONF_MODE], + config[CONF_INVERTED], + config[CONF_INTERRUPT], + ) + + +# BEGIN Removed pin schemas below to show error in configuration +# TODO remove in 1.19.0 + +for id in ["mcp23008", "mcp23s08", "mcp23017", "mcp23s17"]: + PIN_SCHEMA = cv.Schema( + { + cv.Required(id): cv.invalid( + f"'{id}:' has been removed from the pin schema in 1.17.0, please use 'mcp23xxx:'" + ), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + MCP23XXX_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( + MCP23XXX_INTERRUPT_MODES, upper=True + ), + } + ) + + @pins.PIN_SCHEMA_REGISTRY.register(id, (PIN_SCHEMA, PIN_SCHEMA)) + def pin_to_code(config): + pass + + +# END Removed pin schemas diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp b/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp new file mode 100644 index 0000000000..37c55fceaf --- /dev/null +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp @@ -0,0 +1,21 @@ +#include "mcp23xxx_base.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp23xxx_base { + +float MCP23XXXBase::get_setup_priority() const { return setup_priority::IO; } + +MCP23XXXGPIOPin::MCP23XXXGPIOPin(MCP23XXXBase *parent, uint8_t pin, uint8_t mode, bool inverted, + MCP23XXXInterruptMode interrupt_mode) + : GPIOPin(pin, mode, inverted), parent_(parent), interrupt_mode_(interrupt_mode) {} +void MCP23XXXGPIOPin::setup() { this->pin_mode(this->mode_); } +void MCP23XXXGPIOPin::pin_mode(uint8_t mode) { + this->parent_->pin_mode(this->pin_, mode); + this->parent_->pin_interrupt_mode(this->pin_, this->interrupt_mode_); +} +bool MCP23XXXGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } +void MCP23XXXGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } + +} // namespace mcp23xxx_base +} // namespace esphome diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.h b/esphome/components/mcp23xxx_base/mcp23xxx_base.h new file mode 100644 index 0000000000..bf01320264 --- /dev/null +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.h @@ -0,0 +1,55 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/esphal.h" + +namespace esphome { +namespace mcp23xxx_base { + +enum MCP23XXXInterruptMode : uint8_t { MCP23XXX_NO_INTERRUPT = 0, MCP23XXX_CHANGE, MCP23XXX_RISING, MCP23XXX_FALLING }; + +/// Modes for MCP23XXX pins +enum MCP23XXXGPIOMode : uint8_t { + MCP23XXX_INPUT = INPUT, // 0x00 + MCP23XXX_INPUT_PULLUP = INPUT_PULLUP, // 0x02 + MCP23XXX_OUTPUT = OUTPUT // 0x01 +}; + +class MCP23XXXBase : public Component { + public: + virtual bool digital_read(uint8_t pin); + virtual void digital_write(uint8_t pin, bool value); + virtual void pin_mode(uint8_t pin, uint8_t mode); + virtual void pin_interrupt_mode(uint8_t pin, MCP23XXXInterruptMode interrupt_mode); + + void set_open_drain_ints(const bool value) { this->open_drain_ints_ = value; } + float get_setup_priority() const override; + + protected: + // read a given register + virtual bool read_reg(uint8_t reg, uint8_t *value); + // write a value to a given register + virtual bool write_reg(uint8_t reg, uint8_t value); + // update registers with given pin value. + virtual void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a); + + bool open_drain_ints_; +}; + +class MCP23XXXGPIOPin : public GPIOPin { + public: + MCP23XXXGPIOPin(MCP23XXXBase *parent, uint8_t pin, uint8_t mode, bool inverted = false, + MCP23XXXInterruptMode interrupt_mode = MCP23XXX_NO_INTERRUPT); + + void setup() override; + void pin_mode(uint8_t mode) override; + bool digital_read() override; + void digital_write(bool value) override; + + protected: + MCP23XXXBase *parent_; + MCP23XXXInterruptMode interrupt_mode_; +}; + +} // namespace mcp23xxx_base +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 6bb842c22b..af5961be88 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -255,6 +255,7 @@ CONF_INTENSITY = "intensity" CONF_INTERLOCK = "interlock" CONF_INTERNAL = "internal" CONF_INTERNAL_FILTER = "internal_filter" +CONF_INTERRUPT = "interrupt" CONF_INTERVAL = "interval" CONF_INVALID_COOLDOWN = "invalid_cooldown" CONF_INVERT = "invert" diff --git a/tests/test1.yaml b/tests/test1.yaml index 4dd3173d47..7651505c9a 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -879,7 +879,7 @@ binary_sensor: - platform: gpio name: 'MCP23S08 Pin #1' pin: - mcp23s08: mcp23s08_hub + mcp23xxx: mcp23s08_hub # Use pin number 1 number: 1 # One of INPUT or INPUT_PULLUP @@ -888,12 +888,22 @@ binary_sensor: - platform: gpio name: 'MCP23S17 Pin #1' pin: - mcp23s17: mcp23s17_hub + mcp23xxx: mcp23s17_hub # Use pin number 1 number: 1 # One of INPUT or INPUT_PULLUP mode: INPUT_PULLUP inverted: False + - platform: gpio + name: 'MCP23S17 Pin #1 with interrupt' + pin: + mcp23xxx: mcp23s17_hub + # Use pin number 1 + number: 1 + # One of INPUT or INPUT_PULLUP + mode: INPUT_PULLUP + inverted: False + interrupt: FALLING - platform: gpio pin: GPIO9 name: 'Living Room Window' @@ -1012,14 +1022,14 @@ binary_sensor: - platform: gpio name: 'MCP21 binary sensor' pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 1 mode: INPUT inverted: True - platform: gpio name: 'MCP22 binary sensor' pin: - mcp23008: mcp23008_hub + mcp23xxx: mcp23008_hub number: 7 mode: INPUT_PULLUP inverted: False @@ -1176,14 +1186,14 @@ output: - platform: gpio id: id22 pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 0 mode: OUTPUT inverted: False - platform: gpio id: id23 pin: - mcp23008: mcp23008_hub + mcp23xxx: mcp23008_hub number: 0 mode: OUTPUT inverted: False @@ -1459,7 +1469,7 @@ switch: - platform: gpio name: 'MCP23S08 Pin #0' pin: - mcp23s08: mcp23s08_hub + mcp23xxx: mcp23s08_hub # Use pin number 0 number: 0 mode: OUTPUT @@ -1467,7 +1477,7 @@ switch: - platform: gpio name: 'MCP23S17 Pin #0' pin: - mcp23s17: mcp23s17_hub + mcp23xxx: mcp23s17_hub # Use pin number 0 number: 1 mode: OUTPUT @@ -1755,10 +1765,10 @@ display: it.print("1234"); - platform: tm1637 clk_pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 1 dio_pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 2 intensity: 3 lambda: |- diff --git a/tests/test3.yaml b/tests/test3.yaml index 30578b451d..f4efe1067d 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -560,14 +560,14 @@ switch: - platform: gpio id: gpio_switch1 pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 0 mode: OUTPUT interlock: &interlock [gpio_switch1, gpio_switch2, gpio_switch3] - platform: gpio id: gpio_switch2 pin: - mcp23008: mcp23008_hub + mcp23xxx: mcp23008_hub number: 0 mode: OUTPUT interlock: *interlock