16×2 LCD Module Control With Backlight Switch


Following on from my article about controlling a 16×2 LCD module with Python and a Raspberry Pi I decided to make a few enhancements. These included :

  • Adding a 10Kohm variable resistor to adjust the contrast
  • Adding a 5Kohm variable resistor to adjust the backlight brightness
  • Adding a transistor to allow the backlight to be switched on and off
  • Allowing left, centred and right justified text

The updated breadboard looks like this :

Contrast Adjustment

Pin 3 is now given a voltage of between 0V and 5V via the middle pin of a 10Kohm trimming pot so that the display contrast can be adjusted.

Backlight Brightness and Switching

Pin 15/16 are in series with a 560ohm and 2Kohm trimming pot via an NPN transitor (BC547, BC548 or equivalent) which is activated by  an additional GPIO connection. The LCD backlight is treated in exactly the same way I switch standard LEDs in my previous Control LED Using GPIO Output Pin article. Using a fixed resistance ensures the resistance can never be adjusted below 560ohm which protects the backlight if you set the trimming pot to zero ohms. The base of the transistor is wired to an additional GPIO pin via a 27Kohm resistor.

Text Justification

The function “lcd_string” has been modified to accept a second parameter. This parameter takes the value 1, 2 or 3 and determines how the text is displayed on the screen.


Here is the updated code :

#    ___  ___  _ ____
#   / _ \/ _ \(_) __/__  __ __
#  / , _/ ___/ /\ \/ _ \/ // /
# /_/|_/_/  /_/___/ .__/\_, /
#                /_/   /___/
#  lcd_16x2.py
#  16x2 LCD Test Script with
#  backlight control and text justification
# Author : Matt Hawkins
# Date   : 06/04/2015
# https://www.raspberrypi-spy.co.uk/

# The wiring for the LCD is as follows:
# 1 : GND
# 2 : 5V
# 3 : Contrast (0-5V)*
# 4 : RS (Register Select)
# 5 : R/W (Read Write)       - GROUND THIS PIN
# 6 : Enable or Strobe
# 7 : Data Bit 0             - NOT USED
# 8 : Data Bit 1             - NOT USED
# 9 : Data Bit 2             - NOT USED
# 10: Data Bit 3             - NOT USED
# 11: Data Bit 4
# 12: Data Bit 5
# 13: Data Bit 6
# 14: Data Bit 7
# 15: LCD Backlight +5V**
# 16: LCD Backlight GND

import RPi.GPIO as GPIO
import time

# Define GPIO to LCD mapping
LCD_RS = 7
LCD_E  = 8
LCD_D4 = 25
LCD_D5 = 24
LCD_D6 = 23
LCD_D7 = 18
LED_ON = 15

# Define some device constants
LCD_WIDTH = 16    # Maximum characters per line
LCD_CHR = True
LCD_CMD = False

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

def main():
  # Main program block
  GPIO.setmode(GPIO.BCM)       # Use BCM GPIO numbers
  GPIO.setup(LCD_E, GPIO.OUT)  # E
  GPIO.setup(LCD_D4, GPIO.OUT) # DB4
  GPIO.setup(LCD_D5, GPIO.OUT) # DB5
  GPIO.setup(LCD_D6, GPIO.OUT) # DB6
  GPIO.setup(LCD_D7, GPIO.OUT) # DB7
  GPIO.setup(LED_ON, GPIO.OUT) # Backlight enable

  # Initialise display

  # Toggle backlight on-off-on

  while True:

    # Send some centred text
    lcd_string("Rasbperry Pi",LCD_LINE_1,2)
    lcd_string("16x2 LCD Test",LCD_LINE_2,2)

    time.sleep(3) # 3 second delay

    # Send some left justified text

    time.sleep(3) # 3 second delay

    # Send some right justified text

    time.sleep(3) # 20 second delay

    # Send some centred text
    lcd_string("Follow me on",LCD_LINE_1,2)
    lcd_string("Twitter @RPiSpy",LCD_LINE_2,2)


def lcd_init():
  # Initialise display
  lcd_byte(0x33,LCD_CMD) # 110011 Initialise
  lcd_byte(0x32,LCD_CMD) # 110010 Initialise
  lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
  lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off
  lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
  lcd_byte(0x01,LCD_CMD) # 000001 Clear display

