Skip to content

Commit

Permalink
Add support for 7 segment displays
Browse files Browse the repository at this point in the history
  • Loading branch information
guillempages committed Nov 11, 2023
1 parent 7351165 commit a3cc01f
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 28 deletions.
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

0 comments on commit a3cc01f

Please sign in to comment.