mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	SSD1331 display support (#1244)
This commit is contained in:
		| @@ -58,6 +58,8 @@ esphome/components/sensor/* @esphome/core | ||||
| esphome/components/shutdown/* @esphome/core | ||||
| esphome/components/sim800l/* @glmnet | ||||
| esphome/components/spi/* @esphome/core | ||||
| esphome/components/ssd1331_base/* @kbx81 | ||||
| esphome/components/ssd1331_spi/* @kbx81 | ||||
| esphome/components/substitutions/* @esphome/core | ||||
| esphome/components/sun/* @OttoWinter | ||||
| esphome/components/switch/* @esphome/core | ||||
|   | ||||
							
								
								
									
										32
									
								
								esphome/components/ssd1331_base/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								esphome/components/ssd1331_base/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| 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_BRIGHTNESS, CONF_LAMBDA, CONF_RESET_PIN | ||||
| from esphome.core import coroutine | ||||
|  | ||||
| CODEOWNERS = ['@kbx81'] | ||||
|  | ||||
| ssd1331_base_ns = cg.esphome_ns.namespace('ssd1331_base') | ||||
| SSD1331 = ssd1331_base_ns.class_('SSD1331', cg.PollingComponent, display.DisplayBuffer) | ||||
|  | ||||
| SSD1331_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ | ||||
|     cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, | ||||
|     cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, | ||||
| }).extend(cv.polling_component_schema('1s')) | ||||
|  | ||||
|  | ||||
| @coroutine | ||||
| def setup_ssd1331(var, config): | ||||
|     yield cg.register_component(var, config) | ||||
|     yield display.register_display(var, config) | ||||
|  | ||||
|     if CONF_RESET_PIN in config: | ||||
|         reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) | ||||
|         cg.add(var.set_reset_pin(reset)) | ||||
|     if CONF_BRIGHTNESS in config: | ||||
|         cg.add(var.init_brightness(config[CONF_BRIGHTNESS])) | ||||
|     if CONF_LAMBDA in config: | ||||
|         lambda_ = yield cg.process_lambda( | ||||
|             config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) | ||||
|         cg.add(var.set_writer(lambda_)) | ||||
							
								
								
									
										155
									
								
								esphome/components/ssd1331_base/ssd1331_base.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								esphome/components/ssd1331_base/ssd1331_base.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| #include "ssd1331_base.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ssd1331_base { | ||||
|  | ||||
| static const char *TAG = "ssd1331"; | ||||
|  | ||||
| static const uint16_t BLACK = 0; | ||||
| static const uint16_t WHITE = 0xffff; | ||||
| static const uint16_t SSD1331_COLORMASK = 0xffff; | ||||
| static const uint8_t SSD1331_MAX_CONTRASTA = 0x91; | ||||
| static const uint8_t SSD1331_MAX_CONTRASTB = 0x50; | ||||
| static const uint8_t SSD1331_MAX_CONTRASTC = 0x7D; | ||||
| static const uint8_t SSD1331_BYTESPERPIXEL = 2; | ||||
| // SSD1331 Commands | ||||
| static const uint8_t SSD1331_DRAWLINE = 0x21;        // Draw line | ||||
| static const uint8_t SSD1331_DRAWRECT = 0x22;        // Draw rectangle | ||||
| static const uint8_t SSD1331_FILL = 0x26;            // Fill enable/disable | ||||
| static const uint8_t SSD1331_SETCOLUMN = 0x15;       // Set column address | ||||
| static const uint8_t SSD1331_SETROW = 0x75;          // Set row adress | ||||
| static const uint8_t SSD1331_CONTRASTA = 0x81;       // Set contrast for color A | ||||
| static const uint8_t SSD1331_CONTRASTB = 0x82;       // Set contrast for color B | ||||
| static const uint8_t SSD1331_CONTRASTC = 0x83;       // Set contrast for color C | ||||
| static const uint8_t SSD1331_MASTERCURRENT = 0x87;   // Master current control | ||||
| static const uint8_t SSD1331_SETREMAP = 0xA0;        // Set re-map & data format | ||||
| static const uint8_t SSD1331_STARTLINE = 0xA1;       // Set display start line | ||||
| static const uint8_t SSD1331_DISPLAYOFFSET = 0xA2;   // Set display offset | ||||
| static const uint8_t SSD1331_NORMALDISPLAY = 0xA4;   // Set display to normal mode | ||||
| static const uint8_t SSD1331_DISPLAYALLON = 0xA5;    // Set entire display ON | ||||
| static const uint8_t SSD1331_DISPLAYALLOFF = 0xA6;   // Set entire display OFF | ||||
| static const uint8_t SSD1331_INVERTDISPLAY = 0xA7;   // Invert display | ||||
| static const uint8_t SSD1331_SETMULTIPLEX = 0xA8;    // Set multiplex ratio | ||||
| static const uint8_t SSD1331_SETMASTER = 0xAD;       // Set master configuration | ||||
| static const uint8_t SSD1331_DISPLAYOFF = 0xAE;      // Display OFF (sleep mode) | ||||
| static const uint8_t SSD1331_DISPLAYON = 0xAF;       // Normal Brightness Display ON | ||||
| static const uint8_t SSD1331_POWERMODE = 0xB0;       // Power save mode | ||||
| static const uint8_t SSD1331_PRECHARGE = 0xB1;       // Phase 1 and 2 period adjustment | ||||
| static const uint8_t SSD1331_CLOCKDIV = 0xB3;        // Set display clock divide ratio/oscillator frequency | ||||
| static const uint8_t SSD1331_PRECHARGEA = 0x8A;      // Set second pre-charge speed for color A | ||||
| static const uint8_t SSD1331_PRECHARGEB = 0x8B;      // Set second pre-charge speed for color B | ||||
| static const uint8_t SSD1331_PRECHARGEC = 0x8C;      // Set second pre-charge speed for color C | ||||
| static const uint8_t SSD1331_PRECHARGELEVEL = 0xBB;  // Set pre-charge voltage | ||||
| static const uint8_t SSD1331_VCOMH = 0xBE;           // Set Vcomh voltge | ||||
|  | ||||
| void SSD1331::setup() { | ||||
|   this->init_internal_(this->get_buffer_length_()); | ||||
|  | ||||
|   this->command(SSD1331_DISPLAYOFF);  // 0xAE | ||||
|   this->command(SSD1331_SETREMAP);    // 0xA0 | ||||
|   this->command(0x72);                // RGB Color | ||||
|   this->command(SSD1331_STARTLINE);   // 0xA1 | ||||
|   this->command(0x0); | ||||
|   this->command(SSD1331_DISPLAYOFFSET);  // 0xA2 | ||||
|   this->command(0x0); | ||||
|   this->command(SSD1331_NORMALDISPLAY);  // 0xA4 | ||||
|   this->command(SSD1331_SETMULTIPLEX);   // 0xA8 | ||||
|   this->command(0x3F);                   // 0x3F 1/64 duty | ||||
|   this->command(SSD1331_SETMASTER);      // 0xAD | ||||
|   this->command(0x8E); | ||||
|   this->command(SSD1331_POWERMODE);  // 0xB0 | ||||
|   this->command(0x0B); | ||||
|   this->command(SSD1331_PRECHARGE);  // 0xB1 | ||||
|   this->command(0x31); | ||||
|   this->command(SSD1331_CLOCKDIV);    // 0xB3 | ||||
|   this->command(0xF0);                // 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio, (A[3:0]+1 = 1..16) | ||||
|   this->command(SSD1331_PRECHARGEA);  // 0x8A | ||||
|   this->command(0x64); | ||||
|   this->command(SSD1331_PRECHARGEB);  // 0x8B | ||||
|   this->command(0x78); | ||||
|   this->command(SSD1331_PRECHARGEC);  // 0x8C | ||||
|   this->command(0x64); | ||||
|   this->command(SSD1331_PRECHARGELEVEL);  // 0xBB | ||||
|   this->command(0x3A); | ||||
|   this->command(SSD1331_VCOMH);  // 0xBE | ||||
|   this->command(0x3E); | ||||
|   this->command(SSD1331_MASTERCURRENT);  // 0x87 | ||||
|   this->command(0x06); | ||||
|   set_brightness(this->brightness_); | ||||
|   this->fill(BLACK);  // clear display - ensures we do not see garbage at power-on | ||||
|   this->display();    // ...write buffer, which actually clears the display's memory | ||||
|   this->turn_on();    // display ON | ||||
| } | ||||
| void SSD1331::display() { | ||||
|   this->command(SSD1331_SETCOLUMN);  // set column address | ||||
|   this->command(0x00);               // set column start address | ||||
|   this->command(0x5F);               // set column end address | ||||
|   this->command(SSD1331_SETROW);     // set row address | ||||
|   this->command(0x00);               // set row start address | ||||
|   this->command(0x3F);               // set last row | ||||
|   this->write_display_data(); | ||||
| } | ||||
| void SSD1331::update() { | ||||
|   this->do_update_(); | ||||
|   this->display(); | ||||
| } | ||||
| void SSD1331::set_brightness(float brightness) { | ||||
|   // validation | ||||
|   this->brightness_ = clamp(brightness, 0, 1); | ||||
|   // now write the new brightness level to the display | ||||
|   this->command(SSD1331_CONTRASTA);  // 0x81 | ||||
|   this->command(int(SSD1331_MAX_CONTRASTA * (this->brightness_))); | ||||
|   this->command(SSD1331_CONTRASTB);  // 0x82 | ||||
|   this->command(int(SSD1331_MAX_CONTRASTB * (this->brightness_))); | ||||
|   this->command(SSD1331_CONTRASTC);  // 0x83 | ||||
|   this->command(int(SSD1331_MAX_CONTRASTC * (this->brightness_))); | ||||
| } | ||||
| bool SSD1331::is_on() { return this->is_on_; } | ||||
| void SSD1331::turn_on() { | ||||
|   this->command(SSD1331_DISPLAYON); | ||||
|   this->is_on_ = true; | ||||
| } | ||||
| void SSD1331::turn_off() { | ||||
|   this->command(SSD1331_DISPLAYOFF); | ||||
|   this->is_on_ = false; | ||||
| } | ||||
| int SSD1331::get_height_internal() { return 64; } | ||||
| int SSD1331::get_width_internal() { return 96; } | ||||
| size_t SSD1331::get_buffer_length_() { | ||||
|   return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) * size_t(SSD1331_BYTESPERPIXEL); | ||||
| } | ||||
| void HOT SSD1331::draw_absolute_pixel_internal(int x, int y, Color color) { | ||||
|   if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) | ||||
|     return; | ||||
|   const uint32_t color565 = color.to_rgb_565(); | ||||
|   // where should the bits go in the big buffer array? math... | ||||
|   uint16_t pos = (x + y * this->get_width_internal()) * SSD1331_BYTESPERPIXEL; | ||||
|   this->buffer_[pos++] = (color565 >> 8) & 0xff; | ||||
|   this->buffer_[pos] = color565 & 0xff; | ||||
| } | ||||
| void SSD1331::fill(Color color) { | ||||
|   const uint32_t color565 = color.to_rgb_565(); | ||||
|   for (uint32_t i = 0; i < this->get_buffer_length_(); i++) | ||||
|     if (i & 1) { | ||||
|       this->buffer_[i] = color565 & 0xff; | ||||
|     } else { | ||||
|       this->buffer_[i] = (color565 >> 8) & 0xff; | ||||
|     } | ||||
| } | ||||
| void SSD1331::init_reset_() { | ||||
|   if (this->reset_pin_ != nullptr) { | ||||
|     this->reset_pin_->setup(); | ||||
|     this->reset_pin_->digital_write(true); | ||||
|     delay(1); | ||||
|     // Trigger Reset | ||||
|     this->reset_pin_->digital_write(false); | ||||
|     delay(10); | ||||
|     // Wake up | ||||
|     this->reset_pin_->digital_write(true); | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace ssd1331_base | ||||
| }  // namespace esphome | ||||
							
								
								
									
										45
									
								
								esphome/components/ssd1331_base/ssd1331_base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								esphome/components/ssd1331_base/ssd1331_base.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/esphal.h" | ||||
| #include "esphome/components/display/display_buffer.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ssd1331_base { | ||||
|  | ||||
| class SSD1331 : public PollingComponent, public display::DisplayBuffer { | ||||
|  public: | ||||
|   void setup() override; | ||||
|  | ||||
|   void display(); | ||||
|  | ||||
|   void update() override; | ||||
|  | ||||
|   void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } | ||||
|   void init_brightness(float brightness) { this->brightness_ = brightness; } | ||||
|   void set_brightness(float brightness); | ||||
|   bool is_on(); | ||||
|   void turn_on(); | ||||
|   void turn_off(); | ||||
|  | ||||
|   float get_setup_priority() const override { return setup_priority::PROCESSOR; } | ||||
|   void fill(Color color) override; | ||||
|  | ||||
|  protected: | ||||
|   virtual void command(uint8_t value) = 0; | ||||
|   virtual void write_display_data() = 0; | ||||
|   void init_reset_(); | ||||
|  | ||||
|   void draw_absolute_pixel_internal(int x, int y, Color color) override; | ||||
|  | ||||
|   int get_height_internal() override; | ||||
|   int get_width_internal() override; | ||||
|   size_t get_buffer_length_(); | ||||
|  | ||||
|   GPIOPin *reset_pin_{nullptr}; | ||||
|   bool is_on_{false}; | ||||
|   float brightness_{1.0}; | ||||
| }; | ||||
|  | ||||
| }  // namespace ssd1331_base | ||||
| }  // namespace esphome | ||||
							
								
								
									
										0
									
								
								esphome/components/ssd1331_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/ssd1331_spi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										28
									
								
								esphome/components/ssd1331_spi/display.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								esphome/components/ssd1331_spi/display.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import spi, ssd1331_base | ||||
| from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES | ||||
|  | ||||
| CODEOWNERS = ['@kbx81'] | ||||
|  | ||||
| AUTO_LOAD = ['ssd1331_base'] | ||||
| DEPENDENCIES = ['spi'] | ||||
|  | ||||
| ssd1331_spi = cg.esphome_ns.namespace('ssd1331_spi') | ||||
| SPISSD1331 = ssd1331_spi.class_('SPISSD1331', ssd1331_base.SSD1331, spi.SPIDevice) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All(ssd1331_base.SSD1331_SCHEMA.extend({ | ||||
|     cv.GenerateID(): cv.declare_id(SPISSD1331), | ||||
|     cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, | ||||
| }).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema()), | ||||
|                        cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) | ||||
|  | ||||
|  | ||||
| def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     yield ssd1331_base.setup_ssd1331(var, config) | ||||
|     yield spi.register_spi_device(var, config) | ||||
|  | ||||
|     dc = yield cg.gpio_pin_expression(config[CONF_DC_PIN]) | ||||
|     cg.add(var.set_dc_pin(dc)) | ||||
							
								
								
									
										58
									
								
								esphome/components/ssd1331_spi/ssd1331_spi.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								esphome/components/ssd1331_spi/ssd1331_spi.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| #include "ssd1331_spi.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/application.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ssd1331_spi { | ||||
|  | ||||
| static const char *TAG = "ssd1331_spi"; | ||||
|  | ||||
| void SPISSD1331::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up SPI SSD1331..."); | ||||
|   this->spi_setup(); | ||||
|   this->dc_pin_->setup();  // OUTPUT | ||||
|   if (this->cs_) | ||||
|     this->cs_->setup();  // OUTPUT | ||||
|  | ||||
|   this->init_reset_(); | ||||
|   delay(500);  // NOLINT | ||||
|   SSD1331::setup(); | ||||
| } | ||||
| void SPISSD1331::dump_config() { | ||||
|   LOG_DISPLAY("", "SPI SSD1331", this); | ||||
|   if (this->cs_) | ||||
|     LOG_PIN("  CS Pin: ", this->cs_); | ||||
|   LOG_PIN("  DC Pin: ", this->dc_pin_); | ||||
|   LOG_PIN("  Reset Pin: ", this->reset_pin_); | ||||
|   ESP_LOGCONFIG(TAG, "  Initial Brightness: %.2f", this->brightness_); | ||||
|   LOG_UPDATE_INTERVAL(this); | ||||
| } | ||||
| void SPISSD1331::command(uint8_t value) { | ||||
|   if (this->cs_) | ||||
|     this->cs_->digital_write(true); | ||||
|   this->dc_pin_->digital_write(false); | ||||
|   delay(1); | ||||
|   this->enable(); | ||||
|   if (this->cs_) | ||||
|     this->cs_->digital_write(false); | ||||
|   this->write_byte(value); | ||||
|   if (this->cs_) | ||||
|     this->cs_->digital_write(true); | ||||
|   this->disable(); | ||||
| } | ||||
| void HOT SPISSD1331::write_display_data() { | ||||
|   if (this->cs_) | ||||
|     this->cs_->digital_write(true); | ||||
|   this->dc_pin_->digital_write(true); | ||||
|   if (this->cs_) | ||||
|     this->cs_->digital_write(false); | ||||
|   delay(1); | ||||
|   this->enable(); | ||||
|   this->write_array(this->buffer_, this->get_buffer_length_()); | ||||
|   if (this->cs_) | ||||
|     this->cs_->digital_write(true); | ||||
|   this->disable(); | ||||
| } | ||||
|  | ||||
| }  // namespace ssd1331_spi | ||||
| }  // namespace esphome | ||||
							
								
								
									
										29
									
								
								esphome/components/ssd1331_spi/ssd1331_spi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								esphome/components/ssd1331_spi/ssd1331_spi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/ssd1331_base/ssd1331_base.h" | ||||
| #include "esphome/components/spi/spi.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace ssd1331_spi { | ||||
|  | ||||
| class SPISSD1331 : public ssd1331_base::SSD1331, | ||||
|                    public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING, | ||||
|                                          spi::DATA_RATE_8MHZ> { | ||||
|  public: | ||||
|   void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } | ||||
|  | ||||
|   void setup() override; | ||||
|  | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   void command(uint8_t value) override; | ||||
|  | ||||
|   void write_display_data() override; | ||||
|  | ||||
|   GPIOPin *dc_pin_; | ||||
| }; | ||||
|  | ||||
| }  // namespace ssd1331_spi | ||||
| }  // namespace esphome | ||||
| @@ -1641,6 +1641,12 @@ display: | ||||
|   reset_pin: GPIO23 | ||||
|   lambda: |- | ||||
|     it.rectangle(0, 0, it.get_width(), it.get_height()); | ||||
| - platform: ssd1331_spi | ||||
|   cs_pin: GPIO23 | ||||
|   dc_pin: GPIO23 | ||||
|   reset_pin: GPIO23 | ||||
|   lambda: |- | ||||
|     it.rectangle(0, 0, it.get_width(), it.get_height()); | ||||
| - platform: ssd1351_spi | ||||
|   model: "SSD1351 128x128" | ||||
|   cs_pin: GPIO23 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user