ESP32接入阿里云物联网平台(MQTT-TLS连接通信),基于VSCode环境下的ESP-IDF开发(附源码)

发布于:2025-04-19 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

1.创建产品和设备

1.1 创建产品

1.2 创建设备

​编辑1.3 复制设备三要素

1.4 下载阿里云物联网平台自签名证书

2.创建工程

2.1 创建基础工程

3.修改代码

3.1 修改 main.c

3.2 编写 WiFi 连接功能

3.2.1 初始化WiFi相关配置

(1)初始化nvs

(2)初始化lwip

(3)创建事件系统循环

(4)创建STA 

(5)初始化wifi

(6)注册事件响应

(7) 配置sta参数

(8)启动WiFi

(9)编写WiFi事件回调函数

3.2.2 完整代码

3.3 编写 MQTT 功能

3.3.1 定义三要素和域名

3.3.2 将自签名证书定义成字符串

3.3.3 初始化MQTT客户端(核心步骤)

(1)配置域名和端口号

(2)配置MQTT的CONNECT报文参数

①配置MQTT客户端ID(mqttClientId)

②配置MQTT用户名(mqttUsername)

③配置MQTT用户密码(mqttPassword)

(3)配置阿里云物联网平台TLS自签名证书

(4)初始化MQTT客户端

3.3.4 注册事件响应回调函数

3.3.5 启动MQTT客户端

3.3.6 完整代码

3.4 链接多个文件

4.验证功能

5.注意事项

6.参考文档

7.源码下载


1.创建产品和设备

1.1 创建产品

进入阿里云物联网平台-->点击右上角的控制台-->搜索“物联网平台”-->点击公共实例:

选择设备管理-->产品-->创建产品:

1.2 创建设备

1.3 复制设备三要素

按以下操作获取设备三要素(ProductKey、DeviceName、DeviceSecret):

1.4 下载阿里云物联网平台自签名证书

这里去下载 阿里云物联网平台自签名证书

2.创建工程

2.1 创建基础工程

以hello world例程为基础,新建一个工程,并保存在一个路径:

创建完成后先编译一下。

3.修改代码

3.1 修改 main.c

将 hello_world_main.c 改成 main.c,并删除原本代码,用以下代码替换,先初始化WiFi,然后等WiFi连接后再启动MQTT:

#include "mqtt_aliot.h"
#include "wifi.h"
#include "esp_log.h"

#define TAG     "main"

void app_main(void)
{
    /* 初始化WIFI */
    wifi_configuration();

    /* 等待信号量(WiFi连接成功后会释放信号量),启动MQTT */
    xSemaphoreTake(s_wifi_connect_sem, portMAX_DELAY);
    mqtt_start();
    ESP_LOGI(TAG, "hello world!");
    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(2000));
    }
}

3.2 编写 WiFi 连接功能

创建wifi.c和wifi.h两个文件,并编写代码实现WiFi功能。

3.2.1 初始化WiFi相关配置

(1)初始化nvs

当初始化wifi热点和密码后,IDF会将其保存到nvs中:

ESP_ERROR_CHECK(nvs_flash_init());
(2)初始化lwip
ESP_ERROR_CHECK(esp_netif_init());
(3)创建事件系统循环
ESP_ERROR_CHECK(esp_event_loop_create_default());
(4)创建STA 
esp_netif_create_default_wifi_sta();
(5)初始化wifi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
(6)注册事件响应
//创建二值信号量,用于WiFi连接后启动MQTT客户端
s_wifi_connect_sem = xSemaphoreCreateBinary();
//注册WIFI事件
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL);
//注册网络事件
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL);
(7) 配置sta参数
/* 配置wifi,用于设置sta参数 */
wifi_config_t wifi_config = {
    .sta.threshold.authmode = WIFI_AUTH_WPA2_PSK, //加密方式
    .sta.pmf_cfg.capable = true, //是否启用保护管理帧,选择启用
    .sta.pmf_cfg.required = false, //是否只和有保护管理帧功能的设备通信,选择否
    .sta.ssid = TEST_SSID, //WIFI热点名称
    .sta.password = TEST_PASSID, //WIFI热点密码
};

ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));//设为sta模式
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));//配置sta参数
(8)启动WiFi
ESP_ERROR_CHECK(esp_wifi_start());
(9)编写WiFi事件回调函数

