Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
RaspberryPi I2C LCD Python stuff
# requires RPi_I2C_driver.py
import RPi_I2C_driver
from time import *
mylcd = RPi_I2C_driver.lcd()
# test 2
mylcd.lcd_display_string("RPi I2C test", 1)
mylcd.lcd_display_string(" Custom chars", 2)
sleep(2) # 2 sec delay
mylcd.lcd_clear()
# let's define a custom icon, consisting of 6 individual characters
# 3 chars in the first row and 3 chars in the second row
fontdata1 = [
# Char 0 - Upper-left
[ 0x00, 0x00, 0x03, 0x04, 0x08, 0x19, 0x11, 0x10 ],
# Char 1 - Upper-middle
[ 0x00, 0x1F, 0x00, 0x00, 0x00, 0x11, 0x11, 0x00 ],
# Char 2 - Upper-right
[ 0x00, 0x00, 0x18, 0x04, 0x02, 0x13, 0x11, 0x01 ],
# Char 3 - Lower-left
[ 0x12, 0x13, 0x1b, 0x09, 0x04, 0x03, 0x00, 0x00 ],
# Char 4 - Lower-middle
[ 0x00, 0x11, 0x1f, 0x1f, 0x0e, 0x00, 0x1F, 0x00 ],
# Char 5 - Lower-right
[ 0x09, 0x19, 0x1b, 0x12, 0x04, 0x18, 0x00, 0x00 ],
# Char 6 - my test
[ 0x1f,0x0,0x4,0xe,0x0,0x1f,0x1f,0x1f],
]
# Load logo chars (fontdata1)
mylcd.lcd_load_custom_chars(fontdata1)
# Write first three chars to row 1 directly
mylcd.lcd_write(0x80)
mylcd.lcd_write_char(0)
mylcd.lcd_write_char(1)
mylcd.lcd_write_char(2)
# Write next three chars to row 2 directly
mylcd.lcd_write(0xC0)
mylcd.lcd_write_char(3)
mylcd.lcd_write_char(4)
mylcd.lcd_write_char(5)
sleep(2)
mylcd.lcd_clear()
mylcd.lcd_display_string_pos("Testing",1,1) # row 1, column 1
sleep(1)
mylcd.lcd_display_string_pos("Testing",2,3) # row 2, column 3
sleep(1)
mylcd.lcd_clear()
# Now let's define some more custom characters
fontdata2 = [
# Char 0 - left arrow
[ 0x1,0x3,0x7,0xf,0xf,0x7,0x3,0x1 ],
# Char 1 - left one bar
[ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10 ],
# Char 2 - left two bars
[ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18 ],
# Char 3 - left 3 bars
[ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c ],
# Char 4 - left 4 bars
[ 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e ],
# Char 5 - left start
[ 0x0,0x1,0x3,0x7,0xf,0x1f,0x1f,0x1f ],
# Char 6 -
# [ ],
]
# Load logo chars from the second set
mylcd.lcd_load_custom_chars(fontdata2)
block = chr(255) # block character, built-in
# display two blocks in columns 5 and 6 (i.e. AFTER pos. 4) in row 1
# first draw two blocks on 5th column (cols 5 and 6), starts from 0
mylcd.lcd_display_string_pos(block * 2,1,4)
#
pauza = 0.2 # define duration of sleep(x)
#
# now draw cust. chars starting from col. 7 (pos. 6)
pos = 6
mylcd.lcd_display_string_pos(unichr(1),1,6)
sleep(pauza)
mylcd.lcd_display_string_pos(unichr(2),1,pos)
sleep(pauza)
mylcd.lcd_display_string_pos(unichr(3),1,pos)
sleep(pauza)
mylcd.lcd_display_string_pos(unichr(4),1,pos)
sleep(pauza)
mylcd.lcd_display_string_pos(block,1,pos)
sleep(pauza)
# and another one, same as above, 1 char-space to the right
pos = pos +1 # increase column by one
mylcd.lcd_display_string_pos(unichr(1),1,pos)
sleep(pauza)
mylcd.lcd_display_string_pos(unichr(2),1,pos)
sleep(pauza)
mylcd.lcd_display_string_pos(unichr(3),1,pos)
sleep(pauza)
mylcd.lcd_display_string_pos(unichr(4),1,pos)
sleep(pauza)
mylcd.lcd_display_string_pos(block,1,pos)
sleep(pauza)
#
# now again load first set of custom chars - smiley
mylcd.lcd_load_custom_chars(fontdata1)
mylcd.lcd_display_string_pos(unichr(0),1,9)
mylcd.lcd_display_string_pos(unichr(1),1,10)
mylcd.lcd_display_string_pos(unichr(2),1,11)
mylcd.lcd_display_string_pos(unichr(3),2,9)
mylcd.lcd_display_string_pos(unichr(4),2,10)
mylcd.lcd_display_string_pos(unichr(5),2,11)
sleep(2)
mylcd.lcd_clear()
sleep(1)
mylcd.backlight(0)
# -*- coding: utf-8 -*-
"""
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
"""
#
#
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)
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)
# 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 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
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)
# 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)
# define precise positioning (addition from the forum)
def lcd_display_string_pos(self, string, line, pos):
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)
Owner

