ESPHome  2024.3.1
max7219.cpp
Go to the documentation of this file.
1 #include "max7219.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 #include "esphome/core/hal.h"
5 
6 namespace esphome {
7 namespace max7219 {
8 
9 static const char *const TAG = "max7219";
10 
11 static const uint8_t MAX7219_REGISTER_NOOP = 0x00;
12 static const uint8_t MAX7219_REGISTER_DECODE_MODE = 0x09;
13 static const uint8_t MAX7219_REGISTER_INTENSITY = 0x0A;
14 static const uint8_t MAX7219_REGISTER_SCAN_LIMIT = 0x0B;
15 static const uint8_t MAX7219_REGISTER_SHUTDOWN = 0x0C;
16 static const uint8_t MAX7219_REGISTER_TEST = 0x0F;
17 static const uint8_t MAX7219_UNKNOWN_CHAR = 0b11111111;
18 
19 const uint8_t MAX7219_ASCII_TO_RAW[95] PROGMEM = {
20  0b00000000, // ' ', ord 0x20
21  0b10110000, // '!', ord 0x21
22  0b00100010, // '"', ord 0x22
23  MAX7219_UNKNOWN_CHAR, // '#', ord 0x23
24  MAX7219_UNKNOWN_CHAR, // '$', ord 0x24
25  0b01001001, // '%', ord 0x25
26  MAX7219_UNKNOWN_CHAR, // '&', ord 0x26
27  0b00000010, // ''', ord 0x27
28  0b01001110, // '(', ord 0x28
29  0b01111000, // ')', ord 0x29
30  0b01000000, // '*', ord 0x2A
31  MAX7219_UNKNOWN_CHAR, // '+', ord 0x2B
32  0b00010000, // ',', ord 0x2C
33  0b00000001, // '-', ord 0x2D
34  0b10000000, // '.', ord 0x2E
35  MAX7219_UNKNOWN_CHAR, // '/', ord 0x2F
36  0b01111110, // '0', ord 0x30
37  0b00110000, // '1', ord 0x31
38  0b01101101, // '2', ord 0x32
39  0b01111001, // '3', ord 0x33
40  0b00110011, // '4', ord 0x34
41  0b01011011, // '5', ord 0x35
42  0b01011111, // '6', ord 0x36
43  0b01110000, // '7', ord 0x37
44  0b01111111, // '8', ord 0x38
45  0b01111011, // '9', ord 0x39
46  0b01001000, // ':', ord 0x3A
47  0b01011000, // ';', ord 0x3B
48  MAX7219_UNKNOWN_CHAR, // '<', ord 0x3C
49  0b00001001, // '=', ord 0x3D
50  MAX7219_UNKNOWN_CHAR, // '>', ord 0x3E
51  0b01100101, // '?', ord 0x3F
52  0b01101111, // '@', ord 0x40
53  0b01110111, // 'A', ord 0x41
54  0b00011111, // 'B', ord 0x42
55  0b01001110, // 'C', ord 0x43
56  0b00111101, // 'D', ord 0x44
57  0b01001111, // 'E', ord 0x45
58  0b01000111, // 'F', ord 0x46
59  0b01011110, // 'G', ord 0x47
60  0b00110111, // 'H', ord 0x48
61  0b00110000, // 'I', ord 0x49
62  0b00111100, // 'J', ord 0x4A
63  MAX7219_UNKNOWN_CHAR, // 'K', ord 0x4B
64  0b00001110, // 'L', ord 0x4C
65  MAX7219_UNKNOWN_CHAR, // 'M', ord 0x4D
66  0b00010101, // 'N', ord 0x4E
67  0b01111110, // 'O', ord 0x4F
68  0b01100111, // 'P', ord 0x50
69  0b11111110, // 'Q', ord 0x51
70  0b00000101, // 'R', ord 0x52
71  0b01011011, // 'S', ord 0x53
72  0b00000111, // 'T', ord 0x54
73  0b00111110, // 'U', ord 0x55
74  0b00111110, // 'V', ord 0x56
75  0b00111111, // 'W', ord 0x57
76  MAX7219_UNKNOWN_CHAR, // 'X', ord 0x58
77  0b00100111, // 'Y', ord 0x59
78  0b01101101, // 'Z', ord 0x5A
79  0b01001110, // '[', ord 0x5B
80  MAX7219_UNKNOWN_CHAR, // '\', ord 0x5C
81  0b01111000, // ']', ord 0x5D
82  MAX7219_UNKNOWN_CHAR, // '^', ord 0x5E
83  0b00001000, // '_', ord 0x5F
84  0b00100000, // '`', ord 0x60
85  0b01110111, // 'a', ord 0x61
86  0b00011111, // 'b', ord 0x62
87  0b00001101, // 'c', ord 0x63
88  0b00111101, // 'd', ord 0x64
89  0b01001111, // 'e', ord 0x65
90  0b01000111, // 'f', ord 0x66
91  0b01011110, // 'g', ord 0x67
92  0b00010111, // 'h', ord 0x68
93  0b00010000, // 'i', ord 0x69
94  0b00111100, // 'j', ord 0x6A
95  MAX7219_UNKNOWN_CHAR, // 'k', ord 0x6B
96  0b00001110, // 'l', ord 0x6C
97  MAX7219_UNKNOWN_CHAR, // 'm', ord 0x6D
98  0b00010101, // 'n', ord 0x6E
99  0b00011101, // 'o', ord 0x6F
100  0b01100111, // 'p', ord 0x70
101  MAX7219_UNKNOWN_CHAR, // 'q', ord 0x71
102  0b00000101, // 'r', ord 0x72
103  0b01011011, // 's', ord 0x73
104  0b00000111, // 't', ord 0x74
105  0b00011100, // 'u', ord 0x75
106  0b00011100, // 'v', ord 0x76
107  MAX7219_UNKNOWN_CHAR, // 'w', ord 0x77
108  MAX7219_UNKNOWN_CHAR, // 'x', ord 0x78
109  0b00100111, // 'y', ord 0x79
110  MAX7219_UNKNOWN_CHAR, // 'z', ord 0x7A
111  0b00110001, // '{', ord 0x7B
112  0b00000110, // '|', ord 0x7C
113  0b00000111, // '}', ord 0x7D
114  0b01100011, // '~', ord 0x7E (degree symbol)
115 };
116 
119  ESP_LOGCONFIG(TAG, "Setting up MAX7219...");
120  this->spi_setup();
121  this->buffer_ = new uint8_t[this->num_chips_ * 8]; // NOLINT
122  for (uint8_t i = 0; i < this->num_chips_ * 8; i++)
123  this->buffer_[i] = 0;
124 
125  // let's assume the user has all 8 digits connected, only important in daisy chained setups anyway
126  this->send_to_all_(MAX7219_REGISTER_SCAN_LIMIT, 7);
127  // let's use our own ASCII -> led pattern encoding
128  this->send_to_all_(MAX7219_REGISTER_DECODE_MODE, 0);
129  this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_);
130  this->display();
131  // power up
132  this->send_to_all_(MAX7219_REGISTER_TEST, 0);
133  this->send_to_all_(MAX7219_REGISTER_SHUTDOWN, 1);
134 }
136  ESP_LOGCONFIG(TAG, "MAX7219:");
137  ESP_LOGCONFIG(TAG, " Number of Chips: %u", this->num_chips_);
138  ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_);
139  LOG_PIN(" CS Pin: ", this->cs_);
140  LOG_UPDATE_INTERVAL(this);
141 }
142 
144  for (uint8_t i = 0; i < 8; i++) {
145  this->enable();
146  for (uint8_t j = 0; j < this->num_chips_; j++) {
147  if (reverse_) {
148  this->send_byte_(8 - i, buffer_[(num_chips_ - j - 1) * 8 + i]);
149  } else {
150  this->send_byte_(8 - i, buffer_[j * 8 + i]);
151  }
152  }
153  this->disable();
154  }
155 }
156 void MAX7219Component::send_byte_(uint8_t a_register, uint8_t data) {
157  this->write_byte(a_register);
158  this->write_byte(data);
159 }
160 void MAX7219Component::send_to_all_(uint8_t a_register, uint8_t data) {
161  this->enable();
162  for (uint8_t i = 0; i < this->num_chips_; i++)
163  this->send_byte_(a_register, data);
164  this->disable();
165 }
167  if (this->intensity_changed_) {
168  this->send_to_all_(MAX7219_REGISTER_INTENSITY, this->intensity_);
169  this->intensity_changed_ = false;
170  }
171  for (uint8_t i = 0; i < this->num_chips_ * 8; i++)
172  this->buffer_[i] = 0;
173  if (this->writer_.has_value())
174  (*this->writer_)(*this);
175  this->display();
176 }
177 uint8_t MAX7219Component::print(uint8_t start_pos, const char *str) {
178  uint8_t pos = start_pos;
179  for (; *str != '\0'; str++) {
180  uint8_t data = MAX7219_UNKNOWN_CHAR;
181  if (*str >= ' ' && *str <= '~')
182  data = progmem_read_byte(&MAX7219_ASCII_TO_RAW[*str - ' ']);
183 
184  if (data == MAX7219_UNKNOWN_CHAR) {
185  ESP_LOGW(TAG, "Encountered character '%c' with no MAX7219 representation while translating string!", *str);
186  }
187  if (*str == '.') {
188  if (pos != start_pos)
189  pos--;
190  this->buffer_[pos] |= 0b10000000;
191  } else {
192  if (pos >= this->num_chips_ * 8) {
193  ESP_LOGE(TAG, "MAX7219 String is too long for the display!");
194  break;
195  }
196  this->buffer_[pos] = data;
197  }
198  pos++;
199  }
200  return pos - start_pos;
201 }
202 uint8_t MAX7219Component::print(const char *str) { return this->print(0, str); }
203 uint8_t MAX7219Component::printf(uint8_t pos, const char *format, ...) {
204  va_list arg;
205  va_start(arg, format);
206  char buffer[64];
207  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
208  va_end(arg);
209  if (ret > 0)
210  return this->print(pos, buffer);
211  return 0;
212 }
213 uint8_t MAX7219Component::printf(const char *format, ...) {
214  va_list arg;
215  va_start(arg, format);
216  char buffer[64];
217  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
218  va_end(arg);
219  if (ret > 0)
220  return this->print(buffer);
221  return 0;
222 }
223 void MAX7219Component::set_writer(max7219_writer_t &&writer) { this->writer_ = writer; }
224 void MAX7219Component::set_intensity(uint8_t intensity) {
225  intensity &= 0xF;
226  if (intensity != this->intensity_) {
227  this->intensity_changed_ = true;
228  this->intensity_ = intensity;
229  }
230 }
231 void MAX7219Component::set_num_chips(uint8_t num_chips) { this->num_chips_ = num_chips; }
232 
233 uint8_t MAX7219Component::strftime(uint8_t pos, const char *format, ESPTime time) {
234  char buffer[64];
235  size_t ret = time.strftime(buffer, sizeof(buffer), format);
236  if (ret > 0)
237  return this->print(pos, buffer);
238  return 0;
239 }
240 uint8_t MAX7219Component::strftime(const char *format, ESPTime time) { return this->strftime(0, format, time); }
241 
242 } // namespace max7219
243 } // namespace esphome
void send_to_all_(uint8_t a_register, uint8_t data)
Definition: max7219.cpp:160
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:20
uint8_t uint8_t uint8_t print(uint8_t pos, const char *str)
Print str at the given position.
Definition: max7219.cpp:177
A more user-friendly version of struct tm from time.h.
Definition: time.h:17
void set_intensity(uint8_t intensity)
Definition: max7219.cpp:224
void send_byte_(uint8_t a_register, uint8_t data)
Definition: max7219.cpp:156
GPIOPin * cs_
Definition: spi.h:395
bool has_value() const
Definition: optional.h:87
const float PROCESSOR
For components that use data from sensors like displays.
Definition: component.cpp:20
optional< max7219_writer_t > writer_
Definition: max7219.h:60
uint8_t printf(uint8_t pos, const char *format,...) __attribute__((format(printf
Evaluate the printf-format and print the result at the given position.
Definition: max7219.cpp:203
uint8_t strftime(uint8_t pos, const char *format, ESPTime time) __attribute__((format(strftime
Evaluate the strftime-format and print the result at the given position.
Definition: max7219.cpp:233
uint8_t progmem_read_byte(const uint8_t *addr)
Definition: core.cpp:55
std::function< void(MAX7219Component &)> max7219_writer_t
Definition: max7219.h:13
float get_setup_priority() const override
Definition: max7219.cpp:117
void set_writer(max7219_writer_t &&writer)
Definition: max7219.cpp:223
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
const uint8_t MAX7219_ASCII_TO_RAW [95] PROGMEM
Definition: max7219.cpp:19
void set_num_chips(uint8_t num_chips)
Definition: max7219.cpp:231