2020-01-13 23:44:55 +01:00
|
|
|
#include "tm1637.h"
|
|
|
|
#include "esphome/core/log.h"
|
|
|
|
#include "esphome/core/helpers.h"
|
2021-09-20 11:47:51 +02:00
|
|
|
#include "esphome/core/hal.h"
|
2020-01-13 23:44:55 +01:00
|
|
|
|
|
|
|
namespace esphome {
|
|
|
|
namespace tm1637 {
|
|
|
|
|
2021-06-10 22:19:44 +02:00
|
|
|
static const char *const TAG = "display.tm1637";
|
2020-01-13 23:44:55 +01:00
|
|
|
const uint8_t TM1637_I2C_COMM1 = 0x40;
|
|
|
|
const uint8_t TM1637_I2C_COMM2 = 0xC0;
|
|
|
|
const uint8_t TM1637_I2C_COMM3 = 0x80;
|
|
|
|
const uint8_t TM1637_UNKNOWN_CHAR = 0b11111111;
|
|
|
|
|
|
|
|
//
|
|
|
|
// A
|
|
|
|
// ---
|
|
|
|
// F | | B
|
|
|
|
// -G-
|
|
|
|
// E | | C
|
|
|
|
// ---
|
|
|
|
// D X
|
|
|
|
// XABCDEFG
|
2020-05-19 01:10:03 +02:00
|
|
|
const uint8_t TM1637_ASCII_TO_RAW[] PROGMEM = {
|
2020-01-13 23:44:55 +01:00
|
|
|
0b00000000, // ' ', ord 0x20
|
|
|
|
0b10110000, // '!', ord 0x21
|
|
|
|
0b00100010, // '"', ord 0x22
|
|
|
|
TM1637_UNKNOWN_CHAR, // '#', ord 0x23
|
|
|
|
TM1637_UNKNOWN_CHAR, // '$', ord 0x24
|
|
|
|
0b01001001, // '%', ord 0x25
|
|
|
|
TM1637_UNKNOWN_CHAR, // '&', ord 0x26
|
|
|
|
0b00000010, // ''', ord 0x27
|
|
|
|
0b01001110, // '(', ord 0x28
|
|
|
|
0b01111000, // ')', ord 0x29
|
|
|
|
0b01000000, // '*', ord 0x2A
|
|
|
|
TM1637_UNKNOWN_CHAR, // '+', ord 0x2B
|
|
|
|
0b00010000, // ',', ord 0x2C
|
|
|
|
0b00000001, // '-', ord 0x2D
|
|
|
|
0b10000000, // '.', ord 0x2E
|
|
|
|
TM1637_UNKNOWN_CHAR, // '/', ord 0x2F
|
|
|
|
0b01111110, // '0', ord 0x30
|
|
|
|
0b00110000, // '1', ord 0x31
|
|
|
|
0b01101101, // '2', ord 0x32
|
|
|
|
0b01111001, // '3', ord 0x33
|
|
|
|
0b00110011, // '4', ord 0x34
|
|
|
|
0b01011011, // '5', ord 0x35
|
|
|
|
0b01011111, // '6', ord 0x36
|
|
|
|
0b01110000, // '7', ord 0x37
|
|
|
|
0b01111111, // '8', ord 0x38
|
2021-05-31 01:54:26 +02:00
|
|
|
0b01111011, // '9', ord 0x39
|
2020-01-13 23:44:55 +01:00
|
|
|
0b01001000, // ':', ord 0x3A
|
|
|
|
0b01011000, // ';', ord 0x3B
|
|
|
|
TM1637_UNKNOWN_CHAR, // '<', ord 0x3C
|
|
|
|
TM1637_UNKNOWN_CHAR, // '=', ord 0x3D
|
|
|
|
TM1637_UNKNOWN_CHAR, // '>', ord 0x3E
|
|
|
|
0b01100101, // '?', ord 0x3F
|
|
|
|
0b01101111, // '@', ord 0x40
|
|
|
|
0b01110111, // 'A', ord 0x41
|
|
|
|
0b00011111, // 'B', ord 0x42
|
|
|
|
0b01001110, // 'C', ord 0x43
|
|
|
|
0b00111101, // 'D', ord 0x44
|
|
|
|
0b01001111, // 'E', ord 0x45
|
|
|
|
0b01000111, // 'F', ord 0x46
|
|
|
|
0b01011110, // 'G', ord 0x47
|
|
|
|
0b00110111, // 'H', ord 0x48
|
|
|
|
0b00110000, // 'I', ord 0x49
|
|
|
|
0b00111100, // 'J', ord 0x4A
|
|
|
|
TM1637_UNKNOWN_CHAR, // 'K', ord 0x4B
|
|
|
|
0b00001110, // 'L', ord 0x4C
|
|
|
|
TM1637_UNKNOWN_CHAR, // 'M', ord 0x4D
|
|
|
|
0b00010101, // 'N', ord 0x4E
|
|
|
|
0b01111110, // 'O', ord 0x4F
|
|
|
|
0b01100111, // 'P', ord 0x50
|
|
|
|
0b11111110, // 'Q', ord 0x51
|
|
|
|
0b00000101, // 'R', ord 0x52
|
|
|
|
0b01011011, // 'S', ord 0x53
|
|
|
|
0b00000111, // 'T', ord 0x54
|
|
|
|
0b00111110, // 'U', ord 0x55
|
|
|
|
0b00111110, // 'V', ord 0x56
|
|
|
|
0b00111111, // 'W', ord 0x57
|
|
|
|
TM1637_UNKNOWN_CHAR, // 'X', ord 0x58
|
|
|
|
0b00100111, // 'Y', ord 0x59
|
|
|
|
0b01101101, // 'Z', ord 0x5A
|
|
|
|
0b01001110, // '[', ord 0x5B
|
|
|
|
TM1637_UNKNOWN_CHAR, // '\', ord 0x5C
|
|
|
|
0b01111000, // ']', ord 0x5D
|
|
|
|
TM1637_UNKNOWN_CHAR, // '^', ord 0x5E
|
|
|
|
0b00001000, // '_', ord 0x5F
|
|
|
|
0b00100000, // '`', ord 0x60
|
|
|
|
0b01110111, // 'a', ord 0x61
|
|
|
|
0b00011111, // 'b', ord 0x62
|
|
|
|
0b00001101, // 'c', ord 0x63
|
|
|
|
0b00111101, // 'd', ord 0x64
|
|
|
|
0b01001111, // 'e', ord 0x65
|
|
|
|
0b01000111, // 'f', ord 0x66
|
|
|
|
0b01011110, // 'g', ord 0x67
|
|
|
|
0b00010111, // 'h', ord 0x68
|
|
|
|
0b00010000, // 'i', ord 0x69
|
|
|
|
0b00111100, // 'j', ord 0x6A
|
|
|
|
TM1637_UNKNOWN_CHAR, // 'k', ord 0x6B
|
|
|
|
0b00001110, // 'l', ord 0x6C
|
|
|
|
TM1637_UNKNOWN_CHAR, // 'm', ord 0x6D
|
|
|
|
0b00010101, // 'n', ord 0x6E
|
|
|
|
0b00011101, // 'o', ord 0x6F
|
|
|
|
0b01100111, // 'p', ord 0x70
|
|
|
|
TM1637_UNKNOWN_CHAR, // 'q', ord 0x71
|
|
|
|
0b00000101, // 'r', ord 0x72
|
|
|
|
0b01011011, // 's', ord 0x73
|
|
|
|
0b00000111, // 't', ord 0x74
|
|
|
|
0b00011100, // 'u', ord 0x75
|
|
|
|
0b00011100, // 'v', ord 0x76
|
|
|
|
TM1637_UNKNOWN_CHAR, // 'w', ord 0x77
|
|
|
|
TM1637_UNKNOWN_CHAR, // 'x', ord 0x78
|
|
|
|
0b00100111, // 'y', ord 0x79
|
|
|
|
TM1637_UNKNOWN_CHAR, // 'z', ord 0x7A
|
|
|
|
0b00110001, // '{', ord 0x7B
|
|
|
|
0b00000110, // '|', ord 0x7C
|
|
|
|
0b00000111, // '}', ord 0x7D
|
2020-05-19 01:10:03 +02:00
|
|
|
0b01100011, // '~', ord 0x7E (degree symbol)
|
2020-01-13 23:44:55 +01:00
|
|
|
};
|
|
|
|
void TM1637Display::setup() {
|
|
|
|
ESP_LOGCONFIG(TAG, "Setting up TM1637...");
|
|
|
|
|
|
|
|
this->clk_pin_->setup(); // OUTPUT
|
|
|
|
this->clk_pin_->digital_write(false); // LOW
|
|
|
|
this->dio_pin_->setup(); // OUTPUT
|
|
|
|
this->dio_pin_->digital_write(false); // LOW
|
|
|
|
|
|
|
|
this->display();
|
|
|
|
}
|
|
|
|
void TM1637Display::dump_config() {
|
|
|
|
ESP_LOGCONFIG(TAG, "TM1637:");
|
2021-12-20 09:30:35 +01:00
|
|
|
ESP_LOGCONFIG(TAG, " Intensity: %d", this->intensity_);
|
|
|
|
ESP_LOGCONFIG(TAG, " Inverted: %d", this->inverted_);
|
|
|
|
ESP_LOGCONFIG(TAG, " Length: %d", this->length_);
|
2020-01-13 23:44:55 +01:00
|
|
|
LOG_PIN(" CLK Pin: ", this->clk_pin_);
|
|
|
|
LOG_PIN(" DIO Pin: ", this->dio_pin_);
|
|
|
|
LOG_UPDATE_INTERVAL(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TM1637Display::update() {
|
2021-06-10 12:55:20 +02:00
|
|
|
for (uint8_t &i : this->buffer_)
|
2020-01-13 23:44:55 +01:00
|
|
|
i = 0;
|
|
|
|
if (this->writer_.has_value())
|
|
|
|
(*this->writer_)(*this);
|
|
|
|
this->display();
|
|
|
|
}
|
|
|
|
|
|
|
|
float TM1637Display::get_setup_priority() const { return setup_priority::PROCESSOR; }
|
|
|
|
void TM1637Display::bit_delay_() { delayMicroseconds(100); }
|
|
|
|
void TM1637Display::start_() {
|
2021-09-20 11:47:51 +02:00
|
|
|
this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
this->bit_delay_();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TM1637Display::stop_() {
|
2021-09-20 11:47:51 +02:00
|
|
|
this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
bit_delay_();
|
2021-09-20 11:47:51 +02:00
|
|
|
this->clk_pin_->pin_mode(gpio::FLAG_INPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
bit_delay_();
|
2021-09-20 11:47:51 +02:00
|
|
|
this->dio_pin_->pin_mode(gpio::FLAG_INPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
bit_delay_();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TM1637Display::display() {
|
|
|
|
ESP_LOGVV(TAG, "Display %02X%02X%02X%02X", buffer_[0], buffer_[1], buffer_[2], buffer_[3]);
|
|
|
|
|
|
|
|
// Write COMM1
|
|
|
|
this->start_();
|
|
|
|
this->send_byte_(TM1637_I2C_COMM1);
|
|
|
|
this->stop_();
|
|
|
|
|
|
|
|
// Write COMM2 + first digit address
|
|
|
|
this->start_();
|
|
|
|
this->send_byte_(TM1637_I2C_COMM2);
|
|
|
|
|
|
|
|
// Write the data bytes
|
2021-12-20 09:30:35 +01:00
|
|
|
if (this->inverted_) {
|
2021-12-20 18:06:04 +01:00
|
|
|
for (int8_t i = this->length_ - 1; i >= 0; i--) {
|
2021-12-20 09:30:35 +01:00
|
|
|
this->send_byte_(this->buffer_[i]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (auto b : this->buffer_) {
|
|
|
|
this->send_byte_(b);
|
|
|
|
}
|
2020-01-13 23:44:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
this->stop_();
|
|
|
|
|
|
|
|
// Write COMM3 + brightness
|
|
|
|
this->start_();
|
|
|
|
this->send_byte_(TM1637_I2C_COMM3 + ((this->intensity_ & 0x7) | 0x08));
|
|
|
|
this->stop_();
|
|
|
|
}
|
|
|
|
bool TM1637Display::send_byte_(uint8_t b) {
|
|
|
|
uint8_t data = b;
|
|
|
|
|
|
|
|
// 8 Data Bits
|
|
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
|
|
// CLK low
|
2021-09-20 11:47:51 +02:00
|
|
|
this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
this->bit_delay_();
|
|
|
|
|
|
|
|
// Set data bit
|
|
|
|
if (data & 0x01)
|
2021-09-20 11:47:51 +02:00
|
|
|
this->dio_pin_->pin_mode(gpio::FLAG_INPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
else
|
2021-09-20 11:47:51 +02:00
|
|
|
this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
|
|
|
|
this->bit_delay_();
|
|
|
|
|
|
|
|
// CLK high
|
2021-09-20 11:47:51 +02:00
|
|
|
this->clk_pin_->pin_mode(gpio::FLAG_INPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
this->bit_delay_();
|
|
|
|
data = data >> 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for acknowledge
|
|
|
|
// CLK to zero
|
2021-09-20 11:47:51 +02:00
|
|
|
this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
|
|
|
this->dio_pin_->pin_mode(gpio::FLAG_INPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
this->bit_delay_();
|
|
|
|
|
|
|
|
// CLK to high
|
2021-09-20 11:47:51 +02:00
|
|
|
this->clk_pin_->pin_mode(gpio::FLAG_INPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
this->bit_delay_();
|
|
|
|
uint8_t ack = this->dio_pin_->digital_read();
|
|
|
|
if (ack == 0) {
|
2021-09-20 11:47:51 +02:00
|
|
|
this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
this->bit_delay_();
|
2021-09-20 11:47:51 +02:00
|
|
|
this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
2020-01-13 23:44:55 +01:00
|
|
|
this->bit_delay_();
|
|
|
|
|
|
|
|
return ack;
|
|
|
|
}
|
|
|
|
|
2021-06-10 12:55:20 +02:00
|
|
|
uint8_t TM1637Display::print(uint8_t start_pos, const char *str) {
|
2020-01-13 23:44:55 +01:00
|
|
|
ESP_LOGV(TAG, "Print at %d: %s", start_pos, str);
|
|
|
|
uint8_t pos = start_pos;
|
|
|
|
for (; *str != '\0'; str++) {
|
|
|
|
uint8_t data = TM1637_UNKNOWN_CHAR;
|
2020-08-14 04:21:19 +02:00
|
|
|
if (*str >= ' ' && *str <= '~')
|
2021-09-20 11:47:51 +02:00
|
|
|
data = progmem_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']);
|
2020-01-13 23:44:55 +01:00
|
|
|
|
|
|
|
if (data == TM1637_UNKNOWN_CHAR) {
|
|
|
|
ESP_LOGW(TAG, "Encountered character '%c' with no TM1637 representation while translating string!", *str);
|
|
|
|
}
|
|
|
|
// Remap segments, for compatibility with MAX7219 segment definition which is
|
|
|
|
// XABCDEFG, but TM1637 is // XGFEDCBA
|
2021-12-20 09:30:35 +01:00
|
|
|
if (this->inverted_) {
|
|
|
|
// XABCDEFG > XGCBAFED
|
|
|
|
data = ((data & 0x80) ? 0x80 : 0) | // no move X
|
|
|
|
((data & 0x40) ? 0x8 : 0) | // A
|
|
|
|
((data & 0x20) ? 0x10 : 0) | // B
|
|
|
|
((data & 0x10) ? 0x20 : 0) | // C
|
|
|
|
((data & 0x8) ? 0x1 : 0) | // D
|
|
|
|
((data & 0x4) ? 0x2 : 0) | // E
|
|
|
|
((data & 0x2) ? 0x4 : 0) | // F
|
|
|
|
((data & 0x1) ? 0x40 : 0); // G
|
|
|
|
} else {
|
|
|
|
// XABCDEFG > XGFEDCBA
|
|
|
|
data = ((data & 0x80) ? 0x80 : 0) | // no move X
|
|
|
|
((data & 0x40) ? 0x1 : 0) | // A
|
|
|
|
((data & 0x20) ? 0x2 : 0) | // B
|
|
|
|
((data & 0x10) ? 0x4 : 0) | // C
|
|
|
|
((data & 0x8) ? 0x8 : 0) | // D
|
|
|
|
((data & 0x4) ? 0x10 : 0) | // E
|
|
|
|
((data & 0x2) ? 0x20 : 0) | // F
|
|
|
|
((data & 0x1) ? 0x40 : 0); // G
|
|
|
|
}
|
2020-01-13 23:44:55 +01:00
|
|
|
if (*str == '.') {
|
|
|
|
if (pos != start_pos)
|
|
|
|
pos--;
|
|
|
|
this->buffer_[pos] |= 0b10000000;
|
|
|
|
} else {
|
2021-05-22 01:04:25 +02:00
|
|
|
if (pos >= 6) {
|
2020-01-13 23:44:55 +01:00
|
|
|
ESP_LOGE(TAG, "String is too long for the display!");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
this->buffer_[pos] = data;
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
return pos - start_pos;
|
|
|
|
}
|
2021-06-10 12:55:20 +02:00
|
|
|
uint8_t TM1637Display::print(const char *str) { return this->print(0, str); }
|
|
|
|
uint8_t TM1637Display::printf(uint8_t pos, const char *format, ...) {
|
2020-01-13 23:44:55 +01:00
|
|
|
va_list arg;
|
|
|
|
va_start(arg, format);
|
|
|
|
char buffer[64];
|
|
|
|
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
|
|
|
va_end(arg);
|
|
|
|
if (ret > 0)
|
|
|
|
return this->print(pos, buffer);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-06-10 12:55:20 +02:00
|
|
|
uint8_t TM1637Display::printf(const char *format, ...) {
|
2020-01-13 23:44:55 +01:00
|
|
|
va_list arg;
|
|
|
|
va_start(arg, format);
|
|
|
|
char buffer[64];
|
|
|
|
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
|
|
|
|
va_end(arg);
|
|
|
|
if (ret > 0)
|
|
|
|
return this->print(buffer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_TIME
|
2021-06-10 12:55:20 +02:00
|
|
|
uint8_t TM1637Display::strftime(uint8_t pos, const char *format, time::ESPTime time) {
|
2020-01-13 23:44:55 +01:00
|
|
|
char buffer[64];
|
|
|
|
size_t ret = time.strftime(buffer, sizeof(buffer), format);
|
|
|
|
if (ret > 0)
|
|
|
|
return this->print(pos, buffer);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-06-10 12:55:20 +02:00
|
|
|
uint8_t TM1637Display::strftime(const char *format, time::ESPTime time) { return this->strftime(0, format, time); }
|
2020-01-13 23:44:55 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
} // namespace tm1637
|
|
|
|
} // namespace esphome
|