Demo 4: How to use Arduino ESP32 to display information on I2C LCD

1. Introduction
This tutorial show you how to connect a LCD module to Arduino ESP32 via LCM1602 module to display information from ESP32. There are 2 ways to connect ESP32 to LCD module:
  • Connect directly. By using this way, ESP32 will waste 7 GPIO pins (4 GPIO pins for data transfer and 3 GPIO pins for LCD control).
  • Connect via LCM1602 module. By using this way, ESP32 will only use 2 GPIO pins which act as I2C SDA and SCL pins. LCM1602 will be responsible for converting I2C data to LCD data and control signals.
    Figure: LCD 16x02 module
    Figure: YwRobot LCM1602 module
Note: the I2C address of LCM1602 is 0x27
2. Hardware
Next we connect the pins of ESP32 to LCD module:
[ESP32 GPIO12 - LCM1602 SDA]
[ESP32 GPIO14 - LCM1602 SCL]
[ESP32 GND - LCM1602 GND]
[LCM1602 - 5V]
 or follow the picture below:
Figure: connect ESP32 to LCM1602

3. Software
We will use the library LiquidCrystal_I2C that is made for Arduino but I modified a little to compatible with ESP32. You can download the library here:
After downloading, unzip and copy the unzipped folder under folder:
The library supplied some functions:
  • begin(sda=-1, int8_t scl=-1): to initialize library and I2C pins, sda is ESP32 GPIO pin connect to LCM1602 SDA, scl is ESP32 GPIO pin connect to LCM1602 SCL.
  • backlight(): to turn on backlight
  • setCursor(uint8_t col, uint8_t row): set cursor at column and row (16x02: 2 rows [with index from 0 to 1] and 16 cols [with index from 0 to 15])
  • print(char s[]): print the string s to LCD
In order to use these functions you need to create an instance of LiquidCrystal_I2C:
LiquidCrystal_I2C(uint8_t lcd_addr, uint8_t lcd_cols, uint8_t lcd_rows, uint8_t charsize = LCD_5x8DOTS);
lcd_Addr: is the I2C address of LCD, in this case it is 0x27.
lcd_cols: is the number of columns of LCD, it is 16.
lcd_rows: is the number of rows of LCD, it is 2.
charsize = LCD_5x8DOTS: is the size of one character. Default value is 5x8 dots.
so it look like this:
LiquidCrystal_I2C lcd(0x27, 16, 2);
lcd.begin(12, 14);
it means ESP3 GPIO12 is connected to LCM1602 SDA and ESP32 GPIO14 is connected to LCM1602 SCL
Finally, you create an Arduino project and save it as esp32lcd with code:
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,16,2);//set the LCD address to 0x27 for a 16 chars and 2 line display

void setup(){ 
  lcd.begin(12, 14);// initialize the lcd with SDA and SCL pins
  // Print a message to the LCD.
  lcd.print("Hello, world!");
  lcd.print("by EasyIoT");
void loop(){

4. Result

Figure: ESP32 display "Hello Woorld" on LCD module

Post a Comment


Unknown said…

I have used the modified ESP32-Master you provided.I am wrestling with the following compilation errors:
I have studied LiquidCrystal_I2C.h and see your define: void setCursor(uint8_t, uint8_t);
but get errors such as:

undefined reference to `LiquidCrystal_I2C::setCursor(unsigned char, unsigned char)'

Any help?

What is your code ? It look like you try to use setCursor with char rather than int?
Bubbleio said…
I've tested the code with ESP32. it work one time but not in loop. Send need time to refresh and ESP is too quick, screen stop to run. My proposition is to add 120ms on each send command:
void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) {
uint8_t highnib=value&0xf0;
uint8_t lownib=(value<<4)&0xf0;

After 2-3 min the LCD "crash" anyway. I restart it after a while:
LiquidCrystal_I2C lcd(0x27, 16, 2);//set the LCD address to 0x27 for a 16 chars and 2 line display

void setup() {

void loop() {
// Reinit every 30x2 secondes
if (++n_Cpt > 30)
n_Cpt = 0;

void Init_LCD() {
// Init LCD.
Serial.println("----INIT LCD----");
lcd.begin(LCD_SDA, LCD_SCL);

//Init message
lcd.setCursor(0, 0);
lcd.setCursor(0, 1);
lcd.print("xx.xx Celsius");

void Processing() {
f_DC = ktc.readCelsius();

// Debug
Serial.printf("Temp = %.2f Celsuis\n", f_DC);

//lcd.setCursor(0, 0);
lcd.setCursor(0, 1);
// Upadte Temp
lcd.printf("%.2f Celsius", f_DC);
//lcd.printf("%.2f", f_DC); => After a while Celsius is eratically erase with random char !

thanks for ur comment. I will check it soon :)
Bubbleio said…
I've run it a while and the LCD freeze after 5 to 15 min anymay. I didn't find resource on internet showing ESP32 running LCM1602 dynamically. It would require a deeper investigation.

@iotsharing dotcom => Yes please keep us inform of your investigation. On my side, I'll work on another subject and suspend THE VERY CHEAP LCM1602 for the moment...
OMG, thank you so much. I lost my LCD and waiting for new one. But I think we should only update the LCD whenever data has been changed. I made a demo that using LCD:
In this demo i only update LCD when I got request and it works well. Could you please try this.
Best reards,
Strange, I've made some test with LCD+OLED on the same I2C network, base on my previous code and it is working!!!!
I've to investigate now to understand where is the issue!
I let you inform
Look like EMC perturbation...
perturbation => disturbance

I think it needs a small delay instead of updating continuously in loop like LED 7segs.

ANW thanks friend.

Shurik said…
I had problems with this script as written, getting errors "A fatal error occurred: Timed out waiting for packet content". Some digging showed that the cause is using PIN 12 for I2C: pin 12 is one of the strapping pins, so if this pin is pulled high at boot time, it interferes with booting. Since I2C pins are normally pulled high, it is a problem.

Changing the script to use another pin as SDA (I used 27) fixed the issue.
Rafal Magda said…
Hi. Great blog.
Just a one concern:
Does not the 5V from VCC kill the ESP32 by SCL and SCA lines? Should not the voltage converters be used on SCL and SCA lines?
I am still a noob. If it is safe please explain why.