Raspberry Pi I2C LCD Set Up and Programming - DHT11 I2C LCD Output

How to Setup an I2C LCD on the Raspberry Pi

Connecting an LCD to your Raspberry Pi will spice up almost any project, but what if your pins are tied up with connections to other modules? No problem, just connect your LCD with I2C, it only uses two pins (well, four if you count the ground and power).

In this tutorial, I’ll show you everything you need to set up an LCD using I2C, but if you want to learn more about I2C and the details of how it works, check out our article Basics of the I2C Communication Protocol.

BONUS: I made a quick start guide for this tutorial that you can download and go back to later if you can’t set this up right now. It covers all of the steps, diagrams, and code you need to get started.

There are a couple ways to use I2C to connect an LCD to the Raspberry Pi. The simplest is to get an LCD with an I2C backpack. But the hardcore DIY way is to use a standard HD44780 LCD and connect it to the Pi via a chip called the PCF8574.

The PCF8574 converts the I2C signal sent from the Pi into a parallel signal that can be used by the LCD. Most I2C LCDs use the PCF8574 anyway. I’ll explain how to connect it both ways in a minute.

I’ll also show you how to program the LCD using Python, and provide examples for how to print and position the text, clear the screen, scroll text, print data from a sensor, print the date and time, and print the IP address of your Pi.

If you don’t have an I2C enabled LCD or a PCF8574, these tutorials will show you how to connect an LCD with the GPIO pins:

Here’s the video version of this tutorial, where I go through the setup and show all of the programming examples below:

Connect the LCD

I2C (inter-integrated circuit) is also known as the two-wire interface since it only uses two wires to send and receive data. Actually it takes four if you count the Vcc and ground wires, but the power could always come from another source.

Connecting an I2C Enabled LCD

Raspberry Pi I2C LCD - I2C Backpack LCD

Connecting an LCD with an I2C backpack is pretty self-explanatory. Connect the SDA pin on the Pi to the SDA pin on the LCD, and the SCL pin on the Pi to the SCL pin on the LCD. The ground and Vcc pins will also need to be connected. Most LCDs can operate with 3.3V, but they’re meant to be run on 5V, so connect it to the 5V pin of the Pi if possible.

Connecting an LCD With a PCF8574

Raspberry Pi I2C LCD - PCF8574

If you have an LCD without I2C and have a PCF8574 chip lying around, you can use it to connect your LCD with a little extra wiring. The PCF8574 is an 8 bit I/O expander which converts a parallel signal into I2C and vice-versa. The Raspberry Pi sends data to the PCF8574 via I2C. The PCF8574 then converts the I2C signal into a 4 bit parallel signal, which is relayed to the LCD.

Use the following diagram to connect the LCD to the Raspberry Pi via the PCF8574:

Raspberry Pi LCD - I2C Connection Diagram
  • R1: 10K Ohm resistor
  • Potentiometers: 10K Ohms, but can be substituted with 1K to 3K Ohm resistors

In the diagram above, the blue wire connects to the Raspberry Pi’s SDA pin. The yellow wire connects to the Pi’s SCL pin.

Enable I2C on the Pi

Before we get into the programming, we need to make sure the I2C module is enabled on the Pi and install a couple tools that will make it easier to use I2C.

Enable I2C in raspi-config

First, log in to your Pi and enter sudo raspi-config to access the configuration menu. Then arrow down and select “Advanced Settings”:

Raspberry Pi LCD - I2C Connections - sudo raspi-config

Now arrow down and select “I2C Enable/Disable automatic loading”:

Raspberry Pi LCD - I2C Connections - sudo raspi-config enable i2c

Choose “Yes” at the next prompt, exit the configuration menu, and reboot the Pi to activate the settings.

Install I2C-tools and SMBUS

Now we need to install a program called I2C-tools, which will tell us the I2C address of the LCD when it’s connected to the Pi. So at the command prompt, enter sudo apt-get install i2c-tools.

Next we need to install SMBUS, which gives the Python library we’re going to use access to the I2C bus on the Pi. At the command prompt, enter sudo apt-get install python-smbus.

Now reboot the Pi and log in again. With your LCD connected, enter i2cdetect -y 1 at the command prompt. This will show you a table of addresses for each I2C device connected to your Pi:

Raspberry Pi LCD - I2C Connections - I2C detect

The I2C address of my LCD is 21. Take note of this number, we’ll need it later.

Programming the LCD

We’ll be using Python to program the LCD, so if this is your first time writing/running a Python program, you may want to check out How to Write and Run a Python Program on the Raspberry Pi before proceeding.

Installing the Library

I found a Python I2C library that has a good set of functions and works pretty well. This library was originally posted here, then expanded and improved by GitHub user DenisFromHR.

Copy this code for the library, then save it in a file named I2C_LCD_driver.py:

# -*- coding: utf-8 -*-
# Original code found at:
# https://gist.github.com/DenisFromHR/cc863375a6e19dce359d

"""
Compiled, mashed and generally mutilated 2014-2015 by Denis Pleic
Made available under GNU GENERAL PUBLIC LICENSE

# Modified Python I2C library for Raspberry Pi
# as found on http://www.recantha.co.uk/blog/?p=4849
# Joined existing 'i2c_lib.py' and 'lcddriver.py' into a single library
# added bits and pieces from various sources
# By DenisFromHR (Denis Pleic)
# 2015-02-10, ver 0.1

"""

# i2c bus (0 -- original Pi, 1 -- Rev 2 Pi)
I2CBUS = 0

# LCD Address
ADDRESS = 0x27

import smbus
from time import sleep

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

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

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

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

# 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)


# commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80

# flags for display entry mode
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00

# flags for display on/off control
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00

# flags for display/cursor shift
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00

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

# flags for backlight control
LCD_BACKLIGHT = 0x08
LCD_NOBACKLIGHT = 0x00

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_device(ADDRESS)

      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x03)
      self.lcd_write(0x02)

      self.lcd_write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE)
      self.lcd_write(LCD_DISPLAYCONTROL | LCD_DISPLAYON)
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_ENTRYMODESET | LCD_ENTRYLEFT)
      sleep(0.2)


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

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

   # 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))

   # write a character to lcd (or character rom) 0x09: backlight | RS=DR<
   # works!
   def lcd_write_char(self, charvalue, mode=1):
      self.lcd_write_four_bits(mode | (charvalue & 0xF0))
      self.lcd_write_four_bits(mode | ((charvalue << 4) & 0xF0))
  
   # put string function with optional char positioning
   def lcd_display_string(self, string, line=1, pos=0):
    if line == 1:
      pos_new = pos
    elif line == 2:
      pos_new = 0x40 + pos
    elif line == 3:
      pos_new = 0x14 + pos
    elif line == 4:
      pos_new = 0x54 + pos

    self.lcd_write(0x80 + pos_new)

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

   # clear lcd and set to home
   def lcd_clear(self):
      self.lcd_write(LCD_CLEARDISPLAY)
      self.lcd_write(LCD_RETURNHOME)

   # define backlight on/off (lcd.backlight(1); off= lcd.backlight(0)
   def backlight(self, state): # for state, 1 = on, 0 = off
      if state == 1:
         self.lcd_device.write_cmd(LCD_BACKLIGHT)
      elif state == 0:
         self.lcd_device.write_cmd(LCD_NOBACKLIGHT)

   # add custom characters (0 - 7)
   def lcd_load_custom_chars(self, fontdata):
      self.lcd_write(0x40);
      for char in fontdata:
         for line in char:
            self.lcd_write_char(line)         

There are a couple things you may need to change in the code above, depending on your set up. On line 19 there is a function that defines the port for the I2C bus (I2CBUS = 0). Older Raspberry Pi’s used port 0, but newer models use port 1. So depending on which RPi model you have, you might need to change this from 0 to 1.

Next, put the I2C address of your LCD in line 22 of the library code. For example, my I2C address is 21, so I’ll change line 22 to ADDRESS = 0x21.

Write to the Display

The following is a bare minimum “Hello World!” program to demonstrate how to initialize the LCD:

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

mylcd.lcd_display_string("Hello World!", 1)

Position the Text

The function mylcd.lcd_display_string() prints text to the screen and also lets you chose where to position it. The function is used as mylcd.lcd_display_string("TEXT TO PRINT", ROW, COLUMN). For example, the following code prints “Hello World!” to row 2, column 3:

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

mylcd.lcd_display_string("Hello World!", 2, 3)

On a 16×2 LCD, the rows are numbered 1 – 2, while the columns are numbered 0 – 15. So to print “Hello World!” at the first column of the top row, you would use mylcd.lcd_display_string("Hello World!", 1, 0).

Clear the Screen

The function mylcd.lcd_clear() clears the screen:

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

mylcd.lcd_display_string("This is how you", 1)
sleep(1)

mylcd.lcd_clear()

mylcd.lcd_display_string("clear the screen", 1)
sleep(1)

mylcd.lcd_clear()

Blinking Text

We can use a simple while loop with the mylcd.lcd_display_string() and mylcd.lcd_clear() functions to create a continuous blinking text effect:

import time
import I2C_LCD_driver
mylcd = I2C_LCD_driver.lcd()

while True:
    mylcd.lcd_display_string(u"Hello world!")
    time.sleep(1)
    mylcd.lcd_clear()
    time.sleep(1)

You can use the time.sleep() function on line 7 to change the time (in seconds) the text stays on. The time the text stays off can be changed in the time.sleep() function on line 9. To end the program, press Ctrl-C.

Print the Date and Time

The following program prints the current date and time to the LCD:

import I2C_LCD_driver
import time
mylcd = I2C_LCD_driver.lcd()


while True:
    mylcd.lcd_display_string("Time: %s" %time.strftime("%H:%M:%S"), 1)
    
    mylcd.lcd_display_string("Date: %s" %time.strftime("%m/%d/%Y"), 2)

Print Your IP Address

This code prints the IP address of your ethernet connection (eth0). To print the IP of your WiFi connection, change eth0 to wlan0 in line 18:

import I2C_LCD_driver
import socket
import fcntl
import struct

mylcd = I2C_LCD_driver.lcd()

def get_ip_address(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(
        s.fileno(),
        0x8915, 
        struct.pack('256s', ifname[:15])
    )[20:24])

mylcd.lcd_display_string("IP Address:", 1) 

mylcd.lcd_display_string(get_ip_address('eth0'), 2)

Scroll Text Right to Left Continuously

This program will scroll a text string from the right side of the LCD to the left side and loop continuously:

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

str_pad = " " * 16
my_long_string = "This is a string that needs to scroll"
my_long_string = str_pad + my_long_string

while True:
    for i in range (0, len(my_long_string)):
        lcd_text = my_long_string[i:(i+16)]
        mylcd.lcd_display_string(lcd_text,1)
        sleep(0.4)
        mylcd.lcd_display_string(str_pad,1)

Scroll Text Right to Left Once

The following code slides text onto the screen from right to left once, then stops and leaves a cleared screen.

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

str_pad = " " * 16
my_long_string = "This is a string that needs to scroll"
my_long_string = str_pad + my_long_string

for i in range (0, len(my_long_string)):
 lcd_text = my_long_string[i:(i+16)]
 mylcd.lcd_display_string(lcd_text,1)
 sleep(0.4)
 mylcd.lcd_display_string(str_pad,1)

Scroll Text Left to Right Once

This program slides text onto the screen from left to right once, then stops and leaves the first 16 characters of the text string on the screen.

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

padding = " " * 16
my_long_string = "This is a string that needs to scroll"
padded_string = my_long_string + padding

for i in range (0, len(my_long_string)):
 lcd_text = padded_string[((len(my_long_string)-1)-i):-i]
 mylcd.lcd_display_string(lcd_text,1)
 sleep(0.4)
 mylcd.lcd_display_string(padding[(15+i):i], 1)

Custom Characters

You can create any pattern you want and print it to the display as a custom character. Each character is an array of 5 x 8 pixels. Up to 8 custom characters can be defined and stored in the LCD’s memory. This custom character generator will help you create the bit array needed to define the characters in the LCD memory.

Printing a Single Custom Character

The following code generates a “<” character:

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

fontdata1 = [      
        [ 0b00010, 
          0b00100, 
          0b01000, 
          0b10000, 
          0b01000, 
          0b00100, 
          0b00010, 
          0b00000 ],
]

mylcd.lcd_load_custom_chars(fontdata1)
mylcd.lcd_write(0x80)
mylcd.lcd_write_char(0)

Printing Multiple Custom Characters

This program prints a large right pointing arrow (→) to the screen:

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

fontdata1 = [
        # char(0) - Upper-left character
        [ 0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b11111, 
          0b11111 ],

        # char(1) - Upper-middle character
        [ 0b00000, 
          0b00000, 
          0b00100, 
          0b00110, 
          0b00111, 
          0b00111, 
          0b11111, 
          0b11111 ],
        
        # char(2) - Upper-right character
        [ 0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b10000, 
          0b11000 ],
        
        # char(3) - Lower-left character
        [ 0b11111, 
          0b11111, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000 ],
       
        # char(4) - Lower-middle character
        [ 0b11111, 
          0b11111, 
          0b00111, 
          0b00111, 
          0b00110, 
          0b00100, 
          0b00000, 
          0b00000 ],
        
        # char(5) - Lower-right character
        [ 0b11000, 
          0b10000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000 ],
]

mylcd.lcd_load_custom_chars(fontdata1)

mylcd.lcd_write(0x80)
mylcd.lcd_write_char(0)
mylcd.lcd_write_char(1)
mylcd.lcd_write_char(2)

mylcd.lcd_write(0xC0)
mylcd.lcd_write_char(3)
mylcd.lcd_write_char(4)
mylcd.lcd_write_char(5)

Print Data from a Sensor

The code below will display data from a DHT11 temperature and humidity sensor. Follow this tutorial for instructions on how to set up the DHT11 on the Raspberry Pi. The DHT11 signal pin is connected to BCM pin 4 (physical pin 7 of the RPi).

Temperature is displayed on line 1, and humidity is displayed on line 2:

import RPi.GPIO as GPIO
import dht11
import I2C_LCD_driver

from time import *

mylcd = I2C_LCD_driver.lcd()

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()

while True:
  
  instance = dht11.DHT11(pin = 4)
  result = instance.read()

# Uncomment for Fahrenheit:
# result.temperature = (result.temperature * 1.8) + 32 

  if result.is_valid():
    mylcd.lcd_display_string("Temp: %d%s C" % (result.temperature, chr(223)), 1)
    mylcd.lcd_display_string("Humidity: %d %%" % result.humidity, 2)

For Fahrenheit, un-comment lines 18 and 19, and change the C to an F in line 22. You can also change the signal pin of the DHT11 input in line 15.

By inserting the variable from your sensor into the mylcd.lcd_display_string() function (line 22 in the code above) you can print the sensor data just like any other text string.

These programs are just basic examples of ways you can control text on your LCD. Try changing things around and combining the code to get some interesting effects. For example, you can make some fun animations by scrolling with custom characters. Don’t have enough screen space to output all of your sensor data? Just print and clear each reading for a couple seconds in a loop.

