OLED & Graphics Libraries

bkgoodman
Posts: 45
Joined: Fri Feb 17, 2017 12:41 pm

OLED & Graphics Libraries

Postby bkgoodman » Mon Dec 21, 2020 4:33 pm

I got an ESP32 with a OLED display I wanted to kick around (using ESP-IDF). The example code for this board was written in Arduino IDE. I was kind of surprised I couldn't find more support for any type of graphics libraries, (or I2C OLED displays) anywhere. Does anyone know if such a thing exists anywhere??

Victoria Nope
Posts: 75
Joined: Fri Dec 04, 2020 9:56 pm

Re: OLED & Graphics Libraries

Postby Victoria Nope » Tue Dec 22, 2020 5:12 pm

Here is one project for the SSD1306 display driver.

Jamawa
Posts: 16
Joined: Sun Aug 16, 2020 10:46 am

Re: OLED & Graphics Libraries

Postby Jamawa » Tue Dec 22, 2020 7:39 pm

Another option could be LVGL: Light and Versatile Graphics Library. A generic library for graphics and touch controllers which is ported to a number of processors amongst which the esp32.
Generic info: https://github.com/lvgl/lvgl
Esp32 port: https://github.com/lvgl/lv_port_esp32

I am using it in a project on a TTGO board with display, works quite nicely, although it takes a bit of time understanding the API

adamwilt
Posts: 13
Joined: Sun Dec 06, 2020 1:54 am

Re: OLED & Graphics Libraries

Postby adamwilt » Wed Dec 23, 2020 3:46 am

Check out u8g2: https://github.com/olikraus/u8g2

Using it in esp-idf: https://www.lucadentella.it/en/2017/10/ ... -con-u8g2/

Here are the necessary "hal" files in the Kolban repository:
https://github.com/nkolban/esp32-snippe ... plays/U8G2
The test files show how easy it is to use. The hardest bit is picking the right device driver for your particular display; there are several variants on the SSD1306, for example.

In the u8g2_esp32_hal.c file, make sure that the init code in the i2c callback function properly clears the i2c_config_t structs, or sets every element explicitly, otherwise random crashes can and will happen! The Kolban repository isn't being maintained; here's my copy of that file with that change incorporated (the memset call at line 120):

Code: Select all

#include <stdio.h>
#include <string.h>

#include "sdkconfig.h"
#include "esp_log.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "u8g2_esp32_hal.h"

static const char *TAG = "u8g2_esp32_hal";
static const unsigned int I2C_TIMEOUT_MS = 1000;

static spi_device_handle_t handle_spi;      // SPI handle.
static i2c_cmd_handle_t    handle_i2c;      // I2C handle.
static u8g2_esp32_hal_t    u8g2_esp32_hal;  // HAL state data.

#undef ESP_ERROR_CHECK
#define ESP_ERROR_CHECK(x)   do { esp_err_t rc = (x); if (rc != ESP_OK) { ESP_LOGE("err", "esp_err_t = %d", rc); assert(0 && #x);} } while(0);

/*
 * Initialze the ESP32 HAL.
 */
void u8g2_esp32_hal_init(u8g2_esp32_hal_t u8g2_esp32_hal_param) {
	u8g2_esp32_hal = u8g2_esp32_hal_param;
} // u8g2_esp32_hal_init

/*
 * HAL callback function as prescribed by the U8G2 library.  This callback is invoked
 * to handle SPI communications.
 */
