Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0cd2d5d
add G7 rotate workspace
classicrocker883 Jun 27, 2025
a7dab7c
Merge branch 'bugfix-2.1.x' into bugfix-2.1.x-June3
classicrocker883 Jul 1, 2025
491620a
rename G68
classicrocker883 Jul 1, 2025
09c6a73
update G68
classicrocker883 Jul 1, 2025
0e74e67
working G68
classicrocker883 Jul 12, 2025
b8a6033
relocate rotation_angle
classicrocker883 Jul 12, 2025
10126fc
relocate G68 stuff, remove from motion
classicrocker883 Jul 13, 2025
558a4b5
remove not necessary
classicrocker883 Jul 13, 2025
c6536e4
fix TERN
classicrocker883 Jul 13, 2025
74c788d
add way for DELTA or square bed
classicrocker883 Jul 13, 2025
003b136
relocate section CNC and safety
classicrocker883 Jul 13, 2025
f10483e
fix G68, G69, add G50, G51 (#3)
DerAndere1 Jul 22, 2025
e64387e
fix some issues, update G68, switch G50/51
classicrocker883 Jul 22, 2025
aa5f0c8
update features.ini
classicrocker883 Jul 22, 2025
4fff50a
add TERN0
classicrocker883 Jul 22, 2025
5f2aebc
revert G50/G51 naming, update Config_adv, add option to limit angle (…
classicrocker883 Jul 23, 2025
4f677de
add options for G51
classicrocker883 Jul 24, 2025
fdd899c
Make adjustments, add test
thinkyhead Aug 2, 2025
a378a20
float trig functions
thinkyhead Aug 2, 2025
f155c69
Merge branch 'bugfix-2.1.x' into pr/27945
thinkyhead Aug 2, 2025
166c167
more refinements
thinkyhead Aug 2, 2025
2deec18
cleanup
thinkyhead Aug 2, 2025
b5c4dec
more refinement
thinkyhead Aug 2, 2025
f99408f
fixes from comments, add flags
classicrocker883 Aug 4, 2025
13dc9d2
adjust gcode, add flag
classicrocker883 Aug 4, 2025
e6f42ec
use TERN0
classicrocker883 Aug 4, 2025
a222334
update get_destination_from_command function to make single move work
classicrocker883 Aug 5, 2025
2549b85
add in gcode.h
classicrocker883 Aug 5, 2025
51b7e56
cache cos() sin(), revert gcode.h private
classicrocker883 Aug 5, 2025
2e34887
update get_destination_from_command logic
classicrocker883 Aug 5, 2025
539c808
add sanitycheck, move struct
classicrocker883 Aug 5, 2025
0be7a6b
optimize invariant Z0
thinkyhead Aug 5, 2025
fa4ffd7
found it
thinkyhead Aug 5, 2025
b73504c
fix missing } and typo
classicrocker883 Aug 6, 2025
70558fd
update comments
classicrocker883 Aug 6, 2025
0cb6c91
Merge branch 'bugfix-2.1.x' into bugfix-2.1.x-June3
classicrocker883 Aug 21, 2025
91637f9
Merge branch 'bugfix-2.1.x' into bugfix-2.1.x-June3
classicrocker883 Sep 10, 2025
deb5694
Merge branch 'MarlinFirmware:bugfix-2.1.x' into bugfix-2.1.x-June3
classicrocker883 Sep 17, 2025
0d0ee8b
Merge branch 'bugfix-2.1.x' into bugfix-2.1.x-June3
classicrocker883 Oct 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 41 additions & 26 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -2289,22 +2289,6 @@

//#define FAST_BUTTON_POLLING // Poll buttons at ~1kHz on 8-bit AVR. Set to 'false' for slow polling on 32-bit.

// @section safety

/**
* The watchdog hardware timer will do a reset and disable all outputs
* if the firmware gets too overloaded to read the temperature sensors.
*
* If you find that watchdog reboot causes your AVR board to hang forever,
* enable WATCHDOG_RESET_MANUAL to use a custom timer instead of WDTO.
* NOTE: This method is less reliable as it can only catch hangups while
* interrupts are enabled.
*/
#define USE_WATCHDOG
#if ENABLED(USE_WATCHDOG)
//#define WATCHDOG_RESET_MANUAL
#endif

// @section lcd

/**
Expand Down Expand Up @@ -3644,6 +3628,33 @@

// @section cnc

/**
* CNC Coordinate Systems
*
* Enables G53 and G54-G59.3 commands to select coordinate systems
* and G92.1 to reset the workspace to native machine space.
*/
//#define CNC_COORDINATE_SYSTEMS

/**
* Coordinate System Scaling
*
* Enable G51 to scale and G50 to cancel scaling of the coordinate system.
* Mirroring can be achieved by using G51 with negative factors.
*
*/
//#define SCALE_WORKSPACE

/**
* Rotate Workspace
*
* Enable G68 to rotate and G69 to cancel rotation of the workspace.
*/
//#define ROTATE_WORKSPACE
#if ENABLED(ROTATE_WORKSPACE) && DISABLED(DELTA)
//#define LIMIT_ROTATION_ANGLE // Limit rotation on square beds
#endif

/**
* Spindle & Laser control
*
Expand Down Expand Up @@ -3911,6 +3922,20 @@

// @section safety

/**
* The watchdog hardware timer will do a reset and disable all outputs
* if the firmware gets too overloaded to read the temperature sensors.
*
* If you find that watchdog reboot causes your AVR board to hang forever,
* enable WATCHDOG_RESET_MANUAL to use a custom timer instead of WDTO.
* NOTE: This method is less reliable as it can only catch hangups while
* interrupts are enabled.
*/
#define USE_WATCHDOG
#if ENABLED(USE_WATCHDOG)
//#define WATCHDOG_RESET_MANUAL
#endif

/**
* Stepper Driver Anti-SNAFU Protection
*
Expand All @@ -3920,16 +3945,6 @@
*/
//#define DISABLE_DRIVER_SAFE_POWER_PROTECT

// @section cnc

/**
* CNC Coordinate Systems
*
* Enables G53 and G54-G59.3 commands to select coordinate systems
* and G92.1 to reset the workspace to native machine space.
*/
//#define CNC_COORDINATE_SYSTEMS

// @section security

/**
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/gcode/bedlevel/G26.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ void GcodeSuite::G26() {
#endif
float trig_table[A_CNT];
for (uint8_t i = 0; i < A_CNT; ++i)
trig_table[i] = INTERSECTION_CIRCLE_RADIUS * cos(RADIANS(i * A_INT));
trig_table[i] = INTERSECTION_CIRCLE_RADIUS * cosf(RADIANS(i * A_INT));

#endif // !ARC_SUPPORT

Expand Down
10 changes: 5 additions & 5 deletions Marlin/src/gcode/calibrate/G33.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
I_LOOP_CAL_PT(rad, start, steps) {
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
r = dcr * 0.1;
const xy_pos_t vec = { cos(a), sin(a) };
const xy_pos_t vec = { cosf(a), sinf(a) };
z_pt[CEN] += calibration_probe(vec * r, stow_after_each, probe_at_offset);
if (isnan(z_pt[CEN])) return false;
}
Expand All @@ -234,12 +234,12 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
r = dcr * (1 - 0.1 * (zig_zag ? offset - circle : circle)),
interpol = FMOD(rad, 1);
const xy_pos_t vec = { cos(a), sin(a) };
const xy_pos_t vec = { cosf(a), sinf(a) };
const float z_temp = calibration_probe(vec * r, stow_after_each, probe_at_offset);
if (isnan(z_temp)) return false;
// split probe point to neighbouring calibration points
z_pt[uint8_t(LROUND(rad - interpol + NPP - 1)) % NPP + 1] += z_temp * sq(cos(RADIANS(interpol * 90)));
z_pt[uint8_t(LROUND(rad - interpol)) % NPP + 1] += z_temp * sq(sin(RADIANS(interpol * 90)));
z_pt[uint8_t(LROUND(rad - interpol + NPP - 1)) % NPP + 1] += z_temp * sq(cosf(RADIANS(interpol * 90)));
z_pt[uint8_t(LROUND(rad - interpol)) % NPP + 1] += z_temp * sq(sinf(RADIANS(interpol * 90)));
}
FLIP(zig_zag);
}
Expand All @@ -266,7 +266,7 @@ static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_
LOOP_CAL_ALL(rad) {
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
r = (rad == CEN ? 0.0f : dcr);
pos.set(cos(a) * r, sin(a) * r, z_pt[rad]);
pos.set(cosf(a) * r, sinf(a) * r, z_pt[rad]);
inverse_kinematics(pos);
mm_at_pt_axis[rad] = delta;
}
Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/gcode/calibrate/M48.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ void GcodeSuite::M48() {
// Choose the next position as an offset to chosen test position
const xy_pos_t noz_pos = test_position - probe.offset_xy;
xy_pos_t next_pos = {
noz_pos.x + float(cos(RADIANS(angle))) * radius,
noz_pos.y + float(sin(RADIANS(angle))) * radius
noz_pos.x + float(cosf(RADIANS(angle))) * radius,
noz_pos.y + float(sinf(RADIANS(angle))) * radius
};

#if ENABLED(DELTA)
Expand Down
78 changes: 72 additions & 6 deletions Marlin/src/gcode/gcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ relative_t GcodeSuite::axis_relative; // Init in constructor
xyz_pos_t GcodeSuite::coordinate_system[MAX_COORDINATE_SYSTEMS];
#endif

#if ENABLED(SCALE_WORKSPACE)
scaling_center_t GcodeSuite::scaling_center;
scaling_factor_t GcodeSuite::scaling_factor;
#endif

#if ENABLED(ROTATE_WORKSPACE)
float GcodeSuite::rotation_angle; // = 0.0f
xy_pos_t GcodeSuite::rotation_center; // = { 0.0f, 0.0f }
#endif

void GcodeSuite::report_echo_start(const bool forReplay) { if (!forReplay) SERIAL_ECHO_START(); }
void GcodeSuite::report_heading(const bool forReplay, FSTR_P const fstr, const bool eol/*=true*/) {
if (forReplay) return;
Expand Down Expand Up @@ -171,19 +181,65 @@ void GcodeSuite::get_destination_from_command() {
constexpr bool skip_move = false;
#endif

#if ANY(ROTATE_WORKSPACE, SCALE_WORKSPACE)
static xyz_pos_t raw_destination; // {0}
#endif

// Get new XYZ position, whether absolute or relative
LOOP_NUM_AXES(i) {
if ( (seen[i] = parser.seenval(AXIS_CHAR(i))) ) {
const float v = parser.value_axis_units((AxisEnum)i);
if (skip_move)
if (skip_move) {
#if ANY(SCALE_WORKSPACE, ROTATE_WORKSPACE)
raw_destination[i] = current_position[i];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks to me like …

The current_position will contain a position that was already scaled and rotated. You must unrotate and unscale the current_position or set some flag that indicates that the raw_destination[i] coordinate is already rotated and scaled, and then don't rotate or scale that axis below.

Copy link
Contributor Author

@classicrocker883 classicrocker883 Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made flags that is set when there is rotation/scaling, not sure if thats the right approach but its there for now.

main concern is getting move commands to take a single one like G1 X100, using X+Y works fine, not sure what logic needs changing.

#else
destination[i] = current_position[i];
#endif
}
else {
const float v = parser.value_axis_units((AxisEnum)i);
#if ANY(SCALE_WORKSPACE, ROTATE_WORKSPACE)
raw_destination[i] = axis_is_relative(AxisEnum(i)) ? raw_destination[i] + v : LOGICAL_TO_NATIVE(v, i);
#else
destination[i] = axis_is_relative(AxisEnum(i)) ? current_position[i] + v : LOGICAL_TO_NATIVE(v, i);
#endif
}
}
else {
#if ANY(SCALE_WORKSPACE, ROTATE_WORKSPACE)
raw_destination[i] = current_position[i];
#else
destination[i] = current_position[i];
else
destination[i] = axis_is_relative(AxisEnum(i)) ? current_position[i] + v : LOGICAL_TO_NATIVE(v, i);
#endif
}
else
destination[i] = current_position[i];
}

#if ANY(SCALE_WORKSPACE, ROTATE_WORKSPACE)
destination = raw_destination;
#endif

#if ENABLED(SCALE_WORKSPACE)
if (!(NEAR(scaling_factor.x, 1.0f) || NEAR(scaling_factor.y, 1.0f) || NEAR(scaling_factor.z, 1.0f))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be: if (!(NEAR(scaling_factor.x, 1.0f) && NEAR(scaling_factor.y, 1.0f) && NEAR(scaling_factor.z, 1.0f)))?

Frankly, there will never be any case where a scaling factor is 0.99999 or 1.000001 when it was explicitly set to 1.0 by any method. So it should be absolutely fine to use if (scaling_factor.x != 1.0f || scaling_factor.y != 1.0f || scaling_factor.z != 1.0f).

destination.x = (raw_destination.x - scaling_center.x) * scaling_factor.x + scaling_center.x;
TERN_(HAS_Y_AXIS, destination.y = (raw_destination.y - scaling_center.y) * scaling_factor.y + scaling_center.y);
TERN_(HAS_Z_AXIS, destination.z = (raw_destination.z - scaling_center.z) * scaling_factor.z + scaling_center.z);
}
#endif

#if ENABLED(ROTATE_WORKSPACE)
if (!NEAR_ZERO(rotation_angle)) {
const float a = RADIANS(rotation_angle),
cos_angle = cosf(a),
sin_angle = sinf(a);

// Apply rotation
const xy_pos_t temp = xy_pos_t(destination) - rotation_center;
destination.set(
temp.x * cos_angle - temp.y * sin_angle + rotation_center.x,
temp.y * cos_angle + temp.x * sin_angle + rotation_center.y
);
}
#endif

#if HAS_EXTRUDERS
// Get new E position, whether absolute or relative
if ( (seen.e = parser.seenval('E')) ) {
Expand Down Expand Up @@ -435,6 +491,11 @@ void GcodeSuite::process_parsed_command(bool no_ok/*=false*/) {
case 42: G42(); break; // G42: Coordinated move to a mesh point
#endif

#if ENABLED(SCALE_WORKSPACE)
case 50: G50(); break; // G50: Cancel Workspace Scaling
case 51: G51(); break; // G51: Set Workspace Scaling
#endif

#if ENABLED(CNC_COORDINATE_SYSTEMS)
case 53: G53(); break; // G53: (prefix) Apply native workspace
case 54: G54(); break; // G54: Switch to Workspace 1
Expand All @@ -450,6 +511,11 @@ void GcodeSuite::process_parsed_command(bool no_ok/*=false*/) {
case 61: G61(); break; // G61: Apply/restore saved coordinates.
#endif

#if ENABLED(ROTATE_WORKSPACE)
case 68: G68(); break; // G68: Set Workspace Rotation
case 69: G69(); break; // G69: Cancel Workspace Rotation
#endif

#if ALL(PTC_PROBE, PTC_BED)
case 76: G76(); break; // G76: Calibrate first layer compensation values
#endif
Expand Down
41 changes: 41 additions & 0 deletions Marlin/src/gcode/gcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@
* G35 - Read bed corners to help adjust bed screws: T<screw_thread> (Requires ASSISTED_TRAMMING)
* G38 - Probe in any direction using the Z_MIN_PROBE (Requires G38_PROBE_TARGET)
* G42 - Coordinated move to a mesh point (Requires MESH_BED_LEVELING, AUTO_BED_LEVELING_BLINEAR, or AUTO_BED_LEVELING_UBL)
* G50 - Cancel Workspace Scaling (Requires SCALE_WORKSPACE)
* G51 - Set Workspace Scaling (Requires SCALE_WORKSPACE)
* G60 - Save current position. (Requires SAVED_POSITIONS)
* G61 - Apply/Restore saved coordinates. (Requires SAVED_POSITIONS)
* G68 - Set Workspace Rotation (Requires ROTATE_WORKSPACE)
* G69 - Cancel Workspace Rotation (Requires ROTATE_WORKSPACE)
* G76 - Calibrate first layer temperature offsets. (Requires PTC_PROBE and PTC_BED)
* G80 - Cancel current motion mode (Requires GCODE_MOTION_MODES)
* G90 - Use Absolute Coordinates
Expand Down Expand Up @@ -378,6 +382,23 @@ typedef bits_t(NUM_REL_MODES) relative_t;

extern const char G28_STR[];

#if ENABLED(SCALE_WORKSPACE)
typedef struct {
float x = 0.0f, y = 0.0f;
#if HAS_Z_AXIS
float z = 0.0f;
#endif
void reset() { x = y = TERN_(HAS_Z_AXIS, z =) 0.0f; }
} scaling_center_t;
typedef struct {
float x = 1.0f, y = 1.0f;
#if HAS_Z_AXIS
float z = 1.0f;
#endif
void reset() { x = y = TERN_(HAS_Z_AXIS, z =) 1.0f; }
} scaling_factor_t;
#endif

class GcodeSuite {
public:

Expand Down Expand Up @@ -431,6 +452,16 @@ class GcodeSuite {
static bool select_coordinate_system(const int8_t _new);
#endif

#if ENABLED(SCALE_WORKSPACE)
static scaling_center_t scaling_center;
static scaling_factor_t scaling_factor;
#endif

#if ENABLED(ROTATE_WORKSPACE)
static float rotation_angle;
static xy_pos_t rotation_center;
#endif

static millis_t previous_move_ms, max_inactive_time;
FORCE_INLINE static bool stepper_max_timed_out(const millis_t ms=millis()) {
return max_inactive_time && ELAPSED(ms, previous_move_ms, max_inactive_time);
Expand Down Expand Up @@ -609,6 +640,11 @@ class GcodeSuite {
static void G42();
#endif

#if ENABLED(SCALE_WORKSPACE)
static void G50();
static void G51();
#endif

#if ENABLED(CNC_COORDINATE_SYSTEMS)
static void G53();
static void G54();
Expand All @@ -628,6 +664,11 @@ class GcodeSuite {
static void G61(int8_t slot=-1);
#endif

#if ENABLED(ROTATE_WORKSPACE)
static void G68();
static void G69();
#endif

#if ENABLED(GCODE_MOTION_MODES)
static void G80();
#endif
Expand Down
9 changes: 7 additions & 2 deletions Marlin/src/gcode/geometry/G17-G19.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,13 @@ inline void report_workspace_plane() {
}

inline void set_workspace_plane(const GcodeSuite::WorkspacePlane plane) {
gcode.workspace_plane = plane;
if (DEBUGGING(INFO)) report_workspace_plane();
if (TERN0(ROTATE_WORKSPACE, !NEAR_ZERO(gcode.rotation_angle))) {
SERIAL_ECHOLNPGM("Error: Workspace plane cannnot change while using workspace rotation.");
}
else {
gcode.workspace_plane = plane;
if (DEBUGGING(INFO)) report_workspace_plane();
}
}

/**
Expand Down
Loading