periph / host

Go·Hardware·Lean - Host drivers
https://periph.io
Apache License 2.0
57 stars 32 forks source link

Edge detection does not work on Raspbian Bookworm #62

Open fischerman opened 1 month ago

fischerman commented 1 month ago

Describe the bug

The pin cannot be associated with a sysfs pin.

$ go run cmd/edge/edge.go 
2024/09/23 10:22:18 bcm283x-gpio (GPIO17): pin 17 is not exported by sysfs
exit status 1

To Reproduce Steps to reproduce the behavior:

  1. Run program
    
    package main

import ( "log"

"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/gpio/gpioreg"
"periph.io/x/host/v3"

)

func main() { if _, err := host.Init(); err != nil { log.Fatal(err) }

// Lookup a pin by its number:
p := gpioreg.ByName("GPIO17")
if p == nil {
    log.Fatal("Failed to find GPIO17")
}

// Set it as input, with an internal pull down resistor:
if err := p.In(gpio.PullDown, gpio.BothEdges); err != nil {
    log.Fatal(err)
}

p.WaitForEdge(-1)

}


2. See error

**Expected behavior**

No error on `In`.

**Platform:**
 - OS: Raspbian GNU/Linux 12 (bookworm)
 - Board: Raspberry Pi 2 Model B v1.1

**Additional context**

I'm not aware of any prerequisites that must be met. I tried running the program as root as well, but I get the same error.
fischerman commented 1 month ago

I think the problem is that starting with Bookworm sysfs GPIO has been removed. It might affect other distributions as well. At least all Raspberries are affected.

maruel commented 1 month ago

Can you test if https://github.com/periph/host/commit/b69b28c4e40f9f233222c9d7ea610ef0fcc8a54b fixes the problem? If so, I'll do a release.

fischerman commented 1 month ago

On the Pi 2 I'm getting a lot of errors now:

go run cmd/edge/edge.go 
2024/09/24 10:28:43 chip gpiochip0  gpioreg.Register(line)  {
    "Line": 33,
    "Name": "NC",
    "Consumer": "",
    "Direction": "NotSet",
    "Pull": "PullNoChange",
    "Edges": "NoEdge"
}  returned  gpioreg: can't register pin "NC" twice; already registered as "{\n    \"Line\": 30,\n    \"Name\": \"NC\",\n    \"Consumer\": \"\",\n    \"Direction\": \"NotSet\",\n    \"Pull\": \"PullNoChange\",\n    \"Edges\": \"NoEdge\"\n}"
2024/09/24 10:28:43 chip gpiochip0  gpioreg.Register(line)  {
    "Line": 34,
    "Name": "NC",
    "Consumer": "",
    "Direction": "NotSet",
    "Pull": "PullNoChange",
    "Edges": "NoEdge"
}  returned  gpioreg: can't register pin "NC" twice; already registered as "{\n    \"Line\": 30,\n    \"Name\": \"NC\",\n    \"Consumer\": \"\",\n    \"Direction\": \"NotSet\",\n    \"Pull\": \"PullNoChange\",\n    \"Edges\": \"NoEdge\"\n}"
2024/09/24 10:28:43 chip gpiochip0  gpioreg.Register(line)  {
    "Line": 36,
    "Name": "NC",
    "Consumer": "",
    "Direction": "NotSet",
    "Pull": "PullNoChange",
    "Edges": "NoEdge"
}  returned  gpioreg: can't register pin "NC" twice; already registered as "{\n    \"Line\": 30,\n    \"Name\": \"NC\",\n    \"Consumer\": \"\",\n    \"Direction\": \"NotSet\",\n    \"Pull\": \"PullNoChange\",\n    \"Edges\": \"NoEdge\"\n}"
2024/09/24 10:28:43 chip gpiochip0  gpioreg.Register(line)  {
    "Line": 37,
    "Name": "NC",
    "Consumer": "",
    "Direction": "NotSet",
    "Pull": "PullNoChange",
    "Edges": "NoEdge"
}  returned  gpioreg: can't register pin "NC" twice; already registered as "{\n    \"Line\": 30,\n    \"Name\": \"NC\",\n    \"Consumer\": \"\",\n    \"Direction\": \"NotSet\",\n    \"Pull\": \"PullNoChange\",\n    \"Edges\": \"NoEdge\"\n}"
2024/09/24 10:28:43 chip gpiochip0  gpioreg.Register(line)  {
    "Line": 39,
    "Name": "NC",
    "Consumer": "",
    "Direction": "NotSet",
    "Pull": "PullNoChange",
    "Edges": "NoEdge"
}  returned  gpioreg: can't register pin "NC" twice; already registered as "{\n    \"Line\": 30,\n    \"Name\": \"NC\",\n    \"Consumer\": \"\",\n    \"Direction\": \"NotSet\",\n    \"Pull\": \"PullNoChange\",\n    \"Edges\": \"NoEdge\"\n}"
2024/09/24 10:28:43 bcm283x-gpio (GPIO17): subsystem gpiomem not initialized and sysfs not accessible
exit status 1

Not sure if it is related but header-list no longer returns functions:

$ go run headers-list/main.go 
AUDIO: 2 pins
  Pos  Name    Func
  1    GPIO45  
  2    GPIO40  

HDMI: 1 pins
  Pos  Name    Func
  1    GPIO46  

P1: 40 pins
  Func    Name  Pos  Pos  Name    Func
          3.3V    1  2    5V          
         GPIO2    3  4    5V          
         GPIO3    5  6    GROUND      
         GPIO4    7  8    GPIO14      
        GROUND    9  10   GPIO15      
        GPIO17   11  12   GPIO18      
        GPIO27   13  14   GROUND      
        GPIO22   15  16   GPIO23      
          3.3V   17  18   GPIO24      
        GPIO10   19  20   GROUND      
         GPIO9   21  22   GPIO25      
        GPIO11   23  24   GPIO8       
        GROUND   25  26   GPIO7       
         GPIO0   27  28   GPIO1       
         GPIO5   29  30   GROUND      
         GPIO6   31  32   GPIO12      
        GPIO13   33  34   GROUND      
        GPIO19   35  36   GPIO16      
        GPIO26   37  38   GPIO20      
        GROUND   39  40   GPIO21
gsexton commented 3 weeks ago

@fischerman Can you try running this example:

host/gpioioctl/example_test.go

Evert-Arends commented 2 weeks ago

I want to see this fixed so I ran a gpio out on my RPI 5, using the branch you provided @maruel

pi@worker-pi-1:~/GraftXL/cmd/run3 $ go get periph.io/x/host/v3@b69b28c4e40f9f233222c9d7ea610ef0fcc8a54b
go: downloading periph.io/x/host/v3 v3.8.3-0.20240918234808-b69b28c4e40f

which quickly died:

2024/10/08 20:13:09 chip gpiochip4  gpioreg.Register(line)  {
    "Line": 47,
    "Name": "2712_WAKE",
    "Consumer": "",
    "Direction": "NotSet",
    "Pull": "PullNoChange",
    "Edges": "NoEdge"
}  returned  gpioreg: can't register pin "2712_WAKE" twice; already registered as "{\n    \"Line\": 8,\n    \"Name\": \"2712_WAKE\",\n    \"Consumer\": \"\",\n    \"Direction\": \"NotSet\",\n    \"Pull\": \"PullNoChange\",\n    \"Edges\": \"NoEdge\"\n}"
Failed to set pin as output: bcm283x-gpio (GPIO17): subsystem gpiomem not initialized and sysfs not accessible

Using this example code for my motor that works on the RPI4:

package main

import (
    "fmt"
    "time"
    "periph.io/x/conn/v3/gpio"
    "periph.io/x/host/v3"
    "periph.io/x/host/v3/rpi"
)

func main() {
    if _, err := host.Init(); err != nil {
        fmt.Println("Failed to initialize periph:", err)
        return
    }

    stepPin := rpi.P1_11 // Use GPIO17

    // Set the pin as an output
    if err := stepPin.Out(gpio.Low); err != nil {
        fmt.Println("Failed to set pin as output:", err)
        return
    }

    pulseCount := 800

    pulseDuration := 1000 * time.Microsecond

    // Generate pulses
    fmt.Printf("Sending %d pulses...\n", pulseCount)
    for i := 0; i < pulseCount; i++ {
        if err := stepPin.Out(gpio.High); err != nil {
            fmt.Println("Failed to set pin high:", err)
            return
        }

        time.Sleep(pulseDuration / 2)

        if err := stepPin.Out(gpio.Low); err != nil {
            fmt.Println("Failed to set pin low:", err)
            return
        }

        time.Sleep(pulseDuration / 2)
    }

    fmt.Println("Finished sending pulses.")
}

I hope this helps in someway!

gsexton commented 2 weeks ago

@Evert-Arends

There are 4 GPIO chips on the Pi 5, and they all export a pin named "2712_WAKE". This causes the error you're seeing.

Take the return out of the host.Init() statement and see what it does.

Evert-Arends commented 2 weeks ago

Did that, removed the return.

