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"