def lcd_byte(bits, mode):
  # Send byte to data pins
  # bits = data
  # mode = True  for character
  #        False for command

  GPIO.output(LCD_RS, mode) # RS

  # High bits
  GPIO.output(LCD_D4, False)
  GPIO.output(LCD_D5, False)
  GPIO.output(LCD_D6, False)
  GPIO.output(LCD_D7, False)
  if bits&0x10==0x10:
    GPIO.output(LCD_D4, True)
  if bits&0x20==0x20:
    GPIO.output(LCD_D5, True)
  if bits&0x40==0x40:
    GPIO.output(LCD_D6, True)
  if bits&0x80==0x80:
    GPIO.output(LCD_D7, True)

  # Toggle 'Enable' pin

  # Low bits
  GPIO.output(LCD_D4, False)
  GPIO.output(LCD_D5, False)
  GPIO.output(LCD_D6, False)
  GPIO.output(LCD_D7, False)
  if bits&0x01==0x01:
    GPIO.output(LCD_D4, True)
  if bits&0x02==0x02:
    GPIO.output(LCD_D5, True)
  if bits&0x04==0x04:
    GPIO.output(LCD_D6, True)
  if bits&0x08==0x08:
    GPIO.output(LCD_D7, True)

  # Toggle 'Enable' pin

def lcd_toggle_enable():
  # Toggle enable
  GPIO.output(LCD_E, True)
  GPIO.output(LCD_E, False)

def lcd_string(message,line,style):
  # Send string to display
  # style=1 Left justified
  # style=2 Centred
  # style=3 Right justified

  if style==1:
    message = message.ljust(LCD_WIDTH," ")
  elif style==2:
    message = message.center(LCD_WIDTH," ")
  elif style==3:
    message = message.rjust(LCD_WIDTH," ")

  lcd_byte(line, LCD_CMD)

  for i in range(LCD_WIDTH):

def lcd_backlight(flag):
  # Toggle backlight on-off-on
  GPIO.output(LED_ON, flag)

if __name__ == '__main__':

  except KeyboardInterrupt:
    lcd_byte(0x01, LCD_CMD)

This script can be downloaded using this link or directly to your Pi using the following command :

wget https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/python/lcd_16x2_backlight.py

Remember to update the constants at the top of the script to match the GPIO signals you are using on your Pi. As always these GPIO references are the Broadcom signal names as described in my GPIO header article.

Take a look at my other LCD Screen related posts which include details of the 20×4 version of the screen used in this post.



  1. Hi
    Great article, just one question. Which NPN transitor did you use?
    Looking in Maplin there are quite a few to choose from with different characteristics e.g. 2N5551, BC635 etc.


  2. First of all: Thanks for the great tutorial! Thanks to this I have a nice new clock with date and everything. I was confident I could somehow pull it off myself but your article/code solved a lot of mysteries that would have made my code extremely complicated (I didn’t notice the HD44780 characters were exactly the ascii value and stuff like that…).
    I tried to understand all that happens but the lcd_init() is still confusing me a bit. I don’t know if I just read the HD44780 documentation completely wrong. I can follow the initialisation that is described here:

    Could you elaborate a bit what the single calls do?

    I’m currently trying to read an rss feed and send it to the lcd. I wanted to use the built in shift function, but if I use one line for the feed and the other one for the time I have to manipulate the rss line in the software itself and cannot send out just one char after the other, right?

  3. The program runs, but the Left – Right – Center isn’t working.
    And I’ve spent 2 hours trying to figure out what’s wrong.

    3 is aligning Center, and 1 & 2 are aligning Left

    • Add some print statements to the lcd_string function. Confirm the “message” and “LCD_WIDTH” variables are correct and the IF statement is calling the correct branch.

  4. Pingback: 20×4 LCD Module Control Using Python | Raspberry Pi Spy

  5. Very good tutorial, thanks! One question: Here, the brightness of the display is regulated with a variable resistor.
    I’m looking for a solution where the brightness is adjusted *automatically* in function of the surrounding light. So, a light sensitive resistor should give me a value for the surrounding light. But how can I control the brightness via the raspberry pi?

Leave A Reply

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