All Products
Search
Document Center

IoT Platform:Examples

Last Updated:Oct 07, 2023

This topic describes how to call the API operations of Link SDK for C to connect Message Queuing Telemetry Transport (MQTT) 5.0-based devices to IoT Platform and enable messaging. In this example, the ./demos/mqtt_v5_basic_demo.c sample code file is used.

Background information

For more information about device connections over MQTT 5.0, see Overview.

Step 1: Initialize the SDK

  1. Add header files.

    #include "aiot_state_api.h"
    #include "aiot_sysdep_api.h"
    #include "aiot_mqtt_api.h"
  2. Add the underlying dependency and configure the log output feature.

        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. Call the aiot_mqtt_init operation to create an MQTT client instance and initialize the default parameters.

        mqtt_handle = aiot_mqtt_init();
        if (mqtt_handle == NULL) {
            printf("aiot_mqtt_init failed\n");
            return -1;
        }

Step 2: Configure required features

Call the aiot_mqtt_setopt operation to configure the following items: For more information about the parameters of the operation, see aiot_mqtt_option_t.

  1. Configure connection parameters.

    • Sample code:

       char *product_key = "a18wP******";
       char *device_name = "LightSwitch";
       char *device_secret = "uwMTmVAMnGGHaAkqmeDY6cHxxB******";
       char *mqtt_host = "iot-06z00ax1o******.mqtt.iothub.aliyuncs.com";
       ...
       ...
       /* Specify the version of MQTT. */
       protocol_version = AIOT_MQTT_VERSION_5_0;
       aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_VERSION, (void *)&protocol_version);
       /* Specify the endpoint of the MQTT broker.  */
       aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)url);
       /* Specify the port of the MQTT broker.  */
       aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
       /* Specify the ProductKey of the product to which the device belongs.  */
       aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
       /* Specify the DeviceName of the device.  */
       aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
       /* Specify the DeviceSecret of the device.  */
       aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
       /* Specify the security credential of the connection.  */
       aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
       /* If you want to use the Assigned Client Identifier feature of MQTT 5.0, set the use_assigned_clientid parameter to 1.*/
       uint8_t use_assigned_clientid = 0;
       aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_ASSIGNED_CLIENTID, (void *)(&use_assigned_clientid));
    • Parameters:

      Parameter

      Example

      Description

      mqtt_host

      iot-06z00ax1o******.mqtt.iothub.aliyuncs.com

      The endpoint to which you want to connect the device.

      • To view the endpoint of an Enterprise Edition instance or a public instance of the new version, go to the Development Configurations panel of the Instance Details page in the IoT Platform console.

      • If you use a public instance of the previous version, the endpoint of the public instance is in the ${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com format.

      For more information about public instances of the new version and the old version, Enterprise Edition instance, and endpoints, see View the endpoint of an instance.

      product_key

      a18wP******

      The device verification information. For more information, see Obtain device verification information.

      In this example, the unique-certificate-per-device verification method is used.

      device_name

      LightSwitch

      device_secret

      uwMTmVAMnGGHaAkqmeDY6cHxxB******

      protocol_version

      AIOT_MQTT_VERSION_5_0

      Specify 5.0 as the version number of MQTT.

      use_assigned_clientid

      1

      If you want to use the Assigned Client Identifier feature of MQTT 5.0, set the use_assigned_clientid parameter to 1.

    • Description of the MQTT keep-alive mechanism:

      Important
      • During a keep-alive period, the device must send at least one message, including ping requests.

      • A timer starts when IoT Platform sends a CONNACK message as a response to a CONNECT message. If IoT Platform receives a PUBLISH, SUBSCRIBE, PING, or PUBACK message, the timer is reset. IoT Platform checks the heartbeat of the MQTT connection every 30 seconds. The wait time for the heartbeat check is the period between the point in time at which a device connects to IoT Platform and the point in time at which the next heartbeat check is performed. The maximum timeout period is calculated by using the following formula: Heartbeat interval × 1.5 + Wait time for the heartbeat check. If the server receives no messages from the device within the maximum timeout period, the server ends the connection with the device.

      Link SDK for C provides the keep-alive mechanism. The following table describes the parameter that you can configure to specify custom heartbeat-related values for device connections. Otherwise, the default values are used.

      Item

      Default value

      Description

      AIOT_MQTTOPT_HEARTBEAT_MAX_LOST

      2

      The maximum number of heartbeats that can be lost. If the limit is exceeded, the system establishes a new connection.

      AIOT_MQTTOPT_HEARTBEAT_INTERVAL_MS

      25,000

      The interval between a disconnection and a new connection that is established by the system. Valid values: 1000 to 1200000. Unit: milliseconds.

      AIOT_MQTTOPT_KEEPALIVE_SEC

      1,200

      The keep-alive period. After the heartbeat is lost, the system can establish a new connection within a specified period. Valid value: 30 to 1200. Unit: seconds. We recommend that you specify a value that is greater than 300.

  2. Configure callbacks to monitor status and receive messages.

    1. Configure a callback to monitor status.

      • Sample code:

         int main(int argc, char *argv[])
        {
         ...
         ...
        
         /* Specify the default callback to receive MQTT messages.  */
         aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
         /* Specify the callback to handle MQTT events.  */
         aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
         ...
         ...
        }
         
      • Parameters:

        Item

        Example

        Description

        AIOT_MQTTOPT_RECV_HANDLER

        demo_mqtt_default_recv_handler

        When a message is received, the callback is called to perform the required operations.

        AIOT_MQTTOPT_EVENT_HANDLER

        demo_mqtt_event_handler

        When the status of the device connection changes, the callback is called to perform the required operations.

    2. Define the callback to monitor status.

      Important
      • Do not specify time-consuming logic to handle events. Otherwise, threads that are used to receive packets may be blocked.

      • Connection status changes due to multiple causes, such as network exceptions, automatic reconnections, and disconnections.

      • To handle changes to connection status, you can modify the code in the TODO section based on your business requirements.

      /* The callback to handle MQTT events. When the connection is created, recovered, or closed, the callback is called. For information about the event definition, see core/aiot_mqtt_api.h.  */
      void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
      {
       switch (event->type) {
       /* Call the aiot_mqtt_connect operation to establish a connection to the MQTT broker.  */
       case AIOT_MQTTEVT_CONNECT: {
       printf("AIOT_MQTTEVT_CONNECT\n");
       /* TODO: Specify the logic to establish a connection. Do not call time-consuming functions that may block threads. */  */
       }
       break;
      
       /* If a disconnection error occurs due to a network exception, the SDK automatically initiates a request to reconnect to the MQTT broker.  */
       case AIOT_MQTTEVT_RECONNECT: {
       printf("AIOT_MQTTEVT_RECONNECT\n");
       /* TODO: Specify the logic to re-establish a connection. Do not call time-consuming functions that may block threads. */  */
       }
       break;
      
       /* A disconnection error occurs due to a network exception. The underlying read or write operation fails. A heartbeat response is not obtained from the MQTT broker.  */
       case AIOT_MQTTEVT_DISCONNECT: {
       char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
       ("heartbeat disconnect");
       printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
       /* TODO: Specify the logic to process disconnection issues. Do not call time-consuming functions that may block threads. */  */
       }
       break;
      
       default: {
      
       }
       }
      }
       
    3. Define the callback to receive messages.

      Important
      • Do not specify time-consuming logic to process messages. Otherwise, threads that are used to receive packets may be blocked.

      • To process received messages, you can modify the code in the TODO section based on your business requirements.

      /* The default callback function that can be called to receive MQTT messages. This function is called when the SDK receives an MQTT message from the MQTT broker and no custom callback is available. */
      void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
      {
          switch (packet->type) {
              case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
                  printf("heartbeat response\n");
                  /* TODO: Define the logic for sending a heartbeat response to the server. In most cases, a response is not sent. */
              }
              break;
      
      
              case AIOT_MQTTRECV_SUB_ACK: {
                  printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",
                         -packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
                  /* TODO: Define the logic for sending a response from the MQTT broker to a subscription request. In most cases, a response is not sent. */
              }
              break;
              case AIOT_MQTTRECV_UNSUB_ACK: {
                  printf("unsuback, , packet id: %d\n",
                         packet->data.unsub_ack.packet_id);
                  /* TODO: Define the logic for sending a response from the MQTT broker to a subscription request. In most cases, a response is not sent. */
              }
              break;
              case AIOT_MQTTRECV_PUB: {
                  printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
                  printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
                  printf("pub, payload len: %x\n", packet->data.pub.payload_len);
                  aiot_mqtt_props_print(packet->data.pub.props);
              }
              break;
      
      
              case AIOT_MQTTRECV_PUB_ACK: {
                  printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);
                  /* TODO: Define the logic for sending a response from the MQTT broker to a reported QoS 1 message. Typically, a response is not sent. */
              }
              break;
      
              case AIOT_MQTTRECV_CON_ACK: {
                  aiot_mqtt_props_print(packet->data.con_ack.props);
              }
              break;
              case AIOT_MQTTRECV_DISCONNECT: {
                  printf("server disconnect, reason code: 0x%x\n", packet->data.server_disconnect.reason_code);
              }
              break;
       
              default: {
      
      
              }
          }
      }

