Connecting an LCD display to your Raspberry Pi will surely take your project up a notch. They’re great for displaying sensor readings, songs or internet radio stations, and stuff from the web like tweets and stock quotes. Whatever you choose to display, LCDs are a simple and inexpensive way to do it.

In this tutorial, I’ll show you two different ways to connect an LCD to the Raspberry Pi with the GPIO pins. One way uses 10 GPIO pins (8 bit mode), and the other way uses 6 (4 bit mode). I’ll be using a 16X2 LCD display here, but the examples below will work with any LCD that uses the Hitachi HD44780 driver.

After connecting the LCD, I’ll show you how to program it in C, using Gordon Henderson’s WiringPi LCD library. If your project uses Python, we have another article that will show you how to program the LCD in Python. The examples I’ll provide will show you how to print text to the display, clear the screen, position the text, and control the cursor. You’ll also see how to scroll text, create custom characters, print data from a sensor, and print the date, time and IP address of your Pi.

There’s another way to connect your LCD that uses only two wires, called I2C. See how to use I2C in our Raspberry Pi I2C LCD Set Up and Programming tutorial.

Watch the video for a quick demonstration of the set up and example programs:

Connecting the LCD

Most people will probably connect their LCD in 4 bit mode since it uses less wires. But in case you’re interested, I’ll show you how to connect it in 8 bit mode as well.

Wiring the LCD in 8 Bit Mode

Each command or character is sent to the LCD as a single byte (8 bits) of data. The byte travels in parallel over 8 data wires, with each bit travelling through it’s own wire. 8 bit mode has twice the bandwidth as 4 bit mode, which in theory translates to higher data transfer speed. The main downside to 8 bit mode is that it uses up a lot of GPIO pins.

Connecting the LCD in 8 bit mode requires 10 GPIO pins:

Raspberry Pi LCD 8 Bit Mode Connection Diagram

The brightness and contrast potentiometers are 10K Ohm, but you can also use 1K to 3K Ohm resistors here.

Wiring the LCD in 4 Bit Mode

In 4 bit mode, each byte of data is sent to the LCD in two sets of 4 bits, one after the other, in what are known as the upper bits and lower bits. Although 8 bit mode transfers data about twice as fast as 4 bit mode, it takes the LCD driver a longer time to process each byte than it takes to transmit the byte. So in reality, there isn’t really a noticeable difference in speed between 4 bit mode and 8 bit mode.

4 bit mode takes up only 6 GPIO pins for input/output, which makes it a popular choice for many projects:

Raspberry Pi LCD 4 bit mode

The brightness and contrast potentiometers are 10K Ohm, but 1K to 3K Ohm resistors will work as well.

Programming the LCD With C

If you’ve never worked with C programs on the Raspberry Pi, you may want to read our article How to Write and Run a C Program on the Raspberry Pi first. This will explain how to write, compile, and run programs written in C.

Install WiringPi

WiringPi is a C module that will make it easier for us to program the LCD. If you already have WiringPi installed on your Pi, you can skip this section. If not, follow the steps below to install it:

1. We can download WiringPi using Git. Your Pi may have Git already installed (if so skip this step), but if not, enter this at the command prompt:

sudo apt-get install git-core

  • Note: If you get an error installing Git, run sudo apt-get update and try it again.

2. Now download WiringPi by entering this at the command prompt:

git clone git://git.drogon.net/wiringPi

3. Enter this to change directories:

cd wiringPi

4. Run the installation script with:

./build

5. Test the installation by entering:

gpio -v

followed by:

gpio readall

Now we’re ready to start programming the LCD!

Examples

All of the examples below are stand-alone C programs, which will need to be complied by entering this at the command prompt:

gcc -o example example.c -lwiringPi -lwiringPiDev 

(change example and example.c to the name you want to use)

Then run the program with:

sudo ./example

WiringPi has it’s own pin numbering system that’s different from the Broadcom (BCM) and RPi physical (BOARD) pin numbering systems. The pin numbers used in the programs below are WiringPi pin numbers. I’m assuming you have your LCD connected like the diagrams above, but you can wire your LCD using other pins too. Just convert the physical pin numbers used on your Pi (BOARD) to WiringPi pin numbers, and use those in the programs. See here for a diagram you can use to convert between the different numbering systems.

Write to Display in 8 Bit Mode

This program shows the minimum code needed to print “Hello, world!” to the LCD:

#include <wiringPi.h>
#include <lcd.h>

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D0  29               //Data pin D0
#define LCD_D1  28               //Data pin D1
#define LCD_D2  27               //Data pin D2
#define LCD_D3  26               //Data pin D3
#define LCD_D4  23               //Data pin D4
#define LCD_D5  22               //Data pin D5
#define LCD_D6  21               //Data pin D6
#define LCD_D7  14               //Data pin D7
 
