piface / pifacedigitalio

The PiFace Digital input/output module.
GNU General Public License v3.0
110 stars 48 forks source link

Interrupts with Multiple PiFace boards #14

Open dpbutler opened 10 years ago

dpbutler commented 10 years ago

Hi all,

I am having a problem with interrupts when using multiple PiFace boards. I have a Model A Pi connected to multiple PiFace boards via a PiFace Rack.

If I have only 1 PiFace connected I can receive interrupts fine. Once I connect a second PiFace with different hardware address my interrupts are no longer received.

I have tried receiving interrupts using pifacecommon and pifacedigitalio methods.

pifacedigitalio test code is as follows

import pifacedigitalio as p
import pifacedigitalio.version as pfdioV
import pifacecommon.version as pfcV
import time

quit = False

def print_flag(event):
    print("You pressed button ", event.pin_num)

def stop_listening(event):
    global quit
    quit = True

p.init()

print("pifacedigitalio version = ", pfdioV.__version__)
print("pifacecommon version = ", pfcV.__version__)

pfd = p.PiFaceDigital(0)

l = p.InputEventListener(pfd)

l.register(0, p.IODIR_OFF, print_flag)
l.register(1, p.IODIR_OFF, print_flag)
l.register(2, p.IODIR_OFF, print_flag)
l.register(3, p.IODIR_OFF, stop_listening)

l.activate()

while not quit:
    time.sleep(1)

l.deactivate()

pifacecommon test code is

import pifacecommon.core
import pifacecommon.interrupts
import pifacecommon.mcp23s17
import time

quit = False

def print_flag(event):
    print("You pressed button ", event.pin_num)

def stop_listening(event):
    global quit
    quit = True

port = pifacecommon.mcp23s17.GPIOB
chip = pifacecommon.mcp23s17.MCP23S17()

listener = pifacecommon.interrupts.PortEventListener(chip=chip, port=port)

listener.register(0, pifacecommon.interrupts.IODIR_FALLING_EDGE, print_flag)
listener.register(1, pifacecommon.interrupts.IODIR_FALLING_EDGE, print_flag)
listener.register(2, pifacecommon.interrupts.IODIR_FALLING_EDGE, print_flag)
listener.register(3, pifacecommon.interrupts.IODIR_FALLING_EDGE, stop_listening)

listener.activate()

while not quit:
    time.sleep(1)

listener.deactivate()

Not sure if I am missing something or if there is an issue. I have tried on multiple PIs using multiple PiFaces.

tompreston commented 10 years ago

I have seen this issue but I'm a bit busy at the moment. I'll get around to testing this later in the week.

Thanks for submitting a detailed report though, that really helps!

dpbutler commented 10 years ago

Hi Tom,

Is there any thing else I can do to help with this issue

Kind Regards,

Dave

tompreston commented 10 years ago

In your first code example are you receiving events from PIFace Digital 0? Have you tried creating two event listeners, one for each PiFace Digital?

dpbutler commented 10 years ago

Hi Tom,

Tried the following code tonight with 4 PiFaces and got some really strange results. If i push a button I see no interrupt until I press another button on a different PiFace. When I push the second button the event is from the first button press. If I press two buttons on the same PiFace nothing happens but when I press a button on a different PiFace the event is from the first button press on the other PiFace.

I know this is very confusing and I wish I could explain better. Hopefully the following example is easier to understand:

***Push button 2 on PiFace 0
***Push button 0 on PiFace 2
You pressed button 2
***Push button 1 on PiFace 1
You pressed button 0
***Push button 1 on PiFace 0
You pressed button 1
***Push button 2 on PiFace 0
***Push button 3 on PiFace 1
You pressed button 1
***Push button 2 on PiFace 0
Deactivating listeners...
Listeners Deactivated

Seems the Pi is not seeing the interrupt pin until another PiFace raises it. Do you agree?

Code is...

import pifacedigitalio as p
import pifacedigitalio.version as pfdioV
import pifacecommon.version as pfcV
import time

quit = False

def print_flag(event):
    print("You pressed button ", event.pin_num)

def stop_listening(event):
    global quit
    quit = True

p.init(0)
p.init(1)
p.init(2)
p.init(3)

