update code base to Marlin 2.0.9.2
This commit is contained in:
54
Marlin/src/feature/ammeter.cpp
Normal file
54
Marlin/src/feature/ammeter.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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 "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(I2C_AMMETER)
|
||||
|
||||
#include "ammeter.h"
|
||||
|
||||
#ifndef I2C_AMMETER_IMAX
|
||||
#define I2C_AMMETER_IMAX 0.500 // Calibration range 500 Milliamps
|
||||
#endif
|
||||
|
||||
INA226 ina;
|
||||
|
||||
Ammeter ammeter;
|
||||
|
||||
float Ammeter::scale;
|
||||
float Ammeter::current;
|
||||
|
||||
void Ammeter::init() {
|
||||
ina.begin();
|
||||
ina.configure(INA226_AVERAGES_16, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT);
|
||||
ina.calibrate(I2C_AMMETER_SHUNT_RESISTOR, I2C_AMMETER_IMAX);
|
||||
}
|
||||
|
||||
float Ammeter::read() {
|
||||
scale = 1;
|
||||
current = ina.readShuntCurrent();
|
||||
if (current <= 0.0001f) current = 0; // Clean up least-significant-bit amplification errors
|
||||
if (current < 0.1f) scale = 1000;
|
||||
return current * scale;
|
||||
}
|
||||
|
||||
#endif // I2C_AMMETER
|
||||
39
Marlin/src/feature/ammeter.h
Normal file
39
Marlin/src/feature/ammeter.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#include <Wire.h>
|
||||
#include <INA226.h>
|
||||
|
||||
class Ammeter {
|
||||
private:
|
||||
static float scale;
|
||||
|
||||
public:
|
||||
static float current;
|
||||
static void init();
|
||||
static float read();
|
||||
};
|
||||
|
||||
extern Ammeter ammeter;
|
||||
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
|
||||
#include "babystep.h"
|
||||
#include "../MarlinCore.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../module/motion.h" // for axes_should_home()
|
||||
#include "../module/planner.h" // for axis_steps_per_mm[]
|
||||
#include "../module/stepper.h"
|
||||
|
||||
#if ENABLED(BABYSTEP_ALWAYS_AVAILABLE)
|
||||
@@ -49,71 +50,18 @@ void Babystep::step_axis(const AxisEnum axis) {
|
||||
}
|
||||
}
|
||||
|
||||
void Babystep::add_mm(const AxisEnum axis, const float &mm) {
|
||||
void Babystep::add_mm(const AxisEnum axis, const_float_t mm) {
|
||||
add_steps(axis, mm * planner.settings.axis_steps_per_mm[axis]);
|
||||
}
|
||||
|
||||
void Babystep::add_steps(const AxisEnum axis, const int16_t distance) {
|
||||
|
||||
if (DISABLED(BABYSTEP_WITHOUT_HOMING) && !TEST(axis_known_position, axis)) return;
|
||||
if (DISABLED(BABYSTEP_WITHOUT_HOMING) && axes_should_home(_BV(axis))) return;
|
||||
|
||||
accum += distance; // Count up babysteps for the UI
|
||||
#if ENABLED(BABYSTEP_DISPLAY_TOTAL)
|
||||
axis_total[BS_TOTAL_IND(axis)] += distance;
|
||||
#endif
|
||||
|
||||
#if ENABLED(BABYSTEP_ALWAYS_AVAILABLE)
|
||||
#define BSA_ENABLE(AXIS) do{ switch (AXIS) { case X_AXIS: ENABLE_AXIS_X(); break; case Y_AXIS: ENABLE_AXIS_Y(); break; case Z_AXIS: ENABLE_AXIS_Z(); break; default: break; } }while(0)
|
||||
#else
|
||||
#define BSA_ENABLE(AXIS) NOOP
|
||||
#endif
|
||||
|
||||
#if IS_CORE
|
||||
#if ENABLED(BABYSTEP_XY)
|
||||
switch (axis) {
|
||||
case CORE_AXIS_1: // X on CoreXY and CoreXZ, Y on CoreYZ
|
||||
BSA_ENABLE(CORE_AXIS_1);
|
||||
BSA_ENABLE(CORE_AXIS_2);
|
||||
steps[CORE_AXIS_1] += distance * 2;
|
||||
steps[CORE_AXIS_2] += distance * 2;
|
||||
break;
|
||||
case CORE_AXIS_2: // Y on CoreXY, Z on CoreXZ and CoreYZ
|
||||
BSA_ENABLE(CORE_AXIS_1);
|
||||
BSA_ENABLE(CORE_AXIS_2);
|
||||
steps[CORE_AXIS_1] += CORESIGN(distance * 2);
|
||||
steps[CORE_AXIS_2] -= CORESIGN(distance * 2);
|
||||
break;
|
||||
case NORMAL_AXIS: // Z on CoreXY, Y on CoreXZ, X on CoreYZ
|
||||
default:
|
||||
BSA_ENABLE(NORMAL_AXIS);
|
||||
steps[NORMAL_AXIS] += distance;
|
||||
break;
|
||||
}
|
||||
#elif CORE_IS_XZ || CORE_IS_YZ
|
||||
// Only Z stepping needs to be handled here
|
||||
BSA_ENABLE(CORE_AXIS_1);
|
||||
BSA_ENABLE(CORE_AXIS_2);
|
||||
steps[CORE_AXIS_1] += CORESIGN(distance * 2);
|
||||
steps[CORE_AXIS_2] -= CORESIGN(distance * 2);
|
||||
#else
|
||||
BSA_ENABLE(Z_AXIS);
|
||||
steps[Z_AXIS] += distance;
|
||||
#endif
|
||||
#else
|
||||
#if ENABLED(BABYSTEP_XY)
|
||||
BSA_ENABLE(axis);
|
||||
#else
|
||||
BSA_ENABLE(Z_AXIS);
|
||||
#endif
|
||||
steps[BS_AXIS_IND(axis)] += distance;
|
||||
#endif
|
||||
#if ENABLED(BABYSTEP_ALWAYS_AVAILABLE)
|
||||
gcode.reset_stepper_timeout();
|
||||
#endif
|
||||
|
||||
#if ENABLED(INTEGRATED_BABYSTEPPING)
|
||||
if (has_steps()) stepper.initiateBabystepping();
|
||||
#endif
|
||||
steps[BS_AXIS_IND(axis)] += distance;
|
||||
TERN_(BABYSTEP_DISPLAY_TOTAL, axis_total[BS_TOTAL_IND(axis)] += distance);
|
||||
TERN_(BABYSTEP_ALWAYS_AVAILABLE, gcode.reset_stepper_timeout());
|
||||
TERN_(INTEGRATED_BABYSTEPPING, if (has_steps()) stepper.initiateBabystepping());
|
||||
}
|
||||
|
||||
#endif // BABYSTEPPING
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -55,16 +55,13 @@ public:
|
||||
#if ENABLED(BABYSTEP_DISPLAY_TOTAL)
|
||||
static int16_t axis_total[BS_TOTAL_IND(Z_AXIS) + 1]; // Total babysteps since G28
|
||||
static inline void reset_total(const AxisEnum axis) {
|
||||
if (true
|
||||
#if ENABLED(BABYSTEP_XY)
|
||||
&& axis == Z_AXIS
|
||||
#endif
|
||||
) axis_total[BS_TOTAL_IND(axis)] = 0;
|
||||
if (TERN1(BABYSTEP_XY, axis == Z_AXIS))
|
||||
axis_total[BS_TOTAL_IND(axis)] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void add_steps(const AxisEnum axis, const int16_t distance);
|
||||
static void add_mm(const AxisEnum axis, const float &mm);
|
||||
static void add_mm(const AxisEnum axis, const_float_t mm);
|
||||
|
||||
static inline bool has_steps() {
|
||||
return steps[BS_AXIS_IND(X_AXIS)] || steps[BS_AXIS_IND(Y_AXIS)] || steps[BS_AXIS_IND(Z_AXIS)];
|
||||
|
||||
67
Marlin/src/feature/backlash.cpp
Executable file → Normal file
67
Marlin/src/feature/backlash.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -60,13 +60,27 @@ Backlash backlash;
|
||||
* spread over multiple segments, smoothing out artifacts even more.
|
||||
*/
|
||||
|
||||
void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const uint8_t dm, block_t * const block) {
|
||||
static uint8_t last_direction_bits;
|
||||
uint8_t changed_dir = last_direction_bits ^ dm;
|
||||
// Ignore direction change if no steps are taken in that direction
|
||||
if (da == 0) CBI(changed_dir, X_AXIS);
|
||||
if (db == 0) CBI(changed_dir, Y_AXIS);
|
||||
if (dc == 0) CBI(changed_dir, Z_AXIS);
|
||||
void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const axis_bits_t dm, block_t * const block) {
|
||||
static axis_bits_t last_direction_bits;
|
||||
axis_bits_t changed_dir = last_direction_bits ^ dm;
|
||||
// Ignore direction change unless steps are taken in that direction
|
||||
#if DISABLED(CORE_BACKLASH) || ENABLED(MARKFORGED_XY)
|
||||
if (!da) CBI(changed_dir, X_AXIS);
|
||||
if (!db) CBI(changed_dir, Y_AXIS);
|
||||
if (!dc) CBI(changed_dir, Z_AXIS);
|
||||
#elif CORE_IS_XY
|
||||
if (!(da + db)) CBI(changed_dir, X_AXIS);
|
||||
if (!(da - db)) CBI(changed_dir, Y_AXIS);
|
||||
if (!dc) CBI(changed_dir, Z_AXIS);
|
||||
#elif CORE_IS_XZ
|
||||
if (!(da + dc)) CBI(changed_dir, X_AXIS);
|
||||
if (!(da - dc)) CBI(changed_dir, Z_AXIS);
|
||||
if (!db) CBI(changed_dir, Y_AXIS);
|
||||
#elif CORE_IS_YZ
|
||||
if (!(db + dc)) CBI(changed_dir, Y_AXIS);
|
||||
if (!(db - dc)) CBI(changed_dir, Z_AXIS);
|
||||
if (!da) CBI(changed_dir, X_AXIS);
|
||||
#endif
|
||||
last_direction_bits ^= changed_dir;
|
||||
|
||||
if (correction == 0) return;
|
||||
@@ -90,7 +104,7 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const
|
||||
|
||||
const float f_corr = float(correction) / 255.0f;
|
||||
|
||||
LOOP_XYZ(axis) {
|
||||
LOOP_LINEAR_AXES(axis) {
|
||||
if (distance_mm[axis]) {
|
||||
const bool reversing = TEST(dm,axis);
|
||||
|
||||
@@ -105,42 +119,57 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const
|
||||
// Take up a portion of the residual_error in this segment, but only when
|
||||
// the current segment travels in the same direction as the correction
|
||||
if (reversing == (error_correction < 0)) {
|
||||
if (segment_proportion == 0)
|
||||
segment_proportion = _MIN(1.0f, block->millimeters / smoothing_mm);
|
||||
if (segment_proportion == 0) segment_proportion = _MIN(1.0f, block->millimeters / smoothing_mm);
|
||||
error_correction = CEIL(segment_proportion * error_correction);
|
||||
}
|
||||
else
|
||||
error_correction = 0; // Don't take up any backlash in this segment, as it would subtract steps
|
||||
}
|
||||
#endif
|
||||
// Making a correction reduces the residual error and adds block steps
|
||||
|
||||
// This correction reduces the residual error and adds block steps
|
||||
if (error_correction) {
|
||||
block->steps[axis] += ABS(error_correction);
|
||||
residual_error[axis] -= error_correction;
|
||||
#if ENABLED(CORE_BACKLASH)
|
||||
switch (axis) {
|
||||
case CORE_AXIS_1:
|
||||
//block->steps[CORE_AXIS_2] += influence_distance_mm[axis] * planner.settings.axis_steps_per_mm[CORE_AXIS_2];
|
||||
//SERIAL_ECHOLNPGM("CORE_AXIS_1 dir change. distance=", distance_mm[axis], " r.err=", residual_error[axis],
|
||||
// " da=", da, " db=", db, " block->steps[axis]=", block->steps[axis], " err_corr=", error_correction);
|
||||
break;
|
||||
case CORE_AXIS_2:
|
||||
//block->steps[CORE_AXIS_1] += influence_distance_mm[axis] * planner.settings.axis_steps_per_mm[CORE_AXIS_1];;
|
||||
//SERIAL_ECHOLNPGM("CORE_AXIS_2 dir change. distance=", distance_mm[axis], " r.err=", residual_error[axis],
|
||||
// " da=", da, " db=", db, " block->steps[axis]=", block->steps[axis], " err_corr=", error_correction);
|
||||
break;
|
||||
case NORMAL_AXIS: break;
|
||||
}
|
||||
residual_error[axis] = 0; // No residual_error needed for next CORE block, I think...
|
||||
#else
|
||||
residual_error[axis] -= error_correction;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
#if HAS_CUSTOM_PROBE_PIN
|
||||
#define TEST_PROBE_PIN (READ(Z_MIN_PROBE_PIN) != Z_MIN_PROBE_ENDSTOP_INVERTING)
|
||||
#else
|
||||
#define TEST_PROBE_PIN (READ(Z_MIN_PIN) != Z_MIN_ENDSTOP_INVERTING)
|
||||
#endif
|
||||
|
||||
#include "../module/probe.h"
|
||||
|
||||
// Measure Z backlash by raising nozzle in increments until probe deactivates
|
||||
void Backlash::measure_with_probe() {
|
||||
if (measured_count.z == 255) return;
|
||||
|
||||
const float start_height = current_position.z;
|
||||
while (current_position.z < (start_height + BACKLASH_MEASUREMENT_LIMIT) && TEST_PROBE_PIN)
|
||||
while (current_position.z < (start_height + BACKLASH_MEASUREMENT_LIMIT) && PROBE_TRIGGERED())
|
||||
do_blocking_move_to_z(current_position.z + BACKLASH_MEASUREMENT_RESOLUTION, MMM_TO_MMS(BACKLASH_MEASUREMENT_FEEDRATE));
|
||||
|
||||
// The backlash from all probe points is averaged, so count the number of measurements
|
||||
measured_mm.z += current_position.z - start_height;
|
||||
measured_count.z++;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // BACKLASH_COMPENSATION
|
||||
|
||||
28
Marlin/src/feature/backlash.h
Executable file → Normal file
28
Marlin/src/feature/backlash.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
static float smoothing_mm;
|
||||
#endif
|
||||
|
||||
static inline void set_correction(const float &v) { correction = _MAX(0, _MIN(1.0, v)) * all_on; }
|
||||
static inline void set_correction(const_float_t v) { correction = _MAX(0, _MIN(1.0, v)) * all_on; }
|
||||
static inline float get_correction() { return float(ui8_to_percent(correction)) / 100.0f; }
|
||||
#else
|
||||
static constexpr uint8_t correction = (BACKLASH_CORRECTION) * 0xFF;
|
||||
@@ -54,34 +54,24 @@ public:
|
||||
#endif
|
||||
|
||||
static inline float get_measurement(const AxisEnum a) {
|
||||
UNUSED(a);
|
||||
// Return the measurement averaged over all readings
|
||||
return (
|
||||
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
measured_count[a] > 0 ? measured_mm[a] / measured_count[a] :
|
||||
#endif
|
||||
0
|
||||
return TERN(MEASURE_BACKLASH_WHEN_PROBING
|
||||
, measured_count[a] > 0 ? measured_mm[a] / measured_count[a] : 0
|
||||
, 0
|
||||
);
|
||||
#if DISABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
UNUSED(a);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool has_measurement(const AxisEnum a) {
|
||||
return (false
|
||||
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
|| (measured_count[a] > 0)
|
||||
#endif
|
||||
);
|
||||
#if DISABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
UNUSED(a);
|
||||
#endif
|
||||
UNUSED(a);
|
||||
return TERN0(MEASURE_BACKLASH_WHEN_PROBING, measured_count[a] > 0);
|
||||
}
|
||||
|
||||
static inline bool has_any_measurement() {
|
||||
return has_measurement(X_AXIS) || has_measurement(Y_AXIS) || has_measurement(Z_AXIS);
|
||||
}
|
||||
|
||||
void add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const uint8_t dm, block_t * const block);
|
||||
void add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const axis_bits_t dm, block_t * const block);
|
||||
};
|
||||
|
||||
extern Backlash backlash;
|
||||
|
||||
2
Marlin/src/feature/baricuda.cpp
Executable file → Normal file
2
Marlin/src/feature/baricuda.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
2
Marlin/src/feature/baricuda.h
Executable file → Normal file
2
Marlin/src/feature/baricuda.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
72
Marlin/src/feature/bedlevel/abl/abl.cpp
Executable file → Normal file
72
Marlin/src/feature/bedlevel/abl/abl.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -47,11 +47,11 @@ static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
DEBUG_ECHOPGM("Extrapolate [");
|
||||
if (x < 10) DEBUG_CHAR(' ');
|
||||
DEBUG_ECHO((int)x);
|
||||
DEBUG_ECHO(x);
|
||||
DEBUG_CHAR(xdir ? (xdir > 0 ? '+' : '-') : ' ');
|
||||
DEBUG_CHAR(' ');
|
||||
if (y < 10) DEBUG_CHAR(' ');
|
||||
DEBUG_ECHO((int)y);
|
||||
DEBUG_ECHO(y);
|
||||
DEBUG_CHAR(ydir ? (ydir > 0 ? '+' : '-') : ' ');
|
||||
DEBUG_ECHOLNPGM("]");
|
||||
}
|
||||
@@ -74,9 +74,7 @@ static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t
|
||||
|
||||
// Take the average instead of the median
|
||||
z_values[x][y] = (a + b + c) / 3.0;
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onMeshUpdate(x, y, z_values[x][y]);
|
||||
#endif
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y]));
|
||||
|
||||
// Median is robust (ignores outliers).
|
||||
// z_values[x][y] = (a < b) ? ((b < c) ? b : (c < a) ? a : c)
|
||||
@@ -87,9 +85,9 @@ static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t
|
||||
//#define EXTRAPOLATE_FROM_EDGE
|
||||
|
||||
#if ENABLED(EXTRAPOLATE_FROM_EDGE)
|
||||
#if GRID_MAX_POINTS_X < GRID_MAX_POINTS_Y
|
||||
#if (GRID_MAX_POINTS_X) < (GRID_MAX_POINTS_Y)
|
||||
#define HALF_IN_X
|
||||
#elif GRID_MAX_POINTS_Y < GRID_MAX_POINTS_X
|
||||
#elif (GRID_MAX_POINTS_Y) < (GRID_MAX_POINTS_X)
|
||||
#define HALF_IN_Y
|
||||
#endif
|
||||
#endif
|
||||
@@ -100,23 +98,23 @@ static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t
|
||||
*/
|
||||
void extrapolate_unprobed_bed_level() {
|
||||
#ifdef HALF_IN_X
|
||||
constexpr uint8_t ctrx2 = 0, xlen = GRID_MAX_POINTS_X - 1;
|
||||
constexpr uint8_t ctrx2 = 0, xend = GRID_MAX_POINTS_X - 1;
|
||||
#else
|
||||
constexpr uint8_t ctrx1 = (GRID_MAX_POINTS_X - 1) / 2, // left-of-center
|
||||
ctrx2 = (GRID_MAX_POINTS_X) / 2, // right-of-center
|
||||
xlen = ctrx1;
|
||||
constexpr uint8_t ctrx1 = (GRID_MAX_CELLS_X) / 2, // left-of-center
|
||||
ctrx2 = (GRID_MAX_POINTS_X) / 2, // right-of-center
|
||||
xend = ctrx1;
|
||||
#endif
|
||||
|
||||
#ifdef HALF_IN_Y
|
||||
constexpr uint8_t ctry2 = 0, ylen = GRID_MAX_POINTS_Y - 1;
|
||||
constexpr uint8_t ctry2 = 0, yend = GRID_MAX_POINTS_Y - 1;
|
||||
#else
|
||||
constexpr uint8_t ctry1 = (GRID_MAX_POINTS_Y - 1) / 2, // top-of-center
|
||||
ctry2 = (GRID_MAX_POINTS_Y) / 2, // bottom-of-center
|
||||
ylen = ctry1;
|
||||
constexpr uint8_t ctry1 = (GRID_MAX_CELLS_Y) / 2, // top-of-center
|
||||
ctry2 = (GRID_MAX_POINTS_Y) / 2, // bottom-of-center
|
||||
yend = ctry1;
|
||||
#endif
|
||||
|
||||
LOOP_LE_N(xo, xlen)
|
||||
LOOP_LE_N(yo, ylen) {
|
||||
LOOP_LE_N(xo, xend)
|
||||
LOOP_LE_N(yo, yend) {
|
||||
uint8_t x2 = ctrx2 + xo, y2 = ctry2 + yo;
|
||||
#ifndef HALF_IN_X
|
||||
const uint8_t x1 = ctrx1 - xo;
|
||||
@@ -145,8 +143,8 @@ void print_bilinear_leveling_grid() {
|
||||
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
|
||||
#define ABL_GRID_POINTS_VIRT_X (GRID_MAX_POINTS_X - 1) * (BILINEAR_SUBDIVISIONS) + 1
|
||||
#define ABL_GRID_POINTS_VIRT_Y (GRID_MAX_POINTS_Y - 1) * (BILINEAR_SUBDIVISIONS) + 1
|
||||
#define ABL_GRID_POINTS_VIRT_X GRID_MAX_CELLS_X * (BILINEAR_SUBDIVISIONS) + 1
|
||||
#define ABL_GRID_POINTS_VIRT_Y GRID_MAX_CELLS_Y * (BILINEAR_SUBDIVISIONS) + 1
|
||||
#define ABL_TEMP_POINTS_X (GRID_MAX_POINTS_X + 2)
|
||||
#define ABL_TEMP_POINTS_Y (GRID_MAX_POINTS_Y + 2)
|
||||
float z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y];
|
||||
@@ -163,10 +161,18 @@ void print_bilinear_leveling_grid() {
|
||||
#define LINEAR_EXTRAPOLATION(E, I) ((E) * 2 - (I))
|
||||
float bed_level_virt_coord(const uint8_t x, const uint8_t y) {
|
||||
uint8_t ep = 0, ip = 1;
|
||||
if (x > (GRID_MAX_POINTS_X) + 1 || y > (GRID_MAX_POINTS_Y) + 1) {
|
||||
// The requested point requires extrapolating two points beyond the mesh.
|
||||
// These values are only requested for the edges of the mesh, which are always an actual mesh point,
|
||||
// and do not require interpolation. When interpolation is not needed, this "Mesh + 2" point is
|
||||
// cancelled out in bed_level_virt_cmr and does not impact the result. Return 0.0 rather than
|
||||
// making this function more complex by extrapolating two points.
|
||||
return 0.0;
|
||||
}
|
||||
if (!x || x == ABL_TEMP_POINTS_X - 1) {
|
||||
if (x) {
|
||||
ep = GRID_MAX_POINTS_X - 1;
|
||||
ip = GRID_MAX_POINTS_X - 2;
|
||||
ep = (GRID_MAX_POINTS_X) - 1;
|
||||
ip = GRID_MAX_CELLS_X - 1;
|
||||
}
|
||||
if (WITHIN(y, 1, ABL_TEMP_POINTS_Y - 2))
|
||||
return LINEAR_EXTRAPOLATION(
|
||||
@@ -181,8 +187,8 @@ void print_bilinear_leveling_grid() {
|
||||
}
|
||||
if (!y || y == ABL_TEMP_POINTS_Y - 1) {
|
||||
if (y) {
|
||||
ep = GRID_MAX_POINTS_Y - 1;
|
||||
ip = GRID_MAX_POINTS_Y - 2;
|
||||
ep = (GRID_MAX_POINTS_Y) - 1;
|
||||
ip = GRID_MAX_CELLS_Y - 1;
|
||||
}
|
||||
if (WITHIN(x, 1, ABL_TEMP_POINTS_X - 2))
|
||||
return LINEAR_EXTRAPOLATION(
|
||||
@@ -207,7 +213,7 @@ void print_bilinear_leveling_grid() {
|
||||
) * 0.5f;
|
||||
}
|
||||
|
||||
static float bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const float &tx, const float &ty) {
|
||||
static float bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty) {
|
||||
float row[4], column[4];
|
||||
LOOP_L_N(i, 4) {
|
||||
LOOP_L_N(j, 4) {
|
||||
@@ -241,9 +247,7 @@ void print_bilinear_leveling_grid() {
|
||||
// Refresh after other values have been updated
|
||||
void refresh_bed_level() {
|
||||
bilinear_grid_factor = bilinear_grid_spacing.reciprocal();
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
bed_level_virt_interpolate();
|
||||
#endif
|
||||
TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());
|
||||
}
|
||||
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
@@ -332,11 +336,11 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
/*
|
||||
static float last_offset = 0;
|
||||
if (ABS(last_offset - offset) > 0.2) {
|
||||
SERIAL_ECHOLNPAIR("Sudden Shift at x=", rel.x, " / ", bilinear_grid_spacing.x, " -> thisg.x=", thisg.x);
|
||||
SERIAL_ECHOLNPAIR(" y=", rel.y, " / ", bilinear_grid_spacing.y, " -> thisg.y=", thisg.y);
|
||||
SERIAL_ECHOLNPAIR(" ratio.x=", ratio.x, " ratio.y=", ratio.y);
|
||||
SERIAL_ECHOLNPAIR(" z1=", z1, " z2=", z2, " z3=", z3, " z4=", z4);
|
||||
SERIAL_ECHOLNPAIR(" L=", L, " R=", R, " offset=", offset);
|
||||
SERIAL_ECHOLNPGM("Sudden Shift at x=", rel.x, " / ", bilinear_grid_spacing.x, " -> thisg.x=", thisg.x);
|
||||
SERIAL_ECHOLNPGM(" y=", rel.y, " / ", bilinear_grid_spacing.y, " -> thisg.y=", thisg.y);
|
||||
SERIAL_ECHOLNPGM(" ratio.x=", ratio.x, " ratio.y=", ratio.y);
|
||||
SERIAL_ECHOLNPGM(" z1=", z1, " z2=", z2, " z3=", z3, " z4=", z4);
|
||||
SERIAL_ECHOLNPGM(" L=", L, " R=", R, " offset=", offset);
|
||||
}
|
||||
last_offset = offset;
|
||||
//*/
|
||||
@@ -352,7 +356,7 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
* Prepare a bilinear-leveled linear move on Cartesian,
|
||||
* splitting the move where it crosses grid borders.
|
||||
*/
|
||||
void bilinear_line_to_destination(const feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) {
|
||||
void bilinear_line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) {
|
||||
// Get current and destination cells for this line
|
||||
xy_int_t c1 { CELL_INDEX(x, current_position.x), CELL_INDEX(y, current_position.y) },
|
||||
c2 { CELL_INDEX(x, destination.x), CELL_INDEX(y, destination.y) };
|
||||
|
||||
4
Marlin/src/feature/bedlevel/abl/abl.h
Executable file → Normal file
4
Marlin/src/feature/bedlevel/abl/abl.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -37,7 +37,7 @@ void refresh_bed_level();
|
||||
#endif
|
||||
|
||||
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
|
||||
void bilinear_line_to_destination(const feedRate_t &scaled_fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF);
|
||||
void bilinear_line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF);
|
||||
#endif
|
||||
|
||||
#define _GET_MESH_X(I) float(bilinear_start.x + (I) * bilinear_grid_spacing.x)
|
||||
|
||||
70
Marlin/src/feature/bedlevel/bedlevel.cpp
Executable file → Normal file
70
Marlin/src/feature/bedlevel/bedlevel.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#endif
|
||||
|
||||
#if ENABLED(LCD_BED_LEVELING)
|
||||
#include "../../lcd/ultralcd.h"
|
||||
#include "../../lcd/marlinui.h"
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
@@ -47,17 +47,9 @@
|
||||
#endif
|
||||
|
||||
bool leveling_is_valid() {
|
||||
return
|
||||
#if ENABLED(MESH_BED_LEVELING)
|
||||
mbl.has_mesh()
|
||||
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
!!bilinear_grid_spacing.x
|
||||
#elif ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
ubl.mesh_is_valid()
|
||||
#else // 3POINT, LINEAR
|
||||
true
|
||||
#endif
|
||||
;
|
||||
return TERN1(MESH_BED_LEVELING, mbl.has_mesh())
|
||||
&& TERN1(AUTO_BED_LEVELING_BILINEAR, !!bilinear_grid_spacing.x)
|
||||
&& TERN1(AUTO_BED_LEVELING_UBL, ubl.mesh_is_valid());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,11 +61,7 @@ bool leveling_is_valid() {
|
||||
*/
|
||||
void set_bed_leveling_enabled(const bool enable/*=true*/) {
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
const bool can_change = (!enable || leveling_is_valid());
|
||||
#else
|
||||
constexpr bool can_change = true;
|
||||
#endif
|
||||
const bool can_change = TERN1(AUTO_BED_LEVELING_BILINEAR, !enable || leveling_is_valid());
|
||||
|
||||
if (can_change && enable != planner.leveling_active) {
|
||||
|
||||
@@ -110,7 +98,7 @@ TemporaryBedLevelingState::TemporaryBedLevelingState(const bool enable) : saved(
|
||||
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
|
||||
void set_z_fade_height(const float zfh, const bool do_report/*=true*/) {
|
||||
void set_z_fade_height(const_float_t zfh, const bool do_report/*=true*/) {
|
||||
|
||||
if (planner.z_fade_height == zfh) return;
|
||||
|
||||
@@ -145,9 +133,7 @@ void reset_bed_level() {
|
||||
bilinear_grid_spacing.reset();
|
||||
GRID_LOOP(x, y) {
|
||||
z_values[x][y] = NAN;
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onMeshUpdate(x, y, 0);
|
||||
#endif
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, 0));
|
||||
}
|
||||
#elif ABL_PLANAR
|
||||
planner.bed_level_matrix.set_to_identity();
|
||||
@@ -174,7 +160,7 @@ void reset_bed_level() {
|
||||
#ifndef SCAD_MESH_OUTPUT
|
||||
LOOP_L_N(x, sx) {
|
||||
serial_spaces(precision + (x < 10 ? 3 : 2));
|
||||
SERIAL_ECHO(int(x));
|
||||
SERIAL_ECHO(x);
|
||||
}
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
@@ -186,7 +172,7 @@ void reset_bed_level() {
|
||||
SERIAL_ECHOPGM(" ["); // open sub-array
|
||||
#else
|
||||
if (y < 10) SERIAL_CHAR(' ');
|
||||
SERIAL_ECHO(int(y));
|
||||
SERIAL_ECHO(y);
|
||||
#endif
|
||||
LOOP_L_N(x, sx) {
|
||||
SERIAL_CHAR(' ');
|
||||
@@ -210,7 +196,7 @@ void reset_bed_level() {
|
||||
#endif
|
||||
}
|
||||
#ifdef SCAD_MESH_OUTPUT
|
||||
SERIAL_CHAR(' ', ']'); // close sub-array
|
||||
SERIAL_ECHOPGM(" ]"); // close sub-array
|
||||
if (y < sy - 1) SERIAL_CHAR(',');
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
@@ -227,29 +213,27 @@ void reset_bed_level() {
|
||||
|
||||
void _manual_goto_xy(const xy_pos_t &pos) {
|
||||
|
||||
// Get the resting Z position for after the XY move
|
||||
#ifdef MANUAL_PROBE_START_Z
|
||||
constexpr float startz = _MAX(0, MANUAL_PROBE_START_Z);
|
||||
#if MANUAL_PROBE_HEIGHT > 0
|
||||
do_blocking_move_to_xy_z(pos, MANUAL_PROBE_HEIGHT);
|
||||
do_blocking_move_to_z(startz);
|
||||
#else
|
||||
do_blocking_move_to_xy_z(pos, startz);
|
||||
#endif
|
||||
#elif MANUAL_PROBE_HEIGHT > 0
|
||||
const float prev_z = current_position.z;
|
||||
do_blocking_move_to_xy_z(pos, MANUAL_PROBE_HEIGHT);
|
||||
do_blocking_move_to_z(prev_z);
|
||||
constexpr float finalz = _MAX(0, MANUAL_PROBE_START_Z); // If a MANUAL_PROBE_START_Z value is set, always respect it
|
||||
#else
|
||||
do_blocking_move_to_xy(pos);
|
||||
#warning "It's recommended to set some MANUAL_PROBE_START_Z value for manual leveling."
|
||||
#endif
|
||||
#if Z_CLEARANCE_BETWEEN_MANUAL_PROBES > 0 // A probe/obstacle clearance exists so there is a raise:
|
||||
#ifndef MANUAL_PROBE_START_Z
|
||||
const float finalz = current_position.z; // - Use the current Z for starting-Z if no MANUAL_PROBE_START_Z was provided
|
||||
#endif
|
||||
do_blocking_move_to_xy_z(pos, Z_CLEARANCE_BETWEEN_MANUAL_PROBES); // - Raise Z, then move to the new XY
|
||||
do_blocking_move_to_z(finalz); // - Lower down to the starting Z height, ready for adjustment!
|
||||
#elif defined(MANUAL_PROBE_START_Z) // A starting-Z was provided, but there's no raise:
|
||||
do_blocking_move_to_xy_z(pos, finalz); // - Move in XY then down to the starting Z height, ready for adjustment!
|
||||
#else // Zero raise and no starting Z height either:
|
||||
do_blocking_move_to_xy(pos); // - Move over with no raise, ready for adjustment!
|
||||
#endif
|
||||
|
||||
current_position = pos;
|
||||
|
||||
#if ENABLED(LCD_BED_LEVELING)
|
||||
ui.wait_for_move = false;
|
||||
#endif
|
||||
TERN_(LCD_BED_LEVELING, ui.wait_for_move = false);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // MESH_BED_LEVELING || PROBE_MANUALLY
|
||||
|
||||
#endif // HAS_LEVELING
|
||||
|
||||
8
Marlin/src/feature/bedlevel/bedlevel.h
Executable file → Normal file
8
Marlin/src/feature/bedlevel/bedlevel.h
Executable file → Normal file
@@ -16,13 +16,17 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if EITHER(RESTORE_LEVELING_AFTER_G28, ENABLE_LEVELING_AFTER_G28)
|
||||
#define CAN_SET_LEVELING_AFTER_G28 1
|
||||
#endif
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
extern bool g29_in_progress;
|
||||
#else
|
||||
@@ -34,7 +38,7 @@ void set_bed_leveling_enabled(const bool enable=true);
|
||||
void reset_bed_level();
|
||||
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
void set_z_fade_height(const float zfh, const bool do_report=true);
|
||||
void set_z_fade_height(const_float_t zfh, const bool do_report=true);
|
||||
#endif
|
||||
|
||||
#if EITHER(MESH_BED_LEVELING, PROBE_MANUALLY)
|
||||
|
||||
110
Marlin/src/feature/bedlevel/hilbert_curve.cpp
Normal file
110
Marlin/src/feature/bedlevel/hilbert_curve.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*********************
|
||||
* hilbert_curve.cpp *
|
||||
*********************/
|
||||
|
||||
/****************************************************************************
|
||||
* Written By Marcio Teixeira 2021 - SynDaver Labs, Inc. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* To view a copy of the GNU General Public License, go to the following *
|
||||
* location: <https://www.gnu.org/licenses/>. *
|
||||
****************************************************************************/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(UBL_HILBERT_CURVE)
|
||||
|
||||
#include "bedlevel.h"
|
||||
#include "hilbert_curve.h"
|
||||
|
||||
constexpr int8_t to_fix(int8_t v) { return v * 2; }
|
||||
constexpr int8_t to_int(int8_t v) { return v / 2; }
|
||||
constexpr uint8_t log2(uint8_t n) { return (n > 1) ? 1 + log2(n >> 1) : 0; }
|
||||
constexpr uint8_t order(uint8_t n) { return uint8_t(log2(n - 1)) + 1; }
|
||||
constexpr uint8_t ord = order(_MAX(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y));
|
||||
constexpr uint8_t dim = _BV(ord);
|
||||
|
||||
static inline bool eval_candidate(int8_t x, int8_t y, hilbert_curve::callback_ptr func, void *data) {
|
||||
// The print bed likely has fewer points than the full Hilbert
|
||||
// curve, so cull unnecessary points
|
||||
return x < (GRID_MAX_POINTS_X) && y < (GRID_MAX_POINTS_Y) ? func(x, y, data) : false;
|
||||
}
|
||||
|
||||
bool hilbert_curve::hilbert(int8_t x, int8_t y, int8_t xi, int8_t xj, int8_t yi, int8_t yj, uint8_t n, hilbert_curve::callback_ptr func, void *data) {
|
||||
/**
|
||||
* Hilbert space-filling curve implementation
|
||||
*
|
||||
* x and y : coordinates of the bottom left corner
|
||||
* xi and xj : i and j components of the unit x vector of the frame
|
||||
* yi and yj : i and j components of the unit y vector of the frame
|
||||
*
|
||||
* From: http://www.fundza.com/algorithmic/space_filling/hilbert/basics/index.html
|
||||
*/
|
||||
if (n)
|
||||
return hilbert(x, y, yi/2, yj/2, xi/2, xj/2, n-1, func, data) ||
|
||||
hilbert(x+xi/2, y+xj/2, xi/2, xj/2, yi/2, yj/2, n-1, func, data) ||
|
||||
hilbert(x+xi/2+yi/2, y+xj/2+yj/2, xi/2, xj/2, yi/2, yj/2, n-1, func, data) ||
|
||||
hilbert(x+xi/2+yi, y+xj/2+yj, -yi/2, -yj/2, -xi/2, -xj/2, n-1, func, data);
|
||||
else
|
||||
return eval_candidate(to_int(x+(xi+yi)/2), to_int(y+(xj+yj)/2), func, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls func(x, y, data) for all points in the Hilbert curve.
|
||||
* If that function returns true, the search is terminated.
|
||||
*/
|
||||
bool hilbert_curve::search(hilbert_curve::callback_ptr func, void *data) {
|
||||
return hilbert(to_fix(0), to_fix(0),to_fix(dim), to_fix(0), to_fix(0), to_fix(dim), ord, func, data);
|
||||
}
|
||||
|
||||
/* Helper function for starting the search at a particular point */
|
||||
|
||||
typedef struct {
|
||||
uint8_t x, y;
|
||||
bool found_1st;
|
||||
hilbert_curve::callback_ptr func;
|
||||
void *data;
|
||||
} search_from_t;
|
||||
|
||||
static bool search_from_helper(uint8_t x, uint8_t y, void *data) {
|
||||
search_from_t *d = (search_from_t *) data;
|
||||
if (d->x == x && d->y == y)
|
||||
d->found_1st = true;
|
||||
return d->found_1st ? d->func(x, y, d->data) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as search, except start at a specific grid intersection point.
|
||||
*/
|
||||
bool hilbert_curve::search_from(uint8_t x, uint8_t y, hilbert_curve::callback_ptr func, void *data) {
|
||||
search_from_t d;
|
||||
d.x = x;
|
||||
d.y = y;
|
||||
d.found_1st = false;
|
||||
d.func = func;
|
||||
d.data = data;
|
||||
// Call twice to allow search to wrap back to the beginning and picked up points prior to the start.
|
||||
return search(search_from_helper, &d) || search(search_from_helper, &d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like search_from, but takes a bed position and starts from the nearest
|
||||
* point on the Hilbert curve.
|
||||
*/
|
||||
bool hilbert_curve::search_from_closest(const xy_pos_t &pos, hilbert_curve::callback_ptr func, void *data) {
|
||||
// Find closest grid intersection
|
||||
const uint8_t grid_x = LROUND(constrain(float(pos.x - (MESH_MIN_X)) / (MESH_X_DIST), 0, (GRID_MAX_POINTS_X) - 1));
|
||||
const uint8_t grid_y = LROUND(constrain(float(pos.y - (MESH_MIN_Y)) / (MESH_Y_DIST), 0, (GRID_MAX_POINTS_Y) - 1));
|
||||
return search_from(grid_x, grid_y, func, data);
|
||||
}
|
||||
|
||||
#endif // UBL_HILBERT_CURVE
|
||||
32
Marlin/src/feature/bedlevel/hilbert_curve.h
Normal file
32
Marlin/src/feature/bedlevel/hilbert_curve.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*******************
|
||||
* hilbert_curve.h *
|
||||
*******************/
|
||||
|
||||
/****************************************************************************
|
||||
* Written By Marcio Teixeira 2021 - SynDaver Labs, Inc. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* To view a copy of the GNU General Public License, go to the following *
|
||||
* location: <https://www.gnu.org/licenses/>. *
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
class hilbert_curve {
|
||||
public:
|
||||
typedef bool (*callback_ptr)(uint8_t x, uint8_t y, void *data);
|
||||
static bool search(callback_ptr func, void *data);
|
||||
static bool search_from(uint8_t x, uint8_t y, callback_ptr func, void *data);
|
||||
static bool search_from_closest(const xy_pos_t &pos, callback_ptr func, void *data);
|
||||
private:
|
||||
static bool hilbert(int8_t x, int8_t y, int8_t xi, int8_t xj, int8_t yi, int8_t yj, uint8_t n, callback_ptr func, void *data);
|
||||
};
|
||||
16
Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp
Executable file → Normal file
16
Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -61,18 +61,18 @@
|
||||
* Prepare a mesh-leveled linear move in a Cartesian setup,
|
||||
* splitting the move where it crosses mesh borders.
|
||||
*/
|
||||
void mesh_bed_leveling::line_to_destination(const feedRate_t &scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) {
|
||||
void mesh_bed_leveling::line_to_destination(const_feedRate_t scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) {
|
||||
// Get current and destination cells for this line
|
||||
xy_int8_t scel = cell_indexes(current_position), ecel = cell_indexes(destination);
|
||||
NOMORE(scel.x, GRID_MAX_POINTS_X - 2);
|
||||
NOMORE(scel.y, GRID_MAX_POINTS_Y - 2);
|
||||
NOMORE(ecel.x, GRID_MAX_POINTS_X - 2);
|
||||
NOMORE(ecel.y, GRID_MAX_POINTS_Y - 2);
|
||||
NOMORE(scel.x, GRID_MAX_CELLS_X - 1);
|
||||
NOMORE(scel.y, GRID_MAX_CELLS_Y - 1);
|
||||
NOMORE(ecel.x, GRID_MAX_CELLS_X - 1);
|
||||
NOMORE(ecel.y, GRID_MAX_CELLS_Y - 1);
|
||||
|
||||
// Start and end in the same cell? No split needed.
|
||||
if (scel == ecel) {
|
||||
line_to_destination(scaled_fr_mm_s);
|
||||
current_position = destination;
|
||||
line_to_current_position(scaled_fr_mm_s);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@
|
||||
else {
|
||||
// Must already have been split on these border(s)
|
||||
// This should be a rare case.
|
||||
line_to_destination(scaled_fr_mm_s);
|
||||
current_position = destination;
|
||||
line_to_current_position(scaled_fr_mm_s);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
45
Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h
Executable file → Normal file
45
Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -32,8 +32,8 @@ enum MeshLevelingState : char {
|
||||
MeshReset // G29 S5
|
||||
};
|
||||
|
||||
#define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / float(GRID_MAX_POINTS_X - 1))
|
||||
#define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / float(GRID_MAX_POINTS_Y - 1))
|
||||
#define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / (GRID_MAX_CELLS_X))
|
||||
#define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / (GRID_MAX_CELLS_Y))
|
||||
#define _GET_MESH_X(I) mbl.index_to_xpos[I]
|
||||
#define _GET_MESH_Y(J) mbl.index_to_ypos[J]
|
||||
#define Z_VALUES_ARR mbl.z_values
|
||||
@@ -56,56 +56,54 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void set_z(const int8_t px, const int8_t py, const float &z) { z_values[px][py] = z; }
|
||||
static void set_z(const int8_t px, const int8_t py, const_float_t z) { z_values[px][py] = z; }
|
||||
|
||||
static inline void zigzag(const int8_t index, int8_t &px, int8_t &py) {
|
||||
px = index % (GRID_MAX_POINTS_X);
|
||||
py = index / (GRID_MAX_POINTS_X);
|
||||
if (py & 1) px = (GRID_MAX_POINTS_X - 1) - px; // Zig zag
|
||||
if (py & 1) px = (GRID_MAX_POINTS_X) - 1 - px; // Zig zag
|
||||
}
|
||||
|
||||
static void set_zigzag_z(const int8_t index, const float &z) {
|
||||
static void set_zigzag_z(const int8_t index, const_float_t z) {
|
||||
int8_t px, py;
|
||||
zigzag(index, px, py);
|
||||
set_z(px, py, z);
|
||||
}
|
||||
|
||||
static int8_t cell_index_x(const float &x) {
|
||||
static int8_t cell_index_x(const_float_t x) {
|
||||
int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST);
|
||||
return constrain(cx, 0, (GRID_MAX_POINTS_X) - 2);
|
||||
return constrain(cx, 0, GRID_MAX_CELLS_X - 1);
|
||||
}
|
||||
static int8_t cell_index_y(const float &y) {
|
||||
static int8_t cell_index_y(const_float_t y) {
|
||||
int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST);
|
||||
return constrain(cy, 0, (GRID_MAX_POINTS_Y) - 2);
|
||||
return constrain(cy, 0, GRID_MAX_CELLS_Y - 1);
|
||||
}
|
||||
static inline xy_int8_t cell_indexes(const float &x, const float &y) {
|
||||
static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) {
|
||||
return { cell_index_x(x), cell_index_y(y) };
|
||||
}
|
||||
static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
|
||||
|
||||
static int8_t probe_index_x(const float &x) {
|
||||
static int8_t probe_index_x(const_float_t x) {
|
||||
int8_t px = (x - (MESH_MIN_X) + 0.5f * (MESH_X_DIST)) * RECIPROCAL(MESH_X_DIST);
|
||||
return WITHIN(px, 0, GRID_MAX_POINTS_X - 1) ? px : -1;
|
||||
return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1;
|
||||
}
|
||||
static int8_t probe_index_y(const float &y) {
|
||||
static int8_t probe_index_y(const_float_t y) {
|
||||
int8_t py = (y - (MESH_MIN_Y) + 0.5f * (MESH_Y_DIST)) * RECIPROCAL(MESH_Y_DIST);
|
||||
return WITHIN(py, 0, GRID_MAX_POINTS_Y - 1) ? py : -1;
|
||||
return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1;
|
||||
}
|
||||
static inline xy_int8_t probe_indexes(const float &x, const float &y) {
|
||||
static inline xy_int8_t probe_indexes(const_float_t x, const_float_t y) {
|
||||
return { probe_index_x(x), probe_index_y(y) };
|
||||
}
|
||||
static inline xy_int8_t probe_indexes(const xy_pos_t &xy) { return probe_indexes(xy.x, xy.y); }
|
||||
|
||||
static float calc_z0(const float &a0, const float &a1, const float &z1, const float &a2, const float &z2) {
|
||||
static float calc_z0(const_float_t a0, const_float_t a1, const_float_t z1, const_float_t a2, const_float_t z2) {
|
||||
const float delta_z = (z2 - z1) / (a2 - a1),
|
||||
delta_a = a0 - a1;
|
||||
return z1 + delta_a * delta_z;
|
||||
}
|
||||
|
||||
static float get_z(const xy_pos_t &pos
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
, const float &factor=1.0f
|
||||
#endif
|
||||
OPTARG(ENABLE_LEVELING_FADE_HEIGHT, const_float_t factor=1.0f)
|
||||
) {
|
||||
#if DISABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
constexpr float factor = 1.0f;
|
||||
@@ -114,13 +112,14 @@ public:
|
||||
const float x1 = index_to_xpos[ind.x], x2 = index_to_xpos[ind.x+1],
|
||||
y1 = index_to_xpos[ind.y], y2 = index_to_xpos[ind.y+1],
|
||||
z1 = calc_z0(pos.x, x1, z_values[ind.x][ind.y ], x2, z_values[ind.x+1][ind.y ]),
|
||||
z2 = calc_z0(pos.x, x1, z_values[ind.x][ind.y+1], x2, z_values[ind.x+1][ind.y+1]);
|
||||
z2 = calc_z0(pos.x, x1, z_values[ind.x][ind.y+1], x2, z_values[ind.x+1][ind.y+1]),
|
||||
zf = calc_z0(pos.y, y1, z1, y2, z2);
|
||||
|
||||
return z_offset + calc_z0(pos.y, y1, z1, y2, z2) * factor;
|
||||
return z_offset + zf * factor;
|
||||
}
|
||||
|
||||
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
|
||||
static void line_to_destination(const feedRate_t &scaled_fr_mm_s, uint8_t x_splits=0xFF, uint8_t y_splits=0xFF);
|
||||
static void line_to_destination(const_feedRate_t scaled_fr_mm_s, uint8_t x_splits=0xFF, uint8_t y_splits=0xFF);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
427
Marlin/src/feature/bedlevel/ubl/ubl.cpp
Executable file → Normal file
427
Marlin/src/feature/bedlevel/ubl/ubl.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -24,220 +24,279 @@
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
|
||||
#include "../bedlevel.h"
|
||||
#include "../bedlevel.h"
|
||||
|
||||
unified_bed_leveling ubl;
|
||||
unified_bed_leveling ubl;
|
||||
|
||||
#include "../../../MarlinCore.h"
|
||||
#include "../../../gcode/gcode.h"
|
||||
#include "../../../MarlinCore.h"
|
||||
#include "../../../gcode/gcode.h"
|
||||
|
||||
#include "../../../module/configuration_store.h"
|
||||
#include "../../../module/planner.h"
|
||||
#include "../../../module/motion.h"
|
||||
#include "../../../module/probe.h"
|
||||
#include "../../../module/settings.h"
|
||||
#include "../../../module/planner.h"
|
||||
#include "../../../module/motion.h"
|
||||
#include "../../../module/probe.h"
|
||||
#include "../../../module/temperature.h"
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../../../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
#include "math.h"
|
||||
|
||||
void unified_bed_leveling::echo_name() { SERIAL_ECHOPGM("Unified Bed Leveling"); }
|
||||
|
||||
void unified_bed_leveling::report_current_mesh() {
|
||||
if (!leveling_is_valid()) return;
|
||||
SERIAL_ECHO_MSG(" G29 I999");
|
||||
GRID_LOOP(x, y)
|
||||
if (!isnan(z_values[x][y])) {
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOPGM(" M421 I", x, " J", y);
|
||||
SERIAL_ECHOLNPAIR_F_P(SP_Z_STR, z_values[x][y], 4);
|
||||
serial_delay(75); // Prevent Printrun from exploding
|
||||
}
|
||||
}
|
||||
|
||||
void unified_bed_leveling::report_state() {
|
||||
echo_name();
|
||||
SERIAL_ECHO_TERNARY(planner.leveling_active, " System v" UBL_VERSION " ", "", "in", "active\n");
|
||||
serial_delay(50);
|
||||
}
|
||||
|
||||
int8_t unified_bed_leveling::storage_slot;
|
||||
|
||||
float unified_bed_leveling::z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
|
||||
|
||||
#define _GRIDPOS(A,N) (MESH_MIN_##A + N * (MESH_##A##_DIST))
|
||||
|
||||
const float
|
||||
unified_bed_leveling::_mesh_index_to_xpos[GRID_MAX_POINTS_X] PROGMEM = ARRAY_N(GRID_MAX_POINTS_X,
|
||||
_GRIDPOS(X, 0), _GRIDPOS(X, 1), _GRIDPOS(X, 2), _GRIDPOS(X, 3),
|
||||
_GRIDPOS(X, 4), _GRIDPOS(X, 5), _GRIDPOS(X, 6), _GRIDPOS(X, 7),
|
||||
_GRIDPOS(X, 8), _GRIDPOS(X, 9), _GRIDPOS(X, 10), _GRIDPOS(X, 11),
|
||||
_GRIDPOS(X, 12), _GRIDPOS(X, 13), _GRIDPOS(X, 14), _GRIDPOS(X, 15)
|
||||
),
|
||||
unified_bed_leveling::_mesh_index_to_ypos[GRID_MAX_POINTS_Y] PROGMEM = ARRAY_N(GRID_MAX_POINTS_Y,
|
||||
_GRIDPOS(Y, 0), _GRIDPOS(Y, 1), _GRIDPOS(Y, 2), _GRIDPOS(Y, 3),
|
||||
_GRIDPOS(Y, 4), _GRIDPOS(Y, 5), _GRIDPOS(Y, 6), _GRIDPOS(Y, 7),
|
||||
_GRIDPOS(Y, 8), _GRIDPOS(Y, 9), _GRIDPOS(Y, 10), _GRIDPOS(Y, 11),
|
||||
_GRIDPOS(Y, 12), _GRIDPOS(Y, 13), _GRIDPOS(Y, 14), _GRIDPOS(Y, 15)
|
||||
);
|
||||
|
||||
volatile int16_t unified_bed_leveling::encoder_diff;
|
||||
|
||||
unified_bed_leveling::unified_bed_leveling() { reset(); }
|
||||
|
||||
void unified_bed_leveling::reset() {
|
||||
const bool was_enabled = planner.leveling_active;
|
||||
set_bed_leveling_enabled(false);
|
||||
storage_slot = -1;
|
||||
ZERO(z_values);
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../../../lcd/extui/ui_api.h"
|
||||
GRID_LOOP(x, y) ExtUI::onMeshUpdate(x, y, 0);
|
||||
#endif
|
||||
if (was_enabled) report_current_position();
|
||||
}
|
||||
|
||||
#include "math.h"
|
||||
void unified_bed_leveling::invalidate() {
|
||||
set_bed_leveling_enabled(false);
|
||||
set_all_mesh_points_to_value(NAN);
|
||||
}
|
||||
|
||||
void unified_bed_leveling::echo_name() {
|
||||
SERIAL_ECHOPGM("Unified Bed Leveling");
|
||||
void unified_bed_leveling::set_all_mesh_points_to_value(const_float_t value) {
|
||||
GRID_LOOP(x, y) {
|
||||
z_values[x][y] = value;
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, value));
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(OPTIMIZED_MESH_STORAGE)
|
||||
|
||||
constexpr float mesh_store_scaling = 1000;
|
||||
constexpr int16_t Z_STEPS_NAN = INT16_MAX;
|
||||
|
||||
void unified_bed_leveling::set_store_from_mesh(const bed_mesh_t &in_values, mesh_store_t &stored_values) {
|
||||
auto z_to_store = [](const_float_t z) {
|
||||
if (isnan(z)) return Z_STEPS_NAN;
|
||||
const int32_t z_scaled = TRUNC(z * mesh_store_scaling);
|
||||
if (z_scaled == Z_STEPS_NAN || !WITHIN(z_scaled, INT16_MIN, INT16_MAX))
|
||||
return Z_STEPS_NAN; // If Z is out of range, return our custom 'NaN'
|
||||
return int16_t(z_scaled);
|
||||
};
|
||||
GRID_LOOP(x, y) stored_values[x][y] = z_to_store(in_values[x][y]);
|
||||
}
|
||||
|
||||
void unified_bed_leveling::report_current_mesh() {
|
||||
if (!leveling_is_valid()) return;
|
||||
SERIAL_ECHO_MSG(" G29 I99");
|
||||
LOOP_L_N(x, GRID_MAX_POINTS_X)
|
||||
for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++)
|
||||
if (!isnan(z_values[x][y])) {
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOPAIR(" M421 I", int(x), " J", int(y));
|
||||
SERIAL_ECHOLNPAIR_F_P(SP_Z_STR, z_values[x][y], 4);
|
||||
serial_delay(75); // Prevent Printrun from exploding
|
||||
}
|
||||
void unified_bed_leveling::set_mesh_from_store(const mesh_store_t &stored_values, bed_mesh_t &out_values) {
|
||||
auto store_to_z = [](const int16_t z_scaled) {
|
||||
return z_scaled == Z_STEPS_NAN ? NAN : z_scaled / mesh_store_scaling;
|
||||
};
|
||||
GRID_LOOP(x, y) out_values[x][y] = store_to_z(stored_values[x][y]);
|
||||
}
|
||||
|
||||
void unified_bed_leveling::report_state() {
|
||||
echo_name();
|
||||
SERIAL_ECHO_TERNARY(planner.leveling_active, " System v" UBL_VERSION " ", "", "in", "active\n");
|
||||
serial_delay(50);
|
||||
}
|
||||
#endif // OPTIMIZED_MESH_STORAGE
|
||||
|
||||
int8_t unified_bed_leveling::storage_slot;
|
||||
static void serial_echo_xy(const uint8_t sp, const int16_t x, const int16_t y) {
|
||||
SERIAL_ECHO_SP(sp);
|
||||
SERIAL_CHAR('(');
|
||||
if (x < 100) { SERIAL_CHAR(' '); if (x < 10) SERIAL_CHAR(' '); }
|
||||
SERIAL_ECHO(x);
|
||||
SERIAL_CHAR(',');
|
||||
if (y < 100) { SERIAL_CHAR(' '); if (y < 10) SERIAL_CHAR(' '); }
|
||||
SERIAL_ECHO(y);
|
||||
SERIAL_CHAR(')');
|
||||
serial_delay(5);
|
||||
}
|
||||
|
||||
float unified_bed_leveling::z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
|
||||
|
||||
#define _GRIDPOS(A,N) (MESH_MIN_##A + N * (MESH_##A##_DIST))
|
||||
|
||||
const float
|
||||
unified_bed_leveling::_mesh_index_to_xpos[GRID_MAX_POINTS_X] PROGMEM = ARRAY_N(GRID_MAX_POINTS_X,
|
||||
_GRIDPOS(X, 0), _GRIDPOS(X, 1), _GRIDPOS(X, 2), _GRIDPOS(X, 3),
|
||||
_GRIDPOS(X, 4), _GRIDPOS(X, 5), _GRIDPOS(X, 6), _GRIDPOS(X, 7),
|
||||
_GRIDPOS(X, 8), _GRIDPOS(X, 9), _GRIDPOS(X, 10), _GRIDPOS(X, 11),
|
||||
_GRIDPOS(X, 12), _GRIDPOS(X, 13), _GRIDPOS(X, 14), _GRIDPOS(X, 15)
|
||||
),
|
||||
unified_bed_leveling::_mesh_index_to_ypos[GRID_MAX_POINTS_Y] PROGMEM = ARRAY_N(GRID_MAX_POINTS_Y,
|
||||
_GRIDPOS(Y, 0), _GRIDPOS(Y, 1), _GRIDPOS(Y, 2), _GRIDPOS(Y, 3),
|
||||
_GRIDPOS(Y, 4), _GRIDPOS(Y, 5), _GRIDPOS(Y, 6), _GRIDPOS(Y, 7),
|
||||
_GRIDPOS(Y, 8), _GRIDPOS(Y, 9), _GRIDPOS(Y, 10), _GRIDPOS(Y, 11),
|
||||
_GRIDPOS(Y, 12), _GRIDPOS(Y, 13), _GRIDPOS(Y, 14), _GRIDPOS(Y, 15)
|
||||
);
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
bool unified_bed_leveling::lcd_map_control = false;
|
||||
#endif
|
||||
|
||||
volatile int unified_bed_leveling::encoder_diff;
|
||||
|
||||
unified_bed_leveling::unified_bed_leveling() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void unified_bed_leveling::reset() {
|
||||
const bool was_enabled = planner.leveling_active;
|
||||
set_bed_leveling_enabled(false);
|
||||
storage_slot = -1;
|
||||
ZERO(z_values);
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
GRID_LOOP(x, y) ExtUI::onMeshUpdate(x, y, 0);
|
||||
#endif
|
||||
if (was_enabled) report_current_position();
|
||||
}
|
||||
|
||||
void unified_bed_leveling::invalidate() {
|
||||
set_bed_leveling_enabled(false);
|
||||
set_all_mesh_points_to_value(NAN);
|
||||
}
|
||||
|
||||
void unified_bed_leveling::set_all_mesh_points_to_value(const float value) {
|
||||
GRID_LOOP(x, y) {
|
||||
z_values[x][y] = value;
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onMeshUpdate(x, y, value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_echo_xy(const uint8_t sp, const int16_t x, const int16_t y) {
|
||||
static void serial_echo_column_labels(const uint8_t sp) {
|
||||
SERIAL_ECHO_SP(7);
|
||||
LOOP_L_N(i, GRID_MAX_POINTS_X) {
|
||||
if (i < 10) SERIAL_CHAR(' ');
|
||||
SERIAL_ECHO(i);
|
||||
SERIAL_ECHO_SP(sp);
|
||||
SERIAL_CHAR('(');
|
||||
if (x < 100) { SERIAL_CHAR(' '); if (x < 10) SERIAL_CHAR(' '); }
|
||||
SERIAL_ECHO(x);
|
||||
SERIAL_CHAR(',');
|
||||
if (y < 100) { SERIAL_CHAR(' '); if (y < 10) SERIAL_CHAR(' '); }
|
||||
SERIAL_ECHO(y);
|
||||
SERIAL_CHAR(')');
|
||||
serial_delay(5);
|
||||
}
|
||||
serial_delay(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce one of these mesh maps:
|
||||
* 0: Human-readable
|
||||
* 1: CSV format for spreadsheet import
|
||||
* 2: TODO: Display on Graphical LCD
|
||||
* 4: Compact Human-Readable
|
||||
*/
|
||||
void unified_bed_leveling::display_map(const uint8_t map_type) {
|
||||
const bool was = gcode.set_autoreport_paused(true);
|
||||
|
||||
constexpr uint8_t eachsp = 1 + 6 + 1, // [-3.567]
|
||||
twixt = eachsp * (GRID_MAX_POINTS_X) - 9 * 2; // Leading 4sp, Coordinates 9sp each
|
||||
|
||||
const bool human = !(map_type & 0x3), csv = map_type == 1, lcd = map_type == 2, comp = map_type & 0x4;
|
||||
|
||||
SERIAL_ECHOPGM("\nBed Topography Report");
|
||||
if (human) {
|
||||
SERIAL_ECHOLNPGM(":\n");
|
||||
serial_echo_xy(4, MESH_MIN_X, MESH_MAX_Y);
|
||||
serial_echo_xy(twixt, MESH_MAX_X, MESH_MAX_Y);
|
||||
SERIAL_EOL();
|
||||
serial_echo_column_labels(eachsp - 2);
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHOPGM(" for ");
|
||||
SERIAL_ECHOPGM_P(csv ? PSTR("CSV:\n") : PSTR("LCD:\n"));
|
||||
}
|
||||
|
||||
static void serial_echo_column_labels(const uint8_t sp) {
|
||||
SERIAL_ECHO_SP(7);
|
||||
for (int8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
|
||||
if (i < 10) SERIAL_CHAR(' ');
|
||||
SERIAL_ECHO(i);
|
||||
SERIAL_ECHO_SP(sp);
|
||||
// Add XY probe offset from extruder because probe.probe_at_point() subtracts them when
|
||||
// moving to the XY position to be measured. This ensures better agreement between
|
||||
// the current Z position after G28 and the mesh values.
|
||||
const xy_int8_t curr = closest_indexes(xy_pos_t(current_position) + probe.offset_xy);
|
||||
|
||||
if (!lcd) SERIAL_EOL();
|
||||
for (int8_t j = (GRID_MAX_POINTS_Y) - 1; j >= 0; j--) {
|
||||
|
||||
// Row Label (J index)
|
||||
if (human) {
|
||||
if (j < 10) SERIAL_CHAR(' ');
|
||||
SERIAL_ECHO(j);
|
||||
SERIAL_ECHOPGM(" |");
|
||||
}
|
||||
serial_delay(10);
|
||||
|
||||
// Row Values (I indexes)
|
||||
LOOP_L_N(i, GRID_MAX_POINTS_X) {
|
||||
|
||||
// Opening Brace or Space
|
||||
const bool is_current = i == curr.x && j == curr.y;
|
||||
if (human) SERIAL_CHAR(is_current ? '[' : ' ');
|
||||
|
||||
// Z Value at current I, J
|
||||
const float f = z_values[i][j];
|
||||
if (lcd) {
|
||||
// TODO: Display on Graphical LCD
|
||||
}
|
||||
else if (isnan(f))
|
||||
SERIAL_ECHOPGM_P(human ? PSTR(" . ") : PSTR("NAN"));
|
||||
else if (human || csv) {
|
||||
if (human && f >= 0.0) SERIAL_CHAR(f > 0 ? '+' : ' '); // Display sign also for positive numbers (' ' for 0)
|
||||
SERIAL_ECHO_F(f, 3); // Positive: 5 digits, Negative: 6 digits
|
||||
}
|
||||
if (csv && i < (GRID_MAX_POINTS_X) - 1) SERIAL_CHAR('\t');
|
||||
|
||||
// Closing Brace or Space
|
||||
if (human) SERIAL_CHAR(is_current ? ']' : ' ');
|
||||
|
||||
SERIAL_FLUSHTX();
|
||||
idle_no_sleep();
|
||||
}
|
||||
if (!lcd) SERIAL_EOL();
|
||||
|
||||
// A blank line between rows (unless compact)
|
||||
if (j && human && !comp) SERIAL_ECHOLNPGM(" |");
|
||||
}
|
||||
|
||||
if (human) {
|
||||
serial_echo_column_labels(eachsp - 2);
|
||||
SERIAL_EOL();
|
||||
serial_echo_xy(4, MESH_MIN_X, MESH_MIN_Y);
|
||||
serial_echo_xy(twixt, MESH_MAX_X, MESH_MIN_Y);
|
||||
SERIAL_EOL();
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
gcode.set_autoreport_paused(was);
|
||||
}
|
||||
|
||||
bool unified_bed_leveling::sanity_check() {
|
||||
uint8_t error_flag = 0;
|
||||
|
||||
if (settings.calc_num_meshes() < 1) {
|
||||
SERIAL_ECHOLNPGM("?Mesh too big for EEPROM.");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
return !!error_flag;
|
||||
}
|
||||
|
||||
#if ENABLED(UBL_MESH_WIZARD)
|
||||
|
||||
/**
|
||||
* Produce one of these mesh maps:
|
||||
* 0: Human-readable
|
||||
* 1: CSV format for spreadsheet import
|
||||
* 2: TODO: Display on Graphical LCD
|
||||
* 4: Compact Human-Readable
|
||||
* M1004: UBL Mesh Wizard - One-click mesh creation with or without a probe
|
||||
*/
|
||||
void unified_bed_leveling::display_map(const int map_type) {
|
||||
const bool was = gcode.set_autoreport_paused(true);
|
||||
void GcodeSuite::M1004() {
|
||||
|
||||
constexpr uint8_t eachsp = 1 + 6 + 1, // [-3.567]
|
||||
twixt = eachsp * (GRID_MAX_POINTS_X) - 9 * 2; // Leading 4sp, Coordinates 9sp each
|
||||
#define ALIGN_GCODE TERN(Z_STEPPER_AUTO_ALIGN, "G34", "")
|
||||
#define PROBE_GCODE TERN(HAS_BED_PROBE, "G29P1\nG29P3", "G29P4R")
|
||||
|
||||
const bool human = !(map_type & 0x3), csv = map_type == 1, lcd = map_type == 2, comp = map_type & 0x4;
|
||||
|
||||
SERIAL_ECHOPGM("\nBed Topography Report");
|
||||
if (human) {
|
||||
SERIAL_ECHOLNPGM(":\n");
|
||||
serial_echo_xy(4, MESH_MIN_X, MESH_MAX_Y);
|
||||
serial_echo_xy(twixt, MESH_MAX_X, MESH_MAX_Y);
|
||||
SERIAL_EOL();
|
||||
serial_echo_column_labels(eachsp - 2);
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHOPGM(" for ");
|
||||
serialprintPGM(csv ? PSTR("CSV:\n") : PSTR("LCD:\n"));
|
||||
}
|
||||
|
||||
// Add XY probe offset from extruder because probe.probe_at_point() subtracts them when
|
||||
// moving to the XY position to be measured. This ensures better agreement between
|
||||
// the current Z position after G28 and the mesh values.
|
||||
const xy_int8_t curr = closest_indexes(xy_pos_t(current_position) + probe.offset_xy);
|
||||
|
||||
if (!lcd) SERIAL_EOL();
|
||||
for (int8_t j = GRID_MAX_POINTS_Y - 1; j >= 0; j--) {
|
||||
|
||||
// Row Label (J index)
|
||||
if (human) {
|
||||
if (j < 10) SERIAL_CHAR(' ');
|
||||
SERIAL_ECHO(j);
|
||||
SERIAL_ECHOPGM(" |");
|
||||
#if HAS_HOTEND
|
||||
if (parser.seenval('H')) { // Handle H# parameter to set Hotend temp
|
||||
const celsius_t hotend_temp = parser.value_int(); // Marlin never sends itself F or K, always C
|
||||
thermalManager.setTargetHotend(hotend_temp, 0);
|
||||
thermalManager.wait_for_hotend(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Row Values (I indexes)
|
||||
LOOP_L_N(i, GRID_MAX_POINTS_X) {
|
||||
|
||||
// Opening Brace or Space
|
||||
const bool is_current = i == curr.x && j == curr.y;
|
||||
if (human) SERIAL_CHAR(is_current ? '[' : ' ');
|
||||
|
||||
// Z Value at current I, J
|
||||
const float f = z_values[i][j];
|
||||
if (lcd) {
|
||||
// TODO: Display on Graphical LCD
|
||||
}
|
||||
else if (isnan(f))
|
||||
serialprintPGM(human ? PSTR(" . ") : PSTR("NAN"));
|
||||
else if (human || csv) {
|
||||
if (human && f >= 0.0) SERIAL_CHAR(f > 0 ? '+' : ' '); // Space for positive ('-' for negative)
|
||||
SERIAL_ECHO_F(f, 3); // Positive: 5 digits, Negative: 6 digits
|
||||
}
|
||||
if (csv && i < GRID_MAX_POINTS_X - 1) SERIAL_CHAR('\t');
|
||||
|
||||
// Closing Brace or Space
|
||||
if (human) SERIAL_CHAR(is_current ? ']' : ' ');
|
||||
|
||||
SERIAL_FLUSHTX();
|
||||
idle();
|
||||
#if HAS_HEATED_BED
|
||||
if (parser.seenval('B')) { // Handle B# parameter to set Bed temp
|
||||
const celsius_t bed_temp = parser.value_int(); // Marlin never sends itself F or K, always C
|
||||
thermalManager.setTargetBed(bed_temp);
|
||||
thermalManager.wait_for_bed(false);
|
||||
}
|
||||
if (!lcd) SERIAL_EOL();
|
||||
#endif
|
||||
|
||||
// A blank line between rows (unless compact)
|
||||
if (j && human && !comp) SERIAL_ECHOLNPGM(" |");
|
||||
process_subcommands_now_P(G28_STR); // Home
|
||||
process_subcommands_now_P(PSTR(ALIGN_GCODE "\n" // Align multi z axis if available
|
||||
PROBE_GCODE "\n" // Build mesh with available hardware
|
||||
"G29P3\nG29P3")); // Ensure mesh is complete by running smart fill twice
|
||||
|
||||
if (parser.seenval('S')) {
|
||||
char umw_gcode[32];
|
||||
sprintf_P(umw_gcode, PSTR("G29S%i"), parser.value_int());
|
||||
queue.inject(umw_gcode);
|
||||
}
|
||||
|
||||
if (human) {
|
||||
serial_echo_column_labels(eachsp - 2);
|
||||
SERIAL_EOL();
|
||||
serial_echo_xy(4, MESH_MIN_X, MESH_MIN_Y);
|
||||
serial_echo_xy(twixt, MESH_MAX_X, MESH_MIN_Y);
|
||||
SERIAL_EOL();
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
gcode.set_autoreport_paused(was);
|
||||
process_subcommands_now_P(PSTR("G29A\nG29F10\n" // Set UBL Active & Fade 10
|
||||
"M140S0\nM104S0\n" // Turn off heaters
|
||||
"M500")); // Store settings
|
||||
}
|
||||
|
||||
bool unified_bed_leveling::sanity_check() {
|
||||
uint8_t error_flag = 0;
|
||||
|
||||
if (settings.calc_num_meshes() < 1) {
|
||||
SERIAL_ECHOLNPGM("?Mesh too big for EEPROM.");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
return !!error_flag;
|
||||
}
|
||||
#endif // UBL_MESH_WIZARD
|
||||
|
||||
#endif // AUTO_BED_LEVELING_UBL
|
||||
|
||||
493
Marlin/src/feature/bedlevel/ubl/ubl.h
Executable file → Normal file
493
Marlin/src/feature/bedlevel/ubl/ubl.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -32,276 +32,279 @@
|
||||
#define UBL_OK false
|
||||
#define UBL_ERR true
|
||||
|
||||
enum MeshPointType : char { INVALID, REAL, SET_IN_BITMAP };
|
||||
enum MeshPointType : char { INVALID, REAL, SET_IN_BITMAP, CLOSEST };
|
||||
|
||||
// External references
|
||||
|
||||
struct mesh_index_pair;
|
||||
|
||||
#define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / float(GRID_MAX_POINTS_X - 1))
|
||||
#define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / float(GRID_MAX_POINTS_Y - 1))
|
||||
#define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / (GRID_MAX_CELLS_X))
|
||||
#define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / (GRID_MAX_CELLS_Y))
|
||||
|
||||
#if ENABLED(OPTIMIZED_MESH_STORAGE)
|
||||
typedef int16_t mesh_store_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
bool C_seen;
|
||||
int8_t KLS_storage_slot;
|
||||
uint8_t R_repetition,
|
||||
V_verbosity,
|
||||
P_phase,
|
||||
T_map_type;
|
||||
float B_shim_thickness,
|
||||
C_constant;
|
||||
xy_pos_t XY_pos;
|
||||
xy_bool_t XY_seen;
|
||||
#if HAS_BED_PROBE
|
||||
uint8_t J_grid_size;
|
||||
#endif
|
||||
} G29_parameters_t;
|
||||
|
||||
class unified_bed_leveling {
|
||||
private:
|
||||
private:
|
||||
|
||||
static int g29_verbose_level,
|
||||
g29_phase_value,
|
||||
g29_repetition_cnt,
|
||||
g29_storage_slot,
|
||||
g29_map_type;
|
||||
static bool g29_c_flag;
|
||||
static float g29_card_thickness,
|
||||
g29_constant;
|
||||
static xy_pos_t g29_pos;
|
||||
static xy_bool_t xy_seen;
|
||||
static G29_parameters_t param;
|
||||
|
||||
#if HAS_BED_PROBE
|
||||
static int g29_grid_size;
|
||||
#endif
|
||||
#if IS_NEWPANEL
|
||||
static void move_z_with_encoder(const_float_t multiplier);
|
||||
static float measure_point_with_encoder();
|
||||
static float measure_business_card_thickness();
|
||||
static void manually_probe_remaining_mesh(const xy_pos_t&, const_float_t , const_float_t , const bool) _O0;
|
||||
static void fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) _O0;
|
||||
#endif
|
||||
|
||||
#if ENABLED(NEWPANEL)
|
||||
static void move_z_with_encoder(const float &multiplier);
|
||||
static float measure_point_with_encoder();
|
||||
static float measure_business_card_thickness(float in_height);
|
||||
static void manually_probe_remaining_mesh(const xy_pos_t&, const float&, const float&, const bool) _O0;
|
||||
static void fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) _O0;
|
||||
#endif
|
||||
static bool G29_parse_parameters() _O0;
|
||||
static void shift_mesh_height();
|
||||
static void probe_entire_mesh(const xy_pos_t &near, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) _O0;
|
||||
static void tilt_mesh_based_on_3pts(const_float_t z1, const_float_t z2, const_float_t z3);
|
||||
static void tilt_mesh_based_on_probed_grid(const bool do_ubl_mesh_map);
|
||||
static bool smart_fill_one(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir);
|
||||
static inline bool smart_fill_one(const xy_uint8_t &pos, const xy_uint8_t &dir) {
|
||||
return smart_fill_one(pos.x, pos.y, dir.x, dir.y);
|
||||
}
|
||||
static void smart_fill_mesh();
|
||||
|
||||
static bool g29_parameter_parsing() _O0;
|
||||
static void shift_mesh_height();
|
||||
static void probe_entire_mesh(const xy_pos_t &near, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) _O0;
|
||||
static void tilt_mesh_based_on_3pts(const float &z1, const float &z2, const float &z3);
|
||||
static void tilt_mesh_based_on_probed_grid(const bool do_ubl_mesh_map);
|
||||
static bool smart_fill_one(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir);
|
||||
static inline bool smart_fill_one(const xy_uint8_t &pos, const xy_uint8_t &dir) {
|
||||
return smart_fill_one(pos.x, pos.y, dir.x, dir.y);
|
||||
#if ENABLED(UBL_DEVEL_DEBUGGING)
|
||||
static void g29_what_command();
|
||||
static void g29_eeprom_dump();
|
||||
static void g29_compare_current_mesh_to_stored_mesh();
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
static void echo_name();
|
||||
static void report_current_mesh();
|
||||
static void report_state();
|
||||
static void save_ubl_active_state_and_disable();
|
||||
static void restore_ubl_active_state_and_leave();
|
||||
static void display_map(const uint8_t) _O0;
|
||||
static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) _O0;
|
||||
static mesh_index_pair find_furthest_invalid_mesh_point() _O0;
|
||||
static void reset();
|
||||
static void invalidate();
|
||||
static void set_all_mesh_points_to_value(const_float_t value);
|
||||
static void adjust_mesh_to_mean(const bool cflag, const_float_t value);
|
||||
static bool sanity_check();
|
||||
|
||||
static void G29() _O0; // O0 for no optimization
|
||||
static void smart_fill_wlsf(const_float_t ) _O2; // O2 gives smaller code than Os on A2560
|
||||
|
||||
static int8_t storage_slot;
|
||||
|
||||
static bed_mesh_t z_values;
|
||||
#if ENABLED(OPTIMIZED_MESH_STORAGE)
|
||||
static void set_store_from_mesh(const bed_mesh_t &in_values, mesh_store_t &stored_values);
|
||||
static void set_mesh_from_store(const mesh_store_t &stored_values, bed_mesh_t &out_values);
|
||||
#endif
|
||||
static const float _mesh_index_to_xpos[GRID_MAX_POINTS_X],
|
||||
_mesh_index_to_ypos[GRID_MAX_POINTS_Y];
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
static bool lcd_map_control;
|
||||
static void steppers_were_disabled();
|
||||
#else
|
||||
static inline void steppers_were_disabled() {}
|
||||
#endif
|
||||
|
||||
static volatile int16_t encoder_diff; // Volatile because buttons may change it at interrupt time
|
||||
|
||||
unified_bed_leveling();
|
||||
|
||||
FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const_float_t z) { z_values[px][py] = z; }
|
||||
|
||||
static int8_t cell_index_x_raw(const_float_t x) {
|
||||
return FLOOR((x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST));
|
||||
}
|
||||
|
||||
static int8_t cell_index_y_raw(const_float_t y) {
|
||||
return FLOOR((y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST));
|
||||
}
|
||||
|
||||
static int8_t cell_index_x_valid(const_float_t x) {
|
||||
return WITHIN(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1);
|
||||
}
|
||||
|
||||
static int8_t cell_index_y_valid(const_float_t y) {
|
||||
return WITHIN(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1);
|
||||
}
|
||||
|
||||
static int8_t cell_index_x(const_float_t x) {
|
||||
return constrain(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1);
|
||||
}
|
||||
|
||||
static int8_t cell_index_y(const_float_t y) {
|
||||
return constrain(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1);
|
||||
}
|
||||
|
||||
static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) {
|
||||
return { cell_index_x(x), cell_index_y(y) };
|
||||
}
|
||||
static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
|
||||
|
||||
static int8_t closest_x_index(const_float_t x) {
|
||||
const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST);
|
||||
return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1;
|
||||
}
|
||||
static int8_t closest_y_index(const_float_t y) {
|
||||
const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * RECIPROCAL(MESH_Y_DIST);
|
||||
return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1;
|
||||
}
|
||||
static inline xy_int8_t closest_indexes(const xy_pos_t &xy) {
|
||||
return { closest_x_index(xy.x), closest_y_index(xy.y) };
|
||||
}
|
||||
|
||||
/**
|
||||
* z2 --|
|
||||
* z0 | |
|
||||
* | | + (z2-z1)
|
||||
* z1 | | |
|
||||
* ---+-------------+--------+-- --|
|
||||
* a1 a0 a2
|
||||
* |<---delta_a---------->|
|
||||
*
|
||||
* calc_z0 is the basis for all the Mesh Based correction. It is used to
|
||||
* find the expected Z Height at a position between two known Z-Height locations.
|
||||
*
|
||||
* It is fairly expensive with its 4 floating point additions and 2 floating point
|
||||
* multiplications.
|
||||
*/
|
||||
FORCE_INLINE static float calc_z0(const_float_t a0, const_float_t a1, const_float_t z1, const_float_t a2, const_float_t z2) {
|
||||
return z1 + (z2 - z1) * (a0 - a1) / (a2 - a1);
|
||||
}
|
||||
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
#define _UBL_OUTER_Z_RAISE UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
#else
|
||||
#define _UBL_OUTER_Z_RAISE NAN
|
||||
#endif
|
||||
|
||||
/**
|
||||
* z_correction_for_x_on_horizontal_mesh_line is an optimization for
|
||||
* the case where the printer is making a vertical line that only crosses horizontal mesh lines.
|
||||
*/
|
||||
static inline float z_correction_for_x_on_horizontal_mesh_line(const_float_t rx0, const int x1_i, const int yi) {
|
||||
if (!WITHIN(x1_i, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(yi, 0, (GRID_MAX_POINTS_Y) - 1)) {
|
||||
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
if (WITHIN(x1_i, 0, (GRID_MAX_POINTS_X) - 1)) DEBUG_ECHOPGM("yi"); else DEBUG_ECHOPGM("x1_i");
|
||||
DEBUG_ECHOLNPGM(" out of bounds in z_correction_for_x_on_horizontal_mesh_line(rx0=", rx0, ",x1_i=", x1_i, ",yi=", yi, ")");
|
||||
}
|
||||
|
||||
// The requested location is off the mesh. Return UBL_Z_RAISE_WHEN_OFF_MESH or NAN.
|
||||
return _UBL_OUTER_Z_RAISE;
|
||||
}
|
||||
static void smart_fill_mesh();
|
||||
|
||||
#if ENABLED(UBL_DEVEL_DEBUGGING)
|
||||
static void g29_what_command();
|
||||
static void g29_eeprom_dump();
|
||||
static void g29_compare_current_mesh_to_stored_mesh();
|
||||
#endif
|
||||
const float xratio = (rx0 - mesh_index_to_xpos(x1_i)) * RECIPROCAL(MESH_X_DIST),
|
||||
z1 = z_values[x1_i][yi];
|
||||
|
||||
public:
|
||||
return z1 + xratio * (z_values[_MIN(x1_i, (GRID_MAX_POINTS_X) - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array
|
||||
// If it is, it is clamped to the last element of the
|
||||
// z_values[][] array and no correction is applied.
|
||||
}
|
||||
|
||||
static void echo_name();
|
||||
static void report_current_mesh();
|
||||
static void report_state();
|
||||
static void save_ubl_active_state_and_disable();
|
||||
static void restore_ubl_active_state_and_leave();
|
||||
static void display_map(const int) _O0;
|
||||
static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) _O0;
|
||||
static mesh_index_pair find_furthest_invalid_mesh_point() _O0;
|
||||
static void reset();
|
||||
static void invalidate();
|
||||
static void set_all_mesh_points_to_value(const float value);
|
||||
static void adjust_mesh_to_mean(const bool cflag, const float value);
|
||||
static bool sanity_check();
|
||||
//
|
||||
// See comments above for z_correction_for_x_on_horizontal_mesh_line
|
||||
//
|
||||
static inline float z_correction_for_y_on_vertical_mesh_line(const_float_t ry0, const int xi, const int y1_i) {
|
||||
if (!WITHIN(xi, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(y1_i, 0, (GRID_MAX_POINTS_Y) - 1)) {
|
||||
|
||||
static void G29() _O0; // O0 for no optimization
|
||||
static void smart_fill_wlsf(const float &) _O2; // O2 gives smaller code than Os on A2560
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
if (WITHIN(xi, 0, (GRID_MAX_POINTS_X) - 1)) DEBUG_ECHOPGM("y1_i"); else DEBUG_ECHOPGM("xi");
|
||||
DEBUG_ECHOLNPGM(" out of bounds in z_correction_for_y_on_vertical_mesh_line(ry0=", ry0, ", xi=", xi, ", y1_i=", y1_i, ")");
|
||||
}
|
||||
|
||||
static int8_t storage_slot;
|
||||
|
||||
static bed_mesh_t z_values;
|
||||
static const float _mesh_index_to_xpos[GRID_MAX_POINTS_X],
|
||||
_mesh_index_to_ypos[GRID_MAX_POINTS_Y];
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
static bool lcd_map_control;
|
||||
#endif
|
||||
|
||||
static volatile int encoder_diff; // Volatile because it's changed at interrupt time.
|
||||
|
||||
unified_bed_leveling();
|
||||
|
||||
FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const float &z) { z_values[px][py] = z; }
|
||||
|
||||
static int8_t cell_index_x(const float &x) {
|
||||
const int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST);
|
||||
return constrain(cx, 0, (GRID_MAX_POINTS_X) - 1); // -1 is appropriate if we want all movement to the X_MAX
|
||||
} // position. But with this defined this way, it is possible
|
||||
// to extrapolate off of this point even further out. Probably
|
||||
// that is OK because something else should be keeping that from
|
||||
// happening and should not be worried about at this level.
|
||||
static int8_t cell_index_y(const float &y) {
|
||||
const int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST);
|
||||
return constrain(cy, 0, (GRID_MAX_POINTS_Y) - 1); // -1 is appropriate if we want all movement to the Y_MAX
|
||||
} // position. But with this defined this way, it is possible
|
||||
// to extrapolate off of this point even further out. Probably
|
||||
// that is OK because something else should be keeping that from
|
||||
// happening and should not be worried about at this level.
|
||||
|
||||
static inline xy_int8_t cell_indexes(const float &x, const float &y) {
|
||||
return { cell_index_x(x), cell_index_y(y) };
|
||||
// The requested location is off the mesh. Return UBL_Z_RAISE_WHEN_OFF_MESH or NAN.
|
||||
return _UBL_OUTER_Z_RAISE;
|
||||
}
|
||||
static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
|
||||
|
||||
static int8_t closest_x_index(const float &x) {
|
||||
const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST);
|
||||
return WITHIN(px, 0, GRID_MAX_POINTS_X - 1) ? px : -1;
|
||||
}
|
||||
static int8_t closest_y_index(const float &y) {
|
||||
const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * RECIPROCAL(MESH_Y_DIST);
|
||||
return WITHIN(py, 0, GRID_MAX_POINTS_Y - 1) ? py : -1;
|
||||
}
|
||||
static inline xy_int8_t closest_indexes(const xy_pos_t &xy) {
|
||||
return { closest_x_index(xy.x), closest_y_index(xy.y) };
|
||||
}
|
||||
const float yratio = (ry0 - mesh_index_to_ypos(y1_i)) * RECIPROCAL(MESH_Y_DIST),
|
||||
z1 = z_values[xi][y1_i];
|
||||
|
||||
return z1 + yratio * (z_values[xi][_MIN(y1_i, (GRID_MAX_POINTS_Y) - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array
|
||||
// If it is, it is clamped to the last element of the
|
||||
// z_values[][] array and no correction is applied.
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the generic Z-Correction. It works anywhere within a Mesh Cell. It first
|
||||
* does a linear interpolation along both of the bounding X-Mesh-Lines to find the
|
||||
* Z-Height at both ends. Then it does a linear interpolation of these heights based
|
||||
* on the Y position within the cell.
|
||||
*/
|
||||
static float get_z_correction(const_float_t rx0, const_float_t ry0) {
|
||||
const int8_t cx = cell_index_x(rx0), cy = cell_index_y(ry0); // return values are clamped
|
||||
|
||||
/**
|
||||
* z2 --|
|
||||
* z0 | |
|
||||
* | | + (z2-z1)
|
||||
* z1 | | |
|
||||
* ---+-------------+--------+-- --|
|
||||
* a1 a0 a2
|
||||
* |<---delta_a---------->|
|
||||
*
|
||||
* calc_z0 is the basis for all the Mesh Based correction. It is used to
|
||||
* find the expected Z Height at a position between two known Z-Height locations.
|
||||
*
|
||||
* It is fairly expensive with its 4 floating point additions and 2 floating point
|
||||
* multiplications.
|
||||
* Check if the requested location is off the mesh. If so, and
|
||||
* UBL_Z_RAISE_WHEN_OFF_MESH is specified, that value is returned.
|
||||
*/
|
||||
FORCE_INLINE static float calc_z0(const float &a0, const float &a1, const float &z1, const float &a2, const float &z2) {
|
||||
return z1 + (z2 - z1) * (a0 - a1) / (a2 - a1);
|
||||
}
|
||||
|
||||
/**
|
||||
* z_correction_for_x_on_horizontal_mesh_line is an optimization for
|
||||
* the case where the printer is making a vertical line that only crosses horizontal mesh lines.
|
||||
*/
|
||||
static inline float z_correction_for_x_on_horizontal_mesh_line(const float &rx0, const int x1_i, const int yi) {
|
||||
if (!WITHIN(x1_i, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(yi, 0, GRID_MAX_POINTS_Y - 1)) {
|
||||
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
if (WITHIN(x1_i, 0, GRID_MAX_POINTS_X - 1)) DEBUG_ECHOPGM("yi"); else DEBUG_ECHOPGM("x1_i");
|
||||
DEBUG_ECHOLNPAIR(" out of bounds in z_correction_for_x_on_horizontal_mesh_line(rx0=", rx0, ",x1_i=", x1_i, ",yi=", yi, ")");
|
||||
}
|
||||
|
||||
// The requested location is off the mesh. Return UBL_Z_RAISE_WHEN_OFF_MESH or NAN.
|
||||
return (
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
#else
|
||||
NAN
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
const float xratio = (rx0 - mesh_index_to_xpos(x1_i)) * RECIPROCAL(MESH_X_DIST),
|
||||
z1 = z_values[x1_i][yi];
|
||||
|
||||
return z1 + xratio * (z_values[_MIN(x1_i, GRID_MAX_POINTS_X - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array
|
||||
// If it is, it is clamped to the last element of the
|
||||
// z_values[][] array and no correction is applied.
|
||||
}
|
||||
|
||||
//
|
||||
// See comments above for z_correction_for_x_on_horizontal_mesh_line
|
||||
//
|
||||
static inline float z_correction_for_y_on_vertical_mesh_line(const float &ry0, const int xi, const int y1_i) {
|
||||
if (!WITHIN(xi, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(y1_i, 0, GRID_MAX_POINTS_Y - 1)) {
|
||||
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
if (WITHIN(xi, 0, GRID_MAX_POINTS_X - 1)) DEBUG_ECHOPGM("y1_i"); else DEBUG_ECHOPGM("xi");
|
||||
DEBUG_ECHOLNPAIR(" out of bounds in z_correction_for_y_on_vertical_mesh_line(ry0=", ry0, ", xi=", xi, ", y1_i=", y1_i, ")");
|
||||
}
|
||||
|
||||
// The requested location is off the mesh. Return UBL_Z_RAISE_WHEN_OFF_MESH or NAN.
|
||||
return (
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
#else
|
||||
NAN
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
const float yratio = (ry0 - mesh_index_to_ypos(y1_i)) * RECIPROCAL(MESH_Y_DIST),
|
||||
z1 = z_values[xi][y1_i];
|
||||
|
||||
return z1 + yratio * (z_values[xi][_MIN(y1_i, GRID_MAX_POINTS_Y - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array
|
||||
// If it is, it is clamped to the last element of the
|
||||
// z_values[][] array and no correction is applied.
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the generic Z-Correction. It works anywhere within a Mesh Cell. It first
|
||||
* does a linear interpolation along both of the bounding X-Mesh-Lines to find the
|
||||
* Z-Height at both ends. Then it does a linear interpolation of these heights based
|
||||
* on the Y position within the cell.
|
||||
*/
|
||||
static float get_z_correction(const float &rx0, const float &ry0) {
|
||||
const int8_t cx = cell_index_x(rx0), cy = cell_index_y(ry0); // return values are clamped
|
||||
|
||||
/**
|
||||
* Check if the requested location is off the mesh. If so, and
|
||||
* UBL_Z_RAISE_WHEN_OFF_MESH is specified, that value is returned.
|
||||
*/
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
if (!WITHIN(rx0, MESH_MIN_X, MESH_MAX_X) || !WITHIN(ry0, MESH_MIN_Y, MESH_MAX_Y))
|
||||
return UBL_Z_RAISE_WHEN_OFF_MESH;
|
||||
#endif
|
||||
|
||||
const float z1 = calc_z0(rx0,
|
||||
mesh_index_to_xpos(cx), z_values[cx][cy],
|
||||
mesh_index_to_xpos(cx + 1), z_values[_MIN(cx, GRID_MAX_POINTS_X - 2) + 1][cy]);
|
||||
|
||||
const float z2 = calc_z0(rx0,
|
||||
mesh_index_to_xpos(cx), z_values[cx][_MIN(cy, GRID_MAX_POINTS_Y - 2) + 1],
|
||||
mesh_index_to_xpos(cx + 1), z_values[_MIN(cx, GRID_MAX_POINTS_X - 2) + 1][_MIN(cy, GRID_MAX_POINTS_Y - 2) + 1]);
|
||||
|
||||
float z0 = calc_z0(ry0,
|
||||
mesh_index_to_ypos(cy), z1,
|
||||
mesh_index_to_ypos(cy + 1), z2);
|
||||
|
||||
if (DEBUGGING(MESH_ADJUST)) {
|
||||
DEBUG_ECHOPAIR(" raw get_z_correction(", rx0);
|
||||
DEBUG_CHAR(','); DEBUG_ECHO(ry0);
|
||||
DEBUG_ECHOPAIR_F(") = ", z0, 6);
|
||||
DEBUG_ECHOLNPAIR_F(" >>>---> ", z0, 6);
|
||||
}
|
||||
|
||||
if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in ubl.z_values[][] and propagate through the
|
||||
// calculations. If our correction is NAN, we throw it out
|
||||
// because part of the Mesh is undefined and we don't have the
|
||||
// information we need to complete the height correction.
|
||||
|
||||
if (DEBUGGING(MESH_ADJUST)) {
|
||||
DEBUG_ECHOPAIR("??? Yikes! NAN in get_z_correction(", rx0);
|
||||
DEBUG_CHAR(',');
|
||||
DEBUG_ECHO(ry0);
|
||||
DEBUG_CHAR(')');
|
||||
DEBUG_EOL();
|
||||
}
|
||||
}
|
||||
return z0;
|
||||
}
|
||||
static inline float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); }
|
||||
|
||||
static inline float mesh_index_to_xpos(const uint8_t i) {
|
||||
return i < GRID_MAX_POINTS_X ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST);
|
||||
}
|
||||
static inline float mesh_index_to_ypos(const uint8_t i) {
|
||||
return i < GRID_MAX_POINTS_Y ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST);
|
||||
}
|
||||
|
||||
#if UBL_SEGMENTED
|
||||
static bool line_to_destination_segmented(const feedRate_t &scaled_fr_mm_s);
|
||||
#else
|
||||
static void line_to_destination_cartesian(const feedRate_t &scaled_fr_mm_s, const uint8_t e);
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
if (!WITHIN(rx0, MESH_MIN_X, MESH_MAX_X) || !WITHIN(ry0, MESH_MIN_Y, MESH_MAX_Y))
|
||||
return UBL_Z_RAISE_WHEN_OFF_MESH;
|
||||
#endif
|
||||
|
||||
static inline bool mesh_is_valid() {
|
||||
GRID_LOOP(x, y) if (isnan(z_values[x][y])) return false;
|
||||
return true;
|
||||
const uint8_t mx = _MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1, my = _MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1;
|
||||
const float z1 = calc_z0(rx0, mesh_index_to_xpos(cx), z_values[cx][cy], mesh_index_to_xpos(cx + 1), z_values[mx][cy]);
|
||||
const float z2 = calc_z0(rx0, mesh_index_to_xpos(cx), z_values[cx][my], mesh_index_to_xpos(cx + 1), z_values[mx][my]);
|
||||
float z0 = calc_z0(ry0, mesh_index_to_ypos(cy), z1, mesh_index_to_ypos(cy + 1), z2);
|
||||
|
||||
if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in ubl.z_values[][] and propagate through the
|
||||
// calculations. If our correction is NAN, we throw it out
|
||||
// because part of the Mesh is undefined and we don't have the
|
||||
// information we need to complete the height correction.
|
||||
|
||||
if (DEBUGGING(MESH_ADJUST)) DEBUG_ECHOLNPGM("??? Yikes! NAN in ");
|
||||
}
|
||||
|
||||
if (DEBUGGING(MESH_ADJUST)) {
|
||||
DEBUG_ECHOPGM("get_z_correction(", rx0, ", ", ry0);
|
||||
DEBUG_ECHOLNPAIR_F(") => ", z0, 6);
|
||||
}
|
||||
|
||||
return z0;
|
||||
}
|
||||
static inline float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); }
|
||||
|
||||
static inline float mesh_index_to_xpos(const uint8_t i) {
|
||||
return i < (GRID_MAX_POINTS_X) ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST);
|
||||
}
|
||||
static inline float mesh_index_to_ypos(const uint8_t i) {
|
||||
return i < (GRID_MAX_POINTS_Y) ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST);
|
||||
}
|
||||
|
||||
#if UBL_SEGMENTED
|
||||
static bool line_to_destination_segmented(const_feedRate_t scaled_fr_mm_s);
|
||||
#else
|
||||
static void line_to_destination_cartesian(const_feedRate_t scaled_fr_mm_s, const uint8_t e);
|
||||
#endif
|
||||
|
||||
static inline bool mesh_is_valid() {
|
||||
GRID_LOOP(x, y) if (isnan(z_values[x][y])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // class unified_bed_leveling
|
||||
|
||||
extern unified_bed_leveling ubl;
|
||||
|
||||
3290
Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
Executable file → Normal file
3290
Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
198
Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp
Executable file → Normal file
198
Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp
Executable file → Normal file
@@ -16,9 +16,10 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
@@ -37,7 +38,7 @@
|
||||
|
||||
#if !UBL_SEGMENTED
|
||||
|
||||
void unified_bed_leveling::line_to_destination_cartesian(const feedRate_t &scaled_fr_mm_s, const uint8_t extruder) {
|
||||
void unified_bed_leveling::line_to_destination_cartesian(const_feedRate_t scaled_fr_mm_s, const uint8_t extruder) {
|
||||
/**
|
||||
* Much of the nozzle movement will be within the same cell. So we will do as little computation
|
||||
* as possible to determine if this is the case. If this move is within the same cell, we will
|
||||
@@ -56,39 +57,32 @@
|
||||
// A move within the same cell needs no splitting
|
||||
if (istart == iend) {
|
||||
|
||||
// For a move off the bed, use a constant Z raise
|
||||
if (!WITHIN(iend.x, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(iend.y, 0, GRID_MAX_POINTS_Y - 1)) {
|
||||
|
||||
// Note: There is no Z Correction in this case. We are off the grid and don't know what
|
||||
// a reasonable correction would be. If the user has specified a UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
// value, that will be used instead of a calculated (Bi-Linear interpolation) correction.
|
||||
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
end.z += UBL_Z_RAISE_WHEN_OFF_MESH;
|
||||
#endif
|
||||
planner.buffer_segment(end, scaled_fr_mm_s, extruder);
|
||||
current_position = destination;
|
||||
return;
|
||||
}
|
||||
|
||||
FINAL_MOVE:
|
||||
|
||||
// The distance is always MESH_X_DIST so multiply by the constant reciprocal.
|
||||
const float xratio = (end.x - mesh_index_to_xpos(iend.x)) * RECIPROCAL(MESH_X_DIST);
|
||||
// When UBL_Z_RAISE_WHEN_OFF_MESH is disabled Z correction is extrapolated from the edge of the mesh
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
// For a move off the UBL mesh, use a constant Z raise
|
||||
if (!cell_index_x_valid(end.x) || !cell_index_y_valid(end.y)) {
|
||||
|
||||
float z1, z2;
|
||||
if (iend.x >= GRID_MAX_POINTS_X - 1)
|
||||
z1 = z2 = 0.0;
|
||||
else {
|
||||
z1 = z_values[iend.x ][iend.y ] + xratio *
|
||||
(z_values[iend.x + 1][iend.y ] - z_values[iend.x][iend.y ]),
|
||||
z2 = z_values[iend.x ][iend.y + 1] + xratio *
|
||||
(z_values[iend.x + 1][iend.y + 1] - z_values[iend.x][iend.y + 1]);
|
||||
}
|
||||
// Note: There is no Z Correction in this case. We are off the mesh and don't know what
|
||||
// a reasonable correction would be, UBL_Z_RAISE_WHEN_OFF_MESH will be used instead of
|
||||
// a calculated (Bi-Linear interpolation) correction.
|
||||
|
||||
end.z += UBL_Z_RAISE_WHEN_OFF_MESH;
|
||||
planner.buffer_segment(end, scaled_fr_mm_s, extruder);
|
||||
current_position = destination;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// The distance is always MESH_X_DIST so multiply by the constant reciprocal.
|
||||
const float xratio = (end.x - mesh_index_to_xpos(iend.x)) * RECIPROCAL(MESH_X_DIST),
|
||||
yratio = (end.y - mesh_index_to_ypos(iend.y)) * RECIPROCAL(MESH_Y_DIST),
|
||||
z1 = z_values[iend.x][iend.y ] + xratio * (z_values[iend.x + 1][iend.y ] - z_values[iend.x][iend.y ]),
|
||||
z2 = z_values[iend.x][iend.y + 1] + xratio * (z_values[iend.x + 1][iend.y + 1] - z_values[iend.x][iend.y + 1]);
|
||||
|
||||
// X cell-fraction done. Interpolate the two Z offsets with the Y fraction for the final Z offset.
|
||||
const float yratio = (end.y - mesh_index_to_ypos(iend.y)) * RECIPROCAL(MESH_Y_DIST),
|
||||
z0 = iend.y < GRID_MAX_POINTS_Y - 1 ? (z1 + (z2 - z1) * yratio) * planner.fade_scaling_factor_for_z(end.z) : 0.0;
|
||||
const float z0 = (z1 + (z2 - z1) * yratio) * planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
@@ -120,20 +114,22 @@
|
||||
const xy_float_t ad = sign * dist;
|
||||
const bool use_x_dist = ad.x > ad.y;
|
||||
|
||||
float on_axis_distance = use_x_dist ? dist.x : dist.y,
|
||||
e_position = end.e - start.e,
|
||||
z_position = end.z - start.z;
|
||||
float on_axis_distance = use_x_dist ? dist.x : dist.y;
|
||||
|
||||
const float e_normalized_dist = e_position / on_axis_distance, // Allow divide by zero
|
||||
z_normalized_dist = z_position / on_axis_distance;
|
||||
const float z_normalized_dist = (end.z - start.z) / on_axis_distance; // Allow divide by zero
|
||||
#if HAS_EXTRUDERS
|
||||
const float e_normalized_dist = (end.e - start.e) / on_axis_distance;
|
||||
const bool inf_normalized_flag = isinf(e_normalized_dist);
|
||||
#endif
|
||||
|
||||
xy_int8_t icell = istart;
|
||||
|
||||
const float ratio = dist.y / dist.x, // Allow divide by zero
|
||||
c = start.y - ratio * start.x;
|
||||
|
||||
const bool inf_normalized_flag = isinf(e_normalized_dist),
|
||||
inf_ratio_flag = isinf(ratio);
|
||||
const bool inf_ratio_flag = isinf(ratio);
|
||||
|
||||
xyze_pos_t dest; // Stores XYZE for segmented moves
|
||||
|
||||
/**
|
||||
* Handle vertical lines that stay within one column.
|
||||
@@ -150,34 +146,36 @@
|
||||
* For others the next X is the same so this can continue.
|
||||
* Calculate X at the next Y mesh line.
|
||||
*/
|
||||
const float rx = inf_ratio_flag ? start.x : (next_mesh_line_y - c) / ratio;
|
||||
dest.x = inf_ratio_flag ? start.x : (next_mesh_line_y - c) / ratio;
|
||||
|
||||
float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, icell.x, icell.y)
|
||||
float z0 = z_correction_for_x_on_horizontal_mesh_line(dest.x, icell.x, icell.y)
|
||||
* planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
if (isnan(z0)) z0 = 0.0;
|
||||
|
||||
const float ry = mesh_index_to_ypos(icell.y);
|
||||
dest.y = mesh_index_to_ypos(icell.y);
|
||||
|
||||
/**
|
||||
* Without this check, it's possible to generate a zero length move, as in the case where
|
||||
* the line is heading down, starting exactly on a mesh line boundary. Since this is rare
|
||||
* it might be fine to remove this check and let planner.buffer_segment() filter it out.
|
||||
*/
|
||||
if (ry != start.y) {
|
||||
if (dest.y != start.y) {
|
||||
if (!inf_normalized_flag) { // fall-through faster than branch
|
||||
on_axis_distance = use_x_dist ? rx - start.x : ry - start.y;
|
||||
e_position = start.e + on_axis_distance * e_normalized_dist;
|
||||
z_position = start.z + on_axis_distance * z_normalized_dist;
|
||||
on_axis_distance = use_x_dist ? dest.x - start.x : dest.y - start.y;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = start.e + on_axis_distance * e_normalized_dist);
|
||||
dest.z = start.z + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
e_position = end.e;
|
||||
z_position = end.z;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = end.e);
|
||||
dest.z = end.z;
|
||||
}
|
||||
|
||||
planner.buffer_segment(rx, ry, z_position + z0, e_position, scaled_fr_mm_s, extruder);
|
||||
dest.z += z0;
|
||||
planner.buffer_segment(dest, scaled_fr_mm_s, extruder);
|
||||
|
||||
} //else printf("FIRST MOVE PRUNED ");
|
||||
}
|
||||
|
||||
@@ -195,12 +193,13 @@
|
||||
*/
|
||||
if (iadd.y == 0) { // Horizontal line?
|
||||
icell.x += ineg.x; // Heading left? Just go to the left edge of the cell for the first move.
|
||||
|
||||
while (icell.x != iend.x + ineg.x) {
|
||||
icell.x += iadd.x;
|
||||
const float rx = mesh_index_to_xpos(icell.x);
|
||||
const float ry = ratio * rx + c; // Calculate Y at the next X mesh line
|
||||
dest.x = mesh_index_to_xpos(icell.x);
|
||||
dest.y = ratio * dest.x + c; // Calculate Y at the next X mesh line
|
||||
|
||||
float z0 = z_correction_for_y_on_vertical_mesh_line(ry, icell.x, icell.y)
|
||||
float z0 = z_correction_for_y_on_vertical_mesh_line(dest.y, icell.x, icell.y)
|
||||
* planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
@@ -212,19 +211,20 @@
|
||||
* the line is heading left, starting exactly on a mesh line boundary. Since this is rare
|
||||
* it might be fine to remove this check and let planner.buffer_segment() filter it out.
|
||||
*/
|
||||
if (rx != start.x) {
|
||||
if (dest.x != start.x) {
|
||||
if (!inf_normalized_flag) {
|
||||
on_axis_distance = use_x_dist ? rx - start.x : ry - start.y;
|
||||
e_position = start.e + on_axis_distance * e_normalized_dist; // is based on X or Y because this is a horizontal move
|
||||
z_position = start.z + on_axis_distance * z_normalized_dist;
|
||||
on_axis_distance = use_x_dist ? dest.x - start.x : dest.y - start.y;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = start.e + on_axis_distance * e_normalized_dist); // Based on X or Y because the move is horizontal
|
||||
dest.z = start.z + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
e_position = end.e;
|
||||
z_position = end.z;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = end.e);
|
||||
dest.z = end.z;
|
||||
}
|
||||
|
||||
if (!planner.buffer_segment(rx, ry, z_position + z0, e_position, scaled_fr_mm_s, extruder))
|
||||
break;
|
||||
dest.z += z0;
|
||||
if (!planner.buffer_segment(dest, scaled_fr_mm_s, extruder)) break;
|
||||
|
||||
} //else printf("FIRST MOVE PRUNED ");
|
||||
}
|
||||
|
||||
@@ -236,9 +236,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Generic case of a line crossing both X and Y Mesh lines.
|
||||
*
|
||||
*/
|
||||
|
||||
xy_int8_t cnt = (istart - iend).ABS();
|
||||
@@ -248,57 +246,65 @@
|
||||
while (cnt) {
|
||||
|
||||
const float next_mesh_line_x = mesh_index_to_xpos(icell.x + iadd.x),
|
||||
next_mesh_line_y = mesh_index_to_ypos(icell.y + iadd.y),
|
||||
ry = ratio * next_mesh_line_x + c, // Calculate Y at the next X mesh line
|
||||
rx = (next_mesh_line_y - c) / ratio; // Calculate X at the next Y mesh line
|
||||
// (No need to worry about ratio == 0.
|
||||
// In that case, it was already detected
|
||||
// as a vertical line move above.)
|
||||
next_mesh_line_y = mesh_index_to_ypos(icell.y + iadd.y);
|
||||
|
||||
if (neg.x == (rx > next_mesh_line_x)) { // Check if we hit the Y line first
|
||||
dest.y = ratio * next_mesh_line_x + c; // Calculate Y at the next X mesh line
|
||||
dest.x = (next_mesh_line_y - c) / ratio; // Calculate X at the next Y mesh line
|
||||
// (No need to worry about ratio == 0.
|
||||
// In that case, it was already detected
|
||||
// as a vertical line move above.)
|
||||
|
||||
if (neg.x == (dest.x > next_mesh_line_x)) { // Check if we hit the Y line first
|
||||
// Yes! Crossing a Y Mesh Line next
|
||||
float z0 = z_correction_for_x_on_horizontal_mesh_line(rx, icell.x - ineg.x, icell.y + iadd.y)
|
||||
float z0 = z_correction_for_x_on_horizontal_mesh_line(dest.x, icell.x - ineg.x, icell.y + iadd.y)
|
||||
* planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
if (isnan(z0)) z0 = 0.0;
|
||||
|
||||
dest.y = next_mesh_line_y;
|
||||
|
||||
if (!inf_normalized_flag) {
|
||||
on_axis_distance = use_x_dist ? rx - start.x : next_mesh_line_y - start.y;
|
||||
e_position = start.e + on_axis_distance * e_normalized_dist;
|
||||
z_position = start.z + on_axis_distance * z_normalized_dist;
|
||||
on_axis_distance = use_x_dist ? dest.x - start.x : dest.y - start.y;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = start.e + on_axis_distance * e_normalized_dist);
|
||||
dest.z = start.z + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
e_position = end.e;
|
||||
z_position = end.z;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = end.e);
|
||||
dest.z = end.z;
|
||||
}
|
||||
if (!planner.buffer_segment(rx, next_mesh_line_y, z_position + z0, e_position, scaled_fr_mm_s, extruder))
|
||||
break;
|
||||
|
||||
dest.z += z0;
|
||||
if (!planner.buffer_segment(dest, scaled_fr_mm_s, extruder)) break;
|
||||
|
||||
icell.y += iadd.y;
|
||||
cnt.y--;
|
||||
}
|
||||
else {
|
||||
// Yes! Crossing a X Mesh Line next
|
||||
float z0 = z_correction_for_y_on_vertical_mesh_line(ry, icell.x + iadd.x, icell.y - ineg.y)
|
||||
float z0 = z_correction_for_y_on_vertical_mesh_line(dest.y, icell.x + iadd.x, icell.y - ineg.y)
|
||||
* planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
if (isnan(z0)) z0 = 0.0;
|
||||
|
||||
dest.x = next_mesh_line_x;
|
||||
|
||||
if (!inf_normalized_flag) {
|
||||
on_axis_distance = use_x_dist ? next_mesh_line_x - start.x : ry - start.y;
|
||||
e_position = start.e + on_axis_distance * e_normalized_dist;
|
||||
z_position = start.z + on_axis_distance * z_normalized_dist;
|
||||
on_axis_distance = use_x_dist ? dest.x - start.x : dest.y - start.y;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = start.e + on_axis_distance * e_normalized_dist);
|
||||
dest.z = start.z + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
e_position = end.e;
|
||||
z_position = end.z;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = end.e);
|
||||
dest.z = end.z;
|
||||
}
|
||||
|
||||
if (!planner.buffer_segment(next_mesh_line_x, ry, z_position + z0, e_position, scaled_fr_mm_s, extruder))
|
||||
break;
|
||||
dest.z += z0;
|
||||
if (!planner.buffer_segment(dest, scaled_fr_mm_s, extruder)) break;
|
||||
|
||||
icell.x += iadd.x;
|
||||
cnt.x--;
|
||||
}
|
||||
@@ -318,6 +324,8 @@
|
||||
#define DELTA_SEGMENT_MIN_LENGTH 0.25 // SCARA minimum segment size is 0.25mm
|
||||
#elif ENABLED(DELTA)
|
||||
#define DELTA_SEGMENT_MIN_LENGTH 0.10 // mm (still subject to DELTA_SEGMENTS_PER_SECOND)
|
||||
#elif ENABLED(POLARGRAPH)
|
||||
#define DELTA_SEGMENT_MIN_LENGTH 0.10 // mm (still subject to DELTA_SEGMENTS_PER_SECOND)
|
||||
#else // CARTESIAN
|
||||
#ifdef LEVELED_SEGMENT_LENGTH
|
||||
#define DELTA_SEGMENT_MIN_LENGTH LEVELED_SEGMENT_LENGTH
|
||||
@@ -332,7 +340,7 @@
|
||||
* Returns true if did NOT move, false if moved (requires current_position update).
|
||||
*/
|
||||
|
||||
bool _O2 unified_bed_leveling::line_to_destination_segmented(const feedRate_t &scaled_fr_mm_s) {
|
||||
bool _O2 unified_bed_leveling::line_to_destination_segmented(const_feedRate_t scaled_fr_mm_s) {
|
||||
|
||||
if (!position_is_reachable(destination)) // fail if moving outside reachable boundary
|
||||
return true; // did not move, so current_position still accurate
|
||||
@@ -344,7 +352,7 @@
|
||||
|
||||
#if IS_KINEMATIC
|
||||
const float seconds = cart_xy_mm / scaled_fr_mm_s; // Duration of XY move at requested rate
|
||||
uint16_t segments = LROUND(delta_segments_per_second * seconds), // Preferred number of segments for distance @ feedrate
|
||||
uint16_t segments = LROUND(segments_per_second * seconds), // Preferred number of segments for distance @ feedrate
|
||||
seglimit = LROUND(cart_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length
|
||||
NOMORE(segments, seglimit); // Limit to minimum segment length (fewer segments)
|
||||
#else
|
||||
@@ -371,15 +379,11 @@
|
||||
while (--segments) {
|
||||
raw += diff;
|
||||
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm
|
||||
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
||||
, inv_duration
|
||||
#endif
|
||||
OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
|
||||
);
|
||||
}
|
||||
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, segment_xyz_mm
|
||||
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
||||
, inv_duration
|
||||
#endif
|
||||
OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
|
||||
);
|
||||
return false; // Did not set current from destination
|
||||
}
|
||||
@@ -406,8 +410,8 @@
|
||||
int8_t((raw.x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST)),
|
||||
int8_t((raw.y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST))
|
||||
};
|
||||
LIMIT(icell.x, 0, (GRID_MAX_POINTS_X) - 1);
|
||||
LIMIT(icell.y, 0, (GRID_MAX_POINTS_Y) - 1);
|
||||
LIMIT(icell.x, 0, GRID_MAX_CELLS_X);
|
||||
LIMIT(icell.y, 0, GRID_MAX_CELLS_Y);
|
||||
|
||||
float z_x0y0 = z_values[icell.x ][icell.y ], // z at lower left corner
|
||||
z_x1y0 = z_values[icell.x+1][icell.y ], // z at upper left corner
|
||||
@@ -451,11 +455,9 @@
|
||||
#endif
|
||||
;
|
||||
|
||||
planner.buffer_line(raw.x, raw.y, raw.z + z_cxcy, raw.e, scaled_fr_mm_s, active_extruder, segment_xyz_mm
|
||||
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
||||
, inv_duration
|
||||
#endif
|
||||
);
|
||||
const float oldz = raw.z; raw.z += z_cxcy;
|
||||
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration) );
|
||||
raw.z = oldz;
|
||||
|
||||
if (segments == 0) // done with last segment
|
||||
return false; // didn't set current from destination
|
||||
|
||||
6
Marlin/src/feature/binary_protocol.cpp → Marlin/src/feature/binary_stream.cpp
Executable file → Normal file
6
Marlin/src/feature/binary_protocol.cpp → Marlin/src/feature/binary_stream.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#if ENABLED(BINARY_FILE_TRANSFER)
|
||||
|
||||
#include "../sd/cardreader.h"
|
||||
#include "binary_protocol.h"
|
||||
#include "binary_stream.h"
|
||||
|
||||
char* SDFileTransferProtocol::Packet::Open::data = nullptr;
|
||||
size_t SDFileTransferProtocol::data_waiting, SDFileTransferProtocol::transfer_timeout, SDFileTransferProtocol::idle_timeout;
|
||||
@@ -33,4 +33,4 @@ bool SDFileTransferProtocol::transfer_active, SDFileTransferProtocol::dummy_tran
|
||||
|
||||
BinaryStream binaryStream[NUM_SERIAL];
|
||||
|
||||
#endif // BINARY_FILE_TRANSFER
|
||||
#endif
|
||||
90
Marlin/src/feature/binary_protocol.h → Marlin/src/feature/binary_stream.h
Executable file → Normal file
90
Marlin/src/feature/binary_protocol.h → Marlin/src/feature/binary_stream.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -24,44 +24,29 @@
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#define BINARY_STREAM_COMPRESSION
|
||||
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
#include "../libs/heatshrink/heatshrink_decoder.h"
|
||||
#endif
|
||||
|
||||
inline bool bs_serial_data_available(const uint8_t index) {
|
||||
switch (index) {
|
||||
case 0: return MYSERIAL0.available();
|
||||
#if NUM_SERIAL > 1
|
||||
case 1: return MYSERIAL1.available();
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int bs_read_serial(const uint8_t index) {
|
||||
switch (index) {
|
||||
case 0: return MYSERIAL0.read();
|
||||
#if NUM_SERIAL > 1
|
||||
case 1: return MYSERIAL1.read();
|
||||
#endif
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
// STM32 (and others?) require a word-aligned buffer for SD card transfers via DMA
|
||||
static __attribute__((aligned(sizeof(size_t)))) uint8_t decode_buffer[512] = {};
|
||||
static heatshrink_decoder hsd;
|
||||
static uint8_t decode_buffer[512] = {};
|
||||
#endif
|
||||
|
||||
inline bool bs_serial_data_available(const serial_index_t index) {
|
||||
return SERIAL_IMPL.available(index);
|
||||
}
|
||||
|
||||
inline int bs_read_serial(const serial_index_t index) {
|
||||
return SERIAL_IMPL.read(index);
|
||||
}
|
||||
|
||||
class SDFileTransferProtocol {
|
||||
private:
|
||||
struct Packet {
|
||||
struct [[gnu::packed]] Open {
|
||||
static bool validate(char* buffer, size_t length) {
|
||||
static bool validate(char *buffer, size_t length) {
|
||||
return (length > sizeof(Open) && buffer[length - 1] == '\0');
|
||||
}
|
||||
static Open& decode(char* buffer) {
|
||||
static Open& decode(char *buffer) {
|
||||
data = &buffer[2];
|
||||
return *reinterpret_cast<Open*>(buffer);
|
||||
}
|
||||
@@ -74,7 +59,7 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
static bool file_open(char* filename) {
|
||||
static bool file_open(char *filename) {
|
||||
if (!dummy_transfer) {
|
||||
card.mount();
|
||||
card.openFileWrite(filename);
|
||||
@@ -82,13 +67,11 @@ private:
|
||||
}
|
||||
transfer_active = true;
|
||||
data_waiting = 0;
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
heatshrink_decoder_reset(&hsd);
|
||||
#endif
|
||||
TERN_(BINARY_STREAM_COMPRESSION, heatshrink_decoder_reset(&hsd));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool file_write(char* buffer, const size_t length) {
|
||||
static bool file_write(char *buffer, const size_t length) {
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
if (compression) {
|
||||
size_t total_processed = 0, processed_count = 0;
|
||||
@@ -127,9 +110,7 @@ private:
|
||||
card.closefile();
|
||||
card.release();
|
||||
}
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
heatshrink_decoder_finish(&hsd);
|
||||
#endif
|
||||
TERN_(BINARY_STREAM_COMPRESSION, heatshrink_decoder_finish(&hsd));
|
||||
transfer_active = false;
|
||||
return true;
|
||||
}
|
||||
@@ -139,9 +120,7 @@ private:
|
||||
card.closefile();
|
||||
card.removeFile(card.filename);
|
||||
card.release();
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
heatshrink_decoder_finish(&hsd);
|
||||
#endif
|
||||
TERN_(BINARY_STREAM_COMPRESSION, heatshrink_decoder_finish(&hsd));
|
||||
}
|
||||
transfer_active = false;
|
||||
return;
|
||||
@@ -163,15 +142,15 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
static void process(uint8_t packet_type, char* buffer, const uint16_t length) {
|
||||
static void process(uint8_t packet_type, char *buffer, const uint16_t length) {
|
||||
transfer_timeout = millis() + TIMEOUT;
|
||||
switch (static_cast<FileTransfer>(packet_type)) {
|
||||
case FileTransfer::QUERY:
|
||||
SERIAL_ECHOPAIR("PFT:version:", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH);
|
||||
SERIAL_ECHOPGM("PFT:version:", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH);
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
SERIAL_ECHOLNPAIR(":compresion:heatshrink,", HEATSHRINK_STATIC_WINDOW_BITS, ",", HEATSHRINK_STATIC_LOOKAHEAD_BITS);
|
||||
SERIAL_ECHOLNPGM(":compression:heatshrink,", HEATSHRINK_STATIC_WINDOW_BITS, ",", HEATSHRINK_STATIC_LOOKAHEAD_BITS);
|
||||
#else
|
||||
SERIAL_ECHOLNPGM(":compresion:none");
|
||||
SERIAL_ECHOLNPGM(":compression:none");
|
||||
#endif
|
||||
break;
|
||||
case FileTransfer::OPEN:
|
||||
@@ -303,7 +282,7 @@ public:
|
||||
millis_t transfer_window = millis() + RX_TIMESLICE;
|
||||
|
||||
#if ENABLED(SDSUPPORT)
|
||||
PORT_REDIRECT(card.transfer_port_index);
|
||||
PORT_REDIRECT(SERIAL_PORTMASK(card.transfer_port_index));
|
||||
#endif
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
@@ -343,7 +322,7 @@ public:
|
||||
if (packet.header.checksum == packet.header_checksum) {
|
||||
// The SYNC control packet is a special case in that it doesn't require the stream sync to be correct
|
||||
if (static_cast<Protocol>(packet.header.protocol()) == Protocol::CONTROL && static_cast<ProtocolControl>(packet.header.type()) == ProtocolControl::SYNC) {
|
||||
SERIAL_ECHOLNPAIR("ss", sync, ",", buffer_size, ",", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH);
|
||||
SERIAL_ECHOLNPGM("ss", sync, ",", buffer_size, ",", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH);
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
break;
|
||||
}
|
||||
@@ -358,7 +337,7 @@ public:
|
||||
stream_state = StreamState::PACKET_PROCESS;
|
||||
}
|
||||
else if (packet.header.sync == sync - 1) { // ok response must have been lost
|
||||
SERIAL_ECHOLNPAIR("ok", packet.header.sync); // transmit valid packet received and drop the payload
|
||||
SERIAL_ECHOLNPGM("ok", packet.header.sync); // transmit valid packet received and drop the payload
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
}
|
||||
else if (packet_retries) {
|
||||
@@ -370,8 +349,7 @@ public:
|
||||
}
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOLNPAIR("Packet header(", packet.header.sync, "?) corrupt");
|
||||
SERIAL_ECHO_MSG("Packet header(", packet.header.sync, "?) corrupt");
|
||||
stream_state = StreamState::PACKET_RESEND;
|
||||
}
|
||||
}
|
||||
@@ -405,8 +383,7 @@ public:
|
||||
stream_state = StreamState::PACKET_PROCESS;
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOLNPAIR("Packet(", packet.header.sync, ") payload corrupt");
|
||||
SERIAL_ECHO_MSG("Packet(", packet.header.sync, ") payload corrupt");
|
||||
stream_state = StreamState::PACKET_RESEND;
|
||||
}
|
||||
}
|
||||
@@ -416,7 +393,7 @@ public:
|
||||
packet_retries = 0;
|
||||
bytes_received += packet.header.size;
|
||||
|
||||
SERIAL_ECHOLNPAIR("ok", packet.header.sync); // transmit valid packet received
|
||||
SERIAL_ECHOLNPGM("ok", packet.header.sync); // transmit valid packet received
|
||||
dispatch();
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
break;
|
||||
@@ -424,9 +401,8 @@ public:
|
||||
if (packet_retries < MAX_RETRIES || MAX_RETRIES == 0) {
|
||||
packet_retries++;
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOLNPAIR("Resend request ", int(packet_retries));
|
||||
SERIAL_ECHOLNPAIR("rs", sync);
|
||||
SERIAL_ECHO_MSG("Resend request ", packet_retries);
|
||||
SERIAL_ECHOLNPGM("rs", sync);
|
||||
}
|
||||
else
|
||||
stream_state = StreamState::PACKET_ERROR;
|
||||
@@ -436,7 +412,7 @@ public:
|
||||
stream_state = StreamState::PACKET_RESEND;
|
||||
break;
|
||||
case StreamState::PACKET_ERROR:
|
||||
SERIAL_ECHOLNPAIR("fe", packet.header.sync);
|
||||
SERIAL_ECHOLNPGM("fe", packet.header.sync);
|
||||
reset(); // reset everything, resync required
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
break;
|
||||
@@ -447,9 +423,9 @@ public:
|
||||
}
|
||||
|
||||
void dispatch() {
|
||||
switch(static_cast<Protocol>(packet.header.protocol())) {
|
||||
switch (static_cast<Protocol>(packet.header.protocol())) {
|
||||
case Protocol::CONTROL:
|
||||
switch(static_cast<ProtocolControl>(packet.header.type())) {
|
||||
switch (static_cast<ProtocolControl>(packet.header.type())) {
|
||||
case ProtocolControl::CLOSE: // revert back to ASCII mode
|
||||
card.flag.binary_mode = false;
|
||||
break;
|
||||
25
Marlin/src/feature/bltouch.cpp
Executable file → Normal file
25
Marlin/src/feature/bltouch.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,7 @@ BLTouch bltouch;
|
||||
bool BLTouch::last_written_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain
|
||||
|
||||
#include "../module/servo.h"
|
||||
#include "../module/probe.h"
|
||||
|
||||
void stop();
|
||||
|
||||
@@ -38,7 +39,7 @@ void stop();
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
bool BLTouch::command(const BLTCommand cmd, const millis_t &ms) {
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPAIR("BLTouch Command :", cmd);
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("BLTouch Command :", cmd);
|
||||
MOVE_SERVO(Z_PROBE_SERVO_NR, cmd);
|
||||
safe_delay(_MAX(ms, (uint32_t)BLTOUCH_DELAY)); // BLTOUCH_DELAY is also the *minimum* delay
|
||||
return triggered();
|
||||
@@ -63,7 +64,7 @@ void BLTouch::init(const bool set_voltage/*=false*/) {
|
||||
#else
|
||||
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
DEBUG_ECHOLNPAIR("last_written_mode - ", (int)last_written_mode);
|
||||
DEBUG_ECHOLNPGM("last_written_mode - ", last_written_mode);
|
||||
DEBUG_ECHOLNPGM("config mode - "
|
||||
#if ENABLED(BLTOUCH_SET_5V_MODE)
|
||||
"BLTOUCH_SET_5V_MODE"
|
||||
@@ -90,15 +91,7 @@ void BLTouch::clear() {
|
||||
_stow(); // STOW to be ready for meaningful work. Could fail, don't care
|
||||
}
|
||||
|
||||
bool BLTouch::triggered() {
|
||||
return (
|
||||
#if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
|
||||
READ(Z_MIN_PIN) != Z_MIN_ENDSTOP_INVERTING
|
||||
#else
|
||||
READ(Z_MIN_PROBE_PIN) != Z_MIN_PROBE_ENDSTOP_INVERTING
|
||||
#endif
|
||||
);
|
||||
}
|
||||
bool BLTouch::triggered() { return PROBE_TRIGGERED(); }
|
||||
|
||||
bool BLTouch::deploy_proc() {
|
||||
// Do a DEPLOY
|
||||
@@ -124,9 +117,7 @@ bool BLTouch::deploy_proc() {
|
||||
}
|
||||
|
||||
// One of the recommended ANTClabs ways to probe, using SW MODE
|
||||
#if ENABLED(BLTOUCH_FORCE_SW_MODE)
|
||||
_set_SW_mode();
|
||||
#endif
|
||||
TERN_(BLTOUCH_FORCE_SW_MODE, _set_SW_mode());
|
||||
|
||||
// Now the probe is ready to issue a 10ms pulse when the pin goes up.
|
||||
// The trigger STOW (see motion.cpp for example) will pull up the probes pin as soon as the pulse
|
||||
@@ -184,7 +175,7 @@ bool BLTouch::status_proc() {
|
||||
_set_SW_mode(); // Incidentally, _set_SW_mode() will also RESET any active alarm
|
||||
const bool tr = triggered(); // If triggered in SW mode, the pin is up, it is STOWED
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("BLTouch is ", (int)tr);
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch is ", tr);
|
||||
|
||||
if (tr) _stow(); else _deploy(); // Turn off SW mode, reset any trigger, honor pin state
|
||||
return !tr;
|
||||
@@ -196,7 +187,7 @@ void BLTouch::mode_conv_proc(const bool M5V) {
|
||||
* BLTOUCH V3.0: This will set the mode (twice) and sadly, a STOW is needed at the end, because of the deploy
|
||||
* BLTOUCH V3.1: This will set the mode and store it in the eeprom. The STOW is not needed but does not hurt
|
||||
*/
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("BLTouch Set Mode - ", (int)M5V);
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Set Mode - ", M5V);
|
||||
_deploy();
|
||||
if (M5V) _set_5V_mode(); else _set_OD_mode();
|
||||
_mode_store();
|
||||
|
||||
41
Marlin/src/feature/bltouch.h
Executable file → Normal file
41
Marlin/src/feature/bltouch.h
Executable file → Normal file
@@ -16,19 +16,24 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if DISABLED(BLTOUCH_HS_MODE)
|
||||
#define BLTOUCH_SLOW_MODE 1
|
||||
#endif
|
||||
|
||||
// BLTouch commands are sent as servo angles
|
||||
typedef unsigned char BLTCommand;
|
||||
|
||||
#define STOW_ALARM true
|
||||
#define BLTOUCH_DEPLOY 10
|
||||
#define BLTOUCH_SW_MODE 60
|
||||
#define BLTOUCH_STOW 90
|
||||
#define BLTOUCH_SW_MODE 60
|
||||
#define BLTOUCH_SELFTEST 120
|
||||
#define BLTOUCH_MODE_STORE 130
|
||||
#define BLTOUCH_5V_MODE 140
|
||||
@@ -69,33 +74,33 @@ public:
|
||||
static bool last_written_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain
|
||||
|
||||
// DEPLOY and STOW are wrapped for error handling - these are used by homing and by probing
|
||||
FORCE_INLINE static bool deploy() { return deploy_proc(); }
|
||||
FORCE_INLINE static bool stow() { return stow_proc(); }
|
||||
FORCE_INLINE static bool status() { return status_proc(); }
|
||||
static bool deploy() { return deploy_proc(); }
|
||||
static bool stow() { return stow_proc(); }
|
||||
static bool status() { return status_proc(); }
|
||||
|
||||
// Native BLTouch commands ("Underscore"...), used in lcd menus and internally
|
||||
FORCE_INLINE static void _reset() { command(BLTOUCH_RESET, BLTOUCH_RESET_DELAY); }
|
||||
static void _reset() { command(BLTOUCH_RESET, BLTOUCH_RESET_DELAY); }
|
||||
|
||||
FORCE_INLINE static void _selftest() { command(BLTOUCH_SELFTEST, BLTOUCH_DELAY); }
|
||||
static void _selftest() { command(BLTOUCH_SELFTEST, BLTOUCH_DELAY); }
|
||||
|
||||
FORCE_INLINE static void _set_SW_mode() { command(BLTOUCH_SW_MODE, BLTOUCH_DELAY); }
|
||||
FORCE_INLINE static void _reset_SW_mode() { if (triggered()) _stow(); else _deploy(); }
|
||||
static void _set_SW_mode() { command(BLTOUCH_SW_MODE, BLTOUCH_DELAY); }
|
||||
static void _reset_SW_mode() { if (triggered()) _stow(); else _deploy(); }
|
||||
|
||||
FORCE_INLINE static void _set_5V_mode() { command(BLTOUCH_5V_MODE, BLTOUCH_SET5V_DELAY); }
|
||||
FORCE_INLINE static void _set_OD_mode() { command(BLTOUCH_OD_MODE, BLTOUCH_SETOD_DELAY); }
|
||||
FORCE_INLINE static void _mode_store() { command(BLTOUCH_MODE_STORE, BLTOUCH_MODE_STORE_DELAY); }
|
||||
static void _set_5V_mode() { command(BLTOUCH_5V_MODE, BLTOUCH_SET5V_DELAY); }
|
||||
static void _set_OD_mode() { command(BLTOUCH_OD_MODE, BLTOUCH_SETOD_DELAY); }
|
||||
static void _mode_store() { command(BLTOUCH_MODE_STORE, BLTOUCH_MODE_STORE_DELAY); }
|
||||
|
||||
FORCE_INLINE static void _deploy() { command(BLTOUCH_DEPLOY, BLTOUCH_DEPLOY_DELAY); }
|
||||
FORCE_INLINE static void _stow() { command(BLTOUCH_STOW, BLTOUCH_STOW_DELAY); }
|
||||
static void _deploy() { command(BLTOUCH_DEPLOY, BLTOUCH_DEPLOY_DELAY); }
|
||||
static void _stow() { command(BLTOUCH_STOW, BLTOUCH_STOW_DELAY); }
|
||||
|
||||
FORCE_INLINE static void mode_conv_5V() { mode_conv_proc(true); }
|
||||
FORCE_INLINE static void mode_conv_OD() { mode_conv_proc(false); }
|
||||
static void mode_conv_5V() { mode_conv_proc(true); }
|
||||
static void mode_conv_OD() { mode_conv_proc(false); }
|
||||
|
||||
static bool triggered();
|
||||
|
||||
private:
|
||||
FORCE_INLINE static bool _deploy_query_alarm() { return command(BLTOUCH_DEPLOY, BLTOUCH_DEPLOY_DELAY); }
|
||||
FORCE_INLINE static bool _stow_query_alarm() { return command(BLTOUCH_STOW, BLTOUCH_STOW_DELAY); }
|
||||
static bool _deploy_query_alarm() { return command(BLTOUCH_DEPLOY, BLTOUCH_DEPLOY_DELAY); }
|
||||
static bool _stow_query_alarm() { return command(BLTOUCH_STOW, BLTOUCH_STOW_DELAY) == STOW_ALARM; }
|
||||
|
||||
static void clear();
|
||||
static bool command(const BLTCommand cmd, const millis_t &ms);
|
||||
|
||||
15
Marlin/src/feature/cancel_object.cpp
Executable file → Normal file
15
Marlin/src/feature/cancel_object.cpp
Executable file → Normal file
@@ -16,16 +16,17 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(CANCEL_OBJECTS)
|
||||
|
||||
#include "cancel_object.h"
|
||||
#include "../gcode/gcode.h"
|
||||
#include "../lcd/ultralcd.h"
|
||||
#include "../lcd/marlinui.h"
|
||||
|
||||
CancelObject cancelable;
|
||||
|
||||
@@ -43,9 +44,9 @@ void CancelObject::set_active_object(const int8_t obj) {
|
||||
else
|
||||
skipping = false;
|
||||
|
||||
#if HAS_DISPLAY
|
||||
#if BOTH(HAS_STATUS_MESSAGE, CANCEL_OBJECTS_REPORTING)
|
||||
if (active_object >= 0)
|
||||
ui.status_printf_P(0, PSTR(S_FMT " %i"), GET_TEXT(MSG_PRINTING_OBJECT), int(active_object + 1));
|
||||
ui.status_printf_P(0, PSTR(S_FMT " %i"), GET_TEXT(MSG_PRINTING_OBJECT), int(active_object));
|
||||
else
|
||||
ui.reset_status();
|
||||
#endif
|
||||
@@ -66,10 +67,8 @@ void CancelObject::uncancel_object(const int8_t obj) {
|
||||
}
|
||||
|
||||
void CancelObject::report() {
|
||||
if (active_object >= 0) {
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOLNPAIR("Active Object: ", int(active_object));
|
||||
}
|
||||
if (active_object >= 0)
|
||||
SERIAL_ECHO_MSG("Active Object: ", active_object);
|
||||
|
||||
if (canceled) {
|
||||
SERIAL_ECHO_START();
|
||||
|
||||
2
Marlin/src/feature/cancel_object.h
Executable file → Normal file
2
Marlin/src/feature/cancel_object.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
91
Marlin/src/feature/caselight.cpp
Executable file → Normal file
91
Marlin/src/feature/caselight.cpp
Executable file → Normal file
@@ -16,63 +16,60 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_CASE_LIGHT
|
||||
#if ENABLED(CASE_LIGHT_ENABLE)
|
||||
|
||||
uint8_t case_light_brightness = CASE_LIGHT_DEFAULT_BRIGHTNESS;
|
||||
bool case_light_on = CASE_LIGHT_DEFAULT_ON;
|
||||
#include "caselight.h"
|
||||
|
||||
#if ENABLED(CASE_LIGHT_USE_NEOPIXEL)
|
||||
CaseLight caselight;
|
||||
|
||||
#if CASELIGHT_USES_BRIGHTNESS && !defined(CASE_LIGHT_DEFAULT_BRIGHTNESS)
|
||||
#define CASE_LIGHT_DEFAULT_BRIGHTNESS 0 // For use on PWM pin as non-PWM just sets a default
|
||||
#endif
|
||||
|
||||
#if CASELIGHT_USES_BRIGHTNESS
|
||||
uint8_t CaseLight::brightness = CASE_LIGHT_DEFAULT_BRIGHTNESS;
|
||||
#endif
|
||||
|
||||
bool CaseLight::on = CASE_LIGHT_DEFAULT_ON;
|
||||
|
||||
#if CASE_LIGHT_IS_COLOR_LED
|
||||
#include "leds/leds.h"
|
||||
LEDColor case_light_color =
|
||||
#ifdef CASE_LIGHT_NEOPIXEL_COLOR
|
||||
CASE_LIGHT_NEOPIXEL_COLOR
|
||||
#else
|
||||
{ 255, 255, 255, 255 }
|
||||
#endif
|
||||
;
|
||||
constexpr uint8_t init_case_light[] = CASE_LIGHT_DEFAULT_COLOR;
|
||||
LEDColor CaseLight::color = { init_case_light[0], init_case_light[1], init_case_light[2] OPTARG(HAS_WHITE_LED, init_case_light[3]) };
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The following are needed because ARM chips ignore a "WRITE(CASE_LIGHT_PIN,x)" command to the pins that
|
||||
* are directly controlled by the PWM module. In order to turn them off the brightness level needs to be
|
||||
* set to off. Since we can't use the pwm register to save the last brightness level we need a variable
|
||||
* to save it.
|
||||
*/
|
||||
uint8_t case_light_brightness_sav; // saves brighness info so can restore when "M355 S1" received
|
||||
bool case_light_arg_flag; // flag to notify if S or P argument type
|
||||
void CaseLight::update(const bool sflag) {
|
||||
#if CASELIGHT_USES_BRIGHTNESS
|
||||
/**
|
||||
* The brightness_sav (and sflag) is needed because ARM chips ignore
|
||||
* a "WRITE(CASE_LIGHT_PIN,x)" command to the pins that are directly
|
||||
* controlled by the PWM module. In order to turn them off the brightness
|
||||
* level needs to be set to OFF. Since we can't use the PWM register to
|
||||
* save the last brightness level we need a variable to save it.
|
||||
*/
|
||||
static uint8_t brightness_sav; // Save brightness info for restore on "M355 S1"
|
||||
|
||||
#ifndef INVERT_CASE_LIGHT
|
||||
#define INVERT_CASE_LIGHT false
|
||||
#endif
|
||||
if (on || !sflag)
|
||||
brightness_sav = brightness; // Save brightness except for M355 S0
|
||||
if (sflag && on)
|
||||
brightness = brightness_sav; // Restore last brightness for M355 S1
|
||||
|
||||
void update_case_light() {
|
||||
|
||||
if (!(case_light_arg_flag && !case_light_on))
|
||||
case_light_brightness_sav = case_light_brightness; // save brightness except if this is an S0 argument
|
||||
if (case_light_arg_flag && case_light_on)
|
||||
case_light_brightness = case_light_brightness_sav; // restore last brightens if this is an S1 argument
|
||||
|
||||
#if ENABLED(CASE_LIGHT_USE_NEOPIXEL) || DISABLED(CASE_LIGHT_NO_BRIGHTNESS)
|
||||
const uint8_t i = case_light_on ? case_light_brightness : 0, n10ct = INVERT_CASE_LIGHT ? 255 - i : i;
|
||||
const uint8_t i = on ? brightness : 0, n10ct = ENABLED(INVERT_CASE_LIGHT) ? 255 - i : i;
|
||||
UNUSED(n10ct);
|
||||
#endif
|
||||
|
||||
#if ENABLED(CASE_LIGHT_USE_NEOPIXEL)
|
||||
#if CASE_LIGHT_IS_COLOR_LED
|
||||
leds.set_color(LEDColor(color.r, color.g, color.b OPTARG(HAS_WHITE_LED, color.w), n10ct));
|
||||
#else // !CASE_LIGHT_IS_COLOR_LED
|
||||
|
||||
leds.set_color(
|
||||
MakeLEDColor(case_light_color.r, case_light_color.g, case_light_color.b, case_light_color.w, n10ct),
|
||||
false
|
||||
);
|
||||
|
||||
#else // !CASE_LIGHT_USE_NEOPIXEL
|
||||
|
||||
#if DISABLED(CASE_LIGHT_NO_BRIGHTNESS)
|
||||
if (PWM_PIN(CASE_LIGHT_PIN))
|
||||
#if CASELIGHT_USES_BRIGHTNESS
|
||||
if (pin_is_pwm())
|
||||
analogWrite(pin_t(CASE_LIGHT_PIN), (
|
||||
#if CASE_LIGHT_MAX_PWM == 255
|
||||
n10ct
|
||||
@@ -83,11 +80,15 @@ void update_case_light() {
|
||||
else
|
||||
#endif
|
||||
{
|
||||
const bool s = case_light_on ? !INVERT_CASE_LIGHT : INVERT_CASE_LIGHT;
|
||||
const bool s = on ? TERN(INVERT_CASE_LIGHT, LOW, HIGH) : TERN(INVERT_CASE_LIGHT, HIGH, LOW);
|
||||
WRITE(CASE_LIGHT_PIN, s ? HIGH : LOW);
|
||||
}
|
||||
|
||||
#endif // !CASE_LIGHT_USE_NEOPIXEL
|
||||
#endif // !CASE_LIGHT_IS_COLOR_LED
|
||||
|
||||
#if ENABLED(CASE_LIGHT_USE_RGB_LED)
|
||||
if (leds.lights_on) leds.update(); else leds.set_off();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // HAS_CASE_LIGHT
|
||||
#endif // CASE_LIGHT_ENABLE
|
||||
|
||||
44
Marlin/src/feature/caselight.h
Executable file → Normal file
44
Marlin/src/feature/caselight.h
Executable file → Normal file
@@ -16,14 +16,46 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
extern uint8_t case_light_brightness;
|
||||
extern bool case_light_on;
|
||||
extern uint8_t case_light_brightness_sav; // saves brighness info when case_light_on is false
|
||||
extern bool case_light_arg_flag; // flag to notify if S or P argument type
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
void update_case_light();
|
||||
#if CASE_LIGHT_IS_COLOR_LED
|
||||
#include "leds/leds.h" // for LEDColor
|
||||
#endif
|
||||
|
||||
#if NONE(CASE_LIGHT_NO_BRIGHTNESS, CASE_LIGHT_IS_COLOR_LED) || ENABLED(CASE_LIGHT_USE_NEOPIXEL)
|
||||
#define CASELIGHT_USES_BRIGHTNESS 1
|
||||
#endif
|
||||
|
||||
class CaseLight {
|
||||
public:
|
||||
static bool on;
|
||||
#if ENABLED(CASELIGHT_USES_BRIGHTNESS)
|
||||
static uint8_t brightness;
|
||||
#endif
|
||||
|
||||
static bool pin_is_pwm() { return TERN0(NEED_CASE_LIGHT_PIN, PWM_PIN(CASE_LIGHT_PIN)); }
|
||||
static bool has_brightness() { return TERN0(CASELIGHT_USES_BRIGHTNESS, TERN(CASE_LIGHT_USE_NEOPIXEL, true, pin_is_pwm())); }
|
||||
|
||||
static void init() {
|
||||
#if NEED_CASE_LIGHT_PIN
|
||||
if (pin_is_pwm()) SET_PWM(CASE_LIGHT_PIN); else SET_OUTPUT(CASE_LIGHT_PIN);
|
||||
#endif
|
||||
update_brightness();
|
||||
}
|
||||
|
||||
static void update(const bool sflag);
|
||||
static inline void update_brightness() { update(false); }
|
||||
static inline void update_enabled() { update(true); }
|
||||
|
||||
#if ENABLED(CASE_LIGHT_IS_COLOR_LED)
|
||||
private:
|
||||
static LEDColor color;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern CaseLight caselight;
|
||||
|
||||
9
Marlin/src/feature/closedloop.cpp
Executable file → Normal file
9
Marlin/src/feature/closedloop.cpp
Executable file → Normal file
@@ -16,9 +16,10 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
|
||||
@@ -29,12 +30,14 @@
|
||||
|
||||
#include "closedloop.h"
|
||||
|
||||
void init_closedloop() {
|
||||
ClosedLoop closedloop;
|
||||
|
||||
void ClosedLoop::init() {
|
||||
OUT_WRITE(CLOSED_LOOP_ENABLE_PIN, LOW);
|
||||
SET_INPUT_PULLUP(CLOSED_LOOP_MOVE_COMPLETE_PIN);
|
||||
}
|
||||
|
||||
void set_closedloop(const byte val) {
|
||||
void ClosedLoop::set(const byte val) {
|
||||
OUT_WRITE(CLOSED_LOOP_ENABLE_PIN, val);
|
||||
}
|
||||
|
||||
|
||||
13
Marlin/src/feature/closedloop.h
Executable file → Normal file
13
Marlin/src/feature/closedloop.h
Executable file → Normal file
@@ -16,10 +16,17 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
void init_closedloop();
|
||||
void set_closedloop(const byte val);
|
||||
class ClosedLoop {
|
||||
public:
|
||||
static void init();
|
||||
static void set(const byte val);
|
||||
};
|
||||
|
||||
extern ClosedLoop closedloop;
|
||||
|
||||
#define CLOSED_LOOP_WAITING() (READ(CLOSED_LOOP_ENABLE_PIN) && !READ(CLOSED_LOOP_MOVE_COMPLETE_PIN))
|
||||
|
||||
49
Marlin/src/feature/controllerfan.cpp
Executable file → Normal file
49
Marlin/src/feature/controllerfan.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#if ENABLED(USE_CONTROLLER_FAN)
|
||||
|
||||
#include "controllerfan.h"
|
||||
#include "../module/stepper/indirection.h"
|
||||
#include "../module/stepper.h"
|
||||
#include "../module/temperature.h"
|
||||
|
||||
ControllerFan controllerFan;
|
||||
@@ -34,6 +34,8 @@ uint8_t ControllerFan::speed;
|
||||
|
||||
#if ENABLED(CONTROLLER_FAN_EDITABLE)
|
||||
controllerFan_settings_t ControllerFan::settings; // {0}
|
||||
#else
|
||||
const controllerFan_settings_t &ControllerFan::settings = controllerFan_defaults;
|
||||
#endif
|
||||
|
||||
void ControllerFan::setup() {
|
||||
@@ -52,46 +54,21 @@ void ControllerFan::update() {
|
||||
if (ELAPSED(ms, nextMotorCheck)) {
|
||||
nextMotorCheck = ms + 2500UL; // Not a time critical function, so only check every 2.5s
|
||||
|
||||
#define MOTOR_IS_ON(A,B) (A##_ENABLE_READ() == bool(B##_ENABLE_ON))
|
||||
#define _OR_ENABLED_E(N) || MOTOR_IS_ON(E##N,E)
|
||||
|
||||
const bool motor_on = MOTOR_IS_ON(Z,Z)
|
||||
#if HAS_Z2_ENABLE
|
||||
|| MOTOR_IS_ON(Z2,Z)
|
||||
#endif
|
||||
#if HAS_Z3_ENABLE
|
||||
|| MOTOR_IS_ON(Z3,Z)
|
||||
#endif
|
||||
#if HAS_Z4_ENABLE
|
||||
|| MOTOR_IS_ON(Z4,Z)
|
||||
#endif
|
||||
|| (DISABLED(CONTROLLER_FAN_USE_Z_ONLY) && (
|
||||
MOTOR_IS_ON(X,X) || MOTOR_IS_ON(Y,Y)
|
||||
#if HAS_X2_ENABLE
|
||||
|| MOTOR_IS_ON(X2,X)
|
||||
#endif
|
||||
#if HAS_Y2_ENABLE
|
||||
|| MOTOR_IS_ON(Y2,Y)
|
||||
#endif
|
||||
#if E_STEPPERS
|
||||
REPEAT(E_STEPPERS, _OR_ENABLED_E)
|
||||
#endif
|
||||
)
|
||||
)
|
||||
;
|
||||
|
||||
// If any of the drivers or the heated bed are enabled...
|
||||
if (motor_on
|
||||
#if HAS_HEATED_BED
|
||||
|| thermalManager.temp_bed.soft_pwm_amount > 0
|
||||
#endif
|
||||
// If any triggers for the controller fan are true...
|
||||
// - At least one stepper driver is enabled
|
||||
// - The heated bed is enabled
|
||||
// - TEMP_SENSOR_BOARD is reporting >= CONTROLLER_FAN_MIN_BOARD_TEMP
|
||||
const ena_mask_t axis_mask = TERN(CONTROLLER_FAN_USE_Z_ONLY, _BV(Z_AXIS), ~TERN0(CONTROLLER_FAN_IGNORE_Z, _BV(Z_AXIS)));
|
||||
if ( (stepper.axis_enabled.bits & axis_mask)
|
||||
|| TERN0(HAS_HEATED_BED, thermalManager.temp_bed.soft_pwm_amount > 0)
|
||||
|| TERN0(HAS_CONTROLLER_FAN_MIN_BOARD_TEMP, thermalManager.wholeDegBoard() >= CONTROLLER_FAN_MIN_BOARD_TEMP)
|
||||
) lastMotorOn = ms; //... set time to NOW so the fan will turn on
|
||||
|
||||
// Fan Settings. Set fan > 0:
|
||||
// - If AutoMode is on and steppers have been enabled for CONTROLLERFAN_IDLE_TIME seconds.
|
||||
// - If System is on idle and idle fan speed settings is activated.
|
||||
set_fan_speed(
|
||||
settings.auto_mode && lastMotorOn && PENDING(ms, lastMotorOn + settings.duration * 1000UL)
|
||||
settings.auto_mode && lastMotorOn && PENDING(ms, lastMotorOn + SEC_TO_MS(settings.duration))
|
||||
? settings.active_speed : settings.idle_speed
|
||||
);
|
||||
|
||||
|
||||
10
Marlin/src/feature/controllerfan.h
Executable file → Normal file
10
Marlin/src/feature/controllerfan.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -58,15 +58,11 @@ class ControllerFan {
|
||||
#if ENABLED(CONTROLLER_FAN_EDITABLE)
|
||||
static controllerFan_settings_t settings;
|
||||
#else
|
||||
static const controllerFan_settings_t constexpr &settings = controllerFan_defaults;
|
||||
static const controllerFan_settings_t &settings;
|
||||
#endif
|
||||
static inline bool state() { return speed > 0; }
|
||||
static inline void init() { reset(); }
|
||||
static inline void reset() {
|
||||
#if ENABLED(CONTROLLER_FAN_EDITABLE)
|
||||
settings = controllerFan_defaults;
|
||||
#endif
|
||||
}
|
||||
static inline void reset() { TERN_(CONTROLLER_FAN_EDITABLE, settings = controllerFan_defaults); }
|
||||
static void setup();
|
||||
static void update();
|
||||
};
|
||||
|
||||
48
Marlin/src/feature/cooler.cpp
Normal file
48
Marlin/src/feature/cooler.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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 "../inc/MarlinConfig.h"
|
||||
|
||||
#if EITHER(HAS_COOLER, LASER_COOLANT_FLOW_METER)
|
||||
|
||||
#include "cooler.h"
|
||||
Cooler cooler;
|
||||
|
||||
#if HAS_COOLER
|
||||
uint8_t Cooler::mode = 0;
|
||||
uint16_t Cooler::capacity;
|
||||
uint16_t Cooler::load;
|
||||
bool Cooler::enabled = false;
|
||||
#endif
|
||||
|
||||
#if ENABLED(LASER_COOLANT_FLOW_METER)
|
||||
bool Cooler::flowmeter = false;
|
||||
millis_t Cooler::flowmeter_next_ms; // = 0
|
||||
volatile uint16_t Cooler::flowpulses;
|
||||
float Cooler::flowrate;
|
||||
#if ENABLED(FLOWMETER_SAFETY)
|
||||
bool Cooler::flowsafety_enabled = true;
|
||||
bool Cooler::flowfault = false;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // HAS_COOLER || LASER_COOLANT_FLOW_METER
|
||||
109
Marlin/src/feature/cooler.h
Normal file
109
Marlin/src/feature/cooler.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#ifndef FLOWMETER_PPL
|
||||
#define FLOWMETER_PPL 5880 // Pulses per liter
|
||||
#endif
|
||||
#ifndef FLOWMETER_INTERVAL
|
||||
#define FLOWMETER_INTERVAL 1000 // milliseconds
|
||||
#endif
|
||||
|
||||
// Cooling device
|
||||
|
||||
class Cooler {
|
||||
public:
|
||||
static uint16_t capacity; // Cooling capacity in watts
|
||||
static uint16_t load; // Cooling load in watts
|
||||
|
||||
static bool enabled;
|
||||
static void enable() { enabled = true; }
|
||||
static void disable() { enabled = false; }
|
||||
static void toggle() { enabled = !enabled; }
|
||||
|
||||
static uint8_t mode; // 0 = CO2 Liquid cooling, 1 = Laser Diode TEC Heatsink Cooling
|
||||
static void set_mode(const uint8_t m) { mode = m; }
|
||||
|
||||
#if ENABLED(LASER_COOLANT_FLOW_METER)
|
||||
static float flowrate; // Flow meter reading in liters-per-minute.
|
||||
static bool flowmeter; // Flag to monitor the flow
|
||||
static volatile uint16_t flowpulses; // Flowmeter IRQ pulse count
|
||||
static millis_t flowmeter_next_ms; // Next time at which to calculate flow
|
||||
|
||||
static void set_flowmeter(const bool sflag) {
|
||||
if (flowmeter != sflag) {
|
||||
flowmeter = sflag;
|
||||
if (sflag) {
|
||||
flowpulses = 0;
|
||||
flowmeter_next_ms = millis() + FLOWMETER_INTERVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To calculate flow we only need to count pulses
|
||||
static void flowmeter_ISR() { flowpulses++; }
|
||||
|
||||
// Enable / Disable the flow meter interrupt
|
||||
static void flowmeter_interrupt_enable() {
|
||||
attachInterrupt(digitalPinToInterrupt(FLOWMETER_PIN), flowmeter_ISR, RISING);
|
||||
}
|
||||
static void flowmeter_interrupt_disable() {
|
||||
detachInterrupt(digitalPinToInterrupt(FLOWMETER_PIN));
|
||||
}
|
||||
|
||||
// Enable / Disable the flow meter interrupt
|
||||
static void flowmeter_enable() { set_flowmeter(true); flowpulses = 0; flowmeter_interrupt_enable(); }
|
||||
static void flowmeter_disable() { set_flowmeter(false); flowmeter_interrupt_disable(); flowpulses = 0; }
|
||||
|
||||
// Get the total flow (in liters per minute) since the last reading
|
||||
static void calc_flowrate() {
|
||||
// flowrate = (litres) * (seconds) = litres per minute
|
||||
flowrate = (flowpulses / (float)FLOWMETER_PPL) * ((1000.0f / (float)FLOWMETER_INTERVAL) * 60.0f);
|
||||
flowpulses = 0;
|
||||
}
|
||||
|
||||
// Userland task to update the flow meter
|
||||
static void flowmeter_task(const millis_t ms=millis()) {
|
||||
if (!flowmeter) // !! The flow meter must always be on !!
|
||||
flowmeter_enable(); // Init and prime
|
||||
if (ELAPSED(ms, flowmeter_next_ms)) {
|
||||
calc_flowrate();
|
||||
flowmeter_next_ms = ms + FLOWMETER_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(FLOWMETER_SAFETY)
|
||||
static bool flowfault; // Flag that the cooler is in a fault state
|
||||
static bool flowsafety_enabled; // Flag to disable the cutter if flow rate is too low
|
||||
static void flowsafety_toggle() { flowsafety_enabled = !flowsafety_enabled; }
|
||||
static bool check_flow_too_low() {
|
||||
const bool too_low = flowsafety_enabled && flowrate < (FLOWMETER_MIN_LITERS_PER_MINUTE);
|
||||
flowfault = too_low;
|
||||
return too_low;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Cooler cooler;
|
||||
50
Marlin/src/feature/dac/dac_dac084s085.cpp
Executable file → Normal file
50
Marlin/src/feature/dac/dac_dac084s085.cpp
Executable file → Normal file
@@ -20,35 +20,35 @@ void dac084s085::begin() {
|
||||
uint8_t externalDac_buf[] = { 0x20, 0x00 }; // all off
|
||||
|
||||
// All SPI chip-select HIGH
|
||||
SET_OUTPUT(DAC0_SYNC);
|
||||
#if EXTRUDERS > 1
|
||||
SET_OUTPUT(DAC1_SYNC);
|
||||
SET_OUTPUT(DAC0_SYNC_PIN);
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
SET_OUTPUT(DAC1_SYNC_PIN);
|
||||
#endif
|
||||
cshigh();
|
||||
spiBegin();
|
||||
|
||||
//init onboard DAC
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC, LOW);
|
||||
WRITE(DAC0_SYNC_PIN, LOW);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC, HIGH);
|
||||
WRITE(DAC0_SYNC_PIN, HIGH);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC, LOW);
|
||||
WRITE(DAC0_SYNC_PIN, LOW);
|
||||
|
||||
spiSend(SPI_CHAN_DAC, externalDac_buf, COUNT(externalDac_buf));
|
||||
WRITE(DAC0_SYNC, HIGH);
|
||||
WRITE(DAC0_SYNC_PIN, HIGH);
|
||||
|
||||
#if EXTRUDERS > 1
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
//init Piggy DAC
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC, LOW);
|
||||
WRITE(DAC1_SYNC_PIN, LOW);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC, HIGH);
|
||||
WRITE(DAC1_SYNC_PIN, HIGH);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC, LOW);
|
||||
WRITE(DAC1_SYNC_PIN, LOW);
|
||||
|
||||
spiSend(SPI_CHAN_DAC, externalDac_buf, COUNT(externalDac_buf));
|
||||
WRITE(DAC1_SYNC, HIGH);
|
||||
WRITE(DAC1_SYNC_PIN, HIGH);
|
||||
#endif
|
||||
|
||||
return;
|
||||
@@ -66,18 +66,18 @@ void dac084s085::setValue(const uint8_t channel, const uint8_t value) {
|
||||
cshigh();
|
||||
|
||||
if (channel > 3) { // DAC Piggy E1,E2,E3
|
||||
WRITE(DAC1_SYNC, LOW);
|
||||
WRITE(DAC1_SYNC_PIN, LOW);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC, HIGH);
|
||||
WRITE(DAC1_SYNC_PIN, HIGH);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC, LOW);
|
||||
WRITE(DAC1_SYNC_PIN, LOW);
|
||||
}
|
||||
else { // DAC onboard X,Y,Z,E0
|
||||
WRITE(DAC0_SYNC, LOW);
|
||||
WRITE(DAC0_SYNC_PIN, LOW);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC, HIGH);
|
||||
WRITE(DAC0_SYNC_PIN, HIGH);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC, LOW);
|
||||
WRITE(DAC0_SYNC_PIN, LOW);
|
||||
}
|
||||
|
||||
DELAY_US(2);
|
||||
@@ -85,14 +85,14 @@ void dac084s085::setValue(const uint8_t channel, const uint8_t value) {
|
||||
}
|
||||
|
||||
void dac084s085::cshigh() {
|
||||
WRITE(DAC0_SYNC, HIGH);
|
||||
#if EXTRUDERS > 1
|
||||
WRITE(DAC1_SYNC, HIGH);
|
||||
WRITE(DAC0_SYNC_PIN, HIGH);
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
WRITE(DAC1_SYNC_PIN, HIGH);
|
||||
#endif
|
||||
WRITE(SPI_EEPROM1_CS, HIGH);
|
||||
WRITE(SPI_EEPROM2_CS, HIGH);
|
||||
WRITE(SPI_FLASH_CS, HIGH);
|
||||
WRITE(SS_PIN, HIGH);
|
||||
WRITE(SPI_EEPROM1_CS_PIN, HIGH);
|
||||
WRITE(SPI_EEPROM2_CS_PIN, HIGH);
|
||||
WRITE(SPI_FLASH_CS_PIN, HIGH);
|
||||
WRITE(SD_SS_PIN, HIGH);
|
||||
}
|
||||
|
||||
#endif // MB(ALLIGATOR)
|
||||
|
||||
2
Marlin/src/feature/dac/dac_dac084s085.h
Executable file → Normal file
2
Marlin/src/feature/dac/dac_dac084s085.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
70
Marlin/src/feature/dac/dac_mcp4728.cpp
Executable file → Normal file
70
Marlin/src/feature/dac/dac_mcp4728.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -24,33 +24,35 @@
|
||||
* mcp4728.cpp - Arduino library for MicroChip MCP4728 I2C D/A converter
|
||||
*
|
||||
* For implementation details, please take a look at the datasheet:
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/22187a.pdf
|
||||
* https://ww1.microchip.com/downloads/en/DeviceDoc/22187a.pdf
|
||||
*
|
||||
* For discussion and feedback, please go to:
|
||||
* http://arduino.cc/forum/index.php/topic,51842.0.html
|
||||
* https://forum.arduino.cc/index.php/topic,51842.0.html
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(DAC_STEPPER_CURRENT)
|
||||
#if HAS_MOTOR_CURRENT_DAC
|
||||
|
||||
#include "dac_mcp4728.h"
|
||||
|
||||
xyze_uint_t mcp4728_values;
|
||||
MCP4728 mcp4728;
|
||||
|
||||
xyze_uint_t dac_values;
|
||||
|
||||
/**
|
||||
* Begin I2C, get current values (input register and eeprom) of mcp4728
|
||||
*/
|
||||
void mcp4728_init() {
|
||||
void MCP4728::init() {
|
||||
Wire.begin();
|
||||
Wire.requestFrom(I2C_ADDRESS(DAC_DEV_ADDRESS), 24);
|
||||
Wire.requestFrom(I2C_ADDRESS(DAC_DEV_ADDRESS), uint8_t(24));
|
||||
while (Wire.available()) {
|
||||
char deviceID = Wire.read(),
|
||||
hiByte = Wire.read(),
|
||||
loByte = Wire.read();
|
||||
|
||||
if (!(deviceID & 0x08))
|
||||
mcp4728_values[(deviceID & 0x30) >> 4] = word((hiByte & 0x0F), loByte);
|
||||
dac_values[(deviceID & 0x30) >> 4] = word((hiByte & 0x0F), loByte);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,38 +60,38 @@ void mcp4728_init() {
|
||||
* Write input resister value to specified channel using fastwrite method.
|
||||
* Channel : 0-3, Values : 0-4095
|
||||
*/
|
||||
uint8_t mcp4728_analogWrite(const uint8_t channel, const uint16_t value) {
|
||||
mcp4728_values[channel] = value;
|
||||
return mcp4728_fastWrite();
|
||||
uint8_t MCP4728::analogWrite(const uint8_t channel, const uint16_t value) {
|
||||
dac_values[channel] = value;
|
||||
return fastWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write all input resistor values to EEPROM using SequencialWrite method.
|
||||
* Write all input resistor values to EEPROM using SequentialWrite method.
|
||||
* This will update both input register and EEPROM value
|
||||
* This will also write current Vref, PowerDown, Gain settings to EEPROM
|
||||
*/
|
||||
uint8_t mcp4728_eepromWrite() {
|
||||
uint8_t MCP4728::eepromWrite() {
|
||||
Wire.beginTransmission(I2C_ADDRESS(DAC_DEV_ADDRESS));
|
||||
Wire.write(SEQWRITE);
|
||||
LOOP_XYZE(i) {
|
||||
Wire.write(DAC_STEPPER_VREF << 7 | DAC_STEPPER_GAIN << 4 | highByte(mcp4728_values[i]));
|
||||
Wire.write(lowByte(mcp4728_values[i]));
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
Wire.write(DAC_STEPPER_VREF << 7 | DAC_STEPPER_GAIN << 4 | highByte(dac_values[i]));
|
||||
Wire.write(lowByte(dac_values[i]));
|
||||
}
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Voltage reference setting to all input regiters
|
||||
* Write Voltage reference setting to all input registers
|
||||
*/
|
||||
uint8_t mcp4728_setVref_all(const uint8_t value) {
|
||||
uint8_t MCP4728::setVref_all(const uint8_t value) {
|
||||
Wire.beginTransmission(I2C_ADDRESS(DAC_DEV_ADDRESS));
|
||||
Wire.write(VREFWRITE | (value ? 0x0F : 0x00));
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
/**
|
||||
* Write Gain setting to all input regiters
|
||||
* Write Gain setting to all input registers
|
||||
*/
|
||||
uint8_t mcp4728_setGain_all(const uint8_t value) {
|
||||
uint8_t MCP4728::setGain_all(const uint8_t value) {
|
||||
Wire.beginTransmission(I2C_ADDRESS(DAC_DEV_ADDRESS));
|
||||
Wire.write(GAINWRITE | (value ? 0x0F : 0x00));
|
||||
return Wire.endTransmission();
|
||||
@@ -98,16 +100,16 @@ uint8_t mcp4728_setGain_all(const uint8_t value) {
|
||||
/**
|
||||
* Return Input Register value
|
||||
*/
|
||||
uint16_t mcp4728_getValue(const uint8_t channel) { return mcp4728_values[channel]; }
|
||||
uint16_t MCP4728::getValue(const uint8_t channel) { return dac_values[channel]; }
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Steph: Might be useful in the future
|
||||
* Return Vout
|
||||
*/
|
||||
uint16_t mcp4728_getVout(const uint8_t channel) {
|
||||
uint16_t MCP4728::getVout(const uint8_t channel) {
|
||||
const uint32_t vref = 2048,
|
||||
vOut = (vref * mcp4728_values[channel] * (_DAC_STEPPER_GAIN + 1)) / 4096;
|
||||
vOut = (vref * dac_values[channel] * (_DAC_STEPPER_GAIN + 1)) / 4096;
|
||||
return _MIN(vOut, defaultVDD);
|
||||
}
|
||||
#endif
|
||||
@@ -115,27 +117,27 @@ uint16_t mcp4728_getVout(const uint8_t channel) {
|
||||
/**
|
||||
* Returns DAC values as a 0-100 percentage of drive strength
|
||||
*/
|
||||
uint8_t mcp4728_getDrvPct(const uint8_t channel) { return uint8_t(100.0 * mcp4728_values[channel] / (DAC_STEPPER_MAX) + 0.5); }
|
||||
uint8_t MCP4728::getDrvPct(const uint8_t channel) { return uint8_t(100.0 * dac_values[channel] / (DAC_STEPPER_MAX) + 0.5); }
|
||||
|
||||
/**
|
||||
* Receives all Drive strengths as 0-100 percent values, updates
|
||||
* DAC Values array and calls fastwrite to update the DAC.
|
||||
*/
|
||||
void mcp4728_setDrvPct(xyze_uint8_t &pct) {
|
||||
mcp4728_values *= 0.01 * pct * (DAC_STEPPER_MAX);
|
||||
mcp4728_fastWrite();
|
||||
void MCP4728::setDrvPct(xyze_uint_t &pct) {
|
||||
dac_values = pct * (DAC_STEPPER_MAX) * 0.01f;
|
||||
fastWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* FastWrite input register values - All DAC ouput update. refer to DATASHEET 5.6.1
|
||||
* FastWrite input register values - All DAC output update. refer to DATASHEET 5.6.1
|
||||
* DAC Input and PowerDown bits update.
|
||||
* No EEPROM update
|
||||
*/
|
||||
uint8_t mcp4728_fastWrite() {
|
||||
uint8_t MCP4728::fastWrite() {
|
||||
Wire.beginTransmission(I2C_ADDRESS(DAC_DEV_ADDRESS));
|
||||
LOOP_XYZE(i) {
|
||||
Wire.write(highByte(mcp4728_values[i]));
|
||||
Wire.write(lowByte(mcp4728_values[i]));
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
Wire.write(highByte(dac_values[i]));
|
||||
Wire.write(lowByte(dac_values[i]));
|
||||
}
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
@@ -143,10 +145,10 @@ uint8_t mcp4728_fastWrite() {
|
||||
/**
|
||||
* Common function for simple general commands
|
||||
*/
|
||||
uint8_t mcp4728_simpleCommand(const byte simpleCommand) {
|
||||
uint8_t MCP4728::simpleCommand(const byte simpleCommand) {
|
||||
Wire.beginTransmission(I2C_ADDRESS(GENERALCALL));
|
||||
Wire.write(simpleCommand);
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
|
||||
#endif // DAC_STEPPER_CURRENT
|
||||
#endif // HAS_MOTOR_CURRENT_DAC
|
||||
|
||||
29
Marlin/src/feature/dac/dac_mcp4728.h
Executable file → Normal file
29
Marlin/src/feature/dac/dac_mcp4728.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -40,7 +40,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef lowByte
|
||||
#define lowByte(w) ((uint8_t) ((w) & 0xff))
|
||||
#define lowByte(w) ((uint8_t) ((w) & 0xFF))
|
||||
#endif
|
||||
|
||||
#ifndef highByte
|
||||
@@ -65,13 +65,18 @@
|
||||
// DAC_OR_ADDRESS defined in pins_BOARD.h file
|
||||
#define DAC_DEV_ADDRESS (BASE_ADDR | DAC_OR_ADDRESS)
|
||||
|
||||
void mcp4728_init();
|
||||
uint8_t mcp4728_analogWrite(const uint8_t channel, const uint16_t value);
|
||||
uint8_t mcp4728_eepromWrite();
|
||||
uint8_t mcp4728_setVref_all(const uint8_t value);
|
||||
uint8_t mcp4728_setGain_all(const uint8_t value);
|
||||
uint16_t mcp4728_getValue(const uint8_t channel);
|
||||
uint8_t mcp4728_fastWrite();
|
||||
uint8_t mcp4728_simpleCommand(const byte simpleCommand);
|
||||
uint8_t mcp4728_getDrvPct(const uint8_t channel);
|
||||
void mcp4728_setDrvPct(xyze_uint8_t &pct);
|
||||
class MCP4728 {
|
||||
public:
|
||||
static void init();
|
||||
static uint8_t analogWrite(const uint8_t channel, const uint16_t value);
|
||||
static uint8_t eepromWrite();
|
||||
static uint8_t setVref_all(const uint8_t value);
|
||||
static uint8_t setGain_all(const uint8_t value);
|
||||
static uint16_t getValue(const uint8_t channel);
|
||||
static uint8_t fastWrite();
|
||||
static uint8_t simpleCommand(const byte simpleCommand);
|
||||
static uint8_t getDrvPct(const uint8_t channel);
|
||||
static void setDrvPct(xyze_uint_t &pct);
|
||||
};
|
||||
|
||||
extern MCP4728 mcp4728;
|
||||
|
||||
83
Marlin/src/feature/dac/stepper_dac.cpp
Executable file → Normal file
83
Marlin/src/feature/dac/stepper_dac.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -26,79 +26,80 @@
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(DAC_STEPPER_CURRENT)
|
||||
#if HAS_MOTOR_CURRENT_DAC
|
||||
|
||||
#include "stepper_dac.h"
|
||||
#include "../../MarlinCore.h" // for SP_X_LBL...
|
||||
|
||||
bool dac_present = false;
|
||||
constexpr xyze_uint8_t dac_order = DAC_STEPPER_ORDER;
|
||||
xyze_uint8_t dac_channel_pct = DAC_MOTOR_CURRENT_DEFAULT;
|
||||
xyze_uint_t dac_channel_pct = DAC_MOTOR_CURRENT_DEFAULT;
|
||||
|
||||
int dac_init() {
|
||||
StepperDAC stepper_dac;
|
||||
|
||||
int StepperDAC::init() {
|
||||
#if PIN_EXISTS(DAC_DISABLE)
|
||||
OUT_WRITE(DAC_DISABLE_PIN, LOW); // set pin low to enable DAC
|
||||
#endif
|
||||
|
||||
mcp4728_init();
|
||||
mcp4728.init();
|
||||
|
||||
if (mcp4728_simpleCommand(RESET)) return -1;
|
||||
if (mcp4728.simpleCommand(RESET)) return -1;
|
||||
|
||||
dac_present = true;
|
||||
|
||||
mcp4728_setVref_all(DAC_STEPPER_VREF);
|
||||
mcp4728_setGain_all(DAC_STEPPER_GAIN);
|
||||
mcp4728.setVref_all(DAC_STEPPER_VREF);
|
||||
mcp4728.setGain_all(DAC_STEPPER_GAIN);
|
||||
|
||||
if (mcp4728_getDrvPct(0) < 1 || mcp4728_getDrvPct(1) < 1 || mcp4728_getDrvPct(2) < 1 || mcp4728_getDrvPct(3) < 1 ) {
|
||||
mcp4728_setDrvPct(dac_channel_pct);
|
||||
mcp4728_eepromWrite();
|
||||
if (mcp4728.getDrvPct(0) < 1 || mcp4728.getDrvPct(1) < 1 || mcp4728.getDrvPct(2) < 1 || mcp4728.getDrvPct(3) < 1) {
|
||||
mcp4728.setDrvPct(dac_channel_pct);
|
||||
mcp4728.eepromWrite();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dac_current_percent(uint8_t channel, float val) {
|
||||
if (!dac_present) return;
|
||||
|
||||
NOMORE(val, 100);
|
||||
|
||||
mcp4728_analogWrite(dac_order[channel], val * 0.01 * (DAC_STEPPER_MAX));
|
||||
mcp4728_simpleCommand(UPDATE);
|
||||
}
|
||||
|
||||
void dac_current_raw(uint8_t channel, uint16_t val) {
|
||||
void StepperDAC::set_current_value(const uint8_t channel, uint16_t val) {
|
||||
if (!dac_present) return;
|
||||
|
||||
NOMORE(val, uint16_t(DAC_STEPPER_MAX));
|
||||
|
||||
mcp4728_analogWrite(dac_order[channel], val);
|
||||
mcp4728_simpleCommand(UPDATE);
|
||||
mcp4728.analogWrite(dac_order[channel], val);
|
||||
mcp4728.simpleCommand(UPDATE);
|
||||
}
|
||||
|
||||
static float dac_perc(int8_t n) { return 100.0 * mcp4728_getValue(dac_order[n]) * RECIPROCAL(DAC_STEPPER_MAX); }
|
||||
static float dac_amps(int8_t n) { return mcp4728_getDrvPct(dac_order[n]) * (DAC_STEPPER_MAX) * 0.125 * RECIPROCAL(DAC_STEPPER_SENSE); }
|
||||
|
||||
uint8_t dac_current_get_percent(const AxisEnum axis) { return mcp4728_getDrvPct(dac_order[axis]); }
|
||||
void dac_current_set_percents(xyze_uint8_t &pct) {
|
||||
LOOP_XYZE(i) dac_channel_pct[i] = pct[dac_order[i]];
|
||||
mcp4728_setDrvPct(dac_channel_pct);
|
||||
void StepperDAC::set_current_percent(const uint8_t channel, float val) {
|
||||
set_current_value(channel, _MIN(val, 100.0f) * (DAC_STEPPER_MAX) / 100.0f);
|
||||
}
|
||||
|
||||
void dac_print_values() {
|
||||
static float dac_perc(int8_t n) { return mcp4728.getDrvPct(dac_order[n]); }
|
||||
static float dac_amps(int8_t n) { return mcp4728.getValue(dac_order[n]) * 0.125 * RECIPROCAL(DAC_STEPPER_SENSE * 1000); }
|
||||
|
||||
uint8_t StepperDAC::get_current_percent(const AxisEnum axis) { return mcp4728.getDrvPct(dac_order[axis]); }
|
||||
void StepperDAC::set_current_percents(xyze_uint8_t &pct) {
|
||||
LOOP_LOGICAL_AXES(i) dac_channel_pct[i] = pct[dac_order[i]];
|
||||
mcp4728.setDrvPct(dac_channel_pct);
|
||||
}
|
||||
|
||||
void StepperDAC::print_values() {
|
||||
if (!dac_present) return;
|
||||
|
||||
SERIAL_ECHO_MSG("Stepper current values in % (Amps):");
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOLNPAIR_P(
|
||||
SP_X_LBL, dac_perc(X_AXIS), PSTR(" ("), dac_amps(X_AXIS), PSTR(")")
|
||||
SP_Y_LBL, dac_perc(Y_AXIS), PSTR(" ("), dac_amps(Y_AXIS), PSTR(")")
|
||||
SP_Z_LBL, dac_perc(Z_AXIS), PSTR(" ("), dac_amps(Z_AXIS), PSTR(")")
|
||||
SP_E_LBL, dac_perc(E_AXIS), PSTR(" ("), dac_amps(E_AXIS), PSTR(")")
|
||||
);
|
||||
SERIAL_ECHOPGM_P(SP_X_LBL, dac_perc(X_AXIS), PSTR(" ("), dac_amps(X_AXIS), PSTR(")"));
|
||||
#if HAS_Y_AXIS
|
||||
SERIAL_ECHOPGM_P(SP_Y_LBL, dac_perc(Y_AXIS), PSTR(" ("), dac_amps(Y_AXIS), PSTR(")"));
|
||||
#endif
|
||||
#if HAS_Z_AXIS
|
||||
SERIAL_ECHOPGM_P(SP_Z_LBL, dac_perc(Z_AXIS), PSTR(" ("), dac_amps(Z_AXIS), PSTR(")"));
|
||||
#endif
|
||||
#if HAS_EXTRUDERS
|
||||
SERIAL_ECHOLNPGM_P(SP_E_LBL, dac_perc(E_AXIS), PSTR(" ("), dac_amps(E_AXIS), PSTR(")"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void dac_commit_eeprom() {
|
||||
void StepperDAC::commit_eeprom() {
|
||||
if (!dac_present) return;
|
||||
mcp4728_eepromWrite();
|
||||
mcp4728.eepromWrite();
|
||||
}
|
||||
|
||||
#endif // DAC_STEPPER_CURRENT
|
||||
#endif // HAS_MOTOR_CURRENT_DAC
|
||||
|
||||
21
Marlin/src/feature/dac/stepper_dac.h
Executable file → Normal file
21
Marlin/src/feature/dac/stepper_dac.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -27,10 +27,15 @@
|
||||
|
||||
#include "dac_mcp4728.h"
|
||||
|
||||
int dac_init();
|
||||
void dac_current_percent(uint8_t channel, float val);
|
||||
void dac_current_raw(uint8_t channel, uint16_t val);
|
||||
void dac_print_values();
|
||||
void dac_commit_eeprom();
|
||||
uint8_t dac_current_get_percent(AxisEnum axis);
|
||||
void dac_current_set_percents(xyze_uint8_t &pct);
|
||||
class StepperDAC {
|
||||
public:
|
||||
static int init();
|
||||
static void set_current_percent(const uint8_t channel, float val);
|
||||
static void set_current_value(const uint8_t channel, uint16_t val);
|
||||
static void print_values();
|
||||
static void commit_eeprom();
|
||||
static uint8_t get_current_percent(const AxisEnum axis);
|
||||
static void set_current_percents(xyze_uint8_t &pct);
|
||||
};
|
||||
|
||||
extern StepperDAC stepper_dac;
|
||||
|
||||
14
Marlin/src/feature/digipot/digipot.h
Executable file → Normal file
14
Marlin/src/feature/digipot/digipot.h
Executable file → Normal file
@@ -16,10 +16,18 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
void digipot_i2c_set_current(const uint8_t channel, const float current);
|
||||
void digipot_i2c_init();
|
||||
//
|
||||
// Header for MCP4018 and MCP4451 current control i2c devices
|
||||
//
|
||||
class DigipotI2C {
|
||||
public:
|
||||
static void init();
|
||||
static void set_current(const uint8_t channel, const float current);
|
||||
};
|
||||
|
||||
extern DigipotI2C digipot_i2c;
|
||||
|
||||
85
Marlin/src/feature/digipot/digipot_mcp4018.cpp
Executable file → Normal file
85
Marlin/src/feature/digipot/digipot_mcp4018.cpp
Executable file → Normal file
@@ -16,67 +16,61 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if BOTH(DIGIPOT_I2C, DIGIPOT_MCP4018)
|
||||
#if ENABLED(DIGIPOT_MCP4018)
|
||||
|
||||
#include "Stream.h"
|
||||
#include "utility/twi.h"
|
||||
#include <SlowSoftI2CMaster.h> //https://github.com/stawel/SlowSoftI2CMaster
|
||||
#include "digipot.h"
|
||||
|
||||
#include <Stream.h>
|
||||
#include <SlowSoftI2CMaster.h> // https://github.com/felias-fogg/SlowSoftI2CMaster
|
||||
|
||||
// Settings for the I2C based DIGIPOT (MCP4018) based on WT150
|
||||
|
||||
#define DIGIPOT_A4988_Rsx 0.250
|
||||
#define DIGIPOT_A4988_Vrefmax 1.666
|
||||
#define DIGIPOT_A4988_MAX_VALUE 127
|
||||
#define DIGIPOT_MCP4018_MAX_VALUE 127
|
||||
|
||||
#define DIGIPOT_A4988_Itripmax(Vref) ((Vref)/(8.0*DIGIPOT_A4988_Rsx))
|
||||
#define DIGIPOT_A4988_Itripmax(Vref) ((Vref) / (8.0 * DIGIPOT_A4988_Rsx))
|
||||
|
||||
#define DIGIPOT_A4988_FACTOR ((DIGIPOT_A4988_MAX_VALUE)/DIGIPOT_A4988_Itripmax(DIGIPOT_A4988_Vrefmax))
|
||||
#define DIGIPOT_A4988_FACTOR ((DIGIPOT_MCP4018_MAX_VALUE) / DIGIPOT_A4988_Itripmax(DIGIPOT_A4988_Vrefmax))
|
||||
#define DIGIPOT_A4988_MAX_CURRENT 2.0
|
||||
|
||||
static byte current_to_wiper(const float current) {
|
||||
const int16_t value = ceil(float(DIGIPOT_A4988_FACTOR) * current);
|
||||
return byte(constrain(value, 0, DIGIPOT_A4988_MAX_VALUE));
|
||||
const int16_t value = TERN(DIGIPOT_USE_RAW_VALUES, current, CEIL(current * DIGIPOT_A4988_FACTOR));
|
||||
return byte(constrain(value, 0, DIGIPOT_MCP4018_MAX_VALUE));
|
||||
}
|
||||
|
||||
const uint8_t sda_pins[DIGIPOT_I2C_NUM_CHANNELS] = {
|
||||
DIGIPOTS_I2C_SDA_X
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 1
|
||||
, DIGIPOTS_I2C_SDA_Y
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 2
|
||||
, DIGIPOTS_I2C_SDA_Z
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 3
|
||||
, DIGIPOTS_I2C_SDA_E0
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 4
|
||||
, DIGIPOTS_I2C_SDA_E1
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
static SlowSoftI2CMaster pots[DIGIPOT_I2C_NUM_CHANNELS] = {
|
||||
SlowSoftI2CMaster { sda_pins[X_AXIS], DIGIPOTS_I2C_SCL }
|
||||
SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_X, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 1
|
||||
, SlowSoftI2CMaster { sda_pins[Y_AXIS], DIGIPOTS_I2C_SCL }
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_Y, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 2
|
||||
, SlowSoftI2CMaster { sda_pins[Z_AXIS], DIGIPOTS_I2C_SCL }
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_Z, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 3
|
||||
, SlowSoftI2CMaster { sda_pins[E_AXIS], DIGIPOTS_I2C_SCL }
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E0, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 4
|
||||
, SlowSoftI2CMaster { sda_pins[E_AXIS + 1], DIGIPOTS_I2C_SCL }
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E1, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 5
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E2, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 6
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E3, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 7
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E4, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
static void i2c_send(const uint8_t channel, const byte v) {
|
||||
static void digipot_i2c_send(const uint8_t channel, const byte v) {
|
||||
if (WITHIN(channel, 0, DIGIPOT_I2C_NUM_CHANNELS - 1)) {
|
||||
pots[channel].i2c_start(((DIGIPOT_I2C_ADDRESS_A) << 1) | I2C_WRITE);
|
||||
pots[channel].i2c_write(v);
|
||||
@@ -85,19 +79,26 @@ static void i2c_send(const uint8_t channel, const byte v) {
|
||||
}
|
||||
|
||||
// This is for the MCP4018 I2C based digipot
|
||||
void digipot_i2c_set_current(const uint8_t channel, const float current) {
|
||||
i2c_send(channel, current_to_wiper(_MIN(_MAX(current, 0), float(DIGIPOT_A4988_MAX_CURRENT))));
|
||||
void DigipotI2C::set_current(const uint8_t channel, const float current) {
|
||||
const float ival = _MIN(_MAX(current, 0), float(DIGIPOT_MCP4018_MAX_VALUE));
|
||||
digipot_i2c_send(channel, current_to_wiper(ival));
|
||||
}
|
||||
|
||||
void digipot_i2c_init() {
|
||||
static const float digipot_motor_current[] PROGMEM = DIGIPOT_I2C_MOTOR_CURRENTS;
|
||||
void DigipotI2C::init() {
|
||||
LOOP_L_N(i, DIGIPOT_I2C_NUM_CHANNELS) pots[i].i2c_init();
|
||||
|
||||
LOOP_L_N(i, DIGIPOT_I2C_NUM_CHANNELS)
|
||||
pots[i].i2c_init();
|
||||
|
||||
// setup initial currents as defined in Configuration_adv.h
|
||||
// Init currents according to Configuration_adv.h
|
||||
static const float digipot_motor_current[] PROGMEM =
|
||||
#if ENABLED(DIGIPOT_USE_RAW_VALUES)
|
||||
DIGIPOT_MOTOR_CURRENT
|
||||
#else
|
||||
DIGIPOT_I2C_MOTOR_CURRENTS
|
||||
#endif
|
||||
;
|
||||
LOOP_L_N(i, COUNT(digipot_motor_current))
|
||||
digipot_i2c_set_current(i, pgm_read_float(&digipot_motor_current[i]));
|
||||
set_current(i, pgm_read_float(&digipot_motor_current[i]));
|
||||
}
|
||||
|
||||
#endif // DIGIPOT_I2C && DIGIPOT_MCP4018
|
||||
DigipotI2C digipot_i2c;
|
||||
|
||||
#endif // DIGIPOT_MCP4018
|
||||
|
||||
51
Marlin/src/feature/digipot/digipot_mcp4451.cpp
Executable file → Normal file
51
Marlin/src/feature/digipot/digipot_mcp4451.cpp
Executable file → Normal file
@@ -16,15 +16,17 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(DIGIPOT_I2C) && DISABLED(DIGIPOT_MCP4018)
|
||||
#if ENABLED(DIGIPOT_MCP4451)
|
||||
|
||||
#include "Stream.h"
|
||||
#include "digipot.h"
|
||||
|
||||
#include <Stream.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#if MB(MKS_SBASE)
|
||||
@@ -33,18 +35,21 @@
|
||||
|
||||
// Settings for the I2C based DIGIPOT (MCP4451) on Azteeg X3 Pro
|
||||
#if MB(5DPRINT)
|
||||
#define DIGIPOT_I2C_FACTOR 117.96
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 1.736
|
||||
#define DIGIPOT_I2C_FACTOR 117.96f
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 1.736f
|
||||
#elif MB(AZTEEG_X5_MINI, AZTEEG_X5_MINI_WIFI)
|
||||
#define DIGIPOT_I2C_FACTOR 113.5
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 2.0
|
||||
#define DIGIPOT_I2C_FACTOR 113.5f
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 2.0f
|
||||
#elif MB(AZTEEG_X5_GT)
|
||||
#define DIGIPOT_I2C_FACTOR 51.0f
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 3.0f
|
||||
#else
|
||||
#define DIGIPOT_I2C_FACTOR 106.7
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 2.5
|
||||
#define DIGIPOT_I2C_FACTOR 106.7f
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 2.5f
|
||||
#endif
|
||||
|
||||
static byte current_to_wiper(const float current) {
|
||||
return byte(CEIL(float((DIGIPOT_I2C_FACTOR * current))));
|
||||
return byte(TERN(DIGIPOT_USE_RAW_VALUES, current, CEIL(DIGIPOT_I2C_FACTOR * current)));
|
||||
}
|
||||
|
||||
static void digipot_i2c_send(const byte addr, const byte a, const byte b) {
|
||||
@@ -61,9 +66,9 @@ static void digipot_i2c_send(const byte addr, const byte a, const byte b) {
|
||||
}
|
||||
|
||||
// This is for the MCP4451 I2C based digipot
|
||||
void digipot_i2c_set_current(const uint8_t channel, const float current) {
|
||||
// these addresses are specific to Azteeg X3 Pro, can be set to others,
|
||||
// In this case first digipot is at address A0=0, A1= 0, second one is at A0=0, A1= 1
|
||||
void DigipotI2C::set_current(const uint8_t channel, const float current) {
|
||||
// These addresses are specific to Azteeg X3 Pro, can be set to others.
|
||||
// In this case first digipot is at address A0=0, A1=0, second one is at A0=0, A1=1
|
||||
const byte addr = channel < 4 ? DIGIPOT_I2C_ADDRESS_A : DIGIPOT_I2C_ADDRESS_B; // channel 0-3 vs 4-7
|
||||
|
||||
// Initial setup
|
||||
@@ -75,16 +80,24 @@ void digipot_i2c_set_current(const uint8_t channel, const float current) {
|
||||
digipot_i2c_send(addr, addresses[channel & 0x3], current_to_wiper(_MIN(float(_MAX(current, 0)), DIGIPOT_I2C_MAX_CURRENT)));
|
||||
}
|
||||
|
||||
void digipot_i2c_init() {
|
||||
void DigipotI2C::init() {
|
||||
#if MB(MKS_SBASE)
|
||||
configure_i2c(16); // Setting clock_option to 16 ensure the I2C bus is initialized at 400kHz
|
||||
configure_i2c(16); // Set clock_option to 16 ensure I2C is initialized at 400kHz
|
||||
#else
|
||||
Wire.begin();
|
||||
#endif
|
||||
// setup initial currents as defined in Configuration_adv.h
|
||||
static const float digipot_motor_current[] PROGMEM = DIGIPOT_I2C_MOTOR_CURRENTS;
|
||||
// Set up initial currents as defined in Configuration_adv.h
|
||||
static const float digipot_motor_current[] PROGMEM =
|
||||
#if ENABLED(DIGIPOT_USE_RAW_VALUES)
|
||||
DIGIPOT_MOTOR_CURRENT
|
||||
#else
|
||||
DIGIPOT_I2C_MOTOR_CURRENTS
|
||||
#endif
|
||||
;
|
||||
LOOP_L_N(i, COUNT(digipot_motor_current))
|
||||
digipot_i2c_set_current(i, pgm_read_float(&digipot_motor_current[i]));
|
||||
set_current(i, pgm_read_float(&digipot_motor_current[i]));
|
||||
}
|
||||
|
||||
#endif // DIGIPOT_I2C
|
||||
DigipotI2C digipot_i2c;
|
||||
|
||||
#endif // DIGIPOT_MCP4451
|
||||
|
||||
263
Marlin/src/feature/direct_stepping.cpp
Normal file
263
Marlin/src/feature/direct_stepping.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(DIRECT_STEPPING)
|
||||
|
||||
#include "direct_stepping.h"
|
||||
|
||||
#include "../MarlinCore.h"
|
||||
|
||||
#define CHECK_PAGE(I, R) do{ \
|
||||
if (I >= sizeof(page_states) / sizeof(page_states[0])) { \
|
||||
fatal_error = true; \
|
||||
return R; \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
#define CHECK_PAGE_STATE(I, R, S) do { \
|
||||
CHECK_PAGE(I, R); \
|
||||
if (page_states[I] != S) { \
|
||||
fatal_error = true; \
|
||||
return R; \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
namespace DirectStepping {
|
||||
|
||||
template<typename Cfg>
|
||||
State SerialPageManager<Cfg>::state;
|
||||
|
||||
template<typename Cfg>
|
||||
volatile bool SerialPageManager<Cfg>::fatal_error;
|
||||
|
||||
template<typename Cfg>
|
||||
volatile PageState SerialPageManager<Cfg>::page_states[Cfg::NUM_PAGES];
|
||||
|
||||
template<typename Cfg>
|
||||
volatile bool SerialPageManager<Cfg>::page_states_dirty;
|
||||
|
||||
template<typename Cfg>
|
||||
uint8_t SerialPageManager<Cfg>::pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE];
|
||||
|
||||
template<typename Cfg>
|
||||
uint8_t SerialPageManager<Cfg>::checksum;
|
||||
|
||||
template<typename Cfg>
|
||||
typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_byte_idx;
|
||||
|
||||
template<typename Cfg>
|
||||
typename Cfg::page_idx_t SerialPageManager<Cfg>::write_page_idx;
|
||||
|
||||
template<typename Cfg>
|
||||
typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_page_size;
|
||||
|
||||
template <typename Cfg>
|
||||
void SerialPageManager<Cfg>::init() {
|
||||
for (int i = 0 ; i < Cfg::NUM_PAGES ; i++)
|
||||
page_states[i] = PageState::FREE;
|
||||
|
||||
fatal_error = false;
|
||||
state = State::NEWLINE;
|
||||
|
||||
page_states_dirty = false;
|
||||
|
||||
SERIAL_ECHOLNPGM("pages_ready");
|
||||
}
|
||||
|
||||
template<typename Cfg>
|
||||
FORCE_INLINE bool SerialPageManager<Cfg>::maybe_store_rxd_char(uint8_t c) {
|
||||
switch (state) {
|
||||
default:
|
||||
case State::MONITOR:
|
||||
switch (c) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
state = State::NEWLINE;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case State::NEWLINE:
|
||||
switch (c) {
|
||||
case Cfg::CONTROL_CHAR:
|
||||
state = State::ADDRESS;
|
||||
return true;
|
||||
case '\n':
|
||||
case '\r':
|
||||
state = State::NEWLINE;
|
||||
return false;
|
||||
default:
|
||||
state = State::MONITOR;
|
||||
return false;
|
||||
}
|
||||
case State::ADDRESS:
|
||||
//TODO: 16 bit address, State::ADDRESS2
|
||||
write_page_idx = c;
|
||||
write_byte_idx = 0;
|
||||
checksum = 0;
|
||||
|
||||
CHECK_PAGE(write_page_idx, true);
|
||||
|
||||
if (page_states[write_page_idx] == PageState::FAIL) {
|
||||
// Special case for fail
|
||||
state = State::UNFAIL;
|
||||
return true;
|
||||
}
|
||||
|
||||
set_page_state(write_page_idx, PageState::WRITING);
|
||||
|
||||
state = Cfg::DIRECTIONAL ? State::COLLECT : State::SIZE;
|
||||
|
||||
return true;
|
||||
case State::SIZE:
|
||||
// Zero means full page size
|
||||
write_page_size = c;
|
||||
state = State::COLLECT;
|
||||
return true;
|
||||
case State::COLLECT:
|
||||
pages[write_page_idx][write_byte_idx++] = c;
|
||||
checksum ^= c;
|
||||
|
||||
// check if still collecting
|
||||
if (Cfg::PAGE_SIZE == 256) {
|
||||
// special case for 8-bit, check if rolled back to 0
|
||||
if (Cfg::DIRECTIONAL || !write_page_size) { // full 256 bytes
|
||||
if (write_byte_idx) return true;
|
||||
} else {
|
||||
if (write_byte_idx < write_page_size) return true;
|
||||
}
|
||||
} else if (Cfg::DIRECTIONAL) {
|
||||
if (write_byte_idx != Cfg::PAGE_SIZE) return true;
|
||||
} else {
|
||||
if (write_byte_idx < write_page_size) return true;
|
||||
}
|
||||
|
||||
state = State::CHECKSUM;
|
||||
return true;
|
||||
case State::CHECKSUM: {
|
||||
const PageState page_state = (checksum == c) ? PageState::OK : PageState::FAIL;
|
||||
set_page_state(write_page_idx, page_state);
|
||||
state = State::MONITOR;
|
||||
return true;
|
||||
}
|
||||
case State::UNFAIL:
|
||||
if (c == 0) {
|
||||
set_page_state(write_page_idx, PageState::FREE);
|
||||
} else {
|
||||
fatal_error = true;
|
||||
}
|
||||
state = State::MONITOR;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Cfg>
|
||||
void SerialPageManager<Cfg>::write_responses() {
|
||||
if (fatal_error) {
|
||||
kill(GET_TEXT(MSG_BAD_PAGE));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!page_states_dirty) return;
|
||||
page_states_dirty = false;
|
||||
|
||||
SERIAL_CHAR(Cfg::CONTROL_CHAR);
|
||||
constexpr int state_bits = 2;
|
||||
constexpr int n_bytes = Cfg::NUM_PAGES >> state_bits;
|
||||
volatile uint8_t bits_b[n_bytes] = { 0 };
|
||||
|
||||
for (page_idx_t i = 0 ; i < Cfg::NUM_PAGES ; i++) {
|
||||
bits_b[i >> state_bits] |= page_states[i] << ((i * state_bits) & 0x7);
|
||||
}
|
||||
|
||||
uint8_t crc = 0;
|
||||
for (uint8_t i = 0 ; i < n_bytes ; i++) {
|
||||
crc ^= bits_b[i];
|
||||
SERIAL_CHAR(bits_b[i]);
|
||||
}
|
||||
|
||||
SERIAL_CHAR(crc);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
template <typename Cfg>
|
||||
FORCE_INLINE void SerialPageManager<Cfg>::set_page_state(const page_idx_t page_idx, const PageState page_state) {
|
||||
CHECK_PAGE(page_idx,);
|
||||
|
||||
page_states[page_idx] = page_state;
|
||||
page_states_dirty = true;
|
||||
}
|
||||
|
||||
template <>
|
||||
FORCE_INLINE uint8_t *PageManager::get_page(const page_idx_t page_idx) {
|
||||
CHECK_PAGE(page_idx, nullptr);
|
||||
|
||||
return pages[page_idx];
|
||||
}
|
||||
|
||||
template <>
|
||||
FORCE_INLINE void PageManager::free_page(const page_idx_t page_idx) {
|
||||
set_page_state(page_idx, PageState::FREE);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
DirectStepping::PageManager page_manager;
|
||||
|
||||
const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS] PROGMEM = {
|
||||
|
||||
#if STEPPER_PAGE_FORMAT == SP_4x4D_128
|
||||
|
||||
{ 1, 1, 1, 1, 1, 1, 1 }, // 0 = -7
|
||||
{ 1, 1, 1, 0, 1, 1, 1 }, // 1 = -6
|
||||
{ 1, 1, 1, 0, 1, 0, 1 }, // 2 = -5
|
||||
{ 1, 1, 0, 1, 0, 1, 0 }, // 3 = -4
|
||||
{ 1, 1, 0, 0, 1, 0, 0 }, // 4 = -3
|
||||
{ 0, 0, 1, 0, 0, 0, 1 }, // 5 = -2
|
||||
{ 0, 0, 0, 1, 0, 0, 0 }, // 6 = -1
|
||||
{ 0, 0, 0, 0, 0, 0, 0 }, // 7 = 0
|
||||
{ 0, 0, 0, 1, 0, 0, 0 }, // 8 = 1
|
||||
{ 0, 0, 1, 0, 0, 0, 1 }, // 9 = 2
|
||||
{ 1, 1, 0, 0, 1, 0, 0 }, // 10 = 3
|
||||
{ 1, 1, 0, 1, 0, 1, 0 }, // 11 = 4
|
||||
{ 1, 1, 1, 0, 1, 0, 1 }, // 12 = 5
|
||||
{ 1, 1, 1, 0, 1, 1, 1 }, // 13 = 6
|
||||
{ 1, 1, 1, 1, 1, 1, 1 }, // 14 = 7
|
||||
{ 0 }
|
||||
|
||||
#elif STEPPER_PAGE_FORMAT == SP_4x2_256
|
||||
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 0, 1, 0 }, // 1
|
||||
{ 1, 0, 1 }, // 2
|
||||
{ 1, 1, 1 }, // 3
|
||||
|
||||
#elif STEPPER_PAGE_FORMAT == SP_4x1_512
|
||||
|
||||
{0} // Uncompressed format, table not used
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // DIRECT_STEPPING
|
||||
133
Marlin/src/feature/direct_stepping.h
Normal file
133
Marlin/src/feature/direct_stepping.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
namespace DirectStepping {
|
||||
|
||||
enum State : char {
|
||||
MONITOR, NEWLINE, ADDRESS, SIZE, COLLECT, CHECKSUM, UNFAIL
|
||||
};
|
||||
|
||||
enum PageState : uint8_t {
|
||||
FREE, WRITING, OK, FAIL
|
||||
};
|
||||
|
||||
// Static state used for stepping through direct stepping pages
|
||||
struct page_step_state_t {
|
||||
// Current page
|
||||
uint8_t *page;
|
||||
// Current segment
|
||||
uint16_t segment_idx;
|
||||
// Current steps within segment
|
||||
uint8_t segment_steps;
|
||||
// Segment delta
|
||||
xyze_uint8_t sd;
|
||||
// Block delta
|
||||
xyze_int_t bd;
|
||||
};
|
||||
|
||||
template<typename Cfg>
|
||||
class SerialPageManager {
|
||||
public:
|
||||
|
||||
typedef typename Cfg::page_idx_t page_idx_t;
|
||||
|
||||
static bool maybe_store_rxd_char(uint8_t c);
|
||||
static void write_responses();
|
||||
|
||||
// common methods for page managers
|
||||
static void init();
|
||||
static uint8_t *get_page(const page_idx_t page_idx);
|
||||
static void free_page(const page_idx_t page_idx);
|
||||
|
||||
protected:
|
||||
|
||||
typedef typename Cfg::write_byte_idx_t write_byte_idx_t;
|
||||
|
||||
static State state;
|
||||
static volatile bool fatal_error;
|
||||
|
||||
static volatile PageState page_states[Cfg::NUM_PAGES];
|
||||
static volatile bool page_states_dirty;
|
||||
|
||||
static uint8_t pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE];
|
||||
static uint8_t checksum;
|
||||
static write_byte_idx_t write_byte_idx;
|
||||
static page_idx_t write_page_idx;
|
||||
static write_byte_idx_t write_page_size;
|
||||
|
||||
static void set_page_state(const page_idx_t page_idx, const PageState page_state);
|
||||
};
|
||||
|
||||
template<bool b, typename T, typename F> struct TypeSelector { typedef T type;} ;
|
||||
template<typename T, typename F> struct TypeSelector<false, T, F> { typedef F type; };
|
||||
|
||||
template <int num_pages, int num_axes, int bits_segment, bool dir, int segments>
|
||||
struct config_t {
|
||||
static constexpr char CONTROL_CHAR = '!';
|
||||
|
||||
static constexpr int NUM_PAGES = num_pages;
|
||||
static constexpr int NUM_AXES = num_axes;
|
||||
static constexpr int BITS_SEGMENT = bits_segment;
|
||||
static constexpr int DIRECTIONAL = dir ? 1 : 0;
|
||||
static constexpr int SEGMENTS = segments;
|
||||
|
||||
static constexpr int NUM_SEGMENTS = _BV(BITS_SEGMENT);
|
||||
static constexpr int SEGMENT_STEPS = _BV(BITS_SEGMENT - DIRECTIONAL) - 1;
|
||||
static constexpr int TOTAL_STEPS = SEGMENT_STEPS * SEGMENTS;
|
||||
static constexpr int PAGE_SIZE = (NUM_AXES * BITS_SEGMENT * SEGMENTS) / 8;
|
||||
|
||||
typedef typename TypeSelector<(PAGE_SIZE>256), uint16_t, uint8_t>::type write_byte_idx_t;
|
||||
typedef typename TypeSelector<(NUM_PAGES>256), uint16_t, uint8_t>::type page_idx_t;
|
||||
};
|
||||
|
||||
template <uint8_t num_pages>
|
||||
using SP_4x4D_128 = config_t<num_pages, 4, 4, true, 128>;
|
||||
|
||||
template <uint8_t num_pages>
|
||||
using SP_4x2_256 = config_t<num_pages, 4, 2, false, 256>;
|
||||
|
||||
template <uint8_t num_pages>
|
||||
using SP_4x1_512 = config_t<num_pages, 4, 1, false, 512>;
|
||||
|
||||
// configured types
|
||||
typedef STEPPER_PAGE_FORMAT<STEPPER_PAGES> Config;
|
||||
|
||||
template class PAGE_MANAGER<Config>;
|
||||
typedef PAGE_MANAGER<Config> PageManager;
|
||||
};
|
||||
|
||||
#define SP_4x4D_128 1
|
||||
//#define SP_4x4_128 2
|
||||
//#define SP_4x2D_256 3
|
||||
#define SP_4x2_256 4
|
||||
#define SP_4x1_512 5
|
||||
|
||||
typedef typename DirectStepping::Config::page_idx_t page_idx_t;
|
||||
|
||||
// TODO: use config
|
||||
typedef DirectStepping::page_step_state_t page_step_state_t;
|
||||
|
||||
extern const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS];
|
||||
extern DirectStepping::PageManager page_manager;
|
||||
5
Marlin/src/feature/e_parser.cpp
Executable file → Normal file
5
Marlin/src/feature/e_parser.cpp
Executable file → Normal file
@@ -16,12 +16,12 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* emergency_parser.cpp - Intercept special commands directly in the serial stream
|
||||
* e_parser.cpp - Intercept special commands directly in the serial stream
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
// Static data members
|
||||
bool EmergencyParser::killed_by_M112, // = false
|
||||
EmergencyParser::quickstop_by_M410,
|
||||
EmergencyParser::enabled;
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
|
||||
170
Marlin/src/feature/e_parser.h
Executable file → Normal file
170
Marlin/src/feature/e_parser.h
Executable file → Normal file
@@ -16,13 +16,13 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* emergency_parser.h - Intercept special commands directly in the serial stream
|
||||
* e_parser.h - Intercept special commands directly in the serial stream
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
@@ -33,36 +33,46 @@
|
||||
|
||||
// External references
|
||||
extern bool wait_for_user, wait_for_heatup;
|
||||
void quickstop_stepper();
|
||||
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
// From motion.h, which cannot be included here
|
||||
void report_current_position_moving();
|
||||
void quickpause_stepper();
|
||||
void quickresume_stepper();
|
||||
#endif
|
||||
|
||||
void HAL_reboot();
|
||||
|
||||
class EmergencyParser {
|
||||
|
||||
public:
|
||||
|
||||
// Currently looking for: M108, M112, M410, M876
|
||||
enum State : char {
|
||||
// Currently looking for: M108, M112, M410, M876 S[0-9], S000, P000, R000
|
||||
enum State : uint8_t {
|
||||
EP_RESET,
|
||||
EP_N,
|
||||
EP_M,
|
||||
EP_M1,
|
||||
EP_M10,
|
||||
EP_M108,
|
||||
EP_M11,
|
||||
EP_M112,
|
||||
EP_M4,
|
||||
EP_M41,
|
||||
EP_M410,
|
||||
EP_M10, EP_M108,
|
||||
EP_M11, EP_M112,
|
||||
EP_M4, EP_M41, EP_M410,
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
EP_M8,
|
||||
EP_M87,
|
||||
EP_M876,
|
||||
EP_M876S,
|
||||
EP_M876SN,
|
||||
EP_M8, EP_M87, EP_M876, EP_M876S, EP_M876SN,
|
||||
#endif
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
EP_S, EP_S0, EP_S00, EP_GRBL_STATUS,
|
||||
EP_R, EP_R0, EP_R00, EP_GRBL_RESUME,
|
||||
EP_P, EP_P0, EP_P00, EP_GRBL_PAUSE,
|
||||
#endif
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
EP_ctrl,
|
||||
EP_K, EP_KI, EP_KIL, EP_KILL,
|
||||
#endif
|
||||
EP_IGNORE // to '\n'
|
||||
};
|
||||
|
||||
static bool killed_by_M112;
|
||||
static bool quickstop_by_M410;
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
static uint8_t M876_reason;
|
||||
@@ -71,32 +81,63 @@ public:
|
||||
EmergencyParser() { enable(); }
|
||||
|
||||
FORCE_INLINE static void enable() { enabled = true; }
|
||||
|
||||
FORCE_INLINE static void disable() { enabled = false; }
|
||||
|
||||
FORCE_INLINE static void update(State &state, const uint8_t c) {
|
||||
#define ISEOL(C) ((C) == '\n' || (C) == '\r')
|
||||
switch (state) {
|
||||
case EP_RESET:
|
||||
switch (c) {
|
||||
case ' ': case '\n': case '\r': break;
|
||||
case 'N': state = EP_N; break;
|
||||
case 'M': state = EP_M; break;
|
||||
default: state = EP_IGNORE;
|
||||
case 'N': state = EP_N; break;
|
||||
case 'M': state = EP_M; break;
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
case 'S': state = EP_S; break;
|
||||
case 'P': state = EP_P; break;
|
||||
case 'R': state = EP_R; break;
|
||||
#endif
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
case '^': state = EP_ctrl; break;
|
||||
case 'K': state = EP_K; break;
|
||||
#endif
|
||||
default: state = EP_IGNORE;
|
||||
}
|
||||
break;
|
||||
|
||||
case EP_N:
|
||||
switch (c) {
|
||||
case '0': case '1': case '2':
|
||||
case '3': case '4': case '5':
|
||||
case '6': case '7': case '8':
|
||||
case '9': case '-': case ' ': break;
|
||||
case 'M': state = EP_M; break;
|
||||
default: state = EP_IGNORE;
|
||||
case '0' ... '9':
|
||||
case '-': case ' ': break;
|
||||
case 'M': state = EP_M; break;
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
case 'S': state = EP_S; break;
|
||||
case 'P': state = EP_P; break;
|
||||
case 'R': state = EP_R; break;
|
||||
#endif
|
||||
default: state = EP_IGNORE;
|
||||
}
|
||||
break;
|
||||
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
case EP_S: state = (c == '0') ? EP_S0 : EP_IGNORE; break;
|
||||
case EP_S0: state = (c == '0') ? EP_S00 : EP_IGNORE; break;
|
||||
case EP_S00: state = (c == '0') ? EP_GRBL_STATUS : EP_IGNORE; break;
|
||||
|
||||
case EP_R: state = (c == '0') ? EP_R0 : EP_IGNORE; break;
|
||||
case EP_R0: state = (c == '0') ? EP_R00 : EP_IGNORE; break;
|
||||
case EP_R00: state = (c == '0') ? EP_GRBL_RESUME : EP_IGNORE; break;
|
||||
|
||||
case EP_P: state = (c == '0') ? EP_P0 : EP_IGNORE; break;
|
||||
case EP_P0: state = (c == '0') ? EP_P00 : EP_IGNORE; break;
|
||||
case EP_P00: state = (c == '0') ? EP_GRBL_PAUSE : EP_IGNORE; break;
|
||||
#endif
|
||||
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
case EP_ctrl: state = (c == 'X') ? EP_KILL : EP_IGNORE; break;
|
||||
case EP_K: state = (c == 'I') ? EP_KI : EP_IGNORE; break;
|
||||
case EP_KI: state = (c == 'L') ? EP_KIL : EP_IGNORE; break;
|
||||
case EP_KIL: state = (c == 'L') ? EP_KILL : EP_IGNORE; break;
|
||||
#endif
|
||||
|
||||
case EP_M:
|
||||
switch (c) {
|
||||
case ' ': break;
|
||||
@@ -117,51 +158,34 @@ public:
|
||||
}
|
||||
break;
|
||||
|
||||
case EP_M10:
|
||||
state = (c == '8') ? EP_M108 : EP_IGNORE;
|
||||
break;
|
||||
|
||||
case EP_M11:
|
||||
state = (c == '2') ? EP_M112 : EP_IGNORE;
|
||||
break;
|
||||
|
||||
case EP_M4:
|
||||
state = (c == '1') ? EP_M41 : EP_IGNORE;
|
||||
break;
|
||||
|
||||
case EP_M41:
|
||||
state = (c == '0') ? EP_M410 : EP_IGNORE;
|
||||
break;
|
||||
case EP_M10: state = (c == '8') ? EP_M108 : EP_IGNORE; break;
|
||||
case EP_M11: state = (c == '2') ? EP_M112 : EP_IGNORE; break;
|
||||
case EP_M4: state = (c == '1') ? EP_M41 : EP_IGNORE; break;
|
||||
case EP_M41: state = (c == '0') ? EP_M410 : EP_IGNORE; break;
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
case EP_M8:
|
||||
state = (c == '7') ? EP_M87 : EP_IGNORE;
|
||||
break;
|
||||
|
||||
case EP_M87:
|
||||
state = (c == '6') ? EP_M876 : EP_IGNORE;
|
||||
break;
|
||||
case EP_M8: state = (c == '7') ? EP_M87 : EP_IGNORE; break;
|
||||
case EP_M87: state = (c == '6') ? EP_M876 : EP_IGNORE; break;
|
||||
|
||||
case EP_M876:
|
||||
switch (c) {
|
||||
case ' ': break;
|
||||
case 'S': state = EP_M876S; break;
|
||||
default: state = EP_IGNORE; break;
|
||||
}
|
||||
break;
|
||||
case EP_M876:
|
||||
switch (c) {
|
||||
case ' ': break;
|
||||
case 'S': state = EP_M876S; break;
|
||||
default: state = EP_IGNORE; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EP_M876S:
|
||||
switch (c) {
|
||||
case ' ': break;
|
||||
case '0' ... '9':
|
||||
state = EP_M876SN;
|
||||
M876_reason = uint8_t(c - '0');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EP_M876S:
|
||||
switch (c) {
|
||||
case ' ': break;
|
||||
case '0': case '1': case '2':
|
||||
case '3': case '4': case '5':
|
||||
case '6': case '7': case '8':
|
||||
case '9':
|
||||
state = EP_M876SN;
|
||||
M876_reason = (uint8_t)(c - '0');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case EP_IGNORE:
|
||||
@@ -173,10 +197,18 @@ public:
|
||||
if (enabled) switch (state) {
|
||||
case EP_M108: wait_for_user = wait_for_heatup = false; break;
|
||||
case EP_M112: killed_by_M112 = true; break;
|
||||
case EP_M410: quickstop_stepper(); break;
|
||||
case EP_M410: quickstop_by_M410 = true; break;
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
case EP_M876SN: host_response_handler(M876_reason); break;
|
||||
#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;
|
||||
#endif
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
case EP_KILL: HAL_reboot(); break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
state = EP_RESET;
|
||||
|
||||
190
Marlin/src/feature/encoder_i2c.cpp
Executable file → Normal file
190
Marlin/src/feature/encoder_i2c.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
|
||||
#include "encoder_i2c.h"
|
||||
|
||||
#include "../module/temperature.h"
|
||||
#include "../module/stepper.h"
|
||||
#include "../gcode/parser.h"
|
||||
|
||||
@@ -42,13 +41,15 @@
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
I2CPositionEncodersMgr I2CPEM;
|
||||
|
||||
void I2CPositionEncoder::init(const uint8_t address, const AxisEnum axis) {
|
||||
encoderAxis = axis;
|
||||
i2cAddress = address;
|
||||
|
||||
initialized++;
|
||||
initialized = true;
|
||||
|
||||
SERIAL_ECHOLNPAIR("Setting up encoder on ", axis_codes[encoderAxis], " axis, addr = ", address);
|
||||
SERIAL_ECHOLNPGM("Setting up encoder on ", AS_CHAR(axis_codes[encoderAxis]), " axis, addr = ", address);
|
||||
|
||||
position = get_position();
|
||||
}
|
||||
@@ -66,7 +67,7 @@ void I2CPositionEncoder::update() {
|
||||
/*
|
||||
if (trusted) { //commented out as part of the note below
|
||||
trusted = false;
|
||||
SERIAL_ECHOLMPAIR("Fault detected on ", axis_codes[encoderAxis], " axis encoder. Disengaging error correction until module is trusted again.");
|
||||
SERIAL_ECHOLNPGM("Fault detected on ", AS_CHAR(axis_codes[encoderAxis]), " axis encoder. Disengaging error correction until module is trusted again.");
|
||||
}
|
||||
*/
|
||||
return;
|
||||
@@ -85,15 +86,15 @@ void I2CPositionEncoder::update() {
|
||||
* the encoder would be re-enabled.
|
||||
*/
|
||||
|
||||
/*
|
||||
#if 0
|
||||
// If the magnetic strength has been good for a certain time, start trusting the module again
|
||||
|
||||
if (millis() - lastErrorTime > I2CPE_TIME_TRUSTED) {
|
||||
trusted = true;
|
||||
|
||||
SERIAL_ECHOLNPAIR("Untrusted encoder module on ", axis_codes[encoderAxis], " axis has been fault-free for set duration, reinstating error correction.");
|
||||
SERIAL_ECHOLNPGM("Untrusted encoder module on ", AS_CHAR(axis_codes[encoderAxis]), " axis has been fault-free for set duration, reinstating error correction.");
|
||||
|
||||
//the encoder likely lost its place when the error occured, so we'll reset and use the printer's
|
||||
//the encoder likely lost its place when the error occurred, so we'll reset and use the printer's
|
||||
//idea of where it the axis is to re-initialize
|
||||
const float pos = planner.get_axis_position_mm(encoderAxis);
|
||||
int32_t positionInTicks = pos * get_ticks_unit();
|
||||
@@ -102,16 +103,16 @@ void I2CPositionEncoder::update() {
|
||||
zeroOffset -= (positionInTicks - get_position());
|
||||
|
||||
#ifdef I2CPE_DEBUG
|
||||
SERIAL_ECHOLNPAIR("Current position is ", pos);
|
||||
SERIAL_ECHOLNPAIR("Position in encoder ticks is ", positionInTicks);
|
||||
SERIAL_ECHOLNPAIR("New zero-offset of ", zeroOffset);
|
||||
SERIAL_ECHOPAIR("New position reads as ", get_position());
|
||||
SERIAL_ECHOLNPGM("Current position is ", pos);
|
||||
SERIAL_ECHOLNPGM("Position in encoder ticks is ", positionInTicks);
|
||||
SERIAL_ECHOLNPGM("New zero-offset of ", zeroOffset);
|
||||
SERIAL_ECHOPGM("New position reads as ", get_position());
|
||||
SERIAL_CHAR('(');
|
||||
SERIAL_ECHO(mm_from_count(get_position()));
|
||||
SERIAL_DECIMAL(mm_from_count(get_position()));
|
||||
SERIAL_ECHOLNPGM(")");
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,12 +149,12 @@ void I2CPositionEncoder::update() {
|
||||
const int32_t error = get_axis_error_steps(false);
|
||||
#endif
|
||||
|
||||
//SERIAL_ECHOLNPAIR("Axis error steps: ", error);
|
||||
//SERIAL_ECHOLNPGM("Axis error steps: ", error);
|
||||
|
||||
#ifdef I2CPE_ERR_THRESH_ABORT
|
||||
if (ABS(error) > I2CPE_ERR_THRESH_ABORT * planner.settings.axis_steps_per_mm[encoderAxis]) {
|
||||
//kill(PSTR("Significant Error"));
|
||||
SERIAL_ECHOLNPAIR("Axis error over threshold, aborting!", error);
|
||||
SERIAL_ECHOLNPGM("Axis error over threshold, aborting!", error);
|
||||
safe_delay(5000);
|
||||
}
|
||||
#endif
|
||||
@@ -171,8 +172,8 @@ void I2CPositionEncoder::update() {
|
||||
float sumP = 0;
|
||||
LOOP_L_N(i, I2CPE_ERR_PRST_ARRAY_SIZE) sumP += errPrst[i];
|
||||
const int32_t errorP = int32_t(sumP * RECIPROCAL(I2CPE_ERR_PRST_ARRAY_SIZE));
|
||||
SERIAL_ECHO(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPAIR(" : CORRECT ERR ", errorP * planner.steps_to_mm[encoderAxis], "mm");
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPGM(" : CORRECT ERR ", errorP * planner.mm_per_step[encoderAxis], "mm");
|
||||
babystep.add_steps(encoderAxis, -LROUND(errorP));
|
||||
errPrstIdx = 0;
|
||||
}
|
||||
@@ -191,8 +192,8 @@ void I2CPositionEncoder::update() {
|
||||
if (ABS(error) > I2CPE_ERR_CNT_THRESH * planner.settings.axis_steps_per_mm[encoderAxis]) {
|
||||
const millis_t ms = millis();
|
||||
if (ELAPSED(ms, nextErrorCountTime)) {
|
||||
SERIAL_ECHO(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPAIR(" : LARGE ERR ", int(error), "; diffSum=", diffSum);
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPGM(" : LARGE ERR ", error, "; diffSum=", diffSum);
|
||||
errorCount++;
|
||||
nextErrorCountTime = ms + I2CPE_ERR_CNT_DEBOUNCE_MS;
|
||||
}
|
||||
@@ -208,12 +209,11 @@ void I2CPositionEncoder::set_homed() {
|
||||
delay(10);
|
||||
|
||||
zeroOffset = get_raw_count();
|
||||
homed++;
|
||||
trusted++;
|
||||
homed = trusted = true;
|
||||
|
||||
#ifdef I2CPE_DEBUG
|
||||
SERIAL_ECHO(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPAIR(" axis encoder homed, offset of ", zeroOffset, " ticks.");
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPGM(" axis encoder homed, offset of ", zeroOffset, " ticks.");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -223,7 +223,7 @@ void I2CPositionEncoder::set_unhomed() {
|
||||
homed = trusted = false;
|
||||
|
||||
#ifdef I2CPE_DEBUG
|
||||
SERIAL_ECHO(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPGM(" axis encoder unhomed.");
|
||||
#endif
|
||||
}
|
||||
@@ -231,7 +231,7 @@ void I2CPositionEncoder::set_unhomed() {
|
||||
bool I2CPositionEncoder::passes_test(const bool report) {
|
||||
if (report) {
|
||||
if (H != I2CPE_MAG_SIG_GOOD) SERIAL_ECHOPGM("Warning. ");
|
||||
SERIAL_ECHO(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
serial_ternary(H == I2CPE_MAG_SIG_BAD, PSTR(" axis "), PSTR("magnetic strip "), PSTR("encoder "));
|
||||
switch (H) {
|
||||
case I2CPE_MAG_SIG_GOOD:
|
||||
@@ -252,8 +252,8 @@ float I2CPositionEncoder::get_axis_error_mm(const bool report) {
|
||||
error = ABS(diff) > 10000 ? 0 : diff; // Huge error is a bad reading
|
||||
|
||||
if (report) {
|
||||
SERIAL_ECHO(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPAIR(" axis target=", target, "mm; actual=", actual, "mm; err=", error, "mm");
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPGM(" axis target=", target, "mm; actual=", actual, "mm; err=", error, "mm");
|
||||
}
|
||||
|
||||
return error;
|
||||
@@ -262,7 +262,7 @@ float I2CPositionEncoder::get_axis_error_mm(const bool report) {
|
||||
int32_t I2CPositionEncoder::get_axis_error_steps(const bool report) {
|
||||
if (!active) {
|
||||
if (report) {
|
||||
SERIAL_ECHO(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPGM(" axis encoder not active!");
|
||||
}
|
||||
return 0;
|
||||
@@ -287,8 +287,8 @@ int32_t I2CPositionEncoder::get_axis_error_steps(const bool report) {
|
||||
errorPrev = error;
|
||||
|
||||
if (report) {
|
||||
SERIAL_ECHO(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPAIR(" axis target=", target, "; actual=", encoderCountInStepperTicksScaled, "; err=", error);
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_ECHOLNPGM(" axis target=", target, "; actual=", encoderCountInStepperTicksScaled, "; err=", error);
|
||||
}
|
||||
|
||||
if (suppressOutput) {
|
||||
@@ -305,7 +305,7 @@ int32_t I2CPositionEncoder::get_raw_count() {
|
||||
|
||||
encoderCount.val = 0x00;
|
||||
|
||||
if (Wire.requestFrom((int)i2cAddress, 3) != 3) {
|
||||
if (Wire.requestFrom(I2C_ADDRESS(i2cAddress), uint8_t(3)) != 3) {
|
||||
//houston, we have a problem...
|
||||
H = I2CPE_MAG_SIG_NF;
|
||||
return 0;
|
||||
@@ -327,17 +327,17 @@ int32_t I2CPositionEncoder::get_raw_count() {
|
||||
}
|
||||
|
||||
bool I2CPositionEncoder::test_axis() {
|
||||
//only works on XYZ cartesian machines for the time being
|
||||
// Only works on XYZ Cartesian machines for the time being
|
||||
if (!(encoderAxis == X_AXIS || encoderAxis == Y_AXIS || encoderAxis == Z_AXIS)) return false;
|
||||
|
||||
const float startPosition = soft_endstop.min[encoderAxis] + 10,
|
||||
endPosition = soft_endstop.max[encoderAxis] - 10;
|
||||
const feedRate_t fr_mm_s = FLOOR(MMM_TO_MMS((encoderAxis == Z_AXIS) ? HOMING_FEEDRATE_Z : HOMING_FEEDRATE_XY));
|
||||
const feedRate_t fr_mm_s = FLOOR(homing_feedrate(encoderAxis));
|
||||
|
||||
ec = false;
|
||||
|
||||
xyze_pos_t startCoord, endCoord;
|
||||
LOOP_XYZ(a) {
|
||||
LOOP_LINEAR_AXES(a) {
|
||||
startCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
|
||||
endCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
|
||||
}
|
||||
@@ -345,9 +345,12 @@ bool I2CPositionEncoder::test_axis() {
|
||||
endCoord[encoderAxis] = endPosition;
|
||||
|
||||
planner.synchronize();
|
||||
startCoord.e = planner.get_axis_position_mm(E_AXIS);
|
||||
planner.buffer_line(startCoord, fr_mm_s, 0);
|
||||
planner.synchronize();
|
||||
|
||||
#if HAS_EXTRUDERS
|
||||
startCoord.e = planner.get_axis_position_mm(E_AXIS);
|
||||
planner.buffer_line(startCoord, fr_mm_s, 0);
|
||||
planner.synchronize();
|
||||
#endif
|
||||
|
||||
// if the module isn't currently trusted, wait until it is (or until it should be if things are working)
|
||||
if (!trusted) {
|
||||
@@ -357,7 +360,7 @@ bool I2CPositionEncoder::test_axis() {
|
||||
}
|
||||
|
||||
if (trusted) { // if trusted, commence test
|
||||
endCoord.e = planner.get_axis_position_mm(E_AXIS);
|
||||
TERN_(HAS_EXTRUDERS, endCoord.e = planner.get_axis_position_mm(E_AXIS));
|
||||
planner.buffer_line(endCoord, fr_mm_s, 0);
|
||||
planner.synchronize();
|
||||
}
|
||||
@@ -382,7 +385,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
|
||||
|
||||
int32_t startCount, stopCount;
|
||||
|
||||
const feedRate_t fr_mm_s = MMM_TO_MMS((encoderAxis == Z_AXIS) ? HOMING_FEEDRATE_Z : HOMING_FEEDRATE_XY);
|
||||
const feedRate_t fr_mm_s = homing_feedrate(encoderAxis);
|
||||
|
||||
bool oldec = ec;
|
||||
ec = false;
|
||||
@@ -392,7 +395,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
|
||||
travelDistance = endDistance - startDistance;
|
||||
|
||||
xyze_pos_t startCoord, endCoord;
|
||||
LOOP_XYZ(a) {
|
||||
LOOP_LINEAR_AXES(a) {
|
||||
startCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
|
||||
endCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
|
||||
}
|
||||
@@ -402,7 +405,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
|
||||
planner.synchronize();
|
||||
|
||||
LOOP_L_N(i, iter) {
|
||||
startCoord.e = planner.get_axis_position_mm(E_AXIS);
|
||||
TERN_(HAS_EXTRUDERS, startCoord.e = planner.get_axis_position_mm(E_AXIS));
|
||||
planner.buffer_line(startCoord, fr_mm_s, 0);
|
||||
planner.synchronize();
|
||||
|
||||
@@ -411,7 +414,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
|
||||
|
||||
//do_blocking_move_to(endCoord);
|
||||
|
||||
endCoord.e = planner.get_axis_position_mm(E_AXIS);
|
||||
TERN_(HAS_EXTRUDERS, endCoord.e = planner.get_axis_position_mm(E_AXIS));
|
||||
planner.buffer_line(endCoord, fr_mm_s, 0);
|
||||
planner.synchronize();
|
||||
|
||||
@@ -421,15 +424,15 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
|
||||
|
||||
travelledDistance = mm_from_count(ABS(stopCount - startCount));
|
||||
|
||||
SERIAL_ECHOLNPAIR("Attempted travel: ", travelDistance, "mm");
|
||||
SERIAL_ECHOLNPAIR(" Actual travel: ", travelledDistance, "mm");
|
||||
SERIAL_ECHOLNPGM("Attempted travel: ", travelDistance, "mm");
|
||||
SERIAL_ECHOLNPGM(" Actual travel: ", travelledDistance, "mm");
|
||||
|
||||
//Calculate new axis steps per unit
|
||||
old_steps_mm = planner.settings.axis_steps_per_mm[encoderAxis];
|
||||
new_steps_mm = (old_steps_mm * travelDistance) / travelledDistance;
|
||||
|
||||
SERIAL_ECHOLNPAIR("Old steps/mm: ", old_steps_mm);
|
||||
SERIAL_ECHOLNPAIR("New steps/mm: ", new_steps_mm);
|
||||
SERIAL_ECHOLNPGM("Old steps/mm: ", old_steps_mm);
|
||||
SERIAL_ECHOLNPGM("New steps/mm: ", new_steps_mm);
|
||||
|
||||
//Save new value
|
||||
planner.settings.axis_steps_per_mm[encoderAxis] = new_steps_mm;
|
||||
@@ -446,7 +449,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
|
||||
|
||||
if (iter > 1) {
|
||||
total /= (float)iter;
|
||||
SERIAL_ECHOLNPAIR("Average steps/mm: ", total);
|
||||
SERIAL_ECHOLNPGM("Average steps/mm: ", total);
|
||||
}
|
||||
|
||||
ec = oldec;
|
||||
@@ -459,9 +462,7 @@ void I2CPositionEncoder::reset() {
|
||||
Wire.write(I2CPE_RESET_COUNT);
|
||||
Wire.endTransmission();
|
||||
|
||||
#if ENABLED(I2CPE_ERR_ROLLING_AVERAGE)
|
||||
ZERO(err);
|
||||
#endif
|
||||
TERN_(I2CPE_ERR_ROLLING_AVERAGE, ZERO(err));
|
||||
}
|
||||
|
||||
|
||||
@@ -499,9 +500,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
|
||||
encoders[i].set_active(encoders[i].passes_test(true));
|
||||
|
||||
#if I2CPE_ENC_1_AXIS == E_AXIS
|
||||
encoders[i].set_homed();
|
||||
#endif
|
||||
TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_1_AXIS == E_AXIS) encoders[i].set_homed());
|
||||
#endif
|
||||
|
||||
#if I2CPE_ENCODER_CNT > 1
|
||||
@@ -530,9 +529,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
|
||||
encoders[i].set_active(encoders[i].passes_test(true));
|
||||
|
||||
#if I2CPE_ENC_2_AXIS == E_AXIS
|
||||
encoders[i].set_homed();
|
||||
#endif
|
||||
TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_2_AXIS == E_AXIS) encoders[i].set_homed());
|
||||
#endif
|
||||
|
||||
#if I2CPE_ENCODER_CNT > 2
|
||||
@@ -559,11 +556,9 @@ void I2CPositionEncodersMgr::init() {
|
||||
encoders[i].set_ec_threshold(I2CPE_ENC_3_EC_THRESH);
|
||||
#endif
|
||||
|
||||
encoders[i].set_active(encoders[i].passes_test(true));
|
||||
encoders[i].set_active(encoders[i].passes_test(true));
|
||||
|
||||
#if I2CPE_ENC_3_AXIS == E_AXIS
|
||||
encoders[i].set_homed();
|
||||
#endif
|
||||
TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_3_AXIS == E_AXIS) encoders[i].set_homed());
|
||||
#endif
|
||||
|
||||
#if I2CPE_ENCODER_CNT > 3
|
||||
@@ -592,9 +587,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
|
||||
encoders[i].set_active(encoders[i].passes_test(true));
|
||||
|
||||
#if I2CPE_ENC_4_AXIS == E_AXIS
|
||||
encoders[i].set_homed();
|
||||
#endif
|
||||
TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_4_AXIS == E_AXIS) encoders[i].set_homed());
|
||||
#endif
|
||||
|
||||
#if I2CPE_ENCODER_CNT > 4
|
||||
@@ -623,9 +616,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
|
||||
encoders[i].set_active(encoders[i].passes_test(true));
|
||||
|
||||
#if I2CPE_ENC_5_AXIS == E_AXIS
|
||||
encoders[i].set_homed();
|
||||
#endif
|
||||
TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_5_AXIS == E_AXIS) encoders[i].set_homed());
|
||||
#endif
|
||||
|
||||
#if I2CPE_ENCODER_CNT > 5
|
||||
@@ -654,9 +645,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
|
||||
encoders[i].set_active(encoders[i].passes_test(true));
|
||||
|
||||
#if I2CPE_ENC_6_AXIS == E_AXIS
|
||||
encoders[i].set_homed();
|
||||
#endif
|
||||
TERN_(HAS_EXTRUDERS, if (I2CPE_ENC_6_AXIS == E_AXIS) encoders[i].set_homed());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -668,8 +657,7 @@ void I2CPositionEncodersMgr::report_position(const int8_t idx, const bool units,
|
||||
else {
|
||||
if (noOffset) {
|
||||
const int32_t raw_count = encoders[idx].get_raw_count();
|
||||
SERIAL_ECHO(axis_codes[encoders[idx].get_axis()]);
|
||||
SERIAL_CHAR(' ');
|
||||
SERIAL_CHAR(axis_codes[encoders[idx].get_axis()], ' ');
|
||||
|
||||
for (uint8_t j = 31; j > 0; j--)
|
||||
SERIAL_ECHO((bool)(0x00000001 & (raw_count >> j)));
|
||||
@@ -687,18 +675,18 @@ void I2CPositionEncodersMgr::change_module_address(const uint8_t oldaddr, const
|
||||
// First check 'new' address is not in use
|
||||
Wire.beginTransmission(I2C_ADDRESS(newaddr));
|
||||
if (!Wire.endTransmission()) {
|
||||
SERIAL_ECHOLNPAIR("?There is already a device with that address on the I2C bus! (", newaddr, ")");
|
||||
SERIAL_ECHOLNPGM("?There is already a device with that address on the I2C bus! (", newaddr, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// Now check that we can find the module on the oldaddr address
|
||||
Wire.beginTransmission(I2C_ADDRESS(oldaddr));
|
||||
if (Wire.endTransmission()) {
|
||||
SERIAL_ECHOLNPAIR("?No module detected at this address! (", oldaddr, ")");
|
||||
SERIAL_ECHOLNPGM("?No module detected at this address! (", oldaddr, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
SERIAL_ECHOLNPAIR("Module found at ", oldaddr, ", changing address to ", newaddr);
|
||||
SERIAL_ECHOLNPGM("Module found at ", oldaddr, ", changing address to ", newaddr);
|
||||
|
||||
// Change the modules address
|
||||
Wire.beginTransmission(I2C_ADDRESS(oldaddr));
|
||||
@@ -724,7 +712,7 @@ void I2CPositionEncodersMgr::change_module_address(const uint8_t oldaddr, const
|
||||
// and enable it (it will likely have failed initialization on power-up, before the address change).
|
||||
const int8_t idx = idx_from_addr(newaddr);
|
||||
if (idx >= 0 && !encoders[idx].get_active()) {
|
||||
SERIAL_ECHO(axis_codes[encoders[idx].get_axis()]);
|
||||
SERIAL_CHAR(axis_codes[encoders[idx].get_axis()]);
|
||||
SERIAL_ECHOLNPGM(" axis encoder was not detected on printer startup. Trying again.");
|
||||
encoders[idx].set_active(encoders[idx].passes_test(true));
|
||||
}
|
||||
@@ -734,11 +722,11 @@ void I2CPositionEncodersMgr::report_module_firmware(const uint8_t address) {
|
||||
// First check there is a module
|
||||
Wire.beginTransmission(I2C_ADDRESS(address));
|
||||
if (Wire.endTransmission()) {
|
||||
SERIAL_ECHOLNPAIR("?No module detected at this address! (", address, ")");
|
||||
SERIAL_ECHOLNPGM("?No module detected at this address! (", address, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
SERIAL_ECHOLNPAIR("Requesting version info from module at address ", address, ":");
|
||||
SERIAL_ECHOLNPGM("Requesting version info from module at address ", address, ":");
|
||||
|
||||
Wire.beginTransmission(I2C_ADDRESS(address));
|
||||
Wire.write(I2CPE_SET_REPORT_MODE);
|
||||
@@ -746,10 +734,10 @@ void I2CPositionEncodersMgr::report_module_firmware(const uint8_t address) {
|
||||
Wire.endTransmission();
|
||||
|
||||
// Read value
|
||||
if (Wire.requestFrom((int)address, 32)) {
|
||||
if (Wire.requestFrom(I2C_ADDRESS(address), uint8_t(32))) {
|
||||
char c;
|
||||
while (Wire.available() > 0 && (c = (char)Wire.read()) > 0)
|
||||
SERIAL_ECHO(c);
|
||||
SERIAL_CHAR(c);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
@@ -785,13 +773,13 @@ int8_t I2CPositionEncodersMgr::parse() {
|
||||
else if (parser.seenval('I')) {
|
||||
|
||||
if (!parser.has_value()) {
|
||||
SERIAL_ECHOLNPAIR("?I seen, but no index specified! [0-", I2CPE_ENCODER_CNT - 1, "]");
|
||||
SERIAL_ECHOLNPGM("?I seen, but no index specified! [0-", I2CPE_ENCODER_CNT - 1, "]");
|
||||
return I2CPE_PARSE_ERR;
|
||||
};
|
||||
|
||||
I2CPE_idx = parser.value_byte();
|
||||
if (I2CPE_idx >= I2CPE_ENCODER_CNT) {
|
||||
SERIAL_ECHOLNPAIR("?Index out of range. [0-", I2CPE_ENCODER_CNT - 1, "]");
|
||||
SERIAL_ECHOLNPGM("?Index out of range. [0-", I2CPE_ENCODER_CNT - 1, "]");
|
||||
return I2CPE_PARSE_ERR;
|
||||
}
|
||||
|
||||
@@ -818,16 +806,15 @@ int8_t I2CPositionEncodersMgr::parse() {
|
||||
* Y Report on Y axis encoder, if present.
|
||||
* Z Report on Z axis encoder, if present.
|
||||
* E Report on E axis encoder, if present.
|
||||
*
|
||||
*/
|
||||
void I2CPositionEncodersMgr::M860() {
|
||||
if (parse()) return;
|
||||
|
||||
const bool hasU = parser.seen('U'), hasO = parser.seen('O');
|
||||
const bool hasU = parser.seen_test('U'), hasO = parser.seen_test('O');
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_XYZE(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen_test(axis_codes[i])) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) report_position(idx, hasU, hasO);
|
||||
}
|
||||
@@ -848,13 +835,12 @@ void I2CPositionEncodersMgr::M860() {
|
||||
* Y Report on Y axis encoder, if present.
|
||||
* Z Report on Z axis encoder, if present.
|
||||
* E Report on E axis encoder, if present.
|
||||
*
|
||||
*/
|
||||
void I2CPositionEncodersMgr::M861() {
|
||||
if (parse()) return;
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_XYZE(i) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) report_status(idx);
|
||||
@@ -877,13 +863,12 @@ void I2CPositionEncodersMgr::M861() {
|
||||
* Y Report on Y axis encoder, if present.
|
||||
* Z Report on Z axis encoder, if present.
|
||||
* E Report on E axis encoder, if present.
|
||||
*
|
||||
*/
|
||||
void I2CPositionEncodersMgr::M862() {
|
||||
if (parse()) return;
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_XYZE(i) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) test_axis(idx);
|
||||
@@ -907,7 +892,6 @@ void I2CPositionEncodersMgr::M862() {
|
||||
* Y Report on Y axis encoder, if present.
|
||||
* Z Report on Z axis encoder, if present.
|
||||
* E Report on E axis encoder, if present.
|
||||
*
|
||||
*/
|
||||
void I2CPositionEncodersMgr::M863() {
|
||||
if (parse()) return;
|
||||
@@ -915,7 +899,7 @@ void I2CPositionEncodersMgr::M863() {
|
||||
const uint8_t iterations = constrain(parser.byteval('P', 1), 1, 10);
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_XYZE(i) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) calibrate_steps_mm(idx, iterations);
|
||||
@@ -963,14 +947,14 @@ void I2CPositionEncodersMgr::M864() {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (parser.seen('X')) newAddress = I2CPE_PRESET_ADDR_X;
|
||||
else if (parser.seen('Y')) newAddress = I2CPE_PRESET_ADDR_Y;
|
||||
else if (parser.seen('Z')) newAddress = I2CPE_PRESET_ADDR_Z;
|
||||
else if (parser.seen('E')) newAddress = I2CPE_PRESET_ADDR_E;
|
||||
if (parser.seen_test('X')) newAddress = I2CPE_PRESET_ADDR_X;
|
||||
else if (parser.seen_test('Y')) newAddress = I2CPE_PRESET_ADDR_Y;
|
||||
else if (parser.seen_test('Z')) newAddress = I2CPE_PRESET_ADDR_Z;
|
||||
else if (parser.seen_test('E')) newAddress = I2CPE_PRESET_ADDR_E;
|
||||
else return;
|
||||
}
|
||||
|
||||
SERIAL_ECHOLNPAIR("Changing module at address ", I2CPE_addr, " to address ", newAddress);
|
||||
SERIAL_ECHOLNPGM("Changing module at address ", I2CPE_addr, " to address ", newAddress);
|
||||
|
||||
change_module_address(I2CPE_addr, newAddress);
|
||||
}
|
||||
@@ -991,7 +975,7 @@ void I2CPositionEncodersMgr::M865() {
|
||||
if (parse()) return;
|
||||
|
||||
if (!I2CPE_addr) {
|
||||
LOOP_XYZE(i) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) report_module_firmware(encoders[idx].get_address());
|
||||
@@ -1019,10 +1003,10 @@ void I2CPositionEncodersMgr::M865() {
|
||||
void I2CPositionEncodersMgr::M866() {
|
||||
if (parse()) return;
|
||||
|
||||
const bool hasR = parser.seen('R');
|
||||
const bool hasR = parser.seen_test('R');
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_XYZE(i) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) {
|
||||
@@ -1060,7 +1044,7 @@ void I2CPositionEncodersMgr::M867() {
|
||||
const int8_t onoff = parser.seenval('S') ? parser.value_int() : -1;
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_XYZE(i) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) {
|
||||
@@ -1096,7 +1080,7 @@ void I2CPositionEncodersMgr::M868() {
|
||||
const float newThreshold = parser.seenval('T') ? parser.value_float() : -9999;
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_XYZE(i) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) {
|
||||
@@ -1130,7 +1114,7 @@ void I2CPositionEncodersMgr::M869() {
|
||||
if (parse()) return;
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_XYZE(i) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) report_error(idx);
|
||||
|
||||
16
Marlin/src/feature/encoder_i2c.h
Executable file → Normal file
16
Marlin/src/feature/encoder_i2c.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -188,7 +188,7 @@ class I2CPositionEncoder {
|
||||
FORCE_INLINE void set_ec_method(const byte method) { ecMethod = method; }
|
||||
|
||||
FORCE_INLINE float get_ec_threshold() { return ecThreshold; }
|
||||
FORCE_INLINE void set_ec_threshold(const float newThreshold) { ecThreshold = newThreshold; }
|
||||
FORCE_INLINE void set_ec_threshold(const_float_t newThreshold) { ecThreshold = newThreshold; }
|
||||
|
||||
FORCE_INLINE int get_encoder_ticks_mm() {
|
||||
switch (type) {
|
||||
@@ -236,7 +236,7 @@ class I2CPositionEncodersMgr {
|
||||
|
||||
static void report_status(const int8_t idx) {
|
||||
CHECK_IDX();
|
||||
SERIAL_ECHOLNPAIR("Encoder ", idx, ": ");
|
||||
SERIAL_ECHOLNPGM("Encoder ", idx, ": ");
|
||||
encoders[idx].get_raw_count();
|
||||
encoders[idx].passes_test(true);
|
||||
}
|
||||
@@ -261,32 +261,32 @@ class I2CPositionEncodersMgr {
|
||||
|
||||
static void report_error_count(const int8_t idx, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
SERIAL_ECHOLNPAIR("Error count on ", axis_codes[axis], " axis is ", encoders[idx].get_error_count());
|
||||
SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(axis_codes[axis]), " axis is ", encoders[idx].get_error_count());
|
||||
}
|
||||
|
||||
static void reset_error_count(const int8_t idx, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
encoders[idx].set_error_count(0);
|
||||
SERIAL_ECHOLNPAIR("Error count on ", axis_codes[axis], " axis has been reset.");
|
||||
SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(axis_codes[axis]), " axis has been reset.");
|
||||
}
|
||||
|
||||
static void enable_ec(const int8_t idx, const bool enabled, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
encoders[idx].set_ec_enabled(enabled);
|
||||
SERIAL_ECHOPAIR("Error correction on ", axis_codes[axis]);
|
||||
SERIAL_ECHOPGM("Error correction on ", AS_CHAR(axis_codes[axis]));
|
||||
SERIAL_ECHO_TERNARY(encoders[idx].get_ec_enabled(), " axis is ", "en", "dis", "abled.\n");
|
||||
}
|
||||
|
||||
static void set_ec_threshold(const int8_t idx, const float newThreshold, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
encoders[idx].set_ec_threshold(newThreshold);
|
||||
SERIAL_ECHOLNPAIR("Error correct threshold for ", axis_codes[axis], " axis set to ", FIXFLOAT(newThreshold), "mm.");
|
||||
SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(axis_codes[axis]), " axis set to ", newThreshold, "mm.");
|
||||
}
|
||||
|
||||
static void get_ec_threshold(const int8_t idx, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
const float threshold = encoders[idx].get_ec_threshold();
|
||||
SERIAL_ECHOLNPAIR("Error correct threshold for ", axis_codes[axis], " axis is ", FIXFLOAT(threshold), "mm.");
|
||||
SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(axis_codes[axis]), " axis is ", threshold, "mm.");
|
||||
}
|
||||
|
||||
static int8_t idx_from_axis(const AxisEnum axis) {
|
||||
|
||||
175
Marlin/src/feature/ethernet.cpp
Normal file
175
Marlin/src/feature/ethernet.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if HAS_ETHERNET
|
||||
|
||||
#include "ethernet.h"
|
||||
#include "../core/serial.h"
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_ETHERNET)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
bool MarlinEthernet::hardware_enabled, // = false
|
||||
MarlinEthernet::have_telnet_client; // = false
|
||||
|
||||
IPAddress MarlinEthernet::ip,
|
||||
MarlinEthernet::myDns,
|
||||
MarlinEthernet::gateway,
|
||||
MarlinEthernet::subnet;
|
||||
|
||||
EthernetClient MarlinEthernet::telnetClient; // connected client
|
||||
|
||||
MarlinEthernet ethernet;
|
||||
|
||||
EthernetServer server(23); // telnet server
|
||||
|
||||
enum linkStates { UNLINKED, LINKING, LINKED, CONNECTING, CONNECTED, NO_HARDWARE } linkState;
|
||||
|
||||
#ifdef __IMXRT1062__
|
||||
|
||||
static void teensyMAC(uint8_t * const mac) {
|
||||
const uint32_t m1 = HW_OCOTP_MAC1, m2 = HW_OCOTP_MAC0;
|
||||
mac[0] = m1 >> 8;
|
||||
mac[1] = m1 >> 0;
|
||||
mac[2] = m2 >> 24;
|
||||
mac[3] = m2 >> 16;
|
||||
mac[4] = m2 >> 8;
|
||||
mac[5] = m2 >> 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
byte mac[] = MAC_ADDRESS;
|
||||
|
||||
#endif
|
||||
|
||||
void ethernet_cable_error() { SERIAL_ERROR_MSG("Ethernet cable is not connected."); }
|
||||
|
||||
void MarlinEthernet::init() {
|
||||
if (!hardware_enabled) return;
|
||||
|
||||
SERIAL_ECHO_MSG("Starting network...");
|
||||
|
||||
// Init the Ethernet device
|
||||
#ifdef __IMXRT1062__
|
||||
uint8_t mac[6];
|
||||
teensyMAC(mac);
|
||||
#endif
|
||||
|
||||
if (!ip) {
|
||||
Ethernet.begin(mac); // use DHCP
|
||||
}
|
||||
else {
|
||||
if (!gateway) {
|
||||
gateway = ip;
|
||||
gateway[3] = 1;
|
||||
myDns = gateway;
|
||||
subnet = IPAddress(255,255,255,0);
|
||||
}
|
||||
if (!myDns) myDns = gateway;
|
||||
if (!subnet) subnet = IPAddress(255,255,255,0);
|
||||
Ethernet.begin(mac, ip, myDns, gateway, subnet);
|
||||
}
|
||||
|
||||
// Check for Ethernet hardware present
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||
SERIAL_ERROR_MSG("No Ethernet hardware found.");
|
||||
linkState = NO_HARDWARE;
|
||||
return;
|
||||
}
|
||||
|
||||
linkState = UNLINKED;
|
||||
|
||||
if (Ethernet.linkStatus() == LinkOFF)
|
||||
ethernet_cable_error();
|
||||
}
|
||||
|
||||
void MarlinEthernet::check() {
|
||||
if (!hardware_enabled) return;
|
||||
|
||||
switch (linkState) {
|
||||
case NO_HARDWARE:
|
||||
break;
|
||||
|
||||
case UNLINKED:
|
||||
if (Ethernet.linkStatus() == LinkOFF) break;
|
||||
|
||||
SERIAL_ECHOLNPGM("Ethernet cable connected");
|
||||
server.begin();
|
||||
linkState = LINKING;
|
||||
break;
|
||||
|
||||
case LINKING:
|
||||
if (!Ethernet.localIP()) break;
|
||||
|
||||
SERIAL_ECHOPGM("Successfully started telnet server with IP ");
|
||||
MYSERIAL1.println(Ethernet.localIP());
|
||||
|
||||
linkState = LINKED;
|
||||
break;
|
||||
|
||||
case LINKED:
|
||||
if (Ethernet.linkStatus() == LinkOFF) {
|
||||
ethernet_cable_error();
|
||||
linkState = UNLINKED;
|
||||
break;
|
||||
}
|
||||
telnetClient = server.accept();
|
||||
if (telnetClient) linkState = CONNECTING;
|
||||
break;
|
||||
|
||||
case CONNECTING:
|
||||
telnetClient.println("Marlin " SHORT_BUILD_VERSION);
|
||||
#if defined(STRING_DISTRIBUTION_DATE) && defined(STRING_CONFIG_H_AUTHOR)
|
||||
telnetClient.println(
|
||||
" Last Updated: " STRING_DISTRIBUTION_DATE
|
||||
" | Author: " STRING_CONFIG_H_AUTHOR
|
||||
);
|
||||
#endif
|
||||
telnetClient.println(" Compiled: " __DATE__);
|
||||
|
||||
SERIAL_ECHOLNPGM("Client connected");
|
||||
have_telnet_client = true;
|
||||
linkState = CONNECTED;
|
||||
break;
|
||||
|
||||
case CONNECTED:
|
||||
if (telnetClient && !telnetClient.connected()) {
|
||||
SERIAL_ECHOLNPGM("Client disconnected");
|
||||
telnetClient.stop();
|
||||
have_telnet_client = false;
|
||||
linkState = LINKED;
|
||||
}
|
||||
if (Ethernet.linkStatus() == LinkOFF) {
|
||||
ethernet_cable_error();
|
||||
if (telnetClient) telnetClient.stop();
|
||||
linkState = UNLINKED;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAS_ETHERNET
|
||||
39
Marlin/src/feature/ethernet.h
Normal file
39
Marlin/src/feature/ethernet.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef __IMXRT1062__
|
||||
#include <NativeEthernet.h>
|
||||
#endif
|
||||
|
||||
// Teensy 4.1 uses internal MAC Address
|
||||
|
||||
class MarlinEthernet {
|
||||
public:
|
||||
static bool hardware_enabled, have_telnet_client;
|
||||
static IPAddress ip, myDns, gateway, subnet;
|
||||
static EthernetClient telnetClient;
|
||||
static void init();
|
||||
static void check();
|
||||
};
|
||||
|
||||
extern MarlinEthernet ethernet;
|
||||
2
Marlin/src/feature/fanmux.cpp
Executable file → Normal file
2
Marlin/src/feature/fanmux.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
6
Marlin/src/feature/fanmux.h
Executable file → Normal file
6
Marlin/src/feature/fanmux.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -25,5 +25,5 @@
|
||||
* feature/fanmux.h - Cooling Fan Multiplexer support functions
|
||||
*/
|
||||
|
||||
extern void fanmux_switch(const uint8_t e);
|
||||
extern void fanmux_init();
|
||||
void fanmux_switch(const uint8_t e);
|
||||
void fanmux_init();
|
||||
|
||||
2
Marlin/src/feature/filwidth.cpp
Executable file → Normal file
2
Marlin/src/feature/filwidth.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
4
Marlin/src/feature/filwidth.h
Executable file → Normal file
4
Marlin/src/feature/filwidth.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
static inline void update_measured_mm() { measured_mm = raw_to_mm(); }
|
||||
|
||||
// Update ring buffer used to delay filament measurements
|
||||
static inline void advance_e(const float &e_move) {
|
||||
static inline void advance_e(const_float_t e_move) {
|
||||
|
||||
// Increment counters with the E distance
|
||||
e_count += e_move;
|
||||
|
||||
156
Marlin/src/feature/fwretract.cpp
Executable file → Normal file
156
Marlin/src/feature/fwretract.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -36,13 +36,15 @@ FWRetract fwretract; // Single instance - this calls the constructor
|
||||
#include "../module/planner.h"
|
||||
#include "../module/stepper.h"
|
||||
|
||||
#include "../gcode/gcode.h"
|
||||
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
#include "mixing.h"
|
||||
#endif
|
||||
|
||||
// private:
|
||||
|
||||
#if EXTRUDERS > 1
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
bool FWRetract::retracted_swap[EXTRUDERS]; // Which extruders are swap-retracted
|
||||
#endif
|
||||
|
||||
@@ -60,9 +62,7 @@ float FWRetract::current_retract[EXTRUDERS], // Retract value used by p
|
||||
FWRetract::current_hop;
|
||||
|
||||
void FWRetract::reset() {
|
||||
#if ENABLED(FWRETRACT_AUTORETRACT)
|
||||
autoretract_enabled = false;
|
||||
#endif
|
||||
TERN_(FWRETRACT_AUTORETRACT, autoretract_enabled = false);
|
||||
settings.retract_length = RETRACT_LENGTH;
|
||||
settings.retract_feedrate_mm_s = RETRACT_FEEDRATE;
|
||||
settings.retract_zraise = RETRACT_ZRAISE;
|
||||
@@ -75,9 +75,7 @@ void FWRetract::reset() {
|
||||
|
||||
LOOP_L_N(i, EXTRUDERS) {
|
||||
retracted[i] = false;
|
||||
#if EXTRUDERS > 1
|
||||
retracted_swap[i] = false;
|
||||
#endif
|
||||
E_TERN_(retracted_swap[i] = false);
|
||||
current_retract[i] = 0.0;
|
||||
}
|
||||
}
|
||||
@@ -93,16 +91,12 @@ void FWRetract::reset() {
|
||||
* Note: Auto-retract will apply the set Z hop in addition to any Z hop
|
||||
* included in the G-code. Use M207 Z0 to to prevent double hop.
|
||||
*/
|
||||
void FWRetract::retract(const bool retracting
|
||||
#if EXTRUDERS > 1
|
||||
, bool swapping /* =false */
|
||||
#endif
|
||||
) {
|
||||
void FWRetract::retract(const bool retracting E_OPTARG(bool swapping/*=false*/)) {
|
||||
// Prevent two retracts or recovers in a row
|
||||
if (retracted[active_extruder] == retracting) return;
|
||||
|
||||
// Prevent two swap-retract or recovers in a row
|
||||
#if EXTRUDERS > 1
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
// Allow G10 S1 only after G11
|
||||
if (swapping && retracted_swap[active_extruder] == retracting) return;
|
||||
// G11 priority to recover the long retract if activated
|
||||
@@ -112,28 +106,24 @@ void FWRetract::retract(const bool retracting
|
||||
#endif
|
||||
|
||||
/* // debugging
|
||||
SERIAL_ECHOLNPAIR(
|
||||
"retracting ", retracting,
|
||||
SERIAL_ECHOLNPGM(
|
||||
"retracting ", AS_DIGIT(retracting),
|
||||
" swapping ", swapping,
|
||||
" active extruder ", active_extruder
|
||||
);
|
||||
LOOP_L_N(i, EXTRUDERS) {
|
||||
SERIAL_ECHOLNPAIR("retracted[", i, "] ", retracted[i]);
|
||||
#if EXTRUDERS > 1
|
||||
SERIAL_ECHOLNPAIR("retracted_swap[", i, "] ", retracted_swap[i]);
|
||||
SERIAL_ECHOLNPGM("retracted[", i, "] ", AS_DIGIT(retracted[i]));
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
SERIAL_ECHOLNPGM("retracted_swap[", i, "] ", AS_DIGIT(retracted_swap[i]));
|
||||
#endif
|
||||
}
|
||||
SERIAL_ECHOLNPAIR("current_position.z ", current_position.z);
|
||||
SERIAL_ECHOLNPAIR("current_position.e ", current_position.e);
|
||||
SERIAL_ECHOLNPAIR("current_hop ", current_hop);
|
||||
SERIAL_ECHOLNPGM("current_position.z ", current_position.z);
|
||||
SERIAL_ECHOLNPGM("current_position.e ", current_position.e);
|
||||
SERIAL_ECHOLNPGM("current_hop ", current_hop);
|
||||
//*/
|
||||
|
||||
const float base_retract = (
|
||||
(swapping ? settings.swap_retract_length : settings.retract_length)
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
* (MIXING_STEPPERS)
|
||||
#endif
|
||||
);
|
||||
const float base_retract = TERN1(RETRACT_SYNC_MIXING, (MIXING_STEPPERS))
|
||||
* (swapping ? settings.swap_retract_length : settings.retract_length);
|
||||
|
||||
// The current position will be the destination for E and Z moves
|
||||
destination = current_position;
|
||||
@@ -147,11 +137,8 @@ void FWRetract::retract(const bool retracting
|
||||
if (retracting) {
|
||||
// Retract by moving from a faux E position back to the current E position
|
||||
current_retract[active_extruder] = base_retract;
|
||||
prepare_internal_move_to_destination( // set current to destination
|
||||
settings.retract_feedrate_mm_s
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
* (MIXING_STEPPERS)
|
||||
#endif
|
||||
prepare_internal_move_to_destination( // set current from destination
|
||||
settings.retract_feedrate_mm_s * TERN1(RETRACT_SYNC_MIXING, (MIXING_STEPPERS))
|
||||
);
|
||||
|
||||
// Is a Z hop set, and has the hop not yet been done?
|
||||
@@ -177,40 +164,107 @@ void FWRetract::retract(const bool retracting
|
||||
|
||||
current_retract[active_extruder] = 0;
|
||||
|
||||
const feedRate_t fr_mm_s = (
|
||||
// Recover E, set_current_to_destination
|
||||
prepare_internal_move_to_destination(
|
||||
(swapping ? settings.swap_retract_recover_feedrate_mm_s : settings.retract_recover_feedrate_mm_s)
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
* (MIXING_STEPPERS)
|
||||
#endif
|
||||
* TERN1(RETRACT_SYNC_MIXING, (MIXING_STEPPERS))
|
||||
);
|
||||
prepare_internal_move_to_destination(fr_mm_s); // Recover E, set_current_to_destination
|
||||
}
|
||||
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
mixer.T(old_mixing_tool); // Restore original mixing tool
|
||||
#endif
|
||||
TERN_(RETRACT_SYNC_MIXING, mixer.T(old_mixing_tool)); // Restore original mixing tool
|
||||
|
||||
retracted[active_extruder] = retracting; // Active extruder now retracted / recovered
|
||||
|
||||
// If swap retract/recover update the retracted_swap flag too
|
||||
#if EXTRUDERS > 1
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
if (swapping) retracted_swap[active_extruder] = retracting;
|
||||
#endif
|
||||
|
||||
/* // debugging
|
||||
SERIAL_ECHOLNPAIR("retracting ", retracting);
|
||||
SERIAL_ECHOLNPAIR("swapping ", swapping);
|
||||
SERIAL_ECHOLNPAIR("active_extruder ", active_extruder);
|
||||
SERIAL_ECHOLNPGM("retracting ", AS_DIGIT(retracting));
|
||||
SERIAL_ECHOLNPGM("swapping ", AS_DIGIT(swapping));
|
||||
SERIAL_ECHOLNPGM("active_extruder ", active_extruder);
|
||||
LOOP_L_N(i, EXTRUDERS) {
|
||||
SERIAL_ECHOLNPAIR("retracted[", i, "] ", retracted[i]);
|
||||
#if EXTRUDERS > 1
|
||||
SERIAL_ECHOLNPAIR("retracted_swap[", i, "] ", retracted_swap[i]);
|
||||
SERIAL_ECHOLNPGM("retracted[", i, "] ", AS_DIGIT(retracted[i]));
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
SERIAL_ECHOLNPGM("retracted_swap[", i, "] ", AS_DIGIT(retracted_swap[i]));
|
||||
#endif
|
||||
}
|
||||
SERIAL_ECHOLNPAIR("current_position.z ", current_position.z);
|
||||
SERIAL_ECHOLNPAIR("current_position.e ", current_position.e);
|
||||
SERIAL_ECHOLNPAIR("current_hop ", current_hop);
|
||||
SERIAL_ECHOLNPGM("current_position.z ", current_position.z);
|
||||
SERIAL_ECHOLNPGM("current_position.e ", current_position.e);
|
||||
SERIAL_ECHOLNPGM("current_hop ", current_hop);
|
||||
//*/
|
||||
}
|
||||
|
||||
//extern const char SP_Z_STR[];
|
||||
|
||||
/**
|
||||
* M207: Set firmware retraction values
|
||||
*
|
||||
* S[+units] retract_length
|
||||
* W[+units] swap_retract_length (multi-extruder)
|
||||
* F[units/min] retract_feedrate_mm_s
|
||||
* Z[units] retract_zraise
|
||||
*/
|
||||
void FWRetract::M207() {
|
||||
if (!parser.seen("FSWZ")) return M207_report();
|
||||
if (parser.seenval('S')) settings.retract_length = parser.value_axis_units(E_AXIS);
|
||||
if (parser.seenval('F')) settings.retract_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
|
||||
if (parser.seenval('Z')) settings.retract_zraise = parser.value_linear_units();
|
||||
if (parser.seenval('W')) settings.swap_retract_length = parser.value_axis_units(E_AXIS);
|
||||
}
|
||||
|
||||
void FWRetract::M207_report() {
|
||||
SERIAL_ECHOLNPGM_P(
|
||||
PSTR(" M207 S"), LINEAR_UNIT(settings.retract_length)
|
||||
, PSTR(" W"), LINEAR_UNIT(settings.swap_retract_length)
|
||||
, PSTR(" F"), LINEAR_UNIT(MMS_TO_MMM(settings.retract_feedrate_mm_s))
|
||||
, SP_Z_STR, LINEAR_UNIT(settings.retract_zraise)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* M208: Set firmware un-retraction values
|
||||
*
|
||||
* S[+units] retract_recover_extra (in addition to M207 S*)
|
||||
* W[+units] swap_retract_recover_extra (multi-extruder)
|
||||
* F[units/min] retract_recover_feedrate_mm_s
|
||||
* R[units/min] swap_retract_recover_feedrate_mm_s
|
||||
*/
|
||||
void FWRetract::M208() {
|
||||
if (!parser.seen("FSRW")) return M208_report();
|
||||
if (parser.seen('S')) settings.retract_recover_extra = parser.value_axis_units(E_AXIS);
|
||||
if (parser.seen('F')) settings.retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
|
||||
if (parser.seen('R')) settings.swap_retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
|
||||
if (parser.seen('W')) settings.swap_retract_recover_extra = parser.value_axis_units(E_AXIS);
|
||||
}
|
||||
|
||||
void FWRetract::M208_report() {
|
||||
SERIAL_ECHOLNPGM(
|
||||
" M208 S", LINEAR_UNIT(settings.retract_recover_extra)
|
||||
, " W", LINEAR_UNIT(settings.swap_retract_recover_extra)
|
||||
, " F", LINEAR_UNIT(MMS_TO_MMM(settings.retract_recover_feedrate_mm_s))
|
||||
);
|
||||
}
|
||||
|
||||
#if ENABLED(FWRETRACT_AUTORETRACT)
|
||||
|
||||
/**
|
||||
* M209: Enable automatic retract (M209 S1)
|
||||
* For slicers that don't support G10/11, reversed extrude-only
|
||||
* moves will be classified as retraction.
|
||||
*/
|
||||
void FWRetract::M209() {
|
||||
if (!parser.seen('S')) return M209_report();
|
||||
if (MIN_AUTORETRACT <= MAX_AUTORETRACT)
|
||||
enable_autoretract(parser.value_bool());
|
||||
}
|
||||
|
||||
void FWRetract::M209_report() {
|
||||
SERIAL_ECHOLNPGM(" M209 S", AS_DIGIT(autoretract_enabled));
|
||||
}
|
||||
|
||||
#endif // FWRETRACT_AUTORETRACT
|
||||
|
||||
|
||||
#endif // FWRETRACT
|
||||
|
||||
19
Marlin/src/feature/fwretract.h
Executable file → Normal file
19
Marlin/src/feature/fwretract.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -42,7 +42,7 @@ typedef struct {
|
||||
|
||||
class FWRetract {
|
||||
private:
|
||||
#if EXTRUDERS > 1
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
static bool retracted_swap[EXTRUDERS]; // Which extruders are swap-retracted
|
||||
#endif
|
||||
|
||||
@@ -74,11 +74,16 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
static void retract(const bool retracting
|
||||
#if EXTRUDERS > 1
|
||||
, bool swapping = false
|
||||
#endif
|
||||
);
|
||||
static void retract(const bool retracting E_OPTARG(bool swapping=false));
|
||||
|
||||
static void M207_report();
|
||||
static void M207();
|
||||
static void M208_report();
|
||||
static void M208();
|
||||
#if ENABLED(FWRETRACT_AUTORETRACT)
|
||||
static void M209_report();
|
||||
static void M209();
|
||||
#endif
|
||||
};
|
||||
|
||||
extern FWRetract fwretract;
|
||||
|
||||
80
Marlin/src/feature/host_actions.cpp
Executable file → Normal file
80
Marlin/src/feature/host_actions.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -37,9 +37,10 @@
|
||||
#include "runout.h"
|
||||
#endif
|
||||
|
||||
void host_action(const char * const pstr, const bool eol) {
|
||||
void host_action(PGM_P const pstr, const bool eol) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
SERIAL_ECHOPGM("//action:");
|
||||
serialprintPGM(pstr);
|
||||
SERIAL_ECHOPGM_P(pstr);
|
||||
if (eol) SERIAL_EOL();
|
||||
}
|
||||
|
||||
@@ -61,11 +62,14 @@ void host_action(const char * const pstr, const bool eol) {
|
||||
#ifdef ACTION_ON_CANCEL
|
||||
void host_action_cancel() { host_action(PSTR(ACTION_ON_CANCEL)); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_START
|
||||
void host_action_start() { host_action(PSTR(ACTION_ON_START)); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
|
||||
const char CONTINUE_STR[] PROGMEM = "Continue",
|
||||
DISMISS_STR[] PROGMEM = "Dismiss";
|
||||
PGMSTR(CONTINUE_STR, "Continue");
|
||||
PGMSTR(DISMISS_STR, "Dismiss");
|
||||
|
||||
#if HAS_RESUME_CONTINUE
|
||||
extern bool wait_for_user;
|
||||
@@ -74,45 +78,57 @@ void host_action(const char * const pstr, const bool eol) {
|
||||
PromptReason host_prompt_reason = PROMPT_NOT_DEFINED;
|
||||
|
||||
void host_action_notify(const char * const message) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
host_action(PSTR("notification "), false);
|
||||
serialprintPGM(message);
|
||||
SERIAL_EOL();
|
||||
SERIAL_ECHOLN(message);
|
||||
}
|
||||
|
||||
void host_action_prompt(const char * const ptype, const bool eol=true) {
|
||||
void host_action_notify_P(PGM_P const message) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
host_action(PSTR("notification "), false);
|
||||
SERIAL_ECHOLNPGM_P(message);
|
||||
}
|
||||
|
||||
void host_action_prompt(PGM_P const ptype, const bool eol=true) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
host_action(PSTR("prompt_"), false);
|
||||
serialprintPGM(ptype);
|
||||
SERIAL_ECHOPGM_P(ptype);
|
||||
if (eol) SERIAL_EOL();
|
||||
}
|
||||
|
||||
void host_action_prompt_plus(const char * const ptype, const char * const pstr, const char extra_char='\0') {
|
||||
void host_action_prompt_plus(PGM_P const ptype, PGM_P const pstr, const char extra_char='\0') {
|
||||
host_action_prompt(ptype, false);
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
SERIAL_CHAR(' ');
|
||||
serialprintPGM(pstr);
|
||||
SERIAL_ECHOPGM_P(pstr);
|
||||
if (extra_char != '\0') SERIAL_CHAR(extra_char);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
void host_action_prompt_begin(const PromptReason reason, const char * const pstr, const char extra_char/*='\0'*/) {
|
||||
void host_action_prompt_begin(const PromptReason reason, PGM_P const pstr, const char extra_char/*='\0'*/) {
|
||||
host_action_prompt_end();
|
||||
host_prompt_reason = reason;
|
||||
host_action_prompt_plus(PSTR("begin"), pstr, extra_char);
|
||||
}
|
||||
void host_action_prompt_button(const char * const pstr) { host_action_prompt_plus(PSTR("button"), pstr); }
|
||||
void host_action_prompt_button(PGM_P const pstr) { host_action_prompt_plus(PSTR("button"), pstr); }
|
||||
void host_action_prompt_end() { host_action_prompt(PSTR("end")); }
|
||||
void host_action_prompt_show() { host_action_prompt(PSTR("show")); }
|
||||
void host_prompt_do(const PromptReason reason, const char * const pstr, const char * const btn1/*=nullptr*/, const char * const btn2/*=nullptr*/) {
|
||||
host_action_prompt_begin(reason, pstr);
|
||||
|
||||
void _host_prompt_show(PGM_P const btn1/*=nullptr*/, PGM_P const btn2/*=nullptr*/) {
|
||||
if (btn1) host_action_prompt_button(btn1);
|
||||
if (btn2) host_action_prompt_button(btn2);
|
||||
host_action_prompt_show();
|
||||
}
|
||||
void host_prompt_do(const PromptReason reason, PGM_P const pstr, PGM_P const btn1/*=nullptr*/, PGM_P const btn2/*=nullptr*/) {
|
||||
host_action_prompt_begin(reason, pstr);
|
||||
_host_prompt_show(btn1, btn2);
|
||||
}
|
||||
void host_prompt_do(const PromptReason reason, PGM_P const pstr, const char extra_char, PGM_P const btn1/*=nullptr*/, PGM_P const btn2/*=nullptr*/) {
|
||||
host_action_prompt_begin(reason, pstr, extra_char);
|
||||
_host_prompt_show(btn1, btn2);
|
||||
}
|
||||
|
||||
void filament_load_host_prompt() {
|
||||
const bool disable_to_continue = (false
|
||||
#if HAS_FILAMENT_SENSOR
|
||||
|| runout.filament_ran_out
|
||||
#endif
|
||||
);
|
||||
const bool disable_to_continue = TERN0(HAS_FILAMENT_SENSOR, runout.filament_ran_out);
|
||||
host_prompt_do(PROMPT_FILAMENT_RUNOUT, PSTR("Paused"), PSTR("PurgeMore"),
|
||||
disable_to_continue ? PSTR("DisableRunout") : CONTINUE_STR
|
||||
);
|
||||
@@ -126,28 +142,20 @@ void host_action(const char * const pstr, const bool eol) {
|
||||
// - Dismissal of info
|
||||
//
|
||||
void host_response_handler(const uint8_t response) {
|
||||
#ifdef DEBUG_HOST_ACTIONS
|
||||
static const char m876_prefix[] PROGMEM = "M876 Handle Re";
|
||||
serialprintPGM(m876_prefix); SERIAL_ECHOLNPAIR("ason: ", host_prompt_reason);
|
||||
serialprintPGM(m876_prefix); SERIAL_ECHOLNPAIR("sponse: ", response);
|
||||
#endif
|
||||
const char *msg = PSTR("UNKNOWN STATE");
|
||||
const PromptReason hpr = host_prompt_reason;
|
||||
host_prompt_reason = PROMPT_NOT_DEFINED; // Reset now ahead of logic
|
||||
switch (hpr) {
|
||||
case PROMPT_FILAMENT_RUNOUT:
|
||||
msg = PSTR("FILAMENT_RUNOUT");
|
||||
switch (response) {
|
||||
|
||||
case 0: // "Purge More" button
|
||||
#if HAS_LCD_MENU && ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
#if BOTH(M600_PURGE_MORE_RESUMABLE, ADVANCED_PAUSE_FEATURE)
|
||||
pause_menu_response = PAUSE_RESPONSE_EXTRUDE_MORE; // Simulate menu selection (menu exits, doesn't extrude more)
|
||||
#endif
|
||||
filament_load_host_prompt(); // Initiate another host prompt. (NOTE: The loop in load_filament may also do this!)
|
||||
break;
|
||||
|
||||
case 1: // "Continue" / "Disable Runout" button
|
||||
#if HAS_LCD_MENU && ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
#if BOTH(M600_PURGE_MORE_RESUMABLE, ADVANCED_PAUSE_FEATURE)
|
||||
pause_menu_response = PAUSE_RESPONSE_RESUME_PRINT; // Simulate menu selection
|
||||
#endif
|
||||
#if HAS_FILAMENT_SENSOR
|
||||
@@ -160,26 +168,18 @@ void host_action(const char * const pstr, const bool eol) {
|
||||
}
|
||||
break;
|
||||
case PROMPT_USER_CONTINUE:
|
||||
#if HAS_RESUME_CONTINUE
|
||||
wait_for_user = false;
|
||||
#endif
|
||||
msg = PSTR("FILAMENT_RUNOUT_CONTINUE");
|
||||
TERN_(HAS_RESUME_CONTINUE, wait_for_user = false);
|
||||
break;
|
||||
case PROMPT_PAUSE_RESUME:
|
||||
msg = PSTR("LCD_PAUSE_RESUME");
|
||||
#if ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
#if BOTH(ADVANCED_PAUSE_FEATURE, SDSUPPORT)
|
||||
extern const char M24_STR[];
|
||||
queue.inject_P(M24_STR);
|
||||
#endif
|
||||
break;
|
||||
case PROMPT_INFO:
|
||||
msg = PSTR("GCODE_INFO");
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
SERIAL_ECHOPGM("M876 Responding PROMPT_");
|
||||
serialprintPGM(msg);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
#endif // HOST_PROMPT_SUPPORT
|
||||
|
||||
18
Marlin/src/feature/host_actions.h
Executable file → Normal file
18
Marlin/src/feature/host_actions.h
Executable file → Normal file
@@ -16,14 +16,15 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
#include "../HAL/shared/Marduino.h"
|
||||
|
||||
void host_action(const char * const pstr, const bool eol=true);
|
||||
void host_action(PGM_P const pstr, const bool eol=true);
|
||||
|
||||
#ifdef ACTION_ON_KILL
|
||||
void host_action_kill();
|
||||
@@ -43,6 +44,9 @@ void host_action(const char * const pstr, const bool eol=true);
|
||||
#ifdef ACTION_ON_CANCEL
|
||||
void host_action_cancel();
|
||||
#endif
|
||||
#ifdef ACTION_ON_START
|
||||
void host_action_start();
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
|
||||
@@ -61,12 +65,14 @@ void host_action(const char * const pstr, const bool eol=true);
|
||||
|
||||
void host_response_handler(const uint8_t response);
|
||||
void host_action_notify(const char * const message);
|
||||
void host_action_prompt_begin(const PromptReason reason, const char * const pstr, const char extra_char='\0');
|
||||
void host_action_prompt_button(const char * const pstr);
|
||||
void host_action_notify_P(PGM_P const message);
|
||||
void host_action_prompt_begin(const PromptReason reason, PGM_P const pstr, const char extra_char='\0');
|
||||
void host_action_prompt_button(PGM_P const pstr);
|
||||
void host_action_prompt_end();
|
||||
void host_action_prompt_show();
|
||||
void host_prompt_do(const PromptReason reason, const char * const pstr, const char * const btn1=nullptr, const char * const btn2=nullptr);
|
||||
inline void host_prompt_open(const PromptReason reason, const char * const pstr, const char * const btn1=nullptr, const char * const btn2=nullptr) {
|
||||
void host_prompt_do(const PromptReason reason, PGM_P const pstr, PGM_P const btn1=nullptr, PGM_P const btn2=nullptr);
|
||||
void host_prompt_do(const PromptReason reason, PGM_P const pstr, const char extra_char, PGM_P const btn1=nullptr, PGM_P const btn2=nullptr);
|
||||
inline void host_prompt_open(const PromptReason reason, PGM_P const pstr, PGM_P const btn1=nullptr, PGM_P const btn2=nullptr) {
|
||||
if (host_prompt_reason == PROMPT_NOT_DEFINED) host_prompt_do(reason, pstr, btn1, btn2);
|
||||
}
|
||||
|
||||
|
||||
27
Marlin/src/feature/joystick.cpp
Executable file → Normal file
27
Marlin/src/feature/joystick.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
#include "../inc/MarlinConfig.h" // for pins
|
||||
#include "../module/planner.h"
|
||||
#include "../module/temperature.h"
|
||||
|
||||
Joystick joystick;
|
||||
|
||||
@@ -69,13 +68,13 @@ Joystick joystick;
|
||||
void Joystick::report() {
|
||||
SERIAL_ECHOPGM("Joystick");
|
||||
#if HAS_JOY_ADC_X
|
||||
SERIAL_ECHOPAIR_P(SP_X_STR, JOY_X(x.raw));
|
||||
SERIAL_ECHOPGM_P(SP_X_STR, JOY_X(x.raw));
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Y
|
||||
SERIAL_ECHOPAIR_P(SP_Y_STR, JOY_Y(y.raw));
|
||||
SERIAL_ECHOPGM_P(SP_Y_STR, JOY_Y(y.raw));
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Z
|
||||
SERIAL_ECHOPAIR_P(SP_Z_STR, JOY_Z(z.raw));
|
||||
SERIAL_ECHOPGM_P(SP_Z_STR, JOY_Z(z.raw));
|
||||
#endif
|
||||
#if HAS_JOY_ADC_EN
|
||||
SERIAL_ECHO_TERNARY(READ(JOY_EN_PIN), " EN=", "HIGH (dis", "LOW (en", "abled)");
|
||||
@@ -127,6 +126,11 @@ Joystick joystick;
|
||||
static bool injecting_now; // = false;
|
||||
if (injecting_now) return;
|
||||
|
||||
#if ENABLED(NO_MOTION_BEFORE_HOMING)
|
||||
if (TERN0(HAS_JOY_ADC_X, axis_should_home(X_AXIS)) || TERN0(HAS_JOY_ADC_Y, axis_should_home(Y_AXIS)) || TERN0(HAS_JOY_ADC_Z, axis_should_home(Z_AXIS)))
|
||||
return;
|
||||
#endif
|
||||
|
||||
static constexpr int QUEUE_DEPTH = 5; // Insert up to this many movements
|
||||
static constexpr float target_lag = 0.25f, // Aim for 1/4 second lag
|
||||
seg_time = target_lag / QUEUE_DEPTH; // 0.05 seconds, short segments inserted every 1/20th of a second
|
||||
@@ -154,20 +158,13 @@ Joystick joystick;
|
||||
// Other non-joystick poll-based jogging could be implemented here
|
||||
// with "jogging" encapsulated as a more general class.
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::_joystick_update(norm_jog);
|
||||
#endif
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::_joystick_update(norm_jog));
|
||||
|
||||
// norm_jog values of [-1 .. 1] maps linearly to [-feedrate .. feedrate]
|
||||
xyz_float_t move_dist{0};
|
||||
float hypot2 = 0;
|
||||
LOOP_XYZ(i) if (norm_jog[i]) {
|
||||
move_dist[i] = seg_time * norm_jog[i] *
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
manual_feedrate_mm_s[i];
|
||||
#else
|
||||
planner.settings.max_feedrate_mm_s[i];
|
||||
#endif
|
||||
LOOP_LINEAR_AXES(i) if (norm_jog[i]) {
|
||||
move_dist[i] = seg_time * norm_jog[i] * TERN(EXTENSIBLE_UI, manual_feedrate_mm_s, planner.settings.max_feedrate_mm_s)[i];
|
||||
hypot2 += sq(move_dist[i]);
|
||||
}
|
||||
|
||||
|
||||
5
Marlin/src/feature/joystick.h
Executable file → Normal file
5
Marlin/src/feature/joystick.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -27,11 +27,8 @@
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
#include "../core/types.h"
|
||||
#include "../core/macros.h"
|
||||
#include "../module/temperature.h"
|
||||
|
||||
//#define JOYSTICK_DEBUG
|
||||
|
||||
class Joystick {
|
||||
friend class Temperature;
|
||||
private:
|
||||
|
||||
2
Marlin/src/feature/leds/blinkm.cpp
Executable file → Normal file
2
Marlin/src/feature/leds/blinkm.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
2
Marlin/src/feature/leds/blinkm.h
Executable file → Normal file
2
Marlin/src/feature/leds/blinkm.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
147
Marlin/src/feature/leds/leds.cpp
Executable file → Normal file
147
Marlin/src/feature/leds/leds.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -39,20 +39,22 @@
|
||||
#endif
|
||||
|
||||
#if ENABLED(PCA9533)
|
||||
#include <SailfishRGB_LED.h>
|
||||
#include "pca9533.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(CASE_LIGHT_USE_RGB_LED)
|
||||
#include "../../feature/caselight.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(LED_COLOR_PRESETS)
|
||||
const LEDColor LEDLights::defaultLEDColor = MakeLEDColor(
|
||||
LED_USER_PRESET_RED,
|
||||
LED_USER_PRESET_GREEN,
|
||||
LED_USER_PRESET_BLUE,
|
||||
LED_USER_PRESET_WHITE,
|
||||
LED_USER_PRESET_BRIGHTNESS
|
||||
const LEDColor LEDLights::defaultLEDColor = LEDColor(
|
||||
LED_USER_PRESET_RED, LED_USER_PRESET_GREEN, LED_USER_PRESET_BLUE
|
||||
OPTARG(HAS_WHITE_LED, LED_USER_PRESET_WHITE)
|
||||
OPTARG(NEOPIXEL_LED, LED_USER_PRESET_BRIGHTNESS)
|
||||
);
|
||||
#endif
|
||||
|
||||
#if EITHER(LED_CONTROL_MENU, PRINTER_EVENT_LEDS)
|
||||
#if ANY(LED_CONTROL_MENU, PRINTER_EVENT_LEDS, CASE_LIGHT_IS_COLOR_LED)
|
||||
LEDColor LEDLights::color;
|
||||
bool LEDLights::lights_on;
|
||||
#endif
|
||||
@@ -68,45 +70,41 @@ void LEDLights::setup() {
|
||||
if (PWM_PIN(RGB_LED_W_PIN)) SET_PWM(RGB_LED_W_PIN); else SET_OUTPUT(RGB_LED_W_PIN);
|
||||
#endif
|
||||
#endif
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
neo.init();
|
||||
#endif
|
||||
#if ENABLED(PCA9533)
|
||||
RGBinit();
|
||||
#endif
|
||||
#if ENABLED(LED_USER_PRESET_STARTUP)
|
||||
set_default();
|
||||
#endif
|
||||
TERN_(NEOPIXEL_LED, neo.init());
|
||||
TERN_(PCA9533, PCA9533_init());
|
||||
TERN_(LED_USER_PRESET_STARTUP, set_default());
|
||||
}
|
||||
|
||||
void LEDLights::set_color(const LEDColor &incol
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, bool isSequence/*=false*/
|
||||
#endif
|
||||
OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence/*=false*/)
|
||||
) {
|
||||
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
|
||||
const uint32_t neocolor = LEDColorWhite() == incol
|
||||
? neo.Color(NEO_WHITE)
|
||||
: neo.Color(incol.r, incol.g, incol.b, incol.w);
|
||||
static uint16_t nextLed = 0;
|
||||
: neo.Color(incol.r, incol.g, incol.b OPTARG(HAS_WHITE_LED, incol.w));
|
||||
|
||||
#ifdef NEOPIXEL_BKGD_LED_INDEX
|
||||
if (NEOPIXEL_BKGD_LED_INDEX == nextLed) {
|
||||
if (++nextLed >= neo.pixels()) nextLed = 0;
|
||||
return;
|
||||
}
|
||||
#if ENABLED(NEOPIXEL_IS_SEQUENTIAL)
|
||||
static uint16_t nextLed = 0;
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
while (WITHIN(nextLed, NEOPIXEL_BKGD_INDEX_FIRST, NEOPIXEL_BKGD_INDEX_LAST)) {
|
||||
neo.reset_background_color();
|
||||
if (++nextLed >= neo.pixels()) { nextLed = 0; return; }
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
neo.set_brightness(incol.i);
|
||||
|
||||
if (isSequence) {
|
||||
neo.set_pixel_color(nextLed, neocolor);
|
||||
neo.show();
|
||||
if (++nextLed >= neo.pixels()) nextLed = 0;
|
||||
return;
|
||||
}
|
||||
#if ENABLED(NEOPIXEL_IS_SEQUENTIAL)
|
||||
if (isSequence) {
|
||||
neo.set_pixel_color(nextLed, neocolor);
|
||||
neo.show();
|
||||
if (++nextLed >= neo.pixels()) nextLed = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
neo.set_color(neocolor);
|
||||
|
||||
@@ -123,26 +121,23 @@ void LEDLights::set_color(const LEDColor &incol
|
||||
|
||||
// This variant uses 3-4 separate pins for the RGB(W) components.
|
||||
// If the pins can do PWM then their intensity will be set.
|
||||
#define UPDATE_RGBW(C,c) do { if (PWM_PIN(RGB_LED_##C##_PIN)) \
|
||||
analogWrite(pin_t(RGB_LED_##C##_PIN), incol.c); \
|
||||
else WRITE(RGB_LED_##C##_PIN, incol.c ? HIGH : LOW); }while(0)
|
||||
UPDATE_RGBW(R,r);
|
||||
UPDATE_RGBW(G,g);
|
||||
UPDATE_RGBW(B,b);
|
||||
#define _UPDATE_RGBW(C,c) do { \
|
||||
if (PWM_PIN(RGB_LED_##C##_PIN)) \
|
||||
analogWrite(pin_t(RGB_LED_##C##_PIN), c); \
|
||||
else \
|
||||
WRITE(RGB_LED_##C##_PIN, c ? HIGH : LOW); \
|
||||
}while(0)
|
||||
#define UPDATE_RGBW(C,c) _UPDATE_RGBW(C, TERN1(CASE_LIGHT_USE_RGB_LED, caselight.on) ? incol.c : 0)
|
||||
UPDATE_RGBW(R,r); UPDATE_RGBW(G,g); UPDATE_RGBW(B,b);
|
||||
#if ENABLED(RGBW_LED)
|
||||
UPDATE_RGBW(W,w);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(PCA9632)
|
||||
// Update I2C LED driver
|
||||
pca9632_set_led_color(incol);
|
||||
#endif
|
||||
|
||||
#if ENABLED(PCA9533)
|
||||
RGBsetColor(incol.r, incol.g, incol.b, true);
|
||||
#endif
|
||||
// Update I2C LED driver
|
||||
TERN_(PCA9632, PCA9632_set_led_color(incol));
|
||||
TERN_(PCA9533, PCA9533_set_rgb(incol.r, incol.g, incol.b));
|
||||
|
||||
#if EITHER(LED_CONTROL_MENU, PRINTER_EVENT_LEDS)
|
||||
// Don't update the color when OFF
|
||||
@@ -160,13 +155,57 @@ void LEDLights::set_color(const LEDColor &incol
|
||||
millis_t LEDLights::led_off_time; // = 0
|
||||
|
||||
void LEDLights::update_timeout(const bool power_on) {
|
||||
const millis_t ms = millis();
|
||||
if (power_on)
|
||||
reset_timeout(ms);
|
||||
else if (ELAPSED(ms, led_off_time))
|
||||
set_off();
|
||||
if (lights_on) {
|
||||
const millis_t ms = millis();
|
||||
if (power_on)
|
||||
reset_timeout(ms);
|
||||
else if (ELAPSED(ms, led_off_time))
|
||||
set_off();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // HAS_COLOR_LEDS
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
|
||||
#if ENABLED(NEO2_COLOR_PRESETS)
|
||||
const LEDColor LEDLights2::defaultLEDColor = LEDColor(
|
||||
LED_USER_PRESET_RED, LED_USER_PRESET_GREEN, LED_USER_PRESET_BLUE
|
||||
OPTARG(HAS_WHITE_LED2, LED_USER_PRESET_WHITE)
|
||||
OPTARG(NEOPIXEL_LED, LED_USER_PRESET_BRIGHTNESS)
|
||||
);
|
||||
#endif
|
||||
|
||||
#if ENABLED(LED_CONTROL_MENU)
|
||||
LEDColor LEDLights2::color;
|
||||
bool LEDLights2::lights_on;
|
||||
#endif
|
||||
|
||||
LEDLights2 leds2;
|
||||
|
||||
void LEDLights2::setup() {
|
||||
neo2.init();
|
||||
TERN_(NEO2_USER_PRESET_STARTUP, set_default());
|
||||
}
|
||||
|
||||
void LEDLights2::set_color(const LEDColor &incol) {
|
||||
const uint32_t neocolor = LEDColorWhite() == incol
|
||||
? neo2.Color(NEO2_WHITE)
|
||||
: neo2.Color(incol.r, incol.g, incol.b OPTARG(HAS_WHITE_LED2, incol.w));
|
||||
neo2.set_brightness(incol.i);
|
||||
neo2.set_color(neocolor);
|
||||
|
||||
#if ENABLED(LED_CONTROL_MENU)
|
||||
// Don't update the color when OFF
|
||||
lights_on = !incol.is_off();
|
||||
if (lights_on) color = incol;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLED(LED_CONTROL_MENU)
|
||||
void LEDLights2::toggle() { if (lights_on) set_off(); else update(); }
|
||||
#endif
|
||||
|
||||
#endif // NEOPIXEL2_SEPARATE
|
||||
|
||||
#endif // HAS_COLOR_LEDS
|
||||
|
||||
153
Marlin/src/feature/leds/leds.h
Executable file → Normal file
153
Marlin/src/feature/leds/leds.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -29,65 +29,42 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
#include "neopixel.h"
|
||||
// A white component can be passed
|
||||
#if EITHER(RGBW_LED, PCA9632_RGBW)
|
||||
#define HAS_WHITE_LED 1
|
||||
#endif
|
||||
|
||||
// A white component can be passed
|
||||
#define HAS_WHITE_LED EITHER(RGBW_LED, NEOPIXEL_LED)
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
#define _NEOPIXEL_INCLUDE_
|
||||
#include "neopixel.h"
|
||||
#undef _NEOPIXEL_INCLUDE_
|
||||
#endif
|
||||
|
||||
/**
|
||||
* LEDcolor type for use with leds.set_color
|
||||
*/
|
||||
typedef struct LEDColor {
|
||||
uint8_t r, g, b
|
||||
#if HAS_WHITE_LED
|
||||
, w
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, i
|
||||
#endif
|
||||
#endif
|
||||
OPTARG(HAS_WHITE_LED, w)
|
||||
OPTARG(NEOPIXEL_LED, i)
|
||||
;
|
||||
|
||||
LEDColor() : r(255), g(255), b(255)
|
||||
#if HAS_WHITE_LED
|
||||
, w(255)
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, i(NEOPIXEL_BRIGHTNESS)
|
||||
#endif
|
||||
#endif
|
||||
OPTARG(HAS_WHITE_LED, w(255))
|
||||
OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS))
|
||||
{}
|
||||
|
||||
LEDColor(uint8_t r, uint8_t g, uint8_t b
|
||||
#if HAS_WHITE_LED
|
||||
, uint8_t w=0
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, uint8_t i=NEOPIXEL_BRIGHTNESS
|
||||
#endif
|
||||
#endif
|
||||
) : r(r), g(g), b(b)
|
||||
#if HAS_WHITE_LED
|
||||
, w(w)
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, i(i)
|
||||
#endif
|
||||
#endif
|
||||
{}
|
||||
LEDColor(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED, uint8_t w=0) OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS))
|
||||
: r(r), g(g), b(b) OPTARG(HAS_WHITE_LED, w(w)) OPTARG(NEOPIXEL_LED, i(i)) {}
|
||||
|
||||
LEDColor(const uint8_t (&rgbw)[4]) : r(rgbw[0]), g(rgbw[1]), b(rgbw[2])
|
||||
#if HAS_WHITE_LED
|
||||
, w(rgbw[3])
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, i(NEOPIXEL_BRIGHTNESS)
|
||||
#endif
|
||||
#endif
|
||||
OPTARG(HAS_WHITE_LED, w(rgbw[3]))
|
||||
OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS))
|
||||
{}
|
||||
|
||||
LEDColor& operator=(const uint8_t (&rgbw)[4]) {
|
||||
r = rgbw[0]; g = rgbw[1]; b = rgbw[2];
|
||||
#if HAS_WHITE_LED
|
||||
w = rgbw[3];
|
||||
#endif
|
||||
TERN_(HAS_WHITE_LED, w = rgbw[3]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -104,26 +81,13 @@ typedef struct LEDColor {
|
||||
bool operator!=(const LEDColor &right) { return !operator==(right); }
|
||||
|
||||
bool is_off() const {
|
||||
return 3 > r + g + b
|
||||
#if HAS_WHITE_LED
|
||||
+ w
|
||||
#endif
|
||||
;
|
||||
return 3 > r + g + b + TERN0(HAS_WHITE_LED, w);
|
||||
}
|
||||
} LEDColor;
|
||||
|
||||
/**
|
||||
* Color helpers and presets
|
||||
* Color presets
|
||||
*/
|
||||
#if HAS_WHITE_LED
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
#define MakeLEDColor(R,G,B,W,I) LEDColor(R, G, B, W, I)
|
||||
#else
|
||||
#define MakeLEDColor(R,G,B,W,I) LEDColor(R, G, B, W)
|
||||
#endif
|
||||
#else
|
||||
#define MakeLEDColor(R,G,B,W,I) LEDColor(R, G, B)
|
||||
#endif
|
||||
|
||||
#define LEDColorOff() LEDColor( 0, 0, 0)
|
||||
#define LEDColorRed() LEDColor(255, 0, 0)
|
||||
@@ -151,27 +115,15 @@ public:
|
||||
static void setup(); // init()
|
||||
|
||||
static void set_color(const LEDColor &color
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, bool isSequence=false
|
||||
#endif
|
||||
OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence=false)
|
||||
);
|
||||
|
||||
inline void set_color(uint8_t r, uint8_t g, uint8_t b
|
||||
#if HAS_WHITE_LED
|
||||
, uint8_t w=0
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, uint8_t i=NEOPIXEL_BRIGHTNESS
|
||||
#endif
|
||||
#endif
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, bool isSequence=false
|
||||
#endif
|
||||
static inline void set_color(uint8_t r, uint8_t g, uint8_t b
|
||||
OPTARG(HAS_WHITE_LED, uint8_t w=0)
|
||||
OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS)
|
||||
OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence=false)
|
||||
) {
|
||||
set_color(MakeLEDColor(r, g, b, w, i)
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
, isSequence
|
||||
#endif
|
||||
);
|
||||
set_color(LEDColor(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, i)) OPTARG(NEOPIXEL_IS_SEQUENTIAL, isSequence));
|
||||
}
|
||||
|
||||
static inline void set_off() { set_color(LEDColorOff()); }
|
||||
@@ -193,13 +145,15 @@ public:
|
||||
static inline LEDColor get_color() { return lights_on ? color : LEDColorOff(); }
|
||||
#endif
|
||||
|
||||
#if EITHER(LED_CONTROL_MENU, PRINTER_EVENT_LEDS)
|
||||
#if ANY(LED_CONTROL_MENU, PRINTER_EVENT_LEDS, CASE_LIGHT_IS_COLOR_LED)
|
||||
static LEDColor color; // last non-off color
|
||||
static bool lights_on; // the last set color was "on"
|
||||
#endif
|
||||
|
||||
#if ENABLED(LED_CONTROL_MENU)
|
||||
static void toggle(); // swap "off" with color
|
||||
#endif
|
||||
#if EITHER(LED_CONTROL_MENU, CASE_LIGHT_USE_RGB_LED)
|
||||
static inline void update() { set_color(color); }
|
||||
#endif
|
||||
|
||||
@@ -209,10 +163,57 @@ public:
|
||||
public:
|
||||
static inline void reset_timeout(const millis_t &ms) {
|
||||
led_off_time = ms + LED_BACKLIGHT_TIMEOUT;
|
||||
if (!lights_on) set_default();
|
||||
if (!lights_on) update();
|
||||
}
|
||||
static void update_timeout(const bool power_on);
|
||||
#endif
|
||||
};
|
||||
|
||||
extern LEDLights leds;
|
||||
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
|
||||
class LEDLights2 {
|
||||
public:
|
||||
LEDLights2() {}
|
||||
|
||||
static void setup(); // init()
|
||||
|
||||
static void set_color(const LEDColor &color);
|
||||
|
||||
static inline void set_color(uint8_t r, uint8_t g, uint8_t b
|
||||
OPTARG(HAS_WHITE_LED, uint8_t w=0)
|
||||
OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS)
|
||||
) {
|
||||
set_color(LEDColor(r, g, b
|
||||
OPTARG(HAS_WHITE_LED, w)
|
||||
OPTARG(NEOPIXEL_LED, i)
|
||||
));
|
||||
}
|
||||
|
||||
static inline void set_off() { set_color(LEDColorOff()); }
|
||||
static inline void set_green() { set_color(LEDColorGreen()); }
|
||||
static inline void set_white() { set_color(LEDColorWhite()); }
|
||||
|
||||
#if ENABLED(NEO2_COLOR_PRESETS)
|
||||
static const LEDColor defaultLEDColor;
|
||||
static inline void set_default() { set_color(defaultLEDColor); }
|
||||
static inline void set_red() { set_color(LEDColorRed()); }
|
||||
static inline void set_orange() { set_color(LEDColorOrange()); }
|
||||
static inline void set_yellow() { set_color(LEDColorYellow()); }
|
||||
static inline void set_blue() { set_color(LEDColorBlue()); }
|
||||
static inline void set_indigo() { set_color(LEDColorIndigo()); }
|
||||
static inline void set_violet() { set_color(LEDColorViolet()); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
static LEDColor color; // last non-off color
|
||||
static bool lights_on; // the last set color was "on"
|
||||
static void toggle(); // swap "off" with color
|
||||
static inline void update() { set_color(color); }
|
||||
#endif
|
||||
};
|
||||
|
||||
extern LEDLights2 leds2;
|
||||
|
||||
#endif // NEOPIXEL2_SEPARATE
|
||||
|
||||
147
Marlin/src/feature/leds/neopixel.cpp
Executable file → Normal file
147
Marlin/src/feature/leds/neopixel.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -28,38 +28,50 @@
|
||||
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
|
||||
#include "neopixel.h"
|
||||
#include "leds.h"
|
||||
|
||||
#if ENABLED(NEOPIXEL_STARTUP_TEST)
|
||||
#if EITHER(NEOPIXEL_STARTUP_TEST, NEOPIXEL2_STARTUP_TEST)
|
||||
#include "../../core/utility.h"
|
||||
#endif
|
||||
|
||||
Marlin_NeoPixel neo;
|
||||
int8_t Marlin_NeoPixel::neoindex;
|
||||
|
||||
Adafruit_NeoPixel Marlin_NeoPixel::adaneo1(NEOPIXEL_PIXELS, NEOPIXEL_PIN, NEOPIXEL_TYPE + NEO_KHZ800)
|
||||
#if MULTIPLE_NEOPIXEL_TYPES
|
||||
, Marlin_NeoPixel::adaneo2(NEOPIXEL_PIXELS, NEOPIXEL2_PIN, NEOPIXEL2_TYPE + NEO_KHZ800)
|
||||
#endif
|
||||
;
|
||||
Adafruit_NeoPixel Marlin_NeoPixel::adaneo1(NEOPIXEL_PIXELS, NEOPIXEL_PIN, NEOPIXEL_TYPE + NEO_KHZ800);
|
||||
#if CONJOINED_NEOPIXEL
|
||||
Adafruit_NeoPixel Marlin_NeoPixel::adaneo2(NEOPIXEL_PIXELS, NEOPIXEL2_PIN, NEOPIXEL2_TYPE + NEO_KHZ800);
|
||||
#endif
|
||||
|
||||
#ifdef NEOPIXEL_BKGD_LED_INDEX
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
|
||||
void Marlin_NeoPixel::set_color_background() {
|
||||
uint8_t background_color[4] = NEOPIXEL_BKGD_COLOR;
|
||||
set_pixel_color(NEOPIXEL_BKGD_LED_INDEX, adaneo1.Color(background_color[0], background_color[1], background_color[2], background_color[3]));
|
||||
void Marlin_NeoPixel::set_background_color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
for (int background_led = NEOPIXEL_BKGD_INDEX_FIRST; background_led <= NEOPIXEL_BKGD_INDEX_LAST; background_led++)
|
||||
set_pixel_color(background_led, adaneo1.Color(r, g, b, w));
|
||||
}
|
||||
|
||||
void Marlin_NeoPixel::reset_background_color() {
|
||||
constexpr uint8_t background_color[4] = NEOPIXEL_BKGD_COLOR;
|
||||
set_background_color(background_color[0], background_color[1], background_color[2], background_color[3]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Marlin_NeoPixel::set_color(const uint32_t color) {
|
||||
for (uint16_t i = 0; i < pixels(); ++i) {
|
||||
#ifdef NEOPIXEL_BKGD_LED_INDEX
|
||||
if (i == NEOPIXEL_BKGD_LED_INDEX && color != 0x000000) {
|
||||
set_color_background();
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
set_pixel_color(i, color);
|
||||
if (neoindex >= 0) {
|
||||
set_pixel_color(neoindex, color);
|
||||
neoindex = -1;
|
||||
}
|
||||
else {
|
||||
for (uint16_t i = 0; i < pixels(); ++i) {
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
if (i == NEOPIXEL_BKGD_INDEX_FIRST && TERN(NEOPIXEL_BKGD_ALWAYS_ON, true, color != 0x000000)) {
|
||||
reset_background_color();
|
||||
i += NEOPIXEL_BKGD_INDEX_LAST - (NEOPIXEL_BKGD_INDEX_FIRST);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
set_pixel_color(i, color);
|
||||
}
|
||||
}
|
||||
show();
|
||||
}
|
||||
@@ -71,47 +83,86 @@ void Marlin_NeoPixel::set_color_startup(const uint32_t color) {
|
||||
}
|
||||
|
||||
void Marlin_NeoPixel::init() {
|
||||
SET_OUTPUT(NEOPIXEL_PIN);
|
||||
set_brightness(NEOPIXEL_BRIGHTNESS); // 0 - 255 range
|
||||
neoindex = -1; // -1 .. NEOPIXEL_PIXELS-1 range
|
||||
set_brightness(NEOPIXEL_BRIGHTNESS); // 0 .. 255 range
|
||||
begin();
|
||||
show(); // initialize to all off
|
||||
|
||||
#if ENABLED(NEOPIXEL_STARTUP_TEST)
|
||||
safe_delay(1000);
|
||||
set_color_startup(adaneo1.Color(255, 0, 0, 0)); // red
|
||||
safe_delay(1000);
|
||||
safe_delay(500);
|
||||
set_color_startup(adaneo1.Color(0, 255, 0, 0)); // green
|
||||
safe_delay(1000);
|
||||
safe_delay(500);
|
||||
set_color_startup(adaneo1.Color(0, 0, 255, 0)); // blue
|
||||
safe_delay(1000);
|
||||
safe_delay(500);
|
||||
#if HAS_WHITE_LED
|
||||
set_color_startup(adaneo1.Color(0, 0, 0, 255)); // white
|
||||
safe_delay(500);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef NEOPIXEL_BKGD_LED_INDEX
|
||||
set_color_background();
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
reset_background_color();
|
||||
#endif
|
||||
|
||||
#if ENABLED(LED_USER_PRESET_STARTUP)
|
||||
set_color(adaneo1.Color(LED_USER_PRESET_RED, LED_USER_PRESET_GREEN, LED_USER_PRESET_BLUE, LED_USER_PRESET_WHITE));
|
||||
#else
|
||||
set_color(adaneo1.Color(0, 0, 0, 0));
|
||||
#endif
|
||||
set_color(adaneo1.Color
|
||||
TERN(LED_USER_PRESET_STARTUP,
|
||||
(LED_USER_PRESET_RED, LED_USER_PRESET_GREEN, LED_USER_PRESET_BLUE, LED_USER_PRESET_WHITE),
|
||||
(0, 0, 0, 0))
|
||||
);
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool Marlin_NeoPixel::set_led_color(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t w, const uint8_t p) {
|
||||
const uint32_t color = adaneo1.Color(r, g, b, w);
|
||||
set_brightness(p);
|
||||
#if DISABLED(NEOPIXEL_IS_SEQUENTIAL)
|
||||
set_color(color);
|
||||
return false;
|
||||
#else
|
||||
static uint16_t nextLed = 0;
|
||||
set_pixel_color(nextLed, color);
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
|
||||
Marlin_NeoPixel2 neo2;
|
||||
|
||||
int8_t Marlin_NeoPixel2::neoindex;
|
||||
Adafruit_NeoPixel Marlin_NeoPixel2::adaneo(NEOPIXEL2_PIXELS, NEOPIXEL2_PIN, NEOPIXEL2_TYPE);
|
||||
|
||||
void Marlin_NeoPixel2::set_color(const uint32_t color) {
|
||||
if (neoindex >= 0) {
|
||||
set_pixel_color(neoindex, color);
|
||||
neoindex = -1;
|
||||
}
|
||||
else {
|
||||
for (uint16_t i = 0; i < pixels(); ++i)
|
||||
set_pixel_color(i, color);
|
||||
}
|
||||
show();
|
||||
if (++nextLed >= pixels()) nextLed = 0;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Marlin_NeoPixel2::set_color_startup(const uint32_t color) {
|
||||
for (uint16_t i = 0; i < pixels(); ++i)
|
||||
set_pixel_color(i, color);
|
||||
show();
|
||||
}
|
||||
|
||||
void Marlin_NeoPixel2::init() {
|
||||
neoindex = -1; // -1 .. NEOPIXEL2_PIXELS-1 range
|
||||
set_brightness(NEOPIXEL2_BRIGHTNESS); // 0 .. 255 range
|
||||
begin();
|
||||
show(); // initialize to all off
|
||||
|
||||
#if ENABLED(NEOPIXEL2_STARTUP_TEST)
|
||||
set_color_startup(adaneo.Color(255, 0, 0, 0)); // red
|
||||
safe_delay(500);
|
||||
set_color_startup(adaneo.Color(0, 255, 0, 0)); // green
|
||||
safe_delay(500);
|
||||
set_color_startup(adaneo.Color(0, 0, 255, 0)); // blue
|
||||
safe_delay(500);
|
||||
#if HAS_WHITE_LED2
|
||||
set_color_startup(adaneo.Color(0, 0, 0, 255)); // white
|
||||
safe_delay(500);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
set_color(adaneo.Color
|
||||
TERN(NEO2_USER_PRESET_STARTUP,
|
||||
(NEO2_USER_PRESET_RED, NEO2_USER_PRESET_GREEN, NEO2_USER_PRESET_BLUE, NEO2_USER_PRESET_WHITE),
|
||||
(0, 0, 0, 0))
|
||||
);
|
||||
}
|
||||
|
||||
#endif // NEOPIXEL2_SEPARATE
|
||||
|
||||
#endif // NEOPIXEL_LED
|
||||
|
||||
126
Marlin/src/feature/leds/neopixel.h
Executable file → Normal file
126
Marlin/src/feature/leds/neopixel.h
Executable file → Normal file
@@ -16,15 +16,19 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Neopixel support
|
||||
* NeoPixel support
|
||||
*/
|
||||
|
||||
#ifndef _NEOPIXEL_INCLUDE_
|
||||
#error "Always include 'leds.h' and not 'neopixel.h' directly."
|
||||
#endif
|
||||
|
||||
// ------------------------
|
||||
// Includes
|
||||
// ------------------------
|
||||
@@ -38,15 +42,24 @@
|
||||
// Defines
|
||||
// ------------------------
|
||||
|
||||
#define MULTIPLE_NEOPIXEL_TYPES (defined(NEOPIXEL2_TYPE) && (NEOPIXEL2_TYPE != NEOPIXEL_TYPE))
|
||||
#define _NEO_IS_RGB(N) (N == NEO_RGB || N == NEO_RBG || N == NEO_GRB || N == NEO_GBR || N == NEO_BRG || N == NEO_BGR)
|
||||
|
||||
#define NEOPIXEL_IS_RGB (NEOPIXEL_TYPE == NEO_RGB || NEOPIXEL_TYPE == NEO_RBG || NEOPIXEL_TYPE == NEO_GRB || NEOPIXEL_TYPE == NEO_GBR || NEOPIXEL_TYPE == NEO_BRG || NEOPIXEL_TYPE == NEO_BGR)
|
||||
#define NEOPIXEL_IS_RGBW !NEOPIXEL_IS_RGB
|
||||
#if !_NEO_IS_RGB(NEOPIXEL_TYPE)
|
||||
#define HAS_WHITE_LED 1
|
||||
#endif
|
||||
|
||||
#if NEOPIXEL_IS_RGB
|
||||
#define NEO_WHITE 255, 255, 255, 0
|
||||
#else
|
||||
#if HAS_WHITE_LED
|
||||
#define NEO_WHITE 0, 0, 0, 255
|
||||
#else
|
||||
#define NEO_WHITE 255, 255, 255
|
||||
#endif
|
||||
|
||||
#if defined(NEOPIXEL2_TYPE) && NEOPIXEL2_TYPE != NEOPIXEL_TYPE && DISABLED(NEOPIXEL2_SEPARATE)
|
||||
#define MULTIPLE_NEOPIXEL_TYPES 1
|
||||
#endif
|
||||
|
||||
#if EITHER(MULTIPLE_NEOPIXEL_TYPES, NEOPIXEL2_INSERIES)
|
||||
#define CONJOINED_NEOPIXEL 1
|
||||
#endif
|
||||
|
||||
// ------------------------
|
||||
@@ -55,66 +68,113 @@
|
||||
|
||||
class Marlin_NeoPixel {
|
||||
private:
|
||||
static Adafruit_NeoPixel adaneo1
|
||||
#if MULTIPLE_NEOPIXEL_TYPES
|
||||
, adaneo2
|
||||
#endif
|
||||
;
|
||||
static Adafruit_NeoPixel adaneo1;
|
||||
#if CONJOINED_NEOPIXEL
|
||||
static Adafruit_NeoPixel adaneo2;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static int8_t neoindex;
|
||||
|
||||
static void init();
|
||||
static void set_color_startup(const uint32_t c);
|
||||
|
||||
static void set_color(const uint32_t c);
|
||||
|
||||
#ifdef NEOPIXEL_BKGD_LED_INDEX
|
||||
static void set_color_background();
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
static void set_background_color(uint8_t r, uint8_t g, uint8_t b, uint8_t w);
|
||||
static void reset_background_color();
|
||||
#endif
|
||||
|
||||
static inline void begin() {
|
||||
adaneo1.begin();
|
||||
#if MULTIPLE_NEOPIXEL_TYPES
|
||||
adaneo2.begin();
|
||||
#endif
|
||||
TERN_(CONJOINED_NEOPIXEL, adaneo2.begin());
|
||||
}
|
||||
|
||||
static inline void set_pixel_color(const uint16_t n, const uint32_t c) {
|
||||
adaneo1.setPixelColor(n, c);
|
||||
#if MULTIPLE_NEOPIXEL_TYPES
|
||||
adaneo2.setPixelColor(n, c);
|
||||
#if ENABLED(NEOPIXEL2_INSERIES)
|
||||
if (n >= NEOPIXEL_PIXELS) adaneo2.setPixelColor(n - (NEOPIXEL_PIXELS), c);
|
||||
else adaneo1.setPixelColor(n, c);
|
||||
#else
|
||||
adaneo1.setPixelColor(n, c);
|
||||
TERN_(MULTIPLE_NEOPIXEL_TYPES, adaneo2.setPixelColor(n, c));
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void set_brightness(const uint8_t b) {
|
||||
adaneo1.setBrightness(b);
|
||||
#if MULTIPLE_NEOPIXEL_TYPES
|
||||
adaneo2.setBrightness(b);
|
||||
#endif
|
||||
TERN_(CONJOINED_NEOPIXEL, adaneo2.setBrightness(b));
|
||||
}
|
||||
|
||||
static inline void show() {
|
||||
// Some platforms cannot maintain PWM output when NeoPixel disables interrupts for long durations.
|
||||
TERN_(HAS_PAUSE_SERVO_OUTPUT, PAUSE_SERVO_OUTPUT());
|
||||
adaneo1.show();
|
||||
#if PIN_EXISTS(NEOPIXEL2)
|
||||
#if MULTIPLE_NEOPIXEL_TYPES
|
||||
#if CONJOINED_NEOPIXEL
|
||||
adaneo2.show();
|
||||
#else
|
||||
adaneo1.setPin(NEOPIXEL2_PIN);
|
||||
adaneo1.show();
|
||||
adaneo1.setPin(NEOPIXEL_PIN);
|
||||
#endif
|
||||
#endif
|
||||
TERN_(HAS_PAUSE_SERVO_OUTPUT, RESUME_SERVO_OUTPUT());
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool set_led_color(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t w, const uint8_t p);
|
||||
#endif
|
||||
|
||||
// Accessors
|
||||
static inline uint16_t pixels() { return adaneo1.numPixels(); }
|
||||
static inline uint16_t pixels() { return adaneo1.numPixels() * TERN1(NEOPIXEL2_INSERIES, 2); }
|
||||
|
||||
static inline uint8_t brightness() { return adaneo1.getBrightness(); }
|
||||
static inline uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
return adaneo1.Color(r, g, b, w);
|
||||
|
||||
static inline uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED, uint8_t w)) {
|
||||
return adaneo1.Color(r, g, b OPTARG(HAS_WHITE_LED, w));
|
||||
}
|
||||
};
|
||||
|
||||
extern Marlin_NeoPixel neo;
|
||||
|
||||
// Neo pixel channel 2
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
|
||||
#if _NEO_IS_RGB(NEOPIXEL2_TYPE)
|
||||
#define NEOPIXEL2_IS_RGB 1
|
||||
#define NEO2_WHITE 255, 255, 255
|
||||
#else
|
||||
#define NEOPIXEL2_IS_RGBW 1
|
||||
#define HAS_WHITE_LED2 1 // A white component can be passed for NEOPIXEL2
|
||||
#define NEO2_WHITE 0, 0, 0, 255
|
||||
#endif
|
||||
|
||||
class Marlin_NeoPixel2 {
|
||||
private:
|
||||
static Adafruit_NeoPixel adaneo;
|
||||
|
||||
public:
|
||||
static int8_t neoindex;
|
||||
|
||||
static void init();
|
||||
static void set_color_startup(const uint32_t c);
|
||||
|
||||
static void set_color(const uint32_t c);
|
||||
|
||||
static inline void begin() { adaneo.begin(); }
|
||||
static inline void set_pixel_color(const uint16_t n, const uint32_t c) { adaneo.setPixelColor(n, c); }
|
||||
static inline void set_brightness(const uint8_t b) { adaneo.setBrightness(b); }
|
||||
static inline void show() {
|
||||
adaneo.show();
|
||||
adaneo.setPin(NEOPIXEL2_PIN);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
static inline uint16_t pixels() { return adaneo.numPixels();}
|
||||
static inline uint8_t brightness() { return adaneo.getBrightness(); }
|
||||
static inline uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED2, uint8_t w)) {
|
||||
return adaneo.Color(r, g, b OPTARG(HAS_WHITE_LED2, w));
|
||||
}
|
||||
};
|
||||
|
||||
extern Marlin_NeoPixel2 neo2;
|
||||
|
||||
#endif // NEOPIXEL2_SEPARATE
|
||||
|
||||
#undef _NEO_IS_RGB
|
||||
|
||||
127
Marlin/src/feature/leds/pca9533.cpp
Normal file
127
Marlin/src/feature/leds/pca9533.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* PCA9533 LED controller driver (MightyBoard, FlashForge Creator Pro, etc.)
|
||||
* by @grauerfuchs - 1 Apr 2020
|
||||
*/
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(PCA9533)
|
||||
|
||||
#include "pca9533.h"
|
||||
#include <Wire.h>
|
||||
|
||||
void PCA9533_init() {
|
||||
Wire.begin();
|
||||
PCA9533_reset();
|
||||
}
|
||||
|
||||
static void PCA9533_writeAllRegisters(uint8_t psc0, uint8_t pwm0, uint8_t psc1, uint8_t pwm1, uint8_t ls0) {
|
||||
uint8_t data[6] = { PCA9533_REG_PSC0 | PCA9533_REGM_AI, psc0, pwm0, psc1, pwm1, ls0 };
|
||||
Wire.beginTransmission(PCA9533_Addr >> 1);
|
||||
Wire.write(data, 6);
|
||||
Wire.endTransmission();
|
||||
delayMicroseconds(1);
|
||||
}
|
||||
|
||||
static void PCA9533_writeRegister(uint8_t reg, uint8_t val) {
|
||||
uint8_t data[2] = { reg, val };
|
||||
Wire.beginTransmission(PCA9533_Addr >> 1);
|
||||
Wire.write(data, 2);
|
||||
Wire.endTransmission();
|
||||
delayMicroseconds(1);
|
||||
}
|
||||
|
||||
// Reset (clear) all registers
|
||||
void PCA9533_reset() {
|
||||
PCA9533_writeAllRegisters(0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Turn all LEDs off
|
||||
void PCA9533_setOff() {
|
||||
PCA9533_writeRegister(PCA9533_REG_SEL, 0);
|
||||
}
|
||||
|
||||
void PCA9533_set_rgb(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
uint8_t r_pwm0 = 0; // Register data - PWM value
|
||||
uint8_t r_pwm1 = 0; // Register data - PWM value
|
||||
|
||||
uint8_t op_g = 0, op_r = 0, op_b = 0; // Opcodes - Green, Red, Blue
|
||||
|
||||
// Light theory! GREEN takes priority because
|
||||
// it's the most visible to the human eye.
|
||||
if (green == 0) op_g = PCA9533_LED_OP_OFF;
|
||||
else if (green == 255) op_g = PCA9533_LED_OP_ON;
|
||||
else { r_pwm0 = green; op_g = PCA9533_LED_OP_PWM0; }
|
||||
|
||||
// RED
|
||||
if (red == 0) op_r = PCA9533_LED_OP_OFF;
|
||||
else if (red == 255) op_r = PCA9533_LED_OP_ON;
|
||||
else if (r_pwm0 == 0 || r_pwm0 == red) {
|
||||
r_pwm0 = red; op_r = PCA9533_LED_OP_PWM0;
|
||||
}
|
||||
else {
|
||||
r_pwm1 = red; op_r = PCA9533_LED_OP_PWM1;
|
||||
}
|
||||
|
||||
// BLUE
|
||||
if (blue == 0) op_b = PCA9533_LED_OP_OFF;
|
||||
else if (blue == 255) op_b = PCA9533_LED_OP_ON;
|
||||
else if (r_pwm0 == 0 || r_pwm0 == blue) {
|
||||
r_pwm0 = blue; op_b = PCA9533_LED_OP_PWM0;
|
||||
}
|
||||
else if (r_pwm1 == 0 || r_pwm1 == blue) {
|
||||
r_pwm1 = blue; op_b = PCA9533_LED_OP_PWM1;
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Conflict. 3 values are requested but only 2 channels exist.
|
||||
* G is on channel 0 and R is on channel 1, so work from there.
|
||||
* Find the closest match, average the values, then use the free channel.
|
||||
*/
|
||||
uint8_t dgb = ABS(green - blue),
|
||||
dgr = ABS(green - red),
|
||||
dbr = ABS(blue - red);
|
||||
if (dgb < dgr && dgb < dbr) { // Mix with G on channel 0.
|
||||
op_b = PCA9533_LED_OP_PWM0;
|
||||
r_pwm0 = uint8_t(((uint16_t)green + (uint16_t)blue) / 2);
|
||||
}
|
||||
else if (dbr <= dgr && dbr <= dgb) { // Mix with R on channel 1.
|
||||
op_b = PCA9533_LED_OP_PWM1;
|
||||
r_pwm1 = uint8_t(((uint16_t)red + (uint16_t)blue) / 2);
|
||||
}
|
||||
else { // Mix R+G on 0 and put B on 1.
|
||||
op_r = PCA9533_LED_OP_PWM0;
|
||||
r_pwm0 = uint8_t(((uint16_t)green + (uint16_t)red) / 2);
|
||||
op_b = PCA9533_LED_OP_PWM1;
|
||||
r_pwm1 = blue;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the changes to the hardware
|
||||
PCA9533_writeAllRegisters(0, r_pwm0, 0, r_pwm1,
|
||||
(op_g << PCA9533_LED_OFS_GRN) | (op_r << PCA9533_LED_OFS_RED) | (op_b << PCA9533_LED_OFS_BLU)
|
||||
);
|
||||
}
|
||||
|
||||
#endif // PCA9533
|
||||
59
Marlin/src/feature/leds/pca9533.h
Normal file
59
Marlin/src/feature/leds/pca9533.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Driver for the PCA9533 LED controller found on the MightyBoard
|
||||
* used by FlashForge Creator Pro, MakerBot, etc.
|
||||
* Written 2020 APR 01 by grauerfuchs
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
|
||||
#define ENABLE_I2C_PULLUPS
|
||||
|
||||
// Chip address (for Wire)
|
||||
#define PCA9533_Addr 0xC4
|
||||
|
||||
// Control registers
|
||||
#define PCA9533_REG_READ 0x00
|
||||
#define PCA9533_REG_PSC0 0x01
|
||||
#define PCA9533_REG_PWM0 0x02
|
||||
#define PCA9533_REG_PSC1 0x03
|
||||
#define PCA9533_REG_PWM1 0x04
|
||||
#define PCA9533_REG_SEL 0x05
|
||||
#define PCA9533_REGM_AI 0x10
|
||||
|
||||
// LED selector operation
|
||||
#define PCA9533_LED_OP_OFF 0B00
|
||||
#define PCA9533_LED_OP_ON 0B01
|
||||
#define PCA9533_LED_OP_PWM0 0B10
|
||||
#define PCA9533_LED_OP_PWM1 0B11
|
||||
|
||||
// Select register bit offsets for LED colors
|
||||
#define PCA9533_LED_OFS_RED 0
|
||||
#define PCA9533_LED_OFS_GRN 2
|
||||
#define PCA9533_LED_OFS_BLU 4
|
||||
|
||||
void PCA9533_init();
|
||||
void PCA9533_reset();
|
||||
void PCA9533_set_rgb(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void PCA9533_setOff();
|
||||
48
Marlin/src/feature/leds/pca9632.cpp
Executable file → Normal file
48
Marlin/src/feature/leds/pca9632.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
#define PCA9632_AUTOGLO 0xC0
|
||||
#define PCA9632_AUTOGI 0xE0
|
||||
|
||||
// Red=LED0 Green=LED1 Blue=LED2
|
||||
// Red=LED0 Green=LED1 Blue=LED2 White=LED3
|
||||
#ifndef PCA9632_RED
|
||||
#define PCA9632_RED 0x00
|
||||
#endif
|
||||
@@ -68,9 +68,12 @@
|
||||
#ifndef PCA9632_BLU
|
||||
#define PCA9632_BLU 0x04
|
||||
#endif
|
||||
#if HAS_WHITE_LED && !defined(PCA9632_WHT)
|
||||
#define PCA9632_WHT 0x06
|
||||
#endif
|
||||
|
||||
// If any of the color indexes are greater than 0x04 they can't use auto increment
|
||||
#if !defined(PCA9632_NO_AUTO_INC) && (PCA9632_RED > 0x04 || PCA9632_GRN > 0x04 || PCA9632_BLU > 0x04)
|
||||
#if !defined(PCA9632_NO_AUTO_INC) && (PCA9632_RED > 0x04 || PCA9632_GRN > 0x04 || PCA9632_BLU > 0x04 || PCA9632_WHT > 0x04)
|
||||
#define PCA9632_NO_AUTO_INC
|
||||
#endif
|
||||
|
||||
@@ -89,25 +92,26 @@ static void PCA9632_WriteRegister(const byte addr, const byte regadd, const byte
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
static void PCA9632_WriteAllRegisters(const byte addr, const byte regadd, const byte vr, const byte vg, const byte vb) {
|
||||
static void PCA9632_WriteAllRegisters(const byte addr, const byte regadd, const byte vr, const byte vg, const byte vb
|
||||
OPTARG(PCA9632_RGBW, const byte vw)
|
||||
) {
|
||||
#if DISABLED(PCA9632_NO_AUTO_INC)
|
||||
uint8_t data[4], len = 4;
|
||||
uint8_t data[4];
|
||||
data[0] = PCA9632_AUTO_IND | regadd;
|
||||
data[1 + (PCA9632_RED >> 1)] = vr;
|
||||
data[1 + (PCA9632_GRN >> 1)] = vg;
|
||||
data[1 + (PCA9632_BLU >> 1)] = vb;
|
||||
Wire.beginTransmission(I2C_ADDRESS(addr));
|
||||
Wire.write(data, sizeof(data));
|
||||
Wire.endTransmission();
|
||||
#else
|
||||
uint8_t data[6], len = 6;
|
||||
data[0] = regadd + (PCA9632_RED >> 1);
|
||||
data[1] = vr;
|
||||
data[2] = regadd + (PCA9632_GRN >> 1);
|
||||
data[3] = vg;
|
||||
data[4] = regadd + (PCA9632_BLU >> 1);
|
||||
data[5] = vb;
|
||||
PCA9632_WriteRegister(addr, regadd + (PCA9632_RED >> 1), vr);
|
||||
PCA9632_WriteRegister(addr, regadd + (PCA9632_GRN >> 1), vg);
|
||||
PCA9632_WriteRegister(addr, regadd + (PCA9632_BLU >> 1), vb);
|
||||
#if ENABLED(PCA9632_RGBW)
|
||||
PCA9632_WriteRegister(addr, regadd + (PCA9632_WHT >> 1), vw);
|
||||
#endif
|
||||
#endif
|
||||
Wire.beginTransmission(I2C_ADDRESS(addr));
|
||||
Wire.write(data, len);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -120,7 +124,7 @@ static void PCA9632_WriteAllRegisters(const byte addr, const byte regadd, const
|
||||
}
|
||||
#endif
|
||||
|
||||
void pca9632_set_led_color(const LEDColor &color) {
|
||||
void PCA9632_set_led_color(const LEDColor &color) {
|
||||
Wire.begin();
|
||||
if (!PCA_init) {
|
||||
PCA_init = 1;
|
||||
@@ -130,15 +134,21 @@ void pca9632_set_led_color(const LEDColor &color) {
|
||||
|
||||
const byte LEDOUT = (color.r ? LED_PWM << PCA9632_RED : 0)
|
||||
| (color.g ? LED_PWM << PCA9632_GRN : 0)
|
||||
| (color.b ? LED_PWM << PCA9632_BLU : 0);
|
||||
| (color.b ? LED_PWM << PCA9632_BLU : 0)
|
||||
#if ENABLED(PCA9632_RGBW)
|
||||
| (color.w ? LED_PWM << PCA9632_WHT : 0)
|
||||
#endif
|
||||
;
|
||||
|
||||
PCA9632_WriteAllRegisters(PCA9632_ADDRESS,PCA9632_PWM0, color.r, color.g, color.b);
|
||||
PCA9632_WriteAllRegisters(PCA9632_ADDRESS,PCA9632_PWM0, color.r, color.g, color.b
|
||||
OPTARG(PCA9632_RGBW, color.w)
|
||||
);
|
||||
PCA9632_WriteRegister(PCA9632_ADDRESS,PCA9632_LEDOUT, LEDOUT);
|
||||
}
|
||||
|
||||
#if ENABLED(PCA9632_BUZZER)
|
||||
|
||||
void pca9632_buzz(const long, const uint16_t) {
|
||||
void PCA9632_buzz(const long, const uint16_t) {
|
||||
uint8_t data[] = PCA9632_BUZZER_DATA;
|
||||
Wire.beginTransmission(I2C_ADDRESS(PCA9632_ADDRESS));
|
||||
Wire.write(data, sizeof(data));
|
||||
|
||||
6
Marlin/src/feature/leds/pca9632.h
Executable file → Normal file
6
Marlin/src/feature/leds/pca9632.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -29,9 +29,9 @@
|
||||
struct LEDColor;
|
||||
typedef LEDColor LEDColor;
|
||||
|
||||
void pca9632_set_led_color(const LEDColor &color);
|
||||
void PCA9632_set_led_color(const LEDColor &color);
|
||||
|
||||
#if ENABLED(PCA9632_BUZZER)
|
||||
#include <stdint.h>
|
||||
void pca9632_buzz(const long, const uint16_t);
|
||||
void PCA9632_buzz(const long, const uint16_t);
|
||||
#endif
|
||||
|
||||
36
Marlin/src/feature/leds/printer_event_leds.cpp
Executable file → Normal file
36
Marlin/src/feature/leds/printer_event_leds.cpp
Executable file → Normal file
@@ -16,12 +16,12 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* printer_event_leds.cpp - LED color changing based on printer status
|
||||
* feature/leds/printer_event_leds.cpp - LED color changing based on printer status
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
@@ -40,24 +40,23 @@ PrinterEventLEDs printerEventLEDs;
|
||||
|
||||
uint8_t PrinterEventLEDs::old_intensity = 0;
|
||||
|
||||
inline uint8_t pel_intensity(const float &start, const float ¤t, const float &target) {
|
||||
return (uint8_t)map(constrain(current, start, target), start, target, 0.f, 255.f);
|
||||
inline uint8_t pel_intensity(const celsius_t start, const celsius_t current, const celsius_t target) {
|
||||
if (start == target) return 255;
|
||||
return (uint8_t)map(constrain(current, start, target), start, target, 0, 255);
|
||||
}
|
||||
|
||||
inline void pel_set_rgb(const uint8_t r, const uint8_t g, const uint8_t b) {
|
||||
inline void pel_set_rgb(const uint8_t r, const uint8_t g, const uint8_t b OPTARG(HAS_WHITE_LED, const uint8_t w=0)) {
|
||||
leds.set_color(
|
||||
MakeLEDColor(r, g, b, 0, neo.brightness())
|
||||
#if ENABLED(NEOPIXEL_IS_SEQUENTIAL)
|
||||
, true
|
||||
#endif
|
||||
);
|
||||
LEDColor(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, neo.brightness()))
|
||||
OPTARG(NEOPIXEL_IS_SEQUENTIAL, true)
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if HAS_TEMP_HOTEND
|
||||
|
||||
void PrinterEventLEDs::onHotendHeating(const float &start, const float ¤t, const float &target) {
|
||||
void PrinterEventLEDs::onHotendHeating(const celsius_t start, const celsius_t current, const celsius_t target) {
|
||||
const uint8_t blue = pel_intensity(start, current, target);
|
||||
if (blue != old_intensity) {
|
||||
old_intensity = blue;
|
||||
@@ -69,13 +68,26 @@ PrinterEventLEDs printerEventLEDs;
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
|
||||
void PrinterEventLEDs::onBedHeating(const float &start, const float ¤t, const float &target) {
|
||||
void PrinterEventLEDs::onBedHeating(const celsius_t start, const celsius_t current, const celsius_t target) {
|
||||
const uint8_t red = pel_intensity(start, current, target);
|
||||
if (red != old_intensity) {
|
||||
old_intensity = red;
|
||||
pel_set_rgb(red, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_CHAMBER
|
||||
|
||||
void PrinterEventLEDs::onChamberHeating(const celsius_t start, const celsius_t current, const celsius_t target) {
|
||||
const uint8_t green = pel_intensity(start, current, target);
|
||||
if (green != old_intensity) {
|
||||
old_intensity = green;
|
||||
pel_set_rgb(255, green, 255);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // PRINTER_EVENT_LEDS
|
||||
|
||||
25
Marlin/src/feature/leds/printer_event_leds.h
Executable file → Normal file
25
Marlin/src/feature/leds/printer_event_leds.h
Executable file → Normal file
@@ -16,13 +16,13 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* printer_event_leds.h - LED color changing based on printer status
|
||||
* feature/leds/printer_event_leds.h - LED color changing based on printer status
|
||||
*/
|
||||
|
||||
#include "leds.h"
|
||||
@@ -36,27 +36,26 @@ private:
|
||||
static bool leds_off_after_print;
|
||||
#endif
|
||||
|
||||
static inline void set_done() {
|
||||
#if ENABLED(LED_COLOR_PRESETS)
|
||||
leds.set_default();
|
||||
#else
|
||||
leds.set_off();
|
||||
#endif
|
||||
}
|
||||
static inline void set_done() { TERN(LED_COLOR_PRESETS, leds.set_default(), leds.set_off()); }
|
||||
|
||||
public:
|
||||
#if HAS_TEMP_HOTEND
|
||||
static inline LEDColor onHotendHeatingStart() { old_intensity = 0; return leds.get_color(); }
|
||||
static void onHotendHeating(const float &start, const float ¤t, const float &target);
|
||||
static void onHotendHeating(const celsius_t start, const celsius_t current, const celsius_t target);
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
static inline LEDColor onBedHeatingStart() { old_intensity = 127; return leds.get_color(); }
|
||||
static void onBedHeating(const float &start, const float ¤t, const float &target);
|
||||
static void onBedHeating(const celsius_t start, const celsius_t current, const celsius_t target);
|
||||
#endif
|
||||
|
||||
#if HAS_TEMP_HOTEND || HAS_HEATED_BED
|
||||
static inline void onHeatingDone() { leds.set_white(); }
|
||||
#if HAS_HEATED_CHAMBER
|
||||
static inline LEDColor onChamberHeatingStart() { old_intensity = 127; return leds.get_color(); }
|
||||
static void onChamberHeating(const celsius_t start, const celsius_t current, const celsius_t target);
|
||||
#endif
|
||||
|
||||
#if HAS_TEMP_HOTEND || HAS_HEATED_BED || HAS_HEATED_CHAMBER
|
||||
static inline void onHeatingDone() { leds.set_white(); }
|
||||
static inline void onPidTuningDone(LEDColor c) { leds.set_color(c); }
|
||||
#endif
|
||||
|
||||
|
||||
11
Marlin/src/feature/leds/tempstat.cpp
Executable file → Normal file
11
Marlin/src/feature/leds/tempstat.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -36,13 +36,10 @@ void handle_status_leds() {
|
||||
static millis_t next_status_led_update_ms = 0;
|
||||
if (ELAPSED(millis(), next_status_led_update_ms)) {
|
||||
next_status_led_update_ms += 500; // Update every 0.5s
|
||||
float max_temp = 0.0;
|
||||
#if HAS_HEATED_BED
|
||||
max_temp = _MAX(thermalManager.degTargetBed(), thermalManager.degBed());
|
||||
#endif
|
||||
celsius_t max_temp = TERN0(HAS_HEATED_BED, _MAX(thermalManager.degTargetBed(), thermalManager.wholeDegBed()));
|
||||
HOTEND_LOOP()
|
||||
max_temp = _MAX(max_temp, thermalManager.degHotend(e), thermalManager.degTargetHotend(e));
|
||||
const int8_t new_red = (max_temp > 55.0) ? HIGH : (max_temp < 54.0 || old_red < 0) ? LOW : old_red;
|
||||
max_temp = _MAX(max_temp, thermalManager.wholeDegHotend(e), thermalManager.degTargetHotend(e));
|
||||
const int8_t new_red = (max_temp > 55) ? HIGH : (max_temp < 54 || old_red < 0) ? LOW : old_red;
|
||||
if (new_red != old_red) {
|
||||
old_red = new_red;
|
||||
#if PIN_EXISTS(STAT_LED_RED)
|
||||
|
||||
2
Marlin/src/feature/leds/tempstat.h
Executable file → Normal file
2
Marlin/src/feature/leds/tempstat.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
8
Marlin/src/feature/max7219.cpp
Executable file → Normal file
8
Marlin/src/feature/max7219.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -127,10 +127,10 @@ uint8_t Max7219::suspended; // = 0;
|
||||
void Max7219::error(const char * const func, const int32_t v1, const int32_t v2/*=-1*/) {
|
||||
#if ENABLED(MAX7219_ERRORS)
|
||||
SERIAL_ECHOPGM("??? Max7219::");
|
||||
serialprintPGM(func);
|
||||
SERIAL_ECHOPGM_P(func);
|
||||
SERIAL_CHAR('(');
|
||||
SERIAL_ECHO(v1);
|
||||
if (v2 > 0) SERIAL_ECHOPAIR(", ", v2);
|
||||
if (v2 > 0) SERIAL_ECHOPGM(", ", v2);
|
||||
SERIAL_CHAR(')');
|
||||
SERIAL_EOL();
|
||||
#else
|
||||
@@ -256,7 +256,7 @@ void Max7219::set(const uint8_t line, const uint8_t bits) {
|
||||
}
|
||||
|
||||
// Draw a float with a decimal point and optional digits
|
||||
void Max7219::print(const uint8_t start, const float value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false) {
|
||||
void Max7219::print(const uint8_t start, const_float_t value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false) {
|
||||
if (pre_size) print(start, value, pre_size, leadzero, !!post_size);
|
||||
if (post_size) {
|
||||
const int16_t after = ABS(value) * (10 ^ post_size);
|
||||
|
||||
9
Marlin/src/feature/max7219.h
Executable file → Normal file
9
Marlin/src/feature/max7219.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -100,6 +100,13 @@ public:
|
||||
// Update a single native line on just one unit
|
||||
static void refresh_unit_line(const uint8_t line);
|
||||
|
||||
#if ENABLED(MAX7219_NUMERIC)
|
||||
// Draw an integer with optional leading zeros and optional decimal point
|
||||
void print(const uint8_t start, int16_t value, uint8_t size, const bool leadzero=false, bool dec=false);
|
||||
// Draw a float with a decimal point and optional digits
|
||||
void print(const uint8_t start, const_float_t value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false);
|
||||
#endif
|
||||
|
||||
// Set a single LED by XY coordinate
|
||||
static void led_set(const uint8_t x, const uint8_t y, const bool on);
|
||||
static void led_on(const uint8_t x, const uint8_t y);
|
||||
|
||||
@@ -39,10 +39,9 @@
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(MEATPACK)
|
||||
#if HAS_MEATPACK
|
||||
|
||||
#include "meatpack.h"
|
||||
MeatPack meatpack;
|
||||
|
||||
#define MeatPack_ProtocolVersion "PV01"
|
||||
//#define MP_DEBUG
|
||||
@@ -50,30 +49,24 @@ MeatPack meatpack;
|
||||
#define DEBUG_OUT ENABLED(MP_DEBUG)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
bool MeatPack::cmd_is_next = false; // A command is pending
|
||||
uint8_t MeatPack::state = 0; // Configuration state OFF
|
||||
uint8_t MeatPack::second_char = 0; // The unpacked 2nd character from an out-of-sequence packed pair
|
||||
uint8_t MeatPack::cmd_count = 0, // Counts how many command bytes are received (need 2)
|
||||
MeatPack::full_char_count = 0, // Counts how many full-width characters are to be received
|
||||
MeatPack::char_out_count = 0; // Stores number of characters to be read out.
|
||||
uint8_t MeatPack::char_out_buf[2]; // Output buffer for caching up to 2 characters
|
||||
|
||||
// The 15 most-common characters used in G-code, ~90-95% of all G-code uses these characters
|
||||
// Stored in SRAM for performance.
|
||||
uint8_t meatPackLookupTable[16] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'.', ' ', '\n', 'G', 'X',
|
||||
'\0' // Unused. 0b1111 indicates a literal character
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'.', ' ', '\n', 'G', 'X',
|
||||
'\0' // Unused. 0b1111 indicates a literal character
|
||||
};
|
||||
|
||||
TERN_(MP_DEBUG, uint8_t chars_decoded = 0); // Log the first 64 bytes after each reset
|
||||
#if ENABLED(MP_DEBUG)
|
||||
uint8_t chars_decoded = 0; // Log the first 64 bytes after each reset
|
||||
#endif
|
||||
|
||||
void MeatPack::reset_state() {
|
||||
state = 0;
|
||||
cmd_is_next = false;
|
||||
second_char = 0;
|
||||
cmd_count = full_char_count = char_out_count = 0;
|
||||
TERN_(MP_DEBUG, chars_decoded = 0);
|
||||
state = 0;
|
||||
cmd_is_next = false;
|
||||
second_char = 0;
|
||||
cmd_count = full_char_count = char_out_count = 0;
|
||||
TERN_(MP_DEBUG, chars_decoded = 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,25 +74,25 @@ void MeatPack::reset_state() {
|
||||
* Return flags indicating whether any literal bytes follow.
|
||||
*/
|
||||
uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out) {
|
||||
uint8_t out = 0;
|
||||
uint8_t out = 0;
|
||||
|
||||
// If lower nybble is 1111, the higher nybble is unused, and next char is full.
|
||||
if ((pk & kFirstNotPacked) == kFirstNotPacked)
|
||||
out = kFirstCharIsLiteral;
|
||||
else {
|
||||
const uint8_t chr = pk & 0x0F;
|
||||
chars_out[0] = meatPackLookupTable[chr]; // Set the first char
|
||||
}
|
||||
// If lower nybble is 1111, the higher nybble is unused, and next char is full.
|
||||
if ((pk & kFirstNotPacked) == kFirstNotPacked)
|
||||
out = kFirstCharIsLiteral;
|
||||
else {
|
||||
const uint8_t chr = pk & 0x0F;
|
||||
chars_out[0] = meatPackLookupTable[chr]; // Set the first char
|
||||
}
|
||||
|
||||
// Check if upper nybble is 1111... if so, we don't need the second char.
|
||||
if ((pk & kSecondNotPacked) == kSecondNotPacked)
|
||||
out |= kSecondCharIsLiteral;
|
||||
else {
|
||||
const uint8_t chr = (pk >> 4) & 0x0F;
|
||||
chars_out[1] = meatPackLookupTable[chr]; // Set the second char
|
||||
}
|
||||
// Check if upper nybble is 1111... if so, we don't need the second char.
|
||||
if ((pk & kSecondNotPacked) == kSecondNotPacked)
|
||||
out |= kSecondCharIsLiteral;
|
||||
else {
|
||||
const uint8_t chr = (pk >> 4) & 0x0F;
|
||||
chars_out[1] = meatPackLookupTable[chr]; // Set the second char
|
||||
}
|
||||
|
||||
return out;
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,33 +100,33 @@ uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars
|
||||
* according to the current MeatPack state.
|
||||
*/
|
||||
void MeatPack::handle_rx_char_inner(const uint8_t c) {
|
||||
if (TEST(state, MPConfig_Bit_Active)) { // Is MeatPack active?
|
||||
if (!full_char_count) { // No literal characters to fetch?
|
||||
uint8_t buf[2] = { 0, 0 };
|
||||
register const uint8_t res = unpack_chars(c, buf); // Decode the byte into one or two characters.
|
||||
if (res & kFirstCharIsLiteral) { // The 1st character couldn't be packed.
|
||||
++full_char_count; // So the next stream byte is a full character.
|
||||
if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. Another stream byte is a full character.
|
||||
else second_char = buf[1]; // Retain the unpacked second character.
|
||||
}
|
||||
else {
|
||||
handle_output_char(buf[0]); // Send the unpacked first character out.
|
||||
if (buf[0] != '\n') { // After a newline the next char won't be set
|
||||
if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. The next stream byte is a full character.
|
||||
else handle_output_char(buf[1]); // Send the unpacked second character out.
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
handle_output_char(c); // Pass through the character that couldn't be packed...
|
||||
if (second_char) {
|
||||
handle_output_char(second_char); // ...and send an unpacked 2nd character, if set.
|
||||
second_char = 0;
|
||||
}
|
||||
--full_char_count; // One literal character was consumed
|
||||
if (TEST(state, MPConfig_Bit_Active)) { // Is MeatPack active?
|
||||
if (!full_char_count) { // No literal characters to fetch?
|
||||
uint8_t buf[2] = { 0, 0 };
|
||||
const uint8_t res = unpack_chars(c, buf); // Decode the byte into one or two characters.
|
||||
if (res & kFirstCharIsLiteral) { // The 1st character couldn't be packed.
|
||||
++full_char_count; // So the next stream byte is a full character.
|
||||
if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. Another stream byte is a full character.
|
||||
else second_char = buf[1]; // Retain the unpacked second character.
|
||||
}
|
||||
else {
|
||||
handle_output_char(buf[0]); // Send the unpacked first character out.
|
||||
if (buf[0] != '\n') { // After a newline the next char won't be set
|
||||
if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. The next stream byte is a full character.
|
||||
else handle_output_char(buf[1]); // Send the unpacked second character out.
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Packing not enabled, just copy character to output
|
||||
else {
|
||||
handle_output_char(c); // Pass through the character that couldn't be packed...
|
||||
if (second_char) {
|
||||
handle_output_char(second_char); // ...and send an unpacked 2nd character, if set.
|
||||
second_char = 0;
|
||||
}
|
||||
--full_char_count; // One literal character was consumed
|
||||
}
|
||||
}
|
||||
else // Packing not enabled, just copy character to output
|
||||
handle_output_char(c);
|
||||
}
|
||||
|
||||
@@ -142,14 +135,12 @@ void MeatPack::handle_rx_char_inner(const uint8_t c) {
|
||||
* GCodeQueue::get_serial_commands via calls to get_result_char
|
||||
*/
|
||||
void MeatPack::handle_output_char(const uint8_t c) {
|
||||
char_out_buf[char_out_count++] = c;
|
||||
char_out_buf[char_out_count++] = c;
|
||||
|
||||
#if ENABLED(MP_DEBUG)
|
||||
if (chars_decoded < 1024) {
|
||||
++chars_decoded;
|
||||
DEBUG_ECHOPGM("RB: ");
|
||||
MYSERIAL.print((char)c);
|
||||
DEBUG_EOL();
|
||||
++chars_decoded;
|
||||
DEBUG_ECHOLNPGM("RB: ", AS_CHAR(c));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -159,29 +150,29 @@ void MeatPack::handle_output_char(const uint8_t c) {
|
||||
* Report the new state to serial.
|
||||
*/
|
||||
void MeatPack::handle_command(const MeatPack_Command c) {
|
||||
switch (c) {
|
||||
case MPCommand_QueryConfig: break;
|
||||
case MPCommand_EnablePacking: SBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] ENA REC"); break;
|
||||
case MPCommand_DisablePacking: CBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] DIS REC"); break;
|
||||
case MPCommand_ResetAll: reset_state(); DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); break;
|
||||
case MPCommand_EnableNoSpaces:
|
||||
SBI(state, MPConfig_Bit_NoSpaces);
|
||||
meatPackLookupTable[kSpaceCharIdx] = kSpaceCharReplace; DEBUG_ECHOLNPGM("[MPDBG] ENA NSP"); break;
|
||||
case MPCommand_DisableNoSpaces:
|
||||
CBI(state, MPConfig_Bit_NoSpaces);
|
||||
meatPackLookupTable[kSpaceCharIdx] = ' '; DEBUG_ECHOLNPGM("[MPDBG] DIS NSP"); break;
|
||||
default: DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC");
|
||||
}
|
||||
report_state();
|
||||
switch (c) {
|
||||
case MPCommand_QueryConfig: break;
|
||||
case MPCommand_EnablePacking: SBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] ENA REC"); break;
|
||||
case MPCommand_DisablePacking: CBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] DIS REC"); break;
|
||||
case MPCommand_ResetAll: reset_state(); DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); break;
|
||||
case MPCommand_EnableNoSpaces:
|
||||
SBI(state, MPConfig_Bit_NoSpaces);
|
||||
meatPackLookupTable[kSpaceCharIdx] = kSpaceCharReplace; DEBUG_ECHOLNPGM("[MPDBG] ENA NSP"); break;
|
||||
case MPCommand_DisableNoSpaces:
|
||||
CBI(state, MPConfig_Bit_NoSpaces);
|
||||
meatPackLookupTable[kSpaceCharIdx] = ' '; DEBUG_ECHOLNPGM("[MPDBG] DIS NSP"); break;
|
||||
default: DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC");
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
void MeatPack::report_state() {
|
||||
// NOTE: if any configuration vars are added below, the outgoing sync text for host plugin
|
||||
// should not contain the "PV' substring, as this is used to indicate protocol version
|
||||
SERIAL_ECHOPGM("[MP] ");
|
||||
SERIAL_ECHOPGM(MeatPack_ProtocolVersion " ");
|
||||
serialprint_onoff(TEST(state, MPConfig_Bit_Active));
|
||||
serialprintPGM(TEST(state, MPConfig_Bit_NoSpaces) ? PSTR(" NSP\n") : PSTR(" ESP\n"));
|
||||
// NOTE: if any configuration vars are added below, the outgoing sync text for host plugin
|
||||
// should not contain the "PV' substring, as this is used to indicate protocol version
|
||||
SERIAL_ECHOPGM("[MP] ");
|
||||
SERIAL_ECHOPGM(MeatPack_ProtocolVersion " ");
|
||||
serialprint_onoff(TEST(state, MPConfig_Bit_Active));
|
||||
SERIAL_ECHOPGM_P(TEST(state, MPConfig_Bit_NoSpaces) ? PSTR(" NSP\n") : PSTR(" ESP\n"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,40 +180,40 @@ void MeatPack::report_state() {
|
||||
* according to the current meatpack state.
|
||||
*/
|
||||
void MeatPack::handle_rx_char(const uint8_t c, const serial_index_t serial_ind) {
|
||||
if (c == kCommandByte) { // A command (0xFF) byte?
|
||||
if (cmd_count) { // In fact, two in a row?
|
||||
cmd_is_next = true; // Then a MeatPack command follows
|
||||
cmd_count = 0;
|
||||
}
|
||||
else
|
||||
++cmd_count; // cmd_count = 1 // One command byte received so far...
|
||||
return;
|
||||
if (c == kCommandByte) { // A command (0xFF) byte?
|
||||
if (cmd_count) { // In fact, two in a row?
|
||||
cmd_is_next = true; // Then a MeatPack command follows
|
||||
cmd_count = 0;
|
||||
}
|
||||
else
|
||||
++cmd_count; // cmd_count = 1 // One command byte received so far...
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd_is_next) { // Were two command bytes received?
|
||||
PORT_REDIRECT(serial_ind);
|
||||
handle_command((MeatPack_Command)c); // Then the byte is a MeatPack command
|
||||
cmd_is_next = false;
|
||||
return;
|
||||
}
|
||||
if (cmd_is_next) { // Were two command bytes received?
|
||||
PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));
|
||||
handle_command((MeatPack_Command)c); // Then the byte is a MeatPack command
|
||||
cmd_is_next = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd_count) { // Only a single 0xFF was received
|
||||
handle_rx_char_inner(kCommandByte); // A single 0xFF is passed on literally so it can be interpreted as kFirstNotPacked|kSecondNotPacked
|
||||
cmd_count = 0;
|
||||
}
|
||||
if (cmd_count) { // Only a single 0xFF was received
|
||||
handle_rx_char_inner(kCommandByte); // A single 0xFF is passed on literally so it can be interpreted as kFirstNotPacked|kSecondNotPacked
|
||||
cmd_count = 0;
|
||||
}
|
||||
|
||||
handle_rx_char_inner(c); // Other characters are passed on for MeatPack decoding
|
||||
handle_rx_char_inner(c); // Other characters are passed on for MeatPack decoding
|
||||
}
|
||||
|
||||
uint8_t MeatPack::get_result_char(char* const __restrict out) {
|
||||
uint8_t res = 0;
|
||||
if (char_out_count) {
|
||||
res = char_out_count;
|
||||
char_out_count = 0;
|
||||
for (register uint8_t i = 0; i < res; ++i)
|
||||
out[i] = (char)char_out_buf[i];
|
||||
}
|
||||
return res;
|
||||
uint8_t MeatPack::get_result_char(char * const __restrict out) {
|
||||
uint8_t res = 0;
|
||||
if (char_out_count) {
|
||||
res = char_out_count;
|
||||
char_out_count = 0;
|
||||
for (uint8_t i = 0; i < res; ++i)
|
||||
out[i] = (char)char_out_buf[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // MEATPACK
|
||||
#endif // HAS_MEATPACK
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../core/serial_hook.h"
|
||||
|
||||
/**
|
||||
* Commands sent to MeatPack to control its behavior.
|
||||
@@ -63,61 +64,112 @@
|
||||
* some non-0xFF character.
|
||||
*/
|
||||
enum MeatPack_Command : uint8_t {
|
||||
MPCommand_None = 0,
|
||||
MPCommand_EnablePacking = 0xFB,
|
||||
MPCommand_DisablePacking = 0xFA,
|
||||
MPCommand_ResetAll = 0xF9,
|
||||
MPCommand_QueryConfig = 0xF8,
|
||||
MPCommand_EnableNoSpaces = 0xF7,
|
||||
MPCommand_DisableNoSpaces = 0xF6
|
||||
MPCommand_None = 0,
|
||||
MPCommand_EnablePacking = 0xFB,
|
||||
MPCommand_DisablePacking = 0xFA,
|
||||
MPCommand_ResetAll = 0xF9,
|
||||
MPCommand_QueryConfig = 0xF8,
|
||||
MPCommand_EnableNoSpaces = 0xF7,
|
||||
MPCommand_DisableNoSpaces = 0xF6
|
||||
};
|
||||
|
||||
enum MeatPack_ConfigStateBits : uint8_t {
|
||||
MPConfig_Bit_Active = 0,
|
||||
MPConfig_Bit_NoSpaces = 1
|
||||
MPConfig_Bit_Active = 0,
|
||||
MPConfig_Bit_NoSpaces = 1
|
||||
};
|
||||
|
||||
class MeatPack {
|
||||
private:
|
||||
friend class GCodeQueue;
|
||||
|
||||
// Utility definitions
|
||||
static const uint8_t kCommandByte = 0b11111111,
|
||||
kFirstNotPacked = 0b00001111,
|
||||
kSecondNotPacked = 0b11110000,
|
||||
kFirstCharIsLiteral = 0b00000001,
|
||||
kSecondCharIsLiteral = 0b00000010;
|
||||
// Utility definitions
|
||||
static const uint8_t kCommandByte = 0b11111111,
|
||||
kFirstNotPacked = 0b00001111,
|
||||
kSecondNotPacked = 0b11110000,
|
||||
kFirstCharIsLiteral = 0b00000001,
|
||||
kSecondCharIsLiteral = 0b00000010;
|
||||
|
||||
static const uint8_t kSpaceCharIdx = 11;
|
||||
static const char kSpaceCharReplace = 'E';
|
||||
static const uint8_t kSpaceCharIdx = 11;
|
||||
static const char kSpaceCharReplace = 'E';
|
||||
|
||||
static bool cmd_is_next; // A command is pending
|
||||
static uint8_t state; // Configuration state
|
||||
static uint8_t second_char; // Buffers a character if dealing with out-of-sequence pairs
|
||||
static uint8_t cmd_count, // Counter of command bytes received (need 2)
|
||||
full_char_count, // Counter for full-width characters to be received
|
||||
char_out_count; // Stores number of characters to be read out.
|
||||
static uint8_t char_out_buf[2]; // Output buffer for caching up to 2 characters
|
||||
bool cmd_is_next; // A command is pending
|
||||
uint8_t state; // Configuration state
|
||||
uint8_t second_char; // Buffers a character if dealing with out-of-sequence pairs
|
||||
uint8_t cmd_count, // Counter of command bytes received (need 2)
|
||||
full_char_count, // Counter for full-width characters to be received
|
||||
char_out_count; // Stores number of characters to be read out.
|
||||
uint8_t char_out_buf[2]; // Output buffer for caching up to 2 characters
|
||||
|
||||
// Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences,
|
||||
// and will control state internally.
|
||||
static void handle_rx_char(const uint8_t c, const serial_index_t serial_ind);
|
||||
public:
|
||||
// Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences,
|
||||
// and will control state internally.
|
||||
void handle_rx_char(const uint8_t c, const serial_index_t serial_ind);
|
||||
|
||||
/**
|
||||
* After passing in rx'd char using above method, call this to get characters out.
|
||||
* Can return from 0 to 2 characters at once.
|
||||
* @param out [in] Output pointer for unpacked/processed data.
|
||||
* @return Number of characters returned. Range from 0 to 2.
|
||||
*/
|
||||
static uint8_t get_result_char(char* const __restrict out);
|
||||
/**
|
||||
* After passing in rx'd char using above method, call this to get characters out.
|
||||
* Can return from 0 to 2 characters at once.
|
||||
* @param out [in] Output pointer for unpacked/processed data.
|
||||
* @return Number of characters returned. Range from 0 to 2.
|
||||
*/
|
||||
uint8_t get_result_char(char * const __restrict out);
|
||||
|
||||
static void reset_state();
|
||||
static void report_state();
|
||||
static uint8_t unpacked_char(register const uint8_t in);
|
||||
static uint8_t unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out);
|
||||
static void handle_command(const MeatPack_Command c);
|
||||
static void handle_output_char(const uint8_t c);
|
||||
static void handle_rx_char_inner(const uint8_t c);
|
||||
void reset_state();
|
||||
void report_state();
|
||||
uint8_t unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out);
|
||||
void handle_command(const MeatPack_Command c);
|
||||
void handle_output_char(const uint8_t c);
|
||||
void handle_rx_char_inner(const uint8_t c);
|
||||
|
||||
MeatPack() : cmd_is_next(false), state(0), second_char(0), cmd_count(0), full_char_count(0), char_out_count(0) {}
|
||||
};
|
||||
|
||||
extern MeatPack meatpack;
|
||||
// Implement the MeatPack serial class so it's transparent to rest of the code
|
||||
template <typename SerialT>
|
||||
struct MeatpackSerial : public SerialBase <MeatpackSerial < SerialT >> {
|
||||
typedef SerialBase< MeatpackSerial<SerialT> > BaseClassT;
|
||||
|
||||
SerialT & out;
|
||||
MeatPack meatpack;
|
||||
|
||||
char serialBuffer[2];
|
||||
uint8_t charCount;
|
||||
uint8_t readIndex;
|
||||
|
||||
NO_INLINE void write(uint8_t c) { out.write(c); }
|
||||
void flush() { out.flush(); }
|
||||
void begin(long br) { out.begin(br); readIndex = 0; }
|
||||
void end() { out.end(); }
|
||||
|
||||
void msgDone() { out.msgDone(); }
|
||||
// Existing instances implement Arduino's operator bool, so use that if it's available
|
||||
bool connected() { return Private::HasMember_connected<SerialT>::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; }
|
||||
void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); }
|
||||
SerialFeature features(serial_index_t index) const { return SerialFeature::MeatPack | CALL_IF_EXISTS(SerialFeature, &out, features, index); }
|
||||
|
||||
|
||||
int available(serial_index_t index) {
|
||||
if (charCount) return charCount; // The buffer still has data
|
||||
if (out.available(index) <= 0) return 0; // No data to read
|
||||
|
||||
// Don't read in read method, instead do it here, so we can make progress in the read method
|
||||
const int r = out.read(index);
|
||||
if (r == -1) return 0; // This is an error from the underlying serial code
|
||||
meatpack.handle_rx_char((uint8_t)r, index);
|
||||
charCount = meatpack.get_result_char(serialBuffer);
|
||||
readIndex = 0;
|
||||
|
||||
return charCount;
|
||||
}
|
||||
|
||||
int readImpl(const serial_index_t index) {
|
||||
// Not enough char to make progress?
|
||||
if (charCount == 0 && available(index) == 0) return -1;
|
||||
|
||||
charCount--;
|
||||
return serialBuffer[readIndex++];
|
||||
}
|
||||
|
||||
int read(serial_index_t index) { return readImpl(index); }
|
||||
int available() { return available(0); }
|
||||
int read() { return readImpl(0); }
|
||||
|
||||
MeatpackSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {}
|
||||
};
|
||||
|
||||
68
Marlin/src/feature/mixing.cpp
Executable file → Normal file
68
Marlin/src/feature/mixing.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -44,7 +44,7 @@ int_fast8_t Mixer::runner = 0;
|
||||
mixer_comp_t Mixer::s_color[MIXING_STEPPERS];
|
||||
mixer_accu_t Mixer::accu[MIXING_STEPPERS] = { 0 };
|
||||
|
||||
#if DUAL_MIXING_EXTRUDER || ENABLED(GRADIENT_MIX)
|
||||
#if EITHER(HAS_DUAL_MIXING, GRADIENT_MIX)
|
||||
mixer_perc_t Mixer::mix[MIXING_STEPPERS];
|
||||
#endif
|
||||
|
||||
@@ -90,15 +90,13 @@ void Mixer::normalize(const uint8_t tool_index) {
|
||||
SERIAL_ECHOLNPGM("]");
|
||||
#endif
|
||||
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
refresh_gradient();
|
||||
#endif
|
||||
TERN_(GRADIENT_MIX, refresh_gradient());
|
||||
}
|
||||
|
||||
void Mixer::reset_vtools() {
|
||||
// Virtual Tools 0, 1, 2, 3 = Filament 1, 2, 3, 4, etc.
|
||||
// Every virtual tool gets a pure filament
|
||||
LOOP_L_N(t, MIXING_VIRTUAL_TOOLS && t < MIXING_STEPPERS)
|
||||
LOOP_L_N(t, _MIN(MIXING_VIRTUAL_TOOLS, MIXING_STEPPERS))
|
||||
MIXER_STEPPER_LOOP(i)
|
||||
color[t][i] = (t == i) ? COLOR_A_MASK : 0;
|
||||
|
||||
@@ -108,28 +106,45 @@ void Mixer::reset_vtools() {
|
||||
MIXER_STEPPER_LOOP(i)
|
||||
color[t][i] = (i == 0) ? COLOR_A_MASK : 0;
|
||||
#endif
|
||||
|
||||
// MIXING_PRESETS: Set a variety of obvious mixes as presets
|
||||
#if ENABLED(MIXING_PRESETS) && WITHIN(MIXING_STEPPERS, 2, 3)
|
||||
#if MIXING_STEPPERS == 2
|
||||
if (MIXING_VIRTUAL_TOOLS > 2) { collector[0] = 1; collector[1] = 1; mixer.normalize(2); } // 1:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 3) { collector[0] = 3; mixer.normalize(3); } // 3:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 4) { collector[0] = 1; collector[1] = 3; mixer.normalize(4); } // 1:3
|
||||
if (MIXING_VIRTUAL_TOOLS > 5) { collector[1] = 2; mixer.normalize(5); } // 1:2
|
||||
if (MIXING_VIRTUAL_TOOLS > 6) { collector[0] = 2; collector[1] = 1; mixer.normalize(6); } // 2:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 7) { collector[0] = 3; collector[1] = 2; mixer.normalize(7); } // 3:2
|
||||
#else
|
||||
if (MIXING_VIRTUAL_TOOLS > 3) { collector[0] = 1; collector[1] = 1; collector[2] = 1; mixer.normalize(3); } // 1:1:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 4) { collector[1] = 3; collector[2] = 0; mixer.normalize(4); } // 1:3:0
|
||||
if (MIXING_VIRTUAL_TOOLS > 5) { collector[0] = 0; collector[2] = 1; mixer.normalize(5); } // 0:3:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 6) { collector[1] = 1; mixer.normalize(6); } // 0:1:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 7) { collector[0] = 1; collector[2] = 0; mixer.normalize(7); } // 1:1:0
|
||||
#endif
|
||||
ZERO(collector);
|
||||
#endif
|
||||
}
|
||||
|
||||
// called at boot
|
||||
void Mixer::init() {
|
||||
|
||||
ZERO(collector);
|
||||
|
||||
reset_vtools();
|
||||
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
#if HAS_MIXER_SYNC_CHANNEL
|
||||
// AUTORETRACT_TOOL gets the same amount of all filaments
|
||||
MIXER_STEPPER_LOOP(i)
|
||||
color[MIXER_AUTORETRACT_TOOL][i] = COLOR_A_MASK;
|
||||
#endif
|
||||
|
||||
ZERO(collector);
|
||||
|
||||
#if DUAL_MIXING_EXTRUDER || ENABLED(GRADIENT_MIX)
|
||||
#if EITHER(HAS_DUAL_MIXING, GRADIENT_MIX)
|
||||
update_mix_from_vtool();
|
||||
#endif
|
||||
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
update_gradient_for_planner_z();
|
||||
#endif
|
||||
TERN_(GRADIENT_MIX, update_gradient_for_planner_z());
|
||||
}
|
||||
|
||||
void Mixer::refresh_collector(const float proportion/*=1.0*/, const uint8_t t/*=selected_vtool*/, float (&c)[MIXING_STEPPERS]/*=collector*/) {
|
||||
@@ -139,11 +154,11 @@ void Mixer::refresh_collector(const float proportion/*=1.0*/, const uint8_t t/*=
|
||||
cmax = _MAX(cmax, v);
|
||||
csum += v;
|
||||
}
|
||||
//SERIAL_ECHOPAIR("Mixer::refresh_collector(", proportion, ", ", int(t), ") cmax=", cmax, " csum=", csum, " color");
|
||||
//SERIAL_ECHOPGM("Mixer::refresh_collector(", proportion, ", ", t, ") cmax=", cmax, " csum=", csum, " color");
|
||||
const float inv_prop = proportion / csum;
|
||||
MIXER_STEPPER_LOOP(i) {
|
||||
c[i] = color[t][i] * inv_prop;
|
||||
//SERIAL_ECHOPAIR(" [", int(t), "][", int(i), "] = ", int(color[t][i]), " (", c[i], ") ");
|
||||
//SERIAL_ECHOPGM(" [", t, "][", i, "] = ", color[t][i], " (", c[i], ") ");
|
||||
}
|
||||
//SERIAL_EOL();
|
||||
}
|
||||
@@ -154,19 +169,17 @@ void Mixer::refresh_collector(const float proportion/*=1.0*/, const uint8_t t/*=
|
||||
#include "../module/planner.h"
|
||||
|
||||
gradient_t Mixer::gradient = {
|
||||
false, // enabled
|
||||
{0}, // color (array)
|
||||
0, 0, // start_z, end_z
|
||||
0, 1, // start_vtool, end_vtool
|
||||
{0}, {0} // start_mix[], end_mix[]
|
||||
#if ENABLED(GRADIENT_VTOOL)
|
||||
, -1 // vtool_index
|
||||
#endif
|
||||
false, // enabled
|
||||
{0}, // color (array)
|
||||
0, 0, // start_z, end_z
|
||||
0, 1, // start_vtool, end_vtool
|
||||
{0}, {0} // start_mix[], end_mix[]
|
||||
OPTARG(GRADIENT_VTOOL, -1) // vtool_index
|
||||
};
|
||||
|
||||
float Mixer::prev_z; // = 0
|
||||
|
||||
void Mixer::update_gradient_for_z(const float z) {
|
||||
void Mixer::update_gradient_for_z(const_float_t z) {
|
||||
if (z == prev_z) return;
|
||||
prev_z = z;
|
||||
|
||||
@@ -184,7 +197,12 @@ void Mixer::refresh_collector(const float proportion/*=1.0*/, const uint8_t t/*=
|
||||
}
|
||||
|
||||
void Mixer::update_gradient_for_planner_z() {
|
||||
update_gradient_for_z(planner.get_axis_position_mm(Z_AXIS));
|
||||
#if ENABLED(DELTA)
|
||||
get_cartesian_from_steppers();
|
||||
update_gradient_for_z(cartes.z);
|
||||
#else
|
||||
update_gradient_for_z(planner.get_axis_position_mm(Z_AXIS));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // GRADIENT_MIX
|
||||
|
||||
71
Marlin/src/feature/mixing.h
Executable file → Normal file
71
Marlin/src/feature/mixing.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
//#define MIXER_NORMALIZER_DEBUG
|
||||
|
||||
#ifndef __AVR__ // || DUAL_MIXING_EXTRUDER
|
||||
#ifndef __AVR__ // || HAS_DUAL_MIXING
|
||||
// Use 16-bit (or fastest) data for the integer mix factors
|
||||
typedef uint_fast16_t mixer_comp_t;
|
||||
typedef uint_fast16_t mixer_accu_t;
|
||||
@@ -48,29 +48,20 @@ typedef int8_t mixer_perc_t;
|
||||
#endif
|
||||
|
||||
enum MixTool {
|
||||
FIRST_USER_VIRTUAL_TOOL = 0,
|
||||
LAST_USER_VIRTUAL_TOOL = MIXING_VIRTUAL_TOOLS - 1,
|
||||
NR_USER_VIRTUAL_TOOLS,
|
||||
MIXER_DIRECT_SET_TOOL = NR_USER_VIRTUAL_TOOLS,
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
MIXER_AUTORETRACT_TOOL,
|
||||
FIRST_USER_VIRTUAL_TOOL = 0
|
||||
, LAST_USER_VIRTUAL_TOOL = MIXING_VIRTUAL_TOOLS - 1
|
||||
, NR_USER_VIRTUAL_TOOLS
|
||||
, MIXER_DIRECT_SET_TOOL = NR_USER_VIRTUAL_TOOLS
|
||||
#if HAS_MIXER_SYNC_CHANNEL
|
||||
, MIXER_AUTORETRACT_TOOL
|
||||
#endif
|
||||
NR_MIXING_VIRTUAL_TOOLS
|
||||
, NR_MIXING_VIRTUAL_TOOLS
|
||||
};
|
||||
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
#define MAX_VTOOLS 254
|
||||
#else
|
||||
#define MAX_VTOOLS 255
|
||||
#endif
|
||||
#define MAX_VTOOLS TERN(HAS_MIXER_SYNC_CHANNEL, 254, 255)
|
||||
static_assert(NR_MIXING_VIRTUAL_TOOLS <= MAX_VTOOLS, "MIXING_VIRTUAL_TOOLS must be <= " STRINGIFY(MAX_VTOOLS) "!");
|
||||
|
||||
#define MIXER_STEPPER_LOOP(VAR) \
|
||||
for (uint_fast8_t VAR = 0; VAR < MIXING_STEPPERS; VAR++)
|
||||
|
||||
#define MIXER_BLOCK_FIELD mixer_comp_t b_color[MIXING_STEPPERS]
|
||||
#define MIXER_POPULATE_BLOCK() mixer.populate_block(block->b_color)
|
||||
#define MIXER_STEPPER_SETUP() mixer.stepper_setup(current_block->b_color)
|
||||
#define MIXER_STEPPER_LOOP(VAR) for (uint_fast8_t VAR = 0; VAR < MIXING_STEPPERS; VAR++)
|
||||
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
|
||||
@@ -79,7 +70,7 @@ static_assert(NR_MIXING_VIRTUAL_TOOLS <= MAX_VTOOLS, "MIXING_VIRTUAL_TOOLS must
|
||||
mixer_comp_t color[MIXING_STEPPERS]; // The current gradient color
|
||||
float start_z, end_z; // Region for gradient
|
||||
int8_t start_vtool, end_vtool; // Start and end virtual tools
|
||||
mixer_perc_t start_mix[MIXING_STEPPERS], // Start and end mixes from those tools
|
||||
mixer_perc_t start_mix[MIXING_STEPPERS], // Start and end mixes from those tools
|
||||
end_mix[MIXING_STEPPERS];
|
||||
#if ENABLED(GRADIENT_VTOOL)
|
||||
int8_t vtool_index; // Use this virtual tool number as index
|
||||
@@ -112,12 +103,8 @@ class Mixer {
|
||||
|
||||
FORCE_INLINE static void T(const uint_fast8_t c) {
|
||||
selected_vtool = c;
|
||||
#if ENABLED(GRADIENT_VTOOL)
|
||||
refresh_gradient();
|
||||
#endif
|
||||
#if DUAL_MIXING_EXTRUDER
|
||||
update_mix_from_vtool();
|
||||
#endif
|
||||
TERN_(GRADIENT_VTOOL, refresh_gradient());
|
||||
TERN_(HAS_DUAL_MIXING, update_mix_from_vtool());
|
||||
}
|
||||
|
||||
// Used when dealing with blocks
|
||||
@@ -135,7 +122,7 @@ class Mixer {
|
||||
MIXER_STEPPER_LOOP(i) s_color[i] = b_color[i];
|
||||
}
|
||||
|
||||
#if DUAL_MIXING_EXTRUDER || ENABLED(GRADIENT_MIX)
|
||||
#if EITHER(HAS_DUAL_MIXING, GRADIENT_MIX)
|
||||
|
||||
static mixer_perc_t mix[MIXING_STEPPERS]; // Scratch array for the Mix in proportion to 100
|
||||
|
||||
@@ -151,9 +138,9 @@ class Mixer {
|
||||
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
SERIAL_ECHOPGM("Mix [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(mix[0]), int(mix[1]), int(mix[2]), int(mix[3]), int(mix[4]), int(mix[5]));
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, mix[0], mix[1], mix[2], mix[3], mix[4], mix[5]);
|
||||
SERIAL_ECHOPGM(" ] to Color [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(tcolor[0]), int(tcolor[1]), int(tcolor[2]), int(tcolor[3]), int(tcolor[4]), int(tcolor[5]));
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, tcolor[0], tcolor[1], tcolor[2], tcolor[3], tcolor[4], tcolor[5]);
|
||||
SERIAL_ECHOLNPGM(" ]");
|
||||
#endif
|
||||
}
|
||||
@@ -165,29 +152,27 @@ class Mixer {
|
||||
MIXER_STEPPER_LOOP(i) mix[i] = mixer_perc_t(100.0f * color[j][i] / ctot);
|
||||
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
SERIAL_ECHOPAIR("V-tool ", int(j), " [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(color[j][0]), int(color[j][1]), int(color[j][2]), int(color[j][3]), int(color[j][4]), int(color[j][5]));
|
||||
SERIAL_ECHOPGM("V-tool ", j, " [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, color[j][0], color[j][1], color[j][2], color[j][3], color[j][4], color[j][5]);
|
||||
SERIAL_ECHOPGM(" ] to Mix [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(mix[0]), int(mix[1]), int(mix[2]), int(mix[3]), int(mix[4]), int(mix[5]));
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, mix[0], mix[1], mix[2], mix[3], mix[4], mix[5]);
|
||||
SERIAL_ECHOLNPGM(" ]");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // DUAL_MIXING_EXTRUDER || GRADIENT_MIX
|
||||
#endif // HAS_DUAL_MIXING || GRADIENT_MIX
|
||||
|
||||
#if DUAL_MIXING_EXTRUDER
|
||||
#if HAS_DUAL_MIXING
|
||||
|
||||
// Update the virtual tool from an edited mix
|
||||
static inline void update_vtool_from_mix() {
|
||||
copy_mix_to_color(color[selected_vtool]);
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
refresh_gradient();
|
||||
#endif
|
||||
TERN_(GRADIENT_MIX, refresh_gradient());
|
||||
// MIXER_STEPPER_LOOP(i) collector[i] = mix[i];
|
||||
// normalize();
|
||||
}
|
||||
|
||||
#endif // DUAL_MIXING_EXTRUDER
|
||||
#endif // HAS_DUAL_MIXING
|
||||
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
|
||||
@@ -195,9 +180,9 @@ class Mixer {
|
||||
static float prev_z;
|
||||
|
||||
// Update the current mix from the gradient for a given Z
|
||||
static void update_gradient_for_z(const float z);
|
||||
static void update_gradient_for_z(const_float_t z);
|
||||
static void update_gradient_for_planner_z();
|
||||
static inline void gradient_control(const float z) {
|
||||
static inline void gradient_control(const_float_t z) {
|
||||
if (gradient.enabled) {
|
||||
if (z >= gradient.end_z)
|
||||
T(gradient.end_vtool);
|
||||
@@ -213,9 +198,9 @@ class Mixer {
|
||||
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
SERIAL_ECHOPGM("Gradient [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(gradient.color[0]), int(gradient.color[1]), int(gradient.color[2]), int(gradient.color[3]), int(gradient.color[4]), int(gradient.color[5]));
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, gradient.color[0], gradient.color[1], gradient.color[2], gradient.color[3], gradient.color[4], gradient.color[5]);
|
||||
SERIAL_ECHOPGM(" ] to Mix [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, int(mix[0]), int(mix[1]), int(mix[2]), int(mix[3]), int(mix[4]), int(mix[5]));
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, mix[0], mix[1], mix[2], mix[3], mix[4], mix[5]);
|
||||
SERIAL_ECHOLNPGM(" ]");
|
||||
#endif
|
||||
}
|
||||
|
||||
18
Marlin/src/feature/snmm.cpp → Marlin/src/feature/mmu2/mmu.cpp
Executable file → Normal file
18
Marlin/src/feature/snmm.cpp → Marlin/src/feature/mmu2/mmu.cpp
Executable file → Normal file
@@ -16,23 +16,31 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(MK2_MULTIPLEXER)
|
||||
#if HAS_PRUSA_MMU1
|
||||
|
||||
#include "../MarlinCore.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../module/stepper.h"
|
||||
|
||||
void mmu_init() {
|
||||
SET_OUTPUT(E_MUX0_PIN);
|
||||
SET_OUTPUT(E_MUX1_PIN);
|
||||
SET_OUTPUT(E_MUX2_PIN);
|
||||
}
|
||||
|
||||
void select_multiplexed_stepper(const uint8_t e) {
|
||||
planner.synchronize();
|
||||
disable_e_steppers();
|
||||
stepper.disable_e_steppers();
|
||||
WRITE(E_MUX0_PIN, TEST(e, 0) ? HIGH : LOW);
|
||||
WRITE(E_MUX1_PIN, TEST(e, 1) ? HIGH : LOW);
|
||||
WRITE(E_MUX2_PIN, TEST(e, 2) ? HIGH : LOW);
|
||||
safe_delay(100);
|
||||
}
|
||||
|
||||
#endif // MK2_MULTIPLEXER
|
||||
#endif // HAS_PRUSA_MMU1
|
||||
3
Marlin/src/feature/snmm.h → Marlin/src/feature/mmu2/mmu.h
Executable file → Normal file
3
Marlin/src/feature/snmm.h → Marlin/src/feature/mmu2/mmu.h
Executable file → Normal file
@@ -16,9 +16,10 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
void mmu_init();
|
||||
void select_multiplexed_stepper(const uint8_t e);
|
||||
2
Marlin/src/feature/mmu2/serial-protocol.md → Marlin/src/feature/mmu2/mmu2-serial-protocol.md
Executable file → Normal file
2
Marlin/src/feature/mmu2/serial-protocol.md → Marlin/src/feature/mmu2/mmu2-serial-protocol.md
Executable file → Normal file
@@ -26,7 +26,7 @@ Now we are sure MMU is available and ready. If there was a timeout or other comm
|
||||
|
||||
- *Firmware version* is an integer value, but we don't care about it
|
||||
- *Build number* is an integer value and has to be >=126, or =>132 if 12V mode is enabled
|
||||
- *FINDA status* is 1 if the is filament loaded to the extruder, 0 otherwise
|
||||
- *FINDA status* is 1 if the filament is loaded to the extruder, 0 otherwise
|
||||
|
||||
|
||||
*Build number* is checked against the required value, if it does not match, printer is halted.
|
||||
915
Marlin/src/feature/mmu2/mmu2.cpp
Executable file → Normal file
915
Marlin/src/feature/mmu2/mmu2.cpp
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
54
Marlin/src/feature/mmu2/mmu2.h
Executable file → Normal file
54
Marlin/src/feature/mmu2/mmu2.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -43,25 +43,24 @@ public:
|
||||
|
||||
static void init();
|
||||
static void reset();
|
||||
static inline bool enabled() { return _enabled; }
|
||||
static void mmu_loop();
|
||||
static void tool_change(uint8_t index);
|
||||
static void tool_change(const char* special);
|
||||
static void tool_change(const uint8_t index);
|
||||
static void tool_change(const char *special);
|
||||
static uint8_t get_current_tool();
|
||||
static void set_filament_type(uint8_t index, uint8_t type);
|
||||
static void set_filament_type(const uint8_t index, const uint8_t type);
|
||||
|
||||
#if HAS_LCD_MENU && ENABLED(MMU2_MENUS)
|
||||
static bool unload();
|
||||
static void load_filament(uint8_t);
|
||||
static void load_all();
|
||||
static bool load_filament_to_nozzle(uint8_t index);
|
||||
static bool eject_filament(uint8_t index, bool recover);
|
||||
#endif
|
||||
static bool unload();
|
||||
static void load_filament(uint8_t);
|
||||
static void load_all();
|
||||
static bool load_filament_to_nozzle(const uint8_t index);
|
||||
static bool eject_filament(const uint8_t index, const bool recover);
|
||||
|
||||
private:
|
||||
static bool rx_str_P(const char* str);
|
||||
static void tx_str_P(const char* str);
|
||||
static void tx_printf_P(const char* format, int argument);
|
||||
static void tx_printf_P(const char* format, int argument1, int argument2);
|
||||
static bool rx_str_P(const char *str);
|
||||
static void tx_str_P(const char *str);
|
||||
static void tx_printf_P(const char *format, const int argument);
|
||||
static void tx_printf_P(const char *format, const int argument1, const int argument2);
|
||||
static void clear_rx_buffer();
|
||||
|
||||
static bool rx_ok();
|
||||
@@ -72,21 +71,32 @@ private:
|
||||
static bool get_response();
|
||||
static void manage_response(const bool move_axes, const bool turn_off_nozzle);
|
||||
|
||||
#if HAS_LCD_MENU && ENABLED(MMU2_MENUS)
|
||||
static void load_to_nozzle();
|
||||
static void filament_ramming();
|
||||
static void execute_extruder_sequence(const E_Step * sequence, int steps);
|
||||
#endif
|
||||
static void load_to_nozzle();
|
||||
static void execute_extruder_sequence(const E_Step * sequence, int steps);
|
||||
|
||||
static void filament_runout();
|
||||
|
||||
static bool enabled, ready, mmu_print_saved;
|
||||
#if HAS_PRUSA_MMU2S
|
||||
static bool mmu2s_triggered;
|
||||
static void check_filament();
|
||||
static bool can_load();
|
||||
static bool load_to_gears();
|
||||
#else
|
||||
FORCE_INLINE static bool load_to_gears() { return true; }
|
||||
#endif
|
||||
|
||||
#if ENABLED(MMU_EXTRUDER_SENSOR)
|
||||
static void mmu_continue_loading();
|
||||
#endif
|
||||
|
||||
static bool _enabled, ready, mmu_print_saved;
|
||||
|
||||
static uint8_t cmd, cmd_arg, last_cmd, extruder;
|
||||
static int8_t state;
|
||||
static volatile int8_t finda;
|
||||
static volatile bool finda_runout_valid;
|
||||
static int16_t version, buildnr;
|
||||
static millis_t last_request, next_P0_request;
|
||||
static millis_t prev_request, prev_P0_request;
|
||||
static char rx_buffer[MMU_RX_SIZE], tx_buffer[MMU_TX_SIZE];
|
||||
|
||||
static inline void set_runout_valid(const bool valid) {
|
||||
|
||||
61
Marlin/src/feature/password/password.cpp
Normal file
61
Marlin/src/feature/password/password.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(PASSWORD_FEATURE)
|
||||
|
||||
#include "password.h"
|
||||
#include "../../gcode/gcode.h"
|
||||
#include "../../core/serial.h"
|
||||
|
||||
Password password;
|
||||
|
||||
// public:
|
||||
bool Password::is_set, Password::is_locked, Password::did_first_run; // = false
|
||||
uint32_t Password::value, Password::value_entry;
|
||||
|
||||
//
|
||||
// Authenticate user with password.
|
||||
// Called from Setup, after SD Prinitng Stops/Aborts, and M510
|
||||
//
|
||||
void Password::lock_machine() {
|
||||
is_locked = true;
|
||||
TERN_(HAS_LCD_MENU, authenticate_user(ui.status_screen, screen_password_entry));
|
||||
}
|
||||
|
||||
//
|
||||
// Authentication check
|
||||
//
|
||||
void Password::authentication_check() {
|
||||
if (value_entry == value) {
|
||||
is_locked = false;
|
||||
did_first_run = true;
|
||||
}
|
||||
else {
|
||||
is_locked = true;
|
||||
SERIAL_ECHOLNPGM(STR_WRONG_PASSWORD);
|
||||
}
|
||||
TERN_(HAS_LCD_MENU, authentication_done());
|
||||
}
|
||||
|
||||
#endif // PASSWORD_FEATURE
|
||||
57
Marlin/src/feature/password/password.h
Normal file
57
Marlin/src/feature/password/password.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../lcd/marlinui.h"
|
||||
|
||||
class Password {
|
||||
public:
|
||||
static bool is_set, is_locked, did_first_run;
|
||||
static uint32_t value, value_entry;
|
||||
|
||||
Password() {}
|
||||
|
||||
static void lock_machine();
|
||||
static void authentication_check();
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
static void access_menu_password();
|
||||
static void authentication_done();
|
||||
static void media_gatekeeper();
|
||||
|
||||
private:
|
||||
static void authenticate_user(const screenFunc_t, const screenFunc_t);
|
||||
static void menu_password();
|
||||
static void menu_password_entry();
|
||||
static void screen_password_entry();
|
||||
static void screen_set_password();
|
||||
static void start_over();
|
||||
|
||||
static void digit_entered();
|
||||
static void set_password_done(const bool with_set=true);
|
||||
static void menu_password_report();
|
||||
|
||||
static void remove_password();
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Password password;
|
||||
497
Marlin/src/feature/pause.cpp
Executable file → Normal file
497
Marlin/src/feature/pause.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
|
||||
//#define DEBUG_PAUSE_RESUME
|
||||
|
||||
#include "../MarlinCore.h"
|
||||
#include "../gcode/gcode.h"
|
||||
#include "../module/motion.h"
|
||||
@@ -51,23 +53,31 @@
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../lcd/extui/ui_api.h"
|
||||
#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
|
||||
#include "../lcd/e3v2/enhanced/dwin.h"
|
||||
#endif
|
||||
|
||||
#include "../core/language.h"
|
||||
#include "../lcd/ultralcd.h"
|
||||
#include "../lcd/marlinui.h"
|
||||
|
||||
#if HAS_BUZZER
|
||||
#include "../libs/buzzer.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(POWER_LOSS_RECOVERY)
|
||||
#include "powerloss.h"
|
||||
#endif
|
||||
|
||||
#include "../libs/nozzle.h"
|
||||
#include "pause.h"
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_PAUSE_RESUME)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
// private:
|
||||
|
||||
static xyze_pos_t resume_position;
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if M600_PURGE_MORE_RESUMABLE
|
||||
PauseMenuResponse pause_menu_response;
|
||||
PauseMode pause_mode = PAUSE_MODE_PAUSE_PRINT;
|
||||
#endif
|
||||
@@ -78,10 +88,6 @@ fil_change_settings_t fc_settings[EXTRUDERS];
|
||||
#include "../sd/cardreader.h"
|
||||
#endif
|
||||
|
||||
#ifdef ANYCUBIC_TOUCHSCREEN
|
||||
#include "../lcd/anycubic_touchscreen.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(EMERGENCY_PARSER)
|
||||
#define _PMSG(L) L##_M108
|
||||
#else
|
||||
@@ -89,26 +95,30 @@ fil_change_settings_t fc_settings[EXTRUDERS];
|
||||
#endif
|
||||
|
||||
#if HAS_BUZZER
|
||||
static void filament_change_beep(const int8_t max_beep_count, const bool init=false) {
|
||||
static void impatient_beep(const int8_t max_beep_count, const bool restart=false) {
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
if (pause_mode == PAUSE_MODE_PAUSE_PRINT) return;
|
||||
#endif
|
||||
if (TERN0(HAS_LCD_MENU, pause_mode == PAUSE_MODE_PAUSE_PRINT)) return;
|
||||
|
||||
static millis_t next_buzz = 0;
|
||||
static int8_t runout_beep = 0;
|
||||
|
||||
if (init) next_buzz = runout_beep = 0;
|
||||
if (restart) next_buzz = runout_beep = 0;
|
||||
|
||||
const bool always = max_beep_count < 0;
|
||||
|
||||
const millis_t ms = millis();
|
||||
if (ELAPSED(ms, next_buzz)) {
|
||||
if (max_beep_count < 0 || runout_beep < max_beep_count + 5) { // Only beep as long as we're supposed to
|
||||
next_buzz = ms + ((max_beep_count < 0 || runout_beep < max_beep_count) ? 1000 : 500);
|
||||
if (always || runout_beep < max_beep_count + 5) { // Only beep as long as we're supposed to
|
||||
next_buzz = ms + ((always || runout_beep < max_beep_count) ? 1000 : 500);
|
||||
BUZZ(50, 880 - (runout_beep & 1) * 220);
|
||||
runout_beep++;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void first_impatient_beep(const int8_t max_beep_count) { impatient_beep(max_beep_count, true); }
|
||||
#else
|
||||
inline void impatient_beep(const int8_t, const bool=false) {}
|
||||
inline void first_impatient_beep(const int8_t) {}
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -120,22 +130,34 @@ fil_change_settings_t fc_settings[EXTRUDERS];
|
||||
*
|
||||
* Returns 'true' if heating was completed, 'false' for abort
|
||||
*/
|
||||
static bool ensure_safe_temperature(const PauseMode mode=PAUSE_MODE_SAME) {
|
||||
static bool ensure_safe_temperature(const bool wait=true, const PauseMode mode=PAUSE_MODE_SAME) {
|
||||
DEBUG_SECTION(est, "ensure_safe_temperature", true);
|
||||
DEBUG_ECHOLNPGM("... wait:", wait, " mode:", mode);
|
||||
|
||||
#if ENABLED(PREVENT_COLD_EXTRUSION)
|
||||
if (!DEBUGGING(DRYRUN) && thermalManager.targetTooColdToExtrude(active_extruder))
|
||||
thermalManager.setTargetHotend(thermalManager.extrude_min_temp, active_extruder);
|
||||
#endif
|
||||
|
||||
ui.pause_show_message(PAUSE_MESSAGE_HEATING, mode); UNUSED(mode);
|
||||
|
||||
if (wait) return thermalManager.wait_for_hotend(active_extruder);
|
||||
|
||||
// Allow interruption by Emergency Parser M108
|
||||
wait_for_heatup = TERN1(PREVENT_COLD_EXTRUSION, !thermalManager.allow_cold_extrude);
|
||||
while (wait_for_heatup && ABS(thermalManager.wholeDegHotend(active_extruder) - thermalManager.degTargetHotend(active_extruder)) > (TEMP_WINDOW))
|
||||
idle();
|
||||
wait_for_heatup = false;
|
||||
|
||||
#if ENABLED(PREVENT_COLD_EXTRUSION)
|
||||
// A user can cancel wait-for-heating with M108
|
||||
if (!DEBUGGING(DRYRUN) && thermalManager.targetTooColdToExtrude(active_extruder)) {
|
||||
SERIAL_ECHO_MSG(STR_ERR_HOTEND_TOO_COLD);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
lcd_pause_show_message(PAUSE_MESSAGE_HEATING, mode);
|
||||
#else
|
||||
UNUSED(mode);
|
||||
#endif
|
||||
|
||||
return thermalManager.wait_for_hotend(active_extruder);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,67 +172,61 @@ static bool ensure_safe_temperature(const PauseMode mode=PAUSE_MODE_SAME) {
|
||||
*
|
||||
* Returns 'true' if load was completed, 'false' for abort
|
||||
*/
|
||||
bool load_filament(const float &slow_load_length/*=0*/, const float &fast_load_length/*=0*/, const float &purge_length/*=0*/, const int8_t max_beep_count/*=0*/,
|
||||
bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load_length/*=0*/, const_float_t purge_length/*=0*/, const int8_t max_beep_count/*=0*/,
|
||||
const bool show_lcd/*=false*/, const bool pause_for_user/*=false*/,
|
||||
const PauseMode mode/*=PAUSE_MODE_PAUSE_PRINT*/
|
||||
DXC_ARGS
|
||||
) {
|
||||
#if !HAS_LCD_MENU
|
||||
UNUSED(show_lcd);
|
||||
#endif
|
||||
DEBUG_SECTION(lf, "load_filament", true);
|
||||
DEBUG_ECHOLNPGM("... slowlen:", slow_load_length, " fastlen:", fast_load_length, " purgelen:", purge_length, " maxbeep:", max_beep_count, " showlcd:", show_lcd, " pauseforuser:", pause_for_user, " pausemode:", mode DXC_SAY);
|
||||
|
||||
if (!ensure_safe_temperature(mode)) {
|
||||
#if HAS_LCD_MENU
|
||||
if (show_lcd) lcd_pause_show_message(PAUSE_MESSAGE_STATUS, mode);
|
||||
#endif
|
||||
if (!ensure_safe_temperature(false, mode)) {
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_STATUS, mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pause_for_user) {
|
||||
#if HAS_LCD_MENU
|
||||
if (show_lcd) lcd_pause_show_message(PAUSE_MESSAGE_INSERT, mode);
|
||||
#endif
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_INSERT, mode);
|
||||
SERIAL_ECHO_MSG(_PMSG(STR_FILAMENT_CHANGE_INSERT));
|
||||
|
||||
#if HAS_BUZZER
|
||||
filament_change_beep(max_beep_count, true);
|
||||
#else
|
||||
UNUSED(max_beep_count);
|
||||
#endif
|
||||
first_impatient_beep(max_beep_count);
|
||||
|
||||
KEEPALIVE_STATE(PAUSED_FOR_USER);
|
||||
wait_for_user = true; // LCD click or M108 will clear this
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("Load Filament")));
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
const char tool = '0'
|
||||
#if NUM_RUNOUT_SENSORS > 1
|
||||
+ active_extruder
|
||||
#endif
|
||||
;
|
||||
host_action_prompt_begin(PROMPT_USER_CONTINUE, PSTR("Load Filament T"), tool);
|
||||
host_action_prompt_button(CONTINUE_STR);
|
||||
host_action_prompt_show();
|
||||
#endif
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onUserConfirmRequired_P(PSTR("Load Filament"));
|
||||
const char tool = '0' + TERN0(MULTI_FILAMENT_SENSOR, active_extruder);
|
||||
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Load Filament T"), tool, CONTINUE_STR);
|
||||
#endif
|
||||
|
||||
while (wait_for_user) {
|
||||
#if HAS_BUZZER
|
||||
filament_change_beep(max_beep_count);
|
||||
impatient_beep(max_beep_count);
|
||||
#if BOTH(FILAMENT_CHANGE_RESUME_ON_INSERT, FILAMENT_RUNOUT_SENSOR)
|
||||
#if ENABLED(MULTI_FILAMENT_SENSOR)
|
||||
#define _CASE_INSERTED(N) case N-1: if (READ(FIL_RUNOUT##N##_PIN) != FIL_RUNOUT##N##_STATE) wait_for_user = false; break;
|
||||
switch (active_extruder) {
|
||||
REPEAT_1(NUM_RUNOUT_SENSORS, _CASE_INSERTED)
|
||||
}
|
||||
#else
|
||||
if (READ(FIL_RUNOUT_PIN) != FIL_RUNOUT_STATE) wait_for_user = false;
|
||||
#endif
|
||||
#endif
|
||||
idle_no_sleep();
|
||||
}
|
||||
}
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
if (show_lcd) lcd_pause_show_message(PAUSE_MESSAGE_LOAD, mode);
|
||||
#endif
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_LOAD, mode);
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
const int8_t saved_ext = active_extruder;
|
||||
const bool saved_ext_dup_mode = extruder_duplication_enabled;
|
||||
active_extruder = DXC_ext;
|
||||
extruder_duplication_enabled = false;
|
||||
set_duplication_enabled(false, DXC_ext);
|
||||
#endif
|
||||
|
||||
TERN_(BELTPRINTER, do_blocking_move_to_xy(0.00, 50.00));
|
||||
|
||||
// Slow Load filament
|
||||
if (slow_load_length) unscaled_e_move(slow_load_length, FILAMENT_CHANGE_SLOW_LOAD_FEEDRATE);
|
||||
|
||||
@@ -229,23 +245,16 @@ bool load_filament(const float &slow_load_length/*=0*/, const float &fast_load_l
|
||||
}
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE) // Tie the two extruders movement back together.
|
||||
active_extruder = saved_ext;
|
||||
extruder_duplication_enabled = saved_ext_dup_mode;
|
||||
stepper.set_directions();
|
||||
set_duplication_enabled(saved_ext_dup_mode, saved_ext);
|
||||
#endif
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_CONTINUOUS_PURGE)
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
if (show_lcd) lcd_pause_show_message(PAUSE_MESSAGE_PURGE);
|
||||
#endif
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_PURGE);
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Filament Purging..."), CONTINUE_STR);
|
||||
#endif
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onUserConfirmRequired_P(PSTR("Filament Purging..."));
|
||||
#endif
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_FILAMENT_CHANGE_PURGE)));
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_FILAMENT_CHANGE_PURGE), CONTINUE_STR));
|
||||
TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_Popup_Confirm(ICON_BLTouch, GET_TEXT(MSG_FILAMENT_CHANGE_PURGE), CONTINUE_STR));
|
||||
wait_for_user = true; // A click or M108 breaks the purge_length loop
|
||||
for (float purge_count = purge_length; purge_count > 0 && wait_for_user; --purge_count)
|
||||
unscaled_e_move(1, ADVANCED_PAUSE_PURGE_FEEDRATE);
|
||||
@@ -256,40 +265,49 @@ bool load_filament(const float &slow_load_length/*=0*/, const float &fast_load_l
|
||||
do {
|
||||
if (purge_length > 0) {
|
||||
// "Wait for filament purge"
|
||||
#if HAS_LCD_MENU
|
||||
if (show_lcd) lcd_pause_show_message(PAUSE_MESSAGE_PURGE);
|
||||
#endif
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_PURGE);
|
||||
|
||||
// Extrude filament to get into hotend
|
||||
unscaled_e_move(purge_length, ADVANCED_PAUSE_PURGE_FEEDRATE);
|
||||
}
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
filament_load_host_prompt(); // Initiate another host prompt. (NOTE: host_response_handler may also do this!)
|
||||
#endif
|
||||
TERN_(HOST_PROMPT_SUPPORT, filament_load_host_prompt()); // Initiate another host prompt.
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if M600_PURGE_MORE_RESUMABLE
|
||||
if (show_lcd) {
|
||||
// Show "Purge More" / "Resume" menu and wait for reply
|
||||
KEEPALIVE_STATE(PAUSED_FOR_USER);
|
||||
wait_for_user = false;
|
||||
lcd_pause_show_message(PAUSE_MESSAGE_OPTION);
|
||||
#if EITHER(HAS_LCD_MENU, DWIN_CREALITY_LCD_ENHANCED)
|
||||
ui.pause_show_message(PAUSE_MESSAGE_OPTION); // Also sets PAUSE_RESPONSE_WAIT_FOR
|
||||
#else
|
||||
pause_menu_response = PAUSE_RESPONSE_WAIT_FOR;
|
||||
#endif
|
||||
while (pause_menu_response == PAUSE_RESPONSE_WAIT_FOR) idle_no_sleep();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Keep looping if "Purge More" was selected
|
||||
} while (false
|
||||
#if HAS_LCD_MENU
|
||||
|| (show_lcd && pause_menu_response == PAUSE_RESPONSE_EXTRUDE_MORE)
|
||||
#endif
|
||||
);
|
||||
} while (TERN0(M600_PURGE_MORE_RESUMABLE, pause_menu_response == PAUSE_RESPONSE_EXTRUDE_MORE));
|
||||
|
||||
#endif
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_action_prompt_end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disabling E steppers for manual filament change should be fine
|
||||
* as long as users don't spin the E motor ridiculously fast and
|
||||
* send current back to their board, potentially frying it.
|
||||
*/
|
||||
inline void disable_active_extruder() {
|
||||
#if HAS_EXTRUDERS
|
||||
stepper.DISABLE_EXTRUDER(active_extruder);
|
||||
safe_delay(100);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload filament from the hotend
|
||||
*
|
||||
@@ -300,30 +318,29 @@ bool load_filament(const float &slow_load_length/*=0*/, const float &fast_load_l
|
||||
*
|
||||
* Returns 'true' if unload was completed, 'false' for abort
|
||||
*/
|
||||
bool unload_filament(const float &unload_length, const bool show_lcd/*=false*/,
|
||||
bool unload_filament(const_float_t unload_length, const bool show_lcd/*=false*/,
|
||||
const PauseMode mode/*=PAUSE_MODE_PAUSE_PRINT*/
|
||||
#if BOTH(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
|
||||
, const float &mix_multiplier/*=1.0*/
|
||||
, const_float_t mix_multiplier/*=1.0*/
|
||||
#endif
|
||||
) {
|
||||
#if !HAS_LCD_MENU
|
||||
UNUSED(show_lcd);
|
||||
#endif
|
||||
DEBUG_SECTION(uf, "unload_filament", true);
|
||||
DEBUG_ECHOLNPGM("... unloadlen:", unload_length, " showlcd:", show_lcd, " mode:", mode
|
||||
#if BOTH(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
|
||||
, " mixmult:", mix_multiplier
|
||||
#endif
|
||||
);
|
||||
|
||||
#if !BOTH(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
|
||||
constexpr float mix_multiplier = 1.0;
|
||||
constexpr float mix_multiplier = 1.0f;
|
||||
#endif
|
||||
|
||||
if (!ensure_safe_temperature(mode)) {
|
||||
#if HAS_LCD_MENU
|
||||
if (show_lcd) lcd_pause_show_message(PAUSE_MESSAGE_STATUS);
|
||||
#endif
|
||||
if (!ensure_safe_temperature(false, mode)) {
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_STATUS);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
if (show_lcd) lcd_pause_show_message(PAUSE_MESSAGE_UNLOAD, mode);
|
||||
#endif
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_UNLOAD, mode);
|
||||
|
||||
// Retract filament
|
||||
unscaled_e_move(-(FILAMENT_UNLOAD_PURGE_RETRACT) * mix_multiplier, (PAUSE_PARK_RETRACT_FEEDRATE) * mix_multiplier);
|
||||
@@ -347,11 +364,8 @@ bool unload_filament(const float &unload_length, const bool show_lcd/*=false*/,
|
||||
planner.settings.retract_acceleration = saved_acceleration;
|
||||
#endif
|
||||
|
||||
// Disable E steppers for manual change
|
||||
#if HAS_E_STEPPER_ENABLE
|
||||
disable_e_stepper(active_extruder);
|
||||
safe_delay(100);
|
||||
#endif
|
||||
// Disable the Extruder for manual change
|
||||
disable_active_extruder();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -373,11 +387,11 @@ bool unload_filament(const float &unload_length, const bool show_lcd/*=false*/,
|
||||
*/
|
||||
uint8_t did_pause_print = 0;
|
||||
|
||||
bool pause_print(const float &retract, const xyz_pos_t &park_point, const float &unload_length/*=0*/, const bool show_lcd/*=false*/ DXC_ARGS) {
|
||||
bool pause_print(const_float_t retract, const xyz_pos_t &park_point, const bool show_lcd/*=false*/, const_float_t unload_length/*=0*/ DXC_ARGS) {
|
||||
DEBUG_SECTION(pp, "pause_print", true);
|
||||
DEBUG_ECHOLNPGM("... park.x:", park_point.x, " y:", park_point.y, " z:", park_point.z, " unloadlen:", unload_length, " showlcd:", show_lcd DXC_SAY);
|
||||
|
||||
#if !HAS_LCD_MENU
|
||||
UNUSED(show_lcd);
|
||||
#endif
|
||||
UNUSED(show_lcd);
|
||||
|
||||
if (did_pause_print) return false; // already paused
|
||||
|
||||
@@ -389,29 +403,15 @@ bool pause_print(const float &retract, const xyz_pos_t &park_point, const float
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
host_prompt_open(PROMPT_INFO, PSTR("Pause"), DISMISS_STR);
|
||||
#endif
|
||||
|
||||
if (!DEBUGGING(DRYRUN) && unload_length && thermalManager.targetTooColdToExtrude(active_extruder)) {
|
||||
SERIAL_ECHO_MSG(STR_ERR_HOTEND_TOO_COLD);
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
if (show_lcd) { // Show status screen
|
||||
lcd_pause_show_message(PAUSE_MESSAGE_STATUS);
|
||||
LCD_MESSAGEPGM(MSG_M600_TOO_COLD);
|
||||
}
|
||||
#endif
|
||||
|
||||
return false; // unable to reach safe temperature
|
||||
}
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_open(PROMPT_INFO, PSTR("Pause"), DISMISS_STR));
|
||||
|
||||
// Indicate that the printer is paused
|
||||
++did_pause_print;
|
||||
|
||||
// Pause the print job and timer
|
||||
#if ENABLED(SDSUPPORT)
|
||||
if (IS_SD_PRINTING()) {
|
||||
const bool was_sd_printing = IS_SD_PRINTING();
|
||||
if (was_sd_printing) {
|
||||
card.pauseSDPrint();
|
||||
++did_pause_print; // Indicate SD pause also
|
||||
}
|
||||
@@ -422,37 +422,48 @@ bool pause_print(const float &retract, const xyz_pos_t &park_point, const float
|
||||
// Save current position
|
||||
resume_position = current_position;
|
||||
|
||||
// Will the nozzle be parking?
|
||||
const bool do_park = !axes_should_home();
|
||||
|
||||
#if ENABLED(POWER_LOSS_RECOVERY)
|
||||
// Save PLR info in case the power goes out while parked
|
||||
const float park_raise = do_park ? nozzle.park_mode_0_height(park_point.z) - current_position.z : POWER_LOSS_ZRAISE;
|
||||
if (was_sd_printing && recovery.enabled) recovery.save(true, park_raise, do_park);
|
||||
#endif
|
||||
|
||||
// Wait for buffered blocks to complete
|
||||
planner.synchronize();
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FANS_PAUSE) && FAN_COUNT > 0
|
||||
#if ENABLED(ADVANCED_PAUSE_FANS_PAUSE) && HAS_FAN
|
||||
thermalManager.set_fans_paused(true);
|
||||
#endif
|
||||
|
||||
// Initial retract before move to filament change position
|
||||
if (retract && thermalManager.hotEnoughToExtrude(active_extruder))
|
||||
if (retract && thermalManager.hotEnoughToExtrude(active_extruder)) {
|
||||
DEBUG_ECHOLNPGM("... retract:", retract);
|
||||
unscaled_e_move(retract, PAUSE_PARK_RETRACT_FEEDRATE);
|
||||
}
|
||||
|
||||
// Park the nozzle by moving up by z_lift and then moving to (x_pos, y_pos)
|
||||
if (!axes_need_homing())
|
||||
nozzle.park(2, park_point);
|
||||
// If axes don't need to home then the nozzle can park
|
||||
if (do_park) nozzle.park(0, park_point); // Park the nozzle by doing a Minimum Z Raise followed by an XY Move
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
const int8_t saved_ext = active_extruder;
|
||||
const bool saved_ext_dup_mode = extruder_duplication_enabled;
|
||||
active_extruder = DXC_ext;
|
||||
extruder_duplication_enabled = false;
|
||||
set_duplication_enabled(false, DXC_ext);
|
||||
#endif
|
||||
|
||||
if (unload_length) // Unload the filament
|
||||
// Unload the filament, if specified
|
||||
if (unload_length)
|
||||
unload_filament(unload_length, show_lcd, PAUSE_MODE_CHANGE_FILAMENT);
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
active_extruder = saved_ext;
|
||||
extruder_duplication_enabled = saved_ext_dup_mode;
|
||||
stepper.set_directions();
|
||||
set_duplication_enabled(saved_ext_dup_mode, saved_ext);
|
||||
#endif
|
||||
|
||||
// Disable the Extruder for manual change
|
||||
disable_active_extruder();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -470,131 +481,92 @@ bool pause_print(const float &retract, const xyz_pos_t &park_point, const float
|
||||
*/
|
||||
|
||||
void show_continue_prompt(const bool is_reload) {
|
||||
#if HAS_LCD_MENU
|
||||
lcd_pause_show_message(is_reload ? PAUSE_MESSAGE_INSERT : PAUSE_MESSAGE_WAITING);
|
||||
#endif
|
||||
DEBUG_SECTION(scp, "pause_print", true);
|
||||
DEBUG_ECHOLNPGM("... is_reload:", is_reload);
|
||||
|
||||
ui.pause_show_message(is_reload ? PAUSE_MESSAGE_INSERT : PAUSE_MESSAGE_WAITING);
|
||||
SERIAL_ECHO_START();
|
||||
serialprintPGM(is_reload ? PSTR(_PMSG(STR_FILAMENT_CHANGE_INSERT) "\n") : PSTR(_PMSG(STR_FILAMENT_CHANGE_WAIT) "\n"));
|
||||
SERIAL_ECHOPGM_P(is_reload ? PSTR(_PMSG(STR_FILAMENT_CHANGE_INSERT) "\n") : PSTR(_PMSG(STR_FILAMENT_CHANGE_WAIT) "\n"));
|
||||
}
|
||||
|
||||
void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep_count/*=0*/ DXC_ARGS) {
|
||||
DEBUG_SECTION(wfc, "wait_for_confirmation", true);
|
||||
DEBUG_ECHOLNPGM("... is_reload:", is_reload, " maxbeep:", max_beep_count DXC_SAY);
|
||||
|
||||
bool nozzle_timed_out = false;
|
||||
|
||||
show_continue_prompt(is_reload);
|
||||
|
||||
#if HAS_BUZZER
|
||||
filament_change_beep(max_beep_count, true);
|
||||
#else
|
||||
UNUSED(max_beep_count);
|
||||
#endif
|
||||
first_impatient_beep(max_beep_count);
|
||||
|
||||
// Start the heater idle timers
|
||||
const millis_t nozzle_timeout = (millis_t)(PAUSE_PARK_NOZZLE_TIMEOUT) * 1000UL;
|
||||
const millis_t nozzle_timeout = SEC_TO_MS(PAUSE_PARK_NOZZLE_TIMEOUT);
|
||||
|
||||
HOTEND_LOOP() thermalManager.hotend_idle[e].start(nozzle_timeout);
|
||||
HOTEND_LOOP() thermalManager.heater_idle[e].start(nozzle_timeout);
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
const int8_t saved_ext = active_extruder;
|
||||
const bool saved_ext_dup_mode = extruder_duplication_enabled;
|
||||
active_extruder = DXC_ext;
|
||||
extruder_duplication_enabled = false;
|
||||
set_duplication_enabled(false, DXC_ext);
|
||||
#endif
|
||||
|
||||
// Wait for filament insert by user and press button
|
||||
KEEPALIVE_STATE(PAUSED_FOR_USER);
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Nozzle Parked"), CONTINUE_STR);
|
||||
#endif
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onUserConfirmRequired_P(PSTR("Nozzle Parked"));
|
||||
#endif
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_NOZZLE_PARKED), CONTINUE_STR));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_NOZZLE_PARKED)));
|
||||
wait_for_user = true; // LCD click or M108 will clear this
|
||||
while (wait_for_user) {
|
||||
#if HAS_BUZZER
|
||||
filament_change_beep(max_beep_count);
|
||||
#endif
|
||||
impatient_beep(max_beep_count);
|
||||
|
||||
// If the nozzle has timed out...
|
||||
if (!nozzle_timed_out)
|
||||
HOTEND_LOOP() nozzle_timed_out |= thermalManager.hotend_idle[e].timed_out;
|
||||
HOTEND_LOOP() nozzle_timed_out |= thermalManager.heater_idle[e].timed_out;
|
||||
|
||||
// Wait for the user to press the button to re-heat the nozzle, then
|
||||
// re-heat the nozzle, re-show the continue prompt, restart idle timers, start over
|
||||
if (nozzle_timed_out) {
|
||||
|
||||
#ifdef ANYCUBIC_TOUCHSCREEN
|
||||
if (AnycubicTouchscreen.ai3m_pause_state < 3) {
|
||||
AnycubicTouchscreen.ai3m_pause_state += 2;
|
||||
#ifdef ANYCUBIC_TFT_DEBUG
|
||||
SERIAL_ECHOPAIR(" DEBUG: NTO - AI3M Pause State set to: ", AnycubicTouchscreen.ai3m_pause_state);
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
lcd_pause_show_message(PAUSE_MESSAGE_HEAT);
|
||||
#endif
|
||||
ui.pause_show_message(PAUSE_MESSAGE_HEAT);
|
||||
SERIAL_ECHO_MSG(_PMSG(STR_FILAMENT_CHANGE_HEAT));
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("HeaterTimeout"), PSTR("Reheat"));
|
||||
#endif
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_HEATER_TIMEOUT), GET_TEXT(MSG_REHEAT)));
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onUserConfirmRequired_P(PSTR("HeaterTimeout"));
|
||||
#endif
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_HEATER_TIMEOUT)));
|
||||
|
||||
wait_for_user_response(0, true); // Wait for LCD click or M108
|
||||
TERN_(HAS_RESUME_CONTINUE, wait_for_user_response(0, true)); // Wait for LCD click or M108
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
host_prompt_do(PROMPT_INFO, PSTR("Reheating"));
|
||||
#endif
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onStatusChanged(PSTR("Reheating..."));
|
||||
#endif
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_INFO, GET_TEXT(MSG_REHEATING)));
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onStatusChanged_P(GET_TEXT(MSG_REHEATING)));
|
||||
|
||||
TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status_P(GET_TEXT(MSG_REHEATING)));
|
||||
|
||||
// Re-enable the heaters if they timed out
|
||||
HOTEND_LOOP() thermalManager.reset_hotend_idle_timer(e);
|
||||
|
||||
// Wait for the heaters to reach the target temperatures
|
||||
ensure_safe_temperature();
|
||||
ensure_safe_temperature(false);
|
||||
|
||||
// Show the prompt to continue
|
||||
show_continue_prompt(is_reload);
|
||||
|
||||
// Start the heater idle timers
|
||||
const millis_t nozzle_timeout = (millis_t)(PAUSE_PARK_NOZZLE_TIMEOUT) * 1000UL;
|
||||
const millis_t nozzle_timeout = SEC_TO_MS(PAUSE_PARK_NOZZLE_TIMEOUT);
|
||||
|
||||
HOTEND_LOOP() thermalManager.heater_idle[e].start(nozzle_timeout);
|
||||
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_REHEATDONE), CONTINUE_STR));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_REHEATDONE)));
|
||||
TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status_P(GET_TEXT(MSG_REHEATDONE)));
|
||||
|
||||
IF_DISABLED(PAUSE_REHEAT_FAST_RESUME, wait_for_user = true);
|
||||
|
||||
HOTEND_LOOP() thermalManager.hotend_idle[e].start(nozzle_timeout);
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Reheat Done"), CONTINUE_STR);
|
||||
#endif
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onUserConfirmRequired_P(PSTR("Reheat finished."));
|
||||
#endif
|
||||
wait_for_user = true;
|
||||
nozzle_timed_out = false;
|
||||
#ifdef ANYCUBIC_TOUCHSCREEN
|
||||
if (AnycubicTouchscreen.ai3m_pause_state > 3) {
|
||||
AnycubicTouchscreen.ai3m_pause_state -= 2;
|
||||
#ifdef ANYCUBIC_TFT_DEBUG
|
||||
SERIAL_ECHOPAIR(" DEBUG: NTO - AI3M Pause State set to: ", AnycubicTouchscreen.ai3m_pause_state);
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_BUZZER
|
||||
filament_change_beep(max_beep_count, true);
|
||||
#endif
|
||||
first_impatient_beep(max_beep_count);
|
||||
}
|
||||
idle_no_sleep();
|
||||
}
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
active_extruder = saved_ext;
|
||||
extruder_duplication_enabled = saved_ext_dup_mode;
|
||||
stepper.set_directions();
|
||||
set_duplication_enabled(saved_ext_dup_mode, saved_ext);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -607,18 +579,23 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
|
||||
* - a nozzle timed out, or
|
||||
* - the nozzle is already heated.
|
||||
* - Display "wait for print to resume"
|
||||
* - Retract to prevent oozing
|
||||
* - Move the nozzle back to resume_position
|
||||
* - Unretract
|
||||
* - Re-prime the nozzle...
|
||||
* - FWRETRACT: Recover/prime from the prior G10.
|
||||
* - !FWRETRACT: Retract by resume_position.e, if negative.
|
||||
* Not sure how this logic comes into use.
|
||||
* - Move the nozzle back to resume_position
|
||||
* - Sync the planner E to resume_position.e
|
||||
* - Send host action for resume, if configured
|
||||
* - Resume the current SD print job, if any
|
||||
*/
|
||||
void resume_print(const float &slow_load_length/*=0*/, const float &fast_load_length/*=0*/, const float &purge_length/*=ADVANCED_PAUSE_PURGE_LENGTH*/, const int8_t max_beep_count/*=0*/ DXC_ARGS) {
|
||||
void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_length/*=0*/, const_float_t purge_length/*=ADVANCED_PAUSE_PURGE_LENGTH*/, const int8_t max_beep_count/*=0*/, const celsius_t targetTemp/*=0*/ DXC_ARGS) {
|
||||
DEBUG_SECTION(rp, "resume_print", true);
|
||||
DEBUG_ECHOLNPGM("... slowlen:", slow_load_length, " fastlen:", fast_load_length, " purgelen:", purge_length, " maxbeep:", max_beep_count, " targetTemp:", targetTemp DXC_SAY);
|
||||
|
||||
/*
|
||||
SERIAL_ECHOLNPAIR(
|
||||
SERIAL_ECHOLNPGM(
|
||||
"start of resume_print()\ndual_x_carriage_mode:", dual_x_carriage_mode,
|
||||
"\nextruder_duplication_enabled:", extruder_duplication_enabled,
|
||||
"\nactive_extruder:", active_extruder,
|
||||
@@ -630,26 +607,42 @@ void resume_print(const float &slow_load_length/*=0*/, const float &fast_load_le
|
||||
|
||||
// Re-enable the heaters if they timed out
|
||||
bool nozzle_timed_out = false;
|
||||
#ifdef ANYCUBIC_TOUCHSCREEN
|
||||
if (AnycubicTouchscreen.ai3m_pause_state > 3) {
|
||||
AnycubicTouchscreen.ai3m_pause_state -= 2;
|
||||
#ifdef ANYCUBIC_TFT_DEBUG
|
||||
SERIAL_ECHOPAIR(" DEBUG: NTO - AI3M Pause State set to: ", AnycubicTouchscreen.ai3m_pause_state);
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
HOTEND_LOOP() {
|
||||
nozzle_timed_out |= thermalManager.hotend_idle[e].timed_out;
|
||||
nozzle_timed_out |= thermalManager.heater_idle[e].timed_out;
|
||||
thermalManager.reset_hotend_idle_timer(e);
|
||||
}
|
||||
|
||||
if (nozzle_timed_out || thermalManager.hotEnoughToExtrude(active_extruder)) // Load the new filament
|
||||
load_filament(slow_load_length, fast_load_length, purge_length, max_beep_count, true, nozzle_timed_out, PAUSE_MODE_SAME DXC_PASS);
|
||||
if (targetTemp > thermalManager.degTargetHotend(active_extruder))
|
||||
thermalManager.setTargetHotend(targetTemp, active_extruder);
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
lcd_pause_show_message(PAUSE_MESSAGE_RESUME);
|
||||
#endif
|
||||
// Load the new filament
|
||||
load_filament(slow_load_length, fast_load_length, purge_length, max_beep_count, true, nozzle_timed_out, PAUSE_MODE_SAME DXC_PASS);
|
||||
|
||||
if (targetTemp > 0) {
|
||||
thermalManager.setTargetHotend(targetTemp, active_extruder);
|
||||
thermalManager.wait_for_hotend(active_extruder, false);
|
||||
}
|
||||
|
||||
ui.pause_show_message(PAUSE_MESSAGE_RESUME);
|
||||
|
||||
// Check Temperature before moving hotend
|
||||
ensure_safe_temperature(DISABLED(BELTPRINTER));
|
||||
|
||||
// Retract to prevent oozing
|
||||
unscaled_e_move(-(PAUSE_PARK_RETRACT_LENGTH), feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE));
|
||||
|
||||
if (!axes_should_home()) {
|
||||
// Move XY back to saved position
|
||||
destination.set(resume_position.x, resume_position.y, current_position.z, current_position.e);
|
||||
prepare_internal_move_to_destination(NOZZLE_PARK_XY_FEEDRATE);
|
||||
|
||||
// Move Z back to saved position
|
||||
destination.z = resume_position.z;
|
||||
prepare_internal_move_to_destination(NOZZLE_PARK_Z_FEEDRATE);
|
||||
}
|
||||
|
||||
// Unretract
|
||||
unscaled_e_move(PAUSE_PARK_RETRACT_LENGTH, feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE));
|
||||
|
||||
// Intelligent resuming
|
||||
#if ENABLED(FWRETRACT)
|
||||
@@ -660,13 +653,6 @@ void resume_print(const float &slow_load_length/*=0*/, const float &fast_load_le
|
||||
|
||||
// If resume_position is negative
|
||||
if (resume_position.e < 0) unscaled_e_move(resume_position.e, feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE));
|
||||
|
||||
// Move XY to starting position, then Z
|
||||
do_blocking_move_to_xy(resume_position, feedRate_t(NOZZLE_PARK_XY_FEEDRATE));
|
||||
|
||||
// Move Z_AXIS to saved position
|
||||
do_blocking_move_to_z(resume_position.z, feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
|
||||
|
||||
#if ADVANCED_PAUSE_RESUME_PRIME != 0
|
||||
unscaled_e_move(ADVANCED_PAUSE_RESUME_PRIME, feedRate_t(ADVANCED_PAUSE_PURGE_FEEDRATE));
|
||||
#endif
|
||||
@@ -675,9 +661,7 @@ void resume_print(const float &slow_load_length/*=0*/, const float &fast_load_le
|
||||
// Set extruder to saved position
|
||||
planner.set_e_position_mm((destination.e = current_position.e = resume_position.e));
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
lcd_pause_show_message(PAUSE_MESSAGE_STATUS);
|
||||
#endif
|
||||
ui.pause_show_message(PAUSE_MESSAGE_STATUS);
|
||||
|
||||
#ifdef ACTION_ON_RESUMED
|
||||
host_action_resumed();
|
||||
@@ -687,34 +671,29 @@ void resume_print(const float &slow_load_length/*=0*/, const float &fast_load_le
|
||||
|
||||
--did_pause_print;
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
host_prompt_open(PROMPT_INFO, PSTR("Resuming"), DISMISS_STR);
|
||||
#endif
|
||||
|
||||
#if ENABLED(SDSUPPORT)
|
||||
if (did_pause_print) {
|
||||
card.startFileprint();
|
||||
--did_pause_print;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FANS_PAUSE) && FAN_COUNT > 0
|
||||
thermalManager.set_fans_paused(false);
|
||||
#endif
|
||||
|
||||
#if HAS_FILAMENT_SENSOR
|
||||
runout.reset();
|
||||
#endif
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_open(PROMPT_INFO, PSTR("Resuming"), DISMISS_STR));
|
||||
|
||||
// Resume the print job timer if it was running
|
||||
if (print_job_timer.isPaused()) print_job_timer.start();
|
||||
|
||||
#if HAS_DISPLAY
|
||||
ui.reset_status();
|
||||
#if HAS_LCD_MENU
|
||||
ui.return_to_status();
|
||||
#endif
|
||||
#if ENABLED(SDSUPPORT)
|
||||
if (did_pause_print) {
|
||||
--did_pause_print;
|
||||
card.startOrResumeFilePrinting();
|
||||
// Write PLR now to update the z axis value
|
||||
TERN_(POWER_LOSS_RECOVERY, if (recovery.enabled) recovery.save(true));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FANS_PAUSE) && HAS_FAN
|
||||
thermalManager.set_fans_paused(false);
|
||||
#endif
|
||||
|
||||
TERN_(HAS_FILAMENT_SENSOR, runout.reset());
|
||||
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.reset_status());
|
||||
TERN_(HAS_LCD_MENU, ui.return_to_status());
|
||||
TERN_(DWIN_CREALITY_LCD_ENHANCED, HMI_ReturnScreen());
|
||||
}
|
||||
|
||||
#endif // ADVANCED_PAUSE_FEATURE
|
||||
|
||||
57
Marlin/src/feature/pause.h
Executable file → Normal file
57
Marlin/src/feature/pause.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -45,7 +45,7 @@ enum PauseMode : char {
|
||||
};
|
||||
|
||||
enum PauseMessage : char {
|
||||
PAUSE_MESSAGE_PAUSING,
|
||||
PAUSE_MESSAGE_PARKING,
|
||||
PAUSE_MESSAGE_CHANGING,
|
||||
PAUSE_MESSAGE_WAITING,
|
||||
PAUSE_MESSAGE_UNLOAD,
|
||||
@@ -59,7 +59,7 @@ enum PauseMessage : char {
|
||||
PAUSE_MESSAGE_HEATING
|
||||
};
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if M600_PURGE_MORE_RESUMABLE
|
||||
enum PauseMenuResponse : char {
|
||||
PAUSE_RESPONSE_WAIT_FOR,
|
||||
PAUSE_RESPONSE_EXTRUDE_MORE,
|
||||
@@ -77,25 +77,60 @@ extern uint8_t did_pause_print;
|
||||
#define DXC_PARAMS , const int8_t DXC_ext=-1
|
||||
#define DXC_ARGS , const int8_t DXC_ext
|
||||
#define DXC_PASS , DXC_ext
|
||||
#define DXC_SAY , " dxc:", int(DXC_ext)
|
||||
#else
|
||||
#define DXC_PARAMS
|
||||
#define DXC_ARGS
|
||||
#define DXC_PASS
|
||||
#define DXC_SAY
|
||||
#endif
|
||||
|
||||
bool pause_print(const float &retract, const xyz_pos_t &park_point, const float &unload_length=0, const bool show_lcd=false DXC_PARAMS);
|
||||
// Pause the print. If unload_length is set, do a Filament Unload
|
||||
bool pause_print(
|
||||
const_float_t retract, // (mm) Retraction length
|
||||
const xyz_pos_t &park_point, // Parking XY Position and Z Raise
|
||||
const bool show_lcd=false, // Set LCD status messages?
|
||||
const_float_t unload_length=0 // (mm) Filament Change Unload Length - 0 to skip
|
||||
DXC_PARAMS // Dual-X-Carriage extruder index
|
||||
);
|
||||
|
||||
void wait_for_confirmation(const bool is_reload=false, const int8_t max_beep_count=0 DXC_PARAMS);
|
||||
void wait_for_confirmation(
|
||||
const bool is_reload=false, // Reload Filament? (otherwise Resume Print)
|
||||
const int8_t max_beep_count=0 // Beep alert for attention
|
||||
DXC_PARAMS // Dual-X-Carriage extruder index
|
||||
);
|
||||
|
||||
void resume_print(const float &slow_load_length=0, const float &fast_load_length=0, const float &extrude_length=ADVANCED_PAUSE_PURGE_LENGTH, const int8_t max_beep_count=0 DXC_PARAMS);
|
||||
void resume_print(
|
||||
const_float_t slow_load_length=0, // (mm) Slow Load Length for finishing move
|
||||
const_float_t fast_load_length=0, // (mm) Fast Load Length for initial move
|
||||
const_float_t extrude_length=ADVANCED_PAUSE_PURGE_LENGTH, // (mm) Purge length
|
||||
const int8_t max_beep_count=0, // Beep alert for attention
|
||||
const celsius_t targetTemp=0 // (°C) A target temperature for the hotend
|
||||
DXC_PARAMS // Dual-X-Carriage extruder index
|
||||
);
|
||||
|
||||
bool load_filament(const float &slow_load_length=0, const float &fast_load_length=0, const float &extrude_length=0, const int8_t max_beep_count=0, const bool show_lcd=false,
|
||||
const bool pause_for_user=false, const PauseMode mode=PAUSE_MODE_PAUSE_PRINT DXC_PARAMS);
|
||||
bool load_filament(
|
||||
const_float_t slow_load_length=0, // (mm) Slow Load Length for finishing move
|
||||
const_float_t fast_load_length=0, // (mm) Fast Load Length for initial move
|
||||
const_float_t extrude_length=0, // (mm) Purge length
|
||||
const int8_t max_beep_count=0, // Beep alert for attention
|
||||
const bool show_lcd=false, // Set LCD status messages?
|
||||
const bool pause_for_user=false, // Pause for user before returning?
|
||||
const PauseMode mode=PAUSE_MODE_PAUSE_PRINT // Pause Mode to apply
|
||||
DXC_PARAMS // Dual-X-Carriage extruder index
|
||||
);
|
||||
|
||||
bool unload_filament(const float &unload_length, const bool show_lcd=false, const PauseMode mode=PAUSE_MODE_PAUSE_PRINT
|
||||
bool unload_filament(
|
||||
const_float_t unload_length, // (mm) Filament Unload Length - 0 to skip
|
||||
const bool show_lcd=false, // Set LCD status messages?
|
||||
const PauseMode mode=PAUSE_MODE_PAUSE_PRINT // Pause Mode to apply
|
||||
#if BOTH(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
|
||||
, const float &mix_multiplier=1.0
|
||||
, const_float_t mix_multiplier=1.0f // Extrusion multiplier (for a Mixing Extruder)
|
||||
#endif
|
||||
);
|
||||
|
||||
#endif // ADVANCED_PAUSE_FEATURE
|
||||
#else // !ADVANCED_PAUSE_FEATURE
|
||||
|
||||
constexpr uint8_t did_pause_print = 0;
|
||||
|
||||
#endif // !ADVANCED_PAUSE_FEATURE
|
||||
|
||||
234
Marlin/src/feature/power.cpp
Executable file → Normal file
234
Marlin/src/feature/power.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -26,97 +26,181 @@
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
|
||||
#include "power.h"
|
||||
#include "../module/temperature.h"
|
||||
#include "../module/stepper/indirection.h"
|
||||
#include "../module/stepper.h"
|
||||
#include "../MarlinCore.h"
|
||||
|
||||
#if ENABLED(PS_OFF_SOUND)
|
||||
#include "../libs/buzzer.h"
|
||||
#endif
|
||||
|
||||
#if defined(PSU_POWERUP_GCODE) || defined(PSU_POWEROFF_GCODE)
|
||||
#include "../gcode/gcode.h"
|
||||
#endif
|
||||
|
||||
#if EITHER(PSU_CONTROL, AUTO_POWER_CONTROL)
|
||||
|
||||
Power powerManager;
|
||||
bool Power::psu_on;
|
||||
|
||||
millis_t Power::lastPowerOn;
|
||||
|
||||
bool Power::is_power_needed() {
|
||||
#if ENABLED(AUTO_POWER_FANS)
|
||||
FANS_LOOP(i) if (thermalManager.fan_speed[i]) return true;
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_POWER_E_FANS)
|
||||
HOTEND_LOOP() if (thermalManager.autofan_speed[e]) return true;
|
||||
#endif
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
#include "../module/temperature.h"
|
||||
|
||||
#if BOTH(USE_CONTROLLER_FAN, AUTO_POWER_CONTROLLERFAN)
|
||||
if (controllerFan.state()) return true;
|
||||
#include "controllerfan.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_POWER_CHAMBER_FAN)
|
||||
if (thermalManager.chamberfan_speed) return true;
|
||||
#endif
|
||||
millis_t Power::lastPowerOn;
|
||||
#endif
|
||||
|
||||
// If any of the drivers or the bed are enabled...
|
||||
if (X_ENABLE_READ() == X_ENABLE_ON || Y_ENABLE_READ() == Y_ENABLE_ON || Z_ENABLE_READ() == Z_ENABLE_ON
|
||||
#if HAS_HEATED_BED
|
||||
|| thermalManager.temp_bed.soft_pwm_amount > 0
|
||||
#endif
|
||||
#if HAS_X2_ENABLE
|
||||
|| X2_ENABLE_READ() == X_ENABLE_ON
|
||||
#endif
|
||||
#if HAS_Y2_ENABLE
|
||||
|| Y2_ENABLE_READ() == Y_ENABLE_ON
|
||||
#endif
|
||||
#if HAS_Z2_ENABLE
|
||||
|| Z2_ENABLE_READ() == Z_ENABLE_ON
|
||||
#endif
|
||||
#if E_STEPPERS
|
||||
#define _OR_ENABLED_E(N) || E##N##_ENABLE_READ() == E_ENABLE_ON
|
||||
REPEAT(E_STEPPERS, _OR_ENABLED_E)
|
||||
#endif
|
||||
) return true;
|
||||
|
||||
HOTEND_LOOP() if (thermalManager.degTargetHotend(e) > 0) return true;
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
if (thermalManager.degTargetBed() > 0) return true;
|
||||
#endif
|
||||
|
||||
#if HOTENDS && AUTO_POWER_E_TEMP
|
||||
HOTEND_LOOP() if (thermalManager.degHotend(e) >= AUTO_POWER_E_TEMP) return true;
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_CHAMBER && AUTO_POWER_CHAMBER_TEMP
|
||||
if (thermalManager.degChamber() >= AUTO_POWER_CHAMBER_TEMP) return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Power::check() {
|
||||
static millis_t nextPowerCheck = 0;
|
||||
millis_t ms = millis();
|
||||
if (ELAPSED(ms, nextPowerCheck)) {
|
||||
nextPowerCheck = ms + 2500UL;
|
||||
if (is_power_needed())
|
||||
power_on();
|
||||
else if (!lastPowerOn || ELAPSED(ms, lastPowerOn + (POWER_TIMEOUT) * 1000UL))
|
||||
power_off();
|
||||
}
|
||||
/**
|
||||
* Initialize pins & state for the power manager.
|
||||
*
|
||||
*/
|
||||
void Power::init() {
|
||||
psu_on = ENABLED(PSU_DEFAULT_OFF); // Set opposite state to get full power_off/on
|
||||
TERN(PSU_DEFAULT_OFF, power_off(), power_on());
|
||||
}
|
||||
|
||||
/**
|
||||
* Power on if the power is currently off.
|
||||
* Restores stepper drivers and processes any PSU_POWERUP_GCODE.
|
||||
*
|
||||
*/
|
||||
void Power::power_on() {
|
||||
lastPowerOn = millis();
|
||||
if (!powersupply_on) {
|
||||
PSU_PIN_ON();
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
const millis_t now = millis();
|
||||
lastPowerOn = now + !now;
|
||||
#endif
|
||||
|
||||
#if HAS_TRINAMIC_CONFIG
|
||||
delay(PSU_POWERUP_DELAY); // Wait for power to settle
|
||||
restore_stepper_drivers();
|
||||
#endif
|
||||
}
|
||||
if (psu_on) return;
|
||||
|
||||
OUT_WRITE(PS_ON_PIN, PSU_ACTIVE_STATE);
|
||||
psu_on = true;
|
||||
safe_delay(PSU_POWERUP_DELAY);
|
||||
restore_stepper_drivers();
|
||||
TERN_(HAS_TRINAMIC_CONFIG, safe_delay(PSU_POWERUP_DELAY));
|
||||
|
||||
#ifdef PSU_POWERUP_GCODE
|
||||
GcodeSuite::process_subcommands_now_P(PSTR(PSU_POWERUP_GCODE));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Power off if the power is currently on.
|
||||
* Processes any PSU_POWEROFF_GCODE and makes a PS_OFF_SOUND if enabled.
|
||||
*
|
||||
*/
|
||||
void Power::power_off() {
|
||||
if (powersupply_on) PSU_PIN_OFF();
|
||||
if (!psu_on) return;
|
||||
|
||||
#ifdef PSU_POWEROFF_GCODE
|
||||
GcodeSuite::process_subcommands_now_P(PSTR(PSU_POWEROFF_GCODE));
|
||||
#endif
|
||||
|
||||
#if ENABLED(PS_OFF_SOUND)
|
||||
BUZZ(1000, 659);
|
||||
#endif
|
||||
|
||||
OUT_WRITE(PS_ON_PIN, !PSU_ACTIVE_STATE);
|
||||
psu_on = false;
|
||||
}
|
||||
|
||||
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
|
||||
#ifndef POWER_TIMEOUT
|
||||
#define POWER_TIMEOUT 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Check all conditions that would signal power needing to be on.
|
||||
*
|
||||
* @returns bool if power is needed
|
||||
*/
|
||||
bool Power::is_power_needed() {
|
||||
|
||||
// If any of the stepper drivers are enabled...
|
||||
if (stepper.axis_enabled.bits) return true;
|
||||
|
||||
if (printJobOngoing() || printingIsPaused()) return true;
|
||||
|
||||
#if ENABLED(AUTO_POWER_FANS)
|
||||
FANS_LOOP(i) if (thermalManager.fan_speed[i]) return true;
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_POWER_E_FANS)
|
||||
HOTEND_LOOP() if (thermalManager.autofan_speed[e]) return true;
|
||||
#endif
|
||||
|
||||
#if BOTH(USE_CONTROLLER_FAN, AUTO_POWER_CONTROLLERFAN)
|
||||
if (controllerFan.state()) return true;
|
||||
#endif
|
||||
|
||||
if (TERN0(AUTO_POWER_CHAMBER_FAN, thermalManager.chamberfan_speed))
|
||||
return true;
|
||||
|
||||
if (TERN0(AUTO_POWER_COOLER_FAN, thermalManager.coolerfan_speed))
|
||||
return true;
|
||||
|
||||
#if HAS_HOTEND
|
||||
HOTEND_LOOP() if (thermalManager.degTargetHotend(e) > 0 || thermalManager.temp_hotend[e].soft_pwm_amount > 0) return true;
|
||||
#endif
|
||||
|
||||
if (TERN0(HAS_HEATED_BED, thermalManager.degTargetBed() > 0 || thermalManager.temp_bed.soft_pwm_amount > 0)) return true;
|
||||
|
||||
#if HAS_HOTEND && AUTO_POWER_E_TEMP
|
||||
HOTEND_LOOP() if (thermalManager.degHotend(e) >= (AUTO_POWER_E_TEMP)) return true;
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_CHAMBER && AUTO_POWER_CHAMBER_TEMP
|
||||
if (thermalManager.degChamber() >= (AUTO_POWER_CHAMBER_TEMP)) return true;
|
||||
#endif
|
||||
|
||||
#if HAS_COOLER && AUTO_POWER_COOLER_TEMP
|
||||
if (thermalManager.degCooler() >= (AUTO_POWER_COOLER_TEMP)) return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should power off automatically (POWER_TIMEOUT elapsed, !is_power_needed).
|
||||
*
|
||||
* @param pause pause the 'timer'
|
||||
*/
|
||||
void Power::check(const bool pause) {
|
||||
static millis_t nextPowerCheck = 0;
|
||||
const millis_t now = millis();
|
||||
#if POWER_TIMEOUT > 0
|
||||
static bool _pause = false;
|
||||
if (pause != _pause) {
|
||||
lastPowerOn = now + !now;
|
||||
_pause = pause;
|
||||
}
|
||||
if (pause) return;
|
||||
#endif
|
||||
if (ELAPSED(now, nextPowerCheck)) {
|
||||
nextPowerCheck = now + 2500UL;
|
||||
if (is_power_needed())
|
||||
power_on();
|
||||
else if (!lastPowerOn || (POWER_TIMEOUT > 0 && ELAPSED(now, lastPowerOn + SEC_TO_MS(POWER_TIMEOUT))))
|
||||
power_off();
|
||||
}
|
||||
}
|
||||
|
||||
#if POWER_OFF_DELAY > 0
|
||||
|
||||
/**
|
||||
* Power off with a delay. Power off is triggered by check() after the delay.
|
||||
*
|
||||
*/
|
||||
void Power::power_off_soon() {
|
||||
lastPowerOn = millis() - SEC_TO_MS(POWER_TIMEOUT) + SEC_TO_MS(POWER_OFF_DELAY);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // AUTO_POWER_CONTROL
|
||||
|
||||
#endif // PSU_CONTROL || AUTO_POWER_CONTROL
|
||||
|
||||
28
Marlin/src/feature/power.h
Executable file → Normal file
28
Marlin/src/feature/power.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -25,16 +25,32 @@
|
||||
* power.h - power control
|
||||
*/
|
||||
|
||||
#include "../core/millis_t.h"
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
#include "../core/millis_t.h"
|
||||
#endif
|
||||
|
||||
class Power {
|
||||
public:
|
||||
static void check();
|
||||
static bool psu_on;
|
||||
|
||||
static void init();
|
||||
static void power_on();
|
||||
static void power_off();
|
||||
private:
|
||||
static millis_t lastPowerOn;
|
||||
static bool is_power_needed();
|
||||
|
||||
#if ENABLED(AUTO_POWER_CONTROL) && POWER_OFF_DELAY > 0
|
||||
static void power_off_soon();
|
||||
#else
|
||||
static inline void power_off_soon() { power_off(); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
static void check(const bool pause);
|
||||
|
||||
private:
|
||||
static millis_t lastPowerOn;
|
||||
static bool is_power_needed();
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Power powerManager;
|
||||
|
||||
78
Marlin/src/feature/power_monitor.cpp
Normal file
78
Marlin/src/feature/power_monitor.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if HAS_POWER_MONITOR
|
||||
|
||||
#include "power_monitor.h"
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#include "../lcd/marlinui.h"
|
||||
#include "../lcd/lcdprint.h"
|
||||
#endif
|
||||
|
||||
#include "../libs/numtostr.h"
|
||||
|
||||
uint8_t PowerMonitor::flags; // = 0
|
||||
|
||||
#if ENABLED(POWER_MONITOR_CURRENT)
|
||||
pm_lpf_t<PowerMonitor::amps_adc_scale, PM_K_VALUE, PM_K_SCALE> PowerMonitor::amps;
|
||||
#endif
|
||||
#if ENABLED(POWER_MONITOR_VOLTAGE)
|
||||
pm_lpf_t<PowerMonitor::volts_adc_scale, PM_K_VALUE, PM_K_SCALE> PowerMonitor::volts;
|
||||
#endif
|
||||
|
||||
millis_t PowerMonitor::display_item_ms;
|
||||
uint8_t PowerMonitor::display_item;
|
||||
|
||||
PowerMonitor power_monitor; // Single instance - this calls the constructor
|
||||
|
||||
#if HAS_MARLINUI_U8GLIB
|
||||
|
||||
#if ENABLED(POWER_MONITOR_CURRENT)
|
||||
void PowerMonitor::draw_current() {
|
||||
const float amps = getAmps();
|
||||
lcd_put_u8str(amps < 100 ? ftostr31ns(amps) : ui16tostr4rj((uint16_t)amps));
|
||||
lcd_put_wchar('A');
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(POWER_MONITOR_VOLTAGE)
|
||||
void PowerMonitor::draw_voltage() {
|
||||
const float volts = getVolts();
|
||||
lcd_put_u8str(volts < 100 ? ftostr31ns(volts) : ui16tostr4rj((uint16_t)volts));
|
||||
lcd_put_wchar('V');
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_POWER_MONITOR_WATTS
|
||||
void PowerMonitor::draw_power() {
|
||||
const float power = getPower();
|
||||
lcd_put_u8str(power < 100 ? ftostr31ns(power) : ui16tostr4rj((uint16_t)power));
|
||||
lcd_put_wchar('W');
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HAS_MARLINUI_U8GLIB
|
||||
|
||||
#endif // HAS_POWER_MONITOR
|
||||
138
Marlin/src/feature/power_monitor.h
Normal file
138
Marlin/src/feature/power_monitor.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#define PM_SAMPLE_RANGE HAL_ADC_RANGE
|
||||
#define PM_K_VALUE 6
|
||||
#define PM_K_SCALE 6
|
||||
|
||||
template <const float & SCALE, int K_VALUE, int K_SCALE>
|
||||
struct pm_lpf_t {
|
||||
uint32_t filter_buf;
|
||||
float value;
|
||||
void add_sample(const uint16_t sample) {
|
||||
filter_buf = filter_buf - (filter_buf >> K_VALUE) + (uint32_t(sample) << K_SCALE);
|
||||
}
|
||||
void capture() {
|
||||
value = filter_buf * (SCALE * (1.0f / (1UL << (PM_K_VALUE + PM_K_SCALE))));
|
||||
}
|
||||
void reset(uint16_t reset_value = 0) {
|
||||
filter_buf = uint32_t(reset_value) << (K_VALUE + K_SCALE);
|
||||
capture();
|
||||
}
|
||||
};
|
||||
|
||||
class PowerMonitor {
|
||||
private:
|
||||
#if ENABLED(POWER_MONITOR_CURRENT)
|
||||
static constexpr float amps_adc_scale = float(ADC_VREF) / (POWER_MONITOR_VOLTS_PER_AMP * PM_SAMPLE_RANGE);
|
||||
static pm_lpf_t<amps_adc_scale, PM_K_VALUE, PM_K_SCALE> amps;
|
||||
#endif
|
||||
#if ENABLED(POWER_MONITOR_VOLTAGE)
|
||||
static constexpr float volts_adc_scale = float(ADC_VREF) / (POWER_MONITOR_VOLTS_PER_VOLT * PM_SAMPLE_RANGE);
|
||||
static pm_lpf_t<volts_adc_scale, PM_K_VALUE, PM_K_SCALE> volts;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static uint8_t flags; // M430 flags to display current
|
||||
|
||||
static millis_t display_item_ms;
|
||||
static uint8_t display_item;
|
||||
|
||||
PowerMonitor() { reset(); }
|
||||
|
||||
enum PM_Display_Bit : uint8_t {
|
||||
PM_DISP_BIT_I, // Current display enable bit
|
||||
PM_DISP_BIT_V, // Voltage display enable bit
|
||||
PM_DISP_BIT_P // Power display enable bit
|
||||
};
|
||||
|
||||
#if ENABLED(POWER_MONITOR_CURRENT)
|
||||
FORCE_INLINE static float getAmps() { return amps.value + (POWER_MONITOR_CURRENT_OFFSET); }
|
||||
void add_current_sample(const uint16_t value) { amps.add_sample(value); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(POWER_MONITOR_VOLTAGE)
|
||||
FORCE_INLINE static float getVolts() { return volts.value + (POWER_MONITOR_VOLTAGE_OFFSET); }
|
||||
void add_voltage_sample(const uint16_t value) { volts.add_sample(value); }
|
||||
#else
|
||||
FORCE_INLINE static float getVolts() { return POWER_MONITOR_FIXED_VOLTAGE; }
|
||||
#endif
|
||||
|
||||
#if HAS_POWER_MONITOR_WATTS
|
||||
FORCE_INLINE static float getPower() { return getAmps() * getVolts(); }
|
||||
#endif
|
||||
|
||||
#if HAS_WIRED_LCD
|
||||
#if HAS_MARLINUI_U8GLIB && DISABLED(LIGHTWEIGHT_UI)
|
||||
FORCE_INLINE static bool display_enabled() { return flags != 0x00; }
|
||||
#endif
|
||||
#if ENABLED(POWER_MONITOR_CURRENT)
|
||||
static void draw_current();
|
||||
FORCE_INLINE static bool current_display_enabled() { return TEST(flags, PM_DISP_BIT_I); }
|
||||
FORCE_INLINE static void set_current_display(const bool b) { SET_BIT_TO(flags, PM_DISP_BIT_I, b); }
|
||||
FORCE_INLINE static void toggle_current_display() { TBI(flags, PM_DISP_BIT_I); }
|
||||
#endif
|
||||
#if ENABLED(POWER_MONITOR_VOLTAGE)
|
||||
static void draw_voltage();
|
||||
FORCE_INLINE static bool voltage_display_enabled() { return TEST(flags, PM_DISP_BIT_V); }
|
||||
FORCE_INLINE static void set_voltage_display(const bool b) { SET_BIT_TO(flags, PM_DISP_BIT_V, b); }
|
||||
FORCE_INLINE static void toggle_voltage_display() { TBI(flags, PM_DISP_BIT_V); }
|
||||
#endif
|
||||
#if HAS_POWER_MONITOR_WATTS
|
||||
static void draw_power();
|
||||
FORCE_INLINE static bool power_display_enabled() { return TEST(flags, PM_DISP_BIT_P); }
|
||||
FORCE_INLINE static void set_power_display(const bool b) { SET_BIT_TO(flags, PM_DISP_BIT_P, b); }
|
||||
FORCE_INLINE static void toggle_power_display() { TBI(flags, PM_DISP_BIT_P); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static void reset() {
|
||||
flags = 0x00;
|
||||
|
||||
#if ENABLED(POWER_MONITOR_CURRENT)
|
||||
amps.reset();
|
||||
#endif
|
||||
|
||||
#if ENABLED(POWER_MONITOR_VOLTAGE)
|
||||
volts.reset();
|
||||
#endif
|
||||
|
||||
#if ENABLED(SDSUPPORT)
|
||||
display_item_ms = 0;
|
||||
display_item = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void capture_values() {
|
||||
#if ENABLED(POWER_MONITOR_CURRENT)
|
||||
amps.capture();
|
||||
#endif
|
||||
#if ENABLED(POWER_MONITOR_VOLTAGE)
|
||||
volts.capture();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
extern PowerMonitor power_monitor;
|
||||
502
Marlin/src/feature/powerloss.cpp
Executable file → Normal file
502
Marlin/src/feature/powerloss.cpp
Executable file → Normal file
@@ -16,12 +16,12 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* power_loss_recovery.cpp - Resume an SD print after power-loss
|
||||
* feature/powerloss.cpp - Resume an SD print after power-loss
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
@@ -40,8 +40,12 @@ uint8_t PrintJobRecovery::queue_index_r;
|
||||
uint32_t PrintJobRecovery::cmd_sdpos, // = 0
|
||||
PrintJobRecovery::sdpos[BUFSIZE];
|
||||
|
||||
#if HAS_DWIN_E3V2_BASIC
|
||||
bool PrintJobRecovery::dwin_flag; // = false
|
||||
#endif
|
||||
|
||||
#include "../sd/cardreader.h"
|
||||
#include "../lcd/ultralcd.h"
|
||||
#include "../lcd/marlinui.h"
|
||||
#include "../gcode/queue.h"
|
||||
#include "../gcode/gcode.h"
|
||||
#include "../module/motion.h"
|
||||
@@ -62,12 +66,13 @@ PrintJobRecovery recovery;
|
||||
#ifndef POWER_LOSS_PURGE_LEN
|
||||
#define POWER_LOSS_PURGE_LEN 0
|
||||
#endif
|
||||
|
||||
#if DISABLED(BACKUP_POWER_SUPPLY)
|
||||
#undef POWER_LOSS_RETRACT_LEN // No retract at outage without backup power
|
||||
#endif
|
||||
#ifndef POWER_LOSS_RETRACT_LEN
|
||||
#define POWER_LOSS_RETRACT_LEN 0
|
||||
#endif
|
||||
#ifndef POWER_LOSS_ZRAISE
|
||||
#define POWER_LOSS_ZRAISE 2
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Clear the recovery info
|
||||
@@ -103,8 +108,8 @@ void PrintJobRecovery::check() {
|
||||
//if (!card.isMounted()) card.mount();
|
||||
if (card.isMounted()) {
|
||||
load();
|
||||
if (!valid()) return purge();
|
||||
queue.inject_P(PSTR("M1000 S"));
|
||||
if (!valid()) return cancel();
|
||||
queue.inject_P(PSTR("M1000S"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,14 +137,16 @@ void PrintJobRecovery::load() {
|
||||
* Set info fields that won't change
|
||||
*/
|
||||
void PrintJobRecovery::prepare() {
|
||||
card.getAbsFilename(info.sd_filename); // SD filename
|
||||
card.getAbsFilenameInCWD(info.sd_filename); // SD filename
|
||||
cmd_sdpos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current machine state to the power-loss recovery file
|
||||
*/
|
||||
void PrintJobRecovery::save(const bool force/*=false*/) {
|
||||
void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POWER_LOSS_ZRAISE*/, const bool raised/*=false*/) {
|
||||
|
||||
// We don't check IS_SD_PRINTING here so a save may occur during a pause
|
||||
|
||||
#if SAVE_INFO_INTERVAL_MS > 0
|
||||
static millis_t next_save_ms; // = 0
|
||||
@@ -172,64 +179,55 @@ void PrintJobRecovery::save(const bool force/*=false*/) {
|
||||
|
||||
// Machine state
|
||||
info.current_position = current_position;
|
||||
#if HAS_HOME_OFFSET
|
||||
info.home_offset = home_offset;
|
||||
#endif
|
||||
#if HAS_POSITION_SHIFT
|
||||
info.position_shift = position_shift;
|
||||
#endif
|
||||
info.feedrate = uint16_t(feedrate_mm_s * 60.0f);
|
||||
info.feedrate = uint16_t(MMS_TO_MMM(feedrate_mm_s));
|
||||
info.zraise = zraise;
|
||||
info.flag.raised = raised; // Was Z raised before power-off?
|
||||
|
||||
#if EXTRUDERS > 1
|
||||
info.active_extruder = active_extruder;
|
||||
#endif
|
||||
TERN_(GCODE_REPEAT_MARKERS, info.stored_repeat = repeat);
|
||||
TERN_(HAS_HOME_OFFSET, info.home_offset = home_offset);
|
||||
TERN_(HAS_POSITION_SHIFT, info.position_shift = position_shift);
|
||||
E_TERN_(info.active_extruder = active_extruder);
|
||||
|
||||
#if DISABLED(NO_VOLUMETRICS)
|
||||
info.volumetric_enabled = parser.volumetric_enabled;
|
||||
#if EXTRUDERS > 1
|
||||
info.flag.volumetric_enabled = parser.volumetric_enabled;
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
for (int8_t e = 0; e < EXTRUDERS; e++) info.filament_size[e] = planner.filament_size[e];
|
||||
#else
|
||||
if (parser.volumetric_enabled) info.filament_size = planner.filament_size[active_extruder];
|
||||
if (parser.volumetric_enabled) info.filament_size[0] = planner.filament_size[active_extruder];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if EXTRUDERS
|
||||
HOTEND_LOOP() info.target_temperature[e] = thermalManager.temp_hotend[e].target;
|
||||
#if HAS_EXTRUDERS
|
||||
HOTEND_LOOP() info.target_temperature[e] = thermalManager.degTargetHotend(e);
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
info.target_temperature_bed = thermalManager.temp_bed.target;
|
||||
#endif
|
||||
TERN_(HAS_HEATED_BED, info.target_temperature_bed = thermalManager.degTargetBed());
|
||||
|
||||
#if FAN_COUNT
|
||||
#if HAS_FAN
|
||||
COPY(info.fan_speed, thermalManager.fan_speed);
|
||||
#endif
|
||||
|
||||
#if HAS_LEVELING
|
||||
info.leveling = planner.leveling_active;
|
||||
info.fade = (
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
planner.z_fade_height
|
||||
#else
|
||||
0
|
||||
#endif
|
||||
);
|
||||
info.flag.leveling = planner.leveling_active;
|
||||
info.fade = TERN0(ENABLE_LEVELING_FADE_HEIGHT, planner.z_fade_height);
|
||||
#endif
|
||||
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
memcpy(&info.gradient, &mixer.gradient, sizeof(info.gradient));
|
||||
#endif
|
||||
TERN_(GRADIENT_MIX, memcpy(&info.gradient, &mixer.gradient, sizeof(info.gradient)));
|
||||
|
||||
#if ENABLED(FWRETRACT)
|
||||
COPY(info.retract, fwretract.current_retract);
|
||||
info.retract_hop = fwretract.current_hop;
|
||||
#endif
|
||||
|
||||
// Elapsed print job time
|
||||
info.print_job_elapsed = print_job_timer.duration();
|
||||
|
||||
// Relative axis modes
|
||||
info.axis_relative = gcode.axis_relative;
|
||||
|
||||
// Elapsed print job time
|
||||
info.print_job_elapsed = print_job_timer.duration();
|
||||
// Misc. Marlin flags
|
||||
info.flag.dryrun = !!(marlin_debug_flags & MARLIN_DEBUG_DRYRUN);
|
||||
info.flag.allow_cold_extrusion = TERN0(PREVENT_COLD_EXTRUSION, thermalManager.allow_cold_extrude);
|
||||
|
||||
write();
|
||||
}
|
||||
@@ -237,33 +235,75 @@ void PrintJobRecovery::save(const bool force/*=false*/) {
|
||||
|
||||
#if PIN_EXISTS(POWER_LOSS)
|
||||
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY)
|
||||
|
||||
void PrintJobRecovery::retract_and_lift(const_float_t zraise) {
|
||||
#if POWER_LOSS_RETRACT_LEN || POWER_LOSS_ZRAISE
|
||||
|
||||
gcode.set_relative_mode(true); // Use relative coordinates
|
||||
|
||||
#if POWER_LOSS_RETRACT_LEN
|
||||
// Retract filament now
|
||||
gcode.process_subcommands_now_P(PSTR("G1 F3000 E-" STRINGIFY(POWER_LOSS_RETRACT_LEN)));
|
||||
#endif
|
||||
|
||||
#if POWER_LOSS_ZRAISE
|
||||
// Raise the Z axis now
|
||||
if (zraise) {
|
||||
char cmd[20], str_1[16];
|
||||
sprintf_P(cmd, PSTR("G0Z%s"), dtostrf(zraise, 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
#else
|
||||
UNUSED(zraise);
|
||||
#endif
|
||||
|
||||
//gcode.axis_relative = info.axis_relative;
|
||||
planner.synchronize();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* An outage was detected by a sensor pin.
|
||||
* - If not SD printing, let the machine turn off on its own with no "KILL" screen
|
||||
* - Disable all heaters first to save energy
|
||||
* - Save the recovery data for the current instant
|
||||
* - If backup power is available Retract E and Raise Z
|
||||
* - Go to the KILL screen
|
||||
*/
|
||||
void PrintJobRecovery::_outage() {
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY)
|
||||
static bool lock = false;
|
||||
if (lock) return; // No re-entrance from idle() during raise_z()
|
||||
if (lock) return; // No re-entrance from idle() during retract_and_lift()
|
||||
lock = true;
|
||||
#endif
|
||||
if (IS_SD_PRINTING()) save(true);
|
||||
|
||||
#if POWER_LOSS_ZRAISE
|
||||
// Get the limited Z-raise to do now or on resume
|
||||
const float zraise = _MAX(0, _MIN(current_position.z + POWER_LOSS_ZRAISE, Z_MAX_POS - 1) - current_position.z);
|
||||
#else
|
||||
constexpr float zraise = 0;
|
||||
#endif
|
||||
|
||||
// Save the current position, distance that Z was (or should be) raised,
|
||||
// and a flag whether the raise was already done here.
|
||||
if (IS_SD_PRINTING()) save(true, zraise, ENABLED(BACKUP_POWER_SUPPLY));
|
||||
|
||||
// Disable all heaters to reduce power loss
|
||||
thermalManager.disable_all_heaters();
|
||||
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY)
|
||||
raise_z();
|
||||
// Do a hard-stop of the steppers (with possibly a loud thud)
|
||||
quickstop_stepper();
|
||||
// With backup power a retract and raise can be done now
|
||||
retract_and_lift(zraise);
|
||||
#endif
|
||||
|
||||
kill(GET_TEXT(MSG_OUTAGE_RECOVERY));
|
||||
}
|
||||
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY)
|
||||
|
||||
void PrintJobRecovery::raise_z() {
|
||||
// Disable all heaters to reduce power loss
|
||||
thermalManager.disable_all_heaters();
|
||||
quickstop_stepper();
|
||||
// Raise Z axis
|
||||
gcode.process_subcommands_now_P(PSTR("G91\nG0 Z" STRINGIFY(POWER_LOSS_ZRAISE)));
|
||||
planner.synchronize();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -285,111 +325,178 @@ void PrintJobRecovery::write() {
|
||||
*/
|
||||
void PrintJobRecovery::resume() {
|
||||
|
||||
char cmd[MAX_CMD_SIZE+16], str_1[16], str_2[16];
|
||||
|
||||
const uint32_t resume_sdpos = info.sdpos; // Get here before the stepper ISR overwrites it
|
||||
|
||||
// Apply the dry-run flag if enabled
|
||||
if (info.flag.dryrun) marlin_debug_flags |= MARLIN_DEBUG_DRYRUN;
|
||||
|
||||
// Restore cold extrusion permission
|
||||
TERN_(PREVENT_COLD_EXTRUSION, thermalManager.allow_cold_extrude = info.flag.allow_cold_extrusion);
|
||||
|
||||
#if HAS_LEVELING
|
||||
// Make sure leveling is off before any G92 and G28
|
||||
gcode.process_subcommands_now_P(PSTR("M420 S0 Z0"));
|
||||
#endif
|
||||
|
||||
// Reset E, raise Z, home XY...
|
||||
gcode.process_subcommands_now_P(PSTR("G92.9 E0"
|
||||
#if Z_HOME_DIR > 0
|
||||
#if HAS_HEATED_BED
|
||||
const celsius_t bt = info.target_temperature_bed;
|
||||
if (bt) {
|
||||
// Restore the bed temperature
|
||||
sprintf_P(cmd, PSTR("M190S%i"), bt);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
#endif
|
||||
|
||||
// If Z homing goes to max, just reset E and home all
|
||||
"\n"
|
||||
"G28R0"
|
||||
#if ENABLED(MARLIN_DEV_MODE)
|
||||
"S"
|
||||
#endif
|
||||
// Heat hotend enough to soften material
|
||||
#if HAS_HOTEND
|
||||
HOTEND_LOOP() {
|
||||
const celsius_t et = _MAX(info.target_temperature[e], 180);
|
||||
if (et) {
|
||||
#if HAS_MULTI_HOTEND
|
||||
sprintf_P(cmd, PSTR("T%iS"), e);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
sprintf_P(cmd, PSTR("M109S%i"), et);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#else // "G92.9 E0 ..."
|
||||
// Interpret the saved Z according to flags
|
||||
const float z_print = info.current_position.z,
|
||||
z_raised = z_print + info.zraise;
|
||||
|
||||
// Set Z to 0, raise Z by RECOVERY_ZRAISE, and Home (XY only for Cartesian)
|
||||
// with no raise. (Only do simulated homing in Marlin Dev Mode.)
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY)
|
||||
"Z" STRINGIFY(POWER_LOSS_ZRAISE) // Z-axis was already raised at outage
|
||||
#else
|
||||
"Z0\n" // Set Z=0
|
||||
"G1Z" STRINGIFY(POWER_LOSS_ZRAISE) // Raise Z
|
||||
#endif
|
||||
"\n"
|
||||
//
|
||||
// Home the axes that can safely be homed, and
|
||||
// establish the current position as best we can.
|
||||
//
|
||||
|
||||
"G28R0"
|
||||
#if ENABLED(MARLIN_DEV_MODE)
|
||||
"S"
|
||||
#elif !IS_KINEMATIC
|
||||
"XY"
|
||||
#endif
|
||||
gcode.process_subcommands_now_P(PSTR("G92.9E0")); // Reset E to 0
|
||||
|
||||
#if Z_HOME_TO_MAX
|
||||
|
||||
float z_now = z_raised;
|
||||
|
||||
// If Z homing goes to max then just move back to the "raised" position
|
||||
sprintf_P(cmd, PSTR(
|
||||
"G28R0\n" // Home all axes (no raise)
|
||||
"G1Z%sF1200" // Move Z down to (raised) height
|
||||
), dtostrf(z_now, 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
|
||||
#elif DISABLED(BELTPRINTER)
|
||||
|
||||
#if ENABLED(POWER_LOSS_RECOVER_ZHOME) && defined(POWER_LOSS_ZHOME_POS)
|
||||
#define HOMING_Z_DOWN 1
|
||||
#else
|
||||
#define HOME_XY_ONLY 1
|
||||
#endif
|
||||
));
|
||||
|
||||
// Pretend that all axes are homed
|
||||
axis_homed = axis_known_position = xyz_bits;
|
||||
float z_now = info.flag.raised ? z_raised : z_print;
|
||||
|
||||
char cmd[MAX_CMD_SIZE+16], str_1[16], str_2[16];
|
||||
// Reset E to 0 and set Z to the real position
|
||||
#if HOME_XY_ONLY
|
||||
sprintf_P(cmd, PSTR("G92.9Z%s"), dtostrf(z_now, 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
|
||||
// Select the previously active tool (with no_move)
|
||||
#if EXTRUDERS > 1
|
||||
sprintf_P(cmd, PSTR("T%i S"), info.active_extruder);
|
||||
// Does Z need to be raised now? It should be raised before homing XY.
|
||||
if (z_raised > z_now) {
|
||||
z_now = z_raised;
|
||||
sprintf_P(cmd, PSTR("G1Z%sF600"), dtostrf(z_now, 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
|
||||
// Home XY with no Z raise, and also home Z here if Z isn't homing down below.
|
||||
gcode.process_subcommands_now_P(PSTR("G28R0" TERN_(HOME_XY_ONLY, "XY"))); // No raise during G28
|
||||
|
||||
#endif
|
||||
|
||||
#if HOMING_Z_DOWN
|
||||
// Move to a safe XY position and home Z while avoiding the print.
|
||||
constexpr xy_pos_t p = POWER_LOSS_ZHOME_POS;
|
||||
sprintf_P(cmd, PSTR("G1X%sY%sF1000\nG28Z"), dtostrf(p.x, 1, 3, str_1), dtostrf(p.y, 1, 3, str_2));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
|
||||
// Mark all axes as having been homed (no effect on current_position)
|
||||
set_all_homed();
|
||||
|
||||
#if HAS_LEVELING
|
||||
// Restore Z fade and possibly re-enable bed leveling compensation.
|
||||
// Leveling may already be enabled due to the ENABLE_LEVELING_AFTER_G28 option.
|
||||
// TODO: Add a G28 parameter to leave leveling disabled.
|
||||
sprintf_P(cmd, PSTR("M420S%cZ%s"), '0' + (char)info.flag.leveling, dtostrf(info.fade, 1, 1, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
|
||||
#if HOME_XY_ONLY
|
||||
// The physical Z was adjusted at power-off so undo the M420S1 correction to Z with G92.9.
|
||||
sprintf_P(cmd, PSTR("G92.9Z%s"), dtostrf(z_now, 1, 1, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(POWER_LOSS_RECOVER_ZHOME)
|
||||
// Z was homed down to the bed, so move up to the raised height.
|
||||
z_now = z_raised;
|
||||
sprintf_P(cmd, PSTR("G1Z%sF600"), dtostrf(z_now, 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
|
||||
// Recover volumetric extrusion state
|
||||
#if DISABLED(NO_VOLUMETRICS)
|
||||
#if EXTRUDERS > 1
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
for (int8_t e = 0; e < EXTRUDERS; e++) {
|
||||
dtostrf(info.filament_size[e], 1, 3, str_1);
|
||||
sprintf_P(cmd, PSTR("M200 T%i D%s"), e, str_1);
|
||||
sprintf_P(cmd, PSTR("M200T%iD%s"), e, dtostrf(info.filament_size[e], 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
if (!info.volumetric_enabled) {
|
||||
sprintf_P(cmd, PSTR("M200 T%i D0"), info.active_extruder);
|
||||
if (!info.flag.volumetric_enabled) {
|
||||
sprintf_P(cmd, PSTR("M200T%iD0"), info.active_extruder);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
#else
|
||||
if (info.volumetric_enabled) {
|
||||
dtostrf(info.filament_size, 1, 3, str_1);
|
||||
sprintf_P(cmd, PSTR("M200 D%s"), str_1);
|
||||
if (info.flag.volumetric_enabled) {
|
||||
sprintf_P(cmd, PSTR("M200D%s"), dtostrf(info.filament_size[0], 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
const int16_t bt = info.target_temperature_bed;
|
||||
if (bt) {
|
||||
// Restore the bed temperature
|
||||
sprintf_P(cmd, PSTR("M190 S%i"), bt);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Restore all hotend temperatures
|
||||
#if HOTENDS
|
||||
#if HAS_HOTEND
|
||||
HOTEND_LOOP() {
|
||||
const int16_t et = info.target_temperature[e];
|
||||
const celsius_t et = info.target_temperature[e];
|
||||
if (et) {
|
||||
#if HOTENDS > 1
|
||||
sprintf_P(cmd, PSTR("T%i"), e);
|
||||
#if HAS_MULTI_HOTEND
|
||||
sprintf_P(cmd, PSTR("T%iS"), e);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
sprintf_P(cmd, PSTR("M109 S%i"), et);
|
||||
sprintf_P(cmd, PSTR("M109S%i"), et);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Restore print cooling fan speeds
|
||||
FANS_LOOP(i) {
|
||||
uint8_t f = info.fan_speed[i];
|
||||
if (f) {
|
||||
sprintf_P(cmd, PSTR("M106 P%i S%i"), i, f);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
}
|
||||
// Restore the previously active tool (with no_move)
|
||||
#if HAS_MULTI_EXTRUDER || HAS_MULTI_HOTEND
|
||||
sprintf_P(cmd, PSTR("T%i S"), info.active_extruder);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
|
||||
// Restore retract and hop state
|
||||
// Restore print cooling fan speeds
|
||||
#if HAS_FAN
|
||||
FANS_LOOP(i) {
|
||||
const int f = info.fan_speed[i];
|
||||
if (f) {
|
||||
sprintf_P(cmd, PSTR("M106P%iS%i"), i, f);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Restore retract and hop state from an active `G10` command
|
||||
#if ENABLED(FWRETRACT)
|
||||
LOOP_L_N(e, EXTRUDERS) {
|
||||
if (info.retract[e] != 0.0) {
|
||||
@@ -400,124 +507,125 @@ void PrintJobRecovery::resume() {
|
||||
fwretract.current_hop = info.retract_hop;
|
||||
#endif
|
||||
|
||||
#if HAS_LEVELING
|
||||
// Restore leveling state before 'G92 Z' to ensure
|
||||
// the Z stepper count corresponds to the native Z.
|
||||
if (info.fade || info.leveling) {
|
||||
sprintf_P(cmd, PSTR("M420 S%i Z%s"), int(info.leveling), dtostrf(info.fade, 1, 1, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
memcpy(&mixer.gradient, &info.gradient, sizeof(info.gradient));
|
||||
#endif
|
||||
|
||||
// Extrude and retract to clean the nozzle
|
||||
#if POWER_LOSS_PURGE_LEN
|
||||
//sprintf_P(cmd, PSTR("G1 E%d F200"), POWER_LOSS_PURGE_LEN);
|
||||
//gcode.process_subcommands_now(cmd);
|
||||
gcode.process_subcommands_now_P(PSTR("G1 E" STRINGIFY(POWER_LOSS_PURGE_LEN) " F200"));
|
||||
// Un-retract if there was a retract at outage
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY) && POWER_LOSS_RETRACT_LEN > 0
|
||||
gcode.process_subcommands_now_P(PSTR("G1E" STRINGIFY(POWER_LOSS_RETRACT_LEN) "F3000"));
|
||||
#endif
|
||||
|
||||
#if POWER_LOSS_RETRACT_LEN
|
||||
sprintf_P(cmd, PSTR("G1 E%d F3000"), POWER_LOSS_PURGE_LEN - (POWER_LOSS_RETRACT_LEN));
|
||||
// Additional purge on resume if configured
|
||||
#if POWER_LOSS_PURGE_LEN
|
||||
sprintf_P(cmd, PSTR("G1 E%d F3000"), (POWER_LOSS_PURGE_LEN) + (POWER_LOSS_RETRACT_LEN));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
|
||||
// Move back to the saved XY
|
||||
sprintf_P(cmd, PSTR("G1 X%s Y%s F3000"),
|
||||
#if ENABLED(NOZZLE_CLEAN_FEATURE)
|
||||
gcode.process_subcommands_now_P(PSTR("G12"));
|
||||
#endif
|
||||
|
||||
// Move back over to the saved XY
|
||||
sprintf_P(cmd, PSTR("G1X%sY%sF3000"),
|
||||
dtostrf(info.current_position.x, 1, 3, str_1),
|
||||
dtostrf(info.current_position.y, 1, 3, str_2)
|
||||
);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
|
||||
// Move back to the saved Z
|
||||
dtostrf(info.current_position.z, 1, 3, str_1);
|
||||
#if Z_HOME_DIR > 0
|
||||
sprintf_P(cmd, PSTR("G1 Z%s F200"), str_1);
|
||||
#else
|
||||
gcode.process_subcommands_now_P(PSTR("G1 Z0 F200"));
|
||||
sprintf_P(cmd, PSTR("G92.9 Z%s"), str_1);
|
||||
#endif
|
||||
// Move back down to the saved Z for printing
|
||||
sprintf_P(cmd, PSTR("G1Z%sF600"), dtostrf(z_print, 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
|
||||
// Un-retract
|
||||
#if POWER_LOSS_PURGE_LEN
|
||||
//sprintf_P(cmd, PSTR("G1 E%d F3000"), POWER_LOSS_PURGE_LEN);
|
||||
//gcode.process_subcommands_now(cmd);
|
||||
gcode.process_subcommands_now_P(PSTR("G1 E" STRINGIFY(POWER_LOSS_PURGE_LEN) " F3000"));
|
||||
#endif
|
||||
|
||||
// Restore the feedrate
|
||||
sprintf_P(cmd, PSTR("G1 F%d"), info.feedrate);
|
||||
sprintf_P(cmd, PSTR("G1F%d"), info.feedrate);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
|
||||
// Restore E position with G92.9
|
||||
sprintf_P(cmd, PSTR("G92.9 E%s"), dtostrf(info.current_position.e, 1, 3, str_1));
|
||||
sprintf_P(cmd, PSTR("G92.9E%s"), dtostrf(info.current_position.e, 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
|
||||
TERN_(GCODE_REPEAT_MARKERS, repeat = info.stored_repeat);
|
||||
TERN_(HAS_HOME_OFFSET, home_offset = info.home_offset);
|
||||
TERN_(HAS_POSITION_SHIFT, position_shift = info.position_shift);
|
||||
#if HAS_HOME_OFFSET || HAS_POSITION_SHIFT
|
||||
LOOP_LINEAR_AXES(i) update_workspace_offset((AxisEnum)i);
|
||||
#endif
|
||||
|
||||
// Relative axis modes
|
||||
gcode.axis_relative = info.axis_relative;
|
||||
|
||||
#if HAS_HOME_OFFSET
|
||||
home_offset = info.home_offset;
|
||||
#endif
|
||||
#if HAS_POSITION_SHIFT
|
||||
position_shift = info.position_shift;
|
||||
#endif
|
||||
#if HAS_HOME_OFFSET || HAS_POSITION_SHIFT
|
||||
LOOP_XYZ(i) update_workspace_offset((AxisEnum)i);
|
||||
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
|
||||
const uint8_t old_flags = marlin_debug_flags;
|
||||
marlin_debug_flags |= MARLIN_DEBUG_ECHO;
|
||||
#endif
|
||||
|
||||
// Continue to apply PLR when a file is resumed!
|
||||
enable(true);
|
||||
|
||||
// Resume the SD file from the last position
|
||||
char *fn = info.sd_filename;
|
||||
extern const char M23_STR[];
|
||||
sprintf_P(cmd, M23_STR, fn);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
sprintf_P(cmd, PSTR("M24 S%ld T%ld"), resume_sdpos, info.print_job_elapsed);
|
||||
sprintf_P(cmd, PSTR("M24S%ldT%ld"), resume_sdpos, info.print_job_elapsed);
|
||||
gcode.process_subcommands_now(cmd);
|
||||
|
||||
TERN_(DEBUG_POWER_LOSS_RECOVERY, marlin_debug_flags = old_flags);
|
||||
}
|
||||
|
||||
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
|
||||
|
||||
void PrintJobRecovery::debug(PGM_P const prefix) {
|
||||
DEBUG_PRINT_P(prefix);
|
||||
DEBUG_ECHOLNPAIR(" Job Recovery Info...\nvalid_head:", int(info.valid_head), " valid_foot:", int(info.valid_foot));
|
||||
DEBUG_ECHOPGM_P(prefix);
|
||||
DEBUG_ECHOLNPGM(" Job Recovery Info...\nvalid_head:", info.valid_head, " valid_foot:", info.valid_foot);
|
||||
if (info.valid_head) {
|
||||
if (info.valid_head == info.valid_foot) {
|
||||
DEBUG_ECHOPGM("current_position: ");
|
||||
LOOP_XYZE(i) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (i) DEBUG_CHAR(',');
|
||||
DEBUG_ECHO(info.current_position[i]);
|
||||
DEBUG_DECIMAL(info.current_position[i]);
|
||||
}
|
||||
DEBUG_EOL();
|
||||
|
||||
DEBUG_ECHOLNPGM("feedrate: ", info.feedrate);
|
||||
|
||||
DEBUG_ECHOLNPGM("zraise: ", info.zraise, " ", info.flag.raised ? "(before)" : "");
|
||||
|
||||
#if ENABLED(GCODE_REPEAT_MARKERS)
|
||||
DEBUG_ECHOLNPGM("repeat index: ", info.stored_repeat.index);
|
||||
LOOP_L_N(i, info.stored_repeat.index)
|
||||
DEBUG_ECHOLNPGM("..... sdpos: ", info.stored_repeat.marker.sdpos, " count: ", info.stored_repeat.marker.counter);
|
||||
#endif
|
||||
|
||||
#if HAS_HOME_OFFSET
|
||||
DEBUG_ECHOPGM("home_offset: ");
|
||||
LOOP_XYZ(i) {
|
||||
LOOP_LINEAR_AXES(i) {
|
||||
if (i) DEBUG_CHAR(',');
|
||||
DEBUG_ECHO(info.home_offset[i]);
|
||||
DEBUG_DECIMAL(info.home_offset[i]);
|
||||
}
|
||||
DEBUG_EOL();
|
||||
#endif
|
||||
|
||||
#if HAS_POSITION_SHIFT
|
||||
DEBUG_ECHOPGM("position_shift: ");
|
||||
LOOP_XYZ(i) {
|
||||
LOOP_LINEAR_AXES(i) {
|
||||
if (i) DEBUG_CHAR(',');
|
||||
DEBUG_ECHO(info.position_shift[i]);
|
||||
DEBUG_DECIMAL(info.position_shift[i]);
|
||||
}
|
||||
DEBUG_EOL();
|
||||
#endif
|
||||
|
||||
DEBUG_ECHOLNPAIR("feedrate: ", info.feedrate);
|
||||
|
||||
#if EXTRUDERS > 1
|
||||
DEBUG_ECHOLNPAIR("active_extruder: ", int(info.active_extruder));
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
DEBUG_ECHOLNPGM("active_extruder: ", info.active_extruder);
|
||||
#endif
|
||||
|
||||
#if HOTENDS
|
||||
#if DISABLED(NO_VOLUMETRICS)
|
||||
DEBUG_ECHOPGM("filament_size:");
|
||||
LOOP_L_N(i, EXTRUDERS) DEBUG_ECHOLNPGM(" ", info.filament_size[i]);
|
||||
DEBUG_EOL();
|
||||
#endif
|
||||
|
||||
#if HAS_HOTEND
|
||||
DEBUG_ECHOPGM("target_temperature: ");
|
||||
HOTEND_LOOP() {
|
||||
DEBUG_ECHO(info.target_temperature[e]);
|
||||
@@ -527,21 +635,22 @@ void PrintJobRecovery::resume() {
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
DEBUG_ECHOLNPAIR("target_temperature_bed: ", info.target_temperature_bed);
|
||||
DEBUG_ECHOLNPGM("target_temperature_bed: ", info.target_temperature_bed);
|
||||
#endif
|
||||
|
||||
#if FAN_COUNT
|
||||
#if HAS_FAN
|
||||
DEBUG_ECHOPGM("fan_speed: ");
|
||||
FANS_LOOP(i) {
|
||||
DEBUG_ECHO(int(info.fan_speed[i]));
|
||||
DEBUG_ECHO(info.fan_speed[i]);
|
||||
if (i < FAN_COUNT - 1) DEBUG_CHAR(',');
|
||||
}
|
||||
DEBUG_EOL();
|
||||
#endif
|
||||
|
||||
#if HAS_LEVELING
|
||||
DEBUG_ECHOLNPAIR("leveling: ", int(info.leveling), "\n fade: ", int(info.fade));
|
||||
DEBUG_ECHOLNPGM("leveling: ", info.flag.leveling ? "ON" : "OFF", " fade: ", info.fade);
|
||||
#endif
|
||||
|
||||
#if ENABLED(FWRETRACT)
|
||||
DEBUG_ECHOPGM("retract: ");
|
||||
for (int8_t e = 0; e < EXTRUDERS; e++) {
|
||||
@@ -549,11 +658,30 @@ void PrintJobRecovery::resume() {
|
||||
if (e < EXTRUDERS - 1) DEBUG_CHAR(',');
|
||||
}
|
||||
DEBUG_EOL();
|
||||
DEBUG_ECHOLNPAIR("retract_hop: ", info.retract_hop);
|
||||
DEBUG_ECHOLNPGM("retract_hop: ", info.retract_hop);
|
||||
#endif
|
||||
DEBUG_ECHOLNPAIR("sd_filename: ", info.sd_filename);
|
||||
DEBUG_ECHOLNPAIR("sdpos: ", info.sdpos);
|
||||
DEBUG_ECHOLNPAIR("print_job_elapsed: ", info.print_job_elapsed);
|
||||
|
||||
// Mixing extruder and gradient
|
||||
#if BOTH(MIXING_EXTRUDER, GRADIENT_MIX)
|
||||
DEBUG_ECHOLNPGM("gradient: ", info.gradient.enabled ? "ON" : "OFF");
|
||||
#endif
|
||||
|
||||
DEBUG_ECHOLNPGM("sd_filename: ", info.sd_filename);
|
||||
DEBUG_ECHOLNPGM("sdpos: ", info.sdpos);
|
||||
DEBUG_ECHOLNPGM("print_job_elapsed: ", info.print_job_elapsed);
|
||||
|
||||
DEBUG_ECHOPGM("axis_relative:");
|
||||
if (TEST(info.axis_relative, REL_X)) DEBUG_ECHOPGM(" REL_X");
|
||||
if (TEST(info.axis_relative, REL_Y)) DEBUG_ECHOPGM(" REL_Y");
|
||||
if (TEST(info.axis_relative, REL_Z)) DEBUG_ECHOPGM(" REL_Z");
|
||||
if (TEST(info.axis_relative, REL_E)) DEBUG_ECHOPGM(" REL_E");
|
||||
if (TEST(info.axis_relative, E_MODE_ABS)) DEBUG_ECHOPGM(" E_MODE_ABS");
|
||||
if (TEST(info.axis_relative, E_MODE_REL)) DEBUG_ECHOPGM(" E_MODE_REL");
|
||||
DEBUG_EOL();
|
||||
|
||||
DEBUG_ECHOLNPGM("flag.dryrun: ", AS_DIGIT(info.flag.dryrun));
|
||||
DEBUG_ECHOLNPGM("flag.allow_cold_extrusion: ", AS_DIGIT(info.flag.allow_cold_extrusion));
|
||||
DEBUG_ECHOLNPGM("flag.volumetric_enabled: ", AS_DIGIT(info.flag.volumetric_enabled));
|
||||
}
|
||||
else
|
||||
DEBUG_ECHOLNPGM("INVALID DATA");
|
||||
|
||||
130
Marlin/src/feature/powerloss.h
Executable file → Normal file
130
Marlin/src/feature/powerloss.h
Executable file → Normal file
@@ -16,18 +16,24 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* power_loss_recovery.h - Resume an SD print after power-loss
|
||||
* feature/powerloss.h - Resume an SD print after power-loss
|
||||
*/
|
||||
|
||||
#include "../sd/cardreader.h"
|
||||
#include "../gcode/gcode.h"
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(GCODE_REPEAT_MARKERS)
|
||||
#include "../feature/repeat.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(MIXING_EXTRUDER)
|
||||
#include "../feature/mixing.h"
|
||||
#endif
|
||||
@@ -36,6 +42,10 @@
|
||||
#define POWER_LOSS_STATE HIGH
|
||||
#endif
|
||||
|
||||
#ifndef POWER_LOSS_ZRAISE
|
||||
#define POWER_LOSS_ZRAISE 2
|
||||
#endif
|
||||
|
||||
//#define DEBUG_POWER_LOSS_RECOVERY
|
||||
//#define SAVE_EACH_CMD_MODE
|
||||
//#define SAVE_INFO_INTERVAL_MS 0
|
||||
@@ -45,6 +55,14 @@ typedef struct {
|
||||
|
||||
// Machine state
|
||||
xyze_pos_t current_position;
|
||||
uint16_t feedrate;
|
||||
|
||||
float zraise;
|
||||
|
||||
// Repeat information
|
||||
#if ENABLED(GCODE_REPEAT_MARKERS)
|
||||
Repeat stored_repeat;
|
||||
#endif
|
||||
|
||||
#if HAS_HOME_OFFSET
|
||||
xyz_pos_t home_offset;
|
||||
@@ -52,36 +70,25 @@ typedef struct {
|
||||
#if HAS_POSITION_SHIFT
|
||||
xyz_pos_t position_shift;
|
||||
#endif
|
||||
|
||||
uint16_t feedrate;
|
||||
|
||||
#if EXTRUDERS > 1
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
uint8_t active_extruder;
|
||||
#endif
|
||||
|
||||
#if DISABLED(NO_VOLUMETRICS)
|
||||
bool volumetric_enabled;
|
||||
#if EXTRUDERS > 1
|
||||
float filament_size[EXTRUDERS];
|
||||
#else
|
||||
float filament_size;
|
||||
#endif
|
||||
float filament_size[EXTRUDERS];
|
||||
#endif
|
||||
|
||||
#if HOTENDS
|
||||
int16_t target_temperature[HOTENDS];
|
||||
#if HAS_HOTEND
|
||||
celsius_t target_temperature[HOTENDS];
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
int16_t target_temperature_bed;
|
||||
celsius_t target_temperature_bed;
|
||||
#endif
|
||||
|
||||
#if FAN_COUNT
|
||||
#if HAS_FAN
|
||||
uint8_t fan_speed[FAN_COUNT];
|
||||
#endif
|
||||
|
||||
#if HAS_LEVELING
|
||||
bool leveling;
|
||||
float fade;
|
||||
#endif
|
||||
|
||||
@@ -98,9 +105,6 @@ typedef struct {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Relative axis modes
|
||||
uint8_t axis_relative;
|
||||
|
||||
// SD Filename and position
|
||||
char sd_filename[MAXPATHNAMELENGTH];
|
||||
volatile uint32_t sdpos;
|
||||
@@ -108,8 +112,26 @@ typedef struct {
|
||||
// Job elapsed time
|
||||
millis_t print_job_elapsed;
|
||||
|
||||
// Relative axis modes
|
||||
uint8_t axis_relative;
|
||||
|
||||
// Misc. Marlin flags
|
||||
struct {
|
||||
bool raised:1; // Raised before saved
|
||||
bool dryrun:1; // M111 S8
|
||||
bool allow_cold_extrusion:1; // M302 P1
|
||||
#if HAS_LEVELING
|
||||
bool leveling:1; // M420 S
|
||||
#endif
|
||||
#if DISABLED(NO_VOLUMETRICS)
|
||||
bool volumetric_enabled:1; // M200 S D
|
||||
#endif
|
||||
} flag;
|
||||
|
||||
uint8_t valid_foot;
|
||||
|
||||
bool valid() { return valid_head && valid_head == valid_foot; }
|
||||
|
||||
} job_recovery_info_t;
|
||||
|
||||
class PrintJobRecovery {
|
||||
@@ -123,17 +145,19 @@ class PrintJobRecovery {
|
||||
static uint32_t cmd_sdpos, //!< SD position of the next command
|
||||
sdpos[BUFSIZE]; //!< SD positions of queued commands
|
||||
|
||||
#if HAS_DWIN_E3V2_BASIC
|
||||
static bool dwin_flag;
|
||||
#endif
|
||||
|
||||
static void init();
|
||||
static void prepare();
|
||||
|
||||
static inline void setup() {
|
||||
#if PIN_EXISTS(POWER_LOSS)
|
||||
#if ENABLED(POWER_LOSS_PULL)
|
||||
#if POWER_LOSS_STATE == LOW
|
||||
SET_INPUT_PULLUP(POWER_LOSS_PIN);
|
||||
#else
|
||||
SET_INPUT_PULLDOWN(POWER_LOSS_PIN);
|
||||
#endif
|
||||
#if ENABLED(POWER_LOSS_PULLUP)
|
||||
SET_INPUT_PULLUP(POWER_LOSS_PIN);
|
||||
#elif ENABLED(POWER_LOSS_PULLDOWN)
|
||||
SET_INPUT_PULLDOWN(POWER_LOSS_PIN);
|
||||
#else
|
||||
SET_INPUT(POWER_LOSS_PIN);
|
||||
#endif
|
||||
@@ -156,36 +180,46 @@ class PrintJobRecovery {
|
||||
static void resume();
|
||||
static void purge();
|
||||
|
||||
static inline void cancel() { purge(); card.autostart_index = 0; }
|
||||
static inline void cancel() { purge(); IF_DISABLED(NO_SD_AUTOSTART, card.autofile_begin()); }
|
||||
|
||||
static void load();
|
||||
static void save(const bool force=ENABLED(SAVE_EACH_CMD_MODE));
|
||||
static void save(const bool force=ENABLED(SAVE_EACH_CMD_MODE), const float zraise=POWER_LOSS_ZRAISE, const bool raised=false);
|
||||
|
||||
#if PIN_EXISTS(POWER_LOSS)
|
||||
static inline void outage() {
|
||||
if (enabled && READ(POWER_LOSS_PIN) == POWER_LOSS_STATE)
|
||||
_outage();
|
||||
}
|
||||
#endif
|
||||
#if PIN_EXISTS(POWER_LOSS)
|
||||
static inline void outage() {
|
||||
static constexpr uint8_t OUTAGE_THRESHOLD = 3;
|
||||
static uint8_t outage_counter = 0;
|
||||
if (enabled && READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) {
|
||||
outage_counter++;
|
||||
if (outage_counter >= OUTAGE_THRESHOLD) _outage();
|
||||
}
|
||||
else
|
||||
outage_counter = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool valid() { return info.valid_head && info.valid_head == info.valid_foot; }
|
||||
// The referenced file exists
|
||||
static inline bool interrupted_file_exists() { return card.fileExists(info.sd_filename); }
|
||||
|
||||
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
|
||||
static void debug(PGM_P const prefix);
|
||||
#else
|
||||
static inline void debug(PGM_P const) {}
|
||||
#endif
|
||||
static inline bool valid() { return info.valid() && interrupted_file_exists(); }
|
||||
|
||||
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
|
||||
static void debug(PGM_P const prefix);
|
||||
#else
|
||||
static inline void debug(PGM_P const) {}
|
||||
#endif
|
||||
|
||||
private:
|
||||
static void write();
|
||||
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY)
|
||||
static void raise_z();
|
||||
#endif
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY)
|
||||
static void retract_and_lift(const_float_t zraise);
|
||||
#endif
|
||||
|
||||
#if PIN_EXISTS(POWER_LOSS)
|
||||
static void _outage();
|
||||
#endif
|
||||
#if PIN_EXISTS(POWER_LOSS)
|
||||
friend class GcodeSuite;
|
||||
static void _outage();
|
||||
#endif
|
||||
};
|
||||
|
||||
extern PrintJobRecovery recovery;
|
||||
|
||||
104
Marlin/src/feature/probe_temp_comp.cpp
Executable file → Normal file
104
Marlin/src/feature/probe_temp_comp.cpp
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -29,27 +29,27 @@
|
||||
|
||||
ProbeTempComp temp_comp;
|
||||
|
||||
int16_t ProbeTempComp::z_offsets_probe[ProbeTempComp::cali_info_init[TSI_PROBE].measurements], // = {0}
|
||||
ProbeTempComp::z_offsets_bed[ProbeTempComp::cali_info_init[TSI_BED].measurements]; // = {0}
|
||||
int16_t ProbeTempComp::z_offsets_probe[cali_info_init[TSI_PROBE].measurements], // = {0}
|
||||
ProbeTempComp::z_offsets_bed[cali_info_init[TSI_BED].measurements]; // = {0}
|
||||
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
int16_t ProbeTempComp::z_offsets_ext[ProbeTempComp::cali_info_init[TSI_EXT].measurements]; // = {0}
|
||||
int16_t ProbeTempComp::z_offsets_ext[cali_info_init[TSI_EXT].measurements]; // = {0}
|
||||
#endif
|
||||
|
||||
int16_t *ProbeTempComp::sensor_z_offsets[TSI_COUNT] = {
|
||||
ProbeTempComp::z_offsets_probe, ProbeTempComp::z_offsets_bed
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
, ProbeTempComp::z_offsets_ext
|
||||
#endif
|
||||
OPTARG(USE_TEMP_EXT_COMPENSATION, ProbeTempComp::z_offsets_ext)
|
||||
};
|
||||
|
||||
const temp_calib_t ProbeTempComp::cali_info[TSI_COUNT] = {
|
||||
ProbeTempComp::cali_info_init[TSI_PROBE], ProbeTempComp::cali_info_init[TSI_BED]
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
, ProbeTempComp::cali_info_init[TSI_EXT]
|
||||
#endif
|
||||
cali_info_init[TSI_PROBE], cali_info_init[TSI_BED]
|
||||
OPTARG(USE_TEMP_EXT_COMPENSATION, cali_info_init[TSI_EXT])
|
||||
};
|
||||
|
||||
constexpr xyz_pos_t ProbeTempComp::park_point;
|
||||
constexpr xy_pos_t ProbeTempComp::measure_point;
|
||||
constexpr celsius_t ProbeTempComp::probe_calib_bed_temp;
|
||||
|
||||
uint8_t ProbeTempComp::calib_idx; // = 0
|
||||
float ProbeTempComp::init_measurement; // = 0.0
|
||||
|
||||
@@ -67,15 +67,15 @@ bool ProbeTempComp::set_offset(const TempSensorID tsi, const uint8_t idx, const
|
||||
|
||||
void ProbeTempComp::print_offsets() {
|
||||
LOOP_L_N(s, TSI_COUNT) {
|
||||
float temp = cali_info[s].start_temp;
|
||||
celsius_t temp = cali_info[s].start_temp;
|
||||
for (int16_t i = -1; i < cali_info[s].measurements; ++i) {
|
||||
serialprintPGM(s == TSI_BED ? PSTR("Bed") :
|
||||
SERIAL_ECHOPGM_P(s == TSI_BED ? PSTR("Bed") :
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
s == TSI_EXT ? PSTR("Extruder") :
|
||||
#endif
|
||||
PSTR("Probe")
|
||||
);
|
||||
SERIAL_ECHOLNPAIR(
|
||||
SERIAL_ECHOLNPGM(
|
||||
" temp: ", temp,
|
||||
"C; Offset: ", i < 0 ? 0.0f : sensor_z_offsets[s][i], " um"
|
||||
);
|
||||
@@ -84,12 +84,12 @@ void ProbeTempComp::print_offsets() {
|
||||
}
|
||||
}
|
||||
|
||||
void ProbeTempComp::prepare_new_calibration(const float &init_meas_z) {
|
||||
void ProbeTempComp::prepare_new_calibration(const_float_t init_meas_z) {
|
||||
calib_idx = 0;
|
||||
init_measurement = init_meas_z;
|
||||
}
|
||||
|
||||
void ProbeTempComp::push_back_new_measurement(const TempSensorID tsi, const float &meas_z) {
|
||||
void ProbeTempComp::push_back_new_measurement(const TempSensorID tsi, const_float_t meas_z) {
|
||||
switch (tsi) {
|
||||
case TSI_PROBE:
|
||||
case TSI_BED:
|
||||
@@ -110,19 +110,19 @@ bool ProbeTempComp::finish_calibration(const TempSensorID tsi) {
|
||||
}
|
||||
|
||||
const uint8_t measurements = cali_info[tsi].measurements;
|
||||
const float start_temp = cali_info[tsi].start_temp,
|
||||
res_temp = cali_info[tsi].temp_res;
|
||||
const celsius_t start_temp = cali_info[tsi].start_temp,
|
||||
res_temp = cali_info[tsi].temp_res;
|
||||
int16_t * const data = sensor_z_offsets[tsi];
|
||||
|
||||
// Extrapolate
|
||||
float k, d;
|
||||
if (calib_idx < measurements) {
|
||||
SERIAL_ECHOLNPAIR("Got ", calib_idx, " measurements. ");
|
||||
SERIAL_ECHOLNPGM("Got ", calib_idx, " measurements. ");
|
||||
if (linear_regression(tsi, k, d)) {
|
||||
SERIAL_ECHOPGM("Applying linear extrapolation");
|
||||
calib_idx--;
|
||||
for (; calib_idx < measurements; ++calib_idx) {
|
||||
const float temp = start_temp + float(calib_idx) * res_temp;
|
||||
const celsius_float_t temp = start_temp + float(calib_idx) * res_temp;
|
||||
data[calib_idx] = static_cast<int16_t>(k * temp + d);
|
||||
}
|
||||
}
|
||||
@@ -139,13 +139,13 @@ bool ProbeTempComp::finish_calibration(const TempSensorID tsi) {
|
||||
// Sanity check
|
||||
for (calib_idx = 0; calib_idx < measurements; ++calib_idx) {
|
||||
// Restrict the max. offset
|
||||
if (abs(data[calib_idx]) > 2000) {
|
||||
if (ABS(data[calib_idx]) > 2000) {
|
||||
SERIAL_ECHOLNPGM("!Invalid Z-offset detected (0-2).");
|
||||
clear_offsets(tsi);
|
||||
return false;
|
||||
}
|
||||
// Restrict the max. offset difference between two probings
|
||||
if (calib_idx > 0 && abs(data[calib_idx - 1] - data[calib_idx]) > 800) {
|
||||
if (calib_idx > 0 && ABS(data[calib_idx - 1] - data[calib_idx]) > 800) {
|
||||
SERIAL_ECHOLNPGM("!Invalid Z-offset between two probings detected (0-0.8).");
|
||||
clear_offsets(TSI_PROBE);
|
||||
return false;
|
||||
@@ -155,34 +155,47 @@ bool ProbeTempComp::finish_calibration(const TempSensorID tsi) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProbeTempComp::compensate_measurement(const TempSensorID tsi, const float &temp, float &meas_z) {
|
||||
void ProbeTempComp::compensate_measurement(const TempSensorID tsi, const celsius_t temp, float &meas_z) {
|
||||
if (WITHIN(temp, cali_info[tsi].start_temp, cali_info[tsi].end_temp))
|
||||
meas_z -= get_offset_for_temperature(tsi, temp);
|
||||
}
|
||||
|
||||
float ProbeTempComp::get_offset_for_temperature(const TempSensorID tsi, const float &temp) {
|
||||
|
||||
float ProbeTempComp::get_offset_for_temperature(const TempSensorID tsi, const celsius_t temp) {
|
||||
const uint8_t measurements = cali_info[tsi].measurements;
|
||||
const float start_temp = cali_info[tsi].start_temp,
|
||||
end_temp = cali_info[tsi].end_temp,
|
||||
res_temp = cali_info[tsi].temp_res;
|
||||
const celsius_t start_temp = cali_info[tsi].start_temp,
|
||||
res_temp = cali_info[tsi].temp_res;
|
||||
const int16_t * const data = sensor_z_offsets[tsi];
|
||||
|
||||
if (temp <= start_temp) return 0.0f;
|
||||
if (temp >= end_temp) return static_cast<float>(data[measurements - 1]) / 1000.0f;
|
||||
auto point = [&](uint8_t i) -> xy_float_t {
|
||||
return xy_float_t({ static_cast<float>(start_temp) + i * res_temp, static_cast<float>(data[i]) });
|
||||
};
|
||||
|
||||
auto linear_interp = [](const_float_t x, xy_float_t p1, xy_float_t p2) {
|
||||
return (p2.y - p1.y) / (p2.x - p2.y) * (x - p1.x) + p1.y;
|
||||
};
|
||||
|
||||
// Linear interpolation
|
||||
int16_t val1 = 0, val2 = data[0];
|
||||
uint8_t idx = 0;
|
||||
float meas_temp = start_temp + res_temp;
|
||||
while (meas_temp < temp) {
|
||||
if (++idx >= measurements) return static_cast<float>(val2) / 1000.0f;
|
||||
meas_temp += res_temp;
|
||||
val1 = val2;
|
||||
val2 = data[idx];
|
||||
}
|
||||
const float factor = (meas_temp - temp) / static_cast<float>(res_temp);
|
||||
return (static_cast<float>(val2) - static_cast<float>(val2 - val1) * factor) / 1000.0f;
|
||||
uint8_t idx = static_cast<uint8_t>((temp - start_temp) / res_temp);
|
||||
|
||||
// offset in µm
|
||||
float offset = 0.0f;
|
||||
|
||||
#if !defined(PTC_LINEAR_EXTRAPOLATION) || PTC_LINEAR_EXTRAPOLATION <= 0
|
||||
if (idx < 0)
|
||||
offset = 0.0f;
|
||||
else if (idx > measurements - 2)
|
||||
offset = static_cast<float>(data[measurements - 1]);
|
||||
#else
|
||||
if (idx < 0)
|
||||
offset = linear_interp(temp, point(0), point(PTC_LINEAR_EXTRAPOLATION));
|
||||
else if (idx > measurements - 2)
|
||||
offset = linear_interp(temp, point(measurements - PTC_LINEAR_EXTRAPOLATION - 1), point(measurements - 1));
|
||||
#endif
|
||||
else
|
||||
offset = linear_interp(temp, point(idx), point(idx + 1));
|
||||
|
||||
// return offset in mm
|
||||
return offset / 1000.0f;
|
||||
}
|
||||
|
||||
bool ProbeTempComp::linear_regression(const TempSensorID tsi, float &k, float &d) {
|
||||
@@ -190,17 +203,18 @@ bool ProbeTempComp::linear_regression(const TempSensorID tsi, float &k, float &d
|
||||
|
||||
if (!WITHIN(calib_idx, 2, cali_info[tsi].measurements)) return false;
|
||||
|
||||
const float start_temp = cali_info[tsi].start_temp,
|
||||
res_temp = cali_info[tsi].temp_res;
|
||||
const celsius_t start_temp = cali_info[tsi].start_temp,
|
||||
res_temp = cali_info[tsi].temp_res;
|
||||
const int16_t * const data = sensor_z_offsets[tsi];
|
||||
|
||||
float sum_x = start_temp,
|
||||
sum_x2 = sq(start_temp),
|
||||
sum_xy = 0, sum_y = 0;
|
||||
|
||||
float xi = static_cast<float>(start_temp);
|
||||
LOOP_L_N(i, calib_idx) {
|
||||
const float xi = start_temp + (i + 1) * res_temp,
|
||||
yi = static_cast<float>(data[i]);
|
||||
const float yi = static_cast<float>(data[i]);
|
||||
xi += res_temp;
|
||||
sum_x += xi;
|
||||
sum_x2 += sq(xi);
|
||||
sum_xy += xi * yi;
|
||||
|
||||
89
Marlin/src/feature/probe_temp_comp.h
Executable file → Normal file
89
Marlin/src/feature/probe_temp_comp.h
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
@@ -34,9 +34,9 @@ enum TempSensorID : uint8_t {
|
||||
|
||||
typedef struct {
|
||||
uint8_t measurements; // Max. number of measurements to be stored (35 - 80°C)
|
||||
float temp_res, // Resolution in °C between measurements
|
||||
start_temp, // Base measurement; z-offset == 0
|
||||
end_temp;
|
||||
celsius_t temp_res, // Resolution in °C between measurements
|
||||
start_temp, // Base measurement; z-offset == 0
|
||||
end_temp;
|
||||
} temp_calib_t;
|
||||
|
||||
/**
|
||||
@@ -44,31 +44,64 @@ typedef struct {
|
||||
* Z-probes like the P.I.N.D.A V2 allow for compensation of
|
||||
* measurement errors/shifts due to changed temperature.
|
||||
*/
|
||||
|
||||
// Probe temperature calibration constants
|
||||
#ifndef PTC_SAMPLE_COUNT
|
||||
#define PTC_SAMPLE_COUNT 10
|
||||
#endif
|
||||
#ifndef PTC_SAMPLE_RES
|
||||
#define PTC_SAMPLE_RES 5
|
||||
#endif
|
||||
#ifndef PTC_SAMPLE_START
|
||||
#define PTC_SAMPLE_START 30
|
||||
#endif
|
||||
#define PTC_SAMPLE_END (PTC_SAMPLE_START + (PTC_SAMPLE_COUNT) * PTC_SAMPLE_RES)
|
||||
|
||||
// Bed temperature calibration constants
|
||||
#ifndef BTC_PROBE_TEMP
|
||||
#define BTC_PROBE_TEMP 30
|
||||
#endif
|
||||
#ifndef BTC_SAMPLE_COUNT
|
||||
#define BTC_SAMPLE_COUNT 10
|
||||
#endif
|
||||
#ifndef BTC_SAMPLE_RES
|
||||
#define BTC_SAMPLE_RES 5
|
||||
#endif
|
||||
#ifndef BTC_SAMPLE_START
|
||||
#define BTC_SAMPLE_START 60
|
||||
#endif
|
||||
#define BTC_SAMPLE_END (BTC_SAMPLE_START + (BTC_SAMPLE_COUNT) * BTC_SAMPLE_RES)
|
||||
|
||||
#ifndef PTC_PROBE_HEATING_OFFSET
|
||||
#define PTC_PROBE_HEATING_OFFSET 0.5f
|
||||
#endif
|
||||
|
||||
#ifndef PTC_PROBE_RAISE
|
||||
#define PTC_PROBE_RAISE 10
|
||||
#endif
|
||||
|
||||
static constexpr temp_calib_t cali_info_init[TSI_COUNT] = {
|
||||
{ PTC_SAMPLE_COUNT, PTC_SAMPLE_RES, PTC_SAMPLE_START, PTC_SAMPLE_END }, // Probe
|
||||
{ BTC_SAMPLE_COUNT, BTC_SAMPLE_RES, BTC_SAMPLE_START, BTC_SAMPLE_END }, // Bed
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
{ 20, 5, 180, 180 + 5 * 20 } // Extruder
|
||||
#endif
|
||||
};
|
||||
|
||||
class ProbeTempComp {
|
||||
public:
|
||||
|
||||
static constexpr temp_calib_t cali_info_init[TSI_COUNT] = {
|
||||
{ 10, 5, 30, 30 + 10 * 5 }, // Probe
|
||||
{ 10, 5, 60, 60 + 10 * 5 }, // Bed
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
{ 20, 5, 180, 180 + 5 * 20 } // Extruder
|
||||
#endif
|
||||
};
|
||||
static const temp_calib_t cali_info[TSI_COUNT];
|
||||
|
||||
// Where to park nozzle to wait for probe cooldown
|
||||
static constexpr float park_point_x = PTC_PARK_POS_X,
|
||||
park_point_y = PTC_PARK_POS_Y,
|
||||
park_point_z = PTC_PARK_POS_Z,
|
||||
// XY coordinates of nozzle for probing the bed
|
||||
measure_point_x = PTC_PROBE_POS_X, // Coordinates to probe
|
||||
measure_point_y = PTC_PROBE_POS_Y;
|
||||
//measure_point_x = 12.0f, // Coordinates to probe on MK52 magnetic heatbed
|
||||
//measure_point_y = 7.3f;
|
||||
static constexpr xyz_pos_t park_point = PTC_PARK_POS;
|
||||
|
||||
static constexpr int max_bed_temp = PTC_MAX_BED_TEMP, // Max temperature to avoid heating errors
|
||||
probe_calib_bed_temp = max_bed_temp, // Bed temperature while calibrating probe
|
||||
bed_calib_probe_temp = 30; // Probe temperature while calibrating bed
|
||||
// XY coordinates of nozzle for probing the bed
|
||||
static constexpr xy_pos_t measure_point = PTC_PROBE_POS; // Coordinates to probe
|
||||
//measure_point = { 12.0f, 7.3f }; // Coordinates for the MK52 magnetic heatbed
|
||||
|
||||
static constexpr celsius_t probe_calib_bed_temp = BED_MAX_TARGET, // Bed temperature while calibrating probe
|
||||
bed_calib_probe_temp = BTC_PROBE_TEMP; // Probe temperature while calibrating bed
|
||||
|
||||
static int16_t *sensor_z_offsets[TSI_COUNT],
|
||||
z_offsets_probe[cali_info_init[TSI_PROBE].measurements], // (µm)
|
||||
@@ -84,16 +117,14 @@ class ProbeTempComp {
|
||||
static inline void clear_all_offsets() {
|
||||
clear_offsets(TSI_BED);
|
||||
clear_offsets(TSI_PROBE);
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
clear_offsets(TSI_EXT);
|
||||
#endif
|
||||
TERN_(USE_TEMP_EXT_COMPENSATION, clear_offsets(TSI_EXT));
|
||||
}
|
||||
static bool set_offset(const TempSensorID tsi, const uint8_t idx, const int16_t offset);
|
||||
static void print_offsets();
|
||||
static void prepare_new_calibration(const float &init_meas_z);
|
||||
static void push_back_new_measurement(const TempSensorID tsi, const float &meas_z);
|
||||
static void prepare_new_calibration(const_float_t init_meas_z);
|
||||
static void push_back_new_measurement(const TempSensorID tsi, const_float_t meas_z);
|
||||
static bool finish_calibration(const TempSensorID tsi);
|
||||
static void compensate_measurement(const TempSensorID tsi, const float &temp, float &meas_z);
|
||||
static void compensate_measurement(const TempSensorID tsi, const celsius_t temp, float &meas_z);
|
||||
|
||||
private:
|
||||
static uint8_t calib_idx;
|
||||
@@ -104,7 +135,7 @@ class ProbeTempComp {
|
||||
*/
|
||||
static float init_measurement;
|
||||
|
||||
static float get_offset_for_temperature(const TempSensorID tsi, const float &temp);
|
||||
static float get_offset_for_temperature(const TempSensorID tsi, const celsius_t temp);
|
||||
|
||||
/**
|
||||
* Fit a linear function in measured temperature offsets
|
||||
|
||||
82
Marlin/src/feature/repeat.cpp
Normal file
82
Marlin/src/feature/repeat.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(GCODE_REPEAT_MARKERS)
|
||||
|
||||
//#define DEBUG_GCODE_REPEAT_MARKERS
|
||||
|
||||
#include "repeat.h"
|
||||
|
||||
#include "../gcode/gcode.h"
|
||||
#include "../sd/cardreader.h"
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_GCODE_REPEAT_MARKERS)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
repeat_marker_t Repeat::marker[MAX_REPEAT_NESTING];
|
||||
uint8_t Repeat::index;
|
||||
|
||||
void Repeat::add_marker(const uint32_t sdpos, const uint16_t count) {
|
||||
if (index >= MAX_REPEAT_NESTING)
|
||||
SERIAL_ECHO_MSG("!Too many markers.");
|
||||
else {
|
||||
marker[index].sdpos = sdpos;
|
||||
marker[index].counter = count ?: -1;
|
||||
index++;
|
||||
DEBUG_ECHOLNPGM("Add Marker ", index, " at ", sdpos, " (", count, ")");
|
||||
}
|
||||
}
|
||||
|
||||
void Repeat::loop() {
|
||||
if (!index) // No marker?
|
||||
SERIAL_ECHO_MSG("!No marker set."); // Inform the user.
|
||||
else {
|
||||
const uint8_t ind = index - 1; // Active marker's index
|
||||
if (!marker[ind].counter) { // Did its counter run out?
|
||||
DEBUG_ECHOLNPGM("Pass Marker ", index);
|
||||
index--; // Carry on. Previous marker on the next 'M808'.
|
||||
}
|
||||
else {
|
||||
card.setIndex(marker[ind].sdpos); // Loop back to the marker.
|
||||
if (marker[ind].counter > 0) // Ignore a negative (or zero) counter.
|
||||
--marker[ind].counter; // Decrement the counter. If zero this 'M808' will be skipped next time.
|
||||
DEBUG_ECHOLNPGM("Goto Marker ", index, " at ", marker[ind].sdpos, " (", marker[ind].counter, ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Repeat::cancel() { LOOP_L_N(i, index) marker[i].counter = 0; }
|
||||
|
||||
void Repeat::early_parse_M808(char * const cmd) {
|
||||
if (is_command_M808(cmd)) {
|
||||
DEBUG_ECHOLNPGM("Parsing \"", cmd, "\"");
|
||||
parser.parse(cmd);
|
||||
if (parser.seen('L'))
|
||||
add_marker(card.getIndex(), parser.value_ushort());
|
||||
else
|
||||
Repeat::loop();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GCODE_REPEAT_MARKERS
|
||||
53
Marlin/src/feature/repeat.h
Normal file
53
Marlin/src/feature/repeat.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
#include "../gcode/parser.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_REPEAT_NESTING 10
|
||||
|
||||
typedef struct {
|
||||
uint32_t sdpos; // The repeat file position
|
||||
int16_t counter; // The counter for looping
|
||||
} repeat_marker_t;
|
||||
|
||||
class Repeat {
|
||||
private:
|
||||
static repeat_marker_t marker[MAX_REPEAT_NESTING];
|
||||
static uint8_t index;
|
||||
public:
|
||||
static inline void reset() { index = 0; }
|
||||
static inline bool is_active() {
|
||||
LOOP_L_N(i, index) if (marker[i].counter) return true;
|
||||
return false;
|
||||
}
|
||||
static bool is_command_M808(char * const cmd) { return cmd[0] == 'M' && cmd[1] == '8' && cmd[2] == '0' && cmd[3] == '8' && !NUMERIC(cmd[4]); }
|
||||
static void early_parse_M808(char * const cmd);
|
||||
static void add_marker(const uint32_t sdpos, const uint16_t count);
|
||||
static void loop();
|
||||
static void cancel();
|
||||
};
|
||||
|
||||
extern Repeat repeat;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user