I2C 20×4 LCD character display on a #RaspberryPi

Take a look at this more up-to-date post which uses a Python library to do the necessary.

I bought this display from Hobby Components:

It is very similar to the Sainsmart 2004 LCD display and the pin mappings are the same (it’s a J204A LCD board).

I came across this thread on the Foundation forum on which member ‘natbett’ gave out some code to drive the display. Fortunately, this code works (after trying out 4 other examples found elsewhere). Here is the code for posterity:

The first file is called ‘i2c_lib.py’

import smbus
from time import *

class i2c_device:
   def __init__(self, addr, port=1):
      self.addr = addr
      self.bus = smbus.SMBus(port)

# Write a single command
   def write_cmd(self, cmd):
      self.bus.write_byte(self.addr, cmd)

# Write a command and argument
   def write_cmd_arg(self, cmd, data):
      self.bus.write_byte_data(self.addr, cmd, data)

# Write a block of data
   def write_block_data(self, cmd, data):
      self.bus.write_block_data(self.addr, cmd, data)

# Read a single byte
   def read(self):
      return self.bus.read_byte(self.addr)

# Read
   def read_data(self, cmd):
      return self.bus.read_byte_data(self.addr, cmd)

# Read a block of data
   def read_block_data(self, cmd):
      return self.bus.read_block_data(self.addr, cmd)

The second file is called lcddriver.py

import i2c_lib
from time import *

# LCD Address
ADDRESS = 0x27

# commands

# flags for display entry mode

# flags for display on/off control

# flags for display/cursor shift

# flags for function set
LCD_2LINE = 0x08
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00

# flags for backlight control

En = 0b00000100 # Enable bit
Rw = 0b00000010 # Read/Write bit
Rs = 0b00000001 # Register select bit

class lcd:
   #initializes objects and lcd
   def __init__(self):
      self.lcd_device = i2c_lib.i2c_device(ADDRESS)


      self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
      self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)

   # clocks EN to latch command
   def lcd_strobe(self, data):
      self.lcd_device.write_cmd(data | En | LCD_BACKLIGHT)
      self.lcd_device.write_cmd(((data & ~En) | LCD_BACKLIGHT))

   def lcd_write_four_bits(self, data):
      self.lcd_device.write_cmd(data | LCD_BACKLIGHT)

   # write a command to lcd
   def lcd_write(self, cmd, mode=0):
      self.lcd_write_four_bits(mode | (cmd & 0xF0))
      self.lcd_write_four_bits(mode | ((cmd << 4) & 0xF0))

   # put string function
   def lcd_display_string(self, string, line):
      if line == 1:
      if line == 2:
      if line == 3:
      if line == 4:

      for char in string:
         self.lcd_write(ord(char), Rs)

   # clear lcd and set to home
   def lcd_clear(self):

The last file, which is the example script, is called lcd.py

import lcddriver
from time import *

lcd = lcddriver.lcd()

lcd.lcd_display_string("Hello world", 1)
lcd.lcd_display_string("My name is", 2)
lcd.lcd_display_string("picorder", 3)
lcd.lcd_display_string("I am a Raspberry Pi", 4)

It displays 4 lines of text.

My next task is to connect this up to the Picorder version 3!