print("pifacedigitalio version = ", pfdioV.__version__)
print("pifacecommon version = ", pfcV.__version__)

print("Creating l0")
pfd0 = p.PiFaceDigital(0)
l0 = p.InputEventListener(pfd0)
l0.register(0, p.IODIR_OFF, print_flag)
l0.register(1, p.IODIR_OFF, print_flag)
l0.register(2, p.IODIR_OFF, print_flag)
l0.register(3, p.IODIR_OFF, stop_listening)

print("Creating l1")
pfd1 = p.PiFaceDigital(1)
l1 = p.InputEventListener(pfd1)
l1.register(0, p.IODIR_OFF, print_flag)
l1.register(1, p.IODIR_OFF, print_flag)
l1.register(2, p.IODIR_OFF, print_flag)
l1.register(3, p.IODIR_OFF, stop_listening)

print("Creating l2")
pfd2 = p.PiFaceDigital(2)
l2 = p.InputEventListener(pfd2)
l2.register(0, p.IODIR_OFF, print_flag)
l2.register(1, p.IODIR_OFF, print_flag)
l2.register(2, p.IODIR_OFF, print_flag)
l2.register(3, p.IODIR_OFF, stop_listening)

print("Creating l3")
pfd3 = p.PiFaceDigital(3)
l3 = p.InputEventListener(pfd3)
l3.register(0, p.IODIR_OFF, print_flag)
l3.register(1, p.IODIR_OFF, print_flag)
l3.register(2, p.IODIR_OFF, print_flag)
l3.register(3, p.IODIR_OFF, stop_listening)

print("Activating l0...")
l0.activate()
print("Activating l1...")
l1.activate()
print("Activating l2...")
l2.activate()
print("Activating l3...")
l3.activate()

print("Waiting for buttons...")
while not quit:
    time.sleep(1)

print("Deactivating listeners...")
l0.deactivate()
l1.deactivate()
l2.deactivate()
l3.deactivate()
print("Listeners Deactivated")

Really appreciate you taking the time to help on this.

tompreston commented 10 years ago

Your explanation makes sense. This seems to be a problem with the interrupt queue in pifacecommon. I recall having a similar issue when I was testing but it disappeared somewhere. I think it has something to do with this function and it is on my todo list this week. In the mean time, if you have the time, you could litter that function with print statements and try to figure out what's going on.

dpbutler commented 10 years ago

Hi Tom,

I added the following print statements to the function you suggested:

    while True:
        # wait here until input
        print("Waiting for port events on ", chip)
        try:
            print("Polling for event")
            events = epoll.poll()
            print("Got events")
        except KeyboardInterrupt as e:
            print("In KeyboardInterrupt")
            if return_after_kbdint:
                return
            else:
                raise e
        except IOError as e:
            print("In IOError")
            # ignore "Interrupted system call" error.
            # I don't really like this solution. Ignoring problems is bad!
            if e.errno != errno.EINTR:
                raise

        # find out where the interrupt came from and put it on the event queue
        if port == pifacecommon.mcp23s17.GPIOA:
            print("Interrupt from GPIOA")
            interrupt_flag = chip.intfa.value
        else:
            print("Interrupt not from GPIOA assume GPIOB")
            interrupt_flag = chip.intfb.value

        if interrupt_flag == 0:
            print("interrupt_flag was 0")
            continue  # The interrupt has not been flagged on this board
        else:
            if port == pifacecommon.mcp23s17.GPIOA:
                print("Interrupt value captured for GPIOA")
                interrupt_capture = chip.intcapa.value
            else:
                print("Interrupt value captured for GPIOB")
                interrupt_capture = chip.intcapb.value
            print("Adding interrupt to queue")
            event_queue.add_event(InterruptEvent(
                interrupt_flag, interrupt_capture, chip, time.time()))
            print("Interrupt_flag: ", interrupt_flag)
            print("interrupt_capture: ", interrupt_capture)
            print("Interrupt added to queue")

Prior to pressing any buttons I see this output

