Cliente MQTT Amazon para IoT

¿Qué es AWS IoT?

AWS IoT proporciona comunicación segura y bidireccional entre dispositivos conectados a Internet, como sensores, actuadores, microcontroladores integrados o electrodomésticos inteligentes, y la nube AWS. Esto le permite recopilar datos de telemetría de múltiples dispositivos, almacenarlos y analizarlos. También puede crear aplicaciones que permitan a sus usuarios controlar estos dispositivos desde sus teléfonos o tabletas.

 

Broker de mensajes

Proporciona un mecanismo seguro para que los dispositivos y las aplicaciones de AWS IoT publiquen y reciban mensajes entre sí. Puede usar el protocolo MQTT directamente o MQTT sobre WebSocket para publicar y suscribirse.

 

El agente de mensajes AWS IoT es un servicio de agente de publicación/suscripción que permite el envío y la recepción de mensajes hacia y desde AWS IoT. Al comunicarse con AWS IoT, un cliente envía un mensaje dirigido a un tema como Sensor/temp/room1.

 

El broker de mensajes, a su vez, envía el mensaje a todos los clientes que se han registrado para recibir mensajes sobre ese topic. El acto de enviar el mensaje se denomina publicación. El acto de registrarse para recibir mensajes sobre un filtro de topic se denomina suscripción.

 

El espacio de nombres de temas está aislado para cada par de cuenta y región de AWS. Por ejemplo, el tema Sensor/temp/room1 de una cuenta AWS es independiente del tema Sensor/temp/room1 de otra cuenta AWS. Lo mismo ocurre con las regiones. El tema Sensor/temp/room1 en la misma cuenta AWS en us-east-1 es independiente del mismo tema en us-east-2. AWS IoT no admite el envío y la recepción de mensajes entre cuentas y regiones de AWS.

 

El broker de mensajes mantiene una lista de todas las sesiones de clientes y las suscripciones de cada sesión. Cuando se publica un mensaje en un tema, el broker busca sesiones con suscripciones que coincidan con el tema. A continuación, el broker reenvía el mensaje publicado a todas las sesiones que tienen un cliente conectado actualmente.

 

Cliente MQTT

TsgcIoTAmazon_MQTT_Client es el componente utilizado para conectarse a AWS IoT. Un cliente solo puede conectarse a un dispositivo. El cliente se conecta mediante el protocolo MQTT estándar y se autentica usando un certificado de cliente X.509.

 

Para conectarse a AWS IoT, el cliente necesita las siguientes propiedades:

 

Amazon.ClientId: identificación del cliente, opcional.

Amazon.Endpoint: nombre del servidor al que se conectará el cliente MQTT.

Amazon.Port: por defecto utiliza el puerto 8883. Si el puerto es 443, utiliza ALPN automáticamente para conectarse (requiere versión personalizada de Indy).

 

AWS IoT Core admite dispositivos y clientes que usan los protocolos MQTT y MQTT sobre WebSocket Secure (WSS) para publicar y suscribirse a mensajes. La siguiente tabla enumera los protocolos que admiten los endpoints de dispositivos de AWS IoT, así como los métodos de autenticación y puertos que utilizan.

 

Protocolo Autenticación Puerto Nombre de Protocolo ALPN
MQTT over WebSocket Signature Version 4 443  
MQTT over WebSocket Autenticación personalizada 443  
MQTT Certificado de cliente X.509 443 x-amzn-mqtt-ca
MQTT Certificado de cliente X.509 8883  
MQTT Autenticación personalizada 443 mqtt

 

 

Autenticación mediante certificados

Necesita crear certificados en su consola de Amazon AWS y establecer la ruta donde están almacenados.

 

Usando OpenSSL como IOHandler debe establecer el certificado en las siguientes rutas

 

Certificate.Enabled: establézcalo en True si desea utilizar certificados.

Certificate.CertFile: ruta al certificado de cliente X.509.

Certificate.KeyFile: ruta al archivo de clave de cliente X.509.

 

Al utilizar SChannel como IOHandler, primero convierta el Certificado PEM + Clave a un certificado PFX. Esto requiere los binarios de OpenSSL:

 


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

A continuación, establezca las siguientes rutas (no es necesario establecer el archivo de clave porque ya está incluido en el certificado).

 

Certificate.Enabled: establézcalo en True si desea utilizar certificados.

Certificate.CertFile: ruta al certificado PFX

 

Autenticación SignatureV4

Debe crear un usuario en su consola de Amazon AWS y guardar las claves de acceso y secretas, que se utilizarán para firmar la solicitud WebSocket.

 

SignatureV4.Enabled: establézcalo en True si desea utilizar este tipo de autenticación.

SignatureV4.Region: la región donde se encuentra su dispositivo (ejemplo: us-east-1).

SignatureV4.AccessKey: la clave de acceso creada en su consola de Amazon u obtenida como credencial temporal.

SignatureV4.SecretKey: la clave secreta creada en su consola de Amazon o obtenida como credencial temporal

SignatureV4.SessionToken: (condicional) si utiliza credenciales de seguridad temporales, establezca aquí el token de seguridad.

OpenSSL_Options: configuración de las bibliotecas OpenSSL.

APIVersion: permite definir qué API de OpenSSL se utilizará.

oslAPI_1_0: utiliza API 1.0 de OpenSSL, la última versión compatible con Indy

oslAPI_1_1: utiliza la API 1.1 de OpenSSL, requiere nuestra biblioteca Indy personalizada y permite usar las bibliotecas OpenSSL 1.1.1 (con soporte para TLS 1.3).

oslAPI_3_0: usa la API 3.0 de OpenSSL, requiere nuestra biblioteca Indy personalizada y permite usar las bibliotecas OpenSSL 3.0.0 (con soporte TLS 1.3).

LibPath: aquí puede configurar dónde se encuentran las bibliotecas openSSL

oslpNone: es el valor predeterminado; las bibliotecas openSSL deben estar en la misma carpeta donde se encuentra el binario o en una ruta conocida.

oslpDefaultFolder: establece automáticamente la ruta de openSSL donde deben ubicarse las bibliotecas para todas las personalidades del IDE.

oslpCustomFolder: si esta es la opción seleccionada, defina la ruta completa en la propiedad LibPathCustom.

LibPathCustom: cuando LibPath = oslpCustomFolder, defina aquí la ruta completa donde se encuentran las bibliotecas openSSL.

UnixSymLinks: habilita o deshabilita la carga de SymLinks en sistemas Unix (por defecto está habilitado, excepto en OSX64):

oslsSymLinksDefault: están habilitados por defecto excepto en OSX64 (tras MacOS Monterey, falla al intentar cargar la biblioteca sin versión.).

oslsSymLinksLoadFirst: Cargar los SymLinks antes de intentar cargar las bibliotecas de versión.

oslsSymLinksLoad: Cargar SymLinks después de intentar cargar las bibliotecas de versión.

oslsSymLinksDontLoad: no carga los SymLinks.

 

*SignatureV4 requiere Indy 10.5.7 o superior

Autenticación personalizada

La autenticación personalizada le permite definir cómo autenticar y autorizar a los clientes mediante recursos de autorizador. El dispositivo transmite credenciales en los campos de cabecera de la solicitud o en los parámetros de consulta (para protocolos MQTT sobre WebSockets) o en los campos de nombre de usuario y contraseña del mensaje MQTT CONNECT (para los protocolos MQTT y MQTT sobre WebSockets).

 

CustomAuthentication.Enabled: establézcalo en True si desea usar este tipo de autenticación.

CustomAuthentication.Parameters: establezca aquí los parámetros de consulta que se pasarán al servidor (por defecto es /mqtt)

CustomAuthentication.Headers: aquí puede incluir los campos de encabezado personalizados.

CustomAuthentication.WebSockets: si se establece en true, la conexión funcionará a través del protocolo WebSocket; de lo contrario, funcionará sobre TCP simple.

 

MQTTAuthentication.Enabled: si necesita pasar el nombre de usuario y la contraseña en la conexión MQTT, habilite esta propiedad

MQTTAuthentication.Username: nombre de usuario de la conexión MQTT

MQTTAuthentication.Password: secreto de la conexión mqtt.

 

 

El cliente puede enviar opcionalmente un ClientId para identificar la conexión del cliente; así, otros clientes pueden suscribirse para recibir una notificación cada vez que este cliente se haya conectado, suscrito, desconectado...

 

Autorización

Si no puede conectarse usando el puerto 8883 y usa TCP como transporte (que es el predeterminado), Amazon utiliza la "política de AWS IoT Core" para conceder o denegar autorización a clientes y suscripciones. Probablemente deba autorizar su ID de cliente.

Entre en su consola de Amazon AWS, vaya a IoT Core y acceda al menú «Secure/Policies»; seleccione la política adjunta a su IoT Thing y compruebe al final cómo está configurada la conexión. Ejemplo:

 

{

"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-*"

]

}

 

Esta configuración significa que solo se permitirá conectarse a los clientes con ID: sdk-java, basicPubSub y sdk-nodejs-*. Modifíquelo según sea necesario e inténtelo de nuevo.

