metachris / RPIO

RPIO is a GPIO toolbox for the Raspberry Pi.
https://pypi.python.org/pypi/RPIO
GNU Lesser General Public License v3.0
331 stars 147 forks source link

add_interrupt_callback error #31

Open ExperiMentor opened 10 years ago

ExperiMentor commented 10 years ago

I am getting an error with add_interrupt_callback. I am using BOARD pin numbering with P0 set to 11 (ie BCM 18) I can set up and read inputs (and setup and write outputs) OK. And my previous code with RPi.GPIO worked OK (except that I want to control the threading)

But when I try to set an interrupt with: RPIO.add_interrupt_callback(P0, bellpush, edge='rising', threaded_callback=False, debounce_timeout_ms=3000) I get a 'not valid pin on Raspberry Pi' error, as follows:

Traceback (most recent call last): File "alarmv3.py", line 106, in IO.add_interrupt_callback(P0, bellpush, edge='rising', threaded_callback=False, debounce_timeout_ms=3000) File "/usr/local/lib/python2.7/dist-packages/RPIO-0.10.0-py2.7-linux-armv6l.egg/RPIO/init.py", line 217, in add_interrupt_callback threaded_callback, debounce_timeout_ms) File "/usr/local/lib/python2.7/dist-packages/RPIO-0.10.0-py2.7-linux-armv6l.egg/RPIO/_RPIO.py", line 139, in add_interrupt_callback if RPIO.gpio_function(int(gpio_id)) == RPIO.IN: RPIO.Exceptions.InvalidChannelException: The channel sent is invalid on a Raspberry Pi (not a valid pin)

I tried using values for RPi pins and BCM pins instead of the variable P0, but all give the same error. Am I doing something wrong, or is there a bug?

excogitation commented 10 years ago

93 # workaround 94 new_gpio= RPIO.channel_to_gpio(self.encoderGPIO) 95 RPIO.setmode(RPIO.BCM) 96 RPIO.setup(new_gpio, RPIO.IN) 97 RPIO.add_interrupt_callback(new_gpio, callback=self.encoder_callback, edge='both',pull_up_down=RPIO.PUD_OFF, debounce_timeout_ms=2,threaded_callback=True) 98 RPIO.setmode(RPIO.BOARD)

ExperiMentor commented 10 years ago

Thank you for helping. I hope I have understood what you intended and have implemented it correctly, but it is not working for me. I am getting an error that the RPIO module does not have an attribute called 'channel_to_gpio':

Traceback (most recent call last): File "alarmv1b.py", line 125, in new_P0= IO.channel_to_gpio(P0) # doorbell push AttributeError: 'module' object has no attribute 'channel_to_gpio'

I changed my program to use BCM pin numbering throughout to avoid needing to change back and forward, and the relevant parts now read:

import RPi.GPIO as IO IO.setmode(IO.BCM)

P0 = 17 P1 = 18 P2 = 27

new_P0= IO.channel_to_gpio(P0) # doorbell push IO.setup(new_P0, IO.IN) IO.add_interrupt_callback(new_P0, callback=bellpush, edge='rising',pull_up_down=IO.PUD_DOWN, debounce_timeout_ms=3000,threaded_callback=True)

new_P1= IO.channel_to_gpio(P1) # magnetic contact IO.setup(new_P1, IO.IN) IO.add_interrupt_callback(new_P1, callback=magnetic, edge='rising',pull_up_down=IO.PUD_UP, debounce_timeout_ms=3000,threaded_callback=True)

new_P2= IO.channel_to_gpio(P2) # PIR IO.setup(new_P2, IO.IN) IO.add_interrupt_callback(new_P2, callback=PIRdetect, edge='rising',pull_up_down=IO.PUD_DOWN, debounce_timeout_ms=15000,threaded_callback=True)

My overall aim is to have 3 different possible triggers (interrupts), each with a different callback. And if there are multiple interrupts, I want the callbacks to run sequentially.

dozencrows commented 10 years ago

Looking at the source to add_interrupt_callback, I can see that the gpio id passed in is run through channel_to_gpio to handle the mapping for the current mode (BOARD or BCM).

When in BOARD mode, the gpio_id is mapped from pin number to GPIO number. add_interrupt_callback then calls other functions such as gpio_function passing this mapped gpio_id. Those functions also call channel_to_gpio with that id, so the GPIO id mapping is applied twice, leading to incorrect values and the resulting behaviour.

In the best scenario, this will be the 'invalid channel' exception - in the worst scenario, this could map to another GPIO altogether...

I suspect add_interrupt_callback has had insufficient testing in BOARD mode :)

dozencrows commented 10 years ago

I have a fix for the issue in my own fork (https://github.com/dozencrows/RPIO/tree/dc-dev) and have made a pull request for it (https://github.com/metachris/RPIO/pull/42)

ExperiMentor commented 9 years ago

I now have my interrupt threading working, such that interrupt handling code for the first interrupt runs to completion before the second interrupt is handled etc. BUT I have a new problem in that my routines using RPIO detect events (triggers) that are not actually present - even if I disconnect all wires from the trigger pins, events are detected and interrupts called.. Apart from being unable to control threading, my hardware works properly with RPi.GPIO, with no unexpected interrupts occurring. So what is wrong?

My full code using RPIO is below, followed by the equivalent version for RPi.GPIO. What might be wrong?

Non-working RPIO version: ''' import RPIO as RPIO import time

on = 1 off = 0 sounder = off

This is not needed for RPIO # RPIO.setmode(RPIO.BCM) # This version set to use BCM (Broadcom, the processor chip) GPIO$

                 # Therefore this program will work only with Rev 2 Raspberry Pis

RPIO.setwarnings(False) # Turn off warning messages (occur when run program after 1st time)

Set up pin numbering

These are tested and found to be correct

right side, top to bottom

P0 = 17 # BCM 17 P1 = 18 # BCM 18 P2 = 27 # BCM 27 (21 on Revision 1) P3 = 22 # BCM 22 P4 = 23 # BCM 23 P5 = 24 # BCM 24 P6 = 25 # BCM 25 P7 = 4 # BCM 4

left side, top to bottom

CE1 = 7 # BCM 7 CE0 = 8 # BCM 8 SCLK = 11 # BCM 11 MISO = 9 # BCM 9 MOSI = 10 # BCM 10 RXD = 15 # BCM 15 TXD = 14 # BCM 14 SCL = 3 # BCM 3 (would be 1 on Revision 1) SDA = 2 # BCM 2 (would be 0 on revision 1)

Set P0 to P3 as inputs with pull up resistors on P0 and P1

RPIO.setup(P0, RPIO.IN, pull_up_down=RPIO.PUD_UP) # Doorbell push; pressing will ground this pin RPIO.setup(P1, RPIO.IN, pull_up_down=RPIO.PUD_UP) # Magnetic contact; normally grounded; removing magnet lets it go high RPIO.setup(P2, RPIO.IN) # PIR; goes ON with movement detected RPIO.setup(P3, RPIO.IN) # Sound detector; goes OFF when sound detected

set P4 to P7 as outputs to relays etc

RPIO.setup(P4, RPIO.OUT) RPIO.setup(P5, RPIO.OUT) RPIO.setup(P6, RPIO.OUT) RPIO.setup(P7, RPIO.OUT)

Set output P4 LOW (for Piezo sounder)

set all other outputs HIGH ( = relay off)

RPIO.output(P4, off) # Piezo sounder RPIO.output(P5, on) # For relay, set ON as this leaves relay quiescent to save power RPIO.output(P6, on) # Relay RPIO.output(P7, on) # Relay

time.sleep(1) # pause to allow inputs to settle before enabling interrupts

P1 is magnetic contact. Pulled UP. Normally pin is connected to GROUND

When magnet removed, switch opens, pull up causes HIGH

P2 is PIR. Requires power of +5v, but output is 3v (RPi 3v3 compatible)

Output goes ON when movement detected

Trimmers adjust (a) sensitivity and (b) ON duration after detection

P3 Sound Detector. Requires power of 3-20v; we are using 3.3v.

Digital Output goes OFF and LED lights when sound detected

Turn resistor ANTICLOCKWISE for greater sensitivity

Because there are multiple triggers, cannot use "Wait for edge"

Define "Events" for each trigger and generate a "Callback" whenever that event occurs

First define the actions to take for each trigger

Then IO.add_event_detect(pin,IO.RISING,callback=name,bouncetime=1000)

def flash(pin, ontime, offtime, count, first):

first = state to send first. Set it to on or off.

global on
global off
second = 1 - first # second state is opposite to the first one

for i in range (1, count+1): # needs +1 so it loops the correct number of times ?why
    RPIO.output (pin, first)
    time.sleep(ontime)
    RPIO.output(pin, second)
    if i <> count: # this is to avoid a wait at the end
        time.sleep(offtime)

def incident (pin, dummyvar): # something has caused an alarm; needs a 2nd parameter global on global off global sounder

print(' ')
print('Incident detected - now decode what it was. Pin '+str(pin)+'; Value '+str(dummyvar))

if pin == P0: # doorbell
    print('Doorbell pushed - sound piezo and flash light')
    sounder = on # set flag to ignore sound detection
    RPIO.output(P4, on) # piezo on
    flash(P7, 0.5, 0.5, 3, off) # pulse relay 3x 0.5 sec
        RPIO.output(P4, off) # Piezo off
    sounder = off
    print('Ended doorbell actions')

elif pin == P1: # Magnetic contact, Magnet removed
    print('Magnet removed - flash light')
    flash(P7, 1, 0.5, 5, off) # pulse relay
    print('Ended magnet removed actions')

elif pin == P2: # PIR detected motion
    print('Motion detected - flicker light')
    flash(P7, 2, 0.2, 3, off) # flicker relay
    print('Ended Motion detected actions')

elif pin == P3: # Sound detected
    if sounder == off: # only react to sound detection if piezo is not sounding !!
         print ('Sound detected - turn light on whilst playing 4 bleeps')
         RPIO.output(P7,off) # turn light on
         sounder = on # set flag to ignore sound detection
         flash(P4, 1, 1, 4, on) # sound piezo 4x for 1 second
         sounder = off
         RPIO.output(P7,on)  # turn light back off
         print('Ended sound detected actions')

    else: # No actions if piezo is sounding
         print('Ignoring sound detected as Piezo is sounding')

else: # unknown trigger
    print ('Unidentified trigger')

If I change all threaded_callback to True, all the outputs run on top of each other. With False, only 1 runs at a time

So False is what I want

RPIO.add_interrupt_callback(P0, incident, edge='falling', threaded_callback=False, debounce_timeout_ms=3000) # ignore repeat doorbell presses within 3000mSec (3 sec)

RPIO.add_interrupt_callback(P1, incident, edge='rising', threaded_callback=False, debounce_timeout_ms=3000) # magnetic contact. Switch normally grounds P1; when magnet removed, pull-

up causes HIGH

RPIO.add_interrupt_callback(P2, incident, edge='rising', threaded_callback=False, debounce_timeout_ms=15000) # PIR ignore repeats within 15 sec

RPIO.add_interrupt_callback(P3, incident, edge='falling', threaded_callback=False, debounce_timeout_ms=30000) # sound detector ignore repats within 30 sec

try: print('Monitoring started ...') print('Press CTRL-C to exit') RPIO.wait_for_interrupts(threaded=False) # False make the main prog stop here - then the following loop is not needed while True: # just wait forever ... time.sleep(10000)

RPIO.cleanup()

except KeyboardInterrupt: print ('Ending monitoring') RPIO.cleanup() # Cleanup on CTRL-C

RPIO.cleanup() '''

Working (apart from threading) RPi.GPIO version: ''' import RPi.GPIO as IO import time

on = 1 off = 0 sounder = off

IO.setmode(IO.BCM) # This version set to use BCM (Broadcom, the processor chip) GPIO numbers

Therefore this program will work only with Rev 2 Raspberry Pis

IO.setwarnings(False) # Turn off warning messages (occur when run program after 1st time)

Set up pin numbering

These are tested and found to be correct

right side, top to bottom

P0 = 17 # BCM 17 P1 = 18 # BCM 18 P2 = 27 # BCM 27 (21 on Revision 1) P3 = 22 # BCM 22 P4 = 23 # BCM 23 P5 = 24 # BCM 24 P6 = 25 # BCM 25 P7 = 4 # BCM 4

left side, top to bottom

CE1 = 7 # BCM 7 CE0 = 8 # BCM 8 SCLK = 11 # BCM 11 MISO = 9 # BCM 9 MOSI = 10 # BCM 10 RXD = 15 # BCM 15 TXD = 14 # BCM 14 SCL = 3 # BCM 3 (would be 1 on Revision 1) SDA = 2 # BCM 2 (would be 0 on revision 1)

Set P0 to P3 as inputs with pull down resistors.

P1 has PULL UP (magnetic contact)

IO.setup(P0,IO.IN,pull_up_down=IO.PUD_UP) # Doorbell push IO.setup(P1,IO.IN,pull_up_down=IO.PUD_UP) # Magnetic contact IO.setup(P2,IO.IN) # PIR IO.setup(P3,IO.IN) # Sound detector

set P4 to P7 as outputs to relays etc

IO.setup(P4,IO.OUT) IO.setup(P5,IO.OUT) IO.setup(P6,IO.OUT) IO.setup(P7,IO.OUT)

Set output P4 LOW (for Piezo sounder)

set all other outputs HIGH ( = relay off)

IO.output(P4,off) # Piezo sounder IO.output(P5,on) # For relay, set ON as this leaves relay quiescent to save power IO.output(P6,on) # Relay IO.output(P7,on) # Relay

First define the actions to take for each trigger

Then IO.add_event_detect(pin, IO.RISING, callback=name, bouncetime=1000)

def flash(pin, ontime, offtime, count, first):

first = state to send first. Set it to on or off.

global on
global off
second = 1 - first # second state is opposite to the first one

for i in range (1, count+1): # needs +1 so it loops the correct number of times ?why
    IO.output (pin, first)
    time.sleep(ontime)
    IO.output(pin, second)
    if i <> count: # this is to avoid a wait at the end
        time.sleep(offtime)

def incident (pin): # something has caused an alarm global on global off global sounder

print(' ')
print('Incident detected - now decode what it was. Pin '+str(pin))

if pin == P0: # doorbell
    print('Doorbell pushed - sound piezo and flash light')
    IO.output(P4, on) # piezo on
    sounder = on # set flag to ignore sound detection
    flash(P7, 0.5, 0.5, 3, off) # pulse relay 3x 0.5 sec
    IO.output(P4, off) # Piezo off
    sounder = off
    print('Ended doorbell actions')

elif pin == P1: # Magnetic contact, Magnet removed
    print('Magnet removed - flash light')
    flash(P7, 1, 0.5, 5, off) # pulse relay
    print('Ended magnet removed actions')

elif pin == P2: # PIR detected motion
    print('Motion detected - flicker light')
    flash(P7, 2, 0.2, 3, off) # flicker relay
    print('Ended Motion detected actions')

elif pin == P3: # Sound detected
    if sounder == off: # only raect to sound detection if piezo is not sounding !!
         print ('Sound detected - turn light on whilst playing 4 bleeps')
         IO.output(P7,off) # turn light on
         sounder = on # set flag to ignore sound detection
         flash(P4, 1, 1, 4, on) # sound piezo 4x for 1 second
         sounder = off
         IO.output(P7,on)  # turn light back off
         print('Ended sound detected actions')

    else: # No actions if piezo is sounding
         print('Ignoring sound detected as Piezo is sounding')

else: # unknown trigger
    print ('Unidentified trigger')

IO.add_event_detect(P0,IO.RISING, callback=incident, bouncetime=3000) # ignore doorbell presses within 3000mSec (3 sec)

IO.add_event_detect(P1,IO.RISING, callback=incident, bouncetime=3000) # magnetic contact. With temp switch, detects RELEASING button (but no matter)

IO.add_event_detect(P2,IO.RISING, callback=incident, bouncetime=15000) # PIR ignore repeats within 15 sec

IO.add_event_detect(P3,IO.FALLING, callback=incident, bouncetime=30000) # sound detector ignore repats within 30 sec

try: print('Monitoring started ...') print('Press CTRL-C to exit') while True: # just wait forever ... time.sleep(10000)

except KeyboardInterrupt: print ('Ending monitoring') IO.cleanup() # Cleanup on CTRL-C

IO.cleanup() '''