Developing for Windows 10 IoT Core on Raspberry Pi 2

15 May 2015


I bought myself a Raspberry Pi 2 several months ago, and have been waiting since then for the promised arrival of Windows 10 for IoT. As part of all the huge Build 2015 excitment last week, Microsoft made a preview version of Windows 10 IoT Core for Raspberry Pi 2 available.

This post provides details of how I installed and configured Windows 10 on the Pi. We then look at how to write, deploy and remote-debug a simple Windows 10 Universal app on the Raspberry Pi 2.

Installing Windows 10 IoT Core on the Raspberry Pi 2

Microsoft provide some excellent instructions for getting started. You can find them here.

First things first. In order to gain access to the Windows 10 IoT Core Preview build you need to be a member of the Microsoft Connect program. If you're not already a member you can sign-up for the IoT program here.

To get started you will need:

  1. A PC with a micro SD card reader running Windows 10 Preview build 10069 or higher
    You need this not only for developing apps for the Pi, but also to copy the Windows IoT Core files onto the Pi's micro SD card. I tried to do this initially using a Windows 8.1 machine and the copy failed, so you definately do need a Windows 10 machine
  2. A Raspberry Pi 2 with an 8GB micro SD card
  3. A micro USB power supply for the Pi
    Microsoft state that you should use a "3.5V micro USB power supply with at least 1.0A current" (> 2A is preferable)
  4. A network connection for the Pi
    This is so that you can connect to it and deploy and debug from your development PC.
    Using an Ethernet cable is the easiest option
  5. Optional: Monitor and USB Keyboard and Mouse

To download and install Windows 10 IoT Core Preview:

  1. Download the package Windows 10 IoT Core Insider Preview Image for Raspberry Pi 2 from the Connect site to a PC with an SD card reader that's running Windows 10
  2. Unzip the package and locate an installer named WindowsDeveloperProgramForIoT.msi. Run the .msi to install Windows IoT Core Watcher, a helpful utility that enables us to monitor the status of connected Pi's, as well as providing shortcuts to common functions

  3. Put the SD card into the reader
  4. Open an admin command prompt on the Windows 10 PC and navigate to the location of the file flash.ffu, which was also contained in the Windows 10 IoT Core Preview package zip
  5. Use the following commands to identify the disk number of the SD card:
    diskpart
    list disk
    exit
  6. Copy the disk image to the SD card with the following command, where X is the disk number of the card:
    dism.exe /Apply-Image /ImageFile:flash.ffu /ApplyDrive:\\.\PhysicalDriveX /SkipPlatformCheck
    
  7. Once the image has finishing copying, remove the SD card from the drive (use the Safely Remove Hardware task bar icon to ensure all files are correctly flushed)
  8. Insert the SD into the Pi2 and apply power to the board. The Pi2 will now boot (and reboot) several times. This takes quite a while the first time. Eventually, you should see a screen similar to the following:

Configuring the Raspberry Pi 2

Before starting development on the Pi you should change the default admin password. Using the host/development PC, open an admin Powershell window and issue the following commands:

net start WinRM
This starts the remote management service on the host PC
Get-Item WSMan:\localhost\Client\TrustedHosts
This checks to see if the Pi is a trusted host.

If the name or IP address of the Pi is not on the trusted hosts list, add it using:

Set-Item WSMan:\localhost\Client\TrustedHosts -Value ip
Where ip is the Pi's IP address/machine name. You can get this value from the landing page displayed by default on the Pi2.

Before connecting to the Pi, the following command will work around a known issue that can cause a stack overflow:

remove-module psreadline -force

Now, connect to the Pi with:

Enter-PsSession -ComputerName ip -Credential ip\Administrator
Where ip is the Pi's IP address/machine name. You'll be prompted for the default admin password (p@ssw0rd) and then the connection will take 30-seconds or more to initiate.

If all goes well, you should see something like the folllowing:

The first thing to do is change the default admin password:

net user Administrator new-pwd
schtasks /Delete /TN Microsoft\Windows\IoT\Startup /F

You can now explore the Pi's fle system...

Headed or Headless?!

Interestingly, Windows 10 IoT Core can be booted in two different modes: headed (with a standard Universal App UI stack, allowing the developer to create "normal" user-interactive apps for the Pi2), or headless (no UI).

Headed mode is the default, and displays the landing page that includes info on the device name, IP address, and so on. If the device is booted into headless mode the default landing page is not displayed. In this case, the easiest way to find its IP address is by using the Windows IoT Core Watcher utility mentioned earlier.

To switch to headless mode, use a remote Powershell management session (as described above):

setbootoption.exe headless
shutdown /r /t 0
To switch back to the default:
setbootoption.exe headed
shutdown /r /t 0

IoT Core Web Management

As well as providing the details on all connected Pi's, the IoT Core Watcher gives easy access to a very useful Web Management tool. Right-click your Pi in the IoT Core Watcher and select Web Browser Here.


Developing for the Raspberry Pi 2 with Visual Studio 2015

Developing apps for the Raspberry Pi 2 running Windows 10 IoT Core is simple: just develop Windows 10 Universal Apps (UAP)!!

To get started you'll need Visual Studio 2015 RC or higher. Then, simply select the Windows Universal > Blank App template:

First, select ARM from the Solution Configurations dropdown:

Then select Remote Machine from the Debug Target list:

When you select Remote Machine from the list of available targets you'll be presented with the Remote Connections dialog. Fill out the connection details for the Pi (set the Authentication Mode to None) and click Select:

