// 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(); }