diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Configuration.h | 21 | ||||
-rw-r--r-- | src/GCodes/GCodeBuffer.h | 12 | ||||
-rw-r--r-- | src/GCodes/GCodeMachineState.h | 6 | ||||
-rw-r--r-- | src/GCodes/GCodes.cpp | 333 | ||||
-rw-r--r-- | src/GCodes/GCodes.h | 5 | ||||
-rw-r--r-- | src/Movement/Grid.cpp | 216 | ||||
-rw-r--r-- | src/Movement/Grid.h | 66 | ||||
-rw-r--r-- | src/Movement/Move.cpp | 59 | ||||
-rw-r--r-- | src/Movement/Move.h | 23 | ||||
-rw-r--r-- | src/Platform.cpp | 14 | ||||
-rw-r--r-- | src/Platform.h | 1 | ||||
-rw-r--r-- | src/Storage/MassStorage.cpp | 7 |
12 files changed, 699 insertions, 64 deletions
diff --git a/src/Configuration.h b/src/Configuration.h index f3947989..40d2b9e5 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -28,11 +28,11 @@ Licence: GPL // Firmware name is now defined in the Pins file #ifndef VERSION -# define VERSION "1.17dev4" +# define VERSION "1.17dev5" #endif #ifndef DATE -# define DATE "2016-11-17" +# define DATE "2016-11-19" #endif #define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman" @@ -124,16 +124,23 @@ const unsigned int DefaultPinWritePwmFreq = 500; // default PWM frequency for M4 // The maximum number of probe points is constrained by RAM usage: // - Each probe point uses 12 bytes of static RAM. So 16 points use 192 bytes -// - The delta probe points use the same static ram, but when auto-calibrating we temporarily need another 44 bytes per probe point to hold the matrices etc. +// - The delta calibration points use the same static ram, but when auto-calibrating we temporarily need another 44 bytes per calibration point to hold the matrices etc. // So 16 points need 704 bytes of stack space. #ifdef DUET_NG -const size_t MAX_PROBE_POINTS = 64; // Maximum number of probe points -const size_t MAX_DELTA_PROBE_POINTS = 64; // Must be <= MaxProbePoints, may be smaller to reduce matrix storage requirements. Preferably a power of 2. +const size_t MaxGridProbePoints = 441; // 441 allows us to probe e.g. 400x400 at 20mm intervals +const size_t MaxProbePoints = 64; // Maximum number of probe points +const size_t MaxDeltaCalibrationPoints = 64; // Should a power of 2 for speed #else -const size_t MAX_PROBE_POINTS = 32; // Maximum number of probe points -const size_t MAX_DELTA_PROBE_POINTS = 32; // Must be <= MaxProbePoints, may be smaller to reduce matrix storage requirements. Preferably a power of 2. +const size_t MaxGridProbePoints = 121; // 121 allows us to probe 200x200 at 20mm intervals +const size_t MaxProbePoints = 32; // Maximum number of probe points +const size_t MaxDeltaCalibrationPoints = 32; // Should a power of 2 for speed #endif +const float DefaultGridSpacing = 20.0; // Default bed probing grid spacing in mm + +static_assert(MaxProbePoints <= MaxGridProbePoints, "MaxProbePoints must be <= MaxGridProbePoints"); +static_assert(MaxDeltaCalibrationPoints <= MaxProbePoints, "MaxDeltaCalibrationPoints must be <= MaxProbePoints"); + const float DEFAULT_Z_DIVE = 5.0; // Millimetres const float DEFAULT_PROBE_SPEED = 2.0; // Default Z probing speed mm/sec const float DEFAULT_TRAVEL_SPEED = 100.0; // Default speed for travel to probe points diff --git a/src/GCodes/GCodeBuffer.h b/src/GCodes/GCodeBuffer.h index 0032c081..aa8723c9 100644 --- a/src/GCodes/GCodeBuffer.h +++ b/src/GCodes/GCodeBuffer.h @@ -46,6 +46,8 @@ public: bool PushState(); // Push state returning true if successful (i.e. stack not overflowed) bool PopState(); // Pop state returning true if successful (i.e. no stack underrun) bool IsDoingFileMacro() const; // Return true if this source is executing a file macro + GCodeState GetState() const; + void SetState(GCodeState newState); private: @@ -106,4 +108,14 @@ inline void GCodeBuffer::SetWritingFileDirectory(const char* wfd) writingFileDirectory = wfd; } +inline GCodeState GCodeBuffer::GetState() const +{ + return machineState->state; +} + +inline void GCodeBuffer::SetState(GCodeState newState) +{ + machineState->state = newState; +} + #endif /* GCODEBUFFER_H_ */ diff --git a/src/GCodes/GCodeMachineState.h b/src/GCodes/GCodeMachineState.h index 3e60f661..8424738a 100644 --- a/src/GCodes/GCodeMachineState.h +++ b/src/GCodes/GCodeMachineState.h @@ -32,7 +32,11 @@ enum class GCodeState : uint8_t flashing1, flashing2, stopping, - sleeping + sleeping, + gridProbing1, + gridProbing2, + gridProbing3, + gridProbing4 }; // Class to hold the state of gcode execution for some input source diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 79ca3dda..eb732666 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -202,7 +202,7 @@ void GCodes::Spin() StringRef reply(replyBuffer, ARRAY_SIZE(replyBuffer)); reply.Clear(); - if (gb.MachineState().state == GCodeState::normal) + if (gb.GetState() == GCodeState::normal) { StartNextGCode(gb, reply); } @@ -211,19 +211,19 @@ void GCodes::Spin() // Perform the next operation of the state machine for this gcode source bool error = false; - switch (gb.MachineState().state) + switch (gb.GetState()) { case GCodeState::waitingForMoveToComplete: if (AllMovesAreFinishedAndMoveBufferIsLoaded()) { - gb.MachineState().state = GCodeState::normal; + gb.SetState(GCodeState::normal); } break; case GCodeState::homing: if (toBeHomed == 0) { - gb.MachineState().state = GCodeState::normal; + gb.SetState(GCodeState::normal); } else { @@ -243,7 +243,7 @@ void GCodes::Spin() case GCodeState::setBed1: reprap.GetMove()->SetIdentityTransform(); probeCount = 0; - gb.MachineState().state = GCodeState::setBed2; + gb.SetState(GCodeState::setBed2); // no break case GCodeState::setBed2: @@ -256,7 +256,7 @@ void GCodes::Spin() { zProbesSet = true; reprap.GetMove()->FinishedBedProbing(0, reply); - gb.MachineState().state = GCodeState::normal; + gb.SetState(GCodeState::normal); } } } @@ -270,7 +270,7 @@ void GCodes::Spin() reprap.StandbyTool(oldTool->Number()); } } - gb.MachineState().state = GCodeState::toolChange2; + gb.SetState(GCodeState::toolChange2); if (reprap.GetTool(newToolNumber) != nullptr && AllAxesAreHomed()) { scratchString.printf("tpre%d.g", newToolNumber); @@ -280,7 +280,7 @@ void GCodes::Spin() case GCodeState::toolChange2: // Select the new tool (even if it doesn't exist - that just deselects all tools) reprap.SelectTool(newToolNumber); - gb.MachineState().state = GCodeState::toolChange3; + gb.SetState(GCodeState::toolChange3); if (reprap.GetTool(newToolNumber) != nullptr && AllAxesAreHomed()) { scratchString.printf("tpost%d.g", newToolNumber); @@ -289,13 +289,13 @@ void GCodes::Spin() break; case GCodeState::toolChange3: - gb.MachineState().state = GCodeState::normal; + gb.SetState(GCodeState::normal); break; case GCodeState::pausing1: if (AllMovesAreFinishedAndMoveBufferIsLoaded()) { - gb.MachineState().state = GCodeState::pausing2; + gb.SetState(GCodeState::pausing2); DoFileMacro(gb, PAUSE_G); } break; @@ -324,16 +324,16 @@ void GCodes::Spin() moveBuffer.endStopsToCheck = 0; moveBuffer.usePressureAdvance = false; moveBuffer.filePos = noFilePosition; - if (gb.MachineState().state == GCodeState::resuming1 && currentZ > pauseRestorePoint.moveCoords[Z_AXIS]) + 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.MachineState().state = GCodeState::resuming2; + gb.SetState(GCodeState::resuming2); } else { // Just move to the saved position in one go - gb.MachineState().state = GCodeState::resuming3; + gb.SetState(GCodeState::resuming3); } moveAvailable = true; } @@ -353,7 +353,7 @@ void GCodes::Spin() feedRate = pauseRestorePoint.feedRate; isPaused = false; reply.copy("Printing resumed"); - gb.MachineState().state = GCodeState::normal; + gb.SetState(GCodeState::normal); } break; @@ -375,11 +375,11 @@ void GCodes::Spin() } if (!updating) { - gb.MachineState().state = GCodeState::flashing2; + gb.SetState(GCodeState::flashing2); } } #else - gb.MachineState().state = GCodeState::flashing2; + gb.SetState(GCodeState::flashing2; #endif break; @@ -392,7 +392,7 @@ void GCodes::Spin() // The above call does not return unless an error occurred } isFlashing = false; - gb.MachineState().state = GCodeState::normal; + gb.SetState(GCodeState::normal); break; case GCodeState::stopping: // MO after executing stop.g if present @@ -410,7 +410,7 @@ void GCodes::Spin() // 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.MachineState().state == GCodeState::sleeping) + if (gb.GetState() == GCodeState::sleeping) { DisableDrives(); } @@ -418,14 +418,147 @@ void GCodes::Spin() { platform->SetDriversIdle(); } - gb.MachineState().state = GCodeState::normal; + 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(); + 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; + 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(); + 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()) + { + if (numPointsProbed >= 4) + { + reprap.GetMove()->UseHeightMap(); + const double mean = heightSum/numPointsProbed; + const double deviation = sqrt(((heightSquaredSum * numPointsProbed) - (heightSum * heightSum)))/numPointsProbed; + reply.printf("%u points probed, mean error %.2f, deviation %.2f", numPointsProbed, mean, deviation); + } + 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.MachineState().state == GCodeState::normal) + if (gb.GetState() == GCodeState::normal) { // We completed a command, so unlock resources and tell the host about it UnlockAll(gb); @@ -588,7 +721,7 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply) // Finished a macro Pop(gb); gb.Init(); - if (gb.MachineState().state == GCodeState::normal) + if (gb.GetState() == GCodeState::normal) { UnlockAll(gb); HandleReply(gb, false, ""); @@ -719,7 +852,7 @@ void GCodes::DoPause(bool externalToFile) { pausedFanValues[i] = platform->GetFanValue(i); } - fileGCode->MachineState().state = GCodeState::pausing1; + fileGCode->SetState(GCodeState::pausing1); isPaused = true; } @@ -889,7 +1022,7 @@ bool GCodes::LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType) { moveArg += moveBuffer.coords[axis]; } - else if (currentTool != nullptr) + else if (currentTool != nullptr && moveType == 0) { moveArg -= currentTool->GetOffset()[axis]; // adjust requested position to compensate for tool offset } @@ -950,7 +1083,6 @@ bool GCodes::LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType) // 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? @@ -1089,7 +1221,7 @@ bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissi } gb.MachineState().fileState.Set(f); gb.MachineState().doingFileMacro = true; - gb.MachineState().state = GCodeState::normal; + gb.SetState(GCodeState::normal); gb.Init(); return true; } @@ -1314,7 +1446,7 @@ bool GCodes::DoHome(GCodeBuffer& gb, StringRef& reply, bool& error) } else { - gb.MachineState().state = GCodeState::homing; + gb.SetState(GCodeState::homing); } } return true; @@ -1522,7 +1654,7 @@ bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer& gb, StringRef& reply) } int probePointIndex = gb.GetIValue(); - if (probePointIndex < 0 || (unsigned int)probePointIndex >= MAX_PROBE_POINTS) + if (probePointIndex < 0 || (unsigned int)probePointIndex >= MaxProbePoints) { reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Z probe point index out of range.\n"); return true; @@ -1632,6 +1764,106 @@ bool GCodes::SetPrintZProbe(GCodeBuffer& gb, StringRef& reply) 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; + } +} + // 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 @@ -2647,7 +2879,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) int res = SetUpMove(gb, reply); if (res == 2) { - gb.MachineState().state = GCodeState::waitingForMoveToComplete; + gb.SetState(GCodeState::waitingForMoveToComplete); } result = (res != 0); } @@ -2696,6 +2928,30 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) result = DoHome(gb, reply, error); break; + case 29: // Grid-based bed probing + LockMovementAndWaitForStandstill(gb); // do this first to make sure that a new grid isn't being defined + + if (!reprap.GetMove()->GetBedProbeGrid().IsValid()) + { + reply.copy("No valid grid defined for G29 bed probing"); + error = true; + } + else if (!AllAxesAreHomed()) + { + reply.copy("Must home printer before G29 bed probing"); + error = true; + } + else + { + gridXindex = gridYindex = 0; + numPointsProbed = 0; + heightSum = heightSquaredSum = 0.0; + + reprap.GetMove()->ClearGridHeights(); + gb.SetState(GCodeState::gridProbing1); + } + break; + case 30: // Z probe/manually set at a position and set that as point P if (!LockMovementAndWaitForStandstill(gb)) { @@ -2732,7 +2988,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) // 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.MachineState().state = GCodeState::setBed1; // no bed.g file, so use the coordinates specified by M557 + gb.SetState(GCodeState::setBed1); // no bed.g file, so use the coordinates specified by M557 } else { @@ -2760,7 +3016,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) reply.printf("invalid G Code: %s", gb.Buffer()); } - if (result && gb.MachineState().state == GCodeState::normal) + if (result && gb.GetState() == GCodeState::normal) { UnlockAll(gb); HandleReply(gb, error, reply.Pointer()); @@ -2804,7 +3060,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } } - gb.MachineState().state = (code == 0) ? GCodeState::stopping : GCodeState::sleeping; + gb.SetState((code == 0) ? GCodeState::stopping : GCodeState::sleeping); DoFileMacro(gb, (code == 0) ? STOP_G : SLEEP_G, false); break; @@ -3019,7 +3275,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) if (isPaused) { - gb.MachineState().state = GCodeState::resuming1; + gb.SetState(GCodeState::resuming1); DoFileMacro(gb, RESUME_G); } else if (!fileToPrint.IsLive()) @@ -4748,7 +5004,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) if (gb.Seen('P')) { int point = gb.GetIValue(); - if (point < 0 || (unsigned int)point >= MAX_PROBE_POINTS) + if (point < 0 || (unsigned int)point >= MaxProbePoints) { reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Z probe point index out of range.\n"); } @@ -4772,6 +5028,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } } } + 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 @@ -5911,7 +6172,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) return false; } - gb.MachineState().state = GCodeState::flashing1; + gb.SetState(GCodeState::flashing1); break; case 998: @@ -5944,7 +6205,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) reply.printf("unsupported command: %s", gb.Buffer()); } - if (result && gb.MachineState().state == GCodeState::normal) + if (result && gb.GetState() == GCodeState::normal) { UnlockAll(gb); HandleReply(gb, error, reply.Pointer()); @@ -5975,7 +6236,7 @@ bool GCodes::HandleTcode(GCodeBuffer& gb, StringRef& reply) // 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.MachineState().state = GCodeState::toolChange1; + gb.SetState(GCodeState::toolChange1); if (oldTool != nullptr && AllAxesAreHomed()) { scratchString.printf("tfree%d.g", oldTool->Number()); diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index 140569f3..c1513714 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -206,6 +206,8 @@ private: void DoPause(bool externalToFile); // Pause the print void SetMappedFanSpeed(); // Set the speeds of fans mapped for the current tool + bool DefineGrid(GCodeBuffer& gb, StringRef &reply); // Define the probing grid, returning true if error + Platform* platform; // The RepRap machine Webserver* webserver; // The webserver class @@ -265,6 +267,9 @@ private: float lastProbedZ; // the last height at which the Z probe stopped bool zProbesSet; // True if all Z probing is done and we can set the bed equation volatile bool zProbeTriggered; // Set by the step ISR when a move is aborted because the Z probe is triggered + size_t gridXindex, gridYindex; // Which grid probe point is next + size_t numPointsProbed; + double heightSum, heightSquaredSum; float simulationTime; // Accumulated simulation time uint8_t simulationMode; // 0 = not simulating, 1 = simulating, >1 are simulation modes for debugging diff --git a/src/Movement/Grid.cpp b/src/Movement/Grid.cpp new file mode 100644 index 00000000..d338ceb7 --- /dev/null +++ b/src/Movement/Grid.cpp @@ -0,0 +1,216 @@ +/* + * Grid.cpp + * + * Created on: 18 Nov 2016 + * Author: David + */ + +#include "Grid.h" +#include "Configuration.h" +#include <cmath> + +// Initialise the grid to be invalid +GridDefinition::GridDefinition() + : xMin(0.0), xMax(-1.0), yMin(0.0), yMax(-1.0), radius(-1.0), spacing(DefaultGridSpacing), gridHeights(nullptr), + numX(0), numY(0), recipSpacing(1.0/spacing), isValid(false) +{ +} + +GridDefinition::GridDefinition(const float xRange[2], const float yRange[2], float pRadius, float pSpacing) + : xMin(xRange[0]), xMax(xRange[1]), yMin(yRange[0]), yMax(yRange[1]), radius(pRadius), spacing(pSpacing) +{ + numX = (xMax - xMin >= MinRange && spacing >= MinSpacing) ? (uint32_t)((xMax - xMin)/spacing) + 1 : 0; + numY = (yMax - yMin >= MinRange && spacing >= MinSpacing) ? (uint32_t)((xMax - xMin)/spacing) + 1 : 0; + isValid = NumPoints() != 0 && NumPoints() <= MaxGridProbePoints && (radius < 0.0 || radius >= 1.0); +} + +void GridDefinition::SetStorage(const float *heightStorage, const uint32_t *heightSetStorage) +{ + gridHeights = heightStorage; + gridHeightSet = heightSetStorage; +} + +float GridDefinition::GetXCoordinate(unsigned int xIndex) const +{ + return xMin + (xIndex * spacing); +} + +float GridDefinition::GetYCoordinate(unsigned int yIndex) const +{ + return yMin + (yIndex * spacing); +} + +bool GridDefinition::IsInRadius(float x, float y) const +{ + return radius < 0.0 || x * x + y * y < radius * radius; +} + +// Append the grid parameters to the end of a string +void GridDefinition::PrintParameters(StringRef& r) const +{ + r.catf("X%.1f:%.1f, Y%.1f:%.1f, radius %.1f, spacing %.1f, %d points", xMin, xMax, yMin, yMax, radius, spacing, NumPoints()); +} + +// Print what is wrong with the grid +void GridDefinition::PrintError(StringRef& r) const +{ + if (spacing < MinSpacing) + { + r.cat("Spacing too small"); + } + else if (NumXpoints() == 0) + { + r.cat("X range too small"); + } + else if (NumYpoints() == 0) + { + r.cat("Y range too small"); + } + else if (NumPoints() > MaxGridProbePoints) + { + r.catf("Too many grid points (maximum %d, needed %d)", MaxGridProbePoints, NumPoints()); + } + else + { + // The only thing left is a bad radius + r.cat("Bad radius"); + } +} + +// Compute the height error at the specified point +float GridDefinition::ComputeHeightError(float x, float y) const +{ + const float xf = (x - xMin) * recipSpacing; + const float xFloor = floor(xf); + const int32_t xIndex = (int32_t)xFloor; + const float yf = (y - yMin) * recipSpacing; + const float yFloor = floor(yf); + const int32_t yIndex = (int32_t)yFloor; + + if (xIndex < 0) + { + if (yIndex < 0) + { + // We are off the bottom left corner of the grid + return GetHeightError(0, 0); + } + else if (yIndex >= (int)NumYpoints()) + { + return GetHeightError(0, NumYpoints()); + } + else + { + return InterpolateY(0, yIndex, yf - yFloor); + } + } + else if (xIndex >= (int)NumXpoints()) + { + if (yIndex < 0) + { + // We are off the bottom left corner of the grid + return GetHeightError(NumXpoints(), 0); + } + else if (yIndex >= (int)NumYpoints()) + { + return GetHeightError(NumXpoints(), NumYpoints()); + } + else + { + return InterpolateY(NumXpoints(), yIndex, yf - yFloor); + } + } + else + { + if (yIndex < 0) + { + // We are off the bottom left corner of the grid + return InterpolateX(xIndex, 0, xf - xFloor); + } + else if (yIndex >= (int)NumYpoints()) + { + return InterpolateX(xIndex, NumYpoints(), xf - xFloor); + } + else + { + return InterpolateXY(xIndex, yIndex, xf - xFloor, yf - yFloor); + } + } +} + +float GridDefinition::GetHeightError(uint32_t xIndex, uint32_t yIndex) const +{ + const uint32_t index = GetMapIndex(xIndex, yIndex); + return (IsHeightSet(index)) ? gridHeights[index] : 0.0; +} + +float GridDefinition::InterpolateX(uint32_t xIndex, uint32_t yIndex, float xFrac) const +{ + const uint32_t index1 = GetMapIndex(xIndex, yIndex); + return Interpolate2(index1, index1 + 1, xFrac); +} + +float GridDefinition::InterpolateY(uint32_t xIndex, uint32_t yIndex, float yFrac) const +{ + const uint32_t index1 = GetMapIndex(xIndex, yIndex); + return Interpolate2(index1, index1 + numX, yFrac); +} + +float GridDefinition::Interpolate2(uint32_t index1, uint32_t index2, float frac) const +{ + const bool b1 = IsHeightSet(index1); + const bool b2 = IsHeightSet(index2); + return (b1 && b2) ? (frac * gridHeights[index1]) + ((1.0 - frac) * gridHeights[index2]) + : (b1) ? gridHeights[index1] + : (b2) ? gridHeights[index2] + : 0.0; +} + +float GridDefinition::InterpolateXY(uint32_t xIndex, uint32_t yIndex, float xFrac, float yFrac) const +{ + const uint32_t indexX0Y0 = GetMapIndex(xIndex, yIndex); // (X0,Y0) + const uint32_t indexX1Y0 = indexX0Y0 + 1; // (X1,Y0) + const uint32_t indexX0Y1 = indexX0Y0 + numX; // (X0 Y1) + const uint32_t indexX1Y1 = indexX0Y1 + 1; // (X1,Y1) + const unsigned int cc = ((unsigned int)IsHeightSet(indexX0Y0) << 0) + + ((unsigned int)IsHeightSet(indexX1Y0) << 1) + + ((unsigned int)IsHeightSet(indexX0Y1) << 2) + + ((unsigned int)IsHeightSet(indexX1Y1) << 3); + switch(cc) + { + case 0: // no points defined + default: + return 0.0; + case 1: // (X0,Y0) defined + return gridHeights[indexX0Y0]; + case 2: // (X1,Y0) defined + return gridHeights[indexX1Y0]; + case 3: // (X0,Y0) and (X1,Y0) defined + return Interpolate2(indexX0Y0, indexX1Y0, xFrac); + case 4: // (X0,Y1) defined + return gridHeights[indexX0Y1]; + case 5: // (X0,Y0) and (X0,Y1) defined + return Interpolate2(indexX0Y0, indexX0Y1, yFrac); + case 6: // (X1,Y0) and (X0,Y1) defined - diagonal interpolation + return DiagonalInterpolate(indexX1Y0, indexX0Y1, 1.0 - xFrac, yFrac); + case 7: // (X0,Y0), (X1,Y0) and (X0,Y1) defined - 3-way interpolation + return Interpolate3(indexX0Y0, indexX1Y0, indexX0Y1, xFrac, yFrac); + case 8: // (X1,Y1) defined + return gridHeights[indexX1Y1]; + case 9: // (X0,Y0) and (X1,Y1) defined - diagonal interpolation + return DiagonalInterpolate(indexX0Y0, indexX1Y1, xFrac, yFrac); + case 10: // (X1,Y0) and (X1,Y1) defined + return Interpolate2(indexX1Y0, indexX1Y1, yFrac); + case 11: // (X0,Y0), (X1,Y0) and (X1,Y1) defined - 3-way interpolation + return Interpolate3(indexX1Y0, indexX0Y0, indexX1Y1, xFrac, yFrac); + case 12: // (X0,Y1) and (X1,Y1) defined + return Interpolate2(indexX0Y1, indexX1Y1, yFrac); + case 13: // (X0,Y0), (X0,Y1) and (X1,Y1) defined - 3-way interpolation + return Interpolate3(indexX0Y1, indexX1Y1, indexX0Y0, xFrac, 1.0 - yFrac); + case 14: // (X1,Y0), (X0,Y1) and (X1,Y1) defined - 3-way interpolation + return Interpolate3(indexX1Y1, indexX0Y1, indexX1Y0, 1.0 - xFrac, 1.0 - yFrac); + case 15: // All points defined + return Interpolate4(indexX0Y0, indexX1Y0, indexX0Y1, indexX1Y1, xFrac, yFrac); + } +} + +// End diff --git a/src/Movement/Grid.h b/src/Movement/Grid.h new file mode 100644 index 00000000..f03ec4e5 --- /dev/null +++ b/src/Movement/Grid.h @@ -0,0 +1,66 @@ +/* + * Grid.h + * + * Created on: 18 Nov 2016 + * Author: David + */ + +#ifndef SRC_MOVEMENT_GRID_H_ +#define SRC_MOVEMENT_GRID_H_ + +#include <cstdint> +#include "ecv.h" +#include "Libraries/General/StringRef.h" + +// This class defines the bed probing grid +class GridDefinition +{ +public: + GridDefinition(); + GridDefinition(const float xRange[2], const float yRange[2], float pRadius, float pSpacing); + void SetStorage(const float *heightStorage, const uint32_t *heightSetStorage); + + uint32_t NumXpoints() const { return numX; } + uint32_t NumYpoints() const { return numY; } + uint32_t NumPoints() const { return numX * numY; } + float GetXCoordinate(unsigned int xIndex) const; + float GetYCoordinate(unsigned int yIndex) const; + bool IsInRadius(float x, float y) const; + bool IsValid() const { return isValid; } + + void PrintParameters(StringRef& r) const; + void PrintError(StringRef& r) const + pre(!IsValid()); + + float ComputeHeightError(float x, float y) const // Compute the height error at the specified point + pre(IsValid(); gridHeights != nullptr; gridHeights.upb >= NumPoints()); + +private: + static constexpr float MinSpacing = 0.1; // The minimum point spacing allowed + static constexpr float MinRange = 1.0; // The minimum X and Y range allowed + + // Primary parameters + float xMin, xMax, yMin, yMax; // The edges of the grid for G29 probing + float radius; // The grid radius to probe + float spacing; // The spacing of the grid probe points + const float *gridHeights; // The map of grid heights + const uint32_t *gridHeightSet; // Bitmap of which heights are set + + // Derived parameters + uint32_t numX, numY; + float recipSpacing; + bool isValid; + + uint32_t GetMapIndex(uint32_t xIndex, uint32_t yIndex) const { return (yIndex * numX) + xIndex; } + bool IsHeightSet(uint32_t index) const { return (gridHeightSet[index/32] & (1 << (index & 31))) != 0; } + float GetHeightError(uint32_t xIndex, uint32_t yIndex) const; + float InterpolateX(uint32_t xIndex, uint32_t yIndex, float xFrac) const; + float InterpolateY(uint32_t xIndex, uint32_t yIndex, float yFrac) const; + float InterpolateXY(uint32_t xIndex, uint32_t yIndex, float xFrac, float yFrac) const; + float Interpolate2(uint32_t index1, uint32_t index2, float frac) const; + float DiagonalInterpolate(uint32_t index1, uint32_t index2, float xFrac, float yFrac) const; + float Interpolate3(uint32_t cornerIndex, uint32_t indexX, uint32_t indexY, float xFrac, float yFrac) const; + float Interpolate4(uint32_t index1, uint32_t index2, uint32_t index3, uint32_t index4, float xFrac, float yFrac) const; +}; + +#endif /* SRC_MOVEMENT_GRID_H_ */ diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp index b1235bba..9d630f34 100644 --- a/src/Movement/Move.cpp +++ b/src/Movement/Move.cpp @@ -66,7 +66,7 @@ void Move::Init() SetPositions(move); // Set up default bed probe points. This is only a guess, because we don't know the bed size yet. - for (size_t point = 0; point < MAX_PROBE_POINTS; point++) + for (size_t point = 0; point < MaxProbePoints; point++) { if (point < 4) { @@ -89,6 +89,7 @@ void Move::Init() simulationTime = 0.0; longestGcodeWaitInterval = 0; waitingForMove = false; + useGridHeights = false; active = true; } @@ -865,7 +866,7 @@ void Move::FinishedBedProbing(int sParam, StringRef& reply) // Clear out the Z heights so that we don't re-use old points. // This allows us to use different numbers of probe point on different occasions. - for (size_t i = 0; i < MAX_PROBE_POINTS; ++i) + for (size_t i = 0; i < MaxProbePoints; ++i) { probePointSet[i] &= ~zSet; } @@ -996,8 +997,8 @@ void Move::DoDeltaCalibration(size_t numFactors, StringRef& reply) //uint32_t startTime = reprap.GetPlatform()->GetInterruptClocks(); // Transform the probing points to motor endpoints and store them in a matrix, so that we can do multiple iterations using the same data - FixedMatrix<floatc_t, MAX_DELTA_PROBE_POINTS, DELTA_AXES> probeMotorPositions; - floatc_t corrections[MAX_DELTA_PROBE_POINTS]; + FixedMatrix<floatc_t, MaxDeltaCalibrationPoints, DELTA_AXES> probeMotorPositions; + floatc_t corrections[MaxDeltaCalibrationPoints]; float_t initialSumOfSquares = 0.0; for (size_t i = 0; i < numPoints; ++i) { @@ -1028,7 +1029,7 @@ void Move::DoDeltaCalibration(size_t numFactors, StringRef& reply) for (;;) { // Build a Nx9 matrix of derivatives with respect to xa, xb, yc, za, zb, zc, diagonal. - FixedMatrix<floatc_t, MAX_DELTA_PROBE_POINTS, NumDeltaFactors> derivativeMatrix; + FixedMatrix<floatc_t, MaxDeltaCalibrationPoints, NumDeltaFactors> derivativeMatrix; for (size_t i = 0; i < numPoints; ++i) { for (size_t j = 0; j < numFactors; ++j) @@ -1079,7 +1080,7 @@ void Move::DoDeltaCalibration(size_t numFactors, StringRef& reply) PrintVector("Solution", solution, numFactors); // Calculate and display the residuals - floatc_t residuals[MAX_DELTA_PROBE_POINTS]; + floatc_t residuals[MaxDeltaCalibrationPoints]; for (size_t i = 0; i < numPoints; ++i) { residuals[i] = zBedProbePoints[i]; @@ -1096,7 +1097,7 @@ void Move::DoDeltaCalibration(size_t numFactors, StringRef& reply) // Calculate the expected probe heights using the new parameters { - floatc_t expectedResiduals[MAX_DELTA_PROBE_POINTS]; + floatc_t expectedResiduals[MaxDeltaCalibrationPoints]; floatc_t sumOfSquares = 0.0; for (size_t i = 0; i < numPoints; ++i) { @@ -1383,7 +1384,7 @@ void Move::ResetExtruderPositions() void Move::SetXBedProbePoint(size_t index, float x) { - if (index >= MAX_PROBE_POINTS) + if (index >= MaxProbePoints) { reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Z probe point X index out of range.\n"); return; @@ -1394,7 +1395,7 @@ void Move::SetXBedProbePoint(size_t index, float x) void Move::SetYBedProbePoint(size_t index, float y) { - if (index >= MAX_PROBE_POINTS) + if (index >= MaxProbePoints) { reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Z probe point Y index out of range.\n"); return; @@ -1405,12 +1406,13 @@ void Move::SetYBedProbePoint(size_t index, float y) void Move::SetZBedProbePoint(size_t index, float z, bool wasXyCorrected, bool wasError) { - if (index >= MAX_PROBE_POINTS) + if (index >= MaxProbePoints) { reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Z probe point Z index out of range.\n"); } else { + useGridHeights = false; zBedProbePoints[index] = z; probePointSet[index] |= zSet; if (wasXyCorrected) @@ -1459,26 +1461,55 @@ bool Move::XYProbeCoordinatesSet(int index) const size_t Move::NumberOfProbePoints() const { - for(size_t i = 0; i < MAX_PROBE_POINTS; i++) + for(size_t i = 0; i < MaxProbePoints; i++) { if(!AllProbeCoordinatesSet(i)) { return i; } } - return MAX_PROBE_POINTS; + return MaxProbePoints; } size_t Move::NumberOfXYProbePoints() const { - for(size_t i = 0; i < MAX_PROBE_POINTS; i++) + for(size_t i = 0; i < MaxProbePoints; i++) { if(!XYProbeCoordinatesSet(i)) { return i; } } - return MAX_PROBE_POINTS; + return MaxProbePoints; +} + +// Set a new grid +void Move::SetBedProbeGrid(const GridDefinition& newGrid) +{ + useGridHeights = false; + grid = newGrid; + grid.SetStorage(zBedProbePoints, gridHeightSet); +} + +void Move::ClearGridHeights() +{ + useGridHeights = false; + SetIdentityTransform(); + for (size_t i = 0; i < ARRAY_SIZE(gridHeightSet); ++i) + { + gridHeightSet[i] = 0; + } +} + +// Set the height of a grid point +void Move::SetGridHeight(size_t xIndex, size_t yIndex, float height) +{ + size_t index = yIndex * grid.NumXpoints() + xIndex; + if (index < MaxGridProbePoints) + { + zBedProbePoints[index] = height; + gridHeightSet[index/32] |= 1u << (index & 31u); + } } // Enter or leave simulation mode diff --git a/src/Movement/Move.h b/src/Movement/Move.h index b7614349..efd6f73e 100644 --- a/src/Movement/Move.h +++ b/src/Movement/Move.h @@ -22,6 +22,7 @@ typedef float floatc_t; // type of matrix element used for delta calibratio #include "DeltaParameters.h" #include "DeltaProbe.h" +#include "Grid.h" enum PointCoordinateSet { @@ -112,6 +113,15 @@ public: bool IsExtruding() const; // Is filament being extruded? + const GridDefinition& GetBedProbeGrid() const { return grid; } // Access the bed probing grid + + void SetBedProbeGrid(const GridDefinition& newGrid) // Set a new grid + pre(newGrid.IsValid()); + + void ClearGridHeights(); // Clear all grid height corrections + void SetGridHeight(size_t xIndex, size_t yIndex, float height); // Set the height of a grid point + void UseHeightMap() { useGridHeights = true; } // Start using the height map + private: enum class IdleState : uint8_t { idle, busy, timing }; @@ -158,18 +168,23 @@ private: volatile bool liveCoordinatesValid; // True if the XYZ live coordinates are reliable (the extruder ones always are) volatile int32_t liveEndPoints[DRIVES]; // The XYZ endpoints of the last completed move in motor coordinates - float xBedProbePoints[MAX_PROBE_POINTS]; // The X coordinates of the points on the bed at which to probe - float yBedProbePoints[MAX_PROBE_POINTS]; // The Y coordinates of the points on the bed at which to probe - float zBedProbePoints[MAX_PROBE_POINTS]; // The Z coordinates of the points on the bed at which to probe + // Variable for G32 bed probing, for bed compensation and delta calibration + float xBedProbePoints[MaxProbePoints]; // The X coordinates of the points on the bed at which to probe + float yBedProbePoints[MaxProbePoints]; // The Y coordinates of the points on the bed at which to probe + float zBedProbePoints[MaxGridProbePoints]; // The Z coordinates of the points on the bed that were probed float baryXBedProbePoints[5]; // The X coordinates of the triangle corner points float baryYBedProbePoints[5]; // The Y coordinates of the triangle corner points float baryZBedProbePoints[5]; // The Z coordinates of the triangle corner points - uint8_t probePointSet[MAX_PROBE_POINTS]; // Has the XY of this point been set? Has the Z been probed? + uint8_t probePointSet[MaxProbePoints]; // Has the XY of this point been set? Has the Z been probed? float aX, aY, aC; // Bed plane explicit equation z' = z + aX*x + aY*y + aC float tanXY, tanYZ, tanXZ; // Axis compensation - 90 degrees + angle gives angle between axes int numBedCompensationPoints; // The number of points we are actually using for bed compensation, 0 means identity bed transform float xRectangle, yRectangle; // The side lengths of the rectangle used for second-degree bed compensation + GridDefinition grid; // Grid definition for G29 bed probing. The probe heights are stored in zBedProbePoints, see above. + uint32_t gridHeightSet[MaxGridProbePoints/32]; // Bitmap of which points have been probed + bool useGridHeights; // True if the zBedProbePoints came from valid bed probing and relate to the current grid + float idleTimeout; // How long we wait with no activity before we reduce motor currents to idle float lastMoveTime; // The approximate time at which the last move was completed, or 0 float longWait; // A long time for things that need to be done occasionally diff --git a/src/Platform.cpp b/src/Platform.cpp index 391b7331..72ddd3f4 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -652,7 +652,7 @@ const ZProbeParameters& Platform::GetZProbeParameters() const } } -void Platform::SetZProbeParameters(const struct ZProbeParameters& params) +void Platform::SetZProbeParameters(const ZProbeParameters& params) { if (GetZProbeParameters() != params) { @@ -683,6 +683,16 @@ void Platform::SetZProbeParameters(const struct ZProbeParameters& params) } } +// Return true if the specified point is accessible to the Z probe +bool Platform::IsAccessibleProbePoint(float x, float y) const +{ + x -= GetZProbeParameters().xOffset; + y -= GetZProbeParameters().yOffset; + return (reprap.GetMove()->IsDeltaMode()) + ? x * x + y * y < reprap.GetMove()->GetDeltaParams().GetPrintRadiusSquared() + : x >= axisMinima[X_AXIS] && y >= axisMinima[Y_AXIS] && x <= axisMaxima[X_AXIS] && y <= axisMaxima[Y_AXIS]; +} + // Return true if we must home X and Y before we home Z (i.e. we are using a bed probe) bool Platform::MustHomeXYBeforeZ() const { @@ -1379,7 +1389,7 @@ void Platform::Diagnostics(MessageType mtype) // Show the current probe position heights Message(mtype, "Bed probe heights:"); - for (size_t i = 0; i < MAX_PROBE_POINTS; ++i) + for (size_t i = 0; i < MaxProbePoints; ++i) { MessageF(mtype, " %.3f", reprap.GetMove()->ZBedProbePoint(i)); } diff --git a/src/Platform.h b/src/Platform.h index 958f5557..036084f9 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -524,6 +524,7 @@ public: float AxisMinimum(size_t axis) const; void SetAxisMinimum(size_t axis, float value); float AxisTotalLength(size_t axis) const; + bool IsAccessibleProbePoint(float x, float y) const; float GetPressureAdvance(size_t drive) const; void SetPressureAdvance(size_t extruder, float factor); void SetEndStopConfiguration(size_t axis, EndStopType endstopType, bool logicLevel); diff --git a/src/Storage/MassStorage.cpp b/src/Storage/MassStorage.cpp index 945045c9..06b38627 100644 --- a/src/Storage/MassStorage.cpp +++ b/src/Storage/MassStorage.cpp @@ -242,6 +242,13 @@ bool MassStorage::MakeDirectory(const char *directory) // Rename a file or directory bool MassStorage::Rename(const char *oldFilename, const char *newFilename) { + if (newFilename[0] >= '0' && newFilename[0] <= '9' && newFilename[1] == ':') + { + // Workaround for DWC 1.13 which send a volume specification at the start of the new path. + // f_rename can't handle this, so skip past the volume specification. + // We are assuming that the user isn't really trying to rename across volumes. This is a safe assumption when the client is DWC. + newFilename += 2; + } if (f_rename(oldFilename, newFilename) != FR_OK) { platform->MessageF(GENERIC_MESSAGE, "Can't rename file or directory %s to %s\n", oldFilename, newFilename); |