Now allows some $-commands while critical events are active or in sleep mode.

Added work envelope data to global sys struct, used by soft limits and jog limit handling.
This commit is contained in:
Terje Io
2023-04-29 15:53:46 +02:00
parent 36035cf72b
commit c652eee852
10 changed files with 206 additions and 120 deletions
+16
View File
@@ -1,5 +1,21 @@
## grblHAL changelog
<a name="20230429"/>Build 20230429
Core:
* Now allows some $-commands while critical events are active or in sleep mode. E.g. settings commands can be used to configure the controller.
* Added work envelope data to [global sys struct](http://svn.io-engineering.com/grblHAL/html/structsystem.html), used by soft limits and jog limit handling. This can be modified by plugin code to restrict motion.
Drivers:
* STM32F1xx: added driver support for ganged and auto-squared axes.
* STM32F4xx: fixed typos.
---
<a name="20230427"/>Build 20230427
Core:
+1
View File
@@ -93,6 +93,7 @@ PROGMEM static const status_detail_t status_detail[] = {
#endif
{ Status_AuthenticationRequired, "Authentication required." },
{ Status_AccessDenied, "Access denied." },
{ Status_NotAllowedCriticalEvent, "Not allowed while critical event is active." }
};
static error_details_t details = {
+1
View File
@@ -105,6 +105,7 @@ typedef enum {
Status_AuthenticationRequired = 77,
Status_AccessDenied = 78,
Status_NotAllowedCriticalEvent = 79,
Status_Unhandled, // For internal use only
Status_StatusMax = Status_Unhandled
+1 -1
View File
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
#define GRBL_BUILD 20230427
#define GRBL_BUILD 20230429
#define GRBL_URL "https://github.com/grblHAL"
+30
View File
@@ -109,7 +109,36 @@ ISR_CODE void ISR_FUNC(limit_interrupt_handler)(limit_signals_t state) // DEFAUL
}
}
// Establish work envelope for homed axes, used by soft limits and jog limits handling.
// When hard limits are enabled pulloff distance is subtracted to avoid triggering limit switches.
void limits_set_work_envelope (void)
{
uint_fast8_t idx = N_AXIS;
do {
if(sys.homed.mask & bit(--idx)) {
float pulloff = settings.limits.flags.hard_enabled && bit_istrue(sys.homing.mask, bit(idx)) ? settings.homing.pulloff : 0.0f;
if(settings.homing.flags.force_set_origin) {
if(bit_isfalse(settings.homing.dir_mask.value, bit(idx))) {
sys.work_envelope.min[idx] = settings.axis[idx].max_travel + pulloff;
sys.work_envelope.max[idx] = 0.0f;
} else {
sys.work_envelope.min[idx] = 0.0f;
sys.work_envelope.max[idx] = - (settings.axis[idx].max_travel + pulloff);
}
} else {
sys.work_envelope.min[idx] = settings.axis[idx].max_travel + pulloff;
sys.work_envelope.max[idx] = - pulloff;
}
} else
sys.work_envelope.min[idx] = sys.work_envelope.max[idx] = 0.0f;
} while(idx);
}
#ifndef KINEMATICS_API
// Set machine positions for homed limit switches. Don't update non-homed axes.
// NOTE: settings.max_travel[] is stored as a negative value.
void limits_set_machine_positions (axes_signals_t cycle, bool add_pulloff)
@@ -133,6 +162,7 @@ void limits_set_machine_positions (axes_signals_t cycle, bool add_pulloff)
}
} while(idx);
}
#endif
// Pulls off axes from asserted homing switches before homing starts.
+2 -1
View File
@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2017-2022 Terje Io
Copyright (c) 2017-2023 Terje Io
Copyright (c) 2012-2015 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
@@ -45,6 +45,7 @@ bool limits_homing_required (void);
// Set axes to be homed from settings.
void limits_set_homing_axes (void);
void limits_set_machine_positions (axes_signals_t cycle, bool add_pulloff);
void limits_set_work_envelope (void);
void limit_interrupt_handler (limit_signals_t state);
+7 -2
View File
@@ -929,8 +929,13 @@ status_code_t mc_homing_cycle (axes_signals_t cycle)
? Status_LimitsEngaged
: Status_OK;
if(homed_status == Status_OK && grbl.on_homing_completed)
grbl.on_homing_completed();
if(homed_status == Status_OK) {
limits_set_work_envelope();
if(grbl.on_homing_completed)
grbl.on_homing_completed();
}
return homed_status;
}
+64 -18
View File
@@ -61,7 +61,7 @@ static char xcommand[LINE_BUFFER_SIZE];
static bool keep_rt_commands = false;
static realtime_queue_t realtime_queue = {0};
static void protocol_exec_rt_suspend ();
static void protocol_exec_rt_suspend (sys_state_t state);
static void protocol_execute_rt_commands (void);
// add gcode to execute not originating from normal input stream
@@ -239,14 +239,14 @@ bool protocol_main_loop (void)
// Direct and execute one line of formatted input, and report status of execution.
if (line_flags.overflow) // Report line overflow error.
gc_state.last_error = Status_Overflow;
else if(line[0] == '\0') // Empty line. For syncing purposes.
else if(*line == '\0') // Empty line. For syncing purposes.
gc_state.last_error = Status_OK;
else if (line[0] == '$') {// Grbl '$' system command
else if(*line == '$') {// Grbl '$' system command
if((gc_state.last_error = system_execute_line(line)) == Status_LimitsEngaged) {
system_raise_alarm(Alarm_LimitsEngaged);
grbl.report.feedback_message(Message_CheckLimits);
}
} else if (line[0] == '[' && grbl.on_user_command)
} else if(*line == '[' && grbl.on_user_command)
gc_state.last_error = grbl.on_user_command(line);
else if (state_get() & (STATE_ALARM|STATE_ESTOP|STATE_JOG)) // Everything else is gcode. Block if in alarm, eStop or jog mode.
gc_state.last_error = Status_SystemGClock;
@@ -276,7 +276,7 @@ bool protocol_main_loop (void)
keep_rt_commands = false;
char_counter = line_flags.value = 0;
} else if (c <= (char_counter > 0 ? ' ' - 1 : ' '))
} else if (c != ASCII_BS && c <= (char_counter > 0 ? ' ' - 1 : ' '))
continue; // Strip control characters and leading whitespace.
else {
switch(c) {
@@ -304,6 +304,7 @@ bool protocol_main_loop (void)
}
break;
case ASCII_BS:
case ASCII_DEL:
if(char_counter) {
line[--char_counter] = '\0';
@@ -388,18 +389,42 @@ bool protocol_execute_realtime (void)
{
if(protocol_exec_rt_system()) {
if (sys.suspend)
protocol_exec_rt_suspend();
sys_state_t state = state_get();
#if NVSDATA_BUFFER_ENABLE
if((state_get() == STATE_IDLE || (state_get() & (STATE_ALARM|STATE_ESTOP))) && settings_dirty.is_dirty && !gc_state.file_run)
if(sys.suspend)
protocol_exec_rt_suspend(state);
#if NVSDATA_BUFFER_ENABLE
if((state == STATE_IDLE || (state & (STATE_ALARM|STATE_ESTOP))) && settings_dirty.is_dirty && !gc_state.file_run)
nvs_buffer_sync_physical();
#endif
#endif
}
return !ABORTED;
}
static void protocol_poll_cmd (void)
{
int16_t c;
if((c = hal.stream.read()) != SERIAL_NO_DATA) {
if ((c == '\n') || (c == '\r')) { // End of line reached
line[char_counter] = '\0';
gc_state.last_error = *line == '\0' ? Status_OK : (*line == '$' ? system_execute_line(line) : Status_SystemGClock);
char_counter = 0;
*line = '\0';
grbl.report.status_message(gc_state.last_error);
} else if(c == ASCII_DEL || c == ASCII_BS) {
if(char_counter)
line[--char_counter] = '\0';
} else if(char_counter == 0 ? c != ' ' : char_counter < (LINE_BUFFER_SIZE - 1))
line[char_counter++] = c;
keep_rt_commands = char_counter > 0 && *line == '$';
}
}
// Executes run-time commands, when required. This function primarily operates as Grbl's state
// machine and controls the various real-time features Grbl has to offer.
// NOTE: Do not alter this unless you know exactly what you are doing!
@@ -426,11 +451,13 @@ bool protocol_exec_rt_system (void)
hal.driver_reset();
// Halt everything upon a critical event flag. Currently hard and soft limits flag this.
if ((alarm_code_t)rt_exec == Alarm_HardLimit ||
(alarm_code_t)rt_exec == Alarm_SoftLimit ||
(alarm_code_t)rt_exec == Alarm_EStop ||
(alarm_code_t)rt_exec == Alarm_MotorFault) {
if((sys.blocking_event = (alarm_code_t)rt_exec == Alarm_HardLimit ||
(alarm_code_t)rt_exec == Alarm_SoftLimit ||
(alarm_code_t)rt_exec == Alarm_EStop ||
(alarm_code_t)rt_exec == Alarm_MotorFault)) {
system_set_exec_alarm(rt_exec);
switch((alarm_code_t)rt_exec) {
case Alarm_EStop:
@@ -445,20 +472,30 @@ bool protocol_exec_rt_system (void)
grbl.report.feedback_message(Message_CriticalEvent);
break;
}
system_clear_exec_state_flag(EXEC_RESET); // Disable any existing reset
*line = '\0';
char_counter = 0;
hal.stream.reset_read_buffer();
while (bit_isfalse(sys.rt_exec_state, EXEC_RESET)) {
// Block everything, except reset and status reports, until user issues reset or power
// cycles. Hard limits typically occur while unattended or not paying attention. Gives
// the user and a GUI time to do what is needed before resetting, like killing the
// incoming stream. The same could be said about soft limits. While the position is not
// lost, continued streaming could cause a serious crash if by chance it gets executed.
if(bit_istrue(sys.rt_exec_state, EXEC_STATUS_REPORT)) {
system_clear_exec_state_flag(EXEC_STATUS_REPORT);
report_realtime_status();
}
protocol_poll_cmd();
grbl.on_execute_realtime(STATE_ESTOP);
}
system_clear_exec_alarm(); // Clear alarm
}
}
@@ -723,13 +760,22 @@ bool protocol_exec_rt_system (void)
// whatever function that invoked the suspend, such that Grbl resumes normal operation.
// This function is written in a way to promote custom parking motions. Simply use this as a
// template.
static void protocol_exec_rt_suspend (void)
static void protocol_exec_rt_suspend (sys_state_t state)
{
while (sys.suspend) {
if((sys.blocking_event = state == STATE_SLEEP)) {
*line = '\0';
char_counter = 0;
hal.stream.reset_read_buffer();
}
if (sys.abort)
while(sys.suspend) {
if(sys.abort)
return;
if(sys.blocking_event)
protocol_poll_cmd();
// Handle spindle overrides during suspend
state_suspend_manager();
@@ -910,7 +956,7 @@ ISR_CODE bool ISR_FUNC(protocol_enqueue_realtime_command)(char c)
break;
default:
if(c < ' ' || (c > ASCII_DEL && c <= 0xBF))
if((c < ' ' && c != ASCII_BS) || (c > ASCII_DEL && c <= 0xBF))
drop = grbl.on_unknown_realtime_cmd == NULL || grbl.on_unknown_realtime_cmd(c);
break;
}
+61 -90
View File
@@ -191,79 +191,79 @@ status_code_t read_int (char *s, int32_t *value)
}
PROGMEM static const sys_command_t sys_commands[] = {
{ "G", true, output_parser_state },
{ "J", false, jog },
{ "#", false, output_ngc_parameters },
{ "$", false, output_settings },
{ "+", false, output_all_settings },
{ "G", output_parser_state, { .noargs = On, .allow_blocking = On } },
{ "J", jog },
{ "#", output_ngc_parameters, { .allow_blocking = On } },
{ "$", output_settings, { .allow_blocking = On } },
{ "+", output_all_settings, { .allow_blocking = On } },
#ifndef NO_SETTINGS_DESCRIPTIONS
{ "SED", false, output_setting_description },
{ "SED", output_setting_description, { .allow_blocking = On } },
#endif
{ "B", true, toggle_block_delete },
{ "S", true, toggle_single_block },
{ "O", true, toggle_optional_stop },
{ "C", true, check_mode },
{ "X", false, disable_lock },
{ "H", false, home },
{ "HX", false, home_x },
{ "HY", false, home_y },
{ "HZ", false, home_z },
{ "B", toggle_block_delete, { .noargs = On } },
{ "S", toggle_single_block, { .noargs = On } },
{ "O", toggle_optional_stop, { .noargs = On } },
{ "C", check_mode, { .noargs = On } },
{ "X", disable_lock },
{ "H", home },
{ "HX", home_x },
{ "HY", home_y },
{ "HZ", home_z },
#if AXIS_REMAP_ABC2UVW
#ifdef A_AXIS
{ "HU", false, home_a },
{ "HU", home_a },
#endif
#ifdef B_AXIS
{ "HV", false, home_b },
{ "HV", home_b },
#endif
#ifdef C_AXIS
{ "HW", false, home_c },
{ "HW", home_c },
#endif
#else
#ifdef A_AXIS
{ "HA", false, home_a },
{ "HA", home_a },
#endif
#ifdef B_AXIS
{ "HB", false, home_b },
{ "HB", home_b },
#endif
#ifdef C_AXIS
{ "HC", false, home_c },
{ "HC", home_c },
#endif
#endif
#ifdef U_AXIS
{ "HU", false, home_u },
{ "HU", home_u },
#endif
#ifdef V_AXIS
{ "HV", false, home_v },
{ "HV", home_v },
#endif
{ "HELP", false, output_help },
{ "SPINDLES", false, output_spindles },
{ "SLP", true, enter_sleep },
{ "TLR", true, set_tool_reference },
{ "TPW", true, tool_probe_workpiece },
{ "I", false, build_info },
{ "I+", true, output_all_build_info },
{ "RST", false, settings_reset },
{ "N", true, output_startup_lines },
{ "N0", false, set_startup_line0 },
{ "N1", false, set_startup_line1 },
{ "EA", true, enumerate_alarms },
{ "EAG", true, enumerate_alarms_grblformatted },
{ "EE", true, enumerate_errors },
{ "EEG", true, enumerate_errors_grblformatted },
{ "EG", true, enumerate_groups },
{ "ES", true, enumerate_settings },
{ "ESG", true, enumerate_settings_grblformatted },
{ "ESH", true, enumerate_settings_halformatted },
{ "E*", true, enumerate_all },
{ "PINS", true, enumerate_pins },
{ "RST", false, settings_reset },
{ "LEV", true, report_last_signals_event },
{ "LIM", true, report_current_limit_state },
{ "SD", false, report_spindle_data },
{ "SR", false, spindle_reset_data },
{ "RTC", false, rtc_action },
{ "HELP", output_help, { .allow_blocking = On } },
{ "SPINDLES", output_spindles },
{ "SLP", enter_sleep, { .noargs = On } },
{ "TLR", set_tool_reference, { .noargs = On } },
{ "TPW", tool_probe_workpiece, { .noargs = On } },
{ "I", build_info, { .allow_blocking = On } },
{ "I+", output_all_build_info, { .noargs = On, .allow_blocking = On } },
{ "RST", settings_reset, { .allow_blocking = On } },
{ "N", output_startup_lines, { .noargs = On, .allow_blocking = On } },
{ "N0", set_startup_line0 },
{ "N1", set_startup_line1 },
{ "EA", enumerate_alarms, { .noargs = On, .allow_blocking = On } },
{ "EAG", enumerate_alarms_grblformatted, { .noargs = On, .allow_blocking = On } },
{ "EE", enumerate_errors, { .noargs = On, .allow_blocking = On } },
{ "EEG", enumerate_errors_grblformatted, { .noargs = On, .allow_blocking = On } },
{ "EG", enumerate_groups, { .noargs = On, .allow_blocking = On } },
{ "ES", enumerate_settings, { .noargs = On, .allow_blocking = On } },
{ "ESG", enumerate_settings_grblformatted, { .noargs = On, .allow_blocking = On } },
{ "ESH", enumerate_settings_halformatted, { .noargs = On, .allow_blocking = On } },
{ "E*", enumerate_all, { .noargs = On, .allow_blocking = On } },
{ "PINS", enumerate_pins, { .noargs = On, .allow_blocking = On } },
{ "RST", settings_reset, { .allow_blocking = On } },
{ "LEV", report_last_signals_event, { .noargs = On, .allow_blocking = On } },
{ "LIM", report_current_limit_state, { .noargs = On, .allow_blocking = On } },
{ "SD", report_spindle_data },
{ "SR", spindle_reset_data },
{ "RTC", rtc_action, { .allow_blocking = On } },
#ifdef DEBUGOUT
{ "Q", true, output_memmap },
{ "Q", output_memmap, { .noargs = On } },
#endif
};
@@ -382,7 +382,10 @@ status_code_t system_execute_line (char *line)
do {
for(idx = 0; idx < cmd->n_commands; idx++) {
if(!strcmp(line, cmd->commands[idx].command)) {
if(!cmd->commands[idx].noargs || args == NULL) {
if(sys.blocking_event && !cmd->commands[idx].flags.allow_blocking) {
retval = Status_NotAllowedCriticalEvent;
break;
} else if(!cmd->commands[idx].flags.noargs || args == NULL) {
if((retval = cmd->commands[idx].execute(state_get(), args)) != Status_Unhandled)
break;
}
@@ -801,7 +804,7 @@ static status_code_t output_ngc_parameters (sys_state_t state, char *args)
static status_code_t build_info (sys_state_t state, char *args)
{
if (!(state == STATE_IDLE || (state & (STATE_ALARM|STATE_ESTOP|STATE_CHECK_MODE))))
if (!(state == STATE_IDLE || (state & (STATE_ALARM|STATE_ESTOP|STATE_SLEEP|STATE_CHECK_MODE))))
return Status_IdleError;
if (args == NULL) {
@@ -1003,62 +1006,30 @@ bool system_xy_at_fixture (coord_system_id_t id, float tolerance)
return ok;
}
// Checks and reports if target array exceeds machine travel limits. Returns false if check failed.
// NOTE: max_travel is stored as negative
// TODO: only check homed axes?
bool system_check_travel_limits (float *target)
{
bool failed = false;
uint_fast8_t idx = N_AXIS;
if(settings.homing.flags.force_set_origin) {
do {
idx--;
// When homing forced set origin is enabled, soft limits checks need to account for directionality.
failed = settings.axis[idx].max_travel < -0.0f &&
(bit_istrue(settings.homing.dir_mask.value, bit(idx))
? (target[idx] < 0.0f || target[idx] > -settings.axis[idx].max_travel)
: (target[idx] > 0.0f || target[idx] < settings.axis[idx].max_travel));
} while(!failed && idx);
} else do {
if(sys.homed.mask) do {
idx--;
failed = settings.axis[idx].max_travel < -0.0f && (target[idx] > 0.0f || target[idx] < settings.axis[idx].max_travel);
if(bit_istrue(sys.homed.mask, bit(idx)) && settings.axis[idx].max_travel < -0.0f)
failed = target[idx] < sys.work_envelope.min[idx] || target[idx] > sys.work_envelope.max[idx];
} while(!failed && idx);
return !failed;
}
// Limits jog commands to be within machine limits, homed axes only.
// When hard limits are enabled pulloff distance is subtracted to avoid triggering limit switches.
// NOTE: max_travel is stored as negative
void system_apply_jog_limits (float *target)
{
uint_fast8_t idx = N_AXIS;
if(sys.homed.mask) do {
idx--;
float pulloff = settings.limits.flags.hard_enabled && bit_istrue(sys.homing.mask, bit(idx)) ? settings.homing.pulloff : 0.0f;
if(bit_istrue(sys.homed.mask, bit(idx)) && settings.axis[idx].max_travel < -0.0f) {
if(settings.homing.flags.force_set_origin) {
if(bit_isfalse(settings.homing.dir_mask.value, bit(idx))) {
if(target[idx] > 0.0f)
target[idx] = 0.0f;
else if(target[idx] < (settings.axis[idx].max_travel + pulloff))
target[idx] = (settings.axis[idx].max_travel + pulloff);
} else {
if(target[idx] < 0.0f)
target[idx] = 0.0f;
else if(target[idx] > -(settings.axis[idx].max_travel + pulloff))
target[idx] = -(settings.axis[idx].max_travel + pulloff);
}
} else {
if(target[idx] > -pulloff)
target[idx] = -pulloff;
else if(target[idx] < (settings.axis[idx].max_travel + pulloff))
target[idx] = (settings.axis[idx].max_travel + pulloff);
}
}
if(bit_istrue(sys.homed.mask, bit(idx)) && settings.axis[idx].max_travel < -0.0f)
target[idx] = max(min(target[idx], sys.work_envelope.max[idx]), sys.work_envelope.min[idx]);
} while(idx);
}
+23 -8
View File
@@ -222,10 +222,10 @@ typedef struct {
typedef union {
uint8_t flags;
struct {
uint16_t feedrate :1,
coolant :1,
spindle :1,
unused :5;
uint8_t feedrate :1,
coolant :1,
spindle :1,
unused :5;
};
} system_override_delay_t;
@@ -246,12 +246,16 @@ typedef union {
};
} system_flags_t;
typedef struct
{
typedef struct {
control_signals_t control;
limit_signals_t limits;
} signal_event_t;
typedef struct {
float min[N_AXIS];
float max[N_AXIS];
} work_envelope_t;
//! Global system variables struct.
// NOTE: probe_position and position variables may need to be declared as volatiles, if problems arise.
typedef struct system {
@@ -260,6 +264,7 @@ typedef struct system {
bool suspend; //!< System suspend state flag.
bool position_lost; //!< Set when mc_reset is called when machine is moving.
bool reset_pending; //!< Set when reset processing is underway.
bool blocking_event; //!< Set when a blocking event that requires reset to clear is active.
volatile bool steppers_deenergize; //!< Set to true to deenergize stepperes
axes_signals_t tlo_reference_set; //!< Axes with tool length reference offset set
int32_t tlo_reference[N_AXIS]; //!< Tool length reference offset
@@ -284,7 +289,8 @@ typedef struct system {
//! @name The following variables are only cleared upon soft reset if position is likely lost, do NOT move. homed must be first!
//@{
axes_signals_t homed; //!< Indicates which axes has been homed.
float home_position[N_AXIS]; //!< Home position for homed axes
float home_position[N_AXIS]; //!< Home position for homed axes.
work_envelope_t work_envelope; //!< Work envelope, only valid for homed axes.
//@}
//! @name The following variables are not cleared upon soft reset, do NOT move. alarm must be first!
//@{
@@ -299,11 +305,20 @@ typedef struct system {
typedef status_code_t (*sys_command_ptr)(sys_state_t state, char *args);
typedef union {
uint8_t flags;
struct {
uint8_t noargs :1, //!< System command does not handle arguments.
allow_blocking :1, //!< System command can be used when blocking event is active.
unused :6;
};
} sys_command_flags_t;
typedef struct
{
const char *command;
bool noargs;
sys_command_ptr execute;
sys_command_flags_t flags;
} sys_command_t;
typedef struct sys_commands_str {