ESPHome  2024.4.0
tm1638.cpp
Go to the documentation of this file.
1 #include "tm1638.h"
2 #include "sevenseg.h"
3 #include "esphome/core/log.h"
4 #include "esphome/core/helpers.h"
5 #include "esphome/core/hal.h"
6 
7 namespace esphome {
8 namespace tm1638 {
9 
10 static const char *const TAG = "display.tm1638";
11 static const uint8_t TM1638_REGISTER_FIXEDADDRESS = 0x44;
12 static const uint8_t TM1638_REGISTER_AUTOADDRESS = 0x40;
13 static const uint8_t TM1638_REGISTER_READBUTTONS = 0x42;
14 static const uint8_t TM1638_REGISTER_DISPLAYOFF = 0x80;
15 static const uint8_t TM1638_REGISTER_DISPLAYON = 0x88;
16 static const uint8_t TM1638_REGISTER_7SEG_0 = 0xC0;
17 static const uint8_t TM1638_REGISTER_LED_0 = 0xC1;
18 static const uint8_t TM1638_UNKNOWN_CHAR = 0b11111111;
19 
20 static const uint8_t TM1638_SHIFT_DELAY = 4; // clock pause between commands, default 4ms
21 
23  ESP_LOGD(TAG, "Setting up TM1638...");
24 
25  this->clk_pin_->setup(); // OUTPUT
26  this->dio_pin_->setup(); // OUTPUT
27  this->stb_pin_->setup(); // OUTPUT
28 
32 
33  this->clk_pin_->digital_write(false);
34  this->dio_pin_->digital_write(false);
35  this->stb_pin_->digital_write(false);
36 
38 
39  this->reset_(); // all LEDs off
40 
41  for (uint8_t i = 0; i < 8; i++) // zero fill print buffer
42  this->buffer_[i] = 0;
43 }
44 
46  ESP_LOGCONFIG(TAG, "TM1638:");
47  ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_);
48  LOG_PIN(" CLK Pin: ", this->clk_pin_);
49  LOG_PIN(" DIO Pin: ", this->dio_pin_);
50  LOG_PIN(" STB Pin: ", this->stb_pin_);
51  LOG_UPDATE_INTERVAL(this);
52 }
53 
55  if (this->listeners_.empty())
56  return;
57 
58  uint8_t keys = this->get_keys();
59  for (auto &listener : this->listeners_)
60  listener->keys_update(keys);
61 }
62 
64  uint8_t buttons = 0;
65 
66  this->stb_pin_->digital_write(false);
67 
68  this->shift_out_(TM1638_REGISTER_READBUTTONS);
69 
71 
73 
74  for (uint8_t i = 0; i < 4; i++) { // read the 4 button registers
75  uint8_t v = this->shift_in_();
76  buttons |= v << i; // shift bits to correct slots in the byte
77  }
78 
80 
81  this->stb_pin_->digital_write(true);
82 
83  return buttons;
84 }
85 
86 void TM1638Component::update() { // this is called at the interval specified in the config.yaml
87  if (this->writer_.has_value()) {
88  (*this->writer_)(*this);
89  }
90 
91  this->display();
92 }
93 
95 
97  for (uint8_t i = 0; i < 8; i++) {
98  this->set_7seg_(i, buffer_[i]);
99  }
100 }
101 
103  uint8_t num_commands = 16; // 16 addresses, 8 for 7seg and 8 for LEDs
104  uint8_t commands[num_commands];
105 
106  for (uint8_t i = 0; i < num_commands; i++) {
107  commands[i] = 0;
108  }
109 
110  this->send_command_sequence_(commands, num_commands, TM1638_REGISTER_7SEG_0);
111 }
112 
114 
115 void TM1638Component::set_led(int led_pos, bool led_on_off) {
116  this->send_command_(TM1638_REGISTER_FIXEDADDRESS);
117 
118  uint8_t commands[2];
119 
120  commands[0] = TM1638_REGISTER_LED_0 + (led_pos << 1);
121  commands[1] = led_on_off;
122 
123  this->send_commands_(commands, 2);
124 }
125 
126 void TM1638Component::set_7seg_(int seg_pos, uint8_t seg_bits) {
127  this->send_command_(TM1638_REGISTER_FIXEDADDRESS);
128 
129  uint8_t commands[2] = {};
130 
131  commands[0] = TM1638_REGISTER_7SEG_0 + (seg_pos << 1);
132  commands[1] = seg_bits;
133 
134  this->send_commands_(commands, 2);
135 }
136 
137 void TM1638Component::set_intensity(uint8_t brightness_level) {
138  this->intensity_ = brightness_level;
139 
140  this->send_command_(TM1638_REGISTER_FIXEDADDRESS);
141 
142  if (brightness_level > 0) {
143  this->send_command_((uint8_t) (TM1638_REGISTER_DISPLAYON | intensity_));
144  } else {
145  this->send_command_(TM1638_REGISTER_DISPLAYOFF);
146  }
147 }
148 
150 
151 uint8_t TM1638Component::print(uint8_t start_pos, const char *str) {
152  uint8_t pos = start_pos;
153 
154  bool last_was_dot = false;
155 
156  for (; *str != '\0'; str++) {
157  uint8_t data = TM1638_UNKNOWN_CHAR;
158 
159  if (*str >= ' ' && *str <= '~') {
160  data = progmem_read_byte(&TM1638Translation::SEVEN_SEG[*str - 32]); // subract 32 to account for ASCII offset
161  } else if (data == TM1638_UNKNOWN_CHAR) {
162  ESP_LOGW(TAG, "Encountered character '%c' with no TM1638 representation while translating string!", *str);
163  }
164 
165  if (*str == '.') // handle dots
166  {
167  if (pos != start_pos &&
168  !last_was_dot) // if we are not at the first position, backup by one unless last char was a dot
169  {
170  pos--;
171  }
172  this->buffer_[pos] |= 0b10000000; // turn on the dot on the previous position
173  last_was_dot = true; // set a bit in case the next chracter is also a dot
174  } else // if not a dot, then just write the character to display
175  {
176  if (pos >= 8) {
177  ESP_LOGI(TAG, "TM1638 String is too long for the display!");
178  break;
179  }
180  this->buffer_[pos] = data;
181  last_was_dot = false; // clear dot tracking bit
182  }
183 
184  pos++;
185  }
186  return pos - start_pos;
187 }
188 
190 
191 uint8_t TM1638Component::print(const char *str) { return this->print(0, str); }
192 
193 uint8_t TM1638Component::printf(uint8_t pos, const char *format, ...) {
194  va_list arg;
195  va_start(arg, format);
196  char buffer[64];
197  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
198  va_end(arg);
199  if (ret > 0)
200  return this->print(pos, buffer);
201  return 0;
202 }
203 uint8_t TM1638Component::printf(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(buffer);
211  return 0;
212 }
213 
214 uint8_t TM1638Component::strftime(uint8_t pos, const char *format, ESPTime time) {
215  char buffer[64];
216  size_t ret = time.strftime(buffer, sizeof(buffer), format);
217  if (ret > 0)
218  return this->print(pos, buffer);
219  return 0;
220 }
221 uint8_t TM1638Component::strftime(const char *format, ESPTime time) { return this->strftime(0, format, time); }
222 
224 
225 void TM1638Component::send_command_(uint8_t value) {
227  this->stb_pin_->digital_write(false);
228  this->shift_out_(value);
229  this->stb_pin_->digital_write(true);
230 }
231 
232 void TM1638Component::send_commands_(uint8_t const commands[], uint8_t num_commands) {
233  this->stb_pin_->digital_write(false);
234 
235  for (uint8_t i = 0; i < num_commands; i++) {
236  uint8_t command = commands[i];
237  this->shift_out_(command);
238  }
239  this->stb_pin_->digital_write(true);
240 }
241 
243  this->stb_pin_->digital_write(false);
244  this->shift_out_(value);
245 }
246 
247 void TM1638Component::send_command_sequence_(uint8_t commands[], uint8_t num_commands, uint8_t starting_address) {
248  this->send_command_(TM1638_REGISTER_AUTOADDRESS);
249  this->send_command_leave_open_(starting_address);
250 
251  for (uint8_t i = 0; i < num_commands; i++) {
252  this->shift_out_(commands[i]);
253  }
254 
255  this->stb_pin_->digital_write(true);
256 }
257 
259  uint8_t value = 0;
260 
261  for (int i = 0; i < 8; ++i) {
262  value |= dio_pin_->digital_read() << i;
263  delayMicroseconds(TM1638_SHIFT_DELAY);
264  this->clk_pin_->digital_write(true);
265  delayMicroseconds(TM1638_SHIFT_DELAY);
266  this->clk_pin_->digital_write(false);
267  delayMicroseconds(TM1638_SHIFT_DELAY);
268  }
269  return value;
270 }
271 
273  for (int i = 0; i < 8; i++) {
274  this->dio_pin_->digital_write((val & (1 << i)));
275  delayMicroseconds(TM1638_SHIFT_DELAY);
276 
277  this->clk_pin_->digital_write(true);
278  delayMicroseconds(TM1638_SHIFT_DELAY);
279 
280  this->clk_pin_->digital_write(false);
281  delayMicroseconds(TM1638_SHIFT_DELAY);
282  }
283 }
284 
285 } // namespace tm1638
286 } // namespace esphome
virtual void digital_write(bool value)=0
GPIOPin * clk_pin_
brghtness of the display 0 through 7
Definition: tm1638.h:69
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
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: tm1638.cpp:214
A more user-friendly version of struct tm from time.h.
Definition: time.h:17
void send_command_(uint8_t value)
Definition: tm1638.cpp:225
virtual void pin_mode(gpio::Flags flags)=0
mopeka_std_values val[4]
bool has_value() const
Definition: optional.h:87
virtual void setup()=0
uint8_t uint8_t void set_led(int led_pos, bool led_on_off)
Definition: tm1638.cpp:115
const char *const TAG
Definition: spi.cpp:8
optional< tm1638_writer_t > writer_
Definition: tm1638.h:73
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: tm1638.cpp:193
void send_command_leave_open_(uint8_t value)
Definition: tm1638.cpp:242
const float PROCESSOR
For components that use data from sensors like displays.
Definition: component.cpp:20
void send_command_sequence_(uint8_t commands[], uint8_t num_commands, uint8_t starting_address)
Definition: tm1638.cpp:247
uint8_t uint8_t uint8_t print(uint8_t pos, const char *str)
Print str at the given position.
Definition: tm1638.cpp:151
virtual bool digital_read()=0
void dump_config() override
Definition: tm1638.cpp:45
uint8_t progmem_read_byte(const uint8_t *addr)
Definition: core.cpp:55
std::vector< KeyListener * > listeners_
Definition: tm1638.h:74
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void send_commands_(uint8_t const commands[], uint8_t num_commands)
Definition: tm1638.cpp:232
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
float get_setup_priority() const override
Definition: tm1638.cpp:94
void set_intensity(uint8_t brightness_level)
Definition: tm1638.cpp:137
void shift_out_(uint8_t value)
Definition: tm1638.cpp:272
void set_7seg_(int seg_pos, uint8_t seg_bits)
Definition: tm1638.cpp:126