Let us know in the comments if you have any questions or trouble setting this up. Also leave a comment if you have any other ideas on how to get some cool effects, or just to share your project!

  • Thank you so much!! I’ve been playing with a couple of other bits of code all day trying to make them work, and all they would give me was seemingly random characters and behave differently at different times… Frustration levels now abating. Thanks again :)

  • weissekat says:

    Greate article! Helped me to connect chinese PCF8574 lcd backpack (blue one).

  • Surya Poudel says:

    It doesn’t work on python 3. why do I make it to work on Python 3??

    • nem esis says:

      want to use Python3 , then change all unichr to chr in I2C_LCD_driver.py,
      this is only a demo program now, the real driver is RPi_I2C_driver.py (line 34 has the i2c address)

      import RPi_I2C_driver, time
      mylcd = RPi_I2C_driver.lcd()
      mylcd.lcd_display_string(“Python3 rocks…”,1)
      time.sleep(3)
      mylcd.lcd_clear()
      mylcd.backlight(0)

    • amigadragon says:

      Did you change I2C bus 0 in line 20 of the driver to 1?

  • ShadowFXD says:

    Hi, I was wondering how to set the backlight off and then on? I tried different methods using the drivers, but none of them worked.

  • Gary says:

    Great article. I’m a total novice to all this but managed to get my LCD display up and running without a glitch.

    I would also like to figure out how to switch the backlight on and off. Any help would be greatly appreciated.

    • Gary says:

      I figured it out. Kind of obvious, but it was late and had been a long day.

      To turn the backlight on:
      mylcd.backlight(1)

      And to turn it off:
      mylcd.backlight(0)

  • mike christofas says:

    Awesome info. Will it hurt anything using the lcd without an i2c 3v to 5v converter? I have the sainsmart 20×4 which is made for Arduino i believe.

  • kb06 says:

    I have connected my HD44780 to RPi via I2C interface, downloaded smbus for python, copy&pasted python-code from this website and… nothing. I have 16 white rectangles on first line and nothing on second line (i have blue lcd with white digits). Good news is that LCD reacts with white rectangles when I run python code, if it is just connected to RPi there is only blue backlight.
    Do you know what might be wrong? My LCD has 0x27 address from i2cdetect. I connected i2c to HD44780 with 16 wires, pin by pin (1–1, 2–2, 3–3, etc.)
    Any ideas?

    • Hi kb06, What size resistors are you using for the contrast? It could be that the contrast resistor is too small, making the contrast too high. Also, does your LCD have an I2C backpack or are you using the PCF8574?

    • why 16 wires? if you use i2c you had to connect only ground,vcc,sda,scl on the i2c adapter of the screen..
      By the way I had the same problem. White character, via python i’m able to turn on and off the backlight.

  • kris mclean says:

    Us newbies that trawl the web looking for drivers etc really appreciate a well explained library that actually works. Like a pearl among the swill of junk code & bloatware, so lucid. Re a 3v3 Pi talking to a 5v PC8574: The iffy i2c signal here is when the Pi releases the lines for the various pullups to squabble over. According to the datasheet the 8574 min threshold for a rising edge is 70% or 3v5. The Pi has 1k8 internal pullups to 3v3 and the $1 ebay 8574 cards typically have 10k external pullups to 5v, the line therefore settle to ~3v6, enough to work without level shifters. And, if you’re squeamish about 0.1v headroom, surely you’d use a shottky diode in series with the plus side of the lcd supply rather than the inconvenience of level shifters?

  • Danny Martin says:

    How would the code be different when using a DHT22 sensor?

  • Matthew says:

    Great tutorial! but…
    Does anybody know how to print a custom character at a certain location (column / row)?

    • rhubarbdog says:

      Line 1 has addresses beginning 0x80
      Line 2 addresses begin at 0xc0
      Columns are numbered 0 to 15

      The following code will scroll a character from left to right on line one
      [code]
      import RPi_I2C_driver
      from time import *

      mylcd = RPi_I2C_driver.lcd()

      fontdata1 = [
      [ 0b000000,
      0b000000,
      0b000000,
      0b000000,
      0b000000,
      0b000000,
      0b000000,
      0b000000 ],

      [ 0b00000,
      0b00100,
      0b01110,
      0b11111,
      0b01110,
      0b00100,
      0b00000,
      0b00000 ]
      ]

      mylcd.lcd_load_custom_chars(fontdata1)
      for column in range(16):
      addr=0x80
      addr+=column
      #print diamond symbol at line 1 column x
      mylcd.lcd_write(addr)
      mylcd.lcd_write_char(1)
      sleep(0.5)
      #blank it out
      mylcd.lcd_write(addr)
      mylcd.lcd_write_char(0)
      [/code]

  • Matthew says:

    Here’s the mapping of a 2004 display. I guess it also applies for the 1602.
    http://web.alfredstate.edu/weimandn/lcd/lcd_addressing/LCD-20×4-memory(b).gif

  • Russell says:

    Thank you, this has helped me greatly! Do you know how to alter the library to connect two I2C LCD displays?

    • nem esis says:

      import RPi_I2C_driver, time

      mylcd0 = RPi_I2C_driver.lcd( ADDR = 0x27 )
      mylcd0.lcd_display_string(“lcd on default address.”,1)
      mylcd1 = RPi_I2C_driver.lcd( ADDR = 0x28 )
      mylcd1.lcd_display_string(“lcd on custom address”,1)
      time.sleep(3)
      mylcd1.lcd_clear()
      mylcd1.backlight(0)
      time.sleep(3)
      mylcd0.lcd_clear()
      mylcd0.backlight(0)

      but first change 2 lines in RPi_I2C_driver.py ( +/- line 25/26 )
      def __init__(self, ADDR = 0x27 ):
      self.lcd_device = i2c_device(ADDR)
      (code not fully tested, driver changes work, use i2cdetect to find i2c address of second lcd)

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

    TO

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

    and then when you initialize the lcd

    mylcd = I2C_LCD_driver.lcd()
    TO
    mylcd = I2C_LCD_driver.lcd(0x3c)
    or
    mylcd = I2C_LCD_driver.lcd()
    if you want to use the default address

  • jc says:

    thanks a lot for tutorial, may I ask – does anyone know if there is a simple way to display info from volumio 2.0 over mpdlcd? LCD 16*2 is connected to RPi3 via i2c and functions…thank you…

  • Edward Haase says:

    My LCD is up and running but I’m looking for a sample ‘Hello World’ sample code in PERL. Does anybody know a source?

  • JimBeamer says:

    Hello, i use mpd and output the information from mpc current to the display.
    This is the command that i use it for output
    title = os.popen(‘mpc current -f %title%’).read()
    mylcd.lcd_display_string(title, 1)
    it works fine but on the end of the string there is a bad character.
    On the end of the string from titel there is a “\n” and this is the bad character on the lcd.
    How i delete this sign in the output?

    Sorry for my bad english

  • Ryan says:

    Glad I found this page, helpd me a lot with my internet radio. I have used the clock as the second line on my screen, but by haivng this clock running on there it makes the next and previous buttons unresponsive. Does anyone know how to solve this please?

  • amigadragon says:

    Do I need to change or add something (lines 91-97 or anywhere else of the driver) for a 20×4 display?

    • It should work on a 20×4 LCD without any changes to the library code

      • amigadragon says:

        Tested and works. Thanks.

        • amigadragon says:

          Though I may need to insert a level converter, running the LCD from 3.3v on the Pi doesn’t give enough contrast straight-on, have to view from 20 degrees below straight-on to see it. Or should it be safe for the Pi to run the LCD from 5v without converting levels on SDA1/SCL1?

          • AmigaDragon says:

            Oops, missed the comment in the article about connecting to “the 5V pin of the Pi if possible”, so I guess it is safe.

            • Pkl says:

              Beware, most other tutorials are clear on the fact that you should NOT plug 5v to raspberry GPIOs. So if LCD has its own 5v pullups, voltage adaptation is needed. Or a 3.3v LCD.

  • Ashkan says:

    I get the following error on RasPi3

    Traceback (most recent call last):
    File “hello.py”, line 1, in
    import I2C_LCD_driver
    File “/home/pi/I2C_LCD_driver.py”, line 21
    def __init__(self, addr, port=I2CBUS):
    ^
    IndentationError: expected an indented block

    Would you please provide me with a solution?

  • Hi guys! Thanks for this tuto. How can I stop the date and time script with another script? No ctrl+c. I need to stop that scrip when another script starts but I don’t know how to doit

  • Ioannes Bracciano says:

    Check out this python module that extends the functionality of the HD44780 controller:
    https://github.com/IoannesBracciano/rpi-dots

  • danielpaulotipan says:

    LCD doesn’t display the DHT11 values part but displays others like the “hello world”. It just prints the value on the terminal. Why is that?

  • raspirate says:

    Is there a way to display more that 8 custom characters? I’m running into problems when trying to.

  • John Aiyelawo says:

    Thanks for your post!! Very helpful. Is there anyway I can use a 4×4 matrix keypad to display characters on the lcd. Thanks in advance :)

  • amigadragon says:

    While the scrolling scripts are working on their own, when I try to incorporate scrolling into my own script, I get an error:
    File “/home/pi/Documents/InternetRadio/I2C_LCD_driver.py”, line 160, in lcd_display_string
    self.lcd_write(ord(char), Rs)
    TypeError: ord() expected string of length 1, but int found

    Why does it work okay in the above script but not in mine? Why is it expecting string length 1 from me? (excerpt):
    for i in range (0, len(currentstream)):
    lcd_text = currentstream[i:(i+20)] # displayed substring width, 4×20 LCD
    mylcd.lcd_display_string(lcd_text,1)
    sleep(0.2)
    mylcd.lcd_display_string(str_pad,1)

  • Michal says:

    got a problem with last python script

    PRINT DATA FROM A SENSOR

    when I try to run it i got this error message :

    python temp.py
    Traceback (most recent call last):
    File “temp.py”, line 2, in
    import dht11
    ImportError: No module named dht11

    can some one help me with this ?

  • George says:

    Just thought I’d say this is a great tutorial, really covers all possible bases. 😁

  • Eduard says:

    hello
    if I follow this steps, I can use this code in a lcd 20×4 ?

  • Damjan says:

    HI!
    Work great!
    Thankyou….
    I try with two display , now i have I2C_LCD_driver.py and I2C_LCD_driver2.py with different address and in code
    import I2C_LCD_driver
    mylcd = I2C_LCD_driver.lcd()
    import I2C_LCD_driver2
    mylcd2 = I2C_LCD_driver2.lcd()

    How can I add in I2C_LCD_driver.py two address for two display ?
    I want to show display with 0x3f and 0x3D same information ?

    • NEM ESIS says:

      import RPi_I2C_driver, time

      text = “same information”
      mylcd0 = RPi_I2C_driver.lcd( ADDR = 0x3f )
      mylcd0.lcd_display_string(text,1)
      mylcd1 = RPi_I2C_driver.lcd( ADDR = 0x3D )
      mylcd1.lcd_display_string(text,1)
      time.sleep(3)
      mylcd1.lcd_clear()
      mylcd1.backlight(0)
      time.sleep(3)
      mylcd0.lcd_clear()
      mylcd0.backlight(0)

      but first change 2 lines in RPi_I2C_driver.py ( +/- line 25/26 )
      def __init__(self, ADDR = 0x27 ):
      self.lcd_device = i2c_device(ADDR)
      (code not fully tested, driver changes work, check your indents)

  • JOE says:

    I am having an issue with my LCD16x2 connected via an I2C module to the pi – the first time I run lcd_display_string and lcd_clear, the display will correctly display and remove the text, however the second (and any further times) I run the script, it does not clear the output, rather text gets added to the end of the last text.

    Does anyone know how to resolve this?

  • freedoM says:

    hallo im traying to use this scipt, whit an crius oled lcd display. but i dont get anything on the screen??
    do i maybe mist something? does the module maybe have to paste into an specific folder, whit modules? or does the module have to be compiled first?

    i only get this error!! ??

    Traceback (most recent call last):
    File “lcdtest2.py”, line 3, in
    mylcd = I2C_LCD_driver.lcd()
    File “/home/pi/lcd_test/I2C_LCD_driver.py”, line 111, in __init__
    self.lcd_write(0x03)
    File “/home/pi/lcd_test/I2C_LCD_driver.py”, line 136, in lcd_write
    self.lcd_write_four_bits(mode | (cmd & 0xF0))
    File “/home/pi/lcd_test/I2C_LCD_driver.py”, line 131, in lcd_write_four_bits
    self.lcd_device.write_cmd(data | LCD_BACKLIGHT)
    File “/home/pi/lcd_test/I2C_LCD_driver.py”, line 34, in write_cmd
    self.bus.write_byte(self.addr, cmd)
    IOError: [Errno 110] Connection timed out

    thanks allready,. greetz,freedom

  • AmigaDragon says:

    On the Raspberry Pi zero W in raspi-config Advanced settings I can’t find the I2C, SPI or Serial enables

    • Nick B. says:

      Look under menu option 5 – Interfacing Options then you should see options P1 – P8. I2C is P5, SPI is P4, Serial is P6.

      • AmigaDragon says:

        Shown above in the tutorial they’re in Advanced. Is this a difference on the Zero W or a change between Raspbian Jessie and Stretch? Either way it should be updated to mention both.

  • DocMad says:

    How do I implement my DHT22? What library is the best one to use?

  • beginner says:

    hey guys, i tried to set up my i2c with my lcd but the problem is the string command doesn’t display on my lcd
    i tried the backlight command and it works.

    • rhubarbdog says:

      I had this problem. The i2c backpack has a variable resistor this adjusts the contrast. Write a program which displays a string on line one and immediately exits. Now with a small screw driver adjust that potentiometer. Hopefully the string is now on display.

  • Crashman39 says:

    Thanks very much for your work and Videos, its works a charm on Python 3

  • Ruman says:

    Can I use this library with 20*4 LCD display?

  • Stefan says:

    Very good tutorial!
    Is it possible to write to the display with the backlight off?
    If I use lcd_display_string(“String”), the backlight is always switched on, even if it was set to off before.
    Any ideas?
    Regards
    Stefan

  • stefan says:

    Another point: To switch the backlight of the display off, I use this snippet:

    import I2C_LCD_driver
    mylcd = I2C_LCD_driver.lcd()
    mylcd.backlight(0)

    The problem now is, if the display was illuminated, the backlight will be switched off as exspected, so far so good.
    But, if it is already black, it gets switched on for one moment and gets switched off again. So during the instantiation of mylcd the light goes on. How can I prevent that?

  • Rene says:

    Thank you very much. It all worked out perfectly.

  • TJT says:

    I have followed this tutorial but still the i2c lcd does not work. I get an error that says it cant find the librabry. Which folder should it be saved to?

  • This excellent website definitely has all the info I
    needed about this subject and didn’t know who to ask.

  • Paul Cresswell says:

    Hi, Fantastic tutorial, after reading so many this is the only one that i have understood and got working.

    Having said that, how would I get text stings that are longer that 16 to scroll?

    any help would be great.

    Thanks

  • Jijish Thomas says:

    Hello, by turning on i2c in PI configuration, will it affect any other device connected to gpios?

  • 9acca9 says:

    Hi. Thanks for this! It help me a lot.
    I want to know if maybe you have some tutorial for making a menu. Im searching in google, and not find anything yet.
    I want to make a menu with this library and physicall buttons. (i know how to make the buttons.. but not the menu, how move trough the menu also.
    Thanks!

    I dont speak english.

  • sravya says:

    I used this module to interface 2 displays using 1 Arduino Uno board to displaying LCD 16×2 , and graphical display interface the display but only one LCD was working is it possible using Arduino Uno board

  • olivier says:

    Bonjour, comment adapter “PRINT DATA FROM A SENSOR” à un AM2302 branché sur un Pi GPIO#4 ? Merci.

  • Bob Harris says:

    Very clear tutorial. Thanks. Worked straight away, but the IP address script gives an error.

    Line 15:
    structpack(‘256s’, ifname[:15])
    structerror: argument for ‘s’ must be a bytes object

  • ALESSAndro says:

    Great article indeed. How to use the LCD with a multiplexer TCA9548A? How can I specify the multiplexer channel? Many thanks for your feedback.

  • MasterCATZ says:

    Thank you I can finally turn backlight on / off :D

  • Sam says:

    This was a great tutorial, thank you! I have my PI setup as a webserver just for experiments and was curios if you have any examples on how to display the server uptime on the LCD? Thank you in advance!

  • Maurizio says:

    Great stuff.
    Just my little contribution. About scrolling long lines: Here is some code to “rotate” instead of scrolling. The rotate left and right functions have been derived from the geeksforgeeks page (https://www.geeksforgeeks.org/string-slicing-python-rotate-string):

    def rotate_str_left(input, d):
    “””
    return the input string rotated of d character to the left
    “””
    Lfirst = input[0: d]
    Lsecond = input[d:]
    return Lsecond + Lfirst

    def rotate_str_right(input, d):
    “””
    return the input string rotated of d character to the right
    “””
    Rfirst = input[0: len(input) – d]
    Rsecond = input[len(input) – d:]
    return Rsecond + Rfirst

    import lib.I2C_LCD_driver as I2C_LCD_driver
    from time import sleep
    my_lcd = I2C_LCD_driver.lcd()
    str_pad = ” — ” # can be any other set of spaces and charcaters, just keep it as short as possible
    long_string = “Logger listening @ 115200 baud on ttyUSB0”
    long_string1 = long_string + str_pad
    max_chars = 20 # I am using a 4 x 20 LCD

    while True:
    if len(long_string) <= max_chars:
    line1_Text = long_string
    my_lcd.lcd_display_string(line1_Text, 1)
    else:
    line1_Text = long_string1[:max_chars]
    my_lcd.lcd_display_string(line1_Text, 1)
    long_string1 = rotate_str_left(long_string1, 1)

    it writes on line 1 of the LCD, long_string can be passed or read from a file. If long_string length stays within the 20 characters limit it'll be printed on the LCD as is, otherwise it will be rotated (instead of scrolled) left bound. The rotate_right can be used if you need to display in languages that read from right to left.

  • SJ says:

    great tutorial, But I get an error when running the write to display script
    In line 114 self.lcd_device.write_cmd(data l LCD_BACKLIGHT)

    I get Attribute error: ‘lcd’ object has no attribute ‘lcd_device’
    trace back are lines 119 and 139

  • stefan says:

    how do I make the orange pi display my ip when I turn it on?
    tnx

  • Joshua says:

    I was looking how to do this with a USB to I2C adapter from amazon (10 bucks) on a windows machine using C#. They have a dll you can download CH341DLL.DLL and CH341DLLA64.DLL. But I had no idea how to use it’s methods to do what I needed with an LCD screen. This post helped me tremendously by running using the I2C_LCD_driver.py on a le potato to understand how everything works.

    Below (if it fits) is the C# class I made to do this.

    public class I2C
    {
    public static UInt32 LCD_CLEARDISPLAY = 0x01;
    public static UInt32 LCD_RETURNHOME = 0x02;
    public static UInt32 LCD_ENTRYMODESET = 0x04;
    public static UInt32 LCD_DISPLAYCONTROL = 0x08;
    public static UInt32 LCD_CURSORSHIFT = 0x10;
    public static UInt32 LCD_FUNCTIONSET = 0x20;
    public static UInt32 LCD_SETCGRAMADDR = 0x40;
    public static UInt32 LCD_SETDDRAMADDR = 0x80;

    public static UInt32 LCD_ENTRYRIGHT = 0x00;
    public static UInt32 LCD_ENTRYLEFT = 0x02;
    public static UInt32 LCD_ENTRYSHIFTINCREMENT = 0x01;
    public static UInt32 LCD_ENTRYSHIFTDECREMENT = 0x00;

    public static UInt32 LCD_DISPLAYON = 0x04;
    public static UInt32 LCD_DISPLAYOFF = 0x00;
    public static UInt32 LCD_CURSORON = 0x02;
    public static UInt32 LCD_CURSOROFF = 0x00;
    public static UInt32 LCD_BLINKON = 0x01;
    public static UInt32 LCD_BLINKOFF = 0x00;

    public static UInt32 LCD_DISPLAYMOVE = 0x08;
    public static UInt32 LCD_CURSORMOVE = 0x00;
    public static UInt32 LCD_MOVERIGHT = 0x04;
    public static UInt32 LCD_MOVELEFT = 0x00;

    public static UInt32 LCD_8BITMODE = 0x10;
    public static UInt32 LCD_4BITMODE = 0x00;
    public static UInt32 LCD_2LINE = 0x08;
    public static UInt32 LCD_1LINE = 0x00;
    public static UInt32 LCD_5x10DOTS = 0x04;
    public static UInt32 LCD_5x8DOTS = 0x00;

    public static UInt32 LCD_BACKLIGHT = 0x08;
    public static UInt32 LCD_NOBACKLIGHT = 0x00;

    public static UInt32 En = 0b00000100;
    public static UInt32 Rw = 0b00000010;
    public static UInt32 Rs = 0b00000001;

    [DllImport(“CH341DLLA64.DLL”, EntryPoint = “CH341OpenDevice”, CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern UInt32 OpenI2C(ulong iIndex);

    [DllImport(“CH341DLLA64.DLL”, EntryPoint = “CH341CloseDevice”, CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern void CloseI2C(ulong iIndex);

    [DllImport(“CH341DLLA64.DLL”, EntryPoint = “CH341WriteI2C”, CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool WriteI2C(ulong iIndex, UInt32 iDevice, UInt32 iAddr, UInt32 iByte);

    [DllImport(“CH341DLLA64.DLL”, EntryPoint = “CH341ReadI2C”, CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool ReadI2C(ulong iIndex, UInt32 iDevice, UInt32 iAddr, out IntPtr oByte);

    [DllImport(“CH341DLLA64.DLL”, EntryPoint = “CH341StreamI2C”, CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool StreamI2C(ulong iIndex, ulong iWriteLength, UInt32[]? iWriteBuffer, ulong iReadLength, out UInt32[] oReadBuffer);

    [DllImport(“CH341DLLA64.DLL”, EntryPoint = “CH341Set_D5_D0”, CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CH341Set_D5_D0(ulong iIndex, UInt32 iSetDirOut, UInt32 iSetDataOut);

    [DllImport(“CH341DLLA64.DLL”, EntryPoint = “CH341SetOutput”, CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CH341SetOutput(ulong iIndex, ulong iEnable, ulong iSetDirOut, ulong iSetDataOut);

    public static void Clear()
    {
    Write(LCD_CLEARDISPLAY);
    Write(LCD_RETURNHOME);
    }

    public static void Strobe(UInt32 data)
    {
    I2C.WriteI2C(0, 0x27, 0x10, data | En | LCD_BACKLIGHT);
    Thread.Sleep(6);
    I2C.WriteI2C(0, 0x27, 0x10, (data & ~En) | LCD_BACKLIGHT);
    Thread.Sleep(2);
    }

    public static void Write_Bits(UInt32 data)
    {
    I2C.WriteI2C(0, 0x27, 0x10, data | LCD_BACKLIGHT);
    Thread.Sleep(1);
    Strobe(data);
    }

    public static void Write(UInt32 cmd, UInt32 mode = 0x00)
    {
    Write_Bits(mode | (cmd & 0xF0));
    Write_Bits(mode | ((cmd << 4) & 0xF0));
    }

    public static void DisplayString(string s, UInt32 line = 0x01, UInt32 pos = 0)
    {
    var pos_new = pos;
    if(line == 0x02)
    {
    pos_new = 0x40 + pos;
    }
    if (line == 0x03)
    {
    pos_new = 0x14 + pos;
    }
    if (line == 0x04)
    {
    pos_new = 0x54 + pos;
    }
    Write(0x80 + pos_new);

    foreach(char c in s)
    {
    Write((UInt32)c, Rs);
    }
    }

    public static void Init(ulong index)
    {
    OpenI2C(index);
    I2C.Write(0x03);
    I2C.Write(0x03);
    I2C.Write(0x03);
    I2C.Write(0x02);

    I2C.Write(LCD_FUNCTIONSET | LCD_2LINE | LCD_5x8DOTS | LCD_4BITMODE);
    I2C.Write(LCD_DISPLAYCONTROL | LCD_DISPLAYON);
    I2C.Write(LCD_CLEARDISPLAY);
    I2C.Write(LCD_ENTRYMODESET | LCD_ENTRYLEFT);
    Thread.Sleep(200);
    }

    public static void Close(ulong index)
    {
    CloseI2C(index);
    }
    }

  • Leave a Comment

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

    Quick Start Guide for Setting Up an I2C LCD on the Raspberry Pi

    Quick Start Guide for Setting Up an I2C LCD on the Raspberry Pi

    Enter your name and email and it will be sent to your inbox:

    Consent to store personal information:

    Email me new tutorials and (very) occasional promotional stuff:

    Check your email to get the PDF!