ESP32 S3 UNO - LCD I2C

This ESP32 S3 Uno LCD I2C tutorial shows you how to connect a 16x2 LCD I2C display to the ESP32 S3 Uno form board and print text on it with Arduino code. Thanks to the I2C interface, the LCD 16x2 needs only four wires. You will see your own messages, numbers and even small icons on the screen.

In this tutorial, you will:

  1. Wire a 16x2 LCD I2C display to the SDA and SCL pins
  2. Install the DIYables_LCD_I2C library in the Arduino IDE
  3. Display text and numbers on the LCD I2C
  4. Create and show custom characters on the LCD display
  5. Find the I2C address with an I2C scanner sketch
ESP32 S3 Uno LCD I2C

Hardware Preparation

1×ESP32 S3 Uno-form Board
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×LCD I2C Display 3.3V
1×Alternatively, LCD I2C 20x4
1×5V to 3.3V Level Converter (Optional, for SDA and SCL)
1×Jumper Wires

Or you can buy the following kits:

1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

Buy Note: Most LCD I2C displays available on the market are 5V displays. They may work with this board, but it is unsafe and can damage the board. We strongly recommend using the 3.3V LCD I2C display recommended above.

Overview of LCD I2C 16x2

A plain 16x2 LCD needs many wires. The I2C version has a small backpack board on the back that cuts this down to just two signal lines. This makes it one of the easiest displays to add to an ESP32 S3 Uno project.

The screen has 16 columns and 2 rows, so it can show up to 32 characters at once. A small potentiometer on the backpack lets you set the contrast.

Pinout

The LCD I2C module has 4 pins. Two of them give power, and two carry the I2C signals.

Pin Function
GND Ground (0V)
VCC Power supply (5V)
SDA I2C data line
SCL I2C clock line
LCD I2C Pinout

LCD Coordinate

To print in the right place, you need to know how the screen positions are numbered. The LCD 16x2 has 16 columns and 2 rows, and both the column and the row numbers start at 0. So the top-left cell is (0, 0) and the bottom-right cell is (15, 1).

ESP32 S3 Uno LCD I2C coordinate

ESP32 S3 Uno Pinout

The image below shows the pinout diagram of the ESP32 S3 Uno form board. Use it to find the SDA and SCL header pins and their GPIO numbers when you wire the LCD.

ESP32 S3 Uno pinout diagram

Wiring Diagram

The LCD takes power from the 5V pin and talks to the board over I2C. On the ESP32 S3 Uno, connect SDA and SCL to the SDA and SCL header pins, not to A4 and A5.

The wiring diagram between ESP32 S3 Uno LCD I2C

This image is created using Fritzing. Click to enlarge image

LCD I2C Pin ESP32 S3 Uno Pin
VCC 5V
GND GND
SDA SDA (GPIO8)
SCL SCL (GPIO9)

※ NOTE THAT:

On this board, A4 and A5 are only analog pins, not I2C pins. The I2C bus is on the SDA (GPIO8) and SCL (GPIO9) header pins, and Wire.begin() uses them by default.

WARNING

The ESP32 S3 Uno pins work at 3.3V and are NOT 5V tolerant. Many LCD I2C backpacks pull SDA and SCL up to their own VCC, so with 5V power these lines can sit at 5V. To keep the board safe, pass SDA and SCL through a bidirectional 5V to 3.3V level converter, or remove the pull-up resistors on the backpack.

How To Program For LCD I2C

The DIYables_LCD_I2C library hides all the low-level I2C work. You only need a few short commands to put text on the screen. Here are the basic steps.

Include the library

#include <DIYables_LCD_I2C.h>

Create the LCD object

Give it the I2C address, the number of columns and the number of rows.

DIYables_LCD_I2C lcd(0x27, 16, 2); // Initialize the LCD with I2C address 0x27 for a 16x2 display

Start the LCD

Call these two lines in setup() to start the display and turn on its backlight.

lcd.init(); // Initialize the LCD display lcd.backlight(); // Turn on the backlight of the LCD

Move the cursor

Pick the column and row where the next text should start.

lcd.setCursor(column_index, row_index);

Print a message

lcd.print("Hello World!");

The LCD can do much more than this. See the "Do More with LCD" section below.

※ NOTE THAT:

The I2C address of the LCD can be different between makers. This code uses 0x27, the address given by the manufacturer DIYables.

ESP32 S3 Uno Code

This sketch starts the LCD, turns on the backlight and prints two lines of text. "DIYables" appears on the first row from column 3, and "www.diyables.io" appears on the second row. All the work happens in setup(), so loop() stays empty.

/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/esp32-s3-uno-lcd-i2c */ #include <DIYables_LCD_I2C.h> DIYables_LCD_I2C lcd(0x27, 16, 2); // I2C address 0x27, 16 column and 2 rows void setup() { lcd.init(); // initialize the lcd lcd.backlight(); lcd.setCursor(3, 0); // move cursor to (3, 0) lcd.print("DIYables"); // print message at (3, 0) lcd.setCursor(0, 1); // move cursor to (0, 1) lcd.print("www.diyables.io"); // print message at (0, 1) } void loop() { }

Detailed Instructions

  1. New to the ESP32 S3 Uno? Follow ESP32 S3 Uno - Getting Started first.
  2. Wire it up as shown in the diagram, with SDA and SCL on the SDA and SCL header pins.
  3. Connect the board to your computer with a USB Type-C cable.
  4. Open Arduino IDE, choose the ESP32S3 Dev Module board and the correct COM port.
  5. Open the Library Manager by clicking the Libraries icon on the left side of the Arduino IDE.
  6. Search for DIYables LCD I2C and find the DIYables_LCD_I2C library by DIYables.
  7. Install the library by clicking the Install button.
  • Search for DIYables LCD I2C created by DIYables.io and click the Install button.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Library Manager