Step 3: Establish a connection

Call the aiot_mqtt_connect_v5 operation to send a connection and authentication request to IoT Platform. For information about how to configure the parameters, see Configure connection parameters.

Note

For more information about the MQTT_PROP_ID_USER_PROPERTY user property that is specified in the following sample code, see mqtt_property_identify_t.

mqtt_properties_t *conn_props = aiot_mqtt_props_init();
mqtt_property_t user_prop = {
    .id = MQTT_PROP_ID_USER_PROPERTY,
    .value.str_pair.key.len = strlen("demo_key"),
    .value.str_pair.key.value = (uint8_t *)"demo_key",
    .value.str_pair.value.len = strlen("demo_value"),
    .value.str_pair.value.value = (uint8_t *)"demo_value",
};
aiot_mqtt_props_add(conn_props, &user_prop);
/* Establish a connection to the server over MQTT 5.0. */
res = aiot_mqtt_connect_v5(mqtt_handle, NULL, conn_props);
aiot_mqtt_props_deinit(&conn_props);
if (res < STATE_SUCCESS) {
    /* If the MQTT connection fails to be established, release the resources of the MQTT instance. */
    aiot_mqtt_deinit(&mqtt_handle);
    printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
    printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
    return -1;
}

Step 4: Enable keep-alive threads

