纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。 Co-authored-by: Cursor <cursoragent@cursor.com>
1291 lines
45 KiB
C
1291 lines
45 KiB
C
// 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();
|
|
}
|