diff --git a/README.md b/README.md
index 59efef3..c36e9ad 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
## grblHAL ##
-Latest build date is 20250222, see the [changelog](changelog.md) for details.
+Latest build date is 20250225, see the [changelog](changelog.md) for details.
> [!NOTE]
> A settings reset will be performed on an update of builds prior to 20241208. Backup and restore of settings is recommended.
@@ -89,4 +89,4 @@ G/M-codes not supported by [legacy Grbl](https://github.com/gnea/grbl/wiki) are
Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes.
---
-20250222
+20250225
diff --git a/changelog.md b/changelog.md
index 3b56655..f05763b 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,33 @@
## grblHAL changelog
+Build 20250225
+
+Core \(for developers\):
+
+* Added `vfs_chmod()` to VFS \(Virtual File System\), for setting basic file attributes.
+
+* Changed signature of `vfs.on_mount() event`.
+
+* Added `grbl.on_cycle_start()` event, fired on cycle start signal asserted or on cycle start real time command received.
+
+Drivers:
+
+* ESP32 and RP2040: updated for plugin changes.
+
+Plugins:
+
+* SD card: moved file streaming code out of main _SD card_ plugin to new sub-plugin _FS stream_ - allows builds without SD card enabled to stream gcode from flash \(stored in littlefs\).
+Add `#define LITTLEFS_ENABLE 2` to _my_machine.h_ to enable, this will mount littlefs as the root file system. Only available for drivers/boards that has littlefs support.
+Added `vfs_chmod()` implementation for FatFS and littlefs VFS drivers.
+Fixed rewind bug, ref. issue [#8](https://github.com/grblHAL/Plugin_SD_card/issues/8).
+
+* Networking, WebUI and Plasma: removed reliance on SD card plugin for related features, moved to new _FS stream_ plugin instead. Updated file headers and done some general cleanup.
+
+* WebUI: now checks _hidden_ file attribute and only lists hidden files in the management \(forcefallback\) page.
+Files uploaded to littlefs from the management page will now get the _hidden_ attribute set automatically.
+
+---
+
Build 20250223
Core:
@@ -11,7 +39,7 @@ Plugins:
* Networking and WebUI: updated for move of string utilities to the core.
* Plasma: enhanced basic materials support and added import of LinuxCNC and SheetCam style [material files](https://github.com/grblHAL/Plugin_plasma#materials).
- Added support for setting feedrate via named parameter set from value in seleced material.
+ Added support for setting feedrate via named parameter set from value in selected material.
---
diff --git a/core_handlers.h b/core_handlers.h
index 62620c4..c7140bd 100644
--- a/core_handlers.h
+++ b/core_handlers.h
@@ -92,6 +92,7 @@ typedef void (*on_wco_changed_ptr)(void);
typedef void (*on_program_completed_ptr)(program_flow_t program_flow, bool check_mode);
typedef void (*on_execute_realtime_ptr)(sys_state_t state);
typedef void (*on_unknown_accessory_override_ptr)(uint8_t cmd);
+typedef void (*on_cycle_start_ptr)(void);
typedef bool (*on_unknown_realtime_cmd_ptr)(char c);
typedef void (*on_report_handlers_init_ptr)(void);
typedef void (*on_report_options_ptr)(bool newopt);
@@ -233,9 +234,10 @@ typedef struct {
on_get_settings_ptr on_get_settings;
on_realtime_report_ptr on_realtime_report;
on_unknown_feedback_message_ptr on_unknown_feedback_message;
- on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd;
- on_unknown_sys_command_ptr on_unknown_sys_command; //!< return Status_Unhandled if not handled.
- on_get_commands_ptr on_get_commands; //!< Deprecated, use system_register_commands() to register new commands.
+ on_cycle_start_ptr on_cycle_start; //!< Called from interrupt context. NOTE: this is for the cycle start signal.
+ on_unknown_realtime_cmd_ptr on_unknown_realtime_cmd; //!< Called from interrupt context.
+ on_unknown_sys_command_ptr on_unknown_sys_command; //!< return Status_Unhandled if not handled.
+ on_get_commands_ptr on_get_commands; //!< Deprecated, use system_register_commands() to register new commands.
on_user_command_ptr on_user_command;
on_stream_changed_ptr on_stream_changed;
on_homing_rate_set_ptr on_homing_rate_set;
diff --git a/driver_opts.h b/driver_opts.h
index 4ed209f..7483c38 100644
--- a/driver_opts.h
+++ b/driver_opts.h
@@ -512,13 +512,17 @@
#undef WEBSOCKET_ENABLE
#endif
#define WEBSOCKET_ENABLE 1
-#if defined(SDCARD_ENABLE) && SDCARD_ENABLE == 0
+#if defined(SDCARD_ENABLE) && SDCARD_ENABLE == 0 && LITTLEFS_ENABLE != 2
#undef SDCARD_ENABLE
#endif
#ifndef SDCARD_ENABLE
+#if LITTLEFS_ENABLE == 2
+#define SDCARD_ENABLE 0
+#else
#define SDCARD_ENABLE 1
#endif
#endif
+#endif
#ifndef ETHERNET_ENABLE
#define ETHERNET_ENABLE 0
@@ -664,6 +668,30 @@
#endif
#endif
+#define FS_FATFS 0b00001
+#define FS_SDCARD 0b00010
+#define FS_LFS 0b00100
+#define FS_LFS_ROOT 0b01000
+#define FS_YMODEM 0b10000
+
+#if SDCARD_ENABLE && LITTLEFS_ENABLE
+#if SDCARD_ENABLE == 2
+#define FS_ENABLE (FS_SDCARD|FS_FATFS|FS_LFS|FS_YMODEM)
+#else
+#define FS_ENABLE (FS_SDCARD|FS_FATFS|FS_LFS)
+#endif
+#elif LITTLEFS_ENABLE == 2
+#define FS_ENABLE (FS_LFS|FS_LFS_ROOT|FS_YMODEM)
+#elif LITTLEFS_ENABLE
+#define FS_ENABLE FS_LFS
+#elif SDCARD_ENABLE == 1
+#define FS_ENABLE (FS_SDCARD|FS_FATFS)
+#elif SDCARD_ENABLE == 2
+#define FS_ENABLE (FS_SDCARD|FS_FATFS|FS_YMODEM)
+#else
+#define FS_ENABLE 0
+#endif
+
#ifndef SPI_ENABLE
#if SDCARD_ENABLE || TRINAMIC_SPI_ENABLE
#define SPI_ENABLE 1
diff --git a/errors.h b/errors.h
index 00c015c..8fe5443 100644
--- a/errors.h
+++ b/errors.h
@@ -89,10 +89,12 @@ typedef enum {
Status_UnexpectedDemarcation = 57,
Status_SDMountError = 60,
- Status_SDReadError = 61,
- Status_SDFailedOpenDir = 62,
- Status_SDDirNotFound = 63,
+ Status_FileReadError = 61,
+ Status_FsFailedOpenDir = 62,
+ Status_FSDirNotFound = 63,
Status_SDNotMounted = 64,
+ Status_FsNotMounted = 65,
+ Status_FsReadOnly = 66,
Status_BTInitError = 70,
diff --git a/plugins_init.h b/plugins_init.h
index ed0fb39..a4fed6b 100644
--- a/plugins_init.h
+++ b/plugins_init.h
@@ -7,22 +7,27 @@
Part of grblHAL
- Copyright (c) 2021-2023 Terje Io
+ Copyright (c) 2021-2025 Terje Io
- 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 .
*/
+#if SDCARD_ENABLE || LITTLEFS_ENABLE == 2
+ extern void fs_stream_init (void);
+ fs_stream_init();
+#endif
+
#if TRINAMIC_ENABLE
extern bool trinamic_init (void);
trinamic_init();
diff --git a/protocol.c b/protocol.c
index cc7318c..721fe2e 100644
--- a/protocol.c
+++ b/protocol.c
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2017-2024 Terje Io
+ Copyright (c) 2017-2025 Terje Io
Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC
Copyright (c) 2009-2011 Simen Svale Skogsrud
@@ -914,6 +914,8 @@ ISR_CODE bool ISR_FUNC(protocol_enqueue_realtime_command)(char c)
// Cancel any pending tool change
gc_state.tool_change = false;
drop = true;
+ if(grbl.on_cycle_start)
+ grbl.on_cycle_start();
break;
case CMD_FEED_HOLD:
@@ -1034,6 +1036,8 @@ ISR_CODE bool ISR_FUNC(protocol_enqueue_realtime_command)(char c)
// Cancel any pending tool change
gc_state.tool_change = false;
drop = true;
+ if(grbl.on_cycle_start)
+ grbl.on_cycle_start();
}
break;
diff --git a/settings.c b/settings.c
index 15893ca..fe2fb74 100644
--- a/settings.c
+++ b/settings.c
@@ -2109,7 +2109,7 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_AxisHomingFeedRate, Group_Axis0, "-axis homing locate feed rate", axis_rate, Format_Decimal, "###0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_float, is_setting_available, AXIS_OPTS },
{ Setting_AxisHomingSeekRate, Group_Axis0, "-axis homing search seek rate", axis_rate, Format_Decimal, "###0", NULL, NULL, Setting_NonCoreFn, set_axis_setting, get_float, is_setting_available, AXIS_OPTS },
{ Setting_SpindleAtSpeedTolerance, Group_Spindle, "Spindle at speed tolerance", "percent", Format_Decimal, "##0.0", NULL, NULL, Setting_IsExtendedFn, set_float, get_float, is_setting_available },
- { Setting_ToolChangeMode, Group_Toolchange, "Tool change mode", NULL, Format_RadioButtons, "Normal,Manual touch off,Manual touch off @ G59.3,Automatic touch off @ G59.3,Ignore M6, Fast Automatic touch off @ G59.3", NULL, NULL, Setting_IsExtendedFn, set_tool_change_mode, get_int, is_setting_available },
+ { Setting_ToolChangeMode, Group_Toolchange, "Tool change mode", NULL, Format_RadioButtons, "Normal,Manual touch off,Manual touch off @ G59.3,Automatic touch off @ G59.3,Ignore M6,Fast Automatic touch off @ G59.3", NULL, NULL, Setting_IsExtendedFn, set_tool_change_mode, get_int, is_setting_available },
{ Setting_ToolChangeProbingDistance, Group_Toolchange, "Tool change probing distance", "mm", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtendedFn, set_tool_change_probing_distance, get_float, is_setting_available },
{ Setting_ToolChangeFeedRate, Group_Toolchange, "Tool change locate feed rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtended, &settings.tool_change.feed_rate, NULL, is_setting_available },
{ Setting_ToolChangeSeekRate, Group_Toolchange, "Tool change search seek rate", "mm/min", Format_Decimal, "#####0.0", NULL, NULL, Setting_IsExtended, &settings.tool_change.seek_rate, NULL, is_setting_available },
diff --git a/stream.c b/stream.c
index 765c025..52ec5bf 100644
--- a/stream.c
+++ b/stream.c
@@ -300,8 +300,6 @@ static bool stream_select (const io_stream_t *stream, bool add)
} else if(add_connection(stream) == NULL)
return false;
- bool webui_connected = hal.stream.state.webui_connected;
-
switch(stream->type) {
case StreamType_Serial:
@@ -354,9 +352,6 @@ static bool stream_select (const io_stream_t *stream, bool add)
if(hal.stream.is_connected == NULL)
hal.stream.is_connected = stream == base.stream ? base.is_up : stream_connected;
- if(stream->type == StreamType_WebSocket && !stream->state.webui_connected)
- hal.stream.state.webui_connected = webui_connected;
-
if(send_init_message)
grbl.report.init_message(stream->write);
diff --git a/system.c b/system.c
index 8fd96ea..83034a6 100644
--- a/system.c
+++ b/system.c
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2017-2024 Terje Io
+ Copyright (c) 2017-2025 Terje Io
Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
grblHAL is free software: you can redistribute it and/or modify
@@ -112,6 +112,8 @@ ISR_CODE void ISR_FUNC(control_interrupt_handler)(control_signals_t signals)
system_set_exec_state_flag(EXEC_CYCLE_START);
sys.report.cycle_start = settings.status_report.pin_state;
gc_state.tool_change = false;
+ if(grbl.on_cycle_start)
+ grbl.on_cycle_start();
}
if(signals.block_delete)
diff --git a/vfs.c b/vfs.c
index 36ccdea..bc3e521 100644
--- a/vfs.c
+++ b/vfs.c
@@ -502,6 +502,13 @@ char *vfs_getcwd (char *buf, size_t len)
return buf ? buf : cwds;
}
+int vfs_chmod (const char *filename, vfs_st_mode_t attr, vfs_st_mode_t mask)
+{
+ vfs_mount_t *mount = get_mount(filename);
+
+ return mount && mount->vfs->fchmod ? mount->vfs->fchmod(get_filename(mount, filename), attr, mask) : -1;
+}
+
int vfs_stat (const char *filename, vfs_stat_t *st)
{
vfs_mount_t *mount = get_mount(filename);
@@ -598,7 +605,7 @@ bool vfs_mount (const char *path, const vfs_t *fs, vfs_st_mode_t mode)
hal.rtc.get_datetime = vfs_get_time;
if(fs && vfs.on_mount)
- vfs.on_mount(path, fs);
+ vfs.on_mount(path, fs, mode);
return fs != NULL;
}
diff --git a/vfs.h b/vfs.h
index bacb789..b2d4a33 100644
--- a/vfs.h
+++ b/vfs.h
@@ -101,6 +101,7 @@ typedef struct {
typedef struct {
const char *name;
size_t size;
+ vfs_st_mode_t st_mode;
const uint8_t data[];
} embedded_file_t;
@@ -121,6 +122,7 @@ typedef int (*vfs_rmdir_ptr)(const char *path);
typedef vfs_dir_t *(*vfs_opendir_ptr)(const char *path);
typedef char *(*vfs_readdir_ptr)(vfs_dir_t *dir, vfs_dirent_t *dirent);
typedef void (*vfs_closedir_ptr)(vfs_dir_t *dir);
+typedef int (*vfs_chmod_ptr)(const char *filename, vfs_st_mode_t attr, vfs_st_mode_t mask);
typedef int (*vfs_stat_ptr)(const char *filename, vfs_stat_t *st);
typedef int (*vfs_utime_ptr)(const char *filename, struct tm *modified);
@@ -146,6 +148,7 @@ typedef struct
vfs_opendir_ptr fopendir;
vfs_readdir_ptr readdir;
vfs_closedir_ptr fclosedir;
+ vfs_chmod_ptr fchmod;
vfs_stat_ptr fstat;
vfs_utime_ptr futime;
vfs_getcwd_ptr fgetcwd;
@@ -154,7 +157,7 @@ typedef struct
} vfs_t;
typedef void (*on_vfs_changed_ptr)(const vfs_t *fs);
-typedef void (*on_vfs_mount_ptr)(const char *path, const vfs_t *fs);
+typedef void (*on_vfs_mount_ptr)(const char *path, const vfs_t *fs, vfs_st_mode_t mode);
typedef void (*on_vfs_unmount_ptr)(const char *path);
typedef struct {
@@ -224,6 +227,7 @@ vfs_dir_t *vfs_opendir (const char *path);
vfs_dirent_t *vfs_readdir (vfs_dir_t *dir);
void vfs_closedir (vfs_dir_t *dir);
char *vfs_getcwd (char *buf, size_t len);
+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);
int vfs_utime (const char *filename, struct tm *modified);
vfs_free_t *vfs_fgetfree (const char *path);