diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h
index e46a3af2de..1656b837fb 100644
--- a/Marlin/Configuration_adv.h
+++ b/Marlin/Configuration_adv.h
@@ -4443,6 +4443,20 @@
#define FREEZE_STATE LOW // State of pin indicating freeze
#endif
+/**
+ * Adds realtime speed control using an external potentiometer and a free thermistor port
+ */
+//#define SPEED_DIAL_FEATURE
+#if ENABLED(SPEED_DIAL_FEATURE)
+ #define SPEED_DIAL_PIN TEMP_BED_PIN
+ #define SPEED_DIAL_MIN_SPEED 10
+ #define SPEED_DIAL_MAX_SPEED 100
+ #define SPEED_DIAL_BOARD_RESISTOR 4700
+ #define SPEED_DIAL_POT_RESISTOR 50000
+ #define SPEED_DIAL_INVERT true
+ #define SPEED_DIAL_DIAL_SENSITIVITY -2
+#endif
+
/**
* MAX7219 Debug Matrix
*
diff --git a/Marlin/src/feature/speed_dial.cpp b/Marlin/src/feature/speed_dial.cpp
new file mode 100644
index 0000000000..7a2ca7a577
--- /dev/null
+++ b/Marlin/src/feature/speed_dial.cpp
@@ -0,0 +1,100 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 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 .
+ *
+ */
+
+/**
+ * speed_dial.cpp - speed dial feature
+ */
+
+#include "../MarlinCore.h"
+#include "../HAL/shared/Delay.h"
+#include "../lcd/marlinui.h"
+#include "../gcode/gcode.h"
+#include "../module/stepper.h"
+#include "speed_dial.h"
+
+#if ENABLED(SPEED_DIAL_FEATURE)
+
+raw_adc_t SpeedDial::current_speed_dial_raw = HAL_ADC_RANGE;
+uint8_t SpeedDial::current_speed_dial = 100;
+uint8_t SpeedDial::next_value_change = SPEED_DIAL_DIAL_SENSITIVITY;
+
+SpeedDial speedDial;
+
+void SpeedDial::set(raw_adc_t value) {
+ current_speed_dial_raw = value;
+
+ #define SPEED_DIAL_MULTIPLIER (SQRT(0x7FFFFFFF / SPEED_DIAL_BOARD_RESISTOR) / 100)
+ #define SPEED_DIAL_MULTIPLIER_FINAL (SPEED_DIAL_MULTIPLIER * 100)
+ #define SPEED_DIAL_SMOOTH_VALUE_TOTAL (SPEED_DIAL_SMOOTH_VALUE + 1)
+
+ //get adc percentage
+ int32_t speed_dial_adc_percent = current_speed_dial_raw * SPEED_DIAL_MULTIPLIER_FINAL / HAL_ADC_RANGE;
+ if(speed_dial_adc_percent > SPEED_DIAL_MULTIPLIER_FINAL) speed_dial_adc_percent = SPEED_DIAL_MULTIPLIER_FINAL;
+ if(speed_dial_adc_percent < 0) speed_dial_adc_percent = 0;
+
+ #if SPEED_DIAL_INVERT
+ speed_dial_adc_percent = SPEED_DIAL_MULTIPLIER_FINAL - speed_dial_adc_percent;
+ #endif
+
+ //dial position percentage
+ int32_t speed_dial_percent = (speed_dial_adc_percent > 0 ?
+ ((SPEED_DIAL_BOARD_RESISTOR * SPEED_DIAL_MULTIPLIER_FINAL / speed_dial_adc_percent) - SPEED_DIAL_BOARD_RESISTOR) * SPEED_DIAL_MULTIPLIER_FINAL / SPEED_DIAL_POT_RESISTOR :
+ SPEED_DIAL_MULTIPLIER_FINAL);
+ if(speed_dial_percent > SPEED_DIAL_MULTIPLIER_FINAL) speed_dial_percent = SPEED_DIAL_MULTIPLIER_FINAL;
+ if(speed_dial_percent < 0) speed_dial_percent = 0;
+
+ #if SPEED_DIAL_INVERT
+ speed_dial_percent = SPEED_DIAL_MULTIPLIER_FINAL - speed_dial_percent;
+ #endif
+
+ //get actual speed value
+ int32_t speed_dial_value = (speed_dial_percent + (SPEED_DIAL_MULTIPLIER / 2)) * (SPEED_DIAL_MAX_SPEED - SPEED_DIAL_MIN_SPEED) / SPEED_DIAL_MULTIPLIER_FINAL + SPEED_DIAL_MIN_SPEED;
+
+ //clamp speed value
+ if(speed_dial_value > SPEED_DIAL_MAX_SPEED) speed_dial_value = SPEED_DIAL_MAX_SPEED;
+ if(speed_dial_value < SPEED_DIAL_MIN_SPEED) speed_dial_value = SPEED_DIAL_MIN_SPEED;
+
+ //store derived value
+ current_speed_dial = speed_dial_value;
+
+ //update stepper speed as per dial sensitivity
+ if(SPEED_DIAL_DIAL_SENSITIVITY > 0) {
+ for(uint8_t i=0; i < SPEED_DIAL_DIAL_SENSITIVITY; i++) {
+ updateStepper();
+ }
+ } else {
+ if(next_value_change) {
+ next_value_change--;
+ } else {
+ updateStepper();
+
+ next_value_change = SPEED_DIAL_DIAL_SENSITIVITY;
+ }
+ }
+}
+
+void SpeedDial::updateStepper() {
+ if(current_speed_dial > stepper.current_speed_dial()) stepper.set_speed_dial(stepper.current_speed_dial() + 1);
+ if(current_speed_dial < stepper.current_speed_dial()) stepper.set_speed_dial(stepper.current_speed_dial() - 1);
+}
+
+#endif
diff --git a/Marlin/src/feature/speed_dial.h b/Marlin/src/feature/speed_dial.h
new file mode 100644
index 0000000000..c55f456e6f
--- /dev/null
+++ b/Marlin/src/feature/speed_dial.h
@@ -0,0 +1,48 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 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 .
+ *
+ */
+#pragma once
+
+/**
+ * speed_dial.h - speed dial feature
+ */
+
+#include "../inc/MarlinConfig.h"
+
+#if ENABLED(SPEED_DIAL_FEATURE)
+
+class SpeedDial {
+ public:
+ static inline uint8_t current() { return current_speed_dial; }
+ static inline raw_adc_t raw() { return current_speed_dial_raw; }
+ static void set(raw_adc_t value);
+
+ private:
+ static void updateStepper();
+
+ static raw_adc_t current_speed_dial_raw;
+ static uint8_t current_speed_dial;
+ static uint8_t next_value_change;
+};
+
+extern SpeedDial speedDial;
+
+#endif
diff --git a/Marlin/src/gcode/temp/M105.cpp b/Marlin/src/gcode/temp/M105.cpp
index 4de5ba8eef..863ade57b6 100644
--- a/Marlin/src/gcode/temp/M105.cpp
+++ b/Marlin/src/gcode/temp/M105.cpp
@@ -23,6 +23,10 @@
#include "../gcode.h"
#include "../../module/temperature.h"
+#if ENABLED(SPEED_DIAL_FEATURE)
+ #include "../../feature/speed_dial.h"
+#endif
+
/**
* M105: Read hot end and bed temperature
*/
@@ -41,7 +45,12 @@ void GcodeSuite::M105() {
#else
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ SERIAL_ECHOPGM(" T:", speedDial.current());
+ SERIAL_ECHOLNPGM(" S@:", speedDial.raw());
+ #else
SERIAL_ECHOLNPGM(" T:0"); // Some hosts send M105 to test the serial connection
+ #endif
#endif
}
diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp
index 2a10c83800..f69f0aec96 100644
--- a/Marlin/src/module/stepper.cpp
+++ b/Marlin/src/module/stepper.cpp
@@ -203,6 +203,10 @@ uint32_t Stepper::acceleration_time, Stepper::deceleration_time;
bool Stepper::frozen; // = false
#endif
+#if ENABLED(SPEED_DIAL_FEATURE)
+ uint8_t Stepper::speed_dial_value = 100;
+#endif
+
// Delta error variables for the Bresenham line tracer
xyze_long_t Stepper::delta_error{0};
xyze_long_t Stepper::advance_dividend{0};
@@ -240,8 +244,11 @@ uint32_t Stepper::advance_divisor = 0,
* Standard Motion Linear Advance state
*/
#if ENABLED(LIN_ADVANCE)
- hal_timer_t Stepper::nextAdvanceISR = LA_ADV_NEVER,
- Stepper::la_interval = LA_ADV_NEVER;
+ hal_timer_t Stepper::nextAdvanceISR = LA_ADV_NEVER,
+ Stepper::la_interval = LA_ADV_NEVER;
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ Stepper::la_interval_nom = LA_ADV_NEVER;
+ #endif
#if ENABLED(SMOOTH_LIN_ADVANCE)
uint32_t Stepper::curr_step_rate,
Stepper::curr_timer_tick = 0;
@@ -2465,6 +2472,10 @@ void Stepper::isr() {
acceleration_time += interval;
deceleration_time = 0; // Reset since we're doing acceleration first.
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ speed_dial_adjust_interval(interval);
+ #endif
+
// Apply Nonlinear Extrusion, if enabled
calc_nonlinear_e(acc_step_rate << oversampling_factor);
@@ -2472,6 +2483,9 @@ void Stepper::isr() {
if (la_active) {
const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0;
la_interval = calc_timer_interval((acc_step_rate + la_step_rate) >> current_block->la_scaling);
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ speed_dial_adjust_interval(la_interval);
+ #endif
}
#endif
@@ -2530,6 +2544,10 @@ void Stepper::isr() {
interval = calc_multistep_timer_interval(step_rate << oversampling_factor);
deceleration_time += interval;
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ speed_dial_adjust_interval(interval);
+ #endif
+
// Apply Nonlinear Extrusion, if enabled
calc_nonlinear_e(step_rate << oversampling_factor);
@@ -2539,6 +2557,9 @@ void Stepper::isr() {
if (la_step_rate != step_rate) {
const bool forward_e = la_step_rate < step_rate;
la_interval = calc_timer_interval((forward_e ? step_rate - la_step_rate : la_step_rate - step_rate) >> current_block->la_scaling);
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ speed_dial_adjust_interval(la_interval);
+ #endif
if (forward_e != motor_direction(E_AXIS)) {
last_direction_bits.toggle(E_AXIS);
@@ -2589,8 +2610,13 @@ void Stepper::isr() {
calc_nonlinear_e(current_block->nominal_rate << oversampling_factor);
#if HAS_ROUGH_LIN_ADVANCE
- if (la_active)
- la_interval = calc_timer_interval(current_block->nominal_rate >> current_block->la_scaling);
+ if (la_active) {
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ la_interval_nom = calc_timer_interval(current_block->nominal_rate >> current_block->la_scaling);
+ #else
+ la_interval = calc_timer_interval(current_block->nominal_rate >> current_block->la_scaling);
+ #endif
+ }
#endif
// Adjust Laser Power - Cruise
@@ -2610,6 +2636,15 @@ void Stepper::isr() {
// The timer interval is just the nominal value for the nominal speed
interval = ticks_nominal;
+
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ speed_dial_adjust_interval(interval);
+
+ #if HAS_ROUGH_LIN_ADVANCE
+ la_interval = la_interval_nom;
+ speed_dial_adjust_interval(la_interval);
+ #endif
+ #endif
}
}
@@ -2921,6 +2956,10 @@ void Stepper::isr() {
// Initialize ac/deceleration time as if half the time passed.
acceleration_time = deceleration_time = interval / 2;
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ speed_dial_adjust_interval(interval);
+ #endif
+
// Apply Nonlinear Extrusion, if enabled
calc_nonlinear_e(current_block->initial_rate << oversampling_factor);
@@ -2931,6 +2970,9 @@ void Stepper::isr() {
if (la_active) {
const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0;
la_interval = calc_timer_interval((current_block->initial_rate + la_step_rate) >> current_block->la_scaling);
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ speed_dial_adjust_interval(la_interval);
+ #endif
}
#endif
#endif
@@ -3864,3 +3906,13 @@ void Stepper::report_positions() {
}
#endif // BABYSTEPPING
+
+#if ENABLED(SPEED_DIAL_FEATURE)
+
+ void Stepper::speed_dial_adjust_interval(uint32_t& interval) {
+ interval *= 100;
+ interval /= speed_dial_value;
+ if (interval == 0) interval = 1;
+ }
+
+#endif // SPEED_DIAL_FEATURE
diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h
index 6e6761f842..e4f2757875 100644
--- a/Marlin/src/module/stepper.h
+++ b/Marlin/src/module/stepper.h
@@ -371,6 +371,11 @@ class Stepper {
static bool frozen; // Set this flag to instantly freeze motion
#endif
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ static inline void set_speed_dial(uint8_t speed) { speed_dial_value = speed > 0 ? speed : 1; }
+ static inline uint8_t current_speed_dial() { return speed_dial_value; }
+ #endif
+
#if ENABLED(NONLINEAR_EXTRUSION)
static nonlinear_t ne;
#endif
@@ -487,6 +492,9 @@ class Stepper {
static constexpr hal_timer_t LA_ADV_NEVER = HAL_TIMER_TYPE_MAX;
static hal_timer_t nextAdvanceISR,
la_interval; // Interval between ISR calls for LA
+#if ENABLED(SPEED_DIAL_FEATURE)
+ static hal_timer_t la_interval_nom;
+#endif
#if ENABLED(SMOOTH_LIN_ADVANCE)
static uint32_t curr_timer_tick, // Current tick relative to block start
curr_step_rate; // Current motion step rate
@@ -800,6 +808,11 @@ class Stepper {
static void ftMotion_stepper();
#endif
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ static void speed_dial_adjust_interval(uint32_t& step_rate);
+ static uint8_t speed_dial_value;
+ #endif
+
};
extern Stepper stepper;
diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp
index f0ca49ef66..7b5ffac67b 100644
--- a/Marlin/src/module/temperature.cpp
+++ b/Marlin/src/module/temperature.cpp
@@ -37,6 +37,10 @@
#include "planner.h"
#include "printcounter.h"
+#if ENABLED(SPEED_DIAL_FEATURE)
+ #include "../feature/speed_dial.h"
+#endif
+
#if ANY(HAS_COOLER, LASER_COOLANT_FLOW_METER)
#include "../feature/cooler.h"
#include "../feature/spindle_laser.h"
@@ -3145,6 +3149,8 @@ void Temperature::init() {
TERF(HAS_JOY_ADC_EN, SET_INPUT_PULLUP)(JOY_EN_PIN);
+ TERN_(SPEED_DIAL_FEATURE, hal.adc_enable(SPEED_DIAL_PIN));
+
HAL_timer_start(MF_TIMER_TEMP, TEMP_TIMER_FREQUENCY);
ENABLE_TEMPERATURE_INTERRUPT();
@@ -4399,6 +4405,14 @@ void Temperature::isr() {
break;
#endif // HAS_ADC_BUTTONS
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ case Prepare_Speed_Dial: hal.adc_start(SPEED_DIAL_PIN); break;
+ case Measure_Speed_Dial:
+ if (!hal.adc_ready()) next_sensor_state = adc_sensor_state;
+ else speedDial.set(hal.adc_value());
+ break;
+ #endif
+
case StartupDelay: break;
} // switch(adc_sensor_state)
@@ -4530,6 +4544,9 @@ void Temperature::isr() {
#if HAS_MULTI_HOTEND
HOTEND_LOOP() s.append(F(" @"), e, ':', getHeaterPower((heater_id_t)e));
#endif
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ s.append(F(" S@:"), speedDial.current());
+# endif
s.echo();
}
diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h
index f2ef4d0f6c..ffeb90591f 100644
--- a/Marlin/src/module/temperature.h
+++ b/Marlin/src/module/temperature.h
@@ -135,6 +135,9 @@ enum ADCSensorState : char {
#if HAS_ADC_BUTTONS
Prepare_ADC_KEY, Measure_ADC_KEY,
#endif
+ #if ENABLED(SPEED_DIAL_FEATURE)
+ Prepare_Speed_Dial, Measure_Speed_Dial,
+ #endif
SensorsReady, // Temperatures ready. Delay the next round of readings to let ADC pins settle.
StartupDelay // Startup, delay initial temp reading a tiny bit so the hardware can settle
};
diff --git a/ini/features.ini b/ini/features.ini
index 03a9bdbba8..89320e70ca 100644
--- a/ini/features.ini
+++ b/ini/features.ini
@@ -383,6 +383,7 @@ IS_SCARA = build_src_filter=+ +
MORGAN_SCARA = build_src_filter=+
HAS_MICROSTEPS = build_src_filter=+
+SPEED_DIAL_FEATURE = build_src_filter=+
(ESP3D_)?WIFISUPPORT = esp32async/AsyncTCP@3.3.3, mathieucarbou/ESP Async WebServer@3.0.6
ESP3DLib=https://github.com/luc-github/ESP3DLib/archive/6d62f76c3f.zip
arduinoWebSockets=links2004/WebSockets@2.3.4