diff --git a/esphome/components/tm1637/binary_sensor.py b/esphome/components/tm1637/binary_sensor.py new file mode 100644 index 0000000000..66b5172358 --- /dev/null +++ b/esphome/components/tm1637/binary_sensor.py @@ -0,0 +1,26 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_ID, CONF_KEY + +CONF_TM1637_ID = "tm1637_id" + +tm1637_ns = cg.esphome_ns.namespace("tm1637") +TM1637Display = tm1637_ns.class_("TM1637Display", cg.PollingComponent) +TM1637Key = tm1637_ns.class_("TM1637Key", binary_sensor.BinarySensor) + +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TM1637Key), + cv.GenerateID(CONF_TM1637_ID): cv.use_id(TM1637Display), + cv.Required(CONF_KEY): cv.int_range(min=0, max=15), + } +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await binary_sensor.register_binary_sensor(var, config) + cg.add(var.set_keycode(config[CONF_KEY])) + hub = await cg.get_variable(config[CONF_TM1637_ID]) + cg.add(hub.add_tm1637_key(var)) diff --git a/esphome/components/tm1637/tm1637.cpp b/esphome/components/tm1637/tm1637.cpp index 44f0a841b8..be2192ea22 100644 --- a/esphome/components/tm1637/tm1637.cpp +++ b/esphome/components/tm1637/tm1637.cpp @@ -7,11 +7,17 @@ namespace esphome { namespace tm1637 { static const char *const TAG = "display.tm1637"; -const uint8_t TM1637_I2C_COMM1 = 0x40; -const uint8_t TM1637_I2C_COMM2 = 0xC0; -const uint8_t TM1637_I2C_COMM3 = 0x80; +const uint8_t TM1637_CMD_DATA = 0x40; //!< Display data command +const uint8_t TM1637_CMD_CTRL = 0x80; //!< Display control command +const uint8_t TM1637_CMD_ADDR = 0xc0; //!< Display address command const uint8_t TM1637_UNKNOWN_CHAR = 0b11111111; +// Data command bits +const uint8_t TM1637_DATA_WRITE = 0x00; //!< Write data +const uint8_t TM1637_DATA_READ_KEYS = 0x02; //!< Read keys +const uint8_t TM1637_DATA_AUTO_INC_ADDR = 0x00; //!< Auto increment address +const uint8_t TM1637_DATA_FIXED_ADDR = 0x04; //!< Fixed address + // // A // --- @@ -138,6 +144,36 @@ void TM1637Display::dump_config() { LOG_UPDATE_INTERVAL(this); } +#ifdef USE_BINARY_SENSOR +void TM1637Display::loop() { + uint8_t val = this->get_keys(); + for (auto *tm1637_key : this->tm1637_keys_) + tm1637_key->process(val); +} + +uint8_t TM1637Display::get_keys() { + this->start_(); + this->send_byte_(TM1637_CMD_DATA | TM1637_DATA_READ_KEYS); + this->start_(); + uint8_t key_code = read_byte_(); + this->stop_(); + if (key_code != 0xFF) { + // Invert key_code: + // Bit | 7 6 5 4 3 2 1 0 + // ------+------------------------- + // From | S0 S1 S2 K1 K2 1 1 1 + // To | S0 S1 S2 K1 K2 0 0 0 + key_code = ~key_code; + // Shift bits to: + // Bit | 7 6 5 4 3 2 1 0 + // ------+------------------------ + // To | 0 0 0 0 K2 S2 S1 S0 + key_code = (uint8_t)((key_code & 0x80) >> 7 | (key_code & 0x40) >> 5 | (key_code & 0x20) >> 3 | (key_code & 0x08)); + } + return key_code; +} +#endif + void TM1637Display::update() { for (uint8_t &i : this->buffer_) i = 0; @@ -165,14 +201,14 @@ void TM1637Display::stop_() { void TM1637Display::display() { ESP_LOGVV(TAG, "Display %02X%02X%02X%02X", buffer_[0], buffer_[1], buffer_[2], buffer_[3]); - // Write COMM1 + // Write DATA CMND this->start_(); - this->send_byte_(TM1637_I2C_COMM1); + this->send_byte_(TM1637_CMD_DATA); this->stop_(); - // Write COMM2 + first digit address + // Write ADDR CMD + first digit address this->start_(); - this->send_byte_(TM1637_I2C_COMM2); + this->send_byte_(TM1637_CMD_ADDR); // Write the data bytes if (this->inverted_) { @@ -187,20 +223,17 @@ void TM1637Display::display() { this->stop_(); - // Write COMM3 + brightness + // Write display CTRL CMND + brightness this->start_(); - this->send_byte_(TM1637_I2C_COMM3 + ((this->intensity_ & 0x7) | 0x08)); + this->send_byte_(TM1637_CMD_CTRL + ((this->intensity_ & 0x7) | 0x08)); this->stop_(); } bool TM1637Display::send_byte_(uint8_t b) { uint8_t data = b; - - // 8 Data Bits for (uint8_t i = 0; i < 8; i++) { // CLK low this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); this->bit_delay_(); - // Set data bit if (data & 0x01) { this->dio_pin_->pin_mode(gpio::FLAG_INPUT); @@ -209,19 +242,16 @@ bool TM1637Display::send_byte_(uint8_t b) { } this->bit_delay_(); - // CLK high this->clk_pin_->pin_mode(gpio::FLAG_INPUT); this->bit_delay_(); data = data >> 1; } - // Wait for acknowledge // CLK to zero this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); this->dio_pin_->pin_mode(gpio::FLAG_INPUT); this->bit_delay_(); - // CLK to high this->clk_pin_->pin_mode(gpio::FLAG_INPUT); this->bit_delay_(); @@ -237,8 +267,38 @@ bool TM1637Display::send_byte_(uint8_t b) { return ack; } +uint8_t TM1637Display::read_byte_() { + uint8_t retval = 0; + // Prepare DIO to read data + this->dio_pin_->pin_mode(gpio::FLAG_INPUT); + this->bit_delay_(); + // Data is shifted out by the TM1637 on the CLK falling edge + for (uint8_t bit = 0; bit < 8; bit++) { + this->clk_pin_->pin_mode(gpio::FLAG_INPUT); + this->bit_delay_(); + // Read next bit + retval <<= 1; + if (this->dio_pin_->digital_read()) { + retval |= 0x01; + } + this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->bit_delay_(); + } + // Return DIO to output mode + // Dummy ACK + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->bit_delay_(); + this->clk_pin_->pin_mode(gpio::FLAG_INPUT); + this->bit_delay_(); + this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->bit_delay_(); + this->dio_pin_->pin_mode(gpio::FLAG_INPUT); + this->bit_delay_(); + return retval; +} + uint8_t TM1637Display::print(uint8_t start_pos, const char *str) { - ESP_LOGV(TAG, "Print at %d: %s", start_pos, str); + // ESP_LOGV(TAG, "Print at %d: %s", start_pos, str); uint8_t pos = start_pos; for (; *str != '\0'; str++) { uint8_t data = TM1637_UNKNOWN_CHAR; diff --git a/esphome/components/tm1637/tm1637.h b/esphome/components/tm1637/tm1637.h index 9b2f014ff9..0a77acaabe 100644 --- a/esphome/components/tm1637/tm1637.h +++ b/esphome/components/tm1637/tm1637.h @@ -8,10 +8,17 @@ #include "esphome/components/time/real_time_clock.h" #endif +#ifdef USE_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif + namespace esphome { namespace tm1637 { class TM1637Display; +#ifdef USE_BINARY_SENSOR +class TM1637Key; +#endif using tm1637_writer_t = std::function; @@ -46,10 +53,15 @@ class TM1637Display : public PollingComponent { void display(); +#ifdef USE_BINARY_SENSOR + void loop() override; + uint8_t get_keys(); + void add_tm1637_key(TM1637Key *tm1637_key) { this->tm1637_keys_.push_back(tm1637_key); } +#endif + #ifdef USE_TIME /// Evaluate the strftime-format and print the result at the given position. uint8_t strftime(uint8_t pos, const char *format, time::ESPTime time) __attribute__((format(strftime, 3, 0))); - /// Evaluate the strftime-format and print the result at position 0. uint8_t strftime(const char *format, time::ESPTime time) __attribute__((format(strftime, 2, 0))); #endif @@ -58,6 +70,7 @@ class TM1637Display : public PollingComponent { void bit_delay_(); void setup_pins_(); bool send_byte_(uint8_t b); + uint8_t read_byte_(); void start_(); void stop_(); @@ -68,7 +81,23 @@ class TM1637Display : public PollingComponent { bool inverted_; optional writer_{}; uint8_t buffer_[6] = {0}; +#ifdef USE_BINARY_SENSOR + std::vector tm1637_keys_{}; +#endif }; +#ifdef USE_BINARY_SENSOR +class TM1637Key : public binary_sensor::BinarySensor { + friend class TM1637Display; + + public: + void set_keycode(uint8_t key_code) { key_code_ = key_code; } + void process(uint8_t data) { this->publish_state(static_cast(data == this->key_code_)); } + + protected: + uint8_t key_code_{0}; +}; +#endif + } // namespace tm1637 } // namespace esphome