Support Forum

Share your projects and post your questions

Register   or   Sign In
The Forum

Hello, and help !

The IO Pi Plus is a 32 channel MCP23017 GPIO expander for the Raspberry Pi

28/07/2017

Posted by:
Sean

Sean Avatar

Hi, I'm new here.

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

Sean Avatar

Back again already...

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

andrew Avatar

Hi Sean

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

Sean Avatar

Thanks for the prompt reply, that's a huge help.I shall try first with a background thread to watch for interrupt status going high, calling the active routine under timer control when triggered.Parental duties will keep me away from this for a day or two over the weekend but I'll post back with how I get on.Thanks againSean

30/07/2017

Posted by:
Sean

Sean Avatar

Found a bit of time to tinker and I have found an interesting problem...

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

Sean Avatar

Another small matter is that if you are a cheapskate like me and do noy buy the mounting kit, the bus2 header shorts perfectly on the HDMI shield. Glad I bought a second board........Using the second board and making sure it was well insulated did not help with the above though.

31/07/2017

Posted by:
andrew

andrew Avatar

Sorry, there was a bug with the invert_pin() function in the IO Pi library where it was updating the wrong value. It was causing all of the buttons to appear as true which is why your code wasn't updating the outputs correctly. I have uploaded a new version of the library to GitHub so if you could update the version on your Raspberry Pi hopefully everything will start working properly.

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

Sean Avatar

Hi, thanks once more for the prompt reply... This has helped with the erratic behaviour, but still does not seem to work as expected.It would be simplest to describe if I could upload a short video of the breadboarded design, is this possible? My best attempt otherwise would be :Button 1 just lights the relevant led, as the valve output is already off.Button 2 lights the relevant led, and turns the proper output on too, all looks good.Buttons 1 and 2 repeatedly toggle the correct leds and output.But when I press button 3, output to relay 1 goes off. Same for button 4, etc.Each button pair seems to work individually, for one valve output at a time, but they also turn off any other valve outputs that are "on" at that time.any button press (using pins on bus2) sets all the pins on bus 1 to off.I'm not sure if that all makes sense or not? I'm aware that the code isn't elegant, but it's so simple I can't see where it goes wrong? ( I have found a couple of pin numbering errors, but these do not cause the issue above. The loop runs sufficiently fast that each button press is caught about 15 times.Sean

31/07/2017

Posted by:
andrew

andrew Avatar

Could you try updating the IO Pi library from GitHub again? It seems that when I rewrote the library a couple of weeks ago to make it compliant with the PEP8 python standards I managed to break every function that wrote to or read from the individual pins. I tried to reduce the number of variables in the library by storing all of the values in an array but it seems that Python doesn't let you do bitwise operations on a bytearray so when it tried to update a single pin it set all of the other pins to 0 at the same time. I have gone through the code and fixed the problems so hopefully, it should work with your code now. Sorry for any inconvenience this has caused.

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

Sean Avatar

Hi and thanks once more for the thorough and prompt reply.

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

Sean Avatar

Just a short post to say thanks, the level of support given for a library that's given away free with a £15 product has been nothing short of outstanding.

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

Sign in to post your reply


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.

Home

Shop

Learn

Forum

FAQ

Contact

0 item

Your cart is empty

Please browse our shop to order from the wide range of Raspberry Pi boards and accessories.

Subtotal:£0.00
View Basket Continue to Checkout