使用阿里雲C-SDK接入aliyun物聯網平臺

設備:開發板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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章