uint8_t u8g2_esp32_spi_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
	ESP_LOGD(TAG, "spi_byte_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr);
	switch(msg) {
		case U8X8_MSG_BYTE_SET_DC:
			if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
				gpio_set_level(u8g2_esp32_hal.dc, arg_int);
			}
			break;

		case U8X8_MSG_BYTE_INIT: {
			if (u8g2_esp32_hal.clk == U8G2_ESP32_HAL_UNDEFINED ||
					u8g2_esp32_hal.mosi == U8G2_ESP32_HAL_UNDEFINED ||
					u8g2_esp32_hal.cs == U8G2_ESP32_HAL_UNDEFINED) {
				break;
			}

		  spi_bus_config_t bus_config;
                  memset(&bus_config, 0, sizeof(spi_bus_config_t));
		  bus_config.sclk_io_num   = u8g2_esp32_hal.clk; // CLK
		  bus_config.mosi_io_num   = u8g2_esp32_hal.mosi; // MOSI
		  bus_config.miso_io_num   = -1; // MISO
		  bus_config.quadwp_io_num = -1; // Not used
		  bus_config.quadhd_io_num = -1; // Not used
		  //ESP_LOGI(TAG, "... Initializing bus.");
		  ESP_ERROR_CHECK(spi_bus_initialize(HSPI_HOST, &bus_config, 1));

		  spi_device_interface_config_t dev_config;
		  memset(&dev_config, 0, sizeof(spi_device_interface_config_t));
//		  dev_config.address_bits     = 0;
//		  dev_config.command_bits     = 0;
//		  dev_config.dummy_bits       = 0;
//		  dev_config.mode             = 0;
//		  dev_config.duty_cycle_pos   = 0;
//		  dev_config.cs_ena_posttrans = 0;
//		  dev_config.cs_ena_pretrans  = 0;
		  dev_config.clock_speed_hz   = 10000;
		  dev_config.spics_io_num     = u8g2_esp32_hal.cs;
//		  dev_config.flags            = 0;
		  dev_config.queue_size       = 200;
//		  dev_config.pre_cb           = NULL;
//		  dev_config.post_cb          = NULL;
		  //ESP_LOGI(TAG, "... Adding device bus.");
		  ESP_ERROR_CHECK(spi_bus_add_device(HSPI_HOST, &dev_config, &handle_spi));

		  break;
		}

		case U8X8_MSG_BYTE_SEND: {
			spi_transaction_t trans_desc;
			trans_desc.addr      = 0;
			trans_desc.cmd   	 = 0;
			trans_desc.flags     = 0;
			trans_desc.length    = 8 * arg_int; // Number of bits NOT number of bytes.
			trans_desc.rxlength  = 0;
			trans_desc.tx_buffer = arg_ptr;
			trans_desc.rx_buffer = NULL;

			//ESP_LOGI(TAG, "... Transmitting %d bytes.", arg_int);
			ESP_ERROR_CHECK(spi_device_transmit(handle_spi, &trans_desc));
			break;
		}
	}
	return 0;
} // u8g2_esp32_spi_byte_cb

/*
 * HAL callback function as prescribed by the U8G2 library.  This callback is invoked
 * to handle I2C communications.
 */
uint8_t u8g2_esp32_i2c_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
	ESP_LOGD(TAG, "i2c_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr);

	switch(msg) {
		case U8X8_MSG_BYTE_SET_DC: {
			if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
				gpio_set_level(u8g2_esp32_hal.dc, arg_int);
			}
			break;
		}

		case U8X8_MSG_BYTE_INIT: {
			if (u8g2_esp32_hal.sda == U8G2_ESP32_HAL_UNDEFINED ||
					u8g2_esp32_hal.scl == U8G2_ESP32_HAL_UNDEFINED) {
				break;
			}
            //i2c_driver_delete(I2C_MASTER_NUM); // delete if already installed
		    i2c_config_t conf;
		    memset(&conf, 0, sizeof(i2c_config_t));
		    conf.mode = I2C_MODE_MASTER;
			//ESP_LOGI(TAG, "sda_io_num %d", u8g2_esp32_hal.sda);
		    conf.sda_io_num = u8g2_esp32_hal.sda;
		    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
			//ESP_LOGI(TAG, "scl_io_num %d", u8g2_esp32_hal.scl);
		    conf.scl_io_num = u8g2_esp32_hal.scl;
		    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
			//ESP_LOGI(TAG, "clk_speed %d", I2C_MASTER_FREQ_HZ);
		    conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
            //ESP_LOGI(TAG, "clk_flags 0x%X", conf.clk_flags);
			//ESP_LOGI(TAG, "i2c_param_config %d", conf.mode);
		    ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_NUM, &conf));
            //ESP_LOGI(TAG, "i2c_driver_install for I2C bus %d", I2C_MASTER_NUM);
		    ESP_ERROR_CHECK(i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0));
            ESP_LOGI(TAG, "i2c_driver_install done.");
			break;
		}

		case U8X8_MSG_BYTE_SEND: {
			uint8_t* data_ptr = (uint8_t*)arg_ptr;
			ESP_LOG_BUFFER_HEXDUMP(TAG, data_ptr, arg_int, ESP_LOG_VERBOSE);

			while( arg_int > 0 ) {
			   ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, *data_ptr, ACK_CHECK_EN));
			   data_ptr++;
			   arg_int--;
			}
			break;
		}

		case U8X8_MSG_BYTE_START_TRANSFER: {
			uint8_t i2c_address = u8x8_GetI2CAddress(u8x8);
			handle_i2c = i2c_cmd_link_create();
			ESP_LOGD(TAG, "Start I2C transfer to %02X.", i2c_address>>1);
			ESP_ERROR_CHECK(i2c_master_start(handle_i2c));
			ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, i2c_address | I2C_MASTER_WRITE, ACK_CHECK_EN));
			break;
		}

		case U8X8_MSG_BYTE_END_TRANSFER: {
			ESP_LOGD(TAG, "End I2C transfer.");
			ESP_ERROR_CHECK(i2c_master_stop(handle_i2c));
			ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_MASTER_NUM, handle_i2c, I2C_TIMEOUT_MS / portTICK_RATE_MS));
			i2c_cmd_link_delete(handle_i2c);
			break;
		}
	}
	return 0;
} // u8g2_esp32_i2c_byte_cb

