diff options
author | David Crocker <dcrocker@eschertech.com> | 2017-02-21 23:16:06 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2017-02-21 23:16:26 +0300 |
commit | 7724eed928456e3e39fd6333202ca1dd4f61ab01 (patch) | |
tree | 2690dc6eb68e9c2b59713d13c7ac27097bb1f885 /src | |
parent | c4d3b9eb8d869f3b04a6cc888d2b2b2a7069459e (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.cpp | 8 | ||||
-rw-r--r-- | src/DuetNG/DuetEthernet/NetworkTransaction.cpp | 2 | ||||
-rw-r--r-- | src/DuetNG/DuetEthernet/Socket.cpp | 26 | ||||
-rw-r--r-- | src/GCodes/GCodeBuffer.cpp | 2 | ||||
-rw-r--r-- | src/GCodes/GCodeBuffer.h | 3 | ||||
-rw-r--r-- | src/GCodes/GCodes.cpp | 176 | ||||
-rw-r--r-- | src/GCodes/GCodes.h | 8 | ||||
-rw-r--r-- | src/GCodes/GCodes2.cpp | 89 | ||||
-rw-r--r-- | src/Heating/Pid.cpp | 229 | ||||
-rw-r--r-- | src/Heating/Pid.h | 35 | ||||
-rw-r--r-- | src/Movement/DDA.cpp | 133 | ||||
-rw-r--r-- | src/Movement/DDA.h | 3 | ||||
-rw-r--r-- | src/Platform.cpp | 14 | ||||
-rw-r--r-- | src/Reprap.cpp | 6 | ||||
-rw-r--r-- | src/Version.h | 4 |
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" |