ESP32 ESP-IDF 通過阿里雲物聯網平臺控制LED

術語

阿里雲物聯網平臺(以下簡稱物聯網平臺)

物聯網平臺爲設備提供安全可靠的連接通信能力,向下連接海量設備,支撐設備數據採集上雲;向上提供雲端API,服務端通過調用雲端API將指令下發至設備端,實現遠程控制。官方定義

MQTT(Message Queuing Telemetry Transport,消息隊列遙測傳輸協議)

MQTT是一個基於客戶端(設備端)-服務器的消息發佈/訂閱傳輸協議,消息通過Topic匹配規則路由到指定的Topic。MQTT協議輕量、簡單、開放和易於實現,可作爲即時通訊協議,在智能家居、移動應用智慧城市有着廣泛的應用。
引用https://zhuanlan.zhihu.com/p/20888181

MQTT 協議的內容

概述

通過修改ESP-IDF中MQTT的tcp的demo修改成連接物聯網平臺控制LED燈開關的實例。該實例會讓我們對物聯網中的感知層、網絡傳輸層有個初步的印象。
下行的數據需要提前訂閱一個light_control主題。
上行數據發送到指定的light_status主題處理。
本項目是參考了examples\protocols\mqtt\tcp的示例。

數據結構

上行數據結構

lightStatus的狀態off表示電燈關閉,on表示電燈狀態是打開。

{
    "lightStatus": "on"
}

下行數據結構

switch 的值爲off時控制電燈的關閉,值爲on時控制電燈的打開。

{
    "switch": "on"
}

物聯網平臺操作

連接物聯網平臺首先要獲取一機一密設備認證信息:ProductKey、ProductSecret、DeviceName、DeviceSecret,然後根據主題進行發佈和訂閱消息。

創建產品

在物聯網的控制檯中展開設備管理->產品->創建產品,在創建產品的表單中輸入如下一個標準燈類的直連產品。產品的名稱爲home-light。

創建設備

在控制檯中展開設備管理->設備->添加設備,設備放在產品home-light下,設備名稱爲light-1

# 獲取認證信息

在設備管理->設備->設備詳情,在設備詳情裏可以看到認證信息ProductKey、ProductSecret、DeviceName、DeviceSecret。

創建自定義Topic

在產品詳情->Topic類列表->自定義 Topic

基礎通訊Topic和物模型通信Topic

創建一個light_status發佈主題,用來上報當前燈的狀態數據。
創建一個light_control訂閱主題,用來控制亮燈和關燈。

基礎通訊Topic

物聯網平臺預定義的基礎功能通信Topic。

  • 固件升級相關Topic
  • 設備標籤相關Topic
  • 時鐘同步相關Topic
  • 設備影子相關Topic
  • 配置更新相關Topic
  • 廣播Topic

物模型通信Topic

物聯網平臺預定義的物模型通信Topic。各物模型功能Topic消息的數據格式。

編碼

文件結構

添加ali_iot模塊

可以從https://github.com/knzjoint/ali-iot下載,放入項目的components目錄中。

添加wifi配置模塊(可選)

因爲在項目的
從esp-idf的examples\common_components中將protocol_examples_common複製到項目的components目錄下。
原先的protocol_example_common是在項目中的CMakeLists.txt裏配置set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common),如果自己是這這個目錄複製到項目文件下,這條語句可以在CmakeLists.txt中註釋掉。

添加固定信息

通過宏添加阿里雲物聯網平臺認證信息和硬件參數。獲取認證信息

// 下面的幾個宏用於定義設備的阿里雲身份認證信息:ProductKey、ProductSecret、DeviceSecret、DeviceName
// 在實際產品開發中,設備的身份認證信息應該是設備廠商將其加密後存放於設備Flash中或者某個文件中,
// 設備上電時將其讀出後使用
#define LIGHT_PRODUCT_KEY "a1CEwF7bES6"
#define LIGHT_PRODUCT_SECRET "xxxxxxxx"
#define LIGHT_DEVICE_SECRET "xxxxxxxx"
#define LIGHT_DEVICE_NAME "light-1"

// 控制Light開關的Topic
#define LIGHT_CONTROL_TOPIC "/a1CEwF7bES6/light-1/user/light_control"
// 上報Light狀態的Topic
#define LIGHT_STATUS_TOPIC "/a1CEwF7bES6/light-1/user/light_status"

// 控制LED的 GPIO
gpio_num_t gpio_led_num = GPIO_NUM_32; // 連接LED的GPIO

mqtt連接設備

通過IOT_Sign_MQTT函數獲取連接阿里去物聯網平臺的簽名。

// 下面的代碼是將上面靜態定義的設備身份信息賦值給meta_info
memcpy(meta_info.product_key, LIGHT_PRODUCT_KEY, strlen(LIGHT_PRODUCT_KEY));
memcpy(meta_info.product_secret, LIGHT_PRODUCT_SECRET, strlen(LIGHT_PRODUCT_SECRET));
memcpy(meta_info.device_name, LIGHT_DEVICE_NAME, strlen(LIGHT_DEVICE_NAME));
memcpy(meta_info.device_secret, LIGHT_DEVICE_SECRET, strlen(LIGHT_DEVICE_SECRET));

// 調用簽名函數,生成MQTT連接時需要的各種數據,IOTX_CLOUD_REGION_SHANGHAI 指連接站點是華東2(上海)
IOT_Sign_MQTT(IOTX_CLOUD_REGION_SHANGHAI, &meta_info, &sign_mqtt);

esp_mqtt_client_config_t mqtt_cfg = {
    // .uri = CONFIG_BROKER_URL,
    .host = sign_mqtt.hostname,         // 完整的阿里雲物聯網站點域名
    .port = 1883,                       // 阿里雲站點的端口號
    .password = sign_mqtt.password,     // MQTT建立連接時需要指定的Password。把提交給服務器的參數按字典排序並拼接後,使用hmacsha256方法和設備的DeviceSecret,加簽生成Password。
    .client_id = sign_mqtt.clientid,    // MQTT建立連接時需要指定的ClientID。建議使用設備的MAC地址或SN碼,64字符內。
    .username = sign_mqtt.username,     // MQTT建立連接時需要指定的Username。由設備名DeviceName、符號(&)和產品ProductKey組成,格式:deviceName+"&"+productKey。示例:Device1&alSseIs****。
    .event_handle = mqtt_event_handler, // mqtt客戶端啓動成功後對連接、斷開連接、訂閱、取消訂閱、發佈、接收數據等事件的處理。
};

訂閱設備的主題消息

在客戶端連接到物聯網平臺時訂閱light_control主題。

case MQTT_EVENT_CONNECTED: // MQTT 客戶端連接上服務器事件
    msg_id = esp_mqtt_client_subscribe(client, LIGHT_CONTROL_TOPIC, 1);
    ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
    break;

消息處理

當前客戶端收到light_control主題消息,需要根據主題路由到指定的處理函數,處理函數需要根據數據格式對數據進行解析,解析的結果對設備進行操作。
處理topic

if (0 == strncmp(event->topic, LIGHT_CONTROL_TOPIC, event->topic_len))
{
    ESP_LOGI(TAG, "deal with topic :%s", event->topic);
    char dest[512] = ""; // event事件未初始化,使用ESP_LOGI()打印的內容部分會出現亂碼
    memcpy(dest, event->data, event->data_len);
    ESP_LOGI(TAG, "DATA=%s", dest);
    // 解析JSON格式數據
    parse_json_data(dest);
}

解析數據、操作設備和上報狀態

if (0 == strncmp(item->string, "switch", sizeof("switch")))
{
    // 獲取switch的value值
    value_str = item->valuestring;
    ESP_LOGI(TAG, "parsed cmd_id:%s", value_str);
    // 判斷 switch的值是否爲on
    if (0 == strncmp(value_str, "on", sizeof("on")))
    {
        // 開燈
        switch_led(1);
        // 上報當前燈的狀態
        msg_id = esp_mqtt_client_publish(client, LIGHT_STATUS_TOPIC, "{\"lightStatus\": \"on\"}", 0, 0, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
        break;
    }
    else if (0 == strncmp(value_str, "off", sizeof("off")))
    {
        // 關燈
        switch_led(0);
        // 上傳當前燈的狀態
        msg_id = esp_mqtt_client_publish(client, LIGHT_STATUS_TOPIC, "{\"lightStatus\": \"off\"}", 0, 0, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
        break;
    }
}

編譯

編譯前需要配置ali-iot庫和wifi的賬戶和密碼,運行idf.py menuconfig

配置ali-iot庫

進入Component config —> ali_iot —> 選中Enable ali_iot

配置wifi賬戶和密碼

進入Example Connection Configuration —> 分別配置 WiFi SSID(賬戶) 和 WiFi Password(密碼)

燒錄

運行idf.py flash 燒錄

監控

運行idf.py monitor監測程序,出現以下內容表示連接物聯網平臺成功。

I (13258) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:1
I (14018) wifi:state: init -> auth (b0)
I (14028) wifi:state: auth -> assoc (0)
I (14038) wifi:state: assoc -> run (10)
I (14048) wifi:connected with maker_knz, aid = 1, channel 1, BW20, bssid = d8:63:75:62:02:b3
I (14048) wifi:security type: 3, phy: bgn, rssi: -75
I (14058) wifi:pm start, type: 1

I (14138) wifi:AP's beacon interval = 102400 us, DTIM period = 2
I (14648) esp_netif_handlers: sta ip: 192.168.43.192, mask: 255.255.255.0, gw: 192.168.43.1
I (14648) example_connect: Got IP event!
I (15648) example_connect: Got IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fe96:f830, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (15648) example_connect: Connected to maker_knz
I (15648) example_connect: IPv4 address: 192.168.43.192
I (15658) example_connect: IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fe96:f830
I (15668) MQTT_ALI_IOT: Other event id:7
I (15738) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000
I (15808) MQTT_ALI_IOT: sent subscribe successful, msg_id=43235
I (15848) MQTT_ALI_IOT: MQTT_EVENT_SUBSCRIBED, msg_id=43235
I (15858) MQTT_ALI_IOT: sent publish successful, msg_id=0

物聯網平臺聯調

從物聯網平臺發送消息控制LED。


查看設備當前狀態。

總結

目前這種方法只適應於將設備認證信息固定在程序中,以後可以配合服務端根據設備唯一標識動態申請認證信息。
項目源碼

參考文檔

阿里雲物聯網套件的Topic
阿里去物聯網套件mqtt協議簽名示例

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