From 2d4a1cd428896e6ff64097d372dc6bbf49d622a6 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 1 Mar 2021 03:16:22 +0100 Subject: [PATCH] Filament Runout handling for Mixing Extruder (#20327) Co-authored-by: Scott Lahteine --- Marlin/Configuration.h | 7 ++- Marlin/src/feature/runout.cpp | 30 +++++++--- Marlin/src/feature/runout.h | 96 +++++++++++++++++++++++-------- Marlin/src/inc/Conditionals_adv.h | 3 + Marlin/src/inc/SanityCheck.h | 32 +++++------ Marlin/src/module/endstops.cpp | 32 +++++------ buildroot/tests/mega2560-tests | 8 ++- 7 files changed, 139 insertions(+), 69 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 79b7f9dff..46b70eb72 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -1278,6 +1278,8 @@ #define FIL_RUNOUT_STATE LOW // Pin state indicating that filament is NOT present. #define FIL_RUNOUT_PULLUP // Use internal pullup for filament runout pins. //#define FIL_RUNOUT_PULLDOWN // Use internal pulldown for filament runout pins. + //#define WATCH_ALL_RUNOUT_SENSORS // Execute runout script on any triggering sensor, not only for the active extruder. + // This is automatically enabled for MIXING_EXTRUDERs. // Override individually if the runout sensors vary //#define FIL_RUNOUT1_STATE LOW @@ -1312,8 +1314,9 @@ //#define FIL_RUNOUT8_PULLUP //#define FIL_RUNOUT8_PULLDOWN - // Set one or more commands to execute on filament runout. - // (After 'M412 H' Marlin will ask the host to handle the process.) + // Commands to execute on filament runout. + // With multiple runout sensors use the %c placeholder for the current tool in commands (e.g., "M600 T%c") + // NOTE: After 'M412 H1' the host handles filament runout and this script does not apply. #define FILAMENT_RUNOUT_SCRIPT "M600" // After a runout is detected, continue printing this length of filament diff --git a/Marlin/src/feature/runout.cpp b/Marlin/src/feature/runout.cpp index 17d2c7400..531ca1081 100644 --- a/Marlin/src/feature/runout.cpp +++ b/Marlin/src/feature/runout.cpp @@ -47,12 +47,12 @@ bool FilamentMonitorBase::enabled = true, #if HAS_FILAMENT_RUNOUT_DISTANCE float RunoutResponseDelayed::runout_distance_mm = FILAMENT_RUNOUT_DISTANCE_MM; - volatile float RunoutResponseDelayed::runout_mm_countdown[EXTRUDERS]; + volatile float RunoutResponseDelayed::runout_mm_countdown[NUM_RUNOUT_SENSORS]; #if ENABLED(FILAMENT_MOTION_SENSOR) uint8_t FilamentSensorEncoder::motion_detected; #endif #else - int8_t RunoutResponseDebounced::runout_count; // = 0 + int8_t RunoutResponseDebounced::runout_count[NUM_RUNOUT_SENSORS]; // = 0 #endif // @@ -70,7 +70,7 @@ bool FilamentMonitorBase::enabled = true, #include "../lcd/extui/ui_api.h" #endif -void event_filament_runout() { +void event_filament_runout(const uint8_t extruder) { if (did_pause_print) return; // Action already in progress. Purge triggered repeated runout. @@ -85,10 +85,10 @@ void event_filament_runout() { } #endif - TERN_(EXTENSIBLE_UI, ExtUI::onFilamentRunout(ExtUI::getActiveTool())); + TERN_(EXTENSIBLE_UI, ExtUI::onFilamentRunout(ExtUI::getTool(extruder))); - #if EITHER(HOST_PROMPT_SUPPORT, HOST_ACTION_COMMANDS) - const char tool = '0' + TERN0(MULTI_FILAMENT_SENSOR, active_extruder); + #if ANY(HOST_PROMPT_SUPPORT, HOST_ACTION_COMMANDS, MULTI_FILAMENT_SENSOR) + const char tool = '0' + TERN0(MULTI_FILAMENT_SENSOR, extruder); #endif //action:out_of_filament @@ -124,8 +124,22 @@ void event_filament_runout() { SERIAL_EOL(); #endif // HOST_ACTION_COMMANDS - if (run_runout_script) - queue.inject_P(PSTR(FILAMENT_RUNOUT_SCRIPT)); + if (run_runout_script) { + #if MULTI_FILAMENT_SENSOR + char script[strlen(FILAMENT_RUNOUT_SCRIPT) + 1]; + sprintf_P(script, PSTR(FILAMENT_RUNOUT_SCRIPT), tool); + #if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG) + SERIAL_ECHOLNPAIR("Runout Command: ", script); + #endif + queue.inject(script); + #else + #if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG) + SERIAL_ECHOPGM("Runout Command: "); + SERIAL_ECHOLNPGM(FILAMENT_RUNOUT_SCRIPT); + #endif + queue.inject_P(PSTR(FILAMENT_RUNOUT_SCRIPT)); + #endif + } } #endif // HAS_FILAMENT_SENSOR diff --git a/Marlin/src/feature/runout.h b/Marlin/src/feature/runout.h index d253071ef..34ae67899 100644 --- a/Marlin/src/feature/runout.h +++ b/Marlin/src/feature/runout.h @@ -43,7 +43,7 @@ #define FILAMENT_RUNOUT_THRESHOLD 5 #endif -void event_filament_runout(); +void event_filament_runout(const uint8_t extruder); template class TFilamentMonitor; @@ -119,11 +119,41 @@ class TFilamentMonitor : public FilamentMonitorBase { TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, cli()); // Prevent RunoutResponseDelayed::block_completed from accumulating here response.run(); sensor.run(); - const bool ran_out = response.has_run_out(); + const uint8_t runout_flags = response.has_run_out(); TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, sei()); + #if MULTI_FILAMENT_SENSOR + #if ENABLED(WATCH_ALL_RUNOUT_SENSORS) + const bool ran_out = !!runout_flags; // any sensor triggers + uint8_t extruder = 0; + if (ran_out) { + uint8_t bitmask = runout_flags; + while (!(bitmask & 1)) { + bitmask >>= 1; + extruder++; + } + } + #else + const bool ran_out = TEST(runout_flags, active_extruder); // suppress non active extruders + uint8_t extruder = active_extruder; + #endif + #else + const bool ran_out = !!runout_flags; + uint8_t extruder = active_extruder; + #endif + + #if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG) + if (runout_flags) { + SERIAL_ECHOPGM("Runout Sensors: "); + LOOP_L_N(i, 8) SERIAL_ECHO('0' + TEST(runout_flags, i)); + SERIAL_ECHOPAIR(" -> ", extruder); + if (ran_out) SERIAL_ECHOPGM(" RUN OUT"); + SERIAL_EOL(); + } + #endif + if (ran_out) { filament_ran_out = true; - event_filament_runout(); + event_filament_runout(extruder); planner.synchronize(); } } @@ -280,16 +310,17 @@ class FilamentSensorBase { static inline void block_completed(const block_t* const) {} static inline void run() { - const bool out = poll_runout_state(active_extruder); - if (!out) filament_present(active_extruder); - #if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG) - static bool was_out = false; - if (out != was_out) { - was_out = out; - SERIAL_ECHOPGM("Filament "); - SERIAL_ECHOPGM_P(out ? PSTR("OUT\n") : PSTR("IN\n")); - } - #endif + LOOP_L_N(s, NUM_RUNOUT_SENSORS) { + const bool out = poll_runout_state(s); + if (!out) filament_present(s); + #if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG) + static uint8_t was_out; // = 0 + if (out != TEST(was_out, s)) { + TBI(was_out, s); + SERIAL_ECHOLNPAIR_P(PSTR("Filament Sensor "), '0' + s, out ? PSTR(" OUT") : PSTR(" IN")); + } + #endif + } } }; @@ -305,13 +336,13 @@ class FilamentSensorBase { // during a runout condition. class RunoutResponseDelayed { private: - static volatile float runout_mm_countdown[EXTRUDERS]; + static volatile float runout_mm_countdown[NUM_RUNOUT_SENSORS]; public: static float runout_distance_mm; static inline void reset() { - LOOP_L_N(i, EXTRUDERS) filament_present(i); + LOOP_L_N(i, NUM_RUNOUT_SENSORS) filament_present(i); } static inline void run() { @@ -320,15 +351,17 @@ class FilamentSensorBase { const millis_t ms = millis(); if (ELAPSED(ms, t)) { t = millis() + 1000UL; - LOOP_L_N(i, EXTRUDERS) + LOOP_L_N(i, NUM_RUNOUT_SENSORS) SERIAL_ECHOPAIR_P(i ? PSTR(", ") : PSTR("Remaining mm: "), runout_mm_countdown[i]); SERIAL_EOL(); } #endif } - static inline bool has_run_out() { - return runout_mm_countdown[active_extruder] < 0; + static inline uint8_t has_run_out() { + uint8_t runout_flags = 0; + LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (runout_mm_countdown[i] < 0) SBI(runout_flags, i); + return runout_flags; } static inline void filament_present(const uint8_t extruder) { @@ -353,13 +386,28 @@ class FilamentSensorBase { class RunoutResponseDebounced { private: static constexpr int8_t runout_threshold = FILAMENT_RUNOUT_THRESHOLD; - static int8_t runout_count; + static int8_t runout_count[NUM_RUNOUT_SENSORS]; + public: - static inline void reset() { runout_count = runout_threshold; } - static inline void run() { if (runout_count >= 0) runout_count--; } - static inline bool has_run_out() { return runout_count < 0; } - static inline void block_completed(const block_t* const) { } - static inline void filament_present(const uint8_t) { runout_count = runout_threshold; } + static inline void reset() { + LOOP_L_N(i, NUM_RUNOUT_SENSORS) filament_present(i); + } + + static inline void run() { + LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (runout_count[i] >= 0) runout_count[i]--; + } + + static inline uint8_t has_run_out() { + uint8_t runout_flags = 0; + LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (runout_count[i] < 0) SBI(runout_flags, i); + return runout_flags; + } + + static inline void block_completed(const block_t* const) { } + + static inline void filament_present(const uint8_t extruder) { + runout_count[extruder] = runout_threshold; + } }; #endif // !HAS_FILAMENT_RUNOUT_DISTANCE diff --git a/Marlin/src/inc/Conditionals_adv.h b/Marlin/src/inc/Conditionals_adv.h index 4738cda20..43f20b6df 100644 --- a/Marlin/src/inc/Conditionals_adv.h +++ b/Marlin/src/inc/Conditionals_adv.h @@ -134,6 +134,9 @@ #ifdef FILAMENT_RUNOUT_DISTANCE_MM #define HAS_FILAMENT_RUNOUT_DISTANCE 1 #endif + #if ENABLED(MIXING_EXTRUDER) + #define WATCH_ALL_RUNOUT_SENSORS + #endif #endif // Let SD_FINISHED_RELEASECOMMAND stand in for SD_FINISHED_STEPPERRELEASE diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index b6678a4bc..6581f629b 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -823,26 +823,24 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS #if HAS_FILAMENT_SENSOR #if !PIN_EXISTS(FIL_RUNOUT) #error "FILAMENT_RUNOUT_SENSOR requires FIL_RUNOUT_PIN." - #elif NUM_RUNOUT_SENSORS > E_STEPPERS - #if HAS_PRUSA_MMU2 + #elif HAS_PRUSA_MMU2 && NUM_RUNOUT_SENSORS != 1 #error "NUM_RUNOUT_SENSORS must be 1 with MMU2 / MMU2S." - #else - #error "NUM_RUNOUT_SENSORS cannot exceed the number of E steppers." - #endif - #elif NUM_RUNOUT_SENSORS >= 2 && !PIN_EXISTS(FIL_RUNOUT2) - #error "FIL_RUNOUT2_PIN is required with NUM_RUNOUT_SENSORS >= 2." - #elif NUM_RUNOUT_SENSORS >= 3 && !PIN_EXISTS(FIL_RUNOUT3) - #error "FIL_RUNOUT3_PIN is required with NUM_RUNOUT_SENSORS >= 3." - #elif NUM_RUNOUT_SENSORS >= 4 && !PIN_EXISTS(FIL_RUNOUT4) - #error "FIL_RUNOUT4_PIN is required with NUM_RUNOUT_SENSORS >= 4." - #elif NUM_RUNOUT_SENSORS >= 5 && !PIN_EXISTS(FIL_RUNOUT5) - #error "FIL_RUNOUT5_PIN is required with NUM_RUNOUT_SENSORS >= 5." - #elif NUM_RUNOUT_SENSORS >= 6 && !PIN_EXISTS(FIL_RUNOUT6) - #error "FIL_RUNOUT6_PIN is required with NUM_RUNOUT_SENSORS >= 6." - #elif NUM_RUNOUT_SENSORS >= 7 && !PIN_EXISTS(FIL_RUNOUT7) - #error "FIL_RUNOUT7_PIN is required with NUM_RUNOUT_SENSORS >= 7." + #elif NUM_RUNOUT_SENSORS != 1 && NUM_RUNOUT_SENSORS != E_STEPPERS + #error "NUM_RUNOUT_SENSORS must be either 1 or number of E steppers." #elif NUM_RUNOUT_SENSORS >= 8 && !PIN_EXISTS(FIL_RUNOUT8) #error "FIL_RUNOUT8_PIN is required with NUM_RUNOUT_SENSORS >= 8." + #elif NUM_RUNOUT_SENSORS >= 7 && !PIN_EXISTS(FIL_RUNOUT7) + #error "FIL_RUNOUT7_PIN is required with NUM_RUNOUT_SENSORS >= 7." + #elif NUM_RUNOUT_SENSORS >= 6 && !PIN_EXISTS(FIL_RUNOUT6) + #error "FIL_RUNOUT6_PIN is required with NUM_RUNOUT_SENSORS >= 6." + #elif NUM_RUNOUT_SENSORS >= 5 && !PIN_EXISTS(FIL_RUNOUT5) + #error "FIL_RUNOUT5_PIN is required with NUM_RUNOUT_SENSORS >= 5." + #elif NUM_RUNOUT_SENSORS >= 4 && !PIN_EXISTS(FIL_RUNOUT4) + #error "FIL_RUNOUT4_PIN is required with NUM_RUNOUT_SENSORS >= 4." + #elif NUM_RUNOUT_SENSORS >= 3 && !PIN_EXISTS(FIL_RUNOUT3) + #error "FIL_RUNOUT3_PIN is required with NUM_RUNOUT_SENSORS >= 3." + #elif NUM_RUNOUT_SENSORS >= 2 && !PIN_EXISTS(FIL_RUNOUT2) + #error "FIL_RUNOUT2_PIN is required with NUM_RUNOUT_SENSORS >= 2." #elif BOTH(FIL_RUNOUT1_PULLUP, FIL_RUNOUT1_PULLDOWN) #error "You can't enable FIL_RUNOUT1_PULLUP and FIL_RUNOUT1_PULLDOWN at the same time." #elif BOTH(FIL_RUNOUT2_PULLUP, FIL_RUNOUT2_PULLDOWN) diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index 9550799a2..4192b444b 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -468,24 +468,22 @@ void _O2 Endstops::report_states() { #if HAS_CUSTOM_PROBE_PIN print_es_state(PROBE_TRIGGERED(), PSTR(STR_Z_PROBE)); #endif - #if HAS_FILAMENT_SENSOR - #if NUM_RUNOUT_SENSORS == 1 - print_es_state(READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE, PSTR(STR_FILAMENT_RUNOUT_SENSOR)); - #else - #define _CASE_RUNOUT(N) case N: pin = FIL_RUNOUT##N##_PIN; state = FIL_RUNOUT##N##_STATE; break; - LOOP_S_LE_N(i, 1, NUM_RUNOUT_SENSORS) { - pin_t pin; - uint8_t state; - switch (i) { - default: continue; - REPEAT_S(1, INCREMENT(NUM_RUNOUT_SENSORS), _CASE_RUNOUT) - } - SERIAL_ECHOPGM(STR_FILAMENT_RUNOUT_SENSOR); - if (i > 1) SERIAL_CHAR(' ', '0' + i); - print_es_state(extDigitalRead(pin) != state); + #if MULTI_FILAMENT_SENSOR + #define _CASE_RUNOUT(N) case N: pin = FIL_RUNOUT##N##_PIN; state = FIL_RUNOUT##N##_STATE; break; + LOOP_S_LE_N(i, 1, NUM_RUNOUT_SENSORS) { + pin_t pin; + uint8_t state; + switch (i) { + default: continue; + REPEAT_S(1, INCREMENT(NUM_RUNOUT_SENSORS), _CASE_RUNOUT) } - #undef _CASE_RUNOUT - #endif + SERIAL_ECHOPGM(STR_FILAMENT_RUNOUT_SENSOR); + if (i > 1) SERIAL_CHAR(' ', '0' + i); + print_es_state(extDigitalRead(pin) != state); + } + #undef _CASE_RUNOUT + #elif HAS_FILAMENT_SENSOR + print_es_state(READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE, PSTR(STR_FILAMENT_RUNOUT_SENSOR)); #endif TERN_(BLTOUCH, bltouch._reset_SW_mode()); diff --git a/buildroot/tests/mega2560-tests b/buildroot/tests/mega2560-tests index aef2b8756..d74aa30f8 100755 --- a/buildroot/tests/mega2560-tests +++ b/buildroot/tests/mega2560-tests @@ -94,8 +94,14 @@ restore_configs opt_set MOTHERBOARD BOARD_AZTEEG_X3_PRO opt_set LCD_LANGUAGE el_gr opt_enable MIXING_EXTRUDER GRADIENT_MIX GRADIENT_VTOOL CR10_STOCKDISPLAY \ - USE_CONTROLLER_FAN CONTROLLER_FAN_EDITABLE CONTROLLER_FAN_IGNORE_Z + USE_CONTROLLER_FAN CONTROLLER_FAN_EDITABLE CONTROLLER_FAN_IGNORE_Z \ + FILAMENT_RUNOUT_SENSOR ADVANCED_PAUSE_FEATURE NOZZLE_PARK_FEATURE opt_set MIXING_STEPPERS 5 +opt_set NUM_RUNOUT_SENSORS E_STEPPERS +opt_set FIL_RUNOUT2_PIN 16 +opt_set FIL_RUNOUT3_PIN 17 +opt_set FIL_RUNOUT4_PIN 4 +opt_set FIL_RUNOUT5_PIN 5 opt_set LCD_LANGUAGE ru exec_test $1 $2 "Azteeg X3 | Mixing Extruder (x5) | Gradient Mix | Greek" "$3"