diff --git a/README.md b/README.md
index 45901dc..392207e 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 20230129, see the [changelog](changelog.md) for details.
+Latest build date is 20230213, see the [changelog](changelog.md) for details.
__NOTE:__ A settings reset will be performed on an update of builds earlier than 20230125. Backup and restore of settings is recommended.
__IMPORTANT!__ A new setting has been introduced for ganged axes motors in build 20211121.
I have only bench tested this for a couple of drivers, correct function should be verified after updating by those who have more than three motors configured.
@@ -87,4 +87,4 @@ List of Supported G-Codes:
Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes.
---
-2023-01-28
+2023-02-13
diff --git a/alarms.c b/alarms.c
index 048088c..2da56ef 100644
--- a/alarms.c
+++ b/alarms.c
@@ -64,3 +64,20 @@ alarm_details_t *alarms_get_details (void)
{
return &details;
}
+
+const char *alarms_get_description (alarm_code_t id)
+{
+ uint_fast16_t n_alarms;
+ const char *description = NULL;
+ alarm_details_t *details = grbl.on_get_alarms();
+
+ do {
+ n_alarms = details->n_alarms;
+ do {
+ if(details->alarms[--n_alarms].id == id)
+ description = details->alarms[n_alarms].description;
+ } while(description == NULL && n_alarms);
+ } while(description == NULL && (details = details->next));
+
+ return description;
+}
diff --git a/alarms.h b/alarms.h
index 11d35a3..e48afe9 100644
--- a/alarms.h
+++ b/alarms.h
@@ -61,8 +61,9 @@ typedef struct alarm_details {
typedef alarm_details_t *(*on_get_alarms_ptr)(void);
-void alarms_register (alarm_details_t *details);
alarm_details_t *alarms_get_details (void);
+const char *alarms_get_description (alarm_code_t id);
+void alarms_register (alarm_details_t *details);
#endif
diff --git a/changelog.md b/changelog.md
index 17d2db2..1e43043 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,11 +1,50 @@
## grblHAL changelog
+Build 20230213
+
+Core:
+
+* First phase of spindle handling refactoring with the aim to support multiple \(up to four\) _simultaneously_ active spindles.
+Spindle selection by tool number has also been implemented as an option. More details can be found in the [spindle plugin readme](https://github.com/grblHAL/Plugins_spindle/blob/master/README.md).
+__NOTE:__ This change is quite large, bugs may have sneaked in. Please report any issues encountered ASAP.
+
+Drivers:
+
+* All: updated for spindle handling refactoring.
+
+* iMXRT1062: updated readme for links to updated/patched libraries for SD card \(uSDFS\) and ethernet. User should switch to the updated/patched libraries if used.
+
+* MSP432P401R: fix for missing file include and serial stream registration.
+
+* RP2040: switched to SDK v1.5.0. Added new settings for RP2040, `$78` for wifi country code and `$337` for AP BSSID of AP to connect to. Both are optional.
+
+* STMF4xx: fix for issue [#110](https://github.com/grblHAL/STM32F4xx/issues/110), incorrect NVIC grouping.
+
+Plugins:
+
+* SD Card: fixed some minor inconsistencies.
+
+* Spindle: updated for spindle handling refactoring. Improved ModBus exception handling in some.
+New spindle select settings and improved functionality, see the [spindle plugin readme](https://github.com/grblHAL/Plugins_spindle/blob/master/README.md) for more.
+
+* Odometer and PPI: updated for spindle handling refactoring.
+
+* Networking: wifi \(ESP32 and RP2040\), changed networking settings from `$30x` range to `$32x` range for station mode.
+Added [MQTT](https://en.wikipedia.org/wiki/MQTT) API for plugin developers, example code using this can be found [here](https://github.com/grblHAL/Templates/tree/master/my_plugin/MQTT%20example).
+__NOTE:__ networking and other plugin settings will be reset on update for ESP32 and RP2040, backup and restore.
+
+---
+
Build 20230129
Core:
* Fixed regression in tool change handling related to refactoring of _config.h_ in build 20230125.
+Drivers:
+
+* ESP32: Added all supported VFD spindles as options in CMakeLists.txt, some code cleanup.
+
---
Build 20230128
@@ -96,7 +135,7 @@ Core:
* Added single axis homing commands for U and V, remapping of ABC homing commands to UVW when configured.
-Plugins
+Plugins:
* WebUI and Networking: fixes for compiler warnings.
diff --git a/config.h b/config.h
index 5a505c0..48be72c 100644
--- a/config.h
+++ b/config.h
@@ -58,6 +58,13 @@ If more than 3 axes are configured a compliant driver and board map file is need
#define N_SPINDLE 1
#endif
+/*! \def N_SYS_SPINDLE
+\brief Defines number of simultaneously active spindles supported - minimum 1 (none), maximum 8.
+*/
+#if !defined N_SYS_SPINDLE || defined __DOXYGEN__
+#define N_SYS_SPINDLE 1
+#endif
+
/*! \def BUILD_INFO
\brief Defines string to be output as part of the `$I` or `$I+` command response.
*/
@@ -871,19 +878,6 @@ not throw an alarm message.
#if !defined DEFAULT_CHECK_LIMITS_AT_INIT || defined __DOXYGEN__
#define DEFAULT_CHECK_LIMITS_AT_INIT Off
#endif
-/*! \def DEFAULT_LIMITS_TWO_SWITCHES_ON_AXES
-\brief
-If your machine has two limits switches wired in parallel to one axis, you will need to enable
-this feature. Since the two switches are sharing a single pin, there is no way for grblHAL to tell
-which one is enabled. This option only effects homing, where if a limit is engaged, grblHAL will
-alarm out and force the user to manually disengage the limit switch. Otherwise, if you have one
-limit switch for each axis, don't enable this option. By keeping it disabled, you can perform a
-homing cycle while on the limit switch and not have to move the machine off of it.
-*/
-#if !defined DEFAULT_LIMITS_TWO_SWITCHES_ON_AXES || defined __DOXYGEN__
-#define DEFAULT_LIMITS_TWO_SWITCHES_ON_AXES Off // Default disabled. Set to \ref On or 1 to enable.
-#endif
-///@}
/*! @name Group_Limits_DualAxis
\brief Dual axis limits settings (Group_Limits_DualAxis)
@@ -1126,15 +1120,15 @@ Requires homing cycles to be defined by \ref DEFAULT_HOMING_CYCLE_0 - \ref DEFAU
#define DEFAULT_HOMING_ENABLE Off // Default disabled. Set to \ref On or 1 to enable.
#endif
-/*! /def HOMING_SINGLE_AXIS_COMMANDS
+/*! /def DEFAULT_HOMING_SINGLE_AXIS_COMMANDS
\brief Enables single axis homing commands.
`$HX`, `$HY`, `$HZ` etc. for homing the respective axes.The full homing
cycle is still invoked by the `$H` command. This is disabled by default.
If you have a two-axis machine, _DON'T USE THIS_. Instead, just alter the homing cycle for two-axes.
\internal Bit 1 in settings.homing.flags.
*/
-#if !defined HOMING_SINGLE_AXIS_COMMANDS || defined __DOXYGEN__
-#define HOMING_SINGLE_AXIS_COMMANDS Off // Default disabled. Set to \ref On or 1 to enable.
+#if !defined DEFAULT_HOMING_SINGLE_AXIS_COMMANDS || defined __DOXYGEN__
+#define DEFAULT_HOMING_SINGLE_AXIS_COMMANDS Off // Default disabled. Set to \ref On or 1 to enable.
#endif
/*! /def DEFAULT_HOMING_INIT_LOCK
@@ -1148,17 +1142,32 @@ mainly a safety feature to remind the user to home, since position is unknown to
#define DEFAULT_HOMING_INIT_LOCK Off // Default disabled. Set to \ref On or 1 to enable.
#endif
-/*! /def HOMING_FORCE_SET_ORIGIN
+/*! /def DEFAULT_HOMING_FORCE_SET_ORIGIN
\brief
After homing, grblHAL will set by default the entire machine space into negative space, as is typical
for professional CNC machines, regardless of where the limit switches are located. Set this
define to \ref On or 1 to force grblHAL to always set the machine origin at the homed location despite switch orientation.
\internal Bit 3 in settings.homing.flags.
*/
-#if !defined HOMING_FORCE_SET_ORIGIN || defined __DOXYGEN__
-#define HOMING_FORCE_SET_ORIGIN Off // Default disabled. Set to \ref On or 1 to enable.
+#if !defined DEFAULT_HOMING_FORCE_SET_ORIGIN || defined __DOXYGEN__
+#define DEFAULT_HOMING_FORCE_SET_ORIGIN Off // Default disabled. Set to \ref On or 1 to enable.
#endif
+/*! \def DEFAULT_LIMITS_TWO_SWITCHES_ON_AXES
+\brief
+If your machine has two limits switches wired in parallel to one axis, you will need to enable
+this feature. Since the two switches are sharing a single pin, there is no way for grblHAL to tell
+which one is enabled. This option only effects homing, where if a limit is engaged, grblHAL will
+alarm out and force the user to manually disengage the limit switch. Otherwise, if you have one
+limit switch for each axis, don't enable this option. By keeping it disabled, you can perform a
+homing cycle while on the limit switch and not have to move the machine off of it.
+\internal Bit 4 in settings.limits.flags.
+*/
+#if !defined DEFAULT_LIMITS_TWO_SWITCHES_ON_AXES || defined __DOXYGEN__
+#define DEFAULT_LIMITS_TWO_SWITCHES_ON_AXES Off // Default disabled. Set to \ref On or 1 to enable.
+#endif
+///@}
+
/*! /def DEFAULT_HOMING_ALLOW_MANUAL
\brief
If enabled this allows using the homing $-commands to set the home position to the
@@ -1365,7 +1374,7 @@ greater.
// pull-out position, power-up with a time-out, and plunge back to the original position at the
// slower pull-out rate.
// NOTE: Still a work-in-progress. Machine coordinates must be in all negative space and
-// does not work with HOMING_FORCE_SET_ORIGIN enabled. Parking motion also moves only in
+// does not work with DEFAULT_HOMING_FORCE_SET_ORIGIN enabled. Parking motion also moves only in
// positive direction.
* // Default disabled. Uncomment to enable.
*/
@@ -1571,7 +1580,7 @@ second motor for ganged/auto squared axes.
*/
///@{
#if !defined DEFAULT_STEP_PULSE_DELAY || defined __DOXYGEN__
-#define DEFAULT_STEP_PULSE_DELAY 5.0f // uncomment to set default > 0.0f
+#define DEFAULT_STEP_PULSE_DELAY 0.0f
#endif
///@}
@@ -1778,6 +1787,21 @@ __NOTE:__ Must be a positive values.
#define N_TOOLS 16
#endif
+#if N_SYS_SPINDLE > N_SPINDLE
+#undef N_SYS_SPINDLE
+#define N_SYS_SPINDLE N_SPINDLE
+#endif
+
+#if N_SYS_SPINDLE < 1
+#undef N_SYS_SPINDLE
+#define N_SYS_SPINDLE 1
+#endif
+
+#if N_SYS_SPINDLE > 8
+#undef N_SYS_SPINDLE
+#define N_SYS_SPINDLE 8
+#endif
+
#if (REPORT_WCO_REFRESH_BUSY_COUNT < REPORT_WCO_REFRESH_IDLE_COUNT)
#error "WCO busy refresh is less than idle refresh."
#endif
@@ -1822,13 +1846,13 @@ __NOTE:__ Must be a positive values.
#if DEFAULT_PARKING_ENABLE > 0
#if DEFAULT_HOMING_FORCE_SET_ORIGIN > 0
- #error "HOMING_FORCE_SET_ORIGIN is not supported with PARKING_ENABLE at this time."
+ #error "DEFAULT_HOMING_FORCE_SET_ORIGIN is not supported with DEFAULT_PARKING_ENABLE at this time."
#endif
#endif
#if DEFAULT_ENABLE_PARKING_OVERRIDE_CONTROL > 0
#if DEFAULT_PARKING_ENABLE < 1
- #error "ENABLE_PARKING_OVERRIDE_CONTROL must be enabled with PARKING_ENABLE."
+ #error "DEFAULT_ENABLE_PARKING_OVERRIDE_CONTROL must be enabled with DEFAULT_PARKING_ENABLE."
#endif
#endif
diff --git a/core_handlers.h b/core_handlers.h
index 76d3723..4f3111b 100644
--- a/core_handlers.h
+++ b/core_handlers.h
@@ -43,7 +43,7 @@ typedef enum {
OverrideChanged_FanState = 0
} override_changed_t;
-/* TODO: add to grbl pointers so that a different formatting (xml, json etc) of reports may be implemented by driver?
+/* TODO: add to grbl pointers so that a different formatting (xml, json etc) of reports may be implemented by a plugin?
typedef struct {
status_code_t (*report_status_message)(status_code_t status_code);
alarm_code_t (*report_alarm_message)(alarm_code_t alarm_code);
@@ -67,8 +67,8 @@ typedef message_code_t (*feedback_message_ptr)(message_code_t message_code);
typedef struct {
setting_output_ptr setting;
- status_message_ptr status_message;
- feedback_message_ptr feedback_message;
+ status_message_ptr status_message; //condition.inverse_time = Off;
pl_data->feed_rate = gc_state.distance_per_rev = pitch;
- pl_data->condition.is_rpm_pos_adjusted = Off; // Switch off CSS.
+ pl_data->spindle.css = NULL; // Switch off CSS.
pl_data->overrides = sys.override.control; // Use current override flags and
pl_data->overrides.sync = On; // set to sync overrides on execution of motion.
// Disable feed rate and spindle overrides for the duration of the cycle.
pl_data->overrides.spindle_rpm_disable = sys.override.control.spindle_rpm_disable = On;
pl_data->overrides.feed_rate_disable = sys.override.control.feed_rate_disable = On;
- sys.override.spindle_rpm = DEFAULT_SPINDLE_RPM_OVERRIDE;
+ pl_data->spindle.hal->param->override_pct = DEFAULT_SPINDLE_RPM_OVERRIDE;
// TODO: need for gc_state.distance_per_rev to be reset on modal change?
- float feed_rate = pl_data->feed_rate * hal.spindle.get_data(SpindleData_RPM)->rpm;
+ float feed_rate = pl_data->feed_rate * pl_data->spindle.hal->get_data(SpindleData_RPM)->rpm;
if(feed_rate == 0.0f)
FAIL(Status_GcodeSpindleNotRunning); // [Spindle not running]
@@ -518,7 +521,7 @@ static status_code_t read_parameter (char *line, uint_fast8_t *char_counter, flo
// In this function, all units and positions are converted and exported to internal functions
// in terms of (mm, mm/min) and absolute machine coordinates, respectively.
-status_code_t gc_execute_block(char *block)
+status_code_t gc_execute_block (char *block)
{
static const parameter_words_t axis_words_mask = {
.x = On,
@@ -612,7 +615,7 @@ status_code_t gc_execute_block(char *block)
bool set_tool = false, spindle_programmed = false;
axis_command_t axis_command = AxisCommand_None;
- uint_fast8_t port_command = 0;
+ io_mcode_t port_command = (io_mcode_t)0;
plane_t plane;
// Initialize bitflag tracking variables for axis indices compatible operations.
@@ -630,7 +633,7 @@ status_code_t gc_execute_block(char *block)
gc_parser_flags.jog_motion = On;
gc_block.modal.motion = MotionMode_Linear;
gc_block.modal.feed_mode = FeedMode_UnitsPerMin;
- gc_block.modal.spindle_rpm_mode = SpindleSpeedMode_RPM;
+ gc_block.modal.spindle.rpm_mode = SpindleSpeedMode_RPM;
gc_block.values.n = JOG_LINE_NUMBER; // Initialize default line number reported during jog.
}
@@ -751,7 +754,7 @@ status_code_t gc_execute_block(char *block)
switch(int_value) {
case 7: case 8:
- if(sys.mode == Mode_Lathe) {
+ if(settings.mode == Mode_Lathe) {
word_bit.modal_group.G15 = On;
gc_block.modal.diameter_mode = int_value == 7; // TODO: find specs for implementation, only affects X calculation? reporting? current position?
} else
@@ -785,8 +788,8 @@ status_code_t gc_execute_block(char *block)
break;
case 33: case 76:
- if(!hal.spindle.get_data || (mantissa != 0))
- FAIL(Status_GcodeUnsupportedCommand); // [G33, G33.1 or G76 not supported]
+ if(mantissa != 0)
+ FAIL(Status_GcodeUnsupportedCommand); // [G33.1 not yet supported]
if (axis_command)
FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict]
axis_command = AxisCommand_MotionMode;
@@ -859,11 +862,8 @@ status_code_t gc_execute_block(char *block)
break;
case 95:
- if(hal.spindle.get_data) {
- word_bit.modal_group.G5 = On;
- gc_block.modal.feed_mode = FeedMode_UnitsPerRev;
- } else
- FAIL(Status_GcodeUnsupportedCommand); // [G95 not supported]
+ word_bit.modal_group.G5 = On;
+ gc_block.modal.feed_mode = FeedMode_UnitsPerRev;
break;
case 20: case 21:
@@ -933,9 +933,9 @@ status_code_t gc_execute_block(char *block)
break;
*/
case 96: case 97:
- if(sys.mode == Mode_Lathe && hal.spindle.cap.variable) {
+ if(settings.mode == Mode_Lathe) {
word_bit.modal_group.G14 = On;
- gc_block.modal.spindle_rpm_mode = (spindle_rpm_mode_t)((int_value - 96) ^ 1);
+ gc_block.modal.spindle.rpm_mode = (spindle_rpm_mode_t)((int_value - 96) ^ 1);
} else
FAIL(Status_GcodeUnsupportedCommand);
break;
@@ -995,8 +995,8 @@ status_code_t gc_execute_block(char *block)
case 3: case 4: case 5:
word_bit.modal_group.M7 = On;
- gc_block.modal.spindle.on = !(int_value == 5);
- gc_block.modal.spindle.ccw = int_value == 4;
+ gc_block.modal.spindle.state.on = !(int_value == 5);
+ gc_block.modal.spindle.state.ccw = int_value == 4;
sys.flags.delay_overrides = On;
break;
@@ -1438,38 +1438,61 @@ status_code_t gc_execute_block(char *block)
// bit_false(gc_block.words,bit(Word_F)); // NOTE: Single-meaning value word. Set at end of error-checking.
- // [4. Set spindle speed ]: S or D is negative (done.)
+ // [4. Set spindle speed and address spindle ]: S or D is negative (done.)
if(gc_block.words.$) {
- if(gc_block.values.$ < (command_words.M7 ? -1 : 0)) // if S word provided allow -1?
- FAIL(command_words.M7 ? Status_GcodeValueOutOfRange: Status_NegativeValue);
- if(gc_block.values.$ >= 1 /*spindle_get_count()*/)
- FAIL(Status_GcodeValueOutOfRange);
- gc_block.words.$ = Off;
- }
+ bool single_spindle_only = (gc_block.words.s && !user_words.s) ||
+ (command_words.G0 && (gc_block.modal.motion == MotionMode_SpindleSynchronized ||
+ gc_block.modal.motion == MotionMode_RigidTapping ||
+ gc_block.modal.motion == MotionMode_Threading)) ||
+ command_words.G14 ||
+ (command_words.M9 && gc_block.override_command == Override_SpindleSpeed);
+ if(command_words.M7 || single_spindle_only) {
+ if(gc_block.values.$ < (single_spindle_only ? 0 : -1))
+ FAIL(single_spindle_only ? Status_NegativeValue : Status_GcodeValueOutOfRange);
+ if(!spindle_is_enabled(gc_block.values.$))
+ FAIL(Status_GcodeValueOutOfRange);
+ if(gc_block.values.$ >= 0)
+ gc_state.spindle.hal = gc_block.spindle = spindle_get(gc_block.values.$);
+ gc_block.words.$ = Off;
+ }
+ } else if(gc_block.spindle == NULL)
+ gc_block.spindle = gc_state.spindle.hal;
+
+ if(gc_block.modal.feed_mode == FeedMode_UnitsPerRev && !gc_state.spindle.hal->get_data)
+ FAIL(Status_GcodeUnsupportedCommand); // [G95 not supported]
if (command_words.G14) {
- if(gc_block.modal.spindle_rpm_mode == SpindleSpeedMode_CSS) {
+ if(gc_block.modal.spindle.rpm_mode == SpindleSpeedMode_CSS) {
+ if(!gc_state.spindle.hal->cap.variable)
+ FAIL(Status_GcodeUnsupportedCommand);
if (!gc_block.words.s) // TODO: add check for S0?
FAIL(Status_GcodeValueWordMissing);
// see below!! gc_block.values.s *= (gc_block.modal.units_imperial ? MM_PER_INCH * 12.0f : 1000.0f); // convert surface speed to mm/min
if (gc_block.words.d) {
- gc_state.spindle.css.max_rpm = min(gc_block.values.d, hal.spindle.rpm_max);
+ gc_state.spindle.hal->param->css.max_rpm = min(gc_block.values.d, gc_state.spindle.hal->rpm_max);
gc_block.words.d = Off;
} else
- gc_state.spindle.css.max_rpm = hal.spindle.rpm_max;
- } else if(gc_state.modal.spindle_rpm_mode == SpindleSpeedMode_CSS)
- gc_state.spindle.rpm = sys.spindle_rpm; // Is it correct to restore latest spindle RPM here?
- gc_state.modal.spindle_rpm_mode = gc_block.modal.spindle_rpm_mode;
+ gc_state.spindle.hal->param->css.max_rpm = gc_state.spindle.hal->rpm_max;
+ } else if(gc_state.modal.spindle.rpm_mode == SpindleSpeedMode_CSS) {
+ if(gc_state.spindle.css) {
+ gc_state.spindle.css = NULL;
+ protocol_buffer_synchronize(); // Empty planner buffer to ensure we get RPM at end of last CSS motion
+ }
+ gc_state.spindle.rpm = gc_state.spindle.hal->param->rpm; // Is it correct to restore latest spindle RPM here?
+ }
+ gc_state.modal.spindle.rpm_mode = gc_block.modal.spindle.rpm_mode;
}
spindle_programmed = gc_block.words.s && !user_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.css.surface_speed;
- else if(!user_words.s && gc_state.modal.spindle_rpm_mode == SpindleSpeedMode_CSS)
+ 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) {
// 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
+ gc_state.spindle.hal->param->css.surface_speed = gc_block.values.s;
+ }
// bit_false(gc_block.words,bit(Word_S)); // NOTE: Single-meaning value word. Set at end of error-checking.
@@ -1491,10 +1514,10 @@ status_code_t gc_execute_block(char *block)
switch(port_command) {
- case 62:
- case 63:
- case 64:
- case 65:
+ case IoMCode_OutputOnSynced:
+ case IoMCode_OutputOffSynced:
+ case IoMCode_OutputOnImmediate:
+ case IoMCode_OutputOffImmediate:
if(!gc_block.words.p)
FAIL(Status_GcodeValueWordMissing);
if(gc_block.values.p < 0.0f)
@@ -1507,7 +1530,7 @@ status_code_t gc_execute_block(char *block)
gc_block.words.p = Off;
break;
- case 66:
+ case IoMCode_WaitOnInput:
if(!(gc_block.words.l || gc_block.words.q))
FAIL(Status_GcodeValueWordMissing);
@@ -1543,8 +1566,8 @@ status_code_t gc_execute_block(char *block)
gc_block.words.e = gc_block.words.l = gc_block.words.p = gc_block.words.q = Off;
break;
- case 67:
- case 68:
+ case IoMCode_AnalogOutSynced:
+ case IoMCode_AnalogOutImmediate:
if(!(gc_block.words.e || gc_block.words.q))
FAIL(Status_GcodeValueWordMissing);
if((uint32_t)gc_block.values.e + 1 > hal.port.num_analog_out)
@@ -1561,7 +1584,19 @@ status_code_t gc_execute_block(char *block)
// [6. Change tool ]: N/A
- // [7. Spindle control ]: N/A - spindle id validation taken care of above in step 4.
+ // [7. Spindle control ]:
+ if(command_words.M7 && gc_block.modal.spindle.state.ccw) {
+ // Check if spindle(s) support reversing direction
+ if(gc_block.spindle == NULL) {
+ uint_fast8_t idx = N_SYS_SPINDLE;
+ do {
+ idx--;
+ if(spindle_is_enabled(idx) && !spindle_get(idx)->cap.direction)
+ FAIL(Status_GcodeUnsupportedCommand);
+ } while(idx);
+ } else if(!gc_block.spindle->cap.direction)
+ FAIL(Status_GcodeUnsupportedCommand);
+ }
// [8. Coolant control ]: N/A
@@ -2020,12 +2055,15 @@ status_code_t gc_execute_block(char *block)
gc_block.modal.retract_mode = CCRetractMode_Previous;
// Initial(?) check for spindle running for moves in G96 mode
- if(gc_block.modal.spindle_rpm_mode == SpindleSpeedMode_CSS && (!gc_block.modal.spindle.on || gc_block.values.s == 0.0f))
+ if(gc_block.modal.spindle.rpm_mode == SpindleSpeedMode_CSS && (!gc_block.modal.spindle.state.on || gc_block.values.s == 0.0f))
FAIL(Status_GcodeSpindleNotRunning);
// Check if feed rate is defined for the motion modes that require it.
if (gc_block.modal.motion == MotionMode_SpindleSynchronized) {
+ if(!gc_state.spindle.hal->get_data)
+ FAIL(Status_GcodeUnsupportedCommand); // [G33, G33.1]
+
if(gc_block.values.k == 0.0f)
FAIL(Status_GcodeValueOutOfRange); // [No distance (pitch) given]
@@ -2036,6 +2074,9 @@ status_code_t gc_execute_block(char *block)
// Fail if cutter radius comp is active
+ if(!gc_state.spindle.hal->get_data)
+ FAIL(Status_GcodeUnsupportedCommand); // [G76 not supported]
+
if(gc_block.modal.plane_select != PlaneSelect_ZX)
FAIL(Status_GcodeIllegalPlane); // [Plane not ZX]
@@ -2057,7 +2098,7 @@ status_code_t gc_execute_block(char *block)
(gc_block.words.l && (gc_taper_type)gc_block.values.l > Taper_Both))
FAIL(Status_GcodeValueOutOfRange);
- if(gc_state.spindle.rpm < hal.spindle.rpm_min || gc_state.spindle.rpm > hal.spindle.rpm_max)
+ if(gc_state.spindle.rpm < gc_state.spindle.hal->rpm_min || gc_state.spindle.rpm > gc_state.spindle.hal->rpm_max)
FAIL(Status_GcodeRPMOutOfRange);
if(gc_block.modal.motion != gc_state.modal.motion) {
@@ -2553,9 +2594,9 @@ status_code_t gc_execute_block(char *block)
// Initialize planner data to current spindle and coolant modal state.
memcpy(&plan_data.spindle, &gc_state.spindle, sizeof(spindle_t));
- plan_data.condition.spindle = gc_state.modal.spindle;
+ plan_data.spindle.state = gc_state.modal.spindle.state;
plan_data.condition.coolant = gc_state.modal.coolant;
- plan_data.condition.is_rpm_rate_adjusted = gc_state.is_rpm_rate_adjusted || (gc_state.modal.spindle.ccw && sys.mode == Mode_Laser);
+ plan_data.condition.is_rpm_rate_adjusted = gc_state.is_rpm_rate_adjusted || (gc_state.modal.spindle.state.ccw && gc_state.spindle.hal->cap.laser);
if ((status_code_t)(int_value = (uint_fast16_t)mc_jog_execute(&plan_data, &gc_block)) == Status_OK)
memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_state.position));
@@ -2564,7 +2605,7 @@ status_code_t gc_execute_block(char *block)
}
// If in laser mode, setup laser power based on current and past parser conditions.
- if(sys.mode == Mode_Laser) {
+ if(gc_state.spindle.hal->cap.laser) {
if(!motion_is_lasercut(gc_block.modal.motion))
gc_parser_flags.laser_disable = On;
@@ -2574,7 +2615,7 @@ status_code_t gc_execute_block(char *block)
// TODO: Check sync conditions for M3 enabled motions that don't enter the planner. (zero length).
if(axis_words.mask && (axis_command == AxisCommand_MotionMode))
gc_parser_flags.laser_is_motion = On;
- else if(gc_state.modal.spindle.on && !gc_state.modal.spindle.ccw) {
+ else if(gc_state.modal.spindle.state.on && !gc_state.modal.spindle.state.ccw) {
// M3 constant power laser requires planner syncs to update the laser when changing between
// a G1/2/3 motion mode state and vice versa when there is no motion in the line.
if(motion_is_lasercut(gc_state.modal.motion)) {
@@ -2584,7 +2625,7 @@ status_code_t gc_execute_block(char *block)
gc_parser_flags.spindle_force_sync = On;
}
- gc_state.is_rpm_rate_adjusted = gc_state.modal.spindle.ccw && !gc_parser_flags.laser_disable;
+ gc_state.is_rpm_rate_adjusted = gc_state.modal.spindle.state.ccw && !gc_parser_flags.laser_disable;
}
// [0. Non-specific/common error-checks and miscellaneous setup]:
@@ -2608,35 +2649,41 @@ status_code_t gc_execute_block(char *block)
plan_data.feed_rate = gc_state.feed_rate; // Record data for planner use.
// [4. Set spindle speed ]:
- if(gc_state.modal.spindle_rpm_mode == SpindleSpeedMode_CSS) {
- if(!user_words.s)
- gc_state.spindle.css.surface_speed = gc_block.values.s;
- if((plan_data.condition.is_rpm_pos_adjusted = gc_block.modal.motion != MotionMode_None && gc_block.modal.motion != MotionMode_Seek)) {
- gc_state.spindle.css.active = true;
- gc_state.spindle.css.axis = plane.axis_1;
- gc_state.spindle.css.tool_offset = gc_get_offset(gc_state.spindle.css.axis);
- 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)));
+ if(gc_state.modal.spindle.rpm_mode == SpindleSpeedMode_CSS) {
+ 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);
+ 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;
} else {
- if(gc_state.spindle.css.active) {
- gc_state.spindle.css.active = false;
+ if(gc_state.spindle.css) {
+ gc_state.spindle.css = NULL;
protocol_buffer_synchronize(); // Empty planner buffer to ensure we get RPM at end of last CSS motion
}
- gc_block.values.s = sys.spindle_rpm; // Keep current RPM
+ gc_block.values.s = gc_state.spindle.rpm; //gc_state.spindle.hal->param->rpm; // Keep current RPM
}
}
- if (!user_words.s && ((gc_state.spindle.rpm != gc_block.values.s) || gc_parser_flags.spindle_force_sync)) {
- if (gc_state.modal.spindle.on && !gc_parser_flags.laser_is_motion)
- spindle_sync(0, gc_state.modal.spindle, gc_parser_flags.laser_disable ? 0.0f : gc_block.values.s);
+ if(!user_words.s && ((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;
+ spindle_sync(gc_block.spindle, gc_state.modal.spindle.state, gc_parser_flags.laser_disable ? 0.0f : gc_block.values.s);
+ }
+ // else... - setting the same rpm for multiple spindles at once is not allowed!
+ }
gc_state.spindle.rpm = gc_block.values.s; // Update spindle speed state.
}
// NOTE: Pass zero spindle speed for all restricted laser motions.
if (!gc_parser_flags.laser_disable)
memcpy(&plan_data.spindle, &gc_state.spindle, sizeof(spindle_t)); // Record data for planner use.
- // else { plan_data.spindle.speed = 0.0; } // Initialized as zero already.
+ else {
+ plan_data.spindle.hal = gc_state.spindle.hal;
+ // plan_data.spindle.speed = 0.0f; // Initialized as zero already.
+ }
// [5. Select tool ]: Only tracks tool value if ATC or manual tool change is not possible.
if(gc_state.tool_pending != gc_block.values.t && !check_mode) {
@@ -2650,6 +2697,16 @@ status_code_t gc_execute_block(char *block)
#else
gc_state.tool->tool = gc_state.tool_pending;
#endif
+ if(grbl.on_tool_selected) {
+
+ spindle_state_t state = gc_state.modal.spindle.state;
+
+ grbl.on_tool_selected(gc_state.tool);
+
+ if(state.value != gc_state.modal.spindle.state.value)
+ gc_block.modal.spindle.state = gc_state.modal.spindle.state;
+ }
+
sys.report.tool = On;
}
@@ -2670,26 +2727,26 @@ status_code_t gc_execute_block(char *block)
switch(port_command) {
- case 62:
- case 63:
+ case IoMCode_OutputOnSynced:
+ case IoMCode_OutputOffSynced:
add_output_command(&gc_block.output_command);
break;
- case 64:
- case 65:
+ case IoMCode_OutputOnImmediate:
+ case IoMCode_OutputOffImmediate:
hal.port.digital_out(gc_block.output_command.port, gc_block.output_command.value != 0.0f);
break;
- case 66:
+ case IoMCode_WaitOnInput:
sys.var5399 = hal.port.wait_on_input((io_port_type_t)gc_block.output_command.is_digital, gc_block.output_command.port, (wait_mode_t)gc_block.values.l, gc_block.values.q);
sys.report.m66result = On;
break;
- case 67:
+ case IoMCode_AnalogOutSynced:
add_output_command(&gc_block.output_command);
break;
- case 68:
+ case IoMCode_AnalogOutImmediate:
hal.port.analog_out(gc_block.output_command.port, gc_block.output_command.value);
break;
}
@@ -2711,6 +2768,17 @@ status_code_t gc_execute_block(char *block)
#else
gc_state.tool->tool = gc_state.tool_pending;
#endif
+
+ if(grbl.on_tool_selected) {
+
+ spindle_state_t state = gc_state.modal.spindle.state;
+
+ grbl.on_tool_selected(gc_state.tool);
+
+ if(state.value != gc_state.modal.spindle.state.value)
+ gc_block.modal.spindle.state = gc_state.modal.spindle.state;
+ }
+
if(hal.tool.change) { // ATC
if((int_value = (uint_fast16_t)hal.tool.change(&gc_state)) != Status_OK)
FAIL((status_code_t)int_value);
@@ -2723,20 +2791,36 @@ status_code_t gc_execute_block(char *block)
}
// [7. Spindle control ]:
- if (gc_state.modal.spindle.value != gc_block.modal.spindle.value) {
+ if (gc_state.modal.spindle.state.value != gc_block.modal.spindle.state.value) {
// Update spindle control and apply spindle speed when enabling it in this block.
// NOTE: All spindle state changes are synced, even in laser mode. Also, plan_data,
// rather than gc_state, is used to manage laser state for non-laser motions.
- if((spindle_programmed = spindle_sync(0, gc_block.modal.spindle, plan_data.spindle.rpm)))
- gc_state.modal.spindle = gc_block.modal.spindle;
+ if(gc_block.spindle) {
+ if((spindle_programmed = 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 {
+ idx = N_SYS_SPINDLE;
+ do {
+ if(spindle_is_enabled(--idx)) {
+ spindle_ptrs_t *spindle = spindle_get(idx);
+ if(spindle_sync(spindle, gc_block.modal.spindle.state, plan_data.spindle.rpm)) {
+ spindle_programmed = true;
+ spindle->param->state = gc_block.modal.spindle.state;
+ }
+ }
+ } while(idx);
+ }
+ if(spindle_programmed)
+ gc_state.modal.spindle.state = gc_block.modal.spindle.state;
}
+ // TODO: add spindle argument and move into loop above?
if(spindle_programmed && grbl.on_spindle_programmed)
- grbl.on_spindle_programmed(gc_state.modal.spindle, gc_state.spindle.rpm, gc_state.modal.spindle_rpm_mode);
+ grbl.on_spindle_programmed(gc_state.spindle.hal, gc_state.modal.spindle.state, gc_state.spindle.rpm, gc_state.modal.spindle.rpm_mode);
-// TODO: Recheck spindle running in CCS mode (is_rpm_pos_adjusted = On)?
+// TODO: Recheck spindle running in CCS mode (is_rpm_pos_adjusted == On)?
- plan_data.condition.spindle = gc_state.modal.spindle; // Set condition flag for planner use.
+ plan_data.spindle.state = gc_state.modal.spindle.state; // Set condition flag for planner use.
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;
@@ -2754,13 +2838,15 @@ status_code_t gc_execute_block(char *block)
// [9. Override control ]:
if (gc_state.modal.override_ctrl.value != gc_block.modal.override_ctrl.value) {
+
gc_state.modal.override_ctrl = gc_block.modal.override_ctrl;
+ gc_state.modal.spindle.state.override_disable = gc_state.spindle.hal->param->state.override_disable = gc_state.modal.override_ctrl.spindle_rpm_disable;
if(gc_state.modal.override_ctrl.feed_rate_disable)
plan_feed_override(0, 0);
if(gc_state.modal.override_ctrl.spindle_rpm_disable)
- spindle_set_override(DEFAULT_SPINDLE_RPM_OVERRIDE);
+ spindle_set_override(gc_state.spindle.hal, DEFAULT_SPINDLE_RPM_OVERRIDE);
mc_override_ctrl_update(gc_state.modal.override_ctrl); // NOTE: must be called last!
}
@@ -2937,7 +3023,7 @@ status_code_t gc_execute_block(char *block)
case MotionMode_Linear:
if(gc_state.modal.feed_mode == FeedMode_UnitsPerRev) {
- plan_data.condition.spindle.synchronized = On;
+ plan_data.spindle.state.synchronized = On;
//?? gc_state.distance_per_rev = plan_data.feed_rate;
// check initial feed rate - fail if zero?
}
@@ -2994,7 +3080,7 @@ status_code_t gc_execute_block(char *block)
if(status != Status_OK)
FAIL(status);
- plan_data.condition.spindle.synchronized = On;
+ plan_data.spindle.state.synchronized = On;
mc_line(gc_block.values.xyz, &plan_data);
@@ -3098,17 +3184,28 @@ status_code_t gc_execute_block(char *block)
gc_block.modal.canned_cycle_active = false;
gc_state.modal.plane_select = PlaneSelect_XY;
// gc_state.modal.plane_select = settings.flags.lathe_mode ? PlaneSelect_ZX : PlaneSelect_XY;
- gc_state.modal.spindle_rpm_mode = SpindleSpeedMode_RPM; // NOTE: not compliant with linuxcnc (?)
+ gc_state.modal.spindle.rpm_mode = SpindleSpeedMode_RPM; // NOTE: not compliant with linuxcnc (?)
gc_state.modal.distance_incremental = false;
gc_state.modal.feed_mode = FeedMode_UnitsPerMin;
// TODO: check gc_state.distance_per_rev = 0.0f;
// gc_state.modal.cutter_comp = CUTTER_COMP_DISABLE; // Not supported.
if((sys.report.gwco = gc_state.modal.coord_system.id != CoordinateSystem_G54))
gc_state.modal.coord_system.id = CoordinateSystem_G54;
- gc_state.modal.spindle = (spindle_state_t){0};
+ gc_state.modal.spindle.state = (spindle_state_t){0};
gc_state.modal.coolant = (coolant_state_t){0};
gc_state.modal.override_ctrl.feed_rate_disable = Off;
gc_state.modal.override_ctrl.spindle_rpm_disable = Off;
+
+ idx = N_SYS_SPINDLE;
+ spindle_ptrs_t *spindle;
+ do {
+ if((spindle = spindle_get(--idx))) {
+ spindle->param->state.override_disable = Off;
+ if(settings.flags.restore_overrides)
+ spindle->param->override_pct = DEFAULT_SPINDLE_RPM_OVERRIDE;
+ }
+ } while(idx);
+
if(settings.parking.flags.enabled)
gc_state.modal.override_ctrl.parking_disable = settings.parking.flags.enable_override_control &&
settings.parking.flags.deactivate_upon_init;
@@ -3117,7 +3214,6 @@ status_code_t gc_execute_block(char *block)
if(settings.flags.restore_overrides) {
sys.override.feed_rate = DEFAULT_FEED_OVERRIDE;
sys.override.rapid_rate = DEFAULT_RAPID_OVERRIDE;
- sys.override.spindle_rpm = DEFAULT_SPINDLE_RPM_OVERRIDE;
}
// Execute coordinate change and spindle/coolant stop.
@@ -3133,7 +3229,8 @@ status_code_t gc_execute_block(char *block)
#endif
system_flag_wco_change(); // Set to refresh immediately just in case something altered.
- hal.spindle.set_state(gc_state.modal.spindle, 0.0f);
+
+ spindle_all_off();
hal.coolant.set_state(gc_state.modal.coolant);
sys.report.spindle = sys.report.coolant = On; // Set to report change immediately
}
diff --git a/gcode.h b/gcode.h
index 6625014..c59b488 100644
--- a/gcode.h
+++ b/gcode.h
@@ -196,8 +196,19 @@ typedef enum {
Override_Parking = 56 //!< 56 - M56
} override_mode_t;
-/*! Modal Group M10: User/driver/plugin defined M commands
+/*! Modal Group M10: i/o control */
+typedef enum {
+ IoMCode_OutputOnSynced = 62, //!< 62 - M62
+ IoMCode_OutputOffSynced = 63, //!< 63 - M63
+ IoMCode_OutputOnImmediate = 64, //!< 64 - M64
+ IoMCode_OutputOffImmediate = 65, //!< 65 - M65
+ IoMCode_WaitOnInput = 66, //!< 66 - M66
+ IoMCode_AnalogOutSynced = 67, //!< 67 - M67
+ IoMCode_AnalogOutImmediate = 68, //!< 68 - M68
+} io_mcode_t;
+
+/*! Modal Group M10: User/driver/plugin defined M commands
__NOTE:__ Not used by the core, may be used by private user code, drivers or plugins.
*/
typedef enum {
@@ -213,8 +224,8 @@ typedef enum {
OpenPNP_GetCurrentPosition = 114, //!< 114 - M114
OpenPNP_FirmwareInfo = 115, //!< 115 - M115
Trinamic_DebugReport = 122, //!< 122 - M122, Marlin format
- Trinamic_ReadRegister = 123, //!< 123 - M123,
- Trinamic_WriteRegister = 124, //!< 124 - M124,
+ Trinamic_ReadRegister = 123, //!< 123 - M123
+ Trinamic_WriteRegister = 124, //!< 124 - M124
LaserPPI_Enable = 126, //!< 126 - M126
LaserPPI_Rate = 127, //!< 127 - M127
LaserPPI_PulseLength = 128, //!< 128 - M128
@@ -405,6 +416,11 @@ typedef union {
};
} parameter_words_t;
+typedef struct {
+ spindle_state_t state; //!< {M3,M4,M5}
+ spindle_rpm_mode_t rpm_mode; //!< {G96,G97}
+} spindle_mode_t;
+
// NOTE: When this struct is zeroed, the above defines set the defaults for the system.
typedef struct {
motion_mode_t motion; //!< {G0,G1,G2,G3,G38.2,G80}
@@ -420,9 +436,8 @@ typedef struct {
// control_mode_t control; //!< {G61} NOTE: Don't track. Only default supported.
program_flow_t program_flow; //!< {M0,M1,M2,M30,M60}
coolant_state_t coolant; //!< {M7,M8,M9}
- spindle_state_t spindle; //!< {M3,M4,M5}
+ spindle_mode_t spindle; //!< {M3,M4,M5 and G96,G97}
gc_override_flags_t override_ctrl; //!< {M48,M49,M50,M51,M53,M56}
- spindle_rpm_mode_t spindle_rpm_mode; //!< {G96,G97}
cc_retract_mode_t retract_mode; //!< {G98,G99}
bool scaling_active; //!< {G50,G51}
bool canned_cycle_active;
@@ -472,24 +487,15 @@ typedef struct {
uint32_t tool; //!< Tool number
} tool_data_t;
-// Data used for Constant Surface Speed (CSS) mode calculations.
typedef struct {
- float surface_speed; //!< Surface speed in millimeters/min
- float target_rpm; //!< Target RPM at end of movement
- float max_rpm; //!< Maximum spindle RPM
- float tool_offset; //!< Tool offset
- uint_fast8_t axis; //!< Linear (tool) axis
- bool active;
-} css_data_t;
-
-typedef struct {
- float rpm; //!< RPM
- css_data_t css; //!< Data used for Constant Surface Speed Mode calculations
+ float rpm; //!< Spindle speed
+ spindle_state_t state;
+ spindle_css_data_t *css; //!< Data used for Constant Surface Speed Mode calculations
+ spindle_ptrs_t *hal;
} spindle_t;
/*! \brief Parser state
-
*/
typedef struct {
gc_modal_t modal;
@@ -541,8 +547,8 @@ typedef struct {
gc_values_t values; //!< Parameter values for block.
parameter_words_t words; //!< Bitfield for tracking found parameter values.
output_command_t output_command; //!< Details about M62-M68 output command to execute if present in block.
- uint32_t arc_turns; //
- int32_t spindle_id; //!< Spindle to control, -1 for all
+ uint32_t arc_turns; //
+ spindle_ptrs_t *spindle; //!< Spindle to control, NULL for all
} parser_block_t;
// Initialize the parser
@@ -572,6 +578,8 @@ float *gc_get_scaling (void);
// Get current axis offset.
float gc_get_offset (uint_fast8_t idx);
+spindle_ptrs_t *gc_spindle_get (void);
+
void gc_spindle_off (void);
void gc_coolant_off (void);
diff --git a/grbl.h b/grbl.h
index 55184d5..06c5c41 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20230129
+#define GRBL_BUILD 20230213
#define GRBL_URL "https://github.com/grblHAL"
@@ -163,8 +163,12 @@
// allowable override values and the coarse and fine increments per command received. Please
// note the allowable values in the descriptions following each define.
#define DEFAULT_FEED_OVERRIDE 100 // 100%. Don't change this value.
-#define MAX_FEED_RATE_OVERRIDE 200 // Percent of programmed feed rate (100-255). Usually 120% or 200%
+#ifndef MAX_FEED_RATE_OVERRIDE
+#define MAX_FEED_RATE_OVERRIDE 200 // Percent of programmed feed rate (100-65535). Usually 120% or 200%
+#endif
+#ifndef MIN_FEED_RATE_OVERRIDE
#define MIN_FEED_RATE_OVERRIDE 10 // Percent of programmed feed rate (1-100). Usually 50% or 1%
+#endif
#define FEED_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%.
#define FEED_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%.
@@ -176,8 +180,12 @@
// #define ENABLE_SPINDLE_LINEARIZATION // Uncomment to enable spindle RPM linearization. Requires compatible driver if enabled.
#define SPINDLE_NPWM_PIECES 4 // Maximum number of pieces for spindle RPM linearization, do not change unless more are needed.
#define DEFAULT_SPINDLE_RPM_OVERRIDE 100 // 100%. Don't change this value.
-#define MAX_SPINDLE_RPM_OVERRIDE 200 // Percent of programmed spindle speed (100-255). Usually 200%.
+#ifndef MAX_SPINDLE_RPM_OVERRIDE
+#define MAX_SPINDLE_RPM_OVERRIDE 200 // Percent of programmed spindle speed (100-65535). Usually 200%.
+#endif
+#ifndef MIN_SPINDLE_RPM_OVERRIDE
#define MIN_SPINDLE_RPM_OVERRIDE 10 // Percent of programmed spindle speed (1-100). Usually 10%.
+#endif
#define SPINDLE_OVERRIDE_COARSE_INCREMENT 10 // (1-99). Usually 10%.
#define SPINDLE_OVERRIDE_FINE_INCREMENT 1 // (1-99). Usually 1%.
@@ -221,4 +229,12 @@
#define MAX_STORED_LINE_LENGTH 70 // do not set > 70 unless less than 5 axes are enabled or COMPATIBILITY_LEVEL > 1
#endif
+#if N_SPINDLE > 4
+#define N_SPINDLE_SELECTABLE 4 // Max 4. for now!
+#else
+#define N_SPINDLE_SELECTABLE N_SPINDLE
+#endif
+
+typedef uint_fast16_t override_t;
+
#endif
diff --git a/grbllib.c b/grbllib.c
index 02b7cff..d4e2a52 100644
--- a/grbllib.c
+++ b/grbllib.c
@@ -203,13 +203,15 @@ int grbl_enter (void)
if(driver.ok == 0xFF)
driver.setup = hal.driver_setup(&settings);
- spindle_select(settings.spindle.flags.type);
-
#ifdef ENABLE_SPINDLE_LINEARIZATION
driver.linearization = hal.driver_cap.spindle_pwm_linearization;
#endif
- driver.spindle = hal.spindle.get_pwm == NULL || hal.spindle.update_pwm != NULL;
+ if((driver.spindle = spindle_select(settings.spindle.flags.type))) {
+ spindle_ptrs_t *spindle = spindle_get(0);
+ driver.spindle = spindle->get_pwm == NULL || spindle->update_pwm != NULL;
+ } else
+ driver.spindle = spindle_select(spindle_add_null());
if(driver.ok != 0xFF) {
sys.alarm = Alarm_SelftestFailed;
@@ -218,9 +220,7 @@ int grbl_enter (void)
hal.stepper.enable(settings.steppers.deenergize);
- if(hal.spindle.set_state)
- hal.spindle.set_state((spindle_state_t){0}, 0.0f);
-
+ spindle_all_off();
hal.coolant.set_state((coolant_state_t){0});
if(hal.get_position)
@@ -253,6 +253,8 @@ int grbl_enter (void)
// will return to this loop to be cleanly re-initialized.
while(looping) {
+ spindle_num_t spindle_num = N_SYS_SPINDLE;
+
// Reset report entry points
report_init_fns();
@@ -264,7 +266,10 @@ int grbl_enter (void)
sys.var5399 = -2; // Clear last M66 result
sys.override.feed_rate = DEFAULT_FEED_OVERRIDE; // Set to 100%
sys.override.rapid_rate = DEFAULT_RAPID_OVERRIDE; // Set to 100%
- sys.override.spindle_rpm = DEFAULT_SPINDLE_RPM_OVERRIDE; // Set to 100%
+ do {
+ if(spindle_is_enabled(--spindle_num))
+ spindle_get(spindle_num)->param->override_pct = DEFAULT_SPINDLE_RPM_OVERRIDE; // Set to 100%
+ } while(spindle_num);
sys.flags.auto_reporting = settings.report_interval != 0;
if(settings.parking.flags.enabled)
diff --git a/hal.h b/hal.h
index d160f2b..46526e3 100644
--- a/hal.h
+++ b/hal.h
@@ -583,7 +583,7 @@ typedef struct {
homing_ptrs_t homing; //!< Handlers for limit switches, used by homing cycle.
control_signals_ptrs_t control; //!< Handlers for control switches.
coolant_ptrs_t coolant; //!< Handlers for coolant.
- spindle_ptrs_t spindle; //!< Handlers for spindle.
+ spindle_data_ptrs_t spindle_data; //!< Handlers for getting/resetting spindle data (RPM, angular position, ...).
stepper_ptrs_t stepper; //!< Handlers for stepper motors.
io_stream_t stream; //!< Handlers for stream I/O.
stream_select_ptr stream_select; //!< Optional handler for switching between I/O streams.
diff --git a/machine_limits.c b/machine_limits.c
index 2c841b3..1b4723d 100644
--- a/machine_limits.c
+++ b/machine_limits.c
@@ -161,7 +161,6 @@ static bool limits_pull_off (axes_signals_t axis, float distance)
} while(idx);
plan_data.feed_rate = settings.homing.seek_rate * sqrtf(n_axis); // Adjust so individual axes all move at pull-off rate.
- plan_data.condition.spindle = gc_state.modal.spindle;
plan_data.condition.coolant = gc_state.modal.coolant;
memcpy(&plan_data.spindle, &gc_state.spindle, sizeof(spindle_t));
@@ -250,16 +249,14 @@ static bool limits_homing_cycle (axes_signals_t cycle, axes_signals_t auto_squar
limit_signals_t limits_state;
squaring_mode_t squaring_mode = SquaringMode_Both;
coord_data_t target;
- plan_line_data_t plan_data;
+ plan_line_data_t plan_data = {
+ .condition.system_motion = On,
+ .condition.no_feed_override = On,
+ .line_number = DEFAULT_HOMING_CYCLE_LINE_NUMBER
+ };
// Initialize plan data struct for homing motion.
-
- memset(&plan_data, 0, sizeof(plan_line_data_t));
- plan_data.condition.system_motion = On;
- plan_data.condition.no_feed_override = On;
- plan_data.line_number = DEFAULT_HOMING_CYCLE_LINE_NUMBER;
memcpy(&plan_data.spindle, &gc_state.spindle, sizeof(spindle_t));
- plan_data.condition.spindle = gc_state.modal.spindle;
plan_data.condition.coolant = gc_state.modal.coolant;
uint_fast8_t idx = N_AXIS;
diff --git a/motion_control.c b/motion_control.c
index 6a934e8..636184f 100644
--- a/motion_control.c
+++ b/motion_control.c
@@ -176,9 +176,9 @@ bool mc_line (float *target, plan_line_data_t *pl_data)
// Plan and queue motion into planner buffer.
// While in M3 laser mode also set spindle state and force a buffer sync
// if there is a coincident position passed.
- if(!plan_buffer_line(target, pl_data) && sys.mode == Mode_Laser && pl_data->condition.spindle.on && !pl_data->condition.spindle.ccw) {
+ if(!plan_buffer_line(target, pl_data) && pl_data->spindle.hal->cap.laser && pl_data->spindle.state.on && !pl_data->spindle.state.ccw) {
protocol_buffer_synchronize();
- hal.spindle.set_state(pl_data->condition.spindle, pl_data->spindle.rpm);
+ pl_data->spindle.hal->set_state(pl_data->spindle.state, pl_data->spindle.rpm);
}
#ifdef KINEMATICS_API
@@ -582,7 +582,7 @@ void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_
mc_dwell(canned->dwell);
if(canned->spindle_off)
- hal.spindle.set_state((spindle_state_t){0}, 0.0f);
+ pl_data->spindle.hal->set_state((spindle_state_t){0}, 0.0f);
// rapid retract
switch(motion) {
@@ -603,7 +603,7 @@ void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_
return;
if(canned->spindle_off)
- spindle_sync(0, gc_state.modal.spindle, pl_data->spindle.rpm);
+ spindle_sync(pl_data->spindle.hal, gc_state.modal.spindle.state, pl_data->spindle.rpm);
}
// rapid move to next position if incremental mode
@@ -670,7 +670,7 @@ void mc_thread (plan_line_data_t *pl_data, float *position, gc_thread_data *thre
// TODO: Add to initial move to compensate for acceleration distance?
/*
- float acc_distance = pl_data->feed_rate * hal.spindle.get_data(SpindleData_RPM)->rpm / settings.acceleration[Z_AXIS];
+ float acc_distance = pl_data->feed_rate * pl_data->spindle.hal->get_data(SpindleData_RPM)->rpm / settings.acceleration[Z_AXIS];
acc_distance = acc_distance * acc_distance * settings.acceleration[Z_AXIS] * 0.5f;
*/
@@ -694,9 +694,9 @@ void mc_thread (plan_line_data_t *pl_data, float *position, gc_thread_data *thre
if(!protocol_buffer_synchronize() && state_get() != STATE_IDLE) // Wait until any previous moves are finished.
return;
- pl_data->condition.rapid_motion = Off; // Clear rapid motion condition flag,
- pl_data->condition.spindle.synchronized = On; // enable spindle sync for cut
- pl_data->overrides.feed_hold_disable = On; // and disable feed hold
+ pl_data->condition.rapid_motion = Off; // Clear rapid motion condition flag,
+ pl_data->spindle.state.synchronized = On; // enable spindle sync for cut
+ pl_data->overrides.feed_hold_disable = On; // and disable feed hold
// Cut thread pass
@@ -723,8 +723,8 @@ void mc_thread (plan_line_data_t *pl_data, float *position, gc_thread_data *thre
return;
}
- pl_data->condition.rapid_motion = On; // Set rapid motion condition flag and
- pl_data->condition.spindle.synchronized = Off; // disable spindle sync for retract & reposition
+ pl_data->condition.rapid_motion = On; // Set rapid motion condition flag and
+ pl_data->spindle.state.synchronized = Off; // disable spindle sync for retract & reposition
if(passes > 1) {
@@ -861,7 +861,7 @@ status_code_t mc_homing_cycle (axes_signals_t cycle)
hal.limits.enable(false, true); // Disable hard limits pin change register for cycle duration
// Turn off spindle and coolant (and update parser state)
- if(hal.spindle.get_state().on)
+ if(spindle_is_on())
gc_spindle_off();
if(hal.coolant.get_state().mask)
diff --git a/ngc_params.c b/ngc_params.c
index d211f4b..b445a5e 100644
--- a/ngc_params.c
+++ b/ngc_params.c
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2021 Terje Io
+ Copyright (c) 2021-2023 Terje Io
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -424,11 +424,11 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id)
break;
case NGCParam_spindle_rpm_mode:
- value = gc_state.modal.spindle_rpm_mode == SpindleSpeedMode_RPM ? 1.0f : 0.0f;
+ value = gc_state.modal.spindle.rpm_mode == SpindleSpeedMode_RPM ? 1.0f : 0.0f;
break;
case NGCParam_spindle_css_mode:
- value = gc_state.modal.spindle_rpm_mode == SpindleSpeedMode_CSS ? 1.0f : 0.0f;
+ value = gc_state.modal.spindle.rpm_mode == SpindleSpeedMode_CSS ? 1.0f : 0.0f;
break;
case NGCParam_ijk_absolute_mode:
@@ -444,11 +444,11 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id)
break;
case NGCParam_spindle_on:
- value = gc_state.modal.spindle.on ? 1.0f : 0.0f;
+ value = gc_state.modal.spindle.state.on ? 1.0f : 0.0f;
break;
case NGCParam_spindle_cw:
- value = gc_state.modal.spindle.ccw ? 1.0f : 0.0f;
+ value = gc_state.modal.spindle.state.ccw ? 1.0f : 0.0f;
break;
case NGCParam_mist:
diff --git a/planner.c b/planner.c
index e9284c1..f79bbc6 100644
--- a/planner.c
+++ b/planner.c
@@ -315,13 +315,13 @@ bool plan_check_full_buffer (void)
// NOTE: All system motion commands, such as homing/parking, are not subject to overrides.
float plan_compute_profile_nominal_speed (plan_block_t *block)
{
- float nominal_speed = block->condition.spindle.synchronized ? block->programmed_rate * hal.spindle.get_data(SpindleData_RPM)->rpm : block->programmed_rate;
+ float nominal_speed = block->spindle.state.synchronized ? block->programmed_rate * block->spindle.hal->get_data(SpindleData_RPM)->rpm : block->programmed_rate;
if (block->condition.rapid_motion)
- nominal_speed *= (0.01f * sys.override.rapid_rate);
+ nominal_speed *= (0.01f * (float)sys.override.rapid_rate);
else {
if (!block->condition.no_feed_override)
- nominal_speed *= (0.01f * sys.override.feed_rate);
+ nominal_speed *= (0.01f * (float)sys.override.feed_rate);
if (nominal_speed > block->rapid_rate)
nominal_speed = block->rapid_rate;
}
@@ -465,21 +465,24 @@ bool plan_buffer_line (float *target, plan_line_data_t *pl_data)
} while(idx);
- // Calculate RPMs to be used for Constant Surface Speed calculations
- if(block->condition.is_rpm_pos_adjusted) {
+ // Calculate RPMs to be used for Constant Surface Speed (CSS) calculations.
+ if(block->spindle.css) {
+
float pos;
- if((pos = (float)position_steps[block->spindle.css.axis] / settings.axis[block->spindle.css.axis].steps_per_mm - block->spindle.css.tool_offset) > 0.0f) {
- block->spindle.rpm = block->spindle.css.surface_speed / (pos * (float)(2.0f * M_PI));
- if(block->spindle.rpm > block->spindle.css.max_rpm)
- block->spindle.rpm = block->spindle.css.max_rpm;
+
+ if((pos = (float)position_steps[block->spindle.css->axis] / settings.axis[block->spindle.css->axis].steps_per_mm - block->spindle.css->tool_offset) > 0.0f) {
+ if((block->spindle.rpm = block->spindle.css->surface_speed / (pos * (float)(2.0f * M_PI))) > block->spindle.css->max_rpm)
+ block->spindle.rpm = block->spindle.css->max_rpm;
} else
- block->spindle.rpm = block->spindle.css.max_rpm;
- if((pos = target[block->spindle.css.axis] - block->spindle.css.tool_offset) > 0.0f) {
- block->spindle.css.target_rpm = block->spindle.css.surface_speed / (pos * (float)(2.0f * M_PI));
- if(block->spindle.css.target_rpm > block->spindle.css.max_rpm)
- block->spindle.css.target_rpm = block->spindle.css.max_rpm;
+ block->spindle.rpm = block->spindle.css->max_rpm;
+
+ if((pos = target[block->spindle.css->axis] - block->spindle.css->tool_offset) > 0.0f) {
+ if((block->spindle.css->target_rpm = block->spindle.css->surface_speed / (pos * (float)(2.0f * M_PI))) > block->spindle.css->max_rpm)
+ block->spindle.css->target_rpm = block->spindle.css->max_rpm;
} else
- block->spindle.css.target_rpm = block->spindle.css.max_rpm;
+ block->spindle.css->target_rpm = block->spindle.css->max_rpm;
+
+ block->spindle.css->delta_rpm = block->spindle.css->target_rpm - block->spindle.rpm;
}
// Bail if this is a zero-length block. Highly unlikely to occur.
@@ -662,7 +665,7 @@ void plan_cycle_reinitialize (void)
}
// Set feed overrides
-void plan_feed_override (uint_fast8_t feed_override, uint_fast8_t rapid_override)
+void plan_feed_override (override_t feed_override, override_t rapid_override)
{
bool feedrate_changed = false, rapidrate_changed = false;
@@ -673,8 +676,8 @@ void plan_feed_override (uint_fast8_t feed_override, uint_fast8_t rapid_override
if ((feedrate_changed = feed_override != sys.override.feed_rate) ||
(rapidrate_changed = rapid_override != sys.override.rapid_rate)) {
- sys.override.feed_rate = (uint8_t)feed_override;
- sys.override.rapid_rate = (uint8_t)rapid_override;
+ sys.override.feed_rate = feed_override;
+ sys.override.rapid_rate = rapid_override;
sys.report.overrides = On; // Set to report change immediately
plan_update_velocity_profile_parameters();
plan_cycle_reinitialize();
diff --git a/planner.h b/planner.h
index c3247a2..5adb813 100644
--- a/planner.h
+++ b/planner.h
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2019-2022 Terje Io
+ Copyright (c) 2019-2023 Terje Io
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
@@ -34,10 +34,8 @@ typedef union {
no_feed_override :1,
inverse_time :1,
is_rpm_rate_adjusted :1,
- is_rpm_pos_adjusted :1,
is_laser_ppi_mode :1,
- unassigned :7;
- spindle_state_t spindle;
+ unassigned :8;
coolant_state_t coolant;
};
} planner_cond_t;
@@ -58,22 +56,22 @@ typedef struct plan_block {
// Fields used by the motion planner to manage acceleration. Some of these values may be updated
// by the stepper module during execution of special motion cases for replanning purposes.
- float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2
- float max_entry_speed_sqr; // Maximum allowable entry speed based on the minimum of junction limit and
- // neighboring nominal speeds with overrides in (mm/min)^2
- float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2). Does not change.
- float millimeters; // The remaining distance for this block to be executed in (mm).
- // NOTE: This value may be altered by stepper algorithm during execution.
+ float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2
+ float max_entry_speed_sqr; // Maximum allowable entry speed based on the minimum of junction limit and
+ // neighboring nominal speeds with overrides in (mm/min)^2
+ float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2). Does not change.
+ float millimeters; // The remaining distance for this block to be executed in (mm).
+ // NOTE: This value may be altered by stepper algorithm during execution.
// Stored rate limiting data used by planner when changes occur.
- float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2
- float rapid_rate; // Axis-limit adjusted maximum rate for this block direction in (mm/min)
- float programmed_rate; // Programmed rate of this block (mm/min).
+ float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2
+ float rapid_rate; // Axis-limit adjusted maximum rate for this block direction in (mm/min)
+ float programmed_rate; // Programmed rate of this block (mm/min).
// Stored spindle speed data used by spindle overrides and resuming methods.
- spindle_t spindle; // Block spindle speed. Copied from pl_line_data.
+ spindle_t spindle; // Block spindle parameters. Copied from pl_line_data.
- char *message; // Message to be displayed when block is executed.
+ char *message; // Message to be displayed when block is executed.
output_command_t *output_commands;
struct plan_block *prev, *next; // Linked list pointers, DO NOT MOVE - these MUST be the last elements in the struct!
} plan_block_t;
@@ -83,7 +81,7 @@ typedef struct plan_block {
typedef struct {
float feed_rate; // Desired feed rate for line motion. Value is ignored, if rapid motion.
// float blending_tolerance; // Motion blending tolerance
- spindle_t spindle; // Desired spindle speed through line motion.
+ 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
int32_t line_number; // Desired line number to report when executing.
@@ -143,6 +141,6 @@ uint_fast16_t plan_get_block_buffer_available (void);
// Returns the status of the block ring buffer. True, if buffer is full.
bool plan_check_full_buffer (void);
-void plan_feed_override (uint_fast8_t feed_override, uint_fast8_t rapid_override);
+void plan_feed_override (override_t feed_override, override_t rapid_override);
#endif
diff --git a/plugins.h b/plugins.h
index 693128a..0e18f8a 100644
--- a/plugins.h
+++ b/plugins.h
@@ -65,9 +65,18 @@ typedef union {
} network_services_t;
typedef char ssid_t[65];
+typedef uint8_t bssid_t[6];
+typedef char username_t[33];
typedef char password_t[33];
typedef char hostname_t[33];
-typedef char sntp_server_t[129]; // URI
+typedef char uri_t[65];
+
+typedef struct {
+ char ip[16];
+ uint16_t port;
+ username_t user;
+ password_t password;
+} mqtt_settings_t;
typedef struct {
char ip[16];
@@ -80,6 +89,9 @@ typedef struct {
uint16_t ftp_port;
ip_mode_t ip_mode;
network_services_t services;
+#if MQTT_ENABLE
+ mqtt_settings_t mqtt;
+#endif
} network_settings_t;
typedef enum {
@@ -94,12 +106,14 @@ typedef struct {
bool link_up;
uint16_t mbps;
char mac[18];
+ char mqtt_client_id[18];
grbl_wifi_mode_t wifi_mode;
network_settings_t status;
} network_info_t;
typedef struct {
ssid_t ssid;
+ bssid_t bssid;
password_t password;
char country[4];
uint8_t channel;
@@ -149,11 +163,13 @@ typedef enum {
typedef union {
uint8_t events;
struct {
- uint8_t position_changed :1,
- click :1,
- dbl_click :1,
- long_click :1,
- index_pulse :1;
+ uint8_t position_changed :1,
+ direction_changed :1,
+ click :1,
+ dbl_click :1,
+ long_click :1,
+ index_pulse :1,
+ unused :2;
};
} encoder_event_t;
diff --git a/plugins_init.h b/plugins_init.h
index 1e686ff..da717b4 100644
--- a/plugins_init.h
+++ b/plugins_init.h
@@ -43,11 +43,6 @@
vfd_init();
#endif
-#if N_SPINDLE > 1
- extern void spindle_select_init(void);
- spindle_select_init();
-#endif
-
#ifndef GRBL_ESP32 // ESP32 has its own bluetooth_init
#if BLUETOOTH_ENABLE
extern void bluetooth_init (void);
@@ -93,6 +88,11 @@
extern void my_plugin_init (void);
my_plugin_init();
+#if N_SPINDLE > 1
+ extern void spindle_select_init(void);
+ spindle_select_init();
+#endif
+
// Third party plugin definitions.
// The code for these has to be downloaded from the source and placed in the same folder as driver.c
// Note: Third party plugins may have more than one implementation, there is no "owner" of plugins listed here.
@@ -108,6 +108,11 @@
probe_relay_init();
#endif
+#if DISPLAY_ENABLE
+ void display_init (void);
+ display_init();
+#endif
+
#if STATUS_LIGHT_ENABLE
extern void status_light_init (void);
status_light_init();
diff --git a/protocol.c b/protocol.c
index 3840d84..edfd90b 100644
--- a/protocol.c
+++ b/protocol.c
@@ -180,7 +180,7 @@ bool protocol_main_loop (void)
// Ensure spindle and coolant is switched off on a cold start
if(sys.cold_start) {
- hal.spindle.set_state((spindle_state_t){0}, 0.0f);
+ spindle_all_off();
hal.coolant.set_state((coolant_state_t){0});
if(realtime_queue.head != realtime_queue.tail)
system_set_exec_state_flag(EXEC_RT_COMMAND); // execute any boot up commands
@@ -411,7 +411,7 @@ bool protocol_exec_rt_system (void)
if((sys.reset_pending = !!(sys.rt_exec_state & EXEC_RESET))) {
// Kill spindle and coolant.
killed = true;
- hal.spindle.set_state((spindle_state_t){0}, 0.0f);
+ spindle_all_off();
hal.coolant.set_state((coolant_state_t){0});
}
@@ -468,7 +468,7 @@ bool protocol_exec_rt_system (void)
if(!killed) {
// Kill spindle and coolant.
- hal.spindle.set_state((spindle_state_t){0}, 0.0f);
+ spindle_all_off();
hal.coolant.set_state((coolant_state_t){0});
}
@@ -500,11 +500,7 @@ bool protocol_exec_rt_system (void)
sys.override.control = gc_state.modal.override_ctrl;
gc_state.tool_change = false;
- gc_state.modal.spindle_rpm_mode = SpindleSpeedMode_RPM;
-
- // Kill spindle and coolant. TODO: Check Mach3 behaviour?
- gc_spindle_off();
- gc_coolant_off();
+ gc_state.modal.spindle.rpm_mode = SpindleSpeedMode_RPM;
// Tell driver/plugins about reset.
hal.driver_reset();
@@ -523,6 +519,11 @@ bool protocol_exec_rt_system (void)
} else*/
st_reset();
sync_position();
+
+ // Kill spindle and coolant. TODO: Check Mach3 behaviour?
+ gc_spindle_off();
+ gc_coolant_off();
+
flush_override_buffers();
if(!((state_get() == STATE_ALARM) && (sys.alarm == Alarm_LimitsEngaged || sys.alarm == Alarm_HomingRequried)))
state_set(hal.control.get_state().safety_door_ajar ? STATE_SAFETY_DOOR : STATE_IDLE);
@@ -567,8 +568,8 @@ bool protocol_exec_rt_system (void)
if((rt_exec = get_feed_override())) {
- int_fast16_t new_f_override = sys.override.feed_rate;
- uint_fast8_t new_r_override = sys.override.rapid_rate;
+ override_t new_f_override = sys.override.feed_rate;
+ override_t new_r_override = sys.override.rapid_rate;
do {
@@ -611,13 +612,14 @@ bool protocol_exec_rt_system (void)
} while((rt_exec = get_feed_override()));
- plan_feed_override((uint_fast8_t)new_f_override, new_r_override);
+ plan_feed_override(new_f_override, new_r_override);
}
if((rt_exec = get_accessory_override())) {
bool spindle_stop = false;
- int_fast16_t last_s_override = sys.override.spindle_rpm;
+ spindle_ptrs_t *spindle = gc_spindle_get();
+ override_t last_s_override = spindle->param->override_pct;
coolant_state_t coolant_state = gc_state.modal.coolant;
do {
@@ -670,7 +672,7 @@ bool protocol_exec_rt_system (void)
} while((rt_exec = get_accessory_override()));
- spindle_set_override((uint_fast8_t)last_s_override);
+ spindle_set_override(spindle, last_s_override);
// NOTE: Since coolant state always performs a planner sync whenever it changes, the current
// run state can be determined by checking the parser state.
@@ -681,7 +683,7 @@ bool protocol_exec_rt_system (void)
grbl.on_override_changed(OverrideChanged_CoolantState);
}
- if (spindle_stop && state_get() == STATE_HOLD && gc_state.modal.spindle.on) {
+ if (spindle_stop && state_get() == STATE_HOLD && gc_state.modal.spindle.state.on) {
// Spindle stop override allowed only while in HOLD state.
// NOTE: Report flag is set in spindle_set_state() when spindle stop is executed.
if (!sys.override.spindle_stop.value)
diff --git a/report.c b/report.c
index 3437a61..8fe5620 100644
--- a/report.c
+++ b/report.c
@@ -53,6 +53,8 @@ static uint8_t wco_counter = 0; // Tracks when to add work coordinate offse
static const char vbar[2] = { '|', '\0' };
static bool report_setting (const setting_detail_t *setting, uint_fast16_t offset, void *data);
+static status_code_t report_status_message (status_code_t status_code);
+static message_code_t report_feedback_message (message_code_t id);
static const report_t report_fns = {
.setting = report_setting,
@@ -230,7 +232,7 @@ void report_init_fns (void)
// operation. Errors events can originate from the g-code parser, settings module, or asynchronously
// from a critical error, such as a triggered hard limit. Interface should always monitor for these
// responses.
-status_code_t report_status_message (status_code_t status_code)
+static status_code_t report_status_message (status_code_t status_code)
{
switch(status_code) {
@@ -284,7 +286,7 @@ void report_message (const char *msg, message_type_t type)
// messages such as setup warnings, switch toggling, and how to exit alarms.
// NOTE: For interfaces, messages are always placed within brackets. And if silent mode
// is installed, the message number codes are less than zero.
-message_code_t report_feedback_message (message_code_t id)
+static message_code_t report_feedback_message (message_code_t id)
{
const char *msg = NULL;
uint_fast16_t idx = 0;
@@ -681,7 +683,7 @@ void report_gcode_modes (void)
#endif
- if(sys.mode == Mode_Lathe)
+ if(settings.mode == Mode_Lathe)
hal.stream.write(gc_state.modal.diameter_mode ? " G7" : " G8");
hal.stream.write(" G");
@@ -694,8 +696,8 @@ void report_gcode_modes (void)
hal.stream.write(" G");
hal.stream.write(uitoa((uint32_t)(94 - gc_state.modal.feed_mode)));
- if(sys.mode == Mode_Lathe && hal.spindle.cap.variable)
- hal.stream.write(gc_state.modal.spindle_rpm_mode == SpindleSpeedMode_RPM ? " G97" : " G96");
+ if(settings.mode == Mode_Lathe && gc_spindle_get()->cap.variable)
+ hal.stream.write(gc_state.modal.spindle.rpm_mode == SpindleSpeedMode_RPM ? " G97" : " G96");
#if COMPATIBILITY_LEVEL < 10
@@ -747,7 +749,7 @@ void report_gcode_modes (void)
}
}
- hal.stream.write(gc_state.modal.spindle.on ? (gc_state.modal.spindle.ccw ? " M4" : " M3") : " M5");
+ hal.stream.write(gc_state.modal.spindle.state.on ? (gc_state.modal.spindle.state.ccw ? " M4" : " M3") : " M5");
if(gc_state.tool_change)
hal.stream.write(" M6");
@@ -779,7 +781,7 @@ void report_gcode_modes (void)
hal.stream.write(appendbuf(2, " F", get_rate_value(gc_state.feed_rate)));
- if(hal.spindle.cap.variable)
+ if(gc_spindle_get()->cap.variable)
hal.stream.write(appendbuf(2, " S", ftoa(gc_state.spindle.rpm, N_DECIMAL_RPMVALUE)));
hal.stream.write("]" ASCII_EOL);
@@ -1177,19 +1179,41 @@ void report_realtime_status (void)
hal.stream.write_all(appendbuf(2, "|Ln:", uitoa((uint32_t)cur_block->line_number)));
}
- spindle_state_t sp_state = hal.spindle.get_state();
+ spindle_ptrs_t *spindle_0;
+ spindle_state_t spindle_0_state;
+
+ spindle_0 = spindle_get(0);
+ spindle_0_state = spindle_0->get_state();
// Report realtime feed speed
if(settings.status_report.feed_speed) {
- if(hal.spindle.cap.variable) {
+ if(spindle_0->cap.variable) {
hal.stream.write_all(appendbuf(2, "|FS:", get_rate_value(st_get_realtime_rate())));
- hal.stream.write_all(appendbuf(2, ",", uitoa(sp_state.on ? lroundf(sys.spindle_rpm) : 0)));
- if(hal.spindle.get_data /* && sys.mpg_mode */)
- hal.stream.write_all(appendbuf(2, ",", uitoa(lroundf(hal.spindle.get_data(SpindleData_RPM)->rpm))));
+ hal.stream.write_all(appendbuf(2, ",", uitoa(spindle_0_state.on ? lroundf(spindle_0->param->rpm_overridden) : 0)));
+ if(spindle_0->get_data /* && sys.mpg_mode */)
+ hal.stream.write_all(appendbuf(2, ",", uitoa(lroundf(spindle_0->get_data(SpindleData_RPM)->rpm))));
} else
hal.stream.write_all(appendbuf(2, "|F:", get_rate_value(st_get_realtime_rate())));
}
+#if N_SYS_SPINDLE > 1
+
+ for(idx = 1; idx < N_SYS_SPINDLE; idx++) {
+
+ spindle_ptrs_t *spindle_n;
+ spindle_state_t spindle_n_state;
+
+ if((spindle_n = spindle_get(idx))) {
+ spindle_n_state = spindle_n->get_state();
+ hal.stream.write_all(appendbuf(3, "|SP", uitoa(idx), ":"));
+ hal.stream.write_all(appendbuf(3, uitoa(spindle_n_state.on ? lroundf(spindle_n->param->rpm_overridden) : 0), ",,", spindle_n_state.on ? (spindle_n_state.ccw ? "C" : "S") : ""));
+ if(settings.status_report.overrides)
+ hal.stream.write_all(appendbuf(2, ",", uitoa(spindle_n->param->override_pct)));
+ }
+ }
+
+#endif
+
if(settings.status_report.pin_state) {
axes_signals_t lim_pin_state = limit_signals_merge(hal.limits.get_state());
@@ -1236,7 +1260,7 @@ void report_realtime_status (void)
if (override_counter > 0 && !sys.report.overrides)
override_counter--;
else if((sys.report.overrides = !sys.report.wco)) {
- sys.report.spindle = sys.report.spindle || hal.spindle.get_state().on;
+ sys.report.spindle = sys.report.spindle || spindle_0_state.on;
sys.report.coolant = sys.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
@@ -1260,7 +1284,7 @@ void report_realtime_status (void)
if(sys.report.overrides) {
hal.stream.write_all(appendbuf(2, "|Ov:", uitoa((uint32_t)sys.override.feed_rate)));
hal.stream.write_all(appendbuf(2, ",", uitoa((uint32_t)sys.override.rapid_rate)));
- hal.stream.write_all(appendbuf(2, ",", uitoa((uint32_t)sys.override.spindle_rpm)));
+ hal.stream.write_all(appendbuf(2, ",", uitoa((uint32_t)spindle_0->param->override_pct)));
}
if(sys.report.spindle || sys.report.coolant || sys.report.tool || gc_state.tool_change) {
@@ -1271,12 +1295,12 @@ void report_realtime_status (void)
strcpy(buf, "|A:");
- if (sp_state.on)
+ if (spindle_0_state.on)
- *append++ = sp_state.ccw ? 'C' : 'S';
+ *append++ = spindle_0_state.ccw ? 'C' : 'S';
#if COMPATIBILITY_LEVEL == 0
- if(sp_state.encoder_error && hal.driver_cap.spindle_sync)
+ if(spindle_0_state.encoder_error && hal.driver_cap.spindle_sync)
*append++ = 'E';
#endif
@@ -1309,7 +1333,7 @@ void report_realtime_status (void)
hal.stream.write_all(appendbuf(2, ",", uitoa(sys.homed.mask)));
}
- if(sys.report.xmode && sys.mode == Mode_Lathe)
+ if(sys.report.xmode && settings.mode == Mode_Lathe)
hal.stream.write_all(gc_state.modal.diameter_mode ? "|D:1" : "|D:0");
if(sys.report.tool)
@@ -2038,23 +2062,25 @@ status_code_t report_current_limit_state (sys_state_t state, char *args)
// Prints spindle data (encoder pulse and index count, angular position).
status_code_t report_spindle_data (sys_state_t state, char *args)
{
- if(hal.spindle.get_data) {
+ spindle_ptrs_t *spindle = gc_spindle_get();
- float apos = hal.spindle.get_data(SpindleData_AngularPosition)->angular_position;
- spindle_data_t *spindle = hal.spindle.get_data(SpindleData_Counters);
+ if(spindle->get_data) {
+
+ float apos = spindle->get_data(SpindleData_AngularPosition)->angular_position;
+ spindle_data_t *data = spindle->get_data(SpindleData_Counters);
hal.stream.write("[SPINDLE:");
- hal.stream.write(uitoa(spindle->index_count));
+ hal.stream.write(uitoa(data->index_count));
hal.stream.write(",");
- hal.stream.write(uitoa(spindle->pulse_count));
+ hal.stream.write(uitoa(data->pulse_count));
hal.stream.write(",");
- hal.stream.write(uitoa(spindle->error_count));
+ hal.stream.write(uitoa(data->error_count));
hal.stream.write(",");
hal.stream.write(ftoa(apos, 3));
hal.stream.write("]" ASCII_EOL);
}
- return hal.spindle.get_data ? Status_OK : Status_InvalidStatement;
+ return spindle->get_data ? Status_OK : Status_InvalidStatement;
}
static const char *get_pinname (pin_function_t function)
@@ -2122,6 +2148,30 @@ status_code_t report_time (void)
return ok ? Status_OK : Status_InvalidStatement;
}
+static void report_spindle (spindle_info_t *spindle)
+{
+ hal.stream.write(uitoa(spindle->id));
+ hal.stream.write(" - ");
+ hal.stream.write(spindle->name);
+ if(spindle->enabled) {
+#if N_SPINDLE > 1
+ hal.stream.write(", enabled as spindle ");
+ hal.stream.write(uitoa(spindle->num));
+#else
+ hal.stream.write(", active");
+#endif
+ }
+ hal.stream.write(ASCII_EOL);
+}
+
+status_code_t report_spindles (void)
+{
+ if(!spindle_enumerate_spindles(report_spindle))
+ hal.stream.write("No spindles registered." ASCII_EOL);
+
+ return Status_OK;
+}
+
void report_pid_log (void)
{
#ifdef PID_LOG
diff --git a/report.h b/report.h
index e078fdb..b489fa9 100644
--- a/report.h
+++ b/report.h
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2018-2021 Terje Io
+ Copyright (c) 2018-2023 Terje Io
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
Grbl is free software: you can redistribute it and/or modify
@@ -44,8 +44,6 @@ typedef enum {
void report_init (void);
void report_init_fns (void);
-// Prints system status messages.
-status_code_t report_status_message (status_code_t status_code);
// Prints system alarm messages.
alarm_code_t report_alarm_message (alarm_code_t alarm_code);
@@ -53,9 +51,6 @@ alarm_code_t report_alarm_message (alarm_code_t alarm_code);
// Prints feedback message, typically from gcode.
void report_message (const char *msg, message_type_t type);
-// Prints miscellaneous feedback messages.
-message_code_t report_feedback_message (message_code_t message_code);
-
// Prints welcome message.
void report_init_message (void);
@@ -114,9 +109,12 @@ status_code_t report_current_limit_state (sys_state_t state, char *args);
// Prints spindle data (encoder pulse and index count, angular position).
status_code_t report_spindle_data (sys_state_t state, char *args);
-// Prints pin assignments
+// Prints pin assignments.
status_code_t report_pins (sys_state_t state, char *args);
+// Prints registered spindles.
+status_code_t report_spindles (void);
+
// Prints current RTC datetime in ISO8601 format (when available)
status_code_t report_time (void);
diff --git a/settings.c b/settings.c
index 494c922..40e216a 100644
--- a/settings.c
+++ b/settings.c
@@ -107,8 +107,8 @@ PROGMEM const settings_t defaults = {
#if DEFAULT_HOMING_ENABLE
.homing.flags.enabled = DEFAULT_HOMING_ENABLE,
.homing.flags.init_lock = DEFAULT_HOMING_INIT_LOCK,
- .homing.flags.single_axis_commands = HOMING_SINGLE_AXIS_COMMANDS,
- .homing.flags.force_set_origin = HOMING_FORCE_SET_ORIGIN,
+ .homing.flags.single_axis_commands = DEFAULT_HOMING_SINGLE_AXIS_COMMANDS,
+ .homing.flags.force_set_origin = DEFAULT_HOMING_FORCE_SET_ORIGIN,
.homing.flags.manual = DEFAULT_HOMING_ALLOW_MANUAL,
.homing.flags.override_locks = DEFAULT_HOMING_OVERRIDE_LOCKS,
.homing.flags.keep_on_reset = DEFAULT_HOMING_KEEP_STATUS_ON_RESET,
@@ -392,6 +392,7 @@ static uint32_t get_int (setting_id_t id);
static bool is_setting_available (const setting_detail_t *setting);
static bool is_group_available (const setting_detail_t *setting);
+static bool machine_mode_changed = false;
static char control_signals[] = "Reset,Feed hold,Cycle start,Safety door,Block delete,Optional stop,EStop,Probe connected,Motor fault";
static char spindle_signals[] = "Spindle enable,Spindle direction,PWM";
static char coolant_signals[] = "Flood,Mist";
@@ -808,7 +809,7 @@ static status_code_t set_enable_invert_mask (setting_id_t id, uint_fast16_t int_
static status_code_t set_limits_invert_mask (setting_id_t id, uint_fast16_t int_value)
{
- settings.limits.invert.mask = (int_value ? ~(INVERT_LIMIT_BIT_MASK) : INVERT_LIMIT_BIT_MASK) & AXES_BITMASK;
+ settings.limits.invert.mask = (int_value ? ~(DEFAULT_LIMIT_SIGNALS_INVERT_MASK) : DEFAULT_LIMIT_SIGNALS_INVERT_MASK) & AXES_BITMASK;
return Status_OK;
}
@@ -981,6 +982,13 @@ static status_code_t set_homing_enable (setting_id_t id, uint_fast16_t int_value
if (bit_istrue(int_value, bit(0))) {
#if COMPATIBILITY_LEVEL > 1
settings.homing.flags.enabled = On;
+ settings.homing.flags.init_lock = DEFAULT_HOMING_INIT_LOCK;
+ settings.homing.flags.single_axis_commands = DEFAULT_HOMING_SINGLE_AXIS_COMMANDS;
+ settings.homing.flags.force_set_origin = DEFAULT_HOMING_FORCE_SET_ORIGIN;
+ settings.homing.flags.manual = DEFAULT_HOMING_ALLOW_MANUAL;
+ settings.homing.flags.override_locks = DEFAULT_HOMING_OVERRIDE_LOCKS;
+ settings.homing.flags.keep_on_reset = DEFAULT_HOMING_KEEP_STATUS_ON_RESET;
+ settings.limits.flags.two_switches = DEFAULT_LIMITS_TWO_SWITCHES_ON_AXES;
#else
settings.homing.flags.value = int_value & 0x0F;
settings.limits.flags.two_switches = bit_istrue(int_value, bit(4));
@@ -1022,8 +1030,8 @@ static status_code_t set_mode (setting_id_t id, uint_fast16_t int_value)
break;
case Mode_Laser:
- if(!spindle_get_caps().laser)
- return Status_SettingDisabledLaser;
+// if(!spindle_get_caps().laser)
+// return Status_SettingDisabledLaser;
if(settings.mode != Mode_Laser)
settings.flags.disable_laser_during_hold = DEFAULT_ENABLE_LASER_DURING_HOLD;
gc_state.modal.diameter_mode = false;
@@ -1037,8 +1045,8 @@ static status_code_t set_mode (setting_id_t id, uint_fast16_t int_value)
return Status_InvalidStatement;
}
+ machine_mode_changed = true;
settings.mode = (machine_mode_t)int_value;
- sys.mode = settings.mode == Mode_Laser && !hal.spindle.cap.laser ? Mode_Standard : settings.mode;
return Status_OK;
}
@@ -1374,7 +1382,7 @@ static uint32_t get_int (setting_id_t id)
#if COMPATIBILITY_LEVEL > 1
case Setting_LimitPinsInvertMask:
- value = settings.limits.invert.mask == INVERT_LIMIT_BIT_MASK ? 0 : 1;
+ value = settings.limits.invert.mask == DEFAULT_LIMIT_SIGNALS_INVERT_MASK ? 0 : 1;
break;
#endif
@@ -1759,11 +1767,11 @@ static bool is_setting_available (const setting_detail_t *setting)
#endif
case Setting_SpindleAtSpeedTolerance:
- available = hal.spindle.cap.at_speed || hal.driver_cap.spindle_sync;
+ available = spindle_get_caps().at_speed || hal.driver_cap.spindle_sync;
break;
case Setting_SpindleOnDelay:
- available = !hal.signals_cap.safety_door_ajar && hal.spindle.cap.at_speed;
+ available = !hal.signals_cap.safety_door_ajar && spindle_get_caps().at_speed;
break;
case Setting_AutoReportInterval:
@@ -1902,8 +1910,6 @@ bool read_global_settings ()
if(settings.planner_buffer_blocks < 30 || settings.planner_buffer_blocks > 1000)
settings.planner_buffer_blocks = 35;
- sys.mode = settings.mode;
-
if(!(hal.driver_cap.spindle_sync || hal.driver_cap.spindle_pid))
settings.spindle.ppr = 0;
@@ -1948,7 +1954,6 @@ void settings_restore (settings_restore_t restore)
memcpy(&settings, &defaults, sizeof(settings_t));
- sys.mode = settings.mode == Mode_Laser && !hal.spindle.cap.laser ? Mode_Standard : settings.mode;
settings.control_invert.mask &= hal.signals_cap.mask;
settings.spindle.invert.ccw &= spindle_get_caps().direction;
settings.spindle.invert.pwm &= spindle_get_caps().pwm_invert;
@@ -2263,6 +2268,35 @@ static status_code_t validate_value (const setting_detail_t *setting, float valu
return Status_OK;
}
+static status_code_t validate_uint_value (const setting_detail_t *setting, uint32_t value)
+{
+ uint32_t val;
+ uint_fast8_t set_idx = 0;
+ status_code_t status;
+
+ if(setting->min_value) {
+ if((status = read_uint((char *)setting->min_value, &set_idx, &val)) != Status_OK)
+ return status;
+
+ if(!(value >= val || (setting->flags.allow_null && value == 0)))
+ return Status_SettingValueOutOfRange;
+
+ } else if(value < 0.0f)
+ return Status_NegativeValue;
+
+ if(setting->max_value) {
+ set_idx = 0;
+
+ if((status = read_uint((char *)setting->max_value, &set_idx, &val)) != Status_OK)
+ return Status_BadNumberFormat;
+
+ if(value > val)
+ return Status_SettingValueOutOfRange;
+ }
+
+ return Status_OK;
+}
+
static uint32_t strnumentries (const char *s, const char delimiter)
{
if(s == NULL || *s == '\0')
@@ -2362,11 +2396,56 @@ inline static bool setting_is_string (setting_datatype_t datatype)
return datatype == Format_String || datatype == Format_Password || datatype == Format_IPv4;
}
-inline static bool setting_is_core (setting_type_t type)
+inline static bool setting_is_core (setting_type_t type)
{
return !(type == Setting_NonCore || type == Setting_NonCoreFn);
}
+static status_code_t setting_validate_me_uint (const setting_detail_t *setting, char *svalue)
+{
+ uint_fast8_t idx = 0;
+ uint32_t value;
+ status_code_t status;
+
+ if((status = read_uint(svalue, &idx, &value)) != Status_OK)
+ return status;
+
+ switch(setting->datatype) {
+
+ case Format_Bool:
+ if(!(value == 0 || value == 1))
+ status = Status_SettingValueOutOfRange;
+ break;
+
+ case Format_Bitfield:
+ case Format_XBitfield:;
+ if(value >= (1UL << strnumentries(setting->format, ',')))
+ status = Status_SettingValueOutOfRange;
+ break;
+
+ case Format_RadioButtons:
+ if(value >= strnumentries(setting->format, ','))
+ status = Status_SettingValueOutOfRange;
+ break;
+
+ case Format_AxisMask:
+ if(value >= (1 << N_AXIS))
+ status = Status_SettingValueOutOfRange;
+ break;
+
+ case Format_Int8:
+ case Format_Int16:
+ case Format_Integer:
+ status = validate_uint_value(setting, value);
+ break;
+
+ default:
+ break;
+ }
+
+ return status;
+}
+
status_code_t setting_validate_me (const setting_detail_t *setting, float value, char *svalue)
{
status_code_t status = Status_OK;
@@ -2374,33 +2453,18 @@ status_code_t setting_validate_me (const setting_detail_t *setting, float value,
switch(setting->datatype) {
case Format_Bool:
- if(!(value == 0.0f || value == 1.0f))
- status = Status_SettingValueOutOfRange;
- break;
-
case Format_Bitfield:
case Format_XBitfield:;
- if(!(isintf(value) && ((uint32_t)value < (1UL << strnumentries(setting->format, ','))))) //)
- status = Status_SettingValueOutOfRange;
- break;
-
case Format_RadioButtons:
- if(!(isintf(value) && (uint32_t)value < strnumentries(setting->format, ',')))
- status = Status_SettingValueOutOfRange;
- break;
-
case Format_AxisMask:
- if(!(isintf(value) && (uint32_t)value < (1 << N_AXIS)))
- status = Status_SettingValueOutOfRange;
- break;
-
case Format_Int8:
case Format_Int16:
case Format_Integer:
+ status = setting_validate_me_uint(setting, svalue);
+ break;
+
case Format_Decimal:
status = validate_value(setting, value);
- if(setting->datatype == Format_Integer && status == Status_OK && !isintf(value))
- status = Status_BadNumberFormat;
break;
case Format_Password:
@@ -2436,10 +2500,23 @@ status_code_t setting_validate (setting_id_t id, float value, char *svalue)
return setting == NULL ? Status_OK : setting_validate_me(setting, value, svalue);
}
+static bool settings_changed_spindle (void)
+{
+ static spindle_settings_t spindle_settings = {0};
+
+ bool changed;
+
+ if((changed = memcmp(&spindle_settings, &settings.spindle, sizeof(spindle_settings_t))) != 0)
+ memcpy(&spindle_settings, &settings.spindle, sizeof(spindle_settings_t));
+
+ return changed;
+}
+
// A helper method to set settings from command line
status_code_t settings_store_setting (setting_id_t id, char *svalue)
{
uint_fast8_t set_idx = 0;
+ uint32_t int_value = 0;
float value = NAN;
status_code_t status = Status_OK;
setting_details_t *set;
@@ -2457,7 +2534,10 @@ status_code_t settings_store_setting (setting_id_t id, char *svalue)
while(*svalue == ' ')
svalue++;
- if(!setting_is_string(setting->datatype) && !read_float(svalue, &set_idx, &value) && setting_is_core(setting->type))
+ if(setting->datatype == Format_Decimal) {
+ if(!read_float(svalue, &set_idx, &value) && setting_is_core(setting->type))
+ return Status_BadNumberFormat;
+ } else if(!setting_is_string(setting->datatype) && read_uint(svalue, &set_idx, &int_value) != Status_OK && setting_is_core(setting->type))
return Status_BadNumberFormat;
if((status = setting_validate_me(setting, value, svalue)) != Status_OK) {
@@ -2485,23 +2565,23 @@ status_code_t settings_store_setting (setting_id_t id, char *svalue)
break;
case Format_AxisMask:
- *((uint8_t *)(setting->value)) = (uint8_t)truncf(value) & AXES_BITMASK;
+ *((uint8_t *)(setting->value)) = (uint8_t)int_value & AXES_BITMASK;
break;
- case Format_Int8:
case Format_Bool:
case Format_Bitfield:
case Format_XBitfield:
case Format_RadioButtons:
- *((uint8_t *)(setting->value)) = (uint8_t)truncf(value);
+ case Format_Int8:
+ *((uint8_t *)(setting->value)) = (uint8_t)int_value;
break;
case Format_Int16:
- *((uint16_t *)(setting->value)) = (uint16_t)truncf(value);
+ *((uint16_t *)(setting->value)) = (uint16_t)int_value;
break;
case Format_Integer:
- *((uint32_t *)(setting->value)) = (uint32_t)truncf(value);
+ *((uint32_t *)(setting->value)) = (uint32_t)int_value;
break;
default:
@@ -2527,7 +2607,7 @@ status_code_t settings_store_setting (setting_id_t id, char *svalue)
break;
default:
- status = ((setting_set_int_ptr)(setting->value))(id, (uint_fast16_t)truncf(value));
+ status = ((setting_set_int_ptr)(setting->value))(id, (uint_fast16_t)int_value);
break;
}
break;
@@ -2535,16 +2615,18 @@ status_code_t settings_store_setting (setting_id_t id, char *svalue)
if(status == Status_OK) {
- if(!hal.spindle.cap.rpm_range_locked) {
- hal.spindle.rpm_min = settings.spindle.rpm_min;
- hal.spindle.rpm_max = settings.spindle.rpm_max;
- }
-
if(set->save)
set->save();
- if(set->on_changed)
- set->on_changed(&settings);
+ if(set->on_changed) {
+
+ settings_changed_flags_t changed = {0};
+
+ changed.spindle = settings_changed_spindle() || machine_mode_changed;
+ machine_mode_changed = false;
+
+ set->on_changed(&settings, changed);
+ }
}
return status;
@@ -2573,11 +2655,16 @@ void settings_clear (void)
// Initialize the config subsystem
void settings_init (void)
{
+ settings_changed_flags_t changed = {0};
+
if(!read_global_settings()) {
+
settings_restore_t settings = settings_all;
settings.defaults = 1; // Ensure global settings get restored
+
if(hal.nvs.type != NVS_None)
grbl.report.status_message(Status_SettingReadFail);
+
settings_restore(settings); // Force restore all non-volatile storage data.
report_init();
#if COMPATIBILITY_LEVEL <= 1
@@ -2585,7 +2672,9 @@ void settings_init (void)
#else
report_grbl_settings(false, NULL);
#endif
+ changed.spindle = settings_changed_spindle();
} else {
+
memset(&tool_table, 0, sizeof(tool_data_t)); // First entry is for tools not in tool table
#if N_TOOLS
uint_fast8_t idx;
@@ -2594,7 +2683,9 @@ void settings_init (void)
#endif
report_init();
- hal.settings_changed(&settings);
+ changed.spindle = settings_changed_spindle();
+
+ hal.settings_changed(&settings, changed);
if(hal.probe.configure) // Initialize probe invert mask.
hal.probe.configure(false, false);
@@ -2625,7 +2716,7 @@ void settings_init (void)
if(details->load)
details->load();
if(details->on_changed)
- details->on_changed(&settings);
+ details->on_changed(&settings, changed);
} while((details = details->next));
setting_details.on_changed = hal.settings_changed;
diff --git a/settings.h b/settings.h
index 5fbf6de..5f0130c 100644
--- a/settings.h
+++ b/settings.h
@@ -162,7 +162,7 @@ typedef enum {
// Optional driver implemented settings
- // Normally used for Ethernet or WiFi Station
+ // Normally used for Ethernet
Setting_Hostname = 300,
Setting_IpMode = 301,
Setting_IpAddress = 302,
@@ -184,6 +184,7 @@ typedef enum {
Setting_WebSocketPort2 = 317,
Setting_FtpPort2 = 318,
+ // Normally used for WiFi Station
Setting_Hostname3 = 320,
Setting_IpMode3 = 321,
Setting_IpAddress3 = 322,
@@ -202,6 +203,8 @@ typedef enum {
Setting_Timezone = 335,
Setting_DSTActive = 336,
+ Setting_Wifi_AP_BSSID = 337,
+
Setting_TrinamicDriver = 338,
Setting_TrinamicHoming = 339,
@@ -299,6 +302,11 @@ typedef enum {
Setting_VFD_20 = 472,
Setting_VFD_21 = 473,
+ Setting_VFD_ModbusAddress0 = 476,
+ Setting_VFD_ModbusAddress1 = 477,
+ Setting_VFD_ModbusAddress2 = 478,
+ Setting_VFD_ModbusAddress3 = 479,
+
Setting_Fan0OffDelay = 480,
Setting_AutoReportInterval = 481,
Setting_TimeZoneOffset = 482,
@@ -326,6 +334,29 @@ typedef enum {
Setting_MacroPort8 = 508,
Setting_MacroPort9 = 509,
+ Setting_SpindleEnable0 = 510,
+ Setting_SpindleEnable1 = 511,
+ Setting_SpindleEnable2 = 512,
+ Setting_SpindleEnable3 = 513,
+ Setting_SpindleEnable4 = 514,
+ Setting_SpindleEnable5 = 515,
+ Setting_SpindleEnable6 = 516,
+ Setting_SpindleEnable7 = 517,
+
+ Setting_SpindleToolStart0 = 520,
+ Setting_SpindleToolStart1 = 521,
+ Setting_SpindleToolStart2 = 522,
+ Setting_SpindleToolStart3 = 523,
+ Setting_SpindleToolStart4 = 524,
+ Setting_SpindleToolStart5 = 525,
+ Setting_SpindleToolStart6 = 526,
+ Setting_SpindleToolStart7 = 527,
+
+ Setting_MQTTBrokerIpAddress = 530,
+ Setting_MQTTBrokerPort = 531,
+ Setting_MQTTBrokerUserName = 532,
+ Setting_MQTTBrokerPassword = 533,
+
Setting_SettingsMax,
Setting_SettingsAll = Setting_SettingsMax,
@@ -770,6 +801,14 @@ typedef struct {
const char *description;
} setting_descr_t;
+typedef union {
+ uint8_t value;
+ struct {
+ uint8_t spindle :1,
+ unassigned :7;
+ };
+} settings_changed_flags_t;
+
typedef status_code_t (*setting_set_int_ptr)(setting_id_t id, uint_fast16_t value);
typedef status_code_t (*setting_set_float_ptr)(setting_id_t id, float value);
typedef status_code_t (*setting_set_string_ptr)(setting_id_t id, char *value);
@@ -780,8 +819,9 @@ typedef bool (*setting_output_ptr)(const setting_detail_t *setting, uint_fast16_
/*! \brief Pointer to callback function to be called when settings are loaded or changed.
\param settings pointer to \a settings_t struct containing the settings.
+\param changed a \a settings_changed_flags_t union containing the changed setting groups.
*/
-typedef void (*settings_changed_ptr)(settings_t *settings);
+typedef void (*settings_changed_ptr)(settings_t *settings, settings_changed_flags_t changed);
typedef void (*driver_settings_load_ptr)(void);
typedef void (*driver_settings_save_ptr)(void);
diff --git a/sleep.c b/sleep.c
index 326a70f..98f0d9c 100644
--- a/sleep.c
+++ b/sleep.c
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2018-2021 Terje Io
+ Copyright (c) 2018-2023 Terje Io
Copyright (c) 2016 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
@@ -67,7 +67,7 @@ void sleep_check()
// has any powered components enabled.
// NOTE: With overrides or in laser mode, modal spindle and coolant state are not guaranteed. Need
// to directly monitor and record running state during parking to ensure proper function.
- if (!sys.steppers_deenergize && (gc_state.modal.spindle.value || gc_state.modal.coolant.value)) {
+ if (!sys.steppers_deenergize && (gc_state.modal.spindle.state.value || gc_state.modal.coolant.value)) {
switch(state_get()) {
case STATE_IDLE:
diff --git a/spindle_control.c b/spindle_control.c
index e438050..86bf5d3 100644
--- a/spindle_control.c
+++ b/spindle_control.c
@@ -27,115 +27,257 @@
#include "hal.h"
#include "protocol.h"
#include "state_machine.h"
+#include "settings.h"
#ifndef UNUSED
#define UNUSED(x) (void)(x)
#endif
+/*! \brief Structure for holding spindle registration data */
+typedef struct {
+ const spindle_ptrs_t *cfg;
+ spindle_ptrs_t hal;
+ const char *name;
+ bool init_ok;
+} spindle_reg_t;
+
+/*! \brief Structure for holding data about an enabled spindle */
+typedef struct {
+ spindle_param_t param;
+ spindle_ptrs_t hal;
+ bool enabled;
+} spindle_sys_t;
+
static uint8_t n_spindle = 0;
-static const spindle_ptrs_t *spindles[N_SPINDLE], *current_spindle = NULL;
+static spindle_sys_t sys_spindle[N_SYS_SPINDLE] = {0};
+static spindle_reg_t spindles[N_SPINDLE] = {0}, *pwm_spindle = NULL;
-spindle_id_t spindle_register (const spindle_ptrs_t *spindle, const char *name)
-{
- if(n_spindle == 0) {
- memcpy(&hal.spindle, spindle, sizeof(spindle_ptrs_t));
- current_spindle = spindle;
- }
-
- if(n_spindle < N_SPINDLE && settings_add_spindle_type(name)) {
- spindles[n_spindle++] = spindle;
- if(spindle->type == SpindleType_PWM)
- hal.driver_cap.pwm_spindle = On;
- return n_spindle - 1;
- }
-
- return -1;
-}
-
-bool spindle_select (spindle_id_t spindle_id)
+/*! \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.
+\param spindle_num spindle number to set as enabled as a \ref spindle_num_t.
+\returns \a true if succsesful, \a false if not.
+*/
+static bool spindle_activate (spindle_id_t spindle_id, spindle_num_t spindle_num)
{
bool ok;
+ spindle_reg_t *spindle;
- if(n_spindle == 0) {
+ // Always configure PWM spindle on startup to ensure outputs are set correctly.
+ if(pwm_spindle && pwm_spindle->cfg->config && pwm_spindle != &spindles[spindle_id]) {
- if(hal.spindle.set_state)
- spindles[n_spindle++] = current_spindle = &hal.spindle;
- else
- spindle_add_null();
- }
-
- if((ok = spindle_id >= 0 && spindle_id < n_spindle)) {
-
- spindle_ptrs_t spindle_org;
-
- if(hal.spindle.set_state && current_spindle != spindles[spindle_id])
- gc_spindle_off();
-
- memcpy(&spindle_org, &hal.spindle, offsetof(spindle_ptrs_t, get_data));
- memcpy(&hal.spindle, spindles[spindle_id], offsetof(spindle_ptrs_t, get_data));
-
- if(!hal.spindle.cap.rpm_range_locked) {
- hal.spindle.rpm_min = settings.spindle.rpm_min;
- hal.spindle.rpm_max = settings.spindle.rpm_max;
+ if(!pwm_spindle->hal.cap.rpm_range_locked) {
+ pwm_spindle->hal.rpm_min = settings.spindle.rpm_min;
+ pwm_spindle->hal.rpm_max = settings.spindle.rpm_max;
}
- if(hal.spindle.config)
- ok = hal.spindle.config();
+ if((pwm_spindle->init_ok = pwm_spindle->hal.config == NULL || pwm_spindle->hal.config(&pwm_spindle->hal)))
+ pwm_spindle->hal.set_state((spindle_state_t){0}, 0.0f);
+ }
+ pwm_spindle = NULL;
+
+ if((ok = spindle_id >= 0 && spindle_id < n_spindle && !!spindles[spindle_id].cfg)) {
+
+ spindle = &spindles[spindle_id];
+
+ if(sys_spindle[spindle_num].enabled && sys_spindle[spindle_num].hal.id != spindle_id && sys_spindle[spindle_num].hal.set_state)
+ gc_spindle_off(); // TODO: switch off only the default spindle?
+
+ if(!spindle->hal.cap.rpm_range_locked) {
+ spindle->hal.rpm_min = settings.spindle.rpm_min;
+ spindle->hal.rpm_max = settings.spindle.rpm_max;
+ }
+
+ if(!spindle->init_ok)
+ ok = spindle->init_ok = spindle->hal.config == NULL || spindle->hal.config(&spindle->hal);
if(ok) {
- current_spindle = spindles[spindle_id];
- sys.mode = settings.mode == Mode_Laser && !hal.spindle.cap.laser ? Mode_Standard : settings.mode;
+
+ spindle_ptrs_t spindle_hal;
+
+ memcpy(&spindle_hal, &spindle->hal, sizeof(spindle_ptrs_t));
+
+ if(spindle->cfg->get_data == NULL) {
+ 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;
+ }
+
+ spindle_hal.cap.laser &= settings.mode == Mode_Laser;
+
if(grbl.on_spindle_select)
- grbl.on_spindle_select(spindle_id);
- } else
- memcpy(&spindle_org, &hal.spindle, offsetof(spindle_ptrs_t, get_data));
+ ok = grbl.on_spindle_select(&spindle_hal);
+
+ if(ok) {
+ sys_spindle[spindle_num].enabled = true;
+ sys_spindle[spindle_num].param.hal = &sys_spindle[spindle_num].hal;
+ if(sys_spindle[spindle_num].param.override_pct == 0)
+ sys_spindle[spindle_num].param.override_pct = DEFAULT_SPINDLE_RPM_OVERRIDE;
+ spindle_hal.param = &sys_spindle[spindle_num].param;
+ memcpy(&sys_spindle[spindle_num].hal, &spindle_hal, sizeof(spindle_ptrs_t));
+ if(grbl.on_spindle_selected)
+ grbl.on_spindle_selected(&sys_spindle[spindle_num].hal);
+ }
+ }
}
return ok;
}
-const spindle_ptrs_t *spindle_get (spindle_id_t spindle_id)
-{
- if(spindle_id >= 0 && spindle_id < n_spindle)
- return spindles[spindle_id];
+/*! \brief Register a spindle with the core.
+\param spindle pointer to a \a spindle_ptrs_t structure.
+\param name pointer to a null terminated string.
+\returns assigned \a spindle id as a \ref spindle_id_t if successful, -1 if not.
- return NULL;
+__NOTE:__ The first spindle registered will become the default active spindle.
+__NOTE:__ up to \ref N_SPINDLE spindles can be registered at a time.
+*/
+spindle_id_t spindle_register (const spindle_ptrs_t *spindle, const char *name)
+{
+ if(n_spindle < N_SPINDLE && settings_add_spindle_type(name)) {
+
+ spindles[n_spindle].cfg = spindle;
+ spindles[n_spindle].name = name;
+ memcpy(&spindles[n_spindle].hal, spindles[n_spindle].cfg, sizeof(spindle_ptrs_t));
+ spindles[n_spindle].hal.id = n_spindle;
+
+ if(spindle->type == SpindleType_PWM && pwm_spindle == NULL) {
+ pwm_spindle = &spindles[n_spindle];
+ hal.driver_cap.pwm_spindle = On;
+ }
+
+ if(n_spindle == 0)
+ memcpy(&sys_spindle[0].hal, spindle, sizeof(spindle_ptrs_t));
+
+ return n_spindle++;
+ }
+
+ return -1;
}
-spindle_id_t spindle_get_current (void)
+/*! \brief Enable a spindle and make it available for use by gcode.
+\param spindle_id spindle id as a \ref spindle_id_t.
+\returns assigned spindle number as a \a spindle_num_t if successful \a -1 if not.
+
+__NOTE:__ up to \ref N_SYS_SPINDLE spindles can be enabled at a time.
+*/
+spindle_num_t spindle_enable (spindle_id_t spindle_id)
{
- spindle_id_t spindle_id = spindle_get_count();
+ uint_fast8_t idx = 0;
+ spindle_num_t spindle_num = -1;
- do {
- if(spindles[--spindle_id] == current_spindle)
- break;
- } while(spindle_id);
+ if(spindle_id >= 0 && spindle_id < n_spindle) do {
+ if(!sys_spindle[idx].enabled && spindle_activate(spindle_id, idx))
+ spindle_num = idx;
+ } while(++idx < N_SYS_SPINDLE && spindle_num == -1);
- return spindle_id;
+ return spindle_num;
}
+/*! \brief Enables a spindle and sets it as default spindle (spindle number 0).
+\param spindle_id spindle id as a \ref spindle_id_t.
+\returns \a true if succsesful, \a false if not.
+*/
+bool spindle_select (spindle_id_t spindle_id)
+{
+ if(n_spindle == 0 && spindle_id >= 0) {
+ spindle_id = 0;
+ spindle_add_null();
+ }
+
+ return (sys_spindle[0].enabled && sys_spindle[0].hal.id == spindle_id) || spindle_activate(spindle_id, 0);
+}
+
+
+/*! \brief Get the handlers (function pointers) etc. associated with the spindle.
+\param spindle_id spindle id as a \ref spindle_id_t.
+\param hal a \ref spindle_hal_t enum value:
+
\ref SpindleHAL_Raw - get the read only version as supplied at registration
+
\ref SpindleHAL_Configured - get the version with run-time modifications applied by the spindle driver.
+
\ref SpindleHAL_Active - get the enabled version available from gcode. Can be overriden by event handlers prior to activation.
+\returns pointer to a \ref spindle_ptrs_t structure if successful, \a NULL if not.
+
+__NOTE:__ do not modify the returned structure!
+*/
+spindle_ptrs_t *spindle_get_hal (spindle_id_t spindle_id, spindle_hal_t hal)
+{
+ spindle_ptrs_t *spindle = NULL;
+
+ if(hal == SpindleHAL_Active) {
+
+ uint_fast8_t idx = N_SYS_SPINDLE;
+
+ do {
+ idx--;
+ if(sys_spindle[idx].hal.id == spindle_id && sys_spindle[idx].enabled)
+ spindle = &sys_spindle[idx].hal;
+ } while(idx && spindle == NULL);
+
+ } else if(spindle_id >= 0 && spindle_id < n_spindle && spindles[spindle_id].cfg)
+ spindle = hal == SpindleHAL_Raw ? (spindle_ptrs_t *)spindles[spindle_id].cfg : &spindles[spindle_id].hal;
+
+ return spindle;
+}
+
+/*! \brief Get the spindle id of the default spindle (spindle number 0).
+\returns spindle id as a \ref spindle_id_t if successful, \a -2 if not (no spindle available).
+*/
+spindle_id_t spindle_get_default (void)
+{
+ return sys_spindle[0].enabled ? sys_spindle[0].hal.id : -2;
+}
+
+/*! \brief Get the merged spindle capabilities of all registered spindles.
+\returns capabilities in a \ref spindle_cap_t structure.
+*/
spindle_cap_t spindle_get_caps (void)
{
spindle_cap_t caps = {0};
uint_fast8_t idx = n_spindle;
- if(!idx)
- caps.value = hal.spindle.cap.value;
- else do {
- caps.value |= spindles[--idx]->cap.value;
+ do {
+ caps.value |= spindles[--idx].hal.cap.value;
} while(idx);
return caps;
}
-void spindle_update_caps (spindle_pwm_t *pwm_caps)
+/*! \brief Get the registered name of a spindle.
+\param spindle_id spindle id as a \ref spindle_id_t.
+\returns pointer to a null terminated string if succesful, \a NULL if not.
+*/
+const char *spindle_get_name (spindle_id_t spindle_id)
{
- hal.spindle.type = pwm_caps ? SpindleType_PWM : SpindleType_Basic;
- hal.spindle.cap.laser = !!pwm_caps && !!hal.spindle.update_pwm;
- hal.spindle.pwm_off_value = pwm_caps ? pwm_caps->off_value : 0;
- sys.mode = settings.mode == Mode_Laser && !hal.spindle.cap.laser ? Mode_Standard : settings.mode;
+ return spindle_id >= 0 && spindle_id < n_spindle && spindles[spindle_id].cfg ? spindles[spindle_id].name : NULL;
}
+/*! \brief Update the capabilities of a registered PWM spindle.
+May be used by the driver on spindle initialization or when spindle settings has been changed.
+\param spindle pointer to a \ref spindle_ptrs_t structure.
+\param pwm_caps pointer to a \ref spindle_pwm_t structure.
+*/
+void spindle_update_caps (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_caps)
+{
+ uint_fast8_t idx = N_SYS_SPINDLE;
+
+ spindle->type = pwm_caps ? SpindleType_PWM : SpindleType_Basic;
+ spindle->cap.laser = !!pwm_caps && !!spindle->update_pwm && settings.mode == Mode_Laser;
+ spindle->pwm_off_value = pwm_caps ? pwm_caps->off_value : 0;
+
+ do {
+ idx--;
+ if(sys_spindle[idx].enabled && spindle->id == sys_spindle[idx].hal.id) {
+ sys_spindle[idx].hal.type = spindle->type;
+ sys_spindle[idx].hal.cap.laser = spindle->cap.laser;
+ sys_spindle[idx].hal.pwm_off_value = spindle->pwm_off_value;
+ break;
+ }
+ } while(idx);
+}
+
+/*! \brief Get number of registered spindles.
+\returns number of registered spindles.
+*/
uint8_t spindle_get_count (void)
{
if(n_spindle == 0)
@@ -144,24 +286,79 @@ uint8_t spindle_get_count (void)
return n_spindle;
}
+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;
+
+ const setting_detail_t *setting;
+
+ do {
+ idx--;
+ if((setting = setting_get_details(idx == 0 ? Setting_SpindleType : Setting_SpindleEnable0 + idx, NULL))) {
+ if(setting_get_int_value(setting, 0) == spindle_id)
+ spindle_num = idx;
+ }
+ } while(idx && spindle_num == -1);
+
+ return spindle_num;
+}
+
+/*! \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.
+\returns \a true if spindles are registered and a callback function was provided, \a false otherwise.
+*/
bool spindle_enumerate_spindles (spindle_enumerate_callback_ptr callback)
{
if(callback == NULL || n_spindle == 0)
return false;
- uint_fast8_t idx = n_spindle;
+ uint_fast8_t idx;
spindle_info_t spindle;
- do {
- spindle.id = --idx;
- spindle.hal = spindles[idx];
- spindle.is_current = spindle.hal == current_spindle;
+ for(idx = 0; idx < n_spindle; idx++) {
+
+ spindle.id = idx;
+ spindle.name = spindles[idx].name;
+ spindle.hal = &spindles[idx].hal;
+ spindle.num = spindle_get_num(idx);
+ spindle.enabled = spindle.num != -1;
+ spindle.is_current = spindle.enabled && sys_spindle[0].hal.id == idx;
+
callback(&spindle);
- } while(idx);
+ }
return true;
}
+// The following calls uses logical spindle numbers pointing into the sys_spindle array
+// containing enabled spindles (spindle number as used by the $ gcode word)
+
+/*! \brief Check if a spindle is enabled and available or not.
+\param spindle_num spindle number as a \ref spindle_num_t.
+\returns \a true if the spindle is enabled, \a false otherwise.
+*/
+bool spindle_is_enabled (spindle_num_t spindle_num)
+{
+ if(spindle_num == -1)
+ spindle_num = 0;
+
+ return spindle_num >= 0 && spindle_num < N_SYS_SPINDLE && sys_spindle[spindle_num].enabled;
+}
+
+/*! \brief Get the handlers (function pointers) etc. associated with an enabled spindle.
+\param spindle_num spindle number as a \ref spindle_num_t.
+\returns pointer to a \ref spindle_ptrs_t structure if successful, \a NULL if not.
+
+__NOTE:__ do not modify the returned structure!
+*/
+spindle_ptrs_t *spindle_get (spindle_num_t spindle_num)
+{
+ return spindle_num >= 0 && spindle_num < N_SYS_SPINDLE && sys_spindle[spindle_num].enabled ? &sys_spindle[spindle_num].hal : NULL;
+}
+
+//
+
//
// Null (dummy) spindle, automatically installed if no spindles are registered.
//
@@ -195,7 +392,11 @@ static void null_update_rpm (float rpm)
UNUSED(rpm);
}
-void spindle_add_null (void)
+/*! \brief Register a null spindle that has no connection to the outside world.
+This is done automatically on startup if no spindle can be succesfully enabled.
+\returns assigned spindle id as a \ref spindle_id_t if successful, \a -1 if not.
+*/
+spindle_id_t spindle_add_null (void)
{
static const spindle_ptrs_t spindle = {
.type = SpindleType_Null,
@@ -209,54 +410,79 @@ void spindle_add_null (void)
.update_rpm = null_update_rpm
};
- spindle_register(&spindle, "NULL");
+ bool registered = false;
+ uint_fast8_t idx = n_spindle;
+
+ if(idx) do {
+ if((registered = spindles[--idx].hal.type == SpindleType_Null))
+ break;
+ } while(idx);
+
+ if(!registered)
+ return spindle_register(&spindle, "NULL");
+
+ return idx;
}
// End null (dummy) spindle.
-// Set spindle speed override
-// NOTE: Unlike motion overrides, spindle overrides do not require a planner reinitialization.
-void spindle_set_override (uint_fast8_t speed_override)
+/*! \brief Set spindle speed override.
+\param spindle pointer to a \ref spindle_ptrs_t structure.
+\param speed_override override as a percentage of the programmed RPM.
+
+__NOTE:__ Unlike motion overrides, spindle overrides do not require a planner reinitialization.
+*/
+void spindle_set_override (spindle_ptrs_t *spindle, override_t speed_override)
{
- if(sys.override.control.spindle_rpm_disable)
+// if(speed_override != 100 && sys.override.control.spindle_rpm_disable)
+// return;
+
+ if(speed_override != 100 && spindle->param->state.override_disable)
return;
speed_override = constrain(speed_override, MIN_SPINDLE_RPM_OVERRIDE, MAX_SPINDLE_RPM_OVERRIDE);
- if ((uint8_t)speed_override != sys.override.spindle_rpm) {
- sys.override.spindle_rpm = (uint8_t)speed_override;
+ if ((uint8_t)speed_override != spindle->param->override_pct) {
+
+ spindle->param->override_pct = speed_override;
+
if(state_get() == STATE_IDLE)
- spindle_set_state(0, gc_state.modal.spindle, gc_state.spindle.rpm);
+ spindle_set_state(spindle, gc_state.modal.spindle.state, gc_state.spindle.rpm);
else
sys.step_control.update_spindle_rpm = On;
sys.report.overrides = On; // Set to report change immediately
if(grbl.on_spindle_programmed)
- grbl.on_spindle_programmed(gc_state.modal.spindle, spindle_set_rpm(gc_state.spindle.rpm, sys.override.spindle_rpm), gc_state.modal.spindle_rpm_mode);
+ grbl.on_spindle_programmed(spindle, gc_state.modal.spindle.state, spindle_set_rpm(spindle, gc_state.spindle.rpm, speed_override), gc_state.modal.spindle.rpm_mode);
if(grbl.on_override_changed)
grbl.on_override_changed(OverrideChanged_SpindleRPM);
}
}
-// Immediately sets spindle running state with direction and spindle rpm, if enabled.
-// Called by g-code parser spindle_sync(), parking retract and restore, g-code program end,
-// sleep, and spindle stop override.
-static bool set_state (const spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
+/*! \internal \brief Immediately sets spindle running state with direction and spindle rpm, if enabled.
+Called by g-code parser spindle_sync(), parking retract and restore, g-code program end,
+sleep, and spindle stop override.
+\param spindle pointer to a \ref spindle_ptrs_t structure.
+\param state a \ref spindle_state_t structure.
+\param rpm the spindle RPM to set.
+\returns \a true if successful, \a false if the current controller state is \ref ABORTED.
+*/
+static bool set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
{
if (!ABORTED) { // Block during abort.
if (!state.on) { // Halt or set spindle direction and rpm.
- sys.spindle_rpm = rpm = 0.0f;
+ spindle->param->rpm = rpm = 0.0f;
spindle->set_state((spindle_state_t){0}, 0.0f);
} else {
// NOTE: Assumes all calls to this function is when Grbl is not moving or must remain off.
// TODO: alarm/interlock if going from CW to CCW directly in non-laser mode?
- if (sys.mode == Mode_Laser && state.ccw)
+ if (spindle->cap.laser && state.ccw)
rpm = 0.0f; // TODO: May need to be rpm_min*(100/MAX_SPINDLE_RPM_OVERRIDE);
- spindle->set_state(state, spindle_set_rpm(rpm, sys.override.spindle_rpm));
+ spindle->set_state(state, spindle_set_rpm(spindle, rpm, spindle->param->override_pct));
}
sys.report.spindle = On; // Set to report change immediately
@@ -267,29 +493,39 @@ static bool set_state (const spindle_ptrs_t *spindle, spindle_state_t state, flo
}
-// Immediately sets spindle running state with direction and spindle rpm, if enabled.
-// Called by g-code parser spindle_sync(), parking retract and restore, g-code program end,
-// sleep, and spindle stop override.
-bool spindle_set_state (spindle_id_t spindle_id, spindle_state_t state, float rpm)
+/*! \brief Immediately sets spindle running state with direction and spindle rpm, if enabled.
+Called by g-code parser spindle_sync(), parking retract and restore, g-code program end,
+sleep, and spindle stop override.
+\param spindle pointer to a \ref spindle_ptrs_t structure.
+\param state a \ref spindle_state_t structure.
+\param rpm the spindle RPM to set.
+\returns \a true if successful, \a false if the current controller state is \ref ABORTED.
+*/
+bool spindle_set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
{
- return set_state(spindle_id == 0 ? &hal.spindle : spindles[spindle_id], state, rpm);
+ return set_state(spindle, state, rpm);
}
-// G-code parser entry-point for setting spindle state. Forces a planner buffer sync and bails
-// if an abort or check-mode is active.
-bool spindle_sync (spindle_id_t spindle_id, spindle_state_t state, float rpm)
+/*! \brief G-code parser entry-point for setting spindle state. Forces a planner buffer sync and bails
+if an abort or check-mode is active. If the spindle supports at speed functionality it will wait
+for it to reach the speed and raise an alarm if the speed is not reached within the timeout period.
+\param spindle pointer to a \ref spindle_ptrs_t structure.
+\param state a \ref spindle_state_t structure.
+\param rpm the spindle RPM to set.
+\returns \a true if successful, \a false if the current controller state is \ref ABORTED.
+*/
+bool spindle_sync (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
{
bool ok;
if (!(ok = state_get() == STATE_CHECK_MODE)) {
- const spindle_ptrs_t *spindle = spindle_id == 0 ? &hal.spindle : spindles[spindle_id];
bool at_speed = !state.on || !spindle->cap.at_speed || settings.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) {
float on_delay = 0.0f;
- while(!(at_speed = hal.spindle.get_state().at_speed)) {
+ while(!(at_speed = spindle->get_state().at_speed)) {
delay_sec(0.2f, DelayMode_Dwell);
on_delay += 0.2f;
if(ABORTED)
@@ -308,21 +544,26 @@ bool spindle_sync (spindle_id_t spindle_id, spindle_state_t state, float rpm)
return ok;
}
-// Restore spindle running state with direction, enable, spindle RPM and appropriate delay.
-bool spindle_restore (spindle_state_t state, float rpm)
+/*! \brief Restore spindle running state with direction, enable, spindle RPM and appropriate delay.
+\param spindle pointer to a \ref spindle_ptrs_t structure.
+\param state a \ref spindle_state_t structure.
+\param rpm the spindle RPM to set.
+\returns \a true if successful, \a false if the current controller state is \ref ABORTED.
+*/
+bool spindle_restore (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
{
bool ok = true;
- if(sys.mode == Mode_Laser) // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
+ if(spindle->cap.laser) // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
sys.step_control.update_spindle_rpm = On;
else { // TODO: add check for current spindle state matches restore state?
- spindle_set_state(0, state, rpm);
+ spindle_set_state(spindle, state, rpm);
if(state.on) {
- if((ok = !hal.spindle.cap.at_speed))
+ if((ok = !spindle->cap.at_speed))
delay_sec(settings.safety_door.spindle_on_delay, DelayMode_SysSuspend);
else if((ok == (settings.spindle.at_speed_tolerance <= 0.0f))) {
float delay = 0.0f;
- while(!(ok = hal.spindle.get_state().at_speed)) {
+ while(!(ok = spindle->get_state().at_speed)) {
delay_sec(0.1f, DelayMode_SysSuspend);
delay += 0.1f;
if(ABORTED)
@@ -339,40 +580,91 @@ bool spindle_restore (spindle_state_t state, float rpm)
return ok;
}
-// Calculate and set programmed RPM according to override and max/min limits
-float spindle_set_rpm (float rpm, uint8_t override_pct)
+/*! \brief Calculate and set programmed RPM according to override and max/min limits
+\param spindle pointer to a \ref spindle_ptrs_t structure.
+\param rpm the programmed RPM.
+\param override_pct override value in percent.
+\returns the calulated RPM.
+*/
+float spindle_set_rpm (spindle_ptrs_t *spindle, float rpm, override_t override_pct)
{
if(override_pct != 100)
rpm *= 0.01f * (float)override_pct; // Scale RPM by override value.
// Apply RPM limits
- if (rpm <= 0.0f)
+ if (rpm <= 0.0f) // TODO: remove this test?
rpm = 0.0f;
- else if (rpm > hal.spindle.rpm_max)
- rpm = hal.spindle.rpm_max;
- else if (rpm < hal.spindle.rpm_min)
- rpm = hal.spindle.rpm_min;
+ else if (rpm > spindle->rpm_max)
+ rpm = spindle->rpm_max;
+ else if (rpm < spindle->rpm_min)
+ rpm = spindle->rpm_min;
- sys.spindle_rpm = rpm;
+ spindle->param->rpm_overridden = rpm;
+ spindle->param->override_pct = override_pct;
return rpm;
}
+/*! \brief Turn off all enabled spindles.
+*/
+void spindle_all_off (void)
+{
+ spindle_ptrs_t *spindle;
+ uint_fast8_t spindle_num = N_SYS_SPINDLE;
+ do {
+ if((spindle = spindle_get(--spindle_num))) {
+ spindle->param->rpm = spindle->param->rpm_overridden = 0.0f;
+ spindle->param->state.value = 0;
+#ifdef GRBL_ESP32
+ spindle->esp32_off();
+#else
+ spindle->set_state((spindle_state_t){0}, 0.0f);
+#endif
+ }
+ } while(spindle_num);
+}
+
+/*! \brief Check if any of the enabled spindles is running.
+\returns \a true if a spindle is running, \a false otherwise.
+*/
+bool spindle_is_on (void)
+{
+ bool on = false;
+
+ spindle_ptrs_t *spindle;
+ uint_fast8_t spindle_num = N_SYS_SPINDLE;
+ do {
+ if((spindle = spindle_get(--spindle_num)))
+ on = spindle->get_state().on;
+ } while(spindle_num && !on);
+
+ return on;
+}
+
//
// The following functions are not called by the core, may be called by driver code.
//
-// calculate inverted pwm value if configured
+/*! \brief calculate inverted pwm value if configured
+\param pwm_data pointer t a \a spindle_pwm_t structure.
+\param pwm_value non inverted PWM value.
+\returns the inverted PWM value to use.
+*/
static inline uint_fast16_t invert_pwm (spindle_pwm_t *pwm_data, uint_fast16_t pwm_value)
{
return pwm_data->invert_pwm ? pwm_data->period - pwm_value - 1 : pwm_value;
}
-// Precompute PWM values for faster conversion.
-// Returns false if no PWM range possible, driver should revert to simple on/off spindle control if so.
-bool spindle_precompute_pwm_values (spindle_pwm_t *pwm_data, uint32_t clock_hz)
+/*! \brief Precompute PWM values for faster conversion.
+\param spindle pointer to a \ref spindle_ptrs_t structure.
+\param pwm_data pointer to a \a spindle_pwm_t structure, to hold the precomputed values.
+\param clock_hz timer clock frequency used for PWM generation.
+\returns \a true if successful, \a false if no PWM range possible - driver should then revert to simple on/off spindle control.
+*/
+bool spindle_precompute_pwm_values (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_data, uint32_t clock_hz)
{
- if(hal.spindle.rpm_max > hal.spindle.rpm_min) {
+ if(spindle->rpm_max > spindle->rpm_min) {
+ pwm_data->rpm_min = spindle->rpm_min;
pwm_data->period = (uint_fast16_t)((float)clock_hz / settings.spindle.pwm_freq);
if(settings.spindle.pwm_off_value == 0.0f)
pwm_data->off_value = pwm_data->invert_pwm ? pwm_data->period : 0;
@@ -380,7 +672,7 @@ bool spindle_precompute_pwm_values (spindle_pwm_t *pwm_data, uint32_t clock_hz)
pwm_data->off_value = invert_pwm(pwm_data, (uint_fast16_t)(pwm_data->period * settings.spindle.pwm_off_value / 100.0f));
pwm_data->min_value = (uint_fast16_t)(pwm_data->period * settings.spindle.pwm_min_value / 100.0f);
pwm_data->max_value = (uint_fast16_t)(pwm_data->period * settings.spindle.pwm_max_value / 100.0f) + pwm_data->offset;
- pwm_data->pwm_gradient = (float)(pwm_data->max_value - pwm_data->min_value) / (hal.spindle.rpm_max - hal.spindle.rpm_min);
+ pwm_data->pwm_gradient = (float)(pwm_data->max_value - pwm_data->min_value) / (spindle->rpm_max - spindle->rpm_min);
pwm_data->always_on = settings.spindle.pwm_off_value != 0.0f;
}
@@ -395,15 +687,23 @@ bool spindle_precompute_pwm_values (spindle_pwm_t *pwm_data, uint32_t clock_hz)
}
#endif
- return hal.spindle.rpm_max > hal.spindle.rpm_min;
+ return spindle->rpm_max > spindle->rpm_min;
}
-// Spindle RPM to PWM conversion.
+/*! \brief Spindle RPM to PWM conversion.
+\param pwm_data pointer t a \a spindle_pwm_t structure.
+\param rpm spindle RPM.
+\param pid_limit boolean, \a true if PID based spindle sync is used, \a false otherwise.
+\returns the PWM value to use.
+
+__NOTE:__ \a spindle_precompute_pwm_values() must be called to precompute values before this function is called.
+Typically this is done by the spindle initialization code.
+*/
uint_fast16_t spindle_compute_pwm_value (spindle_pwm_t *pwm_data, float rpm, bool pid_limit)
{
uint_fast16_t pwm_value;
- if(rpm > hal.spindle.rpm_min) {
+ if(rpm > pwm_data->rpm_min) {
#ifdef ENABLE_SPINDLE_LINEARIZATION
// Compute intermediate PWM value with linear spindle speed model via piecewise linear fit model.
uint_fast8_t idx = pwm_data->n_pieces;
@@ -419,7 +719,7 @@ uint_fast16_t spindle_compute_pwm_value (spindle_pwm_t *pwm_data, float rpm, boo
} else
#endif
// Compute intermediate PWM value with linear spindle speed model.
- pwm_value = (uint_fast16_t)floorf((rpm - hal.spindle.rpm_min) * pwm_data->pwm_gradient) + pwm_data->min_value;
+ pwm_value = (uint_fast16_t)floorf((rpm - pwm_data->rpm_min) * pwm_data->pwm_gradient) + pwm_data->min_value;
if(pwm_value >= (pid_limit ? pwm_data->period : pwm_data->max_value))
pwm_value = pid_limit ? pwm_data->period - 1 : pwm_data->max_value;
diff --git a/spindle_control.h b/spindle_control.h
index 2b8d268..47d6fdb 100644
--- a/spindle_control.h
+++ b/spindle_control.h
@@ -25,24 +25,25 @@
#define _SPINDLE_CONTROL_H_
typedef int8_t spindle_id_t;
+typedef int8_t spindle_num_t;
// if changed to > 8 bits planner_cond_t needs to be changed too
typedef union {
uint8_t value;
uint8_t mask;
struct {
- uint8_t on :1,
- ccw :1,
- pwm :1, // NOTE: only used for PWM inversion setting
- reserved3 :1,
- reserved4 :1,
- encoder_error :1,
- at_speed :1, //!< Spindle is at speed.
- synchronized :1;
+ uint8_t on :1,
+ ccw :1,
+ pwm :1, //!< NOTE: only used for PWM inversion setting
+ reserved :1,
+ override_disable :1,
+ encoder_error :1,
+ at_speed :1, //!< Spindle is at speed.
+ synchronized :1;
};
} spindle_state_t;
-/// Bitmap flags for spindle capabilities.
+/*! \brief Bitmap flags for spindle capabilities. */
typedef union {
uint8_t value; //!< All bitmap flags.
struct {
@@ -57,12 +58,12 @@ typedef union {
};
} spindle_cap_t;
-// Used when HAL driver supports spindle synchronization
+/*! \brief Used when HAL driver supports spindle synchronization. */
typedef struct {
float rpm;
float rpm_low_limit;
float rpm_high_limit;
- float angular_position; // Number of revolutions since last reset
+ float angular_position; //!< Number of revolutions since last reset
float rpm_programmed;
uint32_t index_count;
uint32_t pulse_count;
@@ -84,11 +85,11 @@ typedef enum {
SpindleType_Null, //!< 4
} spindle_type_t;
-/*! \brief Pointer to function for configuring the spindle.
-\returns state in a \a spindle_state_t union variable.
-*/
-typedef bool (*spindle_config_ptr)(void);
-
+typedef enum {
+ SpindleHAL_Raw, //!< 0 - NOTE: read-only
+ SpindleHAL_Configured, //!< 1
+ SpindleHAL_Active, //!< 2
+} spindle_hal_t;
/*! \brief Pointer to function for setting the spindle state.
\param state a \a spindle_state_t union variable.
@@ -141,14 +142,18 @@ Used for Pulses Per Inch (PPI) laser mode.
*/
typedef void (*spindle_pulse_on_ptr)(uint_fast16_t pulse_length);
-//! Handlers for spindle support.
-typedef struct {
+struct spindle_param; // members defined below
+
+/*! \brief Handlers and data for spindle support. */
+struct spindle_ptrs {
+ spindle_id_t id; //!< Spindle id, assingned on spindle registration .
+ struct spindle_param *param; //!< Pointer to current spindle parameters, assigned when spindle is enabled.
spindle_type_t type; //!< Spindle type.
spindle_cap_t cap; //!< Spindle capabilities.
uint_fast16_t pwm_off_value; //!< Value for switching PWM signal off.
float rpm_min; //!< Minimum spindle RPM.
float rpm_max; //!< Maximum spindle RPM.
- spindle_config_ptr config; //!< Optional handler for configuring the spindle.
+ bool (*config)(struct spindle_ptrs *spindle); //!< Optional handler for configuring the spindle.
spindle_set_state_ptr set_state; //!< Handler for setting spindle state.
spindle_get_state_ptr get_state; //!< Handler for getting spindle state.
spindle_get_pwm_ptr get_pwm; //!< Handler for calculating spindle PWM value from RPM.
@@ -157,14 +162,50 @@ typedef struct {
#ifdef GRBL_ESP32
void (*esp32_off)(void); //!< Workaround handler for snowflake ESP32 Guru awaken by floating point data in ISR context.
#endif
- // Optional entry points:
+ // Optional entry points.
spindle_pulse_on_ptr pulse_on; //!< Optional handler for Pulses Per Inch (PPI) mode. Required for the laser PPI plugin.
- spindle_get_data_ptr get_data; //!< Optional handler for getting spindle data. Required for spindle sync.
- spindle_reset_data_ptr reset_data; //!< Optional handler for resetting spindle data. Required for spindle sync.
-} spindle_ptrs_t;
+ spindle_get_data_ptr get_data; //!< Optional handler for getting spindle data. Required for spindle sync, copied from hal.spindle_data.get on selection.
+ spindle_reset_data_ptr reset_data; //!< Optional handler for resetting spindle data. Required for spindle sync, copied from hal.spindle_data.reset on selection.
+};
+typedef struct spindle_ptrs spindle_ptrs_t;
+
+//! \brief Data used for Constant Surface Speed (CSS) mode calculations.
+typedef struct {
+ float surface_speed; //!< Surface speed in millimeters/min
+ float target_rpm; //!< Target RPM at end of movement
+ float delta_rpm; //!< Delta between start and target RPM
+ float max_rpm; //!< Maximum spindle RPM
+ float tool_offset; //!< Tool offset
+ uint_fast8_t axis; //!< Linear (tool) axis
+} spindle_css_data_t;
+
+/*! \brief Structure used for holding the current state of an enabled spindle. */
+typedef struct spindle_param {
+ float rpm;
+ float rpm_overridden;
+ spindle_state_t state;
+ override_t override_pct; //!< Spindle RPM override value in percent
+ spindle_css_data_t css; //!< Data used for Constant Surface Speed Mode (CSS) calculations, NULL if not in CSS mode.
+ spindle_ptrs_t *hal;
+} spindle_param_t;
+
+typedef struct {
+ spindle_get_data_ptr get; //!< Optional handler for getting spindle data. Required for spindle sync.
+ spindle_reset_data_ptr reset; //!< Optional handler for resetting spindle data. Required for spindle sync.
+} spindle_data_ptrs_t;
+
+/*! \brief Pointer to function for configuring the spindle.
+\returns state in a \a spindle_state_t union variable.
+*/
+typedef bool (*spindle_config_ptr)(spindle_ptrs_t *spindle);
+
+/*! \brief Structure holding data passed to the callback function called by spindle_enumerate_spindles(). */
typedef struct {
spindle_id_t id;
+ spindle_num_t num;
+ const char *name;
+ bool enabled;
bool is_current;
const spindle_ptrs_t *hal;
} spindle_info_t;
@@ -175,64 +216,53 @@ typedef struct {
float end;
} pwm_piece_t;
-// Precalculated values that may be set/used by HAL driver to speed up RPM to PWM conversions if variable spindle is supported
+//!* \brief Precalculated values that may be set/used by HAL driver to speed up RPM to PWM conversions if variable spindle is supported. */
typedef struct {
uint_fast16_t period;
- uint_fast16_t off_value; // NOTE: this value holds the inverted version if software PWM inversion is enabled by the driver.
+ uint_fast16_t off_value; //!< NOTE: this value holds the inverted version if software PWM inversion is enabled by the driver.
uint_fast16_t min_value;
uint_fast16_t max_value;
+ float rpm_min; //!< Minimum spindle RPM.
float pwm_gradient;
- bool invert_pwm; // NOTE: set (by driver) when inversion is done in code
+ bool invert_pwm; //!< NOTE: set (by driver) when inversion is done in code
bool always_on;
int_fast16_t offset;
uint_fast16_t n_pieces;
pwm_piece_t piece[SPINDLE_NPWM_PIECES];
} spindle_pwm_t;
+/*! \brief Pointer to callback function called by spindle_enumerate_spindles().
+\param spindle prointer to a \a spindle_info_t struct.
+*/
typedef void (*spindle_enumerate_callback_ptr)(spindle_info_t *spindle);
-void spindle_set_override (uint_fast8_t speed_override);
+void spindle_set_override (spindle_ptrs_t *spindle, override_t speed_override);
// Called by g-code parser when setting spindle state and requires a buffer sync.
-// Immediately sets spindle running state with direction and spindle RPM, if enabled.
-// Called by spindle_sync() after sync and parking motion/spindle stop override during restore.
-
-// Called by g-code parser when setting spindle state and requires a buffer sync.
-bool spindle_sync (spindle_id_t spindle_id, spindle_state_t state, float rpm);
+bool spindle_sync (spindle_ptrs_t *spindle, spindle_state_t state, float rpm);
// Sets spindle running state with direction, enable, and spindle RPM.
-bool spindle_set_state (spindle_id_t spindle_id, spindle_state_t state, float rpm);
+bool spindle_set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm);
// Spindle speed calculation and limit handling
-float spindle_set_rpm (float rpm, uint8_t speed_override);
+float spindle_set_rpm (spindle_ptrs_t *spindle, float rpm, override_t speed_override);
// Restore spindle running state with direction, enable, spindle RPM and appropriate delay.
-bool spindle_restore (spindle_state_t state, float rpm);
+bool spindle_restore (spindle_ptrs_t *spindle, spindle_state_t state, float rpm);
+
+void spindle_all_off (void);
//
// The following functions are not called by the core, may be called by driver code.
//
-/*! \brief Precompute PWM values for faster conversion.
-\param pwm_data pointer t a \a spindle_pwm_t structure.
-\param clock_hz timer clock frequency used for PWM generation.
-\returns true if successful, false if no PWM range possible - driver should revert to simple on/off spindle control if so.
-*/
-bool spindle_precompute_pwm_values (spindle_pwm_t *pwm_data, uint32_t clock_hz);
+bool spindle_precompute_pwm_values (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_data, uint32_t clock_hz);
-/*! \brief Spindle RPM to PWM conversion.
-\param pwm_data pointer t a \a spindle_pwm_t structure.
-\param rpm spindle RPM.
-\param pid_limit boolean, true if PID based spindle sync is used, false otherwise.
-
-__NOTE:__ \a spindle_precompute_pwm_values() must be called to precompute values before this function is called.
-Typically this is done in the \a hal.driver_setup handler.
- */
uint_fast16_t spindle_compute_pwm_value (spindle_pwm_t *pwm_data, float rpm, bool pid_limit);
spindle_id_t spindle_register (const spindle_ptrs_t *spindle, const char *name);
-void spindle_add_null (void);
+spindle_id_t spindle_add_null (void);
uint8_t spindle_get_count (void);
@@ -240,16 +270,25 @@ bool spindle_select (spindle_id_t spindle_id);
spindle_cap_t spindle_get_caps (void);
-/*! \brief Update PWM spindle capabilities with run-time determined parameters.
-\param pwm_caps pointer to \a spindle_pwm_t struct, NULL if spindle if not PWM capable.
+void spindle_update_caps (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_caps);
- */
-void spindle_update_caps (spindle_pwm_t *pwm_caps);
-const spindle_ptrs_t *spindle_get (spindle_id_t spindle_id);
+spindle_ptrs_t *spindle_get_hal (spindle_id_t spindle_id, spindle_hal_t hal);
-spindle_id_t spindle_get_current (void);
+const char *spindle_get_name (spindle_id_t spindle_id);
+
+spindle_id_t spindle_get_default (void);
+
+spindle_num_t spindle_enable (spindle_id_t spindle_id);
bool spindle_enumerate_spindles (spindle_enumerate_callback_ptr callback);
+//
+
+bool spindle_is_enabled (spindle_num_t spindle_num);
+
+bool spindle_is_on (void);
+
+spindle_ptrs_t *spindle_get (spindle_num_t spindle_num);
+
#endif
diff --git a/state_machine.c b/state_machine.c
index 530a9c0..23be470 100644
--- a/state_machine.c
+++ b/state_machine.c
@@ -43,8 +43,13 @@ static void state_await_resumed (uint_fast16_t rt_exec);
static void (* volatile stateHandler)(uint_fast16_t rt_exec) = state_idle;
-static float restore_spindle_rpm;
-static planner_cond_t restore_condition;
+typedef struct {
+ coolant_state_t coolant;
+ spindle_num_t spindle_num; // Active spindle
+ spindle_t spindle[N_SYS_SPINDLE];
+} restore_condition_t;
+
+static restore_condition_t restore_condition;
static sys_state_t pending_state = STATE_IDLE, sys_state = STATE_IDLE;
typedef union {
@@ -69,17 +74,33 @@ typedef struct {
// Declare and initialize parking local variables
static parking_data_t park = {0};
-static void state_restore_conditions (planner_cond_t *condition, float rpm)
+static void state_spindle_restore (spindle_t *spindle)
+{
+ if(spindle->hal)
+ spindle_restore(spindle->hal, spindle->state, spindle->rpm);
+}
+
+static void state_spindle_set_state (spindle_t *spindle)
+{
+ if(spindle->hal)
+ spindle_set_state(spindle->hal, spindle->state, spindle->rpm);
+}
+
+static void state_restore_conditions (restore_condition_t *condition)
{
if (!settings.parking.flags.enabled || !park.flags.restart) {
+ spindle_num_t spindle_num = N_SYS_SPINDLE;
+
park.flags.restoring = On; //
- spindle_restore(condition->spindle, rpm);
+ do {
+ state_spindle_restore(&condition->spindle[--spindle_num]);
+ } while(spindle_num);
// Block if safety door re-opened during prior restore actions.
if (gc_state.modal.coolant.value != hal.coolant.get_state().value) {
- // NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this pin.
+ // NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this signal.
coolant_set_state(condition->coolant);
delay_sec(settings.safety_door.coolant_on_delay, DelayMode_SysSuspend);
}
@@ -92,6 +113,9 @@ static void state_restore_conditions (planner_cond_t *condition, float rpm)
bool initiate_hold (uint_fast16_t new_state)
{
+ spindle_ptrs_t *spindle;
+ spindle_num_t spindle_num = N_SYS_SPINDLE;
+
if (settings.parking.flags.enabled) {
memset(&park.plan_data, 0, sizeof(plan_line_data_t));
park.plan_data.condition.system_motion = On;
@@ -101,16 +125,35 @@ bool initiate_hold (uint_fast16_t new_state)
plan_block_t *block = plan_get_current_block();
- if (block == NULL) {
- restore_condition.spindle = gc_state.modal.spindle;
- restore_condition.coolant.mask = gc_state.modal.coolant.mask | hal.coolant.get_state().mask;
- restore_spindle_rpm = gc_state.spindle.rpm;
- } else {
- restore_condition = block->condition;
- restore_spindle_rpm = block->spindle.rpm;
- }
+ restore_condition.spindle_num = 0;
- if (sys.mode == Mode_Laser && settings.flags.disable_laser_during_hold)
+ do {
+ if((spindle = spindle_get(--spindle_num))) {
+ if(block && block->spindle.hal == spindle) {
+ restore_condition.spindle_num = spindle_num;
+ restore_condition.spindle[spindle_num].hal = block->spindle.hal;
+ restore_condition.spindle[spindle_num].rpm = block->spindle.rpm;
+ restore_condition.spindle[spindle_num].state = block->spindle.state;
+ } else if(gc_state.spindle.hal == spindle) {
+ restore_condition.spindle_num = spindle_num;
+ restore_condition.spindle[spindle_num].hal = gc_state.spindle.hal;
+ restore_condition.spindle[spindle_num].rpm = gc_state.spindle.rpm;
+ restore_condition.spindle[spindle_num].state = gc_state.modal.spindle.state;
+ } else {
+ restore_condition.spindle[spindle_num].hal = spindle;
+ restore_condition.spindle[spindle_num].rpm = spindle->param->rpm;
+ restore_condition.spindle[spindle_num].state = spindle->param->state;
+ }
+ } else
+ restore_condition.spindle[spindle_num].hal = NULL;
+ } while(spindle_num);
+
+ if (block)
+ restore_condition.coolant.mask = block->condition.coolant.mask;
+ else
+ restore_condition.coolant.mask = gc_state.modal.coolant.mask | hal.coolant.get_state().mask;
+
+ if (restore_condition.spindle[restore_condition.spindle_num].hal->cap.laser && settings.flags.disable_laser_during_hold)
enqueue_accessory_override(CMD_OVERRIDE_SPINDLE_STOP);
if (sys_state & (STATE_CYCLE|STATE_JOG)) {
@@ -174,14 +217,14 @@ void state_set (sys_state_t new_state)
sys_state = new_state;
sys.steppers_deenergize = false; // Cancel stepper deenergize if pending.
st_prep_buffer(); // Initialize step segment buffer before beginning cycle.
- if (block->condition.spindle.synchronized) {
+ if (block->spindle.state.synchronized) {
- if (hal.spindle.reset_data)
- hal.spindle.reset_data();
+ if (block->spindle.hal->reset_data)
+ block->spindle.hal->reset_data();
- uint32_t index = hal.spindle.get_data(SpindleData_Counters)->index_count + 2;
+ uint32_t index = block->spindle.hal->get_data(SpindleData_Counters)->index_count + 2;
- while(index != hal.spindle.get_data(SpindleData_Counters)->index_count); // check for abort in this loop?
+ while(index != block->spindle.hal->get_data(SpindleData_Counters)->index_count); // check for abort in this loop?
}
st_wake_up();
@@ -252,7 +295,7 @@ void state_set (sys_state_t new_state)
// Suspend manager. Controls spindle overrides in hold states.
void state_suspend_manager (void)
{
- if (stateHandler != state_await_resume || !gc_state.modal.spindle.on)
+ if (stateHandler != state_await_resume || !gc_state.modal.spindle.state.on)
return;
if (sys.override.spindle_stop.value) {
@@ -260,7 +303,7 @@ void state_suspend_manager (void)
// Handles beginning of spindle stop
if (sys.override.spindle_stop.initiate) {
sys.override.spindle_stop.value = 0; // Clear stop override state
- spindle_set_state(0, (spindle_state_t){0}, 0.0f); // De-energize
+ spindle_set_state(restore_condition.spindle[restore_condition.spindle_num].hal, (spindle_state_t){0}, 0.0f); // De-energize
sys.override.spindle_stop.enabled = On; // Set stop override state to enabled, if de-energized.
if(grbl.on_override_changed)
grbl.on_override_changed(OverrideChanged_SpindleState);
@@ -269,18 +312,18 @@ void state_suspend_manager (void)
// Handles restoring of spindle state
if (sys.override.spindle_stop.restore) {
grbl.report.feedback_message(Message_SpindleRestore);
- if (sys.mode == Mode_Laser) // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
+ if (restore_condition.spindle[restore_condition.spindle_num].hal->cap.laser) // When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
sys.step_control.update_spindle_rpm = On;
else
- spindle_set_state(0, restore_condition.spindle, restore_spindle_rpm);
+ state_spindle_set_state(&restore_condition.spindle[restore_condition.spindle_num]);
sys.override.spindle_stop.value = 0; // Clear stop override state
if(grbl.on_override_changed)
grbl.on_override_changed(OverrideChanged_SpindleState);
}
- } else if (sys.step_control.update_spindle_rpm && hal.spindle.get_state().on) {
+ } else if (sys.step_control.update_spindle_rpm && restore_condition.spindle[0].hal->get_state().on) {
// Handles spindle state during hold. NOTE: Spindle speed overrides may be altered during hold state.
- spindle_set_state(0, restore_condition.spindle, restore_spindle_rpm);
+ state_spindle_set_state(&restore_condition.spindle[restore_condition.spindle_num]);;
sys.step_control.update_spindle_rpm = Off;
}
}
@@ -394,7 +437,7 @@ static void state_await_hold (uint_fast16_t rt_exec)
switch (sys_state) {
case STATE_TOOL_CHANGE:
- hal.spindle.set_state((spindle_state_t){0}, 0.0f); // De-energize
+ spindle_all_off(); // De-energize
hal.coolant.set_state((coolant_state_t){0}); // De-energize
break;
@@ -410,7 +453,7 @@ static void state_await_hold (uint_fast16_t rt_exec)
// Parking requires parking axis homed, the current location not exceeding the???
// parking target location, and laser mode disabled.
- if (settings.parking.flags.enabled && !sys.override.control.parking_disable && sys.mode != Mode_Laser) {
+ if (settings.parking.flags.enabled && !sys.override.control.parking_disable && settings.mode != Mode_Laser) {
// Get current position and store as restore location.
if (!park.flags.active) {
@@ -441,8 +484,9 @@ static void state_await_hold (uint_fast16_t rt_exec)
park.target[settings.parking.axis] = park.retract_waypoint;
park.plan_data.feed_rate = settings.parking.pullout_rate;
park.plan_data.condition.coolant = restore_condition.coolant; // Retain coolant state
- park.plan_data.condition.spindle = restore_condition.spindle; // Retain spindle state
- park.plan_data.spindle.rpm = restore_spindle_rpm;
+ park.plan_data.spindle.state = restore_condition.spindle[restore_condition.spindle_num].state; // Retain spindle state
+ park.plan_data.spindle.hal = restore_condition.spindle[restore_condition.spindle_num].hal;
+ park.plan_data.spindle.rpm = restore_condition.spindle[restore_condition.spindle_num].rpm;
await_motion = mc_parking_motion(park.target, &park.plan_data);
}
@@ -455,13 +499,13 @@ static void state_await_hold (uint_fast16_t rt_exec)
} else {
// Parking motion not possible. Just disable the spindle and coolant.
// NOTE: Laser mode does not start a parking motion to ensure the laser stops immediately.
- hal.spindle.set_state((spindle_state_t){0}, 0.0f); // De-energize
+ spindle_all_off(); // De-energize
if (!settings.safety_door.flags.keep_coolant_on)
hal.coolant.set_state((coolant_state_t){0}); // De-energize
sys.parking_state = hal.control.get_state().safety_door_ajar ? Parking_DoorAjar : Parking_DoorClosed;
}
} else {
- hal.spindle.set_state((spindle_state_t){0}, 0.0f); // De-energize
+ spindle_all_off(); // De-energize
if (!settings.safety_door.flags.keep_coolant_on)
hal.coolant.set_state((coolant_state_t){0}); // De-energize
sys.parking_state = hal.control.get_state().safety_door_ajar ? Parking_DoorAjar : Parking_DoorClosed;
@@ -540,14 +584,14 @@ static void state_await_resume (uint_fast16_t rt_exec)
default:
if (!settings.flags.restore_after_feed_hold) {
- if (!hal.spindle.get_state().on)
+ if (!restore_condition.spindle[restore_condition.spindle_num].hal->get_state().on)
gc_spindle_off();
sys.override.spindle_stop.value = 0; // Clear spindle stop override states
} else {
- if (restore_condition.spindle.on != hal.spindle.get_state().on) {
+ if (restore_condition.spindle[restore_condition.spindle_num].state.on != restore_condition.spindle[restore_condition.spindle_num].hal->get_state().on) {
grbl.report.feedback_message(Message_SpindleRestore);
- spindle_restore(restore_condition.spindle, restore_spindle_rpm);
+ state_spindle_restore(&restore_condition.spindle[restore_condition.spindle_num]);
}
if (restore_condition.coolant.value != hal.coolant.get_state().value) {
@@ -631,9 +675,9 @@ static void state_await_waypoint_retract (uint_fast16_t rt_exec)
}
// NOTE: Clear accessory state after retract and after an aborted restore motion.
- park.plan_data.condition.spindle.value = 0;
+ park.plan_data.spindle.state.value = 0;
park.plan_data.spindle.rpm = 0.0f;
- hal.spindle.set_state(park.plan_data.condition.spindle, 0.0f); // De-energize
+ park.plan_data.spindle.hal->set_state(park.plan_data.spindle.state, 0.0f); // De-energize
if (!settings.safety_door.flags.keep_coolant_on) {
park.plan_data.condition.coolant.value = 0;
@@ -682,7 +726,7 @@ static void state_restore (uint_fast16_t rt_exec)
stateHandler = state_await_resumed;
// Restart spindle and coolant, delay to power-up.
- state_restore_conditions(&restore_condition, restore_spindle_rpm);
+ state_restore_conditions(&restore_condition);
if(park.flags.restart) {
// Restart flag was set by a safety door event during
@@ -702,8 +746,8 @@ static void state_restore (uint_fast16_t rt_exec)
// original position through valid machine space or by not moving at all.
park.plan_data.feed_rate = settings.parking.pullout_rate;
park.plan_data.condition.coolant = restore_condition.coolant;
- park.plan_data.condition.spindle = restore_condition.spindle;
- park.plan_data.spindle.rpm = restore_spindle_rpm;
+ park.plan_data.spindle.state = restore_condition.spindle[restore_condition.spindle_num].state;
+ park.plan_data.spindle.rpm = restore_condition.spindle[restore_condition.spindle_num].rpm;
await_motion = mc_parking_motion(park.restore_target, &park.plan_data);
}
diff --git a/stepper.c b/stepper.c
index a0bbbdd..6647c03 100644
--- a/stepper.c
+++ b/stepper.c
@@ -395,16 +395,16 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void)
#endif
if(st.exec_segment->update_pwm)
- hal.spindle.update_pwm(st.exec_segment->spindle_pwm);
+ st.exec_segment->update_pwm(st.exec_segment->spindle_pwm);
else if(st.exec_segment->update_rpm)
- hal.spindle.update_rpm(st.exec_segment->spindle_rpm);
+ st.exec_segment->update_rpm(st.exec_segment->spindle_rpm);
} else {
// Segment buffer empty. Shutdown.
st_go_idle();
// Ensure pwm is set properly upon completion of rate-controlled motion.
- if (st.exec_block->dynamic_rpm && sys.mode == Mode_Laser)
- hal.spindle.update_pwm(hal.spindle.pwm_off_value);
+ if (st.exec_block->dynamic_rpm && st.exec_block->spindle->cap.laser)
+ st.exec_block->spindle->update_pwm(st.exec_block->spindle->pwm_off_value);
st.exec_block = NULL;
system_set_exec_state_flag(EXEC_CYCLE_COMPLETE); // Flag main program for cycle complete
@@ -741,11 +741,12 @@ void st_prep_buffer (void)
// Setup laser mode variables. RPM rate adjusted motions will always complete a motion with the
// spindle off.
- if ((st_prep_block->dynamic_rpm = pl_block->condition.is_rpm_rate_adjusted))
+ if ((st_prep_block->dynamic_rpm = pl_block->condition.is_rpm_rate_adjusted)) {
// Pre-compute inverse programmed rate to speed up RPM updating per step segment.
+ st_prep_block->spindle = pl_block->spindle.hal;
prep.inv_feedrate = pl_block->condition.is_laser_ppi_mode ? 1.0f : 1.0f / pl_block->programmed_rate;
- else
- st_prep_block->dynamic_rpm = pl_block->condition.is_rpm_pos_adjusted;
+ } else
+ st_prep_block->dynamic_rpm = !!pl_block->spindle.css;
}
/* ---------------------------------------------------------------------------------
@@ -842,7 +843,7 @@ void st_prep_buffer (void)
}
if(state_get() != STATE_HOMING)
- sys.step_control.update_spindle_rpm |= (sys.mode == Mode_Laser); // Force update whenever updating block in laser mode.
+ sys.step_control.update_spindle_rpm |= pl_block->spindle.hal->cap.laser; // Force update whenever updating block in laser mode.
probe_asserted = false;
}
@@ -856,7 +857,8 @@ void st_prep_buffer (void)
// Set new segment to point to the current segment data block.
prep_segment->exec_block = st_prep_block;
- prep_segment->update_rpm = prep_segment->update_pwm = false;
+ prep_segment->update_rpm = NULL;
+ prep_segment->update_pwm = NULL;
/*------------------------------------------------------------------------------------
Compute the average velocity of this new segment by determining the total distance
@@ -969,27 +971,31 @@ void st_prep_buffer (void)
if (sys.step_control.update_spindle_rpm || st_prep_block->dynamic_rpm) {
float rpm;
- if (pl_block->condition.spindle.on) {
- // NOTE: Feed and rapid overrides are independent of PWM value and do not alter laser power/rate.
- // If current_speed is zero, then may need to be rpm_min*(100/MAX_SPINDLE_RPM_OVERRIDE)
- // but this would be instantaneous only and during a motion. May not matter at all.
- rpm = spindle_set_rpm(pl_block->condition.is_rpm_rate_adjusted && !pl_block->condition.is_laser_ppi_mode
- ? pl_block->spindle.rpm * prep.current_speed * prep.inv_feedrate
- : pl_block->spindle.rpm, sys.override.spindle_rpm);
-
- if(pl_block->condition.is_rpm_pos_adjusted) {
+ if (pl_block->spindle.state.on) {
+ if(pl_block->spindle.css) {
float npos = (float)(pl_block->step_event_count - prep.steps_remaining) / (float)pl_block->step_event_count;
- rpm += (spindle_set_rpm(pl_block->spindle.css.target_rpm, sys.override.spindle_rpm) - prep.current_spindle_rpm) * npos;
+ rpm = spindle_set_rpm(pl_block->spindle.hal,
+ pl_block->spindle.rpm + pl_block->spindle.css->delta_rpm * npos,
+ pl_block->spindle.hal->param->override_pct);
+ } else {
+ // NOTE: Feed and rapid overrides are independent of PWM value and do not alter laser power/rate.
+ // If current_speed is zero, then may need to be rpm_min*(100/MAX_SPINDLE_RPM_OVERRIDE)
+ // but this would be instantaneous only and during a motion. May not matter at all.
+ rpm = spindle_set_rpm(pl_block->spindle.hal,
+ pl_block->condition.is_rpm_rate_adjusted && !pl_block->condition.is_laser_ppi_mode
+ ? pl_block->spindle.rpm * prep.current_speed * prep.inv_feedrate
+ : pl_block->spindle.rpm, pl_block->spindle.hal->param->override_pct);
}
} else
- sys.spindle_rpm = rpm = 0.0f;
+ pl_block->spindle.hal->param->rpm = rpm = 0.0f;
if(rpm != prep.current_spindle_rpm) {
- if((prep_segment->update_pwm = hal.spindle.get_pwm != NULL)) {
+ if(pl_block->spindle.hal->get_pwm != NULL) {
prep.current_spindle_rpm = rpm;
- prep_segment->spindle_pwm = hal.spindle.get_pwm(rpm);
+ prep_segment->update_pwm = pl_block->spindle.hal->update_pwm;
+ prep_segment->spindle_pwm = pl_block->spindle.hal->get_pwm(rpm);
} else {
- prep_segment->update_rpm = true;
+ prep_segment->update_rpm = pl_block->spindle.hal->update_rpm;
prep.current_spindle_rpm = prep_segment->spindle_rpm = rpm;
}
sys.step_control.update_spindle_rpm = Off;
@@ -1036,7 +1042,7 @@ void st_prep_buffer (void)
uint32_t cycles = (uint32_t)ceilf(cycles_per_min * inv_rate); // (cycles/step)
// Record end position of segment relative to block if spindle synchronized motion
- if((prep_segment->spindle_sync = pl_block->condition.spindle.synchronized)) {
+ if((prep_segment->spindle_sync = pl_block->spindle.state.synchronized)) {
prep.target_position += dt * prep.target_feed;
prep_segment->cruising = prep.ramp_type == Ramp_Cruise;
prep_segment->target_position = prep.target_position; //st_prep_block->millimeters - pl_block->millimeters;
diff --git a/stepper.h b/stepper.h
index 46e1f2a..02b0676 100644
--- a/stepper.h
+++ b/stepper.h
@@ -50,24 +50,25 @@ typedef struct st_block {
char *message; //!< Message to be displayed when block is executed
output_command_t *output_commands; //!< Output commands (linked list) to be performed when block is executed
bool dynamic_rpm; //!< Tracks motions that require dynamic RPM adjustment
+ spindle_ptrs_t *spindle; //!< Pointer to current spindle for motions that require dynamic RPM adjustment
bool backlash_motion;
} st_block_t;
typedef struct st_segment {
- uint_fast8_t id; //!< Id may be used by driver to track changes
- struct st_segment *next; //!< Pointer to next element in cirular list of segments
- st_block_t *exec_block; //!< Pointer to the block data for the segment
- uint32_t cycles_per_tick; //!< Step distance traveled per ISR tick, aka step rate.
+ uint_fast8_t id; //!< Id may be used by driver to track changes
+ struct st_segment *next; //!< Pointer to next element in cirular list of segments
+ st_block_t *exec_block; //!< Pointer to the block data for the segment
+ uint32_t cycles_per_tick; //!< Step distance traveled per ISR tick, aka step rate.
float current_rate;
- float target_position; //!< Target position of segment relative to block start, used by spindle sync code
- uint_fast16_t n_step; //!< Number of step events to be executed for this segment
- uint_fast16_t spindle_pwm; //!< Spindle PWM to be set at the start of segment execution
- float spindle_rpm; //!< Spindle RPM to be set at the start of the segment execution
- bool update_pwm; //!< True if set spindle speed at the start of the segment execution
- bool update_rpm; //!< True if set spindle speed at the start of the segment execution
- bool spindle_sync; //!< True if block is spindle synchronized
- bool cruising; //!< True when in cruising part of profile, only set for spindle synced moves
- uint_fast8_t amass_level; //!< Indicates AMASS level for the ISR to execute this segment
+ float target_position; //!< Target position of segment relative to block start, used by spindle sync code
+ uint_fast16_t n_step; //!< Number of step events to be executed for this segment
+ uint_fast16_t spindle_pwm; //!< Spindle PWM to be set at the start of segment execution
+ float spindle_rpm; //!< Spindle RPM to be set at the start of the segment execution
+ bool spindle_sync; //!< True if block is spindle synchronized
+ bool cruising; //!< True when in cruising part of profile, only set for spindle synced moves
+ uint_fast8_t amass_level; //!< Indicates AMASS level for the ISR to execute this segment
+ spindle_update_pwm_ptr update_pwm; //!< Valid pointer to spindle.update_pwm() if set spindle speed at the start of the segment execution
+ spindle_update_rpm_ptr update_rpm; //!< Valid pointer to spindle.update_rmp() if set spindle speed at the start of the segment execution
} segment_t;
//! Stepper ISR data struct. Contains the running data for the main stepper ISR.
diff --git a/system.c b/system.c
index 091f95d..46d3730 100644
--- a/system.c
+++ b/system.c
@@ -56,6 +56,7 @@ static status_code_t toggle_optional_stop (sys_state_t state, char *args);
static status_code_t check_mode (sys_state_t state, char *args);
static status_code_t disable_lock (sys_state_t state, char *args);
static status_code_t output_help (sys_state_t state, char *args);
+static status_code_t output_spindles (sys_state_t state, char *args);
static status_code_t home (sys_state_t state, char *args);
static status_code_t home_x (sys_state_t state, char *args);
static status_code_t home_y (sys_state_t state, char *args);
@@ -120,8 +121,8 @@ ISR_CODE void ISR_FUNC(control_interrupt_handler)(control_signals_t signals)
// NOTE: at least for lasers there should be an external interlock blocking laser power.
if(state_get() != STATE_IDLE && state_get() != STATE_JOG)
system_set_exec_state_flag(EXEC_SAFETY_DOOR);
- if(sys.mode == Mode_Laser) // Turn off spindle immediately (laser) when in laser mode
- hal.spindle.set_state((spindle_state_t){0}, 0.0f);
+ if(settings.mode == Mode_Laser) // Turn off spindle immediately (laser) when in laser mode
+ spindle_all_off();
} else
system_set_exec_state_flag(EXEC_SAFETY_DOOR);
}
@@ -166,10 +167,12 @@ void system_execute_startup (void)
// Reset spindle encoder data
status_code_t spindle_reset_data (sys_state_t state, char *args)
{
- if(hal.spindle.reset_data)
- hal.spindle.reset_data();
+ spindle_ptrs_t *spindle = gc_spindle_get();
- return hal.spindle.reset_data ? Status_OK : Status_InvalidStatement;
+ if(spindle->reset_data)
+ spindle->reset_data();
+
+ return spindle->reset_data ? Status_OK : Status_InvalidStatement;
}
status_code_t read_int (char *s, int32_t *value)
@@ -233,6 +236,7 @@ PROGMEM static const sys_command_t sys_commands[] = {
{ "HV", false, home_v },
#endif
{ "HELP", false, output_help },
+ { "SPINDLES", false, output_spindles },
{ "SLP", true, enter_sleep },
{ "TLR", true, set_tool_reference },
{ "TPW", true, tool_probe_workpiece },
@@ -284,6 +288,7 @@ void system_command_help (void)
hal.stream.write("$SLP - enter sleep mode" ASCII_EOL);
hal.stream.write("$HELP - output help topics" ASCII_EOL);
hal.stream.write("$HELP - output help for " ASCII_EOL);
+ hal.stream.write("$SPINDLES - output spindle list" ASCII_EOL);
hal.stream.write("$RST=* - restore/reset all settings" ASCII_EOL);
hal.stream.write("$RST=$ - restore default settings" ASCII_EOL);
if(settings_get_details()->next)
@@ -293,10 +298,13 @@ void system_command_help (void)
#else
hal.stream.write("$RST=# - reset offsets" ASCII_EOL);
#endif
- if(hal.spindle.reset_data)
+
+ spindle_ptrs_t *spindle = gc_spindle_get();
+ if(spindle->reset_data)
hal.stream.write("$SR - reset spindle encoder data" ASCII_EOL);
- if(hal.spindle.get_data)
+ if(spindle->get_data)
hal.stream.write("$SD - output spindle encoder data" ASCII_EOL);
+
hal.stream.write("$TLR - set tool offset reference" ASCII_EOL);
hal.stream.write("$TPW - probe tool plate" ASCII_EOL);
hal.stream.write("$EA - enumerate alarms" ASCII_EOL);
@@ -626,6 +634,11 @@ static status_code_t output_help (sys_state_t state, char *args)
return report_help(args);
}
+static status_code_t output_spindles (sys_state_t state, char *args)
+{
+ return report_spindles();
+}
+
static status_code_t go_home (sys_state_t state, axes_signals_t axes)
{
if(axes.mask && !settings.homing.flags.single_axis_commands)
@@ -661,7 +674,7 @@ static status_code_t go_home (sys_state_t state, axes_signals_t axes)
if (retval == Status_OK && !sys.abort) {
state_set(STATE_IDLE); // Set to IDLE when complete.
st_go_idle(); // Set steppers to the settings idle state before returning.
- report_feedback_message(Message_None);
+ grbl.report.feedback_message(Message_None);
// Execute startup scripts after successful homing.
if (sys.homing.mask && (sys.homing.mask & sys.homed.mask) == sys.homing.mask)
system_execute_startup();
diff --git a/system.h b/system.h
index aecb245..4b432d8 100644
--- a/system.h
+++ b/system.h
@@ -190,9 +190,9 @@ typedef union {
} report_tracking_flags_t;
typedef struct {
- uint8_t feed_rate; //!< Feed rate override value in percent
- uint8_t rapid_rate; //!< Rapids override value in percent
- uint8_t spindle_rpm; //!< Spindle speed override value in percent
+ override_t feed_rate; //!< Feed rate override value in percent
+ override_t rapid_rate; //!< Rapids override value in percent
+// Spindle override has been moved to per spindle in spindle_param_t
spindle_stop_t spindle_stop; //!< Tracks spindle stop override states
gc_override_flags_t control; //!< Tracks override control states.
} overrides_t;
@@ -245,7 +245,6 @@ typedef struct system {
volatile probing_state_t probing_state; //!< Probing state value. Used to coordinate the probing cycle with stepper ISR.
volatile rt_exec_t rt_exec_state; //!< Realtime executor bitflag variable for state management. See EXEC bitmasks.
volatile uint_fast16_t rt_exec_alarm; //!< Realtime executor bitflag variable for setting various alarms.
- float spindle_rpm; //!< Current spindle RPM
int32_t var5399; //!< Last result from M66 - wait on input
#ifdef PID_LOG
pid_data_t pid_log;
@@ -261,7 +260,6 @@ typedef struct system {
bool cold_start; //!< Set to true on boot, is false on subsequent soft resets.
bool driver_started; //!< Set to true when driver initialization is completed.
bool mpg_mode; //!< To be moved to system_flags_t
- machine_mode_t mode; //!< Current machine mode, copied from settings.mode on startup.
signal_event_t last_event; //!< Last signal events (control and limits signal).
int32_t position[N_AXIS]; //!< Real-time machine (aka home) position vector in steps.
//@}
diff --git a/tool_change.c b/tool_change.c
index 7127f14..86360c0 100644
--- a/tool_change.c
+++ b/tool_change.c
@@ -128,7 +128,7 @@ static bool restore (void)
sync_position();
coolant_sync(gc_state.modal.coolant);
- spindle_restore(gc_state.modal.spindle, gc_state.spindle.rpm);
+ spindle_restore(spindle_get(0), 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);
@@ -162,7 +162,7 @@ static void execute_restore (sys_state_t state)
change_completed();
- report_feedback_message(Message_None);
+ grbl.report.feedback_message(Message_None);
if(ok)
system_set_exec_state_flag(EXEC_CYCLE_START);
@@ -196,6 +196,7 @@ static void execute_probe (sys_state_t state)
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;
if((ok = ok && mc_probe_cycle(target.values, &plan_data, flags) == GCProbe_Found))
@@ -217,7 +218,7 @@ static void execute_probe (sys_state_t state)
sys.tlo_reference[plane.axis_linear] = sys.probe_position[plane.axis_linear];
sys.tlo_reference_set.mask |= bit(plane.axis_linear);
sys.report.tlo_reference = On;
- report_feedback_message(Message_ReferenceTLOEstablished);
+ grbl.report.feedback_message(Message_ReferenceTLOEstablished);
} else
gc_set_tool_offset(ToolLengthOffset_EnableDynamic, plane.axis_linear,
sys.probe_position[plane.axis_linear] - sys.tlo_reference[plane.axis_linear]);
@@ -332,7 +333,7 @@ static status_code_t tool_change (parser_state_t *parser_state)
block_cycle_start = settings.tool_change.mode != ToolChange_SemiAutomatic;
// Stop spindle and coolant.
- hal.spindle.set_state((spindle_state_t){0}, 0.0f);
+ spindle_all_off();
hal.coolant.set_state((coolant_state_t){0});
execute_posted = false;
@@ -434,7 +435,7 @@ void tc_clear_tlo_reference (axes_signals_t homing_cycle)
#else
gc_get_plane_data(&plane, gc_state.modal.plane_select);
#endif
- if(homing_cycle.mask & (sys.mode == Mode_Lathe ? (X_AXIS_BIT|Z_AXIS_BIT) : bit(plane.axis_linear))) {
+ if(homing_cycle.mask & (settings.mode == Mode_Lathe ? (X_AXIS_BIT|Z_AXIS_BIT) : bit(plane.axis_linear))) {
sys.report.tlo_reference = sys.tlo_reference_set.mask != 0;
sys.tlo_reference_set.mask = 0; // Invalidate tool length offset reference
}