New report module. 6 persistent work coordinates. New G-codes and settings. README and minor bug updates
(NOTE: This push is likely buggy so proceed with caution. Just uploading to let people know where we're going.) - New report.c module. Moved all feedback functions into this module to centralize these processes. Includes realtime status reports, status messages, feedback messages. - Official support 6 work coordinate systems (G54-G59), which are persistently held in EEPROM memory. - New g-code support: G28.1, G30.1 stores current machine position as a home position into EEPROM. G10 L20 Px stores current machine position into work coordinates without needing to explicitly send XYZ words. - Homing performed with '$H' command. G28/G30 no longer start the homing cycle. This is how it's supposed to be. - New settings: Stepper enable invert and n_arc correction installed. - Updated and changed up some limits and homing functionality. Pull-off travel will now move after the homing cycle regardless of hard limits enabled. Fixed direction of pull-off travel (went wrong way). - Started on designing an internal Grbl command protocol based on the '$' settings letter. Commands with non numeric characters after '$' will perform switch commands, homing cycle, jogging, printing paramters, etc. Much more to do here. - Updated README to reflect all of the new features.
This commit is contained in:
259
protocol.c
259
protocol.c
@ -31,124 +31,16 @@
|
||||
#include <avr/pgmspace.h>
|
||||
#include "stepper.h"
|
||||
#include "planner.h"
|
||||
#include "report.h"
|
||||
#include "motion_control.h"
|
||||
|
||||
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
|
||||
static uint8_t char_counter; // Last character counter in line variable.
|
||||
static uint8_t iscomment; // Comment/block delete flag for processor to ignore comment characters.
|
||||
|
||||
// Method to handle status messages, from an 'ok' to report any 'error' that has occurred.
|
||||
// Errors can originate from the g-code parser, settings module, or a critical error, such
|
||||
// as a triggered hard limit.
|
||||
void protocol_status_message(uint8_t status_code)
|
||||
{
|
||||
// TODO: Compile time option to only return numeric codes for GUIs.
|
||||
if (status_code == 0) { // STATUS_OK
|
||||
printPgmString(PSTR("ok\r\n"));
|
||||
} else {
|
||||
printPgmString(PSTR("error: "));
|
||||
switch(status_code) {
|
||||
case STATUS_BAD_NUMBER_FORMAT:
|
||||
printPgmString(PSTR("Bad number format")); break;
|
||||
case STATUS_EXPECTED_COMMAND_LETTER:
|
||||
printPgmString(PSTR("Expected command letter")); break;
|
||||
case STATUS_UNSUPPORTED_STATEMENT:
|
||||
printPgmString(PSTR("Unsupported statement")); break;
|
||||
case STATUS_FLOATING_POINT_ERROR:
|
||||
printPgmString(PSTR("Floating point error")); break;
|
||||
case STATUS_MODAL_GROUP_VIOLATION:
|
||||
printPgmString(PSTR("Modal group violation")); break;
|
||||
case STATUS_INVALID_STATEMENT:
|
||||
printPgmString(PSTR("Invalid statement")); break;
|
||||
case STATUS_HARD_LIMIT:
|
||||
printPgmString(PSTR("Limit triggered")); break;
|
||||
case STATUS_SETTING_DISABLED:
|
||||
printPgmString(PSTR("Grbl setting disabled")); break;
|
||||
case STATUS_SETTING_STEPS_NEG:
|
||||
printPgmString(PSTR("Steps/mm must be > 0.0")); break;
|
||||
case STATUS_SETTING_STEP_PULSE_MIN:
|
||||
printPgmString(PSTR("Step pulse must be >= 3 microseconds")); break;
|
||||
case STATUS_SETTING_READ_FAIL:
|
||||
printPgmString(PSTR("Failed to read EEPROM settings. Using defaults")); break;
|
||||
}
|
||||
printPgmString(PSTR("\r\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Prints miscellaneous messages. This serves as a centralized method to provide additional
|
||||
// user feedback for things that do not pass through the protocol_execute_line() function
|
||||
// and are not errors or confirmations, such as setup warnings and how to exit alarms.
|
||||
void protocol_misc_message(uint8_t message_code)
|
||||
{
|
||||
// TODO: Install silence misc messages option in settings
|
||||
switch(message_code) {
|
||||
case MESSAGE_SYSTEM_ALARM:
|
||||
printPgmString(PSTR("<ALARM: Reset to continue>")); break;
|
||||
case MESSAGE_HOMING_ENABLE:
|
||||
printPgmString(PSTR("warning: Install all axes limit switches before use")); break;
|
||||
}
|
||||
printPgmString(PSTR("\r\n"));
|
||||
}
|
||||
|
||||
|
||||
void protocol_status_report()
|
||||
{
|
||||
// TODO: Status report data is written to the user here. This function should be able to grab a
|
||||
// real-time snapshot of the stepper subprogram and the actual location of the CNC machine. At a
|
||||
// minimum, status report should return real-time location information. Other important information
|
||||
// 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. '[0.23,120.4,2.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 (5-20Hz).
|
||||
|
||||
// **Under construction** Bare-bones status report. Provides real-time machine position relative to
|
||||
// the system power on location (0,0,0) and work coordinate position (G54 and G92 applied).
|
||||
// The following are still needed: user setting of output units (mm|inch), compressed (non-human
|
||||
// readable) data for interfaces?, save last known position in EEPROM?, code optimizations, solidify
|
||||
// the reporting schemes, move to a separate .c file for easy user accessibility, and setting the
|
||||
// home position by the user (likely through '$' setting interface).
|
||||
// Successfully tested at a query rate of 10-20Hz while running a gauntlet of programs at various
|
||||
// speeds.
|
||||
uint8_t i;
|
||||
int32_t current_position[3]; // Copy current state of the system position variable
|
||||
memcpy(current_position,sys.position,sizeof(sys.position));
|
||||
float print_position[3];
|
||||
|
||||
// Report machine position
|
||||
printPgmString(PSTR("MPos:["));
|
||||
for (i=0; i<= 2; i++) {
|
||||
print_position[i] = current_position[i]/settings.steps_per_mm[i];
|
||||
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { print_position[i] *= INCH_PER_MM; }
|
||||
printFloat(print_position[i]);
|
||||
if (i < 2) { printPgmString(PSTR(",")); }
|
||||
else { printPgmString(PSTR("]")); }
|
||||
}
|
||||
|
||||
// Report work position
|
||||
printPgmString(PSTR(",WPos:["));
|
||||
for (i=0; i<= 2; i++) {
|
||||
if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) {
|
||||
print_position[i] -= (sys.coord_system[sys.coord_select][i]+sys.coord_offset[i])*INCH_PER_MM;
|
||||
} else {
|
||||
print_position[i] -= sys.coord_system[sys.coord_select][i]+sys.coord_offset[i];
|
||||
}
|
||||
printFloat(print_position[i]);
|
||||
if (i < 2) { printPgmString(PSTR(",")); }
|
||||
else { printPgmString(PSTR("]")); }
|
||||
}
|
||||
|
||||
printPgmString(PSTR("\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;
|
||||
}
|
||||
@ -160,29 +52,43 @@ void protocol_init()
|
||||
// This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code
|
||||
// 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 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.
|
||||
// define more computationally-expensive volatile variables. This also provides a controlled way to
|
||||
// execute certain tasks without having two or more instances of the same task, such as the planner
|
||||
// recalculating the buffer upon a feedhold or override.
|
||||
// NOTE: The sys.execute variable flags are set by the serial read subprogram, except where noted,
|
||||
// but may be set by any process, such as a switch pin change interrupt when pinouts are installed.
|
||||
void protocol_execute_runtime()
|
||||
{
|
||||
if (sys.execute) { // Enter only if any bit flag is true
|
||||
uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times
|
||||
|
||||
// System alarm. Something has gone wrong. Disable everything until system reset.
|
||||
// System alarm. Something has gone wrong. Disable everything by entering an infinite
|
||||
// loop until system reset/abort.
|
||||
if (rt_exec & EXEC_ALARM) {
|
||||
if (bit_isfalse(sys.execute,EXEC_RESET)) { protocol_misc_message(MESSAGE_SYSTEM_ALARM); }
|
||||
while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); }
|
||||
if (bit_isfalse(rt_exec,EXEC_RESET)) { // Ignore loop if reset is already issued
|
||||
report_feedback_message(MESSAGE_SYSTEM_ALARM);
|
||||
while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); }
|
||||
}
|
||||
bit_false(sys.execute,EXEC_ALARM);
|
||||
}
|
||||
|
||||
// System abort. Steppers have already been force stopped.
|
||||
if (rt_exec & EXEC_RESET) {
|
||||
sys.abort = true;
|
||||
|
||||
// If the cycle is active before killing the motion, the event will likely caused a loss
|
||||
// of position since there is no controlled deceleration(feed hold) to a stop.
|
||||
// TODO: Add force home option upon position lost. Need to verify that cycle start isn't
|
||||
// set false by anything but the stepper module. Also, need to look at a better place for
|
||||
// this. Main.c?
|
||||
// if (sys.cycle_start) { protocol_feedback_message(MESSAGE_POSITION_LOST); }
|
||||
|
||||
return; // Nothing else to do but exit.
|
||||
}
|
||||
|
||||
// Execute and serial print status
|
||||
if (rt_exec & EXEC_STATUS_REPORT) {
|
||||
protocol_status_report();
|
||||
report_realtime_status();
|
||||
bit_false(sys.execute,EXEC_STATUS_REPORT);
|
||||
}
|
||||
|
||||
@ -206,41 +112,95 @@ void protocol_execute_runtime()
|
||||
}
|
||||
bit_false(sys.execute,EXEC_CYCLE_START);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overrides flag byte (sys.override) and execution should be installed here, since they
|
||||
// are runtime and require a direct and controlled interface to the main stepper program.
|
||||
}
|
||||
|
||||
|
||||
// Executes one line of input according to protocol
|
||||
// Directs and executes one line of formatted input from protocol_process. While mostly
|
||||
// incoming streaming g-code blocks, this also executes Grbl internal commands, such as
|
||||
// settings, initiating the homing cycle, and toggling switch states. This differs from
|
||||
// the runtime command module by being susceptible to when Grbl is ready to execute the
|
||||
// next line during a cycle, so for switches like block delete, the switch only effects
|
||||
// the lines that are processed afterward, not necessarily real-time during a cycle,
|
||||
// since there are motions already stored in the buffer. However, this 'lag' should not
|
||||
// be an issue, since these commands are not typically used during a cycle.
|
||||
uint8_t protocol_execute_line(char *line)
|
||||
{
|
||||
{
|
||||
// Grbl internal command and parameter lines are of the form '$4=374.3' or '$' for help
|
||||
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 {
|
||||
//
|
||||
// JOG MODE
|
||||
//
|
||||
// TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be
|
||||
// susceptible to other runtime commands except for e-stop. The jogging function is intended to
|
||||
// be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped
|
||||
// steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would
|
||||
// start motion and toggle off would initiate a deceleration to stop. One could 'feather' the
|
||||
// motion by repeatedly toggling to slow the motion to the desired location. Location data would
|
||||
// need to be updated real-time and supplied to the user through status queries.
|
||||
// More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are
|
||||
// handled by the planner. It would be possible for the jog subprogram to insert blocks into the
|
||||
// block buffer without having the planner plan them. It would need to manage de/ac-celerations
|
||||
// on its own carefully. This approach could be effective and possibly size/memory efficient.
|
||||
|
||||
|
||||
uint8_t char_counter = 1;
|
||||
float parameter, value;
|
||||
switch( line[char_counter] ) {
|
||||
case 0 :
|
||||
report_grbl_help();
|
||||
return(STATUS_OK);
|
||||
break;
|
||||
// case '#' :
|
||||
// if ( line[++char_counter] == 0 ) {
|
||||
// // Print all parameters
|
||||
// return(STATUS_OK);
|
||||
// } else {
|
||||
// return(STATUS_UNSUPPORTED_STATEMENT);
|
||||
// }
|
||||
// case 'G' : // Start up blocks
|
||||
// if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); }
|
||||
// if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
// // Extract startup block, execute, and store.
|
||||
// for (char_counter = 0; char_counter < LINE_BUFFER_SIZE-3; char_counter++) {
|
||||
// line[char_counter] = line[char_counter+3];
|
||||
// }
|
||||
// uint8_t status = gc_execute_line(line);
|
||||
// if (status) { return(status); }
|
||||
// else { settings_store_startup_block(line); }
|
||||
// break;
|
||||
case 'H' : // Perform homing cycle
|
||||
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
|
||||
mc_go_home();
|
||||
return(STATUS_OK);
|
||||
} else {
|
||||
return(STATUS_SETTING_DISABLED);
|
||||
}
|
||||
break;
|
||||
// // case 'J' : break; // Jogging methods
|
||||
// // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be
|
||||
// // susceptible to other runtime commands except for e-stop. The jogging function is intended to
|
||||
// // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped
|
||||
// // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would
|
||||
// // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the
|
||||
// // motion by repeatedly toggling to slow the motion to the desired location. Location data would
|
||||
// // need to be updated real-time and supplied to the user through status queries.
|
||||
// // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are
|
||||
// // handled by the planner. It would be possible for the jog subprogram to insert blocks into the
|
||||
// // block buffer without having the planner plan them. It would need to manage de/ac-celerations
|
||||
// // on its own carefully. This approach could be effective and possibly size/memory efficient.
|
||||
// case 'P' : // Print g-code parameters and parser state
|
||||
// if(line[char_counter] != 0) { return(STATUS_UNSUPPORTED_STATEMENT); }
|
||||
//
|
||||
// break;
|
||||
// case 'S' : // Switch methods
|
||||
// // Opt stop and block delete are referred to as switches.
|
||||
// // How to store home position and work offsets real-time??
|
||||
// break;
|
||||
// // Parse $parameter=value settings
|
||||
default :
|
||||
if(!read_float(line, &char_counter, ¶meter)) {
|
||||
return(STATUS_BAD_NUMBER_FORMAT);
|
||||
}
|
||||
if(line[char_counter++] != '=') {
|
||||
return(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
if(!read_float(line, &char_counter, &value)) {
|
||||
return(STATUS_BAD_NUMBER_FORMAT);
|
||||
}
|
||||
if(line[char_counter] != 0) {
|
||||
return(STATUS_UNSUPPORTED_STATEMENT);
|
||||
}
|
||||
return(settings_store_global_setting(parameter, value));
|
||||
}
|
||||
} else {
|
||||
return(gc_execute_line(line)); // Everything else is gcode
|
||||
// TODO: Install option to set system alarm upon any error code received back from the
|
||||
@ -250,7 +210,8 @@ uint8_t protocol_execute_line(char *line)
|
||||
}
|
||||
|
||||
|
||||
// Process one line of incoming serial data. Remove unneeded characters and capitalize.
|
||||
// Process and report status one line of incoming serial data. Performs an initial filtering
|
||||
// by removing spaces and comments and capitalizing all letters.
|
||||
void protocol_process()
|
||||
{
|
||||
uint8_t c;
|
||||
@ -265,10 +226,10 @@ void protocol_process()
|
||||
|
||||
if (char_counter > 0) {// Line is complete. Then execute!
|
||||
line[char_counter] = 0; // Terminate string
|
||||
protocol_status_message(protocol_execute_line(line));
|
||||
report_status_message(protocol_execute_line(line));
|
||||
} else {
|
||||
// Empty or comment line. Skip block.
|
||||
protocol_status_message(STATUS_OK); // Send status message for syncing purposes.
|
||||
report_status_message(STATUS_OK); // Send status message for syncing purposes.
|
||||
}
|
||||
char_counter = 0; // Reset line buffer index
|
||||
iscomment = false; // Reset comment flag
|
||||
|
Reference in New Issue
Block a user