devices icon indicating copy to clipboard operation
devices copied to clipboard

[inky] Add Inky Impression 7.3" support

Open fstanis opened this issue 1 year ago • 4 comments

Inky Impression 7.3" is a larger e-ink display that unlike smaller variants is based on AC073TC1A rather than UC8159.

This PR is effectively a port of pimoroni/inky#158 since the difference is primarily in the reset and update commads.

(draft until I have a chance to properly test)

fstanis avatar Sep 28 '24 15:09 fstanis

Codecov Report

Attention: Patch coverage is 0% with 141 lines in your changes missing coverage. Please review.

Project coverage is 51.3%. Comparing base (cad2275) to head (2208b5e). Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
inky/impression.go 0.0% 138 Missing :warning:
inky/types.go 0.0% 3 Missing :warning:

:x: Your patch status has failed because the patch coverage (0.0%) is below the target coverage (40.0%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@           Coverage Diff           @@
##            main     #75     +/-   ##
=======================================
+ Coverage   49.8%   51.3%   +1.5%     
=======================================
  Files         91     102     +11     
  Lines      11867   13201   +1334     
=======================================
+ Hits        5914    6774    +860     
- Misses      5695    6135    +440     
- Partials     258     292     +34     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

codecov-commenter avatar Sep 28 '24 18:09 codecov-commenter

I just saw the PR as it is a draft. Can you fix the linter errors? You'll need to rebase.

maruel avatar Dec 07 '24 21:12 maruel

@fstanis I updated the PR to the current head of periph.io/devices and addressed a minor lint error.

Did you ever get a chance to test this?

gsexton avatar Feb 10 '25 02:02 gsexton

Wow as was actually looking exactly for this :D

Looking forward to be able to use this.

Thanks

kedare avatar Feb 21 '25 16:02 kedare

Any way I can help to merge this ? I do own the display

kedare avatar Mar 02 '25 10:03 kedare

Great thanks :) I think it be only available when a new version has been tagged ? (except if I hardcode the commit during go get)

kedare avatar Mar 02 '25 15:03 kedare

Hmm I could not make it work so far, I found a few issues so far: Dev.Bounds() is always (0,0),(0,0), it's not properly set as it should automatically depending on the model (I had to force is via Opts.Height and Opts.Width)

  • When Drawing, it would randomly fail with the following logs :
2025/03/02 19:22:47 impression.go:558: Err: bcm283x-gpio (GPIO17): line_request ioctl: bad file descriptor
2025/03/02 19:22:48 impression.go:558: Err: bcm283x-gpio (GPIO17): line_request ioctl: bad file descriptor
2025/03/02 19:22:48 impression.go:558: Err: bcm283x-gpio (GPIO17): line_request ioctl: bad file descriptor
2025/03/02 19:22:48 impression.go:558: Err: bcm283x-gpio (GPIO17): line_request ioctl: bad file descriptor

Which apparently correspond to this : https://github.com/pimoroni/inky/issues/205 But on the python implementation this is not making the drawing fail, those are just warnings, it looks like on the Go implementation this stops the drawing (can't confirm as I can't draw at all)

  • When not raising the previous error, it's not actually drawing anything, it does block however the code like if it was indeed drawing something (around 40s)

This is the code I am using: https://github.com/kedare/dashink/blob/main/pkg/hardware/host.go

Thanks

kedare avatar Mar 02 '25 18:03 kedare

For the size always set to 0, This is the issue: https://github.com/periph/devices/blob/main/inky/impression.go#L208

It should be this I guess

	if o.Width != 0 && o.Height != 0 {
		d.width = o.Width
		d.height = o.Height
	}

kedare avatar Mar 02 '25 18:03 kedare

@kedare I'm assuming you're using a Raspberry Pi. Is that correct?

Can you tell us what version of periph.io/x/host is listed in your go.mod file? The latest version has updates to the WaitForEdge() calls.

Can you try testing with different pin names? For each required pin, ensure that they are prefixed with "GPIOXX". E.G. for the reset pin, pass "GPIO27".

gsexton avatar Mar 03 '25 04:03 gsexton

Yes, I am using a Raspberry Pi Zero 2W, I can use the official python code without problem (just with the warning about busy wait but that is not blocking it from working)

This is what I have in my go.mod

grep periph.io go.mod
        periph.io/x/conn/v3 v3.7.2 // indirect
        periph.io/x/devices/v3 v3.7.4-0.20250302141124-eb90d535341b // indirect
        periph.io/x/host/v3 v3.8.3 // indirect

Tried with the last revision from master, I don't get the logs anymore but same issue.

grep periph.io go.mod
        periph.io/x/conn/v3 v3.7.2
        periph.io/x/devices/v3 v3.7.4-0.20250302172059-ea55a05aa126
        periph.io/x/host/v3 v3.8.4-0.20250117130906-28c0c75f3193

I was using the GPIOxx before but switched to XX as I checked that the code from cmd/inky was doing this. I tried again with GPIOxx but same issue

kedare avatar Mar 03 '25 09:03 kedare

@kedare Thanks for posting that. The error you're getting seems to be coming out of the GPIO subsystem. Unfortunately, I don't have one of those displays and they're on the expensive side. Just to narrow it down, could you change how you're getting the GPIO pins and see if you're still getting the ioctl errors?

import (
	"periph.io/x/host/v3/gpioioctl"
)

func Example() {
	_, _ = host.Init()
	_, _ = driverreg.Init()


	chip := gpioioctl.Chips[0]
	fmt.Println(chip.String())

	reset:= chip.ByName("GPIO5")
	busy:=chip.ByName("GPIO6")
	...

gsexton avatar Mar 03 '25 14:03 gsexton

Thanks I will try that and let you know

Do you know if there is something like tcpdump that can be used for SPI or GPIO ? I could capture the python run and the go run so we can compare them.

kedare avatar Mar 03 '25 17:03 kedare

@kedare yes there's http://periph.io/x/conn/v3/gpio/gpiotest#LogPinIO and https://periph.io/x/conn/v3/spi/spitest#Record

maruel avatar Mar 03 '25 19:03 maruel

I tried with your GPIO method, I am getting the same issue (randomly throwing the error or not throwing the error, when it's not throwing it, it then pause like if it was drawing on the screen but nothing actually happen).

I posted the info there : https://gist.github.com/kedare/dab9dbb19d9d681c514d221e57f63235

Do you want me to setup the gpiotest and spitest thing ?

kedare avatar Mar 04 '25 09:03 kedare

@kedare Can you report what kernel you're using?

I've looked through the code, and I'm not seeing where the file descriptor is getting closed. Just to confirm the operation of the library, I ran some sample code on a pi zero, and it's working.

I'd like to understand why the file descriptor error is happening. I would be interested in log output of the file descriptor value when errors happen, and don't. It seems like a longshot, but it would confirm things.

If that doesn't offer anything really obvious, I would either run the program under strace and look for the close of the file descriptor, or run it under a debugger and see what I could notice.

FWIW, here's some code that inits and it gets the GPIO IOCTL pins via the bcm283x registration.

package main

import (
	"fmt"
	"time"

	"periph.io/x/conn/v3/driver/driverreg"
	"periph.io/x/conn/v3/gpio"
	"periph.io/x/conn/v3/gpio/gpioreg"
	"periph.io/x/host/v3"
	_ "periph.io/x/host/v3/bcm283x"
	"periph.io/x/host/v3/gpioioctl"
)

func main() {
	fmt.Println("Calling driverreg.init")
	if _, err := driverreg.Init(); err != nil {
		fmt.Println("Failed to initialize periph:", err)
		return
	}
	if _, err := host.Init(); err != nil {
		fmt.Println("Failed to initialize periph:", err)
		return
	}
	outPin := gpioreg.ByName("GPIO2")
	inPin := gpioreg.ByName("GPIO10")
	inPin.In(gpio.PullDown, gpio.RisingEdge)
	fmt.Printf("inPin=%s, Type: %#v\n", inPin, inPin)
	fmt.Printf("outPin=%s Type: %#v\n", outPin, outPin)

	go func() {
		l := gpio.Low
		for {
			l = !l
			fmt.Println("setting outPin to ", l)
			outPin.Out(l)
			time.Sleep(1000 * time.Millisecond)
		}
	}()

	for {
		if inPin.WaitForEdge(0) {
			fmt.Println("Received edge")
		} else {
			fmt.Println("WaitForEdge() unblocked without receiving edge.")
		}
	}
}

Output

}
inPin=GPIO10, Type: &bcm283x.Pin{number:10, name:"GPIO10", defaultPull:0x2, ioctlPin:(*gpioioctl.GPIOLine)(0x879b00), usingEdge:true, usingClock:false, dmaCh:(*bcm283x.dmaChannel)(nil), dmaBuf:(*videocore.Mem)(nil)}
outPin=GPIO2 Type: &bcm283x.Pin{number:2, name:"GPIO2", defaultPull:0x3, ioctlPin:(*gpioioctl.GPIOLine)(0x8798c0), usingEdge:false, usingClock:false, dmaCh:(*bcm283x.dmaChannel)(nil), dmaBuf:(*videocore.Mem)(nil)}
setting outPin to  High
setting outPin to  Low
setting outPin to  High
Received edge
setting outPin to  Low
setting outPin to  High
Received edge
setting outPin to  Low
setting outPin to  High
Received edge
...

gsexton avatar Mar 08 '25 19:03 gsexton

This is the kernel I am currently using (on RPI Zero 2W)

Linux 6.6.74+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.6.74-1+rpt1 (2025-01-27) aarch64 GNU/Linux

For the strace, I attached one when I don't have the error (but it doesn't draw anything), and one when it give the ioctl error.

trace-no-error.log trace-error.log

Cases happens randomly.

Somehow I had some issues getting the strace, it would hangs up most of the time before even being at the drawing step.

kedare avatar Mar 08 '25 21:03 kedare

From your code (I just ran it without any change), this is what I get (not sure if this expecting some wiring or manual operation?)

./gpio-test
Calling driverreg.init
inPin=GPIO10, Type: &bcm283x.Pin{number:10, name:"GPIO10", defaultPull:0x2, ioctlPin:(*gpioioctl.GPIOLine)(0x4000180600), usingEdge:true, usingClock:false, dmaCh:(*bcm283x.dmaChannel)(nil), dmaBuf:(*videocore.Mem)(nil)}
outPin=GPIO2 Type: &bcm283x.Pin{number:2, name:"GPIO2", defaultPull:0x3, ioctlPin:(*gpioioctl.GPIOLine)(0x4000180300), usingEdge:false, usingClock:false, dmaCh:(*bcm283x.dmaChannel)(nil), dmaBuf:(*videocore.Mem)(nil)}
setting outPin to  High
setting outPin to  Low
setting outPin to  High
setting outPin to  Low
setting outPin to  High
setting outPin to  Low
setting outPin to  High
setting outPin to  Low
setting outPin to  High
setting outPin to  Low
setting outPin to  High
setting outPin to  Low
setting outPin to  High
setting outPin to  Low
setting outPin to  High
setting outPin to  Low
setting outPin to  High
setting outPin to  Low

kedare avatar Mar 08 '25 21:03 kedare

@kedare It would need a jumper between GPIO2 and GPIO10 for the edge on inpin to trigger.

gsexton avatar Mar 10 '25 23:03 gsexton

Thanks, there you have

❯ ./gpio-test
Calling driverreg.init
inPin=GPIO10, Type: &bcm283x.Pin{number:10, name:"GPIO10", defaultPull:0x2, ioctlPin:(*gpioioctl.GPIOLine)(0x4000100660), usingEdge:true, usingClock:false, dmaCh:(*bcm283x.dmaChannel)(nil), dmaBuf:(*videocore.Mem)(nil)}
outPin=GPIO2 Type: &bcm283x.Pin{number:2, name:"GPIO2", defaultPull:0x3, ioctlPin:(*gpioioctl.GPIOLine)(0x4000100360), usingEdge:false, usingClock:false, dmaCh:(*bcm283x.dmaChannel)(nil), dmaBuf:(*videocore.Mem)(nil)}
setting outPin to  High
Received edge
setting outPin to  Low
setting outPin to  High
Received edge
setting outPin to  Low
setting outPin to  High
Received edge
setting outPin to  Low
setting outPin to  High
Received edge
setting outPin to  Low
setting outPin to  High
Received edge
setting outPin to  Low
setting outPin to  High
Received edge

kedare avatar Mar 12 '25 12:03 kedare

That's working as expected then. I've got a display on order. If you leave your repo up, I'll take a look. It will take at least a week, I'm sure for it to arrive.

gsexton avatar Mar 18 '25 01:03 gsexton