Windows 10 IoT - Connected Nightlight Workshop
Lab03: Sending Telemetry to the Cloud
Table of Contents
- Table of Contents
- Capturing Analog Data with a Voltage Divider
- Build the Universal Windows Platform Application
- Create a Blank Universal App
- Add the Windows IoT Extensions for the UWP
- Add the Microsoft.Azure.Devices.Client NuGet Package
- Design the App UI
- Add ‘using’ Statements
- Define Constants and Variables
- Add a Clean Up Event Handler
- Initialize the SPI and GPIO Busses
- Create a Timer to Read the Sensor Values
- Test Run the App
- Send A Message to Azure IoT Hub
- Run the Application
- Conclusion & Next Steps
In this lab you will build a Universal Windows Platform application that detects ambient light and sends the data that is being collected to Azure IoT Hub. You will build a data pipeline to process the incoming data stream and output it to a visualization tool.
Capturing Analog Data with a Voltage Divider
For this lab you will work with a few new concepts, both in the circuits connected to the Raspberry Pi 2 and in the Cloud. The first thing you will do is wire up the Raspberry Pi 2 to be able to read voltage as determined by the resistance created by a photoresistor. Wire your board according to the diagram. (Wire colors don’t matter, but they help with identification of purpose.) This wiring uses an analog-to-digital-convertor (ADC) which enables you to capture analog input instead of simply digital input. You can use either an MCP3208, MCP3008, or MCP3002 ADC. When you did the lab with the LED you dealt only with a digital signal - you sent voltage to the LED to turn it on or off (with no voltage). Many sensors, such as a photoresistor, are capable of analog input or output, giving them a broader range than simply a 1 or a 0.
A photoresistor, also known as light-dependent resistor (LDR), or a photocell, works by limiting the amount of voltage that passes through it based on the intensity of light detected. The resistance decreases as light input increases - in other words, the more light, the more voltage passes through the photoresistor.
In order to take advantage of the photoresistor you will create a voltage divider, which is a passive linear circuit that splits the input voltage amongst two or more components (similar to a Y-splitter). The following schematic shows a voltage divider in use on an Arduino Uno R3 (this is a simpler way to show the diagram as compared to the Raspberry Pi 2 which, as you will see, incorporates an external ADC).
To create the voltage divider needed for this the Raspberry Pi 2 has been connected as follows:
- Voltage connected from the 5-volt (input voltage) pin to a circuit using a breadboard.
- Input voltage connected to a 10k Ohm static resistor.
- A voltage divider is established coming out of the static resistor:
- One route to the ADC which is connected to the Raspberry Pi 2.
- One route to a variable resistor: the photoresistor.
- The circuit is completed out of the variable resistor to ground.
As the photoresistor increases its resistance (lower light intensity) more of the input voltage coming through the circuit is diverted to the ADC. That means that the less intense the light into the photoresistor the more resistance it creates, which in turn diverts more voltage to the ADC (the current has to go somewhere). Likewise, the more intense the light into the photoresistor, the less resistance it creates, which in turn means there is less voltage to divert to the ADC.
In short, the darker it is, the more resistance the photoresistor provides and the more voltage is diverted to the ADC.
Following is the wiring diagram for this circuit. Select the ADC you have and take a minute to identify the circuit on the board.
MCP3002 - 10-bit, 2-channel ADC
MCP3008 - 10-bit, 8-channel ADC
NOTE: The ADC has a notch out of one side - ensure that the side with the notch is (according to the diagram) on the lower edge of the breadboard.
Build the Universal Windows Platform Application
In this application you will read the voltage value coming into the ADC from the voltage divider - the higher the voltage, the darker it is (remember, you are reading in the residual voltage, which is diverted to the ADC when there is resistance in the photoresistor). You will use the darkness value to determine if the LED should be on or off.
The ADC is connected to the Raspberry Pi 2 through the Serial Peripheral Interface (SPI) bus. SPI is a synchronous serial communication interface used for short distance communication, primarily in embedded systems. SPI devices communicate in full duplex mode using a master-slave architecture with a single master. The master device originates the frame for reading and writing. Multiple slave devices are supported through selection with individual slave select (SS) lines. SPI is a four-wire serial bus as follows:
- SCLK - Serial Clock (output from master).
- MOSI - Master Output, Slave Input (output from master).
- MISO - Master Input, Slave Output (output from slave).
- SS - Slave Select (active low, output from master).
We won’t go any deeper into SPI or the pin layout of the two ADCs; suffice to say that the ADC is wired up to support the four-channel SPI bus, plus supply voltage and ground. The wire connecting the voltage divider to the ADC is the input channel you will read the residual voltage from.
Create a Blank Universal App
- Launch Visual Studio and start a new Blank App (Universal Windows) (found in the C# -> Windows -> Universal node).
- Name the application IoTNightlight.
Add the Windows IoT Extensions for the UWP
Once the solution is created:
- Click on the Project menu and select Add Reference.
- In the Reference Manager dialog, expand the Universal Windows node and select Extensions.
- In the list of extensions, check the box next to Windows IoT Extensions for the UWP and click OK . Make sure to select the same version number as the OS running on the Raspberry Pi. It is easy to accidently select the Windows Mobile Extensions for the UWP, (which is just below the IoT extensions) so take extra care to make sure you have added the correct reference.
Add the Microsoft.Azure.Devices.Client NuGet Package
Once the Windows IoT Extensions for the UWP are added…
- Click on the Project menu and select Manage NuGet Packages.
- Use the search field to search for Microsoft.Azure.Devices.Client.
- Click on the Install button to install the package.
Design the App UI
Open the MainPage.xaml file. This is the layout definition for the initial page that loads when the app is run.
- Replace the
<Grid>...</Grid>
code with the following:
Add ‘using’ Statements
Throughout this lab you will use a feature in Visual Studio called light bulbs. Light bulbs are a new productivity feature in Visual Studio 2015. They are icons that appear in the Visual Studio editor and that you can click to perform quick actions including refactoring fixing errors. Light bulbs bring error-fixing and refactoring assistance into a single focal point, often right on the line where you are typing. As you write the code in this lab you will add calls to methods that don’t yet exist. The editor will indicate this to you by putting a red “squiggle” underline beneath the method call. When you hover over the offending code a light bulb will appear and you can expand it to see options for generating the missing method.
- Open the MainPage.xaml.cs file. This is the code behind the layout for the MainPage.xaml.
- Add the following to the using statements at the top of the file.
Define Constants and Variables
There are several constants and variables that you will reference throughout this code. This code is written to support the MCP3002 (10-bit, 2-channel), MCP3008 (10-bit, 8-channel) , or the MCP3208 (12-bit, 8-channel) ADCs. You must set the value of ADC_DEVICE
to the specific ADC you are using, and follow the appropriate wiring diagram (above). For this lab you have the MCP3002.
There is a lot defined here - the function of each constant or variable will become evident as you code the application.
Add a Clean Up Event Handler
Inside the MainPage() constructor, register an event handler for the Unloaded event.
This event handler will be invoked whenever the MainPage is unloaded. You will use it to clean up a few resources. Use the Visual Studio light bulb feature to Generate method ‘MainPage_Unloaded’ and add the following code to the method. MainPageUnloaded will dispose of connections to the pins of the Raspberry Pi 2 when invoked.
Initialize the SPI and GPIO Busses
Still in the ManPage() constructor, add a call to a new method named InitAllAsync().
- Use the Visual Studio light bulb feature to add the InitAllAsync() method.
- Modify the method signature to mark it as an
async
method and return aTask
.
This method uses Task.WhenAll()
to await two different asynchronous calls - one that will initialize the GPIO bus and one that will initialize the SPI bus. Using Task.WhenAll()
suspends the InitAllAsync
method until both of the contained asynchronous calls are are completed.
Use the Visual Studio light bulb feature to create the InitGpioAsync() method.
- Initialize the GPIO bus by assigning the default GPIO controller to the
gpio
variable - Check for null (throw an exception if it is null).
- Initialize the
redLedPin
in the same way you did in the earlier lab, ‘Hello, Windows IoT!’.
Make sure that you added the async
modifier to the method signature to make this an asynchronous method. Also, make sure you set its return type to Task
.
Go back to the MainPage() constructor
- Use the Visual Studio light bulb feature to add the InitSpiAsync() method.
- In this method you initialize the SPI buss so that you can use it to communicate through the ADC.
- Modify the method signature to mark it as an async method and return a
Task
.
Create a Timer to Read the Sensor Values
Next you will create a timer to read the data from the photoresistor and set the state of the LED. To do this, in the InitAllAsync() method, replace // TODO: Read sensors every 100ms and refresh the UI
with:
Use the Visual Studio light bulb feature to add an event handler for SensorTimer_Tick.
In SensorTmer_Tick you will call two methods: one to read the sensor data in, and one to set the state of the LED.
- Use the Visual Studio light bulb feature to create the ReadAdc() method.
In this method you create a command buffer to write to the ADC, and a read buffer to capture the values from the ADC. The SPI configuration (based on which ADC you are using) is the first node in the command/write buffer. When you call TransferFullDuplex()
you open a two-way channel with the ADC over the SPI bus: a command/write channel and a read channel.
The convertToInt(readBuffer)
method is used to convert the byte array returned from the ADC into an integer.
- Use the Visual Studio light bulb feature to add convertToInt(byte[]).
- Modify the method signature by changing the input variable name from readBuffer to data. Each ADC returns the data a little differently, so this command will convert the byte array to an integer based on the ADC you are using.
In the ReadAdc() method there was also a reference to a Map() method.
- Use the Visual Studio light bulb feature to add Map(int, int, int, int, int)__. This method makes it easy to map data from one value range to another.
- Modify the method signature according to the code example below.
In this example you are using the Map() method to map the value from the ADC, which is a 10-bit range (0 - 1023), to a range of 0 - 300, which is used to define the width of the darkness indicator bar in the UI. (Its max width is 300.)
- In the SensorTimer_Tick method use the Visual Studio light bulb feature to add an event handler for LightLed().
In this method you simply check to see if the ADC value is greater than two-thirds of the ADC’s resolution. In other words, you’re asking if it’s kind of dark. If it is, turn on the LED and paint the indicator bar in the UI red. If it isn’t, turn off the LED and paint the indicator bar light gray.
Test Run the App
At this point you can deploy and run the application to see if the photoresistor and LED are working. You still haven’t sent a message to Azure, but testing the circuit is never a bad idea. To deploy this application to your Raspberry Pi 2, select ARM from the Solution Platforms list in the toolbar, and select REMOTE MACHINE from the Device dropdown list in the toolbar.
You will be prompted with the Remote Connections dialog. Select your device from the list of Auto Detected devices, or type in the device name or IP address into the Manual Configuration textbox. Set the Authentication Mode to Universal (Unencrypted Protocol), and click Select.
NOTE: You can verify or modify these values by navigating to the project properties (select Properties in the Solution Explorer) and choosing the Debug tab on the left.
Now press F5 to run the application and you should see it deploy on the Raspberry Pi 2. Test the circuit and application by changing the amount of light the photoresistor is exposed to.
Send A Message to Azure IoT Hub
Now that you know your physical device is working, it is time to send its data to Azure.
In the previous lab you created a device in Azure IoT Hub. The last step of that lab was to copy the device-specific connection string for that device. If the connection string is still in your copy buffer, simply paste it into the IOT_HUB_CONN_STRING field in Step 1 below. If the connection string is no longer in your copy buffer, you can get the device-specific connection string again by selecting it in the DeviceExplorer Devices list by right-clicking and selecting Copy connection string for selected device.
Just before the MainPage() constructor definition, update the following code.
- Use the device-specific connection string as the value of IOT_HUB_CONN_STRING.
- Use the name of the device you created in Azure IoT Hub as the IOT_HUB_DEVICE.
- Use any string value you’d like as the IOT_HUB_DEVICE_LOCATION.
- In the InitAllAsync() method, replace the comment
// TODO: Instantiate the Azure device client
with:
- Replace the comment
// TODO: Send messages to Azure IoT Hub every one-second
with:
- Use the Visual Studio light bulb feature to create the MessageTimer_Tick() event handler.
Each time the MessageTimer ticks (once per second) this event handler will invoke the SendMessageToIoTHubAsync() method. Use the Visual Studio light bulb feature to create the SendMessageToIoTHubAsync() method. Modify the method signature to mark it as an async method.
With this method you attempt to construct a JSON message payload, display it on the screen, and send it to your Azure IoT Hub. The communication with the Azure IoT Hub is managed by the deviceClient
object from the Microsoft.Azure.Devices.Client namespace.
The final MainPage.xaml.cs can be found here and the complete solution can be found here.
Run the Application
Now you can run the application on your Raspberry Pi 2 and not only will you see the indicator bar changing, but you will also see the log of messages being sent to Azure IoT Hub at a rate of one per second.
Conclusion & Next Steps
Congratulations! You have built a Universal Windows Platform application that captures data from the physical word and sends it to Azure IoT Hub. The core concepts you’ve learned are:
- Working with analog data using a voltage divider and an analog-t-digital converter (ADC).
- Configuring and using the Serial Peripheral Interface (SPI).
- Creating and sending messages to Azure IoT Hub.
At this point, nothing interesting is happening with that data you are sending to Azure. It is simply being persisted for a default amount of time (1-day) and then being dropped. In the next lab you will setup some Azure services to process and visualize the data.