🚸 Detect multi-volume insert / remove

This commit is contained in:
Scott Lahteine 2025-04-24 11:06:47 -05:00
parent 38d6d61912
commit f78aaf9562
7 changed files with 168 additions and 66 deletions

View file

@ -1349,8 +1349,11 @@ void setup() {
#endif
#endif
#if HAS_MEDIA && ANY(SDCARD_EEPROM_EMULATION, POWER_LOSS_RECOVERY)
SETUP_RUN(card.mount()); // Mount media with settings before first_load
#if HAS_MEDIA
SETUP_RUN(card.init()); // Prepare for media usage
#if ANY(SDCARD_EEPROM_EMULATION, POWER_LOSS_RECOVERY)
SETUP_RUN(card.mount()); // Mount media with settings before first_load
#endif
#endif
// Prepare some LCDs to display early

View file

@ -740,7 +740,7 @@ void ChironTFT::panelAction(uint8_t req) {
break;
case 26: // A26 Refresh SD
if (card.isMounted())card.release();
card.release();
card.mount();
safe_delay(500);
filenavigator.reset();

View file

@ -131,32 +131,16 @@ void tft_lvgl_init() {
// Init TFT first!
SPI_TFT.spiInit(SPI_FULL_SPEED);
SPI_TFT.lcdInit();
hal.watchdog_refresh(); // LVGL init takes time
#if HAS_USB_FLASH_DRIVE
#if HAS_MULTI_VOLUME && !HAS_SD_HOST_DRIVE
if (card.isSDCardInserted())
card.selectMediaSDCard();
else
card.selectMediaFlashDrive();
#endif
// Wait up to two seconds for USB Drive to mount
for (uint16_t usb_flash_loop = 500; --usb_flash_loop;) {
hal.watchdog_refresh();
card.media_driver_usbFlash.idle();
delay(4);
if (card.media_driver_usbFlash.isInserted()) break;
}
card.mount();
#elif HAS_LOGO_IN_FLASH
#if HAS_LOGO_IN_FLASH
// Leave the boot screen visible for a moment
delay(1000);
hal.watchdog_refresh();
hal.watchdog_refresh(); // LVGL init takes time
delay(1000);
hal.watchdog_refresh(); // LVGL init takes time
#endif
hal.watchdog_refresh(); // LVGL init takes time
#if HAS_MEDIA
UpdateAssets();
hal.watchdog_refresh(); // LVGL init takes time

View file

@ -1891,40 +1891,60 @@ uint8_t expand_u8str_P(char * const outstr, PGM_P const ptpl, const int8_t ind,
#include "extui/ui_api.h"
#endif
void MarlinUI::media_changed(const uint8_t old_status, const uint8_t status) {
void MarlinUI::media_changed(const MediaPresence old_status, const MediaPresence status) {
TERN_(HAS_DISPLAY_SLEEP, refresh_screen_timeout());
if (old_status == status) {
TERN_(EXTENSIBLE_UI, ExtUI::onMediaError()); // Failed to mount/unmount
return;
}
if (old_status < 2) { // Skip this section on first boot check
if (status) { // Media Mounted
if (old_status > MEDIA_BOOT) { // Skip this section on first boot check
if (status > old_status) { // Media Mounted
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMediaMounted();
#elif ENABLED(BROWSE_MEDIA_ON_INSERT)
clear_menu_history();
quick_feedback();
goto_screen(MEDIA_MENU_GATEWAY);
#else
if (card.isSDCardSelected())
LCD_MESSAGE(MSG_MEDIA_INSERTED_SD);
else if (card.isFlashDriveSelected())
LCD_MESSAGE(MSG_MEDIA_INSERTED_USB);
else
LCD_MESSAGE(MSG_MEDIA_INSERTED);
#endif
}
else { // Media Removed
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMediaRemoved();
#elif HAS_SD_DETECT // Q: Does "Media Removed" need to be shown for manual release too?
LCD_MESSAGE(MSG_MEDIA_REMOVED);
#if HAS_MARLINUI_MENU
if (ENABLED(HAS_WIRED_LCD) || !defer_return_to_status) return_to_status();
#endif
#elif HAS_SD_DETECT || HAS_USB_FLASH_DRIVE // Q: Does "Media Removed" need to be shown for manual release too?
if ((old_status ^ status) & INSERT_SD)
LCD_MESSAGE(MSG_MEDIA_REMOVED_SD);
else if ((old_status ^ status) & INSERT_USB)
LCD_MESSAGE(MSG_MEDIA_REMOVED_USB);
else
LCD_MESSAGE(MSG_MEDIA_REMOVED);
if (ENABLED(HAS_WIRED_LCD) || !defer_return_to_status)
return_to_status();
#elif HAS_WIRED_LCD
return_to_status();
#endif
}
}

View file

@ -256,7 +256,7 @@ public:
#if HAS_MEDIA
#define MEDIA_MENU_GATEWAY TERN(PASSWORD_ON_SD_PRINT_MENU, password.media_gatekeeper, menu_media)
static void media_changed(const uint8_t old_stat, const uint8_t stat);
static void media_changed(const MediaPresence old_stat, const MediaPresence stat);
#endif
#if HAS_LCD_BRIGHTNESS

View file

@ -145,6 +145,7 @@ int16_t CardReader::nrItems = -1;
#endif
DiskIODriver* CardReader::driver = nullptr;
MarlinVolume CardReader::volume;
MediaFile CardReader::myfile;
@ -483,11 +484,15 @@ void CardReader::mount() {
nrItems = -1;
if (root.isOpen()) root.close();
if (!driver->init(SD_SPI_SPEED, SD_SS_PIN)
const bool driver_init = (
driver->init(SD_SPI_SPEED, SD_SS_PIN)
#if PIN_EXISTS(LCD_SDSS) && (LCD_SDSS_PIN != SD_SS_PIN)
&& !driver->init(SD_SPI_SPEED, LCD_SDSS_PIN)
|| driver->init(SD_SPI_SPEED, LCD_SDSS_PIN)
#endif
) SERIAL_ECHO_MSG(STR_SD_INIT_FAIL);
);
if (!driver_init)
SERIAL_ECHO_MSG(STR_SD_INIT_FAIL);
else if (!volume.init(driver))
SERIAL_WARN_MSG(STR_SD_VOL_INIT_FAIL);
else if (!root.openRoot(&volume))
@ -519,69 +524,146 @@ void CardReader::mount() {
#include "../module/stepper.h"
#endif
// Provide a little time for drives to prepare
void CardReader::init() {
#if HAS_USB_FLASH_DRIVE
for (uint8_t i = 10; --i;) {
media_driver_usbFlash.idle();
hal.watchdog_refresh();
if (media_driver_usbFlash.isInserted()) break;
delay(20);
}
#endif
}
/**
* Handle SD card events
* Handle media insertion and removal events
* based on SD Card detect and/or driver.isInserted()
*
* MULTI_VOLUME:
* - Track insert/remove for both media drives.
* - If the MOUNTED media is removed call release().
* - If media is INSERTED when NO MEDIA is mounted, select and mount it.
*/
void CardReader::manage_media() {
#if HAS_USB_FLASH_DRIVE // Wrap for optimal non-virtual?
driver->idle(); // Handle device tasks (e.g., USB Drive insert / remove)
/**
* Handle device tasks (e.g., USB Drive insert / remove)
* - USB Flash Drive needs to run even when not selected.
* - SD Card currently has no background tasks.
*/
//driver->idle();
#if HAS_USB_FLASH_DRIVE
//if (!isFlashDriveSelected())
media_driver_usbFlash.idle();
#endif
static uint8_t prev_stat = 2; // At boot we don't know if media is present or not
uint8_t stat = uint8_t(isInserted());
// Prevent re-entry during Marlin::idle
#if HAS_MULTI_VOLUME
static bool no_reenter = false;
if (no_reenter) return;
#endif
static MediaPresence prev_stat = MEDIA_BOOT; // At boot we don't know if media is present or not
// Live status is based on available media flags
MediaPresence stat = MediaPresence(
#if HAS_MULTI_VOLUME
(isSDCardInserted() ? INSERT_SD : 0) // Without SD Detect it's always "inserted"
| (isFlashDriveInserted() ? INSERT_USB : 0)
#else
isInserted() ? INSERT_MEDIA : 0 // Without SD Detect it's always "inserted"
#endif
);
if (stat == prev_stat) return; // Already checked and still no change?
DEBUG_SECTION(cmm, "CardReader::manage_media()", true);
DEBUG_ECHOLNPGM("Media present: ", prev_stat, " -> ", stat);
if (!ui.detected()) {
DEBUG_ECHOLNPGM("SD: No UI Detected.");
return;
}
// Without a UI there's no auto-mount or release
if (!ui.detected()) { DEBUG_ECHOLNPGM("SD: No UI Detected."); return; }
flag.workDirIsRoot = true; // Return to root on mount/release/init
const uint8_t old_stat = prev_stat;
const MediaPresence old_stat = prev_stat,
old_real = old_stat == MEDIA_BOOT ? INSERT_NONE : old_stat;
prev_stat = stat; // Change now to prevent re-entry in safe_delay
if (stat) { // Media Inserted
safe_delay(500); // Some boards need a delay to get settled
#if HAS_MULTI_VOLUME
const int8_t vdiff = (old_real ^ stat), vadd = vdiff & stat;
#endif
const bool did_insert = TERN(HAS_MULTI_VOLUME, vadd, stat) != INSERT_NONE;
// Try to mount the media (only later with SD_IGNORE_AT_STARTUP)
if (TERN1(SD_IGNORE_AT_STARTUP, old_stat != 2)) mount();
if (!isMounted()) stat = 0; // Not mounted?
if (did_insert) { // Media Inserted
TERN_(HAS_MULTI_VOLUME, ui.refresh()); // Refresh for insert events without messages
// Some media is already mounted? Nothing to do.
if (TERN0(HAS_MULTI_VOLUME, isMounted())) return;
// Prevent re-entry during the following phases
TERN_(HAS_MULTI_VOLUME, no_reenter = true);
// Try to mount the media (but not at boot if SD_IGNORE_AT_STARTUP)
if (TERN1(SD_IGNORE_AT_STARTUP, old_stat > MEDIA_BOOT)) {
#if HAS_MULTI_VOLUME
if ((vadd & INSERT_SD) && !isSDCardSelected())
selectMediaSDCard();
if ((vadd & INSERT_USB) && !isFlashDriveSelected())
selectMediaFlashDrive();
#endif
safe_delay(500); // Time for inserted media to settle. May re-enter for multiple media?
mount();
}
// If the selected media isn't mounted throw an alert in ui.media_changed
if (!isMounted()) stat = old_real;
TERN_(RESET_STEPPERS_ON_MEDIA_INSERT, reset_stepper_drivers()); // Workaround for Cheetah bug
// Re-enable media detection logic
TERN_(HAS_MULTI_VOLUME, no_reenter = false);
}
else if (
// Media was removed from the device slot
#if HAS_MULTI_VOLUME
(isSDCardSelected() && (vdiff & INSERT_SD))
|| (isFlashDriveSelected() && (vdiff & INSERT_USB))
#else
stat // == INSERT_MEDIA
#endif
) {
flag.workDirIsRoot = true; // Return to root on release
release();
//TERN_(HAS_MULTI_VOLUME, prev_stat = INSERT_NONE); // HACK to try mounting any remaining media
}
else {
TERN_(HAS_SD_DETECT, release()); // Card is released
#if HAS_MULTI_VOLUME
stat = old_real; // Ignore un-mounted media being ejected
ui.refresh(); // Refresh for menus that show inserted unmounted media
#endif
}
ui.media_changed(old_stat, stat); // Update the UI or flag an error
ui.media_changed(old_stat, stat); // Update the UI or flag an error
if (!stat) return; // Exit if no media is present
bool do_auto = true; UNUSED(do_auto);
if (stat == INSERT_NONE) return; // Exit if no media is present
// First mount on boot? Load emulated EEPROM and look for PLR file.
if (old_stat == 2) {
if (old_stat <= MEDIA_BOOT) {
DEBUG_ECHOLNPGM("First mount.");
// Load settings the first time media is inserted (not just during init)
TERN_(SDCARD_EEPROM_EMULATION, settings.first_load());
// Check for PLR file. Skip One-Click and auto#.g if found
TERN_(POWER_LOSS_RECOVERY, if (recovery.check()) do_auto = false);
// Check for PLR file. If found skip other procedures!
if (TERN0(POWER_LOSS_RECOVERY, recovery.check())) return;
}
// Find the newest file and prompt to print it.
TERN_(ONE_CLICK_PRINT, if (do_auto && one_click_check()) do_auto = false);
// Find the newest file and prompt to print it. Skip other procedures!
if (TERN0(ONE_CLICK_PRINT, one_click_check())) return;
// Also for the first mount run auto#.g for machine init.
// (Skip if PLR or One-Click Print was invoked.)
if (old_stat == 2) {
// On first mount at boot run auto#.g for machine init.
if (old_stat <= MEDIA_BOOT) {
// Look for auto0.g on the next idle()
IF_DISABLED(NO_SD_AUTOSTART, if (do_auto) autofile_begin());
IF_DISABLED(NO_SD_AUTOSTART, autofile_begin());
}
}
@ -590,6 +672,8 @@ void CardReader::manage_media() {
* Used by M22, "Release Media", manage_media.
*/
void CardReader::release() {
if (!flag.mounted) return;
// Card removed while printing? Abort!
if (isStillPrinting())
abortFilePrintSoon();

View file

@ -78,6 +78,14 @@ typedef struct {
;
} card_flags_t;
enum MediaPresence : int8_t {
MEDIA_BOOT = -1,
INSERT_NONE = 0x00,
INSERT_MEDIA = 0x01,
INSERT_SD = TERN(HAS_MULTI_VOLUME, 0x02, 0x00),
INSERT_USB = TERN(HAS_MULTI_VOLUME, 0x04, 0x00)
};
enum ListingFlags : uint8_t { LS_LONG_FILENAME, LS_ONLY_BIN, LS_TIMESTAMP };
enum SortFlag : int8_t { AS_REV = -1, AS_OFF, AS_FWD, AS_ALSO_REV };
@ -102,6 +110,9 @@ public:
CardReader();
// Init at startup before mounting media
static void init();
/**
* Media Selection - Only one drive may be active at a time,
* so switching drives (currently) returns to the root folder.