Refactored the tool table HAL API a bit to improve functionality.

Added tool name, pocket number and some lathe specific data fields to the $# tool output when available from the tool table.
If name is available it will be output as a push message on M6 if there is no message in the same block.
Added support for directory up, .., to VFS.
Added new option to $650 - File systems options to enable hierarchical listing of files and directories via $F and $F+ commands.
This commit is contained in:
Terje Io
2025-11-11 16:51:08 +01:00
parent 18d6272060
commit e272f773b5
13 changed files with 173 additions and 55 deletions
+26
View File
@@ -1,5 +1,31 @@
## grblHAL changelog
<a name="20251111">Build 20251111
Core:
* Refactored the tool table HAL API a bit to improve functionality.
Added tool name, pocket number and some lathe specific data fields to the `$#` tool output when available from the tool table.
If name is available it will be output as a push message on `M6` if there is no message in the same block.
* Added support for directory up, `..`, to VFS.
* Added new option to `$650` - _File systems options_ to enable hierarchical listing of files and directories via `$F` and `$F+` commands.
When enabled directories are added to the output with file size set to `-1` and only the contents of the current directory is reported.
The `$F=<dirname>` command can then be used to set the current working direcory \(CWD\). `$F=..` can be used to move up one level and `$F=/` to move to the root directory.
Files or directory names for `$F`, `$F+` `$F<` and `$FD` commands not starting with `/` will executed be relative to the CWD.
Plugins:
* Misc, tooltable: removed limit on max number of tools, it is now constrained by available RAM. Added name \(remark, comment\) to imported data and
a new command, `$TTLOAD`, that can be used to reload the tool table after it has been updated.
* EEPROM: added experimental code that allows mounting unused part of large EEPROM as littlefs file system. This may then be used to store a file based tooltable etc.
* SD Card, littlefs VFS wrapper: added support for `vfs_chdir()` \(change directory\) to allow ftp access to subdirectories.
---
<a name="20251109">Build 20251109
Core:
+46 -3
View File
@@ -542,7 +542,7 @@ Set to \ref On or 1 to enable experimental support for expressions.
Some LinuxCNC extensions are supported, conditionals and subroutines are not.
*/
#if !defined NGC_EXPRESSIONS_ENABLE || defined __DOXYGEN__
#define NGC_EXPRESSIONS_ENABLE Off
#define NGC_EXPRESSIONS_ENABLE On
#endif
/*! \def NGC_PARAMETERS_ENABLE
@@ -1467,11 +1467,14 @@ and less range over the total 255 PWM levels to signal different spindle speeds.
// Homing settings (Group_Homing)
/*! @name $22 - Setting_HomingEnable
\brief Enable homing.
\brief Enable and control homing functionality.
Requires homing cycles to be defined by \ref DEFAULT_HOMING_CYCLE_0 - \ref DEFAULT_HOMING_CYCLE_2 +.
\internal Bit 0 in settings.homing.flags.
*/
///@{
/*! /def DEFAULT_HOMING_ENABLE
\brief Enables homing overall.
\internal Bit 0 in settings.homing.flags.
*/
#if !defined DEFAULT_HOMING_ENABLE || defined __DOXYGEN__
#define DEFAULT_HOMING_ENABLE Off // Default disabled. Set to \ref On or 1 to enable.
#endif
@@ -2119,6 +2122,46 @@ Default stream format settings for ModBus RTU stream.
#endif
///@}
/*! @name $681 - Setting_ModBus_StreamFormat
Default stream format settings for ModBus RTU stream.
*/
///@{
#if !defined DEFAULT_MODBUS_STREAM_PARITY || defined __DOXYGEN__
#define DEFAULT_MODBUS_STREAM_PARITY 0 // 0 = None, 1 = Even, 2 = Odd
#endif
///@}
/*! @name $650 - Setting_FSOptions
Filing systems options.
*/
///@{
/*! /def DEFAULT_FS_SD_AUTOMOUNT
\brief Auto mount SD card on startup.
\internal Bit 0 in settings.fs_options.mask.
*/
#if !defined DEFAULT_FS_SD_AUTOMOUNT || defined __DOXYGEN__
#define DEFAULT_FS_SD_AUTOMOUNT Off // Default disabled. Set to \ref On or 1 to enable.
#endif
/*! /def DEFAULT_FS_LITLLEFS_HIDDEN
\brief Hides LittleFS mount from directory listings.
\internal Bit 1 in settings.fs_options.mask.
*/
#if !defined DEFAULT_FS_LITLLEFS_HIDDEN || defined __DOXYGEN__
#define DEFAULT_FS_LITLLEFS_HIDDEN Off // Default disabled. Set to \ref On or 1 to enable.
#endif
/*! /def DEFAULT_FS_HIERACHICAL_LISTING
\brief
Adds directory entries in $F and $F+ output to allow hierarchical navigation of the directoy tree.
\internal Bit 2 in settings.fs_options.mask.
*/
#if !defined DEFAULT_FS_HIERACHICAL_LISTING || defined __DOXYGEN__
#define DEFAULT_FS_HIERACHICAL_LISTING Off // Default disabled. Set to \ref On or 1 to enable.
#endif
///@}
// Axis settings (Group_XAxis - Group_VAxis)
/*! @name $10x - Setting_AxisStepsPerMM
+2 -4
View File
@@ -136,10 +136,9 @@ typedef status_code_t (*on_macro_execute_ptr)(macro_id_t macro); // macro implem
typedef void (*on_macro_return_ptr)(void);
typedef void (*on_file_demarcate_ptr)(bool start);
typedef tool_data_t *(*get_tool_ptr)(tool_id_t tool_id);
typedef tool_data_t *(*get_tool_by_idx_ptr)(uint32_t idx);
typedef tool_table_entry_t *(*get_tool_ptr)(tool_id_t tool_id);
typedef tool_table_entry_t *(*get_tool_by_idx_ptr)(uint32_t idx);
typedef bool (*set_tool_data_ptr)(tool_data_t *tool_data);
typedef pocket_id_t (*get_pocket_ptr)(tool_id_t tool_id);
typedef bool (*clear_tool_data_ptr)(void);
typedef struct {
@@ -147,7 +146,6 @@ typedef struct {
get_tool_ptr get_tool;
get_tool_by_idx_ptr get_tool_by_idx;
set_tool_data_ptr set_tool;
get_pocket_ptr get_pocket;
clear_tool_data_ptr clear;
} tool_table_t;
+24 -18
View File
@@ -373,16 +373,16 @@ void gc_init (bool stop)
{
#if COMPATIBILITY_LEVEL > 1
memset(&gc_state, 0, sizeof(parser_state_t));
gc_state.tool = grbl.tool_table.get_tool(0);
gc_state.tool = grbl.tool_table.get_tool(0)->data;
if(grbl.tool_table.n_tools == 0)
memset(gc_state.tool, 0, sizeof(tool_data_t));
#else
if(sys.cold_start) {
memset(&gc_state, 0, sizeof(parser_state_t));
if(settings.flags.tool_persistent)
gc_state.tool = grbl.tool_table.get_tool(settings.tool_id);
gc_state.tool = grbl.tool_table.get_tool(settings.tool_id)->data;
if(gc_state.tool == NULL)
gc_state.tool = grbl.tool_table.get_tool(0);
gc_state.tool = grbl.tool_table.get_tool(0)->data;
if(grbl.tool_table.n_tools == 0) {
memset(gc_state.tool, 0, sizeof(tool_data_t));
if(settings.flags.tool_persistent)
@@ -503,12 +503,21 @@ static void add_offset (const coord_data_t *offset)
system_flag_wco_change();
}
static tool_data_t *tool_get_pending (tool_id_t tool_id)
static tool_data_t *tool_get_pending (tool_id_t tool_id, char **message)
{
static tool_data_t tool_data = {0};
if(grbl.tool_table.n_tools)
return grbl.tool_table.get_tool(tool_id);
if(grbl.tool_table.n_tools) {
tool_table_entry_t *tool = grbl.tool_table.get_tool(tool_id);
if(message && tool->name) {
if((*message = malloc(strlen(tool->name) + 1)))
strcpy(*message, tool->name);
}
return tool->data;
}
memcpy(&tool_data, gc_state.tool, sizeof(tool_data_t));
tool_data.tool_id = tool_id;
@@ -1711,7 +1720,7 @@ status_code_t gc_execute_block (char *block)
case 'T':
if(mantissa > 0)
FAIL(Status_GcodeCommandValueNotInteger);
if(grbl.tool_table.n_tools ? (grbl.tool_table.get_tool((tool_id_t)int_value) == NULL) : (int_value > MAX_TOOL_NUMBER))
if(grbl.tool_table.n_tools ? (grbl.tool_table.get_tool((tool_id_t)int_value)->data == NULL) : (int_value > MAX_TOOL_NUMBER))
FAIL(Status_GcodeIllegalToolTableEntry);
word_bit.parameter.t = On;
gc_block.values.t = isnan(value) ? 0xFFFFFFFF : int_value;
@@ -2059,7 +2068,7 @@ status_code_t gc_execute_block (char *block)
FAIL(Status_GcodeValueWordMissing);
if(!isintf(gc_block.values.q))
FAIL(Status_GcodeCommandValueNotInteger);
if(grbl.tool_table.n_tools ? grbl.tool_table.get_tool((tool_id_t)gc_block.values.q) == NULL : gc_block.values.q > MAX_TOOL_NUMBER)
if(grbl.tool_table.n_tools ? grbl.tool_table.get_tool((tool_id_t)gc_block.values.q)->data == NULL : gc_block.values.q > MAX_TOOL_NUMBER)
FAIL(Status_GcodeIllegalToolTableEntry);
gc_block.values.t = (uint32_t)gc_block.values.q;
@@ -2402,7 +2411,7 @@ status_code_t gc_execute_block (char *block)
if(gc_block.words.h) {
if(gc_block.values.h == 0 && !settings.macro_atc_flags.random_toolchanger)
gc_block.values.h = gc_block.values.t; // !! no tool clear offset
if(!grbl.tool_table.get_tool((tool_id_t)gc_block.values.h))
if(!grbl.tool_table.get_tool((tool_id_t)gc_block.values.h)->data)
FAIL(Status_GcodeIllegalToolTableEntry);
gc_block.words.h = Off;
} else
@@ -2414,7 +2423,7 @@ status_code_t gc_execute_block (char *block)
case ToolLengthOffset_ApplyAdditional:
if(grbl.tool_table.n_tools) {
if(gc_block.words.h) {
if(gc_block.values.h == 0 || !grbl.tool_table.get_tool((tool_id_t)gc_block.values.h))
if(gc_block.values.h == 0 || !grbl.tool_table.get_tool((tool_id_t)gc_block.values.h)->data)
FAIL(Status_GcodeIllegalToolTableEntry);
gc_block.words.h = Off;
} else
@@ -2532,8 +2541,8 @@ status_code_t gc_execute_block (char *block)
tool_data_t *tool_data;
if((tool_data = grbl.tool_table.get_tool((tool_id_t)p_value)) == NULL)
FAIL(Status_GcodeIllegalToolTableEntry); // [Greater than max allowed tool number]
if((tool_data = grbl.tool_table.get_tool((tool_id_t)p_value)->data) == NULL)
FAIL(Status_GcodeIllegalToolTableEntry); // [Greater than max allowed tool number or not in tool table]
if(gc_block.words.r) {
tool_data->radius = gc_block.values.r;
@@ -2546,9 +2555,6 @@ status_code_t gc_execute_block (char *block)
FAIL(Status_SettingReadFail);
#endif
if(gc_block.values.l == 1)
tool_data = grbl.tool_table.get_tool((tool_id_t)p_value);
idx = N_AXIS;
do {
if(bit_istrue(axis_words.mask, bit(--idx))) {
@@ -3427,7 +3433,7 @@ status_code_t gc_execute_block (char *block)
if(!check_mode) {
tool_data_t *pending_tool = tool_get_pending(gc_state.tool_pending);
tool_data_t *pending_tool = tool_get_pending(gc_state.tool_pending, NULL);
// If M6 not available or M61 commanded set new tool immediately
if(set_tool || (hal.driver_cap.atc ? !hal.tool.change : settings.tool_change.mode == ToolChange_Ignore || !(hal.stream.suspend_read || hal.tool.change))) {
@@ -3494,7 +3500,7 @@ status_code_t gc_execute_block (char *block)
// [6. Change tool ]: Delegated to (possible) driver implementation
if(command_words.M6 && !set_tool && !check_mode) {
tool_data_t *pending_tool = tool_get_pending(gc_state.tool_pending);
tool_data_t *pending_tool = tool_get_pending(gc_state.tool_pending, plan_data.message ? NULL : &plan_data.message);
protocol_buffer_synchronize();
@@ -3760,7 +3766,7 @@ status_code_t gc_execute_block (char *block)
gc_state.modal.tool_offset_mode = gc_block.modal.tool_offset_mode;
if(gc_state.modal.tool_offset_mode == ToolLengthOffset_Enable || gc_state.modal.tool_offset_mode == ToolLengthOffset_ApplyAdditional)
tool_data = grbl.tool_table.get_tool((tool_id_t)gc_block.values.h);
tool_data = grbl.tool_table.get_tool((tool_id_t)gc_block.values.h)->data;
do {
+8
View File
@@ -589,9 +589,17 @@ typedef struct {
tool_id_t tool_id; //!< Tool number
} tool_data_t;
//! Tool table entry.
typedef struct {
const char *name;
tool_data_t *data;
pocket_id_t pocket;
} tool_table_entry_t;
typedef struct {
tool_data_t tool;
pocket_id_t pocket_id;
char name[101];
} tool_pocket_t;
/*! \brief Parser state
+1 -1
View File
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
#define GRBL_BUILD 20251109
#define GRBL_BUILD 20251111
#define GRBL_URL "https://github.com/grblHAL"
+1 -1
View File
@@ -432,7 +432,7 @@ int grbl_enter (void)
task_add_delayed(auto_realtime_report, NULL, settings.report_interval);
if(hal.driver_cap.sd_card || hal.driver_cap.littlefs) {
fs_options_t fs_options = {0};
fs_options_t fs_options = { .hierarchical_listing = On };
fs_options.lfs_hidden = hal.driver_cap.littlefs;
fs_options.sd_mount_on_boot = hal.driver_cap.sd_card;
setting_remove_elements(Setting_FSOptions, fs_options.mask);
+3 -3
View File
@@ -596,7 +596,7 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id)
break;
case NGCParam_current_pocket:
value = (float)grbl.tool_table.get_pocket(gc_state.tool->tool_id);
value = (float)grbl.tool_table.get_tool(gc_state.tool->tool_id)->pocket;
break;
case NGCParam_selected_tool:
@@ -604,7 +604,7 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id)
break;
case NGCParam_selected_pocket:
value = gc_state.tool_pending != gc_state.tool->tool_id ? (float)grbl.tool_table.get_pocket(gc_state.tool_pending) : -1.0f;
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:
@@ -1094,7 +1094,7 @@ static status_code_t macro_get_tool_offset (void)
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);
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]);
+10 -1
View File
@@ -646,9 +646,10 @@ void report_ngc_parameters (void)
hal.stream.write("]" ASCII_EOL);
tool_data_t *tool_data;
tool_table_entry_t *tool;
for (idx = 1; idx <= grbl.tool_table.n_tools; idx++) {
if((tool_data = grbl.tool_table.get_tool_by_idx((uint32_t)idx)) &&
if((tool_data = (tool = grbl.tool_table.get_tool_by_idx((uint32_t)idx))->data) &&
(settings.macro_atc_flags.random_toolchanger ? tool_data->tool_id >= 0 : tool_data->tool_id > 0)) {
hal.stream.write("[T:");
hal.stream.write(uitoa(tool_data->tool_id));
@@ -656,7 +657,15 @@ void report_ngc_parameters (void)
hal.stream.write(get_axis_values(tool_data->offset.values));
hal.stream.write("|");
hal.stream.write(get_axis_value(tool_data->radius));
hal.stream.write("|6,0,0|");
hal.stream.write(tool->name ? tool->name : ""); // TODO: sanitize name? (| not allowed)
if(tool->pocket >= 0) {
hal.stream.write("|");
hal.stream.write(uitoa((uint32_t)tool->pocket));
}
hal.stream.write("]" ASCII_EOL);
if(tool->name)
hal.delay_ms(5, NULL);
}
}
+27 -18
View File
@@ -358,6 +358,10 @@ PROGMEM const settings_t defaults = {
.safety_door.spindle_on_delay = DEFAULT_SAFETY_DOOR_SPINDLE_DELAY,
.safety_door.coolant_on_delay = DEFAULT_SAFETY_DOOR_COOLANT_DELAY,
.fs_options.sd_mount_on_boot = DEFAULT_FS_SD_AUTOMOUNT,
.fs_options.lfs_hidden = DEFAULT_FS_LITLLEFS_HIDDEN,
.fs_options.hierarchical_listing = DEFAULT_FS_HIERACHICAL_LISTING,
.modbus_baud = DEFAULT_MODBUS_STREAM_BAUD,
.modbus_stream_format.value = (DEFAULT_MODBUS_STREAM_PARITY << 4),
@@ -455,7 +459,7 @@ static char ganged_axes[] = "X-Axis,Y-Axis,Z-Axis";
static on_file_demarcate_ptr on_file_demarcate;
static char step_us_min[4];
static char fs_options[] = "Auto mount SD card,Hide LittleFS";
static char fs_options[] = "Auto mount SD card,Hide LittleFS,Hierarchical listing";
static char spindle_types[100] = "";
static char axis_dist[4] = "mm";
static char axis_rate[8] = "mm/min";
@@ -2510,18 +2514,22 @@ static bool settings_set_tool_data (tool_data_t *tool)
}
// Read selected tool data from persistent storage.
static tool_data_t *settings_get_tool_data (tool_id_t tool_id)
static tool_table_entry_t *settings_get_tool_data (tool_id_t tool_id)
{
tool_data_t *tool = tool_id <= N_TOOLS ? &tool_data[tool_id] : NULL;
static tool_table_entry_t tool = {0};
if(tool_id && tool && !(hal.nvs.type != NVS_None &&
hal.nvs.memcpy_from_nvs((uint8_t *)tool, NVS_ADDR_TOOL_TABLE + (tool_id - 1) * (sizeof(tool_data_t) + NVS_CRC_BYTES),
sizeof(tool_data_t), true) == NVS_TransferResult_OK && tool->tool_id == tool_id)) {
memset(tool, 0, sizeof(tool_data_t));
tool->tool_id = tool_id;
tool.data = tool_id <= N_TOOLS ? &tool_data[tool_id] : NULL;
if(tool_id && tool.data && !(hal.nvs.type != NVS_None &&
hal.nvs.memcpy_from_nvs((uint8_t *)tool.data, NVS_ADDR_TOOL_TABLE + (tool_id - 1) * (sizeof(tool_data_t) + NVS_CRC_BYTES),
sizeof(tool_data_t), true) == NVS_TransferResult_OK && tool.data->tool_id == tool_id)) {
memset(tool.data, 0, sizeof(tool_data_t));
tool.data->tool_id = tool_id;
}
return tool;
tool.pocket = (pocket_id_t)(tool.data ? tool_id : -1);
return &tool;
}
// Clear all tool data in persistent storage.
@@ -2550,13 +2558,20 @@ static bool settings_set_tool_data (tool_data_t *tool_data)
}
// Read selected tool data from persistent storage.
static tool_data_t *settings_get_tool_data (tool_id_t tool_id)
static tool_table_entry_t *settings_get_tool_data (tool_id_t tool_id)
{
static tool_table_entry_t tool = {0};
if(tool_id <= MAX_TOOL_NUMBER)
if(tool_id <= MAX_TOOL_NUMBER) {
tool_data.tool_id = tool_id;
tool.pocket = (pocket_id_t)tool_id;
tool.data = &tool_data;
} else {
tool.data = NULL;
tool.pocket = (pocket_id_t)-1;
}
return tool_id <= MAX_TOOL_NUMBER ? &tool_data : NULL;
return &tool;
}
// Clear all tool data in persistent storage.
@@ -2569,11 +2584,6 @@ static bool settings_clear_tool_data (void)
#endif // N_TOOLS
static pocket_id_t settings_get_tool_pocket (tool_id_t tool_id)
{
return (pocket_id_t)tool_id;
}
// Sanity check of settings, board map could have been changed...
static void sanity_check (void)
{
@@ -3412,7 +3422,6 @@ void settings_init (void)
grbl.tool_table.get_tool = settings_get_tool_data;
grbl.tool_table.get_tool_by_idx = (get_tool_by_idx_ptr)settings_get_tool_data;
grbl.tool_table.set_tool = settings_set_tool_data;
grbl.tool_table.get_pocket = settings_get_tool_pocket;
grbl.tool_table.clear = settings_clear_tool_data;
}
+4 -3
View File
@@ -773,9 +773,10 @@ typedef union {
uint8_t value;
uint8_t mask;
struct {
uint8_t sd_mount_on_boot :1,
lfs_hidden :1,
unused :6;
uint8_t sd_mount_on_boot :1,
lfs_hidden :1,
hierarchical_listing :1,
unused :5;
};
} fs_options_t;
+2 -1
View File
@@ -515,6 +515,8 @@ bool stream_close (io_stream_t const *stream)
void stream_set_defaults (const io_stream_t *stream, uint32_t baud_rate)
{
stream->set_enqueue_rt_handler(protocol_enqueue_realtime_command);
if(stream->set_baud_rate)
stream->set_baud_rate(baud_rate);
@@ -528,7 +530,6 @@ void stream_set_defaults (const io_stream_t *stream, uint32_t baud_rate)
stream->reset_write_buffer();
stream->reset_read_buffer();
stream->set_enqueue_rt_handler(protocol_enqueue_realtime_command);
}
// MPG stream
+19 -2
View File
@@ -27,6 +27,10 @@
#include "hal.h"
#include "vfs.h"
#ifndef VFS_CWD_LENGTH
#define VFS_CWD_LENGTH 100
#endif
#ifdef ARDUINO_SAM_DUE
#undef feof
#endif
@@ -202,7 +206,7 @@ static vfs_mount_t root = {
.next = NULL
};
static vfs_mount_t *cwdmount = &root;
static char cwd[100] = "/";
static char cwd[VFS_CWD_LENGTH] = "/";
volatile int vfs_errno = 0;
vfs_events_t vfs = {0};
@@ -381,9 +385,13 @@ int vfs_rmdir (const char *path)
int vfs_chdir (const char *path)
{
int ret;
char *p;
vfs_errno = 0;
if(!strcmp("..", path) && strcmp("/", (path = cwd)) && (p = strrchr(cwd, '/')))
*(p + (p == cwd ? 1 : 0)) = '\0';
if(*path != '/' && strcmp(cwd, "/")) {
if(strcmp(path, "..")) {
if(strlen(cwd) > 1)
@@ -511,11 +519,20 @@ int vfs_chmod (const char *filename, vfs_st_mode_t attr, vfs_st_mode_t mask)
int vfs_stat (const char *filename, vfs_stat_t *st)
{
char tmp[VFS_CWD_LENGTH], *p;
if(!strcmp("..", filename)) {
strcpy(tmp, cwd);
if((p = strrchr(tmp, '/')))
*(p + (p == tmp ? 1 : 0)) = '\0';
filename = tmp;
}
vfs_mount_t *mount = get_mount(filename);
int ret = mount ? mount->vfs->fstat(get_filename(mount, filename), st) : -1;
if(ret == -1 && strchr(filename, '/') == NULL && !strcmp("/", cwd)) {
if(ret == -1 && (!strcmp("/", filename) || (strchr(filename, '/') == NULL && !strcmp("/", cwd)))) {
strcat(cwd, filename);
mount = get_mount(cwd);