ESP32 UNO - LCD I2C

Need a small screen for your ESP32 Uno LCD I2C build? A 16x2 LCD with an I2C backpack plugs into the ESP32 Uno form board with only four jumpers. Two lines of 16 characters are enough for a status message, a counter or a sensor reading, and the DIYables library prints them with a single lcd.print() call.

What you will do:

  1. Hook up the 16x2 I2C screen to the SDA and SCL header pins
  2. Add the DIYables_LCD_I2C library through the Arduino IDE Library Manager
  3. Print words and numbers at any column and row
  4. Draw your own icons with the custom character tool
  5. Scan the I2C bus when the display stays blank
ESP32 Uno LCD I2C

Components You Need

1×ESP32 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×Logic Level Shifter 5V/3.3V (Optional, for the two I2C lines)
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.

The LCD I2C 16x2 Module

After this part, you know what each pin of the module does and how the screen counts its cells.

Feature Value
Screen size 16 columns x 2 rows (32 characters)
Interface I2C, through a backpack board soldered on the back
Common I2C address 0x27 (some makers use 0x3F)
Supply 5V
Contrast Small trimmer screw on the backpack

Pinout

Only two of the four pins carry data. The other two feed the screen and its backlight.

Module Pin Role
GND Ground, 0V
VCC Supply, 5V
SDA Serial data for the I2C bus
SCL Serial clock for the I2C bus
LCD I2C Pinout

LCD Coordinate

Every cell on the screen has a column number and a row number, and both counts begin at 0. Column 0 to 15 runs left to right. Row 0 is the upper line and row 1 is the lower line.

ESP32 Uno LCD I2C Coordinate

ESP32 Uno Pinout

The text printed next to each header (D2, SDA, SCL) is not what your sketch uses; the sketch uses the GPIO number. Look up the SDA and SCL pins on this picture before you plug in the jumpers.

ESP32 Uno pinout diagram

How to Connect

When this step is done, the screen gets 5V and sits on the I2C header of the board.

The wiring diagram between ESP32 Uno LCD I2C

This image is created using Fritzing. Click to enlarge image

※ NOTE THAT:

The wiring diagram may not show the correct wiring for the ESP32 Uno. The LCD backpack pulls SDA and SCL up to 5V, but ESP32 Uno pins are 3.3V only. The picture shows the LCD wired straight to SDA and SCL. We suggest a level converter between the LCD and the board, as the hardware list shows. We will update the diagram soon. Until then, follow the wiring table and the text on this page, not the picture.

LCD I2C Pin ESP32 Uno Pin
VCC 5V
GND GND
SDA SDA (GPIO21)
SCL SCL (GPIO22)

Good to know: on the ESP32 Uno, A4 (GPIO36) and A5 (GPIO39) are input-only analog pins with no I2C role. The bus lives on the separate SDA and SCL header pins, and Wire.begin() picks GPIO21 and GPIO22 without any extra code.

WARNING

ESP32 Uno pins are NOT 5V tolerant; they expect 3.3V signals. The backpack often ties SDA and SCL to its own 5V supply with pull-up resistors, so the lines can idle at 5V. For a safe long-term build, put a bidirectional 5V/3.3V level shifter on SDA and SCL, or take the pull-up resistors off the backpack.

Programming the LCD I2C

By the end of this part, you know the five calls that every LCD sketch needs.

Call Purpose
#include Loads the LCD library
DIYables_LCD_I2C lcd(0x27, 16, 2) Makes the screen object with its address, columns and rows
lcd.init() Wakes up the controller chip on the backpack
lcd.backlight() Switches the backlight on
lcd.setCursor(col, row) Chooses where the next text starts
lcd.print("text") Writes text or a number at the cursor

Step 1 — Load the library

#include <DIYables_LCD_I2C.h>

Step 2 — Make the LCD object

DIYables_LCD_I2C lcd(0x27, 16, 2); // screen at address 0x27, 16 columns by 2 rows

Step 3 — Start the screen in setup()

lcd.init(); // start the LCD controller lcd.backlight(); // light up the screen

Step 4 — Pick a spot

lcd.setCursor(column_index, row_index);

Step 5 — Write the text

lcd.print("Hello World!");

