diff --git a/Firmware/Configuration.h b/Firmware/Configuration.h index e36c78c5..538102e7 100644 --- a/Firmware/Configuration.h +++ b/Firmware/Configuration.h @@ -519,7 +519,13 @@ const bool Z_MAX_ENDSTOP_INVERTING = true; // set to true to invert the logic of // please keep turned on if you can. //#define EEPROM_CHITCHAT - +// Host Keepalive +// +// When enabled Marlin will send a busy status message to the host +// every couple of seconds when it can't accept commands. +// +#define HOST_KEEPALIVE_FEATURE // Disable this if your host doesn't like keepalive messages +#define HOST_KEEPALIVE_INTERVAL 2 // Number of seconds between "busy" messages. Set with M113. //LCD and SD support #define ULTRA_LCD //general LCD support, also 16x2 diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index 469081dd..6c7decda 100644 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -290,6 +290,10 @@ extern float retract_length, retract_length_swap, retract_feedrate, retract_zlif extern float retract_recover_length, retract_recover_length_swap, retract_recover_feedrate; #endif +#ifdef HOST_KEEPALIVE_FEATURE +extern uint8_t host_keepalive_interval; +#endif + extern unsigned long starttime; extern unsigned long stoptime; extern int bowden_length[4]; @@ -381,6 +385,24 @@ extern void print_world_coordinates(); extern void print_physical_coordinates(); extern void print_mesh_bed_leveling_table(); +#ifdef HOST_KEEPALIVE_FEATURE + +// States for managing Marlin and host communication +// Marlin sends messages if blocked or busy +enum MarlinBusyState { + NOT_BUSY, // Not in a handler + IN_HANDLER, // Processing a GCode + IN_PROCESS, // Known to be blocking command input (as in G29) + PAUSED_FOR_USER, // Blocking pending any input + PAUSED_FOR_INPUT // Blocking pending text input (concept) +}; + +#define KEEPALIVE_STATE(n) do { busy_state = n;} while (0) +extern void host_keepalive(); +extern MarlinBusyState busy_state; + +#endif //HOST_KEEPALIVE_FEATURE + // G-codes bool gcode_M45(bool onlyZ); void gcode_M701(); diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index b520cdb2..366cb250 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -176,6 +176,7 @@ // Rxxx Wait for extruder current temp to reach target temp. Waits when heating and cooling // IF AUTOTEMP is enabled, S B F. Exit autotemp by any M109 without F // M112 - Emergency stop +// M113 - Get or set the timeout interval for Host Keepalive "busy" messages // M114 - Output current position to serial port // M115 - Capabilities string // M117 - display message @@ -396,6 +397,16 @@ int fanSpeed=0; bool cancel_heatup = false ; +#ifdef HOST_KEEPALIVE_FEATURE + + MarlinBusyState busy_state = NOT_BUSY; + static long prev_busy_signal_ms = -1; + uint8_t host_keepalive_interval = HOST_KEEPALIVE_INTERVAL; +#else + #define host_keepalive(); + #define KEEPALIVE_STATE(n); +#endif + #ifdef FILAMENT_SENSOR //Variables for Filament Sensor input float filament_width_nominal=DEFAULT_NOMINAL_FILAMENT_DIA; //Set nominal filament width, can be changed with M404 @@ -832,7 +843,7 @@ void setup() // Reset the machine correction matrix. // It does not make sense to load the correction matrix until the machine is homed. world2machine_reset(); - + KEEPALIVE_STATE(PAUSED_FOR_USER); if (!READ(BTN_ENC)) { _delay_ms(1000); @@ -1021,7 +1032,7 @@ void setup() lcd_show_fullscreen_message_and_wait_P(MSG_FOLLOW_CALIBRATION_FLOW); } } - + KEEPALIVE_STATE(IN_PROCESS); #endif //DEBUG_DISABLE_STARTMSGS lcd_update_enable(true); lcd_implementation_clear(); @@ -1029,6 +1040,7 @@ void setup() // Store the currently running firmware into an eeprom, // so the next time the firmware gets updated, it will know from which version it has been updated. update_current_firmware_version_to_eeprom(); + if (eeprom_read_byte((uint8_t*)EEPROM_UVLO) == 1) { //previous print was terminated by UVLO /* if (lcd_show_fullscreen_message_yes_no_and_wait_P(MSG_RECOVER_PRINT, false)) recover_print(); @@ -1068,7 +1080,7 @@ void setup() } } - + KEEPALIVE_STATE(NOT_BUSY); } void trace(); @@ -1145,10 +1157,43 @@ int serial_read_stream() { } } +#ifdef HOST_KEEPALIVE_FEATURE +/** +* Output a "busy" message at regular intervals +* while the machine is not accepting commands. +*/ +void host_keepalive() { + if (farm_mode) return; + long ms = millis(); + if (host_keepalive_interval && busy_state != NOT_BUSY) { + if ((ms - prev_busy_signal_ms) < (long)(1000L * host_keepalive_interval)) return; + switch (busy_state) { + case IN_HANDLER: + case IN_PROCESS: + SERIAL_ECHO_START; + SERIAL_ECHOLNPGM("busy: processing"); + break; + case PAUSED_FOR_USER: + SERIAL_ECHO_START; + SERIAL_ECHOLNPGM("busy: paused for user"); + break; + case PAUSED_FOR_INPUT: + SERIAL_ECHO_START; + SERIAL_ECHOLNPGM("busy: paused for input"); + break; + default: + break; + } + } + prev_busy_signal_ms = ms; +} +#endif + // The loop() function is called in an endless loop by the Arduino framework from the default main() routine. // Before loop(), the setup() function is called by the main() routine. void loop() { + KEEPALIVE_STATE(NOT_BUSY); bool stack_integrity = true; if (usb_printing_counter > 0 && millis()-_usb_timer > 1000) @@ -1218,6 +1263,7 @@ void loop() planner_add_sd_length(sdlen.value); sei(); } + host_keepalive(); } } //check heater every n milliseconds @@ -1237,6 +1283,7 @@ void loop() enquecommand_P((PSTR("D999"))); } #endif //TMC2130 + } #define DEFINE_PGM_READ_ANY(type, reader) \ @@ -1787,9 +1834,11 @@ bool gcode_M45(bool onlyZ) { // lcd_wait_for_cool_down(); //} if(!onlyZ){ + KEEPALIVE_STATE(PAUSED_FOR_USER); bool result = lcd_show_fullscreen_message_yes_no_and_wait_P(MSG_STEEL_SHEET_CHECK, false, false); if(result) lcd_show_fullscreen_message_and_wait_P(MSG_REMOVE_STEEL_SHEET); lcd_show_fullscreen_message_and_wait_P(MSG_PAPER); + KEEPALIVE_STATE(IN_HANDLER); lcd_display_message_fullscreen_P(MSG_FIND_BED_OFFSET_AND_SKEW_LINE1); lcd_implementation_print_at(0, 2, 1); lcd_printPGM(MSG_FIND_BED_OFFSET_AND_SKEW_LINE2); @@ -1937,12 +1986,14 @@ void process_commands() #endif // PRUSA GCODES + KEEPALIVE_STATE(IN_HANDLER); #ifdef SNMM float tmp_motor[3] = DEFAULT_PWM_MOTOR_CURRENT; float tmp_motor_loud[3] = DEFAULT_PWM_MOTOR_CURRENT_LOUD; int8_t SilentMode; #endif + if (code_seen("M117")) { //moved to highest priority place to be able to to print strings which includes "G", "PRUSA" and "^" starpos = (strchr(strchr_pointer + 5, '*')); if (starpos != NULL) @@ -2782,6 +2833,7 @@ void process_commands() enquecommand_front_P((PSTR("G28 W0"))); break; } + KEEPALIVE_STATE(NOT_BUSY); //no need to print busy messages as we print current temperatures periodicaly SERIAL_ECHOLNPGM("PINDA probe calibration start"); float zero_z; @@ -3362,6 +3414,7 @@ void process_commands() current_position[E_AXIS] += DEFAULT_RETRACTION; plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], 400, active_extruder); } + KEEPALIVE_STATE(NOT_BUSY); // Restore custom message state custom_message = custom_message_old; custom_message_type = custom_message_type_old; @@ -3568,20 +3621,24 @@ void process_commands() previous_millis_cmd = millis(); if (codenum > 0){ codenum += millis(); // keep track of when we started waiting + KEEPALIVE_STATE(PAUSED_FOR_USER); while(millis() < codenum && !lcd_clicked()){ manage_heater(); manage_inactivity(true); lcd_update(); } + KEEPALIVE_STATE(IN_HANDLER); lcd_ignore_click(false); }else{ if (!lcd_detected()) break; + KEEPALIVE_STATE(PAUSED_FOR_USER); while(!lcd_clicked()){ manage_heater(); manage_inactivity(true); lcd_update(); } + KEEPALIVE_STATE(IN_HANDLER); } if (IS_SD_PRINTING) LCD_MESSAGERPGM(MSG_RESUMING); @@ -3797,7 +3854,9 @@ void process_commands() case 47: // M47: Prusa3D: Show end stops dialog on the display. + KEEPALIVE_STATE(PAUSED_FOR_USER); lcd_diag_show_end_stops(); + KEEPALIVE_STATE(IN_HANDLER); break; #if 0 @@ -4192,6 +4251,7 @@ Sigma_Exit: }} #endif SERIAL_PROTOCOLLN(""); + KEEPALIVE_STATE(NOT_BUSY); return; break; case 109: @@ -4228,12 +4288,15 @@ Sigma_Exit: /* See if we are heating up or cooling down */ target_direction = isHeatingHotend(tmp_extruder); // true if heating, false if cooling + + KEEPALIVE_STATE(NOT_BUSY); cancel_heatup = false; wait_for_heater(codenum); //loops until target temperature is reached LCD_MESSAGERPGM(MSG_HEATING_COMPLETE); + KEEPALIVE_STATE(IN_HANDLER); heating_status = 2; if (farm_mode) { prusa_statistics(2); }; @@ -4261,6 +4324,7 @@ Sigma_Exit: cancel_heatup = false; target_direction = isHeatingBed(); // true if heating, false if cooling + KEEPALIVE_STATE(NOT_BUSY); while ( (target_direction)&&(!cancel_heatup) ? (isHeatingBed()) : (isCoolingBed()&&(CooldownNoWait==false)) ) { if(( millis() - codenum) > 1000 ) //Print Temp Reading every 1 second while heating up. @@ -4283,6 +4347,7 @@ Sigma_Exit: lcd_update(); } LCD_MESSAGERPGM(MSG_BED_DONE); + KEEPALIVE_STATE(IN_HANDLER); heating_status = 4; previous_millis_cmd = millis(); @@ -4420,6 +4485,19 @@ Sigma_Exit: } } break; +#ifdef HOST_KEEPALIVE_FEATURE + case 113: // M113 - Get or set Host Keepalive interval + if (code_seen('S')) { + host_keepalive_interval = (uint8_t)code_value_short(); + NOMORE(host_keepalive_interval, 60); + } + else { + SERIAL_ECHO_START; + SERIAL_ECHOPAIR("M113 S", (unsigned long)host_keepalive_interval); + SERIAL_PROTOCOLLN(""); + } + break; +#endif case 115: // M115 if (code_seen('V')) { // Report the Prusa version number. @@ -5252,6 +5330,7 @@ case 404: //M404 Enter the nominal filament width (3mm, 1.75mm ) N<3.0> or disp int counterBeep = 0; lcd_wait_interact(); load_filament_time = millis(); + KEEPALIVE_STATE(PAUSED_FOR_USER); while(!lcd_clicked()){ cnt++; @@ -5288,13 +5367,17 @@ case 404: //M404 Enter the nominal filament width (3mm, 1.75mm ) N<3.0> or disp } } + KEEPALIVE_STATE(IN_HANDLER); + #ifdef SNMM display_loading(); + KEEPALIVE_STATE(PAUSED_FOR_USER); do { target[E_AXIS] += 0.002; plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], 500, active_extruder); delay_keep_alive(2); - } while (!lcd_clicked()); + } while (!lcd_clicked()); + KEEPALIVE_STATE(IN_HANDLER); /*if (millis() - load_filament_time > 2) { load_filament_time = millis(); target[E_AXIS] += 0.001; @@ -5334,7 +5417,9 @@ case 404: //M404 Enter the nominal filament width (3mm, 1.75mm ) N<3.0> or disp lcd_loading_filament(); while ((lcd_change_fil_state == 0)||(lcd_change_fil_state != 1)){ lcd_change_fil_state = 0; + KEEPALIVE_STATE(PAUSED_FOR_USER); lcd_alright(); + KEEPALIVE_STATE(IN_HANDLER); switch(lcd_change_fil_state){ // Filament failed to load so load it again @@ -5914,7 +5999,7 @@ case 404: //M404 Enter the nominal filament width (3mm, 1.75mm ) N<3.0> or disp SERIAL_ECHO(CMDBUFFER_CURRENT_STRING); SERIAL_ECHOLNPGM("\"(2)"); } - + KEEPALIVE_STATE(NOT_BUSY); ClearToSend(); } diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index c13cceea..1a77ada4 100644 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -783,6 +783,9 @@ void manage_heater() volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]=0.01; } #endif +#ifdef HOST_KEEPALIVE_FEATURE + host_keepalive(); +#endif } #define PGM_RD_W(x) (short)pgm_read_word(&x) diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index cd1d3685..b938abae 100644 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -1987,13 +1987,14 @@ void lcd_menu_statistics() lcd.print(itostr3(_days)); - + KEEPALIVE_STATE(PAUSED_FOR_USER); while (!lcd_clicked()) { manage_heater(); manage_inactivity(true); delay(100); } + KEEPALIVE_STATE(NOT_BUSY); lcd_quick_feedback(); lcd_return_to_status(); @@ -2069,6 +2070,7 @@ void lcd_service_mode_show_result() { } else lcd_print_at_PGM(8, i + 1, PSTR("N/A")); } delay_keep_alive(500); + KEEPALIVE_STATE(PAUSED_FOR_USER); while (!lcd_clicked()) { delay_keep_alive(100); } @@ -2094,6 +2096,7 @@ void lcd_service_mode_show_result() { while (!lcd_clicked()) { delay_keep_alive(100); } + KEEPALIVE_STATE(NOT_BUSY); delay_keep_alive(500); lcd_set_custom_characters_arrows(); lcd_return_to_status(); @@ -2570,8 +2573,9 @@ void lcd_show_fullscreen_message_and_wait_P(const char *msg) const char *msg_next = lcd_display_message_fullscreen_P(msg); bool multi_screen = msg_next != NULL; lcd_set_custom_characters_nextpage(); - // Until confirmed by a button click. - for (;;) { + KEEPALIVE_STATE(PAUSED_FOR_USER); + // Until confirmed by a button click. + for (;;) { if (!multi_screen) { lcd.setCursor(19, 3); // Display the confirm char. @@ -2584,6 +2588,7 @@ void lcd_show_fullscreen_message_and_wait_P(const char *msg) while (lcd_clicked()) ; delay(10); while (lcd_clicked()) ; + KEEPALIVE_STATE(IN_HANDLER); lcd_set_custom_characters(); lcd_update_enable(true); lcd_update(2); @@ -2606,6 +2611,7 @@ void lcd_show_fullscreen_message_and_wait_P(const char *msg) void lcd_wait_for_click() { + KEEPALIVE_STATE(PAUSED_FOR_USER); for (;;) { manage_heater(); manage_inactivity(true); @@ -2613,6 +2619,7 @@ void lcd_wait_for_click() while (lcd_clicked()) ; delay(10); while (lcd_clicked()) ; + KEEPALIVE_STATE(IN_HANDLER); return; } } @@ -2712,6 +2719,7 @@ int8_t lcd_show_fullscreen_message_yes_no_and_wait_P(const char *msg, bool allow // Wait for user confirmation or a timeout. unsigned long previous_millis_cmd = millis(); int8_t enc_dif = encoderDiff; + KEEPALIVE_STATE(PAUSED_FOR_USER); for (;;) { if (allow_timeouting && millis() - previous_millis_cmd > LCD_TIMEOUT_TO_STATUS) return -1; @@ -2737,6 +2745,7 @@ int8_t lcd_show_fullscreen_message_yes_no_and_wait_P(const char *msg, bool allow while (lcd_clicked()); delay(10); while (lcd_clicked()); + KEEPALIVE_STATE(IN_HANDLER); return yes; } } @@ -4013,7 +4022,7 @@ static char snmm_stop_print_menu() { //menu for choosing which filaments will be lcd_print_at_PGM(1,3,MSG_CURRENT); char cursor_pos = 1; int enc_dif = 0; - + KEEPALIVE_STATE(PAUSED_FOR_USER); while (1) { manage_heater(); manage_inactivity(true); @@ -4041,6 +4050,7 @@ static char snmm_stop_print_menu() { //menu for choosing which filaments will be while (lcd_clicked()); delay(10); while (lcd_clicked()); + KEEPALIVE_STATE(IN_HANDLER); return(cursor_pos - 1); } } @@ -4063,7 +4073,7 @@ char choose_extruder_menu() { for (int i = 0; i < 3; i++) { lcd_print_at_PGM(1, i + 1, MSG_EXTRUDER); } - + KEEPALIVE_STATE(PAUSED_FOR_USER); while (1) { for (int i = 0; i < 3; i++) { @@ -4127,6 +4137,7 @@ char choose_extruder_menu() { while (lcd_clicked()); delay(10); while (lcd_clicked()); + KEEPALIVE_STATE(IN_HANDLER); return(cursor_pos + first - 1); } @@ -4335,13 +4346,13 @@ static void extr_adj(int extruder) //loading filament for SNMM case 3: lcd_display_message_fullscreen_P(MSG_FILAMENT_LOADING_T3); break; default: lcd_display_message_fullscreen_P(MSG_FILAMENT_LOADING_T0); break; } - + KEEPALIVE_STATE(PAUSED_FOR_USER); do{ extr_mov(0.001,1000); delay_keep_alive(2); } while (!lcd_clicked()); //delay_keep_alive(500); - + KEEPALIVE_STATE(IN_HANDLER); st_synchronize(); //correct = lcd_show_fullscreen_message_yes_no_and_wait_P(MSG_FIL_LOADED_CHECK, false); //if (!correct) goto START;