/*
 * HAL callback function as prescribed by the U8G2 library.  This callback is invoked
 * to handle callbacks for GPIO and delay functions.
 */
uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
	ESP_LOGD(TAG, "gpio_and_delay_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr);

	switch(msg) {
	// Initialize the GPIO and DELAY HAL functions.  If the pins for DC and RESET have been
	// specified then we define those pins as GPIO outputs.
		case U8X8_MSG_GPIO_AND_DELAY_INIT: {
			uint64_t bitmask = 0;
			if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
				bitmask = bitmask | (1ull<<u8g2_esp32_hal.dc);
			}
			if (u8g2_esp32_hal.reset != U8G2_ESP32_HAL_UNDEFINED) {
				bitmask = bitmask | (1ull<<u8g2_esp32_hal.reset);
			}
			if (u8g2_esp32_hal.cs != U8G2_ESP32_HAL_UNDEFINED) {
				bitmask = bitmask | (1ull<<u8g2_esp32_hal.cs);
			}

            if (bitmask==0) {
            	break;
            }
			gpio_config_t gpioConfig;
			gpioConfig.pin_bit_mask = bitmask;
			gpioConfig.mode         = GPIO_MODE_OUTPUT;
			gpioConfig.pull_up_en   = GPIO_PULLUP_DISABLE;
			gpioConfig.pull_down_en = GPIO_PULLDOWN_ENABLE;
			gpioConfig.intr_type    = GPIO_INTR_DISABLE;
			gpio_config(&gpioConfig);
			break;
		}

	// Set the GPIO reset pin to the value passed in through arg_int.
		case U8X8_MSG_GPIO_RESET:
			if (u8g2_esp32_hal.reset != U8G2_ESP32_HAL_UNDEFINED) {
				gpio_set_level(u8g2_esp32_hal.reset, arg_int);
			}
			break;
	// Set the GPIO client select pin to the value passed in through arg_int.
		case U8X8_MSG_GPIO_CS:
			if (u8g2_esp32_hal.cs != U8G2_ESP32_HAL_UNDEFINED) {
				gpio_set_level(u8g2_esp32_hal.cs, arg_int);
			}
			break;
	// Set the Software I²C pin to the value passed in through arg_int.
		case U8X8_MSG_GPIO_I2C_CLOCK:
			if (u8g2_esp32_hal.scl != U8G2_ESP32_HAL_UNDEFINED) {
				gpio_set_level(u8g2_esp32_hal.scl, arg_int);
//				printf("%c",(arg_int==1?'C':'c'));
			}
			break;
	// Set the Software I²C pin to the value passed in through arg_int.
		case U8X8_MSG_GPIO_I2C_DATA:
			if (u8g2_esp32_hal.sda != U8G2_ESP32_HAL_UNDEFINED) {
				gpio_set_level(u8g2_esp32_hal.sda, arg_int);
//				printf("%c",(arg_int==1?'D':'d'));
			}
			break;

	// Delay for the number of milliseconds passed in through arg_int.
		case U8X8_MSG_DELAY_MILLI:
			vTaskDelay(arg_int/portTICK_PERIOD_MS);
			break;
	}
	return 0;
} // u8g2_esp32_gpio_and_delay_cb

J00003
Posts: 13
Joined: Sun Mar 04, 2018 10:14 pm

Re: OLED & Graphics Libraries

Postby J00003 » Tue Mar 23, 2021 9:03 pm

Thanks @adamwilt for the pointer here.
I'd like to share my experience in order to possibly help someone else out trying to do this.

I bought a couple of 0.91" 128x32 pixel displays and started down this path using esp-idf and the u8g2 library: https://github.com/olikraus/u8g2. However couldn't get anything working. The displays would not budge at all - no sparks, no smoke, no signs of life at all.
Well, almost nothing. Just a few clues:
  • Getting ESP_FAIL (-1) from calling i2c_master_cmd_begin() in the HAL I2C byte callback.
  • With Vcc wired to the OLED, I could not enable flash download. (ie: getting famous: flash read err, 1000). So pretty sure the board is at least drawing power.
  • Unplugging the Oled Vcc, causes I2C_master_cmd_begin() to return 0x107 (ESP_TIMEOUT).
