diff --git a/components/ht16k33_7segment/README.md b/components/ht16k33_7segment/README.md new file mode 100644 index 0000000..877155f --- /dev/null +++ b/components/ht16k33_7segment/README.md @@ -0,0 +1,50 @@ +# HT16K33 4 character 7-segment display + +This component supports the 4 character (plus colon) 7 segment character display. + +> :warning: If using this component as an external component, you need to include both `ht16k33_7segment` **and** `ht16k33_base` components. +```yaml +external_components: + source: github://ssieb/custom_components/ + components: [ ht16k33_base, ht16k33_7segment ] +``` + +There are no print functions for addressing rows and columns. With such a small display I didn't see any point. +All the print functions without the row and column parameters are available. +A "." will get added to the previous character as the decimal point. +All the same parameters for the i2c display can be used other than the dimensions. +There are also lambda functions `get_brightness` and `set_brightness` for adjusting the brightness of the display. +You can extend the display across multiple units. + +The colon in the middle of the display will be lit if the print string contains a ":" at the 3rd position (e.g. 12:34). + +Example: +```yaml +i2c: + sda: D0 + scl: D1 + +display: + - platform: ht16k33_7segment + address: 0x70 + scroll: true + scroll_speed: 250ms + scroll_dwell: 2s + scroll_delay: 3 + lambda: |- + auto time = id(time_sensor).now(); + it.strftime("%H:%M", time); +``` + +# Optional parameters + +`scroll:` defaults to false + +`scroll_speed:` is the time between each movement, default 250ms + +`scroll_dwell:` is the time to wait at the end before going back to the start, default 2s + +`scroll_delay:` is the number (float, minimum 1) of `scroll_speed` cycles to wait at the beginning before starting to scroll, default 3 + +`secondary_display:` is a list of i2c devices where `address:` is required and `i2c_id:` is optional unless there is more than one i2c bus. + diff --git a/components/ht16k33_7segment/__init__.py b/components/ht16k33_7segment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/components/ht16k33_7segment/display.py b/components/ht16k33_7segment/display.py new file mode 100644 index 0000000..1ac4d95 --- /dev/null +++ b/components/ht16k33_7segment/display.py @@ -0,0 +1,27 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_ID +from ..ht16k33_base.display import ( + base_to_code, + CONF_SECONDARY_DISPLAYS, + CONFIG_SCHEMA, + ht16k33_ns, + HT16K33BaseDisplay, +) + +AUTO_LOAD = ['ht16k33_base'] + +HT16K337SegmentDisplay = ht16k33_ns.class_("HT16K337SegmentDisplay", HT16K33BaseDisplay) + +async def to_code(config): + instance_var = HT16K337SegmentDisplay.new() + var = cg.Pvariable(config[CONF_ID], instance_var) + await base_to_code(var, config) + + if CONF_SECONDARY_DISPLAYS in config: + for conf in config[CONF_SECONDARY_DISPLAYS]: + instance_disp = HT16K337SegmentDisplay.new() + disp = cg.Pvariable(conf[CONF_ID], instance_disp) + await i2c.register_i2c_device(disp, conf) + cg.add(var.add_secondary_display(disp)) + diff --git a/components/ht16k33_7segment/font.h b/components/ht16k33_7segment/font.h new file mode 100644 index 0000000..35738ae --- /dev/null +++ b/components/ht16k33_7segment/font.h @@ -0,0 +1,102 @@ +#pragma once +// based on Adafruit backpack library + +static const uint8_t sevensegfonttable[] PROGMEM = { + + 0b00000000, // (space) + 0b10000110, // ! + 0b00100010, // " + 0b01111110, // # + 0b01101101, // $ + 0b11010010, // % + 0b01000110, // & + 0b00100000, // ' + 0b00101001, // ( + 0b00001011, // ) + 0b00100001, // * + 0b01110000, // + + 0b00010000, // , + 0b01000000, // - + 0b10000000, // . + 0b01010010, // / + 0b00111111, // 0 + 0b00000110, // 1 + 0b01011011, // 2 + 0b01001111, // 3 + 0b01100110, // 4 + 0b01101101, // 5 + 0b01111101, // 6 + 0b00000111, // 7 + 0b01111111, // 8 + 0b01101111, // 9 + 0b00001001, // : + 0b00001101, // ; + 0b01100001, // < + 0b01001000, // = + 0b01000011, // > + 0b11010011, // ? + 0b01011111, // @ + 0b01110111, // A + 0b01111100, // B + 0b00111001, // C + 0b01011110, // D + 0b01111001, // E + 0b01110001, // F + 0b00111101, // G + 0b01110110, // H + 0b00110000, // I + 0b00011110, // J + 0b01110101, // K + 0b00111000, // L + 0b00010101, // M + 0b00110111, // N + 0b00111111, // O + 0b01110011, // P + 0b01101011, // Q + 0b00110011, // R + 0b01101101, // S + 0b01111000, // T + 0b00111110, // U + 0b00111110, // V + 0b00101010, // W + 0b01110110, // X + 0b01101110, // Y + 0b01011011, // Z + 0b00111001, // [ + 0b01100100, // + 0b00001111, // ] + 0b00100011, // ^ + 0b00001000, // _ + 0b00000010, // ` + 0b01011111, // a + 0b01111100, // b + 0b01011000, // c + 0b01011110, // d + 0b01111011, // e + 0b01110001, // f + 0b01101111, // g + 0b01110100, // h + 0b00010000, // i + 0b00001100, // j + 0b01110101, // k + 0b00110000, // l + 0b00010100, // m + 0b01010100, // n + 0b01011100, // o + 0b01110011, // p + 0b01100111, // q + 0b01010000, // r + 0b01101101, // s + 0b01111000, // t + 0b00011100, // u + 0b00011100, // v + 0b00010100, // w + 0b01110110, // x + 0b01101110, // y + 0b01011011, // z + 0b01000110, // { + 0b00110000, // | + 0b01110000, // } + 0b00000001, // ~ + 0b00000000, // del +}; diff --git a/components/ht16k33_7segment/ht16k33_7segment.cpp b/components/ht16k33_7segment/ht16k33_7segment.cpp new file mode 100644 index 0000000..6617baf --- /dev/null +++ b/components/ht16k33_7segment/ht16k33_7segment.cpp @@ -0,0 +1,59 @@ +#include "esphome/core/log.h" +#include "esphome/core/hal.h" +#include "ht16k33_7segment.h" +#include "font.h" + +#ifndef USE_ESP8266 + #define pgm_read_word(s) (*s) +#endif + +namespace esphome { +namespace ht16k33 { + +static const char *TAG = "ht16k33"; + +void HT16K337SegmentDisplay::display_() { + constexpr uint8_t size = 10; + uint8_t buffer[size]; + uint8_t src_idx = this->offset_; + for (auto *display : this->displays_) { + for (uint8_t dst_idx = 0; dst_idx < size; dst_idx++) { + if (dst_idx == 4) { + buffer[dst_idx++] = this->show_colon_ ? 0x02 : 0; + buffer[dst_idx] = 0; + } else { + buffer[dst_idx] = this->buffer_[src_idx++]; + } + } + display->write_bytes(DISPLAY_COMMAND_SET_DDRAM_ADDR, buffer, size); + } +} + +//void HT16K337SegmentDisplay::display_() { +// int offset = this->offset_; +// static const uint8_t size = this->display_size_(); +// uint8_t buffer[size]; +// memcpy(buffer, this->buffer_ + offset, 4); +// offset += 4; +// if (this->show_colon_) { +// buffer[4] = 0x02; +// } else { +// buffer[4] = 0; +// } +// buffer[5] = 0; +// memcpy(buffer + 6, this->buffer_ + offset, 4); +// offset += 4; +// +// for (auto *display : this->displays_) { +// display->write_bytes(DISPLAY_COMMAND_SET_DDRAM_ADDR, buffer, size); +// offset += 8; +// } +//} + +uint16_t HT16K337SegmentDisplay::read_character_(uint8_t c) const { + return pgm_read_word(&sevensegfonttable[c - 32]); +} + +} // namespace ht16k33 +} // namespace esphome + diff --git a/components/ht16k33_7segment/ht16k33_7segment.h b/components/ht16k33_7segment/ht16k33_7segment.h new file mode 100644 index 0000000..eccd7da --- /dev/null +++ b/components/ht16k33_7segment/ht16k33_7segment.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../ht16k33_base/ht16k33_display.h" + +namespace esphome { +namespace ht16k33 { + +class HT16K337SegmentDisplay : public HT16K33BaseDisplay { + protected: + void display_() override; + uint16_t read_character_(uint8_t c) const override; + uint16_t decimal_point_mask_() const override { return 0x80; }; + bool supports_colon_() const override { return true; } +}; + +} // namespace ht16k33 +} // namespace esphome diff --git a/components/ht16k33_alpha/README.md b/components/ht16k33_alpha/README.md index 0c03f6c..5d802d4 100644 --- a/components/ht16k33_alpha/README.md +++ b/components/ht16k33_alpha/README.md @@ -1,6 +1,13 @@ -# HT16K33 4 character display +# HT16K33 4 character alphanumeric display -This component supports both the 4 character 14 segment alphanumeric display and the 4 character (plus colon) 7 segment character display. Use the `type` parameter to select which kind of display you have. +This component supports the 4 character 14 segment alphanumeric display. + +> :warning: If using this component as an external component, you need to include both `ht16k33_alpha` **and** `ht16k33_base` components. +```yaml +external_components: + source: github://ssieb/custom_components/ + components: [ ht16k33_base, ht16k33_alpha ] +``` There are no print functions for addressing rows and columns. With such a small display I didn't see any point. All the print functions without the row and column parameters are available. @@ -9,8 +16,6 @@ All the same parameters for the i2c display can be used other than the dimension There are also lambda functions `get_brightness` and `set_brightness` for adjusting the brightness of the display. You can extend the display across multiple units. -For the 7-segment displays with colon, there is also the `show_colon` lambda function, that can be used to select whether the colon between the second and third digits should be lit or not. - Example: ```yaml i2c: @@ -30,10 +35,6 @@ display: secondary_displays: - address: 0x71 ``` -# Required parameters - -`type:` choose between `alpha` or `7segment`, depending on which kind of display you have - # Optional parameters `scroll:` defaults to false diff --git a/components/ht16k33_alpha/display.py b/components/ht16k33_alpha/display.py index f55b093..7b1d9fd 100644 --- a/components/ht16k33_alpha/display.py +++ b/components/ht16k33_alpha/display.py @@ -1,59 +1,27 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import display, i2c -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_TYPE +from esphome.const import CONF_ID +from ..ht16k33_base.display import ( + base_to_code, + CONF_SECONDARY_DISPLAYS, + CONFIG_SCHEMA, + ht16k33_ns, + HT16K33BaseDisplay, +) -DEPENDENCIES = ['i2c'] +AUTO_LOAD = ['ht16k33_base'] -ht16k33_ns = cg.esphome_ns.namespace('ht16k33') -HT16K33BaseDisplay = ht16k33_ns.class_('HT16K33BaseDisplay', cg.PollingComponent, i2c.I2CDevice) - -TYPES = { - "ALPHA": ht16k33_ns.class_("HT16K33AlphaDisplay", HT16K33BaseDisplay), - "7SEGMENT": ht16k33_ns.class_("HT16K337SegmentDisplay", HT16K33BaseDisplay), -} - -CONF_SCROLL = "scroll" -CONF_SCROLL_SPEED = "scroll_speed" -CONF_SCROLL_DWELL = "scroll_dwell" -CONF_SCROLL_DELAY = "scroll_delay" -CONF_SECONDARY_DISPLAYS = "secondary_displays" - - -CONFIG_SECONDARY = cv.Schema({ - cv.GenerateID(): cv.declare_id(i2c.I2CDevice) -}).extend(i2c.i2c_device_schema(None)) - -CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(HT16K33BaseDisplay), - cv.Optional(CONF_TYPE, default="ALPHA"): cv.enum(TYPES, upper=True), - cv.Optional(CONF_SCROLL, default=False): cv.boolean, - cv.Optional(CONF_SCROLL_SPEED, default='250ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_SCROLL_DWELL, default='2s'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_SCROLL_DELAY, default='3'): cv.float_range(min=1), - cv.Optional(CONF_SECONDARY_DISPLAYS): cv.ensure_list(CONFIG_SECONDARY), -}).extend(cv.polling_component_schema('1s')).extend(i2c.i2c_device_schema(0x70)) +HT16K33AlphaDisplay = ht16k33_ns.class_("HT16K33AlphaDisplay", HT16K33BaseDisplay) async def to_code(config): - instance_var = TYPES[config[CONF_TYPE]].new() + instance_var = HT16K33AlphaDisplay.new() var = cg.Pvariable(config[CONF_ID], instance_var) - await cg.register_component(var, config) - await display.register_display(var, config) - await i2c.register_i2c_device(var, config) + await base_to_code(var, config) - if CONF_LAMBDA in config: - lambda_ = await cg.process_lambda(config[CONF_LAMBDA], - [(HT16K33BaseDisplay.operator('ref'), 'it')], - return_type=cg.void) - cg.add(var.set_writer(lambda_)) - if config[CONF_SCROLL]: - cg.add(var.set_scroll(True)) - cg.add(var.set_scroll_speed(config[CONF_SCROLL_SPEED])) - cg.add(var.set_scroll_dwell(config[CONF_SCROLL_DWELL])) - cg.add(var.set_scroll_delay(int(config[CONF_SCROLL_DELAY] * config[CONF_SCROLL_SPEED].total_milliseconds))) if CONF_SECONDARY_DISPLAYS in config: for conf in config[CONF_SECONDARY_DISPLAYS]: - disp = cg.new_Pvariable(conf[CONF_ID]) + instance_disp = HT16K33AlphaDisplay.new() + disp = cg.Pvariable(conf[CONF_ID], instance_disp) await i2c.register_i2c_device(disp, conf) cg.add(var.add_secondary_display(disp)) diff --git a/components/ht16k33_alpha/font.h b/components/ht16k33_alpha/font.h index e85d598..6caba42 100644 --- a/components/ht16k33_alpha/font.h +++ b/components/ht16k33_alpha/font.h @@ -1,3 +1,4 @@ +#pragma once // based on Adafruit backpack library static const uint16_t alphafonttable[] PROGMEM = { @@ -131,102 +132,3 @@ static const uint16_t alphafonttable[] PROGMEM = { 0b0011111111111111, }; -static const uint8_t sevensegfonttable[] PROGMEM = { - - 0b00000000, // (space) - 0b10000110, // ! - 0b00100010, // " - 0b01111110, // # - 0b01101101, // $ - 0b11010010, // % - 0b01000110, // & - 0b00100000, // ' - 0b00101001, // ( - 0b00001011, // ) - 0b00100001, // * - 0b01110000, // + - 0b00010000, // , - 0b01000000, // - - 0b10000000, // . - 0b01010010, // / - 0b00111111, // 0 - 0b00000110, // 1 - 0b01011011, // 2 - 0b01001111, // 3 - 0b01100110, // 4 - 0b01101101, // 5 - 0b01111101, // 6 - 0b00000111, // 7 - 0b01111111, // 8 - 0b01101111, // 9 - 0b00001001, // : - 0b00001101, // ; - 0b01100001, // < - 0b01001000, // = - 0b01000011, // > - 0b11010011, // ? - 0b01011111, // @ - 0b01110111, // A - 0b01111100, // B - 0b00111001, // C - 0b01011110, // D - 0b01111001, // E - 0b01110001, // F - 0b00111101, // G - 0b01110110, // H - 0b00110000, // I - 0b00011110, // J - 0b01110101, // K - 0b00111000, // L - 0b00010101, // M - 0b00110111, // N - 0b00111111, // O - 0b01110011, // P - 0b01101011, // Q - 0b00110011, // R - 0b01101101, // S - 0b01111000, // T - 0b00111110, // U - 0b00111110, // V - 0b00101010, // W - 0b01110110, // X - 0b01101110, // Y - 0b01011011, // Z - 0b00111001, // [ - 0b01100100, // - 0b00001111, // ] - 0b00100011, // ^ - 0b00001000, // _ - 0b00000010, // ` - 0b01011111, // a - 0b01111100, // b - 0b01011000, // c - 0b01011110, // d - 0b01111011, // e - 0b01110001, // f - 0b01101111, // g - 0b01110100, // h - 0b00010000, // i - 0b00001100, // j - 0b01110101, // k - 0b00110000, // l - 0b00010100, // m - 0b01010100, // n - 0b01011100, // o - 0b01110011, // p - 0b01100111, // q - 0b01010000, // r - 0b01101101, // s - 0b01111000, // t - 0b00011100, // u - 0b00011100, // v - 0b00010100, // w - 0b01110110, // x - 0b01101110, // y - 0b01011011, // z - 0b01000110, // { - 0b00110000, // | - 0b01110000, // } - 0b00000001, // ~ - 0b00000000, // del -}; diff --git a/components/ht16k33_alpha/ht16k33_alpha.cpp b/components/ht16k33_alpha/ht16k33_alpha.cpp new file mode 100644 index 0000000..617aa41 --- /dev/null +++ b/components/ht16k33_alpha/ht16k33_alpha.cpp @@ -0,0 +1,29 @@ +#include "esphome/core/log.h" +#include "esphome/core/hal.h" +#include "ht16k33_alpha.h" +#include "font.h" + +#ifndef USE_ESP8266 + #define pgm_read_word(s) (*s) +#endif + +namespace esphome { +namespace ht16k33 { + +static const char *TAG = "ht16k33"; + +void HT16K33AlphaDisplay::display_() { + int offset = this->offset_; + for (auto *display : this->displays_) { + display->write_bytes(DISPLAY_COMMAND_SET_DDRAM_ADDR, this->buffer_ + offset, 8); + offset += 8; + } +} + +uint16_t HT16K33AlphaDisplay::read_character_(uint8_t c) const { + return pgm_read_word(&alphafonttable[c]); +} + +} // namespace ht16k33 +} // namespace esphome + diff --git a/components/ht16k33_alpha/ht16k33_alpha.h b/components/ht16k33_alpha/ht16k33_alpha.h new file mode 100644 index 0000000..d302746 --- /dev/null +++ b/components/ht16k33_alpha/ht16k33_alpha.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../ht16k33_base/ht16k33_display.h" + +namespace esphome { +namespace ht16k33 { + +class HT16K33AlphaDisplay : public HT16K33BaseDisplay { + protected: + void display_() override; + uint16_t read_character_(uint8_t c) const override; + uint16_t decimal_point_mask_() const override { return 0x4000; } + bool supports_colon_() const override { return false; } +}; + +} // namespace ht16k33 +} // namespace esphome diff --git a/components/ht16k33_base/README.md b/components/ht16k33_base/README.md new file mode 100644 index 0000000..2d7b865 --- /dev/null +++ b/components/ht16k33_base/README.md @@ -0,0 +1,4 @@ +# HT16K33 4 character display base component + +This component provides the common functionality for both the 4 character 14 segment alphanumeric display and the 4 character (plus colon) 7 segment character display. It cannot be used as standalone. Use the ``ht16k33_alpha`` or ``ht16k33_7segment`` components instead. + diff --git a/components/ht16k33_base/__init__.py b/components/ht16k33_base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/components/ht16k33_base/display.py b/components/ht16k33_base/display.py new file mode 100644 index 0000000..76b85ca --- /dev/null +++ b/components/ht16k33_base/display.py @@ -0,0 +1,46 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import display, i2c +from esphome.const import CONF_ID, CONF_LAMBDA + +DEPENDENCIES = ['i2c'] + +ht16k33_ns = cg.esphome_ns.namespace('ht16k33') +HT16K33BaseDisplay = ht16k33_ns.class_('HT16K33BaseDisplay', cg.PollingComponent, i2c.I2CDevice) + +CONF_SCROLL = "scroll" +CONF_SCROLL_SPEED = "scroll_speed" +CONF_SCROLL_DWELL = "scroll_dwell" +CONF_SCROLL_DELAY = "scroll_delay" +CONF_SECONDARY_DISPLAYS = "secondary_displays" + + +CONFIG_SECONDARY = cv.Schema({ + cv.GenerateID(): cv.declare_id(i2c.I2CDevice) +}).extend(i2c.i2c_device_schema(None)) + +CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(HT16K33BaseDisplay), + cv.Optional(CONF_SCROLL, default=False): cv.boolean, + cv.Optional(CONF_SCROLL_SPEED, default='250ms'): cv.positive_time_period_milliseconds, + cv.Optional(CONF_SCROLL_DWELL, default='2s'): cv.positive_time_period_milliseconds, + cv.Optional(CONF_SCROLL_DELAY, default='3'): cv.float_range(min=1), + cv.Optional(CONF_SECONDARY_DISPLAYS): cv.ensure_list(CONFIG_SECONDARY), +}).extend(cv.polling_component_schema('1s')).extend(i2c.i2c_device_schema(0x70)) + +async def base_to_code(var, config): + await cg.register_component(var, config) + await display.register_display(var, config) + await i2c.register_i2c_device(var, config) + + if CONF_LAMBDA in config: + lambda_ = await cg.process_lambda(config[CONF_LAMBDA], + [(HT16K33BaseDisplay.operator('ref'), 'it')], + return_type=cg.void) + cg.add(var.set_writer(lambda_)) + if config[CONF_SCROLL]: + cg.add(var.set_scroll(True)) + cg.add(var.set_scroll_speed(config[CONF_SCROLL_SPEED])) + cg.add(var.set_scroll_dwell(config[CONF_SCROLL_DWELL])) + cg.add(var.set_scroll_delay(int(config[CONF_SCROLL_DELAY] * config[CONF_SCROLL_SPEED].total_milliseconds))) + diff --git a/components/ht16k33_alpha/ht16k33_display.cpp b/components/ht16k33_base/ht16k33_display.cpp similarity index 71% rename from components/ht16k33_alpha/ht16k33_display.cpp rename to components/ht16k33_base/ht16k33_display.cpp index 58f9756..381279f 100644 --- a/components/ht16k33_alpha/ht16k33_display.cpp +++ b/components/ht16k33_base/ht16k33_display.cpp @@ -2,23 +2,16 @@ #include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "ht16k33_display.h" -#include "font.h" - -#ifndef USE_ESP8266 - #define pgm_read_word(s) (*s) -#endif namespace esphome { namespace ht16k33 { static const char *TAG = "ht16k33"; -// First set bit determines command, bits after that are the data. -static const uint8_t DISPLAY_COMMAND_SET_DDRAM_ADDR = 0x00; -static const uint8_t DISPLAY_COMMAND_SYSTEM_SETUP = 0x21; -static const uint8_t DISPLAY_COMMAND_DISPLAY_OFF = 0x80; -static const uint8_t DISPLAY_COMMAND_DISPLAY_ON = 0x81; -static const uint8_t DISPLAY_COMMAND_DIMMING = 0xE0; +void HT16K33BaseDisplay::dump_config() { + ESP_LOGCONFIG(TAG, "HT16K33 Display:"); + LOG_I2C_DEVICE(this); +} void HT16K33BaseDisplay::setup() { for (auto *display : this->displays_) { @@ -57,33 +50,6 @@ void HT16K33BaseDisplay::loop() { float HT16K33BaseDisplay::get_setup_priority() const { return setup_priority::PROCESSOR; } -void HT16K337SegmentDisplay::display_() { - int offset = this->offset_; - constexpr uint8_t size = 10; - uint8_t buffer[size]; - memcpy(buffer, this->buffer_ + offset, 4); - if (this->show_colon_) { - buffer[4] = 0x02; - } else { - buffer[4] = 0; - } - buffer[5] = 0; - memcpy(buffer + 6, this->buffer_ + offset + 4, 4); - - for (auto *display : this->displays_) { - display->write_bytes(DISPLAY_COMMAND_SET_DDRAM_ADDR, buffer, size); - offset += 8; - } -} - -void HT16K33AlphaDisplay::display_() { - int offset = this->offset_; - for (auto *display : this->displays_) { - display->write_bytes(DISPLAY_COMMAND_SET_DDRAM_ADDR, this->buffer_ + offset, 8); - offset += 8; - } -} - void HT16K33BaseDisplay::update() { memset(this->buffer_, 0, 64); int prev_fill = this->buffer_fill_; @@ -119,7 +85,9 @@ float HT16K33BaseDisplay::get_brightness() { void HT16K33BaseDisplay::print(const char *str) { uint8_t pos = this->buffer_fill_; + uint8_t idx = 1; uint16_t fontc = 0; + this->show_colon_ = false; while (*str != '\0') { if (pos >= 64) { ESP_LOGW(TAG, "output buffer full!"); @@ -135,9 +103,16 @@ void HT16K33BaseDisplay::print(const char *str) { if (c == '.') { fontc |= decimal_point_mask_(); str++; + idx++; + } + if (c == ':' && idx == 2 && this->supports_colon_()) { + this->show_colon_ = true; + str++; + idx++; } this->buffer_[pos++] = fontc & 0xff; this->buffer_[pos++] = fontc >> 8; + idx++; } this->buffer_fill_ = pos; } @@ -163,14 +138,6 @@ void HT16K33BaseDisplay::strftime(const char *format, ESPTime time) { } #endif -uint16_t HT16K33AlphaDisplay::read_character_(uint8_t c) const { - return pgm_read_word(&alphafonttable[c]); -} - -uint16_t HT16K337SegmentDisplay::read_character_(uint8_t c) const { - return pgm_read_word(&sevensegfonttable[c - 32]); -} - } // namespace ht16k33 } // namespace esphome diff --git a/components/ht16k33_alpha/ht16k33_display.h b/components/ht16k33_base/ht16k33_display.h similarity index 81% rename from components/ht16k33_alpha/ht16k33_display.h rename to components/ht16k33_base/ht16k33_display.h index ea57bcb..af034a7 100644 --- a/components/ht16k33_alpha/ht16k33_display.h +++ b/components/ht16k33_base/ht16k33_display.h @@ -12,9 +12,17 @@ namespace esphome { namespace ht16k33 { +// First set bit determines command, bits after that are the data. +constexpr uint8_t DISPLAY_COMMAND_SET_DDRAM_ADDR = 0x00; +constexpr uint8_t DISPLAY_COMMAND_SYSTEM_SETUP = 0x21; +constexpr uint8_t DISPLAY_COMMAND_DISPLAY_OFF = 0x80; +constexpr uint8_t DISPLAY_COMMAND_DISPLAY_ON = 0x81; +constexpr uint8_t DISPLAY_COMMAND_DIMMING = 0xE0; + class HT16K33BaseDisplay : public PollingComponent, public i2c::I2CDevice { public: void set_writer(std::function &&writer) { this->writer_ = std::move(writer); } + void dump_config() override; void setup() override; void loop() override; float get_setup_priority() const override; @@ -40,14 +48,14 @@ class HT16K33BaseDisplay : public PollingComponent, public i2c::I2CDevice { void strftime(const char *format, ESPTime time) __attribute__((format(strftime, 2, 0))); #endif - void show_colon(bool show) { this->show_colon_ = show; } - protected: void command_(uint8_t value); void call_writer() { this->writer_(*this); } + virtual void display_() = 0; virtual uint16_t read_character_(uint8_t c) const = 0; virtual uint16_t decimal_point_mask_() const = 0; + virtual bool supports_colon_() const =0; std::vector displays_ {this}; std::function writer_; @@ -63,19 +71,5 @@ class HT16K33BaseDisplay : public PollingComponent, public i2c::I2CDevice { bool show_colon_ {false}; }; -class HT16K33AlphaDisplay : public HT16K33BaseDisplay { - protected: - void display_() override; - uint16_t read_character_(uint8_t c) const override; - uint16_t decimal_point_mask_() const override { return 0x4000; } -}; - -class HT16K337SegmentDisplay : public HT16K33BaseDisplay { - protected: - void display_() override; - uint16_t read_character_(uint8_t c) const override; - uint16_t decimal_point_mask_() const override { return 0x80; }; -}; - } // namespace ht16k33 } // namespace esphome