For those who have watched the "Big Bang Theory", you would probably remember that those physicists build an Internet controlled lamp. The signal send from their laptop traveled around the world and come back to their house to light up the lamp. Now, with Windows 10 IoT Core and Microsoft Azure, we could also achieve the same thing on a Raspberry Pi 3.

First, I strongly recommend you to read these 2 Azure documents first:

Get started with Azure IoT Hub for .NET

https://azure.microsoft.com/en-us/documentation/articles/iot-hub-csharp-csharp-c2d/

1. Create an Azure IoT Hub and Register the Device


Please refer to the document Get started with Azure IoT Hub for .NET to create an IoT Hub. The process is same.

For registering the device, I found an easy way. There is a tool by Microsoft here https://github.com/Azure/azure-iot-sdks/tree/master/tools/DeviceExplorer 

After download and install. Copy your IoT Hub connection string and paste it into the main tab. Then hit "Update" button.

You can find the connection string in your Azure portal. The key parameters needed are:

HostnameSharedAccessKeyNameSharedAccessKey

The connection string format is:

HostName=YOURIOTHUBNAME.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=YOURKEY

Then go to "Management" tab, click "Create", enter your device name for your Raspberry Pi 3 and select "Auto Generate Keys"

After the registration, you would see the result listed in the grid view.

2. Physical Connection on the Pi


Required Items: 1 LED, 2 Jump Wires.

Connect the long leg of LED to DC3.3V, short leg to GPIO 04. Please be careful if your LED is not standard 3.3V, you will need to put a resistor to it.

After the connection, it looks like this:

3. Coding


We need 2 projects. One is for running on the Raspberry Pi, as a signal receiver, seeking messages coming from Azure and switch the LED. Another is for running on your PC, as a controller to send message to Azure.

The project structure looks like:

Raspberry Pi Project

Use Visual Studio 2015 to create a new UWP project, for example "AzureRemoteLight", add reference to "Windows IoT Extensions for the UWP"

Install this package from NuGet:

"Microsoft.Azure.Devices.Client": "1.0.5"

BTW, recommend to update Json.NET to the latest version, which is now:

"Newtonsoft.Json": "8.0.3"

I also use MVVMLight and Edi.UWP.Helpers, these are not necessary, just make the code looks cooler. My project.json:

{
  "dependencies": {
    "Edi.UWP.Helpers": "1.0.11",
    "Microsoft.Azure.Devices.Client": "1.0.5",
    "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0",
    "MvvmLight": "5.2.0",
    "Newtonsoft.Json": "8.0.3"
  },
  "frameworks": {
    "uap10.0": {}
  },
  "runtimes": {
    "win10-arm": {},
    "win10-arm-aot": {},
    "win10-x86": {},
    "win10-x86-aot": {},
    "win10-x64": {},
    "win10-x64-aot": {}
  }
}

UI

As for switching the light, the UI won't be necessary. However, getting a UI will make your project looks cooler, so I make a simple UI like this:

There are 2 points in the UI: Azure IoT Hub Connection will show the connection status to Azure. CloudToDeviceLog is showing the operation details.

 

<Page
    x:Class="AzureRemoteLight.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:AzureRemoteLight"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    DataContext="{Binding Source={StaticResource Locator}, Path=Main}"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Padding="12">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        
        <Image Source="Assets/cloud-hero.png" Grid.Row="0" Height="80" HorizontalAlignment="Right" VerticalAlignment="Top" />
        
        <StackPanel Grid.Row="0">
            <TextBlock Text="Windows 10 IoT + Microsoft Azure" Style="{StaticResource SubtitleTextBlockStyle}" />
            <TextBlock Text="Remote Light Control" Style="{StaticResource SubheaderTextBlockStyle}" />
        </StackPanel>

        <Grid Grid.Row="1" Margin="0,20,0,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            
            <Image Grid.Row="0" Grid.RowSpan="2" Source="Assets/Windows_Insiders_Flag.png" HorizontalAlignment="Right" Height="300" VerticalAlignment="Bottom" />
            
            <StackPanel Orientation="Horizontal" Grid.Row="0">
                <TextBlock Text="Azure IoT Hub Connection: " />
                <TextBlock Text="{Binding IsAzureConnected}" />
            </StackPanel>
            
            <Border Grid.Row="1" BorderBrush="#CCC" BorderThickness="1" Margin="0,10,0,0" Padding="10">
                <TextBlock TextWrapping="Wrap" Text="{Binding CloudToDeviceLog}" Foreground="#CCC" FontFamily="Consolas" />
            </Border>
        </Grid>
    </Grid>
</Page>

ViewModel Code

First, we need to define the GPIO Controller and the PIN for the LED

#region GPIO Settings

public GpioController GpioController { get; }

public GpioPin LedPin { get; }

#endregion

And also, Azure IoT Hub Connection Properties

#region Azure IoT Hub Settings

public DeviceClient DeviceClient { get; }

public string IotHubUri { get; } = "YOURIOTHUBNAME.azure-devices.net";

public string DeviceKey { get; } = "YOUR DEVICE KEY";

public string DeviceId => "YOUR DEVICE NAME";

#endregion

You can find these properties under the "Management" tab in Device Explorer.

Finally, define 2 properties for showing on the UI.

#region Display Fields

private bool _isAzureConnected;
private string _cloudToDeviceLog;

public bool IsAzureConnected
{
    get { return _isAzureConnected; }
    set { _isAzureConnected = value; RaisePropertyChanged(); }
}

public string CloudToDeviceLog
{
    get { return _cloudToDeviceLog; }
    set { _cloudToDeviceLog = value; RaisePropertyChanged(); }
}