Call the aiot_mqtt_process operation to send a heartbeat message to the MQTT broker and re-send the QoS 1 messages for which no responses are generated. This way, a persistent connection is enabled.

  1. Enable keep-alive threads.

        res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
        if (res < 0) {
            printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
            return -1;
        }
  2. To manage the keep-alive threads, define the following function:

    void *demo_mqtt_process_thread(void *args)
    {
        int32_t res = STATE_SUCCESS;
    
        while (g_mqtt_process_thread_running) {
            res = aiot_mqtt_process(args);
            if (res == STATE_USER_INPUT_EXEC_DISABLED) {
                break;
            }
            sleep(1);
        }
        return NULL;
    }

Step 5: Enable threads to receive messages

Call the aiot_mqtt_recv operation to receive MQTT messages from the broker. The required operations are performed by using the callback to receive messages. If a disconnection and then an automatic reconnection occur, the required operations are performed by using the callback to handle events.

  1. Enable threads to receive messages.

        res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
        if (res < 0) {
            printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
            return -1;
        }
                                        
  2. To manage the threads, define the following function:

    void *demo_mqtt_recv_thread(void *args)
    {
        int32_t res = STATE_SUCCESS;
    
        while (g_mqtt_recv_thread_running) {
            res = aiot_mqtt_recv(args);
            if (res < STATE_SUCCESS) {
                if (res == STATE_USER_INPUT_EXEC_DISABLED) {
                    break;
                }
                sleep(1);
            }
        }
        return NULL;
    }

Step 6: Subscribe to a topic

Call the aiot_mqtt_sub_v5 operation to subscribe to a specified topic.

  • Sample code:

        /* Use the sample code that shows how to subscribe to a topic over MQTT based on your business requirements. */
        {
            char *sub_topic = "/sys/${YourProductKey}/${YourDeviceName}/thing/event/property/post_reply";
            mqtt_properties_t *sub_props = aiot_mqtt_props_init();
            aiot_mqtt_props_add(sub_props, &user_prop);
            /* The subscription options. */
            sub_options_t opts = {
                .no_local = 1,
                .qos = 1,
                .retain_as_publish = 1,
                .retain_handling = 1,
            };
            res = aiot_mqtt_sub_v5(mqtt_handle, sub_topic, &opts, NULL, NULL, sub_props);
            aiot_mqtt_props_deinit(&sub_props);
            if (res < 0) {
                printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);
                aiot_mqtt_deinit(&mqtt_handle);
                return -1;
            }
        }
    Note
    • For more information about the MQTT_PROP_ID_USER_PROPERTY user property that is specified in the following code, see mqtt_property_identify_t.

    • For more information about how to specify subscription options, see sub_options_t.

  • Parameters:

    Parameter

    Example

    Description

    sub_topic

    /a18wP******/LightSwitch/user/get

    The topic on which the device has the Subscribe permission. Description:

    • a1oGs****** is the ProductKey of the device.

    • LightSwitch is the DeviceName of the device.

    In this example, the default custom topic is used.

    The device can receive messages from IoT Platform by using this topic.

    For more information, see Topics.

    sub_props

    aiot_mqtt_props_init()

    The additional properties that you want to specify for the subscription.

    opts

    .no_local = 1,

    .qos = 1,

    .retain_as_publish = 1,

    .retain_handling = 1,

    The subscription options.