More tricks, such as icons and a blinking cursor, are in the "Do More with LCD" part further down.

※ NOTE THAT:

Different LCD makers ship different I2C addresses. The sketches here use 0x27, which is the address of the DIYables LCD I2C module.

ESP32 Uno Code

This sketch writes "DIYables" on the upper line and "www.diyables.io" on the lower line, one time at start-up.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-lcd-i2c */ #include <DIYables_LCD_I2C.h> DIYables_LCD_I2C lcd(0x27, 16, 2); // address 0x27, 16 x 2 characters void setup() { lcd.init(); // start the screen lcd.backlight(); lcd.setCursor(3, 0); // column 3 of the upper line lcd.print("DIYables"); // text begins at (3, 0) lcd.setCursor(0, 1); // first cell of the lower line lcd.print("www.diyables.io"); // text begins at (0, 1) } void loop() { }

Upload and Test

  1. First time with this board? Set it up with ESP32 Uno - Getting Started before you continue.
  2. Build the circuit by following the wiring table above.
  3. Plug in the USB Type-C cable between the ESP32 Uno and your computer.
  4. Select the board in Arduino IDE: pick ESP32 Dev Module and the COM port of the board (CH340).
  5. Open the Library Manager with the Libraries icon in the left bar.
  6. Type the name DIYables LCD I2C into the search field and spot DIYables_LCD_I2C by DIYables.
  7. Press Install to add it.
  • 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
ESP32 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
ESP32 Dev Module on COM15
1
  1. Paste the sketch into an empty Arduino IDE window.
  2. Hit Upload and wait until the IDE says "Done uploading".
  3. Look at the screen: the brand name sits on the upper line, a little in from the left, and the web address fills the lower line.
  4. Edit and repeat: put your own name in the code, move it with setCursor(), and upload once more.
  5. Pro tip: if you see a row of solid blocks on the upper line, the screen has power but the sketch is not talking to it. Turn the contrast screw down a little, then check the address with the scanner in the "Troubleshooting on LCD I2C" part.

Video Tutorial

See the ESP32 Uno build in action in the video below.

Code Explanation

After this part, you can change the text and its place without guessing.

Code Part What It Does
DIYables_LCD_I2C lcd(0x27, 16, 2) Links the sketch to the screen at I2C address 0x27
lcd.init() Sends the start-up commands to the backpack chip
lcd.backlight() Turns the light behind the screen on
lcd.setCursor(3, 0) Moves to column 3 on row 0 before printing
lcd.print(...) Writes the text from the cursor to the right
Empty loop() Nothing repeats, so the text stays on the screen

The result is a fixed two-line message that stays until you power off the board.

ESP32 Uno vs UNO R4 for This Project

This table shows what changes when you move this LCD build from the UNO R4 to the ESP32 Uno.

Item Arduino UNO R4 ESP32 Uno
Logic voltage 5V 3.3V
LCD SDA pin SDA header or A4 SDA header only (GPIO21)
LCD SCL pin SCL header or A5 SCL header only (GPIO22)
I2C pull-ups to 5V Safe Level shifter suggested
Library DIYables_LCD_I2C Same library, same code
Arduino IDE board Arduino UNO R4 WiFi or Minima ESP32 Dev Module

The sketch runs as-is with no edits. The only point to watch is the 5V pull-up on the backpack, since the ESP32 Uno pins expect 3.3V.

Do More with LCD

By the end of this part, you can show your own symbols and control the cursor.

Custom Character

lcd.print() only knows the ASCII set. A heart, a bell or a small arrow has to be drawn by you as a custom character.

Fact Value
Character cells 32 (16 per row)
Dots per cell 40 (5 wide x 8 tall)
Custom slots 8 (index 0 to 7)
ESP32 Uno LCD 16x2 Pixel

Draw your 5 x 8 icon in the tool below. It writes the matching byte array for you:

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> // screen at address 0x27 with 16 columns and 2 rows DIYables_LCD_I2C lcd(0x27, 16, 2); // dot pattern of the icon, one byte per row of the cell byte customChar[8] = { 0b00000, 0b01010, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000, 0b00000 }; void setup() { lcd.init(); // start the screen lcd.backlight(); // light it up lcd.createChar(0, customChar); // save the icon in slot 0 lcd.setCursor(2, 0); // column 2 on the upper line lcd.write((byte)0); // draw the icon from slot 0 } void loop(){ }

