本文介绍如何在ESP32开发板上移植C-SDK4.0, 并用mqtt demo连上阿里云物联网平台。

开发环境说明

为了完成移植实践, 您需要:

  • 一块搭载ESP32的开发板。
  • USB连接线。
  • 运行Windows, Linux或者MacOS的计算机。

demo使用的开发板为ESP32 Core Board V2/ESP32 DevKitC,它搭载了ESP-WROOM-32官方模组,,板载USB转串口模块(CP2102)和电源模块。

说明 我们使用的开发环境为MacOS,使用其他系统的用户也可以参考ESP32的官方入门教程完成开发环境搭建。

搭建开发环境

说明 本节仅供参考使用,客户在搭建开发环境中碰到问题请联系开发板的供应商进行解决,然后再进行SDK的移植。建议用户先阅读乐鑫官方入门教程以加快开发环境搭建。

下面对MacOS环境搭建的相关步骤做简要说明:

  1. 安装必要的软件
  2. 克隆esp-idf官方仓库

    本次演示使用esp-idf仓库的release/v4.2分支。

    cd ~
    mkdir esp && cd esp
    git clone --recursive -b release/v4.2 https://github.com/espressif/esp-idf.git
  3. 安装工具链和编译工具
    cd esp-idf
    ./install.sh
  4. 配置环境变量
    • 运行以下脚本:
      . $HOME/esp/esp-idf/export.sh
    • 用户也可参考以下脚本在$HOME/.bash_profile中添加配置以快速完成环境变量配置:
      set_esp32 ()
      {
          export IDF_PATH=$HOME/esp/esp-idf
          . $HOME/esp/esp-idf/export.sh
      }
  5. 拷贝WiFi state例程到独立目录
    cd ~/esp
    cp -r $IDF_PATH/examples/wifi/getting_started/station .
  6. 连接开发板
  7. 配置工程

    使用idf.py menuconfig配置工程, 演示中使用了默认配置。

  8. 编译, 烧写, 串口监视
    • 进入station项目路径,使用idf.py build编译工程。
    • 编译完成后使用idf.py -p PORT flash命令烧写固件(请用实际的USB端口名替换PORT)。
    • 烧写成功后可运行idf.py -p PORT monitor打开串口打印信息监视器。
    • 用户也可使用idf.py -p PORT flash monitor连续执行这两条命令。

    至此, 用户已经完成了ESP32开发环境的搭建, 并完成了wifi station例程的编译烧写,下面我们将介绍如何移植C-SDK4.0, 并成功连接阿里云物联网平台。

移植C-SDK4.0

移植C-SDK的过程主要包括了SDK代码的导入,SDK port层文件配置和编译系统的配置。

SDK的portfiles目录已经包含了ESP32的portfile, 因此用户只需要导入SDK源码,配置编译系统即可完成移植工作。

基本概念

建议提前阅读idf的编译系统介绍以理解移植过程,用户需要了解idf的一些基本概念:

  • project:项目目录,仅包含了所有用于构建app的源文件和配置文件。
  • components: 功能独立的模块化代码, 将会编译成.a静态库并链接到app,这些模块化的组件存放在idf的components目录下,用户可以添加自定义component。

idf的编译系统默认使用cmake和ninja组合,我们只需移入sdk代码, 编写sdk对应的CMakeList.txt即可将sdk加入编译。

移植思路

  • 方法一:在project目录中引入C-SDK, 将SDK源码与用户app源码共同编译。
  • 方法二:将C-SDK作为idf自定义组件引入到idf的components目录中。

我们推荐用第二种方法, 将C-SDK作为独立组件有助于在不同项目中复用,并解除与用户app代码的耦合。

