Tutorial: uso de MQTT para desarrollar un cliente de dispositivo IoT sin usar un SDK de dispositivo

Si es posible, debería usar uno de los SDK de dispositivos Azure IoT para compilar los clientes de dispositivos IoT. Sin embargo, en escenarios tales como el uso de un dispositivo con restricción de memoria, es posible que tenga que usar una biblioteca de MQTT para comunicarse con el centro de IoT.

Los ejemplos de este tutorial usan la biblioteca MQTT de Eclipse Mosquitto.

En este tutorial aprenderá a:

  • Compile las aplicaciones de ejemplo de clientes de dispositivos del lenguaje C.
  • Ejecute un ejemplo que use la biblioteca MQTT para enviar telemetría.
  • Ejecute un ejemplo que use la biblioteca MQTT para procesar un mensaje de la nube al dispositivo enviado desde el centro de IoT.
  • Ejecute un ejemplo que use la biblioteca MQTT para administrar el dispositivo gemelo en el dispositivo.

Puede usar una máquina de desarrollo de Windows o Linux para completar los pasos descritos en este tutorial.

Si no tiene una suscripción a Azure, cree una cuenta gratuita antes de empezar.

Prerrequisitos

Preparación del entorno para la CLI de Azure

Requisitos previos de la máquina de desarrollo

Si usa Windows:

  1. Instalar Visual Studio (Community, Professional o Enterprise). Asegúrese de habilitar la carga de trabajo Desarrollo del escritorio con C++.

  2. Instale CMake. Habilite la opción Agregar CMake al sistema PATH para todos los usuarios.

  3. Instale la versión x64 de Mosquitto.

Si usa Linux:

  1. Ejecute el siguiente comando para instalar las herramientas de compilación:

    sudo apt install cmake g++
    
  2. Ejecute el siguiente comando para instalar la biblioteca cliente de Mosquitto:

    sudo apt install libmosquitto-dev
    

Configurar el entorno

Si aún no tuviera un centro de IoT, ejecute los siguientes comandos para crear un centro de IoT de nivel gratuito en un grupo de recursos llamado mqtt-sample-rg. El comando usa el nombre my-hub como ejemplo del nombre del centro de IoT que se vaya a crear. Elija un nombre único para el centro de IoT que se usará en lugar de my-hub:

az group create --name mqtt-sample-rg --location eastus
az iot hub create --name my-hub --resource-group mqtt-sample-rg --sku F1 

Anote el nombre del centro de IoT, ya que lo necesitará más adelante.

Registro de un dispositivo en su centro de IoT. El siguiente comando registra un dispositivo llamado mqtt-dev-01 en un centro de IoT denominado my-hub. Asegúrese de usar el nombre del centro de IoT:

az iot hub device-identity create --hub-name my-hub --device-id mqtt-dev-01

Use el siguiente comando para crear un token de SAS que conceda al dispositivo acceso al centro de IoT. Asegúrese de usar el nombre del centro de IoT:

az iot hub generate-sas-token --device-id mqtt-dev-01 --hub-name my-hub --du 7200

Anote el token de SAS que genere el comando, ya que lo necesita más adelante. El token SAS parece SharedAccessSignature sr=my-hub.azure-devices.net%2Fdevices%2Fmqtt-dev-01&sig=%2FnM...sNwtnnY%3D&se=1677855761

Sugerencia

De forma predeterminada, el token de SAS será válido durante 60 minutos. La opción --du 7200 del comando anterior extenderá la duración del token a dos horas. Si expirara antes de que esté listo para usarlo, genere uno nuevo. También podrá crear un token con una duración más larga. Para más información, consulte az iot hub generate-sas-token.

Clonación del repositorio de ejemplo

Use el siguiente comando para clonar el repositorio de ejemplo en una ubicación adecuada de la máquina local:

git clone https://github.com/Azure-Samples/IoTMQTTSample.git

El repositorio también incluye:

  • Ejemplo de Python que usa la biblioteca paho-mqtt.
  • Instrucciones para usar la CLI mosquitto_pub para interactuar con el centro de IoT.