116 comments for “I2C 20×4 LCD character display on a #RaspberryPi

  1. pi@raspberrypi ~/projects/python/lcd $ python lcd.py
    Traceback (most recent call last):
    File “lcd.py”, line 4, in
    lcd = lcddriver.lcd()
    File “/home/pi/projects/python/lcd/lcddriver.py”, line 56, in __init__
    self.lcd_device = i2c_lib.i2c_device(ADDRESS)
    File “/home/pi/projects/python/lcd/i2c_lib.py”, line 7, in __init__
    self.bus = smbus.SMBus(port)
    IOError: [Errno 2] No such file or directory
    pi@raspberrypi ~/projects/python/lcd $

    i get the following error, can you share any insight?

  2. I have a 20×4 I2C SAINSMART display which does want to not work with RPI.
    I’have tried to power it with 5V (default voltage), 3.3V, convert I2C bus voltage with “4-channel I2C-safe Bi-directional Logic Level Converter – BSS138”, nothing.
    It’s live with his address (0x3f) but using different library nothing compare on display.

  3. Using raspbian wheezy 23-09-25 model B 512mb. Using LCD 20×4

    Traceback (most recent call last):
    File “lcd.py”, line 1, in
    import lcddriver
    File “/home/pi/lcddriver.py”, line 1, in
    import i2c_lib
    ImportError: No module named i2c_lib

    Ic2_lib.py still in home/pi directory.

    Can you help me?

  4. Never mind! I got lcd 20×4 working. I mistyped wrong filename ic2_lib.py instead of i2c-lib.py

    I;m very happy that my lcd 20×4 working.
    Thank, Michael Horne for posting source code.
    Many thanks!

  5. How do you clear all lines?
    lcd = lcddriver.lcd()

    lcd.lcd_display_string(“Hello world”, 1)
    lcd.lcd_display_string(“My name is Supra”, 2)
    lcd.lcd_display_string(“picorder”, 3)
    lcd.lcd_display_string(“I am a Raspberry Pi”, 4)
    while True:
    distance = display_measure()
    lcd.lcd_display_string(“Distance : %.1f cm” % distance, 2)

  6. Never mind. I solved problem by adding brace bracket.

    Many thanks!
    We wish you A Merry Christmas and a Happy New Year to You.

  7. hello I’m trying to turn on and off the LCD light with these commands you, but wanted a python function to do, you know?

    pi@RaspFishServ ~/lcd $ sudo i2cset -y 1 0x27 0x08 0x00 – OFF
    pi@RaspFishServ ~/lcd $ sudo i2cset -y 1 0x27 0x08 0x08 – ON

  8. hi fish, using the above code as an example i added a function to lcddriver.py:

    def backlight(self, state): # for state, 1 = on, 0 = off
    if state == 1:
    elif state == 0:

    then in lcd.py just put “lcd.backlight(1)” to turn it on, an use 0 to turn it off


  9. Hi, just popping in to say thanks, I mean, thanks a lot !

    I had this page bookmarked for quite a while, and today I finally connected my 20×4 to my raspi : everything worked straight away using your code, and the comments are really useful too.

    Thanks for making this available !

      • thank you,
        i have modified lcd.py to read text from file, but i am not good in python….

        import lcddriver
        from time import *

        lcd = lcddriver.lcd()

        f = open(‘text.txt’, ‘r’)

        mystring = f.readline()
        mystring = mystring.rstrip(‘n’)
        lcd.lcd_display_string(mystring, 1)

        mystring = f.readline()
        mystring = mystring.rstrip(‘n’)
        lcd.lcd_display_string(mystring, 2)

        mystring = f.readline()
        mystring = mystring.rstrip(‘n’)
        lcd.lcd_display_string(mystring, 3)

        mystring = f.readline()
        mystring = mystring.rstrip(‘n’)
        lcd.lcd_display_string(mystring, 4)


  10. Hi again,

    I have made a few tweaks to lcddriver.py, mostly cosmetic.
    There is one addition that may be of use to others though : the ability to control the backlight when writing to the screen.

    The code is available here, feel free to pilfer 🙂

  11. Many Thanks For the above code i got it to display no problem
    How do i get it to display time and date 🙂
    Thanks again

  12. After THREE DAYS of trying every python script that Google could find, your example above worked!!!
    Thank you for now giving me the inspiration to keep moving toward my goal! It’s nice when things finally work.

  13. recantha, thanks for driver – it really works.

    Is there a way to get some features from Adafruit_CharLCD like positioning cursor before printing, turning underline cursor on/off, (auto)scrolling? Actually I’d like to use a simple 20×4 in Scratch though Pridopia (children, you know) – but they use Adafruit’s library which does not suit for simple displays. Maybe you suggest your library to Adafruit and/or Pridopia?

  14. hello Sir,
    thank you for such nice tutorials.

    I am completely noobs in Raspi and electronics 🙁 ; I have a question
    I soldered Adafruit 16×2 LCD -I2C on Raspi and followed tutorial and works nicely ;
    now I want to blink a single LED and show the count on LCD(I2C).. so I soldered LED on GPIO pins which are resurrecting from LCD …..and now nothing works???

    so what i did wrong?
    was soldering LED on GPIO while using I2C LCD is wrong ??? or i need some libraries to get it work?(i.e. I2c LCD and LED input)?

    Please help!

  15. Thanks for this! I can confirm it works on my I2C LCD (same as yours), running with RPi and Wolfson audio card…
    But, is there any way to add autoscrolling on long lines?

  16. I managed to cobble up a text scroller from various snippets around the net, which works withthe above library (including the backlight command…).
    Here’s my line scroller – this one refers to line 1 of the LCD:

    ====> start of code
    import lcddriver
    from time import *

    lcd = lcddriver.lcd()

    str_pad = ” ” * 20
    my_long_string = “this is a long string that needs to scroll”
    my_long_string = my_long_string + ” ” # add space at the end
    lcd.lcd_display_string(my_long_string[:19], 1) # initially show just the first part
    for i in range (0, len(my_long_string)):
    lcd_text = my_long_string[i:(i+19)]
    lcd.lcd_display_string(my_long_string[:19], 1)
    ===> end of code

    Hope this saves some time to others who might find this page 🙂

    • Hi Denis. Can you send me your code to my email about scroll ? i copied it, but works in part. I think that is something about python Indentation.


  17. Thanks for this code. But some questions.
    I have some basic knowledge in coding.
    I tried the code from Matt to controll the Backligth ist works.
    But when i change the LCD-Command to blinking courser or someting else, the Backlight also switch of.
    I use this LCD http://www.ebay.de/itm/IIC-I2C-TWI-2004-Serial-Blue-LCD-Module-For-Arduino-und-Raspberry-Pi-CP02015-J72/281353509316?_trksid=p2045573.c100033.m2042&_trkparms=aid%3D111001%26algo%3DREC.SEED%26ao%3D1%26asc%3D20131017132637%26meid%3D8707713610718416030%26pid%3D100033%26prg%3D20131017132637%26rk%3D1%26rkt%3D4%26sd%3D281353509316 with out an levelshifter for the i2c.
    I dont can imagine how to work with the code, i really confused. Have somebody any additional examples?

  18. thanks for the code, but some questions.
    i tried several display commands e.g. LCD_CURSORON.
    And it is ever the same result, the backlight swtichs off.
    I have also seen there is the 0x00 value. How should this work for MOVELEFT, NOBACKLIGHT?
    I tried this:
    def cursor(self, state):
    if state == 1:
    elif state == 0:

    I use the Pi B+ with an 20×4 I2C LCD with out an Levelshifter.

  19. What code i need add to lcddriver.py so was possible do scrolling and write (x,y,line)
    Someone have a updated version of it ?

    I have a 20×4 display working with this code already.


  20. Thaks alot for this script! It works fine.

    Can you help me with an script for C?
    My main program is in C and I do:

    system(“sudo pyhton lcd.py -t text”)

    to use the LCD with my own text.
    Do you know a runnig script for this LCD?


    • I don’t I’m afraid – I’m not very good at C. Try the Raspberry Pi Foundation forum – there’s plenty of bright people on there who might be able to help 🙂

  21. Hi
    i try to get my SaintSmart 20×4 LCD to work.
    I have activated the modules and installed smbus and i2c-tools.
    When i do #i2cdetect -y 1 i got:
    0 1 2 3 4 5 6 7 8 9 a b c d e f
    00: — — — — — — — — — — — — —
    10: — — — — — — — — — — — — — — — —
    20: — — — — — — — — — — — — — — — —
    30: — — — — — — — — — — — UU — — — —
    40: — — — — — — — — — — — — — — — —
    50: — — — — — — — — — — — — — — — —
    60: — — — — — — — — — — — — — — — —
    70: — — — — — — — —

    When i start lcd.py i got the following error:

    Traceback (most recent call last):
    File “./lcd.py”, line 4, in
    lcd = lcddriver.lcd()
    File “/home/pi/i2c/lcddriver.py”, line 58, in __init__
    File “/home/pi/i2c/lcddriver.py”, line 82, in lcd_write
    self.lcd_write_four_bits(mode | (cmd & 0xF0))
    File “/home/pi/i2c/lcddriver.py”, line 77, in lcd_write_four_bits
    self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
    File “/home/pi/i2c/i2c_lib.py”, line 11, in write_cmd
    self.bus.write_byte(self.addr, cmd)
    IOError: [Errno 5] Input/output error

    Need help


  22. Thanks for the driver! Works fine. I want to share python code for printing the alphabet of the lcd, I know there are ‘asian’ and ‘normal’. I have the asian one. So you can see and pick up symbols you like for later use with lcd.write(number of symbol, Rs). Im using only two first lines becouse i have only two. if you have only one then remove section for second line.
    # -*- coding: utf-8 -*-
    import lcddriver
    Rs = 0b00000001 # Register select bit
    lcd = lcddriver.lcd()

    #Print 1602 lcd alphabet, start from position:

    #End at:
    while (x < 256):
    # There can be 'pages' of empty addresses
    # so keep hitting enter.
    # Wait for enter-key:
    raw_input("Press Enter")

    #First line
    while (x < (start+16)):
    lcd.lcd_write(x, Rs)

    while (x < (start+32)):
    lcd.lcd_write(x, Rs)

    #Show numbers of symbols
    print start, "-" ,x-1

  23. I have the same problem as the user Marcel Ro. i get the following errors
    Traceback (most recent call last):
    File “lcd.py”, line 4, in
    lcd = lcddriver.lcd()
    File “/home/pi/lcddriver.py”, line 58, in __init__
    File “/home/pi/lcddriver.py”, line 82, in lcd_write
    self.lcd_write_four_bits(mode | (cmd & 0xF0))
    File “/home/pi/lcddriver.py”, line 77, in lcd_write_four_bits
    self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
    File “/home/pi/i2c_lib.py”, line 11, in write_cmd
    self.bus.write_byte(self.addr, cmd)
    IOError: [Errno 5] Input/output error

    i have installed python-smbus, what else can be wrong?

  24. How do you connect the display? I have returned my LCD to the seller, but a few days later i found a comment at Amazon where somebody wrote, that he has to connect the display to 3,3v power at the Pi and not to 5v and the display works. Conected to 5V it won’t.

    • He probably used extra I2C chip that connected to 3.3V Pi.
      But what I did. I connected LCD to external power supply(5V). And that ground connected to both PI and power supply. So I merely used I2C with lcd to 5V.

  25. @ Eric and Marcel Ro.

    Can you delete function?
    def write_cmd(self, cmd):
    self.bus.write_byte(self.addr, cmd)

    After you deleted it. Just type manual on keyboard, but do not copy and paste it. But same indentation.

    Hope that help!

  26. Hi,
    Has anyone written a function to make custom characters for this library yet?
    I’m trying to do something about it, so if something already exists, there’s no point in reinventing the wheel 🙂


  27. OK, again I did everything myself 🙂

    But, since copy-paste from this blog leaves a lot to be desired, I’ve put the new library on github/gists, plus one example Python script, which will make it easier to understand and use the custom character function.
    Again, 90% of it all was lifted from other web sites and publicly accessible code, so I really can’t take much credit for this, except for making it publicly available and writing a few comments and some examples.
    Hope this helps others!
    The new, modified library, which combines i2c_lib.py and lcddriver.py into a single library/file, is called “RPi_I2C_driver.py”, and there’s also an example of what can be done with it.
    I’ll probably also add some scrolling examples, while I’m at it 🙂
    The URL is:

  28. Some picture for lcd, you can modify it…

    pi@raspberrypi ~ $ cat ./bin/startscript.sh
    verzia=$(uname -r)

    cat > /home/pi/lcd/text.txt <
    / )\ Raspberry Pi
    ,/_/_/ $verzia
    cd /home/pi/lcd
    sudo python lcd.py
    cd – >/dev/null
    pi@raspberrypi ~ $ uname -r
    pi@raspberrypi ~ $

  29. I wonder if anyone can help when a execute the file a get this error.

    sudo python lcd.py

    Traceback (most recent call last):
    File “lcd.py”, line 1, in
    import lcddriver
    File “/home/pi/lcddriver.py”, line 1, in
    import i2c_lib
    File “/home/pi/i2c_lib.py”, line 35

  30. Really good tuto.

    For some reason I can barely see the text on the lcd and my lcd works fine on with my arduino…

    Any ideas?

      • Sorry for bothering you with this.

        Your question help me in a strange way. I try my lcd on my arduino just to make sure it was properly working and it was the case. So I reconnected my lcd to my pi and then I realized it was connected to my 3.3v pin instead of 5v… And now everything works fine.

        • Sandrine,
          Easy way for you. You don’t have to test in Arduino
          You can test:
          sudo i2cdetect -y 1
          This tell you when you see address—meaning your lcd is working

  31. Hello,

    Thanks for your help. Your script worked on my project. I can put static words to my lcd but i couldnt connect it to lcdproc or mpdlcd. How can i get nowplaying info on my screen. I guess i have to edit something on LCDd.conf file.
    I hope someone help me

      • When i run lcdproc my screen’s backligt flicker. But no text on screen. only light switches on and of. no info shown on screen.
        my LCDd.conf


  32. Yes i enabled i2c and i have executed your lcd.py script succesfully. It displayed Hello world without any problem. I can edit your lcd.py file and could display any static text on my lcd. but cant make any dynamic text like lcdproc and mpdlcd script output. like i said before its backlight turns on and off again and again and no words shown on screen. my only goal is display mpd info on my screen

    • Did u upgrade mplcd 0.4.3?
      did u install lcdproc and mplcd?
      U don’t needed to do lcdproc.conf.
      U will have to edit /etc/mpdlcd.conf

      • Hi Supra
        Thanks for the link!!
        I installed the software and the library and now I’m stuck with the following error:

        Traceback (most recent call last):
        File "main.py", line 6, in
        from LineController import *
        File "/home/pi/raspi-mpd-lcd/LineController.py", line 184, in class MPDLine(TextLine):
        File "/home/pi/raspi-mpd-lcd/LineController.py", line 185, in MPDLine from mpd import MPDClient
        ImportError: No module named 'mpd'

        Have you any idea on how I can bypass this?
        Thanks again

  33. I’ve tried everything and just can’t get this working. I have a RPi2 and 20×4 LCD with I2C converter on the board. It’s connected to 5v and the backlight is on – so power is getting through.

    Whatever I try the display stays completely blank. I can’t get anything to display. I2C is enabled and address of board is hex 3f so it’s seen my the RPi.

    I’m completely stuck now … any help would be very much appreciated.

    • Try twiddling the trimming potentiometer on the back – it might be working but not turned up enough for the text to display.

  34. Haha. I’m such a dork.

    Michael – you, sir, are a genius. Who’d have thought that it would be the simple thing stopping it working. I tried every complicated way imaginable and it was the bl&*dy brightness setting !

    Thank you very much. Off to start again with Jessie now – which will no doubt stop it working 😉

  35. Greetings.

    So, I got the LCD working and even displaying temperatures from my 2 probes, complete with a degree symbol. Whoopee.

    Thought it might be fun to indicate whether the temp was going up or down so I made a couple of arrows. But I can’t get them to display after the temp reading.

    I’m defining them then applying to upA and downA : upA = unichr(0) etc.

    Temp is sent to LCD like this :

    lcd.lcd_display_string(‘Desk: %s %sC ‘ % (temp_1,degree), 2)

    But where, and how, do I add my upA bit. Nothing seems to work.

    lcd.lcd_display_string(‘Desk: %s %sC %s’ % (temp_1,degree, upA), 2)

    It’s a string so why doesn’t the above line work ?

    Another silly error on my part most like. Any thoughts anyone ?

  36. Has anyone gotten cursor to work ? I can’t figure out how to turn it on or move it to the desired position.

    • LCD_CURSORON = 0x02
      LCD_CURSOROFF = 0x00
      LCD_CURSORMOVE = 0x00

      Scroll up to see example of how to turn backlight off
      U should do in search engine

  37. Hi! Thanks for sharing this code, works great! What would be the easiest way to adjust backlight strength programatically? It’s easily done with a resistor, but could it be done via PWM?

    • Used potentiometer. If u know value from potentiometer, then u can used resistor with 5 bands resistor

  38. What code do I need in my script to get the cursor to turn on/off. Tried supras comment but cant get it to work ??

    • U will have to write own procedure event in lcddriver library
      for example: How to turn off backlight. The code was written by fpp. See above february 2nd, 2014

      # turn backlight on and off
      def backlight(self, state): # state: 1 = on, 0 = off
      self.lcd_device.write_cmd( (LCD_NOBACKLIGHT,LCD_BACKLIGHT)[state])

      I haven’t tried it yet, because I’m getting new one raspberry pi 3.

    • me sirvio el ejemplo que tengo para llamar el lcd.backlight(0) es:

      import lcddriver
      from time import *
      import i2c_lib
      lcd = lcddriver.lcd()


      lcd.lcd_display_string(“*Control de Acceso*”,2)

      lcd.lcd_display_string(“* Iniciando… *”,3)


      lcd.lcd_display_string(“* Se ah iniciado *”,2)

      lcd.lcd_display_string(“* el sistema *”,3)


      esto es en raspberry pi 2

      ingresar las lineas en el archivo lcddriver.py
      # turn backlight on and off
      def backlight(self, state): # state:1=on, 0=off

      thanks users fpp and supra

  39. Hello again, my question is how to enter the command to move the text from left to right or vice versa
    LCD_MOVERIGHT = 0x04
    LCD_MOVELEFT = 0x00

    as the income and I do not see a pacman wanted to do work that was charging indicator system , so to speak

    • Added 4 buttons: up, down left and right. Or u can use joystick or whatever u can.
      I haven’t attempt yet, because of moving to raspberry pi 3.

  40. I’m using 20×4 lcd.
    Absolutely, commands can not drive.
    U needed to do this with extra spaces
    For instance:
    lcd.lcd_display_string(” RASPBIAN JESSIE”, 1). Depended number of characters.

    Right now I’m setting up for RPI 3.

  41. i am looking for help i can not get my LCD to work. i have taken the code and pasted it into python 3 but i get this error…..

    Traceback (most recent call last):
    File “/home/pi/RPi_I2C_driver.py”, line 1, in
    import smbus
    ImportError: No module named ‘smbus’

    if anyone could help me that would be great. thank you.

  42. Hello
    Happy new year
    With your code I have got my LCD running and showing pre defined strings, thank you.
    But l have failed to get it to display the value of a variable ‘latitude’, usually getting an error about not enough arguments for format string.
    My script contains
    print latitude
    And the value of latitude from gpsd is displayed in the terminal window correctly.
    But how do I get it to display on the LCD screen please?

  43. Hi Friend,
    I would like to do text movement on LCD 20×4.
    Can anyone help to show me how to write the codes?
    Appreciate in advance.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.