Filament Runout handling for Mixing Extruder (#20327)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -43,7 +43,7 @@ | ||||
|   #define FILAMENT_RUNOUT_THRESHOLD 5 | ||||
| #endif | ||||
|  | ||||
| void event_filament_runout(); | ||||
| void event_filament_runout(const uint8_t extruder); | ||||
|  | ||||
| template<class RESPONSE_T, class SENSOR_T> | ||||
| 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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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()); | ||||
|   | ||||
| @@ -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" | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user