diff --git a/README.md b/README.md
index 0df5071..178c209 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
## grblHAL ##
-Latest build date is 20250216, see the [changelog](changelog.md) for details.
+Latest build date is 20250220, 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.
@@ -89,4 +89,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.
---
-20250216
+20250220
diff --git a/changelog.md b/changelog.md
index 97e46f2..318c527 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,10 +1,37 @@
## grblHAL changelog
+Build 20250220
+
+Core:
+
+* Changed `$65` setting to flags for allowing feed rate overrides during probing and limiting probe motion to be within machine workspace.
+Ref. ioSender issue [#402](https://github.com/terjeio/ioSender/issues/402).
+
+* Fixed regression, ref. discussion [#610](https://github.com/grblHAL/core/issues/610#issuecomment-2670224036).
+
+* Improved handling of stop realtime command \(`0x19`\) for faster motion halt without losing position.
+
+* Optimized RAM usage.
+
+Drivers:
+
+* Networking capable: updated for networking plugin changes.
+
+Plugins:
+
+* Keypad: now sends `$H` and `$X` commands directly to the system command parser bypassing status checks. Ref. issue [#12](https://github.com/grblHAL/Plugin_keypad/issues/12).
+
+* Networking: added network status changed event, refactored top level code to allow multiple interfaces. Added `$NETIF` command for listing interfaces with associated IP and MAC addresses.
+
+* WebUI: updated for networking plugin changes.
+
+---
+
20250216
Core:
-* Fixed issue with restoring spindle status for spindles making use optional spindle status flags. Ref. PR [#673](https://github.com/grblHAL/core/pull/673);
+* Fixed issue with restoring spindle status for spindles making use optional spindle status flags. Ref. PR [#680](https://github.com/grblHAL/core/pull/680).
Drivers:
diff --git a/config.h b/config.h
index b7683a6..6d3fb7b 100644
--- a/config.h
+++ b/config.h
@@ -1642,7 +1642,7 @@ are used the logic of the input signals should be be inverted with the \ref axis
#endif
///@}
-/*! @name $65 - Setting_ProbingFeedOverride
+/*! @name $65 - Setting_ProbingFlags
// By default, grblHAL disables feed rate overrides for all G38.x probe cycle commands. Although this
// may be different than some pro-class machine control, it's arguable that it should be this way.
// Most probe sensors produce different levels of error that is dependent on rate of speed. By
@@ -1653,6 +1653,9 @@ are used the logic of the input signals should be be inverted with the \ref axis
#if !defined DEFAULT_ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES || defined __DOXYGEN__
#define DEFAULT_ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES Off
#endif
+#if !defined DEFAULT_SOFT_LIMIT_PROBE_CYCLES || defined __DOXYGEN__
+#define DEFAULT_SOFT_LIMIT_PROBE_CYCLES Off
+#endif
///@}
// Safety door/parking settings (Group_SafetyDoor)
diff --git a/core_handlers.h b/core_handlers.h
index e13bf18..62620c4 100644
--- a/core_handlers.h
+++ b/core_handlers.h
@@ -81,7 +81,7 @@ typedef bool (*protocol_enqueue_realtime_command_ptr)(char c);
typedef bool (*travel_limits_ptr)(float *target, axes_signals_t axes, bool is_cartesian);
typedef bool (*arc_limits_ptr)(coord_data_t *target, coord_data_t *position, point_2d_t center, float radius, plane_t plane, int32_t turns);
-typedef void (*jog_limits_ptr)(float *target, float *position);
+typedef void (*apply_travel_limits_ptr)(float *target, float *position);
typedef bool (*home_machine_ptr)(axes_signals_t cycle, axes_signals_t auto_square);
typedef void (*on_parser_init_ptr)(parser_state_t *gc_state);
@@ -263,7 +263,7 @@ typedef struct {
home_machine_ptr home_machine;
travel_limits_ptr check_travel_limits;
arc_limits_ptr check_arc_travel_limits;
- jog_limits_ptr apply_jog_limits;
+ apply_travel_limits_ptr apply_travel_limits;
enqueue_gcode_ptr enqueue_gcode;
enqueue_realtime_command_ptr enqueue_realtime_command;
on_macro_execute_ptr on_macro_execute;
diff --git a/gcode.h b/gcode.h
index 1c229f8..021a593 100644
--- a/gcode.h
+++ b/gcode.h
@@ -516,8 +516,8 @@ typedef union {
typedef struct {
spindle_state_t state; //!< {M3,M4,M5}
spindle_rpm_mode_t rpm_mode; //!< {G96,G97}
- spindle_css_data_t *css; //!< Data used for Constant Surface Speed Mode calculations
spindle_cond_t condition; //!< TODO: move data from planner_cond_t here
+ spindle_css_data_t *css; //!< Data used for Constant Surface Speed Mode calculations
float rpm; //!< Spindle speed. Must be second last!
spindle_ptrs_t *hal; //!< Spindle function pointers etc. Must be last!
} spindle_t;
diff --git a/grbl.h b/grbl.h
index 90fca64..767f2fa 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20250213
+#define GRBL_BUILD 20250220
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/ioports.c b/ioports.c
index b13ca7b..cf64fce 100644
--- a/ioports.c
+++ b/ioports.c
@@ -32,9 +32,11 @@
#include "hal.h"
#include "settings.h"
+#define MAX_PORTS 16
+
typedef struct {
io_ports_detail_t *ports;
- char port_names[50];
+ char port_names[8 * 6 + (MAX_PORTS - 8) * 7];
ioport_bus_t enabled;
} io_ports_private_t;
@@ -137,7 +139,7 @@ static bool match_port (xbar_t *properties, uint8_t port, void *data)
*/
uint8_t ioport_find_free (io_port_type_t type, io_port_direction_t dir, pin_cap_t filter, const char *description)
{
- ff_data.port = 0xFF;
+ ff_data.port = IOPORT_UNASSIGNED;
ff_data.description = (description && *description) ? description : NULL;
// TODO: pass modified filter with .claimable off when looking for description match?
@@ -382,7 +384,7 @@ bool ioports_add (io_ports_data_t *ports, io_port_type_t type, uint8_t n_in, uin
ports->in.map[i] = i;
if(hal.port.set_pin_description)
hal.port.set_pin_description(type, Port_Input, i, get_pnum(ports, i));
- if(i < 8) {
+ if(i < MAX_PORTS) {
cfg->inx.mask = (cfg->inx.mask << 1) + 1;
strcat(cfg->in.port_names, i == 0 ? "Aux " : ",Aux ");
strcat(cfg->in.port_names, uitoa(i));
@@ -394,7 +396,7 @@ bool ioports_add (io_ports_data_t *ports, io_port_type_t type, uint8_t n_in, uin
ports->out.map[i] = i;
if(hal.port.set_pin_description)
hal.port.set_pin_description(type, Port_Output, i, get_pnum(ports, i));
- if(i < 8) {
+ if(i < MAX_PORTS) {
cfg->outx.mask = (cfg->outx.mask << 1) + 1;
strcat(cfg->out.port_names, i == 0 ? "Aux " : ",Aux ");
strcat(cfg->out.port_names, uitoa(i));
diff --git a/ioports.h b/ioports.h
index bcb56aa..1c3a661 100644
--- a/ioports.h
+++ b/ioports.h
@@ -21,6 +21,8 @@
#pragma once
+#define IOPORT_UNASSIGNED 255
+
typedef enum {
Port_Analog = 0, //!< 0
Port_Digital = 1 //!< 1
diff --git a/kinematics/delta.c b/kinematics/delta.c
index a59ebbb..4df1f72 100644
--- a/kinematics/delta.c
+++ b/kinematics/delta.c
@@ -94,7 +94,7 @@ static on_homing_completed_ptr on_homing_completed;
static on_set_axis_setting_unit_ptr on_set_axis_setting_unit;
static on_setting_get_description_ptr on_setting_get_description;
static on_realtime_report_ptr on_realtime_report;
-static jog_limits_ptr apply_jog_limits;
+static apply_travel_limits_ptr apply_travel_limits;
#if N_AXIS > 3
static travel_limits_ptr check_travel_limits;
#endif
@@ -616,15 +616,15 @@ static bool delta_check_travel_limits (float *target, axes_signals_t axes, bool
return !failed;
}
-static void delta_apply_jog_limits (float *target, float *position)
+static void delta_apply_travel_limits (float *target, float *position)
{
if(sys.homed.mask == 0)
return;
if(machine.cfg.flags.limit_to_cuboid)
- apply_jog_limits(target, position);
+ apply_travel_limits(target, position);
- else if(!is_target_inside_cuboid(target, true)) {
+ else if(position && !is_target_inside_cuboid(target, true)) {
coord_data_t pos;
@@ -828,7 +828,7 @@ static void report_options (bool newopt)
on_report_options(newopt);
if(!newopt)
- hal.stream.write("[KINEMATICS:Delta v0.04]" ASCII_EOL);
+ hal.stream.write("[KINEMATICS:Delta v0.05]" ASCII_EOL);
}
static status_code_t delta_info (sys_state_t state, char *args)
@@ -919,8 +919,8 @@ void delta_robot_init (void)
grbl.on_jog_cancel = cancel_jog;
- apply_jog_limits = grbl.apply_jog_limits;
- grbl.apply_jog_limits = delta_apply_jog_limits;
+ apply_travel_limits = grbl.apply_travel_limits;
+ grbl.apply_travel_limits = delta_apply_travel_limits;
#if N_AXIS > 3
check_travel_limits = grbl.check_travel_limits;
diff --git a/machine_limits.c b/machine_limits.c
index 4594bad..3ebcce7 100644
--- a/machine_limits.c
+++ b/machine_limits.c
@@ -814,14 +814,15 @@ static void clip_3d_target (coord_data_t *position, coord_data_t *target, work_e
}
// Limits jog commands to be within machine limits, homed axes only.
-static void apply_jog_limits (float *target, float *position)
+// If position is non-null clip XYZ motion.
+static void apply_travel_limits (float *target, float *position)
{
if(sys.homed.mask == 0)
return;
uint_fast8_t idx;
- if((sys.homed.mask & 0b111) == 0b111) {
+ if(position && (sys.homed.mask & 0b111) == 0b111) {
uint_fast8_t n_axes = 0;
@@ -849,6 +850,6 @@ void limits_init (void)
hal.homing.get_feedrate = get_homing_rate;
grbl.check_travel_limits = check_travel_limits;
grbl.check_arc_travel_limits = check_arc_travel_limits;
- grbl.apply_jog_limits = apply_jog_limits;
+ grbl.apply_travel_limits = apply_travel_limits;
grbl.home_machine = homing_cycle;
}
diff --git a/motion_control.c b/motion_control.c
index 9f79203..afac85c 100644
--- a/motion_control.c
+++ b/motion_control.c
@@ -787,7 +787,7 @@ status_code_t mc_jog_execute (plan_line_data_t *pl_data, parser_block_t *gc_bloc
pl_data->line_number = gc_block->values.n;
if(settings.limits.flags.jog_soft_limited)
- grbl.apply_jog_limits(gc_block->values.xyz, position);
+ grbl.apply_travel_limits(gc_block->values.xyz, position);
else if(sys.soft_limits.mask && !grbl.check_travel_limits(gc_block->values.xyz, sys.soft_limits, true))
return Status_TravelExceeded;
@@ -978,6 +978,9 @@ gc_probe_t mc_probe_cycle (float *target, plan_line_data_t *pl_data, gc_parser_f
if (state_get() == STATE_CHECK_MODE)
return GCProbe_CheckMode;
+ if(settings.probe.soft_limited)
+ grbl.apply_travel_limits(target, NULL);
+
do {
idx--;
sys.probe_position[idx] = lroundf(target[idx] * settings.axis[idx].steps_per_mm);
@@ -1116,7 +1119,7 @@ void mc_override_ctrl_update (gc_override_flags_t override_state)
ISR_CODE void ISR_FUNC(mc_reset)(void)
{
// Only this function can set the system reset. Helps prevent multiple kill calls.
- if (bit_isfalse(sys.rt_exec_state, EXEC_RESET)) {
+ if(bit_isfalse(sys.rt_exec_state, EXEC_RESET)) {
system_set_exec_state_flag(EXEC_RESET);
@@ -1127,13 +1130,11 @@ ISR_CODE void ISR_FUNC(mc_reset)(void)
// NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps
// the steppers enabled by avoiding the go_idle call altogether, unless the motion state is
// violated, by which, all bets are off.
- if ((state_get() & (STATE_CYCLE|STATE_HOMING|STATE_JOG)) || sys.step_control.execute_hold || sys.step_control.execute_sys_motion) {
+ if((sys.position_lost = (state_get() & (STATE_CYCLE|STATE_HOMING|STATE_JOG)) || sys.step_control.execute_hold || sys.step_control.execute_sys_motion)) {
- sys.position_lost = true;
-
- if (state_get() != STATE_HOMING)
+ if(state_get() != STATE_HOMING)
system_set_exec_alarm(Alarm_AbortCycle);
- else if (!sys.rt_exec_alarm)
+ else if(!sys.rt_exec_alarm)
system_set_exec_alarm(Alarm_HomingFailReset);
st_go_idle(); // Force kill steppers. Position has likely been lost.
diff --git a/planner.c b/planner.c
index 110f299..663cf1a 100644
--- a/planner.c
+++ b/planner.c
@@ -136,7 +136,7 @@ static void planner_recalculate (void)
if (block == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete.
// Check if the first block is the tail. If so, notify stepper to update its current parameters.
if (block == block_buffer_tail)
- st_update_plan_block_parameters();
+ st_update_plan_block_parameters(false);
} else while (block != block_buffer_planned) { // Three or more plan-able blocks
next = current;
@@ -145,7 +145,7 @@ static void planner_recalculate (void)
// Check if next block is the tail block(=planned block). If so, update current stepper parameters.
if (block == block_buffer_tail)
- st_update_plan_block_parameters();
+ st_update_plan_block_parameters(false);
// Compute maximum entry speed decelerating over the current block from its exit speed.
if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
@@ -679,7 +679,7 @@ uint_fast16_t plan_get_block_buffer_available (void)
void plan_cycle_reinitialize (void)
{
// Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer.
- st_update_plan_block_parameters();
+ st_update_plan_block_parameters(false);
if((block_buffer_planned = block_buffer_tail) != block_buffer_head)
planner_recalculate();
}
diff --git a/plugins.h b/plugins.h
index a3f870f..a4ef0ff 100644
--- a/plugins.h
+++ b/plugins.h
@@ -7,7 +7,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
@@ -105,6 +105,7 @@ typedef enum {
} grbl_wifi_mode_t;
typedef struct {
+ const char *interface;
bool is_ethernet;
bool link_up;
uint16_t mbps;
diff --git a/protocol.c b/protocol.c
index 6c27f48..4af4ebd 100644
--- a/protocol.c
+++ b/protocol.c
@@ -75,7 +75,7 @@ bool protocol_enqueue_gcode (char *gcode)
{
bool ok = xcommand[0] == '\0' &&
(state_get() == STATE_IDLE || (state_get() & (STATE_ALARM|STATE_JOG|STATE_TOOL_CHANGE))) &&
- bit_isfalse(sys.rt_exec_state, EXEC_MOTION_CANCEL);
+ !((sys.rt_exec_state & (EXEC_MOTION_CANCEL|EXEC_MOTION_CANCEL_FAST)));
if(ok && gc_state.file_run)
ok = gc_state.modal.program_flow != ProgramFlow_Running || strncmp((char *)gcode, "$J=", 3);
@@ -362,7 +362,7 @@ bool protocol_main_loop (void)
// completed. In either case, auto-cycle start, if enabled, any queued moves.
protocol_auto_cycle_start();
- if(sys.abort || !protocol_execute_realtime()) // Runtime command check point.
+ if(!protocol_execute_realtime() && sys.abort) // Runtime command check point.
return !sys.flags.exit; // Bail to main() program loop to reset system.
sys.cancel = false;
@@ -399,7 +399,7 @@ bool protocol_buffer_synchronize (void)
// execute calls a buffer sync, or the planner buffer is full and ready to go.
void protocol_auto_cycle_start (void)
{
- if (plan_get_current_block() != NULL) // Check if there are any blocks in the buffer.
+ if(!ABORTED && plan_get_current_block()) // Check if there are any blocks in the buffer.
system_set_exec_state_flag(EXEC_CYCLE_START); // If so, execute them!
}
@@ -562,6 +562,8 @@ bool protocol_exec_rt_system (void)
if(rt_exec & EXEC_STOP) { // Experimental for now, must be verified. Do NOT move to interrupt context!
+ // Note: homing cannot be cancelled with EXEC_STOP
+
sys.cancel = true;
sys.step_control.flags = 0;
sys.flags.feed_hold_pending = Off;
@@ -579,14 +581,27 @@ bool protocol_exec_rt_system (void)
sys.flags.keep_input = Off;
+ if(sys.alarm_pending != Alarm_None) {
+
+ sys.position_lost = st_is_stepping();
+ system_raise_alarm(sys.alarm_pending);
+ sys.alarm_pending = Alarm_None;
+
+ } else if(st_is_stepping()) {
+
+ state_update(EXEC_MOTION_CANCEL_FAST);
+
+ do {
+ st_prep_buffer(); // Check and prep segment buffer.
+ grbl.on_execute_realtime(state_get());
+ } while(st_is_stepping());
+
+ rt_exec |= system_clear_exec_states();
+ }
+
gc_init(true);
plan_reset();
- if(sys.alarm_pending == Alarm_ProbeProtect) {
- st_go_idle();
- system_set_exec_alarm(sys.alarm_pending);
- sys.alarm_pending = Alarm_None;
- } else
- st_reset();
+ st_reset();
sync_position();
// Kill spindle and coolant. TODO: Check Mach3 behaviour?
@@ -594,7 +609,8 @@ bool protocol_exec_rt_system (void)
gc_coolant((coolant_state_t){0});
flush_override_buffers();
- if(!((state_get() == STATE_ALARM) && (sys.alarm == Alarm_LimitsEngaged || sys.alarm == Alarm_HomingRequired))) {
+
+ if(!((state_get() == STATE_ALARM) && (sys.alarm == Alarm_LimitsEngaged || sys.alarm == Alarm_HomingRequired || sys.alarm == Alarm_ProbeProtect))) {
state_set(hal.control.get_state().safety_door_ajar ? STATE_SAFETY_DOOR : STATE_IDLE);
grbl.report.feedback_message(Message_Stop);
}
diff --git a/settings.c b/settings.c
index 5e4f006..7152e71 100644
--- a/settings.c
+++ b/settings.c
@@ -88,6 +88,7 @@ PROGMEM const settings_t defaults = {
.probe.disable_probe_pullup = DEFAULT_PROBE_SIGNAL_DISABLE_PULLUP,
.probe.allow_feed_override = DEFAULT_ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES,
+ .probe.soft_limited = DEFAULT_SOFT_LIMIT_PROBE_CYCLES,
.probe.invert_probe_pin = DEFAULT_PROBE_SIGNAL_INVERT,
.probe.invert_toolsetter_input = DEFAULT_TOOLSETTER_SIGNAL_INVERT,
.probe.disable_toolsetter_pullup = DEFAULT_TOOLSETTER_SIGNAL_DISABLE_PULLUP,
@@ -996,9 +997,11 @@ static status_code_t set_force_initialization_alarm (setting_id_t id, uint_fast1
return Status_OK;
}
-static status_code_t set_probe_allow_feed_override (setting_id_t id, uint_fast16_t int_value)
+static status_code_t set_probe_flags (setting_id_t id, uint_fast16_t int_value)
{
- settings.probe.allow_feed_override = int_value != 0;
+ settings.probe.allow_feed_override = bit_istrue(int_value, bit(0));
+ settings.probe.soft_limited = bit_istrue(int_value, bit(1));
+ settings.probe.enable_protection = bit_istrue(int_value, bit(2));
return Status_OK;
}
@@ -1513,15 +1516,15 @@ static uint32_t get_int (setting_id_t id)
break;
case Setting_HoldActions:
- value = (settings.flags.disable_laser_during_hold ? bit(0) : 0) | (settings.flags.restore_after_feed_hold ? bit(1) : 0);
+ value = settings.flags.disable_laser_during_hold | (settings.flags.restore_after_feed_hold << 1);
break;
case Setting_ForceInitAlarm:
value = settings.flags.force_initialization_alarm;
break;
- case Setting_ProbingFeedOverride:
- value = settings.probe.allow_feed_override;
+ case Setting_ProbingFlags:
+ value = settings.probe.allow_feed_override | (settings.probe.soft_limited << 1) | (settings.probe.enable_protection << 2);
break;
case Setting_ToolChangeMode:
@@ -1770,7 +1773,7 @@ static bool is_setting_available (const setting_detail_t *setting, uint_fast16_t
available = hal.stepper.get_ganged && hal.stepper.get_ganged(false).mask != 0;
break;
- case Setting_ProbingFeedOverride:
+ case Setting_ProbingFlags:
// case Setting_ToolChangeProbingDistance:
// case Setting_ToolChangeFeedRate:
// case Setting_ToolChangeSeekRate:
@@ -2063,7 +2066,7 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_SleepEnable, Group_General, "Sleep enable", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_sleep_enable, get_int, is_setting_available },
{ Setting_HoldActions, Group_General, "Feed hold actions", NULL, Format_Bitfield, "Disable laser during hold,Restore spindle and coolant state on resume", NULL, NULL, Setting_IsExtendedFn, set_hold_actions, get_int, NULL },
{ Setting_ForceInitAlarm, Group_General, "Force init alarm", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_force_initialization_alarm, get_int, NULL },
- { Setting_ProbingFeedOverride, Group_Probing, "Probing feed override", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_probe_allow_feed_override, get_int, is_setting_available },
+ { Setting_ProbingFlags, Group_Probing, "Probing flags", NULL, Format_Bitfield, "Allow feed override,Apply soft limits", NULL, NULL, Setting_IsExtendedFn, set_probe_flags, get_int, is_setting_available },
#if ENABLE_SPINDLE_LINEARIZATION
{ Setting_LinearSpindlePiece1, Group_Spindle, "Spindle linearisation, 1st point", NULL, Format_String, "x(39)", NULL, "39", Setting_IsExtendedFn, set_linear_piece, get_linear_piece, NULL },
#if SPINDLE_NPWM_PIECES > 1
@@ -2211,7 +2214,7 @@ PROGMEM static const setting_descr_t setting_descr[] = {
{ Setting_SteppersEnergize, "Specifies which steppers not to disable when stopped." },
{ Setting_SpindlePPR, "Spindle encoder pulses per revolution." },
{ Setting_EnableLegacyRTCommands, "Enables \"normal\" processing of ?, ! and ~ characters when part of $-setting or comment. If disabled then they are added to the input string instead." },
- { Setting_JogSoftLimited, "Limit jog commands to machine limits for homed axes." },
+ { Setting_JogSoftLimited, "Limit jog commands to machine workspace for homed axes." },
{ Setting_ParkingEnable, "Enables parking cycle, requires parking axis homed." },
{ Setting_ParkingAxis, "Define which axis that performs the parking motion." },
{ Setting_HomingLocateCycles, "Number of homing passes. Minimum 1, maximum 128." },
@@ -2244,7 +2247,7 @@ PROGMEM static const setting_descr_t setting_descr[] = {
{ Setting_SleepEnable, "Enable sleep mode." },
{ Setting_HoldActions, "Actions taken during feed hold and on resume from feed hold." },
{ Setting_ForceInitAlarm, "Start in alarm mode after a cold reset." },
- { Setting_ProbingFeedOverride, "Allow feed override during probing." },
+ { Setting_ProbingFlags, "Allow feed override during probing and/or limit probing commands to machine workspace for homed axes." },
#if ENABLE_SPINDLE_LINEARIZATION
{ Setting_LinearSpindlePiece1, "Comma separated list of values: RPM_MIN, RPM_LINE_A1, RPM_LINE_B1, set to blank to disable." },
#if SPINDLE_NPWM_PIECES > 1
diff --git a/settings.h b/settings.h
index e65e1ad..552f55c 100644
--- a/settings.h
+++ b/settings.h
@@ -113,7 +113,7 @@ typedef enum {
Setting_SleepEnable = 62,
Setting_HoldActions = 63,
Setting_ForceInitAlarm = 64,
- Setting_ProbingFeedOverride = 65,
+ Setting_ProbingFlags = 65,
// Optional driver implemented settings for piecewise linear spindle PWM algorithm
Setting_LinearSpindlePiece1 = 66,
Setting_LinearSpindlePiece2 = 67,
@@ -602,7 +602,8 @@ typedef union {
enable_protection :1,
invert_toolsetter_input :1,
disable_toolsetter_pullup :1,
- unassigned :8;
+ soft_limited :1,
+ unassigned :7;
};
} probeflags_t;
diff --git a/state_machine.c b/state_machine.c
index f160cf7..02fc7e2 100644
--- a/state_machine.c
+++ b/state_machine.c
@@ -163,8 +163,8 @@ static bool initiate_hold (uint_fast16_t new_state)
enqueue_spindle_override(CMD_OVERRIDE_SPINDLE_STOP);
if (sys_state & (STATE_CYCLE|STATE_JOG)) {
- st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
- sys.step_control.execute_hold = On; // Initiate suspend state with active flag.
+ st_update_plan_block_parameters(false); // Notify stepper module to recompute for hold deceleration.
+ sys.step_control.execute_hold = On; // Initiate suspend state with active flag.
stateHandler = state_await_hold;
}
@@ -424,8 +424,8 @@ static void state_cycle (uint_fast16_t rt_exec)
if (rt_exec & EXEC_CYCLE_COMPLETE)
state_set(gc_state.tool_change ? STATE_TOOL_CHANGE : STATE_IDLE);
- if (rt_exec & EXEC_MOTION_CANCEL) {
- st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
+ if (rt_exec & (EXEC_MOTION_CANCEL|EXEC_MOTION_CANCEL_FAST)) {
+ st_update_plan_block_parameters(!!(rt_exec & EXEC_MOTION_CANCEL_FAST)); // Notify stepper module to recompute for hold deceleration.
sys.suspend = true;
sys.step_control.execute_hold = On; // Initiate suspend state with active flag.
stateHandler = state_await_motion_cancel;
@@ -470,8 +470,13 @@ static void state_await_motion_cancel (uint_fast16_t rt_exec)
sync_position();
sys.suspend = false;
}
+
state_set(pending_state);
- if (gc_state.tool_change)
+
+ if(sys.alarm_pending) {
+ system_set_exec_alarm(sys.alarm_pending);
+ sys.alarm_pending = Alarm_None;
+ } else if(gc_state.tool_change)
state_set(STATE_TOOL_CHANGE);
}
}
@@ -719,7 +724,7 @@ static void restart_retract (void)
sys.parking_state = Parking_Retracting;
if (sys.step_control.execute_sys_motion) {
- st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
+ st_update_plan_block_parameters(false); // Notify stepper module to recompute for hold deceleration.
sys.step_control.execute_hold = On;
sys.step_control.execute_sys_motion = On;
} else // else NO_MOTION is active.
diff --git a/stepper.c b/stepper.c
index 91c6b6e..4293a19 100644
--- a/stepper.c
+++ b/stepper.c
@@ -39,13 +39,6 @@
#define DT_SEGMENT (1.0f / (ACCELERATION_TICKS_PER_SECOND * 60.0f)) // min/segment
#define REQ_MM_INCREMENT_SCALAR 1.25f
-typedef enum {
- Ramp_Accel,
- Ramp_Cruise,
- Ramp_Decel,
- Ramp_DecelOverride
-} ramp_type_t;
-
typedef union {
uint8_t flags;
struct {
@@ -57,6 +50,8 @@ typedef union {
};
} prep_flags_t;
+static bool stepping = false;
+
// Holds the planner block Bresenham algorithm execution data for the segments in the segment
// buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will
// never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1).
@@ -85,15 +80,14 @@ static amass_t amass;
#endif
// Used for blocking new segments being added to the seqment buffer until deceleration starts
-// after probe signal has been asserted.
-static volatile bool probe_asserted = false;
+// when fast stop is called for. TODO: it is likely that this flag can be removed - more testing required.
+static volatile bool exec_fast_hold = false;
// Stepper timer ticks per minute
static float cycles_per_min;
// Step segment ring buffer pointers
-static volatile segment_t *segment_buffer_tail;
-static segment_t *segment_buffer_head, *segment_next_head;
+static volatile segment_t *segment_buffer_tail, *segment_buffer_head;
#if ENABLE_JERK_ACCELERATION
// Static storage for acceleration value of last computed segment.
@@ -200,6 +194,7 @@ void st_wake_up (void)
// Initialize stepper data to ensure first ISR call does not step and
// cancel any pending steppers deenergize
//st.exec_block = NULL;
+ stepping = true;
sys.steppers_deenergize = false;
hal.stepper.wake_up();
}
@@ -211,6 +206,7 @@ ISR_CODE void ISR_FUNC(st_go_idle)(void)
sys_state_t state = state_get();
+ stepping = false;
hal.stepper.go_idle(false);
// Set stepper driver idle state, disabled or enabled, depending on settings and circumstances.
@@ -227,6 +223,10 @@ ISR_CODE void ISR_FUNC(st_go_idle)(void)
hal.stepper.enable(settings.steppers.idle_lock_time == 255 ? (axes_signals_t){AXES_BITMASK} : settings.steppers.energize, true);
}
+bool st_is_stepping (void)
+{
+ return stepping && st.exec_block;
+}
/* "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of grblHAL. grblHAL employs
the venerable Bresenham line algorithm to manage and exactly synchronize multi-axis moves.
@@ -411,19 +411,12 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void)
// Monitors probe pin state and records the system position when detected.
// NOTE: This function must be extremely efficient as to not bog down the stepper ISR.
if (sys.probing_state == Probing_Active && hal.probe.get_state().triggered) {
-
sys.probing_state = Probing_Off;
memcpy(sys.probe_position, sys.position, sizeof(sys.position));
- bit_true(sys.rt_exec_state, EXEC_MOTION_CANCEL);
-
#ifdef MINIMIZE_PROBE_OVERSHOOT
- // "Flush" segment buffer if full in order to start deceleration early.
- if((probe_asserted = segment_buffer_head->next == segment_buffer_tail)) {
- segment_buffer_head = segment_buffer_tail->next;
- if(st.step_count < 3 || st.step_count < (st.exec_segment->n_step >> 3))
- segment_buffer_head = segment_buffer_head->next;
- segment_next_head = segment_next_head->next;
- }
+ bit_true(sys.rt_exec_state, EXEC_MOTION_CANCEL_FAST);
+#else
+ bit_true(sys.rt_exec_state, EXEC_MOTION_CANCEL);
#endif
}
@@ -567,7 +560,6 @@ void st_reset (void)
// Initialize stepper algorithm variables.
pl_block = NULL; // Planner block pointer used by segment buffer
segment_buffer_tail = segment_buffer_head = &segment_buffer[0]; // empty = tail
- segment_next_head = segment_buffer_head->next;
memset(&prep, 0, sizeof(st_prep_t));
memset(&st, 0, sizeof(stepper_t));
@@ -592,9 +584,28 @@ void st_rpm_changed (float rpm)
}
// Called by planner_recalculate() when the executing block is updated by the new plan.
-void st_update_plan_block_parameters (void)
+void st_update_plan_block_parameters (bool fast_hold)
{
- if (pl_block != NULL) { // Ignore if at start of a new block.
+ if(fast_hold) { // NOTE: experimental code!
+
+ hal.irq_disable();
+
+ segment_t *head = (segment_t *)segment_buffer_head;
+
+ if((exec_fast_hold = segment_buffer_head->next == segment_buffer_tail)) {
+ segment_buffer_head = segment_buffer_tail->next;
+ if(st.step_count < 3 || st.step_count < (st.exec_segment->n_step >> 3))
+ segment_buffer_head = segment_buffer_head->next;
+ while(segment_buffer_head->next != head && segment_buffer_head->ramp_type == Ramp_Decel)
+ segment_buffer_head = segment_buffer_head->next;
+ prep.current_speed = segment_buffer_head->current_rate;
+ segment_buffer_head = segment_buffer_head->next;
+ }
+
+ hal.irq_enable();
+ }
+
+ if(pl_block) { // Ignore if at start of a new block.
prep.recalculate.velocity_profile = On;
pl_block->entry_speed_sqr = prep.current_speed * prep.current_speed; // Update entry speed.
pl_block = NULL; // Flag st_prep_segment() to load and check active velocity profile.
@@ -657,7 +668,7 @@ void st_prep_buffer (void)
if (sys.step_control.end_motion)
return;
- while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer.
+ while (segment_buffer_head->next != segment_buffer_tail) { // Check if we need to fill the buffer.
// Determine if we need to load a new planner block or if the block needs to be recomputed.
if (pl_block == NULL) {
@@ -838,15 +849,15 @@ void st_prep_buffer (void)
if(state_get() != STATE_HOMING)
sys.step_control.update_spindle_rpm |= pl_block->spindle.hal->cap.laser; // Force update whenever updating block in laser mode.
- probe_asserted = false;
+ exec_fast_hold = false;
}
- // Block adding new segments after probe is asserted until deceleration is started.
- if(probe_asserted)
+ // Block adding new segments until deceleration is started.
+ if(exec_fast_hold)
return;
// Initialize new segment
- segment_t *prep_segment = segment_buffer_head;
+ segment_t *prep_segment = (segment_t *)segment_buffer_head;
// Set new segment to point to the current segment data block.
prep_segment->exec_block = st_prep_block;
@@ -1096,10 +1107,10 @@ void st_prep_buffer (void)
prep_segment->cycles_per_tick = cycles;
prep_segment->current_rate = prep.current_speed;
+ prep_segment->ramp_type = prep.ramp_type;
- // Segment complete! Increment segment pointers, so stepper ISR can immediately execute it.
- segment_buffer_head = segment_next_head;
- segment_next_head = segment_next_head->next;
+ // Segment complete! Increment segment pointer, so stepper ISR can immediately execute it.
+ segment_buffer_head = segment_buffer_head->next;
// Update the appropriate planner and segment data.
pl_block->millimeters = mm_remaining;
diff --git a/stepper.h b/stepper.h
index 946524c..a6d7533 100644
--- a/stepper.h
+++ b/stepper.h
@@ -3,22 +3,22 @@
Part of grblHAL
- Copyright (c) 2019-2023 Terje Io
+ Copyright (c) 2019-2025 Terje Io
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
- 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 "planner.h"
@@ -32,6 +32,13 @@ typedef enum {
SquaringMode_B, //!< 2
} squaring_mode_t;
+typedef enum {
+ Ramp_Accel,
+ Ramp_Cruise,
+ Ramp_Decel,
+ Ramp_DecelOverride
+} ramp_type_t;
+
/*! \brief Holds the planner block Bresenham algorithm execution data for the segments in the segment buffer.
__NOTE:__ This data is copied from the prepped planner blocks so that the planner blocks may be
@@ -42,13 +49,13 @@ typedef struct st_block {
struct st_block *next; //!< Pointer to next element in cirular list of blocks
uint32_t steps[N_AXIS];
uint32_t step_event_count;
- axes_signals_t direction_bits;
- gc_override_flags_t overrides; //!< Block bitfield variable for overrides
float steps_per_mm;
float millimeters;
float programmed_rate;
char *message; //!< Message to be displayed when block is executed
output_command_t *output_commands; //!< Output commands (linked list) to be performed when block is executed
+ axes_signals_t direction_bits;
+ gc_override_flags_t overrides; //!< Block bitfield variable for overrides
bool backlash_motion;
bool dynamic_rpm; //!< Tracks motions that require dynamic RPM adjustment
offset_id_t offset_id;
@@ -65,6 +72,7 @@ typedef struct st_segment {
uint_fast16_t n_step; //!< Number of step events to be executed for this segment
uint_fast16_t spindle_pwm; //!< Spindle PWM to be set at the start of segment execution
float spindle_rpm; //!< Spindle RPM to be set at the start of the segment execution
+ ramp_type_t ramp_type; //!< Segment ramp type
bool spindle_sync; //!< True if block is spindle synchronized
bool cruising; //!< True when in cruising part of profile, only set for spindle synced moves
uint_fast8_t amass_level; //!< Indicates AMASS level for the ISR to execute this segment
@@ -115,14 +123,17 @@ void st_wake_up (void);
// Immediately disables steppers
void st_go_idle (void);
+// Returns true if motion is ongoing
+bool st_is_stepping (void);
+
// Reset the stepper subsystem variables
void st_reset (void);
// Called by spindle_set_state() to inform about RPM changes.
-void st_rpm_changed(float rpm);
+void st_rpm_changed (float rpm);
// Changes the run state of the step segment buffer to execute the special parking motion.
-void st_parking_setup_buffer();
+void st_parking_setup_buffer (void);
// Restores the step segment buffer to the normal run state after a parking motion.
void st_parking_restore_buffer (void);
@@ -131,7 +142,7 @@ void st_parking_restore_buffer (void);
void st_prep_buffer (void);
// Called by planner_recalculate() when the executing block is updated by the new plan.
-void st_update_plan_block_parameters (void);
+void st_update_plan_block_parameters (bool fast_hold);
// Called by realtime status reporting if realtime rate reporting is enabled in config.h.
float st_get_realtime_rate (void);
diff --git a/system.h b/system.h
index 8e780f9..b071b73 100644
--- a/system.h
+++ b/system.h
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2017-2024 Terje Io
+ Copyright (c) 2017-2025 Terje Io
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
grblHAL is free software: you can redistribute it and/or modify
@@ -42,21 +42,22 @@ flags are always false, so the realtime protocol only needs to check for a non-z
know when there is a realtime command to execute.
*/
///@{
-#define EXEC_STATUS_REPORT bit(0)
-#define EXEC_CYCLE_START bit(1)
-#define EXEC_CYCLE_COMPLETE bit(2)
-#define EXEC_FEED_HOLD bit(3)
-#define EXEC_STOP bit(4)
-#define EXEC_RESET bit(5)
-#define EXEC_SAFETY_DOOR bit(6)
-#define EXEC_MOTION_CANCEL bit(7)
-#define EXEC_SLEEP bit(8)
-#define EXEC_TOOL_CHANGE bit(9)
-#define EXEC_PID_REPORT bit(10)
-#define EXEC_GCODE_REPORT bit(11)
-#define EXEC_TLO_REPORT bit(12)
-#define EXEC_RT_COMMAND bit(13)
-#define EXEC_DOOR_CLOSED bit(14)
+#define EXEC_STATUS_REPORT bit(0)
+#define EXEC_CYCLE_START bit(1)
+#define EXEC_CYCLE_COMPLETE bit(2)
+#define EXEC_FEED_HOLD bit(3)
+#define EXEC_STOP bit(4)
+#define EXEC_RESET bit(5)
+#define EXEC_SAFETY_DOOR bit(6)
+#define EXEC_MOTION_CANCEL bit(7)
+#define EXEC_MOTION_CANCEL_FAST bit(7)
+#define EXEC_SLEEP bit(8)
+#define EXEC_TOOL_CHANGE bit(9)
+#define EXEC_PID_REPORT bit(10)
+#define EXEC_GCODE_REPORT bit(11)
+#define EXEC_TLO_REPORT bit(12)
+#define EXEC_RT_COMMAND bit(13)
+#define EXEC_DOOR_CLOSED bit(14)
///@}
//! \def sys_state