Skip to content

Add support for 7 segment displays #61

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
20 changes: 14 additions & 6 deletions components/ht16k33_alpha/display.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
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, CONF_LAMBDA, CONF_TYPE

DEPENDENCIES = ['i2c']

ht16k33_alpha_ns = cg.esphome_ns.namespace('ht16k33_alpha')
HT16K33AlphaDisplay = ht16k33_alpha_ns.class_('HT16K33AlphaDisplay', cg.PollingComponent, i2c.I2CDevice)
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(HT16K33AlphaDisplay),
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,
Expand All @@ -28,14 +35,15 @@
}).extend(cv.polling_component_schema('1s')).extend(i2c.i2c_device_schema(0x70))

async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
instance_var = TYPES[config[CONF_TYPE]].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)

if CONF_LAMBDA in config:
lambda_ = await cg.process_lambda(config[CONF_LAMBDA],
[(HT16K33AlphaDisplay.operator('ref'), 'it')],
[(HT16K33BaseDisplay.operator('ref'), 'it')],
return_type=cg.void)
cg.add(var.set_writer(lambda_))
if config[CONF_SCROLL]:
Expand Down
101 changes: 100 additions & 1 deletion components/ht16k33_alpha/font.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// based on Adafruit backpack library

static const uint16_t alphafonttable[] PROGMEM = {
static const uint16_t alphafonttable[] PROGMEM = {
0b0000000000000000, // 0 = space
0b0000000000000001, // 1 = seg A
0b0000000000000010, // 2 = seg B
Expand Down Expand Up @@ -131,3 +131,102 @@ 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
};
58 changes: 43 additions & 15 deletions components/ht16k33_alpha/ht16k33_display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
#endif

namespace esphome {
namespace ht16k33_alpha {
namespace ht16k33 {

static const char *TAG = "ht16k33_alpha";
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;
Expand All @@ -20,7 +20,7 @@ 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 HT16K33AlphaDisplay::setup() {
void HT16K33BaseDisplay::setup() {
for (auto *display : this->displays_) {
display->write_bytes(DISPLAY_COMMAND_SYSTEM_SETUP, nullptr, 0);
display->write_bytes(DISPLAY_COMMAND_DISPLAY_ON, nullptr, 0);
Expand All @@ -29,7 +29,7 @@ void HT16K33AlphaDisplay::setup() {
memset(this->buffer_, 0, 64);
}

void HT16K33AlphaDisplay::loop() {
void HT16K33BaseDisplay::loop() {
unsigned long now = millis();
int numc = this->displays_.size() * 8;
// check if the buffer has shrunk past the current position since last update
Expand All @@ -55,7 +55,27 @@ void HT16K33AlphaDisplay::loop() {
}
}

float HT16K33AlphaDisplay::get_setup_priority() const { return setup_priority::PROCESSOR; }
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);
// TODO: implement method to show/hide colon symbol
// 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_;
Expand All @@ -65,7 +85,7 @@ void HT16K33AlphaDisplay::display_() {
}
}

void HT16K33AlphaDisplay::update() {
void HT16K33BaseDisplay::update() {
memset(this->buffer_, 0, 64);
int prev_fill = this->buffer_fill_;
this->buffer_fill_ = 0;
Expand All @@ -77,7 +97,7 @@ void HT16K33AlphaDisplay::update() {
this->display_();
}

void HT16K33AlphaDisplay::set_brightness(float level) {
void HT16K33BaseDisplay::set_brightness(float level) {
int val = (int) round(level * 16);
if (val < 0)
val = 0;
Expand All @@ -94,11 +114,11 @@ void HT16K33AlphaDisplay::set_brightness(float level) {
}
}

float HT16K33AlphaDisplay::get_brightness() {
float HT16K33BaseDisplay::get_brightness() {
return this->brightness_ / 16.0;
}

void HT16K33AlphaDisplay::print(const char *str) {
void HT16K33BaseDisplay::print(const char *str) {
uint8_t pos = this->buffer_fill_;
uint16_t fontc = 0;
while (*str != '\0') {
Expand All @@ -111,10 +131,10 @@ void HT16K33AlphaDisplay::print(const char *str) {
if (c > 127)
fontc = 0;
else
fontc = pgm_read_word(&alphafonttable[c]);
fontc = read_character_(c);
c = *reinterpret_cast<const uint8_t *>(str);
if (c == '.') {
fontc |= 0x4000;
fontc |= decimal_point_mask_();
str++;
}
this->buffer_[pos++] = fontc & 0xff;
Expand All @@ -123,9 +143,9 @@ void HT16K33AlphaDisplay::print(const char *str) {
this->buffer_fill_ = pos;
}

void HT16K33AlphaDisplay::print(const std::string &str) { this->print(str.c_str()); }
void HT16K33BaseDisplay::print(const std::string &str) { this->print(str.c_str()); }

void HT16K33AlphaDisplay::printf(const char *format, ...) {
void HT16K33BaseDisplay::printf(const char *format, ...) {
va_list arg;
va_start(arg, format);
char buffer[64];
Expand All @@ -136,14 +156,22 @@ void HT16K33AlphaDisplay::printf(const char *format, ...) {
}

#ifdef USE_TIME
void HT16K33AlphaDisplay::strftime(const char *format, ESPTime time) {
void HT16K33BaseDisplay::strftime(const char *format, ESPTime time) {
char buffer[64];
size_t ret = time.strftime(buffer, sizeof(buffer), format);
if (ret > 0)
this->print(buffer);
}
#endif

} // namespace ht16k33_alpha
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

28 changes: 22 additions & 6 deletions components/ht16k33_alpha/ht16k33_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
#endif

namespace esphome {
namespace ht16k33_alpha {
namespace ht16k33 {

class HT16K33AlphaDisplay : public PollingComponent, public i2c::I2CDevice {
class HT16K33BaseDisplay : public PollingComponent, public i2c::I2CDevice {
public:
void set_writer(std::function<void(HT16K33AlphaDisplay &)> &&writer) { this->writer_ = std::move(writer); }
void set_writer(std::function<void(HT16K33BaseDisplay &)> &&writer) { this->writer_ = std::move(writer); }
void setup() override;
void loop() override;
float get_setup_priority() const override;
Expand Down Expand Up @@ -43,10 +43,12 @@ class HT16K33AlphaDisplay : public PollingComponent, public i2c::I2CDevice {
protected:
void command_(uint8_t value);
void call_writer() { this->writer_(*this); }
void display_();
virtual void display_() = 0;
virtual uint16_t read_character_(uint8_t c) const = 0;
virtual uint16_t decimal_point_mask_() const = 0;

std::vector<i2c::I2CDevice *> displays_ {this};
std::function<void(HT16K33AlphaDisplay &)> writer_;
std::function<void(HT16K33BaseDisplay &)> writer_;
bool scroll_ {false};
unsigned long scroll_speed_ {250};
unsigned long scroll_dwell_ {2000};
Expand All @@ -58,5 +60,19 @@ class HT16K33AlphaDisplay : public PollingComponent, public i2c::I2CDevice {
uint8_t brightness_ = 16;
};

} // namespace ht16k33_alpha
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