Si aún no funciona, habilite el registro y compruebe en CloudWatch el motivo por el que no puede conectarse.

 

Otras propiedades

 

MQTTHeartBeat: si está habilitado, intenta mantener activa la conexión MQTT enviando un ping cada x segundos.

 

Interval: número de segundos entre cada ping.

 

MQTTAuthentication: si está habilitado, incluye en la conexión MQTT el nombre de usuario y la contraseña

 

UserName: nombre del usuario

Contraseña: cadena secreta

 

WatchDog: si está habilitado, cuando se detecta una desconexión inesperada, intenta reconectarse al servidor automáticamente.

 

Interval: segundos antes de los intentos de reconexión.

 

Attempts: número máximo de intentos de reconexión; cero significa ilimitado.

 

LogFile: si está habilitado, guarda los mensajes del socket en un archivo de registro (útil para depuración). El acceso al archivo de registro no es seguro para subprocesos si se accede desde varios subprocesos.

 

Enabled: si está habilitado, cada vez que el socket reciba o envíe un mensaje, este se guardará en un archivo.

 

FileName: ruta completa al nombre de archivo.

 

Implementación

 

La implementación MQTT de Amazon se basa en MQTT versión 3.1.1 pero se desvía de la especificación de la siguiente manera:

 

 

Conectarse a AWS IoT

Primero, debe iniciar sesión en su consola de AWS, registrar un nuevo dispositivo y crear un certificado X.509 para ese dispositivo. Una vez hecho esto, puede crear un nuevo TsgcIoTAmazon_MQTT_Client y conectarse al servidor AWS IoT. Por ejemplo:

 


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;

Temas

El broker de mensajes utiliza temas para enrutar mensajes de los clientes publicadores a los clientes suscriptores. La barra diagonal (/) se utiliza para separar la jerarquía de temas. La siguiente tabla lista los comodines que pueden usarse en el filtro de temas al suscribirse. # Debe ser el último carácter en el tema al que se suscribe. Actúa como comodín al coincidir con el árbol actual y todos sus subárboles.

Por ejemplo, una suscripción a Sensor/# recibe mensajes publicados en Sensor/, Sensor/temp, Sensor/temp/room1, pero no los mensajes publicados en Sensor.

+ Coincide exactamente con un elemento en la jerarquía de temas. Por ejemplo, una suscripción a Sensor/+/room1 recibe mensajes publicados en Sensor/temp/room1, Sensor/moisture/room1, y así sucesivamente.

 


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;

Temas Reservados

Los siguientes métodos se utilizan para suscribirse a topics reservados o publicar en ellos.

 

Subscribe_ClientConnected(const aClientId: String): AWS IoT publica en este tema cuando un cliente MQTT con el ID de cliente especificado se conecta a AWS IoT

Subscribe_ClientDisconnected(const aClientId: String): AWS IoT publica en este tema cuando un cliente MQTT con el identificador de cliente especificado se desconecta de AWS IoT

Subscribe_ClientSubscribed(const aClientId: String): AWS IoT publica en este tema cuando un cliente MQTT con el ID de cliente especificado se suscribe a un tema MQTT

Subscribe_ClientUnSubscribed(const aClientId: String): AWS IoT publica en este tema cuando un cliente MQTT con el ID de cliente especificado cancela la suscripción a un tema MQTT

 

Publish_Rule(const aRuleName, aText: String): Un dispositivo o una aplicación publica en este tema para activar reglas directamente

 

Publish_DeleteShadow(const aThingName, aText: String): Un dispositivo o una aplicación publica en este tema para eliminar una sombra

Subscribe_DeleteShadow(const aThingName: String): Un dispositivo o una aplicación se suscribe a este topic para eliminar una sombra

Subscribe_ShadowDeleted(const aThingName: String): El servicio Device Shadow envía mensajes a este topic cuando se elimina un shadow

Subscribe_ShadowRejected(const aThingName: String): El servicio Device Shadow envía mensajes a este topic cuando se rechaza una solicitud de eliminación de un shadow

Publish_ShadowGet(const aThingName, aText: String): Una aplicación o un dispositivo publica un mensaje vacío en este topic para obtener un shadow

Subscribe_ShadowGet(const aThingName: String): Una aplicación o un dispositivo se suscribe a este topic para obtener un shadow

Subscribe_ShadowGetAccepted(const aThingName: String): El servicio Device Shadow envía mensajes a este tema cuando una solicitud de sombra se realiza correctamente

Subscribe_ShadowGetRejected(const aThingName: String): El servicio Device Shadow envía mensajes a este topic cuando se rechaza una solicitud de sombra

Publish_ShadowUpdate(const aThingName, aText: String): Un objeto o aplicación publica en este tema para actualizar un shadow

