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