Homemade Azure Remote Light

 

1. Create an Azure IoT Hub and register the device

See the documentation Get started with Azure IoT Hub for .NET to create an IoT hub. The process is the same.

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

After downloading and installing. Copy and paste the IoT Hub connection string into the main tab. Then click the "Update" button.

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

hostname, SharedAccessKeyName, SharedAccessKey

The connection string format is:

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

Then go to the "Manage" tab, click "Create", enter your Raspberry Pi 3 device name and select "Auto Generate Key"

After registering, you will be able to see the results listed in the grid view.

2. Physical connections on the Pi
Items needed: 1 LED, 2 jumper wires.

Connect the long pin of the LED to DC3.3V and the short pin to GPIO 04. If your LED is not standard 3.3V, be aware that you will need to put a resistor across it.

After connecting it looks like this:

3. Coding
We need 2 items. One is to run on a Raspberry Pi as a signal receiver, looking for messages from Azure and toggles LEDs. The other runs on your PC as a controller for sending messages to Azure.

The project structure is as follows:

Raspberry Pi Project

Create a new UWP project with Visual Studio 2015, say "AzureRemoteLight", add a reference to "Windows IoT Extensions for the UWP"

Install this package from NuGet:

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

By the way, it is recommended to update Json.NET to the latest version, which is now:

"Newtonsoft.Json": "8.0.3"

I also used MVVMLight and Edi.UWP.Helpers, but these are not required.

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":{} User Interface }
  }

As for toggling lights, UI isn't necessary though. However, getting a UI makes your project look cooler, so I made a simple UI like this:

There are 2 points in the UI:  

  • Azure IoT Hub Connection will show the connection status with Azure.
  • CloudToDeviceLog shows operation details.

view model 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

Additionally, the 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 Manage tab in Device Explorer.

Finally, define 2 properties for display 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, start 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 make sure the connection is successful.

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 call the 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();
        };
    }
}
运行

Important: Make sure your system time is up to date on the Raspberry Pi, otherwise the SAS token will expire.

Switch to the Data tab in Device Explorer before running the app. Click "Monitor" to receive messages sent from your Raspberry Pi to Azure.

Deploy your project to your Pi via ARM/Remote Machine. If you run successfully, you will get the message in Device Explorer:

Then we can continue coding.

In the ViewModel, create another method that receives a message from Azure and turns the LED on or off based on the 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 messages I use are of type string, "on" means turn on the LED and "off" means turn the LED off.

And, 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 your application again. You should see results on the Raspberry Pi screen.

At this point, the work of the Raspberry Pi is complete.

Controller project

Create a WPF project, such as LightController, which cannot be UWP, and add the NuGet package "Microsoft.Azure.Devices". This package does not support UWP, so we have to create a WPF application.

Add 2 buttons to the MainWindow to turn the LEDs on and off.

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("你的设备名称", 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 sent here is also a string type, which is consistent with the program on the Raspberry Pi.

The IoT Hub connection string used here is exactly the same that you use in the home page of Device Explorer.

4. Test
Since we got the controller and client projects, we need to start them together. In Visual Studio, right-click on your solution, select Properties and select multiple startup projects as follows:

After the project starts. You will be able to control the lights on your PC using a WPF application. Your Raspberry Pi will display a detailed operation log on the screen.

If you have any thoughts, comments or questions about this project, please leave a comment below.