How the icon looks on the LCD:

LCD custom character

Multiple custom characters

All eight slots (0 to 7) can hold an icon at the same time. This sketch fills three slots with a heart, an up arrow and a down arrow, and prints them with one free cell between each.

#include <DIYables_LCD_I2C.h> // LCD object for address 0x27, 16 x 2 characters DIYables_LCD_I2C lcd(0x27, 16, 2); // slot 0 pattern: heart byte customChar0[8] = { 0b00000, 0b01010, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000, 0b00000 }; // slot 1 pattern: arrow up byte customChar1[8] = { 0b00100, 0b01110, 0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100 }; // slot 2 pattern: arrow down byte customChar2[8] = { 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b11111, 0b01110, 0b00100 }; void setup() { lcd.init(); // start the screen lcd.backlight(); // light it up // store the heart in slot 0 lcd.createChar(0, customChar0); // store the up arrow in slot 1 lcd.createChar(1, customChar1); // store the down arrow in slot 2 lcd.createChar(2, customChar2); // go to column 2, upper line lcd.setCursor(2, 0); // print the slot 0 icon lcd.write((byte)0); // go to column 4, upper line lcd.setCursor(4, 0); // print the slot 1 icon lcd.write((byte)1); // go to column 6, upper line lcd.setCursor(6, 0); // print the slot 2 icon lcd.write((byte)2); } void loop() { }

The three icons on the screen:

LCD multiple custom characters

Custom Icons in Four Moves

  1. Click the dots in the generator until the icon looks right.
  2. Copy the byte array it shows into your sketch.
byte customChar[8] = { 0b00000, 0b01010, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000, 0b00000 };
  1. Inside setup(), store the array in a slot from 0 to 7.
lcd.createChar(index, customChar);
  1. Print the slot anywhere, from setup() or from loop().
lcd.setCursor(column, row); // go to this column and row lcd.write((byte)index); // draw the icon saved in this slot

Other functions

Test each call below on its own. Place it in loop() with a delay(5000) after it, so you have time to watch what it does.

Function Effect on the Screen
lcd.clear() Wipes every cell and sends the cursor to (0, 0)
lcd.home() Sends the cursor to (0, 0) but keeps the text
lcd.setCursor(column, row) Jumps the cursor to that cell
lcd.cursor() Shows an underscore cursor
lcd.noCursor() Hides the underscore cursor
lcd.blink() Makes the cursor cell flash
lcd.noBlink() Stops the flashing
lcd.clear();
lcd.home();
lcd.setCursor(column, row);
lcd.cursor();
lcd.noCursor();
lcd.blink()
lcd.noBlink()

The full list of calls is in the LiquidCrystal Library Reference.

Troubleshooting on LCD I2C

By the end of this part, you know the real I2C address of your screen.

A blank LCD nearly always comes down to two things:

  1. Contrast: turn the trimmer screw on the backpack slowly until the characters appear.
  2. Address: most modules answer at 0x27 or 0x3F. Try both in the code. If neither one works, upload the scanner below; it lists every device on the I2C bus.
// scan the I2C bus and list every address that answers #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); // pause 5 s, then scan again }

Set the Serial Monitor to 9600 baud. With a DIYables LCD on the bus, you get this:

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

Good to know: the scanner uses GPIO21 and GPIO22 too, so "No I2C devices found" points to the wiring, not to the LCD code.

Go Wireless: Send Messages to the LCD From Your Phone

After this part, you have an idea for changing the screen text without a USB cable.

The ESP32 Uno has WiFi and Bluetooth (Classic and BLE) on board. Type a short message in a chat app on your phone, and the sketch prints it on the 16x2 screen. It works well as a note board on a door or a desk. You can also mirror the LCD text to a web monitor page to check it from another room.

Troubleshooting

