# 表情包下载流程与文件路径文档 ## 1. 下载流程概述 表情包下载流程涉及BLE通信、MQTT文件管理、HTTP下载、ZIP解压和文件存储等多个环节。 ## 2. 完整下载流程 ### 2.1 触发下载(BLE指令) **位置**: `main/BLE/ble_data_ansy.c` **流程**: 1. APP通过BLE发送表情包下载指令 `0x000B`(`emoji_pack_download`) 2. 指令格式:`帧头(1) + 类(2) + 指令(2) + Target_pack(1) + CRC16(2) + 帧尾(1) = 9字节` 3. `Target_pack` 为ASCII字符:`'A'`, `'B'`, `'C'`, `'D'` 之一 4. 解析后保存到 `s_emoji_download_target_pack` 5. 设置系统事件 `DOWNLOAD_EMOJI_BIT` **关键代码**: ```c // main/BLE/ble_data_ansy.c:398-410 case emoji_pack_download: char target_pack = (char)input[5]; /* Target_pack: A/B/C/D ASCII */ s_emoji_download_target_pack = target_pack; ESP_LOGI("BLE_ANSY", "表情包下载 目标系列: %c", target_pack); set_system_event(DOWNLOAD_EMOJI_BIT); ``` ### 2.2 主任务处理下载事件 **位置**: `main/main.c` **流程**: 1. `main_task` 检测到 `DOWNLOAD_EMOJI_BIT` 事件 2. 检查MQTT连接状态,如未连接则初始化MQTT 3. 等待MQTT连接(最多60秒超时) 4. 调用 `publish_json_download(filename_emoji)` 请求下载URL **关键代码**: ```c // main/main.c:212-231 if (event_bits & DOWNLOAD_EMOJI_BIT) { ESP_LOGI(TAG_MAIN, "处理下载表情事件: %s", filename_emoji); if (!Get_MQTT_FLAG()) mqtt_main(); // 等待MQTT连接(60秒超时) while (!Get_MQTT_FLAG()) { vTaskDelay(pdMS_TO_TICKS(500)); if (超时) break; } if (Get_MQTT_FLAG()) publish_json_download(filename_emoji); clear_system_event(DOWNLOAD_EMOJI_BIT); } ``` **注意**: `filename_emoji` 需要在触发 `DOWNLOAD_EMOJI_BIT` 之前构建,格式应为 `"icar/A.zip"` 或类似格式(根据 `target_pack` 构建)。 ### 2.3 MQTT请求下载URL **位置**: `main/MQTT/mqtt.c` **流程**: 1. `publish_json_download(filename)` 构建JSON请求 2. 服务ID: `"$file_manager"` 3. 事件类型: `"get_download_url"` 4. 参数: `file_name` = `filename_emoji`(如 `"icar/A.zip"`) 5. 发布到MQTT主题: `$oc/devices/{device_id}/sys/events/up` **关键代码**: ```c // main/MQTT/mqtt.c:181-199 esp_err_t publish_json_download(char *filename) { cJSON *root = create_data("$file_manager", "get_download_url"); cJSON *paras = cJSON_GetObjectItem(service_object, "paras"); cJSON_AddStringToObject(paras, "file_name", filename); // 发布到MQTT esp_mqtt_client_publish(client, mqtt_event_up(object_device_id), ...); } ``` ### 2.4 接收下载URL响应 **位置**: `main/MQTT/mqtt.c` **流程**: 1. 接收MQTT消息:`$oc/devices/{device_id}/sys/events/down` 2. 解析JSON响应:`event_type = "get_download_url_response"` 3. 提取参数: - `url`: 下载URL(HTTP/HTTPS) - `object_name`: 文件名(如 `"icar/A.zip"`) 4. 判断文件类型: - 如果是 `.bin` 文件 → 触发OTA升级 - 否则 → 调用 `download_file(url, filename)` 下载 **关键代码**: ```c // main/MQTT/mqtt.c:373-398 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"); char *filename = object_name->valuestring; char *slash = strchr(filename, '/'); if (slash != NULL) slash++; // 提取文件名部分 if (是.bin文件) { trigger_ota_upgrade(url->valuestring); } else if (download_file(url_copy, slash) == ESP_OK) { transmit_download_success(object_name->valuestring); } } } ``` ### 2.5 HTTP下载文件 **位置**: `main/FLASH/flash.c` **流程**: 1. `download_file(firmware_url, filename)` 使用 `esp_http_client` 下载 2. 下载路径: `/extflash/{filename}`(如 `/extflash/A.zip`) 3. 支持断点续传(Range请求) 4. 下载完成后创建解压任务 `extract_task` **关键代码**: ```c // main/FLASH/flash.c:383-629 esp_err_t download_file(char *firmware_url, char *filename) { // 构建完整路径 snprintf(full_path, sizeof(full_path), "%s/%s", base_path, filename); // base_path = "/extflash" // HTTP下载 esp_http_client_handle_t client = esp_http_client_init(&config); // ... 下载数据写入文件 ... // 下载完成后创建解压任务 xTaskCreateStaticPinnedToCore(extract_task, "extract_task", ..., (void *)filename, ...); } ``` **存储位置**: - **下载ZIP文件路径**: `/extflash/{filename}`(如 `/extflash/A.zip`) - **文件系统**: FAT文件系统,挂载在外部SPI Flash(`storage_extFlash`分区) ### 2.6 ZIP解压 **位置**: `main/ZIP_EXTRACTOR/zip_extractor.c` **流程**: 1. `extract_task` 调用 `extract_json_zip(filename)` 2. 构建ZIP文件路径: `/extflash/{filename}` 3. 构建解压目录: `/extflash/{filename去掉.zip后缀}` - 例如:`/extflash/A.zip` → 解压到 `/extflash/A/` 4. 使用 `miniz` 库解压ZIP文件 5. 解压后的JSON文件直接放在解压目录下(如 `/extflash/A/default.json`) **关键代码**: ```c // main/ZIP_EXTRACTOR/zip_extractor.c:533-553 void extract_json_zip(char* filename) { snprintf(zip_file_path, sizeof(zip_file_path), "%s/%s", "/extflash", filename); snprintf(extract_directory, sizeof(extract_directory), "%s", zip_file_path); // 去掉.zip后缀 char *zip_pos = strstr(extract_directory, ".zip"); if (zip_pos) *zip_pos = '\0'; // 解压到目录 extract_zip_file(zip_file_path, extract_directory); } // 解压逻辑(main/ZIP_EXTRACTOR/zip_extractor.c:278-491) void extract_zip_file(const char* zip_path, const char* extract_dir) { // 遍历ZIP内所有文件 for (mz_uint i = 0; i < num_files; i++) { // 构建输出路径: extract_dir/文件名 snprintf(output_path, 1024, "%s/%s", extract_dir, file_stat.m_filename); // 解压文件 mz_zip_reader_extract_to_file(zip_archive, i, output_path, ...); } // 解压成功/失败后设置事件 if (成功) { set_system_event(DOWNLOAD_SUCCESS_BIT); } else { set_system_event(DOWNLOAD_FAILED_BIT); } } ``` **解压后文件结构**: ``` /extflash/ ├── A.zip (下载的ZIP文件) └── A/ (解压后的目录) ├── default.json ├── calling.json ├── charging.json └── ... (其他JSON文件) ``` ### 2.7 下载成功处理 **位置**: `main/main.c`, `main/BLE/ble.c` **流程**: 1. `main_task` 检测到 `DOWNLOAD_SUCCESS_BIT` 事件 2. 通知 `notification_task` 发送下载成功反馈(BLE协议帧 `0x0003`) 3. **需要添加**: 调用 `emoji_storage_add_downloaded_pack(pack_id)` 将pack_id添加到已下载列表 **关键代码**: ```c // main/main.c:233-249 if (event_bits & DOWNLOAD_SUCCESS_BIT) { ESP_LOGI(TAG_MAIN, "处理下载成功事件"); xTaskNotify(notification_task_handle, BLE_SEND_DL_EMOJI_SUCCESS, eSetBits); // TODO: 需要添加 emoji_storage_add_downloaded_pack(pack_id) clear_system_event(DOWNLOAD_SUCCESS_BIT); } // main/BLE/ble.c:1221-1231 else if (bits & BLE_SEND_DL_EMOJI_SUCCESS) { send_emoji_download_result_frame(1); // 发送成功反馈 } ``` ## 3. 文件存储路径 ### 3.1 下载ZIP文件路径 **格式**: `/extflash/{filename}` **示例**: - `/extflash/A.zip` - `/extflash/B.zip` - `/extflash/icar/A.zip`(如果filename包含目录) **文件系统**: FAT文件系统,挂载在外部SPI Flash ### 3.2 解压后文件路径 **格式**: `/extflash/{pack_id}/{json_filename}` **示例**: - `/extflash/A/default.json` - `/extflash/A/calling.json` - `/extflash/B/charging.json` **目录结构**: ``` /extflash/ ├── A.zip (下载的ZIP文件) ├── A/ (解压后的目录) │ ├── default.json │ ├── calling.json │ └── ... ├── B.zip ├── B/ │ └── ... └── ... ``` ### 3.3 默认表情包路径(SPIFFS) **格式**: `/spiffs/{json_filename}` **示例**: - `/spiffs/default.json` - `/spiffs/calling.json` **说明**: 默认表情包存储在SPIFFS分区(`storage`分区),编译时打包到固件中。 ## 4. 通过文件路径查找表情包 ### 4.1 根据pack_id构建路径 **当前使用的表情包**: - 通过 `emoji_storage_get_using_pack()` 获取当前pack_id(如 `'A'`, `'B'`, `'C'`, `'D'`) - 如果pack_id为 `'0'`,使用SPIFFS路径:`/spiffs/{json_filename}` - 否则使用extflash路径:`/extflash/{pack_id}/{json_filename}` **路径构建逻辑**(需要实现): ```c char* get_emoji_path(char pack_id, const char* json_filename) { static char path[128]; if (pack_id == '0') { // 默认表情包,使用SPIFFS snprintf(path, sizeof(path), "/spiffs/%s", json_filename); } else { // 下载的表情包,使用extflash snprintf(path, sizeof(path), "/extflash/%c/%s", pack_id, json_filename); } return path; } ``` ### 4.2 文件查找流程 1. **获取当前使用的pack_id**: ```c char pack_id = emoji_storage_get_using_pack(); ``` 2. **构建完整路径**: ```c char* path = get_emoji_path(pack_id, "default.json"); // pack_id='A' → "/extflash/A/default.json" // pack_id='0' → "/spiffs/default.json" ``` 3. **检查文件是否存在**: ```c struct stat st; if (stat(path, &st) == 0) { // 文件存在,可以使用 } else { // 文件不存在,回退到默认路径 path = "/spiffs/default.json"; } ``` ### 4.3 在remapping中使用 **位置**: `main/remapping.c` **当前实现**: 直接使用 `lottie_files[]` 数组,所有路径都是 `/spiffs/...` **需要修改**: 根据当前使用的pack_id动态构建路径 **建议修改**: ```c char* remapping(uint32_t bits) { char *path = NULL; char pack_id = emoji_storage_get_using_pack(); switch (bits) { case emoji_event_start: if (pack_id == '0') { path = lottie_files[start_emojo]; // "/spiffs/default.json" } else { // 构建路径: "/extflash/{pack_id}/default.json" snprintf(g_target_path, sizeof(g_target_path), "/extflash/%c/default.json", pack_id); path = g_target_path; } break; // ... 其他case类似处理 } return path; } ``` ## 5. 关键文件位置总结 | 功能 | 文件路径 | 关键函数/变量 | |------|---------|--------------| | BLE指令解析 | `main/BLE/ble_data_ansy.c` | `ble_data_ansy()`, `s_emoji_download_target_pack` | | 主任务处理 | `main/main.c` | `main_task()`, `filename_emoji` | | MQTT请求 | `main/MQTT/mqtt.c` | `publish_json_download()`, `handle_event()` | | HTTP下载 | `main/FLASH/flash.c` | `download_file()`, `base_path = "/extflash"` | | ZIP解压 | `main/ZIP_EXTRACTOR/zip_extractor.c` | `extract_json_zip()`, `extract_zip_file()` | | 表情包存储 | `main/EMOJI_STORAGE/emoji_storage.c` | `emoji_storage_add_downloaded_pack()`, `emoji_storage_get_using_pack()` | | 路径映射 | `main/remapping.c` | `remapping()`, `lottie_files[]` | ## 6. 待完善的功能 1. **filename_emoji构建**: 需要在设置 `DOWNLOAD_EMOJI_BIT` 之前构建 `filename_emoji`,格式应为 `"icar/{pack_id}.zip"` 或 `"{pack_id}.zip"` 2. **下载成功后添加pack_id**: 在 `DOWNLOAD_SUCCESS_BIT` 处理中调用 `emoji_storage_add_downloaded_pack(pack_id)` 将pack_id添加到已下载列表 3. **动态路径查找**: 修改 `remapping()` 函数,根据当前使用的pack_id动态构建文件路径,支持从 `/extflash/{pack_id}/` 或 `/spiffs/` 加载JSON文件 4. **文件存在性检查**: 在使用下载的表情包文件前,检查文件是否存在,如果不存在则回退到默认表情包 ## 7. 流程图 ``` APP发送BLE指令(0x000B, pack_id='A') ↓ ble_data_ansy解析,设置DOWNLOAD_EMOJI_BIT ↓ main_task处理DOWNLOAD_EMOJI_BIT ↓ 构建filename_emoji = "icar/A.zip" (或 "A.zip") ↓ publish_json_download(filename_emoji) → MQTT ↓ MQTT响应: get_download_url_response ↓ 提取url和object_name ↓ download_file(url, "A.zip") → HTTP下载 ↓ 保存到: /extflash/A.zip ↓ extract_task解压ZIP ↓ 解压到: /extflash/A/ ↓ 设置DOWNLOAD_SUCCESS_BIT ↓ main_task处理成功事件 ↓ emoji_storage_add_downloaded_pack('A') ↓ 发送BLE反馈: 下载成功(0x0003) ``` ## 8. 注意事项 1. **文件系统挂载**: 确保外部Flash的FAT文件系统已正确挂载到 `/extflash` 2. **路径格式**: 文件名可能包含目录(如 `"icar/A.zip"`),需要正确处理 3. **内存管理**: ZIP解压使用PSRAM,避免栈溢出 4. **错误处理**: 下载失败、解压失败都需要设置相应的事件位并通知APP 5. **pack_id管理**: 确保pack_id('0', 'A', 'B', 'C', 'D')与文件路径一致