diff --git a/Repetier/Commands.cpp b/Repetier/Commands.cpp
new file mode 100644
index 0000000..14e3bb0
--- /dev/null
+++ b/Repetier/Commands.cpp
@@ -0,0 +1,2353 @@
+/*
+ This file is part of Repetier-Firmware.
+
+ Repetier-Firmware is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Repetier-Firmware is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Repetier-Firmware. If not, see .
+
+ This firmware is a nearly complete rewrite of the sprinter firmware
+ by kliment (https://github.com/kliment/Sprinter)
+ which based on Tonokip RepRap firmware rewrite based off of Hydra-mmm firmware.
+*/
+
+#include "Repetier.h"
+
+const uint8_t sensitive_pins[] PROGMEM = SENSITIVE_PINS; // Sensitive pin list for M42
+int Commands::lowestRAMValue = MAX_RAM;
+int Commands::lowestRAMValueSend = MAX_RAM;
+
+void Commands::commandLoop()
+{
+ while(true)
+ {
+#ifdef DEBUG_PRINT
+ debugWaitLoop = 1;
+#endif
+ if(!Printer::isBlockingReceive())
+ {
+ GCode::readFromSerial();
+ GCode *code = GCode::peekCurrentCommand();
+ //UI_SLOW; // do longer timed user interface action
+ UI_MEDIUM; // do check encoder
+ if(code)
+ {
+#if SDSUPPORT
+ if(sd.savetosd)
+ {
+ if(!(code->hasM() && code->M == 29)) // still writing to file
+ sd.writeCommand(code);
+ else
+ sd.finishWrite();
+#if ECHO_ON_EXECUTE
+ code->echoCommand();
+#endif
+ }
+ else
+#endif
+ Commands::executeGCode(code);
+ code->popCurrentCommand();
+ }
+ }
+ else
+ {
+ UI_MEDIUM;
+ }
+ Printer::defaultLoopActions();
+ }
+}
+
+void Commands::checkForPeriodicalActions(bool allowNewMoves)
+{
+ Printer::handleInterruptEvent();
+ EVENT_PERIODICAL;
+ if(!executePeriodical) return;
+ executePeriodical = 0;
+ EVENT_TIMER_100MS;
+ Extruder::manageTemperatures();
+ if(--counter250ms == 0)
+ {
+ if(manageMonitor)
+ writeMonitor();
+ counter250ms = 5;
+ EVENT_TIMER_500MS;
+ }
+ // If called from queueDelta etc. it is an error to start a new move since it
+ // would invalidate old computation resulting in unpredicted behaviour.
+ // lcd controller can start new moves, so we disallow it if called from within
+ // a move command.
+ UI_SLOW(allowNewMoves);
+}
+
+/** \brief Waits until movement cache is empty.
+
+ Some commands expect no movement, before they can execute. This function
+ waits, until the steppers are stopped. In the meanwhile it buffers incoming
+ commands and manages temperatures.
+*/
+void Commands::waitUntilEndOfAllMoves()
+{
+#ifdef DEBUG_PRINT
+ debugWaitLoop = 8;
+#endif
+ while(PrintLine::hasLines())
+ {
+ GCode::readFromSerial();
+ checkForPeriodicalActions(false);
+ UI_MEDIUM;
+ }
+}
+
+void Commands::waitUntilEndOfAllBuffers()
+{
+ GCode *code = NULL;
+#ifdef DEBUG_PRINT
+ debugWaitLoop = 9;
+#endif
+ while(PrintLine::hasLines() || (code != NULL))
+ {
+ GCode::readFromSerial();
+ code = GCode::peekCurrentCommand();
+ UI_MEDIUM; // do check encoder
+ if(code)
+ {
+#if SDSUPPORT
+ if(sd.savetosd)
+ {
+ if(!(code->hasM() && code->M == 29)) // still writing to file
+ sd.writeCommand(code);
+ else
+ sd.finishWrite();
+#if ECHO_ON_EXECUTE
+ code->echoCommand();
+#endif
+ }
+ else
+#endif
+ Commands::executeGCode(code);
+ code->popCurrentCommand();
+ }
+ Commands::checkForPeriodicalActions(false); // only called from memory
+ UI_MEDIUM;
+ }
+}
+
+void Commands::printCurrentPosition(FSTRINGPARAM(s))
+{
+ float x, y, z;
+ Printer::realPosition(x, y, z);
+ if (isnan(x) || isinf(x) || isnan(y) || isinf(y) || isnan(z) || isinf(z))
+ {
+ Com::printErrorFLN(s); // flag where the error condition came from
+ }
+ x += Printer::coordinateOffset[X_AXIS];
+ y += Printer::coordinateOffset[Y_AXIS];
+ z += Printer::coordinateOffset[Z_AXIS];
+ Com::printF(Com::tXColon, x * (Printer::unitIsInches ? 0.03937 : 1), 2);
+ Com::printF(Com::tSpaceYColon, y * (Printer::unitIsInches ? 0.03937 : 1), 2);
+ Com::printF(Com::tSpaceZColon, z * (Printer::unitIsInches ? 0.03937 : 1), 3);
+ Com::printFLN(Com::tSpaceEColon, Printer::currentPositionSteps[E_AXIS] * Printer::invAxisStepsPerMM[E_AXIS] * (Printer::unitIsInches ? 0.03937 : 1), 4);
+ //Com::printF(PSTR("OffX:"),Printer::offsetX); // to debug offset handling
+ //Com::printFLN(PSTR(" OffY:"),Printer::offsetY);
+}
+
+void Commands::printTemperatures(bool showRaw)
+{
+ float temp = Extruder::current->tempControl.currentTemperatureC;
+#if HEATED_BED_SENSOR_TYPE == 0
+ Com::printF(Com::tTColon,temp);
+ Com::printF(Com::tSpaceSlash,Extruder::current->tempControl.targetTemperatureC,0);
+#else
+ Com::printF(Com::tTColon,temp);
+ Com::printF(Com::tSpaceSlash,Extruder::current->tempControl.targetTemperatureC,0);
+#if HAVE_HEATED_BED
+ Com::printF(Com::tSpaceBColon,Extruder::getHeatedBedTemperature());
+ Com::printF(Com::tSpaceSlash,heatedBedController.targetTemperatureC,0);
+ if(showRaw)
+ {
+ Com::printF(Com::tSpaceRaw,(int)NUM_EXTRUDER);
+ Com::printF(Com::tColon,(1023 << (2 - ANALOG_REDUCE_BITS)) - heatedBedController.currentTemperature);
+ }
+ Com::printF(Com::tSpaceBAtColon,(pwm_pos[heatedBedController.pwmIndex])); // Show output of autotune when tuning!
+#endif
+#endif
+#if TEMP_PID
+ Com::printF(Com::tSpaceAtColon,(autotuneIndex == 255 ? pwm_pos[Extruder::current->id] : pwm_pos[autotuneIndex])); // Show output of autotune when tuning!
+#endif
+#if NUM_EXTRUDER > 1 && MIXING_EXTRUDER == 0
+ for(uint8_t i = 0; i < NUM_EXTRUDER; i++)
+ {
+ Com::printF(Com::tSpaceT,(int)i);
+ Com::printF(Com::tColon,extruder[i].tempControl.currentTemperatureC);
+ Com::printF(Com::tSpaceSlash,extruder[i].tempControl.targetTemperatureC,0);
+#if TEMP_PID
+ Com::printF(Com::tSpaceAt,(int)i);
+ Com::printF(Com::tColon,(pwm_pos[extruder[i].tempControl.pwmIndex])); // Show output of autotune when tuning!
+#endif
+ if(showRaw)
+ {
+ Com::printF(Com::tSpaceRaw,(int)i);
+ Com::printF(Com::tColon,(1023 << (2 - ANALOG_REDUCE_BITS)) - extruder[i].tempControl.currentTemperature);
+ }
+ }
+#endif
+ Com::println();
+}
+void Commands::changeFeedrateMultiply(int factor)
+{
+ if(factor < 25) factor = 25;
+ if(factor > 500) factor = 500;
+ Printer::feedrate *= (float)factor / (float)Printer::feedrateMultiply;
+ Printer::feedrateMultiply = factor;
+ Com::printFLN(Com::tSpeedMultiply, factor);
+}
+
+void Commands::changeFlowrateMultiply(int factor)
+{
+ if(factor < 25) factor = 25;
+ if(factor > 200) factor = 200;
+ Printer::extrudeMultiply = factor;
+ if(Extruder::current->diameter <= 0)
+ Printer::extrusionFactor = 0.01f * static_cast(factor);
+ else
+ Printer::extrusionFactor = 0.01f * static_cast(factor) * 4.0f / (Extruder::current->diameter * Extruder::current->diameter * 3.141592654f);
+ Com::printFLN(Com::tFlowMultiply, factor);
+}
+
+uint8_t fanKickstart;
+void Commands::setFanSpeed(int speed,bool wait)
+{
+#if FAN_PIN>-1 && FEATURE_FAN_CONTROL
+ speed = constrain(speed,0,255);
+ Printer::setMenuMode(MENU_MODE_FAN_RUNNING,speed != 0);
+ if(wait)
+ Commands::waitUntilEndOfAllMoves(); // use only if neededthis to change the speed exactly at that point, but it may cause blobs if you do!
+ if(speed != pwm_pos[NUM_EXTRUDER + 2])
+ {
+ Com::printFLN(Com::tFanspeed,speed); // send only new values to break update loops!
+#if FAN_KICKSTART_TIME
+ if(fanKickstart == 0 && speed > pwm_pos[NUM_EXTRUDER + 2] && speed < 85)
+ {
+ if(pwm_pos[NUM_EXTRUDER + 2]) fanKickstart = FAN_KICKSTART_TIME/100;
+ else fanKickstart = FAN_KICKSTART_TIME/25;
+ }
+#endif
+ }
+ pwm_pos[NUM_EXTRUDER + 2] = speed;
+#endif
+}
+
+void Commands::reportPrinterUsage()
+{
+#if EEPROM_MODE != 0
+ float dist = Printer::filamentPrinted * 0.001 + HAL::eprGetFloat(EPR_PRINTING_DISTANCE);
+ Com::printF(Com::tPrintedFilament, dist, 2);
+ Com::printF(Com::tSpacem);
+ bool alloff = true;
+ for(uint8_t i = 0; i < NUM_EXTRUDER; i++)
+ if(tempController[i]->targetTemperatureC > 15) alloff = false;
+
+ int32_t seconds = (alloff ? 0 : (HAL::timeInMilliseconds() - Printer::msecondsPrinting) / 1000) + HAL::eprGetInt32(EPR_PRINTING_TIME);
+ int32_t tmp = seconds / 86400;
+ seconds -= tmp * 86400;
+ Com::printF(Com::tPrintingTime,tmp);
+ tmp = seconds / 3600;
+ Com::printF(Com::tSpaceDaysSpace,tmp);
+ seconds -= tmp * 3600;
+ tmp = seconds / 60;
+ Com::printF(Com::tSpaceHoursSpace,tmp);
+ Com::printFLN(Com::tSpaceMin);
+#endif
+}
+#if STEPPER_CURRENT_CONTROL == CURRENT_CONTROL_DIGIPOT
+// Digipot methods for controling current and microstepping
+
+#if defined(DIGIPOTSS_PIN) && DIGIPOTSS_PIN > -1
+int digitalPotWrite(int address, uint16_t value) // From Arduino DigitalPotControl example
+{
+ if(value > 255)
+ value = 255;
+ WRITE(DIGIPOTSS_PIN,LOW); // take the SS pin low to select the chip
+ HAL::spiSend(address); // send in the address and value via SPI:
+ HAL::spiSend(value);
+ WRITE(DIGIPOTSS_PIN,HIGH); // take the SS pin high to de-select the chip:
+ //delay(10);
+}
+
+void setMotorCurrent(uint8_t driver, uint16_t current)
+{
+ if(driver > 4) return;
+ const uint8_t digipot_ch[] = DIGIPOT_CHANNELS;
+ digitalPotWrite(digipot_ch[driver], current);
+}
+
+void setMotorCurrentPercent( uint8_t channel, float level)
+{
+ uint16_t raw_level = ( level * 255 / 100 );
+ setMotorCurrent(channel,raw_level);
+}
+#endif
+
+void motorCurrentControlInit() //Initialize Digipot Motor Current
+{
+#if DIGIPOTSS_PIN && DIGIPOTSS_PIN > -1
+ HAL::spiInit(0); //SPI.begin();
+ SET_OUTPUT(DIGIPOTSS_PIN);
+#ifdef MOTOR_CURRENT_PERCENT
+ const float digipot_motor_current[] = MOTOR_CURRENT_PERCENT;
+ for(int i = 0; i <= 4; i++)
+ //digitalPotWrite(digipot_ch[i], digipot_motor_current[i]);
+ setMotorCurrentPercent(i,digipot_motor_current[i]);
+#else
+ const uint8_t digipot_motor_current[] = MOTOR_CURRENT;
+ for(int i = 0; i <= 4; i++)
+ //digitalPotWrite(digipot_ch[i], digipot_motor_current[i]);
+ setMotorCurrent(i,digipot_motor_current[i]);
+#endif
+#endif
+}
+#endif
+
+#if STEPPER_CURRENT_CONTROL == CURRENT_CONTROL_LTC2600
+
+void setMotorCurrent( uint8_t channel, unsigned short level )
+{
+ if(channel >= LTC2600_NUM_CHANNELS) return;
+ const uint8_t ltc_channels[] = LTC2600_CHANNELS;
+ if(channel > LTC2600_NUM_CHANNELS) return;
+ uint8_t address = ltc_channels[channel];
+ fast8_t i;
+
+
+ // NOTE: Do not increase the current endlessly. In case the engine reaches its current saturation, the engine and the driver can heat up and loss power.
+ // When the saturation is reached, more current causes more heating and more power loss.
+ // In case of engines with lower quality, the saturation current may be reached before the nominal current.
+
+ // configure the pins
+ WRITE( LTC2600_CS_PIN, HIGH );
+ SET_OUTPUT( LTC2600_CS_PIN );
+ WRITE( LTC2600_SCK_PIN, LOW );
+ SET_OUTPUT( LTC2600_SCK_PIN );
+ WRITE( LTC2600_SDI_PIN, LOW );
+ SET_OUTPUT( LTC2600_SDI_PIN );
+
+ // enable the command interface of the LTC2600
+ WRITE( LTC2600_CS_PIN, LOW );
+
+ // transfer command and address
+ for( i = 7; i >= 0; i-- )
+ {
+ WRITE( LTC2600_SDI_PIN, address & (0x01 << i));
+ WRITE( LTC2600_SCK_PIN, 1 );
+ WRITE( LTC2600_SCK_PIN, 0 );
+ }
+
+ // transfer the data word
+ for( i = 15; i >= 0; i-- )
+ {
+ WRITE( LTC2600_SDI_PIN, level & (0x01 << i));
+ WRITE( LTC2600_SCK_PIN, 1 );
+ WRITE( LTC2600_SCK_PIN, 0 );
+ }
+
+ // disable the ommand interface of the LTC2600 -
+ // this carries out the specified command
+ WRITE( LTC2600_CS_PIN, HIGH );
+
+} // setLTC2600
+void setMotorCurrentPercent( uint8_t channel, float level)
+{
+ if(level > 100.0f) level = 100.0f;
+ uint16_t raw_level = static_cast( (long)level * 65535L / 100L );
+ setMotorCurrent(channel,raw_level);
+}
+
+void motorCurrentControlInit() //Initialize LTC2600 Motor Current
+{
+ uint8_t i;
+#ifdef MOTOR_CURRENT_PERCENT
+ const float digipot_motor_current[] = MOTOR_CURRENT_PERCENT;
+ for(int i = 0; i < LTC2600_NUM_CHANNELS; i++)
+ //digitalPotWrite(digipot_ch[i], digipot_motor_current[i]);
+ setMotorCurrentPercent(i,digipot_motor_current[i]);
+#else
+ const unsigned int ltc_current[] = MOTOR_CURRENT;
+ for(i = 0; i < LTC2600_NUM_CHANNELS; i++)
+ {
+ setMotorCurrent(i, ltc_current[i] );
+ }
+#endif
+}
+#endif
+
+#if STEPPER_CURRENT_CONTROL == CURRENT_CONTROL_ALLIGATOR
+void setMotorCurrent(uint8_t channel, unsigned short value)
+{
+ if(channel >= 7) // max channel (X,Y,Z,E0,E1,E2,E3)
+ return;
+ if(value > 255)
+ value=255;
+
+ uint8_t externalDac_buf[2] = {0x10, 0x00};
+
+ if(channel > 3)
+ externalDac_buf[0] |= ( 7 - channel << 6);
+ else
+ externalDac_buf[0] |= ( 3 - channel << 6);
+
+ externalDac_buf[0] |= (value >> 4);
+ externalDac_buf[1] |= (value << 4);
+
+ // All SPI chip-select HIGH
+ WRITE(DAC0_SYNC, HIGH);
+ WRITE(DAC1_SYNC, HIGH);
+ WRITE(SPI_EEPROM1_CS, HIGH);
+ WRITE(SPI_EEPROM2_CS, HIGH);
+ WRITE(SPI_FLASH_CS, HIGH);
+ WRITE(SDSS, HIGH);
+
+ if(channel > 3) // DAC Piggy E1,E2,E3
+ {
+ WRITE(DAC1_SYNC,LOW);
+ HAL::delayMicroseconds(2);
+ WRITE(DAC1_SYNC,HIGH);
+ HAL::delayMicroseconds(2);
+ WRITE(DAC1_SYNC,LOW);
+ }
+ else // DAC onboard X,Y,Z,E0
+ {
+ WRITE(DAC0_SYNC,LOW);
+ HAL::delayMicroseconds(2);
+ WRITE(DAC0_SYNC,HIGH);
+ HAL::delayMicroseconds(2);
+ WRITE(DAC0_SYNC,LOW);
+ }
+
+ HAL::delayMicroseconds(2);
+ HAL::spiSend(SPI_CHAN_DAC, externalDac_buf, 2);
+}
+
+void setMotorCurrentPercent( uint8_t channel, float level)
+{
+ uint16_t raw_level = ( level * 255 / 100 );
+ setMotorCurrent(channel,raw_level);
+}
+
+void motorCurrentControlInit() //Initialize Motor Current
+{
+ uint8_t externalDac_buf[2] = {0x20, 0x00};//all off
+
+ // All SPI chip-select HIGH
+ WRITE(DAC0_SYNC, HIGH);
+ WRITE(DAC1_SYNC, HIGH);
+ WRITE(SPI_EEPROM1_CS, HIGH);
+ WRITE(SPI_EEPROM2_CS, HIGH);
+ WRITE(SPI_FLASH_CS, HIGH);
+ WRITE(SDSS, HIGH);
+
+ // init onboard DAC
+ WRITE(DAC0_SYNC, LOW);
+ HAL::delayMicroseconds(2);
+ WRITE(DAC0_SYNC, HIGH);
+ HAL::delayMicroseconds(2);
+ WRITE(DAC0_SYNC, LOW);
+
+ HAL::spiSend(SPI_CHAN_DAC,externalDac_buf, 2);
+ WRITE(DAC0_SYNC, HIGH);
+
+#if NUM_EXTRUDER > 1
+ // init Piggy DAC
+ WRITE(DAC1_SYNC, LOW);
+ HAL::delayMicroseconds(2);
+ WRITE(DAC1_SYNC, HIGH);
+ HAL::delayMicroseconds(2);
+ WRITE(DAC1_SYNC, LOW);
+
+ HAL::spiSend(SPI_CHAN_DAC,externalDac_buf, 2);
+ WRITE(DAC1_SYNC, HIGH);
+#endif
+
+#ifdef MOTOR_CURRENT_PERCENT
+ const float digipot_motor_current[] = MOTOR_CURRENT_PERCENT;
+ for(int i = 0; i < NUM_EXTRUDER+3; i++)
+ setMotorCurrentPercent(i,digipot_motor_current[i]);
+#else
+ const uint8_t digipot_motor_current[] = MOTOR_CURRENT;
+ for(uint8_t i = 0; i < NUM_EXTRUDER+3; i++)
+ setMotorCurrent(i,digipot_motor_current[i]);
+#endif
+}
+#endif
+
+
+#if STEPPER_CURRENT_CONTROL == CURRENT_CONTROL_MCP4728
+uint8_t _intVref[] = {MCP4728_VREF, MCP4728_VREF, MCP4728_VREF, MCP4728_VREF};
+uint8_t _gain[] = {MCP4728_GAIN, MCP4728_GAIN, MCP4728_GAIN, MCP4728_GAIN};
+uint8_t _powerDown[] = {0,0,0,0};
+int16_t dac_motor_current[] = {0,0,0,0};
+
+uint8_t _intVrefEp[] = {MCP4728_VREF, MCP4728_VREF, MCP4728_VREF, MCP4728_VREF};
+uint8_t _gainEp[] = {MCP4728_GAIN, MCP4728_GAIN, MCP4728_GAIN, MCP4728_GAIN};
+uint8_t _powerDownEp[] = {0,0,0,0};
+int16_t _valuesEp[] = {0,0,0,0};
+
+uint8_t dac_stepper_channel[] = MCP4728_STEPPER_ORDER;
+
+int dacSimpleCommand(uint8_t simple_command)
+{
+ HAL::i2cStartWait(MCP4728_GENERALCALL_ADDRESS + I2C_WRITE);
+ HAL::i2cWrite(simple_command);
+ HAL::i2cStop();
+}
+
+void dacReadStatus()
+{
+ HAL::delayMilliseconds(500);
+ HAL::i2cStartWait(MCP4728_I2C_ADDRESS | I2C_READ);
+
+ for (int i = 0; i < 8; i++) // 2 sets of 4 Channels (1 EEPROM, 1 Runtime)
+ {
+ uint8_t deviceID = HAL::i2cReadAck();
+ uint8_t hiByte = HAL::i2cReadAck();
+ uint8_t loByte = ((i < 7) ? HAL::i2cReadAck() : HAL::i2cReadNak());
+
+ uint8_t isEEPROM = (deviceID & 0B00001000) >> 3;
+ uint8_t channel = (deviceID & 0B00110000) >> 4;
+ if (isEEPROM == 1)
+ {
+ _intVrefEp[channel] = (hiByte & 0B10000000) >> 7;
+ _gainEp[channel] = (hiByte & 0B00010000) >> 4;
+ _powerDownEp[channel] = (hiByte & 0B01100000) >> 5;
+ _valuesEp[channel] = word((hiByte & 0B00001111), loByte);
+ }
+ else
+ {
+ _intVref[channel] = (hiByte & 0B10000000) >> 7;
+ _gain[channel] = (hiByte & 0B00010000) >> 4;
+ _powerDown[channel] = (hiByte & 0B01100000) >> 5;
+ dac_motor_current[channel] = word((hiByte & 0B00001111), loByte);
+ }
+ }
+
+ HAL::i2cStop();
+}
+
+void dacAnalogUpdate(bool saveEEPROM = false)
+{
+ uint8_t dac_write_cmd = MCP4728_CMD_SEQ_WRITE;
+
+ HAL::i2cStartWait(MCP4728_I2C_ADDRESS + I2C_WRITE);
+ if (saveEEPROM) HAL::i2cWrite(dac_write_cmd);
+
+ for (int i = 0; i < MCP4728_NUM_CHANNELS; i++)
+ {
+ uint16_t level = dac_motor_current[i];
+
+ uint8_t highbyte = ( _intVref[i] << 7 | _gain[i] << 4 | (uint8_t)((level) >> 8) );
+ uint8_t lowbyte = ( (uint8_t) ((level) & 0xff) );
+ dac_write_cmd = MCP4728_CMD_MULTI_WRITE | (i << 1);
+
+ if (!saveEEPROM) HAL::i2cWrite(dac_write_cmd);
+ HAL::i2cWrite(highbyte);
+ HAL::i2cWrite(lowbyte);
+ }
+
+ HAL::i2cStop();
+
+ // Instruct the MCP4728 to reflect our updated value(s) on its DAC Outputs
+ dacSimpleCommand((uint8_t)MCP4728_CMD_GC_UPDATE); // MCP4728 General Command Software Update (Update all DAC Outputs to reflect settings)
+
+ // if (saveEEPROM) dacReadStatus(); // Not necessary, just a read-back sanity check.
+}
+
+void dacCommitEeprom()
+{
+ dacAnalogUpdate(true);
+ dacReadStatus(); // Refresh EEPROM Values with values actually stored in EEPROM. .
+}
+
+void dacPrintSet(int dacChannelSettings[], const char* dacChannelPrefixes[])
+{
+ for (int i = 0; i < MCP4728_NUM_CHANNELS; i++)
+ {
+ uint8_t dac_channel = dac_stepper_channel[i]; // DAC Channel is a mapped lookup.
+ Com::printF(dacChannelPrefixes[i], ((float)dacChannelSettings[dac_channel] * 100 / MCP4728_VOUT_MAX));
+ Com::printF(Com::tSpaceRaw);
+ Com::printFLN(Com::tColon,dacChannelSettings[dac_channel]);
+ }
+}
+
+void dacPrintValues()
+{
+ const char* dacChannelPrefixes[] = {Com::tSpaceXColon, Com::tSpaceYColon, Com::tSpaceZColon, Com::tSpaceEColon};
+
+ Com::printFLN(Com::tMCPEpromSettings);
+ dacPrintSet(_valuesEp, dacChannelPrefixes); // Once for the EEPROM set
+
+ Com::printFLN(Com::tMCPCurrentSettings);
+ dacPrintSet(dac_motor_current, dacChannelPrefixes); // And another for the RUNTIME set
+}
+
+void setMotorCurrent( uint8_t xyz_channel, uint16_t level )
+{
+ if (xyz_channel >= MCP4728_NUM_CHANNELS) return;
+ uint8_t stepper_channel = dac_stepper_channel[xyz_channel];
+ dac_motor_current[stepper_channel] = level < MCP4728_VOUT_MAX ? level : MCP4728_VOUT_MAX;
+ dacAnalogUpdate();
+}
+
+void setMotorCurrentPercent( uint8_t channel, float level)
+{
+ uint16_t raw_level = ( level * MCP4728_VOUT_MAX / 100 );
+ setMotorCurrent(channel,raw_level);
+}
+
+void motorCurrentControlInit() //Initialize MCP4728 Motor Current
+{
+ HAL::i2cInit(400000); // Initialize the i2c bus.
+ dacSimpleCommand((uint8_t)MCP4728_CMD_GC_RESET); // MCP4728 General Command Reset
+ dacReadStatus(); // Load Values from EEPROM.
+
+ for(int i = 0; i < MCP4728_NUM_CHANNELS; i++)
+ {
+ setMotorCurrent(dac_stepper_channel[i], _valuesEp[i] ); // This is not strictly necessary, but serves as a good sanity check to ensure we're all on the same page.
+ }
+}
+#endif
+
+#if defined(X_MS1_PIN) && X_MS1_PIN > -1
+void microstepMS(uint8_t driver, int8_t ms1, int8_t ms2)
+{
+ if(ms1 > -1) switch(driver)
+ {
+ case 0:
+#if X_MS1_PIN > -1
+ WRITE( X_MS1_PIN,ms1);
+#endif
+ break;
+ case 1:
+#if Y_MS1_PIN > -1
+ WRITE( Y_MS1_PIN,ms1);
+#endif
+ break;
+ case 2:
+#if Z_MS1_PIN > -1
+ WRITE( Z_MS1_PIN,ms1);
+#endif
+ break;
+ case 3:
+#if E0_MS1_PIN > -1
+ WRITE(E0_MS1_PIN,ms1);
+#endif
+ break;
+ case 4:
+#if E1_MS1_PIN > -1
+ WRITE(E1_MS1_PIN,ms1);
+#endif
+ break;
+ }
+ if(ms2 > -1) switch(driver)
+ {
+ case 0:
+#if X_MS2_PIN > -1
+ WRITE( X_MS2_PIN,ms2);
+#endif
+ break;
+ case 1:
+#if Y_MS2_PIN > -1
+ WRITE( Y_MS2_PIN,ms2);
+#endif
+ break;
+ case 2:
+#if Z_MS2_PIN > -1
+ WRITE( Z_MS2_PIN,ms2);
+#endif
+ break;
+ case 3:
+#if E0_MS2_PIN > -1
+ WRITE(E0_MS2_PIN,ms2);
+#endif
+ break;
+ case 4:
+#if E1_MS2_PIN > -1
+ WRITE(E1_MS2_PIN,ms2);
+#endif
+ break;
+ }
+}
+
+void microstepMode(uint8_t driver, uint8_t stepping_mode)
+{
+ switch(stepping_mode)
+ {
+ case 1:
+ microstepMS(driver,MICROSTEP1);
+ break;
+ case 2:
+ microstepMS(driver,MICROSTEP2);
+ break;
+ case 4:
+ microstepMS(driver,MICROSTEP4);
+ break;
+ case 8:
+ microstepMS(driver,MICROSTEP8);
+ break;
+ case 16:
+ microstepMS(driver,MICROSTEP16);
+ break;
+ case 32:
+ microstepMS(driver,MICROSTEP32);
+ break;
+ }
+}
+
+void microstepReadings()
+{
+ Com::printFLN(Com::tMS1MS2Pins);
+#if X_MS1_PIN > -1 && X_MS2_PIN > -1
+ Com::printF(Com::tXColon,READ(X_MS1_PIN));
+ Com::printFLN(Com::tComma,READ(X_MS2_PIN));
+#elif X_MS1_PIN > -1
+ Com::printFLN(Com::tXColon,READ(X_MS1_PIN));
+#endif
+#if Y_MS1_PIN > -1 && Y_MS2_PIN > -1
+ Com::printF(Com::tYColon,READ(Y_MS1_PIN));
+ Com::printFLN(Com::tComma,READ(Y_MS2_PIN));
+#elif Y_MS1_PIN > -1
+ Com::printFLN(Com::tYColon,READ(Y_MS1_PIN));
+#endif
+#if Z_MS1_PIN > -1 && Z_MS2_PIN > -1
+ Com::printF(Com::tZColon,READ(Z_MS1_PIN));
+ Com::printFLN(Com::tComma,READ(Z_MS2_PIN));
+#elif Z_MS1_PIN > -1
+ Com::printFLN(Com::tZColon,READ(Z_MS1_PIN));
+#endif
+#if E0_MS1_PIN > -1 && E0_MS2_PIN > -1
+ Com::printF(Com::tE0Colon,READ(E0_MS1_PIN));
+ Com::printFLN(Com::tComma,READ(E0_MS2_PIN));
+#elif E0_MS1_PIN > -1
+ Com::printFLN(Com::tE0Colon,READ(E0_MS1_PIN));
+#endif
+#if E1_MS1_PIN > -1 && E1_MS2_PIN > -1
+ Com::printF(Com::tE1Colon,READ(E1_MS1_PIN));
+ Com::printFLN(Com::tComma,READ(E1_MS2_PIN));
+#elif E1_MS1_PIN > -1
+ Com::printFLN(Com::tE1Colon,READ(E1_MS1_PIN));
+#endif
+}
+#endif
+
+void microstepInit()
+{
+#if defined(X_MS1_PIN) && X_MS1_PIN > -1
+ const uint8_t microstep_modes[] = MICROSTEP_MODES;
+#if X_MS1_PIN > -1
+ SET_OUTPUT(X_MS1_PIN);
+#endif
+#if Y_MS1_PIN > -1
+ SET_OUTPUT(Y_MS1_PIN);
+#endif
+#if Z_MS1_PIN > -1
+ SET_OUTPUT(Z_MS1_PIN);
+#endif
+#if E0_MS1_PIN > -1
+ SET_OUTPUT(E0_MS1_PIN);
+#endif
+#if E1_MS1_PIN > -1
+ SET_OUTPUT(E1_MS1_PIN);
+#endif
+#if X_MS2_PIN > -1
+ SET_OUTPUT(X_MS2_PIN);
+#endif
+#if Y_MS2_PIN > -1
+ SET_OUTPUT(Y_MS2_PIN);
+#endif
+#if Z_MS2_PIN > -1
+ SET_OUTPUT(Z_MS2_PIN);
+#endif
+#if E0_MS2_PIN > -1
+ SET_OUTPUT(E0_MS2_PIN);
+#endif
+#if E1_MS2_PIN > -1
+ SET_OUTPUT(E1_MS2_PIN);
+#endif
+ for(int i = 0; i <= 4; i++) microstepMode(i, microstep_modes[i]);
+#endif
+}
+
+/**
+ \brief Execute the Arc command stored in com.
+*/
+#if ARC_SUPPORT
+void Commands::processArc(GCode *com)
+{
+ float position[Z_AXIS_ARRAY];
+ Printer::realPosition(position[X_AXIS],position[Y_AXIS],position[Z_AXIS]);
+ if(!Printer::setDestinationStepsFromGCode(com)) return; // For X Y Z E F
+ float offset[2] = {Printer::convertToMM(com->hasI() ? com->I : 0),Printer::convertToMM(com->hasJ() ? com->J : 0)};
+ float target[E_AXIS_ARRAY] = {Printer::realXPosition(),Printer::realYPosition(),Printer::realZPosition(),Printer::destinationSteps[E_AXIS]*Printer::invAxisStepsPerMM[E_AXIS]};
+ float r;
+ if (com->hasR())
+ {
+ /*
+ We need to calculate the center of the circle that has the designated radius and passes
+ through both the current position and the target position. This method calculates the following
+ set of equations where [x,y] is the vector from current to target position, d == magnitude of
+ that vector, h == hypotenuse of the triangle formed by the radius of the circle, the distance to
+ the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to the
+ length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form the new point
+ [i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc.
+
+ d^2 == x^2 + y^2
+ h^2 == r^2 - (d/2)^2
+ i == x/2 - y/d*h
+ j == y/2 + x/d*h
+
+ O <- [i,j]
+ - |
+ r - |
+ - |
+ - | h
+ - |
+ [0,0] -> C -----------------+--------------- T <- [x,y]
+ | <------ d/2 ---->|
+
+ C - Current position
+ T - Target position
+ O - center of circle that pass through both C and T
+ d - distance from C to T
+ r - designated radius
+ h - distance from center of CT to O
+
+ Expanding the equations:
+
+ d -> sqrt(x^2 + y^2)
+ h -> sqrt(4 * r^2 - x^2 - y^2)/2
+ i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
+ j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2)) / sqrt(x^2 + y^2)) / 2
+
+ Which can be written:
+
+ i -> (x - (y * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
+ j -> (y + (x * sqrt(4 * r^2 - x^2 - y^2))/sqrt(x^2 + y^2))/2
+
+ Which we for size and speed reasons optimize to:
+
+ h_x2_div_d = sqrt(4 * r^2 - x^2 - y^2)/sqrt(x^2 + y^2)
+ i = (x - (y * h_x2_div_d))/2
+ j = (y + (x * h_x2_div_d))/2
+
+ */
+ r = Printer::convertToMM(com->R);
+ // Calculate the change in position along each selected axis
+ double x = target[X_AXIS]-position[X_AXIS];
+ double y = target[Y_AXIS]-position[Y_AXIS];
+
+ double h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y); // == -(h * 2 / d)
+ // If r is smaller than d, the arc is now traversing the complex plane beyond the reach of any
+ // real CNC, and thus - for practical reasons - we will terminate promptly:
+ if(isnan(h_x2_div_d))
+ {
+ Com::printErrorFLN(Com::tInvalidArc);
+ return;
+ }
+ // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below)
+ if (com->G==3)
+ {
+ h_x2_div_d = -h_x2_div_d;
+ }
+
+ /* The counter clockwise circle lies to the left of the target direction. When offset is positive,
+ the left hand circle will be generated - when it is negative the right hand circle is generated.
+
+
+ T <-- Target position
+
+ ^
+ Clockwise circles with this center | Clockwise circles with this center will have
+ will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing!
+ \ | /
+ center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative
+ |
+ |
+
+ C <-- Current position */
+
+
+ // Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!),
+ // even though it is advised against ever generating such circles in a single line of g-code. By
+ // inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of
+ // travel and thus we get the unadvisably long arcs as prescribed.
+ if (r < 0)
+ {
+ h_x2_div_d = -h_x2_div_d;
+ r = -r; // Finished with r. Set to positive for mc_arc
+ }
+ // Complete the operation by calculating the actual center of the arc
+ offset[0] = 0.5*(x-(y*h_x2_div_d));
+ offset[1] = 0.5*(y+(x*h_x2_div_d));
+
+ }
+ else // Offset mode specific computations
+ {
+ r = hypot(offset[0], offset[1]); // Compute arc radius for arc
+ }
+ // Set clockwise/counter-clockwise sign for arc computations
+ uint8_t isclockwise = com->G == 2;
+ // Trace the arc
+ PrintLine::arc(position, target, offset, r, isclockwise);
+}
+#endif
+
+/**
+ \brief Execute the G command stored in com.
+*/
+void Commands::processGCode(GCode *com)
+{
+ uint32_t codenum; //throw away variable
+ switch(com->G)
+ {
+ case 0: // G0 -> G1
+ case 1: // G1
+ if(com->hasS()) Printer::setNoDestinationCheck(com->S != 0);
+ if(Printer::setDestinationStepsFromGCode(com)) // For X Y Z E F
+#if NONLINEAR_SYSTEM
+ if (!PrintLine::queueDeltaMove(ALWAYS_CHECK_ENDSTOPS, true, true))
+ {
+ Com::printWarningFLN(PSTR("executeGCode / queueDeltaMove returns error"));
+ }
+#else
+ PrintLine::queueCartesianMove(ALWAYS_CHECK_ENDSTOPS, true);
+#endif
+#if UI_HAS_KEYS
+ // ui can only execute motion commands if we are not waiting inside a move for an
+ // old move to finish. For normal response times, we always leave one free after
+ // sending a line. Drawback: 1 buffer line less for limited time. Since input cache
+ // gets filled while waiting, the lost is neglectible.
+ PrintLine::waitForXFreeLines(1, true);
+#endif // UI_HAS_KEYS
+#ifdef DEBUG_QUEUE_MOVE
+ {
+
+ InterruptProtectedBlock noInts;
+ int lc = (int)PrintLine::linesCount;
+ int lp = (int)PrintLine::linesPos;
+ int wp = (int)PrintLine::linesWritePos;
+ int n = (wp-lp);
+ if(n < 0) n += PRINTLINE_CACHE_SIZE;
+ noInts.unprotect();
+ if(n != lc)
+ Com::printFLN(PSTR("Buffer corrupted"));
+ }
+#endif
+ break;
+#if ARC_SUPPORT
+ case 2: // CW Arc
+ case 3: // CCW Arc MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC:
+ processArc(com);
+ break;
+#endif
+ case 4: // G4 dwell
+ Commands::waitUntilEndOfAllMoves();
+ codenum = 0;
+ if(com->hasP()) codenum = com->P; // milliseconds to wait
+ if(com->hasS()) codenum = com->S * 1000; // seconds to wait
+ codenum += HAL::timeInMilliseconds(); // keep track of when we started waiting
+ while((uint32_t)(codenum-HAL::timeInMilliseconds()) < 2000000000 )
+ {
+ GCode::readFromSerial();
+ Commands::checkForPeriodicalActions(true);
+ }
+ break;
+#if FEATURE_RETRACTION && NUM_EXTRUDER > 0
+ case 10: // G10 S<1 = long retract, 0 = short retract = default> retracts filament accoridng to stored setting
+#if NUM_EXTRUDER > 1
+ Extruder::current->retract(true, com->hasS() && com->S > 0);
+#else
+ Extruder::current->retract(true, false);
+#endif
+ break;
+ case 11: // G11 S<1 = long retract, 0 = short retract = default> = Undo retraction according to stored setting
+#if NUM_EXTRUDER > 1
+ Extruder::current->retract(false, com->hasS() && com->S > 0);
+#else
+ Extruder::current->retract(false, false);
+#endif
+ break;
+#endif // FEATURE_RETRACTION
+ case 20: // G20 Units to inches
+ Printer::unitIsInches = 1;
+ break;
+ case 21: // G21 Units to mm
+ Printer::unitIsInches = 0;
+ break;
+ case 28: //G28 Home all Axis one at a time
+ {
+ uint8_t homeAllAxis = (com->hasNoXYZ() && !com->hasE());
+ if(com->hasE())
+ Printer::currentPositionSteps[E_AXIS] = 0;
+ if(homeAllAxis || !com->hasNoXYZ())
+ Printer::homeAxis(homeAllAxis || com->hasX(),homeAllAxis || com->hasY(),homeAllAxis || com->hasZ());
+ Printer::updateCurrentPosition();
+ }
+ break;
+#if FEATURE_Z_PROBE
+ case 29: // G29 3 points, build average or distortion compensation
+ {
+#if DISTORTION_CORRECTION
+ float oldFeedrate = Printer::feedrate;
+ Printer::measureDistortion();
+ Printer::feedrate = oldFeedrate;
+#else
+ GCode::executeFString(Com::tZProbeStartScript);
+ bool oldAutolevel = Printer::isAutolevelActive();
+ Printer::setAutolevelActive(false);
+ float sum = 0, last,oldFeedrate = Printer::feedrate;
+ Printer::moveTo(EEPROM::zProbeX1(), EEPROM::zProbeY1(), IGNORE_COORDINATE, IGNORE_COORDINATE, EEPROM::zProbeXYSpeed());
+ sum = Printer::runZProbe(true,false,Z_PROBE_REPETITIONS,false);
+ if(sum < -1) break;
+ Printer::moveTo(EEPROM::zProbeX2(), EEPROM::zProbeY2(), IGNORE_COORDINATE, IGNORE_COORDINATE, EEPROM::zProbeXYSpeed());
+ last = Printer::runZProbe(false,false);
+ if(last < -2) break;
+ sum+= last;
+ Printer::moveTo(EEPROM::zProbeX3(), EEPROM::zProbeY3(), IGNORE_COORDINATE, IGNORE_COORDINATE, EEPROM::zProbeXYSpeed());
+ last = Printer::runZProbe(false,true);
+ if(last < -3) break;
+ sum += last;
+ sum *= 0.33333333333333;
+ Com::printFLN(Com::tZProbeAverage, sum);
+ if(com->hasS() && com->S)
+ {
+#if MAX_HARDWARE_ENDSTOP_Z
+#if DRIVE_SYSTEM == DELTA
+ Printer::updateCurrentPosition();
+ Printer::zLength += sum - Printer::currentPosition[Z_AXIS];
+ Printer::updateDerivedParameter();
+ Printer::homeAxis(true,true,true);
+#else
+ Printer::currentPositionSteps[Z_AXIS] = sum * Printer::axisStepsPerMM[Z_AXIS];
+ Printer::zLength = Printer::runZMaxProbe() + sum-ENDSTOP_Z_BACK_ON_HOME;
+#endif
+ Com::printInfoFLN(Com::tZProbeZReset);
+ Com::printFLN(Com::tZProbePrinterHeight,Printer::zLength);
+#else
+ Printer::currentPositionSteps[Z_AXIS] = sum * Printer::axisStepsPerMM[Z_AXIS];
+ Com::printFLN(PSTR("Adjusted z origin"));
+#endif
+ }
+ Printer::feedrate = oldFeedrate;
+ Printer::setAutolevelActive(oldAutolevel);
+ if(com->hasS() && com->S == 2)
+ EEPROM::storeDataIntoEEPROM();
+ Printer::updateCurrentPosition(true);
+ printCurrentPosition(PSTR("G29 "));
+ GCode::executeFString(Com::tZProbeEndScript);
+ Printer::feedrate = oldFeedrate;
+#endif // DISTORTION_CORRECTION
+ }
+ break;
+ case 30: // G30 single probe set Z0
+ {
+ uint8_t p = (com->hasP() ? (uint8_t)com->P : 3);
+ //bool oldAutolevel = Printer::isAutolevelActive();
+ //Printer::setAutolevelActive(false);
+ Printer::runZProbe(p & 1,p & 2);
+ //Printer::setAutolevelActive(oldAutolevel);
+ Printer::updateCurrentPosition(p & 1);
+ //printCurrentPosition(PSTR("G30 "));
+ }
+ break;
+ case 31: // G31 display hall sensor output
+ Endstops::update();
+ Endstops::update();
+ Com::printF(Com::tZProbeState);
+ Com::printF(Endstops::zProbe() ? Com::tHSpace : Com::tLSpace);
+ Com::println();
+ break;
+#if FEATURE_AUTOLEVEL
+ case 32: // G32 Auto-Bed leveling
+ {
+#if DISTORTION_CORRECTION
+ Printer::distortion.disable(true); // if level has changed, distortion is also invalid
+#endif
+ Printer::setAutolevelActive(false); // iterate
+#if DRIVE_SYSTEM == DELTA
+ // It is not possible to go to the edges at the top, also users try
+ // it often and wonder why the coordinate system is then wrong.
+ // For that reason we ensure a correct behaviour by code.
+ Printer::homeAxis(true, true, true);
+ Printer::moveTo(IGNORE_COORDINATE, IGNORE_COORDINATE, EEPROM::zProbeBedDistance() + EEPROM::zProbeHeight(), IGNORE_COORDINATE, Printer::homingFeedrate[Z_AXIS]);
+#endif
+ GCode::executeFString(Com::tZProbeStartScript);
+ //bool iterate = com->hasP() && com->P>0;
+ Printer::coordinateOffset[X_AXIS] = Printer::coordinateOffset[Y_AXIS] = Printer::coordinateOffset[Z_AXIS] = 0;
+ float h1,h2,h3,hc,oldFeedrate = Printer::feedrate;
+ Printer::moveTo(EEPROM::zProbeX1(),EEPROM::zProbeY1(),IGNORE_COORDINATE,IGNORE_COORDINATE,EEPROM::zProbeXYSpeed());
+ h1 = Printer::runZProbe(true,false,Z_PROBE_REPETITIONS,false);
+ if(h1 < -1) break;
+ Printer::moveTo(EEPROM::zProbeX2(),EEPROM::zProbeY2(),IGNORE_COORDINATE,IGNORE_COORDINATE,EEPROM::zProbeXYSpeed());
+ h2 = Printer::runZProbe(false,false);
+ if(h2 < -1) break;
+ Printer::moveTo(EEPROM::zProbeX3(),EEPROM::zProbeY3(),IGNORE_COORDINATE,IGNORE_COORDINATE,EEPROM::zProbeXYSpeed());
+ h3 = Printer::runZProbe(false,true);
+ if(h3 < -1) break;
+ // Zprobe with force feedback may bed bed differently for different points.
+ // these settings allow correction of the bending distance so leveling is correct afterwards.
+ // Values are normally negative with bending amount on trigger.
+#ifdef ZPROBE_1_BENDING_CORRECTION
+ h1 += ZPROBE_1_BENDING_CORRECTION;
+#endif
+#ifdef ZPROBE_2_BENDING_CORRECTION
+ h2 += ZPROBE_2_BENDING_CORRECTION;
+#endif
+#ifdef ZPROBE_3_BENDING_CORRECTION
+ h3 += ZPROBE_3_BENDING_CORRECTION;
+#endif
+#if defined(MOTORIZED_BED_LEVELING) && defined(NUM_MOTOR_DRIVERS) && NUM_MOTOR_DRIVERS >= 2
+ // h1 is reference heights, h2 => motor 0, h3 => motor 1
+ h2 -= h1;
+ h3 -= h1;
+ MotorDriverInterface *motor2 = getMotorDriver(0);
+ MotorDriverInterface *motor3 = getMotorDriver(1);
+ motor2->setCurrentAs(0);
+ motor3->setCurrentAs(0);
+ motor2->gotoPosition(h2);
+ motor3->gotoPosition(h3);
+ motor2->disable();
+ motor3->disable(); // now bed is even
+ Printer::currentPositionSteps[Z_AXIS] = h1 * Printer::axisStepsPerMM[Z_AXIS];
+#else // defined(MOTORIZED_BED_LEVELING)
+ Printer::buildTransformationMatrix(h1,h2,h3);
+ //-(Rxx*Ryz*y-Rxz*Ryx*y+(Rxz*Ryy-Rxy*Ryz)*x)/(Rxy*Ryx-Rxx*Ryy)
+ // z = z-deviation from origin due to bed transformation
+ float z = -((Printer::autolevelTransformation[0] * Printer::autolevelTransformation[5] -
+ Printer::autolevelTransformation[2] * Printer::autolevelTransformation[3]) *
+ (float)Printer::currentPositionSteps[Y_AXIS] * Printer::invAxisStepsPerMM[Y_AXIS] +
+ (Printer::autolevelTransformation[2] * Printer::autolevelTransformation[4] -
+ Printer::autolevelTransformation[1] * Printer::autolevelTransformation[5]) *
+ (float)Printer::currentPositionSteps[X_AXIS] * Printer::invAxisStepsPerMM[X_AXIS]) /
+ (Printer::autolevelTransformation[1] * Printer::autolevelTransformation[3] - Printer::autolevelTransformation[0] * Printer::autolevelTransformation[4]);
+ Printer::zMin = 0;
+ if(com->hasS() && com->S < 3 && com->S > 0)
+ {
+#if MAX_HARDWARE_ENDSTOP_Z
+#if DRIVE_SYSTEM == DELTA
+ /* Printer::offsetX = 0;
+ Printer::offsetY = 0;
+ Printer::moveToReal(0,0,cz,IGNORE_COORDINATE,Printer::homingFeedrate[X_AXIS]);
+ PrintLine::moveRelativeDistanceInSteps(Printer::offsetX-Printer::currentPositionSteps[X_AXIS],Printer::offsetY-Printer::currentPositionSteps[Y_AXIS],0,0,Printer::homingFeedrate[X_AXIS],true,ALWAYS_CHECK_ENDSTOPS);
+ Printer::offsetX = 0;
+ Printer::offsetY = 0;*/
+ Printer::zLength += (h3 + z) - Printer::currentPosition[Z_AXIS];
+#else
+ int32_t zBottom = Printer::currentPositionSteps[Z_AXIS] = (h3 + z) * Printer::axisStepsPerMM[Z_AXIS];
+ Printer::zLength = Printer::runZMaxProbe() + zBottom * Printer::invAxisStepsPerMM[Z_AXIS] - ENDSTOP_Z_BACK_ON_HOME;
+#endif
+ Com::printFLN(Com::tZProbePrinterHeight,Printer::zLength);
+#else // max hardware endstop
+#if DRIVE_SYSTEM != DELTA
+ Printer::currentPositionSteps[Z_AXIS] = (h3 + z) * Printer::axisStepsPerMM[Z_AXIS];
+#endif
+#endif
+ Printer::setAutolevelActive(true);
+ if(com->S == 2)
+ EEPROM::storeDataIntoEEPROM();
+ }
+ else
+ {
+#if DRIVE_SYSTEM != DELTA
+ Printer::currentPositionSteps[Z_AXIS] = (h3 + z) * Printer::axisStepsPerMM[Z_AXIS];
+#endif
+ if(com->hasS() && com->S == 3)
+ EEPROM::storeDataIntoEEPROM();
+ }
+ Printer::setAutolevelActive(true);
+#endif // defined(MOTORIZED_BED_LEVELING)
+ Printer::updateDerivedParameter();
+ Printer::updateCurrentPosition(true);
+ printCurrentPosition(PSTR("G32 "));
+#if DRIVE_SYSTEM == DELTA
+ Printer::homeAxis(true, true, true);
+#endif
+ Printer::feedrate = oldFeedrate;
+ }
+ break;
+#endif
+#endif
+ case 90: // G90
+ Printer::relativeCoordinateMode = false;
+ if(com->internalCommand)
+ Com::printInfoFLN(PSTR("Absolute positioning"));
+ break;
+ case 91: // G91
+ Printer::relativeCoordinateMode = true;
+ if(com->internalCommand)
+ Com::printInfoFLN(PSTR("Relative positioning"));
+ break;
+ case 92: // G92
+ {
+ float xOff = Printer::coordinateOffset[X_AXIS];
+ float yOff = Printer::coordinateOffset[Y_AXIS];
+ float zOff = Printer::coordinateOffset[Z_AXIS];
+ if(com->hasX()) xOff = Printer::convertToMM(com->X) - Printer::currentPosition[X_AXIS];
+ if(com->hasY()) yOff = Printer::convertToMM(com->Y) - Printer::currentPosition[Y_AXIS];
+ if(com->hasZ()) zOff = Printer::convertToMM(com->Z) - Printer::currentPosition[Z_AXIS];
+ Printer::setOrigin(xOff, yOff, zOff);
+ if(com->hasE())
+ {
+ Printer::currentPositionSteps[E_AXIS] = Printer::convertToMM(com->E) * Printer::axisStepsPerMM[E_AXIS];
+ }
+ }
+ break;
+#if DRIVE_SYSTEM == DELTA
+ case 100: // G100 Calibrate floor or rod radius
+ {
+ // Using manual control, adjust hot end to contact floor.
+ // G100 No action. Avoid accidental floor reset.
+ // G100 [X] [Y] [Z] set floor for argument passed in. Number ignored and may be absent.
+ // G100 R with X Y or Z flag error, sets only floor or radius, not both.
+ // G100 R[n] Add n to radius. Adjust to be above floor if necessary
+ // G100 R[0] set radius based on current z measurement. Moves to (0,0,0)
+ float currentZmm = Printer::currentPosition[Z_AXIS];
+ if (currentZmm/Printer::zLength > 0.1)
+ {
+ Com::printErrorFLN(PSTR("Calibration code is limited to bottom 10% of Z height"));
+ break;
+ }
+ if (com->hasR())
+ {
+ if (com->hasX() || com->hasY() || com->hasZ())
+ Com::printErrorFLN(PSTR("Cannot set radius and floor at same time."));
+ else if (com->R != 0)
+ {
+ //add r to radius
+ if (abs(com->R) <= 10) EEPROM::incrementRodRadius(com->R);
+ else Com::printErrorFLN(PSTR("Calibration movement is limited to 10mm."));
+ }
+ else
+ {
+ // auto set radius. Head must be at 0,0 and touching
+ // Z offset will be corrected for.
+ if (Printer::currentPosition[X_AXIS] == 0
+ && Printer::currentPosition[Y_AXIS] == 0)
+ {
+ if(Printer::isLargeMachine())
+ {
+ // calculate radius assuming we are at surface
+ // If Z is greater than 0 it will get calculated out for correct radius
+ // Use either A or B tower as they acnhor x cartesian axis and always have
+ // Radius distance to center in simplest set up.
+ float h = Printer::deltaDiagonalStepsSquaredB.f;
+ unsigned long bSteps = Printer::currentDeltaPositionSteps[B_TOWER];
+ // The correct Rod Radius would put us here at z==0 and B height is
+ // square root (rod length squared minus rod radius squared)
+ // Reverse that to get calculated Rod Radius given B height
+ h -= RMath::sqr((float)bSteps);
+ h = sqrt(h);
+ EEPROM::setRodRadius(h*Printer::invAxisStepsPerMM[Z_AXIS]);
+ }
+ else
+ {
+ // calculate radius assuming we are at surface
+ // If Z is greater than 0 it will get calculated out for correct radius
+ // Use either A or B tower as they acnhor x cartesian axis and always have
+ // Radius distance to center in simplest set up.
+ unsigned long h = Printer::deltaDiagonalStepsSquaredB.l;
+ unsigned long bSteps = Printer::currentDeltaPositionSteps[B_TOWER];
+ // The correct Rod Radius would put us here at z==0 and B height is
+ // square root (rod length squared minus rod radius squared)
+ // Reverse that to get calculated Rod Radius given B height
+ h -= RMath::sqr(bSteps);
+ h = SQRT(h);
+ EEPROM::setRodRadius(h*Printer::invAxisStepsPerMM[Z_AXIS]);
+ }
+ }
+ else
+ Com::printErrorFLN(PSTR("First move to touch at x,y=0,0 to auto-set radius."));
+ }
+ }
+ else
+ {
+ bool tooBig = false;
+ if (com->hasX())
+ {
+ if (abs(com->X) <= 10)
+ EEPROM::setTowerXFloor(com->X + currentZmm + Printer::xMin);
+ else tooBig = true;
+ }
+ if (com->hasY())
+ {
+ if (abs(com->Y) <= 10)
+ EEPROM::setTowerYFloor(com->Y + currentZmm + Printer::yMin);
+ else tooBig = true;
+ }
+ if (com->hasZ())
+ {
+ if (abs(com->Z) <= 10)
+ EEPROM::setTowerZFloor(com->Z + currentZmm + Printer::zMin);
+ else tooBig = true;
+ }
+ if (tooBig)
+ Com::printErrorFLN(PSTR("Calibration movement is limited to 10mm."));
+ }
+ // after adjusting zero, physical position is out of sync with memory position
+ // this could cause jerky movement or push head into print surface.
+ // moving gets back into safe zero'ed position with respect to newle set floor or Radius.
+ Printer::moveTo(IGNORE_COORDINATE,IGNORE_COORDINATE,12.0,IGNORE_COORDINATE,IGNORE_COORDINATE);
+ break;
+ }
+ case 131: // G131 Remove offset
+ {
+ float cx,cy,cz;
+ Printer::realPosition(cx,cy,cz);
+ float oldfeedrate = Printer::feedrate;
+ Printer::offsetX = 0;
+ Printer::offsetY = 0;
+ Printer::moveToReal(cx,cy,cz,IGNORE_COORDINATE,Printer::homingFeedrate[X_AXIS]);
+ Printer::feedrate = oldfeedrate;
+ Printer::updateCurrentPosition();
+ }
+ break;
+ case 132: // G132 Calibrate endstop offsets
+ {
+// This has the probably unintended side effect of turning off leveling.
+ Printer::setAutolevelActive(false); // don't let transformations change result!
+ Printer::coordinateOffset[X_AXIS] = 0;
+ Printer::coordinateOffset[Y_AXIS] = 0;
+ Printer::coordinateOffset[Z_AXIS] = 0;
+// I think this is coded incorrectly, as it depends on the biginning position of the
+// of the hot end, and so should first move to x,y,z= 0,0,0, but as that may not
+// be possible if the printer is not in the homes/zeroed state, the printer
+// cannot safely move to 0 z coordinate without crashong into the print surface.
+// so other than commenting, I'm not meddling.
+// but you will always get different counts from different positions.
+ Printer::deltaMoveToTopEndstops(Printer::homingFeedrate[Z_AXIS]);
+ int32_t m = RMath::max(Printer::stepsRemainingAtXHit,RMath::max(Printer::stepsRemainingAtYHit,Printer::stepsRemainingAtZHit));
+ int32_t offx = m - Printer::stepsRemainingAtXHit;
+ int32_t offy = m - Printer::stepsRemainingAtYHit;
+ int32_t offz = m - Printer::stepsRemainingAtZHit;
+ Com::printFLN(Com::tTower1, offx);
+ Com::printFLN(Com::tTower2, offy);
+ Com::printFLN(Com::tTower3, offz);
+#if EEPROM_MODE != 0
+ if(com->hasS() && com->S > 0)
+ {
+ EEPROM::setDeltaTowerXOffsetSteps(offx);
+ EEPROM::setDeltaTowerYOffsetSteps(offy);
+ EEPROM::setDeltaTowerZOffsetSteps(offz);
+ }
+#endif
+ Printer::homeAxis(true,true,true);
+ }
+ break;
+ case 133: // G133 Measure steps to top
+ {
+ bool oldAuto = Printer::isAutolevelActive();
+ Printer::setAutolevelActive(false); // don't let transformations change result!
+ Printer::currentPositionSteps[X_AXIS] = 0;
+ Printer::currentPositionSteps[Y_AXIS] = 0;
+ Printer::currentPositionSteps[Z_AXIS] = 0;
+ Printer::coordinateOffset[X_AXIS] = 0;
+ Printer::coordinateOffset[Y_AXIS] = 0;
+ Printer::coordinateOffset[Z_AXIS] = 0;
+ Printer::currentDeltaPositionSteps[A_TOWER] = 0;
+ Printer::currentDeltaPositionSteps[B_TOWER] = 0;
+ Printer::currentDeltaPositionSteps[C_TOWER] = 0;
+// similar to comment above, this will get a different answer from any different starting point
+// so it is unclear how this is helpful. It must start at a well defined point.
+ Printer::deltaMoveToTopEndstops(Printer::homingFeedrate[Z_AXIS]);
+ int32_t offx = HOME_DISTANCE_STEPS-Printer::stepsRemainingAtXHit;
+ int32_t offy = HOME_DISTANCE_STEPS-Printer::stepsRemainingAtYHit;
+ int32_t offz = HOME_DISTANCE_STEPS-Printer::stepsRemainingAtZHit;
+ Com::printFLN(Com::tTower1,offx);
+ Com::printFLN(Com::tTower2,offy);
+ Com::printFLN(Com::tTower3,offz);
+ Printer::setAutolevelActive(oldAuto);
+ Printer::homeAxis(true,true,true);
+ }
+ break;
+ case 134: // G134
+ Com::printF(PSTR("CompDelta:"),Printer::currentDeltaPositionSteps[A_TOWER]);
+ Com::printF(Com::tComma,Printer::currentDeltaPositionSteps[B_TOWER]);
+ Com::printFLN(Com::tComma,Printer::currentDeltaPositionSteps[C_TOWER]);
+#ifdef DEBUG_REAL_POSITION
+ Com::printF(PSTR("RealDelta:"),Printer::realDeltaPositionSteps[A_TOWER]);
+ Com::printF(Com::tComma,Printer::realDeltaPositionSteps[B_TOWER]);
+ Com::printFLN(Com::tComma,Printer::realDeltaPositionSteps[C_TOWER]);
+#endif
+ Printer::updateCurrentPosition();
+ Com::printF(PSTR("PosFromSteps:"));
+ printCurrentPosition(PSTR("G134 "));
+ break;
+
+#endif // DRIVE_SYSTEM
+#if defined(NUM_MOTOR_DRIVERS) && NUM_MOTOR_DRIVERS > 0
+ case 201:
+ commandG201(*com);
+ break;
+ case 202:
+ commandG202(*com);
+ break;
+ case 203:
+ commandG203(*com);
+ break;
+ case 204:
+ commandG204(*com);
+ break;
+#endif // defined
+ default:
+ if(!EVENT_UNHANDLED_G_CODE(com) && Printer::debugErrors())
+ {
+ Com::printF(Com::tUnknownCommand);
+ com->printCommand();
+ }
+ }
+ previousMillisCmd = HAL::timeInMilliseconds();
+}
+/**
+ \brief Execute the G command stored in com.
+*/
+void Commands::processMCode(GCode *com)
+{
+ uint32_t codenum; //throw away variable
+ switch( com->M )
+ {
+
+#if SDSUPPORT
+ case 20: // M20 - list SD card
+ sd.ls();
+ break;
+ case 21: // M21 - init SD card
+ sd.mount();
+ break;
+ case 22: //M22 - release SD card
+ sd.unmount();
+ break;
+ case 23: //M23 - Select file
+ if(com->hasString())
+ {
+ sd.fat.chdir();
+ sd.selectFile(com->text);
+ }
+ break;
+ case 24: //M24 - Start SD print
+ sd.startPrint();
+ break;
+ case 25: //M25 - Pause SD print
+ sd.pausePrint();
+ break;
+ case 26: //M26 - Set SD index
+ if(com->hasS())
+ sd.setIndex(com->S);
+ break;
+ case 27: //M27 - Get SD status
+ sd.printStatus();
+ break;
+ case 28: //M28 - Start SD write
+ if(com->hasString())
+ sd.startWrite(com->text);
+ break;
+ case 29: //M29 - Stop SD write
+ //processed in write to file routine above
+ //savetosd = false;
+ break;
+ case 30: // M30 filename - Delete file
+ if(com->hasString())
+ {
+ sd.fat.chdir();
+ sd.deleteFile(com->text);
+ }
+ break;
+ case 32: // M32 directoryname
+ if(com->hasString())
+ {
+ sd.fat.chdir();
+ sd.makeDirectory(com->text);
+ }
+ break;
+#endif
+ case 42: //M42 -Change pin status via gcode
+ if (com->hasP())
+ {
+ int pin_number = com->P;
+ for(uint8_t i = 0; i < (uint8_t)sizeof(sensitive_pins); i++)
+ {
+ if (pgm_read_byte(&sensitive_pins[i]) == pin_number)
+ {
+ pin_number = -1;
+ break;
+ }
+ }
+ if (pin_number > -1)
+ {
+ if(com->hasS())
+ {
+ if(com->S >= 0 && com->S <= 255)
+ {
+ pinMode(pin_number, OUTPUT);
+ digitalWrite(pin_number, com->S);
+ analogWrite(pin_number, com->S);
+ Com::printF(Com::tSetOutputSpace, pin_number);
+ Com::printFLN(Com::tSpaceToSpace,(int)com->S);
+ }
+ else
+ Com::printErrorFLN(PSTR("Illegal S value for M42"));
+ }
+ else
+ {
+ pinMode(pin_number, INPUT_PULLUP);
+ Com::printF(Com::tSpaceToSpace, pin_number);
+ Com::printFLN(Com::tSpaceIsSpace, digitalRead(pin_number));
+ }
+ }
+ else
+ {
+ Com::printErrorFLN(PSTR("Pin can not be set by M42, is in sensitive pins! "));
+ }
+ }
+ break;
+ case 80: // M80 - ATX Power On
+#if PS_ON_PIN>-1
+ Commands::waitUntilEndOfAllMoves();
+ previousMillisCmd = HAL::timeInMilliseconds();
+ SET_OUTPUT(PS_ON_PIN); //GND
+ Printer::setPowerOn(true);
+ WRITE(PS_ON_PIN, (POWER_INVERTING ? HIGH : LOW));
+#endif
+ break;
+ case 81: // M81 - ATX Power Off
+#if PS_ON_PIN>-1
+ Commands::waitUntilEndOfAllMoves();
+ SET_OUTPUT(PS_ON_PIN); //GND
+ Printer::setPowerOn(false);
+ WRITE(PS_ON_PIN,(POWER_INVERTING ? LOW : HIGH));
+#endif
+ break;
+ case 82: // M82
+ Printer::relativeExtruderCoordinateMode = false;
+ break;
+ case 83: // M83
+ Printer::relativeExtruderCoordinateMode = true;
+ break;
+ case 84: // M84
+ if(com->hasS())
+ {
+ stepperInactiveTime = com->S * 1000;
+ }
+ else
+ {
+ Commands::waitUntilEndOfAllMoves();
+ Printer::kill(true);
+ }
+ break;
+ case 85: // M85
+ if(com->hasS())
+ maxInactiveTime = (int32_t)com->S * 1000;
+ else
+ maxInactiveTime = 0;
+ break;
+ case 92: // M92
+ if(com->hasX()) Printer::axisStepsPerMM[X_AXIS] = com->X;
+ if(com->hasY()) Printer::axisStepsPerMM[Y_AXIS] = com->Y;
+ if(com->hasZ()) Printer::axisStepsPerMM[Z_AXIS] = com->Z;
+ Printer::updateDerivedParameter();
+ if(com->hasE())
+ {
+ Extruder::current->stepsPerMM = com->E;
+ Extruder::selectExtruderById(Extruder::current->id);
+ }
+ break;
+ case 99: // M99 S