Problem Possible Cause Solution
COM port does not show up CH340 driver missing or a charge-only cable Install the CH340 driver and use a USB cable that carries data
Upload ends with "Failed to connect" or "Waiting for download" Board did not enter download mode Hold the BOOT button when the upload starts, then let go
Screen stays dark with no backlight No 5V on VCC or GND not shared Wire VCC to 5V and GND to GND, and press the jumpers in fully
LCD wired to A4 and A5 but nothing works A4 and A5 are not I2C on this board Move SDA to GPIO21 and SCL to GPIO22 on the I2C header
One line of full blocks and no text lcd.init() not run or wrong address in the sketch Keep lcd.init() in setup() and use the address the scanner reports
Text shows up but is very pale Contrast set too low or LCD fed from 3.3V Turn the trimmer screw and power the LCD from 5V
Garbled letters after a few minutes Long or loose I2C jumpers Use short jumpers and keep SDA and SCL away from motor wires
Old digits stay behind a new number Shorter text printed over longer text Print a few spaces after the number or call lcd.clear() first
Error "DIYables_LCD_I2C.h No such file" Library not added Install DIYables_LCD_I2C from the Library Manager

FAQ

What is the default I2C address of a 16x2 LCD I2C?

Most backpacks with the PCF8574 chip use 0x27. Versions with the PCF8574A chip use 0x3F. The DIYables module uses 0x27, and the scanner sketch shows the real value in a few seconds.

Which header pins carry I2C on the ESP32 Uno?

The pins marked SDA (GPIO21) and SCL (GPIO22). They sit at the end of the digital header, next to the AREF position. You do not pass pin numbers to the library, because Wire.begin() uses these two by default.

Why can't I wire the LCD to A4 and A5 on the ESP32 Uno like on an UNO board?

On the UNO R4, A4 and A5 are joined to the I2C bus. On the ESP32 Uno they map to GPIO36 and GPIO39, which are input-only analog pins with no I2C hardware, so the screen belongs on the SDA and SCL header pins.

Is it safe to run a 5V LCD I2C with the 3.3V ESP32 Uno?

The screen itself needs 5V to look sharp. The risk is the backpack pull-up resistors, which lift SDA and SCL to 5V. A bidirectional level shifter, or removing those resistors, keeps the ESP32 Uno pins at 3.3V.

Can the same sketch drive a 20x4 LCD I2C?

Yes. Change the object to DIYables_LCD_I2C lcd(0x27, 20, 4); and use rows 0 to 3 and columns 0 to 19 in setCursor(). The wiring stays the same.

How do I print a float or an integer on the screen?

Pass the variable straight to lcd.print(), for example lcd.print(count); or lcd.print(voltage, 2); for two decimals. The library turns it into text for you.

How many custom icons can one LCD hold?

Eight at a time, in slots 0 to 7. If you call createChar() on a slot that is in use, the new pattern replaces the old one, and every place that shows that slot changes too.

Do I need to change the UNO R4 LCD code for the ESP32 Uno?

No. The DIYables_LCD_I2C library runs on top of the Wire library, which the ESP32 Uno core supports. Only the wiring changes, from A4/A5 to the SDA/SCL header pins.

Can I put several I2C modules on the same two pins?

Yes. An OLED, an RTC or a second LCD can share GPIO21 and GPIO22, as long as each device has its own address. Set a different address on a second LCD with the A0, A1 and A2 solder pads on its backpack.

Can the ESP32 Uno show web or phone messages on this LCD?

Yes. The board has WiFi and Bluetooth built in, so a sketch can receive text from a web page or a phone app and pass it to lcd.print(). See the "Go Wireless" part above for matching tutorials.

What if my LCD project runs out of Uno header pins?

The ESP32 Uno has a few extra holes near the power header. GPIO32 and GPIO33 offer I/O, PWM and analog on ADC1, so they can still read analog values with WiFi on. GPIO15 offers I/O, PWM and analog on ADC2. Solder pin headers there and write the GPIO number in code. The LCD only takes the I2C pins, so GPIO32 or GPIO33 is a good place for a button or a sensor whose value you want on the screen. Two warnings: GPIO15 and GPIO0 (the IO0 pin at the unused Uno NC spot) are boot strapping pins, and a wrong connection can stop the board from booting or uploading code, so use GPIO32/33 first. Never use the six holes under the digital header (SD0 to SD3, CMD, CLK = GPIO6 to GPIO11), because they connect to the flash chip and using them crashes the board.

※ 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!