All Products
Search
Document Center

IoT Platform:Usage example

Last Updated:Jun 20, 2026

This topic uses the ./demos/data_model_basic_demo.c file from the Link SDK for C to demonstrate how to implement Thing Specification Language (TSL) on a device by using the Link SDK API.

Background

Step 1: Initialize

  1. Include the header file.

    ……
    ……
    #include "aiot_dm_api.h"
  2. Configure underlying dependencies and log output.

        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. Call aiot_dm_init to create and initialize a data-model client instance.

        dm_handle = aiot_dm_init();
        if (dm_handle == NULL) {
            printf("aiot_dm_init failed");
            return -1;}

Step 2: Configure features

Call aiot_dm_setopt to configure the following features.

  1. Associate an MQTT connection handle.

    Important

    Before you configure TSL feature parameters, ensure that you have configured required parameters, such as device authentication information. For more information, see Configure MQTT connection parameters.

    • Sample code:

          aiot_dm_setopt(dm_handle, AIOT_DMOPT_MQTT_HANDLE, mqtt_handle);
    • Related parameters:

      Parameter

      Example value

      Description

      AIOT_DMOPT_MQTT_HANDLE

      mqtt_handle

      Associates the MQTT connection handle. TSL requests use this connection.

  2. Implement a callback function to process TSL messages.

    1. Define the callback function for TSL messages.

      static void demo_dm_recv_handler(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
      {
          printf("demo_dm_recv_handler, type = %d\r\n", recv->type);
          switch (recv->type) {
              /* Response to commands for property reporting, event reporting, getting desired property values, or deleting desired property values */
              case AIOT_DMRECV_GENERIC_REPLY: {
                  demo_dm_recv_generic_reply(dm_handle, recv, userdata);
              }
              break;
              /* Set properties */
              case AIOT_DMRECV_PROPERTY_SET: {
                  demo_dm_recv_property_set(dm_handle, recv, userdata);
              }
              break;
              /* Asynchronously invoke a service */
              case AIOT_DMRECV_ASYNC_SERVICE_INVOKE: {
                  demo_dm_recv_async_service_invoke(dm_handle, recv, userdata);
              }
              break;
              /* Synchronously invoke a service */
              case AIOT_DMRECV_SYNC_SERVICE_INVOKE: {
                  demo_dm_recv_sync_service_invoke(dm_handle, recv, userdata);
              }
              break;
              /* Downstream binary data */
              case AIOT_DMRECV_RAW_DATA: {
                  demo_dm_recv_raw_data(dm_handle, recv, userdata);
              }
              break;
              /* Synchronously invoke a service in binary format. This message contains an rrpc_id in addition to the binary data. */
              case AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE: {
                  demo_dm_recv_raw_sync_service_invoke(dm_handle, recv, userdata);
              }
              break;
              /* Response from IoT Platform to upstream binary data */
              case AIOT_DMRECV_RAW_DATA_REPLY: {
                  demo_dm_recv_raw_data_reply(dm_handle, recv, userdata);
              }
              break;
              default:
                  break;
          }
      }
    2. Register the callback function for TSL messages.

      • Sample code:

            aiot_dm_setopt(dm_handle, AIOT_DMOPT_RECV_HANDLER, (void *)demo_dm_recv_handler);                   
      • Related parameters:

        Parameter

        Example value

        Description

        AIOT_DMOPT_RECV_HANDLER

        demo_dm_recv_handler

        The SDK invokes this callback function when it receives a TSL message.

  3. Configure whether IoT Platform sends response messages.

    This setting specifies whether IoT Platform sends a response message after it receives a device message.

    • Sample code:

          uint8_t post_reply = 1;
          ……
          ……
         aiot_dm_setopt(dm_handle, AIOT_DMOPT_POST_REPLY, (void *)&post_reply);
    • Related parameters:

      Parameter

      Example

      Description

      AIOT_DMOPT_POST_REPLY

      1

      Valid values:

      • 1: Send a response message.

      • 0: Do not send a response message.

    • If you set the AIOT_DMOPT_POST_REPLY parameter to 1, you must implement logic to handle response messages.

      You can implement the logic based on your business requirements. The sample code only prints logs.

      • ICA Standard Data Format (Alink JSON): When you create a product, set the Data Format parameter to ICA Standard Data Format (Alink JSON).

        The response message is returned in the recv->data.generic_reply struct and uses the Alink data format. For more information, see Device properties, events, and services.

        static void demo_dm_recv_generic_reply(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata){
            printf("demo_dm_recv_generic_reply msg_id = %d, code = %d, data = %.*s, message = %.*s\r\n",
                recv->data.generic_reply.msg_id,
                recv->data.generic_reply.code,
                recv->data.generic_reply.data_len,
                recv->data.generic_reply.data,
                recv->data.generic_reply.message_len,
                recv->data.generic_reply.message);
        }
      • Passthrough/Custom format: On the product settings page in the IoT Platform console, set the Data Format parameter to Passthrough/Custom.

        The response message is returned in the recv->data.raw_data struct. Before you send or receive messages, you must upload a parsing script to IoT Platform. For more information, see Message parsing.

        static void demo_dm_recv_raw_data_reply(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
        {
            printf("demo_dm_recv_async_service_invoke receive reply for up_raw msg, data len = %d\r\n", recv->data.raw_data.data_len);
        }

Step 3: Report properties

After the device establishes an MQTT connection, call aiot_dm_send to send property messages to IoT Platform. Store the property message in the aiot_dm_msg_t struct and pass it to aiot_dm_send.

Write code to report property messages based on one of the following device data formats.

ICA standard data format (Alink JSON)

  1. Add a TSL model.

    • Before you report properties, log on to the IoT Platform console. On the Product Details page, go to the Feature Definition tab and add properties to the TSL model for the default module or a custom module, such as demo_extra_block. For more information, see Add a TSL model manually.

      The following table lists the TSL model properties that correspond to the sample code.

      TSL module name

      Feature name

      Identifier

      Type

      Data definition

      Read/write type

      default module

      Night light switch 1

      LightSwitch

      bool

      • 0: on

      • 1: off

      Read/Write

      Power

      Power

      text

      Data length: 10,240

      Read/Write

      Operating current

      WF

      int32

      • Value range: 0–10

      • Step size: 1

      • Unit: A

      Read/Write

      demo_extra_block

      Night light switch 2

      NightLightSwitch

      bool

      • 0: on

      • 1: off

      Read/Write

    • You can also import the TSL file for this example. For instructions, see Add TSL models in batches.

  2. Set the content of the property message.

    • Sample code:

      • Default module:

        /* Content for reporting a single property */
                demo_send_property_post(dm_handle, "{\"LightSwitch\": 0}");
        /* Content for reporting properties in a batch */
                demo_send_property_batch_post(dm_handle, "{\"properties\":{\"Power\": [ {\"value\":\"on\",\"time\":1612684518000}],\"WF\": [{\"value\": 3,\"time\":1612684518000}]}}");
      • Custom module:

                demo_send_property_post(dm_handle, "{\"demo_extra_block:NightLightSwitch\": 1}");
    • Sample message description:

      • Reported TSL model messages are in JSON format. The following table shows sample messages and their corresponding Alink data formats. For more information, see Report device properties.

        Note

        You only need to provide the device data, which is the value of the params parameter in the Alink data format. The Link SDK handles the encapsulation of reported messages.

        Message type

        Sample message

        Alink format

        Single property

        {\"LightSwitch\": 0}
        {
          "id": "123",
          "version": "1.0",
          "sys":{
              "ack":0
          },
          "params": {
                 "LightSwitch": 0
            },
          "method": "thing.event.property.post"
        }

        Batch properties

        {\"properties\":{\"Power\": [ {\"value\":\"on\",\"time\":1612684518000}],\"WF\": [{\"value\": 3,\"time\":1612684518000}]}}
        {
          "id": 123, 
          "version": "1.0",
          "sys":{
              "ack":0
          }, 
          "method": "thing.event.property.batch.post", 
          "params": {
            "properties": {
                  "Power": [{
                        "value": "on", 
                        "time": 1612684518000
                    },
                  ], 
                  "WF": [{
                        "value": 3, 
                        "time": 1612684518000
                        },
                  ]
              }, 
        }
      • After properties are reported in a batch, IoT Platform sends a response message. To verify that IoT Platform received the message, subscribe to the topic /sys/a18wP******/LightSwitch/thing/event/property/batch/post_reply.

        Sample code:

            aiot_mqtt_sub(mqtt_handle, "/sys/a18wP******/LightSwitch/thing/event/property/batch/post_reply", NULL, 1,
                          NULL);

        Where:

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

        • LightSwitch is the DeviceName of the device.

  3. Define the functions for reporting properties.

    /* Report a single property */
    int32_t demo_send_property_post(void *dm_handle, char *params)
    {
        aiot_dm_msg_t msg;
        memset(&msg, 0, sizeof(aiot_dm_msg_t));
        msg.type = AIOT_DMMSG_PROPERTY_POST;
        msg.data.property_post.params = params;
        return aiot_dm_send(dm_handle, &msg);
    }
    /* Report properties in a batch */
    int32_t demo_send_property_batch_post(void *dm_handle, char *params)
    {
        aiot_dm_msg_t msg;
        memset(&msg, 0, sizeof(aiot_dm_msg_t));
        msg.type = AIOT_DMMSG_PROPERTY_BATCH_POST;
        msg.data.property_post.params = params;
        return aiot_dm_send(dm_handle, &msg);
    }

Custom data format

  1. Upload a TSL model data parsing script to IoT Platform.

    For more information, see Messages in a custom data format.

  2. Write the device code to report binary messages.

    Sample code:

        {
            aiot_dm_msg_t msg;
            uint8_t raw_data[] = {0x01, 0x02};
            memset(&msg, 0, sizeof(aiot_dm_msg_t));
            msg.type = AIOT_DMMSG_RAW_DATA;
            msg.data.raw_data.data = raw_data;
            msg.data.raw_data.data_len = sizeof(raw_data);
            aiot_dm_send(dm_handle, &msg);
        }
Note

When you use TSL models for communication, make sure that the data traffic does not exceed the threshold.

Step 4: Set properties

Call the IoT Platform cloud API SetDeviceProperty or SetDevicesProperty to send a property-setting command to a device. The device calls aiot_mqtt_recv to receive the command and processes it with the demo_dm_recv_handler callback function.

Use the following information to write the processing logic for the callback function:

  • Notes:

    • The received message is passed as an input parameter of type aiot_dm_recv_handler_t and is stored in a location that you specify using the aiot_dm_recv_t structure.

    • The field in the received message that contains the property-setting command content depends on the device data format, as shown in the following table.

      Device data format

      Message field

      Description

      ICA standard data format (Alink JSON)

      recv->data.property_set.params

      Matches the value of the params field in the Alink data format. For more information, see Property messages.

      passthrough/custom format

      recv->data.raw_data

      The device must parse the data. For more information, see Messages in passthrough/custom format.

  • Recommended processing logic:

    1. Update the device properties.

    2. Report the updated properties.

  • Processing logic in the sample code:

    The sample code only prints the received message. To see a demonstration of reporting the set properties, uncomment the code block under the TODO comment by removing the /* and */ symbols.

    • ICA standard data format (Alink JSON)

      static void demo_dm_recv_property_set(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
      {
          printf("demo_dm_recv_property_set msg_id = %ld, params = %.*s\r\n",
                  (unsigned long)recv->data.property_set.msg_id,
                  recv->data.property_set.params_len,
                  recv->data.property_set.params);
          /* TODO: The following code demonstrates how to respond to a property-setting command from IoT Platform. Uncomment it to see how it works. */
          /*
          {
              aiot_dm_msg_t msg;
              memset(&msg, 0, sizeof(aiot_dm_msg_t));
              msg.type = AIOT_DMMSG_PROPERTY_SET_REPLY;
              msg.data.property_set_reply.msg_id = recv->data.property_set.msg_id;
              msg.data.property_set_reply.code = 200;
              msg.data.property_set_reply.data = "{}";
              int32_t res = aiot_dm_send(dm_handle, &msg);
              if (res < 0) {
                  printf("aiot_dm_send failed\r\n");
              }
          }
          */
      }
    • passthrough/custom format

      static void demo_dm_recv_raw_data(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
      {
          printf("demo_dm_recv_raw_data raw data len = %d\r\n", recv->data.raw_data.data_len);
          /* TODO: The following code demonstrates how to send data in binary format. To use this feature, you must deploy the corresponding passthrough script in IoT Platform. */
          /*
          {
              aiot_dm_msg_t msg;
              uint8_t raw_data[] = {0x01, 0x02};
              memset(&msg, 0, sizeof(aiot_dm_msg_t));
              msg.type = AIOT_DMMSG_RAW_DATA;
              msg.data.raw_data.data = raw_data;
              msg.data.raw_data.data_len = sizeof(raw_data);
              aiot_dm_send(dm_handle, &msg);
          }
          */
      }

Step 5: Report events

After the device establishes an MQTT connection, call aiot_dm_send to send event messages to IoT Platform. The message is stored in an aiot_dm_msg_t struct and passed as a parameter to aiot_dm_send.

Write code to report events based on one of the following device data formats.

  1. Add a TSL model.

    • Before you report an event, log in to the IoT Platform console. On the Product details page, go to the Feature definition tab and add an event for the product. For more information, see Add a TSL model manually.

      The following table describes the TSL model event used in the sample code.

      Feature name

      Identifier

      Type

      Output parameter

      Error

      Error

      Fault

      • Parameter name: Error code

      • Parameter identifier: ErrorCode

      • Data type: enum

      • Enum items:

        • 0: Device hardware failure

        • 1: Device software failure

        • 2: Unknown error

    • You can also import the TSL file for this example. For instructions, see Add TSL models in batches.

  2. Set the content of the event message.

    • Sample code:

              demo_send_event_post(dm_handle, "Error", "{\"ErrorCode\": 0}");
    • Sample message description:

      Devices report TSL model messages in JSON format. The following table shows a sample message and its corresponding Alink data format. For more information, see Report device events.

      Note

      You only need to provide the device data, which is the value of the params parameter in the Alink data format. The Link SDK handles the encapsulation of reported messages.

      Sample payload

      Alink format

      {\"ErrorCode\": 0}
      {
        "id": "123",
        "version": "1.0",
        "params": {
              "ErrorCode": 0
        },
        "method": "thing.event.alarm.post"
      }
  3. Define a function to report event messages.

    
    int32_t demo_send_event_post(void *dm_handle, char *event_id, char *params)
    {
        aiot_dm_msg_t msg;
        memset(&msg, 0, sizeof(aiot_dm_msg_t));
        msg.type = AIOT_DMMSG_EVENT_POST;
        msg.data.event_post.event_id = event_id;
        msg.data.event_post.params = params;
        return aiot_dm_send(dm_handle, &msg);
    }

Step 6: Service invocation

You can call the cloud APIs of IoT Platform, InvokeThingService or InvokeThingsService, to send a service invocation command to a device. The device calls aiot_mqtt_recv to receive the command and performs the corresponding action based on the settings of the demo_dm_recv_handler callback function.

Use the following information to write the processing logic for the callback function:

  • Usage notes:

    • The received message is passed as an input parameter of type aiot_dm_recv_handler_t and is stored in a location that you specify using the aiot_dm_recv_t structure.

    • The content of the service invocation command varies based on the device data format. The following table lists the fields where the command content is stored.

      Device data format

      Synchronous field

      Asynchronous field

      Description

      ICA standard data format (Alink JSON)

      recv->data.sync_service_invoke

      recv->data.async_service_invoke

      The data in this field has the same format as the value of the params parameter in Alink data. For more information, see Service messages.

      passthrough/custom format

      recv->data.raw_data

      recv->data.raw_service_invoke

      You must upload a parsing script to parse the command content. For more information, see Messages in passthrough/custom format.

    • If you use a separate function to handle the response instead of sending it from the callback function, ensure the values of the following parameters in the response message match those in the request from IoT Platform.

      • Synchronous call: rrpc_id and msg_id

      • Asynchronous call: msg_id

    • When sending a service response to IoT Platform, be aware of the timeout period:

      • Synchronous call: After you receive a synchronous service message, you must send a response within 8 seconds. Otherwise, IoT Platform considers the invocation failed, even if the device received the message.

      • Asynchronous call: You can define custom timeout logic for asynchronous calls based on your business requirements. IoT Platform does not impose any limits.

  • Recommended processing logic:

    1. Execute the service command.

    2. Return a service response to IoT Platform.

  • Processing logic in the sample code:

    The sample code only prints the received message. To see a demonstration of reporting the set properties, uncomment the code block under the TODO comment by removing the /* and */ symbols.

    • ICA standard data format (Alink JSON)

      • Synchronous call:

        static void demo_dm_recv_sync_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
        {
            printf("demo_dm_recv_sync_service_invoke msg_id = %ld, rrpc_id = %s, service_id = %s, params = %.*s\r\n",
                    (unsigned long)recv->data.sync_service_invoke.msg_id,
                    recv->data.sync_service_invoke.rrpc_id,
                    recv->data.sync_service_invoke.service_id,
                    recv->data.sync_service_invoke.params_len,
                    recv->data.sync_service_invoke.params);
            /* TODO: The following code shows how to respond to a synchronous service invocation from IoT Platform.
                */
            {
                aiot_dm_msg_t msg;
                memset(&msg, 0, sizeof(aiot_dm_msg_t));
                msg.type = AIOT_DMMSG_SYNC_SERVICE_REPLY;
                msg.data.sync_service_reply.rrpc_id = recv->data.sync_service_invoke.rrpc_id;
                msg.data.sync_service_reply.msg_id = recv->data.sync_service_invoke.msg_id;
                msg.data.sync_service_reply.code = 200;
                msg.data.sync_service_reply.service_id = "SetLightSwitchTimer";
                msg.data.sync_service_reply.data = "{}";
                int32_t res = aiot_dm_send(dm_handle, &msg);
                if (res < 0) {
                    printf("aiot_dm_send failed\r\n");
                }
            }
        }
      • Asynchronous call:

        static void demo_dm_recv_async_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
        {
            printf("demo_dm_recv_async_service_invoke msg_id = %ld, service_id = %s, params = %.*s\r\n",
                    (unsigned long)recv->data.async_service_invoke.msg_id,
                    recv->data.async_service_invoke.service_id,
                    recv->data.async_service_invoke.params_len,
                    recv->data.async_service_invoke.params);
            /* TODO: The following code shows how to respond to an asynchronous service invocation from IoT Platform. You can uncomment it to see the effect.
                */
            /*
            {
                aiot_dm_msg_t msg;
                memset(&msg, 0, sizeof(aiot_dm_msg_t));
                msg.type = AIOT_DMMSG_ASYNC_SERVICE_REPLY;
                msg.data.async_service_reply.msg_id = recv->data.async_service_invoke.msg_id;
                msg.data.async_service_reply.code = 200;
                msg.data.async_service_reply.service_id = "ToggleLightSwitch";
                msg.data.async_service_reply.data = "{\"dataA\": 20}";
                int32_t res = aiot_dm_send(dm_handle, &msg);
                if (res < 0) {
                    printf("aiot_dm_send failed\r\n");
                }
            }
            */
        }
    • passthrough/custom format

      • The processing logic for asynchronous binary service invocation is the same as for binary property setting. For more information, see Custom topic message parsing.

      • The following sample code shows the processing logic for synchronous binary service invocation:

        static void demo_dm_recv_raw_sync_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
        {
            printf("demo_dm_recv_raw_sync_service_invoke raw sync service rrpc_id = %s, data_len = %d\r\n",
                    recv->data.raw_service_invoke.rrpc_id,
                    recv->data.raw_service_invoke.data_len);
            /* TODO: The following code shows how to respond to a synchronous service invocation from IoT Platform. You can uncomment it to see the effect. */
            /*
            {
                aiot_dm_msg_t msg;
                uint8_t raw_data[] = {0x01, 0x02};
                memset(&msg, 0, sizeof(aiot_dm_msg_t));
                msg.type = AIOT_DMMSG_RAW_SERVICE_REPLY;
                msg.data.raw_service_reply.rrpc_id = recv->data.raw_service_invoke.rrpc_id;
                msg.data.raw_data.data = raw_data;
                msg.data.raw_data.data_len = sizeof(raw_data);
                aiot_dm_send(dm_handle, &msg);
            }
            */
        }

Step 7: Disconnect

Note

MQTT is typically used for devices that require a persistent connection. Therefore, the program usually does not reach this point.

In the sample program, the main thread is responsible for configuring parameters and establishing a connection. After the connection is established, the main thread can enter hibernation.

You can call aiot_mqtt_disconnect to send a disconnection message to IoT Platform and disconnect from the network.

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

Step 8: Exit the program

Call aiot_dm_deinit to destroy the data-model client instance and release its resources.

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

Next steps

  • Compile the code to generate the executable file ./output/data-model-basic-demo.

    For more information, see Compile and run.

  • See the Runtime logs for details on the execution result.