├── .gitignore ├── COPYING ├── Makefile ├── README.md ├── config.h ├── cpu_map.h ├── defaults.h ├── eeprom.c ├── eeprom.h ├── gcode.c ├── gcode.h ├── horus-fw.ino ├── laser_control.c ├── laser_control.h ├── ldr.c ├── ldr.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 ├── stepper.c ├── stepper.h ├── system.c └── system.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.hex 2 | *.o 3 | *.elf 4 | *.DS_Store 5 | *.d 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Part of Horus Firmware 2 | # 3 | # Copyright (c) 2014-2015 Mundo Reader S.L. 4 | # 5 | # Horus Firmware is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Horus Firmware is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with Horus Firmware. If not, see . 17 | 18 | # This file is based on work from Grbl v0.9, distributed under the 19 | # terms of the GPLv3. See COPYING for more details. 20 | # Copyright (c) 2009-2011 Simen Svale Skogsrud 21 | # Copyright (c) 2011-2014 Sungeun K. Jeon 22 | 23 | # This is a prototype Makefile. Modify it according to your needs. 24 | # You should at least check the settings for 25 | # DEVICE ....... The AVR device you compile for 26 | # CLOCK ........ Target AVR clock rate in Hertz 27 | # OBJECTS ...... The object files created from your source files. This list is 28 | # usually the same as the list of source files with suffix ".o". 29 | # PROGRAMMER ... Options to avrdude which define the hardware you use for 30 | # uploading to the AVR and the interface where this hardware 31 | # is connected. 32 | # FUSES ........ Parameters for avrdude to flash the fuses appropriately. 33 | 34 | DEVICE ?= atmega328p 35 | CLOCK = 16000000 36 | PROGRAMMER ?= -c avrisp2 -P usb 37 | OBJECTS = main.o motion_control.o gcode.o serial.o laser_control.o ldr.o \ 38 | protocol.o stepper.o eeprom.o settings.o planner.o nuts_bolts.o \ 39 | print.o probe.o report.o system.o 40 | # FUSES = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m 41 | FUSES = -U hfuse:w:0xd2:m -U lfuse:w:0xff:m 42 | # update that line with this when programmer is back up: 43 | # FUSES = -U hfuse:w:0xd7:m -U lfuse:w:0xff:m 44 | 45 | # Tune the lines below only if you know what you are doing: 46 | 47 | AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE) -B 10 -F 48 | COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -I. -ffunction-sections 49 | 50 | # symbolic targets: 51 | all: horus-fw.hex 52 | 53 | .c.o: 54 | $(COMPILE) -c $< -o $@ 55 | @$(COMPILE) -MM $< > $*.d 56 | 57 | .S.o: 58 | $(COMPILE) -x assembler-with-cpp -c $< -o $@ 59 | # "-x assembler-with-cpp" should not be necessary since this is the default 60 | # file type for the .S (with capital S) extension. However, upper case 61 | # characters are not always preserved on Windows. To ensure WinAVR 62 | # compatibility define the file type manually. 63 | 64 | .c.s: 65 | $(COMPILE) -S $< -o $@ 66 | 67 | flash: all 68 | $(AVRDUDE) -U flash:w:horus-fw.hex:i 69 | 70 | fuse: 71 | $(AVRDUDE) $(FUSES) 72 | 73 | # Xcode uses the Makefile targets "", "clean" and "install" 74 | install: flash fuse 75 | 76 | # if you use a bootloader, change the command below appropriately: 77 | load: all 78 | bootloadHID horus-fw.hex 79 | 80 | clean: 81 | rm -f horus-fw.hex main.elf $(OBJECTS) $(OBJECTS:.o=.d) 82 | 83 | # file targets: 84 | main.elf: $(OBJECTS) 85 | $(COMPILE) -o main.elf $(OBJECTS) -lm -Wl,--gc-sections 86 | 87 | horus-fw.hex: main.elf 88 | rm -f horus-fw.hex 89 | avr-objcopy -j .text -j .data -O ihex main.elf horus-fw.hex 90 | avr-size --format=berkeley main.elf 91 | # If you have an EEPROM section, you must also create a hex file for the 92 | # EEPROM and add it to the "flash" target. 93 | 94 | # Targets for code debugging and analysis: 95 | disasm: main.elf 96 | avr-objdump -d main.elf 97 | 98 | cpp: 99 | $(COMPILE) -E main.c 100 | 101 | # include generated header dependencies 102 | -include $(OBJECTS:.o=.d) 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Horus 3D Scanner Firmware 2 | 3 | This firmware is written in C. Version 0.2. 4 | 5 | Derived from Grbl v0.9 by Jesús Arroyo (Mundo Reader S.L.) 6 | 7 | Grbl's lead developer is Simen Svale Skogsrud. Sonney Jeon (Chamnit) improved some parts of grbl. 8 | 9 | 10 | ## Features 11 | 12 | * Angular stepper motor movement 13 | * Interrupt based movement with real angular acceleration 14 | * Laser modules control 15 | * Analog sensor read 16 | * Configuration interface with $ commands 17 | 18 | The default baudrate is 115200. 19 | 20 | 21 | ## Implemented G Codes 22 | 23 | * G1 - Angular movement 24 | * G50 - Reset all positions to zero 25 | * M0 - Program pause 26 | * M2 - Program end and reset 27 | * M17 - Enable/Power stepper motor 28 | * M18 - Disable stepper motor 29 | * M50 - Read LDR 30 | * M70 - Laser off 31 | * M71 - Laser on 32 | 33 | ## Build 34 | 35 | ### Arduino 36 | 37 | Open *horus-fw.ino*, select your board and upload. 38 | 39 | ### Make 40 | 41 | ```bash 42 | sudo apt-get install gcc-avr avr-libc 43 | make 44 | ``` 45 | 46 | The binary *horus-fw.hex* can be flashed with [Horus GUI](https://github.com/bqlabs/horus). 47 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | config.h - compile time configuration 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | // This file contains compile-time configurations for Grbl's internal system. For the most part, 28 | // users will not need to directly modify these, but they are here for specific needs, i.e. 29 | // performance tuning or adjusting to non-typical machines. 30 | 31 | // IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them. 32 | 33 | #ifndef config_h 34 | #define config_h 35 | #include "system.h" 36 | 37 | 38 | // Default settings. Used when resetting EEPROM. Change to desired name in defaults.h 39 | #define DEFAULTS_HORUS 40 | 41 | // This enables the serial port associated to the Bluetooth interface 42 | //#define BTENABLED // Enable BT interface 43 | 44 | // Serial baud rate 45 | #ifndef BTENABLED 46 | #define BAUD_RATE 115200 // Default baud rate 47 | #else 48 | #define BAUD_RATE 19200 // Bluetooth baudrate 49 | #endif 50 | 51 | 52 | // Default cpu mappings. Grbl officially supports the Arduino Uno only. Other processor types 53 | // may exist from user-supplied templates or directly user-defined in cpu_map.h 54 | #define CPU_MAP_ATMEGA328P_HORUS // Arduino Uno CPU for Horus Project 55 | 56 | // Define runtime command special characters. These characters are 'picked-off' directly from the 57 | // serial read data stream and are not passed to the grbl line execution parser. Select characters 58 | // that do not and must not exist in the streamed g-code program. ASCII control characters may be 59 | // used, if they are available per user setup. Also, extended ASCII codes (>127), which are never in 60 | // g-code programs, maybe selected for interface programs. 61 | // NOTE: If changed, manually update help message in report.c. 62 | #define CMD_STATUS_REPORT '?' 63 | #define CMD_FEED_HOLD '!' 64 | #define CMD_CYCLE_START '~' 65 | #define CMD_RESET 0x18 // ctrl-x. 66 | 67 | // If homing is enabled, homing init lock sets Grbl into an alarm state upon power up. This forces 68 | // the user to perform the homing cycle (or override the locks) before doing anything else. This is 69 | // mainly a safety feature to remind the user to home, since position is unknown to Grbl. 70 | #define HOMING_INIT_LOCK // Comment to disable 71 | 72 | // Define the homing cycle patterns with bitmasks. The homing cycle first performs a search mode 73 | // to quickly engage the limit switches, followed by a slower locate mode, and finished by a short 74 | // pull-off motion to disengage the limit switches. The following HOMING_CYCLE_x defines are executed 75 | // in order starting with suffix 0 and completes the homing routine for the specified-axes only. If 76 | // an axis is omitted from the defines, it will not home, nor will the system update its position. 77 | // Meaning that this allows for users with non-standard cartesian machines, such as a lathe (x then z, 78 | // with no y), to configure the homing cycle behavior to their needs. 79 | // NOTE: The homing cycle is designed to allow sharing of limit pins, if the axes are not in the same 80 | // cycle, but this requires some pin settings changes in cpu_map.h file. For example, the default homing 81 | // cycle can share the Z limit pin with either X or Y limit pins, since they are on different cycles. 82 | // By sharing a pin, this frees up a precious IO pin for other purposes. In theory, all axes limit pins 83 | // may be reduced to one pin, if all axes are homed with seperate cycles, or vice versa, all three axes 84 | // on separate pin, but homed in one cycle. Also, it should be noted that the function of hard limits 85 | // will not be affected by pin sharing. 86 | // NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y. 87 | #define HOMING_CYCLE_0 (1< 3us, and, when added with the 208 | // user-supplied step pulse time, the total time must not exceed 127us. Reported successful 209 | // values for certain setups have ranged from 5 to 20us. 210 | // #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled. 211 | 212 | // The number of linear motions in the planner buffer to be planned at any give time. The vast 213 | // majority of RAM that Grbl uses is based on this buffer size. Only increase if there is extra 214 | // available RAM, like when re-compiling for a Mega or Sanguino. Or decrease if the Arduino 215 | // begins to crash due to the lack of available RAM or if the CPU is having trouble keeping 216 | // up with planning new incoming motions as they are executed. 217 | // #define BLOCK_BUFFER_SIZE 18 // Uncomment to override default in planner.h. 218 | 219 | // Governs the size of the intermediary step segment buffer between the step execution algorithm 220 | // and the planner blocks. Each segment is set of steps executed at a constant velocity over a 221 | // fixed time defined by ACCELERATION_TICKS_PER_SECOND. They are computed such that the planner 222 | // block velocity profile is traced exactly. The size of this buffer governs how much step 223 | // execution lead time there is for other Grbl processes have to compute and do their thing 224 | // before having to come back and refill this buffer, currently at ~50msec of step moves. 225 | // #define SEGMENT_BUFFER_SIZE 6 // Uncomment to override default in stepper.h. 226 | 227 | // Line buffer size from the serial input stream to be executed. Also, governs the size of 228 | // each of the startup blocks, as they are each stored as a string of this size. Make sure 229 | // to account for the available EEPROM at the defined memory address in settings.h and for 230 | // the number of desired startup blocks. 231 | // NOTE: 80 characters is not a problem except for extreme cases, but the line buffer size 232 | // can be too small and g-code blocks can get truncated. Officially, the g-code standards 233 | // support up to 256 characters. In future versions, this default will be increased, when 234 | // we know how much extra memory space we can re-invest into this. 235 | // #define LINE_BUFFER_SIZE 80 // Uncomment to override default in protocol.h 236 | 237 | // Serial send and receive buffer size. The receive buffer is often used as another streaming 238 | // buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming 239 | // interfaces will character count and track each block send to each block response. So, 240 | // increase the receive buffer if a deeper receive buffer is needed for streaming and avaiable 241 | // memory allows. The send buffer primarily handles messages in Grbl. Only increase if large 242 | // messages are sent and Grbl begins to stall, waiting to send the rest of the message. 243 | // NOTE: Buffer size values must be greater than zero and less than 256. 244 | // #define RX_BUFFER_SIZE 128 // Uncomment to override defaults in serial.h 245 | // #define TX_BUFFER_SIZE 64 246 | 247 | // Toggles XON/XOFF software flow control for serial communications. Not officially supported 248 | // due to problems involving the Atmega8U2 USB-to-serial chips on current Arduinos. The firmware 249 | // on these chips do not support XON/XOFF flow control characters and the intermediate buffer 250 | // in the chips cause latency and overflow problems with standard terminal programs. However, 251 | // using specifically-programmed UI's to manage this latency problem has been confirmed to work. 252 | // As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard 253 | // terminal programs since their firmware correctly manage these XON/XOFF characters. In any 254 | // case, please report any successes to grbl administrators! 255 | // #define ENABLE_XONXOFF // Default disabled. Uncomment to enable. 256 | 257 | // A simple software debouncing feature for hard limit switches. When enabled, the interrupt 258 | // monitoring the hard limit switch pins will enable the Arduino's watchdog timer to re-check 259 | // the limit pin state after a delay of about 32msec. This can help with CNC machines with 260 | // problematic false triggering of their hard limit switches, but it WILL NOT fix issues with 261 | // electrical interference on the signal cables from external sources. It's recommended to first 262 | // use shielded signal cables with their shielding connected to ground (old USB/computer cables 263 | // work well and are cheap to find) and wire in a low-pass circuit into each limit pin. 264 | // #define ENABLE_SOFTWARE_DEBOUNCE // Default disabled. Uncomment to enable. 265 | 266 | // --------------------------------------------------------------------------------------- 267 | 268 | // TODO: Install compile-time option to send numeric status codes rather than strings. 269 | 270 | // --------------------------------------------------------------------------------------- 271 | // COMPILE-TIME ERROR CHECKING OF DEFINE VALUES: 272 | 273 | // #if (ISR_TICKS_PER_ACCELERATION_TICK > 255) 274 | // #error Parameters ACCELERATION_TICKS / ISR_TICKS must be < 256 to prevent integer overflow. 275 | // #endif 276 | 277 | // --------------------------------------------------------------------------------------- 278 | 279 | 280 | #endif 281 | -------------------------------------------------------------------------------- /cpu_map.h: -------------------------------------------------------------------------------- 1 | /* 2 | cpu_map.h - CPU and pin mapping configuration file 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2012-2014 Sungeun K. Jeon 24 | */ 25 | 26 | /* The cpu_map.h file serves as a central pin mapping settings file for different processor 27 | types, i.e. AVR 328p or AVR Mega 2560. Grbl officially supports the Arduino Uno, but the 28 | other supplied pin mappings are supplied by users, so your results may vary. */ 29 | 30 | // NOTE: This is still a work in progress. We are still centralizing the configurations to 31 | // this file, so your success may vary for other CPUs. 32 | 33 | #ifndef cpu_map_h 34 | #define cpu_map_h 35 | 36 | //---------------------------------------------------------------------------------------- 37 | 38 | #ifdef CPU_MAP_ATMEGA328P_HORUS // Arduino Uno for Horus Project 39 | 40 | // Serial port pins 41 | #define SERIAL_RX USART_RX_vect 42 | #define SERIAL_UDRE USART_UDRE_vect 43 | 44 | // Define laser pulse output pins. NOTE: All laser pins must be on the same port. 45 | #define LASER_DDR DDRD 46 | #define LASER_PORT PORTD 47 | #define LASER1_BIT 2 // Uno Digital Pin 2 48 | #define LASER2_BIT 3 // Uno Digital Pin 3 49 | #define LASER3_BIT 4 // Uno Digital Pin 4 50 | #define LASER4_BIT 5 // Uno Digital Pin 5 51 | #define LASER_MASK ((1<. 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2012-2014 Sungeun K. Jeon 24 | */ 25 | 26 | /* The defaults.h file serves as a central default settings file for different machine 27 | types, from DIY CNC mills to CNC conversions of off-the-shelf machines. The settings 28 | here are supplied by users, so your results may vary. However, this should give you 29 | a good starting point as you get to know your machine and tweak the settings for your 30 | our nefarious needs. */ 31 | 32 | #ifndef defaults_h 33 | #define defaults_h 34 | 35 | #ifdef DEFAULTS_HORUS 36 | // Grbl generic default settings. Should work across different machines. 37 | #define DEFAULT_X_STEPS_PER_DEG 16/1.8 // step/deg 38 | #define DEFAULT_Y_STEPS_PER_DEG 16/1.8 // step/deg 39 | #define DEFAULT_Z_STEPS_PER_DEG 16/1.8 // step/deg 40 | #define DEFAULT_X_MAX_RATE (180.0*60) // 180*60 deg/min = 180 deg/sec 41 | #define DEFAULT_Y_MAX_RATE (180.0*60) // 180*60 deg/min = 180 deg/sec 42 | #define DEFAULT_Z_MAX_RATE (180.0*60) // 180*60 deg/min = 180 deg/sec 43 | #define DEFAULT_X_ACCELERATION (180.0*60*60) // 180*60*60 deg/min^2 = 180 deg/sec^2 44 | #define DEFAULT_Y_ACCELERATION (180.0*60*60) // 180*60*60 deg/min^2 = 180 deg/sec^2 45 | #define DEFAULT_Z_ACCELERATION (180.0*60*60) // 180*60*60 deg/min^2 = 180 deg/sec^2 46 | #define DEFAULT_X_MAX_TRAVEL 1080.0 // deg 47 | #define DEFAULT_Y_MAX_TRAVEL 1080.0 // deg 48 | #define DEFAULT_Z_MAX_TRAVEL 1080.0 // deg 49 | #define DEFAULT_STEP_PULSE_MICROSECONDS 10 50 | #define DEFAULT_STEPPING_INVERT_MASK 0 51 | #define DEFAULT_DIRECTION_INVERT_MASK ((1< 25 | #include 26 | 27 | /* These EEPROM bits have different names on different devices. */ 28 | #ifndef EEPE 29 | #define EEPE EEWE //!< EEPROM program/write enable. 30 | #define EEMPE EEMWE //!< EEPROM master program/write enable. 31 | #endif 32 | 33 | /* These two are unfortunately not defined in the device include files. */ 34 | #define EEPM1 5 //!< EEPROM Programming Mode Bit 1. 35 | #define EEPM0 4 //!< EEPROM Programming Mode Bit 0. 36 | 37 | /* Define to reduce code size. */ 38 | #define EEPROM_IGNORE_SELFPROG //!< Remove SPM flag polling. 39 | 40 | /*! \brief Read byte from EEPROM. 41 | * 42 | * This function reads one byte from a given EEPROM address. 43 | * 44 | * \note The CPU is halted for 4 clock cycles during EEPROM read. 45 | * 46 | * \param addr EEPROM address to read from. 47 | * \return The byte read from the EEPROM address. 48 | */ 49 | unsigned char eeprom_get_char( unsigned int addr ) 50 | { 51 | do {} while( EECR & (1< 0; size--) { 133 | checksum = (checksum << 1) || (checksum >> 7); 134 | checksum += *source; 135 | eeprom_put_char(destination++, *(source++)); 136 | } 137 | eeprom_put_char(destination, checksum); 138 | } 139 | 140 | int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size) { 141 | unsigned char data, checksum = 0; 142 | for(; size > 0; size--) { 143 | data = eeprom_get_char(source++); 144 | checksum = (checksum << 1) || (checksum >> 7); 145 | checksum += data; 146 | *(destination++) = data; 147 | } 148 | return(checksum == eeprom_get_char(source)); 149 | } 150 | 151 | // end of file 152 | -------------------------------------------------------------------------------- /eeprom.h: -------------------------------------------------------------------------------- 1 | /* 2 | eeprom.h - EEPROM methods 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl, distributed under the 22 | terms of the MIT-license. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | */ 25 | 26 | #ifndef eeprom_h 27 | #define eeprom_h 28 | 29 | unsigned char eeprom_get_char(unsigned int addr); 30 | void eeprom_put_char(unsigned int addr, unsigned char new_value); 31 | void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size); 32 | int memcpy_from_eeprom_with_checksum(char *destination, unsigned int source, unsigned int size); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /gcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | gcode.h - rs274/ngc parser. 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #ifndef gcode_h 28 | #define gcode_h 29 | 30 | 31 | #include "ldr.h" 32 | 33 | // Define modal group internal numbers for checking multiple command violations and tracking the 34 | // type of command that is called in the block. A modal group is a group of g-code commands that are 35 | // mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute 36 | // a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online, 37 | // and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc). 38 | // NOTE: Modal group define values must be sequential and starting from zero. 39 | #define MODAL_GROUP_G0 0 // [G4,G10,G28,G28.1,G30,G30.1,G53,G92,G92.1] Non-modal 40 | #define MODAL_GROUP_G1 1 // [G0,G1,G2,G3,G38.2,G80] Motion 41 | #define MODAL_GROUP_G2 2 // [G17,G18,G19] Plane selection 42 | #define MODAL_GROUP_G3 3 // [G90,G91] Distance mode 43 | #define MODAL_GROUP_G5 4 // [G93,G94] Feed rate mode 44 | #define MODAL_GROUP_G6 5 // [G20,G21] Units 45 | #define MODAL_GROUP_G8 6 // [G43,G43.1,G49] Tool length offset 46 | #define MODAL_GROUP_G12 7 // [G54,G55,G56,G57,G58,G59] Coordinate system selection 47 | 48 | #define MODAL_GROUP_M4 8 // [M0,M1,M2,M30] Stopping 49 | #define MODAL_GROUP_M7 9 // [M3,M4,M5] Spindle turning 50 | #define MODAL_GROUP_M8 10 // [M7,M8,M9] Coolant control 51 | 52 | #define OTHER_INPUT_F 11 53 | #define OTHER_INPUT_S 12 54 | #define OTHER_INPUT_T 13 55 | 56 | #define MODAL_GROUP_M70 14 // [M70,M71] Laser control 57 | 58 | // Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used 59 | // internally by the parser to know which command to execute. 60 | 61 | // Modal Group G0: Non-modal actions 62 | #define NON_MODAL_NO_ACTION 0 // (Default: Must be zero) 63 | #define NON_MODAL_DWELL 1 // G4 64 | #define NON_MODAL_SET_COORDINATE_DATA 2 // G10 65 | #define NON_MODAL_GO_HOME_0 3 // G28 66 | #define NON_MODAL_SET_HOME_0 4 // G28.1 67 | #define NON_MODAL_GO_HOME_1 5 // G30 68 | #define NON_MODAL_SET_HOME_1 6 // G30.1 69 | #define NON_MODAL_ABSOLUTE_OVERRIDE 7 // G53 70 | #define NON_MODAL_SET_COORDINATE_OFFSET 8 // G92 71 | #define NON_MODAL_RESET_COORDINATE_OFFSET 9 //G92.1 72 | #define NON_MODAL_RESET_POSITION 10 //G50 73 | 74 | // Modal Group G1: Motion modes 75 | #define MOTION_MODE_SEEK 0 // G0 (Default: Must be zero) 76 | #define MOTION_MODE_LINEAR 1 // G1 77 | #define MOTION_MODE_CW_ARC 2 // G2 78 | #define MOTION_MODE_CCW_ARC 3 // G3 79 | #define MOTION_MODE_PROBE 4 // G38.2 80 | #define MOTION_MODE_NONE 5 // G80 81 | 82 | // Modal Group G2: Plane select 83 | #define PLANE_SELECT_XY 0 // G17 (Default: Must be zero) 84 | #define PLANE_SELECT_ZX 1 // G18 85 | #define PLANE_SELECT_YZ 2 // G19 86 | 87 | // Modal Group G3: Distance mode 88 | #define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero) 89 | #define DISTANCE_MODE_INCREMENTAL 1 // G91 90 | 91 | // Modal Group M4: Program flow 92 | #define PROGRAM_FLOW_RUNNING 0 // (Default: Must be zero) 93 | #define PROGRAM_FLOW_PAUSED 1 // M0, M1 94 | #define PROGRAM_FLOW_COMPLETED 2 // M2, M30 95 | 96 | // Modal Group G5: Feed rate mode 97 | #define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero) 98 | #define FEED_RATE_MODE_INVERSE_TIME 1 // G93 99 | 100 | // Modal Group G6: Units mode 101 | #define UNITS_MODE_MM 0 // G21 (Default: Must be zero) 102 | #define UNITS_MODE_INCHES 1 // G20 103 | 104 | // Modal Group M7: Spindle control 105 | #define SPINDLE_DISABLE 0 // M5 (Default: Must be zero) 106 | #define SPINDLE_ENABLE_CW 1 // M3 107 | #define SPINDLE_ENABLE_CCW 2 // M4 108 | 109 | // Modal Group M8: Coolant control 110 | #define COOLANT_DISABLE 0 // M9 (Default: Must be zero) 111 | #define COOLANT_MIST_ENABLE 1 // M7 112 | #define COOLANT_FLOOD_ENABLE 2 // M8 113 | 114 | // Modal Group G8: Tool length offset 115 | #define TOOL_LENGTH_OFFSET_CANCEL 0 // G49 (Default: Must be zero) 116 | #define TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC 1 // G43.1 117 | 118 | // Modal Group G12: Active work coordinate system 119 | // N/A: Stores coordinate system value (54-59) to change to. 120 | 121 | // Modal Group M70: Laser control 122 | #define LASER_DISABLE 0 // M70 123 | #define LASER_ENABLE 1 // M71 124 | 125 | // Modal Group: Motor control 126 | #define MOTOR_ENABLE 1 // M17 127 | #define MOTOR_DISABLE 0 // M18 128 | 129 | // Modal Group: LDR 130 | #define LDR_READ 1 131 | 132 | #define WORD_F 0 133 | #define WORD_I 1 134 | #define WORD_J 2 135 | #define WORD_K 3 136 | #define WORD_L 4 137 | #define WORD_N 5 138 | #define WORD_P 6 139 | #define WORD_R 7 140 | #define WORD_S 8 141 | #define WORD_T 9 142 | #define WORD_X 10 143 | #define WORD_Y 11 144 | #define WORD_Z 12 145 | 146 | 147 | 148 | 149 | // NOTE: When this struct is zeroed, the above defines set the defaults for the system. 150 | typedef struct { 151 | uint8_t motion; // {G0,G1,G2,G3,G38.2,G80} 152 | uint8_t feed_rate; // {G93,G94} 153 | uint8_t units; // {G20,G21} 154 | uint8_t distance; // {G90,G91} 155 | uint8_t plane_select; // {G17,G18,G19} 156 | uint8_t tool_length; // {G43.1,G49} 157 | uint8_t coord_select; // {G54,G55,G56,G57,G58,G59} 158 | uint8_t program_flow; // {M0,M1,M2,M30} 159 | uint8_t coolant; // {M7,M8,M9} 160 | uint8_t spindle; // {M3,M4,M5} 161 | uint8_t laser; // {M70,M71} 162 | uint8_t motor; // {M17,M18} 163 | uint8_t ldr; // {M50} 164 | } gc_modal_t; 165 | 166 | typedef struct { 167 | float f; // Feed 168 | float ijk[3]; // I,J,K Axis arc offsets 169 | uint8_t l; // G10 or canned cycles parameters 170 | int32_t n; // Line number 171 | float p; // G10 or dwell parameters 172 | // float q; // G82 peck drilling 173 | float r; // Arc radius 174 | float s; // Spindle speed 175 | uint8_t t; // Tool selection 176 | float xyz[3]; // X,Y,Z Translational axes 177 | } gc_values_t; 178 | 179 | 180 | typedef struct { 181 | gc_modal_t modal; 182 | 183 | float spindle_speed; // RPM 184 | float feed_rate; // Millimeters/min 185 | uint8_t tool; // Tracks tool number. NOT USED. 186 | 187 | float position[N_AXIS]; // Where the interpreter considers the tool to be at this point in the code 188 | 189 | float coord_system[N_AXIS]; // Current work coordinate system (G54+). Stores offset from absolute machine 190 | // position in mm. Loaded from EEPROM when called. 191 | float coord_offset[N_AXIS]; // Retains the G92 coordinate offset (work coordinates) relative to 192 | // machine zero in mm. Non-persistent. Cleared upon reset and boot. 193 | float tool_length_offset; // Tracks tool length offset value when enabled. 194 | } parser_state_t; 195 | extern parser_state_t gc_state; 196 | 197 | typedef struct { 198 | // uint16_t command_words; // NOTE: If this bitflag variable fills, G and M words can be separated. 199 | // uint16_t value_words; 200 | 201 | uint8_t non_modal_command; 202 | gc_modal_t modal; 203 | gc_values_t values; 204 | 205 | } parser_block_t; 206 | extern parser_block_t gc_block; 207 | 208 | // Initialize the parser 209 | void gc_init(); 210 | 211 | // Execute one block of rs275/ngc/g-code 212 | uint8_t gc_execute_line(char *line); 213 | 214 | // Set g-code parser position. Input in steps. 215 | void gc_sync_position(); 216 | 217 | #endif 218 | -------------------------------------------------------------------------------- /horus-fw.ino: -------------------------------------------------------------------------------- 1 | /* */ 2 | /* Hack file to use Arduino IDE */ 3 | /* */ 4 | -------------------------------------------------------------------------------- /laser_control.c: -------------------------------------------------------------------------------- 1 | /* 2 | laser_control.c - laser control methods 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2105 Jesus Arroyo (Mundo Reader S.L.) 6 | 7 | Horus is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus. If not, see . 19 | */ 20 | 21 | #include "system.h" 22 | #include "laser_control.h" 23 | #include "protocol.h" 24 | #include "gcode.h" 25 | 26 | // Used to count 1 second with timer2 27 | volatile uint8_t timer = 0; 28 | 29 | // Laser status array 30 | volatile uint8_t laser[4]; 31 | 32 | // Laser timer array 33 | volatile uint8_t laser_timer[4]; 34 | 35 | void laser_init() 36 | { 37 | // Initialize lasers 38 | LASER_DDR |= LASER_MASK; 39 | LASER_PORT &= ~LASER_MASK; 40 | 41 | // Initialize timer2 42 | cli(); // disable interrupts 43 | TCNT2 = 0; // initialize counter value 44 | OCR2A = 244-1; // compare match register 16MHz/256/256Hz 45 | TCCR2A = (1 << WGM21); // CTC mode 46 | TCCR2B = (1 << CS22) | (1 << CS21); // 256 prescaler 47 | TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt 48 | sei(); // enable interrupts 49 | } 50 | 51 | void laser_on(uint8_t laser_bit) 52 | { 53 | LASER_PORT |= laser_bit; 54 | } 55 | 56 | void laser_off(uint8_t laser_bit) 57 | { 58 | LASER_PORT &= ~laser_bit; 59 | } 60 | 61 | void laser_set(uint8_t id, uint8_t value) 62 | { 63 | uint8_t bit = 0; 64 | 65 | switch (id) { 66 | case 0: bit = (1< 0) { 73 | if (value == LASER_ENABLE) { 74 | laser_on(bit); 75 | laser[id] = 1; 76 | } else { 77 | laser_off(bit); 78 | laser[id] = 0; 79 | laser_timer[id] = 0; 80 | } 81 | } 82 | } 83 | 84 | void laser_run(uint8_t id, uint8_t value) 85 | { 86 | if (sys.state == STATE_CHECK_MODE) { return; } 87 | 88 | protocol_auto_cycle_start(); 89 | protocol_buffer_synchronize(); 90 | 91 | laser_set(id-1, value); 92 | } 93 | 94 | ISR(TIMER2_COMPA_vect) 95 | { 96 | if (!++timer) { // 1 second reached! 97 | uint8_t i; 98 | for (i = 0; i < 4; i++) { 99 | if (laser[i]) laser_timer[i]++; 100 | if (laser_timer[i] == 255) // Laser i disabled after 255 seconds max 101 | laser_set(i, LASER_DISABLE); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /laser_control.h: -------------------------------------------------------------------------------- 1 | /* 2 | laser_control.h - laser control methods 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Jesus Arroyo (Mundo Reader S.L.) 6 | 7 | Horus is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus. If not, see . 19 | */ 20 | 21 | #ifndef laser_control_h 22 | #define laser_control_h 23 | 24 | 25 | void laser_init(); 26 | void laser_off(uint8_t laser_bit); 27 | void laser_on(uint8_t laser_bit); 28 | void laser_run(uint8_t mode, uint8_t value); 29 | 30 | #endif -------------------------------------------------------------------------------- /ldr.c: -------------------------------------------------------------------------------- 1 | /* 2 | ldr.c - analog read methods 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2015 Irene Sanz (Mundo Reader S.L.) 6 | 7 | Horus is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus. If not, see . 19 | */ 20 | 21 | #include "ldr.h" 22 | 23 | void ldr_init(void){ 24 | ADCSRA |= ((1<=0){ 40 | char buffer[5]; 41 | itoa(ldr_read(tool), buffer, 10); 42 | printString(buffer); 43 | printString("\r\n"); 44 | } 45 | } -------------------------------------------------------------------------------- /ldr.h: -------------------------------------------------------------------------------- 1 | /* 2 | ldr.h - analog read methods 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2015 Irene Sanz (Mundo Reader S.L.) 6 | 7 | Horus is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus. If not, see . 19 | */ 20 | 21 | #ifndef laser_control_h 22 | #define laser_control_h 23 | 24 | #include 25 | #include 26 | #define F_CPU 16000000UL 27 | #include 28 | #define BAUDRATE 9600 29 | #define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1) 30 | 31 | void ldr_init(void); 32 | uint16_t ldr_read(uint8_t channel); 33 | 34 | #endif -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | main.c - Firmware for 3D Scanners using g-codes 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #include "system.h" 28 | #include "serial.h" 29 | #include "settings.h" 30 | #include "protocol.h" 31 | #include "gcode.h" 32 | #include "planner.h" 33 | #include "stepper.h" 34 | #include "motion_control.h" 35 | #include "probe.h" 36 | #include "report.h" 37 | #include "ldr.h" 38 | 39 | 40 | // Declare system global variable structure 41 | system_t sys; 42 | 43 | 44 | int main(void) 45 | { 46 | // Initialize system upon power-up. 47 | serial_init(); // Setup serial baud rate and interrupts 48 | settings_init(); // Load grbl settings from EEPROM 49 | stepper_init(); // Configure stepper pins and interrupt timers 50 | system_init(); // Configure pinout pins and pin-change interrupt 51 | ldr_init(); //Setup the ADC 52 | 53 | memset(&sys, 0, sizeof(sys)); // Clear all system variables 54 | sys.abort = true; // Set abort to complete initialization 55 | sei(); // Enable interrupts 56 | 57 | // Check for power-up and set system alarm if homing is enabled to force homing cycle 58 | // by setting Grbl's alarm state. Alarm locks out all g-code commands, including the 59 | // startup scripts, but allows access to settings and internal commands. Only a homing 60 | // cycle '$H' or kill alarm locks '$X' will disable the alarm. 61 | // NOTE: The startup script will run after successful completion of the homing cycle, but 62 | // not after disabling the alarm locks. Prevents motion startup blocks from crashing into 63 | // things uncontrollably. Very bad. 64 | #ifdef HOMING_INIT_LOCK 65 | if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; } 66 | #endif 67 | 68 | // Grbl initialization loop upon power-up or a system abort. For the latter, all processes 69 | // will return to this loop to be cleanly re-initialized. 70 | for(;;) { 71 | 72 | // TODO: Separate configure task that require interrupts to be disabled, especially upon 73 | // a system abort and ensuring any active interrupts are cleanly reset. 74 | 75 | // Reset Grbl primary systems. 76 | serial_reset_read_buffer(); // Clear serial read buffer 77 | gc_init(); // Set g-code parser to default state 78 | laser_init(); 79 | probe_init(); 80 | plan_reset(); // Clear block buffer and planner variables 81 | st_reset(); // Clear stepper subsystem variables. 82 | 83 | // Sync cleared gcode and planner positions to current system position. 84 | plan_sync_position(); 85 | gc_sync_position(); 86 | 87 | // Reset system variables. 88 | sys.abort = false; 89 | sys.execute = 0; 90 | if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { sys.auto_start = true; } 91 | else { sys.auto_start = false; } 92 | 93 | // Start Grbl main loop. Processes program inputs and executes them. 94 | protocol_main_loop(); 95 | 96 | } 97 | return 0; /* Never reached */ 98 | } 99 | -------------------------------------------------------------------------------- /motion_control.c: -------------------------------------------------------------------------------- 1 | /* 2 | motion_control.c - high level interface for issuing motion commands 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011 Jens Geisler 25 | Copyright (c) 2011-2014 Sungeun K. Jeon 26 | */ 27 | 28 | #include "system.h" 29 | #include "settings.h" 30 | #include "protocol.h" 31 | #include "gcode.h" 32 | #include "planner.h" 33 | #include "stepper.h" 34 | #include "motion_control.h" 35 | #include "probe.h" 36 | #include "report.h" 37 | 38 | 39 | // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second 40 | // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in 41 | // (1 minute)/feed_rate time. 42 | // NOTE: This is the primary gateway to the grbl planner. All line motions, including arc line 43 | // segments, must pass through this routine before being passed to the planner. The seperation of 44 | // mc_line and plan_buffer_line is done primarily to place non-planner-type functions from being 45 | // in the planner and to let backlash compensation or canned cycle integration simple and direct. 46 | #ifdef USE_LINE_NUMBERS 47 | void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number) 48 | #else 49 | void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate) 50 | #endif 51 | { 52 | /*// If enabled, check for soft limit violations. Placed here all line motions are picked up 53 | // from everywhere in Grbl. 54 | if (bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)) { limits_soft_check(target); }*/ 55 | 56 | // If in check gcode mode, prevent motion by blocking planner. Soft limits still work. 57 | if (sys.state == STATE_CHECK_MODE) { return; } 58 | 59 | // NOTE: Backlash compensation may be installed here. It will need direction info to track when 60 | // to insert a backlash line motion(s) before the intended line motion and will require its own 61 | // plan_check_full_buffer() and check for system abort loop. Also for position reporting 62 | // backlash steps will need to be also tracked, which will need to be kept at a system level. 63 | // There are likely some other things that will need to be tracked as well. However, we feel 64 | // that backlash compensation should NOT be handled by Grbl itself, because there are a myriad 65 | // of ways to implement it and can be effective or ineffective for different CNC machines. This 66 | // would be better handled by the interface as a post-processor task, where the original g-code 67 | // is translated and inserts backlash motions that best suits the machine. 68 | // NOTE: Perhaps as a middle-ground, all that needs to be sent is a flag or special command that 69 | // indicates to Grbl what is a backlash compensation motion, so that Grbl executes the move but 70 | // doesn't update the machine position values. Since the position values used by the g-code 71 | // parser and planner are separate from the system machine positions, this is doable. 72 | 73 | // If the buffer is full: good! That means we are well ahead of the robot. 74 | // Remain in this loop until there is room in the buffer. 75 | do { 76 | protocol_execute_runtime(); // Check for any run-time commands 77 | if (sys.abort) { return; } // Bail, if system abort. 78 | if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full. 79 | else { break; } 80 | } while (1); 81 | 82 | #ifdef USE_LINE_NUMBERS 83 | plan_buffer_line(target, feed_rate, invert_feed_rate, line_number); 84 | #else 85 | plan_buffer_line(target, feed_rate, invert_feed_rate); 86 | #endif 87 | 88 | // If idle, indicate to the system there is now a planned block in the buffer ready to cycle 89 | // start. Otherwise ignore and continue on. 90 | if (!sys.state) { sys.state = STATE_QUEUED; } 91 | } 92 | 93 | 94 | // Execute an arc in offset mode format. position == current xyz, target == target xyz, 95 | // offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is 96 | // the direction of helical travel, radius == circle radius, isclockwise boolean. Used 97 | // for vector transformation direction. 98 | // The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance 99 | // of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal 100 | // distance from segment to the circle when the end points both lie on the circle. 101 | #ifdef USE_LINE_NUMBERS 102 | void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, 103 | uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, int32_t line_number) 104 | #else 105 | void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, 106 | uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear) 107 | #endif 108 | { 109 | float center_axis0 = position[axis_0] + offset[axis_0]; 110 | float center_axis1 = position[axis_1] + offset[axis_1]; 111 | float r_axis0 = -offset[axis_0]; // Radius vector from center to current location 112 | float r_axis1 = -offset[axis_1]; 113 | float rt_axis0 = target[axis_0] - center_axis0; 114 | float rt_axis1 = target[axis_1] - center_axis1; 115 | 116 | // CCW angle between position and target from circle center. Only one atan2() trig computation required. 117 | float angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1); 118 | if (gc_state.modal.motion == MOTION_MODE_CW_ARC) { // Correct atan2 output per direction 119 | if (angular_travel >= 0) { angular_travel -= 2*M_PI; } 120 | } else { 121 | if (angular_travel <= 0) { angular_travel += 2*M_PI; } 122 | } 123 | 124 | // NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to 125 | // (2x) settings.arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit 126 | // is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation. 127 | // For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases. 128 | uint16_t segments = floor(fabs(0.5*angular_travel*radius)/ 129 | sqrt(settings.arc_tolerance*(2*radius - settings.arc_tolerance)) ); 130 | 131 | if (segments) { 132 | // Multiply inverse feed_rate to compensate for the fact that this movement is approximated 133 | // by a number of discrete segments. The inverse feed_rate should be correct for the sum of 134 | // all segments. 135 | if (invert_feed_rate) { feed_rate *= segments; } 136 | 137 | float theta_per_segment = angular_travel/segments; 138 | float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments; 139 | 140 | /* Vector rotation by transformation matrix: r is the original vector, r_T is the rotated vector, 141 | and phi is the angle of rotation. Solution approach by Jens Geisler. 142 | r_T = [cos(phi) -sin(phi); 143 | sin(phi) cos(phi] * r ; 144 | 145 | For arc generation, the center of the circle is the axis of rotation and the radius vector is 146 | defined from the circle center to the initial position. Each line segment is formed by successive 147 | vector rotations. Single precision values can accumulate error greater than tool precision in rare 148 | cases. So, exact arc path correction is implemented. This approach avoids the problem of too many very 149 | expensive trig operations [sin(),cos(),tan()] which can take 100-200 usec each to compute. 150 | 151 | Small angle approximation may be used to reduce computation overhead further. A third-order approximation 152 | (second order sin() has too much error) holds for most, if not, all CNC applications. Note that this 153 | approximation will begin to accumulate a numerical drift error when theta_per_segment is greater than 154 | ~0.25 rad(14 deg) AND the approximation is successively used without correction several dozen times. This 155 | scenario is extremely unlikely, since segment lengths and theta_per_segment are automatically generated 156 | and scaled by the arc tolerance setting. Only a very large arc tolerance setting, unrealistic for CNC 157 | applications, would cause this numerical drift error. However, it is best to set N_ARC_CORRECTION from a 158 | low of ~4 to a high of ~20 or so to avoid trig operations while keeping arc generation accurate. 159 | 160 | This approximation also allows mc_arc to immediately insert a line segment into the planner 161 | without the initial overhead of computing cos() or sin(). By the time the arc needs to be applied 162 | a correction, the planner should have caught up to the lag caused by the initial mc_arc overhead. 163 | This is important when there are successive arc motions. 164 | */ 165 | // Computes: cos_T = 1 - theta_per_segment^2/2, sin_T = theta_per_segment - theta_per_segment^3/6) in ~52usec 166 | float cos_T = 2.0 - theta_per_segment*theta_per_segment; 167 | float sin_T = theta_per_segment*0.16666667*(cos_T + 4.0); 168 | cos_T *= 0.5; 169 | 170 | float sin_Ti; 171 | float cos_Ti; 172 | float r_axisi; 173 | uint16_t i; 174 | uint8_t count = 0; 175 | 176 | for (i = 1; i 0) { 227 | // NOTE: Check and execute runtime commands during dwell every <= DWELL_TIME_STEP milliseconds. 228 | protocol_execute_runtime(); 229 | if (sys.abort) { return; } 230 | _delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment 231 | } 232 | } 233 | 234 | 235 | /*// Perform homing cycle to locate and set machine zero. Only '$H' executes this command. 236 | // NOTE: There should be no motions in the buffer and Grbl must be in an idle state before 237 | // executing the homing cycle. This prevents incorrect buffered plans after homing. 238 | void mc_homing_cycle() 239 | { 240 | sys.state = STATE_HOMING; // Set system state variable 241 | limits_disable(); // Disable hard limits pin change register for cycle duration 242 | 243 | // ------------------------------------------------------------------------------------- 244 | // Perform homing routine. NOTE: Special motion case. Only system reset works. 245 | 246 | // Search to engage all axes limit switches at faster homing seek rate. 247 | limits_go_home(HOMING_CYCLE_0); // Homing cycle 0 248 | #ifdef HOMING_CYCLE_1 249 | limits_go_home(HOMING_CYCLE_1); // Homing cycle 1 250 | #endif 251 | #ifdef HOMING_CYCLE_2 252 | limits_go_home(HOMING_CYCLE_2); // Homing cycle 2 253 | #endif 254 | 255 | protocol_execute_runtime(); // Check for reset and set system abort. 256 | if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm. 257 | 258 | // Homing cycle complete! Setup system for normal operation. 259 | // ------------------------------------------------------------------------------------- 260 | 261 | // Gcode parser position was circumvented by the limits_go_home() routine, so sync position now. 262 | gc_sync_position(); 263 | 264 | // Set idle state after homing completes and before returning to main program. 265 | sys.state = STATE_IDLE; 266 | st_go_idle(); // Set idle state after homing completes 267 | 268 | // If hard limits feature enabled, re-enable hard limits pin change register after homing cycle. 269 | limits_init(); 270 | }*/ 271 | 272 | 273 | // Perform tool length probe cycle. Requires probe switch. 274 | // NOTE: Upon probe failure, the program will be stopped and placed into ALARM state. 275 | #ifdef USE_LINE_NUMBERS 276 | void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number) 277 | #else 278 | void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate) 279 | #endif 280 | { 281 | // TODO: Need to update this cycle so it obeys a non-auto cycle start. 282 | if (sys.state == STATE_CHECK_MODE) { return; } 283 | 284 | // Finish all queued commands and empty planner buffer before starting probe cycle. 285 | protocol_buffer_synchronize(); 286 | uint8_t auto_start_state = sys.auto_start; // Store run state 287 | 288 | // After syncing, check if probe is already triggered. If so, halt and issue alarm. 289 | if (probe_get_state()) { 290 | bit_true_atomic(sys.execute, EXEC_CRIT_EVENT); 291 | protocol_execute_runtime(); 292 | } 293 | if (sys.abort) { return; } // Return if system reset has been issued. 294 | 295 | // Setup and queue probing motion. Auto cycle-start should not start the cycle. 296 | #ifdef USE_LINE_NUMBERS 297 | mc_line(target, feed_rate, invert_feed_rate, line_number); 298 | #else 299 | mc_line(target, feed_rate, invert_feed_rate); 300 | #endif 301 | 302 | // Activate the probing monitor in the stepper module. 303 | sys.probe_state = PROBE_ACTIVE; 304 | 305 | // Perform probing cycle. Wait here until probe is triggered or motion completes. 306 | bit_true_atomic(sys.execute, EXEC_CYCLE_START); 307 | do { 308 | protocol_execute_runtime(); 309 | if (sys.abort) { return; } // Check for system abort 310 | } while ((sys.state != STATE_IDLE) && (sys.state != STATE_QUEUED)); 311 | 312 | // Probing motion complete. If the probe has not been triggered, error out. 313 | if (sys.probe_state == PROBE_ACTIVE) { bit_true_atomic(sys.execute, EXEC_CRIT_EVENT); } 314 | protocol_execute_runtime(); // Check and execute run-time commands 315 | if (sys.abort) { return; } // Check for system abort 316 | 317 | // Reset the stepper and planner buffers to remove the remainder of the probe motion. 318 | st_reset(); // Reest step segment buffer. 319 | plan_reset(); // Reset planner buffer. Zero planner positions. Ensure probing motion is cleared. 320 | plan_sync_position(); // Sync planner position to current machine position. 321 | 322 | // Pull-off triggered probe to the trigger location since we had to decelerate a little beyond 323 | // it to stop the machine in a controlled manner. 324 | uint8_t idx; 325 | for(idx=0; idx. 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #ifndef motion_control_h 28 | #define motion_control_h 29 | 30 | #define HOMING_CYCLE_LINE_NUMBER -1 31 | 32 | // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second 33 | // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in 34 | // (1 minute)/feed_rate time. 35 | #ifdef USE_LINE_NUMBERS 36 | void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number); 37 | #else 38 | void mc_line(float *target, float feed_rate, uint8_t invert_feed_rate); 39 | #endif 40 | 41 | // Execute an arc in offset mode format. position == current xyz, target == target xyz, 42 | // offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is 43 | // the direction of helical travel, radius == circle radius, isclockwise boolean. Used 44 | // for vector transformation direction. 45 | #ifdef USE_LINE_NUMBERS 46 | void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, 47 | uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, int32_t line_number); 48 | #else 49 | void mc_arc(float *position, float *target, float *offset, float radius, float feed_rate, 50 | uint8_t invert_feed_rate, uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear); 51 | #endif 52 | 53 | // Dwell for a specific number of seconds 54 | void mc_dwell(float seconds); 55 | 56 | // Perform homing cycle to locate machine zero. Requires limit switches. 57 | void mc_homing_cycle(); 58 | 59 | // Perform tool length probe cycle. Requires probe switch. 60 | #ifdef USE_LINE_NUMBERS 61 | void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number); 62 | #else 63 | void mc_probe_cycle(float *target, float feed_rate, uint8_t invert_feed_rate); 64 | #endif 65 | 66 | // Performs system reset. If in motion state, kills all motion and sets system alarm. 67 | void mc_reset(); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /nuts_bolts.c: -------------------------------------------------------------------------------- 1 | /* 2 | nuts_bolts.c - Shared functions 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #include "system.h" 28 | #include "print.h" 29 | 30 | 31 | #define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float) 32 | 33 | 34 | // Extracts a floating point value from a string. The following code is based loosely on 35 | // the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely 36 | // available conversion method examples, but has been highly optimized for Grbl. For known 37 | // CNC applications, the typical decimal value is expected to be in the range of E0 to E-4. 38 | // Scientific notation is officially not supported by g-code, and the 'E' character may 39 | // be a g-code word on some CNC systems. So, 'E' notation will not be recognized. 40 | // NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod(). 41 | uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr) 42 | { 43 | char *ptr = line + *char_counter; 44 | unsigned char c; 45 | 46 | // Grab first character and increment pointer. No spaces assumed in line. 47 | c = *ptr++; 48 | 49 | // Capture initial positive/minus character 50 | bool isnegative = false; 51 | if (c == '-') { 52 | isnegative = true; 53 | c = *ptr++; 54 | } else if (c == '+') { 55 | c = *ptr++; 56 | } 57 | 58 | // Extract number into fast integer. Track decimal in terms of exponent value. 59 | uint32_t intval = 0; 60 | int8_t exp = 0; 61 | uint8_t ndigit = 0; 62 | bool isdecimal = false; 63 | while(1) { 64 | c -= '0'; 65 | if (c <= 9) { 66 | ndigit++; 67 | if (ndigit <= MAX_INT_DIGITS) { 68 | if (isdecimal) { exp--; } 69 | intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c 70 | } else { 71 | if (!(isdecimal)) { exp++; } // Drop overflow digits 72 | } 73 | } else if (c == (('.'-'0') & 0xff) && !(isdecimal)) { 74 | isdecimal = true; 75 | } else { 76 | break; 77 | } 78 | c = *ptr++; 79 | } 80 | 81 | // Return if no digits have been read. 82 | if (!ndigit) { return(false); }; 83 | 84 | // Convert integer into floating point. 85 | float fval; 86 | fval = (float)intval; 87 | 88 | // Apply decimal. Should perform no more than two floating point multiplications for the 89 | // expected range of E0 to E-4. 90 | if (fval != 0) { 91 | while (exp <= -2) { 92 | fval *= 0.01; 93 | exp += 2; 94 | } 95 | if (exp < 0) { 96 | fval *= 0.1; 97 | } else if (exp > 0) { 98 | do { 99 | fval *= 10.0; 100 | } while (--exp > 0); 101 | } 102 | } 103 | 104 | // Assign floating point value with correct sign. 105 | if (isnegative) { 106 | *float_ptr = -fval; 107 | } else { 108 | *float_ptr = fval; 109 | } 110 | 111 | *char_counter = ptr - line - 1; // Set char_counter to next statement 112 | 113 | return(true); 114 | } 115 | 116 | 117 | // Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(), 118 | // which only accepts constants in future compiler releases. 119 | void delay_ms(uint16_t ms) 120 | { 121 | while ( ms-- ) { _delay_ms(1); } 122 | } 123 | 124 | 125 | // Delays variable defined microseconds. Compiler compatibility fix for _delay_us(), 126 | // which only accepts constants in future compiler releases. Written to perform more 127 | // efficiently with larger delays, as the counter adds parasitic time in each iteration. 128 | void delay_us(uint32_t us) 129 | { 130 | while (us) { 131 | if (us < 10) { 132 | _delay_us(1); 133 | us--; 134 | } else if (us < 100) { 135 | _delay_us(10); 136 | us -= 10; 137 | } else if (us < 1000) { 138 | _delay_us(100); 139 | us -= 100; 140 | } else { 141 | _delay_ms(1); 142 | us -= 1000; 143 | } 144 | } 145 | } 146 | 147 | 148 | // Simple hypotenuse computation function. 149 | float hypot_f(float x, float y) { return(sqrt(x*x + y*y)); } 150 | -------------------------------------------------------------------------------- /nuts_bolts.h: -------------------------------------------------------------------------------- 1 | /* 2 | nuts_bolts.h - Header file for shared definitions, variables, and functions 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #ifndef nuts_bolts_h 28 | #define nuts_bolts_h 29 | 30 | #define false 0 31 | #define true 1 32 | 33 | #define N_AXIS 3 // Number of axes 34 | #define X_AXIS 0 // Axis indexing value. Must start with 0 and be continuous. 35 | #define Y_AXIS 1 36 | #define Z_AXIS 2 37 | // #define A_AXIS 3 38 | 39 | #define MM_PER_INCH (25.40) 40 | #define INCH_PER_MM (0.0393701) 41 | 42 | #define TICKS_PER_MICROSECOND (F_CPU/1000000) 43 | 44 | // Useful macros 45 | #define clear_vector(a) memset(a, 0, sizeof(a)) 46 | #define clear_vector_float(a) memset(a, 0.0, sizeof(float)*N_AXIS) 47 | // #define clear_vector_long(a) memset(a, 0.0, sizeof(long)*N_AXIS) 48 | #define max(a,b) (((a) > (b)) ? (a) : (b)) 49 | #define min(a,b) (((a) < (b)) ? (a) : (b)) 50 | 51 | // Bit field and masking macros 52 | #define bit(n) (1 << n) 53 | #define bit_true_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) |= (mask); SREG = sreg; } 54 | #define bit_false_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) &= ~(mask); SREG = sreg; } 55 | #define bit_toggle_atomic(x,mask) {uint8_t sreg = SREG; cli(); (x) ^= (mask); SREG = sreg; } 56 | #define bit_true(x,mask) (x) |= (mask) 57 | #define bit_false(x,mask) (x) &= ~(mask) 58 | #define bit_istrue(x,mask) ((x & mask) != 0) 59 | #define bit_isfalse(x,mask) ((x & mask) == 0) 60 | 61 | // Read a floating point value from a string. Line points to the input buffer, char_counter 62 | // is the indexer pointing to the current character of the line, while float_ptr is 63 | // a pointer to the result variable. Returns true when it succeeds 64 | uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr); 65 | 66 | // Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms(). 67 | void delay_ms(uint16_t ms); 68 | 69 | // Delays variable-defined microseconds. Compiler compatibility fix for _delay_us(). 70 | void delay_us(uint32_t us); 71 | 72 | // Computes hypotenuse, avoiding avr-gcc's bloated version and the extra error checking. 73 | float hypot_f(float x, float y); 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /planner.h: -------------------------------------------------------------------------------- 1 | /* 2 | planner.h - buffers movement commands and manages the acceleration profile plan 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #ifndef planner_h 28 | #define planner_h 29 | 30 | 31 | // The number of linear motions that can be in the plan at any give time 32 | #ifndef BLOCK_BUFFER_SIZE 33 | #ifdef USE_LINE_NUMBERS 34 | #define BLOCK_BUFFER_SIZE 16 35 | #else 36 | #define BLOCK_BUFFER_SIZE 18 37 | #endif 38 | #endif 39 | 40 | // This struct stores a linear movement of a g-code block motion with its critical "nominal" values 41 | // are as specified in the source g-code. 42 | typedef struct { 43 | // Fields used by the bresenham algorithm for tracing the line 44 | // NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values. 45 | uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h) 46 | uint32_t steps[N_AXIS]; // Step count along each axis 47 | uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block. 48 | 49 | // Fields used by the motion planner to manage acceleration 50 | float entry_speed_sqr; // The current planned entry speed at block junction in (deg/min)^2 51 | float max_entry_speed_sqr; // Maximum allowable entry speed based on the minimum of junction limit and 52 | // neighboring nominal speeds with overrides in (deg/min)^2 53 | float max_junction_speed_sqr; // Junction entry speed limit based on direction vectors in (deg/min)^2 54 | float nominal_speed_sqr; // Axis-limit adjusted nominal speed for this block in (deg/min)^2 55 | float acceleration; // Axis-limit adjusted line acceleration in (deg/min^2) 56 | float degrees; // The remaining distance for this block to be executed in (deg) 57 | // uint8_t max_override; // Maximum override value based on axis speed limits 58 | 59 | #ifdef USE_LINE_NUMBERS 60 | int32_t line_number; 61 | #endif 62 | } plan_block_t; 63 | 64 | 65 | // Initialize and reset the motion plan subsystem 66 | void plan_reset(); 67 | 68 | // Add a new linear movement to the buffer. target[N_AXIS] is the signed, absolute target position 69 | // in millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed 70 | // rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. 71 | #ifdef USE_LINE_NUMBERS 72 | void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate, int32_t line_number); 73 | #else 74 | void plan_buffer_line(float *target, float feed_rate, uint8_t invert_feed_rate); 75 | #endif 76 | 77 | // Called when the current block is no longer needed. Discards the block and makes the memory 78 | // availible for new blocks. 79 | void plan_discard_current_block(); 80 | 81 | // Gets the current block. Returns NULL if buffer empty 82 | plan_block_t *plan_get_current_block(); 83 | 84 | // Called periodically by step segment buffer. Mostly used internally by planner. 85 | uint8_t plan_next_block_index(uint8_t block_index); 86 | 87 | // Called by step segment buffer when computing executing block velocity profile. 88 | float plan_get_exec_block_exit_speed(); 89 | 90 | // Reset the planner position vector (in steps) 91 | void plan_sync_position(); 92 | 93 | // Reinitialize plan with a partially completed block 94 | void plan_cycle_reinitialize(); 95 | 96 | // Returns the number of active blocks are in the planner buffer. 97 | uint8_t plan_get_block_buffer_count(); 98 | 99 | // Returns the status of the block ring buffer. True, if buffer is full. 100 | uint8_t plan_check_full_buffer(); 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /print.c: -------------------------------------------------------------------------------- 1 | /* 2 | print.c - Functions for formatting output strings 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #include "system.h" 28 | #include "serial.h" 29 | #include "settings.h" 30 | 31 | 32 | void printString(const char *s) 33 | { 34 | while (*s) 35 | serial_write(*s++); 36 | } 37 | 38 | 39 | // Print a string stored in PGM-memory 40 | void printPgmString(const char *s) 41 | { 42 | char c; 43 | while ((c = pgm_read_byte_near(s++))) 44 | serial_write(c); 45 | } 46 | 47 | 48 | // void printIntegerInBase(unsigned long n, unsigned long base) 49 | // { 50 | // unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars. 51 | // unsigned long i = 0; 52 | // 53 | // if (n == 0) { 54 | // serial_write('0'); 55 | // return; 56 | // } 57 | // 58 | // while (n > 0) { 59 | // buf[i++] = n % base; 60 | // n /= base; 61 | // } 62 | // 63 | // for (; i > 0; i--) 64 | // serial_write(buf[i - 1] < 10 ? 65 | // '0' + buf[i - 1] : 66 | // 'A' + buf[i - 1] - 10); 67 | // } 68 | 69 | 70 | void print_uint8_base2(uint8_t n) 71 | { 72 | unsigned char buf[8]; 73 | uint8_t i = 0; 74 | 75 | for (; i < 8; i++) { 76 | buf[i] = n & 1; 77 | n >>= 1; 78 | } 79 | 80 | for (; i > 0; i--) 81 | serial_write('0' + buf[i - 1]); 82 | } 83 | 84 | 85 | void print_uint8_base10(uint8_t n) 86 | { 87 | if (n == 0) { 88 | serial_write('0'); 89 | return; 90 | } 91 | 92 | unsigned char buf[3]; 93 | uint8_t i = 0; 94 | 95 | while (n > 0) { 96 | buf[i++] = n % 10 + '0'; 97 | n /= 10; 98 | } 99 | 100 | for (; i > 0; i--) 101 | serial_write(buf[i - 1]); 102 | } 103 | 104 | 105 | void print_uint32_base10(unsigned long n) 106 | { 107 | if (n == 0) { 108 | serial_write('0'); 109 | return; 110 | } 111 | 112 | unsigned char buf[10]; 113 | uint8_t i = 0; 114 | 115 | while (n > 0) { 116 | buf[i++] = n % 10; 117 | n /= 10; 118 | } 119 | 120 | for (; i > 0; i--) 121 | serial_write('0' + buf[i-1]); 122 | } 123 | 124 | 125 | void printInteger(long n) 126 | { 127 | if (n < 0) { 128 | serial_write('-'); 129 | print_uint32_base10((-n)); 130 | } else { 131 | print_uint32_base10(n); 132 | } 133 | } 134 | 135 | 136 | // Convert float to string by immediately converting to a long integer, which contains 137 | // more digits than a float. Number of decimal places, which are tracked by a counter, 138 | // may be set by the user. The integer is then efficiently converted to a string. 139 | // NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up 140 | // techniques are actually just slightly slower. Found this out the hard way. 141 | void printFloat(float n, uint8_t decimal_places) 142 | { 143 | if (n < 0) { 144 | serial_write('-'); 145 | n = -n; 146 | } 147 | 148 | uint8_t decimals = decimal_places; 149 | while (decimals >= 2) { // Quickly convert values expected to be E0 to E-4. 150 | n *= 100; 151 | decimals -= 2; 152 | } 153 | if (decimals) { n *= 10; } 154 | n += 0.5; // Add rounding factor. Ensures carryover through entire value. 155 | 156 | // Generate digits backwards and store in string. 157 | unsigned char buf[10]; 158 | uint8_t i = 0; 159 | uint32_t a = (long)n; 160 | buf[decimal_places] = '.'; // Place decimal point, even if decimal places are zero. 161 | while(a > 0) { 162 | if (i == decimal_places) { i++; } // Skip decimal point location 163 | buf[i++] = (a % 10) + '0'; // Get digit 164 | a /= 10; 165 | } 166 | while (i < decimal_places) { 167 | buf[i++] = '0'; // Fill in zeros to decimal point for (n < 1) 168 | } 169 | if (i == decimal_places) { // Fill in leading zero, if needed. 170 | i++; 171 | buf[i++] = '0'; 172 | } 173 | 174 | // Print the generated string. 175 | for (; i > 0; i--) 176 | serial_write(buf[i-1]); 177 | } 178 | 179 | 180 | // Floating value printing handlers for special variables types used in Grbl and are defined 181 | // in the config.h. 182 | // - CoordValue: Handles all position or coordinate values in inches or mm reporting. 183 | // - RateValue: Handles feed rate and current velocity in inches or mm reporting. 184 | // - SettingValue: Handles all floating point settings values (always in mm.) 185 | void printFloat_CoordValue(float n) { 186 | if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { 187 | printFloat(n*INCH_PER_MM,N_DECIMAL_COORDVALUE_INCH); 188 | } else { 189 | printFloat(n,N_DECIMAL_COORDVALUE_MM); 190 | } 191 | } 192 | 193 | void printFloat_RateValue(float n) { 194 | if (bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)) { 195 | printFloat(n*INCH_PER_MM,N_DECIMAL_RATEVALUE_INCH); 196 | } else { 197 | printFloat(n,N_DECIMAL_RATEVALUE_MM); 198 | } 199 | } 200 | 201 | void printFloat_SettingValue(float n) { printFloat(n,N_DECIMAL_SETTINGVALUE); } 202 | 203 | 204 | // Debug tool to print free memory in bytes at the called point. Not used otherwise. 205 | void printFreeMemory() 206 | { 207 | extern int __heap_start, *__brkval; 208 | uint16_t free; // Up to 64k values. 209 | free = (int) &free - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 210 | printInteger((int32_t)free); 211 | printString(" "); 212 | } 213 | -------------------------------------------------------------------------------- /print.h: -------------------------------------------------------------------------------- 1 | /* 2 | print.h - Functions for formatting output strings 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #ifndef print_h 28 | #define print_h 29 | 30 | 31 | void printString(const char *s); 32 | 33 | void printPgmString(const char *s); 34 | 35 | void printInteger(long n); 36 | 37 | void print_uint32_base10(uint32_t n); 38 | 39 | void print_uint8_base2(uint8_t n); 40 | 41 | void print_uint8_base10(uint8_t n); 42 | 43 | void printFloat(float n, uint8_t decimal_places); 44 | 45 | // Floating value printing handlers for special variables types used in Grbl. 46 | // - CoordValue: Handles all position or coordinate values in inches or mm reporting. 47 | // - RateValue: Handles feed rate and current velocity in inches or mm reporting. 48 | // - SettingValue: Handles all floating point settings values (always in mm.) 49 | void printFloat_CoordValue(float n); 50 | 51 | void printFloat_RateValue(float n); 52 | 53 | void printFloat_SettingValue(float n); 54 | 55 | // Debug tool to print free memory in bytes at the called point. Not used otherwise. 56 | void printFreeMemory(); 57 | 58 | #endif -------------------------------------------------------------------------------- /probe.c: -------------------------------------------------------------------------------- 1 | /* 2 | probe.c - code pertaining to probing methods 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2014 Sungeun K. Jeon 24 | */ 25 | 26 | #include "system.h" 27 | #include "settings.h" 28 | #include "probe.h" 29 | 30 | // Inverts the probe pin state depending on user settings. 31 | uint8_t probe_invert_mask; 32 | 33 | 34 | // Probe pin initialization routine. 35 | void probe_init() 36 | { 37 | PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins 38 | if (bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN)) { 39 | PROBE_PORT &= ~(PROBE_MASK); // Normal low operation. Requires external pull-down. 40 | probe_invert_mask = 0; 41 | } else { 42 | PROBE_PORT |= PROBE_MASK; // Enable internal pull-up resistors. Normal high operation. 43 | probe_invert_mask = PROBE_MASK; 44 | } 45 | } 46 | 47 | 48 | // Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor. 49 | uint8_t probe_get_state() { return((PROBE_PIN & PROBE_MASK) ^ probe_invert_mask); } 50 | 51 | 52 | // Monitors probe pin state and records the system position when detected. Called by the 53 | // stepper ISR per ISR tick. 54 | // NOTE: This function must be extremely efficient as to not bog down the stepper ISR. 55 | void probe_state_monitor() 56 | { 57 | if (sys.probe_state == PROBE_ACTIVE) { 58 | if (probe_get_state()) { 59 | sys.probe_state = PROBE_OFF; 60 | memcpy(sys.probe_position, sys.position, sizeof(float)*N_AXIS); 61 | bit_true(sys.execute, EXEC_FEED_HOLD); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /probe.h: -------------------------------------------------------------------------------- 1 | /* 2 | probe.h - code pertaining to probing methods 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2014 Sungeun K. Jeon 24 | */ 25 | 26 | #ifndef probe_h 27 | #define probe_h 28 | 29 | // Values that define the probing state machine. 30 | #define PROBE_OFF 0 // No probing. (Must be zero.) 31 | #define PROBE_ACTIVE 1 // Actively watching the input pin. 32 | 33 | 34 | // Probe pin initialization routine. 35 | void probe_init(); 36 | 37 | // Returns probe pin state. 38 | uint8_t probe_get_state(); 39 | 40 | // Monitors probe pin state and records the system position when detected. Called by the 41 | // stepper ISR per ISR tick. 42 | void probe_state_monitor(); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /protocol.c: -------------------------------------------------------------------------------- 1 | /* 2 | protocol.c - controls Grbl execution protocol and procedures 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #include "system.h" 28 | #include "serial.h" 29 | #include "settings.h" 30 | #include "protocol.h" 31 | #include "gcode.h" 32 | #include "planner.h" 33 | #include "stepper.h" 34 | #include "motion_control.h" 35 | #include "report.h" 36 | 37 | 38 | static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. 39 | 40 | 41 | // Directs and executes one line of formatted input from protocol_process. While mostly 42 | // incoming streaming g-code blocks, this also directs and executes Grbl internal commands, 43 | // such as settings, initiating the homing cycle, and toggling switch states. 44 | static void protocol_execute_line(char *line) 45 | { 46 | protocol_execute_runtime(); // Runtime command check point. 47 | if (sys.abort) { return; } // Bail to calling function upon system abort 48 | 49 | if (line[0] == 0) { 50 | // Empty or comment line. Send status message for syncing purposes. 51 | //report_status_message(STATUS_OK); 52 | report_status_message(STATUS_NONE); 53 | 54 | } else if (line[0] == '$') { 55 | // Grbl '$' system command 56 | report_status_message(system_execute_line(line)); 57 | 58 | } else if (sys.state == STATE_ALARM) { 59 | // Everything else is gcode. Block if in alarm mode. 60 | report_status_message(STATUS_ALARM_LOCK); 61 | 62 | } else { 63 | // Parse and execute g-code block! 64 | report_status_message(gc_execute_line(line)); 65 | } 66 | } 67 | 68 | 69 | /* 70 | GRBL PRIMARY LOOP: 71 | */ 72 | void protocol_main_loop() 73 | { 74 | // ------------------------------------------------------------ 75 | // Complete initialization procedures upon a power-up or reset. 76 | // ------------------------------------------------------------ 77 | 78 | // Print welcome message 79 | report_init_message(); 80 | 81 | // Check for and report alarm state after a reset, error, or an initial power up. 82 | if (sys.state == STATE_ALARM) { 83 | report_feedback_message(MESSAGE_ALARM_LOCK); 84 | } else { 85 | // All systems go! 86 | sys.state = STATE_IDLE; // Set system to ready. Clear all state flags. 87 | system_execute_startup(line); // Execute startup script. 88 | } 89 | 90 | // --------------------------------------------------------------------------------- 91 | // Primary loop! Upon a system abort, this exits back to main() to reset the system. 92 | // --------------------------------------------------------------------------------- 93 | 94 | uint8_t iscomment = false; 95 | uint8_t char_counter = 0; 96 | uint8_t c; 97 | for (;;) { 98 | 99 | // Process one line of incoming serial data, as the data becomes available. Performs an 100 | // initial filtering by removing spaces and comments and capitalizing all letters. 101 | 102 | // NOTE: While comment, spaces, and block delete(if supported) handling should technically 103 | // be done in the g-code parser, doing it here helps compress the incoming data into Grbl's 104 | // line buffer, which is limited in size. The g-code standard actually states a line can't 105 | // exceed 256 characters, but the Arduino Uno does not have the memory space for this. 106 | // With a better processor, it would be very easy to pull this initial parsing out as a 107 | // seperate task to be shared by the g-code parser and Grbl's system commands. 108 | 109 | while((c = serial_read()) != SERIAL_NO_DATA) { 110 | if ((c == '\n') || (c == '\r')) { // End of line reached 111 | line[char_counter] = 0; // Set string termination character. 112 | protocol_execute_line(line); // Line is complete. Execute it! 113 | iscomment = false; 114 | char_counter = 0; 115 | } else { 116 | if (iscomment) { 117 | // Throw away all comment characters 118 | if (c == ')') { 119 | // End of comment. Resume line. 120 | iscomment = false; 121 | } 122 | } else { 123 | if (c <= ' ') { 124 | // Throw away whitepace and control characters 125 | } else if (c == '/') { 126 | // Block delete NOT SUPPORTED. Ignore character. 127 | // NOTE: If supported, would simply need to check the system if block delete is enabled. 128 | } else if (c == '(') { 129 | // Enable comments flag and ignore all characters until ')' or EOL. 130 | // NOTE: This doesn't follow the NIST definition exactly, but is good enough for now. 131 | // In the future, we could simply remove the items within the comments, but retain the 132 | // comment control characters, so that the g-code parser can error-check it. 133 | iscomment = true; 134 | // } else if (c == ';') { 135 | // Comment character to EOL NOT SUPPORTED. LinuxCNC definition. Not NIST. 136 | 137 | // TODO: Install '%' feature 138 | // } else if (c == '%') { 139 | // Program start-end percent sign NOT SUPPORTED. 140 | // NOTE: This maybe installed to tell Grbl when a program is running vs manual input, 141 | // where, during a program, the system auto-cycle start will continue to execute 142 | // everything until the next '%' sign. This will help fix resuming issues with certain 143 | // functions that empty the planner buffer to execute its task on-time. 144 | 145 | } else if (char_counter >= (LINE_BUFFER_SIZE-1)) { 146 | // Detect line buffer overflow. Report error and reset line buffer. 147 | report_status_message(STATUS_OVERFLOW); 148 | iscomment = false; 149 | char_counter = 0; 150 | } else if (c >= 'a' && c <= 'z') { // Upcase lowercase 151 | line[char_counter++] = c-'a'+'A'; 152 | } else { 153 | line[char_counter++] = c; 154 | } 155 | } 156 | } 157 | } 158 | 159 | // If there are no more characters in the serial read buffer to be processed and executed, 160 | // this indicates that g-code streaming has either filled the planner buffer or has 161 | // completed. In either case, auto-cycle start, if enabled, any queued moves. 162 | protocol_auto_cycle_start(); 163 | 164 | protocol_execute_runtime(); // Runtime command check point. 165 | if (sys.abort) { return; } // Bail to main() program loop to reset system. 166 | 167 | } 168 | 169 | return; /* Never reached */ 170 | } 171 | 172 | 173 | // Executes run-time commands, when required. This is called from various check points in the main 174 | // program, primarily where there may be a while loop waiting for a buffer to clear space or any 175 | // point where the execution time from the last check point may be more than a fraction of a second. 176 | // This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code 177 | // parsing and planning functions. This function also serves as an interface for the interrupts to 178 | // set the system runtime flags, where only the main program handles them, removing the need to 179 | // define more computationally-expensive volatile variables. This also provides a controlled way to 180 | // execute certain tasks without having two or more instances of the same task, such as the planner 181 | // recalculating the buffer upon a feedhold or override. 182 | // NOTE: The sys.execute variable flags are set by any process, step or serial interrupts, pinouts, 183 | // limit switches, or the main program. 184 | void protocol_execute_runtime() 185 | { 186 | uint8_t rt_exec = sys.execute; // Copy to avoid calling volatile multiple times 187 | if (rt_exec) { // Enter only if any bit flag is true 188 | 189 | // System alarm. Everything has shutdown by something that has gone severely wrong. Report 190 | // the source of the error to the user. If critical, Grbl disables by entering an infinite 191 | // loop until system reset/abort. 192 | if (rt_exec & (EXEC_ALARM | EXEC_CRIT_EVENT)) { 193 | sys.state = STATE_ALARM; // Set system alarm state 194 | 195 | // Critical events. Hard/soft limit events identified by both critical event and alarm exec 196 | // flags. Probe fail is identified by the critical event exec flag only. 197 | if (rt_exec & EXEC_CRIT_EVENT) { 198 | if (rt_exec & EXEC_ALARM) { report_alarm_message(ALARM_LIMIT_ERROR); } 199 | else { report_alarm_message(ALARM_PROBE_FAIL); } 200 | report_feedback_message(MESSAGE_CRITICAL_EVENT); 201 | bit_false_atomic(sys.execute,EXEC_RESET); // Disable any existing reset 202 | do { 203 | // Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits 204 | // typically occur while unattended or not paying attention. Gives the user time 205 | // to do what is needed before resetting, like killing the incoming stream. The 206 | // same could be said about soft limits. While the position is not lost, the incoming 207 | // stream could be still engaged and cause a serious crash if it continues afterwards. 208 | } while (bit_isfalse(sys.execute,EXEC_RESET)); 209 | 210 | // Standard alarm event. Only abort during motion qualifies. 211 | } else { 212 | // Runtime abort command issued during a cycle, feed hold, or homing cycle. Message the 213 | // user that position may have been lost and set alarm state to enable the alarm lockout 214 | // to indicate the possible severity of the problem. 215 | report_alarm_message(ALARM_ABORT_CYCLE); 216 | } 217 | bit_false_atomic(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT)); 218 | } 219 | 220 | // Execute system abort. 221 | if (rt_exec & EXEC_RESET) { 222 | sys.abort = true; // Only place this is set true. 223 | return; // Nothing else to do but exit. 224 | } 225 | 226 | // Execute and serial print status 227 | if (rt_exec & EXEC_STATUS_REPORT) { 228 | report_realtime_status(); 229 | bit_false_atomic(sys.execute,EXEC_STATUS_REPORT); 230 | } 231 | 232 | // Execute a feed hold with deceleration, only during cycle. 233 | if (rt_exec & EXEC_FEED_HOLD) { 234 | // !!! During a cycle, the segment buffer has just been reloaded and full. So the math involved 235 | // with the feed hold should be fine for most, if not all, operational scenarios. 236 | if (sys.state == STATE_CYCLE) { 237 | sys.state = STATE_HOLD; 238 | st_update_plan_block_parameters(); 239 | st_prep_buffer(); 240 | sys.auto_start = false; // Disable planner auto start upon feed hold. 241 | } 242 | bit_false_atomic(sys.execute,EXEC_FEED_HOLD); 243 | } 244 | 245 | // Execute a cycle start by starting the stepper interrupt begin executing the blocks in queue. 246 | if (rt_exec & EXEC_CYCLE_START) { 247 | if (sys.state == STATE_QUEUED) { 248 | sys.state = STATE_CYCLE; 249 | st_prep_buffer(); // Initialize step segment buffer before beginning cycle. 250 | st_wake_up(); 251 | if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) { 252 | sys.auto_start = true; // Re-enable auto start after feed hold. 253 | } else { 254 | sys.auto_start = false; // Reset auto start per settings. 255 | } 256 | } 257 | bit_false_atomic(sys.execute,EXEC_CYCLE_START); 258 | } 259 | 260 | // Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by 261 | // runtime command execution in the main program, ensuring that the planner re-plans safely. 262 | // NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper 263 | // cycle reinitializations. The stepper path should continue exactly as if nothing has happened. 264 | // NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes. 265 | if (rt_exec & EXEC_CYCLE_STOP) { 266 | if ( plan_get_current_block() ) { sys.state = STATE_QUEUED; } 267 | else { sys.state = STATE_IDLE; } 268 | bit_false_atomic(sys.execute,EXEC_CYCLE_STOP); 269 | } 270 | 271 | } 272 | 273 | // Overrides flag byte (sys.override) and execution should be installed here, since they 274 | // are runtime and require a direct and controlled interface to the main stepper program. 275 | 276 | // Reload step segment buffer 277 | if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_HOMING)) { st_prep_buffer(); } 278 | 279 | } 280 | 281 | 282 | // Block until all buffered steps are executed or in a cycle state. Works with feed hold 283 | // during a synchronize call, if it should happen. Also, waits for clean cycle end. 284 | void protocol_buffer_synchronize() 285 | { 286 | // If system is queued, ensure cycle resumes if the auto start flag is present. 287 | protocol_auto_cycle_start(); 288 | // Check and set auto start to resume cycle after synchronize and caller completes. 289 | if (sys.state == STATE_CYCLE) { sys.auto_start = true; } 290 | while (plan_get_current_block() || (sys.state == STATE_CYCLE)) { 291 | protocol_execute_runtime(); // Check and execute run-time commands 292 | if (sys.abort) { return; } // Check for system abort 293 | } 294 | } 295 | 296 | 297 | // Auto-cycle start has two purposes: 1. Resumes a plan_synchronize() call from a function that 298 | // requires the planner buffer to empty (spindle enable, dwell, etc.) 2. As a user setting that 299 | // automatically begins the cycle when a user enters a valid motion command manually. This is 300 | // intended as a beginners feature to help new users to understand g-code. It can be disabled 301 | // as a beginner tool, but (1.) still operates. If disabled, the operation of cycle start is 302 | // manually issuing a cycle start command whenever the user is ready and there is a valid motion 303 | // command in the planner queue. 304 | // NOTE: This function is called from the main loop and mc_line() only and executes when one of 305 | // two conditions exist respectively: There are no more blocks sent (i.e. streaming is finished, 306 | // single commands), or the planner buffer is full and ready to go. 307 | void protocol_auto_cycle_start() { if (sys.auto_start) { bit_true_atomic(sys.execute, EXEC_CYCLE_START); } } 308 | -------------------------------------------------------------------------------- /protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | protocol.h - controls Grbl execution protocol and procedures 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #ifndef protocol_h 28 | #define protocol_h 29 | 30 | // Line buffer size from the serial input stream to be executed. 31 | // NOTE: Not a problem except for extreme cases, but the line buffer size can be too small 32 | // and g-code blocks can get truncated. Officially, the g-code standards support up to 256 33 | // characters. In future versions, this will be increased, when we know how much extra 34 | // memory space we can invest into here or we re-write the g-code parser not to have this 35 | // buffer. 36 | #ifndef LINE_BUFFER_SIZE 37 | #define LINE_BUFFER_SIZE 80 38 | #endif 39 | 40 | // Starts Grbl main loop. It handles all incoming characters from the serial port and executes 41 | // them as they complete. It is also responsible for finishing the initialization procedures. 42 | void protocol_main_loop(); 43 | 44 | // Checks and executes a runtime command at various stop points in main program 45 | void protocol_execute_runtime(); 46 | 47 | // Notify the stepper subsystem to start executing the g-code program in buffer. 48 | // void protocol_cycle_start(); 49 | 50 | // Reinitializes the buffer after a feed hold for a resume. 51 | // void protocol_cycle_reinitialize(); 52 | 53 | // Initiates a feed hold of the running program 54 | // void protocol_feed_hold(); 55 | 56 | // Executes the auto cycle feature, if enabled. 57 | void protocol_auto_cycle_start(); 58 | 59 | // Block until all buffered steps are executed 60 | void protocol_buffer_synchronize(); 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /report.c: -------------------------------------------------------------------------------- 1 | /* 2 | report.c - reporting and messaging methods 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2012-2014 Sungeun K. Jeon 24 | */ 25 | 26 | /* 27 | This file functions as the primary feedback interface for Grbl. Any outgoing data, such 28 | as the protocol status messages, feedback messages, and status reports, are stored here. 29 | For the most part, these functions primarily are called from protocol.c methods. If a 30 | different style feedback is desired (i.e. JSON), then a user can change these following 31 | methods to accomodate their needs. 32 | */ 33 | 34 | #include "system.h" 35 | #include "report.h" 36 | #include "print.h" 37 | #include "settings.h" 38 | #include "gcode.h" 39 | #include "planner.h" 40 | #include "stepper.h" 41 | #include "serial.h" 42 | 43 | 44 | // Handles the primary confirmation protocol response for streaming interfaces and human-feedback. 45 | // For every incoming line, this method responds with an 'ok' for a successful command or an 46 | // 'error:' to indicate some error event with the line or some critical system error during 47 | // operation. Errors events can originate from the g-code parser, settings module, or asynchronously 48 | // from a critical error, such as a triggered hard limit. Interface should always monitor for these 49 | // responses. 50 | // NOTE: In silent mode, all error codes are greater than zero. 51 | // TODO: Install silent mode to return only numeric values, primarily for GUIs. 52 | void report_status_message(uint8_t status_code) 53 | { 54 | if (status_code != STATUS_NONE) { 55 | if (status_code == STATUS_OK) { 56 | printPgmString(PSTR("ok\r\n")); 57 | } else { 58 | printPgmString(PSTR("error: ")); 59 | switch(status_code) { 60 | case STATUS_EXPECTED_COMMAND_LETTER: 61 | printPgmString(PSTR("Expected command letter")); break; 62 | case STATUS_BAD_NUMBER_FORMAT: 63 | printPgmString(PSTR("Bad number format")); break; 64 | case STATUS_INVALID_STATEMENT: 65 | printPgmString(PSTR("Invalid statement")); break; 66 | case STATUS_NEGATIVE_VALUE: 67 | printPgmString(PSTR("Value < 0")); break; 68 | case STATUS_SETTING_DISABLED: 69 | printPgmString(PSTR("Setting disabled")); break; 70 | case STATUS_SETTING_STEP_PULSE_MIN: 71 | printPgmString(PSTR("Value < 3 usec")); break; 72 | case STATUS_SETTING_READ_FAIL: 73 | printPgmString(PSTR("EEPROM read fail. Using defaults")); break; 74 | case STATUS_IDLE_ERROR: 75 | printPgmString(PSTR("Not idle")); break; 76 | case STATUS_ALARM_LOCK: 77 | printPgmString(PSTR("Alarm lock")); break; 78 | case STATUS_SOFT_LIMIT_ERROR: 79 | printPgmString(PSTR("Homing not enabled")); break; 80 | case STATUS_OVERFLOW: 81 | printPgmString(PSTR("Line overflow")); break; 82 | 83 | // Common g-code parser errors. 84 | case STATUS_GCODE_MODAL_GROUP_VIOLATION: 85 | printPgmString(PSTR("Modal group violation")); break; 86 | case STATUS_GCODE_UNSUPPORTED_COMMAND: 87 | printPgmString(PSTR("Unsupported command")); break; 88 | case STATUS_GCODE_UNDEFINED_FEED_RATE: 89 | printPgmString(PSTR("Undefined feed rate")); break; 90 | default: 91 | // Remaining g-code parser errors with error codes 92 | printPgmString(PSTR("Invalid gcode ID:")); 93 | print_uint8_base10(status_code); // Print error code for user reference 94 | } 95 | printPgmString(PSTR("\r\n")); 96 | } 97 | } 98 | } 99 | 100 | // Prints alarm messages. 101 | void report_alarm_message(int8_t alarm_code) 102 | { 103 | printPgmString(PSTR("ALARM: ")); 104 | switch (alarm_code) { 105 | case ALARM_LIMIT_ERROR: 106 | printPgmString(PSTR("Hard/soft limit")); break; 107 | case ALARM_ABORT_CYCLE: 108 | printPgmString(PSTR("Abort during cycle")); break; 109 | case ALARM_PROBE_FAIL: 110 | printPgmString(PSTR("Probe fail")); break; 111 | } 112 | printPgmString(PSTR("\r\n")); 113 | delay_ms(500); // Force delay to ensure message clears serial write buffer. 114 | } 115 | 116 | // Prints feedback messages. This serves as a centralized method to provide additional 117 | // user feedback for things that are not of the status/alarm message protocol. These are 118 | // messages such as setup warnings, switch toggling, and how to exit alarms. 119 | // NOTE: For interfaces, messages are always placed within brackets. And if silent mode 120 | // is installed, the message number codes are less than zero. 121 | // TODO: Install silence feedback messages option in settings 122 | void report_feedback_message(uint8_t message_code) 123 | { 124 | printPgmString(PSTR("[")); 125 | switch(message_code) { 126 | case MESSAGE_CRITICAL_EVENT: 127 | printPgmString(PSTR("Reset to continue")); break; 128 | case MESSAGE_ALARM_LOCK: 129 | printPgmString(PSTR("'$H'|'$X' to unlock")); break; 130 | case MESSAGE_ALARM_UNLOCK: 131 | printPgmString(PSTR("Caution: Unlocked")); break; 132 | case MESSAGE_ENABLED: 133 | printPgmString(PSTR("Enabled")); break; 134 | case MESSAGE_DISABLED: 135 | printPgmString(PSTR("Disabled")); break; 136 | } 137 | printPgmString(PSTR("]\r\n")); 138 | } 139 | 140 | 141 | // Welcome message 142 | void report_init_message() 143 | { 144 | printPgmString(PSTR("\r\nHorus " HORUS_VERSION " ['$' for help]\r\n")); 145 | } 146 | 147 | // Grbl help message 148 | void report_grbl_help() { 149 | printPgmString(PSTR("$$ (view settings)\r\n" 150 | "$# (view # parameters)\r\n" 151 | "$G (view parser state)\r\n" 152 | "$I (view build info)\r\n" 153 | "$N (view startup blocks)\r\n" 154 | "$x=value (save setting)\r\n" 155 | "$Nx=line (save startup block)\r\n" 156 | "$C (check gcode mode)\r\n" 157 | "$X (kill alarm lock)\r\n" 158 | "~ (cycle start)\r\n" 159 | "! (feed hold)\r\n" 160 | "? (current status)\r\n" 161 | "ctrl-x (reset)\r\n")); 162 | } 163 | 164 | 165 | // Grbl global settings print out. 166 | // NOTE: The numbering scheme here must correlate to storing in settings.c 167 | void report_grbl_settings() { 168 | // Print Grbl settings. 169 | printPgmString(PSTR("$0=")); print_uint8_base10(settings.pulse_microseconds); 170 | printPgmString(PSTR(" (step pulse, usec)\r\n$1=")); print_uint8_base10(settings.stepper_idle_lock_time); 171 | printPgmString(PSTR(" (step idle delay, msec)\r\n$2=")); print_uint8_base10(settings.step_invert_mask); 172 | printPgmString(PSTR(" (step port invert mask:")); print_uint8_base2(settings.step_invert_mask); 173 | printPgmString(PSTR(")\r\n$3=")); print_uint8_base10(settings.dir_invert_mask); 174 | printPgmString(PSTR(" (dir port invert mask:")); print_uint8_base2(settings.dir_invert_mask); 175 | printPgmString(PSTR(")\r\n$4=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)); 176 | printPgmString(PSTR(" (step enable invert, bool)\r\n$5=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_LIMIT_PINS)); 177 | printPgmString(PSTR(" (limit pins invert, bool)\r\n$6=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_INVERT_PROBE_PIN)); 178 | printPgmString(PSTR(" (probe pin invert, bool)\r\n$10=")); print_uint8_base10(settings.status_report_mask); 179 | printPgmString(PSTR(" (status report mask:")); print_uint8_base2(settings.status_report_mask); 180 | printPgmString(PSTR(")\r\n$11=")); printFloat_SettingValue(settings.junction_deviation); 181 | printPgmString(PSTR(" (junction deviation, deg)\r\n$12=")); printFloat_SettingValue(settings.arc_tolerance); 182 | printPgmString(PSTR(" (arc tolerance, deg)\r\n$13=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_REPORT_INCHES)); 183 | printPgmString(PSTR(" (report inches, bool)\r\n$14=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_AUTO_START)); 184 | printPgmString(PSTR(" (auto start, bool)\r\n$20=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_SOFT_LIMIT_ENABLE)); 185 | printPgmString(PSTR(" (soft limits, bool)\r\n$21=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)); 186 | printPgmString(PSTR(" (hard limits, bool)\r\n$22=")); print_uint8_base10(bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)); 187 | /*printPgmString(PSTR(" (homing cycle, bool)\r\n$23=")); print_uint8_base10(settings.homing_dir_mask); 188 | printPgmString(PSTR(" (homing dir invert mask:")); print_uint8_base2(settings.homing_dir_mask); 189 | printPgmString(PSTR(")\r\n$24=")); printFloat_SettingValue(settings.homing_feed_rate); 190 | printPgmString(PSTR(" (homing feed, mm/min)\r\n$25=")); printFloat_SettingValue(settings.homing_seek_rate); 191 | printPgmString(PSTR(" (homing seek, mm/min)\r\n$26=")); print_uint8_base10(settings.homing_debounce_delay); 192 | printPgmString(PSTR(" (homing debounce, msec)\r\n$27=")); printFloat_SettingValue(settings.homing_pulloff);*/ 193 | printPgmString(PSTR(" (homing pull-off, mm)\r\n")); 194 | 195 | // Print axis settings 196 | uint8_t idx, set_idx; 197 | uint8_t val = AXIS_SETTINGS_START_VAL; 198 | for (set_idx=0; set_idxline_number; 436 | } 437 | printInteger(ln); 438 | #endif 439 | 440 | #ifdef REPORT_REALTIME_RATE 441 | // Report realtime rate 442 | printPgmString(PSTR(",F:")); 443 | printFloat_RateValue(st_get_realtime_rate()); 444 | #endif 445 | 446 | printPgmString(PSTR(">\r\n")); 447 | } 448 | -------------------------------------------------------------------------------- /report.h: -------------------------------------------------------------------------------- 1 | /* 2 | report.h - reporting and messaging methods 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2012-2014 Sungeun K. Jeon 24 | */ 25 | 26 | #ifndef report_h 27 | #define report_h 28 | 29 | // Define Grbl status codes. 30 | #define STATUS_OK 0 31 | #define STATUS_EXPECTED_COMMAND_LETTER 1 32 | #define STATUS_BAD_NUMBER_FORMAT 2 33 | #define STATUS_INVALID_STATEMENT 3 34 | #define STATUS_NEGATIVE_VALUE 4 35 | #define STATUS_SETTING_DISABLED 5 36 | #define STATUS_SETTING_STEP_PULSE_MIN 6 37 | #define STATUS_SETTING_READ_FAIL 7 38 | #define STATUS_IDLE_ERROR 8 39 | #define STATUS_ALARM_LOCK 9 40 | #define STATUS_SOFT_LIMIT_ERROR 10 41 | #define STATUS_OVERFLOW 11 42 | #define STATUS_NONE 12 43 | 44 | #define STATUS_GCODE_UNSUPPORTED_COMMAND 20 45 | #define STATUS_GCODE_MODAL_GROUP_VIOLATION 21 46 | #define STATUS_GCODE_UNDEFINED_FEED_RATE 22 47 | #define STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER 23 48 | #define STATUS_GCODE_AXIS_COMMAND_CONFLICT 24 49 | #define STATUS_GCODE_WORD_REPEATED 25 50 | #define STATUS_GCODE_NO_AXIS_WORDS 26 51 | #define STATUS_GCODE_INVALID_LINE_NUMBER 27 52 | #define STATUS_GCODE_VALUE_WORD_MISSING 28 53 | #define STATUS_GCODE_UNSUPPORTED_COORD_SYS 29 54 | #define STATUS_GCODE_G53_INVALID_MOTION_MODE 30 55 | #define STATUS_GCODE_AXIS_WORDS_EXIST 31 56 | #define STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE 32 57 | #define STATUS_GCODE_INVALID_TARGET 33 58 | #define STATUS_GCODE_ARC_RADIUS_ERROR 34 59 | #define STATUS_GCODE_NO_OFFSETS_IN_PLANE 35 60 | #define STATUS_GCODE_UNUSED_WORDS 36 61 | #define STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR 37 62 | 63 | // Define Grbl alarm codes. Less than zero to distinguish alarm error from status error. 64 | #define ALARM_LIMIT_ERROR -1 65 | #define ALARM_ABORT_CYCLE -2 66 | #define ALARM_PROBE_FAIL -3 67 | 68 | // Define Grbl feedback message codes. 69 | #define MESSAGE_CRITICAL_EVENT 1 70 | #define MESSAGE_ALARM_LOCK 2 71 | #define MESSAGE_ALARM_UNLOCK 3 72 | #define MESSAGE_ENABLED 4 73 | #define MESSAGE_DISABLED 5 74 | 75 | // Prints system status messages. 76 | void report_status_message(uint8_t status_code); 77 | 78 | // Prints system alarm messages. 79 | void report_alarm_message(int8_t alarm_code); 80 | 81 | // Prints miscellaneous feedback messages. 82 | void report_feedback_message(uint8_t message_code); 83 | 84 | // Prints welcome message 85 | void report_init_message(); 86 | 87 | // Prints Grbl help and current global settings 88 | void report_grbl_help(); 89 | 90 | // Prints Grbl global settings 91 | void report_grbl_settings(); 92 | 93 | // Prints realtime status report 94 | void report_realtime_status(); 95 | 96 | // Prints recorded probe position 97 | void report_probe_parameters(); 98 | 99 | // Prints Grbl NGC parameters (coordinate offsets, probe) 100 | void report_ngc_parameters(); 101 | 102 | // Prints current g-code parser mode state 103 | void report_gcode_modes(); 104 | 105 | // Prints startup line 106 | void report_startup_line(uint8_t n, char *line); 107 | 108 | // Prints build info and user info 109 | void report_build_info(char *line); 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /serial.c: -------------------------------------------------------------------------------- 1 | /* 2 | serial.c - Low level functions for sending and recieving bytes via the serial port 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #include 28 | #include "system.h" 29 | #include "serial.h" 30 | #include "motion_control.h" 31 | #include "protocol.h" 32 | 33 | 34 | uint8_t serial_rx_buffer[RX_BUFFER_SIZE]; 35 | uint8_t serial_rx_buffer_head = 0; 36 | volatile uint8_t serial_rx_buffer_tail = 0; 37 | 38 | uint8_t serial_tx_buffer[TX_BUFFER_SIZE]; 39 | uint8_t serial_tx_buffer_head = 0; 40 | volatile uint8_t serial_tx_buffer_tail = 0; 41 | 42 | 43 | #ifdef ENABLE_XONXOFF 44 | volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable 45 | #endif 46 | 47 | 48 | // Returns the number of bytes used in the RX serial buffer. 49 | uint8_t serial_get_rx_buffer_count() 50 | { 51 | uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile 52 | if (serial_rx_buffer_head >= rtail) { return(serial_rx_buffer_head-rtail); } 53 | return (RX_BUFFER_SIZE - (rtail-serial_rx_buffer_head)); 54 | } 55 | 56 | 57 | // Returns the number of bytes used in the TX serial buffer. 58 | // NOTE: Not used except for debugging and ensuring no TX bottlenecks. 59 | uint8_t serial_get_tx_buffer_count() 60 | { 61 | uint8_t ttail = serial_tx_buffer_tail; // Copy to limit multiple calls to volatile 62 | if (serial_tx_buffer_head >= ttail) { return(serial_tx_buffer_head-ttail); } 63 | return (TX_BUFFER_SIZE - (ttail-serial_tx_buffer_head)); 64 | } 65 | 66 | 67 | void serial_init() 68 | { 69 | // Set baud rate 70 | #if BAUD_RATE < 57600 71 | uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1)/2 ; 72 | UCSR0A &= ~(1 << U2X0); // baud doubler off - Only needed on Uno XXX 73 | #else 74 | uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2; 75 | UCSR0A |= (1 << U2X0); // baud doubler on for high baud rates, i.e. 115200 76 | #endif 77 | UBRR0H = UBRR0_value >> 8; 78 | UBRR0L = UBRR0_value; 79 | 80 | // enable rx and tx 81 | UCSR0B |= 1<= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) { 191 | flow_ctrl = SEND_XOFF; 192 | UCSR0B |= (1 << UDRIE0); // Force TX 193 | } 194 | #endif 195 | 196 | } 197 | //TODO: else alarm on overflow? 198 | } 199 | } 200 | 201 | 202 | void serial_reset_read_buffer() 203 | { 204 | serial_rx_buffer_tail = serial_rx_buffer_head; 205 | 206 | #ifdef ENABLE_XONXOFF 207 | flow_ctrl = XON_SENT; 208 | #endif 209 | } 210 | -------------------------------------------------------------------------------- /serial.h: -------------------------------------------------------------------------------- 1 | /* 2 | serial.c - Low level functions for sending and recieving bytes via the serial port 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #ifndef serial_h 28 | #define serial_h 29 | 30 | 31 | #ifndef RX_BUFFER_SIZE 32 | #define RX_BUFFER_SIZE 128 33 | #endif 34 | #ifndef TX_BUFFER_SIZE 35 | #define TX_BUFFER_SIZE 64 36 | #endif 37 | 38 | #define SERIAL_NO_DATA 0xff 39 | 40 | #ifdef ENABLE_XONXOFF 41 | #define RX_BUFFER_FULL 96 // XOFF high watermark 42 | #define RX_BUFFER_LOW 64 // XON low watermark 43 | #define SEND_XOFF 1 44 | #define SEND_XON 2 45 | #define XOFF_SENT 3 46 | #define XON_SENT 4 47 | #define XOFF_CHAR 0x13 48 | #define XON_CHAR 0x11 49 | #endif 50 | 51 | void serial_init(); 52 | 53 | // Writes one byte to the TX serial buffer. Called by main program. 54 | void serial_write(uint8_t data); 55 | 56 | // Fetches the first byte in the serial read buffer. Called by main program. 57 | uint8_t serial_read(); 58 | 59 | // Reset and empty data in read buffer. Used by e-stop and reset. 60 | void serial_reset_read_buffer(); 61 | 62 | // Returns the number of bytes used in the RX serial buffer. 63 | uint8_t serial_get_rx_buffer_count(); 64 | 65 | // Returns the number of bytes used in the TX serial buffer. 66 | // NOTE: Not used except for debugging and ensuring no TX bottlenecks. 67 | uint8_t serial_get_tx_buffer_count(); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /settings.c: -------------------------------------------------------------------------------- 1 | /* 2 | settings.c - eeprom configuration handling 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #include "system.h" 28 | #include "settings.h" 29 | #include "eeprom.h" 30 | #include "protocol.h" 31 | #include "report.h" 32 | #include "stepper.h" 33 | 34 | settings_t settings; 35 | 36 | 37 | // Method to store startup lines into EEPROM 38 | void settings_store_startup_line(uint8_t n, char *line) 39 | { 40 | uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; 41 | memcpy_to_eeprom_with_checksum(addr,(char*)line, LINE_BUFFER_SIZE); 42 | } 43 | 44 | 45 | // Method to store build info into EEPROM 46 | void settings_store_build_info(char *line) 47 | { 48 | memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO,(char*)line, LINE_BUFFER_SIZE); 49 | } 50 | 51 | 52 | // Method to store coord data parameters into EEPROM 53 | void settings_write_coord_data(uint8_t coord_select, float *coord_data) 54 | { 55 | uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; 56 | memcpy_to_eeprom_with_checksum(addr,(char*)coord_data, sizeof(float)*N_AXIS); 57 | } 58 | 59 | 60 | // Method to store Grbl global settings struct and version number into EEPROM 61 | void write_global_settings() 62 | { 63 | eeprom_put_char(0, SETTINGS_VERSION); 64 | memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char*)&settings, sizeof(settings_t)); 65 | } 66 | 67 | 68 | // Method to restore EEPROM-saved Grbl global settings back to defaults. 69 | void settings_restore_global_settings() { 70 | settings.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS; 71 | settings.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME; 72 | settings.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK; 73 | settings.dir_invert_mask = DEFAULT_DIRECTION_INVERT_MASK; 74 | settings.status_report_mask = DEFAULT_STATUS_REPORT_MASK; 75 | settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; 76 | settings.arc_tolerance = DEFAULT_ARC_TOLERANCE; 77 | settings.homing_dir_mask = DEFAULT_HOMING_DIR_MASK; 78 | settings.homing_feed_rate = DEFAULT_HOMING_FEED_RATE; 79 | settings.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE; 80 | settings.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY; 81 | settings.homing_pulloff = DEFAULT_HOMING_PULLOFF; 82 | 83 | settings.flags = 0; 84 | if (DEFAULT_REPORT_INCHES) { settings.flags |= BITFLAG_REPORT_INCHES; } 85 | if (DEFAULT_AUTO_START) { settings.flags |= BITFLAG_AUTO_START; } 86 | if (DEFAULT_INVERT_ST_ENABLE) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } 87 | if (DEFAULT_INVERT_LIMIT_PINS) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } 88 | if (DEFAULT_SOFT_LIMIT_ENABLE) { settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; } 89 | if (DEFAULT_HARD_LIMIT_ENABLE) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } 90 | if (DEFAULT_HOMING_ENABLE) { settings.flags |= BITFLAG_HOMING_ENABLE; } 91 | 92 | settings.steps_per_deg[X_AXIS] = DEFAULT_X_STEPS_PER_DEG; 93 | settings.steps_per_deg[Y_AXIS] = DEFAULT_Y_STEPS_PER_DEG; 94 | settings.steps_per_deg[Z_AXIS] = DEFAULT_Z_STEPS_PER_DEG; 95 | settings.max_rate[X_AXIS] = DEFAULT_X_MAX_RATE; 96 | settings.max_rate[Y_AXIS] = DEFAULT_Y_MAX_RATE; 97 | settings.max_rate[Z_AXIS] = DEFAULT_Z_MAX_RATE; 98 | settings.acceleration[X_AXIS] = DEFAULT_X_ACCELERATION; 99 | settings.acceleration[Y_AXIS] = DEFAULT_Y_ACCELERATION; 100 | settings.acceleration[Z_AXIS] = DEFAULT_Z_ACCELERATION; 101 | settings.max_travel[X_AXIS] = (-DEFAULT_X_MAX_TRAVEL); 102 | settings.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL); 103 | settings.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL); 104 | 105 | write_global_settings(); 106 | } 107 | 108 | 109 | // Helper function to clear the EEPROM space containing parameter data. 110 | void settings_clear_parameters() { 111 | uint8_t idx; 112 | float coord_data[3]; 113 | memset(&coord_data, 0, sizeof(coord_data)); 114 | for (idx=0; idx < SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data); } 115 | } 116 | 117 | 118 | // Helper function to clear the EEPROM space containing the startup lines. 119 | void settings_clear_startup_lines() { 120 | #if N_STARTUP_LINE > 0 121 | eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK, 0); 122 | #endif 123 | #if N_STARTUP_LINE > 1 124 | eeprom_put_char(EEPROM_ADDR_STARTUP_BLOCK+(LINE_BUFFER_SIZE+1), 0); 125 | #endif 126 | } 127 | 128 | 129 | // Helper function to clear the EEPROM space containing the user build info string. 130 | void settings_clear_build_info() { eeprom_put_char(EEPROM_ADDR_BUILD_INFO , 0); } 131 | 132 | 133 | // Reads startup line from EEPROM. Updated pointed line string data. 134 | uint8_t settings_read_startup_line(uint8_t n, char *line) 135 | { 136 | uint32_t addr = n*(LINE_BUFFER_SIZE+1)+EEPROM_ADDR_STARTUP_BLOCK; 137 | if (!(memcpy_from_eeprom_with_checksum((char*)line, addr, LINE_BUFFER_SIZE))) { 138 | // Reset line with default value 139 | line[0] = 0; // Empty line 140 | settings_store_startup_line(n, line); 141 | return(false); 142 | } 143 | return(true); 144 | } 145 | 146 | 147 | // Reads startup line from EEPROM. Updated pointed line string data. 148 | uint8_t settings_read_build_info(char *line) 149 | { 150 | if (!(memcpy_from_eeprom_with_checksum((char*)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) { 151 | // Reset line with default value 152 | line[0] = 0; // Empty line 153 | settings_store_build_info(line); 154 | return(false); 155 | } 156 | return(true); 157 | } 158 | 159 | 160 | // Read selected coordinate data from EEPROM. Updates pointed coord_data value. 161 | uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) 162 | { 163 | uint32_t addr = coord_select*(sizeof(float)*N_AXIS+1) + EEPROM_ADDR_PARAMETERS; 164 | if (!(memcpy_from_eeprom_with_checksum((char*)coord_data, addr, sizeof(float)*N_AXIS))) { 165 | // Reset with default zero vector 166 | clear_vector_float(coord_data); 167 | settings_write_coord_data(coord_select,coord_data); 168 | return(false); 169 | } 170 | return(true); 171 | } 172 | 173 | 174 | // Reads Grbl global settings struct from EEPROM. 175 | uint8_t read_global_settings() { 176 | // Check version-byte of eeprom 177 | uint8_t version = eeprom_get_char(0); 178 | if (version == SETTINGS_VERSION) { 179 | // Read settings-record and check checksum 180 | if (!(memcpy_from_eeprom_with_checksum((char*)&settings, EEPROM_ADDR_GLOBAL, sizeof(settings_t)))) { 181 | return(false); 182 | } 183 | } else { 184 | return(false); 185 | } 186 | return(true); 187 | } 188 | 189 | 190 | // A helper method to set settings from command line 191 | uint8_t settings_store_global_setting(uint8_t parameter, float value) { 192 | if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); } 193 | if (parameter >= AXIS_SETTINGS_START_VAL) { 194 | // Store axis configuration. Axis numbering sequence set by AXIS_SETTING defines. 195 | // NOTE: Ensure the setting index corresponds to the report.c settings printout. 196 | parameter -= AXIS_SETTINGS_START_VAL; 197 | uint8_t set_idx = 0; 198 | while (set_idx < AXIS_N_SETTINGS) { 199 | if (parameter < N_AXIS) { 200 | // Valid axis setting found. 201 | switch (set_idx) { 202 | case 0: settings.steps_per_deg[parameter] = value; break; 203 | case 1: settings.max_rate[parameter] = value*60; break; // Convert to deg/min for grbl internal use. 204 | case 2: settings.acceleration[parameter] = value*60*60; break; // Convert to deg/min^2 for grbl internal use. 205 | case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use. 206 | } 207 | break; // Exit while-loop after setting has been configured and proceed to the EEPROM write call. 208 | } else { 209 | set_idx++; 210 | // If axis index greater than N_AXIS or setting index greater than number of axis settings, error out. 211 | if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) { return(STATUS_INVALID_STATEMENT); } 212 | parameter -= AXIS_SETTINGS_INCREMENT; 213 | } 214 | } 215 | } else { 216 | // Store non-axis Grbl settings 217 | uint8_t int_value = trunc(value); 218 | switch(parameter) { 219 | case 0: 220 | if (int_value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); } 221 | settings.pulse_microseconds = int_value; break; 222 | case 1: settings.stepper_idle_lock_time = int_value; break; 223 | case 2: 224 | settings.step_invert_mask = int_value; 225 | st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks. 226 | break; 227 | case 3: 228 | settings.dir_invert_mask = int_value; 229 | st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks. 230 | break; 231 | case 4: // Reset to ensure change. Immediate re-init may cause problems. 232 | if (int_value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; } 233 | else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; } 234 | break; 235 | case 5: // Reset to ensure change. Immediate re-init may cause problems. 236 | if (int_value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; } 237 | else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; } 238 | break; 239 | case 6: // Reset to ensure change. Immediate re-init may cause problems. 240 | if (int_value) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; } 241 | else { settings.flags &= ~BITFLAG_INVERT_PROBE_PIN; } 242 | break; 243 | case 10: settings.status_report_mask = int_value; 244 | case 11: settings.junction_deviation = value; break; 245 | case 12: settings.arc_tolerance = value; break; 246 | case 13: 247 | if (int_value) { settings.flags |= BITFLAG_REPORT_INCHES; } 248 | else { settings.flags &= ~BITFLAG_REPORT_INCHES; } 249 | break; 250 | case 14: // Reset to ensure change. Immediate re-init may cause problems. 251 | if (int_value) { settings.flags |= BITFLAG_AUTO_START; } 252 | else { settings.flags &= ~BITFLAG_AUTO_START; } 253 | break; 254 | case 20: 255 | if (int_value) { 256 | if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); } 257 | settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE; 258 | } else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; } 259 | break; 260 | /*case 21: 261 | if (int_value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; } 262 | else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; } 263 | limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later. 264 | break;*/ 265 | case 22: 266 | if (int_value) { settings.flags |= BITFLAG_HOMING_ENABLE; } 267 | else { 268 | settings.flags &= ~BITFLAG_HOMING_ENABLE; 269 | settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits. 270 | } 271 | break; 272 | case 23: settings.homing_dir_mask = int_value; break; 273 | case 24: settings.homing_feed_rate = value; break; 274 | case 25: settings.homing_seek_rate = value; break; 275 | case 26: settings.homing_debounce_delay = int_value; break; 276 | case 27: settings.homing_pulloff = value; break; 277 | default: 278 | return(STATUS_INVALID_STATEMENT); 279 | } 280 | } 281 | write_global_settings(); 282 | return(STATUS_OK); 283 | } 284 | 285 | 286 | // Initialize the config subsystem 287 | void settings_init() { 288 | if(!read_global_settings()) { 289 | report_status_message(STATUS_SETTING_READ_FAIL); 290 | 291 | settings_restore_global_settings(); 292 | 293 | // Force clear startup lines and build info user data. Parameters should be ok. 294 | // TODO: For next version, remove these clears. Only here because line buffer increased. 295 | settings_clear_startup_lines(); 296 | settings_clear_build_info(); 297 | 298 | report_grbl_settings(); 299 | } 300 | 301 | // Check all parameter data into a dummy variable. If error, reset to zero, otherwise do nothing. 302 | float coord_data[N_AXIS]; 303 | uint8_t i; 304 | for (i=0; i<=SETTING_INDEX_NCOORD; i++) { 305 | if (!settings_read_coord_data(i, coord_data)) { 306 | report_status_message(STATUS_SETTING_READ_FAIL); 307 | } 308 | } 309 | // NOTE: Startup lines are checked and executed by protocol_main_loop at the end of initialization. 310 | // TODO: Build info should be checked here, but will wait until v1.0 to address this. Ok for now. 311 | } 312 | 313 | 314 | // Returns step pin mask according to Grbl internal axis indexing. 315 | uint8_t get_step_pin_mask(uint8_t axis_idx) 316 | { 317 | /*if ( axis_idx == X_AXIS ) { return((1<. 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #ifndef settings_h 28 | #define settings_h 29 | 30 | 31 | #define HORUS_VERSION "0.2" 32 | 33 | // Version of the EEPROM data. Will be used to migrate existing data from older versions of Horus 34 | // when firmware is upgraded. Always stored in byte 0 of eeprom 35 | #define SETTINGS_VERSION 1 // NOTE: Check settings_reset() when moving to next version. 36 | 37 | // Define bit flag masks for the boolean settings in settings.flag. 38 | #define BITFLAG_REPORT_INCHES bit(0) 39 | #define BITFLAG_AUTO_START bit(1) 40 | #define BITFLAG_INVERT_ST_ENABLE bit(2) 41 | #define BITFLAG_HARD_LIMIT_ENABLE bit(3) 42 | #define BITFLAG_HOMING_ENABLE bit(4) 43 | #define BITFLAG_SOFT_LIMIT_ENABLE bit(5) 44 | #define BITFLAG_INVERT_LIMIT_PINS bit(6) 45 | #define BITFLAG_INVERT_PROBE_PIN bit(7) 46 | 47 | // Define status reporting boolean enable bit flags in settings.status_report_mask 48 | #define BITFLAG_RT_STATUS_MACHINE_POSITION bit(0) 49 | #define BITFLAG_RT_STATUS_WORK_POSITION bit(1) 50 | #define BITFLAG_RT_STATUS_PLANNER_BUFFER bit(2) 51 | #define BITFLAG_RT_STATUS_SERIAL_RX bit(3) 52 | 53 | // Define EEPROM memory address location values for Horus settings and parameters 54 | // NOTE: The Atmega328p has 1KB EEPROM. The upper half is reserved for parameters and 55 | // the startup script. The lower half contains the global settings and space for future 56 | // developments. 57 | #define EEPROM_ADDR_GLOBAL 1U 58 | #define EEPROM_ADDR_PARAMETERS 512U 59 | #define EEPROM_ADDR_STARTUP_BLOCK 768U 60 | #define EEPROM_ADDR_BUILD_INFO 942U 61 | 62 | // Define EEPROM address indexing for coordinate parameters 63 | #define N_COORDINATE_SYSTEM 6 // Number of supported work coordinate systems (from index 1) 64 | #define SETTING_INDEX_NCOORD N_COORDINATE_SYSTEM+1 // Total number of system stored (from index 0) 65 | // NOTE: Work coordinate indices are (0=G54, 1=G55, ... , 6=G59) 66 | #define SETTING_INDEX_G28 N_COORDINATE_SYSTEM // Home position 1 67 | #define SETTING_INDEX_G30 N_COORDINATE_SYSTEM+1 // Home position 2 68 | // #define SETTING_INDEX_G92 N_COORDINATE_SYSTEM+2 // Coordinate offset (G92.2,G92.3 not supported) 69 | 70 | // Define Grbl axis settings numbering scheme. Starts at START_VAL, every INCREMENT, over N_SETTINGS. 71 | #define AXIS_N_SETTINGS 4 72 | #define AXIS_SETTINGS_START_VAL 100 // NOTE: Reserving settings values >= 100 for axis settings. Up to 255. 73 | #define AXIS_SETTINGS_INCREMENT 10 // Must be greater than the number of axis settings 74 | 75 | // Global persistent settings (Stored from byte EEPROM_ADDR_GLOBAL onwards) 76 | typedef struct { 77 | // Axis settings 78 | float steps_per_deg[N_AXIS]; 79 | float max_rate[N_AXIS]; 80 | float acceleration[N_AXIS]; 81 | float max_travel[N_AXIS]; 82 | 83 | // Remaining Grbl settings 84 | uint8_t pulse_microseconds; 85 | uint8_t step_invert_mask; 86 | uint8_t dir_invert_mask; 87 | uint8_t stepper_idle_lock_time; // If max value 255, steppers do not disable. 88 | uint8_t status_report_mask; // Mask to indicate desired report data. 89 | float junction_deviation; 90 | float arc_tolerance; 91 | 92 | uint8_t flags; // Contains default boolean settings 93 | 94 | uint8_t homing_dir_mask; 95 | float homing_feed_rate; 96 | float homing_seek_rate; 97 | uint16_t homing_debounce_delay; 98 | float homing_pulloff; 99 | } settings_t; 100 | extern settings_t settings; 101 | 102 | // Initialize the configuration subsystem (load settings from EEPROM) 103 | void settings_init(); 104 | 105 | // Helper functions to clear and restore EEPROM defaults 106 | void settings_restore_global_settings(); 107 | void settings_clear_parameters(); 108 | void settings_clear_startup_line(); 109 | void settings_clear_build_info(); 110 | 111 | // A helper method to set new settings from command line 112 | uint8_t settings_store_global_setting(uint8_t parameter, float value); 113 | 114 | // Stores the protocol line variable as a startup line in EEPROM 115 | void settings_store_startup_line(uint8_t n, char *line); 116 | 117 | // Reads an EEPROM startup line to the protocol line variable 118 | uint8_t settings_read_startup_line(uint8_t n, char *line); 119 | 120 | // Stores build info user-defined string 121 | void settings_store_build_info(char *line); 122 | 123 | // Reads build info user-defined string 124 | uint8_t settings_read_build_info(char *line); 125 | 126 | // Writes selected coordinate data to EEPROM 127 | void settings_write_coord_data(uint8_t coord_select, float *coord_data); 128 | 129 | // Reads selected coordinate data from EEPROM 130 | uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data); 131 | 132 | // Returns the step pin mask according to Grbl's internal axis numbering 133 | uint8_t get_step_pin_mask(uint8_t i); 134 | 135 | // Returns the direction pin mask according to Grbl's internal axis numbering 136 | uint8_t get_direction_pin_mask(uint8_t i); 137 | 138 | // Returns the limit pin mask according to Grbl's internal axis numbering 139 | uint8_t get_limit_pin_mask(uint8_t i); 140 | 141 | 142 | #endif 143 | -------------------------------------------------------------------------------- /stepper.h: -------------------------------------------------------------------------------- 1 | /* 2 | stepper.h - stepper motor driver: executes motion plans of planner.c using the stepper motors 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2009-2011 Simen Svale Skogsrud 24 | Copyright (c) 2011-2014 Sungeun K. Jeon 25 | */ 26 | 27 | #ifndef stepper_h 28 | #define stepper_h 29 | 30 | #ifndef SEGMENT_BUFFER_SIZE 31 | #define SEGMENT_BUFFER_SIZE 6 32 | #endif 33 | 34 | // Initialize and setup the stepper motor subsystem 35 | void stepper_init(); 36 | 37 | // Enable steppers, but cycle does not start unless called by motion control or runtime command. 38 | void st_wake_up(); 39 | 40 | // Sets a flag to disable/enable motors on st_go_idle() function. 41 | void st_disable_on_idle(uint8_t); 42 | 43 | // Immediately disables steppers 44 | void st_go_idle(); 45 | 46 | // Generate the step and direction port invert masks. 47 | void st_generate_step_dir_invert_masks(); 48 | 49 | // Reset the stepper subsystem variables 50 | void st_reset(); 51 | 52 | // Reloads step segment buffer. Called continuously by runtime execution system. 53 | void st_prep_buffer(); 54 | 55 | // Called by planner_recalculate() when the executing block is updated by the new plan. 56 | void st_update_plan_block_parameters(); 57 | 58 | // Called by runtime status reporting if realtime rate reporting is enabled in config.h. 59 | #ifdef REPORT_REALTIME_RATE 60 | float st_get_realtime_rate(); 61 | #endif 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /system.c: -------------------------------------------------------------------------------- 1 | /* 2 | system.c - Handles system level commands and real-time processes 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2014 Sungeun K. Jeon 24 | */ 25 | 26 | #include "system.h" 27 | #include "settings.h" 28 | #include "gcode.h" 29 | #include "motion_control.h" 30 | #include "report.h" 31 | #include "print.h" 32 | 33 | 34 | void system_init() 35 | { 36 | /*PINOUT_DDR &= ~(PINOUT_MASK); // Configure as input pins 37 | PINOUT_PORT |= PINOUT_MASK; // Enable internal pull-up resistors. Normal high operation. 38 | PINOUT_PCMSK |= PINOUT_MASK; // Enable specific pins of the Pin Change Interrupt 39 | PCICR |= (1 << PINOUT_INT); // Enable Pin Change Interrupt*/ 40 | } 41 | 42 | 43 | // Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets 44 | // only the runtime command execute variable to have the main program execute these when 45 | // its ready. This works exactly like the character-based runtime commands when picked off 46 | // directly from the incoming serial data stream. 47 | /*ISR(PINOUT_INT_vect) 48 | { 49 | // Enter only if any pinout pin is actively low. 50 | if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) { 51 | if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) { 52 | mc_reset(); 53 | } else if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) { 54 | bit_true(sys.execute, EXEC_FEED_HOLD); 55 | } else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) { 56 | bit_true(sys.execute, EXEC_CYCLE_START); 57 | } 58 | } 59 | }*/ 60 | 61 | 62 | // Executes user startup script, if stored. 63 | void system_execute_startup(char *line) 64 | { 65 | uint8_t n; 66 | for (n=0; n < N_STARTUP_LINE; n++) { 67 | if (!(settings_read_startup_line(n, line))) { 68 | report_status_message(STATUS_SETTING_READ_FAIL); 69 | } else { 70 | if (line[0] != 0) { 71 | printString(line); // Echo startup line to indicate execution. 72 | report_status_message(gc_execute_line(line)); 73 | } 74 | } 75 | } 76 | } 77 | 78 | 79 | // Directs and executes one line of formatted input from protocol_process. While mostly 80 | // incoming streaming g-code blocks, this also executes Grbl internal commands, such as 81 | // settings, initiating the homing cycle, and toggling switch states. This differs from 82 | // the runtime command module by being susceptible to when Grbl is ready to execute the 83 | // next line during a cycle, so for switches like block delete, the switch only effects 84 | // the lines that are processed afterward, not necessarily real-time during a cycle, 85 | // since there are motions already stored in the buffer. However, this 'lag' should not 86 | // be an issue, since these commands are not typically used during a cycle. 87 | uint8_t system_execute_line(char *line) 88 | { 89 | uint8_t char_counter = 1; 90 | uint8_t helper_var = 0; // Helper variable 91 | float parameter, value; 92 | switch( line[char_counter] ) { 93 | case 0 : report_grbl_help(); break; 94 | case '$' : // Prints Grbl settings 95 | if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); } 96 | if ( sys.state & (STATE_CYCLE | STATE_HOLD) ) { return(STATUS_IDLE_ERROR); } // Block during cycle. Takes too long to print. 97 | else { report_grbl_settings(); } 98 | break; 99 | case 'G' : // Prints gcode parser state 100 | if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); } 101 | else { report_gcode_modes(); } 102 | break; 103 | case 'C' : // Set check g-code mode [IDLE/CHECK] 104 | if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); } 105 | // Perform reset when toggling off. Check g-code mode should only work if Grbl 106 | // is idle and ready, regardless of alarm locks. This is mainly to keep things 107 | // simple and consistent. 108 | if ( sys.state == STATE_CHECK_MODE ) { 109 | mc_reset(); 110 | report_feedback_message(MESSAGE_DISABLED); 111 | } else { 112 | if (sys.state) { return(STATUS_IDLE_ERROR); } // Requires no alarm mode. 113 | sys.state = STATE_CHECK_MODE; 114 | report_feedback_message(MESSAGE_ENABLED); 115 | } 116 | break; 117 | case 'X' : // Disable alarm lock [ALARM] 118 | if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); } 119 | if (sys.state == STATE_ALARM) { 120 | report_feedback_message(MESSAGE_ALARM_UNLOCK); 121 | sys.state = STATE_IDLE; 122 | // Don't run startup script. Prevents stored moves in startup from causing accidents. 123 | } // Otherwise, no effect. 124 | break; 125 | // case 'J' : break; // Jogging methods 126 | // TODO: Here jogging can be placed for execution as a seperate subprogram. It does not need to be 127 | // susceptible to other runtime commands except for e-stop. The jogging function is intended to 128 | // be a basic toggle on/off with controlled acceleration and deceleration to prevent skipped 129 | // steps. The user would supply the desired feedrate, axis to move, and direction. Toggle on would 130 | // start motion and toggle off would initiate a deceleration to stop. One could 'feather' the 131 | // motion by repeatedly toggling to slow the motion to the desired location. Location data would 132 | // need to be updated real-time and supplied to the user through status queries. 133 | // More controlled exact motions can be taken care of by inputting G0 or G1 commands, which are 134 | // handled by the planner. It would be possible for the jog subprogram to insert blocks into the 135 | // block buffer without having the planner plan them. It would need to manage de/ac-celerations 136 | // on its own carefully. This approach could be effective and possibly size/memory efficient. 137 | default : 138 | // Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing) 139 | if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); } 140 | switch( line[char_counter] ) { 141 | case '#' : // Print Grbl NGC parameters 142 | if ( line[++char_counter] != 0 ) { return(STATUS_INVALID_STATEMENT); } 143 | else { report_ngc_parameters(); } 144 | break; 145 | case 'H' : // Perform homing cycle [IDLE/ALARM] 146 | if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { 147 | // Only perform homing if Grbl is idle or lost. 148 | /*mc_homing_cycle();*/ 149 | if (!sys.abort) { system_execute_startup(line); } // Execute startup scripts after successful homing. 150 | } else { return(STATUS_SETTING_DISABLED); } 151 | break; 152 | case 'I' : // Print or store build info. [IDLE/ALARM] 153 | if ( line[++char_counter] == 0 ) { 154 | settings_read_build_info(line); 155 | report_build_info(line); 156 | } else { // Store startup line [IDLE/ALARM] 157 | if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); } 158 | helper_var = char_counter; // Set helper variable as counter to start of user info line. 159 | do { 160 | line[char_counter-helper_var] = line[char_counter]; 161 | } while (line[char_counter++] != 0); 162 | settings_store_build_info(line); 163 | } 164 | break; 165 | case 'N' : // Startup lines. [IDLE/ALARM] 166 | if ( line[++char_counter] == 0 ) { // Print startup lines 167 | for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) { 168 | if (!(settings_read_startup_line(helper_var, line))) { 169 | report_status_message(STATUS_SETTING_READ_FAIL); 170 | } else { 171 | report_startup_line(helper_var,line); 172 | } 173 | } 174 | break; 175 | } else { // Store startup line [IDLE Only] Prevents motion during ALARM. 176 | if (sys.state != STATE_IDLE) { return(STATUS_IDLE_ERROR); } // Store only when idle. 177 | helper_var = true; // Set helper_var to flag storing method. 178 | // No break. Continues into default: to read remaining command characters. 179 | } 180 | default : // Storing setting methods [IDLE/ALARM] 181 | if(!read_float(line, &char_counter, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); } 182 | if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); } 183 | if (helper_var) { // Store startup line 184 | // Prepare sending gcode block to gcode parser by shifting all characters 185 | helper_var = char_counter; // Set helper variable as counter to start of gcode block 186 | do { 187 | line[char_counter-helper_var] = line[char_counter]; 188 | } while (line[char_counter++] != 0); 189 | // Execute gcode block to ensure block is valid. 190 | helper_var = gc_execute_line(line); // Set helper_var to returned status code. 191 | if (helper_var) { return(helper_var); } 192 | else { 193 | helper_var = trunc(parameter); // Set helper_var to int value of parameter 194 | settings_store_startup_line(helper_var,line); 195 | } 196 | } else { // Store global setting. 197 | if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); } 198 | if(line[char_counter] != 0) { return(STATUS_INVALID_STATEMENT); } 199 | return(settings_store_global_setting((uint8_t)parameter, value)); 200 | } 201 | } 202 | } 203 | return(STATUS_OK); // If '$' command makes it to here, then everything's ok. 204 | } 205 | -------------------------------------------------------------------------------- /system.h: -------------------------------------------------------------------------------- 1 | /* 2 | system.h - Header for system level commands and real-time processes 3 | Part of Horus Firmware 4 | 5 | Copyright (c) 2014-2015 Mundo Reader S.L. 6 | 7 | Horus Firmware is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | Horus Firmware is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with Horus Firmware. If not, see . 19 | */ 20 | /* 21 | This file is based on work from Grbl v0.9, distributed under the 22 | terms of the GPLv3. See COPYING for more details. 23 | Copyright (c) 2014 Sungeun K. Jeon 24 | */ 25 | 26 | #ifndef system_h 27 | #define system_h 28 | 29 | // Define system header files and standard libraries used by Grbl 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | // Define Grbl configuration and shared header files 43 | #include "config.h" 44 | #include "defaults.h" 45 | #include "cpu_map.h" 46 | #include "nuts_bolts.h" 47 | 48 | 49 | // Define system executor bit map. Used internally by runtime protocol as runtime command flags, 50 | // which notifies the main program to execute the specified runtime command asynchronously. 51 | // NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.) The default 52 | // flags are always false, so the runtime protocol only needs to check for a non-zero value to 53 | // know when there is a runtime command to execute. 54 | #define EXEC_STATUS_REPORT bit(0) // bitmask 00000001 55 | #define EXEC_CYCLE_START bit(1) // bitmask 00000010 56 | #define EXEC_CYCLE_STOP bit(2) // bitmask 00000100 57 | #define EXEC_FEED_HOLD bit(3) // bitmask 00001000 58 | #define EXEC_RESET bit(4) // bitmask 00010000 59 | #define EXEC_ALARM bit(5) // bitmask 00100000 60 | #define EXEC_CRIT_EVENT bit(6) // bitmask 01000000 61 | // #define bit(7) // bitmask 10000000 62 | 63 | // Define system state bit map. The state variable primarily tracks the individual functions 64 | // of Grbl to manage each without overlapping. It is also used as a messaging flag for 65 | // critical events. 66 | #define STATE_IDLE 0 // Must be zero. No flags. 67 | #define STATE_ALARM bit(0) // In alarm state. Locks out all g-code processes. Allows settings access. 68 | #define STATE_CHECK_MODE bit(1) // G-code check mode. Locks out planner and motion only. 69 | #define STATE_HOMING bit(2) // Performing homing cycle 70 | #define STATE_QUEUED bit(3) // Indicates buffered blocks, awaiting cycle start. 71 | #define STATE_CYCLE bit(4) // Cycle is running 72 | #define STATE_HOLD bit(5) // Executing feed hold 73 | // #define STATE_JOG bit(6) // Jogging mode is unique like homing. 74 | 75 | 76 | // Define global system variables 77 | typedef struct { 78 | uint8_t abort; // System abort flag. Forces exit back to main loop for reset. 79 | uint8_t state; // Tracks the current state of Grbl. 80 | volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks. 81 | uint8_t homing_axis_lock; 82 | int32_t position[N_AXIS]; // Real-time machine (aka home) position vector in steps. 83 | // NOTE: This may need to be a volatile variable, if problems arise. 84 | uint8_t auto_start; // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings. 85 | volatile uint8_t probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR. 86 | int32_t probe_position[N_AXIS]; // Last probe position in machine coordinates and steps. 87 | } system_t; 88 | extern system_t sys; 89 | 90 | 91 | // Initialize the serial protocol 92 | void system_init(); 93 | 94 | // Executes an internal system command, defined as a string starting with a '$' 95 | uint8_t system_execute_line(char *line); 96 | 97 | // Checks and executes a runtime command at various stop points in main program 98 | void system_execute_runtime(); 99 | 100 | // Execute the startup script lines stored in EEPROM upon initialization 101 | void system_execute_startup(char *line); 102 | 103 | #endif 104 | --------------------------------------------------------------------------------