v1.1c: New sleep mode. Laser mode and other bug fixes.

- New $SLP sleep mode that will disable spindle, coolant, and stepper
enable pins. Allows users to disable their steppers without having to
alter their settings. A reset is required to exit and re-initializes in
alarm state.

- Laser mode wasn’t updating the spindle PWM correctly (effected
spindle speed overrides) and not checking for modal states either.
Fixed both issues.

- While in laser mode, parking motions are ignored, since the power off
delay with the retract motion would burn the material. It will just
turn off and not move. A restore immediately powers up and resumes. No
delays.

- Changing rpm max and min settings did not update the spindle PWM
calculations. Now fixed.

- Increased default planner buffer from 16 to 17 block. It seems to be
stable, but need to monitor this carefully.

- Removed software debounce routine for limit pins. Obsolete.

- Fixed a couple parking motion bugs. One related to restoring
incorrectly and the other the parking rate wasn’t compatible with the
planner structs.

- Fixed a bug caused by refactoring the critical alarms in a recent
push. Soft limits weren’t invoking a critical alarm.

- Updated the documentation with the new sleep feature and added some
more details to the change summary.
This commit is contained in:
Sonny Jeon 2016-10-11 17:07:44 -06:00
parent e2e2bb5242
commit d1037268c8
20 changed files with 176 additions and 202 deletions

View File

@ -40,6 +40,8 @@ Grbl includes full acceleration management with look ahead. That means the contr
- **Laser Mode** : The new "laser" mode will cause Grbl to move continuously through consecutive G1, G2, and G3 commands with spindle speed changes. When "laser" mode is disabled, Grbl will instead come to a stop to ensure a spindle comes up to speed properly. Spindle speed overrides also work with laser mode so you can tweak the laser power, if you need to during the job. Switch between "laser" mode and "normal" mode via a `$` setting.
- **Sleep Mode** : Grbl may now be put to "sleep" via a `$SLP` command. This will disable everything, including the stepper drivers. Nice to have when you are leaving your machine unattended and want to power down everything automatically. Only a reset exits the sleep state.
- **Significant Interface Improvements**: Just this one time and done simultaneously with adding override data and controls, Grbl has tweaked the communication interface to make it easier for developers to write and maintain their GUIs. _NOTE: GUIs need to specifically update their code to be compatible with v1.1 and later._
- **New Status Reports**: To account for the additional override data, status reports have been tweaked to cram more data into it, while still being smaller than before. Documentation is included, outlining how it has been changed.

View File

@ -1,3 +1,21 @@
----------------
Date: 2016-09-28
Author: Sonny Jeon
Subject: New jog cancel real-time command. Parser typo fix from last push.
- Added a new jog cancel real-time command. Rather than depending on a
feed hold to cancel a jogging motion, this realtime command can be used
instead. The main advantage is if a feed hold is used, you can
accidentally hold the machine right when Grbl returns to IDLE after
completing a jog. And the GUI doesnt have to worry about tracking this
either.
- Fixed a typo in the g-code parser edits from the last push. Was
causing the G10 set coordinate system command to not work correctly.
- Updated the documentation with the jog cancel command.
----------------
Date: 2016-09-27
Author: Sonny Jeon

View File

@ -2,7 +2,7 @@
--------
### _Specific details are available in the interface.md document._
### _Specific details are available in the other markdown documents._
--------
#### GUI Interface Tweaks from Grbl v0.9
@ -33,7 +33,9 @@ Grbl v1.1's interface protocol has been tweaked in the attempt to make GUI devel
- `>G54G20:ok` : The open chevron indicates startup line execution. The `:ok` suffix shows it executed correctly without adding an unmatched `ok` response on a new line.
On a final note, this interface tweak came about out of necessity, as more data is being sent back from Grbl and it is capable of doing many more things. It's not intended to be altered again in the near future, if at all. This is likely the only and last major change to this. If you have any comments or suggestions before Grbl v1.1 goes to master, please do immediately so we can all vet the new alteration before its installed.
In addition, all `$x=val` settings, `error:`, and `ALARM:` messages no longer contain human-readable strings, but rather codes that are defined in other documents. The `$` help message is also reduced to just showing the available commands. Doing this saves incredible amounts of flash space. Otherwise, the new overrides features would not have fit.
On a final note, these interface tweaks came about out of necessity, because more data is being sent back from Grbl, it is capable of doing many more things, and flash space is at a premium. It's not intended to be altered again in the near future, if at all. This is likely the only and last major change to this. If you have any comments or suggestions before Grbl v1.1 goes to master, please do immediately so we can all vet the new alteration before its installed.
----
@ -92,3 +94,13 @@ On a final note, this interface tweak came about out of necessity, as more data
- Grbl is executing g-code block that does not contain a motion, like `G20G54` or `G4P1` dwell. (NOTE: Looking to fixing this later.)
-------
#### New Commands
- `$SLP` - Grbl v1.1 now has a sleep mode that can be invoked by this command. It requires Grbl to be in either an IDLE or ALARM state. Once invoked, Grbl will de-energize all connected systems, including the spindle, coolant, and stepper drivers. It'll enter a suspend state that can only be exited by a reset. When reset, Grbl will re-initiatize in an ALARM state because the steppers were disabled and position can not be guaranteed.
- NOTE: Grbl-Mega can invoke the sleep mode at any time, when the sleep timeout feature is enabled in config.h. It does so when Grbl has not received any external input after a timeout period.
- `$J=line` New jogging commands. This command behaves much like a normal G1 command, but there are some key differences. Jog commands don't alter the g-code parser state, meaning a GUI doesn't have to manage it anymore. Jog commands may be queued and cancelled at any time, where they are automatically flushed from the planner buffer without requiring a reset. See the jogging documentation on how they work and how they may be used to implement a low-latency joystick or rotary dial.
- Laser mode `$` setting - When enabled, laser mode will move through consecutive G1, G2, and G3 motion commands that have different spindle speed values without stopping. A spindle speed of zero will disable the laser without stopping as well. However, when spindle states change, like M3 or M5, stops are still enforced.
- NOTE: Parking motions are automatically disabled when laser mode is enabled to prevent burning.

