mcarr823 / papertty-init

Installer scripts for PaperTTY
GNU General Public License v3.0
5 stars 1 forks source link

Failed to init gpiozero spi device #3

Open Coastline-3102 opened 4 months ago

Coastline-3102 commented 4 months ago

Hello!

I am trying to get papertty-init to run on my Pi4,. I am using a fresh install of Raspberry Pi OS Lite and this e-ink display. I have ran papertty-init cli with the following settings:

Your settings are as follows:
PaperTTY version: forked
Library: RPi.GPIO
Automatic login: enabled
Panel/driver: EPD7in5v2

papertty-init runs and reports the installation has finished. When I reboot the screen does not work, and if I manually run bash startpapertty.sh it says

gpiozero not found - defaulting to RPi.GPIO
Setting spacing to 0.
Failed to init gpiozero spi device

and freezes. My /boot/firmware/config.txt does have

#dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=on

and I have rebooted multiple times, so I believe SPI is working. Any guidance on how to fix this?

mcarr823 commented 3 months ago

Taking a look at the Amazon listing, I think you might have the wrong driver. The Amazon listing has a bit.ly link to the waveshare manual for the EPD7in5 rather than the EPD7in5v2. Could you give that a shot and see if it works? They're similar panels, but they as far as the driver is concerned they're controlled differently.

The error messages are a bit of a red herring. Failed to init gpiozero spi device means the gpiozero driver wasn't installed, so it's defaulting to Rpi.GPIO. That bit isn't what's causing the hang. But since it's the last message you see right before the hang, it's natural to assume that it's related.

Coastline-3102 commented 3 months ago

Thanks for the response!

I was using EPD7in5v2 as the demo on that link says

Compile the demo (Note: -j4 is to compile with 4 threads, the numbers can be modified by yourself; EPD=epd7in5V2 is to specify a macro definition, and epd7in5v2 corresponds to the test demo in the main function).

and so I assumed EPD7in5v2 was the correct driver. I will admit I am a bit confused about just which drivers to use, so it is entirely possible I am mistaken! To further complicate matters, my display does have a small sticker that says "V2" on it...

Regardless, I did just test your suggestion

Your settings are as follows:
PaperTTY version: forked
Library: RPi.GPIO
Automatic login: enabled
Panel/driver: EPD7in5

this behaved the same, with the program hanging at

gpiozero not found - defaulting to RPi.GPIO
Setting spacing to 0.
Failed to init gpiozero spi device

Also, I have done some troubleshooting of my own, with mixed success:

I noticed that the driver board has some switches on them (see below image) but was unfortunately unable to find any explicit documentation on what they should be set to. While still using my original config

Your settings are as follows:
PaperTTY version: forked
Library: RPi.GPIO
Automatic login: enabled
Panel/driver: EPD7in5v2

I tried just switching them to see if that would have any affect. Changing "Display Config" did nothing. Changing "Interface Config" to 1 (3-line SPI) seems to have some sort of affect. When using 3-line SPI if I run bash startpapertty.sh I get the following message:

gpiozero not found - defaulting to RPi.GPIO
Setting spacing to 0.
Failed to init gpiozero spi device
Automatic resize of TTY to 12 rows, 42 columns
Started displaying /dev/vcsa1, minimum update interval 0.1 s, exit with Ctrl-C

This does seem like progress, but despite that, the display still does not work, or even turn on. I have also tested 3-line SPI with the EPD7in5 drivers and it behaves the same (hangs at Started displaying /dev/vcsa1, minimum update interval 0.1 s, exit with Ctrl-C) with the e-ink display remaining blank.

I should perhaps mention I am doing all this while SSHed into my Pi. I don't think that would be affecting things?

image

mcarr823 commented 3 months ago

It sounds like your panel must be the V2 then. I was just going off the link on the Amazon listing. The V2 sticker on the board/panel is a better indication.

Being SSHed into the rpi shouldn't make a difference. The 3/4 wire SPI selection does seem to though. As far as papertty is concerned it seems to think it's working, since it got up to the "Started displaying" line.

