diff --git a/README.md b/README.md
index dd9a8f7..53b095d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
## grblHAL ##
-Latest build date is 20250718, see the [changelog](changelog.md) for details.
+Latest build date is 20250724, see the [changelog](changelog.md) for details.
> [!NOTE]
> A settings reset will be performed on an update of builds prior to 20241208. Backup and restore of settings is recommended.
diff --git a/changelog.md b/changelog.md
index d397ddf..81100c9 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,6 +1,23 @@
## grblHAL changelog
-Build 20250724
+
+Core:
+
+Fix for optional control signals not respecting `$14` inversion setting. Ref. issue [#780](https://github.com/grblHAL/core/issues/780).
+
+For developers: added core `on_spindle_at_speed` event. This must be verified with actual VFD spindles, I only have a simulator available.
+Improved handling of "iterated" settings, can now be fully implemented by plugins.
+
+Plugins:
+
+* Spindle, select: updated for core change, fixed bug in tool number start vs. spindle settings \($52x\). Ref. iMXRT1062 issue [#99](https://github.com/grblHAL/iMXRT1062/issues/99).
+
+* Spindle, offset: added setting `$772` for options, currently _Update G92 on spindle change_ is available.
+
+---
+
+Build 20250720
Core:
diff --git a/core_handlers.h b/core_handlers.h
index ac253ef..6dc9936 100644
--- a/core_handlers.h
+++ b/core_handlers.h
@@ -88,6 +88,7 @@ typedef void (*on_parser_init_ptr)(parser_state_t *gc_state);
typedef void (*on_state_change_ptr)(sys_state_t state);
typedef void (*on_override_changed_ptr)(override_changed_t override);
typedef void (*on_spindle_programmed_ptr)(spindle_ptrs_t *spindle, spindle_state_t state, float rpm, spindle_rpm_mode_t mode);
+typedef void (*on_spindle_at_speed_ptr)(spindle_ptrs_t *spindle, spindle_state_t state);
typedef void (*on_wco_changed_ptr)(void);
typedef void (*on_wco_saved_ptr)(coord_system_id_t id, coord_data_t *data);
typedef void (*on_program_completed_ptr)(program_flow_t program_flow, bool check_mode);
@@ -223,6 +224,7 @@ typedef struct {
on_override_changed_ptr on_override_changed;
on_report_handlers_init_ptr on_report_handlers_init;
on_spindle_programmed_ptr on_spindle_programmed;
+ on_spindle_at_speed_ptr on_spindle_at_speed;
on_wco_changed_ptr on_wco_changed;
on_wco_saved_ptr on_wco_saved;
on_program_completed_ptr on_program_completed;
diff --git a/grbl.h b/grbl.h
index 8c86072..69259f1 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20250720
+#define GRBL_BUILD 20250724
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/ioports.c b/ioports.c
index 04c0164..759a1fe 100644
--- a/ioports.c
+++ b/ioports.c
@@ -1276,8 +1276,6 @@ static const setting_detail_t ioport_settings[] = {
// { Settings_IoPort_OD_Enable, Group_AuxPorts, "I/O Port outputs as open drain", NULL, Format_Bitfield, digital.out.port_names, NULL, NULL, Setting_NonCoreFn, aux_set_value, aux_get_value, is_setting_available }
};
-#ifndef NO_SETTINGS_DESCRIPTIONS
-
static const setting_descr_t ioport_settings_descr[] = {
{ Settings_IoPort_InvertIn, "Invert IOPort inputs." },
// { Settings_IoPort_Pullup_Disable, "Disable IOPort input pullups." },
@@ -1285,8 +1283,6 @@ static const setting_descr_t ioport_settings_descr[] = {
// { Settings_IoPort_OD_Enable, "Set IOPort outputs as open drain (OD)." }
};
-#endif
-
static bool config_probe_pins (pin_function_t function, gpio_in_config_t *config)
{
bool ok = true;
@@ -1360,12 +1356,13 @@ void ioport_setting_changed (setting_id_t id)
if(xbar->config && xbar->function < Input_Probe) {
in_config.debounce = xbar->mode.debounce;
- in_config.inverted = !!(settings.ioport.invert_in.mask & (1 << port));
- in_config.pull_mode = (pull_mode_t)xbar->mode.pull_mode;
if((ctrl = xbar_fn_to_signals_mask(xbar->function)).mask) {
in_config.inverted = !!(settings.control_invert.mask & ctrl.mask);
in_config.pull_mode = (settings.control_disable_pullup.mask & ctrl.mask) ? PullMode_None : PullMode_Up;
+ } else {
+ in_config.inverted = !!(settings.ioport.invert_in.mask & (1 << port));
+ in_config.pull_mode = (pull_mode_t)xbar->mode.pull_mode;
}
if(in_config.inverted)
@@ -1417,9 +1414,7 @@ static void ioports_configure (settings_t *settings)
if(!config_probe_pins(xbar->function, &in_config) && xbar->function < Input_Probe) {
control_signals_t ctrl;
if((ctrl = xbar_fn_to_signals_mask(xbar->function)).mask) {
-#ifdef RP2040 // RP2xxx MCUs use hardware signal inversion
in_config.inverted = !!(settings->control_invert.mask & ctrl.mask);
-#endif
in_config.pull_mode = (settings->control_disable_pullup.mask & ctrl.mask) ? PullMode_None : PullMode_Up;
}
}
@@ -1463,10 +1458,8 @@ void ioports_add_settings (driver_settings_load_ptr settings_loaded, setting_cha
.n_groups = sizeof(ioport_groups) / sizeof(setting_group_detail_t),
.settings = ioport_settings,
.n_settings = sizeof(ioport_settings) / sizeof(setting_detail_t),
- #ifndef NO_SETTINGS_DESCRIPTIONS
.descriptions = ioport_settings_descr,
.n_descriptions = sizeof(ioport_settings_descr) / sizeof(setting_descr_t),
- #endif
.save = settings_write_global
};
diff --git a/pin_bits_masks.h b/pin_bits_masks.h
index 8c57369..d7f0994 100644
--- a/pin_bits_masks.h
+++ b/pin_bits_masks.h
@@ -297,8 +297,10 @@ typedef bool (*aux_claim_explicit_ptr)(aux_ctrl_t *aux_ctrl);
static bool aux_ctrl_claim_port (xbar_t *properties, uint8_t port, void *data)
{
- if(ioport_claim(Port_Digital, Port_Input, &port, xbar_fn_to_pinname(((aux_ctrl_t *)data)->function)))
+ if(ioport_claim(Port_Digital, Port_Input, &port, NULL)) {
((aux_ctrl_t *)data)->aux_port = port;
+ ioport_set_function(properties, ((aux_ctrl_t *)data)->function, &((aux_ctrl_t *)data)->cap);
+ }
return ((aux_ctrl_t *)data)->aux_port != IOPORT_UNASSIGNED;
}
diff --git a/settings.c b/settings.c
index 0fa8aec..7824c08 100644
--- a/settings.c
+++ b/settings.c
@@ -1233,8 +1233,7 @@ inline static setting_id_t normalize_id (setting_id_t id)
(id > Setting_MacroPort0 && id <= Setting_MacroPort9) ||
(id > Setting_ButtonAction0 && id <= Setting_ButtonAction9) ||
(id > Setting_Action0 && id <= Setting_Action9) ||
- (id > Setting_ActionPort0 && id <= Setting_ActionPort9) ||
- (id > Setting_SpindleToolStart0 && id <= Setting_SpindleToolStart7))
+ (id > Setting_ActionPort0 && id <= Setting_ActionPort9))
id = (setting_id_t)(id - (id % 10));
return id;
@@ -2151,7 +2150,7 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_DoorCoolantOnDelay, Group_SafetyDoor, "Coolant on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtended, &settings.safety_door.coolant_on_delay, NULL, is_setting_available, { .allow_null = On } },
#endif
{ Setting_SpindleOnDelay, Group_Spindle, "Spindle on delay", "s", Format_Decimal, "#0.0", "0.5", "20", Setting_IsExtendedFn, set_float, get_float, is_setting_available, { .allow_null = On } },
- { Setting_SpindleType, Group_Spindle, "Default spindle", NULL, Format_RadioButtons, spindle_types, NULL, NULL, Setting_IsExtendedFn, set_default_spindle, get_int, is_setting_available },
+ { Setting_SpindleType, Group_Spindle, "Default spindle", NULL, Format_RadioButtons, spindle_types, NULL, NULL, Setting_IsExtendedFn, set_default_spindle, get_int, is_setting_available, { .reboot_required = On } },
{ Setting_PlannerBlocks, Group_General, "Planner buffer blocks", NULL, Format_Int16, "####0", "30", "1000", Setting_IsExtended, &settings.planner_buffer_blocks, NULL, NULL, { .reboot_required = On } },
{ Setting_AutoReportInterval, Group_General, "Autoreport interval", "ms", Format_Int16, "###0", "100", "1000", Setting_IsExtendedFn, set_report_interval, get_int, NULL, { .reboot_required = On, .allow_null = On } },
// { Setting_TimeZoneOffset, Group_General, "Timezone offset", NULL, Format_Decimal, "-#0.00", "0", "12", Setting_IsExtended, &settings.timezone, NULL, NULL },
@@ -2841,9 +2840,9 @@ bool settings_iterator (const setting_detail_t *setting, setting_output_ptr call
return ok;
}
-const setting_detail_t *setting_get_details (setting_id_t id, setting_details_t **set)
+static inline const setting_detail_t *_setting_get_details (setting_id_t id, uint_fast16_t offset, setting_details_t **set)
{
- uint_fast16_t idx, offset = id - normalize_id(id);
+ uint_fast16_t idx;
setting_details_t *details = settings_get_details();
id -= offset;
@@ -2869,6 +2868,37 @@ const setting_detail_t *setting_get_details (setting_id_t id, setting_details_t
return NULL;
}
+const setting_detail_t *setting_get_details (setting_id_t id, setting_details_t **set)
+{
+ const setting_detail_t *detail;
+
+ if((detail = _setting_get_details(id, id - normalize_id(id), set)) == NULL) {
+
+ uint_fast16_t idx, offset;
+ setting_details_t *details = settings_get_details();
+
+ do {
+ if(details->normalize && (offset = id - details->normalize(id))) {
+
+ id -= offset;
+
+ for(idx = 0; idx < details->n_settings; idx++) {
+ if(details->settings[idx].id == id && is_available(&details->settings[idx], offset)) {
+
+ detail = &details->settings[idx];
+
+ if(set)
+ *set = details;
+ }
+ }
+ break;
+ }
+ } while((details = details->next));
+ }
+
+ return detail;
+}
+
const char *setting_get_description (setting_id_t id)
{
const char *description = NULL;
diff --git a/settings.h b/settings.h
index 32a1202..238cbc5 100644
--- a/settings.h
+++ b/settings.h
@@ -509,10 +509,11 @@ typedef enum {
Setting_ActionPort8 = 768,
Setting_ActionPort9 = 769,
- Setting_SpindleOffsetX = 770,
- Setting_SpindleOffsetY = 771,
+ Setting_SpindleOffsetX = 770,
+ Setting_SpindleOffsetY = 771,
+ Setting_SpindleOffsetOptions = 772,
//
-// 772-779 - reserved for spindle offset settings
+// 773-779 - reserved for spindle offset settings
//
// Reserving settings in the range 800 - 899 for axis settings.
@@ -1074,6 +1075,7 @@ typedef void (*driver_settings_load_ptr)(void);
typedef void (*driver_settings_save_ptr)(void);
typedef void (*driver_settings_restore_ptr)(void);
typedef bool (*driver_settings_iterator_ptr)(const setting_detail_t *setting, setting_output_ptr callback, void *data);
+typedef setting_id_t (*driver_settings_normalize_ptr)(setting_id_t id);
typedef struct setting_details {
const bool is_core;
@@ -1092,6 +1094,7 @@ typedef struct setting_details {
driver_settings_load_ptr load;
driver_settings_restore_ptr restore;
driver_settings_iterator_ptr iterator;
+ driver_settings_normalize_ptr normalize;
} setting_details_t;
// NOTE: this must match the signature of on_get_settings in the setting_details_t structure above!
@@ -1105,7 +1108,7 @@ void settings_clear (void);
// Initialize the configuration subsystem (load settings from persistent storage)
void settings_init();
-// Write Grbl global settings and version number to persistent storage
+// Write grblHAL global settings and version number to persistent storage
void settings_write_global(void);
// Helper function to clear and restore persistent storage defaults
diff --git a/spindle_control.c b/spindle_control.c
index 8edbccf..099fdea 100644
--- a/spindle_control.c
+++ b/spindle_control.c
@@ -332,7 +332,7 @@ static spindle_num_t spindle_get_num (spindle_id_t spindle_id)
do {
idx--;
if((setting = setting_get_details(idx == 0 ? Setting_SpindleType : (setting_id_t)(Setting_SpindleEnable0 + idx), NULL))) {
- if(setting_get_int_value(setting, 0) - (idx == 0 ? 0 : 1) == spindle_id)
+ if(setting_get_int_value(setting, setting->flags.increment ? idx : 0) - (idx == 0 ? 0 : 1) == spindle_id)
spindle_num = idx;
}
} while(idx && spindle_num == -1);
@@ -652,6 +652,9 @@ static bool spindle_set_state_wait (spindle_ptrs_t *spindle, spindle_state_t sta
ok &= at_speed;
}
}
+
+ if(ok && grbl.on_spindle_at_speed)
+ grbl.on_spindle_at_speed(spindle, state);
}
return ok;
diff --git a/spindle_control.h b/spindle_control.h
index 9e2bcfd..6c19fe3 100644
--- a/spindle_control.h
+++ b/spindle_control.h
@@ -209,7 +209,8 @@ typedef union {
uint8_t enable_rpm_controlled :1, // PWM spindle only
laser_mode_disable :1, // PWM spindle only
pwm_disable :1, // PWM spindle only
- unassigned :5;
+ g92offset :1,
+ unassigned :4;
};
} spindle_settings_flags_t;