2024/10/09 19:10:11 chip gpiochip4  gpioreg.Register(line)  {
    "Line": 47,
    "Name": "2712_WAKE",
    "Consumer": "",
    "Direction": "NotSet",
    "Pull": "PullNoChange",
    "Edges": "NoEdge"
}  returned  gpioreg: can't register pin "2712_WAKE" twice; already registered as "{\n    \"Line\": 8,\n    \"Name\": \"2712_WAKE\",\n    \"Consumer\": \"\",\n    \"Direction\": \"NotSet\",\n    \"Pull\": \"PullNoChange\",\n    \"Edges\": \"NoEdge\"\n}"
Failed to set pin as output: bcm283x-gpio (GPIO23): subsystem gpiomem not initialized and sysfs not accessible
func main() {
        if _, err := host.Init(); err != nil {
                fmt.Println("Failed to initialize periph:", err)
                //        return
        }

        stepPin := rpi.P1_16 // 23

        // Set the pin as an output
        if err := stepPin.Out(gpio.Low); err != nil {
                fmt.Println("Failed to set pin as output:", err)
                return
        }

        pulseCount := 800

        pulseDuration := 1000 * time.Microsecond

        // Generate pulses
        fmt.Printf("Sending %d pulses...\n", pulseCount)
        for i := 0; i < pulseCount; i++ {
                if err := stepPin.Out(gpio.High); err != nil {
                        fmt.Println("Failed to set pin high:", err)
                        return
                }

                time.Sleep(pulseDuration / 2)

                if err := stepPin.Out(gpio.Low); err != nil {
                        fmt.Println("Failed to set pin low:", err)
                        return
                }

                time.Sleep(pulseDuration / 2)
        }

        fmt.Println("Finished sending pulses.")
}
gsexton commented 2 weeks ago

@fischerman and @Evert-Arends

Here is a basic program using the gpioioctl code that demonstrates edge detection. It assumes you have a jumper connecting GPIO2 and GPIO 10. This was run on a Raspberry Pi 5:

gsexton@raspberrypi:~ $ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 12 (bookworm)
Release:        12
Codename:       bookworm
gsexton@raspberrypi:~ $ uname -a
Linux raspberrypi 6.6.45-v8-16k+ #1791 SMP PREEMPT Tue Aug 13 12:52:29 BST 2024 aarch64 GNU/Linux

To build this, you'll need the commit that has gpioioctl in it:

gsexton@raspberrypi:~ $ go get periph.io/x/host/v3@b69b28c4e40f9f233222c9d7ea610ef0fcc8a54b

main.go

package main

import (
    "fmt"
    "time"

    "periph.io/x/conn/v3/gpio"
    "periph.io/x/host/v3"
    "periph.io/x/host/v3/gpioioctl"
)

func main() {
    if _, err := host.Init(); err != nil {
        fmt.Println("Failed to initialize periph:", err)
        //        return
    }

    gpiochip:=gpioioctl.Chips[0]
    outPin:=gpiochip.ByName("GPIO2")
    inPin:=gpiochip.ByName("GPIO10")
    inPin.In(gpio.PullDown,gpio.RisingEdge)
    fmt.Println("inPin=",inPin)
    fmt.Println("outPin=",outPin)

    l:=gpio.Low
    go func() {
        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.")
        }
    }
}

Sample Output:

gsexton@raspberrypi:~ $ ./simpledge 
2024/10/09 18:57:50 chip gpiochip12  gpioreg.Register(line)  {
    "Line": 8,
    "Name": "2712_WAKE",
    "Consumer": "",
    "Direction": "NotSet",
    "Pull": "PullNoChange",
    "Edges": "NoEdge"
}  returned  gpioreg: can't register pin "2712_WAKE" twice; already registered as "{\n    \"Line\": 47,\n    \"Name\": \"2712_WAKE\",\n    \"Consumer\": \"\",\n    \"Direction\": \"NotSet\",\n    \"Pull\": \"PullNoChange\",\n    \"Edges\": \"NoEdge\"\n}"
inPin= {
    "Line": 10,
    "Name": "GPIO10",
    "Consumer": "simpledge@2246",
    "Direction": "Input",
    "Pull": "PullDown",
    "Edges": "RisingEdge"
}
outPin= {
    "Line": 2,
    "Name": "GPIO2",
    "Consumer": "",
    "Direction": "NotSet",
    "Pull": "PullNoChange",
    "Edges": "NoEdge"
}
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
^C
fischerman commented 1 week ago

@gsexton Your example works on my RPi2. I compared it to my program and what seems to break it is:

gpioreg.ByName("GPIO17")

gpioioctl.Chips[0] on the other hand works fine.

gsexton commented 1 week ago

@fischerman I've looked into this.

The problem is the bcm283x driver is running after the gpioioctl register code. That driver explicitly unregisters the gpioioctl registered GPIO pins values and supplies non-working sysfs based pins, causing things to break (line 1395 of host/bcm283x/gpio.go).

@maruel do you have any ideas on the correct fix?

maruel commented 1 week ago

bcm283x needs to condition on which driver is loaded and act accordingly.

damdo commented 1 day ago

Is this then solved by https://github.com/periph/host/pull/63 ? Should this be closed? Thanks!