启动了sta模式后则连接WiFi,获取到IP地址后则释放信号量,允许启动MQTT客户端:

void wifi_event_handler(void *event_handler_arg,
                        esp_event_base_t event_base,
                        int32_t event_id,
                        void *event_data)
{
    if (event_base == WIFI_EVENT)
    {
        switch (event_id)
        {
            case WIFI_EVENT_STA_START://启动了wifi的sta工作模式
                esp_wifi_connect();//连接wifi
                break;
            case WIFI_EVENT_STA_CONNECTED:
                ESP_LOGI(TAG, "esp32 connected to ap!");
                break;
            case WIFI_EVENT_STA_DISCONNECTED:
                ESP_LOGI(TAG, "esp32 connected the ap failed! retry");
                esp_wifi_connect();
                break;
            default: break;
        }
    }
    else if (event_base == IP_EVENT)
    {
        switch (event_id)
        {
            case IP_EVENT_STA_GOT_IP: //获取到ip地址
                ESP_LOGI(TAG, "esp32 get ip address!");
                xSemaphoreGive(s_wifi_connect_sem);//释放信号量,允许启动mqtt客户端
                break;
        }
    }
}

3.2.2 完整代码

wifi.h完整代码:

#ifndef _WIFI_H_
#define _WIFI_H_

#ifdef __cplusplus
extern "C" {
#endif

#include "esp_event.h"
#include "freertos/semphr.h"

extern SemaphoreHandle_t s_wifi_connect_sem;
void wifi_configuration(void);

#ifdef __cplusplus
}
#endif

#endif

wifi.c完整代码:

#include "wifi.h"
#include <stdio.h>
#include <string.h>
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_err.h"
#include "mqtt_client.h"

#define TAG             "wifi"

#define TEST_SSID       "Mi 11 LE"
#define TEST_PASSID     "12345678"

SemaphoreHandle_t s_wifi_connect_sem = NULL;

void wifi_event_handler(void *event_handler_arg,
                        esp_event_base_t event_base,
                        int32_t event_id,
                        void *event_data)
{
    if (event_base == WIFI_EVENT)
    {
        switch (event_id)
        {
            case WIFI_EVENT_STA_START://启动了wifi的sta工作模式
                esp_wifi_connect();//连接wifi
                break;
            case WIFI_EVENT_STA_CONNECTED:
                ESP_LOGI(TAG, "esp32 connected to ap!");
                break;
            case WIFI_EVENT_STA_DISCONNECTED:
                ESP_LOGI(TAG, "esp32 connected the ap failed! retry");
                esp_wifi_connect();
                break;
            default: break;
        }
    }
    else if (event_base == IP_EVENT)
    {
        switch (event_id)
        {
            case IP_EVENT_STA_GOT_IP: //获取到ip地址
                ESP_LOGI(TAG, "esp32 get ip address!");
                xSemaphoreGive(s_wifi_connect_sem);//释放信号量,允许启动mqtt客户端
                break;
        }
    }
}

void wifi_configuration(void)
{
    /* 初始化nvs(当初始化wifi热点和密码后,IDF会将其保存到nvs中) */
    ESP_ERROR_CHECK(nvs_flash_init());

    /* 初始化lwip */
    ESP_ERROR_CHECK(esp_netif_init());

    /* 创建事件系统循环 */
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* 创建STA */
    esp_netif_create_default_wifi_sta();

    /* 初始化wifi */
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    /* 注册事件响应 */
    //创建二值信号量,用于WiFi连接后启动MQTT客户端
    s_wifi_connect_sem = xSemaphoreCreateBinary();
    //注册WIFI事件
    esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL);
    //注册网络事件
    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL);

    /* 配置wifi,用于设置sta参数 */
    wifi_config_t wifi_config = {
        .sta.threshold.authmode = WIFI_AUTH_WPA2_PSK, //加密方式
        .sta.pmf_cfg.capable = true, //是否启用保护管理帧,选择启用
        .sta.pmf_cfg.required = false, //是否只和有保护管理帧功能的设备通信,选择否
        .sta.ssid = TEST_SSID, //WIFI热点名称
        .sta.password = TEST_PASSID, //WIFI热点密码
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));//设为sta模式
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));//配置sta参数

    /* 启动wifi */
    ESP_ERROR_CHECK(esp_wifi_start());
}

3.3 编写 MQTT 功能

创建mqtt_aliot.c和mqtt_aliot.h两个文件,并实现通过mqtt接入阿里云的代码

3.3.1 定义三要素和域名

将步骤1.3中获取的三要素和域名以宏定义方式放到mqtt_aliot.h中

3.3.2 将自签名证书定义成字符串

将步骤1.4中下载的阿里云物联网平台自签名证书以字符串方式定义在mqtt_aliot.c中:

3.3.3 初始化MQTT客户端(核心步骤)

核心函数:

esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *config)

关键步骤:需要对参数config进行初始化,先定义一个结构体mqtt_cfg

esp_mqtt_client_config_t mqtt_cfg = {0};
(1)配置域名和端口号
mqtt_cfg.broker.address.uri = ALIOT_MQTT_URL;
mqtt_cfg.broker.address.port = 8883;//注意:由于使用了阿里云平台的自签名证书,端口号需改成8883
(2)配置MQTT的CONNECT报文参数

使用TLS加密设备和物联网平台的MQTT通信_物联网平台(IoT)-阿里云帮助中心可知,MQTT的CONNECT报文参数主要由 mqttClientId、mqttUsername、mqttPassword 三部分组成。

例如:
mqttClientId: clientId+"|securemode=3,signmethod=hmacsha1,timestamp=132323232|"
mqttUsername: deviceName+"&"+productKey
mqttPassword: sign_hmac(deviceSecret,content)

  • mqttClientId:格式中| |内为扩展参数。

  • clientId:表示客户端ID,可自定义,这里我们使用设备的MAC地址。

  • securemode:表示目前安全模式,可选值有2(TLS直连模式)和3(TCP直连模式)。

  • signmethod:表示签名算法类型。支持hmacmd5,hmacsha1和hmacsha256。

  • timestamp:表示当前时间毫秒值,可以不传递。

  • mqttPassword:sign签名需把提交给服务器的参数按字典排序后,根据signmethod加签。签名计算示例,请参见MQTT连接签名示例

  • content的值为提交给服务器的参数(productKey、deviceName、timestamp和clientId),按照参数名称首字母字典排序, 然后将参数值依次拼接。

    重要

    此处productKey和deviceName为必填参数,timestamp和clientId为可选参数。若传入timestamp或clientId,必须与mqttClientId中的设置相同。

①配置MQTT客户端ID(mqttClientId)

mqttClientId: clientId+"|securemode=2,signmethod=hmacmd5|"

这里选择MAC地址作为clientId,加密方式选择选择TLS直连,md5加密,不添加时间戳

功能实现:

//mqttClientId: clientId+"|securemode=3,signmethod=hmacsha1,timestamp=132323232|"
char client_id[128];
snprintf(client_id, sizeof(client_id),
         "%s|securemode=2,signmethod=hmacmd5|",// 选择TLS直连,md5加密
         get_clientid()); 
mqtt_cfg.credentials.client_id = client_id;

实现具体函数功能:

/* 获取客户端ID(自定义):以MAC地址作为客户端ID */
char *get_clientid(void)
{
    uint8_t mac[6];
    static char client_id[32] = {0};
    esp_wifi_get_mac(WIFI_IF_STA, mac);//获取mac地址
    snprintf(client_id, sizeof(client_id), "%02X%02X%02X%02X%02X%02X",
             mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

    return client_id;
}
②配置MQTT用户名(mqttUsername)

mqttUsername: deviceName+"&"+productKey

其中deviceName是设备名,productKey是产品秘钥

替换后:

mqttUsername: d001&k1m123NM9XY

功能实现:

//mqttUsername: deviceName+"&"+productKey
char user_name[128];
snprintf(user_name, sizeof(user_name),"%s&%s", ALIOT_DEVICENAME, ALIOT_PRODUCTKEY);
mqtt_cfg.credentials.username = user_name; 
③配置MQTT用户密码(mqttPassword)

mqttPassword: sign_hmac(deviceSecret, content)

  • sign_hmac:表示一个函数,将deviceSecret和content通过哈希算法转换后,将生成的结果再转还能为十六进制返回。
  • deviceSecret:是设备秘钥
  • content:是提交给服务器的参数

    content:clientId+X1+deviceName+X2+productKey+X3+timestamp+X4

  • X1:表示步骤①中的clientId,即MAC地址(假设是1234)
  • X2:表示设备名,此示例中是d001
  • X3:表示设备秘钥,此示例中是k1m123NM9XY
  • X4:表示时间戳,可选,此示例中未使用

替换后:

content:clientId1234deviceNamed001productKeyk1m123NM9XY

功能实现:

//mqttPassword:sign_hmac(deviceSecret, content)
char sign_content[128];
snprintf(sign_content, sizeof(sign_content), "clientId%sdeviceName%sproductKey%s",
         get_clientid(), ALIOT_DEVICENAME, ALIOT_PRODUCTKEY);//获取内容
char *password_pStr = sign_hmac(ALIOT_DEVICESECRET, sign_content);
mqtt_cfg.credentials.authentication.password = password_pStr;

实现具体函数功能:

/** md5算法,散列算法的一种,用于将任意长度字符串转换为固定16位的字符串
 * key: 设备秘钥
 */
void cal_md5(char *key, char *content, unsigned char *output)
{
    mbedtls_md_context_t md5_ctx;
    //取得指定算法的摘要信息
    const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);

    mbedtls_md_init(&md5_ctx);//初始化算法
    mbedtls_md_setup(&md5_ctx, md_info, 1);//设置要指定的算法,并使用hmac算法
    mbedtls_md_hmac_starts(&md5_ctx, (const unsigned char *)key, strlen(key));//开始计算
    mbedtls_md_hmac_update(&md5_ctx, (const unsigned char *)content, strlen(content));
    mbedtls_md_hmac_finish(&md5_ctx, (unsigned char *)output);
    mbedtls_md_free(&md5_ctx);
}

/* 将16进制数转换为字符串 */
void hex2str(uint8_t *input, uint32_t input_len, char *output)
{
    char *upper = "0123456789ABCDEF";
    int i = 0;
    int j = 0;
    for (i = 0; i< input_len; i++)
    {
        output[j++] = upper[(input[i]>>4)&0x0F];
        output[j++] = upper[(input[i])&0x0F];
    }
    output[j] = 0;
}

char password_buf[33];
char *sign_hmac(char *deviceScrect, char *content)
{
    unsigned char password_hex[16];
    cal_md5(deviceScrect, content, password_hex);//计算出16进制的密码
    hex2str(password_hex, 16, password_buf);//将16进制数转换为字符串
    return password_buf;
}
(3)配置阿里云物联网平台TLS自签名证书
mqtt_cfg.broker.verification.certificate = ali_iot_ca;
(4)初始化MQTT客户端
mqtt_handle = esp_mqtt_client_init(&mqtt_cfg);

3.3.4 注册事件响应回调函数

核心函数:

esp_err_t esp_mqtt_client_register_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, esp_event_handler_t event_handler, void *event_handler_arg)

关键步骤:注册回调函数mqtt_event_callback

esp_mqtt_client_register_event(mqtt_handle, ESP_EVENT_ANY_ID, mqtt_event_callback, NULL);//注册事件响应回调函数

回调函数实现:

void mqtt_event_callback(void *event_handler_arg,
                         esp_event_base_t event_base,
                         int32_t event_id,
                         void *event_data)
{
    esp_mqtt_event_handle_t data = (esp_mqtt_event_handle_t)event_data;
    switch (event_id)
    {
        case MQTT_EVENT_CONNECTED:
            ESP_LOGI(TAG, "aliot mqtt connected");
            break;
        case MQTT_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "aliot mqtt disconnected");
            break;
        case MQTT_EVENT_PUBLISHED:
            ESP_LOGI(TAG, "aliot mqtt published ack");
            break;
        case MQTT_EVENT_SUBSCRIBED:
            ESP_LOGI(TAG, "aliot mqtt subcribed ack");
            break;
        case MQTT_EVENT_DATA:
            ESP_LOGI(TAG, "topic->%s", data->topic);
            ESP_LOGI(TAG, "payload->%s", data->data);
            break;
        default: break;
    }
}

3.3.5 启动MQTT客户端

esp_mqtt_client_start(mqtt_handle);

3.3.6 完整代码

mqtt_aliot.h完整代码:

#ifndef _MQTT_ALIOT_H_
#define _MQTT_ALIOT_H_

#define ALIOT_PRODUCTKEY        "k1m123NM9XY"                       //产品秘钥
#define ALIOT_DEVICENAME        "d001"                              //设备名称
#define ALIOT_DEVICESECRET      "eb7a380d7c24160ec2d255cfce7bef70"  //设备秘钥

//域名
// #define ALIOT_MQTT_URL          "mqtts://"ALIOT_PRODUCTKEY".iot-as-mqtt.cn-shanghai.aliyuncs.com"   //旧版
#define ALIOT_MQTT_URL          "mqtts://iot-06z00cmxnxfz6pg.mqtt.iothub.aliyuncs.com"  //新版(公版)  两版均可

/* 启动阿里云IoT连接 */
void mqtt_start(void);

#endif

mqtt_aliot.c完整代码:

#include "mqtt_aliot.h"
#include "esp_log.h"
#include "mqtt_client.h"
#include "mbedtls/md5.h"
#include "mbedtls/md.h"
#include "esp_wifi.h"
#include <stdio.h>

#define TAG     "aliot"

//阿里云物联网平台自签名证书:
char *ali_iot_ca = "\"-----BEGIN CERTIFICATE-----\n"
    "MIID3zCCAsegAwIBAgISfiX6mTa5RMUTGSC3rQhnestIMA0GCSqGSIb3DQEBCwUA"
    "MHcxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8GA1UEBwwISGFu"
    "Z3pob3UxEzARBgNVBAoMCkFsaXl1biBJb1QxEDAOBgNVBAsMB1Jvb3QgQ0ExGzAZ"
    "BgNVBAMMEkFsaXl1biBJb1QgUm9vdCBDQTAgFw0yMzA3MDQwNjM2NThaGA8yMDUz"
    "MDcwNDA2MzY1OFowdzELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREw"
    "DwYDVQQHDAhIYW5nemhvdTETMBEGA1UECgwKQWxpeXVuIElvVDEQMA4GA1UECwwH"
    "Um9vdCBDQTEbMBkGA1UEAwwSQWxpeXVuIElvVCBSb290IENBMIIBIjANBgkqhkiG"
    "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoK//6vc2oXhnvJD7BVhj6grj7PMlN2N4iNH4"
    "GBmLmMdkF1z9eQLjksYc4Zid/FX67ypWFtdycOei5ec0X00m53Gvy4zLGBo2uKgi"
    "T9IxMudmt95bORZbaph4VK82gPNU4ewbiI1q2loRZEHRdyPORTPpvNLHu8DrYBnY"
    "Vg5feEYLLyhxg5M1UTrT/30RggHpaa0BYIPxwsKyylQ1OskOsyZQeOyPe8t8r2D4"
    "RBpUGc5ix4j537HYTKSyK3Hv57R7w1NzKtXoOioDOm+YySsz9sTLFajZkUcQci4X"
    "aedyEeguDLAIUKiYicJhRCZWljVlZActorTgjCY4zRajodThrQIDAQABo2MwYTAO"
    "BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUkWHoKi2h"
    "DlS1/rYpcT/Ue+aKhP8wHwYDVR0jBBgwFoAUkWHoKi2hDlS1/rYpcT/Ue+aKhP8w"
    "DQYJKoZIhvcNAQELBQADggEBADrrLcBY7gDXN8/0KHvPbGwMrEAJcnF9z4MBxRvt"
    "rEoRxhlvRZzPi7w/868xbipwwnksZsn0QNIiAZ6XzbwvIFG01ONJET+OzDy6ZqUb"
    "YmJI09EOe9/Hst8Fac2D14Oyw0+6KTqZW7WWrP2TAgv8/Uox2S05pCWNfJpRZxOv"
    "Lr4DZmnXBJCMNMY/X7xpcjylq+uCj118PBobfH9Oo+iAJ4YyjOLmX3bflKIn1Oat"
    "vdJBtXCj3phpfuf56VwKxoxEVR818GqPAHnz9oVvye4sQqBp/2ynrKFxZKUaJtk0"
    "7UeVbtecwnQTrlcpWM7ACQC0OO0M9+uNjpKIbksv1s11xu0=\n"
    "-----END CERTIFICATE-----";

static esp_mqtt_client_handle_t mqtt_handle = NULL;

/* 获取客户端ID(自定义):以MAC地址作为客户端ID */
char *get_clientid(void)
{
    uint8_t mac[6];
    static char client_id[32] = {0};
    esp_wifi_get_mac(WIFI_IF_STA, mac);//获取mac地址
    snprintf(client_id, sizeof(client_id), "%02X%02X%02X%02X%02X%02X",
             mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

    return client_id;
}

/** md5算法,散列算法的一种,用于将任意长度字符串转换为固定16位的字符串
 * key: 设备秘钥
 */
void cal_md5(char *key, char *content, unsigned char *output)
{
    mbedtls_md_context_t md5_ctx;
    //取得指定算法的摘要信息
    const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);

    mbedtls_md_init(&md5_ctx);//初始化算法
    mbedtls_md_setup(&md5_ctx, md_info, 1);//设置要指定的算法,并使用hmac算法
    mbedtls_md_hmac_starts(&md5_ctx, (const unsigned char *)key, strlen(key));//开始计算
    mbedtls_md_hmac_update(&md5_ctx, (const unsigned char *)content, strlen(content));
    mbedtls_md_hmac_finish(&md5_ctx, (unsigned char *)output);
    mbedtls_md_free(&md5_ctx);
}

/* 将16进制数转换为字符串 */
void hex2str(uint8_t *input, uint32_t input_len, char *output)
{
    char *upper = "0123456789ABCDEF";
    int i = 0;
    int j = 0;
    for (i = 0; i< input_len; i++)
    {
        output[j++] = upper[(input[i]>>4)&0x0F];
        output[j++] = upper[(input[i])&0x0F];
    }
    output[j] = 0;
}

void mqtt_event_callback(void *event_handler_arg,
                         esp_event_base_t event_base,
                         int32_t event_id,
                         void *event_data)
{
    esp_mqtt_event_handle_t data = (esp_mqtt_event_handle_t)event_data;
    switch (event_id)
    {
        case MQTT_EVENT_CONNECTED:
            ESP_LOGI(TAG, "aliot mqtt connected");
            // esp_mqtt_client_subscribe_single(mqtt_handle, MQTT_TOPOC2, 1);
            break;
        case MQTT_EVENT_DISCONNECTED:
            ESP_LOGI(TAG, "aliot mqtt disconnected");
            break;
        case MQTT_EVENT_PUBLISHED:
            ESP_LOGI(TAG, "aliot mqtt published ack");
            break;
        case MQTT_EVENT_SUBSCRIBED:
            ESP_LOGI(TAG, "aliot mqtt subcribed ack");
            break;
        case MQTT_EVENT_DATA:
            ESP_LOGI(TAG, "topic->%s", data->topic);
            ESP_LOGI(TAG, "payload->%s", data->data);
            break;
        default: break;
    }
}

char password_buf[33];
char *sign_hmac(char *deviceScrect, char *content)
{
    unsigned char password_hex[16];
    cal_md5(deviceScrect, content, password_hex);//计算出16进制的密码
    hex2str(password_hex, 16, password_buf);//将16进制数转换为字符串
    return password_buf;
}

/* 启动阿里云IoT连接 */
void mqtt_start(void)
{
    esp_mqtt_client_config_t mqtt_cfg = {0};
    mqtt_cfg.broker.address.uri = ALIOT_MQTT_URL;
    mqtt_cfg.broker.address.port = 8883;//注意:由于使用了阿里云平台的自签名证书,端口号需改成8883

    //mqttClientId: clientId+"|securemode=3,signmethod=hmacsha1,timestamp=132323232|"
    char client_id[128];
    snprintf(client_id, sizeof(client_id),
             "%s|securemode=2,signmethod=hmacmd5|",// 选择TLS直连,md5加密
             get_clientid()); 
    mqtt_cfg.credentials.client_id = client_id;

    //mqttUsername: deviceName+"&"+productKey
    char user_name[128];
    snprintf(user_name, sizeof(user_name),"%s&%s", ALIOT_DEVICENAME, ALIOT_PRODUCTKEY);
    mqtt_cfg.credentials.username = user_name; 

    //mqttPassword:sign_hmac(deviceSecret, content)
    char sign_content[128];
    snprintf(sign_content, sizeof(sign_content), "clientId%sdeviceName%sproductKey%s",
             get_clientid(), ALIOT_DEVICENAME, ALIOT_PRODUCTKEY);//获取内容
    #if 0
    unsigned char password_hex[16];
    char password_str[33];
    cal_md5(ALIOT_DEVICESECRET, sign_content, password_hex);//计算出16进制的密码
    hex2str(password_hex, 16, password_str);//将16进制数转换为字符串
    mqtt_cfg.credentials.authentication.password = password_str;
    #else
    char *password_pStr = sign_hmac(ALIOT_DEVICESECRET, sign_content);
    mqtt_cfg.credentials.authentication.password = password_pStr;
    #endif

    //TLS证书
    mqtt_cfg.broker.verification.certificate = ali_iot_ca;

    //发起MQTT连接
    mqtt_handle = esp_mqtt_client_init(&mqtt_cfg);
    esp_mqtt_client_register_event(mqtt_handle, ESP_EVENT_ANY_ID, mqtt_event_callback, NULL);//注册事件响应回调函数
    esp_mqtt_client_start(mqtt_handle);
}

3.4 链接多个文件

在./main/cMakeList.txt文件中添加以下代码,将main.c、wifi.c、mqtt_aliot.c链接起来:

idf_component_register(SRCS "main.c" "wifi.c" "mqtt_aliot.c"
                    INCLUDE_DIRS "")

4.验证功能

设备运行前:

编译-->烧录-->打开串口监视-->打开WiFi热点:

5.注意事项

  • 因为使用了阿里云物联网平台TLS自签名证书,因此端口号要用8883替换1883;
  • MQTT域名需加上"mqtts://"前缀(因为是加密的,所以mqtt后必须加一个s);
  • 要在menuconfig中使能Enable RIPEMD-160 hash algorithm这个哈希算法,否则无法识别阿里云物联网平台自签名证书;

6.参考文档

使用TLS加密设备和物联网平台的MQTT通信_物联网平台(IoT)-阿里云帮助中心

7.源码下载

https://download.csdn.net/download/Freddy_Ssc/90622560


网站公告

今日签到

点亮在社区的每一天
去签到