The IoT Journey : Interacting with the Physical World


In the first two posts of this series (See Post One and Post Two) I detailed the process of configuring a Raspberry Pi 3 to enable development and testing of Internet of Things (IoT) projects. The first post covered the basics of the Raspberry Pi, and the second detailed the basics of the GPIO interface and demonstrated how to turn an LED on and off with code. In this post, we will extend the project that we created earlier and add interaction with the physical world through the use of a mechanical button press.

When interacting with sensors and devices, one of the more important things to understand is how interrupts work, and how you code interrupt service routines.

Understanding Hardware Interrupts

This is a topic that you could spend many hours on and not understand all of the nuances, yet it’s very important to understand the basics of how interrupts work if you’re going to develop IoT applications that utilize sensors or other electronic components that work with the Raspberry Pi.

If you have an electronic circuit that contains a pushbutton switch, and you want the activity of the switch to cause action in your program, you need to write code that will read the state of the switch and then react based on the state of the switch. There are many different ways that you can approach this problem, but probably the two most common ways are polling and interrupt-driven. In a polling solution, you simply write code that reads the state of the switch, and call it on a regular basis. This works well if you can anticipate the pressing of the switch (i.e, the flow of your application is such that the user would press the switch at known points in the code), or if you use an asynchronous method to create a timer that reads the switch at defined intervals (of course this could be a problem if the switch is pressed and released outside of the defined interval, it would go unnoticed).

An interrupt is a signal to the processor to tell it to stop whatever it’s currently doing and do something else. In the switch example above, if we connected the switch to a GPIO and then defined that pin as an interrupt, whenever the pin detected a change in state (if the button were pressed, for example), it would activate the interrupt, and our code would simply have to handle that interrupt.

Because the Raspberry Pi GPIO interface is a digital interface, we have to understand the concept of Signal Edge when working with interrupts. Digital signals are either on or off and are represented by a square wave:

image

The normal HIGH (or current is not flowing) signal on the Raspberry Pi GPIO is 3.3v. This is why, in the previous post, we set the pin to LOW in order to light the LED. If you look at the above chart, you will notice that there are transitions between zero and 3.3v represented at the edge of the square. For example, if you look at 10 on the x axis, you’ll see the transition from 3.3v to 0v. This is known as a Falling Edge. Conversely, if you look at 15, you’ll notice the transition from 0v to 3.3v. This is known as the Rising Edge. If we construct our circuit such that the button press allows current to flow, then we will want to ensure the interrupt is generated only on the Falling Edge, otherwise we would trigger two interrupts for every button press. The simplified circuit diagram would look like this:

image

When the button is pressed, the GPIO pin is “Pulled Low”, meaning that current will flow between the pin and ground. One issue that might arise in this configuration is the fact that the mechanical switch isn’t precise and can possibly generate false signals as the contacts travel close during the push or release of the switch. Sometimes this “bouncing” effect can generate multiple signal transitions and must be considered when coding routines that deal with switch presses. This concept is known as de-bouncing. (As this series of posts is meant to be instructional and not production-quality, we will not de-bounce our switch inputs)

With this basic understanding out of the way, let’s move on to creating a simple circuit and writing some code to react to the button press.

Step One – Construct the Simple Circuit

In the previous post, we created a simple circuit that included an LED and a resistor that connected to the GPIO interface and allowed us to control the LED in code. The circuit diagram looks like:

image

and the resulting breadboard configuration looks like:

This circuit allowed current to flow through the LED when the GPIO pin was set (or “pulled”) LOW. We wrote code that simply set the GPIO interface to LOW whenever a key was pressed on the keyboard.

For this example, we will add the switch to the breadboard and connect one side to ground, and the other side to GPIO 6 (physical pin 31). The circuit will look like this:

image

And the resulting breadboard configuration will be (ignore the additional LEDs and Resistors at the top, we will discuss those later):

20160424_173812624_iOS

(note that even though the same breadboard is used, the switch circuit is not connected to the LED in any way)

Once the circuit is constructed, we’re ready to move on to coding the solution.

