diff --git a/changelog.md b/changelog.md index 12ad356..d33583b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,27 @@ ## grblHAL changelog +20230312 + +Core: + +* Added event definition for SD card file open, fix for issue #118. + +Drivers: + +* ESP32: Added aux I/O and cycle start/feed hold inputs to MKS DLC32 board, expanded max aux out to 4. +__NOTE:__ The aux input 0 port on the MKS DLC32 board does not have an internal pullup. + +Plugins: + +* SD card: added publish for file open event allowing plugins to take over stream handling. Added `$F+` command for listing all files on card. + +--- + 20230311 Core: -* Fix for isisue #264, stepper motors not disabled when entering sleep mode. +* Fix for issue #264, stepper motors not disabled when entering sleep mode. __NOTE:__ all stepper motors will now be disabled even if the $37 setting is set to keep some enabled. * Fix for recent regression that disabled G7/G8 handling in lathe mode. diff --git a/core_handlers.h b/core_handlers.h index f1092ad..8a91895 100644 --- a/core_handlers.h +++ b/core_handlers.h @@ -33,6 +33,7 @@ #include "report.h" #include "planner.h" #include "machine_limits.h" +#include "vfs.h" typedef enum { OverrideChanged_FeedRate = 0, @@ -107,6 +108,7 @@ 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 void (*on_rt_reports_added_ptr)(report_tracking_flags_t report); +typedef status_code_t (*on_file_open_ptr)(const char *fname, vfs_file_t *handle, bool stream); typedef status_code_t (*on_unknown_sys_command_ptr)(sys_state_t state, char *line); // return Status_Unhandled. typedef status_code_t (*on_user_command_ptr)(char *line); typedef sys_commands_t *(*on_get_commands_ptr)(void); @@ -148,9 +150,10 @@ typedef struct { on_toolchange_ack_ptr on_toolchange_ack; //!< Called from interrupt context. on_jog_cancel_ptr on_jog_cancel; //!< Called from interrupt context. on_laser_ppi_enable_ptr on_laser_ppi_enable; - on_spindle_select_ptr on_spindle_select; //!< Called before spindle is selected, hook in HAL overrides here - on_spindle_selected_ptr on_spindle_selected; //!< Called when spindle is selected, do not change HAL pointers here! + on_spindle_select_ptr on_spindle_select; //!< Called before spindle is selected, hook in HAL overrides here + on_spindle_selected_ptr on_spindle_selected; //!< Called when spindle is selected, do not change HAL pointers here! on_reset_ptr on_reset; //!< Called from interrupt context. + on_file_open_ptr on_file_open; //!< Called when a file is opened for streaming. // core entry points - set up by core before driver_init() is called. enqueue_gcode_ptr enqueue_gcode; enqueue_realtime_command_ptr enqueue_realtime_command; diff --git a/gcode.c b/gcode.c index c583fef..d8c40ab 100644 --- a/gcode.c +++ b/gcode.c @@ -1014,7 +1014,7 @@ status_code_t gc_execute_block (char *block) word_bit.modal_group.M7 = On; gc_block.modal.spindle.state.on = !(int_value == 5); gc_block.modal.spindle.state.ccw = int_value == 4; - sys.flags.delay_overrides = On; + sys.override_delay.spindle = On; break; case 6: @@ -1028,7 +1028,7 @@ status_code_t gc_execute_block (char *block) case 7: case 8: case 9: word_bit.modal_group.M8 = On; - sys.flags.delay_overrides = On; + sys.override_delay.coolant = On; gc_parser_flags.set_coolant = On; switch(int_value) { @@ -2856,7 +2856,7 @@ status_code_t gc_execute_block (char *block) plan_data.condition.coolant = gc_state.modal.coolant; // Set condition flag for planner use. - sys.flags.delay_overrides = Off; + sys.override_delay.flags = 0; // [9. Override control ]: if (gc_state.modal.override_ctrl.value != gc_block.modal.override_ctrl.value) { diff --git a/grbl.h b/grbl.h index 599bee5..e1542f5 100644 --- a/grbl.h +++ b/grbl.h @@ -42,7 +42,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20230311 +#define GRBL_BUILD 20230312 #define GRBL_URL "https://github.com/grblHAL" diff --git a/override.c b/override.c index 6d47c08..490592b 100644 --- a/override.c +++ b/override.c @@ -5,7 +5,7 @@ Part of grblHAL - Copyright (c) 2017-2019 Terje Io + Copyright (c) 2017-2023 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,7 +30,7 @@ typedef struct { uint8_t buf[OVERRIDE_BUFSIZE]; } override_queue_t; -static override_queue_t feed = {0}, accessory = {0}; +static override_queue_t feed = {0}, spindle = {0}, coolant = {0}; ISR_CODE void ISR_FUNC(enqueue_feed_override)(uint8_t cmd) { @@ -56,30 +56,55 @@ uint8_t get_feed_override (void) return data; } -ISR_CODE void ISR_FUNC(enqueue_accessory_override)(uint8_t cmd) +ISR_CODE void ISR_FUNC(enqueue_spindle_override)(uint8_t cmd) { - uint_fast8_t bptr = (accessory.head + 1) & (OVERRIDE_BUFSIZE - 1); // Get next head pointer + uint_fast8_t bptr = (spindle.head + 1) & (OVERRIDE_BUFSIZE - 1); // Get next head pointer - if(bptr != accessory.tail) { // If not buffer full - accessory.buf[accessory.head] = cmd; // add data to buffer - accessory.head = bptr; // and update pointer + if(bptr != spindle.tail) { // If not buffer full + spindle.buf[spindle.head] = cmd; // add data to buffer + spindle.head = bptr; // and update pointer } } // Returns 0 if no commands enqueued -uint8_t get_accessory_override (void) +uint8_t get_spindle_override (void) { uint8_t data = 0; - uint_fast8_t bptr = accessory.tail; + uint_fast8_t bptr = spindle.tail; - if(bptr != accessory.head) { - data = accessory.buf[bptr++]; // Get next character, increment tmp pointer - accessory.tail = bptr & (OVERRIDE_BUFSIZE - 1); // and update pointer + if(bptr != spindle.head) { + data = spindle.buf[bptr++]; // Get next character, increment tmp pointer + spindle.tail = bptr & (OVERRIDE_BUFSIZE - 1); // and update pointer } return data; } -void flush_override_buffers () { - feed.head = feed.tail = accessory.head = accessory.tail = 0; +ISR_CODE void ISR_FUNC(enqueue_coolant_override)(uint8_t cmd) +{ + uint_fast8_t bptr = (coolant.head + 1) & (OVERRIDE_BUFSIZE - 1); // Get next head pointer + + if(bptr != coolant.tail) { // If not buffer full + coolant.buf[coolant.head] = cmd; // add data to buffer + coolant.head = bptr; // and update pointer + } +} + +// Returns 0 if no commands enqueued +uint8_t get_coolant_override (void) +{ + uint8_t data = 0; + uint_fast8_t bptr = coolant.tail; + + if(bptr != coolant.head) { + data = coolant.buf[bptr++]; // Get next character, increment tmp pointer + coolant.tail = bptr & (OVERRIDE_BUFSIZE - 1); // and update pointer + } + + return data; +} + +void flush_override_buffers (void) +{ + feed.head = feed.tail = spindle.head = spindle.tail = coolant.head = coolant.tail = 0; } diff --git a/override.h b/override.h index 990ccab..88ca449 100644 --- a/override.h +++ b/override.h @@ -5,7 +5,7 @@ Part of grblHAL - Copyright (c) 2016-2019 Terje Io + Copyright (c) 2016-2023 Terje Io Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,7 +31,9 @@ void flush_override_buffers (); void enqueue_feed_override (uint8_t cmd); uint8_t get_feed_override (void); -void enqueue_accessory_override (uint8_t cmd); -uint8_t get_accessory_override (void); +void enqueue_spindle_override (uint8_t cmd); +uint8_t get_spindle_override (void); +void enqueue_coolant_override (uint8_t cmd); +uint8_t get_coolant_override (void); #endif diff --git a/plugins_init.h b/plugins_init.h index da717b4..d73bd59 100644 --- a/plugins_init.h +++ b/plugins_init.h @@ -85,6 +85,11 @@ webui_init(); #endif +#if EMBROIDERY_ENABLE + extern void embroidery_init (void); + embroidery_init(); +#endif + extern void my_plugin_init (void); my_plugin_init(); diff --git a/protocol.c b/protocol.c index 059bdcf..fc13514 100644 --- a/protocol.c +++ b/protocol.c @@ -495,7 +495,7 @@ bool protocol_exec_rt_system (void) sys.cancel = true; sys.step_control.flags = 0; sys.flags.feed_hold_pending = Off; - sys.flags.delay_overrides = Off; + sys.override_delay.flags = 0; if(sys.override.control.sync) sys.override.control = gc_state.modal.override_ctrl; @@ -562,137 +562,152 @@ bool protocol_exec_rt_system (void) grbl.on_execute_realtime(state_get()); - if(!sys.flags.delay_overrides) { + // Execute overrides. - // Execute overrides. + if(!sys.override_delay.feedrate && (rt_exec = get_feed_override())) { - if((rt_exec = get_feed_override())) { + override_t new_f_override = sys.override.feed_rate; + override_t new_r_override = sys.override.rapid_rate; - override_t new_f_override = sys.override.feed_rate; - override_t new_r_override = sys.override.rapid_rate; + do { - do { + switch(rt_exec) { - switch(rt_exec) { + case CMD_OVERRIDE_FEED_RESET: + new_f_override = DEFAULT_FEED_OVERRIDE; + break; - case CMD_OVERRIDE_FEED_RESET: - new_f_override = DEFAULT_FEED_OVERRIDE; - break; + case CMD_OVERRIDE_FEED_COARSE_PLUS: + new_f_override += FEED_OVERRIDE_COARSE_INCREMENT; + break; - case CMD_OVERRIDE_FEED_COARSE_PLUS: - new_f_override += FEED_OVERRIDE_COARSE_INCREMENT; - break; + case CMD_OVERRIDE_FEED_COARSE_MINUS: + new_f_override -= FEED_OVERRIDE_COARSE_INCREMENT; + break; - case CMD_OVERRIDE_FEED_COARSE_MINUS: - new_f_override -= FEED_OVERRIDE_COARSE_INCREMENT; - break; + case CMD_OVERRIDE_FEED_FINE_PLUS: + new_f_override += FEED_OVERRIDE_FINE_INCREMENT; + break; - case CMD_OVERRIDE_FEED_FINE_PLUS: - new_f_override += FEED_OVERRIDE_FINE_INCREMENT; - break; + case CMD_OVERRIDE_FEED_FINE_MINUS: + new_f_override -= FEED_OVERRIDE_FINE_INCREMENT; + break; - case CMD_OVERRIDE_FEED_FINE_MINUS: - new_f_override -= FEED_OVERRIDE_FINE_INCREMENT; - break; + case CMD_OVERRIDE_RAPID_RESET: + new_r_override = DEFAULT_RAPID_OVERRIDE; + break; - case CMD_OVERRIDE_RAPID_RESET: - new_r_override = DEFAULT_RAPID_OVERRIDE; - break; + case CMD_OVERRIDE_RAPID_MEDIUM: + new_r_override = RAPID_OVERRIDE_MEDIUM; + break; - case CMD_OVERRIDE_RAPID_MEDIUM: - new_r_override = RAPID_OVERRIDE_MEDIUM; - break; + case CMD_OVERRIDE_RAPID_LOW: + new_r_override = RAPID_OVERRIDE_LOW; + break; + } - case CMD_OVERRIDE_RAPID_LOW: - new_r_override = RAPID_OVERRIDE_LOW; - break; - } + new_f_override = constrain(new_f_override, MIN_FEED_RATE_OVERRIDE, MAX_FEED_RATE_OVERRIDE); - new_f_override = constrain(new_f_override, MIN_FEED_RATE_OVERRIDE, MAX_FEED_RATE_OVERRIDE); + } while((rt_exec = get_feed_override())); - } while((rt_exec = get_feed_override())); + plan_feed_override(new_f_override, new_r_override); + } - plan_feed_override(new_f_override, new_r_override); - } + if(!sys.override_delay.spindle && (rt_exec = get_spindle_override())) { - if((rt_exec = get_accessory_override())) { + bool spindle_stop = false; + spindle_ptrs_t *spindle = gc_spindle_get(); + override_t last_s_override = spindle->param->override_pct; - bool spindle_stop = false; - spindle_ptrs_t *spindle = gc_spindle_get(); - override_t last_s_override = spindle->param->override_pct; - coolant_state_t coolant_state = gc_state.modal.coolant; + do { - do { + switch(rt_exec) { - switch(rt_exec) { + case CMD_OVERRIDE_SPINDLE_RESET: + last_s_override = DEFAULT_SPINDLE_RPM_OVERRIDE; + break; - case CMD_OVERRIDE_SPINDLE_RESET: - last_s_override = DEFAULT_SPINDLE_RPM_OVERRIDE; - break; + case CMD_OVERRIDE_SPINDLE_COARSE_PLUS: + last_s_override += SPINDLE_OVERRIDE_COARSE_INCREMENT; + break; - case CMD_OVERRIDE_SPINDLE_COARSE_PLUS: - last_s_override += SPINDLE_OVERRIDE_COARSE_INCREMENT; - break; + case CMD_OVERRIDE_SPINDLE_COARSE_MINUS: + last_s_override -= SPINDLE_OVERRIDE_COARSE_INCREMENT; + break; - case CMD_OVERRIDE_SPINDLE_COARSE_MINUS: - last_s_override -= SPINDLE_OVERRIDE_COARSE_INCREMENT; - break; + case CMD_OVERRIDE_SPINDLE_FINE_PLUS: + last_s_override += SPINDLE_OVERRIDE_FINE_INCREMENT; + break; - case CMD_OVERRIDE_SPINDLE_FINE_PLUS: - last_s_override += SPINDLE_OVERRIDE_FINE_INCREMENT; - break; + case CMD_OVERRIDE_SPINDLE_FINE_MINUS: + last_s_override -= SPINDLE_OVERRIDE_FINE_INCREMENT; + break; - case CMD_OVERRIDE_SPINDLE_FINE_MINUS: - last_s_override -= SPINDLE_OVERRIDE_FINE_INCREMENT; - break; + case CMD_OVERRIDE_SPINDLE_STOP: + spindle_stop = !spindle_stop; + break; - case CMD_OVERRIDE_SPINDLE_STOP: - spindle_stop = !spindle_stop; - break; + default: + if(grbl.on_unknown_accessory_override) + grbl.on_unknown_accessory_override(rt_exec); + break; + } - case CMD_OVERRIDE_COOLANT_MIST_TOGGLE: - if (hal.driver_cap.mist_control && ((state_get() == STATE_IDLE) || (state_get() & (STATE_CYCLE | STATE_HOLD)))) { - coolant_state.mist = !coolant_state.mist; - } - break; + last_s_override = constrain(last_s_override, MIN_SPINDLE_RPM_OVERRIDE, MAX_SPINDLE_RPM_OVERRIDE); - case CMD_OVERRIDE_COOLANT_FLOOD_TOGGLE: - if ((state_get() == STATE_IDLE) || (state_get() & (STATE_CYCLE | STATE_HOLD))) { - coolant_state.flood = !coolant_state.flood; - } - break; + } while((rt_exec = get_spindle_override())); - default: - if(grbl.on_unknown_accessory_override) - grbl.on_unknown_accessory_override(rt_exec); - break; - } + spindle_set_override(spindle, last_s_override); - last_s_override = constrain(last_s_override, MIN_SPINDLE_RPM_OVERRIDE, MAX_SPINDLE_RPM_OVERRIDE); + if (spindle_stop && state_get() == STATE_HOLD && gc_state.modal.spindle.state.on) { + // Spindle stop override allowed only while in HOLD state. + // NOTE: Report flag is set in spindle_set_state() when spindle stop is executed. + if (!sys.override.spindle_stop.value) + sys.override.spindle_stop.initiate = On; + else if (sys.override.spindle_stop.enabled) + sys.override.spindle_stop.restore = On; + } + } - } while((rt_exec = get_accessory_override())); + if(!sys.override_delay.coolant && (rt_exec = get_coolant_override())) { - spindle_set_override(spindle, last_s_override); + coolant_state_t coolant_state = gc_state.modal.coolant; - // NOTE: Since coolant state always performs a planner sync whenever it changes, the current - // run state can be determined by checking the parser state. - if(coolant_state.value != gc_state.modal.coolant.value) { - coolant_set_state(coolant_state); // Report flag set in coolant_set_state(). - gc_state.modal.coolant = coolant_state; - if(grbl.on_override_changed) - grbl.on_override_changed(OverrideChanged_CoolantState); - } + do { + + switch(rt_exec) { - if (spindle_stop && state_get() == STATE_HOLD && gc_state.modal.spindle.state.on) { - // Spindle stop override allowed only while in HOLD state. - // NOTE: Report flag is set in spindle_set_state() when spindle stop is executed. - if (!sys.override.spindle_stop.value) - sys.override.spindle_stop.initiate = On; - else if (sys.override.spindle_stop.enabled) - sys.override.spindle_stop.restore = On; + case CMD_OVERRIDE_COOLANT_MIST_TOGGLE: + if (hal.driver_cap.mist_control && ((state_get() == STATE_IDLE) || (state_get() & (STATE_CYCLE | STATE_HOLD)))) { + coolant_state.mist = !coolant_state.mist; + } + break; + + case CMD_OVERRIDE_COOLANT_FLOOD_TOGGLE: + if ((state_get() == STATE_IDLE) || (state_get() & (STATE_CYCLE | STATE_HOLD))) { + coolant_state.flood = !coolant_state.flood; + } + break; + + default: + if(grbl.on_unknown_accessory_override) + grbl.on_unknown_accessory_override(rt_exec); + break; } + + } while((rt_exec = get_coolant_override())); + + // NOTE: Since coolant state always performs a planner sync whenever it changes, the current + // run state can be determined by checking the parser state. + if(coolant_state.value != gc_state.modal.coolant.value) { + coolant_set_state(coolant_state); // Report flag set in coolant_set_state(). + gc_state.modal.coolant = coolant_state; + if(grbl.on_override_changed) + grbl.on_override_changed(OverrideChanged_CoolantState); } - } // End execute overrides. + } + + // End execute overrides. // Reload step segment buffer if (state_get() & (STATE_CYCLE | STATE_HOLD | STATE_SAFETY_DOOR | STATE_HOMING | STATE_SLEEP| STATE_JOG)) @@ -876,11 +891,15 @@ ISR_CODE bool ISR_FUNC(protocol_enqueue_realtime_command)(char c) case CMD_OVERRIDE_SPINDLE_FINE_PLUS: case CMD_OVERRIDE_SPINDLE_FINE_MINUS: case CMD_OVERRIDE_SPINDLE_STOP: + drop = true; + enqueue_spindle_override((uint8_t)c); + break; + case CMD_OVERRIDE_COOLANT_FLOOD_TOGGLE: case CMD_OVERRIDE_COOLANT_MIST_TOGGLE: case CMD_OVERRIDE_FAN0_TOGGLE: drop = true; - enqueue_accessory_override((uint8_t)c); + enqueue_coolant_override((uint8_t)c); break; case CMD_REBOOT: diff --git a/settings.h b/settings.h index 5f0130c..a5032d2 100644 --- a/settings.h +++ b/settings.h @@ -709,6 +709,7 @@ typedef enum { Group_MotorDriver, Group_VFD, Group_CANbus, + Group_Embroidery, Group_Axis, // NOTE: axis groups MUST be sequential AND last Group_Axis0, diff --git a/state_machine.c b/state_machine.c index b85ae00..ebfe0bb 100644 --- a/state_machine.c +++ b/state_machine.c @@ -163,7 +163,7 @@ static bool initiate_hold (uint_fast16_t new_state) restore_condition.coolant.mask = gc_state.modal.coolant.mask | hal.coolant.get_state().mask; if (restore_condition.spindle[restore_condition.spindle_num].hal->cap.laser && settings.flags.disable_laser_during_hold) - enqueue_accessory_override(CMD_OVERRIDE_SPINDLE_STOP); + enqueue_spindle_override(CMD_OVERRIDE_SPINDLE_STOP); if (sys_state & (STATE_CYCLE|STATE_JOG)) { st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration. diff --git a/system.h b/system.h index cbef6f8..24ff621 100644 --- a/system.h +++ b/system.h @@ -219,21 +219,30 @@ typedef struct { gc_override_flags_t control; //!< Tracks override control states. } overrides_t; +typedef union { + uint8_t flags; + struct { + uint16_t feedrate :1, + coolant :1, + spindle :1, + unused :5; + }; +} system_override_delay_t; + typedef union { uint16_t value; struct { - uint16_t mpg_mode :1, //!< MPG mode flag. Set when switched to secondary input stream. (unused for now). - probe_succeeded :1, //!< Tracks if last probing cycle was successful. - soft_limit :1, //!< Tracks soft limit errors for the state machine. - exit :1, //!< System exit flag. Used in combination with abort to terminate main loop. - block_delete_enabled :1, //!< Set to true to enable block delete. - feed_hold_pending :1, - delay_overrides :1, - optional_stop_disable :1, - single_block :1, //!< Set to true to disable M1 (optional stop), via realtime command. - keep_input :1, //!< Set to true to not flush stream input buffer on executing STOP. - auto_reporting :1, //!< Set to true when auto real time reporting is enabled. - unused :5; + uint16_t mpg_mode :1, //!< MPG mode flag. Set when switched to secondary input stream. (unused for now). + probe_succeeded :1, //!< Tracks if last probing cycle was successful. + soft_limit :1, //!< Tracks soft limit errors for the state machine. + exit :1, //!< System exit flag. Used in combination with abort to terminate main loop. + block_delete_enabled :1, //!< Set to true to enable block delete. + feed_hold_pending :1, + optional_stop_disable :1, + single_block :1, //!< Set to true to disable M1 (optional stop), via realtime command. + keep_input :1, //!< Set to true to not flush stream input buffer on executing STOP. + auto_reporting :1, //!< Set to true when auto real time reporting is enabled. + unused :6; }; } system_flags_t; @@ -260,6 +269,7 @@ typedef struct system { axes_signals_t homing_axis_lock; //!< Locks axes when limits engage. Used as an axis motion mask in the stepper ISR. axes_signals_t homing; //!< Axes with homing enabled. overrides_t override; //!< Override values & states + system_override_delay_t override_delay; //!< Flags for delayed overrides. report_tracking_flags_t report; //!< Tracks when to add data to status reports. parking_state_t parking_state; //!< Tracks parking state hold_state_t holding_state; //!< Tracks holding state diff --git a/task.c b/task.c new file mode 100644 index 0000000..bab016e --- /dev/null +++ b/task.c @@ -0,0 +1,59 @@ +#include "core_handlers.h" +/* +typedef struct { + volatile uint_fast8_t head; + volatile uint_fast8_t tail; + on_execute_realtime_ptr fn[RT_QUEUE_SIZE]; +} realtime_queue_t; + +static realtime_queue_t realtime_queue = {0}; + +// Enqueue a function to be called once by the +// foreground process, typically enqueued from an interrupt handler. +ISR_CODE bool ISR_FUNC(grbl_task_enqueue)(on_execute_realtime_ptr fn) +{ + bool ok; + uint_fast8_t bptr = (realtime_queue.head + 1) & (RT_QUEUE_SIZE - 1); // Get next head pointer + + if((ok = bptr != realtime_queue.tail)) { // If not buffer full + realtime_queue.fn[realtime_queue.head] = fn; // add function pointer to buffer, + realtime_queue.head = bptr; // update pointer and + system_set_exec_state_flag(EXEC_RT_COMMAND); // flag it for execute + } + + return ok; +} + +// Enqueue a function to be called once by the +// foreground process, typically enqueued from an interrupt handler. +ISR_CODE bool ISR_FUNC(grbl_task_enqueue_delayed)(on_execute_realtime_ptr fn, uint32_t ms_delay, void *context) +{ + bool ok; + uint_fast8_t bptr = (realtime_queue.head + 1) & (RT_QUEUE_SIZE - 1); // Get next head pointer + + if((ok = bptr != realtime_queue.tail)) { // If not buffer full + realtime_queue.fn[realtime_queue.head] = fn; // add function pointer to buffer, + realtime_queue.head = bptr; // update pointer and + system_set_exec_state_flag(EXEC_RT_COMMAND); // flag it for execute + } + + return ok; +} + +// Execute enqueued functions. +static void grbl_task_execute (void) +{ + while(realtime_queue.tail != realtime_queue.head) { + uint_fast8_t bptr = realtime_queue.tail; + on_execute_realtime_ptr call; + if((call = realtime_queue.fn[bptr])) { + realtime_queue.fn[bptr] = NULL; + call(state_get()); + } + realtime_queue.tail = (bptr + 1) & (RT_QUEUE_SIZE - 1); + } + + if(!sys.driver_started) + while(true); +} +*/