Compilación de los ejemplos de C

Antes de compilar el ejemplo, deberá agregar los detalles del dispositivo y el centro de IoT. En el repositorio IoTMQTTSample clonado, abra el archivo mosquitto/src/config.h. Agregue el nombre del centro de IoT, el identificador de dispositivo y el token de SAS de la siguiente manera. Asegúrese de usar el nombre del centro de IoT:

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#define IOTHUBNAME "my-hub"
#define DEVICEID   "mqtt-dev-01"
#define SAS_TOKEN  "SharedAccessSignature sr=my-hub.azure-devices.net%2Fdevices%2Fmqtt-dev-01&sig=%2FnM...sNwtnnY%3D&se=1677855761"

#define CERTIFICATEFILE CERT_PATH "IoTHubRootCA.crt.pem"

Nota:

El archivo IoTHubRootCA.crt.pem incluye los certificados raíz de CA para la conexión TLS.

Guarde los cambios en el archivo mosquitto/src/config.h.

Para compilar los ejemplos, ejecute los siguientes comandos en el shell:

cd mosquitto
cmake -Bbuild
cmake --build build

En Linux, los archivos binarios se encontrarán en la carpeta ./build debajo de la carpeta mosquitto.

En Windows, los archivos binarios se encontrarán en la carpeta .\build\Debug debajo de la carpeta mosquitto.

Envío de datos de telemetría

En el ejemplo de mosquitto_telemetry se muestra cómo enviar un mensaje de telemetría del dispositivo a la nube al centro de IoT mediante la biblioteca MQTT.

Antes de ejecutar la aplicación de ejemplo, ejecute el siguiente comando para iniciar el monitor de eventos del centro de IoT. Asegúrese de usar el nombre del centro de IoT:

az iot hub monitor-events --hub-name my-hub

Ejecute el ejemplo de mosquitto_telemetry. Por ejemplo, en Linux:

./build/mosquitto_telemetry

az iot hub monitor-events genera la salida siguiente que muestra la carga enviada por el dispositivo:

Starting event monitor, use ctrl-c to stop...
{
    "event": {
        "origin": "mqtt-dev-01",
        "module": "",
        "interface": "",
        "component": "",
        "payload": "Bonjour MQTT from Mosquitto"
    }
}

Ahora puede detener el monitor de eventos.

Revisión del código

Los siguientes fragmentos de código se toman del archivo mosquitto/src/mosquitto_telemetry.cpp.

Las siguientes instrucciones definen la información de conexión y el nombre del tema MQTT que se usa para enviar el mensaje de telemetría:

#define HOST IOTHUBNAME ".azure-devices.net"
#define PORT 8883
#define USERNAME HOST "/" DEVICEID "/?api-version=2020-09-30"

#define TOPIC "devices/" DEVICEID "/messages/events/"

La función main establece el nombre de usuario y la contraseña para autenticarse con el centro de IoT. La contraseña es el token de SAS que creó para el dispositivo:

mosquitto_username_pw_set(mosq, USERNAME, SAS_TOKEN);

En el ejemplo se usa el tema MQTT para enviar un mensaje de telemetría al centro de IoT:

int msgId  = 42;
char msg[] = "Bonjour MQTT from Mosquitto";

// once connected, we can publish a Telemetry message
printf("Publishing....\r\n");
rc = mosquitto_publish(mosq, &msgId, TOPIC, sizeof(msg) - 1, msg, 1, true);
if (rc != MOSQ_ERR_SUCCESS)
{
    return mosquitto_error(rc);
}
printf("Publish returned OK\r\n");

Para más información, consulte Envío de mensajes del dispositivo a la nube.

Recepción de mensajes de la nube al dispositivo

En el ejemplo de mosquitto_subscribe se muestra cómo suscribirse a temas MQTT y recibir mensajes de la nube al dispositivo desde el centro de IoT mediante la biblioteca MQTT.

