Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -2750,6 +2750,22 @@
//#define REALTIME_REPORTING_COMMANDS
#if ENABLED(REALTIME_REPORTING_COMMANDS)
//#define FULL_REPORT_TO_HOST_FEATURE // Auto-report the machine status like Grbl CNC

/**
* If enabled, P000 and R000 commands will apply ramping.
*
* REALTIME_RAMPING_STEP determines the ramping resolution.
* Acceptable values: 125, 250, or 500.
*
* REALTIME_RAMPING_STEP_DURATION controls how quickly the stop is executed.
* Valid range: 1 to 10 — lower values result in faster stops.
*/
//#define REALTIME_RAMPING
#if ENABLED(REALTIME_RAMPING)
#define REALTIME_RAMPING_STEP 125
#define REALTIME_RAMPING_STEP_DURATION 5
#endif

#endif

/**
Expand Down Expand Up @@ -3720,6 +3736,9 @@
#define SPINDLE_LASER_POWERUP_DELAY 5000 // (ms) Delay to allow the spindle/laser to come up to speed/power
#define SPINDLE_LASER_POWERDOWN_DELAY 5000 // (ms) Delay to allow the spindle to stop

#define MIN_SPINDLE_OVERRIDE 10 // (%) (Min. 1) Minimum allowed spindle override
#define MAX_SPINDLE_OVERRIDE 200 // (%) (Max. 255) Maximum allowed spindle override

/**
* M3/M4 Power Equation
*
Expand Down
3 changes: 3 additions & 0 deletions Marlin/src/MarlinCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,9 @@ void idle(const bool no_stepper_sleep/*=false*/) {
// Manage Heaters (and Watchdog)
thermalManager.task();

// Realtime pause/resume ramping loop
TERN_(REALTIME_RAMPING, updateSoftStopResume());

// Max7219 heartbeat, animation, etc
TERN_(MAX7219_DEBUG, max7219.idle_tasks());

Expand Down
9 changes: 7 additions & 2 deletions Marlin/src/feature/e_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ extern bool wait_for_user, wait_for_heatup;
void quickresume_stepper();
#endif

#if ENABLED(REALTIME_RAMPING)
bool realtime_ramping_pause_flag, // = false
realtime_ramping_resume_flag; // = false
#endif

void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) {
auto uppercase = [](char c) {
return TERN0(GCODE_CASE_INSENSITIVE, WITHIN(c, 'a', 'z')) ? c + 'A' - 'a' : c;
Expand Down Expand Up @@ -207,8 +212,8 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) {
#endif
#if ENABLED(REALTIME_REPORTING_COMMANDS)
case EP_GRBL_STATUS: report_current_position_moving(); break;
case EP_GRBL_PAUSE: quickpause_stepper(); break;
case EP_GRBL_RESUME: quickresume_stepper(); break;
case EP_GRBL_PAUSE: TERN(REALTIME_RAMPING, realtime_ramping_pause_flag = true, quickpause_stepper()); break;
case EP_GRBL_RESUME: TERN(REALTIME_RAMPING, realtime_ramping_resume_flag = true, quickresume_stepper()); break;
#endif
#if ENABLED(SOFT_RESET_VIA_SERIAL)
case EP_KILL: hal.reboot(); break;
Expand Down
4 changes: 4 additions & 0 deletions Marlin/src/feature/e_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,7 @@ class EmergencyParser {
};

extern EmergencyParser emergency_parser;

#if ENABLED(REALTIME_RAMPING)
extern bool realtime_ramping_pause_flag, realtime_ramping_resume_flag;
#endif
12 changes: 11 additions & 1 deletion Marlin/src/feature/spindle_laser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,17 @@ cutter_power_t SpindleLaser::menuPower = 0, // Power v

cutter_frequency_t SpindleLaser::frequency; // PWM frequency setting; range: 2K - 50K

#if ENABLED(SPINDLE_FEATURE)
uint16_t SpindleLaser::spindle_override; // M222 Power Override for the Spindle
#endif

#define SPINDLE_LASER_PWM_OFF TERN(SPINDLE_LASER_PWM_INVERT, 255, 0)

/**
* Init the cutter to a safe OFF state
*/
void SpindleLaser::init() {
TERN_(SPINDLE_FEATURE, spindle_override = 100);
#if ENABLED(SPINDLE_SERVO)
servo[SPINDLE_SERVO_NR].move(SPINDLE_SERVO_MIN);
#elif PIN_EXISTS(SPINDLE_LASER_ENA)
Expand Down Expand Up @@ -100,7 +105,12 @@ void SpindleLaser::init() {
*
* @param ocr Power value
*/
void SpindleLaser::_set_ocr(const uint8_t ocr) {
void SpindleLaser::_set_ocr(const uint8_t unscaledOcr) {

// Apply spindle override
const uint16_t scaled = MUL_TERN(static_cast<uint16_t>(unscaledOcr), spindle_override);
const uint8_t ocr = TERN(SPINDLE_FEATURE, scaled > 25500 ? 255 : scaled / 100, scaled > 255 ? 255 : scaled);

#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency);
#endif
Expand Down
6 changes: 5 additions & 1 deletion Marlin/src/feature/spindle_laser.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ class SpindleLaser {
static cutter_power_t menuPower, // Power as set via LCD menu in PWM, Percentage, or RPM
unitPower; // Power as displayed status in PWM, Percentage, or RPM

#if ENABLED(SPINDLE_FEATURE)
static uint16_t spindle_override; // Spindle speed override by percentage
#endif

#if HAS_SPINDLE_ACCELERATION
static uint32_t acceleration_spindle_deg_per_s2; // (°/s/s) Spindle acceleration
#endif
Expand All @@ -135,7 +139,7 @@ class SpindleLaser {

private:

static void _set_ocr(const uint8_t ocr);
static void _set_ocr(const uint8_t unscaledOcr);

public:

Expand Down
45 changes: 45 additions & 0 deletions Marlin/src/gcode/config/M222.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program 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.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

#include "../gcode.h"
#include "../../feature/spindle_laser.h"

#if ENABLED(SPINDLE_FEATURE)

/**
* M222: Set/read spindle override (M222 S120)
*/

void GcodeSuite::M222() {
if (parser.seenval('S')) {
const uint8_t new_percentage = constrain((uint8_t)parser.value_int(), MIN_SPINDLE_OVERRIDE, MAX_SPINDLE_OVERRIDE);
if (new_percentage != cutter.spindle_override) {
cutter.spindle_override = new_percentage;
if (cutter.enable_state)
cutter.set_ocr(cutter.upower_to_ocr(cutter.unitPower));
}
}

SERIAL_ECHOLNPGM("SPINDLE-OVERRIDE:", cutter.spindle_override);
}

#endif // SPINDLE_FEATURE
4 changes: 4 additions & 0 deletions Marlin/src/gcode/gcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,10 @@ void GcodeSuite::process_parsed_command(bool no_ok/*=false*/) {
case 221: M221(); break; // M221: Set Flow Percentage
#endif

#if ENABLED(SPINDLE_FEATURE)
case 222: M222(); break; // M222: Spindle override
#endif

#if ENABLED(DIRECT_PIN_CONTROL)
case 226: M226(); break; // M226: Wait until a pin reaches a state
#endif
Expand Down
5 changes: 5 additions & 0 deletions Marlin/src/gcode/gcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
* M220 - Set Feedrate Percentage: 'M220 S<percent>' (i.e., "FR" on the LCD)
* Use 'M220 B' to back up the Feedrate Percentage and 'M220 R' to restore it. (Requires an MMU_MODEL version 2 or 2S)
* M221 - Set Flow Percentage: 'M221 S<percent>' (Requires an extruder)
* M222 - Set Spindle Override: 'M222 S<percent>' (Requires SPINDLE_FEATURE)
* M226 - Wait until a pin is in a given state: 'M226 P<pin> S<state>' (Requires DIRECT_PIN_CONTROL)
* M240 - Trigger a camera to take a photograph. (Requires PHOTO_GCODE)
* M250 - Set LCD contrast: 'M250 C<contrast>' (0-63). (Requires LCD support)
Expand Down Expand Up @@ -937,6 +938,10 @@ class GcodeSuite {
static void M221();
#endif

#if ENABLED(SPINDLE_FEATURE)
static void M222();
#endif

#if ENABLED(DIRECT_PIN_CONTROL)
static void M226();
#endif
Expand Down
11 changes: 10 additions & 1 deletion Marlin/src/inc/SanityCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -4163,7 +4163,16 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
static_assert(LASER_SAFETY_TIMEOUT_MS < (DEFAULT_STEPPER_TIMEOUT_SEC) * 1000UL, "LASER_SAFETY_TIMEOUT_MS must be less than DEFAULT_STEPPER_TIMEOUT_SEC (" STRINGIFY(DEFAULT_STEPPER_TIMEOUT_SEC) " seconds)");
#endif

#endif
#if ENABLED(SPINDLE_FEATURE)
#if !WITHIN(MIN_SPINDLE_OVERRIDE, 1, 100)
#error "MIN_SPINDLE_OVERRIDE must be an integer power value from 1 to 100."
#elif !WITHIN(MAX_SPINDLE_OVERRIDE, 100, 255)
#error "MAX_SPINDLE_OVERRIDE must be an integer power value from 100 to 255."
#elif MAX_SPINDLE_OVERRIDE < MIN_SPINDLE_OVERRIDE
#error "MAX_SPINDLE_OVERRIDE must be >= MIN_SPINDLE_OVERRIDE."
#endif
#endif
#endif // HAS_CUTTER

#if ENABLED(COOLANT_CONTROL)
#if NONE(COOLANT_MIST, COOLANT_FLOOD)
Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/inc/Warnings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,10 +927,10 @@
#endif

/**
* User doesn't have or disabled G92?
* User doesn't have or disabled M92?
*/
#if DISABLED(EDITABLE_STEPS_PER_UNIT)
#warning "EDITABLE_STEPS_PER_UNIT is required to enable G92 runtime configuration of steps-per-unit."
#warning "EDITABLE_STEPS_PER_UNIT is required to enable M92 runtime configuration of steps-per-unit."
#endif

/**
Expand Down
81 changes: 81 additions & 0 deletions Marlin/src/module/motion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@
#include "../feature/bedlevel/bdl/bdl.h"
#endif

#if ENABLED(REALTIME_RAMPING)
#include "../feature/e_parser.h"
#endif

// Relative Mode. Enable with G91, disable with G90.
bool relative_mode; // = false

Expand Down Expand Up @@ -2860,3 +2864,80 @@ void set_axis_is_at_home(const AxisEnum axis) {
home_offset[axis] = v;
}
#endif

#if ENABLED(REALTIME_RAMPING)

static bool smooth_motion_flag = false;
static millis_t smooth_motion_start = 0;
static bool smooth_stopped_flag = false;

void realtime_soft_stop() {
if (!smooth_motion_flag) {
smooth_motion_flag = true;
stepper.isr_ramp_factor = MAX_REALTIME_RAMPING_FACTOR;
smooth_motion_start = millis();
}

if (stepper.isr_ramp_factor <= MIN_REALTIME_RAMPING_FACTOR) {
stepper.isr_ramp_factor = MIN_REALTIME_RAMPING_FACTOR;
smooth_motion_flag = false;
realtime_ramping_pause_flag = false;
smooth_stopped_flag = true;
quickpause_stepper();
set_and_report_grblstate(M_HOLD);
return;
}

const millis_t smooth_now = millis();
if (smooth_now - smooth_motion_start >= REALTIME_RAMPING_STEP_DURATION) {
stepper.isr_ramp_factor -= REALTIME_RAMPING_STEP;
smooth_motion_start = smooth_now;
}
}

void realtime_soft_resume() {
if (!smooth_motion_flag) {
smooth_motion_flag = true;
stepper.isr_ramp_factor = MIN_REALTIME_RAMPING_FACTOR;
smooth_motion_start = millis();

if (!stepper.is_awake()) stepper.wake_up();

if ( TERN0(HAS_X_AXIS, stepper.axis_is_moving(X_AXIS))
|| TERN0(HAS_Y_AXIS, stepper.axis_is_moving(Y_AXIS))
|| TERN0(HAS_Z_AXIS, stepper.axis_is_moving(Z_AXIS))
|| TERN0(HAS_EXTRUDERS, stepper.axis_is_moving(E_AXIS))
) {
set_and_report_grblstate(M_RUNNING);
}
else {
set_and_report_grblstate(M_IDLE);
}
}

if (stepper.isr_ramp_factor >= MAX_REALTIME_RAMPING_FACTOR) {
stepper.isr_ramp_factor = MAX_REALTIME_RAMPING_FACTOR;
smooth_motion_flag = false;
realtime_ramping_resume_flag = false;
return;
}

const millis_t smooth_now = millis();

if (smooth_now - smooth_motion_start >= REALTIME_RAMPING_STEP_DURATION) {
stepper.isr_ramp_factor += REALTIME_RAMPING_STEP;
smooth_motion_start = smooth_now;
}
}

void updateSoftStopResume() {
if (realtime_ramping_pause_flag) {
realtime_ramping_resume_flag = false; // Prioritize pause in case of a conflict
realtime_soft_stop();
}
else if (realtime_ramping_resume_flag) {
realtime_soft_resume();
}
}

#endif // REALTIME_RAMPING
6 changes: 6 additions & 0 deletions Marlin/src/module/motion.h
Original file line number Diff line number Diff line change
Expand Up @@ -652,3 +652,9 @@ void home_if_needed(const bool keeplev=false);
void set_homing_current(const AxisEnum axis);
void restore_homing_current(const AxisEnum axis);
#endif

#if ENABLED(REALTIME_RAMPING)
void realtime_soft_stop();
void realtime_soft_resume();
void updateSoftStopResume();
#endif
17 changes: 17 additions & 0 deletions Marlin/src/module/stepper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ Stepper stepper; // Singleton

// public:

#if ENABLED(REALTIME_RAMPING)
volatile uint16_t Stepper::isr_ramp_factor;
#endif

#if ANY(HAS_EXTRA_ENDSTOPS, Z_STEPPER_AUTO_ALIGN)
bool Stepper::separate_multi_axis = false;
#endif
Expand Down Expand Up @@ -1713,6 +1717,15 @@ void Stepper::isr() {
// Now 'next_isr_ticks' contains the period to the next Stepper ISR - And we are
// sure that the time has not arrived yet - Warrantied by the scheduler

#if ENABLED(REALTIME_RAMPING)
if (isr_ramp_factor != MAX_REALTIME_RAMPING_FACTOR) {
// Scale the ISR frequency by isr_ramp_factor
uint32_t temp_isr_ticks = (uint32_t)next_isr_ticks * 10000UL / isr_ramp_factor;
//LIMIT(temp_isr_ticks, 0U, 65535);
next_isr_ticks = (hal_timer_t)temp_isr_ticks;
}
#endif

// Set the next ISR to fire at the proper time
HAL_timer_set_compare(MF_TIMER_STEP, next_isr_ticks);

Expand Down Expand Up @@ -3135,6 +3148,10 @@ bool Stepper::is_block_busy(const block_t * const block) {

void Stepper::init() {

#if ENABLED(REALTIME_RAMPING)
isr_ramp_factor = MAX_REALTIME_RAMPING_FACTOR;
#endif

#if MB(ALLIGATOR)
const float motor_current[] = MOTOR_CURRENT;
unsigned int digipot_motor = 0;
Expand Down
11 changes: 11 additions & 0 deletions Marlin/src/module/stepper.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,12 @@ constexpr ena_mask_t enable_overlap[] = {

#endif // NONLINEAR_EXTRUSION

// Pause resume ramping constants
#if ENABLED(REALTIME_RAMPING)
#define MIN_REALTIME_RAMPING_FACTOR 500
#define MAX_REALTIME_RAMPING_FACTOR 10000
#endif

//
// Stepper class definition
//
Expand All @@ -329,6 +335,11 @@ class Stepper {

public:

// Pause resume ramping factor
#if ENABLED(REALTIME_RAMPING)
static volatile uint16_t isr_ramp_factor;
#endif

// The minimal step rate ensures calculations stay within limits
// and avoid the most unreasonably slow step rates.
static constexpr uint32_t minimal_step_rate = (
Expand Down
Loading
Loading