Skip to content

Commit 2e24637

Browse files
ellenspthinkyhead
andauthored
"One Click" Print newest file (MarlinFirmware#25781)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
1 parent d17d86d commit 2e24637

File tree

11 files changed

+174
-18
lines changed

11 files changed

+174
-18
lines changed

Marlin/Configuration_adv.h

+1
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,7 @@
16751675
//#define NO_SD_AUTOSTART // Remove auto#.g file support completely to save some Flash, SRAM
16761676
//#define MENU_ADDAUTOSTART // Add a menu option to run auto#.g files
16771677

1678+
//#define ONE_CLICK_PRINT // Prompt to print the newest file on inserted media
16781679
//#define BROWSE_MEDIA_ON_INSERT // Open the file browser when media is inserted
16791680

16801681
//#define MEDIA_MENU_AT_TOP // Force the media menu to be listed on the top of the main menu

Marlin/src/inc/SanityCheck.h

+13
Original file line numberDiff line numberDiff line change
@@ -4044,6 +4044,19 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
40444044
// Multi-Stepping Limit
40454045
static_assert(WITHIN(MULTISTEPPING_LIMIT, 1, 128) && IS_POWER_OF_2(MULTISTEPPING_LIMIT), "MULTISTEPPING_LIMIT must be 1, 2, 4, 8, 16, 32, 64, or 128.");
40464046

4047+
// One Click Print
4048+
#if ENABLED(ONE_CLICK_PRINT)
4049+
#if !HAS_MEDIA
4050+
#error "SD Card or Flash Drive is required for ONE_CLICK_PRINT."
4051+
#elif ENABLED(BROWSE_MEDIA_ON_INSERT)
4052+
#error "ONE_CLICK_PRINT is incompatible with BROWSE_MEDIA_ON_INSERT."
4053+
#elif DISABLED(NO_SD_AUTOSTART)
4054+
#error "NO_SD_AUTOSTART must be enabled for ONE_CLICK_PRINT."
4055+
#elif !defined(HAS_MARLINUI_MENU)
4056+
#error "ONE_CLICK_PRINT needs a display that has Marlin UI menus."
4057+
#endif
4058+
#endif
4059+
40474060
// Misc. Cleanup
40484061
#undef _TEST_PWM
40494062
#undef _NUM_AXES_STR

Marlin/src/lcd/marlinui.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ void MarlinUI::init() {
424424

425425
#if !HAS_GRAPHICAL_TFT
426426

427-
void _wrap_string(uint8_t &col, uint8_t &row, const char * const string, read_byte_cb_t cb_read_byte, bool wordwrap/*=false*/) {
427+
void _wrap_string(uint8_t &col, uint8_t &row, const char * const string, read_byte_cb_t cb_read_byte, const bool wordwrap/*=false*/) {
428428
SETCURSOR(col, row);
429429
if (!string) return;
430430

Marlin/src/lcd/menu/menu.h

+4
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ void _lcd_draw_homing();
259259
void touch_screen_calibration();
260260
#endif
261261

262+
#if ENABLED(ONE_CLICK_PRINT)
263+
void one_click_print();
264+
#endif
265+
262266
extern uint8_t screen_history_depth;
263267
inline void clear_menu_history() { screen_history_depth = 0; }
264268

Marlin/src/lcd/menu/menu_media.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,11 @@ class MenuItem_sdfile : public MenuItem_sdbase {
7373
#endif
7474
#if ENABLED(SD_MENU_CONFIRM_START)
7575
MenuItem_submenu::action(fstr, []{
76-
char * const longest = card.longest_filename();
77-
char buffer[strlen(longest) + 2];
78-
buffer[0] = ' ';
79-
strcpy(buffer + 1, longest);
76+
char * const filename = card.longest_filename();
8077
MenuItem_confirm::select_screen(
8178
GET_TEXT_F(MSG_BUTTON_PRINT), GET_TEXT_F(MSG_BUTTON_CANCEL),
8279
sdcard_start_selected_file, nullptr,
83-
GET_TEXT_F(MSG_START_PRINT), buffer, F("?")
80+
GET_TEXT_F(MSG_START_PRINT), filename, F("?")
8481
);
8582
});
8683
#else
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Marlin 3D Printer Firmware
3+
* Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4+
*
5+
* Based on Sprinter and grbl.
6+
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU General Public License
19+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
#include "../../inc/MarlinConfigPre.h"
24+
25+
#if ENABLED(ONE_CLICK_PRINT)
26+
27+
#include "menu.h"
28+
29+
void one_click_print() {
30+
ui.goto_screen([]{
31+
char * const filename = card.longest_filename();
32+
MenuItem_confirm::select_screen(
33+
GET_TEXT_F(MSG_BUTTON_PRINT), GET_TEXT_F(MSG_BUTTON_CANCEL),
34+
[]{
35+
card.openAndPrintFile(card.filename);
36+
ui.return_to_status();
37+
ui.reset_status();
38+
}, nullptr,
39+
GET_TEXT_F(MSG_START_PRINT), filename, F("?")
40+
);
41+
});
42+
}
43+
44+
#endif // ONE_CLICK_PRINT

Marlin/src/sd/cardreader.cpp

+97-10
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@
5656
#include "../feature/pause.h"
5757
#endif
5858

59+
#if ENABLED(ONE_CLICK_PRINT)
60+
#include "../../src/lcd/menu/menu.h"
61+
#endif
62+
5963
#define DEBUG_OUT EITHER(DEBUG_CARDREADER, MARLIN_DEV_MODE)
6064
#include "../core/debug_out.h"
6165
#include "../libs/hex_print.h"
@@ -290,7 +294,7 @@ void CardReader::printListing(MediaFile parent, const char * const prepend, cons
290294
while (parent.readDir(&p, longFilename) > 0) {
291295
if (DIR_IS_SUBDIR(&p)) {
292296

293-
size_t lenPrepend = prepend ? strlen(prepend) + 1 : 0;
297+
const size_t lenPrepend = prepend ? strlen(prepend) + 1 : 0;
294298
// Allocate enough stack space for the full path including / separator
295299
char path[lenPrepend + FILENAME_LENGTH];
296300
if (prepend) { strcpy(path, prepend); path[lenPrepend - 1] = '/'; }
@@ -545,20 +549,28 @@ void CardReader::manage_media() {
545549

546550
if (!stat) return; // Exit if no media is present
547551

548-
if (old_stat != 2) return; // First mount?
552+
bool do_auto = true; UNUSED(do_auto);
549553

550-
DEBUG_ECHOLNPGM("First mount.");
554+
// First mount on boot? Load emulated EEPROM and look for PLR file.
555+
if (old_stat == 2) {
556+
DEBUG_ECHOLNPGM("First mount.");
551557

552-
// Load settings the first time media is inserted (not just during init)
553-
TERN_(SDCARD_EEPROM_EMULATION, settings.first_load());
558+
// Load settings the first time media is inserted (not just during init)
559+
TERN_(SDCARD_EEPROM_EMULATION, settings.first_load());
554560

555-
bool do_auto = true; UNUSED(do_auto);
561+
// Check for PLR file. Skip One-Click and auto#.g if found
562+
TERN_(POWER_LOSS_RECOVERY, if (recovery.check()) do_auto = false);
563+
}
556564

557-
// Check for PLR file.
558-
TERN_(POWER_LOSS_RECOVERY, if (recovery.check()) do_auto = false);
565+
// Find the newest file and prompt to print it.
566+
TERN_(ONE_CLICK_PRINT, if (do_auto && one_click_check()) do_auto = false);
559567

560-
// Look for auto0.g on the next idle()
561-
IF_DISABLED(NO_SD_AUTOSTART, if (do_auto) autofile_begin());
568+
// Also for the first mount run auto#.g for machine init.
569+
// (Skip if PLR or One-Click Print was invoked.)
570+
if (old_stat == 2) {
571+
// Look for auto0.g on the next idle()
572+
IF_DISABLED(NO_SD_AUTOSTART, if (do_auto) autofile_begin());
573+
}
562574
}
563575

564576
/**
@@ -887,6 +899,81 @@ void CardReader::write_command(char * const buf) {
887899
}
888900
#endif
889901

902+
#if ENABLED(ONE_CLICK_PRINT)
903+
904+
/**
905+
* Select the newest file and ask the user if they want to print it.
906+
*/
907+
bool CardReader::one_click_check() {
908+
const bool found = selectNewestFile();
909+
if (found) {
910+
//SERIAL_ECHO_MSG(" OCP File: ", longest_filename(), "\n");
911+
//ui.init();
912+
one_click_print();
913+
}
914+
return found;
915+
}
916+
917+
/**
918+
* Recurse the entire directory to find the newest file.
919+
* This may take a very long time so watch out for watchdog reset.
920+
* It may be best to only look at root for reasonable boot and mount times.
921+
*/
922+
void CardReader::diveToNewestFile(MediaFile parent, uint32_t &compareDateTime, MediaFile &outdir, char * const outname) {
923+
// Iterate the given parent dir
924+
parent.rewind();
925+
for (dir_t p; parent.readDir(&p, longFilename) > 0;) {
926+
927+
// If the item is a dir, recurse into it
928+
if (DIR_IS_SUBDIR(&p)) {
929+
// Get the name of the dir for opening
930+
char dirname[FILENAME_LENGTH];
931+
createFilename(dirname, p);
932+
933+
// Open the item in a new MediaFile
934+
MediaFile child; // child.close() in destructor
935+
if (child.open(&parent, dirname, O_READ))
936+
diveToNewestFile(child, compareDateTime, outdir, outname);
937+
}
938+
else if (is_visible_entity(p)) {
939+
// Get the newer of the modified/created date and time
940+
const uint32_t modDateTime = uint32_t(p.lastWriteDate) << 16 | p.lastWriteTime,
941+
createDateTime = uint32_t(p.creationDate) << 16 | p.creationTime,
942+
newerDateTime = _MAX(modDateTime, createDateTime);
943+
// If a newer item is found overwrite the outdir and outname
944+
if (newerDateTime > compareDateTime) {
945+
compareDateTime = newerDateTime;
946+
outdir = parent;
947+
createFilename(outname, p);
948+
}
949+
}
950+
}
951+
}
952+
953+
/**
954+
* Recurse the entire directory to find the newest file.
955+
* Make the found file the current selection.
956+
*/
957+
bool CardReader::selectNewestFile() {
958+
uint32_t dateTimeStorage = 0;
959+
MediaFile foundDir;
960+
char foundName[FILENAME_LENGTH];
961+
foundName[0] = '\0';
962+
963+
diveToNewestFile(root, dateTimeStorage, foundDir, foundName);
964+
965+
if (foundName[0]) {
966+
workDir = foundDir;
967+
workDir.rewind();
968+
selectByName(workDir, foundName);
969+
//workDir.close(); // Not needed?
970+
return true;
971+
}
972+
return false;
973+
}
974+
975+
#endif // ONE_CLICK_PRINT
976+
890977
void CardReader::closefile(const bool store_location/*=false*/) {
891978
file.sync();
892979
file.close();

Marlin/src/sd/cardreader.h

+6
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ class CardReader {
128128
static void autofile_cancel() { autofile_index = 0; }
129129
#endif
130130

131+
#if ENABLED(ONE_CLICK_PRINT)
132+
static bool one_click_check(); // Check for the newest file and prompt to run it.
133+
static void diveToNewestFile(MediaFile parent, uint32_t &compareDateTime, MediaFile &outdir, char * const outname);
134+
static bool selectNewestFile();
135+
#endif
136+
131137
// Basic file ops
132138
static void openFileRead(const char * const path, const uint8_t subcall=0);
133139
static void openFileWrite(const char * const path);

buildroot/share/PlatformIO/scripts/common-dependencies.h

+3
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@
104104
#if ENABLED(AUTO_BED_LEVELING_UBL)
105105
#define HAS_MENU_UBL
106106
#endif
107+
#if ENABLED(ONE_CLICK_PRINT)
108+
#define HAS_MENU_ONE_CLICK_PRINT
109+
#endif
107110
#endif
108111

109112
#if HAS_GRAPHICAL_TFT

buildroot/tests/mks_tinybee

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ opt_set MOTHERBOARD BOARD_MKS_TINYBEE \
2525
LCD_INFO_SCREEN_STYLE 0 \
2626
DISPLAY_CHARSET_HD44780 WESTERN \
2727
NEOPIXEL_TYPE NEO_RGB
28-
opt_enable FYSETC_MINI_12864_2_1 SDSUPPORT
29-
opt_enable LED_CONTROL_MENU LED_USER_PRESET_STARTUP LED_COLOR_PRESETS NEOPIXEL_LED
28+
opt_enable FYSETC_MINI_12864_2_1 SDSUPPORT ONE_CLICK_PRINT NO_SD_AUTOSTART \
29+
NEOPIXEL_LED LED_CONTROL_MENU LED_USER_PRESET_STARTUP LED_COLOR_PRESETS
3030
exec_test $1 $2 "MKS TinyBee with NeoPixel LCD, SD and Speaker" "$3"
3131

3232
# cleanup

ini/features.ini

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ HAS_MENU_LED = build_src_filter=+<src/lcd/menu/menu_le
8181
HAS_MENU_MEDIA = build_src_filter=+<src/lcd/menu/menu_media.cpp>
8282
HAS_MENU_MIXER = build_src_filter=+<src/lcd/menu/menu_mixer.cpp>
8383
HAS_MENU_MMU2 = build_src_filter=+<src/lcd/menu/menu_mmu2.cpp>
84+
HAS_MENU_ONE_CLICK_PRINT = build_src_filter=+<src/lcd/menu/menu_one_click_print.cpp>
8485
HAS_MENU_PASSWORD = build_src_filter=+<src/lcd/menu/menu_password.cpp>
8586
HAS_MENU_POWER_MONITOR = build_src_filter=+<src/lcd/menu/menu_power_monitor.cpp>
8687
HAS_MENU_CUTTER = build_src_filter=+<src/lcd/menu/menu_spindle_laser.cpp>

0 commit comments

Comments
 (0)