Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

One extruder can simultaneously enable two types of runout sensors #25548

Merged
merged 4 commits into from
Mar 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions Marlin/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,47 @@
// as the filament moves. (Be sure to set FILAMENT_RUNOUT_DISTANCE_MM
// large enough to avoid false positives.)
//#define FILAMENT_MOTION_SENSOR

#if ENABLED(FILAMENT_MOTION_SENSOR)
//#define FILAMENT_SWITCH_AND_MOTION
#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
#define NUM_MOTION_SENSORS 1 // Number of sensors, up to one per extruder. Define a FIL_MOTION#_PIN for each.
//#define FIL_MOTION1_PIN -1

// Override individually if the motion sensors vary
//#define FIL_MOTION1_STATE LOW
//#define FIL_MOTION1_PULLUP
//#define FIL_MOTION1_PULLDOWN

//#define FIL_MOTION2_STATE LOW
//#define FIL_MOTION2_PULLUP
//#define FIL_MOTION2_PULLDOWN

//#define FIL_MOTION3_STATE LOW
//#define FIL_MOTION3_PULLUP
//#define FIL_MOTION3_PULLDOWN

//#define FIL_MOTION4_STATE LOW
//#define FIL_MOTION4_PULLUP
//#define FIL_MOTION4_PULLDOWN

//#define FIL_MOTION5_STATE LOW
//#define FIL_MOTION5_PULLUP
//#define FIL_MOTION5_PULLDOWN

//#define FIL_MOTION6_STATE LOW
//#define FIL_MOTION6_PULLUP
//#define FIL_MOTION6_PULLDOWN

//#define FIL_MOTION7_STATE LOW
//#define FIL_MOTION7_PULLUP
//#define FIL_MOTION7_PULLDOWN

//#define FIL_MOTION8_STATE LOW
//#define FIL_MOTION8_PULLUP
//#define FIL_MOTION8_PULLDOWN
#endif
#endif
#endif
#endif

Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/feature/runout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ bool FilamentMonitorBase::enabled = true,

#if HAS_FILAMENT_RUNOUT_DISTANCE
float RunoutResponseDelayed::runout_distance_mm = FILAMENT_RUNOUT_DISTANCE_MM;
volatile float RunoutResponseDelayed::runout_mm_countdown[NUM_RUNOUT_SENSORS];
volatile countdown_t RunoutResponseDelayed::mm_countdown;
#if ENABLED(FILAMENT_MOTION_SENSOR)
uint8_t FilamentSensorEncoder::motion_detected;
#endif
Expand Down
172 changes: 107 additions & 65 deletions Marlin/src/feature/runout.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,26 @@
#define FILAMENT_RUNOUT_THRESHOLD 5
#endif

#if ENABLED(FILAMENT_MOTION_SENSOR)
#define HAS_FILAMENT_MOTION 1
#endif
#if DISABLED(FILAMENT_MOTION_SENSOR) || ENABLED(FILAMENT_SWITCH_AND_MOTION)
#define HAS_FILAMENT_SWITCH 1
#endif

void event_filament_runout(const uint8_t extruder);

template<class RESPONSE_T, class SENSOR_T>
class TFilamentMonitor;
class FilamentSensorEncoder;
class FilamentSensorSwitch;
class FilamentSensor;
class RunoutResponseDelayed;
class RunoutResponseDebounced;

/********************************* TEMPLATE SPECIALIZATION *********************************/

typedef TFilamentMonitor<
TERN(HAS_FILAMENT_RUNOUT_DISTANCE, RunoutResponseDelayed, RunoutResponseDebounced),
TERN(FILAMENT_MOTION_SENSOR, FilamentSensorEncoder, FilamentSensorSwitch)
FilamentSensor
> FilamentMonitor;

extern FilamentMonitor runout;
Expand Down Expand Up @@ -98,6 +104,11 @@ class TFilamentMonitor : public FilamentMonitorBase {
static void filament_present(const uint8_t extruder) {
response.filament_present(extruder);
}
#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
static void filament_motion_present(const uint8_t extruder) {
response.filament_motion_present(extruder);
}
#endif

#if HAS_FILAMENT_RUNOUT_DISTANCE
static float& runout_distance() { return response.runout_distance_mm; }
Expand Down Expand Up @@ -171,37 +182,25 @@ class FilamentSensorBase {
static void filament_present(const uint8_t extruder) {
runout.filament_present(extruder); // ...which calls response.filament_present(extruder)
}
#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
static void filament_motion_present(const uint8_t extruder) {
runout.filament_motion_present(extruder); // ...which calls response.filament_motion_present(extruder)
}
#endif

public:
static void setup() {
#define _INIT_RUNOUT_PIN(P,S,U,D) do{ if (ENABLED(U)) SET_INPUT_PULLUP(P); else if (ENABLED(D)) SET_INPUT_PULLDOWN(P); else SET_INPUT(P); }while(0)
#define _INIT_RUNOUT_PIN(P,S,U,D) do{ if (ENABLED(U)) SET_INPUT_PULLUP(P); else if (ENABLED(D)) SET_INPUT_PULLDOWN(P); else SET_INPUT(P); }while(0);
#define INIT_RUNOUT_PIN(N) _INIT_RUNOUT_PIN(FIL_RUNOUT##N##_PIN, FIL_RUNOUT##N##_STATE, FIL_RUNOUT##N##_PULLUP, FIL_RUNOUT##N##_PULLDOWN)
#if NUM_RUNOUT_SENSORS >= 1
INIT_RUNOUT_PIN(1);
#endif
#if NUM_RUNOUT_SENSORS >= 2
INIT_RUNOUT_PIN(2);
#endif
#if NUM_RUNOUT_SENSORS >= 3
INIT_RUNOUT_PIN(3);
#endif
#if NUM_RUNOUT_SENSORS >= 4
INIT_RUNOUT_PIN(4);
#endif
#if NUM_RUNOUT_SENSORS >= 5
INIT_RUNOUT_PIN(5);
#endif
#if NUM_RUNOUT_SENSORS >= 6
INIT_RUNOUT_PIN(6);
#endif
#if NUM_RUNOUT_SENSORS >= 7
INIT_RUNOUT_PIN(7);
#endif
#if NUM_RUNOUT_SENSORS >= 8
INIT_RUNOUT_PIN(8);
REPEAT_1(NUM_RUNOUT_SENSORS, INIT_RUNOUT_PIN);
#undef INIT_RUNOUT_PIN

#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
#define INIT_MOTION_PIN(N) _INIT_RUNOUT_PIN(FIL_MOTION##N##_PIN, FIL_MOTION##N##_STATE, FIL_MOTION##N##_PULLUP, FIL_MOTION##N##_PULLDOWN)
REPEAT_1(NUM_MOTION_SENSORS, INIT_MOTION_PIN);
#undef INIT_MOTION_PIN
#endif
#undef _INIT_RUNOUT_PIN
#undef INIT_RUNOUT_PIN
}

// Return a bitmask of runout pin states
Expand All @@ -213,36 +212,29 @@ class FilamentSensorBase {

// Return a bitmask of runout flag states (1 bits always indicates runout)
static uint8_t poll_runout_states() {
return poll_runout_pins() ^ uint8_t(0
#if NUM_RUNOUT_SENSORS >= 1
| (FIL_RUNOUT1_STATE ? 0 : _BV(1 - 1))
#endif
#if NUM_RUNOUT_SENSORS >= 2
| (FIL_RUNOUT2_STATE ? 0 : _BV(2 - 1))
#endif
#if NUM_RUNOUT_SENSORS >= 3
| (FIL_RUNOUT3_STATE ? 0 : _BV(3 - 1))
#endif
#if NUM_RUNOUT_SENSORS >= 4
| (FIL_RUNOUT4_STATE ? 0 : _BV(4 - 1))
#endif
#if NUM_RUNOUT_SENSORS >= 5
| (FIL_RUNOUT5_STATE ? 0 : _BV(5 - 1))
#endif
#if NUM_RUNOUT_SENSORS >= 6
| (FIL_RUNOUT6_STATE ? 0 : _BV(6 - 1))
#endif
#if NUM_RUNOUT_SENSORS >= 7
| (FIL_RUNOUT7_STATE ? 0 : _BV(7 - 1))
#endif
#if NUM_RUNOUT_SENSORS >= 8
| (FIL_RUNOUT8_STATE ? 0 : _BV(8 - 1))
#endif
);
#define _OR_RUNOUT(N) | (FIL_RUNOUT##N##_STATE ? 0 : _BV(N - 1))
return poll_runout_pins() ^ uint8_t(0 REPEAT_1(NUM_RUNOUT_SENSORS, _OR_RUNOUT));
#undef _OR_RUNOUT
}

#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
// Return a bitmask of motion pin states
static uint8_t poll_motion_pins() {
#define _OR_MOTION(N) | (READ(FIL_MOTION##N##_PIN) ? _BV((N) - 1) : 0)
return (0 REPEAT_1(NUM_MOTION_SENSORS, _OR_MOTION));
#undef _OR_MOTION
}

// Return a bitmask of motion flag states (1 bits always indicates runout)
static uint8_t poll_motion_states() {
#define _OR_MOTION(N) | (FIL_MOTION##N##_STATE ? 0 : _BV(N - 1))
return poll_motion_pins() ^ uint8_t(0 REPEAT_1(NUM_MOTION_SENSORS, _OR_MOTION));
#undef _OR_MOTION
}
#endif
};

#if ENABLED(FILAMENT_MOTION_SENSOR)
#if HAS_FILAMENT_MOTION

/**
* This sensor uses a magnetic encoder disc and a Hall effect
Expand All @@ -256,14 +248,14 @@ class FilamentSensorBase {

static void poll_motion_sensor() {
static uint8_t old_state;
const uint8_t new_state = poll_runout_pins(),
const uint8_t new_state = TERN(FILAMENT_SWITCH_AND_MOTION, poll_motion_pins, poll_runout_pins)(),
change = old_state ^ new_state;
old_state = new_state;

#if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG)
if (change) {
SERIAL_ECHOPGM("Motion detected:");
LOOP_L_N(e, NUM_RUNOUT_SENSORS)
LOOP_L_N(e, TERN(FILAMENT_SWITCH_AND_MOTION, NUM_MOTION_SENSORS, NUM_RUNOUT_SENSORS))
if (TEST(change, e)) SERIAL_CHAR(' ', '0' + e);
SERIAL_EOL();
}
Expand All @@ -277,7 +269,7 @@ class FilamentSensorBase {
// If the sensor wheel has moved since the last call to
// this method reset the runout counter for the extruder.
if (TEST(motion_detected, b->extruder))
filament_present(b->extruder);
TERN(FILAMENT_SWITCH_AND_MOTION, filament_motion_present, filament_present)(b->extruder);

// Clear motion triggers for next block
motion_detected = 0;
Expand All @@ -286,7 +278,9 @@ class FilamentSensorBase {
static void run() { poll_motion_sensor(); }
};

#else
#endif // HAS_FILAMENT_MOTION

#if HAS_FILAMENT_SWITCH

/**
* This is a simple endstop switch in the path of the filament.
Expand Down Expand Up @@ -324,25 +318,56 @@ class FilamentSensorBase {
}
};

#endif // HAS_FILAMENT_SWITCH

/**
* This is a simple endstop switch in the path of the filament.
* It can detect filament runout, but not stripouts or jams.
*/
class FilamentSensor : public FilamentSensorBase {
private:
TERN_(HAS_FILAMENT_MOTION, static FilamentSensorEncoder encoder_sensor);
TERN_(HAS_FILAMENT_SWITCH, static FilamentSensorSwitch switch_sensor);

public:
static void block_completed(const block_t * const b) {
TERN_(HAS_FILAMENT_MOTION, encoder_sensor.block_completed(b));
TERN_(HAS_FILAMENT_SWITCH, switch_sensor.block_completed(b));
}

static void run() {
TERN_(HAS_FILAMENT_MOTION, encoder_sensor.run());
TERN_(HAS_FILAMENT_SWITCH, switch_sensor.run());
}
};

#endif // !FILAMENT_MOTION_SENSOR

/********************************* RESPONSE TYPE *********************************/

#if HAS_FILAMENT_RUNOUT_DISTANCE

typedef struct {
float runout[NUM_RUNOUT_SENSORS];
#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
float motion[NUM_MOTION_SENSORS];
#endif
} countdown_t;

// RunoutResponseDelayed triggers a runout event only if the length
// of filament specified by FILAMENT_RUNOUT_DISTANCE_MM has been fed
// during a runout condition.
class RunoutResponseDelayed {
private:
static volatile float runout_mm_countdown[NUM_RUNOUT_SENSORS];
static volatile countdown_t mm_countdown;

public:
static float runout_distance_mm;

static void reset() {
LOOP_L_N(i, NUM_RUNOUT_SENSORS) filament_present(i);
#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
LOOP_L_N(i, NUM_MOTION_SENSORS) filament_motion_present(i);
#endif
}

static void run() {
Expand All @@ -352,28 +377,45 @@ class FilamentSensorBase {
if (ELAPSED(ms, t)) {
t = millis() + 1000UL;
LOOP_L_N(i, NUM_RUNOUT_SENSORS)
SERIAL_ECHOF(i ? F(", ") : F("Remaining mm: "), runout_mm_countdown[i]);
SERIAL_ECHOF(i ? F(", ") : F("Runout remaining mm: "), mm_countdown.runout[i]);
#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
LOOP_L_N(i, NUM_MOTION_SENSORS)
SERIAL_ECHOF(i ? F(", ") : F("Motion remaining mm: "), mm_countdown.motion[i]);
#endif
SERIAL_EOL();
}
#endif
}

static 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);
LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (mm_countdown.runout[i] < 0) SBI(runout_flags, i);
#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
LOOP_L_N(i, NUM_MOTION_SENSORS) if (mm_countdown.motion[i] < 0) SBI(runout_flags, i);
#endif
return runout_flags;
}

static void filament_present(const uint8_t extruder) {
runout_mm_countdown[extruder] = runout_distance_mm;
mm_countdown.runout[extruder] = runout_distance_mm;
}

#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
static void filament_motion_present(const uint8_t extruder) {
mm_countdown.motion[extruder] = runout_distance_mm;
}
#endif

static void block_completed(const block_t * const b) {
if (b->steps.x || b->steps.y || b->steps.z || did_pause_print) { // Allow pause purge move to re-trigger runout state
// Only trigger on extrusion with XYZ movement to allow filament change and retract/recover.
const uint8_t e = b->extruder;
const int32_t steps = b->steps.e;
runout_mm_countdown[e] -= (TEST(b->direction_bits, E_AXIS) ? -steps : steps) * planner.mm_per_step[E_AXIS_N(e)];
const float mm = (TEST(b->direction_bits, E_AXIS) ? -steps : steps) * planner.mm_per_step[E_AXIS_N(e)];
if (e < NUM_RUNOUT_SENSORS) mm_countdown.runout[e] -= mm;
#if ENABLED(FILAMENT_SWITCH_AND_MOTION)
if (e < NUM_MOTION_SENSORS) mm_countdown.motion[e] -= mm;
#endif
}
}
};
Expand Down
Loading