字數: 59710個
系統: linux、esp32
聲明: 非軟文
1、快速入門創建產品 —— 小白,打包帶走去吹牛
鏈接: https://iot.console.aliyun.com/quick_start
直接看下面的視頻:(注意瀏覽器使能下unsafe load,因爲我的視頻服務器是自己搭的)
這裏創建了一個名字爲aliyun_test
的產品,在該產品下創建一個名爲linux_aliyun_teset
的設備,並生成了一個基於linux平臺的嵌入式C開發工具包aliyun_iot_device_quickstart.zip
。我們按照指引將工具包解壓、編譯、運行可看到通過MQTT雲端和本地進行通信的效果:
unzip aliyun_iot_device_quickstart.zip
cd aliyun_iot_device_quickstart
sh ./start.sh
運行啓動腳本後,可以在雲端的設備日誌和本地termial中發現設備通信的LOG:
2、代碼分析 —— 老炮,快速瞭解能用上
如果本文僅僅講體驗一下就太沒勁了!說不定博客園小編還會把我的文章從主頁上“拉下馬”。
2.1 從start.sh分析開發環境如何自動構建
下面是沒有執行腳本之前的壓縮包內容:
➜ Downloads mv aliyun_iot_device_quickstart aliyun_iot_device_quickstart2
➜ Downloads unzip aliyun_iot_device_quickstart.zip
➜ Downloads cd aliyun_iot_device_quickstart
➜ aliyun_iot_device_quickstart tree
.
├── device_id_password.json
├── makefile
├── sample.c
└── start.sh
0 directories, 4 files
該壓縮包內包含:開始腳本start.sh,makefile,應用層代碼sample.c,設備訪問Aliyun的核心信息*.json。
1)其中start.sh前50行都在檢測你的環境是否安裝必要工具,例如gcc、tar、cmake...;然後讀取json
文件,抽出其中的pk\dn\ds
,分別是productKey\deviceName\deviceSecret;接下來是構建開發環境,主要是從雲端下載一個sdk:linkkit2.2.1.tar.gz
;接下來將託來的SDK調用make
進行編譯,生成aliyun-iot-c-sdk
的lib
庫文件,然後分別把對應的lib
和include
分別複製到根目錄下的lib
和include
中;最後再次使用make進行clean\all,然後啓動。(下面抽幾個核心代碼貼下)
-
從
json
讀取pk\dn\ds
:(我會用jq來處理)pk=`grep -Po '(?<=productKey": ")[0-9a-zA-Z]*' ${json}` dn=`grep -Po '(?<=deviceName": ")[\-_@\.:0-9a-zA-Z]*' ${json}` ds=`grep -Po '(?<=deviceSecret": ")[0-9a-zA-Z]*' ${json}`
-
下載sdk並解壓:
echo "download sdk tar" wget ${url} tar -zxf ${sdktar} ${sdkdir}/ rm -f ${sdktar}
-
編譯成庫:
cd ./iotx-sdk-c make distclean make cd .. cp -r ./iotx-sdk-c/output/release/lib ./lib cp -r ./iotx-sdk-c/output/release/include ./include
-
編譯並運行:
make clean -s make all PK=${pk} DN=${dn} DS=${ds} ./quickstart
2)其中makefile過於簡單,主要是用SDK生成的lib
來編譯應用層代碼sample.c
,核心代碼如下:
sample.o:sample.c
$(CC) $(CFLAGS) $(INCLUDE) ${DID} -c $^
OBJS= sample.o
.PHONY:all
all:$(OBJS) $(LIB)
$(CC) $(CFLAGS) $(INCLUDE) -o $(TARGET) $(OBJS) $(LIBVAR) $(LIBPATH)
.PHONY:clean
clean:
rm -f *.o
rm -f $(TARGET)
2.2 從sample.c分析程序流程
應用層代碼總共約400行,下面是main函數:
int main(int argc, char **argv)
{
app_print_banner();
IOT_OpenLog("sample");
APP_TRACE("sample start!\n");
/*
* C-SDK quick start sample
* please check document: https://help.aliyun.com/document_detail/73708.html?spm=a2c4g.11174283.6.560.zfcQ3y
* API introduce: https://help.aliyun.com/document_detail/68687.html?spm=a2c4g.11186623.6.627.RJcT3F
*/
app_setup_info();
app_linkkit_sample();
IOT_CloseLog();
APP_TRACE("sample end!\n");
return 0;
}
其中最重要的就是app_linkkit_sampl(void)
, 該函數的前30行主要負責初始化linkkit結構體並啓動linkkit:
- linkkit_ops_t結構體內的變量是從linkkit底層引出到應用層的函數指針,可見該結構體的作用相當於SDK層和應用層的咽喉;
- .on_connect函數指針,用於SDK層通知APP層設備連接上雲了;
- .on_disconnect函數指針,用於SDK層通知APP層設備與雲斷開了;
- .raw_data_arrived函數指針,用於SDK層將收到的原始數據通知到APP層;
- .thing_creat函數指針,用於SDK層通知APP層thing創建了(thing可能就是alithing吧);
- .thing_enable/disable = NULL;
- .thing_call_service函數指針,用於自定義服務回調(暫時不太理解);
- .thing_prop_changed函數指針,比較重要,從下圖log來看是雲端下發數據時設備收數據的回調函數;
- .linkit_data_arrived函數指針,暫時不清楚;
下圖非常詳細的展示了應用層上述函數指針的實現,以及程序運行起來後每部分的作用(建議單獨tab打開圖片):
爲了方便看,我把其縱向切割成3份:
-
linkkit_ops_t結構體初始化:
-
各種函數指針給linkkit_ops_t賦值:
-
LOG:
2.3 數據下發流程分析
我們創建的aliyun_test只有兩個自定義功能:
每次雲端有PROPERTY數據變化會出發下面的回調函數,在該函數中我們通過判斷PROPERTY_ID,來區分不同功能點:
static int thing_prop_changed(const void *thing_id, const char *property, void *ctx)
{
int status[1];
char *data;
if (memcmp(property, PROPERTY_ID_STATUS, strlen(PROPERTY_ID_STATUS)) == 0) {
linkkit_get_value(linkkit_method_get_property_value, thing_id, property, status, NULL);
APP_TRACE("Received a message: {\"%s\":%d}\n", property, status[0]);
/* do user's data arrived process logical here. */
}
else if (memcmp(property, PROPERTY_ID_DATA, strlen(PROPERTY_ID_DATA)) == 0) {
linkkit_get_value(linkkit_method_get_property_value, thing_id, property, NULL, &data);
APP_TRACE("Received a message: {\"%s\":\"%s\"}\n", property, data);
HAL_Free(data); /* free memery as it was malloc by linkkit api linkkit_get_value() */
}
return 0;
}
下面是雲端主動推送信息下來後本地打印的LOG:
2.4 數據讀取與上報流程分析
本DEMO啓動之後會每隔5S將上報所有(2個)property,具體邏輯是:先讀取STATUS和DATA,如果DATA沒有數據,則發送hello world:
static int app_post_all_property(void)
{
int res;
int status[1] = {0};
char *data = NULL;
linkkit_get_value(linkkit_method_get_property_value, app_context.thing, PROPERTY_ID_STATUS, status, NULL);
linkkit_get_value(linkkit_method_get_property_value, app_context.thing, PROPERTY_ID_DATA, NULL, &data);
/* init data property to "Hello world" if it is value is NULL */
if (data == NULL) {
linkkit_set_value(linkkit_method_set_property_value, app_context.thing, PROPERTY_ID_DATA, NULL, PROPERTY_ID_DATA_VALUE);
linkkit_get_value(linkkit_method_get_property_value, app_context.thing, PROPERTY_ID_DATA, NULL, &data);
}
APP_TRACE("{\"%s\":%d, \"%s\":\"%s\"}", PROPERTY_ID_STATUS, status[0], PROPERTY_ID_DATA, data);
HAL_Free(data); /* free memery as it was malloc by linkkit api linkkit_get_value() */
/* demo for post all property */
res = linkkit_post_property(app_context.thing, NULL, post_property_cb);
if (res == SUCCESS_RETURN) {
APP_TRACE("app post all properties every 5 seconds successfully");
}
else {
APP_TRACE("app post all properties every 5 seconds fail");
}
return res;
}
下面是本地主動週期性上報的LOG:
3、移植到ESP32上搞IOT —— 二營長,把老子的意大利炮拿上來
如果本文到此爲止,老炮們肯定會吐槽這是個騙流量的文章!老炮內心OS中:阿里的linux上的調試工具挺方便的,如果上面不寫代碼分析,貼這麼多圖、變着花樣的貼圖,而且自己服務器上搭的圖牀帶寬還辣麼窄,看你寫啥(捂臉笑)。
3.1 搭建ESP32全自動命令行開發環境
通過下面兩個資料,大家可以自行搭建環境:
-
SDK介紹:對於ESP32樂鑫官方提供了一個IDF :
-
環境搭建:如果你想自己搭建開發環境,參見樂鑫官方資料:
不過!作爲系統潔癖和拒絕重複造輪子的博主,已經寫了一個全自動構建環境的腳本、並把該工具在github上開源了:esp32_linux_tool [5]
注:nbtool是博主專門放自己造的或收集到的牛逼輪子的github組
博主造的這個輪子比較好用,基於all-in-one思想(所有相關文件在一個文件夾下;所有相關環境變量不需要額外配置):
-
用的時候需要git clone到本地,進入tool文件夾,運行bash run.sh tool會自動構建ESP-IDF開發環境:
git clone [email protected]:nbtool/esp32_linux_tool.git cd esp32_linux_tool cd tool bash run.sh help bash run.sh tool
-
當需要創建hello world時,只需要調用下面命令,即可從SDK中的DEMO複製到app文件夾下,並自動在app/hello_world下創建run.sh腳本:
bash run.sh create ../sdk/esp-idf/examples/get-started/hello_world hello_world cd ../app/hello_world ./run.sh help
-
當需要編譯並燒寫固件到ESP32中的時,只需要調用./run.sh flash即可:
./run.sh flash
-
當需要觀察LOG的時候,只需要:
./run.sh monitor
是不是很好用?(哈哈),想要了解其機制,只需要參考下tool下的run.sh即可~
3.2 基於ESP32移植並編譯阿里iotkit-embedded成lib
設備廠商在設備上集成 Link Kit C-SDK 後, 可以將設備安全的接入到阿里雲IoT物聯網平臺, 從而讓設備可以被阿里雲IoT物聯網平臺進行管理。
設備需要支持TCP/IP協議棧才能集成SDK, zigbee/433/KNX這樣的非IP設備需要通過網關設備接入到阿里雲IoT物聯網平臺, 網關設備需要集成Link Kit SDK [6]。
基於我們的esp_32_linux_tool環境來編譯iotkit-embedded:
cd esp32_linux_tool
cd app
git clone https://github.com/aliyun/iotkit-embedded.git
cd iotkit-embedded
在iot-embedded文件夾下創建一個run.sh文件:
➜ iotkit-embedded git:(master) ✗ cat run.sh
#!/bin/bash
#I don't like to set environment variables in the system,
#so I put the environment variables in run.sh.
#Every time I use run.sh, the enviroment variables will be set, after use that will be unsetted.
PROJECT_ROOT=../..
TOOLS_PATH=$PROJECT_ROOT/tool
SDK_PATH=$PROJECT_ROOT/sdk
APP_PATH=$PROJECT_ROOT/app
XTENSA_ESP32_ELF_PATH=$TOOLS_PATH/xtensa-esp32-elf
ESP_IDF_PATH=$SDK_PATH/esp-idf
the_sdk_path=`cd $ESP_IDF_PATH; pwd`
the_tool_chain_path=`cd $XTENSA_ESP32_ELF_PATH/bin; pwd`
export PATH="$PATH:$the_tool_chain_path"
export IDF_PATH="$the_sdk_path"
if [ "$1" == "reconfig" ]; then
make reconfig
elif [ "$1" == "make" ]; then
make
elif [ "$1" == "clean" ]; then
make clean
else
echo "error, try bash run.sh help"
fi
將config.esp8266.aos
, 複製一份保存爲config.esp32.aos
,做一些細微調整,最終如下:
➜ iotkit-embedded git:(master) ✗ cat src/board/config.esp32.aos
CONFIG_ENV_CFLAGS += \
-DBOARD_ESP32 -u call_user_start \
-fno-inline-functions \
-ffunction-sections \
-fdata-sections \
-mlongcalls \
-DESPOS_FOR_ESP32 -Wl,-static \
-DXT_USE_THREAD_SAFE_CLIB=0 \
CONFIG_ENV_CFLAGS += \
-Os \
-DCONFIG_HTTP_AUTH_TIMEOUT=500 \
-DCONFIG_MID_HTTP_TIMEOUT=500 \
-DCONFIG_GUIDER_AUTH_TIMEOUT=500 \
-DCONFIG_MQTT_TX_MAXLEN=640 \
-DCONFIG_MQTT_RX_MAXLEN=1200 \
CONFIG_src/ref-impl/tls :=
CONFIG_src/ref-impl/hal :=
CONFIG_examples :=
CONFIG_tests :=
CONFIG_src/tools/linkkit_tsl_convert :=
CROSS_PREFIX := xtensa-esp32-elf-
最後,運行下面語句進行選擇平臺並編譯生成lib庫:
./run.sh reconfig
./run.sh make
最終生成libiot_sdk.a如下:
3.3 基於esp-aliyun和iotkit-embedded實現ESP32 DEMO —— 上下求索、坑外有坑
esp-aliyun [7] 是樂鑫官網提供的一個通過MQTT訪問aliyun iot服務器的開源項目。該項目不僅依賴於3.2節介紹的iotkit-embedded [6] 生成的lib,而且尷尬的是辛辛苦苦編譯成功後,還不能和我們第一章創建的產品通信(成功操作會連接到雲端保持online,但是update\get數據都有誤)。
樓主依次做了如下操作,均失敗(阿里雲IOT更新太快(資料不全)呀!):
- 對比linux SDK和esp-aliyun項目的區別,發現兩個實現方法不一樣:linux SDK用了linkkit(比較方便能和雲通信,代碼架構也清晰);esp-aliyun僅僅實現了MQTT的DEMO,只有沒有封裝太多的MQTT發佈訂閱函數(因此兩個代碼有區別)
- 發現linkkit_ops_t項目本身也有DEMO:這個在項目的examples下,其中包含mqtt/mqtt_example.c(和esp-aliyun一模一樣!!!)和linkkit/linkkit_example_solo.c(和linux SDK相似,用 IOT_Linkkit_xx實現高級玩法)
- 直接將linkkit/linkkit_example_solo.c覆蓋掉esp-aliyun下的mqtt_example.c,做合理修改,發現總是編譯不通過(嘗試各種iotkit-embedded參數配置,生成lib)
- 分析iotkit-embedded工程,發現編譯平臺爲linux的時候能夠自動編譯example下面的demo,同樣的操作選擇esp32的時候不能。
- 發現AliOS Thing似乎實現了上面我想要在esp-32上編譯運行基於IOT_Linkkit_xx的linkkit_example_solo.c demo!(捂臉笑)
但是樓主在前面已經立下flag,要基於我做的esp32_linux_tool實現一個能夠與第一章創建的aliyun_test交互的DEMO,那是絕對不能拿AliOS Thing來糊弄大家的(AliOS Thing之後講,哈哈)!
3.4 基於esp-aliyun和iotkit-embedded實現ESP32 DEMO —— 方法突破、絕處逢生
利用下班的一點點時間,將3.3的問題趟了兩天、阿里資料看了多遍,最終又找到了一個奇巧淫技!我將linux版的DEMO開一段時間,在aliyun後臺看所有交互的log的數據包,然後我參考這個數據包用mqtt_example.c裏的原始MQTT函數進行合成高級命令,然後實現和阿里雲通信。
採用上述方法,我發現原來mqtt_example.c中TOPIC和上報json數據格式是有問題的:
-
其中TOPIC要按照創建產品的topic列表中來(其中發佈用來上報數據、訂閱用來收取數據):
/* These are pre-defined topics */ #define TOPIC_UPDATE "/"PRODUCT_KEY"/"DEVICE_NAME"/user/update" #define TOPIC_ERROR "/"PRODUCT_KEY"/"DEVICE_NAME"/user/update/error" #define TOPIC_GET "/"PRODUCT_KEY"/"DEVICE_NAME"/user/get" #define DEVICE_PROPERTY_POST "/sys/"PRODUCT_KEY"/"DEVICE_NAME"/thing/event/property/post"//設備屬性上報 #define DEVICE_PROPERTY_POST_REPLY "/sys/"PRODUCT_KEY"/"DEVICE_NAME"/thing/event/property/post_reply"//設備屬性上報變化訂閱(這個topic列表中沒有,我自己抓出來的) #define DEVICE_PROPERTY_SET "/sys/"PRODUCT_KEY"/"DEVICE_NAME"/thing/service/property/set"//設備屬性設置訂閱 #define DEVICE_INFO_UPDATE "/sys/"PRODUCT_KEY"/"DEVICE_NAME"/thing/deviceinfo/update"//設備標籤上報
-
其中json數據格式有一定的規範,不能直接組一個{"Status":1}就上報,要帶一部分參數:
{ "method":"thing.event.property.post", "id":"7", "version":"1.0", "params":{ "Status":1, "Data":"Hello, World!" } }
注: 其實最後通過仔細閱讀Link Kit SDK用戶手冊 [8] ,也印證了我上面的嘗試~
3.5 基於esp-aliyun和iotkit-embedded實現ESP32 DEMO —— 發佈訂閱、全部實現
採用上述json格式,成功將數據上報到DEVICE_PROPERTY_POST的TOPIC下,通過進一步查後臺LOG發現一個隱藏的TOPIC:DEVICE_PROPERTY_POST_REPLY,通過訂閱該TOPIC每次上報導致數據變化就能被監聽到了!(和linux SDK版本一樣了,舒服)
一不做二不休,直接實現所有訂閱:
/* Subscribe the specific topic */
rc = IOT_MQTT_Subscribe(pclient, DEVICE_PROPERTY_SET, IOTX_MQTT_QOS1, _demo_message_arrive, NULL);
if (rc < 0) {
IOT_MQTT_Destroy(&pclient);
EXAMPLE_TRACE("IOT_MQTT_Subscribe() failed, rc = %d", rc);
return -1;
}
rc = IOT_MQTT_Subscribe(pclient, DEVICE_INFO_UPDATE, IOTX_MQTT_QOS1, _demo_message_arrive, NULL);
if (rc < 0) {
IOT_MQTT_Destroy(&pclient);
EXAMPLE_TRACE("IOT_MQTT_Subscribe() failed, rc = %d", rc);
return -1;
}
rc = IOT_MQTT_Subscribe(pclient, TOPIC_GET, IOTX_MQTT_QOS1, _demo_message_arrive, NULL);
if (rc < 0) {
IOT_MQTT_Destroy(&pclient);
EXAMPLE_TRACE("IOT_MQTT_Subscribe() failed, rc = %d", rc);
return -1;
}
rc = IOT_MQTT_Subscribe(pclient, DEVICE_PROPERTY_POST_REPLY, IOTX_MQTT_QOS1, _demo_message_arrive, NULL);
if (rc < 0) {
IOT_MQTT_Destroy(&pclient);
EXAMPLE_TRACE("IOT_MQTT_Subscribe() failed, rc = %d", rc);
return -1;
}
實現數據週期性上報:
do {
/* Generate topic message */
cnt++;
msg_len = snprintf(msg_pub, sizeof(msg_pub), "{\"method\":\"thing.event.property.post\",\"id\":\"7\",\"version\":\"1.0\",\"params\":{\"Status\":%d,\"Data\":\"Hello, World!-%d\"}}",cnt%2 == 0,cnt);
if (msg_len < 0) {
EXAMPLE_TRACE("Error occur! Exit program");
return -1;
}
topic_msg.payload = (void *)msg_pub;
topic_msg.payload_len = msg_len;
rc = IOT_MQTT_Publish(pclient, DEVICE_PROPERTY_POST, &topic_msg);
...
}
最終週期性上報數據並收到訂閱的回調LOG截圖如下:
3.6 基於esp-aliyun和iotkit-embedded實現ESP32 DEMO —— 全自動腳本、免費送
最後,爲了感謝2018年來新老訪客的點贊(瘋狂暗示中),我編寫了一個超級輕量級全自動構建、編譯、燒寫、DEBUG的腳本,助你一鍵體驗,爽翻。
GIT 地址: https://github.com/nbtool/esp32_linux_tool
體驗方法:
#克隆項目到本地
> git clone [email protected]:nbtool/esp32_linux_tool.git
#構建esp32開發環境
> cd ./esp32_linux_tool/tool
> ./run.sh help
> ./run.sh tool
#進入aliyun_demo應用文件夾,查看幫助
> cd ../app/aliyun_demo
> ./run.sh help
|----------------------------------------------------
| ./run.sh op param
| op:
| create : downloads iotkit and aliyun-esp32 from github and make some change
| sdk : param = reconfig/config/make/clean
| app : param = deconfig/config/make/erase/flash/monitor/clean
| examples:
| first create sdk lib : create -> sdk reconfig -> sdk config -> sdk make
| second create app : config -> make -> flash -> monitor
|----------------------------------------------------
#編譯iotkit-embedded成lib
> ./run.sh create
> ./run.sh sdk reconfig
> ./run.sh sdk make
#編譯應用層代碼,並燒寫查看LOG
> ./run.sh app config
> ./run.sh app make
> ./run.sh app flash
> ./run.sh app monitor
- 注: 可以從aliyun_demo/run.sh中瞭解如何實現自動化的~
- 完~
- 大家覺得不錯,可以點推薦給更多人~
- 明天年會,再幹一週,放假,用這篇超長文做個年終總結吧(笑)~
LINKS
[1]. MarkDown語法進階(三)(文字居中、圖片處理、插入視頻音樂、標準字體)
[2]. Aliyun IOT Console
[3]. ESP32-IDF GITHUB地址
[4]. ESP-IDF Program Guide
[5]. esp32_linux_tool GITHUB地址
[6]. iotkit-embedded GITHUB地址
[7]. esp-aliyun GITHUB地址
[8]. Link Kit SDK 用戶手冊
@beautifulzzzz
以藍牙技術爲基礎的的末梢無線網絡系統架構及創新型應用探索!
領域:智能硬件、物聯網、自動化、前沿軟硬件
博客:https://www.cnblogs.com/zjutlitao/
園友交流羣|微信交流羣:414948975|園友交流羣