mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 14:43:51 +00:00 
			
		
		
		
	
				
					committed by
					
						 Brandon Davidson
						Brandon Davidson
					
				
			
			
				
	
			
			
			
						parent
						
							6a60f01753
						
					
				
				
					commit
					990a4d4774
				
			
							
								
								
									
										35
									
								
								esphome/components/tm1637/display.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								esphome/components/tm1637/display.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import display | ||||
| from esphome.const import CONF_CLK_PIN, CONF_DIO_PIN, CONF_ID, CONF_LAMBDA, CONF_INTENSITY | ||||
|  | ||||
| tm1637_ns = cg.esphome_ns.namespace('tm1637') | ||||
| TM1637Display = tm1637_ns.class_('TM1637Display', cg.PollingComponent) | ||||
| TM1637DisplayRef = TM1637Display.operator('ref') | ||||
|  | ||||
| CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ | ||||
|     cv.GenerateID(): cv.declare_id(TM1637Display), | ||||
|  | ||||
|     cv.Optional(CONF_INTENSITY, default=7): cv.All(cv.uint8_t, cv.Range(min=0, max=7)), | ||||
|     cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema, | ||||
|     cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, | ||||
| }).extend(cv.polling_component_schema('1s')) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield cg.register_component(var, config) | ||||
|     yield display.register_display(var, config) | ||||
|  | ||||
|     clk = yield cg.gpio_pin_expression(config[CONF_CLK_PIN]) | ||||
|     cg.add(var.set_clk_pin(clk)) | ||||
|     dio = yield cg.gpio_pin_expression(config[CONF_DIO_PIN]) | ||||
|     cg.add(var.set_dio_pin(dio)) | ||||
|  | ||||
|     cg.add(var.set_intensity(config[CONF_INTENSITY])) | ||||
|  | ||||
|     if CONF_LAMBDA in config: | ||||
|         lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(TM1637DisplayRef, 'it')], | ||||
|                                           return_type=cg.void) | ||||
|         cg.add(var.set_writer(lambda_)) | ||||
							
								
								
									
										299
									
								
								esphome/components/tm1637/tm1637.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								esphome/components/tm1637/tm1637.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,299 @@ | ||||
| #include "tm1637.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace tm1637 { | ||||
|  | ||||
| const char* TAG = "display.tm1637"; | ||||
| 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 | ||||
| const uint8_t TM1637_ASCII_TO_RAW[94] PROGMEM = { | ||||
|     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 | ||||
|     0b01110011,           // '9', ord 0x39 | ||||
|     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 | ||||
| }; | ||||
| 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:"); | ||||
|   ESP_LOGCONFIG(TAG, "  INTENSITY: %d", this->intensity_); | ||||
|   LOG_PIN("  CLK Pin: ", this->clk_pin_); | ||||
|   LOG_PIN("  DIO Pin: ", this->dio_pin_); | ||||
|   LOG_UPDATE_INTERVAL(this); | ||||
| } | ||||
|  | ||||
| void TM1637Display::update() { | ||||
|   for (uint8_t& i : this->buffer_) | ||||
|     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_() { | ||||
|   this->dio_pin_->pin_mode(OUTPUT); | ||||
|   this->bit_delay_(); | ||||
| } | ||||
|  | ||||
| void TM1637Display::stop_() { | ||||
|   this->dio_pin_->pin_mode(OUTPUT); | ||||
|   bit_delay_(); | ||||
|   this->clk_pin_->pin_mode(INPUT); | ||||
|   bit_delay_(); | ||||
|   this->dio_pin_->pin_mode(INPUT); | ||||
|   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 | ||||
|   for (auto b : this->buffer_) { | ||||
|     this->send_byte_(b); | ||||
|   } | ||||
|  | ||||
|   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 | ||||
|     this->clk_pin_->pin_mode(OUTPUT); | ||||
|     this->bit_delay_(); | ||||
|  | ||||
|     // Set data bit | ||||
|     if (data & 0x01) | ||||
|       this->dio_pin_->pin_mode(INPUT); | ||||
|     else | ||||
|       this->dio_pin_->pin_mode(OUTPUT); | ||||
|  | ||||
|     this->bit_delay_(); | ||||
|  | ||||
|     // CLK high | ||||
|     this->clk_pin_->pin_mode(INPUT); | ||||
|     this->bit_delay_(); | ||||
|     data = data >> 1; | ||||
|   } | ||||
|  | ||||
|   // Wait for acknowledge | ||||
|   // CLK to zero | ||||
|   this->clk_pin_->pin_mode(OUTPUT); | ||||
|   this->dio_pin_->pin_mode(INPUT); | ||||
|   this->bit_delay_(); | ||||
|  | ||||
|   // CLK to high | ||||
|   this->clk_pin_->pin_mode(INPUT); | ||||
|   this->bit_delay_(); | ||||
|   uint8_t ack = this->dio_pin_->digital_read(); | ||||
|   if (ack == 0) { | ||||
|     this->dio_pin_->pin_mode(OUTPUT); | ||||
|   } | ||||
|  | ||||
|   this->bit_delay_(); | ||||
|   this->clk_pin_->pin_mode(OUTPUT); | ||||
|   this->bit_delay_(); | ||||
|  | ||||
|   return ack; | ||||
| } | ||||
|  | ||||
| uint8_t TM1637Display::print(uint8_t start_pos, const char* str) { | ||||
|   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; | ||||
|     if (*str >= ' ' && *str <= '}') | ||||
|       data = pgm_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']); | ||||
|  | ||||
|     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 | ||||
|     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 | ||||
|     if (*str == '.') { | ||||
|       if (pos != start_pos) | ||||
|         pos--; | ||||
|       this->buffer_[pos] |= 0b10000000; | ||||
|     } else { | ||||
|       if (pos >= 4) { | ||||
|         ESP_LOGE(TAG, "String is too long for the display!"); | ||||
|         break; | ||||
|       } | ||||
|       this->buffer_[pos] = data; | ||||
|     } | ||||
|     pos++; | ||||
|   } | ||||
|   return pos - start_pos; | ||||
| } | ||||
| uint8_t TM1637Display::print(const char* str) { return this->print(0, str); } | ||||
| uint8_t TM1637Display::printf(uint8_t pos, const char* format, ...) { | ||||
|   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; | ||||
| } | ||||
| uint8_t TM1637Display::printf(const char* format, ...) { | ||||
|   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 | ||||
| uint8_t TM1637Display::strftime(uint8_t pos, const char* format, time::ESPTime time) { | ||||
|   char buffer[64]; | ||||
|   size_t ret = time.strftime(buffer, sizeof(buffer), format); | ||||
|   if (ret > 0) | ||||
|     return this->print(pos, buffer); | ||||
|   return 0; | ||||
| } | ||||
| uint8_t TM1637Display::strftime(const char* format, time::ESPTime time) { return this->strftime(0, format, time); } | ||||
| #endif | ||||
|  | ||||
| }  // namespace tm1637 | ||||
| }  // namespace esphome | ||||
							
								
								
									
										70
									
								
								esphome/components/tm1637/tm1637.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								esphome/components/tm1637/tm1637.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/core/esphal.h" | ||||
