Files
grblHAL/ngc_params.c
Terje Io 16e5df9d93 Marked a large number of non-critical functions with FLASHMEM to save RAM for the iMXRT1062 driver.
Improved alarm handling, if a critical alarm is active when a non-critical alarm is raised the non-critical alarm will be delayed until after reset for the critical alarm.
Improved Modbus exception handling and added high level API call for creating and sending Modbus messages.
Added G65P7 inbuilt macro for interacting with Modbus devices from gcode. See the Wiki for details.
2026-02-15 22:16:03 +01:00

1277 lines
39 KiB
C

/*
ngc_params.c - get/set NGC parameter value by id or name
Part of grblHAL
Copyright (c) 2021-2026 Terje Io
grblHAL 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.
grblHAL 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 grblHAL. If not, see <http://www.gnu.org/licenses/>.
*/
/*
All predefined parameters defined in NIST RS274NGC version 3 (ref section 3.2.1) are implemented.
Most additional predefined parameters defined by LinuxCNC (ref section 5.2.3.1) are implemented.
*/
#include "hal.h"
#if NGC_PARAMETERS_ENABLE
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "system.h"
#include "settings.h"
#include "ngc_params.h"
#include "state_machine.h"
#include "modbus.h"
#ifndef NGC_MAX_CALL_LEVEL
#define NGC_MAX_CALL_LEVEL 10
#endif
typedef float (*ngc_param_get_ptr)(ngc_param_id_t id);
typedef float (*ngc_named_param_get_ptr)(void);
typedef struct {
ngc_param_id_t id_min;
ngc_param_id_t id_max;
ngc_param_get_ptr get;
} ngc_ro_param_t;
typedef struct ngc_rw_param {
void *context;
ngc_param_id_t id;
float value;
struct ngc_rw_param *next;
} ngc_rw_param_t;
typedef struct {
const char *name;
ncg_name_param_id_t id;
ngc_named_param_get_ptr get;
} ngc_named_ro_param_t;
typedef struct ngc_named_rw_param {
void *context;
char name[NGC_MAX_PARAM_LENGTH + 1];
float value;
struct ngc_named_rw_param *next;
} ngc_named_rw_param_t;
typedef struct ngc_string_param {
struct ngc_string_param *next;
ngc_string_id_t id;
uint8_t len;
char value[1];
} ngc_string_param_t;
typedef struct {
uint32_t level;
void *context;
gc_modal_snapshot_t *modal_state;
} ngc_param_context_t;
static int32_t call_level = -1;
static void *call_context;
static gc_modal_snapshot_t *modal_state;
static ngc_param_context_t call_levels[NGC_MAX_CALL_LEVEL];
static ngc_rw_param_t *rw_params = NULL;
static ngc_named_rw_param_t *rw_global_params = NULL;
static ngc_string_id_t ref_id = (uint32_t)-1;
static ngc_string_param_t *ngc_string_params = NULL;
static on_macro_execute_ptr on_macro_execute;
PROGMEM static const uint8_t axis_map[] = {
#ifdef ROTATION_ENABLE
10,
#else
255, // R - XY rotation angle around the Z axis. N/A.
#endif
X_AXIS,
Y_AXIS,
Z_AXIS,
#ifdef A_AXIS
A_AXIS,
#else
255,
#endif
#ifdef B_AXIS
B_AXIS,
#else
255,
#endif
#ifdef C_AXIS
C_AXIS,
#else
255,
#endif
#ifdef U_AXIS
U_AXIS,
#else
255,
#endif
#ifdef V_AXIS
V_AXIS,
#else
255,
#endif
#ifdef W_AXIS
W_AXIS
#else
255
#endif
};
#if N_AXIS > 3
FLASHMEM static float _convert_pos (float value, uint_fast8_t axis)
{
return settings.flags.report_inches && bit_isfalse(settings.steppers.is_rotary.mask, bit(axis)) ? value * 25.4f : value;
}
#else
FLASHMEM static inline float _convert_pos (float value, uint_fast8_t axis)
{
return settings.flags.report_inches ? value * 25.4f : value;
}
#endif
FLASHMEM static float _absolute_pos (uint_fast8_t axis)
{
axis = axis_map[axis];
return _convert_pos(axis <= 9 ? sys.position[axis] / settings.axis[axis].steps_per_mm : 0.0f, axis);
}
FLASHMEM static float _relative_pos (uint_fast8_t axis)
{
axis = axis_map[axis];
return _convert_pos(axis <= 9 ? sys.position[axis] / settings.axis[axis].steps_per_mm - gc_get_offset(axis, false) : 0.0f, axis);
}
// numbered parameters
FLASHMEM static float probe_coord (ngc_param_id_t id)
{
float value = 0.0f;
uint_fast8_t axis = axis_map[(id % 10)];
coord_system_data_t offset;
if(axis <= 9 && (sys.probe_coordsys_id == gc_state.modal.g5x_offset.id || settings_read_coord_data(sys.probe_coordsys_id, &offset)))
value = sys.probe_position[axis] / settings.axis[axis].steps_per_mm -
(sys.probe_coordsys_id == gc_state.modal.g5x_offset.id ? gc_state.modal.g5x_offset.data.coord.values[axis] : offset.coord.values[axis]);
return _convert_pos(value, axis);
}
FLASHMEM static float scaling_factors (ngc_param_id_t id)
{
float *factors = gc_get_scaling();
uint_fast8_t axis = axis_map[(id % 10)];
return axis <= 9 ? factors[axis] : 0.0f;
}
FLASHMEM static float probe_result (ngc_param_id_t id)
{
return sys.flags.probe_succeeded ? 1.0f : 0.0f;
}
/*
static float home_pos (ngc_param_id_t id)
{
uint_fast8_t axis = axis_map[(id % 10)];
return axis <= N_AXIS ? sys.home_position[axis - 1] : 0.0f;
}
*/
FLASHMEM static float m66_result (ngc_param_id_t id)
{
return (float)sys.var5399;
}
FLASHMEM static float tool_number (ngc_param_id_t id)
{
return (float)gc_state.tool->tool_id;
}
FLASHMEM static float tool_offset (ngc_param_id_t id)
{
uint_fast8_t axis = axis_map[(id % 10)];
return axis <= 9 ? gc_state.modal.tool_length_offset[axis] : 0.0f;
}
FLASHMEM static float g28_home (ngc_param_id_t id)
{
float value = 0.0f;
uint_fast8_t axis = axis_map[(id % 10)];
coord_system_data_t offset;
if(axis <= 9 && settings_read_coord_data(CoordinateSystem_G28, &offset))
value = offset.coord.values[axis];
return value;
}
FLASHMEM static float g30_home (ngc_param_id_t id)
{
float value = 0.0f;
uint_fast8_t axis = axis_map[(id % 10)];
coord_system_data_t offset;
if(axis <= 9 && settings_read_coord_data(CoordinateSystem_G30, &offset))
value = offset.coord.values[axis];
return value;
}
FLASHMEM static float coord_system (ngc_param_id_t id)
{
return (float)gc_state.modal.g5x_offset.id + 1;
}
FLASHMEM static float coord_system_offset (ngc_param_id_t id)
{
float value = 0.0f;
uint_fast8_t axis = id % 10;
coord_system_data_t offset;
id = (id - 5220 - axis - (id == 0 ? 10 : 0)) / 20;
axis = axis_map[axis];
#if COMPATIBILITY_LEVEL > 1
if(id <= CoordinateSystem_G59) {
#endif
#ifdef ROTATION_ENABLE
if(axis <= 10 && settings_read_coord_data((coord_system_id_t)id, &offset))
value = axis == 10 ? offset.rotation : offset.coord.values[axis];
#else
if(axis <= 9 && settings_read_coord_data((coord_system_id_t)id, &offset))
value = offset.coord.values[axis];
#endif
#if COMPATIBILITY_LEVEL > 1
}
#endif
return value;
}
FLASHMEM static float g92_offset_applied (ngc_param_id_t id)
{
return (float)gc_state.g92_offset_applied;
}
FLASHMEM static float g92_offset (ngc_param_id_t id)
{
uint_fast8_t axis = axis_map[(id % 10)];
return axis <= 9 ? gc_state.g92_offset.coord.values[axis] : 0.0f;
}
FLASHMEM static float work_position (ngc_param_id_t id)
{
return _relative_pos(id % 10 + 1);
}
FLASHMEM static float debug_output (ngc_param_id_t id)
{
return (float)settings.flags.ngc_debug_out;
}
PROGMEM static const ngc_ro_param_t ngc_ro_params[] = {
{ .id_min = 5061, .id_max = 5069, .get = probe_coord }, // LinuxCNC
{ .id_min = 5070, .id_max = 5070, .get = probe_result }, // LinuxCNC
{ .id_min = 5161, .id_max = 5169, .get = g28_home },
{ .id_min = 5181, .id_max = 5189, .get = g30_home },
{ .id_min = 5191, .id_max = 5199, .get = scaling_factors }, // Mach3
{ .id_min = 5210, .id_max = 5210, .get = g92_offset_applied }, // LinuxCNC
{ .id_min = 5211, .id_max = 5219, .get = g92_offset },
{ .id_min = 5220, .id_max = 5220, .get = coord_system },
{ .id_min = 5221, .id_max = 5230, .get = coord_system_offset }, // G54
{ .id_min = 5241, .id_max = 5250, .get = coord_system_offset }, // G55
{ .id_min = 5261, .id_max = 5270, .get = coord_system_offset }, // G56
{ .id_min = 5281, .id_max = 5290, .get = coord_system_offset }, // G57
{ .id_min = 5301, .id_max = 5310, .get = coord_system_offset }, // G58
{ .id_min = 5321, .id_max = 5330, .get = coord_system_offset }, // G59
{ .id_min = 5341, .id_max = 5350, .get = coord_system_offset }, // G59.1
{ .id_min = 5361, .id_max = 5370, .get = coord_system_offset }, // G59.2
{ .id_min = 5381, .id_max = 5390, .get = coord_system_offset }, // G59.3
{ .id_min = 5399, .id_max = 5399, .get = m66_result }, // LinuxCNC
{ .id_min = 5400, .id_max = 5400, .get = tool_number }, // LinuxCNC
{ .id_min = 5401, .id_max = 5409, .get = tool_offset }, // LinuxCNC
{ .id_min = 5420, .id_max = 5428, .get = work_position }, // LinuxCNC
{ .id_min = 5599, .id_max = 5599, .get = debug_output } // LinuxCNC
};
FLASHMEM bool ngc_param_get (ngc_param_id_t id, float *value)
{
bool found = id > 0 && id < ngc_ro_params[0].id_min;
uint_fast8_t idx = sizeof(ngc_ro_params) / sizeof(ngc_ro_param_t);
*value = 0.0f;
if(found) {
void *context = id > (ngc_param_id_t)30 ? NULL : call_context;
ngc_rw_param_t *rw_param = rw_params;
while(rw_param) {
if(rw_param->context == context && rw_param->id == id) {
*value = rw_param->value;
rw_param = NULL;
} else
rw_param = rw_param->next;
}
} else do {
idx--;
if((found = id >= ngc_ro_params[idx].id_min && id <= ngc_ro_params[idx].id_max))
*value = ngc_ro_params[idx].get(id);
} while(idx && !found);
return found;
}
FLASHMEM bool ngc_param_is_rw (ngc_param_id_t id)
{
return id > 0 && id < ngc_ro_params[0].id_min;
}
FLASHMEM bool ngc_param_exists (ngc_param_id_t id)
{
return id > 0 && id <= ngc_ro_params[(sizeof(ngc_ro_params) / sizeof(ngc_ro_param_t)) - 1].id_max;
}
FLASHMEM bool ngc_param_set (ngc_param_id_t id, float value)
{
bool ok = id > 0 && id < ngc_ro_params[0].id_min;
if(ok) {
void *context = id > (ngc_param_id_t)30 ? NULL : call_context;
ngc_rw_param_t *rw_param = rw_params, *rw_param_last = rw_params;
while(rw_param) {
if(rw_param->context == context && rw_param->id == id) {
break;
} else {
rw_param_last = rw_param;
rw_param = rw_param->next;
}
}
if(rw_param == NULL && value != 0.0f && (rw_param = malloc(sizeof(ngc_rw_param_t)))) {
rw_param->id = id;
rw_param->context = context;
rw_param->next = NULL;
if(rw_params == NULL)
rw_params = rw_param;
else
rw_param_last->next = rw_param;
}
if(rw_param)
rw_param->value = value;
else
ok = value == 0.0f;
}
return ok;
}
PROGMEM static const ngc_named_ro_param_t ngc_named_ro_param[] = {
{ .name = "_vmajor", .id = NGCParam_vmajor },
{ .name = "_vminor", .id = NGCParam_vminor },
{ .name = "_line", .id = NGCParam_line },
{ .name = "_motion_mode", .id = NGCParam_motion_mode },
{ .name = "_plane", .id = NGCParam_plane },
{ .name = "_ccomp", .id = NGCParam_ccomp },
{ .name = "_metric", .id = NGCParam_metric },
{ .name = "_imperial", .id = NGCParam_imperial },
{ .name = "_absolute", .id = NGCParam_absolute },
{ .name = "_incremental", .id = NGCParam_incremental },
{ .name = "_inverse_time", .id = NGCParam_inverse_time },
{ .name = "_units_per_minute", .id = NGCParam_units_per_minute },
{ .name = "_units_per_rev", .id = NGCParam_units_per_rev },
{ .name = "_coord_system", .id = NGCParam_coord_system },
{ .name = "_tool_offset", .id = NGCParam_tool_offset },
{ .name = "_retract_r_plane", .id = NGCParam_retract_r_plane },
{ .name = "_retract_old_z", .id = NGCParam_retract_old_z },
{ .name = "_spindle_rpm_mode", .id = NGCParam_spindle_rpm_mode },
{ .name = "_spindle_css_mode", .id = NGCParam_spindle_css_mode },
{ .name = "_ijk_absolute_mode", .id = NGCParam_ijk_absolute_mode },
{ .name = "_lathe_diameter_mode", .id = NGCParam_lathe_diameter_mode },
{ .name = "_lathe_radius_mode", .id = NGCParam_lathe_radius_mode },
{ .name = "_spindle_on", .id = NGCParam_spindle_on },
{ .name = "_spindle_cw", .id = NGCParam_spindle_cw },
{ .name = "_mist", .id = NGCParam_mist },
{ .name = "_flood", .id = NGCParam_flood },
{ .name = "_speed_override", .id = NGCParam_speed_override },
{ .name = "_feed_override", .id = NGCParam_feed_override },
{ .name = "_adaptive_feed", .id = NGCParam_adaptive_feed },
{ .name = "_feed_hold", .id = NGCParam_feed_hold },
{ .name = "_feed", .id = NGCParam_feed },
{ .name = "_rpm", .id = NGCParam_rpm },
{ .name = "_x", .id = NGCParam_x },
{ .name = "_y", .id = NGCParam_y },
{ .name = "_z", .id = NGCParam_z },
{ .name = "_a", .id = NGCParam_a },
{ .name = "_b", .id = NGCParam_b },
{ .name = "_c", .id = NGCParam_c },
{ .name = "_u", .id = NGCParam_u },
{ .name = "_v", .id = NGCParam_v },
{ .name = "_w", .id = NGCParam_w },
{ .name = "_abs_x", .id = NGCParam_abs_x },
{ .name = "_abs_y", .id = NGCParam_abs_y },
{ .name = "_abs_z", .id = NGCParam_abs_z },
{ .name = "_abs_a", .id = NGCParam_abs_a },
{ .name = "_abs_b", .id = NGCParam_abs_b },
{ .name = "_abs_c", .id = NGCParam_abs_c },
{ .name = "_abs_u", .id = NGCParam_abs_u },
{ .name = "_abs_v", .id = NGCParam_abs_v },
{ .name = "_abs_w", .id = NGCParam_abs_w },
{ .name = "_current_tool", .id = NGCParam_current_tool },
{ .name = "_current_pocket", .id = NGCParam_current_pocket },
{ .name = "_selected_tool", .id = NGCParam_selected_tool },
{ .name = "_selected_pocket", .id = NGCParam_selected_pocket },
{ .name = "_call_level", .id = NGCParam_call_level },
{ .name = "_probe_state", .id = NGCParam_probe_state },
{ .name = "_probe2_state", .id = NGCParam_probe2_state },
{ .name = "_toolsetter_state", .id = NGCParam_toolsetter_state },
{ .name = "_active_probe", .id = NGCParam_active_probe },
{ .name = "_homed_state", .id = NGCParam_homed_state },
{ .name = "_homed_axes", .id = NGCParam_homed_axes },
{ .name = "_tool_table_size", .id = NGCParam_tool_table_size },
{ .name = "_free_memory", .id = NGCParam_free_memory }
};
// Named parameters
FLASHMEM float ngc_named_param_get_by_id (ncg_name_param_id_t id)
{
float value;
switch(id) {
case NGCParam_vmajor:
value = 1.1f;
break;
case NGCParam_vminor:
value = (float)(GRBL_BUILD - 20000000);
break;
case NGCParam_line:
value = (float)gc_state.line_number;
break;
case NGCParam_motion_mode:
value = (float)(gc_state.modal.motion * 10); // TODO: Fix G38.x
break;
case NGCParam_plane:
value = (float)(170 + gc_state.modal.plane_select * 10);
break;
case NGCParam_ccomp:
value = 400.0f;
break;
case NGCParam_metric:
value = gc_state.modal.units_imperial ? 0.0f : 1.0f;
break;
case NGCParam_imperial:
value = gc_state.modal.units_imperial ? 1.0f : 0.0f;
break;
case NGCParam_absolute:
value = gc_state.modal.distance_incremental ? 0.0f : 1.0f;
break;
case NGCParam_incremental:
value = gc_state.modal.distance_incremental ? 1.0f : 0.0f;
break;
case NGCParam_inverse_time:
value = gc_state.modal.feed_mode == FeedMode_InverseTime ? 1.0f : 0.0f;
break;
case NGCParam_units_per_minute:
value = gc_state.modal.feed_mode == FeedMode_UnitsPerMin ? 1.0f : 0.0f;
break;
case NGCParam_units_per_rev:
value = gc_state.modal.feed_mode == FeedMode_UnitsPerRev ? 1.0f : 0.0f;
break;
case NGCParam_coord_system:
{
uint_fast16_t id = gc_state.modal.g5x_offset.id * 10;
if(id > (CoordinateSystem_G59 * 10))
id = (CoordinateSystem_G59 * 10) + gc_state.modal.g5x_offset.id - CoordinateSystem_G59;
value = (float)(540 + id);
}
break;
case NGCParam_tool_offset:
value = gc_state.modal.tool_offset_mode >= ToolLengthOffset_Enable ? 1.0f : 0.0f;
break;
case NGCParam_retract_r_plane:
value = gc_state.modal.retract_mode == CCRetractMode_Previous ? 1.0f : 0.0f;
break;
case NGCParam_retract_old_z:
value = gc_state.modal.retract_mode == CCRetractMode_RPos ? 1.0f : 0.0f;
break;
case NGCParam_spindle_rpm_mode:
value = gc_spindle_get(0)->rpm_mode == SpindleSpeedMode_RPM ? 1.0f : 0.0f;
break;
case NGCParam_spindle_css_mode:
value = gc_spindle_get(0)->rpm_mode == SpindleSpeedMode_CSS ? 1.0f : 0.0f;
break;
case NGCParam_ijk_absolute_mode:
value = 0.0f;
break;
case NGCParam_lathe_diameter_mode:
value = gc_state.modal.diameter_mode ? 1.0f : 0.0f;
break;
case NGCParam_lathe_radius_mode:
value = gc_state.modal.diameter_mode ? 0.0f : 1.0f;
break;
case NGCParam_spindle_on:
value = gc_spindle_get(0)->state.on ? 1.0f : 0.0f;
break;
case NGCParam_spindle_cw:
value = gc_spindle_get(0)->state.ccw ? 1.0f : 0.0f;
break;
case NGCParam_mist:
value = gc_state.modal.coolant.mist ? 1.0f : 0.0f;
break;
case NGCParam_flood:
value = gc_state.modal.coolant.flood ? 1.0f : 0.0f;
break;
case NGCParam_speed_override:
value = gc_state.modal.override_ctrl.spindle_rpm_disable ? 0.0f : 1.0f;
break;
case NGCParam_feed_override:
value = gc_state.modal.override_ctrl.feed_rates_disable ? 0.0f : 1.0f;
break;
case NGCParam_adaptive_feed:
value = 0.0f;
break;
case NGCParam_feed_hold:
value = gc_state.modal.override_ctrl.feed_hold_disable ? 0.0f : 1.0f;
break;
case NGCParam_feed:
value = gc_state.feed_rate;
break;
case NGCParam_rpm:
value = gc_spindle_get(0)->rpm;
break;
case NGCParam_x:
//no break
case NGCParam_y:
//no break
case NGCParam_z:
//no break
case NGCParam_a:
//no break
case NGCParam_b:
//no break
case NGCParam_c:
//no break
case NGCParam_u:
//no break
case NGCParam_v:
//no break
case NGCParam_w:
value = _relative_pos(id - NGCParam_x + 1);
break;
case NGCParam_abs_x:
//no break
case NGCParam_abs_y:
//no break
case NGCParam_abs_z:
//no break
case NGCParam_abs_a:
//no break
case NGCParam_abs_b:
//no break
case NGCParam_abs_c:
//no break
case NGCParam_abs_u:
//no break
case NGCParam_abs_v:
//no break
case NGCParam_abs_w:
value = _absolute_pos(id - NGCParam_abs_x + 1);
break;
case NGCParam_current_tool:
value = (float)gc_state.tool->tool_id;
break;
case NGCParam_current_pocket:
value = (float)grbl.tool_table.get_tool(gc_state.tool->tool_id)->pocket;
break;
case NGCParam_selected_tool:
value = gc_state.tool_pending != gc_state.tool->tool_id ? (float)gc_state.tool_pending : -1.0f;
break;
case NGCParam_selected_pocket:
value = gc_state.tool_pending != gc_state.tool->tool_id ? (float)grbl.tool_table.get_tool(gc_state.tool_pending)->pocket : -1.0f;
break;
case NGCParam_call_level:
value = (float)ngc_call_level();
break;
// grblHAL extensions
case NGCParam_probe_state:
value = hal.driver_cap.probe && hal.probe.is_triggered ? (float)hal.probe.is_triggered(Probe_Default) : -1.0f;
break;
case NGCParam_probe2_state:
value = hal.driver_cap.probe2 && hal.probe.is_triggered ? (float)hal.probe.is_triggered(Probe_2) : -1.0f;
break;
case NGCParam_toolsetter_state:
value = hal.driver_cap.toolsetter && hal.probe.is_triggered ? (float)hal.probe.is_triggered(Probe_Toolsetter) : -1.0f;
break;
case NGCParam_active_probe:
value = hal.probe.get_state ? (float)hal.probe.get_state().probe_id : -1.0f;
break;
case NGCParam_homed_state:
if(sys.homing.mask || settings.homing.flags.single_axis_commands || settings.homing.flags.manual) {
axes_signals_t homing = { sys.homing.mask ? sys.homing.mask : AXES_BITMASK };
value = (homing.mask & sys.homed.mask) == homing.mask ? 1.0f : 0.0f;
} else
value = 0.0f;
break;
case NGCParam_homed_axes:
value = (float)sys.homed.mask;
break;
case NGCParam_tool_table_size:
value = (float)grbl.tool_table.n_tools;
break;
case NGCParam_free_memory:
value = hal.get_free_mem ? (float)hal.get_free_mem() / 1024.0f : -1.0f;
break;
default:
value = NAN;
}
return value;
}
// Lowercase name, remove control characters and spaces
FLASHMEM static char *ngc_name_tolower (char *s)
{
static char name[NGC_MAX_PARAM_LENGTH + 1];
uint_fast8_t len = 0;
char c, *s1 = s, *s2 = name;
while((c = *s1++) && len <= NGC_MAX_PARAM_LENGTH) {
if(c > ' ') {
*s2++ = LCAPS(c);
len++;
}
}
*s2 = '\0';
return name;
}
FLASHMEM bool ngc_named_param_get (char *name, float *value)
{
bool found = false;
uint_fast8_t idx = sizeof(ngc_named_ro_param) / sizeof(ngc_named_ro_param_t);
name = ngc_name_tolower(name);
// Check if name is supplied, return false if not.
if((*name == '_' ? *(name + 1) : *name) == '\0')
return false;
*value = 0.0f;
if(*name == '_') do {
idx--;
if((found = !strcmp(name, ngc_named_ro_param[idx].name)))
*value = ngc_named_param_get_by_id(ngc_named_ro_param[idx].id);
} while(idx && !found);
if(!found) {
void *context = *name == '_' ? NULL : call_context;
ngc_named_rw_param_t *rw_param = rw_global_params;
while(rw_param && !found) {
if((found = rw_param->context == context && !strcmp(rw_param->name, name)))
*value = rw_param->value;
else
rw_param = rw_param->next;
}
}
return found;
}
FLASHMEM bool ngc_named_param_exists (char *name)
{
float value;
return ngc_named_param_get(name, &value);
}
FLASHMEM float *ngc_named_param_set (char *name, float value)
{
bool ok = false;
uint_fast8_t idx = sizeof(ngc_named_ro_param) / sizeof(ngc_named_ro_param_t);
name = ngc_name_tolower(name);
// Check if name is supplied, return false if not.
if((*name == '_' ? *(name + 1) : *name) == '\0')
return NULL;
ngc_named_rw_param_t *rw_param = NULL;
// Check if it is a (read only) predefined parameter.
if(*name == '_') do {
idx--;
ok = !strcmp(name, ngc_named_ro_param[idx].name);
} while(idx && !ok);
// If not predefined attempt to set it.
if(!ok && (ok = strlen(name) <= NGC_MAX_PARAM_LENGTH)) {
void *context = *name == '_' ? NULL : call_context;
ngc_named_rw_param_t *rw_param_last = rw_global_params;
rw_param = rw_global_params;
while(rw_param) {
if(rw_param->context == context && !strcmp(rw_param->name, name)) {
break;
} else {
rw_param_last = rw_param;
rw_param = rw_param->next;
}
}
if(rw_param == NULL && (rw_param = malloc(sizeof(ngc_named_rw_param_t)))) {
strcpy(rw_param->name, name);
rw_param->context = context;
rw_param->next = NULL;
if(rw_global_params == NULL)
rw_global_params = rw_param;
else
rw_param_last->next = rw_param;
}
if((ok = rw_param != NULL))
rw_param->value = value;
}
return ok ? &rw_param->value : NULL;
}
FLASHMEM static ngc_string_param_t *sp_get_by_name (char *name)
{
ngc_string_param_t *sr = ngc_string_params;
if(sr) do {
if(sr->id > NGC_MAX_PARAM_ID && !(strcmp(sr->value, name)))
break;
} while((sr = sr->next));
return sr;
}
FLASHMEM static ngc_string_param_t *sp_set (ngc_string_id_t id, char *value)
{
size_t len;
ngc_string_param_t *last = NULL, *sp;
if((sp = ngc_string_params)) do {
if(sp->id == id)
break;
last = sp;
} while((sp = sp->next));
if((len = strlen(value)) <= 255) {
if(sp == NULL || len > sp->len) {
ngc_string_param_t *sp_org = sp;
if((sp = realloc(sp, sizeof(ngc_string_param_t) + len))) {
if(sp_org == NULL) {
sp->id = id;
sp->next = NULL;
}
sp->len = len;
if(last == NULL) {
ngc_string_params = sp;
} else {
if(sp_org == NULL)
last->next = sp;
else if(sp_org != sp) {
ngc_string_param_t *sr = ngc_string_params;
do {
if(sr->next == sp_org) {
sr->next = sp;
break;
}
} while((sr = sr->next));
}
}
} else if(sp_org)
ngc_string_param_delete(sp_org->id);
}
if(sp)
strcpy(sp->value, value);
} else if(sp) {
ngc_string_param_delete(sp->id);
sp = NULL;
}
return sp;
}
FLASHMEM char *ngc_string_param_get (ngc_string_id_t id)
{
ngc_string_param_t *sp;
if((sp = ngc_string_params)) do {
if(sp->id == id)
break;
} while((sp = sp->next));
return sp ? sp->value : NULL;
}
FLASHMEM bool ngc_string_param_exists (ngc_string_id_t id)
{
return !!ngc_string_param_get(id);
}
FLASHMEM bool ngc_string_param_set (ngc_param_id_t id, char *value)
{
return id > 0 && !!sp_set((ngc_string_id_t)id, value);
}
FLASHMEM ngc_string_id_t ngc_string_param_set_name (char *name)
{
ngc_string_param_t *sr = *name ? sp_get_by_name(name) : NULL;
if(*name && sr == NULL)
sr = sp_set(--ref_id, name);
return sr ? sr->id : 0;
}
FLASHMEM void ngc_string_param_delete (ngc_string_id_t id)
{
ngc_string_param_t *sr = ngc_string_params, *rm;
if(sr) {
if(sr->id == id) {
rm = sr;
ngc_string_params = sr->next;
free(rm);
} else do {
if(sr->next && sr->next->id == id) {
rm = sr->next;
sr->next = sr->next->next;
free(rm);
break;
}
} while((sr = sr->next));
}
}
FLASHMEM bool ngc_modal_state_save (gc_modal_t *state, gc_override_values_t *overrides, float feed_rate, bool auto_restore)
{
gc_modal_snapshot_t **saved_state = call_level == -1 ? &modal_state : &call_levels[call_level].modal_state;
if(*saved_state == NULL)
*saved_state = malloc(sizeof(gc_modal_snapshot_t));
if(*saved_state) {
memcpy(&(*saved_state)->modal, state, sizeof(gc_modal_t));
memcpy(&(*saved_state)->override, overrides, sizeof(gc_override_values_t));
(*saved_state)->modal.feed_rate = feed_rate;
(*saved_state)->modal.auto_restore = auto_restore;
}
return *saved_state != NULL;
}
FLASHMEM void ngc_modal_state_invalidate (void)
{
gc_modal_snapshot_t **saved_state = call_level == -1 ? &modal_state : &call_levels[call_level].modal_state;
if(*saved_state) {
free(*saved_state);
*saved_state = NULL;
}
}
FLASHMEM gc_modal_snapshot_t *ngc_modal_state_get (void)
{
return call_level == -1 ? modal_state : call_levels[call_level].modal_state;
}
FLASHMEM bool ngc_modal_state_restore (void)
{
return gc_modal_state_restore(call_level == -1 ? modal_state : call_levels[call_level].modal_state);
}
FLASHMEM bool ngc_call_push (void *context)
{
bool ok;
if((ok = call_level < (NGC_MAX_CALL_LEVEL - 1)))
call_levels[++call_level].context = call_context = context;
return ok;
}
FLASHMEM bool ngc_call_pop (void)
{
if(call_level >= 0) {
if(call_context) {
ngc_rw_param_t *rw_param = rw_params, *rw_param_last = rw_params;
while(rw_param) {
if(rw_param->context == call_context) {
ngc_rw_param_t *rw_param_free = rw_param;
rw_param = rw_param->next;
if(rw_param_free == rw_params)
rw_params = rw_param_last = rw_param;
else
rw_param_last->next = rw_param;
free(rw_param_free);
} else {
rw_param_last = rw_param;
rw_param = rw_param->next;
}
}
ngc_named_rw_param_t *rw_named_param = rw_global_params, *rw_named_param_last = rw_global_params;
while(rw_named_param) {
if(rw_named_param->context == call_context) {
ngc_named_rw_param_t *rw_named_param_free = rw_named_param;
rw_named_param = rw_named_param->next;
if(rw_named_param_free == rw_global_params)
rw_global_params = rw_named_param_last = rw_named_param;
else
rw_named_param_last->next = rw_named_param;
free(rw_named_param_free);
} else {
rw_named_param_last = rw_named_param;
rw_named_param = rw_named_param->next;
}
}
}
if(call_levels[call_level].modal_state) {
if(call_levels[call_level].modal_state->modal.auto_restore)
gc_modal_state_restore(call_levels[call_level].modal_state);
free(call_levels[call_level].modal_state);
call_levels[call_level].modal_state = NULL;
}
call_context = --call_level >= 0 ? call_levels[call_level].context : NULL;
}
return call_level >= 0;
}
FLASHMEM uint_fast8_t ngc_call_level (void)
{
return (uint_fast8_t)(call_level + 1);
}
FLASHMEM uint8_t ngc_float_decimals (void)
{
return settings.flags.report_inches ? N_DECIMAL_COORDVALUE_INCH : N_DECIMAL_COORDVALUE_MM;
}
FLASHMEM static status_code_t macro_set_get_setting (parameter_words_t args)
{
float setting_id;
status_code_t status = Status_OK;
const setting_detail_t *setting;
if(!args.q)
status = Status_GcodeValueWordMissing;
else if(ngc_param_get(17 /* Q word */, &setting_id) && (setting = setting_get_details((setting_id_t)setting_id, NULL))) {
uint_fast8_t offset = (setting_id_t)setting_id - setting->id;
bool is_numeric = setting->datatype == Format_Decimal || setting_is_integer(setting) || setting_is_list(setting);
if(args.s && is_numeric) {
float new_value;
if(ngc_param_get(19 /* S word */, &new_value))
status = settings_store_setting(setting->id + offset, setting->datatype == Format_Decimal ? ftoa(new_value, 6) : uitoa((uint32_t)new_value));
}
if(status == Status_OK) {
if(setting->datatype == Format_Decimal) {
ngc_named_param_set("_value", setting_get_float_value(setting, offset));
ngc_named_param_set("_value_returned", 1.0f);
} else if(is_numeric) {
ngc_named_param_set("_value", (float)setting_get_int_value(setting, offset));
ngc_named_param_set("_value_returned", 1.0f);
}
}
} else
status = Status_GcodeValueOutOfRange;
return status;
}
FLASHMEM static status_code_t macro_ngc_parameter_rw (parameter_words_t args)
{
float idx, value;
status_code_t status = Status_OK;
if(!args.i)
status = Status_GcodeValueWordMissing;
else if(ngc_param_get(4 /* I word */, &idx)) {
if(args.q) {
if(!(ngc_param_get(17 /* Q word */, &value) && ngc_param_set((ngc_param_id_t)idx, value)))
status = Status_GcodeValueOutOfRange;
} else if(ngc_param_get((ngc_param_id_t)idx, &value)) {
if(args.s) {
if(!(ngc_param_get(19 /* S word */, &idx) && ngc_param_set((ngc_param_id_t)idx, value)))
status = Status_GcodeValueOutOfRange;
} else {
ngc_named_param_set("_value", value);
ngc_named_param_set("_value_returned", 1.0f);
}
} else
status = Status_GcodeValueOutOfRange;
} else
status = Status_GcodeValueOutOfRange;
return status;
}
FLASHMEM static status_code_t macro_get_machine_state (parameter_words_t args)
{
ngc_named_param_set("_value", (float)ffs(state_get()));
ngc_named_param_set("_value_returned", 1.0f);
return Status_OK;
}
FLASHMEM static status_code_t macro_select_probe (parameter_words_t args)
{
float probe_id;
status_code_t status = Status_OK;
if(!args.q)
status = Status_GcodeValueWordMissing;
else if(ngc_param_get(17 /* Q word */, &probe_id) && (hal.probe.select ? hal.probe.select((probe_id_t)probe_id) : probe_id == 0.0f))
report_add_realtime(Report_ProbeId);
else
status = Status_GcodeValueOutOfRange;
return status;
}
FLASHMEM static status_code_t macro_get_tool_offset (parameter_words_t args)
{
float tool_id, axis_id;
status_code_t status = Status_OK;
if(!(args.q && args.r))
status = Status_GcodeValueWordMissing;
else if(grbl.tool_table.n_tools && ngc_param_get(17 /* Q word */, &tool_id) && ngc_param_get(18 /* R word */, &axis_id)) {
tool_data_t *tool_data = grbl.tool_table.get_tool((tool_id_t)tool_id)->data;
if(tool_data && (uint8_t)axis_id < N_AXIS) {
ngc_named_param_set("_value", tool_data->offset.values[(uint8_t)axis_id]);
ngc_named_param_set("_value_returned", 1.0f);
} else
status = Status_GcodeIllegalToolTableEntry;
} else
status = Status_GcodeIllegalToolTableEntry;
return status;
}
static void modbus_response_handler (modbus_response_t *response)
{
ngc_named_param_set("_value", (float)(response->exception ? response->exception : response->values[0]));
ngc_named_param_set("_value_returned", response->exception ? 0.0f : (float)response->num_values);
if(response->exception == ModBus_NoException) {
if(response->num_values == 2)
ngc_named_param_set("_value2", (float)(response->exception ? response->exception : response->values[1]));
if(response->num_values == 3)
ngc_named_param_set("_value2", (float)(response->exception ? response->exception : response->values[2]));
}
}
FLASHMEM static status_code_t macro_modbus_msg (parameter_words_t args)
{
float tmpvar;
status_code_t status = Status_OK;
if(!(args.f && args.s))
status = Status_GcodeValueWordMissing;
else if(ngc_param_get(9 /* F word -> function */, &tmpvar)) {
uint16_t server, address = 0, n_values = 0, values[MODBUS_MAX_REGISTERS];
const modbus_function_properties_t *p = modbus_get_function_properties((modbus_function_t)tmpvar);
if(p->function != ModBus_ReadExceptionStatus && p->function && args.r && ngc_param_get(18 /* R word - register address */, &tmpvar)) {
address = (uint16_t)tmpvar;
if(p->is_write) {
if(args.a && ngc_param_get(1 /* A word - first register value */, &tmpvar))
values[n_values++] = (uint16_t)tmpvar;
if(!p->single_register) {
if(args.b && ngc_param_get(2 /* B word - second register value */, &tmpvar))
values[n_values++] = (uint16_t)tmpvar;
if(args.c && ngc_param_get(3 /* C word - third register value */, &tmpvar))
values[n_values++] = (uint16_t)tmpvar;
}
} else if(args.x && ngc_param_get(24 /* X word - number of registers to read */, &tmpvar)) {
if((n_values = (uint16_t)tmpvar) > MODBUS_MAX_REGISTERS)
return Status_GcodeValueOutOfRange;
} else
n_values = 1;
}
if((p->function == ModBus_ReadExceptionStatus || n_values) &&
args.s && ngc_param_get(19 /* S word - server address */, &tmpvar)) {
server = (uint16_t)tmpvar;
status = modbus_message(server, p->function, address, values, n_values, modbus_response_handler);
} else
status = p->function ? Status_GcodeValueWordMissing : Status_GcodeUnsupportedCommand;
} else
status = Status_GcodeUnsupportedCommand;
return status;
}
FLASHMEM static status_code_t onMacroExecute (macro_id_t macro_id, parameter_words_t args, uint32_t repeats)
{
status_code_t status = repeats > 1 && macro_id >= 1 && macro_id <= G65Macro_LastInbuilt
? Status_GcodeValueOutOfRange
: Status_Unhandled;
if(status == Status_Unhandled) switch((g65_inbuilt_t)macro_id) {
case G65Macro_GetSetting:
status = macro_set_get_setting(args);
break;
case G65Macro_GetToolOffset:
status = macro_get_tool_offset(args);
break;
case G65Macro_ParameterRW:
status = macro_ngc_parameter_rw(args);
break;
case G65Macro_GetMachineState:
status = macro_get_machine_state(args);
break;
case G65Macro_SelectProbe:
status = macro_select_probe(args);
break;
case G65Macro_SpindleDelayDisable:
sys.override.control.spindle_wait_disable = On;
status = Status_OK;
break;
case G65Macro_ModbusMessage:
status = modbus_isup().ok ? macro_modbus_msg(args) : Status_GcodeUnsupportedCommand;
break;
}
return status == Status_Unhandled && on_macro_execute ? on_macro_execute(macro_id, args, repeats) : status;
}
FLASHMEM void ngc_params_init (void)
{
static bool init_ok = false;
if(!init_ok) {
init_ok = true;
on_macro_execute = grbl.on_macro_execute;
grbl.on_macro_execute = onMacroExecute;
}
ngc_modal_state_invalidate();
}
#endif // NGC_PARAMETERS_ENABLE