Merge remote-tracking branch 'upstream/MK3' into vintagepc/more-cmake-fixes

This commit is contained in:
VintagePC 2023-09-22 16:42:06 -04:00
commit 949539dfc9
59 changed files with 1141 additions and 1018 deletions

154
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,154 @@
name: ci-build
on:
pull_request:
branches:
- '*'
push:
branches: [ MK3, MK3_* ]
env:
GH_ANNOTATIONS: 1
jobs:
build:
runs-on: ubuntu-latest
steps:
# setup base required dependencies
- name: Setup dependencies
run: |
sudo apt-get install cmake ninja-build python3-pyelftools python3-regex python3-polib
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Checkout ${{ github.event.pull_request.head.ref }}
uses: actions/checkout@v3
if: ${{ github.event.pull_request }}
with:
ref: ${{ github.event.pull_request.head.sha }}
submodules: true
- name: Checkout ${{ github.event.ref }}
uses: actions/checkout@v3
if: ${{ !github.event.pull_request }}
with:
ref: ${{ github.event.ref }}
submodules: true
- name: Cache Dependencies
uses: actions/cache@v3.0.11
id: cache-pkgs
with:
path: ".dependencies"
key: "build-deps-1_0_0-linux"
- name: Setup build dependencies
run: |
./utils/bootstrap.py
- name: Cache permissions
run: sudo chmod -R 744 .dependencies
- name: Build
run: |
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE="../cmake/AvrGcc.cmake" -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja
- name: Upload artifacts
if: ${{ !github.event.pull_request }}
uses: actions/upload-artifact@v3.1.1
with:
name: Firmware
path: build/*.hex
check-lang:
runs-on: ubuntu-latest
steps:
# setup base required dependencies
- name: Setup dependencies
run: |
sudo apt-get install gcc-11 g++11 lcov cmake ninja-build python3-pyelftools python3-regex python3-polib
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Checkout ${{ github.event.pull_request.head.ref }}
uses: actions/checkout@v3
if: ${{ github.event.pull_request }}
with:
ref: ${{ github.event.pull_request.head.sha }}
submodules: true
- name: Checkout ${{ github.event.ref }}
uses: actions/checkout@v3
if: ${{ !github.event.pull_request }}
with:
ref: ${{ github.event.ref }}
submodules: true
- name: Cache Dependencies
uses: actions/cache@v3.0.11
id: cache-pkgs
with:
path: ".dependencies"
key: "build-deps-1_0_0-linux"
- name: Setup build dependencies
run: |
./utils/bootstrap.py
- name: Cache permissions
run: sudo chmod -R 744 .dependencies
- name: Run check
run: |
mkdir build
cd build
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="../cmake/AvrGcc.cmake" -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja check_lang
tests:
runs-on: ubuntu-latest
steps:
# setup base required dependencies
- name: Setup dependencies
run: |
sudo apt-get install gcc-11 g++11 lcov cmake ninja-build python3-pyelftools python3-regex python3-polib
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Checkout ${{ github.event.pull_request.head.ref }}
uses: actions/checkout@v3
if: ${{ github.event.pull_request }}
with:
ref: ${{ github.event.pull_request.head.sha }}
submodules: true
- name: Checkout ${{ github.event.ref }}
uses: actions/checkout@v3
if: ${{ !github.event.pull_request }}
with:
ref: ${{ github.event.ref }}
submodules: true
- name: Cache Dependencies
uses: actions/cache@v3.0.11
id: cache-pkgs
with:
path: ".dependencies"
key: "build-deps-1_0_0-linux"
- name: Setup build dependencies
run: |
./utils/bootstrap.py
- name: Cache permissions
run: sudo chmod -R 744 .dependencies
- name: Run check
run: |
mkdir build
cd build
cmake .. -G Ninja
ninja test_run_all

View File

@ -24,10 +24,20 @@ jobs:
- name: Checkout base - name: Checkout base
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Cache Dependencies
uses: actions/cache@v3.0.11
id: cache-pkgs
with:
path: ".dependencies"
key: "build-deps-1_0_0-linux"
- name: Setup build dependencies - name: Setup build dependencies
run: | run: |
./utils/bootstrap.py ./utils/bootstrap.py
- name: Cache permissions
run: sudo chmod -R 744 .dependencies
- name: Build base - name: Build base
run: | run: |
rm -rf build-base rm -rf build-base

View File

@ -22,7 +22,6 @@
#include "pins.h" #include "pins.h"
#include "Timer.h" #include "Timer.h"
#include "mmu2.h" #include "mmu2.h"
extern uint8_t mbl_z_probe_nr;
#ifndef AT90USB #ifndef AT90USB
#define HardwareSerial_h // trick to disable the standard HWserial #define HardwareSerial_h // trick to disable the standard HWserial
@ -236,7 +235,7 @@ enum class HeatingStatus : uint8_t
extern HeatingStatus heating_status; extern HeatingStatus heating_status;
extern bool fans_check_enabled; extern bool fans_check_enabled;
extern float homing_feedrate[]; constexpr float homing_feedrate[] = HOMING_FEEDRATE;
extern uint8_t axis_relative_modes; extern uint8_t axis_relative_modes;
extern float feedrate; extern float feedrate;
extern int feedmultiply; extern int feedmultiply;

View File

@ -148,8 +148,6 @@
CardReader card; CardReader card;
#endif #endif
uint8_t mbl_z_probe_nr = 3; //numer of Z measurements for each point in mesh bed leveling calibration
//used for PINDA temp calibration and pause print //used for PINDA temp calibration and pause print
#define DEFAULT_RETRACTION 1 #define DEFAULT_RETRACTION 1
#define DEFAULT_RETRACTION_MM 4 //MM #define DEFAULT_RETRACTION_MM 4 //MM
@ -157,8 +155,6 @@ uint8_t mbl_z_probe_nr = 3; //numer of Z measurements for each point in mesh bed
float default_retraction = DEFAULT_RETRACTION; float default_retraction = DEFAULT_RETRACTION;
float homing_feedrate[] = HOMING_FEEDRATE;
//Although this flag and many others like this could be represented with a struct/bitfield for each axis (more readable and efficient code), the implementation //Although this flag and many others like this could be represented with a struct/bitfield for each axis (more readable and efficient code), the implementation
//would not be standard across all platforms. That being said, the code will continue to use bitmasks for independent axis. //would not be standard across all platforms. That being said, the code will continue to use bitmasks for independent axis.
//Moreover, according to C/C++ standard, the ordering of bits is platform/compiler dependent and the compiler is allowed to align the bits arbitrarily, //Moreover, according to C/C++ standard, the ordering of bits is platform/compiler dependent and the compiler is allowed to align the bits arbitrarily,
@ -271,7 +267,7 @@ const char axis_codes[NUM_AXIS] = {'X', 'Y', 'Z', 'E'};
float destination[NUM_AXIS] = { 0.0, 0.0, 0.0, 0.0}; float destination[NUM_AXIS] = { 0.0, 0.0, 0.0, 0.0};
// For tracing an arc // For tracing an arc
static float offset[3] = {0.0, 0.0, 0.0}; static float offset[2] = {0.0, 0.0};
// Current feedrate // Current feedrate
float feedrate = 1500.0; float feedrate = 1500.0;
@ -2791,23 +2787,16 @@ static void gcode_G28(bool home_x_axis, bool home_y_axis, bool home_z_axis)
// G80 - Automatic mesh bed leveling // G80 - Automatic mesh bed leveling
static void gcode_G80() static void gcode_G80()
{ {
constexpr float XY_AXIS_FEEDRATE = (homing_feedrate[X_AXIS] * 3) / 60;
constexpr float Z_LIFT_FEEDRATE = homing_feedrate[Z_AXIS] / 60;
constexpr float Z_CALIBRATION_THRESHOLD = 0.35f;
constexpr float MESH_HOME_Z_SEARCH_FAST = 0.35f;
st_synchronize(); st_synchronize();
if (planner_aborted) if (planner_aborted)
return; return;
mesh_bed_leveling_flag = true; mesh_bed_leveling_flag = true;
#ifndef PINDA_THERMISTOR
static bool run = false; // thermistor-less PINDA temperature compensation is running
#endif // ndef PINDA_THERMISTOR
#ifdef SUPPORT_VERBOSITY
int8_t verbosity_level = 0;
if (code_seen('V')) {
// Just 'V' without a number counts as V1.
char c = strchr_pointer[1];
verbosity_level = (c == ' ' || c == '\t' || c == 0) ? 1 : code_value_short();
}
#endif //SUPPORT_VERBOSITY
// Firstly check if we know where we are // Firstly check if we know where we are
if (!(axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS])) { if (!(axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS])) {
// We don't know where we are! HOME! // We don't know where we are! HOME!
@ -2818,30 +2807,8 @@ static void gcode_G80()
return; return;
} }
uint8_t nMeasPoints = MESH_MEAS_NUM_X_POINTS;
if (code_seen('N')) {
nMeasPoints = code_value_uint8();
if (nMeasPoints != 7) {
nMeasPoints = 3;
}
}
else {
nMeasPoints = eeprom_read_byte((uint8_t*)EEPROM_MBL_POINTS_NR);
}
uint8_t nProbeRetry = 3;
if (code_seen('R')) {
nProbeRetry = code_value_uint8();
if (nProbeRetry > 10) {
nProbeRetry = 10;
}
}
else {
nProbeRetry = eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR);
}
bool magnet_elimination = (eeprom_read_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION) > 0);
#ifndef PINDA_THERMISTOR #ifndef PINDA_THERMISTOR
static bool run = false; // thermistor-less PINDA temperature compensation is running
if (run == false && eeprom_read_byte((uint8_t *)EEPROM_TEMP_CAL_ACTIVE) && calibration_status_pinda() == true && target_temperature_bed >= 50) if (run == false && eeprom_read_byte((uint8_t *)EEPROM_TEMP_CAL_ACTIVE) && calibration_status_pinda() == true && target_temperature_bed >= 50)
{ {
temp_compensation_start(); temp_compensation_start();
@ -2852,113 +2819,108 @@ static void gcode_G80()
} }
run = false; run = false;
#endif //PINDA_THERMISTOR #endif //PINDA_THERMISTOR
// Save custom message state, set a new custom message state to display: Calibrating point 9.
CustomMsg custom_message_type_old = custom_message_type; uint8_t nMeasPoints = eeprom_read_byte((uint8_t*)EEPROM_MBL_POINTS_NR);
uint8_t custom_message_state_old = custom_message_state; if (uint8_t codeSeen = code_seen('N'), value = code_value_uint8(); codeSeen && (value == 7 || value == 3))
custom_message_type = CustomMsg::MeshBedLeveling; nMeasPoints = value;
custom_message_state = (nMeasPoints * nMeasPoints) + 10;
lcd_update(1); uint8_t nProbeRetryCount = eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR);
if (uint8_t codeSeen = code_seen('C'), value = code_value_uint8(); codeSeen && value >= 1 && value <= 10)
nProbeRetryCount = value;
const float area_min_x = code_seen('X') ? code_value() - x_mesh_density - X_PROBE_OFFSET_FROM_EXTRUDER : -INFINITY;
const float area_min_y = code_seen('Y') ? code_value() - y_mesh_density - Y_PROBE_OFFSET_FROM_EXTRUDER : -INFINITY;
const float area_max_x = code_seen('W') ? area_min_x + code_value() + 2 * x_mesh_density : INFINITY;
const float area_max_y = code_seen('H') ? area_min_y + code_value() + 2 * y_mesh_density : INFINITY;
mbl.reset(); //reset mesh bed leveling mbl.reset(); //reset mesh bed leveling
mbl.z_values[0][0] = min_pos[Z_AXIS];
// Reset baby stepping to zero, if the babystepping has already been loaded before. // Reset baby stepping to zero, if the babystepping has already been loaded before.
babystep_undo(); babystep_undo();
// Cycle through all points and probe them // Initialize the default mesh from eeprom and calculate how many points are to be probed
// First move up. During this first movement, the babystepping will be reverted.
current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 60);
// The move to the first calibration point.
current_position[X_AXIS] = BED_X0;
current_position[Y_AXIS] = BED_Y0;
#ifdef SUPPORT_VERBOSITY
if (verbosity_level >= 1)
{
bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
clamped ? SERIAL_PROTOCOLPGM("First calibration point clamped.\n") : SERIAL_PROTOCOLPGM("No clamping for first calibration point.\n");
}
#else //SUPPORT_VERBOSITY
world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
#endif //SUPPORT_VERBOSITY
int XY_AXIS_FEEDRATE = homing_feedrate[X_AXIS] / 20;
plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE);
// Wait until the move is finished.
st_synchronize();
if (planner_aborted)
{
custom_message_type = custom_message_type_old;
custom_message_state = custom_message_state_old;
return;
}
uint8_t mesh_point = 0; //index number of calibration point
int Z_LIFT_FEEDRATE = homing_feedrate[Z_AXIS] / 40;
bool has_z = is_bed_z_jitter_data_valid(); //checks if we have data from Z calibration (offsets of the Z heiths of the 8 calibration points from the first point) bool has_z = is_bed_z_jitter_data_valid(); //checks if we have data from Z calibration (offsets of the Z heiths of the 8 calibration points from the first point)
#ifdef SUPPORT_VERBOSITY uint8_t meshPointsToProbe = 0;
if (verbosity_level >= 1) { for (uint8_t row = 0; row < MESH_NUM_Y_POINTS; row++) {
has_z ? SERIAL_PROTOCOLPGM("Z jitter data from Z cal. valid.\n") : SERIAL_PROTOCOLPGM("Z jitter data from Z cal. not valid.\n"); for (uint8_t col = 0; col < MESH_NUM_X_POINTS; col++) {
} bool isOn3x3Mesh = ((row % 3 == 0) && (col % 3 == 0));
#endif // SUPPORT_VERBOSITY if (isOn3x3Mesh) {
int l_feedmultiply = setup_for_endstop_move(false); //save feedrate and feedmultiply, sets feedmultiply to 100 if (has_z && (row || col)) {
while (mesh_point != nMeasPoints * nMeasPoints) { // Reconstruct the mesh saved in eeprom
// Get coords of a measuring point. uint16_t z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * ((col/3) + row - 1)));
uint8_t ix = mesh_point % nMeasPoints; // from 0 to MESH_NUM_X_POINTS - 1 const float z0 = mbl.z_values[0][0] + *reinterpret_cast<int16_t*>(&z_offset_u) * 0.01;
uint8_t iy = mesh_point / nMeasPoints; mbl.set_z(col, row, z0);
/*if (!mbl_point_measurement_valid(ix, iy, nMeasPoints, true)) { }
printf_P(PSTR("Skipping point [%d;%d] \n"), ix, iy); } else {
custom_message_state--; mbl.set_z(col, row, NAN);
mesh_point++; }
continue; //skip
}*/ // check for points that are skipped
if (iy & 1) ix = (nMeasPoints - 1) - ix; // Zig zag if (nMeasPoints == 3) {
if (nMeasPoints == 7) //if we have 7x7 mesh, compare with Z-calibration for points which are in 3x3 mesh if (!isOn3x3Mesh)
{ continue;
has_z = ((ix % 3 == 0) && (iy % 3 == 0)) && is_bed_z_jitter_data_valid(); } else {
const float x_pos = BED_X(col);
const float y_pos = BED_Y(row);
if ((x_pos < area_min_x || x_pos > area_max_x || y_pos < area_min_y || y_pos > area_max_y) && (!isOn3x3Mesh || has_z)) {
continue;
}
}
// increment the total point counter if the points are not skipped
meshPointsToProbe++;
} }
float z0 = 0.f; }
if (has_z && (mesh_point > 0)) { mbl.upsample_3x3(); //upsample the default mesh
uint16_t z_offset_u = 0;
if (nMeasPoints == 7) { // Save custom message state, set a new custom message state to display: Calibrating point 9.
z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * ((ix/3) + iy - 1))); CustomMsg custom_message_type_old = custom_message_type;
} uint8_t custom_message_state_old = custom_message_state;
else { custom_message_type = CustomMsg::MeshBedLeveling;
z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * (ix + iy * 3 - 1))); custom_message_state = meshPointsToProbe + 10;
} lcd_update(1);
z0 = mbl.z_values[0][0] + *reinterpret_cast<int16_t*>(&z_offset_u) * 0.01;
#ifdef SUPPORT_VERBOSITY // Lift Z to a safe position before probing the first point
if (verbosity_level >= 1) { current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
printf_P(PSTR("Bed leveling, point: %d, calibration Z stored in eeprom: %d, calibration z: %f \n"), mesh_point, z_offset_u, z0); plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
}
#endif // SUPPORT_VERBOSITY // Cycle through all points and probe them
int l_feedmultiply = setup_for_endstop_move(false); //save feedrate and feedmultiply, sets feedmultiply to 100
uint8_t mesh_point = 0; //index number of calibration point
while (mesh_point != MESH_NUM_X_POINTS * MESH_NUM_Y_POINTS) {
// Get coords of a measuring point.
uint8_t ix = mesh_point % MESH_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1
uint8_t iy = mesh_point / MESH_NUM_X_POINTS;
if (iy & 1) ix = (MESH_NUM_X_POINTS - 1) - ix; // Zig zag
bool isOn3x3Mesh = ((ix % 3 == 0) && (iy % 3 == 0));
float x_pos = BED_X(ix);
float y_pos = BED_Y(iy);
if ((nMeasPoints == 3) && !isOn3x3Mesh) {
mesh_point++;
mbl.set_z(ix, iy, NAN);
continue; //skip
} else if ((x_pos < area_min_x || x_pos > area_max_x || y_pos < area_min_y || y_pos > area_max_y) && (!isOn3x3Mesh || has_z)) {
mesh_point++;
continue; //skip
} }
// Move Z up to MESH_HOME_Z_SEARCH. // Move Z up to the probe height of the current Z point.
if((ix == 0) && (iy == 0)) current_position[Z_AXIS] = MESH_HOME_Z_SEARCH; const float z0 = mbl.z_values[iy][ix];
else current_position[Z_AXIS] += 2.f / nMeasPoints; //use relative movement from Z coordinate where PINDa triggered on previous point. This makes calibration faster. const float init_z_bckp = !has_z ? MESH_HOME_Z_SEARCH : z0 + MESH_HOME_Z_SEARCH_FAST;
float init_z_bckp = current_position[Z_AXIS]; if (init_z_bckp > current_position[Z_AXIS]) {
plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE); current_position[Z_AXIS] = init_z_bckp;
st_synchronize(); plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
st_synchronize();
}
// Move to XY position of the sensor point. // Move to XY position of the sensor point.
current_position[X_AXIS] = BED_X(ix, nMeasPoints); current_position[X_AXIS] = x_pos;
current_position[Y_AXIS] = BED_Y(iy, nMeasPoints); current_position[Y_AXIS] = y_pos;
//printf_P(PSTR("[%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
#ifdef SUPPORT_VERBOSITY
if (verbosity_level >= 1) {
bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
SERIAL_PROTOCOL(mesh_point);
clamped ? SERIAL_PROTOCOLPGM(": xy clamped.\n") : SERIAL_PROTOCOLPGM(": no xy clamping\n");
}
#else //SUPPORT_VERBOSITY
world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]); world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
#endif // SUPPORT_VERBOSITY
//printf_P(PSTR("after clamping: [%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE); plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE);
st_synchronize(); st_synchronize();
if (planner_aborted) if (planner_aborted)
@ -2969,72 +2931,48 @@ static void gcode_G80()
} }
// Go down until endstop is hit // Go down until endstop is hit
const float Z_CALIBRATION_THRESHOLD = 1.f; if (!find_bed_induction_sensor_point_z(has_z ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetryCount)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point
if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point
printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW)); printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
break; break;
} }
if (init_z_bckp - current_position[Z_AXIS] < 0.1f) { //broken cable or initial Z coordinate too low. Go to MESH_HOME_Z_SEARCH and repeat last step (z-probe) again to distinguish between these two cases. if (init_z_bckp - current_position[Z_AXIS] < 0.f) { //broken cable or initial Z coordinate too low. Go to MESH_HOME_Z_SEARCH and repeat last step (z-probe) again to distinguish between these two cases.
//printf_P(PSTR("Another attempt! Current Z position: %f\n"), current_position[Z_AXIS]);
current_position[Z_AXIS] = MESH_HOME_Z_SEARCH; current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE); plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
st_synchronize(); st_synchronize();
if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point if (!find_bed_induction_sensor_point_z(has_z ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetryCount)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point
printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW)); printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
break; break;
} }
if (MESH_HOME_Z_SEARCH - current_position[Z_AXIS] < 0.1f) { if (MESH_HOME_Z_SEARCH - current_position[Z_AXIS] < 0.1f) {
puts_P(PSTR("Bed leveling failed. Sensor disconnected or cable broken.")); puts_P(PSTR("Bed leveling failed. Sensor triggered too soon"));
break; break;
} }
} }
if (has_z && fabs(z0 - current_position[Z_AXIS]) > Z_CALIBRATION_THRESHOLD) { //if we have data from z calibration, max. allowed difference is 1mm for each point if (has_z && fabs(z0 - current_position[Z_AXIS]) > Z_CALIBRATION_THRESHOLD) { //if we have data from z calibration, max. allowed difference is 1mm for each point
puts_P(PSTR("Bed leveling failed. Sensor triggered too high.")); puts_P(PSTR("Bed leveling failed. Too much variation from eeprom mesh"));
break; break;
} }
#ifdef SUPPORT_VERBOSITY
if (verbosity_level >= 10) {
SERIAL_ECHOPGM("X: ");
MYSERIAL.print(current_position[X_AXIS], 5);
SERIAL_ECHOLNPGM("");
SERIAL_ECHOPGM("Y: ");
MYSERIAL.print(current_position[Y_AXIS], 5);
SERIAL_PROTOCOLPGM("\n");
}
#endif // SUPPORT_VERBOSITY
float offset_z = 0;
#ifdef PINDA_THERMISTOR #ifdef PINDA_THERMISTOR
offset_z = temp_compensation_pinda_thermistor_offset(current_temperature_pinda); float offset_z = temp_compensation_pinda_thermistor_offset(current_temperature_pinda);
#endif //PINDA_THERMISTOR
// #ifdef SUPPORT_VERBOSITY
/* if (verbosity_level >= 1)
{
SERIAL_ECHOPGM("mesh bed leveling: ");
MYSERIAL.print(current_position[Z_AXIS], 5);
SERIAL_ECHOPGM(" offset: ");
MYSERIAL.print(offset_z, 5);
SERIAL_ECHOLNPGM("");
}*/
// #endif // SUPPORT_VERBOSITY
mbl.set_z(ix, iy, current_position[Z_AXIS] - offset_z); //store measured z values z_values[iy][ix] = z - offset_z; mbl.set_z(ix, iy, current_position[Z_AXIS] - offset_z); //store measured z values z_values[iy][ix] = z - offset_z;
#else
mbl.set_z(ix, iy, current_position[Z_AXIS]); //store measured z values z_values[iy][ix] = z;
#endif //PINDA_THERMISTOR
custom_message_state--; custom_message_state--;
mesh_point++; mesh_point++;
lcd_update(1); lcd_update(1);
} }
current_position[Z_AXIS] = MESH_HOME_Z_SEARCH; current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
#ifdef SUPPORT_VERBOSITY
if (verbosity_level >= 20) {
SERIAL_ECHOLNPGM("Mesh bed leveling while loop finished.");
SERIAL_ECHOLNPGM("MESH_HOME_Z_SEARCH: ");
MYSERIAL.print(current_position[Z_AXIS], 5);
}
#endif // SUPPORT_VERBOSITY
plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE); plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
st_synchronize(); st_synchronize();
if (mesh_point != nMeasPoints * nMeasPoints) { static uint8_t g80_fail_cnt = 0;
if (mesh_point != MESH_NUM_X_POINTS * MESH_NUM_Y_POINTS) {
if (g80_fail_cnt++ >= 2) {
kill(_i("Mesh bed leveling failed. Please run Z calibration.")); ////MSG_MBL_FAILED_Z_CAL c=20 r=4
}
Sound_MakeSound(e_SOUND_TYPE_StandardAlert); Sound_MakeSound(e_SOUND_TYPE_StandardAlert);
bool bState; bool bState;
do { // repeat until Z-leveling o.k. do { // repeat until Z-leveling o.k.
@ -3054,14 +2992,13 @@ static void gcode_G80()
tmc2130_home_enter(Z_AXIS_MASK); tmc2130_home_enter(Z_AXIS_MASK);
#endif // TMC2130 #endif // TMC2130
current_position[Z_AXIS] = MESH_HOME_Z_SEARCH; current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 40); plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
st_synchronize(); st_synchronize();
#ifdef TMC2130 #ifdef TMC2130
tmc2130_home_exit(); tmc2130_home_exit();
#endif // TMC2130 #endif // TMC2130
enable_z_endstop(bState); enable_z_endstop(bState);
} while (st_get_position_mm(Z_AXIS) > MESH_HOME_Z_SEARCH); // i.e. Z-leveling not o.k. } while (st_get_position_mm(Z_AXIS) > MESH_HOME_Z_SEARCH); // i.e. Z-leveling not o.k.
// plan_set_z_position(MESH_HOME_Z_SEARCH); // is not necessary ('do-while' loop always ends at the expected Z-position)
custom_message_type = custom_message_type_old; custom_message_type = custom_message_type_old;
custom_message_state = custom_message_state_old; custom_message_state = custom_message_state_old;
@ -3070,101 +3007,64 @@ static void gcode_G80()
repeatcommand_front(); // re-run (i.e. of "G80") repeatcommand_front(); // re-run (i.e. of "G80")
return; return;
} }
g80_fail_cnt = 0; // no fail was detected. Reset the error counter.
clean_up_after_endstop_move(l_feedmultiply); clean_up_after_endstop_move(l_feedmultiply);
// SERIAL_ECHOLNPGM("clean up finished ");
#ifndef PINDA_THERMISTOR #ifndef PINDA_THERMISTOR
if(eeprom_read_byte((uint8_t *)EEPROM_TEMP_CAL_ACTIVE) && calibration_status_pinda() == true) temp_compensation_apply(); //apply PINDA temperature compensation if(eeprom_read_byte((uint8_t *)EEPROM_TEMP_CAL_ACTIVE) && calibration_status_pinda() == true) temp_compensation_apply(); //apply PINDA temperature compensation
#endif #endif
babystep_apply(); // Apply Z height correction aka baby stepping before mesh bed leveing gets activated. babystep_apply(); // Apply Z height correction aka baby stepping before mesh bed leveing gets activated.
// SERIAL_ECHOLNPGM("babystep applied");
// Apply the bed level correction to the mesh
bool eeprom_bed_correction_valid = eeprom_read_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID) == 1; bool eeprom_bed_correction_valid = eeprom_read_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID) == 1;
#ifdef SUPPORT_VERBOSITY auto bedCorrectHelper = [eeprom_bed_correction_valid] (char code, uint8_t *eep_address) -> int8_t {
if (verbosity_level >= 1) { if (code_seen(code)) {
eeprom_bed_correction_valid ? SERIAL_PROTOCOLPGM("Bed correction data valid\n") : SERIAL_PROTOCOLPGM("Bed correction data not valid\n"); // Verify value is within allowed range
} int16_t temp = code_value_short();
#endif // SUPPORT_VERBOSITY if (abs(temp) > BED_ADJUSTMENT_UM_MAX) {
const constexpr uint8_t sides = 4; printf_P(PSTR("%SExcessive bed leveling correction: %i microns\n"), errormagic, temp);
int8_t correction[sides] = {0};
for (uint8_t i = 0; i < sides; ++i) {
static const char codes[sides] PROGMEM = { 'L', 'R', 'F', 'B' };
static uint8_t *const eep_addresses[sides] PROGMEM = {
(uint8_t*)EEPROM_BED_CORRECTION_LEFT,
(uint8_t*)EEPROM_BED_CORRECTION_RIGHT,
(uint8_t*)EEPROM_BED_CORRECTION_FRONT,
(uint8_t*)EEPROM_BED_CORRECTION_REAR,
};
if (code_seen(pgm_read_byte(&codes[i])))
{ // Verify value is within allowed range
int32_t temp = code_value_long();
if (labs(temp) > BED_ADJUSTMENT_UM_MAX) {
SERIAL_ERROR_START;
SERIAL_ECHOPGM("Excessive bed leveling correction: ");
SERIAL_ECHO(temp);
SERIAL_ECHOLNPGM(" microns");
correction[i] = 0;
} else { } else {
// Value is valid, save it return (int8_t)temp; // Value is valid, use it
correction[i] = (int8_t)temp;
} }
} else if (eeprom_bed_correction_valid) } else if (eeprom_bed_correction_valid) {
correction[i] = (int8_t)eeprom_read_byte((uint8_t*)pgm_read_ptr(&eep_addresses[i])); return (int8_t)eeprom_read_byte(eep_address);
if (correction[i] == 0) }
continue; return 0;
} };
for (uint8_t row = 0; row < nMeasPoints; ++row) { const int8_t correction[4] = {
for (uint8_t col = 0; col < nMeasPoints; ++col) { bedCorrectHelper('L', (uint8_t*)EEPROM_BED_CORRECTION_LEFT),
mbl.z_values[row][col] +=0.001f * ( bedCorrectHelper('R', (uint8_t*)EEPROM_BED_CORRECTION_RIGHT),
+ correction[0] * (nMeasPoints - 1 - col) bedCorrectHelper('F', (uint8_t*)EEPROM_BED_CORRECTION_FRONT),
bedCorrectHelper('B', (uint8_t*)EEPROM_BED_CORRECTION_REAR),
};
for (uint8_t row = 0; row < MESH_NUM_Y_POINTS; row++) {
for (uint8_t col = 0; col < MESH_NUM_X_POINTS; col++) {
constexpr float scaler = 0.001f / (MESH_NUM_X_POINTS - 1);
mbl.z_values[row][col] += scaler * (
+ correction[0] * (MESH_NUM_X_POINTS - 1 - col)
+ correction[1] * col + correction[1] * col
+ correction[2] * (nMeasPoints - 1 - row) + correction[2] * (MESH_NUM_Y_POINTS - 1 - row)
+ correction[3] * row) / (float)(nMeasPoints - 1); + correction[3] * row);
} }
} }
// SERIAL_ECHOLNPGM("Bed leveling correction finished");
if (nMeasPoints == 3) { mbl.upsample_3x3(); //interpolation from 3x3 to 7x7 points using largrangian polynomials while using the same array z_values[iy][ix] for storing (just coppying measured data to new destination and interpolating between them)
mbl.upsample_3x3(); //interpolation from 3x3 to 7x7 points using largrangian polynomials while using the same array z_values[iy][ix] for storing (just coppying measured data to new destination and interpolating between them)
uint8_t useMagnetCompensation = code_seen('M') ? code_value_uint8() : eeprom_read_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION);
if (nMeasPoints == 7 && useMagnetCompensation) {
mbl_magnet_elimination();
} }
/*
SERIAL_PROTOCOLPGM("Num X,Y: ");
SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
SERIAL_PROTOCOLPGM(",");
SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
SERIAL_PROTOCOLPGM("\nZ search height: ");
SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
SERIAL_PROTOCOLLNPGM("\nMeasured points:");
for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
SERIAL_PROTOCOLPGM(" ");
SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
}
SERIAL_PROTOCOLPGM("\n");
}
*/
if (nMeasPoints == 7 && magnet_elimination) {
mbl_interpolation(nMeasPoints);
}
/*
SERIAL_PROTOCOLPGM("Num X,Y: ");
SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
SERIAL_PROTOCOLPGM(",");
SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
SERIAL_PROTOCOLPGM("\nZ search height: ");
SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
SERIAL_PROTOCOLLNPGM("\nMeasured points:");
for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
SERIAL_PROTOCOLPGM(" ");
SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
}
SERIAL_PROTOCOLPGM("\n");
}
*/
// SERIAL_ECHOLNPGM("Upsample finished");
mbl.active = 1; //activate mesh bed leveling mbl.active = 1; //activate mesh bed leveling
// SERIAL_ECHOLNPGM("Mesh bed leveling activated");
go_home_with_z_lift(); if (code_seen('O') && !code_value_uint8()) {
// SERIAL_ECHOLNPGM("Go home finished"); // Don't let the manage_inactivity() function remove power from the motors.
refresh_cmd_timeout();
} else {
go_home_with_z_lift();
}
#ifndef PINDA_THERMISTOR #ifndef PINDA_THERMISTOR
//unretract (after PINDA preheat retraction) //unretract (after PINDA preheat retraction)
if (temp_compensation_retracted) { if (temp_compensation_retracted) {
@ -3426,10 +3326,10 @@ static void mmu_M600_filament_change_screen(uint8_t eject_slot) {
manage_heater(); manage_heater();
manage_inactivity(true); manage_inactivity(true);
btn = MMU2::mmu2.getPrinterButtonOperation(); btn = MMU2::mmu2.GetPrinterButtonOperation();
if (btn != MMU2::Buttons::NoButton) if (btn != MMU2::Buttons::NoButton)
{ {
MMU2::mmu2.clearPrinterButtonOperation(); MMU2::mmu2.ClearPrinterButtonOperation();
if (btn == MMU2::Buttons::Eject) { if (btn == MMU2::Buttons::Eject) {
if (eject_slot != (uint8_t)MMU2::FILAMENT_UNKNOWN) { if (eject_slot != (uint8_t)MMU2::FILAMENT_UNKNOWN) {
@ -4981,12 +4881,13 @@ void process_commands()
Default 3x3 grid can be changed on MK2.5/s and MK3/s to 7x7 grid. Default 3x3 grid can be changed on MK2.5/s and MK3/s to 7x7 grid.
#### Usage #### Usage
G80 [ N | R | V | L | R | F | B ] G80 [ N | C | O | M | L | R | F | B | X | Y | W | H ]
#### Parameters #### Parameters
- `N` - Number of mesh points on x axis. Default is 3. Valid values are 3 and 7. - `N` - Number of mesh points on x axis. Default is value stored in EEPROM. Valid values are 3 and 7.
- `R` - Probe retries. Default 3 max. 10 - `C` - Probe retry counts. Default is value stored in EEPROM. Valid values are 1 to 10.
- `V` - Verbosity level 1=low, 10=mid, 20=high. It only can be used if the firmware has been compiled with SUPPORT_VERBOSITY active. - `O` - Return to origin. Default is 1. Valid values are 0 (false) and 1 (true).
- `M` - Use magnet compensation. Will only be used if number of mesh points is set to 7. Default is value stored in EEPROM. Valid values are 0 (false) and 1 (true).
Using the following parameters enables additional "manual" bed leveling correction. Valid values are -100 microns to 100 microns. Using the following parameters enables additional "manual" bed leveling correction. Valid values are -100 microns to 100 microns.
#### Additional Parameters #### Additional Parameters
@ -4994,16 +4895,13 @@ void process_commands()
- `R` - Right Bed Level correct value in um. - `R` - Right Bed Level correct value in um.
- `F` - Front Bed Level correct value in um. - `F` - Front Bed Level correct value in um.
- `B` - Back Bed Level correct value in um. - `B` - Back Bed Level correct value in um.
The following parameters are used to define the area used by the print:
- `X` - area lower left point X coordinate
- `Y` - area lower left point Y coordinate
- `W` - area width (on X axis)
- `H` - area height (on Y axis)
*/ */
/*
* Probes a grid and produces a mesh to compensate for variable bed height
* The S0 report the points as below
* +----> X-axis
* |
* |
* v Y-axis
*/
case 80: { case 80: {
gcode_G80(); gcode_G80();
@ -5016,20 +4914,7 @@ void process_commands()
*/ */
case 81: case 81:
if (mbl.active) { if (mbl.active) {
SERIAL_PROTOCOLPGM("Num X,Y: "); mbl.print();
SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
SERIAL_PROTOCOL(',');
SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
SERIAL_PROTOCOLPGM("\nZ search height: ");
SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
SERIAL_PROTOCOLLNPGM("\nMeasured points:");
for (uint8_t y = MESH_NUM_Y_POINTS; y-- > 0;) {
for (uint8_t x = 0; x < MESH_NUM_X_POINTS; x++) {
SERIAL_PROTOCOLPGM(" ");
SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
}
SERIAL_PROTOCOLLN();
}
} }
else else
SERIAL_PROTOCOLLNPGM("Mesh bed leveling not active."); SERIAL_PROTOCOLLNPGM("Mesh bed leveling not active.");
@ -7757,14 +7642,14 @@ Sigma_Exit:
case 850: { case 850: {
//! ### M850 - set sheet parameters //! ### M850 - set sheet parameters
//! //!@n M850 - Set sheet data S[id] Z[offset] L[label] B[bed_temp] P[PINDA_TEMP] //! //!@n M850 - Set sheet data S[id] Z[offset] L[label] B[bed_temp] P[PINDA_TEMP] A[IS_ACTIVE]
bool bHasZ = false, bHasLabel = false, bHasBed = false, bHasPinda = false;
uint8_t iSel = 0; uint8_t iSel = 0;
int16_t zraw = 0; int16_t zraw = 0;
float z_val = 0; float z_val = 0;
char strLabel[8]; char strLabel[8];
uint8_t iBedC = 0; uint8_t iBedC = 0;
uint8_t iPindaC = 0; uint8_t iPindaC = 0;
bool bIsActive=false;
strLabel[7] = '\0'; // null terminate. strLabel[7] = '\0'; // null terminate.
size_t max_sheets = sizeof(EEPROM_Sheets_base->s)/sizeof(EEPROM_Sheets_base->s[0]); size_t max_sheets = sizeof(EEPROM_Sheets_base->s)/sizeof(EEPROM_Sheets_base->s[0]);
@ -7780,6 +7665,7 @@ Sigma_Exit:
} else { } else {
break; break;
} }
if (code_seen('Z')){ if (code_seen('Z')){
z_val = code_value(); z_val = code_value();
zraw = z_val*cs.axis_steps_per_mm[Z_AXIS]; zraw = z_val*cs.axis_steps_per_mm[Z_AXIS];
@ -7788,7 +7674,7 @@ Sigma_Exit:
SERIAL_PROTOCOLLNPGM(" Z VALUE OUT OF RANGE"); SERIAL_PROTOCOLLNPGM(" Z VALUE OUT OF RANGE");
break; break;
} }
bHasZ = true; eeprom_update_word(reinterpret_cast<uint16_t *>(&(EEPROM_Sheets_base->s[iSel].z_offset)),zraw);
} }
else else
{ {
@ -7798,13 +7684,13 @@ Sigma_Exit:
if (code_seen('L')) if (code_seen('L'))
{ {
bHasLabel = true;
char *src = strchr_pointer + 1; char *src = strchr_pointer + 1;
while (*src == ' ') ++src; while (*src == ' ') ++src;
if (*src != '\0') if (*src != '\0')
{ {
strncpy(strLabel,src,7); strncpy(strLabel,src,7);
} }
eeprom_update_block(strLabel,EEPROM_Sheets_base->s[iSel].name,sizeof(Sheet::name));
} }
else else
{ {
@ -7813,8 +7699,8 @@ Sigma_Exit:
if (code_seen('B')) if (code_seen('B'))
{ {
bHasBed = true;
iBedC = code_value_uint8(); iBedC = code_value_uint8();
eeprom_update_byte(&EEPROM_Sheets_base->s[iSel].bed_temp, iBedC);
} }
else else
{ {
@ -7823,12 +7709,22 @@ Sigma_Exit:
if (code_seen('P')) if (code_seen('P'))
{ {
bHasPinda = true;
iPindaC = code_value_uint8(); iPindaC = code_value_uint8();
eeprom_update_byte(&EEPROM_Sheets_base->s[iSel].pinda_temp, iPindaC);
} }
else else
iPindaC = eeprom_read_byte(&EEPROM_Sheets_base->s[iSel].pinda_temp);
{ {
iPindaC = eeprom_read_byte(&EEPROM_Sheets_base->s[iSel].pinda_temp);
}
if (code_seen('A'))
{
bIsActive |= code_value_uint8() || (eeprom_read_byte(&(EEPROM_Sheets_base->active_sheet)) == iSel);
if(bIsActive) eeprom_update_byte(&EEPROM_Sheets_base->active_sheet, iSel);
}
else
{
bIsActive = (eeprom_read_byte(&(EEPROM_Sheets_base->active_sheet)) == iSel);
} }
SERIAL_PROTOCOLPGM("Sheet "); SERIAL_PROTOCOLPGM("Sheet ");
@ -7836,22 +7732,6 @@ Sigma_Exit:
if (!eeprom_is_sheet_initialized(iSel)) if (!eeprom_is_sheet_initialized(iSel))
SERIAL_PROTOCOLLNPGM(" NOT INITIALIZED"); SERIAL_PROTOCOLLNPGM(" NOT INITIALIZED");
if (bHasZ)
{
eeprom_update_word(reinterpret_cast<uint16_t *>(&(EEPROM_Sheets_base->s[iSel].z_offset)),zraw);
}
if (bHasLabel)
{
eeprom_update_block(strLabel,EEPROM_Sheets_base->s[iSel].name,sizeof(Sheet::name));
}
if (bHasBed)
{
eeprom_update_byte(&EEPROM_Sheets_base->s[iSel].bed_temp, iBedC);
}
if (bHasPinda)
{
eeprom_update_byte(&EEPROM_Sheets_base->s[iSel].pinda_temp, iPindaC);
}
SERIAL_PROTOCOLPGM(" Z"); SERIAL_PROTOCOLPGM(" Z");
SERIAL_PROTOCOL_F(z_val,4); SERIAL_PROTOCOL_F(z_val,4);
@ -7863,7 +7743,8 @@ Sigma_Exit:
SERIAL_PROTOCOL((int)iBedC); SERIAL_PROTOCOL((int)iBedC);
SERIAL_PROTOCOLPGM(" P"); SERIAL_PROTOCOLPGM(" P");
SERIAL_PROTOCOLLN((int)iPindaC); SERIAL_PROTOCOLLN((int)iPindaC);
SERIAL_PROTOCOLPGM(" A");
SERIAL_PROTOCOLLN((int)bIsActive);
break; break;
} }
@ -7898,9 +7779,7 @@ Sigma_Exit:
cancel_heatup = false; cancel_heatup = false;
bool is_pinda_cooling = false; bool is_pinda_cooling = false;
if ((degTargetBed() == 0) && (degTargetHotend(0) == 0)) { if (!(CHECK_ALL_HEATERS)) is_pinda_cooling = true;
is_pinda_cooling = true;
}
while ( ((!is_pinda_cooling) && (!cancel_heatup) && (current_temperature_pinda < set_target_pinda)) || (is_pinda_cooling && (current_temperature_pinda > set_target_pinda)) ) { while ( ((!is_pinda_cooling) && (!cancel_heatup) && (current_temperature_pinda < set_target_pinda)) || (is_pinda_cooling && (current_temperature_pinda > set_target_pinda)) ) {
if ((_millis() - codenum) > 1000) //Print Temp Reading every 1 second while waiting. if ((_millis() - codenum) > 1000) //Print Temp Reading every 1 second while waiting.
@ -8250,43 +8129,53 @@ Sigma_Exit:
### M914 - Set TMC2130 normal mode <a href="https://reprap.org/wiki/G-code#M914:_Set_TMC2130_normal_mode">M914: Set TMC2130 normal mode</a> ### M914 - Set TMC2130 normal mode <a href="https://reprap.org/wiki/G-code#M914:_Set_TMC2130_normal_mode">M914: Set TMC2130 normal mode</a>
Updates EEPROM only if "P" is given, otherwise temporary (lasts until reset or motor idle timeout) Updates EEPROM only if "P" is given, otherwise temporary (lasts until reset or motor idle timeout)
#### Usage #### Usage
M914 [ P | R ] M914 [ P | R | Q ]
#### Parameters #### Parameters
- `P` - Make the mode change permanent (write to EEPROM) - `P` - Make the mode change permanent (write to EEPROM)
- `R` - Revert to EEPROM value - `R` - Revert to EEPROM value
- `Q` - Print effective silent/normal status. (Does not report override)
*/ */
/*! /*!
### M915 - Set TMC2130 silent mode <a href="https://reprap.org/wiki/G-code#M915:_Set_TMC2130_silent_mode">M915: Set TMC2130 silent mode</a> ### M915 - Set TMC2130 silent mode <a href="https://reprap.org/wiki/G-code#M915:_Set_TMC2130_silent_mode">M915: Set TMC2130 silent mode</a>
Updates EEPROM only if "P" is given, otherwise temporary (lasts until reset or motor idle timeout) Updates EEPROM only if "P" is given, otherwise temporary (lasts until reset or motor idle timeout)
#### Usage #### Usage
M915 [ P | R ] M915 [ P | R | Q]
#### Parameters #### Parameters
- `P` - Make the mode change permanent (write to EEPROM) - `P` - Make the mode change permanent (write to EEPROM)
- `R` - Revert to EEPROM value - `R` - Revert to EEPROM value
- `Q` - Print effective silent/normal status. (Does not report override)
*/ */
#ifdef TMC2130 #ifdef TMC2130
case 914: case 914:
case 915: case 915:
{ {
uint8_t newMode = (mcode_in_progress==914) ? TMC2130_MODE_NORMAL : TMC2130_MODE_SILENT; uint8_t newMode = (mcode_in_progress==914) ? TMC2130_MODE_NORMAL : TMC2130_MODE_SILENT;
//printf_P(_n("tmc2130mode/smm/eep: %d %d %d %d"),tmc2130_mode,SilentModeMenu,eeprom_read_byte((uint8_t*)EEPROM_SILENT), bEnableForce_z); //printf_P(_n("tmc2130mode/smm/eep: %d %d %d %d"),tmc2130_mode,SilentModeMenu,eeprom_read_byte((uint8_t*)EEPROM_SILENT), bEnableForce_z);
if (code_seen('R')) if (code_seen('R'))
{ {
newMode = eeprom_read_byte((uint8_t*)EEPROM_SILENT); newMode = eeprom_read_byte((uint8_t*)EEPROM_SILENT);
} }
else if (code_seen('P')) else if (code_seen('P'))
{ {
uint8_t newMenuMode = (mcode_in_progress==914) ? SILENT_MODE_NORMAL : SILENT_MODE_STEALTH; uint8_t newMenuMode = (mcode_in_progress==914) ? SILENT_MODE_NORMAL : SILENT_MODE_STEALTH;
eeprom_update_byte((unsigned char *)EEPROM_SILENT, newMenuMode); eeprom_update_byte((unsigned char *)EEPROM_SILENT, newMenuMode);
SilentModeMenu = newMenuMode; SilentModeMenu = newMenuMode;
//printf_P(_n("tmc2130mode/smm/eep: %d %d %d %d"),tmc2130_mode,SilentModeMenu,eeprom_read_byte((uint8_t*)EEPROM_SILENT), bEnableForce_z); //printf_P(_n("tmc2130mode/smm/eep: %d %d %d %d"),tmc2130_mode,SilentModeMenu,eeprom_read_byte((uint8_t*)EEPROM_SILENT), bEnableForce_z);
} }
else if (code_seen('Q'))
{
printf_P(PSTR("%S: %S\n"), _O(MSG_MODE),
tmc2130_mode == TMC2130_MODE_NORMAL ?
_O(MSG_NORMAL) : _O(MSG_SILENT)
);
}
if (tmc2130_mode != newMode if (tmc2130_mode != newMode
#ifdef PSU_Delta #ifdef PSU_Delta
|| !bEnableForce_z || !bEnableForce_z
@ -9392,11 +9281,11 @@ void controllerFan()
*/ */
static void handleSafetyTimer() static void handleSafetyTimer()
{ {
if (printer_active() || (!degTargetBed() && !degTargetHotend(0)) || (!safetytimer_inactive_time)) if (printer_active() || !(CHECK_ALL_HEATERS) || !safetytimer_inactive_time)
{ {
safetyTimer.stop(); safetyTimer.stop();
} }
else if ((degTargetBed() || degTargetHotend(0)) && (!safetyTimer.running())) else if ((CHECK_ALL_HEATERS) && !safetyTimer.running())
{ {
safetyTimer.start(); safetyTimer.start();
} }

View File

@ -6,17 +6,6 @@
#include "Timer.h" #include "Timer.h"
#include "system_timer.h" #include "system_timer.h"
/**
* @brief construct Timer
*
* It is guaranteed, that construction is equivalent with zeroing all members.
* This property can be exploited in menu_data.
*/
template<typename T>
Timer<T>::Timer() : m_isRunning(false), m_started()
{
}
/** /**
* @brief Start timer * @brief Start timer
*/ */

View File

@ -17,7 +17,10 @@ template <class T>
class Timer class Timer
{ {
public: public:
Timer(); inline constexpr Timer()
: m_isRunning(false)
, m_started(0) {};
void start(); void start();
void stop(){m_isRunning = false;} void stop(){m_isRunning = false;}
bool running()const {return m_isRunning;} bool running()const {return m_isRunning;}

View File

@ -685,11 +685,11 @@ bool is_bed_z_jitter_data_valid()
// offsets of the Z heiths of the calibration points from the first point are saved as 16bit signed int, scaled to tenths of microns // offsets of the Z heiths of the calibration points from the first point are saved as 16bit signed int, scaled to tenths of microns
// if at least one 16bit integer has different value then -1 (0x0FFFF), data are considered valid and function returns true, otherwise it returns false // if at least one 16bit integer has different value then -1 (0x0FFFF), data are considered valid and function returns true, otherwise it returns false
{ {
bool data_valid = false;
for (int8_t i = 0; i < 8; ++i) { for (int8_t i = 0; i < 8; ++i) {
if (eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + i * 2)) != 0x0FFFF) data_valid = true; if (eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + i * 2)) != 0x0FFFF)
return true;
} }
return data_valid; return false;
} }
static void world2machine_update(const float vec_x[2], const float vec_y[2], const float cntr[2]) static void world2machine_update(const float vec_x[2], const float vec_y[2], const float cntr[2])
@ -776,7 +776,7 @@ void world2machine_revert_to_uncorrected()
static inline bool vec_undef(const float v[2]) static inline bool vec_undef(const float v[2])
{ {
const uint32_t *vx = (const uint32_t*)v; const uint32_t *vx = (const uint32_t*)v;
return vx[0] == 0x0FFFFFFFF || vx[1] == 0x0FFFFFFFF; return vx[0] == 0xFFFFFFFF || vx[1] == 0xFFFFFFFF;
} }
@ -2184,6 +2184,16 @@ inline void scan_bed_induction_sensor_point()
#define MESH_BED_CALIBRATION_SHOW_LCD #define MESH_BED_CALIBRATION_SHOW_LCD
float __attribute__((noinline)) BED_X(const uint8_t col)
{
return ((float)col * x_mesh_density + BED_X0);
}
float __attribute__((noinline)) BED_Y(const uint8_t row)
{
return ((float)row * y_mesh_density + BED_Y0);
}
BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level, uint8_t &too_far_mask) BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level, uint8_t &too_far_mask)
{ {
// Don't let the manage_inactivity() function remove power from the motors. // Don't let the manage_inactivity() function remove power from the motors.
@ -2481,8 +2491,8 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
uint8_t ix = mesh_point % MESH_MEAS_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1 uint8_t ix = mesh_point % MESH_MEAS_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1
uint8_t iy = mesh_point / MESH_MEAS_NUM_X_POINTS; uint8_t iy = mesh_point / MESH_MEAS_NUM_X_POINTS;
if (iy & 1) ix = (MESH_MEAS_NUM_X_POINTS - 1) - ix; if (iy & 1) ix = (MESH_MEAS_NUM_X_POINTS - 1) - ix;
current_position[X_AXIS] = BED_X(ix, MESH_MEAS_NUM_X_POINTS); current_position[X_AXIS] = BED_X(ix * 3);
current_position[Y_AXIS] = BED_Y(iy, MESH_MEAS_NUM_Y_POINTS); current_position[Y_AXIS] = BED_Y(iy * 3);
go_to_current(homing_feedrate[X_AXIS] / 60); go_to_current(homing_feedrate[X_AXIS] / 60);
delay_keep_alive(3000); delay_keep_alive(3000);
} }
@ -2820,16 +2830,16 @@ void go_home_with_z_lift()
// Go home. // Go home.
// First move up to a safe height. // First move up to a safe height.
current_position[Z_AXIS] = MESH_HOME_Z_SEARCH; current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
go_to_current(homing_feedrate[Z_AXIS]/60); go_to_current(homing_feedrate[Z_AXIS] / 60);
// Second move to XY [0, 0]. // Second move to XY [0, 0].
current_position[X_AXIS] = X_MIN_POS+0.2; current_position[X_AXIS] = X_MIN_POS + 0.2;
current_position[Y_AXIS] = Y_MIN_POS+0.2; current_position[Y_AXIS] = Y_MIN_POS + 0.2;
// Clamp to the physical coordinates. // Clamp to the physical coordinates.
world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]); world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
go_to_current(homing_feedrate[X_AXIS]/20); go_to_current((3 * homing_feedrate[X_AXIS]) / 60);
// Third move up to a safe height. // Third move up to a safe height.
current_position[Z_AXIS] = Z_MIN_POS; current_position[Z_AXIS] = Z_MIN_POS;
go_to_current(homing_feedrate[Z_AXIS]/60); go_to_current(homing_feedrate[Z_AXIS] / 60);
} }
// Sample the 9 points of the bed and store them into the EEPROM as a reference. // Sample the 9 points of the bed and store them into the EEPROM as a reference.
@ -2890,8 +2900,8 @@ bool sample_mesh_and_store_reference()
uint8_t ix = mesh_point % MESH_MEAS_NUM_X_POINTS; uint8_t ix = mesh_point % MESH_MEAS_NUM_X_POINTS;
uint8_t iy = mesh_point / MESH_MEAS_NUM_X_POINTS; uint8_t iy = mesh_point / MESH_MEAS_NUM_X_POINTS;
if (iy & 1) ix = (MESH_MEAS_NUM_X_POINTS - 1) - ix; // Zig zag if (iy & 1) ix = (MESH_MEAS_NUM_X_POINTS - 1) - ix; // Zig zag
current_position[X_AXIS] = BED_X(ix, MESH_MEAS_NUM_X_POINTS); current_position[X_AXIS] = BED_X(ix * 3);
current_position[Y_AXIS] = BED_Y(iy, MESH_MEAS_NUM_Y_POINTS); current_position[Y_AXIS] = BED_Y(iy * 3);
world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]); world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
go_to_current(homing_feedrate[X_AXIS]/60); go_to_current(homing_feedrate[X_AXIS]/60);
#ifdef MESH_BED_CALIBRATION_SHOW_LCD #ifdef MESH_BED_CALIBRATION_SHOW_LCD
@ -2957,8 +2967,7 @@ bool sample_mesh_and_store_reference()
} }
} }
mbl.upsample_3x3(); mbl.reset();
mbl.active = true;
go_home_with_z_lift(); go_home_with_z_lift();
@ -3010,8 +3019,8 @@ bool scan_bed_induction_points(int8_t verbosity_level)
uint8_t ix = mesh_point % MESH_MEAS_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1 uint8_t ix = mesh_point % MESH_MEAS_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1
uint8_t iy = mesh_point / MESH_MEAS_NUM_X_POINTS; uint8_t iy = mesh_point / MESH_MEAS_NUM_X_POINTS;
if (iy & 1) ix = (MESH_MEAS_NUM_X_POINTS - 1) - ix; if (iy & 1) ix = (MESH_MEAS_NUM_X_POINTS - 1) - ix;
float bedX = BED_X(ix, MESH_MEAS_NUM_X_POINTS); float bedX = BED_X(ix * 3);
float bedY = BED_Y(iy, MESH_MEAS_NUM_Y_POINTS); float bedY = BED_Y(iy * 3);
current_position[X_AXIS] = vec_x[0] * bedX + vec_y[0] * bedY + cntr[0]; current_position[X_AXIS] = vec_x[0] * bedX + vec_y[0] * bedY + cntr[0];
current_position[Y_AXIS] = vec_x[1] * bedX + vec_y[1] * bedY + cntr[1]; current_position[Y_AXIS] = vec_x[1] * bedX + vec_y[1] * bedY + cntr[1];
// The calibration points are very close to the min Y. // The calibration points are very close to the min Y.
@ -3040,9 +3049,12 @@ bool scan_bed_induction_points(int8_t verbosity_level)
// To replace loading of the babystep correction. // To replace loading of the babystep correction.
static void shift_z(float delta) static void shift_z(float delta)
{ {
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] - delta, current_position[E_AXIS], homing_feedrate[Z_AXIS]/40); const float curpos_z = current_position[Z_AXIS];
current_position[Z_AXIS] -= delta;
plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 60);
st_synchronize(); st_synchronize();
plan_set_z_position(current_position[Z_AXIS]); current_position[Z_AXIS] = curpos_z;
plan_set_z_position(curpos_z);
} }
// Number of baby steps applied // Number of baby steps applied
@ -3118,20 +3130,19 @@ void mbl_settings_init() {
//magnet elimination: use aaproximate Z-coordinate instead of measured values for points which are near magnets //magnet elimination: use aaproximate Z-coordinate instead of measured values for points which are near magnets
eeprom_init_default_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION, 1); eeprom_init_default_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION, 1);
eeprom_init_default_byte((uint8_t*)EEPROM_MBL_POINTS_NR, 3); eeprom_init_default_byte((uint8_t*)EEPROM_MBL_POINTS_NR, 3);
mbl_z_probe_nr = eeprom_init_default_byte((uint8_t*)EEPROM_MBL_PROBE_NR, 3); eeprom_init_default_byte((uint8_t*)EEPROM_MBL_PROBE_NR, 3);
} }
//parameter ix: index of mesh bed leveling point in X-axis (for meas_points == 7 is valid range from 0 to 6; for meas_points == 3 is valid range from 0 to 2 ) //parameter ix: index of mesh bed leveling point in X-axis (for meas_points == 7 is valid range from 0 to 6; for meas_points == 3 is valid range from 0 to 2 )
//parameter iy: index of mesh bed leveling point in Y-axis (for meas_points == 7 is valid range from 0 to 6; for meas_points == 3 is valid range from 0 to 2 ) //parameter iy: index of mesh bed leveling point in Y-axis (for meas_points == 7 is valid range from 0 to 6; for meas_points == 3 is valid range from 0 to 2 )
//parameter meas_points: number of mesh bed leveling points in one axis; currently designed and tested for values 3 and 7
//parameter zigzag: false if ix is considered 0 on left side of bed and ix rises with rising X coordinate; true if ix is considered 0 on the right side of heatbed for odd iy values (zig zag mesh bed leveling movements)
//function returns true if point is considered valid (typicaly in safe distance from magnet or another object which inflences PINDA measurements) //function returns true if point is considered valid (typicaly in safe distance from magnet or another object which inflences PINDA measurements)
bool mbl_point_measurement_valid(uint8_t ix, uint8_t iy, uint8_t meas_points, bool zigzag) { bool mbl_point_measurement_valid(uint8_t ix, uint8_t iy) {
//"human readable" heatbed plan //"human readable" heatbed plan
//magnet proximity influence Z coordinate measurements significantly (40 - 100 um) //magnet proximity influence Z coordinate measurements significantly (40 - 100 um)
//0 - measurement point is above magnet and Z coordinate can be influenced negatively //0 - measurement point is above magnet and Z coordinate can be influenced negatively
//1 - we should be in safe distance from magnets, measurement should be accurate //1 - we should be in safe distance from magnets, measurement should be accurate
if ((ix >= meas_points) || (iy >= meas_points)) return false; if ((ix >= MESH_NUM_X_POINTS) || (iy >= MESH_NUM_Y_POINTS))
return false;
uint8_t valid_points_mask[7] = { uint8_t valid_points_mask[7] = {
//[X_MAX,Y_MAX] //[X_MAX,Y_MAX]
@ -3145,36 +3156,26 @@ bool mbl_point_measurement_valid(uint8_t ix, uint8_t iy, uint8_t meas_points, bo
0b1111111,//0 0b1111111,//0
//[0,0] //[0,0]
}; };
if (meas_points == 3) { return (valid_points_mask[6 - iy] & (1 << (6 - ix)));
ix *= 3;
iy *= 3;
}
if (zigzag) {
if ((iy % 2) == 0) return (valid_points_mask[6 - iy] & (1 << (6 - ix)));
else return (valid_points_mask[6 - iy] & (1 << ix));
}
else {
return (valid_points_mask[6 - iy] & (1 << (6 - ix)));
}
} }
void mbl_single_point_interpolation(uint8_t x, uint8_t y, uint8_t meas_points) { void mbl_single_point_interpolation(uint8_t x, uint8_t y) {
//printf_P(PSTR("x = %d; y = %d \n"), x, y); //printf_P(PSTR("x = %d; y = %d \n"), x, y);
uint8_t count = 0; uint8_t count = 0;
float z = 0; float z = 0;
if (mbl_point_measurement_valid(x, y + 1, meas_points, false)) { z += mbl.z_values[y + 1][x]; /*printf_P(PSTR("x; y+1: Z = %f \n"), mbl.z_values[y + 1][x]);*/ count++; } if (mbl_point_measurement_valid(x, y + 1)) { z += mbl.z_values[y + 1][x]; /*printf_P(PSTR("x; y+1: Z = %f \n"), mbl.z_values[y + 1][x]);*/ count++; }
if (mbl_point_measurement_valid(x, y - 1, meas_points, false)) { z += mbl.z_values[y - 1][x]; /*printf_P(PSTR("x; y-1: Z = %f \n"), mbl.z_values[y - 1][x]);*/ count++; } if (mbl_point_measurement_valid(x, y - 1)) { z += mbl.z_values[y - 1][x]; /*printf_P(PSTR("x; y-1: Z = %f \n"), mbl.z_values[y - 1][x]);*/ count++; }
if (mbl_point_measurement_valid(x + 1, y, meas_points, false)) { z += mbl.z_values[y][x + 1]; /*printf_P(PSTR("x+1; y: Z = %f \n"), mbl.z_values[y][x + 1]);*/ count++; } if (mbl_point_measurement_valid(x + 1, y)) { z += mbl.z_values[y][x + 1]; /*printf_P(PSTR("x+1; y: Z = %f \n"), mbl.z_values[y][x + 1]);*/ count++; }
if (mbl_point_measurement_valid(x - 1, y, meas_points, false)) { z += mbl.z_values[y][x - 1]; /*printf_P(PSTR("x-1; y: Z = %f \n"), mbl.z_values[y][x - 1]);*/ count++; } if (mbl_point_measurement_valid(x - 1, y)) { z += mbl.z_values[y][x - 1]; /*printf_P(PSTR("x-1; y: Z = %f \n"), mbl.z_values[y][x - 1]);*/ count++; }
if(count != 0) mbl.z_values[y][x] = z / count; //if we have at least one valid point in surrounding area use average value, otherwise use inaccurately measured Z-coordinate if(count != 0) mbl.z_values[y][x] = z / count; //if we have at least one valid point in surrounding area use average value, otherwise use inaccurately measured Z-coordinate
//printf_P(PSTR("result: Z = %f \n\n"), mbl.z_values[y][x]); //printf_P(PSTR("result: Z = %f \n\n"), mbl.z_values[y][x]);
} }
void mbl_interpolation(uint8_t meas_points) { void mbl_magnet_elimination() {
for (uint8_t x = 0; x < meas_points; x++) { for (uint8_t y = 0; y < MESH_NUM_Y_POINTS; y++) {
for (uint8_t y = 0; y < meas_points; y++) { for (uint8_t x = 0; x < MESH_NUM_X_POINTS; x++) {
if (!mbl_point_measurement_valid(x, y, meas_points, false)) { if (!mbl_point_measurement_valid(x, y)) {
mbl_single_point_interpolation(x, y, meas_points); mbl_single_point_interpolation(x, y);
} }
} }
} }

View File

@ -21,8 +21,8 @@
#endif //not HEATBED_V2 #endif //not HEATBED_V2
#define BED_X(i, n) ((float)i * (BED_Xn - BED_X0) / (n - 1) + BED_X0) constexpr float x_mesh_density = (BED_Xn - BED_X0) / (MESH_NUM_X_POINTS - 1);
#define BED_Y(i, n) ((float)i * (BED_Yn - BED_Y0) / (n - 1) + BED_Y0) constexpr float y_mesh_density = (BED_Yn - BED_Y0) / (MESH_NUM_Y_POINTS - 1);
// Exact positions of the print head above the bed reference points, in the world coordinates. // Exact positions of the print head above the bed reference points, in the world coordinates.
// The world coordinates match the machine coordinates only in case, when the machine // The world coordinates match the machine coordinates only in case, when the machine
@ -145,6 +145,17 @@ inline bool world2machine_clamp(float &x, float &y)
machine2world(tmpx, tmpy, x, y); machine2world(tmpx, tmpy, x, y);
return clamped; return clamped;
} }
/// @brief For a given column on the mesh calculate the bed X coordinate
/// @param col column index on mesh
/// @return Bed X coordinate
float BED_X(const uint8_t col);
/// @brief For a given row on the mesh calculate the bed Y coordinate
/// @param row row index on mesh
/// @return Bed Y coordinate
float BED_Y(const uint8_t row);
/** /**
* @brief Bed skew and offest detection result * @brief Bed skew and offest detection result
* *
@ -203,6 +214,5 @@ extern void count_xyz_details(float (&distanceMin)[2]);
extern bool sample_z(); extern bool sample_z();
extern void mbl_settings_init(); extern void mbl_settings_init();
extern bool mbl_point_measurement_valid(uint8_t ix, uint8_t iy);
extern bool mbl_point_measurement_valid(uint8_t ix, uint8_t iy, uint8_t meas_points, bool zigzag); extern void mbl_magnet_elimination();
extern void mbl_interpolation(uint8_t meas_points);

View File

@ -6,23 +6,44 @@
mesh_bed_leveling mbl; mesh_bed_leveling mbl;
mesh_bed_leveling::mesh_bed_leveling() { reset(); }
void mesh_bed_leveling::reset() { void mesh_bed_leveling::reset() {
active = 0; active = 0;
memset(z_values, 0, sizeof(float) * MESH_NUM_X_POINTS * MESH_NUM_Y_POINTS); memset(z_values, 0, sizeof(z_values));
} }
static inline bool vec_undef(const float v[2]) float mesh_bed_leveling::get_z(float x, float y) {
{ int i, j;
const uint32_t *vx = (const uint32_t*)v; float s, t;
return vx[0] == 0x0FFFFFFFF || vx[1] == 0x0FFFFFFFF;
} i = int(floor((x - (BED_X0 + X_PROBE_OFFSET_FROM_EXTRUDER)) / x_mesh_density));
if (i < 0) {
i = 0;
s = (x - (BED_X0 + X_PROBE_OFFSET_FROM_EXTRUDER)) / x_mesh_density;
} else {
if (i > MESH_NUM_X_POINTS - 2) {
i = MESH_NUM_X_POINTS - 2;
}
s = (x - get_x(i)) / x_mesh_density;
}
#if MESH_NUM_X_POINTS>=5 && MESH_NUM_Y_POINTS>=5 && (MESH_NUM_X_POINTS&1)==1 && (MESH_NUM_Y_POINTS&1)==1 j = int(floor((y - (BED_Y0 + Y_PROBE_OFFSET_FROM_EXTRUDER)) / y_mesh_density));
if (j < 0) {
j = 0;
t = (y - (BED_Y0 + Y_PROBE_OFFSET_FROM_EXTRUDER)) / y_mesh_density;
} else {
if (j > MESH_NUM_Y_POINTS - 2) {
j = MESH_NUM_Y_POINTS - 2;
}
t = (y - get_y(j)) / y_mesh_density;
}
float si = 1.f-s;
float z0 = si * z_values[j ][i] + s * z_values[j ][i+1];
float z1 = si * z_values[j+1][i] + s * z_values[j+1][i+1];
return (1.f-t) * z0 + t * z1;
}
// Works for an odd number of MESH_NUM_X_POINTS and MESH_NUM_Y_POINTS // Works for an odd number of MESH_NUM_X_POINTS and MESH_NUM_Y_POINTS
// #define MBL_BILINEAR
void mesh_bed_leveling::upsample_3x3() void mesh_bed_leveling::upsample_3x3()
{ {
int idx0 = 0; int idx0 = 0;
@ -30,76 +51,53 @@ void mesh_bed_leveling::upsample_3x3()
int idx2 = MESH_NUM_X_POINTS - 1; int idx2 = MESH_NUM_X_POINTS - 1;
{ {
// First interpolate the points in X axis. // First interpolate the points in X axis.
static const float x0 = MESH_MIN_X; static const float x0 = (BED_X0 + X_PROBE_OFFSET_FROM_EXTRUDER);
static const float x1 = 0.5f * float(MESH_MIN_X + MESH_MAX_X); static const float x1 = 0.5f * float(BED_X0 + BED_Xn) + X_PROBE_OFFSET_FROM_EXTRUDER;
static const float x2 = MESH_MAX_X; static const float x2 = BED_Xn + X_PROBE_OFFSET_FROM_EXTRUDER;
for (int j = 0; j < 3; ++ j) { for (int j = 0; j < MESH_NUM_Y_POINTS; ++ j) {
// 1) Copy the source points to their new destination. // Interpolate the remaining values by Largrangian polynomials.
z_values[j][idx2] = z_values[j][2]; for (int i = 0; i < MESH_NUM_X_POINTS; ++ i) {
z_values[j][idx1] = z_values[j][1]; if (!isnan(z_values[j][i]))
// 2) Interpolate the remaining values by Largrangian polynomials.
for (int i = idx0 + 1; i < idx2; ++ i) {
if (i == idx1)
continue; continue;
float x = get_x(i); float x = get_x(i);
#ifdef MBL_BILINEAR
z_values[j][i] = (x < x1) ?
((z_values[j][idx0] * (x - x0) + z_values[j][idx1] * (x1 - x)) / (x1 - x0)) :
((z_values[j][idx1] * (x - x1) + z_values[j][idx2] * (x2 - x)) / (x2 - x1));
#else
z_values[j][i] = z_values[j][i] =
z_values[j][idx0] * (x - x1) * (x - x2) / ((x0 - x1) * (x0 - x2)) + z_values[j][idx0] * (x - x1) * (x - x2) / ((x0 - x1) * (x0 - x2)) +
z_values[j][idx1] * (x - x0) * (x - x2) / ((x1 - x0) * (x1 - x2)) + z_values[j][idx1] * (x - x0) * (x - x2) / ((x1 - x0) * (x1 - x2)) +
z_values[j][idx2] * (x - x0) * (x - x1) / ((x2 - x0) * (x2 - x1)); z_values[j][idx2] * (x - x0) * (x - x1) / ((x2 - x0) * (x2 - x1));
#endif
} }
} }
} }
{ {
// Second interpolate the points in Y axis. // Second interpolate the points in Y axis.
static const float y0 = MESH_MIN_Y; static const float y0 = (BED_Y0 + Y_PROBE_OFFSET_FROM_EXTRUDER);
static const float y1 = 0.5f * float(MESH_MIN_Y + MESH_MAX_Y); static const float y1 = 0.5f * float(BED_Y0 + BED_Yn) + Y_PROBE_OFFSET_FROM_EXTRUDER;
static const float y2 = MESH_MAX_Y; static const float y2 = BED_Yn + Y_PROBE_OFFSET_FROM_EXTRUDER;
for (int i = 0; i < MESH_NUM_X_POINTS; ++ i) { for (int i = 0; i < MESH_NUM_X_POINTS; ++ i) {
// 1) Copy the intermediate points to their new destination. // Interpolate the remaining values by Largrangian polynomials.
z_values[idx2][i] = z_values[2][i];
z_values[idx1][i] = z_values[1][i];
// 2) Interpolate the remaining values by Largrangian polynomials.
for (int j = 1; j + 1 < MESH_NUM_Y_POINTS; ++ j) { for (int j = 1; j + 1 < MESH_NUM_Y_POINTS; ++ j) {
if (j == idx1) if (!isnan(z_values[j][i]))
continue; continue;
float y = get_y(j); float y = get_y(j);
#ifdef MBL_BILINEAR
z_values[j][i] = (y < y1) ?
((z_values[idx0][i] * (y - y0) + z_values[idx1][i] * (y1 - y)) / (y1 - y0)) :
((z_values[idx1][i] * (y - y1) + z_values[idx2][i] * (y2 - y)) / (y2 - y1));
#else
z_values[j][i] = z_values[j][i] =
z_values[idx0][i] * (y - y1) * (y - y2) / ((y0 - y1) * (y0 - y2)) + z_values[idx0][i] * (y - y1) * (y - y2) / ((y0 - y1) * (y0 - y2)) +
z_values[idx1][i] * (y - y0) * (y - y2) / ((y1 - y0) * (y1 - y2)) + z_values[idx1][i] * (y - y0) * (y - y2) / ((y1 - y0) * (y1 - y2)) +
z_values[idx2][i] * (y - y0) * (y - y1) / ((y2 - y0) * (y2 - y1)); z_values[idx2][i] * (y - y0) * (y - y1) / ((y2 - y0) * (y2 - y1));
#endif
} }
} }
} }
/*
// Relax the non-measured points.
const float weight = 0.2f;
for (uint8_t iter = 0; iter < 20; ++ iter) {
for (int8_t j = 1; j < 6; ++ j) {
for (int8_t i = 1; i < 6; ++ i) {
if (i == 3 || j == 3)
continue;
if ((i % 3) == 0 && (j % 3) == 0)
continue;
float avg = 0.25f * (z_values[j][i-1]+z_values[j][i+1]+z_values[j-1][i]+z_values[j+1][i]);
z_values[j][i] = (1.f-weight)*z_values[j][i] + weight*avg;
}
}
}
*/
} }
#endif
void mesh_bed_leveling::print() {
SERIAL_PROTOCOLLNPGM("Num X,Y: " STRINGIFY(MESH_NUM_X_POINTS) "," STRINGIFY(MESH_NUM_Y_POINTS));
SERIAL_PROTOCOLLNPGM("Z search height: " STRINGIFY(MESH_HOME_Z_SEARCH));
SERIAL_PROTOCOLLNPGM("Measured points:");
for (uint8_t y = MESH_NUM_Y_POINTS; y-- > 0;) {
for (uint8_t x = 0; x < MESH_NUM_X_POINTS; x++) {
SERIAL_PROTOCOLPGM(" ");
SERIAL_PROTOCOL_F(z_values[y][x], 5);
}
SERIAL_PROTOCOLLN();
}
}
#endif // MESH_BED_LEVELING #endif // MESH_BED_LEVELING