int main()
{
    int lcd;
    wiringPiSetup();
    lcd = lcdInit (2, 16, 8, LCD_RS, LCD_E, LCD_D0, LCD_D1, LCD_D2, LCD_D3, LCD_D4, LCD_D5, LCD_D6, LCD_D7);

    lcdPuts(lcd, "Hello, world!");
}

To change the pin connections, change the pin numbers defined in lines 5 to 14. You’ll need to convert the WiringPi pin numbers to the physical pin numbers of the Raspberry Pi.

The function in line 20 is used to initialize the LCD:

lcd = lcdInit (ROWS, COLUMNS, BIT MODE, LCD_RS, LCD_E, LCD_D0, LCD_D1, LCD_D2, LCD_D3, LCD_D4, LCD_D5, LCD_D6, LCD_D7);

The function lcdPuts(lcd, “Hello, world!”) prints “Hello, world!” to the screen.

Write to Display in 4 Bit Mode

To use the LCD in 4 bit mode, we need to set the bit mode number to 4 in the initialization function (line 20 below). The following code prints “Hello, world!” to the screen in 4 bit mode:

#include <wiringPi.h>          
#include <lcd.h>               
 
//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7
 
int main()
{
    int lcd;               
    wiringPiSetup();        
    lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);
               
    lcdPuts(lcd, "Hello, world!");   
}

Position the Text

By default, text is printed to the screen at the top row, second column. To change the position, use lcdPosition(lcd, COLUMN, ROW). Note that the rows are numbered 0 to 1, and the columns 0 to 15. The following code prints “Hello, world!” to the bottom row, fourth column:

#include <wiringPi.h>           
#include <lcd.h>                
 
//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7
 
int main()
{
    int lcd;               
    wiringPiSetup();        
    lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);
    
    lcdPosition(lcd, 3, 1); 
    lcdPuts(lcd, "Hello, world!");   
}

Clear the Screen

The function lcdClear(lcd) clears the screen and sets the cursor position at the top row, first column. This program prints “This is how you” for two seconds, clears the screen, then prints “clear the screen” for another two seconds:

#include <wiringPi.h>          
#include <lcd.h>               
 
//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7
 
int main()
{
    int lcd;               
    wiringPiSetup();        
    lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);
               
    lcdPuts(lcd, "This is how you"); 
    sleep(2);
    lcdClear(lcd);  

    lcdPuts(lcd, "clear the screen"); 
    sleep(2);
    lcdClear(lcd);
}

Notice how the first string is printed to the top row, second column (the default position). Then after clearing the screen, the second string is printed to the top row, first column.

Blinking Text

Using a while loop with lcdclear() and lcdputs() creates a blinking text effect:

#include <wiringPi.h>
#include <lcd.h>

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7

int main()
{
    int lcd;
    wiringPiSetup();
    lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);

    while(1){
        lcdPosition(lcd, 0, 0);
        lcdPuts(lcd, "Hello, world!");
        sleep(2);
        lcdClear(lcd);
        sleep(2);
        }
}

Press Ctrl-C to exit the program.

Cursor On/Off

The cursor is turned off by default, but you can get different styles of cursors using the following functions:

  • Underline non-blinking cursor: lcdCursor(lcd, 1)
  • Underline blinking cursor: lcdCursor(lcd, 1), followed by lcdCursorBlink(lcd, 1)
  • Blinking block style cursor: lcdCursorBlink(lcd, 1)
  • Cursor off : lcdCursor(lcd, 0)
#include <wiringPi.h>          
#include <lcd.h>               

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7

int main()
{
    int lcd;
    wiringPiSetup();
    lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);

    //lcdCursor(lcd, 0); //Cursor OFF
   
    lcdCursor(lcd, 1); //Cursor ON, underline, not blinking
    //lcdCursorBlink(lcd, 1); //Cursor ON, block, blinking    
    
    //Use both lines below to get a blinking underline/block cursor 
    //lcdCursor(lcd, 1);
    //lcdCursorBlink(lcd, 1);

    lcdPuts(lcd, "Hello, world!");
}

Print the Date and Time

This program will print the current date and time to the LCD:

#include <wiringPi.h>         
#include <lcd.h>               
#include <stdio.h>
#include <time.h>

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7

int main()
{
    int lcd;
    wiringPiSetup();
    lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);

    while(1){
        time_t timer;
        char buffer_date[26];
        char buffer_time[26];
        struct tm* tm_info;

        time(&timer);
        tm_info = localtime(&timer);

        strftime(buffer_date, 26, "Date: %m:%d:%Y", tm_info);
        strftime(buffer_time, 26, "Time: %H:%M:%S", tm_info);

        lcdPosition(lcd, 0, 0);
        lcdPuts(lcd, buffer_date);

        lcdPosition(lcd, 0, 1);
        lcdPuts(lcd, buffer_time);

        }
}

