Prusa MINI Firmware overview
runout.h
Go to the documentation of this file.
1 /**
2  * Marlin 3D Printer Firmware
3  * Copyright (c) 2019 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 <http://www.gnu.org/licenses/>.
20  *
21  */
22 #pragma once
23 
24 /**
25  * feature/runout.h - Runout sensor support
26  */
27 
28 #include "../sd/cardreader.h"
29 #include "../module/printcounter.h"
30 #include "../module/planner.h"
31 #include "../module/stepper.h" // for block_t
32 #include "../gcode/queue.h"
33 
34 #include "../inc/MarlinConfig.h"
35 
36 #if ENABLED(EXTENSIBLE_UI)
37  #include "../lcd/extensible_ui/ui_api.h"
38 #endif
39 
40 #if ENABLED(ADVANCED_PAUSE_FEATURE)
41  #include "pause.h"
42 #endif
43 
44 //#define FILAMENT_RUNOUT_SENSOR_DEBUG
45 #ifndef FILAMENT_RUNOUT_THRESHOLD
46  #define FILAMENT_RUNOUT_THRESHOLD 5
47 #endif
48 
50 
52  public:
53  static bool enabled, filament_ran_out;
54 
55  #if ENABLED(HOST_ACTION_COMMANDS)
56  static bool host_handling;
57  #else
58  static constexpr bool host_handling = false;
59  #endif
60 };
61 
62 template<class RESPONSE_T, class SENSOR_T>
64  private:
65  typedef RESPONSE_T response_t;
66  typedef SENSOR_T sensor_t;
67  static response_t response;
68  static sensor_t sensor;
69 
70  public:
71  static inline void setup() {
72  sensor.setup();
73  reset();
74  }
75 
76  static inline void reset() {
77  filament_ran_out = false;
78  response.reset();
79  }
80 
81  // Call this method when filament is present,
82  // so the response can reset its counter.
83  static inline void filament_present(const uint8_t extruder) {
84  response.filament_present(extruder);
85  }
86 
87  #ifdef FILAMENT_RUNOUT_DISTANCE_MM
88  static inline float& runout_distance() { return response.runout_distance_mm; }
89  static inline void set_runout_distance(const float &mm) { response.runout_distance_mm = mm; }
90  #endif
91 
92  // Handle a block completion. RunoutResponseDelayed uses this to
93  // add up the length of filament moved while the filament is out.
94  static inline void block_completed(const block_t* const b) {
95  if (enabled) {
96  response.block_completed(b);
97  sensor.block_completed(b);
98  }
99  }
100 
101  // Give the response a chance to update its counter.
102  static inline void run() {
105  || did_pause_print
106  #endif
107  )) {
108  #ifdef FILAMENT_RUNOUT_DISTANCE_MM
109  cli(); // Prevent RunoutResponseDelayed::block_completed from accumulating here
110  #endif
111  response.run();
112  sensor.run();
113  const bool ran_out = response.has_run_out();
114  #ifdef FILAMENT_RUNOUT_DISTANCE_MM
115  sei();
116  #endif
117  if (ran_out) {
118  filament_ran_out = true;
121  }
122  }
123  }
124 };
125 
126 /*************************** FILAMENT PRESENCE SENSORS ***************************/
127 
129  protected:
130  static void filament_present(const uint8_t extruder);
131 
132  public:
133  static inline void setup() {
134  #if ENABLED(FIL_RUNOUT_PULLUP)
135  #define INIT_RUNOUT_PIN(P) SET_INPUT_PULLUP(P)
136  #elif ENABLED(FIL_RUNOUT_PULLDOWN)
137  #define INIT_RUNOUT_PIN(P) SET_INPUT_PULLDOWN(P)
138  #else
139  #define INIT_RUNOUT_PIN(P) SET_INPUT(P)
140  #endif
141 
143  #if NUM_RUNOUT_SENSORS > 1
145  #if NUM_RUNOUT_SENSORS > 2
146  INIT_RUNOUT_PIN(FIL_RUNOUT3_PIN);
147  #if NUM_RUNOUT_SENSORS > 3
148  INIT_RUNOUT_PIN(FIL_RUNOUT4_PIN);
149  #if NUM_RUNOUT_SENSORS > 4
150  INIT_RUNOUT_PIN(FIL_RUNOUT5_PIN);
151  #if NUM_RUNOUT_SENSORS > 5
152  INIT_RUNOUT_PIN(FIL_RUNOUT6_PIN);
153  #endif
154  #endif
155  #endif
156  #endif
157  #endif
158  }
159 
160  // Return a bitmask of runout pin states
161  static inline uint8_t poll_runout_pins() {
162  return (
163  (READ(FIL_RUNOUT_PIN ) ? _BV(0) : 0)
164  #if NUM_RUNOUT_SENSORS > 1
165  | (READ(FIL_RUNOUT2_PIN) ? _BV(1) : 0)
166  #if NUM_RUNOUT_SENSORS > 2
167  | (READ(FIL_RUNOUT3_PIN) ? _BV(2) : 0)
168  #if NUM_RUNOUT_SENSORS > 3
169  | (READ(FIL_RUNOUT4_PIN) ? _BV(3) : 0)
170  #if NUM_RUNOUT_SENSORS > 4
171  | (READ(FIL_RUNOUT5_PIN) ? _BV(4) : 0)
172  #if NUM_RUNOUT_SENSORS > 5
173  | (READ(FIL_RUNOUT6_PIN) ? _BV(5) : 0)
174  #endif
175  #endif
176  #endif
177  #endif
178  #endif
179  );
180  }
181 
182  // Return a bitmask of runout flag states (1 bits always indicates runout)
183  static inline uint8_t poll_runout_states() {
184  return poll_runout_pins() ^ uint8_t(
185  #if DISABLED(FIL_RUNOUT_INVERTING)
187  #else
188  0
189  #endif
190  );
191  }
192 };
193 
194 #if ENABLED(FILAMENT_MOTION_SENSOR)
195 
196  /**
197  * This sensor uses a magnetic encoder disc and a Hall effect
198  * sensor (or a slotted disc and optical sensor). The state
199  * will toggle between 0 and 1 on filament movement. It can detect
200  * filament runout and stripouts or jams.
201  */
202  class FilamentSensorEncoder : public FilamentSensorBase {
203  private:
204  static uint8_t motion_detected;
205 
206  static inline void poll_motion_sensor() {
207  static uint8_t old_state;
208  const uint8_t new_state = poll_runout_pins(),
209  change = old_state ^ new_state;
210  old_state = new_state;
211 
212  #ifdef FILAMENT_RUNOUT_SENSOR_DEBUG
213  if (change) {
214  SERIAL_ECHOPGM("Motion detected:");
215  for (uint8_t e = 0; e < NUM_RUNOUT_SENSORS; e++)
216  if (TEST(change, e)) { SERIAL_CHAR(' '); SERIAL_CHAR('0' + e); }
217  SERIAL_EOL();
218  }
219  #endif
220 
221  motion_detected |= change;
222  }
223 
224  public:
225  static inline void block_completed(const block_t* const b) {
226  // If the sensor wheel has moved since the last call to
227  // this method reset the runout counter for the extruder.
228  if (TEST(motion_detected, b->extruder))
229  filament_present(b->extruder);
230 
231  // Clear motion triggers for next block
232  motion_detected = 0;
233  }
234 
235  static inline void run() { poll_motion_sensor(); }
236  };
237 
238 #else
239 
240  /**
241  * This is a simple endstop switch in the path of the filament.
242  * It can detect filament runout, but not stripouts or jams.
243  */
245  private:
246  static inline bool poll_runout_state(const uint8_t extruder) {
247  const uint8_t runout_states = poll_runout_states();
248 
249  #if NUM_RUNOUT_SENSORS == 1
250  UNUSED(extruder);
251  #endif
252 
253  if (true
254  #if NUM_RUNOUT_SENSORS > 1
255  #if ENABLED(DUAL_X_CARRIAGE)
256  && (dual_x_carriage_mode == DXC_DUPLICATION_MODE || dual_x_carriage_mode == DXC_MIRRORED_MODE)
257  #elif ENABLED(MULTI_NOZZLE_DUPLICATION)
259  #else
260  && false
261  #endif
262  #endif
263  ) return runout_states; // Any extruder
264 
265  #if NUM_RUNOUT_SENSORS > 1
266  return TEST(runout_states, extruder); // Specific extruder
267  #endif
268  }
269 
270  public:
271  static inline void block_completed(const block_t* const) {}
272 
273  static inline void run() {
274  const bool out = poll_runout_state(active_extruder);
276  #ifdef FILAMENT_RUNOUT_SENSOR_DEBUG
277  static bool was_out = false;
278  if (out != was_out) {
279  was_out = out;
280  SERIAL_ECHOPGM("Filament ");
281  serialprintPGM(out ? PSTR("OUT\n") : PSTR("IN\n"));
282  }
283  #endif
284  }
285  };
286 
287 
288 #endif // !FILAMENT_MOTION_SENSOR
289 
290 /********************************* RESPONSE TYPE *********************************/
291 
292 #ifdef FILAMENT_RUNOUT_DISTANCE_MM
293 
294  // RunoutResponseDelayed triggers a runout event only if the length
295  // of filament specified by FILAMENT_RUNOUT_DISTANCE_MM has been fed
296  // during a runout condition.
297  class RunoutResponseDelayed {
298  private:
299  static volatile float runout_mm_countdown[EXTRUDERS];
300 
301  public:
302  static float runout_distance_mm;
303 
304  static inline void reset() {
305  LOOP_L_N(i, EXTRUDERS) filament_present(i);
306  }
307 
308  static inline void run() {
309  #ifdef FILAMENT_RUNOUT_SENSOR_DEBUG
310  static millis_t t = 0;
311  const millis_t ms = millis();
312  if (ELAPSED(ms, t)) {
313  t = millis() + 1000UL;
314  LOOP_L_N(i, EXTRUDERS) {
315  serialprintPGM(i ? PSTR(", ") : PSTR("Remaining mm: "));
316  SERIAL_ECHO(runout_mm_countdown[i]);
317  }
318  SERIAL_EOL();
319  }
320  #endif
321  }
322 
323  static inline bool has_run_out() {
324  return runout_mm_countdown[active_extruder] < 0;
325  }
326 
327  static inline void filament_present(const uint8_t extruder) {
328  runout_mm_countdown[extruder] = runout_distance_mm;
329  }
330 
331  static inline void block_completed(const block_t* const b) {
332  if (b->steps.x || b->steps.y || b->steps.z
334  || did_pause_print // Allow pause purge move to re-trigger runout state
335  #endif
336  ) {
337  // Only trigger on extrusion with XYZ movement to allow filament change and retract/recover.
338  const uint8_t e = b->extruder;
339  const int32_t steps = b->steps.e;
340  runout_mm_countdown[e] -= (TEST(b->direction_bits, E_AXIS) ? -steps : steps) * planner.steps_to_mm[E_AXIS_N(e)];
341  }
342  }
343  };
344 
345 #else // !FILAMENT_RUNOUT_DISTANCE_MM
346 
347  // RunoutResponseDebounced triggers a runout event after a runout
348  // condition has been detected runout_threshold times in a row.
349 
351  private:
352  static constexpr int8_t runout_threshold = FILAMENT_RUNOUT_THRESHOLD;
353  static int8_t runout_count;
354  public:
355  static inline void reset() { runout_count = runout_threshold; }
356  static inline void run() { if (runout_count >= 0) runout_count--; }
357  static inline bool has_run_out() { return runout_count < 0; }
358  static inline void block_completed(const block_t* const) { }
359  static inline void filament_present(const uint8_t) { runout_count = runout_threshold; }
360  };
361 
362 #endif // !FILAMENT_RUNOUT_DISTANCE_MM
363 
364 /********************************* TEMPLATE SPECIALIZATION *********************************/
365 
366 typedef TFilamentMonitor<
367  #ifdef FILAMENT_RUNOUT_DISTANCE_MM
368  RunoutResponseDelayed,
369  #if ENABLED(FILAMENT_MOTION_SENSOR)
370  FilamentSensorEncoder
371  #else
373  #endif
374  #else
376  #endif
378 
379 extern FilamentMonitor runout;
SERIAL_CHAR
#define SERIAL_CHAR(x)
Definition: serial.h:69
FilamentSensorBase::poll_runout_pins
static uint8_t poll_runout_pins()
Definition: runout.h:161
Planner::synchronize
static void synchronize()
Definition: planner.cpp:1556
host_action_prompt_end
void host_action_prompt_end()
Definition: marlin_server.cpp:1047
SERIAL_ECHO
#define SERIAL_ECHO(x)
Definition: serial.h:70
RunoutResponseDebounced::filament_present
static void filament_present(const uint8_t)
Definition: runout.h:359
runout.h
queue
GCodeQueue queue
Definition: queue.cpp:28
host_action
void host_action(const char *const pstr, const bool eol=true)
Definition: marlin_server.cpp:959
TFilamentMonitor
Definition: runout.h:63
RunoutResponseDebounced::block_completed
static void block_completed(const block_t *const)
Definition: runout.h:358
FilamentSensorSwitch::block_completed
static void block_completed(const block_t *const)
Definition: runout.h:271
FilamentMonitorBase::filament_ran_out
static bool filament_ran_out
Definition: runout.h:53
i
uint8_t i
Definition: screen_test_graph.c:72
FIL_RUNOUT2_PIN
#define FIL_RUNOUT2_PIN
Definition: pins_GT2560_V3.h:83
millis
uint32_t millis(void)
Definition: wiring_time.c:29
INIT_RUNOUT_PIN
#define INIT_RUNOUT_PIN(P)
extruder_duplication_enabled
bool extruder_duplication_enabled
Definition: motion.cpp:876
FILAMENT_RUNOUT_THRESHOLD
#define FILAMENT_RUNOUT_THRESHOLD
Definition: runout.h:46
E_AXIS_N
#define E_AXIS_N(E)
Definition: Conditionals_LCD.h:454
TFilamentMonitor::block_completed
static void block_completed(const block_t *const b)
Definition: runout.h:94
RunoutResponseDebounced::reset
static void reset()
Definition: runout.h:355
PSTR
#define PSTR(str)
Definition: pgmspace.h:31
FilamentMonitor
TFilamentMonitor< RunoutResponseDebounced, FilamentSensorSwitch > FilamentMonitor
Definition: runout.h:377
LOOP_L_N
#define LOOP_L_N(VAR, N)
Definition: types.h:58
DISABLED
#define DISABLED(V...)
Definition: macros.h:178
RunoutResponseDebounced
Definition: runout.h:350
runout
FilamentMonitor runout
TFilamentMonitor::reset
static void reset()
Definition: runout.h:76
cli
void cli()
FilamentSensorSwitch::run
static void run()
Definition: runout.h:273
FilamentSensorBase
Definition: runout.h:128
SERIAL_ECHOPGM
#define SERIAL_ECHOPGM(S)
Definition: serial.h:173
event_filament_runout
void event_filament_runout()
NUM_RUNOUT_SENSORS
#define NUM_RUNOUT_SENSORS
Definition: pins_FORMBOT_TREX3.h:132
ELAPSED
#define ELAPSED(NOW, SOON)
Definition: millis_t.h:29
uint8_t
const uint8_t[]
Definition: 404_html.c:3
_BV
#define _BV(bit)
Definition: wiring_constants.h:99
sei
void sei()
Planner::steps_to_mm
static float steps_to_mm[XYZE_N]
Definition: planner.h:254
FIL_RUNOUT_PIN
#define FIL_RUNOUT_PIN
Definition: pins_RAMPS_LINUX.h:216
pause.h
FilamentSensorBase::poll_runout_states
static uint8_t poll_runout_states()
Definition: runout.h:183
EXTRUDERS
#define EXTRUDERS
Definition: Configuration_A3ides_2209_MINI.h:148
FilamentSensorSwitch
Definition: runout.h:244
FilamentSensorBase::filament_present
static void filament_present(const uint8_t extruder)
ADVANCED_PAUSE_FEATURE
#define ADVANCED_PAUSE_FEATURE
Definition: Configuration_A3ides_2209_MINI_adv.h:1311
SERIAL_EOL
#define SERIAL_EOL()
Definition: serial.h:181
host_actions.h
host_action_paused
void host_action_paused(const bool eol)
Definition: marlin_server.cpp:1021
FilamentMonitorBase::enabled
static bool enabled
Definition: runout.h:53
TEST
#define TEST(n, b)
Definition: macros.h:81
FilamentSensorBase::setup
static void setup()
Definition: runout.h:133
ExtUI::onFilamentRunout
void onFilamentRunout(const extruder_t extruder)
Definition: marlin_server.cpp:894
host_action_pause
void host_action_pause(const bool eol)
Definition: marlin_server.cpp:1009
printingIsActive
bool printingIsActive()
Definition: Marlin.cpp:349
GCodeQueue::inject_P
static void inject_P(PGM_P const pgcode)
Definition: queue.cpp:206
E_AXIS
Definition: types.h:40
FilamentMonitorBase::host_handling
static constexpr bool host_handling
Definition: runout.h:58
RunoutResponseDebounced::has_run_out
static bool has_run_out()
Definition: runout.h:357
serialprintPGM
void serialprintPGM(PGM_P str)
Definition: serial.cpp:35
RunoutResponseDebounced::run
static void run()
Definition: runout.h:356
host_action_prompt_show
void host_action_prompt_show()
Definition: marlin_server.cpp:1053
TFilamentMonitor::filament_present
static void filament_present(const uint8_t extruder)
Definition: runout.h:83
TFilamentMonitor::setup
static void setup()
Definition: runout.h:71
createSpeedLookupTable.b
list b
Definition: createSpeedLookupTable.py:30
READ
#define READ(IO)
Definition: fastio.h:95
FilamentMonitorBase
Definition: runout.h:51
block_t
Definition: planner.h:95
UNUSED
#define UNUSED(X)
Definition: stm32f4xx_hal_def.h:74
millis_t
uint32_t millis_t
Definition: millis_t.h:26
TFilamentMonitor::run
static void run()
Definition: runout.h:102
active_extruder
constexpr uint8_t active_extruder
Definition: motion.h:107
ENABLED
#define ENABLED(V...)
Definition: macros.h:177
host_action_prompt_begin
void host_action_prompt_begin(const char *const pstr, const bool eol)
Definition: marlin_server.cpp:1033
planner
Planner planner
Definition: planner.cpp:111
host_prompt_reason
PromptReason host_prompt_reason
Definition: marlin_server.cpp:77