diff --git a/README.md b/README.md index 237e980..98b01ed 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## grblHAL ## -Latest build date is 20250102, see the [changelog](changelog.md) for details. +Latest build date is 20250103, see the [changelog](changelog.md) for details. > [!NOTE] > A settings reset will be performed on an update of builds prior to 20241208. Backup and restore of settings is recommended. @@ -93,4 +93,4 @@ G/M-codes not supported by [legacy Grbl](https://github.com/gnea/grbl/wiki) are Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes. --- -20250102 +20250103 diff --git a/changelog.md b/changelog.md index 004b7e6..b3a5487 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,23 @@ ## grblHAL changelog +20250103 + +Core: + +* Added new handling for spindle spin up and coolant start delays. Setting `$394` is for spindle spin up and `$673` for coolant start. +They can be either set to 0 or to a value in the range 0.5 - 20s. The settings are used both for initial delay and for restore delay after cancelling a feed hold or completing a tool change. +> [!NOTE] +> If the spindle supports "at speed" functionality and this is enabled by setting `$340` \(Spindle at speed tolerance\) > 0 then the spin up delay is used as a timeout value before alarm 14 is raised. If `$394` is set to 0 the timeout will default to one minute. + +> [!NOTE] +> Setting `$392` and `$393`, if available, are now only used for spindle spin up delay and coolant start delay respectively when the safety door is closed. + +Plugins: + +* Laser, coolant: updated for internal naming changes, no functional changes. + +--- + 20250102 Core: diff --git a/config.h b/config.h index 0ff7883..6fc7e7f 100644 --- a/config.h +++ b/config.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2020-2024 Terje Io + Copyright (c) 2020-2025 Terje Io grblHAL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1024,6 +1024,14 @@ Useful for some pre-built electronic boards. #endif ///@} +/*! @name $673 - Setting_CoolantOnDelay +*/ +///@{ +#if !defined DEFAULT_COOLANT_ON_DELAY || defined __DOXYGEN__ +#define DEFAULT_COOLANT_ON_DELAY 0 // milliseconds: 0 or 500 - 20000 +#endif +///@} + // Spindle settings (Group_Spindle) /*! @name $9 - Setting_SpindlePWMOptions @@ -1130,6 +1138,14 @@ Default value is 0, meaning spindle sync is disabled #endif ///@} +/*! @name $394 - Setting_SpindleOnDelay +*/ +///@{ +#if !defined DEFAULT_SPINDLE_ON_DELAY || defined __DOXYGEN__ +#define DEFAULT_SPINDLE_ON_DELAY 0 // milliseconds: 0 or 500 - 20000 +#endif +///@} + /*! @name $395 - Setting_SpindleType */ ///@{ @@ -2197,6 +2213,26 @@ __NOTE:__ Must be a positive values. #define DEFAULT_ENABLE_SIGNALS_INVERT_MASK AXES_BITMASK #endif +#if DEFAULT_SPINDLE_ON_DELAY +#if DEFAULT_SPINDLE_ON_DELAY < 500 +#undef DEFAULT_SPINDLE_ON_DELAY +#define DEFAULT_SPINDLE_ON_DELAY 500 +#elif DEFAULT_SPINDLE_ON_DELAY > 20000 +#undef DEFAULT_SPINDLE_ON_DELAY +#define DEFAULT_SPINDLE_ON_DELAY 20000 +#endif +#endif + +#if DEFAULT_COOLANT_ON_DELAY +#if DEFAULT_COOLANT_ON_DELAY < 500 +#undef DEFAULT_COOLANT_ON_DELAY +#define DEFAULT_COOLANT_ON_DELAY 500 +#elif DEFAULT_COOLANT_ON_DELAY > 20000 +#undef DEFAULT_COOLANT_ON_DELAY +#define DEFAULT_COOLANT_ON_DELAY 20000 +#endif +#endif + #if DEFAULT_PARKING_ENABLE > 0 #if DEFAULT_HOMING_FORCE_SET_ORIGIN > 0 #error "DEFAULT_HOMING_FORCE_SET_ORIGIN is not supported with DEFAULT_PARKING_ENABLE at this time." diff --git a/coolant_control.c b/coolant_control.c index 7f5efff..8fdd4f0 100644 --- a/coolant_control.c +++ b/coolant_control.c @@ -3,21 +3,21 @@ Part of grblHAL - Copyright (c) 2016-2023 Terje Io + Copyright (c) 2016-2025 Terje Io Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC - Grbl is free software: you can redistribute it and/or modify + grblHAL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - Grbl is distributed in the hope that it will be useful, + grblHAL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with Grbl. If not, see . + along with grblHAL. If not, see . */ #include @@ -28,24 +28,36 @@ #include "coolant_control.h" #include "state_machine.h" +void coolant_restore (coolant_state_t mode, uint16_t on_delay_ms) +{ + if(mode.value != hal.coolant.get_state().value) { + hal.coolant.set_state(mode); + if(mode.value && on_delay_ms) + delay_sec((float)on_delay_ms / 1000.0f, sys.suspend ? DelayMode_SysSuspend : DelayMode_Dwell); + system_add_rt_report(Report_Coolant); + } +} + // Main program only. Immediately sets flood coolant running state and also mist coolant, // if enabled. Also sets a flag to report an update to a coolant state. // Called by coolant toggle override, parking restore, parking retract, sleep mode, g-code -// parser program end, and g-code parser coolant_sync(). +// parser program end, and g-code parser coolant_set_state_synced(). void coolant_set_state (coolant_state_t mode) { - if (!ABORTED) { // Block during abort. + if(!ABORTED) { // Block during abort. hal.coolant.set_state(mode); - system_add_rt_report(Report_Coolant); // Set to report change immediately + if(mode.value && settings.coolant.on_delay) + delay_sec((float)settings.coolant.on_delay / 1000.0f, DelayMode_Dwell); } } // G-code parser entry-point for setting coolant state. Forces a planner buffer sync and bails // if an abort or check-mode is active. -bool coolant_sync (coolant_state_t mode) +bool coolant_set_state_synced (coolant_state_t mode) { - bool ok = true; - if (state_get() != STATE_CHECK_MODE) { + bool ok; + + if(!(ok = state_get() == STATE_CHECK_MODE)) { if((ok = protocol_buffer_synchronize())) // Ensure coolant changes state when specified in program. coolant_set_state(mode); } diff --git a/coolant_control.h b/coolant_control.h index 81f4aba..e7452e6 100644 --- a/coolant_control.h +++ b/coolant_control.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2017-2024 Terje Io + Copyright (c) 2017-2025 Terje Io Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC grblHAL is free software: you can redistribute it and/or modify @@ -43,9 +43,11 @@ typedef struct { } coolant_settings_t; // Sets the coolant pins according to state specified. -void coolant_set_state(coolant_state_t mode); +void coolant_set_state (coolant_state_t mode); // G-code parser entry-point for setting coolant states. Checks for and executes additional conditions. -bool coolant_sync(coolant_state_t mode); +bool coolant_set_state_synced (coolant_state_t mode); + +void coolant_restore (coolant_state_t mode, uint16_t on_delay_ms); #endif diff --git a/gcode.c b/gcode.c index 8029762..d6c81d6 100644 --- a/gcode.c +++ b/gcode.c @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2017-2024 Terje Io + Copyright (c) 2017-2025 Terje Io Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -552,10 +552,8 @@ bool gc_modal_state_restore (gc_modal_t *copy) copy->auto_restore = false; copy->motion = gc_state.modal.motion; - if(copy->coolant.value != gc_state.modal.coolant.value) { - hal.coolant.set_state(copy->coolant); - delay_sec(settings.safety_door.coolant_on_delay, DelayMode_SysSuspend); - } + if(copy->coolant.value != gc_state.modal.coolant.value) + coolant_restore(copy->coolant, settings.coolant.on_delay); #if N_SYS_SPINDLE > 1 uint_fast8_t idx = N_SYS_SPINDLE; @@ -569,7 +567,7 @@ bool gc_modal_state_restore (gc_modal_t *copy) } while(idx); #else if(!memcmp(©->spindle, &gc_state.modal.spindle, offsetof(spindle_t, hal))) - spindle_restore(gc_state.modal.spindle.hal, copy->spindle.state, copy->spindle.rpm); + spindle_restore(gc_state.modal.spindle.hal, copy->spindle.state, copy->spindle.rpm, settings.spindle.on_delay); #endif memcpy(&gc_state.modal, copy, sizeof(gc_modal_t)); @@ -3177,7 +3175,7 @@ status_code_t gc_execute_block (char *block) if(sspindle->rpm != gc_block.values.s || gc_parser_flags.spindle_force_sync) { if(sspindle->state.on && !gc_parser_flags.laser_is_motion) { sspindle->hal->param->rpm = gc_block.values.s; - spindle_sync(sspindle->hal, sspindle->state, gc_parser_flags.laser_disable ? 0.0f : gc_block.values.s); + spindle_set_state_synced(sspindle->hal, sspindle->state, gc_parser_flags.laser_disable ? 0.0f : gc_block.values.s); } sspindle->rpm = gc_block.values.s; // Update spindle speed state. } @@ -3333,7 +3331,7 @@ status_code_t gc_execute_block (char *block) if(grbl.on_spindle_programmed) grbl.on_spindle_programmed(sys_spindle->hal, gc_block.spindle_modal.state, sys_spindle->rpm, sys_spindle->rpm_mode); - if((spindle_ok = spindle_sync(sys_spindle->hal, gc_block.spindle_modal.state, sys_spindle->rpm))) { + if((spindle_ok = spindle_set_state_synced(sys_spindle->hal, gc_block.spindle_modal.state, sys_spindle->rpm))) { if((sys_spindle->state = sys_spindle->hal->param->state = gc_block.spindle_modal.state).on) sspindle = sys_spindle; } @@ -3355,7 +3353,7 @@ status_code_t gc_execute_block (char *block) if(grbl.on_spindle_programmed) grbl.on_spindle_programmed(sspindle->hal, gc_block.spindle_modal.state, plan_data.spindle.rpm, sspindle->rpm_mode); - if((spindle_ok = spindle_sync(sspindle->hal, gc_block.spindle_modal.state, plan_data.spindle.rpm))) + if((spindle_ok = spindle_set_state_synced(sspindle->hal, gc_block.spindle_modal.state, plan_data.spindle.rpm))) sspindle->state = sspindle->hal->param->state = gc_block.spindle_modal.state; spindle_event = !spindle_ok; @@ -3407,7 +3405,7 @@ status_code_t gc_execute_block (char *block) if (gc_parser_flags.set_coolant && gc_state.modal.coolant.value != gc_block.modal.coolant.value) { // NOTE: Coolant M-codes are modal. Only one command per line is allowed. But, multiple states // can exist at the same time, while coolant disable clears all states. - if(coolant_sync(gc_block.modal.coolant)) + if(coolant_set_state_synced(gc_block.modal.coolant)) gc_state.modal.coolant = gc_block.modal.coolant; } diff --git a/grbl.h b/grbl.h index 28e4498..c7b741b 100644 --- a/grbl.h +++ b/grbl.h @@ -42,7 +42,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20250102 +#define GRBL_BUILD 20250103 #define GRBL_URL "https://github.com/grblHAL" diff --git a/motion_control.c b/motion_control.c index de2e43a..9f79203 100644 --- a/motion_control.c +++ b/motion_control.c @@ -627,7 +627,7 @@ void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_ return; if(canned->spindle_off) - spindle_sync(pl_data->spindle.hal, pl_data->spindle.state, pl_data->spindle.rpm); + spindle_set_state_synced(pl_data->spindle.hal, pl_data->spindle.state, pl_data->spindle.rpm); } pl_data->condition.rapid_motion = On; // Set rapid motion condition flag. diff --git a/ngc_params.c b/ngc_params.c index 1dc03d1..c9c9264 100644 --- a/ngc_params.c +++ b/ngc_params.c @@ -646,6 +646,10 @@ bool ngc_named_param_get (char *name, float *value) name = ngc_name_tolower(name); + // Check if name is supplied, return false if not. + if((*name == '_' ? *(name + 1) : *name) == '\0') + return false; + *value = 0.0f; if(*name == '_') do { @@ -670,34 +674,9 @@ bool ngc_named_param_get (char *name, float *value) bool ngc_named_param_exists (char *name) { - bool ok = false; - uint_fast8_t idx = sizeof(ngc_named_ro_param) / sizeof(ngc_named_ro_param_t); + float value; - name = ngc_name_tolower(name); - - // Check if name is supplied, return false if not. - if((*name == '_' ? *(name + 1) : *name) == '\0') - return false; - - // Check if it is a (read only) predefined parameter. - if(*name == '_') do { - ok = !strcmp(name, ngc_named_ro_param[--idx].name); - } while(idx && !ok); - - // If not predefined attempt to find it. - if(!ok && rw_global_params && strlen(name) <= NGC_MAX_PARAM_LENGTH) { - - void *context = *name == '_' ? NULL : call_context; - ngc_named_rw_param_t *rw_param = rw_global_params; - - while(rw_param) { - if((ok = rw_param->context == context && !strcmp(rw_param->name, name))) - break; - rw_param = rw_param->next; - } - } - - return ok; + return ngc_named_param_get(name, &value); } bool ngc_named_param_set (char *name, float value) diff --git a/settings.c b/settings.c index f817f83..c5f89c1 100644 --- a/settings.c +++ b/settings.c @@ -160,6 +160,8 @@ PROGMEM const settings_t defaults = { .control_disable_pullup.mask = DEFAULT_DISABLE_CONTROL_PINS_PULL_UP_MASK, .spindle.ref_id = DEFAULT_SPINDLE, + .spindle.on_delay = DEFAULT_SPINDLE_ON_DELAY, + .spindle.at_speed_tolerance = DEFAULT_SPINDLE_AT_SPEED_TOLERANCE, .spindle.encoder_spindle = DEFAULT_SPINDLE, .spindle.ppr = DEFAULT_SPINDLE_PPR, @@ -204,6 +206,7 @@ PROGMEM const settings_t defaults = { #endif #endif + .coolant.on_delay = DEFAULT_COOLANT_ON_DELAY, .coolant.invert.flood = DEFAULT_INVERT_COOLANT_FLOOD_PIN, .coolant.invert.mist = DEFAULT_INVERT_COOLANT_MIST_PIN, @@ -1130,6 +1133,31 @@ setting_id_t settings_get_axis_base (setting_id_t id, uint_fast8_t *idx) return *idx < N_AXIS ? base : Setting_SettingsMax; } +static status_code_t set_float (setting_id_t setting, float value) +{ + status_code_t status = Status_OK; + + switch(setting) { + + case Setting_SpindleAtSpeedTolerance: + settings.spindle.at_speed_tolerance = settings.pwm_spindle.at_speed_tolerance = value; + break; + + case Setting_SpindleOnDelay: + settings.spindle.on_delay = (uint16_t)(value * 1000.0f); + break; + + case Setting_CoolantOnDelay: + settings.coolant.on_delay = (uint16_t)(value * 1000.0f); + break; + + default: + break; + } + + return status; +} + static status_code_t set_axis_setting (setting_id_t setting, float value) { uint_fast8_t idx; @@ -1292,10 +1320,22 @@ static float get_float (setting_id_t setting) value = settings.homing.pulloff; break; + case Setting_SpindleAtSpeedTolerance: + value = settings.pwm_spindle.at_speed_tolerance; + break; + case Setting_ToolChangeProbingDistance: value = settings.tool_change.probing_distance; break; + case Setting_SpindleOnDelay: + value = (float)settings.spindle.on_delay / 1000.0f; + break; + + case Setting_CoolantOnDelay: + value = (float)settings.coolant.on_delay / 1000.0f; + break; + default: break; } @@ -1767,7 +1807,7 @@ static bool is_setting_available (const setting_detail_t *setting) break; case Setting_SpindleOnDelay: - available = !hal.signals_cap.safety_door_ajar && spindle_get_count() && !spindle_get_caps(true).at_speed; + available = spindle_get_count(); break; case Setting_AutoReportInterval: @@ -1794,7 +1834,7 @@ static bool is_setting_available (const setting_detail_t *setting) available = hal.homing.get_state != NULL && hal.home_cap.a.mask != 0; break; - case Setting_HoldCoolantOnDelay: + case Setting_CoolantOnDelay: available = !hal.signals_cap.safety_door_ajar && hal.coolant_cap.mask; break; @@ -1987,7 +2027,7 @@ PROGMEM static const setting_detail_t setting_detail[] = { { Setting_AxisAutoSquareOffset, Group_Axis0, "-axis dual axis offset", "mm", Format_Decimal, "-0.000", "-10", "10", Setting_IsExtendedFn, set_axis_setting, get_float, is_setting_available, AXIS_OPTS }, { Setting_AxisHomingFeedRate, Group_Axis0, "-axis homing locate feed rate", axis_rate, Format_Decimal, "###0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_float, is_setting_available, AXIS_OPTS }, { Setting_AxisHomingSeekRate, Group_Axis0, "-axis homing search seek rate", axis_rate, Format_Decimal, "###0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_float, is_setting_available, AXIS_OPTS }, - { Setting_SpindleAtSpeedTolerance, Group_Spindle, "Spindle at speed tolerance", "percent", Format_Decimal, "##0.0", NULL, NULL, Setting_IsExtended, &settings.pwm_spindle.at_speed_tolerance, NULL, is_setting_available }, + { Setting_SpindleAtSpeedTolerance, Group_Spindle, "Spindle at speed tolerance", "percent", Format_Decimal, "##0.0", NULL, NULL, Setting_IsExtendedFn, set_float, get_float, is_setting_available }, { Setting_ToolChangeMode, Group_Toolchange, "Tool change mode", NULL, Format_RadioButtons, "Normal,Manual touch off,Manual touch off @ G59.3,Automatic touch off @ G59.3,Ignore M6", NULL, NULL, Setting_IsExtendedFn, set_tool_change_mode, get_int, NULL }, { Setting_ToolChangeProbingDistance, Group_Toolchange, "Tool change probing distance", "mm", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtendedFn, set_tool_change_probing_distance, get_float, NULL }, { Setting_ToolChangeFeedRate, Group_Toolchange, "Tool change locate feed rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtended, &settings.tool_change.feed_rate, NULL, NULL }, @@ -2004,10 +2044,10 @@ PROGMEM static const setting_detail_t setting_detail[] = { { Settings_RotaryAxes, Group_Stepper, "Rotary axes", NULL, Format_Bitfield, rotary_axes, NULL, NULL, Setting_IsExtendedFn, set_rotary_axes, get_int, NULL }, #endif #ifndef NO_SAFETY_DOOR_SUPPORT - { Setting_DoorSpindleOnDelay, Group_SafetyDoor, "Spindle on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtended, &settings.safety_door.spindle_on_delay, NULL, is_setting_available }, - { Setting_DoorCoolantOnDelay, Group_SafetyDoor, "Coolant on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtended, &settings.safety_door.coolant_on_delay, NULL, is_setting_available }, + { Setting_DoorSpindleOnDelay, Group_SafetyDoor, "Spindle on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtended, &settings.safety_door.spindle_on_delay, NULL, is_setting_available, { .allow_null = On } }, + { Setting_DoorCoolantOnDelay, Group_SafetyDoor, "Coolant on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtended, &settings.safety_door.coolant_on_delay, NULL, is_setting_available, { .allow_null = On } }, #endif - { Setting_SpindleOnDelay, Group_Spindle, "Spindle on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtended, &settings.safety_door.spindle_on_delay, NULL, is_setting_available }, + { Setting_SpindleOnDelay, Group_Spindle, "Spindle on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtendedFn, set_float, get_float, is_setting_available, { .allow_null = On } }, { Setting_SpindleType, Group_Spindle, "Default spindle", NULL, Format_RadioButtons, spindle_types, NULL, NULL, Setting_IsExtendedFn, set_default_spindle, get_int, is_setting_available }, { Setting_PlannerBlocks, Group_General, "Planner buffer blocks", NULL, Format_Int16, "####0", "30", "1000", Setting_IsExtended, &settings.planner_buffer_blocks, NULL, NULL, { .reboot_required = On } }, { Setting_AutoReportInterval, Group_General, "Autoreport interval", "ms", Format_Int16, "###0", "100", "1000", Setting_IsExtendedFn, set_report_interval, get_int, NULL, { .reboot_required = On, .allow_null = On } }, @@ -2025,7 +2065,7 @@ PROGMEM static const setting_detail_t setting_detail[] = { #endif { Setting_FSOptions, Group_General, "File systems options", NULL, Format_Bitfield, fs_options, NULL, NULL, Setting_IsExtended, &settings.fs_options.mask, NULL, is_setting_available }, { Setting_HomePinsInvertMask, Group_Limits, "Invert home inputs", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.home_invert.mask, NULL, is_setting_available }, - { Setting_HoldCoolantOnDelay, Group_Coolant, "Coolant on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtended, &settings.safety_door.coolant_on_delay, NULL, is_setting_available } + { Setting_CoolantOnDelay, Group_Coolant, "Coolant on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtendedFn, set_float, get_float, is_setting_available, { .allow_null = On } } }; #ifndef NO_SETTINGS_DESCRIPTIONS @@ -2169,7 +2209,8 @@ PROGMEM static const setting_descr_t setting_descr[] = { { Setting_AxisHomingFeedRate, "Feed rate to slowly engage limit switch to determine its location accurately." }, { Setting_AxisHomingSeekRate, "Seek rate to quickly find the limit switch before the slower locating phase." }, { Setting_SpindleAtSpeedTolerance, "Spindle at speed tolerance as percentage deviation from programmed speed, set to 0 to disable.\\n" - "If not within tolerance when checked after spindle on delay ($392) alarm 14 is raised." + "If not within tolerance after timeout set by spindle on delay ($394) alarm 14 is raised.\\n" + "NOTE: if the spindle on delay is set to 0 the timeout defaults to one minute." }, { Setting_ToolChangeMode, "Normal: allows jogging for manual touch off. Set new position manually.\\n\\n" "Manual touch off: retracts tool axis to home position for tool change, use jogging or $TPW for touch off.\\n\\n" @@ -2192,12 +2233,12 @@ PROGMEM static const setting_descr_t setting_descr[] = { { Settings_RotaryAxes, "Designates axes as rotary, interpretation some other relevant axis settings is changed accordingly." }, #endif #ifndef NO_SAFETY_DOOR_SUPPORT - { Setting_DoorSpindleOnDelay, "Delay to allow spindle to spin up after safety door is opened or feed hold is canceled." }, - { Setting_DoorCoolantOnDelay, "Delay to allow coolant to restart after safety door is opened or feed hold is canceled." }, -#else - { Setting_DoorSpindleOnDelay, "Delay to allow spindle to spin up when spindle at speed tolerance is > 0." }, + { Setting_DoorSpindleOnDelay, "Delay to allow spindle to spin up after safety door is opened." }, + { Setting_DoorCoolantOnDelay, "Delay to allow coolant to restart after safety door is opened." }, #endif - { Setting_SpindleOnDelay, "Delay to allow spindle to restart after feed hold is canceled." }, + { Setting_SpindleOnDelay, "Delay to allow spindle to spin up. 0 or 0.5 - 20s\\n" + "If spindle supports ""at speed"" functionality it is the time to wait before alarm 14 is raised." + }, { Setting_SpindleType, "Spindle selected on startup." }, { Setting_PlannerBlocks, "Number of blocks in the planner buffer." }, { Setting_AutoReportInterval, "Interval the real time report will be sent, set to 0 to disable." }, @@ -2219,7 +2260,7 @@ PROGMEM static const setting_descr_t setting_descr[] = { #endif { Setting_FSOptions, "Auto mount SD card on startup." }, { Setting_HomePinsInvertMask, "Inverts the axis home input signals." }, - { Setting_HoldCoolantOnDelay, "Delay to allow coolant to restart after feed hold is canceled." } + { Setting_CoolantOnDelay, "Delay to allow coolant to start. 0 or 0.5 - 20s" } }; #endif @@ -3221,9 +3262,19 @@ void settings_init (void) details->on_changed(&settings, changed); } while((details = details->next)); - if(!settings.flags.settings_downgrade && settings.version.build != (GRBL_BUILD - 20000000UL)) { + + if(settings.version.build <= 250102) { + settings.spindle.on_delay = settings.safety_door.spindle_on_delay * 1000.0f; + settings.coolant.on_delay = settings.safety_door.coolant_on_delay * 1000.0f; + if((changed.spindle = settings.spindle.at_speed_tolerance != settings.pwm_spindle.at_speed_tolerance)) { + settings.spindle.at_speed_tolerance = settings.pwm_spindle.at_speed_tolerance; + hal.settings_changed(&settings, changed); + } + } + settings.version.build = (GRBL_BUILD - 20000000UL); + settings_write_global(); } diff --git a/settings.h b/settings.h index d9c6c31..4c5f703 100644 --- a/settings.h +++ b/settings.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2017-2024 Terje Io + Copyright (c) 2017-2025 Terje Io Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -249,20 +249,20 @@ typedef enum { Settings_ModBus_RXTimeout = 375, Settings_RotaryAxes = 376, Setting_BlueToothInitOK = 377, - Setting_CoolantOnDelay = 378, - Setting_CoolantOffDelay = 379, - Setting_CoolantMinTemp = 380, - Setting_CoolantMaxTemp = 381, - Setting_CoolantOffset = 382, - Setting_CoolantGain = 383, + Setting_LaserCoolantOnDelay = 378, + Setting_LaserCoolantOffDelay = 379, + Setting_LaserCoolantMinTemp = 380, + Setting_LaserCoolantMaxTemp = 381, + Setting_LaserCoolantOffset = 382, + Setting_LaserCoolantGain = 383, Setting_DisableG92Persistence = 384, Setting_BlueToothStateInput = 385, Setting_FanPort0 = 386, Setting_FanPort1 = 387, Setting_FanPort2 = 388, Setting_FanPort3 = 389, - Setting_CoolantTempPort = 390, - Setting_CoolantOkPort = 391, + Setting_LaserCoolantTempPort = 390, + Setting_LaserCoolantOkPort = 391, Setting_DoorSpindleOnDelay = 392, Setting_DoorCoolantOnDelay = 393, Setting_SpindleOnDelay = 394, // made available if safety door input not provided @@ -450,7 +450,7 @@ typedef enum { Setting_HomePinsInvertMask = 671, Setting_Reserved672 = 672, - Setting_HoldCoolantOnDelay = 673, // made available if safety door input not provided + Setting_CoolantOnDelay = 673, Setting_SpindleInvertMask1 = 716, @@ -628,9 +628,9 @@ typedef union { } safety_door_setting_flags_t; typedef struct { - safety_door_setting_flags_t flags; - float spindle_on_delay; - float coolant_on_delay; + safety_door_setting_flags_t flags; // TODO: move to last element in next revision + float spindle_on_delay; // TODO: change to uint16_t in next revision + float coolant_on_delay; // TODO: change to uint16_t in next revision } safety_door_settings_t; typedef union { diff --git a/spindle_control.c b/spindle_control.c index 57936bc..da08a88 100644 --- a/spindle_control.c +++ b/spindle_control.c @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2017-2024 Terje Io + Copyright (c) 2017-2025 Terje Io Copyright (c) 2012-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -551,14 +551,14 @@ void spindle_set_override (spindle_ptrs_t *spindle, override_t speed_override) } /*! \internal \brief Immediately sets spindle running state with direction and spindle rpm, if enabled. -Called by g-code parser spindle_sync(), parking retract and restore, g-code program end, +Called by g-code parser spindle_set_state_synced(), parking retract and restore, g-code program end, sleep, and spindle stop override. \param spindle pointer to a \ref spindle_ptrs_t structure. \param state a \ref spindle_state_t structure. \param rpm the spindle RPM to set. \returns \a true if successful, \a false if the current controller state is \ref ABORTED. */ -static bool set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm) +bool spindle_set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm) { if (!ABORTED) { // Block during abort. @@ -585,18 +585,45 @@ static bool set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm return !ABORTED; } - -/*! \brief Immediately sets spindle running state with direction and spindle rpm, if enabled. -Called by g-code parser spindle_sync(), parking retract and restore, g-code program end, -sleep, and spindle stop override. +/*! \brief If the spindle supports at speed functionality it will wait +for it to reach the speed and raise an alarm if the speed is not reached within the timeout period. \param spindle pointer to a \ref spindle_ptrs_t structure. \param state a \ref spindle_state_t structure. \param rpm the spindle RPM to set. \returns \a true if successful, \a false if the current controller state is \ref ABORTED. */ -bool spindle_set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm) +static bool spindle_set_state_wait (spindle_ptrs_t *spindle, spindle_state_t state, float rpm, uint16_t on_delay_ms, delaymode_t delay_mode) { - return set_state(spindle, state, rpm); + bool ok; + + if(!(ok = state_get() == STATE_CHECK_MODE)) { + + if((ok = spindle_set_state(spindle, state, rpm))) { + + bool at_speed = !state.on || !spindle->cap.at_speed || spindle->at_speed_tolerance <= 0.0f; + + if(at_speed) + ok = on_delay_ms == 0 || delay_sec((float)on_delay_ms / 1000.0f, delay_mode); + else { + uint16_t delay = 0; + if(on_delay_ms == 0) + on_delay_ms = 60000; // one minute... + while(!(at_speed = spindle->get_state(spindle).at_speed)) { + if(!delay_sec(0.2f, delay_mode)) + break; + delay += 200; + if(delay > on_delay_ms) { + gc_spindle_off(); + system_raise_alarm(Alarm_Spindle); + break; + } + } + ok &= at_speed; + } + } + } + + return ok; } /*! \brief G-code parser entry-point for setting spindle state. Forces a planner buffer sync and bails @@ -607,33 +634,10 @@ for it to reach the speed and raise an alarm if the speed is not reached within \param rpm the spindle RPM to set. \returns \a true if successful, \a false if the current controller state is \ref ABORTED. */ -bool spindle_sync (spindle_ptrs_t *spindle, spindle_state_t state, float rpm) +bool spindle_set_state_synced (spindle_ptrs_t *spindle, spindle_state_t state, float rpm) { - bool ok; - - if (!(ok = state_get() == STATE_CHECK_MODE)) { - - bool at_speed = !state.on || !spindle->cap.at_speed || spindle->at_speed_tolerance <= 0.0f; - - // Empty planner buffer to ensure spindle is set when programmed. - if((ok = protocol_buffer_synchronize()) && set_state(spindle, state, rpm) && !at_speed) { - float on_delay = 0.0f; - while(!(at_speed = spindle->get_state(spindle).at_speed)) { - if(!(ok = delay_sec(0.2f, DelayMode_Dwell))) - break; - on_delay += 0.2f; - if(!(ok = on_delay < settings.safety_door.spindle_on_delay)) { - gc_spindle_off(); - system_raise_alarm(Alarm_Spindle); - break; - } - } - } - - ok &= at_speed; - } - - return ok; + // Empty planner buffer to ensure spindle is set when programmed. + return protocol_buffer_synchronize() && spindle_set_state_wait(spindle, state, rpm, settings.spindle.on_delay, DelayMode_Dwell); } /*! \brief Restore spindle running state with direction, enable, spindle RPM and appropriate delay. @@ -642,31 +646,14 @@ bool spindle_sync (spindle_ptrs_t *spindle, spindle_state_t state, float rpm) \param rpm the spindle RPM to set. \returns \a true if successful, \a false if the current controller state is \ref ABORTED. */ -bool spindle_restore (spindle_ptrs_t *spindle, spindle_state_t state, float rpm) +bool spindle_restore (spindle_ptrs_t *spindle, spindle_state_t state, float rpm, uint16_t on_delay_ms) { - bool ok = true; + bool ok; - if(spindle->cap.laser) // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts. + if((ok = spindle->cap.laser)) // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts. sys.step_control.update_spindle_rpm = On; - else { // TODO: add check for current spindle state matches restore state? - spindle_set_state(spindle, state, rpm); - if(state.on) { - if((ok = !spindle->cap.at_speed)) - ok = delay_sec(settings.safety_door.spindle_on_delay, DelayMode_SysSuspend); - else if((ok == (spindle->at_speed_tolerance <= 0.0f))) { - float delay = 0.0f; - while(!(ok = spindle->get_state(spindle).at_speed)) { - if(!(ok = delay_sec(0.1f, DelayMode_SysSuspend))) - break; - delay += 0.1f; - if(!(ok = delay < settings.safety_door.spindle_on_delay)) { - system_raise_alarm(Alarm_Spindle); - break; - } - } - } - } - } + else if(!(ok = state.value == spindle->get_state(spindle).value)) + ok = spindle_set_state_wait(spindle, state, rpm, on_delay_ms, DelayMode_SysSuspend); return ok; } diff --git a/spindle_control.h b/spindle_control.h index 08dda1b..c8c5808 100644 --- a/spindle_control.h +++ b/spindle_control.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2017-2024 Terje Io + Copyright (c) 2017-2025 Terje Io Copyright (c) 2012-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -340,17 +340,17 @@ typedef bool (*spindle_enumerate_callback_ptr)(spindle_info_t *spindle, void *da void spindle_set_override (spindle_ptrs_t *spindle, override_t speed_override); -// Called by g-code parser when setting spindle state and requires a buffer sync. -bool spindle_sync (spindle_ptrs_t *spindle, spindle_state_t state, float rpm); - // Sets spindle running state with direction, enable, and spindle RPM. bool spindle_set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm); +// Called by g-code parser when setting spindle state and requires a buffer sync. +bool spindle_set_state_synced (spindle_ptrs_t *spindle, spindle_state_t state, float rpm); + // Spindle speed calculation and limit handling float spindle_set_rpm (spindle_ptrs_t *spindle, float rpm, override_t speed_override); // Restore spindle running state with direction, enable, spindle RPM and appropriate delay. -bool spindle_restore (spindle_ptrs_t *spindle, spindle_state_t state, float rpm); +bool spindle_restore (spindle_ptrs_t *spindle, spindle_state_t state, float rpm, uint16_t on_delay_ms); void spindle_all_off (void); diff --git a/state_machine.c b/state_machine.c index 50d2f4f..f160cf7 100644 --- a/state_machine.c +++ b/state_machine.c @@ -5,7 +5,7 @@ Part of grblHAL - Copyright (c) 2018-2024 Terje Io + Copyright (c) 2018-2025 Terje Io Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -74,41 +74,32 @@ typedef struct { // Declare and initialize parking local variables static parking_data_t park = {0}; -static void state_spindle_restore (spindle_t *spindle) +static void state_spindle_restore (spindle_t *spindle, uint16_t on_delay_ms) { if(spindle->hal) { if(grbl.on_spindle_programmed) grbl.on_spindle_programmed(spindle->hal, spindle->state, spindle->rpm, spindle->rpm_mode); - spindle_restore(spindle->hal, spindle->state, spindle->rpm); - } -} - -static void state_spindle_set_state (spindle_t *spindle) -{ - if(spindle->hal) { - if(grbl.on_spindle_programmed) - grbl.on_spindle_programmed(spindle->hal, spindle->state, spindle->rpm, spindle->rpm_mode); - spindle_set_state(spindle->hal, spindle->state, spindle->rpm); + spindle_restore(spindle->hal, spindle->state, spindle->rpm, on_delay_ms); } } static void state_restore_conditions (restore_condition_t *condition) { - if (!settings.parking.flags.enabled || !park.flags.restart) { + if(!settings.parking.flags.enabled || !park.flags.restart) { spindle_num_t spindle_num = N_SYS_SPINDLE; park.flags.restoring = On; // do { - state_spindle_restore(&condition->spindle[--spindle_num]); + state_spindle_restore(&condition->spindle[--spindle_num], (uint16_t)(settings.safety_door.spindle_on_delay * 1000.0f)); } while(spindle_num); // Block if safety door re-opened during prior restore actions. - if (gc_state.modal.coolant.value != hal.coolant.get_state().value) { + if(gc_state.modal.coolant.value != hal.coolant.get_state().value) { // NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this signal. - gc_coolant(condition->coolant);; - delay_sec(settings.safety_door.coolant_on_delay, DelayMode_SysSuspend); + coolant_restore(condition->coolant, (uint16_t)(settings.safety_door.coolant_on_delay * 1000.0f)); + gc_coolant(condition->coolant); } park.flags.restoring = Off; @@ -361,10 +352,10 @@ void state_suspend_manager (void) if (stateHandler != state_await_resume || !gc_spindle_get(0)->state.on) return; - if (sys.override.spindle_stop.value) { + if(sys.override.spindle_stop.value) { // Handles beginning of spindle stop - if (sys.override.spindle_stop.initiate) { + if(sys.override.spindle_stop.initiate) { sys.override.spindle_stop.value = 0; // Clear stop override state if(grbl.on_spindle_programmed) grbl.on_spindle_programmed(restore_condition.spindle[restore_condition.spindle_num].hal, (spindle_state_t){0}, 0.0f, 0); @@ -375,20 +366,17 @@ void state_suspend_manager (void) } // Handles restoring of spindle state - if (sys.override.spindle_stop.restore) { + if(sys.override.spindle_stop.restore) { grbl.report.feedback_message(Message_SpindleRestore); - if (restore_condition.spindle[restore_condition.spindle_num].hal->cap.laser) // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts. - sys.step_control.update_spindle_rpm = On; - else - state_spindle_set_state(&restore_condition.spindle[restore_condition.spindle_num]); + state_spindle_restore(&restore_condition.spindle[restore_condition.spindle_num], settings.spindle.on_delay); sys.override.spindle_stop.value = 0; // Clear stop override state if(grbl.on_override_changed) grbl.on_override_changed(OverrideChanged_SpindleState); } - } else if (sys.step_control.update_spindle_rpm && restore_condition.spindle[0].hal->get_state(restore_condition.spindle[0].hal).on) { + } else if(sys.step_control.update_spindle_rpm && restore_condition.spindle[0].hal->get_state(restore_condition.spindle[0].hal).on) { // Handles spindle state during hold. NOTE: Spindle speed overrides may be altered during hold state. - state_spindle_set_state(&restore_condition.spindle[restore_condition.spindle_num]); + state_spindle_restore(&restore_condition.spindle[restore_condition.spindle_num], settings.spindle.on_delay); sys.step_control.update_spindle_rpm = Off; } } @@ -669,13 +657,13 @@ static void state_await_resume (uint_fast16_t rt_exec) if (restore_condition.spindle[restore_condition.spindle_num].state.on != restore_condition.spindle[restore_condition.spindle_num].hal->get_state(restore_condition.spindle[restore_condition.spindle_num].hal).on) { grbl.report.feedback_message(Message_SpindleRestore); - state_spindle_restore(&restore_condition.spindle[restore_condition.spindle_num]); + state_spindle_restore(&restore_condition.spindle[restore_condition.spindle_num], settings.spindle.on_delay); } if (restore_condition.coolant.value != hal.coolant.get_state().value) { // NOTE: Laser mode will honor this delay. An exhaust system is often controlled by coolant signals. + coolant_restore(restore_condition.coolant, settings.coolant.on_delay); gc_coolant(restore_condition.coolant); - delay_sec(settings.safety_door.coolant_on_delay, DelayMode_SysSuspend); } sys.override.spindle_stop.value = 0; // Clear spindle stop override states diff --git a/tool_change.c b/tool_change.c index d2ea925..e179a2a 100644 --- a/tool_change.c +++ b/tool_change.c @@ -5,7 +5,7 @@ Part of grblHAL - Copyright (c) 2020-2024 Terje Io + Copyright (c) 2020-2025 Terje Io grblHAL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -141,12 +141,11 @@ static bool restore (void) if(protocol_buffer_synchronize()) { - sync_position(); - coolant_sync(gc_state.modal.coolant); + coolant_restore(gc_state.modal.coolant, settings.coolant.on_delay); spindle_t *spindle = gc_spindle_get(-1); - spindle_restore(spindle->hal, spindle->state, spindle->rpm); + spindle_restore(spindle->hal, spindle->state, spindle->rpm, settings.spindle.on_delay); if(!settings.flags.no_restore_position_after_M6) { previous.values[plane.axis_linear] += gc_get_offset(plane.axis_linear, false);