## 功能迁移清单(从 Dzbj_ESP32_S3_Key → Baji_Rtc_Toy) ### 1. 设备间BLE图片传输(GATT Client + 协议) - 新增 ble_transfer.c/h:发送方 GATT Client 扫描→连接→MTU协商→分包写入 - 接收方复用现有 GATT Server(IMAGE_WRITE 0x0B01),协议完全兼容 - 发送完成/失败自动跳转 Img 界面并关闭蓝牙 ### 2. APP传图显示 Update 界面 - 新增 ui_ScreenUpdate.c/h:更新进度界面(Gengxin背景 + Update_GIF动画) - dzbj_ble.c WRITE_EVT 中通过 ble_transfer_is_receiving() 区分 APP传图 vs 设备间传输 - APP传图 → ScreenUpdate,设备间传输 → ScreenReceiving ### 3. KEY2 按键功能入口(iot_button 单击/双击/长按) - KEY2 单击:开蓝牙 → Peiwang 配对界面(APP传图) - KEY2 双击:接收模式 → ScreenImageReception(等待配对) - KEY2 长按:发送模式 → ScreenImageShar(等待配对) - 按键参数与按键版对齐:long_press_time=1200ms, short_press_time=300ms ### 4. BOOT+KEY2 组合键模式切换(替代 BOOT 长按3秒) - BOOT 2秒长按 + KEY2 同时按下 → 触发模式切换 - 消除单键长按的误触发问题 - AI模式和吧唧模式均注册组合键 ### 5. 按键上下文状态机 - btn_context_t 枚举:HOME/IMG/SET/PEIWANG/IMAGE_SHAR/IMAGE_RECEPTION/SHARING/RECEIVING/UPDATE - 所有界面切换点(手势/按键/BLE自动跳转)同步设置 context - BOOT 单击按 context 分发:Home无操作、Img/Set返回Home、配对退出蓝牙、传输等待取消 ### 6. 新增 UI 界面(6个Screen + 7张图片) - ScreenPeiwang:蓝牙配对等待 - ScreenUpdate:APP传图更新中 - ScreenImageShar:发送方等待配对 - ScreenImageReception:接收方等待配对 - ScreenSharing:发送方传输中 - ScreenReceiving:接收方接收中 ### 7. 其他适配 - BLE 广播改为按需启动(dzbj_ble_start/stop/is_active) - sleep_mgr 移除 KEY2 唤醒(仅 BOOT 唤醒屏幕) - device_mode 新增模式切换按键抑制(防止重启后立即触发) - battery_ui 电池指示器组件 - sdkconfig 启用 BLE GATTC 支持 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
517 lines
18 KiB
C
517 lines
18 KiB
C
// 设备间蓝牙图片传输模块
|
||
// 发送方:GATT Client 扫描→发现→连接→MTU协商→发现服务→分包写入
|
||
// 接收方:复用现有 GATT Server(IMAGE_WRITE 0x0B01),协议完全兼容
|
||
// 不影响现有 APP 传图:APP 通过 KEY2 单击(Peiwang),设备间通过 KEY2 长按/双击
|
||
|
||
#include "ble_transfer.h"
|
||
#include "dzbj_ble.h"
|
||
#include "pages.h"
|
||
// 触屏版不使用key_nav,界面导航由手势控制
|
||
#include "dzbj_button.h"
|
||
#include "esp_log.h"
|
||
#include "esp_bt.h"
|
||
#include "esp_gap_ble_api.h"
|
||
#include "esp_gattc_api.h"
|
||
#include "esp_bt_main.h"
|
||
#include "esp_gatt_common_api.h"
|
||
#include "esp_lvgl_port.h"
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
#include "ui/ui.h"
|
||
#include "ui/screens/ui_ScreenImg.h"
|
||
#include "ui/screens/ui_ScreenSharing.h"
|
||
#include "ui/screens/ui_ScreenReceiving.h"
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
|
||
static const char *TAG = "BLE_XFER";
|
||
|
||
// === 状态管理 ===
|
||
static ble_xfer_state_t xfer_state = BLE_XFER_IDLE;
|
||
static bool receiving_mode = false; // 接收方模式标识
|
||
|
||
// === GATT Client 相关 ===
|
||
#define XFER_APP_ID 1 // 与现有Server的APP_ID=0区分
|
||
#define IMAGE_SERVICE_UUID 0x0B00
|
||
#define IMAGE_WRITE_UUID 0x0B01
|
||
|
||
static esp_gatt_if_t client_gattc_if = ESP_GATT_IF_NONE;
|
||
static uint16_t client_conn_id = 0;
|
||
static uint16_t client_write_handle = 0;
|
||
static esp_bd_addr_t target_bda; // 扫描到的目标设备地址
|
||
static bool target_found = false;
|
||
static uint16_t client_mtu = 23; // 协商后的MTU值
|
||
|
||
// === 发送任务相关 ===
|
||
static TaskHandle_t send_task_handle = NULL;
|
||
static volatile bool send_cancel_flag = false;
|
||
static volatile bool gattc_congested = false; // BLE链路拥塞标志
|
||
|
||
// === 扫描参数 ===
|
||
static esp_ble_scan_params_t scan_params = {
|
||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||
.scan_interval = 0x50, // 50ms
|
||
.scan_window = 0x30, // 30ms
|
||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE,
|
||
};
|
||
|
||
// === 前向声明 ===
|
||
static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
||
static void send_image_task(void *arg);
|
||
|
||
// === 接口实现 ===
|
||
|
||
ble_xfer_state_t ble_transfer_get_state(void) {
|
||
return xfer_state;
|
||
}
|
||
|
||
bool ble_transfer_is_receiving(void) {
|
||
return receiving_mode;
|
||
}
|
||
|
||
void ble_transfer_init(void) {
|
||
// 注册 GATT Client 回调
|
||
esp_err_t ret = esp_ble_gattc_register_callback(gattc_event_handler);
|
||
if (ret) {
|
||
ESP_LOGE(TAG, "GATTC回调注册失败: %s", esp_err_to_name(ret));
|
||
return;
|
||
}
|
||
// 注册 GATT Client 应用
|
||
ret = esp_ble_gattc_app_register(XFER_APP_ID);
|
||
if (ret) {
|
||
ESP_LOGE(TAG, "GATTC应用注册失败: %s", esp_err_to_name(ret));
|
||
return;
|
||
}
|
||
ESP_LOGI(TAG, "设备间传输模块初始化完成");
|
||
}
|
||
|
||
void ble_transfer_start_send(void) {
|
||
if (xfer_state != BLE_XFER_IDLE) {
|
||
ESP_LOGW(TAG, "传输正在进行中,忽略");
|
||
return;
|
||
}
|
||
|
||
// 如果当前正在为APP广播,先停止
|
||
if (dzbj_ble_is_active()) {
|
||
dzbj_ble_stop();
|
||
}
|
||
|
||
xfer_state = BLE_XFER_SCANNING;
|
||
target_found = false;
|
||
send_cancel_flag = false;
|
||
|
||
// 开始扫描(扫描5秒)
|
||
esp_ble_gap_set_scan_params(&scan_params);
|
||
ESP_LOGI(TAG, "发送方:开始扫描接收设备...");
|
||
}
|
||
|
||
void ble_transfer_start_receive(void) {
|
||
if (xfer_state != BLE_XFER_IDLE) {
|
||
ESP_LOGW(TAG, "传输正在进行中,忽略");
|
||
return;
|
||
}
|
||
|
||
receiving_mode = true;
|
||
xfer_state = BLE_XFER_RECEIVING;
|
||
|
||
// 启动广播(复用现有GATT Server)
|
||
dzbj_ble_start();
|
||
ESP_LOGI(TAG, "接收方:已开始广播,等待发送设备连接...");
|
||
}
|
||
|
||
void ble_transfer_cancel(void) {
|
||
ESP_LOGI(TAG, "取消传输,当前状态: %d", xfer_state);
|
||
|
||
send_cancel_flag = true;
|
||
|
||
switch (xfer_state) {
|
||
case BLE_XFER_SCANNING:
|
||
esp_ble_gap_stop_scanning();
|
||
break;
|
||
case BLE_XFER_CONNECTING:
|
||
case BLE_XFER_SENDING:
|
||
if (client_conn_id != 0) {
|
||
esp_ble_gattc_close(client_gattc_if, client_conn_id);
|
||
}
|
||
break;
|
||
case BLE_XFER_RECEIVING:
|
||
dzbj_ble_stop();
|
||
receiving_mode = false;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
xfer_state = BLE_XFER_IDLE;
|
||
}
|
||
|
||
// === GAP 扫描结果回调(由 dzbj_ble.c 的 esp_gap_cb 转发) ===
|
||
|
||
void ble_transfer_handle_scan_result(void *param) {
|
||
if (xfer_state != BLE_XFER_SCANNING) return;
|
||
|
||
esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
|
||
switch (scan_result->scan_rst.search_evt) {
|
||
case ESP_GAP_SEARCH_INQ_RES_EVT: {
|
||
// 检查广播数据 + Scan Response 中是否包含服务UUID 0x0B00
|
||
uint8_t *adv_data = scan_result->scan_rst.ble_adv;
|
||
uint8_t adv_len = scan_result->scan_rst.adv_data_len
|
||
+ scan_result->scan_rst.scan_rsp_len;
|
||
|
||
// 解析广播数据,查找 0x0B00 服务UUID
|
||
uint8_t *p = adv_data;
|
||
while (p < adv_data + adv_len) {
|
||
uint8_t field_len = p[0];
|
||
if (field_len == 0) break;
|
||
uint8_t field_type = p[1];
|
||
|
||
// 检查完整16位服务UUID列表
|
||
if (field_type == ESP_BLE_AD_TYPE_16SRV_CMPL ||
|
||
field_type == ESP_BLE_AD_TYPE_16SRV_PART) {
|
||
for (int i = 2; i < field_len + 1; i += 2) {
|
||
uint16_t uuid16 = p[i] | (p[i + 1] << 8);
|
||
if (uuid16 == IMAGE_SERVICE_UUID) {
|
||
memcpy(target_bda, scan_result->scan_rst.bda, sizeof(esp_bd_addr_t));
|
||
target_found = true;
|
||
ESP_LOGI(TAG, "发现接收设备: "ESP_BD_ADDR_STR,
|
||
ESP_BD_ADDR_HEX(target_bda));
|
||
esp_ble_gap_stop_scanning();
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查厂商数据中的"LDdzbj"标识
|
||
if (field_type == ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE && field_len >= 7) {
|
||
if (memcmp(p + 2, "\x4C\x44\x64\x7A\x62\x6A", 6) == 0) {
|
||
memcpy(target_bda, scan_result->scan_rst.bda, sizeof(esp_bd_addr_t));
|
||
target_found = true;
|
||
ESP_LOGI(TAG, "发现接收设备(厂商标识): "ESP_BD_ADDR_STR,
|
||
ESP_BD_ADDR_HEX(target_bda));
|
||
esp_ble_gap_stop_scanning();
|
||
return;
|
||
}
|
||
}
|
||
|
||
p += field_len + 1;
|
||
}
|
||
break;
|
||
}
|
||
case ESP_GAP_SEARCH_INQ_CMPL_EVT:
|
||
// 扫描完成
|
||
if (!target_found) {
|
||
ESP_LOGW(TAG, "扫描完成,未发现接收设备,保持分享界面等待用户操作");
|
||
xfer_state = BLE_XFER_IDLE;
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void ble_transfer_handle_scan_stop(void) {
|
||
if (xfer_state != BLE_XFER_SCANNING) return;
|
||
|
||
if (target_found) {
|
||
// 扫描停止成功,开始连接
|
||
xfer_state = BLE_XFER_CONNECTING;
|
||
esp_ble_gattc_open(client_gattc_if, target_bda, BLE_ADDR_TYPE_PUBLIC, true);
|
||
ESP_LOGI(TAG, "正在连接目标设备...");
|
||
}
|
||
}
|
||
|
||
// === GATT Client 事件处理 ===
|
||
|
||
static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||
switch (event) {
|
||
case ESP_GATTC_REG_EVT:
|
||
if (param->reg.status == ESP_GATT_OK) {
|
||
client_gattc_if = gattc_if;
|
||
ESP_LOGI(TAG, "GATTC注册成功, if=%d", gattc_if);
|
||
} else {
|
||
ESP_LOGE(TAG, "GATTC注册失败, status=%d", param->reg.status);
|
||
}
|
||
break;
|
||
|
||
case ESP_GATTC_OPEN_EVT:
|
||
if (param->open.status != ESP_GATT_OK) {
|
||
ESP_LOGE(TAG, "连接失败, status=%d", param->open.status);
|
||
xfer_state = BLE_XFER_IDLE;
|
||
return;
|
||
}
|
||
client_conn_id = param->open.conn_id;
|
||
ESP_LOGI(TAG, "已连接, conn_id=%d", client_conn_id);
|
||
// 协商MTU为512以加速传输
|
||
esp_ble_gattc_send_mtu_req(gattc_if, client_conn_id);
|
||
break;
|
||
|
||
case ESP_GATTC_CFG_MTU_EVT:
|
||
client_mtu = param->cfg_mtu.mtu;
|
||
ESP_LOGI(TAG, "MTU协商完成: %d", client_mtu);
|
||
// 开始发现服务
|
||
esp_ble_gattc_search_service(gattc_if, client_conn_id, NULL);
|
||
break;
|
||
|
||
case ESP_GATTC_SEARCH_RES_EVT: {
|
||
// 找到服务
|
||
if (param->search_res.srvc_id.uuid.len == ESP_UUID_LEN_16 &&
|
||
param->search_res.srvc_id.uuid.uuid.uuid16 == IMAGE_SERVICE_UUID) {
|
||
ESP_LOGI(TAG, "发现图片传输服务, start_handle=%d, end_handle=%d",
|
||
param->search_res.start_handle, param->search_res.end_handle);
|
||
|
||
// 获取特征值
|
||
uint16_t count = 0;
|
||
esp_gatt_status_t status = esp_ble_gattc_get_attr_count(
|
||
gattc_if, client_conn_id, ESP_GATT_DB_CHARACTERISTIC,
|
||
param->search_res.start_handle, param->search_res.end_handle, 0, &count);
|
||
if (status == ESP_GATT_OK && count > 0) {
|
||
esp_gattc_char_elem_t *char_elems = malloc(sizeof(esp_gattc_char_elem_t) * count);
|
||
if (char_elems) {
|
||
status = esp_ble_gattc_get_all_char(
|
||
gattc_if, client_conn_id,
|
||
param->search_res.start_handle,
|
||
param->search_res.end_handle,
|
||
char_elems, &count, 0);
|
||
if (status == ESP_GATT_OK) {
|
||
for (int i = 0; i < count; i++) {
|
||
if (char_elems[i].uuid.len == ESP_UUID_LEN_16 &&
|
||
char_elems[i].uuid.uuid.uuid16 == IMAGE_WRITE_UUID) {
|
||
client_write_handle = char_elems[i].char_handle;
|
||
ESP_LOGI(TAG, "发现IMAGE_WRITE特征, handle=%d", client_write_handle);
|
||
}
|
||
}
|
||
}
|
||
free(char_elems);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
case ESP_GATTC_SEARCH_CMPL_EVT:
|
||
if (client_write_handle == 0) {
|
||
ESP_LOGE(TAG, "未找到IMAGE_WRITE特征");
|
||
esp_ble_gattc_close(gattc_if, client_conn_id);
|
||
xfer_state = BLE_XFER_IDLE;
|
||
return;
|
||
}
|
||
|
||
// 切换到传输中界面
|
||
xfer_state = BLE_XFER_SENDING;
|
||
if (lvgl_port_lock(100)) {
|
||
_ui_screen_change(&ui_ScreenSharing, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenSharing_screen_init);
|
||
lvgl_port_unlock();
|
||
}
|
||
dzbj_button_set_context(BTN_CTX_SHARING);
|
||
// 启动发送任务
|
||
xTaskCreate(send_image_task, "ble_send", 8192, NULL, 5, &send_task_handle);
|
||
break;
|
||
|
||
case ESP_GATTC_WRITE_CHAR_EVT:
|
||
if (param->write.status != ESP_GATT_OK) {
|
||
ESP_LOGE(TAG, "写入失败, handle=%d, status=%d",
|
||
param->write.handle, param->write.status);
|
||
}
|
||
break;
|
||
|
||
case ESP_GATTC_CONGEST_EVT:
|
||
// BLE链路拥塞状态变化
|
||
gattc_congested = param->congest.congested;
|
||
if (gattc_congested) {
|
||
ESP_LOGW(TAG, "BLE链路拥塞,暂停发送");
|
||
} else {
|
||
ESP_LOGI(TAG, "BLE链路恢复,继续发送");
|
||
}
|
||
break;
|
||
|
||
case ESP_GATTC_CLOSE_EVT:
|
||
case ESP_GATTC_DISCONNECT_EVT:
|
||
ESP_LOGI(TAG, "GATTC断开连接");
|
||
client_conn_id = 0;
|
||
client_write_handle = 0;
|
||
gattc_congested = false;
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
// === 图片发送任务 ===
|
||
|
||
static void send_image_task(void *arg) {
|
||
ESP_LOGI(TAG, "开始发送图片...");
|
||
|
||
// 获取当前显示的图片文件名
|
||
const char *img_name = get_current_image();
|
||
if (!img_name) {
|
||
ESP_LOGE(TAG, "没有可发送的图片");
|
||
goto send_fail;
|
||
}
|
||
|
||
// 打开图片文件
|
||
char filepath[48];
|
||
snprintf(filepath, sizeof(filepath), "/spiflash/%s", img_name);
|
||
FILE *f = fopen(filepath, "rb");
|
||
if (!f) {
|
||
ESP_LOGE(TAG, "无法打开图片: %s", filepath);
|
||
goto send_fail;
|
||
}
|
||
|
||
// 获取文件大小
|
||
fseek(f, 0, SEEK_END);
|
||
long file_size = ftell(f);
|
||
fseek(f, 0, SEEK_SET);
|
||
ESP_LOGI(TAG, "图片: %s, 大小: %ld 字节", img_name, file_size);
|
||
|
||
// === 发送前序数据包 ===
|
||
// 格式: [0xFD] [filename 22B] [size 3B big-endian] = 26 bytes
|
||
uint8_t header[26];
|
||
header[0] = 0xFD;
|
||
memset(header + 1, 0, 22);
|
||
strncpy((char *)(header + 1), img_name, 22);
|
||
header[23] = (file_size >> 16) & 0xFF;
|
||
header[24] = (file_size >> 8) & 0xFF;
|
||
header[25] = file_size & 0xFF;
|
||
|
||
esp_err_t ret = esp_ble_gattc_write_char(client_gattc_if, client_conn_id,
|
||
client_write_handle, sizeof(header), header,
|
||
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||
if (ret != ESP_OK) {
|
||
ESP_LOGE(TAG, "前序包写入失败: %s", esp_err_to_name(ret));
|
||
fclose(f);
|
||
goto send_fail;
|
||
}
|
||
vTaskDelay(pdMS_TO_TICKS(50)); // 等待对端建立接收缓冲区
|
||
|
||
// === 分包发送图片数据 ===
|
||
// 格式: [pkt_no 1B] [isEnd 1B] [data ...]
|
||
int max_write_len = client_mtu - 3;
|
||
if (max_write_len > 509) max_write_len = 509;
|
||
int payload_size = max_write_len - 2;
|
||
ESP_LOGI(TAG, "MTU=%d, 每包最大数据=%d字节", client_mtu, payload_size);
|
||
|
||
uint8_t pkt_buf[512];
|
||
int pkt_no = 0;
|
||
size_t total_sent = 0;
|
||
gattc_congested = false;
|
||
|
||
while (total_sent < file_size) {
|
||
if (send_cancel_flag) {
|
||
ESP_LOGW(TAG, "发送被取消");
|
||
fclose(f);
|
||
goto send_fail;
|
||
}
|
||
|
||
// 拥塞等待
|
||
int congest_wait = 0;
|
||
while (gattc_congested && congest_wait < 500) {
|
||
vTaskDelay(pdMS_TO_TICKS(10));
|
||
congest_wait += 10;
|
||
}
|
||
if (congest_wait >= 500) {
|
||
ESP_LOGE(TAG, "拥塞超时,传输中止");
|
||
fclose(f);
|
||
goto send_fail;
|
||
}
|
||
|
||
size_t remaining = file_size - total_sent;
|
||
size_t chunk = (remaining > payload_size) ? payload_size : remaining;
|
||
bool is_last = (total_sent + chunk >= file_size);
|
||
|
||
pkt_buf[0] = (uint8_t)(pkt_no & 0xFF);
|
||
pkt_buf[1] = is_last ? 1 : 0;
|
||
size_t read_len = fread(pkt_buf + 2, 1, chunk, f);
|
||
if (read_len != chunk) {
|
||
ESP_LOGE(TAG, "文件读取错误");
|
||
fclose(f);
|
||
goto send_fail;
|
||
}
|
||
|
||
ret = esp_ble_gattc_write_char(client_gattc_if, client_conn_id,
|
||
client_write_handle, read_len + 2, pkt_buf,
|
||
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||
if (ret != ESP_OK) {
|
||
// API层面失败,递增延时重试
|
||
int attempt = 0;
|
||
while (ret != ESP_OK && attempt < 5) {
|
||
attempt++;
|
||
vTaskDelay(pdMS_TO_TICKS(20 * attempt));
|
||
ret = esp_ble_gattc_write_char(client_gattc_if, client_conn_id,
|
||
client_write_handle, read_len + 2, pkt_buf,
|
||
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||
}
|
||
if (ret != ESP_OK) {
|
||
ESP_LOGE(TAG, "写入失败(重试耗尽): %s", esp_err_to_name(ret));
|
||
fclose(f);
|
||
goto send_fail;
|
||
}
|
||
}
|
||
|
||
total_sent += chunk;
|
||
pkt_no++;
|
||
|
||
// 每包间隔20ms流控
|
||
vTaskDelay(pdMS_TO_TICKS(20));
|
||
|
||
if (pkt_no % 50 == 0) {
|
||
ESP_LOGI(TAG, "已发送 %d/%ld 字节 (%d%%)",
|
||
(int)total_sent, file_size, (int)(total_sent * 100 / file_size));
|
||
}
|
||
}
|
||
|
||
fclose(f);
|
||
ESP_LOGI(TAG, "图片发送完成!共 %d 包,%ld 字节", pkt_no, file_size);
|
||
|
||
// 等待对端处理完成
|
||
vTaskDelay(pdMS_TO_TICKS(500));
|
||
|
||
// 断开连接并关闭蓝牙
|
||
ESP_LOGI(TAG, "发送完成:断开连接,关闭蓝牙");
|
||
esp_ble_gattc_close(client_gattc_if, client_conn_id);
|
||
dzbj_ble_stop();
|
||
|
||
// 传输成功,跳转到Img界面
|
||
xfer_state = BLE_XFER_DONE;
|
||
if (lvgl_port_lock(100)) {
|
||
_ui_screen_change(&ui_ScreenImg, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenImg_screen_init);
|
||
lvgl_port_unlock();
|
||
}
|
||
dzbj_button_set_context(BTN_CTX_IMG);
|
||
xfer_state = BLE_XFER_IDLE;
|
||
|
||
send_task_handle = NULL;
|
||
vTaskDelete(NULL);
|
||
return;
|
||
|
||
send_fail:
|
||
xfer_state = BLE_XFER_FAILED;
|
||
ESP_LOGW(TAG, "发送失败:断开连接,关闭蓝牙");
|
||
esp_ble_gattc_close(client_gattc_if, client_conn_id);
|
||
dzbj_ble_stop();
|
||
if (lvgl_port_lock(100)) {
|
||
_ui_screen_change(&ui_ScreenImg, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenImg_screen_init);
|
||
lvgl_port_unlock();
|
||
}
|
||
dzbj_button_set_context(BTN_CTX_IMG);
|
||
xfer_state = BLE_XFER_IDLE;
|
||
|
||
send_task_handle = NULL;
|
||
vTaskDelete(NULL);
|
||
}
|
||
|
||
// === 接收完成回调(由 dzbj_ble.c 的 GATTS 写入完成时调用) ===
|
||
|
||
void ble_transfer_on_receive_complete(void) {
|
||
if (!receiving_mode) return;
|
||
|
||
ESP_LOGI(TAG, "接收方:图片接收完成");
|
||
receiving_mode = false;
|
||
// dzbj_ble_stop 和 key_nav_set_context 由 ble_process_task 统一处理
|
||
|
||
xfer_state = BLE_XFER_DONE;
|
||
xfer_state = BLE_XFER_IDLE;
|
||
}
|