Skip to content

MQTT application patterns to work across different Cloud Vendors

License

Notifications You must be signed in to change notification settings

iotmodels/MQTTnet.Extensions.MultiCloud

Repository files navigation

MQTTnet.Extensions.MultiCloud

🚀 Create dotnet MQTT applications compatible with any MQTT Cloud provider, such as Azure IoT, AWS IoT Core, Hive MQ or Mosquitto. Based on dotnet/MQTTnet. See the feature matrix by cloud provider.

Architecture

Nuget Nuget Nuget

ci

Note: Pre-Release versions can be found in MyGet: https://www.myget.org/F/ridopackages/api/v3/index.json

QuickStarts

Using test.mosquitto.org

  • Navigate to the Memory Monitor sample in samples/memmon and run it with dotnet run (or in Visual Studio hit F5). The console app will connect by default to test.mosquitto.org:8886 (encrypted/no auth) by default.
  • Browse to https://iotmodels.github.io/iotux-mqtt and connect to test.mosquitto.org:8081 (websockets, encrypted/no auth)
  • You should see a list of devices, select a device matching your machine name. Invoke the command, or change a property. The console application should show those changes.

test.mosquitto.org

Next Steps

  • The same app can be executed with a local mosquitto server, you can use a pre-configured docker image like: mosquitto-local
  • Start mosquitto with docker run -it --rm -p 8080:8080 -p 1883:1883 -p 8883:8883 -p 8884:8884 -p 8443:8443 ridomin/mosquitto-local:dev
  • Update the connection settings: dotnet run /ConnectionSettings:cs="HostName=localhost;TcpPort=8883;ClientId=memmon-device;Username=user;Password=password;CaFile=RidoFY23CA.crt"
  • Connect https://iotmodels.github.io/iotux-mqtt to localhost:8443 to interact with your device

Using Azure IoT Central

The same application code works with Azure IoT, you can use Azure IoT Hub and IoT Explorer, or Azure IoT Central:

  • You will need an IoT Central application
  • Create a new device template importing the Memory Monitor interface: samples/memmon/dtmi_rido_pnp_memmon-1.json, customize the Views to edit the properties, an Publish the template.
  • Create a new Device Identity, select Connect to get the IdScope, DeviceID and Key
  • Start the sample with dotnet run /ConnectionStrings:cs="IdScope=<dps-id-scope>;DeviceId=<deviceId>;SharedAccessKey=<deviceSasKey>"
  • Interact with the device from the Central application.

Next Steps

  • Connect to Azure IoT Hub by providing a device connection string
  • Use IoT Explorer to interact with the device. Must configure IoT Explorer to be able to resolve the DTDL model from a local or a private model repository.
  • Configure DPS with Azure IoT Hub

Create your own

Start from this device-template (repo template)

  1. Define your device interactions using the DTDL language. Like this DTDL interface
  2. Create the base libraries to implement the DTDL interface for each cloud vendor. See the Memory Monitor sample
  3. Implement the device logic, by using the interface, the device logic can be reused across different cloud vendor implementations. Device Logic
  4. Connect the device using different Connection Settings
  5. Interact with the device with a DTDL enabled solution. Like Azure IoT Central, IoTExplorer or Pnp-Mqtt

TL;DR;

Any MQTT solution will have at least two parts: Devices and Solutions to interact with those devices..

This repo focuses on the first part: how to implement things/devices that can work with any cloud vendor supporting a MQTT service.

  1. Connect the devices to the endpoint in a secure way, this can be done by using Basic-Auth-Credentials, Shared Access Keys or X509 client certificates.

  2. Describe the interaction patterns (basically Pub/Sub) in a way that can be implemented for different cloud vendors, these interaction patterns consist of:

    • Telemetry. Ephimeral data sent by device sensors, eg. the device temperature, aka d2c messages
    • Commands. To invoke specific actions in the device from the solution, acka c2d messages
    • Properties. To manage the device state, reported by the device and optionally being managed from the solution. eg How often the telemetry must be sent. d2c+c2d messages
  3. Enable solutions to reflect those interaction patterns to create UI experiences, IoT Central, IoTExplorer or iotux-mqtt are examples of PnP enabled solutions.

Read the IoT Plug and Play convention for more details.

