Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for 7 segment displays #61

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
26 changes: 26 additions & 0 deletions components/ht16k33_7segment/display.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import esphome.codegen as cg
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
12 changes: 10 additions & 2 deletions components/ht16k33_alpha/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# HT16K33 4 character alphanumeric display

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.
A "." will get added to the previous character as the decimal point.
Expand All @@ -9,7 +18,7 @@ You can extend the display across multiple units.

Example:
```yaml
i2c:
i2c:
sda: D0
scl: D1

Expand All @@ -25,7 +34,6 @@ display:
secondary_displays:
- address: 0x71
```

# Optional parameters

`scroll:` defaults to false
Expand Down
55 changes: 15 additions & 40 deletions components/ht16k33_alpha/display.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,26 @@
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
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_alpha_ns = cg.esphome_ns.namespace('ht16k33_alpha')
HT16K33AlphaDisplay = ht16k33_alpha_ns.class_('HT16K33AlphaDisplay', 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(HT16K33AlphaDisplay),
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):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await display.register_display(var, config)
await i2c.register_i2c_device(var, config)
instance_var = HT16K33AlphaDisplay.new()
var = cg.Pvariable(config[CONF_ID], instance_var)
await base_to_code(var, config)

if CONF_LAMBDA in config:
lambda_ = await cg.process_lambda(config[CONF_LAMBDA],
[(HT16K33AlphaDisplay.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))

3 changes: 2 additions & 1 deletion components/ht16k33_alpha/font.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
// based on Adafruit backpack library

static const uint16_t alphafonttable[] PROGMEM = {
static const uint16_t alphafonttable[] PROGMEM = {
guillempages marked this conversation as resolved.
Show resolved Hide resolved
0b0000000000000000, // 0 = space
0b0000000000000001, // 1 = seg A
0b0000000000000010, // 2 = seg B
Expand Down
29 changes: 29 additions & 0 deletions components/ht16k33_alpha/ht16k33_alpha.cpp
Original file line number Diff line number Diff line change
@@ -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

Loading