diff --git a/changelog.md b/changelog.md
index b3a5487..3a1e45a 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,26 @@
## grblHAL changelog
+20250104
+
+Core:
+
+* Changed error code reported when file not found for `G65` macro or named `O`-sub calls from `39` to `84`.
+Removed `IDLE` state requirement for executing `G65` macros, an error will no longer be returned and the macro will be run.
+
+* Added event handler for outputting welcome message on "native" USB connect for drivers that provides the linestate changed event. Ref. STMF32F4xx issue [#206](https://github.com/grblHAL/STM32F4xx/issues/206).
+
+Drivers:
+
+* LPC176x: added support for USB linestate changed event.
+
+* iMXRT1062: now outputs the welcome message on the first USB connection after a cold start.
+
+Plugins:
+
+* Bluetooth: updated for core change.
+
+---
+
20250103
Core:
@@ -10,7 +31,7 @@ They can be either set to 0 or to a value in the range 0.5 - 20s. The settings a
> If the spindle supports "at speed" functionality and this is enabled by setting `$340` \(Spindle at speed tolerance\) > 0 then the spin up delay is used as a timeout value before alarm 14 is raised. If `$394` is set to 0 the timeout will default to one minute.
> [!NOTE]
-> Setting `$392` and `$393`, if available, are now only used for spindle spin up delay and coolant start delay respectively when the safety door is closed.
+> Settings `$392` and `$393`, if available, are now only used for spindle spin up delay and coolant start delay respectively when the safety door is closed.
Plugins:
diff --git a/core_handlers.h b/core_handlers.h
index db2ab08..a324884 100644
--- a/core_handlers.h
+++ b/core_handlers.h
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2020-2024 Terje Io
+ Copyright (c) 2020-2025 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
@@ -58,7 +58,7 @@ typedef struct {
// Report entry points set by core at startup and reset.
-typedef void (*init_message_ptr)(void);
+typedef void (*init_message_ptr)(stream_write_ptr write);
typedef void (*help_message_ptr)(void);
typedef status_code_t (*status_message_ptr)(status_code_t status_code);
typedef message_code_t (*feedback_message_ptr)(message_code_t message_code);
diff --git a/errors.c b/errors.c
index 56a3335..46bb8b8 100644
--- a/errors.c
+++ b/errors.c
@@ -104,6 +104,7 @@ PROGMEM static const status_detail_t status_detail[] = {
{ Status_FlowControlStackOverflow, "Stack overflow while executing flow statement." },
{ Status_FlowControlOutOfMemory, "Out of memory while executing flow statement." },
#endif
+ { Status_FileOpenFailed, "Could not open file." },
{ Status_UserException, "User defined error occured." }
#endif // NO_SETTINGS_DESCRIPTIONS
};
diff --git a/errors.h b/errors.h
index 78aba0c..79adfda 100644
--- a/errors.h
+++ b/errors.h
@@ -112,6 +112,7 @@ typedef enum {
Status_FlowControlSyntaxError = 81,
Status_FlowControlStackOverflow = 82,
Status_FlowControlOutOfMemory = 83,
+ Status_FileOpenFailed = 84,
Status_StatusMax = Status_FlowControlOutOfMemory,
Status_UserException = 253,
Status_Handled, // For internal use only
diff --git a/grbl.h b/grbl.h
index c7b741b..8b8c365 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20250103
+#define GRBL_BUILD 20250104
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/grbllib.c b/grbllib.c
index e97843a..038a81b 100644
--- a/grbllib.c
+++ b/grbllib.c
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2017-2024 Terje Io
+ Copyright (c) 2017-2025 Terje Io
Copyright (c) 2011-2015 Sungeun K. Jeon
Copyright (c) 2009-2011 Simen Svale Skogsrud
@@ -92,6 +92,7 @@ grbl_hal_t hal;
static driver_startup_t driver = { .ok = 0xFF };
static core_task_t task_pool[CORE_TASK_POOL_SIZE] = {0};
static core_task_t *next_task = NULL, *immediate_task = NULL, *systick_task = NULL, *last_freed = NULL;
+static on_linestate_changed_ptr on_linestate_changed;
#ifdef KINEMATICS_API
kinematics_t kinematics;
@@ -166,6 +167,20 @@ ISR_CODE static home_signals_t ISR_FUNC(get_homing_status2)(void)
return home;
}
+static void output_welcome_message (void *data)
+{
+ grbl.report.init_message(hal.stream.write);
+}
+
+static void onLinestateChanged (serial_linestate_t state)
+{
+ if(state.dtr)
+ task_add_delayed(output_welcome_message, NULL, 200);
+
+ if(on_linestate_changed)
+ on_linestate_changed(state);
+}
+
// main entry point
int grbl_enter (void)
@@ -333,6 +348,11 @@ int grbl_enter (void)
setting_remove_elements(Setting_FSOptions, fs_options.mask);
}
+ if(hal.stream.state.linestate_event && !hal.stream.state.passthru) {
+ on_linestate_changed = hal.stream.on_linestate_changed;
+ hal.stream.on_linestate_changed = onLinestateChanged;
+ }
+
// Initialization loop upon power-up or a system abort. For the latter, all processes
// will return to this loop to be cleanly re-initialized.
while(looping) {
@@ -380,7 +400,7 @@ int grbl_enter (void)
tc_init();
// Print welcome message. Indicates an initialization has occurred at power-up or with a reset.
- grbl.report.init_message();
+ grbl.report.init_message(hal.stream.write_all);
if(!settings.flags.no_unlock_after_estop && state_get() == STATE_ESTOP)
state_set(STATE_ALARM);
diff --git a/ngc_flowctrl.c b/ngc_flowctrl.c
index a7e8985..67b11b0 100644
--- a/ngc_flowctrl.c
+++ b/ngc_flowctrl.c
@@ -661,7 +661,7 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo
if(!skipping) {
- ngc_sub_t *sub;
+ ngc_sub_t *sub = NULL;
if(o_label > NGC_MAX_PARAM_ID) {
@@ -684,7 +684,7 @@ status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, boo
if((sub = add_sub(o_label, file)) == NULL)
status = Status_FlowControlOutOfMemory;
} else
- status = Status_FlowControlOutOfMemory; // file not found...
+ status = Status_FileOpenFailed;
}
} else if((sub = subs)) do {
if(sub->o_label == o_label && sub->file == hal.stream.file)
diff --git a/report.c b/report.c
index 98b565b..db15086 100644
--- a/report.c
+++ b/report.c
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2017-2024 Terje Io
+ Copyright (c) 2017-2025 Terje Io
Copyright (c) 2012-2016 Sungeun K. Jeon for Gnea Research LLC
grblHAL is free software: you can redistribute it and/or modify
@@ -323,14 +323,14 @@ static message_code_t report_feedback_message (message_code_t id)
}
// Welcome message
-static void report_init_message (void)
+static void report_init_message (stream_write_ptr write)
{
override_counter = wco_counter = 0;
#if COMPATIBILITY_LEVEL == 0
- hal.stream.write_all(ASCII_EOL "GrblHAL " GRBL_VERSION " ['$' or '$HELP' for help]" ASCII_EOL);
+ write(ASCII_EOL "GrblHAL " GRBL_VERSION " ['$' or '$HELP' for help]" ASCII_EOL);
#else
- hal.stream.write_all(ASCII_EOL "Grbl " GRBL_VERSION " ['$' for help]" ASCII_EOL);
+ write(ASCII_EOL "Grbl " GRBL_VERSION " ['$' for help]" ASCII_EOL);
#endif
}
diff --git a/stream.c b/stream.c
index 0152e43..c0e4b9d 100644
--- a/stream.c
+++ b/stream.c
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2021-2024 Terje Io
+ Copyright (c) 2021-2025 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
@@ -255,12 +255,21 @@ static bool stream_select (const io_stream_t *stream, bool add)
static const io_stream_t *active_stream = NULL;
bool send_init_message = false, mpg_enable = false;
+ static struct {
+ const io_stream_t *stream;
+ on_linestate_changed_ptr on_linestate_changed;
+ } usb = {};
if(stream == base.stream) {
base.is_up = add ? (stream->is_connected ? stream->is_connected : stream_connected) : is_not_connected;
return true;
}
+ if(active_stream != NULL && hal.stream.state.is_usb) {
+ usb.stream = active_stream;
+ usb.on_linestate_changed = hal.stream.on_linestate_changed;
+ }
+
if(!add) { // disconnect
if(stream == base.stream || stream == mpg.stream)
@@ -336,6 +345,9 @@ static bool stream_select (const io_stream_t *stream, bool add)
memcpy(&hal.stream, stream, sizeof(io_stream_t));
+ if(stream == usb.stream)
+ hal.stream.on_linestate_changed = usb.on_linestate_changed;
+
if(stream == base.stream && base.is_up == is_not_connected)
base.is_up = stream_connected;
@@ -345,10 +357,8 @@ static bool stream_select (const io_stream_t *stream, bool add)
if(stream->type == StreamType_WebSocket && !stream->state.webui_connected)
hal.stream.state.webui_connected = webui_connected;
- if(send_init_message) {
- hal.stream.write_all = stream->write;
- grbl.report.init_message();
- }
+ if(send_init_message)
+ grbl.report.init_message(stream->write);
hal.stream.write_all = stream_write_all;
hal.stream.set_enqueue_rt_handler(protocol_enqueue_realtime_command);
diff --git a/stream.h b/stream.h
index 9e81619..a87c033 100644
--- a/stream.h
+++ b/stream.h
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2019-2024 Terje Io
+ Copyright (c) 2019-2025 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
@@ -235,7 +235,8 @@ typedef union {
uint8_t webui_connected :1,
is_usb :1,
linestate_event :1, //!< Set when driver supports on_linestate_changed event.
- unused :5;
+ passthru :1, //!< Set when stream is in passthru mode.
+ unused :4;
};
} io_stream_state_t;
diff --git a/stream_passthru.c b/stream_passthru.c
index f8bbe55..780c75c 100644
--- a/stream_passthru.c
+++ b/stream_passthru.c
@@ -3,7 +3,7 @@
Part of grblHAL
- Copyright (c) 2024 Terje Io
+ Copyright (c) 2024-2025 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
@@ -190,7 +190,7 @@ void stream_passthru_init (uint8_t instance, uint32_t baud_rate, bool start)
io_stream_t const *stream = stream_open_instance(instance, baud_rate, NULL, "Passthru");
- if(stream != NULL) {
+ if((hal.stream.state.passthru = stream != NULL)) {
protocol_enqueue_foreground_task(passthru_start1, NULL); // enter passthrouh mode after finished booting grblHAL