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 Marco Schwartz. Click on that entry and then choose Install.
Troubleshooting on LCD I2C
If the text is not displayed on LCD I2C, please check the following issues:
- Adjust the brightness of LCD by rotating potentiometer in the backside of LCD
- Depending on manufacturers, the I2C address of LCD may be different. Usually, the default I2C address of LCD is 0x27 or 0x3F. Try these values one by one. If you still failed, run the below code to find the I2C address.
The result on Serial Monitor:
Using a Liquid Crystal Display or LCD is a great and cheap way to add monitoring capabilities to your Arduino projects. It’s a very handy way to get feedback on what’s happening with your project. Many appliances and gadgets are using LCDs to communicate with the end-users. It is used in many useful applications such as digital thermometers, cash registers, calculators, and so many more.
Liquid Crystal Display (LCD)
LCD (Liquid Crystal Display) is a type of flat panel display which uses liquid crystals to form characters through a set of instructions or code. The liquid crystals in an LCD produce an image using a backlight.
I2C
I2C (IIC) means inter-integrated communication protocol. This is usually used to communicate between one master and multiple slaves. This setup eliminates the need for having to use many digital pins in the Arduino board. I2C is a serial communication protocol, so data is transferred bit by bit along a single wire (the SDA line).
You will know that your LCD has I2C because you will see pins labeled SDA and SCL. These are the connections going to the Arduino board:
I2C PINS | ARDUINO BOARD |
GND | GND |
VCC | 5V |
SDA | A4 |
SCL | A5 |
If you use the regular LCD display, the total number of connections is 12. If you use an I2C LCD display, you only need just 4.
Install the Necessary Libraries
Libraries are ready-mode codes that you just need to include in your Arduino sketch in order for your LCD to work. The LCD module I’m using is a generic 16×2 LCD with I2C. I have tried to include several libraries but I keep receiving errors. After many trials, I was able to make it work using 2 libraries, the LiquidCrystal_V1.2.1 library, and the Wire library. You can download these files at the bottom of this page.
Here are the steps to install the libraries.
Step 1. First, download the LiquidCrystal_V1.2.1 and Wire zip files.
Step 2. Open your Arduino IDE and from the menu bar, go to Sketch->Include Library->Add .ZIP Library..
Step 3. Browse to where you saved the zip files and select one file at a time, then click Open. In this case, we select the LiquidCrystal_V1.2.1 zip file first.
After clicking Open, the library is now installed.
Step 4. Repeat the same process to include the Wire library.
PROJECT: LCD Hello World
Now that you have installed the libraries, you are now ready to make your first LCD project. For your first project, let’s have the inevitable “Hello World!” project which simply displays the text Hello World! on the LCD.
This is Project 49 of the Arduino Intro app.
Here’s the breadboard circuit:
This is the code:
//SDA-->A4 //SCL-->A5 #include
#include//I2C pins declaration LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); void setup() { lcd.begin(16,2);//Defining 16 columns and 2 rows of lcd display lcd.backlight();//To Power ON the back light } void loop() { lcd.clear();//Clean the screen lcd.setCursor(0,0); //Defining positon to write from first column,first row . lcd.print("Hello"); //You can write 16 Characters per line . delay(1000);//Delay used to give a dynamic effect lcd.setCursor(0,1); //Defining positon to write from first column,second row . lcd.print("World!!!!!!"); delay(2000); }
Here are the important parts of the sketch:
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
The LiquidCrystal_I2C() function sets the pins the Arduino uses to connect to the LCD. You can use any of Arduino’s digital pins to control the LCD. Also, in this function, you need to specify the name of your LCD module, in this case, the name is LCD.
lcd.begin(16,2);
This function sets the dimensions of the LCD. It needs to be placed before any other LiquidCrystal function in the void setup() section of the program.
The number of rows and columns is specified as:
lcd.begin(columns, rows)
For a 16×2 LCD, you would use lcd.begin(16,2)
lcd.backlight();
This function is used to turn on the LCD backlight.
lcd.clear();
This function clears any text or data already displayed on the LCD.
If you use lcd.clear() with lcd.print() and the delay() function in the void loop() section, you can make a simple blinking text program.
lcd.setCursor(0,0);
This function places the cursor (and any printed text) at any position on the screen. It can be used in the void setup() or void loop() section of your program. In this case, the cursor is placed at the first column, first row. Remember that the column is written first before the row. 0 means the first column and the second 0 means the first row.
lcd.print("Hello");
This function is used to print text to the LCD. It can be used in the void setup() sections or the void loop() section of the program. The text enclosed inside the double quotes is exactly what will be displayed.
delay(1000);
This simply waits for 1 second before the next set of text is displayed. The delay here also produces a nice fading effect. This is completely optional.
lcd.setCursor(0,1);
Now the cursor is placed on the first column, second row. It is where the “World!” text will be displayed.
That’s it for the Hello World! LCD project. It is very easy to do, yet, it is a very nice addition to your projects.
Simple I2C LCD Display Project
Before we learn about I2C LCD Display, let’s learn what is LCD to understand better. Liquid Crystal Display (LCD) has many designs which come in different shapes and sizes. But for now, let’s learn the most common LCD which is the 16 x 2 display. You can display up to 32 characters including letters, numbers, or special characters to communicate with this LCD they comes with 16 pin slots on top of the display each slot has its own function to print out the display.
Now let’s move on to i2c enables 2 devices to communicate with each other in a stable, high-speed, bidirectional way and with the least I/O pins. I2C consists of 2 lines to communicate, Serial Data Line (SDA) and Serial Clock Line (SCL), which is also called “bidirectional” protocol.
Steps on connecting i2c LCD with Arduino
Component Required
- Arduino Uno (or any model)
- LCD1602 16×2 backlit LCD display screen
- LCD1602/2004 display I2C backpack module
- Female-to-male jumper wire x 4
The table above shows how to connect the Arduino Uno with LCD I2C Display. After connecting with the respective wires the result should be looks like the diagram below.
Sketch and Compiling
After connecting the wires now let’s do some coding to make the LCD out the characters. to do that we need Arduino IDE software make sure to install and run it. 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.
First, need to print the Serial Monitor code before running on the LCD Display to make sure the display function works out before proceeding to the actual code. you may use the below code as your reference to print out the Serial Monitor.
Serial Monitor (Sample Code)
#include
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…”);
for (byte address = 1; address < 127; ++address) {
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
byte 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
Basic Arduino Sketch – Hello World
The test sketch below will print ‘Hello World!’ on the first line of the LCD and ‘LCD Tutorial’ on the second line
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.
Once you are done, go ahead and try the sketch.
#include
LiquidCrystal_I2C lcd(0x3F,16,2); // set the LCD address to 0x3F for a 16 chars and 2 line display
void setup() {
lcd.init();
lcd.clear();
lcd.backlight(); // Make sure backlight is on
// Print a message on both lines of the LCD.
lcd.setCursor(2,0); //Set cursor to character 2 on line 0
lcd.print(“Hello world!”);
lcd.setCursor(2,1); //Move cursor to character 2 on line 1
lcd.print(“LCD Tutorial”);
void loop() {
This is what you should see on the screen (final result):-
Assigned by :Muhammad Firdaus Badauraudine
#kidocode
#kidocode_internship
#kidocode_electronics_department
In the previous Arduino LCD tutorial, you have noticed that the classic parallel LCD consumes a lot of pins on the Arduino. Even in the 4-bit mode, it requires at least 6 digital I/O pins on the Arduino. So in many projects where you use the classic parallel LCD, you will run out of pins very easily.
The solution to this problem is to use an I2C LCD display. It requires only two digital I/O pins and you can even share those two pins with other I2C devices.
In this tutorial, I will show you how to interface an I2C LCD with Arduino Uno and print some text, numbers, and custom characters.
Table of Contents
- 1 I2C LCD Overview
- 2 I2C LCD Pinout
- 3 Parts required for this Tutorial
- 4 Connect an I2C LCD with Arduino
- 5 Arduino I2C LCD Code
- 6 Find the I2C address of your LCD
- 7 Arduino I2C LCD – Print a Simple Text
- 8 Useful Functions from the Library
- 9 I2C LCD Custom Character
I2C LCD Custom Character
As you see in the above section the character in a Liquid crystal display is built using a grid of 5×8 small pixels. You can individually turn on and off any pixel and make your own custom character. The custom character data is stored in the CGRAM of the display.
CGROM and CGROM
Displays that use the Hitachi HD44780 controller have two types of memories – CGROM and CGRAM (Character Generator ROM & RAM). CGROM is non-volatile means it can’t be modified whereas CGRAM is volatile so it can be modified at any time.
CGROM stored all permanent fonts that can be displayed by using their ASCII code. For example, the character ‘A’ can be written using write(65) or write(0x41).
CGRAM is used for storing user-defined characters. The Hitachi HD44780 controller has a CGROM of 64 bytes. So for a 5×8 pixel-based LCD, up to 8 user-defined characters can be stored in the CGRAM. And for a 5×10 pixel-based LCD, only 4 user-defined characters can be stored.
To print a custom character, you need to make the byte code for that character first. Creating a byte code for a custom character is very simple. You need to go through row-wise and write “1” if you need to turn on a pixel and write “0” if you need to turn off a pixel. Then go to the next row and do the same. Do it for all eight rows.
So for example, If I want to create a custom character for a smiley like below –
When you go through row-wise and follow the above instructions you will get numbers like this –
00000, 10001, 00000, 00000, 10001, 01110, 00000, 00000,
Now simply place a “B” before every row and this would be the byte code for that character.
B00000, B10001, B00000, B00000, B10001, B01110, B00000, B00000,
You can store that data in an array like this –
byte smiley[] = {B00000, B10001, B00000, B00000, B10001, B01110, B00000, B00000}
You can convert the binary value to hexadecimal and use it like this –
byte smiley[] = {0x0, 0x11, 0x0 , 0x0, 0x11, 0xE, 0x0, 0x0}
There are some online and offline tools available to visually draw the custom character and it will automatically generate the byte code for you. I suggest you use the online LCD Character Creator tools.
Now in the below sketch, I will use that bite code to print the custom character on the LCD. Upload the below code to your Arduino board and see what the display looks like.
PS:- Don’t forget to change the I2C address and dimension of the display in the below sketch.
Arduino Code
#include// Set the LCD address and dimension LiquidCrystal_I2C lcd(0x27, 16, 2); // Create a custom character: byte smiley[] = { B00000, B10001, B00000, B00000, B10001, B01110, B00000, B00000, }; void setup() { lcd.init(); lcd.clear(); lcd.backlight(); // Turns on backlight // Create a new characters: lcd.createChar(0, smiley); } void loop() { // Print all the custom characters: lcd.setCursor(0, 0); lcd.write(0); }
Explaining the Code
Here you can see that I include the
LiquidCrystal_I2C
library first. Then I create a
LiquidCrystal_I2C
variable for my LCD using the I2C address and dimension of my LCD. I am using an array
smiley[]
to store the bit data for the custom character.
In the setup section, I create a custom character from that byte array using the
createChar()
function. It needs two parameters – a number between 0 – 7 to reserve a space in the CGRAM for the custom character and the name of the byte array.
Then in the loop section, I use the
write()
function to display custom characters. This function needs the character number as an argument.
In the below example, I will print eight different custom characters on the LCD.
#includeLiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); // Make custom characters: byte smiley[8] = {B00000, B10001, B00000, B00000, B10001, B01110, B00000, B00000}; byte man[8] = {B00000, B01110, B10001, B01110, B00100, B11111, B00100, B11011}; byte music[8] = {B00001, B00011, B00101, B01001, B01001, B01011, B11011, B11000}; byte heart[8] = {B00000, B01010, B11111, B11111, B01110, B00100, B00000, B00000}; byte speaker[8] = {B00001, B00011, B01111, B01111, B01111, B00011, B00001, B00000}; byte bell[8] = {0x00, 0x04, 0x0E, 0x0E, 0x0E, 0x1F, 0x04, 0x00}; byte pie[8] = {0x00, 0x1F, 0x0A, 0x0A, 0x0A, 0x13, 0x00, 0x00}; byte ohm[8] = {0x00, 0x0E, 0x11, 0x11, 0x0A, 0x1B, 0x00, 0x00}; void setup() { lcd.init(); lcd.backlight(); // Create a new characters: lcd.createChar(0, smiley); lcd.createChar(1, man); lcd.createChar(2, music); lcd.createChar(3, heart); lcd.createChar(4, speaker); lcd.createChar(5, bell); lcd.createChar(6, pie); lcd.createChar(7, ohm); // Clears the LCD screen: lcd.clear(); lcd.setCursor(0, 0); lcd.print("Custom Caracter"); } void loop() { // Print all the custom characters: lcd.setCursor(0, 1); lcd.write(byte(0)); lcd.setCursor(2, 1); lcd.write(byte(1)); lcd.setCursor(4, 1); lcd.write(byte(2)); lcd.setCursor(6, 1); lcd.write(byte(3)); lcd.setCursor(8, 1); lcd.write(byte(4)); lcd.setCursor(10, 1); lcd.write(byte(5)); lcd.setCursor(12, 1); lcd.write(byte(6)); lcd.setCursor(14, 1); lcd.write(byte(7)); }
If you’ve ever attempted to connect an LCD display to an Arduino, you’ve probably noticed that it uses a lot of Arduino pins. Even in 4-bit mode, the Arduino requires seven connections – half of the Arduino’s available digital I/O pins.
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.
About LCD I2C 16×2
In the previous tutorial, we had learned how to use the normal LCD. However, wiring between Arduino and the normal LCD is complicated. Therefore, LCD I2C has been created to simplify the wiring. Actually, LCD I2C is composed of a normal LCD, an I2C module and a potentiometer.
Pinout
LCD I2C uses I2C interface, so it has 4 pins:
- GND pin: needs to be connected to GND (0V).
- VCC pin: the power supply for the LCD, needs to be connected to VCC (5V).
- SDA pin: I2C data signal
- SCL pin: I2C clock signal
LCD Coordinate
LCD I2C 16×2 includes 16 columns and 2 rows. the conlums and rows are indexed from 0.
Determining the I2C Address
As previously stated, the I2C address of your LCD depends on the manufacturer. If your LCD has a PCF8574 chip from Texas Instruments, its I2C address is 0x27; if it has a PCF8574 chip from NXP Semiconductors, its I2C address is 0x3F.
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.
Load the i2c_scanner sketch into your Arduino IDE.
#include
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..."); for (byte address = 1; address < 127; ++address) { // The i2c_scanner uses the return value of // the Write.endTransmisstion to see if // a device did acknowledge to the address. Wire.beginTransmission(address); byte 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 }
After you’ve uploaded the sketch, launch the serial monitor at 9600 baud. You should see the I2C address of your I2C LCD display.
Please make a note of this address. You’ll need it in later examples.
I2C LCD Overview
An I2C LCD display consists of a classic parallel LCD and an I2C LCD adapter. The I2C LCD adapter is soldered from the back of the LCD.
If you already have an LCD you can only purchase the I2C LCD adapter and solder it by yourself.
Now let’s have a look at the LCD display and the I2C LCD adapter in detail.
Liquid Crystal Display (LCD)
Commonly available LCD displays with I2C LCD adapters are 16×2 and 20×4 character LCD displays. They both have a total of 16 pins including 8 parallel data pins. So if you don’t use an I2C LCD you will need at least 6 digital I/O pins on the Arduino to display something.
If you look closely you will find that the characters of the LCD are built using a grid of 5×8 pixels. Later in this tutorial, you will learn how to turn on and off any individual pixels to make custom characters.
You can read more about the LCD display in my Arduino LCD tutorial.
I2C LCD Adapter Overview
At the center of this adapter, there is an 8-bit I/O expander chip – PCF8574. It takes the I2C data from the MCU (Arduino) and converts it into serial data required for an LCD display. On one side the I2C LCD adapter has four pins that can be connected to Arduino or any microcontroller that supports the I2C communication protocol. On another side, it has 16 pins that are connected to the LCD display.
There are two header pins to control the backlight of the LCD display. One pin supplies a 5v power and another pin is for the backlight LED. These two pins are connected together by default. So the backlight will be always on. You can remove the jumper to turn off the backlight LED or you can use a potentiometer in between these two pins to control the intensity of the backlight LED.
The I2C LCD adapter also has a small trim pot to adjust the contrast of the display.
How to Change the Default I2C Address
Some I2C LCD adapters come with PCF8574, while others use PCF8574A chips. Each of these chips has its own I2C address. The PCF8574 chip from NXP Semiconductor uses 0x27 while the PCF8574A chip uses 0x3F.
Sometimes you need to change this default I2C address for a project where you use multiple I2C devices on the same I2C bus So that it does not conflict with other I2C devices.
If you use multiple I2C devices on the same I2C bus, sometimes you need to change this default I2C address to avoid conflict with other I2C devices.
To change the default address it has address selection pads – A0, A1, and A2.
Each of these pads has a ground connection above it. By default, each of these pins is connected to VDD or a positive power supply. You can change the address by connecting any pin to the ground pin above it. You can make any possible combination and you will get a different address. From three address pins, you can get 23 different combinations means 8 different addresses.
If you go through the datasheet of PCF8574 by NXP, you will find that PCF8574 and PCF8574A use a 7-bit I2C address. The first four-bit is fixed and the last three-bit is hardware selectable.
As you read above A0, A2, and A3 are connected to the positive power supply by default. So the default address of PCF8574 is 0100111 in binary or 0x27 in hex. If you connect the A0 pin to the ground pin it will become logic LOW. The address will change to 0100110 in binary or 0x26 in hex.
See the illustration below to find all the possible connections and the hex addresses.
In the same way, you can change the address of a PCF8575A chip. But keep in mind that the first four bits of the PCF8575 are 0100 and the first four bits of the PCF8575A are 0111. Calculate the address according to it.
Arduino Code
Quick Steps
- Navigate to the Libraries icon on the left bar of the Arduino IDE.
- Search “LiquidCrystal I2C”, then find the LiquidCrystal_I2C library by Frank de Brabander
- Click Install button to install LiquidCrystal_I2C library.
- Copy the above code and open with Arduino IDE
- Click Upload button on Arduino IDE to upload code to Arduino
- See the result on LCD
- Try modifying text and position
Hardware Required
Arduino UNO or Genuino UNO |
USB 2.0 cable type A/B |
LCD I2C |
Jumper Wires |
(Optional) 9V Power Adapter for Arduino |
(Recommended) Screw Terminal Block Shield for Arduino Uno |
(Optional) Transparent Acrylic Enclosure For Arduino Uno |
Or you can buy the following sensor kit:
DIYables Sensor Kit 30 types, 69 units |
Conclusion
In this article, I have shown you how to use a character I2C LCD with Arduino.
I hope you found it useful and informative. If you did, please share it with a friend that also likes electronics and making things!
I would love to know what projects you plan on building (or have already built) with these LCDs. If you have any questions, suggestions or if you think that things are missing in this tutorial, please leave a comment down below.
Note that comments are held for moderation to prevent spam.
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Basic Arduino Sketch – Hello World
The test sketch below will print ‘Hello World!’ on the first line of the LCD and ‘LCD Tutorial’ on the second.
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);
Once you are done, go ahead and try the sketch.
#includeLiquidCrystal_I2C lcd(0x3F,16,2); // set the LCD address to 0x3F for a 16 chars and 2 line display void setup() { lcd.init(); lcd.clear(); lcd.backlight(); // Make sure backlight is on // Print a message on both lines of the LCD. lcd.setCursor(2,0); //Set cursor to character 2 on line 0 lcd.print("Hello world!"); lcd.setCursor(2,1); //Move cursor to character 2 on line 1 lcd.print("LCD Tutorial"); } void loop() { }
This is what you should see on the screen.
Code Explanation:
The sketch begins by including the LiquidCrystal_I2C library.
#include
The next step is to create an object of LiquidCrystal_I2C class. The LiquidCrystal_I2C constructor accepts three inputs: I2C address, number of columns, and number of rows of the display.
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");
How To Program For LCD I2C
Thanks to the LiquidCrystal_I2C library, the using LCD is a piece of cake.
- Include the library:
- Declare a LiquidCrystal_I2C object with I2C address, the number of columns, the number of rows:
- Initialize the LCD.
- Move cursor to the desired position (column_index, row_index)
- Print a message to the LCD.
There are many things more that we can do with LCD (see Do More with LCD part)
※ NOTE THAT:
The I2C address of LCD can vary according to the manufacturers. In the code, we used 0x27 that is specified by DIYables manufacturer
Parts required for this Tutorial
SL. | Preview | Name | Link |
Arduino Uno R3 | Amazon | Aliexpress | ||
I2C LCD | Amazon | Aliexpress | ||
Jumper Wires | Amazon | Aliexpress |
Adjusting the contrast of the LCD
After you have wired up the LCD, you will need to adjust the contrast of the display. On the I2C module, you will find a potentiometer that you can turn with a small screwdriver.
Plug in the USB connector of the Arduino to power the LCD. You should see the backlight light up. Now rotate the potentiometer until one (16×2 LCD) or 2 rows (20×4 LCD) of rectangles appear. You can tweak the contrast later if needed.
Once that is done, we can start programming the LCD.
Arduino I2C LCD Code
In this tutorial, I am using the LiquidCrystal-I2C library to control the display. It has many pre-built functions to control an I2C LCD display in a much simpler way.
To install the library first you need to download it from the LiquidCrystal-I2C GitHub repository. Then open your Arduino IDE and navigate to Sketch > Include Library > Add .ZIP Library.
Select the .zip file you just downloaded and click the open button. The installation is done, now you can include the library in your code.
Wiring an I2C LCD Display to an Arduino
Connecting an I2C LCD is much simpler than connecting a standard LCD. You only need to connect four pins.
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).
The following table lists the pin connections:
I2C LCD | Arduino |
VCC | 5V |
GND | GND |
SCL | SCL or A5 |
SDA | SDA or A4 |
The diagram below shows how to connect everything.
I2C Address of LCD
If you have multiple devices on the same I2C bus, you may need to set a different I2C address for the LCD adapter to avoid conflicting with another I2C device.
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.
If your LCD has Texas Instruments’ PCF8574 chip:
According to the Texas Instruments’ datasheet, the three address selection bits (A0, A1, and A2) are located at the end of the 7-bit I2C address register.
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.
If your LCD has NXP’s PCF8574 chip:
According to the NXP Semiconductors’ datasheet, the three address selection bits (A0, A1, and A2) are located at the end of the 7-bit I2C address register. However, the remaining bits in the address register are different.
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.
I2C LCD Basics
This guide is part of our hub of articles on Arduino Displays. This type of LCD is ideal for displaying text and numbers, hence the name ‘character LCD’.
The I2C LCD that we are using in this tutorial comes with a small add-on circuit mounted on the back of the module. This module features a PCF8574 chip (for I2C communication) and a potentiometer to adjust the LED backlight.
The advantage of an I2C LCD is that the wiring is very simple. You only need two data pins to control the LCD. Standard LCDs typically require around 12 connections, which can be a problem if you do not have many GPIO pins available.
Luckily, you can also buy the I2C add-on circuit separately on Amazon, so you can easily upgrade a standard LCD as well.
For a tutorial and wiring diagram for standard character LCDs, please see the following article:
If you look closely at the LCD, you can see the small rectangles that form the individual characters of the LCD. Each rectangle is made up of a grid of 5×8 pixels. Later in this tutorial, I will show you how you can control the individual pixels to display custom characters on the LCD.
Other useful functions of the LiquidCrystal_I2C Library
There are many useful functions you can use with LiquidCrystal_I2C Object. Some of them are listed below:
-
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.noBlink()
function turns off the blinking LCD cursor. -
lcd.cursor()
function displays an underscore (line) at the position to which the next character will be written. -
lcd.noCursor()
function hides the LCD cursor. -
lcd.scrollDisplayRight()
function scrolls the contents of the display one space to the right. If you want the text to scroll continuously, you have to use this function inside a
for
loop. -
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.
Recommended articles
- How to use an HC-SR04 Ultrasonic Distance Sensor with Arduino
- How to use DHT11 and DHT22 Sensors with Arduino
- LM35 analog temperature sensor with Arduino tutorial
- TMP36 analog temperature sensor with Arduino tutorial
(I also have an article on How To Control A Character I2C LCD with ESP32 if you want to work with an ESP32 microcontroller instead).
Components and supplies
Tactile Switch, Top Actuated
Male/Female Jumper Wires
I2C 16×2 Arduino LCD Display Module
USB-A to B Cable
Arduino UNO
Jumper wires (generic)
Tools and machines
Breadboard, 170 Pin
Apps and platforms
Arduino IDE
Project description
Code
Code
csharp
The code
1#include
2#include
3#define PIN_BUTTON 2 4#define PIN_AUTOPLAY 1 5#define SPRITE_RUN1 1 //Car sprite 6#define SPRITE_RUN2 2 7#define SPRITE_JUMP 3 8#define SPRITE_JUMP_UPPER ‘.’ 9#define SPRITE_JUMP_LOWER 4 10#define SPRITE_TERRAIN_EMPTY ‘ ‘ 11#define SPRITE_TERRAIN_SOLID 5 12#define SPRITE_TERRAIN_SOLID_RIGHT 6 13#define SPRITE_TERRAIN_SOLID_LEFT 7 14#define CAR_HORIZONTAL_POSITION 1 // Horizontal position of CAR on screen 15#define TERRAIN_WIDTH 16 16#define TERRAIN_EMPTY 0 17#define TERRAIN_LOWER_BLOCK 1 18#define TERRAIN_UPPER_BLOCK 2 19#define CAR_POSITION_OFF 0 // CAR is invisible 20#define CAR_POSITION_RUN_LOWER_1 1 // CAR is running on lower row (pose 1) 21#define CAR_POSITION_RUN_LOWER_2 2 // (pose 2) 22#define CAR_POSITION_JUMP_1 3 // Starting a jump 23#define CAR_POSITION_JUMP_2 4 // Half-way up 24#define CAR_POSITION_JUMP_3 5 // Jump is on upper row 25#define CAR_POSITION_JUMP_4 6 // Jump is on upper row 26#define CAR_POSITION_JUMP_5 7 // Jump is on upper row 27#define CAR_POSITION_JUMP_6 8 // Jump is on upper row 28#define CAR_POSITION_JUMP_7 9 // Half-way down 29#define CAR_POSITION_JUMP_8 10 // About to land 30#define CAR_POSITION_RUN_UPPER_1 11 // CAR is running on upper row (pose 1) 31#define CAR_POSITION_RUN_UPPER_2 12 // (pose 2) 32LiquidCrystal_I2C lcd(0x27, 16, 2); 33static char terrainUpper[TERRAIN_WIDTH + 1]; 34static char terrainLower[TERRAIN_WIDTH + 1]; 35static bool buttonPushed = false; 36void initializeGraphics() { 37 static byte graphics[] = { 38 // Run position 1 39 0b00000, 40 0b00000, 41 0b00100, 42 0b11110, 43 0b11110, 44 0b11111, 45 0b01001, 46 0b00000, 47 // Run position 2 48 0b00000, 49 0b00000, 50 0b00100, 51 0b11110, 52 0b11110, 53 0b11111, 54 0b01001, 55 0b00000, 56 // Jump 57 0b00000, 58 0b00000, 59 0b00100, 60 0b11110, 61 0b11110, 62 0b11111, 63 0b01001, 64 0b00000, 65 // Jump lower 66 0b00000, 67 0b00000, 68 0b00100, 69 0b11110, 70 0b11110, 71 0b11111, 72 0b01001, 73 0b00000, 74 // Ground 75 0b01110, 76 0b11111, 77 0b10101, 78 0b11111, 79 0b10101, 80 0b11111, 81 0b10101, 82 0b11111, 83 // Ground right 84 0b01110, 85 0b11111, 86 0b10101, 87 0b11111, 88 0b10101, 89 0b11111, 90 0b10101, 91 0b11111, 92 // Ground left 93 0b01110, 94 0b11111, 95 0b10101, 96 0b11111, 97 0b10101, 98 0b11111, 99 0b10101, 100 0b11111, 101 }; 102 int i; 103 // Skip using character 0, this allows lcd.print() to be used to 104 // quickly draw multiple characters 105 for (i = 0; i < 7; ++i) { 106 lcd.createChar(i + 1, &graphics[i * 8]); 107 } 108 for (i = 0; i < TERRAIN_WIDTH; ++i) { 109 terrainUpper[i] = SPRITE_TERRAIN_EMPTY; 110 terrainLower[i] = SPRITE_TERRAIN_EMPTY; 111 } 112} 113 114// Slide the terrain to the left in half-character increments 115// 116void advanceTerrain(char* terrain, byte newTerrain) { 117 for (int i = 0; i < TERRAIN_WIDTH; ++i) { 118 char current = terrain[i]; 119 char next = (i == TERRAIN_WIDTH – 1) ? newTerrain : terrain[i + 1]; 120 switch (current) { 121 case SPRITE_TERRAIN_EMPTY: 122 terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY; 123 break; 124 case SPRITE_TERRAIN_SOLID: 125 terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID; 126 break; 127 case SPRITE_TERRAIN_SOLID_RIGHT: 128 terrain[i] = SPRITE_TERRAIN_SOLID; 129 break; 130 case SPRITE_TERRAIN_SOLID_LEFT: 131 terrain[i] = SPRITE_TERRAIN_EMPTY; 132 break; 133 } 134 } 135} 136 137bool drawCAR(byte position, char* terrainUpper, char* terrainLower, unsigned int score) { 138 bool collide = false; 139 char upperSave = terrainUpper[CAR_HORIZONTAL_POSITION]; 140 char lowerSave = terrainLower[CAR_HORIZONTAL_POSITION]; 141 byte upper, lower; 142 switch (position) { 143 case CAR_POSITION_OFF: 144 upper = lower = SPRITE_TERRAIN_EMPTY; 145 break; 146 case CAR_POSITION_RUN_LOWER_1: 147 upper = SPRITE_TERRAIN_EMPTY; 148 lower = SPRITE_RUN1; 149 break; 150 case CAR_POSITION_RUN_LOWER_2: 151 upper = SPRITE_TERRAIN_EMPTY; 152 lower = SPRITE_RUN2; 153 break; 154 case CAR_POSITION_JUMP_1: 155 case CAR_POSITION_JUMP_8: 156 upper = SPRITE_TERRAIN_EMPTY; 157 lower = SPRITE_JUMP; 158 break; 159 case CAR_POSITION_JUMP_2: 160 case CAR_POSITION_JUMP_7: 161 upper = SPRITE_JUMP_UPPER; 162 lower = SPRITE_JUMP_LOWER; 163 break; 164 case CAR_POSITION_JUMP_3: 165 case CAR_POSITION_JUMP_4: 166 case CAR_POSITION_JUMP_5: 167 case CAR_POSITION_JUMP_6: 168 upper = SPRITE_JUMP; 169 lower = SPRITE_TERRAIN_EMPTY; 170 break; 171 case CAR_POSITION_RUN_UPPER_1: 172 upper = SPRITE_RUN1; 173 lower = SPRITE_TERRAIN_EMPTY; 174 break; 175 case CAR_POSITION_RUN_UPPER_2: 176 upper = SPRITE_RUN2; 177 lower = SPRITE_TERRAIN_EMPTY; 178 break; 179 } 180 if (upper != ‘ ‘) { 181 terrainUpper[CAR_HORIZONTAL_POSITION] = upper; 182 collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true; 183 } 184 if (lower != ‘ ‘) { 185 terrainLower[CAR_HORIZONTAL_POSITION] = lower; 186 collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true; 187 } 188 189 byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1; 190 191 // Draw the scene 192 terrainUpper[TERRAIN_WIDTH] = ‘\\0’; 193 terrainLower[TERRAIN_WIDTH] = ‘\\0’; 194 char temp = terrainUpper[16 – digits]; 195 terrainUpper[16 – digits] = ‘\\0’; 196 lcd.setCursor(0, 0); 197 lcd.print(terrainUpper); 198 terrainUpper[16 – digits] = temp; 199 lcd.setCursor(0, 1); 200 lcd.print(terrainLower); 201 202 lcd.setCursor(16 – digits, 0); 203 lcd.print(score); 204 205 terrainUpper[CAR_HORIZONTAL_POSITION] = upperSave; 206 terrainLower[CAR_HORIZONTAL_POSITION] = lowerSave; 207 return collide; 208} 209 210// Handle the button push as an interrupt 211void buttonPush() { 212 buttonPushed = true; 213} 214 215void setup() { 216 pinMode(PIN_BUTTON, INPUT); 217 digitalWrite(PIN_BUTTON, HIGH); 218 pinMode(PIN_AUTOPLAY, OUTPUT); 219 digitalWrite(PIN_AUTOPLAY, HIGH); 220 221 // Digital pin 2 maps to interrupt 0 222 attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING); 223 224 initializeGraphics(); 225 226 lcd.init(); 227 lcd.backlight(); 228} 229 230void loop() { 231 static byte CARPos = CAR_POSITION_RUN_LOWER_1; 232 static byte newTerrainType = TERRAIN_EMPTY; 233 static byte newTerrainDuration = 1; 234 static bool playing = false; 235 static bool blink = false; 236 static unsigned int distance = 0; 237 238 if (!playing) { 239 drawCAR((blink) ? CAR_POSITION_OFF : CARPos, terrainUpper, terrainLower, distance >> 3); 240 if (blink) { 241 lcd.setCursor(0, 0); 242 lcd.print(“Press Start”); 243 } 244 delay(100); 245 blink = !blink; 246 if (buttonPushed) { 247 initializeGraphics(); 248 CARPos = CAR_POSITION_RUN_LOWER_1; 249 playing = true; 250 buttonPushed = false; 251 distance = 0; 252 } 253 return; 254 } 255 256 // Shift the terrain to the left 257 advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 258 advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 259 260 // Make new terrain to enter on the right 261 if (–newTerrainDuration == 0) { 262 if (newTerrainType == TERRAIN_EMPTY) { 263 newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK; 264 newTerrainDuration = 10 + random(6); 265 } else { 266 newTerrainType = TERRAIN_EMPTY; 267 newTerrainDuration = 10 + random(6); 268 } 269 } 270 271 if (buttonPushed) { 272 if (CARPos <= CAR_POSITION_RUN_LOWER_2) CARPos = CAR_POSITION_JUMP_1; 273 buttonPushed = false; 274 } 275 276 if (drawCAR(CARPos, terrainUpper, terrainLower, distance >> 3)) { 277 playing = false; // The CAR collided with something. Too bad. 278 for (int i = 0; i <= 2; i++) { 279 } 280 } else { 281 if (CARPos == CAR_POSITION_RUN_LOWER_2 || CARPos == CAR_POSITION_JUMP_8) { 282 CARPos = CAR_POSITION_RUN_LOWER_1; 283 } else if ((CARPos >= CAR_POSITION_JUMP_3 && CARPos <= CAR_POSITION_JUMP_5) && terrainLower[CAR_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) { 284 CARPos = CAR_POSITION_RUN_UPPER_1; 285 } else if (CARPos >= CAR_POSITION_RUN_UPPER_1 && terrainLower[CAR_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) { 286 CARPos = CAR_POSITION_JUMP_5; 287 } else if (CARPos == CAR_POSITION_RUN_UPPER_2) { 288 CARPos = CAR_POSITION_RUN_UPPER_1; 289 } else { 290 ++CARPos; 291 } 292 ++distance; 293 294 digitalWrite(PIN_AUTOPLAY, terrainLower[CAR_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW); 295 } 296}
Code
csharp
The code
1#include
2#include
3#define PIN_BUTTON 2 4#define PIN_AUTOPLAY 1 5#define SPRITE_RUN1 1 //Car sprite 6#define SPRITE_RUN2 2 7#define SPRITE_JUMP 3 8#define SPRITE_JUMP_UPPER ‘.’ 9#define SPRITE_JUMP_LOWER 4 10#define SPRITE_TERRAIN_EMPTY ‘ ‘ 11#define SPRITE_TERRAIN_SOLID 5 12#define SPRITE_TERRAIN_SOLID_RIGHT 6 13#define SPRITE_TERRAIN_SOLID_LEFT 7 14#define CAR_HORIZONTAL_POSITION 1 // Horizontal position of CAR on screen 15#define TERRAIN_WIDTH 16 16#define TERRAIN_EMPTY 0 17#define TERRAIN_LOWER_BLOCK 1 18#define TERRAIN_UPPER_BLOCK 2 19#define CAR_POSITION_OFF 0 // CAR is invisible 20#define CAR_POSITION_RUN_LOWER_1 1 // CAR is running on lower row (pose 1) 21#define CAR_POSITION_RUN_LOWER_2 2 // (pose 2) 22#define CAR_POSITION_JUMP_1 3 // Starting a jump 23#define CAR_POSITION_JUMP_2 4 // Half-way up 24#define CAR_POSITION_JUMP_3 5 // Jump is on upper row 25#define CAR_POSITION_JUMP_4 6 // Jump is on upper row 26#define CAR_POSITION_JUMP_5 7 // Jump is on upper row 27#define CAR_POSITION_JUMP_6 8 // Jump is on upper row 28#define CAR_POSITION_JUMP_7 9 // Half-way down 29#define CAR_POSITION_JUMP_8 10 // About to land 30#define CAR_POSITION_RUN_UPPER_1 11 // CAR is running on upper row (pose 1) 31#define CAR_POSITION_RUN_UPPER_2 12 // (pose 2) 32LiquidCrystal_I2C lcd(0x27, 16, 2); 33static char terrainUpper[TERRAIN_WIDTH + 1]; 34static char terrainLower[TERRAIN_WIDTH + 1]; 35static bool buttonPushed = false; 36void initializeGraphics() { 37 static byte graphics[] = { 38 // Run position 1 39 0b00000, 40 0b00000, 41 0b00100, 42 0b11110, 43 0b11110, 44 0b11111, 45 0b01001, 46 0b00000, 47 // Run position 2 48 0b00000, 49 0b00000, 50 0b00100, 51 0b11110, 52 0b11110, 53 0b11111, 54 0b01001, 55 0b00000, 56 // Jump 57 0b00000, 58 0b00000, 59 0b00100, 60 0b11110, 61 0b11110, 62 0b11111, 63 0b01001, 64 0b00000, 65 // Jump lower 66 0b00000, 67 0b00000, 68 0b00100, 69 0b11110, 70 0b11110, 71 0b11111, 72 0b01001, 73 0b00000, 74 // Ground 75 0b01110, 76 0b11111, 77 0b10101, 78 0b11111, 79 0b10101, 80 0b11111, 81 0b10101, 82 0b11111, 83 // Ground right 84 0b01110, 85 0b11111, 86 0b10101, 87 0b11111, 88 0b10101, 89 0b11111, 90 0b10101, 91 0b11111, 92 // Ground left 93 0b01110, 94 0b11111, 95 0b10101, 96 0b11111, 97 0b10101, 98 0b11111, 99 0b10101, 100 0b11111, 101 }; 102 int i; 103 // Skip using character 0, this allows lcd.print() to be used to 104 // quickly draw multiple characters 105 for (i = 0; i < 7; ++i) { 106 lcd.createChar(i + 1, &graphics[i * 8]); 107 } 108 for (i = 0; i < TERRAIN_WIDTH; ++i) { 109 terrainUpper[i] = SPRITE_TERRAIN_EMPTY; 110 terrainLower[i] = SPRITE_TERRAIN_EMPTY; 111 } 112} 113 114// Slide the terrain to the left in half-character increments 115// 116void advanceTerrain(char* terrain, byte newTerrain) { 117 for (int i = 0; i < TERRAIN_WIDTH; ++i) { 118 char current = terrain[i]; 119 char next = (i == TERRAIN_WIDTH – 1) ? newTerrain : terrain[i + 1]; 120 switch (current) { 121 case SPRITE_TERRAIN_EMPTY: 122 terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY; 123 break; 124 case SPRITE_TERRAIN_SOLID: 125 terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID; 126 break; 127 case SPRITE_TERRAIN_SOLID_RIGHT: 128 terrain[i] = SPRITE_TERRAIN_SOLID; 129 break; 130 case SPRITE_TERRAIN_SOLID_LEFT: 131 terrain[i] = SPRITE_TERRAIN_EMPTY; 132 break; 133 } 134 } 135} 136 137bool drawCAR(byte position, char* terrainUpper, char* terrainLower, unsigned int score) { 138 bool collide = false; 139 char upperSave = terrainUpper[CAR_HORIZONTAL_POSITION]; 140 char lowerSave = terrainLower[CAR_HORIZONTAL_POSITION]; 141 byte upper, lower; 142 switch (position) { 143 case CAR_POSITION_OFF: 144 upper = lower = SPRITE_TERRAIN_EMPTY; 145 break; 146 case CAR_POSITION_RUN_LOWER_1: 147 upper = SPRITE_TERRAIN_EMPTY; 148 lower = SPRITE_RUN1; 149 break; 150 case CAR_POSITION_RUN_LOWER_2: 151 upper = SPRITE_TERRAIN_EMPTY; 152 lower = SPRITE_RUN2; 153 break; 154 case CAR_POSITION_JUMP_1: 155 case CAR_POSITION_JUMP_8: 156 upper = SPRITE_TERRAIN_EMPTY; 157 lower = SPRITE_JUMP; 158 break; 159 case CAR_POSITION_JUMP_2: 160 case CAR_POSITION_JUMP_7: 161 upper = SPRITE_JUMP_UPPER; 162 lower = SPRITE_JUMP_LOWER; 163 break; 164 case CAR_POSITION_JUMP_3: 165 case CAR_POSITION_JUMP_4: 166 case CAR_POSITION_JUMP_5: 167 case CAR_POSITION_JUMP_6: 168 upper = SPRITE_JUMP; 169 lower = SPRITE_TERRAIN_EMPTY; 170 break; 171 case CAR_POSITION_RUN_UPPER_1: 172 upper = SPRITE_RUN1; 173 lower = SPRITE_TERRAIN_EMPTY; 174 break; 175 case CAR_POSITION_RUN_UPPER_2: 176 upper = SPRITE_RUN2; 177 lower = SPRITE_TERRAIN_EMPTY; 178 break; 179 } 180 if (upper != ‘ ‘) { 181 terrainUpper[CAR_HORIZONTAL_POSITION] = upper; 182 collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true; 183 } 184 if (lower != ‘ ‘) { 185 terrainLower[CAR_HORIZONTAL_POSITION] = lower; 186 collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true; 187 } 188 189 byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1; 190 191 // Draw the scene 192 terrainUpper[TERRAIN_WIDTH] = ‘\\0’; 193 terrainLower[TERRAIN_WIDTH] = ‘\\0’; 194 char temp = terrainUpper[16 – digits]; 195 terrainUpper[16 – digits] = ‘\\0’; 196 lcd.setCursor(0, 0); 197 lcd.print(terrainUpper); 198 terrainUpper[16 – digits] = temp; 199 lcd.setCursor(0, 1); 200 lcd.print(terrainLower); 201 202 lcd.setCursor(16 – digits, 0); 203 lcd.print(score); 204 205 terrainUpper[CAR_HORIZONTAL_POSITION] = upperSave; 206 terrainLower[CAR_HORIZONTAL_POSITION] = lowerSave; 207 return collide; 208} 209 210// Handle the button push as an interrupt 211void buttonPush() { 212 buttonPushed = true; 213} 214 215void setup() { 216 pinMode(PIN_BUTTON, INPUT); 217 digitalWrite(PIN_BUTTON, HIGH); 218 pinMode(PIN_AUTOPLAY, OUTPUT); 219 digitalWrite(PIN_AUTOPLAY, HIGH); 220 221 // Digital pin 2 maps to interrupt 0 222 attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING); 223 224 initializeGraphics(); 225 226 lcd.init(); 227 lcd.backlight(); 228} 229 230void loop() { 231 static byte CARPos = CAR_POSITION_RUN_LOWER_1; 232 static byte newTerrainType = TERRAIN_EMPTY; 233 static byte newTerrainDuration = 1; 234 static bool playing = false; 235 static bool blink = false; 236 static unsigned int distance = 0; 237 238 if (!playing) { 239 drawCAR((blink) ? CAR_POSITION_OFF : CARPos, terrainUpper, terrainLower, distance >> 3); 240 if (blink) { 241 lcd.setCursor(0, 0); 242 lcd.print(“Press Start”); 243 } 244 delay(100); 245 blink = !blink; 246 if (buttonPushed) { 247 initializeGraphics(); 248 CARPos = CAR_POSITION_RUN_LOWER_1; 249 playing = true; 250 buttonPushed = false; 251 distance = 0; 252 } 253 return; 254 } 255 256 // Shift the terrain to the left 257 advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 258 advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); 259 260 // Make new terrain to enter on the right 261 if (–newTerrainDuration == 0) { 262 if (newTerrainType == TERRAIN_EMPTY) { 263 newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK; 264 newTerrainDuration = 10 + random(6); 265 } else { 266 newTerrainType = TERRAIN_EMPTY; 267 newTerrainDuration = 10 + random(6); 268 } 269 } 270 271 if (buttonPushed) { 272 if (CARPos <= CAR_POSITION_RUN_LOWER_2) CARPos = CAR_POSITION_JUMP_1; 273 buttonPushed = false; 274 } 275 276 if (drawCAR(CARPos, terrainUpper, terrainLower, distance >> 3)) { 277 playing = false; // The CAR collided with something. Too bad. 278 for (int i = 0; i <= 2; i++) { 279 } 280 } else { 281 if (CARPos == CAR_POSITION_RUN_LOWER_2 || CARPos == CAR_POSITION_JUMP_8) { 282 CARPos = CAR_POSITION_RUN_LOWER_1; 283 } else if ((CARPos >= CAR_POSITION_JUMP_3 && CARPos <= CAR_POSITION_JUMP_5) && terrainLower[CAR_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) { 284 CARPos = CAR_POSITION_RUN_UPPER_1; 285 } else if (CARPos >= CAR_POSITION_RUN_UPPER_1 && terrainLower[CAR_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) { 286 CARPos = CAR_POSITION_JUMP_5; 287 } else if (CARPos == CAR_POSITION_RUN_UPPER_2) { 288 CARPos = CAR_POSITION_RUN_UPPER_1; 289 } else { 290 ++CARPos; 291 } 292 ++distance; 293 294 digitalWrite(PIN_AUTOPLAY, terrainLower[CAR_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW); 295 } 296}
Downloadable files
Schematics
Schematics (Edited)
Schematics
Comments
Only logged in users can leave comments
bruno_opaiva
0 Followers
•
0 Projects
Table of contents
Intro
15
This article includes everything you need to know about using a character I2C LCD with Arduino. I have included a wiring diagram and many example codes to help you get started.
The first part of this article covers the basics of displaying text and numbers. In the second half, I will go into more detail on how to display custom characters and how you can use the other functions of the LiquidCrystal_I2C library.
Once you know how to display text and numbers on the LCD, I suggest you take a look at the articles below. In these tutorials, you will learn how to measure and display sensor data on the LCD.
Adjusting The LCD Contrast
After wiring the LCD, you will need to adjust the contrast of the LCD. On the I2C module, there is a potentiometer that can be rotated with a small screwdriver.
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.
Do More with LCD
Custom Character
lcd.print() function supports only ASCII characters. If you want to display a special character or symbol (e.g. heart, angry bird), you need to use the below character generator.
LCD 16×2 can display 32 characters (2 rows and 16 columns). Each character is composed of 40 pixels (8 rows and 5 columns).
The character generator represents a character (40 pixels). You just need to do the following steps:
Result on LCD:
Multiple custom characters
We can create up to 8 custom characters (indexed 0 to 7). The below example creates and displays three characters.
Result on LCD:
Summary: how to use custom character on LCD
- Use the above character generator to create binary code for the custom character.
- Declare the binary code for the custom character (copy from above step)
- Create custom character and assign to an index value (from 0 to 7) in setup() function
- Print the custom character in LCD anytime, anywhere (in setup() or loop() function)
Other functions
Add the below functions into loop() function one by one. And add delay(5000) after each function
- Clear LCD screen
- Move the cursor to the upper-left of the LCD
- Move the cursor to the a position (column, row)
- Display the LCD cursor
- Hides the LCD cursor.
- Display the blinking LCD cursor
- Turns off the blinking LCD cursor.
- And more at LiquidCrystal Library Reference
Specifications
The specifications of the 16×2, 20×4, and other sized LCDs are mostly the same. They all use the same HD44780 Hitachi LCD controller, so you can easily swap them. You will only need to change the size specifications in your Arduino code.
The specifications of a typical 16×2 I2C display can be found in the table below.
16×2 I2C LCD Specifications
Operating voltage | 5 V |
Controller | Hitachi HD44780 LCD controller |
Default address | 0x27 |
Screen resolution | 2-lines × 16 characters |
Character resolution | 5 × 8 pixels |
Module dimensions | 80 × 36 × 12 mm |
Viewing area dimensions | 64.5 × 16.4 mm |
Cost | Check price |
For more information, you can check out the datasheets below.
The 16×2 and 20×4 datasheets include the dimensions of the LCD and you can find more information about the Hitachi LCD driver in the HD44780 datasheet.
The PCF8574 chip is used in the I2C module on the back of the LCD.
Arduino I2C LCD – Print a Simple Text
Printing text on the LCD is very simple. The below sketch will print some text on the display. But before uploading this sketch you need to make some minor changes according to your display size and address.
In the second line, I created a
LiquidCrystal_I2C
variable. It requires three variables – the I2C address of the LCD and the dimension of the LCD (columns and rows of the display). The I2C address of my display is
0x27
and it has 16 columns and 2 rows. So I will use –
LiquidCrystal_I2C lcd(0x27,16,2)
. If you have a different LCD display change the I2C address and dimension accordingly.
Now the sketch is ready for your display. Upload it to your Arduino board and see the output.
Code to Print a Simple Text
#includeLiquidCrystal_I2C lcd(0x27,16,2); void setup() { lcd.init(); lcd.clear(); lcd.backlight(); lcd.setCursor(2,0); lcd.print("Hello world!"); lcd.setCursor(2,1); lcd.print("LCD Tutorial"); } void loop() {}
Explaining the Code
The sketch is very simple. It begins with including the
LiquidCrystal_I2C
header file –
#include LiquidCrystal_I2C.h>
Then you need to define a
LiquidCrystal_I2C
variable for your LCD. It requires three variables – the I2C address of the LCD and the dimension of the LCD (columns and rows of the display).
Then in the setup section, you have to initialize the display using the
init()
function. The
clear()
function will clear the display buffer. It is good to clear the display buffer before printing anything on the display so that it does not display anything that was previously stored in the display buffer.
backlight()
– this will turn on the display backlight.
setCursor(col, row)
– this will set the cursor to a position from where the text and characters will print. This function requires two variables – columns, and rows.One thing to note here is that columns and rows counting start from the top left corner and it starts with (0, 0).
print()
– this will print text and characters on the display.
Now you can understand that first I set the cursor to the third column of the first row and printed some text there.
lcd.setCursor(2,0); lcd.print("Hello world!");
Then I set the cursor to the third column of the second row and print some text there.
lcd.setCursor(2,1); lcd.print("LCD Tutorial");
For this example, we don’t have to use the loop section, so leave it blank.
How to find the I2C address of my LCD?
Most I2C LCDs ship with the default address ‘0x27’, but it can be different depending on the batch/manufacturer. If this is the case, you will need to find the actual address of the LCD before you can start using it. On the Arduino website, you can find a simple example sketch that scans the I2C-bus for devices. If a device is found, it will display the address in the serial monitor.
/*I2C_scanner This sketch tests standard 7-bit addresses. Devices with higher bit address might not be seen properly. */ #include “Wire.h” void setup() { Wire.begin(); Serial.begin(9600); while (!Serial); Serial.println(“\nI2C 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\n”); else Serial.println(“done\n”); delay(5000); }
If you upload this sketch to the Arduino and run it, you should see the following output in the Serial Monitor (Ctrl + Shift + M).
Write down the address you find, you will need it later when programming the LCD.
How to create and display custom characters?
With the function
createChar()
it is possible to create and display custom characters on the LCD. This is especially useful if you want to display a character that is not part of the standard ASCII character set.
CGROM and CGRAM
LCDs that are based on the Hitachi HD44780 LCD controller have two types of memory: CGROM and CGRAM (Character Generator ROM and RAM).
CGROM generates all the 5 x 8 dot character patterns from the standard 8-bit character codes. CGRAM can generate user-defined character patterns.
For 5 x 8 dot displays, CGRAM can write up to 8 custom characters and for 5 x 10 dot displays 4. For more info see the datasheet.
Custom character example code
The following example sketch creates and displays eight custom characters (numbered 0 – 7). You can copy the code by clicking on the button in the top right corner of the code field.
/* Arduino example code to display custom characters on I2C character LCD. More info: www.www.makerguides.com */ // Include the library: #include “LiquidCrystal_I2C.h” // Create lcd object of class LiquidCrystal_I2C: LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); // Change to (0x27,20,4) for 20×4 LCD. // Make custom characters: byte Heart[] = { B00000, B01010, B11111, B11111, B01110, B00100, B00000, B00000 }; byte Bell[] = { B00100, B01110, B01110, B01110, B11111, B00000, B00100, B00000 }; byte Alien[] = { B11111, B10101, B11111, B11111, B01110, B01010, B11011, B00000 }; byte Check[] = { B00000, B00001, B00011, B10110, B11100, B01000, B00000, B00000 }; byte Speaker[] = { B00001, B00011, B01111, B01111, B01111, B00011, B00001, B00000 }; byte Sound[] = { B00001, B00011, B00101, B01001, B01001, B01011, B11011, B11000 }; byte Skull[] = { B00000, B01110, B10101, B11011, B01110, B01110, B00000, B00000 }; byte Lock[] = { B01110, B10001, B10001, B11111, B11011, B11011, B11111, B00000 }; void setup() { // Initialize LCD and turn on the backlight: lcd.init(); lcd.backlight(); // Create new characters: lcd.createChar(0, Heart); lcd.createChar(1, Bell); lcd.createChar(2, Alien); lcd.createChar(3, Check); lcd.createChar(4, Speaker); lcd.createChar(5, Sound); lcd.createChar(6, Skull); lcd.createChar(7, Lock); // Clear the LCD screen: lcd.clear(); // Print a message to the lcd: lcd.print(“Custom Character”); } // Print all the custom characters: void loop() { lcd.setCursor(0, 1); lcd.write(0); lcd.setCursor(2, 1); lcd.write(1); lcd.setCursor(4, 1); lcd.write(2); lcd.setCursor(6, 1); lcd.write(3); lcd.setCursor(8, 1); lcd.write(4); lcd.setCursor(10, 1); lcd.write(5); lcd.setCursor(12, 1); lcd.write(6); lcd.setCursor(14, 1); lcd.write(7); }
You should see the following output on the LCD:
How the code works
After including the library and creating the LCD object, the custom character arrays are defined. Each array consists of 8 bytes (only 5 bits are considered). There is 1 byte for each row of the 5 x 8 led matrix. In this example, 8 custom characters are created.
// Make custom characters: byte Heart[] = { B00000, B01010, B11111, B11111, B01110, B00100, B00000, B00000 };
When looking closely at the array, you will see the following. Each row consists of 5 numbers corresponding to the 5 pixels in a 5 x 8 dot character. A 0 means pixel off and a 1 means pixel on. The prefix ‘B’ is the Arduino specific binary formatter.
It is possible to edit each row by hand, but I recommend using this visual tool on GitHub. This application automatically creates the character array and you can click on the pixels to turn them on or off.
In the setup, the custom characters are created with
lcd.createChar(num, data)
. The first argument in this function is the number of the custom character (0-7) and the second argument is the character array that we created.
// Create new characters: lcd.createChar(0, Heart); lcd.createChar(1, Bell); lcd.createChar(2, Alien); lcd.createChar(3, Check); lcd.createChar(4, Speaker); lcd.createChar(5, Sound); lcd.createChar(6, Skull); lcd.createChar(7, Lock);
In the loop, all the characters are displayed with lcd.write(). As the argument, we use the number of the custom character that we want to display.
lcd.setCursor(0, 1); lcd.write(0);
Create and Display Custom Characters
If you find the default font uninteresting, you can create your own custom characters (glyphs) and symbols. They come in handy when you need to display a character that isn’t in the standard ASCII character set.
As previously discussed in this tutorial, a character is made up of a 5×8 pixel matrix; therefore, you must define your custom character within this matrix. You can define a character by using the
createChar()
function.
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 of these user-defined characters are stored in the LCD’s CGRAM.
CGROM and CGRAM
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.
CGRAM is an additional memory for storing user-defined characters. This RAM is limited to 64 bytes. Therefore, for a 5×8 pixel LCD, only 8 user-defined characters can be stored in CGRAM, whereas for a 5×10 pixel LCD, only 4 can be stored.
Custom Character Generator
Creating custom characters has never been easier! We’ve developed a small application called Custom Character Generator. Can you see the blue grid below? You can click on any pixel to set or clear that pixel. And as you click, the code for the character is generated next to the grid. This code can be used directly in your Arduino sketch.
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.
Arduino Example Code
The sketch below demonstrates how to display custom characters on the LCD.
#includeLiquidCrystal_I2C lcd(0x3F, 16, 2); // set the LCD address to 0x3F for a 16 chars and 2 line display // make some custom characters: byte Heart[8] = { 0b00000, 0b01010, 0b11111, 0b11111, 0b01110, 0b00100, 0b00000, 0b00000 }; byte Bell[8] = { 0b00100, 0b01110, 0b01110, 0b01110, 0b11111, 0b00000, 0b00100, 0b00000 }; byte Alien[8] = { 0b11111, 0b10101, 0b11111, 0b11111, 0b01110, 0b01010, 0b11011, 0b00000 }; byte Check[8] = { 0b00000, 0b00001, 0b00011, 0b10110, 0b11100, 0b01000, 0b00000, 0b00000 }; byte Speaker[8] = { 0b00001, 0b00011, 0b01111, 0b01111, 0b01111, 0b00011, 0b00001, 0b00000 }; byte Sound[8] = { 0b00001, 0b00011, 0b00101, 0b01001, 0b01001, 0b01011, 0b11011, 0b11000 }; byte Skull[8] = { 0b00000, 0b01110, 0b10101, 0b11011, 0b01110, 0b01110, 0b00000, 0b00000 }; byte Lock[8] = { 0b01110, 0b10001, 0b10001, 0b11111, 0b11011, 0b11011, 0b11111, 0b00000 }; void setup() { lcd.init(); // Make sure backlight is on lcd.backlight(); // create a new characters lcd.createChar(0, Heart); lcd.createChar(1, Bell); lcd.createChar(2, Alien); lcd.createChar(3, Check); lcd.createChar(4, Speaker); lcd.createChar(5, Sound); lcd.createChar(6, Skull); lcd.createChar(7, Lock); // Clears the LCD screen lcd.clear(); // Print a message to the lcd. lcd.print("Custom Character"); } // Print All the custom characters void loop() { lcd.setCursor(0, 1); lcd.write(0); lcd.setCursor(2, 1); lcd.write(1); lcd.setCursor(4, 1); lcd.write(2); lcd.setCursor(6, 1); lcd.write(3); lcd.setCursor(8, 1); lcd.write(4); lcd.setCursor(10, 1); lcd.write(5); lcd.setCursor(12, 1); lcd.write(6); lcd.setCursor(14, 1); lcd.write(7); }
The output appears as shown.
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);
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.
Character LCD Display
As the name suggests, these LCDs are ideal for displaying only characters. A 16×2 character LCD, for example, can display 32 ASCII characters across two rows.
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.
I2C LCD Adapter
At the heart of the adapter is an 8-bit I/O expander chip – PCF8574. This chip converts the I2C data from an Arduino into the parallel data required for an LCD display.
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’.
Supplies
Hardware components
16×2 character I2C LCD | × 1 | Amazon |
20×4 character I2C LCD (alternative) | × 1 | Amazon |
Arduino Uno Rev3 | × 1 | Amazon |
Jumper wires (male to female) | × 4 | Amazon |
USB cable type A/B | × 1 | Amazon |
Tools
Small screwdriver | Amazon |
Software
Arduino IDE |
Makerguides.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to products on Amazon.com. As an Amazon Associate we earn from qualifying purchases.
Wiring Diagram
This image is created using Fritzing. Click to enlarge image
LCD I2C | Arduino Uno, Nano | Arduino Mega |
Vin | 5V | 5V |
GND | GND | GND |
SDA | A4 | 20 |
SCL | A5 | 21 |
Installing the LiquidCrystal_I2C Arduino library
In this tutorial, I will be using the LiquidCrystal_I2C library. This library has many built-in functions that make programming the LCD quite easy.
The latest version of this library can be found here on GitHub or click the download button below.
Make sure that you have this exact library installed and delete any other libraries that have the same name (LiquidCrystal_I2C). Other libraries will probably work as well but might use slightly different names for the different functions.
The LiquidCrystal_I2C library works in combination with the Wire.h library which allows you to communicate with I2C devices. This library comes pre-installed with the Arduino IDE.
To install this library, go to Tools > Manage Libraries (Ctrl + Shift + I on Windows) in the Arduino IDE. The Library Manager will open and update the list of installed libraries.
Now search for ‘liquidcrystal_i2c’ and look for the library by Frank de Brabander. Select the latest version and then click Install.
The library does include some examples that you can use, but you will have to modify them to match your hardware setup. I have included many example codes below that you can use with the wiring setup I have shown earlier.
First I will show you some basic example code and then I will explain the functions in more detail.
How to connect the I2C LCD to Arduino UNO
The wiring diagram below shows you how to connect the I2C LCD to the Arduino. Wiring an I2C LCD is a lot easier than connecting a standard LCD. You only need to connect 4 pins instead of 12.
The connections are also given in the table below.
I2C LCD Connections
I2C Character LCD | Arduino |
GND | GND |
VCC | 5 V |
SDA | A4 |
SCL | A5 |
If you are not using an Arduino Uno, the SDA and SCL pins can be at a different location.
Note that an Arduino Uno with the R3 layout (1.0 pinout) also has the SDA (data line) and SCL (clock line) pin headers close to the AREF pin. Check the table below for more details.
Board | SDA | SCL |
Arduino Uno | A4 | A5 |
Arduino Nano | A4 | A5 |
Arduino Micro | ||
Arduino Mega 2560 | 20 | 21 |
Arduino Leonardo | ||
Arduino Due | 20 | 21 |
Find the I2C address of your LCD
You need to know the I2C address of the LCD before starting the communication. To know the I2C address of your LCD run the below sketch.
#include
void setup() { Serial.begin(9600); Serial.println("\nI2C Scanner"); Serial.println("Scanning..."); byte device_count = 0; Wire.begin(); for (byte address = 1; address < 127; address++ ) { Wire.beginTransmission(address); if (Wire.endTransmission() == 0) { // Serial.print ("Found address: "); Serial.print("I2C device found at address 0x"); Serial.print(address, HEX); Serial.println(" !"); device_count++; delay(1); } } Serial.println ("Done."); Serial.print ("Found "); Serial.print (device_count); Serial.println (" device(s)."); } void loop() {}
Open the serial monitor on the Arduino IDE and you will get a result like this –
Note down the I2C address of your LCD somewhere. You will need it later in the following sketches.
Connect an I2C LCD with Arduino
Connecting an I2C LCD display with Arduino is very simple compared to a normal LCD display. You only need to connect four wires to the Arduino.
Connect the LCD’s VCC pin to the Arduino 5v pin and the Ground pin to the Arduino Ground pin. The remaining two pins are SCL and SDA. You need to connect the SCL pin to the Arduino SCL pin and SDA to the Arduino SDA pin.
On the Arduino Uno, the SCL pin is A5 and the SDA pin is A4 pin, connect it accordingly.
If you are using a different Arduino board, you will find the SCL and SDA pin for the respective board in the below table.
Arduino Board | SCL Pin | SDA Pin |
Arduino Uno | A5 | A4 |
Arduino Nano | A5 | A4 |
Arduino Micro | ||
Arduino Leonardo | ||
Arduino Mega 2560 | 21 | 20 |
Arduino Due | 21 | 20 |
Basic Arduino example code for I2C LCD
You can upload the following example code to the Arduino using the Arduino IDE.
For this tutorial, I used this 16×2 I2C character LCD display, but you can use other I2C LCDs of different sizes as well. This example sketch will display the classic ‘Hello World!’ on the first line of the LCD and ‘LCD tutorial’ on the second line.
Next, I will explain how the code works.
/* I2C LCD with Arduino example code. More info: https://www.makerguides.com */ #include “Wire.h” // Library for I2C communication #include “LiquidCrystal_I2C.h” // Library for LCD // Wiring: SDA pin is connected to A4 and SCL pin to A5. // Connect to LCD via I2C, default address 0x27 (A0-A2 not jumpered) LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); // Change to (0x27,20,4) for 20×4 LCD. void setup() { // Initiate the LCD: lcd.init(); lcd.backlight(); } void loop() { // Print ‘Hello World!’ on the first line of the LCD: lcd.setCursor(2, 0); // Set the cursor on the third column and first row. lcd.print(“Hello World!”); // Print the string “Hello World!” lcd.setCursor(2, 1); //Set the cursor on the third column and the second row (counting starts at 0!). lcd.print(“LCD tutorial”); }
You should see the following output on the LCD:
How the code works
First, the required libraries are included. As mentioned earlier we need both the Wire.h and the LiquidCrystal_I2C library. In the rest of this tutorial, I will cover more of the built-in functions of this library.
*When using the latest version of the LiquidCrystal_I2C library it is no longer needed to include the wire.h library in your sketch. The other library imports wire.h automatically.
#include “Wire.h” // Library for I2C communication #include “LiquidCrystal_I2C.h” // Library for LCD
The next step is to create an LCD object with the LiquidCrystal_I2C class and specify the address and dimensions. For this, we use the function
LiquidCrystal_I2C(address, columns, rows)
.
This is where you will need to change the default address to the address you found earlier if it happens to be different. When using a 20×4 LCD, change this line to
LiquidCrystal_I2C(0x27,20,4);
Note that we have called the display ‘lcd’. You can give it a different name if you want like ‘menu_display’. You will need to change ‘lcd’ to the new name in the rest of the sketch.
// Connect to LCD via I2C, default address 0x27 (A0-A2 not jumpered) LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); // Change to (0x27,20,4) for 20×4 LCD.
Setup
In the setup, the LCD is initiated with
lcd.init()
and the backlight is turned on with
lcd.backlight()
.
void setup() { // Initiate the LCD: lcd.init(); lcd.backlight(); }
Loop
In the loop section of the code, the cursor is set to the third column and the first row of the LCD with
lcd.setCursor(2,0)
.
Note that counting starts at 0 and the first argument specifies the column. So
lcd.setCursor(2,1)
sets the cursor on the third column and the second row.
Next the string ‘Hello World!’ is printed with
lcd.print("Hello World!")
. Note that you need to place quotation marks (” “) around the text since we are printing a text string.
When you want to print numbers, no quotation marks are necessary. For example
lcd.print(12345)
.
void loop() { lcd.setCursor(2, 0); // Set the cursor on the third column and first row. lcd.print(“Hello World!”); // Print the string “Hello World!”. lcd.setCursor(2, 1); //Set the cursor on the third column and the second row. lcd.print(“LCD tutorial”); // Print the string “LCD tutorial”. }
If you want to see an example for displaying (changing) variables on the LCD, check out my tutorial for the HC-SR04 ultrasonic distance sensor:
Other useful functions of the LiquidCrystal_I2C library
The example sketch above shows you the basics of displaying text on the LCD. Now we will take a look at the other functions of the LiquidCrystal_I2C library.
clear()
Clears the LCD screen and positions the cursor in the upper-left corner (first row and first column) of the display. You can use this function to display different words in a loop.
#include “LiquidCrystal_I2C.h” LiquidCrystal_I2C lcd(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); } void loop() { lcd.clear(); lcd.print(“Monday”); delay(2000); lcd.clear(); lcd.print(“13:45”); delay(2000); }
home()
Positions the cursor in the top-left corner of the LCD. Use
clear()
if you also want to clear the display.
cursor()
Displays the LCD cursor: an underscore (line) at the position of the next character to be printed.
noCursor()
Hides the LCD cursor. The following example creates a blinking cursor at the end of “Hello World!”.
#include “LiquidCrystal_I2C.h” LiquidCrystal_I2C lcd(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); lcd.print(“Hello World!”); } void loop() { lcd.cursor(); delay(500); lcd.noCursor(); delay(500); }
blink()
Creates a blinking block style LCD cursor: a blinking rectangle at the position of the next character to be printed.
noBlink()
Disables the block style LCD cursor. The following example displays the blinking cursor for 5 seconds and then disables it for 2 seconds.
#include “LiquidCrystal_I2C.h” LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); lcd.print(“blink() example”); } void loop() { lcd.blink(); delay(5000); lcd.noBlink(); delay(2000); }
display()
This function turns on the LCD screen and displays any text or cursors that have been printed to the display.
noDisplay()
This function turns off any text or cursors printed to the LCD. The text/data is not cleared from the LCD memory.
This means it will be shown again when the function
display()
is called.
The following example creates a blinking text effect.
#include “LiquidCrystal_I2C.h” LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); lcd.print(“Blinking text”); } void loop() { lcd.display(); delay(2000); lcd.noDisplay(); delay(2000); }
write()
This function can be used to write a character to the LCD. See the section about creating and displaying custom characters below for more info.
scrollDisplayLeft()
Scrolls the contents of the display (text and cursor) one space to the left.
You can use this function in the loop section of the code in combination with
delay(500)
, to create a scrolling text animation.
#include “LiquidCrystal_I2C.h” LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); lcd.print(“Hello World!”); } void loop() { lcd.scrollDisplayLeft(); delay(500); }
scrollDisplayRight()
Scrolls the contents of the display (text and cursor) one space to the right.
autoscroll()
This function 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.
The following example sketch enables automatic scrolling and prints the character 0 to 9 at the position (16,0) of the LCD. Change this to (20,0) for a 20×4 LCD.
#include “LiquidCrystal_I2C.h” LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); } void loop() { lcd.autoscroll(); lcd.setCursor(16, 0); for (int x = 0; x < 10; x++) { lcd.print(x); delay(500); } lcd.clear(); }
noAutoscroll()
Turns off automatic scrolling of the LCD.
leftToRight()
This function causes text to flow to the right from the cursor, as if the display is left-justified (default).
rightToLeft()
This function causes text to flow to the left from the cursor, as if the display is right-justified.
Useful Functions from the Library
In this section, I have picked some useful functions from the library and discussed them. I have written some example code where needed. Try to run those codes and see the output. You will understand the function very easily.
clear()
– Clears the display and places the cursor at the top left corner. You can use this function to display different text/strings at the same place at a time. The below code shows the use of this function.
#includeLiquidCrystal_I2C lcd(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); } void loop() { lcd.clear(); lcd.print("Hello !"); delay(1000); lcd.clear(); lcd.print("Welcome to"); delay(1000); lcd.clear(); lcd.print("Circuit Geeks"); delay(1000); }
home()
– Places the cursor at the top left corner of the display without clearing the display.
cursor()
– Displays the LCD cursor. The default cursor is an underscore line.
noCursor()
– Hides the LCD cursor.
blink()
– Creates a blinking block-type LCD cursor.
noBlink()
– Disable the blinking-block type LCD cursor.
display()
– Turns on the LCD screen and displays the texts/characters that were previously printed on the display.
noDisplay()
– Turns off the LCD screen but does not clear data from the LCD memory. The below code shows the use of display() and noDisplay() functions.
#includeLiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); lcd.print("Blinking Display"); } void loop() { lcd.display(); delay(1000); lcd.noDisplay(); delay(1000); }
write()
– This function is used to write a character to the display. You can see the use of this function in the I2C LCD custom character section below.
scrollDisplayLeft()
– Moves the display content one step to the left. You can use this function in a loop to create a scrolling text effect.
scrollDisplayRight()
– Moves the display content one step to the right. The below code shows the use of these functions.
#includeLiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); lcd.setCursor(2, 0); lcd.print("Hello world!"); } void loop() { for (int i = 0; i < 8; i++) { lcd.scrollDisplayLeft(); delay(250); } for (int i = 0; i < 16; i++) { lcd.scrollDisplayRight(); delay(250); } for (int i = 0; i < 8; i++) { lcd.scrollDisplayLeft(); delay(250); } }
autoscroll()
– Scrolls the content of the display automatically.
noAutoscroll()
– Stops the auto-scrolling display. See the below code to understand these functions.
#includeLiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); } char text_1[12] = "Hello world!"; void loop() { lcd.setCursor(14, 0); lcd.autoscroll(); for (int i = 0; i < 12; i++) { lcd.print(text_1[i]); delay(300); } lcd.noAutoscroll(); delay(2000); lcd.clear(); }
leftToRight()
– sets the display orientation from left to right. That means the text/strings will flow from left to right.
rightToLeft()
– sets the display orientation from right to left. That means the text/strings will flow from right to left. The below code shows the use of this function.
#includeLiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); void setup() { lcd.init(); lcd.backlight(); lcd.setCursor(15, 0); lcd.rightToLeft(); lcd.print("ABCDEFG"); lcd.setCursor(0, 1); lcd.leftToRight(); lcd.print("ABCDEFG"); } void loop() {}
Other Useful Links From Around The Web:
Benne is professional Systems Engineer with a deep expertise in Arduino and a passion for DIY projects.
Arduino – LCD I2C
In this Arduino LCD I2C tutorial, we will learn how to connect an LCD I2C (Liquid Crystal Display) to the Arduino board. LCDs are very popular and widely used in electronics projects for displaying information. There are many types of LCD. This tutorial takes LCD 16×2 (16 columns and 2 rows) as an example. The other LCDs are similar.
Keywords searched by users: arduino lcd i2c projects
Categories: Chi tiết 12 Arduino Lcd I2C Projects
See more here: kientrucannam.vn
See more: https://kientrucannam.vn/vn/