diff --git a/examples/rep2-eeprom.ini b/examples/rep2-eeprom.ini new file mode 100644 index 0000000..d083808 --- /dev/null +++ b/examples/rep2-eeprom.ini @@ -0,0 +1,178 @@ +; +; Replicator 2 EEPROM setting configuration +; + +[byte] + +; VERSION_LOW +; Firmware Version, low byte: 1 byte + +;0x0000= + +; VERSION_HIGH +; Firmware Version, high byte: 1 byte + +;0x0001= + +; AXIS_INVERSION +; Axis inversion flags: 1 byte. +; Axis N (where X=0, Y=1, etc.) is inverted if the Nth bit is set. +; Bit 7 is used for HoldZ OFF: 1 = off, 0 = on + +0x0002= + +; ENDSTOP_INVERSION +; Endstop inversion flags: 1 byte. +; The endstops for axis N (where X=0, Y=1, etc.) are considered +; to be logically inverted if the Nth bit is set. +; Bit 7 is set to indicate endstops are present; it is zero to indicate +; that endstops are not present. +; Ordinary endstops (H21LOB et. al.) are inverted. + +0x0004= + +; DIGI_POT_SETTINGS +; Digital Potentiometer Settings : 5 Bytes + +0x0006= +0x0007= +0x0008= +0x0009= +0x000A= + +; AXIS_HOME_DIRECTION +; axis home direction (1 byte) + +0x000C= + +[integer] + +; AXIS_HOME_POSITIONS_STEPS +; Default locations for the axis in step counts: 5 x 32 bit = 20 bytes + +0x000E= +0x0012= +0x0016= +0x001A= +0x001E= + +[string] + +; MACHINE_NAME +; Name of this machine: 16 bytes (16 bytes extra buffer) + +0x0022=Replicator 2 + +[byte] + +; TOOL_COUNT +; Tool count : 1 byte + +0x0042=1 + +; VID_PID_INFO +; Hardware ID. Must exactly match the USB VendorId/ProductId pair: 4 bytes + +;0x0044= +;0x0045= +;0x0046= +;0x0047= + +; INTERNAL_VERSION +; Version Number for internal releases +;0x0048= + +; Versin number to be tagged with Git Commit +const static uint16_t COMMIT_VERSION = 0x004A; +; HBP Present or not +//$BEGIN_ENTRY +//$type:B +const static uint16_t HBP_PRESENT = 0x004C; +; 40 bytes padding +; Thermistor table 0: 128 bytes +const static uint16_t THERM_TABLE = 0x0074; +; Padding: 8 bytes +// Toolhead 0 data: 28 bytes (see above) +const static uint16_t T0_DATA_BASE = 0x0100; +// Toolhead 0 data: 28 bytes (see above) +const static uint16_t T1_DATA_BASE = 0x011C; +; unused 8 bytes = 0x0138; + +; Light Effect table. 3 Bytes x 3 entries +const static uint16_t LED_STRIP_SETTINGS = 0x0140; +; Buzz Effect table. 4 Bytes x 3 entries +const static uint16_t BUZZ_SETTINGS = 0x014A; +; 1 byte. 0x01 for 'never booted before' 0x00 for 'have been booted before) +;const static uint16_t FIRST_BOOT_FLAG = 0x0156; +; 7 bytes, short int x 3 entries, 1 byte on/off +const static uint16_t PREHEAT_SETTINGS = 0x0158; +; 1 byte, 0x01 for help menus on, 0x00 for off +const static uint16_t FILAMENT_HELP_SETTINGS = 0x0160; +; This indicates how far out of tolerance the toolhead0 toolhead1 distance is +; in steps. 3 x 32 bits = 12 bytes +const static uint16_t TOOLHEAD_OFFSET_SETTINGS = 0x0162; +; Acceleraton settings 22 bytes: 1 byte (on/off), 2 bytes default acceleration rate, +//$BEGIN_ENTRY +//$eeprom_map:acceleration_eeprom_offsets +const static uint16_t ACCELERATION_SETTINGS = 0x016E; +; 2 bytes bot status info bytes +const static uint16_t BOT_STATUS_BYTES = 0x018A; +; axis lengths XYZ AB 5*32bit = 20 bytes +const static uint16_t AXIS_LENGTHS = 0x018C; +; total lifetime print hours, 3bytes +//$BEGIN_ENTRY +//$eeprom_map: build_time_offsets +const static uint16_t TOTAL_BUILD_TIME = 0x01A0; +; axis steps per mm XYZAB 5*32bit = 20 bytes +const static uint16_t AXIS_STEPS_PER_MM = 0x01A4; +; Filament lifetime counter (in steps) 8 bytes (int64) x 2 (for 2 extruders) +const static uint16_t FILAMENT_LIFETIME = 0x01B8; +; Filament trip counter (in steps) 8 bytes (int64) x 2 (for 2 extruders) +const static uint16_t FILAMENT_TRIP = 0x01C8; +; Acceleraton settings 60 bytes: 1 byte (on/off) + acceleration settings +const static uint16_t ACCELERATION2_SETTINGS = 0x01D8; +; axis max feedrates XYZAB 5*32bit = 20 bytes +const static uint16_t AXIS_MAX_FEEDRATES = 0x01F4; +; Hardware configuration settings +//$BEGIN_ENTRY +//$type:B +const static uint16_t BOTSTEP_TYPE = 0x0208; +; temperature offset calibration: 1 byte x 3 heaters = 3 bytes +//$BEGIN_ENTRY +//$type:BBB +const static uint16_t HEATER_CALIBRATION = 0x020A; + +; start of free space +const static uint16_t FREE_EEPROM_STARTS = 0x020B; + + + +//Sailfish specific settings work backwards from the end of the eeprom 0xFFF + +//P-Stop enable (1 byte) +//$BEGIN_ENTRY +//$type:B +const static uint16_t PSTOP_ENABLE = 0x0F90; + +//Use SD card CRC(1 byte) +//$BEGIN_ENTRY +//$type:B +const static uint16_t SD_USE_CRC = 0x0F91; + +//Extruder hold (1 byte) +//$BEGIN_ENTRY +//$type:B +const static uint16_t EXTRUDER_HOLD = 0x0F92; + +//Toolhead offset system (1 byte; 0x00 == RepG 39; 0x01 == RepG 40+) +//$BEGIN_ENTRY +//$type:B +const static uint16_t TOOLHEAD_OFFSET_SYSTEM = 0x0F93; + +;Location of the profiles, 4 x 26 bytes (PROFILES_QUANTITY * PROFILE_SIZE) +const static uint16_t PROFILES_BASE = 0x0F94; +;1 byte, set to PROFILES_INITIALIZED (0xAC) when profiles have been initialized +const static uint16_t PROFILES_INIT = 0x0FFC; +const static uint16_t OVERRIDE_GCODE_TEMP = 0x0FFD; +const static uint16_t HEAT_DURING_PAUSE = 0x0FFE; +const static uint16_t DITTO_PRINT_ENABLED = 0x0FFF; diff --git a/gpx-main.c b/gpx-main.c index f5f2dd7..6663cec 100644 --- a/gpx-main.c +++ b/gpx-main.c @@ -24,6 +24,7 @@ // along with this program; if not, write to the Free Software Foundation, // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#include #include #include #include @@ -84,20 +85,25 @@ static void usage() fputs("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" EOL, stderr); fputs("GNU General Public License for more details." EOL, stderr); - fputs(EOL "Usage: gpx [-dgiprsvw] [-b B] [-c C] [-f F] [-m M] [-x X] [-y Y] [-z Z] IN [OUT]" EOL, stderr); + fputs(EOL "Usage:" EOL, stderr); + fputs("gpx [-dgilpqrstvw] [-b BAUDRATE] [-c CONFIG] [-e EEPROM] [-f DIAMETER] [-m MACHINE] [-n SCALE] [-x X] [-y Y] [-z Z] IN [OUT]" EOL, stderr); fputs(EOL "Options:" EOL, stderr); fputs("\t-d\tsimulated ditto printing" EOL, stderr); fputs("\t-g\tMakerbot/ReplicatorG GCODE flavor" EOL, stderr); fputs("\t-i\tenable stdin and stdout support for command line pipes" EOL, stderr); + fputs("\t-l\tlog to file" EOL, stderr); fputs("\t-p\toverride build percentage" EOL, stderr); + fputs("\t-q\tquiet mode" EOL, stderr); fputs("\t-r\tReprap GCODE flavor" EOL, stderr); fputs("\t-s\tenable USB serial I/O and send x3G output to 3D printer" EOL, stderr); + fputs("\t-t\ttruncate filename (DOS 8.3 format)" EOL, stderr); fputs("\t-v\tverose mode" EOL, stderr); fputs("\t-w\trewrite 5d extrusion values" EOL, stderr); - fputs(EOL "B is baudrate for serial I/O (default is 115200)" EOL, stderr); - fputs("C is the filename of a custom machine definition (ini)" EOL, stderr); - fputs("F is the actual filament diameter in the printer" EOL, stderr); - fputs(EOL "M is the predefined machine type:" EOL, stderr); + fputs(EOL "BAUDRATE: the baudrate for serial I/O (default is 115200)" EOL, stderr); + fputs("CONFIG: the filename of a custom machine definition (ini file)" EOL, stderr); + fputs("EEPROM: the filename of an eeprom settings definition (ini file)" EOL, stderr); + fputs("DIAMETER: the actual filament diameter in the printer" EOL, stderr); + fputs(EOL "MACHINE: the predefined machine type" EOL, stderr); fputs("\tc3 = Cupcake Gen3 XYZ, Mk5/6 + Gen4 Extruder" EOL, stderr); fputs("\tc4 = Cupcake Gen4 XYZ, Mk5/6 + Gen4 Extruder" EOL, stderr); fputs("\tcp4 = Cupcake Pololu XYZ, Mk5/6 + Gen4 Extruder" EOL, stderr); @@ -110,12 +116,13 @@ static void usage() fputs("\tr2 = Replicator 2 (default)" EOL, stderr); fputs("\tr2h = Replicator 2 with HBP" EOL, stderr); fputs("\tr2x = Replicator 2X" EOL, stderr); - fputs(EOL "X,Y & Z are the coordinate system offsets for the conversion:" EOL, stderr); + fputs(EOL "SCALE: the coordinate system scale for the conversion (ABS = 1.0035)" EOL, stderr); + fputs("X,Y & Z: the coordinate system offsets for the conversion" EOL, stderr); fputs("\tX = the x axis offset" EOL, stderr); fputs("\tY = the y axis offset" EOL, stderr); fputs("\tZ = the z axis offset" EOL, stderr); - fputs(EOL "IN is the name of the sliced gcode input filename" EOL, stderr); - fputs("OUT is the name of the x3g output filename or the serial I/O port" EOL, stderr); + fputs(EOL "IN: the name of the sliced gcode input filename" EOL, stderr); + fputs("OUT: the name of the x3g output filename or the serial I/O port" EOL, stderr); fputs(EOL "Examples:" EOL, stderr); fputs("\tgpx -p -m r2 my-sliced-model.gcode" EOL, stderr); fputs("\tgpx -c custom-tom.ini example.gcode /volumes/things/example.x3g" EOL, stderr); @@ -125,17 +132,88 @@ static void usage() exit(1); } +static void sio_open(char *filename, speed_t baud_rate) +{ + struct termios tp; + // open and configure the serial port + if((sio_port = open(filename, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) { + perror("Error opening port"); + exit(-1); + } + + if(fcntl(sio_port, F_SETFL, O_RDWR) < 0) { + perror("Setting port descriptor flags"); + exit(-1); + } + + if(tcgetattr(sio_port, &tp) < 0) { + perror("Error getting port attributes"); + exit(-1); + } + + cfmakeraw(&tp); + + /* + // 8N1 + tp.c_cflag &= ~PARENB; + tp.c_cflag &= ~CSTOPB; + tp.c_cflag &= ~CSIZE; + tp.c_cflag |= CS8; + + // no flow control + tp.c_cflag &= ~CRTSCTS; + + // disable hang-up-on-close to avoid reset + //tp.c_cflag &= ~HUPCL; + + // turn on READ & ignore ctrl lines + tp.c_cflag |= CREAD | CLOCAL; + + // turn off s/w flow ctrl + tp.c_cflag &= ~(IXON | IXOFF | IXANY); + + // make raw + tp.c_cflag &= ~(ICANON | ECHO | ECHOE | ISIG); + tp.c_cflag &= ~OPOST; + + // see: http://unixwiz.net/techtips/termios-vmin-vtime.html + tp.c_cc[VMIN] = 0; + tp.c_cc[VTIME] = 0; + */ + + cfsetspeed(&tp, baud_rate); + // cfsetispeed(&tp, baud_rate); + // cfsetospeed(&tp, baud_rate); + + if(tcsetattr(sio_port, TCSANOW, &tp) < 0) { + perror("Error setting port attributes"); + exit(-1); + } + + sleep(2); + if(tcflush(sio_port, TCIOFLUSH) < 0) { + perror("Error flushing port"); + exit(-1); + } + + if(gpx.flag.verboseMode) fprintf(gpx.log, "Communicating via: %s" EOL, filename); +} + + // GPX program entry point int main(int argc, char * argv[]) { int c, i, rval; + int log_to_file = 0; int standard_io = 0; + int serial_io = 0; + int truncate_filename = 0; char *config = NULL; + char *eeprom = NULL; double filament_diameter = 0; char *buildname = "GPX " GPX_VERSION; char *filename; - struct termios tp; speed_t baud_rate = B115200; // default to standard I/O @@ -171,7 +249,7 @@ int main(int argc, char * argv[]) *appname++ = 'i'; *appname++ = '\0'; appname = gpx.buffer.out; - i = gpx_read_config(&gpx, appname); + i = gpx_load_config(&gpx, appname); if(i == 0) { if(gpx.flag.verboseMode) fprintf(stderr, "Loaded config: %s" EOL, appname); } @@ -184,7 +262,7 @@ int main(int argc, char * argv[]) // READ COMMAND LINE // get the command line options - while ((c = getopt(argc, argv, "b:c:dgf:im:prsvwx:y:z:?")) != -1) { + while ((c = getopt(argc, argv, "b:c:de:gf:ilm:n:pqrstvwx:y:z:?")) != -1) { switch (c) { case 'b': i = atoi(optarg); @@ -222,6 +300,10 @@ int main(int argc, char * argv[]) usage(); } if(gpx.flag.verboseMode) fprintf(stderr, "Setting baud rate to: %i bps" EOL, i); + // fall through + case 's': + serial_io = 1; + gpx.flag.framingEnabled = 1; break; case 'c': config = optarg; @@ -229,6 +311,9 @@ int main(int argc, char * argv[]) case 'd': gpx.flag.dittoPrinting = 1; break; + case 'e': + eeprom = optarg; + break; case 'g': gpx.flag.reprapFlavor = 0; break; @@ -242,20 +327,29 @@ int main(int argc, char * argv[]) case 'i': standard_io = 1; break; + case 'l': + gpx.flag.verboseMode = 1; + log_to_file = 1; + break; case 'm': if(gpx_set_property(&gpx, "printer", "machine_type", optarg)) { usage(); } break; + case 'n': + gpx.user.scale = strtod(optarg, NULL); + break; case 'p': gpx.flag.buildProgress = 1; break; + case 'q': + gpx.flag.logMessages = 0; + break; case 'r': gpx.flag.reprapFlavor = 1; break; - case 's': - gpx.flag.serialIO = 1; - gpx.flag.framingEnabled = 1; + case 't': + truncate_filename = 1; break; case 'v': gpx.flag.verboseMode = 1; @@ -264,13 +358,13 @@ int main(int argc, char * argv[]) gpx.flag.rewrite5D = 1; break; case 'x': - gpx.userOffset.x = strtod(optarg, NULL); + gpx.user.offset.x = strtod(optarg, NULL); break; case 'y': - gpx.userOffset.y = strtod(optarg, NULL); + gpx.user.offset.y = strtod(optarg, NULL); break; case 'z': - gpx.userOffset.z = strtod(optarg, NULL); + gpx.user.offset.z = strtod(optarg, NULL); break; case '?': default: @@ -278,11 +372,44 @@ int main(int argc, char * argv[]) } } + argc -= optind; + argv += optind; + + // LOG TO FILE + + if(log_to_file && argc > 0) { + filename = (argc > 1 && !serial_io) ? argv[1] : argv[0]; + // or use the input filename with a .log extension + char *dot = strrchr(filename, '.'); + if(dot) { + long l = dot - filename; + memcpy(gpx.buffer.out, filename, l); + filename = gpx.buffer.out + l; + } + // or just append one if no .gcode extension is present + else { + size_t sl = strlen(filename); + memcpy(gpx.buffer.out, filename, sl); + filename = gpx.buffer.out + sl; + } + *filename++ = '.'; + *filename++ = 'l'; + *filename++ = 'o'; + *filename++ = 'g'; + *filename++ = '\0'; + filename = gpx.buffer.out; + + if((gpx.log = fopen(filename, "w+")) == NULL) { + gpx.log = stderr; + perror("Error opening log"); + } + } + // READ CONFIGURATION if(config) { - if(gpx.flag.verboseMode) fprintf(stderr, "Reading custom config: %s" EOL, config); - i = gpx_read_config(&gpx, config); + if(gpx.flag.verboseMode) fprintf(gpx.log, "Loading custom config: %s" EOL, config); + i = gpx_load_config(&gpx, config); if (i < 0) { fprintf(stderr, "Command line error: cannot load configuration file '%s'" EOL, config); usage(); @@ -294,18 +421,27 @@ int main(int argc, char * argv[]) } if(baud_rate == B57600 && gpx.machine.type >= MACHINE_TYPE_REPLICATOR_1) { - if(gpx.flag.verboseMode) fputs("WARNING: a 57600 bps baud rate will cause problems with Repicator 2/2X Mightyboards" EOL, stderr); + if(gpx.flag.verboseMode) fputs("WARNING: a 57600 bps baud rate will cause problems with Repicator 2/2X Mightyboards" EOL, gpx.log); } - argc -= optind; - argv += optind; - // OPEN FILES AND PORTS FOR INPUT AND OUTPUT + if(standard_io) { + if(serial_io) { + if(argc > 0) { + filename = argv[0]; + sio_open(filename, baud_rate); + } + else { + fputs("Command line error: port required for serial I/O" EOL, stderr); + usage(); + } + } + } // open the input filename if one is provided - if(argc > 0) { + else if(argc > 0) { filename = argv[0]; - if(gpx.flag.verboseMode) fprintf(stderr, "Reading from: %s" EOL, filename); + if(gpx.flag.verboseMode) fprintf(gpx.log, "Reading from: %s" EOL, filename); if((file_in = fopen(filename, "rw")) == NULL) { perror("Error opening input"); exit(1); @@ -318,6 +454,7 @@ int main(int argc, char * argv[]) else { buildname = filename; } + argc--; argv++; // use the output filename if one is provided @@ -325,7 +462,7 @@ int main(int argc, char * argv[]) filename = argv[0]; } else { - if(gpx.flag.serialIO) { + if(serial_io) { fputs("Command line error: port required for serial I/O" EOL, stderr); usage(); } @@ -342,83 +479,46 @@ int main(int argc, char * argv[]) memcpy(gpx.buffer.out, filename, sl); filename = gpx.buffer.out + sl; } - *filename++ = '.'; - *filename++ = 'x'; - *filename++ = '3'; - *filename++ = 'g'; - *filename++ = '\0'; + if(truncate_filename) { + char *s = gpx.buffer.out; + for(i = 0; s < filename && i < 8; i++) { + char c = *s; + if(isalnum(c)) { + *s++ = toupper(c); + } + else { + *s++ = '_'; + } + } + *s++ = '.'; + *s++ = 'X'; + *s++ = '3'; + *s++ = 'G'; + *s++ = '\0'; + } + else { + *filename++ = '.'; + *filename++ = 'x'; + *filename++ = '3'; + *filename++ = 'g'; + *filename++ = '\0'; + } filename = gpx.buffer.out; } - if(gpx.flag.serialIO) { - // open and configure the serial port - if((sio_port = open(filename, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) { - perror("Error opening port"); - exit(-1); - } - - if(fcntl(sio_port, F_SETFL, O_RDWR) < 0) { - perror("Setting port descriptor flags"); - exit(-1); - } + + // trim build name extension + char *dot = strrchr(buildname, '.'); + if(dot) *dot = 0; - if(tcgetattr(sio_port, &tp) < 0) { - perror("Error getting port attributes"); - exit(-1); - } - - cfmakeraw(&tp); - - /* - // 8N1 - tp.c_cflag &= ~PARENB; - tp.c_cflag &= ~CSTOPB; - tp.c_cflag &= ~CSIZE; - tp.c_cflag |= CS8; - - // no flow control - tp.c_cflag &= ~CRTSCTS; - - // disable hang-up-on-close to avoid reset - //tp.c_cflag &= ~HUPCL; - - // turn on READ & ignore ctrl lines - tp.c_cflag |= CREAD | CLOCAL; - - // turn off s/w flow ctrl - tp.c_cflag &= ~(IXON | IXOFF | IXANY); - - // make raw - tp.c_cflag &= ~(ICANON | ECHO | ECHOE | ISIG); - tp.c_cflag &= ~OPOST; - - // see: http://unixwiz.net/techtips/termios-vmin-vtime.html - tp.c_cc[VMIN] = 0; - tp.c_cc[VTIME] = 0; - */ - - cfsetspeed(&tp, baud_rate); - // cfsetispeed(&tp, baud_rate); - // cfsetospeed(&tp, baud_rate); - - if(tcsetattr(sio_port, TCSANOW, &tp) < 0) { - perror("Error setting port attributes"); - exit(-1); - } - - sleep(2); - if(tcflush(sio_port, TCIOFLUSH) < 0) { - perror("Error flushing port"); - exit(-1); - } - - if(gpx.flag.verboseMode) fprintf(stderr, "Communicating via: %s" EOL, filename); + if(serial_io) { + sio_open(filename, baud_rate); } else { if((file_out = fopen(filename, "wb")) == NULL) { perror("Error creating output"); exit(-1); } - if(gpx.flag.verboseMode) fprintf(stderr, "Writing to: %s" EOL, filename); + if(gpx.flag.verboseMode) fprintf(gpx.log, "Writing to: %s" EOL, filename); // write a second copy to the SD Card if(gpx.sdCardPath) { long sl = strlen(gpx.sdCardPath); @@ -440,31 +540,56 @@ int main(int argc, char * argv[]) gpx.buffer.out[sl + l] = 0; } file_out2 = fopen(gpx.buffer.out, "wb"); - if(file_out2 && gpx.flag.verboseMode) fprintf(stderr, "Writing to: %s" EOL, gpx.buffer.out); + if(file_out2 && gpx.flag.verboseMode) fprintf(gpx.log, "Writing to: %s" EOL, gpx.buffer.out); } } } - else if(!standard_io) { + else { fputs("Command line error: provide an input file or enable standard I/O" EOL, stderr); usage(); } - // at this point we have read the command line, set the machine definition - // and both the input and output files or ports are open, so its time to parse - // the gcode input and convert it to x3g output. - - // READ INPUT AND CONVERT TO OUTPUT - - gpx_start_build(&gpx, buildname); - - if(gpx.flag.serialIO) { - rval = gpx_send_file(&gpx, file_in, sio_port); + if(log_to_file) { + if(gpx.flag.buildProgress) fputs("Build progress: enabled" EOL, gpx.log); + if(gpx.flag.dittoPrinting) fputs("Ditto printing: enabled" EOL, gpx.log); + if(serial_io) fputs("Serial IO: enabled" EOL, gpx.log); + fprintf(gpx.log, "GCode flavor: %s" EOL, gpx.flag.reprapFlavor ? "Reprap" : "Makerbot"); + if(gpx.flag.rewrite5D) fputs("Rewrite 5D: enabled" EOL, gpx.log); } - else { - rval = gpx_convert_file(&gpx, file_in, file_out, file_out2); - } - - gpx_end_build(&gpx); + /* at this point we have read the command line, set the machine definition + and both the input and output files or ports are open, so its time to parse + the gcode input and convert it to x3g output. */ + + if(serial_io) { + // READ CONFIG AND WRITE EEPROM SETTINGS + if(eeprom) { + if(gpx.flag.verboseMode) fprintf(gpx.log, "Loading eeprom config: %s" EOL, eeprom); + i = eeprom_load_config(&gpx, eeprom); + if (i < 0) { + fprintf(stderr, "Command line error: cannot load eeprom configuration file '%s'" EOL, eeprom); + usage(); + } + else if (i > 0) { + fprintf(stderr, "(line %u) Eeprom configuration syntax error in %s: unrecognised paremeters" EOL, i, eeprom); + usage(); + } + exit(SUCCESS); + } + else { + // READ INPUT AND SEND OUTPUT TO PRINTER + + gpx_start_convert(&gpx, buildname); + rval = gpx_convert_and_send(&gpx, file_in, sio_port); + gpx_end_convert(&gpx); + } + } + else { + // READ INPUT AND CONVERT TO OUTPUT + + gpx_start_convert(&gpx, buildname); + rval = gpx_convert(&gpx, file_in, file_out, file_out2); + gpx_end_convert(&gpx); + } exit(rval); } diff --git a/gpx.c b/gpx.c index 01f0872..7508064 100644 --- a/gpx.c +++ b/gpx.c @@ -26,11 +26,13 @@ #include #include -//#include +#include #include #include #include #include +#include +#include #include #include "gpx.h" @@ -38,8 +40,9 @@ #define A 0 #define B 1 -#define SHOW(FN) if(gpx->flag.showErrorMessages) FN -#define CALL(FN) if((rval = FN) != 0) return rval +#define SHOW(FN) if(gpx->flag.logMessages) {FN;} +#define VERBOSE(FN) if(gpx->flag.verboseMode && gpx->flag.logMessages) {FN;} +#define CALL(FN) if((rval = FN) != SUCCESS) return rval // Machine definitions @@ -217,91 +220,117 @@ int gpx_set_machine(Gpx *gpx, char *machine) if(MACHINE_IS("c3")) { if(gpx->machine.type != 1) { gpx->machine = cupcake_G3; - if(gpx->flag.verboseMode) fputs("Loading machine definition: Cupcake Gen3 XYZ, Mk5/6 + Gen4 Extruder" EOL, stderr); + VERBOSE( fputs("Loading machine definition: Cupcake Gen3 XYZ, Mk5/6 + Gen4 Extruder" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m c3" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m c3" EOL, stderr); } else if(MACHINE_IS("c4")) { if(gpx->machine.type != 2) { gpx->machine = cupcake_G4; - if(gpx->flag.verboseMode) fputs("Loading machine definition: Cupcake Gen4 XYZ, Mk5/6 + Gen4 Extruder" EOL, stderr); + VERBOSE( fputs("Loading machine definition: Cupcake Gen4 XYZ, Mk5/6 + Gen4 Extruder" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m c4" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m c4" EOL, stderr); } else if(MACHINE_IS("cp4")) { if(gpx->machine.type != 3) { gpx->machine = cupcake_P4; - if(gpx->flag.verboseMode) fputs("Loading machine definition: Cupcake Pololu XYZ, Mk5/6 + Gen4 Extruder" EOL, stderr); + VERBOSE( fputs("Loading machine definition: Cupcake Pololu XYZ, Mk5/6 + Gen4 Extruder" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m cp4" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m cp4" EOL, stderr); } else if(MACHINE_IS("cpp")) { if(gpx->machine.type != 4) { gpx->machine = cupcake_PP; - if(gpx->flag.verboseMode) fputs("Loading machine definition: Cupcake Pololu XYZ, Mk5/6 + Pololu Extruder" EOL, stderr); + VERBOSE( fputs("Loading machine definition: Cupcake Pololu XYZ, Mk5/6 + Pololu Extruder" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m cpp" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m cpp" EOL, stderr); } else if(MACHINE_IS("t6")) { if(gpx->machine.type != 5) { gpx->machine = thing_o_matic_7; - if(gpx->flag.verboseMode) fputs("Loading machine definition: TOM Mk6 - single extruder" EOL, stderr); + VERBOSE( fputs("Loading machine definition: TOM Mk6 - single extruder" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m t6" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m t6" EOL, stderr); } else if(MACHINE_IS("t7")) { if(gpx->machine.type != 5) { gpx->machine = thing_o_matic_7; - if(gpx->flag.verboseMode) fputs("Loading machine definition: TOM Mk7 - single extruder" EOL, stderr); + VERBOSE( fputs("Loading machine definition: TOM Mk7 - single extruder" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m t7" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m t7" EOL, stderr); } else if(MACHINE_IS("t7d")) { if(gpx->machine.type != 6) { gpx->machine = thing_o_matic_7D; - if(gpx->flag.verboseMode) fputs("Loading machine definition: TOM Mk7 - dual extruder" EOL, stderr); + VERBOSE( fputs("Loading machine definition: TOM Mk7 - dual extruder" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m t7d" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m t7d" EOL, stderr); } else if(MACHINE_IS("r1")) { if(gpx->machine.type != 7) { gpx->machine = replicator_1; - if(gpx->flag.verboseMode) fputs("Loading machine definition: Replicator 1 - single extruder" EOL, stderr); + VERBOSE( fputs("Loading machine definition: Replicator 1 - single extruder" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m r1" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m r1" EOL, stderr); } else if(MACHINE_IS("r1d")) { if(gpx->machine.type != 8) { gpx->machine = replicator_1D; - if(gpx->flag.verboseMode) fputs("Loading machine definition: Replicator 1 - dual extruder" EOL, stderr); + VERBOSE( fputs("Loading machine definition: Replicator 1 - dual extruder" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m r1d" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m r1d" EOL, stderr); } else if(MACHINE_IS("r2")) { if(gpx->machine.type != 9) { gpx->machine = replicator_2; - if(gpx->flag.verboseMode) fputs("Loading machine definition: Replicator 2" EOL, stderr); + VERBOSE( fputs("Loading machine definition: Replicator 2" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m r2" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m r2" EOL, stderr); } else if(MACHINE_IS("r2h")) { if(gpx->machine.type != 10) { gpx->machine = replicator_2H; - if(gpx->flag.verboseMode) fputs("Loading machine definition: Replicator 2 with HBP" EOL, stderr); + VERBOSE( fputs("Loading machine definition: Replicator 2 with HBP" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m r2h" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m r2h" EOL, stderr); } else if(MACHINE_IS("r2x")) { if(gpx->machine.type != 11) { gpx->machine = replicator_2X; - if(gpx->flag.verboseMode) fputs("Loading machine definition: Replicator 2X" EOL, stderr); + VERBOSE( fputs("Loading machine definition: Replicator 2X" EOL, gpx->log) ); + } + else { + VERBOSE( fputs("Ignoring duplicate machine definition: -m r2x" EOL, gpx->log) ); } - else if(gpx->flag.verboseMode) fputs("Ignoring duplicate machine definition: -m r2x" EOL, stderr); } else { - return 1; + return ERROR; } - return 0; + // update known position mask + gpx->axis.mask = gpx->machine.extruder_count == 1 ? (XYZ_BIT_MASK | A_IS_SET) : AXES_BIT_MASK;; + return SUCCESS; } // PRIVATE FUNCTION PROTOTYPES @@ -361,12 +390,14 @@ void gpx_initialize(Gpx *gpx, int firstTime) gpx->current.position.a = 0.0; gpx->current.position.b = 0.0; - gpx->current.positionKnown = 0; gpx->current.feedrate = get_home_feedrate(gpx, XYZ_BIT_MASK); gpx->current.extruder = 0; gpx->current.offset = 0; gpx->current.percent = 0; + gpx->axis.positionKnown = 0; + gpx->axis.mask = gpx->machine.extruder_count == 1 ? (XYZ_BIT_MASK | A_IS_SET) : AXES_BIT_MASK;; + // initialize the accumulated rounding error gpx->excess.a = 0.0; gpx->excess.b = 0.0; @@ -380,9 +411,10 @@ void gpx_initialize(Gpx *gpx, int firstTime) // initialize the command line offset if(firstTime) { - gpx->userOffset.x = 0.0; - gpx->userOffset.y = 0.0; - gpx->userOffset.z = 0.0; + gpx->user.offset.x = 0.0; + gpx->user.offset.y = 0.0; + gpx->user.offset.z = 0.0; + gpx->user.scale = 1.0; } for(i = 0; i < 2; i++) { @@ -409,8 +441,10 @@ void gpx_initialize(Gpx *gpx, int firstTime) gpx->filamentLength = 1; } - if(firstTime) gpx->commandAtIndex = 0; - gpx->commandAtLength = 0; + if(firstTime) { + gpx->commandAtIndex = 0; + gpx->commandAtLength = 0; + } gpx->commandAtZ = 0.0; // SETTINGS @@ -424,22 +458,25 @@ void gpx_initialize(Gpx *gpx, int firstTime) gpx->flag.extruderIsRelative = 0; if(firstTime) { - gpx->flag.reprapFlavor = 1; // default is reprap flavor + gpx->flag.reprapFlavor = 1; // reprap flavor is enabled by default gpx->flag.dittoPrinting = 0; gpx->flag.buildProgress = 0; gpx->flag.verboseMode = 0; + gpx->flag.logMessages = 1; // logging is enabled by default gpx->flag.rewrite5D = 0; - gpx->flag.serialIO = 0; } - + // STATE gpx->flag.programState = 0; gpx->flag.doPauseAtZPos = 0; gpx->flag.pausePending = 0; gpx->flag.macrosEnabled = 0; + if(firstTime) { + gpx->flag.loadMacros = 1; + gpx->flag.runMacros = 1; + } gpx->flag.framingEnabled = 0; - if(firstTime) gpx->flag.showErrorMessages = 1; gpx->longestDDA = 0; gpx->layerHeight = 0.34; @@ -463,9 +500,13 @@ void gpx_initialize(Gpx *gpx, int firstTime) gpx->callbackHandler = NULL; gpx->callbackData = NULL; + + // LOGGING + + if(firstTime) gpx->log = stderr; } -// STATE +// PRINT STATE #define start_program() gpx->flag.programState = RUNNING_STATE #define end_program() gpx->flag.programState = ENDED_STATE @@ -480,6 +521,11 @@ static void write_8(Gpx *gpx, unsigned char value) *gpx->buffer.ptr++ = value; } +static unsigned char read_8(Gpx *gpx) +{ + return *gpx->buffer.ptr++; +} + static void write_16(Gpx *gpx, unsigned short value) { union { @@ -491,6 +537,17 @@ static void write_16(Gpx *gpx, unsigned short value) *gpx->buffer.ptr++ = u.b[1]; } +static unsigned short read_16(Gpx *gpx) +{ + union { + unsigned short s; + unsigned char b[2]; + } u; + u.b[0] = *gpx->buffer.ptr++; + u.b[1] = *gpx->buffer.ptr++; + return u.s; +} + static void write_32(Gpx *gpx, unsigned int value) { union { @@ -504,6 +561,19 @@ static void write_32(Gpx *gpx, unsigned int value) *gpx->buffer.ptr++ = u.b[3]; } +static unsigned int read_32(Gpx *gpx) +{ + union { + unsigned int i; + unsigned char b[4]; + } u; + u.b[0] = *gpx->buffer.ptr++; + u.b[1] = *gpx->buffer.ptr++; + u.b[2] = *gpx->buffer.ptr++; + u.b[3] = *gpx->buffer.ptr++; + return u.i; +} + static void write_float(Gpx *gpx, float value) { union { @@ -517,6 +587,37 @@ static void write_float(Gpx *gpx, float value) *gpx->buffer.ptr++ = u.b[3]; } +static float read_float(Gpx *gpx) +{ + union { + float f; + unsigned char b[4]; + } u; + u.b[0] = *gpx->buffer.ptr++; + u.b[1] = *gpx->buffer.ptr++; + u.b[2] = *gpx->buffer.ptr++; + u.b[3] = *gpx->buffer.ptr++; + return u.f; +} + +static long write_bytes(Gpx *gpx, char *data, long length) +{ + long l = length; + while(l--) { + *gpx->buffer.ptr++ = *data++; + } + return length; +} + +static long read_bytes(Gpx *gpx, char *data, long length) +{ + long l = length; + while(l--) { + *data++ = *gpx->buffer.ptr++; + } + return length; +} + static long write_string(Gpx *gpx, char *string, long length) { long l = length; @@ -585,13 +686,14 @@ static int end_frame(Gpx *gpx) if(gpx->flag.framingEnabled) { unsigned char *start = (unsigned char *)gpx->buffer.out + 2; unsigned char *end = (unsigned char *)gpx->buffer.ptr; - long frameLength = end - start; - gpx->buffer.out[1] = (unsigned char)frameLength; - *gpx->buffer.ptr++ = calculate_crc(start, frameLength); + size_t payload_length = end - start; + gpx->buffer.out[1] = (unsigned char)payload_length; + *gpx->buffer.ptr++ = calculate_crc(start, payload_length); } - gpx->accumulated.bytes += gpx->buffer.ptr - gpx->buffer.out; - if(gpx->callbackHandler) return gpx->callbackHandler(gpx, gpx->callbackData); - return 0; + size_t length = gpx->buffer.ptr - gpx->buffer.out; + gpx->accumulated.bytes += length; + if(gpx->callbackHandler) return gpx->callbackHandler(gpx, gpx->callbackData, gpx->buffer.out, length); + return SUCCESS; } // 5D VECTOR FUNCTIONS @@ -632,27 +734,27 @@ static double magnitude(int flag, Ptr5d vector) static double largest_axis(int flag, Ptr5d vector) { - double length, rval = 0.0; + double length, result = 0.0; if(flag & X_IS_SET) { - rval = fabs(vector->x); + result = fabs(vector->x); } if(flag & Y_IS_SET) { length = fabs(vector->y); - if(rval < length) rval = length; + if(result < length) result = length; } if(flag & Z_IS_SET) { length = fabs(vector->z); - if(rval < length) rval = length; + if(result < length) result = length; } if(flag & A_IS_SET) { length = fabs(vector->a); - if(rval < length) rval = length; + if(result < length) result = length; } if(flag & B_IS_SET) { length = fabs(vector->b); - if(rval < length) rval = length; + if(result < length) result = length; } - return rval; + return result; } // calculate the dda for the longest axis for the current machine definition @@ -797,65 +899,472 @@ static Point5d delta_steps(Gpx *gpx,Point5d deltaMM) // X3G QUERIES +#define COMMAND_OFFSET 2 +#define EXTRUDER_ID_OFFSET 3 +#define QUERY_COMMAND_OFFSET 4 +#define EEPROM_LENGTH_OFFSET 8 + // 00 - Get version -// 01 - Initialize firmware to boot state +static int get_version(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 0); + + // uint16: host version + write_16(gpx, HOST_VERSION); + + return end_frame(gpx); +} + +/* 01 - Initialize firmware to boot state + This is treated as a NOOP in the Sailfish firmware. */ + +static int initialize_firmware(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 1); + + return end_frame(gpx); +} // 02 - Get available buffer size -// 03 - Clear buffer +static int get_buffer_size(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 2); + + return end_frame(gpx); +} + +// 03 - Clear buffer (same as 07 and 17) + +static int clear_buffer(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 3); + + return end_frame(gpx); +} // 07 - Abort immediately +static int abort_immediately(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 7); + + return end_frame(gpx); +} + // 08 - Pause/Resume -// 10 - Tool query +static int pause_resume(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 8); + + return end_frame(gpx); +} -// 11 - Is finished +// 10 - Extruder Query Commands + +// Query 00 - Query firmware version information + +static int get_extruder_version(Gpx *gpx, unsigned extruder_id) +{ + begin_frame(gpx); + + write_8(gpx, 10); + + // uint8: ID of the extruder to query + write_8(gpx, extruder_id); + + // uint8: Query command to send to the extruder + write_8(gpx, 0); + + // uint8: Length of the extruder command payload (N) + write_8(gpx, 2); + + // uint16: host version + write_16(gpx, HOST_VERSION); + + return end_frame(gpx); +} + +// Query 02 - Get extruder temperature + +static int get_extruder_temperature(Gpx *gpx, unsigned extruder_id) +{ + begin_frame(gpx); + + write_8(gpx, 10); + + // uint8: ID of the extruder to query + write_8(gpx, extruder_id); + + // uint8: Query command to send to the extruder + write_8(gpx, 2); + + // uint8: Length of the extruder command payload (N) + write_8(gpx, 0); + + return end_frame(gpx); +} + +// Query 22 - Is extruder ready + +static int is_extruder_ready(Gpx *gpx, unsigned extruder_id) +{ + begin_frame(gpx); + + write_8(gpx, 10); + + // uint8: ID of the extruder to query + write_8(gpx, extruder_id); + + // uint8: Query command to send to the extruder + write_8(gpx, 22); + + // uint8: Length of the extruder command payload (N) + write_8(gpx, 0); + + return end_frame(gpx); +} + +// Query 30 - Get build platform temperature + +static int get_build_platform_temperature(Gpx *gpx, unsigned extruder_id) +{ + begin_frame(gpx); + + write_8(gpx, 10); + + // uint8: ID of the extruder to query + write_8(gpx, extruder_id); + + // uint8: Query command to send to the extruder + write_8(gpx, 30); + + // uint8: Length of the extruder command payload (N) + write_8(gpx, 0); + + return end_frame(gpx); +} + +// Query 32 - Get extruder target temperature + +static int get_extruder_target_temperature(Gpx *gpx, unsigned extruder_id) +{ + begin_frame(gpx); + + write_8(gpx, 10); + + // uint8: ID of the extruder to query + write_8(gpx, extruder_id); + + // uint8: Query command to send to the extruder + write_8(gpx, 32); + + // uint8: Length of the extruder command payload (N) + write_8(gpx, 0); + + return end_frame(gpx); +} + +// Query 33 - Get build platform target temperature + +static int get_build_platform_target_temperature(Gpx *gpx, unsigned extruder_id) +{ + begin_frame(gpx); + + write_8(gpx, 10); + + // uint8: ID of the extruder to query + write_8(gpx, extruder_id); + + // uint8: Query command to send to the extruder + write_8(gpx, 33); + + // uint8: Length of the extruder command payload (N) + write_8(gpx, 0); + + return end_frame(gpx); +} + +// Query 35 - Is build platform ready? + +static int is_build_platform_ready(Gpx *gpx, unsigned extruder_id) +{ + begin_frame(gpx); + + write_8(gpx, 10); + + // uint8: ID of the extruder to query + write_8(gpx, extruder_id); + + // uint8: Query command to send to the extruder + write_8(gpx, 35); + + // uint8: Length of the extruder command payload (N) + write_8(gpx, 0); + + return end_frame(gpx); +} + +// Query 36 - Get extruder status + +static int get_extruder_status(Gpx *gpx, unsigned extruder_id) +{ + begin_frame(gpx); + + write_8(gpx, 10); + + // uint8: ID of the extruder to query + write_8(gpx, extruder_id); + + // uint8: Query command to send to the extruder + write_8(gpx, 36); + + // uint8: Length of the extruder command payload (N) + write_8(gpx, 0); + + return end_frame(gpx); +} + +// Query 37 - Get PID state + +static int get_PID_state(Gpx *gpx, unsigned extruder_id) +{ + begin_frame(gpx); + + write_8(gpx, 10); + + // uint8: ID of the extruder to query + write_8(gpx, extruder_id); + + // uint8: Query command to send to the extruder + write_8(gpx, 37); + + // uint8: Length of the extruder command payload (N) + write_8(gpx, 0); + + return end_frame(gpx); +} + +// 11 - Is ready + +static int is_ready(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 11); + + return end_frame(gpx); +} // 12 - Read from EEPROM +static int read_eeprom(Gpx *gpx, unsigned address, unsigned length) +{ + begin_frame(gpx); + + write_8(gpx, 12); + + // uint16: EEPROM memory offset to begin reading from + write_16(gpx, address); + + // uint8: Number of bytes to read, N. + write_8(gpx, length); + + return end_frame(gpx); +} + // 13 - Write to EEPROM +static int write_eeprom(Gpx *gpx, unsigned address, char *data, unsigned length) +{ + begin_frame(gpx); + + write_8(gpx, 13); + + // uint16: EEPROM memory offset to begin writing to + write_16(gpx, address); + + // uint8: Number of bytes to write + write_8(gpx, length); + + // N bytes: Data to write to EEPROM + write_bytes(gpx, data, length); + + return end_frame(gpx); +} + // 14 - Capture to file +static int capture_to_file(Gpx *gpx, char *filename) +{ + begin_frame(gpx); + + write_8(gpx, 14); + + /* 1+N bytes: Filename to write to, in ASCII, terminated with a null character. + N can be 1-12 bytes long, not including the null character. */ + write_string(gpx, filename, strlen(filename)); + + return end_frame(gpx); +} + // 15 - End capture to file +static int end_capture_to_file(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 15); + + return end_frame(gpx); +} + // 16 - Play back capture +static int play_back_capture(Gpx *gpx, char *filename) +{ + begin_frame(gpx); + + write_8(gpx, 16); + + /* 1+N bytes: Filename to write to, in ASCII, terminated with a null character. + N can be 1-12 bytes long, not including the null character. */ + write_string(gpx, filename, strlen(filename)); + + return end_frame(gpx); +} + // 17 - Reset +static int reset(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 17); + + return end_frame(gpx); +} + // 18 - Get next filename +static int get_next_filename(Gpx *gpx, unsigned restart) +{ + begin_frame(gpx); + + write_8(gpx, 18); + + // uint8: 0 if file listing should continue, 1 to restart listing. + write_8(gpx, restart); + + return end_frame(gpx); +} + // 20 - Get build name +static int get_build_name(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 20); + + return end_frame(gpx); +} + // 21 - Get extended position +static int get_extended_position(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 21); + + return end_frame(gpx); +} + // 22 - Extended stop +static int extended_stop(Gpx *gpx, unsigned halt_steppers, unsigned clear_queue) +{ + unsigned flag = 0; + if(halt_steppers) flag |= 0x1; + if(clear_queue) flag |= 0x2; + + begin_frame(gpx); + + write_8(gpx, 22); + + /* uint8: Bitfield indicating which subsystems to shut down. + If bit 0 is set, halt all stepper motion. + If bit 1 is set, clear the command queue. */ + write_8(gpx, flag); + + return end_frame(gpx); +} + // 23 - Get motherboard status +static int get_motherboard_status(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 23); + + return end_frame(gpx); +} + // 24 - Get build statistics -// 25 - Get communication statistics +static int get_build_statistics(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 24); + + return end_frame(gpx); +} // 27 - Get advanced version number +static int get_advanced_version_number(Gpx *gpx) +{ + begin_frame(gpx); + + write_8(gpx, 27); + + // uint16: Host version + write_16(gpx, HOST_VERSION); + + return end_frame(gpx); +} + // X3G COMMANDS // 131 - Find axes minimums // 132 - Find axes maximums -static int home_axes(Gpx *gpx, unsigned direction) +static int home_axes(Gpx *gpx, unsigned axes, unsigned direction) { Point5d unitVector; - int xyz_flag = gpx->command.flag & XYZ_BIT_MASK; double feedrate = gpx->command.flag & F_IS_SET ? gpx->current.feedrate : get_home_feedrate(gpx, gpx->command.flag); double longestAxis = 0.0; assert(direction <= 1); // compute the slowest feedrate - if(xyz_flag & X_IS_SET) { + if(axes & X_IS_SET) { if(gpx->machine.x.home_feedrate < feedrate) { feedrate = gpx->machine.x.home_feedrate; } @@ -863,10 +1372,10 @@ static int home_axes(Gpx *gpx, unsigned direction) longestAxis = gpx->machine.x.steps_per_mm; // confirm machine compatibility if(direction != gpx->machine.x.endstop) { - SHOW( fprintf(stderr, "(line %u) Semantic warning: X axis homing to %s endstop" EOL, gpx->lineNumber, direction ? "maximum" : "minimum") ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: X axis homing to %s endstop" EOL, gpx->lineNumber, direction ? "maximum" : "minimum") ); } } - if(xyz_flag & Y_IS_SET) { + if(axes & Y_IS_SET) { if(gpx->machine.y.home_feedrate < feedrate) { feedrate = gpx->machine.y.home_feedrate; } @@ -875,10 +1384,10 @@ static int home_axes(Gpx *gpx, unsigned direction) longestAxis = gpx->machine.y.steps_per_mm; } if(direction != gpx->machine.y.endstop) { - SHOW( fprintf(stderr, "(line %u) Semantic warning: Y axis homing to %s endstop" EOL, gpx->lineNumber, direction ? "maximum" : "minimum") ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: Y axis homing to %s endstop" EOL, gpx->lineNumber, direction ? "maximum" : "minimum") ); } } - if(xyz_flag & Z_IS_SET) { + if(axes & Z_IS_SET) { if(gpx->machine.z.home_feedrate < feedrate) { feedrate = gpx->machine.z.home_feedrate; } @@ -887,12 +1396,12 @@ static int home_axes(Gpx *gpx, unsigned direction) longestAxis = gpx->machine.z.steps_per_mm; } if(direction != gpx->machine.z.endstop) { - SHOW( fprintf(stderr, "(line %u) Semantic warning: Z axis homing to %s endstop" EOL, gpx->lineNumber, direction ? "maximum" : "minimum") ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: Z axis homing to %s endstop" EOL, gpx->lineNumber, direction ? "maximum" : "minimum") ); } } // unit vector distance in mm - double distance = magnitude(xyz_flag, &unitVector); + double distance = magnitude(axes, &unitVector); // move duration in microseconds = distance / feedrate * 60,000,000 double microseconds = distance / feedrate * 60000000.0; // time between steps for longest axis = microseconds / longestStep @@ -905,7 +1414,7 @@ static int home_axes(Gpx *gpx, unsigned direction) write_8(gpx, direction == ENDSTOP_IS_MIN ? 131 :132); // uint8: Axes bitfield. Axes whose bits are set will be moved. - write_8(gpx, xyz_flag); + write_8(gpx, axes); // uint32: Feedrate, in microseconds between steps on the max delta. (DDA) write_32(gpx, step_delay); @@ -979,6 +1488,11 @@ static int set_nozzle_temperature(Gpx *gpx, unsigned extruder_id, unsigned tempe { assert(extruder_id < gpx->machine.extruder_count); + double tDelta = (double)temperature - (double)gpx->tool[extruder_id].nozzle_temperature - AMBIENT_TEMP; + if(tDelta > 0.0) { + gpx->accumulated.time += tDelta * NOZZLE_TIME; + } + begin_frame(gpx); write_8(gpx, 136); @@ -1063,10 +1577,10 @@ static int set_valve(Gpx *gpx, unsigned extruder_id, unsigned state) return end_frame(gpx); } - else if(gpx->flag.verboseMode) { - fputs("Warning: ignoring M126/M127 with Gen 4 extruder electronics" EOL, stderr); + else if(gpx->flag.logMessages) { + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: ignoring M126/M127 with Gen 4 extruder electronics" EOL, gpx->lineNumber) ); } - return 0; + return SUCCESS; } // Action 31 - Set build platform target temperature @@ -1074,6 +1588,11 @@ static int set_valve(Gpx *gpx, unsigned extruder_id, unsigned state) static int set_build_platform_temperature(Gpx *gpx, unsigned extruder_id, unsigned temperature) { assert(extruder_id < gpx->machine.extruder_count); + + double tDelta = (double)temperature - (double)gpx->tool[extruder_id].build_platform_temperature - AMBIENT_TEMP; + if(tDelta > 0.0) { + gpx->accumulated.time += tDelta * HBP_TIME; + } begin_frame(gpx); @@ -1142,6 +1661,9 @@ static int queue_absolute_point(Gpx *gpx) // uint32: Feedrate, in microseconds between steps on the max delta. (DDA) write_32(gpx, (int)longestDDA); + // reset current position + gpx->axis.positionKnown = gpx->axis.mask; + return end_frame(gpx); } @@ -1240,7 +1762,7 @@ static int queue_new_point(Gpx *gpx, unsigned milliseconds) Point5d steps = mm_to_steps(gpx, &target, &gpx->excess); - gpx->accumulated.time += milliseconds / 1000.0; + gpx->accumulated.time += (milliseconds / 1000.0) * ACCELERATION_TIME; begin_frame(gpx); @@ -1431,7 +1953,7 @@ static int display_message(Gpx *gpx, char *message, unsigned vPos, unsigned hPos assert(vPos < 4); assert(hPos < 20); - int rval = 0; + int rval; long bytesSent = 0; unsigned bitfield = 0; unsigned seconds = 0; @@ -1476,10 +1998,9 @@ static int display_message(Gpx *gpx, char *message, unsigned vPos, unsigned hPos long rowLength = length - bytesSent; bytesSent += write_string(gpx, message + bytesSent, rowLength < maxLength ? rowLength : maxLength); - rval = end_frame(gpx); - if(rval) break; + CALL( end_frame(gpx) ); } - return rval; + return SUCCESS; } // 150 - Set Build Percentage @@ -1495,7 +2016,7 @@ static int set_build_progress(Gpx *gpx, unsigned percent) // uint8: percent (0-100) write_8(gpx, percent); - // uint8: 0 (reserved for future use) (reserved for future use) + // uint8: 0 (reserved for future use) write_8(gpx, 0); return end_frame(gpx); @@ -1573,9 +2094,10 @@ static int end_build(Gpx *gpx) static int queue_ext_point(Gpx *gpx, double feedrate) { - // Because we don't know our previous position, we can't calculate the feedrate or - // distance correctly, so we use an unaccelerated command with a fixed DDA - if(!gpx->current.positionKnown) { + /* If we don't know our previous position on a command axis, we can't calculate the feedrate + or distance correctly, so we use an unaccelerated command with a fixed DDA. */ + unsigned mask = gpx->command.flag & gpx->axis.mask; + if((gpx->axis.positionKnown & mask) != mask) { return queue_absolute_point(gpx); } Point5d deltaMM = delta_mm(gpx); @@ -1703,7 +2225,7 @@ static int queue_ext_point(Gpx *gpx, double feedrate) // Convert dda_interval into dda_rate (dda steps per second on the longest axis) double dda_rate = 1000000.0 / dda_interval; - gpx->accumulated.time += minutes * 60; + gpx->accumulated.time += (minutes * 60) * ACCELERATION_TIME; begin_frame(gpx); @@ -1738,7 +2260,7 @@ static int queue_ext_point(Gpx *gpx, double feedrate) return end_frame(gpx); } - return 0; + return SUCCESS; } // 156 - Set segment acceleration @@ -1800,7 +2322,7 @@ static int stream_version(Gpx *gpx) return end_frame(gpx); } - return 0; + return SUCCESS; } // 158 - Pause @ zPos @@ -1849,7 +2371,7 @@ static int add_filament(Gpx *gpx, char *filament_id, double diameter, unsigned t gpx->filament[index].LED = LED; } else { - SHOW( fprintf(stderr, "(line %u) Buffer overflow: too many @filament definitions (maximum = %i)" EOL, gpx->lineNumber, FILAMENT_MAX - 1) ); + SHOW( fprintf(gpx->log, "(line %u) Buffer overflow: too many @filament definitions (maximum = %i)" EOL, gpx->lineNumber, FILAMENT_MAX - 1) ); index = 0; } } @@ -1863,48 +2385,56 @@ static int add_command_at(Gpx *gpx, double z, char *filament_id, unsigned nozzle int rval; int index = filament_id ? find_filament(gpx, filament_id) : 0; if(index < 0) { - SHOW( fprintf(stderr, "(line %u) Semantic error: @pause macro with undefined filament name '%s', use a @filament macro to define it" EOL, gpx->lineNumber, filament_id) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic error: @pause macro with undefined filament name '%s', use a @filament macro to define it" EOL, gpx->lineNumber, filament_id) ); index = 0; } // insert command if(gpx->commandAtLength < COMMAND_AT_MAX) { - int i = gpx->commandAtLength; - if(z <= gpx->commandAtZ) { - // make a space - while(i > 0 && z <= gpx->commandAt[i - 1].z) { - gpx->commandAt[i] = gpx->commandAt[i - 1]; - i--; - } - gpx->commandAt[i].z = z; - gpx->commandAt[i].filament_index = index; - gpx->commandAt[i].nozzle_temperature = nozzle_temperature; - gpx->commandAt[i].build_platform_temperature = build_platform_temperature; - gpx->commandAtZ = gpx->commandAt[gpx->commandAtLength].z; - } - // append command - else { - gpx->commandAt[i].z = z; - gpx->commandAt[i].filament_index = index; - gpx->commandAt[i].nozzle_temperature = nozzle_temperature; - gpx->commandAt[i].build_platform_temperature = build_platform_temperature; - gpx->commandAtZ = z; - } - // nonzero temperature signals a temperature change, not a pause @ zPos - // so if its the first pause @ zPos que it up - if(nozzle_temperature == 0 && build_platform_temperature == 0 && gpx->commandAtLength == 0) { - if(gpx->flag.macrosEnabled) { - CALL( pause_at_zpos(gpx, z) ); + if(gpx->flag.loadMacros) { + int i = gpx->commandAtLength; + if(z <= gpx->commandAtZ) { + // make a space + while(i > 0 && z <= gpx->commandAt[i - 1].z) { + gpx->commandAt[i] = gpx->commandAt[i - 1]; + i--; + } + gpx->commandAt[i].z = z; + gpx->commandAt[i].filament_index = index; + gpx->commandAt[i].nozzle_temperature = nozzle_temperature; + gpx->commandAt[i].build_platform_temperature = build_platform_temperature; + gpx->commandAtZ = gpx->commandAt[gpx->commandAtLength].z; } + // append command else { - gpx->flag.pausePending = 1; + gpx->commandAt[i].z = z; + gpx->commandAt[i].filament_index = index; + gpx->commandAt[i].nozzle_temperature = nozzle_temperature; + gpx->commandAt[i].build_platform_temperature = build_platform_temperature; + gpx->commandAtZ = z; } + // nonzero temperature signals a temperature change, not a pause @ zPos + // so if its the first pause @ zPos que it up + if(nozzle_temperature == 0 && build_platform_temperature == 0 && gpx->commandAtLength == 0) { + if(gpx->flag.macrosEnabled) { + CALL( pause_at_zpos(gpx, z) ); + } + else { + gpx->flag.pausePending = 1; + } + } + gpx->commandAtLength++; } - gpx->commandAtLength++; } else { - SHOW( fprintf(stderr, "(line %u) Buffer overflow: too many @pause definitions (maximum = %i)" EOL, gpx->lineNumber, COMMAND_AT_MAX) ); + SHOW( fprintf(gpx->log, "(line %u) Buffer overflow: too many @pause definitions (maximum = %i)" EOL, gpx->lineNumber, COMMAND_AT_MAX) ); } - return 0; + return SUCCESS; +} + +static int display_tag(Gpx *gpx) { + int rval; + CALL( display_message(gpx, "GPX " GPX_VERSION, 0, 0, 2, 0) ); + return SUCCESS; } // TARGET POSITION @@ -1913,22 +2443,25 @@ static int add_command_at(Gpx *gpx, double z, char *filament_id, unsigned nozzle static int calculate_target_position(Gpx *gpx) { - int rval = 0; + int rval; // G10 ofset Point3d userOffset = gpx->offset[gpx->current.offset]; + double userScale = 1.0; if(gpx->flag.macrosEnabled) { // plus command line offset - userOffset.x += gpx->userOffset.x; - userOffset.y += gpx->userOffset.y; - userOffset.z += gpx->userOffset.z; + userOffset.x += gpx->user.offset.x; + userOffset.y += gpx->user.offset.y; + userOffset.z += gpx->user.offset.z; + // multiply by command line scale + userScale = gpx->user.scale; } // CALCULATE TARGET POSITION // x if(gpx->command.flag & X_IS_SET) { - gpx->target.position.x = gpx->flag.relativeCoordinates ? (gpx->current.position.x + gpx->command.x) : (gpx->command.x + userOffset.x); + gpx->target.position.x = gpx->flag.relativeCoordinates ? (gpx->current.position.x + (gpx->command.x * userScale)) : ((gpx->command.x + userOffset.x) * userScale); } else { gpx->target.position.x = gpx->current.position.x; @@ -1936,7 +2469,7 @@ static int calculate_target_position(Gpx *gpx) // y if(gpx->command.flag & Y_IS_SET) { - gpx->target.position.y = gpx->flag.relativeCoordinates ? (gpx->current.position.y + gpx->command.y) : (gpx->command.y + userOffset.y); + gpx->target.position.y = gpx->flag.relativeCoordinates ? (gpx->current.position.y + (gpx->command.y * userScale)) : ((gpx->command.y + userOffset.y) * userScale); } else { gpx->target.position.y = gpx->current.position.y; @@ -1944,7 +2477,7 @@ static int calculate_target_position(Gpx *gpx) // z if(gpx->command.flag & Z_IS_SET) { - gpx->target.position.z = gpx->flag.relativeCoordinates ? (gpx->current.position.z + gpx->command.z) : (gpx->command.z + userOffset.z); + gpx->target.position.z = gpx->flag.relativeCoordinates ? (gpx->current.position.z + (gpx->command.z * userScale)) : ((gpx->command.z + userOffset.z) * userScale); } else { gpx->target.position.z = gpx->current.position.z; @@ -1989,7 +2522,7 @@ static int calculate_target_position(Gpx *gpx) // CHECK FOR COMMAND @ Z POS // check if there are more commands on the stack - if(gpx->flag.macrosEnabled && gpx->commandAtIndex < gpx->commandAtLength) { + if(gpx->flag.macrosEnabled && gpx->flag.runMacros && gpx->commandAtIndex < gpx->commandAtLength) { // check if the next command will cross the z threshold if(gpx->commandAt[gpx->commandAtIndex].z <= gpx->target.position.z) { // is this a temperature change macro? @@ -2001,20 +2534,32 @@ static int calculate_target_position(Gpx *gpx) if((gpx->current.extruder == A || gpx->tool[A].nozzle_temperature) && gpx->tool[A].nozzle_temperature != nozzle_temperature) { CALL( set_nozzle_temperature(gpx, A, nozzle_temperature) ); gpx->tool[A].nozzle_temperature = gpx->override[A].active_temperature = nozzle_temperature; + VERBOSE( fprintf(gpx->log, "(@zPos %0.2f) Nozzle[A] temperature %uc" EOL, + gpx->commandAt[gpx->commandAtIndex].z, + nozzle_temperature) ); } if((gpx->current.extruder == B || gpx->tool[B].nozzle_temperature) && gpx->tool[B].nozzle_temperature != nozzle_temperature) { CALL( set_nozzle_temperature(gpx, B, nozzle_temperature) ); gpx->tool[B].nozzle_temperature = gpx->override[B].active_temperature = nozzle_temperature; + VERBOSE( fprintf(gpx->log, "(@zPos %0.2f) Nozzle[B] temperature %uc" EOL, + gpx->commandAt[gpx->commandAtIndex].z, + nozzle_temperature) ); } } if(build_platform_temperature) { if(gpx->machine.a.has_heated_build_platform && gpx->tool[A].build_platform_temperature && gpx->tool[A].build_platform_temperature != build_platform_temperature) { CALL( set_build_platform_temperature(gpx, A, build_platform_temperature) ); gpx->tool[A].build_platform_temperature = gpx->override[A].build_platform_temperature = build_platform_temperature; + VERBOSE( fprintf(gpx->log, "(@zPos %0.2f) Build platform[A] temperature %uc" EOL, + gpx->commandAt[gpx->commandAtIndex].z, + build_platform_temperature) ); } else if(gpx->machine.b.has_heated_build_platform && gpx->tool[B].build_platform_temperature && gpx->tool[B].build_platform_temperature != build_platform_temperature) { CALL( set_build_platform_temperature(gpx, B, build_platform_temperature) ); gpx->tool[B].build_platform_temperature = gpx->override[B].build_platform_temperature = build_platform_temperature; + VERBOSE( fprintf(gpx->log, "(@zPos %0.2f) Build platform[B] temperature %uc" EOL, + gpx->commandAt[gpx->commandAtIndex].z, + build_platform_temperature) ); } } gpx->commandAtIndex++; @@ -2022,19 +2567,25 @@ static int calculate_target_position(Gpx *gpx) // no its a pause macro else if(gpx->commandAt[gpx->commandAtIndex].z <= gpx->target.position.z) { int index = gpx->commandAt[gpx->commandAtIndex].filament_index; + VERBOSE( fprintf(gpx->log, "(@zPos %0.2f) %s", + gpx->commandAt[gpx->commandAtIndex].z, + gpx->filament[index].colour) ); // override filament diameter - if(gpx->filament[index].diameter > 0.0001) { + double filament_diameter = gpx->filament[index].diameter; + if(filament_diameter > 0.0001) { + VERBOSE( fprintf(gpx->log, ", %0.2fmm", filament_diameter) ); if(gpx->flag.dittoPrinting) { - set_filament_scale(gpx, B, gpx->filament[index].diameter); - set_filament_scale(gpx, A, gpx->filament[index].diameter); + set_filament_scale(gpx, B, filament_diameter); + set_filament_scale(gpx, A, filament_diameter); } else { - set_filament_scale(gpx, gpx->current.extruder, gpx->filament[index].diameter); + set_filament_scale(gpx, gpx->current.extruder, filament_diameter); } } + unsigned temperature = gpx->filament[index].temperature; // override nozzle temperature - if(gpx->filament[index].temperature) { - unsigned temperature = gpx->filament[index].temperature; + if(temperature) { + VERBOSE( fprintf(gpx->log, ", %uc", temperature) ); if(gpx->tool[gpx->current.extruder].nozzle_temperature != temperature) { if(gpx->flag.dittoPrinting) { CALL( set_nozzle_temperature(gpx, B, temperature) ); @@ -2055,10 +2606,11 @@ static int calculate_target_position(Gpx *gpx) if(gpx->commandAtIndex < gpx->commandAtLength) { gpx->flag.doPauseAtZPos = COMMAND_QUE_MAX; } + VERBOSE( fputs(EOL, gpx->log) ); } } } - return rval; + return SUCCESS; } static void update_current_position(Gpx *gpx) @@ -2073,7 +2625,7 @@ static void update_current_position(Gpx *gpx) } } gpx->current.position = gpx->target.position; - gpx->current.positionKnown = 1; + if(!gpx->flag.relativeCoordinates) gpx->axis.positionKnown |= gpx->command.flag & gpx->axis.mask; } // TOOL CHANGE @@ -2104,7 +2656,7 @@ static int do_tool_change(Gpx *gpx, int timeout) { CALL( change_extruder_offset(gpx, gpx->target.extruder) ); // set current extruder so changes in E are expressed as changes to A or B gpx->current.extruder = gpx->target.extruder; - return 0; + return SUCCESS; } // PARSER PRE-PROCESSOR @@ -2209,7 +2761,7 @@ static char *normalize_comment(char *p) { static int parse_macro(Gpx *gpx, const char* macro, char *p) { - int rval = 0; + int rval; char *name = NULL; double z = 0.0; double diameter = 0.0; @@ -2262,7 +2814,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) } } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: unrecognised macro parameter" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: unrecognised macro parameter" EOL, gpx->lineNumber) ); break; } } @@ -2270,7 +2822,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) if(MACRO_IS("machine") || MACRO_IS("printer") || MACRO_IS("slicer")) { if(name) { if(gpx_set_machine(gpx, name)) { - SHOW( fprintf(stderr, "(line %u) Semantic error: @%s macro with unrecognised type '%s'" EOL, gpx->lineNumber, macro, name) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic error: @%s macro with unrecognised type '%s'" EOL, gpx->lineNumber, macro, name) ); } gpx->override[A].packing_density = gpx->machine.nominal_packing_density; gpx->override[B].packing_density = gpx->machine.nominal_packing_density; @@ -2283,7 +2835,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) if(gpx->machine.a.has_heated_build_platform) gpx->override[A].build_platform_temperature = build_platform_temperature; else if(gpx->machine.b.has_heated_build_platform) gpx->override[B].build_platform_temperature = build_platform_temperature; else { - SHOW( fprintf(stderr, "(line %u) Semantic warning: @%s macro cannot override non-existant heated build platform" EOL, gpx->lineNumber, macro) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: @%s macro cannot override non-existant heated build platform" EOL, gpx->lineNumber, macro) ); } } if(LED) { @@ -2296,7 +2848,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) if(name) { if(NAME_IS("ditto")) { if(gpx->machine.extruder_count == 1) { - SHOW( fprintf(stderr, "(line %u) Semantic warning: ditto printing cannot access non-existant second extruder" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: ditto printing cannot access non-existant second extruder" EOL, gpx->lineNumber) ); gpx->flag.dittoPrinting = 0; } else { @@ -2305,11 +2857,11 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) } else if(NAME_IS("progress")) gpx->flag.buildProgress = 1; else { - SHOW( fprintf(stderr, "(line %u) Semantic error: @enable macro with unrecognised parameter '%s'" EOL, gpx->lineNumber, name) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic error: @enable macro with unrecognised parameter '%s'" EOL, gpx->lineNumber, name) ); } } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: @enable macro with missing parameter" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: @enable macro with missing parameter" EOL, gpx->lineNumber) ); } } // ;@filament mm c # @@ -2318,7 +2870,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) add_filament(gpx, name, diameter, nozzle_temperature, LED); } else { - SHOW( fprintf(stderr, "(line %u) Semantic error: @filament macro with missing name" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic error: @filament macro with missing name" EOL, gpx->lineNumber) ); } } // ;@right mm c @@ -2328,7 +2880,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) if(index > 0) { if(gpx->filament[index].diameter > 0.0001) set_filament_scale(gpx, A, gpx->filament[index].diameter); if(gpx->filament[index].temperature) gpx->override[A].active_temperature = gpx->filament[index].temperature; - return 0; + return SUCCESS; } } if(z > 0.0001) gpx->override[A].packing_density = z; @@ -2342,7 +2894,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) if(index > 0) { if(gpx->filament[index].diameter > 0.0001) set_filament_scale(gpx, B, gpx->filament[index].diameter); if(gpx->filament[index].temperature) gpx->override[B].active_temperature = gpx->filament[index].temperature; - return 0; + return SUCCESS; } } if(z > 0.0001) gpx->override[A].packing_density = z; @@ -2358,7 +2910,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) CALL( add_command_at(gpx, diameter, name, 0, 0) ); } else { - SHOW( fprintf(stderr, "(line %u) Semantic error: @pause macro with missing zPos" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic error: @pause macro with missing zPos" EOL, gpx->lineNumber) ); } } // ;@temp c @@ -2372,17 +2924,18 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) CALL( add_command_at(gpx, diameter, NULL, nozzle_temperature, build_platform_temperature) ); } else { - SHOW( fprintf(stderr, "(line %u) Semantic error: @%s macro with missing zPos" EOL, gpx->lineNumber, macro) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic error: @%s macro with missing zPos" EOL, gpx->lineNumber, macro) ); } } else { - SHOW( fprintf(stderr, "(line %u) Semantic error: @%s macro with missing temperature" EOL, gpx->lineNumber, macro) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic error: @%s macro with missing temperature" EOL, gpx->lineNumber, macro) ); } } // ;@start c else if(MACRO_IS("start")) { if(nozzle_temperature || build_platform_temperature) { if(nozzle_temperature) { + VERBOSE( fprintf(gpx->log, "(@start) Nozzle temperature %uc" EOL, nozzle_temperature) ); if(gpx->tool[A].nozzle_temperature && gpx->tool[A].nozzle_temperature != nozzle_temperature) { if(program_is_running()) { CALL( set_nozzle_temperature(gpx, A, nozzle_temperature) ); @@ -2403,6 +2956,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) } } if(build_platform_temperature) { + VERBOSE( fprintf(gpx->log, "(@start) Build platform temperature %uc" EOL, build_platform_temperature) ); if(gpx->machine.a.has_heated_build_platform && gpx->tool[A].build_platform_temperature && gpx->tool[A].build_platform_temperature != build_platform_temperature) { if(program_is_running()) { CALL( set_build_platform_temperature(gpx, A, build_platform_temperature) ); @@ -2420,7 +2974,9 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) else if(name) { int index = find_filament(gpx, name); if(index > 0) { + VERBOSE( fprintf(gpx->log, "(@start) %s", name) ); if(gpx->filament[index].diameter > 0.0001) { + VERBOSE( fprintf(gpx->log, ", %0.2fmm", gpx->filament[index].diameter) ); if(gpx->flag.dittoPrinting) { set_filament_scale(gpx, B, gpx->filament[index].diameter); set_filament_scale(gpx, A, gpx->filament[index].diameter); @@ -2434,6 +2990,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) } nozzle_temperature = gpx->filament[index].temperature; if(nozzle_temperature) { + VERBOSE( fprintf(gpx->log, ", %uc", nozzle_temperature) ); if(gpx->tool[A].nozzle_temperature && gpx->tool[A].nozzle_temperature != nozzle_temperature) { if(program_is_running()) { CALL( set_nozzle_temperature(gpx, A, nozzle_temperature) ); @@ -2453,9 +3010,10 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) gpx->override[B].active_temperature = nozzle_temperature; } } + VERBOSE( fputs(EOL, gpx->log) ); } else { - SHOW( fprintf(stderr, "(line %u) Semantic error: @start with undefined filament name '%s', use a @filament macro to define it" EOL, gpx->lineNumber, name ? name : "") ); + SHOW( fprintf(gpx->log, "(line %u) Semantic error: @start with undefined filament name '%s', use a @filament macro to define it" EOL, gpx->lineNumber, name ? name : "") ); } } } @@ -2472,7 +3030,7 @@ static int parse_macro(Gpx *gpx, const char* macro, char *p) else if(MACRO_IS("header") && MACRO_IS("footer")) { gpx->flag.macrosEnabled = 0; } - return 0; + return SUCCESS; } /* @@ -2638,10 +3196,11 @@ static int ini_parse(Gpx* gpx, const char* filename, { FILE* file; int error; - + unsigned ln = gpx->lineNumber; file = fopen(filename, "r"); - if(!file) return -1; + if(!file) return ERROR; error = ini_parse_file(gpx, file, handler); + gpx->lineNumber = ln; fclose(file); return error; } @@ -2682,7 +3241,7 @@ int gpx_set_property(Gpx *gpx, const char* section, const char* property, char* else if(PROPERTY_IS("machine_type")) { // only load/clobber the on-board machine definition if the one specified is different if(gpx_set_machine(gpx, value)) { - SHOW( fprintf(stderr, "(line %u) Configuration error: unrecognised machine type '%s'" EOL, gpx->lineNumber, value) ); + SHOW( fprintf(gpx->log, "(line %u) Configuration error: unrecognised machine type '%s'" EOL, gpx->lineNumber, value) ); return gpx->lineNumber; } gpx->override[A].packing_density = gpx->machine.nominal_packing_density; @@ -2693,7 +3252,7 @@ int gpx_set_property(Gpx *gpx, const char* section, const char* property, char* if(VALUE_IS("reprap")) gpx->flag.reprapFlavor = 1; else if(VALUE_IS("makerbot")) gpx->flag.reprapFlavor = 0; else { - SHOW( fprintf(stderr, "(line %u) Configuration error: unrecognised GCODE flavor '%s'" EOL, gpx->lineNumber, value) ); + SHOW( fprintf(gpx->log, "(line %u) Configuration error: unrecognised GCODE flavor '%s'" EOL, gpx->lineNumber, value) ); return gpx->lineNumber; } } @@ -2767,38 +3326,41 @@ int gpx_set_property(Gpx *gpx, const char* section, const char* property, char* || PROPERTY_IS("slicer_filament_diameter")) gpx->machine.nominal_filament_diameter = strtod(value, NULL); else if(PROPERTY_IS("packing_density")) gpx->machine.nominal_packing_density = strtod(value, NULL); else if(PROPERTY_IS("nozzle_diameter")) gpx->machine.nozzle_diameter = strtod(value, NULL); - else if(PROPERTY_IS("extruder_count")) gpx->machine.extruder_count = atoi(value); + else if(PROPERTY_IS("extruder_count")) { + gpx->machine.extruder_count = atoi(value); + gpx->axis.mask = gpx->machine.extruder_count == 1 ? (XYZ_BIT_MASK | A_IS_SET) : AXES_BIT_MASK;; + } else if(PROPERTY_IS("timeout")) gpx->machine.timeout = atoi(value); else goto SECTION_ERROR; } else { - SHOW( fprintf(stderr, "(line %u) Configuration error: unrecognised section [%s]" EOL, gpx->lineNumber, section) ); + SHOW( fprintf(gpx->log, "(line %u) Configuration error: unrecognised section [%s]" EOL, gpx->lineNumber, section) ); return gpx->lineNumber; } - return 0; + return SUCCESS; SECTION_ERROR: - SHOW( fprintf(stderr, "(line %u) Configuration error: [%s] section contains unrecognised property %s = %s" EOL, gpx->lineNumber, section, property, value) ); + SHOW( fprintf(gpx->log, "(line %u) Configuration error: [%s] section contains unrecognised property %s = %s" EOL, gpx->lineNumber, section, property, value) ); return gpx->lineNumber; } -int gpx_read_config(Gpx *gpx, const char *filename) +int gpx_load_config(Gpx *gpx, const char *filename) { return ini_parse(gpx, filename, gpx_set_property); } -void gpx_register_callback(Gpx *gpx, int (*callbackHandler)(Gpx*, void*), void *callbackData) +void gpx_register_callback(Gpx *gpx, int (*callbackHandler)(Gpx*, void*, char*, size_t), void *callbackData) { gpx->callbackHandler = callbackHandler; gpx->callbackData = callbackData; } -void gpx_start_build(Gpx *gpx, char *buildName) +void gpx_start_convert(Gpx *gpx, char *buildName) { if(buildName) gpx->buildName = buildName; if(gpx->flag.dittoPrinting && gpx->machine.extruder_count == 1) { - SHOW( fputs("Configuration error: ditto printing cannot access non-existant second extruder" EOL, stderr) ); + SHOW( fputs("Configuration error: ditto printing cannot access non-existant second extruder" EOL, gpx->log) ); gpx->flag.dittoPrinting = 0; } @@ -2815,23 +3377,6 @@ void gpx_start_build(Gpx *gpx, char *buildName) } } -void gpx_end_build(Gpx *gpx) -{ - if(gpx->flag.verboseMode) { - long seconds = round(gpx->accumulated.time); - long minutes = seconds / 60; - long hours = minutes / 60; - minutes %= 60; - seconds %= 60; - fprintf(stderr, "Extrusion length: %#0.3f metres" EOL, round(gpx->accumulated.a + gpx->accumulated.b) / 1000); - fputs("Estimated print time: ", stderr); - if(hours) fprintf(stderr, "%lu hours ", hours); - if(minutes) fprintf(stderr, "%lu minutes ", minutes); - fprintf(stderr, "%lu seconds" EOL, seconds); - fprintf(stderr, "X3G output filesize: %lu bytes" EOL, gpx->accumulated.bytes); - } -} - int gpx_convert_line(Gpx *gpx, char *gcode_line) { int i, rval; @@ -2848,7 +3393,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) digits = p; p = normalize_word(p); if(*p == 0) { - SHOW( fprintf(stderr, "(line %u) Syntax error: line number command word 'N' is missing digits" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: line number command word 'N' is missing digits" EOL, gpx->lineNumber) ); next_line = gpx->lineNumber + 1; } else { @@ -2960,7 +3505,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) break; default: - SHOW( fprintf(stderr, "(line %u) Syntax warning: unrecognised command word '%c'" EOL, gpx->lineNumber, c) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax warning: unrecognised command word '%c'" EOL, gpx->lineNumber, c) ); } } else if(*p == ';') { @@ -3004,7 +3549,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) char *e = strchr(p + 1, ')'); // check for nested comment if(s && e && s < e) { - SHOW( fprintf(stderr, "(line %u) Syntax warning: nested comment detected" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax warning: nested comment detected" EOL, gpx->lineNumber) ); e = strrchr(p + 1, ')'); } if(e) { @@ -3014,7 +3559,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) p = e + 1; } else { - SHOW( fprintf(stderr, "(line %u) Syntax warning: comment is missing closing ')'" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax warning: comment is missing closing ')'" EOL, gpx->lineNumber) ); gpx->command.comment = normalize_comment(p + 1); gpx->command.flag |= COMMENT_IS_SET; *p = 0; @@ -3030,7 +3575,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) break; } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: unrecognised gcode '%s'" EOL, gpx->lineNumber, p) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: unrecognised gcode '%s'" EOL, gpx->lineNumber, p) ); break; } } @@ -3045,7 +3590,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) gpx->target.extruder = tool_id; } else { - SHOW( fprintf(stderr, "(line %u) Semantic warning: T%u cannot select non-existant extruder" EOL, gpx->lineNumber, tool_id) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: T%u cannot select non-existant extruder" EOL, gpx->lineNumber, tool_id) ); } } @@ -3137,7 +3682,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: G4 is missing delay parameter, use Pn where n is milliseconds" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: G4 is missing delay parameter, use Pn where n is milliseconds" EOL, gpx->lineNumber) ); } break; @@ -3151,7 +3696,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) // set standby temperature if(gpx->command.flag & R_IS_SET) { unsigned temperature = (unsigned)gpx->command.r; - if(temperature > TEMPERATURE_MAX) temperature = TEMPERATURE_MAX; + if(temperature > NOZZLE_MAX) temperature = NOZZLE_MAX; switch(i) { case 1: gpx->override[A].standby_temperature = temperature; @@ -3164,7 +3709,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) // set tool temperature if(gpx->command.flag & S_IS_SET) { unsigned temperature = (unsigned)gpx->command.s; - if(temperature > TEMPERATURE_MAX) temperature = TEMPERATURE_MAX; + if(temperature > NOZZLE_MAX) temperature = NOZZLE_MAX; switch(i) { case 1: gpx->override[A].active_temperature = temperature; @@ -3176,7 +3721,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: G10 is missing coordiante system, use Pn where n is 1-6" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: G10 is missing coordiante system, use Pn where n is 1-6" EOL, gpx->lineNumber) ); } break; @@ -3186,6 +3731,62 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) case 71: break; + // G28 - Home given axes to machine defined endstop + case 28: { + unsigned endstop_max = 0; + unsigned endstop_min = 0; + if(gpx->command.flag & F_IS_SET) gpx->current.feedrate = gpx->command.f; + + if(gpx->command.flag & X_IS_SET) { + if(gpx->machine.x.endstop) { + endstop_max |= X_IS_SET; + } + else { + endstop_min |= X_IS_SET; + } + } + + if(gpx->command.flag & Y_IS_SET) { + if(gpx->machine.y.endstop) { + endstop_max |= Y_IS_SET; + } + else { + endstop_min |= Y_IS_SET; + } + } + + if(gpx->command.flag & Z_IS_SET) { + if(gpx->machine.z.endstop) { + endstop_max |= Z_IS_SET; + } + else { + endstop_min |= Z_IS_SET; + } + } + // home xy before z + if(gpx->machine.x.endstop) { + if(endstop_max) { + CALL( home_axes(gpx, endstop_max, ENDSTOP_IS_MAX) ); + } + if(endstop_min) { + CALL( home_axes(gpx, endstop_min, ENDSTOP_IS_MIN) ); + } + } + else { + if(endstop_min) { + CALL( home_axes(gpx, endstop_min, ENDSTOP_IS_MAX) ); + } + if(endstop_max) { + CALL( home_axes(gpx, endstop_max, ENDSTOP_IS_MIN) ); + } + } + command_emitted++; + gpx->axis.positionKnown &= ~(gpx->command.flag & gpx->axis.mask); + gpx->excess.a = 0; + gpx->excess.b = 0; + break; + } + // G53 - Set absolute coordinate system case 53: gpx->current.offset = 0; @@ -3228,27 +3829,27 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) // G91 - Relative Positioning case 91: - if(gpx->current.positionKnown) { + if((gpx->axis.positionKnown & XYZ_BIT_MASK) == XYZ_BIT_MASK) { gpx->flag.relativeCoordinates = 1; } else { - SHOW( fprintf(stderr, "(line %u) Semantic error: G91 switch to relitive positioning prior to first absolute move" EOL, gpx->lineNumber) ); - return -1; + SHOW( fprintf(gpx->log, "(line %u) Semantic error: G91 switch to relitive positioning prior to first absolute move" EOL, gpx->lineNumber) ); + return ERROR; } break; // G92 - Define current position on axes case 92: { - if(gpx->command.flag & X_IS_SET) gpx->current.position.x = gpx->command.x; - if(gpx->command.flag & Y_IS_SET) gpx->current.position.y = gpx->command.y; - if(gpx->command.flag & Z_IS_SET) gpx->current.position.z = gpx->command.z; + double userScale = gpx->flag.macrosEnabled ? gpx->user.scale : 1.0; + if(gpx->command.flag & X_IS_SET) gpx->current.position.x = gpx->command.x * userScale; + if(gpx->command.flag & Y_IS_SET) gpx->current.position.y = gpx->command.y * userScale; + if(gpx->command.flag & Z_IS_SET) gpx->current.position.z = gpx->command.z * userScale; if(gpx->command.flag & A_IS_SET) gpx->current.position.a = gpx->command.a; if(gpx->command.flag & B_IS_SET) gpx->current.position.b = gpx->command.b; CALL( set_position(gpx) ); command_emitted++; - // check if we know where we are - int mask = gpx->machine.extruder_count == 1 ? (XYZ_BIT_MASK | A_IS_SET) : AXES_BIT_MASK; - if((gpx->command.flag & mask) == mask) gpx->current.positionKnown = 1; + // flag axes that are known + gpx->axis.positionKnown |= (gpx->command.flag & gpx->axis.mask); break; } @@ -3278,46 +3879,52 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) // G161 - Home given axes to minimum case 161: if(gpx->command.flag & F_IS_SET) gpx->current.feedrate = gpx->command.f; - CALL( home_axes(gpx, ENDSTOP_IS_MIN) ); + CALL( home_axes(gpx, gpx->command.flag & XYZ_BIT_MASK, ENDSTOP_IS_MIN) ); command_emitted++; - gpx->current.positionKnown = 0; + // clear homed axes + gpx->axis.positionKnown &= ~(gpx->command.flag & gpx->axis.mask); gpx->excess.a = 0; gpx->excess.b = 0; break; - // G28 - Home given axes to maximum // G162 - Home given axes to maximum - case 28: case 162: if(gpx->command.flag & F_IS_SET) gpx->current.feedrate = gpx->command.f; - CALL( home_axes(gpx, ENDSTOP_IS_MAX) ); + CALL( home_axes(gpx, gpx->command.flag & XYZ_BIT_MASK, ENDSTOP_IS_MAX) ); command_emitted++; - gpx->current.positionKnown = 0; + // clear homed axes + gpx->axis.positionKnown &= ~(gpx->command.flag & gpx->axis.mask); gpx->excess.a = 0; gpx->excess.b = 0; break; default: - SHOW( fprintf(stderr, "(line %u) Syntax warning: unsupported gcode command 'G%u'" EOL, gpx->lineNumber, gpx->command.g) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax warning: unsupported gcode command 'G%u'" EOL, gpx->lineNumber, gpx->command.g) ); } } else if(gpx->command.flag & M_IS_SET) { switch(gpx->command.m) { - // M2 - End program + // M0 - Program stop + case 0: + break; + // M1 - Program pause + case 1: + break; + // M2 - Program end case 2: if(program_is_running()) { end_program(); CALL( set_build_progress(gpx, 100) ); CALL( end_build(gpx) ); } - return 1; + return END_OF_FILE; - // M6 - Tool change (AND) + // M6 - Automatic tool change (AND) // M116 - Wait for extruder AND build platfrom to reach (or exceed) temperature case 6: case 116: { - int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : 0xFFFF; + int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : MAX_TIMEOUT; if(!gpx->flag.dittoPrinting && #if !ENABLE_TOOL_CHANGE_ON_WAIT - gpx->command.m != 116 && + gpx->command.m == 6 && #endif gpx->target.extruder != gpx->current.extruder) { CALL( do_tool_change(gpx, timeout) ); @@ -3339,6 +3946,9 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } if(gpx->tool[A].nozzle_temperature > 0) { CALL( wait_for_extruder(gpx, A, timeout) ); + if(gpx->flag.verboseMode) { + CALL( display_tag(gpx) ); + } } command_emitted++; } @@ -3346,12 +3956,11 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) if(gpx->tool[gpx->target.extruder].nozzle_temperature > 0) { CALL( wait_for_extruder(gpx, gpx->target.extruder, timeout) ); command_emitted++; + if(gpx->flag.verboseMode) { + CALL( display_tag(gpx) ); + } } } - if(gpx->flag.verboseMode) { - CALL( display_message(gpx, "GPX " GPX_VERSION, 0, 0, 0, 0) ); - CALL( display_message(gpx, "by Dr Henry Thomas", 1, 0, 1, 0) ); - } break; } @@ -3387,6 +3996,54 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } break; + // M20 - List SD card + case 20: + break; + + // M21 - Init SD card + case 21: + break; + + // M22 - Release SD card + case 22: + break; + + // M23 - Select SD file + case 23: + break; + + // M24 - Start/resume SD print + case 24: + break; + + // M25 - Pause SD print + case 25: + break; + + // M26 - Set SD position + case 26: + break; + + // M27 - Report SD print status + case 27: + break; + + // M28 - Begin write to SD card + case 28: + break; + + // M29 - Stop writing to SD card + case 29: + break; + + // M30 - Delete file from SD card + case 30: + break; + + // M31 - Output time since last M109 or SD card start to serial + case 31: + break; + // M70 - Display message on LCD case 70: if(gpx->command.flag & COMMENT_IS_SET) { @@ -3399,7 +4056,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) command_emitted++; } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: M70 is missing message text, use (text) where text is message" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: M70 is missing message text, use (text) where text is message" EOL, gpx->lineNumber) ); } break; @@ -3425,7 +4082,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) command_emitted++; } else { - SHOW( fprintf(stderr, "(line %u) Syntax warning: M72 is missing song number, use Pn where n is 0-2" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax warning: M72 is missing song number, use Pn where n is 0-2" EOL, gpx->lineNumber) ); } break; @@ -3459,7 +4116,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } gpx->flag.macrosEnabled = 1; } - if(percent && (gpx->total.time == 0.0 || gpx->flag.buildProgress == 0)) { + if(gpx->current.percent < percent && (percent == 1 || gpx->total.time == 0.0 || gpx->flag.buildProgress == 0)) { CALL( set_build_progress(gpx, percent) ); gpx->current.percent = percent; } @@ -3467,7 +4124,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } } else { - SHOW( fprintf(stderr, "(line %u) Syntax warning: M73 is missing build percentage, use Pn where n is 0-100" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax warning: M73 is missing build percentage, use Pn where n is 0-100" EOL, gpx->lineNumber) ); } break; @@ -3481,7 +4138,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) gpx->flag.extruderIsRelative = 1; break; - // M84 - Stop idle hold + // M84 - Disable steppers until next move case 84: CALL( set_steppers(gpx, gpx->machine.extruder_count == 1 ? (XYZ_BIT_MASK | A_IS_SET) : AXES_BIT_MASK, 0) ); command_emitted++; @@ -3523,7 +4180,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) case 104: if(gpx->command.flag & S_IS_SET) { unsigned temperature = (unsigned)gpx->command.s; - if(temperature > TEMPERATURE_MAX) temperature = TEMPERATURE_MAX; + if(temperature > NOZZLE_MAX) temperature = NOZZLE_MAX; if(gpx->flag.dittoPrinting) { if(temperature && gpx->override[gpx->current.extruder].active_temperature) { temperature = gpx->override[gpx->current.extruder].active_temperature; @@ -3543,10 +4200,14 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: M104 is missing temperature, use Sn where n is 0-280" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: M104 is missing temperature, use Sn where n is 0-280" EOL, gpx->lineNumber) ); } break; + // M105 - Get extruder temperature + case 105: + break; + // M106 - Turn cooling fan on case 106: { int state = (gpx->command.flag & S_IS_SET) ? ((unsigned)gpx->command.s ? 1 : 0) : 1; @@ -3601,7 +4262,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } break; - // M108 - set extruder motor 5D 'simulated' RPM + // M108 - Set extruder motor 5D 'simulated' RPM case 108: #if ENABLE_SIMULATED_RPM if(gpx->command.flag & R_IS_SET) { @@ -3613,19 +4274,19 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: M108 is missing motor RPM, use Rn where n is 0-5" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: M108 is missing motor RPM, use Rn where n is 0-5" EOL, gpx->lineNumber) ); } #endif break; - // M109 - Set Extruder Temperature and Wait + // M109 - Set extruder temperature and wait case 109: if(gpx->flag.reprapFlavor) { if(gpx->command.flag & S_IS_SET) { - int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : 0xFFFF; + int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : MAX_TIMEOUT; unsigned temperature = (unsigned)gpx->command.s; - if(temperature > TEMPERATURE_MAX) temperature = TEMPERATURE_MAX; + if(temperature > NOZZLE_MAX) temperature = NOZZLE_MAX; if(gpx->flag.dittoPrinting) { unsigned tempB = temperature; // set extruder temperatures @@ -3647,6 +4308,9 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } if(gpx->tool[A].nozzle_temperature > 0) { CALL( wait_for_extruder(gpx, A, timeout) ); + if(gpx->flag.verboseMode) { + CALL( display_tag(gpx) ); + } } command_emitted++; } @@ -3666,18 +4330,21 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) // wait for extruder to reach (or exceed) temperature if(gpx->tool[gpx->target.extruder].nozzle_temperature > 0) { CALL( wait_for_extruder(gpx, gpx->target.extruder, timeout) ); + if(gpx->flag.verboseMode) { + CALL( display_tag(gpx) ); + } } command_emitted++; } } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: M109 is missing temperature, use Sn where n is 0-280" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: M109 is missing temperature, use Sn where n is 0-280" EOL, gpx->lineNumber) ); } break; } // fall through to M140 for Makerbot/ReplicatorG flavor - // M140 - Set Build Platform Temperature + // M140 - Set build platform temperature case 140: if(gpx->machine.a.has_heated_build_platform || gpx->machine.b.has_heated_build_platform) { if(gpx->command.flag & S_IS_SET) { @@ -3696,18 +4363,25 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) gpx->tool[tool_id].build_platform_temperature = temperature; } else { - SHOW( fprintf(stderr, "(line %u) Semantic warning: M%u cannot select non-existant heated build platform T%u" EOL, gpx->lineNumber, gpx->command.m, tool_id) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: M%u cannot select non-existant heated build platform T%u" EOL, gpx->lineNumber, gpx->command.m, tool_id) ); } } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: M%u is missing temperature, use Sn where n is 0-120" EOL, gpx->lineNumber, gpx->command.m) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: M%u is missing temperature, use Sn where n is 0-120" EOL, gpx->lineNumber, gpx->command.m) ); } } else { - SHOW( fprintf(stderr, "(line %u) Semantic warning: M%u cannot select non-existant heated build platform" EOL, gpx->lineNumber, gpx->command.m) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: M%u cannot select non-existant heated build platform" EOL, gpx->lineNumber, gpx->command.m) ); } break; + // M110 - Set current line number + case 110: + break; + + // M111 - Set debug level + case 111: + // M126 - Turn blower fan on (valve open) case 126: { int state = (gpx->command.flag & S_IS_SET) ? ((unsigned)gpx->command.s ? 1 : 0) : 1; @@ -3743,7 +4417,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) command_emitted++; } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: M131 is missing axes, use X Y Z A B" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: M131 is missing axes, use X Y Z A B" EOL, gpx->lineNumber) ); } break; @@ -3752,18 +4426,19 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) if(gpx->command.flag & AXES_BIT_MASK) { CALL( recall_home_positions(gpx) ); command_emitted++; - gpx->current.positionKnown = 0; + // clear loaded axes + gpx->axis.positionKnown &= ~(gpx->command.flag & gpx->axis.mask);; gpx->excess.a = 0; gpx->excess.b = 0; } else { - SHOW( fprintf(stderr, "(line %u) Syntax error: M132 is missing axes, use X Y Z A B" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax error: M132 is missing axes, use X Y Z A B" EOL, gpx->lineNumber) ); } break; // M133 - Wait for extruder case 133: { - int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : 0xFFFF; + int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : MAX_TIMEOUT; // changing the if(gpx->flag.dittoPrinting) { if(gpx->tool[B].nozzle_temperature > 0) { @@ -3771,6 +4446,9 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } if(gpx->tool[A].nozzle_temperature > 0) { CALL( wait_for_extruder(gpx, A, timeout) ); + if(gpx->flag.verboseMode) { + CALL( display_tag(gpx) ); + } } command_emitted++; } @@ -3784,6 +4462,9 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) // any tool changes have already occured if(gpx->tool[gpx->target.extruder].nozzle_temperature > 0) { CALL( wait_for_extruder(gpx, gpx->target.extruder, timeout) ); + if(gpx->flag.verboseMode) { + CALL( display_tag(gpx) ); + } } command_emitted++; } @@ -3795,7 +4476,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) case 134: case 190: { if(gpx->machine.a.has_heated_build_platform || gpx->machine.b.has_heated_build_platform) { - int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : 0xFFFF; + int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : MAX_TIMEOUT; unsigned tool_id = gpx->machine.a.has_heated_build_platform ? A : B; if(gpx->command.flag & T_IS_SET) { tool_id = gpx->target.extruder; @@ -3806,11 +4487,11 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) command_emitted++; } else { - SHOW( fprintf(stderr, "(line %u) Semantic warning: M%u cannot select non-existant heated build platform T%u" EOL, gpx->lineNumber, gpx->command.m, tool_id) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: M%u cannot select non-existant heated build platform T%u" EOL, gpx->lineNumber, gpx->command.m, tool_id) ); } } else { - SHOW( fprintf(stderr, "(line %u) Semantic warning: M%u cannot select non-existant heated build platform" EOL, gpx->lineNumber, gpx->command.m) ); + SHOW( fprintf(gpx->log, "(line %u) Semantic warning: M%u cannot select non-existant heated build platform" EOL, gpx->lineNumber, gpx->command.m) ); } break; } @@ -3818,7 +4499,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) // M135 - Change tool case 135: if(!gpx->flag.dittoPrinting && gpx->target.extruder != gpx->current.extruder) { - int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : 0xFFFF; + int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : MAX_TIMEOUT; CALL( do_tool_change(gpx, timeout) ); command_emitted++; } @@ -3838,6 +4519,8 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) // M137 - Build end notification case 137: if(program_is_running()) { + // disable macros in footer + gpx->flag.macrosEnabled = 0; end_program(); CALL( set_build_progress(gpx, 100) ); CALL( end_build(gpx) ); @@ -3874,14 +4557,14 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) float conditional_z = gpx->offset[gpx->current.offset].z; if(gpx->flag.macrosEnabled) { - conditional_z += gpx->userOffset.z; + conditional_z += gpx->user.offset.z; } double z = gpx->flag.relativeCoordinates ? (gpx->current.position.z + gpx->command.z) : (gpx->command.z + conditional_z); CALL( pause_at_zpos(gpx, z) ); } else { - SHOW( fprintf(stderr, "(line %u) Syntax warning: M322 is missing Z axis" EOL, gpx->lineNumber) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax warning: M322 is missing Z axis" EOL, gpx->lineNumber) ); } command_emitted++; break; @@ -3901,8 +4584,12 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) break; } + // M500 - Write paramters to EEPROM + // M501 - Read parameters from EEPROM + // M502 - Revert to default "factory settings" + // M503 - Print/log current settings default: - SHOW( fprintf(stderr, "(line %u) Syntax warning: unsupported mcode command 'M%u'" EOL, gpx->lineNumber, gpx->command.m) ); + SHOW( fprintf(gpx->log, "(line %u) Syntax warning: unsupported mcode command 'M%u'" EOL, gpx->lineNumber, gpx->command.m) ); } } else { @@ -3915,7 +4602,7 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) } // Tn else if(!gpx->flag.dittoPrinting && gpx->target.extruder != gpx->current.extruder) { - int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : 0xFFFF; + int timeout = gpx->command.flag & P_IS_SET ? (int)gpx->command.p : MAX_TIMEOUT; CALL( do_tool_change(gpx, timeout) ); command_emitted++; } @@ -3940,14 +4627,21 @@ int gpx_convert_line(Gpx *gpx, char *gcode_line) CALL( change_extruder_offset(gpx, gpx->current.extruder) ); } else if(percent < 100 && program_is_running()) { - CALL( set_build_progress(gpx, percent) ); - gpx->current.percent = percent; + if(gpx->current.percent) { + CALL( set_build_progress(gpx, percent) ); + gpx->current.percent = percent; + } + // force 1% + else { + CALL( set_build_progress(gpx, 1) ); + gpx->current.percent = 1; + } } command_emitted = 0; } } gpx->lineNumber = next_line; - return 0; + return SUCCESS; } typedef struct tFile { @@ -3956,41 +4650,40 @@ typedef struct tFile { FILE *out2; } File; -static int file_handler(Gpx *gpx, File *file) +static int file_handler(Gpx *gpx, File *file, char *buffer, size_t length) { - size_t bytes, length = gpx->buffer.ptr - gpx->buffer.out; if(length) { - bytes = fwrite(gpx->buffer.out, 1, length, file->out); - if(bytes != length) return -1; + ssize_t bytes = fwrite(buffer, 1, length, file->out); + if(bytes != length) return ERROR; if(file->out2) { - bytes = fwrite(gpx->buffer.out, 1, length, file->out2); - if(bytes != length) return -1; + bytes = fwrite(buffer, 1, length, file->out2); + if(bytes != length) return ERROR; } } - return 0; + return SUCCESS; } -int gpx_convert_file(Gpx *gpx, FILE *file_in, FILE *file_out, FILE *file_out2) +int gpx_convert(Gpx *gpx, FILE *file_in, FILE *file_out, FILE *file_out2) { int i, rval; File file; file.in = stdin; file.out = stdout; file.out2 = NULL; - int verboseMode = gpx->flag.verboseMode; - int showErrorMessages = gpx->flag.showErrorMessages; + int logMessages = gpx->flag.logMessages; if(file_in && file_in != stdin) { // Multi-pass file.in = file_in; i = 0; + gpx->flag.runMacros = 0; gpx->callbackHandler = NULL; gpx->callbackData = NULL; } else { // Single-pass i = 1; - gpx->callbackHandler = (int (*)(Gpx*, void*))file_handler;; + gpx->callbackHandler = (int (*)(Gpx*, void*, char*, size_t))file_handler;; gpx->callbackData = &file; } @@ -4013,12 +4706,12 @@ int gpx_convert_file(Gpx *gpx, FILE *file_in, FILE *file_out, FILE *file_out2) } if(strlen(gpx->buffer.in) == BUFFER_MAX - 1) { overflow = 1; - SHOW( fprintf(stderr, "(line %u) Buffer overflow: input exceeds %u character limit, remaining characters in line will be ignored" EOL, gpx->lineNumber, BUFFER_MAX) ); + SHOW( fprintf(gpx->log, "(line %u) Buffer overflow: input exceeds %u character limit, remaining characters in line will be ignored" EOL, gpx->lineNumber, BUFFER_MAX) ); } rval = gpx_convert_line(gpx, gpx->buffer.in); // normal exit - if(rval > 0) break; + if(rval == END_OF_FILE) break; // error if(rval < 0) return rval; } @@ -4040,44 +4733,651 @@ int gpx_convert_file(Gpx *gpx, FILE *file_in, FILE *file_out, FILE *file_out2) // rewind for second pass fseek(file.in, 0L, SEEK_SET); gpx_initialize(gpx, 0); - - gpx->flag.verboseMode = 0; - gpx->flag.showErrorMessages = 0; - gpx->callbackHandler = (int (*)(Gpx*, void*))file_handler; + gpx->flag.loadMacros = 0; + gpx->flag.runMacros = 1; + //gpx->flag.logMessages = 0; + gpx->callbackHandler = (int (*)(Gpx*, void*, char*, size_t))file_handler; gpx->callbackData = &file; } - gpx->flag.verboseMode = verboseMode;; - gpx->flag.showErrorMessages = showErrorMessages;; - return 0; + gpx->flag.logMessages = logMessages;; + return SUCCESS; } typedef struct tSio { FILE *in; int port; + unsigned bytes_out; + unsigned bytes_in; + + union { + struct { + unsigned short version; + unsigned char variant; + } firmware; + + unsigned int bufferSize; + unsigned short temperature; + unsigned int isReady; + + union { + unsigned char bitfield; + struct { + unsigned char ready: 1; // The extruder has reached target temperature + unsigned char notPluggedIn: 1; // The tool or platform is not plugged in. + unsigned char softwareCutoff: 1; // Temperature was recorded above maximum allowable. + unsigned char notHeating: 1; // Heater is not heating up as expected. + unsigned char temperatureDropping: 1; // Heater temperature dropped below target temp. + unsigned char reserved: 1; + unsigned char buildPlateError: 1; // An error was detected with the platform heater. + unsigned char extruderError: 1; // An error was detected with the extruder heater. + } flag; + } extruder; + + struct { + char buffer[31]; + unsigned char length; + } eeprom; + + struct { + short extruderError; + short extruderDelta; + short extruderOutput; + + short buildPlateError; + short buildPlateDelta; + short buildPlateOutput; + } pid; + + struct { + unsigned int length; + char filename[65]; + unsigned char status; + } sd; + + struct { + int x; + int y; + int z; + int a; + int b; + + union { + unsigned short bitfield; + struct { + unsigned short xMin: 1; // X min switch pressed + unsigned short xMax: 1; // X max switch pressed + + unsigned short yMin: 1; // Y min switch pressed + unsigned short yMax: 1; // Y max switch pressed + + unsigned short zMin: 1; // Z min switch pressed + unsigned short zMax: 1; // Z max switch pressed + + unsigned short aMin: 1; // A min switch pressed + unsigned short aMax: 1; // A max switch pressed + + unsigned short bMin: 1; // B min switch pressed + unsigned short bMax: 1; // B max switch pressed + } flag; + } endstop; + } position; + + union { + unsigned char bitfield; + struct { + unsigned char preheat: 1; // Onboard preheat active + unsigned char manualMode: 1; // Manual move mode active + unsigned char onboardScript: 1; // Bot is running an onboard script + unsigned char onboardProcess: 1; // Bot is running an onboard process + unsigned char waitForButton: 1; // Bot is waiting for button press + unsigned char buildCancelling: 1; // Watchdog reset flag was set at restart + unsigned char heatShutdown: 1; // Heaters were shutdown after 30 minutes of inactivity + unsigned char powerError: 1; // An error was detected with the system power. + } flag; + } motherboard; + + struct { + unsigned lineNumber; + unsigned char status; + unsigned char hours; + unsigned char minutes; + } build; + + } response; + } Sio; -static int port_handler(Gpx *gpx, Sio *sio) +char *sd_status[] = { + "operation successful", + "SD Card not present", + "SD Card initialization failed", + "partition table could not be read", + "filesystem could not be opened", + "root directory could not be opened", + "SD Card is locked", + "unknown status" +}; + +static char *get_sd_status(unsigned int status) { - size_t bytes, length = gpx->buffer.ptr - gpx->buffer.out; - if(length) { - // #TODO write serial I/O - } - return 0; + return sd_status[status < 7 ? status : 7]; } -int gpx_send_file(Gpx *gpx, FILE *file_in, int sio_port) +char *build_status[] = { + "no build initialized (boot state)", + "build running", + "build finished normally", + "build paused", + "build cancelled", + "build sleeping", + "unknown status" +}; + +static char *get_build_status(unsigned int status) +{ + return sd_status[status < 6 ? status : 6]; +} + +static void read_extruder_query_response(Gpx *gpx, Sio *sio, unsigned command, char *buffer) +{ + unsigned extruder_id = buffer[EXTRUDER_ID_OFFSET]; + + switch(command) { + // Query 00 - Query firmware version information + case 0: + // uint16: Firmware Version + sio->response.firmware.version = read_16(gpx); + VERBOSE( fprintf(gpx->log, "Extruder T%u firmware v%u.%u" EOL, + extruder_id, + sio->response.firmware.version / 100, + sio->response.firmware.version % 100) ); + break; + + // Query 02 - Get extruder temperature + case 2: + // int16: Current temperature, in Celsius + sio->response.temperature = read_16(gpx); + VERBOSE( fprintf(gpx->log, "Extruder T%u temperature: %uc" EOL, + extruder_id, + sio->response.temperature) ); + break; + + // Query 22 - Is extruder ready + case 22: + // uint8: 1 if ready, 0 otherwise. + sio->response.isReady = read_8(gpx); + VERBOSE( fprintf(gpx->log, "Extruder T%u is%sready" EOL, + extruder_id, + sio->response.isReady ? " " : " not ") ); + break; + + // Query 30 - Get build platform temperature + case 30: + // int16: Current temperature, in Celsius + sio->response.temperature = read_16(gpx); + VERBOSE( fprintf(gpx->log, "Build platform T%u temperature: %uc" EOL, + extruder_id, + sio->response.temperature) ); + break; + + // Query 32 - Get extruder target temperature + case 32: + // int16: Current temperature, in Celsius + sio->response.temperature = read_16(gpx); + VERBOSE( fprintf(gpx->log, "Extruder T%u target temperature: %uc" EOL, + extruder_id, + sio->response.temperature) ); + break; + + // Query 33 - Get build platform target temperature + case 33: + // int16: Current temperature, in Celsius + sio->response.temperature = read_16(gpx); + VERBOSE( fprintf(gpx->log, "Build platform T%u target temperature: %uc" EOL, + extruder_id, + sio->response.temperature) ); + break; + + // Query 35 - Is build platform ready? + case 35: + // uint8: 1 if ready, 0 otherwise. + sio->response.isReady = read_8(gpx); + VERBOSE( fprintf(gpx->log, "Build platform T%u is%sready" EOL, + extruder_id, + sio->response.isReady ? " " : " not ") ); + break; + + // Query 36 - Get extruder status + case 36: + // uint8: Bitfield containing status information + sio->response.extruder.bitfield = read_8(gpx); + + if(gpx->flag.verboseMode && gpx->flag.logMessages) { + fprintf(gpx->log, "Extruder T%u status" EOL, extruder_id); + if(sio->response.extruder.flag.ready) fputs("Target temperature reached" EOL, gpx->log); + if(sio->response.extruder.flag.notPluggedIn) fputs("The extruder or build plate is not plugged in" EOL, gpx->log); + if(sio->response.extruder.flag.softwareCutoff) fputs("Above maximum allowable temperature recorded: heater shutdown for safety" EOL, gpx->log); + if(sio->response.extruder.flag.temperatureDropping) fputs("Heater temperature dropped below target temperature" EOL, gpx->log); + if(sio->response.extruder.flag.buildPlateError) fputs("An error was detected with the build plate heater or sensor" EOL, gpx->log); + if(sio->response.extruder.flag.extruderError) fputs("An error was detected with the extruder heater or sensor" EOL, gpx->log); + } + break; + + // Query 37 - Get PID state + case 37: + // int16: Extruder heater error term + sio->response.pid.extruderError = read_16(gpx); + + // int16: Extruder heater delta term + sio->response.pid.extruderDelta = read_16(gpx); + + // int16: Extruder heater last output + sio->response.pid.extruderOutput = read_16(gpx); + + // int16: Platform heater error term + sio->response.pid.buildPlateError = read_16(gpx); + + // int16: Platform heater delta term + sio->response.pid.buildPlateDelta = read_16(gpx); + + // int16: Platform heater last output + sio->response.pid.buildPlateOutput = read_16(gpx); + + break; + + default: + abort(); + } +} + +static void read_query_response(Gpx *gpx, Sio *sio, unsigned command, char *buffer) +{ + gpx->buffer.ptr = gpx->buffer.in + 2; + switch(command) { + // 00 - Query firmware version information + case 0: + // uint16: Firmware Version + sio->response.firmware.version = read_16(gpx); + VERBOSE( fprintf(gpx->log, "Motherboard firmware v%u.%u" EOL, + sio->response.firmware.version / 100, sio->response.firmware.version % 100) ); + break; + + // 02 - Get available buffer size + case 2: + // uint32: Number of bytes availabe in the command buffer + sio->response.bufferSize = read_32(gpx); + break; + + // 10 - Extruder query command + case 10: { + unsigned query_command = buffer[QUERY_COMMAND_OFFSET]; + read_extruder_query_response(gpx, sio, query_command, buffer); + break; + } + + // 11 - Is ready + case 11: + // uint8: 1 if ready, 0 otherwise. + sio->response.isReady = read_8(gpx); + VERBOSE( fprintf(gpx->log, "Printer is%sready" EOL, + sio->response.isReady ? " " : " not ") ); + break; + + // 12 - Read from EEPROM + case 12: + // N bytes: Data read from the EEPROM + sio->response.eeprom.length = buffer[EEPROM_LENGTH_OFFSET]; + read_bytes(gpx, sio->response.eeprom.buffer, sio->response.eeprom.length); + break; + + // 13 - Write to EEPROM + case 13: + // uint8: Number of bytes successfully written to the EEPROM + sio->response.eeprom.length = read_8(gpx); + break; + + // 14 - Capture to file + case 14: + // uint8: SD response code + sio->response.sd.status = read_8(gpx); + VERBOSE( fprintf(gpx->log, "Capture to file: %s" EOL, + get_sd_status(sio->response.sd.status)) ); + break; + + // 15 - End capture to file + case 15: + // uint32: Number of bytes captured to file. + sio->response.sd.length = read_32(gpx); + VERBOSE( fprintf(gpx->log, "Capture to file ended: %u bytes written" EOL, + sio->response.sd.length) ); + break; + + // 16 - Play back capture + case 16: + // uint8: SD response code + sio->response.sd.status = read_8(gpx); + VERBOSE( fprintf(gpx->log, "Play back captured file: %s" EOL, + get_sd_status(sio->response.sd.status)) ); + break; + + // 18 - Get next filename + case 18: + // uint8: SD Response code. + sio->response.sd.status = read_8(gpx); + /* 1+N bytes: Name of the next file, in ASCII, terminated with a null character. + If the operation was unsuccessful, this will be a null character */ + strncpy0(sio->response.sd.filename, gpx->buffer.ptr, 65); + VERBOSE( fprintf(gpx->log, "Get next filename: '%s' %s" EOL, + sio->response.sd.filename, + get_sd_status(sio->response.sd.status)) ); + break; + + // 20 - Get build name + case 20: + // 1+N bytes: A null terminated string representing the filename of the current build. + strncpy0(sio->response.sd.filename, gpx->buffer.ptr, 65); + VERBOSE( fprintf(gpx->log, "Get build name: '%s'" EOL, sio->response.sd.filename) ); + break; + + // 21 - Get extended position + case 21: + // int32: X position, in steps + sio->response.position.x = read_32(gpx); + + // int32: Y position, in steps + sio->response.position.y = read_32(gpx); + + // int32: Z position, in steps + sio->response.position.z = read_32(gpx); + + // int32: A position, in steps + sio->response.position.a = read_32(gpx); + + // int32: B position, in steps + sio->response.position.b = read_32(gpx); + + // uint16: bitfield corresponding to the endstop status: + sio->response.position.endstop.bitfield = read_16(gpx); + + if(gpx->flag.verboseMode && gpx->flag.logMessages) { + fputs("Current position" EOL, gpx->log); + fprintf(gpx->log, "X = %0.2fmm%s%s" EOL, + (double)sio->response.position.x / gpx->machine.x.steps_per_mm, + sio->response.position.endstop.flag.xMax ? ", at max endstop" : "", + sio->response.position.endstop.flag.xMin ? ", at min endstop" : ""); + fprintf(gpx->log, "Y = %0.2fmm%s%s" EOL, + (double)sio->response.position.y / gpx->machine.y.steps_per_mm, + sio->response.position.endstop.flag.yMax ? ", at max endstop" : "", + sio->response.position.endstop.flag.yMin ? ", at min endstop" : ""); + fprintf(gpx->log, "Z = %0.2fmm%s%s" EOL, + (double)sio->response.position.z / gpx->machine.z.steps_per_mm, + sio->response.position.endstop.flag.zMax ? ", at max endstop" : "", + sio->response.position.endstop.flag.zMin ? ", at min endstop" : ""); + fprintf(gpx->log, "A = %0.2fmm%s%s" EOL, + (double)sio->response.position.a / gpx->machine.a.steps_per_mm, + sio->response.position.endstop.flag.aMax ? ", at max endstop" : "", + sio->response.position.endstop.flag.aMin ? ", at min endstop" : ""); + fprintf(gpx->log, "B = %0.2fmm%s%s" EOL, + (double)sio->response.position.b / gpx->machine.b.steps_per_mm, + sio->response.position.endstop.flag.bMax ? ", at max endstop" : "", + sio->response.position.endstop.flag.bMin ? ", at min endstop" : ""); + } + break; + + // 22 - Extended stop + case 22: + // int8: 0 (reserved for future use) + read_8(gpx); + VERBOSE( fputs("Build stopped" EOL, gpx->log) ); + break; + + // 23 - Get motherboard status + case 23: + // uint8: bitfield containing status information + sio->response.motherboard.bitfield = read_8(gpx); + if(gpx->flag.verboseMode && gpx->flag.logMessages) { + fputs("Motherboard status" EOL, gpx->log); + if(sio->response.motherboard.flag.preheat) fputs("Onboard preheat active" EOL, gpx->log); + if(sio->response.motherboard.flag.manualMode) fputs("Manual move active" EOL, gpx->log); + if(sio->response.motherboard.flag.onboardScript) fputs("Running onboard script" EOL, gpx->log); + if(sio->response.motherboard.flag.onboardProcess) fputs("Running onboard process" EOL, gpx->log); + if(sio->response.motherboard.flag.waitForButton) fputs("Waiting for buttons press" EOL, gpx->log); + if(sio->response.motherboard.flag.buildCancelling) fputs("Build cancelling" EOL, gpx->log); + if(sio->response.motherboard.flag.heatShutdown) fputs("Heaters were shutdown after 30 minutes of inactivity" EOL, gpx->log); + if(sio->response.motherboard.flag.powerError) fputs("Error detected in system power" EOL, gpx->log); + } + break; + + // 24 - Get build statistics + case 24: + // uint8 : Build status + sio->response.build.status = read_8(gpx); + + // uint8 : Hours elapsed on print + sio->response.build.hours = read_8(gpx); + + // uint8 : Minutes elapsed on print (add hours for total time) + sio->response.build.minutes = read_8(gpx); + + // uint32: Line number (number of commands processed) + sio->response.build.lineNumber = read_32(gpx); + + // uint32: Reserved for future use + read_32(gpx); + + VERBOSE( fprintf(gpx->log, "(line %u) Build status: %s, %u hours, %u minutes" EOL, + sio->response.build.lineNumber, + get_build_status(sio->response.build.status), + sio->response.build.hours, + sio->response.build.minutes) ); + break; + + // 27 - Get advanced version number + case 27: + // uint16_t Firmware version + sio->response.firmware.version = read_16(gpx); + + // uint16_t Internal version + read_16(gpx); + + // uint8_t Software variant (0x01 MBI Official, 0x80 Sailfish) + sio->response.firmware.variant = read_8(gpx); + + // uint8_t Reserved for future use + read_8(gpx); + + // uint16_t Reserved for future use + read_16(gpx); + + if(gpx->flag.verboseMode && gpx->flag.logMessages) { + char *varient = "Unknown"; + switch(sio->response.firmware.variant) { + case 0x01: + varient = "Makerbot"; + break; + case 0x80: + varient = "Sailfish"; + break; + } + fprintf(gpx->log, "%s firmware v%u.%u" EOL, varient, sio->response.firmware.version / 100, sio->response.firmware.version % 100); + } + break; + } +} + +// 02 - Get available buffer size + +char buffer_size_query[] = { + 0xD5, // start byte + 1, // length + 2, // query command + 0 // crc +}; + +static int port_handler(Gpx *gpx, Sio *sio, char *buffer, size_t length) +{ + int rval = SUCCESS; + if(length) { + ssize_t bytes; + int retry_count = 0; + do { + // send the packet + if((bytes = write(sio->port, buffer, length)) == -1) { + return errno; + } + else if(bytes != length) { + return ESIOWRITE; + } + sio->bytes_out += length; + + if(sio->bytes_in) { + // recieve the response + if((bytes = read(sio->port, gpx->buffer.in, 2)) == -1) { + return errno; + } + else if(bytes != 2) { + return ESIOREAD; + } + // invalid start byte + if(gpx->buffer.in[0] != 0xD5) { + return ESIOFRAME; + } + } + else { + // first read + for(;;) { + // read start byte + if((bytes = read(sio->port, gpx->buffer.in, 1)) == -1) { + return errno; + } + else if(bytes != 1) { + return ESIOREAD; + } + // loop until we get a valid start byte + if(gpx->buffer.in[0] == 0xD5) break; + } + // read length + if((bytes = read(sio->port, gpx->buffer.in + 1, 1)) == -1) { + return errno; + } + else if(bytes != 1) { + return ESIOREAD; + } + } + size_t payload_length = gpx->buffer.in[1]; + // recieve payload + if((bytes = read(sio->port, gpx->buffer.in + 2, payload_length + 1)) == -1) { + return errno; + } + else if(bytes != payload_length + 1) { + return ESIOREAD; + } + // check CRC + unsigned crc = (unsigned char)gpx->buffer.in[2 + payload_length]; + if(crc != calculate_crc((unsigned char*)gpx->buffer.in + 2, payload_length)) { + fprintf(gpx->log, "(retry %u) Input CRC mismatch: packet discarded" EOL, retry_count); + rval = ESIOCRC; + goto L_RETRY; + } + // check response code + rval = gpx->buffer.in[2]; + switch((unsigned)gpx->buffer.in[2]) { + // 0x80 - Generic Packet error, packet discarded (retry) + case 0x80: + VERBOSE( fprintf(gpx->log, "(retry %u) Generic Packet error: packet discarded" EOL, retry_count) ); + break; + + // 0x81 - Success + case 0x81: { + unsigned command = (unsigned)buffer[COMMAND_OFFSET]; + if ((command & 0x80) == 0) { + read_query_response(gpx, sio, command, buffer); + } + return SUCCESS; + } + + // 0x82 - Action buffer overflow, entire packet discarded + case 0x82: + do { + // wait for 1/10 seconds + usleep(100000); + // query buffer size + buffer_size_query[3] = calculate_crc((unsigned char *)buffer_size_query + 2, 1); + CALL( port_handler(gpx, sio, buffer_size_query, 4) ); + // loop until buffer has space for the next command + } while(sio->response.bufferSize < length); + break; + + // 0x83 - CRC mismatch, packet discarded. (retry) + case 0x83: + VERBOSE( fprintf(gpx->log, "(retry %u) Output CRC mismatch: packet discarded" EOL, retry_count) ); + break; + + // 0x84 - Query packet too big, packet discarded + case 0x84: + VERBOSE( fprintf(gpx->log, "(retry %u) Query packet too big: packet discarded" EOL, retry_count) ); + goto L_ABORT; + + // 0x85 - Command not supported/recognized + case 0x85: + VERBOSE( fprintf(gpx->log, "(retry %u) Command not supported or recognized" EOL, retry_count) ); + goto L_ABORT; + + // 0x87 - Downstream timeout + case 0x87: + VERBOSE( fprintf(gpx->log, "(retry %u) Downstream timeout" EOL, retry_count) ); + goto L_ABORT; + + // 0x88 - Tool lock timeout (retry) + case 0x88: + VERBOSE( fprintf(gpx->log, "(retry %u) Tool lock timeout" EOL, retry_count) ); + break; + + // 0x89 - Cancel build (retry) + case 0x89: + VERBOSE( fprintf(gpx->log, "(retry %u) Cancel build" EOL, retry_count) ); + break; + + // 0x8A - Bot is building from SD + case 0x8A: + VERBOSE( fprintf(gpx->log, "(retry %u) Bot is Building from SD card" EOL, retry_count) ); + goto L_ABORT; + + // 0x8B - Bot is shutdown due to overheating + case 0x8B: + VERBOSE( fprintf(gpx->log, "(retry %u) Bot is shutdown due to overheating" EOL, retry_count) ); + goto L_ABORT; + + // 0x8C - Packet timeout error, packet discarded (retry) + case 0x8C: + VERBOSE( fprintf(gpx->log, "(retry %u) Packet timeout error: packet discarded" EOL, retry_count) ); + break; + } +L_RETRY: + // wait for 2 seconds + sleep(2); + } while(++retry_count < 5); + } + +L_ABORT: + return rval; +} + +int gpx_convert_and_send(Gpx *gpx, FILE *file_in, int sio_port) { int i, rval; Sio sio; sio.in = stdin; sio.port = -1; - int verboseMode = gpx->flag.verboseMode; - int showErrorMessages = gpx->flag.showErrorMessages; + sio.bytes_out = 0; + sio.bytes_in = 0; + int logMessages = gpx->flag.logMessages; if(file_in && file_in != stdin) { // Multi-pass sio.in = file_in; i = 0; + gpx->flag.logMessages = 0; gpx->flag.framingEnabled = 0; gpx->callbackHandler = NULL; gpx->callbackData = NULL; @@ -4086,7 +5386,7 @@ int gpx_send_file(Gpx *gpx, FILE *file_in, int sio_port) // Single-pass i = 1; gpx->flag.framingEnabled = 1; - gpx->callbackHandler = (int (*)(Gpx*, void*))port_handler;; + gpx->callbackHandler = (int (*)(Gpx*, void*, char*, size_t))port_handler;; gpx->callbackData = &sio; } @@ -4107,7 +5407,7 @@ int gpx_send_file(Gpx *gpx, FILE *file_in, int sio_port) } if(strlen(gpx->buffer.in) == BUFFER_MAX - 1) { overflow = 1; - SHOW( fprintf(stderr, "(line %u) Buffer overflow: input exceeds %u character limit, remaining characters in line will be ignored" EOL, gpx->lineNumber, BUFFER_MAX) ); + SHOW( fprintf(gpx->log, "(line %u) Buffer overflow: input exceeds %u character limit, remaining characters in line will be ignored" EOL, gpx->lineNumber, BUFFER_MAX) ); } rval = gpx_convert_line(gpx, gpx->buffer.in); @@ -4135,13 +5435,125 @@ int gpx_send_file(Gpx *gpx, FILE *file_in, int sio_port) fseek(sio.in, 0L, SEEK_SET); gpx_initialize(gpx, 0); - gpx->flag.verboseMode = 0; - gpx->flag.showErrorMessages = 0; + gpx->flag.logMessages = 1; gpx->flag.framingEnabled = 1; - gpx->callbackHandler = (int (*)(Gpx*, void*))port_handler;; + gpx->callbackHandler = (int (*)(Gpx*, void*, char*, size_t))port_handler;; gpx->callbackData = &sio; } - gpx->flag.verboseMode = verboseMode;; - gpx->flag.showErrorMessages = showErrorMessages;; - return 0; -} \ No newline at end of file + gpx->flag.logMessages = logMessages;; + return SUCCESS; +} + +void gpx_end_convert(Gpx *gpx) +{ + if(gpx->flag.verboseMode && gpx->flag.logMessages) { + long seconds = round(gpx->accumulated.time); + long minutes = seconds / 60; + long hours = minutes / 60; + minutes %= 60; + seconds %= 60; + fprintf(gpx->log, "Extrusion length: %#0.3f metres" EOL, round(gpx->accumulated.a + gpx->accumulated.b) / 1000); + fputs("Estimated print time: ", gpx->log); + if(hours) fprintf(gpx->log, "%lu hours ", hours); + if(minutes) fprintf(gpx->log, "%lu minutes ", minutes); + fprintf(gpx->log, "%lu seconds" EOL, seconds); + fprintf(gpx->log, "X3G output filesize: %lu bytes" EOL, gpx->accumulated.bytes); + } +} + +// EEPROM + +static int write_eeprom_8(Gpx *gpx, Sio *sio, unsigned address, unsigned char value) +{ + int rval; + gpx->buffer.ptr = sio->response.eeprom.buffer; + write_8(gpx, value); + CALL( write_eeprom(gpx, address, sio->response.eeprom.buffer, 1) ); + return SUCCESS; +} + +static int read_eeprom_8(Gpx *gpx, Sio *sio, unsigned address, unsigned char *value) +{ + int rval; + CALL( read_eeprom(gpx, address, 1) ); + gpx->buffer.ptr = sio->response.eeprom.buffer; + *value = read_8(gpx); + return SUCCESS; +} + +static int write_eeprom_32(Gpx *gpx, Sio *sio, unsigned address, unsigned value) +{ + int rval; + gpx->buffer.ptr = sio->response.eeprom.buffer; + write_32(gpx, value); + CALL( write_eeprom(gpx, address, sio->response.eeprom.buffer, 4) ); + return SUCCESS; +} + +static int read_eeprom_32(Gpx *gpx, Sio *sio, unsigned address, unsigned *value) +{ + int rval; + CALL( read_eeprom(gpx, address, 4) ); + gpx->buffer.ptr = sio->response.eeprom.buffer; + *value = read_32(gpx); + return SUCCESS; +} + +static int write_eeprom_float(Gpx *gpx, Sio *sio, unsigned address, float value) +{ + int rval; + gpx->buffer.ptr = sio->response.eeprom.buffer; + write_float(gpx, value); + CALL( write_eeprom(gpx, address, sio->response.eeprom.buffer, 4) ); + return SUCCESS; +} + +static int read_eeprom_float(Gpx *gpx, Sio *sio, unsigned address, float *value) +{ + int rval; + CALL( read_eeprom(gpx, address, 4) ); + gpx->buffer.ptr = sio->response.eeprom.buffer; + *value = read_float(gpx); + return SUCCESS; +} + +static int eeprom_set_property(Gpx *gpx, const char* section, const char* property, char* value) +{ + int rval; + unsigned int address = (unsigned int)strtol(property, NULL, 0); + if(SECTION_IS("byte")) { + unsigned char b = (unsigned char)strtol(value, NULL, 0); + CALL( write_eeprom_8(gpx, (Sio *)gpx->callbackData, address, b) ); + } + else if(SECTION_IS("integer")) { + unsigned int i = (unsigned int)strtol(value, NULL, 0); + CALL( write_eeprom_32(gpx, (Sio *)gpx->callbackData, address, i) ); + } + else if(SECTION_IS("hex") || SECTION_IS("hexadecimal")) { + unsigned int h = (unsigned int)strtol(value, NULL, 16); + unsigned length = (unsigned)strlen(value) / 2; + if(length > 4) length = 4; + gpx->buffer.ptr = ((Sio *)gpx->callbackData)->response.eeprom.buffer; + write_32(gpx, h); + CALL( write_eeprom(gpx, address, ((Sio *)gpx->callbackData)->response.eeprom.buffer, length) ); + } + else if(SECTION_IS("float")) { + float f = strtof(value, NULL); + CALL( write_eeprom_float(gpx, (Sio *)gpx->callbackData, address, f) ); + } + else if(SECTION_IS("string")) { + unsigned length = (unsigned)strlen(value); + CALL( write_eeprom(gpx, address, value, length) ); + } + else { + SHOW( fprintf(gpx->log, "(line %u) Configuration error: unrecognised section [%s]" EOL, gpx->lineNumber, section) ); + return gpx->lineNumber; + } + return SUCCESS; +} + +int eeprom_load_config(Gpx *gpx, const char *filename) +{ + return ini_parse(gpx, filename, eeprom_set_property); +} + diff --git a/gpx.h b/gpx.h index d8158e8..b63a6af 100644 --- a/gpx.h +++ b/gpx.h @@ -35,7 +35,17 @@ extern "C" { #include #define GPX_VERSION "2.0-alpha" - +#define HOST_VERSION 50 + +#define END_OF_FILE 1 +#define SUCCESS 0 +#define ERROR -1 + +#define ESIOWRITE -2 +#define ESIOREAD -3 +#define ESIOFRAME -4 +#define ESIOCRC -5 + #define STREAM_VERSION_HIGH 0 #define STREAM_VERSION_LOW 0 @@ -51,9 +61,14 @@ extern "C" { // BOUNDS CHECKING VARIABLES -#define TEMPERATURE_MAX 280 +#define NOZZLE_MAX 280 +#define NOZZLE_TIME 0.6 #define HBP_MAX 120 - +#define HBP_TIME 6 +#define AMBIENT_TEMP 24 +#define ACCELERATION_TIME 1.15 + +#define MAX_TIMEOUT 0xFFFF #ifdef _WIN32 # define PATH_DELIM '\\' @@ -247,16 +262,23 @@ extern "C" { struct { Point5d position; // the current position of the extruder in 5D space - int positionKnown; // is the current extruder position known double feedrate; // the current feed rate int extruder; // the currently selected extruder being used by the bot int offset; // current G10 offset unsigned percent; // current percent progress } current; + struct { + unsigned int positionKnown; // axis bitfields for known positions of the extruder + unsigned int mask; + } axis; + Point2d excess; // the accumulated rounding error in mm to step conversion Point3d offset[7]; // G10 offsets - Point3d userOffset; // command line offset + struct { + Point3d offset; // command line offset + double scale; // command line scale + } user; Tool tool[2]; // tool state Override override[2]; // gcode override @@ -280,16 +302,17 @@ extern "C" { unsigned dittoPrinting:1; // enable ditto printing unsigned buildProgress:1; // override build percent unsigned verboseMode:1; // verbose output + unsigned logMessages:1; // enable stderr message logging unsigned rewrite5D:1; // calculate 5D E values rather than scaling them - unsigned serialIO:1; // output to serial io port // STATE unsigned programState:8; // gcode program state used to trigger start and end code sequences unsigned doPauseAtZPos:8; // signals that a pause is ready to be unsigned pausePending:1; // signals a pause is pending before the macro script has started - unsigned macrosEnabled:1; // M73 P1 or ;@body encountered signalling body start + unsigned macrosEnabled:1; // M73 P1 or ;@body encountered signalling body start (so we don't pause during homing) + unsigned loadMacros:1; // used by the multi-pass converter to maintain state + unsigned runMacros:1; // used by the multi-pass converter to maintain state unsigned framingEnabled:1; // enable framming of packets with header and crc - unsigned showErrorMessages:1; } flag; @@ -297,7 +320,6 @@ extern "C" { unsigned lineNumber; // the current line number int longestDDA; - // STATISTICS struct { @@ -312,28 +334,34 @@ extern "C" { double time; unsigned long bytes; } total; - + // CALLBACK - int (*callbackHandler)(Gpx *gpx, void *callbackData); + int (*callbackHandler)(Gpx *gpx, void *callbackData, char *buffer, size_t length); void *callbackData; + // LOGGING + + FILE *log; }; void gpx_initialize(Gpx *gpx, int firstTime); int gpx_set_machine(Gpx *gpx, char *machine); int gpx_set_property(Gpx *gpx, const char* section, const char* property, char* value); - int gpx_read_config(Gpx *gpx, const char *filename); + int gpx_load_config(Gpx *gpx, const char *filename); - void gpx_register_callback(Gpx *gpx, int (*callbackHandler)(Gpx *gpx, void *callbackData), void *callbackData); - - void gpx_start_build(Gpx *gpx, char *buildName); - void gpx_end_build(Gpx *gpx); + void gpx_register_callback(Gpx *gpx, int (*callbackHandler)(Gpx *gpx, void *callbackData, char *buffer, size_t length), void *callbackData); + void gpx_start_convert(Gpx *gpx, char *buildName); + int gpx_convert_line(Gpx *gpx, char *gcode_line); - int gpx_convert_file(Gpx *gpx, FILE *file_in, FILE *file_out, FILE *file_out2); - int gpx_send_file(Gpx *gpx, FILE *file_in, int sio_port); + int gpx_convert(Gpx *gpx, FILE *file_in, FILE *file_out, FILE *file_out2); + int gpx_convert_and_send(Gpx *gpx, FILE *file_in, int sio_port); + + void gpx_end_convert(Gpx *gpx); + + int eeprom_load_config(Gpx *gpx, const char *filename); #ifdef __cplusplus } diff --git a/scripts/gpx.py b/scripts/gpx.py index c903a63..b32fd88 100755 --- a/scripts/gpx.py +++ b/scripts/gpx.py @@ -1,10 +1,10 @@ #Name: GPX -#Info: Cura x3g conversion post processor +#Info: GCode to x3g conversion post processor #Help: GPX #Depend: GCode #Type: postprocess #Param: gpxPath(str:/Applications/GPX) GPX path -#Param: machineType(str:r2) Machine type +#Param: flags(str:-m r2) Flags import platform import os @@ -23,9 +23,5 @@ def getGpxAppName(): x3gFile = profile.getPreference('lastFile') x3gFile = x3gFile[0:x3gFile.rfind('.')] + '.x3g' -commandList = [getGpxAppName(), '-p', '-r'] -if machineType is not None and machineType != '': - commandList += ['-m', machineType] -commandList += [filename, x3gFile] +commandList = [getGpxAppName(), '-p', '-r', flags, filename, x3gFile] call(commandList) -