Quick start
This topic describes how to send and receive messages on an Ubuntu host using MQTT topics or the Thing Specification Language (TSL) model. The Ubuntu host acts as an Internet of Things (IoT) device to demonstrate how a device connects to Alibaba Cloud IoT Platform. This guide uses Link SDK 3.0.1.
Install the local development environment
You can develop on an Ubuntu system.
Install Ubuntu 16.04
This guide is based on a 64-bit host that runs
Ubuntu 16.04. The guide has not been tested on other Linux distributions. To avoid compatibility issues, we recommend that you install the same distribution.You can install the 64-bit desktop version of
Ubuntu 16.04.x LTS. You can download it from http://releases.ubuntu.com/16.04.If you use the
Windowsoperating system, you can installVirtualboxto obtain a Linux development environment. You can download it from https://www.virtualbox.org/wiki/Downloads.Install required software
This SDK requires the following tools for development and compilation:
make-4.1,git-2.7.4,gcc-5.4.0,gcov-5.4.0,lcov-1.12,bash-4.3.48,tar-1.28, andmingw-5.3.1.Run the following command to install the required software:
$ sudo apt-get install -y build-essential make git gcc
Connect a device by programming with MQTT topics
Create a product and a device
Log on to the Alibaba Cloud IoT Platform console with your Alibaba Cloud account to create a product. Because you will implement product features directly using MQTT topics, select the Basic Edition when you create the product.
After you create the product, add a device. IoT Platform then generates identity information for the device.
If you are not familiar with how to create products in the cloud, see Create a product and a device.
Implement product features
Understand the SDK root directory structure
After you obtain the Linkkit SDK, the root directory has the following structure:
$ ls certs config.bat external_libs extract.bat extract.sh LICENSE makefile make.settings model.json README.md src tools wrappersConfigure the SDK
By default, the TSL model option is enabled in the SDK. This demo uses the Basic Edition. Therefore, you must first disable the TSL model option.
$ make menuconfigAdd the device credentials to the sample code
You can implement the Hardware Abstraction Layer (HAL) functions that are defined in the SDK to obtain the device identity. This topic uses Ubuntu to impersonate an IoT device. In SDK version 3.0.1, open the
wrappers/os/ubuntu/HAL_OS_linux.cfile. In versions 3.1.0 and 3.2.0, open thesrc/mqtt/examples/mqtt_example.cfile. Edit the following code snippet and add the device identity information that you obtained from IoT Platform:ProductKey: The unique identifier for the product.
ProductSecret: The product secret.
DeviceName: The unique identifier for the device.
DeviceSecret: The device secret.
#ifdef DYNAMIC_REGISTER ... ... #else #ifdef DEVICE_MODEL_ENABLED ... ... #else char _product_key[IOTX_PRODUCT_KEY_LEN + 1] = "xxxx"; /* Replace with your actual product key. */ char _product_secret[IOTX_PRODUCT_SECRET_LEN + 1] = "yyyy"; /* Replace with your actual product secret. */ char _device_name[IOTX_DEVICE_NAME_LEN + 1] = "zzzz"; /* Replace with your actual device name. */ char _device_secret[IOTX_DEVICE_SECRET_LEN + 1] = "ssss"; /* Replace with your device secret. */ #endif #endifNoteIn the IoT Platform console, you must set the permissions for the
/${productKey}/${deviceName}/gettopic to `Subscribe and Publish`. The following code uses this topic.Initialize and establish a connection
The following code snippet is from the
src/mqtt/examples/mqtt_example.cMQTT connection sample. It shows the device initialization and connection process.Customize MQTT parameters.
iotx_mqtt_param_t mqtt_params; memset(&mqtt_params, 0x0, sizeof(mqtt_params)); /* mqtt_params.request_timeout_ms = 2000; */ /* mqtt_params.clean_session = 0; */ /* mqtt_params.keepalive_interval_ms = 60000; */ /* mqtt_params.write_buf_size = 1024; */ /* mqtt_params.read_buf_size = 1024; */ mqtt_params.handle_event.h_fp = example_event_handle;NoteThe commented-out lines in the code show the default values for the MQTT configuration. You do not need to assign these values because the SDK automatically fills them in. If you want to change the default connection parameters, you can uncomment the relevant lines and enter new values.
Establish an MQTT connection with the server.
pclient = IOT_MQTT_Construct(&mqtt_params); if (NULL == pclient) { EXAMPLE_TRACE("MQTT construct failed"); return -1; }NotePass the connection parameter struct to the
IOT_MQTT_Construct()function. This initiates the MQTT connection. The function returns a handle if the connection succeeds, or null if it fails.Report data to the cloud
The sample file defines the following topic.
/${productKey}/${deviceName}/getThe following code snippet shows how to send data to this topic.
int example_publish(void *handle) { int res = 0; const char *fmt = "/%s/%s/get"; char *topic = NULL; int topic_len = 0; char *payload = "{\"message\":\"hello!\"}"; topic_len = strlen(fmt) + strlen(DEMO_PRODUCT_KEY) + strlen(DEMO_DEVICE_NAME) + 1; topic = HAL_Malloc(topic_len); if (topic == NULL) { EXAMPLE_TRACE("memory not enough"); return -1; } memset(topic, 0, topic_len); HAL_Snprintf(topic, topic_len, fmt, DEMO_PRODUCT_KEY, DEMO_DEVICE_NAME); res = IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, strlen(payload));NoteFor the first parameter of
IOT_MQTT_Publish_Simple(), you can use the handle that is returned by the previous call toIOT_MQTT_Construct(). You can also use 0. A value of 0 indicates that the SDK uses the current, single, and established MQTT connection to send the message.Subscribe to and process data from the cloud
NoteTo keep the publish/subscribe demo simple, the sample code subscribes to the
/${productKey}/${deviceName}/gettopic. This means that data sent from the device to IoT Platform is then sent back to the device by IoT Platform.The following code subscribes to the specified topic and defines a handler function for the received data.
res = example_subscribe(pclient); if (res < 0) { IOT_MQTT_Destroy(&pclient); return -1; } ... ... int example_subscribe(void *handle) { ... res = IOT_MQTT_Subscribe(handle, topic, IOTX_MQTT_QOS0, example_message_arrive, NULL); ...NoteFor the first parameter of
IOT_MQTT_Subscribe(), you can use the handle that is returned by the previous call toIOT_MQTT_Construct(). You can also use 0. A value of 0 indicates that the SDK uses the current, single, and established MQTT connection to subscribe to the topic.In the sample program, when a message is received from the cloud, the callback function only prints the message.
void example_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg) { iotx_mqtt_topic_info_t *topic_info = (iotx_mqtt_topic_info_pt) msg->msg; switch (msg->event_type) { case IOTX_MQTT_EVENT_PUBLISH_RECEIVED: /* Print the topic name and topic message. */ EXAMPLE_TRACE("Message Arrived:"); EXAMPLE_TRACE("Topic : %.*s", topic_info->topic_len, topic_info->ptopic); EXAMPLE_TRACE("Payload: %.*s", topic_info->payload_len, topic_info->payload); EXAMPLE_TRACE("\n"); break; default: break; } }The sample code periodically sends data to this topic. When you implement your own product logic, you do not need to periodically send data. You can send data only when you need to report it.
while (1) { if (0 == loop_cnt % 20) { example_publish(pclient); } IOT_MQTT_Yield(pclient, 200); loop_cnt += 1; }Compile the sample program
Run the following commands in the SDK root directory:
make distclean makeNote: Each time you run the `make` command in the root directory, code is automatically generated in the `output/` folder. If you have modified the code in the `output/` folder, you must back it up first.
After the compilation is successful, the sample program is generated in the
output/release/bindirectory:$ tree output/release output/release/ +-- bin ... ... | +-- mqtt-example ... ...
Monitor the data
Run the following command:
$ ./output/release/bin/mqtt-exampleIn the IoT Platform console, you can find your product and view the messages that are reported by the device in Simple Log Service. For more information, see View device data.
In the Linux console, you can also see the data from the cloud that is printed by the sample program:
example_message_arrive|031 :: Message Arrived: example_message_arrive|032 :: Topic : /a1MZxO*****/test_01/get example_message_arrive|033 :: Payload: {"message":"hello!"} example_message_arrive|034 ::
Connect a device by programming with the TSL model
Create a product and a device
You can create products in Alibaba Cloud IoT Platform and in the various industry services that it supports.
The TSL model for this sample product is in the
model_for_examples.JSONfile, located in the./src/dev_model/examples/directory. To simplify the setup, you can import this model. First, create your product in the IoT Platform console. Then, in the `model_for_examples.JSON` file, replace theproductkeyplaceholder with your product'sproductKey. In the console, go to the Product Details page and select the Feature Definition tab. Click Import TSL Model and select the JSON file. Your product will then have all the TSL model definitions from the sample.Implement product features
Add the device identity information to the sample code.
The device identity information is returned to the SDK through HAL calls. Because this guide is based on Linux, the relevant HAL implementation is in the
wrappers/os/ubuntu/HAL_OS_linux.cfile. You must replace the device identity information in the file with the information for your device.#ifdef DEVICE_MODEL_ENABLED char _product_key[IOTX_PRODUCT_KEY_LEN + 1] = "a1RIsMLz2BJ"; char _product_secret[IOTX_PRODUCT_SECRET_LEN + 1] = "fSAF0hle6xL0oRWd"; char _device_name[IOTX_DEVICE_NAME_LEN + 1] = "example1"; char _device_secret[IOTX_DEVICE_SECRET_LEN + 1] = "RDXf67itLqZCwdMCRrw0N5FHbv5D7jrE";NoteInstead of modifying these global variables, you can directly modify functions, such as
HAL_GetProductKey(), to return the device identity information.Compile and run the program.
Run the following commands in the SDK root directory.
$ make distclean $ makeAfter the compilation is successful, the Premium Edition sample program, named
linkkit-example-solo, is generated in theoutput/release/bindirectory.Run the following command in the SDK root directory.
$ ./output/release/bin/linkkit-example-solo
Monitor the data
The sample program periodically reports the value of the
Counterproperty to the cloud. You can view the received property in the cloud. You can also configure this property as read/write, set it in the cloud, and then check theCountervalue that is reported from the device again.Property reporting
The sample uses
__user_post_property__as an example of how to report a property. This sample loops through and reports various payload scenarios. You can observe the messages that are returned when an incorrect payload is reported.The following snippet shows the code for reporting a property.
/* Post Property Example */ if (time_now_sec % 11 == 0 && user_master_dev_available()) { user_post_property(); }Observe the sample function for property reporting.
void user_post_property(void) { static int example_index = 0; int res = 0; user_example_ctx_t *user_example_ctx = user_example_get_ctx(); char *property_payload = "NULL"; if (example_index == 0) {Normal property reporting scenario.
void user_post_property(void) { static int cnt = 0; int res = 0; char property_payload[30] = {0}; HAL_Snprintf(property_payload, sizeof(property_payload), "{\"Counter\": %d}", cnt++); res = IOT_Linkkit_Report(EXAMPLE_MASTER_DEVID, ITM_MSG_POST_PROPERTY, (unsigned char *)property_payload, strlen(property_payload)); EXAMPLE_TRACE("Post Property Message ID: %d", res); }The following log shows a normal property report.
[inf] dm_msg_request(205): DM Send Message, URI: /sys/a1X2bEnP82z/test_06/thing/event/property/post, Payload: {"id":"2","version":"1.0","params":{"LightSwitch":1},"method":"thing.event.property.post"} [inf] MQTTPublish(2546): Upstream Topic: '/sys/a1X2bEnP82z/test_06/thing/event/property/post'This is the message sent to the cloud.
> { > "id": "2", > "version": "1.0", > "params": { > "Counter": 1 > }, > "method": "thing.event.property.post" > }This is the acknowledgement received from the cloud.
< { < "code": 200, < "data": { < }, < "id": "1", < "message": "success", < "method": "thing.event.property.post", < "version": "1.0" < }This is the log from the user callback function.
user_report_reply_event_handler.314: Message Post Reply Received, Devid: 0, Message ID: 2, Code: 200, Reply: {}Property setting
When a property set request is received, the following callback function is entered.
static int user_property_set_event_handler(const int devid, const char *request, const int request_len) { int res = 0; user_example_ctx_t *user_example_ctx = user_example_get_ctx(); EXAMPLE_TRACE("Property Set Received, Devid: %d, Request: %s", devid, request);Send the result of the property setting back to the cloud to update the device property in the cloud.
res = IOT_Linkkit_Report(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY, (unsigned char *)request, request_len); EXAMPLE_TRACE("Post Property Message ID: %d", res); return 0; }The log shows the property setting message from the server-side.
[dbg] iotx_mc_cycle(1774): PUBLISH [inf] iotx_mc_handle_recv_PUBLISH(1549): Downstream Topic: '/sys/a1csED27mp7/AdvExample1/thing/service/property/set' [inf] iotx_mc_handle_recv_PUBLISH(1550): Downstream Payload:This is the content of the property setting message received from the cloud.
< { < "method": "thing.service.property.set", < "id": "161430786", < "params": { < "LightSwitch": 1 < }, < "version": "1.0.0" < }This is the acknowledgement message sent back to the cloud.
> { > "id": "161430786", > "code": 200, > "data": { > } > } [inf] dm_client_publish(106): Publish Result: 0 [inf] _iotx_linkkit_event_callback(219): Receive Message Type: 15 [inf] _iotx_linkkit_event_callback(221): Receive Message: {"devid":0,"payload":{"LightSwitch":1}} [dbg] _iotx_linkkit_event_callback(339): Current Devid: 0 [dbg] _iotx_linkkit_event_callback(340): Current Payload: {"LightSwitch":1}This is the log from the
user_property_set_event_handler()sample callback function when a property setting is received.user_property_set_event_handler.160: Property Set Received, Devid: 0, Request: {"LightSwitch":1}A command to set a property from the server has now reached and been executed on the device.
This is the final acknowledgement received for the property report.
< { < "code": 200, < "data": { < }, < "id": "2", < "message": "success", < "method": "thing.event.property.post", < "version": "1.0" < } [dbg] iotx_mc_handle_recv_PUBLISH(1555): Packet Ident : 00000000 [dbg] iotx_mc_handle_recv_PUBLISH(1556): Topic Length : 60 [dbg] iotx_mc_handle_recv_PUBLISH(1560): Topic Name : /sys/a1csED27mp7/AdvExample1/thing/event/property/post_reply [dbg] iotx_mc_handle_recv_PUBLISH(1563): Payload Len/Room : 104 / 4935 [dbg] iotx_mc_handle_recv_PUBLISH(1564): Receive Buflen : 5000 [dbg] iotx_mc_handle_recv_PUBLISH(1575): delivering msg ... [dbg] iotx_mc_deliver_message(1291): topic be matched [inf] dm_msg_proc_thing_event_post_reply(258): Event Id: property [dbg] dm_msg_response_parse(167): Current Request Message ID: 2 [dbg] dm_msg_response_parse(168): Current Request Message Code: 200 [dbg] dm_msg_response_parse(169): Current Request Message Data: {} [dbg] dm_msg_response_parse(174): Current Request Message Desc: success [dbg] dm_ipc_msg_insert(87): dm msg list size: 0, max size: 50 [dbg] dm_msg_cache_remove(142): Remove Message ID: 2 [inf] _iotx_linkkit_event_callback(219): Receive Message Type: 30 [inf] _iotx_linkkit_event_callback(221): Receive Message: {"id":2,"code":200,"devid":0,"payload":{}} [dbg] _iotx_linkkit_event_callback(476): Current Id: 2 [dbg] _iotx_linkkit_event_callback(477): Current Code: 200 [dbg] _iotx_linkkit_event_callback(478): Current Devid: 0 user_report_reply_event_handler.300: Message Post Reply Received, Devid: 0, Message ID: 2, Code: 200, Reply: {}NoteWhen a real product receives a property setting, it must parse the property and process it accordingly, instead of just sending the value back to the cloud.
Event reporting
The sample uses
IOT_Linkkit_TriggerEventto report an event. This sample loops through and reports various payload scenarios. You can observe the messages that are returned when an incorrect payload is reported.Normal event reporting scenario.
void user_post_event(void) { int res = 0; char *event_id = "HardwareError"; char *event_payload = "{\"ErrorCode\": 0}"; res = IOT_Linkkit_TriggerEvent(EXAMPLE_MASTER_DEVID, event_id, strlen(event_id), event_payload, strlen(event_payload)); EXAMPLE_TRACE("Post Event Message ID: %d", res); }In the sample program, the
Errorevent is reported approximately every 10 seconds and cycles through the described scenarios. The following log shows a normal report.[inf] dm_msg_request(218): DM Send Message, URI: /sys/a1csED27mp7/AdvExample1/thing/event/HardwareError/post, Payload: {"id":"1","version":"1.0","params":{"ErrorCode":0},"method":"thing.event.HardwareError.post"} [dbg] MQTTPublish(319): ALLOC: (136) / [200] @ 0x1195150 [inf] MQTTPublish(378): Upstream Topic: '/sys/a1csED27mp7/AdvExample1/thing/event/HardwareError/post' [inf] MQTTPublish(379): Upstream Payload:This is the content and log of the event message reported to the cloud.
> { > "id": "1", > "version": "1.0", > "params": { > "ErrorCode": 0 > }, > "method": "thing.event.HardwareError.post" > } [inf] dm_client_publish(106): Publish Result: 0 [dbg] alcs_observe_notify(105): payload:{"id":"1","version":"1.0","params":{"ErrorCode":0},"method":"thing.event.Error.post"} [inf] dm_server_send(76): Send Observe Notify Result 0 [dbg] dm_msg_cache_insert(79): dmc list size: 0 user_post_event.470: Post Event Message ID: 1 [dbg] iotx_mc_cycle(1774): PUBLISH [inf] iotx_mc_handle_recv_PUBLISH(1549): Downstream Topic: '/sys/a1csED27mp7/AdvExample1/thing/event/HardwareError/post_reply' [inf] iotx_mc_handle_recv_PUBLISH(1550): Downstream Payload:This is the content and log of the acknowledgement message received from the cloud.
< { < "code": 200, < "data": { < }, < "id": "1", < "message": "success", < "method": "thing.event.HardwareError.post", < "version": "1.0" < } [dbg] iotx_mc_handle_recv_PUBLISH(1555): Packet Ident : 00000000 [dbg] iotx_mc_handle_recv_PUBLISH(1556): Topic Length : 57 [dbg] iotx_mc_handle_recv_PUBLISH(1560): Topic Name : /sys/a1csED27mp7/AdvExample1/thing/event/Error/post_reply [dbg] iotx_mc_handle_recv_PUBLISH(1563): Payload Len/Room : 101 / 4938 [dbg] iotx_mc_handle_recv_PUBLISH(1564): Receive Buflen : 5000 [dbg] iotx_mc_handle_recv_PUBLISH(1575): delivering msg ... [dbg] iotx_mc_deliver_message(1291): topic be matched [inf] dm_msg_proc_thing_event_post_reply(258): Event Id: Error [dbg] dm_msg_response_parse(167): Current Request Message ID: 1 [dbg] dm_msg_response_parse(168): Current Request Message Code: 200 [dbg] dm_msg_response_parse(169): Current Request Message Data: {} [dbg] dm_msg_response_parse(174): Current Request Message Desc: success [dbg] dm_ipc_msg_insert(87): dm msg list size: 0, max size: 50 [dbg] dm_msg_cache_remove(142): Remove Message ID: 1 [inf] _iotx_linkkit_event_callback(219): Receive Message Type: 31 [inf] _iotx_linkkit_event_callback(221): Receive Message: {"id":1,"code":200,"devid":0,"eventid":"Error","payload":"success"} [dbg] _iotx_linkkit_event_callback(513): Current Id: 1 [dbg] _iotx_linkkit_event_callback(514): Current Code: 200 [dbg] _iotx_linkkit_event_callback(515): Current Devid: 0 [dbg] _iotx_linkkit_event_callback(516): Current EventID: Error [dbg] _iotx_linkkit_event_callback(517): Current Message: successThis is the log from the user callback function
user_trigger_event_reply_event_handler().user_trigger_event_reply_event_handler.310: Trigger Event Reply Received, Devid: 0, Message ID: 1, Code: 200, EventID: Error, Message: successService invocation
Register the handler function for service messages.
IOT_RegisterCallback(ITE_SERVICE_REQUEST, user_service_request_event_handler);When a service request message is received, the following callback function is entered. The device-side demo shows a simple addition service. The input parameters are
NumberAandNumberB, and the response parameter isResult. The sample usescJSONto parse the property values.static int user_service_request_event_handler(const int devid, const char *serviceid, const int serviceid_len, const char *request, const int request_len, char **response, int *response_len) { int add_result = 0; cJSON *root = NULL, *item_number_a = NULL, *item_number_b = NULL; const char *response_fmt = "{\"Result\": %d}"; EXAMPLE_TRACE("Service Request Received, Service ID: %.*s, Payload: %s", serviceid_len, serviceid, request); /* Parse Root */ root = cJSON_Parse(request); if (root == NULL || !cJSON_IsObject(root)) { EXAMPLE_TRACE("JSON Parse Error"); return -1; } if (strlen("Operation_Service") == serviceid_len && memcmp("Operation_Service", serviceid, serviceid_len) == 0) { /* Parse NumberA */ item_number_a = cJSON_GetObjectItem(root, "NumberA"); if (item_number_a == NULL || !cJSON_IsNumber(item_number_a)) { cJSON_Delete(root); return -1; } EXAMPLE_TRACE("NumberA = %d", item_number_a->valueint); /* Parse NumberB */ item_number_b = cJSON_GetObjectItem(root, "NumberB"); if (item_number_b == NULL || !cJSON_IsNumber(item_number_b)) { cJSON_Delete(root); return -1; } EXAMPLE_TRACE("NumberB = %d", item_number_b->valueint); add_result = item_number_a->valueint + item_number_b->valueint; /* Send Service Response To Cloud */ *response_len = strlen(response_fmt) + 10 + 1; *response = (char *)HAL_Malloc(*response_len); if (*response == NULL) { EXAMPLE_TRACE("Memory Not Enough"); return -1; } memset(*response, 0, *response_len); HAL_Snprintf(*response, *response_len, response_fmt, add_result); *response_len = strlen(*response); } cJSON_Delete(root); return 0; }At this point, you can view the following logs on the device.
This is the service invocation received from the cloud. The input parameters are
NumberA(value: 1) andNumberB(value: 2).< { < "method": "thing.service.Operation_Service", < "id": "280532170", < "params": { < "NumberB": 2, < "NumberA": 1 < }, < "version": "1.0.0" < }In the callback function, the values of
NumberAandNumberBare added. The sum is assigned toResultand reported to the cloud.> { > "id": "280532170", > "code": 200, > "data": { > "Result": 3 > } > }This concludes the explanation of services, properties, and events in the Premium Edition single-product sample.