Merge pull request #4183 from leptun/mbl_print_area

MBL print area
This commit is contained in:
3d-gussner 2023-09-13 10:28:08 +02:00 committed by GitHub
commit 2e70697fa7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 289 additions and 538 deletions

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,
@ -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,110 @@ 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 = code_seen('N') ? code_value_uint8() : eeprom_read_byte((uint8_t*)EEPROM_MBL_POINTS_NR);
uint8_t custom_message_state_old = custom_message_state; if (nMeasPoints != 7) {
custom_message_type = CustomMsg::MeshBedLeveling; nMeasPoints = 3;
custom_message_state = (nMeasPoints * nMeasPoints) + 10; }
lcd_update(1);
uint8_t nProbeRetry = code_seen('R') ? code_value_uint8() : eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR);
if (nProbeRetry > 10) {
nProbeRetry = 10;
}
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 +2933,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, 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 && 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, 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 (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(PSTR("Mesh bed leveling failed. Please run Z calibration."));
}
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 +2994,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 +3009,63 @@ 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)
if (nMeasPoints == 7 && eeprom_read_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION)) {
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) {
@ -4981,12 +4882,12 @@ 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 | R | 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 3. Valid values are 3 and 7.
- `R` - Probe retries. Default 3 max. 10 - `R` - Probe retries. Default 3 max. 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 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.");

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

@ -5484,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;
@ -5499,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();

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

@ -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

@ -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