Raspberry Pi plus Hitachi HD44780-compatible LCD using GPIO

It's not listed on the microcontrollers page, because I class it way outside the embedded category, though within "small computing", but I also own a Raspberry Pi.

Raspberry Pi plus Hitachi HD44780-compatible LCD

The Pi, if you haven't clicked that link, is a credit-card sized computer, ARMed with a Broadcom SoC, running at 700MHz, with 256Mb of RAM, an SD card interface for holding the operating system,
composite video and analogue audio outputs, HDMI out, wired ethernet, and two USB ports.

It's also really cheap, at about £30, although you will need certain accessories to make any use of it. You'll need power, in the form of a microusb phone charger typically (look for 1 amp and above supplies), you'll need an SD card (2Gb is realistically the minimum to install the versions of linux available). You'll probably want to connect it to the internet, so you'll need an ethernet cable or a carefully selected wireless card. You may, of course, want a mouse and a keyboard- so you might also want a USB hub- be sure to get a powered one, as the Pi can't supply the full 500mA and won't power an external hard drive, say. As far as displays go, you can use an old television (repurposing old televisions in poorer places is why it has composite video out in this day and age), or a brand new (high def)
television, over HDMI.

Apart from the price, what attracted me was the GPIO header. General Purpose Input/Output pins are those on a microcontroller that you can easily set high or low, or read the state of. The physical pin can often be told to do something more specialised, such as be a USB interface, or an Analogue-to-Digital Converter. This is not something you tend to see on full-blown microprocessors, but the ARM chip used on the Pi has a foot in both camps, and the pins have been brought out from the chip onto a 2 x 13 pin header.

Cleverer people than me have written sample code for getting at the pins in C, Python, Ruby, and there's even a /sys interface exposed by the linux kernel that you can get at with simple shell scripting. I chose C.

HD44780 clone LCD dot matrix display

I wanted to make use of the 2 x 16 character dot-matrix LCD displays I had. Many of these are available from various manufacturers for a few pounds based off a clone of the Hitachi HD44780 chip. There are libraries for the Arduino for this, and I found a simpler one written for the MSP430Gxxx chips which I though was ideal to port to the Pi, as I could more or less do a find and replace on the pin-changing code. I figured this could be really handy when running the Pi "headless" (without a display) to at least see what its IP address was.

Step One - Volts

Step one on the hardware side- check it is compatible, electrically. These LCD displays, unlike the other common option, phone displays, are usually 5 volts. The Pi has a 5 volt pin, but is otherwise resolutely 3.3V, and its IO pins are not 5V tolerant.
5V appearing on those pins can easily damage the chip, possibly killing it. It's not an expensive board, but breaking one can mean (as of the moment) a long wait for a replacement.

By grounding (connecting directly to ground) the LCD module's RW pin, we can set and keep it in read-only mode. That is to say, the module only ever receives data from the Pi. We set the voltages with the Pi, and the LCD module reads them- thus we will never see "high voltage" 5V on any control or data line. You can perform level conversion with resistors, transistors, specialised ICs, but obviating them is cheaper and simpler still!

Step Two - Pin Assignments

The other important option is that it will be set up in 4 bit mode. Although we will input 8 bit characters, we will transfer those in two chunks, the high 4 bits, then the low, saving us four GPIO pins. Other than that, we need 5V power, ground, and two control lines, named EN and RS.

The selection of which GPIO pins would be
used was dictated primarily by a desire to avoid using any pins with useful and specific alternate functions, such as I2C, SPI, serial, etc. Thankfully, there were four such pins in a (numeric) row, GPIO22 to 25, slightly simplifying the code. In version 1.0.3, I allow for arbitrary pins to be used for the D4 to D7, even further increasing your chances of picking pins to your needs.

Raspberry Pi & LCD module wiring diagram

In the diagram above, red is 5V, black is ground (0V). Blue is RS, Green is EN. Orange is D4, the lowest bit of the 4 bit interface, yellow is D5, brown D6, and purple is D7. The grey
wire is the wiper of the potentiometer, setting the contrast via the VO pin. RW is the black wire (because it is grounded) between the blue and green wires of RS and EN).

Note that I have swapped the pins on the Pi used for RS and EN around, purely to make the wiring diagram easier to read. This swapping is reflected in v1.0.1 of the code.

Step Three - The code

The code came from 3 sources. There was the MSP430 code, which I found here: http://www.circuitvalley.com/2011/12/16x2-char-lcd-with-ti-msp430-launch-pad.html

The second was the code to memory-map the IO, and to set and clear GPIO pins, by Pi experts Gert and Dom, and I found it here: http://elinux.org/Rpi_Low-level_peripherals

The third, more minor source was the Arduino LiquidCrystal library, which was
used to double-check a few timing and command issues, plus it is where I copied all the command code #defines, to make my code easier to follow.

I also have to thank my good friend John Honniball for his invaluable help and coffee (and so-so tea...damn but that man needs to go on a brewing course).

One thing you may notice, comparing them is that the MSP430 has a register which you write to with the pin states- 0 for low, 1 for high. The Pi doesn't have this, and instead requires you to write to one register for setting pins (any 1s set the pin, driving it high, 0s are ignored), and one pin to clear pins (any 1s clear the pin, driving it low, 0s are ignored).

So what was a single action on the MSP430, is two on the Pi. This is no hindrance to use here, as the data pins, which we may have to set and clear to the new pattern, are not read until a rising edge (transition from low to high, 0 to 1) on the EN line.

The other change
concerns the timings. In the MSP430 code this was done with a busy loop, a for loop set for enough iterations to waste time until it is safe to perform another command. By skimping on reading from the LCD module, we cannot know when it has completed a command, so we abide by the maximum times as stated in the datasheet. These may well be much longer that necessary, but getting it wrong might mean 0.0001% of the time a bit is lost, and the display corrupts.

So given that the busy loop wasn't calibrated for the Pi, and that a Pi may be overclocked in some cases, and that it is running a multi-tasking OS where other processes may take up CPU time, I used usleep() for my timings instead.

I rewrote the function that displays integers to accommodate the larger numbers an int can hold, and to make it a bit more consistent. Pointless though, as I fall back to printf for other purposes...

The code is a work in progress, but it's hopefully useful already. When it starts up (I'
ve set it to run on boot with a line in /etc/rc.local) it displays eth0's IP address (useful for locating it to ssh into) on the top line, and on the lower line it displays the uptime, system load, the free space percentage of the root partition, and the current time of day. These cycle every four seconds. When the program is killed with a control-c (SIGINT), it clears the display before exiting. When the program received SIGTERM (such as when the system shuts down), it prints "Shutting down...". I'd like to know when it really has halted, in practice I have to wait, a guessed-at 10 seconds before removing power.

I intend to add a few more display options, such as number of logged in users, etc, and perhaps read from a button to pause the display. I like the idea of a rotary encoder, but to reliably read it would require extra hardware- not that a microcontroller is expensive...

Version 1.0.5 has added multiple displays as an option by supplying pin assignments to lcdInit(), after I saw what
Gordon Henderson had done- as he pointed out, the modules only pay any attention to their data lines when the enable pin is blipped, so all you need for any extra displays, is one extra pin for a dedicated enable pin. All the other pins can be shared. That's what my main.c does, although it merely prints a static message to the second display. If you haven't got a second display, don't worry, even if you leave the code in, no harm will come, since the software does no reading of the modules.


This was compiled on the Pi running Raspbian Wheezy (although works fine with Debian Squeeze too). If you have that installed, you should have a working C build environment. Just unpack the .tar.gz ("tar zxf lcd-1.0.5.tar.gz" will do the trick) file, enter the created directory, and type "make". This will create an executable called "lcdinfo". Wire up your LCD module if
you haven't already, as it is only initialised once, when the program starts. Because it accesses the GPIO pins, you need to run it as root, the superuser. Typing "sudo ./lcdinfo" will achieve that.

Download the code:

Pi + LCD module Fritzing file