Ejecute el ejemplo de mosquitto_subscribe. Por ejemplo, en Linux:

./build/mosquitto_subscribe

Ejecute el siguiente comando para enviar mensajes de la nube al dispositivo desde el centro de IoT. Asegúrese de usar el nombre del centro de IoT:

az iot device c2d-message send --hub-name my-hub --device-id mqtt-dev-01 --data "hello world"

El resultado de mosquitto_subscribe se parece al siguiente ejemplo:

Waiting for C2D messages...
C2D message 'hello world' for topic 'devices/mqtt-dev-01/messages/devicebound/%24.mid=d411e727-...f98f&%24.to=%2Fdevices%2Fmqtt-dev-01%2Fmessages%2Fdevicebound&%24.ce=utf-8&iothub-ack=none'
Got message for devices/mqtt-dev-01/messages/# topic

Revisión del código

Los siguientes fragmentos de código se toman del archivo mosquitto/src/mosquitto_subscribe.cpp.

La siguiente instrucción define el filtro de tema que usa el dispositivo para recibir mensajes de la nube al dispositivo. # es un carácter comodín de varios niveles:

#define DEVICEMESSAGE "devices/" DEVICEID "/messages/#"

La función main usa la función mosquitto_message_callback_set para establecer una devolución de llamada para controlar los mensajes enviados desde el centro de IoT y usa la función mosquitto_subscribe para suscribirse a todos los mensajes. El siguiente fragmento de código muestra la función de devolución de llamada:

void message_callback(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message)
{
    printf("C2D message '%.*s' for topic '%s'\r\n", message->payloadlen, (char*)message->payload, message->topic);

    bool match = 0;
    mosquitto_topic_matches_sub(DEVICEMESSAGE, message->topic, &match);

    if (match)
    {
        printf("Got message for " DEVICEMESSAGE " topic\r\n");
    }
}

Para más información, consulte Uso de MQTT para recibir mensajes de la nube al dispositivo.

Actualización de un dispositivo gemelo

En el ejemplo de mosquitto_device_twin se muestra cómo establecer una propiedad notificada en un dispositivo gemelo y, a continuación, volver a leer la propiedad.

Ejecute el ejemplo de mosquitto_device_twin. Por ejemplo, en Linux:

./build/mosquitto_device_twin

El resultado de mosquitto_device_twin se parece al siguiente ejemplo:

Setting device twin reported properties....
Device twin message '' for topic '$iothub/twin/res/204/?$rid=0&$version=2'
Setting device twin properties SUCCEEDED.

Getting device twin properties....
Device twin message '{"desired":{"$version":1},"reported":{"temperature":32,"$version":2}}' for topic '$iothub/twin/res/200/?$rid=1'
Getting device twin properties SUCCEEDED.

Revisión del código

Los siguientes fragmentos de código se toman del archivo mosquitto/src/mosquitto_device_twin.cpp.

Las siguientes instrucciones definen los temas que usa el dispositivo para suscribirse a las actualizaciones de dispositivos gemelos, leen el dispositivo gemelo y lo actualizan:

#define DEVICETWIN_SUBSCRIPTION  "$iothub/twin/res/#"
#define DEVICETWIN_MESSAGE_GET   "$iothub/twin/GET/?$rid=%d"
#define DEVICETWIN_MESSAGE_PATCH "$iothub/twin/PATCH/properties/reported/?$rid=%d"

La función main usa la función mosquitto_connect_callback_set para establecer una devolución de llamada para controlar los mensajes enviados desde el centro de IoT y usa la función mosquitto_subscribe para suscribirse al tema $iothub/twin/res/#.

En el fragmento de código siguiente se muestra la función connect_callback que usa mosquitto_publish para establecer una propiedad notificada en el dispositivo gemelo. El dispositivo publica el mensaje en el tema $iothub/twin/PATCH/properties/reported/?$rid=%d. El valor %d se incrementará cada vez que el dispositivo publique un mensaje en el tema:

