diff --git a/grbl/config.h b/grbl/config.h index a5c7f4e..0635c65 100644 --- a/grbl/config.h +++ b/grbl/config.h @@ -54,6 +54,7 @@ #define CMD_FEED_HOLD '!' #define CMD_CYCLE_START '~' #define CMD_RESET 0x18 // ctrl-x. +#define CMD_SAFETY_DOOR '@' //0x13 // ctrl-s // If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces // the user to perform the homing cycle (or override the locks) before doing anything else. This is @@ -133,6 +134,18 @@ // NOTE: The M8 flood coolant control pin on analog pin 4 will still be functional regardless. // #define ENABLE_M7 // Disabled by default. Uncomment to enable. +// This option causes the feed hold input to act as a safety door switch. A safety door, when triggered, +// immediately forces a feed hold and then safely de-energizes the machine. Resuming is blocked until +// the safety door is re-engaged. When it is, Grbl will re-energize the machine and then resume on the +// previous tool path, as if nothing happened. +// #define ENABLE_SAFETY_DOOR_INPUT_PIN // Default disabled. Uncomment to enable. + +// After the safety door switch has been toggled and restored, this setting sets the power-up delay +// between restoring the spindle and coolant and resuming the cycle. +// NOTE: Delay value is defined in milliseconds from zero to 65,535. +#define SAFETY_DOOR_SPINDLE_DELAY 4000 // Disabled by default. Comment to enable. +#define SAFETY_DOOR_COOLANT_DELAY 1000 // Disabled by default. Comment to enable. + // Enable CoreXY kinematics. Use ONLY with CoreXY machines. // IMPORTANT: If homing is enabled, you must reconfigure the homing cycle #defines above to // #define HOMING_CYCLE_0 (1<entry_speed_sqr = prep.exit_speed*prep.exit_speed; @@ -587,7 +593,7 @@ void st_prep_buffer() */ prep.mm_complete = 0.0; // Default velocity profile complete at 0.0mm from end of block. float inv_2_accel = 0.5/pl_block->acceleration; - if (sys.state == STATE_HOLD) { // [Forced Deceleration to Zero Velocity] + if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { // [Forced Deceleration to Zero Velocity] // Compute velocity profile parameters for a feed hold in-progress. This profile overrides // the planner block profile, enforcing a deceleration to zero speed. prep.ramp_type = RAMP_DECEL; @@ -746,16 +752,14 @@ void st_prep_buffer() // Bail if we are at the end of a feed hold and don't have a step to execute. if (prep_segment->n_step == 0) { - if (sys.state == STATE_HOLD) { - + if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { // Less than one step to decelerate to zero speed, but already very close. AMASS // requires full steps to execute. So, just bail. - prep.current_speed = 0.0; + prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold. prep.dt_remainder = 0.0; prep.steps_remaining = n_steps_remaining; pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps. plan_cycle_reinitialize(); - sys.state = STATE_QUEUED; return; // Segment not generated, but current step data still retained. } } @@ -818,21 +822,18 @@ void st_prep_buffer() } else { // End of planner block or forced-termination. No more distance to be executed. if (mm_remaining > 0.0) { // At end of forced-termination. - // Reset prep parameters for resuming and then bail. - // NOTE: Currently only feed holds qualify for this scenario. May change with overrides. - prep.current_speed = 0.0; + // Reset prep parameters for resuming and then bail. Allow the stepper ISR to complete + // the segment queue, where realtime protocol will set new state upon receiving the + // cycle stop flag from the ISR. Prep_segment is blocked until then. + prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold. prep.dt_remainder = 0.0; prep.steps_remaining = ceil(steps_remaining); pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps. plan_cycle_reinitialize(); - sys.state = STATE_QUEUED; // End cycle. - return; // Bail! -// TODO: Try to move QUEUED setting into cycle re-initialize. - } else { // End of planner block // The planner block is complete. All steps are set to be executed in the segment buffer. - pl_block = NULL; + pl_block = NULL; // Set pointer to indicate check and load next planner block. plan_discard_current_block(); } } @@ -848,7 +849,7 @@ void st_prep_buffer() #ifdef REPORT_REALTIME_RATE float st_get_realtime_rate() { - if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD)){ + if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR)){ return prep.current_speed; } return 0.0f; diff --git a/grbl/system.c b/grbl/system.c index bec36d6..7a09162 100644 --- a/grbl/system.c +++ b/grbl/system.c @@ -48,15 +48,35 @@ ISR(CONTROL_INT_vect) if (pin) { if (bit_istrue(pin,bit(RESET_BIT))) { mc_reset(); - } else if (bit_istrue(pin,bit(FEED_HOLD_BIT))) { - bit_true(sys.rt_exec_state, EXEC_FEED_HOLD); } else if (bit_istrue(pin,bit(CYCLE_START_BIT))) { bit_true(sys.rt_exec_state, EXEC_CYCLE_START); + #ifndef ENABLE_SAFETY_DOOR_INPUT_PIN + } else if (bit_istrue(pin,bit(FEED_HOLD_BIT))) { + bit_true(sys.rt_exec_state, EXEC_FEED_HOLD); + #else + } else if (bit_istrue(pin,bit(SAFETY_DOOR_BIT))) { + bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR); + #endif } } } +// Returns if safety door is ajar(T) or closed(F), based on pin state. +uint8_t system_check_safety_door_ajar() +{ + #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN + #ifdef INVERT_CONTROL_PIN + return(bit_istrue(CONTROL_PIN,bit(SAFETY_DOOR_BIT))); + #else + return(bit_isfalse(CONTROL_PIN,bit(SAFETY_DOOR_BIT))); + #endif + #else + return(false); // Input pin not enabled, so just return that it's closed. + #endif +} + + // Executes user startup script, if stored. void system_execute_startup(char *line) { @@ -95,6 +115,7 @@ uint8_t system_execute_line(char *line) else { report_grbl_settings(); } break; case 'G' : // Prints gcode parser state + // TODO: Move this to realtime commands for GUIs to request this data during suspend-state. if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); } else { report_gcode_modes(); } break; @@ -118,6 +139,10 @@ uint8_t system_execute_line(char *line) report_feedback_message(MESSAGE_ALARM_UNLOCK); sys.state = STATE_IDLE; // Don't run startup script. Prevents stored moves in startup from causing accidents. + if (system_check_safety_door_ajar()) { // Check safety door switch before returning. + bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR); + protocol_execute_realtime(); // Enter safety door mode. + } } // Otherwise, no effect. break; // case 'J' : break; // Jogging methods @@ -144,6 +169,14 @@ uint8_t system_execute_line(char *line) if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_HOMING; // Set system state variable // Only perform homing if Grbl is idle or lost. + + // TODO: Likely not required. + if (system_check_safety_door_ajar()) { // Check safety door switch before homing. + bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR); + protocol_execute_realtime(); // Enter safety door mode. + } + + mc_homing_cycle(); if (!sys.abort) { // Execute startup scripts after successful homing. sys.state = STATE_IDLE; // Set to IDLE when complete. diff --git a/grbl/system.h b/grbl/system.h index 2c2a89d..58b67d1 100644 --- a/grbl/system.h +++ b/grbl/system.h @@ -33,6 +33,8 @@ #define EXEC_CYCLE_STOP bit(2) // bitmask 00000100 #define EXEC_FEED_HOLD bit(3) // bitmask 00001000 #define EXEC_RESET bit(4) // bitmask 00010000 +#define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000 +#define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000 // Alarm executor bit map. // NOTE: EXEC_CRITICAL_EVENT is an optional flag that must be set with an alarm flag. When enabled, @@ -47,27 +49,34 @@ // Define system state bit map. The state variable primarily tracks the individual functions // of Grbl to manage each without overlapping. It is also used as a messaging flag for // critical events. -#define STATE_IDLE 0 // Must be zero. No flags. -#define STATE_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access. -#define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only. -#define STATE_HOMING bit(2) // Performing homing cycle -#define STATE_QUEUED bit(3) // Indicates buffered blocks, awaiting cycle start. -#define STATE_CYCLE bit(4) // Cycle is running -#define STATE_HOLD bit(5) // Executing feed hold -// #define STATE_JOG bit(6) // Jogging mode is unique like homing. +#define STATE_IDLE 0 // Must be zero. No flags. +#define STATE_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access. +#define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only. +#define STATE_HOMING bit(2) // Performing homing cycle +#define STATE_CYCLE bit(3) // Cycle is running or motions are being executed. +#define STATE_HOLD bit(4) // Active feed hold +#define STATE_SAFETY_DOOR bit(5) // Safety door is ajar. Feed holds and de-energizes system. +#define STATE_MOTION_CANCEL bit(6) // Motion cancel by feed hold and return to idle. + +// Define system suspend states. +#define SUSPEND_DISABLE 0 // Must be zero. +#define SUSPEND_ENABLE_HOLD bit(0) // Enabled. Indicates the cycle is active and currently undergoing a hold. +#define SUSPEND_ENABLE_READY bit(1) // Ready to resume with a cycle start command. +#define SUSPEND_ENERGIZE bit(2) // Re-energizes output before resume. +#define SUSPEND_MOTION_CANCEL bit(3) // Cancels resume motion. Used by probing routine. // Define global system variables typedef struct { uint8_t abort; // System abort flag. Forces exit back to main loop for reset. uint8_t state; // Tracks the current state of Grbl. + uint8_t suspend; // System suspend flag. Allows only realtime commands. Used primarily for holds. volatile uint8_t rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks. volatile uint8_t rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms. int32_t position[N_AXIS]; // Real-time machine (aka home) position vector in steps. // NOTE: This may need to be a volatile variable, if problems arise. - uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings. uint8_t homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR. volatile uint8_t probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR. @@ -80,6 +89,9 @@ extern system_t sys; // Initialize the serial protocol void system_init(); +// Returns if safety door is open or closed, based on pin state. +uint8_t system_check_safety_door_ajar(); + // Executes an internal system command, defined as a string starting with a '$' uint8_t system_execute_line(char *line);