纳入固件源码、屏幕组件、nand_xfer 工具和文档。忽略本机构建产物、sdkconfig,以及含本机路径的依赖锁。 Co-authored-by: Cursor <[email protected]>
13 KiB
表情包下载流程与文件路径文档
1. 下载流程概述
表情包下载流程涉及BLE通信、MQTT文件管理、HTTP下载、ZIP解压和文件存储等多个环节。
2. 完整下载流程
2.1 触发下载(BLE指令)
位置: main/BLE/ble_data_ansy.c
流程:
- APP通过BLE发送表情包下载指令
0x000B(emoji_pack_download) - 指令格式:
帧头(1) + 类(2) + 指令(2) + Target_pack(1) + CRC16(2) + 帧尾(1) = 9字节 Target_pack为ASCII字符:'A','B','C','D'之一- 解析后保存到
s_emoji_download_target_pack - 设置系统事件
DOWNLOAD_EMOJI_BIT
关键代码:
// 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
流程:
main_task检测到DOWNLOAD_EMOJI_BIT事件- 检查MQTT连接状态,如未连接则初始化MQTT
- 等待MQTT连接(最多60秒超时)
- 调用
publish_json_download(filename_emoji)请求下载URL
关键代码:
// 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
流程:
publish_json_download(filename)构建JSON请求- 服务ID:
"$file_manager" - 事件类型:
"get_download_url" - 参数:
file_name=filename_emoji(如"icar/A.zip") - 发布到MQTT主题:
$oc/devices/{device_id}/sys/events/up
关键代码:
// 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
流程:
- 接收MQTT消息:
$oc/devices/{device_id}/sys/events/down - 解析JSON响应:
event_type = "get_download_url_response" - 提取参数:
url: 下载URL(HTTP/HTTPS)object_name: 文件名(如"icar/A.zip")
- 判断文件类型:
- 如果是
.bin文件 → 触发OTA升级 - 否则 → 调用
download_file(url, filename)下载
- 如果是
关键代码:
// 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
流程:
download_file(firmware_url, filename)使用esp_http_client下载- 下载路径:
/extflash/{filename}(如/extflash/A.zip) - 支持断点续传(Range请求)
- 下载完成后创建解压任务
extract_task
关键代码:
// 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
流程:
extract_task调用extract_json_zip(filename)- 构建ZIP文件路径:
/extflash/{filename} - 构建解压目录:
/extflash/{filename去掉.zip后缀}- 例如:
/extflash/A.zip→ 解压到/extflash/A/
- 例如:
- 使用
miniz库解压ZIP文件 - 解压后的JSON文件直接放在解压目录下(如
/extflash/A/default.json)
关键代码:
// 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
流程:
main_task检测到DOWNLOAD_SUCCESS_BIT事件- 通知
notification_task发送下载成功反馈(BLE协议帧0x0003) - 需要添加: 调用
emoji_storage_add_downloaded_pack(pack_id)将pack_id添加到已下载列表
关键代码:
// 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}
路径构建逻辑(需要实现):
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 文件查找流程
-
获取当前使用的pack_id:
char pack_id = emoji_storage_get_using_pack(); -
构建完整路径:
char* path = get_emoji_path(pack_id, "default.json"); // pack_id='A' → "/extflash/A/default.json" // pack_id='0' → "/spiffs/default.json" -
检查文件是否存在:
struct stat st; if (stat(path, &st) == 0) { // 文件存在,可以使用 } else { // 文件不存在,回退到默认路径 path = "/spiffs/default.json"; }
4.3 在remapping中使用
位置: main/remapping.c
当前实现: 直接使用 lottie_files[] 数组,所有路径都是 /spiffs/...
需要修改: 根据当前使用的pack_id动态构建路径
建议修改:
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. 待完善的功能
-
filename_emoji构建: 需要在设置
DOWNLOAD_EMOJI_BIT之前构建filename_emoji,格式应为"icar/{pack_id}.zip"或"{pack_id}.zip" -
下载成功后添加pack_id: 在
DOWNLOAD_SUCCESS_BIT处理中调用emoji_storage_add_downloaded_pack(pack_id)将pack_id添加到已下载列表 -
动态路径查找: 修改
remapping()函数,根据当前使用的pack_id动态构建文件路径,支持从/extflash/{pack_id}/或/spiffs/加载JSON文件 -
文件存在性检查: 在使用下载的表情包文件前,检查文件是否存在,如果不存在则回退到默认表情包
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. 注意事项
-
文件系统挂载: 确保外部Flash的FAT文件系统已正确挂载到
/extflash -
路径格式: 文件名可能包含目录(如
"icar/A.zip"),需要正确处理 -
内存管理: ZIP解压使用PSRAM,避免栈溢出
-
错误处理: 下载失败、解压失败都需要设置相应的事件位并通知APP
-
pack_id管理: 确保pack_id('0', 'A', 'B', 'C', 'D')与文件路径一致