Files
P4-USB-transfer/main/MQTT/mqtt.c
T

1291 lines
45 KiB
C
Raw Normal View History

2026-10-13 21:54:50 +08:00
// version 1.0.1
#include "esp_mac.h"
#include "MQTT/mqtt.h"
#include "OTA/ota_upgrade.h"
#include "FLASH/flash.h"
#include "config.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "event.h"
#define OTA_UPGRADE_SUCCESS "0"
#define PRODUCT_ID "69719d0ecbb0cf6bb936c2c0"
#include "mbedtls/md.h"
#include "time.h"
char custom_info[32] = {0};
esp_err_t ota_upgrade_init(void);
char object_device_id[70] = {0};
char secret[33] = {0};
static const char *TAG = "example";
bool mqtt_connected = false;
static char *generate_password(const char *secret, const char *timestamp);
static char *mqtt_client_id(char *object_device_id, char *now);
static char *mqtt_event_up(char *object_device_id);
static char *mqtt_event_down(char *object_device_id);
static void generate_device_key(uint64_t chip_id, char *key_buffer, size_t buffer_size);
static void ensure_complexity(char *str, int length);
static int validate_key_complexity(const char *key);
static void strengthen_key(char *key, int length);
void get_object_device_id(char *node_id);
static void transmit_download_success(char *object_name);
static uint64_t chip_id = 0;
static bool is_network_ready(void);
static void log_error_if_nonzero(const char *message, int error_code);
static void log_error_if_nonzero(const char *message, int error_code)
{
if (error_code != 0) {
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
}
}
static esp_mqtt5_user_property_item_t user_property_arr[] = {
{"board", "esp32"},
{"u", "user"},
{"p", "password"}};
#define USE_PROPERTY_ARR_SIZE sizeof(user_property_arr) / sizeof(esp_mqtt5_user_property_item_t)
static esp_mqtt5_publish_property_config_t publish_property = {
.payload_format_indicator = 1,
.message_expiry_interval = 1000,
.topic_alias = 0,
.response_topic = "/topic/test/response",
.correlation_data = "123456",
.correlation_data_len = 6,
};
static esp_mqtt5_subscribe_property_config_t subscribe_property = {
.subscribe_id = 25555,
.no_local_flag = false,
.retain_as_published_flag = false,
.retain_handle = 0,
.is_share_subscribe = true,
.share_name = "group1",
};
static esp_mqtt5_subscribe_property_config_t subscribe1_property = {
.subscribe_id = 25555,
.no_local_flag = true,
.retain_as_published_flag = false,
.retain_handle = 0,
};
static esp_mqtt5_unsubscribe_property_config_t unsubscribe_property = {
.is_share_subscribe = true,
.share_name = "group1",
};
static esp_mqtt5_disconnect_property_config_t disconnect_property = {
.session_expiry_interval = 60,
.disconnect_reason = 0,
};
static void print_user_property(mqtt5_user_property_handle_t user_property)
{
if (user_property)
{
uint8_t count = esp_mqtt5_client_get_user_property_count(user_property);
if (count)
{
esp_mqtt5_user_property_item_t *item = malloc(count * sizeof(esp_mqtt5_user_property_item_t));
if (esp_mqtt5_client_get_user_property(user_property, item, &count) == ESP_OK)
{
for (int i = 0; i < count; i++)
{
esp_mqtt5_user_property_item_t *t = &item[i];
ESP_LOGI(TAG, "key is %s, value is %s", t->key, t->value);
free((char *)t->key);
free((char *)t->value);
}
}
free(item);
}
}
}
static char *mqtt_client_id(char *object_device_id, char *now)
{
static char client_id[256]; // 静态变量,生命周期持续到程序结束
snprintf(client_id, sizeof(client_id), "%s_0_0_%s", object_device_id, now);
return client_id;
}
static char *mqtt_event_up(char *object_device_id)
{
static char event_topic[256]; // 静态变量,生命周期持续到程序结束
snprintf(event_topic, sizeof(event_topic), "%s/%s/%s",
"$oc/devices", object_device_id, "sys/events/up");
return event_topic;
}
static char *mqtt_event_down(char *object_device_id)
{
static char event_topic[256]; // 静态变量,生命周期持续到程序结束
snprintf(event_topic, sizeof(event_topic), "%s/%s/%s",
"$oc/devices", object_device_id, "sys/events/down");
return event_topic;
}
/*
* @brief Event handler registered to receive MQTT events
*
* This function is called by the MQTT client event loop.
*
* @param handler_args user data registered to the event.
* @param base Event base for the handler(always MQTT Base in this example).
* @param event_id The id for the received event.
* @param event_data The data for the event, esp_mqtt_event_handle_t.
*/
static cJSON *create_data(char *service_id, char *event_type)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "object_device_id", object_device_id);
cJSON *services = cJSON_CreateArray(); //
cJSON *service_object = cJSON_CreateObject();
cJSON_AddItemToArray(services, service_object);
cJSON *paras = cJSON_CreateObject();
cJSON_AddItemToObject(service_object, "paras", paras);
cJSON_AddItemToObject(root, "services", services);
cJSON_AddStringToObject(service_object, "service_id", service_id);
cJSON_AddStringToObject(service_object, "event_type", event_type);
return root;
}
esp_err_t subscribe_ota()
{
ESP_LOGI(TAG, "[DEBUG] subscribe_ota 被调用,mqtt_connected=%d", mqtt_connected ? 1 : 0);
esp_err_t err = publish_json_download("icarota/mqtt5.bin");
if (err != ESP_OK)
{
ESP_LOGE(TAG, "[DEBUG] 发布下载请求失败");
return ESP_FAIL;
}
ESP_LOGI(TAG, "[DEBUG] 发布 OTA 下载请求成功");
return ESP_OK;
// // 订阅OTA升级主题
// if(mqtt_connected){
// int msg_id = esp_mqtt_client_subscribe(client, mqtt_event_down(object_device_id), 1);
// ESP_LOGI(TAG, "订阅OTA主题, msg_id=%d", msg_id);
// return ESP_OK;
// }
// return ESP_FAIL;
}
// //下载多个文件
// esp_err_t download_files(cJSON* root){
// }
// 下载单个文件
esp_err_t publish_json_download(char *filename)
{
ESP_LOGI(TAG, "[DEBUG] publish_json_download(%s) mqtt_connected=%d", filename ? filename : "NULL", mqtt_connected ? 1 : 0);
if (!mqtt_connected)
{
ESP_LOGW(TAG, "[DEBUG] MQTT 未连接,无法发布");
return ESP_FAIL;
}
// 上报获取文件下载URL请求
cJSON *root = create_data("$file_manager", "get_download_url");
cJSON *services = cJSON_GetObjectItem(root, "services");
cJSON *service_object = cJSON_GetArrayItem(services, 0); //
cJSON *paras = cJSON_GetObjectItem(service_object, "paras");
cJSON_AddStringToObject(paras, "file_name", filename);
int array_size = cJSON_GetArraySize(services);
ESP_LOGI(TAG, "✅ services数组大小: %d", array_size);
char *response_data = cJSON_PrintUnformatted(root);
print_json_structure_recursive(root, 0);
int msg_id = esp_mqtt_client_publish(client, mqtt_event_up(object_device_id), response_data, 0, 1, 0);
ESP_LOGI(TAG, "订阅OTA主题, msg_id=%d", msg_id);
return ESP_OK;
}
static void mqtt3_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32, base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
ESP_LOGD(TAG, "free heap size is %" PRIu32 ", minimum %" PRIu32, esp_get_free_heap_size(), esp_get_minimum_free_heap_size());
switch ((esp_mqtt_event_id_t)event_id)
{
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "[DEBUG] MQTT_EVENT_CONNECTED");
if (!mqtt_connected)
mqtt_connected = true;
// 订阅其他主题(如果需要)
// msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 2);
// ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
// 取消订阅(如果需要)
// msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos0");
// ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
// 取消订阅后断开连接(根据需求决定是否需要)
// esp_mqtt_client_disconnect(client);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
ESP_LOGI(TAG, "TOPIC=%.*s", event->topic_len, event->topic);
ESP_LOGI(TAG, "DATA=%.*s", event->data_len, event->data);
// 处理接收到的数据
handle_received_data(event->topic, event->topic_len, event->data, event->data_len);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
if (event->error_handle)
{
ESP_LOGI(TAG, "Error type: %d", event->error_handle->error_type);
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT)
{
log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
}
}
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}
// 添加数据处理函数(如果需要)
void handle_received_data(const char *topic, int topic_len, const char *data, int data_len)
{
char topic_str[256] = {0};
char data_str[2048] = {0};
// 复制主题和数据到缓冲区
memcpy(topic_str, topic, topic_len < 255 ? topic_len : 255);
memcpy(data_str, data, data_len < 2048 ? data_len : 2048);
ESP_LOGI(TAG, "处理消息 - 主题: %s, 数据: %s", topic_str, data_str);
// 根据不同的主题进行不同的处理
if (strstr(topic_str, "/sys/commands/request_id=") != NULL)
{
// 处理平台下发的命令(版本查询)
cJSON *root = cJSON_Parse(data);
cJSON *services = cJSON_GetObjectItem(root, "services");
cJSON *services_id = cJSON_GetObjectItem(services, "service_id");
// if(strcmp(services_id->valuestring, "$ota") == 0)
// report_firmware_version();
}
else if (strstr(topic_str, "/events/down") != NULL)
{
// 处理华为云平台的其他消息
ESP_LOGI(TAG, "收到华为云平台事件");
// 🚨 5. 递归打印所有字段
ESP_LOGI(TAG, "=== 完整字段结构 ===");
print_json_structure_recursive(cJSON_Parse(data), 0);
handle_event(data, data_len);
}
}
// === 新增:事件命令 ===
void handle_event(const char *data, int data_len)
{
ESP_LOGI(TAG, "收到事件命令,数据长度: %d", data_len);
if (data == NULL || data_len <= 0)
{
ESP_LOGE(TAG, "❌ 输入数据无效");
return;
}
ESP_LOGI(TAG, "data: %s", data);
cJSON *root = cJSON_Parse(data);
if (root == NULL)
{
ESP_LOGE(TAG, "❌ JSON解析失败");
return;
}
// 处理ota命令
cJSON *object_device_id = cJSON_GetObjectItem(root, "object_device_id");
cJSON *services = cJSON_GetObjectItem(root, "services");
cJSON *first_service = cJSON_GetArrayItem(services, 0);
cJSON *event_type = cJSON_GetObjectItem(first_service, "event_type");
cJSON *service_id = cJSON_GetObjectItem(first_service, "service_id");
cJSON *event_time = cJSON_GetObjectItem(first_service, "event_time");
cJSON *paras = cJSON_GetObjectItem(first_service, "paras");
strncpy(time_event, event_time->valuestring, sizeof(time_event) - 1);
// ESP_LOGI(TAG, "请求ID: %s, 命令: %s", current_request_id, command_obj->valuestring);
// 首先检查指针是否有效
if (object_device_id == NULL || object_device_id->valuestring == NULL ||
event_type == NULL || event_type->valuestring == NULL ||
service_id == NULL || service_id->valuestring == NULL)
{
// 处理空指针情况,比如打印错误日志并返回
ESP_LOGE(TAG, "Error: NULL pointer in JSON fields");
return;
}
if (strcmp(service_id->valuestring, "$ota") == 0)
{
if (strcmp(event_type->valuestring, "version_query") == 0)
{
// strncpy(current_request_id, request_id_obj->valuestring, sizeof(current_request_id)-1);
// ESP_LOGI(TAG, "请求ID: %s, 命令: %s", current_request_id, command_obj->valuestring);
// 检查是否是版本查询命令
// if (strcmp(command_obj->valuestring, "query_version") == 0) {
// 创建3分钟超时定时器
setup_ota_timeout_timer();
// 立即响应版本信息
report_firmware_version(event_time->valuestring);
//}
}
else if (strcmp(event_type->valuestring, "firmware_upgrade_v2") == 0)
{
cJSON *url = cJSON_GetObjectItem(paras, "url");
cJSON *version_recive = cJSON_GetObjectItem(paras, "version");
ESP_LOGI(TAG, "[DEBUG] 收到 firmware_upgrade_v2, url=%s", url && url->valuestring ? url->valuestring : "NULL");
strncpy(version, version_recive->valuestring, sizeof(version) - 1);
ota_upgrade_init();
trigger_ota_upgrade(url->valuestring);
// cJSON_Delete(root);
// esp_mqtt_client_destroy(client);
}
}
else if (strcmp(service_id->valuestring, "$file_manager") == 0)
{
if (strcmp(event_type->valuestring, "get_download_url_response") == 0)
{
cJSON *url = cJSON_GetObjectItem(paras, "url");
cJSON *object_name = cJSON_GetObjectItem(paras, "object_name");
if (url && url->valuestring && object_name && object_name->valuestring)
{
char *url_copy = strdup(url->valuestring);
char *filename = object_name->valuestring;
if (filename && filename[0] != '\0')
{
/* 本地保存用文件名:取第一个 '/' 后的部分,若无 '/' 则用整串,避免传空导致 /extflash/ 报错 */
char *local_name = strchr(filename, '/');
if (local_name != NULL)
local_name++;
if (local_name == NULL || local_name[0] == '\0')
local_name = filename;
size_t len = strlen(filename);
if (len >= 4)
{
if (strcmp(filename + len - 4, ".bin") == 0)
{
ESP_LOGI(TAG, "[DEBUG] 收到 .bin 文件 URL,触发 OTA: %s", url->valuestring);
ota_upgrade_init();
trigger_ota_upgrade(url->valuestring);
}
else if (download_file(url_copy, local_name) == ESP_OK)
{
transmit_download_success(object_name->valuestring);
}
else
{
/* HTTP 下载失败(网络超时、断线等),反馈失败结果 */
ESP_LOGW(TAG, "表情包 HTTP 下载失败,已设置 DOWNLOAD_FAILED_BIT");
set_system_event(DOWNLOAD_FAILED_BIT);
}
}
}
free(url_copy);
}
}
}
cJSON_Delete(root);
}
void report_firmware_version(char *event_time)
{
cJSON *root = create_data("$ota", "version_report");
// 构建版本信息JSON
cJSON *services = cJSON_GetObjectItem(root, "services");
cJSON *service_object = cJSON_GetArrayItem(services, 0); //
cJSON *paras = cJSON_GetObjectItem(service_object, "paras");
cJSON_AddStringToObject(paras, "fw_version", DEVICE_FIRMWARE_VERSION);
cJSON_AddStringToObject(paras, "sw_version", DEVICE_FIRMWARE_VERSION);
int array_size = cJSON_GetArraySize(services);
ESP_LOGI(TAG, "✅ services数组大小: %d", array_size);
char *response_data = cJSON_PrintUnformatted(root);
print_json_structure_recursive(root, 0);
// 发布版本信息
int msg_id = esp_mqtt_client_publish(client, mqtt_event_up(object_device_id), response_data, 0, 1, 0);
ESP_LOGI(TAG, "上报固件版本: %s, 主题: %s, msg_id: %d",
DEVICE_FIRMWARE_VERSION, mqtt_event_up(object_device_id), msg_id);
// 清理资源和停止定时器
free(response_data);
cJSON_Delete(root);
clear_ota_timeout_timer();
memset(current_request_id, 0, sizeof(current_request_id));
}
// === 新增:处理OTA升级命令 ===
void handle_ota_command(const char *data)
{
ESP_LOGI(TAG, "收到OTA升级命令");
cJSON *root = cJSON_Parse(data);
if (root == NULL)
{
ESP_LOGE(TAG, "解析OTA命令JSON失败");
return;
}
// 解析OTA升级信息
cJSON *paras = cJSON_GetObjectItem(root, "paras");
if (paras)
{
cJSON *url = cJSON_GetObjectItem(paras, "url");
cJSON *version = cJSON_GetObjectItem(paras, "version");
cJSON *file_size = cJSON_GetObjectItem(paras, "file_size");
if (url && version)
{
ESP_LOGI(TAG, "OTA升级信息:");
ESP_LOGI(TAG, " 目标版本: %s", version->valuestring);
ESP_LOGI(TAG, " 当前版本: %s", DEVICE_FIRMWARE_VERSION);
ESP_LOGI(TAG, " 下载URL: %s", url->valuestring);
ESP_LOGI(TAG, " 文件大小: %s", file_size ? file_size->valuestring : "unknown");
// 比较版本号,判断是否需要升级
if (strcmp(version->valuestring, DEVICE_FIRMWARE_VERSION) != 0)
{
ESP_LOGI(TAG, "需要升级: %s -> %s", DEVICE_FIRMWARE_VERSION, version->valuestring);
// 这里可以触发OTA升级流程
// start_ota_upgrade(url->valuestring, version->valuestring);
}
else
{
ESP_LOGI(TAG, "当前已是最新版本,无需升级");
}
}
}
cJSON_Delete(root);
}
// === 新增:3分钟超时定时器处理 ===
void ota_timeout_handler(void *arg)
{
if (strlen(current_request_id) > 0)
{
ESP_LOGE(TAG, "版本查询响应超时(3分钟)");
// 上报超时错误
char response_topic[256];
snprintf(response_topic, sizeof(response_topic),
"$oc/devices/691eeb17bf22cc5a8c0857d0_ESP_1/sys/commands/response/request_id=%s",
current_request_id);
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "result_code", "1");
cJSON_AddStringToObject(root, "result_desc", "timeout");
char *response_data = cJSON_PrintUnformatted(root);
// esp_mqtt_client_handle_t client = esp_mqtt_client_get_handle();
esp_mqtt_client_publish(client, response_topic, response_data, 0, 1, 0);
free(response_data);
cJSON_Delete(root);
// 清理状态
memset(current_request_id, 0, sizeof(current_request_id));
ota_timeout_timer = NULL;
}
}
// === 新增:设置超时定时器 ===
void setup_ota_timeout_timer(void)
{
const esp_timer_create_args_t timer_args = {
.callback = &ota_timeout_handler,
.name = "ota_timeout"};
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &ota_timeout_timer));
ESP_ERROR_CHECK(esp_timer_start_once(ota_timeout_timer, 180000000)); // 3分钟(180秒 * 1000000微秒)
ESP_LOGI(TAG, "启动3分钟超时定时器");
}
// === 新增:清除超时定时器 ===
void clear_ota_timeout_timer(void)
{
if (ota_timeout_timer)
{
esp_timer_stop(ota_timeout_timer);
esp_timer_delete(ota_timeout_timer);
ota_timeout_timer = NULL;
ESP_LOGI(TAG, "清除超时定时器");
}
}
// === 新增:获取MQTT客户端句柄的辅助函数 ===
static esp_mqtt_client_handle_t esp_mqtt_client_get_handle(void)
{
// 这个函数需要根据你的实际实现来获取客户端句柄
// 这里只是一个示例,你可能需要修改为你的实际实现
static esp_mqtt_client_handle_t s_client = NULL;
// 你需要确保在mqtt5_app_start中保存client句柄到s_client
return s_client;
}
// 递归打印JSON结构
static void print_json_structure_recursive(cJSON *item, int depth)
{
if (item == NULL)
return;
char indent[64] = {0};
for (int i = 0; i < depth * 2 && i < 62; i++)
{
indent[i] = ' ';
}
if (item->string)
{
ESP_LOGI(TAG, "%s%s:", indent, item->string);
}
if (cJSON_IsObject(item))
{
cJSON *child = item->child;
while (child)
{
print_json_structure_recursive(child, depth + 1);
child = child->next;
}
}
else if (cJSON_IsArray(item))
{
ESP_LOGI(TAG, "%s [数组,大小: %d]", indent, cJSON_GetArraySize(item));
cJSON *child = item->child;
int index = 0;
while (child)
{
ESP_LOGI(TAG, "%s [%d]:", indent, index++);
print_json_structure_recursive(child, depth + 2);
child = child->next;
}
}
else if (cJSON_IsString(item))
{
ESP_LOGI(TAG, "%s 字符串: %s", indent, item->valuestring);
}
else if (cJSON_IsNumber(item))
{
ESP_LOGI(TAG, "%s 数字: %f", indent, item->valuedouble);
}
}
// 手动查找字段
static void find_field_manually(cJSON *root, const char *field_name)
{
if (root == NULL || field_name == NULL)
return;
// 广度优先搜索
cJSON *stack[100] = {0};
int stack_top = 0;
stack[stack_top++] = root;
while (stack_top > 0)
{
cJSON *current = stack[--stack_top];
if (current->string && strcmp(current->string, field_name) == 0)
{
ESP_LOGI(TAG, "✅ 手动找到字段: %s", field_name);
if (cJSON_IsString(current))
{
ESP_LOGI(TAG, " 值: %s", current->valuestring);
}
return;
}
if (cJSON_IsObject(current) || cJSON_IsArray(current))
{
cJSON *child = current->child;
while (child)
{
if (stack_top < 100)
{
stack[stack_top++] = child;
}
child = child->next;
}
}
}
ESP_LOGI(TAG, "❌ 手动查找未找到字段: %s", field_name);
}
// static void mqtt5_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
// {
// ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32, base, event_id);
// esp_mqtt_event_handle_t event = event_data;
// esp_mqtt_client_handle_t client = event->client;
// int msg_id;
// ESP_LOGD(TAG, "free heap size is %" PRIu32 ", minimum %" PRIu32, esp_get_free_heap_size(), esp_get_minimum_free_heap_size());
// switch ((esp_mqtt_event_id_t)event_id) {
// case MQTT_EVENT_CONNECTED:
// ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
// print_user_property(event->property->user_property);
// esp_mqtt5_client_set_user_property(&publish_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE);
// esp_mqtt5_client_set_publish_property(client, &publish_property);
// msg_id = esp_mqtt_client_publish(client, "$oc/devices/691eeb17bf22cc5a8c0857d0_ESP_1/sys/messages/up", "data_3", 0, 1, 0);
// esp_mqtt5_client_delete_user_property(publish_property.user_property);
// publish_property.user_property = NULL;
// ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
// esp_mqtt5_client_set_user_property(&subscribe_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE);
// esp_mqtt5_client_set_subscribe_property(client, &subscribe_property);
// msg_id = esp_mqtt_client_subscribe(client, "$oc/devices/691eeb17bf22cc5a8c0857d0_ESP_1/sys/messages/down", 0);
// esp_mqtt5_client_delete_user_property(subscribe_property.user_property);
// subscribe_property.user_property = NULL;
// ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
// esp_mqtt5_client_set_user_property(&subscribe1_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE);
// esp_mqtt5_client_set_subscribe_property(client, &subscribe1_property);
// msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 2);
// esp_mqtt5_client_delete_user_property(subscribe1_property.user_property);
// subscribe1_property.user_property = NULL;
// ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
// esp_mqtt5_client_set_user_property(&unsubscribe_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE);
// esp_mqtt5_client_set_unsubscribe_property(client, &unsubscribe_property);
// msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos0");
// ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
// esp_mqtt5_client_delete_user_property(unsubscribe_property.user_property);
// unsubscribe_property.user_property = NULL;
// break;
// case MQTT_EVENT_DISCONNECTED:
// ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
// print_user_property(event->property->user_property);
// break;
// case MQTT_EVENT_SUBSCRIBED:
// ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
// print_user_property(event->property->user_property);
// esp_mqtt5_client_set_publish_property(client, &publish_property);
// msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
// ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
// break;
// case MQTT_EVENT_UNSUBSCRIBED:
// ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
// print_user_property(event->property->user_property);
// esp_mqtt5_client_set_user_property(&disconnect_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE);
// esp_mqtt5_client_set_disconnect_property(client, &disconnect_property);
// esp_mqtt5_client_delete_user_property(disconnect_property.user_property);
// disconnect_property.user_property = NULL;
// esp_mqtt_client_disconnect(client);
// break;
// case MQTT_EVENT_PUBLISHED:
// ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
// print_user_property(event->property->user_property);
// break;
// case MQTT_EVENT_DATA:
// ESP_LOGI(TAG, "MQTT_EVENT_DATA");
// print_user_property(event->property->user_property);
// ESP_LOGI(TAG, "payload_format_indicator is %d", event->property->payload_format_indicator);
// ESP_LOGI(TAG, "response_topic is %.*s", event->property->response_topic_len, event->property->response_topic);
// ESP_LOGI(TAG, "correlation_data is %.*s", event->property->correlation_data_len, event->property->correlation_data);
// ESP_LOGI(TAG, "content_type is %.*s", event->property->content_type_len, event->property->content_type);
// ESP_LOGI(TAG, "TOPIC=%.*s", event->topic_len, event->topic);
// ESP_LOGI(TAG, "DATA=%.*s", event->data_len, event->data);
// break;
// case MQTT_EVENT_ERROR:
// ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
// print_user_property(event->property->user_property);
// ESP_LOGI(TAG, "MQTT5 return code is %d", event->error_handle->connect_return_code);
// if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
// log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
// log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
// log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
// ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
// }
// break;
// default:
// ESP_LOGI(TAG, "Other event id:%d", event->event_id);
// break;
// }
// }
static void mqtt5_app_start(void)
{
// esp_mqtt5_connection_property_config_t connect_property = {
// .session_expiry_interval = 10,
// .maximum_packet_size = 1024,
// .receive_maximum = 65535,
// .topic_alias_maximum = 2,
// .request_resp_info = true,
// .request_problem_info = true,
// .will_delay_interval = 10,
// .payload_format_indicator = true,
// .message_expiry_interval = 10,
// .response_topic = "$oc/devices/691eeb17bf22cc5a8c0857d0_ESP_1/sys/messages/down",
// .correlation_data = "123456",
// .correlation_data_len = 6,
// };
time_t now = time(NULL);
struct tm *timeinfo = localtime(&now);
char time_str[15]; // 10位数字 + 结束符 = 11字符,但多分配一些更安全
strftime(time_str, sizeof(time_str), "%Y%m%d%H", timeinfo);
esp_mqtt_client_config_t mqtt5_cfg = {
.session.disable_clean_session = false,
.broker.address.uri = "mqtts://7577d49692.st1.iotda-device.cn-north-4.myhuaweicloud.com:8883", // CONFIG_BROKER_URL,
.session.protocol_ver = MQTT_PROTOCOL_V_3_1_1,
.network.disable_auto_reconnect = true,
.credentials.client_id = mqtt_client_id(object_device_id, time_str), //"691eeb17bf22cc5a8c0857d0_ESP_1_0_0_2025112013",
.credentials.username = object_device_id, //"691eeb17bf22cc5a8c0857d0_ESP_1",
.credentials.authentication.password = generate_password(secret, time_str), //"265bbf5df810c722c4a9894ded6708c2bcd6061f61397f2ceec995127f31471b",
.task.stack_size = 12288,
.buffer.out_size = 12288,
.buffer.size = 12288,
.task.priority = 15,
.broker.verification.certificate = NULL, // 不使用证书验证
.broker.verification.skip_cert_common_name_check = true, // 跳过CN检查
//.session.last_will.topic = "/topic/will",
//.session.last_will.msg = "i will leave",
//.session.last_will.msg_len = 12,
//.session.last_will.qos = 0,
//.session.last_will.retain = true,
};
#if CONFIG_BROKER_URL_FROM_STDIN
char line[128];
if (strcmp(mqtt5_cfg.uri, "FROM_STDIN") == 0)
{
int count = 0;
printf("Please enter url of mqtt broker\n");
while (count < 128)
{
int c = fgetc(stdin);
if (c == '\n')
{
line[count] = '\0';
break;
}
else if (c > 0 && c < 127)
{
line[count] = c;
++count;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
mqtt5_cfg.broker.address.uri = line;
printf("Broker url: %s\n", line);
}
else
{
ESP_LOGE(TAG, "Configuration mismatch: wrong broker url");
abort();
}
#endif /* CONFIG_BROKER_URL_FROM_STDIN */
/* 若已有 client 且未连接,只尝试重连,避免重复创建导致 "Error create mqtt task"(重复创建任务失败) */
if (client != NULL)
{
if (mqtt_connected)
return;
ESP_LOGI(TAG, "MQTT client already exists, reconnecting...");
esp_mqtt_client_reconnect(client);
return;
}
client = esp_mqtt_client_init(&mqtt5_cfg);
if (client == NULL)
{
ESP_LOGE(TAG, "[DEBUG] Failed to create MQTT client (init returned NULL)");
return;
}
ESP_LOGI(TAG, "[DEBUG] MQTT client 初始化成功,准备 start");
client_http = client;
/* Set connection properties and user properties */
// esp_mqtt5_client_set_user_property(&connect_property.user_property, user_property_arr, USE_PROPERTY_ARR_SIZE);
// esp_mqtt5_client_set_user_property(&connect_property.will_user_property, user_property_arr, USE_PROPERTY_ARR_SIZE);
// esp_mqtt5_client_set_connect_property(client, &connect_property);
/* If you call esp_mqtt5_client_set_user_property to set user properties, DO NOT forget to delete them.
* esp_mqtt5_client_set_connect_property will malloc buffer to store the user_property and you can delete it after
*/
// esp_mqtt5_client_delete_user_property(connect_property.user_property);
// esp_mqtt5_client_delete_user_property(connect_property.will_user_property);
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt3_event_handler, NULL);
esp_err_t err = esp_mqtt_client_start(client);
ESP_LOGI(TAG, "[DEBUG] esp_mqtt_client_start 返回: %s", err == ESP_OK ? "OK" : esp_err_to_name(err));
if (err != ESP_OK)
{
ESP_LOGE(TAG, "esp_mqtt_client_start failed: %s", esp_err_to_name(err));
}
}
void transmit_ota_success(void)
{
cJSON *root = create_data("$ota", "upgrade_progress_report");
// 构建版本信息JSON
cJSON *services = cJSON_GetObjectItem(root, "services");
cJSON *service_object = cJSON_GetArrayItem(services, 0); //
cJSON *paras = cJSON_GetObjectItem(service_object, "paras");
cJSON_AddNumberToObject(paras, "result_code", 0);
cJSON_AddStringToObject(paras, "version", version);
int array_size = cJSON_GetArraySize(services);
ESP_LOGI(TAG, "✅ services数组大小: %d", array_size);
char *response_data = cJSON_PrintUnformatted(root);
print_json_structure_recursive(root, 0);
// 发布OTA成功升级信息
int msg_id = esp_mqtt_client_publish(client_http, mqtt_event_up(object_device_id), response_data, 0, 1, 0);
ESP_LOGI(TAG, "上报固件版本: %s, 主题: %s, msg_id: %d",
version, mqtt_event_up(object_device_id), msg_id);
}
static void transmit_download_success(char *object_name)
{
cJSON *root = create_data("$file_manager", "download_result_report");
cJSON *services = cJSON_GetObjectItem(root, "services");
cJSON *service = cJSON_GetArrayItem(services, 0);
cJSON *paras = cJSON_GetObjectItem(service, "paras");
cJSON_AddNumberToObject(paras, "result_code", 0);
cJSON_AddStringToObject(paras, "object_name", object_name);
int array_size = cJSON_GetArraySize(services);
ESP_LOGI(TAG, "✅ services数组大小: %d", array_size);
char *response_data = cJSON_PrintUnformatted(root);
print_json_structure_recursive(root, 0);
// 发布文件下载成功信息
int msg_id = esp_mqtt_client_publish(client_http, mqtt_event_up(object_device_id), response_data, 0, 1, 0);
}
static char *generate_password(const char *secret, const char *timestamp)
{
// 定义HMAC-SHA256上下文
mbedtls_md_context_t ctx;
unsigned char hmac_result[32]; // SHA256结果为32字节
static char password[65]; // 64字符十六进制字符串 + 结束符
// 初始化上下文
mbedtls_md_init(&ctx);
// 设置使用SHA256哈希算法
const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (info == NULL)
{
printf("Error: SHA256 not available\n");
return NULL;
}
// 设置HMAC密钥和初始化
if (mbedtls_md_setup(&ctx, info, 1) != 0)
{
printf("Error: mbedtls_md_setup failed\n");
mbedtls_md_free(&ctx);
return NULL;
}
// 根据描述:时间戳为密钥,secret为内容
// 所以密钥是timestamp,消息是secret
if (mbedtls_md_hmac_starts(&ctx, (const unsigned char *)timestamp, strlen(timestamp)) != 0)
{
printf("Error: mbedtls_md_hmac_starts failed\n");
mbedtls_md_free(&ctx);
return NULL;
}
// 添加secret数据
if (mbedtls_md_hmac_update(&ctx, (const unsigned char *)secret, strlen(secret)) != 0)
{
printf("Error: mbedtls_md_hmac_update failed\n");
mbedtls_md_free(&ctx);
return NULL;
}
// 完成HMAC计算
if (mbedtls_md_hmac_finish(&ctx, hmac_result) != 0)
{
printf("Error: mbedtls_md_hmac_finish failed\n");
mbedtls_md_free(&ctx);
return NULL;
}
// 清理上下文
mbedtls_md_free(&ctx);
// 将二进制结果转换为十六进制字符串
for (int i = 0; i < 32; i++)
{
sprintf(&password[i * 2], "%02x", hmac_result[i]);
}
password[64] = '\0'; // 确保字符串正确终止
return password;
}
/**
* @brief 基于芯片ID生成符合华为云要求的设备密钥
* @param chip_id 64位芯片唯一ID
* @param key_buffer 输出缓冲区,最小33字节
* @param buffer_size 缓冲区大小
* @return 生成的密钥字符串,失败返回NULL
*/
static void generate_device_key(uint64_t chip_id, char *key_buffer, size_t buffer_size)
{
// 将芯片ID转换为字节数组进行处理
uint8_t id_bytes[8];
for (int i = 0; i < 8; i++)
{
id_bytes[i] = (chip_id >> (56 - i * 8)) & 0xFF;
}
// 步骤1: 基础转换 - 将芯片ID转换为基础字符串
char base_string[33]; // 32字符 + 结束符
// 使用芯片ID字节的多种组合生成基础字符串
int pos = 0;
for (int i = 0; i < 8; i++)
{
uint8_t byte_val = id_bytes[i];
// 每个字节生成4个字符:数字、小写字母、大写字母、特殊字符
// 数字: 0-9
base_string[pos++] = '0' + (byte_val % 10);
// 小写字母: a-z
base_string[pos++] = 'a' + (byte_val % 26);
// 大写字母: A-Z
base_string[pos++] = 'A' + (byte_val % 26);
// 特殊字符: - 或 _
base_string[pos++] = (byte_val % 2) ? '-' : '_';
}
base_string[32] = '\0';
// 步骤2: 应用简单加密变换
char transformed[33];
for (int i = 0; i < 32; i++)
{
// 基于芯片ID的简单异或加密
uint8_t key_byte = id_bytes[i % 8];
char orig_char = base_string[i];
char new_char;
// 保持字符在允许范围内
if (orig_char >= '0' && orig_char <= '9')
{
new_char = '0' + ((orig_char - '0' + key_byte) % 10);
}
else if (orig_char >= 'a' && orig_char <= 'z')
{
new_char = 'a' + ((orig_char - 'a' + key_byte) % 26);
}
else if (orig_char >= 'A' && orig_char <= 'Z')
{
new_char = 'A' + ((orig_char - 'A' + key_byte) % 26);
}
else
{
// 特殊字符保持不变或交替
new_char = (key_byte % 2) ? '-' : '_';
}
transformed[i] = new_char;
}
transformed[32] = '\0';
// 步骤3: 确保满足复杂度要求
ensure_complexity(transformed, 32);
// 步骤4: 截取适当长度(16-24字符)
int key_length = 16 + (chip_id % 9); // 16-24字符长度
// 复制到输出缓冲区
strncpy(key_buffer, transformed, key_length);
key_buffer[key_length] = '\0';
// 最终验证
if (!validate_key_complexity(key_buffer))
{
ESP_LOGW(TAG, "生成的密钥复杂度不足,进行强化处理");
strengthen_key(key_buffer, key_length);
}
ESP_LOGI(TAG, "生成的密钥长度: %zu,生成的密钥: %s", strlen(key_buffer), secret);
}
/**
* @brief 确保字符串包含所有要求的字符类型
*/
static void ensure_complexity(char *str, int length)
{
int has_lower = 0, has_upper = 0, has_digit = 0, has_special = 0;
// 检查当前包含的字符类型
for (int i = 0; i < length; i++)
{
if (str[i] >= 'a' && str[i] <= 'z')
has_lower = 1;
else if (str[i] >= 'A' && str[i] <= 'Z')
has_upper = 1;
else if (str[i] >= '0' && str[i] <= '9')
has_digit = 1;
else if (str[i] == '-' || str[i] == '_')
has_special = 1;
}
// 确保至少包含两种字符类型
int type_count = has_lower + has_upper + has_digit + has_special;
if (type_count >= 2)
{
return; // 已经满足要求
}
// 如果不满足,强制添加缺失的类型
if (!has_lower)
{
str[0] = 'a' + (str[0] % 26); // 添加小写字母
}
if (!has_upper)
{
str[1] = 'A' + (str[1] % 26); // 添加大写字母
}
if (!has_digit)
{
str[2] = '0' + (str[2] % 10); // 添加数字
}
if (!has_special)
{
str[3] = (str[3] % 2) ? '-' : '_'; // 添加特殊字符
}
}
/**
* @brief 验证密钥复杂度
*/
static int validate_key_complexity(const char *key)
{
int has_lower = 0, has_upper = 0, has_digit = 0, has_special = 0;
size_t len = strlen(key);
if (len < 8 || len > 32)
{
return 0;
}
for (size_t i = 0; i < len; i++)
{
char c = key[i];
if (c >= 'a' && c <= 'z')
{
has_lower = 1;
}
else if (c >= 'A' && c <= 'Z')
{
has_upper = 1;
}
else if (c >= '0' && c <= '9')
{
has_digit = 1;
}
else if (c == '-' || c == '_')
{
has_special = 1;
}
else
{
return 0; // 包含非法字符
}
}
// 至少包含两种字符类型
return (has_lower + has_upper + has_digit + has_special) >= 2;
}
/**
* @brief 强化密钥复杂度
*/
static void strengthen_key(char *key, int length)
{
// 确保包含小写字母
int has_lower = 0;
for (int i = 0; i < length; i++)
{
if (key[i] >= 'a' && key[i] <= 'z')
{
has_lower = 1;
break;
}
}
if (!has_lower)
{
key[length / 2] = 'a' + (key[length / 2] % 26);
}
// 确保包含大写字母
int has_upper = 0;
for (int i = 0; i < length; i++)
{
if (key[i] >= 'A' && key[i] <= 'Z')
{
has_upper = 1;
break;
}
}
if (!has_upper)
{
key[length / 3] = 'A' + (key[length / 3] % 26);
}
// 确保包含数字
int has_digit = 0;
for (int i = 0; i < length; i++)
{
if (key[i] >= '0' && key[i] <= '9')
{
has_digit = 1;
break;
}
}
if (!has_digit)
{
key[length / 4] = '0' + (key[length / 4] % 10);
}
// 确保包含特殊字符
int has_special = 0;
for (int i = 0; i < length; i++)
{
if (key[i] == '-' || key[i] == '_')
{
has_special = 1;
break;
}
}
if (!has_special)
{
key[length - 1] = (key[length - 1] % 2) ? '-' : '_';
}
}
void get_object_device_id(char *product_id)
{
// 获取基础MAC地址(芯片唯一标识)
static uint8_t base_mac[6];
if (esp_efuse_mac_get_default(base_mac) == ESP_OK)
{
// 将MAC地址转换为64位整数作为芯片ID
for (int i = 0; i < 6; i++)
{
chip_id |= (uint64_t)base_mac[i] << (40 - i * 8);
}
// 转换为device_id字符串(12字符十六进制)
char device_id[13]; // 12字符 + 结束符
snprintf(device_id, sizeof(device_id), "%012llX", chip_id);
// 生成最终的object_device_id
// 足够存储完整字符串
snprintf(object_device_id, sizeof(object_device_id),
"%s_%s", product_id, device_id);
ESP_LOGI(TAG, "芯片唯一ID: 0x%012llX", chip_id);
ESP_LOGI(TAG, "设备ID字符串: %s", device_id);
ESP_LOGI(TAG, "最终对象设备ID: %s", object_device_id);
}
}
bool Get_MQTT_FLAG(void) { return mqtt_connected; }
void mqtt_client_shutdown(void)
{
if (client == NULL)
{
mqtt_connected = false;
return;
}
ESP_LOGW(TAG, "mqtt_client_shutdown: stopping and destroying client");
(void)esp_mqtt_client_stop(client);
(void)esp_mqtt_client_destroy(client);
client = NULL;
client_http = NULL;
mqtt_connected = false;
}
void mqtt_main(void)
{
ESP_LOGI(TAG, "[DEBUG] mqtt_main 被调用");
get_object_device_id(PRODUCT_ID);
generate_device_key(chip_id, secret, sizeof(secret));
// 现在你可以使用 object_device_id 进行其他操作
// 例如:发送到服务器、保存到文件等
ESP_LOGI(TAG, "[APP] Startup..");
ESP_LOGI(TAG, "[APP] 设备固件版本: %s", DEVICE_FIRMWARE_VERSION); // 新增这行
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE);
esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE);
esp_log_level_set("transport_base", ESP_LOG_VERBOSE);
esp_log_level_set("esp-tls", ESP_LOG_VERBOSE);
esp_log_level_set("transport", ESP_LOG_VERBOSE);
esp_log_level_set("outbox", ESP_LOG_VERBOSE);
// ESP_ERROR_CHECK(nvs_flash_init());
// ESP_ERROR_CHECK(esp_netif_init());
// ESP_ERROR_CHECK(esp_event_loop_create_default());
// ESP_ERROR_CHECK(example_connect());
mqtt5_app_start();
}