This is a comprehensive guide for Arduino LCD 20×4 I2C Interfacing. You’ll learn how to use LCD 2004 I2C With Arduino and create some example projects to practice what we’ll be learning in this tutorial. We’ll start by explaining how the I2C LCD module works, its pinout, and how to connect LCD 20×4 I2C With Arduino. We’ll display some text and numbers on the I2C 20×4 LCD screen, and also create some custom characters and emojis. Without further ado, let’s get right into it!
Table of Contents
- Arduino LCD 20×4 I2C
- Arduino LCD 20×4 I2C Interfacing
- Arduino I2C LCD Library Installation
- Arduino LCD 20×4 I2C Example
- Arduino LCD 20×4 I2C Custom Characters Example
- Arduino Multiple I2C LCD Displays
- Arduino LiquidCrystal_I2C Library Useful Function
- Arduino I2C LCD 20×4 Issues Troubleshooting
- Wrap Up
Arduino LCD 20×4 I2C
LCD (Liquid Crystal Display) is typically used in embedded systems to display text and numbers for the end user as an output device. The 20×4 alphanumeric display is based on the Hitachi HD44780 driver IC. Which is the small black circular chip on the back of the LCD module itself. This is the controller that controls the LCD unit and we communicate with using Arduino to send commands and text messages.
Alphanumeric LCD 20×4 (2004)
The LCD module consists of 20×4 character cells (4 rows x 20 columns), each cell of which is 5×8 dots. Controlling all of these individual dots is a tedious task for our Arduino. However, it doesn’t have to do so. There is a specific function controller on the LCD itself controlling the display while reading the user’s commands & data (the Hitachi HD44780 controller).
Buy a 20×4 I2C LCD Display: you can find it here on Amazon.com
The tutorial linked below is highly recommended to learn more about Arduino LCD interfacing & LiquidCrystal Library (Without I2C).
This article will provide you with more in-depth information about Arduino LCD interfacing, how LCDs work, how to use the LiquidCrystal library functions, create custom LCD characters & emojis, and more. It’s highly recommended!
I2C LCD Module (PCF8574 IO Expander)
The I2C IO expander IC (PCF8574) is commonly used as a cheap solution to implement an I2C LCD interface that replaces the classic parallel connection to the LCDs (at least 6 pins) with an easy-to-use I2C bus (only 2 pins). Using it saves a lot of Arduino IO pins that would have been consumed to create a generic LCD parallel communication.
Moreover, it’s shared with all I2C devices on the bus, so you can still have so many other modules/sensors connected on the same bus. In this previous article, I’ve demonstrated everything about this IC using its datasheet and built a driver library (in Embedded-C) for this IC. You can check it out if you’re interested in learning more about it.
In this tutorial, we’ll only discuss the important things that you need to know in order to get started with LCD 20×4 I2C interfacing with Arduino.
I2C LCD Address (Default)
You need to refer to the PCF8574 chip manufacturer’s datasheet to make sure what’s the I2C device address for the chip you’ve got around. If you’ve got an I2C LCD with Ti (Texas Instruments) PCF8574 chip, the default I2C address is 0x27 which is the 7-bit device address with the three least significant bits (A0 – A1 – A2) pulled up to HIGH.
If you’ve got an I2C LCD with an NXP PCF8574 chip, the default I2C address is therefore 0x3F. The question is what if we’d like to add multiple I2C LCDs to the I2C bus and control them with Arduino, how is that possible?
First of all, any I2C device on the bus must have a unique address. And therefore, we need to change the I2C address of the PCF8574 module if we’d like to add multiple units of it on the same I2C bus. Let’s next see how to do it!
Change I2C LCD Address
The I2C LCD interface (PCF8574) has 3 solder pads on the module’s board which control the value of the last 3 digits in the 7-bit address of the device (A0 – A1 – A2). The pins are by default (internally) pulled up to HIGH (1), but if we short the solder pads together, this will drive the corresponding address bit pin to LOW (0). And that’s how we can change the device address.
You can use the interactive tool below to check the I2C LCD device address after soldering any of the solder pads (A0, A1, or A2). There are 8 different combinations, which means we can connect up to 8 different I2C LCDs on the same bus with a single Arduino board and control all of them at the same time.
I2C LCD Address (Texas Instruments’ PCF8574)
I2C LCD Address (NXP’s PCF8574)
The I2C LCD module has a default I2C device address of either 0x27 or 0x3F depending on the hardware manufacturer. If you’re not quite sure about the device address, you can use this Arduino I2C Scanner application to detect the exact device address.
Using the solder pads on the PCD8574 module will enable you to set the low 3 address bits (A0-A1-A2). This means we can have up to 8 different address combinations and consequently be able to connect up to 8 I2C LCD units on the same I2C bus and control them with only one Arduino.
Arduino LCD 20×4 I2C Interfacing
Now, let’s move to interfacing the I2C LCD 20×4 display with Arduino. Let’s check the pinout, wiring diagram, LCD contrast control, and the I2C LCD device address.
LCD 2004 I2C Pinout
The I2C LCD Display has only four pins. The pinout is shown below:
SCL is the serial clock line for the I2C LCD interface.
SDA is the serial data line for the I2C LCD interface.
VCC is the LCD’s power supply input pin (connects to +5v).
GND is the ground pin.
Wiring LCD 20×4 I2C With Arduino
Here is the wiring diagram for the LCD 20×4 I2C display with Arduino that we’ll be using in the examples hereafter in this tutorial.
And here is a summary table for different Arduino Boards -> I2C LCD connections.
SDA | SCL | |
Arduino UNO, Nano, Pro Mini | A4 | A5 |
Arduino Mega | 20 | 21 |
Arduino Leonardo, Micro | 2 | 3 |
Contrast Adjustment For I2C LCD
After connecting the I2C LCD module, you’ll be able to control the LCD contrast by using the PCF8574 module’s on-board potentiometer. Turn it to the right and to the left until you feel satisfied with the current contrast level.
Get The I2C LCD Address
If you’re not quite sure about the I2C LCD’s device address, you can use the code example below and run it on your Arduino board after connecting the I2C LCD display to your Arduino. It’ll automatically detect and print out the I2C device address for the LCD over the serial monitor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
/* * LAB Name: Arduino I2C Scanner * Author: Khaled Magdy * For More Info Visit: www.DeepBlueMbedded.com */ #include <Wire.h> void setup() { Serial.begin(9600); Wire.begin(); } 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\n"); } else { Serial.println("done\n"); } delay(5000); // wait 5 seconds for next scan } |
And here is the result after running this code example on my Arduino UNO board.
This article will give you more in-depth information about the Arduino I2C Scanner application and how to use it to detect various I2C devices’ addresses.
Arduino I2C LCD Library Installation
You can download and install the Arduino I2C LCD library manually from GitHub, or alternatively, install it within the Arduino IDE itself. Just open the library manager. Tools > Manage Libraries > Search for LiquidCrystal I2C. Then, click Install and let it finish the installation.
Now, you can easily use the Arduino LiquidCrystal I2C LCD library and check the built-in examples for the library to help you get started.
We’ll move now to the practical code examples to test the Arduino I2C LCD display.
Arduino LCD 20×4 I2C Example
Now, let’s test what we’ve learned so far about the Arduino LCD 20×4 I2C and create our first project to display text messages on 4 separate lines of the LCD using the .print() function.
The .print() function can also accept a lot of variable data types (strings, integers, float, double, etc). So luckily, we won’t need to do string manipulations and data type conversion.
Wiring
This wiring of Arduino with LCD 20×4 I2C is the same as shown before in the previous section.
Example Code
Here is the full code listing for this example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/* * LAB Name: Arduino I2C LCD 20x4 Example * Author: Khaled Magdy * For More Info Visit: www.DeepBlueMbedded.com */ #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C MyLCD(0x27, 20, 4); // Creates I2C LCD Object With (Address=0x27, Cols=20, Rows=4) void setup() { MyLCD.init(); MyLCD.backlight(); MyLCD.setCursor(0, 0); MyLCD.print(" Hello World!"); MyLCD.setCursor(0, 1); MyLCD.print(" I2C LCD 20x4"); MyLCD.setCursor(0, 2); MyLCD.print(" Arduino Tutorial"); MyLCD.setCursor(0, 3); MyLCD.print(" DeepBlueMbedded"); } void loop() { } |
Code Explanation
First of all, we need to include the Arduino Wire.h library to use the I2C communication module, then the LiquidCrystal_I2C.h library which we’ll be using to control the I2C LCD module (PCF8574).
1 2 |
#include <Wire.h> #include <LiquidCrystal.h> |
Next, we’ll create an object of the LiquidCrystal_I2C class and define its parameters. The parameters for the LiquidCrystal_I2C object are the I2C device address (0x27), the number of LCD columns, and the number of LCD rows. Those are (20, 4) for 20×4 LCDs.
1 |
LiquidCrystal_I2C MyLCD(0x27, 20, 4); // Creates I2C LCD Object With (Address=0x27, Cols=20, Rows=4) |
setup()
in the setup() function, we initialize the I2C LCD object ( MyLCD) using the .init() function. We also activate the LCD’s backlight by using the .backlight() function.
1 2 |
MyLCD.init(); MyLCD.backlight(); |
Then, we set the LCD cursor position to point to the first line, the first character cell. Any write operation to the LCD will start from the current cursor position value, that’s why it’s important to set it manually before attempting any write operation. To make sure that the text will be displayed exactly where we want it to be.
1 |
MyLCD.setCursor(0, 0); // (CharIndex, LineIndex) |
Next, we’ll print the first text message "Hello World!" to the LCD starting from the current cursor position (0, 0). Using the .print() function.
1 |
MyLCD.print(" Hello World!"); |
and we’ll do the same for the other 3 messages to display them on the subsequent lines on the LCD.
loop()
Nothing to be done in the loop() function.
Simulation
Here is the simulation result for this project on the Wokwi simulator.
You can check this simulation project on Wokwi using this link.
Testing Results
Here is the result of testing this project code example on my Arduino UNO board.
Arduino LCD 20×4 I2C Custom Characters Example
In this example project, we’ll create some custom characters (emojis and icons) and send them to the LCD 20×4 I2C display. The LCD module’s onboard controller has an internal CGRAM that can hold up to 8 custom characters at maximum and you’ll learn how to use it in this example project.
The LCD has an internal Character Generator ROM (CGROM). The character generator ROM generates (5×8) dots or (5×10) dot character patterns from 8-bit character codes. It can generate 208 (5×8) dot character patterns and 32 (5×10) dot character patterns. User-defined character patterns are also available by mask-programmed ROM.
Given that the CGROM has 208 character patterns for (5×8) dot displays, this means that not all the 255 ASCII table characters are available by default in the LCD display. You can refer to this tutorial for more information about this. As we’re more interested in the LCD’s internal CGRAM.
In the Character Generator RAM (CGRAM), the user can write new custom character patterns. For (5×8) dots display, eight-character patterns can be written at maximum. Only 8 custom characters seems like a small space but it’s what it’s and we’ll see how to use it in this example project.
Arduino LCD Custom Character Generator
You can use this online LCD Custom Character Generator Tool and it’ll give you the Arduino C-Code for it, which you can easily copy and paste into your project code. And here is how to use it:
Click on the pixels to draw your custom LCD character, you can invert or clear the entire display cell if you want with the buttons below. If you’re satisfied with how your icon/emoji looks, you can copy the code and you’re good to go. Here are some example custom characters generated by this tool.
I used my custom LCD character generator tool to create the above icons/emojis (heart, speaker, smiley face, notification bell, battery level indicator). We’ll display all of those icons in this example project to show you how it’s done in Arduino code.
Wiring
Exactly the same as the previous example projects.
Example Code
Here is the full code listing for this example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
/* * LAB Name: Arduino I2C LCD 20x4 Custom Characters Display * Author: Khaled Magdy * For More Info Visit: www.DeepBlueMbedded.com */ #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C MyLCD(0x27, 20, 4); // Creates I2C LCD Object With (Address=0x27, Cols=20, Rows=4) // LCD Custom Characters uint8_t HeartChar[] = {0x00, 0x00, 0x0a, 0x15, 0x11, 0x0a, 0x04, 0x00}; uint8_t SpeakerChar[] = {0x01, 0x03, 0x07, 0x1f, 0x1f, 0x07, 0x03, 0x01}; uint8_t SmilyFaceChar[] = {0x00, 0x00, 0x0a, 0x00, 0x1f, 0x11, 0x0e, 0x00}; uint8_t BellChar[] = {0x04, 0x0e, 0x0a, 0x0a, 0x0a, 0x1f, 0x00, 0x04}; uint8_t Battery1Char[] = {0x0e, 0x1b, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1f}; uint8_t Battery2Char[] = {0x0e, 0x1b, 0x11, 0x11, 0x11, 0x11, 0x1f, 0x1f}; uint8_t Battery3Char[] = {0x0e, 0x1b, 0x11, 0x11, 0x11, 0x1f, 0x1f, 0x1f}; uint8_t Battery4Char[] = {0x0e, 0x1b, 0x11, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f}; void setup() { MyLCD.init(); MyLCD.backlight(); // Send The Custom Characters To LCD's CGRAM MyLCD.createChar(0, HeartChar); MyLCD.createChar(1, SpeakerChar); MyLCD.createChar(2, SmilyFaceChar); MyLCD.createChar(3, BellChar); MyLCD.createChar(4, Battery1Char); MyLCD.createChar(5, Battery2Char); MyLCD.createChar(6, Battery3Char); MyLCD.createChar(7, Battery4Char); // Clear The LCD Dispaly MyLCD.clear(); MyLCD.print("Custom Characters:"); // Print The Custom Characters MyLCD.setCursor(0, 1); MyLCD.write(byte(0)); MyLCD.setCursor(2, 1); MyLCD.write(byte(1)); MyLCD.setCursor(4, 1); MyLCD.write(byte(2)); MyLCD.setCursor(6, 1); MyLCD.write(byte(3)); MyLCD.setCursor(8, 1); MyLCD.write(byte(4)); MyLCD.setCursor(10, 1); MyLCD.write(byte(5)); MyLCD.setCursor(12, 1); MyLCD.write(byte(6)); MyLCD.setCursor(14, 1); MyLCD.write(byte(7)); } void loop() { // DO NOTHING! } |
Simulation
Here is the simulation result for this project on the Wokwi simulator.
You can check this simulation project on Wokwi using this link.
For more information about custom character generation and display on I2C LCDs with Arduino, it’s highly recommended to check out the tutorial below which has more code examples with step-by-step explanations for each line of code.
This article will provide you with more in-depth information about Arduino LCD 16×2 interfacing, how LCDs work, how to use the LiquidCrystal library functions, create custom LCD characters & emojis, and more. It’s highly recommended!
Arduino Multiple I2C LCD Displays
In this previous example project, we connected multiple I2C LCDs with Arduino and wrote different text messages to each of them. Obviously, we had to change the I2C LCD device address using the solder pads on the PCF8574 module’s board.
The objective was to connect two I2C LCD displays with Arduino UNO board. Therefore, I’ve left one of them with the default address of 0x27 and have changed the other I2C LCD’s address by shorting the A0 solder pads. This changed its address to 0x26 as shown in the figure below.
The same example code and procedure can also be used with the I2C LCD 20×4 display units, you’ll only need to change 2 lines of code which is the initialization function call from (16, 2) to be (20, 4) as shown below, and that’s all.
1 2 |
LiquidCrystal_I2C I2C_LCD1(0x27, 20, 4); LiquidCrystal_I2C I2C_LCD2(0x26, 20, 4); |
There is an interactive tool that I’ve created to help you get the corresponding I2C device address when you short the 3 address control solder pads (A0, A1, A2). You’ll find it near the beginning of this tutorial.
Required Parts List
Here is the full components list for all the parts that you’d need to perform the practical LABs mentioned here in this article and for the whole Arduino Programming series of tutorials found here on DeepBlueMbedded. Please, note that those are affiliate links and we’ll receive a small commission on your purchase at no additional cost to you, and it’d definitely support our work.
Download Attachments
You can download all attachment files for this Article/Tutorial (project files, schematics, code, etc..) using the link below. Please consider supporting my work through the various support options listed in the link down below. Every small donation helps to keep this website up and running and ultimately supports our community.
Arduino LiquidCrystal_I2C Library Useful Function
Here is a summarized list of the most important and useful functions in the Arduino I2C LCD LiquidCrystal_I2C.h library that you’d need to use in your Arduino projects.
LiquidCrystal_I2C Library Function | Function Description | |
lcd.init(); | Initializes the interface to the I2C LCD screen, and specifies the I2C device address, and dimensions (width and height) of the display. the .init() function needs to be called before any other LCD library commands. | |
lcd.backlight(); | Enabled (Activates) the LCD’s backlight LED. | |
lcd.noBacklight(); | Disables (Deactivates) the LCD’s backlight LED. | |
lcd.clear(); | Clears the LCD screen and points the cursor to the home position (in the upper-left corner). | |
lcd.home(); | Sets the cursor to the home position (in the upper-left of the LCD) without clearing the text on the display. | |
lcd.setCursor(col, row); | Position the LCD cursor to a desired location. | |
lcd.noCursor(); | Hides the LCD cursor. | |
lcd.cursor(); | Display the LCD cursor if it has been hidden. | |
lcd.blink(); | Display the blinking LCD cursor. If used in combination with the cursor() function, the result will depend on the particular display device. | |
lcd.noBlink(); | Turns off the blinking LCD cursor. | |
lcd.noDisplay(); | Turns off the LCD display, without losing the text currently shown on it. This can be done to save some power if needed. | |
lcd.display(); | Turns on the LCD display, after it’s been turned off with the noDisplay() function. This will restore the text (and cursor) that was on the display. | |
lcd.scrollDisplayLeft(); |
Scrolls the contents of the display (text and cursor) one space to the left. |
|
lcd.scrollDisplayRight(); |
Scrolls the contents of the display (text and cursor) one space to the right. |
|
lcd.autoscroll(); |
Turns on automatic scrolling of the LCD. This causes each character output to the display to push previous characters over by one space. If the current text direction is left-to-right (the default), the display scrolls to the left; if the current direction is right-to-left, the display scrolls to the right. This has the effect of outputting each new character to the same location on the LCD. |
|
lcd.noAutoscroll(); |
Turns off the automatic scrolling feature of the LCD. |
|
lcd.leftToRight(); |
Set the direction for text written to the LCD to left-to-right, the default. This means that subsequent characters written to the display will go from left to right, but does not affect previously-output text. |
|
lcd.rightToLeft(); |
Set the direction for text written to the LCD to right-to-left (the default is left-to-right). This means that subsequent characters written to the display will go from right to left, but does not affect previously-output text. |
You can also refer to LiquidCrystal_I2C library page for more information and examples of the available APIs (functions) in this library.
Arduino I2C LCD 20×4 Issues Troubleshooting
In this section, we’ll discuss some of the most common Arduino LCD 20×4 I2C interfacing issues and how to troubleshoot those issues if you’re facing any of them in your project.
Garbage Characters Display
If your LCD is displaying garbage characters, it’s a strong indicator that there is an issue with data communication between your Arduino board and the I2C LCD module itself. Make sure your connections are correct and the device address is correct. The Default I2C address is 0x27 for Ti’s PCF8574 and 0x3F for NXP’s PCF8574 modules.
1 |
LiquidCrystal_I2C I2C_LCD1(0x27, 20, 4); |
LCD Showing Black Boxes or Blank Display
This can be a contrast issue at the Vo pin, so make sure you’re connecting the contrast control potentiometer and turn it to the right and left until it’s properly set to an acceptable level.
Contrast is Ok, But Still Blank Display
This can be a power supply issue, make sure you’re connected to a stable +5v power supply. Sometimes powering from a USB port in a computer can cause power issues like this, so try using a power bank or a proper power supply and check if the issue is still persistent.
Using a poor power supply USB port, poor quality USB cables, or even USB extender cables can cause all sorts of power issues. And the LCD display module is sensitive to such issues that you’d not detect during LEDs and buttons example projects.
Wrap Up
To conclude this tutorial, we can say that the Arduino LiquidCrystal_I2C library eases the process of interfacing Arduino with I2C LCD 20×4 display modules. Make sure you’ve learned all the concepts and implemented the practice examples whether on your Arduino board or at least in the simulation environment.
If you’re just starting with Arduino, check out the Arduino Getting Started [Ultimate Guide] here.
And follow this Arduino Series of Tutorials to learn more about Arduino Programming.
This is the ultimate guide for Arduino I2C communication. It’ll give you an in-depth explanation of Arduino I2C fundamentals, wire library functions, Arduino I2C device programming, and a handful of Arduino I2C communication example projects.