ESPHome  2024.10.0
tm1637.cpp
Go to the documentation of this file.
1 #include "tm1637.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 tm1637 {
8 
9 static const char *const TAG = "display.tm1637";
10 const uint8_t TM1637_CMD_DATA = 0x40;
11 const uint8_t TM1637_CMD_CTRL = 0x80;
12 const uint8_t TM1637_CMD_ADDR = 0xc0;
13 const uint8_t TM1637_UNKNOWN_CHAR = 0b11111111;
14 
15 // Data command bits
16 const uint8_t TM1637_DATA_WRITE = 0x00;
17 const uint8_t TM1637_DATA_READ_KEYS = 0x02;
18 const uint8_t TM1637_DATA_AUTO_INC_ADDR = 0x00;
19 const uint8_t TM1637_DATA_FIXED_ADDR = 0x04;
20 
21 //
22 // A
23 // ---
24 // F | | B
25 // -G-
26 // E | | C
27 // ---
28 // D X
29 // XABCDEFG
30 const uint8_t TM1637_ASCII_TO_RAW[] PROGMEM = {
31  0b00000000, // ' ', ord 0x20
32  0b10110000, // '!', ord 0x21
33  0b00100010, // '"', ord 0x22
34  TM1637_UNKNOWN_CHAR, // '#', ord 0x23
35  TM1637_UNKNOWN_CHAR, // '$', ord 0x24
36  0b01001001, // '%', ord 0x25
37  TM1637_UNKNOWN_CHAR, // '&', ord 0x26
38  0b00000010, // ''', ord 0x27
39  0b01001110, // '(', ord 0x28
40  0b01111000, // ')', ord 0x29
41  0b01000000, // '*', ord 0x2A
42  TM1637_UNKNOWN_CHAR, // '+', ord 0x2B
43  0b00010000, // ',', ord 0x2C
44  0b00000001, // '-', ord 0x2D
45  0b10000000, // '.', ord 0x2E
46  TM1637_UNKNOWN_CHAR, // '/', ord 0x2F
47  0b01111110, // '0', ord 0x30
48  0b00110000, // '1', ord 0x31
49  0b01101101, // '2', ord 0x32
50  0b01111001, // '3', ord 0x33
51  0b00110011, // '4', ord 0x34
52  0b01011011, // '5', ord 0x35
53  0b01011111, // '6', ord 0x36
54  0b01110000, // '7', ord 0x37
55  0b01111111, // '8', ord 0x38
56  0b01111011, // '9', ord 0x39
57  0b01001000, // ':', ord 0x3A
58  0b01011000, // ';', ord 0x3B
59  TM1637_UNKNOWN_CHAR, // '<', ord 0x3C
60  TM1637_UNKNOWN_CHAR, // '=', ord 0x3D
61  TM1637_UNKNOWN_CHAR, // '>', ord 0x3E
62  0b01100101, // '?', ord 0x3F
63  0b01101111, // '@', ord 0x40
64  0b01110111, // 'A', ord 0x41
65  0b00011111, // 'B', ord 0x42
66  0b01001110, // 'C', ord 0x43
67  0b00111101, // 'D', ord 0x44
68  0b01001111, // 'E', ord 0x45
69  0b01000111, // 'F', ord 0x46
70  0b01011110, // 'G', ord 0x47
71  0b00110111, // 'H', ord 0x48
72  0b00110000, // 'I', ord 0x49
73  0b00111100, // 'J', ord 0x4A
74  TM1637_UNKNOWN_CHAR, // 'K', ord 0x4B
75  0b00001110, // 'L', ord 0x4C
76  TM1637_UNKNOWN_CHAR, // 'M', ord 0x4D
77  0b00010101, // 'N', ord 0x4E
78  0b01111110, // 'O', ord 0x4F
79  0b01100111, // 'P', ord 0x50
80  0b11111110, // 'Q', ord 0x51
81  0b00000101, // 'R', ord 0x52
82  0b01011011, // 'S', ord 0x53
83  0b00000111, // 'T', ord 0x54
84  0b00111110, // 'U', ord 0x55
85  0b00111110, // 'V', ord 0x56
86  0b00111111, // 'W', ord 0x57
87  TM1637_UNKNOWN_CHAR, // 'X', ord 0x58
88  0b00100111, // 'Y', ord 0x59
89  0b01101101, // 'Z', ord 0x5A
90  0b01001110, // '[', ord 0x5B
91  TM1637_UNKNOWN_CHAR, // '\', ord 0x5C
92  0b01111000, // ']', ord 0x5D
93  TM1637_UNKNOWN_CHAR, // '^', ord 0x5E
94  0b00001000, // '_', ord 0x5F
95  0b00100000, // '`', ord 0x60
96  0b01110111, // 'a', ord 0x61
97  0b00011111, // 'b', ord 0x62
98  0b00001101, // 'c', ord 0x63
99  0b00111101, // 'd', ord 0x64
100  0b01001111, // 'e', ord 0x65
101  0b01000111, // 'f', ord 0x66
102  0b01011110, // 'g', ord 0x67
103  0b00010111, // 'h', ord 0x68
104  0b00010000, // 'i', ord 0x69
105  0b00111100, // 'j', ord 0x6A
106  TM1637_UNKNOWN_CHAR, // 'k', ord 0x6B
107  0b00001110, // 'l', ord 0x6C
108  TM1637_UNKNOWN_CHAR, // 'm', ord 0x6D
109  0b00010101, // 'n', ord 0x6E
110  0b00011101, // 'o', ord 0x6F
111  0b01100111, // 'p', ord 0x70
112  TM1637_UNKNOWN_CHAR, // 'q', ord 0x71
113  0b00000101, // 'r', ord 0x72
114  0b01011011, // 's', ord 0x73
115  0b00000111, // 't', ord 0x74
116  0b00011100, // 'u', ord 0x75
117  0b00011100, // 'v', ord 0x76
118  TM1637_UNKNOWN_CHAR, // 'w', ord 0x77
119  TM1637_UNKNOWN_CHAR, // 'x', ord 0x78
120  0b00100111, // 'y', ord 0x79
121  TM1637_UNKNOWN_CHAR, // 'z', ord 0x7A
122  0b00110001, // '{', ord 0x7B
123  0b00000110, // '|', ord 0x7C
124  0b00000111, // '}', ord 0x7D
125  0b01100011, // '~', ord 0x7E (degree symbol)
126 };
128  ESP_LOGCONFIG(TAG, "Setting up TM1637...");
129 
130  this->clk_pin_->setup(); // OUTPUT
131  this->clk_pin_->digital_write(false); // LOW
132  this->dio_pin_->setup(); // OUTPUT
133  this->dio_pin_->digital_write(false); // LOW
134 
135  this->display();
136 }
138  ESP_LOGCONFIG(TAG, "TM1637:");
139  ESP_LOGCONFIG(TAG, " Intensity: %d", this->intensity_);
140  ESP_LOGCONFIG(TAG, " Inverted: %d", this->inverted_);
141  ESP_LOGCONFIG(TAG, " Length: %d", this->length_);
142  LOG_PIN(" CLK Pin: ", this->clk_pin_);
143  LOG_PIN(" DIO Pin: ", this->dio_pin_);
144  LOG_UPDATE_INTERVAL(this);
145 }
146 
147 #ifdef USE_BINARY_SENSOR
149  uint8_t val = this->get_keys();
150  for (auto *tm1637_key : this->tm1637_keys_)
151  tm1637_key->process(val);
152 }
153 
155  this->start_();
156  this->send_byte_(TM1637_CMD_DATA | TM1637_DATA_READ_KEYS);
157  this->start_();
158  uint8_t key_code = read_byte_();
159  this->stop_();
160  if (key_code != 0xFF) {
161  // Invert key_code:
162  // Bit | 7 6 5 4 3 2 1 0
163  // ------+-------------------------
164  // From | S0 S1 S2 K1 K2 1 1 1
165  // To | S0 S1 S2 K1 K2 0 0 0
166  key_code = ~key_code;
167  // Shift bits to:
168  // Bit | 7 6 5 4 3 2 1 0
169  // ------+------------------------
170  // To | 0 0 0 0 K2 S2 S1 S0
171  key_code = (uint8_t) ((key_code & 0x80) >> 7 | (key_code & 0x40) >> 5 | (key_code & 0x20) >> 3 | (key_code & 0x08));
172  }
173  return key_code;
174 }
175 #endif
176 
178  for (uint8_t &i : this->buffer_)
179  i = 0;
180  if (this->writer_.has_value())
181  (*this->writer_)(*this);
182  this->display();
183 }
184 
189  this->bit_delay_();
190 }
191 
194  bit_delay_();
196  bit_delay_();
198  bit_delay_();
199 }
200 
202  ESP_LOGVV(TAG, "Display %02X%02X%02X%02X", buffer_[0], buffer_[1], buffer_[2], buffer_[3]);
203 
204  // Write DATA CMND
205  this->start_();
206  this->send_byte_(TM1637_CMD_DATA);
207  this->stop_();
208 
209  // Write ADDR CMD + first digit address
210  this->start_();
211  this->send_byte_(TM1637_CMD_ADDR);
212 
213  // Write the data bytes
214  if (this->inverted_) {
215  for (int8_t i = this->length_ - 1; i >= 0; i--) {
216  this->send_byte_(this->buffer_[i]);
217  }
218  } else {
219  for (auto b : this->buffer_) {
220  this->send_byte_(b);
221  }
222  }
223 
224  this->stop_();
225 
226  // Write display CTRL CMND + brightness
227  this->start_();
228  this->send_byte_(TM1637_CMD_CTRL + ((this->intensity_ & 0x7) | (this->on_ ? 0x08 : 0x00)));
229  this->stop_();
230 }
231 bool TM1637Display::send_byte_(uint8_t b) {
232  uint8_t data = b;
233  for (uint8_t i = 0; i < 8; i++) {
234  // CLK low
236  this->bit_delay_();
237  // Set data bit
238  if (data & 0x01) {
240  } else {
242  }
243 
244  this->bit_delay_();
245  // CLK high
247  this->bit_delay_();
248  data = data >> 1;
249  }
250  // Wait for acknowledge
251  // CLK to zero
254  this->bit_delay_();
255  // CLK to high
257  this->bit_delay_();
258  uint8_t ack = this->dio_pin_->digital_read();
259  if (ack == 0) {
261  }
262 
263  this->bit_delay_();
265  this->bit_delay_();
266 
267  return ack;
268 }
269 
271  uint8_t retval = 0;
272  // Prepare DIO to read data
274  this->bit_delay_();
275  // Data is shifted out by the TM1637 on the CLK falling edge
276  for (uint8_t bit = 0; bit < 8; bit++) {
278  this->bit_delay_();
279  // Read next bit
280  retval <<= 1;
281  if (this->dio_pin_->digital_read()) {
282  retval |= 0x01;
283  }
285  this->bit_delay_();
286  }
287  // Return DIO to output mode
288  // Dummy ACK
290  this->bit_delay_();
292  this->bit_delay_();
294  this->bit_delay_();
296  this->bit_delay_();
297  return retval;
298 }
299 
300 uint8_t TM1637Display::print(uint8_t start_pos, const char *str) {
301  // ESP_LOGV(TAG, "Print at %d: %s", start_pos, str);
302  uint8_t pos = start_pos;
303  bool use_dot = false;
304  for (; *str != '\0'; str++) {
305  uint8_t data = TM1637_UNKNOWN_CHAR;
306  if (*str >= ' ' && *str <= '~')
307  data = progmem_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']);
308 
309  if (data == TM1637_UNKNOWN_CHAR) {
310  ESP_LOGW(TAG, "Encountered character '%c' with no TM1637 representation while translating string!", *str);
311  }
312  // Remap segments, for compatibility with MAX7219 segment definition which is
313  // XABCDEFG, but TM1637 is // XGFEDCBA
314  if (this->inverted_) {
315  // XABCDEFG > XGCBAFED
316  data = ((data & 0x80) || use_dot ? 0x80 : 0) | // no move X
317  ((data & 0x40) ? 0x8 : 0) | // A
318  ((data & 0x20) ? 0x10 : 0) | // B
319  ((data & 0x10) ? 0x20 : 0) | // C
320  ((data & 0x8) ? 0x1 : 0) | // D
321  ((data & 0x4) ? 0x2 : 0) | // E
322  ((data & 0x2) ? 0x4 : 0) | // F
323  ((data & 0x1) ? 0x40 : 0); // G
324  } else {
325  // XABCDEFG > XGFEDCBA
326  data = ((data & 0x80) ? 0x80 : 0) | // no move X
327  ((data & 0x40) ? 0x1 : 0) | // A
328  ((data & 0x20) ? 0x2 : 0) | // B
329  ((data & 0x10) ? 0x4 : 0) | // C
330  ((data & 0x8) ? 0x8 : 0) | // D
331  ((data & 0x4) ? 0x10 : 0) | // E
332  ((data & 0x2) ? 0x20 : 0) | // F
333  ((data & 0x1) ? 0x40 : 0); // G
334  }
335  use_dot = *str == '.';
336  if (use_dot) {
337  if ((!this->inverted_) && (pos != start_pos)) {
338  this->buffer_[pos - 1] |= 0b10000000;
339  }
340  } else {
341  if (pos >= 6) {
342  ESP_LOGE(TAG, "String is too long for the display!");
343  break;
344  }
345  this->buffer_[pos++] = data;
346  }
347  }
348  return pos - start_pos;
349 }
350 uint8_t TM1637Display::print(const char *str) { return this->print(0, str); }
351 uint8_t TM1637Display::printf(uint8_t pos, const char *format, ...) {
352  va_list arg;
353  va_start(arg, format);
354  char buffer[64];
355  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
356  va_end(arg);
357  if (ret > 0)
358  return this->print(pos, buffer);
359  return 0;
360 }
361 uint8_t TM1637Display::printf(const char *format, ...) {
362  va_list arg;
363  va_start(arg, format);
364  char buffer[64];
365  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
366  va_end(arg);
367  if (ret > 0)
368  return this->print(buffer);
369  return 0;
370 }
371 
372 uint8_t TM1637Display::strftime(uint8_t pos, const char *format, ESPTime time) {
373  char buffer[64];
374  size_t ret = time.strftime(buffer, sizeof(buffer), format);
375  if (ret > 0)
376  return this->print(pos, buffer);
377  return 0;
378 }
379 uint8_t TM1637Display::strftime(const char *format, ESPTime time) { return this->strftime(0, format, time); }
380 
381 } // namespace tm1637
382 } // namespace esphome
virtual void digital_write(bool value)=0
bool send_byte_(uint8_t b)
Definition: tm1637.cpp:231
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: tm1637.cpp:372
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:18
float get_setup_priority() const override
Definition: tm1637.cpp:185
A more user-friendly version of struct tm from time.h.
Definition: time.h:17
optional< tm1637_writer_t > writer_
Definition: tm1637.h:81
virtual void pin_mode(gpio::Flags flags)=0
mopeka_std_values val[4]
bool has_value() const
Definition: optional.h:87
const uint8_t TM1637_DATA_FIXED_ADDR
Fixed address.
Definition: tm1637.cpp:19
virtual void setup()=0
const uint8_t TM1637_ASCII_TO_RAW [] PROGMEM
Definition: tm1637.cpp:30
const uint8_t TM1637_UNKNOWN_CHAR
Definition: tm1637.cpp:13
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: tm1637.cpp:351
const float PROCESSOR
For components that use data from sensors like displays.
Definition: component.cpp:20
const uint8_t TM1637_CMD_ADDR
Display address command.
Definition: tm1637.cpp:12
virtual bool digital_read()=0
const uint8_t TM1637_DATA_WRITE
Write data.
Definition: tm1637.cpp:16
uint8_t progmem_read_byte(const uint8_t *addr)
Definition: core.cpp:55
const uint8_t TM1637_CMD_DATA
Display data command.
Definition: tm1637.cpp:10
uint8_t uint8_t uint8_t print(uint8_t pos, const char *str)
Print str at the given position.
Definition: tm1637.cpp:300
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void dump_config() override
Definition: tm1637.cpp:137
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
std::vector< TM1637Key * > tm1637_keys_
Definition: tm1637.h:84
const uint8_t TM1637_DATA_READ_KEYS
Read keys.
Definition: tm1637.cpp:17
const uint8_t TM1637_DATA_AUTO_INC_ADDR
Auto increment address.
Definition: tm1637.cpp:18
const uint8_t TM1637_CMD_CTRL
Display control command.
Definition: tm1637.cpp:11