Add support for Telemetry via Azure-IoT Hub

This commit is contained in:
Denis-Cosmin Nutiu 2019-11-17 15:29:33 +02:00
parent 70c1dc290c
commit b2ff223ad6
6 changed files with 30 additions and 98 deletions

View file

@ -109,12 +109,12 @@ namespace NucuCar.Sensors.EnvironmentSensor
return nameof(EnvironmentSensor);
}
public Dictionary<string, double> GetTelemetryData()
public Dictionary<string, object> GetTelemetryData()
{
Dictionary<string, double> returnValue = null;
Dictionary<string, object> returnValue = null;
if (_lastMeasurement != null)
{
returnValue = new Dictionary<string, double>
returnValue = new Dictionary<string, object>
{
["temperature"] = _lastMeasurement.Temperature,
["humidity"] = _lastMeasurement.Humidity,

View file

@ -10,9 +10,8 @@
<PackageReference Include="Iot.Device.Bindings" Version="1.0.0" />
<PackageReference Include="jose-jwt" Version="2.5.0" />
<PackageReference Include="libgrpc_csharp_ext.arm7" Version="1.0.8" />
<PackageReference Include="Microsoft.Azure.Devices.Client" Version="1.29.0-preview-002" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" />
<PackageReference Include="MQTTnet.Extensions.ManagedClient" Version="3.0.8" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5.2" />
<PackageReference Include="System.Device.Gpio" Version="1.0.0" />
</ItemGroup>

View file

@ -8,11 +8,7 @@ namespace NucuCar.Sensors.Telemetry
{
public class BackgroundWorker : BackgroundService
{
private readonly string _projectId;
private readonly string _region;
private readonly string _registryId;
private readonly string _deviceId;
private readonly string _rs256KeyFile;
private readonly string _azureIotHubConnectionString;
private readonly bool _serviceEnabled;
private readonly int _interval;
private readonly ILogger _logger;
@ -20,14 +16,9 @@ namespace NucuCar.Sensors.Telemetry
public BackgroundWorker(ILogger<BackgroundWorker> logger, IConfiguration configuration)
{
_logger = logger;
var configSection = configuration.GetSection("Telemetry");
_serviceEnabled = configSection.GetValue<bool>("Enabled");
_interval = configSection.GetValue<int>("Interval");
_projectId = configSection.GetValue<string>("ProjectId");
_region = configSection.GetValue<string>("Region");
_registryId = configSection.GetValue<string>("RegistryId");
_deviceId = configSection.GetValue<string>("DeviceId");
_rs256KeyFile = configSection.GetValue<string>("RS256File");
_serviceEnabled = configuration.GetValue<bool>("Telemetry:Enabled");
_interval = configuration.GetValue<int>("Telemetry:Interval");
_azureIotHubConnectionString = configuration.GetValue<string>("Telemetry:AzureIotHubConnectionString");
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@ -42,13 +33,9 @@ namespace NucuCar.Sensors.Telemetry
using var telemetryService = TelemetryService.Instance;
telemetryService.SetLogger(_logger);
telemetryService.ProjectId = _projectId;
telemetryService.DeviceId = _deviceId;
telemetryService.RegistryId = _registryId;
telemetryService.Region = _region;
telemetryService.Rs256File = _rs256KeyFile;
telemetryService.AzureIotHubConnectionString = _azureIotHubConnectionString;
await telemetryService.StartAsync();
telemetryService.Start();
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Publishing telemetry data!");

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
namespace NucuCar.Sensors.Telemetry
@ -7,6 +6,6 @@ namespace NucuCar.Sensors.Telemetry
{
string GetIdentifier();
/* Dictionary containing the topic and the value */
Dictionary<string, double> GetTelemetryData();
Dictionary<string, object> GetTelemetryData();
}
}

View file

@ -1,99 +1,49 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Client;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Client.Options;
using MQTTnet.Extensions.ManagedClient;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Newtonsoft.Json;
namespace NucuCar.Sensors.Telemetry
{
public class TelemetryService : IDisposable
{
private readonly List<ITelemetrySensor> _registeredSensors;
private readonly IManagedMqttClient _mqttClient;
private DeviceClient _deviceClient;
private ILogger _logger;
/* Singleton Instance */
public static TelemetryService Instance { get; } = new TelemetryService();
public string ProjectId { get; set; }
public string Region { get; set; }
public string RegistryId { get; set; }
public string DeviceId { get; set; }
public string Rs256File { get; set; }
public string AzureIotHubConnectionString { get; set; }
static TelemetryService()
{
}
private string GetMqttPassword()
{
string jwt;
AsymmetricCipherKeyPair keyPair;
using (var sr = new StreamReader(Rs256File))
{
var pr = new PemReader(sr);
keyPair = (AsymmetricCipherKeyPair) pr.ReadObject();
}
var rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)keyPair.Private);
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaParams);
jwt = Jose.JWT.Encode(new Dictionary<string, object>()
{
["iat"] = DateTime.UtcNow,
["exp"] = DateTime.UtcNow.AddDays(60),
["aud"] = ProjectId
}, rsa, Jose.JwsAlgorithm.RS256);
}
return jwt;
}
private TelemetryService()
{
_registeredSensors = new List<ITelemetrySensor>(5);
_mqttClient = new MqttFactory().CreateManagedMqttClient();
}
public void Dispose()
{
}
public async Task StartAsync()
public void Start()
{
_logger.LogInformation("Starting the MQTT client.");
ManagedMqttClientOptions options;
try
{
options = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
.WithClientOptions(new MqttClientOptionsBuilder()
.WithClientId($"projects/{ProjectId}/locations/{Region}/registries/{RegistryId}/devices/{DeviceId}")
.WithCredentials("unused", GetMqttPassword())
.WithTcpServer("mqtt.googleapis.com")
.WithTls().Build())
.Build();
_deviceClient = DeviceClient.CreateFromConnectionString(AzureIotHubConnectionString, TransportType.Mqtt);
}
catch (IOException e)
catch (FormatException)
{
_logger.LogCritical(e.Message);
_logger.LogCritical("Can't start telemetry service! Malformed connection string!");
throw;
}
await _mqttClient.StartAsync(options);
_logger.LogInformation("Started the MQTT client!");
}
@ -107,22 +57,23 @@ namespace NucuCar.Sensors.Telemetry
_logger.LogWarning($"Warning! Data for {sensor.GetIdentifier()} is null!");
continue;
}
await UploadData(data, cancellationToken);
}
}
private async Task UploadData(Dictionary<string, double> data, CancellationToken cancellationToken)
private async Task UploadData(Dictionary<string, object> data, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
_logger.LogInformation("Stopping the MQTT client, cancellation requested.");
await _mqttClient.StopAsync();
}
foreach (var entry in data)
{
await _mqttClient.PublishAsync(entry.Key, entry.Value.ToString(CultureInfo.InvariantCulture));
await _deviceClient.CloseAsync(cancellationToken);
return;
}
var messageString = JsonConvert.SerializeObject(data);
var message = new Message(Encoding.ASCII.GetBytes(messageString));
_logger.LogDebug($"Telemetry message: {message}");
await _deviceClient.SendEventAsync(message, cancellationToken);
}
public void RegisterSensor(ITelemetrySensor sensor)

View file

@ -2,11 +2,7 @@
"Telemetry": {
"Enabled": true,
"Interval": 3000,
"ProjectId": "playground-239221",
"DeviceId": "3087236321317137",
"RegistryId": "Development",
"Region": "europe-west1",
"RS256File": "/home/denis/Projects/RiderProjects/NucuCar/NucuCar.Sensors/Certificates/jwt.key"
"AzureIotHubConnectionString": "YOUR_IOT_HUB_CONNECTION_STRING"
},
"EnvironmentSensor": {
"Enabled": true,
@ -26,7 +22,7 @@
"Protocols": "Http2"
},
"EndPoints": {
"Https":{
"Https": {
"Url": "https://0.0.0.0:8000"
}
}