rm-hull / luma.lcd

Python module to drive PCD8544, HT1621, ST7735, ST7567 and UC1701X-based LCDs
https://luma-lcd.readthedocs.io
MIT License
156 stars 56 forks source link

Add support for I2C PCF8574 #90

Closed satiromarra closed 3 years ago

satiromarra commented 4 years ago

image

https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf

dhrone commented 3 years ago

Luma.core v1.16.1 has support for the pcf8574. However, you still need a display driver that supports the specific display you are attempting to use. There is an hd44780 driver that is close to release in the luma.lcd project that does work with the luma.core.interface.serial.pcf8574 class.

Which device do you want to attach the pcf8574 to?

satiromarra commented 3 years ago

I don't remember but I have 2 lcd displays, LCD2004 and LCD1602, both uses the hd44780 driver.

thijstriemstra commented 3 years ago

LCD2004 and LCD1602

I also have an I2C PCF8574 and these displays available to test this.

dhrone commented 3 years ago

The luma.lcd.device.hd44780 class in the current PR for luma.lcd does support the hd44780 using the luma.core.interface.serial.PCF8574 class. So it should work for both your LCD2004 and LCD1602. I have a 2004 display running the code as I am typing this.

satiromarra commented 3 years ago

I don't see the class luma.lcd.driver.hd44780, where is it? I have tried a year ago and I have not gotten it to work for me.

dhrone commented 3 years ago

It hasn't been approved yet but if you want to try it out, it is in luma.lcd branch hd44780.

satiromarra commented 3 years ago

In this branch: https://github.com/dhrone/luma.lcd/tree/hd44780 Any example?

Traceback (most recent call last): File "./lcd1602.py", line 4, in from luma.core.interface.serial import PCF8574 ImportError: cannot import name 'PCF8574' from 'luma.core.interface.serial'

dhrone commented 3 years ago

Make sure you have luma.core v1.16.1 loaded. pcf8574 is lowercase.

Example:

from luma.core.interface.serial import parallel, pcf8574
from luma.lcd.device import hd44780
interface = pcf8574(address=0x27, backlight_enabled=True)
device = hd44780(interface, width=16, height=2)
device.text = 'Hello'

You also need to make sure that you have the wiring between the pcf8574 and the hd44780 set correctly. I just noticed that the pcf8574 class documentation markup mangled the description of how you deal with different wiring between the pcf8574 and the HD44780. Here's a cleaner version...

   Default wiring is...
   RS - Register Select
   E - Enable
   RW - Read/Write (note: unused by this driver)
   D4-D7 - The upper data pins

   :          RS  RW   E   D4  D5  D6  D7  BACKLIGHT
   Display     4   5   6   11  12  13  14
   Backpack   P0  P1  P2   P4  P5  P6  P7         P3

   If your PCF8574 is wired up differently to this you will need to provide
   the correct values for the RS, E, COMMAND, BACKLIGHT parameters.
   RS, E and BACKLIGHT are set to the pin numbers of the backpack pins
   they are connect to from P0-P7.  COMMAND is set to 'high' if the 
   Register Select (RS) pin needs to be high to inform the device that a 
   command byte is being sent or 'low' if RS low is used for commands.
   PINS is a list of the pin positions that match where the devices data
   pins have been connected on the backpack (P0-P7).  For many devices this
   will be d4->P4, d5->P5, d6->P6, and d7->P7 ([4, 5, 6, 7]) which is the
   default.

   Example:
   If your data lines D4-D7 are connected to the PCF8574s pins P0-P3 with
   the RS pin connected to P4, the enable pin to P5, the backlight pin
   connected to P7, and the RS value to indicate command is low, your
   initialization would look something like...

   pcf8574(port=1, address=0x27, PINS=[0,1,2,3], RS=0x10, E=0x20,
   COMMAND='low', BACKLIGHT=0x40)

   Explanation:
   PINS are set to [0, 1, 2, 3] which assigns P0 to D4, P1 to D5, P2 to D6,
   and P3 to D7.  RS is set to 4 to associate with P4. Similarly E is set
   to 5 to associate E with P5.  BACKLIGHT set to 7 connects it to pin P7
   of the backpack.  COMMAND is set to 'low' so that RS will be set to low
   when a command is sent and high when data is sent.
satiromarra commented 3 years ago

Yeah! works fine, but when I run the script again receive this error: luma/lcd/device.py:76: RuntimeWarning: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings. self._gpio.setup(self._pin, self._gpio.OUT)

