本文以C Link SDK中的Demo檔案./demos/mqtt_v5_basic_demo.c
為例,介紹如何調用C Link SDK的API,將MQTT 5.0協議的裝置接入物聯網平台並進行訊息收發。
背景資訊
MQTT 5.0接入的更多資訊,請參見概述。
步驟一:初始化
添加標頭檔。
#include "aiot_state_api.h" #include "aiot_sysdep_api.h" #include "aiot_mqtt_api.h"
配置底層依賴和日誌輸出。
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);
調用aiot_mqtt_init,建立MQTT用戶端執行個體,並初始化預設參數。
mqtt_handle = aiot_mqtt_init(); if (mqtt_handle == NULL) { printf("aiot_mqtt_init failed\n"); return -1; }
步驟二:配置功能
調用aiot_mqtt_setopt,配置以下功能。更多功能的配置項,請參見aiot_mqtt_option_t。
配置串連參數。
範例程式碼:
char *product_key = "a18wP******"; char *device_name = "LightSwitch"; char *device_secret = "uwMTmVAMnGGHaAkqmeDY6cHxxB******"; char *mqtt_host = "iot-06z00ax1o******.mqtt.iothub.aliyuncs.com"; ... ... /* 配置MQTT協議的版本 */ protocol_version = AIOT_MQTT_VERSION_5_0; aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_VERSION, (void *)&protocol_version); /* 配置MQTT伺服器位址。 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)url); /* 配置MQTT伺服器連接埠。 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port); /* 配置裝置ProductKey。 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key); /* 配置裝置DeviceName。 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name); /* 配置裝置DeviceSecret。 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret); /* 配置網路連接的安全憑據。 */ aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred); /* 如果要使用MQTT 5.0的assigned clientId功能, 需要將use_assigned_clientid置為1 */ uint8_t use_assigned_clientid = 0; aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_ASSIGNED_CLIENTID, (void *)(&use_assigned_clientid));
相關參數:
參數
樣本
說明
mqtt_host
iot-06z00ax1o******.mqtt.iothub.aliyuncs.com
裝置接入網域名稱。
企業版執行個體和新版公用執行個體:在執行個體詳情頁面的開發配置面板,查看接入網域名稱。
舊版公用執行個體:接入網域名稱格式為
${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com
。
新舊版公用執行個體和企業版執行個體、以及接入網域名稱的更多資訊,請參見查看執行個體終端節點。
product_key
a18wP******
裝置認證資訊。更多資訊,請參見擷取裝置認證資訊。
本常式的身份認證方式為一機一密。
device_name
LightSwitch
device_secret
uwMTmVAMnGGHaAkqmeDY6cHxxB******
protocol_version
AIOT_MQTT_VERSION_5_0
配置MQTT協議的版本為5.0。
use_assigned_clientid
1
使用MQTT 5.0的
assigned clientId
功能時,需要將use_assigned_clientid
置為1。MQTT保活說明:
重要裝置端在保活時間間隔內,至少需要發送一次報文,包括ping請求。
從物聯網平台發送CONNACK響應CONNECT訊息時,開始心跳計時。收到PUBLISH、SUBSCRIBE、PING或PUBACK訊息時,會重設計時器。物聯網平台每隔30秒定時檢測一次裝置的保活心跳,裝置上線時間點距離最新定時檢測時間點的時間,是定時檢測的等待時間。定義最大逾時時間為:
保活心跳時間*1.5+定時檢測的等待時間
。超過最大逾時時間未收到裝置訊息,伺服器會自動中斷連線。
C Link SDK具備保活能力,您可以設定以下配置項,自訂裝置串連的保活心跳。如果不配置,則取預設值。
配置項
預設值
說明
AIOT_MQTTOPT_HEARTBEAT_MAX_LOST
2
可容忍的心跳丟失閾值。即:心跳請求報文達到設定的次數後,發起重連。
AIOT_MQTTOPT_HEARTBEAT_INTERVAL_MS
25,000
每次發起重連之間的間隔時間。單位毫秒, 取值範圍:1,000~1,200,000。
AIOT_MQTTOPT_KEEPALIVE_SEC
1,200
可容忍的心跳丟失時間閾值。即:失去心跳後,設定的時間內,允許發起重連。單位秒,取值範圍:30~1,200。建議取值大於300。
配置狀態監控和訊息回調。
步驟三:請求串連
調用aiot_mqtt_connect_v5,根據配置串連的參數,向物聯網平台,發起串連認證請求。
範例程式碼中添加的使用者屬性(MQTT_PROP_ID_USER_PROPERTY
)更多資訊,請參見mqtt_property_identify_t。
mqtt_properties_t *conn_props = aiot_mqtt_props_init();
mqtt_property_t user_prop = {
.id = MQTT_PROP_ID_USER_PROPERTY,
.value.str_pair.key.len = strlen("demo_key"),
.value.str_pair.key.value = (uint8_t *)"demo_key",
.value.str_pair.value.len = strlen("demo_value"),
.value.str_pair.value.value = (uint8_t *)"demo_value",
};
aiot_mqtt_props_add(conn_props, &user_prop);
/* 通過MQTT 5.0的方式與伺服器建連 */
res = aiot_mqtt_connect_v5(mqtt_handle, NULL, conn_props);
aiot_mqtt_props_deinit(&conn_props);
if (res < STATE_SUCCESS) {
/* 嘗試建立串連失敗, 銷毀MQTT執行個體, 回收資源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
步驟四:開啟保活線程
調用aiot_mqtt_process,向伺服器發送心跳報文,使裝置保持長串連狀態,並重發QoS=1
的未應答報文。
開啟保活線程。
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; }
設定保活線程處理函數。
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; }
步驟五:開啟接收線程
調用aiot_mqtt_recv,收取伺服器下發的MQTT訊息,根據訊息回呼函數,執行對應處理。在斷線時自動重連,根據事件回呼函數,執行對應處理。
開啟接收線程。
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; }
設定接收線程處理函數。
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; }
步驟六:訂閱Topic
調用aiot_mqtt_sub_v5,訂閱指定Topic。
範例程式碼:
/* MQTT 訂閱topic功能樣本, 請根據自己的業務需求進行使用 */ { char *sub_topic = "/sys/${YourProductKey}/${YourDeviceName}/thing/event/property/post_reply"; mqtt_properties_t *sub_props = aiot_mqtt_props_init(); aiot_mqtt_props_add(sub_props, &user_prop); /* 訂閱選項 */ sub_options_t opts = { .no_local = 1, .qos = 1, .retain_as_publish = 1, .retain_handling = 1, }; res = aiot_mqtt_sub_v5(mqtt_handle, sub_topic, &opts, NULL, NULL, sub_props); aiot_mqtt_props_deinit(&sub_props); if (res < 0) { printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res); aiot_mqtt_deinit(&mqtt_handle); return -1; } }
說明範例程式碼中添加的使用者屬性(
MQTT_PROP_ID_USER_PROPERTY
)更多資訊,請參見mqtt_property_identify_t。添加訂閱選項的詳細內容,請參見sub_options_t。
相關參數:
參數
樣本
說明
sub_topic
/a18wP******/LightSwitch/user/get
擁有訂閱許可權的Topic。其中:
a18wP******
為裝置的ProductKey。LightSwitch
為裝置的DeviceName。
本樣本為預設的自訂Topic。
裝置通過該Topic,可接收物聯網平台的訊息。
關於Topic的更多資訊,請參見什麼是Topic。
sub_props
aiot_mqtt_props_init()
訂閱附加屬性。
opts
.no_local = 1,
.qos = 1,
.retain_as_publish = 1,
.retain_handling = 1,
訂閱選項。
步驟七:發送訊息
調用aiot_mqtt_pub_v5,向指定Topic發送訊息。
範例程式碼:
mqtt_properties_t *pub_props = aiot_mqtt_props_init(); /* MQTT 發布訊息功能樣本, 請根據自己的業務需求進行使用 */ char *pub_topic = "/sys/${YourProductKey}/${YourDeviceName}/thing/event/property/post"; char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}"; mqtt_property_t response_prop = { .id = MQTT_PROP_ID_RESPONSE_TOPIC, .value.str.len = strlen(pub_topic), .value.str.value = (uint8_t *)pub_topic, }; char *demo_data_str = "12345"; mqtt_property_t correlation_prop = { .id = MQTT_PROP_ID_CORRELATION_DATA, .value.str.len = strlen(demo_data_str), .value.str.value = (uint8_t *)demo_data_str, }; aiot_mqtt_props_add(pub_props, &response_prop); aiot_mqtt_props_add(pub_props, &correlation_prop); res = aiot_mqtt_pub_v5(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)(strlen(pub_payload)), 1,0, pub_props); if (res < 0) { printf("aiot_mqtt pub failed, res: -0x%04X\n", -res); aiot_mqtt_deinit(&mqtt_handle); return -1; }
說明範例程式碼中添加的使用者屬性(
MQTT_PROP_ID_USER_PROPERTY
)更多資訊,請參見mqtt_property_identify_t。相關參數:
參數
樣本
說明
pub_topic
/a18wP******/LightSwitch/user/update
擁有發布許可權的Topic。其中:
a18wP******
為裝置的ProductKey。LightSwitch
為裝置的DeviceName。
裝置通過該Topic向物聯網平台發送訊息。
關於Topic的更多資訊,請參見什麼是Topic。
pub_payload
{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}
上報至物聯網平台的訊息內容。
由於樣本訊息的Topic類型為自訂,因此資料格式可自訂。
關於資料格式的更多資訊,請參見資料格式。
pub_props
參考範例程式碼。
發布訊息攜帶的屬性。
步驟八:中斷連線
調用aiot_mqtt_disconnect_v5,向物聯網平台發送中斷連線的報文,然後斷開網路連接。
範例程式碼中添加的使用者屬性(MQTT_PROP_ID_USER_PROPERTY
)更多資訊,請參見mqtt_property_identify_t。
MQTT接入常應用於長串連的裝置,程式通常不會運行至此。常式的主線程任務為配置參數並成功建立串連。串連建立後,主線程可進入休眠。
{
mqtt_properties_t *disconn_props = aiot_mqtt_props_init();
/* reason code 0x0 表示Normal disconnection */
int demo_reason_code = 0x0;
char *demo_reason_string = "normal_exit";
mqtt_property_t reason_prop = {.id = MQTT_PROP_ID_REASON_STRING, .value.str.len = strlen(demo_reason_string), .value.str.value = (uint8_t *)demo_reason_string};
aiot_mqtt_props_add(disconn_props, &reason_prop);
res = aiot_mqtt_disconnect_v5(mqtt_handle, demo_reason_code, disconn_props);
aiot_mqtt_props_deinit(&disconn_props);
}
步驟九:退出程式
調用aiot_mqtt_deinit,銷毀MQTT用戶端執行個體,釋放資源。
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}