GPIO interrupts and python


#1

I’m trying to control the motion of a robot with buttons working through GPIO pins. I have three buttons that detect various events, but although each one interrupts the continuous motion when programmed as single buttons when I have all three in one piece of code only the first one works.
Actually the second and third ones do work if the first one trips first, but if they trip first there is no interrupt detected.
If anyone has any ideas (or even just good questions) I will post the code (hang the embarrassment).
It seems as if the buttons are nested in terms of priority instead of all being the same level of priority.


#2

You can post the code in three backticks, like so:

```
Your code here
```

And it’ll be formatted and indented so we can have a gander.

We’ve all suffered coding embarrassment at one point or another, don’t worry about it! :D


#3

OK, you asked to see my code, so here goes:

#!/usr/bin/env python
# my development program, to create the robot version 1

# ________________________________________
# create the environment

import explorerhat
import time
import sys
import RPi.GPIO as GPIO
import colorsys
import flotilla
from button import *
import pygame
from pygame.locals import *

# _________________________________________________
# various initialisations
global stopit
stopit=1
global engine
engine=1
global whichway
whichway="right"                     # this is used to select the correct turn
GPIO.setmode(GPIO.BCM)
pygame.mixer.init()
buttonleft = Button(8)
buttonright = Button(18)
buttoncentre = Button(9)

# ___________________________________________
# and the sub-routines
def startingbell():
    pygame.mixer.music.load("Starting Bell.mp3")
    pygame.mixer.music.set_volume(1.0)
    pygame.mixer.music.play()
    time.sleep(2)

def boatengine():
    pygame.mixer.music.load("long steamboat.mp3")
    pygame.mixer.music.set_volume(1.0)
    pygame.mixer.music.play()

def reversingnoise():
    pygame.mixer.music.load("reversing beeps.mp3")
    pygame.mixer.music.set_volume(1.0)
    pygame.mixer.music.play()


def driveforwards():
    boatengine()
    explorerhat.motor.one.forwards(10)
    explorerhat.motor.two.forwards(10)

    
def forwardsabit():
    explorerhat.motor.one.forwards(8)
    explorerhat.motor.two.forwards(8)
    time.sleep(2)

def drivebackwards():
    reversingnoise()
    time.sleep(2)
    explorerhat.motor.one.backwards(8)
    explorerhat.motor.two.backwards(8)


def turnright():
    explorerhat.motor.one.forwards(8)
    explorerhat.motor.two.backwards(8)
    time.sleep(2)

def turnleft():
    explorerhat.motor.one.backwards(8)
    explorerhat.motor.two.forwards(8)
    time.sleep(2)
    
def stopengines():
    explorerhat.motor.one.forwards(0)
    explorerhat.motor.two.forwards(0)
    pygame.mixer.music.stop()

def swerve(whichway):
    stopengines()
    time.sleep(2)
    drivebackwards()
    time.sleep(2)
    if whichway=="right":
        turnright()
    if whichway=="left":
        turnleft()
    time.sleep(2)
    forwardsabit()
    time.sleep(2)
    if whichway=="right":      
        turnleft()
    if whichway=="left":
        turnright()
    time.sleep(2)


def closedown():
    print("final stop")
    stopengines()
    stopit=2
    rainbow.set_all (0,0,0).update()
    client.stop()
    sys.exit(1)

def offswitch(ch,event):
    closedown()

    

# ____________________________________
# this is the start of the execution

client = flotilla.Client(
        requires={
            'one':flotilla.Slider,
            'eight':flotilla.Rainbow
              }
        )
rainbow=client.first(flotilla.Rainbow)
rainbow.set_all(0,255,0).update()
time.sleep(1)
startingbell()


try:
    
    while stopit==1:
        slider = client.first(flotilla.Slider)      
        slider_val=int(slider.data[0])
        print slider_val
        
        while slider_val >400 :
            print ("first value in loop")
            print slider_val
            driveforwards()
            print ("forwards")

            if buttonleft.is_pressed():
                whichway="right"
                print ("left button")
                swerve(whichway)
             
            if buttonright.is_pressed():
                whichway="left"
                print ("right button")
                swerve(whichway)

            if buttoncentre.is_pressed():
                swerve(whichway)
                print ("centre button")

            else:
                print ("loop")
            slider = client.first(flotilla.Slider)     
            slider_val=int(slider.data[0])
            if slider_val<400:

                print ("new value")
                print slider_val
        else:
# this is the final stop            
            closedown()
#    client.stop()

except KeyboardInterrupt:
    print("keyboard stop")
    closedown()

I am VERY old school, so my conventions in terms of data names are not current python conventions, but I think you can read it.
Thanks in advance for any advice.


#4

Is Button() part of GPIOZero?

Presumably it’s finding its way into your code via from button import *. Is there a button.py alongside the Python file that this code is in?

It looks like button presses will only be handled if slider_val > 400 too?


#5

Button is a library that comes with the three buttons I bought from Simon Monk.
Do you want to see the button.py?
Slider_val is taken from the flotilla slider, and is a switch that I can use once the robot is disconnected from the monitor. When explorerhat has finished its internal checks I get five green lights on the flotilla rainbow, and I then slide the flotilla slider forward to start the robot program proper.
The idea is that I can slide the slider back to read 0 and stop the robot without CTRL-C, but that ‘interrupt’* only works if it is tripped during a left-button-press event.
All the buttons work individually, and if I hash out the first one and change the ELIF to IF on the second one then that one works, and becomes the ‘master’ interrupt.
*I know that the slider is not a real GPIO interrupt, but I don’t know what else to call it.


#6

Aha! The slider acting as a brake is a good idea!

Looks like this is the button.py you’re using? https://github.com/simonmonk/squid/blob/master/button.py

Normally we call this method of GPIO access/monitoring “polling”, although I’m not sure how you would describe a singular event in that case so “interrupt” is as good a word as any. (RPi.GPIOs interrupts are just really low level polling anyway!)

I can’t yet see why it results in the behaviour you describe, but using RPi.GPIOs interrupts is simple enough that, perhaps, it would be better to try and refactor some of the code to use those.

You could do something like:

import time
import RPi.GPIO as GPIO

BTN_LEFT = 8
BTN_RIGHT = 18
BTN_CENTRE = 9

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(BTN_LEFT, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(BTN_RIGHT, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(BTN_CENTRE, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def go_left():
    global current_direction
    current_direction = "left"

def go_right():
    global current_direction
    current_direction = "right"

def go_forwards():
    global current_direction
    current_direction = "forwards"

GPIO.add_event_detect(BTN_LEFT, GPIO.FALLING, callback=go_left, bouncetime=200)
GPIO.add_event_detect(BTN_RIGHT, GPIO.FALLING, callback=go_right, bouncetime=200)
GPIO.add_event_detect(BTN_CENTRE, GPIO.FALLING, callback=go_forwards, bouncetime=200)

while running:
    print(current_direction)

    if current_direction == "left":
        # Make robot go left

    if current_direction == "right":
        # Make robot go right

    if current_direction == "forwards":
        # Make robot go forwards

    time.sleep(0.01)

This style decouples the button handling from the code that governs the behaviour of the robot. It makes it difficult to do express like if BUTTON_LEFT and BUTTON_RIGHT then do this though.

add_event_detect is RPi.GPIOs method of handling “interrupts”, but I’m pretty sure it just implements a polling loop in C code since the Pi doesn’t have hardware interrupts on all pins.


#7

Thank you Phil. I will try this out tomorrow, and let you know how it works!
Thanks again