From 4bdc20ffb94f70eaf99ad2c19d96f6c951f191c5 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Wed, 11 Feb 2015 21:19:00 -0700 Subject: [PATCH] Overhauled state machine. New safety door feature. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Overhauled the state machine and cleaned up its overall operation. This involved creating a new ‘suspend’ state for what all external commands, except real-time commands, are ignored. All hold type states enter this suspend state. - Removed ‘auto cycle start’ setting from Grbl. This was not used by users in its intended way and is somewhat redundant, as GUI manage the cycle start by streaming. It also muddled up how Grbl should interpret how and when to execute a g-code block. Removing it made everything much much simpler. - Fixed a program pause bug when used with other buffer_sync commands. - New safety door feature for OEMs. Immediately forces a feed hold and then de-energizes the machine. Resuming is blocked until the door is closed. When it is, it re-energizes the system and then resumes on the normal toolpath. - Safety door input pin is optional and uses the feed hold pin on A1. Enabled by config.h define. - Spindle and coolant re-energizing upon a safety door resume has a programmable delay time to allow for complete spin up to rpm and turning on the coolant before resuming motion. - Safety door-style feed holds can be used instead of regular feed hold (doesn’t de-energize the machine) with a ‘@‘ character. If the safety door input pin is not enabled, the system can be resumed at any time. --- grbl/config.h | 13 ++++ grbl/cpu_map.h | 3 +- grbl/gcode.c | 6 +- grbl/limits.c | 10 ++- grbl/main.c | 3 +- grbl/motion_control.c | 14 +--- grbl/probe.c | 2 +- grbl/protocol.c | 163 ++++++++++++++++++++++++++++++++---------- grbl/report.c | 8 ++- grbl/report.h | 1 + grbl/serial.c | 1 + grbl/settings.c | 5 -- grbl/settings.h | 2 +- grbl/stepper.c | 31 ++++---- grbl/system.c | 37 +++++++++- grbl/system.h | 30 +++++--- 16 files changed, 237 insertions(+), 92 deletions(-) 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);