Home » Blog » Python Script | Interface a 16×2 I2C LCD with Raspberry Pi
Python Script Interface a 16×2 I2C LCD with Raspberry Pi

Python Script | Interface a 16×2 I2C LCD with Raspberry Pi

Most of the cheap and widely available 16×2 character LCD’s are driven by Hitachi’s HD44780 driver, and is an essential part for any project that displays information. The 8 bits needed per character are transferred either on 8 parallel lines or sequentially on 4 lines which will require at least 6 I/O pins to talk to the LCD. Although its not a problem when used with AVR or other micros having abundant I/O pins, these inexpensive integrated HD44780 LCD displays can result in a messy setup over complicating things for simple projects. However, if you use an LCD module with I2C interface, you only need 2 data lines to talk to the LCD. These modules are powered by a PCF8574 chip which converts a parallel signal into I2C and vice-versa. The Raspberry Pi sends data to the PCF8574 via I2C and the PCF8574 converts the I2C signal into a 4 bit parallel signal, which is relayed to the HD44780.

Initial Setup

This tutorial is written in mind considering you have a clean install of Raspbian without any additional configurations. I2C and SPI interfaces of your Raspberry Pi aren’t enabled by default, and need some extra configuration before you can use them. You need to run sudo raspi-config  to enable it. Navigate to “Advanced Options” and enable automatic loading of the kernel module.

enable i2c raspberry pi

Wiring

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. Also make sure to use a logic level shifter if your LCD is powered by 5V just to be on the safer side 🙂

Raspberry Pi 16x2 i2c lcd wiring

I2C-Tools and SMBus

By default,  I2C-tools and SMBus comes pre-installed with the latest version of Raspbian. However, if its not installed or you are not 100% sure you can install them by executing the below commands on the terminal.

sudo apt-get install i2c-tools

sudo apt-get install python-smbus

Now with your serial LCD connected with Pi, enter sudo i2cdetect  -y 1 at the terminal and if everything is working as expected it should print a table of addresses for each I2C device connected to your Pi. In my case the I2C address of my LCD is 0x27. Make sure to take note of this number as we’ll need it later.

i2c scanning of serial lcd

Code

The last and final step of interfacing our I2C 16×2 LCD with Raspberry Pi is to create a python script and display a sample text. Make sure to follow the previous steps before attempting to execute this code. Save the below code as pylcdlib.py and execute it.

# Credit for this code goes to "natbett" of the Raspberry Pi Forum 18/02/13
# https://www.raspberrypi.org/forums/viewtopic.php?t=34261&p=378524
# Before running this code make sure to run sudo i2cdetect -y 1
# and match the LCD address of your device

import smbus
from time import *

# LCD Address
ADDRESS = 0x27

# 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 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)
      sleep(0.0001)

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

   # put string function
   def lcd_display_string(self, string, line):
      if line == 1:
         self.lcd_write(0x80)
      if line == 2:
         self.lcd_write(0xC0)
      if line == 3:
         self.lcd_write(0x94)
      if line == 4:
         self.lcd_write(0xD4)

      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)

display = lcd()
display.lcd_display_string("RaspiNews - 16x2", 1)
display.lcd_display_string("I2C LCD Demo..", 2)

Note: If the I2C address for your LCD module is different from this sample code make sure to change it.