Skip to content
Snippets Groups Projects
Marlin_main.cpp 363 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Marlin 3D Printer Firmware
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     * Copyright (C) 2016, 2017 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
    
     *
     * Based on Sprinter and grbl.
     * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation, either version 3 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
     * About Marlin
     *
     * This firmware is a mashup between Sprinter and grbl.
     *  - https://github.com/kliment/Sprinter
     *  - https://github.com/simen/grbl/tree
    
    Erik vd Zalm's avatar
    Erik vd Zalm committed
    
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    /**
    
     * -----------------
     * G-Codes in Marlin
     * -----------------
     *
     * Helpful G-code references:
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     *  - http://linuxcnc.org/handbook/gcode/g-code.html
     *  - http://objects.reprap.org/wiki/Mendel_User_Manual:_RepRapGCodes
     *
    
     * Help to document Marlin's G-codes online:
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     *  - http://reprap.org/wiki/G-code
    
     *  - https://github.com/MarlinFirmware/MarlinDocumentation
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     *
     * "G" Codes
     *
     * G0  -> G1
     * G1  - Coordinated Movement X Y Z E
     * G2  - CW ARC
     * G3  - CCW ARC
     * G4  - Dwell S<seconds> or P<milliseconds>
    
     * G5  - Cubic B-spline with XYZE destination and IJPQ offsets
    
     * G10 - Retract filament according to settings of M207
     * G11 - Retract recover filament according to settings of M208
     * G12 - Clean tool
    
     * G20 - Set input units to inches
     * G21 - Set input units to millimeters
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     * G28 - Home one or more axes
    
     * G29 - Detailed Z probe, probes the bed at 3 or more points.  Will fail if you haven't homed yet.
    
    Nikolay Zinov's avatar
    Nikolay Zinov committed
     * G30 - Single Z probe, probes bed at X Y location (defaults to current XY location)
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     * G31 - Dock sled (Z_PROBE_SLED only)
     * G32 - Undock sled (Z_PROBE_SLED only)
    
    Bob-the-Kuhn's avatar
    Bob-the-Kuhn committed
     * G38 - Probe target - similar to G28 except it uses the Z_MIN_PROBE for all three axes
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     * G90 - Use Absolute Coordinates
     * G91 - Use Relative Coordinates
     * G92 - Set current position to coordinates given
     *
     * "M" Codes
     *
     * M0   - Unconditional stop - Wait for user to press a button on the LCD (Only if ULTRA_LCD is enabled)
     * M1   - Same as M0
     * M17  - Enable/Power all stepper motors
     * M18  - Disable all stepper motors; same as M84
    
     * M20  - List SD card. (Requires SDSUPPORT)
     * M21  - Init SD card. (Requires SDSUPPORT)
     * M22  - Release SD card. (Requires SDSUPPORT)
     * M23  - Select SD file: "M23 /path/file.gco". (Requires SDSUPPORT)
     * M24  - Start/resume SD print. (Requires SDSUPPORT)
     * M25  - Pause SD print. (Requires SDSUPPORT)
     * M26  - Set SD position in bytes: "M26 S12345". (Requires SDSUPPORT)
     * M27  - Report SD print status. (Requires SDSUPPORT)
     * M28  - Start SD write: "M28 /path/file.gco". (Requires SDSUPPORT)
     * M29  - Stop SD write. (Requires SDSUPPORT)
     * M30  - Delete file from SD: "M30 /path/file.gco"
     * M31  - Report time since last M109 or SD card start to serial.
     * M32  - Select file and start SD print: "M32 [S<bytepos>] !/path/file.gco#". (Requires SDSUPPORT)
     *        Use P to run other files as sub-programs: "M32 P !filename#"
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     *        The '#' is necessary when calling from within sd files, as it stops buffer prereading
    
     * M33  - Get the longname version of a path. (Requires LONG_FILENAME_HOST_SUPPORT)
    
     * M34  - Set SD Card sorting options. (Requires SDCARD_SORT_ALPHA)
    
     * M42  - Change pin status via gcode: M42 P<pin> S<value>. LED pin assumed if P is omitted.
    
     * M43  - Display pin status, watch pins for changes, watch endstops & toggle LED, Z servo probe test, toggle pins
    
     * M48  - Measure Z Probe repeatability: M48 P<points> X<pos> Y<pos> V<level> E<engage> L<legs>. (Requires Z_MIN_PROBE_REPEATABILITY_TEST)
     * M75  - Start the print job timer.
     * M76  - Pause the print job timer.
     * M77  - Stop the print job timer.
     * M78  - Show statistical information about the print jobs. (Requires PRINTCOUNTER)
     * M80  - Turn on Power Supply. (Requires POWER_SUPPLY)
     * M81  - Turn off Power Supply. (Requires POWER_SUPPLY)
     * M82  - Set E codes absolute (default).
     * M83  - Set E codes relative while in Absolute (G90) mode.
     * M84  - Disable steppers until next move, or use S<seconds> to specify an idle
     *        duration after which steppers should turn off. S0 disables the timeout.
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     * M85  - Set inactivity shutdown timer with parameter S<seconds>. To disable set zero (default)
    
     * M92  - Set planner.axis_steps_per_mm for one or more axes.
     * M104 - Set extruder target temp.
     * M105 - Report current temperatures.
     * M106 - Fan on.
     * M107 - Fan off.
     * M108 - Break out of heating loops (M109, M190, M303). With no controller, breaks out of M0/M1. (Requires EMERGENCY_PARSER)
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     * M109 - Sxxx Wait for extruder current temp to reach target temp. Waits only when heating
     *        Rxxx Wait for extruder current temp to reach target temp. Waits when heating and cooling
    
     *        If AUTOTEMP is enabled, S<mintemp> B<maxtemp> F<factor>. Exit autotemp by any M109 without F
    
     * M110 - Set the current line number. (Used by host printing)
     * M111 - Set debug flags: "M111 S<flagbits>". See flag bits defined in enum.h.
     * M112 - Emergency stop.
     * M113 - Get or set the timeout interval for Host Keepalive "busy" messages. (Requires HOST_KEEPALIVE_FEATURE)
     * M114 - Report current position.
    
     * M115 - Report capabilities. (Extended capabilities requires EXTENDED_CAPABILITIES_REPORT)
    
     * M117 - Display a message on the controller screen. (Requires an LCD)
     * M119 - Report endstops status.
     * M120 - Enable endstops detection.
     * M121 - Disable endstops detection.
    
     * M125 - Save current position and move to filament change position. (Requires PARK_HEAD_ON_PAUSE)
    
     * M126 - Solenoid Air Valve Open. (Requires BARICUDA)
     * M127 - Solenoid Air Valve Closed. (Requires BARICUDA)
     * M128 - EtoP Open. (Requires BARICUDA)
     * M129 - EtoP Closed. (Requires BARICUDA)
     * M140 - Set bed target temp. S<temp>
     * M145 - Set heatup values for materials on the LCD. H<hotend> B<bed> F<fan speed> for S<material> (0=PLA, 1=ABS)
     * M149 - Set temperature units. (Requires TEMPERATURE_UNITS_SUPPORT)
    
     * M150 - Set Status LED Color as R<red> U<green> B<blue>. Values 0-255. (Requires BLINKM or RGB_LED)
    
     * M155 - Auto-report temperatures with interval of S<seconds>. (Requires AUTO_REPORT_TEMPERATURES)
    
     * M163 - Set a single proportion for a mixing extruder. (Requires MIXING_EXTRUDER)
     * M164 - Save the mix as a virtual extruder. (Requires MIXING_EXTRUDER and MIXING_VIRTUAL_TOOLS)
     * M165 - Set the proportions for a mixing extruder. Use parameters ABCDHI to set the mixing factors. (Requires MIXING_EXTRUDER)
     * M190 - Sxxx Wait for bed current temp to reach target temp. ** Waits only when heating! **
     *        Rxxx Wait for bed current temp to reach target temp. ** Waits for heating or cooling. **
    
     * M200 - Set filament diameter, D<diameter>, setting E axis units to cubic. (Use S0 to revert to linear units.)
    
     * M201 - Set max acceleration in units/s^2 for print moves: "M201 X<accel> Y<accel> Z<accel> E<accel>"
     * M202 - Set max acceleration in units/s^2 for travel moves: "M202 X<accel> Y<accel> Z<accel> E<accel>" ** UNUSED IN MARLIN! **
     * M203 - Set maximum feedrate: "M203 X<fr> Y<fr> Z<fr> E<fr>" in units/sec.
     * M204 - Set default acceleration in units/sec^2: P<printing> R<extruder_only> T<travel>
    
     * M205 - Set advanced settings. Current units apply:
                S<print> T<travel> minimum speeds
                B<minimum segment time>
    
    MagoKimbra's avatar
    MagoKimbra committed
                X<max X jerk>, Y<max Y jerk>, Z<max Z jerk>, E<max E jerk>
    
     * M206 - Set additional homing offset.
     * M207 - Set Retract Length: S<length>, Feedrate: F<units/min>, and Z lift: Z<distance>. (Requires FWRETRACT)
     * M208 - Set Recover (unretract) Additional (!) Length: S<length> and Feedrate: F<units/min>. (Requires FWRETRACT)
     * M209 - Turn Automatic Retract Detection on/off: S<0|1> (For slicers that don't support G10/11). (Requires FWRETRACT)
    
              Every normal extrude-only move will be classified as retract depending on the direction.
    
     * M211 - Enable, Disable, and/or Report software endstops: S<0|1> (Requires MIN_SOFTWARE_ENDSTOPS or MAX_SOFTWARE_ENDSTOPS)
    
     * M218 - Set a tool offset: "M218 T<index> X<offset> Y<offset>". (Requires 2 or more extruders)
     * M220 - Set Feedrate Percentage: "M220 S<percent>" (i.e., "FR" on the LCD)
     * M221 - Set Flow Percentage: "M221 S<percent>"
     * M226 - Wait until a pin is in a given state: "M226 P<pin> S<state>"
     * M240 - Trigger a camera to take a photograph. (Requires CHDK or PHOTOGRAPH_PIN)
     * M250 - Set LCD contrast: "M250 C<contrast>" (0-63). (Requires LCD support)
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     * M260 - i2c Send Data (Requires EXPERIMENTAL_I2CBUS)
     * M261 - i2c Request Data (Requires EXPERIMENTAL_I2CBUS)
    
     * M280 - Set servo position absolute: "M280 P<index> S<angle|µs>". (Requires servos)
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     * M300 - Play beep sound S<frequency Hz> P<duration ms>
    
     * M301 - Set PID parameters P I and D. (Requires PIDTEMP)
     * M302 - Allow cold extrudes, or set the minimum extrude S<temperature>. (Requires PREVENT_COLD_EXTRUSION)
     * M303 - PID relay autotune S<temperature> sets the target temperature. Default 150C. (Requires PIDTEMP)
     * M304 - Set bed PID parameters P I and D. (Requires PIDTEMPBED)
    
     * M355 - Turn the Case Light on/off and set its brightness. (Requires CASE_LIGHT_PIN)
    
     * M380 - Activate solenoid on active extruder. (Requires EXT_SOLENOID)
     * M381 - Disable all solenoids. (Requires EXT_SOLENOID)
     * M400 - Finish all moves.
     * M401 - Lower Z probe. (Requires a probe)
     * M402 - Raise Z probe. (Requires a probe)
     * M404 - Display or set the Nominal Filament Width: "W<diameter>". (Requires FILAMENT_WIDTH_SENSOR)
     * M405 - Enable Filament Sensor flow control. "M405 D<delay_cm>". (Requires FILAMENT_WIDTH_SENSOR)
     * M406 - Disable Filament Sensor flow control. (Requires FILAMENT_WIDTH_SENSOR)
     * M407 - Display measured filament diameter in millimeters. (Requires FILAMENT_WIDTH_SENSOR)
     * M410 - Quickstop. Abort all planned moves.
    
     * M420 - Enable/Disable Leveling (with current values) S1=enable S0=disable (Requires MESH_BED_LEVELING or ABL)
    
     * M421 - Set a single Z coordinate in the Mesh Leveling grid. X<units> Y<units> Z<units> (Requires MESH_BED_LEVELING or AUTO_BED_LEVELING_UBL)
    
     * M428 - Set the home_offset based on the current_position. Nearest edge applies.
     * M500 - Store parameters in EEPROM. (Requires EEPROM_SETTINGS)
     * M501 - Restore parameters from EEPROM. (Requires EEPROM_SETTINGS)
     * M502 - Revert to the default "factory settings". ** Does not write them to EEPROM! **
     * M503 - Print the current settings (in memory): "M503 S<verbose>". S0 specifies compact output.
     * M540 - Enable/disable SD card abort on endstop hit: "M540 S<state>". (Requires ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED)
     * M600 - Pause for filament change: "M600 X<pos> Y<pos> Z<raise> E<first_retract> L<later_retract>". (Requires FILAMENT_CHANGE_FEATURE)
    
     * M665 - Set delta configurations: "M665 L<diagonal rod> R<delta radius> S<segments/s> A<rod A trim mm> B<rod B trim mm> C<rod C trim mm> I<tower A trim angle> J<tower B trim angle> K<tower C trim angle>" (Requires DELTA)
    
     * M666 - Set delta endstop adjustment. (Requires DELTA)
     * M605 - Set dual x-carriage movement mode: "M605 S<mode> [X<x_offset>] [R<temp_offset>]". (Requires DUAL_X_CARRIAGE)
     * M851 - Set Z probe's Z offset in current units. (Negative = below the nozzle.)
    
     * M906 - Set or get motor current in milliamps using axis codes X, Y, Z, E. Report values if no axis codes given. (Requires HAVE_TMC2130)
    
     * M907 - Set digital trimpot motor current using axis codes. (Requires a board with digital trimpots)
     * M908 - Control digital trimpot directly. (Requires DAC_STEPPER_CURRENT or DIGIPOTSS_PIN)
     * M909 - Print digipot/DAC current value. (Requires DAC_STEPPER_CURRENT)
     * M910 - Commit digipot/DAC value to external EEPROM via I2C. (Requires DAC_STEPPER_CURRENT)
    
     * M911 - Report stepper driver overtemperature pre-warn condition. (Requires HAVE_TMC2130)
     * M912 - Clear stepper driver overtemperature pre-warn condition flag. (Requires HAVE_TMC2130)
    
     * M350 - Set microstepping mode. (Requires digital microstepping pins.)
     * M351 - Toggle MS1 MS2 pins directly. (Requires digital microstepping pins.)
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     *
     * M360 - SCARA calibration: Move to cal-position ThetaA (0 deg calibration)
     * M361 - SCARA calibration: Move to cal-position ThetaB (90 deg calibration - steps per degree)
     * M362 - SCARA calibration: Move to cal-position PsiA (0 deg calibration)
     * M363 - SCARA calibration: Move to cal-position PsiB (90 deg calibration - steps per degree)
     * M364 - SCARA calibration: Move to cal-position PSIC (90 deg to Theta calibration position)
     *
    
     * ************ Custom codes - This can change to suit future G-code regulations
    
     * M100 - Watch Free Memory (For Debugging). (Requires M100_FREE_MEMORY_WATCHER)
     * M928 - Start SD logging: "M928 filename.gco". Stop with M29. (Requires SDSUPPORT)
    
    Scott Lahteine's avatar
    Scott Lahteine committed
     * M999 - Restart after being stopped by error
    
     * T0-T3 - Select an extruder (tool) by index: "T<n> F<units/min>"
    
    #include "Marlin.h"
    
    #include "ultralcd.h"
    #include "planner.h"
    #include "stepper.h"
    #include "endstops.h"
    #include "temperature.h"
    #include "cardreader.h"
    #include "configuration_store.h"
    #include "language.h"
    #include "pins_arduino.h"
    #include "math.h"
    #include "nozzle.h"
    #include "duration_t.h"
    #include "types.h"
    
    #if HAS_ABL
      #include "vector_3.h"
      #if ENABLED(AUTO_BED_LEVELING_LINEAR)
        #include "qr_solve.h"
      #endif
    #elif ENABLED(MESH_BED_LEVELING)
      #include "mesh_bed_leveling.h"
    #endif
    
    #if ENABLED(BEZIER_CURVE_SUPPORT)
      #include "planner_bezier.h"
    #endif
    
    #if HAS_BUZZER && DISABLED(LCD_USE_I2C_BUZZER)
      #include "buzzer.h"
    #endif
    
    #if ENABLED(USE_WATCHDOG)
      #include "watchdog.h"
    #endif
    
    #if ENABLED(BLINKM)
      #include "blinkm.h"
      #include "Wire.h"
    #endif
    
    #if HAS_SERVOS
      #include "servo.h"
    #endif
    
    #if HAS_DIGIPOTSS
      #include <SPI.h>
    #endif
    
    #if ENABLED(DAC_STEPPER_CURRENT)
      #include "stepper_dac.h"
    #endif
    
    #if ENABLED(EXPERIMENTAL_I2CBUS)
      #include "twibus.h"
    #endif
    
    #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
      #include "endstop_interrupts.h"
    #endif
    
    
    #if ENABLED(M100_FREE_MEMORY_WATCHER)
    
    Richard Wackerbarth's avatar
    Richard Wackerbarth committed
      void gcode_M100();
    
    Richard Wackerbarth's avatar
    Richard Wackerbarth committed
    #endif
    
    
    Scott Lahteine's avatar
    Scott Lahteine committed
      CardReader card;
    
    #if ENABLED(EXPERIMENTAL_I2CBUS)
      TWIBus i2c;
    #endif
    
    
    #if ENABLED(G38_PROBE_TARGET)
    
    Scott Lahteine's avatar
    Scott Lahteine committed
      bool G38_move = false,
           G38_endstop_hit = false;
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    #if ENABLED(AUTO_BED_LEVELING_UBL)
    
      #include "ubl.h"
    
      unified_bed_leveling ubl;
    
      #define UBL_MESH_VALID !( ( ubl.z_values[0][0] == ubl.z_values[0][1] && ubl.z_values[0][1] == ubl.z_values[0][2] \
                               && ubl.z_values[1][0] == ubl.z_values[1][1] && ubl.z_values[1][1] == ubl.z_values[1][2] \
                               && ubl.z_values[2][0] == ubl.z_values[2][1] && ubl.z_values[2][1] == ubl.z_values[2][2] \
                               && ubl.z_values[0][0] == 0 && ubl.z_values[1][0] == 0 && ubl.z_values[2][0] == 0 )  \
                               || isnan(ubl.z_values[0][0]))
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    #endif
    
    
    bool Running = true;
    
    
    uint8_t marlin_debug_flags = DEBUG_NONE;
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    
    
    /**
     * Cartesian Current Position
     *   Used to track the logical position as moves are queued.
     *   Used by 'line_to_current_position' to do a move after changing it.
     *   Used by 'SYNC_PLAN_POSITION_KINEMATIC' to update 'planner.position'.
     */
    float current_position[XYZE] = { 0.0 };
    
    /**
     * Cartesian Destination
     *   A temporary position, usually applied to 'current_position'.
     *   Set with 'gcode_get_destination' or 'set_destination_to_current'.
     *   'line_to_destination' sets 'current_position' to 'destination'.
     */
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    float destination[XYZE] = { 0.0 };
    
    
    /**
     * axis_homed
     *   Flags that each linear axis was homed.
     *   XYZ on cartesian, ABC on delta, ABZ on SCARA.
     *
     * axis_known_position
     *   Flags that the position is known in each linear axis. Set when homed.
     *   Cleared whenever a stepper powers off, potentially losing its position.
     */
    bool axis_homed[XYZ] = { false }, axis_known_position[XYZ] = { false };
    
    /**
     * GCode line number handling. Hosts may opt to include line numbers when
     * sending commands to Marlin, and lines will be checked for sequentiality.
    
    AnHardt's avatar
    AnHardt committed
     * M110 N<int> sets the current line number.
    
    static long gcode_N, gcode_LastN, Stopped_gcode_LastN = 0;
    
    /**
     * GCode Command Queue
     * A simple ring buffer of BUFSIZE command strings.
     *
     * Commands are copied into this buffer by the command injectors
     * (immediate, serial, sd card) and they are processed sequentially by
     * the main loop. The process_next_command function parses the next
     * command and hands off execution to individual handler functions.
     */
    
    uint8_t commands_in_queue = 0; // Count of commands in the queue
    
    static uint8_t cmd_queue_index_r = 0, // Ring buffer read position
    
                   cmd_queue_index_w = 0; // Ring buffer write position
    static char command_queue[BUFSIZE][MAX_CMD_SIZE];
    
    
    /**
     * Current GCode Command
     * When a GCode handler is running, these will be set
     */
    static char *current_command,      // The command currently being executed
                *current_command_args, // The address where arguments begin
                *seen_pointer;         // Set by code_seen(), used by the code_value functions
    
    /**
     * Next Injected Command pointer. NULL if no commands are being injected.
     * Used by Marlin internally to ensure that commands initiated from within
     * are enqueued ahead of any pending serial or sd card commands.
     */
    static const char *injected_commands_P = NULL;
    
    #if ENABLED(INCH_MODE_SUPPORT)
    
      float linear_unit_factor = 1.0, volumetric_unit_factor = 1.0;
    
    #if ENABLED(TEMPERATURE_UNITS_SUPPORT)
      TempUnit input_temp_units = TEMPUNIT_C;
    #endif
    
    
    /**
     * Feed rates are often configured with mm/m
     * but the planner and stepper like mm/s units.
     */
    
    float constexpr homing_feedrate_mm_s[] = {
    
      #if ENABLED(DELTA)
    
        MMM_TO_MMS(HOMING_FEEDRATE_Z), MMM_TO_MMS(HOMING_FEEDRATE_Z),
    
        MMM_TO_MMS(HOMING_FEEDRATE_XY), MMM_TO_MMS(HOMING_FEEDRATE_XY),
    
      MMM_TO_MMS(HOMING_FEEDRATE_Z), 0
    
    static float feedrate_mm_s = MMM_TO_MMS(1500.0), saved_feedrate_mm_s;
    
    int feedrate_percentage = 100, saved_feedrate_percentage,
        flow_percentage[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(100);
    
    bool axis_relative_modes[] = AXIS_RELATIVE_MODES,
    
         volumetric_enabled =
    
            #if ENABLED(VOLUMETRIC_DEFAULT_ON)
              true
            #else
              false
            #endif
    
    float filament_size[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(DEFAULT_NOMINAL_FILAMENT_DIA),
          volumetric_multiplier[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(1.0);
    
    #if DISABLED(NO_WORKSPACE_OFFSETS)
    
      // The distance that XYZ has been offset by G92. Reset by G28.
      float position_shift[XYZ] = { 0 };
    
      // This offset is added to the configured home position.
      // Set by M206, M428, or menu item. Saved to EEPROM.
      float home_offset[XYZ] = { 0 };
    
    
      // The above two are combined to save on computes
      float workspace_offset[XYZ] = { 0 };
    
    
    // Software Endstops are based on the configured limits.
    
    #if HAS_SOFTWARE_ENDSTOPS
    
      bool soft_endstops_enabled = true;
    #endif
    float soft_endstop_min[XYZ] = { X_MIN_POS, Y_MIN_POS, Z_MIN_POS },
          soft_endstop_max[XYZ] = { X_MAX_POS, Y_MAX_POS, Z_MAX_POS };
    
    #if FAN_COUNT > 0
      int fanSpeeds[FAN_COUNT] = { 0 };
    #endif
    
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    // The active extruder (tool). Set with T<extruder> command.
    
    uint8_t active_extruder = 0;
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    
    // Relative Mode. Enable with G91, disable with G90.
    static bool relative_mode = false;
    
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    // For M109 and M190, this flag may be cleared (by M108) to exit the wait loop
    
    volatile bool wait_for_heatup = true;
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    // For M0/M1, this flag may be cleared (by M108) to exit the wait-for-user loop
    
    #if HAS_RESUME_CONTINUE
    
      volatile bool wait_for_user = false;
    
    const char axis_codes[XYZE] = {'X', 'Y', 'Z', 'E'};
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    // Number of characters read in the current line of serial input
    
    static int serial_count = 0;
    
    // Inactivity shutdown
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    millis_t previous_cmd_ms = 0;
    static millis_t max_inactive_time = 0;
    
    static millis_t stepper_inactive_time = (DEFAULT_STEPPER_DEACTIVE_TIME) * 1000UL;
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    
    // Print Job Timer
    
    João Brázio's avatar
    João Brázio committed
    #if ENABLED(PRINTCOUNTER)
      PrintCounter print_job_timer = PrintCounter();
    #else
      Stopwatch print_job_timer = Stopwatch();
    #endif
    
    // Buzzer - I2C on the LCD or a BEEPER_PIN
    #if ENABLED(LCD_USE_I2C_BUZZER)
      #define BUZZ(d,f) lcd_buzz(d, f)
    
    #elif PIN_EXISTS(BEEPER)
    
      Buzzer buzzer;
      #define BUZZ(d,f) buzzer.tone(d, f)
    #else
      #define BUZZ(d,f) NOOP
    
    João Brázio's avatar
    João Brázio committed
    #endif
    
    
    static uint8_t target_extruder;
    
    #if HAS_BED_PROBE
      float zprobe_zoffset = Z_PROBE_OFFSET_FROM_EXTRUDER;
    #endif
    
    
    #define PLANNER_XY_FEEDRATE() (min(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS]))
    
      float xy_probe_feedrate_mm_s = MMM_TO_MMS(XY_PROBE_SPEED);
      #define XY_PROBE_FEEDRATE_MM_S xy_probe_feedrate_mm_s
    
    #elif defined(XY_PROBE_SPEED)
    
      #define XY_PROBE_FEEDRATE_MM_S MMM_TO_MMS(XY_PROBE_SPEED)
    
      #define XY_PROBE_FEEDRATE_MM_S PLANNER_XY_FEEDRATE()
    
    #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
    
      #if ENABLED(DELTA)
        #define ADJUST_DELTA(V) \
          if (planner.abl_enabled) { \
            const float zadj = bilinear_z_offset(V); \
            delta[A_AXIS] += zadj; \
            delta[B_AXIS] += zadj; \
            delta[C_AXIS] += zadj; \
          }
      #else
        #define ADJUST_DELTA(V) if (planner.abl_enabled) { delta[Z_AXIS] += bilinear_z_offset(V); }
      #endif
    
    #elif IS_KINEMATIC
      #define ADJUST_DELTA(V) NOOP
    #endif
    
    
    #if ENABLED(Z_DUAL_ENDSTOPS)
    
      float z_endstop_adj =
        #ifdef Z_DUAL_ENDSTOPS_ADJUSTMENT
          Z_DUAL_ENDSTOPS_ADJUSTMENT
        #else
          0
        #endif
      ;
    
    // Extruder offsets
    
    #if HOTENDS > 1
    
      float hotend_offset[XYZ][HOTENDS];
    
    #if HAS_Z_SERVO_ENDSTOP
    
      const int z_servo_angle[2] = Z_SERVO_ANGLES;
    
      int baricuda_valve_pressure = 0;
      int baricuda_e_to_p_pressure = 0;
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    
      bool autoretract_enabled = false;
    
      bool retracted[EXTRUDERS] = { false };
      bool retracted_swap[EXTRUDERS] = { false };
    
      float retract_length = RETRACT_LENGTH;
    
      float retract_length_swap = RETRACT_LENGTH_SWAP;
    
    Scott Lahteine's avatar
    Scott Lahteine committed
      float retract_feedrate_mm_s = RETRACT_FEEDRATE;
    
      float retract_zlift = RETRACT_ZLIFT;
      float retract_recover_length = RETRACT_RECOVER_LENGTH;
    
      float retract_recover_length_swap = RETRACT_RECOVER_LENGTH_SWAP;
    
      float retract_recover_feedrate_mm_s = RETRACT_RECOVER_FEEDRATE;
    
    MagoKimbra's avatar
    MagoKimbra committed
    #endif // FWRETRACT
    
    #if ENABLED(ULTIPANEL) && HAS_POWER_SWITCH
    
      bool powersupply =
    
        #if ENABLED(PS_DEFAULT_OFF)
    
    Scott Lahteine's avatar
    Scott Lahteine committed
          false
        #else
    
    Scott Lahteine's avatar
    Scott Lahteine committed
        #endif
      ;
    
    #if HAS_CASE_LIGHT
    
    Kai's avatar
    Kai committed
      bool case_light_on =
        #if ENABLED(CASE_LIGHT_DEFAULT_ON)
          true
        #else
          false
        #endif
      ;
    #endif
    
    
      float delta[ABC],
            endstop_adj[ABC] = { 0 };
    
    
      // These values are loaded or reset at boot time when setup() calls
    
      // settings.load(), which calls recalc_delta_settings().
    
      float delta_radius,
            delta_tower_angle_trim[ABC],
            delta_tower[ABC][2],
            delta_diagonal_rod,
            delta_diagonal_rod_trim[ABC],
            delta_diagonal_rod_2_tower[ABC],
            delta_segments_per_second,
    
            delta_clip_start_height = Z_MAX_POS;
    
    
      float delta_safe_distance_from_top();
    
    #if ENABLED(AUTO_BED_LEVELING_BILINEAR)
    
    Scott Lahteine's avatar
    Scott Lahteine committed
      int bilinear_grid_spacing[2], bilinear_start[2];
    
      float bed_level_grid[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
    
    #if IS_SCARA
      // Float constants for SCARA calculations
      const float L1 = SCARA_LINKAGE_1, L2 = SCARA_LINKAGE_2,
                  L1_2 = sq(float(L1)), L1_2_2 = 2.0 * L1_2,
                  L2_2 = sq(float(L2));
    
    
      float delta_segments_per_second = SCARA_SEGMENTS_PER_SECOND,
    
            delta[ABC];
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    float cartes[XYZ] = { 0 };
    
    
    #if ENABLED(FILAMENT_WIDTH_SENSOR)
    
      bool filament_sensor = false;  //M405 turns on filament_sensor control, M406 turns it off
    
      float filament_width_nominal = DEFAULT_NOMINAL_FILAMENT_DIA,  // Nominal filament width. Change with M404
            filament_width_meas = DEFAULT_MEASURED_FILAMENT_DIA;    // Measured filament diameter
      int8_t measurement_delay[MAX_MEASUREMENT_DELAY + 1]; // Ring buffer to delayed measurement. Store extruder factor after subtracting 100
      int filwidth_delay_index[2] = { 0, -1 };  // Indexes into ring buffer
    
      int meas_delay_cm = MEASUREMENT_DELAY_CM;  //distance delay setting
    #endif
    
    
    #if ENABLED(FILAMENT_RUNOUT_SENSOR)
    
      static bool filament_ran_out = false;
    
    #if ENABLED(FILAMENT_CHANGE_FEATURE)
      FilamentChangeMenuResponse filament_change_menu_response;
    #endif
    
    
    #if ENABLED(MIXING_EXTRUDER)
    
      float mixing_factor[MIXING_STEPPERS]; // Reciprocal of mix proportion. 0.0 = off, otherwise >= 1.0.
    
      #if MIXING_VIRTUAL_TOOLS > 1
        float mixing_virtual_tool_mix[MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS];
      #endif
    #endif
    
    
    static bool send_ok[BUFSIZE];
    
      Servo servo[NUM_SERVOS];
    
      #define MOVE_SERVO(I, P) servo[I].move(P)
    
      #if HAS_Z_SERVO_ENDSTOP
        #define DEPLOY_Z_SERVO() MOVE_SERVO(Z_ENDSTOP_SERVO_NR, z_servo_angle[0])
        #define STOW_Z_SERVO() MOVE_SERVO(Z_ENDSTOP_SERVO_NR, z_servo_angle[1])
      #endif
    
    blddk's avatar
    blddk committed
    #ifdef CHDK
    
      millis_t chdkHigh = 0;
    
      bool chdkActive = false;
    
    blddk's avatar
    blddk committed
    #endif
    
    
    #if ENABLED(PID_EXTRUSION_SCALING)
    
      int lpq_len = 20;
    #endif
    
    
    #if ENABLED(HOST_KEEPALIVE_FEATURE)
    
      MarlinBusyState busy_state = NOT_BUSY;
    
      static millis_t next_busy_signal_ms = 0;
    
      uint8_t host_keepalive_interval = DEFAULT_KEEPALIVE_INTERVAL;
    
      #define host_keepalive() NOOP
    #endif
    
    static inline float pgm_read_any(const float *p) { return pgm_read_float_near(p); }
    static inline signed char pgm_read_any(const signed char *p) { return pgm_read_byte_near(p); }
    
    
    #define XYZ_CONSTS_FROM_CONFIG(type, array, CONFIG) \
    
      static const PROGMEM type array##_P[XYZ] = { X_##CONFIG, Y_##CONFIG, Z_##CONFIG }; \
      static inline type array(AxisEnum axis) { return pgm_read_any(&array##_P[axis]); }
    
    Josef Pavlik's avatar
    Josef Pavlik committed
    XYZ_CONSTS_FROM_CONFIG(float, base_min_pos,   MIN_POS)
    XYZ_CONSTS_FROM_CONFIG(float, base_max_pos,   MAX_POS)
    XYZ_CONSTS_FROM_CONFIG(float, base_home_pos,  HOME_POS)
    XYZ_CONSTS_FROM_CONFIG(float, max_length,     MAX_LENGTH)
    XYZ_CONSTS_FROM_CONFIG(float, home_bump_mm,   HOME_BUMP_MM)
    XYZ_CONSTS_FROM_CONFIG(signed char, home_dir, HOME_DIR)
    
    jbrazio's avatar
    jbrazio committed
    /**
     * ***************************************************************************
     * ******************************** FUNCTIONS ********************************
     * ***************************************************************************
     */
    
    void stop();
    
    
    void get_available_commands();
    
    void process_next_command();
    
    void prepare_move_to_destination();
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    
    void get_cartesian_from_steppers();
    
    void set_current_from_steppers_for_axis(const AxisEnum axis);
    
    #if ENABLED(ARC_SUPPORT)
    
      void plan_arc(float target[XYZE], float* offset, uint8_t clockwise);
    
    #if ENABLED(BEZIER_CURVE_SUPPORT)
      void plan_cubic_move(const float offset[4]);
    #endif
    
    
    void tool_change(const uint8_t tmp_extruder, const float fr_mm_s=0.0, bool no_move=false);
    
    static void report_current_position();
    
    #if ENABLED(DEBUG_LEVELING_FEATURE)
    
      void print_xyz(const char* prefix, const char* suffix, const float x, const float y, const float z) {
        serialprintPGM(prefix);
    
        SERIAL_ECHOPAIR("(", x);
    
        SERIAL_ECHOPAIR(", ", y);
        SERIAL_ECHOPAIR(", ", z);
    
        SERIAL_CHAR(')');
    
    
        if (suffix) serialprintPGM(suffix);
        else SERIAL_EOL;
    
    
      void print_xyz(const char* prefix, const char* suffix, const float xyz[]) {
    
        print_xyz(prefix, suffix, xyz[X_AXIS], xyz[Y_AXIS], xyz[Z_AXIS]);
    
        void print_xyz(const char* prefix, const char* suffix, const vector_3 &xyz) {
    
          print_xyz(prefix, suffix, xyz.x, xyz.y, xyz.z);
    
        }
      #endif
    
    
      #define DEBUG_POS(SUFFIX,VAR) do { \
    
        print_xyz(PSTR("  " STRINGIFY(VAR) "="), PSTR(" : " SUFFIX "\n"), VAR); } while(0)
    
    /**
     * sync_plan_position
    
     *
     * Set the planner/stepper positions directly from current_position with
     * no kinematic translation. Used for homing axes and cartesian/core syncing.
    
     */
    inline void sync_plan_position() {
      #if ENABLED(DEBUG_LEVELING_FEATURE)
        if (DEBUGGING(LEVELING)) DEBUG_POS("sync_plan_position", current_position);
      #endif
      planner.set_position_mm(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
    }
    inline void sync_plan_position_e() { planner.set_e_position_mm(current_position[E_AXIS]); }
    
    
    #if IS_KINEMATIC
    
      inline void sync_plan_position_kinematic() {
    
        #if ENABLED(DEBUG_LEVELING_FEATURE)
    
          if (DEBUGGING(LEVELING)) DEBUG_POS("sync_plan_position_kinematic", current_position);
    
        planner.set_position_mm_kinematic(current_position);
    
      #define SYNC_PLAN_POSITION_KINEMATIC() sync_plan_position_kinematic()
    
      #define SYNC_PLAN_POSITION_KINEMATIC() sync_plan_position()
    
      #include "SdFatUtil.h"
      int freeMemory() { return SdFatUtil::FreeRam(); }
    #else
    
      extern char __bss_end;
      extern char __heap_start;
    
      extern void* __brkval;
    
      int freeMemory() {
        int free_memory;
        if ((int)__brkval == 0)
          free_memory = ((int)&free_memory) - ((int)&__bss_end);
        else
          free_memory = ((int)&free_memory) - ((int)__brkval);
        return free_memory;
    
    #if ENABLED(DIGIPOT_I2C)
      extern void digipot_i2c_set_current(int channel, float current);
      extern void digipot_i2c_init();
    #endif
    
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    /**
    
     * Inject the next "immediate" command, when possible, onto the front of the queue.
    
     * Return true if any immediate commands remain to inject.
    
    static bool drain_injected_commands_P() {
      if (injected_commands_P != NULL) {
    
        char c, cmd[30];
    
        strncpy_P(cmd, injected_commands_P, sizeof(cmd) - 1);
    
        cmd[sizeof(cmd) - 1] = '\0';
        while ((c = cmd[i]) && c != '\n') i++; // find the end of this gcode command
        cmd[i] = '\0';
    
        if (enqueue_and_echo_command(cmd))     // success?
    
          injected_commands_P = c ? injected_commands_P + i + 1 : NULL; // next command or done
    
      return (injected_commands_P != NULL);    // return whether any more remain
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    /**
     * Record one or many commands to run from program memory.
     * Aborts the current queue, if any.
    
     * Note: drain_injected_commands_P() must be called repeatedly to drain the commands afterwards
    
    void enqueue_and_echo_commands_P(const char* pgcode) {
    
      injected_commands_P = pgcode;
      drain_injected_commands_P(); // first command executed asap (when possible)
    
    /**
     * Clear the Marlin command queue
     */
    
    void clear_command_queue() {
      cmd_queue_index_r = cmd_queue_index_w;
      commands_in_queue = 0;
    }
    
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    /**
    
     * Once a new command is in the ring buffer, call this to commit it
    
    inline void _commit_command(bool say_ok) {
      send_ok[cmd_queue_index_w] = say_ok;
    
    Scott Lahteine's avatar
    Scott Lahteine committed
      cmd_queue_index_w = (cmd_queue_index_w + 1) % BUFSIZE;
      commands_in_queue++;
    
     * Copy a command from RAM into the main command buffer.
     * Return true if the command was successfully added.
     * Return false for a full buffer, or if the 'command' is a comment.
    
     */
    inline bool _enqueuecommand(const char* cmd, bool say_ok=false) {
      if (*cmd == ';' || commands_in_queue >= BUFSIZE) return false;
      strcpy(command_queue[cmd_queue_index_w], cmd);
      _commit_command(say_ok);
    
    /**
     * Enqueue with Serial Echo
     */
    bool enqueue_and_echo_command(const char* cmd, bool say_ok/*=false*/) {
      if (_enqueuecommand(cmd, say_ok)) {
    
        SERIAL_ECHO_START;
        SERIAL_ECHOPAIR(MSG_ENQUEUEING, cmd);
        SERIAL_CHAR('"');
        SERIAL_EOL;
    
        return true;
      }
      return false;
    }
    
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    void setup_killpin() {
    
      #if HAS_KILL
    
        SET_INPUT_PULLUP(KILL_PIN);
    
    #if ENABLED(FILAMENT_RUNOUT_SENSOR)
    
      void setup_filrunoutpin() {
    
        #if ENABLED(ENDSTOPPULLUP_FIL_RUNOUT)
    
          SET_INPUT_PULLUP(FIL_RUNOUT_PIN);
        #else
          SET_INPUT(FIL_RUNOUT_PIN);
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    void setup_homepin(void) {
    
      #if HAS_HOME
    
        SET_INPUT_PULLUP(HOME_PIN);
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    void setup_powerhold() {
    
      #if HAS_SUICIDE
    
        OUT_WRITE(SUICIDE_PIN, HIGH);
    
      #if HAS_POWER_SWITCH
    
        #if ENABLED(PS_DEFAULT_OFF)
    
          OUT_WRITE(PS_ON_PIN, PS_ON_ASLEEP);
    
          OUT_WRITE(PS_ON_PIN, PS_ON_AWAKE);
        #endif
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    void suicide() {
    
      #if HAS_SUICIDE
    
        OUT_WRITE(SUICIDE_PIN, LOW);
    
    Scott Lahteine's avatar
    Scott Lahteine committed
    void servo_init() {
    
      #if NUM_SERVOS >= 1 && HAS_SERVO_0
    
        servo[0].attach(SERVO0_PIN);
    
    AnHardt's avatar
    AnHardt committed
        servo[0].detach(); // Just set up the pin. We don't have a position yet. Don't move to a random position.
    
      #if NUM_SERVOS >= 2 && HAS_SERVO_1
    
        servo[1].attach(SERVO1_PIN);
    
    AnHardt's avatar
    AnHardt committed
        servo[1].detach();
    
      #if NUM_SERVOS >= 3 && HAS_SERVO_2
    
        servo[2].attach(SERVO2_PIN);
    
    AnHardt's avatar
    AnHardt committed
        servo[2].detach();
    
      #if NUM_SERVOS >= 4 && HAS_SERVO_3
    
        servo[3].attach(SERVO3_PIN);
    
    AnHardt's avatar
    AnHardt committed
        servo[3].detach();
    
      #if HAS_Z_SERVO_ENDSTOP
    
    AnHardt's avatar
    AnHardt committed
        /**
    
         * Set position of Z Servo Endstop
    
    AnHardt's avatar
    AnHardt committed
         *
         * The servo might be deployed and positioned too low to stow
         * when starting up the machine or rebooting the board.
         * There's no way to know where the nozzle is positioned until
         * homing has been done - no homing with z-probe without init!
         *
         */
    
        STOW_Z_SERVO();
    
    /**
     * Stepper Reset (RigidBoard, et.al.)
     */
    #if HAS_STEPPER_RESET
      void disableStepperDrivers() {
    
        OUT_WRITE(STEPPER_RESET_PIN, LOW);  // drive it down to hold in reset motor driver chips
    
      void enableStepperDrivers() { SET_INPUT(STEPPER_RESET_PIN); }  // set to input, which allows it to be pulled high by pullups
    
    #if ENABLED(EXPERIMENTAL_I2CBUS) && I2C_SLAVE_ADDRESS > 0
    
    
      void i2c_on_receive(int bytes) { // just echo all bytes received to serial
        i2c.receive(bytes);
      }
    
      void i2c_on_request() {          // just send dummy data for now
    
        i2c.reply("Hello World!\n");
    
      void set_led_color(
        const uint8_t r, const uint8_t g, const uint8_t b
          #if ENABLED(RGBW_LED)
            , const uint8_t w=0
          #endif
      ) {
    
    
        #if ENABLED(BLINKM)
    
          // This variant uses i2c to send the RGB components to the device.
          SendColors(r, g, b);
    
        #else
    
          // This variant uses 3 separate pins for the RGB components.
          // If the pins can do PWM then their intensity will be set.
    
          WRITE(RGB_LED_R_PIN, r ? HIGH : LOW);
          WRITE(RGB_LED_G_PIN, g ? HIGH : LOW);
          WRITE(RGB_LED_B_PIN, b ? HIGH : LOW);
    
          analogWrite(RGB_LED_R_PIN, r);
          analogWrite(RGB_LED_G_PIN, g);
          analogWrite(RGB_LED_B_PIN, b);
    
    
          #if ENABLED(RGBW_LED)
    
            WRITE(RGB_LED_W_PIN, w ? HIGH : LOW);
    
            analogWrite(RGB_LED_W_PIN, w);
          #endif
    
    
    void gcode_line_error(const char* err, bool doFlush = true) {
    
      SERIAL_ERROR_START;
      serialprintPGM(err);
      SERIAL_ERRORLN(gcode_LastN);
      //Serial.println(gcode_N);
      if (doFlush) FlushSerialRequestResend();
      serial_count = 0;
    }
    
    
    /**
     * Get all commands waiting on the serial port and queue them.
     * Exit when the buffer is full or when no more characters are
     * left on the serial port.
     */
    
    inline void get_serial_commands() {
    
      static char serial_line_buffer[MAX_CMD_SIZE];
    
      static bool serial_comment_mode = false;