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
path: root/src
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2017-02-21 23:16:06 +0300
committerDavid Crocker <dcrocker@eschertech.com>2017-02-21 23:16:26 +0300
commit7724eed928456e3e39fd6333202ca1dd4f61ab01 (patch)
tree2690dc6eb68e9c2b59713d13c7ac27097bb1f885 /src
parentc4d3b9eb8d869f3b04a6cc888d2b2b2a7069459e (diff)
Version 1.18betas1
- Baby stepping is now implemented using the M290 command. The accumulated baby stepping amount is reported in M408 replies. - Faster and easier-to-use auto tune algorithm with more consistent dead time measurement - M109, M190 and M191 commands now send the temperatures ponce a second if the command cam form the USB port and Marlin emulation is chosen - The name of the firmware file to load is now passed to IAP, so that iap4e.bin cab be used on both the Duet WiFi and the Duet Ethernet - Reduced the Duet WiFi VIN over-voltage detection threshold from 29.5V to 29.0V Bug fixes - On the Duet WiFi, if you sent command M122 while the machine was printing then occasionally it would stop and reset due to a watchdog timeout - If multiple input sources sent overlapping G4 (dwell) commands, either or both of them wold not be executed correctly
Diffstat (limited to 'src')
-rw-r--r--src/DuetNG/DuetEthernet/Network.cpp8
-rw-r--r--src/DuetNG/DuetEthernet/NetworkTransaction.cpp2
-rw-r--r--src/DuetNG/DuetEthernet/Socket.cpp26
-rw-r--r--src/GCodes/GCodeBuffer.cpp2
-rw-r--r--src/GCodes/GCodeBuffer.h3
-rw-r--r--src/GCodes/GCodes.cpp176
-rw-r--r--src/GCodes/GCodes.h8
-rw-r--r--src/GCodes/GCodes2.cpp89
-rw-r--r--src/Heating/Pid.cpp229
-rw-r--r--src/Heating/Pid.h35
-rw-r--r--src/Movement/DDA.cpp133
-rw-r--r--src/Movement/DDA.h3
-rw-r--r--src/Platform.cpp14
-rw-r--r--src/Reprap.cpp6
-rw-r--r--src/Version.h4
15 files changed, 593 insertions, 145 deletions
diff --git a/src/DuetNG/DuetEthernet/Network.cpp b/src/DuetNG/DuetEthernet/Network.cpp
index 5738be75..863330d3 100644
--- a/src/DuetNG/DuetEthernet/Network.cpp
+++ b/src/DuetNG/DuetEthernet/Network.cpp
@@ -352,10 +352,10 @@ void Network::CloseDataPort()
bool Network::AcquireTransaction(size_t socketNumber)
{
const bool success = sockets[socketNumber].AcquireTransaction();
-// if (success)
-// {
-// currentTransactionSocketNumber = socketNumber;
-// }
+ if (success)
+ {
+ currentTransactionSocketNumber = socketNumber;
+ }
return success;
}
diff --git a/src/DuetNG/DuetEthernet/NetworkTransaction.cpp b/src/DuetNG/DuetEthernet/NetworkTransaction.cpp
index a45deb5e..9012f1f8 100644
--- a/src/DuetNG/DuetEthernet/NetworkTransaction.cpp
+++ b/src/DuetNG/DuetEthernet/NetworkTransaction.cpp
@@ -302,7 +302,7 @@ Port NetworkTransaction::GetLocalPort() const
// Allocate a buffer from the freelist
/*static*/ NetworkTransaction *NetworkTransaction::Allocate()
{
- NetworkTransaction *ret = freelist;
+ NetworkTransaction * const ret = freelist;
if (ret != nullptr)
{
freelist = ret->next;
diff --git a/src/DuetNG/DuetEthernet/Socket.cpp b/src/DuetNG/DuetEthernet/Socket.cpp
index cb8ecc05..dcd281c4 100644
--- a/src/DuetNG/DuetEthernet/Socket.cpp
+++ b/src/DuetNG/DuetEthernet/Socket.cpp
@@ -17,6 +17,8 @@
//***************************************************************************************************
// Socket class
+#define IMMEDIATE_ACQUIRE 1
+
Socket::Socket() : currentTransaction(nullptr), receivedData(nullptr), state(SocketState::inactive)
{
}
@@ -227,6 +229,16 @@ void Socket::Poll(bool full)
}
}
+#ifdef IMMEDIATE_ACQUIRE
+ if (currentTransaction == nullptr && receivedData != nullptr)
+ {
+ currentTransaction = NetworkTransaction::Allocate();
+ if (currentTransaction != nullptr)
+ {
+ currentTransaction->Set(this, TransactionStatus::receiving);
+ }
+ }
+#else
if (currentTransaction == nullptr && (receivedData != nullptr || needTransaction))
{
currentTransaction = NetworkTransaction::Allocate();
@@ -236,6 +248,7 @@ void Socket::Poll(bool full)
needTransaction = false;
}
}
+#endif
// See if we can send any data.
// Currently we don't send if we are being called from hsmci because we don't want to risk releasing a buffer that we may be reading data into.
@@ -386,15 +399,24 @@ void Socket::DiscardReceivedData()
// Return true if we can do it, false if the connection is closed or closing.
bool Socket::AcquireTransaction()
{
- if (currentTransaction != nullptr && currentTransaction->GetStatus() == TransactionStatus::acquired)
+ if (currentTransaction != nullptr)
{
- return true;
+ return currentTransaction->GetStatus() == TransactionStatus::acquired;
}
if (getSn_SR(socketNum) == SOCK_ESTABLISHED)
{
+#ifdef IMMEDIATE_ACQUIRE
+ currentTransaction = NetworkTransaction::Allocate();
+ if (currentTransaction != nullptr)
+ {
+ currentTransaction->Set(this, TransactionStatus::acquired);
+ return true;
+ }
+#else
needTransaction = true;
return true;
+#endif
}
return false;
}
diff --git a/src/GCodes/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer.cpp
index e8120abc..4b770829 100644
--- a/src/GCodes/GCodeBuffer.cpp
+++ b/src/GCodes/GCodeBuffer.cpp
@@ -23,7 +23,7 @@ void GCodeBuffer::Init()
{
gcodePointer = 0;
readPointer = -1;
- inComment = false;
+ inComment = timerRunning = false;
bufferState = GCodeBufferState::idle;
}
diff --git a/src/GCodes/GCodeBuffer.h b/src/GCodes/GCodeBuffer.h
index 79c79d36..49141d22 100644
--- a/src/GCodes/GCodeBuffer.h
+++ b/src/GCodes/GCodeBuffer.h
@@ -54,6 +54,9 @@ public:
void AdvanceState();
const char *GetIdentity() const { return identity; }
+ uint32_t whenTimerStarted; // when we started waiting
+ bool timerRunning; // true if we are waiting
+
private:
enum class GCodeBufferState
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
index fb0a518d..5fd49c54 100644
--- a/src/GCodes/GCodes.cpp
+++ b/src/GCodes/GCodes.cpp
@@ -97,7 +97,6 @@ void GCodes::Init()
doingToolChange = false;
active = true;
longWait = platform->Time();
- dwellTime = longWait;
limitAxes = true;
for(size_t axis = 0; axis < MAX_AXES; axis++)
{
@@ -132,7 +131,6 @@ void GCodes::Reset()
fileToPrint.Close();
fileBeingWritten = NULL;
- dwellWaiting = false;
probeCount = 0;
cannedCycleMoveCount = 0;
cannedCycleMoveQueued = false;
@@ -335,8 +333,31 @@ void GCodes::Spin()
cancelWait = isWaiting = false;
gb.SetState(GCodeState::normal);
}
- // In Marlin emulation mode we should return some sort of (undocumented) message here every second...
- isWaiting = true;
+ else
+ {
+ isWaiting = true;
+
+ // In Marlin emulation mode we should return some sort of undocumented message here every second. Try a standard temperature report.
+ if (platform->Emulating() == marlin && gb.GetResponseMessageType() == MessageType::HOST_MESSAGE)
+ {
+ const uint32_t now = millis();
+ if (gb.timerRunning)
+ {
+ if (now - gb.whenTimerStarted >= 1000)
+ {
+ gb.whenTimerStarted = now;
+ GenerateTemperatureReport(reply);
+ reply.cat('\n');
+ platform->Message(HOST_MESSAGE, reply.Pointer());
+ }
+ }
+ else
+ {
+ gb.whenTimerStarted = now;
+ gb.timerRunning = true;
+ }
+ }
+ }
break;
case GCodeState::pausing1:
@@ -624,6 +645,7 @@ void GCodes::Spin()
if (gb.GetState() == GCodeState::normal)
{
// We completed a command, so unlock resources and tell the host about it
+ gb.timerRunning = false;
UnlockAll(gb);
HandleReply(gb, error, reply.Pointer());
}
@@ -1522,11 +1544,16 @@ bool GCodes::ReadMove(RawMove& m)
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;
}
@@ -1539,6 +1566,11 @@ void GCodes::ClearMove()
moveBuffer.isFirmwareRetraction = false;
}
+float GCodes::GetBabyStepOffset() const
+{
+ return currentBabyStepZOffset + pendingBabyStepZOffset;
+}
+
// Run a file macro. Prior to calling this, 'state' must be set to the state we want to enter when the macro has been completed.
// Return true if the file was found or it wasn't and we were asked to report that fact.
bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, bool runningM502)
@@ -1628,66 +1660,51 @@ bool GCodes::DoCannedCycleMove(GCodeBuffer& gb, EndstopChecks ce)
// This handles G92. Return true if completed, false if it needs to be called again.
bool GCodes::SetPositions(GCodeBuffer& gb)
{
- if (gb.Seen('R') && gb.GetIValue() == 1)
+ // Don't pause the machine if only extruder drives are being reset (DC, 2015-09-06).
+ // This avoids blobs and seams when the gcode uses absolute E coordinates and periodically includes G92 E0.
+ bool includingAxes = false;
+ for (size_t drive = 0; drive < numAxes; ++drive)
{
- // Babystepping command. All coordinates except Z are ignored.
- if (gb.Seen('Z'))
+ if (gb.Seen(axisLetters[drive]))
{
- const float babystepAmount = gb.GetFValue() * distanceScale;
- if (fabs(babystepAmount) <= 1.0) // limit babystepping to 1mm
- {
- pendingBabyStepZOffset += babystepAmount;
- }
+ includingAxes = true;
+ break;
}
}
- else
- {
- // Don't pause the machine if only extruder drives are being reset (DC, 2015-09-06).
- // This avoids blobs and seams when the gcode uses absolute E coordinates and periodically includes G92 E0.
- bool includingAxes = false;
- for (size_t drive = 0; drive < numAxes; ++drive)
- {
- if (gb.Seen(axisLetters[drive]))
- {
- includingAxes = true;
- break;
- }
- }
- if (includingAxes)
- {
- if (!LockMovementAndWaitForStandstill(gb)) // lock movement and get current coordinates
- {
- return false;
- }
- 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
+ if (includingAxes)
+ {
+ if (!LockMovementAndWaitForStandstill(gb)) // lock movement and get current coordinates
{
return false;
}
+ 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);
+ // 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);
+ if (includingAxes)
+ {
+ (void)LoadMoveBufferFromGCode(gb, -1);
#if SUPPORT_ROLAND
- if (reprap.GetRoland()->Active())
+ if (reprap.GetRoland()->Active())
+ {
+ for(size_t axis = 0; axis < AXES; axis++)
{
- for(size_t axis = 0; axis < AXES; axis++)
+ if (!reprap.GetRoland()->ProcessG92(moveBuffer[axis], axis))
{
- if (!reprap.GetRoland()->ProcessG92(moveBuffer[axis], axis))
- {
- return false;
- }
+ return false;
}
}
-#endif
- SetPositions(moveBuffer.coords);
}
+#endif
+ SetPositions(moveBuffer.coords);
}
return true;
}
@@ -2524,28 +2541,33 @@ void GCodes::DeleteFile(const char* fileName)
}
}
-// Function to handle dwell delays. Return true for dwell finished, false otherwise.
+// Function to handle dwell delays. Returns true for dwell finished, false otherwise.
bool GCodes::DoDwell(GCodeBuffer& gb)
{
- float dwell;
+ int32_t dwell;
if (gb.Seen('S'))
{
- dwell = gb.GetFValue();
+ dwell = (int32_t)(gb.GetFValue() * 1000.0); // S values are in seconds
}
else if (gb.Seen('P'))
{
- dwell = 0.001 * (float) gb.GetIValue(); // P values are in milliseconds; we need seconds
+ dwell = gb.GetIValue(); // P value are in milliseconds
}
else
{
return true; // No time given - throw it away
}
+ if (dwell <= 0)
+ {
+ return true;
+ }
+
#if SUPPORT_ROLAND
// Deal with a Roland configuration
if (reprap.GetRoland()->Active())
{
- return reprap.GetRoland()->ProcessDwell(gb.GetLValue());
+ return reprap.GetRoland()->ProcessDwell(dwell);
}
#endif
@@ -2557,31 +2579,33 @@ bool GCodes::DoDwell(GCodeBuffer& gb)
if (simulationMode != 0)
{
- simulationTime += dwell;
+ simulationTime += (float)dwell * 0.001;
return true;
}
else
{
- return DoDwellTime(dwell);
+ return DoDwellTime(gb, (uint32_t)dwell);
}
}
-bool GCodes::DoDwellTime(float dwell)
+bool GCodes::DoDwellTime(GCodeBuffer& gb, uint32_t dwellMillis)
{
+ const uint32_t now = millis();
+
// Are we already in the dwell?
- if (dwellWaiting)
+ if (gb.timerRunning)
{
- if (platform->Time() - dwellTime >= 0.0)
+ if (now - gb.whenTimerStarted >= dwellMillis)
{
- dwellWaiting = false;
+ gb.timerRunning = false;
return true;
}
return false;
}
// New dwell - set it up
- dwellWaiting = true;
- dwellTime = platform->Time() + dwell;
+ gb.whenTimerStarted = now;
+ gb.timerRunning = true;
return false;
}
@@ -3514,6 +3538,38 @@ bool GCodes::WriteConfigOverrideFile(StringRef& reply, const char *fileName) con
return !ok;
}
+// Store a standard-format temperature report in 'reply'. This doesn't put a newline character at the end.
+void GCodes::GenerateTemperatureReport(StringRef& reply)
+{
+ const int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
+ const int8_t chamberHeater = reprap.GetHeat()->GetChamberHeater();
+ reply.copy("T:");
+ for (int8_t heater = 0; heater < HEATERS; heater++)
+ {
+ if (heater != bedHeater && heater != chamberHeater)
+ {
+ Heat::HeaterStatus hs = reprap.GetHeat()->GetStatus(heater);
+ if (hs != Heat::HS_off && hs != Heat::HS_fault)
+ {
+ reply.catf("%.1f ", reprap.GetHeat()->GetTemperature(heater));
+ }
+ }
+ }
+ if (bedHeater >= 0)
+ {
+ reply.catf("B:%.1f", reprap.GetHeat()->GetTemperature(bedHeater));
+ }
+ else
+ {
+ // I'm not sure whether Pronterface etc. can handle a missing bed temperature, so return zero
+ reply.cat("B:0.0");
+ }
+ if (chamberHeater >= 0.0)
+ {
+ reply.catf(" C:%.1f", reprap.GetHeat()->GetTemperature(chamberHeater));
+ }
+}
+
// Resource locking/unlocking
// Lock the resource, returning true if success.
diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h
index 015ab22e..5b2e1077 100644
--- a/src/GCodes/GCodes.h
+++ b/src/GCodes/GCodes.h
@@ -70,6 +70,7 @@ 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
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
@@ -106,6 +107,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
bool IsFlashing() const { return isFlashing; } // Is a new firmware binary going to be flashed?
@@ -168,7 +170,7 @@ private:
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 DoDwell(GCodeBuffer& gb); // Wait for a bit
- bool DoDwellTime(float dwell); // Really 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
@@ -201,6 +203,7 @@ private:
void ManageTool(GCodeBuffer& gb, StringRef& reply); // Create a new tool definition
void SetToolHeaters(Tool *tool, float temperature); // Set all a tool's heaters to the temperature. For M104...
bool ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling) const; // Wait for the heaters associated with the specified tool to reach their set temperatures
+ void GenerateTemperatureReport(StringRef& reply); // Store a standard-format temperature report in reply
void SetAllAxesNotHomed(); // Flag all axes as not homed
void SetPositions(const float positionNow[DRIVES], bool doBedCompensation = true); // Set the current position to be this
@@ -246,12 +249,9 @@ private:
bool active; // Live and running?
bool isPaused; // true if the print has been paused
- bool dwellWaiting; // We are in a dwell
bool runningConfigFile; // We are running config.g during the startup process
bool doingToolChange; // We are running tool change macros
- float dwellTime; // How long a pause for a dwell (seconds)?
-
// 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
diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp
index 104a4570..6ff6aa9e 100644
--- a/src/GCodes/GCodes2.cpp
+++ b/src/GCodes/GCodes2.cpp
@@ -942,35 +942,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
break;
case 105: // Get temperatures
- {
- const int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
- const int8_t chamberHeater = reprap.GetHeat()->GetChamberHeater();
- reply.copy("T:");
- for (int8_t heater = 0; heater < HEATERS; heater++)
- {
- if (heater != bedHeater && heater != chamberHeater)
- {
- Heat::HeaterStatus hs = reprap.GetHeat()->GetStatus(heater);
- if (hs != Heat::HS_off && hs != Heat::HS_fault)
- {
- reply.catf("%.1f ", reprap.GetHeat()->GetTemperature(heater));
- }
- }
- }
- if (bedHeater >= 0)
- {
- reply.catf("B:%.1f", reprap.GetHeat()->GetTemperature(bedHeater));
- }
- else
- {
- // I'm not sure whether Pronterface etc. can handle a missing bed temperature, so return zero
- reply.cat("B:0.0");
- }
- if (chamberHeater >= 0.0)
- {
- reply.catf(" C:%.1f", reprap.GetHeat()->GetTemperature(chamberHeater));
- }
- }
+ GenerateTemperatureReport(reply);
break;
case 106: // Set/report fan values
@@ -1317,7 +1289,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
case 122:
{
- int val = (gb.Seen('P')) ? gb.GetIValue() : 0;
+ const int val = (gb.Seen('P')) ? gb.GetIValue() : 0;
if (val == 0)
{
reprap.Diagnostics(gb.GetResponseMessageType());
@@ -1537,7 +1509,27 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
cancelWait = isWaiting = false;
break;
}
- // In Marlin emulation mode we should return some sort of (undocumented) message here every second...
+
+ // In Marlin emulation mode we should return some sort of undocumented message here every second. Try a standard temperature report.
+ if (platform->Emulating() == marlin && gb.GetResponseMessageType() == MessageType::HOST_MESSAGE)
+ {
+ const uint32_t now = millis();
+ if (gb.timerRunning)
+ {
+ if (now - gb.whenTimerStarted >= 1000)
+ {
+ gb.whenTimerStarted = now;
+ GenerateTemperatureReport(reply);
+ reply.cat('\n');
+ platform->Message(HOST_MESSAGE, reply.Pointer());
+ }
+ }
+ else
+ {
+ gb.whenTimerStarted = now;
+ gb.timerRunning = true;
+ }
+ }
isWaiting = true;
return false;
}
@@ -1822,6 +1814,21 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
break;
+ case 290: // Baby stepping
+ if (gb.Seen('S'))
+ {
+ const float babystepAmount = gb.GetFValue();
+ if (fabs(babystepAmount) <= 1.0) // limit babystepping to 1mm
+ {
+ pendingBabyStepZOffset += babystepAmount;
+ }
+ }
+ else
+ {
+ reply.printf("Baby stepping offset is %.3d", GetBabyStepOffset());
+ }
+ break;
+
case 300: // Beep
{
const int ms = (gb.Seen('P')) ? gb.GetIValue() : 1000; // time in milliseconds
@@ -1849,10 +1856,13 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
case 303: // Run PID tuning
if (gb.Seen('H'))
{
- const size_t heater = gb.GetIValue();
- const float temperature = (gb.Seen('S')) ? gb.GetFValue() : 225.0;
- const float maxPwm = (gb.Seen('P')) ? gb.GetFValue() : 0.5;
- if (heater < HEATERS && maxPwm >= 0.1 && maxPwm <= 1.0 && temperature >= 55.0 && temperature <= reprap.GetHeat()->GetTemperatureLimit(heater))
+ const int heater = gb.GetIValue();
+ const float temperature = (gb.Seen('S')) ? gb.GetFValue()
+ : heater == reprap.GetHeat()->GetBedHeater() ? 75.0
+ : heater == reprap.GetHeat()->GetChamberHeater() ? 50.0
+ : 200.0;
+ const float maxPwm = (gb.Seen('P')) ? gb.GetFValue() : 1.0;
+ if (heater >= 0 && heater < HEATERS && maxPwm >= 0.1 && maxPwm <= 1.0 && temperature <= reprap.GetHeat()->GetTemperatureLimit(heater))
{
reprap.GetHeat()->StartAutoTune(heater, temperature, maxPwm, reply);
}
@@ -1923,9 +1933,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
{
// When reporting the PID parameters, we scale them by 255 for compatibility with older firmware and other firmware
M301PidParameters params = model.GetM301PidParameters(false);
- reply.catf("\nSetpoint change: P%.1f, I%.3f, D%.1f", params.kP, params.kI, params.kD);
+ reply.catf("\nComputed PID parameters for setpoint change: P%.1f, I%.3f, D%.1f", params.kP, params.kI, params.kD);
params = model.GetM301PidParameters(true);
- reply.catf("\nLoad change: P%.1f, I%.3f, D%.1f", params.kP, params.kI, params.kD);
+ reply.catf("\nComputed PID parameters for load change: P%.1f, I%.3f, D%.1f", params.kP, params.kI, params.kD);
}
}
}
@@ -3509,7 +3519,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
// If we get here then we have the module map, and all prerequisites are satisfied
isFlashing = true; // this tells the web interface and PanelDue that we are about to flash firmware
- if (!DoDwellTime(1.0)) // wait a second so all HTTP clients and PanelDue are notified
+ if (!DoDwellTime(gb, 1000)) // wait a second so all HTTP clients and PanelDue are notified
{
return false;
}
@@ -3531,7 +3541,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
break;
case 999:
- result = DoDwellTime(0.5); // wait half a second to allow the response to be sent back to the web server, otherwise it may retry
+ result = DoDwellTime(gb, 500); // wait half a second to allow the response to be sent back to the web server, otherwise it may retry
if (result)
{
reprap.EmergencyStop(); // this disables heaters and drives - Duet WiFi pre-production boards need drives disabled here
@@ -3549,6 +3559,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
if (result && gb.GetState() == GCodeState::normal)
{
+ gb.timerRunning = false;
UnlockAll(gb);
HandleReply(gb, error, reply.Pointer());
}
diff --git a/src/Heating/Pid.cpp b/src/Heating/Pid.cpp
index 0347f066..45f13a36 100644
--- a/src/Heating/Pid.cpp
+++ b/src/Heating/Pid.cpp
@@ -20,13 +20,21 @@ const uint32_t TempSettleTimeout = 20000; // how long we allow the initial tempe
float *PID::tuningTempReadings = nullptr; // the readings from the heater being tuned
float PID::tuningStartTemp; // the temperature when we turned on the heater
float PID::tuningPwm; // the PWM to use
-float PID::tuningMaxTemp; // the maximum temperature we are allowed to reach
+float PID::tuningTargetTemp; // the maximum temperature we are allowed to reach
uint32_t PID::tuningBeginTime; // when we started the tuning process
uint32_t PID::tuningPhaseStartTime; // when we started the current tuning phase
uint32_t PID::tuningReadingInterval; // how often we are sampling
size_t PID::tuningReadingsTaken; // how many samples we have taken
+
+#ifdef NEW_TUNING
+float PID::tuningHeaterOffTemp; // the temperature when we turned the heater off
+float PID::tuningPeakTemperature; // the peak temperature reached, averaged over 3 readings (so slightly less than the true peak)
+uint32_t PID::tuningHeatingTime; // how long we had the heating on for
+uint32_t PID::tuningPeakDelay; // how many milliseconds the temperature continues to rise after turning the heater off
+#else
float PID::tuningTimeOfFastestRate; // how long after turn-on the fastest temperature rise occurred
float PID::tuningFastestRate; // the fastest temperature rise
+#endif
// Member functions and constructors
@@ -458,7 +466,7 @@ float PID::GetExpectedHeatingRate() const
}
// Auto tune this PID
-void PID::StartAutoTune(float maxTemp, float maxPwm, StringRef& reply)
+void PID::StartAutoTune(float targetTemp, float maxPwm, StringRef& reply)
{
// Starting an auto tune
if (!model.IsEnabled())
@@ -488,8 +496,8 @@ void PID::StartAutoTune(float maxTemp, float maxPwm, StringRef& reply)
tuningTempReadings[0] = temperature;
tuningReadingInterval = platform->HeatSampleInterval();
tuningPwm = min<float>(maxPwm, model.GetMaxPwm());
- tuningMaxTemp = maxTemp;
- reply.printf("Auto tuning heater %d - do not leave printer unattended", heater);
+ tuningTargetTemp = targetTemp;
+ reply.printf("Auto tuning heater %d using target temperature %.1fC and PWM %.2f - do not leave printer unattended", heater, targetTemp, maxPwm);
}
}
}
@@ -591,11 +599,129 @@ void PID::DoTuningStep()
tuningTempReadings[tuningReadingsTaken] = temperature;
++tuningReadingsTaken;
+#ifdef NEW_TUNING
+ switch(mode)
+ {
+ case HeaterMode::tuning0:
+ // Waiting for initial temperature to settle after any thermostatic fans have turned on
+ if (ReadingsStable(6000/platform->HeatSampleInterval(), 2.0)) // expect temperature to be stable within a 2C band for 6 seconds
+ {
+ // Starting temperature is stable, so move on
+ tuningReadingsTaken = 1;
+ tuningTempReadings[0] = tuningStartTemp = temperature;
+ timeSetHeating = tuningPhaseStartTime = millis();
+ lastPwm = tuningPwm; // turn on heater at specified power
+ tuningReadingInterval = platform->HeatSampleInterval(); // reset sampling interval
+ mode = HeaterMode::tuning1;
+ platform->Message(GENERIC_MESSAGE, "Auto tune phase 1, heater on\n");
+ return;
+ }
+ if (millis() - tuningPhaseStartTime < 20000)
+ {
+ // Allow up to 20 seconds for starting temperature to settle
+ return;
+ }
+ platform->Message(GENERIC_MESSAGE, "Auto tune cancelled because starting temperature is not stable\n");
+ break;
+
+ case HeaterMode::tuning1:
+ // Heating up
+ {
+ const uint32_t heatingTime = millis() - tuningPhaseStartTime;
+ if (heatingTime > (uint32_t)(model.GetDeadTime() * SecondsToMillis) + (30 * 1000) && (temperature - tuningStartTemp) < 3.0)
+ {
+ platform->Message(GENERIC_MESSAGE, "Auto tune cancelled because temperature is not increasing\n");
+ break;
+ }
+
+ const uint32_t timeoutMinutes = (heater == reprap.GetHeat()->GetBedHeater() || heater == reprap.GetHeat()->GetChamberHeater()) ? 20 : 5;
+ if (heatingTime >= timeoutMinutes * 60 * 1000)
+ {
+ platform->Message(GENERIC_MESSAGE, "Auto tune cancelled because target temperature was not reached\n");
+ break;
+ }
+
+ if (temperature >= tuningTargetTemp) // if reached target
+ {
+ tuningHeatingTime = heatingTime;
+
+ // Move on to next phase
+ tuningReadingsTaken = 1;
+ tuningHeaterOffTemp = tuningTempReadings[0] = temperature;
+ tuningPhaseStartTime = millis();
+ tuningReadingInterval = platform->HeatSampleInterval(); // reset sampling interval
+ mode = HeaterMode::tuning2;
+ lastPwm = 0.0;
+ SetHeater(0.0);
+ platform->Message(GENERIC_MESSAGE, "Auto tune phase 2, heater off\n");
+ }
+ }
+ return;
+
+ case HeaterMode::tuning2:
+ // Heater turned off, looking for peak temperature
+ {
+ const int peakIndex = GetPeakTempIndex();
+ if (peakIndex < 0)
+ {
+ if (millis() - tuningPhaseStartTime < 60 * 1000)
+ {
+ return; // still waiting for peak temperature
+ }
+ platform->Message(GENERIC_MESSAGE, "Auto tune cancelled because temperature is not falling\n");
+ }
+ else if (peakIndex == 0)
+ {
+ if (reprap.Debug(moduleHeat))
+ {
+ DisplayBuffer("At no peak found");
+ }
+ platform->Message(GENERIC_MESSAGE, "Auto tune cancelled because temperature peak was not identified\n");
+ }
+ else
+ {
+ tuningPeakTemperature = tuningTempReadings[peakIndex];
+ tuningPeakDelay = peakIndex * tuningReadingInterval;
+
+ // Move on to next phase
+ tuningReadingsTaken = 1;
+ tuningTempReadings[0] = temperature;
+ tuningPhaseStartTime = millis();
+ tuningReadingInterval = platform->HeatSampleInterval(); // reset sampling interval
+ mode = HeaterMode::tuning3;
+ platform->MessageF(GENERIC_MESSAGE, "Auto tune phase 3, peak temperature was %.1f\n", tuningPeakTemperature);
+ return;
+ }
+ }
+ break;
+
+ case HeaterMode::tuning3:
+ {
+ // Heater is past the peak temperature and cooling down. Wait until it is part way back to the starting temperature so we can measure the cooling rate.
+ // In the case of a bed that shows a reservoir effect, the choice of how far we wait for it to cool down will effect the result.
+ // If we wait for it to cool down by 50% then we get a short time constant and a low gain, which causes overshoot. So try a bit more.
+ const float coolDownProportion = 0.6;
+ if (temperature > (tuningTempReadings[0] * (1.0 - coolDownProportion)) + (tuningStartTemp * coolDownProportion))
+ {
+ return;
+ }
+ CalculateModel();
+ }
+ break;
+
+ default:
+ // Should not happen, but if it does then quit
+ break;
+ }
+
+ // If we get here, we have finished
+ SwitchOff(); // sets mode and lastPWM, also deletes tuningTempReadings
+#else
if (temperature > tuningMaxTemp)
{
platform->MessageF(GENERIC_MESSAGE,
"Auto tune of heater %u with P=%.2f S=%.1f cancelled because temperature limit exceeded. Use lower P or higher S in m303 command.\n",
- heater, tuningPwm, tuningMaxTemp);
+ heater, tuningPwm, tuningTargetTemp);
}
else
{
@@ -681,10 +807,11 @@ void PID::DoTuningStep()
// If we get here, we have finished
SwitchOff(); // sets mode and lastPWM, also deletes tuningTempReadings
+#endif
}
// Return true if the last 'numReadings' readings are stable
-bool PID::ReadingsStable(size_t numReadings, float maxDiff) const
+/*static*/ bool PID::ReadingsStable(size_t numReadings, float maxDiff)
{
if (tuningTempReadings == nullptr || tuningReadingsTaken < numReadings)
{
@@ -703,6 +830,94 @@ bool PID::ReadingsStable(size_t numReadings, float maxDiff) const
return maxReading - minReading <= maxDiff;
}
+#ifdef NEW_TUNING
+
+// Calculate which reading gave us the peak temperature.
+// Return -1 if peak not identified yet, 0 if we failed to find a peak, else the index of the peak (which can't be zero because we always average 3 readings)
+/*static*/ int PID::GetPeakTempIndex()
+{
+ // Check we have enough readings to look for the peak
+ if (tuningReadingsTaken < 15)
+ {
+ return -1; // too few readings
+ }
+
+ // Look for the peak
+ int peakIndex = IdentifyPeak(1);
+ if (peakIndex < 0)
+ {
+ peakIndex = IdentifyPeak(3);
+ if (peakIndex < 0)
+ {
+ peakIndex = IdentifyPeak(5);
+ if (peakIndex < 0)
+ {
+ return 0; // more than one peak
+ }
+ }
+ }
+
+ // If we have found one peak and it's not too near the end of the readings, return it
+ return ((size_t)peakIndex + 6 < tuningReadingsTaken) ? peakIndex : -1;
+}
+
+// See if there is exactly one peak in the readings.
+// Return -1 if more than one peak, else the index of the peak. The so-called peak may be right at the end , in which case it isn't really a peak.
+/*static*/ int PID::IdentifyPeak(size_t numToAverage)
+{
+ int firstPeakIndex = -1;
+ float peakTempTimesN = -999.0;
+ for (size_t i = 0; i + numToAverage <= tuningReadingsTaken; ++i)
+ {
+ float peak = 0.0;
+ for (size_t j = 0; j < numToAverage; ++j)
+ {
+ peak += tuningTempReadings[i + j];
+ }
+ if (peak >= peakTempTimesN)
+ {
+ if ((int)i == firstPeakIndex + 1)
+ {
+ firstPeakIndex = (int)i; // readings still going up or staying the same, so advance the first peak index
+ peakTempTimesN = peak;
+ }
+ else
+ {
+ return -1; // error, more than one peak
+ }
+ }
+ }
+ return firstPeakIndex + (numToAverage - 1)/2;
+}
+
+// Calculate the heater model from the accumulated heater parameters
+void PID::CalculateModel()
+{
+ if (reprap.Debug(moduleHeat))
+ {
+ DisplayBuffer("At completion");
+ }
+ const float tc = (float)((tuningReadingsTaken - 1) * tuningReadingInterval)/(1000.0 * log((tuningTempReadings[0] - tuningStartTemp)/(tuningTempReadings[tuningReadingsTaken - 1] - tuningStartTemp)));
+ const float td = (float)tuningPeakDelay * 0.001;
+ const float heatingTime = (tuningHeatingTime - tuningPeakDelay) * 0.001;
+ const float gain = (tuningHeaterOffTemp - tuningStartTemp)/(1.0 - exp(-heatingTime/tc));
+
+ tuned = SetModel(gain, tc, td, model.GetMaxPwm(), true);
+ if (tuned)
+ {
+ platform->MessageF(GENERIC_MESSAGE,
+ "Auto tune heater %d completed in %u sec\n"
+ "Use M307 H%d to see the result, or M500 to save the result in config-override.g\n",
+ heater, (millis() - tuningBeginTime)/(uint32_t)SecondsToMillis, heater);
+ }
+ else
+ {
+ platform->MessageF(GENERIC_MESSAGE, "Auto tune of heater %u failed due to bad curve fit (G=%.1f, tc=%.1f, td=%.1f)\n", heater, gain, tc, td);
+ }
+}
+
+#else
+
// Return the index in the temperature readings of the maximum rate of increase
// In view of the poor resolution of most thermistors at low temperatures, we measure over 4 time intervals instead of 2.
/*static*/ size_t PID::GetMaxRateIndex()
@@ -771,6 +986,8 @@ void PID::FitCurve()
}
}
+#endif
+
void PID::DisplayBuffer(const char *intro)
{
OutputBuffer *buf;
diff --git a/src/Heating/Pid.h b/src/Heating/Pid.h
index 523eea95..cc604b2e 100644
--- a/src/Heating/Pid.h
+++ b/src/Heating/Pid.h
@@ -16,6 +16,8 @@
#include "FOPDT.h"
#include "TemperatureError.h"
+#define NEW_TUNING (1)
+
class PID
{
enum class HeaterMode : uint8_t
@@ -30,7 +32,12 @@ class PID
tuning0,
tuning1,
tuning2,
+#ifdef NEW_TUNING
+ tuning3,
+ lastTuningMode = tuning3
+#else
lastTuningMode = tuning2
+#endif
};
static const size_t NumPreviousTemperatures = 4; // How many samples we average the temperature derivative over
@@ -58,7 +65,7 @@ public:
float GetAveragePWM() const; // Return the running average PWM to the heater. Answer is a fraction in [0, 1].
uint32_t GetLastSampleTime() const; // Return when the temp sensor was last sampled
float GetAccumulator() const; // Return the integral accumulator
- void StartAutoTune(float maxTemp, float maxPwm, StringRef& reply); // Start an auto tune cycle for this PID
+ void StartAutoTune(float targetTemp, float maxPwm, StringRef& reply); // Start an auto tune cycle for this PID
bool IsTuning() const;
void GetAutoTuneStatus(StringRef& reply); // Get the auto tune status or last result
@@ -85,11 +92,17 @@ private:
void SetHeater(float power) const; // Power is a fraction in [0,1]
TemperatureError ReadTemperature(); // Read and store the temperature of this heater
void DoTuningStep(); // Called on each temperature sample when auto tuning
- bool ReadingsStable(size_t numReadings, float maxDiff) const
+ static bool ReadingsStable(size_t numReadings, float maxDiff)
pre(numReadings >= 2; numReadings <= MaxTuningTempReadings);
+#ifdef NEW_TUNING
+ static int GetPeakTempIndex(); // Auto tune helper function
+ static int IdentifyPeak(size_t numToAverage); // Auto tune helper function
+ void CalculateModel(); // Calculate G, td and tc from the accumulated readings
+#else
static size_t GetMaxRateIndex(); // Auto tune helper function
- void DisplayBuffer(const char *intro); // Debug helper
void FitCurve(); // Calculate G, td and tc from the accumulated readings
+#endif
+ void DisplayBuffer(const char *intro); // Debug helper
float GetExpectedHeatingRate() const; // Get the minimum heating rate we expect
Platform* platform; // The instance of the class that is the RepRap hardware
@@ -119,20 +132,30 @@ private:
static_assert(sizeof(previousTemperaturesGood) * 8 >= NumPreviousTemperatures, "too few bits in previousTemperaturesGood");
+ // Variables used during heater tuning
+ static const size_t MaxTuningTempReadings = 128; // The maximum number of readings we keep. Must be an even number.
+
static float *tuningTempReadings; // the readings from the heater being tuned
static float tuningStartTemp; // the temperature when we turned on the heater
static float tuningPwm; // the PWM to use, 0..1
- static float tuningMaxTemp; // the maximum temperature we are allowed to reach
+ static float tuningTargetTemp; // the maximum temperature we are allowed to reach
static uint32_t tuningBeginTime; // when we started the tuning process
static uint32_t tuningPhaseStartTime; // when we started the current tuning phase
static uint32_t tuningReadingInterval; // how often we are sampling, in milliseconds
static size_t tuningReadingsTaken; // how many temperature samples we have taken
+
+#ifdef NEW_TUNING
+ static float tuningHeaterOffTemp; // the temperature when we turned the heater off
+ static float tuningPeakTemperature; // the peak temperature reached, averaged over 3 readings (so slightly less than the true peak)
+ static uint32_t tuningHeatingTime; // how long we had the heating on for
+ static uint32_t tuningPeakDelay; // how many milliseconds the temperature continues to rise after turning the heater off
+#else
static float tuningTimeOfFastestRate; // how long after turn-on the fastest temperature rise occurred
static float tuningFastestRate; // the fastest temperature rise
+#endif
+};
- static const size_t MaxTuningTempReadings = 128; // The maximum number of readings we keep. Must be an even number.
-};
inline bool PID::Active() const
{
diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp
index c0c1cde4..45b007fa 100644
--- a/src/Movement/DDA.cpp
+++ b/src/Movement/DDA.cpp
@@ -195,6 +195,12 @@ 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 * const move = reprap.GetMove();
@@ -502,6 +508,105 @@ pre(state == provisional)
}
}
+// Try to push babystepping earlier in the move queue
+void DDA::AdvanceBabyStepping(float amount)
+{
+ DDA *cdda = this;
+ while (cdda->prev->state == DDAState::provisional)
+ {
+ cdda = cdda->prev;
+ }
+
+ // cdda addresses the earliest un-prepared move, which is the first one we can apply babystepping to
+ // Allow babystepping Z speed up to 10% of the move top speed or up to half the Z jerk rate, whichever is lower
+ float babySteppingDone = 0.0;
+ while(cdda != this)
+ {
+ float babySteppingToDo = 0.0;
+ if (amount != 0.0)
+ {
+ // Limit the babystepping Z speed to the lower of 0.1 times the original XYZ speed and 0.5 times the Z jerk
+ const float maxBabySteppingAmount = cdda->totalDistance * min<float>(0.1, 0.5 * reprap.GetPlatform()->ConfiguredInstantDv(Z_AXIS)/cdda->topSpeed);
+ babySteppingToDo = constrain<float>(amount, -maxBabySteppingAmount, maxBabySteppingAmount);
+ cdda->directionVector[Z_AXIS] += babySteppingToDo/cdda->totalDistance;
+ cdda->totalDistance *= Normalise(cdda->directionVector, DRIVES, reprap.GetGCodes()->GetNumAxes());
+ cdda->RecalculateMove();
+ babySteppingDone += babySteppingToDo;
+ amount -= babySteppingToDo;
+ }
+
+ // Even if there is no babystepping to do this move, we may need to adjust the end coordinates
+ cdda->endCoordinates[Z_AXIS] += babySteppingDone;
+ if (cdda->isDeltaMovement)
+ {
+ for (size_t tower = 0; tower < DELTA_AXES; ++tower)
+ {
+ cdda->endPoint[tower] += (int32_t)(babySteppingDone * reprap.GetPlatform()->DriveStepsPerUnit(tower));
+ if (babySteppingToDo != 0.0)
+ {
+ int32_t steps = (int32_t)(babySteppingToDo * reprap.GetPlatform()->DriveStepsPerUnit(tower));
+ DriveMovement& dm = cdda->ddm[tower];
+ if (dm.direction) // if moving up
+ {
+ steps += (int32_t)dm.totalSteps;
+ }
+ else
+ {
+ steps -= (int32_t)dm.totalSteps;
+ }
+ if (steps >= 0)
+ {
+ dm.direction = true;
+ dm.totalSteps = (uint32_t)steps;
+ }
+ else
+ {
+ dm.direction = false;
+ dm.totalSteps = (uint32_t)(-steps);
+ }
+ }
+ }
+ }
+ else
+ {
+ cdda->endPoint[Z_AXIS] += (int32_t)(babySteppingDone * reprap.GetPlatform()->DriveStepsPerUnit(Z_AXIS));
+ if (babySteppingToDo != 0.0)
+ {
+ int32_t steps = (int32_t)(babySteppingToDo * reprap.GetPlatform()->DriveStepsPerUnit(Z_AXIS));
+ DriveMovement& dm = cdda->ddm[Z_AXIS];
+ if (dm.state == DMState::moving)
+ {
+ if (dm.direction) // if moving up
+ {
+ steps += (int32_t)dm.totalSteps;
+ }
+ else
+ {
+ steps -= (int32_t)dm.totalSteps;
+ }
+ }
+ else
+ {
+ dm.state = DMState::moving;
+ }
+ if (steps >= 0)
+ {
+ dm.direction = true;
+ dm.totalSteps = (uint32_t)steps;
+ }
+ else
+ {
+ dm.direction = false;
+ dm.totalSteps = (uint32_t)(-steps);
+ }
+ }
+ }
+
+ // Now do the next move
+ cdda = cdda->next;
+ }
+}
+
// Recalculate the top speed, acceleration distance and deceleration distance, and whether we can pause after this move
// This may cause a move that we intended to be a deceleration-only move to have a tiny acceleration segment at the start
void DDA::RecalculateMove()
@@ -522,19 +627,25 @@ void DDA::RecalculateMove()
decelDistance = totalDistance - accelDistance;
topSpeed = sqrtf(vsquared);
}
- else if (startSpeed < endSpeed)
- {
- // This would ideally never happen, but might because of rounding errors
- accelDistance = totalDistance;
- decelDistance = 0.0;
- topSpeed = endSpeed;
- }
else
{
- // This would ideally never happen, but might because of rounding errors
- accelDistance = 0.0;
- decelDistance = totalDistance;
- topSpeed = startSpeed;
+ // It's an accelerate-only or decelerate-only move.
+ // Due to rounding errors and babystepping adjustments, we may have to adjust the acceleration slightly.
+ if (startSpeed < endSpeed)
+ {
+ // This would ideally never happen, but might because of rounding errors
+ accelDistance = totalDistance;
+ decelDistance = 0.0;
+ topSpeed = endSpeed;
+ acceleration = (fsquare(endSpeed) - fsquare(startSpeed))/(2.0 * totalDistance);
+ }
+ else
+ {
+ accelDistance = 0.0;
+ decelDistance = totalDistance;
+ topSpeed = startSpeed;
+ acceleration = (fsquare(startSpeed) - fsquare(endSpeed))/(2.0 * totalDistance);
+ }
}
}
else
diff --git a/src/Movement/DDA.h b/src/Movement/DDA.h
index 4ba65436..dfa2f1dd 100644
--- a/src/Movement/DDA.h
+++ b/src/Movement/DDA.h
@@ -106,8 +106,9 @@ 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
- static void DoLookahead(DDA *laDDA); // called by AdjustEndSpeed to do the real work
+ static void DoLookahead(DDA *laDDA); // Try to smooth out moves in the queue
static float Normalise(float v[], size_t dim1, size_t dim2); // Normalise a vector of dim1 dimensions to unit length in the first dim1 dimensions
static void Absolute(float v[], size_t dimensions); // Put a vector in the positive hyperquadrant
static float Magnitude(const float v[], size_t dimensions); // Return the length of a vector
diff --git a/src/Platform.cpp b/src/Platform.cpp
index 6946c4c6..9bd9140a 100644
--- a/src/Platform.cpp
+++ b/src/Platform.cpp
@@ -47,7 +47,7 @@ extern "C" char *sbrk(int i);
#ifdef DUET_NG
const uint16_t driverPowerOnAdcReading = (uint16_t)(4096 * 10.0/PowerFailVoltageRange); // minimum voltage at which we initialise the drivers
const uint16_t driverPowerOffAdcReading = (uint16_t)(4096 * 9.5/PowerFailVoltageRange); // voltages below this flag the drivers as unusable
-const uint16_t driverOverVoltageAdcReading = (uint16_t)(4096 * 29.5/PowerFailVoltageRange); // voltages above this cause driver shutdown
+const uint16_t driverOverVoltageAdcReading = (uint16_t)(4096 * 29.0/PowerFailVoltageRange); // voltages above this cause driver shutdown
const uint16_t driverNormalVoltageAdcReading = (uint16_t)(4096 * 27.5/PowerFailVoltageRange); // voltages at or below this are normal
#endif
@@ -1460,7 +1460,12 @@ void Platform::Diagnostics(MessageType mtype)
int slot = -1;
#ifdef DUET_NG
- if (flash_read_user_signature(reinterpret_cast<uint32_t*>(srdBuf), sizeof(srdBuf)/sizeof(uint32_t)) == FLASH_RC_OK)
+ // Work around bug in ASF flash library: flash_read_user_signature calls a RAMFUNC wito7ut disabling interrupts first.
+ // This caused a crash (watchdog timeout) sometimes if we run M122 while a print is in progress
+ const irqflags_t flags = cpu_irq_save();
+ const uint32_t rc = flash_read_user_signature(reinterpret_cast<uint32_t*>(srdBuf), sizeof(srdBuf)/sizeof(uint32_t));
+ cpu_irq_restore(flags);
+ if (rc == FLASH_RC_OK)
#else
DueFlashStorage::read(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf));
#endif
@@ -1501,11 +1506,6 @@ void Platform::Diagnostics(MessageType mtype)
// Show the current error codes
MessageF(mtype, "Error status: %u\n", errorCodeBits);
- //***TEMPORARY show the maximum PWM loop count, which should never exceed 1
- extern uint32_t maxPwmLoopCount;
- MessageF(mtype, "Max PWM loop count %u\n", maxPwmLoopCount);
- maxPwmLoopCount = 0;
-
// Show the number of free entries in the file table
unsigned int numFreeFiles = 0;
for (size_t i = 0; i < MAX_FILES; i++)
diff --git a/src/Reprap.cpp b/src/Reprap.cpp
index d2ac20ca..3eee7bc6 100644
--- a/src/Reprap.cpp
+++ b/src/Reprap.cpp
@@ -651,7 +651,8 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
response->catf("%c%.2f", ch, gCodes->GetExtrusionFactor(extruder) * 100.0);
ch = ',';
}
- response->cat((ch == '[') ? "[]}" : "]}");
+ response->cat((ch == '[') ? "[]" : "]");
+ response->catf(",\"babystep\":%.03f}", gCodes->GetBabyStepOffset());
}
// G-code reply sequence for webserver (seqence number for AUX is handled later)
@@ -1188,6 +1189,9 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
}
response->cat((ch == '[') ? "[]" : "]");
+ // Send the baby stepping offset
+ response->catf(",\"babystep\":%.03f", gCodes->GetBabyStepOffset());
+
// Send the current tool number
const int toolNumber = (currentTool == nullptr) ? 0 : currentTool->Number();
response->catf(",\"tool\":%d", toolNumber);
diff --git a/src/Version.h b/src/Version.h
index adf9827e..03326bb3 100644
--- a/src/Version.h
+++ b/src/Version.h
@@ -9,11 +9,11 @@
#define SRC_VERSION_H_
#ifndef VERSION
-# define VERSION "1.18alpha2"
+# define VERSION "1.18beta1"
#endif
#ifndef DATE
-# define DATE "2017-02-17"
+# define DATE "2017-02-21"
#endif
#define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman"