mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 21:44:55 +01:00
Max7219digit multiline (#1622)
Co-authored-by: Otto winter <otto@otto-winter.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
c422b2fb0b
commit
92321e219a
3 changed files with 110 additions and 47 deletions
|
@ -13,10 +13,20 @@ CONF_SCROLL_DELAY = "scroll_delay"
|
|||
CONF_SCROLL_ENABLE = "scroll_enable"
|
||||
CONF_SCROLL_MODE = "scroll_mode"
|
||||
CONF_REVERSE_ENABLE = "reverse_enable"
|
||||
CONF_NUM_CHIP_LINES = "num_chip_lines"
|
||||
CONF_CHIP_LINES_STYLE = "chip_lines_style"
|
||||
|
||||
integration_ns = cg.esphome_ns.namespace("max7219digit")
|
||||
ChipLinesStyle = integration_ns.enum("ChipLinesStyle")
|
||||
CHIP_LINES_STYLE = {
|
||||
"ZIGZAG": ChipLinesStyle.ZIGZAG,
|
||||
"SNAKE": ChipLinesStyle.SNAKE,
|
||||
}
|
||||
|
||||
ScrollMode = integration_ns.enum("ScrollMode")
|
||||
SCROLL_MODES = {
|
||||
"CONTINUOUS": 0,
|
||||
"STOP": 1,
|
||||
"CONTINUOUS": ScrollMode.CONTINUOUS,
|
||||
"STOP": ScrollMode.STOP,
|
||||
}
|
||||
|
||||
CHIP_MODES = {
|
||||
|
@ -37,6 +47,10 @@ CONFIG_SCHEMA = (
|
|||
{
|
||||
cv.GenerateID(): cv.declare_id(MAX7219Component),
|
||||
cv.Optional(CONF_NUM_CHIPS, default=4): cv.int_range(min=1, max=255),
|
||||
cv.Optional(CONF_NUM_CHIP_LINES, default=1): cv.int_range(min=1, max=255),
|
||||
cv.Optional(CONF_CHIP_LINES_STYLE, default="SNAKE"): cv.enum(
|
||||
CHIP_LINES_STYLE, upper=True
|
||||
),
|
||||
cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15),
|
||||
cv.Optional(CONF_ROTATE_CHIP, default="0"): cv.enum(CHIP_MODES, upper=True),
|
||||
cv.Optional(CONF_SCROLL_MODE, default="CONTINUOUS"): cv.enum(
|
||||
|
@ -67,6 +81,8 @@ async def to_code(config):
|
|||
await display.register_display(var, config)
|
||||
|
||||
cg.add(var.set_num_chips(config[CONF_NUM_CHIPS]))
|
||||
cg.add(var.set_num_chip_lines(config[CONF_NUM_CHIP_LINES]))
|
||||
cg.add(var.set_chip_lines_style(config[CONF_CHIP_LINES_STYLE]))
|
||||
cg.add(var.set_intensity(config[CONF_INTENSITY]))
|
||||
cg.add(var.set_chip_orientation(config[CONF_ROTATE_CHIP]))
|
||||
cg.add(var.set_scroll_speed(config[CONF_SCROLL_SPEED]))
|
||||
|
|
|
@ -26,9 +26,12 @@ void MAX7219Component::setup() {
|
|||
ESP_LOGCONFIG(TAG, "Setting up MAX7219_DIGITS...");
|
||||
this->spi_setup();
|
||||
this->stepsleft_ = 0;
|
||||
this->max_displaybuffer_.reserve(500); // Create base space to write buffer
|
||||
// Initialize buffer with 0 for display so all non written pixels are blank
|
||||
this->max_displaybuffer_.resize(this->num_chips_ * 8, 0);
|
||||
for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
|
||||
std::vector<uint8_t> vec(1);
|
||||
this->max_displaybuffer_.push_back(vec);
|
||||
// Initialize buffer with 0 for display so all non written pixels are blank
|
||||
this->max_displaybuffer_[chip_line].resize(get_width_internal(), 0);
|
||||
}
|
||||
// let's assume the user has all 8 digits connected, only important in daisy chained setups anyway
|
||||
this->send_to_all_(MAX7219_REGISTER_SCAN_LIMIT, 7);
|
||||
// let's use our own ASCII -> led pattern encoding
|
||||
|
@ -46,6 +49,8 @@ void MAX7219Component::setup() {
|
|||
void MAX7219Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MAX7219DIGIT:");
|
||||
ESP_LOGCONFIG(TAG, " Number of Chips: %u", this->num_chips_);
|
||||
ESP_LOGCONFIG(TAG, " Number of Chips Lines: %u", this->num_chip_lines_);
|
||||
ESP_LOGCONFIG(TAG, " Chips Lines Style : %u", this->chip_lines_style_);
|
||||
ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_);
|
||||
ESP_LOGCONFIG(TAG, " Scroll Mode: %u", this->scroll_mode_);
|
||||
ESP_LOGCONFIG(TAG, " Scroll Speed: %u", this->scroll_speed_);
|
||||
|
@ -59,19 +64,19 @@ void MAX7219Component::loop() {
|
|||
uint32_t now = millis();
|
||||
|
||||
// check if the buffer has shrunk past the current position since last update
|
||||
if ((this->max_displaybuffer_.size() >= this->old_buffer_size_ + 3) ||
|
||||
(this->max_displaybuffer_.size() <= this->old_buffer_size_ - 3)) {
|
||||
if ((this->max_displaybuffer_[0].size() >= this->old_buffer_size_ + 3) ||
|
||||
(this->max_displaybuffer_[0].size() <= this->old_buffer_size_ - 3)) {
|
||||
this->stepsleft_ = 0;
|
||||
this->display();
|
||||
this->old_buffer_size_ = this->max_displaybuffer_.size();
|
||||
this->old_buffer_size_ = this->max_displaybuffer_[0].size();
|
||||
}
|
||||
|
||||
// Reset the counter back to 0 when full string has been displayed.
|
||||
if (this->stepsleft_ > this->max_displaybuffer_.size())
|
||||
if (this->stepsleft_ > this->max_displaybuffer_[0].size())
|
||||
this->stepsleft_ = 0;
|
||||
|
||||
// Return if there is no need to scroll or scroll is off
|
||||
if (!this->scroll_ || (this->max_displaybuffer_.size() <= this->num_chips_ * 8)) {
|
||||
if (!this->scroll_ || (this->max_displaybuffer_[0].size() <= get_width_internal())) {
|
||||
this->display();
|
||||
return;
|
||||
}
|
||||
|
@ -82,8 +87,8 @@ void MAX7219Component::loop() {
|
|||
}
|
||||
|
||||
// Dwell time at end of string in case of stop at end
|
||||
if (this->scroll_mode_ == 1) {
|
||||
if (this->stepsleft_ >= this->max_displaybuffer_.size() - this->num_chips_ * 8 + 1) {
|
||||
if (this->scroll_mode_ == ScrollMode::STOP) {
|
||||
if (this->stepsleft_ >= this->max_displaybuffer_[0].size() - get_width_internal() + 1) {
|
||||
if (now - this->last_scroll_ >= this->scroll_dwell_) {
|
||||
this->stepsleft_ = 0;
|
||||
this->last_scroll_ = now;
|
||||
|
@ -107,30 +112,53 @@ void MAX7219Component::display() {
|
|||
// Run this routine for the rows of every chip 8x row 0 top to 7 bottom
|
||||
// Fill the pixel parameter with display data
|
||||
// Send the data to the chip
|
||||
for (uint8_t i = 0; i < this->num_chips_; i++) {
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
if (this->reverse_) {
|
||||
pixels[j] = this->max_displaybuffer_[(this->num_chips_ - i - 1) * 8 + j];
|
||||
} else {
|
||||
pixels[j] = this->max_displaybuffer_[i * 8 + j];
|
||||
for (uint8_t chip = 0; chip < this->num_chips_ / this->num_chip_lines_; chip++) {
|
||||
for (uint8_t chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
bool reverse =
|
||||
chip_line % 2 != 0 && this->chip_lines_style_ == ChipLinesStyle::SNAKE ? !this->reverse_ : this->reverse_;
|
||||
if (reverse) {
|
||||
pixels[j] =
|
||||
this->max_displaybuffer_[chip_line][(this->num_chips_ / this->num_chip_lines_ - chip - 1) * 8 + j];
|
||||
} else {
|
||||
pixels[j] = this->max_displaybuffer_[chip_line][chip * 8 + j];
|
||||
}
|
||||
}
|
||||
if (chip_line % 2 != 0 && this->chip_lines_style_ == ChipLinesStyle::SNAKE)
|
||||
this->orientation_ = orientation_180_();
|
||||
this->send64pixels(chip_line * this->num_chips_ / this->num_chip_lines_ + chip, pixels);
|
||||
if (chip_line % 2 != 0 && this->chip_lines_style_ == ChipLinesStyle::SNAKE)
|
||||
this->orientation_ = orientation_180_();
|
||||
}
|
||||
this->send64pixels(i, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MAX7219Component::orientation_180_() {
|
||||
switch (this->orientation_) {
|
||||
case 0:
|
||||
return 2;
|
||||
case 1:
|
||||
return 3;
|
||||
case 2:
|
||||
return 0;
|
||||
case 3:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int MAX7219Component::get_height_internal() {
|
||||
return 8; // TO BE DONE -> STACK TWO DISPLAYS ON TOP OF EACH OTHER
|
||||
// TO BE DONE -> CREATE Virtual size of screen and scroll
|
||||
return 8 * this->num_chip_lines_; // TO BE DONE -> CREATE Virtual size of screen and scroll
|
||||
}
|
||||
|
||||
int MAX7219Component::get_width_internal() { return this->num_chips_ * 8; }
|
||||
|
||||
size_t MAX7219Component::get_buffer_length_() { return this->num_chips_ * 8; }
|
||||
int MAX7219Component::get_width_internal() { return this->num_chips_ / this->num_chip_lines_ * 8; }
|
||||
|
||||
void HOT MAX7219Component::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||
if (x + 1 > this->max_displaybuffer_.size()) { // Extend the display buffer in case required
|
||||
this->max_displaybuffer_.resize(x + 1, this->bckgrnd_);
|
||||
if (x + 1 > this->max_displaybuffer_[0].size()) { // Extend the display buffer in case required
|
||||
for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
|
||||
this->max_displaybuffer_[chip_line].resize(x + 1, this->bckgrnd_);
|
||||
}
|
||||
}
|
||||
|
||||
if ((y >= this->get_height_internal()) || (y < 0) || (x < 0)) // If pixel is outside display then dont draw
|
||||
|
@ -140,9 +168,9 @@ void HOT MAX7219Component::draw_absolute_pixel_internal(int x, int y, Color colo
|
|||
uint8_t subpos = y; // Y is starting at 0 top left
|
||||
|
||||
if (color.is_on()) {
|
||||
this->max_displaybuffer_[pos] |= (1 << subpos);
|
||||
this->max_displaybuffer_[subpos / 8][pos] |= (1 << subpos % 8);
|
||||
} else {
|
||||
this->max_displaybuffer_[pos] &= ~(1 << subpos);
|
||||
this->max_displaybuffer_[subpos / 8][pos] &= ~(1 << subpos % 8);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,8 +186,10 @@ void MAX7219Component::send_to_all_(uint8_t a_register, uint8_t data) {
|
|||
}
|
||||
void MAX7219Component::update() {
|
||||
this->update_ = true;
|
||||
this->max_displaybuffer_.clear();
|
||||
this->max_displaybuffer_.resize(this->num_chips_ * 8, this->bckgrnd_);
|
||||
for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
|
||||
this->max_displaybuffer_[chip_line].clear();
|
||||
this->max_displaybuffer_[chip_line].resize(get_width_internal(), this->bckgrnd_);
|
||||
}
|
||||
if (this->writer_local_.has_value()) // insert Labda function if available
|
||||
(*this->writer_local_)(*this);
|
||||
}
|
||||
|
@ -175,7 +205,7 @@ void MAX7219Component::turn_on_off(bool on_off) {
|
|||
}
|
||||
}
|
||||
|
||||
void MAX7219Component::scroll(bool on_off, uint8_t mode, uint16_t speed, uint16_t delay, uint16_t dwell) {
|
||||
void MAX7219Component::scroll(bool on_off, ScrollMode mode, uint16_t speed, uint16_t delay, uint16_t dwell) {
|
||||
this->set_scroll(on_off);
|
||||
this->set_scroll_mode(mode);
|
||||
this->set_scroll_speed(speed);
|
||||
|
@ -183,7 +213,7 @@ void MAX7219Component::scroll(bool on_off, uint8_t mode, uint16_t speed, uint16_
|
|||
this->set_scroll_delay(delay);
|
||||
}
|
||||
|
||||
void MAX7219Component::scroll(bool on_off, uint8_t mode) {
|
||||
void MAX7219Component::scroll(bool on_off, ScrollMode mode) {
|
||||
this->set_scroll(on_off);
|
||||
this->set_scroll_mode(mode);
|
||||
}
|
||||
|
@ -196,24 +226,26 @@ void MAX7219Component::intensity(uint8_t intensity) {
|
|||
void MAX7219Component::scroll(bool on_off) { this->set_scroll(on_off); }
|
||||
|
||||
void MAX7219Component::scroll_left() {
|
||||
if (this->update_) {
|
||||
this->max_displaybuffer_.push_back(this->bckgrnd_);
|
||||
for (uint16_t i = 0; i < this->stepsleft_; i++) {
|
||||
this->max_displaybuffer_.push_back(this->max_displaybuffer_.front());
|
||||
this->max_displaybuffer_.erase(this->max_displaybuffer_.begin());
|
||||
this->update_ = false;
|
||||
for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
|
||||
if (this->update_) {
|
||||
this->max_displaybuffer_[chip_line].push_back(this->bckgrnd_);
|
||||
for (uint16_t i = 0; i < this->stepsleft_; i++) {
|
||||
this->max_displaybuffer_[chip_line].push_back(this->max_displaybuffer_[chip_line].front());
|
||||
this->max_displaybuffer_[chip_line].erase(this->max_displaybuffer_[chip_line].begin());
|
||||
}
|
||||
} else {
|
||||
this->max_displaybuffer_[chip_line].push_back(this->max_displaybuffer_[chip_line].front());
|
||||
this->max_displaybuffer_[chip_line].erase(this->max_displaybuffer_[chip_line].begin());
|
||||
}
|
||||
} else {
|
||||
this->max_displaybuffer_.push_back(this->max_displaybuffer_.front());
|
||||
this->max_displaybuffer_.erase(this->max_displaybuffer_.begin());
|
||||
}
|
||||
this->update_ = false;
|
||||
this->stepsleft_++;
|
||||
}
|
||||
|
||||
void MAX7219Component::send_char(uint8_t chip, uint8_t data) {
|
||||
// get this character from PROGMEM
|
||||
for (uint8_t i = 0; i < 8; i++)
|
||||
this->max_displaybuffer_[chip * 8 + i] = progmem_read_byte(&MAX7219_DOT_MATRIX_FONT[data][i]);
|
||||
this->max_displaybuffer_[0][chip * 8 + i] = progmem_read_byte(&MAX7219_DOT_MATRIX_FONT[data][i]);
|
||||
} // end of send_char
|
||||
|
||||
// send one character (data) to position (chip)
|
||||
|
|
|
@ -12,6 +12,16 @@
|
|||
namespace esphome {
|
||||
namespace max7219digit {
|
||||
|
||||
enum ChipLinesStyle {
|
||||
ZIGZAG = 0,
|
||||
SNAKE,
|
||||
};
|
||||
|
||||
enum ScrollMode {
|
||||
CONTINUOUS = 0,
|
||||
STOP,
|
||||
};
|
||||
|
||||
class MAX7219Component;
|
||||
|
||||
using max7219_writer_t = std::function<void(MAX7219Component &)>;
|
||||
|
@ -46,20 +56,22 @@ class MAX7219Component : public PollingComponent,
|
|||
|
||||
void set_intensity(uint8_t intensity) { this->intensity_ = intensity; };
|
||||
void set_num_chips(uint8_t num_chips) { this->num_chips_ = num_chips; };
|
||||
void set_num_chip_lines(uint8_t num_chip_lines) { this->num_chip_lines_ = num_chip_lines; };
|
||||
void set_chip_lines_style(ChipLinesStyle chip_lines_style) { this->chip_lines_style_ = chip_lines_style; };
|
||||
void set_chip_orientation(uint8_t rotate) { this->orientation_ = rotate; };
|
||||
void set_scroll_speed(uint16_t speed) { this->scroll_speed_ = speed; };
|
||||
void set_scroll_dwell(uint16_t dwell) { this->scroll_dwell_ = dwell; };
|
||||
void set_scroll_delay(uint16_t delay) { this->scroll_delay_ = delay; };
|
||||
void set_scroll(bool on_off) { this->scroll_ = on_off; };
|
||||
void set_scroll_mode(uint8_t mode) { this->scroll_mode_ = mode; };
|
||||
void set_scroll_mode(ScrollMode mode) { this->scroll_mode_ = mode; };
|
||||
void set_reverse(bool on_off) { this->reverse_ = on_off; };
|
||||
|
||||
void send_char(uint8_t chip, uint8_t data);
|
||||
void send64pixels(uint8_t chip, const uint8_t pixels[8]);
|
||||
|
||||
void scroll_left();
|
||||
void scroll(bool on_off, uint8_t mode, uint16_t speed, uint16_t delay, uint16_t dwell);
|
||||
void scroll(bool on_off, uint8_t mode);
|
||||
void scroll(bool on_off, ScrollMode mode, uint16_t speed, uint16_t delay, uint16_t dwell);
|
||||
void scroll(bool on_off, ScrollMode mode);
|
||||
void scroll(bool on_off);
|
||||
void intensity(uint8_t intensity);
|
||||
|
||||
|
@ -84,9 +96,12 @@ class MAX7219Component : public PollingComponent,
|
|||
protected:
|
||||
void send_byte_(uint8_t a_register, uint8_t data);
|
||||
void send_to_all_(uint8_t a_register, uint8_t data);
|
||||
uint8_t orientation_180_();
|
||||
|
||||
uint8_t intensity_; /// Intensity of the display from 0 to 15 (most)
|
||||
uint8_t num_chips_;
|
||||
uint8_t num_chip_lines_;
|
||||
ChipLinesStyle chip_lines_style_;
|
||||
bool scroll_;
|
||||
bool reverse_;
|
||||
bool update_{false};
|
||||
|
@ -94,11 +109,11 @@ class MAX7219Component : public PollingComponent,
|
|||
uint16_t scroll_delay_;
|
||||
uint16_t scroll_dwell_;
|
||||
uint16_t old_buffer_size_ = 0;
|
||||
uint8_t scroll_mode_;
|
||||
ScrollMode scroll_mode_;
|
||||
bool invert_ = false;
|
||||
uint8_t orientation_;
|
||||
uint8_t bckgrnd_ = 0x0;
|
||||
std::vector<uint8_t> max_displaybuffer_;
|
||||
std::vector<std::vector<uint8_t>> max_displaybuffer_;
|
||||
uint32_t last_scroll_ = 0;
|
||||
uint16_t stepsleft_;
|
||||
size_t get_buffer_length_();
|
||||
|
|
Loading…
Reference in a new issue