Type:
All
Topic:
All
DIYables LCD I2C by DIYables.io
This library is designed for HD44780-based I2C LCD displays. It provides LiquidCrystal-compatible API plus helper functions (text alignment, progress bars, predefined custom characters). Supports multiple I2C buses (Wire, Wire1, Wire2) for advanced boards like Arduino Giga, Due, and ESP32. Compatible with all Arduino-based platforms including Arduino Uno, Mega, Nano, ESP32, ESP8266, STM32, and Raspberry Pi Pico. More info
1.0.0
INSTALL
Newbiely.ino
···
1 void setup() {
Output
Serial Monitor
Ln 1, Col 1
ESP32S3 Dev Module on COM15
1
  1. Copy the code above and paste it into the Arduino IDE.
  2. Upload the code by clicking the Upload button.
  3. Check the LCD. You should see the two lines of text on the screen.
  4. Tip: change the text and the cursor position in the code, then upload again to see how the layout changes.

Video Tutorial

Watch the video below to see this ESP32 S3 Uno project step by step.

Do More with LCD

Once basic text works, you can draw your own symbols and control the cursor. The parts below show you how.

Custom Character

The lcd.print() function can only show standard ASCII characters. To show a symbol like a heart or a small arrow, you need to design a custom character.

The LCD 16x2 has 32 character cells, 16 on each row. Every cell is a grid of 40 small dots: 8 rows and 5 columns. A custom character tells the LCD which of these dots to turn on.

ESP32 S3 Uno LCD 16x2 pixel

Use the character generator below to design your 40-dot character. Just follow the steps:

Click on each pixel to select/deselect


Copy below custom character code
Replace the customChar[8] in the below code
#include <DIYables_LCD_I2C.h> // Initialize the LCD connected via I2C at address 0x27 with 16 columns and 2 rows DIYables_LCD_I2C lcd(0x27, 16, 2); // Define byte array for creating custom character shape byte customChar[8] = { 0b00000, 0b01010, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000, 0b00000 }; void setup() { lcd.init(); // Initialize LCD screen lcd.backlight(); // Turn on the backlight lcd.createChar(0, customChar); // Register the custom character on LCD with ID 0 lcd.setCursor(2, 0); // Set cursor to start at column 2, row 0 lcd.write((byte)0); // Display the custom character at the current cursor position } void loop(){ }

This is what you see on the screen:

LCD custom character

Multiple custom characters

The LCD can store up to 8 custom characters, with index numbers 0 to 7. The sketch below creates three of them, a heart, an up arrow and a down arrow, and shows them side by side.

#include <DIYables_LCD_I2C.h> // Create an LCD object, using I2C address 0x27 with 16 columns and 2 rows DIYables_LCD_I2C lcd(0x27, 16, 2); // Define custom character 0 (heart shape) byte customChar0[8] = { 0b00000, 0b01010, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000, 0b00000 }; // Define custom character 1 (arrow pointing upwards) byte customChar1[8] = { 0b00100, 0b01110, 0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100 }; // Define custom character 2 (arrow pointing downwards) byte customChar2[8] = { 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b11111, 0b01110, 0b00100 }; void setup() { lcd.init(); // Initialize the LCD display lcd.backlight(); // Turn on the LCD backlight // Register custom character 0 with index 0 on the LCD lcd.createChar(0, customChar0); // Register custom character 1 with index 1 on the LCD lcd.createChar(1, customChar1); // Register custom character 2 with index 2 on the LCD lcd.createChar(2, customChar2); // Set the cursor to column 2, row 0 lcd.setCursor(2, 0); // Display the custom character 0 at the current cursor position lcd.write((byte)0); // Set the cursor to column 4, row 0 lcd.setCursor(4, 0); // Display the custom character 1 at the current cursor position lcd.write((byte)1); // Set the cursor to column 6, row 0 lcd.setCursor(6, 0); // Display the custom character 2 at the current cursor position lcd.write((byte)2); } void loop() { }

The result on the LCD display:

LCD multiple custom characters

Summary: how to use a custom character on the LCD

  1. Design your symbol with the character generator above.
  2. Copy the binary code it gives you into your sketch.
byte customChar[8] = { 0b00000, 0b01010, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000, 0b00000 };
  1. In setup(), register the character under an index from 0 to 7.
lcd.createChar(index, customChar);
  1. Show the character wherever you need it, in setup() or in loop().
lcd.setCursor(column, row); // Set the cursor to the specified column and row lcd.write((byte)index); // Display the custom character stored at 'index' position

Other functions

The library has more handy functions. Try them one at a time: put each one in loop() and add delay(5000) after it, so you have time to see the effect.

Clear the screen

lcd.clear();

Move the cursor to the top-left corner

lcd.home();

Move the cursor to a given column and row

lcd.setCursor(column, row);

Show the cursor

lcd.cursor();

Hide the cursor

lcd.noCursor();

Make the cursor blink

lcd.blink()

Stop the cursor blinking

lcd.noBlink()

You can find more details in the LiquidCrystal Library Reference.

Troubleshooting on LCD I2C

When the LCD stays blank, the cause is almost always the contrast or the I2C address. Check these two things first:

  1. Turn the small potentiometer on the back of the LCD to change the contrast.
  2. Check the I2C address. It depends on the maker and is usually 0x27 or 0x3F. Try each one in the code. If neither works, upload the scanner sketch below to find the real address.
// I2C address scanner program #include <Wire.h> void setup() { Wire.begin(); Serial.begin(9600); Serial.println("I2C Scanner"); } void loop() { byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address < 16) Serial.print("0"); Serial.print(address,HEX); Serial.println(" !"); nDevices++; } else if (error==4) { Serial.print("Unknown error at address 0x"); if (address < 16) Serial.print("0"); Serial.println(address,HEX); } } if (nDevices == 0) Serial.println("No I2C devices found"); else Serial.println("done"); delay(5000); // wait 5 seconds for next scan }

Open the Serial Monitor at 9600 baud. The output looks like this:

∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
Scanning... I2C device found at address 0x3F ! done Scanning... I2C device found at address 0x3F ! done
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2

FAQ

Why is the LCD wired to SDA and SCL and not to A4 and A5?

On the UNO R4, A4 and A5 double as I2C pins, but on the ESP32 S3 Uno they are only analog pins. The I2C bus is on the SDA (GPIO8) and SCL (GPIO9) header pins. Wire.begin() uses these pins by default, so the code needs no pin numbers.

Can I power the LCD I2C from 3.3V on the ESP32 S3 Uno?

Most 16x2 LCD I2C modules are made for 5V. At 3.3V the text is often very faint or not visible at all. Power the LCD from 5V, and use a level converter on SDA and SCL to keep the 5V pull-ups away from the board's 3.3V pins.

Does the DIYables_LCD_I2C library work on the ESP32 S3 Uno?

Yes. The library uses the standard Wire library, which works on the ESP32 S3 Uno. The same code runs on the UNO R4 and on the ESP32 S3 Uno without any change.

How do I show a number or a sensor value on the LCD?

Pass the value to lcd.print(), for example lcd.print(temperature);. Call lcd.clear() or print spaces first, so old digits do not stay on the screen when the number gets shorter.

Can I connect two LCD I2C displays to one ESP32 S3 Uno?

Yes, if they have different I2C addresses. Many backpacks have A0, A1 and A2 solder pads to change the address. Create two DIYables_LCD_I2C objects, one for each address, and wire both LCDs to the same SDA and SCL pins.

What if my project needs more pins than the Uno headers give?

The ESP32 S3 Uno has two extra rows of holes with more GPIOs: GPIO15 and GPIO16 (I/O, PWM, analog), GPIO45 and GPIO35 to GPIO42 (I/O, PWM), and GPIO47/GPIO48 (output only, PWM). Solder pin headers to them and write the GPIO number in code. The LCD only uses the I2C pins, so the extra pins are free for buttons or sensors. Use GPIO15/GPIO16 for analog sensors and GPIO35 to GPIO42 for buttons. Do not use GPIO47/GPIO48 for inputs, because they are output only. Be careful with two things. First, GPIO47/GPIO48 may run at 1.8V instead of 3.3V on some modules (especially ones with "V" in the name, like R8V or R16V), so 3.3V devices can be damaged or act strangely. Second, GPIO0, GPIO3 (D6), GPIO45 and GPIO46 (D9) are boot strapping pins, and a wrong connection can stop the board from booting or uploading code. Use the other extra pins first.

Troubleshooting

Problem Possible Cause Solution
No COM port in Arduino IDE USB driver missing or bad cable Install the CP210x or CH340 driver, try a data USB cable, or hold BOOT while pressing RESET
LCD shows nothing at all LCD not powered or SDA/SCL on A4/A5 Power the LCD from 5V and move SDA and SCL to the SDA (GPIO8) and SCL (GPIO9) header pins
Backlight is on but no text Contrast too low or wrong I2C address Turn the contrast screw on the back and run the I2C scanner to find the address (often 0x27 or 0x3F)
Top row shows only white blocks LCD not started or wrong address in code Make sure lcd.init() runs in setup() and the address in the code matches the scanner result
Scanner prints No I2C devices found SDA and SCL swapped or loose wires Check that SDA goes to SDA (GPIO8) and SCL goes to SCL (GPIO9), and press the wires in firmly
Text is faint or flickers LCD powered from 3.3V or weak USB power Power the LCD from the 5V pin and use a good USB cable
Compile error about DIYables_LCD_I2C.h Library not installed Install the DIYables_LCD_I2C library from the Library Manager
Custom character shows wrong symbol Index used in write() does not match createChar() Use the same index from 0 to 7 in lcd.createChar() and lcd.write((byte)index)

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!