My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bedlevel_tools.cpp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * Bed Level Tools for Pro UI
  24. * Extended by: Miguel A. Risco-Castillo (MRISCOC)
  25. * Version: 2.0.0
  26. * Date: 2022/05/23
  27. *
  28. * Based on the original work of: Henri-J-Norden
  29. * https://github.com/Jyers/Marlin/pull/126
  30. */
  31. #include "../../../inc/MarlinConfigPre.h"
  32. #include "bedlevel_tools.h"
  33. #if BOTH(DWIN_LCD_PROUI, HAS_LEVELING)
  34. #include "../../marlinui.h"
  35. #include "../../../core/types.h"
  36. #include "dwin.h"
  37. #include "dwinui.h"
  38. #include "dwin_popup.h"
  39. #include "../../../feature/bedlevel/bedlevel.h"
  40. #include "../../../module/probe.h"
  41. #include "../../../gcode/gcode.h"
  42. #include "../../../module/planner.h"
  43. #include "../../../gcode/queue.h"
  44. #include "../../../libs/least_squares_fit.h"
  45. #include "../../../libs/vector_3.h"
  46. BedLevelToolsClass BedLevelTools;
  47. #if USE_UBL_VIEWER
  48. bool BedLevelToolsClass::viewer_asymmetric_range = false;
  49. bool BedLevelToolsClass::viewer_print_value = false;
  50. #endif
  51. bool BedLevelToolsClass::goto_mesh_value = false;
  52. uint8_t BedLevelToolsClass::mesh_x = 0;
  53. uint8_t BedLevelToolsClass::mesh_y = 0;
  54. uint8_t BedLevelToolsClass::tilt_grid = 1;
  55. bool drawing_mesh = false;
  56. char cmd[MAX_CMD_SIZE+16], str_1[16], str_2[16], str_3[16];
  57. #if ENABLED(AUTO_BED_LEVELING_UBL)
  58. void BedLevelToolsClass::manual_value_update(const uint8_t mesh_x, const uint8_t mesh_y, bool undefined/*=false*/) {
  59. sprintf_P(cmd, PSTR("M421 I%i J%i Z%s %s"), mesh_x, mesh_y, dtostrf(current_position.z, 1, 3, str_1), undefined ? "N" : "");
  60. gcode.process_subcommands_now(cmd);
  61. planner.synchronize();
  62. }
  63. bool BedLevelToolsClass::create_plane_from_mesh() {
  64. struct linear_fit_data lsf_results;
  65. incremental_LSF_reset(&lsf_results);
  66. GRID_LOOP(x, y) {
  67. if (!isnan(bedlevel.z_values[x][y])) {
  68. xy_pos_t rpos = { bedlevel.get_mesh_x(x), bedlevel.get_mesh_y(y) };
  69. incremental_LSF(&lsf_results, rpos, bedlevel.z_values[x][y]);
  70. }
  71. }
  72. if (finish_incremental_LSF(&lsf_results)) {
  73. SERIAL_ECHOPGM("Could not complete LSF!");
  74. return true;
  75. }
  76. bedlevel.set_all_mesh_points_to_value(0);
  77. matrix_3x3 rotation = matrix_3x3::create_look_at(vector_3(lsf_results.A, lsf_results.B, 1));
  78. GRID_LOOP(i, j) {
  79. float mx = bedlevel.get_mesh_x(i),
  80. my = bedlevel.get_mesh_y(j),
  81. mz = bedlevel.z_values[i][j];
  82. if (DEBUGGING(LEVELING)) {
  83. DEBUG_ECHOPAIR_F("before rotation = [", mx, 7);
  84. DEBUG_CHAR(',');
  85. DEBUG_ECHO_F(my, 7);
  86. DEBUG_CHAR(',');
  87. DEBUG_ECHO_F(mz, 7);
  88. DEBUG_ECHOPGM("] ---> ");
  89. DEBUG_DELAY(20);
  90. }
  91. rotation.apply_rotation_xyz(mx, my, mz);
  92. if (DEBUGGING(LEVELING)) {
  93. DEBUG_ECHOPAIR_F("after rotation = [", mx, 7);
  94. DEBUG_CHAR(',');
  95. DEBUG_ECHO_F(my, 7);
  96. DEBUG_CHAR(',');
  97. DEBUG_ECHO_F(mz, 7);
  98. DEBUG_ECHOLNPGM("]");
  99. DEBUG_DELAY(20);
  100. }
  101. bedlevel.z_values[i][j] = mz - lsf_results.D;
  102. }
  103. return false;
  104. }
  105. #else
  106. void BedLevelToolsClass::manual_value_update(const uint8_t mesh_x, const uint8_t mesh_y) {
  107. sprintf_P(cmd, PSTR("G29 I%i J%i Z%s"), mesh_x, mesh_y, dtostrf(current_position.z, 1, 3, str_1));
  108. gcode.process_subcommands_now(cmd);
  109. planner.synchronize();
  110. }
  111. #endif
  112. void BedLevelToolsClass::manual_move(const uint8_t mesh_x, const uint8_t mesh_y, bool zmove/*=false*/) {
  113. gcode.process_subcommands_now(F("G28O"));
  114. if (zmove) {
  115. planner.synchronize();
  116. current_position.z = goto_mesh_value ? bedlevel.z_values[mesh_x][mesh_y] : Z_CLEARANCE_BETWEEN_PROBES;
  117. planner.buffer_line(current_position, homing_feedrate(Z_AXIS), active_extruder);
  118. planner.synchronize();
  119. }
  120. else {
  121. DWIN_Show_Popup(ICON_BLTouch, F("Moving to Point"), F("Please wait until done."));
  122. HMI_SaveProcessID(NothingToDo);
  123. sprintf_P(cmd, PSTR("G0 F300 Z%s"), dtostrf(Z_CLEARANCE_BETWEEN_PROBES, 1, 3, str_1));
  124. gcode.process_subcommands_now(cmd);
  125. sprintf_P(cmd, PSTR("G42 F4000 I%i J%i"), mesh_x, mesh_y);
  126. gcode.process_subcommands_now(cmd);
  127. planner.synchronize();
  128. current_position.z = goto_mesh_value ? bedlevel.z_values[mesh_x][mesh_y] : Z_CLEARANCE_BETWEEN_PROBES;
  129. planner.buffer_line(current_position, homing_feedrate(Z_AXIS), active_extruder);
  130. planner.synchronize();
  131. HMI_ReturnScreen();
  132. }
  133. }
  134. void BedLevelToolsClass::MoveToXYZ() {
  135. BedLevelTools.goto_mesh_value = true;
  136. BedLevelTools.manual_move(BedLevelTools.mesh_x, BedLevelTools.mesh_y, false);
  137. }
  138. void BedLevelToolsClass::MoveToXY() {
  139. BedLevelTools.goto_mesh_value = false;
  140. BedLevelTools.manual_move(BedLevelTools.mesh_x, BedLevelTools.mesh_y, false);
  141. }
  142. void BedLevelToolsClass::MoveToZ() {
  143. BedLevelTools.goto_mesh_value = true;
  144. BedLevelTools.manual_move(BedLevelTools.mesh_x, BedLevelTools.mesh_y, true);
  145. }
  146. void BedLevelToolsClass::ProbeXY() {
  147. sprintf_P(cmd, PSTR("G30X%sY%s"),
  148. dtostrf(bedlevel.get_mesh_x(BedLevelTools.mesh_x), 1, 2, str_1),
  149. dtostrf(bedlevel.get_mesh_y(BedLevelTools.mesh_y), 1, 2, str_2)
  150. );
  151. gcode.process_subcommands_now(cmd);
  152. }
  153. float BedLevelToolsClass::get_max_value() {
  154. float max = __FLT_MAX__ * -1;
  155. GRID_LOOP(x, y) {
  156. if (!isnan(bedlevel.z_values[x][y]) && bedlevel.z_values[x][y] > max)
  157. max = bedlevel.z_values[x][y];
  158. }
  159. return max;
  160. }
  161. float BedLevelToolsClass::get_min_value() {
  162. float min = __FLT_MAX__;
  163. GRID_LOOP(x, y) {
  164. if (!isnan(bedlevel.z_values[x][y]) && bedlevel.z_values[x][y] < min)
  165. min = bedlevel.z_values[x][y];
  166. }
  167. return min;
  168. }
  169. bool BedLevelToolsClass::meshvalidate() {
  170. float min = __FLT_MAX__, max = __FLT_MAX__ * -1;
  171. GRID_LOOP(x, y) {
  172. if (isnan(bedlevel.z_values[x][y])) return false;
  173. if (bedlevel.z_values[x][y] < min) min = bedlevel.z_values[x][y];
  174. if (bedlevel.z_values[x][y] > max) max = bedlevel.z_values[x][y];
  175. }
  176. return WITHIN(max, MESH_Z_OFFSET_MIN, MESH_Z_OFFSET_MAX);
  177. }
  178. #if USE_UBL_VIEWER
  179. void BedLevelToolsClass::Draw_Bed_Mesh(int16_t selected /*= -1*/, uint8_t gridline_width /*= 1*/, uint16_t padding_x /*= 8*/, uint16_t padding_y_top /*= 40 + 53 - 7*/) {
  180. drawing_mesh = true;
  181. const uint16_t total_width_px = DWIN_WIDTH - padding_x - padding_x;
  182. const uint16_t cell_width_px = total_width_px / (GRID_MAX_POINTS_X);
  183. const uint16_t cell_height_px = total_width_px / (GRID_MAX_POINTS_Y);
  184. const float v_max = abs(get_max_value()), v_min = abs(get_min_value()), range = _MAX(v_min, v_max);
  185. // Clear background from previous selection and select new square
  186. DWIN_Draw_Rectangle(1, Color_Bg_Black, _MAX(0, padding_x - gridline_width), _MAX(0, padding_y_top - gridline_width), padding_x + total_width_px, padding_y_top + total_width_px);
  187. if (selected >= 0) {
  188. const auto selected_y = selected / (GRID_MAX_POINTS_X);
  189. const auto selected_x = selected - (GRID_MAX_POINTS_X) * selected_y;
  190. const auto start_y_px = padding_y_top + selected_y * cell_height_px;
  191. const auto start_x_px = padding_x + selected_x * cell_width_px;
  192. DWIN_Draw_Rectangle(1, Color_White, _MAX(0, start_x_px - gridline_width), _MAX(0, start_y_px - gridline_width), start_x_px + cell_width_px, start_y_px + cell_height_px);
  193. }
  194. // Draw value square grid
  195. char buf[8];
  196. GRID_LOOP(x, y) {
  197. const auto start_x_px = padding_x + x * cell_width_px;
  198. const auto end_x_px = start_x_px + cell_width_px - 1 - gridline_width;
  199. const auto start_y_px = padding_y_top + ((GRID_MAX_POINTS_Y) - y - 1) * cell_height_px;
  200. const auto end_y_px = start_y_px + cell_height_px - 1 - gridline_width;
  201. DWIN_Draw_Rectangle(1, // RGB565 colors: http://www.barth-dev.de/online/rgb565-color-picker/
  202. isnan(bedlevel.z_values[x][y]) ? Color_Grey : ( // gray if undefined
  203. (bedlevel.z_values[x][y] < 0 ?
  204. (uint16_t)round(0x1F * -bedlevel.z_values[x][y] / (!viewer_asymmetric_range ? range : v_min)) << 11 : // red if mesh point value is negative
  205. (uint16_t)round(0x3F * bedlevel.z_values[x][y] / (!viewer_asymmetric_range ? range : v_max)) << 5) | // green if mesh point value is positive
  206. _MIN(0x1F, (((uint8_t)abs(bedlevel.z_values[x][y]) / 10) * 4))), // + blue stepping for every mm
  207. start_x_px, start_y_px, end_x_px, end_y_px
  208. );
  209. safe_delay(10);
  210. LCD_SERIAL.flushTX();
  211. // Draw value text on
  212. if (viewer_print_value) {
  213. int8_t offset_x, offset_y = cell_height_px / 2 - 6;
  214. if (isnan(bedlevel.z_values[x][y])) { // undefined
  215. DWIN_Draw_String(false, font6x12, Color_White, Color_Bg_Blue, start_x_px + cell_width_px / 2 - 5, start_y_px + offset_y, F("X"));
  216. }
  217. else { // has value
  218. if (GRID_MAX_POINTS_X < 10)
  219. sprintf_P(buf, PSTR("%s"), dtostrf(abs(bedlevel.z_values[x][y]), 1, 2, str_1));
  220. else
  221. sprintf_P(buf, PSTR("%02i"), (uint16_t)(abs(bedlevel.z_values[x][y] - (int16_t)bedlevel.z_values[x][y]) * 100));
  222. offset_x = cell_width_px / 2 - 3 * (strlen(buf)) - 2;
  223. if (!(GRID_MAX_POINTS_X < 10))
  224. DWIN_Draw_String(false, font6x12, Color_White, Color_Bg_Blue, start_x_px - 2 + offset_x, start_y_px + offset_y /*+ square / 2 - 6*/, F("."));
  225. DWIN_Draw_String(false, font6x12, Color_White, Color_Bg_Blue, start_x_px + 1 + offset_x, start_y_px + offset_y /*+ square / 2 - 6*/, buf);
  226. }
  227. safe_delay(10);
  228. LCD_SERIAL.flushTX();
  229. }
  230. }
  231. }
  232. void BedLevelToolsClass::Set_Mesh_Viewer_Status() { // TODO: draw gradient with values as a legend instead
  233. float v_max = abs(get_max_value()), v_min = abs(get_min_value()), range = _MAX(v_min, v_max);
  234. if (v_min > 3e+10F) v_min = 0.0000001;
  235. if (v_max > 3e+10F) v_max = 0.0000001;
  236. if (range > 3e+10F) range = 0.0000001;
  237. char msg[46];
  238. if (viewer_asymmetric_range) {
  239. dtostrf(-v_min, 1, 3, str_1);
  240. dtostrf( v_max, 1, 3, str_2);
  241. }
  242. else {
  243. dtostrf(-range, 1, 3, str_1);
  244. dtostrf( range, 1, 3, str_2);
  245. }
  246. sprintf_P(msg, PSTR("Red %s..0..%s Green"), str_1, str_2);
  247. ui.set_status(msg);
  248. drawing_mesh = false;
  249. }
  250. #endif // USE_UBL_VIEWER
  251. #endif // DWIN_LCD_PROUI && HAS_LEVELING