Step Two – Develop the Code

For this exercise, we will create a new solution in Visual Studio that uses interrupts to detect when the button is pressed. We will then toggle the LED every time the button is pressed.  In Visual Studio, create a new C# Windows Console application named ISR.

image

Once the solution is created, add a reference to WiringPi.dll (Because you have previously referenced this .dll, it should be present in the Reference Manager dialog and you should just have to select it).

image

Once the reference is complete, add a using statement for the WiringPi library:

image

The basic flow the application we are about to create is:

  1. Initialize the GPIO Library and tell it how we will reference the GPIO pins
  2. Instruct the GPIO that we will be writing to the pin with the LED connected
  3. Turn the LED On
  4. Initialize the WiringPi Interrupt Service Routine and tell it which signal edge will trigger an interrupt
  5. Wait for a button press, and if one is detected toggle the state of the LED
  6. Check if a key is pressed, and if so, exit the program

The completed code, with comments is below:

using System;
using System.Threading.Tasks;
using WiringPi;

namespace ISR
{
class Program
{
const int redLedPin = 29; // GPIO 5, physical pin 29
const int buttonPin = 31; // GPIO 6, physical pin 31
static void buttonPress()
{
if (GPIO.digitalRead(redLedPin) == (int)GPIO.GPIOpinvalue.Low) // Check to see if LED is on
GPIO.digitalWrite(redLedPin, (int)GPIO.GPIOpinvalue.High); // If so, turn it off
else
GPIO.digitalWrite(redLedPin, (int)GPIO.GPIOpinvalue.Low); // Otherwise turn it on

        }
static void Main(string[] args)
{
Console.WriteLine("Initializing GPIO Interface");
if (Init.WiringPiSetupPhys() < 0) //Initialize the GPIO Interface with physical pin numbering
{
throw new Exception("Unable to Initialize GPIO Interface"); // Any value less than 0 represents a failure
}
GPIO.pinMode(redLedPin, (int)GPIO.GPIOpinmode.Output); // Tell the Pi we will be writing to the GPIO
GPIO.digitalWrite(redLedPin, (int)GPIO.GPIOpinvalue.Low); // Turn LED On
Console.WriteLine("Initializing Interrupt Service Routine");
// We will fire the interrupt on the falling edge of the button press. Note that we are not "debouncing" the switch, so we will likely
// see some extra button presses during operation
if (GPIO.PiThreadInterrupts.wiringPiISR(buttonPin, (int)GPIO.PiThreadInterrupts.InterruptLevels.INT_EDGE_FALLING, buttonPress) < 0) // Initialize the Interrupt and set the callback to our method above
{
throw new Exception("Unable to Initialize ISR");
}
Console.WriteLine("Press the button to toggle LED or press any key (and then press button) to exit");
Console.ReadKey(); // Wait for a key to be pressed
GPIO.digitalWrite(redLedPin, (int)GPIO.GPIOpinvalue.High);
}
}
}

 

Once you have the application coded, build it and prepare to deploy it to the Raspberry Pi.

Deploying and Executing the ISR Application

Use a file transfer program (I use FileZilla) to copy the ISR.exe and WiringPi.dll files to a folder on the Raspberry Pi. (I used ~/DevOps/ISR)

image

 

Once the files are copied, you can execute the application with the command sudo mono ISR.exe:

image

The LED will light, and when you press the button it should toggle state. You will likely very quickly see the result of our not de-bouncing the switch, as some presses will likely result in multiple toggles of the LED. There are many patterns for de-bouncing in software, but the most reliable solutions are hardware-based.  You will also note that when you press a key, the application does not exit until you press the button as well. This is a byproduct of the ISR Routine needing to execute in order to capture the keypress.

Congrats! You’ve now developed an application that bridges the gap between code and the physical world. This is an extremely important concept for developing any IoT Application.

In the next post in this series, we will discuss the concept of digital signaling and will develop an application that interacts with an RGB LED that requires Pulse Width Modulation in order to work properly.

Comments (0)

Skip to main content