Browse Source

New GCode Parser - Implementation

Scott Lahteine 7 years ago
parent
commit
f4028fe088

+ 3
- 0
Marlin/Conditionals_post.h View File

@@ -838,4 +838,7 @@
838 838
   // Shorthand
839 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 844
 #endif // CONDITIONALS_POST_H

+ 28
- 30
Marlin/G26_Mesh_Validation_Tool.cpp View File

@@ -34,6 +34,7 @@
34 34
   #include "stepper.h"
35 35
   #include "temperature.h"
36 36
   #include "ultralcd.h"
37
+  #include "gcode.h"
37 38
 
38 39
   #define EXTRUSION_MULTIPLIER 1.0
39 40
   #define RETRACTION_MULTIPLIER 1.0
@@ -130,11 +131,7 @@
130 131
   void set_destination_to_current();
131 132
   void set_current_to_destination();
132 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 135
   void sync_plan_position_e();
139 136
   void chirp_at_user();
140 137
 
@@ -625,29 +622,29 @@
625 622
     g26_hotend_temp           = HOTEND_TEMP;
626 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 631
       if (!WITHIN(g26_bed_temp, 15, 140)) {
635 632
         SERIAL_PROTOCOLLNPGM("?Specified bed temperature not plausible.");
636 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 639
       if (!WITHIN(g26_layer_height, 0.0, 2.0)) {
643 640
         SERIAL_PROTOCOLLNPGM("?Specified layer height not plausible.");
644 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 648
         if (!WITHIN(g26_retraction_multiplier, 0.05, 15.0)) {
652 649
           SERIAL_PROTOCOLLNPGM("?Specified Retraction Multiplier not plausible.");
653 650
           return UBL_ERR;
@@ -659,20 +656,20 @@
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 661
       if (!WITHIN(g26_nozzle, 0.1, 1.0)) {
665 662
         SERIAL_PROTOCOLLNPGM("?Specified nozzle size not plausible.");
666 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 669
         g26_prime_flag = -1;
673 670
       else {
674 671
         g26_prime_flag++;
675
-        g26_prime_length = code_value_linear_units();
672
+        g26_prime_length = parser.value_linear_units();
676 673
         if (!WITHIN(g26_prime_length, 0.0, 25.0)) {
677 674
           SERIAL_PROTOCOLLNPGM("?Specified prime length not plausible.");
678 675
           return UBL_ERR;
@@ -680,8 +677,8 @@
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 682
       if (!WITHIN(g26_filament_diameter, 1.0, 4.0)) {
686 683
         SERIAL_PROTOCOLLNPGM("?Specified filament size not plausible.");
687 684
         return UBL_ERR;
@@ -693,27 +690,28 @@
693 690
 
694 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 695
       if (!WITHIN(g26_hotend_temp, 165, 280)) {
699 696
         SERIAL_PROTOCOLLNPGM("?Specified nozzle temperature not plausible.");
700 697
         return UBL_ERR;
701 698
       }
702 699
     }
703 700
 
704
-    if (code_seen('U')) {
701
+    if (parser.seen('U')) {
705 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 708
     if (g26_repeats < 1) {
711 709
       SERIAL_PROTOCOLLNPGM("?(R)epeat value not plausible; must be at least 1.");
712 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 715
     if (!position_is_reachable_xy(g26_x_pos, g26_y_pos)) {
718 716
       SERIAL_PROTOCOLLNPGM("?Specified X,Y coordinate out of bounds.");
719 717
       return UBL_ERR;
@@ -722,7 +720,7 @@
722 720
     /**
723 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 725
     return UBL_OK;
728 726
   }

+ 7
- 6
Marlin/M100_Free_Mem_Chk.cpp View File

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

+ 1
- 17
Marlin/Marlin.h View File

@@ -287,22 +287,6 @@ extern float soft_endstop_min[XYZ], soft_endstop_max[XYZ];
287 287
   void update_software_endstops(const AxisEnum axis);
288 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 290
 #if IS_KINEMATIC
307 291
   extern float delta[ABC];
308 292
   void inverse_kinematics(const float logical[XYZ]);
@@ -490,4 +474,4 @@ FORCE_INLINE bool position_is_reachable_xy(const float &lx, const float &ly) {
490 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,6 +178,10 @@ MarlinSettings settings;
178 178
 #include "temperature.h"
179 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 185
 #if ENABLED(MESH_BED_LEVELING)
182 186
   #include "mesh_bed_leveling.h"
183 187
 #endif
@@ -1331,13 +1335,12 @@ void MarlinSettings::reset() {
1331 1335
      */
1332 1336
     CONFIG_ECHO_START;
1333 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 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 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 1344
     #else
1342 1345
       #define LINEAR_UNIT(N) N
1343 1346
       #define VOLUMETRIC_UNIT(N) N
@@ -1351,13 +1354,11 @@ void MarlinSettings::reset() {
1351 1354
 
1352 1355
       CONFIG_ECHO_START;
1353 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 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 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 1362
       #else
1362 1363
         #define TEMP_UNIT(N) N
1363 1364
         SERIAL_ECHOLNPGM("  M149 C ; Units in Celsius\n");

+ 279
- 0
Marlin/gcode.cpp View File

@@ -0,0 +1,279 @@
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

@@ -0,0 +1,285 @@
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,7 +20,9 @@
20 20
  *
21 21
  */
22 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 27
 #include "hex_print_routines.h"
26 28
 
@@ -50,4 +52,4 @@ void print_hex_byte(const uint8_t b)         { SERIAL_ECHO(hex_byte(b));    }
50 52
 void print_hex_word(const uint16_t w)        { SERIAL_ECHO(hex_word(w));    }
51 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,8 +24,9 @@
24 24
 #define HEX_PRINT_ROUTINES_H
25 25
 
26 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 32
 // Utility functions to create and print hex strings as nybble, byte, and word.
@@ -43,5 +44,5 @@ void print_hex_byte(const uint8_t b);
43 44
 void print_hex_word(const uint16_t w);
44 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 48
 #endif // HEX_PRINT_ROUTINES_H

+ 3
- 1
Marlin/macros.h View File

@@ -124,7 +124,9 @@
124 124
 
125 125
 #define WITHIN(V,L,H) ((V) >= (L) && (V) <= (H))
126 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 130
 #define COUNT(a) (sizeof(a)/sizeof(*a))
129 131
 #define ZERO(a) memset(a,0,sizeof(a))
130 132
 #define COPY(a,b) memcpy(a,b,min(sizeof(a),sizeof(b)))

+ 5
- 4
Marlin/planner.cpp View File

@@ -64,6 +64,7 @@
64 64
 #include "ultralcd.h"
65 65
 #include "language.h"
66 66
 #include "ubl.h"
67
+#include "gcode.h"
67 68
 
68 69
 #include "Marlin.h"
69 70
 
@@ -1549,10 +1550,10 @@ void Planner::refresh_positioning() {
1549 1550
 #if ENABLED(AUTOTEMP)
1550 1551
 
1551 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 1559
 #endif

+ 69
- 77
Marlin/ubl_G29.cpp View File

@@ -30,6 +30,7 @@
30 30
   #include "configuration_store.h"
31 31
   #include "ultralcd.h"
32 32
   #include "stepper.h"
33
+  #include "gcode.h"
33 34
 
34 35
   #include <math.h>
35 36
   #include "least_squares_fit.h"
@@ -47,10 +48,6 @@
47 48
   float lcd_z_offset_edit();
48 49
   extern float meshedit_done;
49 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 51
   extern float probe_pt(const float &x, const float &y, bool, int);
55 52
   extern bool set_probe_deployed(bool);
56 53
 
@@ -322,26 +319,20 @@
322 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 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 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 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 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 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 336
       while (g29_repetition_cnt--) {
346 337
         if (cnt > 20) { cnt = 0; idle(); }
347 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,10 +346,10 @@
355 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 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 353
         return;
363 354
       }
364 355
       SERIAL_PROTOCOLLNPGM("Loading test_pattern values.\n");
@@ -385,15 +376,15 @@
385 376
           // Allow the user to specify the height because 10mm is a little extreme in some cases.
386 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 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 380
           break;
390 381
       }
391 382
     }
392 383
 
393
-    if (code_seen('J')) {
384
+    if (parser.seen('J')) {
394 385
       if (g29_grid_size) {  // if not 0 it is a normal n x n grid being probed
395 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 388
         restore_ubl_active_state_and_leave();
398 389
       }
399 390
       else { // grid_size == 0 : A 3-Point leveling has been requested
@@ -425,7 +416,7 @@
425 416
       }
426 417
     }
427 418
 
428
-    if (code_seen('P')) {
419
+    if (parser.seen('P')) {
429 420
       if (WITHIN(g29_phase_value, 0, 1) && state.storage_slot == -1) {
430 421
         state.storage_slot = 0;
431 422
         SERIAL_PROTOCOLLNPGM("Default storage slot 0 selected.");
@@ -444,7 +435,7 @@
444 435
           //
445 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 439
             invalidate();
449 440
             SERIAL_PROTOCOLLNPGM("Mesh invalidated. Probing mesh.");
450 441
           }
@@ -455,7 +446,7 @@
455 446
             SERIAL_PROTOCOLLNPGM(").\n");
456 447
           }
457 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 450
           break;
460 451
 
461 452
         case 2: {
@@ -481,30 +472,29 @@
481 472
             #endif
482 473
           }
483 474
 
484
-          if (code_seen('C')) {
475
+          if (parser.seen('C')) {
485 476
             g29_x_pos = current_position[X_AXIS];
486 477
             g29_y_pos = current_position[Y_AXIS];
487 478
           }
488 479
 
489 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 484
             if (fabs(g29_card_thickness) > 1.5) {
495 485
               SERIAL_PROTOCOLLNPGM("?Error in Business Card measurement.");
496 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 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 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 498
           SERIAL_PROTOCOLLNPGM("G29 P2 finished.");
509 499
         } break;
510 500
 
@@ -531,7 +521,7 @@
531 521
               }
532 522
             }
533 523
           } else {
534
-            const float cvf = code_value_float();
524
+            const float cvf = parser.value_float();
535 525
             switch((int)truncf(cvf * 10.0) - 30) {   // 3.1 -> 1
536 526
               #if ENABLED(UBL_G29_P31)
537 527
                 case 1: {
@@ -561,9 +551,7 @@
561 551
           //
562 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 555
           break;
568 556
 
569 557
         case 5: find_mean_mesh_height(); break;
@@ -576,22 +564,22 @@
576 564
     // Much of the 'What?' command can be eliminated. But until we are fully debugged, it is
577 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 570
     // When we are fully debugged, this may go away. But there are some valid
583 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 575
       g29_compare_current_mesh_to_stored_mesh();
588 576
 
589 577
     //
590 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 584
       int16_t a = settings.calc_num_meshes();
597 585
 
@@ -616,8 +604,8 @@
616 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 610
       if (g29_storage_slot == -1) {                     // Special case, we are going to 'Export' the mesh to the
623 611
         SERIAL_ECHOLNPGM("G29 I 999");              // host in a form it can be reconstructed on a different machine
@@ -654,15 +642,17 @@
654 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 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 656
       else {
667 657
         save_ubl_active_state_and_disable();
668 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,7 +702,7 @@
712 702
         restore_ubl_active_state_and_leave();
713 703
       }
714 704
     }
715
-    */
705
+    #endif
716 706
 
717 707
     LEAVE:
718 708
 
@@ -1015,10 +1005,7 @@
1015 1005
 
1016 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 1010
       while (ubl_lcd_clicked()) delay(50);             // wait for user to release encoder wheel
1024 1011
       delay(50);                                       // debounce
@@ -1073,13 +1060,13 @@
1073 1060
     g29_constant = 0.0;
1074 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 1070
       NOMORE(g29_repetition_cnt, GRID_MAX_POINTS);
1084 1071
       if (g29_repetition_cnt < 1) {
1085 1072
         SERIAL_PROTOCOLLNPGM("?(R)epetition count invalid (1+).\n");
@@ -1087,22 +1074,22 @@
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 1078
     if (!WITHIN(g29_verbose_level, 0, 4)) {
1092 1079
       SERIAL_PROTOCOLLNPGM("?(V)erbose level is implausible (0-4).\n");
1093 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 1085
       if (!WITHIN(g29_phase_value, 0, 6)) {
1099 1086
         SERIAL_PROTOCOLLNPGM("?(P)hase value invalid (0-6).\n");
1100 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 1093
       if (g29_grid_size && !WITHIN(g29_grid_size, 2, 9)) {
1107 1094
         SERIAL_PROTOCOLLNPGM("?Invalid grid size (J) specified (2-9).\n");
1108 1095
         err_flag = true;
@@ -1125,27 +1112,32 @@
1125 1112
 
1126 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 1123
         SERIAL_PROTOCOLLNPGM("?Can't activate and deactivate at the same time.\n");
1132 1124
         return UBL_ERR;
1133 1125
       }
1134 1126
       state.active = true;
1135 1127
       report_state();
1136 1128
     }
1137
-    else if (code_seen('D')) {
1129
+    else if (parser.seen('D')) {
1138 1130
       state.active = false;
1139 1131
       report_state();
1140 1132
     }
1141 1133
 
1142 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 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 1141
         if (!WITHIN(fh, 0.0, 100.0)) {
1150 1142
           SERIAL_PROTOCOLLNPGM("?(F)ade height for Bed Level Correction not plausible.\n");
1151 1143
           return UBL_ERR;
@@ -1154,7 +1146,7 @@
1154 1146
       }
1155 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 1150
     if (!WITHIN(g29_map_type, 0, 1)) {
1159 1151
       SERIAL_PROTOCOLLNPGM("Invalid map type.\n");
1160 1152
       return UBL_ERR;
@@ -1319,13 +1311,13 @@
1319 1311
       return;
1320 1312
     }
1321 1313
 
1322
-    if (!code_has_value()) {
1314
+    if (!parser.has_value()) {
1323 1315
       SERIAL_PROTOCOLLNPGM("?Storage slot # required.");
1324 1316
       SERIAL_PROTOCOLLNPAIR("?Use 0 to ", a - 1);
1325 1317
       return;
1326 1318
     }
1327 1319
 
1328
-    g29_storage_slot = code_value_int();
1320
+    g29_storage_slot = parser.value_int();
1329 1321
 
1330 1322
     if (!WITHIN(g29_storage_slot, 0, a - 1)) {
1331 1323
       SERIAL_PROTOCOLLNPGM("?Invalid storage slot.");
@@ -1416,7 +1408,7 @@
1416 1408
   }
1417 1409
 
1418 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 1412
       g29_repetition_cnt = 1;   // do exactly one mesh location. Otherwise use what the parser decided.
1421 1413
 
1422 1414
     mesh_index_pair location;
@@ -1587,7 +1579,7 @@
1587 1579
       const float x = float(x_min) + ix * dx;
1588 1580
       for (int8_t iy = 0; iy < g29_grid_size; iy++) {
1589 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 1583
         #if ENABLED(DEBUG_LEVELING_FEATURE)
1592 1584
           if (DEBUGGING(LEVELING)) {
1593 1585
             SERIAL_CHAR('(');

Loading…
Cancel
Save