|  | ||||
| #ifdef USE_TIME | ||||
| #include "esphome/components/time/real_time_clock.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace tm1637 { | ||||
|  | ||||
| class TM1637Display; | ||||
|  | ||||
| using tm1637_writer_t = std::function<void(TM1637Display &)>; | ||||
|  | ||||
| class TM1637Display : public PollingComponent { | ||||
|  public: | ||||
|   void set_writer(tm1637_writer_t &&writer) { this->writer_ = writer; } | ||||
|  | ||||
|   void setup() override; | ||||
|  | ||||
|   void dump_config() override; | ||||
|  | ||||
|   void set_clk_pin(GPIOPin *pin) { clk_pin_ = pin; } | ||||
|   void set_dio_pin(GPIOPin *pin) { dio_pin_ = pin; } | ||||
|  | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|   void update() override; | ||||
|  | ||||
|   /// Evaluate the printf-format and print the result at the given position. | ||||
|   uint8_t printf(uint8_t pos, const char *format, ...) __attribute__((format(printf, 3, 4))); | ||||
|   /// Evaluate the printf-format and print the result at position 0. | ||||
|   uint8_t printf(const char *format, ...) __attribute__((format(printf, 2, 3))); | ||||
|  | ||||
|   /// Print `str` at the given position. | ||||
|   uint8_t print(uint8_t pos, const char *str); | ||||
|   /// Print `str` at position 0. | ||||
|   uint8_t print(const char *str); | ||||
|  | ||||
|   void set_intensity(uint8_t intensity) { this->intensity_ = intensity; } | ||||
|  | ||||
|   void display(); | ||||
|  | ||||
| #ifdef USE_TIME | ||||
|   /// Evaluate the strftime-format and print the result at the given position. | ||||
|   uint8_t strftime(uint8_t pos, const char *format, time::ESPTime time) __attribute__((format(strftime, 3, 0))); | ||||
|  | ||||
|   /// Evaluate the strftime-format and print the result at position 0. | ||||
|   uint8_t strftime(const char *format, time::ESPTime time) __attribute__((format(strftime, 2, 0))); | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   void bit_delay_(); | ||||
|   void setup_pins_(); | ||||
|   bool send_byte_(uint8_t b); | ||||
|   void start_(); | ||||
|   void stop_(); | ||||
|  | ||||
|   GPIOPin *dio_pin_; | ||||
|   GPIOPin *clk_pin_; | ||||
|   uint8_t intensity_; | ||||
|   optional<tm1637_writer_t> writer_{}; | ||||
|   uint8_t buffer_[4] = {0}; | ||||
| }; | ||||
|  | ||||
| }  // namespace tm1637 | ||||
| }  // namespace esphome | ||||
| @@ -1,7 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins, automation | ||||
| from esphome.const import CONF_ID, CONF_CLK_PIN, CONF_LEVEL, CONF_BRIGHTNESS | ||||
| from esphome.const import CONF_ID, CONF_CLK_PIN, CONF_DIO_PIN, CONF_LEVEL, CONF_BRIGHTNESS | ||||
|  | ||||
| tm1651_ns = cg.esphome_ns.namespace('tm1651') | ||||
| TM1651Display = tm1651_ns.class_('TM1651Display', cg.Component) | ||||
| @@ -15,8 +15,6 @@ TM1651_BRIGHTNESS_OPTIONS = { | ||||
|     3: TM1651Display.TM1651_BRIGHTNESS_HIGH | ||||
| } | ||||
|  | ||||
| CONF_DIO_PIN = 'dio_pin' | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema({ | ||||
|     cv.GenerateID(): cv.declare_id(TM1651Display), | ||||
|     cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema, | ||||
|   | ||||
| @@ -125,6 +125,7 @@ CONF_DELTA = 'delta' | ||||
| CONF_DEVICE = 'device' | ||||
| CONF_DEVICE_CLASS = 'device_class' | ||||
| CONF_DIMENSIONS = 'dimensions' | ||||
| CONF_DIO_PIN = 'dio_pin' | ||||
| CONF_DIR_PIN = 'dir_pin' | ||||
| CONF_DIRECTION = 'direction' | ||||
| CONF_DISCOVERY = 'discovery' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user