/* 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 . */ /* 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 #include #include #include #include #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