Beta Release

All functionality implemented and unit tests passed.
master
WHPThomas 2013-04-18 02:47:52 +10:00
parent 81701abcc0
commit ad69b21b27
6 changed files with 399 additions and 51 deletions

41
example.ini Normal file
View File

@ -0,0 +1,41 @@
;
; example.ini (custom machine definition)
;
; Replicator 2 machine definition
;
[x]
max_feedrate=18000
home_feedrate=2500
steps_per_mm=88.573186
endstop=1
[y]
max_feedrate=18000
home_feedrate=2500
steps_per_mm=88.573186
endstop=1
[z]
max_feedrate=1170
home_feedrate=1100
steps_per_mm=400
endstop=0
[a]
max_feedrate=1600
steps_per_mm=96.275201870333662468889989185642
motor_steps=3200
has_heated_build_platform=0
[b]
max_feedrate=1600
steps_per_mm=96.275201870333662468889989185642
motor_steps=3200
has_heated_build_platform=0
[machine]
;nominal filament diameter
filament_diameter=1.75
extruder_count=1
timeout=20

155
gpx.c
View File

@ -40,6 +40,7 @@
#endif
#include "gpx.h"
#include "ini.h"
// Machine definitions
@ -95,7 +96,7 @@ static Machine replicator_2X = {
Machine machine = {
{18000, 2500, 88.573186, ENDSTOP_IS_MAX}, // x axis
{18000, 2500, 88.573186, ENDSTOP_IS_MAX}, // y axis
{1170, 400, ENDSTOP_IS_MIN}, // z axis
{1170, 1100, 400, ENDSTOP_IS_MIN}, // z axis
{1600, 96.275201870333662468889989185642, 3200, 0}, // a extruder
{1600, 96.275201870333662468889989185642, 3200, 0}, // b extruder
1.75, // nominal filament diameter
@ -266,6 +267,62 @@ static int write_float(float value) {
return 0;
}
// Custom machine definition ini handler
#define SECTION_IS(s) strcmp(section, s) == 0
#define NAME_IS(n) strcmp(name, n) == 0
static int config_handler(void* user, const char* section, const char* name, const char* value)
{
if(SECTION_IS("x")) {
if(NAME_IS("max_feedrate")) machine.x.max_feedrate = strtod(value, NULL);
else if(NAME_IS("home_feedrate")) machine.x.home_feedrate = strtod(value, NULL);
else if(NAME_IS("steps_per_mm")) machine.x.steps_per_mm = strtod(value, NULL);
else if(NAME_IS("endstop")) machine.x.endstop = atoi(value);
else return 0;
}
else if(SECTION_IS("y")) {
if(NAME_IS("max_feedrate")) machine.y.max_feedrate = strtod(value, NULL);
else if(NAME_IS("home_feedrate")) machine.y.home_feedrate = strtod(value, NULL);
else if(NAME_IS("steps_per_mm")) machine.y.steps_per_mm = strtod(value, NULL);
else if(NAME_IS("endstop")) machine.y.endstop = atoi(value);
else return 0;
}
else if(SECTION_IS("z")) {
if(NAME_IS("max_feedrate")) machine.z.max_feedrate = strtod(value, NULL);
else if(NAME_IS("home_feedrate")) machine.z.home_feedrate = strtod(value, NULL);
else if(NAME_IS("steps_per_mm")) machine.z.steps_per_mm = strtod(value, NULL);
else if(NAME_IS("endstop")) machine.z.endstop = atoi(value);
else return 0;
}
else if(SECTION_IS("a")) {
if(NAME_IS("max_feedrate")) machine.a.max_feedrate = strtod(value, NULL);
else if(NAME_IS("steps_per_mm")) machine.a.steps_per_mm = strtod(value, NULL);
else if(NAME_IS("motor_steps")) machine.a.motor_steps = strtod(value, NULL);
else if(NAME_IS("has_heated_build_platform")) machine.a.has_heated_build_platform = atoi(value);
else return 0;
}
else if(SECTION_IS("b")) {
if(NAME_IS("max_feedrate")) machine.b.max_feedrate = strtod(value, NULL);
else if(NAME_IS("steps_per_mm")) machine.b.steps_per_mm = strtod(value, NULL);
else if(NAME_IS("motor_steps")) machine.b.motor_steps = strtod(value, NULL);
else if(NAME_IS("has_heated_build_platform")) machine.b.has_heated_build_platform = atoi(value);
else return 0;
}
else if(SECTION_IS("machine")) {
if(NAME_IS("filament_diameter")) machine.filament_diameter = strtod(value, NULL);
else if(NAME_IS("extruder_count")) machine.extruder_count = atoi(value);
else if(NAME_IS("timeout")) machine.timeout = atoi(value);
else return 0;
}
else {
return 0; // unknown section/name, error
}
return 1;
}
// 5D VECTOR FUNCTIONS
// return the magnitude (length) of the 5D vector
static double magnitude(int flag, Ptr5d vector)
@ -652,10 +709,10 @@ static void queue_absolute_point()
if(write_32((int)steps.z) == EOF) exit(1);
// int32: A coordinate, in steps
if(write_32((int)steps.a) == EOF) exit(1);
if(write_32(-(int)steps.a) == EOF) exit(1);
// int32: B coordinate, in steps
if(write_32((int)steps.b) == EOF) exit(1);
if(write_32(-(int)steps.b) == EOF) exit(1);
// uint32: Feedrate, in microseconds between steps on the max delta. (DDA)
if(write_32((int)longestDDA) == EOF) exit(1);
@ -828,7 +885,8 @@ void display_message(char *message, unsigned timeout, int wait_for_button)
// uint8: Timeout, in seconds. If 0, this message will left on the screen
if(write_8(seconds) == EOF) exit(1);
// 1+N bytes: Message to write to the screen, in ASCII, terminated with a null character.
bytesSent += fwrite(message + bytesSent, 1, length > maxLength ? maxLength : length, out);
long rowLength = length - bytesSent;
bytesSent += fwrite(message + bytesSent, 1, rowLength < maxLength ? rowLength : maxLength, out);
if(write_8('\0') == EOF) exit(1);
}
}
@ -1127,17 +1185,18 @@ static char *normalize_comment(char *p) {
static void usage()
{
fputs("GPX " GPX_VERSION " Copyright (c) 2013 WHPThomas, All rights reserved.", stderr);
fputs("\nUsage: gpx [-m <MACHINE> | -c <CONFIG>] INPUT [OUTPUT]", stderr);
fputs("\nSwitches:\n\t-p\toverride build percentage", stderr);
fputs("\nMACHINE is the predefined machine type", stderr);
fputs("\n\tr1 = Replicator 1 - single extruder", stderr);
fputs("\tr1d = Replicator 1 - dual extruder", stderr);
fputs("\tr2 = Replicator 2 (default config)", stderr);
fputs("\tr2x = Replicator 2X", stderr);
fputs("\nCONFIG is the filename of a custom machine definition (ini)", stderr);
fputs("\nINPUT is the name of the sliced gcode input filename", stderr);
fputs("\nOUTPUT is the name of the x3g output filename", stderr);
fputs(EOL "GPX " GPX_VERSION " Copyright (c) 2013 WHPThomas, All rights reserved." EOL, stderr);
fputs("Usage: gpx [-p] [-m <MACHINE> | -c <CONFIG>] INPUT [OUTPUT]" EOL, stderr);
fputs(EOL "Switches:" EOL, stderr);
fputs("\t-p\toverride build percentage" EOL, stderr);
fputs(EOL "MACHINE is the predefined machine type" EOL, stderr);
fputs("\tr1 = Replicator 1 - single extruder" EOL, stderr);
fputs("\tr1d = Replicator 1 - dual extruder" EOL, stderr);
fputs("\tr2 = Replicator 2 (default config)" EOL, stderr);
fputs("\tr2x = Replicator 2X" EOL, stderr);
fputs(EOL "CONFIG is the filename of a custom machine definition (ini)" EOL, stderr);
fputs(EOL "INPUT is the name of the sliced gcode input filename" EOL, stderr);
fputs(EOL "OUTPUT is the name of the x3g output filename" EOL, stderr);
exit(1);
}
@ -1159,12 +1218,10 @@ int main(int argc, char * argv[])
while ((c = getopt(argc, argv, "pm:c:")) != -1) {
switch (c) {
case 'c':
/*
TODO
if(!get_custom_definition(&machine, optarg)) {
if (ini_parse(optarg, config_handler, NULL) < 0) {
fprintf(stderr, "Command line error: cannot load custom machine definition '%s'" EOL, optarg);
usage();
};
*/
}
break;
case 'm':
if(strcasecmp(optarg, "r1") == 0) {
@ -1725,6 +1782,34 @@ int main(int argc, char * argv[])
}
break;
}
// M17 - Enable axes steppers
case 17:
if(command.flag & AXES_BIT_MASK) {
set_steppers(command.flag & AXES_BIT_MASK, 1);
if(command.flag & A_IS_SET) tool[0].motor_enabled = 1;
if(command.flag & B_IS_SET) tool[1].motor_enabled = 1;
}
else {
set_steppers(machine.extruder_count == 1 ? (XYZ_BIT_MASK | A_IS_SET) : AXES_BIT_MASK, 1);
tool[0].motor_enabled = 1;
if(machine.extruder_count == 2) tool[1].motor_enabled = 1;
}
break;
// M18 - Disable axes steppers
case 18:
if(command.flag & AXES_BIT_MASK) {
set_steppers(command.flag & AXES_BIT_MASK, 0);
if(command.flag & A_IS_SET) tool[0].motor_enabled = 0;
if(command.flag & B_IS_SET) tool[1].motor_enabled = 0;
}
else {
set_steppers(machine.extruder_count == 1 ? (XYZ_BIT_MASK | A_IS_SET) : AXES_BIT_MASK, 0);
tool[0].motor_enabled = 0;
if(machine.extruder_count == 2) tool[1].motor_enabled = 0;
}
break;
// M70 - Display message on LCD
case 70:
@ -1999,7 +2084,7 @@ int main(int argc, char * argv[])
// M132 - Load Current Position from EEPROM
case 132:
if(command.flag & AXES_BIT_MASK) {
store_home_positions();
recall_home_positions();
positionKnown = 0;
excess.a = 0;
excess.b = 0;
@ -2009,33 +2094,7 @@ int main(int argc, char * argv[])
exit(1);
}
break;
// M137 - Enable axes steppers
case 137:
if(command.flag & AXES_BIT_MASK) {
set_steppers(command.flag & AXES_BIT_MASK, 1);
if(command.flag & A_IS_SET) tool[0].motor_enabled = 1;
if(command.flag & B_IS_SET) tool[1].motor_enabled = 1;
}
else {
fprintf(stderr, "(line %u) Syntax Error: M137 is missing axes, use X Y Z A B" EOL, line_number);
exit(1);
}
break;
// M138 - Disable axes steppers
case 138:
if(command.flag & AXES_BIT_MASK) {
set_steppers(command.flag & AXES_BIT_MASK, 0);
if(command.flag & A_IS_SET) tool[0].motor_enabled = 0;
if(command.flag & B_IS_SET) tool[1].motor_enabled = 0;
}
else {
fprintf(stderr, "(line %u) Syntax Error: M138 is missing axes, use X Y Z A B" EOL, line_number);
exit(1);
}
break;
// M146 - Set RGB LED value (RLS - P)
case 146: {
unsigned red = 0;

2
gpx.h
View File

@ -29,7 +29,7 @@
#include <limits.h>
#define GPX_VERSION "0.1a"
#define GPX_VERSION "0.2 (beta)"
// x3g axes bitfields

176
ini.c Normal file
View File

@ -0,0 +1,176 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
http://code.google.com/p/inih/
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "ini.h"
#if !INI_USE_STACK
#include <stdlib.h>
#endif
#define MAX_SECTION 50
#define MAX_NAME 50
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
/* Return pointer to first char c or ';' comment in given string, or pointer to
null at end of string if neither found. ';' must be prefixed by a whitespace
character to register as a comment. */
static char* find_char_or_comment(const char* s, char c)
{
int was_whitespace = 0;
while (*s && *s != c && !(was_whitespace && *s == ';')) {
was_whitespace = isspace((unsigned char)(*s));
s++;
}
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = '\0';
return dest;
}
/* See documentation in header file. */
int ini_parse_file(FILE* file,
int (*handler)(void*, const char*, const char*,
const char*),
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
#else
char* line;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_MAX_LINE);
if (!line) {
return -2;
}
#endif
/* Scan through file line by line */
while (fgets(line, INI_MAX_LINE, file) != NULL) {
lineno++;
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
start = lskip(rstrip(start));
if (*start == ';' || *start == '#') {
/* Per Python ConfigParser, allow '#' comments at start of line */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-black line with leading whitespace, treat as continuation
of previous name's value (as per Python ConfigParser). */
if (!handler(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_char_or_comment(start + 1, ']');
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start && *start != ';') {
/* Not a comment, must be a name[=:]value pair */
end = find_char_or_comment(start, '=');
if (*end != '=') {
end = find_char_or_comment(start, ':');
}
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = lskip(end + 1);
end = find_char_or_comment(value, '\0');
if (*end == ';')
*end = '\0';
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!handler(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
}
#if !INI_USE_STACK
free(line);
#endif
return error;
}
/* See documentation in header file. */
int ini_parse(const char* filename,
int (*handler)(void*, const char*, const char*, const char*),
void* user)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);
fclose(file);
return error;
}

72
ini.h Normal file
View File

@ -0,0 +1,72 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
http://code.google.com/p/inih/
*/
#ifndef __INI_H__
#define __INI_H__
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's ConfigParser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
of handler call). Handler should return nonzero on success, zero on error.
Returns 0 on success, line number of first error on parse error (doesn't
stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero).
*/
int ini_parse(const char* filename,
int (*handler)(void* user, const char* section,
const char* name, const char* value),
void* user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file,
int (*handler)(void* user, const char* section,
const char* name, const char* value),
void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
ConfigParser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Nonzero to use stack, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Maximum line length for any line in INI file. */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
#ifdef __cplusplus
}
#endif
#endif /* __INI_H__ */

View File

@ -236,10 +236,10 @@ M132 X Y Z A B
; M137 - Enable axes steppers
M70 (M137 - steppers on)
M132 X Y Z A B
M137 X Y Z A B
; M138 - Disable axes steppers
M70 (M138 - steppers on)
M70 (M138 - steppers off)
M138 X Y Z A B
; M146 - Set RGB LED value