├── .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 |
--------------------------------------------------------------------------------