One thing you could try is running the commands from within startpapertty.sh with different arguments. For example, you could append --scrub to the end, which should force it to scrub the screen (drawing lots of black and white lines) before starting up. That would be a clear visual indication of if it's working or not. Or you could adjust the font size (it might be too big/small to show correctly).

Other than that I'm not sure what to suggest. It sounds like papertty is communicating with the board now at least. I don't own an EPD7in5v2, and I'm not sure what the Display Config or Interface Config options on the board should be.

Coastline-3102 commented 3 months ago

Alright. I have noticed that there seem to be a few different 7in5 drivers

EPD7in5
EPD7in5b_V2
EPD7in5v2
EPD7in5b

Thus far I have been using EPD7in5v2 (assuming I have a V2 display). Do you know what the 'b' model drivers are? Could those be the correct ones?

Unfortunately --scrub does not seem to be doing anything. Do you have any advice on how to further debug this? For example, is there a way to get papertty to provide more logs/debug info? I'm afraid right now I am a bit at a loss...

You mentioned

It sounds like papertty is communicating with the board now at least.

So does that mean the line Started displaying /dev/vcsa1, minimum update interval 0.1 s, exit with Ctrl-C indicates that it has a connection to the display, and something is just causing it not to actually display?

I will also note, on the off chance this is relevant, that once it says Started displaying /dev/vcsa1, minimum update interval 0.1 s, exit with Ctrl-C hitting Ctrl-C does print Exiting (SIGINT)... but does not exit when I am using EPD7in5v2 (it does exit when using EPD7in5 or EPD7in5b_V2), so I have to power down the pi. I'm not sure if this is a completely unrelated issue...

Thanks for all your help with this.

mcarr823 commented 3 months ago

The "b" models are the color panels. The box or the driver board might have something to say if it's B&W or color. Some have a third color, so they're black, white and red, for example.

The fact that it got up to the Started displaying /dev/vcsa1 line would suggest to me that papertty was able to communicate with the display. ie. It didn't run into an error when trying to setup the GPIO pins or SPI device. If it did run into any errors there I would expect it to either crash or hang.

One suggestion I would have is to try the test programs made by waveshare. https://github.com/waveshareteam/e-Paper/tree/master/RaspberryPi_JetsonNano/python/examples By running a couple of those for different models you should be able to identify which panel you have and whether the panel is working or not. It should be a much faster way of testing the panel and connection than running papertty with different arguments each time.

Coastline-3102 commented 3 months ago

Alright, I have done some more testing with the demo code. Following this guide, and on a fresh install, I tried both the C and python examples. C with sudo make -j4 EPD=epd7in5V2 and python with python3 epd_7in5_V2_test.py worked without issues, so I think it is safe to say that I have a epd_7in5_V2 display.

Notably, both demos only worked when the driver board was set to 4-line SPI, and hung when set to 3-line SPI. This is opposite to the behavior I saw when working with papertty-init.

At this point, I am pretty confident that the issue is with papertty-init/papertty as the fact that the demos work seem to suggest that the HW is good, and I am using the right driver. I'll keep working on this and see if I can somehow figure out the issue.

mcarr823 commented 3 months ago

If it's working with the waveshare demo code then it's sounding like an issue with papertty. It could be that the epd7in5v2 driver has a bug in it, or that a newer revision of the board requires some code changes.

Looking at https://github.com/waveshareteam/e-Paper/blob/master/RaspberryPi_JetsonNano/python/lib/waveshare_epd/ there are both epd7in5_V2.py and epd7in5_V2_old.py I don't see any other files in there with a _old version. Which might suggest that, unlike new revisions of other boards, this one changed in an incompatible way. The initialization code does look a bit different than what's currently in papertty.

You could try putting the board in 4-line SPI and updating the EPD7in5v2 class in drivers_full.py to more closely match the waveshare version. I haven't looked at the differences in detail, but given where it was failing before, I'd start with updating the init function and trying again.

Coastline-3102 commented 3 months ago

I made a fork of papertty and for my initial attempt added a few lines from the epd7in5_V2.py driver which seemed to be missing from papertty's def init. Unfortunately this has not fixed the issue. Regardless of the SPI line type, my adjusted papertty just reports:

gpiozero not found - defaulting to RPi.GPIO
Setting spacing to 0.
Failed to init gpiozero spi device

Is there any documentation you can point me to that might help me better understand papertty and the waveshare drivers? I am afraid with only intermediate python skills and my unfamiliarity with e-ink displays figuring out what changes need to be made is proving tricky.

Thanks!

mcarr823 commented 3 months ago

What you've added looks correct to me. My suggestion would be to add logging statements to all of the functions in class EPD7in5v2. Then narrow down where exactly it's choking. eg. Is the init function getting halfway through and then hanging on a particular statement? Is the init function finishing, but the display_frame function hanging? The comments on the overridden "reset" method suggest that some of those panels hang after the POWER_ON command if the delays are too short.

It might be that you need to override one of the functions which it's currently inheriting from WaveshareFull. The comments in the EPD7in5v2 class suggest that it's already doing a few things differently than the parent class. (On a side note, its overridden reset method should be updated to remove the import and replace the GPIO.HIGH/LOW methods to make it compatible with gpiozero. Currently it's only going to work with Rpi.GPIO... though this wouldn't be related to your issues)

I'm not aware of any resources for learning papertty. All I've been doing is reading the source code and going through trial and error (occasionally going through the waveshare docs). The papertty device drivers use fairly similar names and conventions to the waveshare example python drivers, so a lot can be gleaned from there.

Waveshare does have documentation, but its usefulness varies. You can find different panels on their wiki. eg. https://www.waveshare.com/wiki/7.5inch_e-Paper_HAT The intro usually has the most useful information. https://www.waveshare.com/wiki/7.5inch_e-Paper_HAT_Manual#Overview

Coastline-3102 commented 1 week ago

So, I have finally had some more time to look at this again...

As you suggested, I added some basic print statements to the code.

        print("Starting command block 1") # runs fine
        # from line 92 of epd7in5_V2.py
        self.send_command(0x06)     # btst
        self.send_data(0x17)
        self.send_data(0x17)
        self.send_data(0x28)
        self.send_data(0x17)

        print("Starting command block 2") # runs fine
        self.send_command(self.POWER_SETTING)
        self.send_data(0x07) # VDS_EN, VDG_EN
        self.send_data(0x07) # VCOM_HV, VGHL_LV[1], VGHL_LV[0]
        self.send_data(0x3f) # VDH
        self.send_data(0x3f) # VDL

        print("Starting command block 3") 
        print("Sending power on")
        self.send_command(self.POWER_ON) 
        print("Waiting") 
        self.wait_until_idle() # Hangs here. Never goes idle?

It runs fine all the way up to the self.wait_until_idle() command, where it hangs. I took a look at that function:

    def wait_until_idle(self):
        self.send_command(0x71)
        while self.digital_read(self.BUSY_PIN) == 0:  # 0: busy, 1: idle
            print(f"Busy pin is {self.digital_read(self.BUSY_PIN)}") 
            self.send_command(0x71)
            print("Sending 0x71")
            self.delay_ms(200) # adjusting this to 200 ms to match waveshare code

and have found that the BUSY_PIN never goes to 1. Running bash /home/user/.local/bin/papertty-init/startpapertty.sh

gpiozero not found - defaulting to RPi.GPIO
Setting spacing to 0.
Failed to init gpiozero spi device
Starting command block 1
Starting command block 2
Starting command block 3
Sending power on
Waiting
Busy pin is 0
Sending 0x71
Busy pin is 0
Sending 0x71
Busy pin is 0
Sending 0x71
Busy pin is 0
Sending 0x71
Busy pin is 0

