Browse Source

New GCode Parser - Implementation

Scott Lahteine 7 years ago
parent
commit
f4028fe088

+ 3
- 0
Marlin/Conditionals_post.h View File

838
   // Shorthand
838
   // Shorthand
839
   #define GRID_MAX_POINTS ((GRID_MAX_POINTS_X) * (GRID_MAX_POINTS_Y))
839
   #define GRID_MAX_POINTS ((GRID_MAX_POINTS_X) * (GRID_MAX_POINTS_Y))
840
 
840
 
841
+  // Add commands that need sub-codes to this list
842
+  #define USE_GCODE_SUBCODES ENABLED(G38_PROBE_TARGET)
843
+
841
 #endif // CONDITIONALS_POST_H
844
 #endif // CONDITIONALS_POST_H

+ 28
- 30
Marlin/G26_Mesh_Validation_Tool.cpp View File

34
   #include "stepper.h"
34
   #include "stepper.h"
35
   #include "temperature.h"
35
   #include "temperature.h"
36
   #include "ultralcd.h"
36
   #include "ultralcd.h"
37
+  #include "gcode.h"
37
 
38
 
38
   #define EXTRUSION_MULTIPLIER 1.0
39
   #define EXTRUSION_MULTIPLIER 1.0
39
   #define RETRACTION_MULTIPLIER 1.0
40
   #define RETRACTION_MULTIPLIER 1.0
130
   void set_destination_to_current();
131
   void set_destination_to_current();
131
   void set_current_to_destination();
132
   void set_current_to_destination();
132
   void prepare_move_to_destination();
133
   void prepare_move_to_destination();
133
-  float code_value_float();
134
-  float code_value_linear_units();
135
-  float code_value_axis_units(const AxisEnum axis);
136
-  bool code_value_bool();
137
-  bool code_has_value();
134
+  void lcd_setstatuspgm(const char* const message, const uint8_t level);
138
   void sync_plan_position_e();
135
   void sync_plan_position_e();
139
   void chirp_at_user();
136
   void chirp_at_user();
140
 
137
 
625
     g26_hotend_temp           = HOTEND_TEMP;
622
     g26_hotend_temp           = HOTEND_TEMP;
626
     g26_prime_flag            = 0;
623
     g26_prime_flag            = 0;
627
 
624
 
628
-    g26_ooze_amount           = code_seen('O') && code_has_value() ? code_value_linear_units() : OOZE_AMOUNT;
629
-    g26_keep_heaters_on       = code_seen('K') && code_value_bool();
630
-    g26_continue_with_closest = code_seen('C') && code_value_bool();
625
+    g26_ooze_amount           = parser.seen('O') && parser.has_value() ? parser.value_linear_units() : OOZE_AMOUNT;
626
+    g26_keep_heaters_on       = parser.seen('K') && parser.value_bool();
627
+    g26_continue_with_closest = parser.seen('C') && parser.value_bool();
631
 
628
 
632
-    if (code_seen('B')) {
633
-      g26_bed_temp = code_value_temp_abs();
629
+    if (parser.seen('B')) {
630
+      g26_bed_temp = parser.value_celsius();
634
       if (!WITHIN(g26_bed_temp, 15, 140)) {
631
       if (!WITHIN(g26_bed_temp, 15, 140)) {
635
         SERIAL_PROTOCOLLNPGM("?Specified bed temperature not plausible.");
632
         SERIAL_PROTOCOLLNPGM("?Specified bed temperature not plausible.");
636
         return UBL_ERR;
633
         return UBL_ERR;
637
       }
634
       }
638
     }
635
     }
639
 
636
 
640
-    if (code_seen('L')) {
641
-      g26_layer_height = code_value_linear_units();
637
+    if (parser.seen('L')) {
638
+      g26_layer_height = parser.value_linear_units();
642
       if (!WITHIN(g26_layer_height, 0.0, 2.0)) {
639
       if (!WITHIN(g26_layer_height, 0.0, 2.0)) {
643
         SERIAL_PROTOCOLLNPGM("?Specified layer height not plausible.");
640
         SERIAL_PROTOCOLLNPGM("?Specified layer height not plausible.");
644
         return UBL_ERR;
641
         return UBL_ERR;
645
       }
642
       }
646
     }
643
     }
647
 
644
 
