mirror of
https://github.com/grblHAL/core.git
synced 2026-03-23 20:54:26 +08:00
1890 lines
73 KiB
C
1890 lines
73 KiB
C
/*
|
|
settings.c - non-volatile storage configuration handling
|
|
|
|
Part of grblHAL
|
|
|
|
Copyright (c) 2017-2021 Terje Io
|
|
Copyright (c) 2011-2015 Sungeun K. Jeon
|
|
Copyright (c) 2009-2011 Simen Svale Skogsrud
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
|
|
#include "hal.h"
|
|
#include "defaults.h"
|
|
#include "limits.h"
|
|
#include "nvs_buffer.h"
|
|
#include "tool_change.h"
|
|
#include "state_machine.h"
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
#include "motion_control.h"
|
|
#endif
|
|
#ifdef ENABLE_SPINDLE_LINEARIZATION
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#ifndef SETTINGS_RESTORE_DEFAULTS
|
|
#define SETTINGS_RESTORE_DEFAULTS 1
|
|
#endif
|
|
#ifndef SETTINGS_RESTORE_PARAMETERS
|
|
#define SETTINGS_RESTORE_PARAMETERS 1
|
|
#endif
|
|
#ifndef SETTINGS_RESTORE_STARTUP_LINES
|
|
#define SETTINGS_RESTORE_STARTUP_LINES 1
|
|
#endif
|
|
#ifndef SETTINGS_RESTORE_BUILD_INFO
|
|
#define SETTINGS_RESTORE_BUILD_INFO 1
|
|
#endif
|
|
#ifndef SETTINGS_RESTORE_DRIVER_PARAMETERS
|
|
#define SETTINGS_RESTORE_DRIVER_PARAMETERS 1
|
|
#endif
|
|
|
|
settings_t settings;
|
|
|
|
const settings_restore_t settings_all = {
|
|
.defaults = SETTINGS_RESTORE_DEFAULTS,
|
|
.parameters = SETTINGS_RESTORE_PARAMETERS,
|
|
.startup_lines = SETTINGS_RESTORE_STARTUP_LINES,
|
|
.build_info = SETTINGS_RESTORE_BUILD_INFO,
|
|
.driver_parameters = SETTINGS_RESTORE_DRIVER_PARAMETERS
|
|
};
|
|
|
|
PROGMEM const settings_t defaults = {
|
|
|
|
.version = SETTINGS_VERSION,
|
|
|
|
.junction_deviation = DEFAULT_JUNCTION_DEVIATION,
|
|
.arc_tolerance = DEFAULT_ARC_TOLERANCE,
|
|
.g73_retract = DEFAULT_G73_RETRACT,
|
|
|
|
.flags.legacy_rt_commands = DEFAULT_LEGACY_RTCOMMANDS,
|
|
.flags.report_inches = DEFAULT_REPORT_INCHES,
|
|
.flags.sleep_enable = DEFAULT_SLEEP_ENABLE,
|
|
#if DEFAULT_LASER_MODE
|
|
.mode = Mode_Laser,
|
|
.flags.disable_laser_during_hold = DEFAULT_DISABLE_LASER_DURING_HOLD,
|
|
#else
|
|
.flags.disable_laser_during_hold = 0,
|
|
#if DEFAULT_LATHE_MODE
|
|
.mode = Mode_Lathe,
|
|
#endif
|
|
#endif
|
|
.flags.restore_after_feed_hold = DEFAULT_RESTORE_AFTER_FEED_HOLD,
|
|
.flags.force_initialization_alarm = DEFAULT_FORCE_INITIALIZATION_ALARM,
|
|
|
|
.probe.disable_probe_pullup = DISABLE_PROBE_PIN_PULL_UP,
|
|
.probe.allow_feed_override = ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES,
|
|
.probe.invert_probe_pin = DEFAULT_INVERT_PROBE_PIN,
|
|
|
|
.steppers.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS,
|
|
.steppers.pulse_delay_microseconds = DEFAULT_STEP_PULSE_DELAY,
|
|
.steppers.idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME,
|
|
.steppers.step_invert.mask = DEFAULT_STEPPING_INVERT_MASK,
|
|
.steppers.dir_invert.mask = DEFAULT_DIRECTION_INVERT_MASK,
|
|
.steppers.enable_invert.mask = INVERT_ST_ENABLE_MASK,
|
|
.steppers.deenergize.mask = ST_DEENERGIZE_MASK,
|
|
// .steppers.is_rotational.mask = 0,
|
|
#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.manual = DEFAULT_HOMING_ALLOW_MANUAL,
|
|
.homing.flags.override_locks = DEFAULT_HOMING_OVERRIDE_LOCKS,
|
|
#else
|
|
.homing.flags.value = 0,
|
|
#endif
|
|
.homing.dir_mask.value = DEFAULT_HOMING_DIR_MASK,
|
|
.homing.feed_rate = DEFAULT_HOMING_FEED_RATE,
|
|
.homing.seek_rate = DEFAULT_HOMING_SEEK_RATE,
|
|
.homing.debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY,
|
|
.homing.pulloff = DEFAULT_HOMING_PULLOFF,
|
|
.homing.locate_cycles = DEFAULT_N_HOMING_LOCATE_CYCLE,
|
|
.homing.cycle[0].mask = HOMING_CYCLE_0,
|
|
.homing.cycle[1].mask = HOMING_CYCLE_1,
|
|
.homing.cycle[2].mask = HOMING_CYCLE_2,
|
|
.homing.dual_axis.fail_length_percent = DUAL_AXIS_HOMING_FAIL_AXIS_LENGTH_PERCENT,
|
|
.homing.dual_axis.fail_distance_min = DUAL_AXIS_HOMING_FAIL_DISTANCE_MIN,
|
|
.homing.dual_axis.fail_distance_max = DUAL_AXIS_HOMING_FAIL_DISTANCE_MAX,
|
|
|
|
.status_report.machine_position = DEFAULT_REPORT_BUFFER_STATE,
|
|
.status_report.buffer_state = DEFAULT_REPORT_BUFFER_STATE,
|
|
.status_report.line_numbers = DEFAULT_REPORT_LINE_NUMBERS,
|
|
.status_report.feed_speed = DEFAULT_REPORT_CURRENT_FEED_SPEED,
|
|
.status_report.pin_state = DEFAULT_REPORT_PIN_STATE,
|
|
.status_report.work_coord_offset = DEFAULT_REPORT_WORK_COORD_OFFSET,
|
|
.status_report.overrides = DEFAULT_REPORT_OVERRIDES,
|
|
.status_report.probe_coordinates = DEFAULT_REPORT_PROBE_COORDINATES,
|
|
.status_report.sync_on_wco_change = DEFAULT_REPORT_SYNC_ON_WCO_CHANGE,
|
|
.status_report.parser_state = DEFAULT_REPORT_PARSER_STATE,
|
|
.status_report.alarm_substate = DEFAULT_REPORT_ALARM_SUBSTATE,
|
|
|
|
.limits.flags.hard_enabled = DEFAULT_HARD_LIMIT_ENABLE,
|
|
.limits.flags.soft_enabled = DEFAULT_SOFT_LIMIT_ENABLE,
|
|
.limits.flags.jog_soft_limited = DEFAULT_JOG_LIMIT_ENABLE,
|
|
.limits.flags.check_at_init = DEFAULT_CHECK_LIMITS_AT_INIT,
|
|
.limits.flags.two_switches = DEFAULT_LIMITS_TWO_SWITCHES_ON_AXES,
|
|
.limits.invert.mask = INVERT_LIMIT_PIN_MASK,
|
|
.limits.disable_pullup.mask = DISABLE_LIMIT_PINS_PULL_UP_MASK,
|
|
|
|
.control_invert.mask = INVERT_CONTROL_PIN_MASK,
|
|
.control_disable_pullup.mask = DISABLE_CONTROL_PINS_PULL_UP_MASK,
|
|
|
|
.spindle.rpm_max = DEFAULT_SPINDLE_RPM_MAX,
|
|
.spindle.rpm_min = DEFAULT_SPINDLE_RPM_MIN,
|
|
.spindle.flags.pwm_action = DEFAULT_SPINDLE_PWM_ACTION,
|
|
.spindle.invert.on = INVERT_SPINDLE_ENABLE_PIN,
|
|
.spindle.invert.ccw = INVERT_SPINDLE_CCW_PIN,
|
|
.spindle.invert.pwm = INVERT_SPINDLE_PWM_PIN,
|
|
.spindle.pwm_freq = DEFAULT_SPINDLE_PWM_FREQ,
|
|
.spindle.pwm_off_value = DEFAULT_SPINDLE_PWM_OFF_VALUE,
|
|
.spindle.pwm_min_value = DEFAULT_SPINDLE_PWM_MIN_VALUE,
|
|
.spindle.pwm_max_value = DEFAULT_SPINDLE_PWM_MAX_VALUE,
|
|
.spindle.at_speed_tolerance = DEFAULT_SPINDLE_AT_SPEED_TOLERANCE,
|
|
.spindle.ppr = DEFAULT_SPINDLE_PPR,
|
|
.spindle.pid.p_gain = DEFAULT_SPINDLE_P_GAIN,
|
|
.spindle.pid.i_gain = DEFAULT_SPINDLE_I_GAIN,
|
|
.spindle.pid.d_gain = DEFAULT_SPINDLE_D_GAIN,
|
|
.spindle.pid.i_max_error = DEFAULT_SPINDLE_I_MAX,
|
|
#if SPINDLE_NPWM_PIECES > 0
|
|
.spindle.pwm_piece[0] = { .rpm = NAN, .start = 0.0f, .end = 0.0f },
|
|
#endif
|
|
#if SPINDLE_NPWM_PIECES > 1
|
|
.spindle.pwm_piece[1] = { .rpm = NAN, .start = 0.0f, .end = 0.0f },
|
|
#endif
|
|
#if SPINDLE_NPWM_PIECES > 2
|
|
.spindle.pwm_piece[2] = { .rpm = NAN, .start = 0.0f, .end = 0.0f },
|
|
#endif
|
|
#if SPINDLE_NPWM_PIECES > 3
|
|
.spindle.pwm_piece[3] = { .rpm = NAN, .start = 0.0f, .end = 0.0f },
|
|
#endif
|
|
|
|
.coolant_invert.flood = INVERT_COOLANT_FLOOD_PIN,
|
|
.coolant_invert.mist = INVERT_COOLANT_MIST_PIN,
|
|
|
|
.axis[X_AXIS].steps_per_mm = DEFAULT_X_STEPS_PER_MM,
|
|
.axis[X_AXIS].max_rate = DEFAULT_X_MAX_RATE,
|
|
.axis[X_AXIS].acceleration = DEFAULT_X_ACCELERATION,
|
|
.axis[X_AXIS].max_travel = (-DEFAULT_X_MAX_TRAVEL),
|
|
.axis[X_AXIS].dual_axis_offset = 0.0f,
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
.axis[X_AXIS].backlash = 0.0f,
|
|
#endif
|
|
|
|
.axis[Y_AXIS].steps_per_mm = DEFAULT_Y_STEPS_PER_MM,
|
|
.axis[Y_AXIS].max_rate = DEFAULT_Y_MAX_RATE,
|
|
.axis[Y_AXIS].max_travel = (-DEFAULT_Y_MAX_TRAVEL),
|
|
.axis[Y_AXIS].acceleration = DEFAULT_Y_ACCELERATION,
|
|
.axis[Y_AXIS].dual_axis_offset = 0.0f,
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
.axis[Y_AXIS].backlash = 0.0f,
|
|
#endif
|
|
|
|
.axis[Z_AXIS].steps_per_mm = DEFAULT_Z_STEPS_PER_MM,
|
|
.axis[Z_AXIS].max_rate = DEFAULT_Z_MAX_RATE,
|
|
.axis[Z_AXIS].acceleration = DEFAULT_Z_ACCELERATION,
|
|
.axis[Z_AXIS].max_travel = (-DEFAULT_Z_MAX_TRAVEL),
|
|
.axis[Z_AXIS].dual_axis_offset = 0.0f,
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
.axis[Z_AXIS].backlash = 0.0f,
|
|
#endif
|
|
|
|
#ifdef A_AXIS
|
|
.axis[A_AXIS].steps_per_mm = DEFAULT_A_STEPS_PER_MM,
|
|
.axis[A_AXIS].max_rate = DEFAULT_A_MAX_RATE,
|
|
.axis[A_AXIS].acceleration = DEFAULT_A_ACCELERATION,
|
|
.axis[A_AXIS].max_travel = (-DEFAULT_A_MAX_TRAVEL),
|
|
.axis[A_AXIS].dual_axis_offset = 0.0f,
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
.axis[A_AXIS].backlash = 0.0f,
|
|
#endif
|
|
.homing.cycle[3].mask = HOMING_CYCLE_3,
|
|
#endif
|
|
|
|
#ifdef B_AXIS
|
|
.axis[B_AXIS].steps_per_mm = DEFAULT_B_STEPS_PER_MM,
|
|
.axis[B_AXIS].max_rate = DEFAULT_B_MAX_RATE,
|
|
.axis[B_AXIS].acceleration = DEFAULT_B_ACCELERATION,
|
|
.axis[B_AXIS].max_travel = (-DEFAULT_B_MAX_TRAVEL),
|
|
.axis[B_AXIS].dual_axis_offset = 0.0f,
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
.axis[B_AXIS].backlash = 0.0f,
|
|
#endif
|
|
.homing.cycle[4].mask = HOMING_CYCLE_4,
|
|
#endif
|
|
|
|
#ifdef C_AXIS
|
|
.axis[C_AXIS].steps_per_mm = DEFAULT_C_STEPS_PER_MM,
|
|
.axis[C_AXIS].acceleration = DEFAULT_C_ACCELERATION,
|
|
.axis[C_AXIS].max_rate = DEFAULT_C_MAX_RATE,
|
|
.axis[C_AXIS].max_travel = (-DEFAULT_C_MAX_TRAVEL),
|
|
.axis[C_AXIS].dual_axis_offset = 0.0f,
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
.axis[C_AXIS].backlash = 0.0f,
|
|
#endif
|
|
.homing.cycle[5].mask = HOMING_CYCLE_5,
|
|
#endif
|
|
|
|
.tool_change.mode = (toolchange_mode_t)DEFAULT_TOOLCHANGE_MODE,
|
|
.tool_change.probing_distance = DEFAULT_TOOLCHANGE_PROBING_DISTANCE,
|
|
.tool_change.feed_rate = DEFAULT_TOOLCHANGE_FEED_RATE,
|
|
.tool_change.seek_rate = DEFAULT_TOOLCHANGE_SEEK_RATE,
|
|
.tool_change.pulloff_rate = DEFAULT_TOOLCHANGE_PULLOFF_RATE,
|
|
|
|
.parking.flags.enabled = DEFAULT_PARKING_ENABLE,
|
|
.parking.flags.deactivate_upon_init = DEFAULT_DEACTIVATE_PARKING_UPON_INIT,
|
|
.parking.flags.enable_override_control= DEFAULT_ENABLE_PARKING_OVERRIDE_CONTROL,
|
|
.parking.axis = DEFAULT_PARKING_AXIS,
|
|
.parking.target = DEFAULT_PARKING_TARGET,
|
|
.parking.rate = DEFAULT_PARKING_RATE,
|
|
.parking.pullout_rate = DEFAULT_PARKING_PULLOUT_RATE,
|
|
.parking.pullout_increment = DEFAULT_PARKING_PULLOUT_INCREMENT
|
|
};
|
|
|
|
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_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"},
|
|
#ifdef A_AXIS
|
|
{ Group_Axis, Group_AAxis, "A-axis"},
|
|
#endif
|
|
#ifdef B_AXIS
|
|
{ Group_Axis, Group_BAxis, "B-axis"},
|
|
#endif
|
|
#ifdef C_AXIS
|
|
{ Group_Axis, Group_CAxis, "C-axis"}
|
|
#endif
|
|
};
|
|
|
|
static status_code_t set_probe_invert (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_report_mask (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_report_inches (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_control_invert (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_spindle_invert (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_control_disable_pullup (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_probe_disable_pullup (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_soft_limits_enable (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_hard_limits_enable (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_jog_soft_limited (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_homing_enable (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_enable_legacy_rt_commands (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_homing_cycle (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_mode (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_sleep_enable (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_hold_actions (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_force_initialization_alarm (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_probe_allow_feed_override (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_tool_change_mode (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_tool_change_probing_distance (setting_id_t id, float value);
|
|
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
|
|
static status_code_t set_parking_enable (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_restore_overrides (setting_id_t id, uint_fast16_t int_value);
|
|
static status_code_t set_door_options (setting_id_t id, uint_fast16_t int_value);
|
|
#endif
|
|
#ifdef ENABLE_SPINDLE_LINEARIZATION
|
|
static status_code_t set_linear_piece (setting_id_t id, char *svalue);
|
|
static char *get_linear_piece (setting_id_t id);
|
|
#endif
|
|
#if COMPATIBILITY_LEVEL > 1
|
|
static status_code_t set_limits_invert_mask (setting_id_t id, uint_fast16_t int_value);
|
|
#endif
|
|
static status_code_t set_axis_setting (setting_id_t setting, float value);
|
|
static float get_float (setting_id_t setting);
|
|
static uint32_t get_int (setting_id_t id);
|
|
static bool is_setting_available (const setting_detail_t *setting);
|
|
|
|
static char control_signals[] = "Reset,Feed hold,Cycle start,Safety door,Block delete,Optional stop,EStop,Probe connected,Motor fault";
|
|
static char control_signals_map[] = "0,1,2,3,4,5,6,7,8";
|
|
static char spindle_signals[] = "Spindle enable,Spindle direction,PWM";
|
|
|
|
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 },
|
|
{ Setting_StepInvertMask, Group_Stepper, "Step pulse invert", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsLegacy, &settings.steppers.step_invert.mask, NULL, NULL },
|
|
{ Setting_DirInvertMask, Group_Stepper, "Step direction invert", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsLegacy, &settings.steppers.dir_invert.mask, NULL, NULL },
|
|
{ Setting_InvertStepperEnable, Group_Stepper, "Invert step enable pin(s)", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsLegacy, &settings.steppers.enable_invert.mask, NULL, NULL },
|
|
#if COMPATIBILITY_LEVEL <= 1
|
|
{ Setting_LimitPinsInvertMask, Group_Limits, "Invert limit pins", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsLegacy, &settings.limits.invert.mask, NULL, NULL },
|
|
#else
|
|
{ Setting_LimitPinsInvertMask, Group_Limits, "Invert limit pins", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsLegacyFn, set_limits_invert_mask, get_int, NULL },
|
|
#endif
|
|
{ Setting_InvertProbePin, Group_Probing, "Invert probe pin", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsLegacyFn, set_probe_invert, get_int, NULL },
|
|
{ Setting_SpindlePWMBehaviour, Group_Spindle, "Disable spindle with zero speed", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtended, &settings.spindle.flags.mask, NULL, is_setting_available },
|
|
// { Setting_SpindlePWMBehaviour, Group_Spindle, "Spindle enable vs. speed behaviour", NULL, Format_RadioButtons, "No action,Disable spindle with zero speed,Enable spindle with all speeds", NULL, NULL, Setting_IsExtended, &settings.spindle.flags.mask, NULL, NULL },
|
|
#if COMPATIBILITY_LEVEL <= 1
|
|
{ Setting_StatusReportMask, Group_General, "Status report options", NULL, Format_Bitfield, "Position in machine coordinate,Buffer state,Line numbers,Feed & speed,Pin state,Work coordinate offset,Overrides,Probe coordinates,Buffer sync on WCO change,Parser state,Alarm substatus,Run substatus", NULL, NULL, Setting_IsExtendedFn, set_report_mask, get_int, NULL },
|
|
#else
|
|
{ Setting_StatusReportMask, Group_General, "Status report options", NULL, Format_Bitfield, "Position in machine coordinate,Buffer state", NULL, NULL, Setting_IsLegacyFn, set_report_mask, get_int, NULL },
|
|
#endif
|
|
{ Setting_JunctionDeviation, Group_General, "Junction deviation", "mm", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacy, &settings.junction_deviation, NULL, NULL },
|
|
{ Setting_ArcTolerance, Group_General, "Arc tolerance", "mm", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacy, &settings.arc_tolerance, NULL, NULL },
|
|
{ Setting_ReportInches, Group_General, "Report in inches", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsLegacyFn, set_report_inches, get_int, NULL },
|
|
{ Setting_ControlInvertMask, Group_ControlSignals, "Invert control pins", NULL, Format_Bitfield, control_signals, control_signals_map, NULL, Setting_IsExpandedFn, set_control_invert, get_int, NULL },
|
|
{ Setting_CoolantInvertMask, Group_Coolant, "Invert coolant pins", NULL, Format_Bitfield, "Flood,Mist", NULL, NULL, Setting_IsExtended, &settings.coolant_invert.mask, NULL, NULL },
|
|
{ Setting_SpindleInvertMask, Group_Spindle, "Invert spindle signals", NULL, Format_Bitfield, spindle_signals, NULL, NULL, Setting_IsExtendedFn, set_spindle_invert, get_int, NULL },
|
|
{ Setting_ControlPullUpDisableMask, Group_ControlSignals, "Pullup disable control pins", NULL, Format_Bitfield, control_signals, control_signals_map, NULL, Setting_IsExtendedFn, set_control_disable_pullup, get_int, NULL },
|
|
{ Setting_LimitPullUpDisableMask, Group_Limits, "Pullup disable limit pins", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.limits.disable_pullup.mask, NULL, NULL },
|
|
{ Setting_ProbePullUpDisable, Group_Probing, "Pullup disable probe pin", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_probe_disable_pullup, get_int, NULL },
|
|
{ Setting_SoftLimitsEnable, Group_Limits, "Soft limits enable", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsLegacyFn, set_soft_limits_enable, get_int, NULL },
|
|
#if COMPATIBILITY_LEVEL <= 1
|
|
{ Setting_HardLimitsEnable, Group_Limits, "Hard limits enable", NULL, Format_XBitfield, "Enable,Strict mode", NULL, NULL, Setting_IsExpandedFn, set_hard_limits_enable, get_int, NULL },
|
|
#else
|
|
{ Setting_HardLimitsEnable, Group_Limits, "Hard limits enable", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsLegacyFn, set_hard_limits_enable, get_int, NULL },
|
|
#endif
|
|
#if COMPATIBILITY_LEVEL <= 1
|
|
{ Setting_HomingEnable, Group_Homing, "Homing cycle", NULL, Format_XBitfield, "Enable,Enable single axis commands,Homing on startup required,Set machine origin to 0,Two switches shares one input pin,Allow manual,Override locks,Keep homed status on reset", NULL, NULL, Setting_IsExpandedFn, set_homing_enable, get_int, NULL },
|
|
#else
|
|
{ Setting_HomingEnable, Group_Homing, "Homing cycle enable", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsLegacyFn, set_homing_enable, get_int, NULL },
|
|
#endif
|
|
{ Setting_HomingDirMask, Group_Homing, "Homing direction invert", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsLegacy, &settings.homing.dir_mask.value, NULL, NULL },
|
|
{ Setting_HomingFeedRate, Group_Homing, "Homing locate feed rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsLegacy, &settings.homing.feed_rate, NULL, NULL },
|
|
{ Setting_HomingSeekRate, Group_Homing, "Homing search seek rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsLegacy, &settings.homing.seek_rate, NULL, NULL },
|
|
{ Setting_HomingDebounceDelay, Group_Homing, "Homing switch debounce delay", "milliseconds", Format_Int16, "##0", NULL, NULL, Setting_IsLegacy, &settings.homing.debounce_delay, NULL, NULL },
|
|
{ Setting_HomingPulloff, Group_Homing, "Homing switch pull-off distance", "mm", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacy, &settings.homing.pulloff, NULL, NULL },
|
|
{ Setting_G73Retract, Group_General, "G73 Retract distance", "mm", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.g73_retract, NULL, NULL },
|
|
{ Setting_PulseDelayMicroseconds, Group_Stepper, "Pulse delay", "microseconds", Format_Decimal, "#0.0", NULL, "10", Setting_IsExtended, &settings.steppers.pulse_delay_microseconds, NULL, NULL },
|
|
{ Setting_RpmMax, Group_Spindle, "Maximum spindle speed", "RPM", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacy, &settings.spindle.rpm_max, NULL, is_setting_available },
|
|
{ Setting_RpmMin, Group_Spindle, "Minimum spindle speed", "RPM", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacy, &settings.spindle.rpm_min, NULL, is_setting_available },
|
|
{ Setting_Mode, Group_General, "Mode of operation", NULL, Format_RadioButtons, "Normal,Laser mode,Lathe mode", NULL, NULL, Setting_IsLegacyFn, set_mode, get_int, NULL },
|
|
{ Setting_PWMFreq, Group_Spindle, "Spindle PWM frequency", "Hz", Format_Decimal, "#####0", NULL, NULL, Setting_IsExtended, &settings.spindle.pwm_freq, NULL, is_setting_available },
|
|
{ Setting_PWMOffValue, Group_Spindle, "Spindle PWM off value", "percent", Format_Decimal, "##0.0", NULL, "100", Setting_IsExtended, &settings.spindle.pwm_off_value, NULL, is_setting_available },
|
|
{ Setting_PWMMinValue, Group_Spindle, "Spindle PWM min value", "percent", Format_Decimal, "##0.0", NULL, "100", Setting_IsExtended, &settings.spindle.pwm_min_value, NULL, is_setting_available },
|
|
{ Setting_PWMMaxValue, Group_Spindle, "Spindle PWM max value", "percent", Format_Decimal, "##0.0", NULL, "100", Setting_IsExtended, &settings.spindle.pwm_max_value, NULL, is_setting_available },
|
|
{ Setting_StepperDeenergizeMask, Group_Stepper, "Steppers deenergize", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.steppers.deenergize.mask, NULL, NULL },
|
|
{ Setting_SpindlePPR, Group_Spindle, "Spindle pulses per revolution (PPR)", NULL, Format_Int16, "###0", NULL, NULL, Setting_IsExtended, &settings.spindle.ppr, NULL, is_setting_available },
|
|
{ Setting_EnableLegacyRTCommands, Group_General, "Enable legacy RT commands", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_enable_legacy_rt_commands, get_int, NULL },
|
|
{ Setting_JogSoftLimited, Group_Jogging, "Limit jog commands", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_jog_soft_limited, get_int, NULL },
|
|
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
|
|
{ Setting_ParkingEnable, Group_SafetyDoor, "Parking cycle", NULL, Format_XBitfield, "Enable,Enable parking override control,Deactivate upon init", NULL, NULL, Setting_IsExtendedFn, set_parking_enable, get_int, NULL },
|
|
{ Setting_ParkingAxis, Group_SafetyDoor, "Parking axis", NULL, Format_RadioButtons, "X,Y,Z", NULL, NULL, Setting_IsExtended, &settings.parking.axis, NULL, NULL },
|
|
#endif
|
|
{ Setting_HomingLocateCycles, Group_Homing, "Homing passes", NULL, Format_Int8, "##0", "1", "128", Setting_IsExtended, &settings.homing.locate_cycles, NULL, NULL },
|
|
{ Setting_HomingCycle_1, Group_Homing, "Axes homing, first pass", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtendedFn, set_homing_cycle, get_int, NULL },
|
|
{ Setting_HomingCycle_2, Group_Homing, "Axes homing, second pass", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtendedFn, set_homing_cycle, get_int, NULL },
|
|
{ Setting_HomingCycle_3, Group_Homing, "Axes homing, third pass", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtendedFn, set_homing_cycle, get_int, NULL },
|
|
#ifdef A_AXIS
|
|
{ Setting_HomingCycle_4, Group_Homing, "Axes homing, fourth pass", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtendedFn, set_homing_cycle, get_int, NULL },
|
|
#endif
|
|
#ifdef B_AXIS
|
|
{ Setting_HomingCycle_5, Group_Homing, "Axes homing, fifth pass", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtendedFn, set_homing_cycle, get_int, NULL },
|
|
#endif
|
|
#ifdef C_AXIS
|
|
{ Setting_HomingCycle_6, Group_Homing, "Axes homing, sixth pass", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtendedFn, set_homing_cycle, get_int, NULL },
|
|
#endif
|
|
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
|
|
{ Setting_ParkingPulloutIncrement, Group_SafetyDoor, "Parking pull-out distance", "mm", Format_Decimal, "###0.0", NULL, NULL, Setting_IsExtended, &settings.parking.pullout_increment, NULL, NULL },
|
|
{ Setting_ParkingPulloutRate, Group_SafetyDoor, "Parking pull-out rate", "mm/min", Format_Decimal, "###0.0", NULL, NULL, Setting_IsExtended, &settings.parking.pullout_rate, NULL, NULL },
|
|
{ Setting_ParkingTarget, Group_SafetyDoor, "Parking target", "mm", Format_Decimal, "-###0.0", "-100000", NULL, Setting_IsExtended, &settings.parking.target, NULL, NULL },
|
|
{ Setting_ParkingFastRate, Group_SafetyDoor, "Parking fast rate", "mm/min", Format_Decimal, "###0.0", NULL, NULL, Setting_IsExtended, &settings.parking.rate, NULL, NULL },
|
|
{ Setting_RestoreOverrides, Group_General, "Restore overrides", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_restore_overrides, get_int, NULL },
|
|
{ Setting_DoorOptions, Group_SafetyDoor, "Safety door options", NULL, Format_Bitfield, "Ignore when idle,Keep coolant state on open", NULL, NULL, Setting_IsExtendedFn, set_door_options, get_int, NULL },
|
|
#endif
|
|
{ Setting_SleepEnable, Group_General, "Sleep enable", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_sleep_enable, get_int, NULL },
|
|
{ Setting_HoldActions, Group_General, "Feed hold actions", NULL, Format_Bitfield, "Disable laser during hold,Restore spindle and coolant state on resume", NULL, NULL, Setting_IsExtendedFn, set_hold_actions, get_int, NULL },
|
|
{ Setting_ForceInitAlarm, Group_General, "Force init alarm", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_force_initialization_alarm, get_int, NULL },
|
|
{ Setting_ProbingFeedOverride, Group_Probing, "Probing feed override", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_probe_allow_feed_override, get_int, NULL },
|
|
#ifdef ENABLE_SPINDLE_LINEARIZATION
|
|
{ Setting_LinearSpindlePiece1, Group_Spindle, "Spindle linearisation, first point", NULL, Format_String, "x30", NULL, "30", Setting_IsExtendedFn, set_linear_piece, get_linear_piece, NULL },
|
|
{ Setting_LinearSpindlePiece2, Group_Spindle, "Spindle linearisation, second point", NULL, Format_String, "x30", NULL, "30", Setting_IsExtendedFn, set_linear_piece, get_linear_piece, NULL },
|
|
{ Setting_LinearSpindlePiece3, Group_Spindle, "Spindle linearisation, third point", NULL, Format_String, "x30", NULL, "30", Setting_IsExtendedFn, set_linear_piece, get_linear_piece, NULL },
|
|
{ Setting_LinearSpindlePiece4, Group_Spindle, "Spindle linearisation, fourth point", NULL, Format_String, "x30", NULL, "30", Setting_IsExtendedFn, set_linear_piece, get_linear_piece, NULL },
|
|
#endif
|
|
{ Setting_SpindlePGain, Group_Spindle_ClosedLoop, "Spindle P-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.spindle.pid.p_gain, NULL, NULL },
|
|
{ Setting_SpindleIGain, Group_Spindle_ClosedLoop, "Spindle I-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.spindle.pid.i_gain, NULL, NULL },
|
|
{ Setting_SpindleDGain, Group_Spindle_ClosedLoop, "Spindle D-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.spindle.pid.d_gain, NULL, NULL },
|
|
{ Setting_SpindleMaxError, Group_Spindle_ClosedLoop, "Spindle PID max error", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.spindle.pid.max_error, NULL, NULL },
|
|
{ Setting_SpindleIMaxError, Group_Spindle_ClosedLoop, "Spindle PID max I error", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.spindle.pid.i_max_error, NULL, NULL },
|
|
{ Setting_PositionPGain, Group_Spindle_Sync, "Spindle sync P-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.position.pid.p_gain, NULL, NULL },
|
|
{ Setting_PositionIGain, Group_Spindle_Sync, "Spindle sync I-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.position.pid.i_gain, NULL, NULL },
|
|
{ Setting_PositionDGain, Group_Spindle_Sync, "Spindle sync D-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.position.pid.d_gain, NULL, NULL },
|
|
{ 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, NULL },
|
|
{ Setting_AxisStepsPerMM, Group_Axis0, "?-axis travel resolution", "step/mm", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL },
|
|
{ Setting_AxisMaxRate, Group_Axis0, "?-axis maximum rate", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL },
|
|
{ Setting_AxisAcceleration, Group_Axis0, "?-axis acceleration", "mm/sec^2", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL },
|
|
{ Setting_AxisMaxTravel, Group_Axis0, "?-axis maximum travel", "mm", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL },
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
{ Setting_AxisBacklash, Group_Axis0, "?-axis backlash compensation", "mm", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtendedFn, set_axis_setting, get_float, NULL, NULL },
|
|
#endif
|
|
{ Setting_AxisAutoSquareOffset, Group_Axis0, "?-axis dual axis offset", "mm", Format_Decimal, "-0.000", "-2", "2", Setting_IsExtendedFn, set_axis_setting, get_float, is_setting_available },
|
|
{ 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 },
|
|
{ Setting_ToolChangeFeedRate, Group_Toolchange, "Tool change locate feed rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtended, &settings.tool_change.feed_rate, NULL, NULL },
|
|
{ Setting_ToolChangeSeekRate, Group_Toolchange, "Tool change search seek rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtended, &settings.tool_change.seek_rate, NULL, NULL },
|
|
{ Setting_ToolChangePulloffRate, Group_Toolchange, "Tool change probe pull-off rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtended, &settings.tool_change.pulloff_rate, NULL, NULL },
|
|
{ Setting_DualAxisLengthFailPercent, Group_Limits_DualAxis, "Dual axis length fail", "percent", Format_Decimal, "##0.0", "0", "100", Setting_IsExtended, &settings.homing.dual_axis.fail_length_percent, NULL, NULL },
|
|
{ Setting_DualAxisLengthFailMin, Group_Limits_DualAxis, "Dual axis length fail min", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_min, NULL, NULL },
|
|
{ Setting_DualAxisLengthFailMax, Group_Limits_DualAxis, "Dual axis length fail max", "mm/min", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsExtended, &settings.homing.dual_axis.fail_distance_max, NULL, NULL }
|
|
// { Settings_Axis_Rotational, Group_Stepper, "Rotational axes", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.steppers.is_rotational.mask, NULL, NULL }
|
|
};
|
|
|
|
static setting_details_t details = {
|
|
.groups = setting_group_detail,
|
|
.n_groups = sizeof(setting_group_detail) / sizeof(setting_group_detail_t),
|
|
.settings = setting_detail,
|
|
.n_settings = sizeof(setting_detail) / sizeof(setting_detail_t),
|
|
.save = settings_write_global
|
|
};
|
|
|
|
// Acceleration override
|
|
|
|
static struct {
|
|
bool valid;
|
|
float acceleration[N_AXIS];
|
|
} override_backup = { .valid = false };
|
|
|
|
static void save_override_backup (void)
|
|
{
|
|
uint_fast8_t idx = N_AXIS;
|
|
|
|
do {
|
|
idx--;
|
|
override_backup.acceleration[idx] = settings.axis[idx].acceleration;
|
|
} while(idx);
|
|
|
|
override_backup.valid = true;
|
|
}
|
|
|
|
static void restore_override_backup (void)
|
|
{
|
|
uint_fast8_t idx = N_AXIS;
|
|
|
|
if(override_backup.valid) do {
|
|
idx--;
|
|
settings.axis[idx].acceleration = override_backup.acceleration[idx];
|
|
} while(idx);
|
|
}
|
|
|
|
// Temporarily override acceleration, if 0 restore to setting value.
|
|
// Note: only allowed when current state is idle.
|
|
bool settings_override_acceleration (uint8_t axis, float acceleration)
|
|
{
|
|
if(state_get() != STATE_IDLE)
|
|
return false;
|
|
|
|
if(acceleration <= 0.0f) {
|
|
if(override_backup.valid)
|
|
settings.axis[axis].acceleration = override_backup.acceleration[axis];
|
|
} else {
|
|
if(!override_backup.valid)
|
|
save_override_backup();
|
|
settings.axis[axis].acceleration = acceleration * 60.0f * 60.0f; // Limit max to setting value?
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ---
|
|
|
|
setting_details_t *settings_get_details (void)
|
|
{
|
|
details.on_get_settings = grbl.on_get_settings;
|
|
|
|
return &details;
|
|
}
|
|
|
|
#if COMPATIBILITY_LEVEL > 1
|
|
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_PIN_MASK) : INVERT_LIMIT_PIN_MASK) & AXES_BITMASK;
|
|
|
|
return Status_OK;
|
|
}
|
|
#endif
|
|
|
|
static status_code_t set_probe_invert (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
if(!hal.probe.configure)
|
|
return Status_SettingDisabled;
|
|
|
|
settings.probe.invert_probe_pin = int_value != 0;
|
|
hal.probe.configure(false, false);
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_report_mask (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
#if COMPATIBILITY_LEVEL <= 1
|
|
settings.status_report.mask = int_value;
|
|
#else
|
|
int_value &= 0b11;
|
|
settings.status_report.mask = (settings.status_report.mask & ~0b11) | int_value;
|
|
#endif
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_report_inches (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.flags.report_inches = int_value != 0;
|
|
report_init();
|
|
system_flag_wco_change(); // Make sure WCO is immediately updated.
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_control_invert (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.control_invert.mask = int_value & hal.signals_cap.mask;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_spindle_invert (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.spindle.invert.mask = int_value;
|
|
if(settings.spindle.invert.pwm && !hal.driver_cap.spindle_pwm_invert) {
|
|
settings.spindle.invert.pwm = Off;
|
|
return Status_SettingDisabled;
|
|
}
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_control_disable_pullup (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.control_disable_pullup.mask = int_value & hal.signals_cap.mask;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_probe_disable_pullup (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
if(!hal.probe.configure)
|
|
return Status_SettingDisabled;
|
|
|
|
settings.probe.disable_probe_pullup = int_value != 0;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_soft_limits_enable (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
if (int_value && !settings.homing.flags.enabled)
|
|
return Status_SoftLimitError;
|
|
|
|
settings.limits.flags.soft_enabled = int_value != 0;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_hard_limits_enable (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.limits.flags.hard_enabled = bit_istrue(int_value, bit(0));
|
|
#if COMPATIBILITY_LEVEL <= 1
|
|
settings.limits.flags.check_at_init = bit_istrue(int_value, bit(1));
|
|
#endif
|
|
hal.limits.enable(settings.limits.flags.hard_enabled, false); // Change immediately. NOTE: Nice to have but could be problematic later.
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_jog_soft_limited (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
if (int_value && !settings.homing.flags.enabled)
|
|
return Status_SoftLimitError;
|
|
|
|
settings.limits.flags.jog_soft_limited = int_value != 0;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
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;
|
|
#else
|
|
settings.homing.flags.value = int_value & 0x0F;
|
|
settings.limits.flags.two_switches = bit_istrue(int_value, bit(4));
|
|
settings.homing.flags.manual = bit_istrue(int_value, bit(5));
|
|
settings.homing.flags.override_locks = bit_istrue(int_value, bit(6));
|
|
settings.homing.flags.keep_on_reset = bit_istrue(int_value, bit(7));
|
|
#endif
|
|
} else {
|
|
settings.homing.flags.value = 0;
|
|
settings.limits.flags.soft_enabled = Off; // Force disable soft-limits.
|
|
settings.limits.flags.jog_soft_limited = Off;
|
|
}
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_enable_legacy_rt_commands (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.flags.legacy_rt_commands = int_value != 0;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_homing_cycle (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.homing.cycle[id - Setting_HomingCycle_1].mask = int_value;
|
|
limits_set_homing_axes();
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_mode (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
switch((machine_mode_t)int_value) {
|
|
|
|
case Mode_Standard:
|
|
settings.flags.disable_laser_during_hold = 0;
|
|
gc_state.modal.diameter_mode = false;
|
|
break;
|
|
|
|
case Mode_Laser:
|
|
if(!hal.driver_cap.variable_spindle)
|
|
return Status_SettingDisabledLaser;
|
|
if(settings.mode != Mode_Laser)
|
|
settings.flags.disable_laser_during_hold = DEFAULT_DISABLE_LASER_DURING_HOLD;
|
|
gc_state.modal.diameter_mode = false;
|
|
break;
|
|
|
|
case Mode_Lathe:
|
|
settings.flags.disable_laser_during_hold = 0;
|
|
break;
|
|
|
|
default: // Mode_Standard
|
|
return Status_InvalidStatement;
|
|
}
|
|
|
|
settings.mode = (machine_mode_t)int_value;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
|
|
|
|
static status_code_t set_parking_enable (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.parking.flags.value = bit_istrue(int_value, bit(0)) ? (int_value & 0x07) : 0;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_restore_overrides (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.flags.restore_overrides = int_value != 0;;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_door_options (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.flags.safety_door_ignore_when_idle = bit_istrue(int_value, bit(0));
|
|
settings.flags.keep_coolant_state_on_door_open = bit_istrue(int_value, bit(1));
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
static status_code_t set_sleep_enable (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.flags.sleep_enable = int_value != 0;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_hold_actions (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.flags.disable_laser_during_hold = bit_istrue(int_value, bit(0));
|
|
settings.flags.restore_after_feed_hold = bit_istrue(int_value, bit(1));
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_force_initialization_alarm (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.flags.force_initialization_alarm = int_value != 0;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_probe_allow_feed_override (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
settings.probe.allow_feed_override = int_value != 0;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_tool_change_mode (setting_id_t id, uint_fast16_t int_value)
|
|
{
|
|
if(!hal.driver_cap.atc && hal.stream.suspend_read && int_value <= ToolChange_Ignore) {
|
|
#if COMPATIBILITY_LEVEL > 1
|
|
if((toolchange_mode_t)int_value == ToolChange_Manual_G59_3 || (toolchange_mode_t)int_value == ToolChange_SemiAutomatic)
|
|
return Status_InvalidStatement;
|
|
#endif
|
|
settings.tool_change.mode = (toolchange_mode_t)int_value;
|
|
tc_init();
|
|
} else
|
|
return Status_InvalidStatement;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static status_code_t set_tool_change_probing_distance (setting_id_t id, float value)
|
|
{
|
|
if(hal.driver_cap.atc)
|
|
return Status_InvalidStatement;
|
|
|
|
settings.tool_change.probing_distance = value;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
#ifdef ENABLE_SPINDLE_LINEARIZATION
|
|
|
|
static status_code_t set_linear_piece (setting_id_t id, char *svalue)
|
|
{
|
|
uint32_t idx = id - Setting_LinearSpindlePiece1;
|
|
float rpm, start, end;
|
|
|
|
if(svalue[0] == '0' && svalue[1] == '\0') {
|
|
settings.spindle.pwm_piece[idx].rpm = NAN;
|
|
settings.spindle.pwm_piece[idx].start =
|
|
settings.spindle.pwm_piece[idx].end = 0.0f;
|
|
} else if(sscanf(svalue, "%f,%f,%f", &rpm, &start, &end) == 3) {
|
|
settings.spindle.pwm_piece[idx].rpm = rpm;
|
|
settings.spindle.pwm_piece[idx].start = start;
|
|
settings.spindle.pwm_piece[idx].end = end;
|
|
} else
|
|
return Status_InvalidStatement;
|
|
|
|
return Status_OK;
|
|
}
|
|
|
|
static char *get_linear_piece (setting_id_t id)
|
|
{
|
|
static char buf[20];
|
|
|
|
uint32_t idx = id - Setting_LinearSpindlePiece1;
|
|
|
|
if(isnan(settings.spindle.pwm_piece[idx].rpm))
|
|
strcpy(buf, ftoa(settings.spindle.pwm_piece[idx].rpm, N_DECIMAL_RPMVALUE));
|
|
else {
|
|
sprintf(buf, "$%d=%f,%f,%f" ASCII_EOL, (setting_id_t)(Setting_LinearSpindlePiece1 + idx), settings.spindle.pwm_piece[idx].rpm, settings.spindle.pwm_piece[idx].start, settings.spindle.pwm_piece[idx].end);
|
|
hal.stream.write(buf);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
#endif
|
|
|
|
inline static setting_id_t normalize_id (setting_id_t id)
|
|
{
|
|
if((id > Setting_AxisSettingsBase && id <= Setting_AxisSettingsMax) ||
|
|
(id > Setting_AxisSettingsBase2 && id <= Setting_AxisSettingsMax2))
|
|
id -= id % AXIS_SETTINGS_INCREMENT;
|
|
else if(id > Setting_EncoderSettingsBase && id <= Setting_EncoderSettingsMax)
|
|
id = (setting_id_t)(Setting_EncoderSettingsBase + (id % ENCODER_SETTINGS_INCREMENT));
|
|
|
|
return id;
|
|
}
|
|
|
|
setting_id_t settings_get_axis_base (setting_id_t id, uint_fast8_t *idx)
|
|
{
|
|
setting_id_t base = normalize_id(id);
|
|
*idx = id - base;
|
|
|
|
return *idx < N_AXIS ? base : Setting_SettingsMax;
|
|
}
|
|
|
|
static status_code_t set_axis_setting (setting_id_t setting, float value)
|
|
{
|
|
uint_fast8_t idx;
|
|
status_code_t status = Status_OK;
|
|
|
|
switch(settings_get_axis_base(setting, &idx)) {
|
|
|
|
case Setting_AxisStepsPerMM:
|
|
if (hal.max_step_rate && value * settings.axis[idx].max_rate > (float)hal.max_step_rate * 60.0f)
|
|
status = Status_MaxStepRateExceeded;
|
|
else
|
|
settings.axis[idx].steps_per_mm = value;
|
|
break;
|
|
|
|
case Setting_AxisMaxRate:
|
|
if (hal.max_step_rate && value * settings.axis[idx].steps_per_mm > (float)hal.max_step_rate * 60.0f)
|
|
status = Status_MaxStepRateExceeded;
|
|
else
|
|
settings.axis[idx].max_rate = value;
|
|
break;
|
|
|
|
case Setting_AxisAcceleration:
|
|
settings.axis[idx].acceleration = override_backup.acceleration[idx] = value * 60.0f * 60.0f; // Convert to mm/min^2 for grbl internal use.
|
|
break;
|
|
|
|
case Setting_AxisMaxTravel:
|
|
settings.axis[idx].max_travel = -value; // Store as negative for grbl internal use.
|
|
break;
|
|
|
|
case Setting_AxisBacklash:
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
settings.axis[idx].backlash = value;
|
|
#else
|
|
status = Status_SettingDisabled;
|
|
#endif
|
|
break;
|
|
|
|
case Setting_AxisAutoSquareOffset:
|
|
if(hal.stepper.get_auto_squared && bit_istrue(hal.stepper.get_auto_squared().mask, bit(idx)))
|
|
settings.axis[idx].dual_axis_offset = value;
|
|
else
|
|
status = Status_SettingDisabled;
|
|
break;
|
|
|
|
default:
|
|
status = Status_SettingDisabled;
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static float get_float (setting_id_t setting)
|
|
{
|
|
float value = 0.0f;
|
|
|
|
if (setting >= Setting_AxisSettingsBase && setting <= Setting_AxisSettingsMax) {
|
|
|
|
uint_fast8_t idx;
|
|
|
|
switch(settings_get_axis_base(setting, &idx)) {
|
|
|
|
case Setting_AxisStepsPerMM:
|
|
value = settings.axis[idx].steps_per_mm;
|
|
break;
|
|
|
|
case Setting_AxisMaxRate:
|
|
value = settings.axis[idx].max_rate;
|
|
break;
|
|
|
|
case Setting_AxisAcceleration:
|
|
value = settings.axis[idx].acceleration / (60.0f * 60.0f); // Convert to mm/min^2 for grbl internal use.
|
|
break;
|
|
|
|
case Setting_AxisMaxTravel:
|
|
value = -settings.axis[idx].max_travel; // Store as negative for grbl internal use.
|
|
break;
|
|
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
case Setting_AxisBacklash:
|
|
value = settings.axis[idx].backlash;
|
|
break;
|
|
#endif
|
|
|
|
case Setting_AxisAutoSquareOffset:
|
|
value = settings.axis[idx].dual_axis_offset;
|
|
break;
|
|
|
|
default: // for stopping compiler warning
|
|
break;
|
|
}
|
|
} else switch(setting) {
|
|
|
|
case Setting_ToolChangeProbingDistance:
|
|
value = settings.tool_change.probing_distance;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static uint32_t get_int (setting_id_t id)
|
|
{
|
|
uint32_t value = 0;
|
|
|
|
switch(id) {
|
|
|
|
#if COMPATIBILITY_LEVEL > 1
|
|
case Setting_LimitPinsInvertMask:
|
|
value = settings.limits.invert.mask == INVERT_LIMIT_PIN_MASK ? 0 : 1;
|
|
break;
|
|
#endif
|
|
|
|
case Setting_Mode:
|
|
value = settings.mode;
|
|
break;
|
|
|
|
case Setting_InvertProbePin:
|
|
value = settings.probe.invert_probe_pin;
|
|
break;
|
|
|
|
case Setting_StatusReportMask:
|
|
#if COMPATIBILITY_LEVEL <= 1
|
|
value = settings.status_report.mask;
|
|
#else
|
|
value = settings.status_report.mask & 0b11;
|
|
#endif
|
|
break;
|
|
|
|
case Setting_ReportInches:
|
|
value = settings.flags.report_inches;
|
|
break;
|
|
|
|
case Setting_ControlInvertMask:
|
|
value = settings.control_invert.mask;
|
|
break;
|
|
|
|
case Setting_SpindleInvertMask:
|
|
value = settings.spindle.invert.mask;
|
|
break;
|
|
|
|
case Setting_ControlPullUpDisableMask:
|
|
value = settings.control_disable_pullup.mask;
|
|
break;
|
|
|
|
case Setting_ProbePullUpDisable:
|
|
value = settings.probe.disable_probe_pullup;
|
|
break;
|
|
|
|
case Setting_SoftLimitsEnable:
|
|
value = settings.limits.flags.soft_enabled;
|
|
break;
|
|
|
|
case Setting_HardLimitsEnable:
|
|
value = ((settings.limits.flags.hard_enabled & bit(0)) ? bit(0) | (settings.limits.flags.check_at_init ? bit(1) : 0) : 0);
|
|
break;
|
|
|
|
case Setting_JogSoftLimited:
|
|
value = settings.limits.flags.jog_soft_limited;
|
|
break;
|
|
|
|
case Setting_HomingEnable:
|
|
value = (settings.homing.flags.value & 0x0F) |
|
|
(settings.limits.flags.two_switches ? bit(4) : 0) |
|
|
(settings.homing.flags.manual ? bit(5) : 0) |
|
|
(settings.homing.flags.override_locks ? bit(6) : 0) |
|
|
(settings.homing.flags.keep_on_reset ? bit(7) : 0);
|
|
break;
|
|
|
|
case Setting_EnableLegacyRTCommands:
|
|
value = settings.flags.legacy_rt_commands;
|
|
break;
|
|
|
|
case Setting_ParkingEnable:
|
|
value = settings.parking.flags.value;
|
|
break;
|
|
|
|
case Setting_HomingCycle_1:
|
|
case Setting_HomingCycle_2:
|
|
case Setting_HomingCycle_3:
|
|
case Setting_HomingCycle_4:
|
|
case Setting_HomingCycle_5:
|
|
case Setting_HomingCycle_6:
|
|
value = settings.homing.cycle[id - Setting_HomingCycle_1].mask;
|
|
break;
|
|
|
|
case Setting_RestoreOverrides:
|
|
value = settings.flags.restore_overrides;
|
|
break;
|
|
|
|
case Setting_DoorOptions:
|
|
value = settings.flags.safety_door_ignore_when_idle | (settings.flags.keep_coolant_state_on_door_open << 1) ;
|
|
break;
|
|
|
|
case Setting_SleepEnable:
|
|
value = settings.flags.sleep_enable;
|
|
break;
|
|
|
|
case Setting_HoldActions:
|
|
value = (settings.flags.disable_laser_during_hold ? bit(0) : 0) | (settings.flags.restore_after_feed_hold ? bit(1) : 0);
|
|
break;
|
|
|
|
case Setting_ForceInitAlarm:
|
|
value = settings.flags.force_initialization_alarm;
|
|
break;
|
|
|
|
case Setting_ProbingFeedOverride:
|
|
value = settings.probe.allow_feed_override;
|
|
break;
|
|
|
|
case Setting_ToolChangeMode:
|
|
value = settings.tool_change.mode;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
inline static uint8_t get_decimal_places (const char *format)
|
|
{
|
|
char *dp = format == NULL ? NULL : strchr(format, '.');
|
|
|
|
return dp ? strchr(format, '\0') - dp - 1 : 1;
|
|
}
|
|
|
|
char *setting_get_value (const setting_detail_t *setting, uint_fast16_t offset)
|
|
{
|
|
char *value = NULL;
|
|
setting_id_t id = (setting_id_t)(setting->id + offset);
|
|
|
|
switch(setting->type) {
|
|
|
|
case Setting_NonCore:
|
|
case Setting_IsExtended:
|
|
case Setting_IsLegacy:
|
|
case Setting_IsExpanded:
|
|
switch(setting->datatype) {
|
|
|
|
case Format_Decimal:
|
|
value = ftoa(*((float *)(setting->value)), get_decimal_places(setting->format));
|
|
break;
|
|
|
|
case Format_Int8:
|
|
case Format_Bool:
|
|
case Format_Bitfield:
|
|
case Format_XBitfield:
|
|
case Format_AxisMask:
|
|
case Format_RadioButtons:
|
|
value = uitoa(*((uint8_t *)(setting->value)));
|
|
break;
|
|
|
|
case Format_Int16:
|
|
value = uitoa(*((uint16_t *)(setting->value)));
|
|
break;
|
|
|
|
case Format_Integer:
|
|
value = uitoa(*((uint32_t *)(setting->value)));
|
|
break;
|
|
|
|
case Format_String:
|
|
case Format_Password:
|
|
case Format_IPv4:
|
|
value = ((char *)(setting->value));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case Setting_NonCoreFn:
|
|
case Setting_IsExtendedFn:
|
|
case Setting_IsLegacyFn:
|
|
case Setting_IsExpandedFn:
|
|
switch(setting->datatype) {
|
|
|
|
case Format_Decimal:
|
|
value = ftoa(((setting_get_float_ptr)(setting->get_value))(id), get_decimal_places(setting->format));
|
|
break;
|
|
|
|
case Format_String:
|
|
case Format_Password:
|
|
case Format_IPv4:
|
|
value = ((setting_get_string_ptr)(setting->get_value))(id);
|
|
break;
|
|
|
|
default:
|
|
value = uitoa(((setting_get_int_ptr)(setting->get_value))(id));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static bool is_setting_available (const setting_detail_t *setting)
|
|
{
|
|
bool available = false; // settings_is_group_available(setting->group);
|
|
|
|
switch(normalize_id(setting->id)) {
|
|
|
|
case Setting_SpindlePWMBehaviour:
|
|
available = hal.driver_cap.variable_spindle;
|
|
break;
|
|
|
|
case Setting_SpindlePPR:
|
|
available = hal.driver_cap.spindle_sync || hal.driver_cap.spindle_pid;
|
|
break;
|
|
|
|
case Setting_SpindleAtSpeedTolerance:
|
|
available = hal.driver_cap.spindle_at_speed;
|
|
break;
|
|
|
|
case Setting_RpmMax:
|
|
case Setting_RpmMin:
|
|
case Setting_PWMFreq:
|
|
case Setting_PWMOffValue:
|
|
case Setting_PWMMinValue:
|
|
case Setting_PWMMaxValue:
|
|
available = hal.driver_cap.variable_spindle;
|
|
break;
|
|
|
|
case Setting_AxisAutoSquareOffset:
|
|
available = hal.stepper.get_auto_squared != NULL;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return available;
|
|
}
|
|
|
|
// Write build info to persistent storage
|
|
void settings_write_build_info (char *line)
|
|
{
|
|
if(hal.nvs.type != NVS_None)
|
|
hal.nvs.memcpy_to_nvs(NVS_ADDR_BUILD_INFO, (uint8_t *)line, sizeof(stored_line_t), true);
|
|
}
|
|
|
|
// Read build info from persistent storage.
|
|
bool settings_read_build_info(char *line)
|
|
{
|
|
if (!(hal.nvs.type != NVS_None && hal.nvs.memcpy_from_nvs((uint8_t *)line, NVS_ADDR_BUILD_INFO, sizeof(stored_line_t), true) == NVS_TransferResult_OK)) {
|
|
// Reset line with default value
|
|
line[0] = 0; // Empty line
|
|
settings_write_build_info(line);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Write startup line to persistent storage
|
|
void settings_write_startup_line (uint8_t idx, char *line)
|
|
{
|
|
assert(idx < N_STARTUP_LINE);
|
|
|
|
#ifdef FORCE_BUFFER_SYNC_DURING_NVS_WRITE
|
|
protocol_buffer_synchronize(); // A startup line may contain a motion and be executing.
|
|
#endif
|
|
|
|
if(hal.nvs.type != NVS_None)
|
|
hal.nvs.memcpy_to_nvs(NVS_ADDR_STARTUP_BLOCK + idx * (sizeof(stored_line_t) + NVS_CRC_BYTES), (uint8_t *)line, sizeof(stored_line_t), true);
|
|
}
|
|
|
|
// Read startup line to persistent storage.
|
|
bool settings_read_startup_line (uint8_t idx, char *line)
|
|
{
|
|
assert(idx < N_STARTUP_LINE);
|
|
|
|
if (!(hal.nvs.type != NVS_None && hal.nvs.memcpy_from_nvs((uint8_t *)line, NVS_ADDR_STARTUP_BLOCK + idx * (sizeof(stored_line_t) + NVS_CRC_BYTES), sizeof(stored_line_t), true) == NVS_TransferResult_OK)) {
|
|
// Reset line with default value
|
|
*line = '\0'; // Empty line
|
|
settings_write_startup_line(idx, line);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Write selected coordinate data to persistent storage.
|
|
void settings_write_coord_data (coord_system_id_t id, float (*coord_data)[N_AXIS])
|
|
{
|
|
assert(id <= N_CoordinateSystems);
|
|
|
|
#ifdef FORCE_BUFFER_SYNC_DURING_NVS_WRITE
|
|
protocol_buffer_synchronize();
|
|
#endif
|
|
|
|
if(hal.nvs.type != NVS_None)
|
|
hal.nvs.memcpy_to_nvs(NVS_ADDR_PARAMETERS + id * (sizeof(coord_data_t) + NVS_CRC_BYTES), (uint8_t *)coord_data, sizeof(coord_data_t), true);
|
|
}
|
|
|
|
// Read selected coordinate data from persistent storage.
|
|
bool settings_read_coord_data (coord_system_id_t id, float (*coord_data)[N_AXIS])
|
|
{
|
|
assert(id <= N_CoordinateSystems);
|
|
|
|
if (!(hal.nvs.type != NVS_None && hal.nvs.memcpy_from_nvs((uint8_t *)coord_data, NVS_ADDR_PARAMETERS + id * (sizeof(coord_data_t) + NVS_CRC_BYTES), sizeof(coord_data_t), true) == NVS_TransferResult_OK)) {
|
|
// Reset with default zero vector
|
|
memset(coord_data, 0, sizeof(coord_data_t));
|
|
settings_write_coord_data(id, coord_data);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Write selected tool data to persistent storage.
|
|
bool settings_write_tool_data (tool_data_t *tool_data)
|
|
{
|
|
#ifdef N_TOOLS
|
|
assert(tool_data->tool > 0 && tool_data->tool <= N_TOOLS); // NOTE: idx 0 is a non-persistent entry for tools not in tool table
|
|
|
|
if(hal.nvs.type != NVS_None)
|
|
hal.nvs.memcpy_to_nvs(NVS_ADDR_TOOL_TABLE + (tool_data->tool - 1) * (sizeof(tool_data_t) + NVS_CRC_BYTES), (uint8_t *)tool_data, sizeof(tool_data_t), true);
|
|
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// Read selected tool data from persistent storage.
|
|
bool settings_read_tool_data (uint32_t tool, tool_data_t *tool_data)
|
|
{
|
|
#ifdef N_TOOLS
|
|
assert(tool > 0 && tool <= N_TOOLS); // NOTE: idx 0 is a non-persistent entry for tools not in tool table
|
|
|
|
if (!(hal.nvs.type != NVS_None && hal.nvs.memcpy_from_nvs((uint8_t *)tool_data, NVS_ADDR_TOOL_TABLE + (tool - 1) * (sizeof(tool_data_t) + NVS_CRC_BYTES), sizeof(tool_data_t), true) == NVS_TransferResult_OK && tool_data->tool == tool)) {
|
|
memset(tool_data, 0, sizeof(tool_data_t));
|
|
tool_data->tool = tool;
|
|
}
|
|
|
|
return tool_data->tool == tool;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// Read Grbl global settings from persistent storage.
|
|
// Checks version-byte of non-volatile storage and global settings copy.
|
|
bool read_global_settings ()
|
|
{
|
|
bool ok = hal.nvs.type != NVS_None && SETTINGS_VERSION == hal.nvs.get_byte(0) && hal.nvs.memcpy_from_nvs((uint8_t *)&settings, NVS_ADDR_GLOBAL, sizeof(settings_t), true) == NVS_TransferResult_OK;
|
|
|
|
return ok && settings.version == SETTINGS_VERSION;
|
|
}
|
|
|
|
|
|
// Write Grbl global settings and version number to persistent storage
|
|
void settings_write_global (void)
|
|
{
|
|
if(override_backup.valid)
|
|
restore_override_backup();
|
|
|
|
if(hal.nvs.type != NVS_None) {
|
|
hal.nvs.put_byte(0, SETTINGS_VERSION);
|
|
hal.nvs.memcpy_to_nvs(NVS_ADDR_GLOBAL, (uint8_t *)&settings, sizeof(settings_t), true);
|
|
}
|
|
}
|
|
|
|
|
|
// Restore Grbl global settings to defaults and write to persistent storage
|
|
void settings_restore (settings_restore_t restore)
|
|
{
|
|
uint_fast8_t idx;
|
|
stored_line_t empty_line;
|
|
|
|
memset(empty_line, 0xFF, sizeof(stored_line_t));
|
|
*empty_line = '\0';
|
|
|
|
if (restore.defaults) {
|
|
memcpy(&settings, &defaults, sizeof(settings_t));
|
|
|
|
settings.control_invert.mask &= hal.signals_cap.mask;
|
|
settings.spindle.invert.ccw &= hal.driver_cap.spindle_dir;
|
|
settings.spindle.invert.pwm &= hal.driver_cap.spindle_pwm_invert;
|
|
|
|
settings_write_global();
|
|
}
|
|
|
|
if (restore.parameters) {
|
|
float coord_data[N_AXIS];
|
|
|
|
memset(coord_data, 0, sizeof(coord_data));
|
|
for (idx = 0; idx <= N_WorkCoordinateSystems; idx++)
|
|
settings_write_coord_data((coord_system_id_t)idx, &coord_data);
|
|
|
|
settings_write_coord_data(CoordinateSystem_G92, &coord_data); // Clear G92 offsets
|
|
|
|
#ifdef N_TOOLS
|
|
tool_data_t tool_data;
|
|
memset(&tool_data, 0, sizeof(tool_data_t));
|
|
for (idx = 1; idx <= N_TOOLS; idx++) {
|
|
tool_data.tool = idx;
|
|
settings_write_tool_data(&tool_data);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (restore.startup_lines) {
|
|
for (idx = 0; idx < N_STARTUP_LINE; idx++)
|
|
settings_write_startup_line(idx, empty_line);
|
|
}
|
|
|
|
if (restore.build_info) {
|
|
settings_write_build_info(empty_line);
|
|
settings_write_build_info(BUILD_INFO);
|
|
}
|
|
|
|
setting_details_t *details = settings_get_details();
|
|
|
|
while(details->on_get_settings) {
|
|
details = details->on_get_settings();
|
|
if(details->restore)
|
|
details->restore();
|
|
};
|
|
|
|
nvs_buffer_sync_physical();
|
|
}
|
|
|
|
inline static bool is_available (const setting_detail_t *setting)
|
|
{
|
|
return setting->is_available == NULL || setting->is_available(setting);
|
|
}
|
|
|
|
bool settings_is_group_available (setting_group_t group)
|
|
{
|
|
bool available = false;
|
|
|
|
switch(group) {
|
|
|
|
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;
|
|
|
|
case Group_Spindle_ClosedLoop:
|
|
available = hal.driver_cap.spindle_pid;
|
|
break;
|
|
|
|
case Group_Limits_DualAxis:
|
|
available = hal.stepper.get_auto_squared != NULL;
|
|
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
|
|
available = true;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
uint_fast16_t idx;
|
|
setting_details_t *details = settings_get_details();
|
|
do {
|
|
if(details->settings) {
|
|
for(idx = 0; idx < details->n_settings; idx++) {
|
|
if(details->settings[idx].group == group && is_available(&details->settings[idx])) {
|
|
available = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
details = !available && details->on_get_settings ? details->on_get_settings() : NULL;
|
|
} while(details);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return available;
|
|
}
|
|
|
|
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) {
|
|
|
|
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_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++) {
|
|
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;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
const setting_detail_t *setting_get_details (setting_id_t id, setting_details_t **set)
|
|
{
|
|
uint_fast16_t idx, offset = id - normalize_id(id);
|
|
setting_details_t *details = settings_get_details();
|
|
|
|
id -= offset;
|
|
|
|
do {
|
|
for(idx = 0; idx < details->n_settings; idx++) {
|
|
if(details->settings[idx].id == id && is_available(&details->settings[idx])) {
|
|
if(offset && offset >= (details->settings[idx].group == Group_Encoder0 ? hal.encoder.get_n_encoders() : N_AXIS))
|
|
return NULL;
|
|
if(set)
|
|
*set = details;
|
|
return &details->settings[idx];
|
|
}
|
|
}
|
|
details = details->on_get_settings ? details->on_get_settings() : NULL;
|
|
} while(details);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static status_code_t validate_value (const setting_detail_t *setting, float value)
|
|
{
|
|
float val;
|
|
uint_fast8_t set_idx = 0;
|
|
|
|
if(setting->min_value) {
|
|
if(!read_float((char *)setting->min_value, &set_idx, &val))
|
|
return Status_BadNumberFormat;
|
|
|
|
if(value < val)
|
|
return Status_SettingValueOutOfRange;
|
|
|
|
} else if(value < 0.0f)
|
|
return Status_NegativeValue;
|
|
|
|
if (setting->max_value) {
|
|
set_idx = 0;
|
|
|
|
if(!read_float((char *)setting->max_value, &set_idx, &val))
|
|
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')
|
|
return 0;
|
|
|
|
char *p = (char *)s;
|
|
uint32_t entries = 1;
|
|
|
|
while((p = strchr(p, delimiter))) {
|
|
p++;
|
|
entries++;
|
|
}
|
|
|
|
return entries;
|
|
}
|
|
|
|
setting_datatype_t setting_datatype_to_external (setting_datatype_t datatype)
|
|
{
|
|
switch(datatype) {
|
|
|
|
case Format_Int8:
|
|
case Format_Int16:
|
|
datatype = Format_Integer;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return datatype;
|
|
}
|
|
|
|
bool setting_is_list (const setting_detail_t *setting)
|
|
{
|
|
return setting->datatype == Format_Bitfield || setting->datatype == Format_XBitfield || setting->datatype == Format_RadioButtons;
|
|
}
|
|
|
|
static char *remove_element (char *s, uint_fast8_t entry)
|
|
{
|
|
if(entry) {
|
|
while(*s && entry) {
|
|
if(*s == ',' && --entry == 0)
|
|
*s = '\0';
|
|
else
|
|
s++;
|
|
}
|
|
}
|
|
|
|
if(entry == 0) {
|
|
char *s2 = s + 1;
|
|
if(*s2 == ',')
|
|
s2++;
|
|
else while(*s2 && *s2 != ',')
|
|
s2++;
|
|
while(*s2)
|
|
*s++ = *s2++;
|
|
*s = '\0';
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static void setting_remove_element (setting_id_t id, uint_fast8_t pos)
|
|
{
|
|
const setting_detail_t *setting = setting_get_details(id, NULL);
|
|
|
|
if(setting && setting_is_list(setting)) {
|
|
remove_element((char *)setting->format, pos);
|
|
if(setting->min_value)
|
|
remove_element((char *)setting->min_value, pos);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
return !(type == Setting_NonCore || type == Setting_NonCoreFn);
|
|
}
|
|
/*
|
|
static uint32_t get_mask (const char *bits)
|
|
{
|
|
uint32_t mask = 0;
|
|
uint_fast8_t set_idx = 0;
|
|
float value;
|
|
|
|
while(read_float((char *)bits, &set_idx, &value)) {
|
|
mask |= (1 << (uint8_t)value);
|
|
if(bits[set_idx] == ',')
|
|
set_idx++;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
*/
|
|
status_code_t setting_validate_me (const setting_detail_t *setting, float value, char *svalue)
|
|
{
|
|
status_code_t status = Status_OK;
|
|
|
|
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) && /* (setting->min_value
|
|
? (((uint32_t)value & ~get_mask(setting->min_value)) == 0)
|
|
: */ ((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:
|
|
case Format_Decimal:
|
|
status = validate_value(setting, value);
|
|
if(setting->datatype == Format_Integer && status == Status_OK && !isintf(value))
|
|
status = Status_BadNumberFormat;
|
|
break;
|
|
|
|
case Format_String:
|
|
case Format_Password:
|
|
{
|
|
uint_fast16_t len = strlen(svalue);
|
|
status = validate_value(setting, (float)len);
|
|
}
|
|
break;
|
|
|
|
case Format_IPv4:
|
|
// handled by driver or plugin, dependent on network library
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
status_code_t setting_validate (setting_id_t id, float value, char *svalue)
|
|
{
|
|
const setting_detail_t *setting = setting_get_details(id, NULL);
|
|
|
|
// If no details available setting could nevertheless be a valid setting id.
|
|
return setting == NULL ? Status_OK : setting_validate_me(setting, value, svalue);
|
|
}
|
|
|
|
// 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;
|
|
float value = NAN;
|
|
status_code_t status = Status_OK;
|
|
setting_details_t *set;
|
|
const setting_detail_t *setting = setting_get_details(id, &set);
|
|
|
|
if(setting == NULL)
|
|
return Status_SettingDisabled;
|
|
|
|
// Trim leading spaces
|
|
while(*svalue == ' ')
|
|
svalue++;
|
|
|
|
if(!setting_is_string(setting->datatype) && !read_float(svalue, &set_idx, &value) && setting_is_core(setting->type))
|
|
return Status_BadNumberFormat;
|
|
|
|
if((status = setting_validate_me(setting, value, svalue)) != Status_OK) {
|
|
if(setting == Setting_PulseMicroseconds && status == Status_SettingValueOutOfRange)
|
|
status = Status_SettingStepPulseMin;
|
|
|
|
return status;
|
|
}
|
|
|
|
switch(setting->type) {
|
|
|
|
case Setting_NonCore:
|
|
case Setting_IsExtended:
|
|
case Setting_IsLegacy:
|
|
case Setting_IsExpanded:
|
|
switch(setting->datatype) {
|
|
|
|
case Format_Decimal:
|
|
*((float *)(setting->value)) = value;
|
|
break;
|
|
|
|
case Format_String:
|
|
case Format_Password:
|
|
strcpy(((char *)(setting->value)), svalue);
|
|
break;
|
|
|
|
case Format_AxisMask:
|
|
*((uint8_t *)(setting->value)) = (uint8_t)truncf(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);
|
|
break;
|
|
|
|
case Format_Int16:
|
|
*((uint16_t *)(setting->value)) = (uint16_t)truncf(value);
|
|
break;
|
|
|
|
case Format_Integer:
|
|
*((uint32_t *)(setting->value)) = (uint32_t)truncf(value);
|
|
break;
|
|
|
|
default:
|
|
status = Status_BadNumberFormat;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case Setting_NonCoreFn:
|
|
case Setting_IsExtendedFn:
|
|
case Setting_IsLegacyFn:
|
|
case Setting_IsExpandedFn:
|
|
switch(setting->datatype) {
|
|
|
|
case Format_Decimal:
|
|
status = ((setting_set_float_ptr)(setting->value))(id, value);
|
|
break;
|
|
|
|
case Format_String:
|
|
case Format_Password:
|
|
case Format_IPv4:
|
|
status = ((setting_set_string_ptr)(setting->value))(id, svalue);
|
|
break;
|
|
|
|
default:
|
|
status = ((setting_set_int_ptr)(setting->value))(id, (uint_fast16_t)truncf(value));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(status == Status_OK) {
|
|
if(set->save)
|
|
set->save();
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
mc_backlash_init();
|
|
#endif
|
|
if(set->on_changed)
|
|
set->on_changed(&settings);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
// Initialize the config subsystem
|
|
void settings_init (void)
|
|
{
|
|
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
|
|
report_grbl_settings(true, NULL);
|
|
#else
|
|
report_grbl_settings(false, NULL);
|
|
#endif
|
|
} else {
|
|
memset(&tool_table, 0, sizeof(tool_data_t)); // First entry is for tools not in tool table
|
|
#ifdef N_TOOLS
|
|
uint_fast8_t idx;
|
|
for (idx = 1; idx <= N_TOOLS; idx++)
|
|
settings_read_tool_data(idx, &tool_table[idx]);
|
|
#endif
|
|
report_init();
|
|
#ifdef ENABLE_BACKLASH_COMPENSATION
|
|
mc_backlash_init();
|
|
#endif
|
|
hal.settings_changed(&settings);
|
|
if(hal.probe.configure) // Initialize probe invert mask.
|
|
hal.probe.configure(false, false);
|
|
}
|
|
|
|
if(!hal.driver_cap.spindle_pwm_invert)
|
|
setting_remove_element(Setting_SpindleInvertMask, 2);
|
|
|
|
if(!hal.signals_cap.motor_fault)
|
|
setting_remove_element(Setting_ControlInvertMask, 8);
|
|
/* TODO
|
|
if(!hal.signals_cap.probe_disconnected)
|
|
setting_remove_element(Setting_ControlInvertMask, 7);
|
|
|
|
if(!hal.signals_cap.e_stop)
|
|
setting_remove_element(Setting_ControlInvertMask, 6);
|
|
|
|
if(!hal.signals_cap.stop_disable)
|
|
setting_remove_element(Setting_ControlInvertMask, 5);
|
|
|
|
if(!hal.signals_cap.block_delete)
|
|
setting_remove_element(Setting_ControlInvertMask, 4);
|
|
|
|
if(!hal.signals_cap.safety_door_ajar)
|
|
setting_remove_element(Setting_ControlInvertMask, 3);
|
|
*/
|
|
|
|
setting_details_t *details = settings_get_details();
|
|
|
|
while(details->on_get_settings) {
|
|
details = details->on_get_settings();
|
|
if(details->load)
|
|
details->load();
|
|
if(details->on_changed)
|
|
details->on_changed(&settings);
|
|
};
|
|
|
|
settings_get_details()->on_changed = hal.settings_changed;
|
|
}
|