All Products
Search
Document Center

IoT Platform:Example

Last Updated:Oct 07, 2023

This topic describes how to call the API operations of Link SDK for C to implement the Thing Specification Language (TSL) model feature for a device. In this example, a sample code file named ./demos/data_model_basic_demo.c is used.

Background information

  • For more information about the TSL model feature, see Example.

  • The device tag feature is available only for devices that are connected to IoT Platform over Message Queuing Telemetry Transport (MQTT). For more information about MQTT connection-related code when you configure the feature, see Overview.

Step 1: Initialize the SDK

  1. Add header files.

    ……
    ……
    
    #include "aiot_dm_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_dm_init operation to create a client instance named data-model and initialize the default parameters

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

Step 2: Configure required features

Call the aiot_dm_setopt operation to configure the following items:

  1. Associate with an MQTT connection handle.

    Important

    Before you configure device tag-specific parameters, make sure that device verification information is specified. For more information, see Example.

    • Sample code:

          aiot_dm_setopt(dm_handle, AIOT_DMOPT_MQTT_HANDLE, mqtt_handle);
    • Parameters:

      Parameter

      Example

      Description

      AIOT_DMOPT_MQTT_HANDLE

      mqtt_handle

      You must establish an MQTT connection. This parameter is used to associate with the MQTT connection handle.

  2. Configure a callback function to process TSL messages.

    1. Define the callback function.

      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) {
      
              /* Send a response from IoT Platform after the device submits properties, submits events, requests desired properties, or deletes desired properties. */
              case AIOT_DMRECV_GENERIC_REPLY: {
                  demo_dm_recv_generic_reply(dm_handle, recv, userdata);
              }
              break;
      
              /* Configure properties. */
              case AIOT_DMRECV_PROPERTY_SET: {
                  demo_dm_recv_property_set(dm_handle, recv, userdata);
              }
              break;
      
              /* Asynchronously call services */
              case AIOT_DMRECV_ASYNC_SERVICE_INVOKE: {
                  demo_dm_recv_async_service_invoke(dm_handle, recv, userdata);
              }
              break;
      
              /* Synchronously call services */
              case AIOT_DMRECV_SYNC_SERVICE_INVOKE: {
                  demo_dm_recv_sync_service_invoke(dm_handle, recv, userdata);
              }
              break;
      
              /* Send downstream binary data */
              case AIOT_DMRECV_RAW_DATA: {
                  demo_dm_recv_raw_data(dm_handle, recv, userdata);
              }
              break;
      
              /* When you synchronously call services to send binary data, the rrpc_id parameter is added to the binary data. */
              case AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE: {
                  demo_dm_recv_raw_sync_service_invoke(dm_handle, recv, userdata);
              }
              break;
      
              /* Send a response from IoT Platform after the device submits binary data. */
              case AIOT_DMRECV_RAW_DATA_REPLY: {
                  demo_dm_recv_raw_data_reply(dm_handle, recv, userdata);
              }
              break;
      
              default:
                  break;
          }
      }
    2. Configure a callback function to process TSL messages.

      • Sample code:

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

        Parameter

        Example

        Description

        AIOT_DMOPT_RECV_HANDLER

        demo_dm_recv_handler

        This function is called when a TSL message is received.

  3. Specify whether IoT Platform returns responses.

    You can use the following code to specify whether IoT Platform returns responses after receiving device messages:

    • Sample code:

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

      Parameter

      Example

      Description

      AIOT_DMOPT_POST_REPLY

      1

      Valid values:

      • 1: returns responses

      • 0: does not return responses

    • If the value of the AIOT_DMOPT_POST_REPLY parameter is 1, you must specify the logic to process response messages.

      You can specify the processing logic based on your business requirements. In this example, response messages are printed.

      • ICA Standard Data Format (Alink JSON)ICA标准数据格式

        Response messages are contained in the recv->data.generic_reply structure. The messages use 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);
        }
      • Custom data format透传/自定义格式

        Response messages are contained in the recv->data.raw_data structure. Before the messages are sent, 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: Submit device properties

After you connect the device to IoT Platform over MQTT, call the aiot_dm_send operation to submit device properties. The properties to be sent are stored in a specified location by using the aiot_dm_msg_t structure. This structure is an input parameter of the aiot_dm_send operation.

Write code to submit device properties based on the following device data formats:

ICA standard data format (Alink JSON)

  1. Add a TSL feature.

    • Log on to the IoT Platform console. On the Feature Definition tab of the Product Details page, add the properties to the default TSL module or a custom module. For example, you can create a custom module named demo_extra_block. For more information, see Add a TSL feature.

      The following table describes the TSL properties that are added in this example.

      TSL module

      Feature

      Identifier

      Data type

      Data definition

      Read/write type

      Default module

      Night light switch 1

      LightSwitch

      bool

      • 0: on

      • 1: off

      Read and write

      Power

      Power

      text

      Data length: 10,240 characters

      Read and write

      Operating current

      WF

      int32

      • Valid values: 0 to 10.

      • Step size: 1

      • Unit: A

      Read and write

      demo_extra_block

      Night light switch 2

      NightLightSwitch

      bool

      • 0: on

      • 1: off

      Read and write

    • In this example, you can also add the features by importing the TSL file. For more information, see Batch add TSL features.

  2. Specify the content of a property message.

    • Sample code:

      • Default module:

        /* Submit a property. */
                demo_send_property_post(dm_handle, "{\"LightSwitch\": 0}");
        /* Submit multiple properties at a time. */
                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 messages:

      • Submitted TSL messages are in the JSON format. The following table describes sample messages and the Alink formats of the messages. For more information, see Device properties, events, and services.

        Note

        You need to only focus on message content, which is indicated by the params parameter in the Alink data. The Link SDK encapsulates and processes the messages.

        Message type

        Example

        Alink format

        One property

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

        Multiple 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 multiple properties are submitted, IoT Platform sends a response to confirm that the message is received. To receive the response, you must subscribe to the /sys/a18wP******/LightSwitch/thing/event/property/batch/post_reply topic.

        Sample code:

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

        Description:

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

        • LightSwitch indicates the DeviceName of the device.

  3. Define a function to submit properties.

    /* Submit a 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);
    }
    
    /* Submit multiple properties at a time. */
    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 data parsing script to IoT Platform.

    For more information, see What is data parsing?

  2. Write code to submit binary device data.

    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 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 4: Configure device properties

You can call the SetDeviceProperty or SetDevicesProperty operation to send a property configuration command to the device. The device calls the aiot_mqtt_recv operation to receive the command and calls the demo_dm_recv_handler callback function to process the command.

When you specify the processing logic of the callback function, take note of the following items:

  • Usage notes:

    • The received message is used as an input parameter of the aiot_dm_recv_handler_t callback function. The message is stored in a specified location by using the aiot_dm_recv_t structure.

    • The command content in the received message varies based on the device data format. The following table describes the command fields.

      Device data format

      Field in the received message

      Description

      ICA standard data format (Alink JSON)

      recv->data.property_set.params

      This field is the same as the params parameter in the Alink data. For more information, see Device properties, events, and services.

      Custom data format

      recv->data.raw_data

      You must parse data on the device. For more information, see What is data parsing?

  • Recommended processing logic:

    1. Update device properties.

    2. Submit the updated properties.

  • The processing logic of the sample code:

    In this example, the received message is printed. To submit the updated properties, you can delete the comment symbols (/* and */) below TODO.

    • 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 sample code shows how to return a response after IoT Platform sends a command to configure properties. To run the sample code, uncomment the sample code. */
          /*
          {
              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");
              }
          }
          */
      }
    • Custom data 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 sample code shows how to send binary data. If you want to send binary data, you must configure a data parsing script in the IoT Platform console.  */
          /*
          {
              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: Submit device events

After you connect the device to IoT Platform over MQTT, call the aiot_dm_send operation to submit device properties. The events to be sent are stored in a specified location by using the aiot_dm_msg_t structure. This structure is an input parameter of the aiot_dm_send operation.

Write code to submit device properties based on the following device data formats:

  • If your device data is in a custom format, you can use the same API operation to submit events and properties. For more information, see Step 3.

  • If your device data is in the ICA standard data format (Alink JSON), perform the following steps:

  1. Add a TSL model.

    • Log on to the IoT Platform console. On the Feature Definition tab of the Product Details page, add the events to the product. For more information, see Add a TSL feature.

      The following table describes the TSL events that are added in this example.

      Feature

      Identifier

      Event type

      Output parameters

      Error

      Error

      Error

      • Parameter name: Error code

      • Parameter identifier: ErrorCode

      • Data type: enum

      • Enumeration items:

        • 0: An error occurred on the device hardware.

        • 1: An error occurred on the device software.

        • 2: An unknown error occurred.

    • In this example, you can also add the features by importing the TSL file. For more information, see Batch add TSL features.

  2. Specify the content of the event message.

    • Sample code:

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

      Submitted TSL messages are in the JSON format. The following table describes sample messages and the Alink formats of the messages. For more information, see Device properties, events, and services.

      Note

      You need to only focus on message content, which is indicated by the params parameter in the Alink data. The Link SDK encapsulates and processes the messages.

      Example

      Alink format

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

    
    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: Call device services

You can call the InvokeThingService or InvokeThingsService operation to send a service invocation command to the device. The device calls the aiot_mqtt_recv operation to receive the command and calls the demo_dm_recv_handler callback function to process the command.

When you specify the processing logic of the callback function, take note of the following items:

  • Usage notes:

    • The received message is used as an input parameter of the aiot_dm_recv_handler_t callback function. The message is stored in a specified location by using the aiot_dm_recv_t structure.

    • The command content in the received message varies based on the device data format. The following table describes the command fields.

      Device data format

      Field in a synchronous message

      Field in an asynchronous message

      Description

      ICA standard data format (Alink JSON)

      recv->data.sync_service_invoke

      recv->data.async_service_invoke

      This field is the same as the params parameter in the Alink data. For more information, see Device properties, events, and services.

      Custom data format

      recv->data.raw_data

      recv->data.raw_service_invoke

      You must upload a script to parse the command content. For more information, see What is data parsing?

    • If the defined callback function does not have logic to respond to service calls, you must configure a function to specify the logic. When you configure the function to send a response, make sure that the values of the following parameters in the response are the same as those in the request.

      • Synchronous call: rrpc_id and msg_id.

      • Asynchronous call: msg_id.

    • When you send a response to IoT Platform, take note of the timeout period.

      • Synchronous call: After the device receives the request, send a response within 8 seconds. Otherwise, IoT Platform determines that the call failed regardless of whether the device received the request.

      • Asynchronous call: You can specify a timeout period based on your business requirements. No limit is applied.

  • Recommended processing logic:

    1. Process the command.

    2. Return a response to IoT Platform.

  • The processing logic of the sample code:

    In this example, the received message is printed. To submit the updated properties, you can delete the comment symbols (/* and */) below TODO.

    • 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 sample code shows how to return a response after IoT Platform synchronously calls device services. 
                */
        
            
            {
                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 sample code shows how to return a response after IoT Platform asynchronously calls device services. To run the sample code, uncomment the sample code. 
                */
        
            /*
            {
                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");
                }
            }
            */
        }
    • Custom data format

      • The operation to asynchronously call services based on the binary data format works the same way the operation to configure properties based on the binary data format. For more information, see Parse messages that are sent to custom topics.

      • The following sample code shows how to synchronously call services based on the binary data format:

        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 example shows how to return a response after IoT Platform sends the command to synchronously call services. To run the sample code, uncomment the 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_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: Close the connection

Note

MQTT connections are applied to devices that remain persistently connected. You can manually disconnect devices from IoT Platform.

In this example, the main thread is used to set parameters and establish a connection. After the connection is established, you can put the main thread to sleep.

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

    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 the aiot_dm_deinit operation to delete the data-model client instance and release the corresponding resources.

    res = aiot_dm_deinit(&dm_handle);
    if (res < STATE_SUCCESS) {
        printf("aiot_dm_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/data-model-basic-demo executable file is generated.

    For more information, see Compilation and running.

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