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.
This commit is contained in:
Terje Io
2022-07-19 21:51:46 +02:00
parent 651a2a903b
commit b4c896e2ad
9 changed files with 174 additions and 28 deletions

View File

@@ -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

View File

@@ -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.

82
gcode.c
View File

@@ -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:

View File

@@ -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

2
grbl.h
View File

@@ -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!

View File

@@ -26,6 +26,7 @@
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#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);

View File

@@ -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);

View File

@@ -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.

View File

@@ -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;