diff --git a/CMakeLists.txt b/CMakeLists.txt
index e619202..cc2763d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,6 +22,7 @@ target_sources(grbl INTERFACE
${CMAKE_CURRENT_LIST_DIR}/state_machine.c
${CMAKE_CURRENT_LIST_DIR}/stream.c
${CMAKE_CURRENT_LIST_DIR}/stream_file.c
+ ${CMAKE_CURRENT_LIST_DIR}/stream_passthru.c
${CMAKE_CURRENT_LIST_DIR}/stepper.c
${CMAKE_CURRENT_LIST_DIR}/stepper2.c
${CMAKE_CURRENT_LIST_DIR}/system.c
diff --git a/changelog.md b/changelog.md
index 47ef8a2..51869f5 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,17 @@
## grblHAL changelog
+Build 20241110
+
+Core:
+
+* Added generic passthru support for programming external MCU via USB > UART bridge. Requires driver support if to be used. Adds `$PTRGH` system command when available.
+
+Plugins:
+
+* Misc, ESP-AT: general improvements.
+
+---
+
Build 20241107
Core:
diff --git a/crossbar.h b/crossbar.h
index d210860..e5b476f 100644
--- a/crossbar.h
+++ b/crossbar.h
@@ -181,6 +181,8 @@ typedef enum {
Output_LED_Adressable,
Output_LED0_Adressable = Output_LED_Adressable,
Output_LED1_Adressable,
+ Output_CoProc_Reset,
+ Output_CoProc_Boot0,
// Multipin peripherals
Input_MISO,
Multipin = Input_MISO,
@@ -382,6 +384,8 @@ PROGMEM static const pin_name_t pin_names[] = {
{ .function = Output_LED_W, .name = "LED W" },
{ .function = Output_LED_Adressable, .name = "LED adressable" },
{ .function = Output_LED1_Adressable, .name = "LED adressable 1" },
+ { .function = Output_CoProc_Reset, .name = "CoProc Reset" },
+ { .function = Output_CoProc_Boot0, .name = "CoProc Boot0" },
{ .function = Input_MISO, .name = "MISO" },
{ .function = Output_MOSI, .name = "MOSI" },
{ .function = Output_SPICLK, .name = "SPI CLK" },
diff --git a/grbl.h b/grbl.h
index 76ddaca..051c780 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20241107
+#define GRBL_BUILD 20241110
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/pin_bits_masks.h b/pin_bits_masks.h
index 459028d..6be6918 100644
--- a/pin_bits_masks.h
+++ b/pin_bits_masks.h
@@ -397,23 +397,29 @@ static inline control_signals_t aux_ctrl_scan_status (control_signals_t signals)
// The following pins are bound explicitly to aux output pins
static aux_ctrl_out_t aux_ctrl_out[] = {
#if DRIVER_SPINDLE_ENABLE
- { .function = Output_SpindleOn, .aux_port = 0xFF, .pin = SPINDLE_ENABLE_PIN, .port = SPINDLE_ENABLE_PORT },
+ { .function = Output_SpindleOn, .aux_port = 0xFF, .pin = SPINDLE_ENABLE_PIN, .port = SPINDLE_ENABLE_PORT },
#if DRIVER_SPINDLE_PWM_ENABLE
- { .function = Output_SpindlePWM, .aux_port = 0xFF, .pin = SPINDLE_PWM_PIN, .port = SPINDLE_PWM_PORT },
+ { .function = Output_SpindlePWM, .aux_port = 0xFF, .pin = SPINDLE_PWM_PIN, .port = SPINDLE_PWM_PORT },
#endif
#if DRIVER_SPINDLE_DIR_ENABLE
- { .function = Output_SpindleDir, .aux_port = 0xFF, .pin = SPINDLE_DIRECTION_PIN, .port = SPINDLE_DIRECTION_PORT },
+ { .function = Output_SpindleDir, .aux_port = 0xFF, .pin = SPINDLE_DIRECTION_PIN, .port = SPINDLE_DIRECTION_PORT },
#endif
#endif // DRIVER_SPINDLE_ENABLE
#if DRIVER_SPINDLE1_ENABLE
- { .function = Output_Spindle1On, .aux_port = 0xFF, .pin = SPINDLE1_ENABLE_PIN, .port = SPINDLE1_ENABLE_PORT },
+ { .function = Output_Spindle1On, .aux_port = 0xFF, .pin = SPINDLE1_ENABLE_PIN, .port = SPINDLE1_ENABLE_PORT },
#if DRIVER_SPINDLE1_PWM_ENABLE
- { .function = Output_Spindle1PWM, .aux_port = 0xFF, .pin = SPINDLE1_PWM_PIN, .port = SPINDLE1_PWM_PORT },
+ { .function = Output_Spindle1PWM, .aux_port = 0xFF, .pin = SPINDLE1_PWM_PIN, .port = SPINDLE1_PWM_PORT },
#endif
#if DRIVER_SPINDLE1_DIR_ENABLE
- { .function = Output_Spindle1Dir, .aux_port = 0xFF, .pin = SPINDLE1_DIRECTION_PIN, .port = SPINDLE1_DIRECTION_PORT },
+ { .function = Output_Spindle1Dir, .aux_port = 0xFF, .pin = SPINDLE1_DIRECTION_PIN, .port = SPINDLE1_DIRECTION_PORT },
#endif
#endif // DRIVER_SPINDLE1_DIR_ENABLE
+#ifdef COPROC_RESET_PIN
+ { .function = Output_CoProc_Reset, .aux_port = 0xFF, .pin = COPROC_RESET_PIN, .port = COPROC_RESET_PORT },
+#endif
+#ifdef COPROC_BOOT0_PIN
+ { .function = Output_CoProc_Boot0, .aux_port = 0xFF, .pin = COPROC_BOOT0_PIN, .port = COPROC_BOOT0_PORT },
+#endif
/*
#ifdef COOLANT_FLOOD_PIN
{ .function = Output_CoolantFlood, .aux_port = 0xFF, .pin = COOLANT_FLOOD_PIN, .port = COOLANT_FLOOD_PORT },
diff --git a/stream_passthru.c b/stream_passthru.c
new file mode 100644
index 0000000..f8bbe55
--- /dev/null
+++ b/stream_passthru.c
@@ -0,0 +1,206 @@
+/*
+ stream_passthru.c - stream redirector for programming coprocessor
+
+ Part of grblHAL
+
+ Copyright (c) 2024 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 .
+*/
+
+#include "hal.h"
+#include "task.h"
+#include "protocol.h"
+
+//static xbar_t *dtr, *rts;
+static uint8_t boot0_port = 0xFF, reset_port = 0xFF;
+static io_stream_t dest;
+static bool conn_ok = false;
+static on_linestate_changed_ptr on_linestate_changed;
+//static on_execute_realtime_ptr on_execute_realtime = NULL;
+
+// Weak implementation of low level function to be provided by the driver
+
+__attribute__((weak)) void stream_passthru_enter (void)
+{
+ // The driver implementation calls stream_passthru_init() with start parameter set true after setting up for execution.
+}
+
+// ****
+
+
+/*
+static void onExecuteRealtime (uint_fast16_t state)
+{
+ static bool lock = false;
+
+ int16_t c;
+
+ if(!lock) {
+ lock = true;
+ if((c = hal.stream.read()) != SERIAL_NO_DATA)
+ dest.write_char(c);
+ lock = false;
+ }
+
+ on_execute_realtime(state);
+}
+*/
+
+ISR_CODE static bool ISR_FUNC(forward_usb_rx)(char c)
+{
+ dest.write_char(c);
+
+ return true;
+}
+
+ISR_CODE static bool ISR_FUNC(sink_uart_rx)(char c)
+{
+ return true;
+}
+
+static void forward_uart_rx (void *data)
+{
+ static char buf[64];
+ static uint_fast8_t idx = 0;
+
+ int16_t c;
+
+ while((c = dest.read()) != SERIAL_NO_DATA) {
+
+ idx = 1;
+ *buf = c;
+
+ while(idx < sizeof(buf) && (c = dest.read()) != SERIAL_NO_DATA)
+ buf[idx++] = c;
+
+ hal.stream.write_n(buf, idx);
+ }
+
+ task_add_delayed(forward_uart_rx, NULL, 8);
+}
+
+static void onLinestateChanged (serial_linestate_t state)
+{
+ /*
+ Auto program
+ DTR RTS-->EN IO0
+ 1 1 1 1
+ 0 0 1 1
+ 1 0 0 1
+ 0 1 1 0
+ */
+ if(conn_ok) {
+
+ if(state.dtr == state.rts) {
+ hal.port.digital_out(boot0_port, 0);
+ hal.port.digital_out(reset_port, 1);
+ } else if(state.dtr) {
+ hal.port.digital_out(reset_port, 0);
+ hal.port.digital_out(boot0_port, 0);
+ } else {
+ hal.port.digital_out(boot0_port, 0);
+ hal.port.digital_out(reset_port, 1);
+ }
+ }
+}
+
+static void passthru_start2 (void *data)
+{
+ conn_ok = true;
+ hal.port.digital_out(reset_port, 1);
+
+ task_add_delayed(forward_uart_rx, NULL, 8);
+
+// on_execute_realtime = grbl.on_execute_realtime;
+// grbl.on_execute_realtime = onExecuteRealtime;
+
+ dest.set_enqueue_rt_handler(stream_buffer_all);
+ dest.cancel_read_buffer();
+}
+
+static void passthru_start1 (void *data)
+{
+ hal.port.digital_out(boot0_port, 0);
+ hal.port.digital_out(reset_port, 0);
+
+ on_linestate_changed = hal.stream.on_linestate_changed;
+ hal.stream.on_linestate_changed = onLinestateChanged;
+
+ task_add_delayed(passthru_start2, NULL, 1250); // delay a bit to allow the USB stack to start
+}
+
+static bool get_ports (xbar_t *properties, uint8_t port, void *data)
+{
+ switch(properties->function) {
+
+ case Output_CoProc_Reset:
+ reset_port = port;
+ break;
+
+ case Output_CoProc_Boot0:
+ boot0_port = port;
+ break;
+
+ default:
+ break;
+ }
+
+ return boot0_port != 0xFF && reset_port != 0xFF;
+}
+
+static status_code_t passthru_enter (sys_state_t state, char *args)
+{
+ report_message("Entering passthru mode", Message_Warning);
+ hal.delay_ms(100, NULL);
+
+ stream_passthru_enter();
+
+ return Status_OK;
+}
+
+void stream_passthru_init (uint8_t instance, uint32_t baud_rate, bool start)
+{
+ static const sys_command_t passthru_command_list[] = {
+ {"PTRGH", passthru_enter, { .noargs = On }, { .str = "enter passthru mode" } }
+ };
+
+ static sys_commands_t passthru_commands = {
+ .n_commands = sizeof(passthru_command_list) / sizeof(sys_command_t),
+ .commands = passthru_command_list
+ };
+
+ if(hal.stream.type == StreamType_Serial &&
+ hal.stream.state.is_usb &&
+ hal.stream.state.linestate_event &&
+ ioports_enumerate(Port_Digital, Port_Output, (pin_cap_t){ .output = On }, get_ports, NULL)) {
+
+ if(start) {
+
+ io_stream_t const *stream = stream_open_instance(instance, baud_rate, NULL, "Passthru");
+
+ if(stream != NULL) {
+
+ protocol_enqueue_foreground_task(passthru_start1, NULL); // enter passthrouh mode after finished booting grblHAL
+
+ memcpy(&dest, stream, sizeof(io_stream_t));
+ dest.set_enqueue_rt_handler(sink_uart_rx);
+ hal.stream.set_enqueue_rt_handler(forward_usb_rx);
+ } else
+ report_message("Entering passthru mode failed!", Message_Warning);
+
+ } else
+ system_register_commands(&passthru_commands);
+ }
+}
diff --git a/stream_passthru.h b/stream_passthru.h
new file mode 100644
index 0000000..e9caadd
--- /dev/null
+++ b/stream_passthru.h
@@ -0,0 +1,22 @@
+/*
+ stream_passthru.h - stream redirector for programming coprocessor
+
+ Part of grblHAL
+
+ Copyright (c) 2024 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 .
+*/
+
+void stream_passthru_init (uint8_t instance, uint32_t baud_rate, bool start);