#ifndef EXTRUDER_H_INCLUDED #define EXTRUDER_H_INCLUDED #define CELSIUS_EXTRA_BITS 3 #define VIRTUAL_EXTRUDER 16 // don't change this to more then 16 without modifying the eeprom positions //#if TEMP_PID //extern uint8_t current_extruder_out; //#endif // Updates the temperature of all extruders and heated bed if it's time. // Toggels the heater power if necessary. extern bool reportTempsensorError(); ///< Report defect sensors extern uint8_t manageMonitor; #define HTR_OFF 0 #define HTR_PID 1 #define HTR_SLOWBANG 2 #define HTR_DEADTIME 3 #define TEMPERATURE_CONTROLLER_FLAG_ALARM 1 #define TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_FULL 2 //< Full heating enabled #define TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_HOLD 4 //< Holding target temperature /** TemperatureController manages one heater-temperature sensore loop. You can have up to 4 loops allowing pid/bang bang for up to 3 extruder and the heated bed. */ class TemperatureController { public: uint8_t pwmIndex; ///< pwm index for output control. 0-2 = Extruder, 3 = Fan, 4 = Heated Bed uint8_t sensorType; ///< Type of temperature sensor. uint8_t sensorPin; ///< Pin to read extruder temperature. int16_t currentTemperature; ///< Currenttemperature value read from sensor. int16_t targetTemperature; ///< Target temperature value in units of sensor. float currentTemperatureC; ///< Current temperature in degC. float targetTemperatureC; ///< Target temperature in degC. uint32_t lastTemperatureUpdate; ///< Time in millis of the last temperature update. int8_t heatManager; ///< How is temperature controled. 0 = on/off, 1 = PID-Control, 3 = deat time control #if TEMP_PID float tempIState; ///< Temp. var. for PID computation. uint8_t pidDriveMax; ///< Used for windup in PID calculation. uint8_t pidDriveMin; ///< Used for windup in PID calculation. #define deadTime pidPGain // deadTime is logically different value but physically overlays pidPGain for saving space float pidPGain; ///< Pgain (proportional gain) for PID temperature control [0,01 Units]. float pidIGain; ///< Igain (integral) for PID temperature control [0,01 Units]. float pidDGain; ///< Dgain (damping) for PID temperature control [0,01 Units]. uint8_t pidMax; ///< Maximum PWM value, the heater should be set. float tempIStateLimitMax; float tempIStateLimitMin; uint8_t tempPointer; float tempArray[4]; #endif uint8_t flags; millis_t lastDecoupleTest; ///< Last time of decoupling sensor-heater test float lastDecoupleTemp; ///< Temperature on last test millis_t decoupleTestPeriod; ///< Time between setting and testing decoupling. void setTargetTemperature(float target); void updateCurrentTemperature(); void updateTempControlVars(); inline bool isAlarm() { return flags & TEMPERATURE_CONTROLLER_FLAG_ALARM; } inline void setAlarm(bool on) { if(on) flags |= TEMPERATURE_CONTROLLER_FLAG_ALARM; else flags &= ~TEMPERATURE_CONTROLLER_FLAG_ALARM; } inline bool isDecoupleFull() { return flags & TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_FULL; } inline bool isDecoupleFullOrHold() { return flags & (TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_FULL | TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_HOLD); } inline void setDecoupleFull(bool on) { flags &= ~(TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_FULL | TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_HOLD); if(on) flags |= TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_FULL; } inline bool isDecoupleHold() { return flags & TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_HOLD; } inline void setDecoupleHold(bool on) { flags &= ~(TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_FULL | TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_HOLD); if(on) flags |= TEMPERATURE_CONTROLLER_FLAG_DECOUPLE_HOLD; } inline void startFullDecouple(millis_t &t) { if(isDecoupleFull()) return; lastDecoupleTest = t; lastDecoupleTemp = currentTemperatureC; setDecoupleFull(true); } inline void startHoldDecouple(millis_t &t) { if(isDecoupleHold()) return; if(fabs(currentTemperatureC - targetTemperatureC) + 1 > DECOUPLING_TEST_MAX_HOLD_VARIANCE) return; lastDecoupleTest = t; lastDecoupleTemp = targetTemperatureC; setDecoupleHold(true); } inline void stopDecouple() { setDecoupleFull(false); } #if TEMP_PID void autotunePID(float temp,uint8_t controllerId,bool storeResult); #endif }; class Extruder; extern Extruder extruder[]; #define EXTRUDER_FLAG_RETRACTED 1 /** \brief Data to drive one extruder. This structure contains all definitions for an extruder and all current state variables, like current temperature, feeder position etc. */ class Extruder // Size: 12*1 Byte+12*4 Byte+4*2Byte = 68 Byte { public: static Extruder *current; #if FEATURE_DITTO_PRINTING static uint8_t dittoMode; #endif #if MIXING_EXTRUDER > 0 static int mixingS; ///< Sum of all weights static uint8_t mixingDir; ///< Direction flag #endif uint8_t id; int32_t xOffset; int32_t yOffset; float stepsPerMM; ///< Steps per mm. int8_t enablePin; ///< Pin to enable extruder stepper motor. // uint8_t directionPin; ///< Pin number to assign the direction. // uint8_t stepPin; ///< Pin number for a step. uint8_t enableOn; // uint8_t invertDir; ///< 1 if the direction of the extruder should be inverted. float maxFeedrate; ///< Maximum feedrate in mm/s. float maxAcceleration; ///< Maximum acceleration in mm/s^2. float maxStartFeedrate; ///< Maximum start feedrate in mm/s. int32_t extrudePosition; ///< Current extruder position in steps. int16_t watchPeriod; ///< Time in seconds, a M109 command will wait to stabalize temperature int16_t waitRetractTemperature; ///< Temperature to retract the filament when waiting for heatup int16_t waitRetractUnits; ///< Units to retract the filament when waiting for heatup #if USE_ADVANCE #if ENABLE_QUADRATIC_ADVANCE float advanceK; ///< Koefficient for advance algorithm. 0 = off #endif float advanceL; int16_t advanceBacklash; #endif // USE_ADVANCE #if MIXING_EXTRUDER > 0 int mixingW; ///< Weight for this extruder when mixing steps int mixingE; ///< Cumulated error for this step. int virtualWeights[VIRTUAL_EXTRUDER]; // Virtual extruder weights #endif // MIXING_EXTRUDER > 0 TemperatureController tempControl; const char * PROGMEM selectCommands; const char * PROGMEM deselectCommands; uint8_t coolerSpeed; ///< Speed to use when enabled uint8_t coolerPWM; ///< current PWM setting float diameter; uint8_t flags; #if MIXING_EXTRUDER > 0 static void setMixingWeight(uint8_t extr,int weight); static void step(); static void unstep(); static void setDirection(uint8_t dir); static void enable(); #else /** \brief Sends the high-signal to the stepper for next extruder step. Call this function only, if interrupts are disabled. */ static inline void step() { #if NUM_EXTRUDER==1 WRITE(EXT0_STEP_PIN,HIGH); #else switch(Extruder::current->id) { case 0: #if NUM_EXTRUDER>0 WRITE(EXT0_STEP_PIN,HIGH); #if FEATURE_DITTO_PRINTING if(Extruder::dittoMode) { WRITE(EXT1_STEP_PIN,HIGH); #if NUM_EXTRUDER > 2 if(Extruder::dittoMode > 1) { WRITE(EXT2_STEP_PIN,HIGH); } #endif #if NUM_EXTRUDER > 3 if(Extruder::dittoMode > 2) { WRITE(EXT3_STEP_PIN,HIGH); } #endif } #endif #endif break; #if defined(EXT1_STEP_PIN) && NUM_EXTRUDER>1 case 1: WRITE(EXT1_STEP_PIN,HIGH); break; #endif #if defined(EXT2_STEP_PIN) && NUM_EXTRUDER>2 case 2: WRITE(EXT2_STEP_PIN,HIGH); break; #endif #if defined(EXT3_STEP_PIN) && NUM_EXTRUDER>3 case 3: WRITE(EXT3_STEP_PIN,HIGH); break; #endif #if defined(EXT4_STEP_PIN) && NUM_EXTRUDER>4 case 4: WRITE(EXT4_STEP_PIN,HIGH); break; #endif #if defined(EXT5_STEP_PIN) && NUM_EXTRUDER>5 case 5: WRITE(EXT5_STEP_PIN,HIGH); break; #endif } #endif } /** \brief Sets stepper signal to low for current extruder. Call this function only, if interrupts are disabled. */ static inline void unstep() { #if NUM_EXTRUDER == 1 WRITE(EXT0_STEP_PIN,LOW); #else switch(Extruder::current->id) { case 0: #if NUM_EXTRUDER > 0 WRITE(EXT0_STEP_PIN,LOW); #if FEATURE_DITTO_PRINTING if(Extruder::dittoMode) { WRITE(EXT1_STEP_PIN,LOW); #if NUM_EXTRUDER > 2 if(Extruder::dittoMode > 1) { WRITE(EXT2_STEP_PIN,LOW); } #endif #if NUM_EXTRUDER > 3 if(Extruder::dittoMode > 2) { WRITE(EXT3_STEP_PIN,LOW); } #endif // NUM_EXTRUDER > 3 } #endif // FEATURE_DITTO_PRINTING #endif // NUM_EXTRUDER > 0 break; #if defined(EXT1_STEP_PIN) && NUM_EXTRUDER > 1 case 1: WRITE(EXT1_STEP_PIN,LOW); break; #endif #if defined(EXT2_STEP_PIN) && NUM_EXTRUDER > 2 case 2: WRITE(EXT2_STEP_PIN,LOW); break; #endif #if defined(EXT3_STEP_PIN) && NUM_EXTRUDER > 3 case 3: WRITE(EXT3_STEP_PIN,LOW); break; #endif #if defined(EXT4_STEP_PIN) && NUM_EXTRUDER > 4 case 4: WRITE(EXT4_STEP_PIN,LOW); break; #endif #if defined(EXT5_STEP_PIN) && NUM_EXTRUDER > 5 case 5: WRITE(EXT5_STEP_PIN,LOW); break; #endif } #endif } /** \brief Activates the extruder stepper and sets the direction. */ static inline void setDirection(uint8_t dir) { #if MIXING_EXTRUDER > 0 mixingDir = dir; #endif #if NUM_EXTRUDER == 1 if(dir) WRITE(EXT0_DIR_PIN, !EXT0_INVERSE); else WRITE(EXT0_DIR_PIN, EXT0_INVERSE); #else switch(Extruder::current->id) { #if NUM_EXTRUDER > 0 case 0: if(dir) WRITE(EXT0_DIR_PIN,!EXT0_INVERSE); else WRITE(EXT0_DIR_PIN,EXT0_INVERSE); #if FEATURE_DITTO_PRINTING if(Extruder::dittoMode) { if(dir) WRITE(EXT1_DIR_PIN,!EXT1_INVERSE); else WRITE(EXT1_DIR_PIN,EXT1_INVERSE); #if NUM_EXTRUDER > 2 if(Extruder::dittoMode > 1) { if(dir) WRITE(EXT2_DIR_PIN,!EXT2_INVERSE); else WRITE(EXT2_DIR_PIN,EXT2_INVERSE); } #endif #if NUM_EXTRUDER > 3 if(Extruder::dittoMode > 2) { if(dir) WRITE(EXT3_DIR_PIN,!EXT3_INVERSE); else WRITE(EXT3_DIR_PIN,EXT3_INVERSE); } #endif } #endif break; #endif #if defined(EXT1_DIR_PIN) && NUM_EXTRUDER > 1 case 1: if(dir) WRITE(EXT1_DIR_PIN,!EXT1_INVERSE); else WRITE(EXT1_DIR_PIN,EXT1_INVERSE); break; #endif #if defined(EXT2_DIR_PIN) && NUM_EXTRUDER > 2 case 2: if(dir) WRITE(EXT2_DIR_PIN,!EXT2_INVERSE); else WRITE(EXT2_DIR_PIN,EXT2_INVERSE); break; #endif #if defined(EXT3_DIR_PIN) && NUM_EXTRUDER > 3 case 3: if(dir) WRITE(EXT3_DIR_PIN,!EXT3_INVERSE); else WRITE(EXT3_DIR_PIN,EXT3_INVERSE); break; #endif #if defined(EXT4_DIR_PIN) && NUM_EXTRUDER > 4 case 4: if(dir) WRITE(EXT4_DIR_PIN,!EXT4_INVERSE); else WRITE(EXT4_DIR_PIN,EXT4_INVERSE); break; #endif #if defined(EXT5_DIR_PIN) && NUM_EXTRUDER > 5 case 5: if(dir) WRITE(EXT5_DIR_PIN,!EXT5_INVERSE); else WRITE(EXT5_DIR_PIN,EXT5_INVERSE); break; #endif } #endif } static inline void enable() { #if NUM_EXTRUDER == 1 #if EXT0_ENABLE_PIN > -1 WRITE(EXT0_ENABLE_PIN,EXT0_ENABLE_ON ); #endif #else if(Extruder::current->enablePin > -1) digitalWrite(Extruder::current->enablePin,Extruder::current->enableOn); #if FEATURE_DITTO_PRINTING if(Extruder::dittoMode) { if(extruder[1].enablePin > -1) digitalWrite(extruder[1].enablePin,extruder[1].enableOn); #if NUM_EXTRUDER > 2 if(Extruder::dittoMode > 1 && extruder[2].enablePin > -1) digitalWrite(extruder[2].enablePin,extruder[2].enableOn); #endif #if NUM_EXTRUDER > 3 if(Extruder::dittoMode > 2 && extruder[3].enablePin > -1) digitalWrite(extruder[3].enablePin,extruder[3].enableOn); #endif } #endif #endif } #endif #if FEATURE_RETRACTION inline bool isRetracted() {return (flags & EXTRUDER_FLAG_RETRACTED) != 0;} inline void setRetracted(bool on) { flags = (flags & (255 - EXTRUDER_FLAG_RETRACTED)) | (on ? EXTRUDER_FLAG_RETRACTED : 0); } void retract(bool isRetract,bool isLong); void retractDistance(float dist); #endif static void manageTemperatures(); static void disableCurrentExtruderMotor(); static void disableAllExtruderMotors(); static void selectExtruderById(uint8_t extruderId); static void disableAllHeater(); static void initExtruder(); static void initHeatedBed(); static void setHeatedBedTemperature(float temp_celsius,bool beep = false); static float getHeatedBedTemperature(); static void setTemperatureForExtruder(float temp_celsius,uint8_t extr,bool beep = false); }; #if HAVE_HEATED_BED #define NUM_TEMPERATURE_LOOPS NUM_EXTRUDER+1 extern TemperatureController heatedBedController; #else #define NUM_TEMPERATURE_LOOPS NUM_EXTRUDER #endif #define TEMP_INT_TO_FLOAT(temp) ((float)(temp)/(float)(1<