diff options
Diffstat (limited to 'src')
45 files changed, 1611 insertions, 1005 deletions
diff --git a/src/Configuration.h b/src/Configuration.h index 9e9e192a..26eddf52 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -43,10 +43,13 @@ const float ABS_ZERO = -273.15; // Celsius const float NEARLY_ABS_ZERO = -273.0; // Celsius const float ROOM_TEMPERATURE = 21.0; // Celsius +// Timeouts const float LONG_TIME = 300.0; // Seconds const uint32_t MinimumWarningInterval = 4000; // Milliseconds const uint32_t FanCheckInterval = 500; // Milliseconds const uint32_t DriverCoolingTimeout = 4000; // Milliseconds +const float DefaultMessageTimeout = 10.0; // How long a message is displayed by default, in seconds + // FanCheckInterval must be lower than MinimumWarningInterval to avoid giving driver over temperature warnings too soon when thermostatic control of electronics cooling fans is used static_assert(FanCheckInterval < MinimumWarningInterval, "FanCheckInterval too large"); @@ -90,8 +93,6 @@ const int8_t DefaultE0Heater = 1; // Index of the default first extruder hea const unsigned int FirstVirtualHeater = 100; // the heater number at which virtual heaters start const unsigned int MaxVirtualHeaters = 10; // the number of virtual heaters supported -const size_t MaxHeaterNameLength = 20; // Maximum number of characters in a heater name - // These parameters are about right for a typical PCB bed heater that maxes out at 110C const float DefaultBedHeaterGain = 90.0; const float DefaultBedHeaterTimeConstant = 700.0; @@ -163,6 +164,7 @@ const size_t GCODE_REPLY_LENGTH = 2048; const size_t MESSAGE_LENGTH = 256; const size_t FILENAME_LENGTH = 100; +const size_t MaxHeaterNameLength = 20; // Maximum number of characters in a heater name // Output buffer lengths diff --git a/src/DuetNG/DueXn.cpp b/src/DuetNG/DueXn.cpp index f8678409..837b9484 100644 --- a/src/DuetNG/DueXn.cpp +++ b/src/DuetNG/DueXn.cpp @@ -18,17 +18,20 @@ namespace DuetExpansion const uint8_t DueXnAddress = 0x3E; // address of the SX1509B on the DueX0/DueX2/DueX5 - const uint16_t BoardTypePins = (1u << 14) | (1u << 15); + // The original DueX2 and DueX5 boards had 2 board ID pins, bits 14 an 15. + // The new ones use bit 15 for fan 8, so not we just have bit 14. + // If we want any more variants, they will have to use a different I2C address. + const uint16_t BoardTypePins = (1u << 14); const unsigned int BoardTypeShift = 14; - const ExpansionBoardType boardTypes[] = - { ExpansionBoardType::DueX5, ExpansionBoardType::DueX2, ExpansionBoardType::DueX0, ExpansionBoardType::none }; + const ExpansionBoardType boardTypes[] = { ExpansionBoardType::DueX5, ExpansionBoardType::DueX2 }; const unsigned int Fan3Bit = 12; const unsigned int Fan4Bit = 7; const unsigned int Fan5Bit = 6; const unsigned int Fan6Bit = 5; const unsigned int Fan7Bit = 4; - const uint16_t AllFanBits = (1u << Fan3Bit) | (1u << Fan4Bit) | (1u << Fan5Bit) | (1u << Fan6Bit) | (1u << Fan7Bit); + const unsigned int Fan8Bit = 15; + const uint16_t AllFanBits = (1u << Fan3Bit) | (1u << Fan4Bit) | (1u << Fan5Bit) | (1u << Fan6Bit) | (1u << Fan7Bit) | (1 << Fan8Bit); const unsigned int E2StopBit = 0; const unsigned int E3StopBit = 3; diff --git a/src/DuetNG/DuetWiFi/Network.cpp b/src/DuetNG/DuetWiFi/Network.cpp index 97e14400..1a8b1a89 100644 --- a/src/DuetNG/DuetWiFi/Network.cpp +++ b/src/DuetNG/DuetWiFi/Network.cpp @@ -315,7 +315,7 @@ bool Network::GetNetworkState(StringRef& reply) reply.cat(TranslateWiFiState(currentMode)); if (currentMode == WiFiState::connected || currentMode == WiFiState::runningAsAccessPoint) { - reply.cat(ssid); + reply.catf("%s, IP address %u.%u.%u.%u", ssid, ipAddress[0], ipAddress[1], ipAddress[2], ipAddress[3]); } break; default: diff --git a/src/DuetNG/Pins_DuetNG.h b/src/DuetNG/Pins_DuetNG.h index 2003b078..b67e9066 100644 --- a/src/DuetNG/Pins_DuetNG.h +++ b/src/DuetNG/Pins_DuetNG.h @@ -26,6 +26,7 @@ const size_t NumFirmwareUpdateModules = 1; // 1 module #define SUPPORT_INKJET 0 // set nonzero to support inkjet control #define SUPPORT_ROLAND 0 // set nonzero to support Roland mill #define SUPPORT_SCANNER 1 // set zero to disable support for FreeLSS scanners +#define SUPPORT_IOBITS 1 // set to support P parameter in G0/G1 commands // The physical capabilities of the machine @@ -117,9 +118,9 @@ const Pin VssaSensePin = 103; // Digital pin number to turn the IR LED on (high) or off (low) const Pin Z_PROBE_MOD_PIN = 34; // Digital pin number to turn the IR LED on (high) or off (low) on Duet v0.6 and v1.0 (PB21) -// COOLING FANS -const size_t NUM_FANS = 8; -const Pin COOLING_FAN_PINS[NUM_FANS] = { 55, 58, 00, 212, 207, 206, 205, 204 }; +// Cooling fans +const size_t NUM_FANS = 9; +const Pin COOLING_FAN_PINS[NUM_FANS] = { 55, 58, 00, 212, 207, 206, 205, 204, 215 }; const Pin COOLING_FAN_RPM_PIN = 102; // PB6 on expansion connector // SD cards diff --git a/src/GCodes/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer.cpp index 1910259c..f3ca4c9b 100644 --- a/src/GCodes/GCodeBuffer.cpp +++ b/src/GCodes/GCodeBuffer.cpp @@ -218,42 +218,6 @@ bool GCodeBuffer::Seen(char c) return false; } -// As Seen but require a space before the letter. Needed when there are string parameters in the command. -bool GCodeBuffer::SeenAfterSpace(char c) -{ - readPointer = 0; - bool seenSpace = false; - bool inQuotes = false; - for (;;) - { - const char b = gcodeBuffer[readPointer]; - if (b == 0) - { - break; - } - if (b == '"') - { - inQuotes = !inQuotes; - seenSpace = false; - } - else if (!inQuotes) - { - if (b == c && seenSpace) - { - return true; - } - if (b == ';' ) - { - break; - } - seenSpace = (b == ' '); - } - ++readPointer; - } - readPointer = -1; - return false; -} - // Return the first G, M or T command letter. Needed so that we don't pick up a spurious command letter form inside a string parameter. char GCodeBuffer::GetCommandLetter() { @@ -606,11 +570,15 @@ bool GCodeBuffer::PushState() GCodeMachineState * const ms = GCodeMachineState::Allocate(); ms->previous = machineState; ms->feedrate = machineState->feedrate; + ms->fileState.CopyFrom(machineState->fileState); + ms->lockedResources = machineState->lockedResources; ms->drivesRelative = machineState->drivesRelative; ms->axesRelative = machineState->axesRelative; ms->doingFileMacro = machineState->doingFileMacro; - ms->fileState.CopyFrom(machineState->fileState); - ms->lockedResources = machineState->lockedResources; + ms->waitWhileCooling = machineState->waitWhileCooling; + ms->runningM502 = machineState->runningM502; + ms->messageAcknowledged = false; + ms->waitingForAcknowledgement = false; machineState = ms; return true; } @@ -621,6 +589,8 @@ bool GCodeBuffer::PopState() GCodeMachineState * const ms = machineState; if (ms->previous == nullptr) { + ms->messageAcknowledged = false; // avoid getting stuck in a loop trying to pop + ms->waitingForAcknowledgement = false; return false; } @@ -635,4 +605,18 @@ bool GCodeBuffer::IsDoingFileMacro() const return machineState->doingFileMacro; } +// Tell this input source that any message it sent and is waiting on has been acknowledged +// Allow for the possibility that the source may have started running a macro since it started waiting +void GCodeBuffer::MessageAcknowledged() +{ + for (GCodeMachineState *ms = machineState; ms != nullptr; ms = ms->previous) + { + if (ms->waitingForAcknowledgement) + { + ms->waitingForAcknowledgement = false; + ms->messageAcknowledged = true; + } + } +} + // End diff --git a/src/GCodes/GCodeBuffer.h b/src/GCodes/GCodeBuffer.h index b1716d8f..14f71552 100644 --- a/src/GCodes/GCodeBuffer.h +++ b/src/GCodes/GCodeBuffer.h @@ -24,7 +24,6 @@ public: bool Put(const char *str, size_t len); // Add an entire string bool IsEmpty() const; // Does this buffer contain any code? bool Seen(char c); // Is a character present? - bool SeenAfterSpace(char c); // Is a character present? char GetCommandLetter(); // Find the first G, M or T command float GetFValue(); // Get a float after a key letter @@ -64,6 +63,7 @@ public: void AdvanceState(); const char *GetIdentity() const { return identity; } const bool CanQueueCodes() const { return queueCodes; } + void MessageAcknowledged(); uint32_t whenTimerStarted; // when we started waiting bool timerRunning; // true if we are waiting diff --git a/src/GCodes/GCodeMachineState.cpp b/src/GCodes/GCodeMachineState.cpp index 420b2c5b..60866a0d 100644 --- a/src/GCodes/GCodeMachineState.cpp +++ b/src/GCodes/GCodeMachineState.cpp @@ -13,7 +13,7 @@ unsigned int GCodeMachineState::numAllocated = 0; // Create a default initialised GCodeMachineState GCodeMachineState::GCodeMachineState() : previous(nullptr), feedrate(DefaultFeedrate * SecondsToMinutes), fileState(), lockedResources(0), state(GCodeState::normal), - drivesRelative(false), axesRelative(false), doingFileMacro(false), runningM502(false) + drivesRelative(false), axesRelative(false), doingFileMacro(false), runningM502(false), waitingForAcknowledgement(false), messageAcknowledged(false) { } diff --git a/src/GCodes/GCodeMachineState.h b/src/GCodes/GCodeMachineState.h index 157392a7..42392f86 100644 --- a/src/GCodes/GCodeMachineState.h +++ b/src/GCodes/GCodeMachineState.h @@ -23,6 +23,7 @@ enum class GCodeState : uint8_t toolChange1, toolChange2, toolChangeComplete, + // These next 4 must be contiguous m109ToolChange0, m109ToolChange1, @@ -38,6 +39,7 @@ enum class GCodeState : uint8_t flashing2, stopping, sleeping, + // These next 5 must be contiguous gridProbing1, gridProbing2, @@ -45,6 +47,13 @@ enum class GCodeState : uint8_t gridProbing3, gridProbing4, + // These next 4 must be contiguous + probingAtPoint1, + probingAtPoint2, + probingAtPoint3, + probingAtPoint4, + probingAtPoint5, + doingFirmwareRetraction, doingFirmwareUnRetraction }; @@ -65,7 +74,9 @@ public: axesRelative : 1, doingFileMacro : 1, waitWhileCooling : 1, - runningM502 : 1; + runningM502 : 1, + waitingForAcknowledgement : 1, + messageAcknowledged : 1; static GCodeMachineState *Allocate() post(!result.IsLive(); result.state == GCodeState::normal); diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 1e5fb3bf..e86ce9e0 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -48,16 +48,6 @@ const char* const DefaultHeightMapFile = "heightmap.csv"; const size_t gcodeReplyLength = 2048; // long enough to pass back a reasonable number of files in response to M20 - -void GCodes::RestorePoint::Init() -{ - for (size_t i = 0; i < DRIVES; ++i) - { - moveCoords[i] = 0.0; - } - feedRate = DefaultFeedrate * SecondsToMinutes; -} - GCodes::GCodes(Platform& p) : platform(p), active(false), isFlashing(false), fileBeingHashed(nullptr), lastWarningMillis(0) @@ -87,9 +77,10 @@ void GCodes::Exit() void GCodes::Init() { - Reset(); - numVisibleAxes = numTotalAxes = XYZ_AXES; + numVisibleAxes = numTotalAxes = XYZ_AXES; // must set this up before calling Reset() numExtruders = MaxExtruders; + Reset(); + distanceScale = 1.0; arcSegmentLength = DefaultArcSegmentLength; rawExtruderTotal = 0.0; @@ -101,17 +92,12 @@ void GCodes::Init() eofString = EOF_STRING; eofStringCounter = 0; eofStringLength = strlen(eofString); - offSetSet = false; runningConfigFile = false; doingToolChange = false; toolChangeParam = DefaultToolChangeParam; active = true; longWait = platform.Time(); limitAxes = true; - for(size_t axis = 0; axis < MaxAxes; axis++) - { - axisScaleFactors[axis] = 1.0; - } SetAllAxesNotHomed(); for (size_t i = 0; i < NUM_FANS; ++i) { @@ -121,7 +107,7 @@ void GCodes::Init() retractLength = DefaultRetractLength; retractExtra = 0.0; - retractHop = 0.0; + currentZHop = retractHop = 0.0; retractSpeed = unRetractSpeed = DefaultRetractSpeed * SecondsToMinutes; isRetracted = false; lastAuxStatusReportType = -1; // no status reports requested yet @@ -149,16 +135,28 @@ void GCodes::Reset() fileToPrint.Close(); fileBeingWritten = NULL; - probeCount = 0; - cannedCycleMoveCount = 0; - cannedCycleMoveQueued = false; speedFactor = SecondsToMinutes; // default is just to convert from mm/minute to mm/second + for (size_t i = 0; i < MaxExtruders; ++i) { extrusionFactors[i] = 1.0; } - reprap.GetMove().GetKinematics().GetAssumedInitialPosition(numVisibleAxes, moveBuffer.coords); + for (size_t i = 0; i < MaxAxes; ++i) + { + axisOffsets[i] = 0.0; + axisScaleFactors[i] = 1.0; + } + + ClearMove(); + ClearBabyStepping(); moveBuffer.xAxes = DefaultXAxisMapping; +#if SUPPORT_IOBITS + moveBuffer.ioBits = 0; +#endif + + reprap.GetMove().GetKinematics().GetAssumedInitialPosition(numVisibleAxes, moveBuffer.coords); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); + for (size_t i = numTotalAxes; i < DRIVES; ++i) { moveBuffer.coords[i] = 0.0; @@ -167,9 +165,6 @@ void GCodes::Reset() pauseRestorePoint.Init(); toolChangeRestorePoint.Init(); - ClearMove(); - ClearBabyStepping(); - for (size_t i = 0; i < MaxTriggers; ++i) { triggers[i].Init(); @@ -180,6 +175,7 @@ void GCodes::Reset() simulationTime = 0.0; isPaused = false; doingToolChange = false; + doingManualBedProbe = false; moveBuffer.filePos = noFilePosition; lastEndstopStates = platform.GetAllEndstopStates(); firmwareUpdateModuleMap = 0; @@ -193,11 +189,6 @@ void GCodes::Reset() } } -void GCodes::ClearBabyStepping() -{ - pendingBabyStepZOffset = currentBabyStepZOffset = 0.0; -} - bool GCodes::DoingFileMacro() const { return fileGCode->IsDoingFileMacro(); @@ -251,7 +242,14 @@ void GCodes::Spin() if (gb.GetState() == GCodeState::normal) { - StartNextGCode(gb, reply); + if (gb.MachineState().messageAcknowledged) + { + Pop(gb); + } + else + { + StartNextGCode(gb, reply); + } } else { @@ -261,8 +259,25 @@ void GCodes::Spin() switch (gb.GetState()) { case GCodeState::waitingForMoveToComplete: - if (LockMovementAndWaitForStandstill(gb)) + if (LockMovementAndWaitForStandstill(gb)) // movement should already be locked, but we need to wait for standstill and fetch the current position { + for (size_t axis = 0; axis < numVisibleAxes; ++axis) + { + if ((axesToSenseLength & (1 << axis)) != 0) + { + EndStopType stopType; + bool dummy; + platform.GetEndStopConfiguration(axis, stopType, dummy); + if (stopType == EndStopType::highEndStop) + { + platform.SetAxisMaximum(axis, moveBuffer.coords[axis]); + } + else if (stopType == EndStopType::lowEndStop) + { + platform.SetAxisMinimum(axis, moveBuffer.coords[axis]); + } + } + } gb.SetState(GCodeState::normal); } break; @@ -487,6 +502,7 @@ void GCodes::Spin() gb.SetState(GCodeState::normal); break; + // States used for grid probing case GCodeState::gridProbing1: // ready to move to next grid probe point { // Move to the current probe point @@ -515,7 +531,7 @@ void GCodes::Spin() } break; - case GCodeState::gridProbing2: // ready to probe the current grid probe point + case GCodeState::gridProbing2: // ready to probe the current grid probe point if (LockMovementAndWaitForStandstill(gb)) { lastProbedTime = millis(); @@ -528,41 +544,61 @@ void GCodes::Spin() { // Probe the bed at the current XY coordinates // Check for probe already triggered at start - if (reprap.GetPlatform().GetZProbeResult() == EndStopHit::lowHit) + if (platform.GetZProbeType() == 0) + { + // No Z probe, so do manual mesh levelling instead + UnlockAll(gb); // release the movement lock to allow manual Z moves + gb.AdvanceState(); // resume at next state when user has finished adjusting the height + doingManualBedProbe = true; // suspend the Z movement limit + DoManualProbe(gb); + } + else 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; - platform.SetProbing(true); - moveBuffer.moveType = 0; - moveBuffer.endStopsToCheck = ZProbeActive; - moveBuffer.usePressureAdvance = false; - moveBuffer.filePos = noFilePosition; - moveBuffer.coords[Z_AXIS] = -platform.GetZProbeDiveHeight(); - moveBuffer.feedRate = platform.GetCurrentZProbeParameters().probeSpeed; - moveBuffer.xAxes = DefaultXAxisMapping; - segmentsLeft = 1; - gb.SetState(GCodeState::gridProbing3); + else + { + zProbeTriggered = false; + platform.SetProbing(true); + moveBuffer.moveType = 0; + moveBuffer.endStopsToCheck = ZProbeActive; + moveBuffer.usePressureAdvance = false; + moveBuffer.filePos = noFilePosition; + moveBuffer.coords[Z_AXIS] = -platform.GetZProbeDiveHeight(); + moveBuffer.feedRate = platform.GetCurrentZProbeParameters().probeSpeed; + moveBuffer.xAxes = DefaultXAxisMapping; + segmentsLeft = 1; + gb.AdvanceState(); + } } break; case GCodeState::gridProbing3: // ready to lift the probe after probing the current grid probe point if (LockMovementAndWaitForStandstill(gb)) { - platform.SetProbing(false); - if (!zProbeTriggered) + doingManualBedProbe = false; + float heightError; + if (platform.GetZProbeType() == 0) { - reply.copy("Z probe was not triggered during probing move"); - error = true; - gb.SetState(GCodeState::normal); - break; + // No Z probe, so we are doing manual mesh levelling. Take the current Z height as the height error. + heightError = moveBuffer.coords[Z_AXIS]; } + else + { + platform.SetProbing(false); + 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(); + heightError = moveBuffer.coords[Z_AXIS] - platform.ZProbeStopHeight(); + } reprap.GetMove().AccessBedProbeGrid().SetGridHeight(gridXindex, gridYindex, heightError); // Move back up to the dive height @@ -632,6 +668,192 @@ void GCodes::Spin() } break; + // States used for G30 probing + case GCodeState::probingAtPoint1: + // Initial state when executing G30 with a P parameter. The move to raise/lower the head to the correct dive height has been commanded. + if (LockMovementAndWaitForStandstill(gb)) + { + // Head is at the dive height but needs to be moved to the correct XY position. + // The XY coordinates have already been stored. + moveBuffer.moveType = 0; + moveBuffer.endStopsToCheck = 0; + moveBuffer.usePressureAdvance = false; + moveBuffer.filePos = noFilePosition; + (void)reprap.GetMove().GetProbeCoordinates(g30ProbePointIndex, moveBuffer.coords[X_AXIS], moveBuffer.coords[Y_AXIS], true); + moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight(); + moveBuffer.feedRate = platform.GetZProbeTravelSpeed(); + moveBuffer.xAxes = DefaultXAxisMapping; + segmentsLeft = 1; + + gb.AdvanceState(); + } + break; + + case GCodeState::probingAtPoint2: + // Executing G30 with a P parameter. The move to put the head at the specified XY coordinates has been commanded. + // OR initial state when executing G30 with no P parameter + if (LockMovementAndWaitForStandstill(gb)) + { + // Head has finished moving to the correct XY position + lastProbedTime = millis(); // start the probe recovery timer + gb.AdvanceState(); + } + break; + + case GCodeState::probingAtPoint3: + // Executing G30 with a P parameter. The move to put the head at the specified XY coordinates has been completed and the recovery timer started. + // OR executing G30 without a P parameter, and the recovery timer has been started. + if (millis() - lastProbedTime >= (uint32_t)(platform.GetCurrentZProbeParameters().recoveryTime * SecondsToMillis)) + { + // The probe recovery time has elapsed, so we can start the probing move + if (platform.GetZProbeType() == 0) + { + // No Z probe, so we are doing manual 'probing' + UnlockAll(gb); // release the movement lock to allow manual Z moves + gb.AdvanceState(); // resume at the next state when the user has finished + doingManualBedProbe = true; // suspend the Z movement limit + DoManualProbe(gb); + } + else if (reprap.GetPlatform().GetZProbeResult() == EndStopHit::lowHit) // check for probe already triggered at start + { + // 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"); + reprap.GetMove().SetZBedProbePoint(g30ProbePointIndex, platform.GetZProbeDiveHeight(), true, true); + if (g30ProbePointIndex < 0) + { + // G30 with no P parameter + gb.SetState(GCodeState::normal); + } + else + { + gb.AdvanceState(); // skip probing + gb.AdvanceState(); // skip recovering to the dive height + } + } + else + { + zProbeTriggered = false; + platform.SetProbing(true); + moveBuffer.moveType = 0; + moveBuffer.endStopsToCheck = ZProbeActive; + moveBuffer.usePressureAdvance = false; + moveBuffer.filePos = noFilePosition; + moveBuffer.coords[Z_AXIS] = (GetAxisIsHomed(Z_AXIS)) + ? -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 + moveBuffer.feedRate = platform.GetCurrentZProbeParameters().probeSpeed; + moveBuffer.xAxes = DefaultXAxisMapping; + segmentsLeft = 1; + gb.AdvanceState(); + } + } + break; + + case GCodeState::probingAtPoint4: + // Executing G30. The probe wasn't triggered at the start of the move, and the probing move has been commanded. + if (LockMovementAndWaitForStandstill(gb)) + { + doingManualBedProbe = false; + bool probingError = false; + float heightError; + if (platform.GetZProbeType() == 0) + { + // No Z probe, so we are doing manual mesh levelling. Take the current Z height as the height error. + heightError = moveBuffer.coords[Z_AXIS]; + } + else + { + platform.SetProbing(false); + if (!zProbeTriggered) + { + reply.copy("Z probe was not triggered during probing move"); + heightError = 0.0; + probingError = true; + } + else + { + // Successful probing + float heightAdjust = 0.0; + bool dummy; + gb.TryGetFValue('H', heightAdjust, dummy); + heightError = moveBuffer.coords[Z_AXIS] - (platform.ZProbeStopHeight() + heightAdjust); + } + } + + if (g30ProbePointIndex < 0) + { + // Simple G30 probing move + if (!probingError) + { + if (gb.Seen('S') && gb.GetIValue() < 0) + { + float m[DRIVES]; + reprap.GetMove().GetCurrentMachinePosition(m, false); // get height without bed compensation + reply.printf("Stopped at height %.3f mm", m[Z_AXIS]); + } + else + { + // Reset the Z axis origin according to the height error + moveBuffer.coords[Z_AXIS] -= heightError; + reprap.GetMove().SetNewPosition(moveBuffer.coords, false); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); + SetAxisIsHomed(Z_AXIS); + lastProbedZ = 0.0; + } + } + gb.SetState(GCodeState::normal); + } + else + { + // Probing with a probe point index number + if (!GetAxisIsHomed(Z_AXIS)) + { + // The Z axis has not yet been homed, so treat this probe as a homing move. + moveBuffer.coords[Z_AXIS] -= heightError; // reset the Z origin + reprap.GetMove().SetNewPosition(moveBuffer.coords, false); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); + SetAxisIsHomed(Z_AXIS); + heightError = 0.0; + } + reprap.GetMove().SetZBedProbePoint(g30ProbePointIndex, heightError, true, probingError); + gb.AdvanceState(); + } + } + break; + + case GCodeState::probingAtPoint5: + if (gb.Seen('S')) + { + const 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; + reprap.GetMove().SetNewPosition(moveBuffer.coords, false); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); + lastProbedZ = 0.0; + } + else + { + reprap.GetMove().FinishedBedProbing(sParam, reply); + } + } + + // The probing move has completed or been abandoned + // Move back up to the dive height + moveBuffer.moveType = 0; + moveBuffer.endStopsToCheck = 0; + moveBuffer.usePressureAdvance = false; + moveBuffer.filePos = noFilePosition; + moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight(); + moveBuffer.feedRate = platform.GetZProbeTravelSpeed(); + moveBuffer.xAxes = DefaultXAxisMapping; + segmentsLeft = 1; + gb.SetState(GCodeState::normal); + break; + + // Firmware retraction/un-retraction states case GCodeState::doingFirmwareRetraction: // We just did the retraction part of a firmware retraction, now we need to do the Z hop if (segmentsLeft == 0) @@ -644,6 +866,7 @@ void GCodes::Spin() } moveBuffer.feedRate = platform.MaxFeedrate(Z_AXIS); moveBuffer.coords[Z_AXIS] += retractHop; + currentZHop = retractHop; moveBuffer.moveType = 0; moveBuffer.isFirmwareRetraction = true; moveBuffer.usePressureAdvance = false; @@ -917,26 +1140,25 @@ 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 < numVisibleAxes; ++drive) - { - pauseRestorePoint.moveCoords[drive] = moveBuffer.coords[drive]; - } - for (size_t drive = numTotalAxes; drive < DRIVES; ++drive) - { - pauseRestorePoint.moveCoords[drive] = lastRawExtruderPosition[drive - numTotalAxes]; // get current extruder positions into pausedMoveBuffer - } - pauseRestorePoint.feedRate = gb.MachineState().feedrate; + SavePosition(pauseRestorePoint, gb); } else { // Pausing a file print via another input source pauseRestorePoint.feedRate = fileGCode->MachineState().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 + +#if SUPPORT_IOBITS + pauseRestorePoint.ioBits = moveBuffer.ioBits; // the call to PausePrint may or may not change this +#endif + + FilePosition fPos = reprap.GetMove().PausePrint(pauseRestorePoint, reprap.GetCurrentXAxes()); // tell Move we wish to pause the current print + ToolOffsetInverseTransform(pauseRestorePoint.moveCoords, moveBuffer.coords); // transform the returned coordinates to user coordinates + reprap.GetMove().SetNewPosition(moveBuffer.coords, true); + FileData& fdata = fileGCode->MachineState().fileState; if (fPos != noFilePosition && fdata.IsLive()) { - fdata.Seek(fPos); // replay the abandoned instructions if/when we resume + fdata.Seek(fPos); // replay the abandoned instructions when we resume } fileInput->Reset(); codeQueue->PurgeEntries(); @@ -1009,6 +1231,7 @@ bool GCodes::LockMovementAndWaitForStandstill(const GCodeBuffer& gb) // Get the current positions. These may not be the same as the ones we remembered from last time if we just did a special move. reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes()); memcpy(moveBuffer.initialCoords, moveBuffer.coords, numVisibleAxes * sizeof(moveBuffer.initialCoords[0])); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); return true; } @@ -1054,7 +1277,7 @@ bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType) } moveBuffer.feedRate = gb.MachineState().feedrate; - // First do extrusion, and check, if we are extruding, that we have a tool to extrude with + // If we are extruding, check that we have a tool to extrude with if (gb.Seen(extrudeLetter)) { moveBuffer.hasExtrusion = true; @@ -1068,8 +1291,7 @@ bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType) const 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 + // Set the drive values for this tool if (tool->GetMixing()) { const float moveArg = gb.GetFValue() * distanceScale; @@ -1107,30 +1329,18 @@ bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType) 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) - { - 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 + numTotalAxes] = extrusionAmount * extrusionFactors[drive]; - } + const float extrusionAmount = (gb.MachineState().drivesRelative) + ? moveArg + : moveArg - lastRawExtruderPosition[drive]; + lastRawExtruderPosition[drive] += extrusionAmount; + rawExtruderTotalByDrive[drive] += extrusionAmount; + rawExtruderTotal += extrusionAmount; + moveBuffer.coords[drive + numTotalAxes] = extrusionAmount * extrusionFactors[drive]; } } } @@ -1138,134 +1348,30 @@ bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType) return true; } -// Set up the axis coordinates of a move for the Move class -// 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 the number of segments in the move -unsigned int GCodes::LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType) -{ - const Tool * const currentTool = reprap.GetCurrentTool(); - unsigned int numSegments = 1; - for (size_t axis = 0; axis < numVisibleAxes; 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 < numVisibleAxes; ++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 - } - const HeightMap& heightMap = reprap.GetMove().AccessBedProbeGrid(); - if (heightMap.UsingHeightMap()) - { - const unsigned int minSegments = heightMap.GetMinimumSegments(fabs(mappedMoveArg - moveBuffer.coords[mappedAxis])); - if (minSegments > numSegments) - { - numSegments = minSegments; - } - } - moveBuffer.coords[mappedAxis] = mappedMoveArg; - } - } - } - else - { - if (gb.MachineState().axesRelative) - { - moveArg += moveBuffer.coords[axis]; - } - else if (moveType == 0) - { - moveArg += currentBabyStepZOffset; - if (axis == Z_AXIS && isRetracted) - { - moveArg += retractHop; // handle firmware retraction on layer change - } - if (currentTool != nullptr) - { - moveArg -= currentTool->GetOffset()[axis]; // adjust requested position to compensate for tool offset - } - } - - if (axis != Z_AXIS && moveType == 0) - { - // Segment the move if necessary - const HeightMap& heightMap = reprap.GetMove().AccessBedProbeGrid(); - if (heightMap.UsingHeightMap()) - { - const unsigned int minSegments = reprap.GetMove().AccessBedProbeGrid().GetMinimumSegments(fabs(moveArg - moveBuffer.coords[axis])); - if (minSegments > numSegments) - { - numSegments = minSegments; - } - } - } - moveBuffer.coords[axis] = moveArg; - } - } - } - - // If doing a regular move and applying limits, limit all axes - if ( ( (moveType == 0 && limitAxes) - || moveType == -1 // always limit G92 commands, for the benefit of SCARA machines - ) -#if SUPPORT_ROLAND - && !reprap.GetRoland()->Active() -#endif - ) - { - reprap.GetMove().GetKinematics().LimitPosition(moveBuffer.coords, numVisibleAxes, axesHomed); - } - - return numSegments; -} - -// 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) +// Execute a straight move returning true if an error was written to 'reply' +// We have already acquired the movement lock and waited for the previous move to be taken. +bool GCodes::DoStraightMove(GCodeBuffer& gb, StringRef& reply) { - // Last one gone yet? - if (segmentsLeft != 0) - { - return 0; - } - - // Check to see if the move is a 'homing' move that endstops are checked on. + // Set up default move parameters moveBuffer.endStopsToCheck = 0; moveBuffer.moveType = 0; doingArcMove = false; moveBuffer.xAxes = reprap.GetCurrentXAxes(); + moveBuffer.usePressureAdvance = false; + moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() - fileInput->BytesCached() : noFilePosition; + axesToSenseLength = 0; + + // Check to see if the move is a 'homing' move that endstops are checked on. if (gb.Seen('S')) { int ival = gb.GetIValue(); - if (ival == 1 || ival == 2) + if (ival == 1 || ival == 2 || ival == 3) { moveBuffer.moveType = ival; moveBuffer.xAxes = DefaultXAxisMapping; } - if (ival == 1) + if (ival == 1 || ival == 3) { for (size_t i = 0; i < numTotalAxes; ++i) { @@ -1274,6 +1380,15 @@ int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply) moveBuffer.endStopsToCheck |= (1u << i); } } + + if (ival == 1) + { + moveBuffer.endStopsToCheck |= HomeAxes; + } + else + { + axesToSenseLength = moveBuffer.endStopsToCheck; + } } else if (ival == 99) // temporary code to log Z probe change positions { @@ -1281,6 +1396,7 @@ int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply) } } + // Check for damaging moves on a delta printer if (reprap.GetMove().GetKinematics().GetKinematicsType() == KinematicsType::linearDelta) { // Extra checks to avoid damaging delta printers @@ -1289,7 +1405,7 @@ int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply) // 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; + return true; } if (moveBuffer.moveType == 0 && !AllAxesAreHomed()) @@ -1299,78 +1415,142 @@ int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply) if (gb.Seen(axisLetters[X_AXIS]) || gb.Seen(axisLetters[Y_AXIS]) || gb.Seen(axisLetters[Z_AXIS])) { displayDeltaNotHomedWarning = true; - return 1; + return false; } } } - // Load the last position into moveBuffer -#if SUPPORT_ROLAND - if (reprap.GetRoland()->Active()) + moveBuffer.canPauseAfter = (moveBuffer.endStopsToCheck == 0); + + // Check for 'R' parameter to move relative to a restore point + int rParam = (moveBuffer.moveType == 0 && gb.Seen('R')) ? gb.GetIValue() : 0; + const RestorePoint * const rp = (rParam == 1) ? &pauseRestorePoint : (rParam == 2) ? &toolChangeRestorePoint : nullptr; + +#if SUPPORT_IOBITS + // Update the iobits parameter + if (rp != nullptr) + { + moveBuffer.ioBits = rp->ioBits; + } + else if (gb.Seen('P')) { - reprap.GetRoland()->GetCurrentRolandPosition(moveBuffer); + moveBuffer.ioBits = (IoBits_t)gb.GetIValue(); } else + { + // Leave moveBuffer.ioBits alone so that we keep the previous value + } #endif + + if (moveBuffer.moveType != 0) { + // This is a raw motor move, so we need the current raw motor positions in moveBuffer.coords reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, moveBuffer.moveType, reprap.GetCurrentXAxes()); } - // Load the move buffer with either the absolute movement required or the relative movement required + // Set up the initial coordinates memcpy(moveBuffer.initialCoords, moveBuffer.coords, numVisibleAxes * sizeof(moveBuffer.initialCoords[0])); - if (LoadExtrusionAndFeedrateFromGCode(gb, moveBuffer.moveType)) + + // Deal with XYZ movement + const float initialX = currentUserPosition[X_AXIS]; + const float initialY = currentUserPosition[Y_AXIS]; + for (size_t axis = 0; axis < numVisibleAxes; axis++) { - segmentsLeft = LoadMoveBufferFromGCode(gb, moveBuffer.moveType); - if (segmentsLeft != 0) + if (gb.Seen(axisLetters[axis])) { - // 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 XYU.. movement. - // The movement code will only apply pressure advance if there is forward extrusion, so we only need to check for XYU.. movement here. - moveBuffer.usePressureAdvance = false; - for (size_t axis = 0; axis < numVisibleAxes; ++axis) + const float moveArg = gb.GetFValue() * distanceScale; + if (moveBuffer.moveType != 0) { - if (axis != Z_AXIS && moveBuffer.coords[axis] != moveBuffer.initialCoords[axis]) + if (gb.MachineState().axesRelative) { - moveBuffer.usePressureAdvance = true; - break; + moveBuffer.coords[axis] += moveArg; + } + else + { + moveBuffer.coords[axis] = moveArg; } } - moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() - fileInput->BytesCached() : noFilePosition; - moveBuffer.canPauseAfter = (moveBuffer.endStopsToCheck == 0); + else if (rp != nullptr) + { + currentUserPosition[axis] = moveArg + rp->moveCoords[axis]; + } + else if (gb.MachineState().axesRelative) + { + currentUserPosition[axis] += moveArg; + } + else + { + currentUserPosition[axis] = moveArg; + } + } + else if (rp != nullptr) + { + currentUserPosition[axis] = rp->moveCoords[axis]; + } + } + + // Deal with extrusion and feed rate + LoadExtrusionAndFeedrateFromGCode(gb, moveBuffer.moveType); - if (moveBuffer.moveType == 0) + if (moveBuffer.moveType != 0) + { + // It's a raw motor move, so do it in a single segment and wait for it to complete + segmentsLeft = 1; + gb.SetState(GCodeState::waitingForMoveToComplete); + } + else + { + // Apply tool offset, baby stepping, Z hop and axis scaling + ToolOffsetTransform(currentUserPosition, moveBuffer.coords, true); + uint32_t effectiveAxesHomed = axesHomed; + if (doingManualBedProbe) + { + effectiveAxesHomed &= ~(1 << Z_AXIS); // if doing a manual Z probe, son't limit the Z movement + } + if (limitAxes && reprap.GetMove().GetKinematics().LimitPosition(moveBuffer.coords, numVisibleAxes, effectiveAxesHomed)) + { + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // make sure the limits are reflected in the user position + } + + // 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 XYU.. movement. + // The movement code will only apply pressure advance if there is forward extrusion, so we only need to check for XYU.. movement here. + for (size_t axis = 0; axis < numVisibleAxes; ++axis) + { + if (axis != Z_AXIS && moveBuffer.coords[axis] != moveBuffer.initialCoords[axis]) { - const Kinematics& kin = reprap.GetMove().GetKinematics(); - if (kin.UseSegmentation() && (moveBuffer.hasExtrusion || !kin.UseRawG0())) - { - // This kinematics approximates linear motion by means of segmentation - // Calculate the XY length of the move - float sumOfSquares = 0.0; - unsigned int numXaxes = 0; - for (size_t axis = 0; axis < numVisibleAxes; ++axis) - { - if ((moveBuffer.xAxes & (1u << axis)) != 0) - { - sumOfSquares += fsquare(moveBuffer.coords[axis] - moveBuffer.initialCoords[axis]); - ++numXaxes; - } - } - if (numXaxes > 1) - { - sumOfSquares /= numXaxes; - } - const float length = sqrtf(sumOfSquares + fsquare(moveBuffer.coords[Y_AXIS] - moveBuffer.initialCoords[Y_AXIS])); - const float moveTime = length/moveBuffer.feedRate; // this is a best-case time, often the move will take longer - segmentsLeft = max<unsigned int>(segmentsLeft, min<unsigned int>(length/kin.GetMinSegmentLength(), (unsigned int)(moveTime * kin.GetSegmentsPerSecond()))); - } + moveBuffer.usePressureAdvance = true; + break; } } + + // Apply segmentation if necessary + // Note for when we use RTOS: as soon as we set segmentsLeft nonzero, the Move process will assume that the move is ready to take, so this must be the last thing we do. + const Kinematics& kin = reprap.GetMove().GetKinematics(); + if (kin.UseSegmentation() && (moveBuffer.hasExtrusion || !kin.UseRawG0())) + { + // This kinematics approximates linear motion by means of segmentation + const float xyLength = sqrtf(fsquare(currentUserPosition[X_AXIS] - initialX) + fsquare(currentUserPosition[Y_AXIS] - initialY)); + const float moveTime = xyLength/moveBuffer.feedRate; // this is a best-case time, often the move will take longer + segmentsLeft = max<unsigned int>(1, min<unsigned int>(xyLength/kin.GetMinSegmentLength(), (unsigned int)(moveTime * kin.GetSegmentsPerSecond()))); + } + else if (reprap.GetMove().IsUsingMesh()) + { + const HeightMap& heightMap = reprap.GetMove().AccessBedProbeGrid(); + segmentsLeft = max<unsigned int>(1, heightMap.GetMinimumSegments(currentUserPosition[X_AXIS] - initialX, currentUserPosition[Y_AXIS] - initialY)); + } + else + { + segmentsLeft = 1; + } } - return (moveBuffer.moveType != 0 || moveBuffer.endStopsToCheck != 0) ? 2 : 1; + + return false; } // Execute an arc move returning true if it was badly-formed // We already have the movement lock and the last move has gone +// Currently, we do not process new babystepping when executing an arc move bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise) { // Get the axis parameters. X Y I J are compulsory, Z is optional. @@ -1383,108 +1563,75 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise) if (!gb.Seen('J')) return true; const float jParam = gb.GetFValue() * distanceScale; - // Adjust them for relative/absolute coordinates, tool offset, and X axis mapping. Also get the optional Z parameter - const Tool * const currentTool = reprap.GetCurrentTool(); - const bool axesRelative = gb.MachineState().axesRelative; memcpy(moveBuffer.initialCoords, moveBuffer.coords, numVisibleAxes * sizeof(moveBuffer.initialCoords[0])); + const bool axesRelative = gb.MachineState().axesRelative; + if (axesRelative) + { + currentUserPosition[X_AXIS] += xParam; + currentUserPosition[Y_AXIS] += yParam; + } + else + { + currentUserPosition[X_AXIS] = xParam; + currentUserPosition[Y_AXIS] = yParam; + } + + // Get the optional Z parameter if (gb.Seen('Z')) { const float zParam = gb.GetFValue() * distanceScale; if (axesRelative) { - moveBuffer.coords[Z_AXIS] += zParam; + currentUserPosition[Z_AXIS] += zParam; } else { - moveBuffer.coords[Z_AXIS] = zParam + currentBabyStepZOffset + retractHop; // handle firmware retraction on layer change - if (currentTool != nullptr) - { - moveBuffer.coords[Z_AXIS] -= currentTool->GetOffset()[Z_AXIS]; - } + currentUserPosition[Z_AXIS] = zParam; } } - // The I and J parameters are always relative to present position - arcCentre[Y_AXIS] = moveBuffer.initialCoords[Y_AXIS] + jParam; - - if (currentTool != nullptr) + ToolOffsetTransform(currentUserPosition, moveBuffer.coords, true); // set the final position + if (limitAxes && reprap.GetMove().GetKinematics().LimitPosition(moveBuffer.coords, numVisibleAxes, axesHomed)) { - // Record which axes behave like an X axis - arcAxesMoving = currentTool->GetXAxisMap() & ~((1 << Y_AXIS) | (1 << Z_AXIS)); - - // Sort out the Y axis - if (axesRelative) - { - moveBuffer.coords[Y_AXIS] += yParam; - } - else - { - moveBuffer.coords[Y_AXIS] = yParam - currentTool->GetOffset()[Y_AXIS]; - } - - // Deal with the X axes - for (size_t axis = 0; axis < numVisibleAxes; ++axis) - { - if (axis != Y_AXIS) - { - arcCentre[axis] = moveBuffer.initialCoords[axis] + iParam; - if ((arcAxesMoving & (1 << axis)) != 0) - { - if (axesRelative) - { - moveBuffer.coords[axis] += xParam; - } - else - { - moveBuffer.coords[axis] = xParam - currentTool->GetOffset()[axis]; - } - } - } - } + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // make sure the limits are reflected in the user position } - else + + // Set up the arc centre coordinates and record which axes behave like an X axis. + // The I and J parameters are always relative to present position. + // For X we need to set up the arc centre for each axis that X is mapped to. + // For simplicity we assume that X may be mapped to all axes except Y, so we set up the arc centre for all of those axes. + arcAxesMoving = reprap.GetCurrentXAxes() & ~((1 << Y_AXIS) | (1 << Z_AXIS)); + for (size_t axis = 0; axis < numVisibleAxes; ++axis) { - arcAxesMoving = (1 << X_AXIS); - arcCentre[X_AXIS] = moveBuffer.initialCoords[X_AXIS] + iParam; - if (axesRelative) - { - moveBuffer.coords[X_AXIS] += xParam; - moveBuffer.coords[Y_AXIS] += yParam; - } - else - { - moveBuffer.coords[X_AXIS] = xParam; - moveBuffer.coords[Y_AXIS] = yParam; - } + arcCentre[axis] = moveBuffer.initialCoords[axis] + (axis == Y_AXIS) ? jParam : iParam; } moveBuffer.endStopsToCheck = 0; moveBuffer.moveType = 0; moveBuffer.xAxes = reprap.GetCurrentXAxes(); - if (LoadExtrusionAndFeedrateFromGCode(gb, moveBuffer.moveType)) // this reports an error if necessary, so no need to return true if it fails + LoadExtrusionAndFeedrateFromGCode(gb, moveBuffer.moveType); + arcRadius = sqrtf(iParam * iParam + jParam * jParam); + arcCurrentAngle = atan2(-jParam, -iParam); + const float finalTheta = atan2(moveBuffer.coords[Y_AXIS] - arcCentre[Y_AXIS], moveBuffer.coords[X_AXIS] - arcCentre[X_AXIS]); + + // Calculate the total angle moved, which depends on which way round we are going + float totalArc = (clockwise) ? arcCurrentAngle - finalTheta : finalTheta - arcCurrentAngle; + if (totalArc < 0) { - arcRadius = sqrtf(iParam * iParam + jParam * jParam); - arcCurrentAngle = atan2(-jParam, -iParam); - const float finalTheta = atan2(moveBuffer.coords[Y_AXIS] - arcCentre[Y_AXIS], moveBuffer.coords[X_AXIS] - arcCentre[X_AXIS]); + totalArc += 2 * PI; + } + arcAngleIncrement = totalArc/segmentsLeft; + if (clockwise) + { + arcAngleIncrement = -arcAngleIncrement; + } + doingArcMove = true; + moveBuffer.usePressureAdvance = true; - // Calculate the total angle moved, which depends on which way round we are going - float totalArc = (clockwise) ? arcCurrentAngle - finalTheta : finalTheta - arcCurrentAngle; - if (totalArc < 0) - { - totalArc += 2 * PI; - } - segmentsLeft = max<unsigned int>((unsigned int)((arcRadius * totalArc)/arcSegmentLength + 0.8), 1); - arcAngleIncrement = totalArc/segmentsLeft; - if (clockwise) - { - arcAngleIncrement = -arcAngleIncrement; - } - doingArcMove = true; - moveBuffer.usePressureAdvance = true; -// debugPrintf("Radius %.2f, initial angle %.1f, increment %.1f, segments %u\n", + segmentsLeft = max<unsigned int>((unsigned int)((arcRadius * totalArc)/arcSegmentLength + 0.8), 1); // must do this last for RTOS +// debugPrintf("Radius %.2f, initial angle %.1f, increment %.1f, segments %u\n", // arcRadius, arcCurrentAngle * RadiansToDegrees, arcAngleIncrement * RadiansToDegrees, segmentsLeft); - } return false; } @@ -1547,51 +1694,16 @@ bool GCodes::ReadMove(RawMove& m) --segmentsLeft; } - // Check for pending baby stepping - if (m.moveType == 0 && pendingBabyStepZOffset != 0.0) - { - // Calculate the move length, to see how much new babystepping is appropriate for this move - float xMoveLength = 0.0; - const uint32_t xAxes = reprap.GetCurrentXAxes(); - for (size_t drive = 0; drive < numVisibleAxes; ++drive) - { - if ((xAxes & (1 << drive)) != 0) - { - xMoveLength = max<float>(xMoveLength, fabs(m.coords[drive] - m.initialCoords[drive])); - } - } - const float distance = sqrtf(fsquare(xMoveLength) + fsquare(m.coords[Y_AXIS] - m.initialCoords[Y_AXIS]) + fsquare(m.coords[Z_AXIS] - m.initialCoords[Z_AXIS])); - - // The maximum Z speed change due to baby stepping that we allow is the Z jerk rate, to avoid slowing the print down too much - const float minMoveTime = distance/m.feedRate; - const float maxBabyStepping = minMoveTime * platform.ConfiguredInstantDv(Z_AXIS); - const float babySteppingToDo = constrain<float>(pendingBabyStepZOffset, -maxBabyStepping, maxBabyStepping); - m.coords[Z_AXIS] += babySteppingToDo; - m.newBabyStepping = babySteppingToDo; - moveBuffer.initialCoords[Z_AXIS] = m.coords[Z_AXIS]; - moveBuffer.coords[Z_AXIS] += babySteppingToDo; - pendingBabyStepZOffset -= babySteppingToDo; - currentBabyStepZOffset += babySteppingToDo; - } - else - { - m.newBabyStepping = 0.0; - } return true; } void GCodes::ClearMove() { - segmentsLeft = 0; doingArcMove = false; moveBuffer.endStopsToCheck = 0; moveBuffer.moveType = 0; moveBuffer.isFirmwareRetraction = false; -} - -float GCodes::GetBabyStepOffset() const -{ - return currentBabyStepZOffset + pendingBabyStepZOffset; + segmentsLeft = 0; // do this last } // 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. @@ -1631,89 +1743,64 @@ void GCodes::FileMacroCyclesReturn(GCodeBuffer& gb) } } -// 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 - } - gb.MachineState().state = gb.MachineState().previous->state; // stay in the same state - - 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 = DefaultXAxisMapping; - moveBuffer.endStopsToCheck = ce; - moveBuffer.filePos = noFilePosition; - moveBuffer.usePressureAdvance = false; - segmentsLeft = 1; - cannedCycleMoveQueued = true; - if ((ce & ZProbeActive) != 0) - { - platform.SetProbing(true); - } - } - return false; -} - // This handles G92. Return true if completed, false if it needs to be called again. bool GCodes::SetPositions(GCodeBuffer& gb) { - // Don't pause the machine if only extruder drives are being reset (DC, 2015-09-06). + // Don't wait for the machine to stop if only extruder drives are being reset. // 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 < numVisibleAxes; ++drive) + for (size_t axis = 0; axis < numVisibleAxes; ++axis) { - if (gb.Seen(axisLetters[drive])) + if (gb.Seen(axisLetters[axis])) { - includingAxes = true; - break; + const float axisValue = gb.GetFValue(); + if (!includingAxes) + { + if (!LockMovementAndWaitForStandstill(gb)) // lock movement and get current coordinates + { + return false; + } + includingAxes = true; + } + currentUserPosition[axis] = axisValue; } } - if (includingAxes) + // Handle any E parameter in the G92 command. If we get an error, ignore it and do the axes anyway. + if (gb.Seen(extrudeLetter)) { - if (!LockMovementAndWaitForStandstill(gb)) // lock movement and get current coordinates + Tool* const tool = reprap.GetCurrentTool(); + if (tool != nullptr) { - return false; + const size_t eMoveCount = tool->DriveCount(); + if (eMoveCount != 0) + { + if (tool->GetMixing()) + { + tool->virtualExtruderPosition = gb.GetFValue() * distanceScale; + } + else + { + float eMovement[MaxExtruders]; + size_t mc = eMoveCount; + gb.GetFloatArray(eMovement, mc, false); + for (size_t eDrive = 0; eDrive < eMoveCount; eDrive++) + { + lastRawExtruderPosition[tool->Drive(eDrive)] = eMovement[eDrive] * distanceScale; + } + } + } } - ClearBabyStepping(); // G92 on any axis clears pending babystepping - } - else if (segmentsLeft != 0) // wait for previous move to be taken so that GetCurrentUserPosition returns the correct value - { - return false; } - // Handle any E parameter in the G92 command. If we get an error, ignore it and do the axes anyway. - (void)LoadExtrusionAndFeedrateFromGCode(gb, -1); - if (includingAxes) { - (void)LoadMoveBufferFromGCode(gb, -1); + ToolOffsetTransform(currentUserPosition, moveBuffer.coords, true); + if (reprap.GetMove().GetKinematics().LimitPosition(moveBuffer.coords, numVisibleAxes, axesHomed)) + { + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // make sure the limits are reflected in the user position + } + reprap.GetMove().SetNewPosition(moveBuffer.coords, true); #if SUPPORT_ROLAND if (reprap.GetRoland()->Active()) @@ -1727,66 +1814,24 @@ bool GCodes::SetPositions(GCodeBuffer& gb) } } #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]. +// Offset the axes by the X, Y, and Z amounts in the M code in gb. The actual movement occurs on the next move command. +// It's not clear from the description in the reprap.org wiki whether offsets are cumulative or not. We assume they are. bool GCodes::OffsetAxes(GCodeBuffer& gb) { - if (!offSetSet) + for (size_t drive = 0; drive < numVisibleAxes; drive++) { - if (!LockMovementAndWaitForStandstill(gb)) - { - return false; - } - for (size_t drive = 0; drive < DRIVES; drive++) - { - cannedMoveType[drive] = CannedMoveType::none; - if (drive < numVisibleAxes) - { - record[drive] = moveBuffer.coords[drive]; - if (gb.Seen(axisLetters[drive])) - { - cannedMoveCoords[drive] = gb.GetFValue() * distanceScale; - cannedMoveType[drive] = CannedMoveType::relative; - } - } - else - { - record[drive] = 0.0; - } - } - - if (gb.Seen(feedrateLetter)) // Has the user specified a feedrate? - { - cannedFeedRate = gb.GetFValue() * distanceScale * SecondsToMinutes; - } - else - { - cannedFeedRate = DefaultFeedrate; - } - - offSetSet = true; - } - - if (DoCannedCycleMove(gb, 0)) - { - // Restore positions - for (size_t drive = 0; drive < DRIVES; drive++) + if (gb.Seen(axisLetters[drive])) { - moveBuffer.coords[drive] = record[drive]; + axisOffsets[drive] += gb.GetFValue() * distanceScale; } - reprap.GetMove().SetLiveCoordinates(record); // This doesn't transform record - reprap.GetMove().SetPositions(record); // This does - offSetSet = false; - return true; } - return false; + return true; } // Home one or more of the axes. Which ones are decided by the @@ -1863,246 +1908,88 @@ bool GCodes::DoHome(GCodeBuffer& gb, StringRef& reply, bool& error) 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, size_t probePointIndex, float heightAdjust) +// This is called to execute a G30. We already own the movement lock. +// 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. +// Return true if an error occurs. +// We already own the movement lock before this is called. +bool GCodes::ExecuteG30(GCodeBuffer& gb, StringRef& reply) { - 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) + g30ProbePointIndex = -1; + bool seen = false; + gb.TryGetIValue('P', g30ProbePointIndex, seen); + if (seen) { - 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.GetZProbeStartingHeight(); - cannedMoveType[Z_AXIS] = CannedMoveType::absolute; - cannedFeedRate = platform.GetZProbeTravelSpeed(); - if (DoCannedCycleMove(gb, 0)) + if (g30ProbePointIndex < 0 || g30ProbePointIndex >= (int)MaxProbePoints) { - cannedCycleMoveCount++; + reply.copy("Z probe point index out of range"); + return true; } - return false; - - case 1: // Move to the correct XY coordinates - (void)reprap.GetMove().GetProbeCoordinates(probePointIndex, cannedMoveCoords[X_AXIS], cannedMoveCoords[Y_AXIS], true); - 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)) + else { - lastProbedTime = millis(); - cannedCycleMoveCount++; - } - return false; + // Set the specified probe point index to the specified coordinates + const float x = (gb.Seen(axisLetters[X_AXIS])) ? gb.GetFValue() : moveBuffer.coords[X_AXIS]; + const float y = (gb.Seen(axisLetters[Y_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Y_AXIS]; + const float z = (gb.Seen(axisLetters[Z_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Z_AXIS]; + reprap.GetMove().SetXYBedProbePoint((size_t)g30ProbePointIndex, x, y); - case 2: // Probe the bed - if (millis() - lastProbedTime >= (uint32_t)(platform.GetCurrentZProbeParameters().recoveryTime * SecondsToMillis)) - { - 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)) + if (z > SILLY_Z_VALUE) { - 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 + // Just set the height error to the specified Z coordinate + reprap.GetMove().SetZBedProbePoint((size_t)g30ProbePointIndex, z, false, false); + if (gb.Seen('S')) { - // 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().FinishedBedProbing(gb.GetIValue(), reply); } - reprap.GetMove().SetZBedProbePoint(probePointIndex, lastProbedZ, true, false); - cannedCycleMoveCount++; - break; - - default: - break; + } + else + { + // Do a Z probe at the specified point. Start by moving to the dive height at the current position. + moveBuffer.moveType = 0; + moveBuffer.endStopsToCheck = 0; + moveBuffer.usePressureAdvance = false; + moveBuffer.filePos = noFilePosition; + moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight(); + moveBuffer.feedRate = platform.GetZProbeTravelSpeed(); + moveBuffer.xAxes = DefaultXAxisMapping; + segmentsLeft = 1; + gb.SetState(GCodeState::probingAtPoint1); } } - return false; - - case 3: // Raise the head back up to the dive height - cannedMoveCoords[Z_AXIS] = platform.GetZProbeStartingHeight(); - 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, StringRef& reply, bool reportOnly, float heightAdjust) -{ - switch (DoZProbe(gb, 1.1 * platform.AxisTotalLength(Z_AXIS))) + else { - 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) - { - float m[DRIVES]; - reprap.GetMove().GetCurrentMachinePosition(m, false); - reply.printf("Stopped at height %.3f mm", m[Z_AXIS]); - } - else - { - moveBuffer.coords[Z_AXIS] = platform.ZProbeStopHeight() + heightAdjust; - SetPositions(moveBuffer.coords, false); // set positions WITHOUT (very important) applying bed compensation - SetAxisIsHomed(Z_AXIS); - reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes()); // update the user position - lastProbedZ = 0.0; - } - return true; - - default: // not finished yet - return false; + // G30 without P parameter. This probes the current location starting from the current position. + // if there is a negative S parameter it just reports the stopped height, else it resets the Z origin. + gb.SetState(GCodeState::probingAtPoint2); } + 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) +// Decide which device to display a message box on +MessageType GCodes::GetMessageBoxDevice(GCodeBuffer& gb) const { - // 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.GetCurrentZProbeParameters().probeSpeed; - - if (DoCannedCycleMove(gb, ZProbeActive)) + MessageType mt = gb.GetResponseMessageType(); + if (mt == GENERIC_MESSAGE) { - platform.SetProbing(false); - return (zProbeTriggered) ? 2 : 1; + // Command source was the file being printed, or a trigger. Send the message to PanelDue if there is one, else to the web server. + mt = (lastAuxStatusReportType >= 0) ? AUX_MESSAGE : HTTP_MESSAGE; } - return -1; + return mt; } -// 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) +// Do a manual bed probe. On entry the state variable is the state we want to return to when the user has finished adjusting the height. +void GCodes::DoManualProbe(GCodeBuffer& gb) { - float heightAdjust = 0.0; - bool dummy; - gb.TryGetFValue('H', heightAdjust, dummy); - - if (!gb.Seen('P')) - { - const bool reportOnly = (gb.Seen('S') && gb.GetIValue() < 0); - return DoSingleZProbe(gb, reply, reportOnly, heightAdjust); - } - - const int probePointIndex = gb.GetIValue(); - if (probePointIndex < 0 || (unsigned int)probePointIndex >= MaxProbePoints) + if (Push(gb)) // stack the machine state including the file position { - reprap.GetPlatform().Message(GENERIC_MESSAGE, "Z probe point index out of range.\n"); - return true; - } - - const float x = (gb.Seen(axisLetters[X_AXIS])) ? gb.GetFValue() : moveBuffer.coords[X_AXIS]; - const float y = (gb.Seen(axisLetters[Y_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Y_AXIS]; - const float z = (gb.Seen(axisLetters[Z_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Z_AXIS]; - - reprap.GetMove().SetXYBedProbePoint(probePointIndex, x, y); - - if (z > SILLY_Z_VALUE) - { - reprap.GetMove().SetZBedProbePoint(probePointIndex, z, false, false); - if (gb.Seen('S')) - { - reprap.GetMove().FinishedBedProbing(gb.GetIValue(), reply); - } - return true; - } - else - { - if (DoSingleZProbeAtPoint(gb, probePointIndex, heightAdjust)) - { - if (gb.Seen('S')) - { - const 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; - } + gb.MachineState().fileState.Close(); // stop reading from file + gb.MachineState().waitingForAcknowledgement = true; // flag that we are waiting for acknowledgement + const MessageType mt = GetMessageBoxDevice(gb); + platform.SendAlert(mt, "Adjust height until the nozzle just touched the bed, then press OK", 1, 0.0, true); } - - return false; } // Set or print the Z probe. Called by G31. @@ -2281,15 +2168,15 @@ bool GCodes::ProbeGrid(GCodeBuffer& gb, StringRef& reply) bool GCodes::LoadHeightMap(GCodeBuffer& gb, StringRef& reply) const { reprap.GetMove().SetIdentityTransform(); // stop using old-style bed compensation and clear the height map - const char* heightMapFileName; - if (gb.SeenAfterSpace('P')) - { - heightMapFileName = gb.GetString(); - } - else + + char heightMapFileName[FILENAME_LENGTH]; + bool seen; + gb.TryGetQuotedString('P', heightMapFileName, ARRAY_SIZE(heightMapFileName), seen); + if (!seen) { - heightMapFileName = DefaultHeightMapFile; + strcpy(heightMapFileName, DefaultHeightMapFile); } + FileStore * const f = platform.GetFileStore(platform.GetSysDir(), heightMapFileName, false); if (f == nullptr) { @@ -2318,19 +2205,12 @@ bool GCodes::LoadHeightMap(GCodeBuffer& gb, StringRef& reply) const // Called by G29 and M374. Both use the P parameter to provide the filename. bool GCodes::SaveHeightMap(GCodeBuffer& gb, StringRef& reply) const { - const char* heightMapFileName; - if (gb.SeenAfterSpace('P')) + char heightMapFileName[FILENAME_LENGTH]; + bool seen; + gb.TryGetQuotedString('P', heightMapFileName, ARRAY_SIZE(heightMapFileName), seen); + if (!seen) { - heightMapFileName = gb.GetString(); - if (heightMapFileName[0] == 0) - { - reply.cat("No height map file name provided"); - return false; // no file name provided, which is legitimate for G29 - } - } - else - { - heightMapFileName = DefaultHeightMapFile; + strcpy(heightMapFileName, DefaultHeightMapFile); } FileStore * const f = platform.GetFileStore(platform.GetSysDir(), heightMapFileName, true); @@ -3108,7 +2988,7 @@ bool GCodes::SetHeaterParameters(GCodeBuffer& gb, StringRef& reply) Heat& heat = reprap.GetHeat(); const int oldChannel = heat.GetHeaterChannel(heater); bool seen = false; - long channel = oldChannel; + int32_t channel = oldChannel; gb.TryGetIValue('X', channel, seen); if (!seen && oldChannel < 0) { @@ -3173,9 +3053,8 @@ bool GCodes::RetractFilament(GCodeBuffer& gb, bool retract) { return false; } -#if 1 - // New code does the retraction and the Z hop as separate moves + // New code does the retraction and the Z hop as separate moves // Get ready to generate a move const uint32_t xAxes = reprap.GetCurrentXAxes(); reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, xAxes); @@ -3213,6 +3092,7 @@ bool GCodes::RetractFilament(GCodeBuffer& gb, bool retract) // Set up the reverse Z hop move moveBuffer.feedRate = platform.MaxFeedrate(Z_AXIS); moveBuffer.coords[Z_AXIS] -= retractHop; + currentZHop = 0.0; moveBuffer.canPauseAfter = false; // don't pause in the middle of a command segmentsLeft = 1; gb.SetState(GCodeState::doingFirmwareUnRetraction); @@ -3232,38 +3112,6 @@ bool GCodes::RetractFilament(GCodeBuffer& gb, bool retract) segmentsLeft = 1; } } -#else - // Old code to do a single synchronised move - const uint32_t xAxes = reprap.GetCurrentXAxes(); - reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, xAxes); - 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 || retractLength == 0.0) - ? speedToUse - : speedToUse * retractHop/retractLength; - moveBuffer.coords[Z_AXIS] += (retract) ? retractHop : -retractHop; - const float lengthToUse = (retract) ? -retractLength : retractLength + retractExtra; - const Tool * const tool = reprap.GetCurrentTool(); - if (tool != nullptr) - { - for (size_t i = 0; i < tool->DriveCount(); ++i) - { - moveBuffer.coords[numAxes + tool->Drive(i)] = lengthToUse; - } - } - - moveBuffer.moveType = 0; - moveBuffer.isFirmwareRetraction = true; - moveBuffer.usePressureAdvance = false; - moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() - fileInput->BytesCached() : noFilePosition; - moveBuffer.canPauseAfter = !retract; // don't pause after a retraction because that could cause too much retraction - moveBuffer.xAxes = xAxes; - segmentsLeft = 1; -#endif isRetracted = retract; } return true; @@ -3319,13 +3167,105 @@ bool GCodes::ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling } // Set the current position, optionally applying bed and axis compensation -void GCodes::SetPositions(const float positionNow[DRIVES], bool doBedCompensation) +void GCodes::SetMachinePosition(const float positionNow[DRIVES], bool doBedCompensation) { - float newPos[DRIVES]; - memcpy(newPos, positionNow, sizeof(newPos)); // copy to local storage because Transform modifies it - reprap.GetMove().AxisAndBedTransform(newPos, reprap.GetCurrentXAxes(), doBedCompensation); - reprap.GetMove().SetLiveCoordinates(newPos); - reprap.GetMove().SetPositions(newPos); + memcpy(moveBuffer.coords, positionNow, sizeof(moveBuffer.coords[0] * numTotalAxes)); + reprap.GetMove().SetNewPosition(positionNow, doBedCompensation); +} + +// Get the current position from the Move class +void GCodes::GetCurrentUserPosition() +{ + reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes()); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); +} + +// Save position to a restore point +void GCodes::SavePosition(RestorePoint& rp, const GCodeBuffer& gb) const +{ + for (size_t axis = 0; axis < numVisibleAxes; ++axis) + { + rp.moveCoords[axis] = currentUserPosition[axis]; + } + //TODO what about virtual extruder positions for mixing extruders? + for (size_t drive = numTotalAxes; drive < DRIVES; ++drive) + { + rp.moveCoords[drive] = lastRawExtruderPosition[drive - numTotalAxes]; // get current extruder positions into pausedMoveBuffer + } + rp.feedRate = gb.MachineState().feedrate; +#if SUPPORT_IOBITS + rp.ioBits = moveBuffer.ioBits; +#endif +} + +// Restore user position form a restore point +void GCodes::RestorePosition(const RestorePoint& rp, GCodeBuffer& gb) +{ + for (size_t axis = 0; axis < numVisibleAxes; ++axis) + { + currentUserPosition[axis] = rp.moveCoords[axis]; + } + gb.MachineState().feedrate = rp.feedRate; +#if SUPPORT_IOBITS + moveBuffer.ioBits = rp.ioBits; +#endif +} + +// Convert user coordinates to head reference point coordinates, optionally allowing for X axis mapping +void GCodes::ToolOffsetTransform(const float coordsIn[MaxAxes], float coordsOut[MaxAxes], bool mapXAxis) +{ + const Tool * const currentTool = reprap.GetCurrentTool(); + if (currentTool == nullptr) + { + for (size_t axis = 0; axis < numVisibleAxes; ++axis) + { + coordsOut[axis] = (coordsIn[axis] * axisScaleFactors[axis]) - axisOffsets[axis]; + } + } + else + { + const uint32_t xAxes = (mapXAxis) ? currentTool->GetXAxisMap() : DefaultXAxisMapping; + for (size_t axis = 0; axis < numVisibleAxes; ++axis) + { + const float totalOffset = currentTool->GetOffset()[axis] + axisOffsets[axis]; + const size_t inputAxis = ((xAxes & (1u << axis)) != 0) ? X_AXIS : axis; + coordsOut[axis] = (coordsIn[inputAxis] * axisScaleFactors[axis]) - totalOffset; + } + } + coordsOut[Z_AXIS] += (currentZHop + currentBabyStepZOffset); +} + +// Convert head reference point coordinates to user coordinates, allowing for X axis mapping +// Caution: coordsIn and coordsOut may address the same array! +void GCodes::ToolOffsetInverseTransform(const float coordsIn[MaxAxes], float coordsOut[MaxAxes]) +{ + const Tool * const currentTool = reprap.GetCurrentTool(); + if (currentTool == nullptr) + { + for (size_t axis = 0; axis < numVisibleAxes; ++axis) + { + coordsOut[axis] = coordsIn[axis]/axisScaleFactors[axis]; + } + } + else + { + const uint32_t xAxes = reprap.GetCurrentXAxes(); + float xCoord = 0.0; + size_t numXAxes = 0; + for (size_t axis = 0; axis < numVisibleAxes; ++axis) + { + coordsOut[axis] = coordsIn[axis] + currentTool->GetOffset()[axis]; + if ((xAxes & (1u << axis)) != 0) + { + xCoord += coordsIn[axis]/axisScaleFactors[axis] + currentTool->GetOffset()[axis]; + } + } + if (numXAxes != 0) + { + coordsOut[X_AXIS] = xCoord/numXAxes; + } + } + coordsOut[Z_AXIS] -= (currentZHop + currentBabyStepZOffset)/axisScaleFactors[Z_AXIS]; } bool GCodes::IsPaused() const diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index d3453f20..ce5ef9f3 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -27,6 +27,7 @@ Licence: GPL #include "Libraries/sha1/sha1.h" #include "Platform.h" // for type EndStopHit #include "GCodeInput.h" +#include "RestorePoint.h" class GCodeBuffer; class GCodeQueue; @@ -38,6 +39,7 @@ const char extrudeLetter = 'E'; // GCode extrude typedef uint16_t EndstopChecks; // must be large enough to hold a bitmap of drive numbers or ZProbeActive const EndstopChecks ZProbeActive = 1 << 15; // must be distinct from 1 << (any drive number) const EndstopChecks LogProbeChanges = 1 << 14; // must be distinct from 1 << (any drive number) +const EndstopChecks HomeAxes = 1 << 13; // must be distinct from 1 << (any drive number) typedef uint16_t TriggerMask; @@ -79,8 +81,10 @@ public: float feedRate; // feed rate of this move FilePosition filePos; // offset in the file being printed at the end of reading this move uint32_t xAxes; // axes that X is mapped to - float newBabyStepping; // the adjustment we made to the Z offset in this move EndstopChecks endStopsToCheck; // endstops to check +#if SUPPORT_IOBITS + IoBits_t ioBits; // I/O bits to set/clear at the start of this move +#endif uint8_t moveType; // the S parameter from the G0 or G1 command, 0 for a normal move bool isFirmwareRetraction; // true if this is a firmware retraction/un-retraction move bool usePressureAdvance; // true if we want to us extruder pressure advance, if there is any extrusion @@ -117,7 +121,7 @@ public: float GetRawExtruderPosition(size_t drive) const; // Get the actual extruder position, after adjusting the extrusion factor float GetRawExtruderTotalByDrive(size_t extruder) const; // Get the total extrusion since start of print, for one drive float GetTotalRawExtrusion() const { return rawExtruderTotal; } // Get the total extrusion since start of print, all drives - float GetBabyStepOffset() const; // Get the current baby stepping Z offset + float GetBabyStepOffset() const { return currentBabyStepZOffset; } // Get the current baby stepping Z offset RegularGCodeInput *GetHTTPInput() const { return httpInput; } RegularGCodeInput *GetTelnetInput() const { return telnetInput; } @@ -150,14 +154,6 @@ private: enum class CannedMoveType : uint8_t { none, relative, absolute }; - struct RestorePoint - { - float moveCoords[DRIVES]; - float feedRate; - RestorePoint() { Init(); } - void Init(); - }; - // Resources that can be locked. // To avoid deadlock, if you need multiple resources then you must lock them in increasing numerical order. typedef unsigned int Resource; @@ -181,29 +177,27 @@ private: void DoFilePrint(GCodeBuffer& gb, StringRef& reply); // Get G Codes from a file and print them bool DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, bool runningM502 = false); // Run a GCode macro file, optionally report error if not found - bool DoCannedCycleMove(GCodeBuffer& gb, EndstopChecks ce); // Do a move from an internally programmed canned cycle void FileMacroCyclesReturn(GCodeBuffer& gb); // End a macro + bool ActOnCode(GCodeBuffer& gb, StringRef& reply); // Do a G, M or T Code bool HandleGcode(GCodeBuffer& gb, StringRef& reply); // Do a G code bool HandleMcode(GCodeBuffer& gb, StringRef& reply); // Do an M code bool HandleTcode(GCodeBuffer& gb, StringRef& reply); // Do a T code - int SetUpMove(GCodeBuffer& gb, StringRef& reply); // Pass a move on to the Move module + + bool DoStraightMove(GCodeBuffer& gb, StringRef& reply); // Execute a straight move returning true if an error was written to 'reply' + bool DoArcMove(GCodeBuffer& gb, bool clockwise) // Execute an arc move returning true if it was badly-formed + pre(segmentsLeft == 0; resourceOwners[MoveResource] == &gb); + bool DoDwell(GCodeBuffer& gb); // Wait for a bit bool DoDwellTime(GCodeBuffer& gb, uint32_t dwellMillis); // Really wait for a bit bool DoHome(GCodeBuffer& gb, StringRef& reply, bool& error); // Home some axes - bool DoSingleZProbeAtPoint(GCodeBuffer& gb, size_t probePointIndex, float heightAdjust); // Probe at a given point - bool DoSingleZProbe(GCodeBuffer& gb, StringRef& reply, bool reportOnly, float heightAdjust); // Probe where we are - int DoZProbe(GCodeBuffer& gb, float distance); // Do a Z probe cycle up to the maximum specified distance - bool SetSingleZProbeAtAPosition(GCodeBuffer& gb, StringRef& reply); // Probes at a given position - see the comment at the head of the function itself + bool ExecuteG30(GCodeBuffer& gb, StringRef& reply); // Probes at a given position - see the comment at the head of the function itself void SetBedEquationWithProbe(int sParam, StringRef& reply); // Probes a series of points and sets the bed equation bool SetPrintZProbe(GCodeBuffer& gb, StringRef& reply); // Either return the probe value, or set its threshold bool SetOrReportOffsets(GCodeBuffer& gb, StringRef& reply); // Deal with a G10 bool SetPositions(GCodeBuffer& gb); // Deal with a G92 bool LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType); // Set up the extrusion and feed rate of a move for the Move class - unsigned int LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType); // Set up the axis coordinates of a move for the Move class - bool DoArcMove(GCodeBuffer& gb, bool clockwise) // Execute an arc move returning true if it was badly-formed - pre(segmentsLeft == 0; resourceOwners[MoveResource] == &gb); bool Push(GCodeBuffer& gb); // Push feedrate etc on the stack void Pop(GCodeBuffer& gb); // Pop feedrate etc @@ -223,8 +217,14 @@ private: OutputBuffer *GenerateJsonStatusResponse(int type, int seq, ResponseSource source) const; // Generate a M408 response void CheckReportDue(GCodeBuffer& gb, StringRef& reply) const; // Check whether we need to report temperatures or status + void SavePosition(RestorePoint& rp, const GCodeBuffer& gb) const; // Save position to a restore point + void RestorePosition(const RestorePoint& rp, GCodeBuffer& gb); // Restore user position form a restore point + void SetAllAxesNotHomed(); // Flag all axes as not homed - void SetPositions(const float positionNow[DRIVES], bool doBedCompensation = true); // Set the current position to be this + void SetMachinePosition(const float positionNow[DRIVES], bool doBedCompensation = true); // Set the current position to be this + void GetCurrentUserPosition(); // Get the current position form the Move class + void ToolOffsetTransform(const float coordsIn[MaxAxes], float coordsOut[MaxAxes], bool mapXAxis); // Convert user coordinates to head reference point coordinates + void ToolOffsetInverseTransform(const float coordsIn[MaxAxes], float coordsOut[MaxAxes]); // Convert head reference point coordinates to user coordinates const char *TranslateEndStopResult(EndStopHit es); // Translate end stop result to text bool RetractFilament(GCodeBuffer& gb, bool retract); // Retract or un-retract filaments bool ChangeMicrostepping(size_t drive, int microsteps, int mode) const; // Change microstepping on the specified drive @@ -246,7 +246,10 @@ private: bool WriteConfigOverrideFile(StringRef& reply, const char *fileName) const; // Write the config-override file void CopyConfigFinalValues(GCodeBuffer& gb); // Copy the feed rate etc. from the daemon to the input channels - void ClearBabyStepping(); + void ClearBabyStepping() { currentBabyStepZOffset = 0.0; } + + MessageType GetMessageBoxDevice(GCodeBuffer& gb) const; // Decide which device to display a message box on + void DoManualProbe(GCodeBuffer& gb); // Do a manual bed probe static uint32_t LongArrayToBitMap(const long *arr, size_t numEntries); // Convert an array of longs to a bit map @@ -276,6 +279,9 @@ private: bool runningConfigFile; // We are running config.g during the startup process bool doingToolChange; // We are running tool change macros + float currentUserPosition[MaxAxes]; // The current position of the axes as commanded by the input gcode, before accounting for tool offset and Z hop + float currentZHop; // The amount of Z hop that is currently applied + // The following contain the details of moves that the Move module fetches RawMove moveBuffer; // Move details to pass to Move class unsigned int segmentsLeft; // The number of segments left to do in the current move, or 0 if no move available @@ -292,15 +298,12 @@ private: size_t numTotalAxes; // How many axes we have size_t numVisibleAxes; // How many axes are visible size_t numExtruders; // How many extruders we have, or may have + float axisOffsets[MaxAxes]; // M206 axis offsets float axisScaleFactors[MaxAxes]; // Scale XYZ coordinates by this factor (for Delta configurations) float lastRawExtruderPosition[MaxExtruders]; // Extruder position of the last move fed into the Move class float rawExtruderTotalByDrive[MaxExtruders]; // Total extrusion amount fed to Move class since starting print, before applying extrusion factor, per drive float rawExtruderTotal; // Total extrusion amount fed to Move class since starting print, before applying extrusion factor, summed over all drives float record[DRIVES]; // Temporary store for move positions - float cannedMoveCoords[DRIVES]; // Where to go or how much to move by in a canned cycle move, last is feed rate - float cannedFeedRate; // How fast to do it - CannedMoveType cannedMoveType[DRIVES]; // Is this drive involved in a canned cycle move? - bool offSetSet; // Are any axis offsets non-zero? float distanceScale; // MM or inches float arcSegmentLength; // Length of segments that we split arc moves into FileData fileToPrint; @@ -312,9 +315,6 @@ private: const char* eofString; // What's at the end of an HTML file? uint8_t eofStringCounter; // Check the... uint8_t eofStringLength; // ... EoF string as we read. - size_t probeCount; // Counts multiple probe points - int8_t cannedCycleMoveCount; // Counts through internal (i.e. not macro) canned cycle moves - bool cannedCycleMoveQueued; // True if a canned cycle move has been set bool limitAxes; // Don't think outside the box. uint32_t axesHomed; // Bitmap of which axes have been homed float pausedFanSpeeds[NUM_FANS]; // Fan speeds when the print was paused or a tool change started @@ -323,13 +323,14 @@ private: float speedFactor; // speed factor, including the conversion from mm/min to mm/sec, normally 1/60 float extrusionFactors[MaxExtruders]; // extrusion factors (normally 1.0) float currentBabyStepZOffset; // The accumulated Z offset due to baby stepping requests - float pendingBabyStepZOffset; // The amount of additional baby stepping requested but not yet acted on // Z probe + int32_t g30ProbePointIndex; // the index of the point we are probing (G30 P parameter), or -1 if none float lastProbedZ; // the last height at which the Z probe stopped uint32_t lastProbedTime; // time in milliseconds that the probe was last triggered 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 + bool doingManualBedProbe; // true if we are waiting for the user to jog the nozzle until it touches the bed float simulationTime; // Accumulated simulation time uint8_t simulationMode; // 0 = not simulating, 1 = simulating, >1 are simulation modes for debugging @@ -363,6 +364,7 @@ private: // Misc float longWait; // Timer for things that happen occasionally (seconds) uint32_t lastWarningMillis; // When we last sent a warning message for things that can happen very often + uint16_t axesToSenseLength; // The axes on which we are performing axis length sensing int8_t lastAuxStatusReportType; // The type of the last status report requested by PanelDue bool isWaiting; // True if waiting to reach temperature bool cancelWait; // Set true to cancel waiting diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index d1773b4d..c861c3eb 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -20,6 +20,10 @@ #include "Tool.h" #include "Version.h" +#if SUPPORT_IOBITS +# include "PortControl.h" +#endif + #ifdef DUET_NG # include "FirmwareUpdater.h" #endif @@ -94,49 +98,19 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) { return false; } + if (segmentsLeft != 0) { - // 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 (segmentsLeft != 0) - { - return false; - } - for (size_t axis = 0; axis < numVisibleAxes; ++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 = numTotalAxes; drive < DRIVES; ++drive) - { - moveBuffer.coords[drive] = 0.0; - } - moveBuffer.feedRate = (gb.Seen(feedrateLetter)) ? gb.GetFValue() * SecondsToMinutes : gb.MachineState().feedrate; - moveBuffer.filePos = noFilePosition; - moveBuffer.usePressureAdvance = false; - segmentsLeft = 1; - } - else - { - int res = SetUpMove(gb, reply); - if (res == 2) - { - gb.SetState(GCodeState::waitingForMoveToComplete); - } - result = (res != 0); - } + return false; + } + if (DoStraightMove(gb, reply)) + { + error = true; } break; case 2: // Clockwise arc case 3: // Anti clockwise arc - // We only support X and Y axes in these, but you can map them to other axes in the tool definitions + // We only support X and Y axes in these (and optionally Z for corkscrew moves), but you can map them to other axes in the tool definitions if (!LockMovement(gb)) { return false; @@ -200,7 +174,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) return false; } { - const int sparam = (gb.SeenAfterSpace('S')) ? gb.GetIValue() : 0; + const int sparam = (gb.Seen('S')) ? gb.GetIValue() : 0; switch(sparam) { case 0: // probe and save height map @@ -231,7 +205,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) else { ClearBabyStepping(); - result = SetSingleZProbeAtAPosition(gb, reply); + error = ExecuteG30(gb, reply); } break; @@ -246,6 +220,13 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) } ClearBabyStepping(); + + // We need to unlock the movement system here in case there is no Z probe and we are doing manual probing. + // Otherwise, even though the bed probing code calls UnlockAll when doing a manual bed probe, the movement system + // remains locked because the current MachineState object already held the lock when the macro file was started, + // which means that no gcode source other than the one that executed G32 is allowed to jog the Z axis. + UnlockAll(gb); + DoFileMacro(gb, BED_EQUATION_G, true); // Try to execute bed.g break; @@ -706,7 +687,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) else if (wasSimulating) { // Ending a simulation, so restore the position - SetPositions(simulationRestorePoint.moveCoords); + RestorePosition(simulationRestorePoint, gb); + ToolOffsetTransform(currentUserPosition, moveBuffer.coords, true); + reprap.GetMove().SetNewPosition(simulationRestorePoint.moveCoords, true); for (size_t i = 0; i < DRIVES; ++i) { moveBuffer.coords[i] = simulationRestorePoint.moveCoords[i]; @@ -768,7 +751,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) { val = 1.0 - val; } - Platform::WriteAnalog(pin, val, DefaultPinWritePwmFreq); + + const uint16_t freq = (gb.Seen('F')) ? (uint16_t)constrain<int32_t>(gb.GetIValue(), 1, 65536) : DefaultPinWritePwmFreq; + Platform::WriteAnalog(pin, val, freq); } // Ignore the command if no S parameter provided } @@ -821,11 +806,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) 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 < numTotalAxes; axis++) { @@ -861,7 +841,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) if (seen) { // On a delta, if we change the drive steps/mm then we need to recalculate the motor positions - SetPositions(positionNow); + reprap.GetMove().SetNewPosition(moveBuffer.coords, true); } else { @@ -1725,11 +1705,18 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) case 290: // Baby stepping if (gb.Seen('S')) { - const float babystepAmount = gb.GetFValue(); - if (fabs(babystepAmount) <= 1.0) // limit babystepping to 1mm + if (!LockMovement(gb)) { - pendingBabyStepZOffset += babystepAmount; + return false; } + const float babyStepAmount = constrain<float>(gb.GetFValue(), -1.0, 1.0); + currentBabyStepZOffset += babyStepAmount; + const float amountPushed = reprap.GetMove().PushBabyStepping(babyStepAmount); + moveBuffer.initialCoords[Z_AXIS] += amountPushed; + + // The following causes all the remaining baby stepping that we didn't manage to push to be added to the [remainder of the] currently-executing move, if there is one. + // This could result in an abrupt Z movement, however the move will be processed as normal so the jerk limit will be honoured. + moveBuffer.coords[Z_AXIS] += babyStepAmount; } else { @@ -1737,6 +1724,44 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } break; + case 291: // Display message, optionally wait for acknowledgement + { + char messageBuffer[80]; + bool seen = false; + gb.TryGetQuotedString('P', messageBuffer, ARRAY_SIZE(messageBuffer), seen); + if (seen) + { + int32_t sParam = 0; + gb.TryGetIValue('S', sParam, seen); + float tParam = DefaultMessageTimeout; + gb.TryGetFValue('T', tParam, seen); + int32_t zParam = 0; + gb.TryGetIValue('Z', zParam, seen); + + const MessageType mt = GetMessageBoxDevice(gb); // get the display device + + // If we need to wait for an acknowledgement, save the state and set waiting + if (sParam == 1 && Push(gb)) // stack the machine state including the file position + { + gb.MachineState().fileState.Close(); // stop reading from file + gb.MachineState().waitingForAcknowledgement = true; // flag that we are waiting for acknowledgement + } + + platform.SendAlert(mt, messageBuffer, (int)sParam, tParam, zParam == 1); + } + } + break; + + case 292: // Acknowledge message + for (GCodeBuffer* targetGb : gcodeSources) + { + if (targetGb != nullptr) + { + targetGb->MessageAcknowledged(); + } + } + break; + case 300: // Beep { const int ms = (gb.Seen('P')) ? gb.GetIValue() : 1000; // time in milliseconds @@ -1810,7 +1835,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) case 307: // Set heater process model parameters if (gb.Seen('H')) { - int heater = gb.GetIValue(); + const int heater = gb.GetIValue(); if (heater >= 0 && heater < (int)Heaters) { const FopDt& model = reprap.GetHeat().GetHeaterModel(heater); @@ -2970,7 +2995,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) ++numTotalAxes; } numVisibleAxes = numTotalAxes; // assume all axes are visible unless there is a P parameter - SetPositions(moveBuffer.coords); // tell the Move system where any new axes are + reprap.GetMove().SetNewPosition(moveBuffer.coords, true); // tell the Move system where any new axes are platform.SetAxisDriversConfig(drive, config); if (numTotalAxes + numExtruders > DRIVES) { @@ -3075,24 +3100,24 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) #ifdef DUET_WIFI case 587: // Add WiFi network or list remembered networks - if (gb.SeenAfterSpace('S')) + if (gb.Seen('S')) { WirelessConfigurationData config; memset(&config, 0, sizeof(config)); bool ok = gb.GetQuotedString(config.ssid, ARRAY_SIZE(config.ssid)); if (ok) { - ok = gb.SeenAfterSpace('P') && gb.GetQuotedString(config.password, ARRAY_SIZE(config.password)); + ok = gb.Seen('P') && gb.GetQuotedString(config.password, ARRAY_SIZE(config.password)); } - if (ok && gb.SeenAfterSpace('I')) + if (ok && gb.Seen('I')) { ok = gb.GetIPAddress(config.ip); } - if (ok && gb.SeenAfterSpace('J')) + if (ok && gb.Seen('J')) { ok = gb.GetIPAddress(config.gateway); } - if (ok && gb.SeenAfterSpace('K')) + if (ok && gb.Seen('K')) { ok = gb.GetIPAddress(config.netmask); } @@ -3120,14 +3145,28 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) if (rslt >= 0) { char* const cbuf = reinterpret_cast<char *>(buffer); - cbuf[declaredBufferLength] = 0; + cbuf[declaredBufferLength] = 0; // ensure null terminated size_t len = strlen(cbuf); + + // If there is a trailing newline, remove it if (len != 0 && cbuf[len - 1] == '\n') { --len; cbuf[len] = 0; } - if (len == 0) + + // DuetWiFiServer 1.19beta7 and later include the SSID used in access point mode at the start + const char *bufp = strchr(cbuf, '\n'); + if (bufp == nullptr) + { + bufp = cbuf; // must be an old version of DuetWiFiServer + } + else + { + ++bufp; // slip the first entry + } + + if (strlen(bufp) == 0) { reply.copy("No remembered networks"); } @@ -3139,7 +3178,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) return false; // try again later } response->copy("Remembered networks:\n"); - response->cat(cbuf); + response->cat(bufp); HandleReply(gb, false, response); return true; } @@ -3153,13 +3192,13 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) break; case 588: // Forget WiFi network - if (gb.SeenAfterSpace('S')) + if (gb.Seen('S')) { uint32_t ssid[NumDwords(SsidLength)]; if (gb.GetQuotedString(reinterpret_cast<char*>(ssid), SsidLength)) { const char* const pssid = reinterpret_cast<const char*>(ssid); - if (strcmp(pssid, "ALL") == 0) + if (strcmp(pssid, "*") == 0) { const int32_t rslt = reprap.GetNetwork().SendCommand(NetworkCommand::networkFactoryReset, 0, 0, nullptr, 0, nullptr, 0); if (rslt != ResponseEmpty) @@ -3187,22 +3226,37 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) break; case 589: // Configure access point - if (gb.SeenAfterSpace('S')) + if (gb.Seen('S')) { WirelessConfigurationData config; memset(&config, 0, sizeof(config)); bool ok = gb.GetQuotedString(config.ssid, ARRAY_SIZE(config.ssid)); if (ok) { - ok = gb.SeenAfterSpace('P') && gb.GetQuotedString(config.password, ARRAY_SIZE(config.password)); - } - if (ok && gb.SeenAfterSpace('I')) - { - ok = gb.GetIPAddress(config.ip); + if (strcmp(config.ssid, "*") == 0) + { + // Delete the access point details + memset(&config, 0xFF, sizeof(config)); + } + else + { + ok = gb.Seen('P') && gb.GetQuotedString(config.password, ARRAY_SIZE(config.password)); + if (ok) + { + if (gb.Seen('I')) + { + ok = gb.GetIPAddress(config.ip); + config.channel = (gb.Seen('C')) ? gb.GetIValue() : 0; + } + } + else + { + ok = false; + } + } } if (ok) { - config.channel = (gb.SeenAfterSpace('C')) ? gb.GetIValue() : 0; const int32_t rslt = reprap.GetNetwork().SendCommand(NetworkCommand::networkConfigureAccessPoint, 0, 0, &config, sizeof(config), nullptr, 0); if (rslt != ResponseEmpty) { @@ -3212,7 +3266,29 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } else { - reply.copy("Bad parameter in M589 command"); + reply.copy("Bad or missing parameter in M589 command"); + error = true; + } + } + else + { + const size_t declaredBufferLength = MaxRememberedNetworks * (SsidLength + 1) + 1; // enough for all the remembered SSIDs with null terminator, plus an extra null + uint32_t buffer[NumDwords(declaredBufferLength + 1)]; + const int32_t rslt = reprap.GetNetwork().SendCommand(NetworkCommand::networkListSsids, 0, 0, nullptr, 0, buffer, declaredBufferLength); + if (rslt >= 0) + { + char* const cbuf = reinterpret_cast<char *>(buffer); + cbuf[declaredBufferLength] = 0; // ensure null terminated + char *p = strchr(cbuf, '\n'); + if (p != nullptr) + { + *p = 0; + } + reply.printf("Own SSID: %s", (cbuf[0] == 0) ? "not configured" : cbuf); + } + else + { + reply.copy("Failed to remove SSID from remembered list"); error = true; } } @@ -3226,8 +3302,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } { Move& move = reprap.GetMove(); - float positionNow[DRIVES]; - move.GetCurrentUserPosition(positionNow, 0, reprap.GetCurrentXAxes()); // get the current position, we may need it later bool changedMode = false; if ((gb.Seen('L') || gb.Seen('D')) && move.GetKinematics().GetKinematicsType() != KinematicsType::linearDelta) @@ -3239,11 +3313,16 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) const bool changed = move.GetKinematics().Configure(code, gb, reply, error); if (changedMode) { - move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, positionNow); + move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, moveBuffer.coords); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); } if (changed || changedMode) { - SetPositions(positionNow); + if (reprap.GetMove().GetKinematics().LimitPosition(moveBuffer.coords, numVisibleAxes, axesHomed)) + { + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // make sure the limits are reflected in the user position + } + reprap.GetMove().SetNewPosition(moveBuffer.coords, true); SetAllAxesNotHomed(); } } @@ -3270,8 +3349,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } { Move& move = reprap.GetMove(); - float positionNow[DRIVES]; - move.GetCurrentUserPosition(positionNow, 0, reprap.GetCurrentXAxes()); // get the current position, we may need it later const KinematicsType oldK = move.GetKinematics().GetKinematicsType(); // get the current kinematics type so we can tell whether it changed bool seen = false; @@ -3316,9 +3393,14 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) // We changed something, so reset the positions and set all axes not homed if (move.GetKinematics().GetKinematicsType() != oldK) { - move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, positionNow); + move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, moveBuffer.coords); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); } - SetPositions(positionNow); + if (reprap.GetMove().GetKinematics().LimitPosition(moveBuffer.coords, numVisibleAxes, axesHomed)) + { + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // make sure the limits are reflected in the user position + } + reprap.GetMove().SetNewPosition(moveBuffer.coords, true); SetAllAxesNotHomed(); } } @@ -3331,8 +3413,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } { Move& move = reprap.GetMove(); - float positionNow[DRIVES]; - move.GetCurrentUserPosition(positionNow, 0, reprap.GetCurrentXAxes()); // get the current position, we may need it later const KinematicsType oldK = move.GetKinematics().GetKinematicsType(); // get the current kinematics type so we can tell whether it changed bool seen = false; @@ -3357,14 +3437,25 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) // We changed something, so reset the positions and set all axes not homed if (move.GetKinematics().GetKinematicsType() != oldK) { - move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, positionNow); + move.GetKinematics().GetAssumedInitialPosition(numVisibleAxes, moveBuffer.coords); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); } - SetPositions(positionNow); + if (reprap.GetMove().GetKinematics().LimitPosition(moveBuffer.coords, numVisibleAxes, axesHomed)) + { + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // make sure the limits are reflected in the user position + } + reprap.GetMove().SetNewPosition(moveBuffer.coords, true); SetAllAxesNotHomed(); } } break; +#if SUPPORT_IOBITS + case 670: + error = reprap.GetPortControl().Configure(gb, reply); + break; +#endif + #if SUPPORT_SCANNER case 750: // Enable 3D scanner extension reprap.GetScanner().Enable(); @@ -3815,11 +3906,7 @@ bool GCodes::HandleTcode(GCodeBuffer& gb, StringRef& reply) 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]; - } + reprap.GetMove().GetCurrentUserPosition(toolChangeRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes()); toolChangeRestorePoint.feedRate = gb.MachineState().feedrate; if (simulationMode == 0) // we don't yet simulate any T codes diff --git a/src/GCodes/RestorePoint.cpp b/src/GCodes/RestorePoint.cpp new file mode 100644 index 00000000..04a71fc6 --- /dev/null +++ b/src/GCodes/RestorePoint.cpp @@ -0,0 +1,27 @@ +/* + * RestorePoint.cpp + * + * Created on: 14 Jun 2017 + * Author: David + */ + +#include "RestorePoint.h" + +RestorePoint::RestorePoint() +{ + Init(); +} + +void RestorePoint::Init() +{ + for (size_t i = 0; i < DRIVES; ++i) + { + moveCoords[i] = 0.0; + } + feedRate = DefaultFeedrate * SecondsToMinutes; +#if SUPPORT_IOBITS + ioBits = 0; +#endif +} + +// End diff --git a/src/GCodes/RestorePoint.h b/src/GCodes/RestorePoint.h new file mode 100644 index 00000000..bfc87acc --- /dev/null +++ b/src/GCodes/RestorePoint.h @@ -0,0 +1,28 @@ +/* + * RestorePoint.h + * + * Created on: 14 Jun 2017 + * Author: David + */ + +#ifndef SRC_GCODES_RESTOREPOINT_H_ +#define SRC_GCODES_RESTOREPOINT_H_ + +#include "RepRapFirmware.h" + +#if SUPPORT_IOBITS +#include "PortControl.h" +#endif + +struct RestorePoint +{ + float moveCoords[DRIVES]; + float feedRate; +#if SUPPORT_IOBITS + IoBits_t ioBits; +#endif + RestorePoint(); + void Init(); +}; + +#endif /* SRC_GCODES_RESTOREPOINT_H_ */ diff --git a/src/Heating/FOPDT.cpp b/src/Heating/FOPDT.cpp index e2d9244e..3ef32a72 100644 --- a/src/Heating/FOPDT.cpp +++ b/src/Heating/FOPDT.cpp @@ -29,7 +29,8 @@ bool FopDt::SetParameters(float pg, float ptc, float pdt, float pMaxPwm, bool pU return true; } - if (pg > 10.0 && pg < 1500.0 && pdt > 0.1 && ptc > 2 * pdt && pMaxPwm > 0.2 && pMaxPwm <= 1.0) + // DC 2017-06-20: allow S down to 0.01 for one of our OEMs (use > 0.0099 because >= 0.01 doesn't work due to rounding error) + if (pg > 10.0 && pg <= 1500.0 && pdt > 0.099 && ptc >= 2 * pdt && pMaxPwm > 0.0099 && pMaxPwm <= 1.0) { gain = pg; timeConstant = ptc; diff --git a/src/Movement/BedProbing/Grid.cpp b/src/Movement/BedProbing/Grid.cpp index d729600e..43c96349 100644 --- a/src/Movement/BedProbing/Grid.cpp +++ b/src/Movement/BedProbing/Grid.cpp @@ -149,8 +149,10 @@ void HeightMap::SetGridHeight(size_t xIndex, size_t yIndex, float height) } // Return the minimum number of segments for a move by this X or Y amount -unsigned int HeightMap::GetMinimumSegments(float distance) const +// Note that deltaX and deltaY may be negative +unsigned int HeightMap::GetMinimumSegments(float deltaX, float deltaY) const { + float distance = max<float>(fabs(deltaX), fabs(deltaY)); return (distance > 0.0) ? (unsigned int)(distance * def.recipSpacing + 0.4) : 1; } diff --git a/src/Movement/BedProbing/Grid.h b/src/Movement/BedProbing/Grid.h index 68b67580..dde258f9 100644 --- a/src/Movement/BedProbing/Grid.h +++ b/src/Movement/BedProbing/Grid.h @@ -74,7 +74,7 @@ public: bool LoadFromFile(FileStore *f, StringRef& r); // Load the grid from file returning true if an error occurred - unsigned int GetMinimumSegments(float distance) const; // Return the minimum number of segments for a move by this X or Y amount + unsigned int GetMinimumSegments(float deltaX, float deltaY) const; // Return the minimum number of segments for a move by this X or Y amount bool UseHeightMap(bool b); bool UsingHeightMap() const { return useMap; } diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp index 6217d5fe..220bc93a 100644 --- a/src/Movement/DDA.cpp +++ b/src/Movement/DDA.cpp @@ -9,6 +9,7 @@ #include "RepRap.h" #include "Platform.h" #include "Move.h" +#include "Kinematics/LinearDeltaKinematics.h" // for DELTA_AXES #ifdef DUET_NG # define DDA_MOVE_DEBUG (1) @@ -195,12 +196,6 @@ void DDA::Init() // Set up a real move. Return true if it represents real movement, else false. bool DDA::Init(const GCodes::RawMove &nextMove, bool doMotorMapping) { - // 0. Try to push any new babystepping forward through the lookahead queue - if (nextMove.newBabyStepping != 0.0 && nextMove.moveType == 0 && nextMove.endStopsToCheck == 0) - { - AdvanceBabyStepping(nextMove.newBabyStepping); - } - // 1. Compute the new endpoints and the movement vector const int32_t * const positionNow = prev->DriveCoordinates(); const Move& move = reprap.GetMove(); @@ -301,6 +296,10 @@ bool DDA::Init(const GCodes::RawMove &nextMove, bool doMotorMapping) usePressureAdvance = nextMove.usePressureAdvance; hadLookaheadUnderrun = false; +#if SUPPORT_IOBITS + ioBits = nextMove.ioBits; +#endif + // The end coordinates will be valid at the end of this move if it does not involve endstop checks and is not a special move on a delta printer endCoordinatesValid = (endStopsToCheck == 0) && (doMotorMapping || !move.IsDeltaMode()); @@ -511,8 +510,8 @@ pre(state == provisional) } } -// Try to push babystepping earlier in the move queue -void DDA::AdvanceBabyStepping(float amount) +// Try to push babystepping earlier in the move queue, returning the amount we pushed +float DDA::AdvanceBabyStepping(float amount) { DDA *cdda = this; while (cdda->prev->state == DDAState::provisional) @@ -608,6 +607,8 @@ void DDA::AdvanceBabyStepping(float amount) // Now do the next move cdda = cdda->next; } + + return babySteppingDone; } // Recalculate the top speed, acceleration distance and deceleration distance, and whether we can pause after this move @@ -968,7 +969,7 @@ void DDA::Prepare() } } - if (reprap.Debug(moduleDda) && reprap.Debug(moduleMove)) // temp show the prepared DDA if debug enabled for both modules + if (reprap.Debug(moduleDda) && reprap.Debug(moduleMove)) // temp show the prepared DDA if debug enabled for both modules { DebugPrint(); } @@ -1137,8 +1138,9 @@ void DDA::CheckEndstops(Platform& platform) { case EndStopHit::lowHit: endStopsToCheck &= ~(1 << drive); // clear this check so that we can check for more - if (endStopsToCheck == 0 || reprap.GetMove().IsCoreXYAxis(drive)) // if no more endstops to check, or this axis uses shared motors + if (endStopsToCheck == 0 || reprap.GetMove().GetKinematics().DriveIsShared(drive)) { + // No more endstops to check, or this axis uses shared motors, so stop the entire move MoveAborted(); } else @@ -1150,8 +1152,9 @@ void DDA::CheckEndstops(Platform& platform) case EndStopHit::highHit: endStopsToCheck &= ~(1 << drive); // clear this check so that we can check for more - if (endStopsToCheck == 0 || reprap.GetMove().IsCoreXYAxis(drive)) // if no more endstops to check, or this axis uses shared motors + if (endStopsToCheck == 0 || reprap.GetMove().GetKinematics().DriveIsShared(drive)) { + // No more endstops to check, or this axis uses shared motors, so stop the entire move MoveAborted(); } else diff --git a/src/Movement/DDA.h b/src/Movement/DDA.h index 49f21bb0..d2533b02 100644 --- a/src/Movement/DDA.h +++ b/src/Movement/DDA.h @@ -64,6 +64,14 @@ public: void SetPositions(const float move[], size_t numDrives); // Force the endpoints to be these FilePosition GetFilePosition() const { return filePos; } float GetRequestedSpeed() const { return requestedSpeed; } + float AdvanceBabyStepping(float amount); // Try to push babystepping earlier in the move queue + bool IsHomingAxes() const { return (endStopsToCheck & HomeAxes) != 0; } + +#if SUPPORT_IOBITS + uint32_t GetMoveStartTime() const { return moveStartTime; } + uint32_t GetClocksNeeded() const { return clocksNeeded; } + IoBits_t GetIoBits() const { return ioBits; } +#endif void DebugPrint() const; @@ -105,7 +113,6 @@ private: bool IsDecelerationMove() const; // return true if this move is or have been might have been intended to be a deceleration-only move void DebugPrintVector(const char *name, const float *vec, size_t len) const; void CheckEndstops(Platform& platform); - void AdvanceBabyStepping(float amount); // Try to push babystepping earlier in the move queue float NormaliseXYZ(); // Make the direction vector unit-normal in XYZ static void DoLookahead(DDA *laDDA); // Try to smooth out moves in the queue @@ -116,7 +123,7 @@ private: static float VectorBoxIntersection(const float v[], // Compute the length that a vector would have to have to... const float box[], size_t dimensions); // ...just touch the surface of a hyperbox. - DDA* next; // The next one in the ring + DDA *next; // The next one in the ring DDA *prev; // The previous one in the ring volatile DDAState state; // What state this DDA is in @@ -159,6 +166,10 @@ private: uint32_t clocksNeeded; // in clocks uint32_t moveStartTime; // clock count at which the move was started +#if SUPPORT_IOBITS + IoBits_t ioBits; // port state required during this move +#endif + #if DDA_LOG_PROBE_CHANGES static bool probeTriggered; diff --git a/src/Movement/Kinematics/CartesianKinematics.h b/src/Movement/Kinematics/CartesianKinematics.h index d2bd6118..f2ad05ec 100644 --- a/src/Movement/Kinematics/CartesianKinematics.h +++ b/src/Movement/Kinematics/CartesianKinematics.h @@ -20,6 +20,8 @@ public: bool CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const override; void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const override; bool SupportsAutoCalibration() const override { return false; } + bool DriveIsShared(size_t drive) const override { return false; } + HomingMode GetHomingMode() const override { return homeCartesianAxes; } }; #endif /* SRC_MOVEMENT_KINEMATICS_CARTESIANKINEMATICS_H_ */ diff --git a/src/Movement/Kinematics/CoreBaseKinematics.cpp b/src/Movement/Kinematics/CoreBaseKinematics.cpp index 15013557..15835822 100644 --- a/src/Movement/Kinematics/CoreBaseKinematics.cpp +++ b/src/Movement/Kinematics/CoreBaseKinematics.cpp @@ -28,6 +28,7 @@ bool CoreBaseKinematics::CartesianToMotorSteps(const float machinePos[], const f // Set the parameters from a M665, M666 or M669 command // Return true if we changed any parameters. Set 'error' true if there was an error, otherwise leave it alone. +// This function is used for CoreXY and CoreXZ kinematics, but it overridden for CoreXYU kinematics bool CoreBaseKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, StringRef& reply, bool& error) /*override*/ { if (mCode == 667) diff --git a/src/Movement/Kinematics/CoreBaseKinematics.h b/src/Movement/Kinematics/CoreBaseKinematics.h index 1d684830..5132e7f3 100644 --- a/src/Movement/Kinematics/CoreBaseKinematics.h +++ b/src/Movement/Kinematics/CoreBaseKinematics.h @@ -17,15 +17,16 @@ public: // Overridden base class functions. See Kinematics.h for descriptions. bool CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const override final; - bool Configure(unsigned int mCode, GCodeBuffer& gb, StringRef& reply, bool& error) override final; + bool Configure(unsigned int mCode, GCodeBuffer& gb, StringRef& reply, bool& error) override; bool SupportsAutoCalibration() const override final { return false; } + HomingMode GetHomingMode() const override { return homeCartesianAxes; } protected: // Calculate the movement fraction for a single axis motor of a Cartesian-like printer. // The default implementation just returns directionVector[drive] but this needs to be overridden for CoreXY and CoreXZ printers. virtual float MotorFactor(size_t drive, const float directionVector[]) const = 0; - float axisFactors[XYZ_AXES]; + float axisFactors[MaxAxes]; // allow more than just XYZ so that we can support e.g. CoreXYU kinematics }; #endif /* SRC_MOVEMENT_KINEMATICS_COREBASEKINEMATICS_H_ */ diff --git a/src/Movement/Kinematics/CoreXYKinematics.cpp b/src/Movement/Kinematics/CoreXYKinematics.cpp index 55258509..8ff27a88 100644 --- a/src/Movement/Kinematics/CoreXYKinematics.cpp +++ b/src/Movement/Kinematics/CoreXYKinematics.cpp @@ -34,6 +34,13 @@ void CoreXYKinematics::MotorStepsToCartesian(const int32_t motorPos[], const flo } } +// Return true if the specified endstop axis uses shared motors. +// Used to determine whether to abort the whole move or just one motor when an endstop switch is triggered. +bool CoreXYKinematics::DriveIsShared(size_t drive) const +{ + return drive == X_AXIS || drive == Y_AXIS; +} + // Calculate the movement fraction for a single axis motor float CoreXYKinematics::MotorFactor(size_t drive, const float directionVector[]) const { diff --git a/src/Movement/Kinematics/CoreXYKinematics.h b/src/Movement/Kinematics/CoreXYKinematics.h index d9166904..1c7cc797 100644 --- a/src/Movement/Kinematics/CoreXYKinematics.h +++ b/src/Movement/Kinematics/CoreXYKinematics.h @@ -18,6 +18,7 @@ public: // Overridden base class functions. See Kinematics.h for descriptions. const char *GetName(bool forStatusReport) const override; void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const override; + bool DriveIsShared(size_t drive) const override; protected: float MotorFactor(size_t drive, const float directionVector[]) const override; diff --git a/src/Movement/Kinematics/CoreXYUKinematics.cpp b/src/Movement/Kinematics/CoreXYUKinematics.cpp new file mode 100644 index 00000000..5bf488f0 --- /dev/null +++ b/src/Movement/Kinematics/CoreXYUKinematics.cpp @@ -0,0 +1,105 @@ +/* + * CoreXYUKinematics.cpp + * + * Created on: 4 Jun 2017 + * Author: Lars + */ + +#include "CoreXYUKinematics.h" +#include "GCodes/GCodes.h" + +const size_t CoreXYU_AXES = 5; +const size_t U_AXIS = 3; // X2 +const size_t V_AXIS = 4; // Y2 + +CoreXYUKinematics::CoreXYUKinematics() : CoreBaseKinematics(KinematicsType::coreXYU) +{ +} + +// Return the name of the current kinematics +const char *CoreXYUKinematics::GetName(bool forStatusReport) const +{ + return (forStatusReport) ? "coreXYU" : "CoreXYU"; +} + +// Set the parameters from a M665, M666 or M669 command +// Return true if we changed any parameters. Set 'error' true if there was an error, otherwise leave it alone. +// This function is used for CoreXY and CoreXZ kinematics, but it overridden for CoreXYU kinematics +bool CoreXYUKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, StringRef& reply, bool& error) /*override*/ +{ + if (mCode == 668) + { + bool seen = false; + for (size_t axis = 0; axis < CoreXYU_AXES; ++axis) + { + if (gb.Seen(GCodes::axisLetters[axis])) + { + axisFactors[axis] = gb.GetFValue(); + seen = true; + } + } + if (!seen && !gb.Seen('S')) + { + reply.printf("Printer mode is %s with axis factors", GetName(false)); + for (size_t axis = 0; axis < CoreXYU_AXES; ++axis) + { + reply.catf(" %c:%f", GCodes::axisLetters[axis], axisFactors[axis]); + } + } + return seen; + } + else + { + return CoreBaseKinematics::Configure(mCode, gb, reply, error); + } +} + +// Convert motor coordinates to machine coordinates. Used after homing and after individual motor moves. +void CoreXYUKinematics::MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const +{ + // Convert the axes + machinePos[X_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) - (motorPos[Y_AXIS] * stepsPerMm[X_AXIS])) + /(2 * axisFactors[X_AXIS] * stepsPerMm[X_AXIS] * stepsPerMm[Y_AXIS]); + machinePos[Y_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) + (motorPos[Y_AXIS] * stepsPerMm[X_AXIS])) + /(2 * axisFactors[Y_AXIS] * stepsPerMm[X_AXIS] * stepsPerMm[Y_AXIS]); + machinePos[U_AXIS] = ((motorPos[U_AXIS] * stepsPerMm[V_AXIS]) - (motorPos[V_AXIS] * stepsPerMm[U_AXIS])) + /(2 * axisFactors[V_AXIS] * stepsPerMm[U_AXIS] * stepsPerMm[V_AXIS]); + machinePos[V_AXIS] = ((motorPos[U_AXIS] * stepsPerMm[V_AXIS]) + (motorPos[V_AXIS] * stepsPerMm[U_AXIS])) + /(2 * axisFactors[V_AXIS] * stepsPerMm[U_AXIS] * stepsPerMm[V_AXIS]); + + machinePos[Z_AXIS] = motorPos[Z_AXIS]/stepsPerMm[Z_AXIS]; + + // Convert any additional axes + for (size_t drive = CoreXYU_AXES; drive < numVisibleAxes; ++drive) + { + machinePos[drive] = motorPos[drive]/stepsPerMm[drive]; + } +} + +// Return true if the specified endstop axis uses shared motors. +// Used to determine whether to abort the whole move or just one motor when an endstop switch is triggered. +bool CoreXYUKinematics::DriveIsShared(size_t drive) const +{ + return drive == X_AXIS || drive == Y_AXIS + || drive == U_AXIS || drive == V_AXIS; // U and V don't have endstop switches, but include them here just in case +} + +// Calculate the movement fraction for a single axis motor +float CoreXYUKinematics::MotorFactor(size_t drive, const float directionVector[]) const +{ + switch(drive) + { + case X_AXIS: + return (directionVector[X_AXIS] * axisFactors[X_AXIS]) + (directionVector[Y_AXIS] * axisFactors[Y_AXIS]); + case Y_AXIS: + return (directionVector[Y_AXIS] * axisFactors[Y_AXIS]) - (directionVector[X_AXIS] * axisFactors[X_AXIS]); + case U_AXIS: // X2, Use Y and U to calculate + return (directionVector[U_AXIS] * axisFactors[U_AXIS]) + (directionVector[Y_AXIS] * axisFactors[Y_AXIS]); + case V_AXIS: // Y2, Use Y and U to calculate + return (directionVector[Y_AXIS] * axisFactors[Y_AXIS]) - (directionVector[U_AXIS] * axisFactors[U_AXIS]); + default: + return directionVector[drive]; + } +} + +// End diff --git a/src/Movement/Kinematics/CoreXYUKinematics.h b/src/Movement/Kinematics/CoreXYUKinematics.h new file mode 100644 index 00000000..c59c693f --- /dev/null +++ b/src/Movement/Kinematics/CoreXYUKinematics.h @@ -0,0 +1,28 @@ +/* + * CoreXYUKinematics.h + * + * Created on: 4 Jun 2017 + * Author: Lars + */ + +#ifndef SRC_MOVEMENT_KINEMATICS_COREXYUKINEMATICS_H_ +#define SRC_MOVEMENT_KINEMATICS_COREXYUKINEMATICS_H_ + +#include "CoreBaseKinematics.h" + +class CoreXYUKinematics : public CoreBaseKinematics +{ +public: + CoreXYUKinematics(); + + // Overridden base class functions. See Kinematics.h for descriptions. + const char *GetName(bool forStatusReport) const override; + bool Configure(unsigned int mCode, GCodeBuffer& gb, StringRef& reply, bool& error) override; + void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const override; + bool DriveIsShared(size_t drive) const override; + +protected: + float MotorFactor(size_t drive, const float directionVector[]) const override; +}; + +#endif /* SRC_MOVEMENT_KINEMATICS_COREXYKINEMATICS_H_ */ diff --git a/src/Movement/Kinematics/CoreXZKinematics.cpp b/src/Movement/Kinematics/CoreXZKinematics.cpp index f9baf985..ede4d8ca 100644 --- a/src/Movement/Kinematics/CoreXZKinematics.cpp +++ b/src/Movement/Kinematics/CoreXZKinematics.cpp @@ -33,6 +33,13 @@ void CoreXZKinematics::MotorStepsToCartesian(const int32_t motorPos[], const flo } } +// Return true if the specified endstop axis uses shared motors. +// Used to determine whether to abort the whole move or just one motor when an endstop switch is triggered. +bool CoreXZKinematics::DriveIsShared(size_t drive) const +{ + return drive == X_AXIS || drive == Z_AXIS; +} + // Calculate the movement fraction for a single axis motor of a Cartesian-like printer float CoreXZKinematics::MotorFactor(size_t drive, const float directionVector[]) const { diff --git a/src/Movement/Kinematics/CoreXZKinematics.h b/src/Movement/Kinematics/CoreXZKinematics.h index 5809f9f3..fb72648b 100644 --- a/src/Movement/Kinematics/CoreXZKinematics.h +++ b/src/Movement/Kinematics/CoreXZKinematics.h @@ -19,6 +19,7 @@ public: const char *GetName(bool forStatusReport) const override; void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const override; uint16_t AxesToHomeBeforeProbing() const override { return (1 << X_AXIS) | (1 << Y_AXIS) | (1 << Z_AXIS); } + bool DriveIsShared(size_t drive) const override; protected: float MotorFactor(size_t drive, const float directionVector[]) const override; diff --git a/src/Movement/Kinematics/Kinematics.cpp b/src/Movement/Kinematics/Kinematics.cpp index 058aef71..dd55b2ed 100644 --- a/src/Movement/Kinematics/Kinematics.cpp +++ b/src/Movement/Kinematics/Kinematics.cpp @@ -11,6 +11,7 @@ #include "CoreXYKinematics.h" #include "CoreXZKinematics.h" #include "ScaraKinematics.h" +#include "CoreXYUKinematics.h" #include "RepRap.h" #include "Platform.h" @@ -45,9 +46,10 @@ bool Kinematics::IsReachable(float x, float y) const // Limit the Cartesian position that the user wants to move to // This default implementation just applies the rectangular limits set up by M208 to those axes that have been homed. -void Kinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const +bool Kinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const { const Platform& platform = reprap.GetPlatform(); + bool limited = false; for (size_t axis = 0; axis < numVisibleAxes; axis++) { if ((axesHomed & (1 << axis)) != 0) @@ -56,13 +58,16 @@ void Kinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint16_t a if (f < platform.AxisMinimum(axis)) { f = platform.AxisMinimum(axis); + limited = true; } else if (f > platform.AxisMaximum(axis)) { f = platform.AxisMaximum(axis); + limited = true; } } } + return limited; } // Return the initial Cartesian coordinates we assume after switching to this kinematics @@ -91,6 +96,8 @@ void Kinematics::GetAssumedInitialPosition(size_t numAxes, float positions[]) co return new CoreXZKinematics(); case KinematicsType::scara: return new ScaraKinematics(); + case KinematicsType::coreXYU: + return new CoreXYUKinematics(); } } diff --git a/src/Movement/Kinematics/Kinematics.h b/src/Movement/Kinematics/Kinematics.h index 75f5b327..ab340b4a 100644 --- a/src/Movement/Kinematics/Kinematics.h +++ b/src/Movement/Kinematics/Kinematics.h @@ -20,6 +20,7 @@ enum class KinematicsType : uint8_t coreXZ, linearDelta, scara, + coreXYU, unknown // this one must be last! }; @@ -34,6 +35,14 @@ enum class MotionType : uint8_t class Kinematics { public: + // Class used to define homing mode + enum HomingMode : uint8_t + { + homeCartesianAxes, + homeIndividualMotors, + homeSharedMotors + }; + // Functions that must be defined in each derived class that implements a kinematics // Return the name of the current kinematics. @@ -89,9 +98,9 @@ public: // The default implementation assumes a rectangular reachable area, so it just used the bed dimensions give in the M208 commands. virtual bool IsReachable(float x, float y) const; - // Limit the Cartesian position that the user wants to move to + // Limit the Cartesian position that the user wants to move to, returning true if any coordinates were changed // The default implementation just applies the rectangular limits set up by M208 to those axes that have been homed. - virtual void LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const; + virtual bool LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const; // Return the set of axes that must have been homed before bed probing is allowed // The default implementation requires just X and Y, but some kinematics require additional axes to be homed (e.g. delta, CoreXZ) @@ -109,6 +118,13 @@ public: // Override this if the homing buttons are not named after the axes (e.g. SCARA printer) virtual const char* HomingButtonNames() const { return "XYZUVW"; } + // Return true if the specified endstop axis uses shared motors. + // Used to determine whether to abort the whole move or just one motor when an endstop switch is triggered. + virtual bool DriveIsShared(size_t drive) const = 0; + + // Return the type of homing we do + virtual HomingMode GetHomingMode() const = 0; + // Override this virtual destructor if your constructor allocates any dynamic memory virtual ~Kinematics() { } diff --git a/src/Movement/Kinematics/LinearDeltaKinematics.cpp b/src/Movement/Kinematics/LinearDeltaKinematics.cpp index c109134c..ee01fbed 100644 --- a/src/Movement/Kinematics/LinearDeltaKinematics.cpp +++ b/src/Movement/Kinematics/LinearDeltaKinematics.cpp @@ -31,12 +31,14 @@ void LinearDeltaKinematics::Init() printRadius = defaultPrintRadius; homedHeight = defaultDeltaHomedHeight; - for (size_t axis = 0; axis < DELTA_AXES; ++axis) - { - angleCorrections[axis] = 0.0; - endstopAdjustments[axis] = 0.0; - towerX[axis] = towerY[axis] = 0.0; - } + for (size_t axis = 0; axis < DELTA_AXES; ++axis) + { + angleCorrections[axis] = 0.0; + endstopAdjustments[axis] = 0.0; + towerX[axis] = towerY[axis] = 0.0; + } + + Recalc(); } void LinearDeltaKinematics::Recalc() @@ -166,9 +168,10 @@ bool LinearDeltaKinematics::IsReachable(float x, float y) const } // Limit the Cartesian position that the user wants to move to -void LinearDeltaKinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const +bool LinearDeltaKinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const { const uint16_t allAxes = (1u << X_AXIS) | (1u << Y_AXIS) | (1u << Z_AXIS); + bool limited = false; if ((axesHomed & allAxes) == allAxes) { // If axes have been homed on a delta printer and this isn't a homing move, check for movements outside limits. @@ -180,11 +183,26 @@ void LinearDeltaKinematics::LimitPosition(float coords[], size_t numVisibleAxes, const float factor = sqrtf(printRadiusSquared / diagonalSquared); coords[X_AXIS] *= factor; coords[Y_AXIS] *= factor; + limited = true; } - // Constrain the end height of the move to be no greater than the homed height and no lower than M208 minimum Z - coords[Z_AXIS] = max<float>(reprap.GetPlatform().AxisMinimum(Z_AXIS), min<float>(coords[Z_AXIS], homedHeight)); + if (coords[Z_AXIS] < reprap.GetPlatform().AxisMinimum(Z_AXIS)) + { + coords[Z_AXIS] = reprap.GetPlatform().AxisMinimum(Z_AXIS); + limited = true; + } + else + { + // Determine the maximum reachable height at this radius, in the worst case when the head is on a radius to a tower + const float maxHeight = homedCarriageHeight - sqrtf(D2 - fsquare(radius - sqrtf(diagonalSquared))); + if (coords[Z_AXIS] > maxHeight) + { + coords[Z_AXIS] = maxHeight; + limited = true; + } + } } + return limited; } // Return the initial Cartesian coordinates we assume after switching to this kinematics diff --git a/src/Movement/Kinematics/LinearDeltaKinematics.h b/src/Movement/Kinematics/LinearDeltaKinematics.h index 54a8371b..506ea52e 100644 --- a/src/Movement/Kinematics/LinearDeltaKinematics.h +++ b/src/Movement/Kinematics/LinearDeltaKinematics.h @@ -42,11 +42,13 @@ public: bool WriteCalibrationParameters(FileStore *f) const override; float GetTiltCorrection(size_t axis) const override; bool IsReachable(float x, float y) const override; - void LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const override; + bool LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const override; void GetAssumedInitialPosition(size_t numAxes, float positions[]) const override; uint16_t AxesToHomeBeforeProbing() const override { return (1 << X_AXIS) | (1 << Y_AXIS) | (1 << Z_AXIS); } MotionType GetMotionType(size_t axis) const override; size_t NumHomingButtons(size_t numVisibleAxes) const override { return 0; } + bool DriveIsShared(size_t drive) const override { return false; } + HomingMode GetHomingMode() const override { return homeIndividualMotors; } // Public functions specific to this class float GetDiagonalSquared() const { return D2; } diff --git a/src/Movement/Kinematics/ScaraKinematics.cpp b/src/Movement/Kinematics/ScaraKinematics.cpp index f9b67772..8dbd5845 100644 --- a/src/Movement/Kinematics/ScaraKinematics.cpp +++ b/src/Movement/Kinematics/ScaraKinematics.cpp @@ -170,8 +170,9 @@ bool ScaraKinematics::IsReachable(float x, float y) const // Limit the Cartesian position that the user wants to move to // TODO take account of arm angle limits -void ScaraKinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const +bool ScaraKinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const { + bool limited = false; float& x = coords[X_AXIS]; float& y = coords[Y_AXIS]; const float r = sqrtf(fsquare(x) + fsquare(y)); @@ -179,12 +180,15 @@ void ScaraKinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint1 { x *= minRadius/r; y *= minRadius/r; + limited = true; } else if (r > maxRadius) { x *= maxRadius/r; y *= maxRadius/r; + limited = true; } + return limited; } // Return the initial Cartesian coordinates we assume after switching to this kinematics @@ -197,6 +201,22 @@ void ScaraKinematics::GetAssumedInitialPosition(size_t numAxes, float positions[ } } +// Return true if the specified endstop axis uses shared motors. +// Used to determine whether to abort the whole move or just one motor when an endstop switch is triggered. +bool ScaraKinematics::DriveIsShared(size_t drive) const +{ + switch (drive) + { + case X_AXIS: + return crosstalk[0] != 0.0 || crosstalk[1] != 0.0; + case Y_AXIS: + return crosstalk[2] != 0.0; + case Z_AXIS: + default: + return false; + } +} + // Recalculate the derived parameters void ScaraKinematics::Recalc() { diff --git a/src/Movement/Kinematics/ScaraKinematics.h b/src/Movement/Kinematics/ScaraKinematics.h index 8e0c13d8..73ff2892 100644 --- a/src/Movement/Kinematics/ScaraKinematics.h +++ b/src/Movement/Kinematics/ScaraKinematics.h @@ -32,9 +32,11 @@ public: void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const override; bool SupportsAutoCalibration() const override { return false; } bool IsReachable(float x, float y) const override; - void LimitPosition(float position[], size_t numAxes, uint16_t axesHomed) const override; + bool LimitPosition(float position[], size_t numAxes, uint16_t axesHomed) const override; void GetAssumedInitialPosition(size_t numAxes, float positions[]) const override; const char* HomingButtonNames() const override { return "PDZUVW"; } + bool DriveIsShared(size_t drive) const override; + HomingMode GetHomingMode() const override { return homeSharedMotors; } private: static constexpr float DefaultSegmentsPerSecond = 200.0; diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp index e8c09e76..a768d391 100644 --- a/src/Movement/Move.cpp +++ b/src/Movement/Move.cpp @@ -8,6 +8,7 @@ #include "Move.h" #include "Platform.h" #include "RepRap.h" +#include "Kinematics/LinearDeltaKinematics.h" // temporary Move::Move() : currentDda(NULL), scheduledMoves(0), completedMoves(0) { @@ -156,7 +157,7 @@ void Move::Spin() // We have a new move if (simulationMode < 2) // in simulation mode 2 and higher, we don't process incoming moves beyond this point { - const bool doMotorMapping = (nextMove.moveType == 0) || (nextMove.moveType == 1 && !IsDeltaMode()); + const bool doMotorMapping = (nextMove.moveType == 0) || (nextMove.moveType == 1 && kinematics->GetHomingMode() == Kinematics::homeCartesianAxes); if (doMotorMapping) { AxisAndBedTransform(nextMove.coords, nextMove.xAxes, nextMove.moveType == 0); @@ -266,6 +267,12 @@ void Move::Spin() reprap.GetPlatform().ClassReport(longWait); } +// Try to push some babystepping through the lookahead queue +float Move::PushBabyStepping(float amount) +{ + return ddaRingAddPointer->AdvanceBabyStepping(amount); +} + // Change the kinematics to the specified type if it isn't already // If it is already correct leave its parameters alone. // This violates our rule on no dynamic memory allocation after the initialisation phase, @@ -295,8 +302,8 @@ bool Move::IsAccessibleProbePoint(float x, float y) const // Pause the print as soon as we can. // Returns the file position of the first queue move we are going to skip, or noFilePosition we we are not skipping any moves. // We update 'positions' to the positions and feed rate expected for the next move, and the amount of extrusion in the moves we skipped. -// If we are not skipping any moves then the feed rate is left alone, therefore the caller should set this up first. -FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, uint32_t xAxes) +// If we are not skipping any moves then the feed rate and iobits are left alone, therefore the caller should set them up first. +FilePosition Move::PausePrint(RestorePoint& rp, uint32_t xAxes) { // Find a move we can pause after. // Ideally, we would adjust a move if necessary and possible so that we can pause after it, but for now we don't do that. @@ -355,13 +362,17 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui // We are going to skip some moves. dda points to the last move we are going to print. for (size_t axis = 0; axis < numAxes; ++axis) { - positions[axis] = dda->GetEndCoordinate(axis, false); + rp.moveCoords[axis] = dda->GetEndCoordinate(axis, false); } for (size_t drive = numAxes; drive < DRIVES; ++drive) { - positions[drive] = 0.0; // clear out extruder movement + rp.moveCoords[drive] = 0.0; // clear out extruder movement } - pausedFeedRate = dda->GetRequestedSpeed(); + rp.feedRate = dda->GetRequestedSpeed(); + +#if SUPPORT_IOBITS + rp.ioBits = dda->GetIoBits(); +#endif // Free the DDAs for the moves we are going to skip, and work out how much extrusion they would have performed dda = ddaRingAddPointer; @@ -369,7 +380,7 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui { for (size_t drive = numAxes; drive < DRIVES; ++drive) { - positions[drive] += dda->GetEndCoordinate(drive, true); // update the amount of extrusion we are going to skip + rp.moveCoords[drive] += dda->GetEndCoordinate(drive, true); // update the amount of extrusion we are going to skip } (void)dda->Free(); dda = dda->GetNext(); @@ -379,7 +390,7 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui } else { - GetCurrentUserPosition(positions, 0, xAxes); // gets positions and clears out extrusion values + GetCurrentUserPosition(rp.moveCoords, 0, xAxes); // gets positions and clears out extrusion values } return fPos; @@ -458,6 +469,16 @@ void Move::Diagnostics(MessageType mtype) #endif } +// Set the current position to be this +void Move::SetNewPosition(const float positionNow[DRIVES], bool doBedCompensation) +{ + float newPos[DRIVES]; + memcpy(newPos, positionNow, sizeof(newPos)); // copy to local storage because Transform modifies it + AxisAndBedTransform(newPos, reprap.GetCurrentXAxes(), doBedCompensation); + SetLiveCoordinates(newPos); + SetPositions(newPos); +} + // These are the actual numbers we want in the positions, so don't transform them. void Move::SetPositions(const float move[DRIVES]) { @@ -789,7 +810,7 @@ bool Move::TryStartNextMove(uint32_t startTime) // This is called from the step ISR. Any variables it modifies that are also read by code outside the ISR must be declared 'volatile'. void Move::HitLowStop(size_t axis, DDA* hitDDA) { - if (axis < reprap.GetGCodes().GetTotalAxes() && !IsDeltaMode()) // should always be true + if (axis < reprap.GetGCodes().GetTotalAxes() && !IsDeltaMode() && hitDDA->IsHomingAxes()) { JustHomed(axis, reprap.GetPlatform().AxisMinimum(axis), hitDDA); } @@ -798,7 +819,7 @@ void Move::HitLowStop(size_t axis, DDA* hitDDA) // This is called from the step ISR. Any variables it modifies that are also read by code outside the ISR must be declared 'volatile'. void Move::HitHighStop(size_t axis, DDA* hitDDA) { - if (axis < reprap.GetGCodes().GetTotalAxes()) // should always be true + if (axis < reprap.GetGCodes().GetTotalAxes() && hitDDA->IsHomingAxes()) { const float hitPoint = (IsDeltaMode()) ? ((LinearDeltaKinematics*)kinematics)->GetHomedCarriageHeight(axis) // this is a delta printer, so the motor is at the homed carriage height for this drive @@ -810,15 +831,16 @@ void Move::HitHighStop(size_t axis, DDA* hitDDA) // This is called from the step ISR. Any variables it modifies that are also read by code outside the ISR must be declared 'volatile'. void Move::JustHomed(size_t axisHomed, float hitPoint, DDA* hitDDA) { - if (IsCoreXYAxis(axisHomed)) + if (kinematics->DriveIsShared(axisHomed)) { - float tempCoordinates[XYZ_AXES]; - for (size_t axis = 0; axis < XYZ_AXES; ++axis) + float tempCoordinates[MaxAxes]; + const size_t numTotalAxes = reprap.GetGCodes().GetTotalAxes(); + for (size_t axis = 0; axis < numTotalAxes; ++axis) { tempCoordinates[axis] = hitDDA->GetEndCoordinate(axis, false); } tempCoordinates[axisHomed] = hitPoint; - hitDDA->SetPositions(tempCoordinates, XYZ_AXES); + hitDDA->SetPositions(tempCoordinates, numTotalAxes); } else { @@ -994,18 +1016,4 @@ void Move::PrintCurrentDda() const } } -// Return true if the specified axis shares its motors with another. Safe to call for extruders as well as axes. -bool Move::IsCoreXYAxis(size_t axis) const -{ - switch(kinematics->GetKinematicsType()) - { - case KinematicsType::coreXY: - return axis == X_AXIS || axis == Y_AXIS; - case KinematicsType::coreXZ: - return axis == X_AXIS || axis == Z_AXIS; - default: - return false; - } -} - // End diff --git a/src/Movement/Move.h b/src/Movement/Move.h index e2eeac41..8234be45 100644 --- a/src/Movement/Move.h +++ b/src/Movement/Move.h @@ -8,13 +8,13 @@ #ifndef MOVE_H_ #define MOVE_H_ -#include <Movement/Kinematics/LinearDeltaKinematics.h> // temporary #include "RepRapFirmware.h" #include "MessageType.h" #include "DDA.h" // needed because of our inline functions #include "BedProbing/RandomProbePointSet.h" #include "BedProbing/Grid.h" #include "Kinematics/Kinematics.h" +#include "GCodes/RestorePoint.h" #ifdef DUET_NG const unsigned int DdaRingLength = 30; @@ -45,7 +45,7 @@ public: void HitLowStop(size_t axis, DDA* hitDDA); // What to do when a low endstop is hit void HitHighStop(size_t axis, DDA* hitDDA); // What to do when a high endstop is hit void ZProbeTriggered(DDA* hitDDA); // What to do when a the Z probe is triggered - void SetPositions(const float move[DRIVES]); // Force the coordinates to be these + void SetNewPosition(const float positionNow[DRIVES], bool doBedCompensation); // Set the current position to be this void SetLiveCoordinates(const float coords[DRIVES]); // Force the live coordinates (see above) to be these void ResetExtruderPositions(); // Resets the extrusion amounts of the live coordinates void SetXYBedProbePoint(size_t index, float x, float y); // Record the X and Y coordinates of a probe point @@ -60,6 +60,8 @@ public: float GetTaperHeight() const { return (useTaper) ? taperHeight : 0.0; } void SetTaperHeight(float h); bool UseMesh(bool b); // Try to enable mesh bed compensation and report the final state + bool IsUsingMesh() const { return usingMesh; } // Return true if we are using mesh compensation + float PushBabyStepping(float amount); // Try to push some babystepping through the lookahead queue void Diagnostics(MessageType mtype); // Report useful stuff @@ -77,7 +79,6 @@ public: // Temporary kinematics functions bool IsDeltaMode() const { return kinematics->GetKinematicsType() == KinematicsType::linearDelta; } - bool IsCoreXYAxis(size_t axis) const; // Return true if the specified axis shares its motors with another // End temporary functions void CurrentMoveCompleted(); // Signal that the current move has just been completed @@ -89,7 +90,7 @@ public: float GetSimulationTime() const { return simulationTime; } // Get the accumulated simulation time void PrintCurrentDda() const; // For debugging - FilePosition PausePrint(float positions[DRIVES], float& pausedFeedRate, uint32_t xAxes); // Pause the print as soon as we can + FilePosition PausePrint(RestorePoint& rp, uint32_t xAxes); // Pause the print as soon as we can bool NoLiveMovement() const; // Is a move running, or are there any queued? bool IsExtruding() const; // Is filament being extruded? @@ -100,6 +101,8 @@ public: HeightMap& AccessBedProbeGrid() { return grid; } // Access the bed probing grid + const DDA *GetCurrentDDA() const { return currentDda; } // Return the DDA of the currently-executing move + static int32_t MotorEndPointToMachine(size_t drive, float coord); // Convert a single motor position to number of steps static float MotorEndpointToPosition(int32_t endpoint, size_t drive); // Convert number of motor steps to motor position @@ -112,6 +115,7 @@ private: void AxisTransform(float move[MaxAxes]) const; // Take a position and apply the axis-angle compensations void InverseAxisTransform(float move[MaxAxes]) const; // Go from an axis transformed point back to user coordinates void JustHomed(size_t axis, float hitPoint, DDA* hitDDA); // Deal with setting positions after a drive has been homed + void SetPositions(const float move[DRIVES]); // Force the machine coordinates to be these bool DDARingAdd(); // Add a processed look-ahead entry to the DDA ring DDA* DDARingGet(); // Get the next DDA ring entry to be run diff --git a/src/Platform.cpp b/src/Platform.cpp index 66eaf063..8a3161b4 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -2847,6 +2847,31 @@ void Platform::MessageF(MessageType type, const char *fmt, ...) Message(type, formatBuffer); } +// Send a message box, which may require an acknowledgement +void Platform::SendAlert(MessageType mt, const char *messageBuffer, int sParam, float tParam, bool zParam) +{ + switch (mt) + { + case AUX_MESSAGE: +// Until the PanelDue firmware changes are implemented, use the default code +// qq; +// break; + + case HTTP_MESSAGE: +// Until the DWC changes are implemented, use the default code +// qq; +// break; + + default: + MessageF(mt, "%s\n", messageBuffer); + if (sParam == 1) + { + Message(mt, "Send M292 to continue\n"); + } + break; + } +} + bool Platform::AtxPower() const { return ReadPin(ATX_POWER_PIN); diff --git a/src/Platform.h b/src/Platform.h index 5c7a5685..3532c18b 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -390,6 +390,7 @@ public: void MessageF(const MessageType type, const char *fmt, ...); void MessageF(const MessageType type, const char *fmt, va_list vargs); bool FlushMessages(); // Flush messages to USB and aux, returning true if there is more to send + void SendAlert(MessageType mt, const char *messageBuffer, int sParam, float tParam, bool zParam); // Movement @@ -1229,7 +1230,8 @@ inline float Platform::AdcReadingToPowerVoltage(uint16_t adcVal) // The PC and PD bit numbers don't overlap, so we use their actual positions. // PA0 clashes with PD0, so we use bit 1 to represent PA0. // RADDS: -// Step pins are distributed over all 4 ports, but they are in different bit positions except for port C +// Step pins are PA2,9,12,15 PB16,19 PC3,12 PD6 +// PC12 clashes with PA12 so we shift PC3,12 left one bit // Calculate the step bit for a driver. This doesn't need to be fast. /*static*/ inline uint32_t Platform::CalcDriverBitmap(size_t driver) @@ -1241,7 +1243,7 @@ inline float Platform::AdcReadingToPowerVoltage(uint16_t adcVal) return (pinDesc.pPort == PIOC) ? pinDesc.ulPin << 1 : pinDesc.ulPin; #elif defined(__ALLIGATOR__) return pinDesc.ulPin; -#else +#else // Duet 06/085 return (pinDesc.pPort == PIOA) ? pinDesc.ulPin << 1 : pinDesc.ulPin; #endif } @@ -1262,7 +1264,7 @@ inline float Platform::AdcReadingToPowerVoltage(uint16_t adcVal) PIOB->PIO_ODSR = driverMap; PIOD->PIO_ODSR = driverMap; PIOC->PIO_ODSR = driverMap; -#else // Duet +#else // Duet 06/085 PIOD->PIO_ODSR = driverMap; PIOC->PIO_ODSR = driverMap; PIOA->PIO_ODSR = driverMap >> 1; // do this last, it means the processor doesn't need to preserve the register containing driverMap diff --git a/src/PortControl.cpp b/src/PortControl.cpp new file mode 100644 index 00000000..0a834370 --- /dev/null +++ b/src/PortControl.cpp @@ -0,0 +1,149 @@ +/* + * PortControl.cpp + * + * Created on: 15 Jun 2017 + * Author: David + */ + +#include "PortControl.h" +#include "Platform.h" +#include "GCodes/GCodeBuffer.h" +#include "Movement/Move.h" +#include "Movement/DDA.h" + +#if SUPPORT_IOBITS + +PortControl::PortControl() +{ +} + +void PortControl::Init() +{ + numConfiguredPorts = 0; + advanceMillis = 0; + advanceClocks = 0; + currentPortState = 0; + for (size_t i = 0; i < MaxPorts; ++i) + { + portMap[i].logicalPort = NoPort; + portMap[i].pin = NoPin; + } +} + +void PortControl::Exit() +{ + UpdatePorts(0); +} + +void PortControl::Spin(bool full) +{ + const DDA * cdda = reprap.GetMove().GetCurrentDDA(); + if (cdda == nullptr) + { + // Movement has stopped, so turn all ports off + UpdatePorts(0); + } + else + { + const uint32_t now = Platform::GetInterruptClocks() + advanceClocks; + uint32_t moveEndTime = cdda->GetMoveStartTime(); + DDA::DDAState st = cdda->GetState(); + do + { + moveEndTime += cdda->GetClocksNeeded(); + if ((int32_t)(moveEndTime - now) >= 0) + { + break; + } + cdda = cdda->GetPrevious(); + st = cdda->GetState(); + } while (st == DDA::executing || st == DDA::frozen); + + const IoBits_t bits = (st == DDA::executing || st == DDA::frozen || st == DDA::provisional) ? cdda->GetIoBits() : 0; + UpdatePorts(bits); + } +} + +bool PortControl::Configure(GCodeBuffer& gb, StringRef& reply) +{ + bool seen = false; + if (gb.Seen('P')) + { + seen = true; + UpdatePorts(0); + numConfiguredPorts = 0; + long portNumbers[MaxPorts]; + size_t numPorts = MaxPorts; + gb.GetLongArray(portNumbers, numPorts); + for (size_t i = 0; i < numPorts; ++i) + { + long pnum = portNumbers[i]; + if (pnum < 0 || pnum > HighestLogicalPin) + { + reply.printf("Port number %d out of range", pnum); + return true; + } + PortMapEntry& pm = portMap[i]; + pm.logicalPort = pnum; + if (!reprap.GetPlatform().GetFirmwarePin(pnum, PinAccess::write, pm.pin, pm.invert)) + { + reply.printf("Port number %d is not available", pnum); + return true; + } + Platform::WriteDigital(pm.pin, pm.invert); // ensure the port is off + if (i >= numConfiguredPorts) + { + numConfiguredPorts = i + 1; + } + } + } + if (gb.Seen('T')) + { + seen = true; + advanceMillis = (unsigned int)constrain<int>(gb.GetIValue(), 0, 1000); + advanceClocks = (advanceMillis * (uint64_t)DDA::stepClockRate)/1000; + } + if (!seen) + { + reply.printf("Advance %ums, ", advanceMillis); + if (numConfiguredPorts == 0) + { + reply.cat("no port mapping configured"); + } + else + { + reply.cat("port numbers"); + for (size_t i = 0; i < numConfiguredPorts; ++i) + { + reply.catf(" %u", (unsigned int)portMap[i].logicalPort); + } + } + } + return false; +} + +void PortControl::UpdatePorts(IoBits_t newPortState) +{ + if (newPortState != currentPortState) + { + const IoBits_t bitsToClear = currentPortState & ~newPortState; + const IoBits_t bitsToSet = newPortState & ~currentPortState; + for (size_t i = 0; i < numConfiguredPorts; ++i) + { + const IoBits_t mask = 1 << i; + if (bitsToClear & mask) + { + Platform::WriteDigital(portMap[i].pin, portMap[i].invert); + } + else if (bitsToSet & mask) + { + Platform::WriteDigital(portMap[i].pin, !portMap[i].invert); + } + } + currentPortState = newPortState; + } +} + +#endif + +// End diff --git a/src/PortControl.h b/src/PortControl.h new file mode 100644 index 00000000..ac4277ce --- /dev/null +++ b/src/PortControl.h @@ -0,0 +1,51 @@ +/* + * PortControl.h + * + * Created on: 15 Jun 2017 + * Author: David + */ + +#ifndef SRC_PORTCONTROL_H_ +#define SRC_PORTCONTROL_H_ + +#include "RepRapFirmware.h" + +class GCodeBuffer; + +#if SUPPORT_IOBITS + +typedef uint16_t IoBits_t; + +class PortControl +{ +public: + PortControl(); + void Init(); + void Exit(); + void Spin(bool full); + bool Configure(GCodeBuffer& gb, StringRef& reply); + +private: + void UpdatePorts(IoBits_t newPortState); + + static const size_t MaxPorts = 16; // the port bitmap is currently a 16-bit word + static const uint16_t NoPort = 0xFFFF; + + struct PortMapEntry + { + uint16_t logicalPort; + Pin pin; + bool invert; + }; + static_assert((sizeof(PortMapEntry) & (sizeof(PortMapEntry) - 1)) == 0, "PortMapEntry is not an efficient size for array inndexing"); + + PortMapEntry portMap[MaxPorts]; + size_t numConfiguredPorts; + unsigned int advanceMillis; + uint32_t advanceClocks; + IoBits_t currentPortState; +}; + +#endif + +#endif /* SRC_PORTCONTROL_H_ */ diff --git a/src/RADDS/Pins_RADDS.h b/src/RADDS/Pins_RADDS.h index 9f8c023b..203c79fa 100644 --- a/src/RADDS/Pins_RADDS.h +++ b/src/RADDS/Pins_RADDS.h @@ -18,11 +18,11 @@ const size_t NumFirmwareUpdateModules = 1; // The physical capabilities of the machine // The number of drives in the machine, including X, Y, and Z plus extruder drives -const size_t DRIVES = 8; +const size_t DRIVES = 9; // Initialization macro used in statements needing to initialize values in arrays of size DRIVES. E.g., -// max_feed_rates[DRIVES] = {DRIVES_(1, 1, 1, 1, 1, 1, 1, 1, 1)} -#define DRIVES_(a,b,c,d,e,f,g,h,i,j) { a,b,c,d,e,f,g,h } +// max_feed_rates[DRIVES] = {DRIVES_(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)} +#define DRIVES_(a,b,c,d,e,f,g,h,i,j) { a,b,c,d,e,f,g,h,i } // The number of heaters in the machine // 0 is the heated bed even if there isn't one. @@ -48,11 +48,11 @@ const size_t NUM_SERIAL_CHANNELS = 2; // The numbers of entries in each array must correspond with the values of DRIVES, AXES, or HEATERS. Set values to NoPin to flag unavailability. // DRIVES -// X Y Z E1 E2 E3 E4 E5 -const Pin ENABLE_PINS[DRIVES] = { 26, 22, 15, 62, 65, 49, 37, 31 }; -// A15 D04 B25 A02 B19 C12 C03 D06 -const Pin STEP_PINS[DRIVES] = { 24, 17, 2, 61, 64, 51, 35, 29 }; -const Pin DIRECTION_PINS[DRIVES] = { 23, 16, 3, 60, 63, 53, 33, 27 }; +// X Y Z E1 E2 E3 E4 E5 E6 +const Pin ENABLE_PINS[DRIVES] = { 26, 22, 15, 62, 65, 49, 37, 31, 68 }; +// A15 A12 A09 A02 B19 C12 C03 D06 B16 +const Pin STEP_PINS[DRIVES] = { 24, 17, 2, 61, 64, 51, 35, 29, 67 }; +const Pin DIRECTION_PINS[DRIVES] = { 23, 16, 3, 60, 63, 53, 33, 27, 66 }; // Endstops // E Stops not currently used @@ -137,10 +137,10 @@ const Pin SdSpiCSPins[2] = { 87, 77 }; // ### Removed: now E0_AXIS endstop D39 / PWMH2 / C.7 // D58 / AD3 / A.6 // D59 / AD2 / A.4 -// D66 / DAC0 / B.15 -// D67 / DAC1 / B.16 -// D68 / CANRX0 / A.1 -// D69 / CANTX0 / A.0 +// ### Removed: now E6_DIR ExtV3 D66 / DAC0 / B.15 +// ### Removed: now E6_STP ExtV3 D67 / DAC1 / B.16 +// ### Removed: now E6_EN ExtV3 D68 / CANRX0 / A.1 +// ### Removed: now MSi(=3.3V) ExtV3 D69 / CANTX0 / A.0 // D70 / SDA1 / A.17 // D71 / SCL1 / A.18 // D72 / RX LED / C.30 @@ -151,7 +151,7 @@ const Pin SdSpiCSPins[2] = { 87, 77 }; const Pin SpecialPinMap[] = { 5, 6, 58, 59, - 66, 67, 68, 69, 70, 71, 73, 73 + 70, 71, 72, 73 }; // This next definition defines the highest one. diff --git a/src/RepRap.cpp b/src/RepRap.cpp index 1dcd27c4..03223037 100644 --- a/src/RepRap.cpp +++ b/src/RepRap.cpp @@ -10,6 +10,10 @@ #include "Tool.h" #include "Version.h" +#if SUPPORT_IOBITS +# include "PortControl.h" +#endif + #if !defined(__RADDS__) && !defined(__ALLIGATOR__) # include "sam/drivers/hsmci/hsmci.h" #endif @@ -21,6 +25,13 @@ extern "C" void hsmciIdle() { reprap.GetNetwork().Spin(false); } + +#if SUPPORT_IOBITS + if (reprap.GetSpinningModule() != modulePortControl) + { + reprap.GetPortControl().Spin(false); + } +#endif } // RepRap member functions. @@ -44,6 +55,9 @@ RepRap::RepRap() : toolList(nullptr), currentTool(nullptr), lastWarningMillis(0) #if SUPPORT_SCANNER scanner = new Scanner(*platform); #endif +#if SUPPORT_IOBITS + portControl = new PortControl(); +#endif printMonitor = new PrintMonitor(*platform, *gCodes); @@ -66,6 +80,9 @@ void RepRap::Init() #if SUPPORT_SCANNER scanner->Init(); #endif +#if SUPPORT_IOBITS + portControl->Init(); +#endif printMonitor->Init(); active = true; // must do this before we start the network, else the watchdog may time out @@ -120,6 +137,9 @@ void RepRap::Exit() #if SUPPORT_SCANNER scanner->Exit(); #endif +#if SUPPORT_IOBITS + portControl->Exit(); +#endif network->Exit(); platform->Message(GENERIC_MESSAGE, "RepRap class exited.\n"); platform->Exit(); @@ -165,6 +185,12 @@ void RepRap::Spin() scanner->Spin(); #endif +#if SUPPORT_IOBITS + spinningModule = modulePortControl; + ticksInSpinState = 0; + portControl->Spin(true); +#endif + spinningModule = modulePrintMonitor; ticksInSpinState = 0; printMonitor->Spin(); diff --git a/src/RepRap.h b/src/RepRap.h index 1c403b65..842b8d64 100644 --- a/src/RepRap.h +++ b/src/RepRap.h @@ -80,6 +80,10 @@ public: Scanner& GetScanner() const; PrintMonitor& GetPrintMonitor() const; +#if SUPPORT_IOBITS + PortControl& GetPortControl() const; +#endif + void Tick(); uint16_t GetTicksInSpinState() const; bool IsStopped() const; @@ -114,6 +118,10 @@ private: Scanner* scanner; PrintMonitor* printMonitor; +#if SUPPORT_IOBITS + PortControl *portControl; +#endif + Tool* toolList; Tool* currentTool; uint32_t lastWarningMillis; // When we last sent a warning message for things that can happen very often @@ -148,6 +156,10 @@ inline Roland& RepRap::GetRoland() const { return *roland; } inline Scanner& RepRap::GetScanner() const { return *scanner; } inline PrintMonitor& RepRap::GetPrintMonitor() const { return *printMonitor; } +#if SUPPORT_IOBITS +inline PortControl& RepRap::GetPortControl() const { return *portControl; } +#endif + inline bool RepRap::Debug(Module m) const { return debug & (1 << m); } inline Module RepRap::GetSpinningModule() const { return spinningModule; } diff --git a/src/RepRapFirmware.cpp b/src/RepRapFirmware.cpp index 170ef9fb..107b107c 100644 --- a/src/RepRapFirmware.cpp +++ b/src/RepRapFirmware.cpp @@ -161,7 +161,6 @@ Licence: GPL ****************************************************************************************************/ #include "RepRapFirmware.h" - #include "MessageType.h" #include "Platform.h" #include "RepRap.h" @@ -183,7 +182,12 @@ const char *moduleName[] = "Scanner", "PrintMonitor", "Storage", - "?","?","?","?", +#if SUPPORT_IOBITS + "PortControl", +#else + "?", +#endif + "?","?","?", "none" }; diff --git a/src/RepRapFirmware.h b/src/RepRapFirmware.h index 358b96d7..9532b7c3 100644 --- a/src/RepRapFirmware.h +++ b/src/RepRapFirmware.h @@ -46,7 +46,8 @@ enum Module : uint8_t moduleScanner = 8, modulePrintMonitor = 9, moduleStorage = 10, - numModules = 11, // make this one greater than the last module number + modulePortControl = 11, + numModules = 12, // make this one greater than the last module number noModule = 15 }; @@ -68,6 +69,10 @@ class FileStore; class OutputBuffer; class OutputStack; +#if SUPPORT_IOBITS +class PortControl; +#endif + // A single instance of the RepRap class contains all the others extern RepRap reprap; diff --git a/src/Version.h b/src/Version.h index 8a642dda..81fe9c18 100644 --- a/src/Version.h +++ b/src/Version.h @@ -9,11 +9,11 @@ #define SRC_VERSION_H_ #ifndef VERSION -# define VERSION "1.19beta6" +# define VERSION "1.19beta7" #endif #ifndef DATE -# define DATE "2017-06-10" +# define DATE "2017-06-22" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman" |