Position reporting, refactored system variables, serial print fixes, updated streaming scripts.

- Added machine position reporting to status queries. This will be
further developed with part positioning/offsets and maintaining
location upon reset.

- System variables refactored into a global struct for better
readability.

- Removed old obsolete Ruby streaming scripts. These were no longer
compatible. Updated Python streaming scripts.

- Fixed printFloat() and other printing functions.

- Decreased planner buffer back to 18 blocks and increased TX serial
buffer to 64 bytes. Need the memory space for future developments.

- Begun adding run-time modes to grbl, where block delete toggle, mm/in
reporting modes, jog modes, etc can be set during runtime. Will be
fleshed out and placed into EEPROM when everything is added.
This commit is contained in:
Sonny Jeon 2012-01-06 10:10:41 -07:00
parent 03e2ca7cd5
commit e8a6bfd179
21 changed files with 396 additions and 194 deletions

View File

@ -27,37 +27,38 @@
#define BAUD_RATE 9600
// Define pin-assignments
#define STEPPERS_DISABLE_DDR DDRB
#define STEPPERS_DISABLE_PORT PORTB
#define STEPPERS_DISABLE_BIT 0
#define STEPPING_DDR DDRD
#define STEPPING_PORT PORTD
#define X_STEP_BIT 2
#define Y_STEP_BIT 3
#define Z_STEP_BIT 4
#define X_DIRECTION_BIT 5
#define Y_DIRECTION_BIT 6
#define Z_DIRECTION_BIT 7
#define X_STEP_BIT 2 // Uno Digital Pin 2
#define Y_STEP_BIT 3 // Uno Digital Pin 3
#define Z_STEP_BIT 4 // Uno Digital Pin 4
#define X_DIRECTION_BIT 5 // Uno Digital Pin 5
#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6
#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7
#define STEPPERS_DISABLE_DDR DDRB
#define STEPPERS_DISABLE_PORT PORTB
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
#define LIMIT_DDR DDRB
#define LIMIT_PIN PINB
#define X_LIMIT_BIT 1
#define Y_LIMIT_BIT 2
#define Z_LIMIT_BIT 3
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11
#define SPINDLE_ENABLE_DDR DDRB
#define SPINDLE_ENABLE_PORT PORTB
#define SPINDLE_ENABLE_BIT 4
#define SPINDLE_ENABLE_BIT 4 // Uno Digital Pin 12
#define SPINDLE_DIRECTION_DDR DDRB
#define SPINDLE_DIRECTION_PORT PORTB
#define SPINDLE_DIRECTION_BIT 5
#define SPINDLE_DIRECTION_BIT 5 // Uno Digital Pin 13
// Define runtime command special characters. These characters are 'picked-off' directly from the
// serial read data stream and are not passed to the grbl line execution parser. Select characters
// that do not and must not exist in the streamed g-code program. ASCII control characters may be
// used, if they are available per user setup.
// used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in
// g-code programs, maybe selected for interface programs.
// TODO: Solidify these default characters. Temporary for now.
#define CMD_STATUS_REPORT '?'
#define CMD_FEED_HOLD '!'
@ -70,8 +71,8 @@
// entering g-code into grbl, i.e. locating part zero or simple manual machining. If the axes drift,
// grbl has no way to know this has happened, since stepper motors are open-loop control. Depending
// on the machine, this parameter may need to be larger or smaller than the default time.
// NOTE: If defined 0, the delay will not be compiled.
#define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer >= 0
// NOTE: If commented out, the delay will not be compiled.
#define STEPPER_IDLE_LOCK_TIME 25 // (milliseconds) - Integer > 0
// The temporal resolution of the acceleration management subsystem. Higher number give smoother
// acceleration but may impact performance.
@ -109,4 +110,20 @@
// time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays.
#define DWELL_TIME_STEP 50 // Integer (milliseconds)
// -----------------------------------------------
// TODO: The following options are set as compile-time options for now, until the next EEPROM
// settings version has solidified.
#define CYCLE_AUTO_START 1 // Cycle auto-start boolean flag for the planner.
#define BLOCK_DELETE_ENABLE 0 // Block delete enable/disable flag during g-code parsing
#define REPORT_INCH_MODE 0 // Status reporting unit mode (1 = inch, 0 = mm)
#if REPORT_INCH_MODE
#define DECIMAL_PLACES 3
#define DECIMAL_MULTIPLIER 1000 // 10^DECIMAL_PLACES
#else
#define DECIMAL_PLACES 2 // mm-mode
#define DECIMAL_MULTIPLIER 100
#endif
#endif

View File

@ -32,8 +32,6 @@
#include "errno.h"
#include "protocol.h"
#define MM_PER_INCH (25.4)
#define NEXT_ACTION_DEFAULT 0
#define NEXT_ACTION_DWELL 1
#define NEXT_ACTION_GO_HOME 2

48
main.c
View File

