Rdzleo 811559be49 feat: 修复BLE蓝牙传图全链路问题 + 实现图片导航显示
1. BLE通信修复:
   - 补充服务UUID长度字段(ESP_UUID_LEN_16),修复APP端发现服务失败
   - 添加WRITE_NR属性,支持writeNoResponse提升传输速度
   - 文件写入改为二进制模式"wb",防止数据损坏
   - 添加数据完整性校验日志

2. JPEG解码修复:
   - 移除过严的FFD9结束标记校验(兼容Android JPEG编码器)

3. 图片导航功能(新增):
   - ble_image_navigate():BLE接收后自动导航到ScreenImg显示新图片
   - set_image_index_by_name():按文件名定位图片列表索引
   - 智能检测当前界面:不在ScreenImg则切换,已在则直接更新
   - 修复update_ui_ImgBle解码失败时double-free崩溃

4. ScreenImg界面优化:
   - 每次进入界面都显示当前图片,支持BLE导航和手势切换场景

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:15:41 +08:00

917 lines
33 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "lvgl.h"
#include "fatfs.h"
#include "driver/ledc.h"
#include "gpio.h"
#include "wifi.h"
#include "jpeg_decoder.h"
#include "../ui/screens/ui_ScreenImg.h"
#include <inttypes.h>
// 前向声明界面切换函数
extern void _ui_screen_change(lv_obj_t **target, lv_scr_load_anim_t fademode, int spd, int delay, void (*target_init)(void));
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <strings.h>
#include <stdbool.h>
char img_path[40];
char *img_filename;
lv_obj_t *app_img;
lv_obj_t *act_mainscreen;
uint8_t *app_img_data = 0;
esp_jpeg_image_output_t outdata;
lv_img_dsc_t image;
#define MAX_IMAGE_FILES 10
#define MAX_FILENAME_LEN 32
static char spiffs_image_files[MAX_IMAGE_FILES][MAX_FILENAME_LEN];
static int spiffs_image_count = 0;
static int current_image_index = 0;
static bool image_list_initialized = false;
// 当前亮度值(用于休眠恢复)
static uint8_t current_brightness = 50;
// 获取当前亮度值
uint8_t pwm_get_brightness(void) {
return current_brightness;
}
// 设置屏幕亮度percent范围0-100
// 0=完全关闭背光10~100为正常亮度范围
// 显示10%~100%映射到实际亮度20%~100%,背光低电平有效需反转占空比
void pwm_set_brightness(uint8_t percent) {
if (percent == 0) {
// 完全关闭背光低电平有效占空比100%=全高=关闭)
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8191);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
return;
}
if (percent < 10) percent = 10;
if (percent > 100) percent = 100;
current_brightness = percent;
uint32_t actual = 20 + (uint32_t)(percent - 10) * 80 / 90;
uint32_t duty = 8191 - (8191 * actual) / 100;
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
}
// 初始化PWM
void pwm_init(){
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
.duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 5000,
.clk_cfg = LEDC_AUTO_CLK
};
ledc_timer_config(&ledc_timer);// 配置PWM定时器
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = PIN_LCD_EN,
.duty = 0,
.hpoint = 0
};
ledc_channel_config(&ledc_channel);// 配置PWM通道
pwm_set_brightness(50);// 初始亮度50%
// ledc_timer_config_t motor_timer = {
// .speed_mode = LEDC_LOW_SPEED_MODE,
// .timer_num = LEDC_TIMER_1,
// .duty_resolution = LEDC_TIMER_13_BIT,
// .freq_hz = 5000,
// .clk_cfg = LEDC_AUTO_CLK
// };
// ledc_timer_config(&motor_timer);
// ledc_channel_config_t motor_channel = {
// .speed_mode = LEDC_LOW_SPEED_MODE,
// .channel = LEDC_CHANNEL_1,
// .timer_sel = LEDC_TIMER_0,
// .intr_type = LEDC_INTR_DISABLE,
// .gpio_num = PIN_MOTOR_EN,
// .duty = 4095,
// .hpoint = 0
// };
// ledc_channel_config(&motor_channel);
// ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, 0);
// ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1);
}
// 测试扫描WiFi列表
void wifi_scan_list_test(){
wifi_ap_record_t* wifi_list = NULL;// 定义WiFi列表指针
uint16_t num = 0;// 定义WiFi数量变量
esp_err_t err = wifi_scan_list(&num,&wifi_list);// 扫描WiFi列表
if(err == ESP_OK){
ESP_LOGI("WIFI","列表获取成功,数量:%d",num);// 打印WiFi数量
for (int i = 0; i < num; i++) {
ESP_LOGI("WIFI", "AP %d - SSID: %s, RSSI: %d", i + 1, wifi_list[i].ssid, wifi_list[i].rssi);// 打印每个WiFi的SSID和RSSI
}
}
}
// 测试连接WiFi
void wifi_connect_test(){
wifi_config_t wifi_config = {
.sta = {
.password = "12345678",
.ssid = "LDL的iPhone"
}
};
esp_wifi_set_config(ESP_IF_WIFI_STA,&wifi_config);// 设置WiFi配置
esp_err_t err = esp_wifi_connect();// 连接WiFi
if(err == ESP_OK){
ESP_LOGI("WIFI","WIFI连接成功");
}
}
// 测试断开WiFi连接
void wifi_disconnect_test(){
esp_err_t err = esp_wifi_disconnect();// 断开WiFi连接
if(err == ESP_OK){
ESP_LOGI("WIFI","已断开WiFi连接");
}
}
// 测试开始扫描WiFi
void wifi_scan_start_test(){
wifi_scan_start();// 开始扫描WiFi
}
// 测试获取可用堆内存
void free_heap_test(){
ESP_LOGI("HEAP","可用堆内存:%d",(int)heap_caps_get_total_size(MALLOC_CAP_SPIRAM));
}
lv_obj_t *act_wifiscreen;// 当前WiFi屏幕对象
lv_obj_t *act_testscreen;// 当前测试屏幕对象
lv_obj_t *act_mainscreen;// 当前主屏幕对象
void app_wifi_display(){
}
// 从NVS中读取图片路径
esp_err_t nvs_read_img(void) {
nvs_handle_t nvs_handle; // NVS 句柄
esp_err_t err; // NVS 错误码
err = nvs_open("config", NVS_READONLY, &nvs_handle);// 打开 NVS 句柄
if (err != ESP_OK) return err; // 如果打开失败,返回错误码
size_t imgname_len;
err = nvs_get_str(nvs_handle, "img_filename", NULL, &imgname_len);// 获取图片路径长度
if (err == ESP_OK) {
img_filename = malloc(imgname_len);// 分配内存
err = nvs_get_str(nvs_handle, "img_filename", img_filename, &imgname_len);// 获取图片路径
if (err != ESP_OK) {
nvs_close(nvs_handle);// 关闭 NVS 句柄
return err; // 如果获取失败,返回错误码
}
ESP_LOGI("NVS", "img_filename: %s", img_filename);// 打印图片路径
}
nvs_close(nvs_handle);// 关闭 NVS 句柄
return err;
}
// 测试改变NVS中的图片路径
esp_err_t nvs_change_img(char *imgname) {
nvs_handle_t nvs_handle;// NVS 句柄
esp_err_t err;
err = nvs_open("config", NVS_READWRITE, &nvs_handle);// 打开 NVS 句柄
if (err != ESP_OK) goto close_handle;
err = nvs_set_str(nvs_handle, "img_filename", imgname);// 设置图片路径
if (err != ESP_OK) goto close_handle;
err = nvs_commit(nvs_handle);// 提交更改
if (err != ESP_OK) goto close_handle;// 如果提交失败,关闭句柄并返回错误码
close_handle:
nvs_close(nvs_handle); // 关闭 NVS 句柄
return err;
}
// 仅更新现有图片,显示其他图片
// img_name: 图片文件名为NULL时从NVS读取
void app_img_change(const char *img_name){
// 释放之前的图片数据
if(app_img_data){
free(app_img_data);
app_img_data = NULL;
ESP_LOGI("IMG", "释放之前显示的图片数据缓存");
}
const char *current_img_name = img_name;
// 如果没有指定图片名从NVS读取
if(!current_img_name) {
esp_err_t ret_nvs = nvs_read_img();// 从NVS中读取图片路径
if(ret_nvs != ESP_OK){
ESP_LOGE("NVS","图片路径获取失败2");
return;
}
current_img_name = img_filename;
}
// 构建图片路径
snprintf(img_path, sizeof(img_path), "/spiflash/%s", current_img_name);// 格式化图片路径
ESP_LOGI("IMG", "准备显示图片: %s, 路径: %s", current_img_name, img_path);
// 检查文件是否存在
struct stat file_stat;
if(stat(img_path, &file_stat) != 0) {
ESP_LOGE("IMG", "文件不存在: %s", img_path);
return;
}
ESP_LOGI("IMG", "文件大小: %ld 字节", file_stat.st_size);
// 解码图片
esp_err_t ret = DecodeImg(img_path,&app_img_data,&outdata);// 解码图片
if(ret == ESP_OK){
ESP_LOGI("IMG", "图片解码成功,数据地址: %p, 宽度: %d, 高度: %d",
app_img_data, outdata.width, outdata.height);
// 检查解码后的数据
if(app_img_data == NULL) {
ESP_LOGE("IMG", "解码数据为空");
return;
}
// 配置图片数据
image.header.cf = LV_IMG_CF_TRUE_COLOR;
image.header.always_zero = 0;
image.header.reserved = 0;
image.header.w = outdata.width;
image.header.h = outdata.height;
image.data_size = outdata.output_len;
image.data = app_img_data;
// 获取屏幕对象
act_mainscreen = lv_scr_act();
if(act_mainscreen == NULL) {
ESP_LOGE("IMG", "获取屏幕对象失败");
return;
}
// 如果图片对象不存在,创建它
if(app_img == NULL) {
app_img = lv_img_create(act_mainscreen);
if(app_img == NULL) {
ESP_LOGE("IMG", "创建图片对象失败");
return;
}
lv_obj_center(app_img);
ESP_LOGI("IMG", "创建图片对象成功");
}
// 更新图片显示
lvgl_port_lock(0);// 锁定LVGL端口
lv_img_set_src(app_img, &image);// 设置图片源
lv_scr_load(act_mainscreen);// 加载主屏幕
lvgl_port_unlock();// 解锁LVGL端口
ESP_LOGI("IMG", "图片显示成功: %s", current_img_name);
} else {
ESP_LOGE("IMG", "图片解码失败,错误码: %d", ret);
}
}
// 完整的图片显示初始化
void app_img_display(){
ESP_LOGI("IMG", "开始显示图片");
esp_err_t ret_nvs = nvs_read_img();// 从NVS中读取图片路径
if(ret_nvs != ESP_OK){
ESP_LOGE("NVS","图片路径获取失败1");
return;
}
ESP_LOGI("IMG", "图片路径: %s", img_filename);
snprintf(img_path, sizeof(img_path), "/spiflash/%s",img_filename);// 格式化图片路径
ESP_LOGI("IMG", "完整路径: %s", img_path);
// 检查文件是否存在
struct stat file_stat;
if(stat(img_path, &file_stat) != 0){
ESP_LOGE("IMG", "文件不存在");
return;
}
ESP_LOGI("IMG", "文件大小: %ld 字节", file_stat.st_size);
esp_err_t ret = DecodeImg(img_path,&app_img_data,&outdata);// 解码图片
if(ret == ESP_OK){
ESP_LOGI("IMG", "图片解码成功,数据地址: %p", app_img_data);
// 检查解码后的数据
if(app_img_data == NULL){
ESP_LOGE("IMG", "解码数据为空");
return;
}
image.header.cf = LV_IMG_CF_TRUE_COLOR;
image.header.always_zero = 0;
image.header.reserved = 0;
image.header.w = outdata.width;
image.header.h = outdata.height;
image.data_size = outdata.output_len;
image.data = app_img_data;
ESP_LOGI("IMG", "LV_IMG_CF_RGB565 值: %d", LV_IMG_CF_RGB565);
ESP_LOGI("IMG", "设置图片数据: 宽度=%lu, 高度=%lu, 数据大小=%lu", (unsigned long)image.header.w, (unsigned long)image.header.h, (unsigned long)image.data_size);
act_mainscreen = lv_scr_act();// 获取当前主屏幕对象
if(act_mainscreen == NULL){
ESP_LOGE("IMG", "获取屏幕对象失败");
return;
}
ESP_LOGI("IMG", "获取屏幕对象成功");
app_img = lv_img_create(act_mainscreen);// 创建图片对象
if(app_img == NULL){
ESP_LOGE("IMG", "创建图片对象失败");
return;
}
ESP_LOGI("IMG", "创建图片对象成功");
lvgl_port_lock(0);// 锁定LVGL端口
ESP_LOGI("IMG", "设置图片源前");
lv_img_set_src(app_img, &image);// 设置图片源
ESP_LOGI("IMG", "设置图片源后");
lv_obj_center(app_img);// 居中显示图片
ESP_LOGI("IMG", "居中显示图片后");
lv_scr_load(act_mainscreen);// 加载主屏幕
ESP_LOGI("IMG", "加载主屏幕后");
lvgl_port_unlock();// 解锁LVGL端口
vTaskDelay(50);// 延时50ms
pwm_init();// 初始化PWM
ESP_LOGI("IMG", "图片显示完成");
} else {
ESP_LOGE("IMG", "图片解码失败,错误码: %d", ret);
}
}
// // 图片切换任务
// void img_switch_task(void *pvParameters) {
// char *image_files[] = {"default.jpg", "02.jpg"};
// int file_count = 2;
// int current_index = 0;
// while(1) {
// // 释放之前的图片数据
// if(app_img_data) {
// free(app_img_data);
// app_img_data = NULL;
// }
// // 获取当前要显示的图片文件名
// const char *current_image = image_files[current_index];
// ESP_LOGI("IMG_SWITCH", "切换到图片: %s", current_image);
// // 构建图片路径
// snprintf(img_path, sizeof(img_path), "/spiflash/%s", current_image);
// ESP_LOGI("IMG_SWITCH", "图片路径: %s", img_path);
// // 检查文件是否存在
// struct stat file_stat;
// if(stat(img_path, &file_stat) != 0) {
// ESP_LOGE("IMG_SWITCH", "文件不存在");
// vTaskDelay(pdMS_TO_TICKS(2000));
// continue;
// }
// ESP_LOGI("IMG_SWITCH", "文件大小: %ld 字节", file_stat.st_size);
// // 解码图片
// esp_err_t ret = DecodeImg(img_path, &app_img_data, &outdata);
// if(ret == ESP_OK) {
// ESP_LOGI("IMG_SWITCH", "图片解码成功,数据地址: %p", app_img_data);
// // 检查解码后的数据
// if(app_img_data == NULL) {
// ESP_LOGE("IMG_SWITCH", "解码数据为空");
// vTaskDelay(pdMS_TO_TICKS(2000));
// continue;
// }
// // 配置图片数据
// image.header.cf = LV_IMG_CF_TRUE_COLOR;
// image.header.always_zero = 0;
// image.header.reserved = 0;
// image.header.w = outdata.width;
// image.header.h = outdata.height;
// image.data_size = outdata.output_len;
// image.data = app_img_data;
// // 获取屏幕对象
// act_mainscreen = lv_scr_act();
// if(act_mainscreen == NULL) {
// ESP_LOGE("IMG_SWITCH", "获取屏幕对象失败");
// vTaskDelay(pdMS_TO_TICKS(2000));
// continue;
// }
// // 如果图片对象不存在,创建它
// if(app_img == NULL) {
// app_img = lv_img_create(act_mainscreen);
// if(app_img == NULL) {
// ESP_LOGE("IMG_SWITCH", "创建图片对象失败");
// vTaskDelay(pdMS_TO_TICKS(2000));
// continue;
// }
// lv_obj_center(app_img);
// }
// // 更新图片显示
// lvgl_port_lock(0);
// lv_img_set_src(app_img, &image);
// lv_scr_load(act_mainscreen);
// lvgl_port_unlock();
// ESP_LOGI("IMG_SWITCH", "图片显示成功");
// } else {
// ESP_LOGE("IMG_SWITCH", "图片解码失败,错误码: %d", ret);
// }
// // 切换到下一张图片
// current_index = (current_index + 1) % file_count;
// // 等待2秒
// vTaskDelay(pdMS_TO_TICKS(2000));
// }
// }
// 新的显示测试屏幕函数
void app_test_display(){
lvgl_port_lock(0);// 锁定LVGL端口
// 获取或创建屏幕对象
lv_obj_t *screen = lv_scr_act();
if(screen == NULL) {
screen = lv_obj_create(NULL);// 创建屏幕对象
if(screen == NULL) {
ESP_LOGE("TEST", "Failed to create screen object");// 创建屏幕对象失败
lvgl_port_unlock();// 解锁LVGL端口
return;
}
}
// 清空屏幕
lv_obj_clean(screen);
// 创建标签
lv_obj_t *label = lv_label_create(screen);
if(label) {
lv_label_set_text(label, "Test Screen\nLCD is working!");// 设置标签文本
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
// 创建基本样式
static lv_style_t style;// 基本样式
lv_style_init(&style);// 初始化基本样式
lv_style_set_text_font(&style, &lv_font_montserrat_14);// 设置字体
lv_style_set_text_color(&style, lv_color_hex(0xf9076a));// 设置文本颜色
lv_style_set_bg_color(&style, lv_color_hex(0x000000));// 设置背景颜色
lv_obj_add_style(label, &style, 0);// 添加样式到标签
}
lv_scr_load(screen);// 加载屏幕
lvgl_port_unlock();// 解锁LVGL端口
}
// 图片循环显示任务 - 显示spiffs中的所有图片
void img_loop_task(void *pvParameters) {
// 存储SPIFFS中的图片文件名
char *image_files[10]; // 最多支持10张图片
int file_count = 0;
int current_index = 0;
static bool backlight_initialized = false;
// 初始化背光(只执行一次)
if(!backlight_initialized) {
pwm_init();
backlight_initialized = true;// 初始化背光
ESP_LOGI("IMG_LOOP", "背光初始化完成");
}
while(1) {
// 重新扫描SPIFFS中的图片文件
ESP_LOGI("IMG_LOOP", "开始扫描SPIFFS中的图片文件");
// 打开SPIFFS目录
DIR *dir = opendir("/spiflash");
if(!dir) {
ESP_LOGE("IMG_LOOP", "无法打开SPIFFS目录");
vTaskDelay(pdMS_TO_TICKS(3000));
continue;
}
// 重置文件计数
file_count = 0;
// 遍历目录
struct dirent *entry;
while((entry = readdir(dir)) != NULL && file_count < 10) {
// 检查是否是图片文件(.jpg, .jpeg, .png等
const char *name = entry->d_name;
int len = strlen(name);
if(len > 4) {
const char *ext = name + len - 4;
if(strcasecmp(ext, ".jpg") == 0 || strcasecmp(ext, ".jpeg") == 0 ||
strcasecmp(ext, ".png") == 0 || strcasecmp(ext, ".bmp") == 0) {
// 存储图片文件名
image_files[file_count] = strdup(name);
ESP_LOGI("IMG_LOOP", "发现图片文件: %s", name);
file_count++;
}
}
}
closedir(dir);
// 检查是否找到图片
if(file_count == 0) {
ESP_LOGE("IMG_LOOP", "未找到图片文件");
vTaskDelay(pdMS_TO_TICKS(3000));
continue;
}
ESP_LOGI("IMG_LOOP", "共发现 %d 张图片,开始循环显示", file_count);
// 循环显示所有图片
for(current_index = 0; current_index < file_count; current_index++) {
const char *current_image = image_files[current_index];
ESP_LOGI("IMG_LOOP", "显示图片 %d/%d: %s", current_index + 1, file_count, current_image);
// 使用修改后的app_img_change函数显示图片
app_img_change(current_image);
// 等待3秒
vTaskDelay(pdMS_TO_TICKS(3000));
}
// 释放文件名内存
for(int i = 0; i < file_count; i++) {
free(image_files[i]);
}
// 再次扫描前短暂延时
vTaskDelay(pdMS_TO_TICKS(500));
}
}
// 图片切换任务 - 显示指定的两张图片
void img_switch_task(void *pvParameters) {
char *image_files[] = {"default.jpg", "02.jpg"};
int file_count = 2;
int current_index = 0;
while(1) {
// 使用修改后的app_img_change函数显示图片
const char *current_image = image_files[current_index];
ESP_LOGI("IMG_SWITCH", "切换到图片: %s", current_image);
app_img_change(current_image);
// 切换到下一张图片
current_index = (current_index + 1) % file_count;
// 等待2秒
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
// 初始化SPIFFS图片列表
void init_spiffs_image_list(void) {
if(image_list_initialized) {
ESP_LOGI("IMG_LIST", "图片列表已初始化,跳过");
return;
}
ESP_LOGI("IMG_LIST", "开始扫描SPIFFS中的图片文件");
// 打开SPIFFS目录
DIR *dir = opendir("/spiflash");
if(!dir) {
ESP_LOGE("IMG_LIST", "无法打开SPIFFS目录");
return;
}
// 重置文件计数
spiffs_image_count = 0;
// 遍历目录
struct dirent *entry;
while((entry = readdir(dir)) != NULL && spiffs_image_count < MAX_IMAGE_FILES) {
// 检查是否是图片文件(.jpg, .jpeg, .png等
const char *name = entry->d_name;
int len = strlen(name);
if(len > 4 && len < MAX_FILENAME_LEN) {
const char *ext = name + len - 4;
if(strcasecmp(ext, ".jpg") == 0 || strcasecmp(ext, ".jpeg") == 0 ||
strcasecmp(ext, ".png") == 0 || strcasecmp(ext, ".bmp") == 0) {
// 存储图片文件名到静态缓冲区
strncpy(spiffs_image_files[spiffs_image_count], name, MAX_FILENAME_LEN - 1);
spiffs_image_files[spiffs_image_count][MAX_FILENAME_LEN - 1] = '\0';
ESP_LOGI("IMG_LIST", "发现图片文件: %s", name);
spiffs_image_count++;
}
}
}
closedir(dir);
// 检查是否找到图片
if(spiffs_image_count == 0) {
ESP_LOGE("IMG_LIST", "未找到图片文件");
return;
}
image_list_initialized = true;
ESP_LOGI("IMG_LIST", "图片列表初始化完成,共发现 %d 张图片", spiffs_image_count);
// 查找default.jpg并设置为当前索引
for(int i = 0; i < spiffs_image_count; i++) {
if(strcmp(spiffs_image_files[i], "default.jpg") == 0) {
current_image_index = i;
ESP_LOGI("IMG_LIST", "设置默认图片索引: %d", current_image_index);
break;
}
}
}
// 获取下一张图片
const char* get_next_image(void) {
if(!image_list_initialized || spiffs_image_count == 0) {
ESP_LOGE("IMG_LIST", "图片列表未初始化或为空");
return NULL;
}
current_image_index = (current_image_index + 1) % spiffs_image_count;
ESP_LOGI("IMG_LIST", "切换到下一张图片,索引: %d/%d", current_image_index + 1, spiffs_image_count);
return spiffs_image_files[current_image_index];
}
// 获取上一张图片
const char* get_prev_image(void) {
if(!image_list_initialized || spiffs_image_count == 0) {
ESP_LOGE("IMG_LIST", "图片列表未初始化或为空");
return NULL;
}
current_image_index = (current_image_index - 1 + spiffs_image_count) % spiffs_image_count;
ESP_LOGI("IMG_LIST", "切换到上一张图片,索引: %d/%d", current_image_index + 1, spiffs_image_count);
return spiffs_image_files[current_image_index];
}
// 重置图片列表
void free_spiffs_image_list(void) {
if(!image_list_initialized) {
return;
}
spiffs_image_count = 0;
current_image_index = 0;
image_list_initialized = false;
ESP_LOGI("IMG_LIST", "图片列表已重置");
}
// 根据文件名设置当前图片索引
bool set_image_index_by_name(const char *name) {
if(!image_list_initialized || spiffs_image_count == 0 || !name) {
return false;
}
for(int i = 0; i < spiffs_image_count; i++) {
if(strcmp(spiffs_image_files[i], name) == 0) {
current_image_index = i;
ESP_LOGI("IMG_LIST", "设置图片索引为 %d: %s", i, name);
return true;
}
}
ESP_LOGW("IMG_LIST", "未找到图片: %s", name);
return false;
}
// BLE接收图片后导航到ScreenImg显示
void ble_image_navigate(const char *filename) {
// 刷新图片列表
free_spiffs_image_list();
init_spiffs_image_list();
// 设置当前索引为新接收的图片
set_image_index_by_name(filename);
// 检查是否已在ScreenImg界面
lvgl_port_lock(0);
bool already_on_screen = (lv_scr_act() == ui_ScreenImg);
if (!already_on_screen) {
// 不在ScreenImg导航过去SCREEN_LOADED事件会触发update_ui_ImgBle
_ui_screen_change(&ui_ScreenImg, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenImg_screen_init);
}
lvgl_port_unlock();
// 已在ScreenImg时_ui_screen_change不会触发SCREEN_LOADED需手动更新图片
if (already_on_screen) {
update_ui_ImgBle(filename);
}
ESP_LOGI("IMG_LIST", "BLE导航到ScreenImg显示: %s", filename);
}
// 获取当前图片文件名
const char* get_current_image(void) {
if(!image_list_initialized || spiffs_image_count == 0) {
ESP_LOGE("IMG_LIST", "图片列表未初始化或为空");
return NULL;
}
return spiffs_image_files[current_image_index];
}
// 删除当前图片并从列表中移除
bool delete_current_image(void) {
if(!image_list_initialized || spiffs_image_count == 0) {
ESP_LOGE("IMG_DEL", "图片列表未初始化或为空");
return false;
}
const char *current_img = spiffs_image_files[current_image_index];
// 构建完整路径
char full_path[64];
snprintf(full_path, sizeof(full_path), "/spiflash/%s", current_img);
ESP_LOGI("IMG_DEL", "准备删除图片: %s", full_path);
// 从SPIFFS文件系统中删除文件
if(unlink(full_path) != 0) {
ESP_LOGE("IMG_DEL", "删除文件失败: %s", full_path);
return false;
}
ESP_LOGI("IMG_DEL", "文件删除成功: %s", current_img);
// 从列表中移除该图片
for(int i = current_image_index; i < spiffs_image_count - 1; i++) {
strncpy(spiffs_image_files[i], spiffs_image_files[i + 1], MAX_FILENAME_LEN);
}
// 更新计数
spiffs_image_count--;
// 如果列表为空,重置状态
if(spiffs_image_count == 0) {
ESP_LOGI("IMG_DEL", "所有图片已删除");
image_list_initialized = false;
current_image_index = 0;
return true;
}
// 调整当前索引(如果删除的是最后一张,回到第一张)
if(current_image_index >= spiffs_image_count) {
current_image_index = 0;
}
ESP_LOGI("IMG_DEL", "图片列表已更新,剩余 %d 张图片,当前索引: %d",
spiffs_image_count, current_image_index);
return true;
}
// 更新ui_ImgBle控件的图片
void update_ui_ImgBle(const char *img_name) {
if(!img_name) {
ESP_LOGE("IMG_UI", "图片名为空");
return;
}
if(!ui_ImgBle) {
ESP_LOGE("IMG_UI", "ui_ImgBle控件不存在");
return;
}
static uint8_t *ui_img_data = NULL;
static lv_img_dsc_t ui_image;
// 释放之前的图片数据
if(ui_img_data) {
free(ui_img_data);
ui_img_data = NULL;
ESP_LOGI("IMG_UI", "释放之前的图片数据");
}
// 构建图片路径
snprintf(img_path, sizeof(img_path), "/spiflash/%s", img_name);
ESP_LOGI("IMG_UI", "准备显示图片: %s, 路径: %s", img_name, img_path);
// 检查文件是否存在
struct stat file_stat;
if(stat(img_path, &file_stat) != 0) {
ESP_LOGE("IMG_UI", "文件不存在: %s", img_path);
return;
}
ESP_LOGI("IMG_UI", "文件大小: %ld 字节", file_stat.st_size);
// 解码图片
esp_jpeg_image_output_t ui_outdata;
esp_err_t ret = DecodeImg(img_path, &ui_img_data, &ui_outdata);
if(ret == ESP_OK) {
ESP_LOGI("IMG_UI", "图片解码成功,宽度: %d, 高度: %d", ui_outdata.width, ui_outdata.height);
// 检查解码后的数据
if(ui_img_data == NULL) {
ESP_LOGE("IMG_UI", "解码数据为空");
return;
}
// 配置图片数据
ui_image.header.cf = LV_IMG_CF_TRUE_COLOR;
ui_image.header.always_zero = 0;
ui_image.header.reserved = 0;
ui_image.header.w = ui_outdata.width;
ui_image.header.h = ui_outdata.height;
ui_image.data_size = ui_outdata.output_len;
ui_image.data = ui_img_data;
// 更新ui_ImgBle控件的图片
lvgl_port_lock(0);
lv_img_set_src(ui_ImgBle, &ui_image);
lvgl_port_unlock();
ESP_LOGI("IMG_UI", "ui_ImgBle图片更新成功: %s", img_name);
} else {
ESP_LOGE("IMG_UI", "图片解码失败,错误码: %d", ret);
ui_img_data = NULL;
}
}
// // 原本显示测试屏幕 代码
// void app_test_display(){
// lvgl_port_lock(0);// 锁定LVGL端口
// act_testscreen = lv_scr_act();// 获取当前测试屏幕对象
// // 创建列表样式
// static lv_style_t list_style;
// lv_style_init(&list_style);
// lv_style_set_bg_color(&list_style, lv_color_hex(0x1E1E1E));
// lv_style_set_border_width(&list_style, 0);
// lv_style_set_radius(&list_style, 0);
// lv_style_set_text_align(&list_style, LV_TEXT_ALIGN_CENTER);
// // 创建列表项样式
// static lv_style_t list_item_style;
// lv_style_init(&list_item_style);
// lv_style_set_bg_color(&list_item_style, lv_color_hex(0x2E2E2E));
// lv_style_set_bg_opa(&list_item_style, LV_OPA_100);
// lv_style_set_text_color(&list_item_style, lv_color_hex(0xFFFFFF));
// lv_style_set_pad_all(&list_item_style, 10);
// lv_style_set_min_height(&list_item_style, 80); // 设置最小高度为60px
// // 创建选中项样式
// static lv_style_t list_item_selected_style;
// lv_style_init(&list_item_selected_style);
// lv_style_set_bg_color(&list_item_selected_style, lv_color_hex(0x4A90E2));
// lv_style_set_text_color(&list_item_selected_style, lv_color_hex(0xFFFFFF));
// lv_obj_t* list = lv_list_create(act_testscreen);
// lv_obj_add_style(list, &list_style, 0);
// lv_obj_set_size(list, lv_pct(100), lv_pct(100));
// lv_obj_t* list_item1 = lv_list_add_btn(list, LV_SYMBOL_FILE, "FLASH TEST");
// lv_obj_t* list_item2 = lv_list_add_btn(list, LV_SYMBOL_FILE, "WIFI SCAN START TEST");
// lv_obj_t* list_item3 = lv_list_add_btn(list, LV_SYMBOL_FILE, "WIFI SCAN LIST TEST");
// lv_obj_t* list_item5 = lv_list_add_btn(list, LV_SYMBOL_FILE, "WIFI CONNECT TEST");
// lv_obj_t* list_item6 = lv_list_add_btn(list, LV_SYMBOL_FILE, "WIFI DISCONNECT TEST");
// lv_obj_t* list_item4 = lv_list_add_btn(list, LV_SYMBOL_FILE, "FREE HEAP");
// lv_obj_t* list_item7 = lv_list_add_btn(list, LV_SYMBOL_FILE, "TEMP TEST");
// lv_obj_add_event_cb(list_item1, fs_test, LV_EVENT_CLICKED, NULL);
// lv_obj_add_event_cb(list_item2, wifi_scan_start_test, LV_EVENT_CLICKED, NULL);
// lv_obj_add_event_cb(list_item3, wifi_scan_list_test, LV_EVENT_CLICKED, NULL);
// lv_obj_add_event_cb(list_item4, free_heap_test, LV_EVENT_CLICKED, NULL);
// lv_obj_add_event_cb(list_item5, wifi_connect_test, LV_EVENT_CLICKED, NULL);
// lv_obj_add_event_cb(list_item6, wifi_disconnect_test, LV_EVENT_CLICKED, NULL);
// lv_obj_add_event_cb(list_item7, temp_test, LV_EVENT_CLICKED, NULL);
// lv_obj_t *parent = lv_obj_create(act_mainscreen);
// lv_obj_set_size(parent, 150, 150);
// lv_obj_center(parent); // 居中显示
// static lv_style_t style_bg_black;
// lv_style_init(&style_bg_black);
// lv_style_set_bg_color(&style_bg_black, lv_color_hex(0x000000));
// lv_obj_add_style(act_mainscreen, &style_bg_black, 0);
// static lv_style_t style_img;
// lv_style_init(&style_img);
// lv_style_set_radius(&style_img,75);
// lv_style_set_clip_corner(&style_img, true);
// lv_style_set_border_width(&style_img,75);
// lv_obj_add_style(parent,&style_img,0);
// // lv_obj_t *img = lv_img_create(act_mainscreen);
// // lv_img_set_src(img,"C:spiflash/face_1759919044875.jpg");
// lv_scr_load(act_testscreen);
// lvgl_port_unlock();
// vTaskDelay(50);
// pwm_init();
// }