Usage example
This topic uses a demo file from the C Link software development kit (SDK) to demonstrate how to call Link SDK APIs to connect a device to IoT Platform over Message Queuing Telemetry Transport (MQTT) and exchange messages.
Background information
For more information about MQTT connections, see MQTT connection overview.
The IoT Platform C Link SDK provides the ./mqtt_basic_demo.c demo file for non-cloud gateway devices and the ./mqtt_userdefine_demo.c demo file for cloud gateway devices.
Step 1: Initialization
Add header files.
#include "aiot_state_api.h" #include "aiot_sysdep_api.h" #include "aiot_mqtt_api.h"Configure underlying dependencies and log output.
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);Call aiot_mqtt_init to create an MQTT client instance and initialize default parameters.
mqtt_handle = aiot_mqtt_init(); if (mqtt_handle == NULL) { printf("aiot_mqtt_init failed\n"); return -1; }
Step 2: Configure features
Call aiot_mqtt_setopt to configure the following features.
For more configuration items, see aiot_mqtt_option_t.
Configure connection parameters.
Non-cloud gateway devices
Sample code:
/* TODO: Replace the following parameters with your device credentials. */ char *product_key = "a18wP******"; char *device_name = "LightSwitch"; char *device_secret = "uwMTmVAMnGGHaAkqmeDY6cHxxB******"; /* TODO: Replace the following parameter with the endpoint for your device. */ char *mqtt_host = "iot-06z00ax1o******.mqtt.iothub.aliyuncs.com"; ... ... /* The following parameters are optional. You can use the default values in the SDK file. */ /* Configure the MQTT server address. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)url); /* Configure the MQTT server port. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port); /* Configure the device ProductKey. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key); /* Configure the device DeviceName. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name); /* Configure the device DeviceSecret. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret); /* Configure the security credentials for the network connection. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);Related parameters:
Parameter
Description
mqtt_host
The endpoint for the device.
Enterprise instances and new public instances: On the Instance Details page, view the endpoint on the Development Configurations panel.
Legacy public instances: The endpoint format is
${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com.
For more information about new and legacy public instances, Enterprise instances, and endpoints, see View instance endpoints.
product_key
Device credentials. For more information, see Obtain device credentials.
This sample code uses one-key-per-device authentication.
device_name
device_secret
MQTT keepalive description:
ImportantThe device must send at least one message, such as a ping request, within the keepalive interval.
The heartbeat timer starts after IoT Platform responds to a connection request. The timer resets whenever the device sends a PUBLISH, SUBSCRIBE, PING, or PUBACK message. IoT Platform checks for a heartbeat every 30 seconds. The maximum timeout period is calculated as:
Keepalive interval × 1.5 + Waiting time for the check. The waiting time is the period from when the device comes online until the next scheduled check. If the device is silent for longer than the maximum timeout period, the server disconnects it.
The C Link SDK has a keepalive feature. You can set the following configuration items to customize the keepalive heartbeat for the device connection. If you do not configure them, the default values are used.
Configuration item
Default value
Description
AIOT_MQTTOPT_HEARTBEAT_MAX_LOST
2
The maximum number of consecutive heartbeat losses that can be tolerated. The device initiates a reconnection after this threshold is reached.
AIOT_MQTTOPT_HEARTBEAT_INTERVAL_MS
25,000
The interval between reconnection attempts. Unit: milliseconds. Valid values: 1,000 to 1,200,000.
AIOT_MQTTOPT_KEEPALIVE_SEC
1,200
The timeout period for heartbeat loss. After a heartbeat is lost, the device can attempt to reconnect within this period. Unit: seconds. Valid values: 30 to 1,200. Set this value to be greater than 300.
Cloud gateway devices
Sample code:
/* TODO: Replace the following parameters with your device information, endpoint (mqtt_host), port, and certificate (user_ca_cert). */ char *username = "LightSwitch"; char *password = "*******"; char *client_id = "client_*******"; char *mqtt_host = "iot-******.igw.iothub.aliyuncs.com"; char *product_key = "*******"; uint16_t port = 1883; const char *user_ca_cert = \ { "-----BEGIN CERTIFICATE-----\r\n" \ "MIIC4jCCAco*************************************MQswCQYDVQQGEwJD\r\n" \ "TjETMBEGA1U*************************************IENBMB4XDTIyMTIw\r\n" \ "MjE0NDk1Mlo*************************************Q04xEzARBgNVBAoM\r\n" \ "CkFsaXl1biB*************************************KoZIhvcNAQEBBQAD\r\n" \ "ggEPADCCAQo*************************************2VaEUrnXNoO40w71\r\n" \ "i3l4Alchs1M*************************************VVxRGEtybsIH8CYO\r\n" \ "kyzGgOKbx7M*************************************49l3opCIfg9LOwjF\r\n" \ "R6x+ZY6yGdv*************************************CDxW2mILl+VwYd9s\r\n" \ "2udrJ7riJ5i*************************************/P9s+2UaBX89TTUd\r\n" \ "lYWKe3tHRg+*************************************BgkqhkiG9w0BAQsF\r\n" \ "AAOCAQEAhr8*************************************odRVrUaVBBLguSAH\r\n" \ "OZmtwUy0ZUf*************************************aJ27G4prMD2DMoby\r\n" \ "uTbXKOPYCvT*************************************5smSmfXIrBrbGrG6\r\n" \ "0CL1Y8DTNlF*************************************TjixfpGS3C/Ogu0H\r\n" \ "uMMbA4RCFhF*************************************X0VprxMrDarUJAa1\r\n" \ "tJ************************Iw==\r\n" \ "-----END CERTIFICATE-----\r\n" \ }; ... ... /* Configure the MQTT server address. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host); /* Configure the MQTT server port. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port); /* Configure the device username. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_USERNAME, (void *)username); /* Configure the device password. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PASSWORD, (void *)password); /* Configure the device client_id. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_CLIENTID, (void *)client_id); /* Configure the device productKey. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key); /* Configure the device deviceName. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)username); /* Configure the security credentials for the network connection. The credentials have been created. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred); /* Configure the default MQTT message callback function. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler); /* Configure the MQTT event callback function. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler); /* Disable the check for topics that start with a forward slash (/). */ uint8_t topic_check = 0; aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_TOPIC_HEADER_CHECK, (void *)&topic_check);Related parameters:
Parameter
Description
product_key
The ProductKey of the cloud gateway product to which the device belongs.
username
Device credentials. For more information, see Obtain cloud gateway device credentials.
password
client_id
The client ID. You must customize this ID. It can be up to 64 characters in length. Use the device's MAC address or serial number (SN) to easily identify different clients.
mqtt_host
The endpoint and port number (default: 1883) for connecting the MQTT cloud gateway device. For information about how to obtain them, see Create a cloud gateway product (MQTT).
port
user_ca_cert
The content of the device root certificate
root-ca.crt.Disable the check for communication topics that start with
/(AIOT_MQTTOPT_TOPIC_HEADER_CHECK):/* Disable the check for topics that start with a forward slash (/). */ uint8_t topic_check = 0; aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_TOPIC_HEADER_CHECK, (void *)&topic_check);IoT Platform and cloud gateway devices use MQTT topics for communication. The communication topics must comply with standard MQTT topic specifications but are not required to start with
/.For more information about communication for MQTT cloud gateway devices, see Message communication.
Configure status monitoring and message callbacks.
You can configure the status monitoring callback function.
Sample code:
int main(int argc, char *argv[]) { ... ... /* Configure the default MQTT message callback function. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler); /* Configure the MQTT event callback function. */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler); ... ... }Related parameters:
Configuration item
Sample value
Description
AIOT_MQTTOPT_RECV_HANDLER
demo_mqtt_default_recv_handler
When a message is received, the corresponding processing logic defined in this callback function is executed.
AIOT_MQTTOPT_EVENT_HANDLER
demo_mqtt_event_handler
When the device connection status changes, the corresponding processing logic defined in this callback function is executed.
Define the status monitoring callback function.
ImportantDo not define time-consuming event handling logic because it can block the message receiving thread.
Connection status can change due to events such as network exceptions, successful automatic reconnections, and disconnections.
You can modify the code at the
TODOmarker to handle connection status changes.
/* This is an MQTT event callback function. This function is triggered when the device connects to, reconnects to, or disconnects from the network. For event definitions, see core/aiot_mqtt_api.h. */ void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata) { switch (event->type) { /* The aiot_mqtt_connect() function is called to establish a connection with the MQTT server. */ case AIOT_MQTTEVT_CONNECT: { printf("AIOT_MQTTEVT_CONNECT\n"); /* TODO: Handle the successful connection establishment of the SDK. Do not call long-running blocking functions here. */ } break; /* The SDK is passively disconnected due to network issues and then automatically and successfully reconnects. */ case AIOT_MQTTEVT_RECONNECT: { printf("AIOT_MQTTEVT_RECONNECT\n"); /* TODO: Handle the successful reconnection of the SDK. Do not call long-running blocking functions here. */ } break; /* The SDK is passively disconnected due to network issues. This can be caused by a read/write failure at the network layer or a failure to receive a heartbeat response from the server as expected. */ 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: Handle the passive disconnection of the SDK. Do not call long-running blocking functions here. */ } break; default: { } } }Define the message-receiving callback function.
ImportantAvoid defining time-consuming event handling logic to prevent blocking the message-receiving thread.
To handle received messages, modify the code at the
TODOmarker.
/* This is the default MQTT message callback function. This function is called when the SDK receives an MQTT message from the server and you have not configured a specific callback for it. */ 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: Handle the server's response to the heartbeat. This is generally not required. */ } 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: Handle the server's response to the subscription request. This is generally not required. */ } 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); /* TODO: Handle the service message sent from the server. */ } break; case AIOT_MQTTRECV_PUB_ACK: { printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id); /* TODO: Handle the server's response to a reported message with QoS=1. This is generally not required. */ } break; default: { } } }
Step 3: Request a connection
Call aiot_mqtt_connect to send a connection authentication request to IoT Platform using the parameters that you configured for the connection.
/* Establish an MQTT connection with the server. */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* If the connection attempt fails, destroy the MQTT instance to release resources. */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n", -res);
printf("please check variables like mqtt_host, product_key, device_name, device_secret in demo\r\n");
return -1;
}Step 4: Start the keepalive thread
Call the aiot_mqtt_process function to send heartbeat messages to the server. This function maintains a persistent connection for the device and resends unacknowledged messages that have a Quality of Service (QoS) level of 1.
Start the keepalive thread.
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; }Set the keepalive thread handler 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: Start the receiving thread
Call aiot_mqtt_recv to receive MQTT messages from the server. The message callback function processes these messages. If the device is disconnected, it automatically reconnects and triggers the event callback function.
Start the receiving thread.
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; }Set the receiving thread handler 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 operation to subscribe to a specific topic.
Sample code:
{ char *sub_topic = "/a18wP******/LightSwitch/user/get"; res = aiot_mqtt_sub(mqtt_handle, sub_topic, NULL, 1, NULL); if (res < 0) { printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res); return -1; } }NoteAfter you complete the configuration, delete the comment symbols from the relevant code.
Related parameters:
Parameter
Example
Description
sub_topic
/a18wP******/LightSwitch/user/get
A topic that the device has permission to subscribe to.
a18wP******is the ProductKey of the device.LightSwitchis the DeviceName of the device.
This example uses a default custom topic.
The device receives messages from IoT Platform through this topic.
For more information about topics, see What is a topic?.
Step 7: Send a message
Call aiot_mqtt_pub to send a message to a specific topic.
Sample code:
{ char *pub_topic = "/a18wP******/LightSwitch/user/update"; char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}"; res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0); if (res < 0) { printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res); return -1; } }NoteAfter you complete the configuration, delete the comment symbols around the relevant code.
Related parameters:
Parameter
Example
Description
pub_topic
/a18wP******/LightSwitch/user/update
A topic that the device has permission to publish to.
a18wP******is the ProductKey of the device.LightSwitchis the DeviceName of the device.
The device sends messages to IoT Platform through this topic.
For more information about topics, see What is a topic?.
pub_payload
{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}
The content of the message reported to IoT Platform.
Because the topic category for the sample message is custom, the data format can also be custom.
For more information about data formats, see Data formats.
After the device establishes MQTT communication with IoT Platform, ensure that the communication volume does not exceed the threshold.
For more information about communication limits, see Limits.
If the communication volume exceeds the threshold, log on to the IoT Platform console to view backlogged messages. For more information, see View and monitor consumer groups.
Step 8: Disconnect
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 9: Exit the program
You can call aiot_mqtt_deinit to destroy the MQTT client instance and release 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 file, compile it to generate an executable file:
Non-cloud gateway devices:
./output/mqtt-basic-demo.Cloud gateway devices:
./output/mqtt-userdefine-demo.For more information, see Compile and run.
For a detailed description of the results, see Operational logs.