View File

@ -23,7 +23,7 @@ The start up message always prints upon startup and after a reset. Whenever you
Every string Grbl receives is assumed to be a G-code block/line for it to execute, except for some special system commands Grbl uses for configuration, provide feedback to the user on what and how it's doing, or perform some task such as a homing cycle. To see a list of these system commands, type `$` followed by an enter, and Grbl will respond with:
```
[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $C $X $H ~ ! ? ctrl-x]
[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H ~ ! ? ctrl-x]
```
- _**NOTE:** Grbl v1.1's new override real-time commands are not included in the help message. They use the extended-ASCII character set, which are not easily type-able, and require a GUI that supports them. This is for two reasons: Establish enough characters for all of the overrides with extra for later growth, and prevent accidental keystrokes or characters in a g-code file from enacting an override inadvertently. _
@ -250,6 +250,8 @@ Feedback messages provide non-critical information on what Grbl is doing, what i
- `[MSG:Restoring defaults]` - Appears as an acknowledgement message when restoring EEPROM defaults via a `$RST=` command. An 'ok' still appears immediately after to denote the `$RST=` was parsed and executed.
- `[MSG:Sleeping]` - Appears as an acknowledgement message when Grbl's sleep mode is invoked by issuing a `$SLP` command when idle. Note that Grbl-Mega may invoke this at any time when the sleep timer option has been enabled and the timeout has been exceeded. Grbl may only be exited by a reset in the sleep state and will automatically enter an alarm state since the steppers were disabled.
- **Queried Feedback Messages:**
- `[GC:]` G-code Parser State Message
@ -362,7 +364,7 @@ Feedback messages provide non-critical information on what Grbl is doing, what i
- **Machine State:**
- Valid states types: `Idle, Run, Hold, Jog, Alarm, Door, Check, Home`
- Valid states types: `Idle, Run, Hold, Jog, Alarm, Door, Check, Home, Sleep`
- Sub-states may be included via `:` a colon delimiter and numeric code.

View File

@ -420,7 +420,7 @@
// available RAM, like when re-compiling for a Mega2560. Or decrease if the Arduino begins to
// crash due to the lack of available RAM or if the CPU is having trouble keeping up with planning
// new incoming motions as they are executed.
// #define BLOCK_BUFFER_SIZE 16 // Uncomment to override default in planner.h.
// #define BLOCK_BUFFER_SIZE 17 // Uncomment to override default in planner.h.
// Governs the size of the intermediary step segment buffer between the step execution algorithm
// and the planner blocks. Each segment is set of steps executed at a constant velocity over a
@ -453,15 +453,6 @@
// #define RX_BUFFER_SIZE 128 // (1-254) Uncomment to override defaults in serial.h
// #define TX_BUFFER_SIZE 90 // (1-254)
// A simple software debouncing feature for hard limit switches. When enabled, the interrupt
// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check
// the limit pin state after a delay of about 32msec. This can help with CNC machines with
// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with
// electrical interference on the signal cables from external sources. It's recommended to first
// use shielded signal cables with their shielding connected to ground (old USB/computer cables
// work well and are cheap to find) and wire in a low-pass circuit into each limit pin.
// #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable.
// Configures the position after a probing cycle during Grbl's check mode. Disabled sets
// the position to the probe target, when enabled sets the position to the start position.
// #define SET_CHECK_MODE_PROBE_TO_START // Default disabled. Uncomment to enable.
@ -548,8 +539,8 @@
// Configure options for the parking motion, if enabled.
#define PARKING_AXIS Z_AXIS // Define which axis that performs the parking motion
#define PARKING_TARGET -5.0 // Parking axis target. In mm, as machine coordinate [-max_travel,0].
#define PARKING_RATE -1.0 // Parking fast rate after pull-out. In mm/min or (-1.0) for seek rate.
#define PARKING_PULLOUT_RATE 250.0 // Pull-out/plunge slow feed rate in mm/min.
#define PARKING_RATE 500.0 // Parking fast rate after pull-out in mm/min.
#define PARKING_PULLOUT_RATE 100.0 // Pull-out/plunge slow feed rate in mm/min.
#define PARKING_PULLOUT_INCREMENT 5.0 // Spindle pull-out and plunge distance in mm. Incremental distance.
// Must be positive value or equal to zero.

View File

