In this example we are going to connect the Raspberry PI with a button, and then make Xojo programs where the first one will read from the button without interrupts and the second will be doing the same using interrupts.
Disclaimer:
We do not take any responsibility for possible errors in the guide or errors that you might do wiring it up. Incorrect wiring can result in damaged sensor or damaged Raspberry PI.
Pieces we use are:
- Raspberry PI 2
- Cobbler and Cobbler cable
- Bread board
- 1 K-ohm resistor
- A button
- Wiring PI Xojo module from Paul Lefebvre (Note we will be making modifications for the interrupts example)
If you are not familiar with working with a breadboard or on how to place a cobbler on the breadboard then click here bellow
If you don’t know resistor color codings to find correct resistors then you can click the link bellow
First thing is understanding the wiring of the button:
My button looked like this:
The button had 4 pins, 2 and 2 are same. At first I was not sure how its internal wiring was, as in which two poles were the same and which were the opposite. So I used digital multimeter to get to know its internal wiring.
Do not worry if you don’t know which pins are what on your button, if you get it wrong then worst thing that can happen is the reading of your application will just be always pressed down.
You will want to protect the GPIO pin from mistakes, like if you were to program it as output instead of input then without protection you likely would damage the Raspberry PI. So we use 1K resistor to protect the pin.
1 KOhm resistor in 4 stripe system = Brown – Black – Red (Read towards the golden stripe)
If you have 5 or 6 stipe system on your resistors then you can 5 and 6 stripe resistors to get help on reading their values if you do not know how.
Now once the resistor is in place then if doing programming error it should not damage the pin.
On the breadboard it looked like this:
So basically red is connected to 3,3 V on the Cobbler, and the other end to the button. From the output of the button we connect the 1K resistor, and from the other end of the button we connect Green which goes from the resistor to the GPIO 6 (which is pin 31) which we choose to use for this example.
The Xojo code without interupts:
An input gpio will float between 0 and 1 if it’s not connected to a voltage. So we will need to take care of that by giving it determined state. We do that by using the GPIO.PullUpDnControl function.
The open event of a window:
Sub Open() Const kPin = 6 GPIO.SetupGPIO GPIO.PinMode(kPin,GPIO.INPUT) GPIO.PullUpDnControl(kPin,GPIO.PUD_DOWN) Timer1.Mode = Timer.ModeMultiple Timer1.Enabled = true End Sub
The timer action event:
Sub Action() Const kPin = 6 for n as Integer = 1 to 10 if GPIO.DigitalRead(kPin) <> 0 then Listbox1.AddRow "Button was clicked" end if App.DoEvents(1) next End Sub
This works…… and completes the example without interrupts……
But this is not really great since no matter how tight your timer or loop runs then you will be able to miss clicks.
So here is how we do it with interrupts:
When using interrupts then we must be really careful to not call anything from within the interrupt since Xojo does not support that. So the idea is we have a safe function that only logs the interrupt then we poll the log. That way event is never lost and timer or whatever method you use to poll does not need to be very intensive.
First thing is we need to patch Paul’s GPIO module, adding the following to the module (the function we are adding likely already exists but is not fully implemented so verify that or just blindly override it) and remember to add the four constants also:
Protected Const EDGE_FALLING = 1 Protected Const EDGE_BOTH = 3 Protected Const EDGE_RISING = 2 Protected Const EDGE_SETUP = 0 Protected Function WIringPiISR(pin as integer,edgeType as integer, p as ptr) As Integer //This function registers a function to received interrupts on the specified pin. // The edgeType parameter is either INT_EDGE_FALLING, INT_EDGE_RISING, // INT_EDGE_BOTH or INT_EDGE_SETUP. If it is INT_EDGE_SETUP then no // initialisation of the pin will happen – it’s assumed that you have already // setup the pin elsewhere (e.g. with the gpio program), but if you specify one // of the other types, then the pin will be exported and initialised as specified. // This is accomplished via a suitable call to the gpio utility program, // so it need to be available. // // The pin number is supplied in the current mode – native wiringPi, BCM_GPIO, // physical or Sys modes. // // This function will work in any mode, and does not need root privileges to work. // // The function will be called when the interrupt triggers. // When it is triggered, it’s cleared in the dispatcher before calling your // function, so if a subsequent interrupt fires before you finish your handler, // then it won’t be missed. (However it can only track one more interrupt, // if more than one interrupt fires while one is being handled then they will be ignored) // // This function is run at a high priority (if the program is run // using sudo, or as root) and executes concurrently with the main program. // It has full access to all the global variables, open file handles and so on. // // See the isr.c example program for more details on how to use this feature. #If TargetARM And TargetLinux Then Soft Declare Function wpISR Lib "libwiringPi.so" Alias "wiringPiISR" (pin As Integer, mode As Integer, p as ptr) as Integer return wpISR(pin, edgeType,p) #Endif End Function
Next we create a module which we will call InteruptModule and this module will contain the following things:
Private DownEventCount As Integer Protected Sub ButtonDownInterupt() // We need some pragmas here to make this safe since this is called from interupt. #Pragma StackOverflowChecking False #Pragma DisableBackgroundTasks // NOTE since we are in interupt then we may not call any methods here it is not safe // however we can count the number of occurances and poll this module with a timer or by other means, and by doing that we can be sure that no clicks were missed. DownEventCount = DownEventCount + 1 End Sub Protected Function GetDetectedClicks() As Integer Dim tmp as Integer = DownEventCount DownEventCount = 0 return tmp End Function
Then the Window open event will look like this:
Sub Open() Const kPin = 6 GPIO.SetupGPIO GPIO.PinMode(kPin,GPIO.INPUT) GPIO.PullUpDnControl(kPin,GPIO.PUD_DOWN) if GPIO.WIringPiISR(kPin,GPIO.EDGE_RISING,Addressof InteruptModule.ButtonDownInterupt) = -1 then MsgBox "Could not register interupt for pin number " + Str(kPin) end if Timer1.Mode = Timer.ModeMultiple Timer1.Enabled = true End Sub
And the timer action event will look like this:
Sub Action() Dim clicks as Integer clicks = InteruptModule.GetDetectedClicks() if clicks > 0 then Listbox1.AddRow "Button was clicked" end if End Sub
No button clicks should ever get missed now.
Thats it for now !
Thanks for the great tutorial,
How would I go about using interrupts if I had 2 or more buttons?
Do I need multiple interrupt managers?
For 2 buttons you would need 2 sets of everything, except one timer would be enough to handle more than one button. By checking them all inside the same timer event.
Thanks!
Do I need too GPIO modules?
*two
I can’t seem to get it with two of everything, whatever I do it counts the pulses for both under the first one. And the other input is silent.
You need to make sure you have two of
GPIO.WIringPiISR(kPin,GPIO.EDGE_RISING,Addressof InteruptModule.ButtonDownInterupt)
On 2 separate pins of course, and Addressing to separate methods, and also of course having two separate counters for them.
Thanks Heaps,
Working now!
Thank you so much for putting for the effort to create these examples. I’ve been learning with Python and Bash, but really wanted to be able to use Xojo and was thrilled when I found your site.
I’m very new to Xojo and Raspberry Pi. So, I chose the easiest project on your site, this button example. It took me over two hours to get both examples to work, and I still don’t know exactly what’s going on.
I’m curious about the results I’m getting. With the interrupts example, sometimes when I press the button, it registers twice. I strongly suspect that my switch suffers from a serious case of bounce, which is why I think that why your code looks for >0 events. I altered the line to show how many clicks were recognized with just one actual press of the button. (Listbox1.AddRow “Button was clicked ” + str(clicks) + ” times.”). I’ve gotten as many as 4 “clicks” out of one press of the button.
At other times though, I get TWO successive, independent lines indicating that the button was pressed only 1 time while pressing the button only once. Again, bounce? But am I just catching the timer between intervals?
I’m wondering if you might consider including a debounce function to weed these out. I’m trying to figure it out, but I’m not making much progress.
Also, as a complete newbie, it would be immensely helpful if you could include source files for comparison. I only just recently figured out that you don’t actually type “Sub” or “Function”. And it too me quite a while to figure out where I was supposed to patch the GPIO module. I figured it out with a lot of trial and error, but it would have been nice to be able to load up the source and see what I was missing.
Again, thank you so much. I look forward to trying my hand at the other more involved projects.
You might be able to prevent WordPress from messing up the code snippets (“<” and the like) by wrapping the code with markdown as shown below:
[code language=”vb”]
your code here
[/code]
This also has the added effect of syntax coloring based on Visual BASIC. If you want to avoid that, you can just set it to “text”.
Hello Scott
Basically the > 0 check is because of the timer, as it is not safe to mix the interrupt in Xojo without isolating it with timer. And because of the timer does not pick it up right away then in theory you could have two clicks before the timer picks up the info about the interrupt.
On why your getting many in one click I am not sure. And it will be a while until I am in range of gear to test so I can give meaningful answer to that. I do not remember having observed such when I tested it last time but I may be wrong of course.
Björn