[matrix_keypad] Keep the keys in flash (#20110)

This commit is contained in:
J. Nick Koston
2026-10-05 12:26:42 +13:00
committed by GitHub
parent ad1b6ab37e
commit 0017b9c439
6 changed files with 66 additions and 8 deletions
+9 -2
View File
@@ -27,6 +27,10 @@ CONF_HAS_PULLDOWNS = "has_pulldowns"
def check_keys(obj: ConfigType) -> ConfigType:
for ch in obj.get(CONF_KEYS, ""):
if not ch.isascii():
# Each key is reported as one byte, so only ASCII characters can be key codes
raise cv.Invalid(f"Key code {ch!r} is not an ASCII character")
if CONF_KEYS in obj and len(obj[CONF_KEYS]) != len(obj[CONF_ROWS]) * len(
obj[CONF_COLUMNS]
):
@@ -74,8 +78,11 @@ async def to_code(config: ConfigType) -> None:
pin = await cg.gpio_pin_expression(conf[CONF_PIN])
col_pins.append(pin)
cg.add(var.set_columns(col_pins))
if CONF_KEYS in config:
cg.add(var.set_keys(config[CONF_KEYS]))
if (keys := config.get(CONF_KEYS)) is not None:
table = cg.shared_progmem_array(
"matrix_keypad_keys", cg.uint8, list(keys.encode())
)
cg.add(var.set_keys(table))
cg.add(var.set_debounce_time(config[CONF_DEBOUNCE_TIME]))
if CONF_HAS_DIODES in config:
cg.add(var.set_has_diodes(config[CONF_HAS_DIODES]))
@@ -58,8 +58,8 @@ void MatrixKeypad::loop() {
ESP_LOGD(TAG, "key @ row %d, col %d released", row, col);
for (auto &listener : this->listeners_)
listener->button_released(row, col);
if (this->pressed_key_ < (int) this->keys_.size()) {
uint8_t keycode = this->keys_[this->pressed_key_];
if (this->keys_ != nullptr) {
uint8_t keycode = progmem_read_byte(&this->keys_[this->pressed_key_]);
ESP_LOGD(TAG, "key '%c' released", keycode);
for (auto &listener : this->listeners_)
listener->key_released(keycode);
@@ -81,8 +81,8 @@ void MatrixKeypad::loop() {
ESP_LOGD(TAG, "key @ row %d, col %d pressed", row, col);
for (auto &listener : this->listeners_)
listener->button_pressed(row, col);
if (key < (int) this->keys_.size()) {
uint8_t keycode = this->keys_[key];
if (this->keys_ != nullptr) {
uint8_t keycode = progmem_read_byte(&this->keys_[key]);
ESP_LOGD(TAG, "key '%c' pressed", keycode);
for (auto &trigger : this->key_triggers_)
trigger->trigger(keycode);
@@ -27,7 +27,8 @@ class MatrixKeypad final : public key_provider::KeyProvider, public Component {
void dump_config() override;
void set_columns(std::vector<GPIOPin *> pins) { columns_ = std::move(pins); };
void set_rows(std::vector<GPIOPin *> pins) { rows_ = std::move(pins); };
void set_keys(std::string keys) { keys_ = std::move(keys); };
/// `keys` is a codegen PROGMEM table with one key code per button.
void set_keys(const uint8_t *keys) { this->keys_ = keys; }
void set_debounce_time(uint32_t debounce_time) { debounce_time_ = debounce_time; };
void set_has_diodes(bool has_diodes) { has_diodes_ = has_diodes; };
void set_has_pulldowns(bool has_pulldowns) { has_pulldowns_ = has_pulldowns; };
@@ -38,7 +39,7 @@ class MatrixKeypad final : public key_provider::KeyProvider, public Component {
protected:
std::vector<GPIOPin *> rows_;
std::vector<GPIOPin *> columns_;
std::string keys_;
const uint8_t *keys_{nullptr};
uint32_t debounce_time_ = 0;
bool has_diodes_{false};
bool has_pulldowns_{false};
@@ -0,0 +1,23 @@
esphome:
name: test
esp8266:
board: esp01_1m
matrix_keypad:
- id: keypad_a
rows:
- pin: 12
- pin: 13
columns:
- pin: 14
- pin: 15
keys: "1234"
- id: keypad_b
rows:
- pin: 4
- pin: 5
columns:
- pin: 0
- pin: 2
keys: "1234"
@@ -0,0 +1,27 @@
"""Matrix keypad key codes live in a shared PROGMEM table instead of a std::string."""
from collections.abc import Callable
from pathlib import Path
import pytest
from esphome.components.matrix_keypad import check_keys
import esphome.config_validation as cv
def test_keys_use_shared_progmem_table(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
main_cpp = generate_main(component_config_path("keys.yaml"))
assert (
"static constexpr uint8_t matrix_keypad_keys[] PROGMEM = {49, 50, 51, 52};"
in main_cpp
)
assert main_cpp.count("->set_keys(matrix_keypad_keys);") == 2
def test_non_ascii_keys_are_rejected() -> None:
with pytest.raises(cv.Invalid, match="'é' is not an ASCII"):
check_keys({"keys": "1é34"})