feat: Add support for MCP23016 IO Expander (#1012)

* feat: Add support for MCP23016 IO extander

* fix: Fix style
This commit is contained in:
Alex Reid 2020-04-12 15:07:10 -04:00 committed by GitHub
parent 835079ad43
commit c1dfed5c08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 230 additions and 0 deletions

View file

@ -0,0 +1,50 @@
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
DEPENDENCIES = ['i2c']
MULTI_CONF = True
mcp23016_ns = cg.esphome_ns.namespace('mcp23016')
MCP23016GPIOMode = mcp23016_ns.enum('MCP23016GPIOMode')
MCP23016_GPIO_MODES = {
'INPUT': MCP23016GPIOMode.MCP23016_INPUT,
'OUTPUT': MCP23016GPIOMode.MCP23016_OUTPUT,
}
MCP23016 = mcp23016_ns.class_('MCP23016', cg.Component, i2c.I2CDevice)
MCP23016GPIOPin = mcp23016_ns.class_('MCP23016GPIOPin', cg.GPIOPin)
CONFIG_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.declare_id(MCP23016),
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20))
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield i2c.register_i2c_device(var, config)
CONF_MCP23016 = 'mcp23016'
MCP23016_OUTPUT_PIN_SCHEMA = cv.Schema({
cv.Required(CONF_MCP23016): cv.use_id(MCP23016),
cv.Required(CONF_NUMBER): cv.int_,
cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(MCP23016_GPIO_MODES, upper=True),
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
})
MCP23016_INPUT_PIN_SCHEMA = cv.Schema({
cv.Required(CONF_MCP23016): cv.use_id(MCP23016),
cv.Required(CONF_NUMBER): cv.int_,
cv.Optional(CONF_MODE, default="INPUT"): cv.enum(MCP23016_GPIO_MODES, upper=True),
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
})
@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23016,
(MCP23016_OUTPUT_PIN_SCHEMA, MCP23016_INPUT_PIN_SCHEMA))
def mcp23016_pin_to_code(config):
parent = yield cg.get_variable(config[CONF_MCP23016])
yield MCP23016GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED])

View file

@ -0,0 +1,91 @@
#include "mcp23016.h"
#include "esphome/core/log.h"
namespace esphome {
namespace mcp23016 {
static const char *TAG = "mcp23016";
void MCP23016::setup() {
ESP_LOGCONFIG(TAG, "Setting up MCP23016...");
uint8_t iocon;
if (!this->read_reg_(MCP23016_IOCON0, &iocon)) {
this->mark_failed();
return;
}
// all pins input
this->write_reg_(MCP23016_IODIR0, 0xFF);
this->write_reg_(MCP23016_IODIR1, 0xFF);
}
bool MCP23016::digital_read(uint8_t pin) {
uint8_t bit = pin % 8;
uint8_t reg_addr = pin < 8 ? MCP23016_GP0 : MCP23016_GP1;
uint8_t value = 0;
this->read_reg_(reg_addr, &value);
return value & (1 << bit);
}
void MCP23016::digital_write(uint8_t pin, bool value) {
uint8_t reg_addr = pin < 8 ? MCP23016_OLAT0 : MCP23016_OLAT1;
this->update_reg_(pin, value, reg_addr);
}
void MCP23016::pin_mode(uint8_t pin, uint8_t mode) {
uint8_t iodir = pin < 8 ? MCP23016_IODIR0 : MCP23016_IODIR1;
switch (mode) {
case MCP23016_INPUT:
this->update_reg_(pin, true, iodir);
break;
case MCP23016_OUTPUT:
this->update_reg_(pin, false, iodir);
break;
default:
break;
}
}
float MCP23016::get_setup_priority() const { return setup_priority::HARDWARE; }
bool MCP23016::read_reg_(uint8_t reg, uint8_t *value) {
if (this->is_failed())
return false;
return this->read_byte(reg, value);
}
bool MCP23016::write_reg_(uint8_t reg, uint8_t value) {
if (this->is_failed())
return false;
return this->write_byte(reg, value);
}
void MCP23016::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 == MCP23016_OLAT0) {
reg_value = this->olat_0_;
} else if (reg_addr == MCP23016_OLAT1) {
reg_value = this->olat_1_;
} 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 == MCP23016_OLAT0) {
this->olat_0_ = reg_value;
} else if (reg_addr == MCP23016_OLAT1) {
this->olat_1_ = reg_value;
}
}
MCP23016GPIOPin::MCP23016GPIOPin(MCP23016 *parent, uint8_t pin, uint8_t mode, bool inverted)
: GPIOPin(pin, mode, inverted), parent_(parent) {}
void MCP23016GPIOPin::setup() { this->pin_mode(this->mode_); }
void MCP23016GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); }
bool MCP23016GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
void MCP23016GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
} // namespace mcp23016
} // namespace esphome

View file

@ -0,0 +1,71 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/esphal.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace mcp23016 {
/// Modes for MCP23016 pins
enum MCP23016GPIOMode : uint8_t {
MCP23016_INPUT = INPUT, // 0x00
MCP23016_OUTPUT = OUTPUT // 0x01
};
enum MCP23016GPIORegisters {
// 0 side
MCP23016_GP0 = 0x00,
MCP23016_OLAT0 = 0x02,
MCP23016_IPOL0 = 0x04,
MCP23016_IODIR0 = 0x06,
MCP23016_INTCAP0 = 0x08,
MCP23016_IOCON0 = 0x0A,
// 1 side
MCP23016_GP1 = 0x01,
MCP23016_OLAT1 = 0x03,
MCP23016_IPOL1 = 0x04,
MCP23016_IODIR1 = 0x07,
MCP23016_INTCAP1 = 0x08,
MCP23016_IOCON1 = 0x0B,
};
class MCP23016 : public Component, public i2c::I2CDevice {
public:
MCP23016() = 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);
float get_setup_priority() const 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_0_{0x00};
uint8_t olat_1_{0x00};
};
class MCP23016GPIOPin : public GPIOPin {
public:
MCP23016GPIOPin(MCP23016 *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:
MCP23016 *parent_;
};
} // namespace mcp23016
} // namespace esphome

View file

@ -867,6 +867,13 @@ binary_sensor:
number: 7 number: 7
mode: INPUT_PULLUP mode: INPUT_PULLUP
inverted: False inverted: False
- platform: gpio
name: "MCP23 binary sensor"
pin:
mcp23016: mcp23016_hub
number: 7
mode: INPUT
inverted: False
- platform: remote_receiver - platform: remote_receiver
name: "Raw Remote Receiver Test" name: "Raw Remote Receiver Test"
@ -990,6 +997,13 @@ output:
number: 0 number: 0
mode: OUTPUT mode: OUTPUT
inverted: False inverted: False
- platform: gpio
id: id25
pin:
mcp23016: mcp23016_hub
number: 0
mode: OUTPUT
inverted: False
- platform: my9231 - platform: my9231
id: my_0 id: my_0
channel: 0 channel: 0
@ -1569,6 +1583,10 @@ mcp23008:
- id: 'mcp23008_hub' - id: 'mcp23008_hub'
address: 0x22 address: 0x22
mcp23016:
- id: 'mcp23016_hub'
address: 0x23
stepper: stepper:
- platform: a4988 - platform: a4988
id: my_stepper id: my_stepper