Build a Distributed IoT Cluster on .NET 6

Set up an IoT application

Your browser needs to be JavaScript capable to view this video

Try reloading this page, or reviewing your browser settings

This video segment demonstrates how to build an IoT application by using .NET 6.

Keywords

  • .NET 6
  • Console Application
  • IoT
  • C# 10
  • Class Library

Conflict of Interest

The author declares no conflict of interest.

About this video

Author(s)
Fiodar Sazanavets
First online
15 May 2022
DOI
https://doi.org/10.1007/978-1-4842-8285-4_4
Online ISBN
978-1-4842-8285-4
Publisher
Apress
Copyright information
© Fiodar Sazanavets 2022

Video Transcript

In this segment, we will add an example of IoT applications that will connect to our server side. It will be an application that you will place on the weather measuring device that will send temperature, pressure, and humidity metrics to the server. So we’ve added two projects to our solution, weather device app, which is a console application, and common device components, which is a class library.

We will be later adding a different type of IoT app. But there are some things that are common to all IoT applications, and this is why we have this class library here. If you are using ID to add such projects, you can just click on your normal new menu, and then search for either console app or class library template, depending on what type of project you want to create.

Otherwise, if you have Visual Studio Code User, you can execute dotnet new command. It will be either dotnet new console with the output of your project name, or if you are creating a class library, it will be dotnet new classlib.

Now, a question you may have at this point– we are building on Windows, so how can you get this application to run on Linux devices, because most of the general purpose IoT devices are running on Linux? Well, there are two ways of doing that. You either add runtime identify to dotnet build command. If you’re using command line, dotnet build is the command that you will need to run to build the application. And it’s either -r, or –runtime parameter that you need to use.

And then you just specify one of the supported runtimes. Runtime identified contains operating system version, and the CPU version. Alternatively, you can right-click on the project properties if you are using ID. Set target OS. And then under packaging, set the CPU version.

If we’re not right-click on the project and click on Edit Project File, we’ll see the target framework is dotnet 6 Android, and platform target is ARM32. We don’t need these attributes for now because we are running on Windows, but this is how you compile application for specific targets if you need to.

Let’s now have a look at the basics of building a SignalR client. To do so, we have this class called SignalRClientWrapper. But before we can start using it, we need to add Microsoft.AspNet Core.SignalR.Client library.

And here’s what the class contains. To be able to use dependency inversion on dependency injection, we are adding an interface that our class will implement. Our class also implements IAsyncDisposable to make sure that the connection that we are using is disposed of properly.

So the methods that we have is GetHubConnection, which is a class that represents a connection between the client and the server. And there are some helper methods that any device will use. Those will be common to any IoT devices that will run.

When we initialize this class, we are passing the serial number, device type, and the URL to our SignalR server, which will be the base URL followed by /followed by devices. So it will be base URL, as defined in launchSettings.json file of our DeviceManagementApp.Server application. It will

Be the first URL from application URL section, which in our case, is localhost with port 7058. And then this address will follow /devices, because this is what we have mapped our hub to. Because we have enabled nullable reference types, we need to set values for all of our private fields in the construction of the class. If we don’t do this, Visual Studio will warn us. So I would always recommend to enable this feature to make your code safer.

And here is what we are doing. We are first building connection. Our connection is built from HubConnectionBuilder class. The URL that we have specifying is the URL we passed into the constructor. WithUrl method, has some other overrides, as well.

You can apply fine tuned client side configuration to it. You can’t, for example, force a particular transport mechanism, such as WebSockets, long polling, and so on. But for now, we are just leaving all the configuration as default.

What we are doing, then, is associating a particular event handlers with the hub connection. These names are the names that our SignalR hub refers to. And whenever such event is triggered, we are calling a particular method, or just do a particular simple action.

So for example, if I copy and paste that and search for it inside a SignalR hub, as we can see, it’s the name of the method that we are sending something to. And that’s how server can trigger events on the client side in SignalR. These three examples are triggered without any return parameters. But this example, here, receives a flawed data type from the server as one of the parameters. Let’s have a look at how it’s being called from the hub.

As we can see, it’s the name of the event handler, followed by firmwareVersion, which is a float. CancellationToken doesn’t count, it’s not a parameter. And this is why when do you expect to receive data from the server, you need to pull the data types into the angle brackets after On method.

This method is used by other device applications to actually get hold of hub connection object. This is so other applications can add its own event handlers to it. ConnectDevice is a common method for all the clients. What we’ll do here is start connection, and then trigger event on the server.

