diff --git a/README.md b/README.md index 99c77e3..e1f79c2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ It has been written to complement grblHAL and has features such as proper keyboa --- -Latest build date is 20241023, see the [changelog](changelog.md) for details. +Latest build date is 20241025, see the [changelog](changelog.md) for details. __NOTE:__ Build 20240222 has moved the probe input to the ioPorts pool of inputs and will be allocated from it when configured. The change is major and _potentially dangerous_, it may damage your probe, so please _verify correct operation_ after installing this, or later, builds. diff --git a/changelog.md b/changelog.md index 82354cd..4b1d6e9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,22 +1,49 @@ ## grblHAL changelog +Build 20241025 + +Core: + +* Changed `_vminor` named parameter to return build date in YYMMDD format, previously value was 0. + +* Added support for LinuxCNC style `(ABORT,)` comment, requires expressions enabled. +Terminates gcode program, outputs message and returns error 253. + +* Added `PRM[]` and `PRM[,]` functions to expressions, returns $-setting value or value of bit in integer type setting. + +* "hardened" flow control code, fixed bug in `repeat...continue` handling. + +* Changed signature of `grbl.on_gcode_comment` event, now returns status code. + +Drivers: + +* STM32F4xx: fixed typos. + +* Some: added note to _platformio.ino_ file. + +Plugins: + +* SD Card, macros: fixed G65P1 settings read macro when reading some indexed settings. + +--- + Build 20241023 Core: * Fixed some odd bugs in NGC flow control, prepared for file based named O-call subroutines. -* Fixed incorrect comment string passed to passed to `grbl.on_gcode_comment` event. +* Fixed incorrect comment string passed to `grbl.on_gcode_comment` event. * Added generic redirector for temporarily changing input stream to read from a file. Supports nesting. Drivers: -*ESP32: fix for overriding UART0 pins, reverted and fixed tests for ESP32-S3 conditional code. +* ESP32: fix for overriding UART0 pins, reverted and fixed tests for ESP32-S3 conditional code. Plugins: -* File system macros: updated to use new input stream redirector, allows nesting of `G65` calls +* SD Card, macros: updated to use new input stream redirector, allows nesting of `G65` calls \(max 5 levels depending on available memory\). __NOTE:__ Not extensively tested, feedback required. * SD card: updated to work alongside new file redirector. diff --git a/core_handlers.h b/core_handlers.h index 4238145..e4e138b 100644 --- a/core_handlers.h +++ b/core_handlers.h @@ -118,7 +118,7 @@ typedef void (*on_reset_ptr)(void); typedef void (*on_jog_cancel_ptr)(sys_state_t state); typedef bool (*on_spindle_select_ptr)(spindle_ptrs_t *spindle); typedef void (*on_spindle_selected_ptr)(spindle_ptrs_t *spindle); -typedef void (*on_gcode_message_ptr)(char *msg); +typedef status_code_t (*on_gcode_message_ptr)(char *msg); typedef void (*on_rt_reports_added_ptr)(report_tracking_flags_t report); typedef const char *(*on_set_axis_setting_unit_ptr)(setting_id_t setting_id, uint_fast8_t axis_idx); typedef status_code_t (*on_file_open_ptr)(const char *fname, vfs_file_t *handle, bool stream); diff --git a/errors.c b/errors.c index 4e9ab08..56a3335 100644 --- a/errors.c +++ b/errors.c @@ -3,22 +3,22 @@ Part of grblHAL - Copyright (c) 2017-2023 Terje Io + Copyright (c) 2017-2024 Terje Io Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud - Grbl is free software: you can redistribute it and/or modify + 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. - Grbl is distributed in the hope that it will be useful, + 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 + 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 . + along with grblHAL. If not, see . */ #include @@ -102,8 +102,9 @@ PROGMEM static const status_detail_t status_detail[] = { { Status_FlowControlNotExecutingMacro, "Flow statement only allowed in filesystem macro." }, { Status_FlowControlSyntaxError, "Unknown flow statement." }, { Status_FlowControlStackOverflow, "Stack overflow while executing flow statement." }, - { Status_FlowControlOutOfMemory, "Out of memory while executing flow statement." } + { Status_FlowControlOutOfMemory, "Out of memory while executing flow statement." }, #endif + { Status_UserException, "User defined error occured." } #endif // NO_SETTINGS_DESCRIPTIONS }; diff --git a/errors.h b/errors.h index 79299bb..78aba0c 100644 --- a/errors.h +++ b/errors.h @@ -3,22 +3,22 @@ Part of grblHAL - Copyright (c) 2017-2023 Terje Io + Copyright (c) 2017-2024 Terje Io Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud - Grbl is free software: you can redistribute it and/or modify + 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. - Grbl is distributed in the hope that it will be useful, + 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 + 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 . + along with grblHAL. If not, see . */ #ifndef _ERRORS_H_ @@ -26,7 +26,7 @@ #include -// Define Grbl status codes. Valid values (0-255) +// Define grblHAL status codes. Valid values (0-255) typedef enum { Status_OK = 0, Status_ExpectedCommandLetter = 1, @@ -112,10 +112,10 @@ typedef enum { Status_FlowControlSyntaxError = 81, Status_FlowControlStackOverflow = 82, Status_FlowControlOutOfMemory = 83, - + Status_StatusMax = Status_FlowControlOutOfMemory, + Status_UserException = 253, Status_Handled, // For internal use only - Status_Unhandled, // For internal use only - Status_StatusMax = Status_Unhandled + Status_Unhandled // For internal use only } __attribute__ ((__packed__)) status_code_t; typedef struct { diff --git a/gcode.c b/gcode.c index 9b29cfd..5936e92 100644 --- a/gcode.c +++ b/gcode.c @@ -557,7 +557,7 @@ bool gc_modal_state_restore (gc_modal_t *copy) // If the driver handles message comments then the first is extracted and returned in a dynamically // allocated memory block, the caller must free this after the message has been processed. -char *gc_normalize_block (char *block, char **message) +char *gc_normalize_block (char *block, status_code_t *status, char **message) { char c, *s1, *s2, *comment = NULL; @@ -608,12 +608,8 @@ char *gc_normalize_block (char *block, char **message) comment += 6; ngc_substitute_parameters(comment, message); *comment = '\0'; // Do not generate grbl.on_gcode_comment event! - } else if(!strncasecmp(comment, "MSG,", 4)) { - comment += 4; - ngc_substitute_parameters(comment, message); - } - } -#else + } else { +#endif size_t len = s1 - comment - 3; if(!strncasecmp(comment, "MSG,", 4) && (*message = malloc(len))) { @@ -625,12 +621,13 @@ char *gc_normalize_block (char *block, char **message) } memcpy(*message, comment, len); } - } +#if NGC_EXPRESSIONS_ENABLE + } #endif + } } - if(*comment && *message == NULL && grbl.on_gcode_comment) - grbl.on_gcode_comment(comment); + *status = grbl.on_gcode_comment(comment); } comment = NULL; break; @@ -773,6 +770,7 @@ status_code_t gc_execute_block (char *block) #endif char *message = NULL; + status_code_t status = Status_OK; struct { float f; uint32_t o; @@ -780,12 +778,15 @@ status_code_t gc_execute_block (char *block) tool_id_t t; } single_meaning_value = {0}; - block = gc_normalize_block(block, &message); + block = gc_normalize_block(block, &status, &message); + + if(status != Status_OK) + FAIL(status); if(block[0] == '\0') { if(message) gc_output_message(message); - return Status_OK; + return status; } // Determine if the line is a program start/end marker. diff --git a/gcode.h b/gcode.h index ec551ba..3564dc5 100644 --- a/gcode.h +++ b/gcode.h @@ -655,7 +655,7 @@ typedef struct { // Initialize the parser void gc_init (void); -char *gc_normalize_block (char *block, char **message); +char *gc_normalize_block (char *block, status_code_t *status, char **message); // Execute one block of rs275/ngc/g-code status_code_t gc_execute_block (char *block); diff --git a/grbl.h b/grbl.h index ae69882..7453bae 100644 --- a/grbl.h +++ b/grbl.h @@ -42,7 +42,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20241023 +#define GRBL_BUILD 20241025 #define GRBL_URL "https://github.com/grblHAL" diff --git a/messages.h b/messages.h index 3983fed..353579d 100644 --- a/messages.h +++ b/messages.h @@ -54,6 +54,7 @@ typedef enum { Message_Plain = 0, Message_Info, Message_Warning, + Message_Error, Message_Debug } message_type_t; diff --git a/ngc_expr.c b/ngc_expr.c index 16ce699..2202a4f 100644 --- a/ngc_expr.c +++ b/ngc_expr.c @@ -28,6 +28,7 @@ #include #include "errors.h" +#include "settings.h" #include "ngc_expr.h" #include "ngc_params.h" @@ -70,7 +71,8 @@ typedef enum { NGCUnaryOp_SIN, NGCUnaryOp_SQRT, NGCUnaryOp_TAN, - NGCUnaryOp_Exists + NGCUnaryOp_Exists, + NGCUnaryOp_Parameter // read setting/setting bit } ngc_unary_op_t; /*! \brief Executes the operations: /, MOD, ** (POW), *. @@ -564,6 +566,14 @@ static status_code_t read_operation_unary (char *line, uint_fast8_t *pos, ngc_un status = Status_ExpressionUknownOp; break; + case 'P': + if(!strncmp(line + *pos, "RM", 2)) { + *operation = NGCUnaryOp_Parameter; + *pos += 2; + } else + status = Status_ExpressionUknownOp; + break; + default: status = Status_ExpressionUknownOp; } @@ -722,6 +732,45 @@ static status_code_t read_unary (char *line, uint_fast8_t *pos, float *value) } else status = Status_ExpressionSyntaxError; + } else if(operation == NGCUnaryOp_Parameter) { + + // get setting value or bit in value + + bool get_bit; + int32_t setting_id, bitnum; + const setting_detail_t *setting; + + (*pos)++; + if((status = ngc_read_integer_value(line, pos, &setting_id)) == Status_OK) { + + if((get_bit = line[*pos] == ',')) { + (*pos)++; + if((status = ngc_read_integer_value(line, pos, &bitnum)) != Status_OK) + return status; + if(bitnum < 0 || bitnum > 31) + return Status_ExpressionArgumentOutOfRange; + } + + if(line[*pos] != ']') + return Status_ExpressionSyntaxError; // Left bracket missing after slash with ATAN; + + (*pos)++; + + if((setting = setting_get_details((setting_id_t)setting_id, NULL))) { + + uint_fast8_t offset = setting_id - setting->id; + + if(setting->datatype == Format_Decimal) + *value = setting_get_float_value(setting, offset); + else if(setting_is_integer(setting) || setting_is_list(setting)) { + *value = (float)setting_get_int_value(setting, offset); + if(get_bit) + *value = (((uint32_t)*value >> bitnum) & 0x1) ? 1.0f : 0.0f; + } else + status = Status_ExpressionArgumentOutOfRange; + } else + status = Status_ExpressionArgumentOutOfRange; + } } else if((status = ngc_eval_expression(line, pos, value)) == Status_OK) { if(operation == NGCUnaryOp_ATAN) status = read_atan(line, pos, value); diff --git a/ngc_flowctrl.c b/ngc_flowctrl.c index eba578c..749b8a7 100644 --- a/ngc_flowctrl.c +++ b/ngc_flowctrl.c @@ -84,6 +84,7 @@ static volatile int_fast8_t stack_idx = -1; static bool skip_sub = false; static ngc_sub_t *subs = NULL, *exec_sub = NULL; static ngc_stack_entry_t stack[NGC_STACK_DEPTH] = {0}; +static on_gcode_message_ptr on_gcode_comment; static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *operation) { @@ -294,8 +295,34 @@ void ngc_flowctrl_unwind_stack (vfs_file_t *file) stack_pull(); } +static status_code_t onGcodeComment (char *comment) +{ + uint_fast8_t pos = 6; + status_code_t status = Status_OK; + + if(!strncasecmp(comment, "ABORT,", 6)) { + char *buf = NULL; + if(ngc_substitute_parameters(comment + pos, &buf)) { + report_message(buf, Message_Error); + free(buf); + } + status = Status_UserException; + } else if(on_gcode_comment) + status = on_gcode_comment(comment); + + return status; +} + void ngc_flowctrl_init (void) { + static bool init_ok = false; + + if(!init_ok) { + init_ok = true; + on_gcode_comment = grbl.on_gcode_comment; + grbl.on_gcode_comment = onGcodeComment; + } + clear_subs(NULL); while(stack_idx >= 0) stack_pull(); @@ -523,7 +550,7 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo if(stack[stack_idx].repeats && --stack[stack_idx].repeats) vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos); else - stack_pull(); + stack[stack_idx].skip = true; break; case NGCFlowCtrl_Do: @@ -606,7 +633,7 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo if(!skipping) { - ngc_sub_t *sub = subs; + ngc_sub_t *sub; if(o_label > NGC_MAX_PARAM_ID) { #if 0 @@ -631,7 +658,7 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo status = Status_FlowControlOutOfMemory; // file not found... } #endif - } else do { + } else if((sub = subs)) do { if(sub->o_label == o_label && sub->file == hal.stream.file) break; } while((sub = sub->next)); diff --git a/ngc_params.c b/ngc_params.c index f5b9697..1ce1527 100644 --- a/ngc_params.c +++ b/ngc_params.c @@ -414,7 +414,7 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id) break; case NGCParam_vminor: - value = 0.0f; // TODO: derive from version letter? + value = (float)(GRBL_BUILD - 20000000); break; case NGCParam_line: diff --git a/nvs_buffer.c b/nvs_buffer.c index 45e0436..e0c10e7 100644 --- a/nvs_buffer.c +++ b/nvs_buffer.c @@ -39,7 +39,7 @@ static uint8_t *nvsbuffer = NULL; static nvs_io_t physical_nvs; static bool dirty; - +uint32_t nvs_size_max = NVS_SIZE; settings_dirty_t settings_dirty; typedef struct { @@ -54,7 +54,6 @@ typedef struct { #define NVS_GROUP_STARTUP 3 #define NVS_GROUP_BUILD 4 - #define PARAMETER_ADDR(n) (NVS_ADDR_PARAMETERS + n * (sizeof(coord_data_t) + NVS_CRC_BYTES)) #define STARTLINE_ADDR(n) (NVS_ADDR_STARTUP_BLOCK + n * (sizeof(stored_line_t) + NVS_CRC_BYTES)) #if N_TOOLS @@ -215,10 +214,20 @@ static nvs_transfer_result_t memcpy_from_ram (uint8_t *destination, uint32_t sou // Try to allocate RAM from heap for buffer/emulation. bool nvs_buffer_alloc (void) { - assert(NVS_SIZE >= GRBL_NVS_SIZE); + static uint32_t nvs_size = NVS_SIZE; - if((nvsbuffer = malloc(NVS_SIZE))) - memset(nvsbuffer, 0xFF, NVS_SIZE); + if(hal.nvs.size_max > nvs_size) { + nvs_size_max = min(4096, hal.nvs.size_max); // Limit to 4K for now + if(nvsbuffer) + free(nvsbuffer); + } + + assert(nvs_size_max >= GRBL_NVS_SIZE); + + if((nvsbuffer = malloc(nvs_size_max))) { + nvs_size = nvs_size_max; + memset(nvsbuffer, 0xFF, nvs_size_max); + } return nvsbuffer != NULL; } @@ -294,7 +303,7 @@ nvs_address_t nvs_alloc (size_t size) } size += NVS_CRC_BYTES; // add room for checksum. - if(hal.nvs.driver_area.size + size < (NVS_SIZE - GRBL_NVS_SIZE)) { + if(hal.nvs.driver_area.size + size < (nvs_size_max - GRBL_NVS_SIZE)) { mem_address = (uint8_t *)((uint32_t)(mem_address - 1) | 0x03) + 1; // Align to word boundary addr = mem_address - nvsbuffer; mem_address += size; diff --git a/report.c b/report.c index 3f615a4..d105292 100644 --- a/report.c +++ b/report.c @@ -270,6 +270,10 @@ void report_message (const char *msg, message_type_t type) hal.stream.write("Warning: "); break; + case Message_Error: + hal.stream.write("Error: "); + break; + case Message_Debug: hal.stream.write("Debug: "); break; diff --git a/system.c b/system.c index aa28eab..c8366b3 100644 --- a/system.c +++ b/system.c @@ -683,12 +683,14 @@ static status_code_t set_startup_line (sys_state_t state, char *args, uint_fast8 status_code_t retval = Status_OK; - args = gc_normalize_block(args, NULL); + args = gc_normalize_block(args, &retval, NULL); - if(strlen(args) >= (sizeof(stored_line_t) - 1)) - retval = Status_Overflow; - else if ((retval = gc_execute_block(args)) == Status_OK) // Execute gcode block to ensure block is valid. - settings_write_startup_line(lnr, args); + if(retval == Status_OK) { + if(strlen(args) >= (sizeof(stored_line_t) - 1)) + retval = Status_Overflow; + else if ((retval = gc_execute_block(args)) == Status_OK) // Execute gcode block to ensure block is valid. + settings_write_startup_line(lnr, args); + } return retval; }