From 559feb97e23b783883c8aef549d3dc330f9171b9 Mon Sep 17 00:00:00 2001 From: Sonny Jeon Date: Wed, 14 Nov 2012 17:36:29 -0700 Subject: [PATCH] Re-factored system states and alarm management. Serial baud support greater than 57600. - Refactored system states to be more clear and concise. Alarm locks processes when position is unknown to indicate to user something has gone wrong. - Changed mc_alarm to mc_reset, which now manages the system reset function. Centralizes it. - Renamed '$X' kill homing lock to kill alarm lock. - Created an alarm error reporting method to clear up what is an alarm: message vs a status error: message. For GUIs mainly. Alarm codes are negative. Status codes are positive. - Serial baud support upto 115200. Previous baudrate calc was unstable for 57600 and above. - Alarm state locks out all g-code blocks, including startup scripts, but allows user to access settings and internal commands. For example, to disable hard limits, if they are problematic. - Hard limits do not respond in an alarm state. - Fixed a problem with the hard limit interrupt during the homing cycle. The interrupt register is still active during the homing cycle and still signal the interrupt to trigger when re-enabled. Instead, just disabled the register. - Homing rate adjusted. All axes move at homing seek rate, regardless of how many axes move at the same time. This is unlike how the stepper module does it as a point to point rate. - New config.h settings to disable the homing rate adjustment and the force homing upon powerup. - Reduced the number of startup lines back down to 2 from 3. This discourages users from placing motion block in there, which can be very dangerous. - Startup blocks now run only after an alarm-free reset or after a homing cycle. Does not run when $X kill is called. For satefy reasons --- config.h | 16 +- gcode.c | 7 +- limits.c | 53 ++++-- main.c | 42 +++-- motion_control.c | 51 +++--- motion_control.h | 4 +- nuts_bolts.c | 2 +- nuts_bolts.h | 20 +-- protocol.c | 77 ++++---- report.c | 106 +++++++----- report.h | 35 ++-- serial.c | 28 ++- serial.h | 2 +- settings.c | 443 +++++++++++++++++++++++------------------------ 14 files changed, 466 insertions(+), 420 deletions(-) diff --git a/config.h b/config.h index 211c34b..6fe5620 100755 --- a/config.h +++ b/config.h @@ -69,9 +69,9 @@ #define COOLANT_FLOOD_PORT PORTC #define COOLANT_FLOOD_BIT 3 // Uno Analog Pin 3 -// #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable. // NOTE: Uno analog pins 4 and 5 are reserved for an i2c interface, and may be installed at // a later date if flash and memory space allows. +// #define ENABLE_M7 // Mist coolant disabled by default. Uncomment to enable. #ifdef ENABLE_M7 #define COOLANT_MIST_DDR DDRC #define COOLANT_MIST_PORT PORTC @@ -158,6 +158,18 @@ // time step. Also, keep in mind that the Arduino delay timer is not very accurate for long delays. #define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds) +// If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces +// the user to perform the homing cycle (or override the locks) before doing anything else. This is +// mainly a safety feature to remind the user to home, since position is unknown to Grbl. +#define HOMING_INIT_LOCK // Comment to disable + +// The homing cycle seek and feed rates will adjust so all axes independently move at the homing +// seek and feed rates regardless of how many axes are in motion simultaneously. If disabled, rates +// are point-to-point rates, as done in normal operation. For example in an XY diagonal motion, the +// diagonal motion moves at the intended rate, but the individual axes move at 70% speed. This option +// just moves them all at 100% speed. +#define HOMING_RATE_ADJUST // Comment to disable + // Number of homing cycles performed after when the machine initially jogs to limit switches. // This help in preventing overshoot and should improve repeatability. This value should be one or // greater. @@ -167,7 +179,7 @@ // and addresses are defined in settings.h. With the current settings, up to 5 startup blocks may // be stored and executed in order. These startup blocks would typically be used to set the g-code // parser state depending on user preferences. -#define N_STARTUP_LINE 3 // Integer (1-5) +#define N_STARTUP_LINE 2 // Integer (1-5) // --------------------------------------------------------------------------------------- // FOR ADVANCED USERS ONLY: diff --git a/gcode.c b/gcode.c index 029e022..9f145a2 100755 --- a/gcode.c +++ b/gcode.c @@ -84,6 +84,11 @@ static float to_millimeters(float value) // internal functions in terms of (mm, mm/min) and absolute machine coordinates, respectively. uint8_t gc_execute_line(char *line) { + + // If in alarm state, don't process. Immediately return with error. + // NOTE: Might not be right place for this, but also prevents $N storing during alarm. + if (sys.state == STATE_ALARM) { return(STATUS_ALARM_LOCK); } + uint8_t char_counter = 0; char letter; float value; @@ -545,7 +550,7 @@ uint8_t gc_execute_line(char *line) // If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9). Otherwise, // re-enable program flow after pause complete, where cycle start will resume the program. - if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { sys.execute |= EXEC_RESET; } + if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { mc_reset(); } else { gc.program_flow = PROGRAM_FLOW_RUNNING; } } diff --git a/limits.c b/limits.c index bc0cf40..2548cd4 100755 --- a/limits.c +++ b/limits.c @@ -41,30 +41,41 @@ void limits_init() LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { - LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt - PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt + LIMIT_PCMSK |= LIMIT_MASK; // Enable specific pins of the Pin Change Interrupt + PCICR |= (1 << LIMIT_INT); // Enable Pin Change Interrupt + } else { + LIMIT_PCMSK &= ~LIMIT_MASK; // Disable + PCICR &= ~(1 << LIMIT_INT); } } -// This is the Limit Pin Change Interrupt, which handles the hard limit feature. +// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing +// limit switch can cause a lot of problems, like false readings and multiple interrupt calls. +// If a switch is triggered at all, something bad has happened and treat it as such, regardless +// if a limit switch is being disengaged. It's impossible to reliably tell the state of a +// bouncing pin without a debouncing method. // NOTE: Do not attach an e-stop to the limit pins, because this interrupt is disabled during // homing cycles and will not respond correctly. Upon user request or need, there may be a // special pinout for an e-stop, but it is generally recommended to just directly connect // your e-stop switch to the Arduino reset pin, since it is the most correct way to do this. -// TODO: This interrupt may be used to manage the homing cycle directly with the main stepper -// interrupt without adding too much to it. All it would need is some way to stop one axis -// when its limit is triggered and continue the others. This may reduce some of the code, but -// would make Grbl a little harder to read and understand down road. Holding off on this until -// we move on to new hardware or flash space becomes an issue. If it ain't broke, don't fix it. ISR(LIMIT_INT_vect) { - // Only enter if the system alarm is not active. - if (bit_isfalse(sys.execute,EXEC_ALARM)) { - // Kill all processes upon hard limit event. - if ((LIMIT_PIN & LIMIT_MASK) ^ LIMIT_MASK) { - mc_alarm(); // Initiate system kill. - sys.state = STATE_LIMIT; // Set system state to indicate event. - } + // TODO: This interrupt may be used to manage the homing cycle directly with the main stepper + // interrupt without adding too much to it. All it would need is some way to stop one axis + // when its limit is triggered and continue the others. This may reduce some of the code, but + // would make Grbl a little harder to read and understand down road. Holding off on this until + // we move on to new hardware or flash space becomes an issue. If it ain't broke, don't fix it. + + // Ignore limit switches if already in an alarm state or in-process of executing an alarm. + // When in the alarm state, Grbl should have been reset or will force a reset, so any pending + // moves in the planner and serial buffers are all cleared and newly sent blocks will be + // locked out until a homing cycle or a kill lock command. Allows the user to disable the hard + // limit setting if their limits are constantly triggering after a reset and move their axes. + if (sys.state != STATE_ALARM) { + if (bit_isfalse(sys.execute,EXEC_ALARM)) { + mc_reset(); // Initiate system kill. + sys.execute |= EXEC_CRIT_EVENT; // Indicate hard limit critical event + } } } @@ -104,6 +115,11 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, // Compute the adjusted step rate change with each acceleration tick. (in step/min/acceleration_tick) uint32_t delta_rate = ceil( ds*settings.acceleration/(60*ACCELERATION_TICKS_PER_SECOND)); + #ifdef HOMING_RATE_ADJUST + // Adjust homing rate so a multiple axes moves all at the homing rate independently. + homing_rate *= sqrt(x_axis+y_axis+z_axis); + #endif + // Nominal and initial time increment per step. Nominal should always be greater then 3 // usec, since they are based on the same parameters as the main stepper routine. Initial // is based on the MINIMUM_STEPS_PER_MINUTE config. Since homing feed can be very slow, @@ -191,12 +207,13 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, int8_t pos_dir, void limits_go_home() { - // Enable only the steppers, not the cycle. Cycle should be complete. + // Enable only the steppers, not the cycle. Cycle should be inactive/complete. st_wake_up(); // Jog all axes toward home to engage their limit switches at faster homing seek rate. - homing_cycle(false, false, true, true, false, settings.homing_seek_rate); // First jog the z axis - homing_cycle(true, true, false, true, false, settings.homing_seek_rate); // Then jog the x and y axis + // First jog z-axis to clear workspace, then jog the x and y axis. + homing_cycle(false, false, true, true, false, settings.homing_seek_rate); // z-axis + homing_cycle(true, true, false, true, false, settings.homing_seek_rate); // xy-axes delay_ms(settings.homing_debounce_delay); // Delay to debounce signal // Now in proximity of all limits. Carefully leave and approach switches in multiple cycles diff --git a/main.c b/main.c index 9174d52..c03735f 100755 --- a/main.c +++ b/main.c @@ -45,14 +45,14 @@ system_t sys; int main(void) { // Initialize system - serial_init(BAUD_RATE); // Setup serial baud rate and interrupts + serial_init(); // Setup serial baud rate and interrupts settings_init(); // Load grbl settings from EEPROM st_init(); // Setup stepper pins and interrupt timers sei(); // Enable interrupts memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization - sys.state = STATE_ALARM; // Set alarm state to indicate unknown initial position + sys.state = STATE_INIT; // Set alarm state to indicate unknown initial position for(;;) { @@ -60,7 +60,6 @@ int main(void) // 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) { - // Reset system. serial_reset_read_buffer(); // Clear serial read buffer plan_init(); // Clear block buffer and planner variables @@ -72,28 +71,33 @@ int main(void) st_reset(); // Clear stepper subsystem variables. // Sync cleared gcode and planner positions to current system position, which is only - // cleared upon startup, not a reset/abort. If Grbl does not know or can ensure its - // position, a feedback message will be sent back to the user to let them know. Also, - // if position is lost and homing is enabled, the axes motions will be locked, and - // user must either perform the homing cycle '$H' or purge the system locks '$P' to - // resume. + // cleared upon startup, not a reset/abort. sys_sync_current_position(); // Reset system variables. sys.abort = false; sys.execute = 0; - if (sys.state == STATE_ALARM && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { - // Position has either been lost from a critical event or a power cycle reboot. If - // homing is enabled, force user to home to get machine position. Otherwise, let the - // user manage position on their own. - report_feedback_message(MESSAGE_HOMING_ALARM); - } else { - sys.state = STATE_IDLE; - } if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; } - - // Execute user startup script - protocol_execute_startup(); + + // Check for power-up and set system alarm if homing is enabled to force homing cycle + // by setting Grbl's alarm state. Alarm locks out all g-code commands, including the + // startup scripts, but allows access to settings and internal commands. Only a homing + // cycle '$H' or kill alarm locks '$X' will disable the alarm. + // NOTE: The startup script will run after successful completion of the homing cycle, but + // not after disabling the alarm locks. Prevents motion startup blocks from crashing into + // things uncontrollably. Very bad. + #ifdef HOMING_INIT_LOCK + if (sys.state == STATE_INIT && bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; } + #endif + + // Check for and report alarm state after a reset, error, or an initial power up. + if (sys.state == STATE_ALARM) { + report_feedback_message(MESSAGE_ALARM_LOCK); + } else { + // All systems go. Set system to ready and execute startup script. + sys.state = STATE_IDLE; + protocol_execute_startup(); + } } protocol_execute_runtime(); diff --git a/motion_control.c b/motion_control.c index c80e557..6cc000f 100755 --- a/motion_control.c +++ b/motion_control.c @@ -67,10 +67,8 @@ void mc_line(float x, float y, float z, float feed_rate, uint8_t invert_feed_rat if (bit_isfalse(gc.switches,BITFLAG_CHECK_GCODE)) { plan_buffer_line(x, y, z, feed_rate, invert_feed_rate); - // Indicate to the system there is now a planned block in the buffer ready to cycle start. - // NOTE: If homing cycles are enabled, a position lost state will lock out all motions, - // until a homing cycle has been completed. This is a safety feature to help prevent - // the machine from crashing. + // If idle, indicate to the system there is now a planned block in the buffer ready to cycle + // start. Otherwise ignore and continue on. if (!sys.state) { sys.state = STATE_QUEUED; } // Auto-cycle start immediately after planner finishes. Enabled/disabled by grbl settings. During @@ -214,13 +212,10 @@ void mc_dwell(float seconds) void mc_go_home() { sys.state = STATE_HOMING; // Set system state variable - PCICR &= ~(1 << LIMIT_INT); // Disable hard limits pin change interrupt + LIMIT_PCMSK &= ~LIMIT_MASK; // Disable hard limits pin change register for cycle duration limits_go_home(); // Perform homing routine. - if (sys.abort) { - sys.state = STATE_ALARM; // Homing routine did not complete. - return; - } + if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. // The machine should now be homed and machine zero has been located. Upon completion, // reset system position and sync internal position vectors. @@ -237,32 +232,42 @@ void mc_go_home() if (bit_istrue(settings.homing_dir_mask,bit(Y_DIRECTION_BIT))) { y_dir = 1; } if (bit_istrue(settings.homing_dir_mask,bit(Z_DIRECTION_BIT))) { z_dir = 1; } mc_line(x_dir*settings.homing_pulloff, y_dir*settings.homing_pulloff, - z_dir*settings.homing_pulloff, settings.homing_feed_rate, false); + z_dir*settings.homing_pulloff, settings.homing_seek_rate, false); st_cycle_start(); // Move it. Nothing should be in the buffer except this motion. plan_synchronize(); // Make sure the motion completes. // The gcode parser position was circumvented by the pull-off maneuver, so sync position vectors. sys_sync_current_position(); - // If hard limits feature enabled, re-enable hard limits interrupt after homing cycle. - if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { PCICR |= (1 << LIMIT_INT); } + // If hard limits feature enabled, re-enable hard limits pin change register after homing cycle. + if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) { LIMIT_PCMSK |= LIMIT_MASK; } // Finished! } -// Method to immediately kill all motion and set system alarm. Used by system abort, hard limits, -// and upon g-code parser error (when installed). -void mc_alarm() +// Method to ready the system to reset by setting the runtime reset command and killing any +// active processes in the system. This also checks if a system reset is issued while Grbl +// is in a motion state. If so, kills the steppers and sets the system alarm to flag position +// lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by +// runtime abort command and hard limits. So, keep to a minimum. +void mc_reset() { - // Only this function can set the system alarm. This is done to prevent multiple kill calls - // by different processes. - if (bit_isfalse(sys.execute, EXEC_ALARM)) { - // Set system alarm flag to have the main program check for anything wrong with shutting - // down the system. - sys.execute |= EXEC_ALARM; - // Immediately force stepper, spindle, and coolant to stop. - st_go_idle(); + // Only this function can set the system reset. Helps prevent multiple kill calls. + if (bit_isfalse(sys.execute, EXEC_RESET)) { + sys.execute |= EXEC_RESET; + + // Kill spindle and coolant. spindle_stop(); coolant_stop(); + + // Kill steppers only if in any motion state, i.e. cycle, feed hold, homing, or jogging + // NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps + // the steppers enabled by avoiding the go_idle call altogether, unless the motion state is + // violated, by which, all bets are off. + switch (sys.state) { + case STATE_CYCLE: case STATE_HOLD: case STATE_HOMING: // case STATE_JOG: + sys.execute |= EXEC_ALARM; // Execute alarm state. + st_go_idle(); // Execute alarm force kills steppers. Position likely lost. + } } } diff --git a/motion_control.h b/motion_control.h index cd35f70..20ecf08 100755 --- a/motion_control.h +++ b/motion_control.h @@ -43,7 +43,7 @@ void mc_dwell(float seconds); // Perform homing cycle to locate machine zero. Requires limit switches. void mc_go_home(); -// Kills all motion and sets system alarm -void mc_alarm(); +// Performs system reset. If in motion state, kills all motion and sets system alarm. +void mc_reset(); #endif diff --git a/nuts_bolts.c b/nuts_bolts.c index 921ac9a..a244bfb 100755 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -140,7 +140,7 @@ void delay_us(uint32_t us) } } - +// Syncs all internal position vectors to the current system position. void sys_sync_current_position() { plan_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]); diff --git a/nuts_bolts.h b/nuts_bolts.h index 7b00210..1f588ca 100755 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -63,31 +63,29 @@ #define EXEC_FEED_HOLD bit(3) // bitmask 00001000 #define EXEC_RESET bit(4) // bitmask 00010000 #define EXEC_ALARM bit(5) // bitmask 00100000 -// #define bit(6) // bitmask 01000000 +#define EXEC_CRIT_EVENT bit(6) // bitmask 01000000 // #define bit(7) // bitmask 10000000 // Define system state bit map. The state variable primarily tracks the individual functions // of Grbl to manage each without overlapping. It is also used as a messaging flag for // critical events. #define STATE_IDLE 0 // Must be zero. -#define STATE_QUEUED 1 // Indicates buffered blocks, awaiting cycle start. -#define STATE_CYCLE 2 // Cycle is running -#define STATE_HOLD 3 // Executing feed hold -#define STATE_HOMING 4 // Performing homing cycle -#define STATE_JOG 5 // Jogging mode is unique like homing. -#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes and messages position lost -#define STATE_LIMIT 7 // Used to message hard limit triggered. +#define STATE_INIT 1 // Initial power up state. +#define STATE_QUEUED 2 // Indicates buffered blocks, awaiting cycle start. +#define STATE_CYCLE 3 // Cycle is running +#define STATE_HOLD 4 // Executing feed hold +#define STATE_HOMING 5 // Performing homing cycle +#define STATE_ALARM 6 // In alarm state. Locks out all g-code processes. Allows settings access. +// #define STATE_JOG 7 // Jogging mode is unique like homing. // Define global system variables typedef struct { uint8_t abort; // System abort flag. Forces exit back to main loop for reset. uint8_t state; // Tracks the current state of Grbl. volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks. - int32_t position[3]; // Real-time machine (aka home) position vector in steps. + int32_t position[N_AXIS]; // Real-time machine (aka home) position vector in steps. // NOTE: This may need to be a volatile variable, if problems arise. uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings. -// uint8_t feed_hold; // Feed hold flag. Held true during feed hold. Released when ready to resume. -// volatile uint8_t cycle_start; // Cycle start flag. Set by stepper subsystem or main program. } system_t; extern system_t sys; diff --git a/protocol.c b/protocol.c index 8152aa9..9ece210 100755 --- a/protocol.c +++ b/protocol.c @@ -74,8 +74,7 @@ ISR(PINOUT_INT_vect) // Enter only if any pinout pin is actively low. if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) { if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) { - mc_alarm(); - sys.execute |= EXEC_RESET; // Set as true + mc_reset(); } else if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) { sys.execute |= EXEC_FEED_HOLD; } else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) { @@ -93,36 +92,41 @@ ISR(PINOUT_INT_vect) // 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. +// NOTE: The sys.execute variable flags are set by any process, step or serial interrupts, pinouts, +// limit switches, or the main program. 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. Everything has shutdown by either something that has gone wrong or issuing - // the Grbl soft-reset/abort. Check the system states to report any critical error found. - // If critical, disable Grbl by entering an infinite loop until system reset/abort. - // NOTE: The system alarm state is also used to set - if (rt_exec & EXEC_ALARM) { - // Limit switch critical event. Lock out Grbl until reset. - if (sys.state == STATE_LIMIT) { - report_status_message(STATUS_HARD_LIMIT); - sys.state = STATE_ALARM; + // System alarm. Everything has shutdown by something that has gone severely wrong. Report + // the source of the error to the user. If critical, Grbl disables by entering an infinite + // loop until system reset/abort. + if (rt_exec & (EXEC_ALARM | EXEC_CRIT_EVENT)) { + sys.state = STATE_ALARM; // Set system alarm state + + // Critical event. Only hard limit qualifies. Update this as new critical events surface. + if (rt_exec & EXEC_CRIT_EVENT) { + report_alarm_message(ALARM_HARD_LIMIT); report_feedback_message(MESSAGE_CRITICAL_EVENT); - while (bit_isfalse(sys.execute,EXEC_RESET)) { sleep_mode(); } + bit_false(sys.execute,EXEC_RESET); // Disable any existing reset + do { + // Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits + // typically occur while unattended or not paying attention. Gives the user time + // to do what is needed before resetting, like killing the incoming stream. + } while (bit_isfalse(sys.execute,EXEC_RESET)); + + // Standard alarm event. Only abort during motion qualifies. + } else { + // Runtime abort command issued during a cycle, feed hold, or homing cycle. Message the + // user that position may have been lost and set alarm state to enable the alarm lockout + // to indicate the possible severity of the problem. + report_alarm_message(ALARM_ABORT_CYCLE); } - - // Check if a runtime abort command was issued during a cycle. If so, message the user - // that position may have been lost and set alarm state to force re-homing, if enabled. - if (sys.state == STATE_CYCLE) { - report_status_message(STATUS_ABORT_CYCLE); - sys.state = STATE_ALARM; - } - bit_false(sys.execute,EXEC_ALARM); + bit_false(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT)); } - // System abort. Steppers have already been force stopped. + // Execute system abort. if (rt_exec & EXEC_RESET) { sys.abort = true; // Only place this is set true. return; // Nothing else to do but exit. @@ -194,8 +198,10 @@ uint8_t protocol_execute_line(char *line) case 'H' : // Perform homing cycle if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { // Only perform homing if Grbl is idle or lost. - if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) { mc_go_home(); } - else { return(STATUS_IDLE_ERROR); } + if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) { + mc_go_home(); + if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing. + } else { return(STATUS_IDLE_ERROR); } } else { return(STATUS_SETTING_DISABLED); } break; // case 'J' : break; // Jogging methods @@ -224,20 +230,19 @@ uint8_t protocol_execute_line(char *line) // Perform reset when toggling off. Check g-code mode should only work if Grbl // is idle and ready, regardless of homing locks. This is mainly to keep things // simple and consistent. - if ( bit_istrue(gc.switches,helper_var) ) { sys.execute |= EXEC_RESET; } + if ( bit_istrue(gc.switches,helper_var) ) { mc_reset(); } else if (sys.state) { return(STATUS_IDLE_ERROR); } } gc.switches ^= helper_var; if (bit_istrue(gc.switches,helper_var)) { report_feedback_message(MESSAGE_ENABLED); } else { report_feedback_message(MESSAGE_DISABLED); } break; - case 'X' : // Disable homing lock + case 'X' : // Disable alarm lock if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); } if (sys.state == STATE_ALARM) { - report_feedback_message(MESSAGE_HOMING_UNLOCK); + report_feedback_message(MESSAGE_ALARM_UNLOCK); sys.state = STATE_IDLE; - } else { - return(STATUS_SETTING_DISABLED); + // Don't run startup script. Prevents stored moves in startup from causing accidents. } break; case 'N' : // Startup lines. @@ -279,17 +284,7 @@ uint8_t protocol_execute_line(char *line) return(STATUS_OK); // If '$' command makes it to here, then everything's ok. } else { - - // If homing is enabled and position is lost, lock out all g-code commands. - if (sys.state != STATE_ALARM) { - return(gc_execute_line(line)); // Everything else is gcode - // TODO: Install option to set system alarm upon any error code received back from the - // the g-code parser. This is a common safety feature on CNCs to help prevent crashes - // if the g-code doesn't perform as intended. - } else { - return(STATUS_HOMING_LOCK); - } - + return(gc_execute_line(line)); // Everything else is gcode } } diff --git a/report.c b/report.c index f5dd4f6..485d240 100644 --- a/report.c +++ b/report.c @@ -62,8 +62,6 @@ void report_status_message(uint8_t status_code) printPgmString(PSTR("Modal group violation")); break; case STATUS_INVALID_STATEMENT: printPgmString(PSTR("Invalid statement")); break; - case STATUS_HARD_LIMIT: - printPgmString(PSTR("Hard limit. MPos lost?")); break; case STATUS_SETTING_DISABLED: printPgmString(PSTR("Setting disabled")); break; case STATUS_SETTING_VALUE_NEG: @@ -73,37 +71,47 @@ void report_status_message(uint8_t status_code) case STATUS_SETTING_READ_FAIL: printPgmString(PSTR("EEPROM read fail. Using defaults")); break; case STATUS_IDLE_ERROR: - printPgmString(PSTR("Busy or locked")); break; - case STATUS_ABORT_CYCLE: - printPgmString(PSTR("Abort during cycle. MPos lost?")); break; - case STATUS_HOMING_LOCK: - printPgmString(PSTR("Locked until homed")); break; + printPgmString(PSTR("Busy or queued")); break; + case STATUS_ALARM_LOCK: + printPgmString(PSTR("Alarm lock")); break; } printPgmString(PSTR("\r\n")); } } +// Prints alarm messages. +void report_alarm_message(int8_t alarm_code) +{ + printPgmString(PSTR("ALARM: ")); + switch (alarm_code) { + case ALARM_HARD_LIMIT: + printPgmString(PSTR("Hard limit")); break; + case ALARM_ABORT_CYCLE: + printPgmString(PSTR("Abort during cycle")); break; + } + printPgmString(PSTR(". MPos?\r\n")); +} // Prints feedback messages. This serves as a centralized method to provide additional -// user feedback for things that are not of the status message response protocol. These -// are messages such as setup warnings and how to exit alarms. +// user feedback for things that are not of the status/alarm message protocol. These are +// messages such as setup warnings, switch toggling, and how to exit alarms. // NOTE: For interfaces, messages are always placed within brackets. And if silent mode // is installed, the message number codes are less than zero. // TODO: Install silence feedback messages option in settings -void report_feedback_message(int8_t message_code) +void report_feedback_message(uint8_t message_code) { printPgmString(PSTR("[")); switch(message_code) { case MESSAGE_CRITICAL_EVENT: - printPgmString(PSTR("ALARM: Check and reset")); break; - case MESSAGE_HOMING_ALARM: - printPgmString(PSTR("'$H' to home and unlock")); break; + printPgmString(PSTR("Reset to continue")); break; + case MESSAGE_ALARM_LOCK: + printPgmString(PSTR("'$H'|'$X' to unlock")); break; + case MESSAGE_ALARM_UNLOCK: + printPgmString(PSTR("Caution: Unlocked")); break; case MESSAGE_ENABLED: printPgmString(PSTR("Enabled")); break; case MESSAGE_DISABLED: printPgmString(PSTR("Disabled")); break; - case MESSAGE_HOMING_UNLOCK: - printPgmString(PSTR("Unlocked. MPos lost?")); break; } printPgmString(PSTR("]\r\n")); } @@ -127,7 +135,7 @@ void report_grbl_help() { "$S1 (toggle blk del)\r\n" "$S2 (toggle single blk)\r\n" "$S3 (toggle opt stop)\r\n" - "$X (kill homing lock)\r\n" + "$X (kill alarm lock)\r\n" "$H (run homing cycle)\r\n" "~ (cycle start)\r\n" "! (feed hold)\r\n" @@ -287,37 +295,39 @@ void report_startup_line(uint8_t n, char *line) // especially during g-code programs with fast, short line segments and high frequency reports (5-20Hz). void report_realtime_status() { - // **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). Eventually - // to be added are distance to go on block, processed block id, and feed rate. Also a settings bitmask - // for a user to select the desired real-time data. - 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("]")); } - } + // **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). Eventually + // to be added are distance to go on block, processed block id, and feed rate. Also a settings bitmask + // for a user to select the desired real-time data. + 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 work position - printPgmString(PSTR(",WPos:[")); - for (i=0; i<= 2; i++) { - if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { - print_position[i] -= (gc.coord_system[i]+gc.coord_offset[i])*INCH_PER_MM; - } else { - print_position[i] -= gc.coord_system[i]+gc.coord_offset[i]; - } - printFloat(print_position[i]); - if (i < 2) { printPgmString(PSTR(",")); } - else { printPgmString(PSTR("]")); } - } - - printPgmString(PSTR("\r\n")); + // TODO: Add Grbl state feedback, i.e. IDLE, RUN, HOLD, HOME, etc. + + // 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] -= (gc.coord_system[i]+gc.coord_offset[i])*INCH_PER_MM; + } else { + print_position[i] -= gc.coord_system[i]+gc.coord_offset[i]; + } + printFloat(print_position[i]); + if (i < 2) { printPgmString(PSTR(",")); } + else { printPgmString(PSTR("]")); } + } + + printPgmString(PSTR("\r\n")); } diff --git a/report.h b/report.h index b79e1af..885114f 100644 --- a/report.h +++ b/report.h @@ -29,27 +29,32 @@ #define STATUS_ARC_RADIUS_ERROR 4 #define STATUS_MODAL_GROUP_VIOLATION 5 #define STATUS_INVALID_STATEMENT 6 -#define STATUS_HARD_LIMIT 7 -#define STATUS_SETTING_DISABLED 8 -#define STATUS_SETTING_VALUE_NEG 9 -#define STATUS_SETTING_STEP_PULSE_MIN 10 -#define STATUS_SETTING_READ_FAIL 11 -#define STATUS_IDLE_ERROR 12 -#define STATUS_ABORT_CYCLE 13 -#define STATUS_HOMING_LOCK 14 +#define STATUS_SETTING_DISABLED 7 +#define STATUS_SETTING_VALUE_NEG 8 +#define STATUS_SETTING_STEP_PULSE_MIN 9 +#define STATUS_SETTING_READ_FAIL 10 +#define STATUS_IDLE_ERROR 11 +#define STATUS_ALARM_LOCK 12 -// Define Grbl feedback message codes. Less than zero to distinguish message from error. -#define MESSAGE_CRITICAL_EVENT -1 -#define MESSAGE_HOMING_ALARM -2 -#define MESSAGE_ENABLED -3 -#define MESSAGE_DISABLED -4 -#define MESSAGE_HOMING_UNLOCK -5 +// Define Grbl alarm codes. Less than zero to distinguish alarm error from status error. +#define ALARM_HARD_LIMIT -1 +#define ALARM_ABORT_CYCLE -2 + +// Define Grbl feedback message codes. +#define MESSAGE_CRITICAL_EVENT 1 +#define MESSAGE_ALARM_LOCK 2 +#define MESSAGE_ALARM_UNLOCK 3 +#define MESSAGE_ENABLED 4 +#define MESSAGE_DISABLED 5 // Prints system status messages. void report_status_message(uint8_t status_code); +// Prints system alarm messages. +void report_alarm_message(int8_t alarm_code); + // Prints miscellaneous feedback messages. -void report_feedback_message(int8_t message_code); +void report_feedback_message(uint8_t message_code); // Prints welcome message void report_init_message(); diff --git a/serial.c b/serial.c index ee22333..69fa717 100755 --- a/serial.c +++ b/serial.c @@ -49,19 +49,19 @@ volatile uint8_t tx_buffer_tail = 0; } #endif -static void set_baud_rate(long baud) { - uint16_t UBRR0_value = ((F_CPU / 16 + baud / 2) / baud - 1); +void serial_init() +{ + // Set baud rate + #if BAUD_RATE < 57600 + uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1)/2 ; + UCSR0A &= ~(1 << U2X0); // baud doubler off - Only needed on Uno XXX + #else + uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2; + UCSR0A |= (1 << U2X0); // baud doubler on for high baud rates, i.e. 115200 + #endif UBRR0H = UBRR0_value >> 8; UBRR0L = UBRR0_value; -} - -void serial_init(long baud) -{ - set_baud_rate(baud); - - /* baud doubler off - Only needed on Uno XXX */ - UCSR0A &= ~(1 << U2X0); - + // enable rx and tx UCSR0B |= 1<. -*/ - -#include -#include "protocol.h" -#include "report.h" -#include "stepper.h" -#include "nuts_bolts.h" -#include "settings.h" -#include "eeprom.h" - -settings_t settings; - -// Version 4 outdated settings record -typedef struct { - float steps_per_mm[3]; - uint8_t microsteps; - uint8_t pulse_microseconds; - float default_feed_rate; - float default_seek_rate; - uint8_t invert_mask; - float mm_per_arc_segment; - float acceleration; - float junction_deviation; -} settings_v4_t; - - -// Method to store startup lines into EEPROM -void settings_store_startup_line(uint8_t n, char *line) -{ - uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; - memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); -} - -// Method to store coord data parameters into EEPROM -void settings_write_coord_data(uint8_t coord_select, float *coord_data) -{ - uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; - memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); -} - -// Method to store Grbl global settings struct and version number into EEPROM -void write_global_settings() -{ - eeprom_put_char(0, SETTINGS_VERSION); - memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); -} - -// Method to reset Grbl global settings back to defaults. -void settings_reset(bool reset_all) { - // Reset all settings or only the migration settings to the new version. - if (reset_all) { - settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; - settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; - settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; - settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; - settings.default_feed_rate = DEFAULT_FEEDRATE; - settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE; - settings.acceleration = DEFAULT_ACCELERATION; - settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT; - settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK; - settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; - } - // New settings since last version - settings.flags = 0; - if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } - if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; } - if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } - if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } - if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } - settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; - settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; - settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; - settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; - settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; - settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; - settings.decimal_places = DEFAULT_DECIMAL_PLACES; - settings.n_arc_correction = DEFAULT_N_ARC_CORRECTION; - write_global_settings(); -} - -// Reads startup line from EEPROM. Updated pointed line string data. -uint8_t settings_read_startup_line(uint8_t n, char *line) -{ - uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; - if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { - // Reset line with default value - line[0] = 0; - settings_store_startup_line(n, line); - return(false); - } else { - return(true); - } -} - -// Read selected coordinate data from EEPROM. Updates pointed coord_data value. -uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) -{ - uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; - if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { - // Reset with default zero vector - clear_vector_float(coord_data); - settings_write_coord_data(coord_select,coord_data); - return(false); - } else { - return(true); - } -} - -// Reads Grbl global settings struct from EEPROM. -uint8_t read_global_settings() { - // Check version-byte of eeprom - uint8_t version = eeprom_get_char(0); - - if (version == SETTINGS_VERSION) { - // Read settings-record and check checksum - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { - return(false); - } - } else { - if (version <= 4) { - // Migrate from settings version 4 to current version. - if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) { - return(false); - } - settings_reset(false); // Old settings ok. Write new settings only. - } else { - return(false); - } - } - return(true); -} - - -// A helper method to set settings from command line -uint8_t settings_store_global_setting(int parameter, float value) { - switch(parameter) { - case 0: case 1: case 2: - if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } - settings.steps_per_mm[parameter] = value; break; - case 3: - if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } - settings.pulse_microseconds = round(value); break; - case 4: settings.default_feed_rate = value; break; - case 5: settings.default_seek_rate = value; break; - case 6: settings.invert_mask = trunc(value); break; - case 7: settings.stepper_idle_lock_time = round(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.mm_per_arc_segment = value; break; - case 11: settings.n_arc_correction = round(value); break; - case 12: settings.decimal_places = round(value); break; - case 13: - if (value) { settings.flags |= BITFLAG_REPORT_INCHES; } - else { settings.flags &= ~BITFLAG_REPORT_INCHES; } - break; - case 14: // Reboot to ensure change - if (value) { settings.flags |= BITFLAG_AUTO_START; } - else { settings.flags &= ~BITFLAG_AUTO_START; } - break; - case 15: // Reboot to ensure change - if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } - else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } - break; - case 16: // Reboot to ensure change - if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } - else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } - break; - case 17: - if (value) { - settings.flags |= BITFLAG_HOMING_ENABLE; - sys.state = STATE_ALARM; - report_feedback_message(MESSAGE_HOMING_ALARM); - } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } - break; - case 18: settings.homing_dir_mask = trunc(value); break; - case 19: settings.homing_feed_rate = value; break; - case 20: settings.homing_seek_rate = value; break; - case 21: settings.homing_debounce_delay = round(value); break; - case 22: settings.homing_pulloff = value; break; - default: - return(STATUS_INVALID_STATEMENT); - } - write_global_settings(); - return(STATUS_OK); -} - -// Initialize the config subsystem -void settings_init() { - if(!read_global_settings()) { - report_status_message(STATUS_SETTING_READ_FAIL); - settings_reset(true); - report_grbl_settings(); - } - // Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing. - float coord_data[N_AXIS]; - uint8_t i; - for (i=0; i<=SETTING_INDEX_NCOORD; i++) { - if (!settings_read_coord_data(i, coord_data)) { - report_status_message(STATUS_SETTING_READ_FAIL); - } - } - // NOTE: Startup lines are handled and called by main.c at the end of initialization. -} +/* + settings.c - eeprom configuration handling + Part of Grbl + + Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2011-2012 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include +#include "protocol.h" +#include "report.h" +#include "stepper.h" +#include "nuts_bolts.h" +#include "settings.h" +#include "eeprom.h" +#include "limits.h" + +settings_t settings; + +// Version 4 outdated settings record +typedef struct { + float steps_per_mm[3]; + uint8_t microsteps; + uint8_t pulse_microseconds; + float default_feed_rate; + float default_seek_rate; + uint8_t invert_mask; + float mm_per_arc_segment; + float acceleration; + float junction_deviation; +} settings_v4_t; + + +// Method to store startup lines into EEPROM +void settings_store_startup_line(uint8_t n, char *line) +{ + uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; + memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); +} + +// Method to store coord data parameters into EEPROM +void settings_write_coord_data(uint8_t coord_select, float *coord_data) +{ + uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; + memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); +} + +// Method to store Grbl global settings struct and version number into EEPROM +void write_global_settings() +{ + eeprom_put_char(0, SETTINGS_VERSION); + memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); +} + +// Method to reset Grbl global settings back to defaults. +void settings_reset(bool reset_all) { + // Reset all settings or only the migration settings to the new version. + if (reset_all) { + settings.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM; + settings.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM; + settings.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM; + settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; + settings.default_feed_rate = DEFAULT_FEEDRATE; + settings.default_seek_rate = DEFAULT_RAPID_FEEDRATE; + settings.acceleration = DEFAULT_ACCELERATION; + settings.mm_per_arc_segment = DEFAULT_MM_PER_ARC_SEGMENT; + settings.invert_mask = DEFAULT_STEPPING_INVERT_MASK; + settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; + } + // New settings since last version + settings.flags = 0; + if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } + if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; } + if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } + if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } + if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } + settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; + settings.homing_feed_rate = DEFAULT_HOMING_FEEDRATE; + settings.homing_seek_rate = DEFAULT_HOMING_RAPID_FEEDRATE; + settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; + settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; + settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; + settings.decimal_places = DEFAULT_DECIMAL_PLACES; + settings.n_arc_correction = DEFAULT_N_ARC_CORRECTION; + write_global_settings(); +} + +// Reads startup line from EEPROM. Updated pointed line string data. +uint8_t settings_read_startup_line(uint8_t n, char *line) +{ + uint16_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; + if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { + // Reset line with default value + line[0] = 0; + settings_store_startup_line(n, line); + return(false); + } else { + return(true); + } +} + +// Read selected coordinate data from EEPROM. Updates pointed coord_data value. +uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) +{ + uint16_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; + if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { + // Reset with default zero vector + clear_vector_float(coord_data); + settings_write_coord_data(coord_select,coord_data); + return(false); + } else { + return(true); + } +} + +// Reads Grbl global settings struct from EEPROM. +uint8_t read_global_settings() { + // Check version-byte of eeprom + uint8_t version = eeprom_get_char(0); + + if (version == SETTINGS_VERSION) { + // Read settings-record and check checksum + if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { + return(false); + } + } else { + if (version <= 4) { + // Migrate from settings version 4 to current version. + if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v4_t)))) { + return(false); + } + settings_reset(false); // Old settings ok. Write new settings only. + } else { + return(false); + } + } + return(true); +} + + +// A helper method to set settings from command line +uint8_t settings_store_global_setting(int parameter, float value) { + switch(parameter) { + case 0: case 1: case 2: + if (value <= 0.0) { return(STATUS_SETTING_VALUE_NEG); } + settings.steps_per_mm[parameter] = value; break; + case 3: + if (value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } + settings.pulse_microseconds = round(value); break; + case 4: settings.default_feed_rate = value; break; + case 5: settings.default_seek_rate = value; break; + case 6: settings.invert_mask = trunc(value); break; + case 7: settings.stepper_idle_lock_time = round(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.mm_per_arc_segment = value; break; + case 11: settings.n_arc_correction = round(value); break; + case 12: settings.decimal_places = round(value); break; + case 13: + if (value) { settings.flags |= BITFLAG_REPORT_INCHES; } + else { settings.flags &= ~BITFLAG_REPORT_INCHES; } + break; + case 14: // Reset to ensure change. Immediate re-init may cause problems. + if (value) { settings.flags |= BITFLAG_AUTO_START; } + else { settings.flags &= ~BITFLAG_AUTO_START; } + break; + case 15: // Reset to ensure change. Immediate re-init may cause problems. + if (value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } + else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } + break; + case 16: + if (value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } + else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } + limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later. + break; + case 17: + if (value) { settings.flags |= BITFLAG_HOMING_ENABLE; } + else { settings.flags &= ~BITFLAG_HOMING_ENABLE; } + break; + case 18: settings.homing_dir_mask = trunc(value); break; + case 19: settings.homing_feed_rate = value; break; + case 20: settings.homing_seek_rate = value; break; + case 21: settings.homing_debounce_delay = round(value); break; + case 22: settings.homing_pulloff = value; break; + default: + return(STATUS_INVALID_STATEMENT); + } + write_global_settings(); + return(STATUS_OK); +} + +// Initialize the config subsystem +void settings_init() { + if(!read_global_settings()) { + report_status_message(STATUS_SETTING_READ_FAIL); + settings_reset(true); + report_grbl_settings(); + } + // Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing. + float coord_data[N_AXIS]; + uint8_t i; + for (i=0; i<=SETTING_INDEX_NCOORD; i++) { + if (!settings_read_coord_data(i, coord_data)) { + report_status_message(STATUS_SETTING_READ_FAIL); + } + } + // NOTE: Startup lines are handled and called by main.c at the end of initialization. +}