Skip to main content

Arduino LCD Display

LCD (Liquid Crystal Display) screens are a popular output method for Arduino projects. They allow you to display text, numbers, and create interactive interfaces without requiring a computer connection. In this tutorial, you'll learn how to connect, program, and use various LCD displays with Arduino.

Introduction​

LCD displays are essential components in many Arduino projects, from simple status displays to complex user interfaces. They provide visual feedback and can display a wide range of information such as:

  • Sensor readings
  • System status
  • Menu options
  • Custom messages
  • Debugging information

In this tutorial, we'll focus on the most common type - character LCD displays (particularly the 16x2 and 20x4 models), which are affordable, easy to connect, and perfect for beginners.

What You'll Need​

  • Arduino board (Uno, Nano, or similar)
  • 16x2 or 20x4 LCD display (HD44780 compatible)
  • Potentiometer (10kΩ) for contrast adjustment
  • Breadboard and jumper wires
  • Arduino IDE with LiquidCrystal library (included by default)

Understanding LCD Displays​

LCD displays compatible with Arduino typically use the HD44780 controller or equivalent. Here's what the numbers like "16x2" or "20x4" mean:

  • First number (16 or 20): Characters per line
  • Second number (2 or 4): Number of lines

Therefore, a 16x2 display can show 16 characters on each of its 2 lines, for a total of 32 characters.

Connecting Your LCD to Arduino​

LCD displays can be connected in two ways:

  1. 4-bit mode - Uses fewer pins but requires more complex code
  2. 8-bit mode - Faster but uses more pins

For most beginner projects, 4-bit mode is recommended as it saves Arduino pins for other components.

Standard Connection Diagram (4-bit mode)​

Here's how to connect a standard 16x2 LCD display to your Arduino:

Connection Table​

LCD PinLCD FunctionArduino Connection
1VSS/GNDGND
2VDD/5V5V
3V0/ContrastPotentiometer wiper
4RSDigital Pin 12
5RWGND (Write mode)
6EnableDigital Pin 11
7-10D0-D3Not connected (4-bit mode)
11D4Digital Pin 5
12D5Digital Pin 4
13D6Digital Pin 3
14D7Digital Pin 2
15A/Backlight+5V (with resistor for some models)
16K/Backlight-GND

Programming Your LCD Display​

To communicate with the LCD, we'll use the built-in LiquidCrystal library that comes with the Arduino IDE. Let's start with a basic example:

Basic "Hello World" Example​

cpp
#include <LiquidCrystal.h>

// Initialize the library with the interface pins
// (RS, E, D4, D5, D6, D7)
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
// Set up the LCD's number of columns and rows
lcd.begin(16, 2);

// Print a message to the LCD
lcd.print("Hello, World!");
}

void loop() {
// Set the cursor to column 0, line 1
// (Note: line 1 is the second row, since counting begins with 0)
lcd.setCursor(0, 1);

// Print the number of seconds since reset
lcd.print(millis() / 1000);
}

Output:

Hello, World!
[increasing number of seconds]

Understanding the Code​

Let's break down the key parts:

  1. Library Import: #include <LiquidCrystal.h> includes the library for controlling the LCD.

  2. Initialization: LiquidCrystal lcd(12, 11, 5, 4, 3, 2) creates an instance of the LiquidCrystal library and defines which pins to use.

  3. Setup: lcd.begin(16, 2) tells the library the dimensions of your LCD (16 columns, 2 rows).

  4. Displaying Text: lcd.print("Hello, World!") shows text on the display.

  5. Setting Cursor Position: lcd.setCursor(0, 1) positions the cursor at the start of the second line.

Essential LCD Functions​

Here are the most commonly used functions for controlling your LCD:

Text Manipulation​

cpp
lcd.print("Text");              // Print text at current cursor position
lcd.setCursor(column, row); // Move cursor to specific position (0-based)
lcd.clear(); // Clear the display
lcd.home(); // Move cursor to home position (0,0)

Display Control​

