Skip to content

Commit

Permalink
Split ht16k33 into separate components
Browse files Browse the repository at this point in the history
  • Loading branch information
guillempages committed Nov 13, 2023
1 parent b091405 commit 89eb4ec
Show file tree
Hide file tree
Showing 16 changed files with 398 additions and 215 deletions.
50 changes: 50 additions & 0 deletions components/ht16k33_7segment/README.md
Original file line number Diff line number Diff line change
@@ -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.

Empty file.
27 changes: 27 additions & 0 deletions components/ht16k33_7segment/display.py
Original file line number Diff line number Diff line change
@@ -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))

102 changes: 102 additions & 0 deletions components/ht16k33_7segment/font.h
Original file line number Diff line number Diff line change
@@ -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
};
59 changes: 59 additions & 0 deletions components/ht16k33_7segment/ht16k33_7segment.cpp
Original file line number Diff line number Diff line change
@@ -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

17 changes: 17 additions & 0 deletions components/ht16k33_7segment/ht16k33_7segment.h
Original file line number Diff line number Diff line change
@@ -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
17 changes: 9 additions & 8 deletions components/ht16k33_alpha/README.md
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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:
Expand All @@ -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
Expand Down
60 changes: 14 additions & 46 deletions components/ht16k33_alpha/display.py
Original file line number Diff line number Diff line change
@@ -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))

Loading

0 comments on commit 89eb4ec

Please sign in to comment.