Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
Hand tools and fabrication machines | ||||||
|
This a supplemental tutorial to our 003 tutorial. In this tutorial we're going to finish developing a UWP Application to collect the sensor data from FezHat, convert the data into JSON, and upload the payload into the Azure IoT Hub via HTTP1.
In our 003 tutorial we've been setting up two devices at the same time, each of which has its own set of sensors and its own code base. In this tutorial we'll be going over the code base used for the Raspberry Pi 2B with a FezHat connected to it.
Setting UpIf you're continuing from our 003 tutorial then you already have a fully functional blank UWP app loaded into your Visual Studio. If you don't, now's the time to set that up. Now, to make use of the FezHat and its sensors, we need to install a NuGet package from GHI Electronics into this application. Just search for FezHat in the NuGet manager window and install. Also, you're going to want to install the Microsoft.Azure.Devices.Client and Newtonsoft.Json NuGet packages into your applications if you haven't already. After all the packages are installed, your NuGet package manager's installed section should look like this:
After installing these NuGet packages you'll be able to import the GHIElectronics.UWP.Shields namespace. Through the classes contained in this namespace you can access the sensors and read their value. Very convenient indeed.
Implementing the programming logicAt this point your UWP application is ready to implement the programming logic provided by us in our fully functional sample code. In the code section of this tutorial we have attached code files for you to reference and for you to embed into your app.
At the end of this tutorial your Visual Studio's Solution Explorer should look like this:
We also decided to give it a basic user interface, which looks like this:
We hope you've looked into the code samples and embedded them into your own applications. Make good use of them! Now it's time for us to go back to our 003 tutorial and finish uploading and monitoring the messages to Azure IoT Hub.
See you back on track in tutorial 003.
IoTHub.cs
C#////The MIT License(MIT)
////Copyright(c) 2016 BardaanA
////Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
////The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
////THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Client;
using Newtonsoft.Json;
//using System.Diagnostics;
namespace RasPi2BApp
{
class IoTHub
{
// Standard IoT Hub device client and message
private DeviceClient device;
private Microsoft.Azure.Devices.Client.Message payload;
string payloadIntoString;
private bool initialized;
public bool IsInitialized { get { return initialized; } }
public void Initialize(string iotHubHostName,string deviceKey,string deviceId)
{
initialized = false;
try
{
// Simply WOW! Good job microsoft Azure/IoT team... Thanks!
device = DeviceClient.Create(iotHubHostName,new DeviceAuthenticationWithRegistrySymmetricKey(deviceId,deviceKey),Microsoft.Azure.Devices.Client.TransportType.Http1);
if(device != null)
{
initialized = true;
}
}
catch(Exception ex)
{
//Debug.WriteLine("IoT Hub initialization failed! " + ex.Message);
throw new SensorException(ex.Message);
}
}
// So this is where all the magic happens, the payload is "Spirited Away"!
public async Task Upload(SensorData gpsData)
{
try
{
// JSON! JSON! JSON!
payloadIntoString = JsonConvert.SerializeObject(gpsData);
payload = new Microsoft.Azure.Devices.Client.Message(Encoding.UTF8.GetBytes(payloadIntoString));
await device.SendEventAsync(payload);
}
catch(Exception ex)
{
//Debug.WriteLine("Upload failed! " + ex.Message); // Seriously!
throw new SensorException(ex.Message);
}
}
public async Task Close()
{
await device.CloseAsync();
device.Dispose();
}
}
}
MainPage.xaml.cs
C#////The MIT License(MIT)
////Copyright(c) 2016 BardaanA
////Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
////The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
////THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace RasPi2BApp
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
///
// In this UWP Application we'll be collecting some sensor data from the FezHat every 30 seconds, convert that data into JSON
// and upload it to Azure IoT Hub for further processing and Storage.
public sealed partial class MainPage : Page
{
// This is the IoT Hub's Hostname it can be retrieved from the Azure Portal
private string iotHubHostname = "...";
// This is the name of the device that we created, when we registered our device to the IoT Hub
// Registry
private string deviceName = "...";
// This is the device Key that was generated when we registerd our device into the registry
// This can also be retrieved from the Azure Portal
private string deviceKey = "...";
// Containment and Delegation once again
// An IoTHub object
private IoTHub iotHub;
// Our Main timer, this timer is going to dictate the program flow and sends device data in 30 second intervals
private DispatcherTimer mainTimer;
// SensorData object reference
private SensorData sensorData;
// Sensors object reference
private Sensors sensors;
public MainPage()
{
// This is where all the UWP elements are initialized... Kind of mandatory!
this.InitializeComponent();
// Setup all the necessary resources and initialize them
SetupSensors();
SetupIoTHub();
SetupTimer();
}
// This is where all the action is. This method is called every 30 seconds
// by the DispatcherTimer
private async void OnMainTimerTick(object sender,object e)
{
try
{
// This is where the sensor data is collected in to the SensorData type
if(sensors.IsInitialized)
{
textBlockSensorStatusStatus.Text = "Initialized";
textBoxPayloadDeliveryStatus.Text = "...";
sensorData = sensors.Read();
textBoxSensorValue.Text = sensorData.ToString();
if(iotHub.IsInitialized)
{
textBlockIoTHubStatusStatus.Text = "Initialized";
// Now this is where our data, in this case it is SensorData is passed on to be packaged in to JSON payload and finally
// uploaded to the IoT Hub. In a way our data is being, "Spirited away!"
await iotHub.Upload(sensorData);
textBoxPayloadDeliveryStatus.Text = "Successfully delivered!";
}
else
{
textBlockIoTHubStatusStatus.Text = "Not Initialized";
textBoxPayloadDeliveryStatus.Text = "...";
}
}
else
{
textBlockSensorStatusStatus.Text = "Not Initialized";
}
}
catch(Exception ex) // Please no!!!!
{
throw new SensorException(ex.Message);
}
}
// This is where we setup our timer
private void SetupTimer()
{
mainTimer = new DispatcherTimer();
mainTimer.Interval = TimeSpan.FromMilliseconds(30000); // 30 second intervals
mainTimer.Tick += OnMainTimerTick;
Task.Delay(60000); // Give some time for the Sensors to warm up
mainTimer.Start(); // Ready set go...
}
// This is where the IoT Hub object is initialized. The security access strings are
// passed in here
private void SetupIoTHub()
{
iotHub = new IoTHub();
iotHub.Initialize(iotHubHostname,deviceKey,deviceName);
}
// This is where we setup out FezHat and initialize out SensorData type
private async void SetupSensors()
{
sensorData = new SensorData();
sensors = new Sensors();
await sensors.Initialize();
}
private async void buttonClose_Click(object sender,RoutedEventArgs e)
{
mainTimer.Stop();
await iotHub.Close();
}
}
}
<Page
x:Class="RasPi2BApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RasPi2BApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="#FF81CCFF">
<TextBlock x:Name="textBlockSensorStatus" HorizontalAlignment="Left" Margin="38,47,0,0" TextWrapping="Wrap" Text="Sensor Status: " VerticalAlignment="Top" Width="251" Height="53" FontSize="36"/>
<TextBlock x:Name="textBlockSensorStatusStatus" HorizontalAlignment="Left" Margin="356,47,0,0" TextWrapping="Wrap" Text="..." VerticalAlignment="Top" Width="618" Height="53" FontSize="36"/>
<TextBox x:Name="textBoxSensorValue" HorizontalAlignment="Left" Margin="38,119,0,0" TextWrapping="Wrap" Text="..." VerticalAlignment="Top" Width="936" Height="213" FontSize="22"/>
<TextBlock x:Name="textBlockIoTStatus" HorizontalAlignment="Left" Margin="38,368,0,0" TextWrapping="Wrap" Text="IoT Hub Status: " VerticalAlignment="Top" Width="276" Height="53" FontSize="36"/>
<TextBlock x:Name="textBlockIoTHubStatusStatus" HorizontalAlignment="Left" Margin="356,368,0,0" TextWrapping="Wrap" Text="..." VerticalAlignment="Top" Width="618" Height="53" FontSize="36"/>
<TextBox x:Name="textBoxPayloadDeliveryStatus" HorizontalAlignment="Left" Margin="38,446,0,0" TextWrapping="Wrap" Text="..." VerticalAlignment="Top" Width="936" Height="65" FontSize="36"/>
<Button x:Name="buttonClose" Content="Close" HorizontalAlignment="Left" Margin="680,609,0,0" VerticalAlignment="Top" Height="114" Width="294" FontSize="72" Background="#FFB80000" Foreground="White" Click="buttonClose_Click"/>
</Grid>
</Page>
SensorData.cs
C#////The MIT License(MIT)
////Copyright(c) 2016 BardaanA
////Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
////The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
////THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RasPi2BApp
{
// Sensor data type from FEZ HAT is packaged into this form for easy transport.
// Note the to string method at the bottom, isn't that beautiful?!
class SensorData
{
public double Temperature { get; set; }
public double LightLevel { get; set; }
public double AnalogInOne { get; set; }
public double AnalogInTwo { get; set; }
public double AnalogInThree { get; set; }
public double ServoOne { get; set; }
public double ServoTwo { get; set; }
public SensorData()
{
Temperature = 0;
LightLevel = 0;
AnalogInOne = 0;
AnalogInTwo = 0;
AnalogInThree = 0;
ServoOne = 0;
ServoTwo = 0;
}
public override string ToString()
{
return $"Sensors:: Temperature: {Temperature}, Light Level: {LightLevel}, Servo One: {ServoOne}, Servo Two: {ServoTwo}, Analog One: {AnalogInOne}, Analog Two: {AnalogInTwo}, Analog Three: {AnalogInThree}";
}
}
}
SensorException.cs
C#////The MIT License(MIT)
////Copyright(c) 2016 BardaanA
////Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
////The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
////THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RasPi2BApp
{
// Just following the exceptional convention ;)
class SensorException : Exception
{
private string message;
public SensorException(string msg)
{
message = msg;
}
public override string Message
{
get
{
return $"Sensor fail! "+ message + " " + base.Message;
}
}
}
}
Sensors.cs
C#////The MIT License(MIT)
////Copyright(c) 2016 BardaanA
////Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
////The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
////THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// Library used to access the FEZ HAT
using GHIElectronics.UWP.Shields;
//using System.Diagnostics;
namespace RasPi2BApp
{
class Sensors
{
// Containment delegation, anyone?? LOL!
private FEZHAT hat;
private SensorData sensorData;
// Initialization test flag
private bool initialized;
public bool IsInitialized { get { return initialized; } }
public async Task Initialize()
{
initialized = false;
try
{
hat = await FEZHAT.CreateAsync();
}
catch(Exception ex)
{
//Debug.WriteLine("FezHat initialization failed! " + ex.Message);
throw new SensorException(ex.Message);
}
if(hat != null)
{
initialized = true;
// You might wanna change these values to prevent your servos from grinning!
// We don't have any replacement to ship to you guys. But we do know where to get them, ebay??
hat.S1.SetLimits(500,2200,0,180);
hat.S2.SetLimits(500,2200,0,180);
// Start position
hat.S1.Position = 90;
hat.S2.Position = 90;
}
}
// Read the sensor data, for updated values.
public SensorData Read()
{
sensorData = new SensorData();
if(this.hat != null)
{
try
{
sensorData.Temperature = Celsius2Fahrenheit(hat.GetTemperature());
sensorData.LightLevel = hat.GetLightLevel();
sensorData.AnalogInOne = hat.ReadAnalog(FEZHAT.AnalogPin.Ain1);
sensorData.AnalogInTwo = hat.ReadAnalog(FEZHAT.AnalogPin.Ain2);
sensorData.AnalogInThree = hat.ReadAnalog(FEZHAT.AnalogPin.Ain3);
sensorData.ServoOne = hat.S1.Position;
sensorData.ServoTwo = hat.S2.Position;
}
catch(Exception ex)
{
//Debug.WriteLine("Sensor data read failed! " + ex.Message);
throw new SensorException(ex.Message);
}
}
// I have been using this querky method of initializing objects to zero, just to avoid errors.
// You are welcome to use a better design pattern
return sensorData;
}
// Servo control method, takes a double array as an input
public void ServoControl(double[] inputArray)
{
if(hat != null)
{
hat.S1.Position = inputArray[0];
hat.S2.Position = inputArray[1];
}
}
// Method to convert Celsius to Fahrenheit because FezHat returns temperature value in Celsius
private double Celsius2Fahrenheit(double input)
{
return ((input * 9.0) / 5.0) + 32.0;
}
}
}
Comments