diff --git a/CMakeLists.txt b/CMakeLists.txt index f0340c4..b4f695c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ target_sources(grbl INTERFACE ${CMAKE_CURRENT_LIST_DIR}/ngc_flowctrl.c ${CMAKE_CURRENT_LIST_DIR}/regex.c ${CMAKE_CURRENT_LIST_DIR}/ioports.c + ${CMAKE_CURRENT_LIST_DIR}/utf8.c ${CMAKE_CURRENT_LIST_DIR}/vfs.c ${CMAKE_CURRENT_LIST_DIR}/canbus.c ${CMAKE_CURRENT_LIST_DIR}/pid.c diff --git a/README.md b/README.md index 8fd4f34..1d25f7a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## grblHAL ## -Latest build date is 20251109, see the [changelog](changelog.md) for details. +Latest build date is 20251130, 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. diff --git a/alarms.c b/alarms.c index 6768db3..2202d40 100644 --- a/alarms.c +++ b/alarms.c @@ -7,18 +7,18 @@ Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud - 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 . */ #include diff --git a/alarms.h b/alarms.h index 9d66209..05d35f9 100644 --- a/alarms.h +++ b/alarms.h @@ -3,22 +3,22 @@ Part of grblHAL - Copyright (c) 2017-2023 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 - 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 . */ #ifndef _ALARMS_H_ @@ -67,5 +67,13 @@ alarm_details_t *alarms_get_details (void); const char *alarms_get_description (alarm_code_t id); void alarms_register (alarm_details_t *details); -#endif +static inline bool alarm_is_critical (alarm_code_t alarm) +{ + return alarm == Alarm_HardLimit || + alarm == Alarm_SoftLimit || + alarm == Alarm_EStop || + alarm == Alarm_MotorFault || + alarm == Alarm_ExpanderException; +} +#endif diff --git a/changelog.md b/changelog.md index d84f563..d56216c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,36 @@ ## grblHAL changelog +Build 20251130 + +Core: + +* Updated to allow Bluetooth serial streams to be used for MPG/pendants. + +* Parking mode improvements. + +* Removed requirement for external encoder for spindle sync if stepper spindle is enabled. + +* Improved handling of `$680` stepper enable delay. + +Drivers: + +* ESP32, RP2040: Updated native Bluetooth implementations to allow Bluetooth serial streams to be used for MPG/pendants. + +* RP2040: updated LED strip code to use non-blocking DMA. +Extended spindle PWM frequency range down to ~10Hz. Ref. discussion [#155](https://github.com/grblHAL/RP2040/discussions/155). + +* STM32F3xx: fixed compiler errors and warnings for generic board, ref. issue [#5](https://github.com/grblHAL/STM32F3xx/issues/5). + +* STM32F4xx: refactored LED strip drivers, now supports up to two strips via DMA based PWM. + +Plugins: + +* Bluetooth: updated to allow use for MPG/pendants. + +* Misc, FNC Expander: added compile time option for PWM outputs, some general improvements. + +--- + Build 20251111 Core: diff --git a/driver_opts.h b/driver_opts.h index 260cc3f..f6803f8 100644 --- a/driver_opts.h +++ b/driver_opts.h @@ -198,14 +198,6 @@ #define SPINDLE_SYNC_ENABLE 0 #endif -#ifndef SPINDLE_ENCODER_ENABLE -#if SPINDLE_SYNC_ENABLE -#define SPINDLE_ENCODER_ENABLE 1 -#else -#define SPINDLE_ENCODER_ENABLE 0 -#endif -#endif - #ifndef TRINAMIC_ENABLE #define TRINAMIC_ENABLE 0 #endif @@ -450,6 +442,14 @@ #endif #endif +#ifndef SPINDLE_ENCODER_ENABLE +#if SPINDLE_SYNC_ENABLE && !(SPINDLE_ENABLE & (1 << SPINDLE_STEPPER)) +#define SPINDLE_ENCODER_ENABLE 1 +#else +#define SPINDLE_ENCODER_ENABLE 0 +#endif +#endif + #ifndef QEI_ENABLE #define QEI_ENABLE 0 #endif diff --git a/driver_opts2.h b/driver_opts2.h index 620e2e7..4988c0b 100644 --- a/driver_opts2.h +++ b/driver_opts2.h @@ -246,6 +246,26 @@ #define TRINAMIC_MOTOR_ENABLE 0 #endif +#if MPG_ENABLE && !defined(MPG_STREAM) +#if USB_SERIAL_CDC +#define MPG_STREAM 0 +#else +#define MPG_STREAM 1 +#endif +#if (MODBUS_ENABLE & MODBUS_RTU_ENABLED) && defined(MODBUS_RTU_STREAM) && MODBUS_RTU_STREAM == MPG_STREAM +#undef MPG_STREAM +#define MPG_STREAM (MODBUS_RTU_STREAM + 1) +#endif +#endif + +#if MPG_ENABLE && MPG_ENABLE != 2 && MPG_STREAM == 20 && BLUETOOTH_ENABLE != 1 +#error "MPG can only be used with native Bluetooth and character mode switching!" +#endif + +#if BLUETOOTH_ENABLE == 2 && !defined(BLUETOOTH_STREAM) +#define BLUETOOTH_STREAM 255 // Select first free UART stream +#endif + #if USB_SERIAL_CDC && defined(SERIAL_PORT) #define SP0 1 #else @@ -276,35 +296,37 @@ #define TRINAMIC_TEST 0 #endif -#if KEYPAD_ENABLE == 2 && MPG_ENABLE == 0 +#if MPG_ENABLE && MPG_STREAM != 20 +#define MPG_TEST 1 +#else +#define MPG_TEST 0 +#endif + +#if KEYPAD_ENABLE == 2 && !MPG_TEST #define KEYPAD_TEST 1 #else #define KEYPAD_TEST 0 #endif -#if (MODBUS_TEST + KEYPAD_TEST + (MPG_ENABLE ? 1 : 0) + TRINAMIC_TEST + (BLUETOOTH_ENABLE == 2 ? 1 : 0)) > (SP0 + SP1 + SP2) -#error "Too many options that uses a serial port are enabled!" +#if BLUETOOTH_ENABLE == 2 && (!MPG_ENABLE || MPG_STREAM != BLUETOOTH_STREAM) +#define BT_TEST 1 +#else +#define BT_TEST 0 +#endif + +#if (MODBUS_TEST + KEYPAD_TEST + MPG_TEST + TRINAMIC_TEST + BT_TEST) > (SP0 + SP1 + SP2) +#error "Too many options that requires a serial port are enabled!" #endif #undef SP0 #undef SP1 #undef SP2 +#undef BT_TEST #undef MODBUS_TEST +#undef MPG_TEST #undef KEYPAD_TEST #undef TRINAMIC_TEST -#if MPG_ENABLE && !defined(MPG_STREAM) -#if USB_SERIAL_CDC -#define MPG_STREAM 0 -#else -#define MPG_STREAM 1 -#endif -#if (MODBUS_ENABLE & MODBUS_RTU_ENABLED) && defined(MODBUS_RTU_STREAM) && MODBUS_RTU_STREAM == MPG_STREAM -#undef MPG_STREAM -#define MPG_STREAM (MODBUS_RTU_STREAM + 1) -#endif -#endif - #if KEYPAD_ENABLE == 2 && !defined(KEYPAD_STREAM) #if MPG_ENABLE #define KEYPAD_STREAM MPG_STREAM diff --git a/grbl.h b/grbl.h index 448beb7..23ab839 100644 --- a/grbl.h +++ b/grbl.h @@ -42,7 +42,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20251111 +#define GRBL_BUILD 20251130 #define GRBL_URL "https://github.com/grblHAL" @@ -113,48 +113,48 @@ // at character value 128 (0x80) and up to 255 (0xFF). If the normal set of realtime commands, // such as status reports, feed hold, reset, and cycle start, are moved to the extended set // space, protocol.c's protocol_process_realtime() will need to be modified to accommodate the change. -#define CMD_STATUS_REPORT 0x80 // TODO: use 0x05 ctrl-E ENQ instead? -#define CMD_CYCLE_START 0x81 // TODO: use 0x06 ctrl-F ACK instead? or SYN/DC2/DC3? -#define CMD_FEED_HOLD 0x82 // TODO: use 0x15 ctrl-U NAK instead? -#define CMD_GCODE_REPORT 0x83 -#define CMD_SAFETY_DOOR 0x84 -#define CMD_JOG_CANCEL 0x85 +#define CMD_STATUS_REPORT 0x80 // (128) TODO: use 0x05 ctrl-E ENQ instead? +#define CMD_CYCLE_START 0x81 // (129) TODO: use 0x06 ctrl-F ACK instead? or SYN/DC2/DC3? +#define CMD_FEED_HOLD 0x82 // (130) TODO: use 0x15 ctrl-U NAK instead? +#define CMD_GCODE_REPORT 0x83 // (131) +#define CMD_SAFETY_DOOR 0x84 // (132) +#define CMD_JOG_CANCEL 0x85 // (133) //#define CMD_DEBUG_REPORT 0x86 // Only when DEBUG enabled, sends debug report in '{}' braces. -#define CMD_STATUS_REPORT_ALL 0x87 -#define CMD_OPTIONAL_STOP_TOGGLE 0x88 -#define CMD_SINGLE_BLOCK_TOGGLE 0x89 -#define CMD_OVERRIDE_FAN0_TOGGLE 0x8A //!< Toggle Fan 0 on/off, not implemented by the core. -#define CMD_MPG_MODE_TOGGLE 0x8B //!< Toggle MPG mode on/off, available when the MPG stream is enabled with MPG mode 2. -#define CMD_AUTO_REPORTING_TOGGLE 0x8C //!< Toggle auto real time reporting if configured. -#define CMD_OVERRIDE_FEED_RESET 0x90 //!< Restores feed override value to 100%. -#define CMD_OVERRIDE_FEED_COARSE_PLUS 0x91 -#define CMD_OVERRIDE_FEED_COARSE_MINUS 0x92 -#define CMD_OVERRIDE_FEED_FINE_PLUS 0x93 -#define CMD_OVERRIDE_FEED_FINE_MINUS 0x94 -#define CMD_OVERRIDE_RAPID_RESET 0x95 //!< Restores rapid override value to 100%. -#define CMD_OVERRIDE_RAPID_MEDIUM 0x96 -#define CMD_OVERRIDE_RAPID_LOW 0x97 +#define CMD_STATUS_REPORT_ALL 0x87 // (135) +#define CMD_OPTIONAL_STOP_TOGGLE 0x88 // (136) +#define CMD_SINGLE_BLOCK_TOGGLE 0x89 // (137) +#define CMD_OVERRIDE_FAN0_TOGGLE 0x8A //!< (138) Toggle Fan 0 on/off, not implemented by the core. +#define CMD_MPG_MODE_TOGGLE 0x8B //!< (139) Toggle MPG mode on/off, available when the MPG stream is enabled with MPG mode 2. +#define CMD_AUTO_REPORTING_TOGGLE 0x8C //!< (140) Toggle auto real time reporting if configured. +#define CMD_OVERRIDE_FEED_RESET 0x90 //!< (144) Restores feed override value to 100%. +#define CMD_OVERRIDE_FEED_COARSE_PLUS 0x91 // (145) +#define CMD_OVERRIDE_FEED_COARSE_MINUS 0x92 // (146) +#define CMD_OVERRIDE_FEED_FINE_PLUS 0x93 // (147) +#define CMD_OVERRIDE_FEED_FINE_MINUS 0x94 // (148) +#define CMD_OVERRIDE_RAPID_RESET 0x95 //!< (149) Restores rapid override value to 100%. +#define CMD_OVERRIDE_RAPID_MEDIUM 0x96 // (150) +#define CMD_OVERRIDE_RAPID_LOW 0x97 // (151) // #define CMD_OVERRIDE_RAPID_EXTRA_LOW 0x98 // *NOT SUPPORTED* -#define CMD_OVERRIDE_SPINDLE_RESET 0x99 // Restores spindle override value to 100%. -#define CMD_OVERRIDE_SPINDLE_COARSE_PLUS 0x9A -#define CMD_OVERRIDE_SPINDLE_COARSE_MINUS 0x9B -#define CMD_OVERRIDE_SPINDLE_FINE_PLUS 0x9C -#define CMD_OVERRIDE_SPINDLE_FINE_MINUS 0x9D -#define CMD_OVERRIDE_SPINDLE_STOP 0x9E -#define CMD_OVERRIDE_COOLANT_FLOOD_TOGGLE 0xA0 -#define CMD_OVERRIDE_COOLANT_MIST_TOGGLE 0xA1 -#define CMD_PID_REPORT 0xA2 -#define CMD_TOOL_ACK 0xA3 -#define CMD_PROBE_CONNECTED_TOGGLE 0xA4 +#define CMD_OVERRIDE_SPINDLE_RESET 0x99 // (153) Restores spindle override value to 100%. +#define CMD_OVERRIDE_SPINDLE_COARSE_PLUS 0x9A // (154) +#define CMD_OVERRIDE_SPINDLE_COARSE_MINUS 0x9B // (155) +#define CMD_OVERRIDE_SPINDLE_FINE_PLUS 0x9C // (156) +#define CMD_OVERRIDE_SPINDLE_FINE_MINUS 0x9D // (157) +#define CMD_OVERRIDE_SPINDLE_STOP 0x9E // (158) +#define CMD_OVERRIDE_COOLANT_FLOOD_TOGGLE 0xA0 // (160) +#define CMD_OVERRIDE_COOLANT_MIST_TOGGLE 0xA1 // (161) +#define CMD_PID_REPORT 0xA2 // (162) +#define CMD_TOOL_ACK 0xA3 // (163) +#define CMD_PROBE_CONNECTED_TOGGLE 0xA4 // (164) // The following character codes are reserved for plugin use -#define CMD_MACRO_0 0xB0 -#define CMD_MACRO_1 0xB1 -#define CMD_MACRO_2 0xB2 -#define CMD_MACRO_3 0xB3 -#define CMD_MACRO_4 0xB4 -#define CMD_MACRO_5 0xB5 -#define CMD_MACRO_6 0xB6 -#define CMD_MACRO_7 0xB7 +#define CMD_MACRO_0 0xB0 // (176) +#define CMD_MACRO_1 0xB1 // (177) +#define CMD_MACRO_2 0xB2 // (178) +#define CMD_MACRO_3 0xB3 // (179) +#define CMD_MACRO_4 0xB4 // (180) +#define CMD_MACRO_5 0xB5 // (181) +#define CMD_MACRO_6 0xB6 // (182) +#define CMD_MACRO_7 0xB7 // (183) // System motion line numbers must be zero. #define JOG_LINE_NUMBER 0 diff --git a/grbllib.c b/grbllib.c index 3488cdb..0ff1009 100644 --- a/grbllib.c +++ b/grbllib.c @@ -95,6 +95,7 @@ static driver_startup_t driver = { .ok = 0xFF }; static core_task_t *next_task = NULL, *immediate_task = NULL, *on_booted = NULL, *systick_task = NULL, *last_freed = NULL; static on_linestate_changed_ptr on_linestate_changed; static settings_changed_ptr hal_settings_changed; +static stepper_enable_ptr stepper_enable; #ifdef KINEMATICS_API kinematics_t kinematics; @@ -211,6 +212,14 @@ static void onLinestateChanged (serial_linestate_t state) on_linestate_changed(state); } +static void stepperEnable (axes_signals_t enable, bool hold) +{ + if(stepper_enable) + stepper_enable(enable, hold); + + sys.steppers_enabled = /*!hold &&*/ enable.bits == AXES_BITMASK; +} + static void print_pos_msg (void *data) { hal.stream.write("grblHAL: power on self-test (POS) failed!" ASCII_EOL); @@ -354,6 +363,9 @@ int grbl_enter (void) // check and configure driver + stepper_enable = hal.stepper.enable; + hal.stepper.enable = stepperEnable; + #if ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING driver.amass = hal.driver_cap.amass_level >= MAX_AMASS_LEVEL; hal.driver_cap.amass_level = MAX_AMASS_LEVEL; diff --git a/modbus_rtu.c b/modbus_rtu.c index 8056e25..e943101 100644 --- a/modbus_rtu.c +++ b/modbus_rtu.c @@ -519,7 +519,7 @@ static void modbus_set_direction (bool tx) ioport_digital_out(dir_port, tx); } -static bool claim_stream (io_stream_properties_t const *sstream) +static bool claim_stream (io_stream_properties_t const *sstream, void *data) { io_stream_t const *claimed = NULL; @@ -598,7 +598,7 @@ void modbus_rtu_init (int8_t instance, int8_t dir_aux) stream_instance = instance; - if((hal.driver_cap.modbus_rtu = stream_enumerate_streams(claim_stream) && (nvs_address = nvs_alloc(sizeof(rtu_settings_t))))) { + if((hal.driver_cap.modbus_rtu = stream_enumerate_streams(claim_stream, NULL) && (nvs_address = nvs_alloc(sizeof(rtu_settings_t))))) { if(stream.set_direction == NULL && dir_aux != -2) { diff --git a/protocol.c b/protocol.c index b1c790f..e12db47 100644 --- a/protocol.c +++ b/protocol.c @@ -462,10 +462,7 @@ bool protocol_exec_rt_system (void) hal.driver_reset(); // Halt everything upon a critical event flag. Currently hard and soft limits flag this. - if((sys.blocking_event = (alarm_code_t)rt_exec == Alarm_HardLimit || - (alarm_code_t)rt_exec == Alarm_SoftLimit || - (alarm_code_t)rt_exec == Alarm_EStop || - (alarm_code_t)rt_exec == Alarm_MotorFault)) { + if((sys.blocking_event = alarm_is_critical((alarm_code_t)rt_exec))) { static const control_signals_t blocking_signals = { .e_stop = On, .motor_fault = On }; @@ -894,9 +891,9 @@ ISR_CODE bool ISR_FUNC(protocol_enqueue_realtime_command)(uint8_t c) break; case CMD_SAFETY_DOOR: - if(state_get() != STATE_SAFETY_DOOR) { + if((drop = state_get() != STATE_SAFETY_DOOR)) { + sys.flags.is_parking = settings.parking.flags.enabled; system_set_exec_state_flag(EXEC_SAFETY_DOOR); - drop = true; } break; diff --git a/report.c b/report.c index 256aeba..2b7e512 100644 --- a/report.c +++ b/report.c @@ -2366,86 +2366,121 @@ status_code_t report_pins (sys_state_t state, char *args) return Status_OK; } +typedef struct { + uint32_t idx; + const io_stream_properties_t *port; +} port_data_t; + +typedef struct { + uint8_t instance; + uint32_t n_pins; + pin_info_t data[2]; +} port_pins_t; + static void get_uart_pins (xbar_t *pin, void *data) { - if(pin->group >= PinGroup_UART && pin->group <= PinGroup_UART4) - get_pin_info(pin, &((pin_data_t *)data)->pins[((pin_data_t *)data)->idx++]); + if(pin->group == PinGroup_UART + ((port_pins_t *)data)->instance) + get_pin_info(pin, &((port_pins_t *)data)->data[((port_pins_t *)data)->n_pins++]); } -static void count_uart_pins (xbar_t *pin, void *data) +static bool report_port_info (const io_stream_properties_t *port, void *data) { - if(pin->group >= PinGroup_UART && pin->group <= PinGroup_UART4) - ((pin_data_t *)data)->n_pins++; -} + if(!stream_is_uart(port->type)) + return false; -static void report_port_info (pin_info_t *pin) -{ + port_pins_t pins = {0}; const io_stream_status_t *status; - if(pin->function == Input_RX) { - strcpy(buf, pin->port); - strcat(buf, uitoa(pin->pin)); - strcat(buf, ","); - strcat(buf, xbar_fn_to_pinname(Input_RX)); - strcat(buf, "|"); + hal.stream.write("[PORT:"); + hal.stream.write(uitoa(port->instance)); + hal.stream.write("|"); + if(port->type == StreamType_Bluetooth) { + hal.stream.write("BT||"); } else { - - uint8_t instance = (pin->sortkey >> 16) - PinGroup_UART; - - hal.stream.write("[PORT:"); - hal.stream.write(uitoa(instance)); - hal.stream.write("|"); - hal.stream.write(pin->description); - hal.stream.write("|"); - hal.stream.write(*buf ? buf : "-|"); - if(*pin->port) - hal.stream.write(pin->port); - hal.stream.write(uitoa(pin->pin)); - hal.stream.write(","); - hal.stream.write(xbar_fn_to_pinname(pin->function)); - if(hal.stream.write_char && (status = stream_get_uart_status(instance))) { - hal.stream.write("|"); - hal.stream.write(uitoa(status->baud_rate)); - hal.stream.write(","); - hal.stream.write_char("87"[status->format.width]); - hal.stream.write(","); - hal.stream.write_char("NEOMS"[status->format.parity]); - hal.stream.write(","); - hal.stream.write(((const char * const[]){"1", "1.5", "2", "0.5"})[status->format.stopbits]); - if(status->flags.rts_handshake) - hal.stream.write(",P"); - hal.stream.write("|"); - hal.stream.write_char("FC"[status->flags.claimed]); - } - hal.stream.write("]" ASCII_EOL); - *buf = '\0'; + uint32_t idx = 0; + pins.instance = port->instance; + if(hal.enumerate_pins) + hal.enumerate_pins(false, get_uart_pins, &pins); + if(pins.n_pins) { + hal.stream.write(pins.data[0].description); + for(idx = 0; idx < pins.n_pins; idx++) { + hal.stream.write("|"); + if(*pins.data[idx].port) + hal.stream.write(pins.data[idx].port); + hal.stream.write(uitoa(pins.data[idx].pin)); + hal.stream.write(","); + hal.stream.write(xbar_fn_to_pinname(pins.data[idx].function)); + } + for(; idx < 2; idx++) + hal.stream.write("|"); + } else + hal.stream.write("UART||"); } + + if(hal.stream.write_char && (status = stream_get_uart_status(port->instance))) { + hal.stream.write("|"); + hal.stream.write(uitoa(status->baud_rate)); + hal.stream.write(","); + hal.stream.write_char("87"[status->format.width]); + hal.stream.write(","); + hal.stream.write_char("NEOMS"[status->format.parity]); + hal.stream.write(","); + hal.stream.write(((const char * const[]){"1", "1.5", "2", "0.5"})[status->format.stopbits]); + if(status->flags.rts_handshake) + hal.stream.write(",P"); + hal.stream.write("|"); + hal.stream.write_char("FC"[status->flags.claimed]); + } + + hal.stream.write("]" ASCII_EOL); + + return false; +} + +bool get_ports (io_stream_properties_t const *port, void *data) +{ + if(stream_is_uart(port->type)) { + ((port_data_t *)data)[((port_data_t *)data)->idx].port = port; + ((port_data_t *)data)->idx++; + } + + return false; +} + +bool count_ports (io_stream_properties_t const *port, void *data) +{ + if(stream_is_uart(port->type)) + (*(uint32_t *)data)++; + + return false; +} + +static int cmp_ports (const void *a, const void *b) +{ + return ((port_data_t *)a)->port->instance - ((port_data_t *)b)->port->instance; } status_code_t report_uart_ports (sys_state_t state, char *args) { - pin_data_t pin_data = {0}; + uint32_t n_ports = 0; + port_data_t *port_data; - if(hal.enumerate_pins) { + stream_enumerate_streams(count_ports, &n_ports); + if(n_ports) { + if((port_data = malloc(n_ports * sizeof(port_data_t)))) { - hal.enumerate_pins(false, count_uart_pins, (void *)&pin_data); + port_data->idx = 0; + stream_enumerate_streams(get_ports, port_data); - if((pin_data.pins = malloc(pin_data.n_pins * sizeof(pin_info_t)))) { - - *buf = '\0'; - - hal.enumerate_pins(false, get_uart_pins, (void *)&pin_data); - - qsort(pin_data.pins, pin_data.n_pins, sizeof(pin_info_t), cmp_pins); - for(pin_data.idx = 0; pin_data.idx < pin_data.n_pins; pin_data.idx++) - report_port_info(&pin_data.pins[pin_data.idx]); - - free(pin_data.pins); + qsort(port_data, n_ports, sizeof(port_data_t), cmp_ports); + for(port_data->idx = 0; port_data->idx < n_ports; port_data->idx++) + report_port_info(port_data[port_data->idx].port, NULL); + free(port_data); } else - hal.enumerate_pins(false, report_pin, NULL); - } - + stream_enumerate_streams(report_port_info, NULL); + } + return Status_OK; } diff --git a/settings.c b/settings.c index ccb8373..c7f0821 100644 --- a/settings.c +++ b/settings.c @@ -434,6 +434,7 @@ static char probing_options[] = "Allow feed override,Apply soft limits,N/A,Auto static char control_signals[] = "Reset,Feed hold,Cycle start,Safety door,Block delete,Optional stop,EStop,Probe disconnected,Motor fault,Motor warning,Limits override,Single step blocks,Toolsetter overtravel"; static char spindle_signals[] = "Spindle enable,Spindle direction,PWM"; static char coolant_signals[] = "Flood,Mist"; +static char door_options[] = "Ignore when idle,Keep coolant state on door open"; static char ganged_axes[] = "X-Axis,Y-Axis,Z-Axis"; #if !AXIS_REMAP_ABC2UVW #if N_AXIS == 4 @@ -950,6 +951,8 @@ static status_code_t set_parking_enable (setting_id_t id, uint_fast16_t int_valu if(settings.parking.flags.deactivate_upon_init) settings.parking.flags.enable_override_control = On; + //setting_remove_elements(Setting_ProbePullUpDisable, mask); + return Status_OK; } @@ -1951,7 +1954,7 @@ static bool is_setting_available (const setting_detail_t *setting, uint_fast16_t #ifndef NO_SAFETY_DOOR_SUPPORT case Setting_DoorOptions: - available = hal.signals_cap.safety_door_ajar; + available = hal.signals_cap.safety_door_ajar || !settings.parking.flags.enabled; break; case Setting_DoorSpindleOnDelay: @@ -2106,7 +2109,7 @@ PROGMEM static const setting_detail_t setting_detail[] = { { Setting_ParkingFastRate, Group_SafetyDoor, "Parking fast rate", "mm/min", Format_Decimal, "###0.0", NULL, NULL, Setting_IsExtended, &settings.parking.rate, NULL, NULL }, { Setting_RestoreOverrides, Group_General, "Restore overrides", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_restore_overrides, get_int, NULL }, #ifndef NO_SAFETY_DOOR_SUPPORT - { Setting_DoorOptions, Group_SafetyDoor, "Safety door options", NULL, Format_Bitfield, "Ignore when idle,Keep coolant state on open", NULL, NULL, Setting_IsExtended, &settings.safety_door.flags.value, NULL, is_setting_available }, + { Setting_DoorOptions, Group_SafetyDoor, "Safety door options", NULL, Format_Bitfield, door_options, NULL, NULL, Setting_IsExtended, &settings.safety_door.flags.value, NULL, is_setting_available }, #endif { Setting_SleepEnable, Group_General, "Sleep enable", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_sleep_enable, get_int, is_setting_available }, { Setting_HoldActions, Group_General, "Feed hold actions", NULL, Format_Bitfield, "Disable laser during hold,Restore spindle and coolant state on resume", NULL, NULL, Setting_IsExtendedFn, set_hold_actions, get_int, NULL }, @@ -2287,7 +2290,8 @@ PROGMEM static const setting_descr_t setting_descr[] = { { Setting_ParkingFastRate, "Parking fast rate to target after pull-out in mm/min." }, { Setting_RestoreOverrides, "Restore overrides to default values at program end." }, #ifndef NO_SAFETY_DOOR_SUPPORT - { Setting_DoorOptions, "Enable this if it is desirable to open the safety door when in IDLE mode (eg. for jogging)." }, + { Setting_DoorOptions, "Ignore when idle: disregard door signal in IDLE state to allow jogging etc. Available when controller has door input.\n" + "Keep coolant state on open: do not turn off coolant if on." }, #endif { Setting_SleepEnable, "Enable sleep mode." }, { Setting_HoldActions, "Actions taken during feed hold and on resume from feed hold." }, @@ -3518,6 +3522,9 @@ void settings_init (void) uint32_t mask = 0b001 | (hal.driver_cap.toolsetter << 1) | (hal.driver_cap.probe2 << 2); setting_remove_elements(Setting_InvertProbePin, mask); setting_remove_elements(Setting_ProbePullUpDisable, mask); +#ifndef NO_SAFETY_DOOR_SUPPORT + setting_remove_elements(Setting_DoorOptions, ((!settings.parking.flags.enabled || hal.signals_cap.safety_door_ajar) << 1) | hal.signals_cap.safety_door_ajar); +#endif mask = 0b00011 | (hal.probe.select ? ((hal.driver_cap.toolsetter << 3) | (hal.driver_cap.probe2 << 4)) : 0); #if 0 diff --git a/state_machine.c b/state_machine.c index 4af1100..08a12bf 100644 --- a/state_machine.c +++ b/state_machine.c @@ -576,16 +576,17 @@ static void state_await_hold (uint_fast16_t rt_exec) // Parking motion not possible. Just disable the spindle and coolant. // NOTE: Laser mode does not start a parking motion to ensure the laser stops immediately. spindle_all_off(); // De-energize - if (!settings.safety_door.flags.keep_coolant_on || sys_state == STATE_SLEEP) + if(sys.flags.is_parking || sys_state == STATE_SLEEP || !settings.safety_door.flags.keep_coolant_on) hal.coolant.set_state((coolant_state_t){0}); // De-energize sys.parking_state = hal.control.get_state().safety_door_ajar ? Parking_DoorAjar : Parking_DoorClosed; } } else { spindle_all_off(); // De-energize - if (!settings.safety_door.flags.keep_coolant_on || sys_state == STATE_SLEEP) + if(sys.flags.is_parking || sys_state == STATE_SLEEP || !settings.safety_door.flags.keep_coolant_on) hal.coolant.set_state((coolant_state_t){0}); // De-energize sys.parking_state = hal.control.get_state().safety_door_ajar ? Parking_DoorAjar : Parking_DoorClosed; } + sys.flags.is_parking = false; break; default: diff --git a/stepper.c b/stepper.c index 7016af5..53868e9 100644 --- a/stepper.c +++ b/stepper.c @@ -200,10 +200,11 @@ void st_wake_up (void) sys.steppers_deenergize = false; hal.stepper.go_idle(true); // Reset step & dir outputs - hal.stepper.wake_up(); - if(settings.stepper_enable_delay) // TODO: do not add delay if deenergize is pending? + if(!sys.steppers_enabled && settings.stepper_enable_delay) hal.delay_ms(settings.stepper_enable_delay, NULL); + + hal.stepper.wake_up(); } // Stepper shutdown diff --git a/stream.c b/stream.c index 91fb394..dab8976 100644 --- a/stream.c +++ b/stream.c @@ -89,7 +89,7 @@ void stream_register_streams (io_stream_details_t *details) } } -bool stream_enumerate_streams (stream_enumerate_callback_ptr callback) +bool stream_enumerate_streams (stream_enumerate_callback_ptr callback, void *data) { if(callback == NULL) return false; @@ -100,7 +100,7 @@ bool stream_enumerate_streams (stream_enumerate_callback_ptr callback) while(details && !claimed) { uint_fast8_t idx; for(idx = 0; idx < details->n_streams; idx++) { - if((claimed = callback(&details->streams[idx]))) + if((claimed = callback(&details->streams[idx], data))) break; } details = details->next; @@ -134,7 +134,8 @@ const io_stream_status_t *stream_get_uart_status (uint8_t instance) while(details) { uint_fast8_t idx; for(idx = 0; idx < details->n_streams; idx++) { - if(details->streams[idx].type == StreamType_Serial && details->streams[idx].instance == instance) { + if(details->streams[idx].instance == instance && + stream_is_uart(details->streams[idx].type)) { if(details->streams[idx].get_status) status = details->streams[idx].get_status(instance); break; @@ -439,33 +440,38 @@ bool stream_connect (const io_stream_t *stream) { bool ok; - if((ok = stream_select(stream, true))) + if((ok = stream && stream_select(stream, true))) stream_set_description(stream, "Primary UART"); return ok; } -static struct { +typedef struct { uint8_t instance; uint32_t baud_rate; io_stream_t const *stream; -} connection; +} connection_t; -static bool _open_instance (io_stream_properties_t const *stream) +static bool _open_instance (io_stream_properties_t const *stream, void *data) { - if(stream->type == StreamType_Serial && (connection.instance == 255 || stream->instance == connection.instance) && stream->flags.claimable && !stream->flags.claimed) - connection.stream = stream->claim(connection.baud_rate); + connection_t *connection = (connection_t *)data; - return connection.stream != NULL; + if(stream_is_uart(stream->type) && + (connection->instance == 255 || stream->instance == connection->instance) && + stream->flags.claimable && !stream->flags.claimed) + connection->stream = stream->claim(connection->baud_rate); + + return connection->stream != NULL; } bool stream_connect_instance (uint8_t instance, uint32_t baud_rate) { - connection.instance = instance; - connection.baud_rate = baud_rate; - connection.stream = NULL; + connection_t connection = { + .instance = instance, + .baud_rate = baud_rate + }; - return stream_enumerate_streams(_open_instance) && stream_connect(connection.stream); + return stream_enumerate_streams(_open_instance, &connection) && stream_connect(connection.stream); } void stream_disconnect (const io_stream_t *stream) @@ -476,11 +482,12 @@ void stream_disconnect (const io_stream_t *stream) io_stream_t const *stream_open_instance (uint8_t instance, uint32_t baud_rate, stream_write_char_ptr rx_handler, const char *description) { - connection.instance = instance; - connection.baud_rate = baud_rate; - connection.stream = NULL; + connection_t connection = { + .instance = instance, + .baud_rate = baud_rate + }; - if(stream_enumerate_streams(_open_instance)) { + if(stream_enumerate_streams(_open_instance, &connection)) { connection.stream->set_enqueue_rt_handler(rx_handler); if(description) stream_set_description(connection.stream, description); @@ -539,7 +546,7 @@ void stream_mpg_set_mode (void *data) stream_mpg_enable(data != NULL); } -ISR_CODE bool ISR_FUNC(stream_mpg_check_enable)(char c) +ISR_CODE bool ISR_FUNC(stream_mpg_check_enable)(uint8_t c) { if(c == CMD_MPG_MODE_TOGGLE) task_add_immediate(stream_mpg_set_mode, (void *)1); @@ -554,7 +561,7 @@ ISR_CODE bool ISR_FUNC(stream_mpg_check_enable)(char c) bool stream_mpg_register (const io_stream_t *stream, bool rx_only, stream_write_char_ptr write_char) { - if(stream == NULL || stream->type != StreamType_Serial || stream->disable_rx == NULL) + if(stream == NULL || !stream_is_uart(stream->type) || stream->disable_rx == NULL) return false; // base.flags.is_up = On; @@ -630,8 +637,11 @@ bool stream_mpg_enable (bool on) hal.stream.read = mpg.stream.read; mpg.stream.disable_rx(false); mpg.stream.set_enqueue_rt_handler(hal.stream.set_enqueue_rt_handler(NULL)); - if(mpg.flags.is_mpg_tx) + if(mpg.flags.is_mpg_tx) { hal.stream.write = mpg.stream.write; + hal.stream.write_n = mpg.stream.write_n; + hal.stream.write_char = mpg.stream.write_char; + } hal.stream.get_rx_buffer_free = mpg.stream.get_rx_buffer_free; hal.stream.cancel_read_buffer = mpg.stream.cancel_read_buffer; hal.stream.reset_read_buffer = mpg.stream.reset_read_buffer; diff --git a/stream.h b/stream.h index db6c90d..1844e60 100644 --- a/stream.h +++ b/stream.h @@ -131,8 +131,10 @@ typedef union { uint8_t value; struct { uint8_t dtr :1, + dsr :1, rts :1, - unused :6; + cts :1, + unused :4; }; } serial_linestate_t; @@ -282,7 +284,9 @@ typedef union { is_usb :1, linestate_event :1, //!< Set when driver supports on_linestate_changed event. passthru :1, //!< Set when stream is in passthru mode. - unused :4; + utf8 :1, //!< Set when stream is in UTF8 mode. + eof :1, //!< Set when a file stream reaches end-of-file. + unused :2; }; } io_stream_state_t; @@ -334,7 +338,7 @@ typedef struct { stream_get_status_ptr get_status; //!< Optional handler for getting stream status, for UART streams only } io_stream_properties_t; -typedef bool (*stream_enumerate_callback_ptr)(io_stream_properties_t const *properties); +typedef bool (*stream_enumerate_callback_ptr)(io_stream_properties_t const *properties, void *data); typedef struct io_stream_details { uint8_t n_streams; @@ -380,6 +384,11 @@ typedef struct { extern "C" { #endif +static inline bool stream_is_uart (stream_type_t type) +{ + return type == StreamType_Serial || type == StreamType_Bluetooth; +} + /*! \brief Dummy function for reading data from a virtual empty input buffer. \returns always -1 as there is no data available. */ @@ -404,7 +413,7 @@ bool stream_mpg_enable (bool on); void stream_mpg_set_mode (void *data); -bool stream_mpg_check_enable (char c); +bool stream_mpg_check_enable (uint8_t c); bool stream_buffer_all (uint8_t c); @@ -414,7 +423,7 @@ bool stream_enqueue_realtime_command (uint8_t c); void stream_register_streams (io_stream_details_t *details); -bool stream_enumerate_streams (stream_enumerate_callback_ptr callback); +bool stream_enumerate_streams (stream_enumerate_callback_ptr callback, void *data); bool stream_connect (const io_stream_t *stream); diff --git a/system.c b/system.c index 4adf0c2..e986ef7 100644 --- a/system.c +++ b/system.c @@ -92,6 +92,7 @@ ISR_CODE void ISR_FUNC(control_interrupt_handler)(control_signals_t signals) } else { #ifndef NO_SAFETY_DOOR_SUPPORT if(signals.safety_door_ajar && hal.signals_cap.safety_door_ajar && !gc_state.tool_change) { + sys.flags.is_parking = false; if(settings.safety_door.flags.ignore_when_idle) { // Only stop the spindle (laser off) when idle or jogging, // this to allow positioning the controlled point (spindle) when door is open. @@ -1254,11 +1255,7 @@ void system_raise_alarm (alarm_code_t alarm) system_set_exec_alarm(alarm); else if(sys.alarm != alarm) { sys.alarm = alarm; - sys.blocking_event = sys.alarm == Alarm_HardLimit || - sys.alarm == Alarm_SoftLimit || - sys.alarm == Alarm_EStop || - sys.alarm == Alarm_MotorFault; - state_set(alarm == Alarm_EStop ? STATE_ESTOP : STATE_ALARM); + sys.blocking_event = alarm_is_critical(sys.alarm); if(sys.driver_started || sys.alarm == Alarm_SelftestFailed) grbl.report.alarm_message(alarm); } diff --git a/system.h b/system.h index e46083c..c373eae 100644 --- a/system.h +++ b/system.h @@ -274,7 +274,8 @@ typedef union { auto_reporting :1, //!< Set to true when auto real time reporting is enabled. travel_changed :1, //!< Set to true when maximum travel settings has changed. is_homing :1, - unused :4; + is_parking :1, //!< Set to true when CMD_SAFETY_DOOR is received. + unused :3; }; } system_flags_t; @@ -331,6 +332,7 @@ typedef struct system { bool cold_start; //!< Set to true on boot, is false on subsequent soft resets. bool ioinit_pending; bool driver_started; //!< Set to true when driver initialization is completed. + bool steppers_enabled; //!< Set to true when all steppers are enabled. bool mpg_mode; //!< To be moved to system_flags_t signal_event_t last_event; //!< Last signal events (control and limits signal). int32_t position[N_AXIS]; //!< Real-time machine (aka home) position vector in steps. diff --git a/utf8.c b/utf8.c new file mode 100644 index 0000000..f6d509a --- /dev/null +++ b/utf8.c @@ -0,0 +1,148 @@ +/* + utf8.c - An embedded CNC Controller with rs274/ngc (g-code) support + + Part of grblHAL + + Copyright (c) 2025 Terje Io + + utf32_to_utf8() is Copyright 2025 Kang-Che Sung, see license below. + + 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 + +/* +static enqueue_realtime_command_ptr utf8_base_handler = NULL; + +int32_t utf8_decode (const io_stream_t *stream) +{ + static int32_t count = 0, utf8_c = SERIAL_NO_DATA; + + if((count && (stream->get_rx_buffer_count()) < count) || (utf8_c = stream->read()) == SERIAL_NO_DATA) + return SERIAL_NO_DATA; + + if(utf8_c & 0b11100000) { + count = (c & 0b11100000) == 0b11100000 ? (c & 0b00110000) >> 4 : 1; + if(c & (0b01000000 >> count)) { + count = 0; + utf8_c = 0xFFFD; + } else + utf8_c = c & (0b00111111 >> count); + } + + if(count) do { + + int32_t c; + + if(((c = stream->read())& 0b11000000) != 0b10000000) { + count = 1; + utf8_c = 0xFFFD; + } else { + utf8_c = (utf8_c << 6) | (c & 0b00111111); + } + } while(--count); + + return utf8_c; +} + +ISR_CODE bool ISR_FUNC(utf8_insert)(uint8_t c) +{ + static int32_t count = 0, utf8_c = 0; + + if((c & 0b11000000) == 0b11000000) + count = (utf8_c & 0b11100000) == 0b11100000 ? (utf8_c & 0b00110000) >> 4 : 1; + else if(count) + count--; + + return count ? false : utf8_base_handler(c); +} + +bool stream_utf8_enable (const io_stream_t *stream, bool enable) +{ + if(enable) { + if(utf8_base_handler == NULL) + utf8_base_handler = hal.stream.set_enqueue_rt_handler(utf8_insert); + } else { + if(utf8_base_handler) { + hal.stream.set_enqueue_rt_handler(utf8_base_handler); + utf8_base_handler = NULL; + } + } + + return true; +} +*/ + +/* + + +Copyright 2025 Kang-Che Sung + +MIT License (MIT/Expat) + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/* SPDX-License-Identifier: MIT */ + +uint16_t utf32_to_utf8 (uint8_t *buffer, uint32_t code_point) +{ +// assert(code_point <= 0x10FFFF); +// assert(code_point < 0xD800 || code_point > 0xDFFF); + + if(code_point > 0x10FFFF) + return 0; + + uint16_t idx, length = 1; + uint32_t first_byte = code_point, mask; + + if(code_point <= 0x7F) { + mask = 0b0111111; // We assume ASCII characters appear most frequently. + } else { + // Find out how many bytes are needed. + mask = 0b00111111; + do { + length++; + first_byte >>= 6; + mask >>= 1; + } while(first_byte > mask); + } + + if(length <= 4) { + buffer[0] = (uint8_t)(first_byte + (~mask << 1)); + for(idx = length - 1; idx > 0; idx--) { + buffer[idx] = (code_point & 0b00111111) | 0b10000000; + code_point >>= 6; + } + } + + return length; +} diff --git a/utf8.h b/utf8.h new file mode 100644 index 0000000..4e25c4c --- /dev/null +++ b/utf8.h @@ -0,0 +1,24 @@ +/* + utf8.c - An embedded CNC Controller with rs274/ngc (g-code) support + + Part of grblHAL + + Copyright (c) 2025 Terje Io + + utf32_to_utf8() is Copyright 2025 Kang-Che Sung, see license in utf8.c. + + 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 . +*/ + +uint16_t utf32_to_utf8 (uint8_t *buffer, uint32_t code_point);