SMUFF (MMU2 clone) support (#19912)

This commit is contained in:
Giuliano Zaro
2020-11-18 08:27:21 +01:00
committed by GitHub
parent 11b811820f
commit 41529b6598
29 changed files with 622 additions and 616 deletions

View File

@ -22,7 +22,7 @@
#include "../inc/MarlinConfig.h"
#if ENABLED(MK2_MULTIPLEXER)
#if HAS_PRUSA_MMU1
#include "../module/stepper.h"
@ -35,4 +35,4 @@ void select_multiplexed_stepper(const uint8_t e) {
safe_delay(100);
}
#endif // MK2_MULTIPLEXER
#endif // HAS_PRUSA_MMU1

View File

@ -22,7 +22,7 @@
#include "../../inc/MarlinConfig.h"
#if ENABLED(PRUSA_MMU2)
#if HAS_PRUSA_MMU2
#include "mmu2.h"
#include "../../lcd/menu/menu_mmu2.h"
@ -94,7 +94,7 @@ MMU2 mmu2;
#define mmuSerial MMU2_SERIAL
bool MMU2::enabled, MMU2::ready, MMU2::mmu_print_saved;
#if ENABLED(PRUSA_MMU2_S_MODE)
#if HAS_PRUSA_MMU2S
bool MMU2::mmu2s_triggered;
#endif
uint8_t MMU2::cmd, MMU2::cmd_arg, MMU2::last_cmd, MMU2::extruder;
@ -105,23 +105,19 @@ int16_t MMU2::version = -1, MMU2::buildnr = -1;
millis_t MMU2::prev_request, MMU2::prev_P0_request;
char MMU2::rx_buffer[MMU_RX_SIZE], MMU2::tx_buffer[MMU_TX_SIZE];
#if BOTH(HAS_LCD_MENU, MMU2_MENUS)
struct E_Step {
float extrude; //!< extrude distance in mm
feedRate_t feedRate; //!< feed rate in mm/s
};
struct E_Step {
float extrude; //!< extrude distance in mm
feedRate_t feedRate; //!< feed rate in mm/s
};
static constexpr E_Step
ramming_sequence[] PROGMEM = { MMU2_RAMMING_SEQUENCE }
, load_to_nozzle_sequence[] PROGMEM = { MMU2_LOAD_TO_NOZZLE_SEQUENCE }
#if ENABLED(PRUSA_MMU2_S_MODE)
, can_load_sequence[] PROGMEM = { MMU2_CAN_LOAD_SEQUENCE }
, can_load_increment_sequence[] PROGMEM = { MMU2_CAN_LOAD_INCREMENT_SEQUENCE }
#endif
;
#endif // MMU2_MENUS
static constexpr E_Step
ramming_sequence[] PROGMEM = { MMU2_RAMMING_SEQUENCE }
, load_to_nozzle_sequence[] PROGMEM = { MMU2_LOAD_TO_NOZZLE_SEQUENCE }
#if HAS_PRUSA_MMU2S
, can_load_sequence[] PROGMEM = { MMU2_CAN_LOAD_SEQUENCE }
, can_load_increment_sequence[] PROGMEM = { MMU2_CAN_LOAD_INCREMENT_SEQUENCE }
#endif
;
MMU2::MMU2() {
rx_buffer[0] = '\0';
@ -162,7 +158,7 @@ uint8_t MMU2::get_current_tool() {
return extruder == MMU2_NO_TOOL ? -1 : extruder;
}
#if EITHER(PRUSA_MMU2_S_MODE, MMU_EXTRUDER_SENSOR)
#if EITHER(HAS_PRUSA_MMU2S, MMU_EXTRUDER_SENSOR)
#define FILAMENT_PRESENT() (READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE)
#endif
@ -188,7 +184,7 @@ void MMU2::mmu_loop() {
case -2:
if (rx_ok()) {
sscanf(rx_buffer, "%uok\n", &version);
sscanf(rx_buffer, "%huok\n", &version);
DEBUG_ECHOLNPAIR("MMU => ", version, "\nMMU <= 'S2'");
@ -199,7 +195,7 @@ void MMU2::mmu_loop() {
case -3:
if (rx_ok()) {
sscanf(rx_buffer, "%uok\n", &buildnr);
sscanf(rx_buffer, "%huok\n", &buildnr);
DEBUG_ECHOLNPAIR("MMU => ", buildnr);
@ -242,7 +238,7 @@ void MMU2::mmu_loop() {
enabled = true;
state = 1;
TERN_(PRUSA_MMU2_S_MODE, mmu2s_triggered = false);
TERN_(HAS_PRUSA_MMU2S, mmu2s_triggered = false);
}
break;
@ -307,7 +303,7 @@ void MMU2::mmu_loop() {
state = 2; // wait for response
}
TERN_(PRUSA_MMU2_S_MODE, check_filament());
TERN_(HAS_PRUSA_MMU2S, check_filament());
break;
case 2: // response to command P0
@ -324,7 +320,7 @@ void MMU2::mmu_loop() {
else if (ELAPSED(millis(), prev_request + MMU_P0_TIMEOUT)) // Resend request after timeout (3s)
state = 1;
TERN_(PRUSA_MMU2_S_MODE, check_filament());
TERN_(HAS_PRUSA_MMU2S, check_filament());
break;
case 3: // response to mmu commands
@ -340,9 +336,9 @@ void MMU2::mmu_loop() {
#endif
if (rx_ok()) {
// Response to C0 mmu command in PRUSA_MMU2_S_MODE
// Response to C0 mmu command in MMU2S model
bool can_reset = true;
#if ENABLED(PRUSA_MMU2_S_MODE)
#if HAS_PRUSA_MMU2S
if (!mmu2s_triggered && last_cmd == MMU_CMD_C0) {
can_reset = false;
// MMU ok received but filament sensor not triggered, retrying...
@ -367,7 +363,7 @@ void MMU2::mmu_loop() {
}
state = 1;
}
TERN_(PRUSA_MMU2_S_MODE, check_filament());
TERN_(HAS_PRUSA_MMU2S, check_filament());
break;
}
}
@ -487,7 +483,7 @@ static void mmu2_not_responding() {
BUZZ(100, 659);
}
#if ENABLED(PRUSA_MMU2_S_MODE)
#if HAS_PRUSA_MMU2S
bool MMU2::load_to_gears() {
command(MMU_CMD_C0);
@ -541,33 +537,38 @@ static void mmu2_not_responding() {
* Tc Load to nozzle after filament was prepared by Tx and extruder nozzle is already heated.
*/
void MMU2::tool_change(const char* special) {
if (!enabled) return;
#if ENABLED(MMU2_MENUS)
if (!enabled) return;
set_runout_valid(false);
switch (*special) {
case '?': {
uint8_t index = mmu2_choose_filament();
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
load_filament_to_nozzle(index);
#if ENABLED(MMU2_MENUS)
const uint8_t index = mmu2_choose_filament();
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
load_filament_to_nozzle(index);
#else
BUZZ(400, 40);
#endif
} break;
case 'x': {
planner.synchronize();
uint8_t index = mmu2_choose_filament();
DISABLE_AXIS_E0();
command(MMU_CMD_T0 + index);
manage_response(true, true);
#if ENABLED(MMU2_MENUS)
planner.synchronize();
const uint8_t index = mmu2_choose_filament();
DISABLE_AXIS_E0();
command(MMU_CMD_T0 + index);
manage_response(true, true);
if (load_to_gears()) {
mmu_loop();
ENABLE_AXIS_E0();
extruder = index;
active_extruder = 0;
}
if (load_to_gears()) {
mmu_loop();
ENABLE_AXIS_E0();
extruder = index;
active_extruder = 0;
}
#else
BUZZ(400, 40);
#endif
} break;
case 'c': {
@ -577,8 +578,6 @@ static void mmu2_not_responding() {
}
set_runout_valid(true);
#endif // MMU2_MENUS
}
#elif ENABLED(MMU_EXTRUDER_SENSOR)
@ -628,20 +627,23 @@ static void mmu2_not_responding() {
void MMU2::tool_change(const char* special) {
if (!enabled) return;
#if ENABLED(MMU2_MENUS)
set_runout_valid(false);
set_runout_valid(false);
switch (*special) {
case '?': {
DEBUG_ECHOLNPGM("case ?\n");
switch (*special) {
case '?': {
DEBUG_ECHOLNPGM("case ?\n");
#if ENABLED(MMU2_MENUS)
uint8_t index = mmu2_choose_filament();
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
load_filament_to_nozzle(index);
} break;
#else
BUZZ(400, 40);
#endif
} break;
case 'x': {
DEBUG_ECHOLNPGM("case x\n");
case 'x': {
DEBUG_ECHOLNPGM("case x\n");
#if ENABLED(MMU2_MENUS)
planner.synchronize();
uint8_t index = mmu2_choose_filament();
DISABLE_AXIS_E0();
@ -654,18 +656,19 @@ static void mmu2_not_responding() {
ENABLE_AXIS_E0();
extruder = index;
active_extruder = 0;
} break;
#else
BUZZ(400, 40);
#endif
} break;
case 'c': {
DEBUG_ECHOLNPGM("case c\n");
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
execute_extruder_sequence((const E_Step *)load_to_nozzle_sequence, COUNT(load_to_nozzle_sequence));
} break;
}
case 'c': {
DEBUG_ECHOLNPGM("case c\n");
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
execute_extruder_sequence((const E_Step *)load_to_nozzle_sequence, COUNT(load_to_nozzle_sequence));
} break;
}
set_runout_valid(true);
#endif // MMU2_MENUS
set_runout_valid(true);
}
void MMU2::mmu_continue_loading() {
@ -682,68 +685,74 @@ static void mmu2_not_responding() {
mmu_idl_sens = 0;
}
#elif DISABLED(MMU_EXTRUDER_SENSOR) && DISABLED(PRUSA_MMU2_S_MODE)
#else // !HAS_PRUSA_MMU2S && !MMU_EXTRUDER_SENSOR
/**
* Handle tool change
*/
void MMU2::tool_change(const uint8_t index) {
if (!enabled) return;
/**
* Handle tool change
*/
void MMU2::tool_change(const uint8_t index) {
if (!enabled) return;
set_runout_valid(false);
set_runout_valid(false);
if (index != extruder) {
DISABLE_AXIS_E0();
ui.status_printf_P(0, GET_TEXT(MSG_MMU2_LOADING_FILAMENT), int(index + 1));
command(MMU_CMD_T0 + index);
manage_response(true, true);
command(MMU_CMD_C0);
extruder = index; //filament change is finished
active_extruder = 0;
ENABLE_AXIS_E0();
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(STR_ACTIVE_EXTRUDER, int(extruder));
ui.reset_status();
if (index != extruder) {
DISABLE_AXIS_E0();
ui.status_printf_P(0, GET_TEXT(MSG_MMU2_LOADING_FILAMENT), int(index + 1));
command(MMU_CMD_T0 + index);
manage_response(true, true);
command(MMU_CMD_C0);
extruder = index; //filament change is finished
active_extruder = 0;
ENABLE_AXIS_E0();
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(STR_ACTIVE_EXTRUDER, int(extruder));
ui.reset_status();
}
set_runout_valid(true);
}
set_runout_valid(true);
}
/**
* Handle special T?/Tx/Tc commands
*
* T? Gcode to extrude shouldn't have to follow, load to extruder wheels is done automatically
* Tx Same as T?, except nozzle doesn't have to be preheated. Tc must be placed after extruder nozzle is preheated to finish filament load.
* Tc Load to nozzle after filament was prepared by Tx and extruder nozzle is already heated.
*/
void MMU2::tool_change(const char* special) {
if (!enabled) return;
#if ENABLED(MMU2_MENUS)
/**
* Handle special T?/Tx/Tc commands
*
* T? Gcode to extrude shouldn't have to follow, load to extruder wheels is done automatically
* Tx Same as T?, except nozzle doesn't have to be preheated. Tc must be placed after extruder nozzle is preheated to finish filament load.
* Tc Load to nozzle after filament was prepared by Tx and extruder nozzle is already heated.
*/
void MMU2::tool_change(const char* special) {
if (!enabled) return;
set_runout_valid(false);
switch (*special) {
case '?': {
DEBUG_ECHOLNPGM("case ?\n");
uint8_t index = mmu2_choose_filament();
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
load_filament_to_nozzle(index);
#if ENABLED(MMU2_MENUS)
uint8_t index = mmu2_choose_filament();
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
load_filament_to_nozzle(index);
#else
BUZZ(400, 40);
#endif
} break;
case 'x': {
DEBUG_ECHOLNPGM("case x\n");
planner.synchronize();
uint8_t index = mmu2_choose_filament();
DISABLE_AXIS_E0();
command(MMU_CMD_T0 + index);
manage_response(true, true);
command(MMU_CMD_C0);
mmu_loop();
#if ENABLED(MMU2_MENUS)
planner.synchronize();
uint8_t index = mmu2_choose_filament();
DISABLE_AXIS_E0();
command(MMU_CMD_T0 + index);
manage_response(true, true);
command(MMU_CMD_C0);
mmu_loop();
ENABLE_AXIS_E0();
extruder = index;
active_extruder = 0;
ENABLE_AXIS_E0();
extruder = index;
active_extruder = 0;
#else
BUZZ(400, 40);
#endif
} break;
case 'c': {
@ -754,11 +763,9 @@ void MMU2::tool_change(const char* special) {
}
set_runout_valid(true);
#endif
}
#endif // MMU_EXTRUDER_SENSOR
#endif // HAS_PRUSA_MMU2S
/**
* Set next command
@ -866,7 +873,7 @@ void MMU2::filament_runout() {
planner.synchronize();
}
#if ENABLED(PRUSA_MMU2_S_MODE)
#if HAS_PRUSA_MMU2S
void MMU2::check_filament() {
const bool present = FILAMENT_PRESENT();
@ -907,162 +914,159 @@ void MMU2::filament_runout() {
DEBUG_ECHOLNPGM(" succeeded.");
return true;
}
#endif
#if BOTH(HAS_LCD_MENU, MMU2_MENUS)
// Load filament into MMU2
void MMU2::load_filament(const uint8_t index) {
if (!enabled) return;
command(MMU_CMD_L0 + index);
manage_response(false, false);
BUZZ(200, 404);
}
// Load filament into MMU2
void MMU2::load_filament(const uint8_t index) {
if (!enabled) return;
command(MMU_CMD_L0 + index);
/**
* Switch material and load to nozzle
*/
bool MMU2::load_filament_to_nozzle(const uint8_t index) {
if (!enabled) return false;
if (thermalManager.tooColdToExtrude(active_extruder)) {
BUZZ(200, 404);
LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD);
return false;
}
command(MMU_CMD_T0 + index);
manage_response(true, true);
const bool success = load_to_gears();
if (success) {
mmu_loop();
extruder = index;
active_extruder = 0;
load_to_nozzle();
BUZZ(200, 404);
}
return success;
}
/**
* Load filament to nozzle of multimaterial printer
*
* This function is used only only after T? (user select filament) and M600 (change filament).
* It is not used after T0 .. T4 command (select filament), in such case, gcode is responsible for loading
* filament to nozzle.
*/
void MMU2::load_to_nozzle() {
if (!enabled) return;
execute_extruder_sequence((const E_Step *)load_to_nozzle_sequence, COUNT(load_to_nozzle_sequence));
}
bool MMU2::eject_filament(const uint8_t index, const bool recover) {
if (!enabled) return false;
if (thermalManager.tooColdToExtrude(active_extruder)) {
BUZZ(200, 404);
LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD);
return false;
}
LCD_MESSAGEPGM(MSG_MMU2_EJECTING_FILAMENT);
ENABLE_AXIS_E0();
current_position.e -= MMU2_FILAMENTCHANGE_EJECT_FEED;
line_to_current_position(MMM_TO_MMS(2500));
planner.synchronize();
command(MMU_CMD_E0 + index);
manage_response(false, false);
if (recover) {
LCD_MESSAGEPGM(MSG_MMU2_EJECT_RECOVER);
BUZZ(200, 404);
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, PSTR("MMU2 Eject Recover"), CONTINUE_STR));
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("MMU2 Eject Recover")));
wait_for_user_response();
BUZZ(200, 404);
BUZZ(200, 404);
command(MMU_CMD_R0);
manage_response(false, false);
}
ui.reset_status();
// no active tool
extruder = MMU2_NO_TOOL;
set_runout_valid(false);
BUZZ(200, 404);
DISABLE_AXIS_E0();
return true;
}
/**
* Unload from hotend and retract to MMU
*/
bool MMU2::unload() {
if (!enabled) return false;
if (thermalManager.tooColdToExtrude(active_extruder)) {
BUZZ(200, 404);
LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD);
return false;
}
/**
* Switch material and load to nozzle
*/
bool MMU2::load_filament_to_nozzle(const uint8_t index) {
filament_ramming();
if (!enabled) return false;
command(MMU_CMD_U0);
manage_response(false, true);
if (thermalManager.tooColdToExtrude(active_extruder)) {
BUZZ(200, 404);
LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD);
return false;
}
BUZZ(200, 404);
command(MMU_CMD_T0 + index);
manage_response(true, true);
// no active tool
extruder = MMU2_NO_TOOL;
const bool success = load_to_gears();
if (success) {
mmu_loop();
extruder = index;
active_extruder = 0;
load_to_nozzle();
BUZZ(200, 404);
}
return success;
}
set_runout_valid(false);
/**
* Load filament to nozzle of multimaterial printer
*
* This function is used only only after T? (user select filament) and M600 (change filament).
* It is not used after T0 .. T4 command (select filament), in such case, gcode is responsible for loading
* filament to nozzle.
*/
void MMU2::load_to_nozzle() {
if (!enabled) return;
execute_extruder_sequence((const E_Step *)load_to_nozzle_sequence, COUNT(load_to_nozzle_sequence));
}
return true;
}
bool MMU2::eject_filament(const uint8_t index, const bool recover) {
/**
* Unload sequence to optimize shape of the tip of the unloaded filament
*/
void MMU2::filament_ramming() {
execute_extruder_sequence((const E_Step *)ramming_sequence, sizeof(ramming_sequence) / sizeof(E_Step));
}
if (!enabled) return false;
void MMU2::execute_extruder_sequence(const E_Step * sequence, int steps) {
if (thermalManager.tooColdToExtrude(active_extruder)) {
BUZZ(200, 404);
LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD);
return false;
}
planner.synchronize();
ENABLE_AXIS_E0();
LCD_MESSAGEPGM(MSG_MMU2_EJECTING_FILAMENT);
const E_Step* step = sequence;
ENABLE_AXIS_E0();
current_position.e -= MMU2_FILAMENTCHANGE_EJECT_FEED;
line_to_current_position(2500 / 60);
LOOP_L_N(i, steps) {
const float es = pgm_read_float(&(step->extrude));
const feedRate_t fr_mm_m = pgm_read_float(&(step->feedRate));
DEBUG_ECHO_START();
DEBUG_ECHOLNPAIR("E step ", es, "/", fr_mm_m);
current_position.e += es;
line_to_current_position(MMM_TO_MMS(fr_mm_m));
planner.synchronize();
command(MMU_CMD_E0 + index);
manage_response(false, false);
if (recover) {
LCD_MESSAGEPGM(MSG_MMU2_EJECT_RECOVER);
BUZZ(200, 404);
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, PSTR("MMU2 Eject Recover"), CONTINUE_STR));
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("MMU2 Eject Recover")));
wait_for_user_response();
BUZZ(200, 404);
BUZZ(200, 404);
command(MMU_CMD_R0);
manage_response(false, false);
}
ui.reset_status();
// no active tool
extruder = MMU2_NO_TOOL;
set_runout_valid(false);
BUZZ(200, 404);
DISABLE_AXIS_E0();
return true;
step++;
}
/**
* Unload from hotend and retract to MMU
*/
bool MMU2::unload() {
DISABLE_AXIS_E0();
}
if (!enabled) return false;
if (thermalManager.tooColdToExtrude(active_extruder)) {
BUZZ(200, 404);
LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD);
return false;
}
filament_ramming();
command(MMU_CMD_U0);
manage_response(false, true);
BUZZ(200, 404);
// no active tool
extruder = MMU2_NO_TOOL;
set_runout_valid(false);
return true;
}
/**
* Unload sequence to optimize shape of the tip of the unloaded filament
*/
void MMU2::filament_ramming() {
execute_extruder_sequence((const E_Step *)ramming_sequence, sizeof(ramming_sequence) / sizeof(E_Step));
}
void MMU2::execute_extruder_sequence(const E_Step * sequence, int steps) {
planner.synchronize();
ENABLE_AXIS_E0();
const E_Step* step = sequence;
LOOP_L_N(i, steps) {
const float es = pgm_read_float(&(step->extrude));
const feedRate_t fr_mm_m = pgm_read_float(&(step->feedRate));
DEBUG_ECHO_START();
DEBUG_ECHOLNPAIR("E step ", es, "/", fr_mm_m);
current_position.e += es;
line_to_current_position(MMM_TO_MMS(fr_mm_m));
planner.synchronize();
step++;
}
DISABLE_AXIS_E0();
}
#endif // HAS_LCD_MENU && MMU2_MENUS
#endif // PRUSA_MMU2
#endif // HAS_PRUSA_MMU2

View File

@ -49,13 +49,11 @@ public:
static uint8_t get_current_tool();
static void set_filament_type(const uint8_t index, const uint8_t type);
#if BOTH(HAS_LCD_MENU, MMU2_MENUS)
static bool unload();
static void load_filament(uint8_t);
static void load_all();
static bool load_filament_to_nozzle(const uint8_t index);
static bool eject_filament(const uint8_t index, const bool recover);
#endif
static bool unload();
static void load_filament(uint8_t);
static void load_all();
static bool load_filament_to_nozzle(const uint8_t index);
static bool eject_filament(const uint8_t index, const bool recover);
private:
static bool rx_str_P(const char* str);
@ -72,15 +70,13 @@ private:
static bool get_response();
static void manage_response(const bool move_axes, const bool turn_off_nozzle);
#if BOTH(HAS_LCD_MENU, MMU2_MENUS)
static void load_to_nozzle();
static void filament_ramming();
static void execute_extruder_sequence(const E_Step * sequence, int steps);
#endif
static void load_to_nozzle();
static void filament_ramming();
static void execute_extruder_sequence(const E_Step * sequence, int steps);
static void filament_runout();
#if ENABLED(PRUSA_MMU2_S_MODE)
#if HAS_PRUSA_MMU2S
static bool mmu2s_triggered;
static void check_filament();
static bool can_load();