diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index dc96673c6..49e20b69d 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -129,6 +129,7 @@ #include "sound.h" #include "cmdqueue.h" +#include "io_atmega2560.h" // Macros for bit masks #define BIT(b) (1<<(b)) @@ -641,6 +642,8 @@ void failstats_reset_print() eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_Y, 0); eeprom_update_byte((uint8_t *)EEPROM_FERROR_COUNT, 0); eeprom_update_byte((uint8_t *)EEPROM_POWER_COUNT, 0); + eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0); + eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0); } @@ -687,6 +690,12 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE)) eeprom_update_word((uint16_t *)EEPROM_FERROR_COUNT_TOT, 0); eeprom_update_word((uint16_t *)EEPROM_POWER_COUNT_TOT, 0); + eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0); + eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0); + eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0); + eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0); + + lcd_menu_statistics(); break; @@ -713,6 +722,11 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE)) eeprom_update_word((uint16_t *)EEPROM_FERROR_COUNT_TOT, 0); eeprom_update_word((uint16_t *)EEPROM_POWER_COUNT_TOT, 0); + eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0); + eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0); + eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0); + eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0); + #ifdef FILAMENT_SENSOR fsensor_enable(); fsensor_autoload_set(true); @@ -1387,6 +1401,12 @@ void setup() if (eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_X_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_CRASH_COUNT_X_TOT, 0); if (eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_Y_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_CRASH_COUNT_Y_TOT, 0); if (eeprom_read_word((uint16_t*)EEPROM_FERROR_COUNT_TOT) == 0xffff) eeprom_write_word((uint16_t*)EEPROM_FERROR_COUNT_TOT, 0); + + if (eeprom_read_word((uint16_t*)EEPROM_MMU_FAIL_TOT) == 0xffff) eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0); + if (eeprom_read_word((uint16_t*)EEPROM_MMU_LOAD_FAIL_TOT) == 0xffff) eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0); + if (eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL) == 0xff) eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0); + if (eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL) == 0xff) eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0); + #ifdef SNMM if (eeprom_read_dword((uint32_t*)EEPROM_BOWDEN_LENGTH) == 0x0ffffffff) { //bowden length used for SNMM int _z = BOWDEN_LENGTH; @@ -6892,16 +6912,20 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE)) if (mmu_enabled) { tmp_extruder = choose_menu_P(_T(MSG_CHOOSE_FILAMENT), _T(MSG_FILAMENT)); + if ((tmp_extruder == mmu_extruder) && mmu_fil_loaded) { + printf_P(PSTR("Duplicit T-code ignored.\n")); + return; //dont execute the same T-code twice in a row + } st_synchronize(); mmu_command(MMU_CMD_T0 + tmp_extruder); - manage_response(true, true); + manage_response(true, true, MMU_TCODE_MOVE); } } else if (*(strchr_pointer + index) == 'c') { //load to from bondtech gears to nozzle (nozzle should be preheated) if (mmu_enabled) { st_synchronize(); - mmu_command(MMU_CMD_C0); + mmu_continue_loading(); mmu_extruder = tmp_extruder; //filament change is finished mmu_load_to_nozzle(); } @@ -6926,11 +6950,15 @@ if((eSoundMode==e_SOUND_MODE_LOUD)||(eSoundMode==e_SOUND_MODE_ONCE)) if (mmu_enabled) { + if ((tmp_extruder == mmu_extruder) && mmu_fil_loaded) { + printf_P(PSTR("Duplicit T-code ignored.\n")); + return; //dont execute the same T-code twice in a row + } mmu_command(MMU_CMD_T0 + tmp_extruder); - manage_response(true, true); - mmu_command(MMU_CMD_C0); - mmu_extruder = tmp_extruder; //filament change is finished + manage_response(true, true, MMU_TCODE_MOVE); + mmu_continue_loading(); + mmu_extruder = tmp_extruder; //filament change is finished if (load_to_nozzle)// for single material usage with mmu { diff --git a/Firmware/eeprom.h b/Firmware/eeprom.h index da003990f..9f878796e 100644 --- a/Firmware/eeprom.h +++ b/Firmware/eeprom.h @@ -148,6 +148,12 @@ #define EEPROM_FSENS_OQ_MEASS_ENABLED (EEPROM_AUTO_DEPLETE - 1) //bool +#define EEPROM_MMU_FAIL_TOT (EEPROM_FSENS_OQ_MEASS_ENABLED - 2) //uint16_t +#define EEPROM_MMU_FAIL (EEPROM_MMU_FAIL_TOT - 1) //uint8_t + +#define EEPROM_MMU_LOAD_FAIL_TOT (EEPROM_MMU_FAIL - 2) //uint16_t +#define EEPROM_MMU_LOAD_FAIL (EEPROM_MMU_LOAD_FAIL_TOT - 1) //uint8_t + // !!!!! // !!!!! this is end of EEPROM section ... all updates MUST BE inserted before this mark !!!!! // !!!!! diff --git a/Firmware/mmu.cpp b/Firmware/mmu.cpp index d8e324581..6433eafb5 100644 --- a/Firmware/mmu.cpp +++ b/Firmware/mmu.cpp @@ -13,6 +13,7 @@ #include "sound.h" #include "printers.h" #include +#include "io_atmega2560.h" #ifdef TMC2130 #include "tmc2130.h" @@ -22,25 +23,31 @@ #define MMU_TODELAY 100 #define MMU_TIMEOUT 10 -#define MMU_CMD_TIMEOUT 300000ul //5min timeout for mmu commands (except P0) +#define MMU_CMD_TIMEOUT 45000ul //5min timeout for mmu commands (except P0) #define MMU_P0_TIMEOUT 3000ul //timeout for P0 command: 3seconds +#define MMU_MAX_RESEND_ATTEMPTS 2 #ifdef MMU_HWRESET #define MMU_RST_PIN 76 #endif //MMU_HWRESET bool mmu_enabled = false; - bool mmu_ready = false; +bool mmu_fil_loaded = false; //if true: blocks execution of duplicit T-codes static int8_t mmu_state = 0; uint8_t mmu_cmd = 0; -uint8_t mmu_extruder = 0; +//idler ir sensor +uint8_t mmu_idl_sens = 0; +bool mmu_idler_sensor_detected = false; +bool mmu_loading_flag = false; + +uint8_t mmu_extruder = MMU_FILAMENT_UNKNOWN; //! This variable probably has no meaning and is planed to be removed -uint8_t tmp_extruder = 0; +uint8_t tmp_extruder = MMU_FILAMENT_UNKNOWN; int8_t mmu_finda = -1; @@ -52,6 +59,7 @@ uint32_t mmu_last_request = 0; uint32_t mmu_last_response = 0; uint8_t mmu_last_cmd = 0; +uint16_t mmu_power_failures = 0; //clear rx buffer @@ -108,11 +116,31 @@ void mmu_init(void) _delay_ms(10); //wait 10ms for sure mmu_reset(); //reset mmu (HW or SW), do not wait for response mmu_state = -1; + PIN_INP(MMU_IDLER_SENSOR_PIN); //input mode + PIN_SET(MMU_IDLER_SENSOR_PIN); //pullup +} + +//returns true if idler IR sensor was detected, otherwise returns false +bool check_for_idler_sensor() +{ + bool detected = false; + //if MMU_IDLER_SENSOR_PIN input is low and pat9125sensor is not present we detected idler sensor + if ((PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) && fsensor_not_responding) + { + detected = true; + //printf_P(PSTR("Idler IR sensor detected\n")); + } + else + { + //printf_P(PSTR("Idler IR sensor not detected\n")); + } + return detected; } //mmu main loop - state machine processing void mmu_loop(void) { + static uint8_t mmu_attempt_nr = 0; int filament = 0; // printf_P(PSTR("MMU loop, state=%d\n"), mmu_state); switch (mmu_state) @@ -160,9 +188,9 @@ void mmu_loop(void) if ((PRINTER_TYPE == PRINTER_MK3) || (PRINTER_TYPE == PRINTER_MK3_SNMM)) { -#ifdef MMU_DEBUG +#if defined MMU_DEBUG && defined MMU_FINDA_DEBUG puts_P(PSTR("MMU <= 'P0'")); -#endif //MMU_DEBUG +#endif //MMU_DEBUG && MMU_FINDA_DEBUG mmu_puts_P(PSTR("P0\n")); //send 'read finda' request mmu_state = -4; } @@ -180,9 +208,9 @@ void mmu_loop(void) case -5: if (mmu_rx_ok() > 0) { -#ifdef MMU_DEBUG +#if defined MMU_DEBUG && defined MMU_FINDA_DEBUG puts_P(PSTR("MMU <= 'P0'")); -#endif //MMU_DEBUG +#endif //MMU_DEBUG && MMU_FINDA_DEBUG mmu_puts_P(PSTR("P0\n")); //send 'read finda' request mmu_state = -4; } @@ -191,11 +219,13 @@ void mmu_loop(void) if (mmu_rx_ok() > 0) { fscanf_P(uart2io, PSTR("%hhu"), &mmu_finda); //scan finda from buffer -#ifdef MMU_DEBUG +#if defined MMU_DEBUG && defined MMU_FINDA_DEBUG printf_P(PSTR("MMU => '%dok'\n"), mmu_finda); -#endif //MMU_DEBUG +#endif //MMU_DEBUG && MMU_FINDA_DEBUG puts_P(PSTR("MMU - ENABLED")); mmu_enabled = true; + //if we have filament loaded into the nozzle, we can decide if printer has idler sensor right now; otherwise we will will wait till start of T-code so it will be detected on beginning of second T-code + if(check_for_idler_sensor()) mmu_idler_sensor_detected = true; mmu_state = 1; } return; @@ -210,6 +240,8 @@ void mmu_loop(void) #endif //MMU_DEBUG mmu_printf_P(PSTR("T%d\n"), filament); mmu_state = 3; // wait for response + mmu_fil_loaded = true; + if(mmu_idler_sensor_detected) mmu_idl_sens = 1; //if idler sensor detected, use it for T-code } else if ((mmu_cmd >= MMU_CMD_L0) && (mmu_cmd <= MMU_CMD_L4)) { @@ -227,6 +259,7 @@ void mmu_loop(void) #endif //MMU_DEBUG mmu_puts_P(PSTR("C0\n")); //send 'continue loading' mmu_state = 3; + if(mmu_idler_sensor_detected) mmu_idl_sens = 1; //if idler sensor detected use it for C0 code } else if (mmu_cmd == MMU_CMD_U0) { @@ -234,6 +267,7 @@ void mmu_loop(void) printf_P(PSTR("MMU <= 'U0'\n")); #endif //MMU_DEBUG mmu_puts_P(PSTR("U0\n")); //send 'unload current filament' + mmu_fil_loaded = false; mmu_state = 3; } else if ((mmu_cmd >= MMU_CMD_E0) && (mmu_cmd <= MMU_CMD_E4)) @@ -243,6 +277,7 @@ void mmu_loop(void) printf_P(PSTR("MMU <= 'E%d'\n"), filament); #endif //MMU_DEBUG mmu_printf_P(PSTR("E%d\n"), filament); //send eject filament + mmu_fil_loaded = false; mmu_state = 3; // wait for response } else if (mmu_cmd == MMU_CMD_R0) @@ -253,14 +288,23 @@ void mmu_loop(void) mmu_puts_P(PSTR("R0\n")); //send recover after eject mmu_state = 3; // wait for response } + else if (mmu_cmd == MMU_CMD_S3) + { +#ifdef MMU_DEBUG + printf_P(PSTR("MMU <= 'S3'\n")); +#endif //MMU_DEBUG + mmu_puts_P(PSTR("S3\n")); //send power failures request + mmu_state = 4; // power failures response + } mmu_last_cmd = mmu_cmd; mmu_cmd = 0; } else if ((mmu_last_response + 300) < millis()) //request every 300ms { -#ifdef MMU_DEBUG + if(check_for_idler_sensor()) mmu_idler_sensor_detected = true; +#if defined MMU_DEBUG && defined MMU_FINDA_DEBUG puts_P(PSTR("MMU <= 'P0'")); -#endif //MMU_DEBUG +#endif //MMU_DEBUG && MMU_FINDA_DEBUG mmu_puts_P(PSTR("P0\n")); //send 'read finda' request mmu_state = 2; } @@ -269,9 +313,9 @@ void mmu_loop(void) if (mmu_rx_ok() > 0) { fscanf_P(uart2io, PSTR("%hhu"), &mmu_finda); //scan finda from buffer -#ifdef MMU_DEBUG +#if defined MMU_DEBUG && MMU_FINDA_DEBUG printf_P(PSTR("MMU => '%dok'\n"), mmu_finda); -#endif //MMU_DEBUG +#endif //MMU_DEBUG && MMU_FINDA_DEBUG //printf_P(PSTR("Eact: %d\n"), int(e_active())); if (!mmu_finda && CHECK_FINDA && fsensor_enabled) { fsensor_stop_and_save_print(); @@ -289,10 +333,57 @@ void mmu_loop(void) } return; case 3: //response to mmu commands + if (mmu_idler_sensor_detected) { + if (mmu_idl_sens) + { + if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0 && mmu_loading_flag) + { +#ifdef MMU_DEBUG + printf_P(PSTR("MMU <= 'A'\n")); +#endif //MMU_DEBUG + mmu_puts_P(PSTR("A\n")); //send 'abort' request + mmu_idl_sens = 0; + //printf_P(PSTR("MMU IDLER_SENSOR = 0 - ABORT\n")); + } + //else + //printf_P(PSTR("MMU IDLER_SENSOR = 1 - WAIT\n")); + } + } if (mmu_rx_ok() > 0) { #ifdef MMU_DEBUG printf_P(PSTR("MMU => 'ok'\n")); +#endif //MMU_DEBUG + mmu_attempt_nr = 0; + mmu_last_cmd = 0; + mmu_ready = true; + mmu_state = 1; + } + else if ((mmu_last_request + MMU_CMD_TIMEOUT) < millis()) + { //resend request after timeout (5 min) + if (mmu_last_cmd) + { + if (mmu_attempt_nr++ < MMU_MAX_RESEND_ATTEMPTS) { +#ifdef MMU_DEBUG + printf_P(PSTR("MMU retry attempt nr. %d\n"), mmu_attempt_nr - 1); +#endif //MMU_DEBUG + mmu_cmd = mmu_last_cmd; + } + else { + mmu_cmd = 0; + mmu_last_cmd = 0; //check + mmu_attempt_nr = 0; + } + } + mmu_state = 1; + } + return; + case 4: + if (mmu_rx_ok() > 0) + { + fscanf_P(uart2io, PSTR("%d"), &mmu_power_failures); //scan power failures +#ifdef MMU_DEBUG + printf_P(PSTR("MMU => 'ok'\n")); #endif //MMU_DEBUG mmu_last_cmd = 0; mmu_ready = true; @@ -300,17 +391,8 @@ void mmu_loop(void) } else if ((mmu_last_request + MMU_CMD_TIMEOUT) < millis()) { //resend request after timeout (5 min) - if (mmu_last_cmd) - { -#ifdef MMU_DEBUG - printf_P(PSTR("MMU retry\n")); -#endif //MMU_DEBUG - mmu_cmd = mmu_last_cmd; -// mmu_last_cmd = 0; //resend just once - } mmu_state = 1; } - return; } } @@ -350,22 +432,85 @@ void mmu_command(uint8_t cmd) mmu_ready = false; } -bool mmu_get_response(void) +void mmu_load_step() { + current_position[E_AXIS] = current_position[E_AXIS] + MMU_LOAD_FEEDRATE * 0.1; + plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], MMU_LOAD_FEEDRATE, active_extruder); + st_synchronize(); +} + +bool mmu_get_response(uint8_t move) { -// printf_P(PSTR("mmu_get_response - begin\n")); + mmu_loading_flag = false; + if (!mmu_idler_sensor_detected) move = MMU_NO_MOVE; + + printf_P(PSTR("mmu_get_response - begin move:%d\n"), move); KEEPALIVE_STATE(IN_PROCESS); while (mmu_cmd != 0) { // mmu_loop(); delay_keep_alive(100); } + while (!mmu_ready) { // mmu_loop(); - if (mmu_state != 3) + + if ((mmu_state != 3) && (mmu_last_cmd == 0)) break; - delay_keep_alive(100); + + //Do load steps only if temperature is higher then min. temp for safe extrusion. + //Otherwise "cold extrusion prevented" would be send to serial line periodically + if (degHotend(active_extruder) < EXTRUDE_MINTEMP) { + disable_e0(); //turn off E-stepper to prevent overheating and alow filament pull-out if necessary + delay_keep_alive(100); + continue; + } + + switch (move) { + case MMU_LOAD_MOVE: + mmu_loading_flag = true; + mmu_load_step(); + //don't rely on "ok" signal from mmu unit; if filament detected by idler sensor during loading stop loading movements to prevent infinite loading + if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) move = MMU_NO_MOVE; + break; + case MMU_UNLOAD_MOVE: + if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) //filament is still detected by idler sensor, printer helps with unlading + { + printf_P(PSTR("Unload 1\n")); + current_position[E_AXIS] = current_position[E_AXIS] - MMU_LOAD_FEEDRATE * MMU_LOAD_TIME_MS*0.001; + plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], MMU_LOAD_FEEDRATE, active_extruder); + st_synchronize(); + } + else //filament was unloaded from idler, no additional movements needed + { + printf_P(PSTR("Unloading finished 1\n")); + disable_e0(); //turn off E-stepper to prevent overheating and alow filament pull-out if necessary + move = MMU_NO_MOVE; + } + break; + case MMU_TCODE_MOVE: //first do unload and then continue with infinite loading movements + if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) //filament detected by idler sensor, we must unload first + { + printf_P(PSTR("Unload 2\n")); + current_position[E_AXIS] = current_position[E_AXIS] - MMU_LOAD_FEEDRATE * MMU_LOAD_TIME_MS*0.001; + plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], MMU_LOAD_FEEDRATE, active_extruder); + st_synchronize(); + } + else //delay to allow mmu unit to pull out filament from bondtech gears and then start with infinite loading + { + printf_P(PSTR("Unloading finished 2\n")); + disable_e0(); //turn off E-stepper to prevent overheating and alow filament pull-out if necessary + delay_keep_alive(MMU_LOAD_TIME_MS); + move = MMU_LOAD_MOVE; + } + break; + case MMU_NO_MOVE: + default: + delay_keep_alive(100); + break; + } } + printf_P(PSTR("mmu_get_response() returning: %d\n"), mmu_ready); bool ret = mmu_ready; mmu_ready = false; // printf_P(PSTR("mmu_get_response - end %d\n"), ret?1:0); @@ -392,7 +537,7 @@ bool mmu_get_response(void) } -void manage_response(bool move_axes, bool turn_off_nozzle) +void manage_response(bool move_axes, bool turn_off_nozzle, uint8_t move) { bool response = false; mmu_print_saved = false; @@ -402,11 +547,18 @@ void manage_response(bool move_axes, bool turn_off_nozzle) float x_position_bckp = current_position[X_AXIS]; float y_position_bckp = current_position[Y_AXIS]; uint8_t screen = 0; //used for showing multiscreen messages + while(!response) { - response = mmu_get_response(); //wait for "ok" from mmu + response = mmu_get_response(move); //wait for "ok" from mmu if (!response) { //no "ok" was received in reserved time frame, user will fix the issue on mmu unit if (!mmu_print_saved) { //first occurence, we are saving current position, park print head in certain position and disable nozzle heater + + uint8_t mmu_fail = eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL); + uint16_t mmu_fail_tot = eeprom_read_word((uint16_t*)EEPROM_MMU_FAIL_TOT); + if(mmu_fail < 255) eeprom_update_byte((uint8_t*)EEPROM_MMU_FAIL, mmu_fail + 1); + if(mmu_fail_tot < 65535) eeprom_update_word((uint16_t*)EEPROM_MMU_FAIL_TOT, mmu_fail_tot + 1); + if (lcd_update_enabled) { lcd_update_was_enabled = true; lcd_update_enable(false); @@ -436,6 +588,7 @@ void manage_response(bool move_axes, bool turn_off_nozzle) //set nozzle target temperature to 0 setAllTargetHotends(0); } + disable_e0(); //turn off E-stepper to prevent overheating and alow filament pull-out if necessary } //first three lines are used for printing multiscreen message; last line contains measured and target nozzle temperature @@ -450,19 +603,21 @@ void manage_response(bool move_axes, bool turn_off_nozzle) } lcd_set_degree(); - lcd_set_cursor(0, 4); //line 4 - //Print the hotend temperature (9 chars total) and fill rest of the line with space - int chars = lcd_printf_P(_N("%c%3d/%d%c"), LCD_STR_THERMOMETER[0],(int)(degHotend(active_extruder) + 0.5), (int)(degTargetHotend(active_extruder) + 0.5), LCD_STR_DEGREE[0]); - lcd_space(9 - chars); //5 seconds delay - for (uint8_t i = 0; i < 50; i++) { + for (uint8_t i = 0; i < 5; i++) { if (lcd_clicked()) { setTargetHotend(hotend_temp_bckp, active_extruder); + /// mmu_cmd = mmu_last_cmd; break; - } - delay_keep_alive(100); + } + + //Print the hotend temperature (9 chars total) and fill rest of the line with space + lcd_set_cursor(0, 4); //line 4 + int chars = lcd_printf_P(_N("%c%3d/%d%c"), LCD_STR_THERMOMETER[0],(int)(degHotend(active_extruder) + 0.5), (int)(degTargetHotend(active_extruder) + 0.5), LCD_STR_DEGREE[0]); + lcd_space(9 - chars); + delay_keep_alive(1000); } } else if (mmu_print_saved) { @@ -519,7 +674,14 @@ void mmu_load_to_nozzle() bool saved_e_relative_mode = axis_relative_modes[E_AXIS]; if (!saved_e_relative_mode) axis_relative_modes[E_AXIS] = true; - current_position[E_AXIS] += 7.2f; + if (mmu_idler_sensor_detected) + { + current_position[E_AXIS] += 3.0f; + } + else + { + current_position[E_AXIS] += 7.2f; + } float feedrate = 562; plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], feedrate / 60, active_extruder); st_synchronize(); @@ -599,9 +761,10 @@ void mmu_M600_load_filament(bool automatic) // mmu_printf_P(PSTR("T%d\n"), tmp_extruder); mmu_command(MMU_CMD_T0 + tmp_extruder); - manage_response(false, true); - mmu_command(MMU_CMD_C0); + manage_response(false, true, MMU_LOAD_MOVE); + mmu_continue_loading(); mmu_extruder = tmp_extruder; //filament change is finished + mmu_load_to_nozzle(); load_filament_final_feed(); st_synchronize(); @@ -803,13 +966,14 @@ void extr_unload() lcd_clear(); lcd_set_cursor(0, 1); lcd_puts_P(_T(MSG_UNLOADING_FILAMENT)); lcd_print(" "); - lcd_print(mmu_extruder + 1); + if (mmu_extruder == MMU_FILAMENT_UNKNOWN) lcd_print(" "); + else lcd_print(mmu_extruder + 1); filament_ramming(); mmu_command(MMU_CMD_U0); // get response - manage_response(false, true); + manage_response(false, true, MMU_UNLOAD_MOVE); lcd_update_enable(true); #else //SNMM @@ -1105,8 +1269,8 @@ void lcd_mmu_load_to_nozzle(uint8_t filament_nr) lcd_print(" "); lcd_print(tmp_extruder + 1); mmu_command(MMU_CMD_T0 + tmp_extruder); - manage_response(true, true); - mmu_command(MMU_CMD_C0); + manage_response(true, true, MMU_TCODE_MOVE); + mmu_continue_loading(); mmu_extruder = tmp_extruder; //filament change is finished mmu_load_to_nozzle(); load_filament_final_feed(); @@ -1142,7 +1306,7 @@ void mmu_eject_filament(uint8_t filament, bool recover) plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], 2500 / 60, active_extruder); st_synchronize(); mmu_command(MMU_CMD_E0 + filament); - manage_response(false, false); + manage_response(false, false, MMU_UNLOAD_MOVE); if (recover) { lcd_show_fullscreen_message_and_wait_P(_i("Please remove filament and then press the knob.")); @@ -1162,3 +1326,46 @@ void mmu_eject_filament(uint8_t filament, bool recover) puts_P(PSTR("Filament nr out of range!")); } } + +void mmu_continue_loading() +{ + + if (mmu_idler_sensor_detected) { + for (uint8_t i = 0; i < MMU_IDLER_SENSOR_ATTEMPTS_NR; i++) { + if (PIN_GET(MMU_IDLER_SENSOR_PIN) == 0) return; +#ifdef MMU_DEBUG + printf_P(PSTR("Additional load attempt nr. %d\n"), i); +#endif // MMU_DEBUG + mmu_command(MMU_CMD_C0); + manage_response(true, true, MMU_LOAD_MOVE); + } + if (PIN_GET(MMU_IDLER_SENSOR_PIN) != 0) { + uint8_t mmu_load_fail = eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL); + uint16_t mmu_load_fail_tot = eeprom_read_word((uint16_t*)EEPROM_MMU_LOAD_FAIL_TOT); + if(mmu_load_fail < 255) eeprom_update_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL, mmu_load_fail + 1); + if(mmu_load_fail_tot < 65535) eeprom_update_word((uint16_t*)EEPROM_MMU_LOAD_FAIL_TOT, mmu_load_fail_tot + 1); + char cmd[3]; + //pause print, show error message and then repeat last T-code + stop_and_save_print_to_ram(0, 0); + + //lift z + current_position[Z_AXIS] += Z_PAUSE_LIFT; + if (current_position[Z_AXIS] > Z_MAX_POS) current_position[Z_AXIS] = Z_MAX_POS; + plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], 15, active_extruder); + st_synchronize(); + + //Move XY to side + current_position[X_AXIS] = X_PAUSE_POS; + current_position[Y_AXIS] = Y_PAUSE_POS; + plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], 50, active_extruder); + st_synchronize(); + //set nozzle target temperature to 0 + setAllTargetHotends(0); + lcd_setstatuspgm(_i("MMU load failed "));////MSG_RECOVERING_PRINT c=20 r=1 + mmu_fil_loaded = false; //so we can retry same T-code again + } + } + else { //mmu_idler_sensor_detected == false + mmu_command(MMU_CMD_C0); + } +} diff --git a/Firmware/mmu.h b/Firmware/mmu.h index 2071891ea..30f35599f 100644 --- a/Firmware/mmu.h +++ b/Firmware/mmu.h @@ -4,16 +4,30 @@ extern bool mmu_enabled; +extern bool mmu_fil_loaded; extern uint8_t mmu_extruder; extern uint8_t tmp_extruder; extern int8_t mmu_finda; +extern bool mmu_idler_sensor_detected; +extern bool mmu_loading_flag; extern int16_t mmu_version; extern int16_t mmu_buildnr; +extern uint16_t mmu_power_failures; + +#define MMU_FILAMENT_UNKNOWN 255 + +#define MMU_NO_MOVE 0 +#define MMU_UNLOAD_MOVE 1 +#define MMU_LOAD_MOVE 2 +#define MMU_TCODE_MOVE 3 + +#define MMU_LOAD_FEEDRATE 19.02f //mm/s +#define MMU_LOAD_TIME_MS 2000 //should be fine tuned to load time for shortest allowed PTFE tubing and maximum loading speed #define MMU_CMD_NONE 0 #define MMU_CMD_T0 0x10 @@ -34,7 +48,7 @@ extern int16_t mmu_buildnr; #define MMU_CMD_E3 0x53 #define MMU_CMD_E4 0x54 #define MMU_CMD_R0 0x60 - +#define MMU_CMD_S3 0x73 extern int mmu_puts_P(const char* str); @@ -42,6 +56,7 @@ extern int mmu_printf_P(const char* format, ...); extern int8_t mmu_rx_ok(void); +extern bool check_for_idler_sensor(); extern void mmu_init(void); @@ -54,9 +69,9 @@ extern int8_t mmu_set_filament_type(uint8_t extruder, uint8_t filament); extern void mmu_command(uint8_t cmd); -extern bool mmu_get_response(void); +extern bool mmu_get_response(uint8_t move = 0); -extern void manage_response(bool move_axes, bool turn_off_nozzle); +extern void manage_response(bool move_axes, bool turn_off_nozzle, uint8_t move = 0); extern void mmu_load_to_nozzle(); @@ -103,3 +118,4 @@ extern void mmu_eject_fil_1(); extern void mmu_eject_fil_2(); extern void mmu_eject_fil_3(); extern void mmu_eject_fil_4(); +extern void mmu_continue_loading(); diff --git a/Firmware/pins_Einsy_1_0.h b/Firmware/pins_Einsy_1_0.h index eabdb4abb..4ef8f9ba8 100644 --- a/Firmware/pins_Einsy_1_0.h +++ b/Firmware/pins_Einsy_1_0.h @@ -121,6 +121,7 @@ #define TACH_0 79 // !!! changed from 81 (EINY03) #define TACH_1 80 +#define MMU_IDLER_SENSOR_PIN 62 //idler sensor @PK0 (digital pin 62/A8) // Support for an 8 bit logic analyzer, for example the Saleae. // Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop. diff --git a/Firmware/pins_Rambo_1_0.h b/Firmware/pins_Rambo_1_0.h index 1e3a142c9..b37ab641d 100644 --- a/Firmware/pins_Rambo_1_0.h +++ b/Firmware/pins_Rambo_1_0.h @@ -102,7 +102,7 @@ #define SDCARDDETECT 72 - +#define MMU_IDLER_SENSOR_PIN 62 //idler sensor @PK0 (digital pin 62/A8) // Support for an 8 bit logic analyzer, for example the Saleae. // Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop. diff --git a/Firmware/pins_Rambo_1_3.h b/Firmware/pins_Rambo_1_3.h index a9a6b390d..297735c18 100644 --- a/Firmware/pins_Rambo_1_3.h +++ b/Firmware/pins_Rambo_1_3.h @@ -102,7 +102,7 @@ #define SDCARDDETECT 15 - +#define MMU_IDLER_SENSOR_PIN 62 //idler sensor @PK0 (digital pin 62/A8) // Support for an 8 bit logic analyzer, for example the Saleae. // Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop. diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index b675a1fdf..937ccd5a1 100644 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -38,7 +38,7 @@ #include "mmu.h" #include "static_assert.h" - +#include "io_atmega2560.h" extern bool fans_check_enabled; @@ -136,6 +136,11 @@ static void lcd_menu_extruder_info(); static void lcd_menu_xyz_y_min(); static void lcd_menu_xyz_skew(); static void lcd_menu_xyz_offset(); +static void lcd_menu_fails_stats_mmu(); +static void lcd_menu_fails_stats_mmu_print(); +static void lcd_menu_fails_stats_mmu_total(); +static void lcd_menu_show_sensors_state(); + #if defined(TMC2130) || defined(FILAMENT_SENSOR) static void lcd_menu_fails_stats(); #endif //TMC2130 or FILAMENT_SENSOR @@ -195,6 +200,9 @@ static void menu_action_sddirectory(const char* filename); #define ENCODER_FEEDRATE_DEADZONE 10 +#define STATE_NA 255 +#define STATE_OFF 0 +#define STATE_ON 1 /* #define MENU_ITEM(type, label, args...) do { \ @@ -537,10 +545,15 @@ void lcdui_print_percent_done(void) void lcdui_print_extruder(void) { int chars = 0; - if (mmu_extruder == tmp_extruder) - chars = lcd_printf_P(_N(" F%u"), mmu_extruder+1); + if (mmu_extruder == tmp_extruder) { + if (mmu_extruder == MMU_FILAMENT_UNKNOWN) chars = lcd_printf_P(_N(" F?")); + else chars = lcd_printf_P(_N(" F%u"), mmu_extruder + 1); + } else - chars = lcd_printf_P(_N(" %u>%u"), mmu_extruder+1, tmp_extruder+1); + { + if (mmu_extruder == MMU_FILAMENT_UNKNOWN) chars = lcd_printf_P(_N(" ?>%u"), tmp_extruder + 1); + else chars = lcd_printf_P(_N(" %u>%u"), mmu_extruder + 1, tmp_extruder + 1); + } lcd_space(5 - chars); } @@ -1914,6 +1927,48 @@ static void lcd_menu_extruder_info() menu_back_if_clicked(); } +static void lcd_menu_fails_stats_mmu() +{ + MENU_BEGIN(); + MENU_ITEM_BACK_P(_T(MSG_MAIN)); + MENU_ITEM_SUBMENU_P(_i("Last print"), lcd_menu_fails_stats_mmu_print); + MENU_ITEM_SUBMENU_P(_i("Total"), lcd_menu_fails_stats_mmu_total); + MENU_END(); +} + +static void lcd_menu_fails_stats_mmu_print() +{ +//01234567890123456789 +//Last print failures +// MMU fails 000 +// MMU load fails 000 +// +////////////////////// + lcd_timeoutToStatus.stop(); //infinite timeout + uint8_t fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL); + uint16_t load_fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL); +// lcd_printf_P(PSTR(ESC_H(0,0) "Last print failures" ESC_H(1,1) "Power failures %-3d" ESC_H(1,2) "Filam. runouts %-3d" ESC_H(1,3) "Crash X %-3d Y %-3d"), power, filam, crashX, crashY); + lcd_printf_P(PSTR(ESC_H(0,0) "%S" ESC_H(1,1) "%S %-3d" ESC_H(1,2) "%S %-3d" ESC_H(1,3)), _i("Last print failures"), _i("MMU fails"), fails, _i("MMU load fails"), load_fails); + menu_back_if_clicked_fb(); +} + +static void lcd_menu_fails_stats_mmu_total() +{ +//01234567890123456789 +//Last print failures +// MMU fails 000 +// MMU load fails 000 +// +////////////////////// + mmu_command(MMU_CMD_S3); + lcd_timeoutToStatus.stop(); //infinite timeout + uint8_t fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL_TOT); + uint16_t load_fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL_TOT); +// lcd_printf_P(PSTR(ESC_H(0,0) "Last print failures" ESC_H(1,1) "Power failures %-3d" ESC_H(1,2) "Filam. runouts %-3d" ESC_H(1,3) "Crash X %-3d Y %-3d"), power, filam, crashX, crashY); + lcd_printf_P(PSTR(ESC_H(0,0) "%S" ESC_H(1,1) "%S %-3d" ESC_H(1,2) "%S %-3d" ESC_H(1,3) "%S %-3d"), _i("Total failures"), _i("MMU fails"), fails, _i("MMU load fails"), load_fails, _i("MMU power fails"), mmu_power_failures); + menu_back_if_clicked_fb(); +} + #if defined(TMC2130) && defined(FILAMENT_SENSOR) static void lcd_menu_fails_stats_total() { @@ -1950,6 +2005,7 @@ static void lcd_menu_fails_stats_print() lcd_printf_P(PSTR(ESC_H(0,0) "%S" ESC_H(1,1) "%S %-3d" ESC_H(1,2) "%S %-3d" ESC_H(1,3) "%S X %-3d Y %-3d"), _i("Last print failures"), _i("Power failures"), power, _i("Filam. runouts"), filam, _i("Crash"), crashX, crashY); menu_back_if_clicked_fb(); } + /** * @brief Open fail statistics menu * @@ -1965,6 +2021,7 @@ static void lcd_menu_fails_stats() MENU_ITEM_SUBMENU_P(_i("Total"), lcd_menu_fails_stats_total); MENU_END(); } + #elif defined(FILAMENT_SENSOR) /** * @brief Print last print and total filament run outs @@ -2180,16 +2237,17 @@ static void lcd_support_menu() #ifndef MK1BP MENU_ITEM_BACK_P(STR_SEPARATOR); MENU_ITEM_SUBMENU_P(_i("XYZ cal. details"), lcd_menu_xyz_y_min);////MSG_XYZ_DETAILS c=19 r=1 - MENU_ITEM_SUBMENU_P(_i("Extruder info"), lcd_menu_extruder_info);////MSG_INFO_EXTRUDER c=15 r=1 + MENU_ITEM_SUBMENU_P(_i("Extruder info"), lcd_menu_extruder_info);////MSG_INFO_EXTRUDER c=18 r=1 + MENU_ITEM_SUBMENU_P(_i("Show sensors"), lcd_menu_show_sensors_state);////MSG_INFO_SENSORS c=18 r=1 #ifdef TMC2130 - MENU_ITEM_SUBMENU_P(_i("Belt status"), lcd_menu_belt_status);////MSG_MENU_BELT_STATUS c=15 r=1 + MENU_ITEM_SUBMENU_P(_i("Belt status"), lcd_menu_belt_status);////MSG_MENU_BELT_STATUS c=18 r=1 #endif //TMC2130 - MENU_ITEM_SUBMENU_P(_i("Temperatures"), lcd_menu_temperatures);////MSG_MENU_TEMPERATURES c=15 r=1 + MENU_ITEM_SUBMENU_P(_i("Temperatures"), lcd_menu_temperatures);////MSG_MENU_TEMPERATURES c=18 r=1 #if defined (VOLT_BED_PIN) || defined (VOLT_PWR_PIN) - MENU_ITEM_SUBMENU_P(_i("Voltages"), lcd_menu_voltages);////MSG_MENU_VOLTAGES c=15 r=1 + MENU_ITEM_SUBMENU_P(_i("Voltages"), lcd_menu_voltages);////MSG_MENU_VOLTAGES c=18 r=1 #endif //defined VOLT_BED_PIN || defined VOLT_PWR_PIN #ifdef DEBUG_BUILD @@ -3571,28 +3629,60 @@ void lcd_diag_show_end_stops() lcd_return_to_status(); } -#ifdef TMC2130 -static void lcd_show_pinda_state() +static void lcd_print_state(uint8_t state) { -lcd_set_cursor(0, 0); -lcd_puts_P((PSTR("P.I.N.D.A. state"))); -lcd_set_cursor(0, 2); -lcd_puts_P(READ(Z_MIN_PIN)?(PSTR("Z1 (LED off)")):(PSTR("Z0 (LED on) "))); // !!! both strings must have same length (due to dynamic refreshing) + switch (state) { + case STATE_ON: + lcd_puts_P(_i("On ")); + break; + case STATE_OFF: + lcd_puts_P(_i("Off")); + break; + default: + lcd_puts_P(_i("N/A")); + break; + } } -static void menu_show_pinda_state() +static void lcd_show_sensors_state() { -lcd_timeoutToStatus.stop(); -lcd_show_pinda_state(); -if(LCD_CLICKED) - { - lcd_timeoutToStatus.start(); - menu_back(); - } + //0: N/A; 1: OFF; 2: ON + uint8_t chars = 0; + uint8_t pinda_state = STATE_NA; + uint8_t finda_state = STATE_NA; + uint8_t idler_state = STATE_NA; + + pinda_state = READ(Z_MIN_PIN); + if (mmu_enabled) { + finda_state = mmu_finda; + } + if (mmu_idler_sensor_detected) { + idler_state = !PIN_GET(MMU_IDLER_SENSOR_PIN); + } + lcd_puts_at_P(0, 0, _i("Sensor state")); + lcd_puts_at_P(1, 1, _i("PINDA:")); + lcd_set_cursor(LCD_WIDTH - 4, 1); + lcd_print_state(pinda_state); + + lcd_puts_at_P(1, 2, _i("FINDA:")); + lcd_set_cursor(LCD_WIDTH - 4, 2); + lcd_print_state(finda_state); + + lcd_puts_at_P(1, 3, _i("IR:")); + lcd_set_cursor(LCD_WIDTH - 4, 3); + lcd_print_state(idler_state); } -#endif // defined TMC2130 - +static void lcd_menu_show_sensors_state() +{ + lcd_timeoutToStatus.stop(); + lcd_show_sensors_state(); + if(LCD_CLICKED) + { + lcd_timeoutToStatus.start(); + menu_back(); + } +} void prusa_statistics(int _message, uint8_t _fil_nr) { #ifdef DEBUG_DISABLE_PRUSA_STATISTICS @@ -4949,9 +5039,7 @@ static void lcd_calibration_menu() MENU_ITEM_SUBMENU_P(_i("Bed level correct"), lcd_adjust_bed);////MSG_BED_CORRECTION_MENU c=0 r=0 MENU_ITEM_SUBMENU_P(_i("PID calibration"), pid_extruder);////MSG_PID_EXTRUDER c=17 r=1 -#ifdef TMC2130 - MENU_ITEM_SUBMENU_P(_i("Show pinda state"), menu_show_pinda_state); -#else +#ifndef TMC2130 MENU_ITEM_SUBMENU_P(_i("Show end stops"), menu_show_end_stops);////MSG_SHOW_END_STOPS c=17 r=1 #endif #ifndef MK1BP @@ -5858,7 +5946,9 @@ static void lcd_main_menu() #if defined(TMC2130) || defined(FILAMENT_SENSOR) MENU_ITEM_SUBMENU_P(_i("Fail stats"), lcd_menu_fails_stats); #endif - + if (mmu_enabled) { + MENU_ITEM_SUBMENU_P(_i("Fail stats MMU"), lcd_menu_fails_stats_mmu); + } MENU_ITEM_SUBMENU_P(_i("Support"), lcd_support_menu);////MSG_SUPPORT c=0 r=0 #ifdef LCD_TEST MENU_ITEM_SUBMENU_P(_i("W25x20CL init"), lcd_test_menu);////MSG_SUPPORT c=0 r=0 diff --git a/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h index f237e79a6..7bceb096f 100644 --- a/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h @@ -432,4 +432,6 @@ THERMISTORS SETTINGS //#define SUPPORT_VERBOSITY #endif +#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning + #endif //__CONFIGURATION_PRUSA_H diff --git a/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h b/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h index e09178bbc..14fbd865b 100644 --- a/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h @@ -432,4 +432,6 @@ THERMISTORS SETTINGS //#define SUPPORT_VERBOSITY #endif +#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning + #endif //__CONFIGURATION_PRUSA_H diff --git a/Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h index f3a5285dd..0472a1082 100644 --- a/Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h @@ -487,4 +487,6 @@ #define MMU_REQUIRED_FW_BUILDNR 132 //#define MMU_DEBUG //print communication between MMU2 and printer on serial +#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning + #endif //__CONFIGURATION_PRUSA_H diff --git a/Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h b/Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h index 2aff81e0c..615d34826 100644 --- a/Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h @@ -488,4 +488,6 @@ #define MMU_REQUIRED_FW_BUILDNR 132 //#define MMU_DEBUG //print communication between MMU2 and printer on serial +#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning + #endif //__CONFIGURATION_PRUSA_H diff --git a/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h index 4655107c0..31d8277ac 100644 --- a/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h @@ -613,6 +613,8 @@ #define MMU_REQUIRED_FW_BUILDNR 83 #define MMU_HWRESET -//#define MMU_DEBUG //print communication between MMU2 and printer on serial +#define MMU_DEBUG //print communication between MMU2 and printer on serial + +#define MMU_IDLER_SENSOR_ATTEMPTS_NR 21 //max. number of attempts to load filament if first load failed; value for max bowden length and case when loading fails right at the beginning #endif //__CONFIGURATION_PRUSA_H