Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intermittent pins #10

Closed
autr opened this issue Jun 27, 2021 · 6 comments
Closed

Intermittent pins #10

autr opened this issue Jun 27, 2021 · 6 comments

Comments

@autr
Copy link

autr commented Jun 27, 2021

Experiencing a quite weird effect and hoping you could help - I'm finding only some my pin bindings will work within my node application, but if I then test a Python script on the same pin numbers, I will start receiving those missing pin bindings inside my node app.

Here is the node code - where initially only OMNI and SKIPPREV (pins 8 and 7) will work:

const VOLUP = 'VOLUP'
const VOLDOWN = 'VOLDOWN'
const OMNI = 'OMNI'
const SKIPNEXT = 'SKIPNEXT'
const SKIPPREV = 'SKIPPREV'
const PLAYPAUSE = 'PLAYPAUSE'

const BTNS = {
    12: VOLUP,
    9: VOLDOWN,
    8: OMNI,
    11: SKIPNEXT,
    10: PLAYPAUSE,
    7: SKIPPREV
}

console.log( `[o-dsk] pins ${Object.keys(BTNS).join(',')}`)

const gpio = async e => {

    let buttons = new RPiGPIOButtons( { 
        pins: Object.keys(BTNS),
        mode: RPiGPIOButtons.MODE_BCM,
        usePullUp: false,
        debounce: 10,
        pressed: 10,
        clicked: 10
    } )
    buttons.on('pressed', async pin => {
        let PIN = BTNS[pin] 
        console.log('[o-dsk] released', PIN)
    })
    buttons.on('clicked', async pin => {
        let PIN = BTNS[pin] 
        console.log('[o-dsk] clicked', PIN)
    })
    buttons.on('released', async pin => {
        let PIN = BTNS[pin]
        console.log('[o-dsk] pressed', PIN)
    })
    buttons.init().catch(err => {
        console.error('[o-dsk] error initialising buttons:', err.message)
    })
}

Then once I run the following Python script the rest of the pins start working:

#!/usr/bin/env python
# sudo apt-get install python-dev python-rpi.gpio

import RPi.GPIO as GPIO
from time import sleep
import signal, os, subprocess, sys

buttons = [26,24,21,19,23,32,7,8,9,10,11,12]


def button_pressed(channel):
    print("BUTTON PRESSED")
    print(channel)
def unregister_events():
    for pin in buttons:
        GPIO.remove_event_detect(pin)

if __name__ == '__main__':
    signal.signal(signal.SIGINT, unregister_events)
    try:
        GPIO.setmode(GPIO.BCM)
        for pin in buttons:
            GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
            GPIO.add_event_detect(pin, GPIO.RISING, callback=button_pressed, bouncetime=200)
        while True:
            sleep(10)
    except Exception as e:
        print("Caught exception:", e)
        unregister_events()

Wondering if its to do with leftover states or bindings - I've tried clearing with destroy function and also unregistering pins with echo "11" > /sys/class/gpio/unexport etc, but it's only the Python script which gets it working again.

@bnielsen1965
Copy link
Owner

Try adding a buttons listener for the "error" event similar to the test script, i.e. test script and see if that provides a clue.

Also try running your script with sudo, perhaps the user does not have permission to access the gpio settings with rpi-gpio library.

@autr
Copy link
Author

autr commented Jun 30, 2021

Hmm, already running with sudo, and output of debug / error seems OK:

debug Setup button pin 7.
debug Setup button pin 8.
debug Setup button pin 9.
debug Setup button pin 10.
debug Setup button pin 11.
debug Setup button pin 12.
debug Initialize listener for button pin 7.
debug Preread button pin 7.
debug Initialize listener for button pin 8.
debug Preread button pin 8.
debug Initialize listener for button pin 9.
debug Preread button pin 9.
debug Initialize listener for button pin 10.
debug Preread button pin 10.
debug Initialize listener for button pin 11.
debug Preread button pin 11.
debug Initialize listener for button pin 12.
debug Preread button pin 12.
debug Listen for changes to gpio pins.

I will take a closer look at rpio-gpio to see what's going on

@autr
Copy link
Author

autr commented Jun 30, 2021

Update: pins 7/8 (which are working) return true during preread, and the rest of the pins return false. I'm on a Pi Zero and ran into the permissions error / race condition outlined here: JamesBarwell/rpi-gpio.js#112

Adding a variable (100ms) delay to each assignment cleared up the permissions error, but still not getting a on('change') event from rpio-gpio.js - will investigate more...

@bnielsen1965
Copy link
Owner

bnielsen1965 commented Jun 30, 2021

What OS are you running on the Pi Zero?

I have a Pi Zero here, I'll set it up on the bench and do some testing.

UPDATE:

I tested on a Pi Zero W with raspbian buster (10.8) and all the pins worked okay with the code you provided. I didn't see that timing bug either.

I created a new directory, did an npm install --save rpi-gpio-buttons, then pasted your code into a new index.js plus some extras to call your gpio method, and tested each button input.

const RPiGPIOButtons = require('rpi-gpio-buttons')

const VOLUP = 'VOLUP'
const VOLDOWN = 'VOLDOWN'
const OMNI = 'OMNI'
const SKIPNEXT = 'SKIPNEXT'
const SKIPPREV = 'SKIPPREV'
const PLAYPAUSE = 'PLAYPAUSE'

const BTNS = {
    12: VOLUP,
    9: VOLDOWN,
    8: OMNI,
    11: SKIPNEXT,
    10: PLAYPAUSE,
    7: SKIPPREV
}

console.log( `[o-dsk] pins ${Object.keys(BTNS).join(',')}`)

const gpio = async e => {

    let buttons = new RPiGPIOButtons( { 
        pins: Object.keys(BTNS),
        mode: RPiGPIOButtons.MODE_BCM,
        usePullUp: false,
        debounce: 10,
        pressed: 10,
        clicked: 10
    } )
    buttons.on('error', error => {
        console.log(`ERROR: ${error.message}`);
    })
    buttons.on('pressed', async pin => {
        let PIN = BTNS[pin] 
        console.log('[o-dsk] released', PIN)
    })
    buttons.on('clicked', async pin => {
        let PIN = BTNS[pin] 
        console.log('[o-dsk] clicked', PIN)
    })
    buttons.on('released', async pin => {
        let PIN = BTNS[pin]
        console.log('[o-dsk] pressed', PIN)
    })
    buttons.init().catch(err => {
        console.error('[o-dsk] error initialising buttons:', err.message)
    })
}

gpio()
.then(r => {
  console.log('ready')
})
.catch(error => {
  console.log(`gpio error: ${error.message}`)
})

Can you paste your /boot/config.txt and I can compare to what I have. Although that wouldn't explain why it works after running the python code.

UPDATE 2:

Forgot to ask, are you using pull up resistors in your button circuit? I am using 1k pull up resistors in my tests and the button pulls to ground when pressed.

UPDATE 3:

I looked at the python code again and I'm fairly certain the issue is the pull up resistors.

GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

The rpi-gpio module does not support setting the internal pull up/down setting and relies on the device tree overlay. There are three possible solutions...

  1. Edit the /boot/config.txt and add a line that reads gpio=7-12=ip,pu That will set pins 7 through 12 as inputs with internal pull up.
  2. Add external pull up resistors to each button.
  3. Create a custom device tree overlay file and load it in the /boot/config.txt file.

I tested the gpio=7-12=ip,pu in my /boot/config.txt file and was able to remove the external pull up resistors and your code works.

Side note, if you add your user to the gpio group then I think you can skip the sudo when running the code. i.e. sudo adduser myuser gpio

UPDATE 4:

I think this is the last edit, lol.

Since you are using a Pi Zero I thought I should make a note about the pi-buttons service and the node-pi-buttons module.

The pi-buttons service is written in C and requires far less processing than rpi-gpio-buttons. The node-pi-buttons module connects to the pi-buttons service and generates the same button events in node.js.

I was working on a project with a Pi Zero and rpi-gpio-buttons had poor button response when other applications were running so I came up with the pi-buttons service written in C to get the clean button response even if the Pi Zero was heavily loaded.

@bnielsen1965
Copy link
Owner

@autr Any luck with this issue?

@autr
Copy link
Author

autr commented Jul 7, 2021

Hey, thank you for taking a good look at this and sorry for getting back so late. I’ve been using DietPi which is a stripped back version of RPiOS Buster, but the actual problem was quite silly in the end - I was getting GPIO numbering and pin numbering mixed up elsewhere (must remember to always use this) 🙄

Hopefully not a total waste of time though: while poking around rpi-gpio I saw that the unexport / export / direction get called together in a chain of Promises. There were a few retry funcs dotted around which seem like quick-fixes for the related (but not the eventual cause of) bug with Pi Zero latency and permissions. So I started refactoring with async functions, in mind to have something like this:

let config = {
	delay: 10, // if 3B+, set to 0
	... // etc
}

const wait = async ms => ( new Promise(resolve => setTimeout(resolve, ms) ) )

const setup = config => {
	await unexport( config )
	await wait( config.delay )
	await export( config )
	await wait( config.delay )
	await direction( config )
}

Unrelated to rpi-gpio-buttons but thanks again for taking a good look! Closing the issue now :)

@autr autr closed this as completed Jul 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants