I2C LCD Library... Help!

Discuss development of drivers for external hardware and components, such as LCD screens, sensors, motor drivers, etc.
Target audience: Users and developers of drivers.
User avatar
liudr
Posts: 211
Joined: Tue Oct 17, 2017 5:18 am

Re: I2C LCD Library... Help!

Post by liudr » Thu Oct 19, 2017 4:06 am

Sorry about getting this old post revived, again. For this library:

https://github.com/dhylands/python_lcd/ ... i2c_lcd.py

Which files should I include for ESP8266 NODEMCU board and I2C LCD? All below?

esp8266_i2c_lcd.py
esp8266_i2c_lcd_test.py
i2c_lcd.py

Say (add, En, Rw, Rs, d4, d5, d6, d7, Bl, Pol) is the tuple of pins:

The PCF8574 backpack you wrote the code for is the (0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE) variety with a blue thruhole trim pot, a back light jumper on a black board, correct?
Image

I know of another variety (0x20, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE) with markings "GY-IICLCD" on a blue board with all surface mount components so I have to do the following, correct?
Image

MASK_RS = 0x40
MASK_RW = 0x20
MASK_E = 0x10
SHIFT_BACKLIGHT = 7
SHIFT_DATA = 0

Thanks.

User avatar
mcauser
Posts: 507
Joined: Mon Jun 15, 2015 8:03 am

Re: I2C LCD Library... Help!

Post by mcauser » Thu Oct 19, 2017 4:37 am

You only need to upload:
* esp8266_i2c_lcd.py
* lcd_api.py

The file esp8266_i2c_lcd_test.py gives you an example on how to use it.

Those black PCF8574 I2C backpacks with blue trimpots are designed to work with all 16x2 displays, as shown in your first image.
The ESP8266 version of this library was designed to work with one of these boards.
You can get one for $0.64 on AliExpress with free shipping.
https://www.aliexpress.com/item/5-pcs-l ... 05492.html

If that GY-IICLCD board has a different pinout, it may not be compatible. Best check the datasheet for the module.

You shouldn't need a logic level converted to use the ESP8266 boards, now that they have confirmed to be 5v tolerant.
Use GPIO pins 5 and 4 if possible and you can use the example code as is:
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)

User avatar
liudr
Posts: 211
Joined: Tue Oct 17, 2017 5:18 am

Re: I2C LCD Library... Help!

Post by liudr » Thu Oct 19, 2017 4:05 pm

Thanks I have the black ones and I mostly have the blue ones because they have a thinner profile that fits my device perfectly. The black ones have two very protruding parts, the pot, and the jumper. If you look inside the enclosure, the shiny copper coil is a boost converter that will otherwise hit the jumper.

Image

Here is a front view of my logger device:

Image

User avatar
liudr
Posts: 211
Joined: Tue Oct 17, 2017 5:18 am

Re: I2C LCD Library... Help!

Post by liudr » Sat Oct 21, 2017 11:53 pm

OK. I used TXB0801 level shifter and the PCF8574 LCD backpack. Works well. Next I'll test the other type of PCF8574 backpack with different wiring. I really like the fact that you can just open a serial port and type in commands to see how they work, instead of having to wait for compile and upload. It's going to be very useful for beginners.

User avatar
liudr
Posts: 211
Joined: Tue Oct 17, 2017 5:18 am

Re: I2C LCD Library... Help!

Post by liudr » Sun Oct 22, 2017 1:15 am

Simply reassigning new values to the MASK_xx doesn't work. I had to change their values in the module. I guess once I import the module, __Init__ runs and unless the MASK_xx are defined correctly in the module, the LCD will fail to initialize. I don't think this is the best way (I mean modifying the code to use another lcd backpack).

I recommend defining the features of the __Init__ in a .begin() function. This allows the user the option to assign correct values of MASK_xx before initializing the lcd. LCD library in Arduino has a begin() method. An even better alternative is to have member values store all MASK_xx values so each LCD can have its own set of values and we can allow multiple LCDs using different pin arrangements to work together.