@ -4,7 +4,6 @@
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
Copyright (c) 2011 Jens Geisler
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,11 +19,8 @@
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include "config.h"
#include "planner.h"
#include "nuts_bolts.h"
@ -34,15 +30,11 @@
#include "gcode.h"
#include "protocol.h"
#include "limits.h"
#include "settings.h"
#include "serial.h"
#include "print.h"
// Declare system global variables
uint8_t sys_abort; // Global system abort flag
volatile uint8_t sys_state; // Global system state variable
// Declare system global variable structure
system_t sys;
int main(void)
{
@ -50,17 +42,18 @@ int main(void)
sei(); // Enable interrupts
serial_init(BAUD_RATE); // Setup serial baud rate and interrupts
st_init(); // Setup stepper pins and interrupt timers
sys_abort = true; // Set abort to complete initialization
sys.abort = true; // Set abort to complete initialization
while(1) {
// Upon a system abort, the main program will return to this loop. Once here, it is safe to
// re-initialize the system. Upon startup, the system will automatically reset to finish the
// initialization process.
if (sys_abort) {
// Execute system reset
sys_state = 0; // Reset system state
sys_abort = false; // Release system abort
// Execute system reset upon a system abort, where the main program will return to this loop.
// Once here, it is safe to re-initialize the system. At startup, the system will automatically
// reset to finish the initialization process.
if (sys.abort) {
// Clear all system variables
memset(&sys, 0, sizeof(sys));
// Reset system.
serial_reset_read_buffer(); // Clear serial read buffer
@ -70,19 +63,14 @@ int main(void)
gc_init(); // Set g-code parser to default state
spindle_init();
limits_init();
st_reset(); // Clear stepper subsystem variables.
// TODO: For now, the stepper subsystem tracks the absolute stepper position from the point
// of power up or hard reset. This reset is a soft reset, where the information of the current
// position is not lost after a system abort. This is not guaranteed to be correct, since
// during an abort, the steppers can lose steps in the immediate stop. However, if a feed
// hold is performed before a system abort, this position should be correct. In the next few
// updates, this soft reset feature will be fleshed out along with the status reporting and
// jogging features.
st_reset(); // Clear stepper subsystem variables. Machine position variable is not reset.
// Print grbl initialization message
printPgmString(PSTR("\r\nGrbl " GRBL_VERSION));
printPgmString(PSTR("\r\n'$' to dump current settings\r\n"));
// Set system runtime defaults
// TODO: Eventual move to EEPROM from config.h when all of the new settings are worked out.
// Mainly to avoid having to maintain several different versions.
#ifdef CYCLE_AUTO_START
sys.auto_start = true;
#endif
}
protocol_execute_runtime();

View File

@ -33,8 +33,6 @@
#include "limits.h"
#include "protocol.h"
#include "print.h"
// Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second
// unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in
// (1 minute)/feed_rate time.
@ -56,17 +54,19 @@ void mc_line(double x, double y, double z, double feed_rate, uint8_t invert_feed
// Remain in this loop until there is room in the buffer.
do {
protocol_execute_runtime(); // Check for any run-time commands
if (sys_abort) { return; } // Bail, if system abort.
if (sys.abort) { return; } // Bail, if system abort.
} while ( plan_check_full_buffer() );
plan_buffer_line(x, y, z, feed_rate, invert_feed_rate);
// Auto-cycle start.
// TODO: Determine a more efficient and robust way of implementing the auto-starting the cycle.
// For example, only auto-starting when the buffer is full; if there was only one g-code command
// sent during manual operation; or if there is buffer starvation, making sure it minimizes any
// dwelling/motion hiccups. Additionally, these situations must not auto-start during a feed hold.
// Only the cycle start runtime command should be able to restart the cycle after a feed hold.
st_cycle_start();
// Auto-cycle start immediately after planner finishes. Enabled/disabled by grbl settings. During
// a feed hold, auto-start is disabled momentarily until the cycle is resumed by the cycle-start
// runtime command.
// NOTE: This is allows the user to decide to exclusively use the cycle start runtime command to
// begin motion or let grbl auto-start it for them. This is useful when: manually cycle-starting
// when the buffer is completely full and primed; auto-starting, if there was only one g-code
// command sent during manual operation; or if a system is prone to buffer starvation, auto-start
// helps make sure it minimizes any dwelling/motion hiccups and keeps the cycle going.
if (sys.auto_start) { st_cycle_start(); }
}
@ -167,7 +167,7 @@ void mc_arc(double *position, double *target, double *offset, uint8_t axis_0, ui
mc_line(arc_target[X_AXIS], arc_target[Y_AXIS], arc_target[Z_AXIS], feed_rate, invert_feed_rate);
// Bail mid-circle on system abort. Runtime command check already performed by mc_line.
if (sys_abort) { return; }
if (sys.abort) { return; }
}
// Ensure last segment arrives at target location.
mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], feed_rate, invert_feed_rate);
@ -183,7 +183,7 @@ void mc_dwell(double seconds)
while (i > 0) {
// NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds.
protocol_execute_runtime();
if (sys_abort) { return; }
if (sys.abort) { return; }
_delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment
i--;
}

View File

@ -32,27 +32,50 @@
#define Y_AXIS 1
#define Z_AXIS 2
#define MM_PER_INCH (25.4)
// Useful macros
#define clear_vector(a) memset(a, 0, sizeof(a))
#define clear_vector_double(a) memset(a, 0.0, sizeof(a))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
// Define system state bit map. Used internally by runtime protocol as runtime command flags.
// NOTE: The system state is an unsigned 8-bit volatile variable and has a 8 flag limit. The default
// flags are always false, so the runtime protocol only needs to check for a non-zero state value to
// Bit field and masking macros
#define bit(n) (1 << n)
#define bit_true(x,mask) (x |= mask)
#define bit_false(x,mask) (x &= ~mask)
#define bit_toggle(x,mask) (x ^= mask)
#define bit_istrue(x,mask) ((x & mask) != 0)
#define bit_isfalse(x,mask) ((x & mask) == 0)
// Define system executor bit map. Used internally by runtime protocol as runtime command flags,
// which notifies the main program to execute the specified runtime command asynchronously.
// NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default
// flags are always false, so the runtime protocol only needs to check for a non-zero value to
// know when there is a runtime command to execute.
#define BIT_STATUS_REPORT 1 // bit 00000001
#define BIT_CYCLE_START 2 // bit 00000010
#define BIT_FEED_HOLD 4 // bit 00000100
#define BIT_RESET 8 // bit 00001000
#define BIT_REPLAN_CYCLE 16 // bit 00010000
// #define 32 // bit 00100000
// #define 64 // bit 01000000
// #define 128 // bit 10000000
#define EXEC_STATUS_REPORT bit(0) // bitmask 00000001
#define EXEC_CYCLE_START bit(1) // bitmask 00000010
#define EXEC_CYCLE_STOP bit(2) // bitmask 00000100
#define EXEC_FEED_HOLD bit(3) // bitmask 00001000
#define EXEC_RESET bit(4) // bitmask 00010000
// #define bit(5) // bitmask 00100000
// #define bit(6) // bitmask 01000000
// #define bit(7) // bitmask 10000000
// Define global system variables
extern uint8_t sys_abort; // Global system abort flag
extern volatile uint8_t sys_state; // Global system state variable
typedef struct {
uint8_t abort; // System abort flag. Forces exit back to main loop for reset.
uint8_t feed_hold; // Feed hold flag. Held true during feed hold. Released when ready to resume.
uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings.
int32_t position[3]; // Real-time machine position vector in steps. This may need to be a volatile
// variable, if problems arise. Subject to change. Need to add coordinate offset
// functionality to correctly track part zero and machine zero.
volatile uint8_t cycle_start; // Cycle start flag. Set by stepper subsystem or main program.
volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks.
} system_t;
extern system_t sys;
// Read a floating point value from a string. Line points to the input buffer, char_counter
// is the indexer pointing to the current character of the line, while double_ptr is

View File

@ -34,7 +34,7 @@
#include "protocol.h"
// The number of linear motions that can be in the plan at any give time
#define BLOCK_BUFFER_SIZE 20
#define BLOCK_BUFFER_SIZE 18
static block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
static volatile uint8_t block_buffer_head; // Index of the next block to be pushed
@ -336,7 +336,7 @@ void plan_synchronize()
{
while(plan_get_current_block()) {
protocol_execute_runtime(); // Check and execute run-time commands
if (sys_abort) { return; } // Check for system abort
if (sys.abort) { return; } // Check for system abort
}
}

View File

@ -35,7 +35,7 @@ typedef struct {
// Fields used by the motion planner to manage acceleration
double nominal_speed; // The nominal speed for this block in mm/min
double entry_speed; // Entry speed at previous-current junction in mm/min
double entry_speed; // Entry speed at previous-current block junction in mm/min
double max_entry_speed; // Maximum allowable junction entry speed in mm/min
double millimeters; // The total travel of this block in mm
uint8_t recalculate_flag; // Planner flag to recalculate trapezoids on entry junction

92
print.c
View File

@ -3,6 +3,7 @@
Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -24,12 +25,9 @@
#include <math.h>
#include <avr/pgmspace.h>
#include "config.h"
#include "serial.h"
#ifndef DECIMAL_PLACES
#define DECIMAL_PLACES 3
#endif
void printString(const char *s)
{
while (*s)
@ -44,10 +42,45 @@ void printPgmString(const char *s)
serial_write(c);
}
void printIntegerInBase(unsigned long n, unsigned long base)
// void printIntegerInBase(unsigned long n, unsigned long base)
// {
// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
// unsigned long i = 0;
//
// if (n == 0) {
// serial_write('0');
// return;
// }
//
// while (n > 0) {
// buf[i++] = n % base;
// n /= base;
// }
//
// for (; i > 0; i--)
// serial_write(buf[i - 1] < 10 ?
// '0' + buf[i - 1] :
// 'A' + buf[i - 1] - 10);
// }
void print_uint8_base2(uint8_t n)
{
unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
unsigned long i = 0;
unsigned char buf[8];
uint8_t i = 0;
for (; i < 8; i++) {
buf[i] = n & 1;
n >>= 1;
}
for (; i > 0; i--)
serial_write('0' + buf[i - 1]);
}
static void print_uint32_base10(unsigned long n)
{
unsigned char buf[32];
uint8_t i = 0;
if (n == 0) {
serial_write('0');
@ -55,14 +88,12 @@ void printIntegerInBase(unsigned long n, unsigned long base)
}
while (n > 0) {
buf[i++] = n % base;
n /= base;
buf[i++] = n % 10;
n /= 10;
}
for (; i > 0; i--)
serial_write(buf[i - 1] < 10 ?
'0' + buf[i - 1] :
'A' + buf[i - 1] - 10);
serial_write('0' + buf[i - 1]);
}
void printInteger(long n)
@ -71,25 +102,30 @@ void printInteger(long n)
serial_write('-');
n = -n;
}
printIntegerInBase(n, 10);
print_uint32_base10(n);
}
// A very simple
void printFloat(double n)
{
double integer_part, fractional_part;
uint8_t decimal_part;
fractional_part = modf(n, &integer_part);
printInteger(integer_part);
serial_write('.');
fractional_part *= 10;
int decimals = DECIMAL_PLACES;
while(decimals-- > 0) {
decimal_part = floor(fractional_part);
serial_write('0'+decimal_part);
fractional_part -= decimal_part;
fractional_part *= 10;
}
if (n < 0) {
serial_write('-');
n = -n;
}
n += 0.5/DECIMAL_MULTIPLIER; // Add rounding factor
long integer_part;
integer_part = (int)n;
print_uint32_base10(integer_part);
serial_write('.');
n -= integer_part;
int decimals = DECIMAL_PLACES;
uint8_t decimal_part;
while(decimals-- > 0) {
n *= 10;
decimal_part = (int) n;
serial_write('0'+decimal_part);
n -= decimal_part;
}
}

View File

@ -3,6 +3,7 @@
Part of Grbl
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -30,7 +31,7 @@ void printPgmString(const char *s);
void printInteger(long n);
void printIntegerInBase(unsigned long n, unsigned long base);
void print_uint8_base2(uint8_t n);
void printFloat(double n);

View File

@ -69,15 +69,39 @@ void protocol_status_report()
// may be distance to go on block, processed block id, and feed rate. A secondary, non-critical
// status report may include g-code state, i.e. inch mode, plane mode, absolute mode, etc.
// The report generated must be as short as possible, yet still provide the user easily readable
// information, i.e. 'x0.23 y120.4 z2.4'. This is necessary as it minimizes the computational
// information, i.e. 'x0.23,y120.4,z2.4'. This is necessary as it minimizes the computational
// overhead and allows grbl to keep running smoothly, especially with g-code programs with fast,
// short line segments and interface setups that require real-time status reports (10-20Hz).
printString("Query Received.\r\n"); // Notify that it's working.
// short line segments and interface setups that require real-time status reports (5-20Hz).
// Additionally, during an abort, the steppers are immediately stopped regardless of what they
// are doing. If they are moving, the abort stop can cause grbl to lose steps. However, if a feed
// hold is performed before a system abort, the steppers will steadily decelerate at the max
// acceleration rate, hence the stopped machine position will be maintained and correct.
// Bare-bones status report. Provides real-time machine position relative to the initialization
// or system reset location (0,0,0), not a home position. This section is under construction and
// the following are needed: coordinate offsets/updating of machine position relative to home, work
// coordinate position?, user setting of output units (mm|inch), compressed (non-human readable)
// data for interfaces?, save last known position in EEPROM?
#if REPORT_INCH_MODE
printString("x"); printFloat(sys.position[X_AXIS]/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH));
printString(",y"); printFloat(sys.position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH));
printString(",z"); printFloat(sys.position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH));
#else
printString("x"); printFloat(sys.position[X_AXIS]/(settings.steps_per_mm[X_AXIS]));
printString(",y"); printFloat(sys.position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS]));
printString(",z"); printFloat(sys.position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS]));
#endif
printString("\r\n");
}
void protocol_init()
{
// Print grbl initialization message
printPgmString(PSTR("\r\nGrbl " GRBL_VERSION));
printPgmString(PSTR("\r\n'$' to dump current settings\r\n"));
char_counter = 0; // Reset line input
iscomment = false;
}
@ -87,40 +111,46 @@ void protocol_init()
// program, primarily where there may be a while loop waiting for a buffer to clear space or any
// point where the execution time from the last check point may be more than a fraction of a second.
// This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code
// parsing and planning functions.
// NOTE: The sys_state variable flags are set by the serial read subprogram, except where noted.
// parsing and planning functions. This function also serves as an interface for the interrupts to
// set the system runtime flags, where only the main program to handles them, removing the need to
// define more computationally-expensive volatile variables.
// NOTE: The sys.execute variable flags are set by the serial read subprogram, except where noted.
void protocol_execute_runtime()
{
if (sys_state) { // Enter only if any bit flag is enabled
if (sys.execute) { // Enter only if any bit flag is true
uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times
// System abort. Steppers have already been force stopped.
if (sys_state & BIT_RESET) {
sys_abort = true;
if (rt_exec & EXEC_RESET) {
sys.abort = true;
return; // Nothing else to do but exit.
}
// Execute and serial print status
if (sys_state & BIT_STATUS_REPORT) {
if (rt_exec & EXEC_STATUS_REPORT) {
bit_false(sys.execute,EXEC_STATUS_REPORT);
protocol_status_report();
sys_state ^= BIT_STATUS_REPORT; // Toggle off
}
// Initiate stepper feed hold
if (sys_state & BIT_FEED_HOLD) {
st_feed_hold();
sys_state ^= BIT_FEED_HOLD; // Toggle off
if (rt_exec & EXEC_FEED_HOLD) {
st_feed_hold(); // Initiate feed hold.
bit_false(sys.execute,EXEC_FEED_HOLD);
}
// Re-plans the buffer after a feed hold completes
// NOTE: BIT_REPLAN_CYCLE is set by the stepper subsystem when the feed hold is complete.
if (sys_state & BIT_REPLAN_CYCLE) {
// Reinitializes the stepper module running flags and re-plans the buffer after a feed hold.
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
if (rt_exec & EXEC_CYCLE_STOP) {
st_cycle_reinitialize();
sys_state ^= BIT_REPLAN_CYCLE; // Toggle off
bit_false(sys.execute,EXEC_CYCLE_STOP);
}
if (sys_state & BIT_CYCLE_START) {
if (rt_exec & EXEC_CYCLE_START) {
st_cycle_start(); // Issue cycle start command to stepper subsystem
sys_state ^= BIT_CYCLE_START; // Toggle off
#ifdef CYCLE_AUTO_START
sys.auto_start = true; // Re-enable auto start after feed hold.
#endif
bit_false(sys.execute,EXEC_CYCLE_START);
}
}
}
@ -130,6 +160,15 @@ void protocol_execute_runtime()
uint8_t protocol_execute_line(char *line)
{
if(line[0] == '$') {
// TODO: Re-write this '$' as a way to change runtime settings without having to reset, i.e.
// auto-starting, status query output formatting and type, jog mode (axes, direction, and
// nominal feedrate), toggle block delete, etc. This differs from the EEPROM settings, as they
// are considered defaults and loaded upon startup/reset.
// This use is envisioned where '$' itself dumps settings and help. Defined characters
// proceeding the '$' may be used to setup modes, such as jog mode with a '$J=X100' for X-axis
// motion with a nominal feedrate of 100mm/min. Writing EEPROM settings will likely stay the
// same or similar. Should be worked out in upcoming releases.
return(settings_execute_line(line)); // Delegate lines starting with '$' to the settings module
// } else if {
@ -165,7 +204,7 @@ void protocol_process()
// NOTE: If there is no line, this function should quickly return to the main program when
// the buffer empties of non-executable data.
protocol_execute_runtime();
if (sys_abort) { return; } // Bail to main program upon system abort
if (sys.abort) { return; } // Bail to main program upon system abort
if (char_counter > 0) {// Line is complete. Then execute!
line[char_counter] = 0; // Terminate string
@ -176,6 +215,7 @@ void protocol_process()
}
char_counter = 0; // Reset line buffer index
iscomment = false; // Reset comment flag
} else {
if (iscomment) {
// Throw away all comment characters
@ -187,9 +227,10 @@ void protocol_process()
if (c <= ' ') {
// Throw away whitepace and control characters
} else if (c == '/') {
// Disable block delete and throw away character
// To enable block delete, uncomment following line. Will ignore until EOL.
// iscomment = true;
// Disable block delete and throw away characters. Will ignore until EOL.
#if BLOCK_DELETE_ENABLE
iscomment = true;
#endif
} else if (c == '(') {
// Enable comments flag and ignore all characters until ')' or EOL.
iscomment = true;

View File

@ -1,6 +1,15 @@
#!/usr/bin/env python
"""\
Simple g-code streaming script for grbl
Provided as an illustration of the basic communication interface
for grbl. When grbl has finished parsing the g-code block, it will
return an 'ok' or 'error' response. When the planner buffer is full,
grbl will not send a response until the planner buffer clears space.
G02/03 arcs are special exceptions, where they inject short line
segments directly into the planner. So there may not be a response
from grbl for the duration of the arc.
"""
import serial

80
script/stream.py Executable file
View File

@ -0,0 +1,80 @@
#!/usr/bin/env python
"""\
Stream g-code to grbl controller
This script differs from the simple_stream.py script by
tracking the number of characters in grbl's serial read
buffer. This allows grbl to fetch the next line directly
from the serial buffer and does not have to wait for a
response from the computer. This effectively adds another
buffer layer to prevent buffer starvation.
TODO: - Add runtime command capabilities
Version: SKJ.20120104
"""
import serial
import re
import time
import sys
import argparse
RX_BUFFER_SIZE = 128
# Define command line argument interface
parser = argparse.ArgumentParser(description='Stream g-code file to grbl. (pySerial library required)')
parser.add_argument('gcode', type=argparse.FileType('r'),
help='g-code filename to be streamed')
parser.add_argument('device',
help='serial device path')
parser.add_argument('-q','--quiet',action='store_true', default=False,
help='suppress output text')
args = parser.parse_args()
# Initialize
s = serial.Serial(args.device_file,9600)
f = args.gcode_file
verbose = True
if args.quiet : verbose = False
# Wake up grbl
print "Initializing grbl..."
s.write("\r\n\r\n")
# Wait for grbl to initialize and flush startup text in serial input
time.sleep(2)
s.flushInput()
# Stream g-code to grbl
print "Streaming ", args.gcode_file.name, " to ", args.device_file
l_count = 0
g_count = 0
c_line = []
for line in f:
l_count += 1 # Iterate line counter
# l_block = re.sub('\s|\(.*?\)','',line).upper() # Strip comments/spaces/new line and capitalize
l_block = line.strip()
c_line.append(len(l_block)) # Track number of characters in grbl serial read buffer
grbl_out = ''
while sum(c_line) >= RX_BUFFER_SIZE-1 | s.inWaiting() :
out_temp = s.readline().strip() # Wait for grbl response
if out_temp not in ['ok','error'] :
print " Debug: ",out_temp # Debug response
else :
grbl_out += out_temp;
g_count += 1 # Iterate g-code counter
grbl_out += str(g_count); # Add line finished indicator
del c_line[0]
if verbose: print "SND: " + str(l_count) + " : " + l_block,
s.write(l_block + '\n') # Send block to grbl
if verbose : print "BUF:",str(sum(c_line)),"REC:",grbl_out
# Wait for user input after streaming is completed
print "G-code streaming finished!\n"
print "WARNING: Wait until grbl completes buffered g-code blocks before exiting."
raw_input(" Press <Enter> to exit and disable grbl.")
# Close file and serial port
f.close()
s.close()

View File

@ -31,7 +31,7 @@
#include "protocol.h"
#define RX_BUFFER_SIZE 128
#define TX_BUFFER_SIZE 32
#define TX_BUFFER_SIZE 64
uint8_t rx_buffer[RX_BUFFER_SIZE];
uint8_t rx_buffer_head = 0;
@ -72,7 +72,7 @@ void serial_write(uint8_t data) {
// Wait until there is space in the buffer
while (next_head == tx_buffer_tail) {
protocol_execute_runtime(); // Check for any run-time commands
if (sys_abort) { return; } // Bail, if system abort.
if (sys.abort) { return; } // Bail, if system abort.
}
// Store data and advance head
@ -124,15 +124,15 @@ ISR(USART_RX_vect)
// Pick off runtime command characters directly from the serial stream. These characters are
// not passed into the buffer, but these set system state flag bits for runtime execution.
switch (data) {
case CMD_STATUS_REPORT: sys_state |= BIT_STATUS_REPORT; break; // Set as true
case CMD_CYCLE_START: sys_state |= BIT_CYCLE_START; break; // Set as true
case CMD_FEED_HOLD: sys_state |= BIT_FEED_HOLD; break; // Set as true
case CMD_STATUS_REPORT: sys.execute |= EXEC_STATUS_REPORT; break; // Set as true
case CMD_CYCLE_START: sys.execute |= EXEC_CYCLE_START; break; // Set as true
case CMD_FEED_HOLD: sys.execute |= EXEC_FEED_HOLD; break; // Set as true
case CMD_RESET:
// Immediately force stepper subsystem idle at an interrupt level.
if (!(sys_state & BIT_RESET)) { // Force stop only first time.
if (!(sys.execute & EXEC_RESET)) { // Force stop only first time.
st_go_idle();
}
sys_state |= BIT_RESET; // Set as true
sys.execute |= EXEC_RESET; // Set as true
break;
default : // Write character to buffer
rx_buffer[rx_buffer_head] = data;

View File

@ -54,6 +54,7 @@ typedef struct {
#define DEFAULT_ACCELERATION (DEFAULT_FEEDRATE*60*60/10.0) // mm/min^2
#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm
#define DEFAULT_STEPPING_INVERT_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT))
// #define DEFAULT_AUTO_START 1 // Boolean
void settings_reset() {
settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM;
@ -77,10 +78,11 @@ void settings_dump() {
printPgmString(PSTR(" (mm/min default feed rate)\r\n$5 = ")); printFloat(settings.default_seek_rate);
printPgmString(PSTR(" (mm/min default seek rate)\r\n$6 = ")); printFloat(settings.mm_per_arc_segment);
printPgmString(PSTR(" (mm/arc segment)\r\n$7 = ")); printInteger(settings.invert_mask);
printPgmString(PSTR(" (step port invert mask. binary = ")); printIntegerInBase(settings.invert_mask, 2);
printPgmString(PSTR(" (step port invert mask. binary = ")); print_uint8_base2(settings.invert_mask);
printPgmString(PSTR(")\r\n$8 = ")); printFloat(settings.acceleration/(60*60)); // Convert from mm/min^2 for human readability
printPgmString(PSTR(" (acceleration in mm/sec^2)\r\n$9 = ")); printFloat(settings.junction_deviation);
printPgmString(PSTR(" (cornering junction deviation in mm)"));
printPgmString(PSTR(" (cornering junction deviation in mm)"));//\r\n$10 = ")); // printInteger(settings.auto_start);
// printPgmString(PSTR(" (auto-start boolean)"));
printPgmString(PSTR("\r\n'$x=value' to set parameter or just '$' to dump current settings\r\n"));
}
@ -131,6 +133,7 @@ int read_settings() {
}
settings.acceleration = DEFAULT_ACCELERATION;
settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
// settings.auto_start = DEFAULT_AUTO_START;
write_settings();
} else if ((version == 2) || (version == 3)) {
// Migrate from settings version 2 and 3
@ -139,7 +142,15 @@ int read_settings() {
}
if (version == 2) { settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; }
settings.acceleration *= 3600; // Convert to mm/min^2 from mm/sec^2
// settings.auto_start = DEFAULT_AUTO_START;
write_settings();
// } else if (version == 4) {
// // Migrate from settings version 4
// if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)))) {
// return(false);
// }
// settings.auto_start = DEFAULT_AUTO_START;
// write_settings();
} else {
return(false);
}
@ -167,6 +178,7 @@ void settings_store_setting(int parameter, double value) {
case 7: settings.invert_mask = trunc(value); break;
case 8: settings.acceleration = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 9: settings.junction_deviation = fabs(value); break;
// case 10: settings.auto_start = value; break;
default:
printPgmString(PSTR("Unknown parameter\r\n"));
return;

View File

@ -22,7 +22,6 @@
#ifndef settings_h
#define settings_h
#include <math.h>
#include <inttypes.h>

View File

@ -4,7 +4,6 @@
Copyright (c) 2009-2011 Simen Svale Skogsrud
Copyright (c) 2011 Sungeun K. Jeon
Copyright (c) 2011 Jens Geisler
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -34,8 +33,6 @@
#include "planner.h"
#include "limits.h"
#include "print.h"
// Some useful constants
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT)) // All direction bits
@ -46,9 +43,6 @@
// Stepper state variable. Contains running data and trapezoid variables.
typedef struct {
volatile uint8_t cycle_start; // Cycle start flag
volatile uint8_t feed_hold; // Feed hold flag
// Used by the bresenham line algorithm
int32_t counter_x, // Counter variables for the bresenham line tracer
counter_y,
@ -62,10 +56,9 @@ typedef struct {
// pace without allocating a separate timer
uint32_t trapezoid_adjusted_rate; // The current rate of step_events according to the trapezoid generator
uint32_t min_safe_rate; // Minimum safe rate for full deceleration rate reduction step. Otherwise halves step_rate.
} stepper_state_t;
} stepper_t;
static stepper_state_t st;
static int32_t st_position[3]; // Track current position in steps from initialization state.
static stepper_t st;
static block_t *current_block; // A pointer to the block currently being traced
// Used by the stepper driver interrupt
@ -112,7 +105,7 @@ void st_go_idle()
TIMSK1 &= ~(1<<OCIE1A);
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
// stop and not drift from residual inertial forces at the end of the last movement.
#if STEPPER_IDLE_LOCK_TIME
#ifdef STEPPER_IDLE_LOCK_TIME
_delay_ms(STEPPER_IDLE_LOCK_TIME);
#endif
// Disable steppers by setting stepper disable
@ -146,6 +139,7 @@ static uint8_t iterate_trapezoid_cycle_counter()
// interrupt doing its thing, not that big of a deal, but the latter cause is unknown and worrisome. Need
// to track down what is causing this problem. Functionally, this shouldn't cause any noticeable issues
// as long as stepper drivers have a pulse minimum of 1usec or so (Pololu and any Allegro IC are ok).
// This seems to be an inherent issue that dates all the way back to Simen's v0.6b.
ISR(TIMER1_COMPA_vect,ISR_NOBLOCK)
{
@ -165,7 +159,8 @@ ISR(TIMER1_COMPA_vect,ISR_NOBLOCK)
// Anything in the buffer? If so, initialize next motion.
current_block = plan_get_current_block();
if (current_block != NULL) {
if (!st.feed_hold) { // During feed hold, do not update rate and trap counter. Keep decelerating.
if (!sys.feed_hold) {
// During feed hold, do not update rate and trap counter. Keep decelerating.
st.trapezoid_adjusted_rate = current_block->initial_rate;
set_step_events_per_minute(st.trapezoid_adjusted_rate); // Initialize cycles_per_step_event
st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule.
@ -177,9 +172,9 @@ ISR(TIMER1_COMPA_vect,ISR_NOBLOCK)
st.event_count = current_block->step_event_count;
st.step_events_completed = 0;
} else {
st.cycle_start = false;
st.feed_hold = false;
st_go_idle();
sys.cycle_start = false;
bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program for cycle end
}
}
@ -190,29 +185,29 @@ ISR(TIMER1_COMPA_vect,ISR_NOBLOCK)
if (st.counter_x > 0) {
out_bits |= (1<<X_STEP_BIT);
st.counter_x -= st.event_count;
if (out_bits & (1<<X_DIRECTION_BIT)) { st_position[X_AXIS]--; }
else { st_position[X_AXIS]++; }
if (out_bits & (1<<X_DIRECTION_BIT)) { sys.position[X_AXIS]--; }
else { sys.position[X_AXIS]++; }
}
st.counter_y += current_block->steps_y;
if (st.counter_y > 0) {
out_bits |= (1<<Y_STEP_BIT);
st.counter_y -= st.event_count;
if (out_bits & (1<<Y_DIRECTION_BIT)) { st_position[Y_AXIS]--; }
else { st_position[Y_AXIS]++; }
if (out_bits & (1<<Y_DIRECTION_BIT)) { sys.position[Y_AXIS]--; }
else { sys.position[Y_AXIS]++; }
}
st.counter_z += current_block->steps_z;
if (st.counter_z > 0) {
out_bits |= (1<<Z_STEP_BIT);
st.counter_z -= st.event_count;
if (out_bits & (1<<Z_DIRECTION_BIT)) { st_position[Z_AXIS]--; }
else { st_position[Z_AXIS]++; }
if (out_bits & (1<<Z_DIRECTION_BIT)) { sys.position[Z_AXIS]--; }
else { sys.position[Z_AXIS]++; }
}
st.step_events_completed++; // Iterate step events
// While in block steps, check for de/ac-celeration events and execute them accordingly.
if (st.step_events_completed < current_block->step_event_count) {
if (st.feed_hold) {
if (sys.feed_hold) {
// Check for and execute feed hold by enforcing a steady deceleration from the moment of
// execution. The rate of deceleration is limited by rate_delta and will never decelerate
// faster or slower than in normal operation. If the distance required for the feed hold
@ -225,10 +220,11 @@ ISR(TIMER1_COMPA_vect,ISR_NOBLOCK)
// If deceleration complete, set system flags and shutdown steppers.
if (st.trapezoid_adjusted_rate <= current_block->rate_delta) {
// Just go idle. Do not NULL current block. The bresenham algorithm variables must
// remain intact to ensure the stepper path is exactly the same.
st.cycle_start = false;
// remain intact to ensure the stepper path is exactly the same. Feed hold is still
// active and is released after the buffer has been reinitialized.
st_go_idle();
sys_state |= BIT_REPLAN_CYCLE; // Flag main program that feed hold is complete.
sys.cycle_start = false;
bit_true(sys.execute,EXEC_CYCLE_STOP); // Flag main program that feed hold is complete.
} else {
st.trapezoid_adjusted_rate -= current_block->rate_delta;
set_step_events_per_minute(st.trapezoid_adjusted_rate);
@ -340,9 +336,6 @@ void st_init()
TCCR2B = (1<<CS21); // Full speed, 1/8 prescaler
TIMSK2 |= (1<<TOIE2);
// Initialize machine position vector
clear_vector(st_position);
// Start in the idle state
st_go_idle();
}
@ -394,12 +387,12 @@ static void set_step_events_per_minute(uint32_t steps_per_minute)
}
// Planner external interface to start stepper interrupt and execute the blocks in queue. Called
// by planner auto-start and run-time command functions.
// by the main program functions: planner auto-start and run-time command execution.
void st_cycle_start()
{
if (!st.cycle_start) {
if (!st.feed_hold) {
st.cycle_start = true;
if (!sys.cycle_start) {
if (!sys.feed_hold) {
sys.cycle_start = true;
st_wake_up();
}
}
@ -408,9 +401,10 @@ void st_cycle_start()
// Execute a feed hold with deceleration, only during cycle. Called by main program.
void st_feed_hold()
{
if (!st.feed_hold) {
if (st.cycle_start) {
st.feed_hold = true;
if (!sys.feed_hold) {
if (sys.cycle_start) {
sys.auto_start = false; // Disable planner auto start upon feed hold.
sys.feed_hold = true;
}
}
}
@ -422,6 +416,7 @@ void st_feed_hold()
// Only the planner de/ac-celerations profiles and stepper rates have been updated.
void st_cycle_reinitialize()
{
if (current_block != NULL) {
// Replan buffer from the feed hold stop location.
plan_cycle_reinitialize(current_block->step_event_count - st.step_events_completed);
// Update initial rate and timers after feed hold.
@ -429,5 +424,8 @@ void st_cycle_reinitialize()
set_step_events_per_minute(st.trapezoid_adjusted_rate);
st.trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule.
st.step_events_completed = 0;
st.feed_hold = false; // Release feed hold. Cycle is ready to re-start.
}
sys.feed_hold = false; // Release feed hold. Cycle is ready to re-start.
}