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

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta7.bin (renamed from Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta3.bin)bin318652 -> 321428 bytes
-rw-r--r--Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta5.binbin294156 -> 0 bytes
-rw-r--r--Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta7.binbin0 -> 297548 bytes
-rw-r--r--Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta5.binbin297532 -> 0 bytes
-rw-r--r--Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta7.binbin0 -> 301060 bytes
-rw-r--r--Release/Duet-WiFi/Edge/DuetWiFiServer-1.19beta7.binbin0 -> 243984 bytes
-rw-r--r--Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta3.binbin248100 -> 0 bytes
-rw-r--r--Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta7.binbin0 -> 251076 bytes
-rw-r--r--src/Configuration.h6
-rw-r--r--src/DuetNG/DueXn.cpp11
-rw-r--r--src/DuetNG/DuetWiFi/Network.cpp2
-rw-r--r--src/DuetNG/Pins_DuetNG.h7
-rw-r--r--src/GCodes/GCodeBuffer.cpp60
-rw-r--r--src/GCodes/GCodeBuffer.h2
-rw-r--r--src/GCodes/GCodeMachineState.cpp2
-rw-r--r--src/GCodes/GCodeMachineState.h13
-rw-r--r--src/GCodes/GCodes.cpp1444
-rw-r--r--src/GCodes/GCodes.h60
-rw-r--r--src/GCodes/GCodes2.cpp261
-rw-r--r--src/GCodes/RestorePoint.cpp27
-rw-r--r--src/GCodes/RestorePoint.h28
-rw-r--r--src/Heating/FOPDT.cpp3
-rw-r--r--src/Movement/BedProbing/Grid.cpp4
-rw-r--r--src/Movement/BedProbing/Grid.h2
-rw-r--r--src/Movement/DDA.cpp25
-rw-r--r--src/Movement/DDA.h15
-rw-r--r--src/Movement/Kinematics/CartesianKinematics.h2
-rw-r--r--src/Movement/Kinematics/CoreBaseKinematics.cpp1
-rw-r--r--src/Movement/Kinematics/CoreBaseKinematics.h5
-rw-r--r--src/Movement/Kinematics/CoreXYKinematics.cpp7
-rw-r--r--src/Movement/Kinematics/CoreXYKinematics.h1
-rw-r--r--src/Movement/Kinematics/CoreXYUKinematics.cpp105
-rw-r--r--src/Movement/Kinematics/CoreXYUKinematics.h28
-rw-r--r--src/Movement/Kinematics/CoreXZKinematics.cpp7
-rw-r--r--src/Movement/Kinematics/CoreXZKinematics.h1
-rw-r--r--src/Movement/Kinematics/Kinematics.cpp9
-rw-r--r--src/Movement/Kinematics/Kinematics.h20
-rw-r--r--src/Movement/Kinematics/LinearDeltaKinematics.cpp36
-rw-r--r--src/Movement/Kinematics/LinearDeltaKinematics.h4
-rw-r--r--src/Movement/Kinematics/ScaraKinematics.cpp22
-rw-r--r--src/Movement/Kinematics/ScaraKinematics.h4
-rw-r--r--src/Movement/Move.cpp64
-rw-r--r--src/Movement/Move.h12
-rw-r--r--src/Platform.cpp25
-rw-r--r--src/Platform.h8
-rw-r--r--src/PortControl.cpp149
-rw-r--r--src/PortControl.h51
-rw-r--r--src/RADDS/Pins_RADDS.h26
-rw-r--r--src/RepRap.cpp26
-rw-r--r--src/RepRap.h12
-rw-r--r--src/RepRapFirmware.cpp8
-rw-r--r--src/RepRapFirmware.h7
-rw-r--r--src/Version.h4
53 files changed, 1611 insertions, 1005 deletions
diff --git a/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta3.bin b/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta7.bin
index 758d1a38..0980dbed 100644
--- a/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta3.bin
+++ b/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta7.bin
Binary files differ
diff --git a/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta5.bin b/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta5.bin
deleted file mode 100644
index 04406ab3..00000000
--- a/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta5.bin
+++ /dev/null
Binary files differ
diff --git a/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta7.bin b/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta7.bin
new file mode 100644
index 00000000..c9f4d1e3
--- /dev/null
+++ b/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta7.bin
Binary files differ
diff --git a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta5.bin b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta5.bin
deleted file mode 100644
index 873cecc5..00000000
--- a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta5.bin
+++ /dev/null
Binary files differ
diff --git a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta7.bin b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta7.bin
new file mode 100644
index 00000000..36a61678
--- /dev/null
+++ b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta7.bin
Binary files differ
diff --git a/Release/Duet-WiFi/Edge/DuetWiFiServer-1.19beta7.bin b/Release/Duet-WiFi/Edge/DuetWiFiServer-1.19beta7.bin
new file mode 100644
index 00000000..4b8b4dd0
--- /dev/null
+++ b/Release/Duet-WiFi/Edge/DuetWiFiServer-1.19beta7.bin
Binary files differ
diff --git a/Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta3.bin b/Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta3.bin
deleted file mode 100644
index 00b3a3d9..00000000
--- a/Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta3.bin
+++ /dev/null
Binary files differ
diff --git a/Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta7.bin b/Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta7.bin
new file mode 100644
index 00000000..ead0fb33
--- /dev/null
+++ b/Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta7.bin
Binary files differ
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"