cpp
lcd.display();                  // Turn on the display
lcd.noDisplay(); // Turn off the display
lcd.cursor(); // Show the cursor
lcd.noCursor(); // Hide the cursor
lcd.blink(); // Make the cursor blink
lcd.noBlink(); // Stop the cursor from blinking

Scrolling​

cpp
lcd.scrollDisplayLeft();        // Scroll content one position left
lcd.scrollDisplayRight(); // Scroll content one position right
lcd.autoscroll(); // Enable auto-scrolling
lcd.noAutoscroll(); // Disable auto-scrolling

Creating Custom Characters​

LCDs allow you to create up to 8 custom characters. Here's how to define and use a custom character:

cpp
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Define a custom character (a heart)
byte heart[8] = {
0b00000,
0b01010,
0b11111,
0b11111,
0b11111,
0b01110,
0b00100,
0b00000
};

void setup() {
lcd.begin(16, 2);

// Create the custom character at position 0
lcd.createChar(0, heart);

lcd.print("Custom char: ");

// Use the custom character
lcd.write(byte(0));
}

void loop() {
// Nothing in the loop
}

Output:

Custom char: ♥

Practical Examples​

Now let's explore some practical applications for LCD displays in real projects.

Example 1: Temperature and Humidity Display​

This example uses a DHT11 or DHT22 sensor to display temperature and humidity readings.

cpp
#include <LiquidCrystal.h>
#include <DHT.h>

#define DHTPIN 7 // DHT sensor connected to digital pin 7
#define DHTTYPE DHT11 // DHT11 sensor type (change to DHT22 if using that model)

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
DHT dht(DHTPIN, DHTTYPE);

void setup() {
lcd.begin(16, 2);
dht.begin();

// Create degree symbol as custom character
byte degreeSymbol[8] = {
0b00110,
0b01001,
0b01001,
0b00110,
0b00000,
0b00000,
0b00000,
0b00000
};
lcd.createChar(0, degreeSymbol);
}

void loop() {
// Wait a few seconds between measurements
delay(2000);

// Read humidity and temperature
float h = dht.readHumidity();
float t = dht.readTemperature();

// Check if readings failed
if (isnan(h) || isnan(t)) {
lcd.clear();
lcd.print("Sensor error!");
return;
}

// Display temperature
lcd.clear();
lcd.print("Temp: ");
lcd.print(t);
lcd.write(byte(0)); // Degree symbol
lcd.print("C");

// Display humidity on second line
lcd.setCursor(0, 1);
lcd.print("Humidity: ");
lcd.print(h);
lcd.print("%");
}

Example 2: Simple Menu System​

This example shows how to create a basic menu system using an LCD and buttons.

cpp
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Button pins
const int btnUp = 8;
const int btnDown = 9;
const int btnSelect = 10;

// Menu variables
int menuItem = 0;
const int menuItems = 4;
String menuOptions[menuItems] = {
"1. LED Control",
"2. Sensor Data",
"3. Settings",
"4. About"
};

void setup() {
lcd.begin(16, 2);

// Initialize buttons as inputs with pull-up resistors
pinMode(btnUp, INPUT_PULLUP);
pinMode(btnDown, INPUT_PULLUP);
pinMode(btnSelect, INPUT_PULLUP);

displayMenu();
}

void loop() {
// Check for button presses (active LOW with pull-up resistors)
if (digitalRead(btnUp) == LOW) {
menuItem--;
if (menuItem < 0) menuItem = menuItems - 1;
displayMenu();
delay(200); // Debounce
}

if (digitalRead(btnDown) == LOW) {
menuItem++;
if (menuItem >= menuItems) menuItem = 0;
displayMenu();
delay(200); // Debounce
}

if (digitalRead(btnSelect) == LOW) {
selectMenuItem();
delay(200); // Debounce
}
}

void displayMenu() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(">");
lcd.print(menuOptions[menuItem]);