That makes me think that something is wrong with the proceeding blocks of code, but from what I can see, they all match the waveshare drivers.

I also took a look at the reset function (which I am pretty sure is not the issue, but might as well be sure). The only diffrance I could see between the waveshare drivers and PaperTTY is waveshare has delay_ms(4) while TTY has delay_ms(2) I did change it to 4, but that had no effect.

mcarr823 commented 1 week ago

I've taken another look at the waveshare code. PaperTTY's EPD7in5v2 driver matches waveshare's original implementation of the driver. But there have been quite a few changes since then which have made the old code no longer work with newer board revisions.

Going by the waveshare docs https://www.waveshare.com/wiki/7.5inch_e-Paper_HAT_Manual

The V2 version code has been upgraded to support partial refresh and fast refresh functionalities. For versions sold after September 2023, use the 7.5V2 program. For versions sold before September 2023, use the 7.5V2_old program.

So in short the board has changed in an incompatible way with the old driver

The display code and init code look pretty similar though. This is just a guess, but I'm thinking the change to the way data is written to SPI is the incompatible part.

send_command and send_data currently only change the DC pin. In the new example, they also need to change the CS pin.

You could try copying those functions from my EPD2in13v4 driver (drivers_partial.py) and putting them in the EPD7in5v2 class.

    def send_command(self, command):
        self.digital_write(self.CS_PIN, GPIO.LOW)
        super().send_command(command)
        self.digital_write(self.CS_PIN, GPIO.HIGH)

    def send_data(self, data):
        self.digital_write(self.CS_PIN, GPIO.LOW)
        super().send_data(data)
        self.digital_write(self.CS_PIN, GPIO.HIGH)

    def send_data_multi(self, data):
        self.digital_write(self.CS_PIN, GPIO.LOW)
        super().send_data_multi(data)
        self.digital_write(self.CS_PIN, GPIO.HIGH)

I'm thinking that although the BUSY pin is never changing, it's because of one/all of the send_data and send_command requests before it not working properly. So if they're updated to match the newer code by also changing the CS pin, hopefully that'll fix it.

Coastline-3102 commented 1 week ago

Okay, I just gave that a try.

Unfortunately, nothing changed. I did confirm that the new functions are being used, it is just still getting stuck in the "busy pin is 0" loop.

I also took another look at the waveshare driver and saw that their version of those functions is different than yours

  # functions from waveshare driver
    def send_command(self, command):
        print("Using WS New Func!")
        self.digital_write(self.DC_PIN, 0)
        self.digital_write(self.CS_PIN, 0)
        self.spi_writebyte([command])
        self.digital_write(self.CS_PIN, 1)

    def send_data(self, data):
        print("Using WS New Func!")
        self.digital_write(self.DC_PIN, 1)
        self.digital_write(self.CS_PIN, 0)
        self.spi_writebyte([data])
        self.digital_write(self.CS_PIN, 1)

    def send_data_multi(self, data):
        print("Using WS New Func!")
        self.digital_write(self.DC_PIN, 1)
        self.digital_write(self.CS_PIN, 0)
        self.SPI.writebytes2(data)
        self.digital_write(self.CS_PIN, 1)

    def spi_writebyte(self, data):
        self.SPI.writebytes(data)

so I tested those as well. They also did not work, with it still stalling with busy pin 0.

For reference, the latest version of the code I'm using.

mcarr823 commented 4 days ago

Going over the comments on the reset function again, it sounds a lot like what you're running into.

    Mirroring behaviour in reference implementation:
    https://github.com/waveshare/e-Paper/blob/702def06bcb75983c98b0f9d25d43c552c248eb0/RaspberryPi%26JetsonNano/python/lib/waveshare_epd/epd7in5_V2.py#L48-L54

    The earlier implementation of `reset` inherited from `WaveshareFull` did not work with some units
    (`init` hanged at `wait_until_idle` after the `POWER_ON` command was sent).

    A quick scan of the other implementations indicates that the reset varies across devices (it's unclear
    whether there is good reason for device specific differences or if the developer was just being inconsistent...)
    e.g. significantly different delay times:
    https://github.com/waveshare/e-Paper/blob/702def06bcb75983c98b0f9d25d43c552c248eb0/RaspberryPi%26JetsonNano/python/lib/waveshare_epd/epd1in54c.py#L46-L52

I'd suggest playing with those values a bit to see if you can get past the wait_until_idle after POWER_ON. eg. Replace GPIO.HIGH with 1 and GPIO.LOW with 0 to match the waveshare code. (Those values should already match, but just to be safe...) And increasing the middle delay from 4 to something more substantial, like 200.

Coastline-3102 commented 4 days ago

Good idea. That comment does seem to describe the error I am getting. I messed around with the reset function though, and could not find a fix.

First, for reference, the waveshare function

    # Hardware reset
    def reset(self):
        epdconfig.digital_write(self.reset_pin, 1)
        epdconfig.delay_ms(20) 
        epdconfig.digital_write(self.reset_pin, 0)
        epdconfig.delay_ms(2)
        epdconfig.digital_write(self.reset_pin, 1)
        epdconfig.delay_ms(20)   

I tested a few different things, such as:

None of these changed the behavior (getting stuck with busy 0). I also confirmed that the reset pin is 17 in both the waveshare code and PaperTTY.

Looking at the issues over on the waveshare repo, I saw this issue, which made me think maybe waveshare's reset could be wrong... except I can run the example code, and it works.


I also saw this issue, and out of curiosity tried changing the line in wait_unit_idle to

while self.digital_read(self.BUSY_PIN) != 0

That did cause the code to run all the way through

gpiozero not found - defaulting to RPi.GPIO
Setting spacing to 0.
Failed to init gpiozero spi device
delay_1: 20, delay_2: 2, delay_3: 20
Starting command block 1
Starting command block 2
Starting command block 3
Sending power on
Waiting
Starting command block 4
Starting command block 5
Starting command block 6
Starting command block 7
Starting command block 8
Init finished.
Automatic resize of TTY to 15 rows, 53 columns
Started displaying /dev/vcsa1, minimum update interval 0.1 s, exit with Ctrl-C

but the screen is blank, so I don't think that is a fix.


Another thing I saw, was this mention of the self.send_command(0x71) in wait_until_idle I tried commenting them out to see if that changed anything. It did not.


Also tested this old issue, by changing wait_until_idle

    def wait_until_idle(self):
        self.send_command(0x71)
        count = 0
        print("Entering while loop....")
        print(f"Busy pin is {self.digital_read(self.BUSY_PIN)}")
        print(f"Count is {count}")
        while (self.digital_read(self.BUSY_PIN) == 0) and (count < 100):  # 0: busy, 1: idle
            print(f"Busy pin is {self.digital_read(self.BUSY_PIN)}") 
            self.send_command(0x71)
            print("Sending 0x71")
            self.delay_ms(20) # adjusting this to 200 ms to match waveshare code
            count += 1
            print(f"Count is {count}")

Intrestingly enough, the code made it through the init process

Count is 100
Starting command block 4
Starting command block 5
Starting command block 6
Starting command block 7
Starting command block 8
Init finished.
Automatic resize of TTY to 15 rows, 53 columns
Started displaying /dev/vcsa1, minimum update interval 0.1 s, exit with Ctrl-C
Entering while loop....
Busy pin is 0
Count is 0
Busy pin is 0
Sending 0x71
Count is 1

only to enter wait_until_idle again and stall at

Count is 99
Busy pin is 0
Sending 0x71
Count is 100

No idea if this is progress towards a fix, or just me breaking it in a new way... I also think this issue predates the new V2 code, so it might not be relevant to my screen. I will say this comment is a bit concerning, as it implies that there may be issues in waveshare's code, although again considering the example runs fine, maybe not?


So far, those are all the ideas I have. As usual, the latest changes are on my fork. And thanks for your continued help with this. I'd be even more lost without it!