DenisFromHR commented Feb 9, 2015

Perhaps someone might find this useful: I took an existing Python I2C LCD library from
http://www.recantha.co.uk/blog/?p=4849
and added some stuff to it (from other sources), and joined 2 existing libs to one single library that doesn't need any other libs.... In short, this works on my 16x2 and 20x4 I2C LCDs with Raspberry Pi: I can define new custom characters and show them on screen (as shown in the examples.py above).
If there is interest, I could also add some examples with text scrolling (in the works).
95% of the library is not mine: I just managed to cobble together pieces existing in several other libraries. The advantage is that this works without requiring installation of other libraries or Python modules (RPi.GPIO or others).
In short, works well enough for me as it is now, but I'll probably tinker with this some more: there is always room for improvement 😄
My commerical stuff: http://www.hana-lab.com
Regards,
Denis

Hi is their a way to invert the characters?
Im trying to make a menu and have the user be able to tell witch line they have selected.

Owner

DenisFromHR commented Feb 11, 2015

Never occured to me, to be honest....
From what I gather, the usual character 16x2 (or 20x4) LCD displays do not provide "invert screen" function: for that you would need a graphical display, I guess...
AFAIK, these character displays offer only a blinking cursor...

Owner

DenisFromHR commented Feb 15, 2015

Another thing which I encountered in Arch Linux on RasPi: you might get the following error message when running the above examples:
NameError: name 'unichr' is not defined
In that case, replace all occurrences of "unichr" with "chr" in the examples.py file (Python 3 handles things a bit differently...)

sturgeo commented Feb 25, 2015

Hi Denis, are you able to describe how to add a degree symbol? I've tried chr(176) chr(223) and unichr(176) unichr(223) and can't get it to work.

Here's a snippet of my code:

mylcd.lcd_display_string("Temp : %.4s"%currenttemp, 1)
mylcd.lcd_display_string("Target : %s"%targettemp, 2)
Owner

DenisFromHR commented Mar 16, 2015

sturgeo,
Your code should be something like this:

mylcd.lcd_display_string("Temp : %.4s"%currenttemp + chr(223), 1)
mylcd.lcd_display_string("Target : %s"%targettemp + chr(223), 2)

(haven't checked it, but should be something like that...)

wared commented Jun 16, 2015

Hi Denis, i have a weired issue when printing a row as above with temperature as %currenttemp, i get the upper right part of the pumpkin character you've made right after the value...

i've already try the recantha library before and had the same issue with a different character

any idea on how to get ride of it?

wared commented Jun 24, 2015

i find my solution
t2, h2 = float(t2), float(h2)
if it help anyone

gknauf commented Jul 18, 2015

Hi Denis,
I was also fiddling with this same I2C LCD stuff when I came over your post in the forum ...
you asked for comment and sharing improvements - so here is some:

  1. I suggest you rename the library to f.e I2C_LCD_driver.py because there's nothing specific to RPi but thats generic Python stuff; f.e. I use it with Voyage Linux x86 on Alix and Wrap boards ...

  2. lcd_display_string_pos() mainly only duplicates code, therefore it should replace lcd_display_string();
    I did it like this:

    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)

this works fine for me, and you can just append the position if needed (and you can even ommit the line which then defaults to 1).
4. Its nice to have the stuff which may need to be modified (I2CBUS and ADDRESS) at the top of the library code.
5. Finally probably a picture of the I2C interface would be nice to see for others; mine looks like this:
http://electronics-diy.com/store/i2c-serial-lcd-display.jpg

Here are your modified files:
https://drive.google.com/open?id=0BwBjwH9WlTLpTl9GM1V6NUFEU0E

Greetz, Guen.

kestel commented Sep 30, 2015

Thanks for the libs!

I've spend several hours for trying start libs working.. And I fix it with screwdriver.. Fix potentiometer brightness.

alexdom commented Nov 4, 2015

Thanks DenisFromHR for your hard time coding for Raspberry Pi I2C LCD HD44780 + PCF8574 backpack! You've done great job! I am using the latest revision from gknauf.. Change it based on my needs.

dentex commented Dec 17, 2015

Thanks @DenisFromHR and @gknauf for your efforts. :)
Thumbs up!

smit468 commented Dec 30, 2015

Hi, thanks for this it looks promising but I can't get it to work... I'm using a 16x2 HD44780 LCD connected to a PCF8574N chip, wired to the RPi 2 via I2C (stand alone chip, not a back pack) wired following this tutorial: http://www.rpiblog.com/2012/07/interfacing-16x2-lcd-with-raspberry-pi.html. My problem is when I run examples.py, the program runs without errors, but nothing displays on the LCD. I have changed the address in the library. Is there anything else I need to change in the library or example program for my specific set up? Any help would really be appreciated!

