Hello, and help !
The IO Pi Plus is a 32 channel MCP23017 GPIO expander for the Raspberry Pi
28/07/2017
Posted by:
Sean
I have looked through the posted topics but cannot find a match for my problem, so here goes, any help is appreciated.
I have everything physically working under python 2.
My desire is to use python to program a fairly simple attraction for a local museum :
There are 10 nice bright buttons (leds inside) and 5 relays to drive that do various things.
I have successfully set up IOPi to use bus1 as the outputs - 10 of them to LEDs, 5 to relays (using a ULN2008 to handle the current)
I have also successfully configured the first 10 pins on bus2 to be inputs, and I can read these individually etc.
The problem is with getting an interrupt to work, the examples just don't seem to provide enough information for me to understand where I'm going wrong.
What I'd like to do is fairly simple (I thought(!))
When idle, flash the buttons randomly and in some pretty patterns, indefinitely, until any button is pressed (interrupt?)
If a button has been pressed, start a simple timer and use the buttons to turn on/off the relays, until the timer expires, at which point, resume the random flashing etc.
I have both bits of python working separately just fine and they are very simple, but watching for "any button press" while the "idle" routine runs and then jumping to the "active" routine has me stumped completely and my attempts to use the interrupt capabilities of IOPi have failed miserably. I'm also not a big python user...
Does anyone have a complete python program that uses an IOPi interrupt that I might be able to learn from? I feel that the example code given requires a level of expertise I don't have to make sense of it.
Thanks in advance.
Sean
28/07/2017
Posted by:
Sean
Well, having now looked at the schematic again I'm guessing that physically hooking up the int pin to one of the IO pins might be part of the issue? Either that or I'm even more confused than I thought - should I be looking to use a thread in the idle routine to watch for a press in the background, rather than an interrupt?
28/07/2017
Posted by:
andrew
The interrupts on the IO Pi are designed so that the IA and IB pins will go high when an interrupt occurs. To read the interrupt pins you can either use a loop to check the status like you would with a port read or you can connect the interrupt pins to the Raspberry Pi GPIO port via a resistor divider and then set the GPIO pin to trigger an interrupt routine when the GPIO pin changes state. Based on what you have described above the easiest solution would probably be to use something similar to our demo_iointerruptsthreading.py demo in the python library. This demo runs a background thread which checks the interrupt status every 0.5 seconds and calls callback_function() if the interrupt is triggered. If you need a faster response time you can reduce the sleep time to a shorter period.
To configure the interrupts for your application you can modify the demo with the following code:
# Set the interrupt polarity to be active high and mirroring enabled, so
# pins 1 to 10 will trigger both INT A and INT B
iobus.set_interrupt_polarity(1)
iobus.mirror_interrupts(1)
# Set the interrupts default value to 0, this means the interrupt will fire when the pins go high
iobus.set_interrupt_defaults(0, 0x00)
iobus.set_interrupt_defaults(1, 0x00)
# Set the interrupt type for ports A and B so an interrupt is
# fired when a pin goes high
iobus.set_interrupt_type(0, 0xFF)
iobus.set_interrupt_type(1, 0xFF)
# Enable interrupts for pins 1 to 10
iobus.set_interrupt_on_port(0, 0xFF)
iobus.set_interrupt_on_port(1, 0x03)
Using the above code the interrupt will be triggered when any of pins 1 to 10 on the bus go high. To find out which pin triggered the interrupt you can use the read_interrupt_capture(port) function which stores the state of the port when the interrupt occurs.
The code in the demo_iointerruptsthreading.py script has to read the interrupt status at regular intervals to see if the interrupt has occurred. When using the interrupts in this way you don't need to connect anything to the IA and IB pins on the IO Pi Plus as everything is handled internally within the IO controller chip.
If you want to make it work without having to read the interrupt status you could connect the interrupt pins to a Raspberry Pi GPIO pin via a voltage divider and then use a GPIO interrupt with a callback function so the function is called when the interrupt pin changes state. As the code above sets the IA and IB pins to be mirrored you only have to connect one of them to the Raspberry Pi GPIO for it to work. Adafruit has a tutorial on how to use GPIO interrupts.
As the IO Pi Plus interrupt outputs are 5V and the Raspberry Pi GPIO is 3.3V you will need to use a voltage divider to drop the 5V to 3.3V. We have a voltage divider calculator on our website Resistor Voltage Divider. A 10K resistor on R1 and 18K for R2 should drop the IO Pi Plus output to around 3.2V which will work.
28/07/2017
Posted by:
Sean
30/07/2017
Posted by:
Sean
everything is set up OK and working - but with a strange bug.
the following is really simple code, but it doesn't work under IOPi, the "earlier" leds (lower numbered pins) don't remember their state and turn off when a button further up the range is pressed...
If it's of any help, using exactly the same code but with calls to the "wiringPi" library works fine!
What is meant to happen :
buttons 1 and 2 (bus2, pins 1&2) light up with the appropriate leds (bus1, pins 1&2) and toggle a relay (bus1, pin15)
repeat for 10 buttons and 5 relays
button 1&2 correctly toggle relay 1 on pin15, but when button 3 (or >) is pressed, pin15 goes back to low. Each "pair" works, but they interfere with each other.
Here's the relevant bit of code, including the setting up etc. The main loop begins at "starttime =" I think there are enough comments to make sense
from time import sleep
from IOPi import IOPi
import time
bus1 = IOPi(0x20)
bus2 = IOPi(0x21)
# set up ins and outs
for pin in range(1,16):
bus1.set_pin_direction(pin,0) # 15 outputs on bus1 , 1-10 are leds, 11-15 relays
for pin in range(1,11):
bus2.set_pin_direction(pin,1) # 10 inputs on bus2
bus2.set_pin_pullup(pin,1) # which are pulled up
bus2.invert_pin(pin,1) # and then inverted so press button to gnd = logic 1
for pin in range (1,11):
bus1.write_pin(pin,1) # turn all leds on
print (pin)
sleep(3)
for pin in range(1,11):
bus1.write_pin(pin,0) # turn all leds off
for pin in range(11,16):
bus1.write_pin(pin,0) # check off all relay outputs
starttime = (time.time())
while ((time.time()-starttime) <100): # counting 100 seconds
if bus2.read_pin(1)==1: # light button 1, open vv1 on 11
print"1"
bus1.write_pin(1,1);
bus1.write_pin(2,0);
bus1.write_pin(11,0)
if bus2.read_pin(2)==1: # light button 2, close vv1 on 11
print"2"
bus1.write_pin(1,0)
bus1.write_pin(2,1)
bus1.write_pin(11,1)
if bus2.read_pin(3): # light button 3 open vv2 on 12
print"3"
bus1.write_pin(4,0)
bus1.write_pin(3,1)
bus1.write_pin(12,0)
if bus2.read_pin(4): # light button 4, close vv2
print"4"
bus1.write_pin(3,0)
bus1.write_pin(4,1)
bus1.write_pin(12,1)
if bus2.read_pin(5):
print"5"
bus1.write_pin(6,0)
bus1.write_pin(5,1)
bus1.write_pin(13,0)
if bus2.read_pin(6):
print"6"
bus1.write_pin(5,0)
bus1.write_pin(6,1)
bus1.write_pin(13,1)
if bus2.read_pin(7):
print"7"
bus1.write_pin(8,0)
bus1.write_pin(7,1)
bus1.write_pin(14,0)
if bus2.read_pin(8):
print"8"
bus1.write_pin(9,0)
bus1.write_pin(8,1)
bus1.write_pin(14,1)
if bus2.read_pin(9):
print"9"
bus1.write_pin(10,0)
bus1.write_pin(9,1)
bus1.write_pin(15,0)
if bus2.read_pin(10):
print"10"
bus1.write_pin(9,0)
bus1.write_pin(10,1)
bus1.write_pin(15,1)
30/07/2017
Posted by:
Sean
31/07/2017
Posted by:
andrew
When we designed the IO Pi Plus there weren't many options on where we could place the connectors due to their size and shape so unfortunately one of them had to sit directly above the HDMI connector. A piece of insulation tape placed on top of the HDMI and audio connectors will reduce the chance of shorts.
31/07/2017
Posted by:
Sean
31/07/2017
Posted by:
andrew
I have made a few changes to your code to reduce the number of I2C calls it has to make to the IO Pi.
from time import sleep
from IOPi import IOPi
import time
bus1 = IOPi(0x20)
bus2 = IOPi(0x21)
# set up ins and outs
# 15 outputs on bus1 , 1-10 are leds, 11-15 relays
bus1.set_port_direction(0, 0x00)
bus1.set_port_direction(1, 0x00)
# 10 inputs on bus2
bus2.set_port_direction(0, 0xFF)
bus2.set_port_direction(1, 0x03)
# which are pulled up
bus2.set_port_pullups(0, 0xFF)
bus2.set_port_pullups(1, 0x03)
# and then inverted so press button to gnd = logic 1
bus2.invert_port(0, 0xFF)
bus2.invert_port(1, 0x03)
# turn all leds on
bus1.write_port(0, 0xFF)
bus1.write_port(1, 0x03)
sleep(3)
# turn all leds and relay outputs off
bus1.write_port(0, 0x00)
bus1.write_port(1, 0x00)
starttime = (time.time())
lastvalue = 0
while (time.time()-starttime) < 100: # counting 100 seconds
if bus2.read_pin(1) and lastvalue != 1: # light button 1, open vv1 on 11
print("1")
bus1.write_pin(1, 1)
bus1.write_pin(2, 0)
bus1.write_pin(11, 0)
lastvalue = 1
if bus2.read_pin(2) and lastvalue != 2: # light button 2, close vv1 on 11
print("2")
bus1.write_pin(1, 0)
bus1.write_pin(2, 1)
bus1.write_pin(11, 1)
lastvalue = 2
if bus2.read_pin(3) and lastvalue != 3: # light button 3 open vv2 on 12
print("3")
bus1.write_pin(4, 0)
bus1.write_pin(3, 1)
bus1.write_pin(12, 0)
lastvalue = 3
if bus2.read_pin(4) and lastvalue != 4: # light button 4, close vv2
print("4")
bus1.write_pin(3, 0)
bus1.write_pin(4, 1)
bus1.write_pin(12, 1)
lastvalue = 4
if bus2.read_pin(5) and lastvalue != 5:
print("5")
bus1.write_pin(6, 0)
bus1.write_pin(5, 1)
bus1.write_pin(13, 0)
lastvalue = 5
if bus2.read_pin(6) and lastvalue != 6:
print("6")
bus1.write_pin(5, 0)
bus1.write_pin(6, 1)
bus1.write_pin(13, 1)
lastvalue = 6
if bus2.read_pin(7) and lastvalue != 7:
print("7")
bus1.write_pin(8, 0)
bus1.write_pin(7, 1)
bus1.write_pin(14, 0)
lastvalue = 7
if bus2.read_pin(8) and lastvalue != 8:
print("8")
bus1.write_pin(9, 0)
bus1.write_pin(8, 1)
bus1.write_pin(14, 1)
lastvalue = 8
if bus2.read_pin(9) and lastvalue != 9:
print("9")
bus1.write_pin(10, 0)
bus1.write_pin(9, 1)
bus1.write_pin(15, 0)
lastvalue = 9
if bus2.read_pin(10) and lastvalue != 10:
print("10")
bus1.write_pin(9, 0)
bus1.write_pin(10, 1)
bus1.write_pin(15, 1)
lastvalue = 10
I changed the first part with all of the for loops so that instead of setting each pin individually it updates the whole port with one command so all 8 pins are updated together. This means that to update all 16 pins Python only has to send two I2C commands to the IO Pi instead of 16.
I also added some extra checking into the read loop so when you press a button it will check to see if that button has already been pressed and only send the write_pin commands if the button is different from the last loop. This stops it from continually writing to the IO Pi and printing to the screen while the button is held down.
If you still have any problems with the code or find any other bugs please let me know.
01/08/2017
Posted by:
Sean
I'm secretly pleased that (a) I'm not going crazy and (b) I've found some bugs and made a contribution to a really useful and simple library - The other library I tried worked fine but uses clumsier language (just my opinion) requires root, which would have been another issue to deal with for an embedded standalone blind box.
I'm not able to test now but will both reupdate my library and try your suggested version tomorrow.
The printing to the screen was added purely in my efforts to debug. I note the use of "and...not" to save on sending multiple keypresses, I'm not sure that the electrons would mind but it'll reduce the traffic on I2C by a factor of about 10(*) times which must be a good thing.
(*) the number of times a single button press is picked up in the loop on a Pi3.
03/08/2017
Posted by:
Sean
I have several routines to add, such at daily flushing of the system etc. but everythign I'm using now seems to be working exactly as expected.
Many thanks
Note: documents in Portable Document Format (PDF) require Adobe Acrobat Reader 5.0 or higher to view.
Download Adobe Acrobat Reader or other PDF reading software for your computer or mobile device.