31 thoughts on “Raspberry Pi plus Hitachi HD44780-compatible LCD using GPIO

  1. Hi,

    first of all thank you very much for building such a great project!

    Two questions:
    1. Is it possible to “feed” my lcd with my “own” content – as example Artist-Name and Song-Title?
    2. Is it possible to “feed” a display with 16×4 characters? (like this one: http://bit.ly/O9MPaP)

    Best wishes from austria


    1. Thanks Bjoern.
      The answer is yes to both your questions- though you’ll need a different main.c with your requirements. The display seems like it is HD44780 compatible.
      The code in lcd.c has been tested by someone else for the four line variant- I don’t have one. Currently, as I say, main.c only produces two lines of data, but you can move the cursor to whatever line you like.
      Now, as for your first question, how you go about it depends on where the data comes from- I assume a program, some sort of digital jukebox.

      The module displays what you feed it, and that stays there as long as the power stays on. So you *could* fire off a program each time you need to update. What might be superior is a daemon that takes in requests and displays them. It remains running all the time. The requests could come from any number of Inter Process Communication methods, such as full-on TCP/IP
      sockets, maybe a named pipe? The former is easier, and if you know only one process fires off the requests, and only at reasonable, non-overlapping intervals, that’s not a bad place to start.

      1. hi, any chance your working on a daemon that can take the playin file name, file current runtime and file length time and display onto the screen and update every second or so?

        i think alot of people would be interested in this in a 2 and 4 line versions with other “optional” information.

        especially if its compatible with different Pi media centers

  2. i was hoping to use one of these screens for “video file name” and “current playtime / runtime length”

    problem is i cant program so although the guide is helpful for the hardware i have no idea what im doing program wise 🙁

    /sigh i need a teacher lol

    1. It would be weird if I didn’t point you at the City of Bristol College…
      What you meant to say was “I can’t program yet.” One of the main stumbling blocks people have is finding the right sort of motivation, and nothing motivates like fixing a problem of your own. So make use of it!

      For file name, you’d either want to get something wider that the most common 16×2 modules, or have the ability to scroll the top line of display. One issue is getting the data out from the media player- there’s no generic solution to this that I’m aware of, so you’d either have to hack on the media player or find one that can be persuaded to spit out status on a regular basis, which can be munged into a format for the display daemon…hmmm.

      1. lol i just saw your reply to my comment as i replied to the one above.

        yes, i was hoping that by being able to look at the code that would make this possible (i.e a finished daemon or program) that i could understand what commands do what as i’ve tried reading on how to program and frankly its overwhelming.

  3. been having troubles getting this working. can you list up the dependancies required for the code you posted to run on?

    1. What’re your errors? It might have slipped my mind, but am pretty sure this compiled on a totally fresh copy of Wheezy. It doesn’t link to any external libraries, either, just the source files you get with this.

  4. Great project! I plan to modify the code to display unread emails or anything of that sort. Here is my screen in action. Not the prettiest wiring, but it is my first go at this kind of thing.
    Thanks for everything!

  5. How is the board called, on which your LCD is mounted? Where do I get it?

    Thanks for this great article!

    1. Do you mean what is it called? No idea. These things are totally generic these days, pretty much any 16 character by 2 line display is going to be compatible (especially if it has 16 pins), but if you want to be sure, look for mention of HD44780 compatibility.

      Here’s the exact white-text-on-blue module that is featured in some of the images. If you want to display text, as opposed to mainly numbers, you might decide to get a wider display. 20 characters is another common width, and I have one here that’s 40, although then you end up needing a wide enclosure to mount it in! You can always use some sort of scrolling technique to show more, albeit not all at once.

      1. Hi!
        Thanks for your answer. I’ve meant the white board. Any idea where I could get something like this?
        I’ve searched the web for it but I didn’t find one.


        1. Ah, breadboards.

          Make sure you get one big enough to allow you the room for the contrast adjustment potentiometer. And having voltage rails is very useful. Thankfully, this is a standard feature on all but the tiniest boards.

          You will also need some prototyping/breadboard wire. This is usually either solid core, or with solid pin ends on stranded wire. You need that to be able to insert it into the hole (stranded wire will bend instead of insert). And to connect to the Pi, you want female connectors, often called Dupont connectors. You can get wires that are female one end, and male on the other, which is very handy for breadboarding with the Pi (or the STM32 ARM boards I also talk about). Or you can make your own- chop a female-to-female wire in half, do the same with a male-to-male, and solder them together- just make sure you insulate the solder joints with tape or heatshrink tubing, or you might accidentally short a connection when plugging wires in, or moving the board about. The Pi would hate that!

          Solid core is neater, generally,because it stays where you put it, but I use both. A cheap source of lots of suitable solid core can be old ethernet cables, which are becoming giveaway items now that most people use wireless. Each cable contains 8 often solid-core wires, and so one 20m cable might keep you going for life! Ribbon cables are useful for doing board-to-board connections, and help things stay neat but flexible.

          you are happy with your design, you might want to make it a bit more permanent- breadboards don’t do so well in transport, wires come loose, etc. Then you can look at making your own PCBs or using veroboard and soldering your circuit. But you can go a long way experimenting with breadboard!

  6. Thanks for making this project 🙂

    I’ve run into some trouble though. LCD is a 16×4 but it seems like your code is updated for that, do I need to change anything in the main.c file for using 16×4?

    As soon as I run “./sudo lcdinfo” my LCD turns from two rows of solid squares (row 2 and 4) too two dim rows with half a row extra on top of each row (row 2 + ½ of 1 and 4 + ½ of 3) which is really weird.

    Any ideas on how I can debug this? I’ve looked over the wires and they should be correct.

    1. I don’t have any good advice, I’m afraid. I’ve not seen that behaviour. I’ve seen a simple flashing cursor caused by having the EN and RS lines transposed. I’ve seen totally black or totally invisible glyphs caused by bad contrast setting.

      I have a report of 16×4 working, so it’s not inherently that. If you could get the datasheet for your module, you might go comparing timings, particularly with regard to delays. I wonder if there is a simple way to increase all timings considerably, to eliminate are marginal timings? Worst case, do a find & manual replace on waitLcd(). All the modules I’ve seen have pins 1-16 in the same order, but that is worth double-checking. Are you definitely seeing 5V where you should? I doubt it is an issue, but theoretically you could be drawing too much current- by default it is limited to 2mA I believe. But that should be plenty.

      If you find out
      the reason, please let us know!

  7. Hi Phil,

    Brilliant project and some nice code.
    I am so not a programmer, but even I can figure out what the code does.

    I can confirm it also works with a 4X20 display.
    There are things I’d like the code to do for my little project, but i am not clever enough to code it myself.
    I’d love to be able to send text to TCP-socket. Maybe one day a future version your program will do that? 🙂

    Kind regards,


  8. PILib is a C++ library that implements an interface for HD44780 based LCD displays for the Raspberry PI. It can be downloaded at PILib . It is pretty easy to use. Source code and complete documentation are included. A “Hello World” is as easy as:

    #include “HD44780.h”

    int main() {

    CTTPI::HD44780 module(1, 16);
    // EN RS RW D4 D5 D6 D7
    module.initialize(17, 18, 0, 21, 23, 24, 25);
    module.writeString(“Hello world!”);
    return 0;

  9. Thanks, You open my mind and answer my questions about control the gpio. Now I will initiate experiences. I hope be suscesfull and post the results.

  10. thanks for the project, someone know, how can i create a personal characters for lcd in phyton? I try to create the degrees simbol (°) with no result…

  11. Hi,

    Awesome tutorial and working like a charm on 20×4 🙂

    How do I run this program as a service, auto start and stop with /etc/init.d/…??


    1. The easiest way is via rc.local, one of the remaining, old-fashioned scripts, and more or less the last thing the init sequence kicks off.
      Classier would be the full Sys V treatment, making its own control file, but I haven’t bothered so far, as the only cleanup it needs to do is to blank the screen or say a goodbye message, which it does on response to getting a TERM signal, which init sends as a matter of course to handle all the other processes not already shut down.

      What would be handy, and something doing it “right” would assist with, is to provide some kind of runlock. Right now it is possible to run two copies of the program, which goes predictably awry.

  12. Hi,
    probably the best how to i have read involving the RASPBERRY Pi, I am looking into LCD additions to read the temperatures at the sensors on the Pi which you can see under system headings on the main screen of XBMC but no where else.
    I am thinking that lmsensors /Psensor could be run in the background and displayed on an LCD screen (rather like the old Proliant compaq servers did.)

    I know how to get this up on a LINUX main screen (VDU) but I am lost on how to hijack that info to the LCD screen for display whilst the main screen displays music info /videos etc.
    Any pointers etc most welcome………..

  13. Huge thanks for this. I was struggling to anything to work but this was clear and I loved the README. After a few wire wiggles it worked brilliantly.

  14. Hello,

    whats the Licence of your Sourcecode?

    I ask, because I intend to cannibalise it for my own Project which I would like to licence under GPL


    1. Hi Sven,

      I haven’t given any thought to a licence, I generally like the idea of the GPL, because of how it perpetuates itself down the forks and revisions, making sure everyone can continue to get the benefit. So in your case, feel free to use it given that you will be GPLing it. If you wish to link back to this article, that would be appreciated, but not necessary. Certainly I would like to see what you do with it!

  15. Hi. Is the procedure same if I am connecting 4×20 lcd module ?
    I am planning to display temperature along with date and time.
    I am using DS18B20 for temperature sensing.
    Can you help me with the source code ?

Comments are closed.