pifacedigitalio version =  3.0.2
pifacecommon version =  4.0.0
Creating l0
Creating l1
Creating l2
Creating l3
Activating l0...
Activating l1...
Activating l2...
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xe29650>
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
Activating l3...
interrupt_flag was 0
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xe29650>
Polling for event
Waiting for buttons...
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xebbb50>
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
interrupt_flag was 0
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xebbb50>
Polling for event
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xed2910>
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xefb670>
interrupt_flag was 0
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xed2910>
Polling for event
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
interrupt_flag was 0
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xefb670>
Polling for event

When I press the first button on a PiFace no output is printed

After one button press on one PiFace followed by another button on a different PiFace the following output is printed

Got events
Interrupt not from GPIOA assume GPIOB
Got events
Interrupt not from GPIOA assume GPIOB
Got events
Interrupt not from GPIOA assume GPIOB
Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Interrupt value captured for GPIOB
interrupt_flag was 0
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xebbb50>
Polling for event
interrupt_flag was 0
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xe29650>
Polling for event
Adding interrupt to queue
Adding interrupt to queue
Interrupt_flag:  1
interrupt_capture:  254
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xed2910>
Polling for event
Interrupt_flag:  1
interrupt_capture:  254
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xefb670>
Polling for event

After pressing two buttons on a PiFace followed by one on a different PiFace the output is

Got events
Interrupt not from GPIOA assume GPIOB
Got events
Interrupt not from GPIOA assume GPIOB
Got events
Interrupt not from GPIOA assume GPIOB
Got events
Interrupt not from GPIOA assume GPIOB
interrupt_flag was 0
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xefb670>
Polling for event
Interrupt value captured for GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  1
interrupt_capture:  254
Adding interrupt to queue
interrupt_flag was 0
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xed2910>
Polling for event
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xebbb50>
Polling for event
Interrupt_flag:  2
interrupt_capture:  255
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xe29650>
Polling for event
You pressed button  1

It seems this function is operating correctly I think. Is this many iterations common if multiple PiFaces are used? I will try tomorrow with one PiFace only and with Multiple PiFaces but only one listener, if you think this will help?

Is the problem likely to be in the epoll.poll()? This does not seem to be returning for the first button press.

dpbutler commented 10 years ago

Hi Tom,

Done some further testing.

With four PiFaces connected and one listener for Interrupts on first PiFace only I see the following and regardless of what buttons I press I get no further output.

pi@pitest ~ $ python3 inttest.py 
pifacedigitalio version =  3.0.2
pifacecommon version =  4.0.0
Creating l0
Activating l0...
Waiting for buttons...
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0x17b7cb0>
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  255
interrupt_capture:  255
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0x17b7cb0>
Polling for event

If I have only 1 PiFace connected I see the following before pressing any buttons:

pi@pitest ~ $ python3 inttest.py 
pifacedigitalio version =  3.0.2
pifacecommon version =  4.0.0
Creating l0
Activating l0...
Waiting for buttons...
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0x2120cb0>
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  255
interrupt_capture:  255
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0x2120cb0>
Polling for event

After I press button 1 I get this:

Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  1
interrupt_capture:  254
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xb00cb0>
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  1
interrupt_capture:  255
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xb00cb0>
Polling for event
You pressed button  0

After button 2:

Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  2
interrupt_capture:  253
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xb00cb0>
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  2
interrupt_capture:  255
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xb00cb0>
Polling for event
You pressed button  1

After button 3

Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  4
interrupt_capture:  251
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xb00cb0>
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  4
interrupt_capture:  255
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xb00cb0>
Polling for event
You pressed button  2

After button 4:

Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  8
interrupt_capture:  247
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xb00cb0>
Polling for event
Got events
Interrupt not from GPIOA assume GPIOB
Interrupt value captured for GPIOB
Adding interrupt to queue
Interrupt_flag:  8
interrupt_capture:  255
Interrupt added to queue
Waiting for port events on  <pifacedigitalio.core.PiFaceDigital object at 0xb00cb0>
Polling for event
Deactivating listeners...
Listeners Deactivated
pi@pitest ~ $ 

Is there any reason why I get two events for each button press? Does it matter? I'm guessing my earlier prognosis that the issue is in epoll.poll() is still correct?

dan-cristian commented 5 years ago

I have a similar issue with two boards. Only getting interrupts on the first board, and randomly on 2nd. Any progress with this issue? How should be the boards connected, is PiRack the only feasible option?