diff --git a/.cproject b/.cproject
index c8e57e4485..a08d73f145 100644
--- a/.cproject
+++ b/.cproject
@@ -7,7 +7,7 @@
-
+
@@ -122,7 +122,7 @@
-
+
@@ -232,7 +232,7 @@
-
+
@@ -353,7 +353,7 @@
-
+
@@ -473,7 +473,7 @@
-
+
@@ -593,7 +593,7 @@
-
+
diff --git a/src/BugList.txt b/src/BugList.txt
index e920e51f39..a72885b31a 100644
--- a/src/BugList.txt
+++ b/src/BugList.txt
@@ -195,7 +195,7 @@ WiFi:
- [resolve library incompatibility] New ESP8266 SDK
- [done] check what's new in Arduino ESP 2.4.1
-Bug fixes/investigations:
+Done in 2.01beta1:
- [done, test] sqrt(2) error factor in extrusion-only moves, see Michael's email
- [done] Homing wrong if axes not labeled sequentially, see Christian's email of 18/6/18
- [done, test] Fix "2dtstc2diva=u" in debug printout, https://forum.duet3d.com/topic/3139/high-level-of-steperrors-what-can-cause-them/5
@@ -212,30 +212,42 @@ Bug fixes/investigations:
- [done] Add SW_ENC pin on CONN_SD to available GPIO ports
- [done] Increased M999 delay to 1 second
- [done] If M911 resume threshold is set too high, save-on-power-off never primes itself
+- [done] display mutex owners in task diagnostics
+
+Bug fixes/investigations for 2.02beta2:
+- [done, test] GCode queue overflow, see https://forum.duet3d.com/topic/5821/major-performance-problem. Suspend processing until queue space is available.
+- [done, test M28/M29 and M559/M560 file uploads] M28 and M29 in a macro, https://forum.duet3d.com/topic/5642/filament-load-macro-writing-to-file-in-sys. Move fileBeingWritten to GCodeBuffer, get rid of writingFileDirectory.
+- [done, test] we get a Pop underflow message when you send DWC jog commands and axes are not homed
+- [done, test] clear bed compensation not working properly, https://forum.duet3d.com/topic/5978/dwc-ui-position-updates-are-behind-causing-leveling-problems/6
+- [done] Duet Maestro default to TMC2224 drivers on expansion
+- [done] bltouch now uses unfiltered digital probe mode
+- [done] Reset/retry after SD card error - we have confirmation that fatfs is reporting a low-level error (error 1)
+- [increased retries, test detection] Doesn't find DueX5 after M999 reset? https://forum.duet3d.com/topic/5713/duet2-with-2-0-rtos-looses-duex5-on-reset
+- [made some changes] Test Duet 06/085 DHCP problem again, see https://forum.duet3d.com/topic/5572/it-s-out-reprapfirmware-2-0-and-1-21-1-released/16 for config.g files
+- [done, test] resurrect.g to include G92 commands to set head position to the power fail position
+- [done] Software watchdog to watch Heat task
+- [done, test] When an under-voltage event occurs, spurious driver status warnings/error were sometimes reported
+- [done, test] When an under- or over-voltage event occurred, the VIN voltage reported was the current voltage, not the voltage when the event was recorded
+- [done, test] Software reset data now includes which task was active
+- [done, test] Step errors on a drive corresponding to an invisible axis, https://forum.duet3d.com/topic/3139/high-level-of-steperrors-what-can-cause-them/5
+
+- [awaiting reply] Step errors when usimng Numbe and pressure advance, https://forum.duet3d.com/topic/3139/high-level-of-steperrors-what-can-cause-them/5
+- [awaiting reply] GCode file that pauses between parts, https://forum.duet3d.com/topic/5702/printer-keeps-pausing-during-print/15
+- [looks like it was a temperature sensing error] Is there a kink during auto tuning? https://forum.duet3d.com/topic/5582/hot-end-auto-tuning-failed-due. Could explain oddly low dead times from auto tuning.
+- Pressure advance, see https://www.duet3d.com/forum/thread.php?pid=38036#p38036 and https://forum.duet3d.com/topic/1935/more-strange-pressure-advance-behaviour/95
+- Stuck in spin loop, https://forum.duet3d.com/topic/5674/damned-crash-stuck-in-spin-loop
+- Report that if you use FileZilla to upload several short files, some of them are written blank, https://forum.duet3d.com/topic/5992/files-uploaded-over-ftp-occasionally-blank
-- Test gcode file that pauses between parts, https://forum.duet3d.com/topic/5702/printer-keeps-pausing-during-print/15
-- Doesn't find DueX5 after M999 reset? https://forum.duet3d.com/topic/5713/duet2-with-2-0-rtos-looses-duex5-on-reset
-- stuck in spin loop, https://forum.duet3d.com/topic/5674/damned-crash-stuck-in-spin-loop
-- step errors, see https://forum.duet3d.com/topic/3139/high-level-of-steperrors-what-can-cause-them/5
- Investigate failed .BMP conversion, https://forum.duet3d.com/topic/5623/boot-logo-corruption-in-1-21/2
-- Duet Maestro default to TMC2224 drivers on expansion? How to detect them?
-- M28 and M29 in a macro, https://forum.duet3d.com/topic/5642/filament-load-macro-writing-to-file-in-sys. Move fileBeingWritten to GCodeBuffer, get rid of writingFileDirectory.
-- Is there a kink during auto tuning? https://forum.duet3d.com/topic/5582/hot-end-auto-tuning-failed-due. Could explain oddly low dead times from auto tuning.
-- Does bltouch need a default recovery time to allow it to deploy? (probably not)
-- Should bltouch use digital probe mode? Some users having problems with P25 in G31 command.
-- Test Duet 06/085 DHCP problem again, see https://forum.duet3d.com/topic/5572/it-s-out-reprapfirmware-2-0-and-1-21-1-released/16 for config.g files
-- Retry after SD card error?
Other:
-- @wilriker PR for M106 PWM limit
-- [done, test] display mutex owners in task diagnostics
Planned for 2.02:
-- WiFi continuous auto reconnect, or extra M552 parameter? See https://forum.duet3d.com/topic/5765/wifi-module-auto-reconnect
+- @wilriker PR for M106 PWM limit
+- WiFi continuous auto reconnect in client mode, or extra M552 parameter? See https://forum.duet3d.com/topic/5765/wifi-module-auto-reconnect
- Better dead time measurement during auto tuning. Measure both turn-on and turn-off?
- Bezier speed curves or other S-curve acceleration, e.g. look at https://github.com/MarlinFirmware/Marlin/pull/10373/files
- Option to send M280 servo commands just a few times instead of continuously, for E3D
-- Recovery from SD card errors during a print (Dan)
- Danny's modified SCARA kinematics (workpiece is on a plate, extruder is fixed)
- Laser support via G1 S parameter, see https://forum.duet3d.com/topic/4702/laser-cnc-support-in-rrf-gcode-semantics/4
- Look at pushover notification support, https://forum.duet3d.com/topic/169/notification-via-pushover-or-other-service/45
@@ -244,11 +256,10 @@ Planned for 2.02:
- M584 when assigning a drive, unmap any existing assignment. Also allow an axis to be mapped to driver -1.
- Add S4 option to G1 command, like S1 but no endstop checks (needed for CoreXY, CoreXZ)
- CNC: look at G17/18/19, see https://forum.duet3d.com/topic/4669/ooznest-workbee-screw-driven
-- M569 command to allow selection of smart/dumb driver, also allow all 12 drivers to be smart
+- M569 command to allow selection of smart/dumb driver (including on Duet M), also allow all 12 drivers to be smart
- M569 TOFF parameter, https://forum.duet3d.com/topic/5392/does-m906-set-rms-or-peak-current/28
- Apostrophe in quoted filename: can we make apostrophe special in SSIDs/passwords but not filenames?
- If wifi module gets stuck in starting or changing mode state, reset it again
-- If wifi disconnects when in client mode, keep retrying the connection
- M140 command to set default bed heaters for M140 S commands (e.g. M140 P0:1:2)
Other (some of these may be in 2.02):
@@ -260,7 +271,6 @@ Other (some of these may be in 2.02):
- support M205 for setting jerk
- support G12 clean tool?
- Add fan PWM limit, https://forum.duet3d.com/topic/5370/m106-feature-request-limit-max-pwm-parameter/4
-- M116 extra "close enough" parameter
- after homing, warn if outside M208 movement limits on SCARA, polar etc.
- Unexpected heaters off/tool selection behaviour, https://www.duet3d.com/forum/thread.php?pid=43059#p43059
- warn when using : where ; was probably meant
@@ -271,7 +281,6 @@ Other (some of these may be in 2.02):
- Auto mount main SD card when inserted
- Workplace offsets are supposed to be persistant (check NIST), https://www.duet3d.com/forum/thread.php?pid=43755#p43755
- At the end of a simulation, restore the original workplace coordinate selection
-- looks like we get a Pop underflow message when you send DWC jog commands and axes are not homed
- Add warning message when print exceeds bounds
- When uploading while a file is being printed, don't allow the currently-printing file to be replaced
@@ -288,7 +297,6 @@ Other (some of these may be in 2.02):
- laser control, https://www.duet3d.com/forum/thread.php?pid=37891#p37891
- slow DWC but fast FTP, https://www.duet3d.com/forum/thread.php?pid=38345#p38345
- no stall detect on Z axis, https://www.duet3d.com/forum/thread.php?pid=38504#p38504, https://www.duet3d.com/forum/thread.php?pid=39484#p39484
-- Pressure advance, see https://www.duet3d.com/forum/thread.php?pid=38036#p38036
- check that we never enable the drivers before we set vsense
- [don't do] Don't report the motor current for a non-existent extruder
diff --git a/src/Configuration.h b/src/Configuration.h
index 13f5c9d86b..d1f602326a 100644
--- a/src/Configuration.h
+++ b/src/Configuration.h
@@ -154,6 +154,8 @@ static_assert(MaxCalibrationPoints <= MaxProbePoints, "MaxCalibrationPoints must
// SD card
constexpr uint32_t SdCardDetectDebounceMillis = 200; // How long we give the SD card to settle in the socket
+constexpr unsigned int MaxSdCardTries = 3; // Number of read or write attempts before giving up
+constexpr uint32_t SdCardRetryDelay = 10; // Number of milliseconds delay between SD transfer retries
// Z probing
constexpr float DefaultZDive = 5.0; // Millimetres
@@ -177,8 +179,10 @@ constexpr size_t PASSWORD_LENGTH = 20;
#if SAM4E || SAM4S || SAME70
// Increased GCODE_LENGTH on the SAM4 because M587 and M589 commands on the Duet WiFi can get very long
constexpr size_t GCODE_LENGTH = 161; // maximum number of non-comment characters in a line of GCode including the null terminator
+constexpr size_t SHORT_GCODE_LENGTH = 61; // maximum length of a GCode that we can queue to synchronise it to a move
#else
constexpr size_t GCODE_LENGTH = 101; // maximum number of non-comment characters in a line of GCode including the null terminator
+constexpr size_t SHORT_GCODE_LENGTH = 61; // maximum length of a GCode that we can queue to synchronise it to a move
#endif
constexpr size_t MaxMessageLength = 256;
@@ -207,6 +211,8 @@ constexpr size_t RESERVED_OUTPUT_BUFFERS = 2; // Number of reserved output buf
# error
#endif
+const size_t maxQueuedCodes = 16; // How many codes can be queued?
+
// Move system
constexpr float DefaultFeedRate = 3000.0; // The initial requested feed rate after resetting the printer, in mm/min
constexpr float DefaultG0FeedRate = 18000; // The initial feed rate for G0 commands after resetting the printer, in mm/min
diff --git a/src/Duet/Lwip/contrib/apps/mdns/mdns_responder.c b/src/Duet/Lwip/contrib/apps/mdns/mdns_responder.c
index 5353b367c9..167b38e7c0 100644
--- a/src/Duet/Lwip/contrib/apps/mdns/mdns_responder.c
+++ b/src/Duet/Lwip/contrib/apps/mdns/mdns_responder.c
@@ -544,7 +544,11 @@ static void setup_hostnames(struct mdns_state *ms, struct netif *netif)
sprintf(ms->hostnames[1], "%c%s-%02X%s", hostlen+3, netif->hostname,
netif->hwaddr[5], dotlocal);
+#if 1 // DC42
+ char macaddr[13];
+#else
char macaddr[12];
+#endif
sprintf(macaddr, "%02X%02X%02X%02X%02X%02X",
netif->hwaddr[0], netif->hwaddr[1], netif->hwaddr[2],
netif->hwaddr[3], netif->hwaddr[4], netif->hwaddr[5]);
diff --git a/src/Duet/Network.cpp b/src/Duet/Network.cpp
index a0ddfaa5f4..7713c2d8ae 100644
--- a/src/Duet/Network.cpp
+++ b/src/Duet/Network.cpp
@@ -160,8 +160,11 @@ static void ethernet_rx_callback(uint32_t ul_status)
static void conn_err(void *arg, err_t err)
{
- // Report the error to the monitor
- reprap.GetPlatform().MessageF(UsbMessage, "Network: Connection error, code %d\n", err);
+ if (reprap.Debug(moduleNetwork) && !inInterrupt())
+ {
+ // Report the error to the monitor
+ reprap.GetPlatform().MessageF(UsbMessage, "Network: Connection error, code %d\n", err);
+ }
// Tell the higher levels about the error
ConnectionState *cs = (ConnectionState*)arg;
@@ -179,7 +182,10 @@ static err_t conn_recv(void *arg, tcp_pcb *pcb, pbuf *p, err_t err)
{
if (cs->pcb != pcb)
{
- reprap.GetPlatform().Message(UsbMessage, "Network: Mismatched pcb in conn_recv!\n");
+ if (reprap.Debug(moduleNetwork) && !inInterrupt())
+ {
+ reprap.GetPlatform().Message(UsbMessage, "Network: Mismatched pcb in conn_recv!\n");
+ }
tcp_abort(pcb);
return ERR_ABRT;
}
diff --git a/src/DuetM/TMC22xx.cpp b/src/DuetM/TMC22xx.cpp
index 5f373c26eb..4a71f2d4da 100644
--- a/src/DuetM/TMC22xx.cpp
+++ b/src/DuetM/TMC22xx.cpp
@@ -268,6 +268,7 @@ class TmcDriverState
void AppendDriverStatus(const StringRef& reply);
uint8_t GetDriverNumber() const { return driverNumber; }
bool UpdatePending() const { return registersToUpdate != 0; }
+ bool UsesGlobalEnable() const { return enablePin == NoPin; }
float GetStandstillCurrentPercent() const;
void SetStandstillCurrentPercent(float percent);
@@ -640,24 +641,27 @@ inline void TmcDriverState::TransferDone()
}
lastIfCount = currentIfCount;
}
- else if (sendData[2] == ReadRegNumbers[registerToRead] && ReadRegNumbers[registerToRead] == receiveData[6] && receiveData[4] == 0x05 && receiveData[5] == 0xFF)
+ else if (driversState != DriversState::noPower) // we don't check the CRC, so only accept the result if power is still good
{
- // We asked to read the scheduled read register, and the sync byte, slave address and register number in the received message match
- //TODO here we could check the CRC of the received message, but for now we assume that we won't get any corruption in the 32-bit received data
- const uint32_t regVal = ((uint32_t)receiveData[7] << 24) | ((uint32_t)receiveData[8] << 16) | ((uint32_t)receiveData[9] << 8) | receiveData[10];
- readRegisters[registerToRead] = regVal;
- accumulatedReadRegisters[registerToRead] |= regVal;
-
- ++registerToRead;
- if (registerToRead == NumReadRegisters)
+ if (sendData[2] == ReadRegNumbers[registerToRead] && ReadRegNumbers[registerToRead] == receiveData[6] && receiveData[4] == 0x05 && receiveData[5] == 0xFF)
{
- registerToRead = 0;
+ // We asked to read the scheduled read register, and the sync byte, slave address and register number in the received message match
+ //TODO here we could check the CRC of the received message, but for now we assume that we won't get any corruption in the 32-bit received data
+ const uint32_t regVal = ((uint32_t)receiveData[7] << 24) | ((uint32_t)receiveData[8] << 16) | ((uint32_t)receiveData[9] << 8) | receiveData[10];
+ readRegisters[registerToRead] = regVal;
+ accumulatedReadRegisters[registerToRead] |= regVal;
+
+ ++registerToRead;
+ if (registerToRead == NumReadRegisters)
+ {
+ registerToRead = 0;
+ }
+ ++numReads;
+ }
+ else
+ {
+ ++readErrors;
}
- ++numReads;
- }
- else
- {
- ++readErrors;
}
}
@@ -903,7 +907,6 @@ namespace SmartDrivers
if (powered)
{
// Power to the drivers has been provided or restored, so we need to enable and re-initialise them
-
for (size_t drive = 0; drive < numTmc22xxDrivers; ++drive)
{
driverStates[drive].WriteAll();
@@ -938,11 +941,11 @@ namespace SmartDrivers
if (driversState == DriversState::initialising)
{
- // If all drivers have been initialised, set the global enable
+ // If all drivers that share the global enable have been initialised, set the global enable
bool allInitialised = true;
for (size_t i = 0; i < numTmc22xxDrivers; ++i)
{
- if (driverStates[i].UpdatePending())
+ if (driverStates[i].UsesGlobalEnable() && driverStates[i].UpdatePending())
{
allInitialised = false;
break;
diff --git a/src/DuetNG/DueXn.cpp b/src/DuetNG/DueXn.cpp
index 1e3418b250..3a6425596d 100644
--- a/src/DuetNG/DueXn.cpp
+++ b/src/DuetNG/DueXn.cpp
@@ -59,12 +59,15 @@ namespace DuetExpansion
{
reprap.GetPlatform().InitI2c(); // initialise I2C
- bool ret = dueXnExpander.begin(DueXnAddress);
- if (!ret)
+ // DC 2018-07-12: occasionally the SX1509B isn't found after doing a software reset, so try a few more attempts
+ bool ret;
+ unsigned int attempts = 0;
+ do
{
- delay(100); // wait a little while
- ret = dueXnExpander.begin(DueXnAddress); // do 1 retry
- }
+ ++attempts;
+ delay(50);
+ ret = dueXnExpander.begin(DueXnAddress);
+ } while (!ret && attempts < 5);
if (ret)
{
@@ -102,12 +105,15 @@ namespace DuetExpansion
void AdditionalOutputInit()
{
reprap.GetPlatform().InitI2c(); // initialise I2C
- bool ret = additionalIoExpander.begin(AdditionalIoExpanderAddress);
- if (!ret)
+
+ bool ret;
+ unsigned int attempts = 0;
+ do
{
- delay(100); // wait a little while
- ret = additionalIoExpander.begin(AdditionalIoExpanderAddress); // do 1 retry
- }
+ ++attempts;
+ delay(50);
+ ret = additionalIoExpander.begin(AdditionalIoExpanderAddress);
+ } while (!ret && attempts < 5);
if (ret)
{
diff --git a/src/DuetNG/SX1509.cpp b/src/DuetNG/SX1509.cpp
index 9b7a54d735..3c601e28bb 100644
--- a/src/DuetNG/SX1509.cpp
+++ b/src/DuetNG/SX1509.cpp
@@ -21,11 +21,10 @@ local, and you've found our code helpful, please buy us a round!
Distributed as-is; no warranty is given.
******************************************************************************/
-#include "Core.h"
+#include "RepRapFirmware.h"
#include "Wire.h"
#include "SX1509.h"
#include "SX1509Registers.h"
-#include "Pins.h"
SX1509::SX1509() : _clkX(0), errorCount(0)
{
@@ -38,6 +37,7 @@ bool SX1509::begin(uint8_t address)
deviceAddress = address;
reset();
+ delay(2); // not sure this is needed, but it may help
pwmPins = 0;
diff --git a/src/DuetNG/TMC2660.cpp b/src/DuetNG/TMC2660.cpp
index c601fcf0fc..afaf625eaa 100644
--- a/src/DuetNG/TMC2660.cpp
+++ b/src/DuetNG/TMC2660.cpp
@@ -501,27 +501,30 @@ uint32_t TmcDriverState::GetChopConf() const
// This is called by the ISR when the SPI transfer has completed
inline void TmcDriverState::TransferDone()
{
- fastDigitalWriteHigh(pin); // set the CS pin high for the driver we just polled
- uint32_t status = be32_to_cpu(spiDataIn) >> 12; // get the status
- const uint32_t interval = reprap.GetMove().GetStepInterval(axisNumber, microstepShiftFactor); // get the full step interval
- if (interval == 0 || interval > maxStallStepInterval) // if the motor speed is too low to get reliable stall indication
+ fastDigitalWriteHigh(pin); // set the CS pin high for the driver we just polled
+ if (driversPowered) // if the power is still good, update the status
{
- status &= ~TMC_RR_SG; // remove the stall status bit
- }
- else
- {
- const uint32_t sgLoad = (status >> TMC_RR_SG_LOAD_SHIFT) & 1023; // get the StallGuard load register
- if (sgLoad < minSgLoadRegister)
+ uint32_t status = be32_to_cpu(spiDataIn) >> 12; // get the status
+ const uint32_t interval = reprap.GetMove().GetStepInterval(axisNumber, microstepShiftFactor); // get the full step interval
+ if (interval == 0 || interval > maxStallStepInterval) // if the motor speed is too low to get reliable stall indication
{
- minSgLoadRegister = sgLoad;
+ status &= ~TMC_RR_SG; // remove the stall status bit
}
- if (sgLoad > maxSgLoadRegister)
+ else
{
- maxSgLoadRegister = sgLoad;
+ const uint32_t sgLoad = (status >> TMC_RR_SG_LOAD_SHIFT) & 1023; // get the StallGuard load register
+ if (sgLoad < minSgLoadRegister)
+ {
+ minSgLoadRegister = sgLoad;
+ }
+ if (sgLoad > maxSgLoadRegister)
+ {
+ maxSgLoadRegister = sgLoad;
+ }
}
+ lastReadStatus = status;
+ accumulatedStatus |= status;
}
- lastReadStatus = status;
- accumulatedStatus |= status;
}
// This is called from the ISR or elsewhere to start a new SPI transfer. Inlined for ISR speed.
diff --git a/src/GCodes/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer.cpp
index 3c190852da..de09870202 100644
--- a/src/GCodes/GCodeBuffer.cpp
+++ b/src/GCodes/GCodeBuffer.cpp
@@ -8,14 +8,16 @@
//*************************************************************************************
#include "GCodeBuffer.h"
-
+#include "GCodes.h"
#include "GCodeInput.h"
#include "Platform.h"
#include "RepRap.h"
+static constexpr char eofString[] = EOF_STRING; // What's at the end of an HTML file?
+
// Create a default GCodeBuffer
GCodeBuffer::GCodeBuffer(const char* id, MessageType mt, bool usesCodeQueue)
- : machineState(new GCodeMachineState()), identity(id), writingFileDirectory(nullptr),
+ : machineState(new GCodeMachineState()), identity(id), fileBeingWritten(nullptr), writingFileSize(0), eofStringCounter(0),
toolNumberAdjust(0), responseMessageType(mt), checksumRequired(false), queueCodes(usesCodeQueue), binaryWriting(false)
{
Init();
@@ -253,7 +255,7 @@ bool GCodeBuffer::LineFinished()
gcodeBuffer[gcodeLineEnd] = 0;
const bool badChecksum = (hadChecksum && computedChecksum != declaredChecksum);
const bool missingChecksum = (checksumRequired && !hadChecksum && machineState->previous == nullptr);
- if (reprap.Debug(moduleGcodes) && !writingFileDirectory)
+ if (reprap.Debug(moduleGcodes) && fileBeingWritten == nullptr)
{
reprap.GetPlatform().MessageF(DebugMessage, "%s%s: %s\n", identity, ((badChecksum) ? "(bad-csum)" : (missingChecksum) ? "(no-csum)" : ""), gcodeBuffer);
}
@@ -760,6 +762,28 @@ void GCodeBuffer::TryGetBValue(char c, bool& val, bool& seen)
}
}
+// Try to get an int array exactly 'numVals' long after parameter letter 'c'.
+// If the wrong number of values is provided, generate an error message and return true.
+// Else set 'seen' if we saw the letter and value, and return false.
+bool GCodeBuffer::TryGetUIArray(char c, size_t numVals, uint32_t vals[], const StringRef& reply, bool& seen, bool doPad)
+{
+ if (Seen(c))
+ {
+ size_t count = numVals;
+ GetUnsignedArray(vals, count, doPad);
+ if (count == numVals)
+ {
+ seen = true;
+ }
+ else
+ {
+ reply.printf("Wrong number of values after '\''%c'\'', expected %d", c, numVals);
+ return true;
+ }
+ }
+ return false;
+}
+
// Try to get a float array exactly 'numVals' long after parameter letter 'c'.
// If the wrong number of values is provided, generate an error message and return true.
// Else set 'seen' if we saw the letter and value, and return false.
@@ -962,16 +986,20 @@ bool GCodeBuffer::PopState()
}
// Abort execution of any files or macros being executed, returning true if any files were closed
+// We now avoid popping the state if we were not executing from a file, so that if DWC or PanelDue is used to jog the axes before they are homed, we don't report stack underflow.
void GCodeBuffer::AbortFile(FileGCodeInput* fileInput)
{
- do
+ if (machineState->fileState.IsLive())
{
- if (machineState->fileState.IsLive())
+ do
{
- fileInput->Reset(machineState->fileState);
- machineState->fileState.Close();
- }
- } while (PopState()); // abandon any macros
+ if (machineState->fileState.IsLive())
+ {
+ fileInput->Reset(machineState->fileState);
+ machineState->fileState.Close();
+ }
+ } while (PopState()); // abandon any macros
+ }
}
// Return true if this source is executing a file macro
@@ -1011,4 +1039,95 @@ void GCodeBuffer::PrintCommand(const StringRef& s) const
}
}
+// Open a file to write to
+bool GCodeBuffer::OpenFileToWrite(const char* directory, const char* fileName, const FilePosition size, const bool binaryWrite, const uint32_t fileCRC32)
+{
+ fileBeingWritten = reprap.GetPlatform().OpenFile(directory, fileName, OpenMode::write);
+ eofStringCounter = 0;
+ writingFileSize = size;
+ if (fileBeingWritten == nullptr)
+ {
+ return false;
+ }
+
+ crc32 = fileCRC32;
+ binaryWriting = binaryWrite;
+ return true;
+}
+
+// Write the current GCode to file
+void GCodeBuffer::WriteToFile()
+{
+ if (GetCommandLetter() == 'M')
+ {
+ if (GetCommandNumber() == 29) // end of file?
+ {
+ fileBeingWritten->Close();
+ fileBeingWritten = nullptr;
+ const char* const r = (reprap.GetPlatform().Emulating() == Compatibility::marlin) ? "Done saving file." : "";
+ reprap.GetGCodes().HandleReply(*this, GCodeResult::ok, r);
+ SetFinished(true);
+ return;
+ }
+ }
+ else if (GetCommandLetter() == 'G' && GetCommandNumber() == 998) // resend request?
+ {
+ if (Seen('P'))
+ {
+ String scratchString;
+ scratchString.printf("%" PRIi32 "\n", GetIValue());
+ reprap.GetGCodes().HandleReply(*this, GCodeResult::ok, scratchString.c_str());
+ SetFinished(true);
+ return;
+ }
+ }
+
+ fileBeingWritten->Write(Buffer());
+ fileBeingWritten->Write('\n');
+ SetFinished(true);
+}
+
+void GCodeBuffer::WriteBinaryToFile(char b)
+{
+ if (b == eofString[eofStringCounter] && writingFileSize == 0)
+ {
+ eofStringCounter++;
+ if (eofStringCounter < ARRAY_SIZE(eofString) - 1)
+ {
+ return; // not reached end of input yet
+ }
+ }
+ else
+ {
+ if (eofStringCounter != 0)
+ {
+ for (uint8_t i = 0; i < eofStringCounter; i++)
+ {
+ fileBeingWritten->Write(eofString[i]);
+ }
+ eofStringCounter = 0;
+ }
+ fileBeingWritten->Write(b); // writing one character at a time isn't very efficient, but uploading HTML files via USB is rarely done these days
+ if (writingFileSize == 0 || fileBeingWritten->Length() < writingFileSize)
+ {
+ return; // not reached end of input yet
+ }
+ }
+
+ // If we get here then we have come to the end of the data
+ fileBeingWritten->Close();
+ const bool crcOk = (crc32 == fileBeingWritten->GetCRC32() || crc32 == 0);
+ fileBeingWritten = nullptr;
+ binaryWriting = false;
+ if (crcOk)
+ {
+ const char* const r = (reprap.GetPlatform().Emulating() == Compatibility::marlin) ? "Done saving file." : "";
+ reprap.GetGCodes().HandleReply(*this, GCodeResult::ok, r);
+ }
+ else
+ {
+ reprap.GetGCodes().HandleReply(*this, GCodeResult::error, "CRC32 checksum doesn't match");
+ }
+}
+
// End
diff --git a/src/GCodes/GCodeBuffer.h b/src/GCodes/GCodeBuffer.h
index 6675cf2a49..bd34a3547a 100644
--- a/src/GCodes/GCodeBuffer.h
+++ b/src/GCodes/GCodeBuffer.h
@@ -48,7 +48,8 @@ class GCodeBuffer
void TryGetUIValue(char c, uint32_t& val, bool& seen);
void TryGetBValue(char c, bool& val, bool& seen);
bool TryGetFloatArray(char c, size_t numVals, float vals[], const StringRef& reply, bool& seen, bool doPad = false);
- bool TryGetQuotedString(char c, const StringRef& str, bool& seen);
+ bool TryGetUIArray(char c, size_t numVals, uint32_t vals[], const StringRef& reply, bool& seen, bool doPad = false);
+ bool TryGetQuotedString(char c, const StringRef& str, bool& seen);
bool TryGetPossiblyQuotedString(char c, const StringRef& str, bool& seen);
const char* Buffer() const;
@@ -57,8 +58,6 @@ class GCodeBuffer
bool IsReady() const; // Return true if a gcode is ready but hasn't been started yet
bool IsExecuting() const; // Return true if a gcode has been started and is not paused
void SetFinished(bool f); // Set the G Code executed (or not)
- const char* WritingFileDirectory() const; // If we are writing the G Code to a file, where that file is
- void SetWritingFileDirectory(const char* wfd); // Set the directory for the file to write the GCode in
int GetToolNumberAdjust() const { return toolNumberAdjust; }
void SetToolNumberAdjust(int arg) { toolNumberAdjust = arg; }
void SetCommsProperties(uint32_t arg) { checksumRequired = (arg & 1); }
@@ -79,10 +78,16 @@ class GCodeBuffer
bool CanQueueCodes() const;
void MessageAcknowledged(bool cancelled);
FilePosition GetFilePosition(size_t bytesCached) const; // Get the file position at the start of the current command
- bool IsWritingBinary() const; // returns true if writing binary
- void SetBinaryWriting(bool state); // set true if writing binary
- uint32_t GetCRC32() const;
- void SetCRC32(uint32_t newCRC32);
+
+ bool OpenFileToWrite(const char* directory, const char* fileName, const FilePosition size, const bool binaryWrite, const uint32_t fileCRC32); // open a file to write to
+ bool IsWritingFile() const { return fileBeingWritten != nullptr; } // returns true if writing a file
+ void WriteToFile(); // write the current GCode to file
+
+ bool IsWritingBinary() const { return IsWritingFile() && binaryWriting; } // returns true if writing binary
+ void WriteBinaryToFile(char b); // write a byte to the file
+
+ size_t CommandLength() const { return commandEnd - commandStart; } // get the length of the current command
+ const char* CommandStart() const { return gcodeBuffer + commandStart; } // get the start of the current command
void PrintCommand(const StringRef& s) const;
@@ -123,7 +128,11 @@ class GCodeBuffer
unsigned int gcodeLineEnd; // Number of characters in the entire line of gcode
int readPointer; // Where in the buffer to read next
GCodeBufferState bufferState; // Idle, executing or paused
- const char* writingFileDirectory; // If the G Code is going into a file, where that is
+
+ FileStore *fileBeingWritten; // If we are copying GCodes to a file, which file it is
+ FilePosition writingFileSize; // Size of the file being written, or zero if not known
+ uint8_t eofStringCounter; // Check the...
+
int toolNumberAdjust; // The adjustment to tool numbers in commands we receive
const MessageType responseMessageType; // The message type we use for responses to commands coming from this channel
unsigned int lineNumber;
@@ -145,26 +154,6 @@ class GCodeBuffer
bool binaryWriting; // Executing gcode or writing binary file?
};
-inline uint32_t GCodeBuffer::GetCRC32() const
-{
- return crc32;
-}
-
-inline void GCodeBuffer::SetCRC32(uint32_t newCRC32)
-{
- crc32 = newCRC32;
-}
-
-inline bool GCodeBuffer::IsWritingBinary() const
-{
- return binaryWriting;
-}
-
-inline void GCodeBuffer::SetBinaryWriting(bool state)
-{
- binaryWriting = state;
-}
-
inline const char* GCodeBuffer::Buffer() const
{
return gcodeBuffer;
@@ -190,16 +179,6 @@ inline bool GCodeBuffer::IsExecuting() const
return bufferState == GCodeBufferState::executing;
}
-inline const char* GCodeBuffer::WritingFileDirectory() const
-{
- return writingFileDirectory;
-}
-
-inline void GCodeBuffer::SetWritingFileDirectory(const char* wfd)
-{
- writingFileDirectory = wfd;
-}
-
inline GCodeState GCodeBuffer::GetState() const
{
return machineState->state;
diff --git a/src/GCodes/GCodeInput.cpp b/src/GCodes/GCodeInput.cpp
index 2e7383db79..155e71e035 100644
--- a/src/GCodes/GCodeInput.cpp
+++ b/src/GCodes/GCodeInput.cpp
@@ -21,15 +21,13 @@ bool GCodeInput::FillBuffer(GCodeBuffer *gb)
if (gb->IsWritingBinary())
{
// HTML uploads are handled by the GCodes class
- reprap.GetGCodes().WriteHTMLToFile(*gb, c);
+ gb->WriteBinaryToFile(c);
}
else if (gb->Put(c))
{
- // Check if we can finish a file upload
- if (gb->WritingFileDirectory() != nullptr)
+ if (gb->IsWritingFile())
{
- reprap.GetGCodes().WriteGCodeToFile(*gb);
- gb->SetFinished(true);
+ gb->WriteToFile();
}
// Code is complete, stop here
@@ -116,7 +114,7 @@ void NetworkGCodeInput::Put(MessageType mtype, char c)
return;
}
- state = (c == 'M') ? GCodeInputState::doingMCode : GCodeInputState::doingCode;
+ state = (c == 'M' || c == 'm') ? GCodeInputState::doingMCode : GCodeInputState::doingCode;
break;
case GCodeInputState::doingCode:
diff --git a/src/GCodes/GCodeQueue.cpp b/src/GCodes/GCodeQueue.cpp
index 207bfd1792..97240d36d2 100644
--- a/src/GCodes/GCodeQueue.cpp
+++ b/src/GCodes/GCodeQueue.cpp
@@ -32,118 +32,94 @@ GCodeQueue::GCodeQueue() : freeItems(nullptr), queuedItems(nullptr)
}
#endif
- switch (gb.GetCommandLetter())
+ // Don't queue anything if no moves are being performed
+ const uint32_t scheduledMoves = reprap.GetMove().GetScheduledMoves();
+ if (scheduledMoves != reprap.GetMove().GetCompletedMoves())
{
- case 'G':
+ switch (gb.GetCommandLetter())
{
- const int code = gb.GetCommandNumber();
- return code == 10 && gb.Seen('P'); // Set active/standby temperatures
- }
+ case 'G':
+ {
+ const int code = gb.GetCommandNumber();
+ return code == 10 && gb.Seen('P'); // Set active/standby temperatures
+ }
- case 'M':
- {
- switch (gb.GetCommandNumber())
+ case 'M':
{
- case 3: // spindle control
- case 4: // spindle control
- case 5: // spindle control
- case 42: // set IO pin
- case 106: // fan control
- case 107: // fan off
- case 104: // set temperatures and return immediately
- case 140: // set bed temperature and return immediately
- case 141: // set chamber temperature and return immediately
- case 144: // bed standby
- case 117: // display message
- case 280: // set servo
- case 300: // beep
- case 420: // set RGB colour
- return true;
-
- case 291:
+ switch (gb.GetCommandNumber())
{
- bool seen = false;
- int32_t sParam = 1;
- gb.TryGetIValue('S', sParam, seen);
- return sParam < 2; // queue non-blocking messages only
+ case 3: // spindle control
+ case 4: // spindle control
+ case 5: // spindle control
+ case 42: // set IO pin
+ case 106: // fan control
+ case 107: // fan off
+ case 104: // set temperatures and return immediately
+ case 140: // set bed temperature and return immediately
+ case 141: // set chamber temperature and return immediately
+ case 144: // bed standby
+ case 117: // display message
+ case 280: // set servo
+ case 300: // beep
+ case 420: // set RGB colour
+ return true;
+
+ case 291:
+ {
+ bool seen = false;
+ int32_t sParam = 1;
+ gb.TryGetIValue('S', sParam, seen);
+ return sParam < 2; // queue non-blocking messages only
+ }
+
+ default:
+ break;
}
-
- default:
- break;
}
- }
- break;
+ break;
- default:
- break;
+ default:
+ break;
+ }
}
return false;
}
-// If moves are scheduled and a command can be queued, try to queue the command in the passed GCodeBuffer.
-// If successful, return true to indicate it has been queued and the caller should not execute it.
-// If the queue is full, free up the oldest queued entry by copying its command to our own gcode buffer so that we have room to queue the original command.
+// Try to queue the command in the passed GCodeBuffer.
+// If successful, return true to indicate it has been queued.
+// If the queue is full or the command is too long to be queued, return false.
bool GCodeQueue::QueueCode(GCodeBuffer &gb)
{
- // Don't queue anything if no moves are being performed
- const uint32_t scheduledMoves = reprap.GetMove().GetScheduledMoves();
- bool queueCode = (scheduledMoves != reprap.GetMove().GetCompletedMoves());
-
- if (queueCode)
+ // Can we queue this code somewhere?
+ if (freeItems == nullptr || gb.CommandLength() > SHORT_GCODE_LENGTH - 1)
{
- char codeToRun[GCODE_LENGTH];
- size_t codeToRunLength;
-
- // Can we queue this code somewhere?
- if (freeItems == nullptr)
- {
- // No - we've run out of free items. Run the first outstanding code
- queueCode = false;
- codeToRunLength = strlen(queuedItems->code) + 1;
- SafeStrncpy(codeToRun, queuedItems->code, ARRAY_SIZE(codeToRun));
-
- // Release the first queued item so that it can be reused later
- QueuedCode * const item = queuedItems;
- queuedItems = item->next;
- item->next = nullptr;
- freeItems = item;
- }
-
- // Unlink a free element and assign gb's code to it
- QueuedCode * const code = freeItems;
- freeItems = code->next;
- code->AssignFrom(gb);
- code->executeAtMove = scheduledMoves;
- code->next = nullptr;
+ return false;
+ }
- // Append it to the list of queued codes
- if (queuedItems == nullptr)
- {
- queuedItems = code;
- }
- else
- {
- QueuedCode *last = queuedItems;
- while (last->Next() != nullptr)
- {
- last = last->Next();
- }
- last->next = code;
- }
+ // Unlink a free element and assign gb's code to it
+ QueuedCode * const code = freeItems;
+ freeItems = code->next;
+ code->AssignFrom(gb);
+ code->executeAtMove = reprap.GetMove().GetScheduledMoves();
+ code->next = nullptr;
- // Overwrite the passed gb's content if we could not store its original code
- if (!queueCode)
+ // Append it to the list of queued codes
+ if (queuedItems == nullptr)
+ {
+ queuedItems = code;
+ }
+ else
+ {
+ QueuedCode *last = queuedItems;
+ while (last->Next() != nullptr)
{
- if (reprap.Debug(moduleGcodes))
- {
- reprap.GetPlatform().Message(DebugMessage, "(swap) ");
- }
- gb.Put(codeToRun, codeToRunLength);
+ last = last->Next();
}
+ last->next = code;
}
- return queueCode;
+ return true;
}
bool GCodeQueue::FillBuffer(GCodeBuffer *gb)
@@ -237,7 +213,9 @@ void GCodeQueue::Diagnostics(MessageType mtype)
void QueuedCode::AssignFrom(GCodeBuffer &gb)
{
toolNumberAdjust = gb.GetToolNumberAdjust();
- SafeStrncpy(code, gb.Buffer(), ARRAY_SIZE(code));
+ const size_t length = min(gb.CommandLength(), ARRAY_SIZE(code) - 1);
+ memcpy(code, gb.CommandStart(), length);
+ code[length] = 0;
}
void QueuedCode::AssignTo(GCodeBuffer *gb)
diff --git a/src/GCodes/GCodeQueue.h b/src/GCodes/GCodeQueue.h
index 26f6e065ed..a366218fca 100644
--- a/src/GCodes/GCodeQueue.h
+++ b/src/GCodes/GCodeQueue.h
@@ -10,8 +10,6 @@
#include "RepRapFirmware.h"
#include "GCodeBuffer.h"
-const size_t maxQueuedCodes = 8; // How many codes can be queued?
-
class QueuedCode;
class GCodeQueue
@@ -44,7 +42,7 @@ class QueuedCode
private:
QueuedCode *next;
- char code[GCODE_LENGTH];
+ char code[SHORT_GCODE_LENGTH];
uint32_t executeAtMove;
int toolNumberAdjust;
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
index 0fd79e689b..8724e9e764 100644
--- a/src/GCodes/GCodes.cpp
+++ b/src/GCodes/GCodes.cpp
@@ -105,16 +105,14 @@ void GCodes::Init()
{
rawExtruderTotalByDrive[extruder] = 0.0;
}
- eofString = EOF_STRING;
- eofStringCounter = 0;
- eofStringLength = strlen(eofString);
+
runningConfigFile = false;
m501SeenInConfigFile = false;
doingToolChange = false;
active = true;
- fileSize = 0;
limitAxes = noMovesBeforeHoming = true;
SetAllAxesNotHomed();
+
for (size_t i = 0; i < NUM_FANS; ++i)
{
pausedFanSpeeds[i] = 0.0;
@@ -160,7 +158,6 @@ void GCodes::Reset()
nextGcodeSource = 0;
fileToPrint.Close();
- fileBeingWritten = nullptr;
speedFactor = SecondsToMinutes; // default is just to convert from mm/minute to mm/second
for (size_t i = 0; i < MaxExtruders; ++i)
@@ -1441,7 +1438,11 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, const StringRef& reply)
// Yes - fill up the GCodeBuffer and run the next code
if (fileInput->FillBuffer(&gb))
{
- gb.SetFinished(ActOnCode(gb, reply));
+ // We read some data, but we don't necessarily have a command available because we may be executing M28 within a file
+ if (gb.IsReady())
+ {
+ gb.SetFinished(ActOnCode(gb, reply));
+ }
}
break;
@@ -1958,6 +1959,17 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure)
&& reprap.WriteToolSettings(f) // set tool temperatures, tool mix ratios etc.
&& reprap.GetMove().WriteResumeSettings(f); // load grid, if we are using one
if (ok)
+ {
+ // Write a G92 command to say where the head is. This is useful if we can't Z-home the printer with a print on the bed and the Z steps/mm is high.
+ buf.copy("G92");
+ for (size_t axis = 0; axis < numVisibleAxes; ++axis)
+ {
+ buf.catf(" %c%.3f", axisLetters[axis], (double)pauseRestorePoint.moveCoords[axis]);
+ }
+ buf.cat('\n');
+ ok = f->Write(buf.c_str());
+ }
+ if (ok)
{
buf.printf("M98 P%s\n", RESUME_PROLOGUE_G); // call the prologue - must contain at least M116
ok = f->Write(buf.c_str())
@@ -2007,7 +2019,7 @@ void GCodes::SaveResumeInfo(bool wasPowerFailure)
{
if (axis != Z_AXIS)
{
- buf.catf(" %c%.2f", axisLetters[axis], (double)pauseRestorePoint.moveCoords[axis]);
+ buf.catf(" %c%.3f", axisLetters[axis], (double)pauseRestorePoint.moveCoords[axis]);
}
}
@@ -3058,6 +3070,7 @@ bool GCodes::SaveHeightMap(GCodeBuffer& gb, const StringRef& reply) const
void GCodes::ClearBedMapping()
{
reprap.GetMove().SetIdentityTransform();
+ reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes());
ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // update user coordinates to remove any height map offset there was at the current position
}
@@ -3103,114 +3116,6 @@ void GCodes::GetCurrentCoordinates(const StringRef& s) const
}
}
-bool GCodes::OpenFileToWrite(GCodeBuffer& gb, const char* directory, const char* fileName, const FilePosition size, const bool binaryWrite, const uint32_t fileCRC32)
-{
- fileBeingWritten = platform.OpenFile(directory, fileName, OpenMode::write);
- eofStringCounter = 0;
- fileSize = size;
- if (fileBeingWritten == nullptr)
- {
- platform.MessageF(ErrorMessage, "Failed to open GCode file \"%s\" for writing.\n", fileName);
- return false;
- }
- else
- {
- gb.SetCRC32(fileCRC32);
- gb.SetBinaryWriting(binaryWrite);
- gb.SetWritingFileDirectory(directory);
- return true;
- }
-}
-
-void GCodes::WriteHTMLToFile(GCodeBuffer& gb, char b)
-{
- if (fileBeingWritten == nullptr)
- {
- platform.Message(ErrorMessage, "Attempt to write to a null file.\n");
- return;
- }
-
- if ((b == eofString[eofStringCounter]) && (fileSize == 0))
- {
- eofStringCounter++;
- if (eofStringCounter >= eofStringLength)
- {
- FinishWrite(gb);
- }
- }
- else
- {
- if (eofStringCounter != 0)
- {
- for (uint8_t i = 0; i < eofStringCounter; i++)
- {
- fileBeingWritten->Write(eofString[i]);
- }
- eofStringCounter = 0;
- }
- fileBeingWritten->Write(b); // writing one character at a time isn't very efficient, but uploading HTML files via USB is rarely done these days
- if (fileSize > 0 && fileBeingWritten->Length() >= fileSize)
- {
- FinishWrite(gb);
- }
- }
-}
-
-void GCodes::FinishWrite(GCodeBuffer& gb)
-{
- const char* r;
- fileBeingWritten->Close();
- if ((gb.GetCRC32() != fileBeingWritten->GetCRC32()) && (gb.GetCRC32() != 0))
- {
- r = "Error: CRC32 checksum doesn't match";
- }
- else
- {
- r = (platform.Emulating() == Compatibility::marlin) ? "Done saving file." : "";
- }
- fileBeingWritten = nullptr;
- gb.SetBinaryWriting(false);
- gb.SetWritingFileDirectory(nullptr);
-
- HandleReply(gb, GCodeResult::ok, r);
-}
-
-void GCodes::WriteGCodeToFile(GCodeBuffer& gb)
-{
- if (fileBeingWritten == nullptr)
- {
- platform.Message(ErrorMessage, "Attempt to write to a null file.\n");
- return;
- }
-
- if (gb.GetCommandLetter() == 'M')
- {
- if (gb.GetCommandNumber() == 29) // end of file?
- {
- fileBeingWritten->Close();
- fileBeingWritten = nullptr;
- gb.SetWritingFileDirectory(nullptr);
- const char* r = (platform.Emulating() == Compatibility::marlin) ? "Done saving file." : "";
- HandleReply(gb, GCodeResult::ok, r);
- return;
- }
- }
- else if (gb.GetCommandLetter() == 'G' && gb.GetCommandNumber() == 998) // resend request?
- {
- if (gb.Seen('P'))
- {
- String scratchString;
- scratchString.printf("%" PRIi32 "\n", gb.GetIValue());
- HandleReply(gb, GCodeResult::ok, scratchString.c_str());
- return;
- }
- }
-
- fileBeingWritten->Write(gb.Buffer());
- fileBeingWritten->Write('\n');
- HandleReply(gb, GCodeResult::ok, "");
-}
-
// Set up a file to print, but don't print it yet.
// If successful return true, else write an error message to reply and return false
bool GCodes::QueueFileToPrint(const char* fileName, const StringRef& reply)
@@ -4911,7 +4816,7 @@ const char* GCodes::GetMachineModeString() const
}
// Respond to a heater fault. The heater has already been turned off and its status set to 'fault' when this is called from the Heat module.
-// The Heat module will generate an appropriate error message, so on need to do that here.
+// The Heat module will generate an appropriate error message, so no need to do that here.
void GCodes::HandleHeaterFault(int heater)
{
if (heaterFaultState == HeaterFaultState::noFault && fileGCode->OriginalMachineState().fileState.IsLive())
@@ -4953,11 +4858,12 @@ void GCodes::CheckHeaterFault()
reprap.GetHeat().SwitchOffAll(true);
platform.MessageF(ErrorMessage, "Shutting down due to un-cleared heater fault after %lu seconds\n", heaterFaultTimeout/1000);
heaterFaultState = HeaterFaultState::stopping;
+ heaterFaultTime = millis();
}
break;
case HeaterFaultState::stopping:
- if (millis() - heaterFaultTime >= 2000) // wait 2 seconds for the message to be picked up by DWC and PanelDue
+ if (millis() - heaterFaultTime >= 1000) // wait 1 second for the message to be picked up by DWC and PanelDue
{
platform.AtxPowerOff(false);
heaterFaultState = HeaterFaultState::stopped;
diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h
index 0c0de8cea3..74f86ba94f 100644
--- a/src/GCodes/GCodes.h
+++ b/src/GCodes/GCodes.h
@@ -171,9 +171,6 @@ class GCodes
NetworkGCodeInput *GetHTTPInput() const { return httpInput; }
NetworkGCodeInput *GetTelnetInput() const { return telnetInput; }
- void WriteGCodeToFile(GCodeBuffer& gb); // Write this GCode into a file
- void WriteHTMLToFile(GCodeBuffer& gb, char b); // Save an HTML file (usually to upload a new web interface)
-
bool IsFlashing() const { return isFlashing; } // Is a new firmware binary going to be flashed?
bool IsPaused() const;
@@ -222,6 +219,7 @@ class GCodes
#endif
void SetMappedFanSpeed(float f); // Set the mapped fan speed
+ void HandleReply(GCodeBuffer& gb, GCodeResult rslt, const char *reply); // Handle G-Code replies
private:
GCodes(const GCodes&); // private copy constructor to prevent copying
@@ -261,7 +259,6 @@ class GCodes
bool HandleMcode(GCodeBuffer& gb, const StringRef& reply); // Do an M code
bool HandleTcode(GCodeBuffer& gb, const StringRef& reply); // Do a T code
bool HandleResult(GCodeBuffer& gb, GCodeResult rslt, const StringRef& reply);
- void HandleReply(GCodeBuffer& gb, GCodeResult rslt, const char *reply); // Handle G-Code replies
void HandleReply(GCodeBuffer& gb, bool error, OutputBuffer *reply);
const char* DoStraightMove(GCodeBuffer& gb, bool isCoordinated) __attribute__((hot)); // Execute a straight move returning any error message
@@ -290,9 +287,7 @@ class GCodes
bool Push(GCodeBuffer& gb); // Push feedrate etc on the stack
void Pop(GCodeBuffer& gb); // Pop feedrate etc
void DisableDrives(); // Turn the motors off
- bool OpenFileToWrite(GCodeBuffer& gb, const char* directory, const char* fileName, const FilePosition size, const bool binaryWrite, const uint32_t fileCRC32);
// Start saving GCodes in a file
- void FinishWrite(GCodeBuffer& gb); // Finish writing to the file and respond
bool SendConfigToLine(); // Deal with M503
GCodeResult OffsetAxes(GCodeBuffer& gb, const StringRef& reply); // Set/report offsets
@@ -478,13 +473,6 @@ class GCodes
FileData fileToPrint; // The next file to print
FilePosition fileOffsetToPrint; // The offset to print from
- FileStore* fileBeingWritten; // A file to write G Codes (or sometimes HTML) to
- FilePosition fileSize; // Size of the file being written
-
- const char* eofString; // What's at the end of an HTML file?
- uint8_t eofStringCounter; // Check the...
- uint8_t eofStringLength; // ... EoF string as we read.
-
char axisLetters[MaxAxes + 1]; // The names of the axes, with a null terminator
bool limitAxes; // Don't think outside the box
bool noMovesBeforeHoming; // Don't allow movement prior to homing the associates axes
diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp
index a87c8d7a8f..2e0f04fe31 100644
--- a/src/GCodes/GCodes2.cpp
+++ b/src/GCodes/GCodes2.cpp
@@ -61,6 +61,8 @@ bool GCodes::ActOnCode(GCodeBuffer& gb, const StringRef& reply)
HandleReply(gb, GCodeResult::ok, "");
return true;
}
+
+ return false; // we should queue this code but we can't, so wait until we can either execute it or queue it
}
switch (gb.GetCommandLetter())
@@ -230,9 +232,13 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply)
result = LoadHeightMap(gb, reply);
break;
- default: // clear height map
+ case 2: // clear height map
ClearBedMapping();
break;
+
+ default:
+ result = GCodeResult::badOrMissingParameter;
+ break;
}
}
break;
@@ -850,7 +856,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply)
String filename;
if (gb.GetUnprecedentedString(filename.GetRef()))
{
- const bool ok = OpenFileToWrite(gb, platform.GetGCodeDir(), filename.c_str(), 0, false, 0);
+ const bool ok = gb.OpenFileToWrite(platform.GetGCodeDir(), filename.c_str(), 0, false, 0);
if (ok)
{
reply.printf("Writing to file: %s", filename.c_str());
@@ -2960,7 +2966,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply)
}
const FilePosition size = (gb.Seen('S') ? (FilePosition)gb.GetIValue() : 0);
const uint32_t crc32 = (gb.Seen('C') ? gb.GetUIValue() : 0);
- const bool ok = OpenFileToWrite(gb, folder, filename.c_str(), size, true, crc32);
+ const bool ok = gb.OpenFileToWrite(folder, filename.c_str(), size, true, crc32);
if (ok)
{
reply.printf("Writing to file: %s", filename.c_str());
@@ -2973,7 +2979,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply)
}
break;
- case 561: // Set identity transform (also clears bed probe grid)
+ case 561: // Set identity transform and disable height map
+ if (!LockMovementAndWaitForStandstill(gb))
+ {
+ return false;
+ }
ClearBedMapping();
break;
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp
index d56fbb8380..ffd44acc20 100644
--- a/src/Heating/Heat.cpp
+++ b/src/Heating/Heat.cpp
@@ -185,6 +185,8 @@ void Heat::Task()
heaterBeingTuned = -1;
}
+ reprap.KickHeatTaskWatchdog();
+
// Delay until it is time again
vTaskDelayUntil(&lastWakeTime, platform.HeatSampleInterval());
}
diff --git a/src/Libraries/Fatfs/diskio.cpp b/src/Libraries/Fatfs/diskio.cpp
index 6ee696eb14..7065677891 100644
--- a/src/Libraries/Fatfs/diskio.cpp
+++ b/src/Libraries/Fatfs/diskio.cpp
@@ -52,6 +52,15 @@
#include
+static unsigned int highestSdRetriesDone = 0;
+
+unsigned int DiskioGetAndClearMaxRetryCount()
+{
+ const unsigned int ret = highestSdRetriesDone;
+ highestSdRetriesDone = 0;
+ return ret;
+}
+
//void debugPrintf(const char*, ...);
//#if (SAM3S || SAM3U || SAM3N || SAM3XA_SERIES || SAM4S)
@@ -161,22 +170,35 @@ DRESULT disk_read(BYTE drv, BYTE *buff, DWORD sector, BYTE count)
#if ACCESS_MEM_TO_RAM
MutexLocker lock((drv >= SD_MMC_HSMCI_MEM_CNT) ? Tasks::GetSpiMutex() : nullptr);
- uint8_t uc_sector_size = mem_sector_size(drv);
- uint32_t ul_last_sector_num;
-
- if (uc_sector_size == 0) {
+ const uint8_t uc_sector_size = mem_sector_size(drv);
+ if (uc_sector_size == 0)
+ {
return RES_ERROR;
}
/* Check valid address */
+ uint32_t ul_last_sector_num;
mem_read_capacity(drv, &ul_last_sector_num);
- if ((sector + count * uc_sector_size) > (ul_last_sector_num + 1) * uc_sector_size) {
+ if ((sector + count * uc_sector_size) > (ul_last_sector_num + 1) * uc_sector_size)
+ {
return RES_PARERR;
}
/* Read the data */
- if (memory_2_ram(drv, sector, buff, count) != CTRL_GOOD) {
- return RES_ERROR;
+ unsigned int retryNumber = 0;
+ while (memory_2_ram(drv, sector, buff, count) != CTRL_GOOD)
+ {
+ ++retryNumber;
+ if (retryNumber == MaxSdCardTries)
+ {
+ return RES_ERROR;
+ }
+ delay(SdCardRetryDelay);
+ }
+
+ if (retryNumber > highestSdRetriesDone)
+ {
+ highestSdRetriesDone = retryNumber;
}
return RES_OK;
@@ -208,23 +230,36 @@ DRESULT disk_write(BYTE drv, BYTE const *buff, DWORD sector, BYTE count)
#if ACCESS_MEM_TO_RAM
MutexLocker lock((drv >= SD_MMC_HSMCI_MEM_CNT) ? Tasks::GetSpiMutex() : nullptr);
- uint8_t uc_sector_size = mem_sector_size(drv);
- uint32_t ul_last_sector_num;
+ const uint8_t uc_sector_size = mem_sector_size(drv);
- if (uc_sector_size == 0) {
+ if (uc_sector_size == 0)
+ {
return RES_ERROR;
}
/* Check valid address */
+ uint32_t ul_last_sector_num;
mem_read_capacity(drv, &ul_last_sector_num);
- if ((sector + count * uc_sector_size) >
- (ul_last_sector_num + 1) * uc_sector_size) {
+ if ((sector + count * uc_sector_size) > (ul_last_sector_num + 1) * uc_sector_size)
+ {
return RES_PARERR;
}
/* Write the data */
- if (ram_2_memory(drv, sector, buff, count) != CTRL_GOOD) {
- return RES_ERROR;
+ unsigned int retryNumber = 0;
+ while (ram_2_memory(drv, sector, buff, count) != CTRL_GOOD)
+ {
+ ++retryNumber;
+ if (retryNumber == MaxSdCardTries)
+ {
+ return RES_ERROR;
+ }
+ delay(SdCardRetryDelay);
+ }
+
+ if (retryNumber > highestSdRetriesDone)
+ {
+ highestSdRetriesDone = retryNumber;
}
return RES_OK;
diff --git a/src/Libraries/Fatfs/diskio.h b/src/Libraries/Fatfs/diskio.h
index 40817bc588..43e5966c58 100644
--- a/src/Libraries/Fatfs/diskio.h
+++ b/src/Libraries/Fatfs/diskio.h
@@ -5,6 +5,7 @@
#ifndef _DISKIO
#ifdef __cplusplus
+unsigned int DiskioGetAndClearMaxRetryCount();
extern "C" {
#endif
diff --git a/src/Libraries/Fatfs/ff.h b/src/Libraries/Fatfs/ff.h
index 7f0d91ab48..9e89aa4ffb 100644
--- a/src/Libraries/Fatfs/ff.h
+++ b/src/Libraries/Fatfs/ff.h
@@ -193,7 +193,7 @@ typedef enum {
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
- FR_LOCKED, /* (16) The operation is rejected according to the file shareing policy */
+ FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_SHARE */
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp
index dfa7ae757b..398a41e0cb 100644
--- a/src/Movement/DDA.cpp
+++ b/src/Movement/DDA.cpp
@@ -248,8 +248,17 @@ void DDA::Init()
// Either way, return the amount of extrusion we didn't do in the extruder coordinates of nextMove
bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping)
{
- // 1. Compute the new endpoints and the movement vector
+ // 0. If there are more total axes than visible axes, then we must ignore any movement data in nextMove for the invisible axes.
+ // The call to CartesianToMotorSteps may adjust the invisible axis endpoints for architectures such as CoreXYU, so set them up here.
+ const size_t numTotalAxes = reprap.GetGCodes().GetTotalAxes();
+ const size_t numVisibleAxes = reprap.GetGCodes().GetVisibleAxes();
const int32_t * const positionNow = prev->DriveCoordinates();
+ for (size_t axis = numVisibleAxes; axis < numTotalAxes; ++axis)
+ {
+ endPoint[axis] = positionNow[axis];
+ }
+
+ // 1. Compute the new endpoints and the movement vector
const Move& move = reprap.GetMove();
if (doMotorMapping)
{
@@ -271,20 +280,19 @@ bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping)
bool realMove = false;
float accelerations[DRIVES];
const float * const normalAccelerations = reprap.GetPlatform().Accelerations();
- const size_t numAxes = reprap.GetGCodes().GetTotalAxes();
const Kinematics& k = move.GetKinematics();
for (size_t drive = 0; drive < DRIVES; drive++)
{
accelerations[drive] = normalAccelerations[drive];
- if (drive >= numAxes || !doMotorMapping)
+ if (drive >= numTotalAxes || (!doMotorMapping && drive < numVisibleAxes))
{
endPoint[drive] = Move::MotorEndPointToMachine(drive, nextMove.coords[drive]);
}
endCoordinates[drive] = nextMove.coords[drive];
int32_t delta;
- if (drive < numAxes)
+ if (drive < numTotalAxes)
{
delta = endPoint[drive] - positionNow[drive];
if (k.IsContinuousRotationAxis(drive) && nextMove.moveType != 1 && nextMove.moveType != 2)
@@ -305,7 +313,7 @@ bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping)
delta = endPoint[drive];
}
- if (drive < numAxes && doMotorMapping)
+ if (drive < numTotalAxes && doMotorMapping)
{
const float positionDelta = nextMove.coords[drive] - prev->GetEndCoordinate(drive, false);
directionVector[drive] = positionDelta;
@@ -317,7 +325,7 @@ bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping)
else
{
directionVector[drive] = (float)delta/reprap.GetPlatform().DriveStepsPerUnit(drive);
- if (drive >= numAxes && nextMove.coords[drive] > 0.0)
+ if (drive >= numTotalAxes && nextMove.coords[drive] > 0.0)
{
extruding = true;
}
@@ -331,14 +339,14 @@ bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping)
pdm->totalSteps = labs(delta); // for now this is the number of net steps, but gets adjusted later if there is a reverse in direction
pdm->direction = (delta >= 0); // for now this is the direction of net movement, but gets adjusted later if it is a delta movement
- if (drive >= numAxes)
+ if (drive >= numTotalAxes)
{
// It's an extruder movement
nextMove.coords[drive] -= directionVector[drive];
// subtract the amount of extrusion we actually did to leave the residue outstanding
if (xyMoving && nextMove.usePressureAdvance)
{
- const float compensationTime = reprap.GetPlatform().GetPressureAdvance(drive - numAxes);
+ const float compensationTime = reprap.GetPlatform().GetPressureAdvance(drive - numTotalAxes);
if (compensationTime > 0.0)
{
// Compensation causes instant velocity changes equal to acceleration * k, so we may need to limit the acceleration
@@ -359,7 +367,7 @@ bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping)
// Update the end position in the previous move, so that on the next move we don't think there is XY movement when the user didn't ask for any
if (doMotorMapping)
{
- for (size_t drive = 0; drive < numAxes; ++drive)
+ for (size_t drive = 0; drive < numTotalAxes; ++drive)
{
prev->endCoordinates[drive] = nextMove.coords[drive];
}
@@ -423,7 +431,7 @@ bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping)
else if (axesMoving)
{
// Some axes are moving, but not axes that X or Y are mapped to. Normalise the movement to the vector sum of the axes that are moving.
- totalDistance = Normalise(directionVector, DRIVES, numAxes);
+ totalDistance = Normalise(directionVector, DRIVES, numTotalAxes);
}
else
{
diff --git a/src/Movement/Kinematics/HangprinterKinematics.cpp b/src/Movement/Kinematics/HangprinterKinematics.cpp
index a77d377c04..f7c066a0a0 100644
--- a/src/Movement/Kinematics/HangprinterKinematics.cpp
+++ b/src/Movement/Kinematics/HangprinterKinematics.cpp
@@ -12,19 +12,14 @@
#include "Movement/Move.h"
//#include "Movement/BedProbing/RandomProbePointSet.h"
-// Default anchor coordinates, copied from https://github.com/tobbelobb/hangprinter/blob/fabf19bf4653d1d1daf72d53217ba5962c9aca6e/firmware/HangprinterMarlin/Configuration.h
-constexpr float DefaultAnchorA[3] = { 0.0, -2163.0, -75.5};
-constexpr float DefaultAnchorB[3] = {-1841.0, 741.0, -75.5};
-constexpr float DefaultAnchorC[3] = { 1639.0, 1404.0, -75.5};
-constexpr float DefaultAnchorDz = 3250.5;
+// Default anchor coordinates
+// These are only placeholders. Each machine must have these values calibrated in order to work correctly.
+constexpr float DefaultAnchorA[3] = { 0.0, -2000.0, -100.0};
+constexpr float DefaultAnchorB[3] = { 2000.0, 1000.0, -100.0};
+constexpr float DefaultAnchorC[3] = {-2000.0, 1000.0, -100.0};
+constexpr float DefaultAnchorDz = 3000.0;
constexpr float DefaultPrintRadius = 1500.0;
-constexpr size_t HANGPRINTER_AXES = 4;
-constexpr size_t A_AXIS = 0;
-constexpr size_t B_AXIS = 1;
-constexpr size_t C_AXIS = 2;
-constexpr size_t D_AXIS = 3;
-
// Constructor
HangprinterKinematics::HangprinterKinematics()
: Kinematics(KinematicsType::scara, DefaultSegmentsPerSecond, DefaultMinSegmentSize, true)
@@ -39,7 +34,7 @@ void HangprinterKinematics::Init()
ARRAY_INIT(anchorA, DefaultAnchorA);
ARRAY_INIT(anchorB, DefaultAnchorB);
ARRAY_INIT(anchorC, DefaultAnchorC);
- doneAutoCalibration = false;
+ doneAutoCalibration = false;
Recalc();
}
@@ -140,7 +135,7 @@ bool HangprinterKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, const
}
// Calculate the square of the line length from a spool from a Cartesian coordinate
-inline float HangprinterKinematics::LineLengthASquared(const float machinePos[3], const float anchor[3]) const
+inline float HangprinterKinematics::LineLengthSquared(const float machinePos[3], const float anchor[3]) const
{
return fsquare(anchor[Z_AXIS] - machinePos[Z_AXIS]) + fsquare(anchor[Y_AXIS] - machinePos[Y_AXIS]) + fsquare(anchor[X_AXIS] - machinePos[X_AXIS]);
}
@@ -148,15 +143,13 @@ inline float HangprinterKinematics::LineLengthASquared(const float machinePos[3]
// Convert Cartesian coordinates to motor coordinates, returning true if successful
bool HangprinterKinematics::CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[], bool isCoordinated) const
{
- // Geometry of hangprinter makes fsquare(anchorABC[Z_AXIS] - machinePos[Z_AXIS]) the smallest term in the sum.
- // Starting sum with smallest number gives smallest roundoff error.
- const float aSquared = LineLengthASquared(machinePos, anchorA);
- const float bSquared = LineLengthASquared(machinePos, anchorB);
- const float cSquared = LineLengthASquared(machinePos, anchorC);
+ const float aSquared = LineLengthSquared(machinePos, anchorA);
+ const float bSquared = LineLengthSquared(machinePos, anchorB);
+ const float cSquared = LineLengthSquared(machinePos, anchorC);
const float dSquared = fsquare(machinePos[X_AXIS])
+ fsquare(machinePos[Y_AXIS])
+ fsquare(anchorDz - machinePos[Z_AXIS]);
- if (aSquared < 0.0 && bSquared < 0.0 && cSquared < 0.0 && dSquared < 0.0)
+ if (aSquared > 0.0 && bSquared > 0.0 && cSquared > 0.0 && dSquared > 0.0)
{
motorPos[A_AXIS] = lrintf(sqrtf(aSquared) * stepsPerMm[A_AXIS]);
motorPos[B_AXIS] = lrintf(sqrtf(bSquared) * stepsPerMm[B_AXIS]);
@@ -372,9 +365,9 @@ bool HangprinterKinematics::DoAutoCalibration(size_t numFactors, const RandomPro
const floatc_t zp = reprap.GetMove().GetProbeCoordinates(i, machinePos[X_AXIS], machinePos[Y_AXIS], probePoints.PointWasCorrected(i));
machinePos[Z_AXIS] = 0.0;
- probeMotorPositions(i, A_AXIS) = sqrtf(LineLengthASquared(machinePos, anchorA));
- probeMotorPositions(i, B_AXIS) = sqrtf(LineLengthASquared(machinePos, anchorB));
- probeMotorPositions(i, C_AXIS) = sqrtf(LineLengthASquared(machinePos, anchorC));
+ probeMotorPositions(i, A_AXIS) = sqrtf(LineLengthSquared(machinePos, anchorA));
+ probeMotorPositions(i, B_AXIS) = sqrtf(LineLengthSquared(machinePos, anchorB));
+ probeMotorPositions(i, C_AXIS) = sqrtf(LineLengthSquared(machinePos, anchorC));
initialSumOfSquares += fcsquare(zp);
}
diff --git a/src/Movement/Kinematics/HangprinterKinematics.h b/src/Movement/Kinematics/HangprinterKinematics.h
index 60a342d996..96ceb9efd0 100644
--- a/src/Movement/Kinematics/HangprinterKinematics.h
+++ b/src/Movement/Kinematics/HangprinterKinematics.h
@@ -43,9 +43,16 @@ class HangprinterKinematics : public Kinematics
static constexpr float DefaultSegmentsPerSecond = 100.0;
static constexpr float DefaultMinSegmentSize = 0.2;
+ // Basic facts about movement system
+ static constexpr size_t HANGPRINTER_AXES = 4;
+ static constexpr size_t A_AXIS = 0;
+ static constexpr size_t B_AXIS = 1;
+ static constexpr size_t C_AXIS = 2;
+ static constexpr size_t D_AXIS = 3;
+
void Init();
void Recalc();
- float LineLengthASquared(const float machinePos[3], const float anchor[3]) const; // Calculate the square of the line length from a spool from a Cartesian coordinate
+ float LineLengthSquared(const float machinePos[3], const float anchor[3]) const; // Calculate the square of the line length from a spool from a Cartesian coordinate
void InverseTransform(float La, float Lb, float Lc, float machinePos[3]) const;
floatc_t ComputeDerivative(unsigned int deriv, float La, float Lb, float Lc) const; // Compute the derivative of height with respect to a parameter at a set of motor endpoints
diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp
index 1765ac714f..8b7b3ba9a5 100644
--- a/src/Movement/Move.cpp
+++ b/src/Movement/Move.cpp
@@ -104,6 +104,7 @@ void Move::Init()
idleTimeout = DefaultIdleTimeout;
moveState = MoveState::idle;
+ lastStateChangeTime = millis();
idleCount = 0;
simulationMode = 0;
@@ -589,7 +590,7 @@ void Move::Diagnostics(MessageType mtype)
{
Platform& p = reprap.GetPlatform();
p.Message(mtype, "=== Move ===\n");
- p.MessageF(mtype, "Hiccups: %" PRIu32 ", StepErrors: %u, LaErrors: %u, FreeDm: %d, MinFreeDm %d, MaxWait: %" PRIu32 "ms, Underruns: %u, %u\n",
+ p.MessageF(mtype, "Hiccups: %" PRIu32 ", StepErrors: %u, LaErrors: %u, FreeDm: %d, MinFreeDm: %d, MaxWait: %" PRIu32 "ms, Underruns: %u, %u\n",
DDA::numHiccups, stepErrors, numLookaheadErrors, DriveMovement::NumFree(), DriveMovement::MinFree(), longestGcodeWaitInterval, numLookaheadUnderruns, numPrepareUnderruns);
DDA::numHiccups = 0;
numLookaheadUnderruns = numPrepareUnderruns = numLookaheadErrors = 0;
diff --git a/src/Platform.cpp b/src/Platform.cpp
index 2e74416ec3..87321ac70f 100644
--- a/src/Platform.cpp
+++ b/src/Platform.cpp
@@ -438,7 +438,7 @@ void Platform::Init()
DuetExpansion::AdditionalOutputInit();
#elif defined(DUET_M)
- numSmartDrivers = 5; // TODO for now we assume that additional drivers are dumb
+ numSmartDrivers = 7; // TODO for now we assume that additional drivers are smart
#endif
#if HAS_SMART_DRIVERS
@@ -653,7 +653,7 @@ void Platform::InitZProbe()
int Platform::GetZProbeReading() const
{
int zProbeVal = 0; // initialised to avoid spurious compiler warning
- if (zProbeType == ZProbeType::unfilteredDigital || (zProbeOnFilter.IsValid() && zProbeOffFilter.IsValid()))
+ if (zProbeType == ZProbeType::unfilteredDigital || zProbeType == ZProbeType::blTouch || (zProbeOnFilter.IsValid() && zProbeOffFilter.IsValid()))
{
switch (zProbeType)
{
@@ -663,7 +663,6 @@ int Platform::GetZProbeReading() const
case ZProbeType::digital: // Switch connected to Z probe input
case ZProbeType::e1Switch: // Switch connected to E1 endstop input
case ZProbeType::zSwitch: // Switch connected to Z endstop input
- case ZProbeType::blTouch:
zProbeVal = (int) ((zProbeOnFilter.GetSum() + zProbeOffFilter.GetSum()) / (8 * Z_PROBE_AVERAGE_READINGS));
break;
@@ -674,6 +673,7 @@ int Platform::GetZProbeReading() const
break;
case ZProbeType::unfilteredDigital: // Switch connected to Z probe input, no filtering
+ case ZProbeType::blTouch: // blTouch is now unfiltered too
zProbeVal = GetRawZProbeReading()/4;
break;
@@ -1330,13 +1330,15 @@ void Platform::Spin()
{
if (currentVin < driverPowerOffAdcReading)
{
- ++numUnderVoltageEvents;
driversPowered = false;
+ ++numUnderVoltageEvents;
+ lastUnderVoltageValue = currentVin; // save this because the voltage may have changed by the time we report it
}
else if (currentVin > driverOverVoltageAdcReading)
{
driversPowered = false;
++numOverVoltageEvents;
+ lastOverVoltageValue = currentVin; // save this because the voltage may have changed by the time we report it
}
else
{
@@ -1510,13 +1512,13 @@ void Platform::Spin()
#if HAS_VOLTAGE_MONITOR
if (numOverVoltageEvents != previousOverVoltageEvents)
{
- MessageF(WarningMessage, "VIN over-voltage event (%.1fV)", (double)GetCurrentPowerVoltage());
+ MessageF(WarningMessage, "VIN over-voltage event (%.1fV)", (double)AdcReadingToPowerVoltage(lastOverVoltageValue));
previousOverVoltageEvents = numOverVoltageEvents;
reported = true;
}
if (numUnderVoltageEvents != previousUnderVoltageEvents)
{
- MessageF(WarningMessage, "VIN under-voltage event (%.1fV)", (double)GetCurrentPowerVoltage());
+ MessageF(WarningMessage, "VIN under-voltage event (%.1fV)", (double)AdcReadingToPowerVoltage(lastUnderVoltageValue));
previousUnderVoltageEvents = numUnderVoltageEvents;
reported = true;
}
@@ -1792,6 +1794,10 @@ void Platform::SoftwareReset(uint16_t reason, const uint32_t *stk)
srdBuf[slot].cfsr = SCB->CFSR;
srdBuf[slot].icsr = SCB->ICSR;
srdBuf[slot].bfar = SCB->BFAR;
+#ifdef RTOS
+ srdBuf[slot].taskName = *reinterpret_cast(pcTaskGetName(nullptr));
+#endif
+
if (stk != nullptr)
{
srdBuf[slot].sp = reinterpret_cast(stk);
@@ -2093,7 +2099,8 @@ void Platform::Diagnostics(MessageType mtype)
: (reason == (uint32_t)SoftwareResetReason::otherFault) ? "Other fault"
: (reason == (uint32_t)SoftwareResetReason::stackOverflow) ? "Stack overflow"
: (reason == (uint32_t)SoftwareResetReason::assertCalled) ? "Assertion failed"
- : "Unknown";
+ : (reason == (uint32_t)SoftwareResetReason::heaterWatchdog) ? "Heat task stuck"
+ : "Unknown";
String scratchString;
if (srdBuf[slot].when != 0)
{
@@ -2112,8 +2119,15 @@ void Platform::Diagnostics(MessageType mtype)
(srdBuf[slot].resetReason & (uint32_t)SoftwareResetReason::deliberate) ? "deliberate " : "",
reasonText, moduleName[srdBuf[slot].resetReason & 0x0F], srdBuf[slot].neverUsedRam, slot);
// Our format buffer is only 256 characters long, so the next 2 lines must be written separately
- MessageF(mtype, "Software reset code 0x%04x HFSR 0x%08" PRIx32 ", CFSR 0x%08" PRIx32 ", ICSR 0x%08" PRIx32 ", BFAR 0x%08" PRIx32 ", SP 0x%08" PRIx32 "\n",
- srdBuf[slot].resetReason, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].icsr, srdBuf[slot].bfar, srdBuf[slot].sp);
+ MessageF(mtype,
+#ifdef RTOS
+ "Software reset code 0x%04x HFSR 0x%08" PRIx32 " CFSR 0x%08" PRIx32 " ICSR 0x%08" PRIx32 " BFAR 0x%08" PRIx32 " SP 0x%08" PRIx32 " Task 0x%08" PRIx32 "\n",
+ srdBuf[slot].resetReason, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].icsr, srdBuf[slot].bfar, srdBuf[slot].sp, srdBuf[slot].taskName
+#else
+ "Software reset code 0x%04x HFSR 0x%08" PRIx32 " CFSR 0x%08" PRIx32 " ICSR 0x%08" PRIx32 " BFAR 0x%08" PRIx32 " SP 0x%08" PRIx32 "\n",
+ srdBuf[slot].resetReason, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].icsr, srdBuf[slot].bfar, srdBuf[slot].sp
+#endif
+ );
if (srdBuf[slot].sp != 0xFFFFFFFF)
{
// We saved a stack dump, so print it
@@ -2145,7 +2159,7 @@ void Platform::Diagnostics(MessageType mtype)
#endif
// Show the longest SD card write time
- MessageF(mtype, "SD card longest block write time: %.1fms\n", (double)FileStore::GetAndClearLongestWriteTime());
+ MessageF(mtype, "SD card longest block write time: %.1fms, max retries %u\n", (double)FileStore::GetAndClearLongestWriteTime(), FileStore::GetAndClearMaxRetryCount());
#if HAS_CPU_TEMP_SENSOR
// Show the MCU temperatures
@@ -2807,12 +2821,15 @@ void Platform::DisableDrive(size_t drive)
}
}
-// Disable all drives
+// Disable all drives. Called from emergency stop and the tick ISR.
void Platform::DisableAllDrives()
{
for (size_t drive = 0; drive < DRIVES; drive++)
{
- SetDriverCurrent(drive, 0.0, false);
+ if (!inInterrupt()) // on the Duet 06/085 we need interrupts running to send the I2C commands to set motor currents
+ {
+ SetDriverCurrent(drive, 0.0, false);
+ }
DisableDriver(drive);
}
}
@@ -3663,12 +3680,11 @@ void Platform::AtxPowerOff(bool defer)
deferredPowerDown = defer;
if (!defer)
{
- deferredPowerDown = false;
if (logger != nullptr)
{
logger->LogMessage(realTime, "Power off commanded");
logger->Flush(true);
- // We don't call logger->Stop() here because we don't now whether turning off the power will work
+ // We don't call logger->Stop() here because we don't know whether turning off the power will work
}
IoPort::WriteDigital(ATX_POWER_PIN, false);
}
diff --git a/src/Platform.h b/src/Platform.h
index 9d04084905..b58e229571 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -180,12 +180,13 @@ enum class SoftwareResetReason : uint16_t
erase = 0x10, // special M999 command to erase firmware and reset
NMI = 0x20,
hardFault = 0x30, // most exceptions get escalated to a hard fault
- stuckInSpin = 0x40, // we got stuck in a Spin() function for too long
+ stuckInSpin = 0x40, // we got stuck in a Spin() function in the Main task for too long
wdtFault = 0x50, // secondary watchdog
usageFault = 0x60,
otherFault = 0x70,
- stackOverflow = 0x80,
- assertCalled = 0x90,
+ stackOverflow = 0x80, // FreeRTOS detected stack overflow
+ assertCalled = 0x90, // FreeRTOS assertion failure
+ heaterWatchdog = 0xA0, // the Heat task didn't kick the watchdog often enough
// Bits that are or'ed in
inAuxOutput = 0x0800, // this bit is or'ed in if we were in aux output at the time
@@ -294,7 +295,7 @@ struct AxisDriversConfig
// The main class that defines the RepRap machine for the benefit of the other classes
class Platform
-{
+{
public:
// Enumeration to describe the status of a drive
enum class DriverStatus : uint8_t { disabled, idle, enabled };
@@ -660,7 +661,12 @@ class Platform
uint32_t bfar; // bus fault address register
uint32_t sp; // stack pointer
uint32_t when; // value of the RTC when the software reset occurred
+#ifdef RTOS
+ uint32_t taskName; // first 4 bytes of the task name
+ uint32_t stack[23]; // stack when the exception occurred, with the program counter at the bottom
+#else
uint32_t stack[24]; // stack when the exception occurred, with the program counter at the bottom
+#endif
bool isVacant() const // return true if this struct can be written without erasing it first
{
@@ -885,6 +891,7 @@ class Platform
#if HAS_VOLTAGE_MONITOR
AnalogChannelNumber vInMonitorAdcChannel;
volatile uint16_t currentVin, highestVin, lowestVin;
+ uint16_t lastUnderVoltageValue, lastOverVoltageValue;
uint16_t autoPauseReading, autoResumeReading;
uint32_t numUnderVoltageEvents, previousUnderVoltageEvents;
volatile uint32_t numOverVoltageEvents, previousOverVoltageEvents;
diff --git a/src/RepRap.cpp b/src/RepRap.cpp
index e83bd6e781..0315f061f3 100644
--- a/src/RepRap.cpp
+++ b/src/RepRap.cpp
@@ -121,10 +121,14 @@ extern "C" void hsmciIdle(uint32_t stBits, uint32_t dmaBits)
// Do nothing more in the constructor; put what you want in RepRap:Init()
RepRap::RepRap() : toolList(nullptr), currentTool(nullptr), lastWarningMillis(0), activeExtruders(0),
- activeToolHeaters(0), ticksInSpinState(0), spinningModule(noModule), debug(0), stopped(false),
+ activeToolHeaters(0), ticksInSpinState(0),
+#ifdef RTOS
+ heatTaskIdleTicks(0),
+#endif
+ spinningModule(noModule), debug(0), stopped(false),
active(false), resetting(false), processingConfig(true), beepFrequency(0), beepDuration(0),
displayMessageBox(false), boxSeq(0),
- diagnosticsDestination(MessageType::NoDestinationMessage)
+ diagnosticsDestination(MessageType::NoDestinationMessage), justSentDiagnostics(false)
{
OutputBuffer::Init();
platform = new Platform();
@@ -355,14 +359,22 @@ void RepRap::Spin()
}
// Keep track of the loop time
- const uint32_t dt = Platform::GetInterruptClocks() - lastTime;
- if (dt < fastLoop)
+ if (justSentDiagnostics)
{
- fastLoop = dt;
+ // Sending diagnostics increases the loop time, so don't count it
+ justSentDiagnostics = false;
}
- if (dt > slowLoop)
+ else
{
- slowLoop = dt;
+ const uint32_t dt = Platform::GetInterruptClocks() - lastTime;
+ if (dt < fastLoop)
+ {
+ fastLoop = dt;
+ }
+ if (dt > slowLoop)
+ {
+ slowLoop = dt;
+ }
}
RTOSIface::Yield();
@@ -407,6 +419,7 @@ void RepRap::Diagnostics(MessageType mtype)
#ifdef DUET_NG
DuetExpansion::Diagnostics(mtype);
#endif
+ justSentDiagnostics = true;
}
// Turn off the heaters, disable the motors, and deactivate the Heat and Move classes. Leave everything else working.
@@ -672,26 +685,29 @@ void RepRap::Tick()
{
platform->Tick();
++ticksInSpinState;
- if (ticksInSpinState >= MaxTicksInSpinState) // if we stall for 20 seconds, save diagnostic data and reset
+#ifdef RTOS
+ ++heatTaskIdleTicks;
+ const bool heatTaskStuck = (heatTaskIdleTicks >= MaxTicksInSpinState);
+ if (heatTaskStuck || ticksInSpinState >= MaxTicksInSpinState) // if we stall for 20 seconds, save diagnostic data and reset
+#else
+ if (ticksInSpinState >= MaxTicksInSpinState) // if we stall for 20 seconds, save diagnostic data and reset
+#endif
{
resetting = true;
for (size_t i = 0; i < Heaters; i++)
{
platform->SetHeater(i, 0.0);
}
- for (size_t i = 0; i < DRIVES; i++)
- {
- platform->DisableDrive(i);
- // We can't set motor currents to 0 here because that requires interrupts to be working, and we are in an ISR
- }
+ platform->DisableAllDrives();
// We now save the stack when we get stuck in a spin loop
register const uint32_t * stackPtr asm ("sp");
-
- platform->SoftwareReset((uint16_t)SoftwareResetReason::stuckInSpin,
+ platform->SoftwareReset(
#ifdef RTOS
+ (heatTaskStuck) ? (uint16_t)SoftwareResetReason::heaterWatchdog : (uint16_t)SoftwareResetReason::stuckInSpin,
stackPtr + 5 + 15 // discard the stack used by the FreeRTOS stack handler and our tick handler
#else
+ (uint16_t)SoftwareResetReason::stuckInSpin,
stackPtr + 5 // discard the stack used by our tick handler
#endif
);
diff --git a/src/RepRap.h b/src/RepRap.h
index 64d2d459a2..0f741126c4 100644
--- a/src/RepRap.h
+++ b/src/RepRap.h
@@ -117,6 +117,10 @@ class RepRap
static uint32_t DoDivide(uint32_t a, uint32_t b); // helper function for diagnostic tests
+#ifdef RTOS
+ void KickHeatTaskWatchdog() { heatTaskIdleTicks = 0; }
+#endif
+
private:
static void EncodeString(StringRef& response, const char* src, size_t spaceToLeave, bool allowControlChars = false, char prefix = 0);
@@ -151,6 +155,9 @@ class RepRap
uint16_t activeToolHeaters;
uint16_t ticksInSpinState;
+#ifdef RTOS
+ uint16_t heatTaskIdleTicks;
+#endif
Module spinningModule;
uint32_t fastLoop, slowLoop;
@@ -176,6 +183,7 @@ class RepRap
// Deferred diagnostics
MessageType diagnosticsDestination;
+ bool justSentDiagnostics;
};
inline Platform& RepRap::GetPlatform() const { return *platform; }
diff --git a/src/Storage/FileStore.cpp b/src/Storage/FileStore.cpp
index 07322cb9de..002c31f660 100644
--- a/src/Storage/FileStore.cpp
+++ b/src/Storage/FileStore.cpp
@@ -5,6 +5,7 @@
#include "MassStorage.h"
#include "Platform.h"
#include "RepRap.h"
+#include "Libraries/Fatfs/diskio.h"
uint32_t FileStore::longestWriteTime = 0;
@@ -476,6 +477,12 @@ float FileStore::GetAndClearLongestWriteTime()
return ret;
}
+// Return the highest SD card retry count that resulted in a successful transfer
+unsigned int FileStore::GetAndClearMaxRetryCount()
+{
+ return DiskioGetAndClearMaxRetryCount();
+}
+
#if 0 // not currently used
// Provide a cluster map for fast seeking. Needs _USE_FASTSEEK defined as 1 in conf_fatfs to make any difference.
diff --git a/src/Storage/FileStore.h b/src/Storage/FileStore.h
index 9f674a4b20..8626974dc7 100644
--- a/src/Storage/FileStore.h
+++ b/src/Storage/FileStore.h
@@ -60,7 +60,7 @@ class FileStore
bool SetClusterMap(uint32_t[]); // Provide a cluster map for fast seeking
#endif
static float GetAndClearLongestWriteTime(); // Return the longest time it took to write a block to a file, in milliseconds
-
+ static unsigned int GetAndClearMaxRetryCount(); // Return the highest SD card retry count that resulted in a successful transfer
friend class MassStorage;
private:
diff --git a/src/Tasks.cpp b/src/Tasks.cpp
index a3a4c78216..26567b43d8 100644
--- a/src/Tasks.cpp
+++ b/src/Tasks.cpp
@@ -201,23 +201,15 @@ namespace Tasks
p.MessageF(mtype, " %s(%s,%u)",
taskDetails.pcTaskName, stateText, (unsigned int)(taskDetails.usStackHighWaterMark * sizeof(StackType_t)));
}
- p.Message(mtype, "\nMutexes:");
+ p.Message(mtype, "\nOwned mutexes:");
for (const Mutex *m = Mutex::GetMutexList(); m != nullptr; m = m->GetNext())
{
const TaskHandle holder = m->GetHolder();
- TaskStatus_t taskDetails;
- const char *holderText;
- if (holder == nullptr)
- {
- holderText = "null";
- }
- else
+ if (holder != nullptr)
{
- vTaskGetInfo(holder, &taskDetails, pdTRUE, eInvalid);
- holderText = taskDetails.pcTaskName;
+ p.MessageF(mtype, " %s(%s)", m->GetName(), pcTaskGetName(holder));
}
- p.MessageF(mtype, " %s(%s)", m->GetName(), holderText);
}
p.MessageF(mtype, "\n");
#endif
diff --git a/src/Version.h b/src/Version.h
index b2e0b33ce0..f033a04d0d 100644
--- a/src/Version.h
+++ b/src/Version.h
@@ -12,9 +12,9 @@
#ifndef VERSION
#ifdef RTOS
# define RTOSVER "(RTOS)"
-# define MAIN_VERSION "2.01beta1"
+# define MAIN_VERSION "2.01beta2"
#else
-# define MAIN_VERSION "1.21.2beta1"
+# define MAIN_VERSION "1.22beta2"
# define RTOSVER
#endif
@@ -22,7 +22,7 @@
#endif
#ifndef DATE
-# define DATE "2018-06-23b1"
+# define DATE "2018-07-14b5"
#endif
#define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman"