.. _lvgl_filemanager: ================================= 2.14 SD Card File Manager ================================= Browse, navigate, and manage files on the SD card through a graphical file explorer. Overview ======== This example creates a complete file manager for the SD card. You can browse folders, view file details (name, size), delete files, and navigate through the directory tree — all from the touchscreen, no computer needed. What You'll Learn ================= * Building a file system explorer with the LVGL list widget * Implementing directory navigation with a breadcrumb path display * Displaying file metadata (name, size, path) * Deleting files from the SD card through the UI Before You Begin ================ Complete the setup steps in :ref:`preparation` before continuing. .. note:: Use the recommended Arduino IDE settings in :ref:`arduino_board_settings` unless this lesson says otherwise. Open the example sketch from the code package: ``LAFVIN-ESP32-S3-Multimedia-Kit-main/2.LVGL/14_LVGL_File_Manager/14_LVGL_File_Manager.ino`` .. note:: You need a Micro SD card formatted as **FAT32** inserted. The SD card from :ref:`arduino_sdmmc` with test files will work perfectly. Upload and Test =============== #. Insert a FAT32-formatted Micro SD card. #. Connect the board with a USB Type-C data cable. #. Open ``14_LVGL_File_Manager.ino`` in Arduino IDE. #. In the **Tools** menu, select ``ESP32S3 Dev Module`` as the board and choose the correct serial port. #. Click **Upload**. #. After uploading, the screen shows: * A path breadcrumb at the top (e.g., ``Path: /music/``) * A scrollable list of files and folders with icons and file sizes * A status bar at the bottom showing item count and storage usage Tapping a folder navigates into it. Tapping a file opens a dialog with its name, size, path, and type — plus a **Delete** button to remove it. Tap the exit button to navigate up to the parent directory. .. image:: img/lvgl14.jpg :alt: LVGL File Manager :align: center How the Code Works ================== The file manager UI logic is mainly implemented in ``File_Manager_ui.cpp``. **SD Card Initialisation** The SD card is configured with the standard ESP32-S3 SDMMC pinout (CLK=39, CMD=38, D0=40). The ``init_sd_card()`` function sets these pins, mounts the filesystem, and returns early if the card is already ready. .. code-block:: cpp bool init_sd_card(void) { if (s_sd_ready) { return true; } Serial.println("Initializing SD card for file manager..."); SD_MMC.setPins(kSdClkPin, kSdCmdPin, kSdD0Pin); s_sd_ready = SD_MMC.begin("/sdcard", true, true, SDMMC_FREQ_DEFAULT, 5); if (!s_sd_ready) { Serial.println("SD card mount failed."); } else { Serial.println("SD card ready."); } return s_sd_ready; } **Directory Navigation** The ``navigate_to()`` function normalises the target path, opens it as a directory, and refreshes the file list. It returns ``false`` if the path is null, empty, or not a valid directory. .. code-block:: cpp bool navigate_to(const char *path) { if (path == nullptr || path[0] == '\0') { return false; } const String normalized = normalize_path(path, true); File dir = SD_MMC.open(normalized.c_str()); if (!dir || !dir.isDirectory()) { if (dir) { dir.close(); } return false; } copy_string(s_current_path, sizeof(s_current_path), normalized); dir.close(); refresh_file_list(); return true; } The ``navigate_to_parent()`` function strips the last path segment and calls ``navigate_to()``, or returns to root if already at the top level. **File Listing** The ``refresh_file_list()`` function clears the list widget, adds a ``..`` parent entry when not at root, then iterates over the directory entries. Each entry is stored in a ``FileEntry`` struct array (up to 48 entries) and rendered as a list button with the appropriate icon and file size. .. code-block:: cpp s_entry_count = 0; if (strcmp(s_current_path, "/") != 0) { lv_obj_t *parent_btn = lv_list_add_btn(g_file_manager_ui.list, LV_SYMBOL_LEFT, ".."); lv_obj_add_event_cb( parent_btn, file_item_event_handler, LV_EVENT_CLICKED, reinterpret_cast(static_cast(kParentEntryIndex))); } File file = dir.openNextFile(); while (file && s_entry_count < kMaxEntries) { const String raw_name = String(file.name()); const String entry_name = basename_from_path(normalize_path(raw_name, false)); String full_path; if (strcmp(s_current_path, "/") == 0) { full_path = normalize_path("/" + entry_name, file.isDirectory()); } else { full_path = normalize_path(String(s_current_path) + "/" + entry_name, file.isDirectory()); } copy_string(s_entries[s_entry_count].name, sizeof(s_entries[s_entry_count].name), entry_name); copy_string(s_entries[s_entry_count].path, sizeof(s_entries[s_entry_count].path), full_path); s_entries[s_entry_count].is_dir = file.isDirectory(); s_entries[s_entry_count].size_bytes = static_cast(file.size()); const char *symbol = s_entries[s_entry_count].is_dir ? LV_SYMBOL_DIRECTORY : LV_SYMBOL_FILE; lv_obj_t *btn = lv_list_add_btn(g_file_manager_ui.list, symbol, s_entries[s_entry_count].name); lv_obj_add_event_cb( btn, file_item_event_handler, LV_EVENT_CLICKED, reinterpret_cast(static_cast(s_entry_count))); if (!s_entries[s_entry_count].is_dir) { lv_obj_t *btn_label = lv_obj_get_child(btn, 1); if (btn_label != nullptr) { char size_text[24]; format_size(s_entries[s_entry_count].size_bytes, size_text, sizeof(size_text)); lv_label_set_text_fmt(btn_label, "%s (%s)", s_entries[s_entry_count].name, size_text); } } ++s_entry_count; file.close(); file = dir.openNextFile(); } The bottom status bar is then updated with the item count and storage usage (``Items: N | Storage: used / total MB``). **File Deletion** The ``delete_event_handler()`` is invoked when the user taps the **Delete** button in the file dialog. It removes the selected file from the SD card and refreshes the list. .. code-block:: cpp void delete_event_handler(lv_event_t *e) { if (lv_event_get_code(e) != LV_EVENT_CLICKED) { return; } if (s_selected_file_path[0] == '\0') { hide_dialog(); return; } if (SD_MMC.remove(s_selected_file_path)) { char status_buffer[96]; snprintf(status_buffer, sizeof(status_buffer), "Deleted: %s", s_selected_file_name); set_status_text(status_buffer); } else { char status_buffer[96]; snprintf(status_buffer, sizeof(status_buffer), "Delete failed: %s", s_selected_file_name); set_status_text(status_buffer); } hide_dialog(); refresh_file_list(); } In the provided sketch: * ``file_item_event_handler()`` dispatches list-item taps: folders call ``navigate_to()``, files call ``show_file_dialog()``, and ``..`` calls ``navigate_to_parent()`` * ``show_file_dialog()`` displays file metadata (type, size, path) in a popup and disables the **Delete** button for directories * The exit button in the header calls ``navigate_to_parent()``, moving up one directory level * File sizes are formatted by ``format_size()``, which chooses B, KB, or MB units automatically Try This Next ============= * Add a "New Folder" button to create directories from the touchscreen * Display file modification dates alongside sizes * Add drag-to-scroll for long file lists