User avatar
liudr
Posts: 211
Joined: Tue Oct 17, 2017 5:18 am

Re: I2C LCD Library... Help!

Post by liudr » Sun Oct 22, 2017 4:37 am

An addition:
The lcd backpack I have uses a negative polarity to turn on the LCD back light. I modified the module by adding a variable:

BACKLIGHT_NEG_POLARITY = True

Then everywhere I see self.backlight, I replace it with (self.backlight!=BACKLIGHT_NEG_POLARITY). I also generalized the backlight on and off functions with:

def hal_backlight_off(self):
"""Allows the hal layer to turn the backlight off."""
self.i2c.writeto(self.i2c_addr, bytearray([(BACKLIGHT_NEG_POLARITY==True) << SHIFT_BACKLIGHT]))

def hal_backlight_on(self):
"""Allows the hal layer to turn the backlight on."""
self.i2c.writeto(self.i2c_addr, bytearray([(BACKLIGHT_NEG_POLARITY==False) << SHIFT_BACKLIGHT]))

Bernd67
Posts: 2
Joined: Sun Jan 14, 2018 6:22 pm

Re: I2C LCD Library... Help!

Post by Bernd67 » Sun Jan 14, 2018 6:25 pm

Thank you for providing this code. I am now able to run my 20x4 display on an ESP32 ...

KR Bernd :D

[quote=sequel post_id=14375 time=1475289361 user_id=1911]
Hi Electra,

So sorry for the late reply.

Here is the code,

esp8266_i2c_lcd.py :

[code]
"""Implements a character based lcd connected via PCF8574 on i2c."""

from lcd import LcdApi
from machine import I2C
from time import sleep_ms

DEFAULT_I2C_ADDR = 0x27

# Defines shifts or masks for the various LCD line attached to the PCF8574

MASK_RS = 0x01
MASK_RW = 0x02
MASK_E = 0x04
SHIFT_BACKLIGHT = 3
SHIFT_DATA = 4


class I2cLcd(LcdApi):
"""Implements a character based lcd connected via PCF8574 on i2c."""

def __init__(self, i2c, i2c_addr, num_lines, num_columns):
self.i2c = i2c
self.i2c_addr = i2c_addr
self.i2c.writeto(self.i2c_addr, bytes([0]))
sleep_ms(20) # Allow LCD time to powerup
# Send reset 3 times
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
sleep_ms(5) # need to delay at least 4.1 msec
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
sleep_ms(1)
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
sleep_ms(1)
# Put LCD into 4 bit mode
self.hal_write_init_nibble(self.LCD_FUNCTION)
sleep_ms(1)
LcdApi.__init__(self, num_lines, num_columns)
cmd = self.LCD_FUNCTION
if num_lines > 1:
cmd |= self.LCD_FUNCTION_2LINES
self.hal_write_command(cmd)

def hal_write_init_nibble(self, nibble):
"""Writes an initialization nibble to the LCD.


This particular function is only used during intiialization.
"""
byte=((nibble >> 4) & 0x0f) << SHIFT_DATA
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))

def hal_backlight_on(self):
"""Allows the hal layer to turn the backlight on."""
self.i2c.writeto(self.i2c_addr, bytes([1 << SHIFT_BACKLIGHT]))

def hal_backlight_off(self):
"""Allows the hal layer to turn the backlight off."""
self.i2c.writeto(self.i2c_addr, bytes([0]))

