Create A Programming Project For A Real Device
Create A Programming Project For A Real Device
Create A Programming Project For A Real Device
" 100 XP
Visual Studio Code + Node.js Visual Studio Code + C# Visual Studio + Node.js Visual Studio + C#
In this unit, you'll create a programming project to simulate a sensor device in a refrigerated truck. This simulation enables you to test the
code long before you need a real truck!
IoT Central treats this simulation as real. The communication code between the device app and the IoT Central app is the same for a real
truck.
In other words, if you actually run a refrigerated truck company, you would start with simulation code like the code in this unit. After this
code works to your satisfaction, you would replace the simulation code with code that receives sensor data. Because the final code
replacement is a simple switch, learning to write the following code is a valuable experience.
1. In Visual Studio, create a new Visual C#/Windows Desktop project. Select Console App (.NET Framework).
3. Under Tools/NuGet Package Manager, select Manage NuGet Packages for Solution. Install the following libraries:
AzureMapsRestToolkit
Microsoft.Azure.Devices.Client
Microsoft.Azure.Devices.Provisioning.Client
Microsoft.Azure.Devices.Provisioning.Transport.Mqtt
5. In the Program.cs file, add all the code in the following section.
7 Note
If want to skip this unit and load all of the code into your app, download all of the contents of Program.cs from the GitHub location .
Then copy the contents into your project's Program.cs file. Be sure to replace the connection and subscription strings. Then go straight
to the next unit and start testing!
1. Add the using statements, including the statements for Azure IoT Central and Azure Maps.
C# = Copy
using System;
using System.Text.Json;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Shared;
using Microsoft.Azure.Devices.Provisioning.Client;
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 1/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
using Microsoft.Azure.Devices.Provisioning.Client.Transport;
using AzureMapsToolkit;
using AzureMapsToolkit.Common;
2. Add the namespace, class, and global variables. Replace the four <your...> strings with the keys you saved in the Truck keys.txt file.
C# = Copy
namespace refrigerated_truck
class Program
enum StateEnum
ready,
enroute,
delivering,
returning,
loading,
dumping
};
enum ContentsEnum
full,
melting,
empty
enum FanEnum
on,
off,
failed
// Telemetry globals.
const double tooWarmThreshold = 2; // Degrees C temperature that is too warm for contents.
const double tooWarmtooLong = 60; // Time in seconds for contents to start melting if temperatures
are above threshold.
static double tooWarmPeriod = 0; // Time that contents are too warm, in seconds.
static double optimalTemperature = -5; // Setting - can be changed by the operator from IoT Central.
// Gasworks Park
{47.645892, -122.336954},
{47.688741, -122.402965},
// Seward Park
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 2/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
{47.551093, -122.249266},
{47.555698, -122.065996},
// Marymoor Park
{47.663747, -122.120879},
{47.857295, -122.316355},
// Lincoln Park
{47.530250, -122.393055},
{47.503266, -122.200194},
{47.591094, -122.226833},
// Pioneer Park
{47.544120, -122.221673 }
};
static double[,] path; // Latitude and longitude steps for the route.
static double[] timeOnPath; // Time in seconds for each section of the route.
static int truckOnSection; // The current path section the truck is on.
static double truckSectionsCompletedTime; // The time the truck has spent on previous completed sections.
// User IDs.
C# = Copy
static double DistanceInMeters(double lat1, double lon1, double lat2, double lon2)
return meters;
return true;
return false;
truckSectionsCompletedTime += timeOnPath[truckOnSection];
++truckOnSection;
}
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 3/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
// Ensure remainder is less than or equal to 1, because interval may take count over what is needed.
// The path should be one entry longer than the timeOnPath array.
// Find how far along the section the truck has moved.
currentLat = path[truckOnSection, 0] + remainderFraction * (path[truckOnSection + 1, 0] -
path[truckOnSection, 0]);
state = StateEnum.ready;
Query = FormattableString.Invariant($"{currentLat},{currentLon}:{destinationLat},{destinationLon}")
};
else
// Clear the path. Add two points for the start point and destination.
int c = 0;
path[c, 0] = currentLat;
path[c, 1] = currentLon;
++c;
// Retrieve the route and push the points onto the array.
var x = directions.Result.Routes[0].Legs[0].Points[n].Latitude;
var y = directions.Result.Routes[0].Legs[0].Points[n].Longitude;
path[c, 0] = x;
path[c, 1] = y;
++c;
path[c, 0] = destinationLat;
path[c, 1] = destinationLon;
// Store the path length and time taken, to calculate the average speed.
double distanceApartInMeters;
double timeForOneSection;
// Clear the time on the path array. The path array is 1 less than the points array.
// Calculate how much time is required for each section of the path.
timeOnPath[t] = timeForOneSection;
truckOnSection = 0;
truckSectionsCompletedTime = 0;
timeOnCurrentTask = 0;
// Update the state now the route has arrived. Either: enroute or returning.
state = newState;
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 4/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
7 Note
The key call here is var directions = azureMapsServices.GetRouteDirections(req).Result; . The directions structure is complex.
Consider setting a breakpoint in this method and examining the contents of directions .
C# = Copy
try
// Pick up variables from the request payload by using the name specified in IoT Central.
switch (state)
case StateEnum.dumping:
case StateEnum.loading:
case StateEnum.delivering:
break;
case StateEnum.ready:
case StateEnum.enroute:
case StateEnum.returning:
if (contents == ContentsEnum.empty)
else
// Find route from current position to destination, and store the route.
GetRoute(StateEnum.enroute);
break;
else
catch
7 Note
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 5/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
The device responds with a conflict if the device isn't in the correct state. The command itself is acknowledged at the end of the
method. The recall command in the next step handles things similarly.
C# = Copy
destinationLat = baseLat;
destinationLon = baseLon;
// Find route from current position to base, and store the route.
GetRoute(StateEnum.returning);
switch (state)
case StateEnum.ready:
case StateEnum.loading:
case StateEnum.dumping:
break;
case StateEnum.returning:
break;
case StateEnum.delivering:
break;
case StateEnum.enroute:
ReturnToBase();
break;
if (eventText == noEvent)
{
else
6. Add the method that updates the truck simulation at each time interval.
C# = Copy
if (contents == ContentsEnum.empty)
// Turn the cooling system off, if possible, when the contents are empty.
if (fan == FanEnum.on)
fan = FanEnum.off;
else
if (fan != FanEnum.failed)
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 6/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
// Turn the cooling system off because contents are getting too cold.
fan = FanEnum.off;
else
fan = FanEnum.on;
if (DieRoll(100) < 1)
fan = FanEnum.failed;
// Set the contents temperature. Maintain a cooler temperature if the cooling system is on.
if (fan == FanEnum.on)
tempContents += -3 + DieRoll(5);
else
// If the temperature is above a threshold, count the seconds of duration. Melt the contents if it goes on
too long.
tooWarmPeriod += interval;
contents = ContentsEnum.melting;
else
timeOnCurrentTask += interval;
switch (state)
case StateEnum.loading:
// Finished loading.
state = StateEnum.ready;
contents = ContentsEnum.full;
timeOnCurrentTask = 0;
// If the fan is in a failed state, assume it has been fixed because it is at the base.
fan = FanEnum.on;
tempContents = -2;
break;
case StateEnum.ready:
timeOnCurrentTask = 0;
break;
case StateEnum.delivering:
// Finished delivering.
contents = ContentsEnum.empty;
ReturnToBase();
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 7/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
break;
case StateEnum.returning:
UpdatePosition();
if (Arrived())
switch (contents)
case ContentsEnum.empty:
state = StateEnum.loading;
break;
case ContentsEnum.full:
state = StateEnum.ready;
break;
case ContentsEnum.melting:
state = StateEnum.dumping;
break;
timeOnCurrentTask = 0;
break;
case StateEnum.enroute:
UpdatePosition();
if (Arrived())
state = StateEnum.delivering;
timeOnCurrentTask = 0;
break;
case StateEnum.dumping:
// Finished dumping.
state = StateEnum.loading;
contents = ContentsEnum.empty;
timeOnCurrentTask = 0;
break;
7 Note
This function is called at every time interval. The actual time interval is set at 5 seconds. But the simulated time (the number of
simulated seconds you specify that have passed each time this function is called) is set by the global static double interval = 60 .
So the simulation runs at a rate of 60 divided by 5, or 12 times the speed of real time.
To shorten the simulated time, reduce interval to, say, 30 (for a simulation that runs 6 times faster than real time). If you set
interval at 5, the simulation will run in real time. This would be realistic but slow, given the real driving times to the customer
destinations.
7. Add the methods to send truck telemetry. Send events too, if any have occurred.
C# = Copy
Console.ForegroundColor = clr;
Console.WriteLine(text + "\n");
Console.ResetColor();
colorMessage(text, ConsoleColor.Green);
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 8/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
colorMessage(text, ConsoleColor.Red);
while (true)
UpdateTruck();
CoolingSystemState = fan.ToString(),
ContentsState = contents.ToString(),
Event = eventText,
};
// Bail if requested.
token.ThrowIfCancellationRequested();
await s_deviceClient.SendEventAsync(telemetryMessage);
greenMessage($"Telemetry sent {DateTime.Now.ToShortTimeString()}");
await Task.Delay(intervalInMilliseconds);
7 Note
The SendTruckTelemetryAsync function is important. It sends telemetry, states, and events to IoT Central. Notice the use of JSON
strings to send the data.
8. Add the code to handle properties. You have only one writeable property and one read-only property in the app. But you could easily
add more if you need to.
C# = Copy
reportedProperties["TruckID"] = truckIdentification;
await s_deviceClient.UpdateReportedPropertiesAsync(reportedProperties);
if (desiredProperties.Contains(setting))
await s_deviceClient.UpdateReportedPropertiesAsync(reportedProperties);
7 Note
This section of code is generic to most C# apps that communicate with IoT Central. You can add more read-only properties to
reportedProperties . To create a new writeable property, set the setting string to the new property name and create an if
statement like the one in this code section.
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 9/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
C# = Copy
currentLat = baseLat;
currentLon = baseLon;
try
return;
SendDevicePropertiesAsync().GetAwaiter().GetResult();
s_deviceClient.SetDesiredPropertyUpdateCallbackAsync(HandleSettingChanged,
null).GetAwaiter().GetResult();
Console.WriteLine("Done");
SendTruckTelemetryAsync(rand, cts.Token);
Console.ReadKey();
cts.Cancel();
Console.WriteLine();
Console.WriteLine(ex.Message);
Console.WriteLine("Register device...");
ProvisioningDeviceClient provClient =
Console.WriteLine($"RegistrationID = {security.GetRegistrationID()}");
Console.Write("ProvisioningClient RegisterAsync...");
Console.WriteLine($"{result.Status}");
return result;
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 10/11
10/3/21, 11:46 PM Exercise - Create a programming project for a real device - Learn | Microsoft Docs
7 Note
You can set direct methods in the client by using statements such as s_deviceClient.SetMethodHandlerAsync("cmdGoTo",
CmdGoToCustomer, null).Wait(); .
Continue T
https://docs.microsoft.com/en-us/learn/modules/create-your-first-iot-central-app/5-create-real-device-nodejs?pivots=vs-csharp 11/11