marsel60 commented Jan 9, 2016

Hello, first of all, thanks Denis for your job... I'm very sorry for this silly question but I'm not able to install your library in my Raspi. Downloaded the library in my RPi and tried to install using "pip install RPi_I2C_driver.py".... I obtain a bunch of errors and library wont install.
Any Hints? please
I have a Jessy distro with Linux 4.1.15-v7+ , consider that I'v connected and used positively an LCD 20x4 with MCP23017 on my RPi.
Thanks in advance guys.

Thanks, Denis. Great implementation.

@ghost

ghost commented Mar 28, 2016

I could not write on a 16x2 display with SPLC780C chip, apareece only strange characters, any tips ?

I can't get mylcd.backlight(0) to work with my i2c LCD. Any ideas?

raphaelyancey commented Sep 7, 2016 edited

It works like a charm on my RPi B. Thank you very much!

Can you only have 8 custom characters? I defined 9 but char(0) then displayed the same symbol as char(8)

darnokb commented Oct 30, 2016

I have following problem:

root@OpenWrt:~/lcd2# python examples.py Traceback (most recent call last): File "examples.py", line 5, in <module> mylcd = RPi_I2C_driver.lcd() File "/root/lcd2/RPi_I2C_driver.py", line 107, in __init__ self.lcd_write(0x03) File "/root/lcd2/RPi_I2C_driver.py", line 132, in lcd_write self.lcd_write_four_bits(mode | (cmd & 0xF0)) File "/root/lcd2/RPi_I2C_driver.py", line 127, in lcd_write_four_bits self.lcd_device.write_cmd(data | LCD_BACKLIGHT) File "/root/lcd2/RPi_I2C_driver.py", line 26, in write_cmd self.bus.write_byte(self.addr, cmd) IOError: [Errno 5] I/O error

Does anyone know what is wrong? I copy pasted code. Running i2cdetect 0 says that my i2c is under 0x27 address (so I did not change anything in the script). Do you have any ideas what is wrong? I connected i2c to hd44780 pin by pin (1--1, 2--2, 3--3, 4--4, etc.)

Anybody experiencing strange characters after a certain amount of uptime? Seems to work again when restarting the script, maybe a memory issue?

Machacator commented Dec 24, 2016 edited

raphaelyancey commented on 6 Nov
Anybody experiencing strange characters after a certain amount of uptime? Seems to work again when restarting the script, maybe a memory issue?

Yes! me!
In previous versions, my script would run fine for about a day or two. I never figured out the reason why it was behaving like that so i gave up.
About a week ago i installed everything from scratch and use raspbian lite, thinking that maybe that would help........well, it now stops working after half an hour.
Using RPI 1B

EDIT: Internet conection seems to be affecting somehow. With either ethernet cable or wifi dongle, it displays the characters of doom after about 30min. Without any conection it will last for about 6 hours.

Looking for help or advice on this.
I have the RPi_I2C_driver working fine for basic use, but when I try to use it how I want, strange things happen similar to what raphaelyancey and Machacator mention about strange characters. I also experience it writing the strange characters to the wrong lines.

What I am trying to do is have 4 independent scripts, each write to one single line.
No scrolling code.
All text is being printed from each script is 20 or less characters.
Each script writes to one line directly using either:
mylcd.lcd_display_string("string here",1);
or
mylcd.lcd_display_string_pos("this string here",2,0);

Script 1 - python - real time date and time with seconds displayed on line 1 - script is always running and updates line one once a second. No lcd_clear() used.
Script 2 - outside temp and inside temp checked every minute displayed on line 2. No lcd_clear() used. Script is run from cron, writes to LCD and exits.
Script 3 - RSS feed tracking my favorite NHL team score updated every 3 minutes and displays on line 3. No lcd_clear() used. Script is run from cron, writes to LCD and exits.
Script 4 - a feedback line showing last home automation feature used and display on line 4. No lcd_clear() used. Script is run when a command is issued, so it can be anytime. Script outputs and then exits.

So, each one works perfectly on their own.

When I have two or more running, it will soon start writing random characters to the screen, mostly on the wrong lines (can be above or below). It can happen nearly instantly or start happening 20 minutes later.
When it starts, only a lcd_clear can fix it. Even if line 1 is being controlled by the always running date time script updating every second.

I have tried adding the screen clear to one or more of the scripts, but due to other issues (data not being present on the script run), lines won't update again until the next cron run or command. Not ideal, plus I hate the way the lcd_clear() looks when it happens.

I am sure it has to do something with two or more scripts trying to access the lcd at nearly the same time.
I assumed having the ability to address the writing per line wouldn't cause any issues, but it doesn't seem that way.

So, any ideas or suggestions to get 4 scripts each writing their own line, to play nice and not corrupt the display?
My only limitation I can think of is that combining the scripts into one is not an option.

Any thoughts are welcomed.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment