LCD1602 RGB Module, 16x2 Characters LCD, RGB Backlight, 3.3V/5V, I2C Bus

LCD1602 RGB Module, 16x2 Characters LCD, RGB Backlight, 3.3V/5V, I2C Bus.

This post is to provide information from my experience that may help others who have purchased one of these LCD modules.

The project was to connect the LCD to a PICO W to display temperature from a local sensor and a remote sensor accessed via WiFi. The Pico was loaded with rp2-pico-w-20220831-unstable-v1.19.1-358-g0b26efe73 Micropython. The project was proceeding nicely until Micropython was updated. Exactly the same code resulted in an I2C I/O error.

The update was to redefine the default I2C pins. The Raspberry Pi Foundation had not specifically declared which pins they considered to be default. Each I2C bus can be configured for 6 different pin sets.

Forum posts showed other persons had problems with their code related to incorrect pins, mostly due to assuming the defaults. The problem I experienced was not related to incorrectly assigned pins, it was to do with LCD driver and how it configured I2C. I did notice a few posts where the situation seemed to be the same as I had experienced. The driver failed on i2c.writetomem().

I2c.scan() worked correctly and the addresses returned were the correct device addresses. But when running the demo for the LCD it produced an I/O error. The only change was the Micropython update, so naturally it was blamed for the fault. A post from a Micropython developer indicated there should nothing in the update to cause I2C to fail. The only change in the update was the default pin assignments.

Based on this I began looking at the LCD driver library. I thought it had been fixed when the frequency was specified as 100 khz, but later testing showed this to be incorrect. The real fix came when the main code created an I2C instance and passed this to the library. This is similar to how the Makerverse RTC library works. Anything else did not work.

Modified example, Choose_color.py

import RGB1602_01
import time
import math
from machine import Pin, I2C

colorR = 64
colorG = 128
colorB = 64

i2c = I2C(id=0, sda=Pin(8), scl=Pin(9), freq=400000)
lcd=RGB1602_01.RGB1602(16,2,i2c=i2c)

rgb1 = (148,0,110) #深紫罗兰色
rgb2 = (255,0,255) #紫色
rgb3 = (144,249,15) #青白
rgb4 = (0,128,60) #浅蓝
rgb5 = (255,209,0)#黄色
rgb6 = (248,248,60)#幽灵的白色
rgb7 = (80,80,145) #深蓝色
rgb8 = (255,0,0) #红色
rgb9 = (0,255,0) #青色

# set the cursor to column 0, line 1

lcd.setCursor(0, 0)
# print the number of seconds since reset:

# print the number of seconds since reset:
lcd.printout("Waveshare")
#   
lcd.setCursor(0, 1)
# 
lcd.printout("Hello,World!")
# 
# 
lcd.setRGB(rgb1[0],rgb1[1],rgb1[2]);
time.sleep(1)
lcd.setRGB(rgb2[0],rgb2[1],rgb2[2]);
time.sleep(1)
lcd.setRGB(rgb3[0],rgb3[1],rgb3[2]);
time.sleep(1)
lcd.setRGB(rgb4[0],rgb4[1],rgb4[2]);
time.sleep(1)
lcd.setRGB(rgb5[0],rgb5[1],rgb5[2]);
time.sleep(1)
lcd.setRGB(rgb6[0],rgb6[1],rgb6[2]);
time.sleep(1)
lcd.setRGB(rgb7[0],rgb7[1],rgb7[2]);
time.sleep(1)
lcd.setRGB(rgb8[0],rgb8[1],rgb8[2]);
time.sleep(1)
lcd.setRGB(rgb9[0],rgb9[1],rgb9[2]);
time.sleep(1)

LCD Library, RGB1602_01.py

# -*- coding: utf-8 -*-
import time
from machine import Pin,I2C

#Device I2C Arress
LCD_ADDRESS   =  (0x7c>>1)
RGB_ADDRESS   =  (0xc0>>1)

#color define
REG_RED    =     0x04
REG_GREEN  =     0x03
REG_BLUE   =     0x02
REG_MODE1  =     0x00
REG_MODE2  =     0x01
REG_OUTPUT =     0x08

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_5x8DOTS = 0x00

class RGB1602():
    def __init__(self,  col=16, row=2, i2c=None): 
        if isinstance(i2c, I2C) is False:
            print("RGB1602 requires a valid i2c device")
            raise TypeError
        self.RGB1602_I2C = i2c
        self._row = row
        self._col = col
        self._showfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
        self.begin(self._row,self._col)

    def command(self,cmd):
        self.RGB1602_I2C.writeto_mem(LCD_ADDRESS, 0x80, chr(cmd))

    def write(self,data):
        self.RGB1602_I2C.writeto_mem(LCD_ADDRESS, 0x40, chr(data))
    
    def setReg(self,reg,data):
        self.RGB1602_I2C.writeto_mem(RGB_ADDRESS, reg, chr(data))

    def setRGB(self,r,g,b):
        self.setReg(REG_RED,r)
        self.setReg(REG_GREEN,g)
        self.setReg(REG_BLUE,b)

    def setCursor(self,col,row):
        if(row == 0):
            col|=0x80
        else:
            col|=0xc0;
        self.RGB1602_I2C.writeto(LCD_ADDRESS, bytearray([0x80,col]))

    def clear(self):
        self.command(LCD_CLEARDISPLAY)
        time.sleep(0.002)

    def printout(self,arg):
        if(isinstance(arg,int)):
            arg=str(arg)
        for x in bytearray(arg,'utf-8'):
            self.write(x)

    def display(self):
        self._showcontrol |= LCD_DISPLAYON 
        self.command(LCD_DISPLAYCONTROL | self._showcontrol)
 
    def begin(self,cols,lines):
        if (lines > 1):
            self._showfunction |= LCD_2LINE 
        self._numlines = lines 
        self._currline = 0 
        time.sleep(0.05)

# Send function set command sequence
        self.command(LCD_FUNCTIONSET | self._showfunction)
#delayMicroseconds(4500);  # wait more than 4.1ms
        time.sleep(0.005)
# second try
        self.command(LCD_FUNCTIONSET | self._showfunction);
#delayMicroseconds(150);
        time.sleep(0.005)
# third go
        self.command(LCD_FUNCTIONSET | self._showfunction)
# finally, set # lines, font size, etc.
        self.command(LCD_FUNCTIONSET | self._showfunction)
# turn the display on with no cursor or blinking default
        self._showcontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF 
        self.display()
# clear it off
        self.clear()
# Initialize to default text direction (for romance languages)
        self._showmode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT 
# set the entry mode
        self.command(LCD_ENTRYMODESET | self._showmode);
# backlight init
        self.setReg(REG_MODE1, 0)
# set LEDs controllable by both PWM and GRPPWM registers
        self.setReg(REG_OUTPUT, 0xFF)
# set MODE2 values
# 0010 0000 -> 0x20  (DMBLNK to 1, ie blinky mode)
        self.setReg(REG_MODE2, 0x20)
        self.setColorWhite()

    def setColorWhite(self):
        self.setRGB(255, 255, 255)

Regards
Jim

PS I will add to this post after I have looked at the Arduino library.

1 Like

@Robert93820 wrt using this LCD with Arduino, suggest the DFRobot Library it seems to work much better than the Waveshare Library.

Use the Arduino library manager to install it. Search for dfrobot_RGBLCD1602
There are a number of examples, the custom one is interesting. Shows what I was referring to when using a character generator. There is another example that displays the number of seconds since startup by using millis()/1000

Cheers
Jim

1 Like

Thanks James. Will give this a try as soon as I can get to it.
Cheers Bob

1 Like