Adapt the Telemetry/Property/Command pattern to different brokers

ConnectionSettings

To simplify the connection by providing a single entry point, there is a ConnectionSettings class that can be configured with a connection string based on Key/Value pairs.

// connect to iot hub with Sas Key
var cs = HostName=<hubName>.azure-devices.net;DeviceId=<deviceId>;SharedAccessKey=<deviceSasKey>;
var client = await  HubDpsFactory.CreateFromConnectionSettingsAsync(cs); 
// connect to mosquitto with client certificate
var cs = HostName=test.mosquitto.org;TcpPort=8884;ClientId=<clientId>;X509Key=<cert.pem>|<cert.key>;
var client = await  BrokerClientFactory.CreateFromConnectionSettingsAsync(cs); 

See ConnectionSettings docs for full reference.

MqttClientOptionsBuilder extensions

The connection settings are used by the .WithConnectionSettings MqttClientOptionsBuilder extension, including:

  • Azure IoT Hub Shared Access Keys (for device and module Identities)
  • Azure IoT Hub Shared X509 Certificates(for device and module Identities)
  • Azure IoT DPS Shared Access Keys
  • Azure IoT DPS Shared X509 Certificates
  • AWS IoT Client X509 Certificates
  • MQTT Username/Password with or without TLS
  • TLS Supoport for private CA
  • TLS Supoport for private CA and Client Certificates

AzureIoTClient

This library implements the default Azure IoT Hub primitives: Provsioning through DPS, SaS Authentication, Telemetry, Property and Commands, see IoTHubClient sample for a full API refernce

Connecting to Iot Hub (with or without DPS)

var connectionString = "HostName=<hubName>.azure-devices.net;DeviceId=<deviceId>;SharedAccessKey=<deviceSasKey>"
var client = new HubMqttClient(await HubDpsFactory.CreateFromConnectionSettingsAsync(connectionString));

Sending Telemetry events:

var puback = await client.SendTelemetryAsync(new { workingSet = Environment.WorkingSet });

Read and Update the Device Twin:

var twin = await client.GetTwinAsync(stoppingToken);
var version = await client.UpdateTwinAsync(new { started = DateTime.Now });

Properties Updates (aka Writable Properties) handling:

client.OnPropertyUpdateReceived = m =>
{
      return new GenericPropertyAck
      {
         Value = m.ToJsonString(),
         Status = 200,
         Version = m["$version"].GetValue<int>()
      };
};

DirectMethods are available as Command Delegates:

client.OnCommandReceived =  m =>
{
   return new CommandResponse()
   {
      Status = 200,
      ReponsePayload = JsonSerializer.Serialize(new { myResponse = "whatever" })
   };
};

Note: This library also supports the Device Provisioning Service by providing the IdScope in the connection settings.

DTDL interfaces

When using a typed interface, there are APIs to create the base types defining the device interaction, based on the DTDL language.

A DTDL interface like:

{
  "@context": "dtmi:dtdl:context;2",
  "@id": "dtmi:rido:pnp:memmon;1",
  "@type": "Interface",
  "contents": [
    
    {
      "@type": ["Property", "TimeSpan"],
      "name": "interval",
      "schema": "integer",
      "writable": true,
      "unit": "second"
    },
    {
      "@type": ["Telemetry", "DataSize"],
      "name": "workingSet",
      "schema": "double",
      "unit": "byte"
    }
}

Can be represented in C# as:

public interface Imemmon 
{
    public const string ModelId = "dtmi:rido:pnp:memmon;1";
    public IMqttClient Connection { get; }
    public IWritableProperty<int> Property_interval { get; set; }
    public ITelemetry<double> Telemetry_workingSet { get; set; }
}

And implemented for different cloud vendors as follows:

for Azure IoT:

public class memmon : HubMqttClient, Imemmon

for AWS IoT Core:

public class memmon : AwsMqttClient, Imemmon

For any MQTT compatible broker

public class memmon : Imemmon

X509 Support

  • CA certificates can be provided in PEM or CER format to intilize the TLS connection
  • Client certificates, including the private key, can be provided by PFX files (with password), or by querying the Certificate Store CurrentUser\My by thumbrpint, see Connecting with Certificates for more details.

About

MQTT application patterns to work across different Cloud Vendors

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages