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:
TVDLoewe 2021-11-10 19:41:04 +01:00 committed by GitHub
parent c422b2fb0b
commit 92321e219a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 110 additions and 47 deletions

View file

@ -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]))

View file

@ -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
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_.resize(this->num_chips_ * 8, 0);
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 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++) {
if (this->reverse_) {
pixels[j] = this->max_displaybuffer_[(this->num_chips_ - i - 1) * 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_[i * 8 + j];
pixels[j] = this->max_displaybuffer_[chip_line][chip * 8 + j];
}
}
this->send64pixels(i, pixels);
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_();
}
}
}
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() {
for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
if (this->update_) {
this->max_displaybuffer_.push_back(this->bckgrnd_);
this->max_displaybuffer_[chip_line].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;
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->max_displaybuffer_[chip_line].push_back(this->max_displaybuffer_[chip_line].front());
this->max_displaybuffer_[chip_line].erase(this->max_displaybuffer_[chip_line].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)

View file

@ -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_();