diff --git a/CMakeLists.txt b/CMakeLists.txt
index e959b07..6bd620d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,6 +7,7 @@ target_sources(grbl INTERFACE
${CMAKE_CURRENT_LIST_DIR}/gcode.c
${CMAKE_CURRENT_LIST_DIR}/machine_limits.c
${CMAKE_CURRENT_LIST_DIR}/messages.c
+ ${CMAKE_CURRENT_LIST_DIR}/modbus.c
${CMAKE_CURRENT_LIST_DIR}/motion_control.c
${CMAKE_CURRENT_LIST_DIR}/my_plugin.c
${CMAKE_CURRENT_LIST_DIR}/nuts_bolts.c
diff --git a/changelog.md b/changelog.md
index 64ded26..84fbead 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,18 +1,46 @@
## grblHAL changelog
+Build 20230610
+
+Core:
+
+Added virtual Modbus API. Some internal settings handling improvements.
+
+Drivers:
+
+* iMXRT1062, STM32F7xx and RP2040: added Modbus TCP network support.
+
+* STM32F1xx: rerouted _Reset_ signal as _Emergency stop_ per default. Can be overriden in _my_machine.h_ or by setting `COMPATIBILITY_LEVEL` > 1.
+
+* STM32F4xx, ESP32, SAM3X8E and MSP432P401R: minor changes to handle new location of Modbus API.
+
+Plugins:
+
+* Spindle: refactored and renamed Modbus RTU code as a driver implementation for the core Modbus API.
+
+* Networking: added Modbus TCP driver for core Modbus API with support for up to 8 devices. Default is four.
+Added WIZNet support for Modbus TCP.
+Modbus TCP is enabled by bit 2 in the `MODBUS_ENABLE` symbol in _my_machine.h_: `#define MOBUS_ENABLE 4`. This can be added to the previous define values for enabling Modbus RTU with or without RS 485 direction signal support.
+__NOTE:__ The new core API only supports the Modbus RTU protocol, this will be translated to/from Modbus TCP by the driver implementation.
+User code _can_ bypass the core API and transmit Modbus TCP messages directly if it wants/needs to.
+__NOTE:__ VFD spindle Modbus communication will be routed to Modbus TCP if the VFD device id \(unit id\) matches the Modbus TCP device id.
+For now this is untested and may lock up the controller since the networking stack comes up too late to avoid power up selftest \(POS\) failure.
+To be addressed in a later revision if someone with a Modbus TCP capable spindle is willing to test.
+
+* Motors and encoder: updated for core setting handling improvements.
+
Build 20230607
Core:
* Added initial support for macro based automatic tool changes (ATC).
Currently macros has to be stored on a SD card or in littlefs and [expression support](https://github.com/grblHAL/core/wiki/Expressions-and-flow-control) has to be enabled.
-
* Added core events for file system mount/unmount.
Plugins:
-* SD Card, macro plugin: Implemented automatic hook to tool change functions when tool change macros are found in the root mount directory.
-Tool change macro: _tc.macro_, called on `M6`. \(required\)
+* SD Card, macro plugin: implemented automatic hook to tool change functions when tool change macros are found in the root mount directory.
+Tool change macro: _tc.macro_, called on `M6`. \(required\).
Tool select macro: _ts.macro_, called on `T`. \(optional\).
__NOTE:__ This functionality needs to be extensively tested by users having access to ATC hardware! [Discuss here](https://github.com/grblHAL/core/discussions/309).
diff --git a/driver_opts.h b/driver_opts.h
index 5a1530b..0dca407 100644
--- a/driver_opts.h
+++ b/driver_opts.h
@@ -218,6 +218,15 @@
#endif
#endif
+#define MODBUS_RTU_ENABLED 0b001
+#define MODBUS_RTU_DIR_ENABLED 0b010
+#define MODBUS_TCP_ENABLED 0b100
+
+#if MODBUS_ENABLE == 2
+#undef MOBUS_ENABLE
+#define MOBUS_ENABLE 0b011
+#endif
+
#ifndef MODBUS_ENABLE
#if VFD_ENABLE
#define MODBUS_ENABLE 1
@@ -371,6 +380,9 @@
#ifndef NETWORK_HTTP_PORT
#define NETWORK_HTTP_PORT 80
#endif
+#ifndef NETWORK_MODBUS_PORT
+#define NETWORK_MODBUS_PORT 502
+#endif
#ifndef NETWORK_MQTT_PORT
#define NETWORK_MQTT_PORT 1883
#endif
diff --git a/grbl.h b/grbl.h
index f30773d..fcca688 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20230607
+#define GRBL_BUILD 20230610
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/maslow.c b/maslow.c
index 70ea8ea..414e396 100644
--- a/maslow.c
+++ b/maslow.c
@@ -101,6 +101,8 @@ static float get_axis_setting (setting_id_t setting);
static void maslow_settings_load (void);
static void maslow_settings_restore (void);
+#define AXIS_OPTS { .subgroups = On, .iterations = 1 }
+
static const setting_detail_t maslow_settings[] = {
#if maslow_MIXED_DRIVERS
{ Setting_maslowDriver, Group_MotorDriver, "maslow driver", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_NonCore, &maslow.driver_enable.mask },
@@ -112,10 +114,10 @@ static const setting_detail_t maslow_settings[] = {
{ (setting_id_t)Maslow_MotorOffsetY, Group_MotorDriver, "Motor offset Y", "mm", Format_Decimal, "###0.0", NULL, NULL, Setting_NonCore, &maslow.motorOffsetY, NULL },
{ (setting_id_t)Maslow_AcorrScaling, Group_MotorDriver, "Acorr Scaling", NULL, Format_Decimal, "###0.0", NULL, NULL, Setting_NonCore, &maslow.XcorrScaling, NULL },
{ (setting_id_t)Maslow_BcorrScaling, Group_MotorDriver, "BcorrScaling", NULL, Format_Decimal, "###0.0", NULL, NULL, Setting_NonCore, &maslow.XcorrScaling, NULL },
- { (setting_id_t)AxisSetting_MaslowKP, Group_Axis0, "?-axis KP", NULL, Format_Decimal, "###0.0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_axis_setting },
- { (setting_id_t)AxisSetting_MaslowKI, Group_Axis0, "?-axis KI", NULL, Format_Decimal, "###0.0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_axis_setting },
- { (setting_id_t)AxisSetting_MaslowKD, Group_Axis0, "?-axis KIt", NULL, Format_Decimal, "###0.0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_axis_setting },
- { (setting_id_t)AxisSetting_MaslowIMax, Group_Axis0, "?-axis I Max", "ma", Format_Decimal, "###0.0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_axis_setting }
+ { (setting_id_t)AxisSetting_MaslowKP, Group_Axis0, "-axis KP", NULL, Format_Decimal, "###0.0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_axis_setting, AXIS_OPTS },
+ { (setting_id_t)AxisSetting_MaslowKI, Group_Axis0, "-axis KI", NULL, Format_Decimal, "###0.0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_axis_setting, AXIS_OPTS },
+ { (setting_id_t)AxisSetting_MaslowKD, Group_Axis0, "-axis KIt", NULL, Format_Decimal, "###0.0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_axis_setting, AXIS_OPTS },
+ { (setting_id_t)AxisSetting_MaslowIMax, Group_Axis0, "-axis I Max", "ma", Format_Decimal, "###0.0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_axis_setting, AXIS_OPTS }
};
static void maslow_settings_save (void)
diff --git a/modbus.c b/modbus.c
new file mode 100644
index 0000000..186ccfd
--- /dev/null
+++ b/modbus.c
@@ -0,0 +1,89 @@
+/*
+
+ modbus.h - a lightweight ModBus implementation, interface wrapper
+
+ Part of grblHAL
+
+ Copyright (c) 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Grbl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Grbl. If not, see .
+
+*/
+
+#include "modbus.h"
+
+#include
+
+#define N_MODBUS_API 2
+
+static uint_fast16_t n_api = 0, tcp_api = N_MODBUS_API, rtu_api = N_MODBUS_API;
+static modbus_api_t modbus[N_MODBUS_API] = {0};
+
+bool modbus_isup (void)
+{
+ bool ok = n_api > 0;
+ uint_fast16_t idx = n_api;
+
+ if(idx) do {
+ ok &= modbus[--idx].is_up();
+ } while(idx);
+
+ return ok;
+}
+
+bool modbus_enabled (void)
+{
+ return n_api > 0;
+}
+
+void modbus_flush_queue (void)
+{
+ uint_fast16_t idx = n_api;
+
+ if(idx) do {
+ modbus[--idx].flush_queue();
+ } while(idx);
+}
+
+void modbus_set_silence (const modbus_silence_timeout_t *timeout)
+{
+ if(rtu_api != N_MODBUS_API)
+ modbus[rtu_api].set_silence(timeout);
+}
+
+bool modbus_send (modbus_message_t *msg, const modbus_callbacks_t *callbacks, bool block)
+{
+ bool ok = false;
+
+ if(tcp_api != N_MODBUS_API)
+ ok = modbus[tcp_api].send(msg, callbacks, block);
+
+ return ok || (rtu_api != N_MODBUS_API && modbus[rtu_api].send(msg, callbacks, block));
+}
+
+bool modbus_register_api (const modbus_api_t *api)
+{
+ bool ok;
+
+ if((ok = n_api < N_MODBUS_API)) {
+ memcpy(&modbus[n_api], api, sizeof(modbus_api_t));
+ if(api->interface == Modbus_InterfaceTCP)
+ tcp_api = n_api;
+ else if(api->interface == Modbus_InterfaceRTU)
+ rtu_api = n_api;
+ n_api++;
+ }
+
+ return ok;
+}
diff --git a/modbus.h b/modbus.h
new file mode 100644
index 0000000..d51880b
--- /dev/null
+++ b/modbus.h
@@ -0,0 +1,101 @@
+/*
+
+ modbus.h - a lightweight ModBus implementation
+
+ Part of grblHAL
+
+ Copyright (c) 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Grbl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Grbl. If not, see .
+
+*/
+
+#ifndef _MODBUS_H_
+#define _MODBUS_H_
+
+#ifndef MODBUS_MAX_ADU_SIZE
+#define MODBUS_MAX_ADU_SIZE 10
+#endif
+#ifndef MODBUS_QUEUE_LENGTH
+#define MODBUS_QUEUE_LENGTH 8
+#endif
+
+#include
+#include
+
+typedef enum {
+ Modbus_InterfaceRTU = 0,
+ Modbus_InterfaceASCII,
+ Modbus_InterfaceTCP
+} modbus_if_t;
+
+typedef enum {
+ ModBus_ReadCoils = 1,
+ ModBus_ReadDiscreteInputs = 2,
+ ModBus_ReadHoldingRegisters = 3,
+ ModBus_ReadInputRegisters = 4,
+ ModBus_WriteCoil = 5,
+ ModBus_WriteRegister = 6,
+ ModBus_ReadExceptionStatus = 7,
+ ModBus_Diagnostics = 8,
+ ModBus_WriteCoils = 15,
+ ModBus_WriteRegisters = 16
+} modbus_function_t;
+
+typedef struct {
+ void *context;
+ bool crc_check;
+ uint8_t tx_length;
+ uint8_t rx_length;
+ char adu[MODBUS_MAX_ADU_SIZE];
+} modbus_message_t;
+
+typedef struct {
+ void (*on_rx_packet)(modbus_message_t *msg);
+ void (*on_rx_exception)(uint8_t code, void *context);
+} modbus_callbacks_t;
+
+typedef union {
+ uint16_t timeout[6];
+ struct {
+ uint16_t b2400;
+ uint16_t b4800;
+ uint16_t b9600;
+ uint16_t b19200;
+ uint16_t b38400;
+ uint16_t b115200;
+ };
+} modbus_silence_timeout_t;
+
+typedef bool (*modbus_is_up_ptr)(void);
+typedef void (*modbus_flush_queue_ptr)(void);
+typedef void (*modbus_set_silence_ptr)(const modbus_silence_timeout_t *timeout);
+typedef bool (*modbus_send_ptr)(modbus_message_t *msg, const modbus_callbacks_t *callbacks, bool block);
+
+typedef struct {
+ modbus_if_t interface;
+ modbus_is_up_ptr is_up;
+ modbus_flush_queue_ptr flush_queue;
+ modbus_set_silence_ptr set_silence;
+ modbus_send_ptr send;
+} modbus_api_t;
+
+bool modbus_isup (void);
+bool modbus_enabled (void);
+void modbus_flush_queue (void);
+void modbus_set_silence (const modbus_silence_timeout_t *timeout);
+bool modbus_send (modbus_message_t *msg, const modbus_callbacks_t *callbacks, bool block);
+bool modbus_register_api (const modbus_api_t *api);
+
+#endif
diff --git a/plugins.h b/plugins.h
index de6bfa3..9e2a9e0 100644
--- a/plugins.h
+++ b/plugins.h
@@ -136,6 +136,20 @@ typedef struct {
uint32_t rx_timeout;
} modbus_settings_t;
+#define MODBUS_TCP_SETTINGS_INCREMENT 5
+
+typedef enum {
+ Setting_ModbusIpAddress = 0,
+ Setting_ModbusPort = 1,
+ Setting_ModbusId = 2
+} modbus_tcp_setting_id_t;
+
+typedef struct {
+ char ip[16];
+ uint16_t port;
+ uint8_t id;
+} modbus_tcp_settings_t;
+
// Quadrature encoder interface
typedef enum {
diff --git a/plugins_init.h b/plugins_init.h
index 12a2a52..6a94f48 100644
--- a/plugins_init.h
+++ b/plugins_init.h
@@ -28,9 +28,9 @@
trinamic_init();
#endif
-#if MODBUS_ENABLE
- extern void modbus_init (void);
- modbus_init();
+#if MODBUS_ENABLE && MODBUS_ENABLE & 0x01
+ extern void modbus_rtu_init (void);
+ modbus_rtu_init();
#endif
#if CANBUS_ENABLE
diff --git a/report.c b/report.c
index 0923463..05edef9 100644
--- a/report.c
+++ b/report.c
@@ -1427,8 +1427,23 @@ static void write_quoted (const char *s, const char *sep)
hal.stream.write(sep);
}
+static void write_name (const char *s, uint_fast8_t offset)
+{
+ char *q = hal.stream.write_n ? strchr(s, '?') : NULL;
+
+ if(q) {
+ if(q != s)
+ hal.stream.write_n(s, q - s);
+ hal.stream.write(uitoa(offset + 1));
+ hal.stream.write(q + 1);
+ } else
+ hal.stream.write(s);
+}
+
static void report_settings_detail (settings_format_t format, const setting_detail_t *setting, uint_fast8_t offset)
{
+ uint_fast8_t suboffset = setting->flags.subgroups ? offset / setting->flags.increment : offset;
+
switch(format)
{
case SettingsFormat_HumanReadable:
@@ -1437,7 +1452,7 @@ static void report_settings_detail (settings_format_t format, const setting_deta
hal.stream.write(": ");
if(setting->group == Group_Axis0)
hal.stream.write(axis_letter[offset]);
- hal.stream.write(setting->name[0] == '?' ? &setting->name[1] : setting->name); // temporary hack for ? prefix...
+ write_name(setting->name, suboffset);
switch(setting_datatype_to_external(setting->datatype)) {
@@ -1526,11 +1541,11 @@ static void report_settings_detail (settings_format_t format, const setting_deta
hal.stream.write("[SETTING:");
hal.stream.write(uitoa(setting->id + offset));
hal.stream.write(vbar);
- hal.stream.write(uitoa(setting->group + (setting->group == Group_Axis0 ? offset : 0)));
+ hal.stream.write(uitoa(setting->group + (setting->flags.subgroups ? suboffset : 0)));
hal.stream.write(vbar);
if(setting->group == Group_Axis0)
hal.stream.write(axis_letter[offset]);
- hal.stream.write(setting->name[0] == '?' ? &setting->name[1] : setting->name); // temporary hack for ? prefix...
+ write_name(setting->name, suboffset);
hal.stream.write(vbar);
if(setting->unit)
hal.stream.write(setting->unit);
@@ -1558,7 +1573,7 @@ static void report_settings_detail (settings_format_t format, const setting_deta
hal.stream.write("\"");
if(setting->group == Group_Axis0)
hal.stream.write(axis_letter[offset]);
- hal.stream.write(setting->name[0] == '?' ? &setting->name[1] : setting->name); // temporary hack for ? prefix...
+ write_name(setting->name, suboffset);
hal.stream.write("\",");
if(setting->unit) {
write_quoted(setting->unit, ",");
@@ -1582,7 +1597,7 @@ static void report_settings_detail (settings_format_t format, const setting_deta
if(setting->group == Group_Axis0)
hal.stream.write(axis_letter[offset]);
- hal.stream.write(setting->name[0] == '?' ? &setting->name[1] : setting->name); // temporary hack for ? prefix...
+ write_name(setting->name, suboffset);
hal.stream.write("\t");
@@ -1949,7 +1964,7 @@ static void print_setting_group (const setting_group_detail_t *group, char *pref
hal.stream.write(vbar);
hal.stream.write(group->name);
hal.stream.write("]" ASCII_EOL);
- } else if(group->id != Group_Root && settings_is_group_available(group->id)) {
+ } else if(group->id != Group_Root) {
hal.stream.write(prefix);
hal.stream.write(group->name);
hal.stream.write(ASCII_EOL);
@@ -1959,12 +1974,23 @@ static void print_setting_group (const setting_group_detail_t *group, char *pref
static int cmp_setting_group_id (const void *a, const void *b)
{
- return (*(setting_detail_t **)(a))->id - (*(setting_detail_t **)(b))->id;
+ return (*(setting_group_detail_t **)(a))->id - (*(setting_group_detail_t **)(b))->id;
}
static int cmp_setting_group_name (const void *a, const void *b)
{
- return strcmp((*(setting_detail_t **)(a))->name, (*(setting_detail_t **)(b))->name);
+ return strcmp((*(setting_group_detail_t **)(a))->name, (*(setting_group_detail_t **)(b))->name);
+}
+
+static bool group_is_dup (setting_group_detail_t **groups, setting_group_t group)
+{
+ while(*groups) {
+ if((*groups)->id == group)
+ return true;
+ groups++;
+ }
+
+ return false;
}
status_code_t report_setting_group_details (bool by_id, char *prefix)
@@ -1984,8 +2010,10 @@ status_code_t report_setting_group_details (bool by_id, char *prefix)
uint_fast16_t idx;
do {
- for(idx = 0; idx < details->n_groups; idx++)
- *group++ = (setting_group_detail_t *)&details->groups[idx];
+ for(idx = 0; idx < details->n_groups; idx++) {
+ if(!group_is_dup(all_groups, details->groups[idx].id))
+ *group++ = (setting_group_detail_t *)&details->groups[idx];
+ }
} while((details = details->next));
qsort(all_groups, n_groups, sizeof(setting_group_detail_t *), by_id ? cmp_setting_group_id : cmp_setting_group_name);
diff --git a/settings.c b/settings.c
index fea71e7..3b3b2cb 100644
--- a/settings.c
+++ b/settings.c
@@ -304,51 +304,56 @@ PROGMEM const settings_t defaults = {
.safety_door.coolant_on_delay = DEFAULT_SAFETY_DOOR_COOLANT_DELAY
};
+static bool group_is_available (const setting_group_detail_t *group)
+{
+ return true;
+}
+
PROGMEM static const setting_group_detail_t setting_group_detail [] = {
- { Group_Root, Group_Root, "Root"},
- { Group_Root, Group_General, "General"},
- { Group_Root, Group_ControlSignals, "Control signals"},
- { Group_Root, Group_Limits, "Limits"},
- { Group_Limits, Group_Limits_DualAxis, "Dual axis"},
- { Group_Root, Group_Coolant, "Coolant"},
- { Group_Root, Group_Spindle, "Spindle"},
- { Group_Spindle, Group_Spindle_Sync, "Spindle sync"},
- { Group_Root, Group_Toolchange, "Tool change"},
- { Group_Root, Group_Homing, "Homing"},
- { Group_Root, Group_Probing, "Probing"},
- { Group_Root, Group_SafetyDoor, "Safety door"},
+ { Group_Root, Group_Root, "Root", group_is_available },
+ { Group_Root, Group_General, "General", group_is_available },
+ { Group_Root, Group_ControlSignals, "Control signals" },
+ { Group_Root, Group_Limits, "Limits" },
+ { Group_Limits, Group_Limits_DualAxis, "Dual axis" },
+ { Group_Root, Group_Coolant, "Coolant" },
+ { Group_Root, Group_Spindle, "Spindle" },
+ { Group_Spindle, Group_Spindle_Sync, "Spindle sync" },
+ { Group_Root, Group_Toolchange, "Tool change" },
+ { Group_Root, Group_Homing, "Homing" },
+ { Group_Root, Group_Probing, "Probing" },
+ { Group_Root, Group_SafetyDoor, "Safety door" },
{ Group_Root, Group_Jogging, "Jogging"},
- { Group_Root, Group_Stepper, "Stepper"},
- { Group_Root, Group_MotorDriver, "Stepper driver"},
- { Group_Root, Group_Axis, "Axis"},
- { Group_Axis, Group_XAxis, "X-axis"},
- { Group_Axis, Group_YAxis, "Y-axis"},
- { Group_Axis, Group_ZAxis, "Z-axis"},
+ { Group_Root, Group_Stepper, "Stepper" },
+ { Group_Root, Group_MotorDriver, "Stepper driver" },
+ { Group_Root, Group_Axis, "Axis", group_is_available },
+ { Group_Axis, Group_XAxis, "X-axis", group_is_available },
+ { Group_Axis, Group_YAxis, "Y-axis", group_is_available },
+ { Group_Axis, Group_ZAxis, "Z-axis", group_is_available },
#if !AXIS_REMAP_ABC2UVW
#ifdef A_AXIS
- { Group_Axis, Group_AAxis, "A-axis"},
+ { Group_Axis, Group_AAxis, "A-axis", group_is_available },
#endif
#ifdef B_AXIS
- { Group_Axis, Group_BAxis, "B-axis"},
+ { Group_Axis, Group_BAxis, "B-axis", group_is_available },
#endif
#ifdef C_AXIS
- { Group_Axis, Group_CAxis, "C-axis"},
+ { Group_Axis, Group_CAxis, "C-axis", group_is_available },
#endif
#ifdef U_AXIS
- { Group_Axis, Group_UAxis, "U-axis"},
+ { Group_Axis, Group_UAxis, "U-axis", group_is_available },
#endif
#ifdef V_AXIS
- { Group_Axis, Group_VAxis, "V-axis"}
+ { Group_Axis, Group_VAxis, "V-axis", group_is_available }
#endif
#else
#ifdef A_AXIS
- { Group_Axis, Group_AAxis, "U-axis"},
+ { Group_Axis, Group_AAxis, "U-axis", group_is_available },
#endif
#ifdef B_AXIS
- { Group_Axis, Group_BAxis, "V-axis"},
+ { Group_Axis, Group_BAxis, "V-axis", group_is_available },
#endif
#ifdef C_AXIS
- { Group_Axis, Group_CAxis, "W-axis"},
+ { Group_Axis, Group_CAxis, "W-axis", group_is_available },
#endif
#endif
};
@@ -420,6 +425,8 @@ static char axis_rate[8] = "mm/min";
static char axis_accel[10] = "mm/sec^2";
static char axis_steps[9] = "step/mm";
+#define AXIS_OPTS { .subgroups = On, .increment = 1 }
+
PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_PulseMicroseconds, Group_Stepper, "Step pulse time", "microseconds", Format_Decimal, "#0.0", "2.0", NULL, Setting_IsLegacy, &settings.steppers.pulse_microseconds, NULL, NULL },
{ Setting_StepperIdleLockTime, Group_Stepper, "Step idle delay", "milliseconds", Format_Int16, "####0", NULL, "65535", Setting_IsLegacy, &settings.steppers.idle_lock_time, NULL, NULL },
@@ -532,14 +539,14 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_PositionIGain, Group_Spindle_Sync, "Spindle sync I-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.position.pid.i_gain, NULL, is_group_available },
{ Setting_PositionDGain, Group_Spindle_Sync, "Spindle sync D-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.position.pid.d_gain, NULL, is_group_available },
{ Setting_PositionIMaxError, Group_Spindle_Sync, "Spindle sync PID max I error", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.position.pid.i_max_error, NULL, is_group_available },
- { Setting_AxisStepsPerMM, Group_Axis0, "?-axis travel resolution", axis_steps, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL },
- { Setting_AxisMaxRate, Group_Axis0, "?-axis maximum rate", axis_rate, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL },
- { Setting_AxisAcceleration, Group_Axis0, "?-axis acceleration", axis_accel, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL },
- { Setting_AxisMaxTravel, Group_Axis0, "?-axis maximum travel", axis_dist, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL },
+ { Setting_AxisStepsPerMM, Group_Axis0, "-axis travel resolution", axis_steps, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
+ { Setting_AxisMaxRate, Group_Axis0, "-axis maximum rate", axis_rate, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
+ { Setting_AxisAcceleration, Group_Axis0, "-axis acceleration", axis_accel, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
+ { Setting_AxisMaxTravel, Group_Axis0, "-axis maximum travel", axis_dist, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
#if ENABLE_BACKLASH_COMPENSATION
- { Setting_AxisBacklash, Group_Axis0, "?-axis backlash compensation", axis_dist, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtendedFn, set_axis_setting, get_float, NULL },
+ { Setting_AxisBacklash, Group_Axis0, "-axis backlash compensation", axis_dist, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtendedFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
#endif
- { Setting_AxisAutoSquareOffset, Group_Axis0, "?-axis dual axis offset", "mm", Format_Decimal, "-0.000", "-10", "10", Setting_IsExtendedFn, set_axis_setting, get_float, is_setting_available },
+ { Setting_AxisAutoSquareOffset, Group_Axis0, "-axis dual axis offset", "mm", Format_Decimal, "-0.000", "-10", "10", Setting_IsExtendedFn, set_axis_setting, get_float, is_setting_available, AXIS_OPTS },
{ Setting_SpindleAtSpeedTolerance, Group_Spindle, "Spindle at speed tolerance", "percent", Format_Decimal, "##0.0", NULL, NULL, Setting_IsExtended, &settings.spindle.at_speed_tolerance, NULL, is_setting_available },
{ Setting_ToolChangeMode, Group_Toolchange, "Tool change mode", NULL, Format_RadioButtons, "Normal,Manual touch off,Manual touch off @ G59.3,Automatic touch off @ G59.3,Ignore M6", NULL, NULL, Setting_IsExtendedFn, set_tool_change_mode, get_int, NULL },
{ Setting_ToolChangeProbingDistance, Group_Toolchange, "Tool change probing distance", "mm", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtendedFn, set_tool_change_probing_distance, get_float, NULL },
@@ -1282,6 +1289,8 @@ inline static setting_id_t normalize_id (setting_id_t id)
id -= id % AXIS_SETTINGS_INCREMENT;
else if(id > Setting_EncoderSettingsBase && id <= Setting_EncoderSettingsMax)
id = (setting_id_t)(Setting_EncoderSettingsBase + (id % ENCODER_SETTINGS_INCREMENT));
+ else if(id > Setting_ModbusTCPBase && id <= Setting_ModbusTCPMax)
+ id = (setting_id_t)(Setting_ModbusTCPBase + (id % MODBUS_TCP_SETTINGS_INCREMENT));
return id;
}
@@ -2071,21 +2080,21 @@ static bool is_group_available (const setting_detail_t *setting)
return settings_is_group_available(setting->group);
}
-bool settings_is_group_available (setting_group_t group)
+bool settings_is_group_available (setting_group_t id)
{
- bool available = false;
+ const setting_group_detail_t *group = setting_get_group_details(id);
- switch(group) {
+ if(!group)
+ return false;
+
+ bool available = group->is_available ? group->is_available(group) : false;
+
+ if(!available) switch(group->id) {
case Group_Probing:
available = hal.probe.get_state != NULL;
break;
- case Group_Encoders:
- case Group_Encoder0:
- available = hal.encoder.get_n_encoders && hal.encoder.get_n_encoders() > 0;
- break;
-
case Group_Spindle_Sync:
available = hal.driver_cap.spindle_sync;
break;
@@ -2098,42 +2107,23 @@ bool settings_is_group_available (setting_group_t group)
available = hal.stepper.get_ganged && hal.stepper.get_ganged(true).mask != 0;
break;
- case Group_General:
case Group_Homing:
case Group_Jogging:
case Group_Limits:
case Group_ControlSignals:
case Group_Spindle:
- case Group_Axis:
- case Group_XAxis:
- case Group_YAxis:
- case Group_ZAxis:
- #ifdef A_AXIS
- case Group_AAxis:
- #endif
- #ifdef B_AXIS
- case Group_BAxis:
- #endif
- #ifdef C_AXIS
- case Group_CAxis:
- #endif
- #ifdef U_AXIS
- case Group_UAxis:
- #endif
- #ifdef V_AXIS
- case Group_VAxis:
- #endif
available = true;
break;
default:
{
uint_fast16_t idx;
- setting_details_t *details = settings_get_details();
+ setting_details_t *details = &setting_details;
+
do {
if(details->settings) {
for(idx = 0; idx < details->n_settings; idx++) {
- if(details->settings[idx].group == group && (available = is_available(&details->settings[idx])))
+ if(details->settings[idx].group == id && (available = is_available(&details->settings[idx])))
break;
}
}
@@ -2150,78 +2140,28 @@ setting_group_t settings_normalize_group (setting_group_t group)
return (group > Group_Axis0 && group < Group_Axis0 + N_AXIS) ? Group_Axis0 : group;
}
-/*
-setting_group_t settings_get_parent_group (setting_group_t group)
-{
- uint_fast16_t idx;
- setting_details_t *settings = settings_get_details();
-
- for(idx = 0; idx < settings->n_groups; idx++) {
- if(settings->groups[idx].id == group) {
- group = settings->groups[idx].parent;
- break;
- }
- }
-
- return group;
-}
-*/
-
bool settings_iterator (const setting_detail_t *setting, setting_output_ptr callback, void *data)
{
bool ok = false;
- switch(setting->id) {
+ if(setting->group == Group_Axis0) {
- case Setting_AxisStepsPerMM:
- case Setting_AxisMaxRate:
- case Setting_AxisAcceleration:
- case Setting_AxisMaxTravel:
- case Setting_AxisStepperCurrent:
- case Setting_AxisMicroSteps:
- case Setting_AxisBacklash:
- case Setting_AxisAutoSquareOffset:
- case Setting_AxisHomingFeedRate:
- case Setting_AxisHomingSeekRate:
- case Setting_AxisExtended0:
- case Setting_AxisExtended1:
- case Setting_AxisExtended2:
- case Setting_AxisExtended3:
- case Setting_AxisExtended4:
- case Setting_AxisExtended5:
- case Setting_AxisExtended6:
- case Setting_AxisExtended7:
- case Setting_AxisExtended8:
- case Setting_AxisExtended9:
- {
- uint_fast8_t axis_idx = 0;
- for(axis_idx = 0; axis_idx < N_AXIS; axis_idx++) {
+ uint_fast8_t axis_idx = 0;
+
+ for(axis_idx = 0; axis_idx < N_AXIS; axis_idx++) {
#if N_AXIS > 3
- set_axis_setting_unit(setting, axis_idx);
+ set_axis_setting_unit(setting, axis_idx);
#endif
- if(callback(setting, axis_idx, data))
- ok = true;
- }
- }
- break;
-
- case Setting_EncoderModeBase:
- case Setting_EncoderCPRBase:
- case Setting_EncoderCPDBase:
- case Setting_EncoderDblClickWindowBase:
- {
- uint_fast8_t encoder_idx = 0, n_encoders = hal.encoder.get_n_encoders();
- for(encoder_idx = 0; encoder_idx < n_encoders; encoder_idx++) {
- if(callback(setting, encoder_idx * ENCODER_SETTINGS_INCREMENT, data))
- ok = true;
- }
- }
- break;
-
- default:
- ok = callback(setting, 0, data);
- break;
- }
+ if(callback(setting, axis_idx, data))
+ ok = true;
+ }
+ } else if(setting->flags.increment) {
+ setting_details_t *set;
+ setting = setting_get_details(setting->id, &set);
+ if(set->iterator)
+ ok = set->iterator(setting, callback, data);
+ } else
+ ok = callback(setting, 0, data);
return ok;
}
@@ -2240,7 +2180,7 @@ const setting_detail_t *setting_get_details (setting_id_t id, setting_details_t
if(details->settings[idx].group == Group_Axis0)
set_axis_setting_unit(&details->settings[idx], offset);
#endif
- if(offset && offset >= (details->settings[idx].group == Group_Encoder0 ? hal.encoder.get_n_encoders() : N_AXIS))
+ if(offset && details->iterator == NULL && offset >= (details->settings[idx].group == Group_Encoder0 ? hal.encoder.get_n_encoders() : N_AXIS))
return NULL;
if(set)
*set = details;
@@ -2298,6 +2238,15 @@ const setting_group_detail_t *setting_get_group_details (setting_group_t id)
return detail;
}
+/*
+setting_group_t setting_get_parent_group (setting_group_t id)
+{
+ const setting_group_detail_t *group = setting_get_group_details(id);
+
+ return group ? group->parent : Group_Unknown;
+}
+*/
+
static status_code_t validate_value (const setting_detail_t *setting, float value)
{
float val;
diff --git a/settings.h b/settings.h
index 7ecdce6..eadad60 100644
--- a/settings.h
+++ b/settings.h
@@ -381,6 +381,12 @@ typedef enum {
Setting_Panel_Encoder3_Cpd = 559,
Setting_Panel_SettingsMax = 579,
+ Setting_ModbusTCPBase = 600, // Reserving settings values 600 to 639 for ModBus TCP (8 sets)
+ Setting_ModbusIpAddressBase = Setting_ModbusTCPBase + Setting_ModbusIpAddress,
+ Setting_ModbusPortBase = Setting_ModbusTCPBase + Setting_ModbusPort,
+ Setting_ModbusIdBase = Setting_ModbusTCPBase + Setting_ModbusId,
+ Setting_ModbusTCPMax = 639,
+
Setting_SettingsMax,
Setting_SettingsAll = Setting_SettingsMax,
@@ -722,40 +728,49 @@ typedef enum {
Group_Bluetooth, //!< 17
Group_AuxPorts, //!< 18
Group_ModBus, //!< 19
- Group_Encoders, //!< 20
- Group_Encoder0, //!< 21
- Group_Encoder1, //!< 22
- Group_Encoder2, //!< 23
- Group_Encoder3, //!< 24
- Group_Encoder4, //!< 25
- Group_UserSettings, //!< 26
- Group_Stepper, //!< 27
- Group_MotorDriver, //!< 28
- Group_VFD, //!< 29
- Group_CANbus, //!< 30
- Group_Embroidery, //!< 31
- Group_Panel, //!< 32
- Group_Axis, //!< 33
+ Group_ModBusUnit0, //!< 20
+ Group_ModBusUnit1, //!< 21
+ Group_ModBusUnit2, //!< 22
+ Group_ModBusUnit3, //!< 23
+ Group_ModBusUnit4, //!< 24
+ Group_ModBusUnit5, //!< 25
+ Group_ModBusUnit6, //!< 26
+ Group_ModBusUnit7, //!< 27
+ Group_Encoders, //!< 28
+ Group_Encoder0, //!< 29
+ Group_Encoder1, //!< 30
+ Group_Encoder2, //!< 31
+ Group_Encoder3, //!< 32
+ Group_Encoder4, //!< 33
+ Group_UserSettings, //!< 34
+ Group_Stepper, //!< 35
+ Group_MotorDriver, //!< 36
+ Group_VFD, //!< 37
+ Group_CANbus, //!< 38
+ Group_Embroidery, //!< 39
+ Group_Panel, //!< 40
+ Group_Axis, //!< 41
// NOTE: axis groups MUST be sequential AND last
- Group_Axis0, //!< 34
- Group_XAxis = Group_Axis0, //!< 35
- Group_YAxis, //!< 36
- Group_ZAxis, //!< 37
+ Group_Axis0, //!< 42
+ Group_XAxis = Group_Axis0, //!< 43
+ Group_YAxis, //!< 44
+ Group_ZAxis, //!< 45
#ifdef A_AXIS
- Group_AAxis, //!< 38
+ Group_AAxis, //!< 46
#endif
#ifdef B_AXIS
- Group_BAxis, //!< 39
+ Group_BAxis, //!< 47
#endif
#ifdef C_AXIS
- Group_CAxis, //!< 40
+ Group_CAxis, //!< 48
#endif
#ifdef U_AXIS
- Group_UAxis, //!< 41
+ Group_UAxis, //!< 49
#endif
#ifdef V_AXIS
- Group_VAxis, //!< 42
+ Group_VAxis, //!< 50
#endif
+ Group_Unknown = 99, //!< 99
Group_All = Group_Root //!< 0
} setting_group_t;
@@ -775,10 +790,11 @@ typedef enum {
Format_Int16,
} setting_datatype_t;
-typedef struct {
+typedef struct setting_group_detail {
setting_group_t parent;
setting_group_t id;
const char *name;
+ bool (*is_available)(const struct setting_group_detail *group);
} setting_group_detail_t;
typedef enum {
@@ -801,8 +817,10 @@ typedef union {
uint8_t value;
struct {
uint8_t reboot_required :1,
- allow_null: 1,
- unused :6;
+ allow_null :1,
+ subgroups :1,
+ increment :4,
+ unused :1;
};
} setting_detail_flags_t;
@@ -854,6 +872,7 @@ typedef void (*settings_changed_ptr)(settings_t *settings, settings_changed_flag
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 struct setting_details {
const uint8_t n_groups;
@@ -870,6 +889,7 @@ typedef struct setting_details {
driver_settings_save_ptr save;
driver_settings_load_ptr load;
driver_settings_restore_ptr restore;
+ driver_settings_iterator_ptr iterator;
} setting_details_t;
// NOTE: this must match the signature of on_get_settings in the setting_details_t structure above!