/** * @file fatfs.c * @brief SPIFFS 文件系统管理模块(从 dzbj 移植) * * 提供 SPIFFS 挂载、文件读写、JPEG 解码等功能。 */ #include "esp_err.h" #include "esp_log.h" #include "esp_spiffs.h" #include "fatfs.h" #include #include #include #include static const char *TAG = "FATFS"; // 初始化SPIFFS文件系统 void fatfs_init(void) { esp_vfs_spiffs_conf_t conf = { .base_path = "/spiflash", .partition_label = "storage", .max_files = 5, .format_if_mount_failed = true, }; esp_err_t err = esp_vfs_spiffs_register(&conf); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to mount SPIFFS (%s)", esp_err_to_name(err)); return; } size_t total = 0, used = 0; err = esp_spiffs_info("storage", &total, &used); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to get SPIFFS info (%s)", esp_err_to_name(err)); } else { ESP_LOGI(TAG, "SPIFFS: Total size: %d, Used: %d", total, used); } } // 读取图片数据到内存 void read_img(uint8_t *img_p) { FILE *f = fopen("/spiflash/img.bin", "r"); if (f == NULL) { ESP_LOGE(TAG, "OPEN ERROR"); return; } size_t size = fread(img_p, sizeof(uint8_t), 129600 * 2, f); fclose(f); if (size != 0) { ESP_LOGI(TAG, "read success!"); } } // 测试FATFS文件系统 void fs_test(void) { FILE *f = fopen("/spiflash/img.bin", "r"); if (f == NULL) { ESP_LOGE(TAG, "Failed to open file for reading"); return; } uint8_t line[2]; fread(line, sizeof(uint8_t), 2, f); fclose(f); ESP_LOGI(TAG, "Read from file: %x %x", line[0], line[1]); } // 列出目录下所有文件名 void fatfs_list_all_filenames(const char *dir_path, bool recursive) { DIR *dir = opendir(dir_path); if (dir == NULL) { ESP_LOGE(TAG, "无法打开目录: %s", dir_path); return; } struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } char full_path[512]; snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name); struct stat file_stat; if (stat(full_path, &file_stat) == 0 && S_ISDIR(file_stat.st_mode)) { if (recursive) { fatfs_list_all_filenames(full_path, recursive); } } else if (stat(full_path, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) { ESP_LOGI(TAG, "文件名: %s, 大小:%d", full_path, (int)file_stat.st_size); } } closedir(dir); } // 删除目录下所有空文件 void fatfs_remove_nullData(const char *dir_path) { DIR *dir = opendir(dir_path); if (dir == NULL) { ESP_LOGE(TAG, "无法打开目录: %s", dir_path); return; } struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } char full_path[512]; snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, (char*)entry->d_name); struct stat file_stat; stat(full_path, &file_stat); if ((int)file_stat.st_size == 0) { remove(full_path); ESP_LOGE(TAG, "删除空文件: %s", full_path); } } closedir(dir); } // 删除目录下所有文件 void fatfs_remove_allData(const char *dir_path) { DIR *dir = opendir(dir_path); if (dir == NULL) { ESP_LOGE(TAG, "无法打开目录: %s", dir_path); return; } struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } char full_path[512]; snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, (char*)entry->d_name); remove(full_path); ESP_LOGE(TAG, "删除文件: %s", full_path); } closedir(dir); } // 检查图片是否有效(文件大小不为0) bool fats_img_isOK(char* img_path) { struct stat file_stat; stat(img_path, &file_stat); return file_stat.st_size > 0; } // JPEG 解码:从 SPIFFS 读取 JPEG 并解码为 RGB565 esp_err_t DecodeImg(char *imgpath, uint8_t** imgData, esp_jpeg_image_output_t *outimage) { FILE *f = fopen(imgpath, "rb"); if (f == NULL) { ESP_LOGE(TAG, "OPEN ERROR: %s", imgpath); return ESP_FAIL; } struct stat file_stat; stat(imgpath, &file_stat); // 分配输出缓冲区(360×360 RGB565) *imgData = malloc(360 * 360 * 2); if (*imgData == NULL) { ESP_LOGE(TAG, "输出缓冲区分配失败"); fclose(f); return ESP_FAIL; } // 分配输入缓冲区(JPEG 原始数据) uint8_t *imgEncoderData = malloc(file_stat.st_size); if (imgEncoderData == NULL) { ESP_LOGE(TAG, "输入缓冲区分配失败(需%d字节)", (int)file_stat.st_size); free(*imgData); *imgData = NULL; fclose(f); return ESP_FAIL; } size_t read_len = fread(imgEncoderData, sizeof(uint8_t), file_stat.st_size, f); fclose(f); if (read_len != (size_t)file_stat.st_size) { ESP_LOGE(TAG, "文件读取不完整(预期:%d,实际:%zu)", (int)file_stat.st_size, read_len); free(imgEncoderData); free(*imgData); *imgData = NULL; return ESP_FAIL; } // 验证 JPEG 头 if (file_stat.st_size < 2 || imgEncoderData[0] != 0xFF || imgEncoderData[1] != 0xD8) { ESP_LOGE(TAG, "不是有效JPEG文件: %s", imgpath); free(imgEncoderData); free(*imgData); *imgData = NULL; return ESP_FAIL; } uint32_t outbuf_size = 360 * 360 * sizeof(uint8_t) * 2; esp_jpeg_image_cfg_t jpeg_cfg = { .indata = imgEncoderData, .indata_size = file_stat.st_size, .outbuf = *imgData, .outbuf_size = outbuf_size, .out_format = JPEG_IMAGE_FORMAT_RGB565, .flags = { .swap_color_bytes = true, }, }; esp_err_t ret = esp_jpeg_decode(&jpeg_cfg, outimage); free(imgEncoderData); return ret; } // 测试读取图片数据 void test_readimg(char *imgpath, uint16_t size) { FILE *f = fopen(imgpath, "r"); if (f == NULL) { ESP_LOGE(TAG, "OPEN ERROR"); return; } uint8_t *head = malloc(size); if (head == NULL) { fclose(f); return; } fread(head, sizeof(uint8_t), size, f); fclose(f); for (int i = 0; i < size; i++) { printf("%x ", *(head + i)); } printf("\n"); free(head); } // 获取目录下所有图片文件名 void fat_getAllimgList(const char *dir_path, char** list, uint8_t* num) { *num = 0; DIR *dir = opendir(dir_path); if (dir == NULL) { ESP_LOGE(TAG, "无法打开目录: %s", dir_path); return; } struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } list[*num] = strdup(entry->d_name); (*num)++; } closedir(dir); }