MCP23XXX Refactor (#1560)

* Refactor MCP23XXX classes to consolidate shared code

* Update test mcp23xxx pin schemas
This commit is contained in:
Jesse Hills 2021-03-08 08:23:54 +13:00 committed by GitHub
parent 69879920eb
commit 570ec36fe3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 603 additions and 740 deletions

View file

@ -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

View file

@ -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]
)

View file

@ -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, &reg_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

View file

@ -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

View file

@ -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]
)

View file

@ -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, &reg_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

View file

@ -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

View file

@ -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]
)

View file

@ -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, &reg_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

View file

@ -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<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
spi::DATA_RATE_10MHZ> {
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

View file

@ -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]
)

View file

@ -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, &reg_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

View file

@ -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<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
spi::DATA_RATE_8MHZ> {
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

View file

@ -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)

View file

@ -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, &reg_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

View file

@ -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

View file

@ -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)

View file

@ -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, &reg_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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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: |-

View file

@ -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