yarf 0.1
Yet Another RepRap Firmware
|
00001 /* 00002 * gcode.c 00003 * 00004 * Copyright 2011 Pieter Agten 00005 * 00006 * This file is part of Yarf. 00007 * 00008 * Yarf is free software: you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation, either version 3 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * Yarf is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with Yarf. If not, see <http://www.gnu.org/licenses/>. 00020 */ 00021 00030 #include "gcode.h" 00031 00032 #include "yarf.h" 00033 #include "commands.h" 00034 00035 #include <math.h> 00036 #include <stdbool.h> 00037 #include <string.h> 00038 00039 #define N_SEEN _BV(0) 00040 #define CODE_SEEN _BV(1) 00041 #define X_SEEN _BV(2) 00042 #define Y_SEEN _BV(3) 00043 #define Z_SEEN _BV(4) 00044 #define E_SEEN _BV(5) 00045 #define F_SEEN _BV(6) 00046 #define S_SEEN _BV(7) 00047 #define CS_SEEN _BV(8) 00048 #define COORD_SEEN (X_SEEN | Y_SEEN | Z_SEEN | E_SEEN | F_SEEN | S_SEEN) 00049 00053 static FILE *host; 00057 static long expected_line_number; 00058 00062 typedef struct { 00063 int16_t fields; 00064 char letter; 00065 int number; 00066 long line_number; 00067 float x; 00068 float y; 00069 float z; 00070 float e; 00071 float f; 00072 float s; 00073 } gcode_command; 00074 00075 00088 static int 00089 get_signed_long(char *s, long *l) 00090 { 00091 int nb_chars = 0; 00092 int stat = sscanf(s, "%ld%n", l, &nb_chars); 00093 if (stat != 1) { 00094 return stat; 00095 } 00096 00097 return nb_chars; 00098 } 00099 00112 static int 00113 get_signed_int(char *s, int *i) 00114 { 00115 int nb_chars = 0; 00116 int stat = sscanf(s, "%d%n", i, &nb_chars); 00117 if (stat != 1) { 00118 return stat; 00119 } 00120 00121 return nb_chars; 00122 } 00123 00139 static int 00140 get_uint8_t(char *s, uint8_t *u) 00141 { 00142 unsigned int i; 00143 int nb_chars = 0; 00144 int stat = sscanf(s, "%u%n", &i, &nb_chars); 00145 if (stat != 1) { 00146 return stat; 00147 } 00148 00149 if (i > 255) { 00150 return -1; 00151 } 00152 00153 *u = (uint8_t)i; 00154 return nb_chars; 00155 } 00156 00157 00169 static int 00170 get_float(char *s, float *f) 00171 { 00172 int nb_chars = 0; 00173 int stat = sscanf(s, "%f%n", f, &nb_chars); 00174 if (stat != 1) { 00175 return stat; 00176 } 00177 00178 return nb_chars; 00179 } 00180 00188 static unsigned char 00189 count_whitespace(char *s) 00190 { 00191 unsigned char i = 0; 00192 while(*s == ' ' || 00193 *s == '\t' || 00194 *s == '\n' || 00195 *s == '\r' || 00196 *s == '\t') { 00197 s += 1; 00198 i += 1; 00199 } 00200 00201 return i; 00202 } 00203 00209 static void 00210 clear_command(gcode_command *cmd) { 00211 cmd->fields = 0; 00212 cmd->letter = 0; 00213 cmd->number = 0; 00214 cmd->line_number = 0; 00215 cmd->x = NAN; 00216 cmd->y = NAN; 00217 cmd->z = NAN; 00218 cmd->e = NAN; 00219 cmd->f = NAN; 00220 cmd->s = NAN; 00221 } 00222 00230 static uint8_t 00231 checksum(char *s, size_t length) 00232 { 00233 uint8_t cs = 0; 00234 unsigned char i; 00235 while (i < length && s[i] != '*' && s[i] != 0) { 00236 cs = cs ^ s[i]; 00237 i += 1; 00238 } 00239 return cs; 00240 } 00241 00251 static int 00252 parse_command(gcode_command *cmd) 00253 { 00254 char buffer[GCODE_BUFFER_SIZE]; 00255 if (fgets(buffer, GCODE_BUFFER_SIZE, host) == NULL) { 00256 /* An error was encountered while reading the input */ 00257 return GCODE_PARSE_COMMAND_READ_FAILURE; 00258 } 00259 //fprintf(host, "// got: %s", buffer); 00260 00261 size_t cmd_length = strlen(buffer); 00262 00263 /* Skip any whitespace */ 00264 unsigned char cur_pos = count_whitespace(buffer); 00265 if (cur_pos == cmd_length) { 00266 return GCODE_PARSE_COMMAND_EMPTY; 00267 } 00268 00269 clear_command(cmd); 00270 int16_t fields = 0; 00271 int nb_chars; 00272 /* Parse part of command based on letter */ 00273 while (cur_pos < cmd_length) { 00274 switch (buffer[cur_pos]) { 00275 case 'N': 00276 /* Line number */ 00277 if (fields & (N_SEEN | CS_SEEN)) { 00278 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00279 } 00280 cur_pos += 1; 00281 fields |= N_SEEN; 00282 nb_chars = get_signed_long(&buffer[cur_pos], &(cmd->line_number)); 00283 if (nb_chars <= 0) { 00284 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00285 } 00286 cur_pos += nb_chars; 00287 break; 00288 case 'G': 00289 case 'M': 00290 case 'T': 00291 /* Command code */ 00292 if (fields & (CODE_SEEN | COORD_SEEN | CS_SEEN)) { 00293 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00294 } 00295 cmd->letter = buffer[cur_pos]; 00296 cur_pos += 1; 00297 fields |= CODE_SEEN; 00298 nb_chars = get_signed_int(&buffer[cur_pos], &(cmd->number)); 00299 if (nb_chars <= 0) { 00300 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00301 } 00302 cur_pos += nb_chars; 00303 break; 00304 case 'X': 00305 if (fields & (X_SEEN | CS_SEEN)) { 00306 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00307 } 00308 cur_pos += 1; 00309 fields |= X_SEEN; 00310 nb_chars = get_float(&buffer[cur_pos], &(cmd->x)); 00311 if (nb_chars <= 0) { 00312 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00313 } 00314 cur_pos += nb_chars; 00315 break; 00316 case 'Y': 00317 if (fields & (Y_SEEN | CS_SEEN)) { 00318 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00319 } 00320 cur_pos += 1; 00321 fields |= Y_SEEN; 00322 nb_chars = get_float(&buffer[cur_pos], &(cmd->y)); 00323 if (nb_chars <= 0) { 00324 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00325 } 00326 cur_pos += nb_chars; 00327 break; 00328 case 'Z': 00329 if (fields & (Z_SEEN | CS_SEEN)) { 00330 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00331 } 00332 cur_pos += 1; 00333 fields |= Z_SEEN; 00334 nb_chars = get_float(&buffer[cur_pos], &(cmd->z)); 00335 if (nb_chars <= 0) { 00336 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00337 } 00338 cur_pos += nb_chars; 00339 break; 00340 case 'E': 00341 if (fields & (E_SEEN | CS_SEEN)) { 00342 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00343 } 00344 cur_pos += 1; 00345 fields |= E_SEEN; 00346 nb_chars = get_float(&buffer[cur_pos], &(cmd->e)); 00347 if (nb_chars <= 0) { 00348 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00349 } 00350 cur_pos += nb_chars; 00351 break; 00352 case 'F': 00353 if (fields & (F_SEEN | CS_SEEN)) { 00354 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00355 } 00356 cur_pos += 1; 00357 fields |= F_SEEN; 00358 nb_chars = get_float(&buffer[cur_pos], &(cmd->f)); 00359 if (nb_chars <= 0) { 00360 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00361 } 00362 cur_pos += nb_chars; 00363 break; 00364 case 'S': 00365 if (fields & (S_SEEN | CS_SEEN)) { 00366 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00367 } 00368 cur_pos += 1; 00369 fields |= S_SEEN; 00370 nb_chars = get_float(&buffer[cur_pos], &(cmd->s)); 00371 if (nb_chars <= 0) { 00372 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00373 } 00374 cur_pos += nb_chars; 00375 break; 00376 case '*': 00377 if (fields & CS_SEEN || !(fields & N_SEEN)) { 00378 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00379 } 00380 cur_pos += 1; 00381 fields |= CS_SEEN; 00382 uint8_t received_cs = 0; 00383 nb_chars = get_uint8_t(&buffer[cur_pos], &received_cs); 00384 if (nb_chars <= 0) { 00385 return GCODE_PARSE_COMMAND_SYNTAX_ERROR; 00386 } 00387 cur_pos += nb_chars; 00388 if (received_cs != checksum(buffer, cmd_length)) { 00389 return GCODE_PARSE_COMMAND_CHECKSUM_FAILED; 00390 } 00391 break; 00392 default: 00393 return GCODE_PARSE_COMMAND_UNEXPECTED_CHARACTER; 00394 } 00395 cur_pos += count_whitespace(&buffer[cur_pos]); 00396 } 00397 cmd->fields = fields; 00398 return GCODE_PARSE_COMMAND_SUCCESS; 00399 } 00400 00406 static inline void 00407 send_resend(unsigned long line_number) 00408 { 00409 fprintf(host,"rs %ld\n",line_number); 00410 } 00411 00412 00420 static inline void 00421 send_reply(gcode_command *cmd, cmd_response_t *r) 00422 { 00423 switch (r->status) { 00424 case OK: 00425 fputs("ok",host); 00426 if (! isnan(r->T)) { // Conversion to double is to prevent compiler warning (GCC Bug 35649) 00427 fprintf(host, " T:%.1f",(double)r->T); 00428 } 00429 if (! isnan(r->B)) { 00430 fprintf(host, " B:%.1f",(double)r->B); 00431 } 00432 if (! isnan(r->X)) { 00433 fprintf(host, " X:%.2f",(double)r->X); 00434 } 00435 if (! isnan(r->Y)) { 00436 fprintf(host, " Y:%.2f",(double)r->Y); 00437 } 00438 if (! isnan(r->Z)) { 00439 fprintf(host, " Z:%.2f",(double)r->Z); 00440 } 00441 if (! isnan(r->E)) { 00442 fprintf(host, " E:%.2f",(double)r->E); 00443 } 00444 if (r->info != NULL) { 00445 fputs(" // ",host); 00446 fputs(r->info,host); 00447 } 00448 break; 00449 case INVALID: 00450 if (cmd->fields & N_SEEN) { 00451 fprintf(host,"rs %ld ",cmd->line_number); 00452 } else { 00453 fputs("ok ",host); 00454 } 00455 fprintf(host,"// command '%c%d' failed", cmd->letter, cmd->number); 00456 if (r->info != NULL) { 00457 fputs(": ",host); 00458 fputs(r->info,host); 00459 } 00460 break; 00461 case FAULT: 00462 fputs("!!",host); 00463 if (r->info != NULL) { 00464 fputs(" // ",host); 00465 fputs(r->info,host); 00466 } 00467 break; 00468 } 00469 fputs("\n",host); 00470 } 00471 00477 static void 00478 command_unknown(cmd_response_t *r) 00479 { 00480 r->status = INVALID; 00481 r->info = "unknown command"; 00482 } 00483 00491 static inline void 00492 dispatch_command(gcode_command *cmd, cmd_response_t *r) 00493 { 00494 switch (cmd->letter) { 00495 case 'G': 00496 switch (cmd->number) { 00497 case 1: 00498 /* G1: Controlled move */ 00499 cmd_controlled_move(r, cmd->x, cmd->y, cmd->z, cmd->e, cmd->f); 00500 break; 00501 case 20: 00502 /* G20: Set Units to Inches */ 00503 cmd_set_units_inches(r); 00504 break; 00505 case 21: 00506 /* G21: Set Units to Millimeters */ 00507 cmd_set_units_millimeters(r); 00508 break; 00509 case 28: 00510 /* G28: Move to origin */ 00511 cmd_move_to_origin(r, 00512 !isnan(cmd->x), 00513 !isnan(cmd->y), 00514 !isnan(cmd->z)); 00515 break; 00516 case 90: 00517 /* G90: Set to absolute positioning */ 00518 cmd_set_absolute_positioning(r); 00519 break; 00520 case 91: 00521 /* G91: Set to relative positioning */ 00522 cmd_set_relative_positioning(r); 00523 break; 00524 case 92: 00525 /* G92: Set Position */ 00526 cmd_set_position(r, cmd->x, cmd->y, cmd->z, cmd->e); 00527 break; 00528 default: 00529 command_unknown(r); 00530 break; 00531 } 00532 break; 00533 case 'M': 00534 switch (cmd->number) { 00535 case 84: 00536 /* M84: Stop idle hold */ 00537 cmd_stop_idle_hold(r); 00538 break; 00539 case 104: 00540 /* M104: Set Extruder Temperature (Fast) */ 00541 cmd_set_extruder_temp_async(r, cmd->s); 00542 break; 00543 case 105: 00544 /* M105: Get extruder and build base temperature */ 00545 cmd_read_nozzle_and_printbed_temp(r); 00546 break; 00547 case 109: 00548 /* M109: Set Extruder Temperature */ 00549 cmd_set_extruder_temp_sync(r, cmd->s); 00550 break; 00551 case 113: 00552 /* M113: Set Extruder PWM */ 00553 cmd_set_extruder_pwm(r, cmd->s); 00554 break; 00555 case 114: 00556 /* M114: Get Current Position */ 00557 cmd_get_position(r); 00558 break; 00559 case 116: 00560 /* M116: Wait */ 00561 cmd_wait(r); 00562 break; 00563 case 140: 00564 /* M140: Set Bed Temperature (Fast) */ 00565 cmd_set_printbed_temp_async(r, cmd->s); 00566 break; 00567 default: 00568 command_unknown(r); 00569 break; 00570 } 00571 break; 00572 case 'T': 00573 /* T: tool select */ 00574 cmd_select_tool(r, cmd->number); 00575 break; 00576 default: 00577 command_unknown(r); 00578 break; 00579 } 00580 } 00581 00582 void 00583 gcode_init(FILE *host_stream) { 00584 host = host_stream; 00585 expected_line_number = 0; 00586 cmd_init(); 00587 } 00588 00589 int 00590 gcode_process_command() 00591 { 00592 gcode_command cmd; 00593 int parse_result = parse_command(&cmd); 00594 if (parse_result != GCODE_PARSE_COMMAND_SUCCESS) { 00595 send_resend(expected_line_number); 00596 return parse_result; 00597 } 00598 00599 // At this point parsing has succeeded and the checksum is correct 00600 if (cmd.fields & N_SEEN) { 00601 if (cmd.letter == 'M' && cmd.number == 110) { 00602 expected_line_number = cmd.line_number + 1; 00603 fputs("ok\n",host); 00604 return GCODE_PROCESS_COMMAND_SUCCESS; 00605 } else if (cmd.line_number != expected_line_number) { 00606 send_resend(expected_line_number); 00607 return GCODE_UNEXPECTED_LINE; 00608 } 00609 expected_line_number += 1; 00610 } 00611 00612 // At this point the line number is correct as well 00613 cmd_response_t r; 00614 dispatch_command(&cmd,&r); 00615 send_reply(&cmd, &r); 00616 00617 return GCODE_PROCESS_COMMAND_SUCCESS; 00618 } 00619 00620