// Display next menu item on second line
lcd.setCursor(0, 1);
int nextItem = (menuItem + 1) % menuItems;
lcd.print(" ");
lcd.print(menuOptions[nextItem]);
}

void selectMenuItem() {
lcd.clear();
lcd.print("Selected:");
lcd.setCursor(0, 1);
lcd.print(menuOptions[menuItem]);
delay(1000);
displayMenu();
}

Example 3: Countdown Timer​

A simple countdown timer with LCD display:

cpp
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

int seconds = 60; // Starting time: 60 seconds
bool timerRunning = false;
unsigned long previousMillis = 0;

void setup() {
lcd.begin(16, 2);
displayTimer();

lcd.setCursor(0, 1);
lcd.print("Press to start");
}

void loop() {
// Check if button is pressed to start/stop timer
if (digitalRead(10) == LOW) {
timerRunning = !timerRunning;
delay(200); // Debounce

// Update instruction text
lcd.setCursor(0, 1);
if (timerRunning) {
lcd.print("Timer running ");
} else {
lcd.print("Timer paused ");
}
}

// If reset button is pressed
if (digitalRead(9) == LOW) {
seconds = 60;
timerRunning = false;
displayTimer();
lcd.setCursor(0, 1);
lcd.print("Timer reset ");
delay(200); // Debounce
}

// Update timer if running
if (timerRunning) {
unsigned long currentMillis = millis();

// Check if 1 second has passed
if (currentMillis - previousMillis >= 1000) {
previousMillis = currentMillis;

// Decrease timer
if (seconds > 0) {
seconds--;
displayTimer();
}

// When timer reaches zero
if (seconds == 0 && timerRunning) {
timerRunning = false;
lcd.setCursor(0, 1);
lcd.print("Time's up! ");
}
}
}
}

void displayTimer() {
// Format minutes and seconds
int minutes = seconds / 60;
int secs = seconds % 60;

// Display timer
lcd.setCursor(0, 0);
lcd.print("Timer: ");

// Add leading zeros
if (minutes < 10) lcd.print("0");
lcd.print(minutes);
lcd.print(":");
if (secs < 10) lcd.print("0");
lcd.print(secs);
lcd.print(" ");
}

Troubleshooting LCD Displays​

Here are solutions to common LCD display issues:

  1. Display is blank or very dim

    • Adjust the contrast using the potentiometer
    • Check power connections
    • Verify backlight connections
  2. Display shows garbled text or strange symbols

    • Check wiring connections
    • Reset the Arduino
    • Make sure your code initializes the display correctly
  3. Only one line works

    • Verify you've set the correct LCD dimensions in lcd.begin()
    • Check all data line connections
  4. Display works but doesn't update

    • Make sure you're calling lcd.clear() when needed
    • Check your loop() function logic

Using I2C LCD Displays​

For projects where you want to save pins, I2C LCD adapters (like the PCF8574) are extremely useful. They allow you to control an LCD using just 2 pins (SDA and SCL) instead of 6 or more.

Connecting an I2C LCD​

I2C LCD Code Example​

cpp
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Set the LCD address to 0x27 (or your specific address)
// Set the LCD dimensions (16x2)
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
// Initialize the LCD
lcd.init();

// Turn on the backlight
lcd.backlight();

// Print a message
lcd.setCursor(0, 0);
lcd.print("I2C LCD Display");
lcd.setCursor(0, 1);
lcd.print("Only 2 wires!");
}

void loop() {
// No action needed in the loop for this example
}

Advanced LCD Techniques​

Creating Animations​

You can create simple animations by rapidly changing custom characters:

cpp
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Define animation frames using custom characters
byte frame1[8] = {
0b00000,
0b00000,
0b00000,
0b11111,
0b11111,
0b00000,
0b00000,
0b00000
};

byte frame2[8] = {
0b00000,
0b00000,
0b11111,
0b11111,
0b00000,
0b00000,
0b00000,
0b00000
};

byte frame3[8] = {
0b00000,
0b11111,
0b11111,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000
};

byte frame4[8] = {
0b11111,
0b11111,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000
};

void setup() {
lcd.begin(16, 2);

// Create custom characters for animation frames
lcd.createChar(0, frame1);
lcd.createChar(1, frame2);
lcd.createChar(2, frame3);
lcd.createChar(3, frame4);

lcd.setCursor(0, 0);
lcd.print("Bouncing ball:");
}

void loop() {
// Animation loop
for (int i = 0; i < 4; i++) {
lcd.setCursor(5, 1);
lcd.write(byte(i));
delay(150);
}

// Reverse animation
for (int i = 2; i >= 0; i--) {
lcd.setCursor(5, 1);
lcd.write(byte(i));
delay(150);
}
}

Progress Bar​

Create a useful progress bar for your projects:

cpp
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Custom characters for progress bar
byte p0[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte p1[8] = { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 };
byte p2[8] = { 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 };
byte p3[8] = { 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C };
byte p4[8] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E };
byte p5[8] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F };

void setup() {
lcd.begin(16, 2);

// Create custom characters for progress bar
lcd.createChar(0, p0);
lcd.createChar(1, p1);
lcd.createChar(2, p2);
lcd.createChar(3, p3);
lcd.createChar(4, p4);
lcd.createChar(5, p5);

lcd.setCursor(0, 0);
lcd.print("Progress Bar:");
}

void loop() {
// Simulate a process with progress from 0% to 100%
for (int i = 0; i <= 100; i++) {
drawProgressBar(i);
delay(100);
}

delay(1000);

// Reset and start again
drawProgressBar(0);
delay(1000);
}

void drawProgressBar(int percent) {
// Map the percentage to 16 characters on the display
int numChars = map(percent, 0, 100, 0, 16);

// Calculate the number of completely filled blocks
int numCompleteBlocks = numChars / 1;

// Calculate the remainder for partial block
int remainder = numChars % 1 * 5;

lcd.setCursor(0, 1);

// Draw complete blocks
for (int i = 0; i < numCompleteBlocks; i++) {
lcd.write(byte(5));
}

// Draw partial block if there is a remainder
if (remainder > 0) {
lcd.write(byte(remainder));
}

// Fill the rest of the line with empty blocks
for (int i = numCompleteBlocks + (remainder > 0 ? 1 : 0); i < 16; i++) {
lcd.write(byte(0));
}

// Display percentage
lcd.setCursor(0, 0);
lcd.print("Progress: ");
lcd.print(percent);
lcd.print("% ");
}

Optimizing LCD Performance​

LCD displays can be relatively slow to update. Here are some tips to optimize performance:

  1. Minimize Updates: Only update parts of the display that change
  2. Avoid lcd.clear(): Instead of clearing the entire display, overwrite specific characters
  3. Use Efficient Text: Display shorter messages when possible
  4. Pre-calculate Values: Calculate values before sending to the display
  5. Use Custom Characters: They're more efficient than scrolling text

Summary​

LCD displays are versatile output devices for Arduino projects that allow you to show text, numbers, and create simple user interfaces. In this tutorial, you've learned:

  • How to connect an LCD display to Arduino
  • Basic LCD programming using the LiquidCrystal library
  • Essential LCD functions for text display and manipulation
  • How to create custom characters
  • Practical examples including sensor displays and menu systems
  • Advanced techniques like animations and progress bars
  • How to optimize LCD performance

With these skills, you can add professional-looking displays to your Arduino projects, making them more interactive and user-friendly.

Exercises​

  1. Temperature Display: Modify the temperature/humidity example to add custom icons for hot and cold temperatures.
  2. Scrolling Text: Create a program that scrolls a long message across the display.
  3. Game: Build a simple game (like Pong or Space Invaders) using custom characters.
  4. Data Logger: Create a system that displays sensor readings and logs them to an SD card, showing the status on the LCD.
  5. Clock: Build a digital clock using an RTC (Real-Time Clock) module and display the time and date on the LCD.

Additional Resources​



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)