@ -125,7 +125,7 @@
// Variable spindle configuration below. Do not change unless you know what you are doing.
// NOTE: Only used when variable spindle is enabled.
#define SPINDLE_PWM_MAX_VALUE 255.0 // Don't change. 328p fast PWM mode fixes top value as 255.
#define SPINDLE_PWM_OFF_VALUE 0
#define SPINDLE_PWM_OFF_VALUE 0.0
#define SPINDLE_TCCRA_REGISTER TCCR2A
#define SPINDLE_TCCRB_REGISTER TCCR2B
#define SPINDLE_OCR_REGISTER OCR2A

View File

@ -901,7 +901,7 @@ uint8_t gc_execute_line(char *line)
if (gc_state.spindle_speed != gc_block.values.s) {
#ifdef VARIABLE_SPINDLE
// Do not stop motion if in laser mode and a G1, G2, or G3 motion is being executed.
if ( !(bit_istrue(settings.flags,BITFLAG_LASER_MODE) && (axis_command == AXIS_COMMAND_MOTION_MODE) &&
if ( (bit_isfalse(settings.flags,BITFLAG_LASER_MODE) && (axis_command == AXIS_COMMAND_MOTION_MODE) &&
((gc_block.modal.motion == MOTION_MODE_LINEAR ) || (gc_block.modal.motion == MOTION_MODE_CW_ARC) || (gc_block.modal.motion == MOTION_MODE_CCW_ARC)) ) ) {
// Update running spindle only if not in check mode and not already enabled.
if (gc_state.modal.spindle != SPINDLE_DISABLE) { spindle_run(gc_state.modal.spindle, gc_block.values.s); }

View File

@ -22,8 +22,8 @@
#define grbl_h
// Grbl versioning system
#define GRBL_VERSION "1.1b"
#define GRBL_VERSION_BUILD "20160928"
#define GRBL_VERSION "1.1c"
#define GRBL_VERSION_BUILD "20161011"
// Define standard libraries used by Grbl.
#include <avr/io.h>

View File

@ -88,53 +88,35 @@ uint8_t limits_get_state()
// limit switch can cause a lot of problems, like false readings and multiple interrupt calls.
// If a switch is triggered at all, something bad has happened and treat it as such, regardless
// if a limit switch is being disengaged. It's impossible to reliably tell the state of a
// bouncing pin without a debouncing method. A simple software debouncing feature may be enabled
// through the config.h file, where an extra timer delays the limit pin read by several milli-
// seconds to help with, not fix, bouncing switches.
// bouncing pin because the Arduino microcontroller does not retain any state information when
// detecting a pin change. If we poll the pins in the ISR, you can miss the correct reading if the
// switch is bouncing.
// NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during
// homing cycles and will not respond correctly. Upon user request or need, there may be a
// special pinout for an e-stop, but it is generally recommended to just directly connect
// your e-stop switch to the Arduino reset pin, since it is the most correct way to do this.
#ifndef ENABLE_SOFTWARE_DEBOUNCE
ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process.
{
// Ignore limit switches if already in an alarm state or in-process of executing an alarm.
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending
// moves in the planner and serial buffers are all cleared and newly sent blocks will be
// locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
// limit setting if their limits are constantly triggering after a reset and move their axes.
if (sys.state != STATE_ALARM) {
if (!(sys_rt_exec_alarm)) {
#ifdef HARD_LIMIT_FORCE_STATE_CHECK
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
#else
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
#endif
}
}
}
#else // OPTIONAL: Software debounce limit pin routine.
// Upon limit pin change, enable watchdog timer to create a short delay.
ISR(LIMIT_INT_vect) { if (!(WDTCSR & (1<<WDIE))) { WDTCSR |= (1<<WDIE); } }
ISR(WDT_vect) // Watchdog timer ISR
{
WDTCSR &= ~(1<<WDIE); // Disable watchdog timer.
if (sys.state != STATE_ALARM) { // Ignore if already in alarm state.
if (!(sys_rt_exec_alarm)) {
ISR(LIMIT_INT_vect) // DEFAULT: Limit pin change interrupt process.
{
// Ignore limit switches if already in an alarm state or in-process of executing an alarm.
// When in the alarm state, Grbl should have been reset or will force a reset, so any pending
// moves in the planner and serial buffers are all cleared and newly sent blocks will be
// locked out until a homing cycle or a kill lock command. Allows the user to disable the hard
// limit setting if their limits are constantly triggering after a reset and move their axes.
if (sys.state != STATE_ALARM) {
if (!(sys_rt_exec_alarm)) {
#ifdef HARD_LIMIT_FORCE_STATE_CHECK
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
}
#else
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
#endif
}
}
#endif
}
// Homes the specified cycle axes, sets the machine position, and performs a pull-off motion after

View File

@ -290,16 +290,6 @@ uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t is_prob
plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared.
plan_sync_position(); // Sync planner position to current machine position.
// TODO: Update the g-code parser code to not require this target calculation but uses a gc_sync_position() call.
// NOTE: The target[] variable updated here will be sent back and synced with the g-code parser.
//!!! This is the problem. Need to set the g-code parser to update the position appropriately.
// - Probe initialization fail: Retain current position.
// - Probe successful: Update new positions across everything, since held before the target.
// - Probe did not contact (alarm or not): Copy original target position as normal
// system_convert_array_steps_to_mpos(target, sys_position);
#ifdef MESSAGE_PROBE_COORDINATES
// All done! Output the probe position as message.
report_probe_parameters();

View File

@ -28,7 +28,7 @@
#ifdef USE_LINE_NUMBERS
#define BLOCK_BUFFER_SIZE 15
#else
#define BLOCK_BUFFER_SIZE 16
#define BLOCK_BUFFER_SIZE 17
#endif
#endif

View File

@ -47,8 +47,11 @@ void protocol_main_loop()
}
#endif
// Check for and report alarm state after a reset, error, or an initial power up.
if (sys.state == STATE_ALARM) {
// NOTE: Sleep mode disables the stepper drivers and position can't be guaranteed.
// Re-initialize the sleep state as an ALARM mode to ensure user homes or acknowledges.
if (sys.state & (STATE_ALARM | STATE_SLEEP)) {
report_feedback_message(MESSAGE_ALARM_LOCK);
sys.state = STATE_ALARM; // Ensure alarm state is set.
} else {
// Check if the safety door is open.
sys.state = STATE_IDLE;
@ -155,7 +158,6 @@ void protocol_main_loop()
protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to main() program loop to reset system.
}
return; /* Never reached */
@ -221,7 +223,7 @@ void protocol_exec_rt_system()
sys.state = STATE_ALARM; // Set system alarm state
report_alarm_message(rt_exec);
// Halt everything upon a critical event flag. Currently hard and soft limits flag this.
if ((rt_exec == EXEC_ALARM_HARD_LIMIT) || (rt_exec == EXEC_ALARM_HARD_LIMIT)) {
if ((rt_exec == EXEC_ALARM_HARD_LIMIT) || (rt_exec == EXEC_ALARM_SOFT_LIMIT)) {
report_feedback_message(MESSAGE_CRITICAL_EVENT);
system_clear_exec_state_flag(EXEC_RESET); // Disable any existing reset
do {
@ -252,25 +254,23 @@ void protocol_exec_rt_system()
// NOTE: Once hold is initiated, the system immediately enters a suspend state to block all
// main program processes until either reset or resumed. This ensures a hold completes safely.
if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)) {
if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR | EXEC_SLEEP)) {
// State check for allowable states for hold methods.
if ((sys.state == STATE_IDLE) ||
(sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD | STATE_SAFETY_DOOR | STATE_JOG))) {
if (!(sys.state & (STATE_ALARM | STATE_CHECK_MODE))) {
// If in CYCLE or JOG states, immediately initiate a motion HOLD.
if (sys.state & (STATE_CYCLE | STATE_JOG)) {
if (!(sys.suspend & (SUSPEND_MOTION_CANCEL | SUSPEND_JOG_CANCEL))) { // Block, if already holding.
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = STEP_CONTROL_EXECUTE_HOLD; // Initiate suspend state with active flag.
if (sys.state == STATE_JOG) { sys.suspend |= SUSPEND_JOG_CANCEL; } // Jog cancelled upon any hold event.
if (sys.state == STATE_JOG) { // Jog cancelled upon any hold event, except for sleeping.
if (!(rt_exec & EXEC_SLEEP)) { sys.suspend |= SUSPEND_JOG_CANCEL; }
}
}
}
// If IDLE, Grbl is not in motion. Simply indicate suspend state and hold is complete.
if (sys.state == STATE_IDLE) {
sys.suspend = SUSPEND_HOLD_COMPLETE;
sys.step_control = STEP_CONTROL_END_MOTION;
}
if (sys.state == STATE_IDLE) { sys.suspend = SUSPEND_HOLD_COMPLETE; }
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
// to halt and cancel the remainder of the motion.
@ -283,9 +283,8 @@ void protocol_exec_rt_system()
// Execute a feed hold with deceleration, if required. Then, suspend system.
if (rt_exec & EXEC_FEED_HOLD) {
// Block SAFETY_DOOR state from prematurely changing back to HOLD, which should only
// occur if the safety door switch closes.
if (!(sys.state & (STATE_SAFETY_DOOR | STATE_JOG))) { sys.state = STATE_HOLD; }
// Block SAFETY_DOOR, JOG, and SLEEP states from changing to HOLD state.
if (!(sys.state & (STATE_SAFETY_DOOR | STATE_JOG | STATE_SLEEP))) { sys.state = STATE_HOLD; }
}
// Execute a safety door stop with a feed hold and disable spindle/coolant.
@ -296,8 +295,8 @@ void protocol_exec_rt_system()
// If jogging, block safety door methods until jog cancel is complete. Just flag that it happened.
if (!(sys.suspend & SUSPEND_JOG_CANCEL)) {
// Check if the safety re-opened during a restore parking motion only. Ignore if
// already retracting or parked.
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
// already retracting, parked or in sleep state.
if (sys.state == STATE_SAFETY_DOOR) {
if (sys.suspend & SUSPEND_INITIATE_RESTORE) { // Actively restoring
#ifdef PARKING_ENABLE
// Set hold and reset appropriate control flags to restart parking sequence.
@ -311,7 +310,7 @@ void protocol_exec_rt_system()
sys.suspend |= SUSPEND_RESTART_RETRACT;
}
}
sys.state = STATE_SAFETY_DOOR;
if (sys.state != STATE_SLEEP) { sys.state = STATE_SAFETY_DOOR; }
}
// NOTE: This flag doesn't change when the door closes, unlike sys.state. Ensures any parking motions
// are executed if the door switch closes and the state returns to HOLD.
@ -320,7 +319,12 @@ void protocol_exec_rt_system()
}
system_clear_exec_state_flag((EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR));
if (rt_exec & EXEC_SLEEP) {
if (sys.state == STATE_ALARM) { sys.suspend |= (SUSPEND_RETRACT_COMPLETE|SUSPEND_HOLD_COMPLETE); }
sys.state = STATE_SLEEP;
}
system_clear_exec_state_flag((EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR | EXEC_SLEEP));
}
// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
@ -363,52 +367,13 @@ void protocol_exec_rt_system()
system_clear_exec_state_flag(EXEC_CYCLE_START);
}
// if (rt_exec & EXEC_CYCLE_START) {
// // Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
// // Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
// if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) {
// // Cycle start only when IDLE or when a hold is complete and ready to resume.
// // NOTE: SAFETY_DOOR is implicitly blocked. It reverts to HOLD when the door is closed.
// if ((sys.state == STATE_IDLE) || ((sys.state & STATE_HOLD) && (sys.suspend & SUSPEND_HOLD_COMPLETE))) {
// if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
// if (sys.suspend & SUSPEND_RETRACT_COMPLETE) {
// if bit_isfalse(sys.suspend,SUSPEND_RESTORE_COMPLETE) {
// // Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
// // NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
// // the retraction execution is complete, which implies the initial feed hold is not active. To
// // restore normal operation, the restore procedures must be initiated by the following flag. Once,
// // they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
// sys.suspend |= SUSPEND_INITIATE_RESTORE;
// } else {
// bit_false(sys.suspend,SUSPEND_SAFETY_DOOR_AJAR);
// }
// }
// }
// if (!(sys.suspend & SUSPEND_SAFETY_DOOR_AJAR)) {
// // Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
// sys.step_control = STEP_CONTROL_NORMAL_OP; // Restore step control to normal operation
// if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) {
// sys.suspend = SUSPEND_DISABLE; // Break suspend state.
// sys.state = STATE_CYCLE;
// st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
// st_wake_up();
// } else { // Otherwise, do nothing. Set and resume IDLE state.
// sys.suspend = SUSPEND_DISABLE; // Break suspend state.
// sys.state = STATE_IDLE;
// }
// }
// }
// }
// system_clear_exec_state_flag(EXEC_CYCLE_START);
// }
if (rt_exec & EXEC_CYCLE_STOP) {
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
// realtime command execution in the main program, ensuring that the planner re-plans safely.
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
if ((sys.state & (STATE_HOLD | STATE_SAFETY_DOOR)) && !(sys.soft_limit) && !(sys.suspend & SUSPEND_JOG_CANCEL)) {
if ((sys.state & (STATE_HOLD|STATE_SAFETY_DOOR|STATE_SLEEP)) && !(sys.soft_limit) && !(sys.suspend & SUSPEND_JOG_CANCEL)) {
// Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
// has issued a resume command or reset.
plan_cycle_reinitialize();
@ -480,6 +445,7 @@ void protocol_exec_rt_system()
last_s_override = max(last_s_override,MIN_SPINDLE_SPEED_OVERRIDE);
if (last_s_override != sys.spindle_speed_ovr) {
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
sys.spindle_speed_ovr = last_s_override;
sys.report_ovr_counter = REPORT_OVR_REFRESH_BUSY_COUNT; // Set to report change immediately
}
@ -534,7 +500,7 @@ void protocol_exec_rt_system()
#endif
// Reload step segment buffer
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_SAFETY_DOOR | STATE_HOMING | STATE_JOG)) {
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_SAFETY_DOOR | STATE_HOMING | STATE_SLEEP| STATE_JOG)) {
st_prep_buffer();
}
@ -586,8 +552,9 @@ static void protocol_exec_rt_suspend()
// Block until initial hold is complete and the machine has stopped motion.
if (sys.suspend & SUSPEND_HOLD_COMPLETE) {
// Safety door manager. Handles de/re-energizing, switch state checks, and parking motions.
if (sys.suspend & SUSPEND_SAFETY_DOOR_AJAR) {
// Parking manager. Handles de/re-energizing, switch state checks, and parking motions for
// the safety door and sleep states.
if (sys.state & (STATE_SAFETY_DOOR | STATE_SLEEP)) {
// Handles retraction motions and de-energizing.
if (bit_isfalse(sys.suspend,SUSPEND_RETRACT_COMPLETE)) {
@ -610,11 +577,12 @@ static void protocol_exec_rt_suspend()
retract_waypoint = min(retract_waypoint,PARKING_TARGET);
}
// Execute slow pull-out parking retract motion. Parking requires homing enabled and
// the current location not exceeding the parking target location.
// Execute slow pull-out parking retract motion. Parking requires homing enabled, the
// current location not exceeding the parking target location, and laser mode disabled.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
if ((bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) &&
(parking_target[PARKING_AXIS] < PARKING_TARGET)) {
(parking_target[PARKING_AXIS] < PARKING_TARGET) &&
bit_isfalse(settings.flags,BITFLAG_LASER_MODE)) {
// Retract spindle by pullout distance. Ensure retraction motion moves away from
// the workpiece and waypoint motion doesn't exceed the parking target location.
@ -637,6 +605,7 @@ static void protocol_exec_rt_suspend()
} else {
// Parking motion not possible. Just disable the spindle and coolant.
// NOTE: Laser mode does not start a parking motion to ensure the laser stops immediately.
spindle_stop(); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
@ -649,8 +618,18 @@ static void protocol_exec_rt_suspend()
} else {
if (sys.state == STATE_SLEEP) {
report_feedback_message(MESSAGE_SLEEP_MODE);
// Spindle and coolant should already be stopped, but do it again just to be sure.
spindle_stop(); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
st_go_idle(); // Disable steppers
while (!(sys.abort)) { protocol_exec_rt_system(); } // Do nothing until reset.
return; // Abort received. Return to re-initialize.
}
// Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to resume.
// NOTE: This unlocks the SAFETY_DOOR state to a HOLD state, such that CYCLE_START can activate a resume.
if (sys.state == STATE_SAFETY_DOOR) {
if (!(system_check_safety_door_ajar())) {
sys.suspend &= ~(SUSPEND_SAFETY_DOOR_AJAR); // Reset door ajar flag to denote ready to resume.
@ -663,7 +642,7 @@ static void protocol_exec_rt_suspend()
#ifdef PARKING_ENABLE
// Execute fast restore motion to the pull-out position. Parking requires homing enabled.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
if ((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) {
// Check to ensure the motion doesn't move below pull-out position.
if (parking_target[PARKING_AXIS] <= PARKING_TARGET) {
parking_target[PARKING_AXIS] = retract_waypoint;
@ -677,13 +656,19 @@ static void protocol_exec_rt_suspend()
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SYS_SUSPEND);
if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
// When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
} else {
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SYS_SUSPEND);
}
}
}
if (gc_state.modal.coolant != COOLANT_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
// NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this pin.
coolant_set_state((restore_condition & (PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_FLOOD)));
delay_sec(SAFETY_DOOR_COOLANT_DELAY, DELAY_MODE_SYS_SUSPEND);
}
@ -691,14 +676,14 @@ static void protocol_exec_rt_suspend()
#ifdef PARKING_ENABLE
// Execute slow plunge motion from pull-out position to resume position.
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
if ((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend,SUSPEND_RESTART_RETRACT)) {
// Regardless if the retract parking motion was a valid/safe motion or not, the
// restore parking motion should logically be valid, either by returning to the
// original position through valid machine space or by not moving at all.
pl_data->feed_rate = PARKING_PULLOUT_RATE;
mc_parking_motion(parking_target, pl_data);
mc_parking_motion(restore_target, pl_data);
}
}
#endif
@ -728,8 +713,13 @@ static void protocol_exec_rt_suspend()
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
report_feedback_message(MESSAGE_SPINDLE_RESTORE);
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SYS_SUSPEND);
if (bit_istrue(settings.flags,BITFLAG_LASER_MODE)) {
// When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
} else {
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SYS_SUSPEND);
}
}
if (sys.toggle_ovr_mask & TOGGLE_OVR_STOP_RESTORE_CYCLE) {
system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.

View File

@ -231,6 +231,8 @@ void report_feedback_message(uint8_t message_code)
printPgmString(PSTR("Restoring defaults")); break;
case MESSAGE_SPINDLE_RESTORE:
printPgmString(PSTR("Restoring spindle")); break;
case MESSAGE_SLEEP_MODE:
printPgmString(PSTR("Sleeping")); break;
}
report_util_feedback_line_feed();
}
@ -245,7 +247,7 @@ void report_init_message()
// Grbl help message
void report_grbl_help() {
#ifdef REPORT_GUI_MODE
printPgmString(PSTR("[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $C $X $H ~ ! ? ctrl-x]\r\n"));
printPgmString(PSTR("[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H ~ ! ? ctrl-x]\r\n"));
#else
printPgmString(PSTR("$$ (view Grbl settings)\r\n"
"$# (view # parameters)\r\n"
@ -255,6 +257,7 @@ void report_grbl_help() {
"$x=value (save Grbl setting)\r\n"
"$Nx=line (save startup block)\r\n"
"$J=line (jog)\r\n"
"$SLP (sleep mode)\r\n"
"$C (check gcode mode)\r\n"
"$X (kill alarm lock)\r\n"
"$H (run homing cycle)\r\n"
@ -571,6 +574,7 @@ void report_realtime_status()
else { printPgmString(PSTR("<Hold")); }
}
break;
case STATE_SLEEP: printPgmString(PSTR("<Sleep")); break;
}
// If reporting a position, convert the current step count (current_position) to millimeters.
@ -681,22 +685,23 @@ void report_realtime_status()
system_convert_array_steps_to_mpos(print_position,current_position);
// Report current machine state and sub-states
serial_write('<');
switch (sys.state) {
case STATE_IDLE: printPgmString(PSTR("<Idle")); break;
case STATE_CYCLE: printPgmString(PSTR("<Run")); break;
case STATE_IDLE: printPgmString(PSTR("Idle")); break;
case STATE_CYCLE: printPgmString(PSTR("Run")); break;
case STATE_HOLD:
if (!(sys.suspend & SUSPEND_JOG_CANCEL)) {
printPgmString(PSTR("<Hold:"));
printPgmString(PSTR("Hold:"));
if (sys.suspend & SUSPEND_HOLD_COMPLETE) { serial_write('0'); } // Ready to resume
else { serial_write('1'); } // Actively holding
break;
} // Continues to print jog state during jog cancel.
case STATE_JOG: printPgmString(PSTR("<Jog")); break;
case STATE_HOMING: printPgmString(PSTR("<Home")); break;
case STATE_ALARM: printPgmString(PSTR("<Alarm")); break;
case STATE_CHECK_MODE: printPgmString(PSTR("<Check")); break;
case STATE_JOG: printPgmString(PSTR("Jog")); break;
case STATE_HOMING: printPgmString(PSTR("Home")); break;
case STATE_ALARM: printPgmString(PSTR("Alarm")); break;
case STATE_CHECK_MODE: printPgmString(PSTR("Check")); break;
case STATE_SAFETY_DOOR:
printPgmString(PSTR("<Door:"));
printPgmString(PSTR("Door:"));
if (sys.suspend & SUSPEND_INITIATE_RESTORE) {
serial_write('3'); // Restoring
} else {
@ -711,10 +716,7 @@ void report_realtime_status()
}
}
break;
// case STATE_SLEEP: printPgmString(PSTR("<Sleep:")); // [Grbl-Mega Only]
// if (sys.suspend & SUSPEND_RETRACT_COMPLETE) { printPgmString(PSTR("0")); } // Parked
// else { printPgmString(PSTR("1")); } // Actively holding and retracting
// break;
case STATE_SLEEP: printPgmString(PSTR("Sleep")); break;
}
float wco[N_AXIS];
@ -730,6 +732,7 @@ void report_realtime_status()
}
}
// Report machine position
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE)) {
printPgmString(PSTR("|MPos:"));
} else {
@ -737,20 +740,6 @@ void report_realtime_status()
}
report_util_axis_values(print_position);
// Report machine position
// if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE)) {
// printPgmString(PSTR("|MPos:"));
// } else {
// // Report work position
// printPgmString(PSTR("|WPos:"));
// for (idx=0; idx< N_AXIS; idx++) {
// // Apply work coordinate offsets and tool length offset to current position.
// print_position[idx] -= gc_state.coord_system[idx]+gc_state.coord_offset[idx];
// if (idx == TOOL_LENGTH_OFFSET_AXIS) { print_position[idx] -= gc_state.tool_length_offset; }
// }
// }
// report_util_axis_values(print_position);
// Returns planner and serial read buffer states.
#ifdef REPORT_FIELD_BUFFER_STATE
if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_BUFFER_STATE)) {
@ -815,23 +804,6 @@ void report_realtime_status()
printPgmString(PSTR("|WCO:"));
report_util_axis_values(wco);
}
// if (sys.report_wco_counter++ >= REPORT_WCO_REFRESH_BUSY_COUNT) {
// if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
// sys.report_wco_counter = 1; // Reset counter for slow refresh
// } else { sys.report_wco_counter = (REPORT_WCO_REFRESH_BUSY_COUNT-REPORT_WCO_REFRESH_IDLE_COUNT+1); }
// if (sys.report_ovr_counter >= REPORT_OVR_REFRESH_BUSY_COUNT) {
// sys.report_ovr_counter = (REPORT_OVR_REFRESH_BUSY_COUNT-1); // Set override on next report.
// }
// printPgmString(PSTR("|WCO:"));
// float axis_offset;
// uint8_t idx;
// for (idx=0; idx<N_AXIS; idx++) {
// axis_offset = gc_state.coord_system[idx]+gc_state.coord_offset[idx];
// if (idx == TOOL_LENGTH_OFFSET_AXIS) { axis_offset += gc_state.tool_length_offset; }
// printFloat_CoordValue(axis_offset);
// if (idx < (N_AXIS-1)) { serial_write(','); }
// }
// }
#endif
#ifdef REPORT_FIELD_OVERRIDES

View File

@ -80,6 +80,7 @@
#define MESSAGE_PROGRAM_END 8
#define MESSAGE_RESTORE_DEFAULTS 9
#define MESSAGE_SPINDLE_RESTORE 10
#define MESSAGE_SLEEP_MODE 11
// Prints system status messages.
void report_status_message(uint8_t status_code);

View File

@ -83,7 +83,6 @@ void serial_init()
// Writes one byte to the TX serial buffer. Called by main program.
// TODO: Check if we can speed this up for writing strings, rather than single bytes.
void serial_write(uint8_t data) {
// Calculate next head
uint8_t next_head = serial_tx_buffer_head + 1;

View File

@ -28,8 +28,6 @@ settings_t settings;
void settings_store_startup_line(uint8_t n, char *line)
{
#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
// TODO: Alter the startup line parsing to prevent motions from being executed before this call.
// Implement it like the jog parsing.
protocol_buffer_synchronize(); // A startup line may contain a motion and be executing.
#endif
uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK;
@ -288,8 +286,8 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
case 25: settings.homing_seek_rate = value; break;
case 26: settings.homing_debounce_delay = int_value; break;
case 27: settings.homing_pulloff = value; break;
case 30: settings.rpm_max = value; break;
case 31: settings.rpm_min = value; break;
case 30: settings.rpm_max = value; spindle_init(); break; // Re-initialize spindle rpm calibration
case 31: settings.rpm_min = value; spindle_init(); break; // Re-initialize spindle rpm calibration
case 32:
#ifdef VARIABLE_SPINDLE
if (int_value) { settings.flags |= BITFLAG_LASER_MODE; }

View File

@ -25,7 +25,7 @@
#ifdef SPINDLE_MINIMUM_PWM
#define SPINDLE_PWM_MIN_VALUE SPINDLE_MINIMUM_PWM
#else
#define SPINDLE_PWM_MIN_VALUE 0
#define SPINDLE_PWM_MIN_VALUE 0.0
#endif
#define SPINDLE_PWM_RANGE (SPINDLE_PWM_MAX_VALUE-SPINDLE_PWM_MIN_VALUE)
@ -63,7 +63,8 @@ void spindle_init()
}
// Stop and start spindle routines. Called by all spindle routines and stepper ISR.
// Stop and start spindle routines. Called by all spindle routines and various interrupts.
// Keep routine small, fast, and efficient.
void spindle_stop()
{
// On the Uno, spindle enable and PWM are shared. Other CPUs have seperate enable pin.
@ -87,6 +88,7 @@ void spindle_stop()
#ifdef VARIABLE_SPINDLE
// Called by spindle state functions and stepper ISR. Keep routine small, fast, and efficient.
void spindle_set_speed(uint8_t pwm_value)
{
if (pwm_value == SPINDLE_PWM_OFF_VALUE) {
@ -102,15 +104,15 @@ void spindle_stop()
SPINDLE_ENABLE_PORT |= (1<<SPINDLE_ENABLE_BIT);
#endif
#endif
}
}
// Called by spindle state functions and step segment generator.
uint8_t spindle_compute_pwm_value(float rpm) // 328p PWM register is 8-bit.
{
// Calculate PWM register value based on rpm max/min settings and programmed rpm.
if ((settings.rpm_min >= settings.rpm_max) || (rpm > settings.rpm_max)) {
if ((settings.rpm_min >= settings.rpm_max) || (rpm >= settings.rpm_max)) {
// No PWM range possible. Set simple on/off spindle control pin state.
return(SPINDLE_PWM_MAX_VALUE);
} else if (rpm < settings.rpm_min) {

View File

@ -232,7 +232,7 @@ void st_go_idle()
// Set stepper driver idle state, disabled or enabled, depending on settings and circumstances.
bool pin_state = false; // Keep enabled.
if (((settings.stepper_idle_lock_time != 0xff) || sys_rt_exec_alarm) && sys.state != STATE_HOMING) {
if (((settings.stepper_idle_lock_time != 0xff) || sys_rt_exec_alarm || sys.state == STATE_SLEEP) && sys.state != STATE_HOMING) {
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
// stop and not drift from residual inertial forces at the end of the last movement.
delay_ms(settings.stepper_idle_lock_time);
@ -739,12 +739,21 @@ void st_prep_buffer()
}
}
#ifdef VARIABLE_SPINDLE
st_prep_block->spindle_pwm = spindle_compute_pwm_value((0.01*sys.spindle_speed_ovr)*pl_block->spindle_speed);
#ifdef VARIABLE_SPINDLE
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); // Force update whenever updating block.
#endif
}
#ifdef VARIABLE_SPINDLE
if (sys.step_control & STEP_CONTROL_UPDATE_SPINDLE_PWM) {
// Configure correct spindle PWM state for block. Updates with planner changes and spindle speed overrides.
if (pl_block->condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)) {
st_prep_block->spindle_pwm = spindle_compute_pwm_value((0.01*sys.spindle_speed_ovr)*pl_block->spindle_speed);
} else { st_prep_block->spindle_pwm = SPINDLE_PWM_OFF_VALUE; }
bit_false(sys.step_control,STEP_CONTROL_UPDATE_SPINDLE_PWM);
}
#endif
// Initialize new segment
segment_t *prep_segment = &segment_buffer[segment_buffer_head];

View File

@ -186,6 +186,10 @@ uint8_t system_execute_line(char *line)
}
} else { return(STATUS_SETTING_DISABLED); }
break;
case 'S' : // Puts Grbl to sleep [IDLE/ALARM]
if ((line[2] != 'L') || (line[3] != 'P') || (line[4] != 0)) { return(STATUS_INVALID_STATEMENT); }
system_set_exec_state_flag(EXEC_SLEEP); // Set to execute sleep mode immediately
break;
case 'I' : // Print or store build info. [IDLE/ALARM]
if ( line[++char_counter] == 0 ) {
settings_read_build_info(line);

View File

@ -35,6 +35,7 @@
#define EXEC_RESET bit(4) // bitmask 00010000
#define EXEC_SAFETY_DOOR bit(5) // bitmask 00100000
#define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000
#define EXEC_SLEEP bit(7) // bitmask 10000000
// Alarm executor codes. Valid values (1-255). Zero is reserved.
#define EXEC_ALARM_HARD_LIMIT 1
@ -79,7 +80,7 @@
#define STATE_HOLD bit(4) // Active feed hold
#define STATE_JOG bit(5) // Jogging mode.
#define STATE_SAFETY_DOOR bit(6) // Safety door is ajar. Feed holds and de-energizes system.
// #define STATE_SLEEP bit(7) // Sleep state. [Grbl-Mega Only]
#define STATE_SLEEP bit(7) // Sleep state.
// Define system suspend flags. Used in various ways to manage suspend states and procedures.
#define SUSPEND_DISABLE 0 // Must be zero.
@ -97,6 +98,7 @@
#define STEP_CONTROL_END_MOTION bit(0)
#define STEP_CONTROL_EXECUTE_HOLD bit(1)
#define STEP_CONTROL_EXECUTE_SYS_MOTION bit(2)
#define STEP_CONTROL_UPDATE_SPINDLE_PWM bit(3)
// Define control pin index for Grbl internal use. Pin maps may change, but these values don't.
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN