/*############################################################################# ## Author: Shaun Reed ## ## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ## ## ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ## ############################################################################## */ #ifndef SSD1306_H #define SSD1306_H #include "panel_device.h" #include #include // According to specific display hardware. // https://www.digikey.com/en/products/detail/winstar-display/WEA012864DWPP3N00003/20533255 #define SCREEN_WIDTH 128 // OLED display width, in pixels. #define SCREEN_HEIGHT 64 // OLED display height, in pixels. // According to SSD1306 datasheet. // https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf #define I2C_HW_ADDR 0x3C #define LCD_PIXEL_CLOCK_HZ (400 * 1000) // Bit number used to represent command and parameter #define LCD_CMD_BITS 8 #define LCD_PARAM_BITS 8 /** * Example of implementing the IPanelDevice interface for SSD1306 LCD device. */ struct SSD1306 { /// SSD1306 configuration structure. esp_lcd_panel_ssd1306_config_t ssd1306_config_; }; /// Initializes the ESP LCD panel handle for the SSD1306 device. static void SSD1306_init_panel_cb(esp_lcd_panel_dev_config_t* config, esp_lcd_panel_io_handle_t io, esp_lcd_panel_handle_t* panel) { ESP_LOGI(LCD_TAG, "Initializing SSD1306 panel"); ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io, config, panel)); } static esp_lcd_panel_ssd1306_config_t SSD1306_config = { .height = SCREEN_HEIGHT, }; /** * Provides the SSD1306 vendor configuration to IPanelDevice consumers. * * @return Address of the SSD1306 vendor configuration structure. */ static void* SSD1306_vendor_config_cb() { ESP_LOGI(LCD_TAG, "Fetching global SSD1306 config"); return &SSD1306_config; } /** * Construct a new SSD1306 device given a specific SSD1306 configuration. * * @param vendor_config SSD1306 vendor configuration. * @param width Width of the device screen in pixels. * @param height Height of the device screen in pixels. */ static struct IPanelDevice SSD1306_config_new(esp_lcd_panel_ssd1306_config_t vendor_config, int width, int height) { // TODO: Make it not global; There could be multiple SSD1306 displays. ESP_LOGI(LCD_TAG, "Setting global SSD1306 configuration"); SSD1306_config = vendor_config; // Must initialize device with LCD_new_panel to use default LVGL callbacks. struct IPanelDevice device = LCD_new_panel(); device.width_ = width, device.height_ = height; device.esp_io_config_ = (esp_lcd_panel_io_i2c_config_t){ .dev_addr = I2C_HW_ADDR, // User data to pass to the LVGL flush_ready callback. // See IPanelDevice::lvgl_flush_ready_cb .user_ctx = NULL, .control_phase_bytes = 1, .dc_bit_offset = 6, .lcd_cmd_bits = LCD_CMD_BITS, .lcd_param_bits = LCD_PARAM_BITS, .scl_speed_hz = LCD_PIXEL_CLOCK_HZ, }; device.init_panel_cb = SSD1306_init_panel_cb; device.vendor_config_cb = SSD1306_vendor_config_cb; return device; } /** * Construct a new SSD1306 device. */ static struct IPanelDevice SSD1306_new() { return SSD1306_config_new( (esp_lcd_panel_ssd1306_config_t){.height = SCREEN_HEIGHT}, SCREEN_WIDTH, SCREEN_HEIGHT); } #endif // SSD1306_H