ESPHome 2025.5.0
Loading...
Searching...
No Matches
max7219digit.cpp
Go to the documentation of this file.
1#include "max7219digit.h"
2#include "esphome/core/log.h"
4#include "esphome/core/hal.h"
6#include "max7219font.h"
7
8#include <algorithm>
9
10namespace esphome {
11namespace max7219digit {
12
13static const char *const TAG = "max7219DIGIT";
14
15static const uint8_t MAX7219_REGISTER_NOOP = 0x00;
16static const uint8_t MAX7219_REGISTER_DECODE_MODE = 0x09;
17static const uint8_t MAX7219_REGISTER_INTENSITY = 0x0A;
18static const uint8_t MAX7219_REGISTER_SCAN_LIMIT = 0x0B;
19static const uint8_t MAX7219_REGISTER_SHUTDOWN = 0x0C;
20static const uint8_t MAX7219_REGISTER_DISPLAY_TEST = 0x0F;
21constexpr uint8_t MAX7219_NO_SHUTDOWN = 0x00;
22constexpr uint8_t MAX7219_SHUTDOWN = 0x01;
23constexpr uint8_t MAX7219_NO_DISPLAY_TEST = 0x00;
24constexpr uint8_t MAX7219_DISPLAY_TEST = 0x01;
25
27
29 ESP_LOGCONFIG(TAG, "Setting up MAX7219_DIGITS...");
30 this->spi_setup();
31 this->stepsleft_ = 0;
32 for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
33 std::vector<uint8_t> vec(1);
34 this->max_displaybuffer_.push_back(vec);
35 // Initialize buffer with 0 for display so all non written pixels are blank
36 this->max_displaybuffer_[chip_line].resize(get_width_internal(), 0);
37 }
38 // let's assume the user has all 8 digits connected, only important in daisy chained setups anyway
39 this->send_to_all_(MAX7219_REGISTER_SCAN_LIMIT, 7);
40 // let's use our own ASCII -> led pattern encoding
41 this->send_to_all_(MAX7219_REGISTER_DECODE_MODE, 0);
42 // No display test with all the pixels on
43 this->send_to_all_(MAX7219_REGISTER_DISPLAY_TEST, MAX7219_NO_DISPLAY_TEST);
44 // SET Intsity of display
45 this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_);
46 // this->send_to_all_(MAX7219_REGISTER_INTENSITY, 1);
47 this->display();
48 // power up
49 this->send_to_all_(MAX7219_REGISTER_SHUTDOWN, 1);
50}
51
53 ESP_LOGCONFIG(TAG, "MAX7219DIGIT:");
54 ESP_LOGCONFIG(TAG, " Number of Chips: %u", this->num_chips_);
55 ESP_LOGCONFIG(TAG, " Number of Chips Lines: %u", this->num_chip_lines_);
56 ESP_LOGCONFIG(TAG, " Chips Lines Style : %u", this->chip_lines_style_);
57 ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_);
58 ESP_LOGCONFIG(TAG, " Scroll Mode: %u", this->scroll_mode_);
59 ESP_LOGCONFIG(TAG, " Scroll Speed: %u", this->scroll_speed_);
60 ESP_LOGCONFIG(TAG, " Scroll Dwell: %u", this->scroll_dwell_);
61 ESP_LOGCONFIG(TAG, " Scroll Delay: %u", this->scroll_delay_);
62 LOG_PIN(" CS Pin: ", this->cs_);
63 LOG_UPDATE_INTERVAL(this);
64}
65
67 const uint32_t now = App.get_loop_component_start_time();
68 const uint32_t millis_since_last_scroll = now - this->last_scroll_;
69 const size_t first_line_size = this->max_displaybuffer_[0].size();
70 // check if the buffer has shrunk past the current position since last update
71 if ((first_line_size >= this->old_buffer_size_ + 3) || (first_line_size <= this->old_buffer_size_ - 3)) {
72 ESP_LOGV(TAG, "Buffer size changed %d to %d", this->old_buffer_size_, first_line_size);
73 this->stepsleft_ = 0;
74 this->display();
75 this->old_buffer_size_ = first_line_size;
76 }
77
78 if (!this->scroll_ || (first_line_size <= (size_t) get_width_internal())) {
79 ESP_LOGVV(TAG, "Return if there is no need to scroll or scroll is off.");
80 this->display();
81 return;
82 }
83
84 if ((this->stepsleft_ == 0) && (millis_since_last_scroll < this->scroll_delay_)) {
85 ESP_LOGVV(TAG, "At first step. Waiting for scroll delay");
86 this->display();
87 return;
88 }
89
90 if (this->scroll_mode_ == ScrollMode::STOP) {
91 if (this->stepsleft_ + get_width_internal() == first_line_size + 1) {
92 if (millis_since_last_scroll < this->scroll_dwell_) {
93 ESP_LOGVV(TAG, "Dwell time at end of string in case of stop at end. Step %d, since last scroll %d, dwell %d.",
94 this->stepsleft_, millis_since_last_scroll, this->scroll_dwell_);
95 return;
96 }
97 ESP_LOGV(TAG, "Dwell time passed. Continue scrolling.");
98 }
99 }
100
101 if (millis_since_last_scroll >= this->scroll_speed_) {
102 ESP_LOGVV(TAG, "Call to scroll left action");
103 this->last_scroll_ = now;
104 this->scroll_left();
105 this->display();
106 }
107}
108
110 uint8_t pixels[8];
111 // Run this loop for every MAX CHIP (GRID OF 64 leds)
112 // Run this routine for the rows of every chip 8x row 0 top to 7 bottom
113 // Fill the pixel parameter with display data
114 // Send the data to the chip
115 for (uint8_t chip = 0; chip < this->num_chips_ / this->num_chip_lines_; chip++) {
116 for (uint8_t chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
117 for (uint8_t j = 0; j < 8; j++) {
118 bool reverse =
119 chip_line % 2 != 0 && this->chip_lines_style_ == ChipLinesStyle::SNAKE ? !this->reverse_ : this->reverse_;
120 if (reverse) {
121 pixels[j] =
122 this->max_displaybuffer_[chip_line][(this->num_chips_ / this->num_chip_lines_ - chip - 1) * 8 + j];
123 } else {
124 pixels[j] = this->max_displaybuffer_[chip_line][chip * 8 + j];
125 }
126 }
127 if (chip_line % 2 != 0 && this->chip_lines_style_ == ChipLinesStyle::SNAKE)
129 this->send64pixels(chip_line * this->num_chips_ / this->num_chip_lines_ + chip, pixels);
130 if (chip_line % 2 != 0 && this->chip_lines_style_ == ChipLinesStyle::SNAKE)
132 }
133 }
134}
135
137 switch (this->orientation_) {
138 case 0:
139 return 2;
140 case 1:
141 return 3;
142 case 2:
143 return 0;
144 case 3:
145 return 1;
146 default:
147 return 0;
148 }
149}
150
152 return 8 * this->num_chip_lines_; // TO BE DONE -> CREATE Virtual size of screen and scroll
153}
154
156
158 if (x + 1 > (int) this->max_displaybuffer_[0].size()) { // Extend the display buffer in case required
159 for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
160 this->max_displaybuffer_[chip_line].resize(x + 1, this->bckgrnd_);
161 }
162 }
163
164 if ((y >= this->get_height_internal()) || (y < 0) || (x < 0)) // If pixel is outside display then dont draw
165 return;
166
167 uint16_t pos = x; // X is starting at 0 top left
168 uint8_t subpos = y; // Y is starting at 0 top left
169
170 if (color.is_on()) {
171 this->max_displaybuffer_[subpos / 8][pos] |= (1 << subpos % 8);
172 } else {
173 this->max_displaybuffer_[subpos / 8][pos] &= ~(1 << subpos % 8);
174 }
175}
176
177void MAX7219Component::send_byte_(uint8_t a_register, uint8_t data) {
178 this->write_byte(a_register); // Write register value to MAX
179 this->write_byte(data); // Followed by actual data
180}
181void MAX7219Component::send_to_all_(uint8_t a_register, uint8_t data) {
182 this->enable(); // Enable SPI
183 for (uint8_t i = 0; i < this->num_chips_; i++) // Run the loop for every MAX chip in the stack
184 this->send_byte_(a_register, data); // Send the data to the chips
185 this->disable(); // Disable SPI
186}
188 this->update_ = true;
189 for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
190 this->max_displaybuffer_[chip_line].clear();
191 this->max_displaybuffer_[chip_line].resize(get_width_internal(), this->bckgrnd_);
192 }
193 if (this->writer_local_.has_value()) // insert Labda function if available
194 (*this->writer_local_)(*this);
195}
196
197void MAX7219Component::invert_on_off(bool on_off) { this->invert_ = on_off; };
199
201 if (on_off) {
202 this->send_to_all_(MAX7219_REGISTER_SHUTDOWN, 1);
203 } else {
204 this->send_to_all_(MAX7219_REGISTER_SHUTDOWN, 0);
205 }
206}
207
208void MAX7219Component::scroll(bool on_off, ScrollMode mode, uint16_t speed, uint16_t delay, uint16_t dwell) {
209 this->set_scroll(on_off);
210 this->set_scroll_mode(mode);
211 this->set_scroll_speed(speed);
212 this->set_scroll_dwell(dwell);
213 this->set_scroll_delay(delay);
214}
215
217 this->set_scroll(on_off);
218 this->set_scroll_mode(mode);
219}
220
221void MAX7219Component::intensity(uint8_t intensity) {
222 this->intensity_ = intensity;
223 this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_);
224}
225
226void MAX7219Component::scroll(bool on_off) { this->set_scroll(on_off); }
227
229 for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
230 auto scroll = [&](std::vector<uint8_t> &line, uint16_t steps) {
231 std::rotate(line.begin(), std::next(line.begin(), steps), line.end());
232 };
233 if (this->update_) {
234 this->max_displaybuffer_[chip_line].push_back(this->bckgrnd_);
235 scroll(this->max_displaybuffer_[chip_line],
236 (this->stepsleft_ + 1) % (this->max_displaybuffer_[chip_line].size()));
237 } else {
238 scroll(this->max_displaybuffer_[chip_line], 1);
239 }
240 }
241 this->update_ = false;
242 this->stepsleft_++;
243 this->stepsleft_ %= this->max_displaybuffer_[0].size();
244}
245
246void MAX7219Component::send_char(uint8_t chip, uint8_t data) {
247 // get this character from PROGMEM
248 for (uint8_t i = 0; i < 8; i++)
249 this->max_displaybuffer_[0][chip * 8 + i] = progmem_read_byte(&MAX7219_DOT_MATRIX_FONT[data][i]);
250} // end of send_char
251
252// send one character (data) to position (chip)
253
254void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
255 for (uint8_t col = 0; col < 8; col++) { // RUN THIS LOOP 8 times until column is 7
256 this->enable(); // start sending by enabling SPI
257 for (uint8_t i = 0; i < chip; i++) { // send extra NOPs to push the pixels out to extra displays
258 this->send_byte_(MAX7219_REGISTER_NOOP,
259 MAX7219_REGISTER_NOOP); // run this loop unit the matching chip is reached
260 }
261 uint8_t b = 0; // rotate pixels 90 degrees -- set byte to 0
262 if (this->orientation_ == 0) {
263 for (uint8_t i = 0; i < 8; i++) {
264 // run this loop 8 times for all the pixels[8] received
265 if (this->flip_x_) {
266 b |= ((pixels[i] >> col) & 1) << i; // change the column bits into row bits
267 } else {
268 b |= ((pixels[i] >> col) & 1) << (7 - i); // change the column bits into row bits
269 }
270 }
271 } else if (this->orientation_ == 1) {
272 b = pixels[col];
273 } else if (this->orientation_ == 2) {
274 for (uint8_t i = 0; i < 8; i++) {
275 if (this->flip_x_) {
276 b |= ((pixels[i] >> (7 - col)) & 1) << (7 - i);
277 } else {
278 b |= ((pixels[i] >> (7 - col)) & 1) << i;
279 }
280 }
281 } else {
282 for (uint8_t i = 0; i < 8; i++) {
283 b |= ((pixels[7 - col] >> i) & 1) << (7 - i);
284 }
285 }
286 // send this byte to display at selected chip
287 if (this->invert_) {
288 this->send_byte_(col + 1, ~b);
289 } else {
290 this->send_byte_(col + 1, b);
291 }
292 for (int i = 0; i < this->num_chips_ - chip - 1; i++) // end with enough NOPs so later chips don't update
293 this->send_byte_(MAX7219_REGISTER_NOOP, MAX7219_REGISTER_NOOP);
294 this->disable(); // all done disable SPI
295 } // end of for each column
296} // end of send64pixels
297
298uint8_t MAX7219Component::printdigit(const char *str) { return this->printdigit(0, str); }
299
300uint8_t MAX7219Component::printdigit(uint8_t start_pos, const char *s) {
301 uint8_t chip = start_pos;
302 for (; chip < this->num_chips_ && *s; chip++)
303 send_char(chip, *s++);
304 // space out rest
305 while (chip < (this->num_chips_))
306 send_char(chip++, ' ');
307 return 0;
308} // end of sendString
309
310uint8_t MAX7219Component::printdigitf(uint8_t pos, const char *format, ...) {
311 va_list arg;
312 va_start(arg, format);
313 char buffer[64];
314 int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
315 va_end(arg);
316 if (ret > 0)
317 return this->printdigit(pos, buffer);
318 return 0;
319}
320uint8_t MAX7219Component::printdigitf(const char *format, ...) {
321 va_list arg;
322 va_start(arg, format);
323 char buffer[64];
324 int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
325 va_end(arg);
326 if (ret > 0)
327 return this->printdigit(buffer);
328 return 0;
329}
330
331uint8_t MAX7219Component::strftimedigit(uint8_t pos, const char *format, ESPTime time) {
332 char buffer[64];
333 size_t ret = time.strftime(buffer, sizeof(buffer), format);
334 if (ret > 0)
335 return this->printdigit(pos, buffer);
336 return 0;
337}
338uint8_t MAX7219Component::strftimedigit(const char *format, ESPTime time) {
339 return this->strftimedigit(0, format, time);
340}
341
342} // namespace max7219digit
343} // namespace esphome
BedjetMode mode
BedJet operating mode.
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void line(int x1, int y1, int x2, int y2, Color color=COLOR_ON)
Draw a straight line from the point [x1,y1] to [x2,y2] with the given color.
Definition display.cpp:18
void send_to_all_(uint8_t a_register, uint8_t data)
void draw_absolute_pixel_internal(int x, int y, Color color) override
uint8_t uint8_t uint8_t printdigit(uint8_t pos, const char *str)
Print str at the given position.
uint8_t printdigitf(uint8_t pos, const char *format,...) __attribute__((format(printf
Evaluate the printf-format and print the result at the given position.
void send64pixels(uint8_t chip, const uint8_t pixels[8])
void scroll(bool on_off, ScrollMode mode, uint16_t speed, uint16_t delay, uint16_t dwell)
void send_byte_(uint8_t a_register, uint8_t data)
float get_setup_priority() const override
void send_char(uint8_t chip, uint8_t data)
optional< max7219_writer_t > writer_local_
uint8_t num_chips_
Intensity of the display from 0 to 15 (most)
uint8_t strftimedigit(uint8_t pos, const char *format, ESPTime time) __attribute__((format(strftime
Evaluate the strftime-format and print the result at the given position.
std::vector< std::vector< uint8_t > > max_displaybuffer_
bool has_value() const
Definition optional.h:87
constexpr uint8_t MAX7219_NO_DISPLAY_TEST
constexpr uint8_t MAX7219_NO_SHUTDOWN
constexpr uint8_t MAX7219_DISPLAY_TEST
constexpr uint8_t MAX7219_SHUTDOWN
const float PROCESSOR
For components that use data from sensors like displays.
Definition component.cpp:20
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.
uint8_t progmem_read_byte(const uint8_t *addr)
Definition core.cpp:57
bool is_on() ESPHOME_ALWAYS_INLINE
Definition color.h:46
A more user-friendly version of struct tm from time.h.
Definition time.h:15
size_t strftime(char *buffer, size_t buffer_len, const char *format)
Convert this ESPTime struct to a null-terminated c string buffer as specified by the format argument.
Definition time.cpp:15
uint16_t x
Definition tt21100.cpp:5
uint16_t y
Definition tt21100.cpp:6