Client MQTT Amazon IoT

Qu'est-ce qu'AWS IoT ?

AWS IoT fournit une communication bidirectionnelle sécurisée entre des appareils connectés à Internet tels que des capteurs, des actionneurs, des microcontrôleurs intégrés ou des appareils intelligents et le Cloud AWS. Cela vous permet de collecter des données de télémétrie depuis plusieurs appareils, de stocker et d'analyser les données. Vous pouvez également créer des applications qui permettent à vos utilisateurs de contrôler ces appareils depuis leurs téléphones ou tablettes.

 

Broker de messages

Fournit un mécanisme sécurisé pour que les appareils et les applications AWS IoT puissent publier et recevoir des messages entre eux. Vous pouvez utiliser le protocole MQTT directement ou MQTT sur WebSocket pour publier et vous abonner.

 

Le courtier de messages AWS IoT est un service de courtier publication/abonnement qui permet l'envoi et la réception de messages vers et depuis AWS IoT. Lors de la communication avec AWS IoT, un client envoie un message adressé à un sujet comme Sensor/temp/room1.

 

Le courtier de messages envoie à son tour le message à tous les clients qui se sont enregistrés pour recevoir des messages relatifs à ce sujet. L'acte d'envoyer le message est appelé publication. L'acte de s'enregistrer pour recevoir des messages pour un filtre de sujet est appelé abonnement.

 

L'espace de noms des sujets est isolé pour chaque paire compte AWS et région. Par exemple, le sujet Sensor/temp/room1 pour un compte AWS est indépendant du sujet Sensor/temp/room1 pour un autre compte AWS. Cela est également vrai pour les régions. Le sujet Sensor/temp/room1 dans le même compte AWS en us-east-1 est indépendant du même sujet en us-east-2. AWS IoT ne prend pas en charge l'envoi et la réception de messages entre différents comptes AWS et régions.

 

Le broker de messages maintient une liste de toutes les sessions clientes et des abonnements pour chaque session. Lorsqu'un message est publié sur un sujet, le broker vérifie les sessions avec des abonnements qui correspondent au sujet. Le broker transmet ensuite le message de publication à toutes les sessions qui ont un client actuellement connecté.

 

Client MQTT

TsgcIoTAmazon_MQTT_Client est le composant utilisé pour se connecter à AWS IoT. Un client ne peut se connecter qu'à un seul appareil. Le client se connecte en utilisant le protocole MQTT standard et s'authentifie à l'aide d'un certificat client X.509.

 

Pour se connecter à AWS IoT, le client a besoin des propriétés suivantes :

 

Amazon.ClientId : identification du client, optionnel.

Amazon.Endpoint : nom du serveur où le client MQTT se connectera.

Amazon.Port: utilise par défaut le port 8883. Si le port est 443, utilise ALPN automatiquement pour se connecter (nécessite une version Indy personnalisée).

 

AWS IoT Core prend en charge les appareils et clients utilisant les protocoles MQTT et MQTT over WebSocket Secure (WSS) pour publier et s'abonner à des messages. Le tableau suivant répertorie les protocoles pris en charge par les points de terminaison d'appareils AWS IoT ainsi que les méthodes d'authentification et les ports utilisés.

 

Protocole Authentification Port Nom du protocole ALPN
MQTT sur WebSocket Signature Version 4 443  
MQTT sur WebSocket Authentification personnalisée 443  
MQTT Certificat client X.509 443 x-amzn-mqtt-ca
MQTT Certificat client X.509 8883  
MQTT Authentification personnalisée 443 mqtt

 

 

Authentification par certificats

Vous devez créer des certificats dans votre console Amazon AWS et définir le chemin où ils sont stockés.

 

En utilisant OpenSSL comme IOHandler, vous devez définir le certificat dans les chemins suivants

 

Certificate.Enabled : définissez à True si vous souhaitez utiliser des certificats.

Certificate.CertFile : chemin vers le certificat client X.509.

Certificate.KeyFile : chemin vers le fichier de clé client X.509.

 

En utilisant SChannel comme IOHandler, convertissez d'abord le certificat PEM + la clé en un certificat PFX. Cela nécessite les binaires OpenSSL :

 


openssl pkcs12 -inkey 884ccf73ff-private.pem.key -in 884ccf73ff-certificate.pem.crt -export -out 884ccf73ff-certificate.pfx

Définissez ensuite les chemins suivants (il n'est pas nécessaire de définir le fichier de clé car il est déjà inclus dans le certificat).

 

Certificate.Enabled : définissez à True si vous souhaitez utiliser des certificats.

Certificate.CertFile : chemin vers le certificat PFX

 

Authentification SignatureV4

Vous devez créer un utilisateur dans votre console Amazon AWS et sauvegarder les clés Access et Secret, qui seront utilisées pour signer la requête WebSocket.

 

SignatureV4.Enabled : définissez à True si vous souhaitez utiliser ce type d'authentification.

SignatureV4.Region : la région où votre appareil est situé (exemple : us-east-1).

SignatureV4.AccessKey : la clé d'accès créée dans votre console Amazon ou obtenue en tant qu'information d'identification temporaire.

SignatureV4.SecretKey : la clé secrète créée dans votre console Amazon ou obtenue en tant que credential temporaire

SignatureV4.SessionToken : (conditionnel) si vous utilisez des informations d'identification de sécurité temporaires, définissez ici le jeton de sécurité.

OpenSSL_Options : configuration des bibliothèques openSSL.

APIVersion : permet de définir quelle API OpenSSL sera utilisée.

oslAPI_1_0 : utilise l'API 1.0 OpenSSL, la dernière version prise en charge par Indy

oslAPI_1_1 : utilise l'API 1.1 OpenSSL, nécessite notre bibliothèque Indy personnalisée et permet d'utiliser les bibliothèques OpenSSL 1.1.1 (avec prise en charge de TLS 1.3).

oslAPI_3_0 : utilise l'API OpenSSL 3.0, nécessite notre bibliothèque Indy personnalisée et permet d'utiliser les bibliothèques OpenSSL 3.0.0 (avec prise en charge de TLS 1.3).

LibPath : ici vous pouvez configurer l'emplacement des bibliothèques openSSL

oslpNone : c'est la valeur par défaut ; les bibliothèques OpenSSL doivent se trouver dans le même dossier que le binaire ou dans un chemin connu.

oslpDefaultFolder : définit automatiquement le chemin openSSL où les bibliothèques doivent être situées pour toutes les personnalités de l'IDE.

oslpCustomFolder : si cette option est sélectionnée, définissez le chemin complet dans la propriété LibPathCustom.

LibPathCustom : lorsque LibPath = oslpCustomFolder, définissez ici le chemin complet où se trouvent les bibliothèques openSSL.

UnixSymLinks : activer ou désactiver le chargement des liens symboliques sous les systèmes Unix (activé par défaut, sauf sous OSX64) :

oslsSymLinksDefault : activés par défaut sauf sous OSX64 (après MacOS Monterey, le chargement de la bibliothèque sans version échoue.).

oslsSymLinksLoadFirst : Charger d'abord les liens symboliques avant de tenter de charger les bibliothèques de version.

oslsSymLinksLoad : Charger les liens symboliques après avoir tenté de charger les bibliothèques de version.

oslsSymLinksDontLoad : ne pas charger les SymLinks.

 

*SignatureV4 nécessite Indy 10.5.7+

Authentification personnalisée

L'authentification personnalisée vous permet de définir comment authentifier et autoriser les clients en utilisant des ressources d'autorisation. L'appareil transmet les identifiants soit dans les champs d'en-tête de la requête ou dans les paramètres de requête (pour les protocoles MQTT sur WebSockets), soit dans le champ nom d'utilisateur et mot de passe du message MQTT CONNECT (pour les protocoles MQTT et MQTT sur WebSockets).

 

CustomAuthentication.Enabled : définissez sur True si vous souhaitez utiliser ce type d'authentification.

CustomAuthentication.Parameters : définissez ici les paramètres de requête qui seront transmis au serveur (par défaut /mqtt)

CustomAuthentication.Headers : vous pouvez y placer les champs d'en-tête personnalisés.

CustomAuthentication.WebSockets : si défini à true, la connexion fonctionnera via le protocole WebSocket, sinon via TCP simple.

 

MQTTAuthentication.Enabled : si vous devez passer le nom d'utilisateur/mot de passe dans la connexion MQTT, activez cette propriété

MQTTAuthentication.Username : nom d'utilisateur de la connexion MQTT

MQTTAuthentication.Password : secret de la connexion MQTT.

 

 

Le client peut optionnellement envoyer un ClientId pour identifier la connexion client, puis d'autres clients peuvent s'abonner pour recevoir une notification chaque fois que ce client s'est connecté, abonné, déconnecté...

 

Autorisation

Si vous ne pouvez pas vous connecter en utilisant le port 8883 et TCP comme transport (qui est le transport par défaut), Amazon utilise la "politique AWS IoT Core" pour accorder ou refuser l'autorisation aux clients et aux abonnements. Vous devrez très probablement autoriser votre identifiant client.

Entrez dans votre console Amazon AWS, allez dans IoT Core et accédez au menu « Secure/Policies », sélectionnez la politique attachée à votre IoT Thing et vérifiez à la fin comment la connexion est configurée. Exemple :

 

{

"Effect": "Allow",

"Action": [

"iot:Connect"

],

"Resource": [

"arn:aws:iot:us-east-1:222178873557:client/sdk-java",

"arn:aws:iot:us-east-1:222178873557:client/basicPubSub",

"arn:aws:iot:us-east-1:222178873557:client/sdk-nodejs-*"

]

}

 

Cette configuration signifie que seuls les clients avec les identifiants sdk-java, basicPubSub et sdk-nodejs-* seront autorisés à se connecter. Modifiez-la en conséquence et réessayez.

Si cela ne fonctionne toujours pas, activez le journal et vérifiez dans CloudWatch la raison pour laquelle vous ne pouvez pas vous connecter.

 

Autres propriétés

 

MQTTHeartBeat : si activé, tente de maintenir la connexion MQTT active en envoyant un ping toutes les x secondes.

 

Interval : nombre de secondes entre chaque ping.

 

MQTTAuthentication : si activé, inclut le nom d'utilisateur et le mot de passe dans la connexion MQTT

 

UserName: nom de l'utilisateur

Mot de passe : chaîne secrète

 

WatchDog : si activé, en cas de déconnexion inattendue, tente de se reconnecter automatiquement au serveur.

 

Interval : secondes avant les tentatives de reconnexion.

 

Attempts : nombre maximum de tentatives de reconnexion ; zéro signifie illimité.

 

LogFile : si activé, sauvegarde les messages socket dans un fichier journal (utile pour le débogage). L'accès au fichier journal n'est pas thread-safe s'il est accédé depuis plusieurs threads.

 

Enabled : si activé, chaque message reçu et envoyé par le socket sera enregistré dans un fichier.

 

FileName : chemin complet vers le nom de fichier.

 

Implémentation

 

L'implémentation MQTT Amazon est basée sur la version 3.1.1 de MQTT mais s'écarte de la spécification comme suit :

 

 

Se connecter à AWS IoT

D'abord, vous devez vous connecter à votre console AWS, enregistrer un nouvel appareil et créer un certificat X.509 pour cet appareil. Une fois fait, vous pouvez créer un nouveau TsgcIoTAmazon_MQTT_Client et vous connecter au serveur AWS IoT. Par exemple :

 


oClient := TsgcIoTAmazon_MQTT_Client.Create(nil);
oClient.Amazon.Endpoint := 'a2ohgdjqitsmij-ats.iot.us-west-2.amazonaws.com';
oClient.Amazon.ClientId := 'sgcWebSockets';
oClient.Certificate.CertFile := 'amazon-certificate.pem.crt';
oClient.Certificate.KeyFile := 'amazon-private.pem.key';
oClient.OnMQTTConnect := OnMQTTConnectEvent;
oClient.Active := True;
 
procedure OnMQTTConnect(Connection: TsgcWSConnection; const Session: Boolean; const ReturnCode: TmqttConnReturnCode);
begin
  ShowMessage('Connected to AWS');
end;

Sujets

Le courtier de messages utilise des sujets pour acheminer les messages des clients éditeurs vers les clients abonnés. La barre oblique (/) est utilisée pour séparer la hiérarchie des sujets. Le tableau suivant répertorie les caractères génériques pouvant être utilisés dans le filtre de sujet lors de l'abonnement. # doit être le dernier caractère du sujet auquel vous vous abonnez. Fonctionne comme un caractère générique en correspondant à l'arborescence actuelle et à tous ses sous-arbres.

Par exemple, un abonnement à Sensor/# reçoit les messages publiés dans Sensor/, Sensor/temp, Sensor/temp/room1, mais pas les messages publiés dans Sensor.

+ Correspond exactement à un élément dans la hiérarchie des sujets. Par exemple, un abonnement à Sensor/+/room1 reçoit les messages publiés sur Sensor/temp/room1, Sensor/moisture/room1, etc.

 


oClient := TsgcIoTAmazon_MQTT_Client.Create(nil);
...
oClient.OnSubscribe := OnSubscribeEvent;
 
vPacketIdentifier := oClient.Subscribe('Sensor/moisture/room1');
  
procedure OnMQTTSubscribe(Connection: TsgcWSConnection; aPacketIdentifier: Word; aCodes: TsgcWSSUBACKS);
begin
  if vPacketIdentifier = aPacketIdentifier then
    ShowMessage('Subscribed to topic Sensor/moisture/room1'); 
end;
 
// Client, can send a message using Publish method.
oClient.Publish('Sensor/moisture/room1', '{"temp"=10}');
  
// Messages received from server, are dispatched OnMQTTPublishEvent.
// For extended payload access (string, bytes or stream), use OnMQTTPublishEx.
procedure OnMQTTPublish(Connection: TsgcWSConnection; aTopic, aText: string);
begin
  DoLog('Received Message: ' + aTopic + ' ' + aText);
end;

Topics réservés

Les méthodes suivantes sont utilisées pour s'abonner / publier sur des sujets réservés.

 

Subscribe_ClientConnected(const aClientId: String) : AWS IoT publie sur ce topic lorsqu'un client MQTT avec l'identifiant client spécifié se connecte à AWS IoT

Subscribe_ClientDisconnected(const aClientId: String): AWS IoT publie sur ce sujet lorsqu'un client MQTT avec l'identifiant client spécifié se déconnecte d'AWS IoT

Subscribe_ClientSubscribed(const aClientId: String): AWS IoT publie sur ce sujet lorsqu'un client MQTT avec l'identifiant client spécifié s'abonne à un sujet MQTT

Subscribe_ClientUnSubscribed(const aClientId: String) : AWS IoT publie sur ce sujet lorsqu'un client MQTT avec l'identifiant client spécifié se désabonne d'un sujet MQTT

 

Publish_Rule(const aRuleName, aText: String) : Un appareil ou une application publie sur ce topic pour déclencher des règles directement

 

Publish_DeleteShadow(const aThingName, aText: String) : un appareil ou une application publie sur ce sujet pour supprimer un shadow

Subscribe_DeleteShadow(const aThingName: String) : Un appareil ou une application s'abonne à ce sujet pour supprimer une ombre

Subscribe_ShadowDeleted(const aThingName: String) : Le service Device Shadow envoie des messages à ce sujet lorsqu'un shadow est supprimé

Subscribe_ShadowRejected(const aThingName: String) : Le service Device Shadow envoie des messages sur ce topic lorsqu'une requête de suppression d'un shadow est rejetée

Publish_ShadowGet(const aThingName, aText: String) : Une application ou un objet publie un message vide sur ce topic pour obtenir un shadow

Subscribe_ShadowGet(const aThingName: String) : Une application ou un objet s'abonne à ce topic pour obtenir un shadow

Subscribe_ShadowGetAccepted(const aThingName: String): Le service Device Shadow envoie des messages à cette rubrique lorsqu'une requête pour un shadow est effectuée avec succès

Subscribe_ShadowGetRejected(const aThingName: String) : Le service Device Shadow envoie des messages à ce sujet lorsqu'une requête pour une ombre est rejetée

Publish_ShadowUpdate(const aThingName, aText: String): Un objet ou une application publie sur ce sujet pour mettre à jour un shadow

Subscribe_ShadowUpdateAccepted(const aThingName: String) : Le service Device Shadow envoie des messages à ce topic lorsqu'une mise à jour est appliquée avec succès à un shadow

Subscribe_ShadowUpdateRejected(const aThingName: String) : Le service Device Shadow envoie des messages à ce sujet lorsqu'une mise à jour d'un shadow est rejetée

Subscribe_ShadowUpdateDelta(const aThingName: String) : Le service Device Shadow envoie des messages sur ce sujet lorsqu'une différence est détectée entre les sections reported et desired d'un shadow

Subscribe_ShadowUpdateDocuments(const aThingName: String) : AWS IoT publie un document d'état sur ce topic chaque fois qu'une mise à jour du shadow est effectuée avec succès

 

Sessions persistantes

Une session persistante représente une connexion continue à un courtier de messages MQTT. Lorsqu'un client se connecte au courtier de messages AWS IoT en utilisant une session persistante, le courtier de messages sauvegarde tous les abonnements effectués par le client pendant la connexion. Lorsque le client se déconnecte, le courtier de messages stocke les messages QoS 1 non accusés de réception et les nouveaux messages QoS 1 publiés sur des sujets auxquels le client est abonné. Lorsque le client se reconnecte à la session persistante, tous les abonnements sont rétablis et tous les messages stockés sont envoyés au client à un taux maximum de 10 messages par seconde.

 

Vous créez une session persistante MQTT en définissant le paramètre cleanSession à False dans l'événement OnMQTTBeforeConnect. Si aucune session n'existe pour le client, une nouvelle session persistante est créée. Si une session existe déjà pour le client, elle est reprise.

 

Les appareils doivent examiner l'attribut Session dans l'événement OnMQTTConnect pour déterminer si une session persistante est présente. Si Session est True, une session persistante est présente et les messages stockés sont délivrés au client. Si Session est False, aucune session persistante n'est présente et le client doit se réabonner à ses filtres de topics.

 

Les sessions persistantes ont une période d'expiration par défaut d'1 heure. La période d'expiration commence lorsque le courtier de messages détecte qu'un client se déconnecte (déconnexion MQTT ou délai d'attente). La période d'expiration de session persistante peut être augmentée via le processus standard d'augmentation de limite. Si un client n'a pas repris sa session dans la période d'expiration, la session est terminée et tous les messages stockés associés sont supprimés. La période d'expiration est approximative ; les sessions peuvent être persistées jusqu'à 30 minutes de plus (mais pas moins) que la durée configurée.

 

Identifiants temporaires

AWS IoT Core peut fonctionner avec des identifiants temporaires obtenus via des pools d'identités ; il existe 2 types d'identités :

 

 

Non authentifié

Si vous utilisez des identifiants non authentifiés, attachez simplement la politique dans le rôle non authentifié créé automatiquement dans le menu IAM. Configurez ensuite le client en définissant la clé d'accès, la clé secrète et le token retournés par le service Cognito.

Retrouvez ci-dessous un code en .NET pour obtenir des informations d'identification non authentifiées

 


CognitoAWSCredentials credentials = new CognitoAWSCredentials(
    "us-east-1:cc3c9c48-646d-44ef-bfd5-0c5fb2f0882f", // Identity pool ID
    Amazon.RegionEndpoint.USEast1 // Region
);
 
var identityPoolId = credentials.GetCredentialsAsync();
 
AmazonCognitoIdentityClient cognitoClient = new AmazonCognitoIdentityClient(
    credentials, // the anonymous credentials
    Amazon.RegionEndpoint.USEast1 // the Amazon Cognito region
);
 
GetIdRequest idRequest = new GetIdRequest();
idRequest.AccountId = "222178873557";
idRequest.IdentityPoolId = "us-east-1:cc3c9c48-646d-44ef-bfd5-0c5fb2f0882f";
 
GetIdResponse idResp = cognitoClient.GetId(idRequest);
 
string AccessKey = identityPoolId.Result.AccessKey;
string SecretKey = identityPoolId.Result.SecretKey;
string SessionToken = identityPoolId.Result.Token;
 
string IdentityId = idResp.IdentityId;

Authentifié

Informations d'identification authentifiées, nécessite l'attachement de la politique dans le Rôle Authentifié créé automatiquement dans le menu IAM et l'attachement de la politique de l'utilisateur dans les politiques AWS IoT Core.

Créez donc une nouvelle politique dans le menu des politiques IoT Core et chaque fois qu'un nouvel utilisateur s'authentifie, attachez cette politique à cet utilisateur.

Vous pouvez utiliser la commande AWS suivante pour attacher une politique ou créer une fonction lambda.

 

aws iot attach-policy --policy-name PolicyName --target us-east-1:XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

 

 

Provisionnement des appareils

Le service Fleet Provisioning prend en charge les opérations d'API MQTT suivantes :

 

 

CreateCertificateFromCsr

 

Utilisez la méthode CreateCertificateFromCsr en passant le CertificateSigningRequest comme paramètre pour créer le certificat. Afin de recevoir la réponse à cette demande, abonnez-vous d'abord aux méthodes suivantes : SubscribeCreateCertificateFromCsrResponse et SubscribeCreateCertificateFromCsrError

 

CreateKeysAndCertificate

 

Utilisez la méthode CreateKeysAndCertificate pour créer un nouveau certificat et des clés. Afin de recevoir la réponse à cette requête, abonnez-vous d'abord aux méthodes suivantes : SubscribeCreateKeysAndCertificateResponse et SubscribeCreateKeysAndCertificateError

 

RegisterThing

 

Utilisez la méthode RegisterThing pour enregistrer un nouvel objet en passant comme paramètre le nom du modèle et le payload au format JSON. Afin de recevoir la réponse à cette requête, abonnez-vous d'abord aux méthodes SubscribeRegisterThingResponse et SubscribeRegisterThingError.