[IOT] 自制藍牙工牌辦公室定位系統 (一)—— 阿里物聯網平臺概覽及打通端到雲(硬核·乾貨)


目錄:老小皆宜、超長乾貨文警告html

  • 一、快速入門建立產品 —— 小白,打包帶走去吹牛
  • 二、代碼分析 —— 老炮,快速瞭解能用上
    • 2.1 從start.sh分析開發環境如何自動構建
    • 2.2 從sample.c分析程序流程
    • 2.3 數據下發流程分析
    • 2.4 數據讀取與上報流程分析
  • 三、移植到ESP32上搞IOT —— 二營長,把老子的意大利炮拿上來
    • 3.1 搭建ESP32全自動命令行開發環境
    • 3.2 基於ESP32移植並編譯阿里iotkit-embedded成lib
    • 3.3 基於esp-aliyun和iotkit-embedded實現ESP32 DEMO —— 上下求索、坑外有坑
    • 3.4 基於esp-aliyun和iotkit-embedded實現ESP32 DEMO —— 方法突破、絕處逢生
    • 3.5 基於esp-aliyun和iotkit-embedded實現ESP32 DEMO —— 發佈訂閱、所有實現
    • 3.6 基於esp-aliyun和iotkit-embedded實現ESP32 DEMO —— 全自動腳本、免費送
  • 附錄-LINK

字數: 59710個
系統: linux、esp32
聲明: 非軟文linux


一、快速入門建立產品 —— 小白,打包帶走去吹牛

連接: https://iot.console.aliyun.com/quick_startgit

直接看下面的視頻:(注意瀏覽器使能下unsafe load,由於個人視頻服務器是本身搭的github

 
這裏建立了一個名字爲aliyun_test的產品,在該產品下建立一個名爲linux_aliyun_teset的設備,並生成了一個基於linux平臺的嵌入式C開發工具包aliyun_iot_device_quickstart.zip。咱們按照指引將工具包解壓、編譯、運行可看到經過MQTT雲端和本地進行通訊的效果:json

unzip aliyun_iot_device_quickstart.zip
cd aliyun_iot_device_quickstart 
sh ./start.sh

 
運行啓動腳本後,能夠在雲端的設備日誌和本地termial中發現設備通訊的LOG:api


二、代碼分析 —— 老炮,快速瞭解能用上

若是本文僅僅講體驗一下就太沒勁了!說不定博客園小編還會把個人文章從主頁上「拉下馬」。瀏覽器

 

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.shmakefile,應用層代碼sample.c,設備訪問Aliyun的核心信息*.jsonbash

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-sdklib庫文件,而後分別把對應的libinclude分別複製到根目錄下的libinclude中;最後再次使用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:


三、移植到ESP32上搞IOT —— 二營長,把老子的意大利炮拿上來

若是本文到此爲止,老炮們確定會吐槽這是個騙流量的文章!老炮心裏OS中:阿里的linux上的調試工具挺方便的,若是上面不寫代碼分析,貼這麼多圖、變着花樣的貼圖,並且本身服務器上搭的圖牀帶寬還辣麼窄,看你寫啥(捂臉笑)。

 

3.1 搭建ESP32全自動命令行開發環境

經過下面兩個資料,你們能夠自行搭建環境:

 
不過!做爲系統潔癖和拒絕重複造輪子的博主,已經寫了一個全自動構建環境的腳本、並把該工具在github上開源了:esp32_linux_tool [5]

注:nbtool是博主專門放本身造的或收集到的牛逼輪子的github組

 
博主造的這個輪子比較好用,基於all-in-one思想(全部相關文件在一個文件夾下;全部相關環境變量不須要額外配置):

  • 用的時候須要git clone到本地,進入tool文件夾,運行bash run.sh tool會自動構建ESP-IDF開發環境:

    git clone git@github.com: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 git@github.com: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中瞭解如何實現自動化的~

: 完~
: 你們以爲不錯,能夠點推薦給更多人~
: 明天年會,再幹一週,放假,用這篇超長文作個年終總結吧(笑)~


[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|園友交流羣
相關文章
相關標籤/搜索