yarf 0.1
Yet Another RepRap Firmware
src/input/gcode.c
Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines