Added automated extruder fan function

This commit is contained in:
blddk 2013-03-22 18:49:05 +01:00
parent 72419f69a9
commit e5a853a3d0

View file

@ -1,2132 +1,2161 @@
/* -*- c++ -*- */ /* -*- c++ -*- */
/* /*
Reprap firmware based on Sprinter and grbl. Reprap firmware based on Sprinter and grbl.
Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* /*
This firmware is a mashup between Sprinter and grbl. This firmware is a mashup between Sprinter and grbl.
(https://github.com/kliment/Sprinter) (https://github.com/kliment/Sprinter)
(https://github.com/simen/grbl/tree) (https://github.com/simen/grbl/tree)
It has preliminary support for Matthew Roberts advance algorithm It has preliminary support for Matthew Roberts advance algorithm
http://reprap.org/pipermail/reprap-dev/2011-May/003323.html http://reprap.org/pipermail/reprap-dev/2011-May/003323.html
*/ */
#include "Marlin.h" #include "Marlin.h"
#include "ultralcd.h" #include "ultralcd.h"
#include "planner.h" #include "planner.h"
#include "stepper.h" #include "stepper.h"
#include "temperature.h" #include "temperature.h"
#include "motion_control.h" #include "motion_control.h"
#include "cardreader.h" #include "cardreader.h"
#include "watchdog.h" #include "watchdog.h"
#include "ConfigurationStore.h" #include "ConfigurationStore.h"
#include "language.h" #include "language.h"
#include "pins_arduino.h" #include "pins_arduino.h"
#if DIGIPOTSS_PIN > -1 #if DIGIPOTSS_PIN > -1
#include <SPI.h> #include <SPI.h>
#endif #endif
#define VERSION_STRING "1.0.0" #define VERSION_STRING "1.0.0"
// look here for descriptions of gcodes: http://linuxcnc.org/handbook/gcode/g-code.html // look here for descriptions of gcodes: http://linuxcnc.org/handbook/gcode/g-code.html
// http://objects.reprap.org/wiki/Mendel_User_Manual:_RepRapGCodes // http://objects.reprap.org/wiki/Mendel_User_Manual:_RepRapGCodes
//Implemented Codes //Implemented Codes
//------------------- //-------------------
// G0 -> G1 // G0 -> G1
// G1 - Coordinated Movement X Y Z E // G1 - Coordinated Movement X Y Z E
// G2 - CW ARC // G2 - CW ARC
// G3 - CCW ARC // G3 - CCW ARC
// G4 - Dwell S<seconds> or P<milliseconds> // G4 - Dwell S<seconds> or P<milliseconds>
// G10 - retract filament according to settings of M207 // G10 - retract filament according to settings of M207
// G11 - retract recover filament according to settings of M208 // G11 - retract recover filament according to settings of M208
// G28 - Home all Axis // G28 - Home all Axis
// G90 - Use Absolute Coordinates // G90 - Use Absolute Coordinates
// G91 - Use Relative Coordinates // G91 - Use Relative Coordinates
// G92 - Set current position to cordinates given // G92 - Set current position to cordinates given
//RepRap M Codes //RepRap M Codes
// M0 - Unconditional stop - Wait for user to press a button on the LCD (Only if ULTRA_LCD is enabled) // M0 - Unconditional stop - Wait for user to press a button on the LCD (Only if ULTRA_LCD is enabled)
// M1 - Same as M0 // M1 - Same as M0
// M104 - Set extruder target temp // M104 - Set extruder target temp
// M105 - Read current temp // M105 - Read current temp
// M106 - Fan on // M106 - Fan on
// M107 - Fan off // M107 - Fan off
// M109 - Wait for extruder current temp to reach target temp. // M109 - Wait for extruder current temp to reach target temp.
// M114 - Display current position // M114 - Display current position
//Custom M Codes //Custom M Codes
// M17 - Enable/Power all stepper motors // M17 - Enable/Power all stepper motors
// M18 - Disable all stepper motors; same as M84 // M18 - Disable all stepper motors; same as M84
// M20 - List SD card // M20 - List SD card
// M21 - Init SD card // M21 - Init SD card
// M22 - Release SD card // M22 - Release SD card
// M23 - Select SD file (M23 filename.g) // M23 - Select SD file (M23 filename.g)
// M24 - Start/resume SD print // M24 - Start/resume SD print
// M25 - Pause SD print // M25 - Pause SD print
// M26 - Set SD position in bytes (M26 S12345) // M26 - Set SD position in bytes (M26 S12345)
// M27 - Report SD print status // M27 - Report SD print status
// M28 - Start SD write (M28 filename.g) // M28 - Start SD write (M28 filename.g)
// M29 - Stop SD write // M29 - Stop SD write
// M30 - Delete file from SD (M30 filename.g) // M30 - Delete file from SD (M30 filename.g)
// M31 - Output time since last M109 or SD card start to serial // M31 - Output time since last M109 or SD card start to serial
// M42 - Change pin status via gcode Use M42 Px Sy to set pin x to value y, when omitting Px the onboard led will be used. // M42 - Change pin status via gcode Use M42 Px Sy to set pin x to value y, when omitting Px the onboard led will be used.
// M80 - Turn on Power Supply // M80 - Turn on Power Supply
// M81 - Turn off Power Supply // M81 - Turn off Power Supply
// M82 - Set E codes absolute (default) // M82 - Set E codes absolute (default)
// M83 - Set E codes relative while in Absolute Coordinates (G90) mode // M83 - Set E codes relative while in Absolute Coordinates (G90) mode
// M84 - Disable steppers until next move, // M84 - Disable steppers until next move,
// or use S<seconds> to specify an inactivity timeout, after which the steppers will be disabled. S0 to disable the timeout. // or use S<seconds> to specify an inactivity timeout, after which the steppers will be disabled. S0 to disable the timeout.
// M85 - Set inactivity shutdown timer with parameter S<seconds>. To disable set zero (default) // M85 - Set inactivity shutdown timer with parameter S<seconds>. To disable set zero (default)
// M92 - Set axis_steps_per_unit - same syntax as G92 // M92 - Set axis_steps_per_unit - same syntax as G92
// M114 - Output current position to serial port // M114 - Output current position to serial port
// M115 - Capabilities string // M115 - Capabilities string
// M117 - display message // M117 - display message
// M119 - Output Endstop status to serial port // M119 - Output Endstop status to serial port
// M140 - Set bed target temp // M140 - Set bed target temp
// M190 - Wait for bed current temp to reach target temp. // M190 - Wait for bed current temp to reach target temp.
// M200 - Set filament diameter // M200 - Set filament diameter
// M201 - Set max acceleration in units/s^2 for print moves (M201 X1000 Y1000) // M201 - Set max acceleration in units/s^2 for print moves (M201 X1000 Y1000)
// M202 - Set max acceleration in units/s^2 for travel moves (M202 X1000 Y1000) Unused in Marlin!! // M202 - Set max acceleration in units/s^2 for travel moves (M202 X1000 Y1000) Unused in Marlin!!
// M203 - Set maximum feedrate that your machine can sustain (M203 X200 Y200 Z300 E10000) in mm/sec // M203 - Set maximum feedrate that your machine can sustain (M203 X200 Y200 Z300 E10000) in mm/sec
// M204 - Set default acceleration: S normal moves T filament only moves (M204 S3000 T7000) im mm/sec^2 also sets minimum segment time in ms (B20000) to prevent buffer underruns and M20 minimum feedrate // M204 - Set default acceleration: S normal moves T filament only moves (M204 S3000 T7000) im mm/sec^2 also sets minimum segment time in ms (B20000) to prevent buffer underruns and M20 minimum feedrate
// M205 - advanced settings: minimum travel speed S=while printing T=travel only, B=minimum segment time X= maximum xy jerk, Z=maximum Z jerk, E=maximum E jerk // M205 - advanced settings: minimum travel speed S=while printing T=travel only, B=minimum segment time X= maximum xy jerk, Z=maximum Z jerk, E=maximum E jerk
// M206 - set additional homeing offset // M206 - set additional homeing offset
// M207 - set retract length S[positive mm] F[feedrate mm/sec] Z[additional zlift/hop] // M207 - set retract length S[positive mm] F[feedrate mm/sec] Z[additional zlift/hop]
// M208 - set recover=unretract length S[positive mm surplus to the M207 S*] F[feedrate mm/sec] // M208 - set recover=unretract length S[positive mm surplus to the M207 S*] F[feedrate mm/sec]
// M209 - S<1=true/0=false> enable automatic retract detect if the slicer did not support G10/11: every normal extrude-only move will be classified as retract depending on the direction. // M209 - S<1=true/0=false> enable automatic retract detect if the slicer did not support G10/11: every normal extrude-only move will be classified as retract depending on the direction.
// M218 - set hotend offset (in mm): T<extruder_number> X<offset_on_X> Y<offset_on_Y> // M218 - set hotend offset (in mm): T<extruder_number> X<offset_on_X> Y<offset_on_Y>
// M220 S<factor in percent>- set speed factor override percentage // M220 S<factor in percent>- set speed factor override percentage
// M221 S<factor in percent>- set extrude factor override percentage // M221 S<factor in percent>- set extrude factor override percentage
// M240 - Trigger a camera to take a photograph // M240 - Trigger a camera to take a photograph
// M301 - Set PID parameters P I and D // M301 - Set PID parameters P I and D
// M302 - Allow cold extrudes // M302 - Allow cold extrudes
// M303 - PID relay autotune S<temperature> sets the target temperature. (default target temperature = 150C) // M303 - PID relay autotune S<temperature> sets the target temperature. (default target temperature = 150C)
// M304 - Set bed PID parameters P I and D // M304 - Set bed PID parameters P I and D
// M400 - Finish all moves // M400 - Finish all moves
// M500 - stores paramters in EEPROM // M500 - stores paramters in EEPROM
// M501 - reads parameters from EEPROM (if you need reset them after you changed them temporarily). // M501 - reads parameters from EEPROM (if you need reset them after you changed them temporarily).
// M502 - reverts to the default "factory settings". You still need to store them in EEPROM afterwards if you want to. // M502 - reverts to the default "factory settings". You still need to store them in EEPROM afterwards if you want to.
// M503 - print the current settings (from memory not from eeprom) // M503 - print the current settings (from memory not from eeprom)
// M540 - Use S[0|1] to enable or disable the stop SD card print on endstop hit (requires ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED) // M540 - Use S[0|1] to enable or disable the stop SD card print on endstop hit (requires ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED)
// M600 - Pause for filament change X[pos] Y[pos] Z[relative lift] E[initial retract] L[later retract distance for removal] // M600 - Pause for filament change X[pos] Y[pos] Z[relative lift] E[initial retract] L[later retract distance for removal]
// M907 - Set digital trimpot motor current using axis codes. // M907 - Set digital trimpot motor current using axis codes.
// M908 - Control digital trimpot directly. // M908 - Control digital trimpot directly.
// M350 - Set microstepping mode. // M350 - Set microstepping mode.
// M351 - Toggle MS1 MS2 pins directly. // M351 - Toggle MS1 MS2 pins directly.
// M999 - Restart after being stopped by error // M999 - Restart after being stopped by error
//Stepper Movement Variables //Stepper Movement Variables
//=========================================================================== //===========================================================================
//=============================imported variables============================ //=============================imported variables============================
//=========================================================================== //===========================================================================
//=========================================================================== //===========================================================================
//=============================public variables============================= //=============================public variables=============================
//=========================================================================== //===========================================================================
#ifdef SDSUPPORT #ifdef SDSUPPORT
CardReader card; CardReader card;
#endif #endif
float homing_feedrate[] = HOMING_FEEDRATE; float homing_feedrate[] = HOMING_FEEDRATE;
bool axis_relative_modes[] = AXIS_RELATIVE_MODES; bool axis_relative_modes[] = AXIS_RELATIVE_MODES;
int feedmultiply=100; //100->1 200->2 int feedmultiply=100; //100->1 200->2
int saved_feedmultiply; int saved_feedmultiply;
int extrudemultiply=100; //100->1 200->2 int extrudemultiply=100; //100->1 200->2
float current_position[NUM_AXIS] = { 0.0, 0.0, 0.0, 0.0 }; float current_position[NUM_AXIS] = { 0.0, 0.0, 0.0, 0.0 };
float add_homeing[3]={0,0,0}; float add_homeing[3]={0,0,0};
float min_pos[3] = { X_MIN_POS, Y_MIN_POS, Z_MIN_POS }; float min_pos[3] = { X_MIN_POS, Y_MIN_POS, Z_MIN_POS };
float max_pos[3] = { X_MAX_POS, Y_MAX_POS, Z_MAX_POS }; float max_pos[3] = { X_MAX_POS, Y_MAX_POS, Z_MAX_POS };
// Extruder offset, only in XY plane // Extruder offset, only in XY plane
#if EXTRUDERS > 1 #if EXTRUDERS > 1
float extruder_offset[2][EXTRUDERS] = { float extruder_offset[2][EXTRUDERS] = {
#if defined(EXTRUDER_OFFSET_X) && defined(EXTRUDER_OFFSET_Y) #if defined(EXTRUDER_OFFSET_X) && defined(EXTRUDER_OFFSET_Y)
EXTRUDER_OFFSET_X, EXTRUDER_OFFSET_Y EXTRUDER_OFFSET_X, EXTRUDER_OFFSET_Y
#endif #endif
}; };
#endif #endif
uint8_t active_extruder = 0; uint8_t active_extruder = 0;
int fanSpeed=0; int fanSpeed=0;
#ifdef FWRETRACT #ifdef FWRETRACT
bool autoretract_enabled=true; bool autoretract_enabled=true;
bool retracted=false; bool retracted=false;
float retract_length=3, retract_feedrate=17*60, retract_zlift=0.8; float retract_length=3, retract_feedrate=17*60, retract_zlift=0.8;
float retract_recover_length=0, retract_recover_feedrate=8*60; float retract_recover_length=0, retract_recover_feedrate=8*60;
#endif #endif
//=========================================================================== //===========================================================================
//=============================private variables============================= //=============================private variables=============================
//=========================================================================== //===========================================================================
const char axis_codes[NUM_AXIS] = {'X', 'Y', 'Z', 'E'}; const char axis_codes[NUM_AXIS] = {'X', 'Y', 'Z', 'E'};
static float destination[NUM_AXIS] = { 0.0, 0.0, 0.0, 0.0}; static float destination[NUM_AXIS] = { 0.0, 0.0, 0.0, 0.0};
static float offset[3] = {0.0, 0.0, 0.0}; static float offset[3] = {0.0, 0.0, 0.0};
static bool home_all_axis = true; static bool home_all_axis = true;
static float feedrate = 1500.0, next_feedrate, saved_feedrate; static float feedrate = 1500.0, next_feedrate, saved_feedrate;
static long gcode_N, gcode_LastN, Stopped_gcode_LastN = 0; static long gcode_N, gcode_LastN, Stopped_gcode_LastN = 0;
static bool relative_mode = false; //Determines Absolute or Relative Coordinates static bool relative_mode = false; //Determines Absolute or Relative Coordinates
static char cmdbuffer[BUFSIZE][MAX_CMD_SIZE]; static char cmdbuffer[BUFSIZE][MAX_CMD_SIZE];
static bool fromsd[BUFSIZE]; static bool fromsd[BUFSIZE];
static int bufindr = 0; static int bufindr = 0;
static int bufindw = 0; static int bufindw = 0;
static int buflen = 0; static int buflen = 0;
//static int i = 0; //static int i = 0;
static char serial_char; static char serial_char;
static int serial_count = 0; static int serial_count = 0;
static boolean comment_mode = false; static boolean comment_mode = false;
static char *strchr_pointer; // just a pointer to find chars in the cmd string like X, Y, Z, E, etc static char *strchr_pointer; // just a pointer to find chars in the cmd string like X, Y, Z, E, etc
const int sensitive_pins[] = SENSITIVE_PINS; // Sensitive pin list for M42 const int sensitive_pins[] = SENSITIVE_PINS; // Sensitive pin list for M42
//static float tt = 0; //static float tt = 0;
//static float bt = 0; //static float bt = 0;
//Inactivity shutdown variables //Inactivity shutdown variables
static unsigned long previous_millis_cmd = 0; static unsigned long previous_millis_cmd = 0;
static unsigned long max_inactive_time = 0; static unsigned long max_inactive_time = 0;
static unsigned long stepper_inactive_time = DEFAULT_STEPPER_DEACTIVE_TIME*1000l; static unsigned long stepper_inactive_time = DEFAULT_STEPPER_DEACTIVE_TIME*1000l;
unsigned long starttime=0; unsigned long starttime=0;
unsigned long stoptime=0; unsigned long stoptime=0;
static uint8_t tmp_extruder; static uint8_t tmp_extruder;
bool Stopped=false; bool Stopped=false;
//=========================================================================== //===========================================================================
//=============================ROUTINES============================= //=============================ROUTINES=============================
//=========================================================================== //===========================================================================
void get_arc_coordinates(); void get_arc_coordinates();
bool setTargetedHotend(int code); bool setTargetedHotend(int code);
void serial_echopair_P(const char *s_P, float v) void serial_echopair_P(const char *s_P, float v)
{ serialprintPGM(s_P); SERIAL_ECHO(v); } { serialprintPGM(s_P); SERIAL_ECHO(v); }
void serial_echopair_P(const char *s_P, double v) void serial_echopair_P(const char *s_P, double v)
{ serialprintPGM(s_P); SERIAL_ECHO(v); } { serialprintPGM(s_P); SERIAL_ECHO(v); }
void serial_echopair_P(const char *s_P, unsigned long v) void serial_echopair_P(const char *s_P, unsigned long v)
{ serialprintPGM(s_P); SERIAL_ECHO(v); } { serialprintPGM(s_P); SERIAL_ECHO(v); }
extern "C"{ extern "C"{
extern unsigned int __bss_end; extern unsigned int __bss_end;
extern unsigned int __heap_start; extern unsigned int __heap_start;
extern void *__brkval; extern void *__brkval;
int freeMemory() { int freeMemory() {
int free_memory; int free_memory;
if((int)__brkval == 0) if((int)__brkval == 0)
free_memory = ((int)&free_memory) - ((int)&__bss_end); free_memory = ((int)&free_memory) - ((int)&__bss_end);
else else
free_memory = ((int)&free_memory) - ((int)__brkval); free_memory = ((int)&free_memory) - ((int)__brkval);
return free_memory; return free_memory;
} }
} }
//adds an command to the main command buffer //adds an command to the main command buffer
//thats really done in a non-safe way. //thats really done in a non-safe way.
//needs overworking someday //needs overworking someday
void enquecommand(const char *cmd) void enquecommand(const char *cmd)
{ {
if(buflen < BUFSIZE) if(buflen < BUFSIZE)
{ {
//this is dangerous if a mixing of serial and this happsens //this is dangerous if a mixing of serial and this happsens
strcpy(&(cmdbuffer[bufindw][0]),cmd); strcpy(&(cmdbuffer[bufindw][0]),cmd);
SERIAL_ECHO_START; SERIAL_ECHO_START;
SERIAL_ECHOPGM("enqueing \""); SERIAL_ECHOPGM("enqueing \"");
SERIAL_ECHO(cmdbuffer[bufindw]); SERIAL_ECHO(cmdbuffer[bufindw]);
SERIAL_ECHOLNPGM("\""); SERIAL_ECHOLNPGM("\"");
bufindw= (bufindw + 1)%BUFSIZE; bufindw= (bufindw + 1)%BUFSIZE;
buflen += 1; buflen += 1;
} }
} }
void enquecommand_P(const char *cmd) void enquecommand_P(const char *cmd)
{ {
if(buflen < BUFSIZE) if(buflen < BUFSIZE)
{ {
//this is dangerous if a mixing of serial and this happsens //this is dangerous if a mixing of serial and this happsens
strcpy_P(&(cmdbuffer[bufindw][0]),cmd); strcpy_P(&(cmdbuffer[bufindw][0]),cmd);
SERIAL_ECHO_START; SERIAL_ECHO_START;
SERIAL_ECHOPGM("enqueing \""); SERIAL_ECHOPGM("enqueing \"");
SERIAL_ECHO(cmdbuffer[bufindw]); SERIAL_ECHO(cmdbuffer[bufindw]);
SERIAL_ECHOLNPGM("\""); SERIAL_ECHOLNPGM("\"");
bufindw= (bufindw + 1)%BUFSIZE; bufindw= (bufindw + 1)%BUFSIZE;
buflen += 1; buflen += 1;
} }
} }
void setup_killpin() void setup_killpin()
{ {
#if( KILL_PIN>-1 ) #if( KILL_PIN>-1 )
pinMode(KILL_PIN,INPUT); pinMode(KILL_PIN,INPUT);
WRITE(KILL_PIN,HIGH); WRITE(KILL_PIN,HIGH);
#endif #endif
} }
void setup_photpin() void setup_photpin()
{ {
#ifdef PHOTOGRAPH_PIN #ifdef PHOTOGRAPH_PIN
#if (PHOTOGRAPH_PIN > -1) #if (PHOTOGRAPH_PIN > -1)
SET_OUTPUT(PHOTOGRAPH_PIN); SET_OUTPUT(PHOTOGRAPH_PIN);
WRITE(PHOTOGRAPH_PIN, LOW); WRITE(PHOTOGRAPH_PIN, LOW);
#endif #endif
#endif #endif
} }
void setup_powerhold() void setup_powerhold()
{ {
#ifdef SUICIDE_PIN #ifdef SUICIDE_PIN
#if (SUICIDE_PIN> -1) #if (SUICIDE_PIN> -1)
SET_OUTPUT(SUICIDE_PIN); SET_OUTPUT(SUICIDE_PIN);
WRITE(SUICIDE_PIN, HIGH); WRITE(SUICIDE_PIN, HIGH);
#endif #endif
#endif #endif
#if (PS_ON_PIN > -1) #if (PS_ON_PIN > -1)
SET_OUTPUT(PS_ON_PIN); SET_OUTPUT(PS_ON_PIN);
WRITE(PS_ON_PIN, PS_ON_AWAKE); WRITE(PS_ON_PIN, PS_ON_AWAKE);
#endif #endif
} }
void suicide() void suicide()
{ {
#ifdef SUICIDE_PIN #ifdef SUICIDE_PIN
#if (SUICIDE_PIN> -1) #if (SUICIDE_PIN> -1)
SET_OUTPUT(SUICIDE_PIN); SET_OUTPUT(SUICIDE_PIN);
WRITE(SUICIDE_PIN, LOW); WRITE(SUICIDE_PIN, LOW);
#endif #endif
#endif #endif
} }
void setup() void setup()
{ {
setup_killpin(); setup_killpin();
setup_powerhold(); setup_powerhold();
MYSERIAL.begin(BAUDRATE); MYSERIAL.begin(BAUDRATE);
SERIAL_PROTOCOLLNPGM("start"); SERIAL_PROTOCOLLNPGM("start");
SERIAL_ECHO_START; SERIAL_ECHO_START;
// Check startup - does nothing if bootloader sets MCUSR to 0 // Check startup - does nothing if bootloader sets MCUSR to 0
byte mcu = MCUSR; byte mcu = MCUSR;
if(mcu & 1) SERIAL_ECHOLNPGM(MSG_POWERUP); if(mcu & 1) SERIAL_ECHOLNPGM(MSG_POWERUP);
if(mcu & 2) SERIAL_ECHOLNPGM(MSG_EXTERNAL_RESET); if(mcu & 2) SERIAL_ECHOLNPGM(MSG_EXTERNAL_RESET);
if(mcu & 4) SERIAL_ECHOLNPGM(MSG_BROWNOUT_RESET); if(mcu & 4) SERIAL_ECHOLNPGM(MSG_BROWNOUT_RESET);
if(mcu & 8) SERIAL_ECHOLNPGM(MSG_WATCHDOG_RESET); if(mcu & 8) SERIAL_ECHOLNPGM(MSG_WATCHDOG_RESET);
if(mcu & 32) SERIAL_ECHOLNPGM(MSG_SOFTWARE_RESET); if(mcu & 32) SERIAL_ECHOLNPGM(MSG_SOFTWARE_RESET);
MCUSR=0; MCUSR=0;
SERIAL_ECHOPGM(MSG_MARLIN); SERIAL_ECHOPGM(MSG_MARLIN);
SERIAL_ECHOLNPGM(VERSION_STRING); SERIAL_ECHOLNPGM(VERSION_STRING);
#ifdef STRING_VERSION_CONFIG_H #ifdef STRING_VERSION_CONFIG_H
#ifdef STRING_CONFIG_H_AUTHOR #ifdef STRING_CONFIG_H_AUTHOR
SERIAL_ECHO_START; SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_CONFIGURATION_VER); SERIAL_ECHOPGM(MSG_CONFIGURATION_VER);
SERIAL_ECHOPGM(STRING_VERSION_CONFIG_H); SERIAL_ECHOPGM(STRING_VERSION_CONFIG_H);
SERIAL_ECHOPGM(MSG_AUTHOR); SERIAL_ECHOPGM(MSG_AUTHOR);
SERIAL_ECHOLNPGM(STRING_CONFIG_H_AUTHOR); SERIAL_ECHOLNPGM(STRING_CONFIG_H_AUTHOR);
SERIAL_ECHOPGM("Compiled: "); SERIAL_ECHOPGM("Compiled: ");
SERIAL_ECHOLNPGM(__DATE__); SERIAL_ECHOLNPGM(__DATE__);
#endif #endif
#endif #endif
SERIAL_ECHO_START; SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_FREE_MEMORY); SERIAL_ECHOPGM(MSG_FREE_MEMORY);
SERIAL_ECHO(freeMemory()); SERIAL_ECHO(freeMemory());
SERIAL_ECHOPGM(MSG_PLANNER_BUFFER_BYTES); SERIAL_ECHOPGM(MSG_PLANNER_BUFFER_BYTES);
SERIAL_ECHOLN((int)sizeof(block_t)*BLOCK_BUFFER_SIZE); SERIAL_ECHOLN((int)sizeof(block_t)*BLOCK_BUFFER_SIZE);
for(int8_t i = 0; i < BUFSIZE; i++) for(int8_t i = 0; i < BUFSIZE; i++)
{ {
fromsd[i] = false; fromsd[i] = false;
} }
Config_RetrieveSettings(); // loads data from EEPROM if available Config_RetrieveSettings(); // loads data from EEPROM if available
for(int8_t i=0; i < NUM_AXIS; i++) for(int8_t i=0; i < NUM_AXIS; i++)
{ {
axis_steps_per_sqr_second[i] = max_acceleration_units_per_sq_second[i] * axis_steps_per_unit[i]; axis_steps_per_sqr_second[i] = max_acceleration_units_per_sq_second[i] * axis_steps_per_unit[i];
} }
tp_init(); // Initialize temperature loop tp_init(); // Initialize temperature loop
plan_init(); // Initialize planner; plan_init(); // Initialize planner;
watchdog_init(); watchdog_init();
st_init(); // Initialize stepper, this enables interrupts! st_init(); // Initialize stepper, this enables interrupts!
setup_photpin(); setup_photpin();
lcd_init(); lcd_init();
}
#ifdef CONTROLLERFAN_PIN
SET_OUTPUT(CONTROLLERFAN_PIN); //Set pin used for driver cooling fan
void loop() #endif
{
if(buflen < (BUFSIZE-1)) #ifdef EXTRUDERFAN_PIN
get_command(); SET_OUTPUT(EXTRUDERFAN_PIN); //Set pin used for extruder cooling fan
#ifdef SDSUPPORT #endif
card.checkautostart(false); }
#endif
if(buflen)
{ void loop()
#ifdef SDSUPPORT {
if(card.saving) if(buflen < (BUFSIZE-1))
{ get_command();
if(strstr_P(cmdbuffer[bufindr], PSTR("M29")) == NULL) #ifdef SDSUPPORT
{ card.checkautostart(false);
card.write_command(cmdbuffer[bufindr]); #endif
SERIAL_PROTOCOLLNPGM(MSG_OK); if(buflen)
} {
else #ifdef SDSUPPORT
{ if(card.saving)
card.closefile(); {
SERIAL_PROTOCOLLNPGM(MSG_FILE_SAVED); if(strstr_P(cmdbuffer[bufindr], PSTR("M29")) == NULL)
} {
} card.write_command(cmdbuffer[bufindr]);
else SERIAL_PROTOCOLLNPGM(MSG_OK);
{ }
process_commands(); else
} {
#else card.closefile();
process_commands(); SERIAL_PROTOCOLLNPGM(MSG_FILE_SAVED);
#endif //SDSUPPORT }
buflen = (buflen-1);
bufindr = (bufindr + 1)%BUFSIZE;
}
//check heater every n milliseconds
manage_heater();
manage_inactivity();
checkHitEndstops();
lcd_update();
}
void get_command()
{
while( MYSERIAL.available() > 0 && buflen < BUFSIZE) {
serial_char = MYSERIAL.read();
if(serial_char == '\n' ||
serial_char == '\r' ||
(serial_char == ':' && comment_mode == false) ||
serial_count >= (MAX_CMD_SIZE - 1) )
{
if(!serial_count) { //if empty line
comment_mode = false; //for new command
return;
}
cmdbuffer[bufindw][serial_count] = 0; //terminate string
if(!comment_mode){
comment_mode = false; //for new command
fromsd[bufindw] = false;
if(strchr(cmdbuffer[bufindw], 'N') != NULL)
{
strchr_pointer = strchr(cmdbuffer[bufindw], 'N');
gcode_N = (strtol(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL, 10));
if(gcode_N != gcode_LastN+1 && (strstr_P(cmdbuffer[bufindw], PSTR("M110")) == NULL) ) {
SERIAL_ERROR_START;
SERIAL_ERRORPGM(MSG_ERR_LINE_NO);
SERIAL_ERRORLN(gcode_LastN);
//Serial.println(gcode_N);
FlushSerialRequestResend();
serial_count = 0;
return;
}
if(strchr(cmdbuffer[bufindw], '*') != NULL)
{
byte checksum = 0;
byte count = 0;
while(cmdbuffer[bufindw][count] != '*') checksum = checksum^cmdbuffer[bufindw][count++];
strchr_pointer = strchr(cmdbuffer[bufindw], '*');
if( (int)(strtod(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL)) != checksum) {
SERIAL_ERROR_START;
SERIAL_ERRORPGM(MSG_ERR_CHECKSUM_MISMATCH);
SERIAL_ERRORLN(gcode_LastN);
FlushSerialRequestResend();
serial_count = 0;
return;
}
//if no errors, continue parsing
}
else
{
SERIAL_ERROR_START;
SERIAL_ERRORPGM(MSG_ERR_NO_CHECKSUM);
SERIAL_ERRORLN(gcode_LastN);
FlushSerialRequestResend();
serial_count = 0;
return;
}
gcode_LastN = gcode_N;
//if no errors, continue parsing
}
else // if we don't receive 'N' but still see '*'
{
if((strchr(cmdbuffer[bufindw], '*') != NULL))
{
SERIAL_ERROR_START;
SERIAL_ERRORPGM(MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM);
SERIAL_ERRORLN(gcode_LastN);
serial_count = 0;
return;
}
}
if((strchr(cmdbuffer[bufindw], 'G') != NULL)){
strchr_pointer = strchr(cmdbuffer[bufindw], 'G');
switch((int)((strtod(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL)))){
case 0:
case 1:
case 2:
case 3:
if(Stopped == false) { // If printer is stopped by an error the G[0-3] codes are ignored.
#ifdef SDSUPPORT
if(card.saving)
break;
#endif //SDSUPPORT
SERIAL_PROTOCOLLNPGM(MSG_OK);
}
else {
SERIAL_ERRORLNPGM(MSG_ERR_STOPPED);
LCD_MESSAGEPGM(MSG_STOPPED);
}
break;
default:
break;
}
}
bufindw = (bufindw + 1)%BUFSIZE;
buflen += 1;
}
serial_count = 0; //clear buffer
}
else
{
if(serial_char == ';') comment_mode = true;
if(!comment_mode) cmdbuffer[bufindw][serial_count++] = serial_char;
}
}
#ifdef SDSUPPORT
if(!card.sdprinting || serial_count!=0){
return;
}
while( !card.eof() && buflen < BUFSIZE) {
int16_t n=card.get();
serial_char = (char)n;
if(serial_char == '\n' ||
serial_char == '\r' ||
(serial_char == ':' && comment_mode == false) ||
serial_count >= (MAX_CMD_SIZE - 1)||n==-1)
{
if(card.eof()){
SERIAL_PROTOCOLLNPGM(MSG_FILE_PRINTED);
stoptime=millis();
char time[30];
unsigned long t=(stoptime-starttime)/1000;
int hours, minutes;
minutes=(t/60)%60;
hours=t/60/60;
sprintf_P(time, PSTR("%i hours %i minutes"),hours, minutes);
SERIAL_ECHO_START;
SERIAL_ECHOLN(time);
lcd_setstatus(time);
card.printingHasFinished();
card.checkautostart(true);
}
if(!serial_count)
{
comment_mode = false; //for new command
return; //if empty line
}
cmdbuffer[bufindw][serial_count] = 0; //terminate string
// if(!comment_mode){
fromsd[bufindw] = true;
buflen += 1;
bufindw = (bufindw + 1)%BUFSIZE;
// }
comment_mode = false; //for new command
serial_count = 0; //clear buffer
}
else
{
if(serial_char == ';') comment_mode = true;
if(!comment_mode) cmdbuffer[bufindw][serial_count++] = serial_char;
}
}
#endif //SDSUPPORT
}
float code_value()
{
return (strtod(&cmdbuffer[bufindr][strchr_pointer - cmdbuffer[bufindr] + 1], NULL));
}
long code_value_long()
{
return (strtol(&cmdbuffer[bufindr][strchr_pointer - cmdbuffer[bufindr] + 1], NULL, 10));
}
bool code_seen(char code)
{
strchr_pointer = strchr(cmdbuffer[bufindr], code);
return (strchr_pointer != NULL); //Return True if a character was found
}
#define DEFINE_PGM_READ_ANY(type, reader) \
static inline type pgm_read_any(const type *p) \
{ return pgm_read_##reader##_near(p); }
DEFINE_PGM_READ_ANY(float, float);
DEFINE_PGM_READ_ANY(signed char, byte);
#define XYZ_CONSTS_FROM_CONFIG(type, array, CONFIG) \
static const PROGMEM type array##_P[3] = \
{ X_##CONFIG, Y_##CONFIG, Z_##CONFIG }; \
static inline type array(int axis) \
{ return pgm_read_any(&array##_P[axis]); }
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_retract_mm, HOME_RETRACT_MM);
XYZ_CONSTS_FROM_CONFIG(signed char, home_dir, HOME_DIR);
static void axis_is_at_home(int axis) {
current_position[axis] = base_home_pos(axis) + add_homeing[axis];
min_pos[axis] = base_min_pos(axis) + add_homeing[axis];
max_pos[axis] = base_max_pos(axis) + add_homeing[axis];
}
static void homeaxis(int axis) {
#define HOMEAXIS_DO(LETTER) \
((LETTER##_MIN_PIN > -1 && LETTER##_HOME_DIR==-1) || (LETTER##_MAX_PIN > -1 && LETTER##_HOME_DIR==1))
if (axis==X_AXIS ? HOMEAXIS_DO(X) :
axis==Y_AXIS ? HOMEAXIS_DO(Y) :
axis==Z_AXIS ? HOMEAXIS_DO(Z) :
0) {
current_position[axis] = 0;
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
destination[axis] = 1.5 * max_length(axis) * home_dir(axis);
feedrate = homing_feedrate[axis];
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
st_synchronize();
current_position[axis] = 0;
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
destination[axis] = -home_retract_mm(axis) * home_dir(axis);
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
st_synchronize();
destination[axis] = 2*home_retract_mm(axis) * home_dir(axis);
feedrate = homing_feedrate[axis]/2 ;
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
st_synchronize();
axis_is_at_home(axis);
destination[axis] = current_position[axis];
feedrate = 0.0;
endstops_hit_on_purpose();
}
}
#define HOMEAXIS(LETTER) homeaxis(LETTER##_AXIS)
void process_commands()
{
unsigned long codenum; //throw away variable
char *starpos = NULL;
if(code_seen('G'))
{
switch((int)code_value())
{
case 0: // G0 -> G1
case 1: // G1
if(Stopped == false) {
get_coordinates(); // For X Y Z E F
prepare_move();
//ClearToSend();
return;
}
//break;
case 2: // G2 - CW ARC
if(Stopped == false) {
get_arc_coordinates();
prepare_arc_move(true);
return;
}
case 3: // G3 - CCW ARC
if(Stopped == false) {
get_arc_coordinates();
prepare_arc_move(false);
return;
}
case 4: // G4 dwell
LCD_MESSAGEPGM(MSG_DWELL);
codenum = 0;
if(code_seen('P')) codenum = code_value(); // milliseconds to wait
if(code_seen('S')) codenum = code_value() * 1000; // seconds to wait
st_synchronize();
codenum += millis(); // keep track of when we started waiting
previous_millis_cmd = millis();
while(millis() < codenum ){
manage_heater();
manage_inactivity();
lcd_update();
}
break;
#ifdef FWRETRACT
case 10: // G10 retract
if(!retracted)
{
destination[X_AXIS]=current_position[X_AXIS];
destination[Y_AXIS]=current_position[Y_AXIS];
destination[Z_AXIS]=current_position[Z_AXIS];
current_position[Z_AXIS]+=-retract_zlift;
destination[E_AXIS]=current_position[E_AXIS]-retract_length;
feedrate=retract_feedrate;
retracted=true;
prepare_move();
}
break;
case 11: // G10 retract_recover
if(!retracted)
{
destination[X_AXIS]=current_position[X_AXIS];
destination[Y_AXIS]=current_position[Y_AXIS];
destination[Z_AXIS]=current_position[Z_AXIS];
current_position[Z_AXIS]+=retract_zlift;
current_position[E_AXIS]+=-retract_recover_length;
feedrate=retract_recover_feedrate;
retracted=false;
prepare_move();
}
break;
#endif //FWRETRACT
case 28: //G28 Home all Axis one at a time
saved_feedrate = feedrate;
saved_feedmultiply = feedmultiply;
feedmultiply = 100;
previous_millis_cmd = millis();
enable_endstops(true);
for(int8_t i=0; i < NUM_AXIS; i++) {
destination[i] = current_position[i];
}
feedrate = 0.0;
home_all_axis = !((code_seen(axis_codes[0])) || (code_seen(axis_codes[1])) || (code_seen(axis_codes[2])));
#if Z_HOME_DIR > 0 // If homing away from BED do Z first
if((home_all_axis) || (code_seen(axis_codes[Z_AXIS]))) {
HOMEAXIS(Z);
}
#endif
#ifdef QUICK_HOME
if((home_all_axis)||( code_seen(axis_codes[X_AXIS]) && code_seen(axis_codes[Y_AXIS])) ) //first diagonal move
{
current_position[X_AXIS] = 0;current_position[Y_AXIS] = 0;
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
destination[X_AXIS] = 1.5 * X_MAX_LENGTH * X_HOME_DIR;destination[Y_AXIS] = 1.5 * Y_MAX_LENGTH * Y_HOME_DIR;
feedrate = homing_feedrate[X_AXIS];
if(homing_feedrate[Y_AXIS]<feedrate)
feedrate =homing_feedrate[Y_AXIS];
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
st_synchronize();
axis_is_at_home(X_AXIS);
axis_is_at_home(Y_AXIS);
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
destination[X_AXIS] = current_position[X_AXIS];
destination[Y_AXIS] = current_position[Y_AXIS];
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
feedrate = 0.0;
st_synchronize();
endstops_hit_on_purpose();
}
#endif
if((home_all_axis) || (code_seen(axis_codes[X_AXIS])))
{
HOMEAXIS(X);
}
if((home_all_axis) || (code_seen(axis_codes[Y_AXIS]))) {
HOMEAXIS(Y);
}
#if Z_HOME_DIR < 0 // If homing towards BED do Z last
if((home_all_axis) || (code_seen(axis_codes[Z_AXIS]))) {
HOMEAXIS(Z);
}
#endif
if(code_seen(axis_codes[X_AXIS]))
{
if(code_value_long() != 0) {
current_position[X_AXIS]=code_value()+add_homeing[0];
}
}
if(code_seen(axis_codes[Y_AXIS])) {
if(code_value_long() != 0) {
current_position[Y_AXIS]=code_value()+add_homeing[1];
}
}
if(code_seen(axis_codes[Z_AXIS])) {
if(code_value_long() != 0) {
current_position[Z_AXIS]=code_value()+add_homeing[2];
}
}
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
#ifdef ENDSTOPS_ONLY_FOR_HOMING
enable_endstops(false);
#endif
feedrate = saved_feedrate;
feedmultiply = saved_feedmultiply;
previous_millis_cmd = millis();
endstops_hit_on_purpose();
break;
case 90: // G90
relative_mode = false;
break;
case 91: // G91
relative_mode = true;
break;
case 92: // G92
if(!code_seen(axis_codes[E_AXIS]))
st_synchronize();
for(int8_t i=0; i < NUM_AXIS; i++) {
if(code_seen(axis_codes[i])) {
if(i == E_AXIS) {
current_position[i] = code_value();
plan_set_e_position(current_position[E_AXIS]);
}
else {
current_position[i] = code_value()+add_homeing[i];
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
}
}
}
break;
}
}
else if(code_seen('M'))
{
switch( (int)code_value() )
{
#ifdef ULTIPANEL
case 0: // M0 - Unconditional stop - Wait for user button press on LCD
case 1: // M1 - Conditional stop - Wait for user button press on LCD
{
LCD_MESSAGEPGM(MSG_USERWAIT);
codenum = 0;
if(code_seen('P')) codenum = code_value(); // milliseconds to wait
if(code_seen('S')) codenum = code_value() * 1000; // seconds to wait
st_synchronize();
previous_millis_cmd = millis();
if (codenum > 0){
codenum += millis(); // keep track of when we started waiting
while(millis() < codenum && !LCD_CLICKED){
manage_heater();
manage_inactivity();
lcd_update();
}
}else{
while(!LCD_CLICKED){
manage_heater();
manage_inactivity();
lcd_update();
}
}
LCD_MESSAGEPGM(MSG_RESUMING);
}
break;
#endif
case 17:
LCD_MESSAGEPGM(MSG_NO_MOVE);
enable_x();
enable_y();
enable_z();
enable_e0();
enable_e1();
enable_e2();
break;
#ifdef SDSUPPORT
case 20: // M20 - list SD card
SERIAL_PROTOCOLLNPGM(MSG_BEGIN_FILE_LIST);
card.ls();
SERIAL_PROTOCOLLNPGM(MSG_END_FILE_LIST);
break;
case 21: // M21 - init SD card
card.initsd();
break;
case 22: //M22 - release SD card
card.release();
break;
case 23: //M23 - Select file
starpos = (strchr(strchr_pointer + 4,'*'));
if(starpos!=NULL)
*(starpos-1)='\0';
card.openFile(strchr_pointer + 4,true);
break;
case 24: //M24 - Start SD print
card.startFileprint();
starttime=millis();
break;
case 25: //M25 - Pause SD print
card.pauseSDPrint();
break;
case 26: //M26 - Set SD index
if(card.cardOK && code_seen('S')) {
card.setIndex(code_value_long());
}
break;
case 27: //M27 - Get SD status
card.getStatus();
break;
case 28: //M28 - Start SD write
starpos = (strchr(strchr_pointer + 4,'*'));
if(starpos != NULL){
char* npos = strchr(cmdbuffer[bufindr], 'N');
strchr_pointer = strchr(npos,' ') + 1;
*(starpos-1) = '\0';
}
card.openFile(strchr_pointer+4,false);
break;
case 29: //M29 - Stop SD write
//processed in write to file routine above
//card,saving = false;
break;
case 30: //M30 <filename> Delete File
if (card.cardOK){
card.closefile();
starpos = (strchr(strchr_pointer + 4,'*'));
if(starpos != NULL){
char* npos = strchr(cmdbuffer[bufindr], 'N');
strchr_pointer = strchr(npos,' ') + 1;
*(starpos-1) = '\0';
}
card.removeFile(strchr_pointer + 4);
}
break;
#endif //SDSUPPORT
case 31: //M31 take time since the start of the SD print or an M109 command
{
stoptime=millis();
char time[30];
unsigned long t=(stoptime-starttime)/1000;
int sec,min;
min=t/60;
sec=t%60;
sprintf_P(time, PSTR("%i min, %i sec"), min, sec);
SERIAL_ECHO_START;
SERIAL_ECHOLN(time);
lcd_setstatus(time);
autotempShutdown();
}
break;
case 42: //M42 -Change pin status via gcode
if (code_seen('S'))
{
int pin_status = code_value();
int pin_number = LED_PIN;
if (code_seen('P') && pin_status >= 0 && pin_status <= 255)
pin_number = code_value();
for(int8_t i = 0; i < (int8_t)sizeof(sensitive_pins); i++)
{
if (sensitive_pins[i] == pin_number)
{
pin_number = -1;
break;
}
}
if (pin_number > -1)
{
pinMode(pin_number, OUTPUT);
digitalWrite(pin_number, pin_status);
analogWrite(pin_number, pin_status);
}
}
break;
case 104: // M104
if(setTargetedHotend(104)){
break;
}
if (code_seen('S')) setTargetHotend(code_value(), tmp_extruder);
setWatch();
break;
case 140: // M140 set bed temp
if (code_seen('S')) setTargetBed(code_value());
break;
case 105 : // M105
if(setTargetedHotend(105)){
break;
}
#if (TEMP_0_PIN > -1)
SERIAL_PROTOCOLPGM("ok T:");
SERIAL_PROTOCOL_F(degHotend(tmp_extruder),1);
SERIAL_PROTOCOLPGM(" /");
SERIAL_PROTOCOL_F(degTargetHotend(tmp_extruder),1);
#if TEMP_BED_PIN > -1
SERIAL_PROTOCOLPGM(" B:");
SERIAL_PROTOCOL_F(degBed(),1);
SERIAL_PROTOCOLPGM(" /");
SERIAL_PROTOCOL_F(degTargetBed(),1);
#endif //TEMP_BED_PIN
#else
SERIAL_ERROR_START;
SERIAL_ERRORLNPGM(MSG_ERR_NO_THERMISTORS);
#endif
SERIAL_PROTOCOLPGM(" @:");
SERIAL_PROTOCOL(getHeaterPower(tmp_extruder));
SERIAL_PROTOCOLPGM(" B@:");
SERIAL_PROTOCOL(getHeaterPower(-1));
SERIAL_PROTOCOLLN("");
return;
break;
case 109:
{// M109 - Wait for extruder heater to reach target.
if(setTargetedHotend(109)){
break;
}
LCD_MESSAGEPGM(MSG_HEATING);
#ifdef AUTOTEMP
autotemp_enabled=false;
#endif
if (code_seen('S')) setTargetHotend(code_value(), tmp_extruder);
#ifdef AUTOTEMP
if (code_seen('S')) autotemp_min=code_value();
if (code_seen('B')) autotemp_max=code_value();
if (code_seen('F'))
{
autotemp_factor=code_value();
autotemp_enabled=true;
}
#endif
setWatch();
codenum = millis();
/* See if we are heating up or cooling down */
bool target_direction = isHeatingHotend(tmp_extruder); // true if heating, false if cooling
#ifdef TEMP_RESIDENCY_TIME
long residencyStart;
residencyStart = -1;
/* continue to loop until we have reached the target temp
_and_ until TEMP_RESIDENCY_TIME hasn't passed since we reached it */
while((residencyStart == -1) ||
(residencyStart >= 0 && (((unsigned int) (millis() - residencyStart)) < (TEMP_RESIDENCY_TIME * 1000UL))) ) {
#else
while ( target_direction ? (isHeatingHotend(tmp_extruder)) : (isCoolingHotend(tmp_extruder)&&(CooldownNoWait==false)) ) {
#endif //TEMP_RESIDENCY_TIME
if( (millis() - codenum) > 1000UL )
{ //Print Temp Reading and remaining time every 1 second while heating up/cooling down
SERIAL_PROTOCOLPGM("T:");
SERIAL_PROTOCOL_F(degHotend(tmp_extruder),1);
SERIAL_PROTOCOLPGM(" E:");
SERIAL_PROTOCOL((int)tmp_extruder);
#ifdef TEMP_RESIDENCY_TIME
SERIAL_PROTOCOLPGM(" W:");
if(residencyStart > -1)
{
codenum = ((TEMP_RESIDENCY_TIME * 1000UL) - (millis() - residencyStart)) / 1000UL;
SERIAL_PROTOCOLLN( codenum );
}
else
{
SERIAL_PROTOCOLLN( "?" );
}
#else
SERIAL_PROTOCOLLN("");
#endif
codenum = millis();
}
manage_heater();
manage_inactivity();
lcd_update();
#ifdef TEMP_RESIDENCY_TIME
/* start/restart the TEMP_RESIDENCY_TIME timer whenever we reach target temp for the first time
or when current temp falls outside the hysteresis after target temp was reached */
if ((residencyStart == -1 && target_direction && (degHotend(tmp_extruder) >= (degTargetHotend(tmp_extruder)-TEMP_WINDOW))) ||
(residencyStart == -1 && !target_direction && (degHotend(tmp_extruder) <= (degTargetHotend(tmp_extruder)+TEMP_WINDOW))) ||
(residencyStart > -1 && labs(degHotend(tmp_extruder) - degTargetHotend(tmp_extruder)) > TEMP_HYSTERESIS) )
{
residencyStart = millis();
}
#endif //TEMP_RESIDENCY_TIME
}
LCD_MESSAGEPGM(MSG_HEATING_COMPLETE);
starttime=millis();
previous_millis_cmd = millis();
}
break;
case 190: // M190 - Wait for bed heater to reach target.
#if TEMP_BED_PIN > -1
LCD_MESSAGEPGM(MSG_BED_HEATING);
if (code_seen('S')) setTargetBed(code_value());
codenum = millis();
while(isHeatingBed())
{
if(( millis() - codenum) > 1000 ) //Print Temp Reading every 1 second while heating up.
{
float tt=degHotend(active_extruder);
SERIAL_PROTOCOLPGM("T:");
SERIAL_PROTOCOL(tt);
SERIAL_PROTOCOLPGM(" E:");
SERIAL_PROTOCOL((int)active_extruder);
SERIAL_PROTOCOLPGM(" B:");
SERIAL_PROTOCOL_F(degBed(),1);
SERIAL_PROTOCOLLN("");
codenum = millis();
}
manage_heater();
manage_inactivity();
lcd_update();
}
LCD_MESSAGEPGM(MSG_BED_DONE);
previous_millis_cmd = millis();
#endif
break;
#if FAN_PIN > -1
case 106: //M106 Fan On
if (code_seen('S')){
fanSpeed=constrain(code_value(),0,255);
}
else {
fanSpeed=255;
}
break;
case 107: //M107 Fan Off
fanSpeed = 0;
break;
#endif //FAN_PIN
#if (PS_ON_PIN > -1)
case 80: // M80 - ATX Power On
SET_OUTPUT(PS_ON_PIN); //GND
WRITE(PS_ON_PIN, PS_ON_AWAKE);
break;
#endif
case 81: // M81 - ATX Power Off
#if defined SUICIDE_PIN && SUICIDE_PIN > -1
st_synchronize();
suicide();
#elif (PS_ON_PIN > -1)
SET_OUTPUT(PS_ON_PIN);
WRITE(PS_ON_PIN, PS_ON_ASLEEP);
#endif
break;
case 82:
axis_relative_modes[3] = false;
break;
case 83:
axis_relative_modes[3] = true;
break;
case 18: //compatibility
case 84: // M84
if(code_seen('S')){
stepper_inactive_time = code_value() * 1000;
}
else
{
bool all_axis = !((code_seen(axis_codes[0])) || (code_seen(axis_codes[1])) || (code_seen(axis_codes[2]))|| (code_seen(axis_codes[3])));
if(all_axis)
{
st_synchronize();
disable_e0();
disable_e1();
disable_e2();
finishAndDisableSteppers();
}
else
{
st_synchronize();
if(code_seen('X')) disable_x();
if(code_seen('Y')) disable_y();
if(code_seen('Z')) disable_z();
#if ((E0_ENABLE_PIN != X_ENABLE_PIN) && (E1_ENABLE_PIN != Y_ENABLE_PIN)) // Only enable on boards that have seperate ENABLE_PINS
if(code_seen('E')) {
disable_e0();
disable_e1();
disable_e2();
}
#endif
}
}
break;
case 85: // M85
code_seen('S');
max_inactive_time = code_value() * 1000;
break;
case 92: // M92
for(int8_t i=0; i < NUM_AXIS; i++)
{
if(code_seen(axis_codes[i]))
{
if(i == 3) { // E
float value = code_value();
if(value < 20.0) {
float factor = axis_steps_per_unit[i] / value; // increase e constants if M92 E14 is given for netfab.
max_e_jerk *= factor;
max_feedrate[i] *= factor;
axis_steps_per_sqr_second[i] *= factor;
}
axis_steps_per_unit[i] = value;
}
else {
axis_steps_per_unit[i] = code_value();
}
}
}
break;
case 115: // M115
SERIAL_PROTOCOLPGM(MSG_M115_REPORT);
break;
case 117: // M117 display message
starpos = (strchr(strchr_pointer + 5,'*'));
if(starpos!=NULL)
*(starpos-1)='\0';
lcd_setstatus(strchr_pointer + 5);
break;
case 114: // M114
SERIAL_PROTOCOLPGM("X:");
SERIAL_PROTOCOL(current_position[X_AXIS]);
SERIAL_PROTOCOLPGM("Y:");
SERIAL_PROTOCOL(current_position[Y_AXIS]);
SERIAL_PROTOCOLPGM("Z:");
SERIAL_PROTOCOL(current_position[Z_AXIS]);
SERIAL_PROTOCOLPGM("E:");
SERIAL_PROTOCOL(current_position[E_AXIS]);
SERIAL_PROTOCOLPGM(MSG_COUNT_X);
SERIAL_PROTOCOL(float(st_get_position(X_AXIS))/axis_steps_per_unit[X_AXIS]);
SERIAL_PROTOCOLPGM("Y:");
SERIAL_PROTOCOL(float(st_get_position(Y_AXIS))/axis_steps_per_unit[Y_AXIS]);
SERIAL_PROTOCOLPGM("Z:");
SERIAL_PROTOCOL(float(st_get_position(Z_AXIS))/axis_steps_per_unit[Z_AXIS]);
SERIAL_PROTOCOLLN("");
break;
case 120: // M120
enable_endstops(false) ;
break;
case 121: // M121
enable_endstops(true) ;
break;
case 119: // M119
SERIAL_PROTOCOLLN(MSG_M119_REPORT);
#if (X_MIN_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_X_MIN);
SERIAL_PROTOCOLLN(((READ(X_MIN_PIN)^X_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (X_MAX_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_X_MAX);
SERIAL_PROTOCOLLN(((READ(X_MAX_PIN)^X_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (Y_MIN_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_Y_MIN);
SERIAL_PROTOCOLLN(((READ(Y_MIN_PIN)^Y_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (Y_MAX_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_Y_MAX);
SERIAL_PROTOCOLLN(((READ(Y_MAX_PIN)^Y_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (Z_MIN_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_Z_MIN);
SERIAL_PROTOCOLLN(((READ(Z_MIN_PIN)^Z_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (Z_MAX_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_Z_MAX);
SERIAL_PROTOCOLLN(((READ(Z_MAX_PIN)^Z_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
break;
//TODO: update for all axis, use for loop
case 201: // M201
for(int8_t i=0; i < NUM_AXIS; i++)
{
if(code_seen(axis_codes[i]))
{
max_acceleration_units_per_sq_second[i] = code_value();
axis_steps_per_sqr_second[i] = code_value() * axis_steps_per_unit[i];
}
}
break;
#if 0 // Not used for Sprinter/grbl gen6
case 202: // M202
for(int8_t i=0; i < NUM_AXIS; i++) {
if(code_seen(axis_codes[i])) axis_travel_steps_per_sqr_second[i] = code_value() * axis_steps_per_unit[i];
}
break;
#endif
case 203: // M203 max feedrate mm/sec
for(int8_t i=0; i < NUM_AXIS; i++) {
if(code_seen(axis_codes[i])) max_feedrate[i] = code_value();
}
break;
case 204: // M204 acclereration S normal moves T filmanent only moves
{
if(code_seen('S')) acceleration = code_value() ;
if(code_seen('T')) retract_acceleration = code_value() ;
}
break;
case 205: //M205 advanced settings: minimum travel speed S=while printing T=travel only, B=minimum segment time X= maximum xy jerk, Z=maximum Z jerk
{
if(code_seen('S')) minimumfeedrate = code_value();
if(code_seen('T')) mintravelfeedrate = code_value();
if(code_seen('B')) minsegmenttime = code_value() ;
if(code_seen('X')) max_xy_jerk = code_value() ;
if(code_seen('Z')) max_z_jerk = code_value() ;
if(code_seen('E')) max_e_jerk = code_value() ;
}
break;
case 206: // M206 additional homeing offset
for(int8_t i=0; i < 3; i++)
{
if(code_seen(axis_codes[i])) add_homeing[i] = code_value();
}
break;
#ifdef FWRETRACT
case 207: //M207 - set retract length S[positive mm] F[feedrate mm/sec] Z[additional zlift/hop]
{
if(code_seen('S'))
{
retract_length = code_value() ;
}
if(code_seen('F'))
{
retract_feedrate = code_value() ;
}
if(code_seen('Z'))
{
retract_zlift = code_value() ;
}
}break;
case 208: // M208 - set retract recover length S[positive mm surplus to the M207 S*] F[feedrate mm/sec]
{
if(code_seen('S'))
{
retract_recover_length = code_value() ;
}
if(code_seen('F'))
{
retract_recover_feedrate = code_value() ;
}
}break;
case 209: // M209 - S<1=true/0=false> enable automatic retract detect if the slicer did not support G10/11: every normal extrude-only move will be classified as retract depending on the direction.
{
if(code_seen('S'))
{
int t= code_value() ;
switch(t)
{
case 0: autoretract_enabled=false;retracted=false;break;
case 1: autoretract_enabled=true;retracted=false;break;
default:
SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_UNKNOWN_COMMAND);
SERIAL_ECHO(cmdbuffer[bufindr]);
SERIAL_ECHOLNPGM("\"");
}
}
}break;
#endif // FWRETRACT
#if EXTRUDERS > 1
case 218: // M218 - set hotend offset (in mm), T<extruder_number> X<offset_on_X> Y<offset_on_Y>
{
if(setTargetedHotend(218)){
break;
}
if(code_seen('X'))
{
extruder_offset[X_AXIS][tmp_extruder] = code_value();
}
if(code_seen('Y'))
{
extruder_offset[Y_AXIS][tmp_extruder] = code_value();
}
SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_HOTEND_OFFSET);
for(tmp_extruder = 0; tmp_extruder < EXTRUDERS; tmp_extruder++)
{
SERIAL_ECHO(" ");
SERIAL_ECHO(extruder_offset[X_AXIS][tmp_extruder]);
SERIAL_ECHO(",");
SERIAL_ECHO(extruder_offset[Y_AXIS][tmp_extruder]);
}
SERIAL_ECHOLN("");
}break;
#endif
case 220: // M220 S<factor in percent>- set speed factor override percentage
{
if(code_seen('S'))
{
feedmultiply = code_value() ;
}
}
break;
case 221: // M221 S<factor in percent>- set extrude factor override percentage
{
if(code_seen('S'))
{
extrudemultiply = code_value() ;
}
}
break;
#ifdef PIDTEMP
case 301: // M301
{
if(code_seen('P')) Kp = code_value();
if(code_seen('I')) Ki = code_value()*PID_dT;
if(code_seen('D')) Kd = code_value()/PID_dT;
#ifdef PID_ADD_EXTRUSION_RATE
if(code_seen('C')) Kc = code_value();
#endif
updatePID();
SERIAL_PROTOCOL(MSG_OK);
SERIAL_PROTOCOL(" p:");
SERIAL_PROTOCOL(Kp);
SERIAL_PROTOCOL(" i:");
SERIAL_PROTOCOL(Ki/PID_dT);
SERIAL_PROTOCOL(" d:");
SERIAL_PROTOCOL(Kd*PID_dT);
#ifdef PID_ADD_EXTRUSION_RATE
SERIAL_PROTOCOL(" c:");
SERIAL_PROTOCOL(Kc*PID_dT);
#endif
SERIAL_PROTOCOLLN("");
}
break;
#endif //PIDTEMP
#ifdef PIDTEMPBED
case 304: // M304
{
if(code_seen('P')) bedKp = code_value();
if(code_seen('I')) bedKi = code_value()*PID_dT;
if(code_seen('D')) bedKd = code_value()/PID_dT;
updatePID();
SERIAL_PROTOCOL(MSG_OK);
SERIAL_PROTOCOL(" p:");
SERIAL_PROTOCOL(bedKp);
SERIAL_PROTOCOL(" i:");
SERIAL_PROTOCOL(bedKi/PID_dT);
SERIAL_PROTOCOL(" d:");
SERIAL_PROTOCOL(bedKd*PID_dT);
SERIAL_PROTOCOLLN("");
}
break;
#endif //PIDTEMP
case 240: // M240 Triggers a camera by emulating a Canon RC-1 : http://www.doc-diy.net/photo/rc-1_hacked/
{
#ifdef PHOTOGRAPH_PIN
#if (PHOTOGRAPH_PIN > -1)
const uint8_t NUM_PULSES=16;
const float PULSE_LENGTH=0.01524;
for(int i=0; i < NUM_PULSES; i++) {
WRITE(PHOTOGRAPH_PIN, HIGH);
_delay_ms(PULSE_LENGTH);
WRITE(PHOTOGRAPH_PIN, LOW);
_delay_ms(PULSE_LENGTH);
}
delay(7.33);
for(int i=0; i < NUM_PULSES; i++) {
WRITE(PHOTOGRAPH_PIN, HIGH);
_delay_ms(PULSE_LENGTH);
WRITE(PHOTOGRAPH_PIN, LOW);
_delay_ms(PULSE_LENGTH);
}
#endif
#endif
}
break;
case 302: // allow cold extrudes
{
allow_cold_extrudes(true);
}
break;
case 303: // M303 PID autotune
{
float temp = 150.0;
int e=0;
int c=5;
if (code_seen('E')) e=code_value();
if (e<0)
temp=70;
if (code_seen('S')) temp=code_value();
if (code_seen('C')) c=code_value();
PID_autotune(temp, e, c);
}
break;
case 400: // M400 finish all moves
{
st_synchronize();
}
break;
case 500: // M500 Store settings in EEPROM
{
Config_StoreSettings();
}
break;
case 501: // M501 Read settings from EEPROM
{
Config_RetrieveSettings();
}
break;
case 502: // M502 Revert to default settings
{
Config_ResetDefault();
}
break;
case 503: // M503 print settings currently in memory
{
Config_PrintSettings();
}
break;
#ifdef ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED
case 540:
{
if(code_seen('S')) abort_on_endstop_hit = code_value() > 0;
}
break;
#endif
#ifdef FILAMENTCHANGEENABLE
case 600: //Pause for filament change X[pos] Y[pos] Z[relative lift] E[initial retract] L[later retract distance for removal]
{
float target[4];
float lastpos[4];
target[X_AXIS]=current_position[X_AXIS];
target[Y_AXIS]=current_position[Y_AXIS];
target[Z_AXIS]=current_position[Z_AXIS];
target[E_AXIS]=current_position[E_AXIS];
lastpos[X_AXIS]=current_position[X_AXIS];
lastpos[Y_AXIS]=current_position[Y_AXIS];
lastpos[Z_AXIS]=current_position[Z_AXIS];
lastpos[E_AXIS]=current_position[E_AXIS];
//retract by E
if(code_seen('E'))
{
target[E_AXIS]+= code_value();
}
else
{
#ifdef FILAMENTCHANGE_FIRSTRETRACT
target[E_AXIS]+= FILAMENTCHANGE_FIRSTRETRACT ;
#endif
}
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder);
//lift Z
if(code_seen('Z'))
{
target[Z_AXIS]+= code_value();
}
else
{
#ifdef FILAMENTCHANGE_ZADD
target[Z_AXIS]+= FILAMENTCHANGE_ZADD ;
#endif
}
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder);
//move xy
if(code_seen('X'))
{
target[X_AXIS]+= code_value();
}
else
{
#ifdef FILAMENTCHANGE_XPOS
target[X_AXIS]= FILAMENTCHANGE_XPOS ;
#endif
}
if(code_seen('Y'))
{
target[Y_AXIS]= code_value();
}
else
{
#ifdef FILAMENTCHANGE_YPOS
target[Y_AXIS]= FILAMENTCHANGE_YPOS ;
#endif
}
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder);
if(code_seen('L'))
{
target[E_AXIS]+= code_value();
}
else
{
#ifdef FILAMENTCHANGE_FINALRETRACT
target[E_AXIS]+= FILAMENTCHANGE_FINALRETRACT ;
#endif
}
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder);
//finish moves
st_synchronize();
//disable extruder steppers so filament can be removed
disable_e0();
disable_e1();
disable_e2();
delay(100);
LCD_ALERTMESSAGEPGM(MSG_FILAMENTCHANGE);
uint8_t cnt=0;
while(!LCD_CLICKED){
cnt++;
manage_heater();
manage_inactivity();
lcd_update();
#if BEEPER > -1
if(cnt==0)
{
SET_OUTPUT(BEEPER);
WRITE(BEEPER,HIGH);
delay(3);
WRITE(BEEPER,LOW);
delay(3);
}
#endif
}
//return to normal
if(code_seen('L'))
{
target[E_AXIS]+= -code_value();
}
else
{
#ifdef FILAMENTCHANGE_FINALRETRACT
target[E_AXIS]+=(-1)*FILAMENTCHANGE_FINALRETRACT ;
#endif
}
current_position[E_AXIS]=target[E_AXIS]; //the long retract of L is compensated by manual filament feeding
plan_set_e_position(current_position[E_AXIS]);
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder); //should do nothing
plan_buffer_line(lastpos[X_AXIS], lastpos[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder); //move xy back
plan_buffer_line(lastpos[X_AXIS], lastpos[Y_AXIS], lastpos[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder); //move z back
plan_buffer_line(lastpos[X_AXIS], lastpos[Y_AXIS], lastpos[Z_AXIS], lastpos[E_AXIS], feedrate/60, active_extruder); //final untretract
}
break;
#endif //FILAMENTCHANGEENABLE
case 907: // M907 Set digital trimpot motor current using axis codes.
{
#if DIGIPOTSS_PIN > -1
for(int i=0;i<=NUM_AXIS;i++) if(code_seen(axis_codes[i])) digipot_current(i,code_value());
if(code_seen('B')) digipot_current(4,code_value());
if(code_seen('S')) for(int i=0;i<=4;i++) digipot_current(i,code_value());
#endif
}
case 908: // M908 Control digital trimpot directly.
{
#if DIGIPOTSS_PIN > -1
uint8_t channel,current;
if(code_seen('P')) channel=code_value();
if(code_seen('S')) current=code_value();
digitalPotWrite(channel, current);
#endif
}
break;
case 350: // M350 Set microstepping mode. Warning: Steps per unit remains unchanged. S code sets stepping mode for all drivers.
{
#if X_MS1_PIN > -1
if(code_seen('S')) for(int i=0;i<=4;i++) microstep_mode(i,code_value());
for(int i=0;i<=NUM_AXIS;i++) if(code_seen(axis_codes[i])) microstep_mode(i,(uint8_t)code_value());
if(code_seen('B')) microstep_mode(4,code_value());
microstep_readings();
#endif
}
break;
case 351: // M351 Toggle MS1 MS2 pins directly, S# determines MS1 or MS2, X# sets the pin high/low.
{
#if X_MS1_PIN > -1
if(code_seen('S')) switch((int)code_value())
{
case 1:
for(int i=0;i<=NUM_AXIS;i++) if(code_seen(axis_codes[i])) microstep_ms(i,code_value(),-1);
if(code_seen('B')) microstep_ms(4,code_value(),-1);
break;
case 2:
for(int i=0;i<=NUM_AXIS;i++) if(code_seen(axis_codes[i])) microstep_ms(i,-1,code_value());
if(code_seen('B')) microstep_ms(4,-1,code_value());
break;
}
microstep_readings();
#endif
}
break;
case 999: // M999: Restart after being stopped
Stopped = false;
lcd_reset_alert_level();
gcode_LastN = Stopped_gcode_LastN;
FlushSerialRequestResend();
break;
}
}
else if(code_seen('T'))
{
tmp_extruder = code_value();
if(tmp_extruder >= EXTRUDERS) {
SERIAL_ECHO_START;
SERIAL_ECHO("T");
SERIAL_ECHO(tmp_extruder);
SERIAL_ECHOLN(MSG_INVALID_EXTRUDER);
}
else {
boolean make_move = false;
if(code_seen('F')) {
make_move = true;
next_feedrate = code_value();
if(next_feedrate > 0.0) {
feedrate = next_feedrate;
}
}
#if EXTRUDERS > 1
if(tmp_extruder != active_extruder) {
// Save current position to return to after applying extruder offset
memcpy(destination, current_position, sizeof(destination));
// Offset extruder (only by XY)
int i;
for(i = 0; i < 2; i++) {
current_position[i] = current_position[i] -
extruder_offset[i][active_extruder] +
extruder_offset[i][tmp_extruder];
}
// Set the new active extruder and position
active_extruder = tmp_extruder;
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
// Move to the old position if 'F' was in the parameters
if(make_move && Stopped == false) {
prepare_move();
}
} }
#endif else
SERIAL_ECHO_START; {
SERIAL_ECHO(MSG_ACTIVE_EXTRUDER); process_commands();
SERIAL_PROTOCOLLN((int)active_extruder); }
} #else
} process_commands();
#endif //SDSUPPORT
else buflen = (buflen-1);
{ bufindr = (bufindr + 1)%BUFSIZE;
SERIAL_ECHO_START; }
SERIAL_ECHOPGM(MSG_UNKNOWN_COMMAND); //check heater every n milliseconds
SERIAL_ECHO(cmdbuffer[bufindr]); manage_heater();
SERIAL_ECHOLNPGM("\""); manage_inactivity();
} checkHitEndstops();
lcd_update();
ClearToSend(); }
}
void get_command()
void FlushSerialRequestResend() {
{ while( MYSERIAL.available() > 0 && buflen < BUFSIZE) {
//char cmdbuffer[bufindr][100]="Resend:"; serial_char = MYSERIAL.read();
MYSERIAL.flush(); if(serial_char == '\n' ||
SERIAL_PROTOCOLPGM(MSG_RESEND); serial_char == '\r' ||
SERIAL_PROTOCOLLN(gcode_LastN + 1); (serial_char == ':' && comment_mode == false) ||
ClearToSend(); serial_count >= (MAX_CMD_SIZE - 1) )
} {
if(!serial_count) { //if empty line
void ClearToSend() comment_mode = false; //for new command
{ return;
previous_millis_cmd = millis(); }
#ifdef SDSUPPORT cmdbuffer[bufindw][serial_count] = 0; //terminate string
if(fromsd[bufindr]) if(!comment_mode){
return; comment_mode = false; //for new command
#endif //SDSUPPORT fromsd[bufindw] = false;
SERIAL_PROTOCOLLNPGM(MSG_OK); if(strchr(cmdbuffer[bufindw], 'N') != NULL)
} {
strchr_pointer = strchr(cmdbuffer[bufindw], 'N');
void get_coordinates() gcode_N = (strtol(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL, 10));
{ if(gcode_N != gcode_LastN+1 && (strstr_P(cmdbuffer[bufindw], PSTR("M110")) == NULL) ) {
bool seen[4]={false,false,false,false}; SERIAL_ERROR_START;
for(int8_t i=0; i < NUM_AXIS; i++) { SERIAL_ERRORPGM(MSG_ERR_LINE_NO);
if(code_seen(axis_codes[i])) SERIAL_ERRORLN(gcode_LastN);
{ //Serial.println(gcode_N);
destination[i] = (float)code_value() + (axis_relative_modes[i] || relative_mode)*current_position[i]; FlushSerialRequestResend();
seen[i]=true; serial_count = 0;
} return;
else destination[i] = current_position[i]; //Are these else lines really needed? }
}
if(code_seen('F')) { if(strchr(cmdbuffer[bufindw], '*') != NULL)
next_feedrate = code_value(); {
if(next_feedrate > 0.0) feedrate = next_feedrate; byte checksum = 0;
} byte count = 0;
#ifdef FWRETRACT while(cmdbuffer[bufindw][count] != '*') checksum = checksum^cmdbuffer[bufindw][count++];
if(autoretract_enabled) strchr_pointer = strchr(cmdbuffer[bufindw], '*');
if( !(seen[X_AXIS] || seen[Y_AXIS] || seen[Z_AXIS]) && seen[E_AXIS])
{ if( (int)(strtod(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL)) != checksum) {
float echange=destination[E_AXIS]-current_position[E_AXIS]; SERIAL_ERROR_START;
if(echange<-MIN_RETRACT) //retract SERIAL_ERRORPGM(MSG_ERR_CHECKSUM_MISMATCH);
{ SERIAL_ERRORLN(gcode_LastN);
if(!retracted) FlushSerialRequestResend();
{ serial_count = 0;
return;
destination[Z_AXIS]+=retract_zlift; //not sure why chaninging current_position negatively does not work. }
//if slicer retracted by echange=-1mm and you want to retract 3mm, corrrectede=-2mm additionally //if no errors, continue parsing
float correctede=-echange-retract_length; }
//to generate the additional steps, not the destination is changed, but inversely the current position else
current_position[E_AXIS]+=-correctede; {
feedrate=retract_feedrate; SERIAL_ERROR_START;
retracted=true; SERIAL_ERRORPGM(MSG_ERR_NO_CHECKSUM);
} SERIAL_ERRORLN(gcode_LastN);
FlushSerialRequestResend();
} serial_count = 0;
else return;
if(echange>MIN_RETRACT) //retract_recover }
{
if(retracted) gcode_LastN = gcode_N;
{ //if no errors, continue parsing
//current_position[Z_AXIS]+=-retract_zlift; }
//if slicer retracted_recovered by echange=+1mm and you want to retract_recover 3mm, corrrectede=2mm additionally else // if we don't receive 'N' but still see '*'
float correctede=-echange+1*retract_length+retract_recover_length; //total unretract=retract_length+retract_recover_length[surplus] {
current_position[E_AXIS]+=correctede; //to generate the additional steps, not the destination is changed, but inversely the current position if((strchr(cmdbuffer[bufindw], '*') != NULL))
feedrate=retract_recover_feedrate; {
retracted=false; SERIAL_ERROR_START;
} SERIAL_ERRORPGM(MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM);
} SERIAL_ERRORLN(gcode_LastN);
serial_count = 0;
} return;
#endif //FWRETRACT }
} }
if((strchr(cmdbuffer[bufindw], 'G') != NULL)){
void get_arc_coordinates() strchr_pointer = strchr(cmdbuffer[bufindw], 'G');
{ switch((int)((strtod(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL)))){
#ifdef SF_ARC_FIX case 0:
bool relative_mode_backup = relative_mode; case 1:
relative_mode = true; case 2:
#endif case 3:
get_coordinates(); if(Stopped == false) { // If printer is stopped by an error the G[0-3] codes are ignored.
#ifdef SF_ARC_FIX #ifdef SDSUPPORT
relative_mode=relative_mode_backup; if(card.saving)
#endif break;
#endif //SDSUPPORT
if(code_seen('I')) { SERIAL_PROTOCOLLNPGM(MSG_OK);
offset[0] = code_value(); }
} else {
else { SERIAL_ERRORLNPGM(MSG_ERR_STOPPED);
offset[0] = 0.0; LCD_MESSAGEPGM(MSG_STOPPED);
} }
if(code_seen('J')) { break;
offset[1] = code_value(); default:
} break;
else { }
offset[1] = 0.0;
} }
} bufindw = (bufindw + 1)%BUFSIZE;
buflen += 1;
void clamp_to_software_endstops(float target[3]) }
{ serial_count = 0; //clear buffer
if (min_software_endstops) { }
if (target[X_AXIS] < min_pos[X_AXIS]) target[X_AXIS] = min_pos[X_AXIS]; else
if (target[Y_AXIS] < min_pos[Y_AXIS]) target[Y_AXIS] = min_pos[Y_AXIS]; {
if (target[Z_AXIS] < min_pos[Z_AXIS]) target[Z_AXIS] = min_pos[Z_AXIS]; if(serial_char == ';') comment_mode = true;
} if(!comment_mode) cmdbuffer[bufindw][serial_count++] = serial_char;
}
if (max_software_endstops) { }
if (target[X_AXIS] > max_pos[X_AXIS]) target[X_AXIS] = max_pos[X_AXIS]; #ifdef SDSUPPORT
if (target[Y_AXIS] > max_pos[Y_AXIS]) target[Y_AXIS] = max_pos[Y_AXIS]; if(!card.sdprinting || serial_count!=0){
if (target[Z_AXIS] > max_pos[Z_AXIS]) target[Z_AXIS] = max_pos[Z_AXIS]; return;
} }
} while( !card.eof() && buflen < BUFSIZE) {
int16_t n=card.get();
void prepare_move() serial_char = (char)n;
{ if(serial_char == '\n' ||
clamp_to_software_endstops(destination); serial_char == '\r' ||
(serial_char == ':' && comment_mode == false) ||
previous_millis_cmd = millis(); serial_count >= (MAX_CMD_SIZE - 1)||n==-1)
// Do not use feedmultiply for E or Z only moves {
if( (current_position[X_AXIS] == destination [X_AXIS]) && (current_position[Y_AXIS] == destination [Y_AXIS])) { if(card.eof()){
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder); SERIAL_PROTOCOLLNPGM(MSG_FILE_PRINTED);
} stoptime=millis();
else { char time[30];
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate*feedmultiply/60/100.0, active_extruder); unsigned long t=(stoptime-starttime)/1000;
} int hours, minutes;
for(int8_t i=0; i < NUM_AXIS; i++) { minutes=(t/60)%60;
current_position[i] = destination[i]; hours=t/60/60;
} sprintf_P(time, PSTR("%i hours %i minutes"),hours, minutes);
} SERIAL_ECHO_START;
SERIAL_ECHOLN(time);
void prepare_arc_move(char isclockwise) { lcd_setstatus(time);
float r = hypot(offset[X_AXIS], offset[Y_AXIS]); // Compute arc radius for mc_arc card.printingHasFinished();
card.checkautostart(true);
// Trace the arc
mc_arc(current_position, destination, offset, X_AXIS, Y_AXIS, Z_AXIS, feedrate*feedmultiply/60/100.0, r, isclockwise, active_extruder); }
if(!serial_count)
// As far as the parser is concerned, the position is now == target. In reality the {
// motion control system might still be processing the action and the real tool position comment_mode = false; //for new command
// in any intermediate location. return; //if empty line
for(int8_t i=0; i < NUM_AXIS; i++) { }
current_position[i] = destination[i]; cmdbuffer[bufindw][serial_count] = 0; //terminate string
} // if(!comment_mode){
previous_millis_cmd = millis(); fromsd[bufindw] = true;
} buflen += 1;
bufindw = (bufindw + 1)%BUFSIZE;
#ifdef CONTROLLERFAN_PIN // }
unsigned long lastMotor = 0; //Save the time for when a motor was turned on last comment_mode = false; //for new command
unsigned long lastMotorCheck = 0; serial_count = 0; //clear buffer
}
void controllerFan() else
{ {
if ((millis() - lastMotorCheck) >= 2500) //Not a time critical function, so we only check every 2500ms if(serial_char == ';') comment_mode = true;
{ if(!comment_mode) cmdbuffer[bufindw][serial_count++] = serial_char;
lastMotorCheck = millis(); }
}
if(!READ(X_ENABLE_PIN) || !READ(Y_ENABLE_PIN) || !READ(Z_ENABLE_PIN)
#if EXTRUDERS > 2 #endif //SDSUPPORT
|| !READ(E2_ENABLE_PIN)
#endif }
#if EXTRUDER > 1
|| !READ(E2_ENABLE_PIN)
#endif float code_value()
|| !READ(E0_ENABLE_PIN)) //If any of the drivers are enabled... {
{ return (strtod(&cmdbuffer[bufindr][strchr_pointer - cmdbuffer[bufindr] + 1], NULL));
lastMotor = millis(); //... set time to NOW so the fan will turn on }
}
long code_value_long()
if ((millis() - lastMotor) >= (CONTROLLERFAN_SEC*1000UL) || lastMotor == 0) //If the last time any driver was enabled, is longer since than CONTROLLERSEC... {
{ return (strtol(&cmdbuffer[bufindr][strchr_pointer - cmdbuffer[bufindr] + 1], NULL, 10));
WRITE(CONTROLLERFAN_PIN, LOW); //... turn the fan off }
}
else bool code_seen(char code)
{ {
WRITE(CONTROLLERFAN_PIN, HIGH); //... turn the fan on strchr_pointer = strchr(cmdbuffer[bufindr], code);
} return (strchr_pointer != NULL); //Return True if a character was found
} }
}
#endif #define DEFINE_PGM_READ_ANY(type, reader) \
static inline type pgm_read_any(const type *p) \
void manage_inactivity() { return pgm_read_##reader##_near(p); }
{
if( (millis() - previous_millis_cmd) > max_inactive_time ) DEFINE_PGM_READ_ANY(float, float);
if(max_inactive_time) DEFINE_PGM_READ_ANY(signed char, byte);
kill();
if(stepper_inactive_time) { #define XYZ_CONSTS_FROM_CONFIG(type, array, CONFIG) \
if( (millis() - previous_millis_cmd) > stepper_inactive_time ) static const PROGMEM type array##_P[3] = \
{ { X_##CONFIG, Y_##CONFIG, Z_##CONFIG }; \
if(blocks_queued() == false) { static inline type array(int axis) \
disable_x(); { return pgm_read_any(&array##_P[axis]); }
disable_y();
disable_z(); XYZ_CONSTS_FROM_CONFIG(float, base_min_pos, MIN_POS);
disable_e0(); XYZ_CONSTS_FROM_CONFIG(float, base_max_pos, MAX_POS);
disable_e1(); XYZ_CONSTS_FROM_CONFIG(float, base_home_pos, HOME_POS);
disable_e2(); XYZ_CONSTS_FROM_CONFIG(float, max_length, MAX_LENGTH);
} XYZ_CONSTS_FROM_CONFIG(float, home_retract_mm, HOME_RETRACT_MM);
} XYZ_CONSTS_FROM_CONFIG(signed char, home_dir, HOME_DIR);
}
#if( KILL_PIN>-1 ) static void axis_is_at_home(int axis) {
if( 0 == READ(KILL_PIN) ) current_position[axis] = base_home_pos(axis) + add_homeing[axis];
kill(); min_pos[axis] = base_min_pos(axis) + add_homeing[axis];
#endif max_pos[axis] = base_max_pos(axis) + add_homeing[axis];
#ifdef CONTROLLERFAN_PIN }
controllerFan(); //Check if fan should be turned on to cool stepper drivers down
#endif static void homeaxis(int axis) {
#ifdef EXTRUDER_RUNOUT_PREVENT #define HOMEAXIS_DO(LETTER) \
if( (millis() - previous_millis_cmd) > EXTRUDER_RUNOUT_SECONDS*1000 ) ((LETTER##_MIN_PIN > -1 && LETTER##_HOME_DIR==-1) || (LETTER##_MAX_PIN > -1 && LETTER##_HOME_DIR==1))
if(degHotend(active_extruder)>EXTRUDER_RUNOUT_MINTEMP)
{ if (axis==X_AXIS ? HOMEAXIS_DO(X) :
bool oldstatus=READ(E0_ENABLE_PIN); axis==Y_AXIS ? HOMEAXIS_DO(Y) :
enable_e0(); axis==Z_AXIS ? HOMEAXIS_DO(Z) :
float oldepos=current_position[E_AXIS]; 0) {
float oldedes=destination[E_AXIS]; current_position[axis] = 0;
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
current_position[E_AXIS]+EXTRUDER_RUNOUT_EXTRUDE*EXTRUDER_RUNOUT_ESTEPS/axis_steps_per_unit[E_AXIS], destination[axis] = 1.5 * max_length(axis) * home_dir(axis);
EXTRUDER_RUNOUT_SPEED/60.*EXTRUDER_RUNOUT_ESTEPS/axis_steps_per_unit[E_AXIS], active_extruder); feedrate = homing_feedrate[axis];
current_position[E_AXIS]=oldepos; plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
destination[E_AXIS]=oldedes; st_synchronize();
plan_set_e_position(oldepos);
previous_millis_cmd=millis(); current_position[axis] = 0;
st_synchronize(); plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
WRITE(E0_ENABLE_PIN,oldstatus); destination[axis] = -home_retract_mm(axis) * home_dir(axis);
} plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
#endif st_synchronize();
check_axes_activity();
} destination[axis] = 2*home_retract_mm(axis) * home_dir(axis);
feedrate = homing_feedrate[axis]/2 ;
void kill() plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
{ st_synchronize();
cli(); // Stop interrupts
disable_heater(); axis_is_at_home(axis);
destination[axis] = current_position[axis];
disable_x(); feedrate = 0.0;
disable_y(); endstops_hit_on_purpose();
disable_z(); }
disable_e0(); }
disable_e1(); #define HOMEAXIS(LETTER) homeaxis(LETTER##_AXIS)
disable_e2();
void process_commands()
if(PS_ON_PIN > -1) pinMode(PS_ON_PIN,INPUT); {
SERIAL_ERROR_START; unsigned long codenum; //throw away variable
SERIAL_ERRORLNPGM(MSG_ERR_KILLED); char *starpos = NULL;
LCD_ALERTMESSAGEPGM(MSG_KILLED);
suicide(); if(code_seen('G'))
while(1) { /* Intentionally left empty */ } // Wait for reset {
} switch((int)code_value())
{
void Stop() case 0: // G0 -> G1
{ case 1: // G1
disable_heater(); if(Stopped == false) {
if(Stopped == false) { get_coordinates(); // For X Y Z E F
Stopped = true; prepare_move();
Stopped_gcode_LastN = gcode_LastN; // Save last g_code for restart //ClearToSend();
SERIAL_ERROR_START; return;
SERIAL_ERRORLNPGM(MSG_ERR_STOPPED); }
LCD_MESSAGEPGM(MSG_STOPPED); //break;
} case 2: // G2 - CW ARC
} if(Stopped == false) {
get_arc_coordinates();
bool IsStopped() { return Stopped; }; prepare_arc_move(true);
return;
#ifdef FAST_PWM_FAN }
void setPwmFrequency(uint8_t pin, int val) case 3: // G3 - CCW ARC
{ if(Stopped == false) {
val &= 0x07; get_arc_coordinates();
switch(digitalPinToTimer(pin)) prepare_arc_move(false);
{ return;
}
#if defined(TCCR0A) case 4: // G4 dwell
case TIMER0A: LCD_MESSAGEPGM(MSG_DWELL);
case TIMER0B: codenum = 0;
// TCCR0B &= ~(_BV(CS00) | _BV(CS01) | _BV(CS02)); if(code_seen('P')) codenum = code_value(); // milliseconds to wait
// TCCR0B |= val; if(code_seen('S')) codenum = code_value() * 1000; // seconds to wait
break;
#endif st_synchronize();
codenum += millis(); // keep track of when we started waiting
#if defined(TCCR1A) previous_millis_cmd = millis();
case TIMER1A: while(millis() < codenum ){
case TIMER1B: manage_heater();
// TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); manage_inactivity();
// TCCR1B |= val; lcd_update();
break; }
#endif break;
#ifdef FWRETRACT
#if defined(TCCR2) case 10: // G10 retract
case TIMER2: if(!retracted)
case TIMER2: {
TCCR2 &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); destination[X_AXIS]=current_position[X_AXIS];
TCCR2 |= val; destination[Y_AXIS]=current_position[Y_AXIS];
break; destination[Z_AXIS]=current_position[Z_AXIS];
#endif current_position[Z_AXIS]+=-retract_zlift;
destination[E_AXIS]=current_position[E_AXIS]-retract_length;
#if defined(TCCR2A) feedrate=retract_feedrate;
case TIMER2A: retracted=true;
case TIMER2B: prepare_move();
TCCR2B &= ~(_BV(CS20) | _BV(CS21) | _BV(CS22)); }
TCCR2B |= val;
break; break;
#endif case 11: // G10 retract_recover
if(!retracted)
#if defined(TCCR3A) {
case TIMER3A: destination[X_AXIS]=current_position[X_AXIS];
case TIMER3B: destination[Y_AXIS]=current_position[Y_AXIS];
case TIMER3C: destination[Z_AXIS]=current_position[Z_AXIS];
TCCR3B &= ~(_BV(CS30) | _BV(CS31) | _BV(CS32));
TCCR3B |= val; current_position[Z_AXIS]+=retract_zlift;
break; current_position[E_AXIS]+=-retract_recover_length;
#endif feedrate=retract_recover_feedrate;
retracted=false;
#if defined(TCCR4A) prepare_move();
case TIMER4A: }
case TIMER4B: break;
case TIMER4C: #endif //FWRETRACT
TCCR4B &= ~(_BV(CS40) | _BV(CS41) | _BV(CS42)); case 28: //G28 Home all Axis one at a time
TCCR4B |= val; saved_feedrate = feedrate;
break; saved_feedmultiply = feedmultiply;
#endif feedmultiply = 100;
previous_millis_cmd = millis();
#if defined(TCCR5A)
case TIMER5A: enable_endstops(true);
case TIMER5B:
case TIMER5C: for(int8_t i=0; i < NUM_AXIS; i++) {
TCCR5B &= ~(_BV(CS50) | _BV(CS51) | _BV(CS52)); destination[i] = current_position[i];
TCCR5B |= val; }
break; feedrate = 0.0;
#endif home_all_axis = !((code_seen(axis_codes[0])) || (code_seen(axis_codes[1])) || (code_seen(axis_codes[2])));
} #if Z_HOME_DIR > 0 // If homing away from BED do Z first
} if((home_all_axis) || (code_seen(axis_codes[Z_AXIS]))) {
#endif //FAST_PWM_FAN HOMEAXIS(Z);
}
bool setTargetedHotend(int code){ #endif
tmp_extruder = active_extruder;
if(code_seen('T')) { #ifdef QUICK_HOME
tmp_extruder = code_value(); if((home_all_axis)||( code_seen(axis_codes[X_AXIS]) && code_seen(axis_codes[Y_AXIS])) ) //first diagonal move
if(tmp_extruder >= EXTRUDERS) { {
SERIAL_ECHO_START; current_position[X_AXIS] = 0;current_position[Y_AXIS] = 0;
switch(code){
case 104: plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
SERIAL_ECHO(MSG_M104_INVALID_EXTRUDER); destination[X_AXIS] = 1.5 * X_MAX_LENGTH * X_HOME_DIR;destination[Y_AXIS] = 1.5 * Y_MAX_LENGTH * Y_HOME_DIR;
break; feedrate = homing_feedrate[X_AXIS];
case 105: if(homing_feedrate[Y_AXIS]<feedrate)
SERIAL_ECHO(MSG_M105_INVALID_EXTRUDER); feedrate =homing_feedrate[Y_AXIS];
break; plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
case 109: st_synchronize();
SERIAL_ECHO(MSG_M109_INVALID_EXTRUDER);
break; axis_is_at_home(X_AXIS);
case 218: axis_is_at_home(Y_AXIS);
SERIAL_ECHO(MSG_M218_INVALID_EXTRUDER); plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
break; destination[X_AXIS] = current_position[X_AXIS];
} destination[Y_AXIS] = current_position[Y_AXIS];
SERIAL_ECHOLN(tmp_extruder); plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
return true; feedrate = 0.0;
} st_synchronize();
} endstops_hit_on_purpose();
return false; }
#endif
if((home_all_axis) || (code_seen(axis_codes[X_AXIS])))
{
HOMEAXIS(X);
}
if((home_all_axis) || (code_seen(axis_codes[Y_AXIS]))) {
HOMEAXIS(Y);
}
#if Z_HOME_DIR < 0 // If homing towards BED do Z last
if((home_all_axis) || (code_seen(axis_codes[Z_AXIS]))) {
HOMEAXIS(Z);
}
#endif
if(code_seen(axis_codes[X_AXIS]))
{
if(code_value_long() != 0) {
current_position[X_AXIS]=code_value()+add_homeing[0];
}
}
if(code_seen(axis_codes[Y_AXIS])) {
if(code_value_long() != 0) {
current_position[Y_AXIS]=code_value()+add_homeing[1];
}
}
if(code_seen(axis_codes[Z_AXIS])) {
if(code_value_long() != 0) {
current_position[Z_AXIS]=code_value()+add_homeing[2];
}
}
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
#ifdef ENDSTOPS_ONLY_FOR_HOMING
enable_endstops(false);
#endif
feedrate = saved_feedrate;
feedmultiply = saved_feedmultiply;
previous_millis_cmd = millis();
endstops_hit_on_purpose();
break;
case 90: // G90
relative_mode = false;
break;
case 91: // G91
relative_mode = true;
break;
case 92: // G92
if(!code_seen(axis_codes[E_AXIS]))
st_synchronize();
for(int8_t i=0; i < NUM_AXIS; i++) {
if(code_seen(axis_codes[i])) {
if(i == E_AXIS) {
current_position[i] = code_value();
plan_set_e_position(current_position[E_AXIS]);
}
else {
current_position[i] = code_value()+add_homeing[i];
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
}
}
}
break;
}
}
else if(code_seen('M'))
{
switch( (int)code_value() )
{
#ifdef ULTIPANEL
case 0: // M0 - Unconditional stop - Wait for user button press on LCD
case 1: // M1 - Conditional stop - Wait for user button press on LCD
{
LCD_MESSAGEPGM(MSG_USERWAIT);
codenum = 0;
if(code_seen('P')) codenum = code_value(); // milliseconds to wait
if(code_seen('S')) codenum = code_value() * 1000; // seconds to wait
st_synchronize();
previous_millis_cmd = millis();
if (codenum > 0){
codenum += millis(); // keep track of when we started waiting
while(millis() < codenum && !LCD_CLICKED){
manage_heater();
manage_inactivity();
lcd_update();
}
}else{
while(!LCD_CLICKED){
manage_heater();
manage_inactivity();
lcd_update();
}
}
LCD_MESSAGEPGM(MSG_RESUMING);
}
break;
#endif
case 17:
LCD_MESSAGEPGM(MSG_NO_MOVE);
enable_x();
enable_y();
enable_z();
enable_e0();
enable_e1();
enable_e2();
break;
#ifdef SDSUPPORT
case 20: // M20 - list SD card
SERIAL_PROTOCOLLNPGM(MSG_BEGIN_FILE_LIST);
card.ls();
SERIAL_PROTOCOLLNPGM(MSG_END_FILE_LIST);
break;
case 21: // M21 - init SD card
card.initsd();
break;
case 22: //M22 - release SD card
card.release();
break;
case 23: //M23 - Select file
starpos = (strchr(strchr_pointer + 4,'*'));
if(starpos!=NULL)
*(starpos-1)='\0';
card.openFile(strchr_pointer + 4,true);
break;
case 24: //M24 - Start SD print
card.startFileprint();
starttime=millis();
break;
case 25: //M25 - Pause SD print
card.pauseSDPrint();
break;
case 26: //M26 - Set SD index
if(card.cardOK && code_seen('S')) {
card.setIndex(code_value_long());
}
break;
case 27: //M27 - Get SD status
card.getStatus();
break;
case 28: //M28 - Start SD write
starpos = (strchr(strchr_pointer + 4,'*'));
if(starpos != NULL){
char* npos = strchr(cmdbuffer[bufindr], 'N');
strchr_pointer = strchr(npos,' ') + 1;
*(starpos-1) = '\0';
}
card.openFile(strchr_pointer+4,false);
break;
case 29: //M29 - Stop SD write
//processed in write to file routine above
//card,saving = false;
break;
case 30: //M30 <filename> Delete File
if (card.cardOK){
card.closefile();
starpos = (strchr(strchr_pointer + 4,'*'));
if(starpos != NULL){
char* npos = strchr(cmdbuffer[bufindr], 'N');
strchr_pointer = strchr(npos,' ') + 1;
*(starpos-1) = '\0';
}
card.removeFile(strchr_pointer + 4);
}
break;
#endif //SDSUPPORT
case 31: //M31 take time since the start of the SD print or an M109 command
{
stoptime=millis();
char time[30];
unsigned long t=(stoptime-starttime)/1000;
int sec,min;
min=t/60;
sec=t%60;
sprintf_P(time, PSTR("%i min, %i sec"), min, sec);
SERIAL_ECHO_START;
SERIAL_ECHOLN(time);
lcd_setstatus(time);
autotempShutdown();
}
break;
case 42: //M42 -Change pin status via gcode
if (code_seen('S'))
{
int pin_status = code_value();
int pin_number = LED_PIN;
if (code_seen('P') && pin_status >= 0 && pin_status <= 255)
pin_number = code_value();
for(int8_t i = 0; i < (int8_t)sizeof(sensitive_pins); i++)
{
if (sensitive_pins[i] == pin_number)
{
pin_number = -1;
break;
}
}
if (pin_number > -1)
{
pinMode(pin_number, OUTPUT);
digitalWrite(pin_number, pin_status);
analogWrite(pin_number, pin_status);
}
}
break;
case 104: // M104
if(setTargetedHotend(104)){
break;
}
if (code_seen('S')) setTargetHotend(code_value(), tmp_extruder);
setWatch();
break;
case 140: // M140 set bed temp
if (code_seen('S')) setTargetBed(code_value());
break;
case 105 : // M105
if(setTargetedHotend(105)){
break;
}
#if (TEMP_0_PIN > -1)
SERIAL_PROTOCOLPGM("ok T:");
SERIAL_PROTOCOL_F(degHotend(tmp_extruder),1);
SERIAL_PROTOCOLPGM(" /");
SERIAL_PROTOCOL_F(degTargetHotend(tmp_extruder),1);
#if TEMP_BED_PIN > -1
SERIAL_PROTOCOLPGM(" B:");
SERIAL_PROTOCOL_F(degBed(),1);
SERIAL_PROTOCOLPGM(" /");
SERIAL_PROTOCOL_F(degTargetBed(),1);
#endif //TEMP_BED_PIN
#else
SERIAL_ERROR_START;
SERIAL_ERRORLNPGM(MSG_ERR_NO_THERMISTORS);
#endif
SERIAL_PROTOCOLPGM(" @:");
SERIAL_PROTOCOL(getHeaterPower(tmp_extruder));
SERIAL_PROTOCOLPGM(" B@:");
SERIAL_PROTOCOL(getHeaterPower(-1));
SERIAL_PROTOCOLLN("");
return;
break;
case 109:
{// M109 - Wait for extruder heater to reach target.
if(setTargetedHotend(109)){
break;
}
LCD_MESSAGEPGM(MSG_HEATING);
#ifdef AUTOTEMP
autotemp_enabled=false;
#endif
if (code_seen('S')) setTargetHotend(code_value(), tmp_extruder);
#ifdef AUTOTEMP
if (code_seen('S')) autotemp_min=code_value();
if (code_seen('B')) autotemp_max=code_value();
if (code_seen('F'))
{
autotemp_factor=code_value();
autotemp_enabled=true;
}
#endif
setWatch();
codenum = millis();
/* See if we are heating up or cooling down */
bool target_direction = isHeatingHotend(tmp_extruder); // true if heating, false if cooling
#ifdef TEMP_RESIDENCY_TIME
long residencyStart;
residencyStart = -1;
/* continue to loop until we have reached the target temp
_and_ until TEMP_RESIDENCY_TIME hasn't passed since we reached it */
while((residencyStart == -1) ||
(residencyStart >= 0 && (((unsigned int) (millis() - residencyStart)) < (TEMP_RESIDENCY_TIME * 1000UL))) ) {
#else
while ( target_direction ? (isHeatingHotend(tmp_extruder)) : (isCoolingHotend(tmp_extruder)&&(CooldownNoWait==false)) ) {
#endif //TEMP_RESIDENCY_TIME
if( (millis() - codenum) > 1000UL )
{ //Print Temp Reading and remaining time every 1 second while heating up/cooling down
SERIAL_PROTOCOLPGM("T:");
SERIAL_PROTOCOL_F(degHotend(tmp_extruder),1);
SERIAL_PROTOCOLPGM(" E:");
SERIAL_PROTOCOL((int)tmp_extruder);
#ifdef TEMP_RESIDENCY_TIME
SERIAL_PROTOCOLPGM(" W:");
if(residencyStart > -1)
{
codenum = ((TEMP_RESIDENCY_TIME * 1000UL) - (millis() - residencyStart)) / 1000UL;
SERIAL_PROTOCOLLN( codenum );
}
else
{
SERIAL_PROTOCOLLN( "?" );
}
#else
SERIAL_PROTOCOLLN("");
#endif
codenum = millis();
}
manage_heater();
manage_inactivity();
lcd_update();
#ifdef TEMP_RESIDENCY_TIME
/* start/restart the TEMP_RESIDENCY_TIME timer whenever we reach target temp for the first time
or when current temp falls outside the hysteresis after target temp was reached */
if ((residencyStart == -1 && target_direction && (degHotend(tmp_extruder) >= (degTargetHotend(tmp_extruder)-TEMP_WINDOW))) ||
(residencyStart == -1 && !target_direction && (degHotend(tmp_extruder) <= (degTargetHotend(tmp_extruder)+TEMP_WINDOW))) ||
(residencyStart > -1 && labs(degHotend(tmp_extruder) - degTargetHotend(tmp_extruder)) > TEMP_HYSTERESIS) )
{
residencyStart = millis();
}
#endif //TEMP_RESIDENCY_TIME
}
LCD_MESSAGEPGM(MSG_HEATING_COMPLETE);
starttime=millis();
previous_millis_cmd = millis();
}
break;
case 190: // M190 - Wait for bed heater to reach target.
#if TEMP_BED_PIN > -1
LCD_MESSAGEPGM(MSG_BED_HEATING);
if (code_seen('S')) setTargetBed(code_value());
codenum = millis();
while(isHeatingBed())
{
if(( millis() - codenum) > 1000 ) //Print Temp Reading every 1 second while heating up.
{
float tt=degHotend(active_extruder);
SERIAL_PROTOCOLPGM("T:");
SERIAL_PROTOCOL(tt);
SERIAL_PROTOCOLPGM(" E:");
SERIAL_PROTOCOL((int)active_extruder);
SERIAL_PROTOCOLPGM(" B:");
SERIAL_PROTOCOL_F(degBed(),1);
SERIAL_PROTOCOLLN("");
codenum = millis();
}
manage_heater();
manage_inactivity();
lcd_update();
}
LCD_MESSAGEPGM(MSG_BED_DONE);
previous_millis_cmd = millis();
#endif
break;
#if FAN_PIN > -1
case 106: //M106 Fan On
if (code_seen('S')){
fanSpeed=constrain(code_value(),0,255);
}
else {
fanSpeed=255;
}
break;
case 107: //M107 Fan Off
fanSpeed = 0;
break;
#endif //FAN_PIN
#if (PS_ON_PIN > -1)
case 80: // M80 - ATX Power On
SET_OUTPUT(PS_ON_PIN); //GND
WRITE(PS_ON_PIN, PS_ON_AWAKE);
break;
#endif
case 81: // M81 - ATX Power Off
#if defined SUICIDE_PIN && SUICIDE_PIN > -1
st_synchronize();
suicide();
#elif (PS_ON_PIN > -1)
SET_OUTPUT(PS_ON_PIN);
WRITE(PS_ON_PIN, PS_ON_ASLEEP);
#endif
break;
case 82:
axis_relative_modes[3] = false;
break;
case 83:
axis_relative_modes[3] = true;
break;
case 18: //compatibility
case 84: // M84
if(code_seen('S')){
stepper_inactive_time = code_value() * 1000;
}
else
{
bool all_axis = !((code_seen(axis_codes[0])) || (code_seen(axis_codes[1])) || (code_seen(axis_codes[2]))|| (code_seen(axis_codes[3])));
if(all_axis)
{
st_synchronize();
disable_e0();
disable_e1();
disable_e2();
finishAndDisableSteppers();
}
else
{
st_synchronize();
if(code_seen('X')) disable_x();
if(code_seen('Y')) disable_y();
if(code_seen('Z')) disable_z();
#if ((E0_ENABLE_PIN != X_ENABLE_PIN) && (E1_ENABLE_PIN != Y_ENABLE_PIN)) // Only enable on boards that have seperate ENABLE_PINS
if(code_seen('E')) {
disable_e0();
disable_e1();
disable_e2();
}
#endif
}
}
break;
case 85: // M85
code_seen('S');
max_inactive_time = code_value() * 1000;
break;
case 92: // M92
for(int8_t i=0; i < NUM_AXIS; i++)
{
if(code_seen(axis_codes[i]))
{
if(i == 3) { // E
float value = code_value();
if(value < 20.0) {
float factor = axis_steps_per_unit[i] / value; // increase e constants if M92 E14 is given for netfab.
max_e_jerk *= factor;
max_feedrate[i] *= factor;
axis_steps_per_sqr_second[i] *= factor;
}
axis_steps_per_unit[i] = value;
}
else {
axis_steps_per_unit[i] = code_value();
}
}
}
break;
case 115: // M115
SERIAL_PROTOCOLPGM(MSG_M115_REPORT);
break;
case 117: // M117 display message
starpos = (strchr(strchr_pointer + 5,'*'));
if(starpos!=NULL)
*(starpos-1)='\0';
lcd_setstatus(strchr_pointer + 5);
break;
case 114: // M114
SERIAL_PROTOCOLPGM("X:");
SERIAL_PROTOCOL(current_position[X_AXIS]);
SERIAL_PROTOCOLPGM("Y:");
SERIAL_PROTOCOL(current_position[Y_AXIS]);
SERIAL_PROTOCOLPGM("Z:");
SERIAL_PROTOCOL(current_position[Z_AXIS]);
SERIAL_PROTOCOLPGM("E:");
SERIAL_PROTOCOL(current_position[E_AXIS]);
SERIAL_PROTOCOLPGM(MSG_COUNT_X);
SERIAL_PROTOCOL(float(st_get_position(X_AXIS))/axis_steps_per_unit[X_AXIS]);
SERIAL_PROTOCOLPGM("Y:");
SERIAL_PROTOCOL(float(st_get_position(Y_AXIS))/axis_steps_per_unit[Y_AXIS]);
SERIAL_PROTOCOLPGM("Z:");
SERIAL_PROTOCOL(float(st_get_position(Z_AXIS))/axis_steps_per_unit[Z_AXIS]);
SERIAL_PROTOCOLLN("");
break;
case 120: // M120
enable_endstops(false) ;
break;
case 121: // M121
enable_endstops(true) ;
break;
case 119: // M119
SERIAL_PROTOCOLLN(MSG_M119_REPORT);
#if (X_MIN_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_X_MIN);
SERIAL_PROTOCOLLN(((READ(X_MIN_PIN)^X_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (X_MAX_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_X_MAX);
SERIAL_PROTOCOLLN(((READ(X_MAX_PIN)^X_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (Y_MIN_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_Y_MIN);
SERIAL_PROTOCOLLN(((READ(Y_MIN_PIN)^Y_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (Y_MAX_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_Y_MAX);
SERIAL_PROTOCOLLN(((READ(Y_MAX_PIN)^Y_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (Z_MIN_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_Z_MIN);
SERIAL_PROTOCOLLN(((READ(Z_MIN_PIN)^Z_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
#if (Z_MAX_PIN > -1)
SERIAL_PROTOCOLPGM(MSG_Z_MAX);
SERIAL_PROTOCOLLN(((READ(Z_MAX_PIN)^Z_ENDSTOPS_INVERTING)?MSG_ENDSTOP_HIT:MSG_ENDSTOP_OPEN));
#endif
break;
//TODO: update for all axis, use for loop
case 201: // M201
for(int8_t i=0; i < NUM_AXIS; i++)
{
if(code_seen(axis_codes[i]))
{
max_acceleration_units_per_sq_second[i] = code_value();
axis_steps_per_sqr_second[i] = code_value() * axis_steps_per_unit[i];
}
}
break;
#if 0 // Not used for Sprinter/grbl gen6
case 202: // M202
for(int8_t i=0; i < NUM_AXIS; i++) {
if(code_seen(axis_codes[i])) axis_travel_steps_per_sqr_second[i] = code_value() * axis_steps_per_unit[i];
}
break;
#endif
case 203: // M203 max feedrate mm/sec
for(int8_t i=0; i < NUM_AXIS; i++) {
if(code_seen(axis_codes[i])) max_feedrate[i] = code_value();
}
break;
case 204: // M204 acclereration S normal moves T filmanent only moves
{
if(code_seen('S')) acceleration = code_value() ;
if(code_seen('T')) retract_acceleration = code_value() ;
}
break;
case 205: //M205 advanced settings: minimum travel speed S=while printing T=travel only, B=minimum segment time X= maximum xy jerk, Z=maximum Z jerk
{
if(code_seen('S')) minimumfeedrate = code_value();
if(code_seen('T')) mintravelfeedrate = code_value();
if(code_seen('B')) minsegmenttime = code_value() ;
if(code_seen('X')) max_xy_jerk = code_value() ;
if(code_seen('Z')) max_z_jerk = code_value() ;
if(code_seen('E')) max_e_jerk = code_value() ;
}
break;
case 206: // M206 additional homeing offset
for(int8_t i=0; i < 3; i++)
{
if(code_seen(axis_codes[i])) add_homeing[i] = code_value();
}
break;
#ifdef FWRETRACT
case 207: //M207 - set retract length S[positive mm] F[feedrate mm/sec] Z[additional zlift/hop]
{
if(code_seen('S'))
{
retract_length = code_value() ;
}
if(code_seen('F'))
{
retract_feedrate = code_value() ;
}
if(code_seen('Z'))
{
retract_zlift = code_value() ;
}
}break;
case 208: // M208 - set retract recover length S[positive mm surplus to the M207 S*] F[feedrate mm/sec]
{
if(code_seen('S'))
{
retract_recover_length = code_value() ;
}
if(code_seen('F'))
{
retract_recover_feedrate = code_value() ;
}
}break;
case 209: // M209 - S<1=true/0=false> enable automatic retract detect if the slicer did not support G10/11: every normal extrude-only move will be classified as retract depending on the direction.
{
if(code_seen('S'))
{
int t= code_value() ;
switch(t)
{
case 0: autoretract_enabled=false;retracted=false;break;
case 1: autoretract_enabled=true;retracted=false;break;
default:
SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_UNKNOWN_COMMAND);
SERIAL_ECHO(cmdbuffer[bufindr]);
SERIAL_ECHOLNPGM("\"");
}
}
}break;
#endif // FWRETRACT
#if EXTRUDERS > 1
case 218: // M218 - set hotend offset (in mm), T<extruder_number> X<offset_on_X> Y<offset_on_Y>
{
if(setTargetedHotend(218)){
break;
}
if(code_seen('X'))
{
extruder_offset[X_AXIS][tmp_extruder] = code_value();
}
if(code_seen('Y'))
{
extruder_offset[Y_AXIS][tmp_extruder] = code_value();
}
SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_HOTEND_OFFSET);
for(tmp_extruder = 0; tmp_extruder < EXTRUDERS; tmp_extruder++)
{
SERIAL_ECHO(" ");
SERIAL_ECHO(extruder_offset[X_AXIS][tmp_extruder]);
SERIAL_ECHO(",");
SERIAL_ECHO(extruder_offset[Y_AXIS][tmp_extruder]);
}
SERIAL_ECHOLN("");
}break;
#endif
case 220: // M220 S<factor in percent>- set speed factor override percentage
{
if(code_seen('S'))
{
feedmultiply = code_value() ;
}
}
break;
case 221: // M221 S<factor in percent>- set extrude factor override percentage
{
if(code_seen('S'))
{
extrudemultiply = code_value() ;
}
}
break;
#ifdef PIDTEMP
case 301: // M301
{
if(code_seen('P')) Kp = code_value();
if(code_seen('I')) Ki = code_value()*PID_dT;
if(code_seen('D')) Kd = code_value()/PID_dT;
#ifdef PID_ADD_EXTRUSION_RATE
if(code_seen('C')) Kc = code_value();
#endif
updatePID();
SERIAL_PROTOCOL(MSG_OK);
SERIAL_PROTOCOL(" p:");
SERIAL_PROTOCOL(Kp);
SERIAL_PROTOCOL(" i:");
SERIAL_PROTOCOL(Ki/PID_dT);
SERIAL_PROTOCOL(" d:");
SERIAL_PROTOCOL(Kd*PID_dT);
#ifdef PID_ADD_EXTRUSION_RATE
SERIAL_PROTOCOL(" c:");
SERIAL_PROTOCOL(Kc*PID_dT);
#endif
SERIAL_PROTOCOLLN("");
}
break;
#endif //PIDTEMP
#ifdef PIDTEMPBED
case 304: // M304
{
if(code_seen('P')) bedKp = code_value();
if(code_seen('I')) bedKi = code_value()*PID_dT;
if(code_seen('D')) bedKd = code_value()/PID_dT;
updatePID();
SERIAL_PROTOCOL(MSG_OK);
SERIAL_PROTOCOL(" p:");
SERIAL_PROTOCOL(bedKp);
SERIAL_PROTOCOL(" i:");
SERIAL_PROTOCOL(bedKi/PID_dT);
SERIAL_PROTOCOL(" d:");
SERIAL_PROTOCOL(bedKd*PID_dT);
SERIAL_PROTOCOLLN("");
}
break;
#endif //PIDTEMP
case 240: // M240 Triggers a camera by emulating a Canon RC-1 : http://www.doc-diy.net/photo/rc-1_hacked/
{
#ifdef PHOTOGRAPH_PIN
#if (PHOTOGRAPH_PIN > -1)
const uint8_t NUM_PULSES=16;
const float PULSE_LENGTH=0.01524;
for(int i=0; i < NUM_PULSES; i++) {
WRITE(PHOTOGRAPH_PIN, HIGH);
_delay_ms(PULSE_LENGTH);
WRITE(PHOTOGRAPH_PIN, LOW);
_delay_ms(PULSE_LENGTH);
}
delay(7.33);
for(int i=0; i < NUM_PULSES; i++) {
WRITE(PHOTOGRAPH_PIN, HIGH);
_delay_ms(PULSE_LENGTH);
WRITE(PHOTOGRAPH_PIN, LOW);
_delay_ms(PULSE_LENGTH);
}
#endif
#endif
}
break;
case 302: // allow cold extrudes
{
allow_cold_extrudes(true);
}
break;
case 303: // M303 PID autotune
{
float temp = 150.0;
int e=0;
int c=5;
if (code_seen('E')) e=code_value();
if (e<0)
temp=70;
if (code_seen('S')) temp=code_value();
if (code_seen('C')) c=code_value();
PID_autotune(temp, e, c);
}
break;
case 400: // M400 finish all moves
{
st_synchronize();
}
break;
case 500: // M500 Store settings in EEPROM
{
Config_StoreSettings();
}
break;
case 501: // M501 Read settings from EEPROM
{
Config_RetrieveSettings();
}
break;
case 502: // M502 Revert to default settings
{
Config_ResetDefault();
}
break;
case 503: // M503 print settings currently in memory
{
Config_PrintSettings();
}
break;
#ifdef ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED
case 540:
{
if(code_seen('S')) abort_on_endstop_hit = code_value() > 0;
}
break;
#endif
#ifdef FILAMENTCHANGEENABLE
case 600: //Pause for filament change X[pos] Y[pos] Z[relative lift] E[initial retract] L[later retract distance for removal]
{
float target[4];
float lastpos[4];
target[X_AXIS]=current_position[X_AXIS];
target[Y_AXIS]=current_position[Y_AXIS];
target[Z_AXIS]=current_position[Z_AXIS];
target[E_AXIS]=current_position[E_AXIS];
lastpos[X_AXIS]=current_position[X_AXIS];
lastpos[Y_AXIS]=current_position[Y_AXIS];
lastpos[Z_AXIS]=current_position[Z_AXIS];
lastpos[E_AXIS]=current_position[E_AXIS];
//retract by E
if(code_seen('E'))
{
target[E_AXIS]+= code_value();
}
else
{
#ifdef FILAMENTCHANGE_FIRSTRETRACT
target[E_AXIS]+= FILAMENTCHANGE_FIRSTRETRACT ;
#endif
}
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder);
//lift Z
if(code_seen('Z'))
{
target[Z_AXIS]+= code_value();
}
else
{
#ifdef FILAMENTCHANGE_ZADD
target[Z_AXIS]+= FILAMENTCHANGE_ZADD ;
#endif
}
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder);
//move xy
if(code_seen('X'))
{
target[X_AXIS]+= code_value();
}
else
{
#ifdef FILAMENTCHANGE_XPOS
target[X_AXIS]= FILAMENTCHANGE_XPOS ;
#endif
}
if(code_seen('Y'))
{
target[Y_AXIS]= code_value();
}
else
{
#ifdef FILAMENTCHANGE_YPOS
target[Y_AXIS]= FILAMENTCHANGE_YPOS ;
#endif
}
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder);
if(code_seen('L'))
{
target[E_AXIS]+= code_value();
}
else
{
#ifdef FILAMENTCHANGE_FINALRETRACT
target[E_AXIS]+= FILAMENTCHANGE_FINALRETRACT ;
#endif
}
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder);
//finish moves
st_synchronize();
//disable extruder steppers so filament can be removed
disable_e0();
disable_e1();
disable_e2();
delay(100);
LCD_ALERTMESSAGEPGM(MSG_FILAMENTCHANGE);
uint8_t cnt=0;
while(!LCD_CLICKED){
cnt++;
manage_heater();
manage_inactivity();
lcd_update();
#if BEEPER > -1
if(cnt==0)
{
SET_OUTPUT(BEEPER);
WRITE(BEEPER,HIGH);
delay(3);
WRITE(BEEPER,LOW);
delay(3);
}
#endif
}
//return to normal
if(code_seen('L'))
{
target[E_AXIS]+= -code_value();
}
else
{
#ifdef FILAMENTCHANGE_FINALRETRACT
target[E_AXIS]+=(-1)*FILAMENTCHANGE_FINALRETRACT ;
#endif
}
current_position[E_AXIS]=target[E_AXIS]; //the long retract of L is compensated by manual filament feeding
plan_set_e_position(current_position[E_AXIS]);
plan_buffer_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder); //should do nothing
plan_buffer_line(lastpos[X_AXIS], lastpos[Y_AXIS], target[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder); //move xy back
plan_buffer_line(lastpos[X_AXIS], lastpos[Y_AXIS], lastpos[Z_AXIS], target[E_AXIS], feedrate/60, active_extruder); //move z back
plan_buffer_line(lastpos[X_AXIS], lastpos[Y_AXIS], lastpos[Z_AXIS], lastpos[E_AXIS], feedrate/60, active_extruder); //final untretract
}
break;
#endif //FILAMENTCHANGEENABLE
case 907: // M907 Set digital trimpot motor current using axis codes.
{
#if DIGIPOTSS_PIN > -1
for(int i=0;i<=NUM_AXIS;i++) if(code_seen(axis_codes[i])) digipot_current(i,code_value());
if(code_seen('B')) digipot_current(4,code_value());
if(code_seen('S')) for(int i=0;i<=4;i++) digipot_current(i,code_value());
#endif
}
case 908: // M908 Control digital trimpot directly.
{
#if DIGIPOTSS_PIN > -1
uint8_t channel,current;
if(code_seen('P')) channel=code_value();
if(code_seen('S')) current=code_value();
digitalPotWrite(channel, current);
#endif
}
break;
case 350: // M350 Set microstepping mode. Warning: Steps per unit remains unchanged. S code sets stepping mode for all drivers.
{
#if X_MS1_PIN > -1
if(code_seen('S')) for(int i=0;i<=4;i++) microstep_mode(i,code_value());
for(int i=0;i<=NUM_AXIS;i++) if(code_seen(axis_codes[i])) microstep_mode(i,(uint8_t)code_value());
if(code_seen('B')) microstep_mode(4,code_value());
microstep_readings();
#endif
}
break;
case 351: // M351 Toggle MS1 MS2 pins directly, S# determines MS1 or MS2, X# sets the pin high/low.
{
#if X_MS1_PIN > -1
if(code_seen('S')) switch((int)code_value())
{
case 1:
for(int i=0;i<=NUM_AXIS;i++) if(code_seen(axis_codes[i])) microstep_ms(i,code_value(),-1);
if(code_seen('B')) microstep_ms(4,code_value(),-1);
break;
case 2:
for(int i=0;i<=NUM_AXIS;i++) if(code_seen(axis_codes[i])) microstep_ms(i,-1,code_value());
if(code_seen('B')) microstep_ms(4,-1,code_value());
break;
}
microstep_readings();
#endif
}
break;
case 999: // M999: Restart after being stopped
Stopped = false;
lcd_reset_alert_level();
gcode_LastN = Stopped_gcode_LastN;
FlushSerialRequestResend();
break;
}
}
else if(code_seen('T'))
{
tmp_extruder = code_value();
if(tmp_extruder >= EXTRUDERS) {
SERIAL_ECHO_START;
SERIAL_ECHO("T");
SERIAL_ECHO(tmp_extruder);
SERIAL_ECHOLN(MSG_INVALID_EXTRUDER);
}
else {
boolean make_move = false;
if(code_seen('F')) {
make_move = true;
next_feedrate = code_value();
if(next_feedrate > 0.0) {
feedrate = next_feedrate;
}
}
#if EXTRUDERS > 1
if(tmp_extruder != active_extruder) {
// Save current position to return to after applying extruder offset
memcpy(destination, current_position, sizeof(destination));
// Offset extruder (only by XY)
int i;
for(i = 0; i < 2; i++) {
current_position[i] = current_position[i] -
extruder_offset[i][active_extruder] +
extruder_offset[i][tmp_extruder];
}
// Set the new active extruder and position
active_extruder = tmp_extruder;
plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
// Move to the old position if 'F' was in the parameters
if(make_move && Stopped == false) {
prepare_move();
}
}
#endif
SERIAL_ECHO_START;
SERIAL_ECHO(MSG_ACTIVE_EXTRUDER);
SERIAL_PROTOCOLLN((int)active_extruder);
}
}
else
{
SERIAL_ECHO_START;
SERIAL_ECHOPGM(MSG_UNKNOWN_COMMAND);
SERIAL_ECHO(cmdbuffer[bufindr]);
SERIAL_ECHOLNPGM("\"");
}
ClearToSend();
}
void FlushSerialRequestResend()
{
//char cmdbuffer[bufindr][100]="Resend:";
MYSERIAL.flush();
SERIAL_PROTOCOLPGM(MSG_RESEND);
SERIAL_PROTOCOLLN(gcode_LastN + 1);
ClearToSend();
}
void ClearToSend()
{
previous_millis_cmd = millis();
#ifdef SDSUPPORT
if(fromsd[bufindr])
return;
#endif //SDSUPPORT
SERIAL_PROTOCOLLNPGM(MSG_OK);
}
void get_coordinates()
{
bool seen[4]={false,false,false,false};
for(int8_t i=0; i < NUM_AXIS; i++) {
if(code_seen(axis_codes[i]))
{
destination[i] = (float)code_value() + (axis_relative_modes[i] || relative_mode)*current_position[i];
seen[i]=true;
}
else destination[i] = current_position[i]; //Are these else lines really needed?
}
if(code_seen('F')) {
next_feedrate = code_value();
if(next_feedrate > 0.0) feedrate = next_feedrate;
}
#ifdef FWRETRACT
if(autoretract_enabled)
if( !(seen[X_AXIS] || seen[Y_AXIS] || seen[Z_AXIS]) && seen[E_AXIS])
{
float echange=destination[E_AXIS]-current_position[E_AXIS];
if(echange<-MIN_RETRACT) //retract
{
if(!retracted)
{
destination[Z_AXIS]+=retract_zlift; //not sure why chaninging current_position negatively does not work.
//if slicer retracted by echange=-1mm and you want to retract 3mm, corrrectede=-2mm additionally
float correctede=-echange-retract_length;
//to generate the additional steps, not the destination is changed, but inversely the current position
current_position[E_AXIS]+=-correctede;
feedrate=retract_feedrate;
retracted=true;
}
}
else
if(echange>MIN_RETRACT) //retract_recover
{
if(retracted)
{
//current_position[Z_AXIS]+=-retract_zlift;
//if slicer retracted_recovered by echange=+1mm and you want to retract_recover 3mm, corrrectede=2mm additionally
float correctede=-echange+1*retract_length+retract_recover_length; //total unretract=retract_length+retract_recover_length[surplus]
current_position[E_AXIS]+=correctede; //to generate the additional steps, not the destination is changed, but inversely the current position
feedrate=retract_recover_feedrate;
retracted=false;
}
}
}
#endif //FWRETRACT
}
void get_arc_coordinates()
{
#ifdef SF_ARC_FIX
bool relative_mode_backup = relative_mode;
relative_mode = true;
#endif
get_coordinates();
#ifdef SF_ARC_FIX
relative_mode=relative_mode_backup;
#endif
if(code_seen('I')) {
offset[0] = code_value();
}
else {
offset[0] = 0.0;
}
if(code_seen('J')) {
offset[1] = code_value();
}
else {
offset[1] = 0.0;
}
}
void clamp_to_software_endstops(float target[3])
{
if (min_software_endstops) {
if (target[X_AXIS] < min_pos[X_AXIS]) target[X_AXIS] = min_pos[X_AXIS];
if (target[Y_AXIS] < min_pos[Y_AXIS]) target[Y_AXIS] = min_pos[Y_AXIS];
if (target[Z_AXIS] < min_pos[Z_AXIS]) target[Z_AXIS] = min_pos[Z_AXIS];
}
if (max_software_endstops) {
if (target[X_AXIS] > max_pos[X_AXIS]) target[X_AXIS] = max_pos[X_AXIS];
if (target[Y_AXIS] > max_pos[Y_AXIS]) target[Y_AXIS] = max_pos[Y_AXIS];
if (target[Z_AXIS] > max_pos[Z_AXIS]) target[Z_AXIS] = max_pos[Z_AXIS];
}
}
void prepare_move()
{
clamp_to_software_endstops(destination);
previous_millis_cmd = millis();
// Do not use feedmultiply for E or Z only moves
if( (current_position[X_AXIS] == destination [X_AXIS]) && (current_position[Y_AXIS] == destination [Y_AXIS])) {
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate/60, active_extruder);
}
else {
plan_buffer_line(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS], destination[E_AXIS], feedrate*feedmultiply/60/100.0, active_extruder);
}
for(int8_t i=0; i < NUM_AXIS; i++) {
current_position[i] = destination[i];
}
}
void prepare_arc_move(char isclockwise) {
float r = hypot(offset[X_AXIS], offset[Y_AXIS]); // Compute arc radius for mc_arc
// Trace the arc
mc_arc(current_position, destination, offset, X_AXIS, Y_AXIS, Z_AXIS, feedrate*feedmultiply/60/100.0, r, isclockwise, active_extruder);
// As far as the parser is concerned, the position is now == target. In reality the
// motion control system might still be processing the action and the real tool position
// in any intermediate location.
for(int8_t i=0; i < NUM_AXIS; i++) {
current_position[i] = destination[i];
}
previous_millis_cmd = millis();
}
#ifdef CONTROLLERFAN_PIN
unsigned long lastMotor = 0; //Save the time for when a motor was turned on last
unsigned long lastMotorCheck = 0;
void controllerFan()
{
if ((millis() - lastMotorCheck) >= 2500) //Not a time critical function, so we only check every 2500ms
{
lastMotorCheck = millis();
if(!READ(X_ENABLE_PIN) || !READ(Y_ENABLE_PIN) || !READ(Z_ENABLE_PIN)
#if EXTRUDERS > 2
|| !READ(E2_ENABLE_PIN)
#endif
#if EXTRUDER > 1
|| !READ(E2_ENABLE_PIN)
#endif
|| !READ(E0_ENABLE_PIN)) //If any of the drivers are enabled...
{
lastMotor = millis(); //... set time to NOW so the fan will turn on
}
if ((millis() - lastMotor) >= (CONTROLLERFAN_SEC*1000UL) || lastMotor == 0) //If the last time any driver was enabled, is longer since than CONTROLLERSEC...
{
WRITE(CONTROLLERFAN_PIN, LOW); //... turn the fan off
}
else
{
WRITE(CONTROLLERFAN_PIN, HIGH); //... turn the fan on
}
}
}
#endif
#ifdef EXTRUDERFAN_PIN
unsigned long lastExtruderCheck = 0;
void extruderFan()
{
if ((millis() - lastExtruderCheck) >= 2500) //Not a time critical function, so we only check every 2500ms
{
lastExtruderCheck = millis();
if (degHotend(active_extruder) < EXTRUDERFAN_DEC)
{
WRITE(EXTRUDERFAN_PIN, LOW); //... turn the fan off
}
else
{
WRITE(EXTRUDERFAN_PIN, HIGH); //... turn the fan on
}
}
}
#endif
void manage_inactivity()
{
if( (millis() - previous_millis_cmd) > max_inactive_time )
if(max_inactive_time)
kill();
if(stepper_inactive_time) {
if( (millis() - previous_millis_cmd) > stepper_inactive_time )
{
if(blocks_queued() == false) {
disable_x();
disable_y();
disable_z();
disable_e0();
disable_e1();
disable_e2();
}
}
}
#if( KILL_PIN>-1 )
if( 0 == READ(KILL_PIN) )
kill();
#endif
#ifdef CONTROLLERFAN_PIN
controllerFan(); //Check if fan should be turned on to cool stepper drivers down
#endif
#ifdef EXTRUDER_RUNOUT_PREVENT
if( (millis() - previous_millis_cmd) > EXTRUDER_RUNOUT_SECONDS*1000 )
if(degHotend(active_extruder)>EXTRUDER_RUNOUT_MINTEMP)
{
bool oldstatus=READ(E0_ENABLE_PIN);
enable_e0();
float oldepos=current_position[E_AXIS];
float oldedes=destination[E_AXIS];
plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS],
current_position[E_AXIS]+EXTRUDER_RUNOUT_EXTRUDE*EXTRUDER_RUNOUT_ESTEPS/axis_steps_per_unit[E_AXIS],
EXTRUDER_RUNOUT_SPEED/60.*EXTRUDER_RUNOUT_ESTEPS/axis_steps_per_unit[E_AXIS], active_extruder);
current_position[E_AXIS]=oldepos;
destination[E_AXIS]=oldedes;
plan_set_e_position(oldepos);
previous_millis_cmd=millis();
st_synchronize();
WRITE(E0_ENABLE_PIN,oldstatus);
}
#endif
check_axes_activity();
}
void kill()
{
cli(); // Stop interrupts
disable_heater();
disable_x();
disable_y();
disable_z();
disable_e0();
disable_e1();
disable_e2();
if(PS_ON_PIN > -1) pinMode(PS_ON_PIN,INPUT);
SERIAL_ERROR_START;
SERIAL_ERRORLNPGM(MSG_ERR_KILLED);
LCD_ALERTMESSAGEPGM(MSG_KILLED);
suicide();
while(1) { /* Intentionally left empty */ } // Wait for reset
}
void Stop()
{
disable_heater();
if(Stopped == false) {
Stopped = true;
Stopped_gcode_LastN = gcode_LastN; // Save last g_code for restart
SERIAL_ERROR_START;
SERIAL_ERRORLNPGM(MSG_ERR_STOPPED);
LCD_MESSAGEPGM(MSG_STOPPED);
}
}
bool IsStopped() { return Stopped; };
#ifdef FAST_PWM_FAN
void setPwmFrequency(uint8_t pin, int val)
{
val &= 0x07;
switch(digitalPinToTimer(pin))
{
#if defined(TCCR0A)
case TIMER0A:
case TIMER0B:
// TCCR0B &= ~(_BV(CS00) | _BV(CS01) | _BV(CS02));
// TCCR0B |= val;
break;
#endif
#if defined(TCCR1A)
case TIMER1A:
case TIMER1B:
// TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
// TCCR1B |= val;
break;
#endif
#if defined(TCCR2)
case TIMER2:
case TIMER2:
TCCR2 &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
TCCR2 |= val;
break;
#endif
#if defined(TCCR2A)
case TIMER2A:
case TIMER2B:
TCCR2B &= ~(_BV(CS20) | _BV(CS21) | _BV(CS22));
TCCR2B |= val;
break;
#endif
#if defined(TCCR3A)
case TIMER3A:
case TIMER3B:
case TIMER3C:
TCCR3B &= ~(_BV(CS30) | _BV(CS31) | _BV(CS32));
TCCR3B |= val;
break;
#endif
#if defined(TCCR4A)
case TIMER4A:
case TIMER4B:
case TIMER4C:
TCCR4B &= ~(_BV(CS40) | _BV(CS41) | _BV(CS42));
TCCR4B |= val;
break;
#endif
#if defined(TCCR5A)
case TIMER5A:
case TIMER5B:
case TIMER5C:
TCCR5B &= ~(_BV(CS50) | _BV(CS51) | _BV(CS52));
TCCR5B |= val;
break;
#endif
}
}
#endif //FAST_PWM_FAN
bool setTargetedHotend(int code){
tmp_extruder = active_extruder;
if(code_seen('T')) {
tmp_extruder = code_value();
if(tmp_extruder >= EXTRUDERS) {
SERIAL_ECHO_START;
switch(code){
case 104:
SERIAL_ECHO(MSG_M104_INVALID_EXTRUDER);
break;
case 105:
SERIAL_ECHO(MSG_M105_INVALID_EXTRUDER);
break;
case 109:
SERIAL_ECHO(MSG_M109_INVALID_EXTRUDER);
break;
case 218:
SERIAL_ECHO(MSG_M218_INVALID_EXTRUDER);
break;
}
SERIAL_ECHOLN(tmp_extruder);
return true;
}
}
return false;
} }