In C#, the way we trigger server side events from the client is exactly the same as we trigger client side events from the server. So in here, we are sending serial number and device type as parameters to a method called ConnectDevice. As we can see, this is our method, and these are the parameters.

So when we make this call from here, this method on SignalR hub will get triggered. And that applies to all methods, as well. So by now, you probably appreciate how easy it is to write code in SignalR. Both the client and the server code is written in such a way that it feels almost like your call and methods in the classes that are located in the same project.

Device diagnostics is a common thing to any device type, as well, and this is why we have SendDiagnostics method. There are many ways you can get data from the hardware, but it’s way beyond the scope of this video. So in our case, we are just generating some random values just to demonstrate how things work. And then we send those diagnostics to UpdateDiagnostics method on the SignalR hub.

DisposeAsync is just an implementation of IAsyncDisposable. When we stop our client, our system will dispose of the collection properly. We are calling the SuppressFinalize in Garbage Collector to make sure that it’s not called again.

And here are the private methods that were associated with various events. So UpdateFirmware method is associated with RequestFirmwareUpdate event. What’s happening here is we are first checking whether the firmware is updating. And if it is, we are just outputting the firmware update is already in progress into the console.

But other than that, we are creating a server side stream. So first, we are triggering the server side stream. In particular, we are sending firmwareVersion float to update firmware method. Then we’ll subscribe to the stream.

And for each of the values that we receive from it, we output the values into the console. And then once we’re finished with it, once firmware has downloaded as we are simulating firmware downloaded, here, we assess and update the firmware value back to false. And there is also RestartConnection private method, which we associate with reboot event handler.

There are also some other common objects that any type of IoT device will use. First of all, there is enum DeviceType, because device needs to know its type, and DeviceDiagnostics object. Now, let’s have a look specifically at weather device app.

In here, we don’t need any other needed packages, because we are already referencing common device components. And common device components already references all the needed packages that we need. We have an object that is specific to our weather device sensor. It’s called sensor matrix, because it is not uncommon for IoT devices to be connected to various sensors, and this application represents this.

And this is what our application is. If you are getting errors like this, you might need to rebuild your project. But we also need to make sure that we restate the target framework just to .NET 6. Now, if we open this project, we can see that our framework is .NET 6 without any other qualifiers. And now we can rebuild our project.

And here’s what we are doing inside of our application. First of all, we are creating the cancellation token source, and we are associating this cancellation token source with the CancelKeyPress, which is normally ctrl-C if you are using the Windows or Linux, or Command C if you are using Mac.

Args, as we mentioned before, is something that is implicitly used. We no longer have to pass arguments into main method of Program class. So the first thing we do here is we are extracting serial number and SignalR of URL from arguments. The first argument is serial number, and the second one is signalRHubURL

But there are many other ways of doing that. You can, for example, have those values in a configuration file, which will be specific to a device. Then we are creating SignalRClientWrapper that we had a look at previously. Our constructor would have already established SignalR connection object, so we’ll, then, go ahead and extract this hubConnection.

We can then associate yet another event with hubConnection object, which is RequestMetricUpdate. And this is associated with SendMetrics method. Once again, in here, we are just pretending that we have a sensor connected. In reality, we are just sending some random values, which nevertheless, is sufficient for demonstration purposes.

And then once we have created those values, we are sending them to UpdateMetrics method on the SignalR hub. If we’ll have a look at this method, then we’ll see that all of our web views will then receive those metrics, and will associate them with a particular connection ID.

Other than that, once we have connected device, all we are doing is just sending the metrics every minute until cancellation is triggered. Let’s now launch our server side application, and then connect the client to it to see what’s happening. You can launch your application from Visual Studio, or write an ID, or you could also execute dotnet run command inside the folder.

So I will first launch our server side application, which is DeviceManagementApp.Server. I will then need to extract URL from launch settings file. We’ll then go to our WeatherDeviceApp folder. Open the terminal there, type dotnet run. The first parameter will be our serial number, and the second parameter would be URL, the SignalR hub, which is our base URL, followed by slash, followed by devices.

Now, if we have a look at our server side console, we can see that device has connected, and it has received metrics, as well. So we can see that our SignalR connection is working. We can now stop both of our applications.

On a real device, you would execute a similar command line command. Of course, you would do it from a project that has already built, and you would probably do it by some startup script that executes every time device starts up to make sure this application is always running.

And this concludes our basic demonstration of how you can build IoT applications by using .NET, and how can you then connect those applications to a server by using SignalR. In the next segment, we will have a look at how to build a web view to manage those applications.