🚀 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.
Note: Pre-Release versions can be found in MyGet: https://www.myget.org/F/ridopackages/api/v3/index.json
- Navigate to the Memory Monitor sample in
samples/memmon
and run it withdotnet run
(or in Visual Studio hitF5
). The console app will connect by default totest.mosquitto.org:8886
(encrypted/no auth) by default. - Browse to https://iotmodels.github.io/pnp-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.
- 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;Username=user;Password=password;CaFile=RidoFY23CA.crt"
- Connect https://iotmodels.github.io/pnp-mqtt to
localhost:8443
to interact with your device
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 theIdScope
,DeviceID
andKey
- Start the sample with
dotnet run /ConnectionStrings:cs="IdScope=<dps-id-scope>;DeviceId=<deviceId>;SharedAccessKey=<deviceSasKey>"
- Interact with the device from the Central application.
- 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
Start from this device-template (repo template)
- Define your device interactions using the DTDL language. Like this DTDL interface
- Create the base libraries to implement the DTDL interface for each cloud vendor. See the Memory Monitor sample
- Implement the device logic, by using the interface, the device logic can be reused across different cloud vendor implementations. Device Logic
- Connect the device using different Connection Settings
- Interact with the device with a DTDL enabled solution. Like Azure IoT Central, IoTExplorer or Pnp-Mqtt
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.
-
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.
-
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
-
Enable solutions to reflect those interaction patterns to create UI experiences, IoT Central, IoTExplorer or Pnp-Mqtt are examples of PnP enabled solutions.
Read the IoT Plug and Play convention for more details.
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.
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
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.ReportPropertyAsync(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.
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 : PnPMqttClient, Imemmon
- CA certificates can be provided in
PEM
orCER
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
bythumbrpint
, see Connecting with Certificates for more details.