操作步骤

  1. 添加C-SDK自定义组件

    下载C-SDK4.0, 将sdk复制到$IDF_PATH/components目录下,在C-SDK目录下添加构建文件CMakeLists.txt,内容如下:

    set(include_dirs core core/sysdep core/utils)
    
    file(GLOB c_sdk_srcs
        "core/*.c"
        "core/utils/*.c"
        "core/sysdep/*.c"
    
        "portfiles/aiot_port/*.c"
        "external/*.c")
    
    idf_component_register(SRCS ${c_sdk_srcs}
                           INCLUDE_DIRS "${include_dirs}"
                           REQUIRES mbedtls)
    说明
    • esp32_port.c为sdk针对ESP32的移植层文件。
    • 由于C-SDK依赖mbedtls库, 因此需要使用REQUIRES mbedtls引入组件依赖。
    • 由于C-SDK没有配置项,无需配置组件Kconfig。
    • 若用户需要使用SDK高级组件(如物模型, OTA),则在此CMakeLists.txt中添加组件对应的源文件和头文件路径
  2. 移植demo程序

    用户需要下载附件posix_port.c(适配了esp32), 用它将$IDF_PATH/components/C-SDK/portfiles/aiot_port目录下的posix_port.c替换掉。

    由于idf兼容posix标准,用户可下载附件station_example_main.c获取并替换原有例程下文件station/main/station_example_main.c。

    说明
    • wifi_init_sta()函数会一直等待WiFi连接成功直到重连次数达到宏EXAMPLE_ESP_MAXIMUM_RETRY定义的数值。
    • WiFi连接成功后, 我们便可以调用C-SDK的API进行MQTT建连, 建连成功后即可与物联网平台进行数据收发了。
    • linkkit_main()函数包含原C-SDK的mqtt demo。
  3. 编译, 烧写
    • 在项目目录下运行idf.py menuconfig, 进入Example Configuration菜单。
    • 修改WiFi SSID, WiFi PasswordMaximum retry(最大重连次数)3个参数, 保存并退出配置。
    • 运行idf.py build进行编译。
    • 编译成功后,运行idf.py -p /dev/cu.SLAB_USBtoUART flash monitor完成烧写并打开串口监视器。
    说明 如果设备使用PSK秘钥交换则需要确保mbedTLS已经打开PSK秘钥交换方法,同时配置PSK最大长度为64,可参考以下步骤配置
    • 进入Component config菜单的mbedTLS子菜单, 进入TLS Key Exchange Methods配置项
    • 打开Enable pre-shared-key ciphersuites开关
    • 同时在mbedtls组件的CMakeLists.txt中添加set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMBEDTLS_PSK_MAX_LEN=64")
  4. 运行日志信息
    ......
    
    I (829) phy: phy_version: 4180, cb3948e, Sep 12 2019, 16:39:13, 0, 0
    I (829) wifi: mode : sta (30:ae:a4:04:81:84)
    I (829) wifi station: wifi_init_sta finished.
    I (949) wifi: new:<11,0>, old:<1,0>, ap:<255,255>, sta:<11,0>, prof:1
    I (949) wifi: state: init -> auth (b0)
    I (969) wifi: state: auth -> assoc (0)
    I (969) wifi: state: assoc -> run (10)
    I (1129) wifi: connected with C_SDK_Test, aid = 1, channel 11, BW20, bssid = ec:26:ca:4b:68:cc
    I (1129) wifi: security type: 3, phy: bgn, rssi: -37
    I (1139) wifi: pm start, type: 1
    
    I (1219) wifi: AP's beacon interval = 102400 us, DTIM period = 1
    I (2129) esp_netif_handlers: sta ip: 192.168.0.100, mask: 255.255.255.0, gw: 192.168.0.1
    I (2129) wifi station: got ip:192.168.0.100
    I (2129) wifi station: connected to ap SSID:C_SDK_Test password:1234abcd
    I (2139) wifi station: Start linkkit mqtt
    [1.583][LK-0313] MQTT user calls aiot_mqtt_connect api, connect
    [1.587][LK-0317] mqtt_basic_demo&a13FNXXXXXX
    [1.590][LK-0318] 4780A5F17990D8DC4CCAD392683ED80160C4C2A1FFA649425CD0E2666A8593EB
    [1.598][LK-0319] a13FN5TplKq.mqtt_basic_demo|timestamp=2524608000000,_ss=1,_v=sdk-c-4.0.0,securemode=2,signmethod=hmacsha256,ext=1,|
    establish mbedtls connection with server(host='a13FN5TplKq.iot-as-mqtt.cn-shanghai.aliyuncs.com', port=[443])
    success to establish mbedtls connection, fd = 54(cost 29739 bytes in total, max used 44007 bytes)
    [3.493][LK-0313] MQTT connect success in 1910 ms
    AIOT_MQTTEVT_CONNECT
    [3.494][LK-0309] sub: /sys/a13FN5TplKq/mqtt_basic_demo/thing/event/+/post_reply
    [3.499][LK-0309] pub: /sys/a13FN5TplKq/mqtt_basic_demo/thing/event/property/post
    
    [LK-030A] > 7B 22 69 64 22 3A 22 31  22 2C 22 76 65 72 73 69 | {"id":"1","versi
    [LK-030A] > 6F 6E 22 3A 22 31 2E 30  22 2C 22 70 61 72 61 6D | on":"1.0","param
    [LK-030A] > 73 22 3A 7B 22 4C 69 67  68 74 53 77 69 74 63 68 | s":{"LightSwitch
    [LK-030A] > 22 3A 30 7D 7D                                   | ":0}}
    
    suback, res: -0x0000, packet id: 1, max qos: 1
    [3.573][LK-0309] pub: /sys/a13FN5TplKq/mqtt_basic_demo/thing/event/property/post_reply
    
    [LK-030A] < 7B 22 63 6F 64 65 22 3A  32 30 30 2C 22 64 61 74 | {"code":200,"dat
    [LK-030A] < 61 22 3A 7B 7D 2C 22 69  64 22 3A 22 31 22 2C 22 | a":{},"id":"1","
    [LK-030A] < 6D 65 73 73 61 67 65 22  3A 22 73 75 63 63 65 73 | message":"succes
    [LK-030A] < 73 22 2C 22 6D 65 74 68  6F 64 22 3A 22 74 68 69 | s","method":"thi
    [LK-030A] < 6E 67 2E 65 76 65 6E 74  2E 70 72 6F 70 65 72 74 | ng.event.propert
    [LK-030A] < 79 2E 70 6F 73 74 22 2C  22 76 65 72 73 69 6F 6E | y.post","version
    [LK-030A] < 22 3A 22 31 2E 30 22 7D                          | ":"1.0"}
    
    pub, qos: 0, topic: /sys/a13FNXXXXXX/mqtt_basic_demo/thing/event/property/post_reply
    pub, payload: {"code":200,"data":{},"id":"1","message":"success","method":"thing.event.property.post","version":"1.0"}
    heartbeat response
    heartbeat response
    heartbeat response
    ......
    注意 如果用户看见"aiot_mqtt_connect failed: -0x0F0F"的报错,说明网络不通畅。建议用户检查wifi网络状态, 确认板子在接收wifi信号的范围内,必要时可以重试烧写和连接。备注: -0x0F0F的语义是STATE_PORT_NETWORK_CONNECT_TIMEOUT,连接超时。