This blog is a Tutorial on how to implement Win 10 IoT Universal Apps that make use of Raspberry PI 2 (RPI2) General Purpose Input Output (GPIO) pins. This covers:

  • A simple interactive app
  • A simulated press button and LED
  • Outputting to a LED
  • Polling an input from a push button
  • Responding to a push button event

The interrupt (event driven) version addresses the InvokeRequired pattern in the XAML context. (Controls can't be updated directly from another thread).

The complete solutions are available (in source) on Codeplex at:
"Windows 10 IoT Samples": https://IoTSampler.Codeplex.com

Preamble

In this tutorial you will create 5 versions of a Universal App that interacts with the RPI2 GPIO pins. The projects should also be able to run on the Minnow board but might need to make some changes to the GPIO Pins used. The RPI2 apps use GPIO5 and GPIO6.

The Universal App Project

You will create a Blank UA project. Note that the template is the same Blank template used for the desktop. It's a Universal app so its supposed to run "universally"

image

Its not the Windows IoT Core – Background Application (IoT). That is another beast!

image

The Target

Being UA the app can be recompiled for various targets, the desktop, a Windows Phone or an IoT device. Note that RT is not an option as the Surface 1 and 2 won't get a Win 10 version.The target is x86/x64  for the desktop (or Minnow) or ARM for the RPI2. You change that when on the Toolbar at the top:

image

Also you select the device from that Toolbar:

image

For the RPI2 you choose Remote Machine and insert the IPAddress and set no authentication:

image

The IPAddress is displayed on the RPI2 screen,, once booted or can be determined over a PowerShell connection,

XAML

To simplify matters, the XAML code is supplied so it can be pasted directly into the MainPage.xaml in the source View.

It is suggested that you configure the XANL editor to show the designer in full window  (no split view) default to Sourece View and turn off "Run project code in XAML Designer. Interaction with XAML pages is easier that way; no more waiting for the Designer to load when you open an XAML page:

Menu—> Tools—>Options—>XAML:

image

The XAML supplied is for the screen size 13.3" Desktop (1280 x 720) 100% scale. That is selectable at top left in Designer Window

image

Application Functionality

The app takes user input as a clicked or pressed event via an XAML button control, an active circular graphic and finally as a hardware push button. When pressed, the RPI2 image visibility toggles and the DateTime gets updated (What is the significance of 29 July 2015 ??).

  • The first version of the app uses an XAML button control.
  • The second version replaces this with a grey circle that changes color to red for a fraction of a second when pressed.
    Note that the Click event that is used only occurs when the mouse press (or touch) is released, and is negated if the mouse or finger moves whilst pressed.
  • In the third version of the app, an external LED flashes in sync with the circle
  • For the fourth version of the app, a hardware push button is polled to see if it is pressed.
  • Finally,for the fifth version, that push button generates an interrupt which causes a Click event which has an event handler in C#..

There is a complication with the fifth app,which if you have done much hardware oriented UI development in .NET (particularly the Compact Framework) you will be aware that the only thread that can update a UI control is the main UI thread. Other threads have to signal it to do their updates.. Read up on InvokeRequired. This issue is resolved using Async code with the fifth app.

You can short circuit the development of one or more of the projects by downloading the complete solution in source from Codeplex:
"Windows 10 IoT Samples": https://IoTSampler.Codeplex.com

Setup

You will need a RPI2, a breadboard, a led and a pushbutton. You will also need a Windows 10 development system with Visual Studio 2015 and the IoT extensions installed.. The RPI2 will need to have the Win10 IoT installed.  Look at getting started at: http://ms-iot.github.io/content/en-US/GetStarted.htm

I use a "Basic Stamp Professional Development Board" for my IO:

clip_image004_thumb1


1. GPIO_Hello_IoT

  1. Create a new Visual C#, Windows, Windows Universal, Blank App.
  2. In the Tools-Options menu configure the XAML editor as previously discussed.
  3. Open MainPage.xaml in Source View (only) and paste the following for the Grid control:
1.MainPage.xaml <Grid>.</Grid>
  1. <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  2.     <TextBlock x:Name="textBlock" Height="67" Margin="375,53,405,0" TextWrapping="Wrap" Text="Hello Internet of Things" VerticalAlignment="Top" FontSize="48" Foreground="#FF0F69EC" d:LayoutOverrides="Width, LeftPosition, RightPosition"/>
  3.     <Image x:Name="image" HorizontalAlignment="Right" Margin="0,175,128,276" Width="391" Source="Assets/RPi2_0.png" Visibility="Collapsed"/>
  4.     <Button x:Name="button" Content="Click Me" Margin="310,224,0,0" VerticalAlignment="Top" Width="147" ClickMode="Press" Click="button_Click" Height="60" FontSize="21.333"/>
  5.     <TextBlock x:Name="date_textblock" Height="67" Margin="253,0,0,177" TextWrapping="Wrap" Text="29 July 2015" VerticalAlignment="Bottom" FontSize="48" HorizontalAlignment="Left" Foreground="#FFEC370F"/>
  6.     <Image x:Name="image1" Height="94" Margin="540,0,0,25" VerticalAlignment="Bottom" HorizontalAlignment="Left" Source="Assets/e101_Logo_OneColor_86_180_v2.jpg"/>
  7. </Grid>
  1. View the page in the XAML Designer and set the screen resolution to 1280 x 720 Change this for your own situation but you may need to make changes with the Designer.
  2. Open the source code page MainPage.xaml.cs (expand MainPage.xaml in Solution Explorer.
  3. Add the button event handler:
    1. Add Button click handler
    1. private void button_Click(object sender, RoutedEventArgs e)
    2. {
    3.     if (this.image.Visibility == Visibility.Collapsed)
    4.         this.image.Visibility = Visibility.Visible;
    5.     else
    6.         this.image.Visibility = Visibility.Collapsed;
    7.  
    8.     this.date_textblock.Text = DateTime.UtcNow.ToString();
    9. }
  4. Build and test run the app on the desktop. Target will be x86 or x64 and Local Machine.
  5. Build the app for the RPI2: Target will be ARM and Remote Machine (as discussed previously
  6. Boot your RPI2 and check that you can deploy the app to it.
  7. Run the app in debug mode.
  8. Insert a breakpoint in the button click event handler as check that it woks Smile.

2. GPIO_Pushy

  1. Replace the Button control in the XAML code with the following Ellipse control:
2. Ellipse Control
  1. <Ellipse x:Name="button" Fill="#FF96969B" HorizontalAlignment="Left" Margin="303,193,0,0" Stroke="Black" Width="147" IsRightTapEnabled="False" IsTapEnabled="True" IsDoubleTapEnabled="False" Height="144" VerticalAlignment="Top" Tapped="button_Click" />
  1. Add the following declarations at the the top of the MainPage class in MainPage.xaml.cs. This adds a timer for the ellipse to blink once when pressed.
    It also defines the pressed and not pressed colors of the ellipse:
    2. Add Declarations
    1. private DispatcherTimer blinkTimer;
    2. private SolidColorBrush redBrush = new SolidColorBrush(Windows.UI.Colors.Red);
    3. private SolidColorBrush grayBrush = new SolidColorBrush(Windows.UI.Colors.LightGray);
  2. Insert the following in the MainPage() constructor below this.InitialzeComponent();
    2. In MainPage()
    1. //Initial color of the button
    2. this.button.Fill = grayBrush;
    3.  
    4. //The single shot blink timer
    5. this.blinkTimer = new DispatcherTimer();
    6. this.blinkTimer.Interval = TimeSpan.FromMilliseconds(200);
    7. this.blinkTimer.Tick += BlinkTimer_Tick;
    8. this.blinkTimer.Stop();
  3. Add the following in button_click() to initiate the "blink"
    2. In button_click
    1. //Chnage "LED" color
    2. this.button.Fill = redBrush;
    3. //Start the one shot blink timer
    4. this.blinkTimer.Start();
  4. Add the following Timer Tick event handler within but at the bottom of MainPage class:
    2. Add BlinkTimer_Tick handler
    1. private void BlinkTimer_Tick(object sender, object e)
    2. {
    3.     //Return "LED" color to unclicked color
    4.     this.button.Fill = grayBrush;
    5.     //Stop the one shot blink timer
    6.     this.blinkTimer.Stop();
    7. }
  5. Test the app on the desktop. No limitations yet.
  6. Test the app with the RRPI2. Smile

3. GPIO_PushyBlink_IO_LED

This version of the app flashes an external LED in sync with the GUI ellipse. For that you will need a suitable LED circuit.
With the development board I am using for my IO, there is an an array of 16 LEDS that each take single bit input and turn on with a logic high. .. Simple.

if you don't have something similar you will need to set up a suitable LED in series with a current limiting resistor. An example in the "Getting Started" documentation is at
http://ms-iot.github.io/content/en-US/win10/samples/Blinky.htm 
This though uses the GPIO 5 pin as a current sink and so a logic low turns the LED on. If you use that circuit then you will need to reverse the polarity *=(change pin highs to low and pin lows to high) in the code in this section.
The default maximum sink current is 8mA although it can be configured to be up to 16 mA. Output source current limitation are similar.
Sink or source current? If you sink then the Vcc for the LED does not need to come from the microprocessor and can even be higher than the 3.3V limit of the RPI2, provided the current limiation is correct.
A warning though,, RPI2 pins should not experience a voltage greater than 3.3V, although there is a 5V supply pin available.

FYI, this link discusses the RPI2 GPIO pin electrical characteristics:
"GPIO Electrical Specifications, Raspberry Pi input and output pin voltage and current capability"

We now need to add the IoT Extensions. This give us access to the GPIO features:

  1. Add a reference (Right click on References in Solution Explorer) and choose Windows Universal-Extensions- Windows IoT Extension SDK:
    image
  2. The app is to built so it will still run on a desktop, even though it doesn't support these extensions.. Fail gracefully, not crash.
    Add a GPIO status TextBlock to the XAML code:
    3. GPIO Status XAML
    1. <TextBlock x:Name="GpioStatus" HorizontalAlignment="Right" Height="29" Margin="0,0,55,142" TextWrapping="Wrap" Text="GpioStatus" VerticalAlignment="Bottom" Width="554" IsDoubleTapEnabled="False" IsHoldingEnabled="False" IsRightTapEnabled="False" IsTapEnabled="False"/>
  3. Add the following GPIO declarations to MainPage class in MainPage.xaml.cs
    3. In MainPage classGPIO Decs
    1. private const int LED_PIN = 5;
    2. private GpioPin pin;
    3. private GpioPinValue pinValue;
    4. private bool bGpioStatus = false;
  4. Add the call to initialize the GPIO in MainPage constructor:
    3. In MainPage() constructor
    1. InitGPIO();
  5. The GPIO initialization involves initializing GPIO, and checking it is started, and setting GPIO Pin 5 to output for the LED.
    Add the following code to Initialize the GPIO at the bottom of the MainPage class:
    3. Add InitGPIO()
    1.         private void InitGPIO()
    2.         {
    3.             var gpio = GpioController.GetDefault();
    4.  
    5.             // Show an error if there is no GPIO controller
    6.             if (gpio == null)
    7.             {
    8.                 pin = null;
    9.                 GpioStatus.Text = "There is no GPIO controller on this device.";
    10.                 bGpioStatus = false;
    11.                 return;
    12.             }
    13.  
    14.             pin = gpio.OpenPin(LED_PIN);
    15.  
    16.             // Show an error if the pin wasn't initialized properly
    17.             if (pin == null)
    18.             {
    19.                 GpioStatus.Text = "There were problems initializing the GPIO pin.";
    20.                 bGpioStatus = false;
    21.                 return;
    22.             }
    23.  
    24.  
    25.             pin.Write(GpioPinValue.High);
    26.             pin.SetDriveMode(GpioPinDriveMode.Output);
    27.  
    28.  
    29.             pinValue = GpioPinValue.Low;
    30.             pin.Write(pinValue);
    31.  
    32.  
    33.             GpioStatus.Text = "GPIO pin initialized correctly.";
    34.             bGpioStatus = true;
    35.         }
    36.     }
    37. }
  6. Add the following to turn the LED on in Button_click. Note that if running on the desktop it will be ignored but the app won't fail:
    3. In button_click()
    1. if (bGpioStatus)
    2. {
    3.     pinValue = GpioPinValue.High;
    4.     pin.Write(pinValue);
    5. }
  7. Add the following to turn the LED off in the Timer Tick handler:
    3. In BlinkTimerTick()
    1. if (bGpioStatus)
    2. {
    3.     pinValue = GpioPinValue.Low;
    4.     pin.Write(pinValue);
    5. }
  8. Test the app on the desktop if wish. It should still function as per version 2 without failure.
  9. Jumper ground, 3,3V and GPIO to your breadboard and set up the LED with a resistor.
    With the software configuration, the LED resistor network is grounded at one end and the high end is connected to GPIO 5.
    Your current limiting resistor should limit to about 8 mA.
    Pin 1: 3,3V
    Pin 39 GND
    Pin 29 GPIO 5 LED
    Pin 31 GPIO 6 PB (Next version of app)

    imageimage
                                                                              RPI2 Pinouts

    You may use the setup as in http://ms-iot.github.io/content/en-US/win10/samples/Blinky.htm but need to reverse the polarity of pinValue in the code.
  10. Test the app on the RPI2. Smile



4. GPIO_PushyBlinky_IO_Poll

In this version of the app, an external push button is polled. When button press is detected the click event is called.

For this you will need a push button circuit that is normally low, but goes high when the push button is pressed This is input to GPIO 6.
Note that the input voltage must be limited to 3.3V not 5V. My IO development board is 5V based so I can either put a suitable resistor in series (say 10K) or use a level shifter such as the Freetronics Logic Level Coverter Moduler.
if you are creating your own circuit on breadboard you might implement it  with the following circuit:

image
The Push Button Circuit,

A suitable resistor would be, say 10K. Although the switch debounce is set in software a small capacitor across the switch can help.

  1. Wire up the Push Button switch with the RPI2, again NO 5V as input.
  2. No changes to the XAML for this version of the app.
  3. Add the following declarations to MainPage class for the PB GPIO and the Poll Timer:
    4. In MainPage class
    1. private const int PB_PIN = 6;
    2. private GpioPin pbPin;     
    3. private GpioPinValue pbPinValue = GpioPinValue.Low;       
    4. private DispatcherTimer pbPolltimer;
  4. Add the following Poll Timer initialization in MainPage():
    4. In MainPage() Constructor
    1. if (bGpioStatus)
    2. {
    3.     //Poll the PB
    4.     this.pbPolltimer = new DispatcherTimer();
    5.     this.pbPolltimer.Interval = TimeSpan.FromMilliseconds(500);
    6.     this.pbPolltimer.Tick += PBTimer_Tick;
    7.     this.pbPolltimer.Start();
    8. }
  5. No changes to button_click() and BlinkTimer_Tick()
  6. Add the following Poll Timer Tick event handler:
    5. Add PBTimer_Tick()
    1. private void PBTimer_Tick(object sender, object e)
    2. {
    3.     GpioPinValue pbPinValueTemp = pbPin.Read();
    4.     if (pbPinValue != pbPinValueTemp)
    5.     {
    6.         //Pulse LED etc if new state is high.
    7.         if (pbPinValueTemp == GpioPinValue.High)
    8.             button_Click(null, null);
    9.         pbPinValue = pbPinValueTemp;
    10.     }
    11. }
  7. Add the following pbPin setup code below that for the LED pin in InitGPIO()
    5. In InitGPIO( )
    1. pbPin = gpio.OpenPin(PB_PIN);
    2.  
    3. // Show an error if the pin wasn't initialized properly
    4. if (pbPin == null)
    5. {
    6.     GpioStatus.Text = "There were problems initializing the GPIO pbPin.";
    7.     bGpioStatus = false;
    8.     return;
    9. }
    10.  
    11. pbPin.SetDriveMode(GpioPinDriveMode.Input);
    12. pbPin.DebounceTimeout = TimeSpan.FromMilliseconds(200);
  8. Test on the desktop if you wish. It should still function as per version 2 of the app without heartache!
  9. Test the app on the RPI2. Smile

5. GPIO_PushyBlink_IO_Intrp

The app is improved in this version of the app by generating an interrupt on GPIO 5 when the button is pressed.
This is captured in the C# code by the pin ValueChanged event handler.

  1. The XAMLcode is unchanged as is the hardware.
  2. Add the following pbPin code to link the pin value changed to an event handler in InitGPIO immediately after the pbPin setup code in that method:
    5. In InitGPIO( )
    1. pbPin.ValueChanged += PBPin_ValueChanged;
  3. Add the event handler PBPin_ValueChanged:
    5. Add PBPin_ValueChanged( )
    1. private async void PBPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
    2. {
    3.     await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    4.     {
    5.         if (args.Edge.CompareTo(GpioPinEdge.RisingEdge) == 0)
    6.         {
    7.             //Pulse LED etc if new state is high.
    8.             {
    9.                 button_Click(null,null);
    10.             }
    11.         }
    12.     });
    13. }
  4. Remove or comment out any reference to the PollTimer:
    - Its declaration
    - Its Init in the constructor
    - Its Tick event handler
  5. Test the app and we are done! Smile

Comment:

In version 4 of the app, the poll timer tick event handler could directly update the UI controls as it runs in the same thread as the UI.
The interrupt occurs as a separate thread and so the app will fail if the PB ValueChanged event handler calls button_click directly. Try it.
This is the exactly the same problem that will occur in .NET CF with hardware buttons where you had to use the
if (control.InvokeRequired) pattern.
The async code above is the equivalent in this context.