#endregion

In the constructor, initiate the GPIO controller and LED PIN.

public MainViewModel()
{
    DeviceClient = DeviceClient.Create(IotHubUri, new DeviceAuthenticationWithRegistrySymmetricKey(DeviceId, DeviceKey));

    GpioController = GpioController.GetDefault();
    if (null != GpioController)
    {
        LedPin = GpioController.OpenPin(4);
        LedPin.SetDriveMode(GpioPinDriveMode.Output);
    }
}

Then Create a method to send a message to Azure to ensure the connection is success.

public async Task SendDeviceToCloudMessagesAsync()
{
    try
    {
        var telemetryDataPoint = new
        {
            deviceId = DeviceId,
            message = "Hello"
        };
        var messageString = JsonConvert.SerializeObject(telemetryDataPoint);
        var message = new Message(Encoding.ASCII.GetBytes(messageString));

        await DeviceClient.SendEventAsync(message);
        Debug.WriteLine("{0} > Sending message: {1}", DateTime.Now, messageString);

        IsAzureConnected = true;
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.Message);
    }
}

Don't forget to invoke the method in the MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    private MainViewModel _vm;

    public MainPage()
    {
        this.InitializeComponent();

        _vm = this.DataContext as MainViewModel;

        Loaded += async (sender, args) =>
        {
            // send device connected message
            await _vm.SendDeviceToCloudMessagesAsync();
        };
    }

Running

IMPORTANT: PLEASE ENSURE YOUR SYSTEM TIME IS UP TO DATE ON THE RASPBERRY PI, OTHERWISE THE SAS TOKEN WILL EXPIRE.

Before running the application, switch to "Data" tab in the Device Explorer. Click "Monitor" to receive message sent from your Raspberry Pi to Azure.

Deploy your project by ARM/Remote Machine to your Pi. If you got a successful run, you will get the message in the Device Explorer:

Then we can continue coding.

In the ViewModel, create another method to receive message from Azure, and switch the LED on or off according to message content.

public async Task ReceiveCloudToDeviceMessageAsync()
{
    CloudToDeviceLog = "Receiving events...";
    Debug.WriteLine("\nReceiving cloud to device messages from service");

    while (true)
    {
        Message receivedMessage = await DeviceClient.ReceiveAsync();
        if (receivedMessage == null) continue;

        var msg = Encoding.ASCII.GetString(receivedMessage.GetBytes());
        CloudToDeviceLog += "\nReceived message: " + msg;

        if (msg == "on")
        {
            LedPin.Write(GpioPinValue.Low);
        }

        if (msg == "off")
        {
            LedPin.Write(GpioPinValue.High);
        }

        await DeviceClient.CompleteAsync(receivedMessage);
    }
}

The message I used is type of string, "on" means turn on the LED, "off" means turn off the LED.

And also, you need to execute this method in MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    private MainViewModel _vm;

    public MainPage()
    {
        this.InitializeComponent();

        _vm = this.DataContext as MainViewModel;

        Loaded += async (sender, args) =>
        {
            // send device connected message
            await _vm.SendDeviceToCloudMessagesAsync();

            // receive remote light control events
            await _vm.ReceiveCloudToDeviceMessageAsync();
        };
    }
}

Now, run again your application. You should see the result from your Raspberry Pi Screen.

So far, the work on Raspberry Pi is complete.

The Controller Project

Create a WPF project,  e.g. LightController, it can not be UWP, and add NuGet package "Microsoft.Azure.Devices". This package is not supporting UWP, that's why I have to create a WPF application instead.

Add 2 buttons in MainWindow for turn on and turn off the LED.

 

<Window x:Class="LightController.MainWindow"
        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"
        xmlns:local="clr-namespace:LightController"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button x:Name="BtnTurnOn" Content="Turn Light On" HorizontalAlignment="Center" Click="BtnTurnOn_OnClick" />
            <Button x:Name="BtnTurnOff" Content="Turn Light Off" Margin="0,10,0,0" Click="BtnTurnOff_OnClick" />
        </StackPanel>
    </Grid>
</Window>

Logic Code:

public partial class MainWindow : Window
{
    static ServiceClient serviceClient;
    static string connectionString = "YOU IOT HUB CONNECTION STRING";

    public MainWindow()
    {
        InitializeComponent();

        serviceClient = ServiceClient.CreateFromConnectionString(connectionString);
    }

    private async Task TurnLight(bool isOn)
    {
        await SendCloudToDeviceMessageAsync(isOn);
    }

    private static async Task SendCloudToDeviceMessageAsync(bool isOn)
    {
        var commandMessage = new Message(Encoding.ASCII.GetBytes(isOn ? "on" : "off"));
        await serviceClient.SendAsync("YOUR DEVICE NAME", commandMessage);
    }

    private async void BtnTurnOn_OnClick(object sender, RoutedEventArgs e)
    {
        await TurnLight(true);
    }

    private async void BtnTurnOff_OnClick(object sender, RoutedEventArgs e)
    {
        await TurnLight(false);
    }
}

The message send here is also type of string, consistent with the program on Raspberry Pi.

The IoT Hub Connection string used here are exactly the same you used in the main page of Device Explorer.

4. Trying Out


Because we got the controller and the client project, we need to start them together. In Visual Studio, right click on your solution, choose "property" and select multiple start up projects like this:

After starting the project. You will be able to control the light on your PC using the WPF application. And your Raspberry Pi will show the detail operation log on the screen.

https://github.com/EdiWang/Windows-IoT-AzureRemoteLight