ESPHome  2024.11.1
tm1621.cpp
Go to the documentation of this file.
1 #include "tm1621.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 tm1621 {
8 
9 static const char *const TAG = "tm1621";
10 
11 const uint8_t TM1621_PULSE_WIDTH = 10; // microseconds (Sonoff = 100)
12 
13 const uint8_t TM1621_SYS_EN = 0x01; // 0b00000001
14 const uint8_t TM1621_LCD_ON = 0x03; // 0b00000011
15 const uint8_t TM1621_TIMER_DIS = 0x04; // 0b00000100
16 const uint8_t TM1621_WDT_DIS = 0x05; // 0b00000101
17 const uint8_t TM1621_TONE_OFF = 0x08; // 0b00001000
18 const uint8_t TM1621_BIAS = 0x29; // 0b00101001 = LCD 1/3 bias 4 commons option
19 const uint8_t TM1621_IRQ_DIS = 0x80; // 0b100x0xxx
20 
22 
24  TM1621_WDT_DIS, TM1621_TONE_OFF, TM1621_IRQ_DIS};
25 
26 const char TM1621_KCHAR[] PROGMEM = {"0|1|2|3|4|5|6|7|8|9|-| "};
27 // 0 1 2 3 4 5 6 7 8 9 - off
28 const uint8_t TM1621_DIGIT_ROW[2][12] = {{0x5F, 0x50, 0x3D, 0x79, 0x72, 0x6B, 0x6F, 0x51, 0x7F, 0x7B, 0x20, 0x00},
29  {0xF5, 0x05, 0xB6, 0x97, 0x47, 0xD3, 0xF3, 0x85, 0xF7, 0xD7, 0x02, 0x00}};
30 
32  ESP_LOGCONFIG(TAG, "Setting up TM1621...");
33 
34  this->cs_pin_->setup(); // OUTPUT
35  this->cs_pin_->digital_write(true);
36  this->data_pin_->setup(); // OUTPUT
37  this->data_pin_->digital_write(true);
38  this->read_pin_->setup(); // OUTPUT
39  this->read_pin_->digital_write(true);
40  this->write_pin_->setup(); // OUTPUT
41  this->write_pin_->digital_write(true);
42 
43  this->state_ = 100;
44 
45  this->cs_pin_->digital_write(false);
47  this->read_pin_->digital_write(false);
49  this->write_pin_->digital_write(false);
51  this->data_pin_->digital_write(false);
52  delayMicroseconds(TM1621_PULSE_WIDTH);
53  this->data_pin_->digital_write(true);
54 
55  for (uint8_t tm1621_command : TM1621_COMMANDS) {
56  this->send_command_(tm1621_command);
57  }
58 
59  this->send_address_(0x00);
60  for (uint32_t segment = 0; segment < 16; segment++) {
61  this->send_common_(0);
62  }
63  this->stop_();
64 
65  snprintf(this->row_[0], sizeof(this->row_[0]), "----");
66  snprintf(this->row_[1], sizeof(this->row_[1]), "----");
67 
68  this->display();
69 }
71  ESP_LOGCONFIG(TAG, "TM1621:");
72  LOG_PIN(" CS Pin: ", this->cs_pin_);
73  LOG_PIN(" DATA Pin: ", this->data_pin_);
74  LOG_PIN(" READ Pin: ", this->read_pin_);
75  LOG_PIN(" WRITE Pin: ", this->write_pin_);
76  LOG_UPDATE_INTERVAL(this);
77 }
78 
80  // memset(this->row, 0, sizeof(this->row));
81  if (this->writer_.has_value())
82  (*this->writer_)(*this);
83  this->display();
84 }
85 
88 
90  this->cs_pin_->digital_write(true); // Stop command sequence
91  delayMicroseconds(TM1621_PULSE_WIDTH / 2);
92  this->data_pin_->digital_write(true); // Reset data
93 }
94 
96  // Tm1621.row[x] = "text", "----", " " or a number with one decimal like "0.4", "237.5", "123456.7"
97  // "123456.7" will be shown as "9999" being a four digit overflow
98 
99  // AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Row1 '%s', Row2 '%s'"), Tm1621.row[0], Tm1621.row[1]);
100 
101  uint8_t buffer[8] = {0}; // TM1621 16-segment 4-bit common buffer
102  char row[4];
103  for (uint32_t j = 0; j < 2; j++) {
104  // 0.4V => " 04", 0.0A => " ", 1234.5V => "1234"
105  uint32_t len = strlen(this->row_[j]);
106  char *dp = nullptr; // Expect number larger than "123"
107  int row_idx = len - 3; // "1234.5"
108  if (len <= 5) { // "----", " ", "0.4", "237.5"
109  dp = strchr(this->row_[j], '.');
110  row_idx = len - 1;
111  } else if (len > 6) { // "12345.6"
112  snprintf(this->row_[j], sizeof(this->row_[j]), "9999");
113  row_idx = 3;
114  }
115  row[3] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' ';
116  if ((row_idx >= 0) && dp) {
117  row_idx--;
118  }
119  row[2] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' ';
120  row[1] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' ';
121  row[0] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' ';
122 
123  // AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Dump%d %4_H"), j +1, row);
124 
125  char command[10];
126  char needle[2] = {0};
127  for (uint32_t i = 0; i < 4; i++) {
128  needle[0] = row[i];
129  int index = this->get_command_code_(command, sizeof(command), (const char *) needle, TM1621_KCHAR);
130  if (-1 == index) {
131  index = 11;
132  }
133  uint32_t bidx = (0 == j) ? i : 7 - i;
134  buffer[bidx] = TM1621_DIGIT_ROW[j][index];
135  }
136  if (dp) {
137  if (0 == j) {
138  buffer[2] |= 0x80; // Row 1 decimal point
139  } else {
140  buffer[5] |= 0x08; // Row 2 decimal point
141  }
142  }
143  }
144 
145  if (this->fahrenheit_) {
146  buffer[1] |= 0x80;
147  }
148  if (this->celsius_) {
149  buffer[3] |= 0x80;
150  }
151  if (this->kwh_) {
152  buffer[4] |= 0x08;
153  }
154  if (this->humidity_) {
155  buffer[6] |= 0x08;
156  }
157  if (this->voltage_) {
158  buffer[7] |= 0x08;
159  }
160 
161  // AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Dump3 %8_H"), buffer);
162 
163  this->send_address_(0x10); // Sonoff only uses the upper 16 Segments
164  for (uint8_t i : buffer) {
165  this->send_common_(i);
166  }
167  this->stop_();
168 }
169 
170 bool TM1621Display::send_command_(uint16_t command) {
171  uint16_t full_command = (0x0400 | command) << 5; // 0b100cccccccc00000
172  this->cs_pin_->digital_write(false); // Start command sequence
173  delayMicroseconds(TM1621_PULSE_WIDTH / 2);
174  for (uint32_t i = 0; i < 12; i++) {
175  this->write_pin_->digital_write(false); // Start write sequence
176  if (full_command & 0x8000) {
177  this->data_pin_->digital_write(true); // Set data
178  } else {
179  this->data_pin_->digital_write(false); // Set data
180  }
181  delayMicroseconds(TM1621_PULSE_WIDTH);
182  this->write_pin_->digital_write(true); // Read data
183  delayMicroseconds(TM1621_PULSE_WIDTH);
184  full_command <<= 1;
185  }
186  this->stop_();
187  return true;
188 }
189 
190 bool TM1621Display::send_common_(uint8_t common) {
191  for (uint32_t i = 0; i < 8; i++) {
192  this->write_pin_->digital_write(false); // Start write sequence
193  if (common & 1) {
194  this->data_pin_->digital_write(true); // Set data
195  } else {
196  this->data_pin_->digital_write(false); // Set data
197  }
198  delayMicroseconds(TM1621_PULSE_WIDTH);
199  this->write_pin_->digital_write(true); // Read data
200  delayMicroseconds(TM1621_PULSE_WIDTH);
201  common >>= 1;
202  }
203  return true;
204 }
205 
207  uint16_t full_address = (address | 0x0140) << 7; // 0b101aaaaaa0000000
208  this->cs_pin_->digital_write(false); // Start command sequence
209  delayMicroseconds(TM1621_PULSE_WIDTH / 2);
210  for (uint32_t i = 0; i < 9; i++) {
211  this->write_pin_->digital_write(false); // Start write sequence
212  if (full_address & 0x8000) {
213  this->data_pin_->digital_write(true); // Set data
214  } else {
215  this->data_pin_->digital_write(false); // Set data
216  }
217  delayMicroseconds(TM1621_PULSE_WIDTH);
218  this->write_pin_->digital_write(true); // Read data
219  delayMicroseconds(TM1621_PULSE_WIDTH);
220  full_address <<= 1;
221  }
222  return true;
223 }
224 
225 uint8_t TM1621Display::print(uint8_t start_pos, const char *str) {
226  // ESP_LOGD(TAG, "Print at %d: %s", start_pos, str);
227  return snprintf(this->row_[start_pos], sizeof(this->row_[start_pos]), "%s", str);
228 }
229 uint8_t TM1621Display::print(const char *str) { return this->print(0, str); }
230 uint8_t TM1621Display::printf(uint8_t pos, const char *format, ...) {
231  va_list arg;
232  va_start(arg, format);
233  char buffer[64];
234  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
235  va_end(arg);
236  if (ret > 0)
237  return this->print(pos, buffer);
238  return 0;
239 }
240 uint8_t TM1621Display::printf(const char *format, ...) {
241  va_list arg;
242  va_start(arg, format);
243  char buffer[64];
244  int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
245  va_end(arg);
246  if (ret > 0)
247  return this->print(buffer);
248  return 0;
249 }
250 
251 int TM1621Display::get_command_code_(char *destination, size_t destination_size, const char *needle,
252  const char *haystack) {
253  // Returns -1 of not found
254  // Returns index and command if found
255  int result = -1;
256  const char *read = haystack;
257  char *write = destination;
258 
259  while (true) {
260  result++;
261  size_t size = destination_size - 1;
262  write = destination;
263  char ch = '.';
264  while ((ch != '\0') && (ch != '|')) {
265  ch = *(read++);
266  if (size && (ch != '|')) {
267  *write++ = ch;
268  size--;
269  }
270  }
271  *write = '\0';
272  if (!strcasecmp(needle, destination)) {
273  break;
274  }
275  if (0 == ch) {
276  result = -1;
277  break;
278  }
279  }
280  return result;
281 }
282 } // namespace tm1621
283 } // namespace esphome
const char TM1621_KCHAR [] PROGMEM
Definition: tm1621.cpp:26
virtual void digital_write(bool value)=0
int get_command_code_(char *destination, size_t destination_size, const char *needle, const char *haystack)
Definition: tm1621.cpp:251
const uint8_t TM1621_WDT_DIS
Definition: tm1621.cpp:16
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: tm1621.cpp:230
const uint8_t TM1621_TONE_OFF
Definition: tm1621.cpp:17
optional< tm1621_writer_t > writer_
Definition: tm1621.h:62
bool has_value() const
Definition: optional.h:87
const uint8_t TM1621_SYS_EN
Definition: tm1621.cpp:13
virtual void setup()=0
bool send_common_(uint8_t common)
Definition: tm1621.cpp:190
const uint8_t TM1621_BIAS
Definition: tm1621.cpp:18
bool send_address_(uint16_t address)
Definition: tm1621.cpp:206
const uint8_t TM1621_TIMER_DIS
Definition: tm1621.cpp:15
void dump_config() override
Definition: tm1621.cpp:70
const float PROCESSOR
For components that use data from sensors like displays.
Definition: component.cpp:20
const uint8_t TM1621_IRQ_DIS
Definition: tm1621.cpp:19
const uint8_t TM1621_COMMANDS[]
Definition: tm1621.cpp:23
const uint8_t TM1621_LCD_ON
Definition: tm1621.cpp:14
void update() override
Definition: tm1621.cpp:79
uint8_t uint8_t uint8_t print(uint8_t pos, const char *str)
Print str at the given position.
Definition: tm1621.cpp:225
bool send_command_(uint16_t command)
Definition: tm1621.cpp:170
const uint8_t TM1621_PULSE_WIDTH
Definition: tm1621.cpp:11
std::string size_t len
Definition: helpers.h:293
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
uint8_t address
Definition: bl0906.h:211
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
float get_setup_priority() const override
Definition: tm1621.cpp:86
const uint8_t TM1621_DIGIT_ROW[2][12]
Definition: tm1621.cpp:28