Now make sure the Build platform is set to ARM in the project's properties (right-click the project name in Solution Explorer and select properties):

Setup debugging (which also allows you to deploy to the Pi with a single click) in the project's Debug properties. Set the Target device to Remote Machine, then enter the Pi's IP address in Remote machine. Uncheck Use authentication:

To prove the connections and configuration are OK, you can now deploy and run the app as-is to the Pi: just press F5!

If you get deployment errors, it's possible that the Visual Studio remote debugger may have stopped (apparently it timesout after a period of innactivity). You can re-start it using a Powershell remote management session and the following command:

schtasks /run /tn StartMsvsmon

Or, simply reboot the Pi, as the remote debugger is configured to auto-start when the device is booted.

At this point you can set breakpoints and debug your app in Visual Studio as you would normally.

As a first attempt at a simple Hello World app, I added the following code to MainPage.xaml.cs. This just displays the Pi's machine name and IP address, along with the current system time:

using System;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Windows.Networking.Connectivity;
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;

namespace HelloIoTWorldApp
{
    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        private string _machineName;
        private string _ip;
        private string _time;
        private Timer _timer;

        public string MachineName
        {
            get { return _machineName; }
            set { _machineName = value; OnPropertyChanged(); }
        }

        public string Time
        {
            get { return _time; }
            set { _time = value; OnPropertyChanged(); }
        }

        public string Ip
        {
            get { return _ip; }
            set { _ip = value; OnPropertyChanged(); }
        }

        public MainPage()
        {
            InitializeComponent();

            MachineName = GetHostName();
            Ip = GetHostIp();

            _timer = new Timer(TimerTick, null, 0, 1000);
        }

        private static string GetHostName()
        {
            var hostName = NetworkInformation.GetHostNames().FirstOrDefault();
            return hostName == null ? string.Empty : hostName.DisplayName;
        }

        private static string GetHostIp()
        {
            var icp = NetworkInformation.GetInternetConnectionProfile();
            if(icp == null || icp.NetworkAdapter == null) return string.Empty;

            var hostInfo = NetworkInformation.GetHostNames().SingleOrDefault(
                h => h.IPInformation != null &&
                     h.IPInformation.NetworkAdapter != null &&
                     h.IPInformation.NetworkAdapter.NetworkAdapterId == 
                     icp.NetworkAdapter.NetworkAdapterId);

            return hostInfo.DisplayName;
        }

        private void TimerTick(object state)
        {
            UpdateTime();
        }

        private async void UpdateTime()
        {
            var tod = DateTime.Now.TimeOfDay;
            await Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal, 
                () => Time = $"{tod.Hours}:{tod.Minutes}:{tod.Seconds}");
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

And here's the XAML:

<Page
    x:Class="HelloIoTWorldApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    mc:Ignorable="d">

    <Grid Margin="10" Background="#FF0078D7">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="95"/>
            <RowDefinition Height="55"/>
            <RowDefinition Height="55"/>
            <RowDefinition Height="55"/>
            <RowDefinition />
        </Grid.RowDefinitions>

        <Image 
            Grid.Column="0" 
            Grid.Row="0" 
            Grid.ColumnSpan="2" 
            Source="../../../Assets/pi.png" />

        <TextBlock 
            Grid.Column="0" 
            Grid.Row="1" 
            HorizontalAlignment="Right" 
            Text="Machine name" 
            FontSize="24" 
            Foreground="Black" 
            Margin="5"/>
        
        <TextBlock 
            Grid.Column="1" 
            Grid.Row="1" 
            HorizontalAlignment="Left"
            Text="{Binding MachineName}" 
            FontSize="24" 
            Foreground="Yellow" 
            Margin="5"/>

        <TextBlock 
            Grid.Column="0" 
            Grid.Row="2" 
            HorizontalAlignment="Right" 
            Text="IP address" 
            FontSize="24" 
            Foreground="Black" 
            Margin="5"/>
        
        <TextBlock 
            Grid.Column="1" 
            Grid.Row="2" 
            HorizontalAlignment="Left" 
            Text="{Binding Ip}" 
            FontSize="24" 
            Foreground="Yellow" 
            Margin="5"/>

        <TextBlock 
            Grid.Column="0" 
            Grid.Row="3" 
            HorizontalAlignment="Right" 
            Text="System time" 
            FontSize="24" 
            Foreground="Black" 
            Margin="5"/>
        
        <TextBlock 
            Grid.Column="1" 
            Grid.Row="3" 
            HorizontalAlignment="Left" 
            Text="{Binding Time}" 
            FontSize="24" 
            Foreground="Yellow" 
            Margin="5"/>
    </Grid>
</Page>

Deploying the app we get the following on the Pi:

Auto-starting your App

In a real-world scenario, you'd want your app to automatically run when the Pi booted. This is easily setup as follows (note that a bug in the IoT Core Preview prevents this working with Debug builds - you must use apps compiled as Release):

As previously described, start a Powershell remote management session with the Pi and use the following command to list the full names of the installed app:

iotstartup list

Now look in your app's Package.appxmanifest for the Identity attribute. Match the identity name to one of the apps installed on the Pi:

You can set your app as the start-up app using:

iotstartup add headed app-name

In our specific case:

iotstartup add headed f49dd9c5-0abd-402b-8579-ddba3ef961de_bb0zrez7bb02g!App

Now simply reboot the Pi and your app should auto-run when the boot is complete.

To change back to the default start-up app:

iotstartup add headed DefaultApp