Repository: robottini/grbl-servo Branch: master Commit: 1e7a63ab05cb Files: 52 Total size: 383.2 KB Directory structure: gitextract_2d22mjbq/ ├── README.md ├── config.h ├── coolant_control.c ├── coolant_control.h ├── cpu_map/ │ ├── cpu_map_atmega2560.h │ └── cpu_map_atmega328p.h ├── cpu_map.h ├── defaults/ │ ├── defaults_generic.h │ ├── defaults_oxcnc.h │ ├── defaults_shapeoko.h │ ├── defaults_shapeoko2.h │ ├── defaults_shapeoko3.h │ ├── defaults_sherline.h │ ├── defaults_simulator.h │ ├── defaults_x_carve_1000mm.h │ ├── defaults_x_carve_500mm.h │ └── defaults_zen_toolworks_7x7.h ├── defaults.h ├── eeprom.c ├── eeprom.h ├── examples/ │ └── grblUpload/ │ ├── grblUpload.ino │ └── license.txt ├── gcode.c ├── gcode.h ├── grbl.h ├── limits.c ├── limits.h ├── main.c ├── motion_control.c ├── motion_control.h ├── nuts_bolts.c ├── nuts_bolts.h ├── planner.c ├── planner.h ├── print.c ├── print.h ├── probe.c ├── probe.h ├── protocol.c ├── protocol.h ├── report.c ├── report.h ├── serial.c ├── serial.h ├── settings.c ├── settings.h ├── spindle_control.c ├── spindle_control.h ├── stepper.c ├── stepper.h ├── system.c └── system.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # grbl-servo grbl 0.9i with Servo motor support GRBL 0.9i with servo motor support. Use the PIN D11 to drive the servo. Use the commands M03 Sxxx (xxx between 0 and 255) to rotate the servo between 0-180. The command M05 turn the servo to zero degrees. you can change the pulse duration in the file spindle_control.c: define RC_SERVO_SHORT 15 // Timer ticks for 0.6ms pulse duration (9 for 0.6ms) define RC_SERVO_LONG 32 // Timer ticks for 2.5 ms pulse duration (39 for 2.5ms) define RC_SERVO_INVERT 1 // Uncomment to invert servo direction If you want to have the servo working from 0 --> 180 degrees change RC_SERVO_SHORT and put 9, RC_SERVO_LONG and put 39 If you want invert the servo direction uncomment the line above. I tested the code very well with 328p (Arduino Uno, Duemilanove etv), not with 2560 (Arduino Mega), but I think it would work well also with the Mega. ------------------------------------------------------------------- The link for GRBL vanilla is: http://github.com/grbl/grbl Grbl is a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling. It will run on a vanilla Arduino (Duemillanove/Uno) as long as it sports an Atmega 328. The controller is written in highly optimized C utilizing every clever feature of the AVR-chips to achieve precise timing and asynchronous operation. It is able to maintain up to 30kHz of stable, jitter free control pulses. It accepts standards-compliant g-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported, as well as, all other primary g-code commands. Macro functions, variables, and most canned cycles are not supported, but we think GUIs can do a much better job at translating them into straight g-code anyhow. Grbl includes full acceleration management with look ahead. That means the controller will look up to 18 motions into the future and plan its velocities ahead to deliver smooth acceleration and jerk-free cornering. Licensing: Grbl is free software, released under the GPLv3 license. For more information and help, check out our Wiki pages! If you find that the information is out-dated, please to help us keep it updated by editing it or notifying our community! Thanks! Lead Developer [2011 - Current]: Sungeun(Sonny) K. Jeon, Ph.D. (USA) aka @chamnit Lead Developer [2009 - 2011]: Simen Svale Skogsrud (Norway). aka The Originator/Creator/Pioneer/Father of Grbl. The link for GRBL vanilla is: http://github.com/grbl/grbl ================================================ FILE: config.h ================================================ /* config.h - compile time configuration Part of Grbl Copyright (c) 2012-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 . */ // This file contains compile-time configurations for Grbl's internal system. For the most part, // users will not need to directly modify these, but they are here for specific needs, i.e. // performance tuning or adjusting to non-typical machines. // IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them. #ifndef config_h #define config_h #include "grbl.h" // For Arduino IDE compatibility. // Default settings. Used when resetting EEPROM. Change to desired name in defaults.h #define DEFAULTS_GENERIC // Serial baud rate #define BAUD_RATE 115200 // Default cpu mappings. Grbl officially supports the Arduino Uno only. Other processor types // may exist from user-supplied templates or directly user-defined in cpu_map.h #define CPU_MAP_ATMEGA328P // Arduino Uno CPU // Define realtime command special characters. These characters are 'picked-off' directly from the // serial read data stream and are not passed to the grbl line execution parser. Select characters // that do not and must not exist in the streamed g-code program. ASCII control characters may be // used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in // g-code programs, maybe selected for interface programs. // NOTE: If changed, manually update help message in report.c. #define CMD_STATUS_REPORT '?' #define CMD_FEED_HOLD '!' #define CMD_CYCLE_START '~' #define CMD_RESET 0x18 // ctrl-x. #define CMD_SAFETY_DOOR '@' // 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 // Define the homing cycle patterns with bitmasks. The homing cycle first performs a search mode // to quickly engage the limit switches, followed by a slower locate mode, and finished by a short // pull-off motion to disengage the limit switches. The following HOMING_CYCLE_x defines are executed // in order starting with suffix 0 and completes the homing routine for the specified-axes only. If // an axis is omitted from the defines, it will not home, nor will the system update its position. // Meaning that this allows for users with non-standard cartesian machines, such as a lathe (x then z, // with no y), to configure the homing cycle behavior to their needs. // NOTE: The homing cycle is designed to allow sharing of limit pins, if the axes are not in the same // cycle, but this requires some pin settings changes in cpu_map.h file. For example, the default homing // cycle can share the Z limit pin with either X or Y limit pins, since they are on different cycles. // By sharing a pin, this frees up a precious IO pin for other purposes. In theory, all axes limit pins // may be reduced to one pin, if all axes are homed with seperate cycles, or vice versa, all three axes // on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits // will not be affected by pin sharing. // NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y. //#define HOMING_CYCLE_0 (1< 3us, and, when added with the // user-supplied step pulse time, the total time must not exceed 127us. Reported successful // values for certain setups have ranged from 5 to 20us. // #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled. // The number of linear motions in the planner buffer to be planned at any give time. The vast // majority of RAM that Grbl uses is based on this buffer size. Only increase if there is extra // available RAM, like when re-compiling for a Mega or Sanguino. Or decrease if the Arduino // begins to crash due to the lack of available RAM or if the CPU is having trouble keeping // up with planning new incoming motions as they are executed. // #define BLOCK_BUFFER_SIZE 18 // Uncomment to override default in planner.h. // Governs the size of the intermediary step segment buffer between the step execution algorithm // and the planner blocks. Each segment is set of steps executed at a constant velocity over a // fixed time defined by ACCELERATION_TICKS_PER_SECOND. They are computed such that the planner // block velocity profile is traced exactly. The size of this buffer governs how much step // execution lead time there is for other Grbl processes have to compute and do their thing // before having to come back and refill this buffer, currently at ~50msec of step moves. // #define SEGMENT_BUFFER_SIZE 6 // Uncomment to override default in stepper.h. // Line buffer size from the serial input stream to be executed. Also, governs the size of // each of the startup blocks, as they are each stored as a string of this size. Make sure // to account for the available EEPROM at the defined memory address in settings.h and for // the number of desired startup blocks. // NOTE: 80 characters is 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 default will be increased, when // we know how much extra memory space we can re-invest into this. // #define LINE_BUFFER_SIZE 80 // Uncomment to override default in protocol.h // Serial send and receive buffer size. The receive buffer is often used as another streaming // buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming // interfaces will character count and track each block send to each block response. So, // increase the receive buffer if a deeper receive buffer is needed for streaming and avaiable // memory allows. The send buffer primarily handles messages in Grbl. Only increase if large // messages are sent and Grbl begins to stall, waiting to send the rest of the message. // NOTE: Buffer size values must be greater than zero and less than 256. // #define RX_BUFFER_SIZE 128 // Uncomment to override defaults in serial.h // #define TX_BUFFER_SIZE 64 // Toggles XON/XOFF software flow control for serial communications. Not officially supported // due to problems involving the Atmega8U2 USB-to-serial chips on current Arduinos. The firmware // on these chips do not support XON/XOFF flow control characters and the intermediate buffer // in the chips cause latency and overflow problems with standard terminal programs. However, // using specifically-programmed UI's to manage this latency problem has been confirmed to work. // As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard // terminal programs since their firmware correctly manage these XON/XOFF characters. In any // 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 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. // Force Grbl to check the state of the hard limit switches when the processor detects a pin // change inside the hard limit ISR routine. By default, Grbl will trigger the hard limits // alarm upon any pin change, since bouncing switches can cause a state check like this to // misread the pin. When hard limits are triggered, they should be 100% reliable, which is the // reason that this option is disabled by default. Only if your system/electronics can guarantee // that the switches don't bounce, we recommend enabling this option. This will help prevent // triggering a hard limit when the machine disengages from the switch. // NOTE: This option has no effect if SOFTWARE_DEBOUNCE is enabled. // #define HARD_LIMIT_FORCE_STATE_CHECK // Default disabled. Uncomment to enable. // --------------------------------------------------------------------------------------- // COMPILE-TIME ERROR CHECKING OF DEFINE VALUES: #ifndef HOMING_CYCLE_0 #error "Required HOMING_CYCLE_0 not defined." #endif #if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(VARIABLE_SPINDLE) #error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with VARIABLE_SPINDLE enabled" #endif #if defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(CPU_MAP_ATMEGA328P) #error "USE_SPINDLE_DIR_AS_ENABLE_PIN may only be used with a 328p processor" #endif // --------------------------------------------------------------------------------------- #endif ================================================ FILE: coolant_control.c ================================================ /* coolant_control.c - coolant control methods Part of Grbl Copyright (c) 2012-2015 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 "grbl.h" void coolant_init() { COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT); #ifdef ENABLE_M7 COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT); #endif coolant_stop(); } void coolant_stop() { COOLANT_FLOOD_PORT &= ~(1 << COOLANT_FLOOD_BIT); #ifdef ENABLE_M7 COOLANT_MIST_PORT &= ~(1 << COOLANT_MIST_BIT); #endif } void coolant_set_state(uint8_t mode) { 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(); } } void coolant_run(uint8_t mode) { if (sys.state == STATE_CHECK_MODE) { return; } protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program. coolant_set_state(mode); } ================================================ FILE: coolant_control.h ================================================ /* coolant_control.h - spindle control methods Part of Grbl Copyright (c) 2012-2015 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 coolant_control_h #define coolant_control_h void coolant_init(); void coolant_stop(); void coolant_set_state(uint8_t mode); void coolant_run(uint8_t mode); #endif ================================================ FILE: cpu_map/cpu_map_atmega2560.h ================================================ /* cpu_map_atmega2560.h - CPU and pin mapping configuration file Part of Grbl Copyright (c) 2012-2015 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 . */ /* This cpu_map file serves as a central pin mapping settings file for AVR Mega 2560 */ #ifdef GRBL_PLATFORM #error "cpu_map already defined: GRBL_PLATFORM=" GRBL_PLATFORM #endif #define GRBL_PLATFORM "Atmega2560" // Serial port pins #define SERIAL_RX USART0_RX_vect #define SERIAL_UDRE USART0_UDRE_vect // Increase Buffers to make use of extra SRAM //#define RX_BUFFER_SIZE 256 //#define TX_BUFFER_SIZE 128 //#define BLOCK_BUFFER_SIZE 36 //#define LINE_BUFFER_SIZE 100 // Define step pulse output pins. NOTE: All step bit pins must be on the same port. #define STEP_DDR DDRA #define STEP_PORT PORTA #define STEP_PIN PINA #define X_STEP_BIT 2 // MEGA2560 Digital Pin 24 #define Y_STEP_BIT 3 // MEGA2560 Digital Pin 25 #define Z_STEP_BIT 4 // MEGA2560 Digital Pin 26 #define STEP_MASK ((1<. */ /* Grbl officially supports the Arduino Uno, but the other supplied pin mappings are supplied by users, so your results may vary. This cpu_map file serves as a central pin mapping settings file for AVR 328p used on the Arduino Uno. */ #ifdef GRBL_PLATFORM #error "cpu_map already defined: GRBL_PLATFORM=" GRBL_PLATFORM #endif #define GRBL_PLATFORM "Atmega328p" // Define serial port pins and interrupt vectors. #define SERIAL_RX USART_RX_vect #define SERIAL_UDRE USART_UDRE_vect // Define step pulse output pins. NOTE: All step bit pins must be on the same port. #define STEP_DDR DDRD #define STEP_PORT PORTD #define X_STEP_BIT 2 // Uno Digital Pin 2 #define Y_STEP_BIT 3 // Uno Digital Pin 3 #define Z_STEP_BIT 4 // Uno Digital Pin 4 #define STEP_MASK ((1<. */ /* The cpu_map.h files serve as a central pin mapping selection file for different processor types, i.e. AVR 328p or AVR Mega 2560. Each processor has its own pin mapping file. (i.e. cpu_map_atmega328p.h) Grbl officially supports the Arduino Uno, but the other supplied pin mappings are supplied by users, so your results may vary. */ // NOTE: With new processors, only add the define name and filename to use. #ifndef cpu_map_h #define cpu_map_h #ifdef CPU_MAP_ATMEGA328P // (Arduino Uno) Officially supported by Grbl. #include "cpu_map/cpu_map_atmega328p.h" #endif #ifdef CPU_MAP_ATMEGA2560 // (Arduino Mega 2560) Working @EliteEng #include "cpu_map/cpu_map_atmega2560.h" #endif /* #ifdef CPU_MAP_CUSTOM_PROC // For a custom pin map or different processor, copy and edit one of the available cpu // map files and modify it to your needs. Make sure the defined name is also changed in // the config.h file. #endif */ #endif ================================================ FILE: defaults/defaults_generic.h ================================================ /* defaults_generic.h - defaults settings configuration file Part of Grbl Copyright (c) 2012-2015 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 . */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Grbl generic default settings. Should work across different machines. #define DEFAULT_X_STEPS_PER_MM 250.0 #define DEFAULT_Y_STEPS_PER_MM 250.0 #define DEFAULT_Z_STEPS_PER_MM 250.0 #define DEFAULT_X_MAX_RATE 500.0 // mm/min #define DEFAULT_Y_MAX_RATE 500.0 // mm/min #define DEFAULT_Z_MAX_RATE 500.0 // mm/min #define DEFAULT_X_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Z_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 200.0 // mm #define DEFAULT_Y_MAX_TRAVEL 200.0 // mm #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK 0 #define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled) #define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION)) #define DEFAULT_JUNCTION_DEVIATION 0.01 // mm #define DEFAULT_ARC_TOLERANCE 0.002 // mm #define DEFAULT_REPORT_INCHES 0 // false #define DEFAULT_INVERT_ST_ENABLE 0 // false #define DEFAULT_INVERT_LIMIT_PINS 0 // false #define DEFAULT_SOFT_LIMIT_ENABLE 0 // false #define DEFAULT_HARD_LIMIT_ENABLE 0 // false #define DEFAULT_HOMING_ENABLE 0 // false #define DEFAULT_HOMING_DIR_MASK 0 // move positive dir #define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min #define DEFAULT_HOMING_SEEK_RATE 500.0 // mm/min #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k) #define DEFAULT_HOMING_PULLOFF 1.0 // mm #endif ================================================ FILE: defaults/defaults_oxcnc.h ================================================ /* defaults_oxcnc.h - defaults settings configuration file Part of Grbl Copyright (c) 2012-2015 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 . */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Grbl settings for OpenBuilds OX CNC Machine // http://www.openbuilds.com/builds/openbuilds-ox-cnc-machine.341/ #define DEFAULT_X_STEPS_PER_MM 26.670 #define DEFAULT_Y_STEPS_PER_MM 26.670 #define DEFAULT_Z_STEPS_PER_MM 50 #define DEFAULT_X_MAX_RATE 500.0 // mm/min #define DEFAULT_Y_MAX_RATE 500.0 // mm/min #define DEFAULT_Z_MAX_RATE 500.0 // mm/min #define DEFAULT_X_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Z_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 500.0 // mm #define DEFAULT_Y_MAX_TRAVEL 750.0 // mm #define DEFAULT_Z_MAX_TRAVEL 80.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK 0 #define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled) #define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION)) #define DEFAULT_JUNCTION_DEVIATION 0.02 // mm #define DEFAULT_ARC_TOLERANCE 0.002 // mm #define DEFAULT_REPORT_INCHES 0 // false #define DEFAULT_INVERT_ST_ENABLE 0 // false #define DEFAULT_INVERT_LIMIT_PINS 0 // false #define DEFAULT_SOFT_LIMIT_ENABLE 0 // false #define DEFAULT_HARD_LIMIT_ENABLE 0 // false #define DEFAULT_HOMING_ENABLE 0 // false #define DEFAULT_HOMING_DIR_MASK 0 // move positive dir #define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min #define DEFAULT_HOMING_SEEK_RATE 500.0 // mm/min #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k) #define DEFAULT_HOMING_PULLOFF 1.0 // mm #endif ================================================ FILE: defaults/defaults_shapeoko.h ================================================ /* defaults_shapeoko.h - defaults settings configuration file Part of Grbl Copyright (c) 2012-2015 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 . */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Description: Shapeoko CNC mill with three NEMA 17 stepper motors, driven by Synthetos // grblShield with a 24V, 4.2A power supply. #define MICROSTEPS_XY 8 #define STEP_REVS_XY 400 #define MM_PER_REV_XY (0.08*18*MM_PER_INCH) // 0.08 in belt pitch, 18 pulley teeth #define MICROSTEPS_Z 2 #define STEP_REVS_Z 400 #define MM_PER_REV_Z 1.250 // 1.25 mm/rev leadscrew #define DEFAULT_X_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Y_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Z_STEPS_PER_MM (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) #define DEFAULT_X_MAX_RATE 1000.0 // mm/min #define DEFAULT_Y_MAX_RATE 1000.0 // mm/min #define DEFAULT_Z_MAX_RATE 1000.0 // mm/min #define DEFAULT_X_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2 #define DEFAULT_Y_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2 #define DEFAULT_Z_ACCELERATION (15.0*60*60) // 15*60*60 mm/min^2 = 15 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 200.0 // mm #define DEFAULT_Y_MAX_TRAVEL 200.0 // mm #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK ((1<. */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Description: Shapeoko CNC mill with three NEMA 17 stepper motors, driven by Synthetos // grblShield at 28V. #define MICROSTEPS_XY 8 #define STEP_REVS_XY 200 #define MM_PER_REV_XY (2.0*20) // 2mm belt pitch, 20 pulley teeth #define MICROSTEPS_Z 2 #define STEP_REVS_Z 200 #define MM_PER_REV_Z 1.250 // 1.25 mm/rev leadscrew #define DEFAULT_X_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Y_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Z_STEPS_PER_MM (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) #define DEFAULT_X_MAX_RATE 5000.0 // mm/min #define DEFAULT_Y_MAX_RATE 5000.0 // mm/min #define DEFAULT_Z_MAX_RATE 500.0 // mm/min #define DEFAULT_X_ACCELERATION (250.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 #define DEFAULT_Y_ACCELERATION (250.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 #define DEFAULT_Z_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 290.0 // mm #define DEFAULT_Y_MAX_TRAVEL 290.0 // mm #define DEFAULT_Z_MAX_TRAVEL 100.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK ((1<. */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Description: Shapeoko CNC mill with three NEMA 23 stepper motors, driven by CarbideMotion #define MICROSTEPS_XY 8 #define STEP_REVS_XY 200 #define MM_PER_REV_XY (2.0*20) // 2mm belt pitch, 20 pulley teeth #define MICROSTEPS_Z 8 #define STEP_REVS_Z 200 #define MM_PER_REV_Z (2.0*20) // 2mm belt pitch, 20 pulley teeth #define DEFAULT_X_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Y_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Z_STEPS_PER_MM (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) #define DEFAULT_X_MAX_RATE 5000.0 // mm/min #define DEFAULT_Y_MAX_RATE 5000.0 // mm/min #define DEFAULT_Z_MAX_RATE 5000.0 // mm/min #define DEFAULT_X_ACCELERATION (400.0*60*60) // 400*60*60 mm/min^2 = 400 mm/sec^2 #define DEFAULT_Y_ACCELERATION (400.0*60*60) // 400*60*60 mm/min^2 = 400 mm/sec^2 #define DEFAULT_Z_ACCELERATION (400.0*60*60) // 400*60*60 mm/min^2 = 400 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 425.0 // mm #define DEFAULT_Y_MAX_TRAVEL 465.0 // mm #define DEFAULT_Z_MAX_TRAVEL 80.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK ((1<. */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Description: Sherline 5400 mill with three NEMA 23 Keling KL23H256-21-8B 185 oz-in stepper motors, // driven by three Pololu A4988 stepper drivers with a 30V, 6A power supply at 1.5A per winding. #define MICROSTEPS 2 #define STEPS_PER_REV 200.0 #define MM_PER_REV (0.050*MM_PER_INCH) // 0.050 inch/rev leadscrew #define DEFAULT_X_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) #define DEFAULT_Y_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) #define DEFAULT_Z_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) #define DEFAULT_X_MAX_RATE 635.0 // mm/min (25 ipm) #define DEFAULT_Y_MAX_RATE 635.0 // mm/min #define DEFAULT_Z_MAX_RATE 635.0 // mm/min #define DEFAULT_X_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2 #define DEFAULT_Y_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2 #define DEFAULT_Z_ACCELERATION (50.0*60*60) // 50*60*60 mm/min^2 = 50 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 225.0 // mm #define DEFAULT_Y_MAX_TRAVEL 125.0 // mm #define DEFAULT_Z_MAX_TRAVEL 170.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK ((1<. */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Settings only for Grbl Simulator (www.github.com/grbl/grbl-sim) // Grbl generic default settings. Should work across different machines. #define DEFAULT_X_STEPS_PER_MM 1000.0 #define DEFAULT_Y_STEPS_PER_MM 1000.0 #define DEFAULT_Z_STEPS_PER_MM 1000.0 #define DEFAULT_X_MAX_RATE 1000.0 // mm/min #define DEFAULT_Y_MAX_RATE 1000.0 // mm/min #define DEFAULT_Z_MAX_RATE 1000.0 // mm/min #define DEFAULT_X_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Y_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_Z_ACCELERATION (100.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 1000.0 // mm #define DEFAULT_Y_MAX_TRAVEL 1000.0 // mm #define DEFAULT_Z_MAX_TRAVEL 1000.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK 0 #define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-254, 255 keeps steppers enabled) #define DEFAULT_STATUS_REPORT_MASK ((BITFLAG_RT_STATUS_MACHINE_POSITION)|(BITFLAG_RT_STATUS_WORK_POSITION)) #define DEFAULT_JUNCTION_DEVIATION 0.01 // mm #define DEFAULT_ARC_TOLERANCE 0.002 // mm #define DEFAULT_REPORT_INCHES 0 // false #define DEFAULT_INVERT_ST_ENABLE 0 // false #define DEFAULT_INVERT_LIMIT_PINS 0 // false #define DEFAULT_SOFT_LIMIT_ENABLE 0 // false #define DEFAULT_HARD_LIMIT_ENABLE 0 // false #define DEFAULT_HOMING_ENABLE 0 // false #define DEFAULT_HOMING_DIR_MASK 0 // move positive dir #define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min #define DEFAULT_HOMING_SEEK_RATE 500.0 // mm/min #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k) #define DEFAULT_HOMING_PULLOFF 1.0 // mm #endif ================================================ FILE: defaults/defaults_x_carve_1000mm.h ================================================ /* defaults_x_carve_1000mm.h - defaults settings configuration file Part of Grbl Copyright (c) 2012-2015 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 . */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Description: X-Carve 3D Carver CNC mill with three 200 step/rev motors driven by Synthetos // grblShield at 24V. #define MICROSTEPS_XY 8 #define STEP_REVS_XY 200 #define MM_PER_REV_XY (2.0*20) // 2mm belt pitch, 20 pulley teeth #define MICROSTEPS_Z 2 #define STEP_REVS_Z 200 #define MM_PER_REV_Z 2.117 // ACME 3/8-12 Leadscrew #define DEFAULT_X_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Y_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Z_STEPS_PER_MM (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) #define DEFAULT_X_MAX_RATE 8000.0 // mm/min #define DEFAULT_Y_MAX_RATE 8000.0 // mm/min #define DEFAULT_Z_MAX_RATE 500.0 // mm/min #define DEFAULT_X_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 #define DEFAULT_Y_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 #define DEFAULT_Z_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 740.0 // mm #define DEFAULT_Y_MAX_TRAVEL 790.0 // mm #define DEFAULT_Z_MAX_TRAVEL 100.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK ((1<. */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Description: X-Carve 3D Carver CNC mill with three 200 step/rev motors driven by Synthetos // grblShield at 24V. #define MICROSTEPS_XY 8 #define STEP_REVS_XY 200 #define MM_PER_REV_XY (2.0*20) // 2mm belt pitch, 20 pulley teeth #define MICROSTEPS_Z 2 #define STEP_REVS_Z 200 #define MM_PER_REV_Z 2.117 // ACME 3/8-12 Leadscrew #define DEFAULT_X_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Y_STEPS_PER_MM (MICROSTEPS_XY*STEP_REVS_XY/MM_PER_REV_XY) #define DEFAULT_Z_STEPS_PER_MM (MICROSTEPS_Z*STEP_REVS_Z/MM_PER_REV_Z) #define DEFAULT_X_MAX_RATE 8000.0 // mm/min #define DEFAULT_Y_MAX_RATE 8000.0 // mm/min #define DEFAULT_Z_MAX_RATE 500.0 // mm/min #define DEFAULT_X_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 #define DEFAULT_Y_ACCELERATION (500.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 #define DEFAULT_Z_ACCELERATION (50.0*60*60) // 25*60*60 mm/min^2 = 25 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 290.0 // mm #define DEFAULT_Y_MAX_TRAVEL 290.0 // mm #define DEFAULT_Z_MAX_TRAVEL 100.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK ((1<. */ /* The defaults.h file serves as a central default settings file for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. */ #ifndef defaults_h #define defaults_h // Description: Zen Toolworks 7x7 mill with three Shinano SST43D2121 65oz-in NEMA 17 stepper motors. // Leadscrew is different from some ZTW kits, where most are 1.25mm/rev rather than 8.0mm/rev here. // Driven by 30V, 6A power supply and TI DRV8811 stepper motor drivers. #define MICROSTEPS 8 #define STEPS_PER_REV 200.0 #define MM_PER_REV 8.0 // 8 mm/rev leadscrew #define DEFAULT_X_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) #define DEFAULT_Y_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) #define DEFAULT_Z_STEPS_PER_MM (STEPS_PER_REV*MICROSTEPS/MM_PER_REV) #define DEFAULT_X_MAX_RATE 6000.0 // mm/min #define DEFAULT_Y_MAX_RATE 6000.0 // mm/min #define DEFAULT_Z_MAX_RATE 6000.0 // mm/min #define DEFAULT_X_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2 #define DEFAULT_Y_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2 #define DEFAULT_Z_ACCELERATION (600.0*60*60) // 600*60*60 mm/min^2 = 600 mm/sec^2 #define DEFAULT_X_MAX_TRAVEL 190.0 // mm #define DEFAULT_Y_MAX_TRAVEL 180.0 // mm #define DEFAULT_Z_MAX_TRAVEL 150.0 // mm #define DEFAULT_STEP_PULSE_MICROSECONDS 10 #define DEFAULT_STEPPING_INVERT_MASK 0 #define DEFAULT_DIRECTION_INVERT_MASK ((1<. */ /* The defaults.h file serves as a central default settings selector for different machine types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings files listed here are supplied by users, so your results may vary. However, this should give you a good starting point as you get to know your machine and tweak the settings for your nefarious needs. Ensure one and only one of these DEFAULTS_XXX values is defined in config.h */ #ifndef defaults_h // Only define the DEFAULT_XXX with where to find the corresponding default_XXX.h file. // Don't #define defaults_h here, let the selected file do it. Prevents including more than one. #ifdef DEFAULTS_GENERIC // Grbl generic default settings. Should work across different machines. #include "defaults/defaults_generic.h" #endif #ifdef DEFAULTS_SHERLINE_5400 // Description: Sherline 5400 mill with three NEMA 23 Keling KL23H256-21-8B 185 oz-in stepper motors, // driven by three Pololu A4988 stepper drivers with a 30V, 6A power supply at 1.5A per winding. #include "defaults/defaults_sherline.h" #endif #ifdef DEFAULTS_SHAPEOKO // Description: Shapeoko CNC mill with three NEMA 17 stepper motors, driven by Synthetos // grblShield with a 24V, 4.2A power supply. #include "defaults/defaults_shapeoko.h" #endif #ifdef DEFAULTS_SHAPEOKO_2 // Description: Shapeoko CNC mill with three NEMA 17 stepper motors, driven by Synthetos // grblShield at 28V. #include "defaults/defaults_shapeoko2.h" #endif #ifdef DEFAULTS_SHAPEOKO_3 // Description: Shapeoko CNC mill with three NEMA 23 stepper motors, driven by CarbideMotion #include "defaults/defaults_shapeoko3.h" #endif #ifdef DEFAULTS_X_CARVE_500MM // Description: X-Carve 3D Carver CNC mill with three 200 step/rev motors driven by Synthetos // grblShield at 24V. #include "defaults/defaults_x_carve_500mm.h" #endif #ifdef DEFAULTS_X_CARVE_1000MM // Description: X-Carve 3D Carver CNC mill with three 200 step/rev motors driven by Synthetos // grblShield at 24V. #include "defaults/defaults_x_carve_1000mm.h" #endif #ifdef DEFAULTS_ZEN_TOOLWORKS_7x7 // Description: Zen Toolworks 7x7 mill with three Shinano SST43D2121 65oz-in NEMA 17 stepper motors. // Leadscrew is different from some ZTW kits, where most are 1.25mm/rev rather than 8.0mm/rev here. // Driven by 30V, 6A power supply and TI DRV8811 stepper motor drivers. #include "defaults/defaults_zen_toolworks_7x7.h" #endif #ifdef DEFAULTS_OXCNC // Grbl settings for OpenBuilds OX CNC Machine // http://www.openbuilds.com/builds/openbuilds-ox-cnc-machine.341/ #include "defaults/defaults_oxcnc.h" #endif #ifdef DEFAULTS_SIMULATOR // Settings only for Grbl Simulator (www.github.com/grbl/grbl-sim) #include "defaults/defaults_simulator.h" #endif #endif ================================================ FILE: eeprom.c ================================================ // This file has been prepared for Doxygen automatic documentation generation. /*! \file ******************************************************************** * * Atmel Corporation * * \li File: eeprom.c * \li Compiler: IAR EWAAVR 3.10c * \li Support mail: avr@atmel.com * * \li Supported devices: All devices with split EEPROM erase/write * capabilities can be used. * The example is written for ATmega48. * * \li AppNote: AVR103 - Using the EEPROM Programming Modes. * * \li Description: Example on how to use the split EEPROM erase/write * capabilities in e.g. ATmega48. All EEPROM * programming modes are tested, i.e. Erase+Write, * Erase-only and Write-only. * * $Revision: 1.6 $ * $Date: Friday, February 11, 2005 07:16:44 UTC $ ****************************************************************************/ #include #include /* These EEPROM bits have different names on different devices. */ #ifndef EEPE #define EEPE EEWE //!< EEPROM program/write enable. #define EEMPE EEMWE //!< EEPROM master program/write enable. #endif /* These two are unfortunately not defined in the device include files. */ #define EEPM1 5 //!< EEPROM Programming Mode Bit 1. #define EEPM0 4 //!< EEPROM Programming Mode Bit 0. /* Define to reduce code size. */ #define EEPROM_IGNORE_SELFPROG //!< Remove SPM flag polling. /*! \brief Read byte from EEPROM. * * This function reads one byte from a given EEPROM address. * * \note The CPU is halted for 4 clock cycles during EEPROM read. * * \param addr EEPROM address to read from. * \return The byte read from the EEPROM address. */ unsigned char eeprom_get_char( unsigned int addr ) { do {} while( EECR & (1< 0; size--) { checksum = (checksum << 1) || (checksum >> 7); checksum += *source; eeprom_put_char(destination++, *(source++)); } eeprom_put_char(destination, checksum); } int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) { unsigned char data, checksum = 0; for(; size > 0; size--) { data = eeprom_get_char(source++); checksum = (checksum << 1) || (checksum >> 7); checksum += data; *(destination++) = data; } return(checksum == eeprom_get_char(source)); } // end of file ================================================ FILE: eeprom.h ================================================ /* eeprom.h - EEPROM methods Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud 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 eeprom_h #define eeprom_h unsigned char eeprom_get_char(unsigned int addr); void eeprom_put_char(unsigned int addr, unsigned char new_value); void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size); int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size); #endif ================================================ FILE: examples/grblUpload/grblUpload.ino ================================================ /*********************************************************************** This sketch compiles and uploads Grbl to your 328p-based Arduino! To use: - First make sure you have imported Grbl source code into your Arduino IDE. There are details on our Github website on how to do this. - Select your Arduino Board and Serial Port in the Tools drop-down menu. NOTE: Grbl only officially supports 328p-based Arduinos, like the Uno. Using other boards will likely not work! - Then just click 'Upload'. That's it! For advanced users: If you'd like to see what else Grbl can do, there are some additional options for customization and features you can enable or disable. Navigate your file system to where the Arduino IDE has stored the Grbl source code files, open the 'config.h' file in your favorite text editor. Inside are dozens of feature descriptions and #defines. Simply comment or uncomment the #defines or alter their assigned values, save your changes, and then click 'Upload' here. Copyright (c) 2015 Sungeun K. Jeon Released under the MIT-license. See license.txt for details. ***********************************************************************/ #include // Do not alter this file! ================================================ FILE: examples/grblUpload/license.txt ================================================ The MIT License (MIT) Copyright (c) 2015 Sungeun K. Jeon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: gcode.c ================================================ /* gcode.c - rs274/ngc parser. Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 "grbl.h" // NOTE: Max line number is defined by the g-code standard to be 99999. It seems to be an // arbitrary value, and some GUIs may require more. So we increased it based on a max safe // value when converting a float (7.2 digit precision)s to an integer. #define MAX_LINE_NUMBER 9999999 #define AXIS_COMMAND_NONE 0 #define AXIS_COMMAND_NON_MODAL 1 #define AXIS_COMMAND_MOTION_MODE 2 #define AXIS_COMMAND_TOOL_LENGTH_OFFSET 3 // *Undefined but required // Declare gc extern struct parser_state_t gc_state; parser_block_t gc_block; #define FAIL(status) return(status); void gc_init() { memset(&gc_state, 0, sizeof(gc_state)); // Load default G54 coordinate system. if (!(settings_read_coord_data(gc_state.modal.coord_select,gc_state.coord_system))) { report_status_message(STATUS_SETTING_READ_FAIL); } } // Sets g-code parser position in mm. Input in steps. Called by the system abort and hard // limit pull-off routines. void gc_sync_position() { system_convert_array_steps_to_mpos(gc_state.position,sys.position); } static uint8_t gc_check_same_position(float *pos_a, float *pos_b) { uint8_t idx; for (idx=0; idx 'Z')) { FAIL(STATUS_EXPECTED_COMMAND_LETTER); } // [Expected word letter] char_counter++; if (!read_float(line, &char_counter, &value)) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // [Expected word value] // Convert values to smaller uint8 significand and mantissa values for parsing this word. // NOTE: Mantissa is multiplied by 100 to catch non-integer command values. This is more // accurate than the NIST gcode requirement of x10 when used for commands, but not quite // accurate enough for value words that require integers to within 0.0001. This should be // a good enough comprimise and catch most all non-integer errors. To make it compliant, // we would simply need to change the mantissa to int16, but this add compiled flash space. // Maybe update this later. int_value = trunc(value); mantissa = round(100*(value - int_value)); // Compute mantissa for Gxx.x commands. // NOTE: Rounding must be used to catch small floating point errors. // Check if the g-code word is supported or errors due to modal group violations or has // been repeated in the g-code block. If ok, update the command or record its value. switch(letter) { /* 'G' and 'M' Command Words: Parse commands and check for modal group violations. NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */ case 'G': // Determine 'G' command and its modal group switch(int_value) { case 10: case 28: case 30: case 92: // Check for G10/28/30/92 being called with G0/1/2/3/38 on same block. // * G43.1 is also an axis command but is not explicitly defined this way. if (mantissa == 0) { // Ignore G28.1, G30.1, and G92.1 if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict] axis_command = AXIS_COMMAND_NON_MODAL; } // No break. Continues to next line. case 4: case 53: word_bit = MODAL_GROUP_G0; switch(int_value) { case 4: gc_block.non_modal_command = NON_MODAL_DWELL; break; // G4 case 10: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_DATA; break; // G10 case 28: switch(mantissa) { case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_0; break; // G28 case 10: gc_block.non_modal_command = NON_MODAL_SET_HOME_0; break; // G28.1 default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G28.x command] } mantissa = 0; // Set to zero to indicate valid non-integer G command. break; case 30: switch(mantissa) { case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_1; break; // G30 case 10: gc_block.non_modal_command = NON_MODAL_SET_HOME_1; break; // G30.1 default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G30.x command] } mantissa = 0; // Set to zero to indicate valid non-integer G command. break; case 53: gc_block.non_modal_command = NON_MODAL_ABSOLUTE_OVERRIDE; break; // G53 case 92: switch(mantissa) { case 0: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_OFFSET; break; // G92 case 10: gc_block.non_modal_command = NON_MODAL_RESET_COORDINATE_OFFSET; break; // G92.1 default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G92.x command] } mantissa = 0; // Set to zero to indicate valid non-integer G command. break; } break; case 0: case 1: case 2: case 3: case 38: // Check for G0/1/2/3/38 being called with G10/28/30/92 on same block. // * G43.1 is also an axis command but is not explicitly defined this way. if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict] axis_command = AXIS_COMMAND_MOTION_MODE; // No break. Continues to next line. case 80: word_bit = MODAL_GROUP_G1; switch(int_value) { case 0: gc_block.modal.motion = MOTION_MODE_SEEK; break; // G0 case 1: gc_block.modal.motion = MOTION_MODE_LINEAR; break; // G1 case 2: gc_block.modal.motion = MOTION_MODE_CW_ARC; break; // G2 case 3: gc_block.modal.motion = MOTION_MODE_CCW_ARC; break; // G3 case 38: switch(mantissa) { case 20: gc_block.modal.motion = MOTION_MODE_PROBE_TOWARD; break; // G38.2 case 30: gc_block.modal.motion = MOTION_MODE_PROBE_TOWARD_NO_ERROR; break; // G38.3 case 40: gc_block.modal.motion = MOTION_MODE_PROBE_AWAY; break; // G38.4 case 50: gc_block.modal.motion = MOTION_MODE_PROBE_AWAY_NO_ERROR; break; // G38.5 default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G38.x command] } mantissa = 0; // Set to zero to indicate valid non-integer G command. break; case 80: gc_block.modal.motion = MOTION_MODE_NONE; break; // G80 } break; case 17: case 18: case 19: word_bit = MODAL_GROUP_G2; switch(int_value) { case 17: gc_block.modal.plane_select = PLANE_SELECT_XY; break; case 18: gc_block.modal.plane_select = PLANE_SELECT_ZX; break; case 19: gc_block.modal.plane_select = PLANE_SELECT_YZ; break; } break; case 90: case 91: if (mantissa == 0) { word_bit = MODAL_GROUP_G3; if (int_value == 90) { gc_block.modal.distance = DISTANCE_MODE_ABSOLUTE; } // G90 else { gc_block.modal.distance = DISTANCE_MODE_INCREMENTAL; } // G91 } else { word_bit = MODAL_GROUP_G4; if ((mantissa != 10) || (int_value == 90)) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G90.1 not supported] mantissa = 0; // Set to zero to indicate valid non-integer G command. // Otherwise, arc IJK incremental mode is default. G91.1 does nothing. } break; case 93: case 94: word_bit = MODAL_GROUP_G5; if (int_value == 93) { gc_block.modal.feed_rate = FEED_RATE_MODE_INVERSE_TIME; } // G93 else { gc_block.modal.feed_rate = FEED_RATE_MODE_UNITS_PER_MIN; } // G94 break; case 20: case 21: word_bit = MODAL_GROUP_G6; if (int_value == 20) { gc_block.modal.units = UNITS_MODE_INCHES; } // G20 else { gc_block.modal.units = UNITS_MODE_MM; } // G21 break; case 40: word_bit = MODAL_GROUP_G7; // NOTE: Not required since cutter radius compensation is always disabled. Only here // to support G40 commands that often appear in g-code program headers to setup defaults. // gc_block.modal.cutter_comp = CUTTER_COMP_DISABLE; // G40 break; case 43: case 49: word_bit = MODAL_GROUP_G8; // NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed, // there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49 // all are explicit axis commands, regardless if they require axis words or not. if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict] } axis_command = AXIS_COMMAND_TOOL_LENGTH_OFFSET; if (int_value == 49) { // G49 gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_CANCEL; } else if (mantissa == 10) { // G43.1 gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC; } else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported G43.x command] mantissa = 0; // Set to zero to indicate valid non-integer G command. break; case 54: case 55: case 56: case 57: case 58: case 59: // NOTE: G59.x are not supported. (But their int_values would be 60, 61, and 62.) word_bit = MODAL_GROUP_G12; gc_block.modal.coord_select = int_value-54; // Shift to array indexing. break; case 61: word_bit = MODAL_GROUP_G13; if (mantissa != 0) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G61.1 not supported] // gc_block.modal.control = CONTROL_MODE_EXACT_PATH; // G61 break; default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G command] } if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [Unsupported or invalid Gxx.x command] // Check for more than one command per modal group violations in the current block // NOTE: Variable 'word_bit' is always assigned, if the command is valid. if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); } command_words |= bit(word_bit); break; case 'M': // Determine 'M' command and its modal group if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [No Mxx.x commands] switch(int_value) { case 0: case 1: case 2: case 30: word_bit = MODAL_GROUP_M4; switch(int_value) { case 0: gc_block.modal.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause case 1: break; // Optional stop not supported. Ignore. case 2: case 30: gc_block.modal.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset } break; #ifndef USE_SPINDLE_DIR_AS_ENABLE_PIN case 4: #endif case 3: case 5: word_bit = MODAL_GROUP_M7; switch(int_value) { case 3: gc_block.modal.spindle = SPINDLE_ENABLE_CW; break; #ifndef USE_SPINDLE_DIR_AS_ENABLE_PIN case 4: gc_block.modal.spindle = SPINDLE_ENABLE_CCW; break; #endif case 5: gc_block.modal.spindle = SPINDLE_DISABLE; break; } break; #ifdef ENABLE_M7 case 7: #endif case 8: case 9: word_bit = MODAL_GROUP_M8; switch(int_value) { #ifdef ENABLE_M7 case 7: gc_block.modal.coolant = COOLANT_MIST_ENABLE; break; #endif case 8: gc_block.modal.coolant = COOLANT_FLOOD_ENABLE; break; case 9: gc_block.modal.coolant = COOLANT_DISABLE; break; } break; default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported M command] } // Check for more than one command per modal group violations in the current block // NOTE: Variable 'word_bit' is always assigned, if the command is valid. if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); } command_words |= bit(word_bit); break; // NOTE: All remaining letters assign values. default: /* Non-Command Words: This initial parsing phase only checks for repeats of the remaining legal g-code words and stores their value. Error-checking is performed later since some words (I,J,K,L,P,R) have multiple connotations and/or depend on the issued commands. */ switch(letter){ // case 'A': // Not supported // case 'B': // Not supported // case 'C': // Not supported // case 'D': // Not supported case 'F': word_bit = WORD_F; gc_block.values.f = value; break; // case 'H': // Not supported case 'I': word_bit = WORD_I; gc_block.values.ijk[X_AXIS] = value; ijk_words |= (1< MAX_LINE_NUMBER) { FAIL(STATUS_GCODE_INVALID_LINE_NUMBER); } // [Exceeds max line number] } // bit_false(value_words,bit(WORD_N)); // NOTE: Single-meaning value word. Set at end of error-checking. // Track for unused words at the end of error-checking. // NOTE: Single-meaning value words are removed all at once at the end of error-checking, because // they are always used when present. This was done to save a few bytes of flash. For clarity, the // single-meaning value words may be removed as they are used. Also, axis words are treated in the // same way. If there is an explicit/implicit axis command, XYZ words are always used and are // are removed at the end of error-checking. // [1. Comments ]: MSG's NOT SUPPORTED. Comment handling performed by protocol. // [2. Set feed rate mode ]: G93 F word missing with G1,G2/3 active, implicitly or explicitly. Feed rate // is not defined after switching to G94 from G93. if (gc_block.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { // = G93 // NOTE: G38 can also operate in inverse time, but is undefined as an error. Missing F word check added here. if (axis_command == AXIS_COMMAND_MOTION_MODE) { if ((gc_block.modal.motion != MOTION_MODE_NONE) || (gc_block.modal.motion != MOTION_MODE_SEEK)) { if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [F word missing] } } // NOTE: It seems redundant to check for an F word to be passed after switching from G94 to G93. We would // accomplish the exact same thing if the feed rate value is always reset to zero and undefined after each // inverse time block, since the commands that use this value already perform undefined checks. This would // also allow other commands, following this switch, to execute and not error out needlessly. This code is // combined with the above feed rate mode and the below set feed rate error-checking. // [3. Set feed rate ]: F is negative (done.) // - In inverse time mode: Always implicitly zero the feed rate value before and after block completion. // NOTE: If in G93 mode or switched into it from G94, just keep F value as initialized zero or passed F word // value in the block. If no F word is passed with a motion command that requires a feed rate, this will error // out in the motion modes error-checking. However, if no F word is passed with NO motion command that requires // a feed rate, we simply move on and the state feed rate value gets updated to zero and remains undefined. } else { // = G94 // - In units per mm mode: If F word passed, ensure value is in mm/min, otherwise push last state value. if (gc_state.modal.feed_rate == FEED_RATE_MODE_UNITS_PER_MIN) { // Last state is also G94 if (bit_istrue(value_words,bit(WORD_F))) { if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; } } else { gc_block.values.f = gc_state.feed_rate; // Push last state feed rate } } // Else, switching to G94 from G93, so don't push last state feed rate. Its undefined or the passed F word value. } // bit_false(value_words,bit(WORD_F)); // NOTE: Single-meaning value word. Set at end of error-checking. // [4. Set spindle speed ]: S is negative (done.) if (bit_isfalse(value_words,bit(WORD_S))) { gc_block.values.s = gc_state.spindle_speed; } // bit_false(value_words,bit(WORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking. // [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool value. // bit_false(value_words,bit(WORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking. // [6. Change tool ]: N/A // [7. Spindle control ]: N/A // [8. Coolant control ]: N/A // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED. // [10. Dwell ]: P value missing. P is negative (done.) NOTE: See below. if (gc_block.non_modal_command == NON_MODAL_DWELL) { if (bit_isfalse(value_words,bit(WORD_P))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P word missing] bit_false(value_words,bit(WORD_P)); } // [11. Set active plane ]: N/A switch (gc_block.modal.plane_select) { case PLANE_SELECT_XY: axis_0 = X_AXIS; axis_1 = Y_AXIS; axis_linear = Z_AXIS; break; case PLANE_SELECT_ZX: axis_0 = Z_AXIS; axis_1 = X_AXIS; axis_linear = Y_AXIS; break; default: // case PLANE_SELECT_YZ: axis_0 = Y_AXIS; axis_1 = Z_AXIS; axis_linear = X_AXIS; } // [12. Set length units ]: N/A // Pre-convert XYZ coordinate values to millimeters, if applicable. uint8_t idx; if (gc_block.modal.units == UNITS_MODE_INCHES) { for (idx=0; idx N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys] if (gc_state.modal.coord_select != gc_block.modal.coord_select) { if (!(settings_read_coord_data(gc_block.modal.coord_select,coordinate_data))) { FAIL(STATUS_SETTING_READ_FAIL); } } } // [16. Set path control mode ]: N/A. Only G61. G61.1 and G64 NOT SUPPORTED. // [17. Set distance mode ]: N/A. Only G91.1. G90.1 NOT SUPPORTED. // [18. Set retract mode ]: NOT SUPPORTED. // [19. Remaining non-modal actions ]: Check go to predefined position, set G10, or set axis offsets. // NOTE: We need to separate the non-modal commands that are axis word-using (G10/G28/G30/G92), as these // commands all treat axis words differently. G10 as absolute offsets or computes current position as // the axis value, G92 similarly to G10 L20, and G28/30 as an intermediate target position that observes // all the current coordinate system and G92 offsets. switch (gc_block.non_modal_command) { case NON_MODAL_SET_COORDINATE_DATA: // [G10 Errors]: L missing and is not 2 or 20. P word missing. (Negative P value done.) // [G10 L2 Errors]: R word NOT SUPPORTED. P value not 0 to nCoordSys(max 9). Axis words missing. // [G10 L20 Errors]: P must be 0 to nCoordSys(max 9). Axis words missing. if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS) }; // [No axis words] if (bit_isfalse(value_words,((1< N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys] if (gc_block.values.l != 20) { if (gc_block.values.l == 2) { if (bit_istrue(value_words,bit(WORD_R))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G10 L2 R not supported] } else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported L] } bit_false(value_words,(bit(WORD_L)|bit(WORD_P))); // Determine coordinate system to change and try to load from EEPROM. if (coord_select > 0) { coord_select--; } // Adjust P1-P6 index to EEPROM coordinate data indexing. else { coord_select = gc_block.modal.coord_select; } // Index P0 as the active coordinate system if (!settings_read_coord_data(coord_select,parameter_data)) { FAIL(STATUS_SETTING_READ_FAIL); } // [EEPROM read fail] // Pre-calculate the coordinate data changes. NOTE: Uses parameter_data since coordinate_data may be in use by G54-59. for (idx=0; idx C -----------------+--------------- T <- [x,y] | <------ d/2 ---->| C - Current position T - Target position O - center of circle that pass through both C and T d - distance from C to T r - designated radius h - distance from center of CT to O Expanding the equations: d -> sqrt(x^2 + y^2) h -> sqrt(4 * r^2 - x^2 - y^2)/2 i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2 Which can be written: i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2 Which we for size and speed reasons optimize to: h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2) i = (x - (y * h_x2_div_d))/2 j = (y + (x * h_x2_div_d))/2 */ // First, use h_x2_div_d to compute 4*h^2 to check if it is negative or r is smaller // than d. If so, the sqrt of a negative number is complex and error out. float h_x2_div_d = 4.0 * gc_block.values.r*gc_block.values.r - x*x - y*y; if (h_x2_div_d < 0) { FAIL(STATUS_GCODE_ARC_RADIUS_ERROR); } // [Arc radius error] // Finish computing h_x2_div_d. h_x2_div_d = -sqrt(h_x2_div_d)/hypot_f(x,y); // == -(h * 2 / d) // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below) if (gc_block.modal.motion == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; } /* The counter clockwise circle lies to the left of the target direction. When offset is positive, the left hand circle will be generated - when it is negative the right hand circle is generated. T <-- Target position ^ Clockwise circles with this center | Clockwise circles with this center will have will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing! \ | / center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative | | C <-- Current position */ // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!), // even though it is advised against ever generating such circles in a single line of g-code. By // inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of // travel and thus we get the unadvisably long arcs as prescribed. if (gc_block.values.r < 0) { h_x2_div_d = -h_x2_div_d; gc_block.values.r = -gc_block.values.r; // Finished with r. Set to positive for mc_arc } // Complete the operation by calculating the actual center of the arc gc_block.values.ijk[axis_0] = 0.5*(x-(y*h_x2_div_d)); gc_block.values.ijk[axis_1] = 0.5*(y+(x*h_x2_div_d)); } else { // Arc Center Format Offset Mode if (!(ijk_words & (bit(axis_0)|bit(axis_1)))) { FAIL(STATUS_GCODE_NO_OFFSETS_IN_PLANE); } // [No offsets in plane] bit_false(value_words,(bit(WORD_I)|bit(WORD_J)|bit(WORD_K))); // Convert IJK values to proper units. if (gc_block.modal.units == UNITS_MODE_INCHES) { for (idx=0; idx 0.005) { if (delta_r > 0.5) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Arc definition error] > 0.5mm if (delta_r > (0.001*gc_block.values.r)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Arc definition error] > 0.005mm AND 0.1% radius } } break; case MOTION_MODE_PROBE_TOWARD: case MOTION_MODE_PROBE_TOWARD_NO_ERROR: case MOTION_MODE_PROBE_AWAY: case MOTION_MODE_PROBE_AWAY_NO_ERROR: // [G38 Errors]: Target is same current. No axis words. Cutter compensation is enabled. Feed rate // is undefined. Probe is triggered. NOTE: Probe check moved to probe cycle. Instead of returning // an error, it issues an alarm to prevent further motion to the probe. It's also done there to // allow the planner buffer to empty and move off the probe trigger before another probing cycle. if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words] if (gc_check_same_position(gc_state.position, gc_block.values.xyz)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Invalid target] break; } } } // [21. Program flow ]: No error checks required. // [0. Non-specific error-checks]: Complete unused value words check, i.e. IJK used when in arc // radius mode, or axis words that aren't used in the block. bit_false(value_words,(bit(WORD_N)|bit(WORD_F)|bit(WORD_S)|bit(WORD_T))); // Remove single-meaning value words. if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z))); } // Remove axis words. if (value_words) { FAIL(STATUS_GCODE_UNUSED_WORDS); } // [Unused words] /* ------------------------------------------------------------------------------------- STEP 4: EXECUTE!! Assumes that all error-checking has been completed and no failure modes exist. We just need to update the state and execute the block according to the order-of-execution. */ // [0. Non-specific/common error-checks and miscellaneous setup]: gc_state.line_number = gc_block.values.n; // [1. Comments feedback ]: NOT SUPPORTED // [2. Set feed rate mode ]: gc_state.modal.feed_rate = gc_block.modal.feed_rate; // [3. Set feed rate ]: gc_state.feed_rate = gc_block.values.f; // Always copy this value. See feed rate error-checking. // [4. Set spindle speed ]: if (gc_state.spindle_speed != gc_block.values.s) { // Update running spindle only if not in check mode and not already enabled. if (gc_state.modal.spindle != SPINDLE_DISABLE) { spindle_run(gc_state.modal.spindle, gc_block.values.s); } gc_state.spindle_speed = gc_block.values.s; } // [5. Select tool ]: NOT SUPPORTED. Only tracks tool value. gc_state.tool = gc_block.values.t; // [6. Change tool ]: NOT SUPPORTED // [7. Spindle control ]: if (gc_state.modal.spindle != gc_block.modal.spindle) { // Update spindle control and apply spindle speed when enabling it in this block. spindle_run(gc_block.modal.spindle, gc_state.spindle_speed); gc_state.modal.spindle = gc_block.modal.spindle; } // [8. Coolant control ]: if (gc_state.modal.coolant != gc_block.modal.coolant) { coolant_run(gc_block.modal.coolant); gc_state.modal.coolant = gc_block.modal.coolant; } // [9. Enable/disable feed rate or spindle overrides ]: NOT SUPPORTED // [10. Dwell ]: if (gc_block.non_modal_command == NON_MODAL_DWELL) { mc_dwell(gc_block.values.p); } // [11. Set active plane ]: gc_state.modal.plane_select = gc_block.modal.plane_select; // [12. Set length units ]: gc_state.modal.units = gc_block.modal.units; // [13. Cutter radius compensation ]: G41/42 NOT SUPPORTED // gc_state.modal.cutter_comp = gc_block.modal.cutter_comp; // NOTE: Not needed since always disabled. // [14. Cutter length compensation ]: G43.1 and G49 supported. G43 NOT SUPPORTED. // NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms // of execution. The error-checking step would simply load the offset value into the correct // axis of the block XYZ value array. if (axis_command == AXIS_COMMAND_TOOL_LENGTH_OFFSET ) { // Indicates a change. gc_state.modal.tool_length = gc_block.modal.tool_length; if (gc_state.modal.tool_length == TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC) { // G43.1 gc_state.tool_length_offset = gc_block.values.xyz[TOOL_LENGTH_OFFSET_AXIS]; } else { // G49 gc_state.tool_length_offset = 0.0; } } // [15. Coordinate system selection ]: if (gc_state.modal.coord_select != gc_block.modal.coord_select) { gc_state.modal.coord_select = gc_block.modal.coord_select; memcpy(gc_state.coord_system,coordinate_data,sizeof(coordinate_data)); } // [16. Set path control mode ]: G61.1/G64 NOT SUPPORTED // gc_state.modal.control = gc_block.modal.control; // NOTE: Always default. // [17. Set distance mode ]: gc_state.modal.distance = gc_block.modal.distance; // [18. Set retract mode ]: NOT SUPPORTED // [19. Go to predefined position, Set G10, or Set axis offsets ]: switch(gc_block.non_modal_command) { case NON_MODAL_SET_COORDINATE_DATA: settings_write_coord_data(coord_select,parameter_data); // Update system coordinate system if currently active. if (gc_state.modal.coord_select == coord_select) { memcpy(gc_state.coord_system,parameter_data,sizeof(parameter_data)); } break; case NON_MODAL_GO_HOME_0: case NON_MODAL_GO_HOME_1: // Move to intermediate position before going home. Obeys current coordinate system and offsets // and absolute and incremental modes. if (axis_command) { #ifdef USE_LINE_NUMBERS mc_line(gc_block.values.xyz, -1.0, false, gc_state.line_number); #else mc_line(gc_block.values.xyz, -1.0, false); #endif } #ifdef USE_LINE_NUMBERS mc_line(parameter_data, -1.0, false, gc_state.line_number); #else mc_line(parameter_data, -1.0, false); #endif memcpy(gc_state.position, parameter_data, sizeof(parameter_data)); break; case NON_MODAL_SET_HOME_0: settings_write_coord_data(SETTING_INDEX_G28,gc_state.position); break; case NON_MODAL_SET_HOME_1: settings_write_coord_data(SETTING_INDEX_G30,gc_state.position); break; case NON_MODAL_SET_COORDINATE_OFFSET: memcpy(gc_state.coord_offset,gc_block.values.xyz,sizeof(gc_block.values.xyz)); break; case NON_MODAL_RESET_COORDINATE_OFFSET: clear_vector(gc_state.coord_offset); // Disable G92 offsets by zeroing offset vector. break; } // [20. Motion modes ]: // NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes. // Enter motion modes only if there are axis words or a motion mode command word in the block. gc_state.modal.motion = gc_block.modal.motion; if (gc_state.modal.motion != MOTION_MODE_NONE) { if (axis_command == AXIS_COMMAND_MOTION_MODE) { switch (gc_state.modal.motion) { case MOTION_MODE_SEEK: #ifdef USE_LINE_NUMBERS mc_line(gc_block.values.xyz, -1.0, false, gc_state.line_number); #else mc_line(gc_block.values.xyz, -1.0, false); #endif break; case MOTION_MODE_LINEAR: #ifdef USE_LINE_NUMBERS mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, gc_state.line_number); #else mc_line(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate); #endif break; case MOTION_MODE_CW_ARC: #ifdef USE_LINE_NUMBERS mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r, gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear, true, gc_state.line_number); #else mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r, gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear, true); #endif break; case MOTION_MODE_CCW_ARC: #ifdef USE_LINE_NUMBERS mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r, gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear, false, gc_state.line_number); #else mc_arc(gc_state.position, gc_block.values.xyz, gc_block.values.ijk, gc_block.values.r, gc_state.feed_rate, gc_state.modal.feed_rate, axis_0, axis_1, axis_linear, false); #endif break; case MOTION_MODE_PROBE_TOWARD: // NOTE: gc_block.values.xyz is returned from mc_probe_cycle with the updated position value. So // upon a successful probing cycle, the machine position and the returned value should be the same. #ifdef USE_LINE_NUMBERS mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, false, false, gc_state.line_number); #else mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, false, false); #endif break; case MOTION_MODE_PROBE_TOWARD_NO_ERROR: #ifdef USE_LINE_NUMBERS mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, false, true, gc_state.line_number); #else mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, false, true); #endif break; case MOTION_MODE_PROBE_AWAY: #ifdef USE_LINE_NUMBERS mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, true, false, gc_state.line_number); #else mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, true, false); #endif break; case MOTION_MODE_PROBE_AWAY_NO_ERROR: #ifdef USE_LINE_NUMBERS mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, true, true, gc_state.line_number); #else mc_probe_cycle(gc_block.values.xyz, gc_state.feed_rate, gc_state.modal.feed_rate, true, true); #endif } // As far as the parser is concerned, the position is now == target. In reality the // motion control system might still be processing the action and the real tool position // in any intermediate location. memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); // gc_state.position[] = gc_block.values.xyz[] } } // [21. Program flow ]: // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may // refill and can only be resumed by the cycle start run-time command. gc_state.modal.program_flow = gc_block.modal.program_flow; if (gc_state.modal.program_flow) { protocol_buffer_synchronize(); // Sync and finish all remaining buffered motions before moving on. if (gc_state.modal.program_flow == PROGRAM_FLOW_PAUSED) { if (sys.state != STATE_CHECK_MODE) { bit_true_atomic(sys.rt_exec_state, EXEC_FEED_HOLD); // Use feed hold for program pause. protocol_execute_realtime(); // Execute suspend. } } else { // == PROGRAM_FLOW_COMPLETED // Upon program complete, only a subset of g-codes reset to certain defaults, according to // LinuxCNC's program end descriptions and testing. Only modal groups [G-code 1,2,3,5,7,12] // and [M-code 7,8,9] reset to [G1,G17,G90,G94,G40,G54,M5,M9,M48]. The remaining modal groups // [G-code 4,6,8,10,13,14,15] and [M-code 4,5,6] and the modal words [F,S,T,H] do not reset. gc_state.modal.motion = MOTION_MODE_LINEAR; gc_state.modal.plane_select = PLANE_SELECT_XY; gc_state.modal.distance = DISTANCE_MODE_ABSOLUTE; gc_state.modal.feed_rate = FEED_RATE_MODE_UNITS_PER_MIN; // gc_state.modal.cutter_comp = CUTTER_COMP_DISABLE; // Not supported. gc_state.modal.coord_select = 0; // G54 gc_state.modal.spindle = SPINDLE_DISABLE; gc_state.modal.coolant = COOLANT_DISABLE; // gc_state.modal.override = OVERRIDE_DISABLE; // Not supported. // Execute coordinate change and spindle/coolant stop. if (sys.state != STATE_CHECK_MODE) { if (!(settings_read_coord_data(gc_state.modal.coord_select,coordinate_data))) { FAIL(STATUS_SETTING_READ_FAIL); } memcpy(gc_state.coord_system,coordinate_data,sizeof(coordinate_data)); spindle_stop(); coolant_stop(); } report_feedback_message(MESSAGE_PROGRAM_END); } gc_state.modal.program_flow = PROGRAM_FLOW_RUNNING; // Reset program flow. } // TODO: % to denote start of program. return(STATUS_OK); } /* Not supported: - Canned cycles - Tool radius compensation - A,B,C-axes - Evaluation of expressions - Variables - Override control (TBD) - Tool changes - Switches (*) Indicates optional parameter, enabled through config.h and re-compile group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets) group 1 = {G81 - G89} (Motion modes: Canned cycles) group 4 = {M1} (Optional stop, ignored) group 6 = {M6} (Tool change) group 7 = {G41, G42} cutter radius compensation (G40 is supported) group 8 = {G43} tool length offset (G43.1/G49 are supported) group 8 = {*M7} enable mist coolant (* Compile-option) group 9 = {M48, M49} enable/disable feed and speed override switches group 10 = {G98, G99} return mode canned cycles group 13 = {G61.1, G64} path control mode (G61 is supported) */ ================================================ FILE: gcode.h ================================================ /* gcode.h - rs274/ngc parser. Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 gcode_h #define gcode_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 // mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute // a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online, // and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc). // NOTE: Modal group define values must be sequential and starting from zero. #define MODAL_GROUP_G0 0 // [G4,G10,G28,G28.1,G30,G30.1,G53,G92,G92.1] Non-modal #define MODAL_GROUP_G1 1 // [G0,G1,G2,G3,G38.2,G38.3,G38.4,G38.5,G80] Motion #define MODAL_GROUP_G2 2 // [G17,G18,G19] Plane selection #define MODAL_GROUP_G3 3 // [G90,G91] Distance mode #define MODAL_GROUP_G4 4 // [G91.1] Arc IJK distance mode #define MODAL_GROUP_G5 5 // [G93,G94] Feed rate mode #define MODAL_GROUP_G6 6 // [G20,G21] Units #define MODAL_GROUP_G7 7 // [G40] Cutter radius compensation mode. G41/42 NOT SUPPORTED. #define MODAL_GROUP_G8 8 // [G43.1,G49] Tool length offset #define MODAL_GROUP_G12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection #define MODAL_GROUP_G13 10 // [G61] Control mode #define MODAL_GROUP_M4 11 // [M0,M1,M2,M30] Stopping #define MODAL_GROUP_M7 12 // [M3,M4,M5] Spindle turning #define MODAL_GROUP_M8 13 // [M7,M8,M9] Coolant control // #define OTHER_INPUT_F 14 // #define OTHER_INPUT_S 15 // #define OTHER_INPUT_T 16 // Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used // internally by the parser to know which command to execute. // Modal Group G0: Non-modal actions #define NON_MODAL_NO_ACTION 0 // (Default: Must be zero) #define NON_MODAL_DWELL 1 // G4 #define NON_MODAL_SET_COORDINATE_DATA 2 // G10 #define NON_MODAL_GO_HOME_0 3 // G28 #define NON_MODAL_SET_HOME_0 4 // G28.1 #define NON_MODAL_GO_HOME_1 5 // G30 #define NON_MODAL_SET_HOME_1 6 // G30.1 #define NON_MODAL_ABSOLUTE_OVERRIDE 7 // G53 #define NON_MODAL_SET_COORDINATE_OFFSET 8 // G92 #define NON_MODAL_RESET_COORDINATE_OFFSET 9 //G92.1 // Modal Group G1: Motion modes #define MOTION_MODE_SEEK 0 // G0 (Default: Must be zero) #define MOTION_MODE_LINEAR 1 // G1 #define MOTION_MODE_CW_ARC 2 // G2 #define MOTION_MODE_CCW_ARC 3 // G3 #define MOTION_MODE_PROBE_TOWARD 4 // G38.2 NOTE: G38.2, G38.3, G38.4, G38.5 must be sequential. See report_gcode_modes(). #define MOTION_MODE_PROBE_TOWARD_NO_ERROR 5 // G38.3 #define MOTION_MODE_PROBE_AWAY 6 // G38.4 #define MOTION_MODE_PROBE_AWAY_NO_ERROR 7 // G38.5 #define MOTION_MODE_NONE 8 // G80 // Modal Group G2: Plane select #define PLANE_SELECT_XY 0 // G17 (Default: Must be zero) #define PLANE_SELECT_ZX 1 // G18 #define PLANE_SELECT_YZ 2 // G19 // Modal Group G3: Distance mode #define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero) #define DISTANCE_MODE_INCREMENTAL 1 // G91 // Modal Group G4: Arc IJK distance mode #define DISTANCE_ARC_MODE_INCREMENTAL 0 // G91.1 (Default: Must be zero) // Modal Group M4: Program flow #define PROGRAM_FLOW_RUNNING 0 // (Default: Must be zero) #define PROGRAM_FLOW_PAUSED 1 // M0, M1 #define PROGRAM_FLOW_COMPLETED 2 // M2, M30 // Modal Group G5: Feed rate mode #define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero) #define FEED_RATE_MODE_INVERSE_TIME 1 // G93 // Modal Group G6: Units mode #define UNITS_MODE_MM 0 // G21 (Default: Must be zero) #define UNITS_MODE_INCHES 1 // G20 // Modal Group G7: Cutter radius compensation mode #define CUTTER_COMP_DISABLE 0 // G40 (Default: Must be zero) // Modal Group G13: Control mode #define CONTROL_MODE_EXACT_PATH 0 // G61 (Default: Must be zero) // Modal Group M7: Spindle control #define SPINDLE_DISABLE 0 // M5 (Default: Must be zero) #define SPINDLE_ENABLE_CW 1 // M3 #define SPINDLE_ENABLE_CCW 2 // M4 // Modal Group M8: Coolant control #define COOLANT_DISABLE 0 // M9 (Default: Must be zero) #define COOLANT_MIST_ENABLE 1 // M7 #define COOLANT_FLOOD_ENABLE 2 // M8 // Modal Group G8: Tool length offset #define TOOL_LENGTH_OFFSET_CANCEL 0 // G49 (Default: Must be zero) #define TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC 1 // G43.1 // Modal Group G12: Active work coordinate system // N/A: Stores coordinate system value (54-59) to change to. // Define parameter word mapping. #define WORD_F 0 #define WORD_I 1 #define WORD_J 2 #define WORD_K 3 #define WORD_L 4 #define WORD_N 5 #define WORD_P 6 #define WORD_R 7 #define WORD_S 8 #define WORD_T 9 #define WORD_X 10 #define WORD_Y 11 #define WORD_Z 12 // NOTE: When this struct is zeroed, the above defines set the defaults for the system. typedef struct { uint8_t motion; // {G0,G1,G2,G3,G38.2,G80} uint8_t feed_rate; // {G93,G94} uint8_t units; // {G20,G21} uint8_t distance; // {G90,G91} // uint8_t distance_arc; // {G91.1} NOTE: Don't track. Only default supported. uint8_t plane_select; // {G17,G18,G19} // uint8_t cutter_comp; // {G40} NOTE: Don't track. Only default supported. uint8_t tool_length; // {G43.1,G49} uint8_t coord_select; // {G54,G55,G56,G57,G58,G59} // uint8_t control; // {G61} NOTE: Don't track. Only default supported. uint8_t program_flow; // {M0,M1,M2,M30} uint8_t coolant; // {M7,M8,M9} uint8_t spindle; // {M3,M4,M5} } gc_modal_t; typedef struct { float f; // Feed float ijk[3]; // I,J,K Axis arc offsets uint8_t l; // G10 or canned cycles parameters int32_t n; // Line number float p; // G10 or dwell parameters // float q; // G82 peck drilling float r; // Arc radius float s; // Spindle speed uint8_t t; // Tool selection float xyz[3]; // X,Y,Z Translational axes } gc_values_t; typedef struct { gc_modal_t modal; float spindle_speed; // RPM float feed_rate; // Millimeters/min uint8_t tool; // Tracks tool number. NOT USED. int32_t line_number; // Last line number sent float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine // position in mm. Loaded from EEPROM when called. float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to // machine zero in mm. Non-persistent. Cleared upon reset and boot. float tool_length_offset; // Tracks tool length offset value when enabled. } parser_state_t; extern parser_state_t gc_state; typedef struct { // uint16_t command_words; // NOTE: If this bitflag variable fills, G and M words can be separated. // uint16_t value_words; uint8_t non_modal_command; gc_modal_t modal; gc_values_t values; } parser_block_t; extern parser_block_t gc_block; // Initialize the parser void gc_init(); // Execute one block of rs275/ngc/g-code uint8_t gc_execute_line(char *line); // Set g-code parser position. Input in steps. void gc_sync_position(); #endif ================================================ FILE: grbl.h ================================================ /* grbl.h - main Grbl include file Part of Grbl Copyright (c) 2015 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 grbl_h #define grbl_h // Grbl versioning system #define GRBL_VERSION "0.9i" #define GRBL_VERSION_BUILD "20150620" // Define standard libraries used by Grbl. #include #include #include #include #include #include #include #include #include #include #include // Define the Grbl system include files. NOTE: Do not alter organization. #include "config.h" #include "nuts_bolts.h" #include "settings.h" #include "system.h" #include "defaults.h" #include "cpu_map.h" #include "coolant_control.h" #include "eeprom.h" #include "gcode.h" #include "limits.h" #include "motion_control.h" #include "planner.h" #include "print.h" #include "probe.h" #include "protocol.h" #include "report.h" #include "serial.h" #include "spindle_control.h" #include "stepper.h" #endif ================================================ FILE: limits.c ================================================ /* limits.c - code pertaining to limit-switches and performing the homing cycle Part of Grbl Copyright (c) 2012-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 "grbl.h" // Homing axis search distance multiplier. Computed by this value times the cycle travel. #ifndef HOMING_AXIS_SEARCH_SCALAR #define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged. #endif #ifndef HOMING_AXIS_LOCATE_SCALAR #define HOMING_AXIS_LOCATE_SCALAR 5.0 // Must be > 1 to ensure limit switch is cleared. #endif void limits_init() { LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins #ifdef DISABLE_LIMIT_PIN_PULL_UP LIMIT_PORT &= ~(LIMIT_MASK); // Normal low operation. Requires external pull-down. #else LIMIT_PORT |= (LIMIT_MASK); // Enable internal pull-up resistors. Normal high operation. #endif 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 } else { limits_disable(); } #ifdef ENABLE_SOFTWARE_DEBOUNCE MCUSR &= ~(1< 0); // The active cycle axes should now be homed and machine limits have been located. By // default, Grbl defines machine space as all negative, as do most CNCs. Since limit switches // can be on either side of an axes, check and set axes machine zero appropriately. Also, // set up pull-off maneuver from axes limit switches that have been homed. This provides // some initial clearance off the switches and should also help prevent them from falsely // triggering when hard limits are enabled or when more than one axes shares a limit pin. #ifdef COREXY int32_t off_axis_position = 0; #endif int32_t set_axis_position; // Set machine positions for homed limit switches. Don't update non-homed axes. for (idx=0; idx -settings.max_travel[idx]) { soft_limit_error = true; } } else { if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; } } #else // NOTE: max_travel is stored as negative if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { soft_limit_error = true; } #endif if (soft_limit_error) { // Force feed hold if cycle is active. All buffered blocks are guaranteed to be within // workspace volume so just come to a controlled stop so position is not lost. When complete // enter alarm mode. if (sys.state == STATE_CYCLE) { bit_true_atomic(sys.rt_exec_state, EXEC_FEED_HOLD); do { protocol_execute_realtime(); if (sys.abort) { return; } } while ( sys.state != STATE_IDLE ); } mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. bit_true_atomic(sys.rt_exec_alarm, (EXEC_ALARM_SOFT_LIMIT|EXEC_CRITICAL_EVENT)); // Indicate soft limit critical event protocol_execute_realtime(); // Execute to enter critical event loop and system abort return; } } } ================================================ FILE: limits.h ================================================ /* limits.h - code pertaining to limit-switches and performing the homing cycle Part of Grbl Copyright (c) 2012-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 limits_h #define limits_h // Initialize the limits module void limits_init(); // Disables hard limits. void limits_disable(); // Returns limit state as a bit-wise uint8 variable. uint8_t limits_get_state(); // Perform one portion of the homing cycle based on the input settings. void limits_go_home(uint8_t cycle_mask); // Check for soft limit violations void limits_soft_check(float *target); #endif ================================================ FILE: main.c ================================================ /* main.c - An embedded CNC Controller with rs274/ngc (g-code) support Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 "grbl.h" // Declare system global variable structure system_t sys; int main(void) { // Initialize system upon power-up. serial_init(); // Setup serial baud rate and interrupts settings_init(); // Load Grbl settings from EEPROM stepper_init(); // Configure stepper pins and interrupt timers system_init(); // Configure pinout pins and pin-change interrupt memset(&sys, 0, sizeof(sys)); // Clear all system variables sys.abort = true; // Set abort to complete initialization sei(); // Enable interrupts // 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 (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; } #endif // Force Grbl into an ALARM state upon a power-cycle or hard reset. #ifdef FORCE_INITIALIZATION_ALARM sys.state = STATE_ALARM; #endif // Grbl initialization loop upon power-up or a system abort. For the latter, all processes // will return to this loop to be cleanly re-initialized. for(;;) { // TODO: Separate configure task that require interrupts to be disabled, especially upon // a system abort and ensuring any active interrupts are cleanly reset. // Reset Grbl primary systems. serial_reset_read_buffer(); // Clear serial read buffer gc_init(); // Set g-code parser to default state spindle_init(); coolant_init(); limits_init(); probe_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. plan_sync_position(); gc_sync_position(); // Reset system variables. sys.abort = false; sys.rt_exec_state = 0; sys.rt_exec_alarm = 0; sys.suspend = false; // Start Grbl main loop. Processes program inputs and executes them. protocol_main_loop(); } return 0; /* Never reached */ } ================================================ FILE: motion_control.c ================================================ /* motion_control.c - high level interface for issuing motion commands Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2011 Simen Svale Skogsrud 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 "grbl.h" // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in // (1 minute)/feed_rate time. // NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line // segments, must pass through this routine before being passed to the planner. The seperation of // mc_line and plan_buffer_line is done primarily to place non-planner-type functions from being // in the planner and to let backlash compensation or canned cycle integration simple and direct. #ifdef USE_LINE_NUMBERS void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number) #else void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate) #endif { // If enabled, check for soft limit violations. Placed here all line motions are picked up // from everywhere in Grbl. if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) { limits_soft_check(target); } // If in check gcode mode, prevent motion by blocking planner. Soft limits still work. if (sys.state == STATE_CHECK_MODE) { return; } // NOTE: Backlash compensation may be installed here. It will need direction info to track when // to insert a backlash line motion(s) before the intended line motion and will require its own // plan_check_full_buffer() and check for system abort loop. Also for position reporting // backlash steps will need to be also tracked, which will need to be kept at a system level. // There are likely some other things that will need to be tracked as well. However, we feel // that backlash compensation should NOT be handled by Grbl itself, because there are a myriad // of ways to implement it and can be effective or ineffective for different CNC machines. This // would be better handled by the interface as a post-processor task, where the original g-code // is translated and inserts backlash motions that best suits the machine. // NOTE: Perhaps as a middle-ground, all that needs to be sent is a flag or special command that // indicates to Grbl what is a backlash compensation motion, so that Grbl executes the move but // doesn't update the machine position values. Since the position values used by the g-code // parser and planner are separate from the system machine positions, this is doable. // If the buffer is full: good! That means we are well ahead of the robot. // Remain in this loop until there is room in the buffer. do { protocol_execute_realtime(); // Check for any run-time commands if (sys.abort) { return; } // Bail, if system abort. if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full. else { break; } } while (1); // Plan and queue motion into planner buffer #ifdef USE_LINE_NUMBERS plan_buffer_line(target, feed_rate, invert_feed_rate, line_number); #else plan_buffer_line(target, feed_rate, invert_feed_rate); #endif } // Execute an arc in offset mode format. position == current xyz, target == target xyz, // offset == offset from current xyz, axis_X defines circle plane in tool space, axis_linear is // the direction of helical travel, radius == circle radius, isclockwise boolean. Used // for vector transformation direction. // The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance // of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal // distance from segment to the circle when the end points both lie on the circle. #ifdef USE_LINE_NUMBERS void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc, int32_t line_number) #else void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc) #endif { float center_axis0 = position[axis_0] + offset[axis_0]; float center_axis1 = position[axis_1] + offset[axis_1]; float r_axis0 = -offset[axis_0]; // Radius vector from center to current location float r_axis1 = -offset[axis_1]; float rt_axis0 = target[axis_0] - center_axis0; float rt_axis1 = target[axis_1] - center_axis1; // CCW angle between position and target from circle center. Only one atan2() trig computation required. float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1); if (is_clockwise_arc) { // Correct atan2 output per direction if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel -= 2*M_PI; } } else { if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel += 2*M_PI; } } // NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to // (2x) settings.arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit // is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation. // For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases. uint16_t segments = floor(fabs(0.5*angular_travel*radius)/ 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 // by a number of discrete segments. The inverse feed_rate should be correct for the sum of // all segments. if (invert_feed_rate) { feed_rate *= segments; } float theta_per_segment = angular_travel/segments; float linear_per_segment = (target[axis_linear] - position[axis_linear])/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); sin(phi) cos(phi] * r ; For arc generation, the center of the circle is the axis of rotation and the radius vector is defined from the circle center to the initial position. Each line segment is formed by successive vector rotations. Single precision values can accumulate error greater than tool precision in rare cases. So, exact arc path correction is implemented. This approach avoids the problem of too many very expensive trig operations [sin(),cos(),tan()] which can take 100-200 usec each to compute. Small angle approximation may be used to reduce computation overhead further. A third-order approximation (second order sin() has too much error) holds for most, if not, all CNC applications. Note that this approximation will begin to accumulate a numerical drift error when theta_per_segment is greater than ~0.25 rad(14 deg) AND the approximation is successively used without correction several dozen times. This scenario is extremely unlikely, since segment lengths and theta_per_segment are automatically generated and scaled by the arc tolerance setting. Only a very large arc tolerance setting, unrealistic for CNC applications, would cause this numerical drift error. However, it is best to set N_ARC_CORRECTION from a low of ~4 to a high of ~20 or so to avoid trig operations while keeping arc generation accurate. This approximation also allows mc_arc to immediately insert a line segment into the planner without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead. This is important when there are successive arc motions. */ // Computes: cos_T = 1 - theta_per_segment^2/2, sin_T = theta_per_segment - theta_per_segment^3/6) in ~52usec float cos_T = 2.0 - theta_per_segment*theta_per_segment; float sin_T = theta_per_segment*0.16666667*(cos_T + 4.0); cos_T *= 0.5; float sin_Ti; float cos_Ti; float r_axisi; uint16_t i; uint8_t count = 0; for (i = 1; i 0) { // NOTE: Check and execute realtime commands during dwell every <= DWELL_TIME_STEP milliseconds. protocol_execute_realtime(); if (sys.abort) { return; } _delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment } } // Perform homing cycle to locate and set machine zero. Only '$H' executes this command. // NOTE: There should be no motions in the buffer and Grbl must be in an idle state before // executing the homing cycle. This prevents incorrect buffered plans after homing. void mc_homing_cycle() { // Check and abort homing cycle, if hard limits are already enabled. Helps prevent problems // with machines with limits wired on both ends of travel to one limit pin. // TODO: Move the pin-specific LIMIT_PIN call to limits.c as a function. #ifdef LIMITS_TWO_SWITCHES_ON_AXES if (limits_get_state()) { mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown. bit_true_atomic(sys.rt_exec_alarm, (EXEC_ALARM_HARD_LIMIT|EXEC_CRITICAL_EVENT)); return; } #endif limits_disable(); // Disable hard limits pin change register for cycle duration // ------------------------------------------------------------------------------------- // Perform homing routine. NOTE: Special motion case. Only system reset works. // Search to engage all axes limit switches at faster homing seek rate. limits_go_home(HOMING_CYCLE_0); // Homing cycle 0 #ifdef HOMING_CYCLE_1 limits_go_home(HOMING_CYCLE_1); // Homing cycle 1 #endif #ifdef HOMING_CYCLE_2 limits_go_home(HOMING_CYCLE_2); // Homing cycle 2 #endif protocol_execute_realtime(); // Check for reset and set system abort. if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. // Homing cycle complete! Setup system for normal operation. // ------------------------------------------------------------------------------------- // Gcode parser position was circumvented by the limits_go_home() routine, so sync position now. gc_sync_position(); // If hard limits feature enabled, re-enable hard limits pin change register after homing cycle. limits_init(); } // Perform tool length probe cycle. Requires probe switch. // NOTE: Upon probe failure, the program will be stopped and placed into ALARM state. #ifdef USE_LINE_NUMBERS void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away, uint8_t is_no_error, int32_t line_number) #else void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away, uint8_t is_no_error) #endif { // TODO: Need to update this cycle so it obeys a non-auto cycle start. if (sys.state == STATE_CHECK_MODE) { return; } // Finish all queued commands and empty planner buffer before starting probe cycle. protocol_buffer_synchronize(); // Initialize probing control variables sys.probe_succeeded = false; // Re-initialize probe history before beginning cycle. probe_configure_invert_mask(is_probe_away); // After syncing, check if probe is already triggered. If so, halt and issue alarm. // NOTE: This probe initialization error applies to all probing cycles. if ( probe_get_state() ) { // Check probe pin state. bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_PROBE_FAIL); protocol_execute_realtime(); } if (sys.abort) { return; } // Return if system reset has been issued. // Setup and queue probing motion. Auto cycle-start should not start the cycle. #ifdef USE_LINE_NUMBERS mc_line(target, feed_rate, invert_feed_rate, line_number); #else mc_line(target, feed_rate, invert_feed_rate); #endif // Activate the probing state monitor in the stepper module. sys.probe_state = PROBE_ACTIVE; // Perform probing cycle. Wait here until probe is triggered or motion completes. bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); do { protocol_execute_realtime(); if (sys.abort) { return; } // Check for system abort } while (sys.state != STATE_IDLE); // Probing cycle complete! // Set state variables and error out, if the probe failed and cycle with error is enabled. if (sys.probe_state == PROBE_ACTIVE) { if (is_no_error) { memcpy(sys.probe_position, sys.position, sizeof(float)*N_AXIS); } else { bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_PROBE_FAIL); } } else { sys.probe_succeeded = true; // Indicate to system the probing cycle completed successfully. } sys.probe_state = PROBE_OFF; // Ensure probe state monitor is disabled. protocol_execute_realtime(); // Check and execute run-time commands if (sys.abort) { return; } // Check for system abort // Reset the stepper and planner buffers to remove the remainder of the probe motion. st_reset(); // Reest step segment buffer. plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared. plan_sync_position(); // Sync planner position to current machine position. // TODO: Update the g-code parser code to not require this target calculation but uses a gc_sync_position() call. // NOTE: The target[] variable updated here will be sent back and synced with the g-code parser. system_convert_array_steps_to_mpos(target, sys.position); #ifdef MESSAGE_PROBE_COORDINATES // All done! Output the probe position as message. report_probe_parameters(); #endif } // Method to ready the system to reset by setting the realtime 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 // realtime abort command and hard limits. So, keep to a minimum. void mc_reset() { // Only this function can set the system reset. Helps prevent multiple kill calls. if (bit_isfalse(sys.rt_exec_state, EXEC_RESET)) { bit_true_atomic(sys.rt_exec_state, EXEC_RESET); // Kill spindle and coolant. spindle_stop(); coolant_stop(); // Kill steppers only if in any motion state, i.e. cycle, actively holding, or homing. // 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. if ((sys.state & (STATE_CYCLE | STATE_HOMING)) || (sys.suspend == SUSPEND_ENABLE_HOLD)) { if (sys.state == STATE_HOMING) { bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_HOMING_FAIL); } else { bit_true_atomic(sys.rt_exec_alarm, EXEC_ALARM_ABORT_CYCLE); } st_go_idle(); // Force kill steppers. Position has likely been lost. } } } ================================================ FILE: motion_control.h ================================================ /* motion_control.h - high level interface for issuing motion commands Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 motion_control_h #define motion_control_h #define HOMING_CYCLE_LINE_NUMBER -1 // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in // (1 minute)/feed_rate time. #ifdef USE_LINE_NUMBERS void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number); #else void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate); #endif // Execute an arc in offset mode format. position == current xyz, target == target xyz, // offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is // the direction of helical travel, radius == circle radius, is_clockwise_arc boolean. Used // for vector transformation direction. #ifdef USE_LINE_NUMBERS void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc, int32_t line_number); #else void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc); #endif // Dwell for a specific number of seconds void mc_dwell(float seconds); // Perform homing cycle to locate machine zero. Requires limit switches. void mc_homing_cycle(); // Perform tool length probe cycle. Requires probe switch. #ifdef USE_LINE_NUMBERS void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away, uint8_t is_no_error, int32_t line_number); #else void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, uint8_t is_probe_away, uint8_t is_no_error); #endif // Performs system reset. If in motion state, kills all motion and sets system alarm. void mc_reset(); #endif ================================================ FILE: nuts_bolts.c ================================================ /* nuts_bolts.c - Shared functions Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 "grbl.h" #define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float) // Extracts a floating point value from a string. The following code is based loosely on // the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely // available conversion method examples, but has been highly optimized for Grbl. For known // CNC applications, the typical decimal value is expected to be in the range of E0 to E-4. // Scientific notation is officially not supported by g-code, and the 'E' character may // be a g-code word on some CNC systems. So, 'E' notation will not be recognized. // NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod(). uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr) { char *ptr = line + *char_counter; unsigned char c; // Grab first character and increment pointer. No spaces assumed in line. c = *ptr++; // Capture initial positive/minus character bool isnegative = false; if (c == '-') { isnegative = true; c = *ptr++; } else if (c == '+') { c = *ptr++; } // Extract number into fast integer. Track decimal in terms of exponent value. uint32_t intval = 0; int8_t exp = 0; uint8_t ndigit = 0; bool isdecimal = false; while(1) { c -= '0'; if (c <= 9) { ndigit++; if (ndigit <= MAX_INT_DIGITS) { if (isdecimal) { exp--; } intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c } else { if (!(isdecimal)) { exp++; } // Drop overflow digits } } else if (c == (('.'-'0') & 0xff) && !(isdecimal)) { isdecimal = true; } else { break; } c = *ptr++; } // Return if no digits have been read. if (!ndigit) { return(false); }; // Convert integer into floating point. float fval; fval = (float)intval; // Apply decimal. Should perform no more than two floating point multiplications for the // expected range of E0 to E-4. if (fval != 0) { while (exp <= -2) { fval *= 0.01; exp += 2; } if (exp < 0) { fval *= 0.1; } else if (exp > 0) { do { fval *= 10.0; } while (--exp > 0); } } // Assign floating point value with correct sign. if (isnegative) { *float_ptr = -fval; } else { *float_ptr = fval; } *char_counter = ptr - line - 1; // Set char_counter to next statement return(true); } // Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(), // which only accepts constants in future compiler releases. void delay_ms(uint16_t ms) { while ( ms-- ) { _delay_ms(1); } } // Delays variable defined microseconds. Compiler compatibility fix for _delay_us(), // which only accepts constants in future compiler releases. Written to perform more // efficiently with larger delays, as the counter adds parasitic time in each iteration. void delay_us(uint32_t us) { while (us) { if (us < 10) { _delay_us(1); us--; } else if (us < 100) { _delay_us(10); us -= 10; } else if (us < 1000) { _delay_us(100); us -= 100; } else { _delay_ms(1); us -= 1000; } } } // Simple hypotenuse computation function. float hypot_f(float x, float y) { return(sqrt(x*x + y*y)); } ================================================ FILE: nuts_bolts.h ================================================ /* nuts_bolts.h - Header file for shared definitions, variables, and functions Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 nuts_bolts_h #define nuts_bolts_h #define false 0 #define true 1 // Axis array index values. Must start with 0 and be continuous. #define N_AXIS 3 // Number of axes #define X_AXIS 0 // Axis indexing value. #define Y_AXIS 1 #define Z_AXIS 2 // #define A_AXIS 3 // CoreXY motor assignments. DO NOT ALTER. // NOTE: If the A and B motor axis bindings are changed, this effects the CoreXY equations. #ifdef COREXY #define A_MOTOR X_AXIS // Must be X_AXIS #define B_MOTOR Y_AXIS // Must be Y_AXIS #endif // Conversions #define MM_PER_INCH (25.40) #define INCH_PER_MM (0.0393701) #define TICKS_PER_MICROSECOND (F_CPU/1000000) // Useful macros #define clear_vector(a) memset(a, 0, sizeof(a)) #define clear_vector_float(a) memset(a, 0.0, sizeof(float)*N_AXIS) // #define clear_vector_long(a) memset(a, 0.0, sizeof(long)*N_AXIS) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) // Bit field and masking macros #define bit(n) (1 << n) #define bit_true_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) |= (mask); SREG = sreg; } #define bit_false_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) &= ~(mask); SREG = sreg; } #define bit_toggle_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) ^= (mask); SREG = sreg; } #define bit_true(x,mask) (x) |= (mask) #define bit_false(x,mask) (x) &= ~(mask) #define bit_istrue(x,mask) ((x & mask) != 0) #define bit_isfalse(x,mask) ((x & mask) == 0) // 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 uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr); // Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms(). void delay_ms(uint16_t ms); // Delays variable-defined microseconds. Compiler compatibility fix for _delay_us(). void delay_us(uint32_t us); // Computes hypotenuse, avoiding avr-gcc's bloated version and the extra error checking. float hypot_f(float x, float y); #endif ================================================ FILE: planner.c ================================================ /* planner.c - buffers movement commands and manages the acceleration profile plan Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud Copyright (c) 2011 Jens Geisler Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by 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 "grbl.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. static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions static uint8_t block_buffer_tail; // Index of the block to process now static uint8_t block_buffer_head; // Index of the next block to be pushed static uint8_t next_buffer_head; // Index of the next buffer head static uint8_t block_buffer_planned; // Index of the optimally planned block // Define planner variables typedef struct { int32_t position[N_AXIS]; // The planner position of the tool in absolute steps. Kept separate // from g-code position for movements requiring multiple line motions, // i.e. arcs, canned cycles, and backlash compensation. float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment float previous_nominal_speed_sqr; // Nominal speed of previous path line segment } planner_t; static planner_t pl; // Returns the index of the next block in the ring buffer. Also called by stepper segment buffer. uint8_t plan_next_block_index(uint8_t block_index) { block_index++; if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; } return(block_index); } // Returns the index of the previous block in the ring buffer static uint8_t plan_prev_block_index(uint8_t block_index) { if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; } block_index--; return(block_index); } /* PLANNER SPEED DEFINITION +--------+ <- current->nominal_speed / \ current->entry_speed -> + \ | + <- next->entry_speed (aka exit speed) +-------------+ time --> Recalculates the motion plan according to the following basic guidelines: 1. Go over every feasible block sequentially in reverse order and calculate the junction speeds (i.e. current->entry_speed) such that: a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of neighboring blocks. b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed) with a maximum allowable deceleration over the block travel distance. c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero). 2. Go over every block in chronological (forward) order and dial down junction speed values if a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable acceleration over the block travel distance. When these stages are complete, the planner will have maximized the velocity profiles throughout the all of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements are possible. If a new block is added to the buffer, the plan is recomputed according to the said guidelines for a new optimal plan. To increase computational efficiency of these guidelines, a set of planner block pointers have been created to indicate stop-compute points for when the planner guidelines cannot logically make any further changes or improvements to the plan when in normal operation and new blocks are streamed and added to the planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are bracketed by junction velocities at their maximums (or by the first planner block as well), no new block added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute point) are all accelerating, they are all optimal and can not be altered by a new block added to the planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum junction velocity is reached. However, if the operational conditions of the plan changes from infrequently used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is recomputed as stated in the general guidelines. Planner buffer index mapping: - block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed. - block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether the buffer is full or empty. As described for standard ring buffers, this block is always empty. - next_buffer_head: Points to next planner buffer block after the buffer head block. When equal to the buffer tail, this indicates the buffer is full. - block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the planner buffer that don't change with the addition of a new block, as describe above. In addition, this block can never be less than block_buffer_tail and will always be pushed forward and maintain this requirement when encountered by the plan_discard_current_block() routine during a cycle. NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and then decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this happens and becomes an annoyance, there are a few simple solutions: (1) Maximize the machine acceleration. The planner will be able to compute higher velocity profiles within the same combined distance. (2) Maximize line motion(s) distance per block to a desired tolerance. The more combined distance the planner has to use, the faster it can go. (3) Maximize the planner buffer size. This also will increase the combined distance for the planner to compute over. It also increases the number of computations the planner has to perform to compute an optimal plan, so select carefully. The Arduino 328p memory is already maxed out, but future ARM versions should have enough memory and speed for look-ahead blocks numbering up to a hundred or more. */ static void planner_recalculate() { // Initialize block index to the last block in the planner buffer. uint8_t block_index = plan_prev_block_index(block_buffer_head); // Bail. Can't do anything with one only one plan-able block. if (block_index == block_buffer_planned) { return; } // Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last // block in buffer. Cease planning when the last optimal planned or tail pointer is reached. // NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan. float entry_speed_sqr; plan_block_t *next; plan_block_t *current = &block_buffer[block_index]; // Calculate maximum entry speed for last block in buffer, where the exit speed is always zero. current->entry_speed_sqr = min( current->max_entry_speed_sqr, 2*current->acceleration*current->millimeters); block_index = plan_prev_block_index(block_index); if (block_index == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete. // Check if the first block is the tail. If so, notify stepper to update its current parameters. if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); } } else { // Three or more plan-able blocks while (block_index != block_buffer_planned) { next = current; current = &block_buffer[block_index]; block_index = plan_prev_block_index(block_index); // Check if next block is the tail block(=planned block). If so, update current stepper parameters. if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); } // Compute maximum entry speed decelerating over the current block from its exit speed. if (current->entry_speed_sqr != current->max_entry_speed_sqr) { entry_speed_sqr = next->entry_speed_sqr + 2*current->acceleration*current->millimeters; if (entry_speed_sqr < current->max_entry_speed_sqr) { current->entry_speed_sqr = entry_speed_sqr; } else { current->entry_speed_sqr = current->max_entry_speed_sqr; } } } } // Forward Pass: Forward plan the acceleration curve from the planned pointer onward. // Also scans for optimal plan breakpoints and appropriately updates the planned pointer. next = &block_buffer[block_buffer_planned]; // Begin at buffer planned pointer block_index = plan_next_block_index(block_buffer_planned); while (block_index != block_buffer_head) { current = next; next = &block_buffer[block_index]; // Any acceleration detected in the forward pass automatically moves the optimal planned // pointer forward, since everything before this is all optimal. In other words, nothing // can improve the plan from the buffer tail to the planned pointer by logic. if (current->entry_speed_sqr < next->entry_speed_sqr) { entry_speed_sqr = current->entry_speed_sqr + 2*current->acceleration*current->millimeters; // If true, current block is full-acceleration and we can move the planned pointer forward. if (entry_speed_sqr < next->entry_speed_sqr) { next->entry_speed_sqr = entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this. block_buffer_planned = block_index; // Set optimal plan pointer. } } // Any block set at its maximum entry speed also creates an optimal plan up to this // point in the buffer. When the plan is bracketed by either the beginning of the // buffer and a maximum entry speed or two maximum entry speeds, every block in between // cannot logically be further improved. Hence, we don't have to recompute them anymore. if (next->entry_speed_sqr == next->max_entry_speed_sqr) { block_buffer_planned = block_index; } block_index = plan_next_block_index( block_index ); } } void plan_reset() { memset(&pl, 0, sizeof(pl)); // Clear planner struct block_buffer_tail = 0; block_buffer_head = 0; // Empty = tail next_buffer_head = 1; // plan_next_block_index(block_buffer_head) block_buffer_planned = 0; // = block_buffer_tail; } void plan_discard_current_block() { if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer. uint8_t block_index = plan_next_block_index( block_buffer_tail ); // Push block_buffer_planned pointer, if encountered. if (block_buffer_tail == block_buffer_planned) { block_buffer_planned = block_index; } block_buffer_tail = block_index; } } plan_block_t *plan_get_current_block() { if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty return(&block_buffer[block_buffer_tail]); } float plan_get_exec_block_exit_speed() { uint8_t block_index = plan_next_block_index(block_buffer_tail); if (block_index == block_buffer_head) { return( 0.0 ); } return( sqrt( block_buffer[block_index].entry_speed_sqr ) ); } // Returns the availability status of the block ring buffer. True, if full. uint8_t plan_check_full_buffer() { if (block_buffer_tail == next_buffer_head) { return(true); } return(false); } /* Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. All position data passed to the planner must be in terms of machine position to keep the planner independent of any coordinate system changes and offsets, which are handled by the g-code parser. NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control. In other words, the buffer head is never equal to the buffer tail. Also the feed rate input value is used in three ways: as a normal feed rate if invert_feed_rate is false, as inverse time if invert_feed_rate is true, or as seek/rapids rate if the feed_rate value is negative (and invert_feed_rate always false). */ #ifdef USE_LINE_NUMBERS void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number) #else void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate) #endif { // Prepare and initialize new block plan_block_t *block = &block_buffer[block_buffer_head]; block->step_event_count = 0; block->millimeters = 0; block->direction_bits = 0; block->acceleration = SOME_LARGE_VALUE; // Scaled down to maximum acceleration later #ifdef USE_LINE_NUMBERS block->line_number = line_number; #endif // Compute and store initial move distance data. // TODO: After this for-loop, we don't touch the stepper algorithm data. Might be a good idea // to try to keep these types of things completely separate from the planner for portability. int32_t target_steps[N_AXIS]; float unit_vec[N_AXIS], delta_mm; uint8_t idx; #ifdef COREXY target_steps[A_MOTOR] = lround(target[A_MOTOR]*settings.steps_per_mm[A_MOTOR]); target_steps[B_MOTOR] = lround(target[B_MOTOR]*settings.steps_per_mm[B_MOTOR]); block->steps[A_MOTOR] = labs((target_steps[X_AXIS]-pl.position[X_AXIS]) + (target_steps[Y_AXIS]-pl.position[Y_AXIS])); block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-pl.position[X_AXIS]) - (target_steps[Y_AXIS]-pl.position[Y_AXIS])); #endif for (idx=0; idxsteps[idx] = labs(target_steps[idx]-pl.position[idx]); } block->step_event_count = max(block->step_event_count, block->steps[idx]); if (idx == A_MOTOR) { delta_mm = ((target_steps[X_AXIS]-pl.position[X_AXIS]) + (target_steps[Y_AXIS]-pl.position[Y_AXIS]))/settings.steps_per_mm[idx]; } else if (idx == B_MOTOR) { delta_mm = ((target_steps[X_AXIS]-pl.position[X_AXIS]) - (target_steps[Y_AXIS]-pl.position[Y_AXIS]))/settings.steps_per_mm[idx]; } else { delta_mm = (target_steps[idx] - pl.position[idx])/settings.steps_per_mm[idx]; } #else target_steps[idx] = lround(target[idx]*settings.steps_per_mm[idx]); block->steps[idx] = labs(target_steps[idx]-pl.position[idx]); block->step_event_count = max(block->step_event_count, block->steps[idx]); delta_mm = (target_steps[idx] - pl.position[idx])/settings.steps_per_mm[idx]; #endif unit_vec[idx] = delta_mm; // Store unit vector numerator. Denominator computed later. // Set direction bits. Bit enabled always means direction is negative. if (delta_mm < 0 ) { block->direction_bits |= get_direction_pin_mask(idx); } // Incrementally compute total move distance by Euclidean norm. First add square of each term. block->millimeters += delta_mm*delta_mm; } block->millimeters = sqrt(block->millimeters); // Complete millimeters calculation with sqrt() // Bail if this is a zero-length block. Highly unlikely to occur. if (block->step_event_count == 0) { return; } // Adjust feed_rate value to mm/min depending on type of rate input (normal, inverse time, or rapids) // TODO: Need to distinguish a rapids vs feed move for overrides. Some flag of some sort. if (feed_rate < 0) { feed_rate = SOME_LARGE_VALUE; } // Scaled down to absolute max/rapids rate later else if (invert_feed_rate) { feed_rate *= block->millimeters; } if (feed_rate < MINIMUM_FEED_RATE) { feed_rate = MINIMUM_FEED_RATE; } // Prevents step generation round-off condition. // Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled // down such that no individual axes maximum values are exceeded with respect to the line direction. // NOTE: This calculation assumes all axes are orthogonal (Cartesian) and works with ABC-axes, // if they are also orthogonal/independent. Operates on the absolute value of the unit vector. float inverse_unit_vec_value; float inverse_millimeters = 1.0/block->millimeters; // Inverse millimeters to remove multiple float divides float junction_cos_theta = 0; for (idx=0; idxacceleration = min(block->acceleration,settings.acceleration[idx]*inverse_unit_vec_value); // Incrementally compute cosine of angle between previous and current path. Cos(theta) of the junction // between the current move and the previous move is simply the dot product of the two unit vectors, // where prev_unit_vec is negative. Used later to compute maximum junction speed. junction_cos_theta -= pl.previous_unit_vec[idx] * unit_vec[idx]; } } // TODO: Need to check this method handling zero junction speeds when starting from rest. if (block_buffer_head == block_buffer_tail) { // Initialize block entry speed as zero. Assume it will be starting from rest. Planner will correct this later. block->entry_speed_sqr = 0.0; block->max_junction_speed_sqr = 0.0; // Starting from rest. Enforce start from zero velocity. } else { /* Compute maximum allowable entry speed at junction by centripetal acceleration approximation. Let a circle be tangent to both previous and current path line segments, where the junction deviation is defined as the distance from the junction to the closest edge of the circle, colinear with the circle center. The circular segment joining the two paths represents the path of centripetal acceleration. Solve for max velocity based on max acceleration about the radius of the circle, defined indirectly by junction deviation. This may be also viewed as path width or max_jerk in the previous Grbl version. This approach does not actually deviate from path, but used as a robust way to compute cornering speeds, as it takes into account the nonlinearities of both the junction angle and junction velocity. NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here is exactly the same. Instead of motioning all the way to junction point, the machine will just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform a continuous mode path, but ARM-based microcontrollers most certainly do. NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be changed dynamically during operation nor can the line move geometry. This must be kept in memory in the event of a feedrate override changing the nominal speeds of blocks, which can change the overall maximum entry speed conditions of all blocks. */ // NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta). if (junction_cos_theta > 0.99) { // For a 0 degree acute junction, just set minimum junction speed. block->max_junction_speed_sqr = MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED; } else { junction_cos_theta = max(junction_cos_theta,-0.99); // Check for numerical round-off to avoid divide by zero. float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta)); // Trig half angle identity. Always positive. // TODO: Technically, the acceleration used in calculation needs to be limited by the minimum of the // two junctions. However, this shouldn't be a significant problem except in extreme circumstances. block->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED, (block->acceleration * settings.junction_deviation * sin_theta_d2)/(1.0-sin_theta_d2) ); } } // Store block nominal speed block->nominal_speed_sqr = feed_rate*feed_rate; // (mm/min). Always > 0 // Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds. block->max_entry_speed_sqr = min(block->max_junction_speed_sqr, min(block->nominal_speed_sqr,pl.previous_nominal_speed_sqr)); // Update previous path unit_vector and nominal speed (squared) memcpy(pl.previous_unit_vec, unit_vec, sizeof(unit_vec)); // pl.previous_unit_vec[] = unit_vec[] pl.previous_nominal_speed_sqr = block->nominal_speed_sqr; // Update planner position memcpy(pl.position, target_steps, sizeof(target_steps)); // pl.position[] = target_steps[] // New block is all set. Update buffer head and next buffer head indices. block_buffer_head = next_buffer_head; next_buffer_head = plan_next_block_index(block_buffer_head); // Finish up by recalculating the plan with the new block. planner_recalculate(); } // Reset the planner position vectors. Called by the system abort/initialization routine. void plan_sync_position() { // TODO: For motor configurations not in the same coordinate frame as the machine position, // this function needs to be updated to accomodate the difference. uint8_t idx; for (idx=0; idx= block_buffer_tail) { return(block_buffer_head-block_buffer_tail); } return(BLOCK_BUFFER_SIZE - (block_buffer_tail-block_buffer_head)); } // Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail. // Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped. void plan_cycle_reinitialize() { // Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer. st_update_plan_block_parameters(); block_buffer_planned = block_buffer_tail; planner_recalculate(); } ================================================ FILE: planner.h ================================================ /* planner.h - buffers movement commands and manages the acceleration profile plan Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 planner_h #define planner_h // The number of linear motions that can be in the plan at any give time #ifndef BLOCK_BUFFER_SIZE #ifdef USE_LINE_NUMBERS #define BLOCK_BUFFER_SIZE 16 #else #define BLOCK_BUFFER_SIZE 18 #endif #endif // This struct stores a linear movement of a g-code block motion with its critical "nominal" values // are as specified in the source g-code. typedef struct { // Fields used by the bresenham algorithm for tracing the line // NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values. uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) uint32_t steps[N_AXIS]; // Step count along each axis uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block. // Fields used by the motion planner to manage acceleration float entry_speed_sqr; // The current planned entry speed at block junction in (mm/min)^2 float max_entry_speed_sqr; // Maximum allowable entry speed based on the minimum of junction limit and // neighboring nominal speeds with overrides in (mm/min)^2 float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (mm/min)^2 float nominal_speed_sqr; // Axis-limit adjusted nominal speed for this block in (mm/min)^2 float acceleration; // Axis-limit adjusted line acceleration in (mm/min^2) float millimeters; // The remaining distance for this block to be executed in (mm) // uint8_t max_override; // Maximum override value based on axis speed limits #ifdef USE_LINE_NUMBERS int32_t line_number; #endif } plan_block_t; // Initialize and reset the motion plan subsystem void plan_reset(); // Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position // in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed // rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. #ifdef USE_LINE_NUMBERS void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number); #else void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate); #endif // Called when the current block is no longer needed. Discards the block and makes the memory // availible for new blocks. void plan_discard_current_block(); // Gets the current block. Returns NULL if buffer empty plan_block_t *plan_get_current_block(); // Called periodically by step segment buffer. Mostly used internally by planner. uint8_t plan_next_block_index(uint8_t block_index); // Called by step segment buffer when computing executing block velocity profile. float plan_get_exec_block_exit_speed(); // Reset the planner position vector (in steps) void plan_sync_position(); // Reinitialize plan with a partially completed block void plan_cycle_reinitialize(); // Returns the number of active blocks are in the planner buffer. uint8_t plan_get_block_buffer_count(); // Returns the status of the block ring buffer. True, if buffer is full. uint8_t plan_check_full_buffer(); #endif ================================================ FILE: print.c ================================================ /* print.c - Functions for formatting output strings Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 "grbl.h" void printString(const char *s) { while (*s) serial_write(*s++); } // Print a string stored in PGM-memory void printPgmString(const char *s) { char c; while ((c = pgm_read_byte_near(s++))) serial_write(c); } // void printIntegerInBase(unsigned long n, unsigned long base) // { // unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars. // unsigned long i = 0; // // if (n == 0) { // serial_write('0'); // return; // } // // while (n > 0) { // buf[i++] = n % base; // n /= base; // } // // for (; i > 0; i--) // serial_write(buf[i - 1] < 10 ? // '0' + buf[i - 1] : // 'A' + buf[i - 1] - 10); // } // Prints an uint8 variable with base and number of desired digits. void print_unsigned_int8(uint8_t n, uint8_t base, uint8_t digits) { unsigned char buf[digits]; uint8_t i = 0; for (; i < digits; i++) { buf[i] = n % base ; n /= base; } for (; i > 0; i--) serial_write('0' + buf[i - 1]); } // Prints an uint8 variable in base 2. void print_uint8_base2(uint8_t n) { print_unsigned_int8(n,2,8); } // Prints an uint8 variable in base 10. void print_uint8_base10(uint8_t n) { uint8_t digits; if (n < 10) { digits = 1; } else if (n < 100) { digits = 2; } else { digits = 3; } print_unsigned_int8(n,10,digits); } void print_uint32_base10(uint32_t n) { if (n == 0) { serial_write('0'); return; } unsigned char buf[10]; uint8_t i = 0; while (n > 0) { buf[i++] = n % 10; n /= 10; } for (; i > 0; i--) serial_write('0' + buf[i-1]); } void printInteger(long n) { if (n < 0) { serial_write('-'); print_uint32_base10(-n); } else { print_uint32_base10(n); } } // Convert float to string by immediately converting to a long integer, which contains // more digits than a float. Number of decimal places, which are tracked by a counter, // may be set by the user. The integer is then efficiently converted to a string. // NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up // techniques are actually just slightly slower. Found this out the hard way. void printFloat(float n, uint8_t decimal_places) { if (n < 0) { serial_write('-'); n = -n; } uint8_t decimals = decimal_places; while (decimals >= 2) { // Quickly convert values expected to be E0 to E-4. n *= 100; decimals -= 2; } if (decimals) { n *= 10; } n += 0.5; // Add rounding factor. Ensures carryover through entire value. // Generate digits backwards and store in string. unsigned char buf[10]; uint8_t i = 0; uint32_t a = (long)n; buf[decimal_places] = '.'; // Place decimal point, even if decimal places are zero. while(a > 0) { if (i == decimal_places) { i++; } // Skip decimal point location buf[i++] = (a % 10) + '0'; // Get digit a /= 10; } while (i < decimal_places) { buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1) } if (i == decimal_places) { // Fill in leading zero, if needed. i++; buf[i++] = '0'; } // Print the generated string. for (; i > 0; i--) serial_write(buf[i-1]); } // Floating value printing handlers for special variables types used in Grbl and are defined // in the config.h. // - CoordValue: Handles all position or coordinate values in inches or mm reporting. // - RateValue: Handles feed rate and current velocity in inches or mm reporting. // - SettingValue: Handles all floating point settings values (always in mm.) void printFloat_CoordValue(float n) { if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { printFloat(n*INCH_PER_MM,N_DECIMAL_COORDVALUE_INCH); } else { printFloat(n,N_DECIMAL_COORDVALUE_MM); } } void printFloat_RateValue(float n) { if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { printFloat(n*INCH_PER_MM,N_DECIMAL_RATEVALUE_INCH); } else { printFloat(n,N_DECIMAL_RATEVALUE_MM); } } void printFloat_SettingValue(float n) { printFloat(n,N_DECIMAL_SETTINGVALUE); } // Debug tool to print free memory in bytes at the called point. // NOTE: Keep commented unless using. Part of this function always gets compiled in. // void printFreeMemory() // { // extern int __heap_start, *__brkval; // uint16_t free; // Up to 64k values. // free = (int) &free - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); // printInteger((int32_t)free); // printString(" "); // } ================================================ FILE: print.h ================================================ /* print.h - Functions for formatting output strings Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 print_h #define print_h void printString(const char *s); void printPgmString(const char *s); void printInteger(long n); void print_uint32_base10(uint32_t n); // Prints uint8 variable with base and number of desired digits. void print_unsigned_int8(uint8_t n, uint8_t base, uint8_t digits); // Prints an uint8 variable in base 2. void print_uint8_base2(uint8_t n); // Prints an uint8 variable in base 10. void print_uint8_base10(uint8_t n); void printFloat(float n, uint8_t decimal_places); // Floating value printing handlers for special variables types used in Grbl. // - CoordValue: Handles all position or coordinate values in inches or mm reporting. // - RateValue: Handles feed rate and current velocity in inches or mm reporting. // - SettingValue: Handles all floating point settings values (always in mm.) void printFloat_CoordValue(float n); void printFloat_RateValue(float n); void printFloat_SettingValue(float n); // Debug tool to print free memory in bytes at the called point. Not used otherwise. void printFreeMemory(); #endif ================================================ FILE: probe.c ================================================ /* probe.c - code pertaining to probing methods Part of Grbl Copyright (c) 2014-2015 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 "grbl.h" // Inverts the probe pin state depending on user settings and probing cycle mode. uint8_t probe_invert_mask; // Probe pin initialization routine. void probe_init() { PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins #ifdef DISABLE_PROBE_PIN_PULL_UP PROBE_PORT &= ~(PROBE_MASK); // Normal low operation. Requires external pull-down. #else PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation. #endif // probe_configure_invert_mask(false); // Initialize invert mask. Not required. Updated when in-use. } // Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to // appropriately set the pin logic according to setting for normal-high/normal-low operation // and the probing cycle modes for toward-workpiece/away-from-workpiece. void probe_configure_invert_mask(uint8_t is_probe_away) { probe_invert_mask = 0; // Initialize as zero. if (bit_isfalse(settings.flags,BITFLAG_INVERT_PROBE_PIN)) { probe_invert_mask ^= PROBE_MASK; } if (is_probe_away) { probe_invert_mask ^= PROBE_MASK; } } // Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor. uint8_t probe_get_state() { return((PROBE_PIN & PROBE_MASK) ^ probe_invert_mask); } // Monitors probe pin state and records the system position when detected. Called by the // stepper ISR per ISR tick. // NOTE: This function must be extremely efficient as to not bog down the stepper ISR. void probe_state_monitor() { if (sys.probe_state == PROBE_ACTIVE) { if (probe_get_state()) { sys.probe_state = PROBE_OFF; memcpy(sys.probe_position, sys.position, sizeof(float)*N_AXIS); bit_true(sys.rt_exec_state, EXEC_MOTION_CANCEL); } } } ================================================ FILE: probe.h ================================================ /* probe.h - code pertaining to probing methods Part of Grbl Copyright (c) 2014-2015 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 probe_h #define probe_h // Values that define the probing state machine. #define PROBE_OFF 0 // Probing disabled or not in use. (Must be zero.) #define PROBE_ACTIVE 1 // Actively watching the input pin. // Probe pin initialization routine. void probe_init(); // Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to // appropriately set the pin logic according to setting for normal-high/normal-low operation // and the probing cycle modes for toward-workpiece/away-from-workpiece. void probe_configure_invert_mask(uint8_t is_probe_away); // Returns probe pin state. Triggered = true. Called by gcode parser and probe state monitor. uint8_t probe_get_state(); // Monitors probe pin state and records the system position when detected. Called by the // stepper ISR per ISR tick. void probe_state_monitor(); #endif ================================================ FILE: protocol.c ================================================ /* protocol.c - controls Grbl execution protocol and procedures Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 "grbl.h" // Define different comment types for pre-parsing. #define COMMENT_NONE 0 #define COMMENT_TYPE_PARENTHESES 1 #define COMMENT_TYPE_SEMICOLON 2 static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. // Directs and executes one line of formatted input from protocol_process. While mostly // incoming streaming g-code blocks, this also directs and executes Grbl internal commands, // such as settings, initiating the homing cycle, and toggling switch states. static void protocol_execute_line(char *line) { protocol_execute_realtime(); // Runtime command check point. if (sys.abort) { return; } // Bail to calling function upon system abort #ifdef REPORT_ECHO_LINE_RECEIVED report_echo_line_received(line); #endif if (line[0] == 0) { // Empty or comment line. Send status message for syncing purposes. report_status_message(STATUS_OK); } else if (line[0] == '$') { // Grbl '$' system command report_status_message(system_execute_line(line)); } else if (sys.state == STATE_ALARM) { // Everything else is gcode. Block if in alarm mode. report_status_message(STATUS_ALARM_LOCK); } else { // Parse and execute g-code block! report_status_message(gc_execute_line(line)); } } /* GRBL PRIMARY LOOP: */ void protocol_main_loop() { // ------------------------------------------------------------ // 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! But first check for safety door. if (system_check_safety_door_ajar()) { bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR); protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state. } else { sys.state = STATE_IDLE; // Set system to ready. Clear all state flags. } system_execute_startup(line); // Execute startup script. } // --------------------------------------------------------------------------------- // Primary loop! Upon a system abort, this exits back to main() to reset the system. // --------------------------------------------------------------------------------- uint8_t comment = COMMENT_NONE; uint8_t char_counter = 0; uint8_t c; for (;;) { // 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. // NOTE: While comment, spaces, and block delete(if supported) handling should technically // be done in the g-code parser, doing it here helps compress the incoming data into Grbl's // line buffer, which is limited in size. The g-code standard actually states a line can't // exceed 256 characters, but the Arduino Uno does not have the memory space for this. // With a better processor, it would be very easy to pull this initial parsing out as a // seperate task to be shared by the g-code parser and Grbl's system commands. 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! comment = COMMENT_NONE; char_counter = 0; } else { if (comment != COMMENT_NONE) { // Throw away all comment characters if (c == ')') { // End of comment. Resume line. But, not if semicolon type comment. if (comment == COMMENT_TYPE_PARENTHESES) { comment = COMMENT_NONE; } } } else { if (c <= ' ') { // Throw away whitepace and control characters } else if (c == '/') { // Block delete NOT SUPPORTED. Ignore character. // NOTE: If supported, would simply need to check the system if block delete is enabled. } else if (c == '(') { // Enable comments flag and ignore all characters until ')' or EOL. // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. // In the future, we could simply remove the items within the comments, but retain the // comment control characters, so that the g-code parser can error-check it. comment = COMMENT_TYPE_PARENTHESES; } else if (c == ';') { // NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST. comment = COMMENT_TYPE_SEMICOLON; // TODO: Install '%' feature // } else if (c == '%') { // Program start-end percent sign NOT SUPPORTED. // NOTE: This maybe installed to tell Grbl when a program is running vs manual input, // where, during a program, the system auto-cycle start will continue to execute // everything until the next '%' sign. This will help fix resuming issues with certain // functions that empty the planner buffer to execute its task on-time. } else if (char_counter >= (LINE_BUFFER_SIZE-1)) { // Detect line buffer overflow. Report error and reset line buffer. report_status_message(STATUS_OVERFLOW); comment = COMMENT_NONE; char_counter = 0; } else if (c >= 'a' && c <= 'z') { // Upcase lowercase line[char_counter++] = c-'a'+'A'; } else { line[char_counter++] = c; } } } } // 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. protocol_auto_cycle_start(); protocol_execute_realtime(); // Runtime command check point. if (sys.abort) { return; } // Bail to main() program loop to reset system. } return; /* Never reached */ } // 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. // This is a way to execute realtime commands asynchronously (aka multitasking) with grbl's g-code // parsing and planning functions. This function also serves as an interface for the interrupts to // set the system realtime flags, where only the main program handles them, removing the need to // 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.rt_exec_state variable flags are set by any process, step or serial interrupts, pinouts, // limit switches, or the main program. void protocol_execute_realtime() { uint8_t rt_exec; // Temp variable to avoid calling volatile multiple times. do { // If system is suspended, suspend loop restarts here. // Check and execute alarms. rt_exec = sys.rt_exec_alarm; // Copy volatile sys.rt_exec_alarm. if (rt_exec) { // Enter only if any bit flag is true // 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. sys.state = STATE_ALARM; // Set system alarm state if (rt_exec & EXEC_ALARM_HARD_LIMIT) { report_alarm_message(ALARM_HARD_LIMIT_ERROR); } else if (rt_exec & EXEC_ALARM_SOFT_LIMIT) { report_alarm_message(ALARM_SOFT_LIMIT_ERROR); } else if (rt_exec & EXEC_ALARM_ABORT_CYCLE) { report_alarm_message(ALARM_ABORT_CYCLE); } else if (rt_exec & EXEC_ALARM_PROBE_FAIL) { report_alarm_message(ALARM_PROBE_FAIL); } else if (rt_exec & EXEC_ALARM_HOMING_FAIL) { report_alarm_message(ALARM_HOMING_FAIL); } // Halt everything upon a critical event flag. Currently hard and soft limits flag this. if (rt_exec & EXEC_CRITICAL_EVENT) { report_feedback_message(MESSAGE_CRITICAL_EVENT); bit_false_atomic(sys.rt_exec_state,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. The // same could be said about soft limits. While the position is not lost, the incoming // stream could be still engaged and cause a serious crash if it continues afterwards. // TODO: Allow status reports during a critical alarm. Still need to think about implications of this. // if (sys.rt_exec_state & EXEC_STATUS_REPORT) { // report_realtime_status(); // bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT); // } } while (bit_isfalse(sys.rt_exec_state,EXEC_RESET)); } bit_false_atomic(sys.rt_exec_alarm,0xFF); // Clear all alarm flags } // Check amd execute realtime commands rt_exec = sys.rt_exec_state; // Copy volatile sys.rt_exec_state. if (rt_exec) { // Enter only if any bit flag is true // Execute system abort. if (rt_exec & EXEC_RESET) { sys.abort = true; // Only place this is set true. return; // Nothing else to do but exit. } // Execute and serial print status if (rt_exec & EXEC_STATUS_REPORT) { report_realtime_status(); bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT); } // Execute hold states. // NOTE: The math involved to calculate the hold should be low enough for most, if not all, // operational scenarios. Once hold is initiated, the system enters a suspend state to block // all main program processes until either reset or resumed. if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)) { // TODO: CHECK MODE? How to handle this? Likely nothing, since it only works when IDLE and then resets Grbl. // State check for allowable states for hold methods. if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_MOTION_CANCEL | STATE_HOLD | STATE_SAFETY_DOOR))) { // If in CYCLE state, all hold states immediately initiate a motion HOLD. if (sys.state == STATE_CYCLE) { st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration. sys.suspend = SUSPEND_ENABLE_HOLD; // Initiate holding cycle with flag. } // If IDLE, Grbl is not in motion. Simply indicate suspend ready state. if (sys.state == STATE_IDLE) { sys.suspend = SUSPEND_ENABLE_READY; } // Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle // to halt and cancel the remainder of the motion. if (rt_exec & EXEC_MOTION_CANCEL) { // MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand // to hold the CYCLE. If so, only flag that motion cancel is complete. if (sys.state == STATE_CYCLE) { sys.state = STATE_MOTION_CANCEL; } sys.suspend |= SUSPEND_MOTION_CANCEL; // Indicate motion cancel when resuming. Special motion complete. } // Execute a feed hold with deceleration, only during cycle. if (rt_exec & EXEC_FEED_HOLD) { // Block SAFETY_DOOR state from prematurely changing back to HOLD. if (bit_isfalse(sys.state,STATE_SAFETY_DOOR)) { sys.state = STATE_HOLD; } } // Execute a safety door stop with a feed hold, only during a cycle, and disable spindle/coolant. // NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered // devices (spindle/coolant), and blocks resuming until switch is re-engaged. The power-down is // executed here, if IDLE, or when the CYCLE completes via the EXEC_CYCLE_STOP flag. if (rt_exec & EXEC_SAFETY_DOOR) { report_feedback_message(MESSAGE_SAFETY_DOOR_AJAR); // If already in active, ready-to-resume HOLD, set CYCLE_STOP flag to force de-energize. // NOTE: Only temporarily sets the 'rt_exec' variable, not the volatile 'rt_exec_state' variable. if (sys.suspend & SUSPEND_ENABLE_READY) { bit_true(rt_exec,EXEC_CYCLE_STOP); } sys.suspend |= SUSPEND_ENERGIZE; sys.state = STATE_SAFETY_DOOR; } } bit_false_atomic(sys.rt_exec_state,(EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)); } // Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue. if (rt_exec & EXEC_CYCLE_START) { // Block if called at same time as the hold commands: feed hold, motion cancel, and safety door. // Ensures auto-cycle-start doesn't resume a hold without an explicit user-input. if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) { // Cycle start only when IDLE or when a hold is complete and ready to resume. // NOTE: SAFETY_DOOR is implicitly blocked. It reverts to HOLD when the door is closed. if ((sys.state == STATE_IDLE) || ((sys.state & (STATE_HOLD | STATE_MOTION_CANCEL)) && (sys.suspend & SUSPEND_ENABLE_READY))) { // Re-energize powered components, if disabled by SAFETY_DOOR. if (sys.suspend & SUSPEND_ENERGIZE) { // Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle. if (gc_state.modal.spindle != SPINDLE_DISABLE) { spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed); delay_ms(SAFETY_DOOR_SPINDLE_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually. } if (gc_state.modal.coolant != COOLANT_DISABLE) { coolant_set_state(gc_state.modal.coolant); delay_ms(SAFETY_DOOR_COOLANT_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually. } // TODO: Install return to pre-park position. } // Start cycle only if queued motions exist in planner buffer and the motion is not canceled. if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) { sys.state = STATE_CYCLE; st_prep_buffer(); // Initialize step segment buffer before beginning cycle. st_wake_up(); } else { // Otherwise, do nothing. Set and resume IDLE state. sys.state = STATE_IDLE; } sys.suspend = SUSPEND_DISABLE; // Break suspend state. } } bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_START); } // Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by // realtime command execution in the main program, ensuring that the planner re-plans safely. // NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper // cycle reinitializations. The stepper path should continue exactly as if nothing has happened. // NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes. if (rt_exec & EXEC_CYCLE_STOP) { if (sys.state & (STATE_HOLD | STATE_SAFETY_DOOR)) { // Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user // has issued a resume command or reset. if (sys.suspend & SUSPEND_ENERGIZE) { // De-energize system if safety door has been opened. spindle_stop(); coolant_stop(); // TODO: Install parking motion here. } bit_true(sys.suspend,SUSPEND_ENABLE_READY); } else { // Motion is complete. Includes CYCLE, HOMING, and MOTION_CANCEL states. sys.suspend = SUSPEND_DISABLE; sys.state = STATE_IDLE; } bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_STOP); } } // Overrides flag byte (sys.override) and execution should be installed here, since they // are realtime and require a direct and controlled interface to the main stepper program. // Reload step segment buffer if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR | STATE_HOMING)) { st_prep_buffer(); } // If safety door was opened, actively check when safety door is closed and ready to resume. // NOTE: This unlocks the SAFETY_DOOR state to a HOLD state, such that CYCLE_START can activate a resume. if (sys.state == STATE_SAFETY_DOOR) { if (bit_istrue(sys.suspend,SUSPEND_ENABLE_READY)) { if (!(system_check_safety_door_ajar())) { sys.state = STATE_HOLD; // Update to HOLD state to indicate door is closed and ready to resume. } } } } while(sys.suspend); // Check for system suspend state before exiting. } // Block until all buffered steps are executed or in a cycle state. Works with feed hold // during a synchronize call, if it should happen. Also, waits for clean cycle end. void protocol_buffer_synchronize() { // If system is queued, ensure cycle resumes if the auto start flag is present. protocol_auto_cycle_start(); do { protocol_execute_realtime(); // Check and execute run-time commands if (sys.abort) { return; } // Check for system abort } while (plan_get_current_block() || (sys.state == STATE_CYCLE)); } // Auto-cycle start has two purposes: 1. Resumes a plan_synchronize() call from a function that // requires the planner buffer to empty (spindle enable, dwell, etc.) 2. As a user setting that // automatically begins the cycle when a user enters a valid motion command manually. This is // intended as a beginners feature to help new users to understand g-code. It can be disabled // as a beginner tool, but (1.) still operates. If disabled, the operation of cycle start is // manually issuing a cycle start command whenever the user is ready and there is a valid motion // command in the planner queue. // NOTE: This function is called from the main loop, buffer sync, and mc_line() only and executes // when one of these conditions exist respectively: There are no more blocks sent (i.e. streaming // is finished, single commands), a command that needs to wait for the motions in the buffer to // execute calls a buffer sync, or the planner buffer is full and ready to go. void protocol_auto_cycle_start() { bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); } ================================================ FILE: protocol.h ================================================ /* protocol.h - controls Grbl execution protocol and procedures Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 protocol_h #define protocol_h // 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 this // buffer. #ifndef LINE_BUFFER_SIZE #define LINE_BUFFER_SIZE 80 #endif // 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_main_loop(); // Checks and executes a realtime command at various stop points in main program void protocol_execute_realtime(); // Notify the stepper subsystem to start executing the g-code program in buffer. // void protocol_cycle_start(); // Reinitializes the buffer after a feed hold for a resume. // void protocol_cycle_reinitialize(); // Initiates a feed hold of the running program // void protocol_feed_hold(); // Executes the auto cycle feature, if enabled. void protocol_auto_cycle_start(); // Block until all buffered steps are executed void protocol_buffer_synchronize(); #endif ================================================ FILE: report.c ================================================ /* report.c - reporting and messaging methods Part of Grbl Copyright (c) 2012-2015 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 . */ /* This file functions as the primary feedback interface for Grbl. Any outgoing data, such as the protocol status messages, feedback messages, and status reports, are stored here. For the most part, these functions primarily are called from protocol.c methods. If a different style feedback is desired (i.e. JSON), then a user can change these following methods to accomodate their needs. */ #include "grbl.h" // Handles the primary confirmation protocol response for streaming interfaces and human-feedback. // For every incoming line, this method responds with an 'ok' for a successful command or an // 'error:' to indicate some error event with the line or some critical system error during // operation. Errors events can originate from the g-code parser, settings module, or asynchronously // from a critical error, such as a triggered hard limit. Interface should always monitor for these // responses. // NOTE: In silent mode, all error codes are greater than zero. // TODO: Install silent mode to return only numeric values, primarily for GUIs. void report_status_message(uint8_t status_code) { if (status_code == 0) { // STATUS_OK printPgmString(PSTR("ok\r\n")); } else { printPgmString(PSTR("error: ")); #ifdef REPORT_GUI_MODE print_uint8_base10(status_code); #else switch(status_code) { case STATUS_EXPECTED_COMMAND_LETTER: printPgmString(PSTR("Expected command letter")); break; case STATUS_BAD_NUMBER_FORMAT: printPgmString(PSTR("Bad number format")); break; case STATUS_INVALID_STATEMENT: printPgmString(PSTR("Invalid statement")); break; case STATUS_NEGATIVE_VALUE: printPgmString(PSTR("Value < 0")); break; case STATUS_SETTING_DISABLED: printPgmString(PSTR("Setting disabled")); break; case STATUS_SETTING_STEP_PULSE_MIN: printPgmString(PSTR("Value < 3 usec")); break; case STATUS_SETTING_READ_FAIL: printPgmString(PSTR("EEPROM read fail. Using defaults")); break; case STATUS_IDLE_ERROR: printPgmString(PSTR("Not idle")); break; case STATUS_ALARM_LOCK: printPgmString(PSTR("Alarm lock")); break; case STATUS_SOFT_LIMIT_ERROR: printPgmString(PSTR("Homing not enabled")); break; case STATUS_OVERFLOW: printPgmString(PSTR("Line overflow")); break; #ifdef MAX_STEP_RATE_HZ case STATUS_MAX_STEP_RATE_EXCEEDED: printPgmString(PSTR("Step rate > 30kHz")); break; #endif // Common g-code parser errors. case STATUS_GCODE_MODAL_GROUP_VIOLATION: printPgmString(PSTR("Modal group violation")); break; case STATUS_GCODE_UNSUPPORTED_COMMAND: printPgmString(PSTR("Unsupported command")); break; case STATUS_GCODE_UNDEFINED_FEED_RATE: printPgmString(PSTR("Undefined feed rate")); break; default: // Remaining g-code parser errors with error codes printPgmString(PSTR("Invalid gcode ID:")); print_uint8_base10(status_code); // Print error code for user reference } #endif printPgmString(PSTR("\r\n")); } } // Prints alarm messages. void report_alarm_message(int8_t alarm_code) { printPgmString(PSTR("ALARM: ")); #ifdef REPORT_GUI_MODE print_uint8_base10(alarm_code); #else switch (alarm_code) { case ALARM_HARD_LIMIT_ERROR: printPgmString(PSTR("Hard limit")); break; case ALARM_SOFT_LIMIT_ERROR: printPgmString(PSTR("Soft limit")); break; case ALARM_ABORT_CYCLE: printPgmString(PSTR("Abort during cycle")); break; case ALARM_PROBE_FAIL: printPgmString(PSTR("Probe fail")); break; case ALARM_HOMING_FAIL: printPgmString(PSTR("Homing fail")); break; } #endif printPgmString(PSTR("\r\n")); delay_ms(500); // Force delay to ensure message clears serial write buffer. } // Prints feedback messages. This serves as a centralized method to provide additional // 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(uint8_t message_code) { printPgmString(PSTR("[")); switch(message_code) { case MESSAGE_CRITICAL_EVENT: 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_SAFETY_DOOR_AJAR: printPgmString(PSTR("Check Door")); break; case MESSAGE_PROGRAM_END: printPgmString(PSTR("Pgm End")); break; case MESSAGE_RESTORE_DEFAULTS: printPgmString(PSTR("Restoring defaults")); break; } printPgmString(PSTR("]\r\n")); } // Welcome message void report_init_message() { printPgmString(PSTR("\r\nGrbl " GRBL_VERSION " ['$' for help]\r\n")); } // Grbl help message void report_grbl_help() { #ifndef REPORT_GUI_MODE printPgmString(PSTR("$$ (view Grbl settings)\r\n" "$# (view # parameters)\r\n" "$G (view parser state)\r\n" "$I (view build info)\r\n" "$N (view startup blocks)\r\n" "$x=value (save Grbl setting)\r\n" "$Nx=line (save startup block)\r\n" "$C (check gcode mode)\r\n" "$X (kill alarm lock)\r\n" "$H (run homing cycle)\r\n" "~ (cycle start)\r\n" "! (feed hold)\r\n" "? (current status)\r\n" "ctrl-x (reset Grbl)\r\n")); #endif } // Grbl global settings print out. // NOTE: The numbering scheme here must correlate to storing in settings.c void report_grbl_settings() { // Print Grbl settings. #ifdef REPORT_GUI_MODE printPgmString(PSTR("$0=")); print_uint8_base10(settings.pulse_microseconds); printPgmString(PSTR("\r\n$1=")); print_uint8_base10(settings.stepper_idle_lock_time); printPgmString(PSTR("\r\n$2=")); print_uint8_base10(settings.step_invert_mask); printPgmString(PSTR("\r\n$3=")); print_uint8_base10(settings.dir_invert_mask); printPgmString(PSTR("\r\n$4=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); printPgmString(PSTR("\r\n$5=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)); printPgmString(PSTR("\r\n$6=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN)); printPgmString(PSTR("\r\n$10=")); print_uint8_base10(settings.status_report_mask); printPgmString(PSTR("\r\n$11=")); printFloat_SettingValue(settings.junction_deviation); printPgmString(PSTR("\r\n$12=")); printFloat_SettingValue(settings.arc_tolerance); printPgmString(PSTR("\r\n$13=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); printPgmString(PSTR("\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)); printPgmString(PSTR("\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); printPgmString(PSTR("\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); printPgmString(PSTR("\r\n$23=")); print_uint8_base10(settings.homing_dir_mask); printPgmString(PSTR("\r\n$24=")); printFloat_SettingValue(settings.homing_feed_rate); printPgmString(PSTR("\r\n$25=")); printFloat_SettingValue(settings.homing_seek_rate); printPgmString(PSTR("\r\n$26=")); print_uint8_base10(settings.homing_debounce_delay); printPgmString(PSTR("\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff); printPgmString(PSTR("\r\n")); #else printPgmString(PSTR("$0=")); print_uint8_base10(settings.pulse_microseconds); printPgmString(PSTR(" (step pulse, usec)\r\n$1=")); print_uint8_base10(settings.stepper_idle_lock_time); printPgmString(PSTR(" (step idle delay, msec)\r\n$2=")); print_uint8_base10(settings.step_invert_mask); printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask); printPgmString(PSTR(")\r\n$3=")); print_uint8_base10(settings.dir_invert_mask); printPgmString(PSTR(" (dir port invert mask:")); print_uint8_base2(settings.dir_invert_mask); printPgmString(PSTR(")\r\n$4=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); printPgmString(PSTR(" (step enable invert, bool)\r\n$5=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)); printPgmString(PSTR(" (limit pins invert, bool)\r\n$6=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN)); printPgmString(PSTR(" (probe pin invert, bool)\r\n$10=")); print_uint8_base10(settings.status_report_mask); printPgmString(PSTR(" (status report mask:")); print_uint8_base2(settings.status_report_mask); printPgmString(PSTR(")\r\n$11=")); printFloat_SettingValue(settings.junction_deviation); printPgmString(PSTR(" (junction deviation, mm)\r\n$12=")); printFloat_SettingValue(settings.arc_tolerance); printPgmString(PSTR(" (arc tolerance, mm)\r\n$13=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); printPgmString(PSTR(" (report inches, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)); printPgmString(PSTR(" (soft limits, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); printPgmString(PSTR(" (hard limits, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); printPgmString(PSTR(" (homing cycle, bool)\r\n$23=")); print_uint8_base10(settings.homing_dir_mask); printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask); printPgmString(PSTR(")\r\n$24=")); printFloat_SettingValue(settings.homing_feed_rate); printPgmString(PSTR(" (homing feed, mm/min)\r\n$25=")); printFloat_SettingValue(settings.homing_seek_rate); printPgmString(PSTR(" (homing seek, mm/min)\r\n$26=")); print_uint8_base10(settings.homing_debounce_delay); printPgmString(PSTR(" (homing debounce, msec)\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff); printPgmString(PSTR(" (homing pull-off, mm)\r\n")); #endif // Print axis settings uint8_t idx, set_idx; uint8_t val = AXIS_SETTINGS_START_VAL; for (set_idx=0; set_idxline_number; } printInteger(ln); #endif #ifdef REPORT_REALTIME_RATE // Report realtime rate printPgmString(PSTR(",F:")); printFloat_RateValue(st_get_realtime_rate()); #endif if (bit_istrue(settings.status_report_mask,BITFLAG_RT_STATUS_LIMIT_PINS)) { printPgmString(PSTR(",Lim:")); print_unsigned_int8(limits_get_state(),2,N_AXIS); } #ifdef REPORT_CONTROL_PIN_STATE printPgmString(PSTR(",Ctl:")); print_uint8_base2(CONTROL_PIN & CONTROL_MASK); #endif printPgmString(PSTR(">\r\n")); } ================================================ FILE: report.h ================================================ /* report.h - reporting and messaging methods Part of Grbl Copyright (c) 2012-2015 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 report_h #define report_h // Define Grbl status codes. #define STATUS_OK 0 #define STATUS_EXPECTED_COMMAND_LETTER 1 #define STATUS_BAD_NUMBER_FORMAT 2 #define STATUS_INVALID_STATEMENT 3 #define STATUS_NEGATIVE_VALUE 4 #define STATUS_SETTING_DISABLED 5 #define STATUS_SETTING_STEP_PULSE_MIN 6 #define STATUS_SETTING_READ_FAIL 7 #define STATUS_IDLE_ERROR 8 #define STATUS_ALARM_LOCK 9 #define STATUS_SOFT_LIMIT_ERROR 10 #define STATUS_OVERFLOW 11 #define STATUS_MAX_STEP_RATE_EXCEEDED 12 #define STATUS_GCODE_UNSUPPORTED_COMMAND 20 #define STATUS_GCODE_MODAL_GROUP_VIOLATION 21 #define STATUS_GCODE_UNDEFINED_FEED_RATE 22 #define STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER 23 #define STATUS_GCODE_AXIS_COMMAND_CONFLICT 24 #define STATUS_GCODE_WORD_REPEATED 25 #define STATUS_GCODE_NO_AXIS_WORDS 26 #define STATUS_GCODE_INVALID_LINE_NUMBER 27 #define STATUS_GCODE_VALUE_WORD_MISSING 28 #define STATUS_GCODE_UNSUPPORTED_COORD_SYS 29 #define STATUS_GCODE_G53_INVALID_MOTION_MODE 30 #define STATUS_GCODE_AXIS_WORDS_EXIST 31 #define STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE 32 #define STATUS_GCODE_INVALID_TARGET 33 #define STATUS_GCODE_ARC_RADIUS_ERROR 34 #define STATUS_GCODE_NO_OFFSETS_IN_PLANE 35 #define STATUS_GCODE_UNUSED_WORDS 36 #define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 37 // Define Grbl alarm codes. #define ALARM_HARD_LIMIT_ERROR 1 #define ALARM_SOFT_LIMIT_ERROR 2 #define ALARM_ABORT_CYCLE 3 #define ALARM_PROBE_FAIL 4 #define ALARM_HOMING_FAIL 5 // 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 #define MESSAGE_SAFETY_DOOR_AJAR 6 #define MESSAGE_PROGRAM_END 7 #define MESSAGE_RESTORE_DEFAULTS 8 // 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(uint8_t message_code); // Prints welcome message void report_init_message(); // Prints Grbl help and current global settings void report_grbl_help(); // Prints Grbl global settings void report_grbl_settings(); // Prints an echo of the pre-parsed line received right before execution. void report_echo_line_received(char *line); // Prints realtime status report void report_realtime_status(); // Prints recorded probe position void report_probe_parameters(); // Prints Grbl NGC parameters (coordinate offsets, probe) void report_ngc_parameters(); // Prints current g-code parser mode state void report_gcode_modes(); // Prints startup line void report_startup_line(uint8_t n, char *line); // Prints build info and user info void report_build_info(char *line); #endif ================================================ FILE: serial.c ================================================ /* serial.c - Low level functions for sending and recieving bytes via the serial port Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 "grbl.h" uint8_t serial_rx_buffer[RX_BUFFER_SIZE]; uint8_t serial_rx_buffer_head = 0; volatile uint8_t serial_rx_buffer_tail = 0; uint8_t serial_tx_buffer[TX_BUFFER_SIZE]; uint8_t serial_tx_buffer_head = 0; volatile uint8_t serial_tx_buffer_tail = 0; #ifdef ENABLE_XONXOFF volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable #endif // Returns the number of bytes used in the RX serial buffer. uint8_t serial_get_rx_buffer_count() { uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile if (serial_rx_buffer_head >= rtail) { return(serial_rx_buffer_head-rtail); } return (RX_BUFFER_SIZE - (rtail-serial_rx_buffer_head)); } // Returns the number of bytes used in the TX serial buffer. // NOTE: Not used except for debugging and ensuring no TX bottlenecks. uint8_t serial_get_tx_buffer_count() { uint8_t ttail = serial_tx_buffer_tail; // Copy to limit multiple calls to volatile if (serial_tx_buffer_head >= ttail) { return(serial_tx_buffer_head-ttail); } return (TX_BUFFER_SIZE - (ttail-serial_tx_buffer_head)); } 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; // enable rx and tx UCSR0B |= 1<= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) { flow_ctrl = SEND_XOFF; UCSR0B |= (1 << UDRIE0); // Force TX } #endif } //TODO: else alarm on overflow? } } void serial_reset_read_buffer() { serial_rx_buffer_tail = serial_rx_buffer_head; #ifdef ENABLE_XONXOFF flow_ctrl = XON_SENT; #endif } ================================================ FILE: serial.h ================================================ /* serial.c - Low level functions for sending and recieving bytes via the serial port Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 serial_h #define serial_h #ifndef RX_BUFFER_SIZE #define RX_BUFFER_SIZE 128 #endif #ifndef TX_BUFFER_SIZE #define TX_BUFFER_SIZE 64 #endif #define SERIAL_NO_DATA 0xff #ifdef ENABLE_XONXOFF #define RX_BUFFER_FULL 96 // XOFF high watermark #define RX_BUFFER_LOW 64 // XON low watermark #define SEND_XOFF 1 #define SEND_XON 2 #define XOFF_SENT 3 #define XON_SENT 4 #define XOFF_CHAR 0x13 #define XON_CHAR 0x11 #endif void serial_init(); // Writes one byte to the TX serial buffer. Called by main program. void serial_write(uint8_t data); // Fetches the first byte in the serial read buffer. Called by main program. uint8_t serial_read(); // Reset and empty data in read buffer. Used by e-stop and reset. void serial_reset_read_buffer(); // Returns the number of bytes used in the RX serial buffer. uint8_t serial_get_rx_buffer_count(); // Returns the number of bytes used in the TX serial buffer. // NOTE: Not used except for debugging and ensuring no TX bottlenecks. uint8_t serial_get_tx_buffer_count(); #endif ================================================ FILE: settings.c ================================================ /* settings.c - eeprom configuration handling Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 "grbl.h" settings_t settings; // Method to store startup lines into EEPROM void settings_store_startup_line(uint8_t n, char *line) { uint32_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 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) { uint32_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 restore EEPROM-saved Grbl global settings back to defaults. void settings_restore(uint8_t restore_flag) { if (restore_flag & SETTINGS_RESTORE_DEFAULTS) { settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK; settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK; settings.status_report_mask = DEFAULT_STATUS_REPORT_MASK; settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; settings.arc_tolerance = DEFAULT_ARC_TOLERANCE; settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE; settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE; settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; settings.flags = 0; if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } if (DEFAULT_SOFT_LIMIT_ENABLE) { settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; } if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } 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.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.max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL); settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL); settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL); write_global_settings(); } if (restore_flag & SETTINGS_RESTORE_PARAMETERS) { uint8_t idx; float coord_data[N_AXIS]; memset(&coord_data, 0, sizeof(coord_data)); for (idx=0; idx <= SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data); } } if (restore_flag & SETTINGS_RESTORE_STARTUP_LINES) { #if N_STARTUP_LINE > 0 eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK, 0); #endif #if N_STARTUP_LINE > 1 eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+1), 0); #endif } if (restore_flag & SETTINGS_RESTORE_BUILD_INFO) { eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0); } } // Reads startup line from EEPROM. Updated pointed line string data. uint8_t settings_read_startup_line(uint8_t n, char *line) { uint32_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; // Empty line settings_store_startup_line(n, line); return(false); } return(true); } // 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; // Empty line settings_store_build_info(line); return(false); } 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) { uint32_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); } 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 { return(false); } return(true); } // A helper method to set settings from command line uint8_t settings_store_global_setting(uint8_t parameter, float value) { if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); } if (parameter >= AXIS_SETTINGS_START_VAL) { // Store axis configuration. Axis numbering sequence set by AXIS_SETTING defines. // NOTE: Ensure the setting index corresponds to the report.c settings printout. parameter -= AXIS_SETTINGS_START_VAL; uint8_t set_idx = 0; while (set_idx < AXIS_N_SETTINGS) { if (parameter < N_AXIS) { // Valid axis setting found. switch (set_idx) { case 0: #ifdef MAX_STEP_RATE_HZ if (value*settings.max_rate[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); } #endif settings.steps_per_mm[parameter] = value; break; case 1: #ifdef MAX_STEP_RATE_HZ if (value*settings.steps_per_mm[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); } #endif settings.max_rate[parameter] = value; break; case 2: settings.acceleration[parameter] = value*60*60; break; // Convert to mm/min^2 for grbl internal use. case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use. } break; // Exit while-loop after setting has been configured and proceed to the EEPROM write call. } else { set_idx++; // If axis index greater than N_AXIS or setting index greater than number of axis settings, error out. if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) { return(STATUS_INVALID_STATEMENT); } parameter -= AXIS_SETTINGS_INCREMENT; } } } else { // Store non-axis Grbl settings uint8_t int_value = trunc(value); switch(parameter) { case 0: if (int_value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } settings.pulse_microseconds = int_value; break; case 1: settings.stepper_idle_lock_time = int_value; break; case 2: settings.step_invert_mask = int_value; st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks. break; case 3: settings.dir_invert_mask = int_value; st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks. break; case 4: // Reset to ensure change. Immediate re-init may cause problems. if (int_value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } break; case 5: // Reset to ensure change. Immediate re-init may cause problems. if (int_value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; } break; case 6: // Reset to ensure change. Immediate re-init may cause problems. if (int_value) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; } else { settings.flags &= ~BITFLAG_INVERT_PROBE_PIN; } break; case 10: settings.status_report_mask = int_value; break; case 11: settings.junction_deviation = value; break; case 12: settings.arc_tolerance = value; break; case 13: if (int_value) { settings.flags |= BITFLAG_REPORT_INCHES; } else { settings.flags &= ~BITFLAG_REPORT_INCHES; } break; case 20: if (int_value) { if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); } settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; } else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; } break; case 21: if (int_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 22: if (int_value) { settings.flags |= BITFLAG_HOMING_ENABLE; } else { settings.flags &= ~BITFLAG_HOMING_ENABLE; settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits. } break; case 23: settings.homing_dir_mask = int_value; break; case 24: settings.homing_feed_rate = value; break; case 25: settings.homing_seek_rate = value; break; case 26: settings.homing_debounce_delay = int_value; break; case 27: 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_restore(SETTINGS_RESTORE_ALL); // Force restore all EEPROM data. report_grbl_settings(); } // NOTE: Checking paramater data, startup lines, and build info string should be done here, // but it seems fairly redundant. Each of these can be manually checked and reset or restored. // Check 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 checked and executed by protocol_main_loop at the end of initialization. } // Returns step pin mask according to Grbl internal axis indexing. uint8_t get_step_pin_mask(uint8_t axis_idx) { if ( axis_idx == X_AXIS ) { return((1<. */ #ifndef settings_h #define settings_h #include "grbl.h" // 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 9 // NOTE: Check settings_reset() when moving to next version. // Define bit flag masks for the boolean settings in settings.flag. #define BITFLAG_REPORT_INCHES bit(0) // #define BITFLAG_AUTO_START bit(1) // Obsolete. Don't alter to keep back compatibility. #define BITFLAG_INVERT_ST_ENABLE bit(2) #define BITFLAG_HARD_LIMIT_ENABLE bit(3) #define BITFLAG_HOMING_ENABLE bit(4) #define BITFLAG_SOFT_LIMIT_ENABLE bit(5) #define BITFLAG_INVERT_LIMIT_PINS bit(6) #define BITFLAG_INVERT_PROBE_PIN bit(7) // Define status reporting boolean enable bit flags in settings.status_report_mask #define BITFLAG_RT_STATUS_MACHINE_POSITION bit(0) #define BITFLAG_RT_STATUS_WORK_POSITION bit(1) #define BITFLAG_RT_STATUS_PLANNER_BUFFER bit(2) #define BITFLAG_RT_STATUS_SERIAL_RX bit(3) #define BITFLAG_RT_STATUS_LIMIT_PINS bit(4) // Define settings restore bitflags. #define SETTINGS_RESTORE_ALL 0xFF // All bitflags #define SETTINGS_RESTORE_DEFAULTS bit(0) #define SETTINGS_RESTORE_PARAMETERS bit(1) #define SETTINGS_RESTORE_STARTUP_LINES bit(2) #define SETTINGS_RESTORE_BUILD_INFO bit(3) // Define EEPROM memory address location values for Grbl settings and parameters // NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and // the startup script. The lower half contains the global settings and space for future // developments. #define EEPROM_ADDR_GLOBAL 1U #define EEPROM_ADDR_PARAMETERS 512U #define EEPROM_ADDR_STARTUP_BLOCK 768U #define EEPROM_ADDR_BUILD_INFO 942U // Define EEPROM address indexing for coordinate parameters #define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1) #define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM+1 // Total number of system stored (from index 0) // NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59) #define SETTING_INDEX_G28 N_COORDINATE_SYSTEM // Home position 1 #define SETTING_INDEX_G30 N_COORDINATE_SYSTEM+1 // Home position 2 // #define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset (G92.2,G92.3 not supported) // Define Grbl axis settings numbering scheme. Starts at START_VAL, every INCREMENT, over N_SETTINGS. #define AXIS_N_SETTINGS 4 #define AXIS_SETTINGS_START_VAL 100 // NOTE: Reserving settings values >= 100 for axis settings. Up to 255. #define AXIS_SETTINGS_INCREMENT 10 // Must be greater than the number of axis settings // Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards) typedef struct { // Axis settings float steps_per_mm[N_AXIS]; float max_rate[N_AXIS]; float acceleration[N_AXIS]; float max_travel[N_AXIS]; // Remaining Grbl settings uint8_t pulse_microseconds; uint8_t step_invert_mask; uint8_t dir_invert_mask; uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable. uint8_t status_report_mask; // Mask to indicate desired report data. float junction_deviation; float arc_tolerance; uint8_t flags; // Contains default boolean settings uint8_t homing_dir_mask; float homing_feed_rate; float homing_seek_rate; uint16_t homing_debounce_delay; float homing_pulloff; } settings_t; extern settings_t settings; // Initialize the configuration subsystem (load settings from EEPROM) void settings_init(); // Helper function to clear and restore EEPROM defaults void settings_restore(uint8_t restore_flag); // A helper method to set new settings from command line uint8_t settings_store_global_setting(uint8_t parameter, float value); // Stores the protocol line variable as a startup line in EEPROM void settings_store_startup_line(uint8_t n, char *line); // Reads an EEPROM startup line to the protocol line variable uint8_t settings_read_startup_line(uint8_t n, char *line); // Stores build info user-defined string void settings_store_build_info(char *line); // Reads build info user-defined string uint8_t settings_read_build_info(char *line); // Writes selected coordinate data to EEPROM void settings_write_coord_data(uint8_t coord_select, float *coord_data); // Reads selected coordinate data from EEPROM uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data); // Returns the step pin mask according to Grbl's internal axis numbering uint8_t get_step_pin_mask(uint8_t i); // Returns the direction pin mask according to Grbl's internal axis numbering uint8_t get_direction_pin_mask(uint8_t i); // Returns the limit pin mask according to Grbl's internal axis numbering uint8_t get_limit_pin_mask(uint8_t i); #endif ================================================ FILE: spindle_control.c ================================================ /* spindle_control.c - spindle control methods Part of Grbl Copyright (c) 2012-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 . */ /* RC-Servo PWM modification: switch between 0.6ms and 2.5ms pulse-width at 61Hz Prescaler 1024 = 15625Hz / 256Steps = 61Hz 64µs/step -> Values 15 / 32 for 1ms / 2ms Reload value = 0x07 Replace this file in C:\Program Files (x86)\Arduino\libraries\GRBL */ #include "grbl.h" #define RC_SERVO_SHORT 15 // Timer ticks for 0.6ms pulse duration (9 for 0.6ms) #define RC_SERVO_LONG 32 // Timer ticks for 2.5 ms pulse duration (39 for 2.5ms) //#define RC_SERVO_INVERT 1 // Uncomment to invert servo direction void spindle_init() { // Configure variable spindle PWM and enable pin, if requried. On the Uno, PWM and enable are // combined unless configured otherwise. #ifdef VARIABLE_SPINDLE SPINDLE_PWM_DDR |= (1< SPINDLE_RPM_RANGE ) { rpm = SPINDLE_RPM_RANGE; } // Prevent integer overflow } #ifdef RC_SERVO_INVERT current_pwm = floor( RC_SERVO_LONG - rpm*(RC_SERVO_RANGE/SPINDLE_RPM_RANGE)); OCR_REGISTER = current_pwm; #else current_pwm = floor( rpm*(RC_SERVO_RANGE/SPINDLE_RPM_RANGE) + RC_SERVO_SHORT); OCR_REGISTER = current_pwm; #endif #ifdef MINIMUM_SPINDLE_PWM if (current_pwm < MINIMUM_SPINDLE_PWM) { current_pwm = MINIMUM_SPINDLE_PWM; } OCR_REGISTER = current_pwm; #endif #endif } } spindle_set_state(uint8_t state, float rpm){ } ================================================ FILE: spindle_control.h ================================================ /* spindle_control.h - spindle control methods Part of Grbl Copyright (c) 2012-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 spindle_control_h #define spindle_control_h // Initializes spindle pins and hardware PWM, if enabled. void spindle_init(); // Sets spindle direction and spindle rpm via PWM, if enabled. void spindle_run(uint8_t direction, float rpm); void spindle_set_state(uint8_t state, float rpm); // Kills spindle. void spindle_stop(); #endif ================================================ FILE: stepper.c ================================================ /* stepper.c - stepper motor driver: executes motion plans using stepper motors Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 "grbl.h" // Some useful constants. #define DT_SEGMENT (1.0/(ACCELERATION_TICKS_PER_SECOND*60.0)) // min/segment #define REQ_MM_INCREMENT_SCALAR 1.25 #define RAMP_ACCEL 0 #define RAMP_CRUISE 1 #define RAMP_DECEL 2 // Define Adaptive Multi-Axis Step-Smoothing(AMASS) levels and cutoff frequencies. The highest level // frequency bin starts at 0Hz and ends at its cutoff frequency. The next lower level frequency bin // starts at the next higher cutoff frequency, and so on. The cutoff frequencies for each level must // be considered carefully against how much it over-drives the stepper ISR, the accuracy of the 16-bit // timer, and the CPU overhead. Level 0 (no AMASS, normal operation) frequency bin starts at the // Level 1 cutoff frequency and up to as fast as the CPU allows (over 30kHz in limited testing). // NOTE: AMASS cutoff frequency multiplied by ISR overdrive factor must not exceed maximum step frequency. // 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. 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) // Stores the planner block Bresenham algorithm execution data for the segments in the segment // buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will // never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1). // NOTE: This data is copied from the prepped planner blocks so that the planner blocks may be // discarded when entirely consumed and completed by the segment buffer. Also, AMASS alters this // data for its own use. typedef struct { uint8_t direction_bits; uint32_t steps[N_AXIS]; uint32_t step_event_count; } st_block_t; static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1]; // Primary stepper segment ring buffer. Contains small, short line segments for the stepper // algorithm to execute, which are "checked-out" incrementally from the first block in the // planner buffer. Once "checked-out", the steps in the segments buffer cannot be modified by // the planner, where the remaining planner block steps still can. typedef struct { uint16_t n_step; // Number of step events to be executed for this segment uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment. uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate. #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING uint8_t amass_level; // Indicates AMASS level for the ISR to execute this segment #else uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing. #endif } segment_t; static segment_t segment_buffer[SEGMENT_BUFFER_SIZE]; // Stepper ISR data struct. Contains the running data for the main stepper ISR. typedef struct { // Used by the bresenham line algorithm uint32_t counter_x, // Counter variables for the bresenham line tracer counter_y, counter_z; #ifdef STEP_PULSE_DELAY uint8_t step_bits; // Stores out_bits output to complete the step pulse delay #endif uint8_t execute_step; // Flags step execution for each interrupt. uint8_t step_pulse_time; // Step pulse reset time after step rise uint8_t step_outbits; // The next stepping-bits to be output uint8_t dir_outbits; #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING uint32_t steps[N_AXIS]; #endif uint16_t step_count; // Steps remaining in line segment motion uint8_t exec_block_index; // Tracks the current st_block index. Change indicates new block. st_block_t *exec_block; // Pointer to the block data for the segment being executed segment_t *exec_segment; // Pointer to the segment being executed } stepper_t; static stepper_t st; // Step segment ring buffer indices static volatile uint8_t segment_buffer_tail; static uint8_t segment_buffer_head; static uint8_t segment_next_head; // Step and direction port invert masks. static uint8_t step_port_invert_mask; static uint8_t dir_port_invert_mask; // Used to avoid ISR nesting of the "Stepper Driver Interrupt". Should never occur though. static volatile uint8_t busy; // Pointers for the step segment being prepped from the planner buffer. Accessed only by the // main program. Pointers may be planning segments or planner blocks ahead of what being executed. static plan_block_t *pl_block; // Pointer to the planner block being prepped static st_block_t *st_prep_block; // Pointer to the stepper block data being prepped // Segment preparation data struct. Contains all the necessary information to compute new segments // based on the current executing planner block. typedef struct { uint8_t st_block_index; // Index of stepper common data block being prepped uint8_t flag_partial_block; // Flag indicating the last block completed. Time to load a new one. float steps_remaining; float step_per_mm; // Current planner block step/millimeter conversion scalar float req_mm_increment; float dt_remainder; uint8_t ramp_type; // Current segment ramp state float mm_complete; // End of velocity profile from end of current planner block in (mm). // NOTE: This value must coincide with a step(no mantissa) when converted. float current_speed; // Current speed at the end of the segment buffer (mm/min) float maximum_speed; // Maximum speed of executing block. Not always nominal speed. (mm/min) float exit_speed; // Exit speed of executing block (mm/min) float accelerate_until; // Acceleration ramp end measured from end of block (mm) float decelerate_after; // Deceleration ramp start measured from end of block (mm) } st_prep_t; static st_prep_t prep; /* BLOCK VELOCITY PROFILE DEFINITION __________________________ /| |\ _________________ ^ / | | \ /| |\ | / | | \ / | | \ s / | | | | | \ p / | | | | | \ e +-----+------------------------+---+--+---------------+----+ e | BLOCK 1 ^ BLOCK 2 | d | time -----> EXAMPLE: Block 2 entry speed is at max junction velocity The planner block buffer is planned assuming constant acceleration velocity profiles and are continuously joined at block junctions as shown above. However, the planner only actively computes the block entry speeds for an optimal velocity plan, but does not compute the block internal velocity profiles. These velocity profiles are computed ad-hoc as they are executed by the stepper algorithm and consists of only 7 possible types of profiles: cruise-only, cruise- deceleration, acceleration-cruise, acceleration-only, deceleration-only, full-trapezoid, and triangle(no cruise). maximum_speed (< nominal_speed) -> + +--------+ <- maximum_speed (= nominal_speed) /|\ / \ / | \ current_speed -> + \ / | + <- exit_speed | + <- exit_speed / | | +-------------+ current_speed -> +----+--+ time --> ^ ^ ^ ^ | | | | decelerate_after(in mm) decelerate_after(in mm) ^ ^ ^ ^ | | | | accelerate_until(in mm) accelerate_until(in mm) The step segment buffer computes the executing block velocity profile and tracks the critical parameters for the stepper algorithm to accurately trace the profile. These critical parameters are shown and defined in the above illustration. */ // Stepper state initialization. Cycle should only start if the st.cycle_start flag is // enabled. Startup init and limits call this function but shouldn't start the cycle. void st_wake_up() { // Enable stepper drivers. if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1<> 3); // Set delay between direction pin write and step command. OCR0A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3); #else // Normal operation // Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement. st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3); #endif // Enable Stepper Driver Interrupt TIMSK1 |= (1<prescaler<cycles_per_tick; st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow. // If the new segment starts a new planner block, initialize stepper variables and counters. // NOTE: When the segment data index changes, this indicates a new planner block. if ( st.exec_block_index != st.exec_segment->st_block_index ) { st.exec_block_index = st.exec_segment->st_block_index; st.exec_block = &st_block_buffer[st.exec_block_index]; // Initialize Bresenham line and distance counters st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1); } st.dir_outbits = st.exec_block->direction_bits ^ dir_port_invert_mask; #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level. st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.exec_segment->amass_level; st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level; st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level; #endif } else { // Segment buffer empty. Shutdown. st_go_idle(); bit_true_atomic(sys.rt_exec_state,EXEC_CYCLE_STOP); // Flag main program for cycle end return; // Nothing to do but exit. } } // Check probing state. probe_state_monitor(); // Reset step out bits. st.step_outbits = 0; // Execute step displacement profile by Bresenham line algorithm #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING st.counter_x += st.steps[X_AXIS]; #else st.counter_x += st.exec_block->steps[X_AXIS]; #endif if (st.counter_x > st.exec_block->step_event_count) { st.step_outbits |= (1<step_event_count; if (st.exec_block->direction_bits & (1<steps[Y_AXIS]; #endif if (st.counter_y > st.exec_block->step_event_count) { st.step_outbits |= (1<step_event_count; if (st.exec_block->direction_bits & (1<steps[Z_AXIS]; #endif if (st.counter_z > st.exec_block->step_event_count) { st.step_outbits |= (1<step_event_count; if (st.exec_block->direction_bits & (1<entry_speed_sqr = prep.current_speed*prep.current_speed; // Update entry speed. pl_block = NULL; // Flag st_prep_segment() to load new velocity profile. } } /* Prepares step segment buffer. Continuously called from main program. The segment buffer is an intermediary buffer interface between the execution of steps by the stepper algorithm and the velocity profiles generated by the planner. The stepper algorithm only executes steps within the segment buffer and is filled by the main program when steps are "checked-out" from the first block in the planner buffer. This keeps the step execution and planning optimization processes atomic and protected from each other. The number of steps "checked-out" from the planner buffer and the number of segments in the segment buffer is sized and computed such that no operation in the main program takes longer than the time it takes the stepper algorithm to empty it before refilling it. Currently, the segment buffer conservatively holds roughly up to 40-50 msec of steps. NOTE: Computation units are in steps, millimeters, and minutes. */ void st_prep_buffer() { if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { // Check if we still need to generate more segments for a motion suspend. if (prep.current_speed == 0.0) { return; } // Nothing to do. Bail. } while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer. // Determine if we need to load a new planner block or if the block has been replanned. if (pl_block == NULL) { pl_block = plan_get_current_block(); // Query planner for a queued block if (pl_block == NULL) { return; } // No planner blocks. Exit. // Check if the segment buffer completed the last planner block. If so, load the Bresenham // data for the block. If not, we are still mid-block and the velocity profile was updated. if (prep.flag_partial_block) { prep.flag_partial_block = false; // Reset flag } else { // Increment stepper common data index to store new planner block data. if ( ++prep.st_block_index == (SEGMENT_BUFFER_SIZE-1) ) { prep.st_block_index = 0; } // Prepare and copy Bresenham algorithm segment data from the new planner block, so that // when the segment buffer completes the planner block, it may be discarded when the // segment buffer finishes the prepped block, but the stepper ISR is still executing it. st_prep_block = &st_block_buffer[prep.st_block_index]; st_prep_block->direction_bits = pl_block->direction_bits; #ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS]; st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS]; st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS]; st_prep_block->step_event_count = pl_block->step_event_count; #else // With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS // level, such that we never divide beyond the original data anywhere in the algorithm. // If the original data is divided, we can lose a step from integer roundoff. st_prep_block->steps[X_AXIS] = pl_block->steps[X_AXIS] << MAX_AMASS_LEVEL; st_prep_block->steps[Y_AXIS] = pl_block->steps[Y_AXIS] << MAX_AMASS_LEVEL; st_prep_block->steps[Z_AXIS] = pl_block->steps[Z_AXIS] << MAX_AMASS_LEVEL; st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL; #endif // Initialize segment buffer data for generating the segments. prep.steps_remaining = pl_block->step_event_count; prep.step_per_mm = prep.steps_remaining/pl_block->millimeters; prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR/prep.step_per_mm; prep.dt_remainder = 0.0; // Reset for new planner block if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { // Override planner block entry speed and enforce deceleration during feed hold. prep.current_speed = prep.exit_speed; pl_block->entry_speed_sqr = prep.exit_speed*prep.exit_speed; } else { prep.current_speed = sqrt(pl_block->entry_speed_sqr); } } /* --------------------------------------------------------------------------------- Compute the velocity profile of a new planner block based on its entry and exit speeds, or recompute the profile of a partially-completed planner block if the planner has updated it. For a commanded forced-deceleration, such as from a feed hold, override the planner velocities and decelerate to the target exit speed. */ prep.mm_complete = 0.0; // Default velocity profile complete at 0.0mm from end of block. float inv_2_accel = 0.5/pl_block->acceleration; if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { // [Forced Deceleration to Zero Velocity] // Compute velocity profile parameters for a feed hold in-progress. This profile overrides // the planner block profile, enforcing a deceleration to zero speed. prep.ramp_type = RAMP_DECEL; // Compute decelerate distance relative to end of block. float decel_dist = pl_block->millimeters - inv_2_accel*pl_block->entry_speed_sqr; if (decel_dist < 0.0) { // Deceleration through entire planner block. End of feed hold is not in this block. prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters); } else { prep.mm_complete = decel_dist; // End of feed hold. prep.exit_speed = 0.0; } } else { // [Normal Operation] // Compute or recompute velocity profile parameters of the prepped planner block. prep.ramp_type = RAMP_ACCEL; // Initialize as acceleration ramp. prep.accelerate_until = pl_block->millimeters; prep.exit_speed = plan_get_exec_block_exit_speed(); float exit_speed_sqr = prep.exit_speed*prep.exit_speed; float intersect_distance = 0.5*(pl_block->millimeters+inv_2_accel*(pl_block->entry_speed_sqr-exit_speed_sqr)); if (intersect_distance > 0.0) { if (intersect_distance < pl_block->millimeters) { // Either trapezoid or triangle types // NOTE: For acceleration-cruise and cruise-only types, following calculation will be 0.0. prep.decelerate_after = inv_2_accel*(pl_block->nominal_speed_sqr-exit_speed_sqr); if (prep.decelerate_after < intersect_distance) { // Trapezoid type prep.maximum_speed = sqrt(pl_block->nominal_speed_sqr); if (pl_block->entry_speed_sqr == pl_block->nominal_speed_sqr) { // Cruise-deceleration or cruise-only type. prep.ramp_type = RAMP_CRUISE; } else { // Full-trapezoid or acceleration-cruise types prep.accelerate_until -= inv_2_accel*(pl_block->nominal_speed_sqr-pl_block->entry_speed_sqr); } } else { // Triangle type prep.accelerate_until = intersect_distance; prep.decelerate_after = intersect_distance; prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr); } } else { // Deceleration-only type prep.ramp_type = RAMP_DECEL; // prep.decelerate_after = pl_block->millimeters; prep.maximum_speed = prep.current_speed; } } else { // Acceleration-only type prep.accelerate_until = 0.0; // prep.decelerate_after = 0.0; prep.maximum_speed = prep.exit_speed; } } } // Initialize new segment segment_t *prep_segment = &segment_buffer[segment_buffer_head]; // Set new segment to point to the current segment data block. prep_segment->st_block_index = prep.st_block_index; /*------------------------------------------------------------------------------------ Compute the average velocity of this new segment by determining the total distance traveled over the segment time DT_SEGMENT. The following code first attempts to create a full segment based on the current ramp conditions. If the segment time is incomplete when terminating at a ramp state change, the code will continue to loop through the progressing ramp states to fill the remaining segment execution time. However, if an incomplete segment terminates at the end of the velocity profile, the segment is considered completed despite having a truncated execution time less than DT_SEGMENT. The velocity profile is always assumed to progress through the ramp sequence: acceleration ramp, cruising state, and deceleration ramp. Each ramp's travel distance may range from zero to the length of the block. Velocity profiles can end either at the end of planner block (typical) or mid-block at the end of a forced deceleration, such as from a feed hold. */ float dt_max = DT_SEGMENT; // Maximum segment time float dt = 0.0; // Initialize segment time float time_var = dt_max; // Time worker variable float mm_var; // mm-Distance worker variable float speed_var; // Speed worker variable float mm_remaining = pl_block->millimeters; // New segment distance from end of block. float minimum_mm = mm_remaining-prep.req_mm_increment; // Guarantee at least one step. if (minimum_mm < 0.0) { minimum_mm = 0.0; } do { switch (prep.ramp_type) { case RAMP_ACCEL: // NOTE: Acceleration ramp only computes during first do-while loop. 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 time_var = 2.0*(pl_block->millimeters-mm_remaining)/(prep.current_speed+prep.maximum_speed); if (mm_remaining == prep.decelerate_after) { prep.ramp_type = RAMP_DECEL; } else { prep.ramp_type = RAMP_CRUISE; } prep.current_speed = prep.maximum_speed; } else { // Acceleration only. prep.current_speed += speed_var; } break; case RAMP_CRUISE: // NOTE: mm_var used to retain the last mm_remaining for incomplete segment time_var calculations. // NOTE: If maximum_speed*time_var value is too low, round-off can cause mm_var to not change. To // prevent this, simply enforce a minimum speed threshold in the planner. mm_var = mm_remaining - prep.maximum_speed*time_var; if (mm_var < prep.decelerate_after) { // End of cruise. // Cruise-deceleration junction or end of block. time_var = (mm_remaining - prep.decelerate_after)/prep.maximum_speed; mm_remaining = prep.decelerate_after; // NOTE: 0.0 at EOB prep.ramp_type = RAMP_DECEL; } else { // Cruising only. mm_remaining = mm_var; } break; default: // case RAMP_DECEL: // NOTE: mm_var used as a misc worker variable to prevent errors when near zero speed. speed_var = pl_block->acceleration*time_var; // Used as delta speed (mm/min) if (prep.current_speed > speed_var) { // Check if at or below zero speed. // Compute distance from end of segment to end of block. mm_var = mm_remaining - time_var*(prep.current_speed - 0.5*speed_var); // (mm) if (mm_var > prep.mm_complete) { // Deceleration only. mm_remaining = mm_var; prep.current_speed -= speed_var; break; // Segment complete. Exit switch-case statement. Continue do-while loop. } } // End of block or end of forced-deceleration. time_var = 2.0*(mm_remaining-prep.mm_complete)/(prep.current_speed+prep.exit_speed); mm_remaining = prep.mm_complete; } 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 > minimum_mm) { // Check for very slow segments with zero steps. // Increase segment time to ensure at least one step in segment. Override and loop // through distance calculations until minimum_mm or mm_complete. dt_max += DT_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. /* ----------------------------------------------------------------------------------- Compute segment step rate, steps to execute, and apply necessary rate corrections. NOTE: Steps are computed by direct scalar conversion of the millimeter distance remaining in the block, rather than incrementally tallying the steps executed per segment. This helps in removing floating point round-off issues of several additions. However, since floats have only 7.2 significant digits, long moves with extremely high step counts can exceed the precision of floats, which can lead to lost steps. Fortunately, this scenario is highly unlikely and unrealistic in CNC machines supported by Grbl (i.e. exceeding 10 meters axis travel at 200 step/mm). */ float steps_remaining = prep.step_per_mm*mm_remaining; // Convert mm_remaining to steps float n_steps_remaining = ceil(steps_remaining); // Round-up current steps remaining float last_n_steps_remaining = ceil(prep.steps_remaining); // Round-up last steps remaining prep_segment->n_step = last_n_steps_remaining-n_steps_remaining; // Compute number of steps to execute. // Bail if we are at the end of a feed hold and don't have a step to execute. if (prep_segment->n_step == 0) { if (sys.state & (STATE_HOLD|STATE_MOTION_CANCEL|STATE_SAFETY_DOOR)) { // Less than one step to decelerate to zero speed, but already very close. AMASS // requires full steps to execute. So, just bail. prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold. prep.dt_remainder = 0.0; prep.steps_remaining = n_steps_remaining; pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps. plan_cycle_reinitialize(); return; // Segment not generated, but current step data still retained. } } // Compute segment step rate. Since steps are integers and mm distances traveled are not, // the end of every segment can have a partial step of varying magnitudes that are not // executed, because the stepper ISR requires whole steps due to the AMASS algorithm. To // compensate, we track the time to execute the previous segment's partial step and simply // apply it with the partial step distance to the current segment, so that it minutely // adjusts the whole segment rate to keep step output exact. These rate adjustments are // typically very small and do not adversely effect performance, but ensures that Grbl // outputs the exact acceleration and velocity profiles as computed by the planner. dt += prep.dt_remainder; // Apply previous segment partial step execute time float inv_rate = dt/(last_n_steps_remaining - steps_remaining); // Compute adjusted step rate inverse prep.dt_remainder = (n_steps_remaining - steps_remaining)*inv_rate; // Update segment partial step time // Compute CPU cycles per step for the prepped segment. uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step) #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // Compute step timing and multi-axis smoothing level. // 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; } else if (cycles < AMASS_LEVEL3) { prep_segment->amass_level = 2; } else { prep_segment->amass_level = 3; } cycles >>= prep_segment->amass_level; prep_segment->n_step <<= prep_segment->amass_level; } if (cycles < (1UL << 16)) { prep_segment->cycles_per_tick = cycles; } // < 65536 (4.1ms @ 16MHz) else { prep_segment->cycles_per_tick = 0xffff; } // Just set the slowest speed possible. #else // Compute step timing and timer prescalar for normal step generation. if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz) prep_segment->prescaler = 1; // prescaler: 0 prep_segment->cycles_per_tick = cycles; } else if (cycles < (1UL << 19)) { // < 524288 (32.8ms@16MHz) prep_segment->prescaler = 2; // prescaler: 8 prep_segment->cycles_per_tick = cycles >> 3; } else { prep_segment->prescaler = 3; // prescaler: 64 if (cycles < (1UL << 22)) { // < 4194304 (262ms@16MHz) prep_segment->cycles_per_tick = cycles >> 6; } else { // Just set the slowest speed possible. (Around 4 step/sec.) prep_segment->cycles_per_tick = 0xffff; } } #endif // Segment complete! Increment segment buffer indices. segment_buffer_head = segment_next_head; if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; } // Setup initial conditions for next segment. if (mm_remaining > prep.mm_complete) { // Normal operation. Block incomplete. Distance remaining in block to be executed. pl_block->millimeters = mm_remaining; prep.steps_remaining = steps_remaining; } else { // End of planner block or forced-termination. No more distance to be executed. if (mm_remaining > 0.0) { // At end of forced-termination. // Reset prep parameters for resuming and then bail. Allow the stepper ISR to complete // the segment queue, where realtime protocol will set new state upon receiving the // cycle stop flag from the ISR. Prep_segment is blocked until then. prep.current_speed = 0.0; // NOTE: (=0.0) Used to indicate completed segment calcs for hold. prep.dt_remainder = 0.0; prep.steps_remaining = ceil(steps_remaining); pl_block->millimeters = prep.steps_remaining/prep.step_per_mm; // Update with full steps. plan_cycle_reinitialize(); return; // Bail! } else { // End of planner block // The planner block is complete. All steps are set to be executed in the segment buffer. pl_block = NULL; // Set pointer to indicate check and load next planner block. plan_discard_current_block(); } } } } // Called by realtime status reporting to fetch the current speed being executed. This value // however is not exactly the current speed, but the speed computed in the last step segment // in the segment buffer. It will always be behind by up to the number of segment blocks (-1) // divided by the ACCELERATION TICKS PER SECOND in seconds. #ifdef REPORT_REALTIME_RATE float st_get_realtime_rate() { if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR)){ return prep.current_speed; } return 0.0f; } #endif ================================================ FILE: stepper.h ================================================ /* stepper.h - stepper motor driver: executes motion plans of planner.c using the stepper motors Part of Grbl Copyright (c) 2011-2015 Sungeun K. Jeon Copyright (c) 2009-2011 Simen Svale Skogsrud 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 stepper_h #define stepper_h #ifndef SEGMENT_BUFFER_SIZE #define SEGMENT_BUFFER_SIZE 6 #endif // Initialize and setup the stepper motor subsystem void stepper_init(); // Enable steppers, but cycle does not start unless called by motion control or realtime command. void st_wake_up(); // Immediately disables steppers void st_go_idle(); // Generate the step and direction port invert masks. void st_generate_step_dir_invert_masks(); // Reset the stepper subsystem variables void st_reset(); // Reloads step segment buffer. Called continuously by realtime execution system. void st_prep_buffer(); // Called by planner_recalculate() when the executing block is updated by the new plan. void st_update_plan_block_parameters(); // Called by realtime status reporting if realtime rate reporting is enabled in config.h. #ifdef REPORT_REALTIME_RATE float st_get_realtime_rate(); #endif #endif ================================================ FILE: system.c ================================================ /* system.c - Handles system level commands and real-time processes Part of Grbl Copyright (c) 2014-2015 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 "grbl.h" void system_init() { CONTROL_DDR &= ~(CONTROL_MASK); // Configure as input pins #ifdef DISABLE_CONTROL_PIN_PULL_UP CONTROL_PORT &= ~(CONTROL_MASK); // Normal low operation. Requires external pull-down. #else CONTROL_PORT |= CONTROL_MASK; // Enable internal pull-up resistors. Normal high operation. #endif CONTROL_PCMSK |= CONTROL_MASK; // Enable specific pins of the Pin Change Interrupt PCICR |= (1 << CONTROL_INT); // Enable Pin Change Interrupt } // Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets // only the realtime command execute variable to have the main program execute these when // its ready. This works exactly like the character-based realtime commands when picked off // directly from the incoming serial data stream. ISR(CONTROL_INT_vect) { uint8_t pin = (CONTROL_PIN & CONTROL_MASK); #ifndef INVERT_CONTROL_PIN pin ^= CONTROL_MASK; #endif // Enter only if any CONTROL pin is detected as active. if (pin) { if (bit_istrue(pin,bit(RESET_BIT))) { mc_reset(); } else if (bit_istrue(pin,bit(CYCLE_START_BIT))) { bit_true(sys.rt_exec_state, EXEC_CYCLE_START); #ifndef ENABLE_SAFETY_DOOR_INPUT_PIN } else if (bit_istrue(pin,bit(FEED_HOLD_BIT))) { bit_true(sys.rt_exec_state, EXEC_FEED_HOLD); #else } else if (bit_istrue(pin,bit(SAFETY_DOOR_BIT))) { bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR); #endif } } } // Returns if safety door is ajar(T) or closed(F), based on pin state. uint8_t system_check_safety_door_ajar() { #ifdef ENABLE_SAFETY_DOOR_INPUT_PIN #ifdef INVERT_CONTROL_PIN return(bit_istrue(CONTROL_PIN,bit(SAFETY_DOOR_BIT))); #else return(bit_isfalse(CONTROL_PIN,bit(SAFETY_DOOR_BIT))); #endif #else return(false); // Input pin not enabled, so just return that it's closed. #endif } // 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 realtime 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 0 : report_grbl_help(); break; case '$': case 'G': case 'C': case 'X': if ( line[(char_counter+1)] != 0 ) { return(STATUS_INVALID_STATEMENT); } switch( line[char_counter] ) { case '$' : // Prints Grbl settings if ( sys.state & (STATE_CYCLE | STATE_HOLD) ) { return(STATUS_IDLE_ERROR); } // Block during cycle. Takes too long to print. else { report_grbl_settings(); } break; case 'G' : // Prints gcode parser state // TODO: Move this to realtime commands for GUIs to request this data during suspend-state. report_gcode_modes(); break; case 'C' : // Set check g-code mode [IDLE/CHECK] // 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); } // Requires no alarm mode. sys.state = STATE_CHECK_MODE; report_feedback_message(MESSAGE_ENABLED); } break; case 'X' : // Disable alarm lock [ALARM] 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. if (system_check_safety_door_ajar()) { // Check safety door switch before returning. bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR); protocol_execute_realtime(); // Enter safety door mode. } } // Otherwise, no effect. 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 realtime 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. // } // break; } break; 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 '#' : // Print Grbl NGC parameters if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); } else { report_ngc_parameters(); } break; case 'H' : // Perform homing cycle [IDLE/ALARM] if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_HOMING; // Set system state variable // Only perform homing if Grbl is idle or lost. // TODO: Likely not required. if (system_check_safety_door_ajar()) { // Check safety door switch before homing. bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR); protocol_execute_realtime(); // Enter safety door mode. } mc_homing_cycle(); if (!sys.abort) { // Execute startup scripts after successful homing. sys.state = STATE_IDLE; // Set to IDLE when complete. st_go_idle(); // Set steppers to the settings idle state before returning. system_execute_startup(line); } } else { return(STATUS_SETTING_DISABLED); } break; case 'I' : // Print or store build info. [IDLE/ALARM] if ( line[++char_counter] == 0 ) { settings_read_build_info(line); report_build_info(line); } else { // Store startup line [IDLE/ALARM] if(line[char_counter++] != '=') { return(STATUS_INVALID_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 'R' : // Restore defaults [IDLE/ALARM] if (line[++char_counter] != 'S') { return(STATUS_INVALID_STATEMENT); } if (line[++char_counter] != 'T') { return(STATUS_INVALID_STATEMENT); } if (line[++char_counter] != '=') { return(STATUS_INVALID_STATEMENT); } if (line[char_counter+2] != 0) { return(STATUS_INVALID_STATEMENT); } switch (line[++char_counter]) { case '$': settings_restore(SETTINGS_RESTORE_DEFAULTS); break; case '#': settings_restore(SETTINGS_RESTORE_PARAMETERS); break; case '*': settings_restore(SETTINGS_RESTORE_ALL); break; default: return(STATUS_INVALID_STATEMENT); } report_feedback_message(MESSAGE_RESTORE_DEFAULTS); mc_reset(); // Force reset to ensure settings are initialized correctly. break; case 'N' : // Startup lines. [IDLE/ALARM] 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 [IDLE Only] Prevents motion during ALARM. if (sys.state != STATE_IDLE) { return(STATUS_IDLE_ERROR); } // Store only when idle. helper_var = true; // Set helper_var to flag storing method. // No break. Continues into default: to read remaining command characters. } default : // Storing setting methods [IDLE/ALARM] if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } if(line[char_counter++] != '=') { return(STATUS_INVALID_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) || (parameter > 255)) { return(STATUS_INVALID_STATEMENT); } return(settings_store_global_setting((uint8_t)parameter, value)); } } } return(STATUS_OK); // If '$' command makes it to here, then everything's ok. } // Returns machine position of axis 'idx'. Must be sent a 'step' array. // NOTE: If motor steps and machine position are not in the same coordinate frame, this function // serves as a central place to compute the transformation. float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx) { float pos; #ifdef COREXY if (idx==A_MOTOR) { pos = 0.5*((steps[A_MOTOR] + steps[B_MOTOR])/settings.steps_per_mm[idx]); } else if (idx==B_MOTOR) { pos = 0.5*((steps[A_MOTOR] - steps[B_MOTOR])/settings.steps_per_mm[idx]); } else { pos = steps[idx]/settings.steps_per_mm[idx]; } #else pos = steps[idx]/settings.steps_per_mm[idx]; #endif return(pos); } void system_convert_array_steps_to_mpos(float *position, int32_t *steps) { uint8_t idx; for (idx=0; idx. */ #ifndef system_h #define system_h #include "grbl.h" // Define system executor bit map. Used internally by realtime protocol as realtime command flags, // which notifies the main program to execute the specified realtime command asynchronously. // NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default // flags are always false, so the realtime protocol only needs to check for a non-zero value to // know when there is a realtime 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_SAFETY_DOOR bit(5) // bitmask 00100000 #define EXEC_MOTION_CANCEL bit(6) // bitmask 01000000 // Alarm executor bit map. // NOTE: EXEC_CRITICAL_EVENT is an optional flag that must be set with an alarm flag. When enabled, // this halts Grbl into an infinite loop until the user aknowledges the problem and issues a soft- // reset command. For example, a hard limit event needs this type of halt and aknowledgement. #define EXEC_CRITICAL_EVENT bit(0) // bitmask 00000001 (SPECIAL FLAG. See NOTE:) #define EXEC_ALARM_HARD_LIMIT bit(1) // bitmask 00000010 #define EXEC_ALARM_SOFT_LIMIT bit(2) // bitmask 00000100 #define EXEC_ALARM_ABORT_CYCLE bit(3) // bitmask 00001000 #define EXEC_ALARM_PROBE_FAIL bit(4) // bitmask 00010000 #define EXEC_ALARM_HOMING_FAIL bit(5) // bitmask 00100000 // 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_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access. #define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only. #define STATE_HOMING bit(2) // Performing homing cycle #define STATE_CYCLE bit(3) // Cycle is running or motions are being executed. #define STATE_HOLD bit(4) // Active feed hold #define STATE_SAFETY_DOOR bit(5) // Safety door is ajar. Feed holds and de-energizes system. #define STATE_MOTION_CANCEL bit(6) // Motion cancel by feed hold and return to idle. // Define system suspend states. #define SUSPEND_DISABLE 0 // Must be zero. #define SUSPEND_ENABLE_HOLD bit(0) // Enabled. Indicates the cycle is active and currently undergoing a hold. #define SUSPEND_ENABLE_READY bit(1) // Ready to resume with a cycle start command. #define SUSPEND_ENERGIZE bit(2) // Re-energizes output before resume. #define SUSPEND_MOTION_CANCEL bit(3) // Cancels resume motion. Used by probing routine. // 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. uint8_t suspend; // System suspend bitflag variable that manages holds, cancels, and safety door. volatile uint8_t rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks. volatile uint8_t rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms. 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 homing_axis_lock; // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR. volatile uint8_t probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR. int32_t probe_position[N_AXIS]; // Last probe position in machine coordinates and steps. uint8_t probe_succeeded; // Tracks if last probing cycle was successful. } system_t; extern system_t sys; // Initialize the serial protocol void system_init(); // Returns if safety door is open or closed, based on pin state. uint8_t system_check_safety_door_ajar(); // Executes an internal system command, defined as a string starting with a '$' uint8_t system_execute_line(char *line); // Execute the startup script lines stored in EEPROM upon initialization void system_execute_startup(char *line); // Returns machine position of axis 'idx'. Must be sent a 'step' array. float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx); // Updates a machine 'position' array based on the 'step' array sent. void system_convert_array_steps_to_mpos(float *position, int32_t *steps); #endif