#include "parameters.h" #include "pins.h" #include "extruder.h" #include "vectors.h" #include "cartesian_dda.h" /* bit-flags for commands and parameters */ #define GCODE_G (1<<0) #define GCODE_M (1<<1) #define GCODE_P (1<<2) #define GCODE_X (1<<3) #define GCODE_Y (1<<4) #define GCODE_Z (1<<5) #define GCODE_I (1<<6) #define GCODE_J (1<<7) #define GCODE_K (1<<8) #define GCODE_F (1<<9) #define GCODE_S (1<<10) #define GCODE_Q (1<<11) #define GCODE_R (1<<12) #define GCODE_E (1<<13) #define PARSE_INT(ch, str, len, val, seen, flag) \ case ch: \ len = scan_int(str, &val, &seen, flag); \ break; #define PARSE_FLOAT(ch, str, len, val, seen, flag) \ case ch: \ len = scan_float(str, &val, &seen, flag); \ break; /* gcode line parse results */ struct GcodeParser { unsigned int seen; int G; int M; float P; float X; float Y; float Z; float E; float I; float J; float F; float S; float R; float Q; }; //our command string char cmdbuffer[COMMAND_SIZE]; char c = '?'; byte serial_count = 0; boolean comment = false; //our feedrate variables. //float feedrate = SLOW_XY_FEEDRATE; /* keep track of the last G code - this is the command mode to use * if there is no command in the current string */ int last_gcode_g = -1; boolean abs_mode = true; //0 = incremental; 1 = absolute float extruder_speed = 0; int scan_int(char *str, int *valp); int scan_float(char *str, float *valp); GcodeParser gc; /* string parse result */ //init our string processing inline void init_process_string() { serial_count = 0; comment = false; } // Get a command and process it void get_and_do_command() { //read in characters if we got them. if (Serial.available()) { c = Serial.read(); if(c == '\r') c = '\n'; // Throw away control chars except \n if(c >= ' ' || c == '\n') { //newlines are ends of commands. if (c != '\n') { // Start of comment - ignore any bytes received from now on if (c == ';') comment = true; // If we're not in comment mode, add it to our array. if (!comment) cmdbuffer[serial_count++] = c; } } } // Data runaway? if(serial_count >= COMMAND_SIZE) init_process_string(); //if we've got a real command, do it if (serial_count && c == '\n') { // Terminate string cmdbuffer[serial_count] = 0; //process our command! process_string(cmdbuffer, serial_count); //clear command. init_process_string(); // Say we're ready for the next one if(debugstring[0] != 0) { Serial.print("ok "); Serial.println(debugstring); debugstring[0] = 0; } else Serial.println("ok"); } } int parse_string(struct GcodeParser * gc, char instruction[ ], int size) { int ind; int len; /* length of parameter argument */ gc->seen = 0; len=0; /* scan the string for commands and parameters, recording the arguments for each, * and setting the seen flag for each that is seen */ for (ind=0; indG, gc->seen, GCODE_G); PARSE_INT('M', &instruction[ind+1], len, gc->M, gc->seen, GCODE_M); PARSE_FLOAT('S', &instruction[ind+1], len, gc->S, gc->seen, GCODE_S); PARSE_FLOAT('P', &instruction[ind+1], len, gc->P, gc->seen, GCODE_P); PARSE_FLOAT('X', &instruction[ind+1], len, gc->X, gc->seen, GCODE_X); PARSE_FLOAT('Y', &instruction[ind+1], len, gc->Y, gc->seen, GCODE_Y); PARSE_FLOAT('Z', &instruction[ind+1], len, gc->Z, gc->seen, GCODE_Z); PARSE_FLOAT('I', &instruction[ind+1], len, gc->I, gc->seen, GCODE_I); PARSE_FLOAT('J', &instruction[ind+1], len, gc->J, gc->seen, GCODE_J); PARSE_FLOAT('F', &instruction[ind+1], len, gc->F, gc->seen, GCODE_F); PARSE_FLOAT('R', &instruction[ind+1], len, gc->R, gc->seen, GCODE_R); PARSE_FLOAT('Q', &instruction[ind+1], len, gc->Q, gc->seen, GCODE_Q); PARSE_FLOAT('E', &instruction[ind+1], len, gc->E, gc->seen, GCODE_E); default: break; } } } //Read the string and execute instructions void process_string(char instruction[], int size) { //the character / means delete block... used for comments and stuff. if (instruction[0] == '/') return; //init baby! FloatPoint fp; FloatPoint sp; float fr; fp.x = 0.0; fp.y = 0.0; fp.z = 0.0; fp.e = 0.0; fp.f = 0.0; //get all our parameters! parse_string(&gc, instruction, size); /* if no command was seen, but parameters were, then use the last G code as * the current command */ if ((!(gc.seen & (GCODE_G | GCODE_M))) && ((gc.seen != 0) && (last_gcode_g >= 0)) ) { /* yes - so use the previous command with the new parameters */ gc.G = last_gcode_g; gc.seen |= GCODE_G; } //did we get a gcode? if (gc.seen & GCODE_G) { last_gcode_g = gc.G; /* remember this for future instructions */ fp = where_i_am; #ifdef MODIFIER_PIN modifier = analogRead(MODIFIER_PIN); // Erik if(((modifier - modifier_old) > 3) || ((modifier - modifier_old) < -3)) { Serial.print("Modifier: "); Serial.println(modifier); modifier_old = modifier; } #endif // Debug Erik: //Serial.print("Modifier: "); //Serial.println(modifier/512); if (abs_mode) { if (gc.seen & GCODE_X) fp.x = gc.X; if (gc.seen & GCODE_Y) fp.y = gc.Y; if (gc.seen & GCODE_Z) fp.z = gc.Z; if (gc.seen & GCODE_E) #ifdef MODIFIER_PIN if(gc.E<0) { Serial.print("Reversing: "); Serial.print(gc.E); Serial.print(", extr pos bef: "); Serial.print(fp.e); fp.e += gc.E*(modifier/512); // Erik Serial.print(", extr pos after: "); Serial.println(fp.e); } else { fp.e += gc.E*(modifier/512); // Erik Serial.print("fp.e is: "); Serial.println(fp.e); } // Erik: Make forced incremental #else fp.e += gc.E; // Erik // fp.e += gc.E; //ERIK: normaal niet aditive! #endif } else { if (gc.seen & GCODE_X) fp.x += gc.X; if (gc.seen & GCODE_Y) fp.y += gc.Y; if (gc.seen & GCODE_Z) fp.z += gc.Z; if (gc.seen & GCODE_E) #ifndef MODIFIER_PIN fp.e += gc.E; #else fp.e += gc.E*(modifier/512); // Erik #endif } // Get feedrate if supplied - feedrates are always absolute??? if ( gc.seen & GCODE_F ) //#ifdef MODIFIER_PIN fp.f = gc.F*(modifier_speed/512); // ERIK ////#else // fp.f = gc.F; //#endif // Process the buffered move commands first // If we get one, return immediately switch (gc.G) { //Rapid move case 0: fr = fp.f; fp.f = FAST_XY_FEEDRATE; qMove(fp); fp.f = fr; return; // Controlled move case 1: qMove(fp); return; //go home. case 28: sp.x = 0.0; sp.y = 0.0; sp.z = 0.0; if(abs_mode) sp.e = where_i_am.e; else sp.e = 0.0; if(where_i_am.z != 0.0) sp.f = FAST_Z_FEEDRATE; else sp.f = FAST_XY_FEEDRATE; qMove(sp); return; /* // We never use this... //go home via an intermediate point. case 30: fr = fp.f; if(where_i_am.z != fp.z) fp.f = FAST_Z_FEEDRATE; else fp.f = FAST_XY_FEEDRATE; fp.f = fr; qMove(fp); sp.x = 0.0; sp.y = 0.0; sp.z = 0.0; if(abs_mode) sp.e = where_i_am.e; else sp.e = 0.0; if(where_i_am.z != 0.0) sp.f = FAST_Z_FEEDRATE; else sp.f = FAST_XY_FEEDRATE; qMove(sp); return; */ default: break; } // Non-buffered G commands // Wait till the buffer q is empty first while(!qEmpty()) delay(WAITING_DELAY); switch (gc.G) { //Dwell case 4: delay((int)(gc.P + 0.5)); // Changed by AB from 1000*gc.P break; //Inches for Units case 20: setUnits(false); break; //mm for Units case 21: setUnits(true); break; //Absolute Positioning case 90: abs_mode = true; break; //Incremental Positioning case 91: abs_mode = false; break; //Set position as fp case 92: setPosition(fp); break; default: Serial.print("huh? G"); Serial.println(gc.G, DEC); } } //find us an m code. if (gc.seen & GCODE_M) { // Wait till the q is empty first while(!qEmpty()) delay(WAITING_DELAY); switch (gc.M) { //TODO: this is a bug because search_string returns 0. gotta fix that. case 0: break; /* case 0: //todo: stop program break; case 1: //todo: optional stop break; case 2: //todo: program end break; */ // Now, with E codes, there is no longer any idea of turning the extruder on or off. // (But see valve on/off below.) /* //turn extruder on, forward case 101: ex[extruder_in_use]->set_direction(1); ex[extruder_in_use]->set_speed(extruder_speed); break; //turn extruder on, reverse case 102: ex[extruder_in_use]->set_direction(0); ex[extruder_in_use]->set_speed(extruder_speed); break; //turn extruder off case 103: ex[extruder_in_use]->set_speed(0); break; */ //custom code for temperature control case 104: if (gc.seen & GCODE_S) { ex[extruder_in_use]->set_temperature((int)gc.S); } break; //custom code for temperature reading case 105: Serial.print("T:"); Serial.println(ex[extruder_in_use]->get_temperature()); break; //turn fan on case 106: ex[extruder_in_use]->set_cooler(255); break; //turn fan off case 107: ex[extruder_in_use]->set_cooler(0); break; /* // Extruder speed is now entirely controlled by E codes //set max extruder speed, 0-255 PWM case 108: if (gc.seen & GCODE_S) extruder_speed = gc.S; break; */ // The valve (real, or virtual...) is now the way to control any extruder (such as // a pressurised paste extruder) that cannot move using E codes. // Open the valve case 126: ex[extruder_in_use]->valve_set(true, (int)(gc.P + 0.5)); break; // Close the valve case 127: ex[extruder_in_use]->valve_set(false, (int)(gc.P + 0.5)); break; default: Serial.print("Huh? M"); Serial.println(gc.M, DEC); } } } int scan_float(char *str, float *valp, unsigned int *seen, unsigned int flag) { float res; int len; char *end; res = (float)strtod(str, &end); len = end - str; if (len > 0) { *valp = res; *seen |= flag; } else *valp = 0; return len; /* length of number */ } int scan_int(char *str, int *valp, unsigned int *seen, unsigned int flag) { int res; int len; char *end; res = (int)strtol(str, &end, 10); len = end - str; if (len > 0) { *valp = res; *seen |= flag; } else *valp = 0; return len; /* length of number */ }