mirror of
https://github.com/esphome/esphome.git
synced 2025-07-29 14:46:40 +00:00
[sx1509] add support for keys (#8413)
Co-authored-by: Samuel Sieb <samuel@sieb.net> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
95a17387a8
commit
361de22370
@ -1,6 +1,6 @@
|
|||||||
from esphome import pins
|
from esphome import automation, pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import i2c
|
from esphome.components import i2c, key_provider
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
@ -8,13 +8,16 @@ from esphome.const import (
|
|||||||
CONF_INVERTED,
|
CONF_INVERTED,
|
||||||
CONF_MODE,
|
CONF_MODE,
|
||||||
CONF_NUMBER,
|
CONF_NUMBER,
|
||||||
|
CONF_ON_KEY,
|
||||||
CONF_OPEN_DRAIN,
|
CONF_OPEN_DRAIN,
|
||||||
CONF_OUTPUT,
|
CONF_OUTPUT,
|
||||||
CONF_PULLDOWN,
|
CONF_PULLDOWN,
|
||||||
CONF_PULLUP,
|
CONF_PULLUP,
|
||||||
|
CONF_TRIGGER_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
CONF_KEYPAD = "keypad"
|
CONF_KEYPAD = "keypad"
|
||||||
|
CONF_KEYS = "keys"
|
||||||
CONF_KEY_ROWS = "key_rows"
|
CONF_KEY_ROWS = "key_rows"
|
||||||
CONF_KEY_COLUMNS = "key_columns"
|
CONF_KEY_COLUMNS = "key_columns"
|
||||||
CONF_SLEEP_TIME = "sleep_time"
|
CONF_SLEEP_TIME = "sleep_time"
|
||||||
@ -22,22 +25,47 @@ CONF_SCAN_TIME = "scan_time"
|
|||||||
CONF_DEBOUNCE_TIME = "debounce_time"
|
CONF_DEBOUNCE_TIME = "debounce_time"
|
||||||
CONF_SX1509_ID = "sx1509_id"
|
CONF_SX1509_ID = "sx1509_id"
|
||||||
|
|
||||||
|
AUTO_LOAD = ["key_provider"]
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
sx1509_ns = cg.esphome_ns.namespace("sx1509")
|
sx1509_ns = cg.esphome_ns.namespace("sx1509")
|
||||||
|
|
||||||
SX1509Component = sx1509_ns.class_("SX1509Component", cg.Component, i2c.I2CDevice)
|
SX1509Component = sx1509_ns.class_(
|
||||||
|
"SX1509Component", cg.Component, i2c.I2CDevice, key_provider.KeyProvider
|
||||||
|
)
|
||||||
SX1509GPIOPin = sx1509_ns.class_("SX1509GPIOPin", cg.GPIOPin)
|
SX1509GPIOPin = sx1509_ns.class_("SX1509GPIOPin", cg.GPIOPin)
|
||||||
|
SX1509KeyTrigger = sx1509_ns.class_(
|
||||||
|
"SX1509KeyTrigger", automation.Trigger.template(cg.uint8)
|
||||||
|
)
|
||||||
|
|
||||||
KEYPAD_SCHEMA = cv.Schema(
|
|
||||||
{
|
def check_keys(config):
|
||||||
cv.Required(CONF_KEY_ROWS): cv.int_range(min=1, max=8),
|
if CONF_KEYS in config:
|
||||||
cv.Required(CONF_KEY_COLUMNS): cv.int_range(min=1, max=8),
|
if len(config[CONF_KEYS]) != config[CONF_KEY_ROWS] * config[CONF_KEY_COLUMNS]:
|
||||||
cv.Optional(CONF_SLEEP_TIME): cv.int_range(min=128, max=8192),
|
raise cv.Invalid(
|
||||||
cv.Optional(CONF_SCAN_TIME): cv.int_range(min=1, max=128),
|
"The number of key codes must equal the number of rows * columns"
|
||||||
cv.Optional(CONF_DEBOUNCE_TIME): cv.int_range(min=1, max=64),
|
)
|
||||||
}
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
KEYPAD_SCHEMA = cv.All(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_KEY_ROWS): cv.int_range(min=2, 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),
|
||||||
|
cv.Optional(CONF_KEYS): cv.string,
|
||||||
|
cv.Optional(CONF_ON_KEY): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SX1509KeyTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
check_keys,
|
||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
@ -56,17 +84,22 @@ async def to_code(config):
|
|||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
if CONF_KEYPAD in config:
|
if conf := config.get(CONF_KEYPAD):
|
||||||
keypad = config[CONF_KEYPAD]
|
cg.add(var.set_rows_cols(conf[CONF_KEY_ROWS], conf[CONF_KEY_COLUMNS]))
|
||||||
cg.add(var.set_rows_cols(keypad[CONF_KEY_ROWS], keypad[CONF_KEY_COLUMNS]))
|
|
||||||
if (
|
if (
|
||||||
CONF_SLEEP_TIME in keypad
|
CONF_SLEEP_TIME in conf
|
||||||
and CONF_SCAN_TIME in keypad
|
and CONF_SCAN_TIME in conf
|
||||||
and CONF_DEBOUNCE_TIME in keypad
|
and CONF_DEBOUNCE_TIME in conf
|
||||||
):
|
):
|
||||||
cg.add(var.set_sleep_time(keypad[CONF_SLEEP_TIME]))
|
cg.add(var.set_sleep_time(conf[CONF_SLEEP_TIME]))
|
||||||
cg.add(var.set_scan_time(keypad[CONF_SCAN_TIME]))
|
cg.add(var.set_scan_time(conf[CONF_SCAN_TIME]))
|
||||||
cg.add(var.set_debounce_time(keypad[CONF_DEBOUNCE_TIME]))
|
cg.add(var.set_debounce_time(conf[CONF_DEBOUNCE_TIME]))
|
||||||
|
if keys := conf.get(CONF_KEYS):
|
||||||
|
cg.add(var.set_keys(keys))
|
||||||
|
for tconf in conf.get(CONF_ON_KEY, []):
|
||||||
|
trigger = cg.new_Pvariable(tconf[CONF_TRIGGER_ID])
|
||||||
|
cg.add(var.register_key_trigger(trigger))
|
||||||
|
await automation.build_automation(trigger, [(cg.uint8, "x")], tconf)
|
||||||
|
|
||||||
|
|
||||||
def validate_mode(value):
|
def validate_mode(value):
|
||||||
|
@ -48,6 +48,30 @@ void SX1509Component::loop() {
|
|||||||
uint16_t key_data = this->read_key_data();
|
uint16_t key_data = this->read_key_data();
|
||||||
for (auto *binary_sensor : this->keypad_binary_sensors_)
|
for (auto *binary_sensor : this->keypad_binary_sensors_)
|
||||||
binary_sensor->process(key_data);
|
binary_sensor->process(key_data);
|
||||||
|
if (this->keys_.empty())
|
||||||
|
return;
|
||||||
|
if (key_data == 0) {
|
||||||
|
this->last_key_ = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int row, col;
|
||||||
|
for (row = 0; row < 7; row++) {
|
||||||
|
if (key_data & (1 << row))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (col = 8; col < 15; col++) {
|
||||||
|
if (key_data & (1 << col))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
col -= 8;
|
||||||
|
uint8_t key = this->keys_[row * this->cols_ + col];
|
||||||
|
if (key == this->last_key_)
|
||||||
|
return;
|
||||||
|
this->last_key_ = key;
|
||||||
|
ESP_LOGV(TAG, "row %d, col %d, key '%c'", row, col, key);
|
||||||
|
for (auto &trigger : this->key_triggers_)
|
||||||
|
trigger->trigger(key);
|
||||||
|
this->send_key_(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,9 +254,9 @@ void SX1509Component::setup_keypad_() {
|
|||||||
scan_time_bits &= 0b111; // Scan time is bits 2:0
|
scan_time_bits &= 0b111; // Scan time is bits 2:0
|
||||||
temp_byte = sleep_time_ | scan_time_bits;
|
temp_byte = sleep_time_ | scan_time_bits;
|
||||||
this->write_byte(REG_KEY_CONFIG_1, temp_byte);
|
this->write_byte(REG_KEY_CONFIG_1, temp_byte);
|
||||||
rows_ = (rows_ - 1) & 0b111; // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc.
|
temp_byte = ((this->rows_ - 1) & 0b111) << 3; // 0 = off, 0b001 = 2 rows, 0b111 = 8 rows, etc.
|
||||||
cols_ = (cols_ - 1) & 0b111; // 0b000 = 1 column, ob111 = 8 columns, etc.
|
temp_byte |= (this->cols_ - 1) & 0b111; // 0b000 = 1 column, ob111 = 8 columns, etc.
|
||||||
this->write_byte(REG_KEY_CONFIG_2, (rows_ << 3) | cols_);
|
this->write_byte(REG_KEY_CONFIG_2, temp_byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t SX1509Component::read_key_data() {
|
uint16_t SX1509Component::read_key_data() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/key_provider/key_provider.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "sx1509_gpio_pin.h"
|
#include "sx1509_gpio_pin.h"
|
||||||
@ -27,7 +28,9 @@ class SX1509Processor {
|
|||||||
virtual void process(uint16_t data){};
|
virtual void process(uint16_t data){};
|
||||||
};
|
};
|
||||||
|
|
||||||
class SX1509Component : public Component, public i2c::I2CDevice {
|
class SX1509KeyTrigger : public Trigger<uint8_t> {};
|
||||||
|
|
||||||
|
class SX1509Component : public Component, public i2c::I2CDevice, public key_provider::KeyProvider {
|
||||||
public:
|
public:
|
||||||
SX1509Component() = default;
|
SX1509Component() = default;
|
||||||
|
|
||||||
@ -47,12 +50,14 @@ class SX1509Component : public Component, public i2c::I2CDevice {
|
|||||||
this->cols_ = cols;
|
this->cols_ = cols;
|
||||||
this->has_keypad_ = true;
|
this->has_keypad_ = true;
|
||||||
};
|
};
|
||||||
|
void set_keys(std::string keys) { this->keys_ = std::move(keys); };
|
||||||
void set_sleep_time(uint16_t sleep_time) { this->sleep_time_ = sleep_time; };
|
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_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 set_debounce_time(uint8_t debounce_time = 1) { this->debounce_time_ = debounce_time; };
|
||||||
void register_keypad_binary_sensor(SX1509Processor *binary_sensor) {
|
void register_keypad_binary_sensor(SX1509Processor *binary_sensor) {
|
||||||
this->keypad_binary_sensors_.push_back(binary_sensor);
|
this->keypad_binary_sensors_.push_back(binary_sensor);
|
||||||
}
|
}
|
||||||
|
void register_key_trigger(SX1509KeyTrigger *trig) { this->key_triggers_.push_back(trig); };
|
||||||
void setup_led_driver(uint8_t pin);
|
void setup_led_driver(uint8_t pin);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -65,10 +70,13 @@ class SX1509Component : public Component, public i2c::I2CDevice {
|
|||||||
bool has_keypad_ = false;
|
bool has_keypad_ = false;
|
||||||
uint8_t rows_ = 0;
|
uint8_t rows_ = 0;
|
||||||
uint8_t cols_ = 0;
|
uint8_t cols_ = 0;
|
||||||
|
std::string keys_;
|
||||||
uint16_t sleep_time_ = 128;
|
uint16_t sleep_time_ = 128;
|
||||||
uint8_t scan_time_ = 1;
|
uint8_t scan_time_ = 1;
|
||||||
uint8_t debounce_time_ = 1;
|
uint8_t debounce_time_ = 1;
|
||||||
|
uint8_t last_key_ = 0;
|
||||||
std::vector<SX1509Processor *> keypad_binary_sensors_;
|
std::vector<SX1509Processor *> keypad_binary_sensors_;
|
||||||
|
std::vector<SX1509KeyTrigger *> key_triggers_;
|
||||||
|
|
||||||
uint32_t last_loop_timestamp_ = 0;
|
uint32_t last_loop_timestamp_ = 0;
|
||||||
const uint32_t min_loop_period_ = 15; // ms
|
const uint32_t min_loop_period_ = 15; // ms
|
||||||
|
@ -6,6 +6,12 @@ i2c:
|
|||||||
sx1509:
|
sx1509:
|
||||||
- id: sx1509_hub
|
- id: sx1509_hub
|
||||||
address: 0x3E
|
address: 0x3E
|
||||||
|
keypad:
|
||||||
|
key_rows: 2
|
||||||
|
key_columns: 2
|
||||||
|
keys: abcd
|
||||||
|
on_key:
|
||||||
|
- lambda: ESP_LOGD("test", "got key '%c'", x);
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
@ -13,6 +19,11 @@ binary_sensor:
|
|||||||
pin:
|
pin:
|
||||||
sx1509: sx1509_hub
|
sx1509: sx1509_hub
|
||||||
number: 3
|
number: 3
|
||||||
|
- platform: sx1509
|
||||||
|
sx1509_id: sx1509_hub
|
||||||
|
name: "keypadkey_0"
|
||||||
|
row: 0
|
||||||
|
col: 0
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
|
Loading…
x
Reference in New Issue
Block a user