In-Depth - Interfacing An I2C LCD With Arduino
In-Depth - Interfacing An I2C LCD With Arduino
The solution is to use an I2C LCD display. It only uses two I/O pins that are not even part of
the digital I/O pin set and can be shared with other I2C devices.
Hardware Overview
A typical I2C LCD display consists of an HD44780-based character LCD display and an I2C
LCD adapter. Let’s learn more about them.
If you look closely, you can see tiny rectangles for each character on the screen as well as the
pixels that make up a character. Each of these rectangles is a grid of 5×8 pixels.
Please refer to our in-depth guide for more information about character LCD displays.
The board also includes a tiny trimpot for making precise adjustments to the display’s
contrast.
There is a jumper on the board that provides power to the backlight. To control the intensity
of the backlight, you can remove the jumper and apply external voltage to the header pin
labeled ‘LED’.
For this purpose, the adapter comes with three solder jumpers/pads (A0, A1, and A2). The
address is set when a jumper is shorted with a blob of solder.
An important point to note here is that several companies, including Texas Instruments and
NXP Semiconductors, manufacture the same PCF8574 chip. And the I2C address of your LCD
depends on the chip manufacturer.
Because there are three address inputs that can take on two states, either HIGH or LOW,
eight (2^3) different combinations (addresses) are possible.
All three address inputs are pulled HIGH using onboard pullups. This gives the PCF8574 a
default I2C address of 0x27.
When you short a solder jumper, you pull that address input LOW. If you were to short all
three jumpers, the address would be 0x20. So the range of all possible addresses spans from
0x20 to 0x27.
You can set a different I2C address, according to the table below.
Because there are three address inputs that can take on two states, either HIGH or LOW,
eight (2^3) different combinations (addresses) are possible.
All three address inputs are pulled HIGH using onboard pullups. This gives the PCF8574 a
default I2C address of 0x3F.
When you short a solder jumper, you pull that address input LOW. If you were to short all
three jumpers, the address would be 0x38. So the range of all possible addresses spans from
0x38 to 0x3F.
You can set a different I2C address, according to the table below.
So the I2C address of your LCD is most likely 0x27 or 0x3F. If you’re not sure what
your LCD’s I2C address is, there’s an easy way to figure it out. You’ll learn about
that later in this tutorial.
VCC is the power supply pin. Connect it to the 5V output of the Arduino or an external 5V
power supply.
Begin by connecting the VCC pin to the Arduino’s 5V output and the GND pin to ground.
Now we are left with the pins that are used for I2C communication. Note that each Arduino
board has different I2C pins that must be connected correctly. On Arduino boards with the
R3 layout, the SDA (data line) and SCL (clock line) are on the pin headers close to the AREF
pin. They are also referred to as A5 (SCL) and A4 (SDA).
SCL SDA
Arduino Uno A5 A4
Arduino Nano A5 A4
Arduino Mega 21 20
Leonardo/Micro 3 2
Now, turn on the Arduino. You will see the backlight light up. As you turn the potentiometer
knob, the first row of rectangles will appear. If you have made it this far, Congratulations!
Your LCD is functioning properly.
Library Installation
Before you can proceed, you must install the LiquidCrystal_I2C library. This library allows you
to control I2C displays using functions that are very similar to the LiquidCrystal library.
To install the library, navigate to Sketch > Include Library > Manage Libraries… Wait for the
Library Manager to download the library index and update the list of installed libraries.
Filter your search by entering ‘liquidcrystal‘. Look for the LiquidCrystal I2C library by Frank
de Brabander. Click on that entry and then choose Install.
If you’re not sure what your LCD’s I2C address is, you can run a simple I2C scanner sketch
that scans your I2C bus and returns the address of each I2C device it finds.
You can find this sketch under File > Examples > Wire > i2c_scanner.
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
while (!Serial); // Leonardo: wait for serial monitor
Serial.println("\nI2C Scanner");
}
void loop() {
int nDevices = 0;
Serial.println("Scanning...");
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address < 16) {
Serial print("0");
After you’ve uploaded the sketch, launch the serial monitor at 9600 baud. You should see the
I2C address of your I2C LCD display.
However, before you upload the sketch, you must make a minor change to make it work for
you. You must pass the I2C address of your LCD as well as the display dimensions to the
LiquidCrystal_I2C constructor. If you’re using a 16×2 character LCD, pass 16 and 2; if you’re
using a 20×4 character LCD, pass 20 and 4.
// enter the I2C address and the dimensions of your LCD here
LiquidCrystal_I2C lcd(0x3F, 16, 2);
#include <LiquidCrystal_I2C.h>
void setup() {
lcd.init();
lcd.clear();
lcd.backlight(); // Make sure backlight is on
void loop() {
}
Code Explanation:
The sketch begins by including the LiquidCrystal_I2C library.
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F,16,2);
In the setup, three functions are called. The first function is init() . It initializes the interface
to the LCD. The second function is clear() . This function clears the LCD screen and
positions the cursor in the upper-left corner. The third function, backlight() , turns on the
LCD backlight.
lcd.init();
lcd.clear();
lcd.backlight();
The function setCursor(2, 0) is then called to move the cursor to the third column of the
first row. The cursor position specifies where you want the new text to appear on the LCD. It
is assumed that the upper left corner is col=0 and row=0 .
lcd.setCursor(2,0);
Next, the print() function is used to print “Hello world!” to the LCD.
lcd.print("Hello world!");
Similarly, the next two lines of code move the cursor to the third column of the second row
and print ‘LCD Tutorial’ to the LCD.
lcd.setCursor(2,1);
lcd.print("LCD Tutorial");
lcd.home() function positions the cursor in the upper-left of the LCD without clearing
the display.
lcd.blink() function displays a blinking block of 5×8 pixels at the position to which
the next character will be written.
lcd.cursor() function displays an underscore (line) at the position to which the next
character will be written.
lcd.scrollDisplayLeft() function scrolls the contents of the display one space to the
left. Similar to the above function, use this inside a for loop for continuous scrolling.
lcd.noDisplay() function turns off the LCD display, without losing the text currently
shown on it.
lcd.display() function turns on the LCD display, after it’s been turned off with
noDisplay() . This will restore the text (and cursor) that was on the display.
To use createChar() , you must first create an 8-byte array. Each byte in the array
corresponds to a row in a 5×8 matrix. In a byte, the digits 0 and 1 indicate which pixels in a
row should be OFF and which should be ON.
All Hitachi HD44780 driver-based LCDs have two types of memory: CGROM and
CGRAM (Character Generator ROM and RAM).
CGROM is non-volatile memory that retains data even when the power is
removed, whereas CGRAM is volatile memory that loses data when the power is
removed.
The CGROM stores the font that appears on a character LCD. When you instruct a
character LCD to display the letter ‘A’, it needs to know which pixels to turn on so
that we see an ‘A’. This data is stored in the CGROM.
byte
Character[8] =
{
0b00000,
0b00000,
0b01010,
0b11111,
0b11111,
0b01110,
0b00100,
Clear all 0b00000
};
There’s no limit to what you can create. The only limitation is that the LiquidCrystal_I2C
library only supports eight custom characters. But don’t be sad, look at the bright side; at
least we have eight characters.
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F, 16, 2); // set the LCD address to 0x3F for a 16 char
byte Bell[8] = {
0b00100,
0b01110,
0b01110,
0b01110,
0b11111,
0b00000,
0b00100,
0b00000
}
Code Explanation:
After including the library and creating the LCD object, custom character arrays are defined.
The array consists of 8 bytes, with each byte representing a row in a 5×8 matrix.
This sketch contains eight custom-characters. Take, for example, the Heart[8] array. You
can see that the bits (0s and 1s) are forming the shape of a heart. 0 turns the pixel off, and 1
turns it on.
byte Heart[8] = {
0b00000,
0b01010,
0b11111,
0b11111,
0b01110,
0b00100,
0b00000,
0b00000
};
In the setup, we use the createChar() function to create a custom character. This function
accepts two parameters: a number between 0 and 7 to reserve one of the eight supported
custom characters, and the name of the array.
lcd.createChar(0, Heart);
In the loop, to display the custom character, we simply call the write() function and pass it
the number of the character we reserved earlier.
lcd.setCursor(0, 1);
lcd.write(0);
SHARE