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 :

# HD44780 LCD Test Script for
# Raspberry Pi
# Author : Matt Hawkins
# Site   : http://www.raspberrypi-spy.co.uk
# Date   : 03/08/2012

# 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.00005
E_DELAY = 0.00005

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
  GPIO.output(LED_ON, True)
  GPIO.output(LED_ON, False)
  GPIO.output(LED_ON, True)

  # Send some centred test
  lcd_byte(LCD_LINE_1, LCD_CMD)
  lcd_string("Rasbperry Pi",2)
  lcd_byte(LCD_LINE_2, LCD_CMD)
  lcd_string("Model B",2)

  time.sleep(3) # 3 second delay

  # Send some left justified text
  lcd_byte(LCD_LINE_1, LCD_CMD)
  lcd_byte(LCD_LINE_2, LCD_CMD)

  time.sleep(3) # 3 second delay

  # Send some right justified text
  lcd_byte(LCD_LINE_1, LCD_CMD)
  lcd_byte(LCD_LINE_2, LCD_CMD)


  # Turn off backlight
  GPIO.output(LED_ON, False)

def lcd_init():
  # Initialise display

def lcd_string(message,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," ")

  for i in range(LCD_WIDTH):

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
  GPIO.output(LCD_E, True)  
  GPIO.output(LCD_E, False)  

  # 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
  GPIO.output(LCD_E, True)  
  GPIO.output(LCD_E, False)  

if __name__ == '__main__':

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.

This entry was posted in Hardware, Python, Tutorials & Help and tagged , , , , , . Bookmark the permalink.

8 Responses to 16×2 LCD Module Control With Backlight Switch

  1. Yo only me again. I’ve got to pop into maplins again to order the correct new parts.
    1. 10Kohm variable resistor
    2. 5Kohm variable resistor
    3. NPN transitor

    Thanks dude

  2. Mark says:

    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.


  3. sma23 says:

    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?

  4. Ted says:

    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

    • Matt says:

      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.

  5. Ted says:

    I’m using a 2N3904. I had to drop to a 1K to be able to see the ON/OFF state.
    27K was barely visible.

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>