Using: https://github.com/rm-hull/luma.core.git and https://github.com/dhrone/luma.lcd.git@hd44780 With https://github.com/dhrone/luma.core.git the display shows invalids characters.

Do you need any more information?

dhrone commented 3 years ago

That warning happens when the RPi.GPIO module is not cleaned up at the end of the program. There was a small bug my code that allocated one of the GPIO pins even when the PCF8574 was going to be responsible for the backlight. I believe I've eliminated it but it would be great if you could pull the latest and give it another run or three to see if the warning goes away.

satiromarra commented 3 years ago

Works fine! good job!! The backlight stays on after the script ends, I need add the line: device.backlight(False) at end of script.

dhrone commented 3 years ago

You need to add that but will also need to send some command or data to the display. So..

device.backlight(False)
device.show()

The reason for this has to do with how the PCF8574 works. Each pin from the pcf8574 is attached to the display and the driver controls the display by sending values that cause the appropriate pins to be low or high. When you call `backlight(False)' you are changing the value for that pin that will be sent inside future bytes but not actually causing the pin to change value. That will not happen until you send something (anything) to the display.

thijstriemstra commented 3 years ago

Yeah! works fine

What code are you using @satiromarra? I have the same i2c backpack and 16x2 display and no text is showing up. Using:

from luma.core.interface.serial import pcf8574
from luma.lcd.device import hd44780

interface = pcf8574(address=0x27, backlight_enabled=True)
device = hd44780(interface, width=16, height=2)
device.text = "Hello world"

Also:

$ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --           

The backlight pin works though;

interface = pcf8574(address=0x27, backlight_enabled=False)

disables the backlight at startup.

Wiring:

IMG_1496

thijstriemstra commented 3 years ago

Oh ffs, I had to add a time.sleep at the end of the program. I expected the display to retain the content that was written to it after the python script exited but alas, no. Final test script:

import time

from luma.core.interface.serial import pcf8574
from luma.lcd.device import hd44780

interface = pcf8574(address=0x27, backlight_enabled=True)
device = hd44780(interface, width=16, height=2)
device.text = "Hello world"

time.sleep(20000)

Awesome!

IMG_1498

thijstriemstra commented 3 years ago

@dhrone can you take a look at documenting the i2c backpack usage in the docs?

And I'm also interested in adding support to luma.examples for i2c pcf8574 16 x 2 hd44780..

thijstriemstra commented 3 years ago

Which device do you want to attach the pcf8574 to?

Does this imply other displays can be connected to this "backpack"? Didn't know that.. Which ones?

satiromarra commented 3 years ago

@thijstriemstra I used 2 displays in the same PCF8574, 20x4 and 16x2; my i2c address is 0x3F When the script finishes the lcd screen is empty. I hope helped you.

the code:

#!/usr/bin/env python3

import time
import subprocess
from datetime import datetime
from luma.core.interface.serial import parallel, pcf8574
from luma.lcd.device import hd44780

interface = pcf8574(address=0x3F, backlight_enabled=True)
device = hd44780(interface, width=20, height=4)
# device = hd44780(interface, width=16, height=2)

try:
    while (True):
        CurDate = datetime.now().strftime('%m/%d/%y %H:%M:%S')

        cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%d GB  %s\", $3,$2,$5}'"
        Disk = subprocess.check_output(cmd, shell=True).decode("utf-8").strip()

        cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%s MB %.2f%%\", $3,$2,$3*100/$2 }'"
        MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8").strip()

        cmd = "top -bn1 | grep load | grep -v grep | sed 's/,/./g' | awk '{printf \"CPU Load: %.2f \", $(NF-2)}'"
        CPU = subprocess.check_output(cmd, shell=True).decode("utf-8").strip()

        device.text = '\n'.join([CurDate, Disk, CPU, MemUsage])
        time.sleep(1)

except KeyboardInterrupt:
    pass

device.backlight(False)
device.show()
thijstriemstra commented 3 years ago

thanks @satiromarra. You should upgrade luma.core (sudo -H pip3 install -U luma.core) and adjust this import:

from luma.core.interface.serial import parallel, pcf8574

and remove the parallel import.

satiromarra commented 3 years ago

luma.core updated: Successfully installed luma.core-1.16.2 I tried again and works fine (and removed parallel import).

thijstriemstra commented 3 years ago

Closing ticket since it's fixed on master, available in next release.