2011-12-22 13:55:45 +00:00
// Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware.
2014-02-25 10:01:15 +00:00
// License: GPL
2011-12-22 13:55:45 +00:00
2012-06-02 18:44:17 +00:00
# ifndef MARLIN_H
# define MARLIN_H
2011-11-04 17:02:56 +00:00
2011-12-22 13:55:45 +00:00
# define FORCE_INLINE __attribute__((always_inline)) inline
2011-11-30 08:07:56 +00:00
# include <math.h>
2011-12-22 13:55:45 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <inttypes.h>
2012-02-09 18:38:53 +00:00
# include <util/delay.h>
2011-12-22 11:38:50 +00:00
# include <avr/pgmspace.h>
2011-12-22 13:55:45 +00:00
# include <avr/eeprom.h>
2012-11-06 11:06:41 +00:00
# include <avr/interrupt.h>
2011-12-22 13:55:45 +00:00
# include "fastio.h"
# include "Configuration.h"
2012-12-09 09:40:33 +00:00
2013-01-07 16:26:50 +00:00
# if (ARDUINO >= 100)
2015-03-07 06:14:34 +00:00
# include "Arduino.h"
2011-12-01 15:38:01 +00:00
# else
2015-03-07 06:14:34 +00:00
# include "WProgram.h"
2015-01-02 16:11:18 +00:00
# endif
2015-03-14 11:28:22 +00:00
# define BIT(b) (1<<(b))
2015-03-15 21:52:50 +00:00
# define TEST(n,b) (((n)&BIT(b))!=0)
2015-04-01 01:52:19 +00:00
# define RADIANS(d) ((d)*M_PI / 180.0)
# define DEGREES(r) ((d)*180.0 / M_PI)
2015-04-13 01:07:08 +00:00
# define NOLESS(v,n) do{ if (v < n) v = n; }while(0)
# define NOMORE(v,n) do{ if (v > n) v = n; }while(0)
typedef unsigned long millis_t ;
2015-03-14 11:28:22 +00:00
2015-01-02 16:11:18 +00:00
// Arduino < 1.0.0 does not define this, so we need to do it ourselves
# ifndef analogInputToDigitalPin
2015-03-07 06:14:34 +00:00
# define analogInputToDigitalPin(p) ((p) + 0xA0)
2011-12-01 15:38:01 +00:00
# endif
2011-12-22 11:38:50 +00:00
2013-10-30 10:45:32 +00:00
# ifdef AT90USB
2015-03-07 06:14:34 +00:00
# include "HardwareSerial.h"
2013-10-30 10:45:32 +00:00
# endif
2011-11-27 20:12:55 +00:00
# include "MarlinSerial.h"
2011-11-09 19:27:15 +00:00
2011-12-22 13:55:45 +00:00
# ifndef cbi
2015-03-07 06:14:34 +00:00
# define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
2011-12-22 13:55:45 +00:00
# endif
# ifndef sbi
2015-03-07 06:14:34 +00:00
# define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
2011-12-22 13:55:45 +00:00
# endif
# include "WString.h"
2013-10-30 10:45:32 +00:00
# ifdef AT90USB
2015-03-07 06:14:34 +00:00
# ifdef BTENABLED
# define MYSERIAL bt
# else
# define MYSERIAL Serial
# endif // BTENABLED
2013-11-10 15:36:37 +00:00
# else
# define MYSERIAL MSerial
2013-10-30 10:45:32 +00:00
# endif
2015-04-04 04:43:30 +00:00
# define SERIAL_CHAR(x) MYSERIAL.write(x)
# define SERIAL_EOL SERIAL_CHAR('\n')
# define SERIAL_PROTOCOLCHAR(x) SERIAL_CHAR(x)
# define SERIAL_PROTOCOL(x) MYSERIAL.print(x)
# define SERIAL_PROTOCOL_F(x,y) MYSERIAL.print(x,y)
# define SERIAL_PROTOCOLPGM(x) serialprintPGM(PSTR(x))
# define SERIAL_PROTOCOLLN(x) do{ MYSERIAL.print(x),MYSERIAL.write('\n'); }while(0)
# define SERIAL_PROTOCOLLNPGM(x) do{ serialprintPGM(PSTR(x)),MYSERIAL.write('\n'); }while(0)
2011-12-04 11:40:18 +00:00
2011-11-09 19:27:15 +00:00
2014-12-28 06:09:42 +00:00
extern const char errormagic [ ] PROGMEM ;
extern const char echomagic [ ] PROGMEM ;
2015-04-04 04:43:30 +00:00
# define SERIAL_ERROR_START serialprintPGM(errormagic)
2011-11-09 19:27:15 +00:00
# define SERIAL_ERROR(x) SERIAL_PROTOCOL(x)
# define SERIAL_ERRORPGM(x) SERIAL_PROTOCOLPGM(x)
# define SERIAL_ERRORLN(x) SERIAL_PROTOCOLLN(x)
# define SERIAL_ERRORLNPGM(x) SERIAL_PROTOCOLLNPGM(x)
2015-04-04 04:43:30 +00:00
# define SERIAL_ECHO_START serialprintPGM(echomagic)
2011-11-09 19:27:15 +00:00
# define SERIAL_ECHO(x) SERIAL_PROTOCOL(x)
# define SERIAL_ECHOPGM(x) SERIAL_PROTOCOLPGM(x)
# define SERIAL_ECHOLN(x) SERIAL_PROTOCOLLN(x)
# define SERIAL_ECHOLNPGM(x) SERIAL_PROTOCOLLNPGM(x)
2015-04-04 04:43:30 +00:00
# define SERIAL_ECHOPAIR(name,value) do{ serial_echopair_P(PSTR(name),(value)); }while(0)
2015-01-29 05:19:51 +00:00
2012-08-04 15:13:25 +00:00
void serial_echopair_P ( const char * s_P , float v ) ;
void serial_echopair_P ( const char * s_P , double v ) ;
void serial_echopair_P ( const char * s_P , unsigned long v ) ;
2011-11-09 21:09:16 +00:00
2015-04-04 03:45:24 +00:00
// Things to write to serial from Program memory. Saves 400 to 2k of RAM.
FORCE_INLINE void serialprintPGM ( const char * str ) {
2015-04-04 04:58:48 +00:00
char ch ;
while ( ( ch = pgm_read_byte ( str ) ) ) {
2012-02-11 15:02:47 +00:00
MYSERIAL . write ( ch ) ;
2015-04-04 04:58:48 +00:00
str + + ;
2011-11-09 19:27:15 +00:00
}
}
2011-08-12 20:28:35 +00:00
void get_command ( ) ;
void process_commands ( ) ;
2014-12-30 07:30:37 +00:00
void manage_inactivity ( bool ignore_stepper_queue = false ) ;
2011-08-12 20:28:35 +00:00
2015-04-03 22:31:35 +00:00
# if defined(DUAL_X_CARRIAGE) && HAS_X_ENABLE && HAS_X2_ENABLE
2015-02-23 15:12:35 +00:00
# define enable_x() do { X_ENABLE_WRITE( X_ENABLE_ON); X2_ENABLE_WRITE( X_ENABLE_ON); } while (0)
# define disable_x() do { X_ENABLE_WRITE(!X_ENABLE_ON); X2_ENABLE_WRITE(!X_ENABLE_ON); axis_known_position[X_AXIS] = false; } while (0)
2015-04-03 22:31:35 +00:00
# elif HAS_X_ENABLE
2015-02-23 15:12:35 +00:00
# define enable_x() X_ENABLE_WRITE( X_ENABLE_ON)
# define disable_x() { X_ENABLE_WRITE(!X_ENABLE_ON); axis_known_position[X_AXIS] = false; }
2011-08-12 20:28:35 +00:00
# else
2011-11-06 18:23:08 +00:00
# define enable_x() ;
# define disable_x() ;
2011-08-12 20:28:35 +00:00
# endif
2011-11-06 18:23:08 +00:00
2015-04-03 22:31:35 +00:00
# if HAS_Y_ENABLE
2013-09-17 18:19:20 +00:00
# ifdef Y_DUAL_STEPPER_DRIVERS
2015-02-23 15:12:35 +00:00
# define enable_y() { Y_ENABLE_WRITE( Y_ENABLE_ON); Y2_ENABLE_WRITE(Y_ENABLE_ON); }
# define disable_y() { Y_ENABLE_WRITE(!Y_ENABLE_ON); Y2_ENABLE_WRITE(!Y_ENABLE_ON); axis_known_position[Y_AXIS] = false; }
2013-09-17 18:19:20 +00:00
# else
2015-02-23 15:12:35 +00:00
# define enable_y() Y_ENABLE_WRITE( Y_ENABLE_ON)
# define disable_y() { Y_ENABLE_WRITE(!Y_ENABLE_ON); axis_known_position[Y_AXIS] = false; }
2013-09-17 18:19:20 +00:00
# endif
2011-08-12 20:28:35 +00:00
# else
2011-11-06 18:23:08 +00:00
# define enable_y() ;
# define disable_y() ;
2011-08-12 20:28:35 +00:00
# endif
2011-11-06 18:23:08 +00:00
2015-04-03 22:31:35 +00:00
# if HAS_Z_ENABLE
2012-08-04 06:32:26 +00:00
# ifdef Z_DUAL_STEPPER_DRIVERS
2015-02-23 15:12:35 +00:00
# define enable_z() { Z_ENABLE_WRITE( Z_ENABLE_ON); Z2_ENABLE_WRITE(Z_ENABLE_ON); }
# define disable_z() { Z_ENABLE_WRITE(!Z_ENABLE_ON); Z2_ENABLE_WRITE(!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
2012-08-04 06:32:26 +00:00
# else
2015-02-23 15:12:35 +00:00
# define enable_z() Z_ENABLE_WRITE( Z_ENABLE_ON)
# define disable_z() { Z_ENABLE_WRITE(!Z_ENABLE_ON); axis_known_position[Z_AXIS] = false; }
2012-08-04 06:32:26 +00:00
# endif
2011-08-12 20:28:35 +00:00
# else
2011-11-06 18:23:08 +00:00
# define enable_z() ;
# define disable_z() ;
2011-08-12 20:28:35 +00:00
# endif
2011-11-04 17:02:56 +00:00
2015-04-03 22:31:35 +00:00
# if HAS_E0_ENABLE
2015-04-04 03:45:24 +00:00
# define enable_e0() E0_ENABLE_WRITE( E_ENABLE_ON)
2015-02-23 15:12:35 +00:00
# define disable_e0() E0_ENABLE_WRITE(!E_ENABLE_ON)
2011-08-12 20:28:35 +00:00
# else
2011-12-06 04:33:33 +00:00
# define enable_e0() /* nothing */
# define disable_e0() /* nothing */
2011-08-12 20:28:35 +00:00
# endif
2015-04-03 22:31:35 +00:00
# if (EXTRUDERS > 1) && HAS_E1_ENABLE
2015-04-04 03:45:24 +00:00
# define enable_e1() E1_ENABLE_WRITE( E_ENABLE_ON)
2015-02-23 15:12:35 +00:00
# define disable_e1() E1_ENABLE_WRITE(!E_ENABLE_ON)
2011-12-06 04:33:33 +00:00
# else
# define enable_e1() /* nothing */
# define disable_e1() /* nothing */
# endif
2015-04-03 22:31:35 +00:00
# if (EXTRUDERS > 2) && HAS_E2_ENABLE
2015-04-04 03:45:24 +00:00
# define enable_e2() E2_ENABLE_WRITE( E_ENABLE_ON)
2015-02-23 15:12:35 +00:00
# define disable_e2() E2_ENABLE_WRITE(!E_ENABLE_ON)
2011-12-06 04:33:33 +00:00
# else
# define enable_e2() /* nothing */
# define disable_e2() /* nothing */
# endif
2015-04-03 22:31:35 +00:00
# if (EXTRUDERS > 3) && HAS_E3_ENABLE
2015-04-04 03:45:24 +00:00
# define enable_e3() E3_ENABLE_WRITE( E_ENABLE_ON)
2015-02-23 15:12:35 +00:00
# define disable_e3() E3_ENABLE_WRITE(!E_ENABLE_ON)
2015-01-23 22:13:06 +00:00
# else
# define enable_e3() /* nothing */
# define disable_e3() /* nothing */
# endif
2011-12-06 04:33:33 +00:00
2015-04-04 03:45:24 +00:00
/**
* The axis order in all axis related arrays is X , Y , Z , E
*/
# define NUM_AXIS 4
/**
* Axis indices as enumerated constants
*
* A_AXIS and B_AXIS are used by COREXY printers
* X_HEAD and Y_HEAD is used for systems that don ' t have a 1 : 1 relationship between X_AXIS and X Head movement , like CoreXY bots .
*/
2015-03-21 03:42:49 +00:00
enum AxisEnum { X_AXIS = 0 , Y_AXIS = 1 , A_AXIS = 0 , B_AXIS = 1 , Z_AXIS = 2 , E_AXIS = 3 , X_HEAD = 4 , Y_HEAD = 5 } ;
2011-08-12 20:28:35 +00:00
2015-05-10 19:37:39 +00:00
enum EndstopEnum { X_MIN = 0 , Y_MIN = 1 , Z_MIN = 2 , Z_PROBE = 3 , X_MAX = 4 , Y_MAX = 5 , Z_MAX = 6 } ;
2015-04-04 02:25:22 +00:00
void enable_all_steppers ( ) ;
void disable_all_steppers ( ) ;
2011-08-12 20:28:35 +00:00
void FlushSerialRequestResend ( ) ;
void ClearToSend ( ) ;
void get_coordinates ( ) ;
2013-06-08 22:51:58 +00:00
# ifdef DELTA
2015-03-20 09:50:28 +00:00
void calculate_delta ( float cartesian [ 3 ] ) ;
2015-03-07 18:36:21 +00:00
# ifdef ENABLE_AUTO_BED_LEVELING
2015-03-20 09:50:28 +00:00
extern int delta_grid_spacing [ 2 ] ;
void adjust_delta ( float cartesian [ 3 ] ) ;
2015-03-07 18:36:21 +00:00
# endif
2015-03-20 09:50:28 +00:00
extern float delta [ 3 ] ;
2013-06-08 22:51:58 +00:00
# endif
2014-06-23 15:09:57 +00:00
# ifdef SCARA
2015-03-20 09:50:28 +00:00
void calculate_delta ( float cartesian [ 3 ] ) ;
void calculate_SCARA_forward_Transform ( float f_scara [ 3 ] ) ;
2014-06-23 15:09:57 +00:00
# endif
2015-03-07 18:36:21 +00:00
void reset_bed_level ( ) ;
2011-08-12 20:28:35 +00:00
void prepare_move ( ) ;
2011-11-05 18:21:36 +00:00
void kill ( ) ;
2012-03-25 12:36:51 +00:00
void Stop ( ) ;
2015-03-07 20:43:15 +00:00
# ifdef FILAMENT_RUNOUT_SENSOR
2015-03-20 09:50:28 +00:00
void filrunout ( ) ;
2015-03-07 20:43:15 +00:00
# endif
2015-04-27 03:08:45 +00:00
/**
* Debug flags - not yet widely applied
*/
enum DebugFlags {
DEBUG_ECHO = BIT ( 0 ) ,
DEBUG_INFO = BIT ( 1 ) ,
DEBUG_ERRORS = BIT ( 2 ) ,
DEBUG_DRYRUN = BIT ( 3 ) ,
DEBUG_COMMUNICATION = BIT ( 4 )
} ;
extern uint8_t marlin_debug_flags ;
2015-04-08 07:56:19 +00:00
extern bool Running ;
inline bool IsRunning ( ) { return Running ; }
inline bool IsStopped ( ) { return ! Running ; }
2011-08-12 20:28:35 +00:00
2015-04-13 01:07:08 +00:00
bool enqueuecommand ( const char * cmd ) ; //put a single ASCII command at the end of the current buffer or return false when it is full
void enqueuecommands_P ( const char * cmd ) ; //put one or many ASCII commands at the end of the current buffer, read from flash
2015-02-07 22:24:20 +00:00
2011-11-15 19:54:40 +00:00
void prepare_arc_move ( char isclockwise ) ;
2012-08-09 17:15:32 +00:00
void clamp_to_software_endstops ( float target [ 3 ] ) ;
2011-11-04 17:02:56 +00:00
2015-04-13 01:07:08 +00:00
extern millis_t previous_cmd_ms ;
inline void refresh_cmd_timeout ( ) { previous_cmd_ms = millis ( ) ; }
2014-02-03 10:30:12 +00:00
2012-05-02 17:26:14 +00:00
# ifdef FAST_PWM_FAN
2015-03-20 09:50:28 +00:00
void setPwmFrequency ( uint8_t pin , int val ) ;
2012-05-02 17:26:14 +00:00
# endif
2011-11-04 17:02:56 +00:00
# ifndef CRITICAL_SECTION_START
2011-11-06 18:23:08 +00:00
# define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli();
# define CRITICAL_SECTION_END SREG = _sreg;
2015-04-04 03:45:24 +00:00
# endif
2011-08-12 20:28:35 +00:00
2011-11-04 17:02:56 +00:00
extern float homing_feedrate [ ] ;
extern bool axis_relative_modes [ ] ;
2015-04-14 00:17:36 +00:00
extern int feedrate_multiplier ;
2014-12-29 01:43:14 +00:00
extern bool volumetric_enabled ;
2014-03-15 17:09:46 +00:00
extern int extruder_multiply [ EXTRUDERS ] ; // sets extrude multiply factor (in percent) for each extruder individually
2014-12-29 01:43:14 +00:00
extern float filament_size [ EXTRUDERS ] ; // cross-sectional area of filament (in millimeters), typically around 1.75 or 2.85, 0 disables the volumetric calculations for the extruder.
2014-01-31 16:43:11 +00:00
extern float volumetric_multiplier [ EXTRUDERS ] ; // reciprocal of cross-sectional area of filament (in square millimeters), stored this way to reduce computational burden in planner
2015-04-04 03:45:24 +00:00
extern float current_position [ NUM_AXIS ] ;
2015-03-21 23:30:02 +00:00
extern float home_offset [ 3 ] ;
2015-04-04 03:45:24 +00:00
2013-08-27 23:15:20 +00:00
# ifdef DELTA
2015-03-20 09:50:28 +00:00
extern float endstop_adj [ 3 ] ;
extern float delta_radius ;
extern float delta_diagonal_rod ;
extern float delta_segments_per_second ;
void recalc_delta_settings ( float radius , float diagonal_rod ) ;
2015-03-24 17:06:44 +00:00
# elif defined(Z_DUAL_ENDSTOPS)
2015-04-04 03:45:24 +00:00
extern float z_endstop_adj ;
2013-08-27 23:15:20 +00:00
# endif
2015-04-04 03:45:24 +00:00
2014-06-23 15:09:57 +00:00
# ifdef SCARA
2015-03-20 09:50:28 +00:00
extern float axis_scaling [ 3 ] ; // Build size scaling
2014-06-23 15:09:57 +00:00
# endif
2015-04-04 03:45:24 +00:00
M206: always use homing ("homeing") offsets
Previously the parameters set in M206 would only be used if a G82
command was sent with specific axis home values. This limits its
usefulness.
Really, we should have a way to adjust the XYZ homing of a machine in
the eeprom. So as the first stage of this, make M206 affect every
home command. The values set using M206 are now added to the
configuration variables [XYZ]_HOME_POS.
This is achieved by replacing all uses of [XYZ]_HOME_POS in the code
by a new home_pos[] which includes the adjustment. We also have to
adjust the uses of [XYZ]_{MIN,MAX}_POS similarly - see below.
To allow axis_is_at_home to be written as a function taking an axis
index rather than a macro taking an axis letter, we provide
constant arrays in program memory containing the values of
[XYZ]_{MIN,MAX,HOME}_POS from the compiled-in configuration.
This is done with some helper macros to deal with the declaration
(XYZ_CONSTS_FROM_CONFIG) and definition of the inline function which
does the program memory access.
We also introduce the overloaded function read_pgm_any, whose
instances are produced with DEFINE_PGM_READ_ANY, which allows the
access functions to automatically produce the correct type.
The type- and pointer-massaging code in the access function boils
down, when compiled, to a simple program memory access.
A question arises: if the M206 offset is set, should this adjustment
to the home position shift or change the possible range of movement
permitted by the software endstops ?
The documentation in Configuration.h describes these limits as:
// Travel limits after homing
Since this is a file containing physical limits, and actual suggested
values for these configuration parameters appear to include a certain
amount of slop, I've taken the view that these should be regarded as
nominal physical distances from the limit switches, and that the
permissible travel should be unaffected by M206.
So for example with the (rather unrealistic)
#define X_HOME_DIR -1
#define X_MIN_POS -20
#define X_HOME_POS 0
#define X_MAX_POS 100
no matter the setting of M206 X, the machine would be permitted
to move from 20mm "beyond" the limit switch trigger point in
the negative X direction and 100mm away from the limit switch in
the positive X direction, for a total travel of 120mm.
With M206 X-10 that would be considered to correspond to X coordinates
-30 to +90. With M206 X+10 that would be considered to correspond to
X coordinates -10 to +110.
fixes #200 (in ErikZalm/Marlin).
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
2012-08-01 20:12:14 +00:00
extern float min_pos [ 3 ] ;
extern float max_pos [ 3 ] ;
2013-11-28 00:37:35 +00:00
extern bool axis_known_position [ 3 ] ;
2015-04-04 03:45:24 +00:00
2015-03-26 04:14:00 +00:00
# ifdef ENABLE_AUTO_BED_LEVELING
extern float zprobe_zoffset ;
# endif
2015-04-04 03:45:24 +00:00
2015-04-09 08:40:48 +00:00
# ifdef PREVENT_DANGEROUS_EXTRUDE
extern float extrude_min_temp ;
# endif
2012-12-03 11:13:20 +00:00
extern int fanSpeed ;
2015-04-04 03:45:24 +00:00
2013-05-14 21:56:32 +00:00
# ifdef BARICUDA
2015-03-20 09:50:28 +00:00
extern int ValvePressure ;
extern int EtoPPressure ;
2013-05-14 21:56:32 +00:00
# endif
2012-12-03 11:13:20 +00:00
2013-06-09 10:52:49 +00:00
# ifdef FAN_SOFT_PWM
2015-03-20 09:50:28 +00:00
extern unsigned char fanSpeedSoftPwm ;
2013-06-09 10:52:49 +00:00
# endif
2014-08-07 00:30:57 +00:00
# ifdef FILAMENT_SENSOR
2015-01-02 16:11:18 +00:00
extern float filament_width_nominal ; //holds the theoretical filament diameter ie., 3.00 or 1.75
extern bool filament_sensor ; //indicates that filament sensor readings should control extrusion
extern float filament_width_meas ; //holds the filament diameter as accurately measured
2014-08-07 00:30:57 +00:00
extern signed char measurement_delay [ ] ; //ring buffer to delay measurement
2015-03-21 03:42:49 +00:00
extern int delay_index1 , delay_index2 ; //ring buffer index. used by planner, temperature, and main code
2014-08-07 00:30:57 +00:00
extern float delay_dist ; //delay distance counter
extern int meas_delay_cm ; //delay distance
# endif
2012-12-03 11:13:20 +00:00
# ifdef FWRETRACT
2015-03-20 09:50:28 +00:00
extern bool autoretract_enabled ;
extern bool retracted [ EXTRUDERS ] ;
extern float retract_length , retract_length_swap , retract_feedrate , retract_zlift ;
extern float retract_recover_length , retract_recover_length_swap , retract_recover_feedrate ;
2012-12-03 11:13:20 +00:00
# endif
2011-11-04 17:02:56 +00:00
2015-04-14 00:17:36 +00:00
extern millis_t print_job_start_ms ;
extern millis_t print_job_stop_ms ;
2012-11-06 11:06:41 +00:00
2011-12-06 04:33:33 +00:00
// Handling multiple extruders pins
extern uint8_t active_extruder ;
2014-02-05 09:47:12 +00:00
# ifdef DIGIPOT_I2C
2015-03-20 09:50:28 +00:00
extern void digipot_i2c_set_current ( int channel , float current ) ;
extern void digipot_i2c_init ( ) ;
2011-11-04 17:02:56 +00:00
# endif
2014-12-29 01:43:14 +00:00
2015-01-02 16:11:18 +00:00
extern void calculate_volumetric_multipliers ( ) ;
2015-03-20 09:50:28 +00:00
# endif //MARLIN_H