View File

@ -1,118 +1,23 @@
#include "Marlin.h" #include "Marlin.h"
#include "mesh_bed_calibration.h"
#ifdef MESH_BED_LEVELING #ifdef MESH_BED_LEVELING
#define MEAS_NUM_X_DIST (float(MESH_MAX_X - MESH_MIN_X)/float(MESH_MEAS_NUM_X_POINTS - 1))
#define MEAS_NUM_Y_DIST (float(MESH_MAX_Y - MESH_MIN_Y)/float(MESH_MEAS_NUM_Y_POINTS - 1))
#define MESH_X_DIST (float(MESH_MAX_X - MESH_MIN_X)/float(MESH_NUM_X_POINTS - 1))
#define MESH_Y_DIST (float(MESH_MAX_Y - MESH_MIN_Y)/float(MESH_NUM_Y_POINTS - 1))
class mesh_bed_leveling { class mesh_bed_leveling {
public: public:
uint8_t active; uint8_t active;
float z_values[MESH_NUM_Y_POINTS][MESH_NUM_X_POINTS]; float z_values[MESH_NUM_Y_POINTS][MESH_NUM_X_POINTS];
mesh_bed_leveling(); mesh_bed_leveling() { reset(); }
void reset(); void reset();
#if MESH_NUM_X_POINTS>=5 && MESH_NUM_Y_POINTS>=5 && (MESH_NUM_X_POINTS&1)==1 && (MESH_NUM_Y_POINTS&1)==1 static float get_x(int i) { return BED_X(i) + X_PROBE_OFFSET_FROM_EXTRUDER; }
void upsample_3x3(); static float get_y(int i) { return BED_Y(i) + Y_PROBE_OFFSET_FROM_EXTRUDER; }
#endif float get_z(float x, float y);
static float get_x(int i) { return float(MESH_MIN_X) + float(MESH_X_DIST) * float(i); }
static float get_y(int i) { return float(MESH_MIN_Y) + float(MESH_Y_DIST) * float(i); }
void set_z(uint8_t ix, uint8_t iy, float z) { z_values[iy][ix] = z; } void set_z(uint8_t ix, uint8_t iy, float z) { z_values[iy][ix] = z; }
void upsample_3x3();
int select_x_index(float x) { void print();
int i = 1;
while (x > get_x(i) && i < MESH_NUM_X_POINTS - 1) i++;
return i - 1;
}
int select_y_index(float y) {
int i = 1;
while (y > get_y(i) && i < MESH_NUM_Y_POINTS - 1) i++;
return i - 1;
}
float get_z(float x, float y) {
int i, j;
float s, t;
#if MESH_NUM_X_POINTS==3 && MESH_NUM_Y_POINTS==3
#define MESH_MID_X (0.5f*(MESH_MIN_X+MESH_MAX_X))
#define MESH_MID_Y (0.5f*(MESH_MIN_Y+MESH_MAX_Y))
if (x < MESH_MID_X) {
i = 0;
s = (x - MESH_MIN_X) / MESH_X_DIST;
if (s > 1.f)
s = 1.f;
} else {
i = 1;
s = (x - MESH_MID_X) / MESH_X_DIST;
if (s < 0)
s = 0;
}
if (y < MESH_MID_Y) {
j = 0;
t = (y - MESH_MIN_Y) / MESH_Y_DIST;
if (t > 1.f)
t = 1.f;
} else {
j = 1;
t = (y - MESH_MID_Y) / MESH_Y_DIST;
if (t < 0)
t = 0;
}
#else
i = int(floor((x - MESH_MIN_X) / MESH_X_DIST));
if (i < 0) {
i = 0;
s = (x - MESH_MIN_X) / MESH_X_DIST;
if (s > 1.f)
s = 1.f;
}
else if (i > MESH_NUM_X_POINTS - 2) {
i = MESH_NUM_X_POINTS - 2;
s = (x - get_x(i)) / MESH_X_DIST;
if (s < 0)
s = 0;
} else {
s = (x - get_x(i)) / MESH_X_DIST;
if (s < 0)
s = 0;
else if (s > 1.f)
s = 1.f;
}
j = int(floor((y - MESH_MIN_Y) / MESH_Y_DIST));
if (j < 0) {
j = 0;
t = (y - MESH_MIN_Y) / MESH_Y_DIST;
if (t > 1.f)
t = 1.f;
} else if (j > MESH_NUM_Y_POINTS - 2) {
j = MESH_NUM_Y_POINTS - 2;
t = (y - get_y(j)) / MESH_Y_DIST;
if (t < 0)
t = 0;
} else {
t = (y - get_y(j)) / MESH_Y_DIST;
if (t < 0)
t = 0;
else if (t > 1.f)
t = 1.f;
}
#endif /* MESH_NUM_X_POINTS==3 && MESH_NUM_Y_POINTS==3 */
float si = 1.f-s;
float z0 = si * z_values[j ][i] + s * z_values[j ][i+1];
float z1 = si * z_values[j+1][i] + s * z_values[j+1][i+1];
return (1.f-t) * z0 + t * z1;
}
}; };
extern mesh_bed_leveling mbl; extern mesh_bed_leveling mbl;

