本文介紹如何使用Paho提供的嵌入式C語言MQTT開源工程,將裝置接入阿里雲物聯網平台,並進行訊息收發。
使用前必讀
本文操作步驟以普通使用者權限為例。如果您在操作過程中涉及到管理員權限才能執行的操作,可嘗試使用sudo
命令執行。
前提條件
已在物聯網平台控制台,對應執行個體下,建立產品和裝置,並擷取MQTT接入網域名稱和裝置認證資訊(ProductKey、DeviceName和DeviceSecret)。具體操作,請參見:
準備開發環境
本樣本使用Ubuntu 16.04-LTS作為開發環境。執行以下命令構建開發環境。
sudo apt-get update
sudo apt-get install build-essential git sed cmake
下載C語言Paho MQTT庫
執行以下命令,複製C語言版本的Paho MQTT庫。
git clone https://github.com/eclipse/paho.mqtt.embedded-c.git
編寫本Demo樣本時,使用master分支,commit id為29ab2aa29c5e47794284376d7f8386cfd54c3eed
。
Paho嵌入式C工程提供了以下三個子專案:
MQTTPacket:提供MQTT資料包的序列化與還原序列化,以及部分輔助函數。
MQTTClient:封裝MQTTPacket產生的進階別C++用戶端程式。
MQTTClient-C:封裝MQTTPacket產生的進階別C用戶端程式。
MQTTClient-C中包含:
├── CMakeLists.txt ├── samples │ ├── CMakeLists.txt │ ├── FreeRTOS │ └── linux ├── src │ ├── CMakeLists.txt │ ├── FreeRTOS │ ├── MQTTClient.c │ ├── MQTTClient.h │ ├── cc3200 │ └── linux └── test ├── CMakeLists.txt └── test1.c
samples目錄提供FreeRTOS和Linux兩個常式,分別支援FreeRTOS和Linux系統。
src目錄提供MQTTClient的代碼實現能力,以及用於移植到FreeRTOS、cc3200和Linux的網路驅動。
瞭解Paho MQTT的更多API細節,可以查看MQTTClient.h。
接入物聯網平台
單擊開啟aiot_mqtt_sign.c,複製阿里雲提供的計算MQTT串連參數所需的源碼,然後粘貼儲存為本地的aiot_mqtt_sign.c檔案。
aiot_mqtt_sign.c檔案定義了函數aiotMqttSign(),函數說明如下:
原型:
int aiotMqttSign(const char *productKey, const char *deviceName, const char *deviceSecret, char clientId[150], char username[65], char password[65]);
功能:
用於計算裝置接入物聯網平台的MQTT串連參數username、password和clientid。
輸入參數:
參數
類型
說明
productKey
const char *
裝置所屬產品的ProductKey,該裝置在物聯網平台上的身份認證資訊之一。
deviceName
const char *
裝置名稱,該裝置在物聯網平台上的身份認證資訊之一。
deviceSecret
const char *
裝置密鑰,該裝置在物聯網平台上的身份認證資訊之一。
輸出參數:
參數
類型
說明
username
char *
MQTT串連所需的使用者名稱。
password
char *
MQTT串連所需的密碼。
clientId
char *
MQTT用戶端ID。
返回碼說明:
返回碼
說明
0
成功
-1
失敗
添加實現裝置接入物聯網平台的程式檔案。
您需編寫程式調用aiot_mqtt_sign.c中的aiotMqttSign()函數計算MQTT串連參數,實現接入物聯網平台和通訊。
開發說明和範例程式碼如下:
調用aiotMqttSign()介面,產生串連MQTT服務端的三個建連參數clientId、username和password。
#define EXAMPLE_PRODUCT_KEY "a11xsrW****" #define EXAMPLE_DEVICE_NAME "paho_****" #define EXAMPLE_DEVICE_SECRET "Y877Bgo8X5owd3lcB5wWDjryNPoB****" extern int aiotMqttSign(const char *productKey, const char *deviceName, const char *deviceSecret, char clientId[150], char username[65], char password[65]); /* invoke aiotMqttSign to generate mqtt connect parameters */ char clientId[150] = {0}; char username[65] = {0}; char password[65] = {0}; if ((rc = aiotMqttSign(EXAMPLE_PRODUCT_KEY, EXAMPLE_DEVICE_NAME, EXAMPLE_DEVICE_SECRET, clientId, username, password) < 0)) { printf("aiotMqttSign -%0x4x\n", -rc); return -1; } printf("clientid: %s\n", clientId); printf("username: %s\n", username); printf("password: %s\n", password);
接入物聯網平台。
需配置以下內容:
調用NetworkInit和NetworkConnect建立TCP串連。
調用MQTTClientInit初始化MQTT用戶端。
配置MQTT建連參數結構體MQTTPacket_connectData。
範例程式碼:
/* network init and establish network to aliyun IoT platform */ NetworkInit(&n); rc = NetworkConnect(&n, host, port); printf("NetworkConnect %d\n", rc); /* init mqtt client */ MQTTClientInit(&c, &n, 1000, buf, sizeof(buf), readbuf, sizeof(readbuf)); /* set the default message handler */ c.defaultMessageHandler = messageArrived; /* set mqtt connect parameter */ MQTTPacket_connectData data = MQTTPacket_connectData_initializer; data.willFlag = 0; data.MQTTVersion = 3; data.clientID.cstring = clientId; data.username.cstring = username; data.password.cstring = password; data.keepAliveInterval = 60; data.cleansession = 1; printf("Connecting to %s %d\n", host, port); rc = MQTTConnect(&c, &data); printf("MQTTConnect %d, Connect aliyun IoT Cloud Success!\n", rc);
發布訊息。
調用MQTTPublish()介面,向指定的自訂Topic發布自訂格式訊息。
通訊Topic介紹,請參見什麼是Topic。
char *pubTopic = "/"EXAMPLE_PRODUCT_KEY"/"EXAMPLE_DEVICE_NAME"/user/update"; int cnt = 0; unsigned int msgid = 0; while (!toStop) { MQTTYield(&c, 1000); if (++cnt % 5 == 0) { MQTTMessage msg = { QOS1, 0, 0, 0, "Hello world", strlen("Hello world"), }; msg.id = ++msgid; rc = MQTTPublish(&c, pubTopic, &msg); printf("MQTTPublish %d, msgid %d\n", rc, msgid); } }
訂閱Topic,擷取雲端下發的訊息。
void messageArrived(MessageData* md) { MQTTMessage* message = md->message; printf("%.*s\t", md->topicName->lenstring.len, md->topicName->lenstring.data); printf("%.*s\n", (int)message->payloadlen, (char*)message->payload); } char *subTopic = "/"EXAMPLE_PRODUCT_KEY"/"EXAMPLE_DEVICE_NAME"/user/get"; printf("Subscribing to %s\n", subTopic); rc = MQTTSubscribe(&c, subTopic, 1, messageArrived); printf("MQTTSubscribe %d\n", rc);
關於裝置、伺服器和物聯網平台的通訊方式介紹,請參見物聯網平台通訊方式概述。
將步驟1下載的aiot_mqtt_sign.c檔案和步驟2編輯的檔案放到../paho.mqtt.embedded-c/MQTTClient-C/samples/linux中,然後編譯工程。
範例程式碼
使用Demo代碼程式接入物聯網平台。
下載Demo包,並解壓縮。
解壓縮後,程式碼封裝裡有以下兩個檔案:
檔案
說明
aiot_mqtt_sign.c
該檔案中的代碼用於產生MQTT建連參數。aiot_c_demo.c運行時,會調用該檔案中定義的aiotMqttSign()函數,計算出串連參數username、password和clientId。
aiot_c_demo.c
該檔案包含裝置與物聯網平台串連和通訊的邏輯代碼。
在aiot_c_demo.c中,將裝置資訊修改為您的裝置資訊。
替換以下代碼中EXAMPLE_PRODUCT_KEY、EXAMPLE_DEVICE_NAME和EXAMPLE_DEVICE_SECRET後的值為您的裝置認證資訊。
#define EXAMPLE_PRODUCT_KEY "產品ProductKey" #define EXAMPLE_DEVICE_NAME "裝置名稱DeviceName" #define EXAMPLE_DEVICE_SECRET "裝置密鑰DeviceSecret"
修改代碼
char *host = EXAMPLE_PRODUCT_KEY".iot-as-mqtt.cn-shanghai.aliyuncs.com"
中的值為對應接入網域名稱。公用執行個體和企業版執行個體接入網域名稱的格式說明,請參見查看執行個體終端資訊。
將aiot_mqtt_sign.c和已修改的aiot_c_demo.c檔案放到Paho工程的目錄../paho.mqtt.embedded-c/MQTTClient-C/samples/linux中。
編譯工程,並運行程式。
有兩種方法可以編譯出可執行檔程式:
使用CMake。
在/paho.mqtt.embedded-c/MQTTClient-C/samples/linux目錄下的CMakeLists.txt檔案中,增加aiot_c_demo.c和aiot_mqtt_sign.c。
修改後的CMakeLists.txt檔案內容如下。
add_executable( stdoutsubc stdoutsub.c ) add_executable( aiot_c_demo aiot_c_demo.c aiot_mqtt_sign.c ) target_link_libraries(stdoutsubc paho-embed-mqtt3cc paho-embed-mqtt3c) target_include_directories(stdoutsubc PRIVATE "../../src" "../../src/linux") target_compile_definitions(stdoutsubc PRIVATE MQTTCLIENT_PLATFORM_HEADER=MQTTLinux.h) target_link_libraries(aiot_c_demo paho-embed-mqtt3cc paho-embed-mqtt3c) target_include_directories(aiot_c_demo PRIVATE "../../src" "../../src/linux") target_compile_definitions(aiot_c_demo PRIVATE MQTTCLIENT_PLATFORM_HEADER=MQTTLinux.h)
回到/paho.mqtt.embedded-c目錄,執行以下命令,完成編譯。
mkdir build.paho cd build.paho cmake .. make
編譯完成後,在/paho.mqtt.embedded-c/build.paho目錄下執行以下命令,運行程式。
./MQTTClient-C/samples/linux/aiot_c_demo
使用build.sh。
開啟/paho.mqtt.embedded-c/MQTTClient-C/samples/linux目錄下的build.sh檔案。
將build.sh中的
stdoutsub.c
替換為aiot_mqtt_sign.c aiot_c_demo.c
,-o stdoutsub
替換為-o aiot_c_demo
,然後儲存build.sh。修改完成後,在/paho.mqtt.embedded-c/MQTTClient-C/samples/linux目錄下,執行命令
./build.sh
,完成編譯。完成編譯後,產生aiot_c_demo可執行檔。
執行命令
./aiot_c_demo
,運行程式。
運行成功,接入物聯網平台的本地日誌如下:
clientid: paho_mqtt&a11xsrW****|timestamp=2524608000000,_v=sdk-c-1.0.0,securemode=3,signmethod=hmacsha256,lan=C| username: paho_mqtt&a11xsrW**** password: 36E955DC3D9D012EF62C80657A29328B1CFAE6186C611A17DC7939FAB637**** NetworkConnect 0 Connecting to a11xsrW****.iot-as-mqtt.cn-shanghai.aliyuncs.com 443 MQTTConnect 0, Connect aliyun IoT Cloud Success! Subscribing to /a11xsrW****/paho_mqtt/user/get MQTTSubscribe 0 MQTTPublish 0, msgid 1 MQTTPublish 0, msgid 2 MQTTPublish 0, msgid 3 MQTTPublish 0, msgid 4 MQTTPublish 0, msgid 5 ...
登入物聯網平台控制台,可查看裝置狀態和日誌。
錯誤碼
如果裝置通過MQTT協議接入物聯網平台失敗,請根據錯誤碼排查問題。服務端錯誤碼說明,請參見錯誤排查。