Step 7: Send a message

Call the aiot_mqtt_pub_v5 operation to send a message to a specified topic.

  • Sample code:

    	mqtt_properties_t *pub_props = aiot_mqtt_props_init();
      /* Use the sample code that shows how to send a message over MQTT based on your business requirements.*/
      char *pub_topic = "/sys/${YourProductKey}/${YourDeviceName}/thing/event/property/post";
      char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}";
      mqtt_property_t response_prop = {
          .id = MQTT_PROP_ID_RESPONSE_TOPIC,
          .value.str.len = strlen(pub_topic),
          .value.str.value = (uint8_t *)pub_topic,
       };
       char *demo_data_str = "12345";
       mqtt_property_t correlation_prop = {
           .id = MQTT_PROP_ID_CORRELATION_DATA,
           .value.str.len = strlen(demo_data_str),
           .value.str.value = (uint8_t *)demo_data_str,
       };
       aiot_mqtt_props_add(pub_props, &response_prop);
       aiot_mqtt_props_add(pub_props, &correlation_prop);
       res = aiot_mqtt_pub_v5(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)(strlen(pub_payload)), 1,0, pub_props);
       if (res < 0) {
           printf("aiot_mqtt pub failed, res: -0x%04X\n", -res);
           aiot_mqtt_deinit(&mqtt_handle);
           return -1;
       }
    Note

    For more information about the MQTT_PROP_ID_USER_PROPERTY user property that is specified in the following code, see mqtt_property_identify_t.

  • Parameters:

    Parameter

    Example

    Description

    pub_topic

    /a18wP******/LightSwitch/user/update

    The topic on which you have the Publish permission. Description:

    • a1oGs****** indicates the ProductKey of the device.

    • LightSwitch indicates the DeviceName of the device.

    The device sends messages to IoT Platform by using this topic.

    For more information, see Topics.

    pub_payload

    {\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}

    The content of the message that you want to send to IoT Platform.

    In this example, a custom topic is used. Therefore, you can specify a custom message format.

    For more information, see Data formats.

    pub_props

    For more information, see the sample code.

    The properties that are included in the message.

After an MQTT connection between the device and IoT Platform is established, make sure that the number of messages does not exceed the threshold.

  • For more information about communication limits, see Limits.

  • If the number of messages exceeds the threshold, log on to the IoT Platform console to view accumulated messages. For more information, see View and monitor consumer groups.

Step 8: Disconnect the device from IoT Platform

Call the aiot_mqtt_disconnect_v5 operation to disconnect the device from IoT Platform.

For more information about the MQTT_PROP_ID_USER_PROPERTY user property that is specified in the following sample code, see mqtt_property_identify_t.

Note

MQTT connections are applied to devices that remain persistently connected. You can disconnect devices from IoT Platform. In this example, the main thread is used to configure parameters and establish a connection. After the connection is established, you can put the main thread to sleep.

    {
        mqtt_properties_t *disconn_props = aiot_mqtt_props_init();
        /* The reason code 0x0 indicates a normal disconnection. */
        int demo_reason_code = 0x0;
        char *demo_reason_string = "normal_exit";
        mqtt_property_t reason_prop = {.id = MQTT_PROP_ID_REASON_STRING, .value.str.len = strlen(demo_reason_string), .value.str.value = (uint8_t *)demo_reason_string};
        aiot_mqtt_props_add(disconn_props, &reason_prop);
        res = aiot_mqtt_disconnect_v5(mqtt_handle, demo_reason_code, disconn_props);
        aiot_mqtt_props_deinit(&disconn_props);
    }

Step 9: Exit the program

Call aiot_mqtt_deinit operation to delete the MQTT client instance and release the corresponding resources.

 res = aiot_mqtt_deinit(&mqtt_handle);
 if (res < STATE_SUCCESS) {
 printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
 return -1;
 }

What to do next

  • After you configure the sample code file, compile the file to generate an executable file. In this example, the ./output/mqtt-v5-basic-demo executable file is generated.

    For more information, see Prepare an environment.

  • For more information about the execution result, see View logs.