View File

@ -81,8 +81,7 @@ void MMU2::StopKeepPowered() {
void MMU2::Tune() { void MMU2::Tune() {
switch (lastErrorCode) { switch (lastErrorCode) {
case ErrorCode::HOMING_SELECTOR_FAILED: case ErrorCode::HOMING_SELECTOR_FAILED:
case ErrorCode::HOMING_IDLER_FAILED: case ErrorCode::HOMING_IDLER_FAILED: {
{
// Prompt a menu for different values // Prompt a menu for different values
tuneIdlerStallguardThreshold(); tuneIdlerStallguardThreshold();
break; break;
@ -115,7 +114,7 @@ void MMU2::ResetX0() {
logic.ResetMMU(); // Send soft reset logic.ResetMMU(); // Send soft reset
} }
void MMU2::ResetX42(){ void MMU2::ResetX42() {
logic.ResetMMU(42); logic.ResetMMU(42);
} }
@ -196,7 +195,7 @@ void __attribute__((noinline)) MMU2::mmu_loop_inner(bool reportErrors) {
if (isErrorScreenRunning()) { if (isErrorScreenRunning()) {
// Call this every iteration to keep the knob rotation responsive // Call this every iteration to keep the knob rotation responsive
// This includes when mmu_loop is called within manage_response // This includes when mmu_loop is called within manage_response
ReportErrorHook((CommandInProgress)logic.CommandInProgress(), (uint16_t)lastErrorCode, uint8_t(lastErrorSource)); ReportErrorHook((CommandInProgress)logic.CommandInProgress(), lastErrorCode, uint8_t(lastErrorSource));
} }
} }
@ -204,7 +203,7 @@ void MMU2::CheckFINDARunout() {
// Check for FINDA filament runout // Check for FINDA filament runout
if (!FindaDetectsFilament() && check_fsensor()) { if (!FindaDetectsFilament() && check_fsensor()) {
SERIAL_ECHOLNPGM("FINDA filament runout!"); SERIAL_ECHOLNPGM("FINDA filament runout!");
stop_and_save_print_to_ram(0, 0); marlin_stop_and_save_print_to_ram();
restore_print_from_ram_and_continue(0); restore_print_from_ram_and_continue(0);
if (SpoolJoin::spooljoin.isSpoolJoinEnabled() && get_current_tool() != (uint8_t)FILAMENT_UNKNOWN){ // Can't auto if F=? if (SpoolJoin::spooljoin.isSpoolJoinEnabled() && get_current_tool() != (uint8_t)FILAMENT_UNKNOWN){ // Can't auto if F=?
enquecommand_front_P(PSTR("M600 AUTO")); // save print and run M600 command enquecommand_front_P(PSTR("M600 AUTO")); // save print and run M600 command
@ -218,10 +217,10 @@ struct ReportingRAII {
CommandInProgress cip; CommandInProgress cip;
explicit inline __attribute__((always_inline)) ReportingRAII(CommandInProgress cip) explicit inline __attribute__((always_inline)) ReportingRAII(CommandInProgress cip)
: cip(cip) { : cip(cip) {
BeginReport(cip, (uint16_t)ProgressCode::EngagingIdler); BeginReport(cip, ProgressCode::EngagingIdler);
} }
inline __attribute__((always_inline)) ~ReportingRAII() { inline __attribute__((always_inline)) ~ReportingRAII() {
EndReport(cip, (uint16_t)ProgressCode::OK); EndReport(cip, ProgressCode::OK);
} }
}; };
@ -237,11 +236,11 @@ bool MMU2::WaitForMMUReady() {
} }
} }
bool MMU2::RetryIfPossible(uint16_t ec) { bool MMU2::RetryIfPossible(ErrorCode ec) {
if (logic.RetryAttempts()) { if (logic.RetryAttempts()) {
SetButtonResponse(ButtonOperations::Retry); SetButtonResponse(ButtonOperations::Retry);
// check, that Retry is actually allowed on that operation // check, that Retry is actually allowed on that operation
if (ButtonAvailable(ec) != NoButton) { if (ButtonAvailable(ec) != Buttons::NoButton) {
logic.SetInAutoRetry(true); logic.SetInAutoRetry(true);
SERIAL_ECHOLNPGM("RetryButtonPressed"); SERIAL_ECHOLNPGM("RetryButtonPressed");
// We don't decrement until the button is acknowledged by the MMU. // We don't decrement until the button is acknowledged by the MMU.
@ -256,23 +255,13 @@ bool MMU2::RetryIfPossible(uint16_t ec) {
bool MMU2::VerifyFilamentEnteredPTFE() { bool MMU2::VerifyFilamentEnteredPTFE() {
planner_synchronize(); planner_synchronize();
if (WhereIsFilament() == FilamentState::NOT_PRESENT) if (WhereIsFilament() != FilamentState::AT_FSENSOR)
return false; return false;
uint8_t fsensorState = 0;
uint8_t fsensorStateLCD = 0;
uint8_t lcd_cursor_col = 0;
// MMU has finished its load, push the filament further by some defined constant length // MMU has finished its load, push the filament further by some defined constant length
// If the filament sensor reads 0 at any moment, then report FAILURE // If the filament sensor reads 0 at any moment, then report FAILURE
const float tryload_length = MMU2_CHECK_FILAMENT_PRESENCE_EXTRUSION_LENGTH - logic.ExtraLoadDistance();
const float delta_mm = MMU2_CHECK_FILAMENT_PRESENCE_EXTRUSION_LENGTH - logic.ExtraLoadDistance(); TryLoadUnloadReporter tlur(tryload_length);
// The total length is twice delta_mm. Divide that length by number of pixels
// available to get length per pixel.
// Note: Below is the reciprocal of (2 * delta_mm) / LCD_WIDTH [mm/pixel]
const float pixel_per_mm = 0.5f * float(LCD_WIDTH) / (delta_mm);
TryLoadUnloadProgressbarInit();
/* The position is a triangle wave /* The position is a triangle wave
// current position is not zero, it is an offset // current position is not zero, it is an offset
@ -284,7 +273,7 @@ bool MMU2::VerifyFilamentEnteredPTFE() {
// in the slope's sign or check the last machine position. // in the slope's sign or check the last machine position.
// y(x) // y(x)
// ▲ // ▲
// │ ^◄────────── delta_mm + current_position // │ ^◄────────── tryload_length + current_position
// machine / \ // machine / \
// position │ / \◄────────── stepper_position_mm + current_position // position │ / \◄────────── stepper_position_mm + current_position
// (mm) / \ // (mm) / \
@ -295,42 +284,24 @@ bool MMU2::VerifyFilamentEnteredPTFE() {
// pixel # // pixel #
*/ */
bool filament_inserted = true; // expect success
// Pixel index will go from 0 to 10, then back from 10 to 0 // Pixel index will go from 0 to 10, then back from 10 to 0
// The change in this number is used to indicate a new pixel // The change in this number is used to indicate a new pixel
// should be drawn on the display // should be drawn on the display
uint8_t dpixel1 = 0;
uint8_t dpixel0 = 0;
for (uint8_t move = 0; move < 2; move++) { for (uint8_t move = 0; move < 2; move++) {
MoveE(move == 0 ? delta_mm : -delta_mm, MMU2_VERIFY_LOAD_TO_NOZZLE_FEED_RATE); extruder_move(move == 0 ? tryload_length : -tryload_length, MMU2_VERIFY_LOAD_TO_NOZZLE_FEED_RATE);
while (planner_any_moves()) { while (planner_any_moves()) {
// Wait for move to finish and monitor the fsensor the entire time filament_inserted = filament_inserted && (WhereIsFilament() == FilamentState::AT_FSENSOR);
// A single 0 reading will set the bit. tlur.Progress(filament_inserted);
fsensorStateLCD |= (WhereIsFilament() == FilamentState::NOT_PRESENT);
fsensorState |= fsensorStateLCD; // No need to do the above comparison twice, just bitwise OR
// Always round up, you can only have 'whole' pixels. (floor is also an option)
dpixel1 = ceil((stepper_get_machine_position_E_mm() - planner_get_current_position_E()) * pixel_per_mm);
if (dpixel1 - dpixel0) {
dpixel0 = dpixel1;
if (lcd_cursor_col > (LCD_WIDTH - 1)) lcd_cursor_col = LCD_WIDTH - 1;
TryLoadUnloadProgressbar(lcd_cursor_col++, fsensorStateLCD);
fsensorStateLCD = 0; // Clear temporary bit
}
safe_delay_keep_alive(0); safe_delay_keep_alive(0);
} }
} }
Disable_E0(); Disable_E0();
TryLoadUnloadProgressbarEcho(); if (!filament_inserted) {
TryLoadUnloadProgressbarDeinit();
if (fsensorState) {
IncrementLoadFails(); IncrementLoadFails();
return false;
} else {
// else, happy printing! :)
return true;
} }
tlur.DumpToSerial();
return filament_inserted;
} }
bool MMU2::ToolChangeCommonOnce(uint8_t slot) { bool MMU2::ToolChangeCommonOnce(uint8_t slot) {
@ -355,8 +326,6 @@ bool MMU2::ToolChangeCommonOnce(uint8_t slot) {
// but honestly - if the MMU restarts during every toolchange, // but honestly - if the MMU restarts during every toolchange,
// something else is seriously broken and stopping a print is probably our best option. // something else is seriously broken and stopping a print is probably our best option.
} }
// reset current position to whatever the planner thinks it is
planner_set_current_position_E(planner_get_current_position_E());
if (VerifyFilamentEnteredPTFE()) { if (VerifyFilamentEnteredPTFE()) {
return true; // success return true; // success
} else { // Prepare a retry attempt } else { // Prepare a retry attempt
@ -455,7 +424,7 @@ uint8_t MMU2::get_tool_change_tool() const {
bool MMU2::set_filament_type(uint8_t /*slot*/, uint8_t /*type*/) { bool MMU2::set_filament_type(uint8_t /*slot*/, uint8_t /*type*/) {
if (!WaitForMMUReady()) if (!WaitForMMUReady())
return false; return false;
// @@TODO - this is not supported in the new MMU yet // @@TODO - this is not supported in the new MMU yet
// slot = slot; // @@TODO // slot = slot; // @@TODO
// type = type; // @@TODO // type = type; // @@TODO
@ -496,9 +465,11 @@ bool MMU2::unload() {
WaitForHotendTargetTempBeep(); WaitForHotendTargetTempBeep();
ReportingRAII rep(CommandInProgress::UnloadFilament); {
UnloadInner(); ReportingRAII rep(CommandInProgress::UnloadFilament);
UnloadInner();
}
ScreenUpdateEnable();
return true; return true;
} }
@ -526,10 +497,10 @@ bool MMU2::cut_filament(uint8_t slot, bool enableFullScreenMsg /*= true*/) {
ReportingRAII rep(CommandInProgress::CutFilament); ReportingRAII rep(CommandInProgress::CutFilament);
CutFilamentInner(slot); CutFilamentInner(slot);
extruder = MMU2_NO_TOOL;
tool_change_extruder = MMU2_NO_TOOL;
MakeSound(SoundType::Confirm);
} }
extruder = MMU2_NO_TOOL;
tool_change_extruder = MMU2_NO_TOOL;
MakeSound(SoundType::Confirm);
ScreenUpdateEnable(); ScreenUpdateEnable();
return true; return true;
} }
@ -548,20 +519,18 @@ bool MMU2::load_filament(uint8_t slot) {
return false; return false;
FullScreenMsgLoad(slot); FullScreenMsgLoad(slot);
{
ReportingRAII rep(CommandInProgress::LoadFilament); ReportingRAII rep(CommandInProgress::LoadFilament);
for (;;) { for (;;) {
Disable_E0(); Disable_E0();
logic.LoadFilament(slot); logic.LoadFilament(slot);
if (manage_response(false, false)) if (manage_response(false, false))
break; break;
IncrementMMUFails(); IncrementMMUFails();
}
MakeSound(SoundType::Confirm);
} }
MakeSound(SoundType::Confirm);
ScreenUpdateEnable(); ScreenUpdateEnable();
return true; return true;
} }
@ -611,10 +580,11 @@ bool MMU2::eject_filament(uint8_t slot, bool enableFullScreenMsg /* = true */) {
break; break;
IncrementMMUFails(); IncrementMMUFails();
} }
extruder = MMU2_NO_TOOL;
tool_change_extruder = MMU2_NO_TOOL;
MakeSound(Confirm);
} }
extruder = MMU2_NO_TOOL; ScreenUpdateEnable();
tool_change_extruder = MMU2_NO_TOOL;
MakeSound(Confirm);
return true; return true;
} }
@ -647,14 +617,14 @@ void MMU2::SaveAndPark(bool move_axes) {
// In case a power panic happens while waiting for the user // In case a power panic happens while waiting for the user
// take a partial back up of print state into RAM (current position, etc.) // take a partial back up of print state into RAM (current position, etc.)
refresh_print_state_in_ram(); marlin_refresh_print_state_in_ram();
if (move_axes) { if (move_axes) {
mmu_print_saved |= SavedState::ParkExtruder; mmu_print_saved |= SavedState::ParkExtruder;
resume_position = planner_current_position(); // save current pos resume_position = planner_current_position(); // save current pos
// lift Z // lift Z
MoveRaiseZ(MMU_ERR_Z_PAUSE_LIFT); move_raise_z(MMU_ERR_Z_PAUSE_LIFT);
// move XY aside // move XY aside
if (all_axes_homed()) { if (all_axes_homed()) {
@ -706,14 +676,14 @@ void MMU2::ResumeUnpark() {
// From this point forward, power panic should not use // From this point forward, power panic should not use
// the partial backup in RAM since the extruder is no // the partial backup in RAM since the extruder is no
// longer in parking position // longer in parking position
clear_print_state_in_ram(); marlin_clear_print_state_in_ram();
mmu_print_saved &= ~(SavedState::ParkExtruder); mmu_print_saved &= ~(SavedState::ParkExtruder);
} }
} }
void MMU2::CheckUserInput() { void MMU2::CheckUserInput() {
auto btn = ButtonPressed((uint16_t)lastErrorCode); auto btn = ButtonPressed(lastErrorCode);
// Was a button pressed on the MMU itself instead of the LCD? // Was a button pressed on the MMU itself instead of the LCD?
if (btn == Buttons::NoButton && lastButton != Buttons::NoButton) { if (btn == Buttons::NoButton && lastButton != Buttons::NoButton) {
@ -733,16 +703,16 @@ void MMU2::CheckUserInput() {
} }
switch (btn) { switch (btn) {
case Left: case Buttons::Left:
case Middle: case Buttons::Middle:
case Right: case Buttons::Right:
SERIAL_ECHOPGM("CheckUserInput-btnLMR "); SERIAL_ECHOPGM("CheckUserInput-btnLMR ");
SERIAL_ECHOLN(btn); SERIAL_ECHOLN(buttons_to_uint8t(btn));
ResumeHotendTemp(); // Recover the hotend temp before we attempt to do anything else... ResumeHotendTemp(); // Recover the hotend temp before we attempt to do anything else...
if (mmu2.MMULastErrorSource() == MMU2::ErrorSourceMMU) { if (mmu2.MMULastErrorSource() == MMU2::ErrorSourceMMU) {
// Do not send a button to the MMU unless the MMU is in error state // Do not send a button to the MMU unless the MMU is in error state
Button(btn); Button(buttons_to_uint8t(btn));
} }
// A quick hack: for specific error codes move the E-motor every time. // A quick hack: for specific error codes move the E-motor every time.
@ -757,22 +727,22 @@ void MMU2::CheckUserInput() {
break; break;
} }
break; break;
case TuneMMU: case Buttons::TuneMMU:
Tune(); Tune();
break; break;
case Load: case Buttons::Load:
case Eject: case Buttons::Eject:
// High level operation // High level operation
setPrinterButtonOperation(btn); SetPrinterButtonOperation(btn);
break; break;
case ResetMMU: case Buttons::ResetMMU:
Reset(ResetPin); // we cannot do power cycle on the MK3 Reset(ResetPin); // we cannot do power cycle on the MK3
// ... but mmu2_power.cpp knows this and triggers a soft-reset instead. // ... but mmu2_power.cpp knows this and triggers a soft-reset instead.
break; break;
case DisableMMU: case Buttons::DisableMMU:
Stop(); // Poweroff handles updating the EEPROM shutoff. Stop(); // Poweroff handles updating the EEPROM shutoff.
break; break;
case StopPrint: case Buttons::StopPrint:
// @@TODO not sure if we shall handle this high level operation at this spot // @@TODO not sure if we shall handle this high level operation at this spot
break; break;
default: default:
@ -929,8 +899,8 @@ void MMU2::execute_extruder_sequence(const E_Step *sequence, uint8_t steps) {
planner_synchronize(); planner_synchronize();
const E_Step *step = sequence; const E_Step *step = sequence;
for (uint8_t i = steps; i ; --i) { for (uint8_t i = steps; i > 0; --i) {
MoveE(pgm_read_float(&(step->extrude)), pgm_read_float(&(step->feedRate))); extruder_move(pgm_read_float(&(step->extrude)), pgm_read_float(&(step->feedRate)));
step++; step++;
} }
planner_synchronize(); // it looks like it's better to sync the moves at the end - smoother move (if the sequence is not too long). planner_synchronize(); // it looks like it's better to sync the moves at the end - smoother move (if the sequence is not too long).
@ -976,12 +946,13 @@ void MMU2::ReportError(ErrorCode ec, ErrorSource res) {
if (ec != lastErrorCode) { // deduplicate: only report changes in error codes into the log if (ec != lastErrorCode) { // deduplicate: only report changes in error codes into the log
lastErrorCode = ec; lastErrorCode = ec;
lastErrorSource = res; lastErrorSource = res;
LogErrorEvent_P(_O(PrusaErrorTitle(PrusaErrorCodeIndex((uint16_t)ec)))); LogErrorEvent_P(_O(PrusaErrorTitle(PrusaErrorCodeIndex(ec))));
if (ec != ErrorCode::OK && ec != ErrorCode::FILAMENT_EJECTED && ec != ErrorCode::FILAMENT_CHANGE) { if (ec != ErrorCode::OK && ec != ErrorCode::FILAMENT_EJECTED && ec != ErrorCode::FILAMENT_CHANGE) {
IncrementMMUFails(); IncrementMMUFails();
// check if it is a "power" failure - we consider TMC-related errors as power failures // check if it is a "power" failure - we consider TMC-related errors as power failures
// clang-format off
static constexpr uint16_t tmcMask = static constexpr uint16_t tmcMask =
( (uint16_t)ErrorCode::TMC_IOIN_MISMATCH ( (uint16_t)ErrorCode::TMC_IOIN_MISMATCH
| (uint16_t)ErrorCode::TMC_RESET | (uint16_t)ErrorCode::TMC_RESET
@ -990,6 +961,7 @@ void MMU2::ReportError(ErrorCode ec, ErrorSource res) {
| (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN | (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN
| (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR | (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR
| (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION ) & 0x7fffU; // skip the top bit | (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION ) & 0x7fffU; // skip the top bit
// clang-format on
static_assert(tmcMask == 0x7e00); // just make sure we fail compilation if any of the TMC error codes change static_assert(tmcMask == 0x7e00); // just make sure we fail compilation if any of the TMC error codes change
if ((uint16_t)ec & tmcMask) { // @@TODO can be optimized to uint8_t operation if ((uint16_t)ec & tmcMask) { // @@TODO can be optimized to uint8_t operation
@ -999,11 +971,11 @@ void MMU2::ReportError(ErrorCode ec, ErrorSource res) {
} }
} }
if (!mmu2.RetryIfPossible((uint16_t)ec)) { if (!mmu2.RetryIfPossible(ec)) {
// If retry attempts are all used up // If retry attempts are all used up
// or if 'Retry' operation is not available // or if 'Retry' operation is not available
// raise the MMU error sceen and wait for user input // raise the MMU error screen and wait for user input
ReportErrorHook((CommandInProgress)logic.CommandInProgress(), (uint16_t)ec, uint8_t(lastErrorSource)); ReportErrorHook((CommandInProgress)logic.CommandInProgress(), ec, uint8_t(lastErrorSource));
} }
static_assert(mmu2Magic[0] == 'M' static_assert(mmu2Magic[0] == 'M'
@ -1016,8 +988,8 @@ void MMU2::ReportError(ErrorCode ec, ErrorSource res) {
} }
void MMU2::ReportProgress(ProgressCode pc) { void MMU2::ReportProgress(ProgressCode pc) {
ReportProgressHook((CommandInProgress)logic.CommandInProgress(), (uint16_t)pc); ReportProgressHook((CommandInProgress)logic.CommandInProgress(), pc);
LogEchoEvent_P(_O(ProgressCodeToText((uint16_t)pc))); LogEchoEvent_P(_O(ProgressCodeToText(pc)));
} }
void MMU2::OnMMUProgressMsg(ProgressCode pc) { void MMU2::OnMMUProgressMsg(ProgressCode pc) {
@ -1058,7 +1030,7 @@ void MMU2::OnMMUProgressMsgChanged(ProgressCode pc) {
} }
void __attribute__((noinline)) MMU2::HelpUnloadToFinda() { void __attribute__((noinline)) MMU2::HelpUnloadToFinda() {
MoveE(-MMU2_RETRY_UNLOAD_TO_FINDA_LENGTH, MMU2_RETRY_UNLOAD_TO_FINDA_FEED_RATE); extruder_move(-MMU2_RETRY_UNLOAD_TO_FINDA_LENGTH, MMU2_RETRY_UNLOAD_TO_FINDA_FEED_RATE);
} }
void MMU2::OnMMUProgressMsgSame(ProgressCode pc) { void MMU2::OnMMUProgressMsgSame(ProgressCode pc) {
@ -1089,7 +1061,7 @@ void MMU2::OnMMUProgressMsgSame(ProgressCode pc) {
// After the MMU knows the FSENSOR is triggered it will: // After the MMU knows the FSENSOR is triggered it will:
// 1. Push the filament by additional 30mm (see fsensorToNozzle) // 1. Push the filament by additional 30mm (see fsensorToNozzle)
// 2. Disengage the idler and push another 2mm. // 2. Disengage the idler and push another 2mm.
MoveE(logic.ExtraLoadDistance() + 2, logic.PulleySlowFeedRate()); extruder_move(logic.ExtraLoadDistance() + 2, logic.PulleySlowFeedRate());
break; break;
case FilamentState::NOT_PRESENT: case FilamentState::NOT_PRESENT:
// fsensor not triggered, continue moving extruder // fsensor not triggered, continue moving extruder
@ -1099,7 +1071,7 @@ void MMU2::OnMMUProgressMsgSame(ProgressCode pc) {
// than 450mm because the firmware will ignore too long extrusions // than 450mm because the firmware will ignore too long extrusions
// for safety reasons. See PREVENT_LENGTHY_EXTRUDE. // for safety reasons. See PREVENT_LENGTHY_EXTRUDE.
// Use 350mm to be safely away from the prevention threshold // Use 350mm to be safely away from the prevention threshold
MoveE(350.0f, logic.PulleySlowFeedRate()); extruder_move(350.0f, logic.PulleySlowFeedRate());
} }
break; break;
default: default:

View File

@ -5,14 +5,11 @@
#include "mmu2_marlin.h" #include "mmu2_marlin.h"
#ifdef __AVR__ #ifdef __AVR__
#include "mmu2_protocol_logic.h" #include "mmu2_protocol_logic.h"
typedef float feedRate_t; typedef float feedRate_t;
#else #else
#include "protocol_logic.h" #include "protocol_logic.h"
#include "../../Marlin/src/core/macros.h"
#include "../../Marlin/src/core/types.h"
#include <atomic> #include <atomic>
#endif #endif
@ -48,9 +45,9 @@ public:
/// Different levels of resetting the MMU /// Different levels of resetting the MMU
enum ResetForm : uint8_t { enum ResetForm : uint8_t {
Software = 0, ///< sends a X0 command into the MMU, the MMU will watchdog-reset itself Software = 0, ///< sends a X0 command into the MMU, the MMU will watchdog-reset itself
ResetPin = 1, ///< trigger the reset pin of the MMU ResetPin = 1, ///< trigger the reset pin of the MMU
CutThePower = 2, ///< power off and power on (that includes +5V and +24V power lines) CutThePower = 2, ///< power off and power on (that includes +5V and +24V power lines)
EraseEEPROM = 42, ///< erase MMU EEPROM and then perform a software reset EraseEEPROM = 42, ///< erase MMU EEPROM and then perform a software reset
}; };
@ -182,7 +179,7 @@ public:
/// Automagically "press" a Retry button if we have any retry attempts left /// Automagically "press" a Retry button if we have any retry attempts left
/// @param ec ErrorCode enum value /// @param ec ErrorCode enum value
/// @returns true if auto-retry is ongoing, false when retry is unavailable or retry attempts are all used up /// @returns true if auto-retry is ongoing, false when retry is unavailable or retry attempts are all used up
bool RetryIfPossible(uint16_t ec); bool RetryIfPossible(ErrorCode ec);
/// @return count for toolchange in current print /// @return count for toolchange in current print
inline uint16_t ToolChangeCounter() const { return toolchange_counter; }; inline uint16_t ToolChangeCounter() const { return toolchange_counter; };
@ -201,9 +198,9 @@ public:
}; };
inline void InvokeErrorScreen(ErrorCode ec) { inline void InvokeErrorScreen(ErrorCode ec) {
// The printer may not raise an error when the MMU is busy // The printer may not raise an error when the MMU is busy
if ( !logic.CommandInProgress() // MMU must not be busy if (!logic.CommandInProgress() // MMU must not be busy
&& MMUCurrentErrorCode() == ErrorCode::OK // The protocol must not be in error state && MMUCurrentErrorCode() == ErrorCode::OK // The protocol must not be in error state
&& lastErrorCode != ec) // The error code is not a duplicate && lastErrorCode != ec) // The error code is not a duplicate
{ {
ReportError(ec, ErrorSource::ErrorSourcePrinter); ReportError(ec, ErrorSource::ErrorSourcePrinter);
} }
@ -217,21 +214,23 @@ public:
/// @brief Queue a button operation which the printer can act upon /// @brief Queue a button operation which the printer can act upon
/// @param btn Button operation /// @param btn Button operation
inline void setPrinterButtonOperation(Buttons btn) { inline void SetPrinterButtonOperation(Buttons btn) {
printerButtonOperation = btn; printerButtonOperation = btn;
} }
/// @brief Get the printer button operation /// @brief Get the printer button operation
/// @return currently set printer button operation, it can be NoButton if nothing is queued /// @return currently set printer button operation, it can be NoButton if nothing is queued
inline Buttons getPrinterButtonOperation() { inline Buttons GetPrinterButtonOperation() {
return printerButtonOperation; return printerButtonOperation;
} }
inline void clearPrinterButtonOperation() { inline void ClearPrinterButtonOperation() {
printerButtonOperation = Buttons::NoButton; printerButtonOperation = Buttons::NoButton;
} }
#ifndef UNITTEST
private: private:
#endif
/// Perform software self-reset of the MMU (sends an X0 command) /// Perform software self-reset of the MMU (sends an X0 command)
void ResetX0(); void ResetX0();
@ -279,6 +278,11 @@ private:
/// Responds to a change of MMU's progress /// Responds to a change of MMU's progress
/// - plans additional steps, e.g. starts the E-motor after fsensor trigger /// - plans additional steps, e.g. starts the E-motor after fsensor trigger
/// The function is quite complex, because it needs to handle asynchronnous
/// progress and error reports coming from the MMU without an explicit command
/// - typically after MMU's start or after some HW issue on the MMU.
/// It must ensure, that calls to @ref ReportProgress and/or @ref ReportError are
/// only executed after @ref BeginReport has been called first.
void OnMMUProgressMsg(ProgressCode pc); void OnMMUProgressMsg(ProgressCode pc);
/// Progress code changed - act accordingly /// Progress code changed - act accordingly
void OnMMUProgressMsgChanged(ProgressCode pc); void OnMMUProgressMsgChanged(ProgressCode pc);

View File

@ -2,8 +2,8 @@
#include <stdint.h> #include <stdint.h>
// Helper macros to parse the operations from Btns() // Helper macros to parse the operations from Btns()
#define BUTTON_OP_RIGHT(X) ( ( X & 0xF0 ) >> 4 ) #define BUTTON_OP_RIGHT(X) ((X & 0xF0) >> 4)
#define BUTTON_OP_MIDDLE(X) ( X & 0x0F ) #define BUTTON_OP_MIDDLE(X) (X & 0x0F)
namespace MMU2 { namespace MMU2 {
@ -23,11 +23,11 @@ enum class ButtonOperations : uint8_t {
}; };
/// Button codes + extended actions performed on the printer's side /// Button codes + extended actions performed on the printer's side
enum Buttons : uint8_t { enum class Buttons : uint_least8_t {
Right = 0, Right = 0,
Middle, Middle,
Left, Left,
// performed on the printer's side // performed on the printer's side
ResetMMU, ResetMMU,
Load, Load,
@ -35,9 +35,12 @@ enum Buttons : uint8_t {
StopPrint, StopPrint,
DisableMMU, DisableMMU,
TuneMMU, // Printer changes MMU register value TuneMMU, // Printer changes MMU register value
NoButton = 0xff // shall be kept last NoButton = 0xff // shall be kept last
}; };
constexpr uint_least8_t buttons_to_uint8t(Buttons b) {
return static_cast<uint8_t>(b);
}
} // namespace MMU2 } // namespace MMU2

View File

@ -1,8 +1,10 @@
#pragma once #pragma once
namespace MMU2 {
// Register map for MMU // Register map for MMU
enum class Register : uint8_t enum class Register : uint8_t {
{
Project_Major = 0x00, Project_Major = 0x00,
Project_Minor = 0x01, Project_Minor = 0x01,
Project_Revision = 0x02, Project_Revision = 0x02,
@ -38,3 +40,5 @@ enum class Register : uint8_t
Set_Get_Idler_iRun = 0x20, Set_Get_Idler_iRun = 0x20,
Reserved = 0x21, Reserved = 0x21,
}; };
} // namespace MMU2

View File

@ -2,12 +2,12 @@
#include "mmu2_crc.h" #include "mmu2_crc.h"
#ifdef __AVR__ #ifdef __AVR__
#include <util/crc16.h> #include <util/crc16.h>
#endif #endif
namespace modules { namespace modules {
// clang-format off
namespace crc { namespace crc {
#ifdef __AVR__ #ifdef __AVR__
uint8_t CRC8::CCITT_update(uint8_t crc, uint8_t b) { uint8_t CRC8::CCITT_update(uint8_t crc, uint8_t b) {
return _crc8_ccitt_update(crc, b); return _crc8_ccitt_update(crc, b);
@ -17,6 +17,6 @@ uint8_t CRC8::CCITT_update(uint8_t crc, uint8_t b) {
return CCITT_updateCX(crc, b); return CCITT_updateCX(crc, b);
} }
#endif #endif
} // namespace crc } // namespace crc
// clang-format on
} // namespace modules } // namespace modules

View File

@ -4,6 +4,9 @@
namespace modules { namespace modules {
// clang-format off
// prevent silly indenting of the whole file
/// Contains all the necessary functions for computation of CRC /// Contains all the necessary functions for computation of CRC
namespace crc { namespace crc {
@ -40,4 +43,6 @@ public:
} // namespace crc } // namespace crc
// clang-format on
} // namespace modules } // namespace modules

View File

@ -35,116 +35,125 @@ static_assert( FindErrorIndex(ERR_MECHANICAL_FINDA_FILAMENT_STUCK) == 1);
static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER) == 2); static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER) == 2);
static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_FILAMENT_STUCK) == 3); static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_FILAMENT_STUCK) == 3);
uint8_t PrusaErrorCodeIndex(uint16_t ec) { constexpr ErrorCode operator&(ErrorCode a, ErrorCode b){
return (ErrorCode)((uint16_t)a & (uint16_t)b);
}
constexpr bool ContainsBit(ErrorCode ec, ErrorCode mask){
return (uint16_t)ec & (uint16_t)mask;
}
uint8_t PrusaErrorCodeIndex(ErrorCode ec) {
switch (ec) { switch (ec) {
case (uint16_t)ErrorCode::FINDA_DIDNT_SWITCH_ON: case ErrorCode::FINDA_DIDNT_SWITCH_ON:
return FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_TRIGGER); return FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_TRIGGER);
case (uint16_t)ErrorCode::FINDA_DIDNT_SWITCH_OFF: case ErrorCode::FINDA_DIDNT_SWITCH_OFF:
return FindErrorIndex(ERR_MECHANICAL_FINDA_FILAMENT_STUCK); return FindErrorIndex(ERR_MECHANICAL_FINDA_FILAMENT_STUCK);
case (uint16_t)ErrorCode::FSENSOR_DIDNT_SWITCH_ON: case ErrorCode::FSENSOR_DIDNT_SWITCH_ON:
return FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER); return FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER);
case (uint16_t)ErrorCode::FSENSOR_DIDNT_SWITCH_OFF: case ErrorCode::FSENSOR_DIDNT_SWITCH_OFF:
return FindErrorIndex(ERR_MECHANICAL_FSENSOR_FILAMENT_STUCK); return FindErrorIndex(ERR_MECHANICAL_FSENSOR_FILAMENT_STUCK);
case (uint16_t)ErrorCode::FSENSOR_TOO_EARLY: case ErrorCode::FSENSOR_TOO_EARLY:
return FindErrorIndex(ERR_MECHANICAL_FSENSOR_TOO_EARLY); return FindErrorIndex(ERR_MECHANICAL_FSENSOR_TOO_EARLY);
case (uint16_t)ErrorCode::FINDA_FLICKERS: case ErrorCode::FINDA_FLICKERS:
return FindErrorIndex(ERR_MECHANICAL_INSPECT_FINDA); return FindErrorIndex(ERR_MECHANICAL_INSPECT_FINDA);
case (uint16_t)ErrorCode::LOAD_TO_EXTRUDER_FAILED: case ErrorCode::LOAD_TO_EXTRUDER_FAILED:
return FindErrorIndex(ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED); return FindErrorIndex(ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED);
case (uint16_t)ErrorCode::FILAMENT_EJECTED: case ErrorCode::FILAMENT_EJECTED:
return FindErrorIndex(ERR_SYSTEM_FILAMENT_EJECTED); return FindErrorIndex(ERR_SYSTEM_FILAMENT_EJECTED);
case (uint16_t)ErrorCode::FILAMENT_CHANGE: case ErrorCode::FILAMENT_CHANGE:
return FindErrorIndex(ERR_SYSTEM_FILAMENT_CHANGE); return FindErrorIndex(ERR_SYSTEM_FILAMENT_CHANGE);
case (uint16_t)ErrorCode::STALLED_PULLEY: case ErrorCode::STALLED_PULLEY:
case (uint16_t)ErrorCode::MOVE_PULLEY_FAILED: case ErrorCode::MOVE_PULLEY_FAILED:
return FindErrorIndex(ERR_MECHANICAL_PULLEY_CANNOT_MOVE); return FindErrorIndex(ERR_MECHANICAL_PULLEY_CANNOT_MOVE);
case (uint16_t)ErrorCode::HOMING_SELECTOR_FAILED: case ErrorCode::HOMING_SELECTOR_FAILED:
return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_HOME); return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_HOME);
case (uint16_t)ErrorCode::MOVE_SELECTOR_FAILED: case ErrorCode::MOVE_SELECTOR_FAILED:
return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_MOVE); return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_MOVE);
case (uint16_t)ErrorCode::HOMING_IDLER_FAILED: case ErrorCode::HOMING_IDLER_FAILED:
return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_HOME); return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_HOME);
case (uint16_t)ErrorCode::MOVE_IDLER_FAILED: case ErrorCode::MOVE_IDLER_FAILED:
return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_MOVE); return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_MOVE);
case (uint16_t)ErrorCode::MMU_NOT_RESPONDING: case ErrorCode::MMU_NOT_RESPONDING:
return FindErrorIndex(ERR_CONNECT_MMU_NOT_RESPONDING); return FindErrorIndex(ERR_CONNECT_MMU_NOT_RESPONDING);
case (uint16_t)ErrorCode::PROTOCOL_ERROR: case ErrorCode::PROTOCOL_ERROR:
return FindErrorIndex(ERR_CONNECT_COMMUNICATION_ERROR); return FindErrorIndex(ERR_CONNECT_COMMUNICATION_ERROR);
case (uint16_t)ErrorCode::FILAMENT_ALREADY_LOADED: case ErrorCode::FILAMENT_ALREADY_LOADED:
return FindErrorIndex(ERR_SYSTEM_FILAMENT_ALREADY_LOADED); return FindErrorIndex(ERR_SYSTEM_FILAMENT_ALREADY_LOADED);
case (uint16_t)ErrorCode::INVALID_TOOL: case ErrorCode::INVALID_TOOL:
return FindErrorIndex(ERR_SYSTEM_INVALID_TOOL); return FindErrorIndex(ERR_SYSTEM_INVALID_TOOL);
case (uint16_t)ErrorCode::QUEUE_FULL: case ErrorCode::QUEUE_FULL:
return FindErrorIndex(ERR_SYSTEM_QUEUE_FULL); return FindErrorIndex(ERR_SYSTEM_QUEUE_FULL);
case (uint16_t)ErrorCode::VERSION_MISMATCH: case ErrorCode::VERSION_MISMATCH:
return FindErrorIndex(ERR_SYSTEM_FW_UPDATE_NEEDED); return FindErrorIndex(ERR_SYSTEM_FW_UPDATE_NEEDED);
case (uint16_t)ErrorCode::INTERNAL: case ErrorCode::INTERNAL:
return FindErrorIndex(ERR_SYSTEM_FW_RUNTIME_ERROR); return FindErrorIndex(ERR_SYSTEM_FW_RUNTIME_ERROR);
case (uint16_t)ErrorCode::FINDA_VS_EEPROM_DISREPANCY: case ErrorCode::FINDA_VS_EEPROM_DISREPANCY:
return FindErrorIndex(ERR_SYSTEM_UNLOAD_MANUALLY); return FindErrorIndex(ERR_SYSTEM_UNLOAD_MANUALLY);
case (uint16_t)ErrorCode::MCU_UNDERVOLTAGE_VCC: case ErrorCode::MCU_UNDERVOLTAGE_VCC:
return FindErrorIndex(ERR_ELECTRICAL_MMU_MCU_ERROR); return FindErrorIndex(ERR_ELECTRICAL_MMU_MCU_ERROR);
default: break;
} }
// Electrical issues which can be detected somehow. // Electrical issues which can be detected somehow.
// Need to be placed before TMC-related errors in order to process couples of error bits between single ones // Need to be placed before TMC-related errors in order to process couples of error bits between single ones
// and to keep the code size down. // and to keep the code size down.
if (ec & (uint16_t)ErrorCode::TMC_PULLEY_BIT) { if (ContainsBit(ec, ErrorCode::TMC_PULLEY_BIT)) {
if ((ec & (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) if ((ec & ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION)
return FindErrorIndex(ERR_ELECTRICAL_MMU_PULLEY_SELFTEST_FAILED); return FindErrorIndex(ERR_ELECTRICAL_MMU_PULLEY_SELFTEST_FAILED);
} else if (ec & (uint16_t)ErrorCode::TMC_SELECTOR_BIT) { } else if (ContainsBit(ec, ErrorCode::TMC_SELECTOR_BIT)) {
if ((ec & (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) if ((ec & ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION)
return FindErrorIndex(ERR_ELECTRICAL_MMU_SELECTOR_SELFTEST_FAILED); return FindErrorIndex(ERR_ELECTRICAL_MMU_SELECTOR_SELFTEST_FAILED);
} else if (ec & (uint16_t)ErrorCode::TMC_IDLER_BIT) { } else if (ContainsBit(ec, ErrorCode::TMC_IDLER_BIT)) {
if ((ec & (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) if ((ec & ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION)
return FindErrorIndex(ERR_ELECTRICAL_MMU_IDLER_SELFTEST_FAILED); return FindErrorIndex(ERR_ELECTRICAL_MMU_IDLER_SELFTEST_FAILED);
} }
// TMC-related errors - multiple of these can occur at once // TMC-related errors - multiple of these can occur at once
// - in such a case we report the first which gets found/converted into Prusa-Error-Codes (usually the fact, that one TMC has an issue is serious enough) // - in such a case we report the first which gets found/converted into Prusa-Error-Codes (usually the fact, that one TMC has an issue is serious enough)
// By carefully ordering the checks here we can prioritize the errors being reported to the user. // By carefully ordering the checks here we can prioritize the errors being reported to the user.
if (ec & (uint16_t)ErrorCode::TMC_PULLEY_BIT) { if (ContainsBit(ec, ErrorCode::TMC_PULLEY_BIT)) {
if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH) if (ContainsBit(ec, ErrorCode::TMC_IOIN_MISMATCH))
return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_DRIVER_ERROR); return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_DRIVER_ERROR);
if (ec & (uint16_t)ErrorCode::TMC_RESET) if (ContainsBit(ec, ErrorCode::TMC_RESET))
return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_DRIVER_RESET); return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_DRIVER_RESET);
if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP) if (ContainsBit(ec, ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP))
return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_UNDERVOLTAGE_ERROR); return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_UNDERVOLTAGE_ERROR);
if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND) if (ContainsBit(ec, ErrorCode::TMC_SHORT_TO_GROUND))
return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_DRIVER_SHORTED); return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_DRIVER_SHORTED);
if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN) if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_WARN))
return FindErrorIndex(ERR_TEMPERATURE_WARNING_TMC_PULLEY_TOO_HOT); return FindErrorIndex(ERR_TEMPERATURE_WARNING_TMC_PULLEY_TOO_HOT);
if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR) if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_ERROR))
return FindErrorIndex(ERR_TEMPERATURE_TMC_PULLEY_OVERHEAT_ERROR); return FindErrorIndex(ERR_TEMPERATURE_TMC_PULLEY_OVERHEAT_ERROR);
} else if (ec & (uint16_t)ErrorCode::TMC_SELECTOR_BIT) { } else if (ContainsBit(ec, ErrorCode::TMC_SELECTOR_BIT)) {
if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH) if (ContainsBit(ec, ErrorCode::TMC_IOIN_MISMATCH))
return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_ERROR); return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_ERROR);
if (ec & (uint16_t)ErrorCode::TMC_RESET) if (ContainsBit(ec, ErrorCode::TMC_RESET))
return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_RESET); return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_RESET);
if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP) if (ContainsBit(ec, ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP))
return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_UNDERVOLTAGE_ERROR); return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_UNDERVOLTAGE_ERROR);
if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND) if (ContainsBit(ec, ErrorCode::TMC_SHORT_TO_GROUND))
return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_SHORTED); return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_SHORTED);
if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN) if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_WARN))
return FindErrorIndex(ERR_TEMPERATURE_WARNING_TMC_SELECTOR_TOO_HOT); return FindErrorIndex(ERR_TEMPERATURE_WARNING_TMC_SELECTOR_TOO_HOT);
if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR) if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_ERROR))
return FindErrorIndex(ERR_TEMPERATURE_TMC_SELECTOR_OVERHEAT_ERROR); return FindErrorIndex(ERR_TEMPERATURE_TMC_SELECTOR_OVERHEAT_ERROR);
} else if (ec & (uint16_t)ErrorCode::TMC_IDLER_BIT) { } else if (ContainsBit(ec, ErrorCode::TMC_IDLER_BIT)) {
if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH) if (ContainsBit(ec, ErrorCode::TMC_IOIN_MISMATCH))
return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_DRIVER_ERROR); return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_DRIVER_ERROR);
if (ec & (uint16_t)ErrorCode::TMC_RESET) if (ContainsBit(ec, ErrorCode::TMC_RESET))
return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_DRIVER_RESET); return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_DRIVER_RESET);
if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP) if (ContainsBit(ec, ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP))
return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_UNDERVOLTAGE_ERROR); return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_UNDERVOLTAGE_ERROR);
if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND) if (ContainsBit(ec, ErrorCode::TMC_SHORT_TO_GROUND))
return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_DRIVER_SHORTED); return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_DRIVER_SHORTED);
if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN) if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_WARN))
return FindErrorIndex(ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT); return FindErrorIndex(ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT);
if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR) if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_ERROR))
return FindErrorIndex(ERR_TEMPERATURE_TMC_IDLER_OVERHEAT_ERROR); return FindErrorIndex(ERR_TEMPERATURE_TMC_IDLER_OVERHEAT_ERROR);
} }
@ -184,16 +193,16 @@ struct ResetOnExit {
} }
}; };
Buttons ButtonPressed(uint16_t ec) { Buttons ButtonPressed(ErrorCode ec) {
if (buttonSelectedOperation == ButtonOperations::NoOperation) { if (buttonSelectedOperation == ButtonOperations::NoOperation) {
return NoButton; // no button return Buttons::NoButton; // no button
} }
ResetOnExit ros; // clear buttonSelectedOperation on exit from this call ResetOnExit ros; // clear buttonSelectedOperation on exit from this call
return ButtonAvailable(ec); return ButtonAvailable(ec);
} }
Buttons ButtonAvailable(uint16_t ec) { Buttons ButtonAvailable(ErrorCode ec) {
uint8_t ei = PrusaErrorCodeIndex(ec); uint8_t ei = PrusaErrorCodeIndex(ec);
// The list of responses which occur in mmu error dialogs // The list of responses which occur in mmu error dialogs
@ -214,7 +223,7 @@ Buttons ButtonAvailable(uint16_t ec) {
switch (buttonSelectedOperation) { switch (buttonSelectedOperation) {
// may be allow move selector right and left in the future // may be allow move selector right and left in the future
case ButtonOperations::Retry: // "Repeat action" case ButtonOperations::Retry: // "Repeat action"
return Middle; return Buttons::Middle;
default: default:
break; break;
} }
@ -224,9 +233,9 @@ Buttons ButtonAvailable(uint16_t ec) {
switch (buttonSelectedOperation) { switch (buttonSelectedOperation) {
// may be allow move selector right and left in the future // may be allow move selector right and left in the future
case ButtonOperations::Tune: // Tune Stallguard threshold case ButtonOperations::Tune: // Tune Stallguard threshold
return TuneMMU; return Buttons::TuneMMU;
case ButtonOperations::Retry: // "Repeat action" case ButtonOperations::Retry: // "Repeat action"
return Middle; return Buttons::Middle;
default: default:
break; break;
} }
@ -235,7 +244,7 @@ Buttons ButtonAvailable(uint16_t ec) {
case ERR_SYSTEM_FILAMENT_EJECTED: case ERR_SYSTEM_FILAMENT_EJECTED:
switch (buttonSelectedOperation) { switch (buttonSelectedOperation) {
case ButtonOperations::Continue: // User solved the serious mechanical problem by hand - there is no other way around case ButtonOperations::Continue: // User solved the serious mechanical problem by hand - there is no other way around
return Middle; return Buttons::Middle;
default: default:
break; break;
} }
@ -243,9 +252,9 @@ Buttons ButtonAvailable(uint16_t ec) {
case ERR_SYSTEM_FILAMENT_CHANGE: case ERR_SYSTEM_FILAMENT_CHANGE:
switch (buttonSelectedOperation) { switch (buttonSelectedOperation) {
case ButtonOperations::Load: case ButtonOperations::Load:
return Load; return Buttons::Load;
case ButtonOperations::Eject: case ButtonOperations::Eject:
return Eject; return Buttons::Eject;
default: default:
break; break;
} }
@ -255,9 +264,9 @@ Buttons ButtonAvailable(uint16_t ec) {
case ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT: case ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT:
switch (buttonSelectedOperation) { switch (buttonSelectedOperation) {
case ButtonOperations::Continue: // "Continue" case ButtonOperations::Continue: // "Continue"
return Left; return Buttons::Left;
case ButtonOperations::ResetMMU: // "Reset MMU" case ButtonOperations::ResetMMU: // "Reset MMU"
return ResetMMU; return Buttons::ResetMMU;
default: default:
break; break;
} }
@ -292,7 +301,7 @@ Buttons ButtonAvailable(uint16_t ec) {
case ERR_ELECTRICAL_MMU_MCU_ERROR: case ERR_ELECTRICAL_MMU_MCU_ERROR:
switch (buttonSelectedOperation) { switch (buttonSelectedOperation) {
case ButtonOperations::ResetMMU: // "Reset MMU" case ButtonOperations::ResetMMU: // "Reset MMU"
return ResetMMU; return Buttons::ResetMMU;
default: default:
break; break;
} }
@ -302,9 +311,9 @@ Buttons ButtonAvailable(uint16_t ec) {
case ERR_SYSTEM_FW_UPDATE_NEEDED: case ERR_SYSTEM_FW_UPDATE_NEEDED:
switch (buttonSelectedOperation) { switch (buttonSelectedOperation) {
case ButtonOperations::DisableMMU: // "Disable" case ButtonOperations::DisableMMU: // "Disable"
return DisableMMU; return Buttons::DisableMMU;
case ButtonOperations::ResetMMU: // "ResetMMU" case ButtonOperations::ResetMMU: // "ResetMMU"
return ResetMMU; return Buttons::ResetMMU;
default: default:
break; break;
} }
@ -312,9 +321,9 @@ Buttons ButtonAvailable(uint16_t ec) {
case ERR_SYSTEM_FILAMENT_ALREADY_LOADED: case ERR_SYSTEM_FILAMENT_ALREADY_LOADED:
switch (buttonSelectedOperation) { switch (buttonSelectedOperation) {
case ButtonOperations::Unload: // "Unload" case ButtonOperations::Unload: // "Unload"
return Left; return Buttons::Left;
case ButtonOperations::Continue: // "Proceed/Continue" case ButtonOperations::Continue: // "Proceed/Continue"
return Right; return Buttons::Right;
default: default:
break; break;
} }
@ -323,9 +332,9 @@ Buttons ButtonAvailable(uint16_t ec) {
case ERR_SYSTEM_INVALID_TOOL: case ERR_SYSTEM_INVALID_TOOL:
switch (buttonSelectedOperation) { switch (buttonSelectedOperation) {
case ButtonOperations::StopPrint: // "Stop print" case ButtonOperations::StopPrint: // "Stop print"
return StopPrint; return Buttons::StopPrint;
case ButtonOperations::ResetMMU: // "Reset MMU" case ButtonOperations::ResetMMU: // "Reset MMU"
return ResetMMU; return Buttons::ResetMMU;
default: default:
break; break;
} }
@ -335,7 +344,7 @@ Buttons ButtonAvailable(uint16_t ec) {
break; break;
} }
return NoButton; return Buttons::NoButton;
} }
void SetButtonResponse(ButtonOperations rsp){ void SetButtonResponse(ButtonOperations rsp){

View File

@ -1,13 +1,20 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include "mmu2/buttons.h" #ifdef __AVR__
#include "mmu2/buttons.h"
#include "mmu2/error_codes.h"
#else
#include "buttons.h"
#include "../../../../../../Prusa-Error-Codes/04_MMU/button_operations.h"
#include "../../../../../../Prusa-Firmware-MMU/src/logic/error_codes.h"
#endif
namespace MMU2 { namespace MMU2 {
/// Translates MMU2::ErrorCode into an index of Prusa-Error-Codes /// Translates MMU2::ErrorCode into an index of Prusa-Error-Codes
/// Basically this is the way to obtain an index into all other functions in this API /// Basically this is the way to obtain an index into all other functions in this API
uint8_t PrusaErrorCodeIndex(uint16_t ec); uint8_t PrusaErrorCodeIndex(ErrorCode ec);
/// @returns pointer to a PROGMEM string representing the Title of the Prusa-Error-Codes error /// @returns pointer to a PROGMEM string representing the Title of the Prusa-Error-Codes error
/// @param i index of the error - obtained by calling ErrorCodeIndex /// @param i index of the error - obtained by calling ErrorCodeIndex
@ -38,11 +45,11 @@ void SetButtonResponse(ButtonOperations rsp);
/// @returns button index/code based on currently processed error/screen /// @returns button index/code based on currently processed error/screen
/// Clears the "pressed" button upon exit /// Clears the "pressed" button upon exit
Buttons ButtonPressed(uint16_t ec); Buttons ButtonPressed(ErrorCode ec);
/// @returns button index/code based on currently processed error/screen /// @returns button index/code based on currently processed error/screen
/// Used as a subfunction of ButtonPressed. /// Used as a subfunction of ButtonPressed.
/// Does not clear the "pressed" button upon exit /// Does not clear the "pressed" button upon exit
Buttons ButtonAvailable(uint16_t ec); Buttons ButtonAvailable(ErrorCode ec);
} // namespace MMU2 } // namespace MMU2

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#ifdef __AVR__
#ifndef UNITTEST #include <avr/pgmspace.h>
#include "Marlin.h" #endif
// Beware - before changing this prefix, think twice // Beware - before changing this prefix, think twice
// you'd need to change appmain.cpp app_marlin_serial_output_write_hook // you'd need to change appmain.cpp app_marlin_serial_output_write_hook
@ -14,16 +14,22 @@ namespace MMU2 {
/// @param msg pointer to a string in PROGMEM /// @param msg pointer to a string in PROGMEM
/// On the AVR platform this variant reads the input string from PROGMEM. /// On the AVR platform this variant reads the input string from PROGMEM.
/// On the ARM platform it calls LogErrorEvent directly (silently expecting the compiler to optimize it away) /// On the ARM platform it calls LogErrorEvent directly (silently expecting the compiler to optimize it away)
void LogErrorEvent_P(const char *msg); void LogErrorEvent_P(const char *msg_P);
/// Report the msg into the general logging subsystem (through Marlin's SERIAL_ECHO stuff) /// Report the msg into the general logging subsystem (through Marlin's SERIAL_ECHO stuff)
/// @param msg pointer to a string in PROGMEM /// @param msg pointer to a string in PROGMEM
/// On the AVR platform this variant reads the input string from PROGMEM. /// On the AVR platform this variant reads the input string from PROGMEM.
/// On the ARM platform it calls LogErrorEvent directly (silently expecting the compiler to optimize it away) /// On the ARM platform it calls LogErrorEvent directly (silently expecting the compiler to optimize it away)
void LogEchoEvent_P(const char *msg); void LogEchoEvent_P(const char *msg_P);
} // namespace } // namespace MMU2
#ifndef UNITTEST
#ifdef __AVR__
#include "Marlin.h"
#else
#include "../../core/serial.h"
#endif
#define SERIAL_MMU2() \ #define SERIAL_MMU2() \
{ serialprintPGM(mmu2Magic); } { serialprintPGM(mmu2Magic); }
@ -49,11 +55,14 @@ void LogEchoEvent_P(const char *msg);
} while (0) } while (0)
#define MMU2_ERROR_MSG(S) MMU2_ECHO_MSG(S) //!@todo Decide MMU errors on serial line #define MMU2_ERROR_MSG(S) MMU2_ECHO_MSG(S) //!@todo Decide MMU errors on serial line
#else // #ifndef UNITTEST #else // #ifndef UNITTEST
#include "stubs/stub_interfaces.h"
#define MMU2_ECHO_MSGLN(S) marlinLogSim.AppendLine(S)
#define MMU2_ERROR_MSGLN(S) marlinLogSim.AppendLine(S)
#define MMU2_ECHO_MSGRPGM(S) /*marlinLogSim.AppendLine(S)*/
#define MMU2_ERROR_MSGRPGM(S) /*marlinLogSim.AppendLine(S)*/
#define SERIAL_ECHOLNPGM(S) /*marlinLogSim.AppendLine(S)*/
#define SERIAL_ECHOPGM(S) /* */
#define SERIAL_ECHOLN(S) /*marlinLogSim.AppendLine(S)*/
#define MMU2_ECHO_MSGLN(S) /* */ #endif // #ifndef UNITTEST
#define MMU2_ERROR_MSGLN(S) /* */
#define MMU2_ECHO_MSGRPGM(S) /* */
#define MMU2_ERROR_MSGRPGM(S) /* */
#endif // #ifndef UNITTEST

View File

@ -14,27 +14,25 @@ struct pos3d {
pos3d() = default; pos3d() = default;
inline constexpr pos3d(float x, float y, float z) inline constexpr pos3d(float x, float y, float z)
: xyz { x, y, z } {} : xyz { x, y, z } {}
pos3d operator=(const float *newP){ pos3d operator=(const float *newP) {
for(uint8_t i = 0; i < 3; ++i){ for (uint8_t i = 0; i < 3; ++i) {
xyz[i] = newP[i]; xyz[i] = newP[i];
} }
return *this; return *this;
} }
}; };
void MoveE(float delta, float feedRate); void extruder_move(float distance, float feed_rate);
void extruder_schedule_turning(float feed_rate);
float MoveRaiseZ(float delta); float move_raise_z(float delta);
void planner_abort_queued_moves(); void planner_abort_queued_moves();
void planner_synchronize(); void planner_synchronize();
bool planner_any_moves(); bool planner_any_moves();
float planner_get_machine_position_E_mm();
float stepper_get_machine_position_E_mm(); float stepper_get_machine_position_E_mm();
float planner_get_current_position_E(); float planner_get_current_position_E();
void planner_set_current_position_E(float e); void planner_set_current_position_E(float e);
void planner_line_to_current_position(float feedRate_mm_s);
void planner_line_to_current_position_sync(float feedRate_mm_s);
pos3d planner_current_position(); pos3d planner_current_position();
void motion_do_blocking_move_to_xy(float rx, float ry, float feedRate_mm_s); void motion_do_blocking_move_to_xy(float rx, float ry, float feedRate_mm_s);
@ -46,6 +44,9 @@ bool marlin_printingIsActive();
void marlin_manage_heater(); void marlin_manage_heater();
void marlin_manage_inactivity(bool b); void marlin_manage_inactivity(bool b);
void marlin_idle(bool b); void marlin_idle(bool b);
void marlin_refresh_print_state_in_ram();
void marlin_clear_print_state_in_ram();
void marlin_stop_and_save_print_to_ram();
int16_t thermal_degTargetHotend(); int16_t thermal_degTargetHotend();
int16_t thermal_degHotend(); int16_t thermal_degHotend();
@ -61,6 +62,4 @@ bool all_axes_homed();
void gcode_reset_stepper_timeout(); void gcode_reset_stepper_timeout();
bool cutter_enabled();
} // namespace MMU2 } // namespace MMU2

View File

@ -9,12 +9,21 @@
namespace MMU2 { namespace MMU2 {
void MoveE(float delta, float feedRate) { static void planner_line_to_current_position(float feedRate_mm_s){
plan_buffer_line_curposXYZE(feedRate_mm_s);
}
static void planner_line_to_current_position_sync(float feedRate_mm_s){
planner_line_to_current_position(feedRate_mm_s);
planner_synchronize();
}
void extruder_move(float delta, float feedRate) {
current_position[E_AXIS] += delta; current_position[E_AXIS] += delta;
planner_line_to_current_position(feedRate); planner_line_to_current_position(feedRate);
} }
float MoveRaiseZ(float delta) { float move_raise_z(float delta) {
return raise_z(delta); return raise_z(delta);
} }
@ -53,15 +62,6 @@ void planner_set_current_position_E(float e){
current_position[E_AXIS] = e; current_position[E_AXIS] = e;
} }
void planner_line_to_current_position(float feedRate_mm_s){
plan_buffer_line_curposXYZE(feedRate_mm_s);
}
void planner_line_to_current_position_sync(float feedRate_mm_s){
planner_line_to_current_position(feedRate_mm_s);
planner_synchronize();
}
pos3d planner_current_position(){ pos3d planner_current_position(){
return pos3d(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS]); return pos3d(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS]);
} }
@ -101,6 +101,18 @@ void marlin_idle(bool b){
manage_inactivity(b); manage_inactivity(b);
} }
void marlin_refresh_print_state_in_ram(){
refresh_print_state_in_ram();
}
void marlin_clear_print_state_in_ram(){
clear_print_state_in_ram();
}
void marlin_stop_and_save_print_to_ram(){
stop_and_save_print_to_ram(0,0);
}
int16_t thermal_degTargetHotend() { int16_t thermal_degTargetHotend() {
return degTargetHotend(0); return degTargetHotend(0);
} }
@ -132,8 +144,4 @@ bool all_axes_homed(){
return axis_known_position[X_AXIS] && axis_known_position[Y_AXIS]; return axis_known_position[X_AXIS] && axis_known_position[Y_AXIS];
} }
bool cutter_enabled(){
return eeprom_read_byte((uint8_t*)EEPROM_MMU_CUTTER_ENABLED) == EEPROM_MMU_CUTTER_ENABLED_enabled;
}
} // namespace MMU2 } // namespace MMU2

View File

@ -8,6 +8,10 @@
// brings _O and _T macros into MMU // brings _O and _T macros into MMU
#include "language.h" #include "language.h"
#define MARLIN_KEEPALIVE_STATE_IN_PROCESS KEEPALIVE_STATE(IN_PROCESS) #define MARLIN_KEEPALIVE_STATE_IN_PROCESS KEEPALIVE_STATE(IN_PROCESS)
#elif defined(UNITTEST)
#define _O(x) x
#define _T(x) x
#define MARLIN_KEEPALIVE_STATE_IN_PROCESS /*KEEPALIVE_STATE(IN_PROCESS) TODO*/
#else #else
#include "../../gcode/gcode.h" #include "../../gcode/gcode.h"
#define _O(x) x #define _O(x) x

View File

@ -62,10 +62,10 @@ static const char * const progressTexts[] PROGMEM = {
_R(MSG_PROGRESS_FEED_FSENSOR) _R(MSG_PROGRESS_FEED_FSENSOR)
}; };
const char * ProgressCodeToText(uint16_t pc){ const char * ProgressCodeToText(ProgressCode pc){
// @@TODO ?? a better fallback option? // @@TODO ?? a better fallback option?
return ( pc <= (sizeof(progressTexts) / sizeof(progressTexts[0])) ) return ( (uint16_t)pc <= (sizeof(progressTexts) / sizeof(progressTexts[0])) )
? static_cast<const char *>(pgm_read_ptr(&progressTexts[pc])) ? static_cast<const char *>(pgm_read_ptr(&progressTexts[(uint16_t)pc]))
: static_cast<const char *>(pgm_read_ptr(&progressTexts[0])); : static_cast<const char *>(pgm_read_ptr(&progressTexts[0]));
} }

View File

@ -1,9 +1,14 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#ifdef __AVR__
#include "mmu2/progress_codes.h"
#else
#include "../../../../../../Prusa-Firmware-MMU/src/logic/progress_codes.h"
#endif
namespace MMU2 { namespace MMU2 {
const char * ProgressCodeToText(uint16_t pc); const char *ProgressCodeToText(ProgressCode pc);
} }

View File

@ -9,6 +9,11 @@
// irrelevant on Buddy FW, just keep "_millis" as "millis" // irrelevant on Buddy FW, just keep "_millis" as "millis"
#include <wiring_time.h> #include <wiring_time.h>
#define _millis millis #define _millis millis
#ifdef UNITTEST
#define strncmp_P strncmp
#else
#include <Marlin/src/core/serial.h>
#endif
#endif #endif
#include <string.h> #include <string.h>
@ -16,7 +21,7 @@
namespace MMU2 { namespace MMU2 {
/// Beware: /// Beware - on AVR/MK3S:
/// Changing the supportedMmuVersion numbers requires patching MSG_DESC_FW_UPDATE_NEEDED and all its related translations by hand. /// Changing the supportedMmuVersion numbers requires patching MSG_DESC_FW_UPDATE_NEEDED and all its related translations by hand.
/// ///
/// The message reads: /// The message reads:
@ -27,18 +32,18 @@ namespace MMU2 {
static constexpr uint8_t supportedMmuFWVersion[3] PROGMEM = { mmuVersionMajor, mmuVersionMinor, mmuVersionPatch }; static constexpr uint8_t supportedMmuFWVersion[3] PROGMEM = { mmuVersionMajor, mmuVersionMinor, mmuVersionPatch };
const Register ProtocolLogic::regs8Addrs[ProtocolLogic::regs8Count] PROGMEM = { const Register ProtocolLogic::regs8Addrs[ProtocolLogic::regs8Count] PROGMEM = {
Register::FINDA_State, // FINDA state Register::FINDA_State, // FINDA state
Register::Set_Get_Selector_Slot, // Selector slot Register::Set_Get_Selector_Slot, // Selector slot
Register::Set_Get_Idler_Slot, // Idler slot Register::Set_Get_Idler_Slot, // Idler slot
}; };
const Register ProtocolLogic::regs16Addrs[ProtocolLogic::regs16Count] PROGMEM = { const Register ProtocolLogic::regs16Addrs[ProtocolLogic::regs16Count] PROGMEM = {
Register::MMU_Errors, // MMU errors - aka statistics Register::MMU_Errors, // MMU errors - aka statistics
Register::Get_Pulley_Position, // Pulley position [mm] Register::Get_Pulley_Position, // Pulley position [mm]
}; };
const Register ProtocolLogic::initRegs8Addrs[ProtocolLogic::initRegs8Count] PROGMEM = { const Register ProtocolLogic::initRegs8Addrs[ProtocolLogic::initRegs8Count] PROGMEM = {
Register::Extra_Load_Distance, // extra load distance [mm] Register::Extra_Load_Distance, // extra load distance [mm]
Register::Pulley_Slow_Feedrate, // pulley slow feedrate [mm/s] Register::Pulley_Slow_Feedrate, // pulley slow feedrate [mm/s]
}; };
@ -181,7 +186,7 @@ StepStatus ProtocolLogic::ExpectingMessage() {
break; break;
} }
} }
[[fallthrough]]; // otherwise [[fallthrough]]; // otherwise
default: default:
RecordUARTActivity(); // something has happened on the UART, update the timeout record RecordUARTActivity(); // something has happened on the UART, update the timeout record
return ProtocolError; return ProtocolError;
@ -197,7 +202,11 @@ StepStatus ProtocolLogic::ExpectingMessage() {
} }
void ProtocolLogic::SendMsg(RequestMsg rq) { void ProtocolLogic::SendMsg(RequestMsg rq) {
#ifdef __AVR__
// Buddy FW cannot use stack-allocated txbuff - DMA doesn't work with CCMRAM
// No restrictions on MK3/S/+ though
uint8_t txbuff[Protocol::MaxRequestSize()]; uint8_t txbuff[Protocol::MaxRequestSize()];
#endif
uint8_t len = Protocol::EncodeRequest(rq, txbuff); uint8_t len = Protocol::EncodeRequest(rq, txbuff);
uart->write(txbuff, len); uart->write(txbuff, len);
LogRequestMsg(txbuff, len); LogRequestMsg(txbuff, len);
@ -205,7 +214,11 @@ void ProtocolLogic::SendMsg(RequestMsg rq) {
} }
void ProtocolLogic::SendWriteMsg(RequestMsg rq) { void ProtocolLogic::SendWriteMsg(RequestMsg rq) {
#ifdef __AVR__
// Buddy FW cannot use stack-allocated txbuff - DMA doesn't work with CCMRAM
// No restrictions on MK3/S/+ though
uint8_t txbuff[Protocol::MaxRequestSize()]; uint8_t txbuff[Protocol::MaxRequestSize()];
#endif
uint8_t len = Protocol::EncodeWriteRequest(rq.value, rq.value2, txbuff); uint8_t len = Protocol::EncodeWriteRequest(rq.value, rq.value2, txbuff);
uart->write(txbuff, len); uart->write(txbuff, len);
LogRequestMsg(txbuff, len); LogRequestMsg(txbuff, len);
@ -275,9 +288,9 @@ StepStatus ProtocolLogic::ScopeStep() {
case Scope::StartSeq: case Scope::StartSeq:
return StartSeqStep(); // ~270B return StartSeqStep(); // ~270B
case Scope::Idle: case Scope::Idle:
return IdleStep(); // ~300B return IdleStep(); // ~300B
case Scope::Command: case Scope::Command:
return CommandStep(); // ~430B return CommandStep(); // ~430B
case Scope::Stopped: case Scope::Stopped:
return StoppedStep(); return StoppedStep();
default: default:
@ -322,7 +335,7 @@ StepStatus ProtocolLogic::StartSeqStep() {
StepStatus ProtocolLogic::DelayedRestartWait() { StepStatus ProtocolLogic::DelayedRestartWait() {
if (Elapsed(heartBeatPeriod)) { // this basically means, that we are waiting until there is some traffic on if (Elapsed(heartBeatPeriod)) { // this basically means, that we are waiting until there is some traffic on
while (uart->read() != -1) while (uart->read() != -1)
; // clear the input buffer ; // clear the input buffer
// switch to StartSeq // switch to StartSeq
Start(); Start();
} }
@ -349,6 +362,7 @@ StepStatus ProtocolLogic::ProcessCommandQueryResponse() {
return Processing; return Processing;
case ResponseMsgParamCodes::Error: case ResponseMsgParamCodes::Error:
// in case of an error the progress code remains as it has been before // in case of an error the progress code remains as it has been before
progressCode = ProgressCode::ERRWaitingForUser;
errorCode = static_cast<ErrorCode>(rsp.paramValue); errorCode = static_cast<ErrorCode>(rsp.paramValue);
// keep on reporting the state of fsensor regularly even in command error state // keep on reporting the state of fsensor regularly even in command error state
// - the MMU checks FINDA and fsensor even while recovering from errors // - the MMU checks FINDA and fsensor even while recovering from errors
@ -469,9 +483,11 @@ StepStatus ProtocolLogic::IdleStep() {
case ResponseMsgParamCodes::Processing: case ResponseMsgParamCodes::Processing:
// @@TODO we may actually use this branch to report progress of manual operation on the MMU // @@TODO we may actually use this branch to report progress of manual operation on the MMU
// The MMU sends e.g. X0 P27 after its restart when the user presses an MMU button to move the Selector // The MMU sends e.g. X0 P27 after its restart when the user presses an MMU button to move the Selector
progressCode = static_cast<ProgressCode>(rsp.paramValue);
errorCode = ErrorCode::OK; errorCode = ErrorCode::OK;
break; break;
default: default:
progressCode = ProgressCode::ERRWaitingForUser;
errorCode = static_cast<ErrorCode>(rsp.paramValue); errorCode = static_cast<ErrorCode>(rsp.paramValue);
StartReading8bitRegisters(); // continue Idle state without restarting the communication StartReading8bitRegisters(); // continue Idle state without restarting the communication
return CommandError; return CommandError;
@ -532,7 +548,7 @@ ProtocolLogic::ProtocolLogic(MMU2Serial *uart, uint8_t extraLoadDistance, uint8_
, uart(uart) , uart(uart)
, errorCode(ErrorCode::OK) , errorCode(ErrorCode::OK)
, progressCode(ProgressCode::OK) , progressCode(ProgressCode::OK)
, buttonCode(NoButton) , buttonCode(Buttons::NoButton)
, lastFSensor((uint8_t)WhereIsFilament()) , lastFSensor((uint8_t)WhereIsFilament())
, regIndex(0) , regIndex(0)
, retryAttempts(MAX_RETRIES) , retryAttempts(MAX_RETRIES)
@ -758,6 +774,7 @@ void ProtocolLogic::LogResponse() {
StepStatus ProtocolLogic::SuppressShortDropOuts(const char *msg_P, StepStatus ss) { StepStatus ProtocolLogic::SuppressShortDropOuts(const char *msg_P, StepStatus ss) {
if (dataTO.Record(ss)) { if (dataTO.Record(ss)) {
LogError(msg_P); LogError(msg_P);
dataTO.Reset(); // prepare for another run of consecutive retries before firing an error
return dataTO.InitialCause(); return dataTO.InitialCause();
} else { } else {
return Processing; // suppress short drop outs of communication return Processing; // suppress short drop outs of communication

View File

@ -3,37 +3,39 @@
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
#ifdef __AVR__ #ifdef __AVR__
#include "mmu2/error_codes.h" #include "mmu2/error_codes.h"
#include "mmu2/progress_codes.h" #include "mmu2/progress_codes.h"
#include "mmu2/buttons.h" #include "mmu2/buttons.h"
#include "mmu2/registers.h" #include "mmu2/registers.h"
#include "mmu2_protocol.h" #include "mmu2_protocol.h"
// #include <array> std array is not available on AVR ... we need to "fake" it // #include <array> std array is not available on AVR ... we need to "fake" it
namespace std { namespace std {
template<typename T, uint8_t N> template <typename T, uint8_t N>
class array { class array {
T data[N]; T data[N];
public: public:
array() = default; array() = default;
inline constexpr T* begin()const { return data; } inline constexpr T *begin() const { return data; }
inline constexpr T* end()const { return data + N; } inline constexpr T *end() const { return data + N; }
static constexpr uint8_t size() { return N; } static constexpr uint8_t size() { return N; }
inline T &operator[](uint8_t i){ inline T &operator[](uint8_t i) {
return data[i]; return data[i];
} }
}; };
} } // namespace std
#else #else
#include <array> #include <array>
#include "../../../../../../Prusa-Firmware-MMU/src/logic/error_codes.h" #include "../../../../../../Prusa-Firmware-MMU/src/logic/error_codes.h"
#include "../../../../../../Prusa-Firmware-MMU/src/logic/progress_codes.h" #include "../../../../../../Prusa-Firmware-MMU/src/logic/progress_codes.h"
// prevent ARM HAL macros from breaking our code // prevent ARM HAL macros from breaking our code
#undef CRC #undef CRC
#include "../../../../../../Prusa-Firmware-MMU/src/modules/protocol.h" #include "../../../../../../Prusa-Firmware-MMU/src/modules/protocol.h"
#include "buttons.h" #include "buttons.h"
#include "registers.h"
#endif #endif
#include "mmu2_serial.h" #include "mmu2_serial.h"
@ -50,9 +52,9 @@ class ProtocolLogic;
/// ProtocolLogic stepping statuses /// ProtocolLogic stepping statuses
enum StepStatus : uint_fast8_t { enum StepStatus : uint_fast8_t {
Processing = 0, Processing = 0,
MessageReady, ///< a message has been successfully decoded from the received bytes MessageReady, ///< a message has been successfully decoded from the received bytes
Finished, ///< Scope finished successfully Finished, ///< Scope finished successfully
Interrupted, ///< received "Finished" message related to a different command than originally issued (most likely the MMU restarted while doing something) Interrupted, ///< received "Finished" message related to a different command than originally issued (most likely the MMU restarted while doing something)
CommunicationTimeout, ///< the MMU failed to respond to a request within a specified time frame CommunicationTimeout, ///< the MMU failed to respond to a request within a specified time frame
ProtocolError, ///< bytes read from the MMU didn't form a valid response ProtocolError, ///< bytes read from the MMU didn't form a valid response
CommandRejected, ///< the MMU rejected the command due to some other command in progress, may be the user is operating the MMU locally (button commands) CommandRejected, ///< the MMU rejected the command due to some other command in progress, may be the user is operating the MMU locally (button commands)
@ -60,19 +62,17 @@ enum StepStatus : uint_fast8_t {
VersionMismatch, ///< the MMU reports its firmware version incompatible with our implementation VersionMismatch, ///< the MMU reports its firmware version incompatible with our implementation
PrinterError, ///< printer's explicit error - MMU is fine, but the printer was unable to complete the requested operation PrinterError, ///< printer's explicit error - MMU is fine, but the printer was unable to complete the requested operation
CommunicationRecovered, CommunicationRecovered,
ButtonPushed, ///< The MMU reported the user pushed one of its three buttons. ButtonPushed, ///< The MMU reported the user pushed one of its three buttons.
}; };
static constexpr uint32_t linkLayerTimeout = 2000; ///< default link layer communication timeout inline constexpr uint32_t linkLayerTimeout = 2000; ///< default link layer communication timeout
static constexpr uint32_t dataLayerTimeout = linkLayerTimeout * 3; ///< data layer communication timeout inline constexpr uint32_t dataLayerTimeout = linkLayerTimeout * 3; ///< data layer communication timeout
static constexpr uint32_t heartBeatPeriod = linkLayerTimeout / 2; ///< period of heart beat messages (Q0) inline constexpr uint32_t heartBeatPeriod = linkLayerTimeout / 2; ///< period of heart beat messages (Q0)
static_assert(heartBeatPeriod < linkLayerTimeout && linkLayerTimeout < dataLayerTimeout, "Incorrect ordering of timeouts"); static_assert(heartBeatPeriod < linkLayerTimeout && linkLayerTimeout < dataLayerTimeout, "Incorrect ordering of timeouts");
///< Filter of short consecutive drop outs which are recovered instantly ///< Filter of short consecutive drop outs which are recovered instantly
class DropOutFilter { class DropOutFilter {
StepStatus cause;
uint8_t occurrences;
public: public:
static constexpr uint8_t maxOccurrences = 10; // ideally set this to >8 seconds -> 12x heartBeatPeriod static constexpr uint8_t maxOccurrences = 10; // ideally set this to >8 seconds -> 12x heartBeatPeriod
static_assert(maxOccurrences > 1, "we should really silently ignore at least 1 comm drop out if recovered immediately afterwards"); static_assert(maxOccurrences > 1, "we should really silently ignore at least 1 comm drop out if recovered immediately afterwards");
@ -86,6 +86,10 @@ public:
/// Rearms the object for further processing - basically call this once the MMU responds with something meaningful (e.g. S0 A2) /// Rearms the object for further processing - basically call this once the MMU responds with something meaningful (e.g. S0 A2)
inline void Reset() { occurrences = maxOccurrences; } inline void Reset() { occurrences = maxOccurrences; }
private:
StepStatus cause;
uint8_t occurrences = maxOccurrences;
}; };
/// Logic layer of the MMU vs. printer communication protocol /// Logic layer of the MMU vs. printer communication protocol
@ -115,11 +119,11 @@ public:
/// Sets the extra load distance to be reported to the MMU. /// Sets the extra load distance to be reported to the MMU.
/// Beware - this call doesn't send anything to the MMU. /// Beware - this call doesn't send anything to the MMU.
/// The MMU gets the newly set value either by a communication restart or via an explicit WriteRegister call /// The MMU gets the newly set value either by a communication restart or via an explicit WriteRegister call
inline void PlanExtraLoadDistance(uint8_t eld_mm){ inline void PlanExtraLoadDistance(uint8_t eld_mm) {
initRegs8[0] = eld_mm; initRegs8[0] = eld_mm;
} }
/// @returns the currently preset extra load distance /// @returns the currently preset extra load distance
inline uint8_t ExtraLoadDistance()const { inline uint8_t ExtraLoadDistance() const {
return initRegs8[0]; return initRegs8[0];
} }
@ -187,13 +191,13 @@ public:
inAutoRetry = iar; inAutoRetry = iar;
} }
inline void SetPrinterError(ErrorCode ec){ inline void SetPrinterError(ErrorCode ec) {
explicitPrinterError = ec; explicitPrinterError = ec;
} }
inline void ClearPrinterError(){ inline void ClearPrinterError() {
explicitPrinterError = ErrorCode::OK; explicitPrinterError = ErrorCode::OK;
} }
inline bool IsPrinterError()const { inline bool IsPrinterError() const {
return explicitPrinterError != ErrorCode::OK; return explicitPrinterError != ErrorCode::OK;
} }
inline ErrorCode PrinterError() const { inline ErrorCode PrinterError() const {
@ -228,15 +232,6 @@ private:
Running ///< normal operation - Idle + Command processing Running ///< normal operation - Idle + Command processing
}; };
// individual sub-state machines - may be they can be combined into a union since only one is active at once
// or we can blend them into ProtocolLogic at the cost of a less nice code (but hopefully shorter)
// Stopped stopped;
// StartSeq startSeq;
// DelayedRestart delayedRestart;
// Idle idle;
// Command command;
// ProtocolLogicPartBase *currentState; ///< command currently being processed
enum class Scope : uint_fast8_t { enum class Scope : uint_fast8_t {
Stopped, Stopped,
StartSeq, StartSeq,
@ -350,25 +345,30 @@ private:
/// Activate the planned state once the immediate response to a sent request arrived /// Activate the planned state once the immediate response to a sent request arrived
bool ActivatePlannedRequest(); bool ActivatePlannedRequest();
uint32_t lastUARTActivityMs; ///< timestamp - last ms when something occurred on the UART uint32_t lastUARTActivityMs; ///< timestamp - last ms when something occurred on the UART
DropOutFilter dataTO; ///< Filter of short consecutive drop outs which are recovered instantly DropOutFilter dataTO; ///< Filter of short consecutive drop outs which are recovered instantly
ResponseMsg rsp; ///< decoded response message from the MMU protocol ResponseMsg rsp; ///< decoded response message from the MMU protocol
State state; ///< internal state of ProtocolLogic State state; ///< internal state of ProtocolLogic
Protocol protocol; ///< protocol codec Protocol protocol; ///< protocol codec
std::array<uint8_t, 16> lastReceivedBytes; ///< remembers the last few bytes of incoming communication for diagnostic purposes std::array<uint8_t, 16> lastReceivedBytes; ///< remembers the last few bytes of incoming communication for diagnostic purposes
uint8_t lrb; uint8_t lrb;
MMU2Serial *uart; ///< UART interface MMU2Serial *uart; ///< UART interface
ErrorCode errorCode; ///< last received error code from the MMU ErrorCode errorCode; ///< last received error code from the MMU
ProgressCode progressCode; ///< last received progress code from the MMU ProgressCode progressCode; ///< last received progress code from the MMU
Buttons buttonCode; ///< Last received button from the MMU. Buttons buttonCode; ///< Last received button from the MMU.
uint8_t lastFSensor; ///< last state of filament sensor uint8_t lastFSensor; ///< last state of filament sensor
#ifndef __AVR__
uint8_t txbuff[Protocol::MaxRequestSize()]; ///< In Buddy FW - a static transmit buffer needs to exist as DMA cannot be used from CCMRAM.
///< On MK3/S/+ the transmit buffer is allocated on the stack without restrictions
#endif
// 8bit registers // 8bit registers
static constexpr uint8_t regs8Count = 3; static constexpr uint8_t regs8Count = 3;

View File

@ -3,6 +3,7 @@
#include "mmu2_log.h" #include "mmu2_log.h"
#include "mmu2_reporting.h" #include "mmu2_reporting.h"
#include "mmu2_error_converter.h" #include "mmu2_error_converter.h"
#include "mmu2_progress_converter.h"
#include "mmu2/error_codes.h" #include "mmu2/error_codes.h"
#include "mmu2/buttons.h" #include "mmu2/buttons.h"
#include "menu.h" #include "menu.h"
@ -14,14 +15,12 @@
namespace MMU2 { namespace MMU2 {
const char * ProgressCodeToText(uint16_t pc); // we may join progress convertor and reporter together void BeginReport(CommandInProgress /*cip*/, ProgressCode ec) {
void BeginReport(CommandInProgress /*cip*/, uint16_t ec) {
custom_message_type = CustomMsg::MMUProgress; custom_message_type = CustomMsg::MMUProgress;
lcd_setstatuspgm( _T(ProgressCodeToText(ec)) ); lcd_setstatuspgm( _T(ProgressCodeToText(ec)) );
} }
void EndReport(CommandInProgress /*cip*/, uint16_t /*ec*/) { void EndReport(CommandInProgress /*cip*/, ProgressCode /*ec*/) {
// clear the status msg line - let the printed filename get visible again // clear the status msg line - let the printed filename get visible again
if (!printJobOngoing()) { if (!printJobOngoing()) {
lcd_setstatuspgm(MSG_WELCOME); lcd_setstatuspgm(MSG_WELCOME);
@ -231,7 +230,7 @@ bool TuneMenuEntered() {
return putErrorScreenToSleep; return putErrorScreenToSleep;
} }
void ReportErrorHook(CommandInProgress /*cip*/, uint16_t ec, uint8_t /*es*/) { void ReportErrorHook(CommandInProgress /*cip*/, ErrorCode ec, uint8_t /*es*/) {
if (putErrorScreenToSleep) return; if (putErrorScreenToSleep) return;
if (mmu2.MMUCurrentErrorCode() == ErrorCode::OK && mmu2.MMULastErrorSource() == MMU2::ErrorSourceMMU) { if (mmu2.MMUCurrentErrorCode() == ErrorCode::OK && mmu2.MMULastErrorSource() == MMU2::ErrorSourceMMU) {
@ -241,7 +240,7 @@ void ReportErrorHook(CommandInProgress /*cip*/, uint16_t ec, uint8_t /*es*/) {
ReportErrorHookState = ReportErrorHookStates::DISMISS_ERROR_SCREEN; ReportErrorHookState = ReportErrorHookStates::DISMISS_ERROR_SCREEN;
} }
const uint8_t ei = PrusaErrorCodeIndex(ec); const uint8_t ei = PrusaErrorCodeIndex((ErrorCode)ec);
switch ((uint8_t)ReportErrorHookState) { switch ((uint8_t)ReportErrorHookState) {
case (uint8_t)ReportErrorHookStates::RENDER_ERROR_SCREEN: case (uint8_t)ReportErrorHookStates::RENDER_ERROR_SCREEN:
@ -289,39 +288,57 @@ void ReportErrorHook(CommandInProgress /*cip*/, uint16_t ec, uint8_t /*es*/) {
} }
} }
void ReportProgressHook(CommandInProgress cip, uint16_t ec) { void ReportProgressHook(CommandInProgress cip, ProgressCode ec) {
if (cip != CommandInProgress::NoCommand) { if (cip != CommandInProgress::NoCommand) {
custom_message_type = CustomMsg::MMUProgress; custom_message_type = CustomMsg::MMUProgress;
lcd_setstatuspgm( _T(ProgressCodeToText(ec)) ); lcd_setstatuspgm( _T(ProgressCodeToText(ec)) );
} }
} }
void TryLoadUnloadProgressbarInit() { TryLoadUnloadReporter::TryLoadUnloadReporter(float delta_mm)
: dpixel0(0)
, dpixel1(0)
, lcd_cursor_col(0)
, pixel_per_mm(0.5F * float(LCD_WIDTH) / (delta_mm))
{
lcd_clearstatus(); lcd_clearstatus();
} }
void TryLoadUnloadProgressbarDeinit() { TryLoadUnloadReporter::~TryLoadUnloadReporter() {
// Delay the next status message just so // Delay the next status message just so
// the user can see the results clearly // the user can see the results clearly
lcd_reset_status_message_timeout(); lcd_reset_status_message_timeout();
} }
void TryLoadUnloadProgressbarEcho() { void TryLoadUnloadReporter::Render(uint8_t col, bool sensorState) {
char buf[LCD_WIDTH]; // Set the cursor position each time in case some other
// part of the firmware changes the cursor position
lcd_insert_char_into_status(col, sensorState ? LCD_STR_SOLID_BLOCK[0] : '-');
if (!lcd_update_enabled) lcdui_print_status_line();
}
void TryLoadUnloadReporter::Progress(bool sensorState){
// Always round up, you can only have 'whole' pixels. (floor is also an option)
dpixel1 = ceil((stepper_get_machine_position_E_mm() - planner_get_current_position_E()) * pixel_per_mm);
if (dpixel1 - dpixel0) {
dpixel0 = dpixel1;
if (lcd_cursor_col > (LCD_WIDTH - 1)) lcd_cursor_col = LCD_WIDTH - 1;
Render(lcd_cursor_col++, sensorState);
}
}
void TryLoadUnloadReporter::DumpToSerial(){
char buf[LCD_WIDTH + 1];
lcd_getstatus(buf); lcd_getstatus(buf);
for (uint8_t i = 0; i < sizeof(buf); i++) { for (uint8_t i = 0; i < sizeof(buf); i++) {
// 0xFF is -1 when converting from unsigned to signed char // 0xFF is -1 when converting from unsigned to signed char
// If the number is negative, that means filament is present // If the number is negative, that means filament is present
buf[i] = (buf[i] < 0) ? '1' : '0'; buf[i] = (buf[i] < 0) ? '1' : '0';
} }
buf[LCD_WIDTH] = 0;
MMU2_ECHO_MSGLN(buf); MMU2_ECHO_MSGLN(buf);
} }
void TryLoadUnloadProgressbar(uint8_t col, bool sensorState) {
lcd_insert_char_into_status(col, sensorState ? '-' : LCD_STR_SOLID_BLOCK[0]);
if (!lcd_update_enabled) lcdui_print_status_line();
}
void IncrementLoadFails(){ void IncrementLoadFails(){
eeprom_increment_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL); eeprom_increment_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL);
eeprom_increment_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT); eeprom_increment_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT);
@ -332,6 +349,10 @@ void IncrementMMUFails(){
eeprom_increment_word((uint16_t *)EEPROM_MMU_FAIL_TOT); eeprom_increment_word((uint16_t *)EEPROM_MMU_FAIL_TOT);
} }
bool cutter_enabled(){
return eeprom_read_byte((uint8_t*)EEPROM_MMU_CUTTER_ENABLED) == EEPROM_MMU_CUTTER_ENABLED_enabled;
}
void MakeSound(SoundType s){ void MakeSound(SoundType s){
Sound_MakeSound( (eSOUND_TYPE)s); Sound_MakeSound( (eSOUND_TYPE)s);
} }

View File

@ -2,12 +2,19 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#ifdef __AVR__
#include "mmu2/error_codes.h"
#include "mmu2/progress_codes.h"
#else
#include "../../../../../../Prusa-Firmware-MMU/src/logic/error_codes.h"
#include "../../../../../../Prusa-Firmware-MMU/src/logic/progress_codes.h"
#endif
namespace MMU2 { namespace MMU2 {
enum CommandInProgress : uint8_t { enum CommandInProgress : uint8_t {
NoCommand = 0, NoCommand = 0,
CutFilament = 'C', CutFilament = 'K',
EjectFilament = 'E', EjectFilament = 'E',
Homing = 'H', Homing = 'H',
LoadFilament = 'L', LoadFilament = 'L',
@ -17,10 +24,10 @@ enum CommandInProgress : uint8_t {
}; };
/// Called at the begin of every MMU operation /// Called at the begin of every MMU operation
void BeginReport(CommandInProgress cip, uint16_t ec); void BeginReport(CommandInProgress cip, ProgressCode ec);
/// Called at the end of every MMU operation /// Called at the end of every MMU operation
void EndReport(CommandInProgress cip, uint16_t ec); void EndReport(CommandInProgress cip, ProgressCode ec);
/// Return true if the printer's LCD is drawing the error screen /// Return true if the printer's LCD is drawing the error screen
bool isErrorScreenRunning(); bool isErrorScreenRunning();
@ -35,24 +42,31 @@ bool TuneMenuEntered();
/// and allow the MMU and printer to communicate with each other. /// and allow the MMU and printer to communicate with each other.
/// @param[in] ec error code /// @param[in] ec error code
/// @param[in] es error source /// @param[in] es error source
void ReportErrorHook(CommandInProgress cip, uint16_t ec, uint8_t es); void ReportErrorHook(CommandInProgress cip, ErrorCode ec, uint8_t es);
/// Called when the MMU sends operation progress update /// Called when the MMU sends operation progress update
void ReportProgressHook(CommandInProgress cip, uint16_t ec); void ReportProgressHook(CommandInProgress cip, ProgressCode ec);
/// @brief Clear the status line and setup the LCD cursor struct TryLoadUnloadReporter {
void TryLoadUnloadProgressbarInit(); TryLoadUnloadReporter(float delta_mm);
~TryLoadUnloadReporter();
void Progress(bool sensorState);
void DumpToSerial();
/// @brief Clear the status line and setup the LCD cursor private:
void TryLoadUnloadProgressbarDeinit(); /// @brief Add one block to the progress bar
/// @param col pixel position on the LCD status line, should range from 0 to (LCD_WIDTH - 1)
/// @param sensorState if true, filament is not present, else filament is present. This controls which character to render
void Render(uint8_t col, bool sensorState);
/// @brief Report the results to serial uint8_t dpixel0;
void TryLoadUnloadProgressbarEcho(); uint8_t dpixel1;
uint8_t lcd_cursor_col;
/// @brief Add one block to the progress bar // The total length is twice delta_mm. Divide that length by number of pixels
/// @param col pixel position on the LCD status line, should range from 0 to (LCD_WIDTH - 1) // available to get length per pixel.
/// @param sensorState if true, filament is not present, else filament is present. This controls which character to render // Note: Below is the reciprocal of (2 * delta_mm) / LCD_WIDTH [mm/pixel]
void TryLoadUnloadProgressbar(uint8_t col, bool sensorState); float pixel_per_mm;
};
/// Remders the sensor status line. Also used by the "resume temperature" screen. /// Remders the sensor status line. Also used by the "resume temperature" screen.
void ReportErrorHookDynamicRender(); void ReportErrorHookDynamicRender();
@ -74,6 +88,9 @@ void IncrementLoadFails();
/// Increments EEPROM cell - number of MMU errors /// Increments EEPROM cell - number of MMU errors
void IncrementMMUFails(); void IncrementMMUFails();
/// @returns true when Cutter is enabled in the menus
bool cutter_enabled();
// Beware: enum values intentionally chosen to match the 8bit FW to save code size // Beware: enum values intentionally chosen to match the 8bit FW to save code size
enum SoundType { enum SoundType {
Prompt = 2, Prompt = 2,
@ -93,4 +110,4 @@ void ScreenClear();
void tuneIdlerStallguardThreshold(); void tuneIdlerStallguardThreshold();
} // namespace } // namespace MMU2

View File

@ -189,20 +189,17 @@ void checkHitEndstops()
{ {
if(endstop_hit) { if(endstop_hit) {
#ifdef VERBOSE_CHECK_HIT_ENDSTOPS #ifdef VERBOSE_CHECK_HIT_ENDSTOPS
SERIAL_ECHO_START; SERIAL_ECHO_START;
SERIAL_ECHORPGM(MSG_ENDSTOPS_HIT); SERIAL_ECHORPGM(PSTR("Endstops Hit"));
if(endstop_hit & _BV(X_AXIS)) { for (uint8_t axis = 0; axis < E_AXIS; axis++) // XYZ
SERIAL_ECHOPAIR(" X:",(float)endstops_trigsteps[X_AXIS]/cs.axis_steps_per_mm[X_AXIS]); {
// LCD_MESSAGERPGM(CAT2((MSG_ENDSTOPS_HIT), PSTR("X"))); if(endstop_hit & _BV(axis)) {
} SERIAL_ECHO(' ');
if(endstop_hit & _BV(Y_AXIS)) { SERIAL_ECHO(char('X' + axis));
SERIAL_ECHOPAIR(" Y:",(float)endstops_trigsteps[Y_AXIS]/cs.axis_steps_per_mm[Y_AXIS]); SERIAL_ECHO(':');
// LCD_MESSAGERPGM(CAT2((MSG_ENDSTOPS_HIT), PSTR("Y"))); SERIAL_ECHO(float(endstops_trigsteps[axis]) / cs.axis_steps_per_mm[axis]);
} }
if(endstop_hit & _BV(Z_AXIS)) { }
SERIAL_ECHOPAIR(" Z:",(float)endstops_trigsteps[Z_AXIS]/cs.axis_steps_per_mm[Z_AXIS]);
// LCD_MESSAGERPGM(CAT2((MSG_ENDSTOPS_HIT),PSTR("Z")));
}
SERIAL_ECHOLN(""); SERIAL_ECHOLN("");
#endif //VERBOSE_CHECK_HIT_ENDSTOPS #endif //VERBOSE_CHECK_HIT_ENDSTOPS
endstop_hit = 0; endstop_hit = 0;

View File

@ -137,7 +137,7 @@ struct TMCInitParams {
inline explicit TMCInitParams(bool bSuppressFlag, bool enableECool):bSuppressFlag(bSuppressFlag), enableECool(enableECool) { } inline explicit TMCInitParams(bool bSuppressFlag, bool enableECool):bSuppressFlag(bSuppressFlag), enableECool(enableECool) { }
inline explicit TMCInitParams(bool enableECool) inline explicit TMCInitParams(bool enableECool)
: bSuppressFlag( : bSuppressFlag(
#ifdef PSU_delta #ifdef PSU_Delta
1 1
#else #else
0 0

View File

@ -241,6 +241,8 @@ static void lcd_sheet_menu();
static void menu_action_sdfile(const char* filename); static void menu_action_sdfile(const char* filename);
static void menu_action_sddirectory(const char* filename); static void menu_action_sddirectory(const char* filename);
static void lcd_rehome_xy();
#define ENCODER_FEEDRATE_DEADZONE 10 #define ENCODER_FEEDRATE_DEADZONE 10
#define STATE_NA 255 #define STATE_NA 255
@ -277,40 +279,34 @@ static void lcd_implementation_drawmenu_sddirectory(uint8_t row, const char* lon
static void menu_item_sddir(const char* str_fn, char* str_fnl) static void menu_item_sddir(const char* str_fn, char* str_fnl)
{ {
if (menu_item == menu_line) if (lcd_draw_update)
{ {
if (lcd_draw_update) lcd_implementation_drawmenu_sddirectory(menu_row, (str_fnl[0] == '\0') ? str_fn : str_fnl);
{ }
lcd_implementation_drawmenu_sddirectory(menu_row, (str_fnl[0] == '\0') ? str_fn : str_fnl); if (menu_clicked && (lcd_encoder == menu_item))
} {
if (menu_clicked && (lcd_encoder == menu_item)) lcd_update_enabled = false;
{ menu_action_sddirectory(str_fn);
lcd_update_enabled = false; lcd_update_enabled = true;
menu_action_sddirectory(str_fn); menu_item_ret();
lcd_update_enabled = true; return;
menu_item_ret();
return;
}
} }
menu_item++; menu_item++;
} }
static void menu_item_sdfile(const char* str_fn, char* str_fnl) static void menu_item_sdfile(const char* str_fn, char* str_fnl)
{ {
if (menu_item == menu_line) if (lcd_draw_update)
{ {
if (lcd_draw_update) lcd_implementation_drawmenu_sdfile(menu_row, (str_fnl[0] == '\0') ? str_fn : str_fnl);
{ }
lcd_implementation_drawmenu_sdfile(menu_row, (str_fnl[0] == '\0') ? str_fn : str_fnl); if (menu_clicked && (lcd_encoder == menu_item))
} {
if (menu_clicked && (lcd_encoder == menu_item)) lcd_update_enabled = false;
{ menu_action_sdfile(str_fn);
lcd_update_enabled = false; lcd_update_enabled = true;
menu_action_sdfile(str_fn); menu_item_ret();
lcd_update_enabled = true; return;
menu_item_ret();
return;
}
} }
menu_item++; menu_item++;
} }
@ -5298,7 +5294,7 @@ static void lcd_main_menu()
if(!isPrintPaused && (custom_message_type != CustomMsg::Resuming)) MENU_ITEM_SUBMENU_P(_T(MSG_CALIBRATION), lcd_calibration_menu); if(!isPrintPaused && (custom_message_type != CustomMsg::Resuming)) MENU_ITEM_SUBMENU_P(_T(MSG_CALIBRATION), lcd_calibration_menu);
} }
if (!usb_timer.running() && (lcd_commands_type == LcdCommands::Idle)) { if (!usb_timer.running()) {
MENU_ITEM_SUBMENU_P(_i("Statistics"), lcd_menu_statistics);////MSG_STATISTICS c=18 MENU_ITEM_SUBMENU_P(_i("Statistics"), lcd_menu_statistics);////MSG_STATISTICS c=18
} }
@ -5335,6 +5331,15 @@ void stepper_timer_overflow() {
} }
#endif /* DEBUG_STEPPER_TIMER_MISSED */ #endif /* DEBUG_STEPPER_TIMER_MISSED */
static void lcd_rehome_xy() {
// Do home directly, G28 X Y resets MBL, which could be bad.
homeaxis(X_AXIS);
homeaxis(Y_AXIS);
lcd_setstatuspgm(_T(MSG_AUTO_HOME));
lcd_return_to_status();
lcd_draw_update = 3;
}
static void lcd_colorprint_change() { static void lcd_colorprint_change() {
@ -5441,7 +5446,9 @@ static void lcd_tune_menu()
if (!farm_mode) if (!farm_mode)
MENU_ITEM_FUNCTION_P(_T(MSG_FILAMENTCHANGE), lcd_colorprint_change); MENU_ITEM_FUNCTION_P(_T(MSG_FILAMENTCHANGE), lcd_colorprint_change);
#endif #endif
if (isPrintPaused) {// Don't allow rehome if actively printing. Maaaaybe it could work to insert on the fly, seems too risky.
MENU_ITEM_FUNCTION_P(_T(MSG_AUTO_HOME), lcd_rehome_xy);
}
#ifdef FILAMENT_SENSOR #ifdef FILAMENT_SENSOR
MENU_ITEM_SUBMENU_P(_T(MSG_FSENSOR), lcd_fsensor_settings_menu); MENU_ITEM_SUBMENU_P(_T(MSG_FSENSOR), lcd_fsensor_settings_menu);
#endif //FILAMENT_SENSOR #endif //FILAMENT_SENSOR
@ -5477,7 +5484,7 @@ static void mbl_mesh_toggle() {
} }
static void mbl_probe_nr_toggle() { static void mbl_probe_nr_toggle() {
mbl_z_probe_nr = eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR); uint8_t mbl_z_probe_nr = eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR);
switch (mbl_z_probe_nr) { switch (mbl_z_probe_nr) {
case 1: mbl_z_probe_nr = 3; break; case 1: mbl_z_probe_nr = 3; break;
case 3: mbl_z_probe_nr = 5; break; case 3: mbl_z_probe_nr = 5; break;
@ -5492,6 +5499,7 @@ static void lcd_mesh_bed_leveling_settings()
bool magnet_elimination = (eeprom_read_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION) > 0); bool magnet_elimination = (eeprom_read_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION) > 0);
uint8_t points_nr = eeprom_read_byte((uint8_t*)EEPROM_MBL_POINTS_NR); uint8_t points_nr = eeprom_read_byte((uint8_t*)EEPROM_MBL_POINTS_NR);
uint8_t mbl_z_probe_nr = eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR);
char sToggle[4]; //enough for nxn format char sToggle[4]; //enough for nxn format
MENU_BEGIN(); MENU_BEGIN();
@ -6188,7 +6196,7 @@ static bool lcd_selfcheck_axis_sg(uint8_t axis) {
float max_error_mm = 5; float max_error_mm = 5;
switch (axis) { switch (axis) {
case 0: axis_length = X_MAX_POS; break; case 0: axis_length = X_MAX_POS; break;
case 1: axis_length = Y_MAX_POS + 8; break; case 1: axis_length = Y_MAX_POS - Y_MIN_POS + 4; break;
default: axis_length = 210; break; default: axis_length = 210; break;
} }

View File

@ -273,12 +273,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -274,12 +274,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -273,12 +273,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -274,12 +274,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -420,12 +420,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -421,12 +421,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -423,12 +423,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -424,12 +424,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -425,12 +425,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -427,12 +427,6 @@
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 24
#define MESH_MAX_X 228
#define MESH_MIN_Y 6
#define MESH_MAX_Y 210
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -202,12 +202,6 @@ BED SETTINGS
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 35
#define MESH_MAX_X 238
#define MESH_MIN_Y 6
#define MESH_MAX_Y 202
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -201,12 +201,6 @@ BED SETTINGS
#define MBL_Z_STEP 0.01 #define MBL_Z_STEP 0.01
// Mesh definitions
#define MESH_MIN_X 35
#define MESH_MAX_X 238
#define MESH_MIN_Y 6
#define MESH_MAX_Y 202
// Mesh upsample definition // Mesh upsample definition
#define MESH_NUM_X_POINTS 7 #define MESH_NUM_X_POINTS 7
#define MESH_NUM_Y_POINTS 7 #define MESH_NUM_Y_POINTS 7

View File

@ -40,10 +40,30 @@ import os
from lib import charset as cs from lib import charset as cs
from lib.io import load_map from lib.io import load_map
import enum
COLORIZE = (stdout.isatty() and os.getenv("TERM", "dumb") != "dumb") or os.getenv('NO_COLOR') == "0" COLORIZE = (stdout.isatty() and os.getenv("TERM", "dumb") != "dumb") or os.getenv('NO_COLOR') == "0"
LCD_WIDTH = 20 LCD_WIDTH = 20
GH_ANNOTATIONS = os.getenv('GH_ANNOTATIONS') == "1"
CURRENT_PO = "Unknown file"
GH_ERR_COUNT = 0
class AN_TYPE(enum.Enum):
def __new__(cls, *args, **kwds):
value = len(cls.__members__) + 1
obj = object.__new__(cls)
obj._value_ = value
return obj
def __init__(self, a, b):
self.prefix = a
self.print_fmt = b
ERROR = "error", "[E]"
WARNING = "warning", "[W]"
NOTICE = "notice", "[S]"
def color_maybe(color_attr, text): def color_maybe(color_attr, text):
if COLORIZE: if COLORIZE:
return '\033[0;' + str(color_attr) + 'm' + text + '\033[0m' return '\033[0;' + str(color_attr) + 'm' + text + '\033[0m'
@ -119,6 +139,33 @@ def ign_char_first(c):
def ign_char_last(c): def ign_char_last(c):
return c.isalnum() or c in {'.', "'"} return c.isalnum() or c in {'.', "'"}
# Print_anyway is used to reduce code copypasta.
# specifically, if we have all the info here to construct the "normal" message as well, it's done here
def gh_annotate(an_type, start_line, message, end_line = None, print_anyway = False):
if not GH_ANNOTATIONS:
if print_anyway:
if end_line is not None:
line_text = "lines {}-{}".format(start_line, end_line)
else:
line_text = "line {}".format(start_line)
message_simple = "{} on {}".format(message, line_text)
if an_type == AN_TYPE.ERROR:
print(red("{}: {}".format(an_type.print_fmt, message_simple)))
else:
print(yellow("{}: {}".format(an_type.print_fmt, message_simple)))
return
if end_line is not None:
line_info = "line={},endLine={}".format(start_line,end_line)
else:
line_info = "line={}".format(start_line)
print("::{} file={},{}::{}".format(an_type.prefix, CURRENT_PO, line_info, message))
if an_type == AN_TYPE.ERROR:
global GH_ERR_COUNT
GH_ERR_COUNT += 1
def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty, warn_same, information, shorter): def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty, warn_same, information, shorter):
"""Check strings to display definition.""" """Check strings to display definition."""
@ -137,10 +184,10 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
# Check comment syntax (non-empty and include a MSG id) # Check comment syntax (non-empty and include a MSG id)
if known_msgid or warn_empty: if known_msgid or warn_empty:
if len(meta) == 0: if len(meta) == 0:
print(red("[E]: Translation doesn't contain any comment metadata on line %d" % line)) gh_annotate(AN_TYPE.ERROR, line, "Translation missing comment metadata", None, True)
return False return False
if not meta.startswith('MSG'): if not meta.startswith('MSG'):
print(red("[E]: Critical syntax error: comment doesn't start with MSG on line %d" % line)) gh_annotate(AN_TYPE.ERROR, line, "Critical Syntax Error: comment doesn't start with MSG", None, True)
print(red(" comment: " + meta)) print(red(" comment: " + meta))
return False return False
@ -158,29 +205,29 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
else: else:
raise ValueError raise ValueError
except ValueError: except ValueError:
print(red("[E]: Invalid display definition on line %d" % line)) gh_annotate(AN_TYPE.ERROR, line, "Invalid display definition", None, True)
print(red(" definition: " + meta)) print(red(" definition: " + meta))
return False return False
if not cols: if not cols:
if not no_warning and known_msgid and not rows: if not no_warning and known_msgid and not rows:
errors += 1 errors += 1
print(yellow("[W]: No usable display definition on line %d" % line)) gh_annotate(AN_TYPE.WARNING, line, "No usable display definition", None, True)
# probably fullscreen, guess from the message length to continue checking # probably fullscreen, guess from the message length to continue checking
cols = LCD_WIDTH cols = LCD_WIDTH
if cols > LCD_WIDTH: if cols > LCD_WIDTH:
errors += 1 errors += 1
print(yellow("[W]: Invalid column count on line %d" % line)) gh_annotate(AN_TYPE.WARNING, line, "Invalid column count", None, True)
if not rows: if not rows:
rows = 1 rows = 1
elif rows > 1 and cols != LCD_WIDTH: elif rows > 1 and cols != LCD_WIDTH:
errors += 1 errors += 1
print(yellow("[W]: Multiple rows with odd number of columns on line %d" % line)) gh_annotate(AN_TYPE.WARNING, line, "Multiple rows with odd number of columns", None, True)
# Check if translation contains unsupported characters # Check if translation contains unsupported characters
invalid_char = cs.translation_check(cs.unicode_to_source(translation)) invalid_char = cs.translation_check(cs.unicode_to_source(translation))
if invalid_char is not None: if invalid_char is not None:
print(red('[E]: Critical syntax: Unhandled char %s found on line %d' % (repr(invalid_char), line))) gh_annotate(AN_TYPE.ERROR, line, "Critical syntax: Unhandled char %s found".format(repr(invalid_char)), None, True )
print(red(' translation: ' + translation)) print(red(' translation: ' + translation))
return False return False
@ -195,13 +242,13 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
# Incorrect number of rows/cols on the definition # Incorrect number of rows/cols on the definition
if rows == 1 and (len(source) > cols or rows_count_source > rows): if rows == 1 and (len(source) > cols or rows_count_source > rows):
errors += 1 errors += 1
print(yellow('[W]: Source text longer than %d cols as defined on line %d:' % (cols, line))) gh_annotate(AN_TYPE.WARNING, line, "Source text longer than %d cols as defined".format(cols), None, True)
print_ruler(4, cols); print_ruler(4, cols);
print_truncated(source, cols) print_truncated(source, cols)
print() print()
elif rows_count_source > rows: elif rows_count_source > rows:
errors += 1 errors += 1
print(yellow('[W]: Wrapped source text longer than %d rows as defined on line %d:' % (rows, line))) gh_annotate(AN_TYPE.WARNING, line, "Source text longer than %d rows as defined".format(rows), None, True)
print_ruler(6, cols); print_ruler(6, cols);
print_wrapped(wrapped_source, rows, cols) print_wrapped(wrapped_source, rows, cols)
print() print()
@ -214,9 +261,9 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
if len(translation) == 0 and (warn_empty or (not no_warning and known_msgid)): if len(translation) == 0 and (warn_empty or (not no_warning and known_msgid)):
errors += 1 errors += 1
if rows == 1: if rows == 1:
print(yellow("[W]: Empty translation for \"%s\" on line %d" % (source, line))) gh_annotate(AN_TYPE.WARNING, line, "Empty translation for \"{}\"".format(source), line + rows, True )
else: else:
print(yellow("[W]: Empty translation on line %d" % line)) gh_annotate(AN_TYPE.WARNING, line, "Empty translation", line + rows, True )
print_ruler(6, cols); print_ruler(6, cols);
print_wrapped(wrapped_source, rows, cols) print_wrapped(wrapped_source, rows, cols)
print() print()
@ -224,6 +271,7 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
# Check for translation length too long # Check for translation length too long
if (rows_count_translation > rows) or (rows == 1 and len(translation) > cols): if (rows_count_translation > rows) or (rows == 1 and len(translation) > cols):
errors += 1 errors += 1
gh_annotate(AN_TYPE.ERROR, line, "Text is longer than definition", line + rows)
print(red('[E]: Text is longer than definition on line %d: cols=%d rows=%d (rows diff=%d)' print(red('[E]: Text is longer than definition on line %d: cols=%d rows=%d (rows diff=%d)'
% (line, cols, rows, rows_count_translation-rows))) % (line, cols, rows, rows_count_translation-rows)))
print_source_translation(source, translation, print_source_translation(source, translation,
@ -232,6 +280,7 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
# Check for translation length shorter # Check for translation length shorter
if shorter and (rows_count_translation < rows-1): if shorter and (rows_count_translation < rows-1):
gh_annotate(AN_TYPE.NOTICE, line, "Text is shorter than definition", line + rows)
print(yellow('[S]: Text is shorter than definition on line %d: cols=%d rows=%d (rows diff=%d)' print(yellow('[S]: Text is shorter than definition on line %d: cols=%d rows=%d (rows diff=%d)'
% (line, cols, rows, rows_count_translation-rows))) % (line, cols, rows, rows_count_translation-rows)))
print_source_translation(source, translation, print_source_translation(source, translation,
@ -241,7 +290,7 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
# Different count of % sequences # Different count of % sequences
if source.count('%') != translation.count('%') and len(translation) > 0: if source.count('%') != translation.count('%') and len(translation) > 0:
errors += 1 errors += 1
print(red('[E]: Unequal count of %% escapes on line %d:' % (line))) gh_annotate(AN_TYPE.ERROR, line, "Unequal count of %% escapes", None, True)
print_source_translation(source, translation, print_source_translation(source, translation,
wrapped_source, wrapped_translation, wrapped_source, wrapped_translation,
rows, cols) rows, cols)
@ -254,14 +303,14 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
end_diff = not (ign_char_last(source_end) and ign_char_last(translation_end)) and source_end != translation_end end_diff = not (ign_char_last(source_end) and ign_char_last(translation_end)) and source_end != translation_end
if start_diff or end_diff: if start_diff or end_diff:
if start_diff: if start_diff:
print(yellow('[S]: Differing first punctuation character (%s => %s) on line %d:' % (source[0], translation[0], line))) gh_annotate(AN_TYPE.NOTICE, line, "Differing first punctuation character: ({} => {})".format(source[0],translation[0]), None, True)
if end_diff: if end_diff:
print(yellow('[S]: Differing last punctuation character (%s => %s) on line %d:' % (source[-1], translation[-1], line))) gh_annotate(AN_TYPE.NOTICE, line, "Differing last punctuation character: ({} => {})".format(source[-1],translation[-1]), None, True)
print_source_translation(source, translation, print_source_translation(source, translation,
wrapped_source, wrapped_translation, wrapped_source, wrapped_translation,
rows, cols) rows, cols)
if not no_suggest and source == translation and (warn_same or len(source.split(' ', 1)) > 1): if not no_suggest and source == translation and (warn_same or len(source.split(' ', 1)) > 1):
print(yellow('[S]: Translation same as original on line %d:' %line)) gh_annotate(AN_TYPE.NOTICE, line, "Translation same as original text", None, True)
print_source_translation(source, translation, print_source_translation(source, translation,
wrapped_source, wrapped_translation, wrapped_source, wrapped_translation,
rows, cols) rows, cols)
@ -269,7 +318,7 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
# Short translation # Short translation
if not no_suggest and len(source) > 0 and len(translation) > 0: if not no_suggest and len(source) > 0 and len(translation) > 0:
if len(translation.rstrip()) < len(source.rstrip()) / 2: if len(translation.rstrip()) < len(source.rstrip()) / 2:
print(yellow('[S]: Short translation on line %d:' % (line))) gh_annotate(AN_TYPE.NOTICE, line, "Short translation", None, True)
print_source_translation(source, translation, print_source_translation(source, translation,
wrapped_source, wrapped_translation, wrapped_source, wrapped_translation,
rows, cols) rows, cols)
@ -280,7 +329,7 @@ def check_translation(entry, msgids, is_pot, no_warning, no_suggest, warn_empty,
translation.rstrip() != translation and \ translation.rstrip() != translation and \
(rows > 1 or len(translation) != len(source)): (rows > 1 or len(translation) != len(source)):
errors += 1 errors += 1
print(yellow('[W]: Incorrect trailing whitespace for translation on line %d:' % (line))) gh_annotate(AN_TYPE.WARNING, line, "Incorrect trailing whitespace for translation", None, True)
source = highlight_trailing_white(source) source = highlight_trailing_white(source)
translation = highlight_trailing_white(translation) translation = highlight_trailing_white(translation)
wrapped_translation = highlight_trailing_white(wrapped_translation) wrapped_translation = highlight_trailing_white(wrapped_translation)
@ -350,9 +399,14 @@ def main():
# check each translation in turn # check each translation in turn
status = True status = True
for translation in polib.pofile(args.po): for translation in polib.pofile(args.po):
global CURRENT_PO
CURRENT_PO=args.po
status &= check_translation(translation, msgids, args.pot, args.no_warning, args.no_suggest, status &= check_translation(translation, msgids, args.pot, args.no_warning, args.no_suggest,
args.warn_empty, args.warn_same, args.information, args.shorter) args.warn_empty, args.warn_same, args.information, args.shorter)
return 0 if status else 1 if GH_ANNOTATIONS:
return GH_ERR_COUNT > 0 # Do not cause a failure if only warnings or notices.
else:
return 0 if status else 1
if __name__ == "__main__": if __name__ == "__main__":
exit(main()) exit(main())

View File

@ -1209,6 +1209,11 @@ msgstr ""
msgid "Mesh Bed Leveling" msgid "Mesh Bed Leveling"
msgstr "" msgstr ""
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr ""
#. MSG_MODE c=6 #. MSG_MODE c=6
#: ../../Firmware/messages.cpp:107 ../../Firmware/ultralcd.cpp:4122 #: ../../Firmware/messages.cpp:107 ../../Firmware/ultralcd.cpp:4122
#: ../../Firmware/ultralcd.cpp:4126 ../../Firmware/ultralcd.cpp:4134 #: ../../Firmware/ultralcd.cpp:4126 ../../Firmware/ultralcd.cpp:4134

View File

@ -2550,6 +2550,11 @@ msgstr "Výměna filamentu M600. Vložte nový filament nebo vysuňte starý."
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Citlivost" msgstr "Citlivost"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Mesh Bed Leveling selhal. Spusťte kalibraci osy Z."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Vyjmete stary filament a stisknete tlacitko pro zavedeni noveho." #~ msgstr "Vyjmete stary filament a stisknete tlacitko pro zavedeni noveho."

View File

@ -2578,6 +2578,11 @@ msgstr ""
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Sensitivität" msgstr "Sensitivität"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "MeshBett Ausgleich fehlgeschlagen. Z Kalibrierung ausführen."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Entferne das alte Fil. und drücke den Knopf, um das neue zu laden." #~ msgstr "Entferne das alte Fil. und drücke den Knopf, um das neue zu laden."

View File

@ -2574,6 +2574,11 @@ msgstr ""
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Sensibilidad" msgstr "Sensibilidad"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Nivelacion fallada. Ejecute la calibración Z."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "" #~ msgstr ""
#~ "Retira el fil. viejo y presione el dial para comenzar a cargar el nuevo." #~ "Retira el fil. viejo y presione el dial para comenzar a cargar el nuevo."

View File

@ -2589,6 +2589,11 @@ msgstr ""
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Sensibilité" msgstr "Sensibilité"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr ""
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "" #~ msgstr ""
#~ "Retirez l'ancien fil. puis appuyez sur le bouton pour charger le nouveau." #~ "Retirez l'ancien fil. puis appuyez sur le bouton pour charger le nouveau."

View File

@ -2567,6 +2567,11 @@ msgstr "Promjena filamenta M600. Stavite novu nit ili izbacite staru."
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Osjetljivost" msgstr "Osjetljivost"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Niveliranje podloge nije uspijelo. Pokrenite Z kalibraciju."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Uklonite stari fil. i pritisnite gumb za pocetak stavljanja novog." #~ msgstr "Uklonite stari fil. i pritisnite gumb za pocetak stavljanja novog."

View File

@ -2572,6 +2572,11 @@ msgstr ""
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Érzékenység" msgstr "Érzékenység"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Sikertelen asztal szintezés. Kérjük, futtasd a Z kalibrálást."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Vedd ki a regi fil., majd nyomd meg a gombot az uj fil. betoltesehez." #~ msgstr "Vedd ki a regi fil., majd nyomd meg a gombot az uj fil. betoltesehez."

View File

@ -2573,6 +2573,11 @@ msgstr ""
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Sensibilità" msgstr "Sensibilità"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Livellamento piano fallito. Si prega di eseguire la calibrazione Z."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Rimuovi il fil. precedente e premi la manopola per caricare il nuovo." #~ msgstr "Rimuovi il fil. precedente e premi la manopola per caricare il nuovo."

View File

@ -2575,6 +2575,11 @@ msgstr "M600-filamentwissel. Laad een nieuw filament of werp het oude uit."
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Sensitiviteit" msgstr "Sensitiviteit"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Bed leveling mislukt. Voer de Z-kalibratie uit."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "" #~ msgstr ""
#~ "Verwijder de oude filament en druk op de knop om nieuwe filament te laden." #~ "Verwijder de oude filament en druk op de knop om nieuwe filament te laden."

View File

@ -2549,6 +2549,11 @@ msgstr "M600 filamentskifte. Sett inn en ny filament eller løs ut den gamle."
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Sensitivitet" msgstr "Sensitivitet"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Sengeplanering feilet. Kjør Z-kalibrering."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Ta bort det gamle filamentet og trykk valghjulet for å laste et nytt." #~ msgstr "Ta bort det gamle filamentet og trykk valghjulet for å laste et nytt."

View File

@ -2568,6 +2568,11 @@ msgstr "Załaduj nowy filament lub wyładuj poprzedni."
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Wrażliwość" msgstr "Wrażliwość"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Poziomowanie stołu nieudane. Proszę uruchomić kalibrację Z."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Wyciagnij poprzedni filament i nacisnij pokretlo aby zaladowac nowy." #~ msgstr "Wyciagnij poprzedni filament i nacisnij pokretlo aby zaladowac nowy."

View File

@ -2573,6 +2573,11 @@ msgstr ""
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Sensibilitate" msgstr "Sensibilitate"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Nivelarea patului a eșuat. Rulează Calibrare Z."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Scoateti fil. vechi si apasati butonul pentru a incarca nou." #~ msgstr "Scoateti fil. vechi si apasati butonul pentru a incarca nou."

View File

@ -2555,6 +2555,11 @@ msgstr "Výmena filamentu M600. Vložte nový filament alebo vysuňte starý."
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Citlivosť" msgstr "Citlivosť"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Vyrovnanie platne zlyhalo. Spustite kalibráciu Z."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Vyberte stary filament a stlacte tlacidlo pre zavedenie noveho." #~ msgstr "Vyberte stary filament a stlacte tlacidlo pre zavedenie noveho."

View File

@ -2562,6 +2562,11 @@ msgstr "M600 filamentbyte. Ladda en ny filament eller mata ut den gamla."
msgid "Sensitivity" msgid "Sensitivity"
msgstr "Känslighet" msgstr "Känslighet"
#. MSG_MBL_FAILED_Z_CAL c=20 r=4
#: ../../Firmware/Marlin_main.cpp:2976
msgid "Mesh bed leveling failed. Please run Z calibration."
msgstr "Bäddnivelleringen felade. Kör Z-kalibrering."
#~ msgid "Remove old filament and press the knob to start loading new filament." #~ msgid "Remove old filament and press the knob to start loading new filament."
#~ msgstr "Ta bort det gamla fil. och tryck på knappen för att börja ladda nytt." #~ msgstr "Ta bort det gamla fil. och tryck på knappen för att börja ladda nytt."

View File

@ -16,3 +16,27 @@ add_executable(tests ${TEST_SOURCES})
target_include_directories(tests PRIVATE tests) target_include_directories(tests PRIVATE tests)
target_link_libraries(tests Catch2::Catch2WithMain) target_link_libraries(tests Catch2::Catch2WithMain)
catch_discover_tests(tests) catch_discover_tests(tests)
set(ctest_test_args --output-on-failure)
include(ProcessorCount)
ProcessorCount(N)
if(N EQUAL 0)
message(
WARNING "CTest: There was an issue reading the core count, tests won't be run in parallel"
)
else()
message(STATUS "CTest: Detected ${N} CPU threads")
set(ctest_test_args -j${N} ${ctest_test_args})
endif()
# This step needs to always return OK but log whether it was successful or not. The thought here
# is that if the tests all pass, .ctest-finished is created and we can check for its existance
# after generating the report to determine if the overall build result is a pass or fail.
add_custom_target(
test_run_all
COMMAND ${CMAKE_CTEST_COMMAND} ${ctest_test_args}
COMMAND ${CMAKE_COMMAND} -E touch .ctest-finished || exit 0
BYPRODUCTS ${PROJECT_BINARY_DIR}/.ctest-finished
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
)