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:
- Hook up the 16x2 I2C screen to the SDA and SCL header pins
- Add the DIYables_LCD_I2C library through the Arduino IDE Library Manager
- Print words and numbers at any column and row
- Draw your own icons with the custom character tool
- Scan the I2C bus when the display stays blank

Components You Need
Or you can buy the following kits:
| 1 | × | DIYables Sensor Kit (18 sensors/displays) |
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 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 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.

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

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
Step 2 — Make the LCD object
Step 3 — Start the screen in setup()
Step 4 — Pick a spot
Step 5 — Write the text
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.
Upload and Test
- First time with this board? Set it up with ESP32 Uno - Getting Started before you continue.
- Build the circuit by following the wiring table above.
- Plug in the USB Type-C cable between the ESP32 Uno and your computer.
- Select the board in Arduino IDE: pick ESP32 Dev Module and the COM port of the board (CH340).
- Open the Library Manager with the Libraries icon in the left bar.
- Type the name DIYables LCD I2C into the search field and spot DIYables_LCD_I2C by DIYables.
- Press Install to add it.
- Search for DIYables LCD I2C created by DIYables.io and click the Install button.
- Paste the sketch into an empty Arduino IDE window.
- Hit Upload and wait until the IDE says "Done uploading".
- 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.
- Edit and repeat: put your own name in the code, move it with setCursor(), and upload once more.
- 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) |
Draw your 5 x 8 icon in the tool below. It writes the matching byte array for you:
How the icon looks on the LCD:

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.
The three icons on the screen:

Custom Icons in Four Moves
- Click the dots in the generator until the icon looks right.
- Copy the byte array it shows into your sketch.
- Inside setup(), store the array in a slot from 0 to 7.
- Print the slot anywhere, from setup() or from loop().
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 |
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:
- Contrast: turn the trimmer screw on the backpack slowly until the characters appear.
- 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.
Set the Serial Monitor to 9600 baud. With a DIYables LCD on the bus, you get this:
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.