Print Your IP Address

This program prints the IP address of your ethernet connection (eth0). To get the IP of your WiFi connection, change eth0 to wlan0 in line 30:

#include <wiringPi.h>          
#include <lcd.h>                

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <unistd.h>
#include <arpa/inet.h>

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7

int main()
{
    int lcd;
    wiringPiSetup();
    lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);

    int n;
    struct ifreq ifr;
    char iface[] = "eth0"; //Change this to the network of your choice (eth0, wlan0, etc.)

    n = socket(AF_INET, SOCK_DGRAM, 0);
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name , iface , IFNAMSIZ - 1);
    ioctl(n, SIOCGIFADDR, &ifr);
    close(n);

    lcdPosition(lcd, 0, 0);
    lcdPrintf(lcd, "IP Address: ");

    lcdPosition(lcd, 0, 1);
    lcdPrintf(lcd, ("%s - %s\n" , iface , inet_ntoa(( (struct sockaddr_in *)&ifr.ifr_addr )->sin_addr)));
    return 0;
}

Custom Characters

Each LCD character is a 5×8 array of pixels. You can create any pattern you want and display it on the LCD as a custom character. This website has a nice visual way to generate the bit array used to define custom characters. Up to 8 custom characters can be stored in the LCD memory at a time.

Printing a Single Custom Character

To print a single custom character, we first define the character (see lines 12 to 19 below). Then we store the character in the LCD’s memory with the function lcdCharDef(lcd, 2, omega). The number 2 in this example is one of the 8 locations in the LCD’s character memory, which can be 0-7. Then, we print the character to the display with lcdPutchar(lcd, 2), where the number 2 in this example is the character stored in memory location 2.

#include <wiringPi.h>           
#include <lcd.h>                

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7

char omega[8] = { 0b00000,
                  0b01110,
                  0b10001,
                  0b10001,
                  0b10001,
                  0b01010,
                  0b11011,
                  0b00000};

void customChar(void);
int lcd;

int main()
{
    int lcd;
    wiringPiSetup();
    lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);

    customChar();
}

void customChar(void)
{
        lcdCharDef(lcd, 2, omega);
        
        lcdClear(lcd);
        lcdPutchar(lcd, 2);
        sleep(3);
}

Printing Multiple Custom Characters

Here’s an example of using multiple custom characters that prints the Greek letters omega, pi, and mu, plus thermometer and water drop symbols for temperature and humidity:

#include <wiringPi.h>         
#include <lcd.h>                

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7

char omega[8] = { 0b00000,
                  0b01110,
                  0b10001,
                  0b10001,
                  0b10001,
                  0b01010,
                  0b11011,
                  0b00000};

char pi[8] = { 0b00000,
               0b00000,
               0b11111,
               0b01010,
               0b01010,
               0b01010,
               0b10011,
               0b00000};

char mu[8] = { 0b00000,
               0b10010,
               0b10010,
               0b10010,
               0b10010,
               0b11101,
               0b10000,
               0b10000};
               
char drop[8] = { 0b00100,
	             0b00100,
	             0b01010,
	             0b01010,
	             0b10001,
	             0b10001,
	             0b10001,
	             0b01110}; 
	       
char temp[8] = { 0b00100,
                 0b01010,
	             0b01010,
	             0b01110,
	             0b01110,
	             0b11111,
	             0b11111,
	             0b01110};

void customChar(void);
int lcd;

int main()
{
    int lcd;
    wiringPiSetup();
    lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);

    customChar();
}

void customChar(void)
{
        lcdCharDef(lcd, 10, omega); 
        lcdCharDef(lcd, 11, pi);
        lcdCharDef(lcd, 12, mu);
        lcdCharDef(lcd, 13, drop);
        lcdCharDef(lcd, 14, temp);

        lcdClear(lcd);

        lcdPutchar(lcd, 10);
        lcdPutchar(lcd, 11);
        lcdPutchar(lcd, 12);
        lcdPutchar(lcd, 13);
        lcdPutchar(lcd, 14);

        sleep(3);
}

Scrolling Text

Scroll Right to Left

This program will scroll text onto the display from right to left, then pause, clear the screen, and loop back to the beginning:

#include <stdio.h>
#include <wiringPi.h>
#include <lcd.h>
#include <string.h>

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7

void scrollText(void);
char message[] = "Hello, world!";
int count = 0;
int j = 0;
int lcd;

int main()
{
        wiringPiSetup();
        lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);
        while(1){
            scrollText();
            }
}

void  scrollText(void)
{
        int i, n;
        int h;
        int tempSpace = 0;
        char scrollPadding[] = "                ";
        int messageLength = strlen(scrollPadding) + strlen(message);
        for (n = 0; n < messageLength; n++){h = 16; usleep(300000); printf("\x1B[2J"); if (j > messageLength)
                        j = 0;

                for (i = 0; i < j ; i++){
                        scrollPadding[h - j] = message[i];
                h++;
                }
                lcdPosition(lcd, 0, 0);
                lcdClear(lcd);
                lcdPrintf(lcd, "%s", scrollPadding);
                j++;
        }
}

Scroll Left to Right

This program scrolls text in from left to right, then pauses, clears the screen, and loops back to the beginning:

#include <stdio.h>
#include <wiringPi.h>
#include <lcd.h>
#include <string.h>

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7

void scrollText(void);
char message[] = "Hello, world!";
int count = 0;
int j = 0;
int lcd;

int main()
{
        wiringPiSetup();
        lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);
        while(1){
            scrollText();
            }
}

void  scrollText(void)
{
        int i, n;
        int h;
        int tempSpace = 0;
        char scrollPadding[] = "                ";
        int messageLength = strlen(scrollPadding) + strlen(message);
        for (n = 0; n < messageLength; n++){h = 16; usleep(300000); printf("\x1B[2J"); if (j > messageLength)
                        j = 0;

                for (i = strlen(message); i >= 0; i--){
                        scrollPadding[j - h] = message[i];
                h++;
                }
                lcdPosition(lcd, 0, 0);
                lcdClear(lcd);
                lcdPrintf(lcd, "%s", scrollPadding);
                j++;
        }
}

Print Data From a Sensor

As an example to show how to display readings from a sensor, this program prints temperature and humidity readings to the LCD using the DHT11 temperature and humidity sensor. To see how to set up the DHT11 on the Raspberry Pi, see our article How to Set Up the DHT11 Humidity Sensor on the Raspberry Pi.

The signal pin of the DHT11 is connected to physical pin 7 of the Raspberry Pi:

#include <wiringPi.h>
#include <lcd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

//USE WIRINGPI PIN NUMBERS
#define LCD_RS  25               //Register select pin
#define LCD_E   24               //Enable Pin
#define LCD_D4  23               //Data pin 4
#define LCD_D5  22               //Data pin 5
#define LCD_D6  21               //Data pin 6
#define LCD_D7  14               //Data pin 7
#define MAXTIMINGS 85
#define DHTPIN 7

int lcd;
int dht11_dat[5] = {0, 0, 0, 0, 0};

void read_dht11_dat()
{
        uint8_t laststate = HIGH;
        uint8_t counter = 0;
        uint8_t j = 0, i;
        float f; 

        dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;

        pinMode(DHTPIN, OUTPUT);
        digitalWrite(DHTPIN, LOW);
        delay(18);
        
        digitalWrite(DHTPIN, HIGH);
        delayMicroseconds(40);
        
        pinMode(DHTPIN, INPUT);

        for (i = 0; i < MAXTIMINGS; i++)
        {
                counter = 0;
                while (digitalRead(DHTPIN) == laststate)
                {
                        counter++;
                        delayMicroseconds(1);
                        if (counter == 255)
                        {
                                break;
                        }
                }
                laststate = digitalRead(DHTPIN);

                if (counter == 255)
                        break;

                if ((i >= 4) && (i % 2 == 0))
                {
                        dht11_dat[j / 8] <<= 1;
                        if (counter > 16)
                                dht11_dat[j / 8] |= 1;
                        j++;
                }
         }

        if ((j >= 40) && (dht11_dat[4] == ((dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xFF)))
        {
                f = dht11_dat[2] * 9. / 5. + 32;

                lcdPosition(lcd, 0, 0);
                lcdPrintf(lcd, "Humidity: %d.%d %%\n", dht11_dat[0], dht11_dat[1]);

                lcdPosition(lcd, 0, 1);
                //lcdPrintf(lcd, "Temp: %d.0 C", dht11_dat[2]); //Uncomment for Celcuis
                lcdPrintf(lcd, "Temp: %.1f F", f); //Comment out for Celcuis
        }
}

int main(void)
{
        int lcd;
        wiringPiSetup();
        lcd = lcdInit (2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0);
        
        while (1)
        {
                read_dht11_dat();
                delay(1000); 
        }

        return(0);
}

For temperature in Celsius, un-comment line 72, then comment out line 73.

Hopefully this has helped you get your LCD up and running with your Raspberry Pi. These are just basic examples of what you can do with your LCD, so try combining the different programs above to create interesting effects and animations. If you have any problems or questions about installing the LCD or programming it, just leave a comment below. And don’t forget to subscribe to get an email when we publish new articles. Just leave your email in the subscribe box at the top of this post. Talk to you next time!


Need an easy-to-use way to design circuits and layout PCBs?

Try EasyEDA, a free circuit design software that also offers low cost, high quality PCB manufacturing.