Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/GCodes/GCodes.cpp')
-rw-r--r--src/GCodes/GCodes.cpp6605
1 files changed, 0 insertions, 6605 deletions
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
deleted file mode 100644
index 37602f94..00000000
--- a/src/GCodes/GCodes.cpp
+++ /dev/null
@@ -1,6605 +0,0 @@
-/****************************************************************************************************
-
- RepRapFirmware - G Codes
-
- This class interprets G Codes from one or more sources, and calls the functions in Move, Heat etc
- that drive the machine to do what the G Codes command.
-
- Most of the functions in here are designed not to wait, and they return a boolean. When you want them to do
- something, you call them. If they return false, the machine can't do what you want yet. So you go away
- and do something else. Then you try again. If they return true, the thing you wanted done has been done.
-
- -----------------------------------------------------------------------------------------------------
-
- Version 0.1
-
- 13 February 2013
-
- Adrian Bowyer
- RepRap Professional Ltd
- http://reprappro.com
-
- Licence: GPL
-
- ****************************************************************************************************/
-
-#include "RepRapFirmware.h"
-
-#ifdef DUET_NG
-#include "FirmwareUpdater.h"
-#endif
-
-#define DEGREE_SYMBOL "\xC2\xB0" // degree-symbol encoding in UTF8
-
-const char GCodes::axisLetters[MAX_AXES] = { 'X', 'Y', 'Z', 'U', 'V', 'W' };
-
-const char* BED_EQUATION_G = "bed.g";
-const char* PAUSE_G = "pause.g";
-const char* RESUME_G = "resume.g";
-const char* CANCEL_G = "cancel.g";
-const char* STOP_G = "stop.g";
-const char* SLEEP_G = "sleep.g";
-const char* homingFileNames[MAX_AXES] = { "homex.g", "homey.g", "homez.g", "homeu.g", "homev.g", "homew.g" };
-const char* HOME_ALL_G = "homeall.g";
-const char* HOME_DELTA_G = "homedelta.g";
-const char* DefaultHeightMapFile = "heightmap.csv";
-
-const size_t gcodeReplyLength = 2048; // long enough to pass back a reasonable number of files in response to M20
-
-const float MinServoPulseWidth = 544.0, MaxServoPulseWidth = 2400.0;
-const uint16_t ServoRefreshFrequency = 50;
-
-void GCodes::RestorePoint::Init()
-{
- for (size_t i = 0; i < DRIVES; ++i)
- {
- moveCoords[i] = 0.0;
- }
- feedRate = DEFAULT_FEEDRATE/minutesToSeconds;
-}
-
-GCodes::GCodes(Platform* p, Webserver* w) :
- platform(p), webserver(w), active(false), isFlashing(false),
- fileBeingHashed(nullptr)
-{
- httpGCode = new GCodeBuffer("http", HTTP_MESSAGE);
- telnetGCode = new GCodeBuffer("telnet", TELNET_MESSAGE);
- fileGCode = new GCodeBuffer("file", GENERIC_MESSAGE);
- serialGCode = new GCodeBuffer("serial", HOST_MESSAGE);
- auxGCode = new GCodeBuffer("aux", AUX_MESSAGE);
- daemonGCode = new GCodeBuffer("daemon", GENERIC_MESSAGE);
-}
-
-void GCodes::Exit()
-{
- platform->Message(HOST_MESSAGE, "GCodes class exited.\n");
- active = false;
-}
-
-void GCodes::Init()
-{
- Reset();
- numAxes = MIN_AXES;
- numExtruders = MaxExtruders;
- distanceScale = 1.0;
- rawExtruderTotal = 0.0;
- for (size_t extruder = 0; extruder < MaxExtruders; extruder++)
- {
- lastRawExtruderPosition[extruder] = 0.0;
- rawExtruderTotalByDrive[extruder] = 0.0;
- }
- eofString = EOF_STRING;
- eofStringCounter = 0;
- eofStringLength = strlen(eofString);
- offSetSet = false;
- zProbesSet = false;
- active = true;
- longWait = platform->Time();
- dwellTime = longWait;
- limitAxes = true;
- for(size_t axis = 0; axis < MAX_AXES; axis++)
- {
- axisScaleFactors[axis] = 1.0;
- }
- SetAllAxesNotHomed();
- for (size_t i = 0; i < NUM_FANS; ++i)
- {
- pausedFanValues[i] = 0.0;
- }
- lastDefaultFanSpeed = 0.0;
-
- retractLength = retractExtra = retractHop = 0.0;
- retractSpeed = unRetractSpeed = 600.0;
-}
-
-// This is called from Init and when doing an emergency stop
-void GCodes::Reset()
-{
- httpGCode->Init();
- telnetGCode->Init();
- fileGCode->Init();
- serialGCode->Init();
- auxGCode->Init();
- auxGCode->SetCommsProperties(1); // by default, we require a checksum on the aux port
- daemonGCode->Init();
-
- nextGcodeSource = 0;
-
- fileToPrint.Close();
- fileBeingWritten = NULL;
- dwellWaiting = false;
- probeCount = 0;
- cannedCycleMoveCount = 0;
- cannedCycleMoveQueued = false;
- speedFactor = 1.0 / minutesToSeconds; // default is just to convert from mm/minute to mm/second
- for (size_t i = 0; i < MaxExtruders; ++i)
- {
- extrusionFactors[i] = 1.0;
- }
- for (size_t i = 0; i < DRIVES; ++i)
- {
- moveBuffer.coords[i] = 0.0;
- }
- moveBuffer.xAxes = DefaultXAxisMapping;
-
- feedRate = DEFAULT_FEEDRATE/minutesToSeconds;
- pauseRestorePoint.Init();
- toolChangeRestorePoint.Init();
-
- ClearMove();
-
- for (size_t i = 0; i < MaxTriggers; ++i)
- {
- triggers[i].Init();
- }
- triggersPending = 0;
-
- simulationMode = 0;
- simulationTime = 0.0;
- isPaused = false;
- filePos = moveBuffer.filePos = noFilePosition;
- lastEndstopStates = platform->GetAllEndstopStates();
- firmwareUpdateModuleMap = 0;
-
- cancelWait = isWaiting = false;
-
- for (size_t i = 0; i < NumResources; ++i)
- {
- resourceOwners[i] = nullptr;
- }
-}
-
-float GCodes::FractionOfFilePrinted() const
-{
- const FileData& fileBeingPrinted = fileGCode->OriginalMachineState().fileState;
- return (fileBeingPrinted.IsLive()) ? fileBeingPrinted.FractionRead() : -1.0;
-}
-
-// Start running the config file
-// We use triggerCGode as the source to prevent any triggers being executed until we have finished
-bool GCodes::RunConfigFile(const char* fileName)
-{
- return DoFileMacro(*daemonGCode, fileName, false);
-}
-
-// Are we still running the config file?
-bool GCodes::IsRunningConfigFile() const
-{
- return daemonGCode->MachineState().fileState.IsLive();
-}
-
-void GCodes::Spin()
-{
- if (!active)
- {
- return;
- }
-
- CheckTriggers();
-
- // Get the GCodeBuffer that we want to work from
- GCodeBuffer& gb = *(gcodeSources[nextGcodeSource]);
-
- // Set up a buffer for the reply
- char replyBuffer[gcodeReplyLength];
- StringRef reply(replyBuffer, ARRAY_SIZE(replyBuffer));
- reply.Clear();
-
- if (gb.GetState() == GCodeState::normal)
- {
- StartNextGCode(gb, reply);
- }
- else
- {
- // Perform the next operation of the state machine for this gcode source
- bool error = false;
-
- switch (gb.GetState())
- {
- case GCodeState::waitingForMoveToComplete:
- if (AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- gb.SetState(GCodeState::normal);
- }
- break;
-
- case GCodeState::homing:
- if (toBeHomed == 0)
- {
- gb.SetState(GCodeState::normal);
- }
- else
- {
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- // Leave the Z axis until all other axes are done
- if ((toBeHomed & (1u << axis)) != 0 && (axis != Z_AXIS || toBeHomed == (1u << Z_AXIS)))
- {
- toBeHomed &= ~(1u << axis);
- DoFileMacro(gb, homingFileNames[axis]);
- break;
- }
- }
- }
- break;
-
- case GCodeState::setBed1:
- reprap.GetMove()->SetIdentityTransform();
- probeCount = 0;
- gb.SetState(GCodeState::setBed2);
- // no break
-
- case GCodeState::setBed2:
- {
- int numProbePoints = reprap.GetMove()->NumberOfXYProbePoints();
- if (DoSingleZProbeAtPoint(gb, probeCount, 0.0))
- {
- probeCount++;
- if (probeCount >= numProbePoints)
- {
- zProbesSet = true;
- reprap.GetMove()->FinishedBedProbing(0, reply);
- gb.SetState(GCodeState::normal);
- }
- }
- }
- break;
-
- case GCodeState::toolChange1: // Release the old tool (if any)
- {
- const Tool *oldTool = reprap.GetCurrentTool();
- if (oldTool != NULL)
- {
- reprap.StandbyTool(oldTool->Number());
- }
- }
- gb.SetState(GCodeState::toolChange2);
- if (reprap.GetTool(newToolNumber) != nullptr && AllAxesAreHomed())
- {
- scratchString.printf("tpre%d.g", newToolNumber);
- DoFileMacro(gb, scratchString.Pointer(), false);
- }
- break;
-
- case GCodeState::toolChange2: // Select the new tool (even if it doesn't exist - that just deselects all tools)
- reprap.SelectTool(newToolNumber);
- gb.SetState(GCodeState::toolChange3);
- if (reprap.GetTool(newToolNumber) != nullptr && AllAxesAreHomed())
- {
- scratchString.printf("tpost%d.g", newToolNumber);
- DoFileMacro(gb, scratchString.Pointer(), false);
- }
- break;
-
- case GCodeState::toolChange3:
- gb.SetState(GCodeState::normal);
- break;
-
- case GCodeState::pausing1:
- if (AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- gb.SetState(GCodeState::pausing2);
- DoFileMacro(gb, PAUSE_G);
- }
- break;
-
- case GCodeState::pausing2:
- if (AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- reply.copy("Printing paused");
- gb.SetState(GCodeState::normal);
- }
- break;
-
- case GCodeState::resuming1:
- case GCodeState::resuming2:
- // Here when we have just finished running the resume macro file.
- // Move the head back to the paused location
- if (AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- float currentZ = moveBuffer.coords[Z_AXIS];
- for (size_t drive = 0; drive < numAxes; ++drive)
- {
- moveBuffer.coords[drive] = pauseRestorePoint.moveCoords[drive];
- }
- for (size_t drive = numAxes; drive < DRIVES; ++drive)
- {
- moveBuffer.coords[drive] = 0.0;
- }
- moveBuffer.feedRate = DEFAULT_FEEDRATE/minutesToSeconds; // ask for a good feed rate, we may have paused during a slow move
- moveBuffer.moveType = 0;
- moveBuffer.endStopsToCheck = 0;
- moveBuffer.usePressureAdvance = false;
- moveBuffer.filePos = noFilePosition;
- if (gb.GetState() == GCodeState::resuming1 && currentZ > pauseRestorePoint.moveCoords[Z_AXIS])
- {
- // First move the head to the correct XY point, then move it down in a separate move
- moveBuffer.coords[Z_AXIS] = currentZ;
- gb.SetState(GCodeState::resuming2);
- }
- else
- {
- // Just move to the saved position in one go
- gb.SetState(GCodeState::resuming3);
- }
- moveAvailable = true;
- }
- break;
-
- case GCodeState::resuming3:
- if (AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- for (size_t i = 0; i < NUM_FANS; ++i)
- {
- platform->SetFanValue(i, pausedFanValues[i]);
- }
- for (size_t drive = numAxes; drive < DRIVES; ++drive)
- {
- lastRawExtruderPosition[drive - numAxes] = pauseRestorePoint.moveCoords[drive]; // reset the extruder position in case we are receiving absolute extruder moves
- }
- feedRate = pauseRestorePoint.feedRate;
- isPaused = false;
- reply.copy("Printing resumed");
- gb.SetState(GCodeState::normal);
- }
- break;
-
- case GCodeState::flashing1:
-#ifdef DUET_NG
- // Update additional modules before the main firmware
- if (FirmwareUpdater::IsReady())
- {
- bool updating = false;
- for (unsigned int module = 1; module < NumFirmwareUpdateModules; ++module)
- {
- if ((firmwareUpdateModuleMap & (1u << module)) != 0)
- {
- firmwareUpdateModuleMap &= ~(1u << module);
- FirmwareUpdater::UpdateModule(module);
- updating = true;
- break;
- }
- }
- if (!updating)
- {
- gb.SetState(GCodeState::flashing2);
- }
- }
-#else
- gb.SetState(GCodeState::flashing2);
-#endif
- break;
-
- case GCodeState::flashing2:
- if ((firmwareUpdateModuleMap & 1) != 0)
- {
- // Update main firmware
- firmwareUpdateModuleMap = 0;
- platform->UpdateFirmware();
- // The above call does not return unless an error occurred
- }
- isFlashing = false;
- gb.SetState(GCodeState::normal);
- break;
-
- case GCodeState::stopping: // MO after executing stop.g if present
- case GCodeState::sleeping: // M1 after executing sleep.g if present
- // Deselect the active tool and turn off all heaters, unless parameter Hn was used with n > 0
- if (!gb.Seen('H') || gb.GetIValue() <= 0)
- {
- Tool* tool = reprap.GetCurrentTool();
- if (tool != nullptr)
- {
- reprap.StandbyTool(tool->Number());
- }
- reprap.GetHeat()->SwitchOffAll();
- }
-
- // chrishamm 2014-18-10: Although RRP says M0 is supposed to turn off all drives and heaters,
- // I think M1 is sufficient for this purpose. Leave M0 for a normal reset.
- if (gb.GetState() == GCodeState::sleeping)
- {
- DisableDrives();
- }
- else
- {
- platform->SetDriversIdle();
- }
- gb.SetState(GCodeState::normal);
- break;
-
- case GCodeState::gridProbing1: // ready to move to next grid probe point
- {
- // Move to the current probe point
- const GridDefinition grid = reprap.GetMove()->GetBedProbeGrid();
- const float x = grid.GetXCoordinate(gridXindex);
- const float y = grid.GetYCoordinate(gridYindex);
- if (grid.IsInRadius(x, y) && platform->IsAccessibleProbePoint(x, y))
- {
- moveBuffer.moveType = 0;
- moveBuffer.endStopsToCheck = 0;
- moveBuffer.usePressureAdvance = false;
- moveBuffer.filePos = noFilePosition;
- moveBuffer.coords[X_AXIS] = x - platform->GetZProbeParameters().xOffset;
- moveBuffer.coords[Y_AXIS] = y - platform->GetZProbeParameters().yOffset;
- moveBuffer.coords[Z_AXIS] = platform->GetZProbeDiveHeight();
- moveBuffer.feedRate = platform->GetZProbeTravelSpeed();
- moveBuffer.xAxes = 0;
- moveAvailable = true;
- gb.SetState(GCodeState::gridProbing2);
- }
- else
- {
- gb.SetState(GCodeState::gridProbing4);
- }
- }
- break;
-
- case GCodeState::gridProbing2: // ready to probe the current grid probe point
- if (AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- // Probe the bed at the current XY coordinates
- // Check for probe already triggered at start
- if (reprap.GetPlatform()->GetZProbeResult() == EndStopHit::lowHit)
- {
- reply.copy("Z probe already triggered before probing move started");
- error = true;
- gb.SetState(GCodeState::normal);
- break;
- }
-
- zProbeTriggered = false;
- moveBuffer.moveType = 0;
- moveBuffer.endStopsToCheck = ZProbeActive;
- moveBuffer.usePressureAdvance = false;
- moveBuffer.filePos = noFilePosition;
- moveBuffer.coords[Z_AXIS] = -platform->GetZProbeDiveHeight();
- moveBuffer.feedRate = platform->GetZProbeParameters().probeSpeed;
- moveBuffer.xAxes = 0;
- moveAvailable = true;
- gb.SetState(GCodeState::gridProbing3);
- }
- break;
-
- case GCodeState::gridProbing3: // ready to lift the probe after probing the current grid probe point
- if (AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- if (!zProbeTriggered)
- {
- reply.copy("Z probe was not triggered during probing move");
- error = true;
- gb.SetState(GCodeState::normal);
- break;
- }
-
- const float heightError = moveBuffer.coords[Z_AXIS] - platform->ZProbeStopHeight();
- reprap.GetMove()->SetGridHeight(gridXindex, gridYindex, heightError);
- ++numPointsProbed;
- heightSum += (double)heightError;
- heightSquaredSum += (double)heightError * (double)heightError;
-
- // Move back up to the dive height
- moveBuffer.moveType = 0;
- moveBuffer.endStopsToCheck = 0;
- moveBuffer.usePressureAdvance = false;
- moveBuffer.filePos = noFilePosition;
- moveBuffer.coords[Z_AXIS] = platform->GetZProbeDiveHeight();
- moveBuffer.feedRate = platform->GetZProbeTravelSpeed();
- moveBuffer.xAxes = 0;
- moveAvailable = true;
- gb.SetState(GCodeState::gridProbing4);
- }
- break;
-
- case GCodeState::gridProbing4: // ready to compute the next probe point
- if (AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- const GridDefinition grid = reprap.GetMove()->GetBedProbeGrid();
- if (gridYindex & 1)
- {
- // Odd row, so decreasing X
- if (gridXindex == 0)
- {
- ++gridYindex;
- }
- else
- {
- --gridXindex;
- }
- }
- else
- {
- // Even row, so increasing X
- if (gridXindex + 1 == grid.NumXpoints())
- {
- ++gridYindex;
- }
- else
- {
- ++gridXindex;
- }
- }
- if (gridYindex == grid.NumYpoints())
- {
- // Finished probing the grid
- if (numPointsProbed >= 4)
- {
- error = reprap.GetMove()->SaveHeightMapToFile(heightMapFile, reply);
- const double mean = heightSum/numPointsProbed;
- const double deviation = sqrt(((heightSquaredSum * numPointsProbed) - (heightSum * heightSum)))/numPointsProbed;
- reply.catf(" - %u points probed, mean error %.2f, deviation %.2f", numPointsProbed, mean, deviation);
- reprap.GetMove()->UseHeightMap();
- }
- else
- {
- reply.copy("Too few points probed");
- error = true;
- }
- gb.SetState(GCodeState::normal);
- }
- else
- {
- gb.SetState(GCodeState::gridProbing1);
- }
- }
- break;
-
- default: // should not happen
- platform->Message(GENERIC_MESSAGE, "Error: undefined GCodeState\n");
- gb.SetState(GCodeState::normal);
- break;
- }
-
- if (gb.GetState() == GCodeState::normal)
- {
- // We completed a command, so unlock resources and tell the host about it
- UnlockAll(gb);
- HandleReply(gb, error, reply.Pointer());
- }
- }
-
- // Move on to the next gcode source ready for next time
- ++nextGcodeSource;
- if (nextGcodeSource == ARRAY_SIZE(gcodeSources))
- {
- nextGcodeSource = 0;
- }
-
- platform->ClassReport(longWait);
-}
-
-// Start a new gcode, or continue to execute one that has already been started:
-void GCodes::StartNextGCode(GCodeBuffer& gb, StringRef& reply)
-{
- if (isPaused && &gb == fileGCode)
- {
- // We are paused, so don't process any more gcodes from the file being printed.
- // There is a potential issue here if fileGCode holds any locks, so unlock everything.
- UnlockAll(gb);
- }
- else if (gb.IsReady() || gb.IsExecuting())
- {
- gb.SetFinished(ActOnCode(gb, reply));
- }
- else if (gb.MachineState().fileState.IsLive())
- {
- DoFilePrint(gb, reply);
- }
- else if (&gb == httpGCode)
- {
- // Webserver
- for (unsigned int i = 0; i < 16 && webserver->GCodeAvailable(WebSource::HTTP); ++i)
- {
- const char b = webserver->ReadGCode(WebSource::HTTP);
- if (gb.Put(b))
- {
- // We have a complete gcode
- if (gb.WritingFileDirectory() != nullptr)
- {
- WriteGCodeToFile(gb);
- gb.SetFinished(true);
- }
- else
- {
- gb.SetFinished(ActOnCode(gb, reply));
- }
- break;
- }
- }
- }
- else if (&gb == telnetGCode)
- {
- // Telnet
- for (unsigned int i = 0; i < GCODE_LENGTH && webserver->GCodeAvailable(WebSource::Telnet); ++i)
- {
- char b = webserver->ReadGCode(WebSource::Telnet);
- if (gb.Put(b))
- {
- gb.SetFinished(ActOnCode(gb, reply));
- break;
- }
- }
- }
- else if (&gb == serialGCode)
- {
- // USB interface
- for (unsigned int i = 0; i < 16 && platform->GCodeAvailable(SerialSource::USB); ++i)
- {
- const char b = platform->ReadFromSource(SerialSource::USB);
- // Check the special case of uploading the reprap.htm file
- if (gb.WritingFileDirectory() == platform->GetWebDir())
- {
- WriteHTMLToFile(gb, b);
- }
- else if (gb.Put(b)) // add char to buffer and test whether the gcode is complete
- {
- // We have a complete gcode
- if (gb.WritingFileDirectory() != nullptr)
- {
- WriteGCodeToFile(gb);
- gb.SetFinished(true);
- }
- else
- {
- gb.SetFinished(ActOnCode(gb, reply));
- }
- break;
- }
- }
- }
- else if (&gb == auxGCode)
- {
- // Aux serial port (typically PanelDue)
- for (unsigned int i = 0; i < 16 && platform->GCodeAvailable(SerialSource::AUX); ++i)
- {
- char b = platform->ReadFromSource(SerialSource::AUX);
- if (gb.Put(b)) // add char to buffer and test whether the gcode is complete
- {
- platform->SetAuxDetected();
- gb.SetFinished(ActOnCode(gb, reply));
- break;
- }
- }
- }
-}
-
-void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply)
-{
- FileData& fd = gb.MachineState().fileState;
- for (int i = 0; i < 50 && fd.IsLive(); ++i)
- {
- char b;
- if (fd.Read(b))
- {
- if (gb.StartingNewCode() && &gb == fileGCode && gb.MachineState().previous == nullptr)
- {
- filePos = fd.GetPosition() - 1;
- //debugPrintf("Set file pos %u\n", filePos);
- }
- if (gb.Put(b))
- {
- gb.SetFinished(ActOnCode(gb, reply));
- return;
- }
- }
- else
- {
- // We have reached the end of the file. Check for the last line of gcode not ending in newline.
- if (!gb.StartingNewCode()) // if there is something in the buffer
- {
- if (gb.Put('\n')) // in case there wasn't a newline ending the file
- {
- gb.SetFinished(ActOnCode(gb, reply));
- return;
- }
- }
-
- gb.Init(); // mark buffer as empty
-
- // Don't close the file until all moves have been completed, in case the print gets paused.
- // Also, this keeps the state as 'Printing' until the print really has finished.
- if (AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- fd.Close();
- if (gb.MachineState().previous == nullptr)
- {
- // Finished printing SD card file
- reprap.GetPrintMonitor()->StoppedPrint();
- if (platform->Emulating() == marlin)
- {
- // Pronterface expects a "Done printing" message
- HandleReply(gb, false, "Done printing file");
- }
- }
- else
- {
- // Finished a macro
- Pop(gb);
- gb.Init();
- if (gb.GetState() == GCodeState::normal)
- {
- UnlockAll(gb);
- HandleReply(gb, false, "");
- }
- }
- }
- return;
- }
- }
-}
-
-// Check for and execute triggers
-void GCodes::CheckTriggers()
-{
- // Check for endstop state changes that activate new triggers
- const TriggerMask oldEndstopStates = lastEndstopStates;
- lastEndstopStates = platform->GetAllEndstopStates();
- const TriggerMask risen = lastEndstopStates & ~oldEndstopStates,
- fallen = ~lastEndstopStates & oldEndstopStates;
- unsigned int lowestTriggerPending = MaxTriggers;
- for (unsigned int triggerNumber = 0; triggerNumber < MaxTriggers; ++triggerNumber)
- {
- const Trigger& ct = triggers[triggerNumber];
- if ( ((ct.rising & risen) != 0 || (ct.falling & fallen) != 0)
- && (ct.condition == 0 || (ct.condition == 1 && reprap.GetPrintMonitor()->IsPrinting()))
- )
- {
- triggersPending |= (1u << triggerNumber);
- }
- if (triggerNumber < lowestTriggerPending && (triggersPending & (1u << triggerNumber)) != 0)
- {
- lowestTriggerPending = triggerNumber;
- }
- }
-
- // If any triggers are pending, activate the one with the lowest number
- if (lowestTriggerPending == 0)
- {
- triggersPending &= ~(1u << lowestTriggerPending); // clear the trigger
- DoEmergencyStop();
- }
- else if (lowestTriggerPending < MaxTriggers // if a trigger is pending
- && !daemonGCode->MachineState().fileState.IsLive()
- && daemonGCode->GetState() == GCodeState::normal // and we are not already executing a trigger or config.g
- )
- {
- if (lowestTriggerPending == 1)
- {
- if (isPaused || !reprap.GetPrintMonitor()->IsPrinting())
- {
- triggersPending &= ~(1u << lowestTriggerPending); // ignore a pause trigger if we are already paused
- }
- else if (LockMovement(*daemonGCode)) // need to lock movement before executing the pause macro
- {
- triggersPending &= ~(1u << lowestTriggerPending); // clear the trigger
- DoPause(*daemonGCode);
- }
- }
- else
- {
- triggersPending &= ~(1u << lowestTriggerPending); // clear the trigger
- char buffer[25];
- StringRef filename(buffer, ARRAY_SIZE(buffer));
- filename.printf(SYS_DIR "trigger%u.g", lowestTriggerPending);
- DoFileMacro(*daemonGCode, filename.Pointer(), true);
- }
- }
-}
-
-// Execute an emergency stop
-void GCodes::DoEmergencyStop()
-{
- reprap.EmergencyStop();
- Reset();
- platform->Message(GENERIC_MESSAGE, "Emergency Stop! Reset the controller to continue.");
-}
-
-// Pause the print. Before calling this, check that we are doing a file print that isn't already paused and get the movement lock.
-void GCodes::DoPause(GCodeBuffer& gb)
-{
- if (&gb == fileGCode)
- {
- // Pausing a file print because of a command in the file itself
- for (size_t drive = 0; drive < numAxes; ++drive)
- {
- pauseRestorePoint.moveCoords[drive] = moveBuffer.coords[drive];
- }
- for (size_t drive = numAxes; drive < DRIVES; ++drive)
- {
- pauseRestorePoint.moveCoords[drive] = lastRawExtruderPosition[drive - numAxes]; // get current extruder positions into pausedMoveBuffer
- }
- pauseRestorePoint.feedRate = feedRate;
- }
- else
- {
- // Pausing a file print via another input source
- pauseRestorePoint.feedRate = feedRate; // the call to PausePrint may or may not change this
- FilePosition fPos = reprap.GetMove()->PausePrint(pauseRestorePoint.moveCoords, pauseRestorePoint.feedRate, reprap.GetCurrentXAxes());
- // tell Move we wish to pause the current print
- FileData& fdata = fileGCode->MachineState().fileState;
- if (fPos != noFilePosition && fdata.IsLive())
- {
- fdata.Seek(fPos); // replay the abandoned instructions if/when we resume
- }
- if (moveAvailable)
- {
- for (size_t drive = numAxes; drive < DRIVES; ++drive)
- {
- pauseRestorePoint.moveCoords[drive] += moveBuffer.coords[drive]; // add on the extrusion in the move not yet taken
- }
- ClearMove();
- }
-
- for (size_t drive = numAxes; drive < DRIVES; ++drive)
- {
- pauseRestorePoint.moveCoords[drive] = lastRawExtruderPosition[drive - numAxes] - pauseRestorePoint.moveCoords[drive];
- }
-
- //TODO record the virtual extruder positions of mixing tools too. But that's very hard to do unless we store it in the move.
-
- if (reprap.Debug(moduleGcodes))
- {
- platform->MessageF(GENERIC_MESSAGE, "Paused print, file offset=%u\n", fPos);
- }
- }
-
- for (size_t i = 0; i < NUM_FANS; ++i)
- {
- pausedFanValues[i] = platform->GetFanValue(i);
- }
- gb.SetState(GCodeState::pausing1);
- isPaused = true;
-}
-
-void GCodes::Diagnostics(MessageType mtype)
-{
- platform->Message(mtype, "=== GCodes ===\n");
- platform->MessageF(mtype, "Move available? %s\n", moveAvailable ? "yes" : "no");
- platform->MessageF(mtype, "Stack records: %u allocated, %u in use\n", GCodeMachineState::GetNumAllocated(), GCodeMachineState::GetNumInUse());
-
- for (size_t i = 0; i < ARRAY_SIZE(gcodeSources); ++i)
- {
- gcodeSources[i]->Diagnostics(mtype);
- }
-}
-
-// The wait till everything's done function. If you need the machine to
-// be idle before you do something (for example homing an axis, or shutting down) call this
-// until it returns true. As a side-effect it loads moveBuffer with the last position and feedrate for you.
-bool GCodes::AllMovesAreFinishedAndMoveBufferIsLoaded()
-{
- // Last one gone?
- if (moveAvailable)
- {
- return false;
- }
-
- // Wait for all the queued moves to stop so we get the actual last position
- if (!reprap.GetMove()->AllMovesAreFinished())
- {
- return false;
- }
-
- reprap.GetMove()->ResumeMoving();
- reprap.GetMove()->GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes());
- return true;
-}
-
-// Save (some of) the state of the machine for recovery in the future.
-bool GCodes::Push(GCodeBuffer& gb)
-{
- bool ok = gb.PushState();
- if (!ok)
- {
- platform->Message(GENERIC_MESSAGE, "Push(): stack overflow!\n");
- }
- return ok;
-}
-
-// Recover a saved state
-void GCodes::Pop(GCodeBuffer& gb)
-{
- if (!gb.PopState())
- {
- platform->Message(GENERIC_MESSAGE, "Pop(): stack underflow!\n");
- }
-}
-
-// Move expects all axis movements to be absolute, and all extruder drive moves to be relative. This function serves that.
-// 'moveType' is the S parameter in the G0 or G1 command, or -1 if we are doing G92.
-// For regular (type 0) moves, we apply limits and do X axis mapping.
-// Returns true if we have a legal move (or G92 argument), false if this gcode should be discarded
-bool GCodes::LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType)
-{
- // Zero every extruder drive as some drives may not be changed
- for (size_t drive = numAxes; drive < DRIVES; drive++)
- {
- moveBuffer.coords[drive] = 0.0;
- }
-
- // Deal with feed rate
- if (gb.Seen(feedrateLetter))
- {
- feedRate = gb.GetFValue() * distanceScale * speedFactor;
- }
- moveBuffer.feedRate = feedRate;
-
- // First do extrusion, and check, if we are extruding, that we have a tool to extrude with
- Tool* tool = reprap.GetCurrentTool();
- if (gb.Seen(extrudeLetter))
- {
- if (tool == nullptr)
- {
- platform->Message(GENERIC_MESSAGE, "Attempting to extrude with no tool selected.\n");
- return false;
- }
- size_t eMoveCount = tool->DriveCount();
- if (eMoveCount > 0)
- {
- // Set the drive values for this tool.
- // chrishamm-2014-10-03: Do NOT check extruder temperatures here, because we may be executing queued codes like M116
- if (tool->GetMixing())
- {
- const float moveArg = gb.GetFValue() * distanceScale;
- if (moveType == -1) // if doing G92
- {
- tool->virtualExtruderPosition = moveArg;
- }
- else
- {
- const float requestedExtrusionAmount = (gb.MachineState().drivesRelative)
- ? moveArg
- : moveArg - tool->virtualExtruderPosition;
- for (size_t eDrive = 0; eDrive < eMoveCount; eDrive++)
- {
- const int drive = tool->Drive(eDrive);
- const float extrusionAmount = requestedExtrusionAmount * tool->GetMix()[eDrive];
- lastRawExtruderPosition[drive] += extrusionAmount;
- rawExtruderTotalByDrive[drive] += extrusionAmount;
- rawExtruderTotal += extrusionAmount;
- moveBuffer.coords[drive + numAxes] = extrusionAmount * extrusionFactors[drive];
- }
- }
- }
- else
- {
- float eMovement[MaxExtruders];
- size_t mc = eMoveCount;
- gb.GetFloatArray(eMovement, mc, false);
- if (eMoveCount != mc)
- {
- platform->MessageF(GENERIC_MESSAGE, "Wrong number of extruder drives for the selected tool: %s\n", gb.Buffer());
- return false;
- }
-
- for (size_t eDrive = 0; eDrive < eMoveCount; eDrive++)
- {
- const int drive = tool->Drive(eDrive);
- const float moveArg = eMovement[eDrive] * distanceScale;
- if (moveType == -1)
- {
- moveBuffer.coords[drive + numAxes] = moveArg;
- lastRawExtruderPosition[drive] = moveArg;
- }
- else
- {
- const float extrusionAmount = (gb.MachineState().drivesRelative)
- ? moveArg
- : moveArg - lastRawExtruderPosition[drive];
- lastRawExtruderPosition[drive] += extrusionAmount;
- rawExtruderTotalByDrive[drive] += extrusionAmount;
- rawExtruderTotal += extrusionAmount;
- moveBuffer.coords[drive + numAxes] = extrusionAmount * extrusionFactors[drive];
- }
- }
- }
- }
- }
-
- // Now the movement axes
- const Tool *currentTool = reprap.GetCurrentTool();
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- float moveArg = gb.GetFValue() * distanceScale * axisScaleFactors[axis];
- if (moveType == -1) // if doing G92
- {
- SetAxisIsHomed(axis); // doing a G92 defines the absolute axis position
- moveBuffer.coords[axis] = moveArg;
- }
- else if (axis == X_AXIS && moveType == 0 && currentTool != nullptr)
- {
- // Perform X axis mapping
- const uint32_t xMap = currentTool->GetXAxisMap();
- for (size_t mappedAxis = 0; mappedAxis < numAxes; ++mappedAxis)
- {
- if ((xMap & (1u << mappedAxis)) != 0)
- {
- float mappedMoveArg = moveArg;
- if (gb.MachineState().axesRelative)
- {
- mappedMoveArg += moveBuffer.coords[mappedAxis];
- }
- else
- {
- mappedMoveArg -= currentTool->GetOffset()[mappedAxis]; // adjust requested position to compensate for tool offset
- }
- moveBuffer.coords[mappedAxis] = mappedMoveArg;
- }
- }
- }
- else
- {
- if (gb.MachineState().axesRelative)
- {
- moveArg += moveBuffer.coords[axis];
- }
- else if (currentTool != nullptr && moveType == 0)
- {
- moveArg -= currentTool->GetOffset()[axis]; // adjust requested position to compensate for tool offset
- }
- moveBuffer.coords[axis] = moveArg;
- }
- }
- }
-
- // If doing a regular move and applying limits, limit all axes
- if (moveType == 0 && limitAxes
-#if SUPPORT_ROLAND
- && !reprap.GetRoland()->Active()
-#endif
- )
- {
- if (!reprap.GetMove()->IsDeltaMode())
- {
- // Cartesian or CoreXY printer, so limit those axes that have been homed
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (GetAxisIsHomed(axis))
- {
- float& f = moveBuffer.coords[axis];
- if (f < platform->AxisMinimum(axis))
- {
- f = platform->AxisMinimum(axis);
- }
- else if (f > platform->AxisMaximum(axis))
- {
- f = platform->AxisMaximum(axis);
- }
- }
- }
- }
- else if (AllAxesAreHomed()) // this is to allow extruder-only moves before homing
- {
- // If axes have been homed on a delta printer and this isn't a homing move, check for movements outside limits.
- // Skip this check if axes have not been homed, so that extruder-only moved are allowed before homing
- // Constrain the move to be within the build radius
- const float diagonalSquared = fsquare(moveBuffer.coords[X_AXIS]) + fsquare(moveBuffer.coords[Y_AXIS]);
- if (diagonalSquared > reprap.GetMove()->GetDeltaParams().GetPrintRadiusSquared())
- {
- const float factor = sqrtf(reprap.GetMove()->GetDeltaParams().GetPrintRadiusSquared() / diagonalSquared);
- moveBuffer.coords[X_AXIS] *= factor;
- moveBuffer.coords[Y_AXIS] *= factor;
- }
-
- // Constrain the end height of the move to be no greater than the homed height and no lower than -0.2mm
- moveBuffer.coords[Z_AXIS] = max<float>(platform->AxisMinimum(Z_AXIS),
- min<float>(moveBuffer.coords[Z_AXIS], reprap.GetMove()->GetDeltaParams().GetHomedHeight()));
- }
- }
-
- return true;
-}
-
-// This function is called for a G Code that makes a move.
-// If the Move class can't receive the move (i.e. things have to wait), return 0.
-// If we have queued the move and the caller doesn't need to wait for it to complete, return 1.
-// If we need to wait for the move to complete before doing another one (e.g. because endstops are checked in this move), return 2.
-int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply)
-{
- // Last one gone yet?
- if (moveAvailable)
- {
- return 0;
- }
-
- // Check to see if the move is a 'homing' move that endstops are checked on.
- moveBuffer.endStopsToCheck = 0;
- moveBuffer.moveType = 0;
- moveBuffer.xAxes = reprap.GetCurrentXAxes();
- if (gb.Seen('S'))
- {
- int ival = gb.GetIValue();
- if (ival == 1 || ival == 2)
- {
- moveBuffer.moveType = ival;
- moveBuffer.xAxes = 0; // don't do bed compensation
- }
-
- if (ival == 1)
- {
- for (size_t i = 0; i < numAxes; ++i)
- {
- if (gb.Seen(axisLetters[i]))
- {
- moveBuffer.endStopsToCheck |= (1u << i);
- }
- }
- }
- else if (ival == 99) // temporary code to log Z probe change positions
- {
- moveBuffer.endStopsToCheck |= LogProbeChanges;
- }
- }
-
- if (reprap.GetMove()->IsDeltaMode())
- {
- // Extra checks to avoid damaging delta printers
- if (moveBuffer.moveType != 0 && !gb.MachineState().axesRelative)
- {
- // We have been asked to do a move without delta mapping on a delta machine, but the move is not relative.
- // This may be damaging and is almost certainly a user mistake, so ignore the move.
- reply.copy("Attempt to move the motors of a delta printer to absolute positions");
- return 1;
- }
-
- if (moveBuffer.moveType == 0 && !AllAxesAreHomed())
- {
- // The user may be attempting to move a delta printer to an XYZ position before homing the axes
- // This may be damaging and is almost certainly a user mistake, so ignore the move. But allow extruder-only moves.
- if (gb.Seen(axisLetters[X_AXIS]) || gb.Seen(axisLetters[Y_AXIS]) || gb.Seen(axisLetters[Z_AXIS]))
- {
- reply.copy("Attempt to move the head of a delta printer before homing the towers");
- return 1;
- }
- }
- }
-
- // Load the last position and feed rate into moveBuffer
-#if SUPPORT_ROLAND
- if (reprap.GetRoland()->Active())
- {
- reprap.GetRoland()->GetCurrentRolandPosition(moveBuffer);
- }
- else
-#endif
- {
- reprap.GetMove()->GetCurrentUserPosition(moveBuffer.coords, moveBuffer.moveType, reprap.GetCurrentXAxes());
- }
-
- // Load the move buffer with either the absolute movement required or the relative movement required
- float oldCoords[MAX_AXES];
- memcpy(oldCoords, moveBuffer.coords, sizeof(oldCoords));
- moveAvailable = LoadMoveBufferFromGCode(gb, moveBuffer.moveType);
- if (moveAvailable)
- {
- // Flag whether we should use pressure advance, if there is any extrusion in this move.
- // We assume it is a normal printing move needing pressure advance if there is forward extrusion and XY movement.
- // The movement code will only apply pressure advance if there is forward extrusion, so we only need to check for XY movement here.
- moveBuffer.usePressureAdvance = false;
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- if (axis != Z_AXIS && moveBuffer.coords[axis] != oldCoords[axis])
- {
- moveBuffer.usePressureAdvance = true;
- break;
- }
- }
- moveBuffer.filePos = (&gb == fileGCode) ? filePos : noFilePosition;
- //debugPrintf("Queue move pos %u\n", moveFilePos);
- }
- return (moveBuffer.moveType != 0 || moveBuffer.endStopsToCheck != 0) ? 2 : 1;
-}
-
-// The Move class calls this function to find what to do next.
-
-bool GCodes::ReadMove(RawMove& m)
-{
- if (!moveAvailable)
- {
- return false;
- }
-
- m = moveBuffer;
- ClearMove();
- return true;
-}
-
-void GCodes::ClearMove()
-{
- moveAvailable = false;
- moveBuffer.endStopsToCheck = 0;
- moveBuffer.moveType = 0;
- moveBuffer.isFirmwareRetraction = false;
-}
-
-// Run a file macro. Prior to calling this, 'state' must be set to the state we want to enter when the macro has been completed.
-// Return true if the file was found or it wasn't and we were asked to report that fact.
-bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing)
-{
- FileStore * const f = platform->GetFileStore(platform->GetSysDir(), fileName, false);
- if (f == nullptr)
- {
- if (reportMissing)
- {
- // Don't use snprintf into scratchString here, because fileName may be aliased to scratchString
- platform->MessageF(GENERIC_MESSAGE, "Macro file %s not found.\n", fileName);
- return true;
- }
- return false;
- }
-
- if (!Push(gb))
- {
- return true;
- }
- gb.MachineState().fileState.Set(f);
- gb.MachineState().doingFileMacro = true;
- gb.SetState(GCodeState::normal);
- gb.Init();
- return true;
-}
-
-void GCodes::FileMacroCyclesReturn(GCodeBuffer& gb)
-{
- if (gb.MachineState().doingFileMacro)
- {
- gb.PopState();
- gb.Init();
- }
-}
-
-// To execute any move, call this until it returns true.
-// There is only one copy of the canned cycle variable so you must acquire the move lock before calling this.
-bool GCodes::DoCannedCycleMove(GCodeBuffer& gb, EndstopChecks ce)
-{
- if (LockMovementAndWaitForStandstill(gb))
- {
- if (cannedCycleMoveQueued) // if the move has already been queued, it must have finished
- {
- Pop(gb);
- cannedCycleMoveQueued = false;
- return true;
- }
-
- // Otherwise, the move has not been queued yet
- if (!Push(gb))
- {
- return true; // stack overflow
- }
-
- for (size_t drive = 0; drive < DRIVES; drive++)
- {
- switch(cannedMoveType[drive])
- {
- case CannedMoveType::none:
- break;
- case CannedMoveType::relative:
- moveBuffer.coords[drive] += cannedMoveCoords[drive];
- break;
- case CannedMoveType::absolute:
- moveBuffer.coords[drive] = cannedMoveCoords[drive];
- break;
- }
- }
- moveBuffer.feedRate = cannedFeedRate;
- moveBuffer.xAxes = 0;
- moveBuffer.endStopsToCheck = ce;
- moveBuffer.filePos = noFilePosition;
- moveBuffer.usePressureAdvance = false;
- moveAvailable = true;
- cannedCycleMoveQueued = true;
- }
- return false;
-}
-
-// This handles G92
-bool GCodes::SetPositions(GCodeBuffer& gb)
-{
- // Don't pause the machine if only extruder drives are being reset (DC, 2015-09-06).
- // This avoids blobs and seams when the gcode uses absolute E coordinates and periodically includes G92 E0.
- bool includingAxes = false;
- for (size_t drive = 0; drive < numAxes; ++drive)
- {
- if (gb.Seen(axisLetters[drive]))
- {
- includingAxes = true;
- break;
- }
- }
-
- if (includingAxes)
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- }
- else if (moveAvailable) // wait for previous move to be taken so that GetCurrentUserPosition returns the correct value
- {
- return false;
- }
-
- reprap.GetMove()->GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes()); // make sure move buffer is up to date
- bool ok = LoadMoveBufferFromGCode(gb, -1);
- if (ok && includingAxes)
- {
-#if SUPPORT_ROLAND
- if (reprap.GetRoland()->Active())
- {
- for(size_t axis = 0; axis < AXES; axis++)
- {
- if (!reprap.GetRoland()->ProcessG92(moveBuffer[axis], axis))
- {
- return false;
- }
- }
- }
-#endif
- SetPositions(moveBuffer.coords);
- }
-
- return true;
-}
-
-// Offset the axes by the X, Y, and Z amounts in the M code in gb. Say the machine is at [10, 20, 30] and
-// the offsets specified are [8, 2, -5]. The machine will move to [18, 22, 25] and henceforth consider that point
-// to be [10, 20, 30].
-bool GCodes::OffsetAxes(GCodeBuffer& gb)
-{
- if (!offSetSet)
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- for (size_t drive = 0; drive < DRIVES; drive++)
- {
- if (drive < numAxes)
- {
- record[drive] = moveBuffer.coords[drive];
- if (gb.Seen(axisLetters[drive]))
- {
- cannedMoveCoords[drive] = gb.GetFValue();
- cannedMoveType[drive] = CannedMoveType::relative;
- }
- }
- else
- {
- record[drive] = 0.0;
- }
- cannedMoveType[drive] = CannedMoveType::none;
- }
-
- if (gb.Seen(feedrateLetter)) // Has the user specified a feedrate?
- {
- cannedFeedRate = gb.GetFValue() * distanceScale * SECONDS_TO_MINUTES;
- }
- else
- {
- cannedFeedRate = DEFAULT_FEEDRATE;
- }
-
- offSetSet = true;
- }
-
- if (DoCannedCycleMove(gb, 0))
- {
- // Restore positions
- for (size_t drive = 0; drive < DRIVES; drive++)
- {
- moveBuffer.coords[drive] = record[drive];
- }
- reprap.GetMove()->SetLiveCoordinates(record); // This doesn't transform record
- reprap.GetMove()->SetPositions(record); // This does
- offSetSet = false;
- return true;
- }
-
- return false;
-}
-
-// Home one or more of the axes. Which ones are decided by the
-// booleans homeX, homeY and homeZ.
-// Returns true if completed, false if needs to be called again.
-// 'reply' is only written if there is an error.
-// 'error' is false on entry, gets changed to true if there is an error.
-bool GCodes::DoHome(GCodeBuffer& gb, StringRef& reply, bool& error)
-{
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
-
-#if SUPPORT_ROLAND
- // Deal with a Roland configuration
- if (reprap.GetRoland()->Active())
- {
- bool rolHome = reprap.GetRoland()->ProcessHome();
- if (rolHome)
- {
- for(size_t axis = 0; axis < AXES; axis++)
- {
- axisIsHomed[axis] = true;
- }
- }
- return rolHome;
- }
-#endif
-
- if (reprap.GetMove()->IsDeltaMode())
- {
- SetAllAxesNotHomed();
- DoFileMacro(gb, HOME_DELTA_G);
- }
- else
- {
- toBeHomed = 0;
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- toBeHomed |= (1u << axis);
- SetAxisNotHomed(axis);
- }
- }
-
- if (toBeHomed == 0 || toBeHomed == ((1u << numAxes) - 1))
- {
- // Homing everything
- SetAllAxesNotHomed();
- DoFileMacro(gb, HOME_ALL_G);
- }
- else if ( platform->MustHomeXYBeforeZ()
- && ((toBeHomed & (1u << Z_AXIS)) != 0)
- && ((toBeHomed | axesHomed | (1u << Z_AXIS)) != ((1u << numAxes) - 1))
- )
- {
- // We can only home Z if both X and Y have already been homed or are being homed
- reply.copy("Must home all other axes before homing Z");
- error = true;
- }
- else
- {
- gb.SetState(GCodeState::homing);
- }
- }
- return true;
-}
-
-// This lifts Z a bit, moves to the probe XY coordinates (obtained by a call to GetProbeCoordinates() ),
-// probes the bed height, and records the Z coordinate probed. If you want to program any general
-// internal canned cycle, this shows how to do it.
-// On entry, probePointIndex specifies which of the points this is.
-bool GCodes::DoSingleZProbeAtPoint(GCodeBuffer& gb, int probePointIndex, float heightAdjust)
-{
- reprap.GetMove()->SetIdentityTransform(); // It doesn't matter if these are called repeatedly
-
- for (size_t drive = 0; drive <= DRIVES; drive++)
- {
- cannedMoveType[drive] = CannedMoveType::none;
- }
-
- switch (cannedCycleMoveCount)
- {
- case 0: // Move Z to the dive height. This only does anything on the first move; on all the others Z is already there
- cannedMoveCoords[Z_AXIS] = platform->GetZProbeDiveHeight() + max<float>(platform->ZProbeStopHeight(), 0.0);
- cannedMoveType[Z_AXIS] = CannedMoveType::absolute;
- cannedFeedRate = platform->GetZProbeTravelSpeed();
- if (DoCannedCycleMove(gb, 0))
- {
- cannedCycleMoveCount++;
- }
- return false;
-
- case 1: // Move to the correct XY coordinates
- GetProbeCoordinates(probePointIndex, cannedMoveCoords[X_AXIS], cannedMoveCoords[Y_AXIS], cannedMoveCoords[Z_AXIS]);
- cannedMoveType[X_AXIS] = CannedMoveType::absolute;
- cannedMoveType[Y_AXIS] = CannedMoveType::absolute;
- // NB - we don't use the Z value
- cannedFeedRate = platform->GetZProbeTravelSpeed();
- if (DoCannedCycleMove(gb, 0))
- {
- cannedCycleMoveCount++;
- }
- return false;
-
- case 2: // Probe the bed
- {
- const float height = (GetAxisIsHomed(Z_AXIS))
- ? 2 * platform->GetZProbeDiveHeight() // Z axis has been homed, so no point in going very far
- : 1.1 * platform->AxisTotalLength(Z_AXIS); // Z axis not homed yet, so treat this as a homing move
- switch(DoZProbe(gb, height))
- {
- case 0:
- // Z probe is already triggered at the start of the move, so abandon the probe and record an error
- platform->Message(GENERIC_MESSAGE, "Error: Z probe already triggered at start of probing move\n");
- cannedCycleMoveCount++;
- reprap.GetMove()->SetZBedProbePoint(probePointIndex, platform->GetZProbeDiveHeight(), true, true);
- break;
-
- case 1:
- // Z probe did not trigger
- platform->Message(GENERIC_MESSAGE, "Error: Z probe was not triggered during probing move\n");
- cannedCycleMoveCount++;
- reprap.GetMove()->SetZBedProbePoint(probePointIndex, -(platform->GetZProbeDiveHeight()), true, true);
- break;
-
- case 2:
- // Successful probing
- if (GetAxisIsHomed(Z_AXIS))
- {
- lastProbedZ = moveBuffer.coords[Z_AXIS] - (platform->ZProbeStopHeight() + heightAdjust);
- }
- else
- {
- // The Z axis has not yet been homed, so treat this probe as a homing move.
- moveBuffer.coords[Z_AXIS] = platform->ZProbeStopHeight() + heightAdjust;
- SetPositions(moveBuffer.coords);
- SetAxisIsHomed(Z_AXIS);
- lastProbedZ = 0.0;
- }
- reprap.GetMove()->SetZBedProbePoint(probePointIndex, lastProbedZ, true, false);
- cannedCycleMoveCount++;
- break;
-
- default:
- break;
- }
- }
- return false;
-
- case 3: // Raise the head back up to the dive height
- cannedMoveCoords[Z_AXIS] = platform->GetZProbeDiveHeight() + max<float>(platform->ZProbeStopHeight(), 0.0);
- cannedMoveType[Z_AXIS] = CannedMoveType::absolute;
- cannedFeedRate = platform->GetZProbeTravelSpeed();
- if (DoCannedCycleMove(gb, 0))
- {
- cannedCycleMoveCount = 0;
- return true;
- }
- return false;
-
- default: // should not happen
- cannedCycleMoveCount = 0;
- return true;
- }
-}
-
-// This simply moves down till the Z probe/switch is triggered. Call it repeatedly until it returns true.
-// Called when we do a G30 with no P parameter.
-bool GCodes::DoSingleZProbe(GCodeBuffer& gb, bool reportOnly, float heightAdjust)
-{
- switch (DoZProbe(gb, 1.1 * platform->AxisTotalLength(Z_AXIS)))
- {
- case 0: // failed
- platform->Message(GENERIC_MESSAGE, "Error: Z probe already triggered at start of probing move\n");
- return true;
-
- case 1:
- platform->Message(GENERIC_MESSAGE, "Error: Z probe was not triggered during probing move\n");
- return true;
-
- case 2: // success
- if (!reportOnly)
- {
- moveBuffer.coords[Z_AXIS] = platform->ZProbeStopHeight() + heightAdjust;
- SetPositions(moveBuffer.coords);
- SetAxisIsHomed(Z_AXIS);
- lastProbedZ = 0.0;
- }
- return true;
-
- default: // not finished yet
- return false;
- }
-}
-
-// Do a Z probe cycle up to the maximum specified distance.
-// Returns -1 if not complete yet
-// Returns 0 if Z probe already triggered at start of probing
-// Returns 1 if Z probe didn't trigger
-// Returns 2 if success, with the current position in moveBuffer
-int GCodes::DoZProbe(GCodeBuffer& gb, float distance)
-{
- if (platform->GetZProbeType() == ZProbeTypeDelta)
- {
- const ZProbeParameters& params = platform->GetZProbeParameters();
- return reprap.GetMove()->DoDeltaProbe(params.param1, params.param2, params.probeSpeed, distance);
- }
- else
- {
- // Check for probe already triggered at start
- if (!cannedCycleMoveQueued)
- {
- if (reprap.GetPlatform()->GetZProbeResult() == EndStopHit::lowHit)
- {
- return 0;
- }
- zProbeTriggered = false;
- }
-
- // Do a normal canned cycle Z movement with Z probe enabled
- for (size_t drive = 0; drive <= DRIVES; drive++)
- {
- cannedMoveType[drive] = CannedMoveType::none;
- }
-
- cannedMoveCoords[Z_AXIS] = -distance;
- cannedMoveType[Z_AXIS] = CannedMoveType::relative;
- cannedFeedRate = platform->GetZProbeParameters().probeSpeed;
-
- if (DoCannedCycleMove(gb, ZProbeActive))
- {
- return (zProbeTriggered) ? 2 : 1;
- }
- return -1;
- }
-}
-
-// This is called to execute a G30.
-// It sets wherever we are as the probe point P (probePointIndex)
-// then probes the bed, or gets all its parameters from the arguments.
-// If X or Y are specified, use those; otherwise use the machine's
-// coordinates. If no Z is specified use the machine's coordinates. If it
-// is specified and is greater than SILLY_Z_VALUE (i.e. greater than -9999.0)
-// then that value is used. If it's less than SILLY_Z_VALUE the bed is
-// probed and that value is used.
-// Call this repeatedly until it returns true.
-bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer& gb, StringRef& reply)
-{
- if (reprap.GetMove()->IsDeltaMode() && !AllAxesAreHomed())
- {
- reply.copy("Must home before bed probing");
- return true;
- }
-
- float heightAdjust = 0.0;
- bool dummy;
- gb.TryGetFValue('H', heightAdjust, dummy);
-
- if (!gb.Seen('P'))
- {
- bool reportOnly = false;
- if (gb.Seen('S') && gb.GetIValue() < 0)
- {
- reportOnly = true;
- }
- return DoSingleZProbe(gb, reportOnly, heightAdjust);
- }
-
- int probePointIndex = gb.GetIValue();
- if (probePointIndex < 0 || (unsigned int)probePointIndex >= MaxProbePoints)
- {
- reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Z probe point index out of range.\n");
- return true;
- }
-
- float x = (gb.Seen(axisLetters[X_AXIS])) ? gb.GetFValue() : moveBuffer.coords[X_AXIS];
- float y = (gb.Seen(axisLetters[Y_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Y_AXIS];
- float z = (gb.Seen(axisLetters[Z_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Z_AXIS];
-
- reprap.GetMove()->SetXBedProbePoint(probePointIndex, x);
- reprap.GetMove()->SetYBedProbePoint(probePointIndex, y);
-
- if (z > SILLY_Z_VALUE)
- {
- reprap.GetMove()->SetZBedProbePoint(probePointIndex, z, false, false);
- if (gb.Seen('S'))
- {
- zProbesSet = true;
- reprap.GetMove()->FinishedBedProbing(gb.GetIValue(), reply);
- }
- return true;
- }
- else
- {
- if (DoSingleZProbeAtPoint(gb, probePointIndex, heightAdjust))
- {
- if (gb.Seen('S'))
- {
- zProbesSet = true;
- int sParam = gb.GetIValue();
- if (sParam == 1)
- {
- // G30 with a silly Z value and S=1 is equivalent to G30 with no parameters in that it sets the current Z height
- // This is useful because it adjusts the XY position to account for the probe offset.
- moveBuffer.coords[Z_AXIS] += lastProbedZ;
- SetPositions(moveBuffer.coords);
- lastProbedZ = 0.0;
- }
- else
- {
- reprap.GetMove()->FinishedBedProbing(sParam, reply);
- }
- }
- return true;
- }
- }
-
- return false;
-}
-
-// This returns the (X, Y) points to probe the bed at probe point count. When probing, it returns false.
-// If called after probing has ended it returns true, and the Z coordinate probed is also returned.
-bool GCodes::GetProbeCoordinates(int count, float& x, float& y, float& z) const
-{
- const ZProbeParameters& rp = platform->GetZProbeParameters();
- x = reprap.GetMove()->XBedProbePoint(count) - rp.xOffset;
- y = reprap.GetMove()->YBedProbePoint(count) - rp.yOffset;
- z = reprap.GetMove()->ZBedProbePoint(count);
- return zProbesSet;
-}
-
-bool GCodes::SetPrintZProbe(GCodeBuffer& gb, StringRef& reply)
-{
- ZProbeParameters params = platform->GetZProbeParameters();
- bool seen = false;
- gb.TryGetFValue(axisLetters[X_AXIS], params.xOffset, seen);
- gb.TryGetFValue(axisLetters[Y_AXIS], params.yOffset, seen);
- gb.TryGetFValue(axisLetters[Z_AXIS], params.height, seen);
- gb.TryGetIValue('P', params.adcValue, seen);
-
- if (gb.Seen('C'))
- {
- params.temperatureCoefficient = gb.GetFValue();
- seen = true;
- if (gb.Seen('S'))
- {
- params.calibTemperature = gb.GetFValue();
- }
- else
- {
- // Use the current bed temperature as the calibration temperature if no value was provided
- params.calibTemperature = platform->GetZProbeTemperature();
- }
- }
-
- if (seen)
- {
- platform->SetZProbeParameters(params);
- }
- else
- {
- const int v0 = platform->ZProbe();
- int v1, v2;
- switch (platform->GetZProbeSecondaryValues(v1, v2))
- {
- case 1:
- reply.printf("%d (%d)", v0, v1);
- break;
- case 2:
- reply.printf("%d (%d, %d)", v0, v1, v2);
- break;
- default:
- reply.printf("%d", v0);
- break;
- }
- }
- return true;
-}
-
-// Define the probing grid, returning true if error
-// Called when we see an M557 command with no P parameter
-bool GCodes::DefineGrid(GCodeBuffer& gb, StringRef &reply)
-{
- bool seenX = false, seenY = false, seenR = false, seenS = false;
- float xValues[2];
- float yValues[2];
-
- if (gb.Seen('X'))
- {
- size_t count = 2;
- gb.GetFloatArray(xValues, count, false);
- if (count == 2)
- {
- seenX = true;
- }
- else
- {
- reply.copy("ERROR: Wrong number of X values in M577, need 2");
- return true;
- }
- }
- if (gb.Seen('Y'))
- {
- size_t count = 2;
- gb.GetFloatArray(yValues, count, false);
- if (count == 2)
- {
- seenY = true;
- }
- else
- {
- reply.copy("ERROR: Wrong number of Y values in M577, need 2");
- return true;
- }
- }
-
- float radius = -1.0;
- gb.TryGetFValue('R', radius, seenR);
- float spacing = DefaultGridSpacing;
- gb.TryGetFValue('S', spacing, seenS);
-
- if (!seenX && !seenY && !seenR && !seenS)
- {
- // Just print the existing grid parameters
- const GridDefinition& grid = reprap.GetMove()->GetBedProbeGrid();
- if (grid.IsValid())
- {
- reply.copy("Grid: ");
- grid.PrintParameters(reply);
- }
- else
- {
- reply.copy("Grid is not defined");
- }
- return false;
- }
-
- if (seenX != seenY)
- {
- reply.copy("ERROR: specify both or neither of X and Y in M577");
- return true;
- }
-
- if (!seenX && !seenR)
- {
- // Must have given just the S parameter
- reply.copy("ERROR: specify at least radius or X,Y ranges in M577");
- return true;
-
- }
-
- if (!seenX)
- {
- if (radius > 0)
- {
- const float effectiveRadius = floor((radius - 0.1)/spacing) * spacing;
- xValues[0] = yValues[0] = -effectiveRadius;
- xValues[1] = yValues[1] = effectiveRadius + 0.1;
- }
- else
- {
- reply.copy("ERROR: M577 radius must be positive unless X and Y are specified");
- return true;
- }
- }
- GridDefinition newGrid(xValues, yValues, radius, spacing); // create a new grid
- if (newGrid.IsValid())
- {
- reprap.GetMove()->SetBedProbeGrid(newGrid);
- return false;
- }
- else
- {
- reply.copy("ERROR: bad grid definition: ");
- newGrid.PrintError(reply);
- return true;
- }
-}
-
-// Start probing the grid, returning true if we didn't because of an error.
-// Prior to calling this the movement system must be locked.
-bool GCodes::ProbeGrid(GCodeBuffer& gb, StringRef& reply)
-{
- long sParam = 0;
- bool dummy;
- gb.TryGetIValue('S', sParam, dummy);
-
- if (gb.Seen('P'))
- {
- heightMapFile = gb.GetString();
- }
- else
- {
- heightMapFile = DefaultHeightMapFile;
- }
-
- switch(sParam)
- {
- case 0: // Probe the bed and save to file
- if (!reprap.GetMove()->GetBedProbeGrid().IsValid())
- {
- reply.copy("No valid grid defined for G29 bed probing");
- return true;
- }
-
- if (!AllAxesAreHomed())
- {
- reply.copy("Must home printer before G29 bed probing");
- return true;
- }
-
- gridXindex = gridYindex = 0;
- numPointsProbed = 0;
- heightSum = heightSquaredSum = 0.0;
-
- reprap.GetMove()->ClearGridHeights();
- reprap.GetMove()->SetIdentityTransform();
- gb.SetState(GCodeState::gridProbing1);
- return false;
-
- case 1: // Load height map from file
- return reprap.GetMove()->LoadHeightMapFromFile(heightMapFile, reply);
-
- case 2: // Clear height map
- reprap.GetMove()->ClearGridHeights();
- return false;
-
- default:
- reply.copy("Invalid S parameter in G29 command");
- return true;
- }
-
-
-}
-
-// Return the current coordinates as a printable string.
-// Coordinates are updated at the end of each movement, so this won't tell you where you are mid-movement.
-void GCodes::GetCurrentCoordinates(StringRef& s) const
-{
- float liveCoordinates[DRIVES];
- reprap.GetMove()->LiveCoordinates(liveCoordinates, reprap.GetCurrentXAxes());
- const Tool *currentTool = reprap.GetCurrentTool();
- if (currentTool != nullptr)
- {
- const float *offset = currentTool->GetOffset();
- for (size_t i = 0; i < numAxes; ++i)
- {
- liveCoordinates[i] += offset[i];
- }
- }
-
- s.Clear();
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- s.catf("%c: %.2f ", axisLetters[axis], liveCoordinates[axis]);
- }
- for (size_t i = numAxes; i < DRIVES; i++)
- {
- s.catf("E%u: %.1f ", i - numAxes, liveCoordinates[i]);
- }
-
- // Print the axis stepper motor positions as Marlin does, as an aid to debugging.
- // Don't bother with the extruder endpoints, they are zero after any non-extruding move.
- s.cat(" Count");
- for (size_t i = 0; i < numAxes; ++i)
- {
- s.catf(" %d", reprap.GetMove()->GetEndPoint(i));
- }
-}
-
-bool GCodes::OpenFileToWrite(GCodeBuffer& gb, const char* directory, const char* fileName)
-{
- fileBeingWritten = platform->GetFileStore(directory, fileName, true);
- eofStringCounter = 0;
- if (fileBeingWritten == NULL)
- {
- platform->MessageF(GENERIC_MESSAGE, "Can't open GCode file \"%s\" for writing.\n", fileName);
- return false;
- }
- else
- {
- gb.SetWritingFileDirectory(directory);
- return true;
- }
-}
-
-void GCodes::WriteHTMLToFile(GCodeBuffer& gb, char b)
-{
- if (fileBeingWritten == NULL)
- {
- platform->Message(GENERIC_MESSAGE, "Attempt to write to a null file.\n");
- return;
- }
-
- if (eofStringCounter != 0 && b != eofString[eofStringCounter])
- {
- fileBeingWritten->Write(eofString);
- eofStringCounter = 0;
- }
-
- if (b == eofString[eofStringCounter])
- {
- eofStringCounter++;
- if (eofStringCounter >= eofStringLength)
- {
- fileBeingWritten->Close();
- fileBeingWritten = NULL;
- gb.SetWritingFileDirectory(NULL);
- const char* r = (platform->Emulating() == marlin) ? "Done saving file." : "";
- HandleReply(gb, false, r);
- return;
- }
- }
- else
- {
- fileBeingWritten->Write(b);
- }
-}
-
-void GCodes::WriteGCodeToFile(GCodeBuffer& gb)
-{
- if (fileBeingWritten == NULL)
- {
- platform->Message(GENERIC_MESSAGE, "Attempt to write to a null file.\n");
- return;
- }
-
- // End of file?
- if (gb.Seen('M'))
- {
- if (gb.GetIValue() == 29)
- {
- fileBeingWritten->Close();
- fileBeingWritten = NULL;
- gb.SetWritingFileDirectory(NULL);
- const char* r = (platform->Emulating() == marlin) ? "Done saving file." : "";
- HandleReply(gb, false, r);
- return;
- }
- }
-
- // Resend request?
- if (gb.Seen('G'))
- {
- if (gb.GetIValue() == 998)
- {
- if (gb.Seen('P'))
- {
- scratchString.printf("%d\n", gb.GetIValue());
- HandleReply(gb, false, scratchString.Pointer());
- return;
- }
- }
- }
-
- fileBeingWritten->Write(gb.Buffer());
- fileBeingWritten->Write('\n');
- HandleReply(gb, false, "");
-}
-
-// Set up a file to print, but don't print it yet.
-void GCodes::QueueFileToPrint(const char* fileName)
-{
- FileStore * const f = platform->GetFileStore(platform->GetGCodeDir(), fileName, false);
- if (f != nullptr)
- {
- // Cancel current print if there is any
- if (!reprap.GetPrintMonitor()->IsPrinting())
- {
- CancelPrint();
- }
-
- fileGCode->SetToolNumberAdjust(0); // clear tool number adjustment
-
- // Reset all extruder positions when starting a new print
- for (size_t extruder = 0; extruder < MaxExtruders; extruder++)
- {
- lastRawExtruderPosition[extruder] = 0.0;
- rawExtruderTotalByDrive[extruder] = 0.0;
- }
- rawExtruderTotal = 0.0;
- reprap.GetMove()->ResetExtruderPositions();
-
- fileToPrint.Set(f);
- }
- else
- {
- platform->MessageF(GENERIC_MESSAGE, "GCode file \"%s\" not found\n", fileName);
- }
-}
-
-void GCodes::DeleteFile(const char* fileName)
-{
- if (!platform->GetMassStorage()->Delete(platform->GetGCodeDir(), fileName))
- {
- platform->MessageF(GENERIC_MESSAGE, "Could not delete file \"%s\"\n", fileName);
- }
-}
-
-// Function to handle dwell delays. Return true for dwell finished, false otherwise.
-bool GCodes::DoDwell(GCodeBuffer& gb)
-{
- float dwell;
- if (gb.Seen('S'))
- {
- dwell = gb.GetFValue();
- }
- else if (gb.Seen('P'))
- {
- dwell = 0.001 * (float) gb.GetIValue(); // P values are in milliseconds; we need seconds
- }
- else
- {
- return true; // No time given - throw it away
- }
-
-#if SUPPORT_ROLAND
- // Deal with a Roland configuration
- if (reprap.GetRoland()->Active())
- {
- return reprap.GetRoland()->ProcessDwell(gb.GetLValue());
- }
-#endif
-
- // Wait for all the queued moves to stop
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
-
- if (simulationMode != 0)
- {
- simulationTime += dwell;
- return true;
- }
- else
- {
- return DoDwellTime(dwell);
- }
-}
-
-bool GCodes::DoDwellTime(float dwell)
-{
- // Are we already in the dwell?
- if (dwellWaiting)
- {
- if (platform->Time() - dwellTime >= 0.0)
- {
- dwellWaiting = false;
- reprap.GetMove()->ResumeMoving();
- return true;
- }
- return false;
- }
-
- // New dwell - set it up
- dwellWaiting = true;
- dwellTime = platform->Time() + dwell;
- return false;
-}
-
-// Set offset, working and standby temperatures for a tool. I.e. handle a G10.
-bool GCodes::SetOrReportOffsets(GCodeBuffer &gb, StringRef& reply)
-{
- if (gb.Seen('P'))
- {
- int8_t toolNumber = gb.GetIValue();
- toolNumber += gb.GetToolNumberAdjust();
- Tool* tool = reprap.GetTool(toolNumber);
- if (tool == NULL)
- {
- reply.printf("Attempt to set/report offsets and temperatures for non-existent tool: %d", toolNumber);
- return true;
- }
-
- // Deal with setting offsets
- float offset[MAX_AXES];
- for (size_t i = 0; i < MAX_AXES; ++i)
- {
- offset[i] = tool->GetOffset()[i];
- }
-
- bool settingOffset = false;
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- gb.TryGetFValue(axisLetters[axis], offset[axis], settingOffset);
- }
- if (settingOffset)
- {
- if (!LockMovement(gb))
- {
- return false;
- }
- tool->SetOffset(offset);
- }
-
- // Deal with setting temperatures
- bool settingTemps = false;
- size_t hCount = tool->HeaterCount();
- float standby[HEATERS];
- float active[HEATERS];
- if (hCount > 0)
- {
- tool->GetVariables(standby, active);
- if (gb.Seen('R'))
- {
- gb.GetFloatArray(standby, hCount, true);
- settingTemps = true;
- }
- if (gb.Seen('S'))
- {
- gb.GetFloatArray(active, hCount, true);
- settingTemps = true;
- }
-
- if (settingTemps && simulationMode == 0)
- {
- tool->SetVariables(standby, active);
- }
- }
-
- if (!settingOffset && !settingTemps)
- {
- // Print offsets and temperatures
- reply.printf("Tool %d offsets:", toolNumber);
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- reply.catf(" %c%.2f", axisLetters[axis], offset[axis]);
- }
- if (hCount != 0)
- {
- reply.cat(", active/standby temperature(s):");
- for (size_t heater = 0; heater < hCount; heater++)
- {
- reply.catf(" %.1f/%.1f", active[heater], standby[heater]);
- }
- }
- }
- }
- return true;
-}
-
-void GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply)
-{
- if (!gb.Seen('P'))
- {
- // DC temporary code to allow tool numbers to be adjusted so that we don't need to edit multi-media files generated by slic3r
- if (gb.Seen('S'))
- {
- int adjust = gb.GetIValue();
- gb.SetToolNumberAdjust(adjust);
- }
- return;
- }
-
- // Check tool number
- bool seen = false;
- const int toolNumber = gb.GetIValue();
- if (toolNumber < 0)
- {
- platform->Message(GENERIC_MESSAGE, "Tool number must be positive!\n");
- return;
- }
-
- // Check drives
- long drives[MaxExtruders]; // There can never be more than we have...
- size_t dCount = numExtruders; // Sets the limit and returns the count
- if (gb.Seen('D'))
- {
- gb.GetLongArray(drives, dCount);
- seen = true;
- }
- else
- {
- dCount = 0;
- }
-
- // Check heaters
- long heaters[HEATERS];
- size_t hCount = HEATERS;
- if (gb.Seen('H'))
- {
- gb.GetLongArray(heaters, hCount);
- seen = true;
- }
- else
- {
- hCount = 0;
- }
-
- // Check X axis mapping
- uint32_t xMap;
- if (gb.Seen('X'))
- {
- long xMapping[MAX_AXES];
- size_t xCount = numAxes;
- gb.GetLongArray(xMapping, xCount);
- xMap = LongArrayToBitMap(xMapping, xCount) & ((1u << numAxes) - 1);
- seen = true;
- }
- else
- {
- xMap = 1; // by default map X axis straight through
- }
-
- // Check for fan mapping
- uint32_t fanMap;
- if (gb.Seen('F'))
- {
- long fanMapping[NUM_FANS];
- size_t fanCount = NUM_FANS;
- gb.GetLongArray(fanMapping, fanCount);
- fanMap = LongArrayToBitMap(fanMapping, fanCount) & ((1u << NUM_FANS) - 1);
- seen = true;
- }
- else
- {
- fanMap = 1; // by default map fan 0 to fan 0
- }
-
- if (seen)
- {
- // Add or delete tool, so start by deleting the old one with this number, if any
- reprap.DeleteTool(reprap.GetTool(toolNumber));
-
- // M563 P# D-1 H-1 removes an existing tool
- if (dCount == 1 && hCount == 1 && drives[0] == -1 && heaters[0] == -1)
- {
- // nothing more to do
- }
- else
- {
- Tool* tool = Tool::Create(toolNumber, drives, dCount, heaters, hCount, xMap, fanMap);
- if (tool != nullptr)
- {
- reprap.AddTool(tool);
- }
- }
- }
- else
- {
- reprap.PrintTool(toolNumber, reply);
- }
-}
-
-// Does what it says.
-void GCodes::DisableDrives()
-{
- for (size_t drive = 0; drive < DRIVES; drive++)
- {
- platform->DisableDrive(drive);
- }
- SetAllAxesNotHomed();
-}
-
-// Does what it says.
-void GCodes::SetEthernetAddress(GCodeBuffer& gb, int mCode)
-{
- byte eth[4];
- const char* ipString = gb.GetString();
- uint8_t sp = 0;
- uint8_t spp = 0;
- uint8_t ipp = 0;
- while (ipString[sp])
- {
- if (ipString[sp] == '.')
- {
- eth[ipp] = atoi(&ipString[spp]);
- ipp++;
- if (ipp > 3)
- {
- platform->MessageF(GENERIC_MESSAGE, "Dud IP address: %s\n", gb.Buffer());
- return;
- }
- sp++;
- spp = sp;
- }
- else
- {
- sp++;
- }
- }
- eth[ipp] = atoi(&ipString[spp]);
- if (ipp == 3)
- {
- switch (mCode)
- {
- case 552:
- platform->SetIPAddress(eth);
- break;
- case 553:
- platform->SetNetMask(eth);
- break;
- case 554:
- platform->SetGateWay(eth);
- break;
-
- default:
- platform->Message(GENERIC_MESSAGE, "Setting ether parameter - dud code.\n");
- }
- }
- else
- {
- platform->MessageF(GENERIC_MESSAGE, "Dud IP address: %s\n", gb.Buffer());
- }
-}
-
-void GCodes::SetMACAddress(GCodeBuffer& gb)
-{
- uint8_t mac[6];
- const char* ipString = gb.GetString();
- uint8_t sp = 0;
- uint8_t spp = 0;
- uint8_t ipp = 0;
- while (ipString[sp])
- {
- if (ipString[sp] == ':')
- {
- mac[ipp] = strtoul(&ipString[spp], NULL, 16);
- ipp++;
- if (ipp > 5)
- {
- platform->MessageF(GENERIC_MESSAGE, "Dud MAC address: %s\n", gb.Buffer());
- return;
- }
- sp++;
- spp = sp;
- }
- else
- {
- sp++;
- }
- }
- mac[ipp] = strtoul(&ipString[spp], NULL, 16);
- if (ipp == 5)
- {
- platform->SetMACAddress(mac);
- }
- else
- {
- platform->MessageF(GENERIC_MESSAGE, "Dud MAC address: %s\n", gb.Buffer());
- }
-}
-
-bool GCodes::ChangeMicrostepping(size_t drive, int microsteps, int mode) const
-{
- bool dummy;
- unsigned int oldSteps = platform->GetMicrostepping(drive, dummy);
- bool success = platform->SetMicrostepping(drive, microsteps, mode);
- if (success && mode <= 1) // modes higher than 1 are used for special functions
- {
- // We changed the microstepping, so adjust the steps/mm to compensate
- float stepsPerMm = platform->DriveStepsPerUnit(drive);
- if (stepsPerMm > 0)
- {
- platform->SetDriveStepsPerUnit(drive, stepsPerMm * (float)microsteps / (float)oldSteps);
- }
- }
- return success;
-}
-
-// Set the speeds of fans mapped for the current tool to lastDefaultFanSpeed
-void GCodes::SetMappedFanSpeed()
-{
- if (reprap.GetCurrentTool() == nullptr)
- {
- platform->SetFanValue(0, lastDefaultFanSpeed);
- }
- else
- {
- const uint32_t fanMap = reprap.GetCurrentTool()->GetFanMapping();
- for (size_t i = 0; i < NUM_FANS; ++i)
- {
- if ((fanMap & (1u << i)) != 0)
- {
- platform->SetFanValue(i, lastDefaultFanSpeed);
- }
- }
- }
-}
-
-// Handle sending a reply back to the appropriate interface(s).
-// Note that 'reply' may be empty. If it isn't, then we need to append newline when sending it.
-// Also, gb may be null if we were executing a trigger macro.
-void GCodes::HandleReply(GCodeBuffer& gb, bool error, const char* reply)
-{
- // Don't report "ok" responses if a (macro) file is being processed
- // Also check that this response was triggered by a gcode
- if ((gb.MachineState().doingFileMacro || &gb == fileGCode) && reply[0] == 0)
- {
- return;
- }
-
- // Second UART device, e.g. dc42's PanelDue. Do NOT use emulation for this one!
- if (&gb == auxGCode)
- {
- platform->AppendAuxReply(reply);
- return;
- }
-
- const Compatibility c = (&gb == serialGCode || &gb == telnetGCode) ? platform->Emulating() : me;
- MessageType type = GENERIC_MESSAGE;
- if (&gb == httpGCode)
- {
- type = HTTP_MESSAGE;
- }
- else if (&gb == telnetGCode)
- {
- type = TELNET_MESSAGE;
- }
- else if (&gb == serialGCode)
- {
- type = HOST_MESSAGE;
- }
-
- const char* response = (gb.Seen('M') && gb.GetIValue() == 998) ? "rs " : "ok";
- const char* emulationType = 0;
-
- switch (c)
- {
- case me:
- case reprapFirmware:
- if (error)
- {
- platform->Message(type, "Error: ");
- }
- platform->Message(type, reply);
- platform->Message(type, "\n");
- return;
-
- case marlin:
- // We don't need to handle M20 here because we always allocate an output buffer for that one
- if (gb.Seen('M') && gb.GetIValue() == 28)
- {
- platform->Message(type, response);
- platform->Message(type, "\n");
- platform->Message(type, reply);
- platform->Message(type, "\n");
- return;
- }
-
- if ((gb.Seen('M') && gb.GetIValue() == 105) || (gb.Seen('M') && gb.GetIValue() == 998))
- {
- platform->Message(type, response);
- platform->Message(type, " ");
- platform->Message(type, reply);
- platform->Message(type, "\n");
- return;
- }
-
- if (reply[0] != 0 && !gb.IsDoingFileMacro())
- {
- platform->Message(type, reply);
- platform->Message(type, "\n");
- platform->Message(type, response);
- platform->Message(type, "\n");
- }
- else if (reply[0] != 0)
- {
- platform->Message(type, reply);
- platform->Message(type, "\n");
- }
- else
- {
- platform->Message(type, response);
- platform->Message(type, "\n");
- }
- return;
-
- case teacup:
- emulationType = "teacup";
- break;
- case sprinter:
- emulationType = "sprinter";
- break;
- case repetier:
- emulationType = "repetier";
- break;
- default:
- emulationType = "unknown";
- }
-
- if (emulationType != 0)
- {
- platform->MessageF(type, "Emulation of %s is not yet supported.\n", emulationType); // don't send this one to the web as well, it concerns only the USB interface
- }
-}
-
-void GCodes::HandleReply(GCodeBuffer& gb, bool error, OutputBuffer *reply)
-{
- // Although unlikely, it's possible that we get a nullptr reply. Don't proceed if this is the case
- if (reply == nullptr)
- {
- return;
- }
-
- // Second UART device, e.g. dc42's PanelDue. Do NOT use emulation for this one!
- if (&gb == auxGCode)
- {
- platform->AppendAuxReply(reply);
- return;
- }
-
- const Compatibility c = (&gb == serialGCode || &gb == telnetGCode) ? platform->Emulating() : me;
- MessageType type = GENERIC_MESSAGE;
- if (&gb == httpGCode)
- {
- type = HTTP_MESSAGE;
- }
- else if (&gb == telnetGCode)
- {
- type = TELNET_MESSAGE;
- }
- else if (&gb == serialGCode)
- {
- type = HOST_MESSAGE;
- }
-
- const char* response = (gb.Seen('M') && gb.GetIValue() == 998) ? "rs " : "ok";
- const char* emulationType = nullptr;
-
- switch (c)
- {
- case me:
- case reprapFirmware:
- if (error)
- {
- platform->Message(type, "Error: ");
- }
- platform->Message(type, reply);
- return;
-
- case marlin:
- if (gb.Seen('M') && gb.GetIValue() == 20)
- {
- platform->Message(type, "Begin file list\n");
- platform->Message(type, reply);
- platform->Message(type, "End file list\n");
- platform->Message(type, response);
- platform->Message(type, "\n");
- return;
- }
-
- if (gb.Seen('M') && gb.GetIValue() == 28)
- {
- platform->Message(type, response);
- platform->Message(type, "\n");
- platform->Message(type, reply);
- return;
- }
-
- if ((gb.Seen('M') && gb.GetIValue() == 105) || (gb.Seen('M') && gb.GetIValue() == 998))
- {
- platform->Message(type, response);
- platform->Message(type, " ");
- platform->Message(type, reply);
- return;
- }
-
- if (reply->Length() != 0 && !gb.IsDoingFileMacro())
- {
- platform->Message(type, reply);
- platform->Message(type, "\n");
- platform->Message(type, response);
- platform->Message(type, "\n");
- }
- else if (reply->Length() != 0)
- {
- platform->Message(type, reply);
- }
- else
- {
- OutputBuffer::ReleaseAll(reply);
- platform->Message(type, response);
- platform->Message(type, "\n");
- }
- return;
-
- case teacup:
- emulationType = "teacup";
- break;
- case sprinter:
- emulationType = "sprinter";
- break;
- case repetier:
- emulationType = "repetier";
- break;
- default:
- emulationType = "unknown";
- }
-
- // If we get here then we didn't handle the message, so release the buffer(s)
- OutputBuffer::ReleaseAll(reply);
- if (emulationType != 0)
- {
- platform->MessageF(type, "Emulation of %s is not yet supported.\n", emulationType); // don't send this one to the web as well, it concerns only the USB interface
- }
-}
-
-// Set PID parameters (M301 or M304 command). 'heater' is the default heater number to use.
-void GCodes::SetPidParameters(GCodeBuffer& gb, int heater, StringRef& reply)
-{
- if (gb.Seen('H'))
- {
- heater = gb.GetIValue();
- }
-
- if (heater >= 0 && heater < HEATERS)
- {
- PidParameters pp = platform->GetPidParameters(heater);
- bool seen = false;
- gb.TryGetFValue('P', pp.kP, seen);
- gb.TryGetFValue('I', pp.kI, seen);
- gb.TryGetFValue('D', pp.kD, seen);
- gb.TryGetFValue('T', pp.kT, seen);
- gb.TryGetFValue('S', pp.kS, seen);
-
- if (seen)
- {
- platform->SetPidParameters(heater, pp);
- reprap.GetHeat()->UseModel(heater, false);
- }
- else
- {
- reply.printf("Heater %d P:%.2f I:%.3f D:%.2f T:%.2f S:%.2f", heater, pp.kP, pp.kI, pp.kD, pp.kT, pp.kS);
- }
- }
-}
-
-void GCodes::SetHeaterParameters(GCodeBuffer& gb, StringRef& reply)
-{
- if (gb.Seen('P'))
- {
- int heater = gb.GetIValue();
- if (heater >= 0 && heater < HEATERS)
- {
- Thermistor& th = platform->GetThermistor(heater);
- bool seen = false;
-
- // We must set the 25C resistance and beta together in order to calculate Rinf. Check for these first.
- float r25 = th.GetR25();
- float beta = th.GetBeta();
- float shC = th.GetShc();
- float seriesR = th.GetSeriesR();
-
- gb.TryGetFValue('T', r25, seen);
- gb.TryGetFValue('B', beta, seen);
- gb.TryGetFValue('C', shC, seen);
- gb.TryGetFValue('R', seriesR, seen);
- if (seen)
- {
- th.SetParameters(r25, beta, shC, seriesR);
- }
-
- if (gb.Seen('L'))
- {
- th.SetLowOffset((int8_t)constrain<int>(gb.GetIValue(), -100, 100));
- seen = true;
- }
- if (gb.Seen('H'))
- {
- th.SetHighOffset((int8_t)constrain<int>(gb.GetIValue(), -100, 100));
- seen = true;
- }
-
- if (gb.Seen('X'))
- {
- int thermistor = gb.GetIValue();
- if ( (0 <= thermistor && thermistor < HEATERS)
- || ((int)FirstThermocoupleChannel <= thermistor && thermistor < (int)(FirstThermocoupleChannel + MaxSpiTempSensors))
- || ((int)FirstRtdChannel <= thermistor && thermistor < (int)(FirstRtdChannel + MaxSpiTempSensors))
- )
- {
- platform->SetThermistorNumber(heater, thermistor);
- }
- else
- {
- platform->MessageF(GENERIC_MESSAGE, "Thermistor number %d is out of range\n", thermistor);
- }
- seen = true;
- }
-
- if (!seen)
- {
- reply.printf("T:%.1f B:%.1f C:%.2e R:%.1f L:%d H:%d X:%d",
- th.GetR25(), th.GetBeta(), th.GetShc(), th.GetSeriesR(),
- th.GetLowOffset(), th.GetHighOffset(), platform->GetThermistorNumber(heater));
- }
- }
- else
- {
- platform->MessageF(GENERIC_MESSAGE, "Heater number %d is out of range\n", heater);
- }
- }
-}
-
-void GCodes::SetToolHeaters(Tool *tool, float temperature)
-{
- if (tool == NULL)
- {
- platform->Message(GENERIC_MESSAGE, "Setting temperature: no tool selected.\n");
- return;
- }
-
- float standby[HEATERS];
- float active[HEATERS];
- tool->GetVariables(standby, active);
- for (size_t h = 0; h < tool->HeaterCount(); h++)
- {
- active[h] = temperature;
- }
- tool->SetVariables(standby, active);
-}
-
-// Retract or un-retract filament, returning true if movement has been queued, false if this needs to be called again
-bool GCodes::RetractFilament(bool retract)
-{
- if (retractLength != 0.0 || retractHop != 0.0 || (!retract && retractExtra != 0.0))
- {
- const Tool *tool = reprap.GetCurrentTool();
- if (tool != nullptr)
- {
- size_t nDrives = tool->DriveCount();
- if (nDrives != 0)
- {
- if (moveAvailable)
- {
- return false;
- }
-
- reprap.GetMove()->GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes());
- for (size_t i = numAxes; i < DRIVES; ++i)
- {
- moveBuffer.coords[i] = 0.0;
- }
- // Set the feed rate. If there is any Z hop then we need to pass the Z speed, else we pass the extrusion speed.
- const float speedToUse = (retract) ? retractSpeed : unRetractSpeed;
- moveBuffer.feedRate = (retractHop == 0.0)
- ? speedToUse * secondsToMinutes
- : speedToUse * secondsToMinutes * retractHop/retractLength;
- moveBuffer.coords[Z_AXIS] += (retract) ? retractHop : -retractHop;
- const float lengthToUse = (retract) ? -retractLength : retractLength + retractExtra;
- for (size_t i = 0; i < nDrives; ++i)
- {
- moveBuffer.coords[E0_AXIS + tool->Drive(i)] = lengthToUse;
- }
-
- moveBuffer.isFirmwareRetraction = true;
- moveBuffer.usePressureAdvance = false;
- moveBuffer.filePos = filePos;
- moveBuffer.xAxes = reprap.GetCurrentXAxes();
- moveAvailable = true;
- }
- }
- }
- return true;
-}
-
-// If the code to act on is completed, this returns true,
-// otherwise false. It is called repeatedly for a given
-// code until it returns true for that code.
-bool GCodes::ActOnCode(GCodeBuffer& gb, StringRef& reply)
-{
- // Discard empty buffers right away
- if (gb.IsEmpty())
- {
- return true;
- }
-
- // M-code parameters might contain letters T and G, e.g. in filenames.
- // dc42 assumes that G-and T-code parameters never contain the letter M.
- // Therefore we must check for an M-code first.
- if (gb.Seen('M'))
- {
- return HandleMcode(gb, reply);
- }
- // dc42 doesn't think a G-code parameter ever contains letter T, or a T-code ever contains letter G.
- // So it doesn't matter in which order we look for them.
- if (gb.Seen('G'))
- {
- return HandleGcode(gb, reply);
- }
- if (gb.Seen('T'))
- {
- return HandleTcode(gb, reply);
- }
-
- // An invalid or queued buffer gets discarded
- HandleReply(gb, false, "");
- return true;
-}
-
-bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
-{
- bool result = true;
- bool error = false;
-
- int code = gb.GetIValue();
- if (simulationMode != 0 && code != 0 && code != 1 && code != 4 && code != 10 && code != 20 && code != 21 && code != 90 && code != 91 && code != 92)
- {
- return true; // we only simulate some gcodes
- }
-
- switch (code)
- {
- case 0: // There are no rapid moves...
- case 1: // Ordinary move
- if (!LockMovement(gb))
- {
- return false;
- }
- {
- // Check for 'R' parameter here to go back to the coordinates at which the print was paused
- // NOTE: restore point 2 (tool change) won't work when changing tools on dual axis machines because of X axis mapping.
- // We could possibly fix this by saving the virtual X axis position instead of the physical axis positions.
- // However, slicers normally command the tool to the correct place after a tool change, so we don't need this feature anyway.
- int rParam = (gb.Seen('R')) ? gb.GetIValue() : 0;
- RestorePoint *rp = (rParam == 1) ? &pauseRestorePoint : (rParam == 2) ? &toolChangeRestorePoint : nullptr;
- if (rp != nullptr)
- {
- if (moveAvailable)
- {
- return false;
- }
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- float offset = gb.Seen(axisLetters[axis]) ? gb.GetFValue() * distanceScale : 0.0;
- moveBuffer.coords[axis] = rp->moveCoords[axis] + offset;
- }
- // For now we don't handle extrusion at the same time
- for (size_t drive = numAxes; drive < DRIVES; ++drive)
- {
- moveBuffer.coords[drive] = 0.0;
- }
- moveBuffer.feedRate = (gb.Seen(feedrateLetter)) ? gb.GetFValue() : feedRate;
- moveBuffer.filePos = noFilePosition;
- moveBuffer.usePressureAdvance = false;
- moveAvailable = true;
- }
- else
- {
- int res = SetUpMove(gb, reply);
- if (res == 2)
- {
- gb.SetState(GCodeState::waitingForMoveToComplete);
- }
- result = (res != 0);
- }
- }
- break;
-
- case 4: // Dwell
- result = DoDwell(gb);
- break;
-
- case 10: // Set/report offsets and temperatures, or retract
- if (gb.Seen('P'))
- {
- if (!SetOrReportOffsets(gb, reply))
- {
- return false;
- }
- }
- else
- {
- if (!LockMovement(gb))
- {
- return false;
- }
- result = RetractFilament(true);
- }
- break;
-
- case 11: // Un-retract
- if (!LockMovement(gb))
- {
- return false;
- }
- result = RetractFilament(false);
- break;
-
- case 20: // Inches (which century are we living in, here?)
- distanceScale = INCH_TO_MM;
- break;
-
- case 21: // mm
- distanceScale = 1.0;
- break;
-
- case 28: // Home
- result = DoHome(gb, reply, error);
- break;
-
- case 29: // Grid-based bed probing
- if (!LockMovementAndWaitForStandstill(gb)) // do this first to make sure that a new grid isn't being defined
- {
- return false;
- }
- error = ProbeGrid(gb, reply);
- break;
-
- case 30: // Z probe/manually set at a position and set that as point P
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- if (reprap.GetMove()->IsDeltaMode() && !AllAxesAreHomed())
- {
- reply.copy("Must home a delta printer before bed probing");
- error = true;
- }
- else
- {
- result = SetSingleZProbeAtAPosition(gb, reply);
- }
- break;
-
- case 31: // Return the probe value, or set probe variables
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- result = SetPrintZProbe(gb, reply);
- break;
-
- case 32: // Probe Z at multiple positions and generate the bed transform
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
-
- // Try to execute bed.g
- if (!DoFileMacro(gb, BED_EQUATION_G, reprap.GetMove()->IsDeltaMode()))
- {
- // If we get here then we are not on a delta printer and there is no bed.g file
- if (GetAxisIsHomed(X_AXIS) && GetAxisIsHomed(Y_AXIS))
- {
- gb.SetState(GCodeState::setBed1); // no bed.g file, so use the coordinates specified by M557
- }
- else
- {
- // We can only do bed levelling if X and Y have already been homed
- reply.copy("Must home X and Y before bed probing");
- error = true;
- }
- }
- break;
-
- case 90: // Absolute coordinates
- gb.MachineState().axesRelative = false;
- break;
-
- case 91: // Relative coordinates
- gb.MachineState().axesRelative = true; // Axis movements (i.e. X, Y and Z)
- break;
-
- case 92: // Set position
- result = SetPositions(gb);
- break;
-
- default:
- error = true;
- reply.printf("invalid G Code: %s", gb.Buffer());
- }
-
- if (result && gb.GetState() == GCodeState::normal)
- {
- UnlockAll(gb);
- HandleReply(gb, error, reply.Pointer());
- }
- return result;
-}
-
-bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
-{
- bool result = true;
- bool error = false;
-
- const int code = gb.GetIValue();
- if (simulationMode != 0 && (code < 20 || code > 37) && code != 0 && code != 1 && code != 82 && code != 83 && code != 105 && code != 111 && code != 112 && code != 122 && code != 408 && code != 999)
- {
- return true; // we don't yet simulate most M codes
- }
-
- switch (code)
- {
- case 0: // Stop
- case 1: // Sleep
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- {
- bool wasPaused = isPaused; // isPaused gets cleared by CancelPrint
- CancelPrint();
- if (wasPaused)
- {
- reply.copy("Print cancelled");
- // If we are cancelling a paused print with M0 and cancel.g exists then run it and do nothing else
- if (code == 0)
- {
- if (DoFileMacro(gb, CANCEL_G, false))
- {
- break;
- }
- }
- }
- }
-
- gb.SetState((code == 0) ? GCodeState::stopping : GCodeState::sleeping);
- DoFileMacro(gb, (code == 0) ? STOP_G : SLEEP_G, false);
- break;
-
-#if SUPPORT_ROLAND
- case 3: // Spin spindle
- if (reprap.GetRoland()->Active())
- {
- if (gb.Seen('S'))
- {
- result = reprap.GetRoland()->ProcessSpindle(gb.GetFValue());
- }
- }
- break;
-#endif
-
- case 18: // Motors off
- case 84:
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- {
- bool seen = false;
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- SetAxisNotHomed(axis);
- platform->DisableDrive(axis);
- seen = true;
- }
- }
-
- if (gb.Seen(extrudeLetter))
- {
- long int eDrive[MaxExtruders];
- size_t eCount = numExtruders;
- gb.GetLongArray(eDrive, eCount);
- for (size_t i = 0; i < eCount; i++)
- {
- seen = true;
- if (eDrive[i] < 0 || (size_t)eDrive[i] >= numExtruders)
- {
- reply.printf("Invalid extruder number specified: %ld", eDrive[i]);
- error = true;
- break;
- }
- platform->DisableDrive(numAxes + eDrive[i]);
- }
- }
-
- if (gb.Seen('S'))
- {
- seen = true;
-
- float idleTimeout = gb.GetFValue();
- if (idleTimeout < 0.0)
- {
- reply.copy("Idle timeouts cannot be negative!");
- error = true;
- }
- else
- {
- reprap.GetMove()->SetIdleTimeout(idleTimeout);
- }
- }
-
- if (!seen)
- {
- DisableDrives();
- }
- }
- break;
-
- case 20: // List files on SD card
- if (!LockFileSystem(gb)) // don't allow more than one at a time to avoid contention on output buffers
- {
- return false;
- }
- {
- OutputBuffer *fileResponse;
- const int sparam = (gb.Seen('S')) ? gb.GetIValue() : 0;
- const char* dir = (gb.Seen('P')) ? gb.GetString() : platform->GetGCodeDir();
-
- if (sparam == 2)
- {
- fileResponse = reprap.GetFilesResponse(dir, true); // Send the file list in JSON format
- fileResponse->cat('\n');
- }
- else
- {
- if (!OutputBuffer::Allocate(fileResponse))
- {
- // Cannot allocate an output buffer, try again later
- return false;
- }
-
- // To mimic the behaviour of the official RepRapPro firmware:
- // If we are emulating RepRap then we print "GCode files:\n" at the start, otherwise we don't.
- // If we are emulating Marlin and the code came via the serial/USB interface, then we don't put quotes around the names and we separate them with newline;
- // otherwise we put quotes around them and separate them with comma.
- if (platform->Emulating() == me || platform->Emulating() == reprapFirmware)
- {
- fileResponse->copy("GCode files:\n");
- }
-
- bool encapsulateList = ((&gb != serialGCode && &gb != telnetGCode) || platform->Emulating() != marlin);
- FileInfo fileInfo;
- if (platform->GetMassStorage()->FindFirst(dir, fileInfo))
- {
- // iterate through all entries and append each file name
- do {
- if (encapsulateList)
- {
- fileResponse->catf("%c%s%c%c", FILE_LIST_BRACKET, fileInfo.fileName, FILE_LIST_BRACKET, FILE_LIST_SEPARATOR);
- }
- else
- {
- fileResponse->catf("%s\n", fileInfo.fileName);
- }
- } while (platform->GetMassStorage()->FindNext(fileInfo));
-
- if (encapsulateList)
- {
- // remove the last separator
- (*fileResponse)[fileResponse->Length() - 1] = 0;
- }
- }
- else
- {
- fileResponse->cat("NONE\n");
- }
- }
-
- UnlockAll(gb);
- HandleReply(gb, false, fileResponse);
- return true;
- }
-
- case 21: // Initialise SD card
- if (!LockFileSystem(gb)) // don't allow more than one at a time to avoid contention on output buffers
- {
- return false;
- }
- {
- size_t card = (gb.Seen('P')) ? gb.GetIValue() : 0;
- result = platform->GetMassStorage()->Mount(card, reply, true);
- }
- break;
-
- case 22: // Release SD card
- if (!LockFileSystem(gb)) // don't allow more than one at a time to avoid contention on output buffers
- {
- return false;
- }
- {
- size_t card = (gb.Seen('P')) ? gb.GetIValue() : 0;
- result = platform->GetMassStorage()->Unmount(card, reply);
- }
- break;
-
- case 23: // Set file to print
- case 32: // Select file and start SD print
- if (fileGCode->OriginalMachineState().fileState.IsLive())
- {
- reply.copy("Cannot set file to print, because a file is already being printed");
- error = true;
- break;
- }
-
- if (code == 32 && !LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- {
- const char* filename = gb.GetUnprecedentedString();
- if (filename != nullptr)
- {
- QueueFileToPrint(filename);
- if (fileToPrint.IsLive())
- {
- reprap.GetPrintMonitor()->StartingPrint(filename);
- if (platform->Emulating() == marlin && (&gb == serialGCode || &gb == telnetGCode))
- {
- reply.copy("File opened\nFile selected");
- }
- else
- {
- // Command came from web interface or PanelDue, or not emulating Marlin, so send a nicer response
- reply.printf("File %s selected for printing", filename);
- }
-
- if (code == 32)
- {
- fileGCode->OriginalMachineState().fileState.MoveFrom(fileToPrint);
- reprap.GetPrintMonitor()->StartedPrint();
- }
- }
- else
- {
- reply.printf("Failed to open file %s", filename);
- }
- }
- }
- break;
-
- case 24: // Print/resume-printing the selected file
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
-
- if (isPaused)
- {
- gb.SetState(GCodeState::resuming1);
- DoFileMacro(gb, RESUME_G);
- }
- else if (!fileToPrint.IsLive())
- {
- reply.copy("Cannot print, because no file is selected!");
- error = true;
- }
- else
- {
- fileGCode->OriginalMachineState().fileState.MoveFrom(fileToPrint);
- reprap.GetPrintMonitor()->StartedPrint();
- }
- break;
-
- case 226: // Gcode Initiated Pause
- if (&gb == fileGCode) // ignore M226 if it did't come from within a file being printed
- {
- DoPause(gb);
- }
- break;
-
- case 25: // Pause the print
- if (isPaused)
- {
- reply.copy("Printing is already paused!!");
- error = true;
- }
- else if (!reprap.GetPrintMonitor()->IsPrinting())
- {
- reply.copy("Cannot pause print, because no file is being printed!");
- error = true;
- }
- else
- {
- if (!LockMovement(gb)) // lock movement before calling DoPause
- {
- return false;
- }
- DoPause(gb);
- }
- break;
-
- case 26: // Set SD position
- if (gb.Seen('S'))
- {
- const FilePosition value = gb.GetIValue();
- if (value < 0)
- {
- reply.copy("SD positions can't be negative!");
- error = true;
- }
- else if (fileGCode->OriginalMachineState().fileState.IsLive())
- {
- if (!fileGCode->OriginalMachineState().fileState.Seek(value))
- {
- reply.copy("The specified SD position is invalid!");
- error = true;
- }
- }
- else if (fileToPrint.IsLive())
- {
- if (!fileToPrint.Seek(value))
- {
- reply.copy("The specified SD position is invalid!");
- error = true;
- }
- }
- else
- {
- reply.copy("Cannot set SD file position, because no print is in progress!");
- error = true;
- }
- }
- else
- {
- reply.copy("You must specify the SD position in bytes using the S parameter.");
- error = true;
- }
- break;
-
- case 27: // Report print status - Deprecated
- if (reprap.GetPrintMonitor()->IsPrinting())
- {
- // Pronterface keeps sending M27 commands if "Monitor status" is checked, and it specifically expects the following response syntax
- FileData& fileBeingPrinted = fileGCode->OriginalMachineState().fileState;
- reply.printf("SD printing byte %lu/%lu", fileBeingPrinted.GetPosition(), fileBeingPrinted.Length());
- }
- else
- {
- reply.copy("Not SD printing.");
- }
- break;
-
- case 28: // Write to file
- {
- const char* str = gb.GetUnprecedentedString();
- if (str != nullptr)
- {
- bool ok = OpenFileToWrite(gb, platform->GetGCodeDir(), str);
- if (ok)
- {
- reply.printf("Writing to file: %s", str);
- }
- else
- {
- reply.printf("Can't open file %s for writing.", str);
- error = true;
- }
- }
- }
- break;
-
- case 29: // End of file being written; should be intercepted before getting here
- reply.copy("GCode end-of-file being interpreted.");
- break;
-
- case 30: // Delete file
- {
- const char *filename = gb.GetUnprecedentedString();
- if (filename != nullptr)
- {
- DeleteFile(filename);
- }
- }
- break;
-
- // For case 32, see case 24
-
- case 36: // Return file information
- if (!LockFileSystem(gb)) // getting file info takes several calls and isn't reentrant
- {
- return false;
- }
- {
- const char* filename = gb.GetUnprecedentedString(true); // get filename, or nullptr if none provided
- OutputBuffer *fileInfoResponse;
- result = reprap.GetPrintMonitor()->GetFileInfoResponse(filename, fileInfoResponse);
- if (result)
- {
- fileInfoResponse->cat('\n');
- UnlockAll(gb);
- HandleReply(gb, false, fileInfoResponse);
- return true;
- }
- }
- break;
-
- case 37: // Simulation mode on/off
- if (gb.Seen('S'))
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
-
- bool wasSimulating = (simulationMode != 0);
- simulationMode = (uint8_t)gb.GetIValue();
- reprap.GetMove()->Simulate(simulationMode);
-
- if (simulationMode != 0)
- {
- simulationTime = 0.0;
- if (!wasSimulating)
- {
- // Starting a new simulation, so save the current position
- reprap.GetMove()->GetCurrentUserPosition(simulationRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes());
- simulationRestorePoint.feedRate = feedRate;
- }
- }
- else if (wasSimulating)
- {
- // Ending a simulation, so restore the position
- SetPositions(simulationRestorePoint.moveCoords);
- for (size_t i = 0; i < DRIVES; ++i)
- {
- moveBuffer.coords[i] = simulationRestorePoint.moveCoords[i];
- }
- feedRate = simulationRestorePoint.feedRate;
- }
- }
- else
- {
- reply.printf("Simulation mode: %s, move time: %.1f sec, other time: %.1f sec",
- (simulationMode != 0) ? "on" : "off", reprap.GetMove()->GetSimulationTime(), simulationTime);
- }
- break;
-
- case 38: // Report SHA1 of file
- if (!LockFileSystem(gb)) // getting file hash takes several calls and isn't reentrant
- {
- return false;
- }
- if (fileBeingHashed == nullptr)
- {
- // See if we can open the file and start hashing
- const char* filename = gb.GetUnprecedentedString(true);
- if (StartHash(filename))
- {
- // Hashing is now in progress...
- result = false;
- }
- else
- {
- error = true;
- reply.printf("Cannot open file: %s", filename);
- }
- }
- else
- {
- // This can take some time. All the actual heavy lifting is in dedicated methods
- result = AdvanceHash(reply);
- }
- break;
-
- case 42: // Turn an output pin on or off
- if (gb.Seen('P'))
- {
- const int logicalPin = gb.GetIValue();
- Pin pin;
- bool invert;
- if (platform->GetFirmwarePin(logicalPin, PinAccess::pwm, pin, invert))
- {
- if (gb.Seen('S'))
- {
- float val = gb.GetFValue();
- if (val > 1.0)
- {
- val /= 255.0;
- }
- val = constrain<float>(val, 0.0, 1.0);
- if (invert)
- {
- val = 1.0 - val;
- }
- Platform::WriteAnalog(pin, val, DefaultPinWritePwmFreq);
- }
- // Ignore the command if no S parameter provided
- }
- else
- {
- reply.printf("Logical pin %d is not available for writing", logicalPin);
- error = true;
- }
- }
- break;
-
- case 80: // ATX power on
- platform->SetAtxPower(true);
- break;
-
- case 81: // ATX power off
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- platform->SetAtxPower(false);
- break;
-
- case 82: // Use absolute extruder positioning
- if (gb.MachineState().drivesRelative) // don't reset the absolute extruder position if it was already absolute
- {
- for (size_t extruder = 0; extruder < MaxExtruders; extruder++)
- {
- lastRawExtruderPosition[extruder] = 0.0;
- }
- gb.MachineState().drivesRelative = false;
- }
- break;
-
- case 83: // Use relative extruder positioning
- if (!gb.MachineState().drivesRelative) // don't reset the absolute extruder position if it was already relative
- {
- for (size_t extruder = 0; extruder < MaxExtruders; extruder++)
- {
- lastRawExtruderPosition[extruder] = 0.0;
- }
- gb.MachineState().drivesRelative = true;
- }
- break;
-
- // For case 84, see case 18
-
- case 85: // Set inactive time
- break;
-
- case 92: // Set/report steps/mm for some axes
- {
- // Save the current positions as we may need them later
- float positionNow[DRIVES];
- Move *move = reprap.GetMove();
- move->GetCurrentUserPosition(positionNow, 0, reprap.GetCurrentXAxes());
-
- bool seen = false;
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- platform->SetDriveStepsPerUnit(axis, gb.GetFValue());
- seen = true;
- }
- }
-
- if (gb.Seen(extrudeLetter))
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- seen = true;
- float eVals[MaxExtruders];
- size_t eCount = numExtruders;
- gb.GetFloatArray(eVals, eCount, true);
-
- // The user may not have as many extruders as we allow for, so just set the ones for which a value is provided
- for (size_t e = 0; e < eCount; e++)
- {
- platform->SetDriveStepsPerUnit(numAxes + e, eVals[e]);
- }
- }
-
- if (seen)
- {
- // On a delta, if we change the drive steps/mm then we need to recalculate the motor positions
- SetPositions(positionNow);
- }
- else
- {
- reply.copy("Steps/mm: ");
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- reply.catf("%c: %.3f, ", axisLetters[axis], platform->DriveStepsPerUnit(axis));
- }
- reply.catf("E:");
- char sep = ' ';
- for (size_t extruder = 0; extruder < numExtruders; extruder++)
- {
- reply.catf("%c%.3f", sep, platform->DriveStepsPerUnit(extruder + numAxes));
- sep = ':';
- }
- }
- }
- break;
-
- case 98: // Call Macro/Subprogram
- if (gb.Seen('P'))
- {
- DoFileMacro(gb, gb.GetString());
- }
- break;
-
- case 99: // Return from Macro/Subprogram
- FileMacroCyclesReturn(gb);
- break;
-
- case 104: // Deprecated. This sets the active temperature of every heater of the active tool
- if (gb.Seen('S'))
- {
- float temperature = gb.GetFValue();
- Tool* tool;
- if (gb.Seen('T'))
- {
- int toolNumber = gb.GetIValue();
- toolNumber += gb.GetToolNumberAdjust();
- tool = reprap.GetTool(toolNumber);
- }
- else
- {
- tool = reprap.GetCurrentTool();
- // If no tool is selected, and there is only one tool, set the active temperature for that one
- if (tool == nullptr)
- {
- tool = reprap.GetOnlyTool();
- }
- }
- SetToolHeaters(tool, temperature);
- }
- break;
-
- case 105: // Get temperatures
- {
- const int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
- const int8_t chamberHeater = reprap.GetHeat()->GetChamberHeater();
- reply.copy("T:");
- for (int8_t heater = 0; heater < HEATERS; heater++)
- {
- if (heater != bedHeater && heater != chamberHeater)
- {
- Heat::HeaterStatus hs = reprap.GetHeat()->GetStatus(heater);
- if (hs != Heat::HS_off && hs != Heat::HS_fault)
- {
- reply.catf("%.1f ", reprap.GetHeat()->GetTemperature(heater));
- }
- }
- }
- if (bedHeater >= 0)
- {
- reply.catf("B:%.1f", reprap.GetHeat()->GetTemperature(bedHeater));
- }
- else
- {
- // I'm not sure whether Pronterface etc. can handle a missing bed temperature, so return zero
- reply.cat("B:0.0");
- }
- if (chamberHeater >= 0.0)
- {
- reply.catf(" C:%.1f", reprap.GetHeat()->GetTemperature(chamberHeater));
- }
- }
- break;
-
- case 106: // Set/report fan values
- {
- bool seenFanNum = false;
- int32_t fanNum = 0; // Default to the first fan
- gb.TryGetIValue('P', fanNum, seenFanNum);
- if (fanNum < 0 || fanNum > (int)NUM_FANS)
- {
- reply.printf("Fan number %d is invalid, must be between 0 and %u", fanNum, NUM_FANS);
- }
- else
- {
- bool seen = false;
- Fan& fan = platform->GetFan(fanNum);
-
- if (gb.Seen('I')) // Invert cooling
- {
- const int invert = gb.GetIValue();
- if (invert < 0)
- {
- fan.Disable();
- }
- else
- {
- fan.SetInverted(invert > 0);
- }
- seen = true;
- }
-
- if (gb.Seen('F')) // Set PWM frequency
- {
- fan.SetPwmFrequency(gb.GetFValue());
- seen = true;
- }
-
- if (gb.Seen('T')) // Set thermostatic trigger temperature
- {
- seen = true;
- fan.SetTriggerTemperature(gb.GetFValue());
- }
-
- if (gb.Seen('B')) // Set blip time
- {
- seen = true;
- fan.SetBlipTime(gb.GetFValue());
- }
-
- if (gb.Seen('L')) // Set minimum speed
- {
- seen = true;
- fan.SetMinValue(gb.GetFValue());
- }
-
- if (gb.Seen('H')) // Set thermostatically-controller heaters
- {
- seen = true;
- long heaters[HEATERS];
- size_t numH = HEATERS;
- gb.GetLongArray(heaters, numH);
- // Note that M106 H-1 disables thermostatic mode. The following code implements that automatically.
- uint16_t hh = 0;
- for (size_t h = 0; h < numH; ++h)
- {
- const int hnum = heaters[h];
- if (hnum >= 0 && hnum < HEATERS)
- {
- hh |= (1u << (unsigned int)hnum);
- }
- }
- if (hh != 0)
- {
- platform->SetFanValue(fanNum, 1.0); // default the fan speed to full for safety
- }
- fan.SetHeatersMonitored(hh);
- }
-
- if (gb.Seen('S')) // Set new fan value - process this after processing 'H' or it may not be acted on
- {
- const float f = constrain<float>(gb.GetFValue(), 0.0, 255.0);
- if (seen || seenFanNum)
- {
- platform->SetFanValue(fanNum, f);
- }
- else
- {
- // We are processing an M106 S### command with no other recognised parameters and we have a tool selected.
- // Apply the fan speed setting to the fans in the fan mapping for the current tool.
- lastDefaultFanSpeed = f;
- SetMappedFanSpeed();
- }
- }
- else if (gb.Seen('R'))
- {
- const int i = gb.GetIValue();
- switch(i)
- {
- case 0:
- case 1:
- // Restore fan speed to value when print was paused
- platform->SetFanValue(fanNum, pausedFanValues[fanNum]);
- break;
- case 2:
- // Set the speeds of mapped fans to the last known value. Fan number is ignored.
- SetMappedFanSpeed();
- break;
- default:
- break;
- }
- }
- else if (!seen)
- {
- reply.printf("Fan%i frequency: %dHz, speed: %d%%, min: %d%%, blip: %.2f, inverted: %s",
- fanNum,
- (int)(fan.GetPwmFrequency()),
- (int)(fan.GetValue() * 100.0),
- (int)(fan.GetMinValue() * 100.0),
- fan.GetBlipTime(),
- (fan.GetInverted()) ? "yes" : "no");
- uint16_t hh = fan.GetHeatersMonitored();
- if (hh != 0)
- {
- reply.catf(", trigger: %dC, heaters:", (int)fan.GetTriggerTemperature());
- for (unsigned int i = 0; i < HEATERS; ++i)
- {
- if ((hh & (1u << i)) != 0)
- {
- reply.catf(" %u", i);
- }
- }
- }
- }
- }
- }
- break;
-
- case 107: // Fan off - deprecated
- platform->SetFanValue(0, 0.0); //T3P3 as deprecated only applies to fan0
- break;
-
- case 108: // Cancel waiting for temperature
- if (isWaiting)
- {
- cancelWait = true;
- }
- break;
-
- case 109: // Deprecated in RRF, but widely generated by slicers
- {
- float temperature;
- bool waitWhenCooling;
- if (gb.Seen('R'))
- {
- waitWhenCooling = true;
- temperature = gb.GetFValue();
- }
- else if (gb.Seen('S'))
- {
- waitWhenCooling = false;
- temperature = gb.GetFValue();
- }
- else
- {
- break; // no target temperature given
- }
-
- Tool *tool;
- if (gb.Seen('T'))
- {
- int toolNumber = gb.GetIValue();
- toolNumber += gb.GetToolNumberAdjust();
- tool = reprap.GetTool(toolNumber);
- }
- else
- {
- tool = reprap.GetCurrentTool();
- // If no tool is selected, and there is only one tool, set the active temperature for that one
- if (tool == nullptr)
- {
- tool = reprap.GetOnlyTool();
- }
- }
- SetToolHeaters(tool, temperature);
- if (cancelWait || ToolHeatersAtSetTemperatures(tool, waitWhenCooling))
- {
- cancelWait = isWaiting = false;
- break;
- }
- // In Marlin emulation mode we should return some sort of (undocumented) message here every second...
- isWaiting = true;
- return false;
- }
- break;
-
- case 110: // Set line numbers - line numbers are dealt with in the GCodeBuffer class
- break;
-
- case 111: // Debug level
- if (gb.Seen('S'))
- {
- bool dbv = (gb.GetIValue() != 0);
- if (gb.Seen('P'))
- {
- reprap.SetDebug(static_cast<Module>(gb.GetIValue()), dbv);
- }
- else
- {
- reprap.SetDebug(dbv);
- }
- }
- else
- {
- reprap.PrintDebug();
- }
- break;
-
- case 112: // Emergency stop - acted upon in Webserver, but also here in case it comes from USB etc.
- DoEmergencyStop();
- break;
-
- case 114:
- GetCurrentCoordinates(reply);
- break;
-
- case 115: // Print firmware version or set hardware type
- if (gb.Seen('P'))
- {
- platform->SetBoardType((BoardType)gb.GetIValue());
- }
- else
- {
- reply.printf("FIRMWARE_NAME: %s FIRMWARE_VERSION: %s ELECTRONICS: %s", NAME, VERSION, platform->GetElectronicsString());
-#ifdef DUET_NG
- const char* expansionName = DuetExpansion::GetExpansionBoardName();
- if (expansionName != nullptr)
- {
- reply.catf(" + %s", expansionName);
- }
-#endif
- reply.catf(" FIRMWARE_DATE: %s", DATE);
- }
- break;
-
- case 116: // Wait for set temperatures
- {
- bool seen = false;
- if (gb.Seen('P'))
- {
- // Wait for the heaters associated with the specified tool to be ready
- int toolNumber = gb.GetIValue();
- toolNumber += gb.GetToolNumberAdjust();
- if (!cancelWait && !ToolHeatersAtSetTemperatures(reprap.GetTool(toolNumber), true))
- {
- isWaiting = true;
- return false;
- }
- seen = true;
- }
-
- if (gb.Seen('H'))
- {
- // Wait for specified heaters to be ready
- long heaters[HEATERS];
- size_t heaterCount = HEATERS;
- gb.GetLongArray(heaters, heaterCount);
- if (!cancelWait)
- {
- for (size_t i=0; i<heaterCount; i++)
- {
- if (!reprap.GetHeat()->HeaterAtSetTemperature(heaters[i], true))
- {
- isWaiting = true;
- return false;
- }
- }
- }
- seen = true;
- }
-
- if (gb.Seen('C'))
- {
- // Wait for chamber heater to be ready
- const int8_t chamberHeater = reprap.GetHeat()->GetChamberHeater();
- if (chamberHeater != -1)
- {
- if (!cancelWait && !reprap.GetHeat()->HeaterAtSetTemperature(chamberHeater, true))
- {
- isWaiting = true;
- return false;
- }
- }
- seen = true;
- }
-
- // Wait for all heaters to be ready
- if (!seen && !cancelWait && !reprap.GetHeat()->AllHeatersAtSetTemperatures(true))
- {
- isWaiting = true;
- return false;
- }
-
- // If we get here, there is nothing more to wait for
- cancelWait = isWaiting = false;
- }
- break;
-
- case 117: // Display message
- {
- const char *msg = gb.GetUnprecedentedString(true);
- reprap.SetMessage((msg == nullptr) ? "" : msg);
- }
- break;
-
- case 119:
- reply.copy("Endstops - ");
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- reply.catf("%c: %s, ", axisLetters[axis], TranslateEndStopResult(platform->Stopped(axis)));
- }
- reply.catf("Z probe: %s", TranslateEndStopResult(platform->GetZProbeResult()));
- break;
-
- case 120:
- Push(gb);
- break;
-
- case 121:
- Pop(gb);
- break;
-
- case 122:
- {
- int val = (gb.Seen('P')) ? gb.GetIValue() : 0;
- if (val == 0)
- {
- reprap.Diagnostics(gb.GetResponseMessageType());
- }
- else
- {
- platform->DiagnosticTest(val);
- }
- }
- break;
-
- case 135: // Set PID sample interval
- if (gb.Seen('S'))
- {
- platform->SetHeatSampleTime(gb.GetFValue() * 0.001); // Value is in milliseconds; we want seconds
- }
- else
- {
- reply.printf("Heat sample time is %.3f seconds", platform->GetHeatSampleTime());
- }
- break;
-
- case 140: // Set bed temperature
- {
- int8_t bedHeater;
- if (gb.Seen('H'))
- {
- bedHeater = gb.GetIValue();
- if (bedHeater < 0)
- {
- // Make sure we stay within reasonable boundaries...
- bedHeater = -1;
-
- // If we're disabling the hot bed, make sure the old heater is turned off
- reprap.GetHeat()->SwitchOff(reprap.GetHeat()->GetBedHeater());
- }
- else if (bedHeater >= HEATERS)
- {
- reply.copy("Invalid heater number!");
- error = true;
- break;
- }
- reprap.GetHeat()->SetBedHeater(bedHeater);
-
- if (bedHeater < 0)
- {
- // Stop here if the hot bed has been disabled
- break;
- }
- }
- else
- {
- bedHeater = reprap.GetHeat()->GetBedHeater();
- if (bedHeater < 0)
- {
- reply.copy("Hot bed is not present!");
- error = true;
- break;
- }
- }
-
- if(gb.Seen('S'))
- {
- float temperature = gb.GetFValue();
- if (temperature < NEARLY_ABS_ZERO)
- {
- reprap.GetHeat()->SwitchOff(bedHeater);
- }
- else
- {
- reprap.GetHeat()->SetActiveTemperature(bedHeater, temperature);
- reprap.GetHeat()->Activate(bedHeater);
- }
- }
- if(gb.Seen('R'))
- {
- reprap.GetHeat()->SetStandbyTemperature(bedHeater, gb.GetFValue());
- }
- }
- break;
-
- case 141: // Chamber temperature
- {
- bool seen = false;
- if (gb.Seen('H'))
- {
- seen = true;
-
- int heater = gb.GetIValue();
- if (heater < 0)
- {
- const int8_t currentHeater = reprap.GetHeat()->GetChamberHeater();
- if (currentHeater != -1)
- {
- reprap.GetHeat()->SwitchOff(currentHeater);
- }
-
- reprap.GetHeat()->SetChamberHeater(-1);
- }
- else if (heater < HEATERS)
- {
- reprap.GetHeat()->SetChamberHeater(heater);
- }
- else
- {
- reply.copy("Bad heater number specified!");
- error = true;
- }
- }
-
- if (gb.Seen('S'))
- {
- seen = true;
-
- const int8_t currentHeater = reprap.GetHeat()->GetChamberHeater();
- if (currentHeater != -1)
- {
- float temperature = gb.GetFValue();
-
- if (temperature < NEARLY_ABS_ZERO)
- {
- reprap.GetHeat()->SwitchOff(currentHeater);
- }
- else
- {
- reprap.GetHeat()->SetActiveTemperature(currentHeater, temperature);
- reprap.GetHeat()->Activate(currentHeater);
- }
- }
- else
- {
- reply.copy("No chamber heater has been set up yet!");
- error = true;
- }
- }
-
- if (!seen)
- {
- const int8_t currentHeater = reprap.GetHeat()->GetChamberHeater();
- if (currentHeater != -1)
- {
- reply.printf("Chamber heater %d is currently at %.1fC", currentHeater, reprap.GetHeat()->GetTemperature(currentHeater));
- }
- else
- {
- reply.copy("No chamber heater has been configured yet.");
- }
- }
- }
- break;
-
- case 143: // Set temperature limit
- {
- const int heater = (gb.Seen('H')) ? gb.GetIValue() : 1; // default to extruder 1 if no heater number provided
- if (heater < 0 || heater >= HEATERS)
- {
- reply.copy("Invalid heater number");
- error = true;
- }
- else if (gb.Seen('S'))
- {
- const float limit = gb.GetFValue();
- if (limit > BAD_LOW_TEMPERATURE && limit < BAD_ERROR_TEMPERATURE)
- {
- reprap.GetHeat()->SetTemperatureLimit(heater, limit);
- }
- else
- {
- reply.copy("Invalid temperature limit");
- error = true;
- }
- }
- else
- {
- reply.printf("Temperature limit for heater %d is %.1fC", heater, reprap.GetHeat()->GetTemperatureLimit(heater));
- }
- }
- break;
-
- case 144: // Set bed to standby
- {
- const int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
- if (bedHeater >= 0)
- {
- reprap.GetHeat()->Standby(bedHeater);
- }
- }
- break;
-
- case 190: // Set bed temperature and wait
- case 191: // Set chamber temperature and wait
- {
- const int8_t heater = (code == 191) ? reprap.GetHeat()->GetChamberHeater() : reprap.GetHeat()->GetBedHeater();
- if (heater >= 0)
- {
- float temperature;
- bool waitWhenCooling;
- if (gb.Seen('R'))
- {
- waitWhenCooling = true;
- temperature = gb.GetFValue();
- }
- else if (gb.Seen('S'))
- {
- waitWhenCooling = false;
- temperature = gb.GetFValue();
- }
- else
- {
- break; // no target temperature given
- }
-
- reprap.GetHeat()->SetActiveTemperature(heater, temperature);
- reprap.GetHeat()->Activate(heater);
- if (cancelWait || reprap.GetHeat()->HeaterAtSetTemperature(heater, waitWhenCooling))
- {
- cancelWait = isWaiting = false;
- break;
- }
- // In Marlin emulation mode we should return some sort of (undocumented) message here every second...
- isWaiting = true;
- return false;
- }
- }
- break;
-
- case 201: // Set/print axis accelerations
- {
- bool seen = false;
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- platform->SetAcceleration(axis, gb.GetFValue() * distanceScale);
- seen = true;
- }
- }
-
- if (gb.Seen(extrudeLetter))
- {
- seen = true;
- float eVals[MaxExtruders];
- size_t eCount = numExtruders;
- gb.GetFloatArray(eVals, eCount, true);
- for (size_t e = 0; e < eCount; e++)
- {
- platform->SetAcceleration(numAxes + e, eVals[e] * distanceScale);
- }
- }
-
- if (gb.Seen('P'))
- {
- // Set max average printing acceleration
- platform->SetMaxAverageAcceleration(gb.GetFValue() * distanceScale);
- seen = true;
- }
-
- if (!seen)
- {
- reply.printf("Accelerations: ");
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- reply.catf("%c: %.1f, ", axisLetters[axis], platform->Acceleration(axis) / distanceScale);
- }
- reply.cat("E:");
- char sep = ' ';
- for (size_t extruder = 0; extruder < numExtruders; extruder++)
- {
- reply.catf("%c%.1f", sep, platform->Acceleration(extruder + numAxes) / distanceScale);
- sep = ':';
- }
- reply.catf(", avg. printing: %.1f", platform->GetMaxAverageAcceleration());
- }
- }
- break;
-
- case 203: // Set/print maximum feedrates
- {
- bool seen = false;
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- platform->SetMaxFeedrate(axis, gb.GetFValue() * distanceScale * secondsToMinutes); // G Code feedrates are in mm/minute; we need mm/sec
- seen = true;
- }
- }
-
- if (gb.Seen(extrudeLetter))
- {
- seen = true;
- float eVals[MaxExtruders];
- size_t eCount = numExtruders;
- gb.GetFloatArray(eVals, eCount, true);
- for (size_t e = 0; e < eCount; e++)
- {
- platform->SetMaxFeedrate(numAxes + e, eVals[e] * distanceScale * secondsToMinutes);
- }
- }
-
- if (!seen)
- {
- reply.copy("Maximum feedrates: ");
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- reply.catf("%c: %.1f, ", axisLetters[axis], platform->MaxFeedrate(axis) / (distanceScale * secondsToMinutes));
- }
- reply.cat("E:");
- char sep = ' ';
- for (size_t extruder = 0; extruder < numExtruders; extruder++)
- {
- reply.catf("%c%.1f", sep, platform->MaxFeedrate(extruder + numAxes) / (distanceScale * secondsToMinutes));
- sep = ':';
- }
- }
- }
- 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
- // This is superseded in this firmware by M codes for the separate types (e.g. M566).
- break;
-
- case 206: // Offset axes - Deprecated
- result = OffsetAxes(gb);
- break;
-
- case 207: // Set firmware retraction details
- {
- bool seen = false;
- if (gb.Seen('S'))
- {
- retractLength = max<float>(gb.GetFValue(), 0.0);
- seen = true;
- }
- if (gb.Seen('R'))
- {
- retractExtra = max<float>(gb.GetFValue(), -retractLength);
- seen = true;
- }
- if (gb.Seen('F'))
- {
- unRetractSpeed = retractSpeed = max<float>(gb.GetFValue(), 60.0);
- seen = true;
- }
- if (gb.Seen('T')) // must do this one after 'F'
- {
- unRetractSpeed = max<float>(gb.GetFValue(), 60.0);
- seen = true;
- }
- if (gb.Seen('Z'))
- {
- retractHop = max<float>(gb.GetFValue(), 0.0);
- seen = true;
- }
- if (!seen)
- {
- reply.printf("Retraction settings: length %.2f/%.2fmm, speed %d/%dmm/min, Z hop %.2fmm",
- retractLength, retractLength + retractExtra, (int)retractSpeed, (int)unRetractSpeed, retractHop);
- }
- }
- break;
-
- case 208: // Set/print maximum axis lengths. If there is an S parameter with value 1 then we set the min value, else we set the max value.
- {
- bool setMin = (gb.Seen('S') ? (gb.GetIValue() == 1) : false);
- bool seen = false;
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- float value = gb.GetFValue() * distanceScale;
- if (setMin)
- {
- platform->SetAxisMinimum(axis, value);
- }
- else
- {
- platform->SetAxisMaximum(axis, value);
- }
- seen = true;
- }
- }
-
- if (!seen)
- {
- reply.copy("Axis limits ");
- char sep = '-';
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- reply.catf("%c %c: %.1f min, %.1f max", sep, axisLetters[axis], platform->AxisMinimum(axis),
- platform->AxisMaximum(axis));
- sep = ',';
- }
- }
- }
- break;
-
- case 210: // Set/print homing feed rates
- // This is no longer used, but for backwards compatibility we don't report an error
- break;
-
- case 220: // Set/report speed factor override percentage
- if (gb.Seen('S'))
- {
- float newSpeedFactor = (gb.GetFValue() / 100.0) * secondsToMinutes; // include the conversion from mm/minute to mm/second
- if (newSpeedFactor > 0.0)
- {
- feedRate *= newSpeedFactor / speedFactor;
- if (moveAvailable && !moveBuffer.isFirmwareRetraction)
- {
- // The last move has not gone yet, so we can update it
- moveBuffer.feedRate *= newSpeedFactor / speedFactor;
- }
- speedFactor = newSpeedFactor;
- }
- else
- {
- reply.printf("Invalid speed factor specified.");
- error = true;
- }
- }
- else
- {
- reply.printf("Speed factor override: %.1f%%", speedFactor * minutesToSeconds * 100.0);
- }
- break;
-
- case 221: // Set/report extrusion factor override percentage
- {
- int extruder = 0;
- if (gb.Seen('D')) // D parameter (if present) selects the extruder number
- {
- extruder = gb.GetIValue();
- }
-
- if (gb.Seen('S')) // S parameter sets the override percentage
- {
- float extrusionFactor = gb.GetFValue() / 100.0;
- if (extruder >= 0 && (size_t)extruder < numExtruders && extrusionFactor >= 0.0)
- {
- if (moveAvailable && !moveBuffer.isFirmwareRetraction)
- {
- moveBuffer.coords[extruder + numAxes] *= extrusionFactor/extrusionFactors[extruder]; // last move not gone, so update it
- }
- extrusionFactors[extruder] = extrusionFactor;
- }
- }
- else
- {
- reply.printf("Extrusion factor override for extruder %d: %.1f%%", extruder,
- extrusionFactors[extruder] * 100.0);
- }
- }
- break;
-
- // For case 226, see case 25
-
- case 280: // Servos
- if (gb.Seen('P'))
- {
- const int servoIndex = gb.GetIValue();
- Pin servoPin;
- bool invert;
- if (platform->GetFirmwarePin(servoIndex, PinAccess::servo, servoPin, invert))
- {
- if (gb.Seen('I'))
- {
- if (gb.GetIValue() > 0)
- {
- invert = !invert;
- }
- }
- if (gb.Seen('S'))
- {
- float angleOrWidth = gb.GetFValue();
- if (angleOrWidth < 0.0)
- {
- // Disable the servo by setting the pulse width to zero
- Platform::WriteAnalog(servoPin, (invert) ? 1.0 : 0.0, ServoRefreshFrequency);
- }
- else
- {
- if (angleOrWidth < MinServoPulseWidth)
- {
- // User gave an angle so convert it to a pulse width in microseconds
- angleOrWidth = (min<float>(angleOrWidth, 180.0) * ((MaxServoPulseWidth - MinServoPulseWidth) / 180.0)) + MinServoPulseWidth;
- }
- else if (angleOrWidth > MaxServoPulseWidth)
- {
- angleOrWidth = MaxServoPulseWidth;
- }
- float pwm = angleOrWidth * (ServoRefreshFrequency/1e6);
- if (invert)
- {
- pwm = 1.0 - pwm;
- }
- Platform::WriteAnalog(servoPin, pwm, ServoRefreshFrequency);
- }
- }
- // We don't currently allow the servo position to be read back
- }
- else
- {
- platform->MessageF(GENERIC_MESSAGE, "Error: Invalid servo index %d in M280 command\n", servoIndex);
- }
- }
- break;
-
- case 300: // Beep
- {
- int ms = (gb.Seen('P')) ? gb.GetIValue() : 1000; // time in milliseconds
- int freq = (gb.Seen('S')) ? gb.GetIValue() : 4600; // 4600Hz produces the loudest sound on a PanelDue
- reprap.Beep(freq, ms);
- }
- break;
-
- case 301: // Set/report hot end PID values
- SetPidParameters(gb, 1, reply);
- break;
-
- case 302: // Allow, deny or report cold extrudes
- if (gb.Seen('P'))
- {
- if (gb.GetIValue() > 0)
- {
- reprap.GetHeat()->AllowColdExtrude();
- }
- else
- {
- reprap.GetHeat()->DenyColdExtrude();
- }
- }
- else
- {
- reply.printf("Cold extrusion is %s, use M302 P[1/0] to allow/deny it",
- reprap.GetHeat()->ColdExtrude() ? "allowed" : "denied");
- }
- break;
-
- case 303: // Run PID tuning
- if (gb.Seen('H'))
- {
- const size_t heater = gb.GetIValue();
- const float temperature = (gb.Seen('S')) ? gb.GetFValue() : 225.0;
- const float maxPwm = (gb.Seen('P')) ? gb.GetFValue() : 0.5;
- if (heater < HEATERS && maxPwm >= 0.1 && maxPwm <= 1.0 && temperature >= 55.0 && temperature <= reprap.GetHeat()->GetTemperatureLimit(heater))
- {
- reprap.GetHeat()->StartAutoTune(heater, temperature, maxPwm, reply);
- }
- else
- {
- reply.printf("Bad parameter in M303 command");
- }
- }
- else
- {
- reprap.GetHeat()->GetAutoTuneStatus(reply);
- }
- break;
-
- case 304: // Set/report heated bed PID values
- {
- const int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
- if (bedHeater >= 0)
- {
- SetPidParameters(gb, bedHeater, reply);
- }
- }
- break;
-
- case 305: // Set/report specific heater parameters
- SetHeaterParameters(gb, reply);
- break;
-
- case 307: // Set heater process model parameters
- if (gb.Seen('H'))
- {
- size_t heater = gb.GetIValue();
- if (heater < HEATERS)
- {
- const FopDt& model = reprap.GetHeat()->GetHeaterModel(heater);
- bool seen = false;
- float gain = model.GetGain(), tc = model.GetTimeConstant(), td = model.GetDeadTime(), maxPwm = model.GetMaxPwm();
- int32_t dontUsePid = model.UsePid() ? 0 : 1;
-
- gb.TryGetFValue('A', gain, seen);
- gb.TryGetFValue('C', tc, seen);
- gb.TryGetFValue('D', td, seen);
- gb.TryGetIValue('B', dontUsePid, seen);
- gb.TryGetFValue('S', maxPwm, seen);
-
- if (seen)
- {
- if (reprap.GetHeat()->SetHeaterModel(heater, gain, tc, td, maxPwm, dontUsePid == 0))
- {
- reprap.GetHeat()->UseModel(heater, true);
- }
- else
- {
- reply.copy("Error: bad model parameters");
- }
- }
- else if (!model.IsEnabled())
- {
- reply.printf("Heater %u is disabled", heater);
- }
- else
- {
- reply.printf("Heater %u model: gain %.1f, time constant %.1f, dead time %.1f, max PWM %.2f, in use: %s, mode: %s",
- heater, model.GetGain(), model.GetTimeConstant(), model.GetDeadTime(), model.GetMaxPwm(),
- (reprap.GetHeat()->IsModelUsed(heater)) ? "yes" : "no",
- (model.UsePid()) ? "PID" : "bang-bang");
- if (model.UsePid())
- {
- // When reporting the PID parameters, we scale them by 255 for compatibility with older firmware and other firmware
- const PidParams& spParams = model.GetPidParameters(false);
- const float scaledSpKp = 255.0 * spParams.kP;
- reply.catf("\nSetpoint change: P%.1f, I%.2f, D%.1f",
- scaledSpKp, scaledSpKp * spParams.recipTi, scaledSpKp * spParams.tD);
- const PidParams& ldParams = model.GetPidParameters(true);
- const float scaledLoadKp = 255.0 * ldParams.kP;
- reply.catf("\nLoad change: P%.1f, I%.2f, D%.1f",
- scaledLoadKp, scaledLoadKp * ldParams.recipTi, scaledLoadKp * ldParams.tD);
- }
- }
- }
- }
- break;
-
- case 350: // Set/report microstepping
- {
- // interp is current an int not a bool, because we use special values of interp to set the chopper control register
- int interp = 0;
- if (gb.Seen('I'))
- {
- interp = gb.GetIValue();
- }
-
- bool seen = false;
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- seen = true;
- int microsteps = gb.GetIValue();
- if (ChangeMicrostepping(axis, microsteps, interp))
- {
- SetAxisNotHomed(axis);
- }
- else
- {
- platform->MessageF(GENERIC_MESSAGE, "Drive %c does not support %dx microstepping%s\n",
- axisLetters[axis], microsteps, (interp) ? " with interpolation" : "");
- }
- }
- }
-
- if (gb.Seen(extrudeLetter))
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- seen = true;
- long eVals[MaxExtruders];
- size_t eCount = numExtruders;
- gb.GetLongArray(eVals, eCount);
- for (size_t e = 0; e < eCount; e++)
- {
- if (!ChangeMicrostepping(numAxes + e, (int)eVals[e], interp))
- {
- platform->MessageF(GENERIC_MESSAGE, "Drive E%u does not support %dx microstepping%s\n",
- e, (int)eVals[e], (interp) ? " with interpolation" : "");
- }
- }
- }
-
- if (!seen)
- {
- reply.copy("Microstepping - ");
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- bool interp;
- int microsteps = platform->GetMicrostepping(axis, interp);
- reply.catf("%c:%d%s, ", axisLetters[axis], microsteps, (interp) ? "(on)" : "");
- }
- reply.cat("E");
- for (size_t extruder = 0; extruder < numExtruders; extruder++)
- {
- bool interp;
- int microsteps = platform->GetMicrostepping(extruder + numAxes, interp);
- reply.catf(":%d%s", microsteps, (interp) ? "(on)" : "");
- }
- }
- }
- break;
-
- case 400: // Wait for current moves to finish
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- break;
-
- case 404: // Filament width and nozzle diameter
- {
- bool seen = false;
-
- if (gb.Seen('N'))
- {
- platform->SetFilamentWidth(gb.GetFValue());
- seen = true;
- }
- if (gb.Seen('D'))
- {
- platform->SetNozzleDiameter(gb.GetFValue());
- seen = true;
- }
-
- if (!seen)
- {
- reply.printf("Filament width: %.2fmm, nozzle diameter: %.2fmm", platform->GetFilamentWidth(), platform->GetNozzleDiameter());
- }
- }
- break;
-
- case 408: // Get status in JSON format
- {
- int type = gb.Seen('S') ? gb.GetIValue() : 0;
- int seq = gb.Seen('R') ? gb.GetIValue() : -1;
-
- OutputBuffer *statusResponse = nullptr;
- switch (type)
- {
- case 0:
- case 1:
- statusResponse = reprap.GetLegacyStatusResponse(type + 2, seq);
- break;
-
- case 2:
- case 3:
- case 4:
- statusResponse = reprap.GetStatusResponse(type - 1, (&gb == auxGCode) ? ResponseSource::AUX : ResponseSource::Generic);
- break;
-
- case 5:
- statusResponse = reprap.GetConfigResponse();
- break;
- }
-
- if (statusResponse != nullptr)
- {
- UnlockAll(gb);
- statusResponse->cat('\n');
- HandleReply(gb, false, statusResponse);
- return true;
- }
- }
- break;
-
- case 500: // Store parameters in EEPROM
- reprap.GetPlatform()->WriteNvData();
- break;
-
- case 501: // Load parameters from EEPROM
- reprap.GetPlatform()->ReadNvData();
- if (gb.Seen('S'))
- {
- reprap.GetPlatform()->SetAutoSave(gb.GetIValue() > 0);
- }
- break;
-
- case 502: // Revert to default "factory settings"
- reprap.GetPlatform()->ResetNvData();
- break;
-
- case 503: // List variable settings
- {
- if (!LockFileSystem(gb))
- {
- return false;
- }
-
- // Need a valid output buffer to continue...
- OutputBuffer *configResponse;
- if (!OutputBuffer::Allocate(configResponse))
- {
- // No buffer available, try again later
- return false;
- }
-
- // Read the entire file
- FileStore * const f = platform->GetFileStore(platform->GetSysDir(), platform->GetConfigFile(), false);
- if (f == nullptr)
- {
- error = true;
- reply.copy("Configuration file not found!");
- }
- else
- {
- char fileBuffer[FILE_BUFFER_SIZE];
- size_t bytesRead, bytesLeftForWriting = OutputBuffer::GetBytesLeft(configResponse);
- while ((bytesRead = f->Read(fileBuffer, FILE_BUFFER_SIZE)) > 0 && bytesLeftForWriting > 0)
- {
- // Don't write more data than we can process
- if (bytesRead < bytesLeftForWriting)
- {
- bytesLeftForWriting -= bytesRead;
- }
- else
- {
- bytesRead = bytesLeftForWriting;
- bytesLeftForWriting = 0;
- }
-
- // Write it
- configResponse->cat(fileBuffer, bytesRead);
- }
- f->Close();
-
- UnlockAll(gb);
- HandleReply(gb, false, configResponse);
- return true;
- }
- }
- break;
-
- case 540: // Set/report MAC address
- if (gb.Seen('P'))
- {
- SetMACAddress(gb);
- }
- else
- {
- const byte* mac = platform->MACAddress();
- reply.printf("MAC: %x:%x:%x:%x:%x:%x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
- }
- break;
-
- case 550: // Set/report machine name
- if (gb.Seen('P'))
- {
- reprap.SetName(gb.GetString());
- }
- else
- {
- reply.printf("RepRap name: %s", reprap.GetName());
- }
- break;
-
- case 551: // Set password (no option to report it)
- if (gb.Seen('P'))
- {
- reprap.SetPassword(gb.GetString());
- }
- break;
-
- case 552: // Enable/Disable network and/or Set/Get IP address
- {
- bool seen = false;
- if (gb.Seen('P'))
- {
- seen = true;
- SetEthernetAddress(gb, code);
- }
-
- if (gb.Seen('R'))
- {
- reprap.GetNetwork()->SetHttpPort(gb.GetIValue());
- seen = true;
- }
-
- // Process this one last in case the IP address is changed and the network enabled in the same command
- if (gb.Seen('S')) // Has the user turned the network on or off?
- {
- seen = true;
- if (gb.GetIValue() != 0)
- {
- reprap.GetNetwork()->Enable();
- }
- else
- {
- reprap.GetNetwork()->Disable();
- }
- }
-
- if (!seen)
- {
- const byte *config_ip = platform->IPAddress();
- const byte *actual_ip = reprap.GetNetwork()->IPAddress();
- reply.printf("Network is %s, configured IP address: %d.%d.%d.%d, actual IP address: %d.%d.%d.%d, HTTP port: %d",
- reprap.GetNetwork()->IsEnabled() ? "enabled" : "disabled",
- config_ip[0], config_ip[1], config_ip[2], config_ip[3], actual_ip[0], actual_ip[1], actual_ip[2], actual_ip[3],
- reprap.GetNetwork()->GetHttpPort());
- }
- }
- break;
-
- case 553: // Set/Get netmask
- if (gb.Seen('P'))
- {
- SetEthernetAddress(gb, code);
- }
- else
- {
- const byte *nm = platform->NetMask();
- reply.printf("Net mask: %d.%d.%d.%d ", nm[0], nm[1], nm[2], nm[3]);
- }
- break;
-
- case 554: // Set/Get gateway
- if (gb.Seen('P'))
- {
- SetEthernetAddress(gb, code);
- }
- else
- {
- const byte *gw = platform->GateWay();
- reply.printf("Gateway: %d.%d.%d.%d ", gw[0], gw[1], gw[2], gw[3]);
- }
- break;
-
- case 555: // Set/report firmware type to emulate
- if (gb.Seen('P'))
- {
- platform->SetEmulating((Compatibility) gb.GetIValue());
- }
- else
- {
- reply.copy("Emulating ");
- switch (platform->Emulating())
- {
- case me:
- case reprapFirmware:
- reply.cat("RepRap Firmware (i.e. in native mode)");
- break;
-
- case marlin:
- reply.cat("Marlin");
- break;
-
- case teacup:
- reply.cat("Teacup");
- break;
-
- case sprinter:
- reply.cat("Sprinter");
- break;
-
- case repetier:
- reply.cat("Repetier");
- break;
-
- default:
- reply.catf("Unknown: (%d)", platform->Emulating());
- }
- }
- break;
-
- case 556: // Axis compensation (we support only X, Y, Z)
- if (gb.Seen('S'))
- {
- float value = gb.GetFValue();
- for (size_t axis = 0; axis <= Z_AXIS; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- reprap.GetMove()->SetAxisCompensation(axis, gb.GetFValue() / value);
- }
- }
- }
- else
- {
- reply.printf("Axis compensations - XY: %.5f, YZ: %.5f, ZX: %.5f",
- reprap.GetMove()->AxisCompensation(X_AXIS), reprap.GetMove()->AxisCompensation(Y_AXIS),
- reprap.GetMove()->AxisCompensation(Z_AXIS));
- }
- break;
-
- case 557: // Set/report Z probe point coordinates
- if (gb.Seen('P'))
- {
- int point = gb.GetIValue();
- if (point < 0 || (unsigned int)point >= MaxProbePoints)
- {
- reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Z probe point index out of range.\n");
- }
- else
- {
- bool seen = false;
- if (gb.Seen(axisLetters[X_AXIS]))
- {
- reprap.GetMove()->SetXBedProbePoint(point, gb.GetFValue());
- seen = true;
- }
- if (gb.Seen(axisLetters[Y_AXIS]))
- {
- reprap.GetMove()->SetYBedProbePoint(point, gb.GetFValue());
- seen = true;
- }
-
- if (!seen)
- {
- reply.printf("Probe point %d - [%.1f, %.1f]", point, reprap.GetMove()->XBedProbePoint(point), reprap.GetMove()->YBedProbePoint(point));
- }
- }
- }
- else
- {
- LockMovement(gb); // to ensure that probing is not already in progress
- error = DefineGrid(gb, reply);
- }
- break;
-
- case 558: // Set or report Z probe type and for which axes it is used
- {
- bool seenAxes = false, seenType = false, seenParam = false;
- uint32_t zProbeAxes = platform->GetZProbeAxes();
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- if (gb.GetIValue() > 0)
- {
- zProbeAxes |= (1u << axis);
- }
- else
- {
- zProbeAxes &= ~(1u << axis);
- }
- seenAxes = true;
- }
- }
- if (seenAxes)
- {
- platform->SetZProbeAxes(zProbeAxes);
- }
-
- // We must get and set the Z probe type first before setting the dive height etc., because different probe types may have different parameters
- if (gb.Seen('P')) // probe type
- {
- platform->SetZProbeType(gb.GetIValue());
- seenType = true;
- }
-
- ZProbeParameters params = platform->GetZProbeParameters();
- gb.TryGetFValue('H', params.diveHeight, seenParam); // dive height
-
- if (gb.Seen('F')) // feed rate i.e. probing speed
- {
- params.probeSpeed = gb.GetFValue() * secondsToMinutes;
- seenParam = true;
- }
-
- if (gb.Seen('T')) // travel speed to probe point
- {
- params.travelSpeed = gb.GetFValue() * secondsToMinutes;
- seenParam = true;
- }
-
- if (gb.Seen('I'))
- {
- params.invertReading = (gb.GetIValue() != 0);
- seenParam = true;
- }
-
- gb.TryGetFValue('S', params.param1, seenParam); // extra parameter for experimentation
- gb.TryGetFValue('R', params.param2, seenParam); // extra parameter for experimentation
-
- if (seenParam)
- {
- platform->SetZProbeParameters(params);
- }
-
- if (!(seenAxes || seenType || seenParam))
- {
- reply.printf("Z Probe type %d, invert %s, dive height %.1fmm, probe speed %dmm/min, travel speed %dmm/min",
- platform->GetZProbeType(), (params.invertReading) ? "yes" : "no", params.diveHeight,
- (int)(params.probeSpeed * minutesToSeconds), (int)(params.travelSpeed * minutesToSeconds));
- if (platform->GetZProbeType() == ZProbeTypeDelta)
- {
- reply.catf(", parameters %.2f %.2f", params.param1, params.param2);
- }
- reply.cat(", used for axes:");
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if ((zProbeAxes & (1u << axis)) != 0)
- {
- reply.catf(" %c", axisLetters[axis]);
- }
- }
- }
- }
- break;
-
- case 559: // Upload config.g or another gcode file to put in the sys directory
- {
- const char* str = (gb.Seen('P') ? gb.GetString() : platform->GetConfigFile());
- bool ok = OpenFileToWrite(gb, platform->GetSysDir(), str);
- if (ok)
- {
- reply.printf("Writing to file: %s", str);
- }
- else
- {
- reply.printf("Can't open file %s for writing.", str);
- error = true;
- }
- }
- break;
-
- case 560: // Upload reprap.htm or another web interface file
- {
- const char* str = (gb.Seen('P') ? gb.GetString() : INDEX_PAGE_FILE);
- bool ok = OpenFileToWrite(gb, platform->GetWebDir(), str);
- if (ok)
- {
- reply.printf("Writing to file: %s", str);
- }
- else
- {
- reply.printf("Can't open file %s for writing.", str);
- error = true;
- }
- }
- break;
-
- case 561:
- reprap.GetMove()->SetIdentityTransform();
- break;
-
- case 562: // Reset temperature fault - use with great caution
- if (gb.Seen('P'))
- {
- int heater = gb.GetIValue();
- if (heater >= 0 && heater < HEATERS)
- {
- reprap.ClearTemperatureFault(heater);
- }
- else
- {
- reply.copy("Invalid heater number.\n");
- error = true;
- }
- }
- break;
-
- case 563: // Define tool
- ManageTool(gb, reply);
- break;
-
- case 564: // Think outside the box?
- if (gb.Seen('S'))
- {
- limitAxes = (gb.GetIValue() != 0);
- }
- else
- {
- reply.printf("Movement outside the bed is %spermitted", (limitAxes) ? "not " : "");
- }
- break;
-
- case 566: // Set/print maximum jerk speeds
- {
- bool seen = false;
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- platform->SetInstantDv(axis, gb.GetFValue() * distanceScale * secondsToMinutes); // G Code feedrates are in mm/minute; we need mm/sec
- seen = true;
- }
- }
-
- if (gb.Seen(extrudeLetter))
- {
- seen = true;
- float eVals[MaxExtruders];
- size_t eCount = numExtruders;
- gb.GetFloatArray(eVals, eCount, true);
- for (size_t e = 0; e < eCount; e++)
- {
- platform->SetInstantDv(numAxes + e, eVals[e] * distanceScale * secondsToMinutes);
- }
- }
- else if (!seen)
- {
- reply.copy("Maximum jerk rates: ");
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- reply.catf("%c: %.1f, ", axisLetters[axis], platform->ConfiguredInstantDv(axis) / (distanceScale * secondsToMinutes));
- }
- reply.cat("E:");
- char sep = ' ';
- for (size_t extruder = 0; extruder < numExtruders; extruder++)
- {
- reply.catf("%c%.1f", sep, platform->ConfiguredInstantDv(extruder + numAxes) / (distanceScale * secondsToMinutes));
- sep = ':';
- }
- }
- }
- break;
-
- case 567: // Set/report tool mix ratios
- if (gb.Seen('P'))
- {
- int8_t tNumber = gb.GetIValue();
- Tool* tool = reprap.GetTool(tNumber);
- if (tool != NULL)
- {
- if (gb.Seen(extrudeLetter))
- {
- float eVals[MaxExtruders];
- size_t eCount = tool->DriveCount();
- gb.GetFloatArray(eVals, eCount, false);
- if (eCount != tool->DriveCount())
- {
- reply.printf("Setting mix ratios - wrong number of E drives: %s", gb.Buffer());
- }
- else
- {
- tool->DefineMix(eVals);
- }
- }
- else
- {
- reply.printf("Tool %d mix ratios:", tNumber);
- char sep = ' ';
- for (size_t drive = 0; drive < tool->DriveCount(); drive++)
- {
- reply.catf("%c%.3f", sep, tool->GetMix()[drive]);
- sep = ':';
- }
- }
- }
- }
- break;
-
- case 568: // Turn on/off automatic tool mixing
- if (gb.Seen('P'))
- {
- Tool* tool = reprap.GetTool(gb.GetIValue());
- if (tool != NULL)
- {
- if (gb.Seen('S'))
- {
- tool->SetMixing(gb.GetIValue() != 0);
- }
- else
- {
- reply.printf("Tool %d mixing is %s", tool->Number(), (tool->GetMixing()) ? "enabled" : "disabled");
- }
- }
- }
- break;
-
- case 569: // Set/report axis direction
- if (gb.Seen('P'))
- {
- size_t drive = gb.GetIValue();
- if (drive < DRIVES)
- {
- bool seen = false;
- if (gb.Seen('S'))
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- platform->SetDirectionValue(drive, gb.GetIValue() != 0);
- seen = true;
- }
- if (gb.Seen('R'))
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- platform->SetEnableValue(drive, gb.GetIValue() != 0);
- seen = true;
- }
- if (gb.Seen('T'))
- {
- platform->SetDriverStepTiming(drive, gb.GetFValue());
- seen = true;
- }
- bool badParameter = false;
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- badParameter = true;
- }
- }
- if (gb.Seen(extrudeLetter))
- {
- badParameter = true;
- }
- if (badParameter)
- {
- platform->Message(GENERIC_MESSAGE, "Error: M569 no longer accepts XYZE parameters; use M584 instead\n");
- }
- else if (!seen)
- {
- reply.printf("A %d sends drive %u forwards, a %d enables it, and the minimum pulse width is %.1f microseconds",
- (int)platform->GetDirectionValue(drive), drive,
- (int)platform->GetEnableValue(drive),
- platform->GetDriverStepTiming(drive));
- }
- }
- }
- break;
-
- case 570: // Set/report heater timeout
- if (gb.Seen('H'))
- {
- const size_t heater = gb.GetIValue();
- bool seen = false;
- if (heater < HEATERS)
- {
- float maxTempExcursion, maxFaultTime;
- reprap.GetHeat()->GetHeaterProtection(heater, maxTempExcursion, maxFaultTime);
- gb.TryGetFValue('P', maxFaultTime, seen);
- gb.TryGetFValue('T', maxTempExcursion, seen);
- if (seen)
- {
- reprap.GetHeat()->SetHeaterProtection(heater, maxTempExcursion, maxFaultTime);
- }
- else
- {
- reply.printf("Heater %u allowed excursion %.1fC, fault trigger time %.1f seconds", heater, maxTempExcursion, maxFaultTime);
- }
- }
- }
- else if (gb.Seen('S'))
- {
- reply.copy("M570 S parameter is no longer required or supported");
- }
- break;
-
- case 571: // Set output on extrude
- if (gb.Seen('S'))
- {
- platform->SetExtrusionAncilliaryPWM(gb.GetFValue());
- }
- else
- {
- reply.printf("Extrusion ancillary PWM: %.3f.", platform->GetExtrusionAncilliaryPWM());
- }
- break;
-
- case 572: // Set/report elastic compensation
- if (gb.Seen('D'))
- {
- // New usage: specify the extruder drive using the D parameter
- size_t extruder = gb.GetIValue();
- if (gb.Seen('S'))
- {
- platform->SetPressureAdvance(extruder, gb.GetFValue());
- }
- else
- {
- reply.printf("Pressure advance for extruder %u is %.3f seconds", extruder, platform->GetPressureAdvance(extruder));
- }
- }
- break;
-
- case 573: // Report heater average PWM
- if (gb.Seen('P'))
- {
- int heater = gb.GetIValue();
- if (heater >= 0 && heater < HEATERS)
- {
- reply.printf("Average heater %d PWM: %.3f", heater, reprap.GetHeat()->GetAveragePWM(heater));
- }
- }
- break;
-
- case 574: // Set endstop configuration
- {
- bool seen = false;
- bool logicLevel = (gb.Seen('S')) ? (gb.GetIValue() != 0) : true;
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- int ival = gb.GetIValue();
- if (ival >= 0 && ival <= 3)
- {
- platform->SetEndStopConfiguration(axis, (EndStopType) ival, logicLevel);
- seen = true;
- }
- }
- }
- if (!seen)
- {
- reply.copy("Endstop configuration:");
- EndStopType config;
- bool logic;
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- platform->GetEndStopConfiguration(axis, config, logic);
- reply.catf(" %c %s (active %s),", axisLetters[axis],
- (config == EndStopType::highEndStop) ? "high end" : (config == EndStopType::lowEndStop) ? "low end" : "none",
- (config == EndStopType::noEndStop) ? "" : (logic) ? "high" : "low");
- }
- }
- }
- break;
-
- case 575: // Set communications parameters
- if (gb.Seen('P'))
- {
- size_t chan = gb.GetIValue();
- if (chan < NUM_SERIAL_CHANNELS)
- {
- bool seen = false;
- if (gb.Seen('B'))
- {
- platform->SetBaudRate(chan, gb.GetIValue());
- seen = true;
- }
- if (gb.Seen('S'))
- {
- uint32_t val = gb.GetIValue();
- platform->SetCommsProperties(chan, val);
- switch (chan)
- {
- case 0:
- serialGCode->SetCommsProperties(val);
- break;
- case 1:
- auxGCode->SetCommsProperties(val);
- break;
- default:
- break;
- }
- seen = true;
- }
- if (!seen)
- {
- uint32_t cp = platform->GetCommsProperties(chan);
- reply.printf("Channel %d: baud rate %d, %s checksum", chan, platform->GetBaudRate(chan),
- (cp & 1) ? "requires" : "does not require");
- }
- }
- }
- break;
-
- case 577: // Wait until endstop is triggered
- if (gb.Seen('S'))
- {
- // Determine trigger type
- EndStopHit triggerCondition;
- switch (gb.GetIValue())
- {
- case 1:
- triggerCondition = EndStopHit::lowHit;
- break;
- case 2:
- triggerCondition = EndStopHit::highHit;
- break;
- case 3:
- triggerCondition = EndStopHit::lowNear;
- break;
- default:
- triggerCondition = EndStopHit::noStop;
- break;
- }
-
- // Axis endstops
- for (size_t axis=0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- if (platform->Stopped(axis) != triggerCondition)
- {
- result = false;
- break;
- }
- }
- }
-
- // Extruder drives
- size_t eDriveCount = MaxExtruders;
- long eDrives[MaxExtruders];
- if (gb.Seen(extrudeLetter))
- {
- gb.GetLongArray(eDrives, eDriveCount);
- for(size_t extruder = 0; extruder < eDriveCount; extruder++)
- {
- const size_t eDrive = eDrives[extruder];
- if (eDrive >= MaxExtruders)
- {
- reply.copy("Invalid extruder drive specified!");
- error = result = true;
- break;
- }
-
- if (platform->Stopped(eDrive + E0_AXIS) != triggerCondition)
- {
- result = false;
- break;
- }
- }
- }
- }
- break;
-
-#if SUPPORT_INKJET
- case 578: // Fire Inkjet bits
- if (!AllMovesAreFinishedAndMoveBufferIsLoaded())
- {
- return false;
- }
-
- if (gb.Seen('S')) // Need to handle the 'P' parameter too; see http://reprap.org/wiki/G-code#M578:_Fire_inkjet_bits
- {
- platform->Inkjet(gb.GetIValue());
- }
- break;
-#endif
-
- case 579: // Scale Cartesian axes (mostly for Delta configurations)
- {
- bool seen = false;
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- gb.TryGetFValue(axisLetters[axis], axisScaleFactors[axis], seen);
- }
-
- if (!seen)
- {
- char sep = ':';
- reply.copy("Axis scale factors");
- for(size_t axis = 0; axis < numAxes; axis++)
- {
- reply.catf("%c %c: %.3f", sep, axisLetters[axis], axisScaleFactors[axis]);
- sep = ',';
- }
- }
- }
- break;
-
-#if SUPPORT_ROLAND
- case 580: // (De)Select Roland mill
- if (gb.Seen('R'))
- {
- if (gb.GetIValue())
- {
- reprap.GetRoland()->Activate();
- if (gb.Seen('P'))
- {
- result = reprap.GetRoland()->RawWrite(gb.GetString());
- }
- }
- else
- {
- result = reprap.GetRoland()->Deactivate();
- }
- }
- else
- {
- reply.printf("Roland is %s.", reprap.GetRoland()->Active() ? "active" : "inactive");
- }
- break;
-#endif
-
- case 581: // Configure external trigger
- case 582: // Check external trigger
- if (gb.Seen('T'))
- {
- unsigned int triggerNumber = gb.GetIValue();
- if (triggerNumber < MaxTriggers)
- {
- if (code == 582)
- {
- uint32_t states = platform->GetAllEndstopStates();
- if ((triggers[triggerNumber].rising & states) != 0 || (triggers[triggerNumber].falling & ~states) != 0)
- {
- triggersPending |= (1u << triggerNumber);
- }
- }
- else
- {
- bool seen = false;
- if (gb.Seen('C'))
- {
- seen = true;
- triggers[triggerNumber].condition = gb.GetIValue();
- }
- else if (triggers[triggerNumber].IsUnused())
- {
- triggers[triggerNumber].condition = 0; // this is a new trigger, so set no condition
- }
- if (gb.Seen('S'))
- {
- seen = true;
- int sval = gb.GetIValue();
- TriggerMask triggerMask = 0;
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- triggerMask |= (1u << axis);
- }
- }
- if (gb.Seen(extrudeLetter))
- {
- long eStops[MaxExtruders];
- size_t numEntries = MaxExtruders;
- gb.GetLongArray(eStops, numEntries);
- for (size_t i = 0; i < numEntries; ++i)
- {
- if (eStops[i] >= 0 && (unsigned long)eStops[i] < MaxExtruders)
- {
- triggerMask |= (1u << (eStops[i] + E0_AXIS));
- }
- }
- }
- switch(sval)
- {
- case -1:
- if (triggerMask == 0)
- {
- triggers[triggerNumber].rising = triggers[triggerNumber].falling = 0;
- }
- else
- {
- triggers[triggerNumber].rising &= (~triggerMask);
- triggers[triggerNumber].falling &= (~triggerMask);
- }
- break;
-
- case 0:
- triggers[triggerNumber].falling |= triggerMask;
- break;
-
- case 1:
- triggers[triggerNumber].rising |= triggerMask;
- break;
-
- default:
- platform->Message(GENERIC_MESSAGE, "Bad S parameter in M581 command\n");
- }
- }
- if (!seen)
- {
- reply.printf("Trigger %u fires on a rising edge on ", triggerNumber);
- ListTriggers(reply, triggers[triggerNumber].rising);
- reply.cat(" or a falling edge on ");
- ListTriggers(reply, triggers[triggerNumber].falling);
- reply.cat(" endstop inputs");
- if (triggers[triggerNumber].condition == 1)
- {
- reply.cat(" when printing from SD card");
- }
- }
- }
- }
- else
- {
- platform->Message(GENERIC_MESSAGE, "Trigger number out of range\n");
- }
- }
- break;
-
- case 584: // Set axis/extruder to stepper driver(s) mapping
- if (!LockMovementAndWaitForStandstill(gb)) // we also rely on this to retrieve the current motor positions to moveBuffer
- {
- return false;
- }
- {
- bool seen = false, badDrive = false;
- for (size_t drive = 0; drive < MAX_AXES; ++drive)
- {
- if (gb.Seen(axisLetters[drive]))
- {
- seen = true;
- size_t numValues = MaxDriversPerAxis;
- long drivers[MaxDriversPerAxis];
- gb.GetLongArray(drivers, numValues);
-
- // Check all the driver numbers are in range
- bool badAxis = false;
- AxisDriversConfig config;
- config.numDrivers = numValues;
- for (size_t i = 0; i < numValues; ++i)
- {
- if ((unsigned long)drivers[i] >= DRIVES)
- {
- badAxis = true;
- }
- else
- {
- config.driverNumbers[i] = (uint8_t)drivers[i];
- }
- }
- if (badAxis)
- {
- badDrive = true;
- }
- else
- {
- while (numAxes <= drive)
- {
- moveBuffer.coords[numAxes] = 0.0; // user has defined a new axis, so set its position
- ++numAxes;
- }
- SetPositions(moveBuffer.coords); // tell the Move system where any new axes are
- platform->SetAxisDriversConfig(drive, config);
- if (numAxes + numExtruders > DRIVES)
- {
- numExtruders = DRIVES - numAxes; // if we added axes, we may have fewer extruders now
- }
- }
- }
- }
-
- if (gb.Seen(extrudeLetter))
- {
- seen = true;
- size_t numValues = DRIVES - numAxes;
- long drivers[MaxExtruders];
- gb.GetLongArray(drivers, numValues);
- numExtruders = numValues;
- for (size_t i = 0; i < numValues; ++i)
- {
- if ((unsigned long)drivers[i] >= DRIVES)
- {
- badDrive = true;
- }
- else
- {
- platform->SetExtruderDriver(i, (uint8_t)drivers[i]);
- }
- }
- }
-
- if (badDrive)
- {
- platform->Message(GENERIC_MESSAGE, "Error: invalid drive number in M584 command\n");
- }
- else if (!seen)
- {
- reply.copy("Driver assignments:");
- for (size_t drive = 0; drive < numAxes; ++ drive)
- {
- reply.cat(' ');
- const AxisDriversConfig& axisConfig = platform->GetAxisDriversConfig(drive);
- char c = axisLetters[drive];
- for (size_t i = 0; i < axisConfig.numDrivers; ++i)
- {
- reply.catf("%c%u", c, axisConfig.driverNumbers[i]);
- c = ':';
- }
- }
- reply.cat(' ');
- char c = extrudeLetter;
- for (size_t extruder = 0; extruder < numExtruders; ++extruder)
- {
- reply.catf("%c%u", c, platform->GetExtruderDriver(extruder));
- c = ':';
- }
- }
- }
- break;
-
- case 665: // Set delta configuration
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- {
- float positionNow[DRIVES];
- Move *move = reprap.GetMove();
- move->GetCurrentUserPosition(positionNow, 0, reprap.GetCurrentXAxes()); // get the current position, we may need it later
- DeltaParameters& params = move->AccessDeltaParams();
- bool wasInDeltaMode = params.IsDeltaMode(); // remember whether we were in delta mode
- bool seen = false;
-
- if (gb.Seen('L'))
- {
- params.SetDiagonal(gb.GetFValue() * distanceScale);
- seen = true;
- }
- if (gb.Seen('R'))
- {
- params.SetRadius(gb.GetFValue() * distanceScale);
- seen = true;
- }
- if (gb.Seen('B'))
- {
- params.SetPrintRadius(gb.GetFValue() * distanceScale);
- seen = true;
- }
- if (gb.Seen('X'))
- {
- // X tower position correction
- params.SetXCorrection(gb.GetFValue());
- seen = true;
- }
- if (gb.Seen('Y'))
- {
- // Y tower position correction
- params.SetYCorrection(gb.GetFValue());
- seen = true;
- }
- if (gb.Seen('Z'))
- {
- // Y tower position correction
- params.SetZCorrection(gb.GetFValue());
- seen = true;
- }
-
- // The homed height must be done last, because it gets recalculated when some of the other factors are changed
- if (gb.Seen('H'))
- {
- params.SetHomedHeight(gb.GetFValue() * distanceScale);
- seen = true;
- }
-
- if (seen)
- {
- move->SetCoreXYMode(0); // CoreXYMode needs to be zero when executing special moves on a delta
-
- // If we have changed between Cartesian and Delta mode, we need to reset the motor coordinates to agree with the XYZ coordinates.
- // This normally happens only when we process the M665 command in config.g. Also flag that the machine is not homed.
- if (params.IsDeltaMode() != wasInDeltaMode)
- {
- SetPositions(positionNow);
- }
- SetAllAxesNotHomed();
- }
- else
- {
- if (params.IsDeltaMode())
- {
- reply.printf("Diagonal %.3f, delta radius %.3f, homed height %.3f, bed radius %.1f"
- ", X %.3f" DEGREE_SYMBOL ", Y %.3f" DEGREE_SYMBOL ", Z %.3f" DEGREE_SYMBOL,
- params.GetDiagonal() / distanceScale, params.GetRadius() / distanceScale,
- params.GetHomedHeight() / distanceScale, params.GetPrintRadius() / distanceScale,
- params.GetXCorrection(), params.GetYCorrection(), params.GetZCorrection());
- }
- else
- {
- reply.printf("Printer is not in delta mode");
- }
- }
- }
- break;
-
- case 666: // Set delta endstop adjustments
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- {
- DeltaParameters& params = reprap.GetMove()->AccessDeltaParams();
- bool seen = false;
- if (gb.Seen('X'))
- {
- params.SetEndstopAdjustment(X_AXIS, gb.GetFValue());
- seen = true;
- }
- if (gb.Seen('Y'))
- {
- params.SetEndstopAdjustment(Y_AXIS, gb.GetFValue());
- seen = true;
- }
- if (gb.Seen('Z'))
- {
- params.SetEndstopAdjustment(Z_AXIS, gb.GetFValue());
- seen = true;
- }
- if (gb.Seen('A'))
- {
- params.SetXTilt(gb.GetFValue() * 0.01);
- seen = true;
- }
- if (gb.Seen('B'))
- {
- params.SetYTilt(gb.GetFValue() * 0.01);
- seen = true;
- }
-
- if (seen)
- {
- SetAllAxesNotHomed();
- }
- else
- {
- reply.printf("Endstop adjustments X%.2f Y%.2f Z%.2f, tilt X%.2f%% Y%.2f%%",
- params.GetEndstopAdjustment(X_AXIS), params.GetEndstopAdjustment(Y_AXIS), params.GetEndstopAdjustment(Z_AXIS),
- params.GetXTilt() * 100.0, params.GetYTilt() * 100.0);
- }
- }
- break;
-
- case 667: // Set CoreXY mode
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- {
- Move* move = reprap.GetMove();
- bool seen = false;
- float positionNow[DRIVES];
- move->GetCurrentUserPosition(positionNow, 0, reprap.GetCurrentXAxes()); // get the current position, we may need it later
- if (gb.Seen('S'))
- {
- move->SetCoreXYMode(gb.GetIValue());
- seen = true;
- }
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- move->SetCoreAxisFactor(axis, gb.GetFValue());
- seen = true;
- }
- }
-
- if (seen)
- {
- SetPositions(positionNow);
- SetAllAxesNotHomed();
- }
- else
- {
- reply.printf("Printer mode is %s with axis factors", move->GetGeometryString());
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- reply.catf(" %c:%f", axisLetters[axis], move->GetCoreAxisFactor(axis));
- }
- }
- }
- break;
-
- case 905: // Set current RTC date and time
- {
- const time_t now = platform->GetDateTime();
- struct tm * const timeInfo = gmtime(&now);
- bool seen = false;
-
- if (gb.Seen('P'))
- {
- // Set date
- const char * const dateString = gb.GetString();
- if (strptime(dateString, "%Y-%m-%d", timeInfo) != nullptr)
- {
- if (!platform->SetDate(mktime(timeInfo)))
- {
- reply.copy("Could not set date");
- error = true;
- break;
- }
- }
- else
- {
- reply.copy("Invalid date format");
- error = true;
- break;
- }
-
- seen = true;
- }
-
- if (gb.Seen('S'))
- {
- // Set time
- const char * const timeString = gb.GetString();
- if (strptime(timeString, "%H:%M:%S", timeInfo) != nullptr)
- {
- if (!platform->SetTime(mktime(timeInfo)))
- {
- reply.copy("Could not set time");
- error = true;
- break;
- }
- }
- else
- {
- reply.copy("Invalid time format");
- error = true;
- break;
- }
-
- seen = true;
- }
-
- // TODO: Add correction parameters for SAM4E
-
- if (!seen)
- {
- // Report current date and time
- reply.printf("Current date and time: %04u-%02u-%02u %02u:%02u:%02u",
- timeInfo->tm_year + 1900, timeInfo->tm_mon + 1, timeInfo->tm_mday,
- timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec);
-
- if (!platform->IsDateTimeSet())
- {
- reply.cat("\nWarning: RTC has not been configured yet!");
- }
- }
- }
- break;
-
- case 906: // Set/report Motor currents
- case 913: // Set/report motor current percent
- {
- bool seen = false;
- for (size_t axis = 0; axis < numAxes; axis++)
- {
- if (gb.Seen(axisLetters[axis]))
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- platform->SetMotorCurrent(axis, gb.GetFValue(), code == 913);
- seen = true;
- }
- }
-
- if (gb.Seen(extrudeLetter))
- {
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
-
- float eVals[MaxExtruders];
- size_t eCount = numExtruders;
- gb.GetFloatArray(eVals, eCount, true);
- // 2014-09-29 DC42: we no longer insist that the user supplies values for all possible extruder drives
- for (size_t e = 0; e < eCount; e++)
- {
- platform->SetMotorCurrent(numAxes + e, eVals[e], code == 913);
- }
- seen = true;
- }
-
- if (code == 906 && gb.Seen('I'))
- {
- float idleFactor = gb.GetFValue();
- if (idleFactor >= 0 && idleFactor <= 100.0)
- {
- platform->SetIdleCurrentFactor(idleFactor/100.0);
- seen = true;
- }
- }
-
- if (!seen)
- {
- reply.copy((code == 913) ? "Motor current % of normal - " : "Motor current (mA) - ");
- for (size_t axis = 0; axis < numAxes; ++axis)
- {
- reply.catf("%c:%d, ", axisLetters[axis], (int)platform->GetMotorCurrent(axis, code == 913));
- }
- reply.cat("E");
- for (size_t extruder = 0; extruder < numExtruders; extruder++)
- {
- reply.catf(":%d", (int)platform->GetMotorCurrent(extruder + numAxes, code == 913));
- }
- if (code == 906)
- {
- reply.catf(", idle factor %d%%", (int)(platform->GetIdleCurrentFactor() * 100.0));
- }
- }
- }
- break;
-
- case 911: // Set power monitor threshold voltages
- reply.printf("M911 not implemented yet");
- break;
-
- case 912: // Set electronics temperature monitor adjustment
- // Currently we ignore the P parameter (i.e. temperature measurement channel)
- if (gb.Seen('S'))
- {
- platform->SetMcuTemperatureAdjust(gb.GetFValue());
- }
- else
- {
- reply.printf("MCU temperature calibration adjustment is %.1f" DEGREE_SYMBOL "C", platform->GetMcuTemperatureAdjust());
- }
- break;
-
- // For case 913, see 906
-
- case 997: // Perform firmware update
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
- reprap.GetHeat()->SwitchOffAll(); // turn all heaters off because the main loop may get suspended
- DisableDrives(); // all motors off
-
- if (firmwareUpdateModuleMap == 0) // have we worked out which modules to update?
- {
- // Find out which modules we have been asked to update
- if (gb.Seen('S'))
- {
- long modulesToUpdate[3];
- size_t numUpdateModules = ARRAY_SIZE(modulesToUpdate);
- gb.GetLongArray(modulesToUpdate, numUpdateModules);
- for (size_t i = 0; i < numUpdateModules; ++i)
- {
- long t = modulesToUpdate[i];
- if (t < 0 || (unsigned long)t >= NumFirmwareUpdateModules)
- {
- platform->MessageF(GENERIC_MESSAGE, "Invalid module number '%ld'\n", t);
- firmwareUpdateModuleMap = 0;
- break;
- }
- firmwareUpdateModuleMap |= (1u << (unsigned int)t);
- }
- }
- else
- {
- firmwareUpdateModuleMap = (1u << 0); // no modules specified, so update module 0 to match old behaviour
- }
-
- if (firmwareUpdateModuleMap == 0)
- {
- break; // nothing to update
- }
-
- // Check prerequisites of all modules to be updated, if any are not met then don't update any of them
-#ifdef DUET_NG
- if (!FirmwareUpdater::CheckFirmwareUpdatePrerequisites(firmwareUpdateModuleMap))
- {
- firmwareUpdateModuleMap = 0;
- break;
- }
-#endif
- if ((firmwareUpdateModuleMap & 1) != 0 && !platform->CheckFirmwareUpdatePrerequisites())
- {
- firmwareUpdateModuleMap = 0;
- break;
- }
- }
-
- // If we get here then we have the module map, and all prerequisites are satisfied
- isFlashing = true; // this tells the web interface and PanelDue that we are about to flash firmware
- if (!DoDwellTime(1.0)) // wait a second so all HTTP clients and PanelDue are notified
- {
- return false;
- }
-
- gb.SetState(GCodeState::flashing1);
- break;
-
- case 998:
- // The input handling code replaces the gcode by this when it detects a checksum error.
- // Since we have no way of asking for the line to be re-sent, just report an error.
- if (gb.Seen('P'))
- {
- int val = gb.GetIValue();
- if (val != 0)
- {
- reply.printf("Checksum error on line %d", val);
- }
- }
- break;
-
- case 999:
- result = DoDwellTime(0.5); // wait half a second to allow the response to be sent back to the web server, otherwise it may retry
- if (result)
- {
- reprap.EmergencyStop(); // this disables heaters and drives - Duet WiFi pre-production boards need drives disabled here
- uint16_t reason = (gb.Seen('P') && StringStartsWith(gb.GetString(), "ERASE"))
- ? (uint16_t)SoftwareResetReason::erase
- : (uint16_t)SoftwareResetReason::user;
- platform->SoftwareReset(reason); // doesn't return
- }
- break;
-
- default:
- error = true;
- reply.printf("unsupported command: %s", gb.Buffer());
- }
-
- if (result && gb.GetState() == GCodeState::normal)
- {
- UnlockAll(gb);
- HandleReply(gb, error, reply.Pointer());
- }
- return result;
-}
-
-bool GCodes::HandleTcode(GCodeBuffer& gb, StringRef& reply)
-{
- if (!LockMovementAndWaitForStandstill(gb))
- {
- return false;
- }
-
- newToolNumber = gb.GetIValue();
- newToolNumber += gb.GetToolNumberAdjust();
-
- // TODO for the tool change restore point to be useful, we should undo any X axis mapping and remove any tool offsets
- for (size_t drive = 0; drive < DRIVES; ++drive)
- {
- toolChangeRestorePoint.moveCoords[drive] = moveBuffer.coords[drive];
- }
- toolChangeRestorePoint.feedRate = feedRate;
-
- if (simulationMode == 0) // we don't yet simulate any T codes
- {
- const Tool * const oldTool = reprap.GetCurrentTool();
- // If old and new are the same we no longer follow the sequence. User can deselect and then reselect the tool if he wants the macros run.
- if (oldTool->Number() != newToolNumber)
- {
- gb.SetState(GCodeState::toolChange1);
- if (oldTool != nullptr && AllAxesAreHomed())
- {
- scratchString.printf("tfree%d.g", oldTool->Number());
- DoFileMacro(gb, scratchString.Pointer(), false);
- }
- return true; // proceeding with state machine, so don't unlock or send a reply
- }
- }
-
- // If we get here, we have finished
- UnlockAll(gb);
- HandleReply(gb, false, "");
- return true;
-}
-
-// Return the amount of filament extruded
-float GCodes::GetRawExtruderPosition(size_t extruder) const
-{
- return (extruder < numExtruders) ? lastRawExtruderPosition[extruder] : 0.0;
-}
-
-float GCodes::GetRawExtruderTotalByDrive(size_t extruder) const
-{
- return (extruder < numExtruders) ? rawExtruderTotalByDrive[extruder] : 0.0;
-}
-
-// Cancel the current SD card print.
-// This is called from Pid.cpp when there is a heater fault, and from elsewhere in this module.
-void GCodes::CancelPrint()
-{
- moveAvailable = false;
- isPaused = false;
-
- fileGCode->Init();
- FileData& fileBeingPrinted = fileGCode->OriginalMachineState().fileState;
- if (fileBeingPrinted.IsLive())
- {
- fileBeingPrinted.Close();
- }
-
- reprap.GetPrintMonitor()->StoppedPrint();
-}
-
-// Return true if all the heaters for the specified tool are at their set temperatures
-bool GCodes::ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling) const
-{
- if (tool != NULL)
- {
- for (size_t i = 0; i < tool->HeaterCount(); ++i)
- {
- if (!reprap.GetHeat()->HeaterAtSetTemperature(tool->Heater(i), waitWhenCooling))
- {
- return false;
- }
- }
- }
- return true;
-}
-
-// Set the current position
-void GCodes::SetPositions(float positionNow[DRIVES])
-{
- // Transform the position so that e.g. if the user does G92 Z0,
- // the position we report (which gets inverse-transformed) really is Z=0 afterwards
- reprap.GetMove()->Transform(positionNow, reprap.GetCurrentXAxes());
- reprap.GetMove()->SetLiveCoordinates(positionNow);
- reprap.GetMove()->SetPositions(positionNow);
-}
-
-bool GCodes::IsPaused() const
-{
- return isPaused && !IsPausing() && !IsResuming();
-}
-
-bool GCodes::IsPausing() const
-{
- const GCodeState topState = fileGCode->OriginalMachineState().state;
- return topState == GCodeState::pausing1 || topState == GCodeState::pausing2;
-}
-
-bool GCodes::IsResuming() const
-{
- const GCodeState topState = fileGCode->OriginalMachineState().state;
- return topState == GCodeState::resuming1 || topState == GCodeState::resuming2 || topState == GCodeState::resuming3;
-}
-
-bool GCodes::IsRunning() const
-{
- return !IsPaused() && !IsPausing() && !IsResuming();
-}
-
-const char *GCodes::TranslateEndStopResult(EndStopHit es)
-{
- switch (es)
- {
- case EndStopHit::lowHit:
- return "at min stop";
-
- case EndStopHit::highHit:
- return "at max stop";
-
- case EndStopHit::lowNear:
- return "near min stop";
-
- case EndStopHit::noStop:
- default:
- return "not stopped";
- }
-}
-
-// Append a list of trigger endstops to a message
-void GCodes::ListTriggers(StringRef reply, TriggerMask mask)
-{
- if (mask == 0)
- {
- reply.cat("(none)");
- }
- else
- {
- bool printed = false;
- for (unsigned int i = 0; i < DRIVES; ++i)
- {
- if ((mask & (1u << i)) != 0)
- {
- if (printed)
- {
- reply.cat(' ');
- }
- if (i < numAxes)
- {
- reply.cat(axisLetters[i]);
- }
- else
- {
- reply.catf("E%d", i - numAxes);
- }
- printed = true;
- }
- }
- }
-}
-
-// M38 (SHA1 hash of a file) implementation:
-bool GCodes::StartHash(const char* filename)
-{
- // Get a FileStore object
- fileBeingHashed = platform->GetFileStore(FS_PREFIX, filename, false);
- if (fileBeingHashed == nullptr)
- {
- return false;
- }
-
- // Start hashing
- SHA1Reset(&hash);
- return true;
-}
-
-bool GCodes::AdvanceHash(StringRef &reply)
-{
- // Read and process some more data from the file
- uint32_t buf32[(FILE_BUFFER_SIZE + 3) / 4];
- char *buffer = reinterpret_cast<char *>(buf32);
-
- int bytesRead = fileBeingHashed->Read(buffer, FILE_BUFFER_SIZE);
- if (bytesRead != -1)
- {
- SHA1Input(&hash, reinterpret_cast<const uint8_t *>(buffer), bytesRead);
-
- if (bytesRead != FILE_BUFFER_SIZE)
- {
- // Calculate and report the final result
- SHA1Result(&hash);
- for(size_t i = 0; i < 5; i++)
- {
- reply.catf("%x", hash.Message_Digest[i]);
- }
-
- // Clean up again
- fileBeingHashed->Close();
- fileBeingHashed = nullptr;
- return true;
- }
- return false;
- }
-
- // Something went wrong, we cannot read any more from the file
- fileBeingHashed->Close();
- fileBeingHashed = nullptr;
- return true;
-}
-
-bool GCodes::AllAxesAreHomed() const
-{
- const uint32_t allAxes = (1u << numAxes) - 1;
- return (axesHomed & allAxes) == allAxes;
-}
-
-void GCodes::SetAllAxesNotHomed()
-{
- axesHomed = 0;
-}
-
-// Resource locking/unlocking
-
-// Lock the resource, returning true if success
-bool GCodes::LockResource(const GCodeBuffer& gb, Resource r)
-{
- if (resourceOwners[r] == &gb)
- {
- return true;
- }
- if (resourceOwners[r] == nullptr)
- {
- resourceOwners[r] = &gb;
- gb.MachineState().lockedResources |= (1 << r);
- return true;
- }
- return false;
-}
-
-bool GCodes::LockHeater(const GCodeBuffer& gb, int heater)
-{
- if (heater >= 0 && heater < HEATERS)
- {
- return LockResource(gb, HeaterResourceBase + heater);
- }
- return true;
-}
-
-bool GCodes::LockFan(const GCodeBuffer& gb, int fan)
-{
- if (fan >= 0 && fan < (int)NUM_FANS)
- {
- return LockResource(gb, FanResourceBase + fan);
- }
- return true;
-}
-
-// Lock the unshareable parts of the file system
-bool GCodes::LockFileSystem(const GCodeBuffer &gb)
-{
- return LockResource(gb, FileSystemResource);
-}
-
-// Lock movement
-bool GCodes::LockMovement(const GCodeBuffer& gb)
-{
- return LockResource(gb, MoveResource);
-}
-
-// Lock movement and wait for pending moves to finish
-bool GCodes::LockMovementAndWaitForStandstill(const GCodeBuffer& gb)
-{
- bool b = LockMovement(gb);
- if (b)
- {
- b = AllMovesAreFinishedAndMoveBufferIsLoaded();
- }
- return b;
-}
-
-// Release all locks, except those that were owned when the current macro was started
-void GCodes::UnlockAll(const GCodeBuffer& gb)
-{
- const GCodeMachineState * const mc = gb.MachineState().previous;
- const uint32_t resourcesToKeep = (mc == nullptr) ? 0 : mc->lockedResources;
- for (size_t i = 0; i < NumResources; ++i)
- {
- if (resourceOwners[i] == &gb && ((1 << i) & resourcesToKeep) == 0)
- {
- resourceOwners[i] = nullptr;
- gb.MachineState().lockedResources &= ~(1 << i);
- }
- }
-}
-
-// Convert an array of longs to a bit map
-/*static*/ uint32_t GCodes::LongArrayToBitMap(const long *arr, size_t numEntries)
-{
- uint32_t res = 0;
- for (size_t i = 0; i < numEntries; ++i)
- {
- const long f = arr[i];
- if (f >= 0 && f < 32)
- {
- res |= 1u << (unsigned int)f;
- }
- }
- return res;
-}
-
-// End