648
-    if (code_seen('Q')) {
649
-      if (code_has_value()) {
650
-        g26_retraction_multiplier = code_value_float();
645
+    if (parser.seen('Q')) {
646
+      if (parser.has_value()) {
647
+        g26_retraction_multiplier = parser.value_float();
651
         if (!WITHIN(g26_retraction_multiplier, 0.05, 15.0)) {
648
         if (!WITHIN(g26_retraction_multiplier, 0.05, 15.0)) {
652
           SERIAL_PROTOCOLLNPGM("?Specified Retraction Multiplier not plausible.");
649
           SERIAL_PROTOCOLLNPGM("?Specified Retraction Multiplier not plausible.");
653
           return UBL_ERR;
650
           return UBL_ERR;
659
       }
656
       }
660
     }
657
     }
661
 
658
 
662
-    if (code_seen('S')) {
663
-      g26_nozzle = code_value_float();
659
+    if (parser.seen('S')) {
660
+      g26_nozzle = parser.value_float();
664
       if (!WITHIN(g26_nozzle, 0.1, 1.0)) {
661
       if (!WITHIN(g26_nozzle, 0.1, 1.0)) {
665
         SERIAL_PROTOCOLLNPGM("?Specified nozzle size not plausible.");
662
         SERIAL_PROTOCOLLNPGM("?Specified nozzle size not plausible.");
666
         return UBL_ERR;
663
         return UBL_ERR;
667
       }
664
       }
668
     }
665
     }
669
 
666
 
670
-    if (code_seen('P')) {
671
-      if (!code_has_value())
667
+    if (parser.seen('P')) {
668
+      if (!parser.has_value())
672
         g26_prime_flag = -1;
669
         g26_prime_flag = -1;
673
       else {
670
       else {
674
         g26_prime_flag++;
671
         g26_prime_flag++;
675
-        g26_prime_length = code_value_linear_units();
672
+        g26_prime_length = parser.value_linear_units();
676
         if (!WITHIN(g26_prime_length, 0.0, 25.0)) {
673
         if (!WITHIN(g26_prime_length, 0.0, 25.0)) {
677
           SERIAL_PROTOCOLLNPGM("?Specified prime length not plausible.");
674
           SERIAL_PROTOCOLLNPGM("?Specified prime length not plausible.");
678
           return UBL_ERR;
675
           return UBL_ERR;
680
       }
677
       }
681
     }
678
     }
682
 
679
 
683
-    if (code_seen('F')) {
684
-      g26_filament_diameter = code_value_linear_units();
680
+    if (parser.seen('F')) {
681
+      g26_filament_diameter = parser.value_linear_units();
685
       if (!WITHIN(g26_filament_diameter, 1.0, 4.0)) {
682
       if (!WITHIN(g26_filament_diameter, 1.0, 4.0)) {
686
         SERIAL_PROTOCOLLNPGM("?Specified filament size not plausible.");
683
         SERIAL_PROTOCOLLNPGM("?Specified filament size not plausible.");
687
         return UBL_ERR;
684
         return UBL_ERR;
693
 
690
 
694
     g26_extrusion_multiplier *= g26_filament_diameter * sq(g26_nozzle) / sq(0.3); // Scale up by nozzle size
691
     g26_extrusion_multiplier *= g26_filament_diameter * sq(g26_nozzle) / sq(0.3); // Scale up by nozzle size
695
 
692
 
696
-    if (code_seen('H')) {
697
-      g26_hotend_temp = code_value_temp_abs();
693
+    if (parser.seen('H')) {
694
+      g26_hotend_temp = parser.value_celsius();
698
       if (!WITHIN(g26_hotend_temp, 165, 280)) {
695
       if (!WITHIN(g26_hotend_temp, 165, 280)) {
699
         SERIAL_PROTOCOLLNPGM("?Specified nozzle temperature not plausible.");
696
         SERIAL_PROTOCOLLNPGM("?Specified nozzle temperature not plausible.");
700
         return UBL_ERR;
697
         return UBL_ERR;
701
       }
698
       }
702
     }
699
     }
703
 
700
 
704
-    if (code_seen('U')) {
701
+    if (parser.seen('U')) {
705
       randomSeed(millis());
702
       randomSeed(millis());
706
-      random_deviation = code_has_value() ? code_value_float() : 50.0;
703
+      // This setting will persist for the next G26
704
+      random_deviation = parser.has_value() ? parser.value_float() : 50.0;
707
     }
705
     }
708
 
706
 
709
-    g26_repeats = code_seen('R') ? (code_has_value() ? code_value_int() : GRID_MAX_POINTS+1) : GRID_MAX_POINTS+1;
707
+    g26_repeats = parser.seen('R') ? (parser.has_value() ? parser.value_int() : GRID_MAX_POINTS + 1) : GRID_MAX_POINTS + 1;
710
     if (g26_repeats < 1) {
708
     if (g26_repeats < 1) {
711
       SERIAL_PROTOCOLLNPGM("?(R)epeat value not plausible; must be at least 1.");
709
       SERIAL_PROTOCOLLNPGM("?(R)epeat value not plausible; must be at least 1.");
712
       return UBL_ERR;
710
       return UBL_ERR;
713
     }
711
     }
714
 
712
 
715
-    g26_x_pos = code_seen('X') ? code_value_linear_units() : current_position[X_AXIS];
716
-    g26_y_pos = code_seen('Y') ? code_value_linear_units() : current_position[Y_AXIS];
713
+    g26_x_pos = parser.seen('X') ? parser.value_linear_units() : current_position[X_AXIS];
714
+    g26_y_pos = parser.seen('Y') ? parser.value_linear_units() : current_position[Y_AXIS];
717
     if (!position_is_reachable_xy(g26_x_pos, g26_y_pos)) {
715
     if (!position_is_reachable_xy(g26_x_pos, g26_y_pos)) {
718
       SERIAL_PROTOCOLLNPGM("?Specified X,Y coordinate out of bounds.");
716
       SERIAL_PROTOCOLLNPGM("?Specified X,Y coordinate out of bounds.");
719
       return UBL_ERR;
717
       return UBL_ERR;
722
     /**
720
     /**
723
      * Wait until all parameters are verified before altering the state!
721
      * Wait until all parameters are verified before altering the state!
724
      */
722
      */
725
-    state.active = !code_seen('D');
723
+    state.active = !parser.seen('D');
726
 
724
 
727
     return UBL_OK;
725
     return UBL_OK;
728
   }
726
   }

+ 7
- 6
Marlin/M100_Free_Mem_Chk.cpp View File

61
 extern char __bss_end;
61
 extern char __bss_end;
62
 
62
 
63
 #include "Marlin.h"
63
 #include "Marlin.h"
64
+#include "gcode.h"
64
 #include "hex_print_routines.h"
65
 #include "hex_print_routines.h"
65
 
66
 
66
 //
67
 //
188
    *  This is useful to check the correctness of the M100 D and the M100 F commands.
189
    *  This is useful to check the correctness of the M100 D and the M100 F commands.
189
    */
190
    */
190
   void corrupt_free_memory(char *ptr, const uint16_t size) {
191
   void corrupt_free_memory(char *ptr, const uint16_t size) {
191
-    if (code_seen('C')) {
192
+    if (parser.seen('C')) {
192
       ptr += 8;
193
       ptr += 8;
193
       const uint16_t near_top = top_of_stack() - ptr - 250, // -250 to avoid interrupt activity that's altered the stack.
194
       const uint16_t near_top = top_of_stack() - ptr - 250, // -250 to avoid interrupt activity that's altered the stack.
194
                      j = near_top / (size + 1);
195
                      j = near_top / (size + 1);
247
 
248
 
248
   // Always init on the first invocation of M100
249
   // Always init on the first invocation of M100
249
   static bool m100_not_initialized = true;
250
   static bool m100_not_initialized = true;
250
-  if (m100_not_initialized || code_seen('I')) {
251
+  if (m100_not_initialized || parser.seen('I')) {
251
     m100_not_initialized = false;
252
     m100_not_initialized = false;
252
     init_free_memory(ptr, sp - ptr);
253
     init_free_memory(ptr, sp - ptr);
253
   }
254
   }
254
 
255
 
255
   #if ENABLED(M100_FREE_MEMORY_DUMPER)
256
   #if ENABLED(M100_FREE_MEMORY_DUMPER)
256
-    if (code_seen('D'))
257
+    if (parser.seen('D'))
257
       return dump_free_memory(ptr, sp);
258
       return dump_free_memory(ptr, sp);
258
   #endif
259
   #endif
259
 
260
 
260
-  if (code_seen('F'))
261
+  if (parser.seen('F'))
261
     return free_memory_pool_report(ptr, sp - ptr);
262
     return free_memory_pool_report(ptr, sp - ptr);
262
 
263
 
263
   #if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
264
   #if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
264
 
265
 
265
-    if (code_seen('C'))
266
-      return corrupt_free_memory(ptr, code_value_int());
266
+    if (parser.seen('C'))
267
+      return corrupt_free_memory(ptr, parser.value_int());
267
 
268
 
268
   #endif
269
   #endif
269
 }
270
 }

+ 1
- 17
Marlin/Marlin.h View File

287
   void update_software_endstops(const AxisEnum axis);
287
   void update_software_endstops(const AxisEnum axis);
288
 #endif
288
 #endif
289
 
289
 
290
-// GCode support for external objects
291
-bool code_seen(char);
292
-int code_value_int();
293
-int16_t code_value_temp_abs();
294
-int16_t code_value_temp_diff();
295
-
296
-#if ENABLED(INCH_MODE_SUPPORT)
297
-  float code_value_linear_units();
298
-  float code_value_axis_units(const AxisEnum axis);
299
-  float code_value_per_axis_unit(const AxisEnum axis);
300
-#else
301
-  #define code_value_linear_units() code_value_float()
302
-  #define code_value_axis_units(A) code_value_float()
303
-  #define code_value_per_axis_unit(A) code_value_float()
304
-#endif
305
-
306
 #if IS_KINEMATIC
290
 #if IS_KINEMATIC
307
   extern float delta[ABC];
291
   extern float delta[ABC];
308
   void inverse_kinematics(const float logical[XYZ]);
292
   void inverse_kinematics(const float logical[XYZ]);
490
   return position_is_reachable_raw_xy(RAW_X_POSITION(lx), RAW_Y_POSITION(ly));
474
   return position_is_reachable_raw_xy(RAW_X_POSITION(lx), RAW_Y_POSITION(ly));
491
 }
475
 }
492
 
476
 
493
-#endif //MARLIN_H
477
+#endif // MARLIN_H

+ 412
- 584
Marlin/Marlin_main.cpp
File diff suppressed because it is too large
View File


+ 11
- 10
Marlin/configuration_store.cpp View File

178
 #include "temperature.h"
178
 #include "temperature.h"
179
 #include "ultralcd.h"
179
 #include "ultralcd.h"
180
 
180
 
181
+#if ENABLED(INCH_MODE_SUPPORT) || (ENABLED(ULTIPANEL) && ENABLED(TEMPERATURE_UNITS_SUPPORT))
182
+  #include "gcode.h"
183
+#endif
184
+
181
 #if ENABLED(MESH_BED_LEVELING)
185
 #if ENABLED(MESH_BED_LEVELING)
182
   #include "mesh_bed_leveling.h"
186
   #include "mesh_bed_leveling.h"
183
 #endif
187
 #endif
1331
      */
1335
      */
1332
     CONFIG_ECHO_START;
1336
     CONFIG_ECHO_START;
1333
     #if ENABLED(INCH_MODE_SUPPORT)
1337
     #if ENABLED(INCH_MODE_SUPPORT)
1334
-      extern float linear_unit_factor, volumetric_unit_factor;
1335
-      #define LINEAR_UNIT(N) ((N) / linear_unit_factor)
1336
-      #define VOLUMETRIC_UNIT(N) ((N) / (volumetric_enabled ? volumetric_unit_factor : linear_unit_factor))
1338
+      #define LINEAR_UNIT(N) ((N) / parser.linear_unit_factor)
1339
+      #define VOLUMETRIC_UNIT(N) ((N) / (volumetric_enabled ? parser.volumetric_unit_factor : parser.linear_unit_factor))
1337
       SERIAL_ECHOPGM("  G2");
1340
       SERIAL_ECHOPGM("  G2");
1338
-      SERIAL_CHAR(linear_unit_factor == 1.0 ? '1' : '0');
1341
+      SERIAL_CHAR(parser.linear_unit_factor == 1.0 ? '1' : '0');
1339
       SERIAL_ECHOPGM(" ; Units in ");
1342
       SERIAL_ECHOPGM(" ; Units in ");
1340
-      serialprintPGM(linear_unit_factor == 1.0 ? PSTR("mm\n") : PSTR("inches\n"));
1343
+      serialprintPGM(parser.linear_unit_factor == 1.0 ? PSTR("mm\n") : PSTR("inches\n"));
1341
     #else
1344
     #else
1342
       #define LINEAR_UNIT(N) N
1345
       #define LINEAR_UNIT(N) N
1343
       #define VOLUMETRIC_UNIT(N) N
1346
       #define VOLUMETRIC_UNIT(N) N
1351
 
1354
 
1352
       CONFIG_ECHO_START;
1355
       CONFIG_ECHO_START;
1353
       #if ENABLED(TEMPERATURE_UNITS_SUPPORT)
1356
       #if ENABLED(TEMPERATURE_UNITS_SUPPORT)
1354
-        extern TempUnit input_temp_units;
1355
-        extern float to_temp_units(const float &f);
1356
-        #define TEMP_UNIT(N) to_temp_units(N)
1357
+        #define TEMP_UNIT(N) parser.to_temp_units(N)
1357
         SERIAL_ECHOPGM("  M149 ");
1358
         SERIAL_ECHOPGM("  M149 ");
1358
-        SERIAL_CHAR(input_temp_units == TEMPUNIT_K ? 'K' : input_temp_units == TEMPUNIT_F ? 'F' : 'C');
1359
+        SERIAL_CHAR(parser.temp_units_code());
1359
         SERIAL_ECHOPGM(" ; Units in ");
1360
         SERIAL_ECHOPGM(" ; Units in ");
1360
-        serialprintPGM(input_temp_units == TEMPUNIT_K ? PSTR("Kelvin\n") : input_temp_units == TEMPUNIT_F ? PSTR("Fahrenheit\n") : PSTR("Celsius\n"));
1361
+        serialprintPGM(parser.temp_units_name());
1361
       #else
1362
       #else
1362
         #define TEMP_UNIT(N) N
1363
         #define TEMP_UNIT(N) N
1363
         SERIAL_ECHOLNPGM("  M149 C ; Units in Celsius\n");
1364
         SERIAL_ECHOLNPGM("  M149 C ; Units in Celsius\n");

+ 279
- 0
Marlin/gcode.cpp View File

1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+ * gcode.cpp - Parser for a GCode line, providing a parameter interface.
25
+ */
26
+
27
+#include "gcode.h"
28
+
29
+#include "Marlin.h"
30
+#include "language.h"
31
+
32
+// Must be declared for allocation and to satisfy the linker
33
+// Zero values need no initialization.
34
+
35
+#if ENABLED(INCH_MODE_SUPPORT)
36
+  float GCodeParser::linear_unit_factor, GCodeParser::volumetric_unit_factor;
37
+#endif
38
+
39
+#if ENABLED(TEMPERATURE_UNITS_SUPPORT)
40
+  TempUnit GCodeParser::input_temp_units;
41
+#endif
42
+
43
+char *GCodeParser::command_ptr,
44
+     *GCodeParser::string_arg,
45
+     *GCodeParser::value_ptr;
46
+char GCodeParser::command_letter;
47
+int GCodeParser::codenum;
48
+#if USE_GCODE_SUBCODES
49
+  int GCodeParser::subcode;
50
+#endif
51
+
52
+#if ENABLED(FASTER_GCODE_PARSER)
53
+  // Optimized Parameters
54
+  byte GCodeParser::codebits[4];   // found bits
55
+  uint8_t GCodeParser::param[26];  // parameter offsets from command_ptr
56
+#else
57
+  char *GCodeParser::command_args; // start of parameters
58
+#endif
59
+
60
+// Create a global instance of the GCode parser singleton
61
+GCodeParser parser;
62
+
63
+/**
64
+ * Clear all code-seen (and value pointers)
65
+ *
66
+ * Since each param is set/cleared on seen codes,
67
+ * this may be optimized by commenting out ZERO(param)
68
+ */
69
+void GCodeParser::reset() {
70
+  string_arg = NULL;                    // No whole line argument
71
+  command_letter = '?';                 // No command letter
72
+  codenum = 0;                          // No command code
73
+  #if USE_GCODE_SUBCODES
74
+    subcode = 0;                        // No command sub-code
75
+  #endif
76
+  #if ENABLED(FASTER_GCODE_PARSER)
77
+    ZERO(codebits);                     // No codes yet
78
+    //ZERO(param);                      // No parameters (should be safe to comment out this line)
79
+  #endif
80
+}
81
+
82
+// Populate all fields by parsing a single line of GCode
83
+// 58 bytes of SRAM are used to speed up seen/value
84
+void GCodeParser::parse(char *p) {
85
+
86
+  reset(); // No codes to report
87
+
88
+  // Skip spaces
89
+  while (*p == ' ') ++p;
90
+
91
+  // Skip N[-0-9] if included in the command line
92
+  if (*p == 'N' && NUMERIC_SIGNED(p[1])) {
93
+    #if ENABLED(FASTER_GCODE_PARSER)
94
+      //set('N', p + 1);     // (optional) Set the 'N' parameter value
95
+    #endif
96
+    p += 2;                  // skip N[-0-9]
97
+    while (NUMERIC(*p)) ++p; // skip [0-9]*
98
+    while (*p == ' ')   ++p; // skip [ ]*
99
+  }
100
+
101
+  // *p now points to the current command, which should be G, M, or T
102
+  command_ptr = p;
103
+
104
+  // Get the command letter, which must be G, M, or T
105
+  const char letter = *p++;
106
+
107
+  // Nullify asterisk and trailing whitespace
108
+  char *starpos = strchr(p, '*');
109
+  if (starpos) {
110
+    --starpos;                          // *
111
+    while (*starpos == ' ') --starpos;  // spaces...
112
+    starpos[1] = '\0';
113
+  }
114
+
115
+  // Bail if the letter is not G, M, or T
116
+  switch (letter) { case 'G': case 'M': case 'T': break; default: return; }
117
+
118
+  // Skip spaces to get the numeric part
119
+  while (*p == ' ') p++;
120
+
121
+  // Bail if there's no command code number
122
+  if (!NUMERIC(*p)) return;
123
+
124
+  // Save the command letter at this point
125
+  // A '?' signifies an unknown command
126
+  command_letter = letter;
127
+
128
+  // Get the code number - integer digits only
129
+  codenum = 0;
130
+  do {
131
+    codenum *= 10, codenum += *p++ - '0';
132
+  } while (NUMERIC(*p));
133
+
134
+  // Allow for decimal point in command
135
+  #if USE_GCODE_SUBCODES
136
+    if (*p == '.') {
137
+      p++;
138
+      while (NUMERIC(*p))
139
+        subcode *= 10, subcode += *p++ - '0';
140
+    }
141
+  #endif
142
+
143
+  // Skip all spaces to get to the first argument, or nul
144
+  while (*p == ' ') p++;
145
+
146
+  // The command parameters (if any) start here, for sure!
147
+
148
+  #if DISABLED(FASTER_GCODE_PARSER)
149
+    command_args = p; // Scan for parameters in seen()
150
+  #endif
151
+
152
+  // Only use string_arg for these M codes
153
+  if (letter == 'M') switch (codenum) { case 23: case 28: case 30: case 117: case 928: string_arg = p; return; default: break; }
154
+
155
+  #if ENABLED(DEBUG_GCODE_PARSER)
156
+    const bool debug = codenum == 800;
157
+  #endif
158
+
159
+  /**
160
+   * Find all parameters, set flags and pointers for fast parsing
161
+   *
162
+   * Most codes ignore 'string_arg', but those that want a string will get the right pointer.
163
+   * The following loop assigns the first "parameter" having no numeric value to 'string_arg'.
164
+   * This allows M0/M1 with expire time to work: "M0 S5 You Win!"
165
+   */
166
+  string_arg = NULL;
167
+  while (char code = *p++) {                    // Get the next parameter. A NUL ends the loop
168
+
169
+    // Special handling for M32 [P] !/path/to/file.g#
170
+    // The path must be the last parameter
171
+    if (code == '!' && letter == 'M' && codenum == 32) {
172
+      string_arg = p;                           // Name starts after '!'
173
+      char * const lb = strchr(p, '#');         // Already seen '#' as SD char (to pause buffering)
174
+      if (lb) *lb = '\0';                       // Safe to mark the end of the filename
175
+      return;
176
+    }
177
+
178
+    // Arguments MUST be uppercase for fast GCode parsing
179
+    #if ENABLED(FASTER_GCODE_PARSER)
180
+      #define PARAM_TEST WITHIN(code, 'A', 'Z')
181
+    #else
182
+      #define PARAM_TEST true
183
+    #endif
184
+
185
+    if (PARAM_TEST) {
186
+
187
+      const bool has_num = DECIMAL_SIGNED(*p);  // The parameter has a number [-+0-9.]
188
+
189
+      #if ENABLED(DEBUG_GCODE_PARSER)
190
+        if (debug) {
191
+          SERIAL_ECHOPAIR("Got letter ", code); // DEBUG
192
+          SERIAL_ECHOPAIR(" at index ", (int)(p - command_ptr - 1)); // DEBUG
193
+          if (has_num) SERIAL_ECHOPGM(" (has_num)");
194
+        }
195
+      #endif
196
+
197
+      if (!has_num && !string_arg) {            // No value? First time, keep as string_arg
198
+        string_arg = p - 1;
199
+        #if ENABLED(DEBUG_GCODE_PARSER)
200
+          if (debug) SERIAL_ECHOPAIR(" string_arg: ", hex_address((void*)string_arg)); // DEBUG
201
+        #endif
202
+      }
203
+
204
+      #if ENABLED(DEBUG_GCODE_PARSER)
205
+        if (debug) SERIAL_EOL;
206
+      #endif
207
+
208
+      #if ENABLED(FASTER_GCODE_PARSER)
209
+        set(code, has_num ? p : NULL            // Set parameter exists and pointer (NULL for no number)
210
+          #if ENABLED(DEBUG_GCODE_PARSER)
211
+            , debug
212
+          #endif
213
+        );
214
+      #endif
215
+    }
216
+    else if (!string_arg) {                     // Not A-Z? First time, keep as the string_arg
217
+      string_arg = p - 1;
218
+      #if ENABLED(DEBUG_GCODE_PARSER)
219
+        if (debug) SERIAL_ECHOPAIR(" string_arg: ", hex_address((void*)string_arg)); // DEBUG
220
+      #endif
221
+    }
222
+
223
+    while (*p && *p != ' ') p++;                // Skip over the parameter
224
+    while (*p == ' ') p++;                      // Skip over all spaces
225
+  }
226
+}
227
+
228
+void GCodeParser::unknown_command_error() {
229
+  SERIAL_ECHO_START;
230
+  SERIAL_ECHOPAIR(MSG_UNKNOWN_COMMAND, command_ptr);
231
+  SERIAL_CHAR('"');
232
+  SERIAL_EOL;
233
+}
234
+
235
+#if ENABLED(DEBUG_GCODE_PARSER)
236
+
237
+  void GCodeParser::debug() {
238
+    SERIAL_ECHOPAIR("Command: ", command_ptr);
239
+    SERIAL_ECHOPAIR(" (", command_letter);
240
+    SERIAL_ECHO(codenum);
241
+    SERIAL_ECHOLNPGM(")");
242
+    #if ENABLED(FASTER_GCODE_PARSER)
243
+      SERIAL_ECHO(" args: \"");
244
+      for (char c = 'A'; c <= 'Z'; ++c)
245
+        if (seen(c)) { SERIAL_CHAR(c); SERIAL_CHAR(' '); }
246
+    #else
247
+      SERIAL_ECHOPAIR(" args: \"", command_args);
248
+    #endif
249
+    SERIAL_ECHOPGM("\"");
250
+    if (string_arg) {
251
+      SERIAL_ECHOPGM(" string: \"");
252
+      SERIAL_ECHO(string_arg);
253
+      SERIAL_CHAR('"');
254
+    }
255
+    SERIAL_ECHOPGM("\n\n");
256
+    for (char c = 'A'; c <= 'Z'; ++c) {
257
+      if (seen(c)) {
258
+        SERIAL_ECHOPAIR("Code '", c); SERIAL_ECHOPGM("':");
259
+        if (has_value()) {
260
+          SERIAL_ECHOPAIR("\n    float: ", value_float());
261
+          SERIAL_ECHOPAIR("\n     long: ", value_long());
262
+          SERIAL_ECHOPAIR("\n    ulong: ", value_ulong());
263
+          SERIAL_ECHOPAIR("\n   millis: ", value_millis());
264
+          SERIAL_ECHOPAIR("\n   sec-ms: ", value_millis_from_seconds());
265
+          SERIAL_ECHOPAIR("\n      int: ", value_int());
266
+          SERIAL_ECHOPAIR("\n   ushort: ", value_ushort());
267
+          SERIAL_ECHOPAIR("\n     byte: ", (int)value_byte());
268
+          SERIAL_ECHOPAIR("\n     bool: ", (int)value_bool());
269
+          SERIAL_ECHOPAIR("\n   linear: ", value_linear_units());
270
+          SERIAL_ECHOPAIR("\n  celsius: ", value_celsius());
271
+        }
272
+        else
273
+          SERIAL_ECHOPGM(" (no value)");
274
+        SERIAL_ECHOPGM("\n\n");
275
+      }
276
+    }
277
+  }
278
+
279
+#endif // DEBUG_GCODE_PARSER

+ 285
- 0
Marlin/gcode.h View File

1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+ * gcode.h - Parser for a GCode line, providing a parameter interface.
25
+ *           Codes like M149 control the way the GCode parser behaves,
26
+ *           so settings for these codes are located in this class.
27
+ */
28
+
29
+#ifndef GCODE_H
30
+#define GCODE_H
31
+
32
+#include "enum.h"
33
+#include "types.h"
34
+#include "MarlinConfig.h"
35
+
36
+//#define DEBUG_GCODE_PARSER
37
+
38
+#if ENABLED(DEBUG_GCODE_PARSER)
39
+  #include "hex_print_routines.h"
40
+  #include "serial.h"
41
+#endif
42
+
43
+#if ENABLED(INCH_MODE_SUPPORT)
44
+  extern bool volumetric_enabled;
45
+#endif
46
+
47
+/**
48
+ * GCode parser
49
+ *
50
+ *  - Parse a single gcode line for its letter, code, subcode, and parameters
51
+ *  - FASTER_GCODE_PARSER:
52
+ *    - Flags existing params (1 bit each)
53
+ *    - Stores value offsets (1 byte each)
54
+ *  - Provide accessors for parameters:
55
+ *    - Parameter exists
56
+ *    - Parameter has value
57
+ *    - Parameter value in different units and types
58
+ */
59
+class GCodeParser {
60
+
61
+private:
62
+  static char *value_ptr;           // Set by seen, used to fetch the value
63
+
64
+  #if ENABLED(FASTER_GCODE_PARSER)
65
+    static byte codebits[4];        // Parameters pre-scanned
66
+    static uint8_t param[26];       // For A-Z, offsets into command args
67
+  #else
68
+    static char *command_args;      // Args start here, for slow scan
69
+  #endif
70
+
71
+public:
72
+
73
+  // Global states for GCode-level units features
74
+
75
+  #if ENABLED(INCH_MODE_SUPPORT)
76
+    static float linear_unit_factor, volumetric_unit_factor;
77
+  #endif
78
+
79
+  #if ENABLED(TEMPERATURE_UNITS_SUPPORT)
80
+    static TempUnit input_temp_units;
81
+  #endif
82
+
83
+  // Command line state
84
+  static char *command_ptr,               // The command, so it can be echoed
85
+              *string_arg;                // string of command line
86
+
87
+  static char command_letter;             // G, M, or T
88
+  static int codenum;                     // 123
89
+  #if USE_GCODE_SUBCODES
90
+    static int subcode;                   // .1
91
+  #endif
92
+
93
+  #if ENABLED(DEBUG_GCODE_PARSER)
94
+    void debug();
95
+  #endif
96
+
97
+  // Reset is done before parsing
98
+  static void reset();
99
+
100
+  #if ENABLED(FASTER_GCODE_PARSER)
101
+
102
+    // Set the flag and pointer for a parameter
103
+    static void set(const char c, char * const ptr
104
+      #if ENABLED(DEBUG_GCODE_PARSER)
105
+        , const bool debug=false
106
+      #endif
107
+    ) {
108
+      const uint8_t ind = c - 'A';
109
+      if (ind >= COUNT(param)) return;           // Only A-Z
110
+      SBI(codebits[ind >> 3], ind & 0x7);        // parameter exists
111
+      param[ind] = ptr ? ptr - command_ptr : 0;  // parameter offset or 0
112
+      #if ENABLED(DEBUG_GCODE_PARSER)
113
+        if (debug) {
114
+          SERIAL_ECHOPAIR("Set bit ", (int)(ind & 0x7));
115
+          SERIAL_ECHOPAIR(" of index ", (int)(ind >> 3));
116
+          SERIAL_ECHOLNPAIR(" | param = ", hex_address((void*)param[ind]));
117
+        }
118
+      #endif
119
+    }
120
+
121
+    // Code seen bit was set. If not found, value_ptr is unchanged.
122
+    // This allows "if (seen('A')||seen('B'))" to use the last-found value.
123
+    static bool seen(const char c) {
124
+      const uint8_t ind = c - 'A';
125
+      if (ind >= COUNT(param)) return false; // Only A-Z
126
+      const bool b = TEST(codebits[ind >> 3], ind & 0x7);
127
+      if (b) value_ptr = command_ptr + param[ind];
128
+      return b;
129
+    }
130
+
131
+  #else
132
+
133
+    // Code is found in the string. If not found, value_ptr is unchanged.
134
+    // This allows "if (seen('A')||seen('B'))" to use the last-found value.
135
+    static bool seen(const char c) {
136
+      char *p = strchr(command_args, c);
137
+      const bool b = !!p;
138
+      if (b) value_ptr = DECIMAL_SIGNED(*p) ? p + 1 : NULL;
139
+      return b;
140
+    }
141
+
142
+  #endif // FASTER_GCODE_PARSER
143
+
144
+  // Populate all fields by parsing a single line of GCode
145
+  // This uses 54 bytes of SRAM to speed up seen/value
146
+  static void parse(char * p);
147
+
148
+  // Code value pointer was set
149
+  FORCE_INLINE static bool has_value() { return value_ptr != NULL; }
150
+
151
+  // Float removes 'E' to prevent scientific notation interpretation
152
+  inline static float value_float() {
153
+    if (value_ptr) {
154
+      char *e = value_ptr;
155
+      for (;;) {
156
+        const char c = *e;
157
+        if (c == '\0' || c == ' ') break;
158
+        if (c == 'E' || c == 'e') {
159
+          *e = '\0';
160
+          const float ret = strtod(value_ptr, NULL);
161
+          *e = c;
162
+          return ret;
163
+        }
164
+        ++e;
165
+      }
166
+      return strtod(value_ptr, NULL);
167
+    }
168
+    return 0.0;
169
+  }
170
+
171
+  // Code value as a long or ulong
172
+  inline          static long value_long()  { return value_ptr ? strtol(value_ptr, NULL, 10) : 0L; }
173
+  inline unsigned static long value_ulong() { return value_ptr ? strtoul(value_ptr, NULL, 10) : 0UL; }
174
+
175
+  // Code value for use as time
176
+  FORCE_INLINE static millis_t value_millis() { return value_ulong(); }
177
+  FORCE_INLINE static millis_t value_millis_from_seconds() { return value_float() * 1000UL; }
178
+
179
+  // Reduce to fewer bits
180
+  FORCE_INLINE static int value_int()    { return (int)value_long(); }
181
+  FORCE_INLINE uint16_t value_ushort()   { return (uint16_t)value_long(); }
182
+  inline static uint8_t value_byte()     { return (uint8_t)(constrain(value_long(), 0, 255)); }
183
+
184
+  // Bool is true with no value or non-zero
185
+  inline static bool value_bool()        { return !has_value() || value_byte(); }
186
+
187
+  // Units modes: Inches, Fahrenheit, Kelvin
188
+
189
+  #if ENABLED(INCH_MODE_SUPPORT)
190
+
191
+    inline static void set_input_linear_units(LinearUnit units) {
192
+      switch (units) {
193
+        case LINEARUNIT_INCH:
194
+          linear_unit_factor = 25.4;
195
+          break;
196
+        case LINEARUNIT_MM:
197
+        default:
198
+          linear_unit_factor = 1.0;
199
+          break;
200
+      }
201
+      volumetric_unit_factor = pow(linear_unit_factor, 3.0);
202
+    }
203
+
204
+    inline static float axis_unit_factor(const AxisEnum axis) {
205
+      return (axis >= E_AXIS && volumetric_enabled ? volumetric_unit_factor : linear_unit_factor);
206
+    }
207
+
208
+    inline static float value_linear_units()                     { return value_float() * linear_unit_factor; }
209
+    inline static float value_axis_units(const AxisEnum axis)    { return value_float() * axis_unit_factor(axis); }
210
+    inline static float value_per_axis_unit(const AxisEnum axis) { return value_float() / axis_unit_factor(axis); }
211
+
212
+  #else
213
+
214
+    FORCE_INLINE static float value_linear_units()                  {            return value_float(); }
215
+    FORCE_INLINE static float value_axis_units(const AxisEnum a)    { UNUSED(a); return value_float(); }
216
+    FORCE_INLINE static float value_per_axis_unit(const AxisEnum a) { UNUSED(a); return value_float(); }
217
+
218
+  #endif
219
+
220
+  #if ENABLED(TEMPERATURE_UNITS_SUPPORT)
221
+
222
+    inline static void set_input_temp_units(TempUnit units) { input_temp_units = units; }
223
+
224
+    #if ENABLED(ULTIPANEL) && DISABLED(DISABLE_M503)
225
+
226
+      FORCE_INLINE static char temp_units_code() {
227
+        return input_temp_units == TEMPUNIT_K ? 'K' : input_temp_units == TEMPUNIT_F ? 'F' : 'C';
228
+      }
229
+      FORCE_INLINE static char* temp_units_name() {
230
+        return input_temp_units == TEMPUNIT_K ? PSTR("Kelvin") : input_temp_units == TEMPUNIT_F ? PSTR("Fahrenheit") : PSTR("Celsius")
231
+      }
232
+      inline static float to_temp_units(const float &f) {
233
+        switch (input_temp_units) {
234
+          case TEMPUNIT_F:
235
+            return f * 0.5555555556 + 32.0;
236
+          case TEMPUNIT_K:
237
+            return f + 273.15;
238
+          case TEMPUNIT_C:
239
+          default:
240
+            return f;
241
+        }
242
+      }
243
+
244
+    #endif // ULTIPANEL && !DISABLE_M503
245
+
246
+    inline static float value_celsius() {
247
+      const float f = value_float();
248
+      switch (input_temp_units) {
249
+        case TEMPUNIT_F:
250
+          return (f - 32.0) * 0.5555555556;
251
+        case TEMPUNIT_K:
252
+          return f - 273.15;
253
+        case TEMPUNIT_C:
254
+        default:
255
+          return f;
256
+      }
257
+    }
258
+
259
+    inline static float value_celsius_diff() {
260
+      switch (input_temp_units) {
261
+        case TEMPUNIT_F:
262
+          return value_float() * 0.5555555556;
263
+        case TEMPUNIT_C:
264
+        case TEMPUNIT_K:
265
+        default:
266
+          return value_float();
267
+      }
268
+    }
269
+
270
+  #else
271
+
272
+    FORCE_INLINE static float value_celsius()      { return value_float(); }
273
+    FORCE_INLINE static float value_celsius_diff() { return value_float(); }
274
+
275
+  #endif
276
+
277
+  FORCE_INLINE static float value_feedrate() { return value_linear_units(); }
278
+
279
+  void unknown_command_error();
280
+
281
+};
282
+
283
+extern GCodeParser parser;
284
+
285
+#endif // GCODE_H

+ 4
- 2
Marlin/hex_print_routines.cpp View File

20
  *
20
  *
21
  */
21
  */
22
 #include "Marlin.h"
22
 #include "Marlin.h"
23
-#if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(M100_FREE_MEMORY_WATCHER)
23
+#include "gcode.h"
24
+
25
+#if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(M100_FREE_MEMORY_WATCHER) || ENABLED(DEBUG_GCODE_PARSER)
24
 
26
 
25
 #include "hex_print_routines.h"
27
 #include "hex_print_routines.h"
26
 
28
 
50
 void print_hex_word(const uint16_t w)        { SERIAL_ECHO(hex_word(w));    }
52
 void print_hex_word(const uint16_t w)        { SERIAL_ECHO(hex_word(w));    }
51
 void print_hex_address(const void * const w) { SERIAL_ECHO(hex_address(w)); }
53
 void print_hex_address(const void * const w) { SERIAL_ECHO(hex_address(w)); }
52
 
54
 
53
-#endif // AUTO_BED_LEVELING_UBL || M100_FREE_MEMORY_WATCHER
55
+#endif // AUTO_BED_LEVELING_UBL || M100_FREE_MEMORY_WATCHER || DEBUG_GCODE_PARSER

+ 3
- 2
Marlin/hex_print_routines.h View File

24
 #define HEX_PRINT_ROUTINES_H
24
 #define HEX_PRINT_ROUTINES_H
25
 
25
 
26
 #include "MarlinConfig.h"
26
 #include "MarlinConfig.h"
27
+#include "gcode.h"
27
 
28
 
28
-#if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(M100_FREE_MEMORY_WATCHER)
29
+#if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(M100_FREE_MEMORY_WATCHER) || ENABLED(DEBUG_GCODE_PARSER)
29
 
30
 
30
 //
31
 //
31
 // Utility functions to create and print hex strings as nybble, byte, and word.
32
 // Utility functions to create and print hex strings as nybble, byte, and word.
43
 void print_hex_word(const uint16_t w);
44
 void print_hex_word(const uint16_t w);
44
 void print_hex_address(const void * const w);
45
 void print_hex_address(const void * const w);
45
 
46
 
46
-#endif // AUTO_BED_LEVELING_UBL || M100_FREE_MEMORY_WATCHER
47
+#endif // AUTO_BED_LEVELING_UBL || M100_FREE_MEMORY_WATCHER || DEBUG_GCODE_PARSER
47
 #endif // HEX_PRINT_ROUTINES_H
48
 #endif // HEX_PRINT_ROUTINES_H

+ 3
- 1
Marlin/macros.h View File

124
 
124
 
125
 #define WITHIN(V,L,H) ((V) >= (L) && (V) <= (H))
125
 #define WITHIN(V,L,H) ((V) >= (L) && (V) <= (H))
126
 #define NUMERIC(a) WITHIN(a, '0', '9')
126
 #define NUMERIC(a) WITHIN(a, '0', '9')
127
-#define NUMERIC_SIGNED(a) (NUMERIC(a) || (a) == '-')
127
+#define DECIMAL(a) (NUMERIC(a) || a == '.')
128
+#define NUMERIC_SIGNED(a) (NUMERIC(a) || (a) == '-' || (a) == '+')
129
+#define DECIMAL_SIGNED(a) (DECIMAL(a) || (a) == '-' || (a) == '+')
128
 #define COUNT(a) (sizeof(a)/sizeof(*a))
130
 #define COUNT(a) (sizeof(a)/sizeof(*a))
129
 #define ZERO(a) memset(a,0,sizeof(a))
131
 #define ZERO(a) memset(a,0,sizeof(a))
130
 #define COPY(a,b) memcpy(a,b,min(sizeof(a),sizeof(b)))
132
 #define COPY(a,b) memcpy(a,b,min(sizeof(a),sizeof(b)))

+ 5
- 4
Marlin/planner.cpp View File

64
 #include "ultralcd.h"
64
 #include "ultralcd.h"
65
 #include "language.h"
65
 #include "language.h"
66
 #include "ubl.h"
66
 #include "ubl.h"
67
+#include "gcode.h"
67
 
68
 
68
 #include "Marlin.h"
69
 #include "Marlin.h"
69
 
70
 
1549
 #if ENABLED(AUTOTEMP)
1550
 #if ENABLED(AUTOTEMP)
1550
 
1551
 
1551
   void Planner::autotemp_M104_M109() {
1552
   void Planner::autotemp_M104_M109() {
1552
-    autotemp_enabled = code_seen('F');
1553
-    if (autotemp_enabled) autotemp_factor = code_value_temp_diff();
1554
-    if (code_seen('S')) autotemp_min = code_value_temp_abs();
1555
-    if (code_seen('B')) autotemp_max = code_value_temp_abs();
1553
+    autotemp_enabled = parser.seen('F');
1554
+    if (autotemp_enabled) autotemp_factor = parser.value_celsius_diff();
1555
+    if (parser.seen('S')) autotemp_min = parser.value_celsius();
1556
+    if (parser.seen('B')) autotemp_max = parser.value_celsius();
1556
   }
1557
   }
1557
 
1558
 
1558
 #endif
1559
 #endif

+ 69
- 77
Marlin/ubl_G29.cpp View File

30
   #include "configuration_store.h"
30
   #include "configuration_store.h"
31
   #include "ultralcd.h"
31
   #include "ultralcd.h"
32
   #include "stepper.h"
32
   #include "stepper.h"
33
+  #include "gcode.h"
33
 
34
 
34
   #include <math.h>
35
   #include <math.h>
35
   #include "least_squares_fit.h"
36
   #include "least_squares_fit.h"
47
   float lcd_z_offset_edit();
48
   float lcd_z_offset_edit();
48
   extern float meshedit_done;
49
   extern float meshedit_done;
49
   extern long babysteps_done;
50
   extern long babysteps_done;
50
-  extern float code_value_float();
51
-  extern uint8_t code_value_byte();
52
-  extern bool code_value_bool();
53
-  extern bool code_has_value();
54
   extern float probe_pt(const float &x, const float &y, bool, int);
51
   extern float probe_pt(const float &x, const float &y, bool, int);
55
   extern bool set_probe_deployed(bool);
52
   extern bool set_probe_deployed(bool);
56
 
53
 
322
       return;
319
       return;
323
     }
320
     }
324
 
321
 
325
-    // Check for commands that require the printer to be homed.
322
+    // Check for commands that require the printer to be homed
326
     if (axis_unhomed_error()) {
323
     if (axis_unhomed_error()) {
327
-      if (code_seen('J'))
324
+      const int8_t p_val = parser.seen('P') && parser.has_value() ? parser.value_int() : -1;
325
+      if (p_val == 1 || p_val == 2 || p_val == 4 || parser.seen('J'))
328
         home_all_axes();
326
         home_all_axes();
329
-      else if (code_seen('P')) {
330
-        if (code_has_value()) {
331
-          const int p_val = code_value_int();
332
-          if (p_val == 1 || p_val == 2 || p_val == 4)
333
-            home_all_axes();
334
-        }
335
-      }
336
     }
327
     }
337
 
328
 
338
     if (g29_parameter_parsing()) return; // abort if parsing the simple parameters causes a problem,
329
     if (g29_parameter_parsing()) return; // abort if parsing the simple parameters causes a problem,
339
 
330
 
340
-    // Invalidate Mesh Points. This command is a little bit asymetrical because
331
+    // Invalidate Mesh Points. This command is a little bit asymmetrical because
341
     // it directly specifies the repetition count and does not use the 'R' parameter.
332
     // it directly specifies the repetition count and does not use the 'R' parameter.
342
-    if (code_seen('I')) {
333
+    if (parser.seen('I')) {
343
       uint8_t cnt = 0;
334
       uint8_t cnt = 0;
344
-      g29_repetition_cnt = code_has_value() ? code_value_int() : 1;
335
+      g29_repetition_cnt = parser.has_value() ? parser.value_int() : 1;
345
       while (g29_repetition_cnt--) {
336
       while (g29_repetition_cnt--) {
346
         if (cnt > 20) { cnt = 0; idle(); }
337
         if (cnt > 20) { cnt = 0; idle(); }
347
         const mesh_index_pair location = find_closest_mesh_point_of_type(REAL, g29_x_pos, g29_y_pos, USE_NOZZLE_AS_REFERENCE, NULL, false);
338
         const mesh_index_pair location = find_closest_mesh_point_of_type(REAL, g29_x_pos, g29_y_pos, USE_NOZZLE_AS_REFERENCE, NULL, false);
355
       SERIAL_PROTOCOLLNPGM("Locations invalidated.\n");
346
       SERIAL_PROTOCOLLNPGM("Locations invalidated.\n");
356
     }
347
     }
357
 
348
 
358
-    if (code_seen('Q')) {
359
-      const int test_pattern = code_has_value() ? code_value_int() : -99;
349
+    if (parser.seen('Q')) {
350
+      const int test_pattern = parser.has_value() ? parser.value_int() : -99;
360
       if (!WITHIN(test_pattern, -1, 2)) {
351
       if (!WITHIN(test_pattern, -1, 2)) {
361
-        SERIAL_PROTOCOLLNPGM("Invalid test_pattern value. (0-2)\n");
352
+        SERIAL_PROTOCOLLNPGM("Invalid test_pattern value. (-1 to 2)\n");
362
         return;
353
         return;
363
       }
354
       }
364
       SERIAL_PROTOCOLLNPGM("Loading test_pattern values.\n");
355
       SERIAL_PROTOCOLLNPGM("Loading test_pattern values.\n");
385
           // Allow the user to specify the height because 10mm is a little extreme in some cases.
376
           // Allow the user to specify the height because 10mm is a little extreme in some cases.
386
           for (uint8_t x = (GRID_MAX_POINTS_X) / 3; x < 2 * (GRID_MAX_POINTS_X) / 3; x++)   // Create a rectangular raised area in
377
           for (uint8_t x = (GRID_MAX_POINTS_X) / 3; x < 2 * (GRID_MAX_POINTS_X) / 3; x++)   // Create a rectangular raised area in
387
             for (uint8_t y = (GRID_MAX_POINTS_Y) / 3; y < 2 * (GRID_MAX_POINTS_Y) / 3; y++) // the center of the bed
378
             for (uint8_t y = (GRID_MAX_POINTS_Y) / 3; y < 2 * (GRID_MAX_POINTS_Y) / 3; y++) // the center of the bed
388
-              z_values[x][y] += code_seen('C') ? g29_constant : 9.99;
379
+              z_values[x][y] += parser.seen('C') ? g29_constant : 9.99;
389
           break;
380
           break;
390
       }
381
       }
391
     }
382
     }
392
 
383
 
393
-    if (code_seen('J')) {
384
+    if (parser.seen('J')) {
394
       if (g29_grid_size) {  // if not 0 it is a normal n x n grid being probed
385
       if (g29_grid_size) {  // if not 0 it is a normal n x n grid being probed
395
         save_ubl_active_state_and_disable();
386
         save_ubl_active_state_and_disable();
396
-        tilt_mesh_based_on_probed_grid(code_seen('T'));
387
+        tilt_mesh_based_on_probed_grid(parser.seen('T'));
397
         restore_ubl_active_state_and_leave();
388
         restore_ubl_active_state_and_leave();
398
       }
389
       }
399
       else { // grid_size == 0 : A 3-Point leveling has been requested
390
       else { // grid_size == 0 : A 3-Point leveling has been requested
425
       }
416
       }
426
     }
417
     }
427
 
418
 
428
-    if (code_seen('P')) {
419
+    if (parser.seen('P')) {
429
       if (WITHIN(g29_phase_value, 0, 1) && state.storage_slot == -1) {
420
       if (WITHIN(g29_phase_value, 0, 1) && state.storage_slot == -1) {
430
         state.storage_slot = 0;
421
         state.storage_slot = 0;
431
         SERIAL_PROTOCOLLNPGM("Default storage slot 0 selected.");
422
         SERIAL_PROTOCOLLNPGM("Default storage slot 0 selected.");
444
           //
435
           //
445
           // Invalidate Entire Mesh and Automatically Probe Mesh in areas that can be reached by the probe
436
           // Invalidate Entire Mesh and Automatically Probe Mesh in areas that can be reached by the probe
446
           //
437
           //
447
-          if (!code_seen('C')) {
438
+          if (!parser.seen('C')) {
448
             invalidate();
439
             invalidate();
449
             SERIAL_PROTOCOLLNPGM("Mesh invalidated. Probing mesh.");
440
             SERIAL_PROTOCOLLNPGM("Mesh invalidated. Probing mesh.");
450
           }
441
           }
455
             SERIAL_PROTOCOLLNPGM(").\n");
446
             SERIAL_PROTOCOLLNPGM(").\n");
456
           }
447
           }
457
           probe_entire_mesh(g29_x_pos + X_PROBE_OFFSET_FROM_EXTRUDER, g29_y_pos + Y_PROBE_OFFSET_FROM_EXTRUDER,
448
           probe_entire_mesh(g29_x_pos + X_PROBE_OFFSET_FROM_EXTRUDER, g29_y_pos + Y_PROBE_OFFSET_FROM_EXTRUDER,
458
-                            code_seen('T'), code_seen('E'), code_seen('U'));
449
+                            parser.seen('T'), parser.seen('E'), parser.seen('U'));
459
           break;
450
           break;
460
 
451
 
461
         case 2: {
452
         case 2: {
481
             #endif
472
             #endif
482
           }
473
           }
483
 
474
 
484
-          if (code_seen('C')) {
475
+          if (parser.seen('C')) {
485
             g29_x_pos = current_position[X_AXIS];
476
             g29_x_pos = current_position[X_AXIS];
486
             g29_y_pos = current_position[Y_AXIS];
477
             g29_y_pos = current_position[Y_AXIS];
487
           }
478
           }
488
 
479
 
489
           float height = Z_CLEARANCE_BETWEEN_PROBES;
480
           float height = Z_CLEARANCE_BETWEEN_PROBES;
490
 
481
 
491
-          if (code_seen('B')) {
492
-            g29_card_thickness = code_has_value() ? code_value_float() : measure_business_card_thickness(height);
493
-
482
+          if (parser.seen('B')) {
483
+            g29_card_thickness = parser.has_value() ? parser.value_float() : measure_business_card_thickness(height);
494
             if (fabs(g29_card_thickness) > 1.5) {
484
             if (fabs(g29_card_thickness) > 1.5) {
495
               SERIAL_PROTOCOLLNPGM("?Error in Business Card measurement.");
485
               SERIAL_PROTOCOLLNPGM("?Error in Business Card measurement.");
496
               return;
486
               return;
497
             }
487
             }
498
           }
488
           }
499
 
489
 
500
-          if (code_seen('H') && code_has_value()) height = code_value_float();
490
+          if (parser.seen('H') && parser.has_value()) height = parser.value_float();
501
 
491
 
502
           if (!position_is_reachable_xy(g29_x_pos, g29_y_pos)) {
492
           if (!position_is_reachable_xy(g29_x_pos, g29_y_pos)) {
503
-            SERIAL_PROTOCOLLNPGM("(X,Y) outside printable radius.");
493
+            SERIAL_PROTOCOLLNPGM("XY outside printable radius.");
504
             return;
494
             return;
505
           }
495
           }
506
 
496
 
507
-          manually_probe_remaining_mesh(g29_x_pos, g29_y_pos, height, g29_card_thickness, code_seen('T'));
497
+          manually_probe_remaining_mesh(g29_x_pos, g29_y_pos, height, g29_card_thickness, parser.seen('T'));
508
           SERIAL_PROTOCOLLNPGM("G29 P2 finished.");
498
           SERIAL_PROTOCOLLNPGM("G29 P2 finished.");
509
         } break;
499
         } break;
510
 
500
 
531
               }
521
               }
532
             }
522
             }
533
           } else {
523
           } else {
534
-            const float cvf = code_value_float();
524
+            const float cvf = parser.value_float();
535
             switch((int)truncf(cvf * 10.0) - 30) {   // 3.1 -> 1
525
             switch((int)truncf(cvf * 10.0) - 30) {   // 3.1 -> 1
536
               #if ENABLED(UBL_G29_P31)
526
               #if ENABLED(UBL_G29_P31)
537
                 case 1: {
527
                 case 1: {
561
           //
551
           //
562
           // Fine Tune (i.e., Edit) the Mesh
552
           // Fine Tune (i.e., Edit) the Mesh
563
           //
553
           //
564
-
565
-          fine_tune_mesh(g29_x_pos, g29_y_pos, code_seen('T'));
566
-
554
+          fine_tune_mesh(g29_x_pos, g29_y_pos, parser.seen('T'));
567
           break;
555
           break;
568
 
556
 
569
         case 5: find_mean_mesh_height(); break;
557
         case 5: find_mean_mesh_height(); break;
576
     // Much of the 'What?' command can be eliminated. But until we are fully debugged, it is
564
     // Much of the 'What?' command can be eliminated. But until we are fully debugged, it is
577
     // good to have the extra information. Soon... we prune this to just a few items
565
     // good to have the extra information. Soon... we prune this to just a few items
578
     //
566
     //
579
-    if (code_seen('W')) g29_what_command();
567
+    if (parser.seen('W')) g29_what_command();
580
 
568
 
581
     //
569
     //
582
     // When we are fully debugged, this may go away. But there are some valid
570
     // When we are fully debugged, this may go away. But there are some valid
583
     // use cases for the users. So we can wait and see what to do with it.
571
     // use cases for the users. So we can wait and see what to do with it.
584
     //
572
     //
585
 
573
 
586
-    if (code_seen('K')) // Kompare Current Mesh Data to Specified Stored Mesh
574
+    if (parser.seen('K')) // Kompare Current Mesh Data to Specified Stored Mesh
587
       g29_compare_current_mesh_to_stored_mesh();
575
       g29_compare_current_mesh_to_stored_mesh();
588
 
576
 
589
     //
577
     //
590
     // Load a Mesh from the EEPROM
578
     // Load a Mesh from the EEPROM
591
     //
579
     //
592
 
580
 
593
-    if (code_seen('L')) {     // Load Current Mesh Data
594
-      g29_storage_slot = code_has_value() ? code_value_int() : state.storage_slot;
581
+    if (parser.seen('L')) {     // Load Current Mesh Data
582
+      g29_storage_slot = parser.has_value() ? parser.value_int() : state.storage_slot;
595
 
583
 
596
       int16_t a = settings.calc_num_meshes();
584
       int16_t a = settings.calc_num_meshes();
597
 
585
 
616
     // Store a Mesh in the EEPROM
604
     // Store a Mesh in the EEPROM
617
     //
605
     //
618
 
606
 
619
-    if (code_seen('S')) {     // Store (or Save) Current Mesh Data
620
-      g29_storage_slot = code_has_value() ? code_value_int() : state.storage_slot;
607
+    if (parser.seen('S')) {     // Store (or Save) Current Mesh Data
608
+      g29_storage_slot = parser.has_value() ? parser.value_int() : state.storage_slot;
621
 
609
 
622
       if (g29_storage_slot == -1) {                     // Special case, we are going to 'Export' the mesh to the
610
       if (g29_storage_slot == -1) {                     // Special case, we are going to 'Export' the mesh to the
623
         SERIAL_ECHOLNPGM("G29 I 999");              // host in a form it can be reconstructed on a different machine
611
         SERIAL_ECHOLNPGM("G29 I 999");              // host in a form it can be reconstructed on a different machine
654
       SERIAL_PROTOCOLLNPGM("Done.");
642
       SERIAL_PROTOCOLLNPGM("Done.");
655
     }
643
     }
656
 
644
 
657
-    if (code_seen('T'))
658
-      display_map(code_has_value() ? code_value_int() : 0);
645
+    if (parser.seen('T'))
646
+      display_map(parser.has_value() ? parser.value_int() : 0);
659
 
647
 
660
-    /*
648
+    /**
661
      * This code may not be needed...  Prepare for its removal...
649
      * This code may not be needed...  Prepare for its removal...
662
      *
650
      *
663
-    if (code_seen('Z')) {
664
-      if (code_has_value())
665
-        state.z_offset = code_value_float();   // do the simple case. Just lock in the specified value
651
+     */
652
+    #if 0
653
+    if (parser.seen('Z')) {
654
+      if (parser.has_value())
655
+        state.z_offset = parser.value_float();   // do the simple case. Just lock in the specified value
666
       else {
656
       else {
667
         save_ubl_active_state_and_disable();
657
         save_ubl_active_state_and_disable();
668
         //float measured_z = probe_pt(g29_x_pos + X_PROBE_OFFSET_FROM_EXTRUDER, g29_y_pos + Y_PROBE_OFFSET_FROM_EXTRUDER, ProbeDeployAndStow, g29_verbose_level);
658
         //float measured_z = probe_pt(g29_x_pos + X_PROBE_OFFSET_FROM_EXTRUDER, g29_y_pos + Y_PROBE_OFFSET_FROM_EXTRUDER, ProbeDeployAndStow, g29_verbose_level);
712
         restore_ubl_active_state_and_leave();
702
         restore_ubl_active_state_and_leave();
713
       }
703
       }
714
     }
704
     }
715
-    */
705
+    #endif
716
 
706
 
717
     LEAVE:
707
     LEAVE:
718
 
708
 
1015
 
1005
 
1016
       if (do_ubl_mesh_map) display_map(g29_map_type);  // show user where we're probing
1006
       if (do_ubl_mesh_map) display_map(g29_map_type);  // show user where we're probing
1017
 
1007
 
1018
-      if (code_seen('B'))
1019
-        LCD_MESSAGEPGM("Place shim & measure"); // TODO: Make translatable string
1020
-      else
1021
-        LCD_MESSAGEPGM("Measure"); // TODO: Make translatable string
1008
+      serialprintPGM(parser.seen('B') ? PSTR("Place shim & measure") : PSTR("Measure")); // TODO: Make translatable strings
1022
 
1009
 
1023
       while (ubl_lcd_clicked()) delay(50);             // wait for user to release encoder wheel
1010
       while (ubl_lcd_clicked()) delay(50);             // wait for user to release encoder wheel
1024
       delay(50);                                       // debounce
1011
       delay(50);                                       // debounce
1073
     g29_constant = 0.0;
1060
     g29_constant = 0.0;
1074
     g29_repetition_cnt = 0;
1061
     g29_repetition_cnt = 0;
1075
 
1062
 
1076
-    g29_x_flag = code_seen('X') && code_has_value();
1077
-    g29_x_pos = g29_x_flag ? code_value_float() : current_position[X_AXIS];
1078
-    g29_y_flag = code_seen('Y') && code_has_value();
1079
-    g29_y_pos = g29_y_flag ? code_value_float() : current_position[Y_AXIS];
1063
+    g29_x_flag = parser.seen('X') && parser.has_value();
1064
+    g29_x_pos = g29_x_flag ? parser.value_float() : current_position[X_AXIS];
1065
+    g29_y_flag = parser.seen('Y') && parser.has_value();
1066
+    g29_y_pos = g29_y_flag ? parser.value_float() : current_position[Y_AXIS];
1080
 
1067
 
1081
-    if (code_seen('R')) {
1082
-      g29_repetition_cnt = code_has_value() ? code_value_int() : GRID_MAX_POINTS;
1068
+    if (parser.seen('R')) {
1069
+      g29_repetition_cnt = parser.has_value() ? parser.value_int() : GRID_MAX_POINTS;
1083
       NOMORE(g29_repetition_cnt, GRID_MAX_POINTS);
1070
       NOMORE(g29_repetition_cnt, GRID_MAX_POINTS);
1084
       if (g29_repetition_cnt < 1) {
1071
       if (g29_repetition_cnt < 1) {
1085
         SERIAL_PROTOCOLLNPGM("?(R)epetition count invalid (1+).\n");
1072
         SERIAL_PROTOCOLLNPGM("?(R)epetition count invalid (1+).\n");
1087
       }
1074
       }
1088
     }
1075
     }
1089
 
1076
 
1090
-    g29_verbose_level = code_seen('V') ? code_value_int() : 0;
1077
+    g29_verbose_level = parser.seen('V') ? parser.value_int() : 0;
1091
     if (!WITHIN(g29_verbose_level, 0, 4)) {
1078
     if (!WITHIN(g29_verbose_level, 0, 4)) {
1092
       SERIAL_PROTOCOLLNPGM("?(V)erbose level is implausible (0-4).\n");
1079
       SERIAL_PROTOCOLLNPGM("?(V)erbose level is implausible (0-4).\n");
1093
       err_flag = true;
1080
       err_flag = true;
1094
     }
1081
     }
1095
 
1082
 
1096
-    if (code_seen('P')) {
1097
-      g29_phase_value = code_value_int();
1083
+    if (parser.seen('P')) {
1084
+      g29_phase_value = parser.value_int();
1098
       if (!WITHIN(g29_phase_value, 0, 6)) {
1085
       if (!WITHIN(g29_phase_value, 0, 6)) {
1099
         SERIAL_PROTOCOLLNPGM("?(P)hase value invalid (0-6).\n");
1086
         SERIAL_PROTOCOLLNPGM("?(P)hase value invalid (0-6).\n");
1100
         err_flag = true;
1087
         err_flag = true;
1101
       }
1088
       }
1102
     }
1089
     }
1103
 
1090
 
1104
-    if (code_seen('J')) {
1105
-      g29_grid_size = code_has_value() ? code_value_int() : 0;
1091
+    if (parser.seen('J')) {
1092
+      g29_grid_size = parser.has_value() ? parser.value_int() : 0;
1106
       if (g29_grid_size && !WITHIN(g29_grid_size, 2, 9)) {
1093
       if (g29_grid_size && !WITHIN(g29_grid_size, 2, 9)) {
1107
         SERIAL_PROTOCOLLNPGM("?Invalid grid size (J) specified (2-9).\n");
1094
         SERIAL_PROTOCOLLNPGM("?Invalid grid size (J) specified (2-9).\n");
1108
         err_flag = true;
1095
         err_flag = true;
1125
 
1112
 
1126
     if (err_flag) return UBL_ERR;
1113
     if (err_flag) return UBL_ERR;
1127
 
1114
 
1128
-    // Activate or deactivate UBL
1129
-    if (code_seen('A')) {
1130
-      if (code_seen('D')) {
1115
+    /**
1116
+     * Activate or deactivate UBL
1117
+     * Note: UBL's G29 restores the state set here when done.
1118
+     *       Leveling is being enabled here with old data, possibly
1119
+     *       none. Error handling should disable for safety...
1120
+     */
1121
+    if (parser.seen('A')) {
1122
+      if (parser.seen('D')) {
1131
         SERIAL_PROTOCOLLNPGM("?Can't activate and deactivate at the same time.\n");
1123
         SERIAL_PROTOCOLLNPGM("?Can't activate and deactivate at the same time.\n");
1132
         return UBL_ERR;
1124
         return UBL_ERR;
1133
       }
1125
       }
1134
       state.active = true;
1126
       state.active = true;
1135
       report_state();
1127
       report_state();
1136
     }
1128
     }
1137
-    else if (code_seen('D')) {
1129
+    else if (parser.seen('D')) {
1138
       state.active = false;
1130
       state.active = false;
1139
       report_state();
1131
       report_state();
1140
     }
1132
     }
1141
 
1133
 
1142
     // Set global 'C' flag and its value
1134
     // Set global 'C' flag and its value
1143
-    if ((g29_c_flag = code_seen('C')))
1144
-      g29_constant = code_value_float();
1135
+    if ((g29_c_flag = parser.seen('C')))
1136
+      g29_constant = parser.value_float();
1145
 
1137
 
1146
     #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
1138
     #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
1147
-      if (code_seen('F') && code_has_value()) {
1148
-        const float fh = code_value_float();
1139
+      if (parser.seen('F') && parser.has_value()) {
1140
+        const float fh = parser.value_float();
1149
         if (!WITHIN(fh, 0.0, 100.0)) {
1141
         if (!WITHIN(fh, 0.0, 100.0)) {
1150
           SERIAL_PROTOCOLLNPGM("?(F)ade height for Bed Level Correction not plausible.\n");
1142
           SERIAL_PROTOCOLLNPGM("?(F)ade height for Bed Level Correction not plausible.\n");
1151
           return UBL_ERR;
1143
           return UBL_ERR;
1154
       }
1146
       }
1155
     #endif
1147
     #endif
1156
 
1148
 
1157
-    g29_map_type = code_seen('T') && code_has_value() ? code_value_int() : 0;
1149
+    g29_map_type = parser.seen('T') && parser.has_value() ? parser.value_int() : 0;
1158
     if (!WITHIN(g29_map_type, 0, 1)) {
1150
     if (!WITHIN(g29_map_type, 0, 1)) {
1159
       SERIAL_PROTOCOLLNPGM("Invalid map type.\n");
1151
       SERIAL_PROTOCOLLNPGM("Invalid map type.\n");
1160
       return UBL_ERR;
1152
       return UBL_ERR;
1319
       return;
1311
       return;
1320
     }
1312
     }
1321
 
1313
 
1322
-    if (!code_has_value()) {
1314
+    if (!parser.has_value()) {
1323
       SERIAL_PROTOCOLLNPGM("?Storage slot # required.");
1315
       SERIAL_PROTOCOLLNPGM("?Storage slot # required.");
1324
       SERIAL_PROTOCOLLNPAIR("?Use 0 to ", a - 1);
1316
       SERIAL_PROTOCOLLNPAIR("?Use 0 to ", a - 1);
1325
       return;
1317
       return;
1326
     }
1318
     }
1327
 
1319
 
1328
-    g29_storage_slot = code_value_int();
1320
+    g29_storage_slot = parser.value_int();
1329
 
1321
 
1330
     if (!WITHIN(g29_storage_slot, 0, a - 1)) {
1322
     if (!WITHIN(g29_storage_slot, 0, a - 1)) {
1331
       SERIAL_PROTOCOLLNPGM("?Invalid storage slot.");
1323
       SERIAL_PROTOCOLLNPGM("?Invalid storage slot.");
1416
   }
1408
   }
1417
 
1409
 
1418
   void unified_bed_leveling::fine_tune_mesh(const float &lx, const float &ly, const bool do_ubl_mesh_map) {
1410
   void unified_bed_leveling::fine_tune_mesh(const float &lx, const float &ly, const bool do_ubl_mesh_map) {
1419
-    if (!code_seen('R'))    // fine_tune_mesh() is special. If no repetition count flag is specified
1411
+    if (!parser.seen('R'))    // fine_tune_mesh() is special. If no repetition count flag is specified
1420
       g29_repetition_cnt = 1;   // do exactly one mesh location. Otherwise use what the parser decided.
1412
       g29_repetition_cnt = 1;   // do exactly one mesh location. Otherwise use what the parser decided.
1421
 
1413
 
1422
     mesh_index_pair location;
1414
     mesh_index_pair location;
1587
       const float x = float(x_min) + ix * dx;
1579
       const float x = float(x_min) + ix * dx;
1588
       for (int8_t iy = 0; iy < g29_grid_size; iy++) {
1580
       for (int8_t iy = 0; iy < g29_grid_size; iy++) {
1589
         const float y = float(y_min) + dy * (zig_zag ? g29_grid_size - 1 - iy : iy);
1581
         const float y = float(y_min) + dy * (zig_zag ? g29_grid_size - 1 - iy : iy);
1590
-        float measured_z = probe_pt(LOGICAL_X_POSITION(x), LOGICAL_Y_POSITION(y), code_seen('E'), g29_verbose_level); // TODO: Needs error handling
1582
+        float measured_z = probe_pt(LOGICAL_X_POSITION(x), LOGICAL_Y_POSITION(y), parser.seen('E'), g29_verbose_level); // TODO: Needs error handling
1591
         #if ENABLED(DEBUG_LEVELING_FEATURE)
1583
         #if ENABLED(DEBUG_LEVELING_FEATURE)
1592
           if (DEBUGGING(LEVELING)) {
1584
           if (DEBUGGING(LEVELING)) {
1593
             SERIAL_CHAR('(');
1585
             SERIAL_CHAR('(');

Loading…
Cancel
Save