diff --git a/README.md b/README.md
index f6b3e72..dfd0f87 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ It has been written to complement grblHAL and has features such as proper keyboa
---
-Latest build date is 20240513, see the [changelog](changelog.md) for details.
+Latest build date is 20240526, see the [changelog](changelog.md) for details.
__NOTE:__ Build 20240222 has moved the probe input to the ioPorts pool of inputs and will be allocated from it when configured.
The change is major and _potentially dangerous_, it may damage your probe, so please _verify correct operation_ after installing this, or later, builds.
@@ -78,8 +78,9 @@ This is a port/rewrite of [grbl 1.1f](https://github.com/gnea/grbl) and should b
- Tool Change: M6* (Two modes possible: manual** - supports jogging, ATC), M61
- Switches: M48, M49, M50, M51, M53
- Input/output control***: M62, M63, M64, M65, M66, M67, M68
+ - Modal state handling*: M70, M71, M72, M73
- Return from macro*****: M99
- - Valid Non-Command Words: A*, B*, C*, D, E*, F, H*, I, J, K, L, N, P, Q*, R, S, T, U*, V*, W*, X, Y, Z
+ - Valid Non-Command Words: A*, B*, C*, D, E*, F, H*, I, J, K, L, N, O*, P, Q*, R, S, T, U*, V*, W*, X, Y, Z
* driver/configuration dependent. W axis only available when ABC axes are remapped to UVW or when lathe UVW mode is enabled.
** requires compatible GCode sender due to protocol extensions, new state and RT command.
@@ -93,4 +94,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.
---
-20240402
+20240523
diff --git a/changelog.md b/changelog.md
index 5b3a16e..42d416a 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,35 @@
## grblHAL changelog
+Build 20240526
+
+Core:
+
+* Added experimental support for M70-M73, save and restore of modal state.
+
+* Added experimental support of LinuxCNC style subroutines.
+Available for gcode run from local filesystem such as on a SD card or in littlefs.
+
+* Improved handling of G92 when G92 offset is changed while motion is ongoing.
+Position and WCO offset in the realtime report will now be the actual realtime values and not
+the values based on the parser state which may be quite a bit ahead of the machine. Ref. [issue #241](https://github.com/grblHAL/core/discussions/241#discussioncomment-9463390).
+
+* Fix for [issue #521](https://github.com/grblHAL/core/issues/521), crash when running G65 macro on ESP32.
+
+* "Hardened" stream switching code, likely fix for [discussion #456](https://github.com/grblHAL/core/discussions/456#discussioncomment-9533613).
+
+Drivers:
+
+* STM32F7xx: added initial support for WizNet W5500 and W5100S ethernet modules. Stack starts up but fails later, likely due to SPI issue?.
+To be completed later.
+
+Plugins:
+
+* Keypad, OpenPNP and Encoder: updated for core signature change related to improved G92 handling.
+
+* Networking: fixed incorrect signature of WizNet ethernet init function.
+
+---
+
Build 20240513
Core:
diff --git a/gcode.c b/gcode.c
index 2091f0d..e3cc3b2 100644
--- a/gcode.c
+++ b/gcode.c
@@ -105,7 +105,7 @@ typedef union {
} ijk_words_t;
// Declare gc extern struct
-parser_state_t gc_state, *saved_state = NULL;
+parser_state_t gc_state;
#define FAIL(status) return(status);
@@ -186,9 +186,16 @@ axes_signals_t gc_get_g51_state (void)
return scaled;
}
-float gc_get_offset (uint_fast8_t idx)
+float gc_get_offset (uint_fast8_t idx, bool real_time)
{
- return gc_state.modal.coord_system.xyz[idx] + gc_state.g92_coord_offset[idx] + gc_state.tool_length_offset[idx];
+ offset_id_t offset_id;
+
+ if(real_time &&
+ !(settings.status_report.machine_position && settings.status_report.sync_on_wco_change) &&
+ (offset_id = st_get_offset_id()) >= 0)
+ return gc_state.modal.coord_system.xyz[idx] + gc_state.offset_queue[offset_id].values[idx] + gc_state.tool_length_offset[idx];
+ else
+ return gc_state.modal.coord_system.xyz[idx] + gc_state.g92_coord_offset[idx] + gc_state.tool_length_offset[idx];
}
inline static float gc_get_block_offset (parser_block_t *gc_block, uint_fast8_t idx)
@@ -299,8 +306,12 @@ void gc_init (void)
if (!settings_read_coord_data(gc_state.modal.coord_system.id, &gc_state.modal.coord_system.xyz))
grbl.report.status_message(Status_SettingReadFail);
- if (sys.cold_start && !settings.flags.g92_is_volatile && !settings_read_coord_data(CoordinateSystem_G92, &gc_state.g92_coord_offset))
- grbl.report.status_message(Status_SettingReadFail);
+ if(sys.cold_start && !settings.flags.g92_is_volatile) {
+ if(!settings_read_coord_data(CoordinateSystem_G92, &gc_state.g92_coord_offset))
+ grbl.report.status_message(Status_SettingReadFail);
+ else
+ memcpy(&gc_state.offset_queue[gc_state.offset_id], &gc_state.g92_coord_offset, sizeof(coord_data_t));
+ }
if(grbl.on_wco_changed && (!sys.cold_start ||
!is0_position_vector(gc_state.modal.coord_system.xyz) ||
@@ -310,6 +321,9 @@ void gc_init (void)
#if NGC_EXPRESSIONS_ENABLE
ngc_flowctrl_init();
#endif
+#if NGC_PARAMETERS_ENABLE
+ ngc_modal_state_invalidate();
+#endif
// if(settings.flags.lathe_mode)
// gc_state.modal.plane_select = PlaneSelect_ZX;
@@ -349,6 +363,13 @@ spindle_ptrs_t *gc_spindle_get (void)
return gc_state.spindle.hal;
}
+static void add_offset (void)
+{
+ gc_state.offset_id = (gc_state.offset_id + 1) & (MAX_OFFSET_ENTRIES - 1);
+ memcpy(&gc_state.offset_queue[gc_state.offset_id], &gc_state.g92_coord_offset, sizeof(coord_data_t));
+ system_flag_wco_change();
+}
+
static tool_data_t *tool_get_pending (tool_id_t tool_id)
{
static tool_data_t tool_data = {0};
@@ -454,8 +475,15 @@ static status_code_t read_parameter (char *line, uint_fast8_t *char_counter, flo
(*char_counter)++;
char *pos = line = line + *char_counter;
- while(*line && *line != '>')
- line++;
+ while(*line && *line != '>') {
+ if(*line == ' ') {
+ char *s1 = line, *s2 = line + 1;
+ while(*s2)
+ *s1++ = *s2++;
+ *(--s2) = '\0';
+ } else
+ line++;
+ }
*char_counter += line - pos + 1;
@@ -467,7 +495,7 @@ static status_code_t read_parameter (char *line, uint_fast8_t *char_counter, flo
} else
status = Status_BadNumberFormat;
- } else if (read_float(line, char_counter, value)) {
+ } else if(read_float(line, char_counter, value)) {
if(!ngc_param_get((ngc_param_id_t)*value, value))
status = Status_BadNumberFormat;
} else
@@ -481,8 +509,39 @@ static status_code_t read_parameter (char *line, uint_fast8_t *char_counter, flo
return status;
}
+
#endif // NGC_EXPRESSIONS_ENABLE
+#if NGC_PARAMETERS_ENABLE
+
+bool gc_modal_state_restore (gc_modal_t *copy)
+{
+ bool ok = false;
+
+ if((ok = !!copy && !ABORTED)) {
+
+ 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->spindle.state.value != gc_state.modal.spindle.state.value || copy->rpm != gc_state.modal.rpm)
+ spindle_restore(gc_state.spindle.hal, copy->spindle.state, copy->rpm);
+
+ memcpy(&gc_state.modal, copy, sizeof(gc_modal_t));
+
+ gc_state.spindle.rpm = gc_state.modal.rpm;
+ gc_state.feed_rate = gc_state.modal.feed_rate;
+ }
+
+ return ok;
+}
+
+#endif // NGC_PARAMETERS_ENABLE
+
// Remove whitespace, control characters, comments and if block delete is active block delete lines
// else the block delete character. Remaining characters are converted to upper case.
// If the driver handles message comments then the first is extracted and returned in a dynamically
@@ -561,7 +620,7 @@ char *gc_normalize_block (char *block, char **message)
if(c == '#') {
char_counter--;
if(read_parameter(comment, &char_counter, &value) == Status_OK)
- len += strlen(ftoa(value, 6));
+ len += strlen(trim_float(ftoa(value, 6)));
else
len += 3; // "N/A"
} else
@@ -578,7 +637,7 @@ char *gc_normalize_block (char *block, char **message)
if(c == '#') {
char_counter--;
if(read_parameter(comment, &char_counter, &value) == Status_OK)
- strcat(s3, ftoa(value, 6));
+ strcat(s3, trim_float(ftoa(value, 6)));
else
strcat(s3, "N/A");
s3 = strchr(s3, '\0');
@@ -1263,30 +1322,13 @@ status_code_t gc_execute_block (char *block)
word_bit.modal_group.M10 = On;
port_command = (io_mcode_t)int_value;
break;
-/*
- case 70:
- if(!saved_state)
- saved_state = malloc(sizeof(parser_state_t));
- if(!saved_state)
- FAIL(Status_GcodeUnsupportedCommand); // [Unsupported M command]
- memcpy(saved_state, &gc_state, sizeof(parser_state_t));
- return Status_OK;
- case 71: // Invalidate saved state
- if(saved_state) {
- free(saved_state);
- saved_state = NULL;
- }
- return Status_OK; // Should fail if no state is saved...
-
- case 72:
- if(saved_state) {
- // TODO: restore state, need to split out execution part of parser to separate functions first?
- free(saved_state);
- saved_state = NULL;
- }
- return Status_OK;
-*/
+#if NGC_PARAMETERS_ENABLE
+ case 70: case 71: case 72: case 73:
+ //word_bit.modal_group.G0 = On; ??
+ gc_block.state_action = (modal_state_action_t)int_value;
+ break;
+#endif
case 99:
word_bit.modal_group.M4 = On;
@@ -2348,6 +2390,10 @@ status_code_t gc_execute_block (char *block)
FAIL(Status_GcodeValueWordMissing); // [P word missing]
if(gc_block.values.p > 65535.0f)
FAIL(Status_GcodeValueOutOfRange); // [P word out of range]
+#if NGC_PARAMETERS_ENABLE
+ if(!ngc_call_push(&gc_state + ngc_call_level()))
+ FAIL(Status_FlowControlStackOverflow); // [Call level too deep]
+#endif
#if NGC_EXPRESSIONS_ENABLE
// TODO: add context for local storage?
{
@@ -2934,6 +2980,7 @@ status_code_t gc_execute_block (char *block)
// Initialize planner data struct for motion blocks.
plan_line_data_t plan_data;
memset(&plan_data, 0, sizeof(plan_line_data_t)); // Zero plan_data struct
+ plan_data.offset_id = gc_state.offset_id;
plan_data.condition.target_validated = plan_data.condition.target_valid = sys.soft_limits.mask == 0;
// Intercept jog commands and complete error checking for valid jog commands and execute.
@@ -3011,7 +3058,7 @@ status_code_t gc_execute_block (char *block)
if(gc_block.modal.motion != MotionMode_None && gc_block.modal.motion != MotionMode_Seek) {
gc_state.spindle.css = &gc_state.spindle.hal->param->css;
gc_state.spindle.css->axis = plane.axis_1;
- gc_state.spindle.css->tool_offset = gc_get_offset(gc_state.spindle.css->axis);
+ gc_state.spindle.css->tool_offset = gc_get_offset(gc_state.spindle.css->axis, false);
float pos = gc_state.position[gc_state.spindle.css->axis] - gc_state.spindle.css->tool_offset;
gc_block.values.s = pos <= 0.0f ? gc_state.spindle.css->max_rpm : min(gc_state.spindle.css->max_rpm, gc_state.spindle.css->surface_speed / (pos * (float)(2.0f * M_PI)));
//?? gc_parser_flags.spindle_force_sync = On;
@@ -3198,6 +3245,33 @@ status_code_t gc_execute_block (char *block)
plan_data.condition.is_rpm_rate_adjusted = gc_state.is_rpm_rate_adjusted;
plan_data.condition.is_laser_ppi_mode = gc_state.is_rpm_rate_adjusted && gc_state.is_laser_ppi_mode;
+#if NGC_PARAMETERS_ENABLE
+
+ // [7a. Modal state actions ]:
+ switch(gc_block.state_action) {
+
+ case ModalState_Save:
+ case ModalState_SaveAutoRestore:
+ gc_state.modal.rpm = gc_state.spindle.rpm;
+ gc_state.modal.feed_rate = gc_state.feed_rate;
+ if(!ngc_modal_state_save(&gc_state.modal, gc_block.state_action == ModalState_SaveAutoRestore))
+ FAIL(Status_FlowControlOutOfMemory); // [Out of memory] TODO: allocate memory during validation? Static allocation?
+ break;
+
+ case ModalState_Invalidate:
+ ngc_modal_state_invalidate();
+ break;
+
+ case ModalState_Restore:
+ ngc_modal_state_restore();
+ break;
+
+ default:
+ break;
+ }
+
+#endif // NGC_PARAMETERS_ENABLE
+
// [8. Coolant control ]:
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
@@ -3370,7 +3444,7 @@ status_code_t gc_execute_block (char *block)
memcpy(gc_state.g92_coord_offset, gc_block.values.xyz, sizeof(gc_state.g92_coord_offset));
if(!settings.flags.g92_is_volatile)
settings_write_coord_data(CoordinateSystem_G92, &gc_state.g92_coord_offset); // Save G92 offsets to non-volatile storage
- system_flag_wco_change();
+ add_offset();
break;
case NonModal_ResetCoordinateOffset: // G92.1
@@ -3378,19 +3452,19 @@ status_code_t gc_execute_block (char *block)
clear_vector(gc_state.g92_coord_offset); // Disable G92 offsets by zeroing offset vector.
if(!settings.flags.g92_is_volatile)
settings_write_coord_data(CoordinateSystem_G92, &gc_state.g92_coord_offset); // Save G92 offsets to non-volatile storage
- system_flag_wco_change();
+ add_offset();
break;
case NonModal_ClearCoordinateOffset: // G92.2
gc_state.g92_coord_offset_applied = false;
clear_vector(gc_state.g92_coord_offset); // Disable G92 offsets by zeroing offset vector.
- system_flag_wco_change();
+ add_offset();
break;
case NonModal_RestoreCoordinateOffset: // G92.3
gc_state.g92_coord_offset_applied = true; // TODO: check for all zero?
settings_read_coord_data(CoordinateSystem_G92, &gc_state.g92_coord_offset); // Restore G92 offsets from non-volatile storage
- system_flag_wco_change();
+ add_offset();
break;
default:
@@ -3647,6 +3721,10 @@ status_code_t gc_execute_block (char *block)
output_commands = next;
}
+#if NGC_PARAMETERS_ENABLE
+ ngc_modal_state_invalidate();
+#endif
+
grbl.report.feedback_message(Message_ProgramEnd);
}
gc_state.modal.program_flow = ProgramFlow_Running; // Reset program flow.
diff --git a/gcode.h b/gcode.h
index 36d29ed..85cf8a0 100644
--- a/gcode.h
+++ b/gcode.h
@@ -3,22 +3,22 @@
Part of grblHAL
- Copyright (c) 2017-2023 Terje Io
+ Copyright (c) 2017-2024 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 .
*/
#ifndef _GCODE_H_
@@ -29,8 +29,11 @@
#include "spindle_control.h"
#include "errors.h"
+#define MAX_OFFSET_ENTRIES 4 // must be a power of 2
+
typedef uint32_t tool_id_t;
typedef uint16_t macro_id_t;
+typedef int8_t offset_id_t;
// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used
// internally by the parser to know which command to execute.
@@ -59,6 +62,15 @@ typedef enum {
NonModal_RestoreCoordinateOffset = 122 //!< 122 - G92.3
} non_modal_t;
+
+typedef enum {
+ ModalState_NoAction = 0, //!< 0 - Default, must be zero
+ ModalState_Save = 70, //!< 70 - M70
+ ModalState_Invalidate = 71, //!< 71 - M71
+ ModalState_Restore = 72, //!< 72 - M72
+ ModalState_SaveAutoRestore = 73, //!< 73 - M73
+} modal_state_action_t;
+
/*! Modal Group G1: Motion modes
Do not alter values!
@@ -411,7 +423,7 @@ typedef struct {
coord_system_t coord_data; //!< Coordinate data
int32_t $; //!< Spindle id - single-meaning word
int32_t n; //!< Line number - single-meaning word
- uint32_t o; //!< Subroutine identifier - single-meaning word (not used by the core)
+ uint32_t o; //!< Subroutine identifier - single-meaning word
uint32_t h; //!< Tool number or number of G76 thread spring passes
tool_id_t t; //!< Tool selection - single-meaning word
uint8_t l; //!< G10 or canned cycles parameters
@@ -493,6 +505,11 @@ typedef struct {
bool scaling_active; //!< {G50,G51}
bool canned_cycle_active;
float spline_pq[2]; //!< {G5}
+#if NGC_PARAMETERS_ENABLE
+ bool auto_restore;
+ float feed_rate; //!< {F} NOTE: only set when saving modal state
+ float rpm; //!< {S} NOTE: only set when saving modal state
+#endif
} gc_modal_t;
//! Data for canned cycles.
@@ -570,6 +587,8 @@ typedef struct {
bool tool_change;
bool skip_blocks; //!< true if skipping conditional blocks
status_code_t last_error; //!< last return value from parser
+ offset_id_t offset_id; //!< id(x) of last G92 coordinate offset (into circular buffer)
+ coord_data_t offset_queue[MAX_OFFSET_ENTRIES];
//!< The following variables are not cleared upon warm restart when COMPATIBILITY_LEVEL <= 1
bool g92_coord_offset_applied; //!< true when G92 offset applied
float g92_coord_offset[N_AXIS]; //!< Retains the G92 coordinate offset (work coordinates) relative to
@@ -602,6 +621,9 @@ typedef struct {
output_command_t output_command; //!< Details about M62-M68 output command to execute if present in block.
uint32_t arc_turns; //
spindle_ptrs_t *spindle; //!< Spindle to control, NULL for all
+#if NGC_PARAMETERS_ENABLE
+ modal_state_action_t state_action; //!< M70-M73 modal state action
+#endif
} parser_block_t;
// Initialize the parser
@@ -629,7 +651,7 @@ axes_signals_t gc_get_g51_state (void);
float *gc_get_scaling (void);
// Get current axis offset.
-float gc_get_offset (uint_fast8_t idx);
+float gc_get_offset (uint_fast8_t idx, bool real_time);
spindle_ptrs_t *gc_spindle_get (void);
@@ -639,4 +661,8 @@ void gc_coolant (coolant_state_t state);
void gc_set_tool_offset (tool_offset_mode_t mode, uint_fast8_t idx, int32_t offset);
plane_t *gc_get_plane_data (plane_t *plane, plane_select_t select);
+#if NGC_PARAMETERS_ENABLE
+bool gc_modal_state_restore (gc_modal_t *copy);
#endif
+
+#endif // _GCODE_H_
diff --git a/grbl.h b/grbl.h
index be5c054..972e12f 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20240513
+#define GRBL_BUILD 20240526
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/kinematics/polar.c b/kinematics/polar.c
index 9db460f..3aa6636 100644
--- a/kinematics/polar.c
+++ b/kinematics/polar.c
@@ -135,7 +135,7 @@ static float *polar_segment_line (float *target, float *position, plan_line_data
if(init) {
jog_cancel = false;
- r_offset = gc_get_offset(RADIUS_AXIS) * 2.0f; //??
+ r_offset = gc_get_offset(RADIUS_AXIS, false) * 2.0f; //??
memcpy(final_target.values, target, sizeof(final_target));
@@ -229,7 +229,7 @@ static void report_options (bool newopt)
on_report_options(newopt);
if(!newopt)
- hal.stream.write("[KINEMATICS:Polar v0.02]" ASCII_EOL);
+ hal.stream.write("[KINEMATICS:Polar v0.03]" ASCII_EOL);
}
static bool polar_homing_cycle (axes_signals_t cycle, axes_signals_t auto_square)
diff --git a/ngc_flowctrl.c b/ngc_flowctrl.c
index a7c2125..3b6568a 100644
--- a/ngc_flowctrl.c
+++ b/ngc_flowctrl.c
@@ -32,7 +32,7 @@
#include "ngc_params.h"
#ifndef NGC_STACK_DEPTH
-#define NGC_STACK_DEPTH 10
+#define NGC_STACK_DEPTH 20
#endif
typedef enum {
@@ -48,14 +48,25 @@ typedef enum {
NGCFlowCtrl_EndWhile,
NGCFlowCtrl_Repeat,
NGCFlowCtrl_EndRepeat,
+ NGCFlowCtrl_Sub,
+ NGCFlowCtrl_EndSub,
+ NGCFlowCtrl_Call,
NGCFlowCtrl_Return,
NGCFlowCtrl_RaiseAlarm,
NGCFlowCtrl_RaiseError
} ngc_cmd_t;
+typedef struct ngc_sub {
+ uint32_t o_label;
+ vfs_file_t *file;
+ size_t file_pos;
+ struct ngc_sub *next;
+} ngc_sub_t;
+
typedef struct {
uint32_t o_label;
ngc_cmd_t operation;
+ ngc_sub_t *sub;
vfs_file_t *file;
size_t file_pos;
char *expr;
@@ -66,6 +77,8 @@ typedef struct {
} ngc_stack_entry_t;
static volatile int_fast8_t stack_idx = -1;
+static bool skip_sub = false;
+static ngc_sub_t *subs = NULL, *exec_sub = NULL;
static ngc_stack_entry_t stack[NGC_STACK_DEPTH] = {0};
static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *operation)
@@ -97,6 +110,9 @@ static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *ope
if (!strncmp(line + *pos, "ONTINUE", 7)) {
*operation = NGCFlowCtrl_Continue;
*pos += 7;
+ } else if (!strncmp(line + *pos, "ALL", 3)) {
+ *operation = NGCFlowCtrl_Call;
+ *pos += 3;
} else
status = Status_FlowControlSyntaxError; // Unknown statement name starting with C
break;
@@ -125,6 +141,9 @@ static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *ope
} else if (!strncmp(line + *pos, "NDREPEAT", 8)) {
*operation = NGCFlowCtrl_EndRepeat;
*pos += 8;
+ } else if (!strncmp(line + *pos, "NDSUB", 5)) {
+ *operation = NGCFlowCtrl_EndSub;
+ *pos += 5;
} else if (!strncmp(line + *pos, "RROR", 4)) {
*operation = NGCFlowCtrl_RaiseError;
*pos += 4;
@@ -151,6 +170,14 @@ static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *ope
*operation = Status_FlowControlSyntaxError; // Unknown statement name starting with R
break;
+ case 'S':
+ if (!strncmp(line + *pos, "UB", 2)) {
+ *operation = NGCFlowCtrl_Sub;
+ *pos += 2;
+ } else
+ status = Status_FlowControlSyntaxError; // Unknown statement name starting with S
+ break;
+
case 'W':
if (!strncmp(line + *pos, "HILE", 4)) {
*operation = NGCFlowCtrl_While;
@@ -166,12 +193,34 @@ static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *ope
return status;
}
+static void clear_subs (vfs_file_t *file)
+{
+ ngc_sub_t *current = subs, *prev = NULL, *next;
+
+ subs = NULL;
+
+ while(current) {
+ next = current->next;
+ if(file == NULL || file == current->file) {
+ free(current);
+ if(prev)
+ prev->next = next;
+ } else {
+ if(subs == NULL)
+ subs = current;
+ prev = current;
+ }
+ current = next;
+ }
+}
+
static status_code_t stack_push (uint32_t o_label, ngc_cmd_t operation)
{
- if(stack_idx < (NGC_STACK_DEPTH - 1)) {
+ if(stack_idx < (NGC_STACK_DEPTH - 1) && (operation != NGCFlowCtrl_Call || ngc_call_push(&stack[stack_idx + 1]))) {
stack[++stack_idx].o_label = o_label;
stack[stack_idx].file = hal.stream.file;
stack[stack_idx].operation = operation;
+ stack[stack_idx].sub = exec_sub;
return Status_OK;
}
@@ -185,6 +234,8 @@ static bool stack_pull (void)
if((ok = stack_idx >= 0)) {
if(stack[stack_idx].expr)
free(stack[stack_idx].expr);
+ if(stack[stack_idx].operation == NGCFlowCtrl_Call)
+ ngc_call_pop();
memset(&stack[stack_idx], 0, sizeof(ngc_stack_entry_t));
stack_idx--;
}
@@ -192,16 +243,31 @@ static bool stack_pull (void)
return ok;
}
+static void stack_unwind_sub (uint32_t o_label)
+{
+ while(stack_idx >= 0 && stack[stack_idx].o_label != o_label)
+ stack_pull();
+
+ if(stack_idx >= 0) {
+ vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
+ stack_pull();
+ }
+
+ exec_sub = stack_idx >= 0 ? stack[stack_idx].sub : NULL;
+}
+
// Public functions
void ngc_flowctrl_unwind_stack (vfs_file_t *file)
{
+ clear_subs(file);
while(stack_idx >= 0 && stack[stack_idx].file == file)
stack_pull();
}
void ngc_flowctrl_init (void)
{
+ clear_subs(NULL);
while(stack_idx >= 0)
stack_pull();
}
@@ -217,8 +283,8 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo
if((status = read_command(line, pos, &operation)) != Status_OK)
return status;
- skipping = stack_idx >= 0 && stack[stack_idx].skip;
- last_op = stack_idx >= 0 ? stack[stack_idx].operation : NGCFlowCtrl_NoOp;
+ skipping = skip_sub || (stack_idx >= 0 && stack[stack_idx].skip);
+ last_op = stack_idx >= 0 ? stack[stack_idx].operation : (skip_sub ? NGCFlowCtrl_Sub : NGCFlowCtrl_NoOp);
switch(operation) {
@@ -421,16 +487,104 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo
status = (status_code_t)value;
break;
+ case NGCFlowCtrl_Sub:
+ if(hal.stream.file) {
+ ngc_sub_t *sub;
+ if((skip_sub = (sub = malloc(sizeof(ngc_sub_t))) != NULL)) {
+ sub->o_label = o_label;
+ sub->file = hal.stream.file;
+ sub->file_pos = vfs_tell(hal.stream.file);
+ sub->next = NULL;
+ if(subs == NULL)
+ subs = sub;
+ else {
+ ngc_sub_t *last = subs;
+ while(last->next)
+ last = last->next;
+ last->next = sub;
+ }
+ } // else out of memory
+ } else
+ status = Status_FlowControlNotExecutingMacro;
+ break;
+
+ case NGCFlowCtrl_EndSub:
+ if(hal.stream.file) {
+ if(!skip_sub)
+ stack_unwind_sub(o_label);
+ skip_sub = false;
+ } else
+ status = Status_FlowControlNotExecutingMacro;
+ break;
+
+ case NGCFlowCtrl_Call:
+ if(hal.stream.file) {
+ if(!skipping) {
+
+ ngc_sub_t *sub = subs;
+ do {
+ if(sub->o_label == o_label && sub->file == hal.stream.file)
+ break;
+ } while((sub = sub->next));
+
+ if(sub == NULL)
+ status = Status_FlowControlSyntaxError;
+ else {
+
+ float params[29];
+ ngc_param_id_t param_id = 1;
+
+ while(line[*pos] && status == Status_OK && param_id <= 30) {
+ status = ngc_eval_expression(line, pos, ¶ms[param_id - 1]);
+ param_id++;
+ }
+
+ if(status == Status_OK && param_id < 30) do {
+ ngc_param_get(param_id, ¶ms[param_id - 1]);
+ } while(++param_id <= 30);
+
+ if(status == Status_OK && (status = stack_push(o_label, operation)) == Status_OK) {
+
+ stack[stack_idx].sub = exec_sub = sub;
+ stack[stack_idx].file = hal.stream.file;
+ stack[stack_idx].file_pos = vfs_tell(hal.stream.file);
+ stack[stack_idx].repeats = 1;
+
+ for(param_id = 1; param_id <= 30; param_id++) {
+ if(params[param_id - 1] != 0.0f) {
+ if(!ngc_param_set(param_id, params[param_id - 1]))
+ status = Status_FlowControlOutOfMemory;
+ }
+ }
+
+ if(status == Status_OK)
+ vfs_seek(sub->file, sub->file_pos);
+ }
+ }
+ }
+ } else
+ status = Status_FlowControlNotExecutingMacro;
+ break;
+
case NGCFlowCtrl_Return:
if(hal.stream.file) {
- if(!skipping && grbl.on_macro_return) {
- ngc_flowctrl_unwind_stack(stack[stack_idx].file);
+ if(!skipping) {
+
+ bool g65_return = false;
+
+ if(exec_sub)
+ stack_unwind_sub(o_label);
+ else if((g65_return = !!grbl.on_macro_return))
+ ngc_flowctrl_unwind_stack(stack[stack_idx].file);
+
if(ngc_eval_expression(line, pos, &value) == Status_OK) {
ngc_named_param_set("_value", value);
ngc_named_param_set("_value_returned", 1.0f);
} else
ngc_named_param_set("_value_returned", 0.0f);
- grbl.on_macro_return();
+
+ if(g65_return)
+ grbl.on_macro_return();
}
} else
status = Status_FlowControlNotExecutingMacro;
@@ -446,7 +600,7 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo
if(settings.flags.ngc_debug_out)
report_message(line, Message_Plain);
} else
- *skip = stack_idx >= 0 && stack[stack_idx].skip;
+ *skip = skip_sub || (stack_idx >= 0 && stack[stack_idx].skip);
return status;
}
diff --git a/ngc_params.c b/ngc_params.c
index 587d6c4..8364ab7 100644
--- a/ngc_params.c
+++ b/ngc_params.c
@@ -38,7 +38,12 @@
#include "settings.h"
#include "ngc_params.h"
-#define MAX_PARAM_LENGTH 20
+#ifndef NGC_MAX_CALL_LEVEL
+#define NGC_MAX_CALL_LEVEL 10
+#endif
+#ifndef NGC_MAX_PARAM_LENGTH
+#define NGC_MAX_PARAM_LENGTH 20
+#endif
typedef float (*ngc_param_get_ptr)(ngc_param_id_t id);
typedef float (*ngc_named_param_get_ptr)(void);
@@ -50,6 +55,7 @@ typedef struct {
} ngc_ro_param_t;
typedef struct ngc_rw_param {
+ void *context;
ngc_param_id_t id;
float value;
struct ngc_rw_param *next;
@@ -62,20 +68,31 @@ typedef struct {
} ngc_named_ro_param_t;
typedef struct ngc_named_rw_param {
- char name[MAX_PARAM_LENGTH + 1];
+ void *context;
+ char name[NGC_MAX_PARAM_LENGTH + 1];
float value;
struct ngc_named_rw_param *next;
} ngc_named_rw_param_t;
-ngc_rw_param_t *rw_params = NULL;
-ngc_named_rw_param_t *rw_global_params = NULL;
+typedef struct {
+ uint32_t level;
+ void *context;
+ gc_modal_t *modal_state;
+} ngc_param_context_t;
+
+static int32_t call_level = -1;
+static void *call_context;
+static gc_modal_t *modal_state;
+static ngc_param_context_t call_levels[NGC_MAX_CALL_LEVEL];
+static ngc_rw_param_t *rw_params = NULL;
+static ngc_named_rw_param_t *rw_global_params = NULL;
static float _relative_pos (uint_fast8_t axis)
{
float value;
if(axis < N_AXIS) {
- value = sys.position[axis] / settings.axis[axis].steps_per_mm - gc_get_offset(axis);
+ value = sys.position[axis] / settings.axis[axis].steps_per_mm - gc_get_offset(axis, false);
if(settings.flags.report_inches)
value *= 25.4f;
} else
@@ -249,9 +266,10 @@ bool ngc_param_get (ngc_param_id_t id, float *value)
*value = 0.0f;
if(found) {
+ void *context = id > (ngc_param_id_t)30 ? NULL : call_context;
ngc_rw_param_t *rw_param = rw_params;
while(rw_param) {
- if(rw_param->id == id) {
+ if(rw_param->context == context && rw_param->id == id) {
*value = rw_param->value;
rw_param = NULL;
} else
@@ -282,10 +300,11 @@ bool ngc_param_set (ngc_param_id_t id, float value)
if(ok) {
+ void *context = id > (ngc_param_id_t)30 ? NULL : call_context;
ngc_rw_param_t *rw_param = rw_params, *rw_param_last = rw_params;
while(rw_param) {
- if(rw_param->id == id) {
+ if(rw_param->context == context && rw_param->id == id) {
break;
} else {
rw_param_last = rw_param;
@@ -295,6 +314,7 @@ bool ngc_param_set (ngc_param_id_t id, float value)
if(rw_param == NULL && value != 0.0f && (rw_param = malloc(sizeof(ngc_rw_param_t)))) {
rw_param->id = id;
+ rw_param->context = context;
rw_param->next = NULL;
if(rw_params == NULL)
rw_params = rw_param;
@@ -357,6 +377,7 @@ PROGMEM static const ngc_named_ro_param_t ngc_named_ro_param[] = {
{ .name = "_current_pocket", .id = NGCParam_current_pocket },
{ .name = "_selected_tool", .id = NGCParam_selected_tool },
{ .name = "_selected_pocket", .id = NGCParam_selected_pocket },
+ { .name = "_call_level", .id = NGCParam_call_level },
};
@@ -539,6 +560,10 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id)
value = 0.0f;
break;
+ case NGCParam_call_level:
+ value = (float)ngc_call_level();
+ break;
+
default:
value = NAN;
}
@@ -546,15 +571,32 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id)
return value;
}
+// Lowercase name, remove control characters and spaces
+// Assumes names stored in flash are lowercase...
+static char *ngc_name_tolower (char *name)
+{
+ char c, *s1 = name, *s2 = name;
+ bool convert = false;
+
+ while((c = *s1++) && c > ' ')
+ convert = (c & 0x20) == 0 || c <= ' ';
+
+ if(convert) {
+ s1 = name;
+ while((c = *s1++) && c > ' ')
+ *s2++ = LCAPS(c);
+ *s2 = '\0';
+ }
+
+ return name;
+}
+
bool ngc_named_param_get (char *name, float *value)
{
- char c, *s = name;
bool found = false;
uint_fast8_t idx = sizeof(ngc_named_ro_param) / sizeof(ngc_named_ro_param_t);
- // Lowercase name
- while((c = *s))
- *s++ = LCAPS(c);
+ ngc_name_tolower(name);
*value = 0.0f;
@@ -565,9 +607,10 @@ bool ngc_named_param_get (char *name, float *value)
} while(idx && !found);
if(!found) {
+ void *context = *name == '_' ? NULL : call_context;
ngc_named_rw_param_t *rw_param = rw_global_params;
while(rw_param && !found) {
- if((found = !strcmp(rw_param->name, name)))
+ if((found = rw_param->context == context && !strcmp(rw_param->name, name)))
*value = rw_param->value;
else
rw_param = rw_param->next;
@@ -579,15 +622,10 @@ bool ngc_named_param_get (char *name, float *value)
bool ngc_named_param_exists (char *name)
{
- char c, *s1 = name, *s2 = name;
bool ok = false;
uint_fast8_t idx = sizeof(ngc_named_ro_param) / sizeof(ngc_named_ro_param_t);
- // Lowercase name, remove control characters and spaces
- while((c = *s1++) && c > ' ')
- *s2++ = LCAPS(c);
-
- *s2 = '\0';
+ ngc_name_tolower(name);
// Check if name is supplied, return false if not.
if((*name == '_' ? *(name + 1) : *name) == '\0')
@@ -595,36 +633,31 @@ bool ngc_named_param_exists (char *name)
// Check if it is a (read only) predefined parameter.
if(*name == '_') do {
- idx--;
- ok = !strcmp(name, ngc_named_ro_param[idx].name);
+ 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) < MAX_PARAM_LENGTH) {
+ 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 = !strcmp(rw_param->name, name)))
+ if((ok = rw_param->context == context && !strcmp(rw_param->name, name)))
break;
rw_param = rw_param->next;
- }
- }
+ }
+ }
return ok;
}
bool ngc_named_param_set (char *name, float value)
{
- char c, *s1 = name, *s2 = name;
bool ok = false;
uint_fast8_t idx = sizeof(ngc_named_ro_param) / sizeof(ngc_named_ro_param_t);
- // Lowercase name, remove control characters and spaces
- while((c = *s1++) && c > ' ')
- *s2++ = LCAPS(c);
-
- *s2 = '\0';
+ ngc_name_tolower(name);
// Check if name is supplied, return false if not.
if((*name == '_' ? *(name + 1) : *name) == '\0')
@@ -637,12 +670,13 @@ bool ngc_named_param_set (char *name, float value)
} while(idx && !ok);
// If not predefined attempt to set it.
- if(!ok && (ok = strlen(name) < MAX_PARAM_LENGTH)) {
+ if(!ok && (ok = strlen(name) < NGC_MAX_PARAM_LENGTH)) {
+ void *context = *name == '_' ? NULL : call_context;
ngc_named_rw_param_t *rw_param = rw_global_params, *rw_param_last = rw_global_params;
while(rw_param) {
- if(!strcmp(rw_param->name, name)) {
+ if(rw_param->context == context && !strcmp(rw_param->name, name)) {
break;
} else {
rw_param_last = rw_param;
@@ -652,6 +686,7 @@ bool ngc_named_param_set (char *name, float value)
if(rw_param == NULL && (rw_param = malloc(sizeof(ngc_named_rw_param_t)))) {
strcpy(rw_param->name, name);
+ rw_param->context = context;
rw_param->next = NULL;
if(rw_global_params == NULL)
rw_global_params = rw_param;
@@ -666,4 +701,101 @@ bool ngc_named_param_set (char *name, float value)
return ok;
}
+bool ngc_modal_state_save (gc_modal_t *state, bool auto_restore)
+{
+ gc_modal_t **saved_state = call_level == -1 ? &modal_state : &call_levels[call_level].modal_state;
+
+ if(*saved_state == NULL)
+ *saved_state = malloc(sizeof(gc_modal_t));
+
+ if(*saved_state)
+ memcpy(*saved_state, state, sizeof(gc_modal_t));
+
+ return *saved_state != NULL;
+}
+
+void ngc_modal_state_invalidate (void)
+{
+ gc_modal_t **saved_state = call_level == -1 ? &modal_state : &call_levels[call_level].modal_state;
+
+ if(*saved_state) {
+ free(*saved_state);
+ *saved_state = NULL;
+ }
+}
+
+bool ngc_modal_state_restore (void)
+{
+ return gc_modal_state_restore(call_level == -1 ? modal_state : call_levels[call_level].modal_state);
+}
+
+bool ngc_call_push (void *context)
+{
+ bool ok;
+
+ if((ok = call_level < (NGC_MAX_CALL_LEVEL - 1)))
+ call_levels[++call_level].context = call_context = context;
+
+ return ok;
+}
+
+bool ngc_call_pop (void)
+{
+ if(call_level >= 0) {
+
+ if(call_context) {
+
+ ngc_rw_param_t *rw_param = rw_params, *rw_param_last = rw_params;
+
+ while(rw_param) {
+ if(rw_param->context == call_context) {
+ ngc_rw_param_t *rw_param_free = rw_param;
+ rw_param = rw_param->next;
+ if(rw_param_free == rw_params)
+ rw_params = rw_param_last = rw_param;
+ else
+ rw_param_last->next = rw_param;
+ free(rw_param_free);
+ } else {
+ rw_param_last = rw_param;
+ rw_param = rw_param->next;
+ }
+ }
+
+ ngc_named_rw_param_t *rw_named_param = rw_global_params, *rw_named_param_last = rw_global_params;
+
+ while(rw_named_param) {
+ if(rw_named_param->context == call_context) {
+ ngc_named_rw_param_t *rw_named_param_free = rw_named_param;
+ rw_named_param = rw_named_param->next;
+ if(rw_named_param_free == rw_global_params)
+ rw_global_params = rw_named_param_last = rw_named_param;
+ else
+ rw_named_param_last->next = rw_named_param;
+ free(rw_named_param_free);
+ } else {
+ rw_named_param_last = rw_named_param;
+ rw_named_param = rw_named_param->next;
+ }
+ }
+ }
+
+ if(call_levels[call_level].modal_state) {
+ if(call_levels[call_level].modal_state->auto_restore)
+ gc_modal_state_restore(call_levels[call_level].modal_state);
+ free(call_levels[call_level].modal_state);
+ call_levels[call_level].modal_state = NULL;
+ }
+
+ call_context = --call_level >= 0 ? call_levels[call_level].context : NULL;
+ }
+
+ return call_level >= 0;
+}
+
+uint_fast8_t ngc_call_level (void)
+{
+ return (uint_fast8_t)(call_level + 1);
+}
+
#endif // NGC_PARAMETERS_ENABLE
diff --git a/ngc_params.h b/ngc_params.h
index 38052fa..198695b 100644
--- a/ngc_params.h
+++ b/ngc_params.h
@@ -1,22 +1,22 @@
/*
- ngc_params.c - get/set NGC parameter value by id or name
+ ngc_params.h - get/set NGC parameter value by id or name
Part of grblHAL
- Copyright (c) 2021 Terje Io
+ Copyright (c) 2021-2024 Terje Io
- 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 .
*/
/*
@@ -28,6 +28,8 @@
#ifndef _NGC_PARAMS_H_
#define _NGC_PARAMS_H_
+#include "gcode.h"
+
typedef uint16_t ngc_param_id_t;
typedef struct {
@@ -81,6 +83,7 @@ typedef enum {
NGCParam_current_pocket,
NGCParam_selected_tool,
NGCParam_selected_pocket,
+ NGCParam_call_level,
NGCParam_Last
} ncg_name_param_id_t;
@@ -92,5 +95,11 @@ bool ngc_named_param_get (char *name, float *value);
float ngc_named_param_get_by_id (ncg_name_param_id_t id);
bool ngc_named_param_set (char *name, float value);
bool ngc_named_param_exists (char *name);
+bool ngc_call_push (void *context);
+bool ngc_call_pop (void);
+uint_fast8_t ngc_call_level (void);
+bool ngc_modal_state_save (gc_modal_t *state, bool auto_restore);
+bool ngc_modal_state_restore (void);
+void ngc_modal_state_invalidate (void);
-#endif
+#endif // _NGC_PARAMS_H_
diff --git a/nuts_bolts.c b/nuts_bolts.c
index ce23790..a81195c 100644
--- a/nuts_bolts.c
+++ b/nuts_bolts.c
@@ -166,6 +166,20 @@ char *ftoa (float n, uint8_t decimal_places)
return bptr;
}
+// Trim trailing zeros and possibly decimal point
+char *trim_float (char *s)
+{
+ if(strchr(s, '.')) {
+ char *s2 = strchr(s, '\0') - 1;
+ while(*s2 == '0')
+ *s2-- = '\0';
+ if(*s2 == '.')
+ *s2 = '\0';
+ }
+
+ return s;
+}
+
// Extracts an unsigned integer value from a string.
status_code_t read_uint (char *line, uint_fast8_t *char_counter, uint32_t *uint_ptr)
{
diff --git a/nuts_bolts.h b/nuts_bolts.h
index 2feafc3..ae6f6f3 100644
--- a/nuts_bolts.h
+++ b/nuts_bolts.h
@@ -211,6 +211,9 @@ char *uitoa (uint32_t n);
// Converts a float variable to string with the specified number of decimal places.
char *ftoa (float n, uint8_t decimal_places);
+// Trim trailing zeros and possibly decimal point
+char *trim_float (char *s);
+
// Returns true if float value is a whole number (integer)
bool isintf (float value);
diff --git a/planner.c b/planner.c
index 5e56695..d3011b1 100644
--- a/planner.c
+++ b/planner.c
@@ -397,6 +397,7 @@ bool plan_buffer_line (float *target, plan_line_data_t *pl_data)
block->condition = pl_data->condition;
block->overrides = pl_data->overrides;
block->line_number = pl_data->line_number;
+ block->offset_id = pl_data->offset_id;
block->output_commands = pl_data->output_commands;
block->message = pl_data->message;
@@ -689,6 +690,7 @@ void plan_feed_override (override_t feed_override, override_t rapid_override)
void plan_data_init (plan_line_data_t *plan_data)
{
memset(plan_data, 0, sizeof(plan_line_data_t));
+ plan_data->offset_id = gc_state.offset_id;
plan_data->spindle.hal = gc_state.spindle.hal ? gc_state.spindle.hal : spindle_get(0);
plan_data->condition.target_validated = plan_data->condition.target_valid = sys.soft_limits.mask == 0;
#ifdef KINEMATICS_API
diff --git a/planner.h b/planner.h
index b4c776f..52667a3 100644
--- a/planner.h
+++ b/planner.h
@@ -52,6 +52,7 @@ typedef struct plan_block {
uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block.
axes_signals_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_PIN in config.h)
+ offset_id_t offset_id;
// Block condition data to ensure correct execution depending on states and overrides.
gc_override_flags_t overrides; // Block bitfield variable for overrides
planner_cond_t condition; // Block bitfield variable defining block run conditions. Copied from pl_line_data.
@@ -95,6 +96,7 @@ typedef struct {
spindle_t spindle; // Desired spindle parameters, such as RPM, through line motion.
planner_cond_t condition; // Bitfield variable to indicate planner conditions. See defines above.
gc_override_flags_t overrides; // Block bitfield variable for overrides
+ offset_id_t offset_id;
int32_t line_number; // Desired line number to report when executing.
// void *parameters; // TODO: pointer to extra parameters, for canned cycles and threading?
char *message; // Message to be displayed when block is executed.
diff --git a/platform.h b/platform.h
index 864eccf..19010a3 100644
--- a/platform.h
+++ b/platform.h
@@ -21,8 +21,28 @@
#pragma once
-#if defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F407xx) || defined(STM32F411xE) || \
- defined(STM32F412Vx) || defined(STM32F446xx) || defined(STM32F756xx) || defined(STM32F765xx) || defined(STM32H743xx) || defined(STM32H723xx)
+#if defined(STM32F103xB) || defined(STM32F103xE)
+#define STM32_F1_PLATFORM
+#endif
+
+#if defined(STM32F303xC)
+#define STM32_F3_PLATFORM
+#endif
+
+#if defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F407xx) || defined(STM32F411xE) || \
+ defined(STM32F412Vx) || defined(STM32F446xx)
+#define STM32_F4_PLATFORM
+#endif
+
+#if defined(STM32F756xx) || defined(STM32F765xx)
+#define STM32_F7_PLATFORM
+#endif
+
+#if defined(STM32H743xx) || defined(STM32H723xx)
+#define STM32_H7_PLATFORM
+#endif
+
+#if defined(STM32_F1_PLATFORM) || defined(STM32_F3_PLATFORM) || defined(STM32_F4_PLATFORM) || defined(STM32_F7_PLATFORM) || defined(STM32_H7_PLATFORM)
#define STM32_PLATFORM
#endif
diff --git a/protocol.c b/protocol.c
index 59735a5..95fa75b 100644
--- a/protocol.c
+++ b/protocol.c
@@ -375,9 +375,13 @@ bool protocol_main_loop (void)
bool protocol_buffer_synchronize (void)
{
bool ok = true;
+
// If system is queued, ensure cycle resumes if the auto start flag is present.
protocol_auto_cycle_start();
+
+ sys.flags.synchronizing = On;
while ((ok = protocol_execute_realtime()) && (plan_get_current_block() || state_get() == STATE_CYCLE));
+ sys.flags.synchronizing = Off;
return ok;
}
@@ -939,12 +943,12 @@ ISR_CODE bool ISR_FUNC(protocol_enqueue_realtime_command)(char c)
break;
case CMD_MPG_MODE_TOGGLE: // Switch off MPG mode
- if(hal.stream.type == StreamType_MPG)
+ if((drop = hal.stream.type == StreamType_MPG))
protocol_enqueue_foreground_task(stream_mpg_set_mode, NULL);
break;
case CMD_AUTO_REPORTING_TOGGLE:
- if(settings.report_interval)
+ if((drop = settings.report_interval != 0))
sys.flags.auto_reporting = !sys.flags.auto_reporting;
break;
diff --git a/report.c b/report.c
index a6528c2..1162a12 100644
--- a/report.c
+++ b/report.c
@@ -1171,11 +1171,11 @@ void report_realtime_status (void)
uint_fast8_t idx;
float wco[N_AXIS];
- if (!settings.status_report.machine_position || report.wco) {
- for (idx = 0; idx < N_AXIS; idx++) {
+ if(!settings.status_report.machine_position || report.wco) {
+ for(idx = 0; idx < N_AXIS; idx++) {
// Apply work coordinate offsets and tool length offset to current position.
- wco[idx] = gc_get_offset(idx);
- if (!settings.status_report.machine_position)
+ wco[idx] = gc_get_offset(idx, true);
+ if(!settings.status_report.machine_position)
print_position[idx] -= wco[idx];
}
}
@@ -1281,13 +1281,13 @@ void report_realtime_status (void)
if(settings.status_report.work_coord_offset) {
if(wco_counter > 0 && !report.wco) {
- if(wco_counter > (REPORT_WCO_REFRESH_IDLE_COUNT - 1) && state_get() == STATE_IDLE)
+ if(wco_counter > (REPORT_WCO_REFRESH_IDLE_COUNT - 1) && state == STATE_IDLE)
wco_counter = REPORT_WCO_REFRESH_IDLE_COUNT - 1;
wco_counter--;
} else
- wco_counter = state_get() & (STATE_HOMING|STATE_CYCLE|STATE_HOLD|STATE_JOG|STATE_SAFETY_DOOR)
- ? (REPORT_WCO_REFRESH_BUSY_COUNT - 1) // Reset counter for slow refresh
- : (REPORT_WCO_REFRESH_IDLE_COUNT - 1);
+ wco_counter = state & (STATE_HOMING|STATE_CYCLE|STATE_HOLD|STATE_JOG|STATE_SAFETY_DOOR)
+ ? (REPORT_WCO_REFRESH_BUSY_COUNT - 1) // Reset counter for slow refresh
+ : (REPORT_WCO_REFRESH_IDLE_COUNT - 1);
} else
report.wco = Off;
@@ -1298,9 +1298,9 @@ void report_realtime_status (void)
else if((report.overrides = !report.wco)) {
report.spindle = report.spindle || spindle_0_state.on;
report.coolant = report.coolant || hal.coolant.get_state().value != 0;
- override_counter = state_get() & (STATE_HOMING|STATE_CYCLE|STATE_HOLD|STATE_JOG|STATE_SAFETY_DOOR)
- ? (REPORT_OVERRIDE_REFRESH_BUSY_COUNT - 1) // Reset counter for slow refresh
- : (REPORT_OVERRIDE_REFRESH_IDLE_COUNT - 1);
+ override_counter = state & (STATE_HOMING|STATE_CYCLE|STATE_HOLD|STATE_JOG|STATE_SAFETY_DOOR)
+ ? (REPORT_OVERRIDE_REFRESH_BUSY_COUNT - 1) // Reset counter for slow refresh
+ : (REPORT_OVERRIDE_REFRESH_IDLE_COUNT - 1);
}
} else
report.overrides = Off;
@@ -1308,8 +1308,14 @@ void report_realtime_status (void)
if(report.value || gc_state.tool_change) {
if(report.wco) {
- hal.stream.write_all("|WCO:");
- hal.stream.write_all(get_axis_values(wco));
+ // If protocol_buffer_synchronize() is running
+ // delay outputting WCO until sync is completed
+ // unless requested from stepper_driver_interrupt_handler.
+ if(report.force_wco || !sys.flags.synchronizing) {
+ hal.stream.write_all("|WCO:");
+ hal.stream.write_all(get_axis_values(wco));
+ } else
+ wco_counter = 0;
}
if(report.gwco) {
diff --git a/stepper.c b/stepper.c
index 5db6b36..c621e25 100644
--- a/stepper.c
+++ b/stepper.c
@@ -305,6 +305,10 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void)
if((st.dir_change = st.exec_block == NULL || st.dir_outbits.value != st.exec_segment->exec_block->direction_bits.value))
st.dir_outbits = st.exec_segment->exec_block->direction_bits;
+
+ if(st.exec_block != NULL && st.exec_block->offset_id != st.exec_segment->exec_block->offset_id)
+ sys.report.wco = sys.report.force_wco = On; // Do not generate grbl.on_rt_reports_added event!
+
st.exec_block = st.exec_segment->exec_block;
st.step_event_count = st.exec_block->step_event_count;
st.new_block = true;
@@ -704,9 +708,10 @@ void st_prep_buffer (void)
st_prep_block->spindle = pl_block->spindle.hal;
st_prep_block->output_commands = pl_block->output_commands;
st_prep_block->overrides = pl_block->overrides;
+ st_prep_block->offset_id = pl_block->offset_id;
st_prep_block->backlash_motion = pl_block->condition.backlash_motion;
st_prep_block->message = pl_block->message;
- pl_block->message= NULL;
+ pl_block->message = NULL;
// Initialize segment buffer data for generating the segments.
prep.steps_per_mm = st_prep_block->steps_per_mm;
@@ -1098,3 +1103,14 @@ float st_get_realtime_rate (void)
#endif
: 0.0f;
}
+
+offset_id_t st_get_offset_id (void)
+{
+ plan_block_t *pl_block;
+
+ return st.exec_block
+ ? st.exec_block->offset_id
+ : (sys.holding_state == Hold_Complete && (pl_block = plan_get_current_block())
+ ? pl_block->offset_id
+ : -1);
+}
diff --git a/stepper.h b/stepper.h
index 2baaf6d..946524c 100644
--- a/stepper.h
+++ b/stepper.h
@@ -51,6 +51,7 @@ typedef struct st_block {
output_command_t *output_commands; //!< Output commands (linked list) to be performed when block is executed
bool backlash_motion;
bool dynamic_rpm; //!< Tracks motions that require dynamic RPM adjustment
+ offset_id_t offset_id;
spindle_ptrs_t *spindle; //!< Pointer to current spindle for motions that require dynamic RPM adjustment
} st_block_t;
@@ -137,4 +138,6 @@ float st_get_realtime_rate (void);
void stepper_driver_interrupt_handler (void);
+offset_id_t st_get_offset_id (void);
+
#endif
diff --git a/stream.c b/stream.c
index 979c10f..c80b814 100644
--- a/stream.c
+++ b/stream.c
@@ -47,7 +47,7 @@ typedef struct stream_connection {
const io_stream_t *stream;
stream_is_connected_ptr is_up;
stream_connection_flags_t flags;
- struct stream_connection *next;
+ struct stream_connection *next, *prev;
} stream_connection_t;
static const io_stream_properties_t null_stream = {
@@ -232,6 +232,7 @@ static stream_connection_t *add_connection (const io_stream_t *stream)
return NULL;
}
}
+ connection->prev = last;
last->next = connection;
}
@@ -246,42 +247,42 @@ static bool stream_select (const io_stream_t *stream, bool add)
{
static const io_stream_t *active_stream = NULL;
- bool send_init_message = false;
+ bool send_init_message = false, mpg_enable = false;
if(stream == base.stream) {
base.is_up = add ? (stream->is_connected ? stream->is_connected : stream_connected) : is_not_connected;
return true;
}
- if(add) {
+ if(!add) { // disconnect
- if(add_connection(stream) == NULL)
- return false;
+ if(stream == base.stream || stream == mpg.stream)
+ return false;
- } else { // disconnect
+ bool disconnected = false;
+ stream_connection_t *connection = connections->next;
- const io_stream_t *org_stream;
- stream_connection_t *prev, *last = connections;
-
- while(last->next) {
- prev = last;
- last = last->next;
- if(prev->stream != mpg.stream)
- org_stream = prev->stream;
- if(last->stream == stream) {
- prev->next = last->next;
- free(last);
- if(prev->next)
- return false;
- else {
- if(mpg.flags.mpg_control || stream->type == StreamType_MPG)
- protocol_enqueue_foreground_task(stream_mpg_set_mode, (void *)1);
- stream = org_stream;
- break;
+ while(connection) {
+ if(stream == connection->stream) {
+ if((connection->prev->next = connection->next))
+ connection->next->prev = connection->prev;
+ if((stream = connection->prev->stream) == mpg.stream) {
+ mpg_enable = mpg.flags.mpg_control;
+ if((stream = connection->prev->prev->stream) == NULL)
+ stream = base.stream;
}
- }
+ free(connection);
+ connection = NULL;
+ disconnected = true;
+ } else
+ connection = connection->next;
}
- }
+
+ if(!disconnected)
+ return false;
+
+ } else if(add_connection(stream) == NULL)
+ return false;
bool webui_connected = hal.stream.state.webui_connected;
@@ -323,7 +324,8 @@ static bool stream_select (const io_stream_t *stream, bool add)
if(hal.stream.type == StreamType_MPG) {
stream_mpg_enable(false);
mpg.flags.mpg_control = On;
- }
+ } else if(mpg_enable)
+ protocol_enqueue_foreground_task(stream_mpg_set_mode, (void *)1);
memcpy(&hal.stream, stream, sizeof(io_stream_t));
@@ -455,7 +457,7 @@ void stream_mpg_set_mode (void *data)
ISR_CODE bool ISR_FUNC(stream_mpg_check_enable)(char c)
{
if(c == CMD_MPG_MODE_TOGGLE)
- protocol_enqueue_foreground_task(stream_mpg_set_mode, (void *)1);
+ task_add_immediate(stream_mpg_set_mode, (void *)1);
else {
protocol_enqueue_realtime_command(c);
if((c == CMD_CYCLE_START || c == CMD_CYCLE_START_LEGACY) && settings.status_report.pin_state)
@@ -492,6 +494,7 @@ bool stream_mpg_register (const io_stream_t *stream, bool rx_only, stream_write_
memcpy(&mpg, connection, sizeof(stream_connection_t));
mpg.flags.is_mpg_tx = On;
+ mpg.flags.mpg_control = Off;
if(mpg_write_char)
mpg.stream->set_enqueue_rt_handler(mpg_write_char);
diff --git a/system.c b/system.c
index ff2db49..b45234e 100644
--- a/system.c
+++ b/system.c
@@ -1143,7 +1143,7 @@ the WCO report element to the next status report.
*/
void system_flag_wco_change (void)
{
- if(!settings.status_report.sync_on_wco_change)
+ if(settings.status_report.sync_on_wco_change)
protocol_buffer_synchronize();
if(grbl.on_wco_changed)
diff --git a/system.h b/system.h
index dfcbcf5..fa9d867 100644
--- a/system.h
+++ b/system.h
@@ -187,6 +187,7 @@ typedef enum {
Report_TLOReference = (1 << 15),
Report_Fan = (1 << 16),
Report_SpindleId = (1 << 17),
+ Report_ForceWCO = (1 << 29),
Report_CycleStart = (1 << 30),
Report_All = 0x8003FFFF
} report_tracking_t;
@@ -212,7 +213,8 @@ typedef union {
tlo_reference :1, //!< Tool length offset reference changed.
fan :1, //!< Fan on/off changed.
spindle_id :1, //!< Spindle changed
- unassigned :12, //
+ unassigned :11, //
+ force_wco :1, //!< Add work coordinates (due to WCO changed during motion).
cycle_start :1, //!< Cycle start signal triggered. __NOTE:__ do __NOT__ add to Report_All enum above!
all :1; //!< Set when CMD_STATUS_REPORT_ALL is requested, may be used by user code.
};
@@ -249,7 +251,8 @@ typedef union {
single_block :1, //!< Set to true to disable M1 (optional stop), via realtime command.
keep_input :1, //!< Set to true to not flush stream input buffer on executing STOP.
auto_reporting :1, //!< Set to true when auto real time reporting is enabled.
- unused :6;
+ synchronizing :1, //!< Set to true when protocol_buffer_synchronize() is running.
+ unused :5;
};
} system_flags_t;
diff --git a/tool_change.c b/tool_change.c
index 69b2e02..ad01e13 100644
--- a/tool_change.c
+++ b/tool_change.c
@@ -143,7 +143,7 @@ static bool restore (void)
spindle_restore(plan_data.spindle.hal, gc_state.modal.spindle.state, gc_state.spindle.rpm);
if(!settings.flags.no_restore_position_after_M6) {
- previous.values[plane.axis_linear] += gc_get_offset(plane.axis_linear);
+ previous.values[plane.axis_linear] += gc_get_offset(plane.axis_linear, false);
mc_line(previous.values, &plan_data);
}
}
@@ -360,7 +360,7 @@ static status_code_t tool_change (parser_state_t *parser_state)
// Establish axis assignments.
- previous.values[plane.axis_linear] -= gc_get_offset(plane.axis_linear);
+ previous.values[plane.axis_linear] -= gc_get_offset(plane.axis_linear, false);
plan_line_data_t plan_data;