diff --git a/Makefile b/Makefile
index 8ff9359..ea1ea61 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@ CLOCK = 16000000
PROGRAMMER ?= -c avrisp2 -P usb
OBJECTS = main.o motion_control.o gcode.o spindle_control.o coolant_control.o serial.o \
protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o limits.o \
- print.o report.o
+ print.o report.o system.o
# FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m
FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m
# update that line with this when programmer is back up:
diff --git a/config.h b/config.h
index 78d0ecf..dbf37e3 100644
--- a/config.h
+++ b/config.h
@@ -79,6 +79,11 @@
// parser state depending on user preferences.
#define N_STARTUP_LINE 2 // Integer (1-3)
+// Enables a second coolant control pin via the mist coolant g-code command M7 on the Arduino Uno
+// analog pin 5. Only use this option if you require a second control pin.
+// NOTE: The M8 flood coolant control pin on analog pin 4 will still be functional regardless.
+// #define ENABLE_M7 // Mist coolant disabled by default. See config.h to enable/disable.
+
// ---------------------------------------------------------------------------------------
// ADVANCED CONFIGURATION OPTIONS:
@@ -103,7 +108,14 @@
// NOTE: IMPORTANT for Arduino Unos! When enabled, the Z-limit pin D11 and spindle enable pin D12 switch!
// The hardware PWM output on pin D11 is required for variable spindle output voltages.
// #define VARIABLE_SPINDLE // Default disabled. Uncomment to enable.
-// #define SPINDLE_MAX_RPM 1000 // Max spindle RPM. This value is equal to 100% Duty Cycle on the PWM.
+
+// Use by the variable spindle output only. These parameters set the maximum and minimum spindle speed
+// "S" g-code values to correspond to the maximum and minimum pin voltages. There are 256 discrete and
+// equally divided voltage bins between the maximum and minimum spindle speeds. So for a 5V pin, 1000
+// max rpm, and 250 min rpm, the spindle output voltage would be set for the following "S" commands:
+// "S1000" @ 5V, "S250" @ 0.02V, and "S625" @ 2.5V (mid-range). The pin outputs 0V when disabled.
+#define SPINDLE_MAX_RPM 1000.0 // Max spindle RPM. This value is equal to 100% duty cycle on the PWM.
+#define SPINDLE_MIN_RPM 0.0 // Min spindle RPM. This value is equal to (1/256) duty cycle on the PWM.
// Minimum planner junction speed. Sets the default minimum junction speed the planner plans to at
// every buffer block junction, except for starting from rest and end of the buffer, which are always
@@ -182,12 +194,13 @@
// case, please report any successes to grbl administrators!
// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.
-// A simple software debouncing feature for hard limit switches. When enabled, the interrupt monitoring
-// the hard limit switch pins will enable the Arduino's watchdog timer to re-check the limit pin state
-// after a delay of about 32msec. This can help with CNC machines with problematic false triggering of
-// their hard limit switches, but it WILL NOT fix issues with electrical interference on the signal
-// cables from external sources. It's recommended to first use shielded signal cables that are grounded
-// (old USB/computer cables work well) and wire in a low-pass circuit into each limit pin.
+// A simple software debouncing feature for hard limit switches. When enabled, the interrupt
+// monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check
+// the limit pin state after a delay of about 32msec. This can help with CNC machines with
+// problematic false triggering of their hard limit switches, but it WILL NOT fix issues with
+// electrical interference on the signal cables from external sources. It's recommended to first
+// use shielded signal cables with their shielding connected to ground (old USB/computer cables
+// work well and are cheap to find) and wire in a low-pass circuit into each limit pin.
// #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable.
// ---------------------------------------------------------------------------------------
diff --git a/coolant_control.c b/coolant_control.c
index b849a78..593fc7c 100644
--- a/coolant_control.c
+++ b/coolant_control.c
@@ -18,19 +18,13 @@
along with Grbl. If not, see .
*/
+#include "system.h"
#include "coolant_control.h"
-#include "settings.h"
-#include "config.h"
#include "planner.h"
-#include
-
-static uint8_t current_coolant_mode;
-
void coolant_init()
{
- current_coolant_mode = COOLANT_DISABLE;
COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT);
#ifdef ENABLE_M7
COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT);
@@ -50,20 +44,16 @@ void coolant_stop()
void coolant_run(uint8_t mode)
{
- if (mode != current_coolant_mode)
- {
- plan_synchronize(); // Ensure coolant turns on when specified in program.
- if (mode == COOLANT_FLOOD_ENABLE) {
- COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
-
+ plan_synchronize(); // Ensure coolant turns on when specified in program.
+ if (mode == COOLANT_FLOOD_ENABLE) {
+ COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
+
#ifdef ENABLE_M7
} else if (mode == COOLANT_MIST_ENABLE) {
COOLANT_MIST_PORT |= (1 << COOLANT_MIST_BIT);
#endif
- } else {
- coolant_stop();
- }
- current_coolant_mode = mode;
+ } else {
+ coolant_stop();
}
}
diff --git a/coolant_control.h b/coolant_control.h
index 38f3b43..c19a673 100644
--- a/coolant_control.h
+++ b/coolant_control.h
@@ -21,12 +21,12 @@
#ifndef coolant_control_h
#define coolant_control_h
-#include
#define COOLANT_MIST_ENABLE 2
#define COOLANT_FLOOD_ENABLE 1
#define COOLANT_DISABLE 0 // Must be zero.
+
void coolant_init();
void coolant_stop();
void coolant_run(uint8_t mode);
diff --git a/cpu_map.h b/cpu_map.h
index bbc006b..39a3bb4 100644
--- a/cpu_map.h
+++ b/cpu_map.h
@@ -36,9 +36,6 @@
#define SERIAL_RX USART_RX_vect
#define SERIAL_UDRE USART_UDRE_vect
- // Start of PWM & Stepper Enabled Spindle
- // #define VARIABLE_SPINDLE // comment this out to disable PWM & Stepper on the spindle
-
// Define step pulse output pins. NOTE: All step bit pins must be on the same port.
#define STEPPING_DDR DDRD
#define STEPPING_PORT PORTD
diff --git a/defaults.h b/defaults.h
index cbee821..a1de3fe 100644
--- a/defaults.h
+++ b/defaults.h
@@ -47,7 +47,7 @@
#define DEFAULT_DIRECTION_INVERT_MASK ((1<
-#include "nuts_bolts.h"
-#include
+#include "system.h"
#include "settings.h"
+#include "gcode.h"
+#include "planner.h"
#include "motion_control.h"
#include "spindle_control.h"
#include "coolant_control.h"
-#include "errno.h"
-#include "protocol.h"
#include "report.h"
// Declare gc extern struct
@@ -186,9 +183,9 @@ uint8_t gc_execute_line(char *line)
case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause
case 1: break; // Optional stop not supported. Ignore.
case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset
- case 3: gc.spindle_direction = 1; break;
- case 4: gc.spindle_direction = -1; break;
- case 5: gc.spindle_direction = 0; break;
+ case 3: gc.spindle_direction = SPINDLE_ENABLE_CW; break;
+ case 4: gc.spindle_direction = SPINDLE_ENABLE_CCW; break;
+ case 5: gc.spindle_direction = SPINDLE_DISABLE; break;
#ifdef ENABLE_M7
case 7: gc.coolant_mode = COOLANT_MIST_ENABLE; break;
#endif
@@ -238,7 +235,7 @@ uint8_t gc_execute_line(char *line)
case 'R': gc.arc_radius = to_millimeters(value); break;
case 'S':
if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative
- gc.spindle_speed = value;
+ gc.spindle_speed = value;
break;
case 'T':
if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative
diff --git a/gcode.h b/gcode.h
index aebc0ba..78d6128 100644
--- a/gcode.h
+++ b/gcode.h
@@ -21,8 +21,8 @@
#ifndef gcode_h
#define gcode_h
-#include
-#include "nuts_bolts.h"
+
+#include "system.h"
// Define modal group internal numbers for checking multiple command violations and tracking the
// type of command that is called in the block. A modal group is a group of g-code commands that are
@@ -70,12 +70,12 @@ typedef struct {
uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21}
uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91}
uint8_t program_flow; // {M0, M1, M2, M30}
- int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5}
- uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable {M8, M9}
+ uint8_t coolant_mode; // 0 = Disable, 1 = Flood Enable, 2 = Mist Enable {M8, M9}
+ int8_t spindle_direction; // 1 = CW, 2 = CCW, 0 = Stop {M3, M4, M5}
+ float spindle_speed; // RPM
float feed_rate; // Millimeters/min
float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code
uint8_t tool;
- uint16_t spindle_speed; // RPM
uint8_t plane_axis_0,
plane_axis_1,
plane_axis_2; // The axes of the selected plane
diff --git a/limits.c b/limits.c
index 0abbc7d..169ca71 100644
--- a/limits.c
+++ b/limits.c
@@ -19,18 +19,12 @@
along with Grbl. If not, see .
*/
-#include
-#include
-#include
-#include
-#include "stepper.h"
+#include "system.h"
#include "settings.h"
-#include "nuts_bolts.h"
-#include "config.h"
-#include "spindle_control.h"
-#include "motion_control.h"
-#include "planner.h"
#include "protocol.h"
+#include "planner.h"
+#include "stepper.h"
+#include "motion_control.h"
#include "limits.h"
#include "report.h"
@@ -42,7 +36,7 @@ void limits_init()
if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) {
LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down.
} else {
- LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
+ LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation.
}
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) {
@@ -88,11 +82,9 @@ void limits_disable()
// 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)) {
- #ifndef LIMIT_SWITCHES_ACTIVE_HIGH
- if ((LIMIT_PIN & LIMIT_MASK) ^ LIMIT_MASK) {
- #else
- if (LIMIT_PIN & LIMIT_MASK) {
- #endif
+ uint8_t bits = LIMIT_PIN;
+ if (bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { bits ^= LIMIT_MASK; }
+ if (bits & LIMIT_MASK) {
mc_reset(); // Initiate system kill.
sys.execute |= EXEC_CRIT_EVENT; // Indicate hard limit critical event
}
@@ -151,9 +143,9 @@ void limits_go_home(uint8_t cycle_mask, bool approach, float homing_rate)
target[i] = 0.0;
}
}
- if (bit_istrue(settings.homing_dir_mask,(1<.
*/
-/* A big thanks to Alden Hart of Synthetos, supplier of grblshield and TinyG, who has
- been integral throughout the development of the higher level details of Grbl, as well
- as being a consistent sounding board for the future of accessible and free CNC. */
-
-#include
-#include "config.h"
+#include "system.h"
+#include "serial.h"
+#include "settings.h"
+#include "protocol.h"
+#include "gcode.h"
#include "planner.h"
-#include "nuts_bolts.h"
#include "stepper.h"
#include "spindle_control.h"
#include "coolant_control.h"
#include "motion_control.h"
-#include "gcode.h"
-#include "protocol.h"
#include "limits.h"
#include "report.h"
-#include "settings.h"
-#include "serial.h"
+
// Declare system global variable structure
system_t sys;
+
int main(void)
{
- // Initialize system
- serial_init(); // Setup serial baud rate and interrupts
+ // Initialize system upon power-up.
+ 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
+ stepper_init(); // Configure stepper pins and interrupt timers
+ system_init(); // Configure pinout pins and pin-change interrupt
+ sei();
memset(&sys, 0, sizeof(sys)); // Clear all system variables
sys.abort = true; // Set abort to complete initialization
@@ -65,49 +62,29 @@ int main(void)
for(;;) {
- // 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) {
- // Reset system.
- serial_reset_read_buffer(); // Clear serial read buffer
- gc_init(); // Set g-code parser to default state
- protocol_init(); // Clear incoming line data and execute startup lines
- spindle_init();
- coolant_init();
- limits_init();
- plan_reset(); // Clear block buffer and planner variables
- st_reset(); // Clear stepper subsystem variables.
+ // Reset the system primary functionality.
+ serial_reset_read_buffer(); // Clear serial read buffer
+ gc_init(); // Set g-code parser to default state
+ spindle_init();
+ coolant_init();
+ limits_init();
+ plan_reset(); // Clear block buffer and planner variables
+ 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.
- plan_sync_position();
- gc_sync_position();
+ // Sync cleared gcode and planner positions to current system position.
+ plan_sync_position();
+ gc_sync_position();
- // Reset system variables.
- sys.abort = false;
- sys.execute = 0;
- if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; }
- else { sys.auto_start = false; }
-
- // 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; // Clear all state flags.
- protocol_execute_startup();
- }
- }
-
- protocol_execute_runtime();
-
- // When the serial protocol returns, there are no more characters in the serial read buffer to
- // be processed and executed. This indicates that individual commands are being issued or
- // streaming is finished. In either case, auto-cycle start, if enabled, any queued moves.
- mc_auto_cycle_start();
- protocol_process(); // ... process the serial protocol
+ // Reset system variables.
+ sys.abort = false;
+ sys.execute = 0;
+ if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; }
+ else { sys.auto_start = false; }
+
+ // Start main loop. Processes inputs and executes them.
+ // NOTE: Upon a system abort, the main loop returns and re-initializes the system.
+ protocol_process();
}
- return 0; /* never reached */
+ return 0; /* Never reached */
}
diff --git a/motion_control.c b/motion_control.c
index e30274a..ee1798b 100644
--- a/motion_control.c
+++ b/motion_control.c
@@ -20,21 +20,17 @@
along with Grbl. If not, see .
*/
-#include
-#include
-#include
-#include
+#include "system.h"
#include "settings.h"
-#include "config.h"
+#include "protocol.h"
#include "gcode.h"
+#include "planner.h"
+#include "stepper.h"
#include "motion_control.h"
#include "spindle_control.h"
#include "coolant_control.h"
-#include "nuts_bolts.h"
-#include "stepper.h"
-#include "planner.h"
#include "limits.h"
-#include "protocol.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
@@ -112,8 +108,8 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
// Computes: mm_per_arc_segment = sqrt(4*arc_tolerance*(2*radius-arc_tolerance)),
// segments = millimeters_of_travel/mm_per_arc_segment
float millimeters_of_travel = hypot(angular_travel*radius, fabs(linear_travel));
- uint16_t segments = floor(millimeters_of_travel/
- sqrt(4*settings.arc_tolerance*(2*radius - settings.arc_tolerance)) );
+ uint16_t segments = floor(0.5*millimeters_of_travel/
+ sqrt(settings.arc_tolerance*(2*radius - settings.arc_tolerance)) );
if (segments) {
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated
@@ -123,7 +119,7 @@ void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8
float theta_per_segment = angular_travel/segments;
float linear_per_segment = linear_travel/segments;
-
+
/* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector,
and phi is the angle of rotation. Solution approach by Jens Geisler.
r_T = [cos(phi) -sin(phi);
diff --git a/motion_control.h b/motion_control.h
index a5e0371..b17e168 100644
--- a/motion_control.h
+++ b/motion_control.h
@@ -22,8 +22,6 @@
#ifndef motion_control_h
#define motion_control_h
-#include
-#include "planner.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
diff --git a/nuts_bolts.c b/nuts_bolts.c
index 6914052..57150ef 100644
--- a/nuts_bolts.c
+++ b/nuts_bolts.c
@@ -19,10 +19,8 @@
along with Grbl. If not, see .
*/
-#include
-#include "nuts_bolts.h"
-#include "gcode.h"
-#include "planner.h"
+#include "system.h"
+
#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
diff --git a/nuts_bolts.h b/nuts_bolts.h
index 5140575..f9f08dd 100644
--- a/nuts_bolts.h
+++ b/nuts_bolts.h
@@ -22,13 +22,6 @@
#ifndef nuts_bolts_h
#define nuts_bolts_h
-#include
-#include
-#include
-#include "config.h"
-#include "defaults.h"
-#include "cpu_map.h"
-
#define false 0
#define true 1
@@ -57,44 +50,6 @@
#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 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 EXEC_ALARM bit(5) // bitmask 00100000
-#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. No flags.
-#define STATE_QUEUED bit(0) // Indicates buffered blocks, awaiting cycle start.
-#define STATE_CYCLE bit(1) // Cycle is running
-#define STATE_HOLD bit(2) // Executing feed hold
-#define STATE_HOMING bit(3) // Performing homing cycle
-#define STATE_ALARM bit(4) // In alarm state. Locks out all g-code processes. Allows settings access.
-#define STATE_CHECK_MODE bit(5) // G-code check mode. Locks out planner and motion only.
-// #define STATE_JOG bit(6) // 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.
- uint8_t homing_axis_lock;
- 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.
-} 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 float_ptr is
// a pointer to the result variable. Returns true when it succeeds
diff --git a/planner.c b/planner.c
index 143837a..9e435c7 100644
--- a/planner.c
+++ b/planner.c
@@ -22,14 +22,12 @@
/* The ring buffer implementation gleaned from the wiring_serial library by David A. Mellis. */
-#include
-#include
+#include "system.h"
#include "planner.h"
-#include "nuts_bolts.h"
+#include "protocol.h"
#include "stepper.h"
#include "settings.h"
-#include "config.h"
-#include "protocol.h"
+
#define SOME_LARGE_VALUE 1.0E+38 // Used by rapids and acceleration maximization calculations. Just needs
// to be larger than any feasible (mm/min)^2 or mm/sec^2 value.
diff --git a/planner.h b/planner.h
index 1cc4309..99298ed 100644
--- a/planner.h
+++ b/planner.h
@@ -21,7 +21,8 @@
#ifndef planner_h
#define planner_h
-#include "nuts_bolts.h"
+
+#include "system.h"
// The number of linear motions that can be in the plan at any give time
#ifndef BLOCK_BUFFER_SIZE
diff --git a/print.c b/print.c
index 0e2f9b8..03e261d 100644
--- a/print.c
+++ b/print.c
@@ -22,12 +22,11 @@
/* This code was initially inspired by the wiring_serial module by David A. Mellis which
used to be a part of the Arduino project. */
-
-#include
-#include "config.h"
+#include "system.h"
#include "serial.h"
#include "settings.h"
+
void printString(const char *s)
{
while (*s)
diff --git a/protocol.c b/protocol.c
index 54c959f..ade1df0 100644
--- a/protocol.c
+++ b/protocol.c
@@ -1,5 +1,5 @@
/*
- protocol.c - the serial protocol master control unit
+ protocol.c - controls Grbl execution procedures
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
@@ -19,78 +19,19 @@
along with Grbl. If not, see .
*/
-#include
-#include
+#include "system.h"
+#include "serial.h"
+#include "settings.h"
#include "protocol.h"
#include "gcode.h"
-#include "serial.h"
-#include "print.h"
-#include "settings.h"
-#include "config.h"
-#include "nuts_bolts.h"
#include "stepper.h"
-#include "report.h"
#include "motion_control.h"
+#include "report.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.
-static void protocol_reset_line_buffer()
-{
- char_counter = 0;
- iscomment = false;
-}
-
-
-void protocol_init()
-{
- protocol_reset_line_buffer(); // Reset line input
- report_init_message(); // Welcome message
-
- PINOUT_DDR &= ~(PINOUT_MASK); // Set as input pins
- PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation.
- PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt
- PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt
-}
-
-
-// Executes user startup script, if stored.
-void protocol_execute_startup()
-{
- uint8_t n;
- for (n=0; n < N_STARTUP_LINE; n++) {
- if (!(settings_read_startup_line(n, line))) {
- report_status_message(STATUS_SETTING_READ_FAIL);
- } else {
- if (line[0] != 0) {
- printString(line); // Echo startup line to indicate execution.
- report_status_message(gc_execute_line(line));
- }
- }
- }
-}
-
-
-// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets
-// only the runtime command execute variable to have the main program execute these when
-// its ready. This works exactly like the character-based runtime commands when picked off
-// directly from the incoming serial data stream.
-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_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))) {
- sys.execute |= EXEC_CYCLE_START;
- }
- }
-}
-
// Executes run-time commands, when required. This is called from various check points in the main
// 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.
@@ -181,188 +122,111 @@ void protocol_execute_runtime()
// 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] == '$') {
- uint8_t char_counter = 1;
- uint8_t helper_var = 0; // Helper variable
- float parameter, value;
- switch( line[char_counter] ) {
- case '#' : // Print gcode parameters
- if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
- else { report_gcode_parameters(); }
- break;
- case 'G' : // Prints gcode parser state
- if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
- else { report_gcode_modes(); }
- 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.
- default :
- // Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing)
- if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); }
- switch( line[char_counter] ) {
- case 0 : report_grbl_help(); break;
- case '$' : // Prints Grbl settings
- if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
- else { report_grbl_settings(); }
- break;
- case 'C' : // Set check g-code mode
- if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
- // Perform reset when toggling off. Check g-code mode should only work if Grbl
- // is idle and ready, regardless of alarm locks. This is mainly to keep things
- // simple and consistent.
- if ( sys.state == STATE_CHECK_MODE ) {
- mc_reset();
- report_feedback_message(MESSAGE_DISABLED);
- } else {
- if (sys.state) { return(STATUS_IDLE_ERROR); }
- sys.state = STATE_CHECK_MODE;
- report_feedback_message(MESSAGE_ENABLED);
- }
- break;
- case 'X' : // Disable alarm lock
- if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
- if (sys.state == STATE_ALARM) {
- report_feedback_message(MESSAGE_ALARM_UNLOCK);
- sys.state = STATE_IDLE;
- // Don't run startup script. Prevents stored moves in startup from causing accidents.
- }
- break;
- case 'H' : // Perform homing cycle
- if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
- // Only perform homing if Grbl is idle or alarm.
- mc_homing_cycle();
- if (!sys.abort) { protocol_execute_startup(); } // Execute startup scripts after successful homing.
- } else { return(STATUS_SETTING_DISABLED); }
- break;
- case 'I' : // Print or store build info.
- if ( line[++char_counter] == 0 ) {
- if (!(settings_read_build_info(line))) {
- report_status_message(STATUS_SETTING_READ_FAIL);
- } else {
- report_build_info(line);
- }
- } else { // Store startup line
- if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); }
- helper_var = char_counter; // Set helper variable as counter to start of user info line.
- do {
- line[char_counter-helper_var] = line[char_counter];
- } while (line[char_counter++] != 0);
- settings_store_build_info(line);
- }
- break;
- case 'N' : // Startup lines.
- if ( line[++char_counter] == 0 ) { // Print startup lines
- for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) {
- if (!(settings_read_startup_line(helper_var, line))) {
- report_status_message(STATUS_SETTING_READ_FAIL);
- } else {
- report_startup_line(helper_var,line);
- }
- }
- break;
- } else { // Store startup line
- helper_var = true; // Set helper_var to flag storing method.
- // No break. Continues into default: to read remaining command characters.
- }
- default : // Storing setting methods
- if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); }
- if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); }
- if (helper_var) { // Store startup line
- // Prepare sending gcode block to gcode parser by shifting all characters
- helper_var = char_counter; // Set helper variable as counter to start of gcode block
- do {
- line[char_counter-helper_var] = line[char_counter];
- } while (line[char_counter++] != 0);
- // Execute gcode block to ensure block is valid.
- helper_var = gc_execute_line(line); // Set helper_var to returned status code.
- if (helper_var) { return(helper_var); }
- else {
- helper_var = trunc(parameter); // Set helper_var to int value of parameter
- settings_store_startup_line(helper_var,line);
- }
- } else { // Store global setting.
- 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));
- }
- }
- }
- return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
+// incoming streaming g-code blocks, this also directs and executes Grbl internal commands,
+// such as settings, initiating the homing cycle, and toggling switch states.
+// TODO: Eventually re-organize this function to more cleanly organize order of operations,
+// which will hopefully reduce some of the current spaghetti logic and dynamic memory usage.
+static void protocol_execute_line(char *line)
+{
+ protocol_execute_runtime(); // Runtime command check point.
+ if (sys.abort) { return; } // Bail to calling function upon system abort
+ uint8_t status;
+ if (line[0] == 0) {
+ // Empty or comment line. Send status message for syncing purposes.
+ status = STATUS_OK;
+
+ } else if (line[0] == '$') {
+ // Grbl '$' system command
+ status = system_execute_line(line);
+
} else {
- return(gc_execute_line(line)); // Everything else is gcode
+ // Everything else is gcode. Send to g-code parser!
+ // TODO: Separate the parsing from the g-code execution. Need to re-write the parser
+ // completely to do this. First parse the line completely, checking for modal group
+ // errors and storing all of the g-code words. Then, send the stored g-code words to
+ // a separate g-code executor. This will be more in-line with actual g-code protocol.
+ status = gc_execute_line(line);
+
}
+
+ report_status_message(status);
}
-// 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()
{
+ // ------------------------------------------------------------
+ // Complete initialization procedures upon a power-up or reset.
+ // ------------------------------------------------------------
+
+ // Print welcome message
+ report_init_message();
+
+ // 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!
+ sys.state = STATE_IDLE; // Set system to ready. Clear all state flags.
+ system_execute_startup(line); // Execute startup script.
+ }
+
+ // ------------------------------------------------------------------------------
+ // Main loop! Upon a system abort, this exits back to main() to reset the system.
+ // ------------------------------------------------------------------------------
+
+ uint8_t iscomment = false;
+ uint8_t char_counter = 0;
uint8_t c;
- while((c = serial_read()) != SERIAL_NO_DATA) {
- if ((c == '\n') || (c == '\r')) { // End of line reached
+ for (;;) {
- // Runtime command check point before executing line. Prevent any furthur line executions.
- // 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 (char_counter > 0) {// Line is complete. Then execute!
- line[char_counter] = 0; // Terminate string
- report_status_message(protocol_execute_line(line));
- } else {
- // Empty or comment line. Skip block.
- report_status_message(STATUS_OK); // Send status message for syncing purposes.
- }
- protocol_reset_line_buffer();
-
- } else {
- if (iscomment) {
- // Throw away all comment characters
- if (c == ')') {
- // End of comment. Resume line.
- iscomment = false;
- }
+ // Process one line of incoming serial data, as the data becomes available. Performs an
+ // initial filtering by removing spaces and comments and capitalizing all letters.
+ while((c = serial_read()) != SERIAL_NO_DATA) {
+ if ((c == '\n') || (c == '\r')) { // End of line reached
+ line[char_counter] = 0; // Set string termination character.
+ protocol_execute_line(line); // Line is complete. Execute it!
+ iscomment = false;
+ char_counter = 0;
} else {
- if (c <= ' ') {
- // Throw away whitepace and control characters
- } else if (c == '/') {
- // Block delete not supported. Ignore character.
- } else if (c == '(') {
- // Enable comments flag and ignore all characters until ')' or EOL.
- iscomment = true;
- } else if (char_counter >= LINE_BUFFER_SIZE-1) {
- // Detect line buffer overflow. Report error and reset line buffer.
- report_status_message(STATUS_OVERFLOW);
- protocol_reset_line_buffer();
- } else if (c >= 'a' && c <= 'z') { // Upcase lowercase
- line[char_counter++] = c-'a'+'A';
+ if (iscomment) {
+ // Throw away all comment characters
+ if (c == ')') {
+ // End of comment. Resume line.
+ iscomment = false;
+ }
} else {
- line[char_counter++] = c;
+ if (c <= ' ') {
+ // Throw away whitepace and control characters
+ } else if (c == '/') {
+ // Block delete not supported. Ignore character.
+ } else if (c == '(') {
+ // Enable comments flag and ignore all characters until ')' or EOL.
+ iscomment = true;
+ } else if (char_counter >= LINE_BUFFER_SIZE-1) {
+ // Detect line buffer overflow. Report error and reset line buffer.
+ report_status_message(STATUS_OVERFLOW);
+ iscomment = false;
+ char_counter = 0;
+ } else if (c >= 'a' && c <= 'z') { // Upcase lowercase
+ line[char_counter++] = c-'a'+'A';
+ } else {
+ line[char_counter++] = c;
+ }
}
}
}
+
+ protocol_execute_runtime(); // Runtime command check point.
+ if (sys.abort) { return; } // Bail to main() program loop to reset system.
+
+ // If there are no more characters in the serial read buffer to be processed and executed,
+ // this indicates that g-code streaming has either filled the planner buffer or has
+ // completed. In either case, auto-cycle start, if enabled, any queued moves.
+ mc_auto_cycle_start();
+
}
+
+ return; /* Never reached */
}
diff --git a/protocol.h b/protocol.h
index b6267d4..9107d1d 100644
--- a/protocol.h
+++ b/protocol.h
@@ -1,5 +1,5 @@
/*
- protocol.h - the serial protocol master control unit
+ protocol.h - controls Grbl execution procedures
Part of Grbl
Copyright (c) 2011-2014 Sungeun K. Jeon
@@ -21,32 +21,21 @@
#ifndef protocol_h
#define protocol_h
-#include
-
// Line buffer size from the serial input stream to be executed.
// NOTE: Not a problem except for extreme cases, but the line buffer size can be too small
// and g-code blocks can get truncated. Officially, the g-code standards support up to 256
// characters. In future versions, this will be increased, when we know how much extra
-// memory space we can invest into here or we re-write the g-code parser not to have his
+// memory space we can invest into here or we re-write the g-code parser not to have this
// buffer.
#ifndef LINE_BUFFER_SIZE
#define LINE_BUFFER_SIZE 70
#endif
-// Initialize the serial protocol
-void protocol_init();
-
-// Read command lines from the serial port and execute them as they
-// come in. Blocks until the serial buffer is emptied.
-void protocol_process();
-
-// Executes one line of input according to protocol
-uint8_t protocol_execute_line(char *line);
-
// Checks and executes a runtime command at various stop points in main program
void protocol_execute_runtime();
-// Execute the startup script lines stored in EEPROM upon initialization
-void protocol_execute_startup();
+// Starts Grbl main loop. It handles all incoming characters from the serial port and executes
+// them as they complete. It is also responsible for finishing the initialization procedures.
+void protocol_process();
#endif
diff --git a/report.c b/report.c
index 1796c8c..ee4de61 100644
--- a/report.c
+++ b/report.c
@@ -26,11 +26,10 @@
methods to accomodate their needs.
*/
-#include
+#include "system.h"
#include "report.h"
#include "print.h"
#include "settings.h"
-#include "nuts_bolts.h"
#include "gcode.h"
#include "coolant_control.h"
@@ -163,9 +162,9 @@ void report_grbl_settings() {
printPgmString(PSTR(" (z max travel, mm)\r\n$12=")); printInteger(settings.pulse_microseconds);
printPgmString(PSTR(" (step pulse, usec)\r\n$13=")); printFloat(settings.default_feed_rate);
printPgmString(PSTR(" (default feed, mm/min)\r\n$14=")); printInteger(settings.step_invert_mask);
- printPgmString(PSTR(" (step port invert mask, int:")); print_uint8_base2(settings.step_invert_mask);
+ printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask);
printPgmString(PSTR(")\r\n$15=")); printInteger(settings.dir_invert_mask);
- printPgmString(PSTR(" (dir port invert mask, int:")); print_uint8_base2(settings.dir_invert_mask);
+ printPgmString(PSTR(" (dir port invert mask:")); print_uint8_base2(settings.dir_invert_mask);
printPgmString(PSTR(")\r\n$16=")); printInteger(settings.stepper_idle_lock_time);
printPgmString(PSTR(" (step idle delay, msec)\r\n$17=")); printFloat(settings.junction_deviation);
printPgmString(PSTR(" (junction deviation, mm)\r\n$18=")); printFloat(settings.arc_tolerance);
@@ -178,7 +177,7 @@ void report_grbl_settings() {
printPgmString(PSTR(" (soft limits, bool)\r\n$25=")); printInteger(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE));
printPgmString(PSTR(" (hard limits, bool)\r\n$26=")); printInteger(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE));
printPgmString(PSTR(" (homing cycle, bool)\r\n$27=")); printInteger(settings.homing_dir_mask);
- printPgmString(PSTR(" (homing dir invert mask, int:")); print_uint8_base2(settings.homing_dir_mask);
+ printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask);
printPgmString(PSTR(")\r\n$28=")); printFloat(settings.homing_feed_rate);
printPgmString(PSTR(" (homing feed, mm/min)\r\n$29=")); printFloat(settings.homing_seek_rate);
printPgmString(PSTR(" (homing seek, mm/min)\r\n$30=")); printInteger(settings.homing_debounce_delay);
diff --git a/serial.c b/serial.c
index 382ee41..29e361c 100644
--- a/serial.c
+++ b/serial.c
@@ -23,11 +23,12 @@
used to be a part of the Arduino project. */
#include
+#include "system.h"
#include "serial.h"
-#include "config.h"
#include "motion_control.h"
#include "protocol.h"
+
uint8_t rx_buffer[RX_BUFFER_SIZE];
uint8_t rx_buffer_head = 0;
volatile uint8_t rx_buffer_tail = 0;
@@ -36,6 +37,7 @@ uint8_t tx_buffer[TX_BUFFER_SIZE];
uint8_t tx_buffer_head = 0;
volatile uint8_t tx_buffer_tail = 0;
+
#ifdef ENABLE_XONXOFF
volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
@@ -49,6 +51,7 @@ volatile uint8_t tx_buffer_tail = 0;
}
#endif
+
void serial_init()
{
// Set baud rate
@@ -72,6 +75,7 @@ void serial_init()
// defaults to 8-bit, no parity, 1 stop bit
}
+
void serial_write(uint8_t data) {
// Calculate next head
uint8_t next_head = tx_buffer_head + 1;
@@ -90,6 +94,7 @@ void serial_write(uint8_t data) {
UCSR0B |= (1 << UDRIE0);
}
+
// Data Register Empty Interrupt handler
ISR(SERIAL_UDRE)
{
@@ -119,6 +124,7 @@ ISR(SERIAL_UDRE)
if (tail == tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
}
+
uint8_t serial_read()
{
uint8_t tail = rx_buffer_tail; // Temporary rx_buffer_tail (to optimize for volatile)
@@ -142,6 +148,7 @@ uint8_t serial_read()
}
}
+
ISR(SERIAL_RX)
{
uint8_t data = UDR0;
@@ -174,6 +181,7 @@ ISR(SERIAL_RX)
}
}
+
void serial_reset_read_buffer()
{
rx_buffer_tail = rx_buffer_head;
diff --git a/serial.h b/serial.h
index e768b44..74b429b 100644
--- a/serial.h
+++ b/serial.h
@@ -25,7 +25,6 @@
#ifndef serial_h
#define serial_h
-#include "nuts_bolts.h"
#ifndef RX_BUFFER_SIZE
#define RX_BUFFER_SIZE 128
diff --git a/settings.c b/settings.c
index a312ee4..76306a3 100644
--- a/settings.c
+++ b/settings.c
@@ -19,29 +19,15 @@
along with Grbl. If not, see .
*/
-#include
-#include "protocol.h"
-#include "report.h"
-#include "stepper.h"
-#include "nuts_bolts.h"
+#include "system.h"
#include "settings.h"
#include "eeprom.h"
+#include "protocol.h"
+#include "report.h"
#include "limits.h"
settings_t settings;
-// Version 4 outdated settings record
-typedef struct {
- float steps_per_mm[N_AXIS];
- uint8_t microsteps;
- uint8_t pulse_microseconds;
- float default_feed_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)
@@ -50,12 +36,14 @@ void settings_store_startup_line(uint8_t n, char *line)
memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE);
}
+
// Method to store build info into EEPROM
void settings_store_build_info(char *line)
{
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO,(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)
{
@@ -63,6 +51,7 @@ void settings_write_coord_data(uint8_t coord_select, float *coord_data)
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()
{
@@ -70,27 +59,24 @@ void write_global_settings()
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.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE;
- settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE;
- settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE;
- settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION;
- settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION;
- settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION;
- settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
- settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK;
- settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK;
- settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
- }
- // New settings since last version
+void settings_reset() {
+ 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.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE;
+ settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE;
+ settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE;
+ settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION;
+ settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION;
+ settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION;
+ settings.arc_tolerance = DEFAULT_ARC_TOLERANCE;
+ settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK;
+ settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK;
+ settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION;
settings.flags = 0;
if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; }
if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; }
@@ -112,13 +98,14 @@ void settings_reset(bool reset_all) {
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;
+ line[0] = 0; // Empty line
settings_store_startup_line(n, line);
return(false);
} else {
@@ -126,12 +113,13 @@ uint8_t settings_read_startup_line(uint8_t n, char *line)
}
}
+
// Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_build_info(char *line)
{
if (!(memcpy_from_eeprom_with_checksum((char*)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) {
// Reset line with default value
- line[0] = 0;
+ line[0] = 0; // Empty line
settings_store_build_info(line);
return(false);
} else {
@@ -139,6 +127,7 @@ uint8_t settings_read_build_info(char *line)
}
}
+
// Read selected coordinate data from EEPROM. Updates pointed coord_data value.
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data)
{
@@ -153,26 +142,18 @@ uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data)
}
}
+
// 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(false);
}
return(true);
}
@@ -180,9 +161,9 @@ uint8_t read_global_settings() {
// A helper method to set settings from command line
uint8_t settings_store_global_setting(int parameter, float value) {
+ if (value < 0.0) { return(STATUS_SETTING_VALUE_NEG); }
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: settings.max_rate[X_AXIS] = value; break;
case 4: settings.max_rate[Y_AXIS] = value; break;
@@ -249,11 +230,12 @@ uint8_t settings_store_global_setting(int parameter, float value) {
return(STATUS_OK);
}
+
// Initialize the config subsystem
void settings_init() {
if(!read_global_settings()) {
report_status_message(STATUS_SETTING_READ_FAIL);
- settings_reset(true);
+ settings_reset();
report_grbl_settings();
}
// Read all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing.
diff --git a/settings.h b/settings.h
index 05840bb..6ff54be 100644
--- a/settings.h
+++ b/settings.h
@@ -22,15 +22,15 @@
#ifndef settings_h
#define settings_h
-#include
-#include "nuts_bolts.h"
+#include "system.h"
+
#define GRBL_VERSION "0.9c"
-#define GRBL_VERSION_BUILD "20131231"
+#define GRBL_VERSION_BUILD "20140110"
// Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl
// when firmware is upgraded. Always stored in byte 0 of eeprom
-#define SETTINGS_VERSION 57
+#define SETTINGS_VERSION 6
// Define bit flag masks for the boolean settings in settings.flag.
#define BITFLAG_REPORT_INCHES bit(0)
diff --git a/spindle_control.c b/spindle_control.c
index 70e819e..e97c3f7 100644
--- a/spindle_control.c
+++ b/spindle_control.c
@@ -19,17 +19,13 @@
along with Grbl. If not, see .
*/
-#include "settings.h"
+#include "system.h"
#include "spindle_control.h"
#include "planner.h"
-static uint8_t current_direction;
-static uint16_t current_rpm;
-
void spindle_init()
-{
- current_direction = 0;
+{
SPINDLE_DIRECTION_DDR |= (1< 0) {
- SPINDLE_DIRECTION_PORT &= ~(1< SPINDLE_MAX_RPM) { rpm = SPINDLE_MAX_RPM; } // Prevent overflow.
- uint8_t current_pwm = floor((((float) rpm / (float) SPINDLE_MAX_RPM ) * 255.0) + 0.5);
- OCR_REGISTER = current_pwm;
-
- #ifndef CPU_MAP_ATMEGA328P // On the Uno, spindle enable and PWM are shared.
- SPINDLE_ENABLE_PORT |= (1< SPINDLE_RPM_RANGE ) { rpm = SPINDLE_RPM_RANGE; } // Prevent uint8 overflow
+ uint8_t current_pwm = floor( rpm*(255.0/SPINDLE_RPM_RANGE) + 0.5);
+ OCR_REGISTER = current_pwm;
+
+ #ifndef CPU_MAP_ATMEGA328P // On the Uno, spindle enable and PWM are shared.
SPINDLE_ENABLE_PORT |= (1<
+
+#define SPINDLE_DISABLE 0 // Must be zero.
+#define SPINDLE_ENABLE_CW 1
+#define SPINDLE_ENABLE_CCW 2
+
// Initializes spindle pins and hardware PWM, if enabled.
void spindle_init();
// Sets spindle direction and spindle rpm via PWM, if enabled.
-void spindle_run(int8_t direction, uint16_t rpm);
+void spindle_run(uint8_t direction, float rpm);
// Kills spindle.
void spindle_stop();
diff --git a/stepper.c b/stepper.c
index 7792896..4618ef8 100644
--- a/stepper.c
+++ b/stepper.c
@@ -19,12 +19,11 @@
along with Grbl. If not, see .
*/
-#include
+#include "system.h"
+#include "nuts_bolts.h"
#include "stepper.h"
-#include "config.h"
#include "settings.h"
#include "planner.h"
-#include "nuts_bolts.h"
// Some useful constants.
@@ -43,7 +42,7 @@
// NOTE: Current settings are set to overdrive the ISR to no more than 16kHz, balancing CPU overhead
// and timer accuracy. Do not alter these settings unless you know what you are doing.
#define MAX_AMASS_LEVEL 3
-// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency.
+// AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency.
#define AMASS_LEVEL1 (F_CPU/8000) // Over-drives ISR (x2). Defined as F_CPU/(Cutoff frequency in Hz)
#define AMASS_LEVEL2 (F_CPU/4000) // Over-drives ISR (x4)
#define AMASS_LEVEL3 (F_CPU/2000) // Over-drives ISR (x8)
@@ -182,12 +181,10 @@ static st_prep_t prep;
// enabled. Startup init and limits call this function but shouldn't start the cycle.
void st_wake_up()
{
- // Enable steppers by resetting the stepper disable port
- if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) {
- STEPPERS_DISABLE_PORT |= (1<millimeters;
float time_var = dt_max; // Time worker variable
float mm_var; // mm-Distance worker variable
@@ -676,8 +673,8 @@ void st_prep_buffer()
switch (prep.ramp_type) {
case RAMP_ACCEL:
// NOTE: Acceleration ramp only computes during first do-while loop.
- speed_var = pl_block->acceleration*dt_max;
- mm_remaining -= dt_max*(prep.current_speed + 0.5*speed_var);
+ speed_var = pl_block->acceleration*time_var;
+ mm_remaining -= time_var*(prep.current_speed + 0.5*speed_var);
if (mm_remaining < prep.accelerate_until) { // End of acceleration ramp.
// Acceleration-cruise, acceleration-deceleration ramp junction, or end of block.
mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB
@@ -718,10 +715,14 @@ void st_prep_buffer()
}
dt += time_var; // Add computed ramp time to total segment time.
if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction.
- else if (mm_remaining > prep.minimum_mm) { // Check for slow segments with zero steps.
- dt_max += DT_SEGMENT; // Increase segment time to ensure at least one step in segment.
- time_var = dt_max - dt;
- } else { break; } // **Complete** Exit loop. Segment execution time maxed.
+ else {
+ if (mm_remaining > prep.minimum_mm) { // Check for slow segments with zero steps.
+ dt_max += DT_SEGMENT; // Increase segment time to ensure at least one step in segment.
+ time_var = dt_max - dt;
+ } else {
+ break; // **Complete** Exit loop. Segment execution time maxed.
+ }
+ }
} while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete.
/* -----------------------------------------------------------------------------------
@@ -745,7 +746,7 @@ void st_prep_buffer()
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
// Compute step timing and multi-axis smoothing level.
- // NOTE: Only one prescalar is required with AMASS enabled.
+ // NOTE: AMASS overdrives the timer with each level, so only one prescalar is required.
if (cycles < AMASS_LEVEL1) { prep_segment->amass_level = 0; }
else {
if (cycles < AMASS_LEVEL2) { prep_segment->amass_level = 1; }
diff --git a/stepper.h b/stepper.h
index a72a565..fe9323f 100644
--- a/stepper.h
+++ b/stepper.h
@@ -27,7 +27,7 @@
#endif
// Initialize and setup the stepper motor subsystem
-void st_init();
+void stepper_init();
// Enable steppers, but cycle does not start unless called by motion control or runtime command.
void st_wake_up();
@@ -47,7 +47,7 @@ void st_cycle_reinitialize();
// Initiates a feed hold of the running program
void st_feed_hold();
-// Reloads step segment buffer. Called continuously by runtime execution protocol.
+// Reloads step segment buffer. Called continuously by runtime execution system.
void st_prep_buffer();
// Called by planner_recalculate() when the executing block is updated by the new plan.
diff --git a/system.c b/system.c
new file mode 100644
index 0000000..4dfe31d
--- /dev/null
+++ b/system.c
@@ -0,0 +1,200 @@
+/*
+ system.c - Handles system level commands and real-time processes
+ Part of Grbl
+
+ Copyright (c) 2014 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 "system.h"
+#include "settings.h"
+#include "gcode.h"
+#include "motion_control.h"
+#include "report.h"
+#include "print.h"
+
+
+void system_init()
+{
+ PINOUT_DDR &= ~(PINOUT_MASK); // Set as input pins
+ PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation.
+ PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt
+ PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt
+}
+
+
+// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets
+// only the runtime command execute variable to have the main program execute these when
+// its ready. This works exactly like the character-based runtime commands when picked off
+// directly from the incoming serial data stream.
+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_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))) {
+ sys.execute |= EXEC_CYCLE_START;
+ }
+ }
+}
+
+
+// Executes user startup script, if stored.
+void system_execute_startup(char *line)
+{
+ uint8_t n;
+ for (n=0; n < N_STARTUP_LINE; n++) {
+ if (!(settings_read_startup_line(n, line))) {
+ report_status_message(STATUS_SETTING_READ_FAIL);
+ } else {
+ if (line[0] != 0) {
+ printString(line); // Echo startup line to indicate execution.
+ report_status_message(gc_execute_line(line));
+ }
+ }
+ }
+}
+
+
+// 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 system_execute_line(char *line)
+{
+ uint8_t char_counter = 1;
+ uint8_t helper_var = 0; // Helper variable
+ float parameter, value;
+ switch( line[char_counter] ) {
+ case '#' : // Print gcode parameters
+ if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
+ else { report_gcode_parameters(); }
+ break;
+ case 'G' : // Prints gcode parser state
+ if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
+ else { report_gcode_modes(); }
+ 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.
+ default :
+ // Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing)
+ if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); }
+ switch( line[char_counter] ) {
+ case 0 : report_grbl_help(); break;
+ case '$' : // Prints Grbl settings
+ if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
+ else { report_grbl_settings(); }
+ break;
+ case 'C' : // Set check g-code mode
+ if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
+ // Perform reset when toggling off. Check g-code mode should only work if Grbl
+ // is idle and ready, regardless of alarm locks. This is mainly to keep things
+ // simple and consistent.
+ if ( sys.state == STATE_CHECK_MODE ) {
+ mc_reset();
+ report_feedback_message(MESSAGE_DISABLED);
+ } else {
+ if (sys.state) { return(STATUS_IDLE_ERROR); }
+ sys.state = STATE_CHECK_MODE;
+ report_feedback_message(MESSAGE_ENABLED);
+ }
+ break;
+ case 'X' : // Disable alarm lock
+ if ( line[++char_counter] != 0 ) { return(STATUS_UNSUPPORTED_STATEMENT); }
+ if (sys.state == STATE_ALARM) {
+ report_feedback_message(MESSAGE_ALARM_UNLOCK);
+ sys.state = STATE_IDLE;
+ // Don't run startup script. Prevents stored moves in startup from causing accidents.
+ }
+ break;
+ case 'H' : // Perform homing cycle
+ if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
+ // Only perform homing if Grbl is idle or lost.
+ mc_homing_cycle();
+ if (!sys.abort) { system_execute_startup(line); } // Execute startup scripts after successful homing.
+ } else { return(STATUS_SETTING_DISABLED); }
+ break;
+ case 'I' : // Print or store build info.
+ if ( line[++char_counter] == 0 ) {
+ if (!(settings_read_build_info(line))) {
+ report_status_message(STATUS_SETTING_READ_FAIL);
+ } else {
+ report_build_info(line);
+ }
+ } else { // Store startup line
+ if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); }
+ helper_var = char_counter; // Set helper variable as counter to start of user info line.
+ do {
+ line[char_counter-helper_var] = line[char_counter];
+ } while (line[char_counter++] != 0);
+ settings_store_build_info(line);
+ }
+ break;
+ case 'N' : // Startup lines.
+ if ( line[++char_counter] == 0 ) { // Print startup lines
+ for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) {
+ if (!(settings_read_startup_line(helper_var, line))) {
+ report_status_message(STATUS_SETTING_READ_FAIL);
+ } else {
+ report_startup_line(helper_var,line);
+ }
+ }
+ break;
+ } else { // Store startup line
+ helper_var = true; // Set helper_var to flag storing method.
+ // No break. Continues into default: to read remaining command characters.
+ }
+ default : // Storing setting methods
+ if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); }
+ if(line[char_counter++] != '=') { return(STATUS_UNSUPPORTED_STATEMENT); }
+ if (helper_var) { // Store startup line
+ // Prepare sending gcode block to gcode parser by shifting all characters
+ helper_var = char_counter; // Set helper variable as counter to start of gcode block
+ do {
+ line[char_counter-helper_var] = line[char_counter];
+ } while (line[char_counter++] != 0);
+ // Execute gcode block to ensure block is valid.
+ helper_var = gc_execute_line(line); // Set helper_var to returned status code.
+ if (helper_var) { return(helper_var); }
+ else {
+ helper_var = trunc(parameter); // Set helper_var to int value of parameter
+ settings_store_startup_line(helper_var,line);
+ }
+ } else { // Store global setting.
+ 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));
+ }
+ }
+ }
+ return(STATUS_OK); // If '$' command makes it to here, then everything's ok.
+}
diff --git a/system.h b/system.h
new file mode 100644
index 0000000..78faa5c
--- /dev/null
+++ b/system.h
@@ -0,0 +1,95 @@
+/*
+ system.h - Header for system level commands and real-time processes
+ Part of Grbl
+
+ Copyright (c) 2014 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 .
+*/
+
+#ifndef system_h
+#define system_h
+
+// Define system header files and standard libraries used by Grbl
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Define Grbl configuration and shared header files
+#include "config.h"
+#include "defaults.h"
+#include "cpu_map.h"
+#include "nuts_bolts.h"
+
+
+// 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 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 EXEC_ALARM bit(5) // bitmask 00100000
+#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. No flags.
+#define STATE_QUEUED bit(0) // Indicates buffered blocks, awaiting cycle start.
+#define STATE_CYCLE bit(1) // Cycle is running
+#define STATE_HOLD bit(2) // Executing feed hold
+#define STATE_HOMING bit(3) // Performing homing cycle
+#define STATE_ALARM bit(4) // In alarm state. Locks out all g-code processes. Allows settings access.
+#define STATE_CHECK_MODE bit(5) // G-code check mode. Locks out planner and motion only.
+// #define STATE_JOG bit(6) // 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.
+ uint8_t homing_axis_lock;
+ 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.
+} system_t;
+extern system_t sys;
+
+
+// Initialize the serial protocol
+void system_init();
+
+// Executes an internal system command, defined as a string starting with a '$'
+uint8_t system_execute_line(char *line);
+
+// Checks and executes a runtime command at various stop points in main program
+void system_execute_runtime();
+
+// Execute the startup script lines stored in EEPROM upon initialization
+void system_execute_startup(char *line);
+
+#endif