Btw... enabling DEBUG level logging was helpful to at least follow the code flow to this point.

First thing I tried to was to disable the internal Esp32 pullup resistors (as these are too week) and add 2 external pullup resistors. Since the Esp32 uses Vcc at 3.3V, I used 2 - 3K3 resistors, one on each of the SDA and SCL lines. The GPIO internal pullup's need to be disabled in the HAL code. (see below).
Btw... using the following GPIO pins:

Code: Select all

#define GPIO_OLED_SDA            GPIO_NUM_15  // pin 23 (white-green wire)
#define GPIO_OLED_SCL            GPIO_NUM_2   // pin 24 (white-blue wire)
So, after learning how to decode I2C with a 2 channel scope and these 2 excellent protocol resources: https://en.wikipedia.org/wiki/I%C2%B2C and https://rheingoldheavy.com/i2c-signals, I found that there was a response Nack on the bus to the first address/write byte written out. Hence the ESP_FAIL.

The scope showed that my code was sending 00111100 from the I2C master (esp32), and reading the 1 bit ack/nack response for a total of 9 bits. Decoding the bits sent on the bus as 7 bit address + 1 bit R/W flag, yields: address = 0011110 (or 0x1E) and R/W = 0 (or write). The ack/nack response bit was: 1 (or Nack).

The reason I rec'd a Nack is due to: "There isn’t anything on the I2C bus that corresponds to the address that was sent. Since no one’s home, there’s nothing to pull the SDA line low, so a NACK is registered." (quoted from 2nd protocol resource above).

So, why is the Oled board not responding?
Well, it seems that the transmitted address is not correct here. I see 0x1E on the bus which is not what I was expecting. I had setup the code to use address 0x3C, as shown here.

Code: Select all

u8x8_SetI2CAddress(&u8g2.u8x8, 0x3C);
Small aside: I found a simple I2C bus scanner here: https://gist.github.com/herzig/8d4c13d8 ... 6c1306bb12 and used this to determine that the Oled device address is 0x3C.

According to the esp-idf docs here: https://docs.espressif.com/projects/esp ... s/i2c.html the data argument (ie: the slave address) to i2c_master_write_byte() should be left shifted and OR'ed with the write bit (ie: 0) as follows.

Code: Select all

i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE, ACK_EN);
The hal code was not doing this properly, so the idf I2C driver transmitted the incorrect address (0x3C >> 1, or 0x1E) on the bus. This is why I rec'd a Nack as the first response bit.

Here's the fixed hal code with that will use the proper address as setup via u8x8_SetI2CAddress() shown above.

Code: Select all

/*
 * HAL callback function as prescribed by the U8G2 library.  This callback is invoked
 * to handle I2C communications.
 */
uint8_t u8g2_esp32_i2c_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
	ESP_LOGD(TAG, "i2c_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr);

	switch(msg) {
		case U8X8_MSG_BYTE_SET_DC:  //32
			if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
				gpio_set_level(u8g2_esp32_hal.dc, arg_int);
			}
			break;

		case U8X8_MSG_BYTE_INIT:  //20
			if (u8g2_esp32_hal.sda == U8G2_ESP32_HAL_UNDEFINED ||
					u8g2_esp32_hal.scl == U8G2_ESP32_HAL_UNDEFINED) {
				break;
			}
		    //i2c_driver_delete(I2C_MASTER_NUM); // delete if already installed
		    i2c_config_t conf;
		    memset(&conf, 0, sizeof(i2c_config_t));
		    conf.mode = I2C_MODE_MASTER;
		    ESP_LOGI(TAG, "sda_io_num %d", u8g2_esp32_hal.sda);
		    conf.sda_io_num = u8g2_esp32_hal.sda;
		    //conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
		    conf.sda_pullup_en = GPIO_PULLUP_DISABLE;                   // External pull-up required, 3K3 for Vcc=3.3V
		    ESP_LOGI(TAG, "scl_io_num %d", u8g2_esp32_hal.scl);
		    conf.scl_io_num = u8g2_esp32_hal.scl;
		    //conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
		    conf.scl_pullup_en = GPIO_PULLUP_DISABLE;                   // External pull-up required, 3K3 for Vcc=3.3V
		    ESP_LOGI(TAG, "clk_speed %d", I2C_MASTER_FREQ_HZ);
		    conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
		    ESP_LOGI(TAG, "clk_flags 0x%X", conf.clk_flags);
		    ESP_LOGI(TAG, "i2c_param_config %d", conf.mode);
		    ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_NUM, &conf));
		    ESP_LOGI(TAG, "i2c_driver_install for I2C bus %d", I2C_MASTER_NUM);
		    ESP_ERROR_CHECK(i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0));
		    ESP_LOGI(TAG, "i2c_driver_install done.");
			break;

		case U8X8_MSG_BYTE_SEND: { //23
			uint8_t* data_ptr = (uint8_t*)arg_ptr;
			ESP_LOG_BUFFER_HEXDUMP(TAG, data_ptr, arg_int, ESP_LOG_VERBOSE);

			while( arg_int > 0 ) {
			   ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, *data_ptr, ACK_CHECK_EN));
			   data_ptr++;
			   arg_int--;
			}
		}
		break;

		case U8X8_MSG_BYTE_START_TRANSFER: { //24
			uint8_t i2c_address = u8x8_GetI2CAddress(u8x8);
			handle_i2c = i2c_cmd_link_create();
			ESP_LOGD(TAG, "Start I2C transfer to 0x%02X", i2c_address);
			ESP_ERROR_CHECK(i2c_master_start(handle_i2c));
			ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, ((i2c_address << 1) | I2C_MASTER_WRITE), ACK_CHECK_EN));
		}
		break;

		case U8X8_MSG_BYTE_END_TRANSFER:  //25
			ESP_LOGD(TAG, "End I2C transfer.");
			ESP_ERROR_CHECK(i2c_master_stop(handle_i2c));
			ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_MASTER_NUM, handle_i2c, I2C_TIMEOUT_MS / portTICK_RATE_MS));
			i2c_cmd_link_delete(handle_i2c);
			break;
	}
	return 0;
} // u8g2_esp32_i2c_byte_cb
One other thing that took a bit to figure out is: what u8g2 setup function to use for my specific Oled board. There are several options in the library for the 128x32 geometry.
My suggestion is try them all until you find the one that works. Some partially work and display garbage pixels on various portions of the display and some do absolutely nothing. You can see the results I did get in the code comments below.

Code: Select all

   /* Configure the u8g2 library and call the setup function for the 128x32 oled module
    * U8G2_R0 - no rotation of the display
    */
   u8g2_t u8g2;
   memset((void*)&u8g2, 0, sizeof(u8g2));
   //u8g2_Setup_ssd1306_128x32_univision_1(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb); // device responds, but no display output
   //u8g2_Setup_ssd1306_128x64_noname_f(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);    // device responds, but no display output
   //u8g2_Setup_ssd1305_128x32_noname_1(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);    // device responds, but no display output
   //u8g2_Setup_sh1106_128x32_visionox_1(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);   // ESP_FAIL, got a nack most likely, did NOT verify with scope

   //u8g2_Setup_ssd1306_i2c_128x32_univision_1(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);  // device responds, address 0x78, get some display output, top line is ok, but garbage after that
   //u8g2_Setup_ssd1306_i2c_128x32_winstar_1(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);    // device responds, get 2 rows of cut off display output, bottom is garbage
   //u8g2_Setup_ssd1306_i2c_128x32_univision_2(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);  // better that 2 above; get more of my text output almost the entire line of "Hello Loop: 0" bottom half is still garbage
   //u8g2_Setup_ssd1306_i2c_128x32_winstar_2(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);    // worse... shift down.. still some garbage... and getting a double line
   u8g2_Setup_ssd1306_i2c_128x32_univision_f(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);    // WORKING: best so far... no garbage, and displays text on the top line, as expected
   //u8g2_Setup_ssd1306_i2c_128x32_winstar_f(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb, u8g2_esp32_gpio_and_delay_cb);    // this is similar to above, didn't clear the entire display, a bit of garbage at the top, and the line is not in the correct position

   /*
    * The real address of the device is 0x3C.
    * Using external pullup resistors for now... need to do a test with out them -> fails.
    */
   u8x8_SetI2CAddress(&u8g2.u8x8, 0x3C);
In summary, there were 4 things I had to do to make this work:
  • Disable the internal pullup resistors and use external ones sized correctly for Vcc. (ie: to deliver ~1mA).
  • Use the correct I2C address, 0x3C in my case.
  • Fix the hal code to properly pass the address to the underlying I2C idf driver.
  • Find & test the proper setup function for my ssd1306 compatible Oled.

sprobe
Posts: 1
Joined: Tue May 25, 2021 8:45 pm

Re: OLED & Graphics Libraries

Postby sprobe » Tue May 25, 2021 8:49 pm

Brilliant thank you, my issue was also with the address. Turns out the default set in the example 0x78 == 0x3c << 1, but I had changed it before even testing. I prefer your fix anyway.

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 325 guests