void connect_callback(struct mosquitto* mosq, void* obj, int result)
{
    // ... other code ...  

    printf("\r\nSetting device twin reported properties....\r\n");

    char msg[] = "{\"temperature\": 32}";
    char mqtt_publish_topic[64];
    snprintf(mqtt_publish_topic, sizeof(mqtt_publish_topic), DEVICETWIN_MESSAGE_PATCH, device_twin_request_id++);

    int rc = mosquitto_publish(mosq, NULL, mqtt_publish_topic, sizeof(msg) - 1, msg, 1, true);
    if (rc != MOSQ_ERR_SUCCESS)

    // ... other code ...  
}

El dispositivo se suscribirá al tema $iothub/twin/res/# y, cuando reciba un mensaje del centro de IoT, la función message_callback lo controlará. Al ejecutar el ejemplo, se llamará a la función message_callback dos veces. La primera vez el dispositivo recibirá una respuesta del centro de IoT a la actualización de la propiedad notificada. A continuación, el dispositivo solicitará el dispositivo gemelo. La segunda vez, el dispositivo recibirá el dispositivo gemelo solicitado. El siguiente fragmento de código muestra la función message_callback:

void message_callback(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message)
{
    printf("Device twin message '%.*s' for topic '%s'\r\n", message->payloadlen, (char*)message->payload, message->topic);

    const char patchTwinTopic[] = "$iothub/twin/res/204/?$rid=0";
    const char getTwinTopic[]   = "$iothub/twin/res/200/?$rid=1";

    if (strncmp(message->topic, patchTwinTopic, sizeof(patchTwinTopic) - 1) == 0)
    {
        // Process the reported property response and request the device twin
        printf("Setting device twin properties SUCCEEDED.\r\n\r\n");

        printf("Getting device twin properties....\r\n");

        char msg[] = "{}";
        char mqtt_publish_topic[64];
        snprintf(mqtt_publish_topic, sizeof(mqtt_publish_topic), DEVICETWIN_MESSAGE_GET, device_twin_request_id++);

        int rc = mosquitto_publish(mosq, NULL, mqtt_publish_topic, sizeof(msg) - 1, msg, 1, true);
        if (rc != MOSQ_ERR_SUCCESS)
        {
            printf("Error: %s\r\n", mosquitto_strerror(rc));
        }
    }
    else if (strncmp(message->topic, getTwinTopic, sizeof(getTwinTopic) - 1) == 0)
    {
        // Process the device twin response and stop the client
        printf("Getting device twin properties SUCCEEDED.\r\n\r\n");

        mosquitto_loop_stop(mosq, false);
        mosquitto_disconnect(mosq); // finished, exit program
    }
}

Para más información, consulte Uso de MQTT para actualizar una propiedad notificada de dispositivos gemelos y Usar MQTT para recuperar propiedades de dispositivos gemelos.

Limpieza de recursos

Si está pensando en leer más artículos de desarrolladores de dispositivos, puede conservar los recursos que usó en este artículo y reutilizarlos. De lo contrario, puede eliminar los recursos que creó para este artículo a fin de evitar cargos adicionales.

Puede eliminar el centro y el dispositivo registrado a la vez si elimina todo el grupo de recursos con el siguiente comando de la CLI de Azure. No use este comando si estos recursos comparten un grupo de recursos con otros recursos que desee conservar.

az group delete --name <YourResourceGroupName>

Para eliminar solo la instancia de IoT Hub, ejecute el siguiente comando con la CLI de Azure:

az iot hub delete --name <YourIoTHubName>

Para eliminar solo la identidad del dispositivo que registró en IoT Hub, ejecute el comando siguiente mediante la CLI de Azure:

az iot hub device-identity delete --hub-name <YourIoTHubName> --device-id <YourDeviceID>

También puede que quiera quitar los archivos de ejemplo clonados de la máquina de desarrollo.

Pasos siguientes

Ahora que ha aprendido a usar la biblioteca MQTT de Mosquitto para comunicarse con IoT Hub, se recomienda revisar un paso siguiente sugerido: