From b4c896e2ada971c31f8fdc0dd4f0bae44d6bfbf8 Mon Sep 17 00:00:00 2001 From: Terje Io Date: Tue, 19 Jul 2022 21:51:46 +0200 Subject: [PATCH] Added support for G5.1 (quadratic spline) and multi turn arc option for G2 and G3 via P-word. Some additions to data structures used by plugins. --- README.md | 11 ++++--- changelog.md | 22 ++++++++++++- gcode.c | 82 +++++++++++++++++++++++++++++++++++++++++++++--- gcode.h | 2 ++ grbl.h | 2 +- motion_control.c | 62 +++++++++++++++++++++++++++--------- motion_control.h | 2 +- nuts_bolts.h | 10 +++++- plugins.h | 9 ++++++ 9 files changed, 174 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 0abe14c..62909c3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ It has been written to complement grblHAL and has features such as proper keyboa --- -Latest build date is 20220710, see the [changelog](changelog.md) for details. +Latest build date is 20220718, see the [changelog](changelog.md) for details. __NOTE:__ A settings reset will be performed on an update for versions earlier than 20211122. Backup and restore of settings is recommended. __IMPORTANT!__ A new setting has been introduced for ganged axes motors in version 20211121. I have only bench tested this for a couple of drivers, correct function should be verified after updating by those who have more than three motors configured. @@ -53,7 +53,7 @@ This is a port/rewrite of [grbl 1.1f](https://github.com/gnea/grbl) and should b List of Supported G-Codes: - Non-Modal Commands: G4, G10L2, G10L20, G28, G30, G28.1, G30.1, G53, G92, G92.1 - Additional Non-Modal Commands: G10L1*, G10L10*, G10L11* - - Motion Modes: G0, G1, G2, G3, G5, G38.2, G38.3, G38.4, G38.5, G80, G33* + - Motion Modes: G0, G1, G2****, G3****, G5, G5.1, G38.2, G38.3, G38.4, G38.5, G80, G33* - Canned cycles: G73, G81, G82, G83, G85, G86, G89, G98, G99 - Repetitive cycles: G76* - Feed Rate Modes: G93, G94, G95*, G96*, G97* @@ -72,15 +72,16 @@ List of Supported G-Codes: - Spindle Control: M3, M4, M5 - Tool Change: M6* (Two modes possible: manual** - supports jogging, ATC), M61 - Switches: M48, M49, M50, M51, M53 - - Output control***: M62, M63, M64, M65, M66, M67, M68 + - Input/uutput control***: M62, M63, M64, M65, M66, M67, M68 - Valid Non-Command Words: A*, B*, C*, F, H*, I, J, K, L, N, P, Q*, R, S, T, X, Y, Z * driver/configuration dependent. ** requires compatible GCode sender due to protocol extensions, new state and RT command. - *** number of outputs supported dependent on driver implementation. + *** number of inputs and outputs supported dependent on driver implementation. + **** supports multi turn arcs from build 20220718. ``` Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes. --- -2022-07-10 +2022-07-18 diff --git a/changelog.md b/changelog.md index 0383c26..7153fc0 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,25 @@ ## grblHAL changelog +20220718: + +Core: + +* Added support for `G5.1` \(quadratic spline\) and multi turn arc option for `G2` and `G3` via P-word. +* Some additions to data structures used by plugins. + +Plugins: + +* Networking: added optional function to API for querying current status of connection and running services. Required for the WebUI plugin. +Added support for client side ping message used by WebUI v3. +* WebUI: added initial support for [WebUI v3](https://github.com/luc-github/ESP3D-WEBUI/discussions/94#discussioncomment-2861616) messaging. +* SDCard: added function call for querying current job status. + +Drivers: + +* iMXRT1062, STM32F7xx, ESP32, TM4C1294 and MSP432E401Y: Implemented optional API function for querying current status of connection and running services. + +--- + 20220710: Core: @@ -8,7 +28,7 @@ Core: Drivers: -* Most: updated for move of stepper enable initial stepper enable call to the core. +* Most: Updated for move of stepper enable initial stepper enable call to the core. * STM32F4xx: Fix for missing code guard, updated my_machine.h options. diff --git a/gcode.c b/gcode.c index 9fb57d2..9f0ebc5 100644 --- a/gcode.c +++ b/gcode.c @@ -148,7 +148,7 @@ inline static float hypot_f (float x, float y) inline static bool motion_is_lasercut (motion_mode_t motion) { - return motion == MotionMode_Linear || motion == MotionMode_CwArc || motion == MotionMode_CcwArc || motion == MotionMode_CubicSpline; + return motion == MotionMode_Linear || motion == MotionMode_CwArc || motion == MotionMode_CcwArc || motion == MotionMode_CubicSpline || motion == MotionMode_QuadraticSpline; } parser_state_t *gc_get_state (void) @@ -811,7 +811,14 @@ status_code_t gc_execute_block(char *block) case 80: word_bit.modal_group.G1 = On; - gc_block.modal.motion = (motion_mode_t)int_value; + if(int_value == 5 && mantissa != 0) { + if(mantissa == 10) { + gc_block.modal.motion = MotionMode_QuadraticSpline; + mantissa = 0; // Set to zero to indicate valid non-integer G command. + } else + FAIL(Status_GcodeUnsupportedCommand); + } else + gc_block.modal.motion = (motion_mode_t)int_value; gc_block.modal.canned_cycle_active = false; break; @@ -2184,7 +2191,7 @@ status_code_t gc_execute_block(char *block) // [G2/3 Radius-Mode Errors]: No axis words in selected plane. Target point is same as current. // [G2/3 Offset-Mode Errors]: No axis words and/or offsets in selected plane. The radius to the current // point and the radius to the target point differs more than 0.002mm (EMC def. 0.5mm OR 0.005mm and 0.1% radius). - // [G2/3 Full-Circle-Mode Errors]: NOT SUPPORTED. Axis words exist. No offsets programmed. P must be an integer. + // [G2/3 Full-Circle-Mode Errors]: Axis words exist. No offsets programmed. P must be an integer. // NOTE: Both radius and offsets are required for arc tracing and are pre-computed with the error-checking. if (!axis_words.mask) @@ -2193,6 +2200,16 @@ status_code_t gc_execute_block(char *block) if (!(axis_words.mask & (bit(plane.axis_0)|bit(plane.axis_1)))) FAIL(Status_GcodeNoAxisWordsInPlane); // [No axis words in plane] + if (gc_block.words.p) { // Number of turns + if(!isintf(gc_block.values.p)) + FAIL(Status_GcodeCommandValueNotInteger); // [P word is not an integer] + gc_block.arc_turns = (uint32_t)truncf(gc_block.values.p); + if(gc_block.arc_turns == 0) + FAIL(Status_GcodeValueOutOfRange); // [P word is 0] + gc_block.words.p = Off; + } else + gc_block.arc_turns = 1; + // Calculate the change in position along each selected axis float x, y; x = gc_block.values.xyz[plane.axis_0] - gc_state.position[plane.axis_0]; // Delta x between current position and target @@ -2397,6 +2414,37 @@ status_code_t gc_execute_block(char *block) gc_block.words.p = gc_block.words.q = gc_block.words.i = gc_block.words.j = Off; break; + case MotionMode_QuadraticSpline: + // [G5.1 Errors]: Feed rate undefined. + // [G5.1 Plane Errors]: The active plane is not G17. + // [G5.1 Offset Errors]: Just one of I or J are specified. + // [G5.1 Offset Errors]: I or J are unspecified in the first of a series of G5 commands. + // [G5.1 Axisword Errors]: An axis other than X or Y is specified. + if(gc_block.modal.plane_select != PlaneSelect_XY) + FAIL(Status_GcodeIllegalPlane); // [The active plane is not G17] + + if (axis_words.mask & ~(bit(X_AXIS)|bit(Y_AXIS))) + FAIL(Status_GcodeAxisCommandConflict); // [An axis other than X or Y is specified] + + if((gc_block.words.mask & ij_words.mask) != ij_words.mask) + FAIL(Status_GcodeValueWordMissing); // [I or J are unspecified] + + if(gc_block.values.ijk[I_VALUE] == 0.0f && gc_block.values.ijk[I_VALUE] == 0.0f) + FAIL(Status_GcodeValueOutOfRange); // [I or J are zero] + + // Convert I and J values to proper units. + if (gc_block.modal.units_imperial) { + gc_block.values.ijk[I_VALUE] *= MM_PER_INCH; + gc_block.values.ijk[J_VALUE] *= MM_PER_INCH; + } + // Scale values if scaling active + if(gc_state.modal.scaling_active) { + gc_block.values.ijk[I_VALUE] *= scale_factor.ijk[X_AXIS]; + gc_block.values.ijk[J_VALUE] *= scale_factor.ijk[Y_AXIS]; + } + gc_block.words.i = gc_block.words.j = Off; + break; + case MotionMode_ProbeTowardNoError: case MotionMode_ProbeAwayNoError: gc_parser_flags.probe_is_no_error = On; @@ -2861,11 +2909,35 @@ status_code_t gc_execute_block(char *block) case MotionMode_CcwArc: // fail if spindle synchronized motion? mc_arc(gc_block.values.xyz, &plan_data, gc_state.position, gc_block.values.ijk, gc_block.values.r, - plane, gc_parser_flags.arc_is_clockwise); + plane, gc_parser_flags.arc_is_clockwise ? gc_block.arc_turns : - gc_block.arc_turns); break; case MotionMode_CubicSpline: - mc_cubic_b_spline(gc_block.values.xyz, &plan_data, gc_state.position, gc_block.values.ijk, gc_state.modal.spline_pq); + { + point_2d cp1 = { + .x = gc_state.position[X_AXIS] + gc_block.values.ijk[X_AXIS], + .y = gc_state.position[Y_AXIS] + gc_block.values.ijk[Y_AXIS] + }; + point_2d cp2 = { + .x = gc_block.values.xyz[X_AXIS] + gc_state.modal.spline_pq[X_AXIS], + .y = gc_block.values.xyz[Y_AXIS] + gc_state.modal.spline_pq[Y_AXIS] + }; + mc_cubic_b_spline(gc_block.values.xyz, &plan_data, gc_state.position, cp1.values, cp2.values); + } + break; + + case MotionMode_QuadraticSpline: + { + point_2d cp1 = { + .x = gc_state.position[X_AXIS] + (gc_block.values.ijk[X_AXIS] * 2.0f) / 3.0f, + .y = gc_state.position[Y_AXIS] + (gc_block.values.ijk[Y_AXIS] * 2.0f) / 3.0f + }; + point_2d cp2 = { + .x = gc_block.values.xyz[X_AXIS] + ((gc_state.position[X_AXIS] + gc_block.values.ijk[X_AXIS] - gc_block.values.xyz[X_AXIS]) * 2.0f) / 3.0f, + .y = gc_block.values.xyz[Y_AXIS] + ((gc_state.position[Y_AXIS] + gc_block.values.ijk[Y_AXIS] - gc_block.values.xyz[Y_AXIS]) * 2.0f) / 3.0f + }; + mc_cubic_b_spline(gc_block.values.xyz, &plan_data, gc_state.position, cp1.values, cp2.values); + } break; case MotionMode_SpindleSynchronized: diff --git a/gcode.h b/gcode.h index a7daef0..6ef0d3f 100644 --- a/gcode.h +++ b/gcode.h @@ -65,6 +65,7 @@ typedef enum { MotionMode_CwArc = 2, //!< 2 - G2 MotionMode_CcwArc = 3, //!< 3 - G3 MotionMode_CubicSpline = 5, //!< 5 - G5 + MotionMode_QuadraticSpline = 51, //!< 51 - G5.1 MotionMode_SpindleSynchronized = 33, //!< 33 - G33 MotionMode_DrillChipBreak = 73, //!< 73 - G73 MotionMode_Threading = 76, //!< 76 - G76 @@ -532,6 +533,7 @@ typedef struct { gc_values_t values; //!< Parameter values for block. parameter_words_t words; //!< Bitfield for tracking found parameter values. output_command_t output_command; //!< Details about M62-M68 output command to execute if present in block. + uint32_t arc_turns; // } parser_block_t; // Initialize the parser diff --git a/grbl.h b/grbl.h index 439889a..304fcc8 100644 --- a/grbl.h +++ b/grbl.h @@ -34,7 +34,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20220710 +#define GRBL_BUILD 20220718 // The following symbols are set here if not already set by the compiler or in config.h // Do NOT change here! diff --git a/motion_control.c b/motion_control.c index e030292..022bae1 100644 --- a/motion_control.c +++ b/motion_control.c @@ -26,6 +26,7 @@ */ #include +#include #include #include "hal.h" @@ -223,8 +224,7 @@ bool mc_line (float *target, plan_line_data_t *pl_data) // The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance // of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal // distance from segment to the circle when the end points both lie on the circle. -void mc_arc (float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, - plane_t plane, bool is_clockwise_arc) +void mc_arc (float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, plane_t plane, int32_t turns) { float center_axis0 = position[plane.axis_0] + offset[plane.axis_0]; float center_axis1 = position[plane.axis_1] + offset[plane.axis_1]; @@ -235,12 +235,49 @@ void mc_arc (float *target, plan_line_data_t *pl_data, float *position, float *o // CCW angle between position and target from circle center. Only one atan2() trig computation required. float angular_travel = atan2f(r_axis0 * rt_axis1 - r_axis1 * rt_axis0, r_axis0 * rt_axis0 + r_axis1 * rt_axis1); - if (is_clockwise_arc) { // Correct atan2 output per direction + if (turns > 0) { // Correct atan2 output per direction if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) angular_travel -= 2.0f * M_PI; - } else { - if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) - angular_travel += 2.0f * M_PI; + } else if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) + angular_travel += 2.0f * M_PI; + + if(labs(turns) > 1) { + + uint32_t n_turns = labs(turns) - 1; + float arc_travel = 2.0f * M_PI * n_turns + angular_travel; + coord_data_t arc_target; +#if N_AXIS > 3 + uint_fast8_t idx = N_AXIS; + float linear_per_turn[N_AXIS]; + do { + idx--; + if(!(idx == plane.axis_0 || idx == plane.axis_1)) + linear_per_turn[idx] = (target[idx] - position[idx]) / arc_travel * 2.0f * M_PI;; + } while(idx); +#else + float linear_per_turn = (target[plane.axis_linear] - position[plane.axis_linear]) / arc_travel * 2.0f * M_PI; +#endif + + memcpy(&arc_target, target, sizeof(coord_data_t)); + + arc_target.values[plane.axis_0] = position[plane.axis_0]; + arc_target.values[plane.axis_1] = position[plane.axis_1]; + arc_target.values[plane.axis_linear] = position[plane.axis_linear]; + + while(n_turns--) { +#if N_AXIS > 3 + idx = N_AXIS; + do { + idx--; + if(!(idx == plane.axis_0 || idx == plane.axis_1)) + arc_target.values[idx] += linear_per_turn[idx]; + } while(idx); +#else + arc_target.values[plane.axis_linear] += linear_per_turn; +#endif + mc_arc(arc_target.values, pl_data, position, offset, radius, plane, turns > 0 ? 1 : -1); + memcpy(position, arc_target.values, sizeof(coord_data_t)); + } } // NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to @@ -345,6 +382,7 @@ void mc_arc (float *target, plan_line_data_t *pl_data, float *position, float *o return; } } + // Ensure last segment arrives at target location. mc_line(target, pl_data); } @@ -353,7 +391,7 @@ void mc_arc (float *target, plan_line_data_t *pl_data, float *position, float *o // By Giovanni Mascellani - https://github.com/giomasce/Marlin // Compute the linear interpolation between two real numbers. -static inline float interp(const float a, const float b, const float t) +static inline float interp (const float a, const float b, const float t) { return (1.0f - t) * a + t * b; } @@ -364,7 +402,7 @@ static inline float interp(const float a, const float b, const float t) * easy to code and has good numerical stability (very important, * since Arudino works with limited precision real numbers). */ -static inline float eval_bezier(const float a, const float b, const float c, const float d, const float t) +static inline float eval_bezier (const float a, const float b, const float c, const float d, const float t) { const float iab = interp(a, b, t), ibc = interp(b, c, t), @@ -379,7 +417,7 @@ static inline float eval_bezier(const float a, const float b, const float c, con * We approximate Euclidean distance with the sum of the coordinates * offset (so-called "norm 1"), which is quicker to compute. */ -static inline float dist1(const float x1, const float y1, const float x2, const float y2) +static inline float dist1 (const float x1, const float y1, const float x2, const float y2) { return fabsf(x1 - x2) + fabsf(y1 - y2); } @@ -424,12 +462,8 @@ static inline float dist1(const float x1, const float y1, const float x2, const * power available on Arduino, I think it is not wise to implement it. */ -void mc_cubic_b_spline (float *target, plan_line_data_t *pl_data, float *position, float *offset1, float *offset2) +void mc_cubic_b_spline (float *target, plan_line_data_t *pl_data, float *position, float *first, float *second) { - // Absolute first and second control points are recovered. - - float first[2] = { position[X_AXIS] + offset1[X_AXIS], position[Y_AXIS] + offset1[Y_AXIS] }; - float second[2] = { target[X_AXIS] + offset2[X_AXIS], target[Y_AXIS] + offset2[Y_AXIS] }; float bez_target[N_AXIS]; memcpy(bez_target, position, sizeof(float) * N_AXIS); diff --git a/motion_control.h b/motion_control.h index 5652c9c..fd837e6 100644 --- a/motion_control.h +++ b/motion_control.h @@ -43,7 +43,7 @@ bool mc_line(float *target, plan_line_data_t *pl_data); // the direction of helical travel, radius == circle radius, is_clockwise_arc boolean. Used // for vector transformation direction. void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, - plane_t plane, bool is_clockwise_arc); + plane_t plane, int32_t turns); // Execute canned cycle (drill) void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_data, float *position, plane_t plane, uint32_t repeats, gc_canned_t *canned); diff --git a/nuts_bolts.h b/nuts_bolts.h index 38b3582..faecef4 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2017-2021 Terje Io + Copyright (c) 2017-2022 Terje Io Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud @@ -131,6 +131,14 @@ typedef union { }; } axes_signals_t; +typedef union { + float values[2]; + struct { + float x; + float y; + }; +} point_2d; + #pragma pack(push, 1) //! \brief Limit switches struct, consists of four packed axes_signals_t structs. diff --git a/plugins.h b/plugins.h index 4390be3..2034102 100644 --- a/plugins.h +++ b/plugins.h @@ -88,6 +88,15 @@ typedef enum { WiFiMode_APSTA } grbl_wifi_mode_t; +typedef struct { + bool is_ethernet; + bool link_up; + uint16_t mbps; + char mac[18]; + grbl_wifi_mode_t wifi_mode; + network_settings_t status; +} network_info_t; + typedef struct { ssid_t ssid; password_t password;