Fixed regression introduced with PR#673.

Added G30 as optional position for tool change.
Moved new tool change mode from PR#673 to $346 - Tool change options.
Moved Modbus RTU code from spindle plugin to the core.
Fixed bug in delayed task handler, might occasionally hang the controller.
This commit is contained in:
Terje Io
2025-04-19 14:08:46 +02:00
parent 77b65c6fdd
commit 837398221b
24 changed files with 1044 additions and 241 deletions
+1
View File
@@ -10,6 +10,7 @@ target_sources(grbl INTERFACE
${CMAKE_CURRENT_LIST_DIR}/machine_limits.c
${CMAKE_CURRENT_LIST_DIR}/messages.c
${CMAKE_CURRENT_LIST_DIR}/modbus.c
${CMAKE_CURRENT_LIST_DIR}/modbus_rtu.c
${CMAKE_CURRENT_LIST_DIR}/motion_control.c
${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
${CMAKE_CURRENT_LIST_DIR}/nuts_bolts.c
+2 -2
View File
@@ -1,6 +1,6 @@
## grblHAL ##
Latest build date is 20250415, see the [changelog](changelog.md) for details.
Latest build date is 20250419, see the [changelog](changelog.md) for details.
> [!NOTE]
> A settings reset will be performed on an update of builds prior to 20241208. Backup and restore of settings is recommended.
@@ -89,4 +89,4 @@ G/M-codes not supported by [legacy Grbl](https://github.com/gnea/grbl/wiki) are
Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes.
---
20250413
20250419
+21
View File
@@ -1,5 +1,26 @@
## grblHAL changelog
<a name="20250419">20250419
Core:
* Fixed regression introduced with [PR#673](https://github.com/grblHAL/core/pull/673), added G30 as optional position for tool change and moved new tool change mode from PR#673 to `$346` - _Tool change options_.
* Moved Modbus RTU code from spindle plugin to the core.
* For developers: deprecated `protocol_enqueue_foreground_task()`, replaced by `task_run_on_startup()` - added alias for the deprecated version.
Changed signature of `modbus_isup()` to return capabilities flag instead of boolean.
* Fixed bug in delayed task handler, might occasionally hang the controller. May be part of keypad issue [#17](https://github.com/grblHAL/Plugin_keypad/issues/17).
Plugins:
Keypad and spindle: updated for core changes.
Keypad, I2C display interface: fixed alignment issue that caused hardfault on WCO changes on some platforms. May resolve issue [#17](https://github.com/grblHAL/Plugin_keypad/issues/17).
---
<a name="20250415">20250415
Core:
+8 -2
View File
@@ -1351,7 +1351,7 @@ and less range over the total 255 PWM levels to signal different spindle speeds.
// Tool change settings (Group_Toolchange)
/*! @name $341 - Setting_ToolChangeMode
0 = Normal mode, 1 = Manual change, 2 = Manual change @ G59.3, 3 = Manual change and probe sensor @ G59.3 - sets TLO
0 = Normal mode, 1 = Manual change, 2 = Manual change @ G59.3, 3 = Manual change and probe tolsetter @ G59.3, 4 = Ignore M6
*/
///@{
#if !defined DEFAULT_TOOLCHANGE_MODE || defined __DOXYGEN__
@@ -1392,12 +1392,18 @@ and less range over the total 255 PWM levels to signal different spindle speeds.
#endif
///@}
/*! @name $346 - Setting_ToolChangeRestorePosition
/*! @name $346 - Setting_ToolChangeOptions
*/
///@{
#if !defined DEFAULT_TOOLCHANGE_NO_RESTORE_POSITION || defined __DOXYGEN__
#define DEFAULT_TOOLCHANGE_NO_RESTORE_POSITION Off
#endif
#if !defined DEFAULT_TOOLCHANGE_AT_G30 || defined __DOXYGEN__
#define DEFAULT_TOOLCHANGE_AT_G30 Off
#endif
#if !defined DEFAULT_TOOLCHANGE_FAST_PROBE_PULLOFF || defined __DOXYGEN__
#define DEFAULT_TOOLCHANGE_FAST_PROBE_PULLOFF Off
#endif
///@}
// Homing settings (Group_Homing)
+2
View File
@@ -89,6 +89,7 @@ typedef void (*on_state_change_ptr)(sys_state_t state);
typedef void (*on_override_changed_ptr)(override_changed_t override);
typedef void (*on_spindle_programmed_ptr)(spindle_ptrs_t *spindle, spindle_state_t state, float rpm, spindle_rpm_mode_t mode);
typedef void (*on_wco_changed_ptr)(void);
typedef void (*on_wco_saved_ptr)(coord_system_id_t id, coord_data_t *data);
typedef void (*on_program_completed_ptr)(program_flow_t program_flow, bool check_mode);
typedef void (*on_execute_realtime_ptr)(sys_state_t state);
typedef void (*on_unknown_accessory_override_ptr)(uint8_t cmd);
@@ -220,6 +221,7 @@ typedef struct {
on_report_handlers_init_ptr on_report_handlers_init;
on_spindle_programmed_ptr on_spindle_programmed;
on_wco_changed_ptr on_wco_changed;
on_wco_saved_ptr on_wco_saved;
on_program_completed_ptr on_program_completed;
on_execute_realtime_ptr on_execute_realtime;
on_execute_realtime_ptr on_execute_delay;
+1 -1
View File
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
#define GRBL_BUILD 20250415
#define GRBL_BUILD 20250419
#define GRBL_URL "https://github.com/grblHAL"
+64 -5
View File
@@ -76,7 +76,7 @@ typedef union {
} driver_startup_t;
#ifndef CORE_TASK_POOL_SIZE
#define CORE_TASK_POOL_SIZE 30
#define CORE_TASK_POOL_SIZE 40
#endif
typedef struct core_task {
@@ -92,7 +92,7 @@ DCRAM grbl_hal_t hal;
DCRAM static core_task_t task_pool[CORE_TASK_POOL_SIZE];
static driver_startup_t driver = { .ok = 0xFF };
static core_task_t *next_task = NULL, *immediate_task = NULL, *systick_task = NULL, *last_freed = NULL;
static core_task_t *next_task = NULL, *immediate_task = NULL, *on_booted = NULL, *systick_task = NULL, *last_freed = NULL;
static on_linestate_changed_ptr on_linestate_changed;
static settings_changed_ptr hal_settings_changed;
@@ -181,8 +181,10 @@ static void output_welcome_message (void *data)
static void onLinestateChanged (serial_linestate_t state)
{
if(state.dtr)
if(state.dtr) {
task_delete(output_welcome_message, NULL);
task_add_delayed(output_welcome_message, NULL, 200);
}
if(on_linestate_changed)
on_linestate_changed(state);
@@ -343,7 +345,7 @@ int grbl_enter (void)
if(driver.ok != 0xFF) {
sys.alarm = Alarm_SelftestFailed;
protocol_enqueue_foreground_task(report_driver_error, NULL);
task_run_on_startup(report_driver_error, NULL);
}
hal.stepper.enable(settings.steppers.energize, true);
@@ -465,6 +467,7 @@ __attribute__((always_inline)) static inline core_task_t *task_alloc (void)
__attribute__((always_inline)) static inline void task_free (core_task_t *task)
{
task->fn = NULL;
task->next = NULL;
if(last_freed == NULL)
last_freed = task;
}
@@ -541,7 +544,7 @@ ISR_CODE bool ISR_FUNC(task_add_delayed)(foreground_task_ptr fn, void *data, uin
if(next_task == NULL)
next_task = task;
else if((int32_t)(task->time - next_task->time) < 0) {
else if((int32_t)(task->time - next_task->time) <= 0) {
task->next = next_task;
next_task = task;
} else {
@@ -662,3 +665,59 @@ ISR_CODE bool ISR_FUNC(task_add_immediate)(foreground_task_ptr fn, void *data)
return task != NULL;
}
/*! \brief Enqueue a function to be called once by the foreground process after the boot sequence is completed.
\param fn pointer to a \a foreground_task_ptr type of function.
\param data pointer to data to be passed to the callee.
\returns true if successful, false otherwise.
*/
ISR_CODE bool ISR_FUNC(task_run_on_startup)(foreground_task_ptr fn, void *data)
{
if(sys.cold_start) {
core_task_t *task = NULL;
hal.irq_disable();
if(fn && (task = task_alloc())) {
task->fn = fn;
task->data = data;
task->next = NULL;
if(on_booted == NULL)
on_booted = task;
else {
core_task_t *t = on_booted;
while(t->next)
t = t->next;
t->next = task;
}
}
hal.irq_enable();
return task != NULL;
} else
return task_add_immediate(fn, data); // TODO: for now, to be removed...
}
// for core use only, called once from protocol.c on cold start
void task_execute_on_startup (void)
{
if(on_booted) do {
core_task_t *task = on_booted;
foreground_task_ptr fn = task->fn;
void *data = task->data;
on_booted = task->next;
task_free(task);
fn(data);
} while(on_booted);
if(!sys.driver_started)
while(true);
}
+2 -2
View File
@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2023-2024 Terje Io
Copyright (c) 2023-2025 Terje Io
Transforms derived from mzavatsky at Trossen Robotics
https://hypertriangle.com/~alex/delta-robot-tutorial/
get_cuboid_envelope() derived from javascript code in
@@ -541,7 +541,7 @@ static void delta_homing_complete (axes_signals_t cycle, bool success)
: machine.home_z - settings.homing.pulloff;
if(machine.cfg.flags.home_to_cuboid_top)
protocol_enqueue_foreground_task(delta_go_home, NULL);
task_add_immediate(delta_go_home, NULL);
}
if(on_homing_completed)
+4 -3
View File
@@ -44,9 +44,10 @@ PROGMEM static const message_t messages[] = {
{ .id = Message_CycleStart2Continue, .text = "Press cycle start to continue." },
{ .id = Message_TPCycleStart2Continue, .text = "Remove any touch plate and press cycle start to continue." },
{ .id = Message_ProbeFailedRetry, .text = "Probe failed, try again." },
{ .id = Message_ExecuteTPW, .text = "Perform a probe with $TPW first!", .type = Message_Warning},
{ .id = Message_ProbeProtected, .text = "Probe protection activated."},
{ .id = Message_Stop, .text = "Stop"}
{ .id = Message_ExecuteTPW, .text = "Perform a probe with $TPW first!", .type = Message_Warning },
{ .id = Message_ProbeProtected, .text = "Probe protection activated." },
{ .id = Message_Stop, .text = "Stop" },
{ .id = Message_CycleStart2TouchOff, .text = "Press cycle start to position for touch-off." }
};
const message_t *message_get (message_code_t id)
+2 -1
View File
@@ -47,7 +47,8 @@ typedef enum {
Message_ExecuteTPW = 20, //!< 20
Message_ProbeProtected = 21, //!< 21
Message_Stop = 22, //!< 22
Message_NextMessage //!< 23 - next unassigned message number.
Message_CycleStart2TouchOff = 23, //!< 23
Message_NextMessage //!< 24 - next unassigned message number.
} message_code_t;
typedef enum {
+25 -9
View File
@@ -4,20 +4,20 @@
Part of grblHAL
Copyright (c) 2023 Terje Io
Copyright (c) 2023-2025 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 <http://www.gnu.org/licenses/>.
along with grblHAL. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -25,21 +25,37 @@
#include <string.h>
#include "nuts_bolts.h"
#define N_MODBUS_API 2
static uint_fast16_t n_api = 0, tcp_api = N_MODBUS_API, rtu_api = N_MODBUS_API;
static modbus_api_t modbus[N_MODBUS_API] = {0};
bool modbus_isup (void)
modbus_cap_t modbus_isup (void)
{
bool ok = n_api > 0;
uint_fast16_t idx = n_api;
modbus_cap_t cap = {};
if(idx) do {
ok &= modbus[--idx].is_up();
idx--;
if(modbus[idx].is_up()) switch(modbus[idx].interface) {
case Modbus_InterfaceRTU:
cap.rtu = On;
break;
case Modbus_InterfaceASCII:
cap.ascii = On;
break;
case Modbus_InterfaceTCP:
cap.tcp = On;
break;
}
} while(idx);
return ok;
return cap;
}
bool modbus_enabled (void)
+16 -6
View File
@@ -4,20 +4,20 @@
Part of grblHAL
Copyright (c) 2023 Terje Io
Copyright (c) 2023-2025 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 <http://www.gnu.org/licenses/>.
along with grblHAL. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -83,6 +83,16 @@ typedef union {
};
} modbus_silence_timeout_t;
typedef union {
uint8_t ok;
struct {
uint8_t rtu :1,
ascii :1,
tcp :1,
unassigned :6;
};
} modbus_cap_t;
typedef bool (*modbus_is_up_ptr)(void);
typedef void (*modbus_flush_queue_ptr)(void);
typedef void (*modbus_set_silence_ptr)(const modbus_silence_timeout_t *timeout);
@@ -96,7 +106,7 @@ typedef struct {
modbus_send_ptr send;
} modbus_api_t;
bool modbus_isup (void);
modbus_cap_t modbus_isup (void);
bool modbus_enabled (void);
void modbus_flush_queue (void);
void modbus_set_silence (const modbus_silence_timeout_t *timeout);
+590
View File
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
/*
modbus_rtu.h - a lightweight ModBus RTU implementation
Part of grblHAL
Copyright (c) 2020-2025 Terje Io
grblHAL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with grblHAL. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _MODBUS_RTU_H_
#define _MODBUS_RTU_H_
#include "grbl/modbus.h"
typedef enum {
ModBus_Idle,
ModBus_Silent,
ModBus_TX,
ModBus_AwaitReply,
ModBus_Timeout,
ModBus_GotReply,
ModBus_Exception,
ModBus_Retry
} modbus_state_t;
typedef void (*stream_set_direction_ptr)(bool tx);
typedef struct {
set_baud_rate_ptr set_baud_rate;
stream_set_direction_ptr set_direction; // NULL if auto direction
get_stream_buffer_count_ptr get_tx_buffer_count;
get_stream_buffer_count_ptr get_rx_buffer_count;
stream_write_n_ptr write;
stream_read_ptr read;
flush_stream_buffer_ptr flush_tx_buffer;
flush_stream_buffer_ptr flush_rx_buffer;
} modbus_stream_t;
void modbus_rtu_init (void);
bool modbus_rtu_send (modbus_message_t *msg, const modbus_callbacks_t *callbacks, bool block);
#endif
+2 -2
View File
@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2017-2024 Terje Io
Copyright (c) 2017-2025 Terje Io
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
@@ -286,7 +286,7 @@ bool nvs_buffer_init (void)
grbl.report.status_message(Status_SettingReadFail);
}
} else
protocol_enqueue_foreground_task(report_warning, "Not enough heap for NVS buffer!");
task_run_on_startup(report_warning, "Not enough heap for NVS buffer!");
// Clear settings dirty flags
memset(&settings_dirty, 0, sizeof(settings_dirty_t));
+1 -1
View File
@@ -232,7 +232,7 @@ bool plan_reset (void)
}
if(block_buffer_size != settings.planner_buffer_blocks)
protocol_enqueue_foreground_task(report_plain, "Planner buffer size was reduced!");
task_run_on_startup(report_plain, "Planner buffer size was reduced!");
}
if(block_buffer == NULL)
+6 -89
View File
@@ -49,25 +49,13 @@ typedef union {
};
} line_flags_t;
typedef struct {
void *data;
fg_task_ptr task;
} delayed_task_t;
typedef struct {
volatile uint_fast8_t head;
volatile uint_fast8_t tail;
delayed_task_t task[RT_QUEUE_SIZE];
} realtime_queue_t;
extern void task_execute_on_startup (void);
static uint_fast16_t char_counter = 0;
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
static char xcommand[LINE_BUFFER_SIZE];
static bool keep_rt_commands = false;
static realtime_queue_t realtime_queue = {0};
static on_execute_realtime_ptr on_execute_delay;
static void protocol_execute_rt_commands (sys_state_t state);
static void protocol_exec_rt_suspend (sys_state_t state);
// add gcode to execute not originating from normal input stream
@@ -86,16 +74,6 @@ bool protocol_enqueue_gcode (char *gcode)
return ok;
}
static void protocol_on_execute_delay (sys_state_t state)
{
if(sys.rt_exec_state & EXEC_RT_COMMAND) {
system_clear_exec_state_flag(EXEC_RT_COMMAND);
protocol_execute_rt_commands(0);
}
on_execute_delay(state);
}
static bool recheck_line (char *line, line_flags_t *flags)
{
bool keep_rt_commands = false, first_char = true;
@@ -196,20 +174,16 @@ bool protocol_main_loop (void)
}
#endif
// All systems go!
protocol_enqueue_foreground_task(system_execute_startup, NULL); // Schedule startup script for execution.
task_add_immediate(system_execute_startup, NULL); // Schedule startup script for execution.
}
// Ensure spindle and coolant is switched off on a cold start
if(sys.cold_start) {
spindle_all_off();
hal.coolant.set_state((coolant_state_t){0});
if(realtime_queue.head != realtime_queue.tail)
system_set_exec_state_flag(EXEC_RT_COMMAND); // execute any boot up commands
on_execute_delay = grbl.on_execute_delay;
grbl.on_execute_delay = protocol_on_execute_delay;
sys.cold_start = false;
} else // TODO: if flushing entries from the queue that has allocated data associated then these will be orphaned/leaked.
memset(&realtime_queue, 0, sizeof(realtime_queue_t));
system_set_exec_state_flag(EXEC_RT_COMMAND); // execute any statup up tasks
}
// ---------------------------------------------------------------------------------
// Primary loop! Upon a system abort, this exits back to main() to reset the system.
@@ -642,7 +616,7 @@ bool protocol_exec_rt_system (void)
report_pid_log();
if(rt_exec & EXEC_RT_COMMAND)
protocol_execute_rt_commands(0);
task_execute_on_startup();
rt_exec &= ~(EXEC_STOP|EXEC_STATUS_REPORT|EXEC_GCODE_REPORT|EXEC_PID_REPORT|EXEC_TLO_REPORT|EXEC_RT_COMMAND); // clear requests already processed
@@ -969,7 +943,7 @@ ISR_CODE bool ISR_FUNC(protocol_enqueue_realtime_command)(char c)
case CMD_MPG_MODE_TOGGLE: // Switch off MPG mode
if((drop = hal.stream.type == StreamType_MPG))
protocol_enqueue_foreground_task(stream_mpg_set_mode, NULL);
task_add_immediate(stream_mpg_set_mode, NULL);
break;
case CMD_AUTO_REPORTING_TOGGLE:
@@ -1058,63 +1032,6 @@ ISR_CODE bool ISR_FUNC(protocol_enqueue_realtime_command)(char c)
return drop;
}
static const uint32_t dummy_data = 0;
/*! \brief Enqueue a function to be called once by the foreground process.
\param fn pointer to a \a foreground_task_ptr type of function.
\param data pointer to data to be passed to the callee.
\returns true if successful, false otherwise.
*/
ISR_CODE bool ISR_FUNC(protocol_enqueue_foreground_task)(fg_task_ptr fn, void *data)
{
bool ok;
uint_fast8_t bptr = (realtime_queue.head + 1) & (RT_QUEUE_SIZE - 1); // Get next head pointer
if((ok = bptr != realtime_queue.tail)) { // If not buffer full
realtime_queue.task[realtime_queue.head].data = data;
realtime_queue.task[realtime_queue.head].task = fn; // add function pointer to buffer,
realtime_queue.head = bptr; // update pointer and
if(sys.driver_started)
system_set_exec_state_flag(EXEC_RT_COMMAND); // flag it for execute
}
return ok;
}
/*! \brief Enqueue a function to be called once by the foreground process.
\param fn pointer to a \a on_execute_realtime_ptr type of function.
\returns true if successful, false otherwise.
__NOTE:__ Deprecated, use protocol_enqueue_foreground_task instead.
*/
ISR_CODE bool ISR_FUNC(protocol_enqueue_rt_command)(on_execute_realtime_ptr fn)
{
return protocol_enqueue_foreground_task(fn, (void *)&dummy_data);
}
// Execute enqueued functions.
static void protocol_execute_rt_commands (sys_state_t state)
{
while(realtime_queue.tail != realtime_queue.head) {
uint_fast8_t bptr = realtime_queue.tail;
if(realtime_queue.task[bptr].task.fn) {
if(realtime_queue.task[bptr].data == (void *)&dummy_data) {
on_execute_realtime_ptr call = realtime_queue.task[bptr].task.fn_deprecated;
realtime_queue.task[bptr].task.fn_deprecated = NULL;
call(state_get());
} else {
foreground_task_ptr call = realtime_queue.task[bptr].task.fn;
realtime_queue.task[bptr].task.fn = NULL;
call(realtime_queue.task[bptr].data);
}
}
realtime_queue.tail = (bptr + 1) & (RT_QUEUE_SIZE - 1);
}
if(!sys.driver_started)
while(true);
}
void protocol_execute_noop (sys_state_t state)
{
(void)state;
+4 -3
View File
@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2016-2024 Terje Io
Copyright (c) 2016-2025 Terje Io
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
@@ -49,8 +49,9 @@ bool protocol_main_loop (void);
bool protocol_execute_realtime (void);
bool protocol_exec_rt_system (void);
void protocol_execute_noop (uint_fast16_t state);
bool protocol_enqueue_rt_command (on_execute_realtime_ptr fn);
bool protocol_enqueue_foreground_task (fg_task_ptr fn, void *data);
// Deprecated, to be deleted
#define protocol_enqueue_foreground_task(fn, data) task_run_on_startup(fn, data)
// Executes the auto cycle feature, if enabled.
void protocol_auto_cycle_start (void);
+33 -15
View File
@@ -84,6 +84,8 @@ PROGMEM const settings_t defaults = {
.flags.force_initialization_alarm = DEFAULT_FORCE_INITIALIZATION_ALARM,
.flags.restore_overrides = DEFAULT_RESET_OVERRIDES,
.flags.no_restore_position_after_M6 = DEFAULT_TOOLCHANGE_NO_RESTORE_POSITION,
.flags.tool_change_at_g30 = DEFAULT_TOOLCHANGE_AT_G30,
.flags.tool_change_fast_pulloff = DEFAULT_TOOLCHANGE_FAST_PROBE_PULLOFF,
.flags.no_unlock_after_estop = DEFAULT_NO_UNLOCK_AFTER_ESTOP,
.flags.keep_offsets_on_reset = DEFAULT_KEEP_OFFSETS_ON_RESET,
@@ -1047,9 +1049,9 @@ static status_code_t set_probe_flags (setting_id_t id, uint_fast16_t int_value)
static status_code_t set_tool_change_mode (setting_id_t id, uint_fast16_t int_value)
{
if(!hal.driver_cap.atc && hal.stream.suspend_read && int_value <= ToolChange_FastSemiAutomatic) {
if(!hal.driver_cap.atc && hal.stream.suspend_read && int_value <= ToolChange_Ignore) {
#if COMPATIBILITY_LEVEL > 1
if((toolchange_mode_t)int_value == ToolChange_Manual_G59_3 || (toolchange_mode_t)int_value == ToolChange_SemiAutomatic || (toolchange_mode_t)int_value == ToolChange_FastSemiAutomatic)
if((toolchange_mode_t)int_value == ToolChange_Manual_G59_3 || (toolchange_mode_t)int_value == ToolChange_SemiAutomatic)
return Status_InvalidStatement;
#endif
settings.tool_change.mode = (toolchange_mode_t)int_value;
@@ -1070,12 +1072,14 @@ static status_code_t set_tool_change_probing_distance (setting_id_t id, float va
return Status_OK;
}
static status_code_t set_tool_restore_pos (setting_id_t id, uint_fast16_t int_value)
static status_code_t set_toolchange_flags (setting_id_t id, uint_fast16_t int_value)
{
if(hal.driver_cap.atc)
return Status_InvalidStatement;
settings.flags.no_restore_position_after_M6 = int_value == 0;
settings.flags.no_restore_position_after_M6 = !(int_value & 0b001);
settings.flags.tool_change_at_g30 = !!(int_value & 0b010);
settings.flags.tool_change_fast_pulloff = !!(int_value & 0b100);
return Status_OK;
}
@@ -1570,8 +1574,10 @@ static uint32_t get_int (setting_id_t id)
value = settings.tool_change.mode;
break;
case Setting_ToolChangeRestorePosition:
value = settings.flags.no_restore_position_after_M6 ? 0 : 1;
case Setting_ToolChangeOptions:
value = (settings.flags.no_restore_position_after_M6 ? 0b000 : 0b001) |
(settings.flags.tool_change_at_g30 ? 0b010 : 0b000) |
(settings.flags.tool_change_fast_pulloff ? 0b100 : 0b00);
break;
case Setting_DisableG92Persistence:
@@ -1863,7 +1869,7 @@ static bool is_setting_available (const setting_detail_t *setting, uint_fast16_t
case Setting_ToolChangeFeedRate:
case Setting_ToolChangeSeekRate:
case Setting_ToolChangePulloffRate:
case Setting_ToolChangeRestorePosition:
case Setting_ToolChangeOptions:
available = !hal.driver_cap.atc;
break;
@@ -2139,12 +2145,12 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_AxisHomingFeedRate, Group_Axis0, "-axis homing locate feed rate", axis_rate, Format_Decimal, "###0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_float, is_setting_available, AXIS_OPTS },
{ Setting_AxisHomingSeekRate, Group_Axis0, "-axis homing search seek rate", axis_rate, Format_Decimal, "###0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_float, is_setting_available, AXIS_OPTS },
{ Setting_SpindleAtSpeedTolerance, Group_Spindle, "Spindle at speed tolerance", "percent", Format_Decimal, "##0.0", NULL, NULL, Setting_IsExtendedFn, set_float, get_float, is_setting_available },
{ Setting_ToolChangeMode, Group_Toolchange, "Tool change mode", NULL, Format_RadioButtons, "Normal,Manual touch off,Manual touch off @ G59.3,Automatic touch off @ G59.3,Ignore M6,Fast Automatic touch off @ G59.3", NULL, NULL, Setting_IsExtendedFn, set_tool_change_mode, get_int, is_setting_available },
{ Setting_ToolChangeMode, Group_Toolchange, "Tool change mode", NULL, Format_RadioButtons, "Normal,Manual touch off,Manual touch off @ G59.3,Automatic touch off @ G59.3,Ignore M6", NULL, NULL, Setting_IsExtendedFn, set_tool_change_mode, get_int, is_setting_available },
{ Setting_ToolChangeProbingDistance, Group_Toolchange, "Tool change probing distance", "mm", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtendedFn, set_tool_change_probing_distance, get_float, is_setting_available },
{ Setting_ToolChangeFeedRate, Group_Toolchange, "Tool change locate feed rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtended, &settings.tool_change.feed_rate, NULL, is_setting_available },
{ Setting_ToolChangeSeekRate, Group_Toolchange, "Tool change search seek rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtended, &settings.tool_change.seek_rate, NULL, is_setting_available },
{ Setting_ToolChangePulloffRate, Group_Toolchange, "Tool change probe pull-off rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtended, &settings.tool_change.pulloff_rate, NULL, is_setting_available },
{ Setting_ToolChangeRestorePosition, Group_Toolchange, "Restore position after M6", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_tool_restore_pos, get_int, is_setting_available },
{ Setting_ToolChangeOptions, Group_Toolchange, "Tool change options", NULL, Format_Bitfield, "Restore position after M6,Change tool at G30,Fast probe pull off", NULL, NULL, Setting_IsExtendedFn, set_toolchange_flags, get_int, is_setting_available },
{ Setting_DualAxisLengthFailPercent, Group_Limits_DualAxis, "Dual axis length fail", "percent", Format_Decimal, "##0.0", "0", "100", Setting_IsExtended, &settings.homing.dual_axis.fail_length_percent, NULL, is_setting_available },
{ Setting_DualAxisLengthFailMin, Group_Limits_DualAxis, "Dual axis length fail min", "mm", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_min, NULL, is_setting_available },
{ Setting_DualAxisLengthFailMax, Group_Limits_DualAxis, "Dual axis length fail max", "mm", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_max, NULL, is_setting_available },
@@ -2331,17 +2337,21 @@ PROGMEM static const setting_descr_t setting_descr[] = {
"NOTE: if the spindle on delay is set to 0 the timeout defaults to one minute."
},
{ Setting_ToolChangeMode, "Normal: allows jogging for manual touch off. Set new position manually.\\n\\n"
"Manual touch off: retracts tool axis to home position for tool change, use jogging or $TPW for touch off.\\n\\n"
"Manual touch off @ G59.3: retracts tool axis to home position then to G59.3 position for tool change, use jogging or $TPW for touch off.\\n\\n"
"Automatic touch off @ G59.3: retracts tool axis to home position for tool change, then to G59.3 position for automatic touch off.\\n\\n"
"Fast Automatic touch off @ G59.3: Same as automatic mode, except that it uses G38.4 style probing for faster touch off.\\n\\n"
"All modes except \"Normal\" and \"Ignore M6\" returns the tool (controlled point) to original position after touch off."
"Manual touch off: rapids to tool change position, use jogging or $TPW for touch off.\\n\\n"
"Manual touch off @ G59.3: rapids to tool change position, after change to G59.3 position for manual touch off. Use jogging or $TPW for touch off.\\n\\n"
"Automatic touch off @ G59.3: rapids to tool change position, after change to G59.3 position for automatic touch off.\\n\\n"
"Depending on settings the tool (controlled point) will be moved back to the to original position after touch off. "
"\"tool change position\" is either tool axis home, G59.3 or G30 position depending on settings."
},
{ Setting_ToolChangeProbingDistance, "Maximum probing distance for automatic or $TPW touch off." },
{ Setting_ToolChangeFeedRate, "Feed rate to slowly engage tool change sensor to determine the tool offset accurately." },
{ Setting_ToolChangeSeekRate, "Seek rate to quickly find the tool change sensor before the slower locating phase." },
{ Setting_ToolChangePulloffRate, "Pull-off rate for the retract move before the slower locating phase." },
{ Setting_ToolChangeRestorePosition, "When set the spindle is moved so that the controlled point (tool tip) is the same as before the M6 command, if not the spindle is only moved to the Z home position." },
{ Setting_ToolChangeOptions, "Restore position after M6: when set the spindle is moved so that the controlled point (tool tip) is the same as before the M6 command,"
"if not the spindle is only moved to the Z home position.\\n\\n"
"Change tool at G30: when set rapids to the G30 position via tool axis home. Requires axes to be homed.\\n\\n"
"Fast probe pulloff: use G38.4 style probing for faster touch off."
},
{ Setting_DualAxisLengthFailPercent, "Dual axis length fail in percent of axis max travel." },
{ Setting_DualAxisLengthFailMin, "Dual axis length fail minimum distance." },
{ Setting_DualAxisLengthFailMax, "Dual axis length fail maximum distance." },
@@ -2475,6 +2485,9 @@ void settings_write_coord_data (coord_system_id_t id, float (*coord_data)[N_AXIS
protocol_buffer_synchronize();
#endif
if(grbl.on_wco_saved)
grbl.on_wco_saved(id, (coord_data_t *)coord_data);
if(hal.nvs.type != NVS_None)
hal.nvs.memcpy_to_nvs(NVS_ADDR_PARAMETERS + id * (sizeof(coord_data_t) + NVS_CRC_BYTES), (uint8_t *)coord_data, sizeof(coord_data_t), true);
}
@@ -2572,6 +2585,11 @@ static void sanity_check (void)
} while(idx);
}
if(settings.tool_change.mode > ToolChange_Ignore) {
settings.tool_change.mode = ToolChange_SemiAutomatic;
settings.flags.tool_change_fast_pulloff = On;
}
if(SLEEP_DURATION <= 0.0f)
settings.flags.sleep_enable = Off;
+5 -4
View File
@@ -214,7 +214,7 @@ typedef enum {
Setting_ToolChangeFeedRate = 343,
Setting_ToolChangeSeekRate = 344,
Setting_ToolChangePulloffRate = 345,
Setting_ToolChangeRestorePosition = 346,
Setting_ToolChangeOptions = 346,
Setting_DualAxisLengthFailPercent = 347,
Setting_DualAxisLengthFailMin = 348,
@@ -592,7 +592,9 @@ typedef union {
no_unlock_after_estop :1,
settings_downgrade :1,
keep_offsets_on_reset :1,
unassigned :14;
tool_change_at_g30 :1,
tool_change_fast_pulloff :1,
unassigned :12;
};
} settingflags_t;
@@ -815,8 +817,7 @@ typedef enum {
ToolChange_Manual,
ToolChange_Manual_G59_3,
ToolChange_SemiAutomatic,
ToolChange_Ignore,
ToolChange_FastSemiAutomatic
ToolChange_Ignore
} toolchange_mode_t;
typedef struct {
+2 -2
View File
@@ -339,7 +339,7 @@ static bool stream_select (const io_stream_t *stream, bool add)
stream_mpg_enable(false);
mpg.flags.mpg_control = On;
} else if(mpg_enable)
protocol_enqueue_foreground_task(stream_mpg_set_mode, (void *)1);
task_add_immediate(stream_mpg_set_mode, (void *)1);
memcpy(&hal.stream, stream, sizeof(io_stream_t));
@@ -750,7 +750,7 @@ bool debug_stream_init (void)
if(stream_enumerate_streams(debug_claim_stream))
hal.debug.write(ASCII_EOL "UART debug active:" ASCII_EOL);
else
protocol_enqueue_foreground_task(report_warning, "Failed to initialize debug stream!");
task_run_on_startup(report_warning, "Failed to initialize debug stream!");
return hal.debug.write == debug_write;
}
+1 -1
View File
@@ -168,7 +168,7 @@ void stream_passthru_init (uint8_t instance, uint32_t baud_rate, bool start)
if((hal.stream.state.passthru = stream != NULL)) {
protocol_enqueue_foreground_task(passthru_start1, NULL); // enter passthrouh mode after finished booting grblHAL
task_run_on_startup(passthru_start1, NULL); // enter passthrouh mode after finished booting grblHAL
memcpy(&dest, stream, sizeof(io_stream_t));
dest.set_enqueue_rt_handler(sink_uart_rx);
+2 -1
View File
@@ -3,7 +3,7 @@
Part of grblHAL
Copyright (c) 2024 Terje Io
Copyright (c) 2024-2025 Terje Io
grblHAL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@ typedef void (*foreground_task_ptr)(void *data);
bool task_add_immediate (foreground_task_ptr fn, void *data);
bool task_add_delayed (foreground_task_ptr fn, void *data, uint32_t delay_ms);
bool task_run_on_startup (foreground_task_ptr fn, void *data);
void task_delete (foreground_task_ptr fn, void *data);
bool task_add_systick (foreground_task_ptr fn, void *data);
void task_delete_systick (foreground_task_ptr fn, void *data);
+194 -92
View File
@@ -37,21 +37,21 @@
#define TOOL_CHANGE_PROBE_RETRACT_DISTANCE 2.0f
#endif
static bool block_cycle_start, probe_toolsetter;
static bool block_cycle_start, probe_toolsetter, change_at_g30;
static volatile bool execute_posted = false;
static volatile uint32_t spin_lock = 0;
static float tool_change_position;
static tool_data_t current_tool = {0}, *next_tool = NULL;
static tool_data_t current_tool = {}, *next_tool = NULL;
static plane_t plane;
static coord_data_t target = {0}, previous;
static coord_data_t target = {}, previous;
static driver_reset_ptr driver_reset = NULL;
static enqueue_realtime_command_ptr enqueue_realtime_command = NULL;
static control_signals_callback_ptr control_interrupt_callback = NULL;
static on_homing_completed_ptr on_homing_completed = NULL;
static on_probe_completed_ptr on_probe_completed;
static on_wco_saved_ptr on_wco_saved;
// Clear tool length offset on homing
static void tc_on_homing_complete (axes_signals_t homing_cycle, bool success)
static void onHomingComplete (axes_signals_t homing_cycle, bool success)
{
if(on_homing_completed)
on_homing_completed(homing_cycle, success);
@@ -60,6 +60,15 @@ static void tc_on_homing_complete (axes_signals_t homing_cycle, bool success)
system_clear_tlo_reference(homing_cycle);
}
static void onWcoSaved (coord_system_id_t id, coord_data_t *offset)
{
if(on_wco_saved)
on_wco_saved(id, offset);
if(id == gc_state.modal.coord_system.id && block_cycle_start)
block_cycle_start = change_at_g30;
}
// Set tool offset on successful $TPW probe, prompt for retry on failure.
// Called via probe completed event.
static void onProbeCompleted (void)
@@ -128,21 +137,25 @@ static void reset (void)
// Restore coolant and spindle status, return controlled point to original position.
static bool restore (void)
{
bool ok;
plan_line_data_t plan_data;
plan_data_init(&plan_data);
plan_data.condition.rapid_motion = On;
target.values[plane.axis_linear] = tool_change_position;
mc_line(target.values, &plan_data);
if(!(ok = (target.values[plane.axis_0] == previous.values[plane.axis_0] &&
target.values[plane.axis_1] == previous.values[plane.axis_1]))) {
if(!settings.flags.no_restore_position_after_M6) {
memcpy(&target, &previous, sizeof(coord_data_t));
target.values[plane.axis_linear] = tool_change_position;
mc_line(target.values, &plan_data);
target.values[plane.axis_linear] = sys.home_position[plane.axis_linear];
if((ok = mc_line(target.values, &plan_data)) && !settings.flags.no_restore_position_after_M6) {
memcpy(&target, &previous, sizeof(coord_data_t));
target.values[plane.axis_linear] = sys.home_position[plane.axis_linear];
ok = mc_line(target.values, &plan_data);
}
}
if(protocol_buffer_synchronize()) {
if(ok && protocol_buffer_synchronize()) {
sync_position();
@@ -164,6 +177,48 @@ static bool restore (void)
return !ABORTED;
}
static bool go_linear_home (plan_line_data_t *pl_data)
{
system_convert_array_steps_to_mpos(target.values, sys.position);
if(target.values[plane.axis_linear] != sys.home_position[plane.axis_linear]) {
target.values[plane.axis_linear] = sys.home_position[plane.axis_linear];
if(!mc_line(target.values, pl_data))
return false;
}
return true;
}
#if COMPATIBILITY_LEVEL <= 1
static bool go_toolsetter (plan_line_data_t *pl_data)
{
// G59.3 contains offsets to toolsetter.
settings_read_coord_data(CoordinateSystem_G59_3, &target.values);
float tmp_pos = target.values[plane.axis_linear];
target.values[plane.axis_linear] = sys.home_position[plane.axis_linear];
if(probe_toolsetter)
grbl.on_probe_toolsetter(next_tool, &target, false, true);
if(!mc_line(target.values, pl_data))
return false;
target.values[plane.axis_linear] = tmp_pos;
if(!mc_line(target.values, pl_data))
return false;
if(probe_toolsetter)
grbl.on_probe_toolsetter(next_tool, NULL, true, true);
return true;
}
#endif
// Issue warning on cycle start event if touch off by $TPW is pending.
// Used in Manual and Manual_G59_3 modes ($341=1 or $341=2). Called from the foreground process.
static void execute_warning (void *data)
@@ -171,6 +226,38 @@ static void execute_warning (void *data)
grbl.report.feedback_message(Message_ExecuteTPW);
}
// Execute restore position after tool change, either back to original or to toolsetter for touch off.
// Used when G30 position is used for changing the tool. Called from the foreground process.
static void execute_return_from_g30 (void *data)
{
bool ok;
plan_line_data_t plan_data;
plan_data_init(&plan_data);
plan_data.condition.rapid_motion = On;
if((ok = go_linear_home(&plan_data))) {
#if COMPATIBILITY_LEVEL <= 1
if(settings.tool_change.mode == ToolChange_Manual_G59_3)
ok = go_toolsetter(&plan_data);
else
#endif
{
// Rapid to original XY position.
target.values[plane.axis_0] = previous.values[plane.axis_0];
target.values[plane.axis_1] = previous.values[plane.axis_1];
ok = mc_line(target.values, &plan_data);
}
}
if(ok) {
protocol_buffer_synchronize();
sync_position();
}
change_at_g30 = execute_posted = false;
}
// Execute restore position after touch off (on cycle start event).
// Used in Manual and Manual_G59_3 modes ($341=1 or $341=2). Called from the foreground process.
static void execute_restore (void *data)
@@ -205,7 +292,7 @@ static void execute_probe (void *data)
bool ok;
coord_data_t offset;
plan_line_data_t plan_data;
gc_parser_flags_t flags = {0};
gc_parser_flags_t flags = {};
// G59.3 contains offsets to position of TLS.
settings_read_coord_data(CoordinateSystem_G59_3, &offset.values);
@@ -213,13 +300,15 @@ static void execute_probe (void *data)
plan_data_init(&plan_data);
plan_data.condition.rapid_motion = On;
ok = !change_at_g30 || go_linear_home(&plan_data);
target.values[plane.axis_0] = offset.values[plane.axis_0];
target.values[plane.axis_1] = offset.values[plane.axis_1];
if(probe_toolsetter)
grbl.on_probe_toolsetter(next_tool, &target, false, true);
if((ok = mc_line(target.values, &plan_data))) {
if((ok = ok && mc_line(target.values, &plan_data))) {
target.values[plane.axis_linear] = offset.values[plane.axis_linear];
ok = mc_line(target.values, &plan_data);
@@ -233,29 +322,24 @@ static void execute_probe (void *data)
set_probe_target(&target, plane.axis_linear);
if((ok = ok && mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found))
{
if((ok = ok && mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found)) {
system_convert_array_steps_to_mpos(target.values, sys.probe_position);
if(settings.tool_change.mode == ToolChange_FastSemiAutomatic){
// Retract slowly until contact lost.
plan_data.feed_rate = settings.tool_change.feed_rate;
target.values[plane.axis_linear] += TOOL_CHANGE_PROBE_RETRACT_DISTANCE;
flags.probe_is_away = true;
ok = mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found;
} else {
// Retract a bit and perform slow probe.
plan_data.feed_rate = settings.tool_change.pulloff_rate;
target.values[plane.axis_linear] += TOOL_CHANGE_PROBE_RETRACT_DISTANCE;
if((ok = mc_line(target.values, &plan_data))) {
plan_data.feed_rate = settings.tool_change.feed_rate;
target.values[plane.axis_linear] -= (TOOL_CHANGE_PROBE_RETRACT_DISTANCE + 2.0f);
ok = mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found;
if((flags.probe_is_away = settings.flags.tool_change_fast_pulloff))
plan_data.feed_rate = settings.tool_change.feed_rate; // Retract slowly until contact lost.
else {
// Retract a bit and perform slow probe.
plan_data.feed_rate = settings.tool_change.pulloff_rate;
if((ok = mc_line(target.values, &plan_data))) {
plan_data.feed_rate = settings.tool_change.feed_rate;
target.values[plane.axis_linear] -= (TOOL_CHANGE_PROBE_RETRACT_DISTANCE + 2.0f);
}
}
ok = ok && mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found;
}
}
if(ok) {
if(!(sys.tlo_reference_set.mask & bit(plane.axis_linear))) {
@@ -287,14 +371,13 @@ ISR_CODE static void ISR_FUNC(trap_control_cycle_start)(control_signals_t signal
if(signals.cycle_start) {
if(!execute_posted) {
if(!block_cycle_start)
execute_posted = protocol_enqueue_foreground_task(
(settings.tool_change.mode == ToolChange_SemiAutomatic ||
settings.tool_change.mode == ToolChange_FastSemiAutomatic)
? execute_probe
: execute_restore,
NULL);
execute_posted = task_add_immediate(settings.tool_change.mode == ToolChange_SemiAutomatic
? execute_probe
: execute_restore, NULL);
else if(change_at_g30)
execute_posted = task_add_immediate(execute_return_from_g30, NULL);
else
protocol_enqueue_foreground_task(execute_warning, NULL);
task_add_immediate(execute_warning, NULL);
}
signals.cycle_start = Off;
} else
@@ -312,14 +395,13 @@ ISR_CODE static bool ISR_FUNC(trap_stream_cycle_start)(char c)
if((drop = (c == CMD_CYCLE_START || c == CMD_CYCLE_START_LEGACY))) {
if(!execute_posted) {
if(!block_cycle_start)
execute_posted = protocol_enqueue_foreground_task(
(settings.tool_change.mode == ToolChange_SemiAutomatic ||
settings.tool_change.mode == ToolChange_FastSemiAutomatic)
? execute_probe
: execute_restore,
NULL);
execute_posted = task_add_immediate(settings.tool_change.mode == ToolChange_SemiAutomatic
? execute_probe
: execute_restore, NULL);
else if(change_at_g30)
execute_posted = task_add_immediate(execute_return_from_g30, NULL);
else
protocol_enqueue_foreground_task(execute_warning, NULL);
task_add_immediate(execute_warning, NULL);
}
} else
drop = enqueue_realtime_command(c);
@@ -356,7 +438,7 @@ static status_code_t tool_change (parser_state_t *parser_state)
return Status_OK;
#if COMPATIBILITY_LEVEL > 1
if(settings.tool_change.mode == ToolChange_Manual_G59_3 || settings.tool_change.mode == ToolChange_SemiAutomatic || settings.tool_change.mode == ToolChange_FastSemiAutomatic)
if(settings.tool_change.mode == ToolChange_Manual_G59_3 || settings.tool_change.mode == ToolChange_SemiAutomatic)
return Status_GcodeUnsupportedCommand;
#endif
@@ -381,12 +463,15 @@ static status_code_t tool_change (parser_state_t *parser_state)
if((sys.homed.mask & homed_req) != homed_req)
return Status_HomingRequired;
if(settings.tool_change.mode != ToolChange_SemiAutomatic &&
settings.tool_change.mode != ToolChange_FastSemiAutomatic)
grbl.on_probe_completed = on_probe_completed;
plan_line_data_t plan_data;
coord_data_t change_position;
block_cycle_start = (settings.tool_change.mode != ToolChange_SemiAutomatic &&
settings.tool_change.mode != ToolChange_FastSemiAutomatic);
if(settings.tool_change.mode != ToolChange_SemiAutomatic && grbl.on_probe_completed != onProbeCompleted) {
on_probe_completed = grbl.on_probe_completed;
grbl.on_probe_completed = onProbeCompleted;
}
block_cycle_start = settings.tool_change.mode != ToolChange_SemiAutomatic;
// Stop spindle and coolant.
spindle_all_off();
@@ -396,8 +481,7 @@ static status_code_t tool_change (parser_state_t *parser_state)
probe_toolsetter = grbl.on_probe_toolsetter != NULL &&
(settings.tool_change.mode == ToolChange_Manual ||
settings.tool_change.mode == ToolChange_Manual_G59_3 ||
settings.tool_change.mode == ToolChange_SemiAutomatic ||
settings.tool_change.mode == ToolChange_FastSemiAutomatic);
settings.tool_change.mode == ToolChange_SemiAutomatic);
// Save current position.
system_convert_array_steps_to_mpos(previous.values, sys.position);
@@ -406,7 +490,7 @@ static status_code_t tool_change (parser_state_t *parser_state)
previous.values[plane.axis_linear] -= gc_get_offset(plane.axis_linear, false);
plan_line_data_t plan_data;
memcpy(&change_position, &previous, sizeof(coord_data_t));
plan_data_init(&plan_data);
plan_data.condition.rapid_motion = On;
@@ -416,36 +500,39 @@ static status_code_t tool_change (parser_state_t *parser_state)
// tool_change_position = ?
//else
tool_change_position = sys.home_position[plane.axis_linear]; // - settings.homing.flags.force_set_origin ? LINEAR_AXIS_HOME_OFFSET : 0.0f;
if((change_at_g30 = (settings.flags.tool_change_at_g30) && (sys.homed.mask & (X_AXIS_BIT|Y_AXIS_BIT|Z_AXIS_BIT)) == (X_AXIS_BIT|Y_AXIS_BIT|Z_AXIS_BIT)))
settings_read_coord_data(CoordinateSystem_G30, &change_position.values);
else
change_position.values[plane.axis_linear] = sys.home_position[plane.axis_linear]; // - settings.homing.flags.force_set_origin ? LINEAR_AXIS_HOME_OFFSET : 0.0f;
// Rapid to home position of linear axis.
memcpy(&target, &previous, sizeof(coord_data_t));
target.values[plane.axis_linear] = tool_change_position;
if(!mc_line(target.values, &plan_data))
if(!go_linear_home(&plan_data))
return Status_Reset;
if(change_at_g30) {
// Rapid to G30 position.
if(!(target.values[plane.axis_0] == change_position.values[plane.axis_0] &&
target.values[plane.axis_1] == change_position.values[plane.axis_1])) {
target.values[plane.axis_0] = change_position.values[plane.axis_0];
target.values[plane.axis_1] = change_position.values[plane.axis_1];
if(!mc_line(target.values, &plan_data))
return Status_Reset;
}
if(target.values[plane.axis_linear] != change_position.values[plane.axis_linear]) {
target.values[plane.axis_linear] = change_position.values[plane.axis_linear];
if(!mc_line(target.values, &plan_data))
return Status_Reset;
}
}
#if COMPATIBILITY_LEVEL <= 1
if(settings.tool_change.mode == ToolChange_Manual_G59_3) {
// G59.3 contains offsets to tool change position.
settings_read_coord_data(CoordinateSystem_G59_3, &target.values);
float tmp_pos = target.values[plane.axis_linear];
target.values[plane.axis_linear] = tool_change_position;
if(probe_toolsetter)
grbl.on_probe_toolsetter(next_tool, &target, false, true);
if(!mc_line(target.values, &plan_data))
else if(settings.tool_change.mode == ToolChange_Manual_G59_3) {
if(!go_toolsetter(&plan_data))
return Status_Reset;
target.values[plane.axis_linear] = tmp_pos;
if(!mc_line(target.values, &plan_data))
return Status_Reset;
if(probe_toolsetter)
grbl.on_probe_toolsetter(next_tool, NULL, true, true);
}
#endif
@@ -483,9 +570,14 @@ void tc_init (void)
hal.tool.change = tool_change;
grbl.on_toolchange_ack = on_toolchange_ack;
if(!on_homing_subscribed) {
on_homing_subscribed = true;
on_homing_completed = grbl.on_homing_completed;
grbl.on_homing_completed = tc_on_homing_complete;
grbl.on_homing_completed = onHomingComplete;
on_wco_saved = grbl.on_wco_saved;
grbl.on_wco_saved = onWcoSaved;
}
if(driver_reset == NULL) {
driver_reset = hal.driver_reset;
@@ -502,6 +594,11 @@ status_code_t tc_probe_workpiece (void)
if(!(settings.tool_change.mode == ToolChange_Manual || settings.tool_change.mode == ToolChange_Manual_G59_3) || enqueue_realtime_command == NULL)
return Status_InvalidStatement;
if(change_at_g30) {
grbl.report.feedback_message(Message_CycleStart2TouchOff);
return Status_OK;
}
// TODO: add check for reference offset set?
bool ok;
@@ -527,23 +624,28 @@ status_code_t tc_probe_workpiece (void)
{
system_convert_array_steps_to_mpos(target.values, sys.probe_position);
// Retract a bit and perform slow probe.
plan_data.feed_rate = settings.tool_change.pulloff_rate;
target.values[plane.axis_linear] += TOOL_CHANGE_PROBE_RETRACT_DISTANCE;
if((ok = mc_line(target.values, &plan_data))) {
plan_data.feed_rate = settings.tool_change.feed_rate;
target.values[plane.axis_linear] -= (TOOL_CHANGE_PROBE_RETRACT_DISTANCE + 2.0f);
if((ok = mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found)) {
// Retract a bit again so that any touch plate can be removed
system_convert_array_steps_to_mpos(target.values, sys.probe_position);
plan_data.feed_rate = settings.tool_change.seek_rate;
target.values[plane.axis_linear] += TOOL_CHANGE_PROBE_RETRACT_DISTANCE * 2.0f;
if(target.values[plane.axis_linear] > tool_change_position)
target.values[plane.axis_linear] = tool_change_position;
ok = mc_line(target.values, &plan_data);
if((flags.probe_is_away = settings.flags.tool_change_fast_pulloff))
plan_data.feed_rate = settings.tool_change.feed_rate; // Retract slowly until contact lost.
else {
// Retract a bit before performing slow probe.
plan_data.feed_rate = settings.tool_change.pulloff_rate;
if((ok = mc_line(target.values, &plan_data))) {
plan_data.feed_rate = settings.tool_change.feed_rate;
target.values[plane.axis_linear] -= (TOOL_CHANGE_PROBE_RETRACT_DISTANCE + 2.0f);
}
}
if((ok = ok && mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found)) {
// Retract a bit again so that any touch plate can be removed
system_convert_array_steps_to_mpos(target.values, sys.probe_position);
plan_data.feed_rate = settings.tool_change.seek_rate;
target.values[plane.axis_linear] += TOOL_CHANGE_PROBE_RETRACT_DISTANCE * 2.0f;
if(target.values[plane.axis_linear] > sys.home_position[plane.axis_linear])
target.values[plane.axis_linear] = sys.home_position[plane.axis_linear];
ok = mc_line(target.values, &plan_data);
}
}
if(ok && protocol_buffer_synchronize()) {