mirror of
https://github.com/esphome/esphome.git
synced 2024-11-22 15:08:10 +01:00
support for the sx1509 i2c device (#651)
* added ANALOG_OUTPUT as first functionality
* added gpio
* seperated the code for different functions
* fixed code
* Revert "fixed code"
This reverts commit 0c6eacb225
.
* add timings for breathe and blink
* made the sx1509_float_output am output component
* add keypad
* implementation for sx1509 keypad
* keypad code cleanup and first device tests
* debounce
* keypad working now.
* update for timings.
does not compile yet
* added all options for breathe and blink
fixed var namings
* blink and breath still not ok
* fixed ms for timings
* sync with repo
* fixed issue with gpio pin output
* code cleanup
* lint
* more lint
* remove log from header
* Update esphome/components/sx1509/__init__.py
Co-Authored-By: Otto Winter <otto@otto-winter.com>
* review
* feedback
* fixed review issues
did some extended testing with mqtt spy
* code cleanup (comments)
* fixed row col swap for binarysensor_keypad
* flake and lint
* travis
* travis
* travis
* Update esphome/components/sx1509/sx1509.cpp
Co-Authored-By: Otto Winter <otto@otto-winter.com>
* review
* separated platforms
* code cleanup
* travis relative paths in python
* remove blink/breathe
code cleanup
* cpp lint
* feedback
* travis
* lint line to long
* check keypad settings to be valid
* clang
* keypad config
* text
* Remove wrong .gitignore from .gitignore
* Remove .pio folder from .gitignore (merge)
* Formatting
* Formatting
* Add i2c log in dump_config
* Remove unused variables
* Disable static for header files
We don't need internal linkage
* Use consistent member default argument style
* Run clang-format
Co-authored-by: null <m.vanturnhout@exxellence.nl>
Co-authored-by: Otto Winter <otto@otto-winter.com>
This commit is contained in:
parent
be91cfb261
commit
5f2808ec2f
11 changed files with 701 additions and 0 deletions
77
esphome/components/sx1509/__init__.py
Normal file
77
esphome/components/sx1509/__init__.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
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_KEYPAD = 'keypad'
|
||||
CONF_KEY_ROWS = 'key_rows'
|
||||
CONF_KEY_COLUMNS = 'key_columns'
|
||||
CONF_SLEEP_TIME = 'sleep_time'
|
||||
CONF_SCAN_TIME = 'scan_time'
|
||||
CONF_DEBOUNCE_TIME = 'debounce_time'
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
MULTI_CONF = True
|
||||
|
||||
sx1509_ns = cg.esphome_ns.namespace('sx1509')
|
||||
SX1509GPIOMode = sx1509_ns.enum('SX1509GPIOMode')
|
||||
SX1509_GPIO_MODES = {
|
||||
'INPUT': SX1509GPIOMode.SX1509_INPUT,
|
||||
'INPUT_PULLUP': SX1509GPIOMode.SX1509_INPUT_PULLUP,
|
||||
'OUTPUT': SX1509GPIOMode.SX1509_OUTPUT
|
||||
}
|
||||
|
||||
SX1509Component = sx1509_ns.class_('SX1509Component', cg.Component, i2c.I2CDevice)
|
||||
SX1509GPIOPin = sx1509_ns.class_('SX1509GPIOPin', cg.GPIOPin)
|
||||
|
||||
KEYPAD_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_KEY_ROWS): cv.int_range(min=1, max=8),
|
||||
cv.Required(CONF_KEY_COLUMNS): cv.int_range(min=1, max=8),
|
||||
cv.Optional(CONF_SLEEP_TIME): cv.int_range(min=128, max=8192),
|
||||
cv.Optional(CONF_SCAN_TIME): cv.int_range(min=1, max=128),
|
||||
cv.Optional(CONF_DEBOUNCE_TIME): cv.int_range(min=1, max=64),
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(SX1509Component),
|
||||
cv.Optional(CONF_KEYPAD): cv.Schema(KEYPAD_SCHEMA),
|
||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x3E))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
if CONF_KEYPAD in config:
|
||||
keypad = config[CONF_KEYPAD]
|
||||
cg.add(var.set_rows_cols(keypad[CONF_KEY_ROWS], keypad[CONF_KEY_COLUMNS]))
|
||||
if CONF_SLEEP_TIME in keypad and CONF_SCAN_TIME in keypad and CONF_DEBOUNCE_TIME in keypad:
|
||||
cg.add(var.set_sleep_time(keypad[CONF_SLEEP_TIME]))
|
||||
cg.add(var.set_scan_time(keypad[CONF_SCAN_TIME]))
|
||||
cg.add(var.set_debounce_time(keypad[CONF_DEBOUNCE_TIME]))
|
||||
|
||||
|
||||
CONF_SX1509 = 'sx1509'
|
||||
CONF_SX1509_ID = 'sx1509_id'
|
||||
|
||||
SX1509_OUTPUT_PIN_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_SX1509): cv.use_id(SX1509Component),
|
||||
cv.Required(CONF_NUMBER): cv.int_,
|
||||
cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(SX1509_GPIO_MODES, upper=True),
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
})
|
||||
SX1509_INPUT_PIN_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_SX1509): cv.use_id(SX1509Component),
|
||||
cv.Required(CONF_NUMBER): cv.int_,
|
||||
cv.Optional(CONF_MODE, default="INPUT"): cv.enum(SX1509_GPIO_MODES, upper=True),
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
})
|
||||
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register(CONF_SX1509,
|
||||
(SX1509_OUTPUT_PIN_SCHEMA, SX1509_INPUT_PIN_SCHEMA))
|
||||
def sx1509_pin_to_code(config):
|
||||
parent = yield cg.get_variable(config[CONF_SX1509])
|
||||
yield SX1509GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE],
|
||||
config[CONF_INVERTED])
|
28
esphome/components/sx1509/binary_sensor/__init__.py
Normal file
28
esphome/components/sx1509/binary_sensor/__init__.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_ID
|
||||
from .. import SX1509Component, sx1509_ns, CONF_SX1509_ID
|
||||
|
||||
CONF_ROW = 'row'
|
||||
CONF_COLUMN = 'col'
|
||||
|
||||
DEPENDENCIES = ['sx1509']
|
||||
|
||||
SX1509BinarySensor = sx1509_ns.class_('SX1509BinarySensor', binary_sensor.BinarySensor)
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(SX1509BinarySensor),
|
||||
cv.GenerateID(CONF_SX1509_ID): cv.use_id(SX1509Component),
|
||||
cv.Required(CONF_ROW): cv.int_range(min=0, max=4),
|
||||
cv.Required(CONF_COLUMN): cv.int_range(min=0, max=4),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield binary_sensor.register_binary_sensor(var, config)
|
||||
hub = yield cg.get_variable(config[CONF_SX1509_ID])
|
||||
cg.add(var.set_row_col(config[CONF_ROW], config[CONF_COLUMN]))
|
||||
|
||||
cg.add(hub.register_keypad_binary_sensor(var))
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/sx1509/sx1509.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace sx1509 {
|
||||
|
||||
class SX1509BinarySensor : public sx1509::SX1509Processor, public binary_sensor::BinarySensor {
|
||||
public:
|
||||
void set_row_col(uint8_t row, uint8_t col) { this->key_ = (1 << (col + 8)) | (1 << row); }
|
||||
void process(uint16_t data) override { this->publish_state(static_cast<bool>(data == key_)); }
|
||||
|
||||
protected:
|
||||
uint16_t key_{0};
|
||||
};
|
||||
|
||||
} // namespace sx1509
|
||||
} // namespace esphome
|
25
esphome/components/sx1509/output/__init__.py
Normal file
25
esphome/components/sx1509/output/__init__.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import output
|
||||
from esphome.const import CONF_PIN, CONF_ID
|
||||
from .. import SX1509Component, sx1509_ns, CONF_SX1509_ID
|
||||
|
||||
DEPENDENCIES = ['sx1509']
|
||||
|
||||
SX1509FloatOutputChannel = sx1509_ns.class_('SX1509FloatOutputChannel',
|
||||
output.FloatOutput, cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||
cv.Required(CONF_ID): cv.declare_id(SX1509FloatOutputChannel),
|
||||
cv.GenerateID(CONF_SX1509_ID): cv.use_id(SX1509Component),
|
||||
cv.Required(CONF_PIN): cv.int_range(min=0, max=15),
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
parent = yield cg.get_variable(config[CONF_SX1509_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield output.register_output(var, config)
|
||||
cg.add(var.set_pin(config[CONF_PIN]))
|
||||
cg.add(var.set_parent(parent))
|
30
esphome/components/sx1509/output/sx1509_float_output.cpp
Normal file
30
esphome/components/sx1509/output/sx1509_float_output.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "sx1509_float_output.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace sx1509 {
|
||||
|
||||
static const char *TAG = "sx1509_float_channel";
|
||||
|
||||
void SX1509FloatOutputChannel::write_state(float state) {
|
||||
const uint16_t max_duty = 255;
|
||||
const float duty_rounded = roundf(state * max_duty);
|
||||
auto duty = static_cast<uint16_t>(duty_rounded);
|
||||
this->parent_->set_pin_value(this->pin_, duty);
|
||||
}
|
||||
|
||||
void SX1509FloatOutputChannel::setup() {
|
||||
ESP_LOGD(TAG, "setup pin %d", this->pin_);
|
||||
this->parent_->pin_mode(this->pin_, SX1509_ANALOG_OUTPUT);
|
||||
this->turn_off();
|
||||
}
|
||||
|
||||
void SX1509FloatOutputChannel::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "SX1509 PWM:");
|
||||
ESP_LOGCONFIG(TAG, " sx1509 pin: %d", this->pin_);
|
||||
LOG_FLOAT_OUTPUT(this);
|
||||
}
|
||||
|
||||
} // namespace sx1509
|
||||
} // namespace esphome
|
27
esphome/components/sx1509/output/sx1509_float_output.h
Normal file
27
esphome/components/sx1509/output/sx1509_float_output.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/sx1509/sx1509.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace sx1509 {
|
||||
|
||||
class SX1509Component;
|
||||
|
||||
class SX1509FloatOutputChannel : public output::FloatOutput, public Component {
|
||||
public:
|
||||
void set_parent(SX1509Component *parent) { this->parent_ = parent; }
|
||||
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
SX1509Component *parent_;
|
||||
uint8_t pin_;
|
||||
};
|
||||
|
||||
} // namespace sx1509
|
||||
} // namespace esphome
|
253
esphome/components/sx1509/sx1509.cpp
Normal file
253
esphome/components/sx1509/sx1509.cpp
Normal file
|
@ -0,0 +1,253 @@
|
|||
#include "sx1509.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace sx1509 {
|
||||
|
||||
static const char *TAG = "sx1509";
|
||||
|
||||
void SX1509Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up SX1509Component...");
|
||||
|
||||
ESP_LOGV(TAG, " Resetting devices...");
|
||||
if (!this->write_byte(REG_RESET, 0x12)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->write_byte(REG_RESET, 0x34);
|
||||
|
||||
uint16_t data;
|
||||
this->read_byte_16(REG_INTERRUPT_MASK_A, &data);
|
||||
if (data == 0xFF00) {
|
||||
clock_(INTERNAL_CLOCK_2MHZ);
|
||||
} else {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
delayMicroseconds(500);
|
||||
if (this->has_keypad_)
|
||||
this->setup_keypad_();
|
||||
}
|
||||
|
||||
void SX1509Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "SX1509:");
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Setting up SX1509 failed!");
|
||||
}
|
||||
LOG_I2C_DEVICE(this);
|
||||
}
|
||||
|
||||
void SX1509Component::loop() {
|
||||
if (this->has_keypad_) {
|
||||
uint16_t key_data = this->read_key_data();
|
||||
for (auto *binary_sensor : this->keypad_binary_sensors_)
|
||||
binary_sensor->process(key_data);
|
||||
}
|
||||
}
|
||||
|
||||
bool SX1509Component::digital_read(uint8_t pin) {
|
||||
if (this->ddr_mask_ & (1 << pin)) {
|
||||
uint16_t temp_reg_data;
|
||||
this->read_byte_16(REG_DATA_B, &temp_reg_data);
|
||||
if (temp_reg_data & (1 << pin))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SX1509Component::digital_write(uint8_t pin, bool bit_value) {
|
||||
if ((~this->ddr_mask_) & (1 << pin)) {
|
||||
// If the pin is an output, write high/low
|
||||
uint16_t temp_reg_data = 0;
|
||||
this->read_byte_16(REG_DATA_B, &temp_reg_data);
|
||||
if (bit_value)
|
||||
temp_reg_data |= (1 << pin);
|
||||
else
|
||||
temp_reg_data &= ~(1 << pin);
|
||||
this->write_byte_16(REG_DATA_B, temp_reg_data);
|
||||
} else {
|
||||
// Otherwise the pin is an input, pull-up/down
|
||||
uint16_t temp_pullup;
|
||||
this->read_byte_16(REG_PULL_UP_B, &temp_pullup);
|
||||
uint16_t temp_pull_down;
|
||||
this->read_byte_16(REG_PULL_DOWN_B, &temp_pull_down);
|
||||
|
||||
if (bit_value) {
|
||||
// if HIGH, do pull-up, disable pull-down
|
||||
temp_pullup |= (1 << pin);
|
||||
temp_pull_down &= ~(1 << pin);
|
||||
this->write_byte_16(REG_PULL_UP_B, temp_pullup);
|
||||
this->write_byte_16(REG_PULL_DOWN_B, temp_pull_down);
|
||||
} else {
|
||||
// If LOW do pull-down, disable pull-up
|
||||
temp_pull_down |= (1 << pin);
|
||||
temp_pullup &= ~(1 << pin);
|
||||
this->write_byte_16(REG_PULL_UP_B, temp_pullup);
|
||||
this->write_byte_16(REG_PULL_DOWN_B, temp_pull_down);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SX1509Component::pin_mode(uint8_t pin, uint8_t mode) {
|
||||
this->read_byte_16(REG_DIR_B, &this->ddr_mask_);
|
||||
if ((mode == SX1509_OUTPUT) || (mode == SX1509_ANALOG_OUTPUT))
|
||||
this->ddr_mask_ &= ~(1 << pin);
|
||||
else
|
||||
this->ddr_mask_ |= (1 << pin);
|
||||
this->write_byte_16(REG_DIR_B, this->ddr_mask_);
|
||||
|
||||
if (mode == INPUT_PULLUP)
|
||||
digital_write(pin, HIGH);
|
||||
|
||||
if (mode == SX1509_ANALOG_OUTPUT) {
|
||||
setup_led_driver_(pin);
|
||||
}
|
||||
}
|
||||
|
||||
void SX1509Component::setup_led_driver_(uint8_t pin) {
|
||||
uint16_t temp_word;
|
||||
uint8_t temp_byte;
|
||||
|
||||
this->read_byte_16(REG_INPUT_DISABLE_B, &temp_word);
|
||||
temp_word |= (1 << pin);
|
||||
this->write_byte_16(REG_INPUT_DISABLE_B, temp_word);
|
||||
|
||||
this->read_byte_16(REG_PULL_UP_B, &temp_word);
|
||||
temp_word &= ~(1 << pin);
|
||||
this->write_byte_16(REG_PULL_UP_B, temp_word);
|
||||
|
||||
this->ddr_mask_ &= ~(1 << pin); // 0=output
|
||||
this->write_byte_16(REG_DIR_B, this->ddr_mask_);
|
||||
|
||||
this->read_byte(REG_CLOCK, &temp_byte);
|
||||
temp_byte |= (1 << 6); // Internal 2MHz oscillator part 1 (set bit 6)
|
||||
temp_byte &= ~(1 << 5); // Internal 2MHz oscillator part 2 (clear bit 5)
|
||||
this->write_byte(REG_CLOCK, temp_byte);
|
||||
|
||||
this->read_byte(REG_MISC, &temp_byte);
|
||||
temp_byte &= ~(1 << 7); // set linear mode bank B
|
||||
temp_byte &= ~(1 << 3); // set linear mode bank A
|
||||
temp_byte |= 0x70; // Frequency of the LED Driver clock ClkX of all IOs:
|
||||
this->write_byte(REG_MISC, temp_byte);
|
||||
|
||||
this->read_byte_16(REG_LED_DRIVER_ENABLE_B, &temp_word);
|
||||
temp_word |= (1 << pin);
|
||||
this->write_byte_16(REG_LED_DRIVER_ENABLE_B, temp_word);
|
||||
|
||||
this->read_byte_16(REG_DATA_B, &temp_word);
|
||||
temp_word &= ~(1 << pin);
|
||||
this->write_byte_16(REG_DATA_B, temp_word);
|
||||
}
|
||||
|
||||
void SX1509Component::clock_(byte osc_source, byte osc_pin_function, byte osc_freq_out, byte osc_divider) {
|
||||
osc_source = (osc_source & 0b11) << 5; // 2-bit value, bits 6:5
|
||||
osc_pin_function = (osc_pin_function & 1) << 4; // 1-bit value bit 4
|
||||
osc_freq_out = (osc_freq_out & 0b1111); // 4-bit value, bits 3:0
|
||||
byte reg_clock = osc_source | osc_pin_function | osc_freq_out;
|
||||
this->write_byte(REG_CLOCK, reg_clock);
|
||||
|
||||
osc_divider = constrain(osc_divider, 1, 7);
|
||||
this->clk_x_ = 2000000;
|
||||
osc_divider = (osc_divider & 0b111) << 4; // 3-bit value, bits 6:4
|
||||
|
||||
uint8_t reg_misc;
|
||||
this->read_byte(REG_MISC, ®_misc);
|
||||
reg_misc &= ~(0b111 << 4);
|
||||
reg_misc |= osc_divider;
|
||||
this->write_byte(REG_MISC, reg_misc);
|
||||
}
|
||||
|
||||
void SX1509Component::setup_keypad_() {
|
||||
uint8_t temp_byte;
|
||||
|
||||
// setup row/col pins for INPUT OUTPUT
|
||||
this->read_byte_16(REG_DIR_B, &this->ddr_mask_);
|
||||
for (int i = 0; i < this->rows_; i++)
|
||||
this->ddr_mask_ &= ~(1 << i);
|
||||
for (int i = 8; i < (this->cols_ * 2); i++)
|
||||
this->ddr_mask_ |= (1 << i);
|
||||
this->write_byte_16(REG_DIR_B, this->ddr_mask_);
|
||||
|
||||
this->read_byte(REG_OPEN_DRAIN_A, &temp_byte);
|
||||
for (int i = 0; i < this->rows_; i++)
|
||||
temp_byte |= (1 << i);
|
||||
this->write_byte(REG_OPEN_DRAIN_A, temp_byte);
|
||||
|
||||
this->read_byte(REG_PULL_UP_B, &temp_byte);
|
||||
for (int i = 0; i < this->cols_; i++)
|
||||
temp_byte |= (1 << i);
|
||||
this->write_byte(REG_PULL_UP_B, temp_byte);
|
||||
|
||||
if (debounce_time_ >= scan_time_) {
|
||||
debounce_time_ = scan_time_ >> 1; // Force debounce_time to be less than scan_time
|
||||
}
|
||||
set_debounce_keypad_(debounce_time_, rows_, cols_);
|
||||
uint8_t scan_time_bits = 0;
|
||||
for (uint8_t i = 7; i > 0; i--) {
|
||||
if (scan_time_ & (1 << i)) {
|
||||
scan_time_bits = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
scan_time_bits &= 0b111; // Scan time is bits 2:0
|
||||
temp_byte = sleep_time_ | scan_time_bits;
|
||||
this->write_byte(REG_KEY_CONFIG_1, temp_byte);
|
||||
rows_ = (rows_ - 1) & 0b111; // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc.
|
||||
cols_ = (cols_ - 1) & 0b111; // 0b000 = 1 column, ob111 = 8 columns, etc.
|
||||
this->write_byte(REG_KEY_CONFIG_2, (rows_ << 3) | cols_);
|
||||
}
|
||||
|
||||
uint16_t SX1509Component::read_key_data() {
|
||||
uint16_t key_data;
|
||||
this->read_byte_16(REG_KEY_DATA_1, &key_data);
|
||||
return (0xFFFF ^ key_data);
|
||||
}
|
||||
|
||||
void SX1509Component::set_debounce_config_(uint8_t config_value) {
|
||||
// First make sure clock is configured
|
||||
uint8_t temp_byte;
|
||||
this->read_byte(REG_MISC, &temp_byte);
|
||||
temp_byte |= (1 << 4); // Just default to no divider if not set
|
||||
this->write_byte(REG_MISC, temp_byte);
|
||||
this->read_byte(REG_CLOCK, &temp_byte);
|
||||
temp_byte |= (1 << 6); // default to internal osc.
|
||||
this->write_byte(REG_CLOCK, temp_byte);
|
||||
|
||||
config_value &= 0b111; // 3-bit value
|
||||
this->write_byte(REG_DEBOUNCE_CONFIG, config_value);
|
||||
}
|
||||
|
||||
void SX1509Component::set_debounce_time_(uint8_t time) {
|
||||
uint8_t config_value = 0;
|
||||
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
if (time & (1 << i)) {
|
||||
config_value = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
config_value = constrain(config_value, 0, 7);
|
||||
|
||||
set_debounce_config_(config_value);
|
||||
}
|
||||
|
||||
void SX1509Component::set_debounce_enable_(uint8_t pin) {
|
||||
uint16_t debounce_enable;
|
||||
this->read_byte_16(REG_DEBOUNCE_ENABLE_B, &debounce_enable);
|
||||
debounce_enable |= (1 << pin);
|
||||
this->write_byte_16(REG_DEBOUNCE_ENABLE_B, debounce_enable);
|
||||
}
|
||||
|
||||
void SX1509Component::set_debounce_pin_(uint8_t pin) { set_debounce_enable_(pin); }
|
||||
|
||||
void SX1509Component::set_debounce_keypad_(uint8_t time, uint8_t num_rows, uint8_t num_cols) {
|
||||
set_debounce_time_(time);
|
||||
for (uint16_t i = 0; i < num_rows; i++)
|
||||
set_debounce_pin_(i);
|
||||
for (uint16_t i = 0; i < (8 + num_cols); i++)
|
||||
set_debounce_pin_(i);
|
||||
}
|
||||
|
||||
} // namespace sx1509
|
||||
} // namespace esphome
|
89
esphome/components/sx1509/sx1509.h
Normal file
89
esphome/components/sx1509/sx1509.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "sx1509_gpio_pin.h"
|
||||
#include "sx1509_registers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace sx1509 {
|
||||
|
||||
// These are used for clock config:
|
||||
const uint8_t INTERNAL_CLOCK_2MHZ = 2;
|
||||
const uint8_t EXTERNAL_CLOCK = 1;
|
||||
const uint8_t SOFTWARE_RESET = 0;
|
||||
const uint8_t HARDWARE_RESET = 1;
|
||||
|
||||
const uint8_t ANALOG_OUTPUT = 0x03; // To set a pin mode for PWM output
|
||||
|
||||
// PinModes for SX1509 pins
|
||||
enum SX1509GPIOMode : uint8_t {
|
||||
SX1509_INPUT = INPUT, // 0x00
|
||||
SX1509_INPUT_PULLUP = INPUT_PULLUP, // 0x02
|
||||
SX1509_ANALOG_OUTPUT = ANALOG_OUTPUT, // 0x03
|
||||
SX1509_OUTPUT = OUTPUT, // 0x01
|
||||
};
|
||||
|
||||
const uint8_t REG_I_ON[16] = {REG_I_ON_0, REG_I_ON_1, REG_I_ON_2, REG_I_ON_3, REG_I_ON_4, REG_I_ON_5,
|
||||
REG_I_ON_6, REG_I_ON_7, REG_I_ON_8, REG_I_ON_9, REG_I_ON_10, REG_I_ON_11,
|
||||
REG_I_ON_12, REG_I_ON_13, REG_I_ON_14, REG_I_ON_15};
|
||||
|
||||
// for all components that implement the process(uint16_t data )
|
||||
class SX1509Processor {
|
||||
public:
|
||||
virtual void process(uint16_t data){};
|
||||
};
|
||||
|
||||
class SX1509Component : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
SX1509Component() = default;
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void loop() override;
|
||||
|
||||
bool digital_read(uint8_t pin);
|
||||
uint16_t read_key_data();
|
||||
void set_pin_value(uint8_t pin, uint8_t i_on) { this->write_byte(REG_I_ON[pin], i_on); };
|
||||
void pin_mode(uint8_t pin, uint8_t mode);
|
||||
void digital_write(uint8_t pin, bool bit_value);
|
||||
u_long get_clock() { return this->clk_x_; };
|
||||
void set_rows_cols(uint8_t rows, uint8_t cols) {
|
||||
this->rows_ = rows;
|
||||
this->cols_ = cols;
|
||||
this->has_keypad_ = true;
|
||||
};
|
||||
void set_sleep_time(uint16_t sleep_time) { this->sleep_time_ = sleep_time; };
|
||||
void set_scan_time(uint8_t scan_time) { this->scan_time_ = scan_time; };
|
||||
void set_debounce_time(uint8_t debounce_time = 1) { this->debounce_time_ = debounce_time; };
|
||||
void register_keypad_binary_sensor(SX1509Processor *binary_sensor) {
|
||||
this->keypad_binary_sensors_.push_back(binary_sensor);
|
||||
};
|
||||
|
||||
protected:
|
||||
u_long clk_x_ = 2000000;
|
||||
uint8_t frequency_ = 0;
|
||||
uint16_t ddr_mask_ = 0x00;
|
||||
uint16_t input_mask_ = 0x00;
|
||||
uint16_t port_mask_ = 0x00;
|
||||
bool has_keypad_ = false;
|
||||
uint8_t rows_ = 0;
|
||||
uint8_t cols_ = 0;
|
||||
uint16_t sleep_time_ = 128;
|
||||
uint8_t scan_time_ = 1;
|
||||
uint8_t debounce_time_ = 1;
|
||||
std::vector<SX1509Processor *> keypad_binary_sensors_;
|
||||
|
||||
void setup_keypad_();
|
||||
void set_debounce_config_(uint8_t config_value);
|
||||
void set_debounce_time_(uint8_t time);
|
||||
void set_debounce_pin_(uint8_t pin);
|
||||
void set_debounce_enable_(uint8_t pin);
|
||||
void set_debounce_keypad_(uint8_t time, uint8_t num_rows, uint8_t num_cols);
|
||||
void setup_led_driver_(uint8_t pin);
|
||||
void clock_(uint8_t osc_source = 2, uint8_t osc_pin_function = 1, uint8_t osc_freq_out = 0, uint8_t osc_divider = 0);
|
||||
};
|
||||
|
||||
} // namespace sx1509
|
||||
} // namespace esphome
|
20
esphome/components/sx1509/sx1509_gpio_pin.cpp
Normal file
20
esphome/components/sx1509/sx1509_gpio_pin.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "sx1509_gpio_pin.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace sx1509 {
|
||||
|
||||
static const char *TAG = "sx1509_gpio_pin";
|
||||
|
||||
void SX1509GPIOPin::setup() {
|
||||
ESP_LOGD(TAG, "setup pin %d", this->pin_);
|
||||
this->parent_->pin_mode(this->pin_, this->mode_);
|
||||
}
|
||||
|
||||
void SX1509GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); }
|
||||
bool SX1509GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
|
||||
void SX1509GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }
|
||||
|
||||
} // namespace sx1509
|
||||
} // namespace esphome
|
24
esphome/components/sx1509/sx1509_gpio_pin.h
Normal file
24
esphome/components/sx1509/sx1509_gpio_pin.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "sx1509.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace sx1509 {
|
||||
|
||||
class SX1509Component;
|
||||
|
||||
class SX1509GPIOPin : public GPIOPin {
|
||||
public:
|
||||
SX1509GPIOPin(SX1509Component *parent, uint8_t pin, uint8_t mode, bool inverted = false)
|
||||
: GPIOPin(pin, mode, inverted), parent_(parent){};
|
||||
void setup() override;
|
||||
void pin_mode(uint8_t mode) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
|
||||
protected:
|
||||
SX1509Component *parent_;
|
||||
};
|
||||
|
||||
} // namespace sx1509
|
||||
} // namespace esphome
|
109
esphome/components/sx1509/sx1509_registers.h
Normal file
109
esphome/components/sx1509/sx1509_registers.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/******************************************************************************
|
||||
sx1509_registers.h
|
||||
Register definitions for SX1509.
|
||||
Jim Lindblom @ SparkFun Electronics
|
||||
Original Creation Date: September 21, 2015
|
||||
https://github.com/sparkfun/SparkFun_SX1509_Arduino_Library
|
||||
|
||||
Here you'll find the Arduino code used to interface with the SX1509 I2C
|
||||
16 I/O expander. There are functions to take advantage of everything the
|
||||
SX1509 provides - input/output setting, writing pins high/low, reading
|
||||
the input value of pins, LED driver utilities (blink, breath, pwm), and
|
||||
keypad engine utilites.
|
||||
|
||||
Development environment specifics:
|
||||
IDE: Arduino 1.6.5
|
||||
Hardware Platform: Arduino Uno
|
||||
SX1509 Breakout Version: v2.0
|
||||
|
||||
This code is beerware; if you see me (or any other SparkFun employee) at the
|
||||
local, and you've found our code helpful, please buy us a round!
|
||||
|
||||
Distributed as-is; no warranty is given.
|
||||
******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
namespace esphome {
|
||||
namespace sx1509 {
|
||||
|
||||
const uint8_t REG_INPUT_DISABLE_B =
|
||||
0x00; // RegInputDisableB Input buffer disable register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_INPUT_DISABLE_A =
|
||||
0x01; // RegInputDisableA Input buffer disable register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_LONG_SLEW_B =
|
||||
0x02; // RegLongSlewB Output buffer long slew register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_LONG_SLEW_A = 0x03; // RegLongSlewA Output buffer long slew register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_LOW_DRIVE_B =
|
||||
0x04; // RegLowDriveB Output buffer low drive register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_LOW_DRIVE_A = 0x05; // RegLowDriveA Output buffer low drive register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_PULL_UP_B = 0x06; // RegPullUpB Pull_up register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_PULL_UP_A = 0x07; // RegPullUpA Pull_up register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_PULL_DOWN_B = 0x08; // RegPullDownB Pull_down register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_PULL_DOWN_A = 0x09; // RegPullDownA Pull_down register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_OPEN_DRAIN_B = 0x0A; // RegOpenDrainB Open drain register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_OPEN_DRAIN_A = 0x0B; // RegOpenDrainA Open drain register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_POLARITY_B = 0x0C; // RegPolarityB Polarity register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_POLARITY_A = 0x0D; // RegPolarityA Polarity register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_DIR_B = 0x0E; // RegDirB Direction register _ I/O[15_8] (Bank B) 1111 1111
|
||||
const uint8_t REG_DIR_A = 0x0F; // RegDirA Direction register _ I/O[7_0] (Bank A) 1111 1111
|
||||
const uint8_t REG_DATA_B = 0x10; // RegDataB Data register _ I/O[15_8] (Bank B) 1111 1111*
|
||||
const uint8_t REG_DATA_A = 0x11; // RegDataA Data register _ I/O[7_0] (Bank A) 1111 1111*
|
||||
const uint8_t REG_INTERRUPT_MASK_B =
|
||||
0x12; // RegInterruptMaskB Interrupt mask register _ I/O[15_8] (Bank B) 1111 1111
|
||||
const uint8_t REG_INTERRUPT_MASK_A =
|
||||
0x13; // RegInterruptMaskA Interrupt mask register _ I/O[7_0] (Bank A) 1111 1111
|
||||
const uint8_t REG_SENSE_HIGH_B = 0x14; // RegSenseHighB Sense register for I/O[15:12] 0000 0000
|
||||
const uint8_t REG_SENSE_LOW_B = 0x15; // RegSenseLowB Sense register for I/O[11:8] 0000 0000
|
||||
const uint8_t REG_SENSE_HIGH_A = 0x16; // RegSenseHighA Sense register for I/O[7:4] 0000 0000
|
||||
const uint8_t REG_SENSE_LOW_A = 0x17; // RegSenseLowA Sense register for I/O[3:0] 0000 0000
|
||||
const uint8_t REG_INTERRUPT_SOURCE_B =
|
||||
0x18; // RegInterruptSourceB Interrupt source register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_INTERRUPT_SOURCE_A =
|
||||
0x19; // RegInterruptSourceA Interrupt source register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_EVENT_STATUS_B = 0x1A; // RegEventStatusB Event status register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_EVENT_STATUS_A = 0x1B; // RegEventStatusA Event status register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_LEVEL_SHIFTER_1 = 0x1C; // RegLevelShifter1 Level shifter register 0000 0000
|
||||
const uint8_t REG_LEVEL_SHIFTER_2 = 0x1D; // RegLevelShifter2 Level shifter register 0000 0000
|
||||
const uint8_t REG_CLOCK = 0x1E; // RegClock Clock management register 0000 0000
|
||||
const uint8_t REG_MISC = 0x1F; // RegMisc Miscellaneous device settings register 0000 0000
|
||||
const uint8_t REG_LED_DRIVER_ENABLE_B =
|
||||
0x20; // RegLEDDriverEnableB LED driver enable register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_LED_DRIVER_ENABLE_A =
|
||||
0x21; // RegLEDDriverEnableA LED driver enable register _ I/O[7_0] (Bank A) 0000 0000
|
||||
// Debounce and Keypad Engine
|
||||
const uint8_t REG_DEBOUNCE_CONFIG = 0x22; // RegDebounceConfig Debounce configuration register 0000 0000
|
||||
const uint8_t REG_DEBOUNCE_ENABLE_B =
|
||||
0x23; // RegDebounceEnableB Debounce enable register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_DEBOUNCE_ENABLE_A =
|
||||
0x24; // RegDebounceEnableA Debounce enable register _ I/O[7_0] (Bank A) 0000 0000
|
||||
const uint8_t REG_KEY_CONFIG_1 = 0x25; // RegKeyConfig1 Key scan configuration register 0000 0000
|
||||
const uint8_t REG_KEY_CONFIG_2 = 0x26; // RegKeyConfig2 Key scan configuration register 0000 0000
|
||||
const uint8_t REG_KEY_DATA_1 = 0x27; // RegKeyData1 Key value (column) 1111 1111
|
||||
const uint8_t REG_KEY_DATA_2 = 0x28; // RegKeyData2 Key value (row) 1111 1111
|
||||
// LED Driver (PWM, blinking, breathing)
|
||||
const uint8_t REG_I_ON_0 = 0x2A; // RegIOn0 ON intensity register for I/O[0] 1111 1111
|
||||
const uint8_t REG_I_ON_1 = 0x2D; // RegIOn1 ON intensity register for I/O[1] 1111 1111
|
||||
const uint8_t REG_I_ON_2 = 0x30; // RegIOn2 ON intensity register for I/O[2] 1111 1111
|
||||
const uint8_t REG_I_ON_3 = 0x33; // RegIOn3 ON intensity register for I/O[3] 1111 1111
|
||||
const uint8_t REG_I_ON_4 = 0x36; // RegIOn4 ON intensity register for I/O[4] 1111 1111
|
||||
const uint8_t REG_I_ON_5 = 0x3B; // RegIOn5 ON intensity register for I/O[5] 1111 1111
|
||||
const uint8_t REG_I_ON_6 = 0x40; // RegIOn6 ON intensity register for I/O[6] 1111 1111
|
||||
const uint8_t REG_I_ON_7 = 0x45; // RegIOn7 ON intensity register for I/O[7] 1111 1111
|
||||
const uint8_t REG_I_ON_8 = 0x4A; // RegIOn8 ON intensity register for I/O[8] 1111 1111
|
||||
const uint8_t REG_I_ON_9 = 0x4D; // RegIOn9 ON intensity register for I/O[9] 1111 1111
|
||||
const uint8_t REG_I_ON_10 = 0x50; // RegIOn10 ON intensity register for I/O[10] 1111 1111
|
||||
const uint8_t REG_I_ON_11 = 0x53; // RegIOn11 ON intensity register for I/O[11] 1111 1111
|
||||
const uint8_t REG_I_ON_12 = 0x56; // RegIOn12 ON intensity register for I/O[12] 1111 1111
|
||||
const uint8_t REG_I_ON_13 = 0x5B; // RegIOn13 ON intensity register for I/O[13] 1111 1111
|
||||
const uint8_t REG_I_ON_14 = 0x60; // RegIOn14 ON intensity register for I/O[14] 1111 1111
|
||||
const uint8_t REG_I_ON_15 = 0x65; // RegIOn15 ON intensity register for I/O[15] 1111 1111
|
||||
// Miscellaneous
|
||||
const uint8_t REG_HIGH_INPUT_B = 0x69; // RegHighInputB High input enable register _ I/O[15_8] (Bank B) 0000 0000
|
||||
const uint8_t REG_HIGH_INPUT_A = 0x6A; // RegHighInputA High input enable register _ I/O[7_0] (Bank A) 0000 0000
|
||||
// Software Reset
|
||||
const uint8_t REG_RESET = 0x7D; // RegReset Software reset register 0000 0000
|
||||
const uint8_t REG_TEST_1 = 0x7E; // RegTest1 Test register 0000 0000
|
||||
const uint8_t REG_TEST_2 = 0x7F; // RegTest2 Test register 0000 0000
|
||||
|
||||
} // namespace sx1509
|
||||
} // namespace esphome
|
Loading…
Reference in a new issue