Skip to content

Commit 94086c1

Browse files
slowbrothinkyhead
andcommitted
🚸 M86-M87 Hotend Idle Timeout (MarlinFirmware#25015)
Co-authored-by: Scott Lahteine <[email protected]>
1 parent 992fb44 commit 94086c1

File tree

13 files changed

+199
-20
lines changed

13 files changed

+199
-20
lines changed

Marlin/src/feature/hotend_idle.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,33 @@
3737
#include "../module/planner.h"
3838
#include "../lcd/marlinui.h"
3939

40-
extern HotendIdleProtection hotend_idle;
40+
HotendIdleProtection hotend_idle;
4141

4242
millis_t HotendIdleProtection::next_protect_ms = 0;
43+
hotend_idle_settings_t HotendIdleProtection::cfg; // Initialized by settings.load()
4344

4445
void HotendIdleProtection::check_hotends(const millis_t &ms) {
46+
const bool busy = (TERN0(HAS_RESUME_CONTINUE, wait_for_user) || planner.has_blocks_queued());
4547
bool do_prot = false;
46-
HOTEND_LOOP() {
47-
const bool busy = (TERN0(HAS_RESUME_CONTINUE, wait_for_user) || planner.has_blocks_queued());
48-
if (thermalManager.degHotend(e) >= (HOTEND_IDLE_MIN_TRIGGER) && !busy) {
49-
do_prot = true; break;
48+
if (!busy && cfg.timeout != 0) {
49+
HOTEND_LOOP() {
50+
if (thermalManager.degHotend(e) >= cfg.trigger) {
51+
do_prot = true; break;
52+
}
5053
}
5154
}
52-
if (bool(next_protect_ms) != do_prot)
53-
next_protect_ms = do_prot ? ms + hp_interval : 0;
55+
if (!do_prot)
56+
next_protect_ms = 0; // No hotends are hot so cancel timeout
57+
else if (!next_protect_ms) // Timeout is possible?
58+
next_protect_ms = ms + cfg.timeout * 1000; // Start timeout if not already set
5459
}
5560

5661
void HotendIdleProtection::check_e_motion(const millis_t &ms) {
5762
static float old_e_position = 0;
5863
if (old_e_position != current_position.e) {
5964
old_e_position = current_position.e; // Track filament motion
6065
if (next_protect_ms) // If some heater is on then...
61-
next_protect_ms = ms + hp_interval; // ...delay the timeout till later
66+
next_protect_ms = ms + cfg.timeout * 1000; // ...delay the timeout till later
6267
}
6368
}
6469

@@ -79,12 +84,12 @@ void HotendIdleProtection::timed_out() {
7984
SERIAL_ECHOLNPGM("Hotend Idle Timeout");
8085
LCD_MESSAGE(MSG_HOTEND_IDLE_TIMEOUT);
8186
HOTEND_LOOP() {
82-
if ((HOTEND_IDLE_NOZZLE_TARGET) < thermalManager.degTargetHotend(e))
83-
thermalManager.setTargetHotend(HOTEND_IDLE_NOZZLE_TARGET, e);
87+
if (cfg.nozzle_target < thermalManager.degTargetHotend(e))
88+
thermalManager.setTargetHotend(cfg.nozzle_target, e);
8489
}
8590
#if HAS_HEATED_BED
86-
if ((HOTEND_IDLE_BED_TARGET) < thermalManager.degTargetBed())
87-
thermalManager.setTargetBed(HOTEND_IDLE_BED_TARGET);
91+
if (cfg.bed_target < thermalManager.degTargetBed())
92+
thermalManager.setTargetBed(cfg.bed_target);
8893
#endif
8994
}
9095

Marlin/src/feature/hotend_idle.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,26 @@
2121
*/
2222
#pragma once
2323

24-
#include "../core/millis_t.h"
24+
#include "../inc/MarlinConfig.h"
25+
26+
typedef struct {
27+
int16_t timeout, trigger, nozzle_target;
28+
#if HAS_HEATED_BED
29+
int16_t bed_target;
30+
#endif
31+
void set_defaults() {
32+
timeout = HOTEND_IDLE_TIMEOUT_SEC;
33+
trigger = HOTEND_IDLE_MIN_TRIGGER;
34+
nozzle_target = HOTEND_IDLE_NOZZLE_TARGET;
35+
bed_target = HOTEND_IDLE_BED_TARGET;
36+
}
37+
} hotend_idle_settings_t;
2538

2639
class HotendIdleProtection {
2740
public:
2841
static void check();
42+
static hotend_idle_settings_t cfg;
2943
private:
30-
static constexpr millis_t hp_interval = SEC_TO_MS(HOTEND_IDLE_TIMEOUT_SEC);
3144
static millis_t next_protect_ms;
3245
static void check_hotends(const millis_t &ms);
3346
static void check_e_motion(const millis_t &ms);

Marlin/src/gcode/gcode.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,8 +658,15 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
658658
case 82: M82(); break; // M82: Set E axis normal mode (same as other axes)
659659
case 83: M83(); break; // M83: Set E axis relative mode
660660
#endif
661+
661662
case 18: case 84: M18_M84(); break; // M18/M84: Disable Steppers / Set Timeout
662663
case 85: M85(); break; // M85: Set inactivity stepper shutdown timeout
664+
665+
#if ENABLED(HOTEND_IDLE_TIMEOUT)
666+
case 86: M86(); break; // M86: Set Hotend Idle Timeout
667+
case 87: M87(); break; // M87: Cancel Hotend Idle Timeout
668+
#endif
669+
663670
case 92: M92(); break; // M92: Set the steps-per-unit for one or more axes
664671
case 114: M114(); break; // M114: Report current position
665672
case 115: M115(); break; // M115: Report capabilities

Marlin/src/gcode/gcode.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,13 @@ class GcodeSuite {
711711
#endif
712712

713713
static void M85();
714+
715+
#if ENABLED(HOTEND_IDLE_TIMEOUT)
716+
static void M86();
717+
static void M86_report(const bool forReplay=true);
718+
static void M87();
719+
#endif
720+
714721
static void M92();
715722
static void M92_report(const bool forReplay=true, const int8_t e=-1);
716723

Marlin/src/gcode/temp/M86-M87.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
/**
24+
* gcode/temp/M86-M87.cpp
25+
*
26+
* Hotend Idle Timeout
27+
*/
28+
29+
#include "../../inc/MarlinConfigPre.h"
30+
31+
#if ENABLED(HOTEND_IDLE_TIMEOUT)
32+
33+
#include "../gcode.h"
34+
#include "../../feature/hotend_idle.h"
35+
36+
void GcodeSuite::M86_report(const bool forReplay/*=true*/) {
37+
hotend_idle_settings_t &c = hotend_idle.cfg;
38+
report_heading(forReplay, F("Hotend Idle Timeout"));
39+
SERIAL_ECHOLNPGM(" M86"
40+
#if HAS_HEATED_BED
41+
" B", c.bed_target,
42+
#endif
43+
" E", c.nozzle_target,
44+
" S", c.timeout,
45+
" T", c.trigger
46+
);
47+
}
48+
49+
/**
50+
* M86: Set / Report Hotend Idle Timeout
51+
*
52+
* Parameters
53+
* S<seconds> : Idle timeout. Set to 0 to disable.
54+
* E<temp> : Extruder idle temperature to set on timeout
55+
* B<temp> : Bed idle temperature to set on timeout
56+
* T<temp> : Minimum extruder temperature to consider for timeout (> idle temperature)
57+
*/
58+
void GcodeSuite::M86() {
59+
if (!parser.seen_any()) return M86_report();
60+
hotend_idle_settings_t &c = hotend_idle.cfg;
61+
if (parser.seenval('S')) c.timeout = parser.value_ushort();
62+
if (parser.seenval('T')) c.trigger = parser.value_celsius();
63+
if (parser.seenval('E')) c.nozzle_target = parser.value_celsius();
64+
#if HAS_HEATED_BED
65+
if (parser.seenval('B')) c.bed_target = parser.value_celsius();
66+
#endif
67+
const celsius_t min_trigger = c.nozzle_target + TEMP_HYSTERESIS;
68+
if (c.trigger <= min_trigger)
69+
SERIAL_ECHOLNPGM("?Idle Timeout (T) trigger temperature should be over ", min_trigger, "C.");
70+
}
71+
72+
/**
73+
* M86: Cancel Hotend Idle Timeout (by setting the timeout period to 0)
74+
*/
75+
void GcodeSuite::M87() {
76+
hotend_idle.cfg.timeout = 0;
77+
}
78+
79+
#endif // HOTEND_IDLE_TIMEOUT

Marlin/src/lcd/language/language_de.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ namespace Language_de {
343343
LSTR MSG_PID_AUTOTUNE_FAILED = _UxGT("PID Autotune fehlge.!");
344344
LSTR MSG_BAD_HEATER_ID = _UxGT("ungültiger Extruder.");
345345
LSTR MSG_TEMP_TOO_HIGH = _UxGT("Temperatur zu hoch.");
346-
LSTR MSG_TIMEOUT = _UxGT("Timeout.");
346+
LSTR MSG_TIMEOUT = _UxGT("Timeout");
347347
LSTR MSG_PID_BAD_HEATER_ID = _UxGT("Autotune fehlge.! Ungültiger Extruder");
348348
LSTR MSG_PID_TEMP_TOO_HIGH = _UxGT("Autotune fehlge.! Temperatur zu hoch.");
349349
LSTR MSG_PID_TIMEOUT = _UxGT("Autotune fehlge.! Timeout.");

Marlin/src/lcd/language/language_en.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ namespace Language_en {
367367
LSTR MSG_PID_AUTOTUNE_FAILED = _UxGT("PID Autotune failed!");
368368
LSTR MSG_BAD_HEATER_ID = _UxGT("Bad extruder.");
369369
LSTR MSG_TEMP_TOO_HIGH = _UxGT("Temperature too high.");
370-
LSTR MSG_TIMEOUT = _UxGT("Timeout.");
370+
LSTR MSG_TIMEOUT = _UxGT("Timeout");
371371
LSTR MSG_PID_BAD_HEATER_ID = _UxGT("Autotune failed! Bad extruder.");
372372
LSTR MSG_PID_TEMP_TOO_HIGH = _UxGT("Autotune failed! Temperature too high.");
373373
LSTR MSG_PID_TIMEOUT = _UxGT("Autotune failed! Timeout.");
@@ -670,6 +670,9 @@ namespace Language_en {
670670
LSTR MSG_INFO_RUNAWAY_OFF = _UxGT("Runaway Watch: OFF");
671671
LSTR MSG_INFO_RUNAWAY_ON = _UxGT("Runaway Watch: ON");
672672
LSTR MSG_HOTEND_IDLE_TIMEOUT = _UxGT("Hotend Idle Timeout");
673+
LSTR MSG_HOTEND_IDLE_DISABLE = _UxGT("Disable Timeout");
674+
LSTR MSG_HOTEND_IDLE_NOZZLE_TARGET = _UxGT("Nozzle Idle Temp");
675+
LSTR MSG_HOTEND_IDLE_BED_TARGET = _UxGT("Bed Idle Temp");
673676
LSTR MSG_FAN_SPEED_FAULT = _UxGT("Fan speed fault");
674677

675678
LSTR MSG_CASE_LIGHT = _UxGT("Case Light");

Marlin/src/lcd/language/language_it.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ namespace Language_it {
364364
LSTR MSG_PID_AUTOTUNE_FAILED = _UxGT("Calibr.PID fallito!");
365365
LSTR MSG_BAD_HEATER_ID = _UxGT("Estrusore invalido.");
366366
LSTR MSG_TEMP_TOO_HIGH = _UxGT("Temp.troppo alta.");
367-
LSTR MSG_TIMEOUT = _UxGT("Tempo scaduto.");
367+
LSTR MSG_TIMEOUT = _UxGT("Tempo scaduto");
368368
LSTR MSG_PID_BAD_HEATER_ID = _UxGT("Calibrazione fallita! Estrusore errato.");
369369
LSTR MSG_PID_TEMP_TOO_HIGH = _UxGT("Calibrazione fallita! Temperatura troppo alta.");
370370
LSTR MSG_PID_TIMEOUT = _UxGT("Calibrazione fallita! Tempo scaduto.");

Marlin/src/lcd/language/language_sk.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ namespace Language_sk {
363363
LSTR MSG_PID_AUTOTUNE_FAILED = _UxGT("Kal. PID zlyhala!");
364364
LSTR MSG_BAD_HEATER_ID = _UxGT("Zlý extrudér");
365365
LSTR MSG_TEMP_TOO_HIGH = _UxGT("Príliš vysoká tepl.");
366-
LSTR MSG_TIMEOUT = _UxGT("Čas vypršal.");
366+
LSTR MSG_TIMEOUT = _UxGT("Čas vypršal");
367367
LSTR MSG_PID_BAD_HEATER_ID = _UxGT("Auto-kal. zlyhala! Zlý extrúder.");
368368
LSTR MSG_PID_TEMP_TOO_HIGH = _UxGT("Auto-kal. zlyhala! Príliš vysoká tepl.");
369369
LSTR MSG_PID_TIMEOUT = _UxGT("Auto-kal. zlyhala! Čas vypršal.");

Marlin/src/lcd/menu/menu_configuration.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@
5959
#include "../../libs/buzzer.h"
6060
#endif
6161

62+
#if ENABLED(HOTEND_IDLE_TIMEOUT)
63+
#include "../../feature/hotend_idle.h"
64+
#endif
65+
6266
#if ANY(LCD_PROGRESS_BAR_TEST, LCD_ENDSTOP_TEST)
6367
#include "../lcdprint.h"
6468
#define HAS_DEBUG_MENU 1
@@ -277,6 +281,24 @@ void menu_advanced_settings();
277281
}
278282
#endif
279283

284+
#if ENABLED(HOTEND_IDLE_TIMEOUT)
285+
286+
void menu_hotend_idle() {
287+
hotend_idle_settings_t &c = hotend_idle.cfg;
288+
START_MENU();
289+
BACK_ITEM(MSG_BACK);
290+
291+
if (c.timeout) GCODES_ITEM(MSG_HOTEND_IDLE_DISABLE, F("M87"));
292+
EDIT_ITEM(int3, MSG_TIMEOUT, &c.timeout, 0, 999);
293+
EDIT_ITEM(int3, MSG_TEMPERATURE, &c.trigger, 0, HEATER_0_MAXTEMP);
294+
EDIT_ITEM(int3, MSG_HOTEND_IDLE_NOZZLE_TARGET, &c.nozzle_target, 0, HEATER_0_MAXTEMP);
295+
EDIT_ITEM(int3, MSG_HOTEND_IDLE_BED_TARGET, &c.bed_target, 0, BED_MAXTEMP);
296+
297+
END_MENU();
298+
}
299+
300+
#endif
301+
280302
#if ENABLED(DUAL_X_CARRIAGE)
281303

282304
void menu_idex() {
@@ -610,6 +632,10 @@ void menu_configuration() {
610632
#endif
611633
}
612634

635+
#if ENABLED(HOTEND_IDLE_TIMEOUT)
636+
SUBMENU(MSG_HOTEND_IDLE_TIMEOUT, menu_hotend_idle);
637+
#endif
638+
613639
//
614640
// Set single nozzle filament retract and prime length
615641
//

0 commit comments

Comments
 (0)