設備:開發板fl2440
開發主機:ubuntu-14.04
cpu:s3c2440
Linux內核版本:3.0
移植USB驅動,rt3070wifi模塊接入,移植DS18B20驅動。
1.獲取阿里雲3.0.1版本C-SDK
地址:https://code.aliyun.com/linkkit/c-sdk/repository/archive.zip?spm=a2c4g.11186623.2.15.3b24492bm3SZ3S&ref=v3.0.1
zhanghang@Ubuntu-14:~/zhanghang$ wget https://code.aliyun.com/linkkit/c-sdk/repository/archive.zip
zhanghang@Ubuntu-14:~/zhanghang$ unzip archive.zip
zhanghang@Ubuntu-14:~/zhanghang$ mv c-sdk-v3.0.1-269691d1b45b15fb9045a8eb178efa54b262aca1c-sdk.git/ c-sdk-v3.0.1
2.由於是在Ubuntu下開發,根據之前在阿里雲物聯網平臺創建的產品,設備,修改文件wrappers/os/ubuntu/HAL_OS_linux.c
設備的創建傳送門:https://mp.csdn.net/mdeditor/97620834#
查看在阿里雲物聯網平臺上創建的產品和設備。
修改文件wrappers/os/ubuntu/HAL_OS_linux.c
#ifdef DYNAMIC_REGISTER
char _product_key[IOTX_PRODUCT_KEY_LEN + 1] = "a1ZETBPbycq";
char _product_secret[IOTX_PRODUCT_SECRET_LEN + 1] = "L68wCVXYUaNg1Ey9";
char _device_name[IOTX_DEVICE_NAME_LEN + 1] = "example1";
char _device_secret[IOTX_DEVICE_SECRET_LEN + 1] = "";
#else
#ifdef DEVICE_MODEL_ENABLED
char _product_key[IOTX_PRODUCT_KEY_LEN + 1] = "a17i4DkIYtW";//修改爲產品的productkey
char _product_secret[IOTX_PRODUCT_SECRET_LEN + 1] = "DxYqtfCggDxqENJb";//修改爲產品的secret
char _device_name[IOTX_DEVICE_NAME_LEN + 1] = "DS18B20";//修改爲設備的name
char _device_secret[IOTX_DEVICE_SECRET_LEN + 1] = "uug19VsZveTVnQhpWAwoulzeQBXhz8TR";//修改爲設備的secert
#else
char _product_key[IOTX_PRODUCT_KEY_LEN + 1] = "a1MZxOdcBnO";
char _product_secret[IOTX_PRODUCT_SECRET_LEN + 1] = "h4I4dneEFp7EImTv";
char _device_name[IOTX_DEVICE_NAME_LEN + 1] = "test_01";
char _device_secret[IOTX_DEVICE_SECRET_LEN + 1] = "t9GmMf2jb3LgWfXBaZD2r3aJrfVWBv56";
#endif
#endif
3.編譯c-sdk
直接在頂層目錄執行make即可。
這裏我們只進行一個測試,make會編譯src/mqtt/examples/mqtt_example.c這個文件,生成可執行文件output/release/bin/mqtt-example
4.測試執行
zhanghang@Ubuntu-14:~/zhanghang/c-sdk-v3.0.1$ ./output/release/bin/mqtt-example
main|125 :: mqtt example
establish tcp connection with server(host='a17i4DkIYtW.iot-as-mqtt.cn-shanghai.aliyuncs.com', port=[1883])
success to establish tcp, fd=3
...
example_event_handle|098 :: msg->event_type : 9
< {
< "id": "0",
< "version": "1.0",
< "params": [
< {
< "attrKey": "SYS_LP_SDK_VERSION",
< "attrValue": "3.0.1",
< "domain": "SYSTEM"
< },
< {
< "attrKey": "SYS_SDK_LANGUAGE",
< "attrValue": "C",
< "domain": "SYSTEM"
< }
< ],
< "method": "thing.deviceinfo.update"
< }
example_event_handle|098 :: msg->event_type : 12
example_event_handle|098 :: msg->event_type : 3
example_event_handle|098 :: msg->event_type : 9
< {
< "message": "hello!"
< }
example_message_arrive|031 :: Message Arrived:
example_message_arrive|032 :: Topic : /a17i4DkIYtW/DS18B20/user/get
example_message_arrive|033 :: Payload: {"message":"hello!"}
example_message_arrive|034 ::
文件src/mqtt/examples/mqtt_example.c訂閱和發佈了"message": "hello!"消息到主題 /a17i4DkIYtW/DS18B20/user/get上,打印消息紅色部分分別顯示了發佈和訂閱收到的內容。
具體參考: https://help.aliyun.com/document_detail/96624.html?spm=a2c4g.11186623.6.554.3b24492bm3SZ3S
源碼分析:src/mqtt/examples/mqtt_example.c
#include "dev_sign_api.h"
#include "mqtt_api.h"
char DEMO_PRODUCT_KEY[IOTX_PRODUCT_KEY_LEN + 1] = {0};
char DEMO_DEVICE_NAME[IOTX_DEVICE_NAME_LEN + 1] = {0};
char DEMO_DEVICE_SECRET[IOTX_DEVICE_SECRET_LEN + 1] = {0};
void *HAL_Malloc(uint32_t size);
void HAL_Free(void *ptr);
void HAL_Printf(const char *fmt, ...);
int HAL_GetProductKey(char product_key[IOTX_PRODUCT_KEY_LEN + 1]);
int HAL_GetDeviceName(char device_name[IOTX_DEVICE_NAME_LEN + 1]);
int HAL_GetDeviceSecret(char device_secret[IOTX_DEVICE_SECRET_LEN]);
uint64_t HAL_UptimeMs(void);
int HAL_Snprintf(char *str, const int len, const char *fmt, ...);
#define EXAMPLE_TRACE(fmt, ...) \
do { \
HAL_Printf("%s|%03d :: ", __func__, __LINE__); \
HAL_Printf(fmt, ##__VA_ARGS__); \
HAL_Printf("%s", "\r\n"); \
} while(0)
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 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;
}
}
int example_subscribe(void *handle)
{
int res = 0;
const char *fmt = "/%s/%s/user/get";
char *topic = NULL;
int topic_len = 0;
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);//將產品product_key,設備的device_name和fmt連接形成topic,即在物聯網平臺設置爲可訂閱發佈的topic
res = IOT_MQTT_Subscribe(handle, topic, IOTX_MQTT_QOS0, example_message_arrive, NULL);
if (res < 0) {
EXAMPLE_TRACE("subscribe failed");
HAL_Free(topic);
return -1;
}
HAL_Free(topic);
return 0;
}
int example_publish(void *handle)
{
int res = 0;
const char *fmt = "/%s/%s/user/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));
if (res < 0) {
EXAMPLE_TRACE("publish failed, res = %d", res);
HAL_Free(topic);
return -1;
}
HAL_Free(topic);
return 0;
}
void example_event_handle(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
EXAMPLE_TRACE("msg->event_type : %d", msg->event_type);
}
int main(int argc, char *argv[])
{
void *pclient = NULL;
int res = 0;
int loop_cnt = 0;
iotx_mqtt_param_t mqtt_params;
HAL_GetProductKey(DEMO_PRODUCT_KEY);
HAL_GetDeviceName(DEMO_DEVICE_NAME);
HAL_GetDeviceSecret(DEMO_DEVICE_SECRET);
EXAMPLE_TRACE("mqtt example");
memset(&mqtt_params, 0x0, sizeof(mqtt_params));
mqtt_params.handle_event.h_fp = example_event_handle;
pclient = IOT_MQTT_Construct(&mqtt_params);
if (NULL == pclient) {
EXAMPLE_TRACE("MQTT construct failed");
return -1;
}
res = example_subscribe(pclient);
if (res < 0) {
IOT_MQTT_Destroy(&pclient);
return -1;
}
while (1) {
if (0 == loop_cnt % 20) {
example_publish(pclient);
}
IOT_MQTT_Yield(pclient, 200);
loop_cnt += 1;
}
return 0;
}
從main函數開始:
HAL_GetProductKey(DEMO_PRODUCT_KEY);
HAL_GetDeviceName(DEMO_DEVICE_NAME);
HAL_GetDeviceSecret(DEMO_DEVICE_SECRET);
這三個函數將讀取我們之前在文件wrappers/os/ubuntu/HAL_OS_linux.c中修改的那部分信息,並填入對應數組中。
從main函數開始:
HAL_GetProductKey(DEMO_PRODUCT_KEY);
HAL_GetDeviceName(DEMO_DEVICE_NAME);
HAL_GetDeviceSecret(DEMO_DEVICE_SECRET);
這三個函數將讀取我們之前在文件wrappers/os/ubuntu/HAL_OS_linux.c中修改的那部分信息,並填入對應數組中。
接下來看到函數IOT_MQTT_Construct的實現:
void *IOT_MQTT_Construct(iotx_mqtt_param_t *pInitParams)
{
void *pclient;
iotx_dev_meta_info_t meta_info;
iotx_mqtt_param_t mqtt_params;
char device_id[IOTX_PRODUCT_KEY_LEN + IOTX_DEVICE_NAME_LEN + 1] = {0};
int region = 0;
int dynamic = 0;
uint8_t enalbe_itls = 0;
int ret;
void *callback;
...
/* get meta_info from hal */
memset(&meta_info, 0, sizeof(iotx_dev_meta_info_t));
HAL_GetProductKey(meta_info.product_key);
HAL_GetDeviceName(meta_info.device_name);
...
#else /* get device secret from hal */
HAL_GetDeviceSecret(meta_info.device_secret);
//獲取三元素到met_info中
#else /* direct mode */
ret = IOT_Sign_MQTT(region, &meta_info, &g_default_sign);
//用存有三元素的meta_info初始化g_default_sign
/* setup device_id */
memcpy(device_id, meta_info.product_key, strlen(meta_info.product_key));
memcpy(device_id + strlen(device_id), ".", strlen("."));
memcpy(device_id + strlen(device_id), meta_info.device_name, strlen(meta_info.device_name));
if (_sign_get_clientid(g_default_sign.clientid, device_id,
(pInitParams != NULL) ? pInitParams->customize_info : NULL, enalbe_itls) != SUCCESS_RETURN)
//同樣是對g_default_sign進行初始化
memset(&mqtt_params, 0x0, sizeof(iotx_mqtt_param_t));
#endif
mqtt_params.request_timeout_ms = CONFIG_MQTT_REQUEST_TIMEOUT;
mqtt_params.clean_session = 0;
mqtt_params.keepalive_interval_ms = CONFIG_MQTT_KEEPALIVE_INTERVAL * 1000;
mqtt_params.read_buf_size = CONFIG_MQTT_MESSAGE_MAXLEN;
mqtt_params.write_buf_size = CONFIG_MQTT_MESSAGE_MAXLEN;
mqtt_params.handle_event.h_fp = NULL;
mqtt_params.handle_event.pcontext = NULL;
/* optional configuration */
if (pInitParams != NULL) {
if (pInitParams->host && strlen(pInitParams->host)) {
mqtt_params.host = pInitParams->host;
} else {
mqtt_warning("Using default hostname: '%s'", g_default_sign.hostname);
mqtt_params.host = g_default_sign.hostname;
}
if (pInitParams->port) {
mqtt_params.port = pInitParams->port;
} else {
mqtt_warning("Using default port: [%d]", g_default_sign.port);
mqtt_params.port = g_default_sign.port;
}
if (pInitParams->client_id && strlen(pInitParams->client_id)) {
mqtt_params.client_id = pInitParams->client_id;
} else {
mqtt_warning("Using default client_id: %s", g_default_sign.clientid);
mqtt_params.client_id = g_default_sign.clientid;
}
if (pInitParams->username && strlen(pInitParams->username)) {
mqtt_params.username = pInitParams->username;
} else {
mqtt_warning("Using default username: %s", g_default_sign.username);
mqtt_params.username = g_default_sign.username;
}
if (pInitParams->password && strlen(pInitParams->password)) {
mqtt_params.password = pInitParams->password;
} else {
#if 1
mqtt_warning("Using default password: %s", "******");
#else
mqtt_warning("Using default password: %s", g_default_sign.password);
#endif
mqtt_params.password = g_default_sign.password;
}
if (pInitParams->request_timeout_ms < CONFIG_MQTT_REQ_TIMEOUT_MIN ||
pInitParams->request_timeout_ms > CONFIG_MQTT_REQ_TIMEOUT_MAX) {
mqtt_warning("Using default request_timeout_ms: %d, configured value(%d) out of [%d, %d]",
mqtt_params.request_timeout_ms,
pInitParams->request_timeout_ms,
CONFIG_MQTT_REQ_TIMEOUT_MIN,
CONFIG_MQTT_REQ_TIMEOUT_MAX);
} else {
mqtt_params.request_timeout_ms = pInitParams->request_timeout_ms;
}
if (pInitParams->clean_session == 0 || pInitParams->clean_session == 1) {
mqtt_params.clean_session = pInitParams->clean_session;
}
if (pInitParams->keepalive_interval_ms < CONFIG_MQTT_KEEPALIVE_INTERVAL_MIN * 1000 ||
pInitParams->keepalive_interval_ms > CONFIG_MQTT_KEEPALIVE_INTERVAL_MAX * 1000) {
mqtt_warning("Using default keepalive_interval_ms: %d, configured value(%d) out of [%d, %d]",
mqtt_params.keepalive_interval_ms,
pInitParams->keepalive_interval_ms,
CONFIG_MQTT_KEEPALIVE_INTERVAL_MIN * 1000,
CONFIG_MQTT_KEEPALIVE_INTERVAL_MAX * 1000);
} else {
mqtt_params.keepalive_interval_ms = pInitParams->keepalive_interval_ms;
if (!pInitParams->read_buf_size) {
mqtt_warning("Using default read_buf_size: %d", mqtt_params.read_buf_size);
} else {
mqtt_params.read_buf_size = pInitParams->read_buf_size;
}
if (!pInitParams->write_buf_size) {
mqtt_warning("Using default write_buf_size: %d", mqtt_params.write_buf_size);
} else {
mqtt_params.write_buf_size = pInitParams->write_buf_size;
}
if (pInitParams->handle_event.h_fp != NULL) {
mqtt_params.handle_event.h_fp = pInitParams->handle_event.h_fp;
}
if (pInitParams->handle_event.pcontext != NULL) {
mqtt_params.handle_event.pcontext = pInitParams->handle_event.pcontext;
}
} else {
mqtt_warning("Using default port: [%d]", g_default_sign.port);
mqtt_params.port = g_default_sign.port;
mqtt_warning("Using default hostname: '%s'", g_default_sign.hostname);
mqtt_params.host = g_default_sign.hostname;
mqtt_warning("Using default client_id: %s", g_default_sign.clientid);
mqtt_params.client_id = g_default_sign.clientid;
mqtt_warning("Using default username: %s", g_default_sign.username);
mqtt_params.username = g_default_sign.username;
#if 1
mqtt_warning("Using default password: %s", "******");
#else
mqtt_warning("Using default password: %s", g_default_sign.password);
#endif
mqtt_params.password = g_default_sign.password;
}
//pInitParams是我們傳進來的只初始化過mqtt_params.handle_event.h_fp = example_event_handle;的參數,此處利用pInitParams和通過三元組初始化的g_default_sign來初始化mqtt_params
pclient = wrapper_mqtt_init(&mqtt_params);//利用mqtt_params初始化pclient,很明顯,pclient應該保存了三元組形成的所有連接到阿里雲物聯網所需要的資源。
ret = wrapper_mqtt_connect(pclient);//與物聯網平臺建立連接
g_mqtt_client = pclient;//全局變量g_mqtt_client保存pclient
return pclient;
}
建立連接後返回pclient句柄,接下來可以利用pclient向物聯網平臺的topic發佈消息和訂閱主題。
函數example_subscribe主要進行平臺topic的訂閱
res = IOT_MQTT_Subscribe(handle, topic, IOTX_MQTT_QOS0, example_message_arrive, NULL);
訂閱主題爲topic,服務質量爲0。handle作爲來連接到平臺的句柄。
函數example_publish發佈內容到平臺的topic
res = IOT_MQTT_Publish_Simple(0, topic, IOTX_MQTT_QOS0, payload, strlen(payload));
發佈消息paylaod到平臺topic上。在這裏,並沒有傳入句柄hanlde,但是卻可以和平臺通信?
事實上,在調用函數IOT_MQTT_construct是,我們將pclient的地址給了一個全局變量g_mqtt_client
我們查看IOT_MQTT_Subscribe和IOT_MQTT_Publish_Simple的源碼:
int IOT_MQTT_Subscribe(void *handle,
const char *topic_filter,
iotx_mqtt_qos_t qos,
iotx_mqtt_event_handle_func_fpt topic_handle_func,
void *pcontext)
{
void *client = handle ? handle : g_mqtt_client;
…
int IOT_MQTT_Publish_Simple(void *handle, const char *topic_name, int qos, void *data, int len)
{
iotx_mqtt_topic_info_t mqtt_msg;
void *client = handle ? handle : g_mqtt_client;
如果不傳入pclient,這兩個函數也回去找g_mqtt_client變量,而這兩個變量存儲的其實pclient的地址。
最後看到這段代碼:
res = example_subscribe(pclient);
if (res < 0) {
IOT_MQTT_Destroy(&pclient);
return -1;
}
while (1) {
if (0 == loop_cnt % 20) {
example_publish(pclient);
}
IOT_MQTT_Yield(pclient, 200);
loop_cnt += 1;
}
函數IOT_MQTT_Subscribe一次訂閱,到斷開連接或取消訂閱前此訂閱會一直生效
函數IOT_MQTT_Publish發佈消息到topic,可以進行多次發送,在這裏,使用一個循環進行不斷髮送
最後函數
int IOT_MQTT_Yield(void *handle, int timeout_ms)
{
void *pClient = (handle ? handle : g_mqtt_client);//同上
return wrapper_mqtt_yield(pClient, timeout_ms);
}
用於接收網絡報文並將消息分發到用戶的回調函數中,即訂閱了某個topic後,此函數會接受此topic的消息,並調用之前在IOT_MQTT_Subscribe的回調函數example_message_arrive,即打印收到的消息。
其他常用函數還有
int IOT_MQTT_Destroy(void **phandle) 銷燬指定MQTT連接並釋放資源
int IOT_MQTT_Unsubscribe(void *handle,const char * topic_filter)向雲端取消訂閱指定topic
int IOT_MQTT_Publish(void *handle,const char *topic_name,iotx_mqtt_topic_info_pt topic_msg)向雲端指定topic推送消息
參考: https://code.aliyun.com/edward.yangx/public-docs/wikis/user-guide/Linkkit_User_Manual