From 0b3d5ef60a56b3c2357401b4bf36d4c59b145bf8 Mon Sep 17 00:00:00 2001
From: yaozhenquan <1756989764@qq.com>
Date: Tue, 19 May 2026 14:41:59 +0800
Subject: [PATCH] update firmware v1.0.1
---
CMakeLists.txt | 2 +
main/apps/app_manager/app_manager.cpp | 2 +-
main/apps/app_manager/app_manager.h | 2 +-
main/apps/app_server/app_server.cpp | 5 +-
main/apps/app_server/index.html | 120 +++++++++----
.../local_photo_slideshow.cpp | 98 ++++++++---
.../local_photo_slideshow.h | 2 +
main/hal/hal.cpp | 2 +
main/hal/hal.h | 1 +
main/hal/storage/hal_storage.cpp | 157 +++++++++++++++---
main/hal/utils/image/image_utils.cpp | 33 ++--
11 files changed, 329 insertions(+), 95 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c0c7610..2b8fecc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,6 +2,8 @@
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
+set(PROJECT_VER "1.0.1")
+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
diff --git a/main/apps/app_manager/app_manager.cpp b/main/apps/app_manager/app_manager.cpp
index 52cc41c..3a7aaf5 100644
--- a/main/apps/app_manager/app_manager.cpp
+++ b/main/apps/app_manager/app_manager.cpp
@@ -689,7 +689,7 @@ static void app_task(void* param)
// ---- Startup ----
esp_err_t app_manager_start()
{
- ESP_LOGI(g_tag, "Software version: V%u", APP_SW_VERSION);
+ ESP_LOGI(g_tag, "Software version: V%s", APP_SW_VERSION);
// ── First boot guide image ──
nvs_handle_t h;
diff --git a/main/apps/app_manager/app_manager.h b/main/apps/app_manager/app_manager.h
index ed44ce2..158f18c 100644
--- a/main/apps/app_manager/app_manager.h
+++ b/main/apps/app_manager/app_manager.h
@@ -21,7 +21,7 @@
#define WIFI_AP_AUTO_OFF_TIMEOUT_MIN (10)
/** @brief Current application software version. */
-#define APP_SW_VERSION (0x01)
+#define APP_SW_VERSION "1.0.1"
/**
* @brief Displays the boot guide image.
diff --git a/main/apps/app_server/app_server.cpp b/main/apps/app_server/app_server.cpp
index 5d14349..cf662c5 100644
--- a/main/apps/app_server/app_server.cpp
+++ b/main/apps/app_server/app_server.cpp
@@ -54,7 +54,7 @@ extern const char _binary_index_html_end[] asm("_binary_index_html_end");
#define SCAN_TIMEOUT_MS (8000)
#define CONNECT_TIMEOUT_MS (15000)
-#define UPLOAD_MAX_SIZE (512 * 1024)
+#define UPLOAD_MAX_SIZE (2 * 1024 * 1024)
#define PHOTOS_PER_PAGE (16)
#define MAX_PHOTOS (500)
@@ -138,7 +138,8 @@ static bool url_encode_component(const char *src, char *dst, size_t dst_sz)
for (size_t i = 0; src[i]; i++) {
unsigned char c = (unsigned char)src[i];
- bool safe = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' ||
+
+ bool safe = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' ||
c == '_' || c == '.' || c == '~';
if (safe) {
diff --git a/main/apps/app_server/index.html b/main/apps/app_server/index.html
index 8518961..3001e2e 100644
--- a/main/apps/app_server/index.html
+++ b/main/apps/app_server/index.html
@@ -1592,6 +1592,10 @@ let modeList = [];
let confirmCallback = null;
let photoPage = 1;
let photoTotalPages = 1;
+let photoPageCache = {}; // { [page]: { total_pages, names: [...] } }
+let photoCache = {}; // { [name]: { name, url } }
+let photoTotal = 0;
+let photoPageLoading = false;
// ==================== API Helper ====================
async function apiCall(method, url, body, isFormData) {
@@ -1600,15 +1604,23 @@ async function apiCall(method, url, body, isFormData) {
opts.body = isFormData ? body : JSON.stringify(body);
if (!isFormData) opts.headers = { "Content-Type": "application/json" };
}
+ let res;
try {
- const res = await fetch(url, opts);
- const data = await res.json();
- if (!res.ok) throw new Error(data.message || `HTTP ${res.status}`);
- return data;
+ res = await fetch(url, opts);
} catch (e) {
- if (e.message && !e.message.startsWith("HTTP")) throw e;
+ throw e;
+ }
+
+ let data = null;
+ try {
+ data = await res.json();
+ } catch (e) {
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
throw new Error("Network request failed");
}
+
+ if (!res.ok) throw new Error(data.message || `HTTP ${res.status}`);
+ return data;
}
// ==================== Toast ====================
@@ -2654,11 +2666,6 @@ function applyModeConfig() {
requestAnimationFrame(() => {
rerenderCompositionLayout(false);
});
-
- if (currentMode === "mode_1") {
- photoPage = 1;
- loadPhotoList(photoPage);
- }
}
// ==================== Unified Settings Modal ====================
@@ -3271,10 +3278,17 @@ async function uploadOnly() {
formData.append("action", "upload_only");
formData.append("algorithm", displayMode);
showToast("Uploading...", "");
- await apiCall("POST", "/api/photos/upload", formData, true);
+ const resp = await apiCall("POST", "/api/photos/upload", formData, true);
showToast("Uploaded successfully", "success");
- photoPage = 9999;
- loadPhotoList(photoPage);
+ if (displayMode === "nearest" && resp && resp.name) {
+ // Nearest photo inserts at front — all page boundaries shift, clear page cache
+ photoPageCache = {};
+ const m = resp.name.match(/^imageN0*(\d+)/i);
+ if (m) photoPage = Math.ceil(Number(m[1]) / getPhotoPerPage());
+ } else {
+ photoPage = 9999;
+ }
+ await loadPhotoList(photoPage, /* forceRefresh */ true);
loadStorage();
} catch(e) { showToast(e.message, "error"); }
}
@@ -3300,10 +3314,18 @@ async function uploadWithDisplay() {
formData.append("algorithm", displayMode);
showToast("Uploading...", "");
- await apiCall("POST", "/api/photos/upload", formData, true);
+ const resp = await apiCall("POST", "/api/photos/upload", formData, true);
showToast("Uploaded & display updated", "success");
- photoPage = 9999;
- loadPhotoList(photoPage);
+ // Compute which page the new photo landed on from its name
+ if (displayMode === "nearest" && resp && resp.name) {
+ // Nearest photo inserts at front — all pages shift, clear page cache
+ photoPageCache = {};
+ const m = resp.name.match(/^imageN0*(\d+)/i);
+ if (m) photoPage = Math.ceil(Number(m[1]) / getPhotoPerPage());
+ } else {
+ photoPage = 9999; // Dither appends at end, only last page changes
+ }
+ await loadPhotoList(photoPage, /* forceRefresh */ true);
loadStorage();
} catch(e) { showToast(e.message, "error"); }
}
@@ -3315,20 +3337,43 @@ function getPhotoPerPage() {
return 12;
}
-async function loadPhotoList(page) {
+async function loadPhotoList(page, forceRefresh = false) {
page = page || photoPage;
const perPage = getPhotoPerPage();
+
+ // Serve from cache if this page was loaded before
+ if (!forceRefresh && photoPageCache[page]) {
+ photoPage = page;
+ renderPhotoGridFromNames(photoPageCache[page]);
+ renderPagination();
+ return;
+ }
+
+ photoPageLoading = true;
try {
const data = await apiCall("GET", `/api/photos/list?page=${page}&per_page=${perPage}`);
- photoPage = data.page;
- photoTotalPages = data.total_pages;
- renderPhotoGrid(data.photos, data.total);
- renderPagination();
+ if (data.page === page || page === photoPage) {
+ // Store per-photo data globally
+ const names = [];
+ data.photos.forEach(p => {
+ photoCache[p.name] = { name: p.name, url: p.url };
+ names.push(p.name);
+ });
+ photoPageCache[data.page] = names;
+ photoPage = data.page;
+ photoTotalPages = data.total_pages;
+ photoTotal = data.total;
+ renderPhotoGridFromNames(names);
+ renderPagination();
+ }
} catch(e) { /* silent */ }
+ photoPageLoading = false;
}
function goPhotoPage(page) {
if (page < 1 || page > photoTotalPages) return;
+ photoPage = page;
+ renderPagination();
loadPhotoList(page);
}
@@ -3348,25 +3393,26 @@ function renderPagination() {
next.disabled = photoPage >= photoTotalPages;
}
-function renderPhotoGrid(photos, total) {
+function renderPhotoGridFromNames(names) {
const grid = document.getElementById("photo-grid");
const count = document.getElementById("photo-count");
- count.textContent = total > 0 ? `(${total})` : "";
+ count.textContent = photoTotal > 0 ? `(${photoTotal})` : "";
- if (photos.length === 0) {
+ if (names.length === 0) {
grid.innerHTML = `
No photos
`;
return;
}
- grid.innerHTML = photos.map(p =>
- `
+ grid.innerHTML = names.map(name => {
+ const p = photoCache[name] || { name, url: "" };
+ return `
-
`
- ).join("");
+
`;
+ }).join("");
}
function escapeAttr(s) { return s.replace(/'/g, "\\'").replace(/"/g, """); }
@@ -3385,13 +3431,23 @@ function deletePhoto(name) {
await apiCall("DELETE", "/api/photos/delete?name=" + encodeURIComponent(name));
showToast("Deleted", "success");
loadStorage();
- // 如果当前页删空则回退一页
- if (photoPage > 1) {
+ // Remove only this photo from caches
+ delete photoCache[name];
+ photoTotal = Math.max(0, photoTotal - 1);
+ for (const key of Object.keys(photoPageCache)) {
+ photoPageCache[key] = photoPageCache[key].filter(n => n !== name);
+ }
+ // If the current page is now empty, step back
+ const names = photoPageCache[photoPage];
+ if (names && names.length === 0 && photoPage > 1) {
+ photoPage--;
+ } else if (!names && photoPage > 1) {
+ // Page not cached — check server whether it's now empty
const perPage = getPhotoPerPage();
const data = await apiCall("GET", `/api/photos/list?page=${photoPage}&per_page=${perPage}`);
- if (data.photos.length === 0 && photoPage > 1) { photoPage--; }
+ if (data.photos.length === 0) { photoPage--; }
}
- loadPhotoList(photoPage);
+ loadPhotoList(photoPage, /* forceRefresh */ true);
} catch(e) { showToast(e.message, "error"); }
});
}
diff --git a/main/apps/local_photo_slideshow/local_photo_slideshow.cpp b/main/apps/local_photo_slideshow/local_photo_slideshow.cpp
index 74249a4..adf98d6 100644
--- a/main/apps/local_photo_slideshow/local_photo_slideshow.cpp
+++ b/main/apps/local_photo_slideshow/local_photo_slideshow.cpp
@@ -22,9 +22,77 @@
static const char *TAG = "Slideshow";
-// Define an invalid index to represent “no photo is currently displayed”
+static constexpr uint8_t RX8130_RAM_INDEX_CURRENT_STORAGE = 0;
+
+// Define an invalid index to represent "no photo is currently displayed"
#define NO_PHOTO 0xFFFF
+namespace {
+
+void resetPhotoIndexState(uint16_t &pending_index)
+{
+ pending_index = NO_PHOTO;
+ hal.rx8130RamWrite(RX8130_RAM_INDEX_CURRENT_STORAGE, 0);
+ hal.rx8130RamWrite(RX8130_RAM_INDEX_CURRENT_STORAGE + 1, 0);
+}
+
+esp_err_t selectStorageMedia(bool sd_inserted, bool &sd_fallback_locked)
+{
+ sd_fallback_locked = false;
+
+ if (sd_inserted) {
+ esp_err_t ret = hal_storage_init(APP_STORAGE_MEDIA_SDMMC);
+ if (ret == ESP_OK) {
+ return ESP_OK;
+ }
+ sd_fallback_locked = true;
+ ESP_LOGW(TAG, "SD init failed, fallback to SPI flash: %s", esp_err_to_name(ret));
+ }
+
+ return hal_storage_init(APP_STORAGE_MEDIA_SPIFLASH);
+}
+
+void ensureStorageMedia(bool sd_inserted, bool &last_sd_inserted, bool &sd_fallback_locked, uint16_t &pending_index)
+{
+ if (!sd_inserted) {
+ last_sd_inserted = false;
+ sd_fallback_locked = false;
+ } else if (!last_sd_inserted) {
+ last_sd_inserted = true;
+ sd_fallback_locked = false;
+ }
+
+ hal_storage_media_t target_media =
+ (sd_inserted && !sd_fallback_locked) ? APP_STORAGE_MEDIA_SDMMC : APP_STORAGE_MEDIA_SPIFLASH;
+ if (hal_storage_get_media() == target_media) {
+ return;
+ }
+
+ esp_err_t ret = hal_storage_switch(target_media);
+ if (ret == ESP_OK) {
+ resetPhotoIndexState(pending_index);
+ return;
+ }
+
+ if (!sd_inserted) {
+ ESP_LOGW(TAG, "SPI flash switch failed: %s", esp_err_to_name(ret));
+ return;
+ }
+
+ sd_fallback_locked = true;
+ ESP_LOGW(TAG, "SD switch failed, fallback to SPI flash: %s", esp_err_to_name(ret));
+ if (hal_storage_get_media() != APP_STORAGE_MEDIA_SPIFLASH) {
+ esp_err_t fallback_ret = hal_storage_switch(APP_STORAGE_MEDIA_SPIFLASH);
+ if (fallback_ret != ESP_OK) {
+ ESP_LOGE(TAG, "SPI flash fallback switch failed: %s", esp_err_to_name(fallback_ret));
+ return;
+ }
+ }
+ resetPhotoIndexState(pending_index);
+}
+
+} // namespace
+
/* ---------- millis() ---------- */
static inline uint32_t millis_()
{
@@ -43,19 +111,15 @@ bool PhotoSlideshow::init(const char *dir_path, uint8_t interval_min)
_needs_refresh = false;
_last_btn_c = false;
_last_btn_b = false;
+ _last_sd_inserted = hal.isSDCardInserted();
+ _sd_fallback_locked = false;
_photo_list.clear();
_scr_w = hal.Canvas->width();
_scr_h = hal.Canvas->height();
M5.Speaker.setVolume(120);
- if (hal.isSDCardInserted()) {
- // Use the SD card at startup
- hal_storage_init(APP_STORAGE_MEDIA_SDMMC);
- } else {
- // Use APP storage at startup
- hal_storage_init(APP_STORAGE_MEDIA_SPIFLASH);
- }
+ selectStorageMedia(_last_sd_inserted, _sd_fallback_locked);
scanPhotos();
hal.statusEventSend(OPERATION_EVENT_STARTUP_SUCCESS);
@@ -238,23 +302,7 @@ void PhotoSlideshow::update()
syncSettings();
- // If an SD card is inserted and FLASH storage is currently in use, switch to the SD card
- // If no SD card is inserted and SD storage is currently in use, switch to FLASH storage
- if (hal.isSDCardInserted()) {
- if (hal_storage_get_media() != APP_STORAGE_MEDIA_SDMMC) {
- hal_storage_switch(APP_STORAGE_MEDIA_SDMMC);
- _pending_index = NO_PHOTO;
- hal.rx8130RamWrite(RX8130_RAM_INDEX_CURRENT, 0);
- hal.rx8130RamWrite(RX8130_RAM_INDEX_CURRENT + 1, 0);
- }
- } else {
- if (hal_storage_get_media() != APP_STORAGE_MEDIA_SPIFLASH) {
- hal_storage_switch(APP_STORAGE_MEDIA_SPIFLASH);
- _pending_index = NO_PHOTO;
- hal.rx8130RamWrite(RX8130_RAM_INDEX_CURRENT, 0);
- hal.rx8130RamWrite(RX8130_RAM_INDEX_CURRENT + 1, 0);
- }
- }
+ ensureStorageMedia(hal.isSDCardInserted(), _last_sd_inserted, _sd_fallback_locked, _pending_index);
// Check buttons
handleButtons();
diff --git a/main/apps/local_photo_slideshow/local_photo_slideshow.h b/main/apps/local_photo_slideshow/local_photo_slideshow.h
index 23d11b1..a248ab4 100644
--- a/main/apps/local_photo_slideshow/local_photo_slideshow.h
+++ b/main/apps/local_photo_slideshow/local_photo_slideshow.h
@@ -158,6 +158,8 @@ private:
bool _last_btn_c = false;
bool _last_btn_b = false;
bool _last_btn_a = false;
+ bool _last_sd_inserted = false;
+ bool _sd_fallback_locked = false;
/* ---- Internal helpers ---- */
void syncSettings();
diff --git a/main/hal/hal.cpp b/main/hal/hal.cpp
index 5ae48c8..f8afd70 100644
--- a/main/hal/hal.cpp
+++ b/main/hal/hal.cpp
@@ -178,6 +178,8 @@ void Hal::init()
s_spi_bus_inited = true;
pm1.begin(&M5.In_I2C, M5PM1_DEFAULT_ADDR, M5PM1_I2C_FREQ_100K);
pm1.setI2cConfig(0);
+ pm1.pinMode(SD_DET_EN, OUTPUT);
+ pm1.digitalWrite(SD_DET_EN, HIGH);
pm1.pinMode(SD_DEC, INPUT_PULLUP);
pm1.pinMode(EPD_EN, OUTPUT);
pm1.digitalWrite(EPD_EN, HIGH);
diff --git a/main/hal/hal.h b/main/hal/hal.h
index 18fc44b..5fdcd53 100644
--- a/main/hal/hal.h
+++ b/main/hal/hal.h
@@ -134,6 +134,7 @@ private:
static void LedStatusIndicateTask(void* task_parameters);
static constexpr m5pm1_gpio_num_t SD_DEC = M5PM1_GPIO_NUM_1;
static constexpr m5pm1_gpio_num_t EPD_EN = M5PM1_GPIO_NUM_0;
+ static constexpr m5pm1_gpio_num_t SD_DET_EN = M5PM1_GPIO_NUM_4;
static constexpr gpio_num_t SYS_SCL_PIN = GPIO_NUM_2;
static constexpr gpio_num_t SYS_SDA_PIN = GPIO_NUM_3;
TaskHandle_t _led_status_indicate_task_handle = nullptr;
diff --git a/main/hal/storage/hal_storage.cpp b/main/hal/storage/hal_storage.cpp
index 1b38c1b..cc22311 100644
--- a/main/hal/storage/hal_storage.cpp
+++ b/main/hal/storage/hal_storage.cpp
@@ -158,9 +158,46 @@ static void usb_event_cb(tinyusb_event_t *event, void *arg)
}
}
-static void _mount(void)
+static esp_err_t install_tinyusb_device_driver(void)
{
- ESP_ERROR_CHECK(tinyusb_msc_set_storage_mount_point(s_ctx.storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_APP));
+ if (s_ctx.driver_installed) {
+ return ESP_OK;
+ }
+
+ tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
+ tusb_cfg.descriptor.device = &descriptor_config;
+ tusb_cfg.descriptor.full_speed_config = msc_fs_configuration_desc;
+ tusb_cfg.descriptor.string = string_desc_arr;
+ tusb_cfg.descriptor.string_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]);
+ tusb_cfg.event_cb = usb_event_cb;
+
+ esp_err_t ret = tinyusb_driver_install(&tusb_cfg);
+ if (ret == ESP_OK) {
+ s_ctx.driver_installed = true;
+ }
+ return ret;
+}
+
+static esp_err_t uninstall_tinyusb_device_driver(void)
+{
+ if (!s_ctx.driver_installed) {
+ return ESP_OK;
+ }
+
+ esp_err_t ret = tinyusb_driver_uninstall();
+ if (ret == ESP_OK) {
+ s_ctx.driver_installed = false;
+ }
+ return ret;
+}
+
+static esp_err_t _mount(void)
+{
+ esp_err_t ret = tinyusb_msc_set_storage_mount_point(s_ctx.storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_APP);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to switch storage mount point to APP: %s", esp_err_to_name(ret));
+ return ret;
+ }
ESP_LOGI(TAG, "ls %s:", BASE_PATH);
DIR *directory_handle = opendir(BASE_PATH);
@@ -168,16 +205,20 @@ static void _mount(void)
if (!directory_handle) {
if (errno == ENOENT) {
ESP_LOGE(TAG, "Directory doesn't exist %s", BASE_PATH);
+ ret = ESP_ERR_NOT_FOUND;
} else {
ESP_LOGE(TAG, "Unable to read directory %s", BASE_PATH);
+ ret = ESP_FAIL;
}
- return;
+ tinyusb_msc_set_storage_mount_point(s_ctx.storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_USB);
+ return ret;
}
struct dirent *entry;
while ((entry = readdir(directory_handle)) != NULL) {
printf(" %s\n", entry->d_name);
}
closedir(directory_handle);
+ return ESP_OK;
}
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
@@ -352,26 +393,37 @@ static esp_err_t _bringup(hal_storage_media_t media)
ESP_LOGE(TAG, "set_storage_callback fail: %s", esp_err_to_name(ret));
goto err_after_storage;
}
- _mount();
- {
- tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
- tusb_cfg.descriptor.device = &descriptor_config;
- tusb_cfg.descriptor.full_speed_config = msc_fs_configuration_desc;
- tusb_cfg.descriptor.string = string_desc_arr;
- tusb_cfg.descriptor.string_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]);
- tusb_cfg.event_cb = usb_event_cb;
- ret = tinyusb_driver_install(&tusb_cfg);
- if (ret != ESP_OK) {
- ESP_LOGE(TAG, "tinyusb_driver_install fail: %s", esp_err_to_name(ret));
- goto err_after_storage;
- }
- s_ctx.driver_installed = true;
+ ret = _mount();
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Storage mount to APP failed: %s", esp_err_to_name(ret));
+ goto err_after_storage;
}
+
+ ret = install_tinyusb_device_driver();
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "tinyusb_driver_install fail: %s", esp_err_to_name(ret));
+ goto err_after_storage;
+ }
+
return ESP_OK;
err_after_storage:
+ uninstall_tinyusb_device_driver();
if (s_ctx.storage_hdl) {
- tinyusb_msc_delete_storage(s_ctx.storage_hdl);
+ tinyusb_msc_mount_point_t cur;
+ if (tinyusb_msc_get_storage_mount_point(s_ctx.storage_hdl, &cur) == ESP_OK &&
+ cur == TINYUSB_MSC_STORAGE_MOUNT_APP) {
+ esp_err_t unmount_ret =
+ tinyusb_msc_set_storage_mount_point(s_ctx.storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_USB);
+ if (unmount_ret != ESP_OK) {
+ ESP_LOGW(TAG, "Failed to switch storage mount point to USB during cleanup: %s",
+ esp_err_to_name(unmount_ret));
+ }
+ }
+ esp_err_t delete_ret = tinyusb_msc_delete_storage(s_ctx.storage_hdl);
+ if (delete_ret != ESP_OK) {
+ ESP_LOGW(TAG, "delete_storage during cleanup failed: %s", esp_err_to_name(delete_ret));
+ }
s_ctx.storage_hdl = NULL;
}
s_ctx.storage_created = false;
@@ -409,13 +461,24 @@ esp_err_t hal_storage_switch(hal_storage_media_t media)
esp_err_t ret = ESP_OK;
+ ret = uninstall_tinyusb_device_driver();
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "tinyusb_driver_uninstall fail: %s", esp_err_to_name(ret));
+ hal_storage_unlock();
+ return ret;
+ }
+
if (s_ctx.storage_created && s_ctx.storage_hdl) {
tinyusb_msc_mount_point_t cur;
- if (tinyusb_msc_get_storage_mount_point(s_ctx.storage_hdl, &cur) == ESP_OK) {
- if (cur != TINYUSB_MSC_STORAGE_MOUNT_APP) {
- tinyusb_msc_set_storage_mount_point(s_ctx.storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_APP);
- vTaskDelay(pdMS_TO_TICKS(100));
+ if (tinyusb_msc_get_storage_mount_point(s_ctx.storage_hdl, &cur) == ESP_OK &&
+ cur == TINYUSB_MSC_STORAGE_MOUNT_APP) {
+ ret = tinyusb_msc_set_storage_mount_point(s_ctx.storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_USB);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "switch mount point to USB fail: %s", esp_err_to_name(ret));
+ hal_storage_unlock();
+ return ret;
}
+ vTaskDelay(pdMS_TO_TICKS(100));
}
ret = tinyusb_msc_delete_storage(s_ctx.storage_hdl);
@@ -484,7 +547,55 @@ esp_err_t hal_storage_switch(hal_storage_media_t media)
ESP_LOGW(TAG, "set_storage_callback fail: %s", esp_err_to_name(ret));
}
- _mount();
+ ret = _mount();
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Storage mount to APP failed after switch: %s", esp_err_to_name(ret));
+ if (s_ctx.storage_hdl) {
+ esp_err_t delete_ret = tinyusb_msc_delete_storage(s_ctx.storage_hdl);
+ if (delete_ret != ESP_OK) {
+ ESP_LOGW(TAG, "delete_storage after mount failure failed: %s", esp_err_to_name(delete_ret));
+ }
+ s_ctx.storage_hdl = NULL;
+ }
+ s_ctx.storage_created = false;
+ if (media == APP_STORAGE_MEDIA_SPIFLASH) {
+ storage_deinit_spiflash();
+ } else {
+ storage_deinit_sdmmc();
+ }
+ hal_storage_unlock();
+ return ret;
+ }
+
+ ret = install_tinyusb_device_driver();
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "tinyusb_driver_install fail after switch: %s", esp_err_to_name(ret));
+ if (s_ctx.storage_hdl) {
+ tinyusb_msc_mount_point_t cur;
+ if (tinyusb_msc_get_storage_mount_point(s_ctx.storage_hdl, &cur) == ESP_OK &&
+ cur == TINYUSB_MSC_STORAGE_MOUNT_APP) {
+ esp_err_t unmount_ret =
+ tinyusb_msc_set_storage_mount_point(s_ctx.storage_hdl, TINYUSB_MSC_STORAGE_MOUNT_USB);
+ if (unmount_ret != ESP_OK) {
+ ESP_LOGW(TAG, "Failed to switch storage mount point to USB during install cleanup: %s",
+ esp_err_to_name(unmount_ret));
+ }
+ }
+ esp_err_t delete_ret = tinyusb_msc_delete_storage(s_ctx.storage_hdl);
+ if (delete_ret != ESP_OK) {
+ ESP_LOGW(TAG, "delete_storage after install failure failed: %s", esp_err_to_name(delete_ret));
+ }
+ s_ctx.storage_hdl = NULL;
+ }
+ s_ctx.storage_created = false;
+ if (media == APP_STORAGE_MEDIA_SPIFLASH) {
+ storage_deinit_spiflash();
+ } else {
+ storage_deinit_sdmmc();
+ }
+ hal_storage_unlock();
+ return ret;
+ }
hal_storage_unlock();
return ESP_OK;
}
diff --git a/main/hal/utils/image/image_utils.cpp b/main/hal/utils/image/image_utils.cpp
index 19bff55..b3b5884 100644
--- a/main/hal/utils/image/image_utils.cpp
+++ b/main/hal/utils/image/image_utils.cpp
@@ -7,6 +7,7 @@
#include
#include
#include
+#include
static bool get_bmp_size_mem(const uint8_t *data, size_t len, int *width, int *height)
{
@@ -82,18 +83,28 @@ bool get_image_size_from_file(const char *path, int *width, int *height)
FILE *f = fopen(path, "rb");
if (!f) return false;
- uint8_t buf[2048];
- size_t n = fread(buf, 1, sizeof(buf), f);
+ constexpr size_t BUF_SIZE = 65536;
+ uint8_t *buf = (uint8_t *)malloc(BUF_SIZE);
+ if (!buf) {
+ fclose(f);
+ return false;
+ }
+ size_t n = fread(buf, 1, BUF_SIZE, f);
fclose(f);
- if (n == 0) return false;
-
- if (strcasecmp(dot, ".jpg") == 0 || strcasecmp(dot, ".jpeg") == 0) {
- return get_jpg_size_mem(buf, n, width, height);
- } else if (strcasecmp(dot, ".bmp") == 0) {
- return get_bmp_size_mem(buf, n, width, height);
- } else if (strcasecmp(dot, ".png") == 0) {
- return get_png_size_mem(buf, n, width, height);
+ if (n == 0) {
+ free(buf);
+ return false;
}
- return false;
+
+ bool ok = false;
+ if (strcasecmp(dot, ".jpg") == 0 || strcasecmp(dot, ".jpeg") == 0) {
+ ok = get_jpg_size_mem(buf, n, width, height);
+ } else if (strcasecmp(dot, ".bmp") == 0) {
+ ok = get_bmp_size_mem(buf, n, width, height);
+ } else if (strcasecmp(dot, ".png") == 0) {
+ ok = get_png_size_mem(buf, n, width, height);
+ }
+ free(buf);
+ return ok;
}