Subscribe_ShadowUpdateAccepted(const aThingName: String): El servicio Device Shadow envía mensajes a este tema cuando una actualización se realiza correctamente en un shadow

Subscribe_ShadowUpdateRejected(const aThingName: String): El servicio Device Shadow envía mensajes a este tema cuando se rechaza una actualización de un shadow

Subscribe_ShadowUpdateDelta(const aThingName: String): El servicio Device Shadow envía mensajes a este topic cuando se detecta una diferencia entre las secciones reported y desired de un shadow

Subscribe_ShadowUpdateDocuments(const aThingName: String): AWS IoT publica un documento de estado en este tema cada vez que se realiza correctamente una actualización del shadow

 

Sesiones persistentes

Una sesión persistente representa una conexión continua con un broker de mensajes MQTT. Cuando un cliente se conecta al broker de mensajes de AWS IoT mediante una sesión persistente, el broker guarda todas las suscripciones que el cliente realiza durante la conexión. Cuando el cliente se desconecta, el broker almacena los mensajes QoS 1 no confirmados y los nuevos mensajes QoS 1 publicados en los temas a los que el cliente está suscrito. Cuando el cliente se vuelve a conectar a la sesión persistente, se restablecen todas las suscripciones y todos los mensajes almacenados se envían al cliente a una tasa máxima de 10 mensajes por segundo.

 

Se crea una sesión persistente MQTT estableciendo el parámetro cleanSession en False en el evento OnMQTTBeforeConnect. Si no existe ninguna sesión para el cliente, se crea una nueva sesión persistente. Si ya existe una sesión para el cliente, se reanuda.

 

Los dispositivos deben comprobar el atributo Session en el evento OnMQTTConnect para determinar si existe una sesión persistente. Si Session es True, existe una sesión persistente y los mensajes almacenados se entregan al cliente. Si Session es False, no existe ninguna sesión persistente y el cliente debe volver a suscribirse a sus filtros de tema.

 

Las sesiones persistentes tienen un período de expiración predeterminado de 1 hora. El período de expiración comienza cuando el broker de mensajes detecta que un cliente se desconecta (desconexión MQTT o tiempo de espera agotado). El período de expiración de la sesión persistente puede aumentarse a través del proceso estándar de incremento de límites. Si un cliente no ha reanudado su sesión dentro del período de expiración, la sesión se termina y los mensajes almacenados asociados se descartan. El período de expiración es aproximado; las sesiones pueden persistir hasta 30 minutos más (pero no menos) que la duración configurada.

 

Credenciales temporales

AWS IoT Core puede funcionar con credenciales temporales obtenidas a través de grupos de identidades; existen 2 tipos de identidades:

 

 

No autenticado

Si utiliza credenciales no autenticadas, simplemente adjunte la política en el rol no autenticado creado automáticamente en el menú IAM. Luego configure el cliente estableciendo el Access, la Secret Key y el Token devueltos por el servicio Cognito.

A continuación se muestra un código en .NET para obtener credenciales no autenticadas

 


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;

Autenticado

Credenciales autenticadas; requiere adjuntar la política en el Rol Autenticado creado automáticamente en el menú IAM y adjuntar la política del usuario en las políticas de AWS IoT Core.

Por lo tanto, cree una nueva política en el menú de políticas de IoT Core y cada vez que un nuevo usuario se autentique, asocie esta política a dicho usuario.

Puede utilizar el siguiente comando de AWS para adjuntar una política o crear una función lambda.

 

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

 

 

Aprovisionamiento de Dispositivos

El servicio Fleet Provisioning admite las siguientes operaciones de la API MQTT:

 

 

CreateCertificateFromCsr

 

Utilice el método CreateCertificateFromCsr pasando el CertificateSigningRequest como parámetro para crear el certificado. Para recibir la respuesta a esta solicitud, suscríbase primero a los siguientes métodos: SubscribeCreateCertificateFromCsrResponse y SubscribeCreateCertificateFromCsrError

 

CreateKeysAndCertificate

 

Utilice el método CreateKeysAndCertificate para crear un nuevo certificado y claves. Para recibir la respuesta a esta solicitud, suscríbase primero a los siguientes métodos: SubscribeCreateKeysAndCertificateResponse y SubscribeCreateKeysAndCertificateError

 

RegisterThing

 

Use el método RegisterThing para registrar un nuevo thing pasando como parámetro el nombre de la plantilla y el payload en formato JSON. Para recibir la respuesta a esta solicitud, suscríbase primero a los siguientes métodos: SubscribeRegisterThingResponse y SubscribeRegisterThingError.