def hal_write_command(self, cmd):
"""Writes a command to the LCD.

Data is latched on the falling edge of E.
"""
byte=((self.backlight << SHIFT_BACKLIGHT) |
(((cmd >> 4) & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))
byte=((self.backlight << SHIFT_BACKLIGHT) |
((cmd & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))
if cmd <= 3:
# The home and clear commands require a worst
# case sleep_ms of 4.1 msec
sleep_ms(5)

def hal_write_data(self, data):
"""Write data to the LCD."""
byte=(MASK_RS |
(self.backlight << SHIFT_BACKLIGHT) |
(((data >> 4) & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))
byte=(MASK_RS |
(self.backlight << SHIFT_BACKLIGHT) |
((data & 0x0f) << SHIFT_DATA))
self.i2c.writeto(self.i2c_addr, bytes([byte | MASK_E]))
self.i2c.writeto(self.i2c_addr, bytes([byte]))
[/code]


lcd.py :

[code]
"""Provides an API for talking to HD44780 style character LCDs."""


class LcdApi(object):
"""Implements the API for talking with n LCD. This class only knows what
commands to send to the LCD, and not how t get them to the LCD.

It is expected that a derived class will implement the hal_xxx functions.
"""

# The following constant names were lifted from the avrlib lcd.h
# header file, however, I changed the definitions from bit numbers
# to bit masks.
#
# HD44780 LCD controller command set

LCD_CLR = 0x01 # DB0: clear display
LCD_HOME = 0x02 # DB1: return to home position

LCD_ENTRY_MODE = 0x04 # DB2: set entry mode
LCD_ENTRY_INC = 0x02 # --DB1: increment
LCD_ENTRY_SHIFT = 0x01 # --DB0: shift

LCD_ON_CTRL = 0x08 # DB3: turn lcd/cursor on
LCD_ON_DISPLAY = 0x04 # --DB2: turn display on
LCD_ON_CURSOR = 0x02 # --DB1: turn cursor on
LCD_ON_BLINK = 0x01 # --DB0: blinking cursor

LCD_MOVE = 0x10 # DB4: move cursor/display
LCD_MOVE_DISP = 0x08 # --DB3: move display (0-> move cursor)
LCD_MOVE_RIGHT = 0x04 # --DB2: move right (0-> left)

LCD_FUNCTION = 0x20 # DB5: function set
LCD_FUNCTION_8BIT = 0x10 # --DB4: set 8BIT mode (0->4BIT mode)
LCD_FUNCTION_2LINES = 0x08 # --DB3: two lines (0->one line)
LCD_FUNCTION_10DOTS = 0x04 # --DB2: 5x10 font (0->5x7 font)
LCD_FUNCTION_RESET = 0x30 # See "Initializing by Instruction" section

LCD_CGRAM = 0x40 # DB6: set CG RAM address
LCD_DDRAM = 0x80 # DB7: set DD RAM address

LCD_RS_CMD = 0
LCD_RS_DATA = 1

LCD_RW_WRITE = 0
LCD_RW_READ = 1

def __init__(self, num_lines, num_columns):
self.num_lines = num_lines
if self.num_lines > 4:
self.num_lines = 4
self.num_columns = num_columns
if self.num_columns > 40:
self.num_columns = 40
self.cursor_x = 0
self.cursor_y = 0
self.backlight = True
self.display_off()
self.backlight_on()
self.clear()
self.hal_write_command(self.LCD_ENTRY_MODE | self.LCD_ENTRY_INC)
self.hide_cursor()
self.display_on()

def clear(self):
"""Clears the LCD display and moves the cursor to the top left
corner.

"""
self.hal_write_command(self.LCD_CLR)
self.hal_write_command(self.LCD_HOME)
self.cursor_x = 0
self.cursor_y = 0

def show_cursor(self):
"""Causes the cursor to be made visible."""
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
self.LCD_ON_CURSOR)

def hide_cursor(self):
"""Causes the cursor to be hidden."""
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)

def blink_cursor_on(self):
"""Turns on the cursor, and makes it blink."""
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
self.LCD_ON_CURSOR | self.LCD_ON_BLINK)

def blink_cursor_off(self):
"""Turns on the cursor, and makes it no blink (i.e. be solid)."""
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY |
self.LCD_ON_CURSOR)

def display_on(self):
"""Turns on (i.e. unblanks) the LCD."""
self.hal_write_command(self.LCD_ON_CTRL | self.LCD_ON_DISPLAY)

def display_off(self):
"""Turns off (i.e. blanks) the LCD."""
self.hal_write_command(self.LCD_ON_CTRL)

def backlight_on(self):
"""Turns the backlight on.

This isn't really an LCD command, but some modules have backlight
controls, so this allows the hal to pass through the command.
"""
self.backlight = True
self.hal_backlight_on()

def backlight_off(self):
"""Turns the backlight off.

This isn't really an LCD command, but some modules have backlight
controls, so this allows the hal to pass through the command.
"""
self.backlight = False
self.hal_backlight_off()

def move_to(self, cursor_x, cursor_y):
"""Moves the cursor position to the indicated position. The cursor
position is zero based (i.e. cursor_x == 0 indicates first column).
"""
self.cursor_x = cursor_x
self.cursor_y = cursor_y
addr = cursor_x & 0x3f
if cursor_y & 1:
addr += 0x40 # Lines 1 & 3 add 0x40
if cursor_y & 2:
addr += 0x14 # Lines 2 & 3 add 0x14
self.hal_write_command(self.LCD_DDRAM | addr)

def putchar(self, char):
"""Writes the indicated charcter to the LCD at the current cursor
position, and advances the cursor by one position.
"""
if self.cursor_x >= self.num_columns or char == '\n':
self.cursor_x = 0
self.cursor_y += 1
if self.cursor_y >= self.num_lines:
self.cursor_y = 0
self.move_to(self.cursor_x, self.cursor_y)
if char != '\n':
self.hal_write_data(ord(char))
self.cursor_x += 1

def putstr(self, string):
"""Write the indicated string to the LCD at the current cursor
position and advances the cursor position appropriately.
"""
for char in string:
self.putchar(char)

def hal_backlight_on(self):
"""Allows the hal layer to turn the backlight on.

If desired, a derived HAL class will implement this function.
"""
pass

def hal_backlight_off(self):
"""Allows the hal layer to turn the backlight off.

If desired, a derived HAL class will implement this function.
"""

pass

def hal_write_command(self, cmd):
"""Write a command to the LCD.

It is expected that a derived HAL class will implement this
function.
"""
raise NotImplementedError

def hal_write_data(self, data):
"""Write data to the LCD.

It is expected that a derived HAL class will implement this
function.
"""
raise NotImplementedError
[/code]
[/quote]

pas_athos
Posts: 1
Joined: Mon Jul 16, 2018 11:20 pm

Re: I2C LCD Library... Help!

Post by pas_athos » Mon Jul 16, 2018 11:24 pm

[quote=dhylands post_id=8812 time=1456901359 user_id=81]
I've added pyb_i2c_adafruit_lcd.py and pyb_i2c_adafruit_lcd_test.py to my repository:
https://github.com/dhylands/python_lcd

and tested it with a 4 line, 20 column LCD (the test code assumes that A0, A1, and A2 are open, and it used I2C1 (although it should also work with any I2c bus).
[/quote]

Dear Dave, I tried the code on Fipy but appears an error saying pyb lib cannot be found.
Am I missing something?
Thank you very much for your kind help,
Paulo

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: I2C LCD Library... Help!

Post by dhylands » Tue Jul 17, 2018 4:28 pm

When I wrote that code, there was only the pyb module. Now there is also the machine module, and newer boards only support the machine module and not the pyb module.

If you look in some of the other examples, you'll see some that use machine instead of pyb. So you'll probably need to update the example.

Feel free to submit a PR with your updates.

Thanks

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: I2C LCD Library... Help!

Post by Roberthh » Tue Jul 17, 2018 4:30 pm

The pyb module is specific for PyBoard. In general, you'll find the i2c methods in the machine module. The documentation for FIPy is at https://docs.pycom.io/, the forum for PyCom devices is https://forum.pycom.io/.

Post Reply