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!