diff --git a/CMakeLists.txt b/CMakeLists.txt
index dc731ae..eb64962 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,6 +33,7 @@ target_sources(grbl INTERFACE
${CMAKE_CURRENT_LIST_DIR}/regex.c
${CMAKE_CURRENT_LIST_DIR}/ioports.c
${CMAKE_CURRENT_LIST_DIR}/vfs.c
+ ${CMAKE_CURRENT_LIST_DIR}/canbus.c
${CMAKE_CURRENT_LIST_DIR}/kinematics/corexy.c
${CMAKE_CURRENT_LIST_DIR}/kinematics/wall_plotter.c
${CMAKE_CURRENT_LIST_DIR}/kinematics/delta.c
diff --git a/README.md b/README.md
index f6b3e72..ec91402 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 20240928, 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/canbus.c b/canbus.c
new file mode 100644
index 0000000..29bf792
--- /dev/null
+++ b/canbus.c
@@ -0,0 +1,207 @@
+/*
+
+ canbus.c -
+
+ Part of grblHAL
+
+ Copyright (c) 2022 Jon Escombe
+ Copyright (c) 2024 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 .
+
+*/
+
+#include "hal.h"
+#include "task.h"
+#include "protocol.h"
+#include "canbus.h"
+
+#ifndef CANBUS_BUFFER_LEN
+#define CANBUS_BUFFER_LEN 8
+#endif
+#ifndef CANBUS_BAUDRATE
+#define CANBUS_BAUDRATE 0 // 125000
+#endif
+
+typedef struct {
+ volatile canbus_message_t message;
+ can_rx_ptr callback;
+} canbus_rx_message;
+
+typedef struct {
+ volatile canbus_message_t message;
+ bool ext_id;
+} canbus_tx_message;
+
+typedef struct {
+ volatile uint8_t head;
+ volatile uint8_t tail;
+ volatile canbus_tx_message tx[CANBUS_BUFFER_LEN];
+} canbus_tx_buffer_t;
+
+typedef struct {
+ volatile uint8_t head;
+ volatile uint8_t tail;
+ volatile canbus_rx_message rx[CANBUS_BUFFER_LEN];
+} canbus_rx_buffer_t;
+
+static bool isEnabled = false;
+static const uint32_t baud[] = { 125000, 250000, 500000, 1000000 };
+static canbus_tx_buffer_t tx_buffer = {0};
+static canbus_rx_buffer_t rx_buffer = {0};
+
+// Weak implementations of low level functions to be provided by the driver
+
+__attribute__((weak)) bool can_start (uint32_t baud, can_rx_enqueue_fn callback)
+{
+ return false;
+}
+
+__attribute__((weak)) bool can_stop (void)
+{
+ return false;
+}
+
+__attribute__((weak)) bool can_set_baud (uint32_t baud)
+{
+ return false;
+}
+
+__attribute__((weak)) bool can_put (canbus_message_t msg, bool ext_id)
+{
+ return false;
+}
+
+__attribute__((weak)) bool can_add_filter (uint32_t id, uint32_t mask, bool ext_id, can_rx_ptr callback)
+{
+ return false;
+}
+
+// ---
+
+ISR_CODE static bool ISR_FUNC(canbus_queue_rx)(canbus_message_t message, can_rx_ptr callback)
+{
+ bool ok;
+ uint8_t next_head = (rx_buffer.head + 1) % CANBUS_BUFFER_LEN;
+
+ if((ok = next_head != rx_buffer.tail)) {
+ rx_buffer.rx[rx_buffer.head].callback = callback;
+ rx_buffer.rx[rx_buffer.head].message = message;
+
+ rx_buffer.head = next_head;
+ }
+
+ return ok;
+}
+
+// called every 1 ms
+static void canbus_poll (void *data)
+{
+ /* if have TX data, sends one message per iteration.. */
+ if(tx_buffer.head != tx_buffer.tail && can_put(tx_buffer.tx[tx_buffer.tail].message, tx_buffer.tx[tx_buffer.tail].ext_id))
+ tx_buffer.tail = (tx_buffer.tail + 1) % CANBUS_BUFFER_LEN;
+
+ /* if have RX data, process one message per iteration.. */
+ if(rx_buffer.head != rx_buffer.tail) {
+ if(rx_buffer.rx[rx_buffer.tail].callback)
+ rx_buffer.rx[rx_buffer.tail].callback(rx_buffer.rx[rx_buffer.tail].message);
+ rx_buffer.tail = (rx_buffer.tail + 1) % CANBUS_BUFFER_LEN;
+ }
+}
+
+static bool canbus_start (uint32_t baud)
+{
+ if((isEnabled = can_start(baud, canbus_queue_rx)))
+ task_add_systick(canbus_poll, NULL);
+
+ return isEnabled;
+}
+
+static status_code_t canbus_set_baud (setting_id_t id, uint_fast16_t value)
+{
+ settings.canbus_baud = value;
+
+ return can_set_baud(baud[settings.canbus_baud]) ? Status_OK : Status_SettingValueOutOfRange;
+}
+
+static uint32_t canbus_get_baud (setting_id_t setting)
+{
+ return settings.canbus_baud < (sizeof(baud) / sizeof(uint32_t)) ? settings.canbus_baud : CANBUS_BAUDRATE;
+}
+
+static const setting_group_detail_t canbus_groups [] = {
+ { Group_Root, Group_CANbus, "CAN bus"}
+};
+
+static const setting_detail_t canbus_setting_detail[] = {
+ { Setting_CANbus_BaudRate, Group_CANbus, "CAN bus baud rate", NULL, Format_RadioButtons, "125000,250000,500000,1000000", NULL, NULL, Setting_NonCoreFn, canbus_set_baud, canbus_get_baud, NULL },
+};
+
+static void canbus_settings_restore (void)
+{
+ settings.canbus_baud = CANBUS_BAUDRATE;
+
+ settings_write_global();
+}
+
+static void canbus_settings_load (void)
+{
+ canbus_start(baud[canbus_get_baud(Setting_CANbus_BaudRate)]);
+}
+
+static setting_details_t setting_details = {
+ .groups = canbus_groups,
+ .n_groups = sizeof(canbus_groups) / sizeof(setting_group_detail_t),
+ .settings = canbus_setting_detail,
+ .n_settings = sizeof(canbus_setting_detail) / sizeof(setting_detail_t),
+ .save = settings_write_global,
+ .load = canbus_settings_load,
+ .restore = canbus_settings_restore
+};
+
+// Public API
+
+bool canbus_enabled (void)
+{
+ return isEnabled;
+}
+
+bool canbus_queue_tx (canbus_message_t message, bool ext_id)
+{
+ bool ok;
+ uint8_t next_head = (tx_buffer.head + 1) % CANBUS_BUFFER_LEN;
+
+ if((ok = next_head != tx_buffer.tail)) {
+ tx_buffer.tx[tx_buffer.head].ext_id = ext_id;
+ tx_buffer.tx[tx_buffer.head].message = message;
+ tx_buffer.head = next_head;
+ }
+
+ return ok;
+}
+
+bool canbus_add_filter (uint32_t id, uint32_t mask, bool ext_id, can_rx_ptr callback)
+{
+ return can_add_filter(id, mask, ext_id, callback);
+}
+
+void canbus_init (void)
+{
+ static bool init_ok = false;
+
+ if(!init_ok) {
+ init_ok = true;
+ settings_register(&setting_details);
+ }
+}
diff --git a/canbus.h b/canbus.h
new file mode 100644
index 0000000..f402f26
--- /dev/null
+++ b/canbus.h
@@ -0,0 +1,48 @@
+/*
+
+ canbus.h -
+
+ Part of grblHAL
+
+ Copyright (c) 2022 Jon Escombe
+ Copyright (c) 2024 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 .
+
+*/
+
+#ifndef _CANBUS_H_
+#define _CANBUS_H_
+
+#include
+#include
+
+typedef struct {
+ uint32_t id;
+ uint8_t len;
+ uint8_t data[8];
+} canbus_message_t;
+
+typedef bool (*can_rx_ptr)(canbus_message_t);
+typedef bool (*can_rx_enqueue_fn)(canbus_message_t msg, can_rx_ptr callback); // used internally by the driver
+
+/*
+ * Function prototypes
+ */
+void canbus_init (void);
+bool canbus_enabled (void);
+bool canbus_queue_tx (canbus_message_t message, bool ext_id);
+bool canbus_add_filter (uint32_t id, uint32_t mask, bool ext_id, can_rx_ptr callback);
+
+#endif /* _CANBUS_H_ */
diff --git a/changelog.md b/changelog.md
index 5b3a16e..1158a89 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,342 @@
## grblHAL changelog
+Build 20240928
+
+* Added `(PRINT, )` support and parameter formatting for `DEBUG` and `PRINT` commands. Available when expression support is enabled.
+
+* Added named parameters for getting absolute \(G53\) position: `_abs_x`, `abs_y`, ... Available when expression support is enabled.
+
+* Changed stepper enable HAL signature to allow current reduction when idle. Requires compatible stepper drivers and low level code support.
+
+* Added reference id to spindle registration in order to allow configuring default spindle, and possibly additional spindles, at compile time.
+
+* Fix for hardfault when some $-settings are changed and there are no auxilary inputs defined in the board map. Ref. [issue #588](https://github.com/grblHAL/core/issues/588).
+
+Drivers:
+
+* All: updated for core changes mentioned above.
+
+* ESP32, STM32F4xx, STM32F7xx: added basic support for the core HAL timer API. Changed step inject code to interrupt driven instead of polled.
+
+Plugins:
+
+* Spindle: updated to support new spindle reference id in the core, simplified code.
+
+* SD Card \(macros\): fixed bug in handling of repeat loops.
+
+---
+
+Build 20240921
+
+Core:
+
+* Added generic HAL timer API and function for getting which `G65` parameter words were supplied.
+
+Plugins:
+
+* Networking: made parsing of HTTP header keywords case insensitive. Ref. [issue #11](https://github.com/grblHAL/Plugin_networking/issues/11).
+
+* SD card \(macros\): added inbuilt `G65` macro `P3` for getting and setting NGC numerical parameters, typical use case will be for indexed access. Ref. [discussion #309 comment](https://github.com/grblHAL/core/discussions/309#discussioncomment-10710468).
+
+---
+
+Build 20240907
+
+Core:
+
+* Added some RGB LED strip properties, improved handling of single meaning G-code words claimed by user M-codes.
+
+Plugins:
+
+* Misc: updated for new RGB LED strip properties.
+
+Drivers:
+
+* iMRX1062, STM32F4xx and STM32F7xx: updated for new RGB LED strip properties.
+
+* RP2040: revised pin mappings for BTT SKR Pico board. Added misc. plugins to compilation.
+
+---
+
+Build 20240903
+
+Core:
+
+* Added some new plugin init calls and setting ids. Added defaults for RGB strip lengths.
+
+Drivers:
+
+* ESP32, RP2040, STM32F4xx and STM32F7xx: updated for core changes related to the RGB HAL.
+
+* RP2040: renamed bluetooth files to avoid conflict with SDK.
+
+* STM32F7xx: moved board maps to separate directory.
+
+Plugins:
+
+* SD card: removed superfluous code. Made _.macro_ file type visible by default. Ref. [ioSender issue #403](https://github.com/terjeio/ioSender/issues/403).
+
+* Misc: initial commit of [new plugins](https://github.com/grblHAL/Plugins_misc), some moved from [Templates](https://github.com/grblHAL/Templates/tree/master/my_plugin).
+
+* WebUI: now delays soft reset commands for ESP32 driver to avoid crash when more than 3 axes are enabled. Ref. [issue #15](https://github.com/grblHAL/Plugin_WebUI/issues/15)
+
+---
+
+Build 20240827
+
+Core:
+
+* Added setting definitions for plugins and some new plugin initialization calls.
+
+Drivers:
+
+* ESP32: changed spindle on signal to GPIO32 when On/Off spindle is configured for MKS DLC32 board. Ref. this [discussion](https://github.com/grblHAL/core/discussions/203#discussioncomment-10454788).
+
+* RP2040: fixed build issues when native Bluetooth is enabled. Ref. [issue #94](https://github.com/grblHAL/RP2040/issues/94).
+
+Plugins:
+
+* Spindle: added "Offset" plugin for spindle \(laser\) movement to be executed when switching between spindles.
+
+* WebUI: workaround for [issue #15](https://github.com/grblHAL/Plugin_WebUI/issues/15), ESP32 crash on soft reset when > 3 axes configured.
+
+* Miscellaneous: added a number of smallish plugins; BLTouch, PWM servo, EventOut, RGB LED strips, RGB LED M150. These are work in progress and requires specific driver configurations.
+
+---
+
+Build 20240817
+
+Core:
+
+* Simplified keypad and MPG symbol handling.
+
+Drivers:
+
+* Most: updated for simplified keypad and MPG symbol handling.
+
+* LPC176x: added support for keypad and MPG plugin. I2C keypad not fully supported and not tested.
+
+Plugins:
+
+* Keypad: updated for simplified keypad and MPG symbol handling.
+
+---
+
+Build 20240812
+
+Core:
+
+* Improved handling of extended M commands \(plugin based\) command words. Fixes issues for programs containing extended M-codes using single meaning words \(which they as a rule should not do\).
+
+* Added core support for spindle encoder binding to spindles.
+
+* Added sorting of spindle report: enabled spindles are sorted first in order of spindle number, disabled by type then spindle id.
+
+* Changed realtime report to report spindle number instead of spindle id on changes in the `|S:` element. Part of fix for ioSender [issue #399](https://github.com/terjeio/ioSender/issues/399).
+
+Drivers:
+
+* imXRT1061, MSP432, STM32F4xx, STM32F7xx: updated to take advantage of new spindle encoder binding functionality.
+
+Plugins:
+
+* Spindle: updated relevant drivers to use new spindle encoder binding functionality, simplified code. Fixes [issue #30](https://github.com/grblHAL/Plugins_spindle/issues/30).
+
+---
+
+Build 20240805
+
+Core:
+
+* Added function for getting speed \(RPM\) of stepper controlled by secondary stepper motor driver.
+
+Plugins:
+
+* Spindle: stepper spindle code now uses new function for getting speed \(RPM\) of motor.
+HAL functions for getting spindle data \(actual RPM, angular position etc.\) directed to stepper spindle code,
+
+---
+
+Build 20240801
+
+Core:
+
+* Added option bit for enabling realtime reporting while homing to `$10`, _Status report options_. Ref. [issue #551](https://github.com/grblHAL/core/issues/551).
+__NOTE:__ Enabling this may affect some senders.
+
+Drivers:
+
+* iMXRT1062, LPC176x, SAM3X8E and STM32F1xx: moved board maps/board specific code to new _boards_ directory.
+
+* STM32F4xx: fixed regression in SD card code affecting boards using SDIO interface.
+
+---
+
+Build 20240719
+
+Core:
+
+* Limited tool change probe moves to be within machine limits. Ref. [issue #542](https://github.com/grblHAL/core/issues/542).
+
+* Added setting `$538` to enable experimental functionality for fast rotary 'rewind' to stored G28 position. Return move should complete in half a rotation or less.
+To use program:
+```
+G91G280
+G90
+```
+where \ is the axisletter of the rotary axis to move.
+
+* For developers: added option to redirect debug via driver provided function when `DEBUGOUT` is set to `-1` in _config.h_. Can be used to send debug output to debugger interfaces.
+
+Plugins:
+
+* WebUI: fix for crash when settings page is opened with macros plugin enabled. Ref. [issue #14](https://github.com/grblHAL/Plugin_WebUI/issues/14).
+
+---
+
+Build 20240709
+
+Core:
+
+* For developers: added printf style debug output function and corresponding macro. See _grbl/stream.h_ for details. Added `grbl.on_report_ngc_parameters` event.
+
+* Fixed silly mistakes in CAN code. Ref. [issue #179](https://github.com/grblHAL/STM32F4xx/issues/179#issuecomment-2217912406).
+
+Drivers:
+
+* SAM3X8E: [PR #25](https://github.com/grblHAL/SAM3X8E/pull/25), adds missing guards around references.
+
+* STM32F1xx: added tentative board map for Creality v4.4.2 and v4.4.7. Ref. [issue #33](https://github.com/grblHAL/STM32F1xx/issues/33). Not tested!
+
+---
+
+Build 20240704
+
+Core:
+
+* Added high level CANbus API for plugin use. If driver/board combo provides the required lowlevel HAL API the `NEWOPT` string in the `$I` output will contain the `CAN` element when CAN is enabled.
+Ref. [issue #179](https://github.com/grblHAL/STM32F4xx/issues/179).
+
+* Added soft limits check for corexy kinematics, ref. [discussion #536](https://github.com/grblHAL/core/discussions/536).
+
+Drivers:
+
+* ESP32: fixed WebUI regression. Ref. [issue #116](https://github.com/grblHAL/ESP32/issues/116).
+
+* STM32F7xx, STM32F4xx: added lowlevel CANbus API and enabled it for some boards.
+
+---
+
+
+Build 20240624
+
+Core:
+
+* Some minor changes to better support keypad macro and networking plugins.
+
+Drivers:
+
+* ESP32: disabled cycle start input for MKS DLC32 board due to incompatible use. Ref. [issue #111](https://github.com/grblHAL/ESP32/issues/111).
+
+* RP2040: fixed incorrect handling of WiFi BSSID.
+
+* STM32F7xx: completed support for WizNet ethernet modules \(W5100S & W5500\). Added code by @dresco for getting unique MAC address, Ref. [discussion #17](https://github.com/grblHAL/STM32F7xx/discussions/17).
+
+Plugins:
+
+* Networking \(WizNet\): fixed incorrect MAC address handling, added weak functions for getting default MAC addresses ++. Ref. [discussion #17](https://github.com/grblHAL/STM32F7xx/discussions/17).
+
+* Keypad macros: added settings for binding single character realtime commands to macro pin event.
+__NOTE:__ this change will reset _all_ plugin settings to default, backup/restore if this plugin is in use.
+
+---
+
+Build 20240619
+
+Core:
+
+* Renamed `grbl.on_probe_fixture` event to `grbl.on_probe_toolsetter` and added pointer to toolsetter coordinates as an additional parameter.
+This will allow plugin code to modify the coordinates before moving to the toolsetter so that tools with off center cutting surfaces can be properly measured.
+Ref. [ioSender issue #386](https://github.com/terjeio/ioSender/issues/386).
+
+* Increased backlash parameters precision to 5 decimals. Ref. [issue #452](https://github.com/grblHAL/core/issues/452#issuecomment-2159050856).
+
+* Some bug fixes in NGC parameters and flow control handling.
+
+Drivers:
+
+* ESP32: improved SPI code.
+
+* SAM3X8E: added support for native and MCP3221 I2C ADCs. Ref. [issue #24](https://github.com/grblHAL/SAM3X8E/issues/24).
+
+Plugins:
+
+* Templates: Updated some for core event rename and signature change.
+
+---
+
+Build 20240604
+
+Core:
+
+* Fixed incorrect implementation of `EXISTS` function. Ref. [issue #527](https://github.com/grblHAL/core/issues/527).
+
+* Added missing clear of parser tool change state when cycle start signal is asserted. Affects tool change mode 'Normal' ($341=0).
+
+---
+
+Build 20240602
+
+Core:
+
+* Fixed typo/bug in expression precedence handling. Ref. [issue #527](https://github.com/grblHAL/core/issues/527).
+
+Drivers:
+
+* STM32F4xx, STM32F7xx: added cast to suppress compiler warning.
+
+* STM32F7xx: added DMA support for SPI4, fix for SPI SCK pin reporting. WizNet code still not working.
+
+---
+
+Build 20240527
+
+Core:
+
+* "Hardened" NGC parameter name case swapping, changed to use float precision according to metric/inches setting for parameter reporting.
+
+---
+
+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. [discussion #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/config.h b/config.h
index 1736f2b..8c372fc 100644
--- a/config.h
+++ b/config.h
@@ -489,11 +489,11 @@ non-volatile storage until the controller is in IDLE state.
/*! \def TOOLSETTER_RADIUS
\brief
-The grbl.on_probe_fixture event handler is called by the default tool change algorithm when probing at G59.3.
+The grbl.on_probe_toolsetter event handler is called by the default tool change algorithm when probing at G59.3.
In addition it will be called on a "normal" probe sequence if the XY position is
within the radius of the G59.3 position defined below.
Change if the default value of 5mm is not suitable or set it to 0.0f to disable.
-
__NOTE:__ A grbl.on_probe_fixture event handler is not installed by the core, it has to be provided
+
__NOTE:__ A grbl.on_probe_toolsetter event handler is not installed by the core, it has to be provided
by a driver or a plugin.
*/
#if !defined TOOLSETTER_RADIUS || defined __DOXYGEN__
@@ -699,6 +699,16 @@ The following codes are defined:
#define DEFAULT_REPORT_RUN_SUBSTATE Off // Default off. Set to \ref On or 1 to enable.
#endif
+/*! \def DEFAULT_REPORT_WHEN_HOMING
+\brief
+Enabling this setting enables status reporting while homing.
+
__NOTE:__ Enabling this option may break senders.
+\internal Bit 12 in settings.status_report.
+*/
+#if !defined DEFAULT_REPORT_WHEN_HOMING || defined __DOXYGEN__
+#define DEFAULT_REPORT_WHEN_HOMING Off // Default off. Set to \ref On or 1 to enable.
+#endif
+
///@}
/*! @name $11 - Setting_JunctionDeviation
@@ -1108,6 +1118,14 @@ Default value is 0, meaning spindle sync is disabled
#endif
///@}
+/*! @name $395 - Setting_SpindleType
+*/
+///@{
+#if !defined DEFAULT_SPINDLE || defined __DOXYGEN__
+#define DEFAULT_SPINDLE SPINDLE_PWM0 // Spindle number from spindle_control.h
+#endif
+///@}
+
// Closed loop spindle settings (Group_Spindle_ClosedLoop)
// $9 - Setting_SpindlePWMOptions
@@ -1786,6 +1804,36 @@ Timezone offset from UTC in hours, allowed range is -12.0 - 12.0.
#endif
///@}
+/*! @name $536 - Setting_RGB_StripLengt0
+Number of LEDs in NeoPixel/WS2812 strip 1.
+*/
+///@{
+#if !defined DEFAULT_RGB_STRIP0_LENGTH || defined __DOXYGEN__
+#define DEFAULT_RGB_STRIP0_LENGTH 0
+#endif
+///@}
+
+/*! @name $537 - Setting_RGB_StripLengt1
+Number of LEDs in NeoPixel/WS2812 strip 2.
+*/
+///@{
+#if !defined DEFAULT_RGB_STRIP1_LENGTH || defined __DOXYGEN__
+#define DEFAULT_RGB_STRIP1_LENGTH 0
+#endif
+///@}
+
+/*! @name $538 - Setting_RotaryWrap
+Enable fast return to G28 position for rotary axes by \ref axismask.
+Use:
+G91G280
+G90
+*/
+///@{
+#if !defined DEFAULT_AXIS_ROTARY_WRAP_MASK || defined __DOXYGEN__
+#define DEFAULT_AXIS_ROTARY_WRAP_MASK 0
+#endif
+///@}
+
// Axis settings (Group_XAxis - Group_VAxis)
/*! @name $10x - Setting_AxisStepsPerMM
diff --git a/core_handlers.h b/core_handlers.h
index 2a9e956..405cfec 100644
--- a/core_handlers.h
+++ b/core_handlers.h
@@ -95,6 +95,7 @@ typedef void (*on_unknown_accessory_override_ptr)(uint8_t cmd);
typedef bool (*on_unknown_realtime_cmd_ptr)(char c);
typedef void (*on_report_handlers_init_ptr)(void);
typedef void (*on_report_options_ptr)(bool newopt);
+typedef void (*on_report_ngc_parameters_ptr)(void);
typedef void (*on_report_command_help_ptr)(void);
typedef const char *(*on_setting_get_description_ptr)(setting_id_t id);
typedef void (*on_global_settings_restore_ptr)(void);
@@ -107,7 +108,7 @@ typedef void (*on_homing_rate_set_ptr)(axes_signals_t axes, float rate, homing_m
// NOTE: cycle contains the axis flags of the executed homing cycle, success will be true when all the configured cycles are completed.
typedef void (*on_homing_completed_ptr)(axes_signals_t cycle, bool success);
-typedef bool (*on_probe_fixture_ptr)(tool_data_t *tool, bool at_g59_3, bool on);
+typedef bool (*on_probe_toolsetter_ptr)(tool_data_t *tool, coord_data_t *position, bool at_g59_3, bool on);
typedef bool (*on_probe_start_ptr)(axes_signals_t axes, float *target, plan_line_data_t *pl_data);
typedef void (*on_probe_completed_ptr)(void);
typedef void (*on_tool_selected_ptr)(tool_data_t *tool);
@@ -156,6 +157,7 @@ typedef struct {
on_execute_realtime_ptr on_execute_delay;
on_unknown_accessory_override_ptr on_unknown_accessory_override;
on_report_options_ptr on_report_options;
+ on_report_ngc_parameters_ptr on_report_ngc_parameters;
on_report_command_help_ptr on_report_command_help; //!< Deprecated, use system_register_commands() to register new commands.
on_rt_reports_added_ptr on_rt_reports_added;
on_global_settings_restore_ptr on_global_settings_restore;
@@ -172,7 +174,7 @@ typedef struct {
on_stream_changed_ptr on_stream_changed;
on_homing_rate_set_ptr on_homing_rate_set;
on_homing_completed_ptr on_homing_completed;
- on_probe_fixture_ptr on_probe_fixture;
+ on_probe_toolsetter_ptr on_probe_toolsetter;
on_probe_start_ptr on_probe_start;
on_probe_completed_ptr on_probe_completed;
on_set_axis_setting_unit_ptr on_set_axis_setting_unit;
diff --git a/crossbar.h b/crossbar.h
index e34954f..d210860 100644
--- a/crossbar.h
+++ b/crossbar.h
@@ -187,6 +187,7 @@ typedef enum {
Output_MOSI,
Output_SPICLK,
Output_SPICS,
+ Output_FlashCS,
Output_SdCardCS,
Input_SdCardDetect,
Output_SPIRST,
@@ -385,6 +386,7 @@ PROGMEM static const pin_name_t pin_names[] = {
{ .function = Output_MOSI, .name = "MOSI" },
{ .function = Output_SPICLK, .name = "SPI CLK" },
{ .function = Output_SPICS, .name = "SPI CS" },
+ { .function = Output_FlashCS, .name = "Flash CS" },
{ .function = Output_SdCardCS, .name = "SD card CS" },
{ .function = Input_SdCardDetect, .name = "SD card detect" },
{ .function = Output_SPIRST, .name = "SPI reset" },
diff --git a/driver_opts.h b/driver_opts.h
index 289526d..f83ff3f 100644
--- a/driver_opts.h
+++ b/driver_opts.h
@@ -94,26 +94,33 @@
#define SERIAL_STREAM 0
#endif
-#ifndef KEYPAD_ENABLE
-#define KEYPAD_ENABLE 0
-#endif
-
#ifndef MACROS_ENABLE
#define MACROS_ENABLE 0
#endif
-#ifndef MPG_ENABLE
-#define MPG_ENABLE 0
+#ifndef KEYPAD_ENABLE
+#define KEYPAD_ENABLE 0
#endif
-#if MPG_ENABLE == 1 && KEYPAD_ENABLE == 2
-#define MPG_MODE 2
-#elif MPG_ENABLE == 2
-#define MPG_MODE 3
-#elif MPG_ENABLE
-#define MPG_MODE 1
+#if KEYPAD_ENABLE == 1
+#ifdef I2C_STROBE_ENABLE
+#undef I2C_STROBE_ENABLE
+#endif
+#define I2C_STROBE_ENABLE 1
+#elif KEYPAD_ENABLE == 2 && !defined(KEYPAD_STREAM)
+#if USB_SERIAL_CDC
+#define KEYPAD_STREAM 0
#else
-#define MPG_MODE 0
+#define KEYPAD_STREAM 1
+#endif
+#endif
+
+#ifndef I2C_STROBE_ENABLE
+#define I2C_STROBE_ENABLE 0
+#endif
+
+#ifndef MPG_ENABLE
+#define MPG_ENABLE 0
#endif
#if MPG_ENABLE && !defined(MPG_STREAM)
@@ -124,24 +131,6 @@
#endif
#endif
-#if KEYPAD_ENABLE == 1
-#ifdef I2C_STROBE_ENABLE
-#undef I2C_STROBE_ENABLE
-#endif
-#define I2C_STROBE_ENABLE 1
-#elif KEYPAD_ENABLE == 2 && !defined(MPG_STREAM)
-#ifndef KEYPAD_STREAM
-#if USB_SERIAL_CDC
-#define KEYPAD_STREAM 0
-#else
-#define KEYPAD_STREAM 1
-#endif
-#endif
-#endif
-#ifndef I2C_STROBE_ENABLE
-#define I2C_STROBE_ENABLE 0
-#endif
-
#if DISPLAY_ENABLE == 2
#ifdef I2C_ENABLE
#undef I2C_ENABLE
@@ -236,40 +225,38 @@
#endif
#endif
-// TODO: remove?
-#ifndef VFD_SPINDLE
-#if VFD_ENABLE
-#define VFD_SPINDLE 1
-#else
-#define VFD_SPINDLE 0
-#endif
-#endif
-
#ifndef SPINDLE0_ENABLE
- #if VFD_ENABLE
- #define SPINDLE0_ENABLE VFD_ENABLE
- #if N_SPINDLE > 1 && !defined(SPINDLE1_ENABLE)
- #define SPINDLE1_ENABLE SPINDLE_PWM0
- #endif
- #else
- #define SPINDLE0_ENABLE SPINDLE_PWM0
- #endif
+#define SPINDLE0_ENABLE SPINDLE_PWM0
#endif
#ifndef SPINDLE1_ENABLE
#define SPINDLE1_ENABLE 0
+#elif SPINDLE1_ENABLE == -1 || SPINDLE1_ENABLE == SPINDLE_ALL || SPINDLE1_ENABLE == SPINDLE_ALL_VFD
+#warning "SPINDLE1_ENABLE cannot be set to -1, SPINDLE_ALL or SPINDLE_ALL_VFD"
+#undef SPINDLE1_ENABLE
+#define SPINDLE1_ENABLE 0
#endif
#ifndef SPINDLE2_ENABLE
#define SPINDLE2_ENABLE 0
+#elif SPINDLE2_ENABLE == -1 || SPINDLE2_ENABLE == SPINDLE_ALL || SPINDLE2_ENABLE == SPINDLE_ALL_VFD
+#warning "SPINDLE2_ENABLE cannot be set to -1, SPINDLE_ALL or SPINDLE_ALL_VFD"
+#undef SPINDLE2_ENABLE
+#define SPINDLE2_ENABLE 0
#endif
#ifndef SPINDLE3_ENABLE
#define SPINDLE3_ENABLE 0
+#elif SPINDLE3_ENABLE == -1 || SPINDLE3_ENABLE == SPINDLE_ALL || SPINDLE3_ENABLE == SPINDLE_ALL_VFD
+#warning "SPINDLE3_ENABLE cannot be set to -1, SPINDLE_ALL or SPINDLE_ALL_VFD"
+#undef SPINDLE1_ENABLE
+#define SPINDLE1_ENABLE 0
#endif
-#if SPINDLE0_ENABLE == SPINDLE_ALL
-#define SPINDLE_ENABLE SPINDLE_ALL
+#if SPINDLE0_ENABLE == -1 || SPINDLE0_ENABLE == SPINDLE_ALL
+#define SPINDLE_ENABLE (SPINDLE_ALL|(1<= 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 +307,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 +322,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 +364,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 +476,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 +496,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 +510,165 @@ static status_code_t read_parameter (char *line, uint_fast8_t *char_counter, flo
return status;
}
+static int8_t get_format (char c, int8_t pos, uint8_t *decimals)
+{
+ static uint8_t d;
+
+ // lcaps c?
+
+ switch(pos) {
+
+ case 1:
+
+ switch(c) {
+
+ case 'd':
+ *decimals = 0;
+ pos = -2;
+ break;
+
+ case 'f':
+ *decimals = ngc_float_decimals();
+ pos = -2;
+ break;
+
+ case '.':
+ pos = 2;
+ break;
+
+ default:
+ pos = 0;
+ break;
+ }
+ break;
+
+ case 2:
+ if(c >= '0' && c <= '9') {
+ d = c - '0';
+ pos = 3;
+ } else
+ pos = 0;
+ break;
+
+ default:
+ if(c == 'f') {
+ *decimals = d;
+ pos = -4;
+ } else
+ pos = 0;
+ break;
+ }
+
+ return pos;
+}
+
+static void substitute_parameters (char *comment, char **message)
+{
+ size_t len = 0;
+ float value;
+ char *s, c;
+ uint_fast8_t char_counter = 0;
+ int8_t parse_format = 0;
+ uint8_t decimals = ngc_float_decimals(); // LinuxCNC is 1 (or l?)
+
+ // Trim leading spaces
+ while(*comment == ' ')
+ comment++;
+
+ // Calculate length of substituted string
+ while((c = comment[char_counter++])) {
+ if(parse_format) {
+ if((parse_format = get_format(c, parse_format, &decimals)) < 0) {
+ len -= parse_format;
+ parse_format = 0;
+ }
+ } else if(c == '%')
+ parse_format = 1;
+ else if(c == '#') {
+ char_counter--;
+ if(read_parameter(comment, &char_counter, &value) == Status_OK)
+ len += strlen(decimals ? ftoa(value, decimals) : trim_float(ftoa(value, decimals)));
+ else
+ len += 3; // "N/A"
+ } else
+ len++;
+ }
+
+ // Perform substitution
+ if((s = *message = malloc(len + 1))) {
+
+ char fmt[5] = {0};
+
+ *s = '\0';
+ char_counter = 0;
+
+ while((c = comment[char_counter++])) {
+ if(parse_format) {
+ fmt[parse_format] = c;
+ if((parse_format = get_format(c, parse_format, &decimals)) < 0)
+ parse_format = 0;
+ else if(parse_format == 0) {
+ strcat(s, fmt);
+ s = strchr(s, '\0');
+ continue;
+ }
+ } else if(c == '%') {
+ parse_format = 1;
+ fmt[0] = c;
+ } else if(c == '#') {
+ char_counter--;
+ if(read_parameter(comment, &char_counter, &value) == Status_OK)
+ strcat(s, decimals ? ftoa(value, decimals) : trim_float(ftoa(value, decimals)));
+ else
+ strcat(s, "N/A");
+ s = strchr(s, '\0');
+ } else {
+ *s++ = c;
+ *s = '\0';
+ }
+ }
+ }
+}
+
#endif // NGC_EXPRESSIONS_ENABLE
+#if NGC_PARAMETERS_ENABLE
+
+static parameter_words_t g65_words = {0};
+
+parameter_words_t gc_get_g65_arguments (void)
+{
+ return g65_words;
+}
+
+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
@@ -529,70 +715,35 @@ char *gc_normalize_block (char *block, char **message)
size_t len = s1 - comment - 4;
- if(message && *message == NULL && !strncmp(comment, "(MSG,", 5) && (*message = malloc(len))) {
- comment += 5;
- // Trim leading spaces
- while(*comment == ' ') {
- comment++;
- len--;
- }
- memcpy(*message, comment, len);
- }
-
+ if(message && *message == NULL) {
#if NGC_EXPRESSIONS_ENABLE
- // Debug message string substitution
- if(message && *message == NULL && !strncmp(comment, "(DEBUG,", 7)) {
-
- if(settings.flags.ngc_debug_out) {
-
- float value;
- char *s3;
- uint_fast8_t char_counter = 0;
-
- len = 0;
- comment += 7;
-
- // Trim leading spaces
- while(*comment == ' ')
- comment++;
-
- // Calculate length of substituted string
- while((c = comment[char_counter++])) {
- if(c == '#') {
- char_counter--;
- if(read_parameter(comment, &char_counter, &value) == Status_OK)
- len += strlen(ftoa(value, 6));
- else
- len += 3; // "N/A"
- } else
- len++;
+ if(!strncmp(comment, "(DEBUG,", 7)) { // Debug message string substitution
+ if(settings.flags.ngc_debug_out) {
+ comment += 7;
+ substitute_parameters(comment, message);
}
-
- // Perform substitution
- if((s3 = *message = malloc(len + 1))) {
-
- *s3 = '\0';
- char_counter = 0;
-
- while((c = comment[char_counter++])) {
- if(c == '#') {
- char_counter--;
- if(read_parameter(comment, &char_counter, &value) == Status_OK)
- strcat(s3, ftoa(value, 6));
- else
- strcat(s3, "N/A");
- s3 = strchr(s3, '\0');
- } else {
- *s3++ = c;
- *s3 = '\0';
- }
- }
+ *comment = '\0'; // Do not generate grbl.on_gcode_comment event!
+ } else if(!strncmp(comment, "(PRINT,", 7)) { // Print message string substitution
+ comment += 7;
+ substitute_parameters(comment, message);
+ *comment = '\0'; // Do not generate grbl.on_gcode_comment event!
+ } else if(!strncmp(comment, "(MSG,", 5)) {
+ comment += 5;
+ substitute_parameters(comment, message);
}
}
+#else
+ if(!strncmp(comment, "(MSG,", 5) && (*message = malloc(len))) {
- *comment = '\0'; // Do not generate grbl.on_gcode_comment event!
+ comment += 5;
+ while(*comment == ' ') {
+ comment++;
+ len--;
+ }
+ memcpy(*message, comment, len);
+ }
}
-#endif // NGC_EXPRESSIONS_ENABLE
+#endif
}
if(*comment && *message == NULL && grbl.on_gcode_comment)
@@ -608,7 +759,7 @@ char *gc_normalize_block (char *block, char **message)
}
#if NGC_EXPRESSIONS_ENABLE
- if(comment && s1 - comment < (strncmp(comment, "(DEBU,", 5) ? 5 : 7))
+ if(comment && s1 - comment < (strncmp(comment, "(DEBU", 5) && strncmp(comment, "(PRIN", 5) ? 5 : 7))
*s1 = CAPS(c);
#else
if(comment && s1 - comment < 5)
@@ -745,6 +896,12 @@ status_code_t gc_execute_block (char *block)
#endif
char *message = NULL;
+ struct {
+ float f;
+ uint32_t o;
+ float s;
+ tool_id_t t;
+ } single_meaning_value = {0};
block = gc_normalize_block(block, &message);
@@ -789,7 +946,7 @@ status_code_t gc_execute_block (char *block)
// Initialize command and value words and parser flags variables.
modal_groups_t command_words = {0}; // Bitfield for tracking G and M command words. Also used for modal group violations.
gc_parser_flags_t gc_parser_flags = {0}; // Parser flags for handling special cases.
- static parameter_words_t user_words = {0}; // User M-code words "taken"
+ parameter_words_t user_words = {0}; // User M-code words "taken"
// Determine if the line is a jogging motion or a normal g-code block.
if (block[0] == '$') { // NOTE: `$J=` already parsed when passed to this function.
@@ -885,6 +1042,7 @@ status_code_t gc_execute_block (char *block)
if(!is_user_mcode && isnanf(value))
FAIL(Status_BadNumberFormat); // [Expected word value]
+ g65_words.value = 0;
#else
if((letter < 'A' && letter != '$') || letter > 'Z')
@@ -1237,7 +1395,7 @@ status_code_t gc_execute_block (char *block)
case 56:
if(!settings.parking.flags.enable_override_control) // TODO: check if enabled?
FAIL(Status_GcodeUnsupportedCommand); // [Unsupported M command]
- // no break;
+ // no break
case 48: case 49: case 50: case 51: case 53:
word_bit.modal_group.M9 = On;
gc_block.override_command = (override_mode_t)int_value;
@@ -1254,14 +1412,14 @@ status_code_t gc_execute_block (char *block)
case 65:
if(hal.port.digital_out == NULL || hal.port.num_digital_out == 0)
FAIL(Status_GcodeUnsupportedCommand); // [Unsupported M command]
- word_bit.modal_group.M10 = On;
+ word_bit.modal_group.M5 = On;
port_command = (io_mcode_t)int_value;
break;
case 66:
if(hal.port.wait_on_input == NULL || (hal.port.num_digital_in == 0 && hal.port.num_analog_in == 0))
FAIL(Status_GcodeUnsupportedCommand); // [Unsupported M command]
- word_bit.modal_group.M10 = On;
+ word_bit.modal_group.M5 = On;
port_command = (io_mcode_t)int_value;
break;
@@ -1269,33 +1427,16 @@ status_code_t gc_execute_block (char *block)
case 68:
if(hal.port.analog_out == NULL || hal.port.num_analog_out == 0)
FAIL(Status_GcodeUnsupportedCommand); // [Unsupported M command]
- word_bit.modal_group.M10 = On;
+ word_bit.modal_group.M5 = 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;
@@ -1647,6 +1788,39 @@ status_code_t gc_execute_block (char *block)
// same way. If there is an explicit/implicit axis command, XYZ words are always used and are
// are removed at the end of error-checking.
+ // [0. User defined M commands ]:
+ if(command_words.M10 && gc_block.user_mcode) {
+
+ user_words.mask = gc_block.words.mask;
+ if((int_value = (uint_fast16_t)hal.user_mcode.validate(&gc_block, &gc_block.words)))
+ FAIL((status_code_t)int_value);
+ user_words.mask ^= gc_block.words.mask; // Flag "taken" words for execution
+
+ if(user_words.i)
+ ijk_words.i = Off;
+ if(user_words.j)
+ ijk_words.j = Off;
+ if(user_words.k)
+ ijk_words.k = Off;
+ if(user_words.f) {
+ single_meaning_value.f = gc_block.values.f;
+ gc_block.values.f = 0.0f;
+ }
+ if(user_words.o) {
+ single_meaning_value.o = gc_block.values.o;
+ gc_block.values.o = 0;
+ }
+ if(user_words.s) {
+ single_meaning_value.s = gc_block.values.s;
+ gc_block.values.s = 0.0f;
+ }
+ if(user_words.t) {
+ single_meaning_value.t = gc_block.values.t;
+ gc_block.values.t = (tool_id_t)0;
+ }
+ axis_words.mask = 0;
+ }
+
// [1. Comments ]: MSG's may be supported by driver layer. Comment handling performed by protocol.
// [2. Set feed rate mode ]: G93 F word missing with G1,G2/3 active, implicitly or explicitly. Feed rate
@@ -1703,7 +1877,7 @@ status_code_t gc_execute_block (char *block)
// [4. Set spindle speed and address spindle ]: S or D is negative (done.)
if(gc_block.words.$) {
- bool single_spindle_only = (gc_block.words.s && !user_words.s) ||
+ bool single_spindle_only = gc_block.words.s ||
(command_words.G0 && (gc_block.modal.motion == MotionMode_SpindleSynchronized ||
gc_block.modal.motion == MotionMode_RigidTapping ||
gc_block.modal.motion == MotionMode_Threading)) ||
@@ -1746,11 +1920,11 @@ status_code_t gc_execute_block (char *block)
gc_state.modal.spindle.rpm_mode = gc_block.modal.spindle.rpm_mode;
}
- spindle_event = gc_block.words.s && !user_words.s;
+ spindle_event = gc_block.words.s;
- if (!gc_block.words.s)
+ if(!gc_block.words.s)
gc_block.values.s = gc_state.modal.spindle.rpm_mode == SpindleSpeedMode_RPM ? gc_state.spindle.rpm : gc_state.spindle.hal->param->css.max_rpm;
- else if(!user_words.s && gc_state.modal.spindle.rpm_mode == SpindleSpeedMode_CSS) {
+ else if(gc_state.modal.spindle.rpm_mode == SpindleSpeedMode_CSS) {
// Unsure what to do about S values when in SpindleSpeedMode_CSS - ignore? For now use it to (re)calculate surface speed.
// Reinsert commented out code above if this is removed!!
gc_block.values.s *= (gc_block.modal.units_imperial ? MM_PER_INCH * 12.0f : 1000.0f); // convert surface speed to mm/min
@@ -1763,9 +1937,9 @@ status_code_t gc_execute_block (char *block)
if(set_tool) { // M61
if(!gc_block.words.q)
FAIL(Status_GcodeValueWordMissing);
- if (floorf(gc_block.values.q) - gc_block.values.q != 0.0f)
+ if(!isintf(gc_block.values.q))
FAIL(Status_GcodeCommandValueNotInteger);
- if ((uint32_t)gc_block.values.q > (grbl.tool_table.n_tools ? grbl.tool_table.n_tools : MAX_TOOL_NUMBER))
+ if((uint32_t)gc_block.values.q > (grbl.tool_table.n_tools ? grbl.tool_table.n_tools : MAX_TOOL_NUMBER))
FAIL(Status_GcodeIllegalToolTableEntry);
gc_block.values.t = (uint32_t)gc_block.values.q;
@@ -1782,7 +1956,7 @@ status_code_t gc_execute_block (char *block)
}
}
#endif
- } else if (!gc_block.words.t)
+ } else if(!gc_block.words.t)
gc_block.values.t = gc_state.tool_pending;
if(command_words.M10 && port_command) {
@@ -1919,15 +2093,6 @@ status_code_t gc_execute_block (char *block)
}
}
- // [9a. User defined M commands ]:
- if (command_words.M10 && gc_block.user_mcode) {
- user_words.mask = gc_block.words.mask;
- if((int_value = (uint_fast16_t)hal.user_mcode.validate(&gc_block, &gc_block.words)))
- FAIL((status_code_t)int_value);
- user_words.mask ^= gc_block.words.mask; // Flag "taken" words for execution
- axis_words.mask = ijk_words.mask = 0;
- }
-
// [10. Dwell ]: P value missing. NOTE: See below.
if (gc_block.non_modal_command == NonModal_Dwell) {
if (!gc_block.words.p)
@@ -1946,7 +2111,7 @@ status_code_t gc_execute_block (char *block)
if (gc_block.modal.units_imperial) do { // Axes indices are consistent, so loop may be used.
idx--;
#if N_AXIS > 3
- if (bit_istrue(axis_words.mask, bit(idx)) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) {
+ if (bit_istrue(axis_words.mask, bit(idx)) && bit_isfalse(settings.steppers.is_rotary.mask, bit(idx))) {
#else
if (bit_istrue(axis_words.mask, bit(idx))) {
#endif
@@ -2298,7 +2463,7 @@ status_code_t gc_execute_block (char *block)
// target position with the coordinate system offsets, G92 offsets, absolute override, and distance
// modes applied. This includes the motion mode commands. We can now pre-compute the target position.
// NOTE: Tool offsets may be appended to these conversions when/if this feature is added.
- if (axis_words.mask && axis_command != AxisCommand_ToolLengthOffset) { // TLO block any axis command.
+ if((axis_words.mask || gc_block.modal.motion == MotionMode_CwArc || gc_block.modal.motion == MotionMode_CcwArc) && axis_command != AxisCommand_ToolLengthOffset) { // TLO block any axis command.
idx = N_AXIS;
do { // Axes indices are consistent, so loop may be used to save flash space.
if(bit_isfalse(axis_words.mask, bit(--idx)))
@@ -2368,6 +2533,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?
{
@@ -2379,11 +2548,14 @@ status_code_t gc_execute_block (char *block)
while(gc_block.words.value) {
if(gc_block.words.value & 0x1 && gc_value_ptr[idx].value) switch(gc_value_ptr[idx].type) {
+
case ValueType_Float:
+ g65_words.value |= (1 << idx);
ngc_param_set((ngc_param_id_t)idx, *(float *)gc_value_ptr[idx].value);
break;
case ValueType_UInt32:
+ g65_words.value |= (1 << idx);
ngc_param_set((ngc_param_id_t)idx, (float)*(uint32_t *)gc_value_ptr[idx].value);
break;
@@ -2652,13 +2824,13 @@ status_code_t gc_execute_block (char *block)
// point and the radius to the target point differs more than 0.002mm (EMC def. 0.5mm OR 0.005mm and 0.1% radius).
// [G2/3 Full-Circle-Mode Errors]: Axis words exist. No offsets programmed. P must be an integer.
// NOTE: Both radius and offsets are required for arc tracing and are pre-computed with the error-checking.
-
+ if (gc_block.words.r) { // Arc Radius Mode
if (!axis_words.mask)
FAIL(Status_GcodeNoAxisWords); // [No axis words]
if (!(axis_words.mask & (bit(plane.axis_0)|bit(plane.axis_1))))
FAIL(Status_GcodeNoAxisWordsInPlane); // [No axis words in plane]
-
+ }
if (gc_block.words.p) { // Number of turns
if(!isintf(gc_block.values.p))
FAIL(Status_GcodeCommandValueNotInteger); // [P word is not an integer]
@@ -2954,6 +3126,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.
@@ -3031,7 +3204,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;
@@ -3044,7 +3217,7 @@ status_code_t gc_execute_block (char *block)
}
}
- if(!user_words.s && ((gc_state.spindle.rpm != gc_block.values.s) || gc_parser_flags.spindle_force_sync)) {
+ if(gc_state.spindle.rpm != gc_block.values.s || gc_parser_flags.spindle_force_sync) {
if(gc_state.modal.spindle.state.on && !gc_parser_flags.laser_is_motion) {
if(gc_block.spindle) {
gc_block.spindle->param->rpm = gc_block.values.s;
@@ -3188,7 +3361,7 @@ status_code_t gc_execute_block (char *block)
bool spindle_ok = false;
if(gc_block.spindle) {
if(grbl.on_spindle_programmed)
- grbl.on_spindle_programmed(gc_block.spindle, gc_block.modal.spindle.state, plan_data.spindle.rpm, gc_block.modal.spindle.rpm_mode);
+ grbl.on_spindle_programmed(gc_block.spindle, gc_block.modal.spindle.state, plan_data.spindle.rpm, gc_block.modal.spindle.rpm_mode);
if((spindle_ok = spindle_sync(gc_block.spindle, gc_block.modal.spindle.state, plan_data.spindle.rpm)))
gc_block.spindle->param->state = gc_block.modal.spindle.state;
} else {
@@ -3197,7 +3370,7 @@ status_code_t gc_execute_block (char *block)
if(spindle_is_enabled(--idx)) {
spindle_ptrs_t *spindle = spindle_get(idx);
if(grbl.on_spindle_programmed)
- grbl.on_spindle_programmed(spindle, gc_block.modal.spindle.state, plan_data.spindle.rpm, gc_block.modal.spindle.rpm_mode);
+ grbl.on_spindle_programmed(spindle, gc_block.modal.spindle.state, plan_data.spindle.rpm, gc_block.modal.spindle.rpm_mode);
if(spindle_sync(spindle, gc_block.modal.spindle.state, plan_data.spindle.rpm))
spindle->param->state = gc_block.modal.spindle.state;
else
@@ -3218,6 +3391,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
@@ -3250,7 +3450,12 @@ status_code_t gc_execute_block (char *block)
if(gc_block.user_mcode_sync)
protocol_buffer_synchronize(); // Ensure user defined mcode is executed when specified in program.
+
gc_block.words.mask = user_words.mask;
+ gc_block.values.f = single_meaning_value.f;
+ gc_block.values.o = single_meaning_value.o;
+ gc_block.values.s = single_meaning_value.s;
+ gc_block.values.t = single_meaning_value.t;
hal.user_mcode.execute(state_get(), &gc_block);
gc_block.words.mask = 0;
}
@@ -3354,12 +3559,60 @@ status_code_t gc_execute_block (char *block)
break;
case NonModal_GoHome_0:
+#if N_AXIS > 3
+ {
+ axes_signals_t wrap = { (axis_words.mask & settings.steppers.is_rotary.mask) & settings.steppers.rotary_wrap.mask };
+ if(gc_state.modal.distance_incremental && wrap.mask) {
+ for(idx = Z_AXIS + 1; idx < N_AXIS; idx++) {
+ if(bit_istrue(wrap.mask, bit(idx)) && gc_block.values.xyz[idx] == gc_state.position[idx])
+ gc_block.rotary_wrap.mask |= bit(idx);
+ }
+ }
+ }
+ // no break
+#endif
+
case NonModal_GoHome_1:
// Move to intermediate position before going home. Obeys current coordinate system and offsets
// and absolute and incremental modes.
plan_data.condition.rapid_motion = On; // Set rapid motion condition flag.
- if (axis_command)
+ if(axis_command)
mc_line(gc_block.values.xyz, &plan_data);
+#if N_AXIS > 3
+ if(gc_block.rotary_wrap.mask) {
+
+ coord_system_t wrap_target;
+
+ protocol_buffer_synchronize();
+ memcpy(wrap_target.xyz, gc_block.values.coord_data.xyz, sizeof(coord_system_t));
+
+ for(idx = Z_AXIS + 1; idx < N_AXIS; idx++) {
+ if(bit_istrue(gc_block.rotary_wrap.mask, bit(idx))) {
+ float position, delta;
+ if((wrap_target.xyz[idx] = fmodf(wrap_target.xyz[idx], 360.0f)) < 0.0f)
+ wrap_target.xyz[idx] = 360.0f + wrap_target.xyz[idx];
+ if((position = fmodf(gc_state.position[idx], 360.0f)) < 0.0)
+ position = 360.0f + position;
+ if((delta = position - wrap_target.xyz[idx]) < -180.0f)
+ position += 360.0f;
+ else if(delta > 180.0f)
+ position -= 360.0f;
+ sys.position[idx] = lroundf(position * settings.axis[idx].steps_per_mm);
+ }
+ }
+
+ sync_position();
+ mc_line(wrap_target.xyz, &plan_data);
+ protocol_buffer_synchronize();
+
+ for(idx = Z_AXIS + 1; idx < N_AXIS; idx++) {
+ if(bit_istrue(gc_block.rotary_wrap.mask, bit(idx)))
+ sys.position[idx] = lroundf(gc_block.values.coord_data.xyz[idx] * settings.axis[idx].steps_per_mm);
+ }
+
+ sync_position();
+ } else
+#endif
mc_line(gc_block.values.coord_data.xyz, &plan_data);
memcpy(gc_state.position, gc_block.values.coord_data.xyz, sizeof(gc_state.position));
set_scaling(1.0f);
@@ -3379,9 +3632,14 @@ status_code_t gc_execute_block (char *block)
ngc_named_param_set("_value", 0.0f);
ngc_named_param_set("_value_returned", 0.0f);
#endif
+
status_code_t status = grbl.on_macro_execute((macro_id_t)gc_block.values.p);
- return status == Status_Unhandled ? Status_GcodeValueOutOfRange : status;
+#if NGC_PARAMETERS_ENABLE
+ if(status != Status_Handled)
+ ngc_call_pop();
+#endif
+ return status == Status_Unhandled ? Status_GcodeValueOutOfRange : (status == Status_Handled ? Status_OK : status);
}
break;
@@ -3390,7 +3648,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
@@ -3398,19 +3656,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:
@@ -3667,6 +3925,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 84692ae..00fd4f8 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.
@@ -64,6 +67,15 @@ typedef enum {
#endif
} 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!
@@ -231,8 +243,8 @@ typedef enum {
UserMCode_Generic3 = 103, //!< 103 - For private use only
UserMCode_Generic4 = 104, //!< 104 - For private use only
OpenPNP_GetADCReading = 105, //!< 105 - M105
- Fan_On = 106, //!< 106 - M106
- Fan_Off = 107, //!< 107 - M107
+ Fan_On = 106, //!< 106 - M106, Marlin format
+ Fan_Off = 107, //!< 107 - M107, Marlin format
OpenPNP_GetCurrentPosition = 114, //!< 114 - M114
OpenPNP_FirmwareInfo = 115, //!< 115 - M115
Trinamic_DebugReport = 122, //!< 122 - M122, Marlin format
@@ -353,6 +365,26 @@ typedef union {
#endif
#ifdef V_AXIS
float v;
+#endif
+ };
+ struct {
+ float m0;
+ float m1;
+ float m2;
+#if N_AXIS > 3
+ float m3;
+#endif
+#if N_AXIS > 4
+ float m4;
+#endif
+#if N_AXIS > 5
+ float m5;
+#endif
+#if N_AXIS > 6
+ float m6;
+#endif
+#if N_AXIS == 8
+ float m7;
#endif
};
} coord_data_t;
@@ -416,7 +448,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
@@ -498,6 +530,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.
@@ -575,6 +612,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
@@ -607,6 +646,12 @@ 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
+#if N_AXIS > 3
+ axes_signals_t rotary_wrap;
+#endif
} parser_block_t;
// Initialize the parser
@@ -634,7 +679,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);
@@ -644,4 +689,9 @@ 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
+parameter_words_t gc_get_g65_arguments (void);
+bool gc_modal_state_restore (gc_modal_t *copy);
#endif
+
+#endif // _GCODE_H_
diff --git a/grbl.h b/grbl.h
index be5c054..4594567 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 20240928
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/grbllib.c b/grbllib.c
index bbfb460..778c8bb 100644
--- a/grbllib.c
+++ b/grbllib.c
@@ -283,7 +283,7 @@ int grbl_enter (void)
protocol_enqueue_foreground_task(report_driver_error, NULL);
}
- hal.stepper.enable(settings.steppers.deenergize);
+ hal.stepper.enable(settings.steppers.energize, true);
spindle_all_off();
hal.coolant.set_state((coolant_state_t){0});
diff --git a/hal.h b/hal.h
index aa5f47a..597f27d 100644
--- a/hal.h
+++ b/hal.h
@@ -246,10 +246,11 @@ typedef void (*stepper_go_idle_ptr)(bool clear_signals);
/*! \brief Pointer to function for enabling/disabling stepper motors.
\param enable a \a axes_signals_t union containing separate flags for each motor to enable/disable.
+\param hold a \a true to keep motor powered with reduced current, \a false otherwise.
__NOTE:__ this function may be called from an interrupt context.
*/
-typedef void (*stepper_enable_ptr)(axes_signals_t enable);
+typedef void (*stepper_enable_ptr)(axes_signals_t enable, bool hold);
/*! \brief Pointer to function for enabling/disabling step signals for individual motors.
@@ -302,6 +303,13 @@ typedef void (*stepper_output_step_ptr)(axes_signals_t step_outbits, axes_signal
*/
typedef axes_signals_t (*stepper_get_ganged_ptr)(bool auto_squared);
+/*! \brief Pointer to function for claiming/releasing motor(s) from/to normal step/dir signalling.
+
+\param axis_id a \a the axis to claim/release motor(s) for.
+\param claim \a true to claim a motor(s), \a false to release.
+*/
+typedef void (*stepper_claim_motor_ptr)(uint_fast8_t axis_id, bool claim);
+
/*! \brief Pointer to callback function for outputting the next direction and step pulse signals. _Set by the core on startup._
To be called by the driver from the main stepper interrupt handler (when the timer times out).
@@ -318,6 +326,7 @@ typedef struct {
stepper_pulse_start_ptr pulse_start; //!< Handler for starting outputting direction signals and a step pulse. Called from interrupt context.
stepper_interrupt_callback_ptr interrupt_callback; //!< Callback for informing about the next step pulse to output. _Set by the core at startup._
stepper_get_ganged_ptr get_ganged; //!< Optional handler getting which axes are configured for ganging or auto squaring.
+ stepper_claim_motor_ptr claim_motor; //!< Optional handler for claiming/releasing motor(s) from normal step/dir control.
stepper_output_step_ptr output_step; //!< Optional handler for outputting a single step pulse. _Experimental._ Called from interrupt context.
motor_iterator_ptr motor_iterator; //!< Optional handler iteration over motor vs. axis mappings. Required for the motors plugin (Trinamic drivers).
} stepper_ptrs_t;
@@ -484,6 +493,73 @@ typedef struct {
*/
typedef bool (*irq_claim_ptr)(irq_type_t irq, uint_fast8_t id, irq_callback_ptr callback);
+/************
+ * Timers *
+ ************/
+
+typedef void *hal_timer_t; //!< Timer handle, actual type defined by driver implementation.
+
+typedef enum {
+ Timer_16bit = 0,
+ Timer_32bit,
+ Timer_64bit
+} timer_resolution_t;
+
+typedef union {
+ uint8_t value; //!< All bitmap flags.
+ struct {
+ uint8_t periodic :1, //!<
+ up :1, //!< Timer supports upcounting
+ comp1 :1, //!< Timer supports compare interrupt 0
+ comp2 :1; //!< Timer supports compare interrupt 1
+ };
+} timer_cap_t;
+
+typedef void (*timer_irq_handler_ptr)(void *context);
+
+typedef struct {
+ void *context; //!< Pointer to data to be passed on to the interrupt handlers
+ bool single_shot; //!< Set to true if timer is single shot
+ timer_irq_handler_ptr timeout_callback; //!< Pointer to main timeout callback
+ uint32_t irq0; //!< Compare value for compare interrupt 0
+ timer_irq_handler_ptr irq0_callback; //!< Pointer to compare interrupt 0 callback
+ uint32_t irq1; //!< Compare value for compare interrupt 10
+ timer_irq_handler_ptr irq1_callback; //!< Pointer to compare interrupt 1 callback
+} timer_cfg_t;
+
+/*! \brief Pointer to function for claiming a timer.
+\param cap pointer to a \a timer_cap_t struct containing the required capabilities.
+\param timebase timebase in ns.
+\returns a \a hal_timer_t pointer if successful, \a NULL if not.
+*/
+typedef hal_timer_t (*timer_claim_ptr)(timer_cap_t cap, uint32_t timebase);
+
+/*! \brief Pointer to function for configuring a timer.
+\param timer a \a hal_timer_t pointer.
+\param cfg pointer to a \a timer_cfg_t struct.
+\returns \a true if successful.
+*/
+typedef bool (*timer_cfg_ptr)(hal_timer_t timer, timer_cfg_t *cfg);
+
+/*! \brief Pointer to function for starting a timer.
+\param timer a \a hal_timer_t pointer.
+\param period delay in.
+\returns \a true if successful.
+*/
+typedef bool (*timer_start_ptr)(hal_timer_t timer, uint32_t period);
+
+/*! \brief Pointer to function for stopping a running timer.
+\param timer a \a hal_timer_t pointer.
+\returns \a true if successful.
+*/
+typedef bool (*timer_stop_ptr)(hal_timer_t timer);
+
+typedef struct {
+ timer_claim_ptr claim;
+ timer_cfg_ptr configure;
+ timer_start_ptr start;
+ timer_stop_ptr stop;
+} timer_ptrs_t;
/**************************
* RTC (Real Time Clock *
@@ -491,13 +567,13 @@ typedef bool (*irq_claim_ptr)(irq_type_t irq, uint_fast8_t id, irq_callback_ptr
/*! \brief Pointer to function for setting the current datetime.
\param datetime pointer to a \a tm struct.
-\returns true if successful.
+\\returns \a true if successful.
*/
typedef bool (*rtc_get_datetime_ptr)(struct tm *datetime);
/*! \brief Pointer to function for setting the current datetime.
\param datetime pointer to a \a tm struct.
-\returns true if successful.
+\returns \a true if successful.
*/
typedef bool (*rtc_set_datetime_ptr)(struct tm *datetime);
@@ -541,7 +617,7 @@ typedef struct {
/*! \brief Driver setup handler.
Called once by the core after settings has been loaded. The driver should enable MCU peripherals in the provided function.
\param settings pointer to settings_t structure.
- \returns true if completed successfully and the driver supports the _settings->version_ number, false otherwise.
+ \returns \a true if completed successfully and the driver supports the _settings->version_ number, false otherwise.
*/
driver_setup_ptr driver_setup;
@@ -595,6 +671,7 @@ typedef struct {
settings_changed_ptr settings_changed; //!< Callback handler to be called on settings loaded or settings changed events.
probe_ptrs_t probe; //!< Optional handlers for probe input(s).
tool_ptrs_t tool; //!< Optional handlers for tool changes.
+ timer_ptrs_t timer; //!< Optional handlers for claiming and controlling timers.
rtc_ptrs_t rtc; //!< Optional handlers for real time clock (RTC).
io_port_t port; //!< Optional handlers for axuillary I/O (adds support for M62-M66).
rgb_ptr_t rgb0; //!< Optional handler for RGB output to LEDs (neopixels) or lamps.
@@ -649,7 +726,7 @@ Then _hal.driver_setup()_ will be called so that the driver can configure the re
__NOTE__: This is the only driver function that is called directly from the core, all others are called via HAL function pointers.
-\returns true if completed successfully and the driver matches the _hal.version number_, false otherwise.
+\returns \a true if completed successfully and the driver matches the _hal.version number_, \a false otherwise.
*/
extern bool driver_init (void);
diff --git a/ioports.c b/ioports.c
index 1dfe90c..1d903ce 100644
--- a/ioports.c
+++ b/ioports.c
@@ -749,7 +749,7 @@ void ioport_setting_changed (setting_id_t id)
if(on_setting_changed)
on_setting_changed(id);
- else switch(id) {
+ else if(digital.in.ports && digital.in.ports->n_ports) switch(id) {
case Setting_InvertProbePin:
case Setting_ProbePullUpDisable:
diff --git a/kinematics/corexy.c b/kinematics/corexy.c
index 9350bdf..be35bbb 100644
--- a/kinematics/corexy.c
+++ b/kinematics/corexy.c
@@ -37,6 +37,7 @@
#define B_MOTOR Y_AXIS // Must be Y_AXIS
static on_report_options_ptr on_report_options;
+static travel_limits_ptr check_travel_limits;
// Returns x or y-axis "steps" based on CoreXY motor steps.
inline static int32_t corexy_convert_to_a_motor_steps (int32_t *steps)
@@ -77,12 +78,25 @@ static inline float *transform_from_cartesian (float *target, float *position)
return target;
}
+// Transform position from motor (corexy) coordinate system to cartesian coordinate system
+static inline float *transform_to_cartesian (float *target, float *position)
+{
+ uint_fast8_t idx;
+
+ target[X_AXIS] = (position[X_AXIS] + position[Y_AXIS]) * 0.5f;
+ target[Y_AXIS] = (position[X_AXIS] - position[Y_AXIS]) * 0.5f;
+
+ for(idx = Z_AXIS; idx < N_AXIS; idx++)
+ target[idx] = position[idx];
+
+ return target;
+}
+
static uint_fast8_t corexy_limits_get_axis_mask (uint_fast8_t idx)
{
return ((idx == A_MOTOR) || (idx == B_MOTOR)) ? (bit(X_AXIS) | bit(Y_AXIS)) : bit(idx);
}
-
static void corexy_limits_set_target_pos (uint_fast8_t idx) // fn name?
{
int32_t axis_position;
@@ -102,6 +116,19 @@ static void corexy_limits_set_target_pos (uint_fast8_t idx) // fn name?
}
}
+// Checks and reports if target array exceeds machine travel limits. Returns false if check failed.
+// NOTE: target for axes X and Y are in motor coordinates if is_cartesian is false.
+static bool corexy_check_travel_limits (float *target, axes_signals_t axes, bool is_cartesian)
+{
+ if(is_cartesian)
+ return check_travel_limits(target, axes, true);
+
+ float cartesian_coords[N_AXIS];
+
+ transform_to_cartesian(cartesian_coords, target);
+
+ return check_travel_limits(cartesian_coords, axes, true);
+}
// Set machine positions for homed limit switches. Don't update non-homed axes.
// NOTE: settings.max_travel[] is stored as a negative value.
@@ -207,7 +234,7 @@ static void report_options (bool newopt)
on_report_options(newopt);
if(!newopt)
- hal.stream.write("[KINEMATICS:CoreXY v2.00]" ASCII_EOL);
+ hal.stream.write("[KINEMATICS:CoreXY v2.01]" ASCII_EOL);
}
// Initialize API pointers for CoreXY kinematics
@@ -222,6 +249,9 @@ void corexy_init (void)
kinematics.homing_cycle_validate = homing_cycle_validate;
kinematics.homing_cycle_get_feedrate = homing_cycle_get_feedrate;
+ check_travel_limits = grbl.check_travel_limits;
+ grbl.check_travel_limits = corexy_check_travel_limits;
+
on_report_options = grbl.on_report_options;
grbl.on_report_options = report_options;
}
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/machine_limits.c b/machine_limits.c
index c679456..4231d59 100644
--- a/machine_limits.c
+++ b/machine_limits.c
@@ -273,6 +273,7 @@ static bool homing_cycle (axes_signals_t cycle, axes_signals_t auto_square)
squaring_mode_t squaring_mode = SquaringMode_Both;
coord_data_t target;
plan_line_data_t plan_data;
+ rt_exec_t rt_exec, rt_exec_states = EXEC_SAFETY_DOOR|EXEC_RESET|EXEC_CYCLE_COMPLETE;
plan_data_init(&plan_data);
plan_data.condition.system_motion = On;
@@ -296,7 +297,7 @@ static bool homing_cycle (axes_signals_t cycle, axes_signals_t auto_square)
// NOTE: settings.axis[].max_travel is stored as a negative value.
if(bit_istrue(cycle.mask, bit(idx))) {
#if N_AXIS > 3
- if(bit_istrue(settings.steppers.is_rotational.mask, bit(idx)))
+ if(bit_istrue(settings.steppers.is_rotary.mask, bit(idx)))
max_travel = max(max_travel, (-HOMING_AXIS_SEARCH_SCALAR) * (settings.axis[idx].max_travel < -0.0f ? settings.axis[idx].max_travel : -360.0f));
else
#endif
@@ -320,6 +321,9 @@ static bool homing_cycle (axes_signals_t cycle, axes_signals_t auto_square)
autosquare_fail_distance = truncf(fail_distance * settings.axis[dual_motor_axis].steps_per_mm);
}
+ if(settings.status_report.when_homing)
+ rt_exec_states |= EXEC_STATUS_REPORT;
+
// Set search mode with approach at seek rate to quickly engage the specified cycle.mask limit switches.
do {
@@ -420,36 +424,40 @@ static bool homing_cycle (axes_signals_t cycle, axes_signals_t auto_square)
st_prep_buffer(); // Check and prep segment buffer.
// Exit routines: No time to run protocol_execute_realtime() in this loop.
- if (sys.rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_COMPLETE)) {
+ if((rt_exec = (sys.rt_exec_state & rt_exec_states))) {
- uint_fast16_t rt_exec = sys.rt_exec_state;
-
- // Homing failure condition: Reset issued during cycle.
- if (rt_exec & EXEC_RESET)
- system_set_exec_alarm(Alarm_HomingFailReset);
-
- // Homing failure condition: Safety door was opened.
- if (rt_exec & EXEC_SAFETY_DOOR)
- system_set_exec_alarm(Alarm_HomingFailDoor);
-
- hal.delay_ms(2, NULL);
-
- // Homing failure condition: Homing switch(es) still engaged after pull-off motion
- if (mode == HomingMode_Pulloff && (homing_signals_select(hal.homing.get_state(), (axes_signals_t){0}, SquaringMode_Both).mask & cycle.mask))
- system_set_exec_alarm(Alarm_FailPulloff);
-
- // Homing failure condition: Limit switch not found during approach.
- if (mode != HomingMode_Pulloff && (rt_exec & EXEC_CYCLE_COMPLETE))
- system_set_exec_alarm(Alarm_HomingFailApproach);
-
- if (sys.rt_exec_alarm) {
- mc_reset(); // Stop motors, if they are running.
- protocol_execute_realtime();
- return false;
+ if(rt_exec == EXEC_STATUS_REPORT) {
+ system_clear_exec_state_flag(EXEC_STATUS_REPORT);
+ report_realtime_status();
} else {
- // Pull-off motion complete. Disable CYCLE_STOP from executing.
- system_clear_exec_state_flag(EXEC_CYCLE_COMPLETE);
- break;
+
+ // Homing failure condition: Reset issued during cycle.
+ if (rt_exec & EXEC_RESET)
+ system_set_exec_alarm(Alarm_HomingFailReset);
+
+ // Homing failure condition: Safety door was opened.
+ if (rt_exec & EXEC_SAFETY_DOOR)
+ system_set_exec_alarm(Alarm_HomingFailDoor);
+
+ hal.delay_ms(2, NULL);
+
+ // Homing failure condition: Homing switch(es) still engaged after pull-off motion
+ if (mode == HomingMode_Pulloff && (homing_signals_select(hal.homing.get_state(), (axes_signals_t){0}, SquaringMode_Both).mask & cycle.mask))
+ system_set_exec_alarm(Alarm_FailPulloff);
+
+ // Homing failure condition: Limit switch not found during approach.
+ if (mode != HomingMode_Pulloff && (rt_exec & EXEC_CYCLE_COMPLETE))
+ system_set_exec_alarm(Alarm_HomingFailApproach);
+
+ if (sys.rt_exec_alarm) {
+ mc_reset(); // Stop motors, if they are running.
+ protocol_execute_realtime();
+ return false;
+ } else {
+ // Pull-off motion complete. Disable CYCLE_STOP from executing.
+ system_clear_exec_state_flag(EXEC_CYCLE_COMPLETE);
+ break;
+ }
}
}
diff --git a/messages.h b/messages.h
index 0b1a8fb..3983fed 100644
--- a/messages.h
+++ b/messages.h
@@ -53,7 +53,8 @@ typedef enum {
typedef enum {
Message_Plain = 0,
Message_Info,
- Message_Warning
+ Message_Warning,
+ Message_Debug
} message_type_t;
typedef struct {
diff --git a/modbus.h b/modbus.h
index c48a538..9368391 100644
--- a/modbus.h
+++ b/modbus.h
@@ -25,7 +25,7 @@
#define _MODBUS_H_
#ifndef MODBUS_MAX_ADU_SIZE
-#define MODBUS_MAX_ADU_SIZE 10
+#define MODBUS_MAX_ADU_SIZE 12
#endif
#ifndef MODBUS_QUEUE_LENGTH
#define MODBUS_QUEUE_LENGTH 8
diff --git a/motion_control.c b/motion_control.c
index 30ad89b..29bbe45 100644
--- a/motion_control.c
+++ b/motion_control.c
@@ -247,7 +247,7 @@ void mc_arc (float *target, plan_line_data_t *pl_data, float *position, float *o
if(labs(turns) > 1) {
uint32_t n_turns = labs(turns) - 1;
- float arc_travel = 2.0f * M_PI * n_turns + angular_travel;
+ float arc_travel = 2.0f * M_PI * (float)n_turns + (turns > 0 ? angular_travel : -angular_travel);
coord_data_t arc_target;
#if N_AXIS > 3
uint_fast8_t idx = N_AXIS;
@@ -874,8 +874,10 @@ status_code_t mc_homing_cycle (axes_signals_t cycle)
state_set(STATE_HOMING); // Set homing system state.
#if COMPATIBILITY_LEVEL == 0
- system_set_exec_state_flag(EXEC_STATUS_REPORT); // Force a status report and
- delay_sec(0.1f, DelayMode_Dwell); // delay a bit to get it sent (or perhaps wait a bit for a request?)
+ if(!settings.status_report.when_homing) {
+ system_set_exec_state_flag(EXEC_STATUS_REPORT); // Force a status report and
+ delay_sec(0.1f, DelayMode_Dwell); // delay a bit to get it sent (or perhaps wait a bit for a request?)
+ }
#endif
// Turn off spindle and coolant (and update parser state)
if(spindle_is_on())
@@ -992,10 +994,10 @@ gc_probe_t mc_probe_cycle (float *target, plan_line_data_t *pl_data, gc_parser_f
hal.probe.configure(parser_flags.probe_is_away, true);
#if COMPATIBILITY_LEVEL <= 1
- bool at_g59_3 = false, probe_fixture = grbl.on_probe_fixture != NULL && state_get() != STATE_TOOL_CHANGE && (sys.homed.mask & (X_AXIS_BIT|Y_AXIS_BIT));
+ bool at_g59_3 = false, probe_toolsetter = grbl.on_probe_toolsetter != NULL && state_get() != STATE_TOOL_CHANGE && (sys.homed.mask & (X_AXIS_BIT|Y_AXIS_BIT));
- if(probe_fixture)
- grbl.on_probe_fixture(NULL, at_g59_3 = system_xy_at_fixture(CoordinateSystem_G59_3, TOOLSETTER_RADIUS), true);
+ if(probe_toolsetter)
+ grbl.on_probe_toolsetter(NULL, NULL, at_g59_3 = system_xy_at_fixture(CoordinateSystem_G59_3, TOOLSETTER_RADIUS), true);
#endif
// After syncing, check if probe is already triggered or not connected. If so, halt and issue alarm.
@@ -1054,8 +1056,8 @@ gc_probe_t mc_probe_cycle (float *target, plan_line_data_t *pl_data, gc_parser_f
protocol_execute_realtime(); // Check and execute run-time commands
#if COMPATIBILITY_LEVEL <= 1
- if(probe_fixture)
- grbl.on_probe_fixture(NULL, at_g59_3, false);
+ if(probe_toolsetter)
+ grbl.on_probe_toolsetter(NULL, NULL, at_g59_3, false);
#endif
// Reset the stepper and planner buffers to remove the remainder of the probe motion.
diff --git a/ngc_expr.c b/ngc_expr.c
index aa2879f..91f336d 100644
--- a/ngc_expr.c
+++ b/ngc_expr.c
@@ -53,7 +53,7 @@ typedef enum {
NGCBinaryOp_LE,
NGCBinaryOp_GE,
NGCBinaryOp_GT,
- NGCBinaryOp_RelationalLast = NGCBinaryOp_GT,
+ NGCBinaryOp_RelationalLast = NGCBinaryOp_GT
} ngc_binary_op_t;
typedef enum {
@@ -70,7 +70,7 @@ typedef enum {
NGCUnaryOp_SIN,
NGCUnaryOp_SQRT,
NGCUnaryOp_TAN,
- NGCUnaryOp_Exists, // Not implemented
+ NGCUnaryOp_Exists
} ngc_unary_op_t;
/*! \brief Executes the operations: /, MOD, ** (POW), *.
@@ -675,19 +675,22 @@ static status_code_t read_unary (char *line, uint_fast8_t *pos, float *value)
else {
- if (operation == NGCUnaryOp_Exists) {
+ if(operation == NGCUnaryOp_Exists) {
- char *arg = &line[++(*pos)], *s;
+ char *arg = &line[++(*pos)], *s = NULL;
- s = arg;
- while(*s && *s != ']')
- s++;
+ if(*arg == '#' && *(arg + 1) == '<') {
+ arg += 2;
+ s = arg;
+ while(*s && *s != ']')
+ s++;
+ }
- if(*s == ']') {
- *s = '\0';
+ if(s && *s == ']' && *(s - 1) == '>') {
+ *(s - 1) = '\0';
*value = ngc_named_param_exists(arg) ? 1.0f : 0.0f;
- *s = ']';
- *pos = *pos + s - arg + 1;
+ *(s - 1) = '>';
+ *pos = *pos + s - arg + 3;
} else
status = Status_ExpressionSyntaxError;
@@ -791,7 +794,7 @@ status_code_t ngc_eval_expression (char *line, uint_fast8_t *pos, float *value)
return status;
operators[stack_index - 1] = operators[stack_index];
- if((stack_index > 1) && precedence(operators[stack_index - 1] <= precedence(operators[stack_index - 2])))
+ if(stack_index > 1 && precedence(operators[stack_index - 1]) <= precedence(operators[stack_index - 2]))
stack_index--;
else
break;
diff --git a/ngc_flowctrl.c b/ngc_flowctrl.c
index a7c2125..f24fe09 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;
@@ -137,7 +156,7 @@ static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *ope
*operation = NGCFlowCtrl_If;
(*pos)++;
} else
- *operation = Status_FlowControlSyntaxError; // Unknown statement name starting with F
+ status = Status_FlowControlSyntaxError; // Unknown statement name starting with F
break;
case 'R':
@@ -148,7 +167,15 @@ static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *ope
*operation = NGCFlowCtrl_Return;
*pos += 5;
} else
- *operation = Status_FlowControlSyntaxError; // Unknown statement name starting with R
+ status = 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':
@@ -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) {
@@ -338,8 +404,8 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo
case NGCFlowCtrl_EndRepeat:
if(hal.stream.file) {
if(last_op == NGCFlowCtrl_Repeat) {
- if(!skipping && o_label == stack[stack_idx].o_label) {
- if(stack[stack_idx].repeats && --stack[stack_idx].repeats)
+ if(o_label == stack[stack_idx].o_label) {
+ if(!skipping && stack[stack_idx].repeats && --stack[stack_idx].repeats)
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
else
stack_pull();
@@ -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[30];
+ 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..b78da3f 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,45 @@ 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 _absolute_pos (uint_fast8_t axis)
+{
+ float value;
+
+ if(axis < N_AXIS) {
+ value = sys.position[axis] / settings.axis[axis].steps_per_mm;
+ if(settings.flags.report_inches)
+ value *= 25.4f;
+ } else
+ value = 0.0f;
+
+ return value;
+}
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 +280,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 +314,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 +328,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;
@@ -353,10 +387,20 @@ PROGMEM static const ngc_named_ro_param_t ngc_named_ro_param[] = {
{ .name = "_u", .id = NGCParam_u },
{ .name = "_v", .id = NGCParam_v },
{ .name = "_w", .id = NGCParam_w },
+ { .name = "_abs_x", .id = NGCParam_abs_x },
+ { .name = "_abs_y", .id = NGCParam_abs_y },
+ { .name = "_abs_z", .id = NGCParam_abs_z },
+ { .name = "_abs_a", .id = NGCParam_abs_a },
+ { .name = "_abs_b", .id = NGCParam_abs_b },
+ { .name = "_abs_c", .id = NGCParam_abs_c },
+ { .name = "_abs_u", .id = NGCParam_abs_u },
+ { .name = "_abs_v", .id = NGCParam_abs_v },
+ { .name = "_abs_w", .id = NGCParam_abs_w },
{ .name = "_current_tool", .id = NGCParam_current_tool },
{ .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 },
};
@@ -523,6 +567,26 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id)
value = _relative_pos(id - NGCParam_x);
break;
+ case NGCParam_abs_x:
+ //no break
+ case NGCParam_abs_y:
+ //no break
+ case NGCParam_abs_z:
+ //no break
+ case NGCParam_abs_a:
+ //no break
+ case NGCParam_abs_b:
+ //no break
+ case NGCParam_abs_c:
+ //no break
+ case NGCParam_abs_u:
+ //no break
+ case NGCParam_abs_v:
+ //no break
+ case NGCParam_abs_w:
+ value = _absolute_pos(id - NGCParam_abs_x);
+ break;
+
case NGCParam_current_tool:
value = (float)gc_state.tool->tool_id;
break;
@@ -539,6 +603,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 +614,31 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id)
return value;
}
+// Lowercase name, remove control characters and spaces
+static char *ngc_name_tolower (char *s)
+{
+ static char name[NGC_MAX_PARAM_LENGTH + 1];
+
+ uint_fast8_t len = 0;
+ char c, *s1 = s, *s2 = name;
+
+ while((c = *s1++) && len < NGC_MAX_PARAM_LENGTH) {
+ if(c > ' ') {
+ *s2++ = LCAPS(c);
+ len++;
+ }
+ }
+ *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);
+ name = ngc_name_tolower(name);
*value = 0.0f;
@@ -565,9 +649,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 +664,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';
+ name = ngc_name_tolower(name);
// Check if name is supplied, return false if not.
if((*name == '_' ? *(name + 1) : *name) == '\0')
@@ -595,36 +675,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';
+ name = ngc_name_tolower(name);
// Check if name is supplied, return false if not.
if((*name == '_' ? *(name + 1) : *name) == '\0')
@@ -637,12 +712,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 +728,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 +743,106 @@ 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);
+}
+
+uint8_t ngc_float_decimals (void)
+{
+ return settings.flags.report_inches ? N_DECIMAL_COORDVALUE_INCH : N_DECIMAL_COORDVALUE_MM;
+}
+
#endif // NGC_PARAMETERS_ENABLE
diff --git a/ngc_params.h b/ngc_params.h
index 38052fa..977eff8 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 {
@@ -77,13 +79,24 @@ typedef enum {
NGCParam_u,
NGCParam_v,
NGCParam_w,
+ NGCParam_abs_x,
+ NGCParam_abs_y,
+ NGCParam_abs_z,
+ NGCParam_abs_a,
+ NGCParam_abs_b,
+ NGCParam_abs_c,
+ NGCParam_abs_u,
+ NGCParam_abs_v,
+ NGCParam_abs_w,
NGCParam_current_tool,
NGCParam_current_pocket,
NGCParam_selected_tool,
NGCParam_selected_pocket,
+ NGCParam_call_level,
NGCParam_Last
} ncg_name_param_id_t;
+uint8_t ngc_float_decimals (void);
bool ngc_param_get (ngc_param_id_t id, float *value);
bool ngc_param_set (ngc_param_id_t id, float value);
bool ngc_param_is_rw (ngc_param_id_t id);
@@ -92,5 +105,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..d32ce75 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)
{
@@ -305,7 +319,7 @@ bool delay_sec (float seconds, delaymode_t mode)
while(--i && ok) {
if(mode == DelayMode_Dwell)
ok = protocol_execute_realtime();
- else // DelayMode_SysSuspende, xecute rt_system() only to avoid nesting suspend loops.
+ else // DelayMode_SysSuspend, execute rt_system() only to avoid nesting suspend loops.
ok = protocol_exec_rt_system() && !state_door_reopened(); // Bail, if safety door reopens.
if(ok)
hal.delay_ms(DWELL_TIME_STEP, NULL); // Delay DWELL_TIME_STEP increment
diff --git a/nuts_bolts.h b/nuts_bolts.h
index 2feafc3..ec5b8ba 100644
--- a/nuts_bolts.h
+++ b/nuts_bolts.h
@@ -129,6 +129,7 @@ extern char const *const axis_letter[];
typedef union {
uint8_t mask;
+ uint8_t bits;
uint8_t value;
struct {
uint8_t x :1,
@@ -211,6 +212,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/pin_bits_masks.h b/pin_bits_masks.h
index ea34cc5..459028d 100644
--- a/pin_bits_masks.h
+++ b/pin_bits_masks.h
@@ -43,7 +43,7 @@
#error "I2C keypad/strobe is not supported in this configuration!"
#endif
-#if MPG_MODE == 1 && !defined(MPG_MODE_PIN)
+#if MPG_ENABLE == 1 && !defined(MPG_MODE_PIN)
#error "MPG mode input is not supported in this configuration!"
#endif
@@ -197,7 +197,7 @@
#if SAFETY_DOOR_ENABLE || MOTOR_FAULT_ENABLE || MOTOR_WARNING_ENABLE || PROBE_DISCONNECT_ENABLE || \
STOP_DISABLE_ENABLE || BLOCK_DELETE_ENABLE || SINGLE_BLOCK_ENABLE || LIMITS_OVERRIDE_ENABLE || \
- (defined(AUX_DEVICES) && (PROBE_ENABLE || I2C_STROBE_ENABLE || MPG_MODE == 1 || QEI_SELECT_ENABLE)) || defined __DOXYGEN__
+ (defined(AUX_DEVICES) && (PROBE_ENABLE || I2C_STROBE_ENABLE || MPG_ENABLE == 1 || QEI_SELECT_ENABLE)) || defined __DOXYGEN__
#define AUX_CONTROLS_ENABLED 1
@@ -244,7 +244,7 @@ static aux_ctrl_t aux_ctrl[] = {
{ .function = Input_I2CStrobe, .aux_port = 0xFF, .irq_mode = (pin_irq_mode_t)(IRQ_Mode_Change), .cap = { .value = 0 }, .pin = I2C_STROBE_PIN, .port = NULL },
#endif
#endif
-#if MPG_MODE == 1 && defined(MPG_MODE_PIN) && defined(AUX_DEVICES)
+#if MPG_ENABLE == 1 && defined(MPG_MODE_PIN) && defined(AUX_DEVICES)
#ifdef MPG_MODE_PORT
{ .function = Input_MPGSelect, .aux_port = 0xFF, .irq_mode = (pin_irq_mode_t)(IRQ_Mode_Change), .cap = { .value = 0 }, .pin = MPG_MODE_PIN, .port = MPG_MODE_PORT },
#else
diff --git a/planner.c b/planner.c
index 7845682..ab8372e 100644
--- a/planner.c
+++ b/planner.c
@@ -416,6 +416,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;
@@ -487,13 +488,13 @@ bool plan_buffer_line (float *target, plan_line_data_t *pl_data)
if(!block->condition.inverse_time &&
!block->condition.rapid_motion &&
- (motion.mask & settings.steppers.is_rotational.mask) &&
- (motion.mask & ~settings.steppers.is_rotational.mask)) {
+ (motion.mask & settings.steppers.is_rotary.mask) &&
+ (motion.mask & ~settings.steppers.is_rotary.mask)) {
float linear_magnitude = 0.0f;
idx = 0;
- motion.mask &= ~settings.steppers.is_rotational.mask;
+ motion.mask &= ~settings.steppers.is_rotary.mask;
while(motion.mask) {
if(motion.mask & 0x01)
@@ -720,6 +721,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 726066c..39191c8 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.
@@ -99,6 +100,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/plugins.h b/plugins.h
index d7c17c0..a3f870f 100644
--- a/plugins.h
+++ b/plugins.h
@@ -7,20 +7,20 @@
Part of grblHAL
- Copyright (c) 2020-2023 Terje Io
+ Copyright (c) 2020-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 .
*/
#ifndef _PLUGINS_H_
@@ -92,7 +92,7 @@ typedef struct {
#if MQTT_ENABLE
mqtt_settings_t mqtt;
#endif
-#ifdef _WIZCHIP_
+#if defined(_WIZCHIP_) || defined(HAS_MAC_SETTING)
uint8_t mac[6];
#endif
} network_settings_t;
diff --git a/plugins_init.h b/plugins_init.h
index c41ef9e..8521056 100644
--- a/plugins_init.h
+++ b/plugins_init.h
@@ -118,12 +118,21 @@
embroidery_init();
#endif
+#if RGB_LED_ENABLE
+ extern void rgb_led_init (void);
+ rgb_led_init();
+#endif
+
extern void my_plugin_init (void);
my_plugin_init();
#if N_SPINDLE > 1
extern void spindle_select_init(void);
spindle_select_init();
+ #if SPINDLE_OFFSET == 1
+ extern void spindle_offset_init (void);
+ spindle_offset_init();
+ #endif
#endif
// Third party plugin definitions.
@@ -166,6 +175,11 @@
panel_init();
#endif
+#if EVENTOUT_ENABLE
+ extern void event_out_init (void);
+ event_out_init();
+#endif
+
// End third party plugin definitions.
#if ODOMETER_ENABLE
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..aaacf30 100644
--- a/report.c
+++ b/report.c
@@ -21,7 +21,7 @@
*/
/*
- This file functions as the primary feedback interface for Grbl. Any outgoing data, such
+ This file functions as the primary feedback interface for grblHAL. Any outgoing data, such
as the protocol status messages, feedback messages, and status reports, are stored here.
For the most part, these functions primarily are called from protocol.c methods. If a
different style feedback is desired (i.e. JSON), then a user can change these following
@@ -38,6 +38,7 @@
#include "nvs_buffer.h"
#include "machine_limits.h"
#include "state_machine.h"
+#include "canbus.h"
#include "regex.h"
#if ENABLE_SPINDLE_LINEARIZATION
@@ -117,7 +118,7 @@ static char *get_axis_values_inches (float *axis_values)
if(idx == X_AXIS && gc_state.modal.diameter_mode)
strcat(buf, ftoa(axis_values[idx] * INCH_PER_MM * 2.0f, N_DECIMAL_COORDVALUE_INCH));
#if N_AXIS > 3
- else if(idx > Z_AXIS && bit_istrue(settings.steppers.is_rotational.mask, bit(idx)))
+ else if(idx > Z_AXIS && bit_istrue(settings.steppers.is_rotary.mask, bit(idx)))
strcat(buf, ftoa(axis_values[idx], N_DECIMAL_COORDVALUE_MM));
#endif
else
@@ -228,6 +229,9 @@ static status_code_t report_status_message (status_code_t status_code)
{
switch(status_code) {
+ case Status_Handled:
+ status_code = Status_OK;
+ // no break
case Status_OK: // STATUS_OK
hal.stream.write("ok" ASCII_EOL);
break;
@@ -266,6 +270,10 @@ void report_message (const char *msg, message_type_t type)
hal.stream.write("Warning: ");
break;
+ case Message_Debug:
+ hal.stream.write("Debug: ");
+ break;
+
default:
break;
}
@@ -328,6 +336,16 @@ static void report_help_message (void)
hal.stream.write("[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H $B ~ ! ? ctrl-x]" ASCII_EOL);
}
+// Prints plugin info.
+void report_plugin (const char *name, const char *version)
+{
+ hal.stream.write("[PLUGIN:");
+ hal.stream.write(name);
+ hal.stream.write(" v");
+ hal.stream.write(version);
+ hal.stream.write("]" ASCII_EOL);
+}
+
static bool report_group_settings (const setting_group_detail_t *groups, const uint_fast8_t n_groups, char *args)
{
bool found = false;
@@ -404,7 +422,7 @@ status_code_t report_help (char *args)
}
-// Grbl settings print out.
+// grblHAL settings print out.
static int cmp_settings (const void *a, const void *b)
{
@@ -484,7 +502,7 @@ void report_grbl_settings (bool all, void *data)
if(all && (details = details->next)) do {
for(idx = 0; idx < details->n_settings; idx++) {
setting = &details->settings[idx];
- if(setting->is_available == NULL ||setting->is_available(setting)) {
+ if(setting->is_available == NULL || setting->is_available(setting)) {
*psetting++ = (setting_detail_t *)setting;
n_settings++;
}
@@ -507,7 +525,7 @@ void report_grbl_settings (bool all, void *data)
// Prints current probe parameters. Upon a probe command, these parameters are updated upon a
// successful probe or upon a failed probe with the G38.3 without errors command (if supported).
-// These values are retained until Grbl is power-cycled, whereby they will be re-zeroed.
+// These values are retained until grblHAL is power-cycled, whereby they will be re-zeroed.
void report_probe_parameters (void)
{
// Report in terms of machine position.
@@ -553,7 +571,7 @@ status_code_t report_ngc_parameter (ngc_param_id_t id)
hal.stream.write(uitoa(id));
if(ngc_param_get(id, &value)) {
hal.stream.write("=");
- hal.stream.write(ftoa(value, 3));
+ hal.stream.write(trim_float(ftoa(value, ngc_float_decimals())));
} else
hal.stream.write("=N/A");
hal.stream.write("]" ASCII_EOL);
@@ -570,7 +588,7 @@ status_code_t report_named_ngc_parameter (char *arg)
hal.stream.write(arg);
if(ngc_named_param_get(arg, &value)) {
hal.stream.write("=");
- hal.stream.write(ftoa(value, 3));
+ hal.stream.write(trim_float(ftoa(value, ngc_float_decimals())));
} else
hal.stream.write("=N/A");
hal.stream.write("]" ASCII_EOL);
@@ -580,7 +598,7 @@ status_code_t report_named_ngc_parameter (char *arg)
#endif
-// Prints Grbl NGC parameters (coordinate offsets, probing, tool table)
+// Prints grblHAL NGC parameters (coordinate offsets, probing, tool table)
void report_ngc_parameters (void)
{
uint_fast8_t idx;
@@ -655,6 +673,9 @@ void report_ngc_parameters (void)
hal.stream.write(get_axis_value(sys.tlo_reference[plane.axis_linear] / settings.axis[plane.axis_linear].steps_per_mm));
hal.stream.write("]" ASCII_EOL);
}
+
+ if(grbl.on_report_ngc_parameters)
+ grbl.on_report_ngc_parameters();
}
static inline bool is_g92_active (void)
@@ -985,6 +1006,9 @@ void report_build_info (char *line, bool extended)
if(hal.rtc.get_datetime)
strcat(buf, "RTC,");
+ if(canbus_enabled())
+ strcat(buf, "CAN,");
+
#ifdef PID_LOG
strcat(buf, "PID,");
#endif
@@ -1077,9 +1101,8 @@ void report_build_info (char *line, bool extended)
}
}
-
-// Prints the character string line Grbl has received from the user, which has been pre-parsed,
-// and has been sent into protocol_execute_line() routine to be executed by Grbl.
+// Prints the character string line grblHAL has received from the user, which has been pre-parsed,
+// and has been sent into protocol_execute_line() routine to be executed by grblHAL.
void report_echo_line_received (char *line)
{
hal.stream.write("[echo: ");
@@ -1087,6 +1110,15 @@ void report_echo_line_received (char *line)
hal.stream.write("]" ASCII_EOL);
}
+#if N_SYS_SPINDLE == 1 && N_SPINDLE > 1
+
+static void report_spindle_num (spindle_info_t *spindle, void *data)
+{
+ if(spindle->id == *((spindle_id_t *)data))
+ hal.stream.write_all(appendbuf(2, "|S:", uitoa((uint32_t)spindle->num)));
+}
+
+#endif
// Prints real-time data. This function grabs a real-time snapshot of the stepper subprogram
// and the actual location of the CNC machine. Users may change the following function to their
@@ -1171,11 +1203,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];
}
}
@@ -1235,7 +1267,7 @@ void report_realtime_status (void)
#elif N_SPINDLE > 1
if(report.spindle_id)
- hal.stream.write_all(appendbuf(2, "|S:", uitoa((uint32_t)spindle_0->id)));
+ spindle_enumerate_spindles(report_spindle_num, &spindle_0->id);
#endif
if(settings.status_report.pin_state) {
@@ -1281,13 +1313,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 +1330,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 +1340,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) {
@@ -2499,9 +2537,56 @@ static void report_spindle (spindle_info_t *spindle, void *data)
}
}
+#if N_SPINDLE > 1
+
+typedef struct {
+ uint32_t idx;
+ uint32_t n_spindles;
+ spindle_info_t *spindles;
+} spindle_rdata_t;
+
+static void get_spindles (spindle_info_t *spindle, void *data)
+{
+ memcpy(&((spindle_rdata_t *)data)->spindles[((spindle_rdata_t *)data)->idx++], spindle, sizeof(spindle_info_t));
+}
+
+static int cmp_spindles (const void *a, const void *b)
+{
+ uint32_t key_a = ((spindle_info_t *)a)->num == -1 ? ((((spindle_info_t *)a)->hal->type + 1) << 8) | ((spindle_info_t *)a)->id : ((spindle_info_t *)a)->num,
+ key_b = ((spindle_info_t *)b)->num == -1 ? ((((spindle_info_t *)b)->hal->type + 1) << 8) | ((spindle_info_t *)b)->id : ((spindle_info_t *)b)->num;
+
+ return key_a - key_b;
+}
+
+#endif
+
status_code_t report_spindles (bool machine_readable)
{
- if(!spindle_enumerate_spindles(report_spindle, (void *)machine_readable) && !machine_readable)
+ bool has_spindles;
+
+#if N_SPINDLE > 1
+
+ spindle_rdata_t spindle_data = {0};
+
+ if((spindle_data.spindles = malloc(N_SPINDLE * sizeof(spindle_info_t)))) {
+
+ has_spindles = spindle_enumerate_spindles(get_spindles, &spindle_data);
+
+ spindle_data.n_spindles = spindle_data.idx;
+
+ qsort(spindle_data.spindles, spindle_data.n_spindles, sizeof(spindle_info_t), cmp_spindles);
+ for(spindle_data.idx = 0; spindle_data.idx < spindle_data.n_spindles; spindle_data.idx++)
+ report_spindle(&spindle_data.spindles[spindle_data.idx], (void *)machine_readable);
+
+ free(spindle_data.spindles);
+
+ } else
+
+#endif
+
+ has_spindles = spindle_enumerate_spindles(report_spindle, (void *)machine_readable);
+
+ if(!has_spindles && !machine_readable)
hal.stream.write("No spindles registered." ASCII_EOL);
return Status_OK;
diff --git a/report.h b/report.h
index b5b1b3a..1a7d99f 100644
--- a/report.h
+++ b/report.h
@@ -52,6 +52,8 @@ void report_warning (void *message);
// Prints Grbl help.
status_code_t report_help (char *args);
+void report_plugin (const char *name, const char *version);
+
// Prints Grbl settings
void report_grbl_settings (bool all, void *data);
diff --git a/rgb.h b/rgb.h
index ff47b7d..8bd7cce 100644
--- a/rgb.h
+++ b/rgb.h
@@ -41,6 +41,15 @@ typedef union {
};
} rgb_color_t;
+typedef union {
+ uint8_t mask;
+ struct {
+ uint8_t is_blocking :1,
+ is_strip :1,
+ unassigned :6;
+ };
+} rgb_properties_t;
+
/*! \brief Pointer to function for setting RGB (LED) output.
\param color a \a rgb_color_t union.
*/
@@ -68,6 +77,7 @@ typedef struct {
rgb_write_ptr write; //!< Optional handler for outputting data to Neopixel strip.
rgb_set_intensity_ptr set_intensity; //!< Optional handler for setting intensity, range 0 - 255.
rgb_color_t cap; //!< Driver capability, color value: 0 - not available, 1 - on off, > 1 - intensity range 0 - n.
+ rgb_properties_t flags; //!< Driver property flags.
uint16_t num_devices; //!< Number of devices (LEDs) available.
} rgb_ptr_t;
diff --git a/settings.c b/settings.c
index ca62a31..ae05b7c 100644
--- a/settings.c
+++ b/settings.c
@@ -101,9 +101,10 @@ PROGMEM const settings_t defaults = {
#else
.steppers.enable_invert.mask = AXES_BITMASK,
#endif
- .steppers.deenergize.mask = DEFAULT_STEPPER_DEENERGIZE_MASK,
+ .steppers.energize.mask = DEFAULT_STEPPER_DEENERGIZE_MASK,
#if N_AXIS > 3
- .steppers.is_rotational.mask = (DEFAULT_AXIS_ROTATIONAL_MASK & AXES_BITMASK) & 0b11111000,
+ .steppers.is_rotary.mask = (DEFAULT_AXIS_ROTATIONAL_MASK & AXES_BITMASK) & 0b11111000,
+ .steppers.rotary_wrap.mask = (DEFAULT_AXIS_ROTARY_WRAP_MASK & AXES_BITMASK) & 0b11111000,
#endif
#if DEFAULT_HOMING_ENABLE
.homing.flags.enabled = DEFAULT_HOMING_ENABLE,
@@ -143,6 +144,7 @@ PROGMEM const settings_t defaults = {
.status_report.parser_state = DEFAULT_REPORT_PARSER_STATE,
.status_report.alarm_substate = DEFAULT_REPORT_ALARM_SUBSTATE,
.status_report.run_substate = DEFAULT_REPORT_RUN_SUBSTATE,
+ .status_report.when_homing = DEFAULT_REPORT_WHEN_HOMING,
.limits.flags.hard_enabled = DEFAULT_HARD_LIMIT_ENABLE,
.limits.flags.soft_enabled = DEFAULT_SOFT_LIMIT_ENABLE,
.limits.flags.jog_soft_limited = DEFAULT_JOG_LIMIT_ENABLE,
@@ -330,7 +332,10 @@ PROGMEM const settings_t defaults = {
.safety_door.flags.ignore_when_idle = DEFAULT_DOOR_IGNORE_WHEN_IDLE,
.safety_door.flags.keep_coolant_on = DEFAULT_DOOR_KEEP_COOLANT_ON,
.safety_door.spindle_on_delay = DEFAULT_SAFETY_DOOR_SPINDLE_DELAY,
- .safety_door.coolant_on_delay = DEFAULT_SAFETY_DOOR_COOLANT_DELAY
+ .safety_door.coolant_on_delay = DEFAULT_SAFETY_DOOR_COOLANT_DELAY,
+
+ .rgb_strip0_length = DEFAULT_RGB_STRIP0_LENGTH,
+ .rgb_strip1_length = DEFAULT_RGB_STRIP1_LENGTH
};
static bool group_is_available (const setting_group_detail_t *group)
@@ -415,7 +420,7 @@ static status_code_t set_tool_change_mode (setting_id_t id, uint_fast16_t int_va
static status_code_t set_tool_change_probing_distance (setting_id_t id, float value);
static status_code_t set_tool_restore_pos (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_ganged_dir_invert (setting_id_t id, uint_fast16_t int_value);
-static status_code_t set_stepper_deenergize_mask (setting_id_t id, uint_fast16_t int_value);
+static status_code_t set_stepper_energize_mask (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_report_interval (setting_id_t setting, uint_fast16_t int_value);
static status_code_t set_estop_unlock (setting_id_t id, uint_fast16_t int_value);
#if COMPATIBILITY_LEVEL <= 1
@@ -430,7 +435,8 @@ static status_code_t set_linear_piece (setting_id_t id, char *svalue);
static char *get_linear_piece (setting_id_t id);
#endif
#if N_AXIS > 3
-static status_code_t set_rotational_axes (setting_id_t id, uint_fast16_t int_value);
+static status_code_t set_rotary_axes (setting_id_t id, uint_fast16_t int_value);
+static status_code_t set_rotary_wrap_axes (setting_id_t id, uint_fast16_t int_value);
#endif
#if COMPATIBILITY_LEVEL > 2
static status_code_t set_enable_invert_mask (setting_id_t id, uint_fast16_t int_value);
@@ -458,6 +464,28 @@ static char control_signals[] = "Reset,Feed hold,Cycle start,Safety door,Block d
static char spindle_signals[] = "Spindle enable,Spindle direction,PWM";
static char coolant_signals[] = "Flood,Mist";
static char ganged_axes[] = "X-Axis,Y-Axis,Z-Axis";
+#if !AXIS_REMAP_ABC2UVW
+ #if N_AXIS == 4
+ static const char rotary_axes[] = "A-Axis";
+ #elif N_AXIS == 5
+ static const char rotary_axes[] = "A-Axis,B-Axis";
+ #elif N_AXIS == 6
+ static const char rotary_axes[] = "A-Axis,B-Axis,C-Axis";
+ #elif N_AXIS == 7
+ static const char rotary_axes[] = "A-Axis,B-Axis,C-Axis,U-Axis";
+ #elif N_AXIS == 8
+ static const char rotary_axes[] = "A-Axis,B-Axis,C-Axis,U-Axis,V-Axis";
+ #endif
+#else
+ #if N_AXIS == 4
+ static const char rotary_axes[] = "U-Axis";
+ #elif N_AXIS == 5
+ static const char rotary_axes[] = "U-Axis,V-Axis";
+ #elif N_AXIS == 6
+ static const char rotary_axes[] = "U-Axis,V-Axis,W-Axis";
+ #endif
+#endif
+
static char fs_options[] = "Auto mount SD card,Hide LittleFS";
static char spindle_types[100] = "";
static char axis_dist[4] = "mm";
@@ -493,7 +521,7 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_GangedDirInvertMask, Group_Stepper, "Ganged axes direction invert", NULL, Format_Bitfield, ganged_axes, NULL, NULL, Setting_IsExtendedFn, set_ganged_dir_invert, get_int, is_setting_available },
{ Setting_SpindlePWMOptions, Group_Spindle, "PWM Spindle", NULL, Format_XBitfield, "Enable,RPM controls spindle enable signal,Disable laser mode capability", NULL, NULL, Setting_IsExtendedFn, set_pwm_options, get_int, is_setting_available },
#if COMPATIBILITY_LEVEL <= 1
- { Setting_StatusReportMask, Group_General, "Status report options", NULL, Format_Bitfield, "Position in machine coordinate,Buffer state,Line numbers,Feed & speed,Pin state,Work coordinate offset,Overrides,Probe coordinates,Buffer sync on WCO change,Parser state,Alarm substatus,Run substatus", NULL, NULL, Setting_IsExtendedFn, set_report_mask, get_int, NULL },
+ { Setting_StatusReportMask, Group_General, "Status report options", NULL, Format_Bitfield, "Position in machine coordinate,Buffer state,Line numbers,Feed & speed,Pin state,Work coordinate offset,Overrides,Probe coordinates,Buffer sync on WCO change,Parser state,Alarm substatus,Run substatus,Enable when homing", NULL, NULL, Setting_IsExtendedFn, set_report_mask, get_int, NULL },
#else
{ Setting_StatusReportMask, Group_General, "Status report options", NULL, Format_Bitfield, "Position in machine coordinate,Buffer state", NULL, NULL, Setting_IsLegacyFn, set_report_mask, get_int, NULL },
#endif
@@ -537,7 +565,7 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_PWMOffValue, Group_Spindle, "Spindle PWM off value", "percent", Format_Decimal, "##0.0", NULL, "100", Setting_IsExtended, &settings.spindle.pwm_off_value, NULL, is_setting_available },
{ Setting_PWMMinValue, Group_Spindle, "Spindle PWM min value", "percent", Format_Decimal, "##0.0", NULL, "100", Setting_IsExtended, &settings.spindle.pwm_min_value, NULL, is_setting_available },
{ Setting_PWMMaxValue, Group_Spindle, "Spindle PWM max value", "percent", Format_Decimal, "##0.0", NULL, "100", Setting_IsExtended, &settings.spindle.pwm_max_value, NULL, is_setting_available },
- { Setting_StepperDeenergizeMask, Group_Stepper, "Steppers deenergize", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtendedFn, set_stepper_deenergize_mask, get_int, NULL },
+ { Setting_SteppersEnergize, Group_Stepper, "Steppers to keep enabled", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtendedFn, set_stepper_energize_mask, get_int, NULL },
{ Setting_SpindlePPR, Group_Spindle, "Spindle pulses per revolution (PPR)", NULL, Format_Int16, "###0", NULL, NULL, Setting_IsExtended, &settings.spindle.ppr, NULL, is_setting_available, { .reboot_required = On } },
{ Setting_EnableLegacyRTCommands, Group_General, "Enable legacy RT commands", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_enable_legacy_rt_commands, get_int, NULL },
{ Setting_JogSoftLimited, Group_Jogging, "Limit jog commands", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_jog_soft_limited, get_int, NULL },
@@ -599,7 +627,7 @@ PROGMEM static const setting_detail_t setting_detail[] = {
#endif
{ Setting_AxisMaxTravel, Group_Axis0, "-axis maximum travel", axis_dist, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
#if ENABLE_BACKLASH_COMPENSATION
- { Setting_AxisBacklash, Group_Axis0, "-axis backlash compensation", axis_dist, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtendedFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
+ { Setting_AxisBacklash, Group_Axis0, "-axis backlash compensation", axis_dist, Format_Decimal, "#####0.000##", NULL, NULL, Setting_IsExtendedFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
#endif
{ Setting_AxisAutoSquareOffset, Group_Axis0, "-axis dual axis offset", "mm", Format_Decimal, "-0.000", "-10", "10", Setting_IsExtendedFn, 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_IsExtended, &settings.spindle.at_speed_tolerance, NULL, is_setting_available },
@@ -615,26 +643,8 @@ PROGMEM static const setting_detail_t setting_detail[] = {
#if COMPATIBILITY_LEVEL <= 1
{ Setting_DisableG92Persistence, Group_General, "Disable G92 persistence", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_g92_disable_persistence, get_int, NULL },
#endif
-#if !AXIS_REMAP_ABC2UVW
- #if N_AXIS == 4
- { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_Bitfield, "A-Axis", NULL, NULL, Setting_IsExtendedFn, set_rotational_axes, get_int, NULL },
- #elif N_AXIS == 5
- { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_Bitfield, "A-Axis,B-Axis", NULL, NULL, Setting_IsExtendedFn, set_rotational_axes, get_int, NULL },
- #elif N_AXIS == 6
- { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_Bitfield, "A-Axis,B-Axis,C-Axis", NULL, NULL, Setting_IsExtendedFn, set_rotational_axes, get_int, NULL },
- #elif N_AXIS == 7
- { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_Bitfield, "A-Axis,B-Axis,C-Axis,U-Axis", NULL, NULL, Setting_IsExtendedFn, set_rotational_axes, get_int, NULL },
- #elif N_AXIS == 8
- { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_Bitfield, "A-Axis,B-Axis,C-Axis,U-Axis,V-Axis", NULL, NULL, Setting_IsExtendedFn, set_rotational_axes, get_int, NULL },
- #endif
-#else
- #if N_AXIS == 4
- { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_Bitfield, "U-Axis", NULL, NULL, Setting_IsExtendedFn, set_rotational_axes, get_int, NULL },
- #elif N_AXIS == 5
- { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_Bitfield, "U-Axis,V-Axis", NULL, NULL, Setting_IsExtendedFn, set_rotational_axes, get_int, NULL },
- #elif N_AXIS == 6
- { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_Bitfield, "U-Axis,V-Axis,W-Axis", NULL, NULL, Setting_IsExtendedFn, set_rotational_axes, get_int, NULL },
- #endif
+#if N_AXIS > 3
+ { Settings_RotaryAxes, Group_Stepper, "Rotary axes", NULL, Format_Bitfield, rotary_axes, NULL, NULL, Setting_IsExtendedFn, set_rotary_axes, get_int, NULL },
#endif
#ifndef NO_SAFETY_DOOR_SUPPORT
{ Setting_DoorSpindleOnDelay, Group_SafetyDoor, "Spindle on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtended, &settings.safety_door.spindle_on_delay, NULL, is_setting_available },
@@ -653,6 +663,9 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_OffsetLock, Group_General, "Lock coordinate systems", NULL, Format_Bitfield, "G59.1,G59.2,G59.3", NULL, NULL, Setting_IsExtendedFn, set_offset_lock, get_int, NULL },
#endif
{ Setting_EncoderSpindle, Group_Spindle, "Encoder spindle", NULL, Format_RadioButtons, spindle_types, NULL, NULL, Setting_IsExtendedFn, set_encoder_spindle, get_int, is_setting_available },
+#if N_AXIS > 3
+ { Setting_RotaryWrap, Group_Stepper, "Fast rotary go to G28", NULL, Format_Bitfield, rotary_axes, NULL, NULL, Setting_IsExtendedFn, set_rotary_wrap_axes, get_int, NULL },
+#endif
{ Setting_FSOptions, Group_General, "File systems options", NULL, Format_Bitfield, fs_options, NULL, NULL, Setting_IsExtended, &settings.fs_options.mask, NULL, is_setting_available },
{ Setting_HomePinsInvertMask, Group_Limits, "Invert home pins", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.home_invert.mask, NULL, is_setting_available },
{ Setting_HoldCoolantOnDelay, Group_Coolant, "Coolant on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtended, &settings.safety_door.coolant_on_delay, NULL, is_setting_available }
@@ -682,7 +695,7 @@ PROGMEM static const setting_descr_t setting_descr[] = {
{ Setting_GangedDirInvertMask, "Inverts the direction signals for the second motor used for ganged axes.\\n\\n"
"NOTE: This inversion will be applied in addition to the inversion from setting $3."
},
- { Setting_StatusReportMask, "Specifies optional data included in status reports.\\n"
+ { Setting_StatusReportMask, "Specifies optional data included in status reports and if report is sent when homing.\\n"
"If Run substatus is enabled it may be used for simple probe protection.\\n\\n"
"NOTE: Parser state will be sent separately after the status report and only on changes."
},
@@ -727,7 +740,7 @@ PROGMEM static const setting_descr_t setting_descr[] = {
{ Setting_PWMOffValue, "Spindle PWM off value in percent (duty cycle)." },
{ Setting_PWMMinValue, "Spindle PWM min value in percent (duty cycle)." },
{ Setting_PWMMaxValue, "Spindle PWM max value in percent (duty cycle)." },
- { Setting_StepperDeenergizeMask, "Specifies which steppers not to disable when stopped." },
+ { Setting_SteppersEnergize, "Specifies which steppers not to disable when stopped." },
{ Setting_SpindlePPR, "Spindle encoder pulses per revolution." },
{ Setting_EnableLegacyRTCommands, "Enables \"normal\" processing of ?, ! and ~ characters when part of $-setting or comment. If disabled then they are added to the input string instead." },
{ Setting_JogSoftLimited, "Limit jog commands to machine limits for homed axes." },
@@ -821,9 +834,12 @@ PROGMEM static const setting_descr_t setting_descr[] = {
#if COMPATIBILITY_LEVEL <= 1
{ Setting_DisableG92Persistence, "Disables save/restore of G92 offset to non-volatile storage (NVS)." },
#endif
+#if N_AXIS > 3
+ { Settings_RotaryAxes, "Designates axes as rotary, interpretation some other relevant axis settings is changed accordingly." },
+#endif
#ifndef NO_SAFETY_DOOR_SUPPORT
- { Setting_DoorSpindleOnDelay, "Delay to allow spindle to spin up after safety door is opened or feed hold is canceled.." },
- { Setting_DoorCoolantOnDelay, "Delay to allow coolant to restart after safety door is opened or feed hold is canceled.." },
+ { Setting_DoorSpindleOnDelay, "Delay to allow spindle to spin up after safety door is opened or feed hold is canceled." },
+ { Setting_DoorCoolantOnDelay, "Delay to allow coolant to restart after safety door is opened or feed hold is canceled." },
#else
{ Setting_DoorSpindleOnDelay, "Delay to allow spindle to spin up when spindle at speed tolerance is > 0." },
#endif
@@ -840,6 +856,13 @@ PROGMEM static const setting_descr_t setting_descr[] = {
{ Setting_NGCDebugOut, "Example: (debug, metric mode: #<_metric>, coord system: #5220)" },
#endif
{ Setting_EncoderSpindle, "Specifies which spindle has the encoder attached." },
+#if N_AXIS > 3
+ { Setting_RotaryWrap, "Perform fast move to angle stored in G28 position.\\n"
+ "Use:\\n"
+ " G91G280\\n"
+ " G90\\n"
+ },
+#endif
{ Setting_FSOptions, "Auto mount SD card on startup." },
{ Setting_HomePinsInvertMask, "Inverts the axis home input signals." },
{ Setting_HoldCoolantOnDelay, "Delay to allow coolant to restart after feed hold is canceled." }
@@ -986,11 +1009,11 @@ static status_code_t set_ganged_dir_invert (setting_id_t id, uint_fast16_t int_v
return Status_OK;
}
-static status_code_t set_stepper_deenergize_mask (setting_id_t id, uint_fast16_t int_value)
+static status_code_t set_stepper_energize_mask (setting_id_t id, uint_fast16_t int_value)
{
- settings.steppers.deenergize.mask = int_value;
+ settings.steppers.energize.mask = int_value;
- hal.stepper.enable(settings.steppers.deenergize);
+ hal.stepper.enable(settings.steppers.energize, true);
return Status_OK;
}
@@ -1181,7 +1204,7 @@ static inline void tmp_set_hard_limits (void)
sys.hard_limits.mask = settings.limits.flags.hard_enabled ? AXES_BITMASK : 0;
#if N_AXIS > 3
if(settings.limits.flags.hard_disabled_rotary)
- sys.hard_limits.mask &= ~settings.steppers.is_rotational.mask;
+ sys.hard_limits.mask &= ~settings.steppers.is_rotary.mask;
#endif
}
@@ -1393,16 +1416,23 @@ static inline void set_axis_unit (const setting_detail_t *setting, const char *u
#if N_AXIS > 3
-static status_code_t set_rotational_axes (setting_id_t id, uint_fast16_t int_value)
+static status_code_t set_rotary_axes (setting_id_t id, uint_fast16_t int_value)
{
- settings.steppers.is_rotational.mask = (int_value << 3) & AXES_BITMASK;
+ settings.steppers.is_rotary.mask = (int_value << 3) & AXES_BITMASK;
+
+ return Status_OK;
+}
+
+static status_code_t set_rotary_wrap_axes (setting_id_t id, uint_fast16_t int_value)
+{
+ settings.steppers.rotary_wrap.mask = (int_value << 3) & AXES_BITMASK;
return Status_OK;
}
static inline bool axis_is_rotary (uint_fast8_t axis_idx)
{
- return bit_istrue(settings.steppers.is_rotational.mask, bit(axis_idx));
+ return bit_istrue(settings.steppers.is_rotary.mask, bit(axis_idx));
}
static const char *set_axis_setting_unit (setting_id_t setting_id, uint_fast8_t axis_idx)
@@ -1493,6 +1523,13 @@ inline static setting_id_t normalize_id (setting_id_t id)
id = (setting_id_t)(Setting_EncoderSettingsBase + (id % ENCODER_SETTINGS_INCREMENT));
else if(id > Setting_ModbusTCPBase && id <= Setting_ModbusTCPMax)
id = (setting_id_t)(Setting_ModbusTCPBase + (id % MODBUS_TCP_SETTINGS_INCREMENT));
+ else if((id > Setting_Macro0 && id <= Setting_Macro9) ||
+ (id > Setting_MacroPort0 && id <= Setting_MacroPort9) ||
+ (id > Setting_ButtonAction0 && id <= Setting_ButtonAction9) ||
+ (id > Setting_Action0 && id <= Setting_Action9) ||
+ (id > Setting_ActionPort0 && id <= Setting_ActionPort9) ||
+ (id > Setting_SpindleToolStart0 && id <= Setting_SpindleToolStart7))
+ id = (setting_id_t)(id - (id % 10));
return id;
}
@@ -1744,8 +1781,8 @@ static uint32_t get_int (setting_id_t id)
#endif
break;
- case Setting_StepperDeenergizeMask:
- value = settings.steppers.deenergize.mask;
+ case Setting_SteppersEnergize:
+ value = settings.steppers.energize.mask;
break;
case Setting_EnableLegacyRTCommands:
@@ -1806,8 +1843,12 @@ static uint32_t get_int (setting_id_t id)
break;
#if N_AXIS > 3
- case Settings_Axis_Rotational:
- value = (settings.steppers.is_rotational.mask & AXES_BITMASK) >> 3;
+ case Settings_RotaryAxes:
+ value = (settings.steppers.is_rotary.mask & AXES_BITMASK) >> 3;
+ break;
+
+ case Setting_RotaryWrap:
+ value = (settings.steppers.rotary_wrap.mask & AXES_BITMASK) >> 3;
break;
#endif
@@ -2260,6 +2301,7 @@ bool read_global_settings ()
bool ok = hal.nvs.type != NVS_None && SETTINGS_VERSION == hal.nvs.get_byte(0) && hal.nvs.memcpy_from_nvs((uint8_t *)&settings, NVS_ADDR_GLOBAL, sizeof(settings_t), true) == NVS_TransferResult_OK;
// Sanity check of settings, board map could have been changed...
+
#if LATHE_UVW_OPTION
settings.mode = Mode_Lathe;
#else
@@ -2285,6 +2327,10 @@ bool read_global_settings ()
settings.steppers.enable_invert.mask = AXES_BITMASK;
#endif
+#if N_AXIS > 3
+ settings.steppers.rotary_wrap.mask &= settings.steppers.is_rotary.mask;
+#endif
+
settings.control_invert.mask |= limits_override.mask;
settings.control_disable_pullup.mask &= ~limits_override.mask;
@@ -2304,6 +2350,15 @@ void settings_write_global (void)
hal.nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)&settings, sizeof(settings_t), true);
}
+#if N_SPINDLE > 1
+
+static void get_default_spindle (spindle_info_t *spindle, void *data)
+{
+ if(spindle->ref_id == (uint8_t)((uint32_t)data))
+ settings.spindle.flags.type = spindle->id;
+}
+
+#endif
// Restore global settings to defaults and write to persistent storage
void settings_restore (settings_restore_t restore)
@@ -2316,7 +2371,7 @@ void settings_restore (settings_restore_t restore)
hal.nvs.put_byte(0, SETTINGS_VERSION); // Forces write to physical storage
- if (restore.defaults) {
+ if(restore.defaults) {
memcpy(&settings, &defaults, sizeof(settings_t));
@@ -2327,6 +2382,9 @@ void settings_restore (settings_restore_t restore)
#if ENABLE_BACKLASH_COMPENSATION
if(sys.driver_started)
mc_backlash_init((axes_signals_t){AXES_BITMASK});
+#endif
+#if N_SPINDLE > 1
+ spindle_enumerate_spindles(get_default_spindle, (void *)DEFAULT_SPINDLE);
#endif
settings_write_global();
}
@@ -2355,11 +2413,16 @@ void settings_restore (settings_restore_t restore)
settings_write_build_info(BUILD_INFO);
}
+ if(restore.defaults && hal.settings_changed)
+ hal.settings_changed(&settings, (settings_changed_flags_t){-1});
+
setting_details_t *details = setting_details.next;
if(details) do {
if(details->restore)
details->restore();
+ if(details->on_changed)
+ details->on_changed(&settings, restore.defaults ? (settings_changed_flags_t){-1} : (settings_changed_flags_t){0});
} while((details = details->next));
nvs_buffer_sync_physical();
diff --git a/settings.h b/settings.h
index f73870b..d353cb9 100644
--- a/settings.h
+++ b/settings.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 _SETTINGS_H_
@@ -81,7 +81,7 @@ typedef enum {
Setting_PWMOffValue = 34,
Setting_PWMMinValue = 35,
Setting_PWMMaxValue = 36,
- Setting_StepperDeenergizeMask = 37,
+ Setting_SteppersEnergize = 37,
Setting_SpindlePPR = 38,
Setting_EnableLegacyRTCommands = 39,
Setting_JogSoftLimited = 40,
@@ -247,7 +247,7 @@ typedef enum {
Settings_IoPort_OD_Enable = 373,
Settings_ModBus_BaudRate = 374,
Settings_ModBus_RXTimeout = 375,
- Settings_Axis_Rotational = 376,
+ Settings_RotaryAxes = 376,
Setting_BlueToothInitOK = 377,
Setting_CoolantOnDelay = 378,
Setting_CoolantOffDelay = 379,
@@ -318,27 +318,29 @@ typedef enum {
Setting_Spindle_DirPort = 488,
Setting_Spindle_PWMPort = 489,
- Setting_Macro0 = 490,
- Setting_Macro1 = 491,
- Setting_Macro2 = 492,
- Setting_Macro3 = 493,
- Setting_Macro4 = 494,
- Setting_Macro5 = 495,
- Setting_Macro6 = 496,
- Setting_Macro7 = 497,
- Setting_Macro8 = 498,
- Setting_Macro9 = 499,
+ Setting_Macro0 = 490,
+ Setting_MacroBase = Setting_Macro0,
+ Setting_Macro1 = 491,
+ Setting_Macro2 = 492,
+ Setting_Macro3 = 493,
+ Setting_Macro4 = 494,
+ Setting_Macro5 = 495,
+ Setting_Macro6 = 496,
+ Setting_Macro7 = 497,
+ Setting_Macro8 = 498,
+ Setting_Macro9 = 499,
- Setting_MacroPort0 = 500,
- Setting_MacroPort1 = 501,
- Setting_MacroPort2 = 502,
- Setting_MacroPort3 = 503,
- Setting_MacroPort4 = 504,
- Setting_MacroPort5 = 505,
- Setting_MacroPort6 = 506,
- Setting_MacroPort7 = 507,
- Setting_MacroPort8 = 508,
- Setting_MacroPort9 = 509,
+ Setting_MacroPort0 = 500,
+ Setting_MacroPortBase = Setting_MacroPort0,
+ Setting_MacroPort1 = 501,
+ Setting_MacroPort2 = 502,
+ Setting_MacroPort3 = 503,
+ Setting_MacroPort4 = 504,
+ Setting_MacroPort5 = 505,
+ Setting_MacroPort6 = 506,
+ Setting_MacroPort7 = 507,
+ Setting_MacroPort8 = 508,
+ Setting_MacroPort9 = 509,
Setting_SpindleEnable0 = 510,
Setting_SpindleEnable1 = 511,
@@ -368,6 +370,7 @@ typedef enum {
Setting_NetworkMAC = 535,
Setting_RGB_StripLengt0 = 536,
Setting_RGB_StripLengt1 = 537,
+ Setting_RotaryWrap = 538,
Setting_Panel_SpindleSpeed = 540, // NOTE: Reserving settings values 540 to 579 for panel settings.
Setting_Panel_ModbusAddress = 541,
@@ -391,6 +394,18 @@ typedef enum {
Setting_Panel_Encoder3_Cpd = 559,
Setting_Panel_SettingsMax = 579,
+ Setting_ButtonAction0 = 590,
+ Setting_ButtonActionBase = Setting_ButtonAction0,
+ Setting_ButtonAction1 = 591,
+ Setting_ButtonAction2 = 592,
+ Setting_ButtonAction3 = 593,
+ Setting_ButtonAction4 = 594,
+ Setting_ButtonAction5 = 595,
+ Setting_ButtonAction6 = 596,
+ Setting_ButtonAction7 = 597,
+ Setting_ButtonAction8 = 598,
+ Setting_ButtonAction9 = 599,
+
Setting_ModbusTCPBase = 600, // Reserving settings values 600 to 639 for ModBus TCP (8 sets)
Setting_ModbusIpAddressBase = Setting_ModbusTCPBase + Setting_ModbusIpAddress,
Setting_ModbusPortBase = Setting_ModbusTCPBase + Setting_ModbusPort,
@@ -444,17 +459,53 @@ typedef enum {
Setting_PWMOffValue1 = 734,
Setting_PWMMinValue1 = 735,
Setting_PWMMaxValue1 = 736,
+
// Optional driver implemented settings for piecewise linear spindle PWM algorithm
Setting_LinearSpindle1Piece1 = 737,
Setting_LinearSpindle1Piece2 = 738,
Setting_LinearSpindle1Piece3 = 739,
Setting_LinearSpindle1Piece4 = 740,
+
+ Setting_Action0 = 750,
+ Setting_ActionBase = Setting_Action0,
+ Setting_Action1 = 751,
+ Setting_Action2 = 752,
+ Setting_Action3 = 753,
+ Setting_Action4 = 754,
+ Setting_Action5 = 755,
+ Setting_Action6 = 756,
+ Setting_Action7 = 757,
+ Setting_Action8 = 758,
+ Setting_Action9 = 759,
+
+ Setting_ActionPort0 = 760,
+ Setting_ActionPortBase = Setting_ActionPort0,
+ Setting_ActionPort1 = 761,
+ Setting_ActionPort2 = 762,
+ Setting_ActionPort3 = 763,
+ Setting_ActionPort4 = 764,
+ Setting_ActionPort5 = 765,
+ Setting_ActionPort6 = 766,
+ Setting_ActionPort7 = 767,
+ Setting_ActionPort8 = 768,
+ Setting_ActionPort9 = 769,
+
+ Setting_SpindleOffsetX = 770,
+ Setting_SpindleOffsetY = 771,
+//
+// 772-779 - reserved for spindle offset settings
+//
+
//
// 900-999 - reserved for automatic tool changers (ATC)
//
+
+// ---
Setting_SettingsMax,
Setting_SettingsAll = Setting_SettingsMax,
+// ---
+
// Calculated base values for core stepper settings
Setting_AxisStepsPerMM = Setting_AxisSettingsBase,
Setting_AxisMaxRate = Setting_AxisSettingsBase + AXIS_SETTINGS_INCREMENT,
@@ -554,7 +605,8 @@ typedef union {
parser_state :1,
alarm_substate :1,
run_substate :1,
- unassigned :4;
+ when_homing :1,
+ unassigned :3;
};
} reportmask_t;
@@ -652,9 +704,10 @@ typedef struct {
axes_signals_t dir_invert;
axes_signals_t ganged_dir_invert; // applied after inversion for the master motor
axes_signals_t enable_invert;
- axes_signals_t deenergize;
+ axes_signals_t energize;
#if N_AXIS > 3
- axes_signals_t is_rotational; // rotational axes distances are not scaled in imperial mode
+ axes_signals_t is_rotary; // rotary axes distances are not scaled in imperial mode
+ axes_signals_t rotary_wrap; // rotary axes that allows G28 wrap for faster move to home position
#endif
float pulse_microseconds;
float pulse_delay_microseconds;
@@ -772,7 +825,8 @@ typedef struct {
control_signals_t control_disable_pullup;
coolant_state_t coolant_invert;
axes_signals_t home_invert;
- uint16_t hole_1;
+ uint8_t modbus_baud;
+ uint8_t canbus_baud;
spindle_settings_t spindle;
stepper_settings_t steppers;
reportmask_t status_report; // Mask to indicate desired report data.
diff --git a/spindle_control.c b/spindle_control.c
index 769c4f1..e6e59d9 100644
--- a/spindle_control.c
+++ b/spindle_control.c
@@ -51,6 +51,7 @@ typedef struct {
static uint8_t n_spindle = 0;
static spindle_sys_t sys_spindle[N_SYS_SPINDLE] = {0};
static spindle_reg_t spindles[N_SPINDLE] = {0}, *pwm_spindle = NULL;
+static const spindle_data_ptrs_t *encoder;
/*! \internal \brief Activates and registers a spindle as enabled with a specific spindle number.
\param spindle_id spindle id of spindle to activate as a \ref spindle_id_t.
@@ -96,20 +97,6 @@ static bool spindle_activate (spindle_id_t spindle_id, spindle_num_t spindle_num
memcpy(&spindle_hal, &spindle->hal, sizeof(spindle_ptrs_t));
- if(spindle->cfg->get_data == NULL) {
- if(settings.offset_lock.encoder_spindle == spindle_id) {
- spindle_hal.get_data = hal.spindle_data.get;
- spindle_hal.reset_data = hal.spindle_data.reset;
- if(!spindle->cfg->cap.at_speed)
- spindle_hal.cap.at_speed = !!spindle_hal.get_data;
- } else {
- spindle_hal.get_data = NULL;
- spindle_hal.reset_data = NULL;
- if(!spindle->cfg->cap.at_speed)
- spindle_hal.cap.at_speed = Off;
- }
- }
-
spindle_hal.cap.laser &= settings.mode == Mode_Laser;
if(grbl.on_spindle_select)
@@ -305,22 +292,68 @@ uint8_t spindle_get_count (void)
static spindle_num_t spindle_get_num (spindle_id_t spindle_id)
{
- uint_fast8_t idx = N_SPINDLE_SELECTABLE;
- spindle_num_t spindle_num = -1;
+ spindle_num_t spindle_num;
- const setting_detail_t *setting;
+ if((spindle_num = spindle_get_count() == 1 ? 0 : -1) == -1) {
- do {
- idx--;
- if((setting = setting_get_details(idx == 0 ? Setting_SpindleType : (setting_id_t)(Setting_SpindleEnable0 + idx), NULL))) {
- if(setting_get_int_value(setting, 0) == spindle_id)
- spindle_num = idx;
- }
- } while(idx && spindle_num == -1);
+ const setting_detail_t *setting;
+ uint_fast8_t idx = N_SPINDLE_SELECTABLE;
+
+ do {
+ idx--;
+ if((setting = setting_get_details(idx == 0 ? Setting_SpindleType : (setting_id_t)(Setting_SpindleEnable0 + idx), NULL))) {
+ if(setting_get_int_value(setting, 0) == spindle_id)
+ spindle_num = idx;
+ }
+ } while(idx && spindle_num == -1);
+ }
return spindle_num;
}
+void spindle_bind_encoder (const spindle_data_ptrs_t *encoder_data)
+{
+ uint_fast8_t idx;
+ spindle_ptrs_t *spindle;
+ spindle_num_t spindle_num;
+
+ encoder = encoder_data;
+
+ for(idx = 0; idx < n_spindle; idx++) {
+
+ spindle = spindle_get((spindle_num = spindle_get_num(idx)));
+
+ if(encoder_data && spindle_num == settings.offset_lock.encoder_spindle) {
+ spindles[idx].hal.get_data = encoder_data->get;
+ spindles[idx].hal.reset_data = encoder_data->reset;
+ spindles[idx].hal.cap.at_speed = spindles[idx].hal.cap.variable;
+ } else {
+ spindles[idx].hal.get_data = spindles[idx].cfg->get_data;
+ spindles[idx].hal.reset_data = spindles[idx].cfg->reset_data;
+ spindles[idx].hal.cap.at_speed = spindles[idx].cfg->cap.at_speed;
+ }
+
+ if(spindle) {
+ spindle->get_data = spindles[idx].hal.get_data;
+ spindle->reset_data = spindles[idx].hal.reset_data;
+ spindle->cap.at_speed = spindles[idx].hal.cap.at_speed;
+ }
+ }
+}
+
+bool spindle_set_at_speed_range (spindle_ptrs_t *spindle, spindle_data_t *spindle_data, float rpm)
+{
+ spindle_data->rpm_programmed = rpm;
+ spindle_data->state_programmed.at_speed = false;
+
+ if((spindle_data->at_speed_enabled = spindle->at_speed_tolerance > 0.0f)) {
+ spindle_data->rpm_low_limit = rpm * (1.0f - (spindle->at_speed_tolerance / 100.0f));
+ spindle_data->rpm_high_limit = rpm * (1.0f + (spindle->at_speed_tolerance / 100.0f));
+ }
+
+ return spindle_data->at_speed_enabled;
+}
+
/*! \brief Enumerate registered spindles by calling a callback function for each of them.
\param callback pointer to a \ref spindle_enumerate_callback_ptr type function.
\param data pointer to optional data to pass to the callback function.
@@ -337,6 +370,7 @@ bool spindle_enumerate_spindles (spindle_enumerate_callback_ptr callback, void *
for(idx = 0; idx < n_spindle; idx++) {
spindle.id = idx;
+ spindle.ref_id = spindles[idx].cfg->ref_id;
spindle.name = spindles[idx].name;
spindle.num = spindle_get_num(idx);
spindle.enabled = spindle.num != -1;
@@ -555,7 +589,7 @@ bool spindle_sync (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
if (!(ok = state_get() == STATE_CHECK_MODE)) {
- bool at_speed = !state.on || !spindle->cap.at_speed || settings.spindle.at_speed_tolerance <= 0.0f;
+ bool at_speed = !state.on || !spindle->cap.at_speed || spindle->at_speed_tolerance <= 0.0f;
// Empty planner buffer to ensure spindle is set when programmed.
if((ok = protocol_buffer_synchronize()) && set_state(spindle, state, rpm) && !at_speed) {
@@ -595,7 +629,7 @@ bool spindle_restore (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
if(state.on) {
if((ok = !spindle->cap.at_speed))
ok = delay_sec(settings.safety_door.spindle_on_delay, DelayMode_SysSuspend);
- else if((ok == (settings.spindle.at_speed_tolerance <= 0.0f))) {
+ else if((ok == (spindle->at_speed_tolerance <= 0.0f))) {
float delay = 0.0f;
while(!(ok = spindle->get_state(spindle).at_speed)) {
if(!(ok = delay_sec(0.1f, DelayMode_SysSuspend)))
@@ -624,13 +658,7 @@ float spindle_set_rpm (spindle_ptrs_t *spindle, float rpm, override_t override_p
if(override_pct != 100)
rpm *= 0.01f * (float)override_pct; // Scale RPM by override value.
- // Apply RPM limits
- if (rpm <= 0.0f) // TODO: remove this test?
- rpm = 0.0f;
- else if (rpm > spindle->rpm_max)
- rpm = spindle->rpm_max;
- else if (rpm < spindle->rpm_min)
- rpm = spindle->rpm_min;
+ rpm = rpm <= 0.0f ? 0.0f : constrain(rpm, spindle->rpm_min, spindle->rpm_max);
spindle->param->rpm_overridden = rpm;
spindle->param->override_pct = override_pct;
@@ -756,6 +784,7 @@ bool spindle_precompute_pwm_values (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_
pwm_data->settings = settings;
spindle->rpm_min = pwm_data->rpm_min = settings->rpm_min;
spindle->rpm_max = settings->rpm_max;
+ spindle->at_speed_tolerance = settings->at_speed_tolerance;
spindle->cap.rpm_range_locked = On;
if((spindle->cap.variable = !settings->flags.pwm_disable && spindle->rpm_max > spindle->rpm_min)) {
diff --git a/spindle_control.h b/spindle_control.h
index 889a9e8..d3e2c5f 100644
--- a/spindle_control.h
+++ b/spindle_control.h
@@ -26,7 +26,6 @@
#include "pid.h"
-#define SPINDLE_ALL -1
#define SPINDLE_NONE 0
#define SPINDLE_HUANYANG1 1
#define SPINDLE_HUANYANG2 2
@@ -50,6 +49,9 @@
#define SPINDLE_NOWFOREVER 20
#define SPINDLE_MY_SPINDLE 30
+#define SPINDLE_ALL_VFD ((1<= d.rpm_low_limit && d.rpm <= d.rpm_high_limit); }
+/*
+__attribute__((always_inline)) static inline void spindle_validate_at_speed (spindle_data_t *spindle_data, float rpm)
+{
+ spindle_data->rpm = rpm;
+ spindle_data->state_programmed.at_speed = !spindle_data->at_speed_enabled || (spindle_data->rpm >= spindle_data->rpm_low_limit && spindle_data->rpm <= spindle_data->rpm_high_limit);
+}
+*/
+
bool spindle_precompute_pwm_values (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_data, spindle_settings_t *settings, uint32_t clock_hz);
spindle_id_t spindle_register (const spindle_ptrs_t *spindle, const char *name);
@@ -364,6 +379,9 @@ spindle_cap_t spindle_get_caps (bool active);
void spindle_update_caps (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_caps);
+void spindle_bind_encoder (const spindle_data_ptrs_t *encoder_data);
+
+bool spindle_set_at_speed_range (spindle_ptrs_t *spindle, spindle_data_t *spindle_data, float rpm);
spindle_ptrs_t *spindle_get_hal (spindle_id_t spindle_id, spindle_hal_t hal);
diff --git a/stepper.c b/stepper.c
index 6d27267..aff75bf 100644
--- a/stepper.c
+++ b/stepper.c
@@ -183,7 +183,7 @@ extern void gc_output_message (char *message);
void st_deenergize (void *data)
{
if(sys.steppers_deenergize) {
- hal.stepper.enable(settings.steppers.deenergize);
+ hal.stepper.enable(settings.steppers.energize, true);
sys.steppers_deenergize = false;
}
}
@@ -211,7 +211,7 @@ ISR_CODE void ISR_FUNC(st_go_idle)(void)
// Set stepper driver idle state, disabled or enabled, depending on settings and circumstances.
if(((settings.steppers.idle_lock_time != 255) || sys.rt_exec_alarm || state == STATE_SLEEP) && state != STATE_HOMING) {
if(settings.steppers.idle_lock_time == 0 || state == STATE_SLEEP)
- hal.stepper.enable((axes_signals_t){0});
+ hal.stepper.enable((axes_signals_t){0}, true);
else {
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
// stop and not drift from residual inertial forces at the end of the last movement.
@@ -219,7 +219,7 @@ ISR_CODE void ISR_FUNC(st_go_idle)(void)
sys.steppers_deenergize = task_add_delayed(st_deenergize, NULL, settings.steppers.idle_lock_time);
}
} else
- hal.stepper.enable(settings.steppers.idle_lock_time == 255 ? (axes_signals_t){AXES_BITMASK} : settings.steppers.deenergize);
+ hal.stepper.enable(settings.steppers.idle_lock_time == 255 ? (axes_signals_t){AXES_BITMASK} : settings.steppers.energize, true);
}
@@ -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;
@@ -1133,3 +1138,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/stepper2.c b/stepper2.c
index 894b60b..57bfa5d 100644
--- a/stepper2.c
+++ b/stepper2.c
@@ -47,6 +47,7 @@ struct st2_motor {
axes_signals_t axis;
bool is_spindle;
bool is_bound;
+ bool polling;
bool position_lost;
volatile int64_t position; // absolute step number
position_t ptype; //
@@ -66,6 +67,8 @@ struct st2_motor {
float acceleration; // acceleration steps/s^2
axes_signals_t dir; // current direction
uint64_t next_step;
+ hal_timer_t step_inject_timer;
+ foreground_task_ptr on_stopped;
st2_motor_t *next;
};
@@ -76,6 +79,8 @@ static on_set_axis_setting_unit_ptr on_set_axis_setting_unit;
static on_setting_get_description_ptr on_setting_get_description;
static on_reset_ptr on_reset;
+static void motor_irq (void *context);
+
/*! \brief Calculate basic motor configuration.
\param motor pointer to a \a st2_motor structure.
@@ -200,6 +205,11 @@ static const char *st2_setting_get_description (setting_id_t id)
: (on_setting_get_description ? on_setting_get_description(id) : NULL);
}
+void st2_motor_register_stopped_callback (st2_motor_t *motor, foreground_task_ptr callback)
+{
+ motor->on_stopped = callback;
+}
+
/*! \brief Bind and initialize a motor.
Binds motor 0 as a spindle.
@@ -240,9 +250,23 @@ If \a is_spindle is set \a true then axis settings will be changed to step/rev e
*/
st2_motor_t *st2_motor_init (uint_fast8_t axis_idx, bool is_spindle)
{
- st2_motor_t *motor, *new = motors;
+ st2_motor_t *motor = NULL, *new = motors;
- if((motor = calloc(sizeof(st2_motor_t), 1))) {
+ if(hal.stepper.output_step && (motor = calloc(sizeof(st2_motor_t), 1))) {
+
+ if(hal.timer.claim && (motor->step_inject_timer = hal.timer.claim((timer_cap_t){ .periodic = Off }, 1000))) {
+ timer_cfg_t step_inject_cfg = {
+ .single_shot = true,
+ .timeout_callback = motor_irq
+ };
+ step_inject_cfg.context = motor;
+ hal.timer.configure(motor->step_inject_timer, &step_inject_cfg);
+ } else if(hal.get_micros)
+ motor->polling = true;
+ else {
+ free(motor);
+ return NULL;
+ }
if(!is_spindle) {
@@ -272,6 +296,15 @@ st2_motor_t *st2_motor_init (uint_fast8_t axis_idx, bool is_spindle)
return motor;
}
+/*! \brief Get current speed (RPM).
+\param motor pointer to a \a st2_motor structure.
+\returns current speed in RPM.
+*/
+float st2_get_speed (st2_motor_t *motor)
+{
+ return motor->state == State_Idle ? 0.0f : 60.0f / ((float)motor->delay * settings.axis[motor->idx].steps_per_mm / 1000000.0f);
+}
+
/*! \brief Set speed.
Change speed of a running motor. Typically used for motors bound as a spindle.
@@ -408,6 +441,9 @@ bool st2_motor_move (st2_motor_t *motor, const float move, const float speed, po
motor->step_no = 0; // step counter
motor->next_step = hal.get_micros();
+ if(motor->step_inject_timer)
+ hal.timer.start(motor->step_inject_timer, motor->delay);
+
#ifdef DEBUGOUT
uint32_t nn = motor->n;
float cn = motor->first_delay;
@@ -455,17 +491,12 @@ bool st2_set_position (st2_motor_t *motor, int64_t position)
}
/*! \brief Execute a move commanded by st2_motor_move().
-
-This should be called from the foreground process as often as possible.
\param motor pointer to a \a st2_motor structure.
\returns \a true if motor is moving (steps are output), \a false if not (motion is completed).
*/
-bool st2_motor_run (st2_motor_t *motor)
+__attribute__((always_inline)) static inline bool _motor_run (st2_motor_t *motor)
{
- uint64_t t = hal.get_micros();
-
- if(motor->state == State_Idle || t - motor->next_step < motor->delay)
- return motor->state != State_Idle;
+ st2_state_t prev_state = motor->state;
switch(motor->state) {
@@ -474,7 +505,7 @@ bool st2_motor_run (st2_motor_t *motor)
motor->denom += 4;
motor->c64 -= (motor->c64 << 1) / motor->denom; // ramp algorithm
motor->delay = (motor->c64 + 32768) >> 16; // round 24.16 format -> int16
- if (motor->delay < motor->min_delay) { // go to constant speed?
+ if (motor->delay < motor->min_delay) { // go to constant speed?
// motor->denom -= 6; // causes issues with speed override for infinite moves
motor->state = motor->ptype == Stepper2_InfiniteSteps ? State_RunInfinite : State_Run;
motor->step_down = motor->move - motor->step_no;
@@ -531,7 +562,41 @@ bool st2_motor_run (st2_motor_t *motor)
motor->position++;
motor->step_no++;
- motor->next_step = t;
+
+ if(motor->state == State_Idle && prev_state != State_Idle && motor->on_stopped)
+ task_add_delayed(motor->on_stopped, motor, 2);
+
+ return motor->state != State_Idle;
+}
+
+ISR_CODE static void ISR_FUNC(motor_irq)(void *context)
+{
+ if(_motor_run((st2_motor_t *)context))
+ hal.timer.start(((st2_motor_t *)context)->step_inject_timer, ((st2_motor_t *)context)->delay);
+ else
+ hal.timer.stop(((st2_motor_t *)context)->step_inject_timer);
+}
+
+/*! \brief Execute a move commanded by st2_motor_move().
+
+This should be called from the foreground process as often as possible
+when step output is not driven by interrupts (polling mode).
+\param motor pointer to a \a st2_motor structure.
+\returns \a true if motor is moving (steps are output), \a false if not (motion is completed).
+*/
+bool st2_motor_run (st2_motor_t *motor)
+{
+ if(motor->polling && motor->state != State_Idle) {
+
+ uint64_t t = hal.get_micros();
+
+ if(t - motor->next_step >= motor->delay) {
+
+ _motor_run(motor);
+
+ motor->next_step = t;
+ }
+ }
return motor->state != State_Idle;
}
@@ -566,6 +631,15 @@ bool st2_motor_stop (st2_motor_t *motor)
return motor->state != State_Idle;
}
+/*! \brief Check if motor is run by polling.
+\param motor pointer to a \a st2_motor structure.
+\returns \a true if motor is run by polling, \a false if not.
+*/
+bool st2_motor_poll (st2_motor_t *motor)
+{
+ return motor->polling;
+}
+
/*! \brief Check if motor is running.
\param motor pointer to a \a st2_motor structure.
\returns \a true if motor is running, \a false if not.
diff --git a/stepper2.h b/stepper2.h
index 8f3dc01..5caffa2 100644
--- a/stepper2.h
+++ b/stepper2.h
@@ -22,6 +22,8 @@
#include
#include
+#include "task.h"
+
typedef enum {
Stepper2_Steps = 0, //!< 0
Stepper2_InfiniteSteps, //!< 1
@@ -32,7 +34,9 @@ struct st2_motor; // members defined in stepper2.c
typedef struct st2_motor st2_motor_t;
st2_motor_t *st2_motor_init (uint_fast8_t axis_idx, bool is_spindle);
+bool st2_motor_poll (st2_motor_t *motor);
bool st2_motor_bind_spindle (uint_fast8_t axis_idx);
+float st2_get_speed (st2_motor_t *motor);
float st2_motor_set_speed (st2_motor_t *motor, float speed);
bool st2_motor_move (st2_motor_t *motor, const float move, const float speed, position_t type);
bool st2_motor_run (st2_motor_t *motor);
@@ -41,3 +45,4 @@ bool st2_motor_cruising (st2_motor_t *motor);
bool st2_motor_stop (st2_motor_t *motor);
int64_t st2_get_position (st2_motor_t *motor);
bool st2_set_position (st2_motor_t *motor, int64_t position);
+void st2_motor_register_stopped_callback (st2_motor_t *motor, foreground_task_ptr callback);
diff --git a/stream.c b/stream.c
index 979c10f..96faf70 100644
--- a/stream.c
+++ b/stream.c
@@ -26,6 +26,14 @@
#include "protocol.h"
#include "state_machine.h"
+#if defined(DEBUG) || defined(DEBUGOUT)
+#include
+#include
+#ifndef DEBUG_BUFFER
+#define DEBUG_BUFFER 100
+#endif
+#endif
+
static stream_rx_buffer_t rxbackup;
typedef struct {
@@ -47,7 +55,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 +240,7 @@ static stream_connection_t *add_connection (const io_stream_t *stream)
return NULL;
}
}
+ connection->prev = last;
last->next = connection;
}
@@ -246,42 +255,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 +332,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 +465,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 +502,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);
@@ -640,6 +651,38 @@ const io_stream_t *stream_null_init (uint32_t baud_rate)
#ifdef DEBUGOUT
+#if DEBUGOUT == -1
+
+__attribute__((weak)) void debug_write (const char *s)
+{
+ // NOOP
+}
+
+void debug_writeln (const char *s)
+{
+ debug_write(s);
+ debug_write(ASCII_EOL);
+}
+
+void debug_printf (const char *fmt, ...)
+{
+ char debug_out[DEBUG_BUFFER];
+
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(debug_out, sizeof(debug_out) - 1, fmt, args);
+ va_end(args);
+
+ debug_writeln(debug_out);
+}
+
+bool debug_stream_init (void)
+{
+ return true;
+}
+
+#else
+
static stream_write_ptr dbg_write = NULL;
void debug_write (const char *s)
@@ -661,6 +704,18 @@ void debug_writeln (const char *s)
}
}
+void debug_printf (const char *fmt, ...)
+{
+ char debug_out[DEBUG_BUFFER];
+
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(debug_out, sizeof(debug_out) - 1, fmt, args);
+ va_end(args);
+
+ debug_writeln(debug_out);
+}
+
static bool debug_claim_stream (io_stream_properties_t const *stream)
{
io_stream_t const *claimed = NULL;
@@ -691,4 +746,33 @@ bool debug_stream_init (void)
return hal.debug.write == debug_write;
}
+#endif // DEBUGOUT
+
+#elif defined(DEBUG)
+
+void debug_printf (const char *fmt, ...)
+{
+ char debug_out[DEBUG_BUFFER];
+
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(debug_out, sizeof(debug_out) - 1, fmt, args);
+ va_end(args);
+
+ if(hal.stream.write) {
+ report_message(debug_out, Message_Debug);
+ if(hal.stream.get_tx_buffer_count) {
+ while(hal.stream.get_tx_buffer_count()) // Wait until message is delivered
+ grbl.on_execute_realtime(state_get());
+ }
+ }
+}
+
+#else
+
+void debug_printf (const char *fmt, ...)
+{
+ // NOOP
+}
+
#endif
diff --git a/stream.h b/stream.h
index 3b41f85..8a3983f 100644
--- a/stream.h
+++ b/stream.h
@@ -5,18 +5,18 @@
Copyright (c) 2019-2023 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 .
*/
/*! \file
@@ -357,11 +357,21 @@ io_stream_t const *stream_open_instance (uint8_t instance, uint32_t baud_rate, s
bool stream_set_description (const io_stream_t *stream, const char *description);
+void debug_printf(const char *fmt, ...);
+
+#if defined(DEBUG) || defined(DEBUGOUT)
+#define DEBUG_PRINT 1
#ifdef DEBUGOUT
void debug_write (const char *s);
void debug_writeln (const char *s);
bool debug_stream_init (void);
#endif
+#else
+#define DEBUG_PRINT 0
+#endif
+
+#define debug_print(fmt, ...) \
+ do { if(DEBUG_PRINT) debug_printf(fmt, __VA_ARGS__); } while(0)
#ifdef __cplusplus
}
diff --git a/system.c b/system.c
index ff2db49..06a8ea7 100644
--- a/system.c
+++ b/system.c
@@ -74,11 +74,11 @@ ISR_CODE void ISR_FUNC(control_interrupt_handler)(control_signals_t signals)
sys.last_event.control.value = signals.value;
- if ((signals.reset || signals.e_stop || signals.motor_fault) && state_get() != STATE_ESTOP)
+ if((signals.reset || signals.e_stop || signals.motor_fault) && state_get() != STATE_ESTOP)
mc_reset();
else {
#ifndef NO_SAFETY_DOOR_SUPPORT
- if (signals.safety_door_ajar && hal.signals_cap.safety_door_ajar) {
+ if(signals.safety_door_ajar && hal.signals_cap.safety_door_ajar) {
if(settings.safety_door.flags.ignore_when_idle) {
// Only stop the spindle (laser off) when idle or jogging,
// this to allow positioning the controlled point (spindle) when door is open.
@@ -91,27 +91,27 @@ ISR_CODE void ISR_FUNC(control_interrupt_handler)(control_signals_t signals)
system_set_exec_state_flag(EXEC_SAFETY_DOOR);
}
#endif
-
if(signals.probe_overtravel) {
limit_signals_t overtravel = { .min.z = On};
hal.limits.interrupt_callback(overtravel);
// TODO: add message?
- } else if (signals.probe_triggered) {
+ } else if(signals.probe_triggered) {
if(sys.probing_state == Probing_Off && (state_get() & (STATE_CYCLE|STATE_JOG))) {
system_set_exec_state_flag(EXEC_STOP);
sys.alarm_pending = Alarm_ProbeProtect;
} else
hal.probe.configure(false, false);
- } else if (signals.probe_disconnected) {
+ } else if(signals.probe_disconnected) {
if(sys.probing_state == Probing_Active && state_get() == STATE_CYCLE) {
system_set_exec_state_flag(EXEC_FEED_HOLD);
sys.alarm_pending = Alarm_ProbeProtect;
}
- } else if (signals.feed_hold)
+ } else if(signals.feed_hold)
system_set_exec_state_flag(EXEC_FEED_HOLD);
- else if (signals.cycle_start) {
+ else if(signals.cycle_start) {
system_set_exec_state_flag(EXEC_CYCLE_START);
sys.report.cycle_start = settings.status_report.pin_state;
+ gc_state.tool_change = false;
}
if(signals.block_delete)
@@ -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..b43ba99 100644
--- a/tool_change.c
+++ b/tool_change.c
@@ -37,7 +37,7 @@
#define TOOL_CHANGE_PROBE_RETRACT_DISTANCE 2.0f
#endif
-static bool block_cycle_start, probe_fixture;
+static bool block_cycle_start, probe_toolsetter;
static volatile bool execute_posted = false;
static volatile uint32_t spin_lock = 0;
static float tool_change_position;
@@ -89,11 +89,11 @@ static void change_completed (void)
hal.irq_enable();
}
- if(probe_fixture)
- grbl.on_probe_fixture(¤t_tool, true, false);
+ if(probe_toolsetter)
+ grbl.on_probe_toolsetter(¤t_tool, NULL, true, false);
grbl.on_probe_completed = NULL;
- gc_state.tool_change = probe_fixture = false;
+ gc_state.tool_change = probe_toolsetter = false;
}
@@ -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);
}
}
@@ -180,6 +180,15 @@ static void execute_restore (void *data)
system_set_exec_state_flag(EXEC_CYCLE_START);
}
+// Set and limit probe travel to be within machine limits.
+static void set_probe_target (coord_data_t *target, uint8_t axis)
+{
+ target->values[axis] -= settings.tool_change.probing_distance;
+
+ if(bit_istrue(sys.homed.mask, bit(axis)) && settings.axis[axis].max_travel < -0.0f)
+ target->values[axis] = max(min(target->values[axis], sys.work_envelope.max.values[axis]), sys.work_envelope.min.values[axis]);
+}
+
// Execute touch off on cycle start event from @ G59.3 position.
// Used in SemiAutomatic mode ($341=3) only. Called from the foreground process.
static void execute_probe (void *data)
@@ -190,9 +199,6 @@ static void execute_probe (void *data)
plan_line_data_t plan_data;
gc_parser_flags_t flags = {0};
- if(probe_fixture)
- grbl.on_probe_fixture(next_tool, true, true);
-
// G59.3 contains offsets to position of TLS.
settings_read_coord_data(CoordinateSystem_G59_3, &offset.values);
@@ -202,15 +208,22 @@ static void execute_probe (void *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))) {
target.values[plane.axis_linear] = offset.values[plane.axis_linear];
ok = mc_line(target.values, &plan_data);
+ if(ok && probe_toolsetter)
+ grbl.on_probe_toolsetter(next_tool, NULL, true, true);
+
plan_data.feed_rate = settings.tool_change.seek_rate;
plan_data.condition.value = 0;
plan_data.spindle.state.value = 0;
- target.values[plane.axis_linear] -= settings.tool_change.probing_distance;
+
+ set_probe_target(&target, plane.axis_linear);
if((ok = ok && mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found))
{
@@ -350,17 +363,17 @@ static status_code_t tool_change (parser_state_t *parser_state)
hal.coolant.set_state((coolant_state_t){0});
execute_posted = false;
- probe_fixture = grbl.on_probe_fixture != NULL &&
- (settings.tool_change.mode == ToolChange_Manual ||
- settings.tool_change.mode == ToolChange_Manual_G59_3 ||
- settings.tool_change.mode == ToolChange_SemiAutomatic);
+ 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);
// Save current position.
system_convert_array_steps_to_mpos(previous.values, sys.position);
// 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;
@@ -389,12 +402,19 @@ static status_code_t tool_change (parser_state_t *parser_state)
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))
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
@@ -463,8 +483,8 @@ status_code_t tc_probe_workpiece (void)
plan_line_data_t plan_data;
#if COMPATIBILITY_LEVEL <= 1
- if(probe_fixture)
- grbl.on_probe_fixture(next_tool, system_xy_at_fixture(CoordinateSystem_G59_3, TOOLSETTER_RADIUS), true);
+ if(probe_toolsetter)
+ grbl.on_probe_toolsetter(next_tool, NULL, system_xy_at_fixture(CoordinateSystem_G59_3, TOOLSETTER_RADIUS), true);
#endif
// Get current position.
@@ -475,7 +495,7 @@ status_code_t tc_probe_workpiece (void)
plan_data_init(&plan_data);
plan_data.feed_rate = settings.tool_change.seek_rate;
- target.values[plane.axis_linear] -= settings.tool_change.probing_distance;
+ set_probe_target(&target, plane.axis_linear);
if((ok = mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found))
{