diff --git a/changelog.md b/changelog.md
index f13185b..07514d6 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,21 @@
## grblHAL changelog
+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.
+
+---
+
Build 20230427
Core:
diff --git a/errors.c b/errors.c
index d51323f..eaa6978 100644
--- a/errors.c
+++ b/errors.c
@@ -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 = {
diff --git a/errors.h b/errors.h
index 0f61050..93de049 100644
--- a/errors.h
+++ b/errors.h
@@ -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
diff --git a/grbl.h b/grbl.h
index 8b96c93..efb6fef 100644
--- a/grbl.h
+++ b/grbl.h
@@ -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"
diff --git a/machine_limits.c b/machine_limits.c
index 0b5cb99..287e205 100644
--- a/machine_limits.c
+++ b/machine_limits.c
@@ -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.
diff --git a/machine_limits.h b/machine_limits.h
index e6467ec..a30e44e 100644
--- a/machine_limits.h
+++ b/machine_limits.h
@@ -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);
diff --git a/motion_control.c b/motion_control.c
index 427f6d7..3837bf6 100644
--- a/motion_control.c
+++ b/motion_control.c
@@ -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;
}
diff --git a/protocol.c b/protocol.c
index 5d0b740..8bc85ca 100644
--- a/protocol.c
+++ b/protocol.c
@@ -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;
}
diff --git a/system.c b/system.c
index 9b51252..4eb4bb5 100644
--- a/system.c
+++ b/system.c
@@ -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);
}
diff --git a/system.h b/system.h
index 24ff621..3406148 100644
--- a/system.h
+++ b/system.h
@@ -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 {