Files
P4-USB-transfer/表情包下载流程与文件路径.md
T

424 lines
13 KiB
Markdown
Raw Normal View History

2026-10-13 21:54:50 +08:00
# 表情包下载流程与文件路径文档
## 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')与文件路径一致