diff options
author | David Crocker <dcrocker@eschertech.com> | 2018-06-23 13:45:38 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2018-06-23 13:45:38 +0300 |
commit | 4a525abb1663ab4817b303501d59760f36eb9ea7 (patch) | |
tree | 06c3c18608e33483c7aec1bb052cf4b901d42404 | |
parent | 5db504363ecf7a00c2f1a687cadb29bda47c7f1e (diff) |
Version 2.01beta1
Bug fixes:
- When using a mixing extruder, the feed rate for extruder-only moves was incorrect
- If additional axes were not created in the order UVWABC then incorrect homing files might be run (thanks chrishamm)
- On the Duet Maestro, the 7th stepper step/dir pin numbers were swapped
- If you paused a print during a G2/G3 arc move, on resuming it the arc centre was at the wrong place. This release defers the pause until the arc move is completed.
- If a command that interrogated the network (e.g. M122 on the Duet WiFi) was sent from USB, PanelDue or another non-network channel, the network subsystem could be accessed by multiple tasks concurrently, causing network disconnections or other errors
- When using a bltouch, between probe points the pin retracted, deployed and retracted again
- M206 with no parameters didn't report the current axis offsets
- During heating, the firmware returned M408 S0 responses to the PanelDue port even if the last request was M408 S2
- Fixed VBUS detection (thanks chrishamm)
- If the resume threshold in the M911 command was set higher than the supply voltage then the save-on-power-off mechanism never got primed. It will now prime at thwe auto-save threshold plus 0.5V or the resume threshold, whichever is lower.
- Fixed "2dtstc2diva=u" in debug printout
- Where a G- or M-code command parameter was supposed to accept unsigned values only, if a negative value was supplied then it was accepted and converted to a large unsigned value
New features and changed behaviour:
- If the firmware gets stuck in a spin loop, the RTOS builds now discard LR and 16 stack dwords in order to get more useful information from the stack trace
- Increased M999 delay to 1 second
- The report generated by M122 now includes a list of mutexes and their owners
- Added SW_ENC pin on CONN_SD to available GPIO ports (thanks chrishamm)
37 files changed, 429 insertions, 159 deletions
diff --git a/src/BugList.txt b/src/BugList.txt index 619a7bcd..e920e51f 100644 --- a/src/BugList.txt +++ b/src/BugList.txt @@ -189,17 +189,58 @@ Fixes in release 2.0: - [done] can't load filaments because DWC doesn't give the option unless a filament is already loaded - look at wifi disconnect issue, https://forum.duet3d.com/topic/4341/wifi-module-keeps-disconnecting/64 -Planned for 2.01: +Planned for 2.01/1.21.2: + +WiFi: +- [resolve library incompatibility] New ESP8266 SDK +- [done] check what's new in Arduino ESP 2.4.1 + +Bug fixes/investigations: +- [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 +- [done, test] when stuck in spin loop, in RTOS build discard LR and 16 stack dwords, they are within FreeRTOS +- [done, test] Rotating magnet monitor reports "allowed movement 2147483647% to 160%" in M591 response, see Tony's email (caused by R-1 in M591 command) +- [done, test] Duet Maestro 7th stepper step/dir pin numbers swapped +- [done, test] Pausing during an arc move, on resume the arc centre is at the wrong place. Temporarily don't allow pause during an arc move? +- [done] WiFi needs a mutex on SendCommand because WiFiInterface::Diagnostics and SetHostName can be called from outside the Network task. Similarly for W5500 Ethernet interface. +- [done, test] bltouch retracts, deploys, retracts between probes. Is the M280 retract command being queued until the end of the movement command? +- [checked, ok] Check that in M569, T3 is equivalent to T3:3:3:3, see https://forum.duet3d.com/topic/5579/expansion-stepper-breakout-tb6600 +- [done, test] M206 with no parameters to report axis offsets +- [done] During heating, returns M408 S0 responses to PanelDue port even if the last request was M408 S2, see https://forum.duet3d.com/topic/5761/printer-is-sending-m408-s0-when-heating. +- [done in CoreNG] Fix VBUS detection in CoreNG, see chrishamm's branch +- [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 + +- 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 +- 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 and BLTouch +- 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 - M3 R parameter so it can restore the spinder/laser after a pause (https://forum.duet3d.com/topic/5418/cnc-laser-m3-unpause-r-parameter) - M81: don't give low voltage warnings when main power has just been turned off -- Does bltouch need a default recovery time? -- Should bltouch use digital probe mode? Some users having problems with P25 in G31 command. - 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 @@ -208,8 +249,9 @@ Planned for 2.01: - 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.01): +Other (some of these may be in 2.02): - Update PanelDue firmware via Duet - Use Heat task to read DHT sensors? - Add option R4 to M915 command, to do an emergency stop (useful for Z motor) @@ -282,16 +324,11 @@ p->ref == 1 in WiFi Later versions: -- Multiple bed heaters, https://www.duet3d.com/forum/thread.php?pid=33903#p33903 -- Use M140H1:2:3 to set multiple bed heater numbers - Check https://www.duet3d.com/forum/thread.php?pid=33951#p33951 - Option to not broadcast SSID? -- On file read error during SD card print, treat like a temperature fault - Support faster homing by using interrupts to check homing switch states - Allow manual bed probing to be aborted -- Implement G1 S4? (like S2 but always relative) -- Add work coordinates? https://www.duet3d.com/forum/thread.php?pid=27128#p27128 - Don't do pressure advance during accel/decel of sequences of short segments - Arc move with same finish and start coordinates to do complete circle - Add T parameter to M207 diff --git a/src/DuetM/Pins_DuetM.h b/src/DuetM/Pins_DuetM.h index 50cf453a..3825e32b 100644 --- a/src/DuetM/Pins_DuetM.h +++ b/src/DuetM/Pins_DuetM.h @@ -67,8 +67,8 @@ constexpr size_t NUM_SERIAL_CHANNELS = 2; // The number of serial IO channels // Drivers constexpr Pin GlobalTmcEnablePin = 1; // The pin that drives ENN of all drivers constexpr Pin ENABLE_PINS[DRIVES] = { NoPin, NoPin, NoPin, NoPin, NoPin, 63, 61 }; -constexpr Pin STEP_PINS[DRIVES] = { 56, 38, 64, 40, 41, 67, 60 }; -constexpr Pin DIRECTION_PINS[DRIVES] = { 54, 8, 30, 33, 42, 18, 57 }; +constexpr Pin STEP_PINS[DRIVES] = { 56, 38, 64, 40, 41, 67, 57 }; +constexpr Pin DIRECTION_PINS[DRIVES] = { 54, 8, 30, 33, 42, 18, 60 }; constexpr Pin DriverMuxPins[3] = { 50, 52, 53 }; // Pins that control the UART multiplexer, LSB first // Endstops diff --git a/src/DuetM/TMC22xx.cpp b/src/DuetM/TMC22xx.cpp index a02c4ff0..5f373c26 100644 --- a/src/DuetM/TMC22xx.cpp +++ b/src/DuetM/TMC22xx.cpp @@ -44,7 +44,7 @@ constexpr uint32_t GCONF_INT_RSENSE = 1 << 1; // use internal sense resistors constexpr uint32_t GCONF_SPREAD_CYCLE = 1 << 2; // use spread cycle mode (else stealthchop mode) constexpr uint32_t GCONF_REV_DIR = 1 << 3; // reverse motor direction constexpr uint32_t GCONF_INDEX_OTPW = 1 << 4; // INDEX output shows over temperature warning (else it shows first microstep position) -constexpr uint32_t GCONF_INDEX_PULSE = 1 << 5; // INDEX output shows pulses form internal pulse generator, else as set by GCONF_INDEX_OTPW +constexpr uint32_t GCONF_INDEX_PULSE = 1 << 5; // INDEX output shows pulses from internal pulse generator, else as set by GCONF_INDEX_OTPW constexpr uint32_t GCONF_UART = 1 << 6; // PDN_UART used for UART interface (else used for power down) constexpr uint32_t GCONF_MSTEP_REG = 1 << 7; // microstep resolution set by MSTEP register (else by MS1 and MS2 pins) constexpr uint32_t GCONF_MULTISTEP_FILT = 1 << 8; // pulse generation optimised for >750Hz full stepping frequency diff --git a/src/DuetNG/Pins_DuetNG.h b/src/DuetNG/Pins_DuetNG.h index 53624560..3aa83879 100644 --- a/src/DuetNG/Pins_DuetNG.h +++ b/src/DuetNG/Pins_DuetNG.h @@ -162,7 +162,8 @@ constexpr Pin ROLAND_RTS_PIN = xx; // Expansion pin 12, PA13_RXD1 // This is the mapping from logical pins 60+ to firmware pin numbers constexpr Pin SpecialPinMap[] = { - 24, 97, 98, 99 // We allow CS5-CS8 to be used because few users need >4 thermocouples or RTDs + 24, 97, 98, 99, // We allow CS5-CS8 to be used because few users need >4 thermocouples or RTDs + 7 // SW_ENC on CONN_SD }; constexpr Pin DueX5GpioPinMap[] = { 211, 210, 209, 208 }; // Pins 100-103 map to GPIO 1-4 on DueX5 // We also allow pins 120-135 to be used if there is an additional SX1509B expander diff --git a/src/FilamentMonitors/FilamentMonitor.cpp b/src/FilamentMonitors/FilamentMonitor.cpp index 4c5254c2..a9482654 100644 --- a/src/FilamentMonitors/FilamentMonitor.cpp +++ b/src/FilamentMonitors/FilamentMonitor.cpp @@ -71,7 +71,7 @@ bool FilamentMonitor::ConfigurePin(GCodeBuffer& gb, const StringRef& reply, Inte // Static initialisation /*static*/ void FilamentMonitor::InitStatic() { - filamentSensorsMutex.Create(); + filamentSensorsMutex.Create("FilamentSensors"); } // Handle M591 diff --git a/src/GCodes/GCodeInput.cpp b/src/GCodes/GCodeInput.cpp index 35371c26..2e7383db 100644 --- a/src/GCodes/GCodeInput.cpp +++ b/src/GCodes/GCodeInput.cpp @@ -201,7 +201,7 @@ void NetworkGCodeInput::Put(MessageType mtype, const char *buf) NetworkGCodeInput::NetworkGCodeInput() : RegularGCodeInput() { - bufMutex.Create(); + bufMutex.Create("NetworkGCodeInput"); } // Fill a GCodeBuffer with the last available G-code diff --git a/src/GCodes/GCodeMachineState.h b/src/GCodes/GCodeMachineState.h index 88404df0..323ba860 100644 --- a/src/GCodes/GCodeMachineState.h +++ b/src/GCodes/GCodeMachineState.h @@ -50,23 +50,25 @@ enum class GCodeState : uint8_t stopping, sleeping, - // These next 7 must be contiguous + // These next 9 must be contiguous gridProbing1, gridProbing2a, gridProbing2b, gridProbing3, gridProbing4, + gridProbing4a, gridProbing5, gridProbing6, gridProbing7, - // These next 9 must be contiguous + // These next 10 must be contiguous probingAtPoint0, probingAtPoint1, probingAtPoint2a, probingAtPoint2b, probingAtPoint3, probingAtPoint4, + probingAtPoint4a, probingAtPoint5, probingAtPoint6, probingAtPoint7, diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 47f7947e..0fd79e68 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -269,6 +269,19 @@ float GCodes::FractionOfFilePrinted() const return (float)(fileBeingPrinted.GetPosition() - bytesCached) / (float)len; } +// Return the current position of the file being printed in bytes +FilePosition GCodes::GetFilePosition() const +{ + const FileData& fileBeingPrinted = fileGCode->OriginalMachineState().fileState; + if (!fileBeingPrinted.IsLive()) + { + return 0; + } + + const FilePosition bytesCached = fileGCode->IsDoingFileMacro() ? 0: fileInput->BytesCached(); + return fileBeingPrinted.GetPosition() - bytesCached; +} + // Start running the config file // We use triggerCGode as the source to prevent any triggers being executed until we have finished bool GCodes::RunConfigFile(const char* fileName) @@ -859,13 +872,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) g30zHeightErrorSum += g30zHeightError; } - // Move back up to the dive height - moveBuffer.SetDefaults(); - moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight(); - moveBuffer.feedRate = platform.GetZProbeTravelSpeed(); - NewMoveAvailable(1); gb.AdvanceState(); - if (platform.GetZProbeType() == ZProbeType::blTouch) { DoFileMacro(gb, RETRACTPROBE_G, false); // bltouch needs to be retracted when it triggers @@ -873,6 +880,15 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) } break; + case GCodeState::gridProbing4a: // ready to lift the probe after probing the current grid probe point + // Move back up to the dive height + moveBuffer.SetDefaults(); + moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight(); + moveBuffer.feedRate = platform.GetZProbeTravelSpeed(); + NewMoveAvailable(1); + gb.AdvanceState(); + break; + case GCodeState::gridProbing5: // finished probing a point and moved back to the dive height if (LockMovementAndWaitForStandstill(gb)) { @@ -1131,20 +1147,23 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) } } - // Move back up to the dive height before we change anything, in particular before we adjust leadscrews - moveBuffer.SetDefaults(); - moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight(); - moveBuffer.feedRate = platform.GetZProbeTravelSpeed(); - NewMoveAvailable(1); gb.AdvanceState(); - if (platform.GetZProbeType() == ZProbeType::blTouch) { - DoFileMacro(gb, RETRACTPROBE_G, false); // bltouch needs to be retracted when it triggers + DoFileMacro(gb, RETRACTPROBE_G, false); // bltouch needs to be retracted when it triggers } } break; + case GCodeState::probingAtPoint4a: + // Move back up to the dive height before we change anything, in particular before we adjust leadscrews + moveBuffer.SetDefaults(); + moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight(); + moveBuffer.feedRate = platform.GetZProbeTravelSpeed(); + NewMoveAvailable(1); + gb.AdvanceState(); + break; + case GCodeState::probingAtPoint5: // Here when we have moved the head back up to the dive height if (LockMovementAndWaitForStandstill(gb)) @@ -2608,7 +2627,7 @@ const char* GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise) // Adjust the move parameters to account for segmentation and/or part of the move having been done already void GCodes::FinaliseMove(GCodeBuffer& gb) { - moveBuffer.canPauseAfter = (moveBuffer.endStopsToCheck == 0); + moveBuffer.canPauseAfter = (moveBuffer.endStopsToCheck == 0) && !doingArcMove; // pausing during an arc move isn't save because the arc centre get recomputed incorrectly when we resume moveBuffer.filePos = (&gb == fileGCode) ? gb.GetFilePosition(fileInput->BytesCached()) : noFilePosition; if (totalSegments > 1) @@ -2663,6 +2682,10 @@ bool GCodes::ReadMove(RawMove& m) } } m.proportionLeft = 0.0; + if (doingArcMove) + { + m.canPauseAfter = true; // we can pause after the final segment of an arc move + } ClearMove(); } else @@ -4721,7 +4744,7 @@ void GCodes::CheckReportDue(GCodeBuffer& gb, const StringRef& reply) const if (lastAuxStatusReportType >= 0) { // Send a standard status response for PanelDue - OutputBuffer * const statusBuf = GenerateJsonStatusResponse(0, -1, ResponseSource::AUX); + OutputBuffer * const statusBuf = GenerateJsonStatusResponse(lastAuxStatusReportType, -1, ResponseSource::AUX); if (statusBuf != nullptr) { platform.AppendAuxReply(statusBuf, true); diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index 29190528..0c0de8ce 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -145,6 +145,7 @@ public: void GetCurrentCoordinates(const StringRef& s) const; // Write where we are into a string bool DoingFileMacro() const; // Or still busy processing a macro file? float FractionOfFilePrinted() const; // Get fraction of file printed + FilePosition GetFilePosition() const; // Return the current position of the file being printed in bytes void Diagnostics(MessageType mtype); // Send helpful information out bool RunConfigFile(const char* fileName); // Start running the config file @@ -294,7 +295,7 @@ private: void FinishWrite(GCodeBuffer& gb); // Finish writing to the file and respond bool SendConfigToLine(); // Deal with M503 - GCodeResult OffsetAxes(GCodeBuffer& gb); // Set offsets + GCodeResult OffsetAxes(GCodeBuffer& gb, const StringRef& reply); // Set/report offsets #if SUPPORT_WORKPLACE_COORDINATES GCodeResult GetSetWorkplaceCoordinates(GCodeBuffer& gb, const StringRef& reply, bool compute); // Set workspace coordinates diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index af298d35..a87c8d7a 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -1928,7 +1928,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) break; case 206: // Offset axes - result = OffsetAxes(gb); + result = OffsetAxes(gb, reply); break; case 207: // Set firmware retraction details @@ -2511,7 +2511,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) { const int type = gb.Seen('S') ? gb.GetIValue() : 0; const int seq = gb.Seen('R') ? gb.GetIValue() : -1; - if (&gb == auxGCode) + if (&gb == auxGCode && (type == 0 || type == 2)) { lastAuxStatusReportType = type; } @@ -3800,7 +3800,10 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) { if (reprap.GetScanner().IsEnabled()) { - result = GetGCodeResultFromFinished(reprap.GetScanner().Register()); + reprap.GetScanner().Register(); + + // The Scanner module will attempt to run a macro via this G-code source so we're not done yet + result = GCodeResult::notFinished; } else { @@ -4163,7 +4166,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) break; case 999: - result = DoDwellTime(gb, 500); // wait half a second to allow the response to be sent back to the web server, otherwise it may retry + result = DoDwellTime(gb, 1000); // wait a second to allow the response to be sent back to the web server, otherwise it may retry if (result != GCodeResult::notFinished) { reprap.EmergencyStop(); // this disables heaters and drives - Duet WiFi pre-production boards need drives disabled here diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp index 05cd8948..fc33b569 100644 --- a/src/GCodes/GCodes3.cpp +++ b/src/GCodes/GCodes3.cpp @@ -173,8 +173,9 @@ GCodeResult GCodes::SetPositions(GCodeBuffer& gb) // Offset the axes by the X, Y, and Z amounts in the M226 code in gb. The actual movement occurs on the next move command. // It's not clear from the description in the reprap.org wiki whether offsets are cumulative or not. We now assume they are not. // Note that M206 offsets are actually negative offsets. -GCodeResult GCodes::OffsetAxes(GCodeBuffer& gb) +GCodeResult GCodes::OffsetAxes(GCodeBuffer& gb, const StringRef& reply) { + bool seen = false; for (size_t axis = 0; axis < numVisibleAxes; axis++) { if (gb.Seen(axisLetters[axis])) @@ -185,6 +186,22 @@ GCodeResult GCodes::OffsetAxes(GCodeBuffer& gb) axisOffsets[axis] #endif = -(gb.GetFValue() * distanceScale); + seen = true; + } + } + + if (!seen) + { + reply.printf("Axis offsets:"); + for (size_t axis = 0; axis < numVisibleAxes; axis++) + { + reply.catf(" %c%.2f", axisLetters[axis], +#if SUPPORT_WORKPLACE_COORDINATES + -(double)(workplaceCoordinates[0][axis]/distanceScale) +#else + -(double)(axisOffsets[axis]/distanceScale) +#endif + ); } } @@ -230,7 +247,7 @@ GCodeResult GCodes::GetSetWorkplaceCoordinates(GCodeBuffer& gb, const StringRef& reply.printf("Origin of workplace %" PRIu32 ":", cs); for (size_t axis = 0; axis < numVisibleAxes; axis++) { - reply.catf(" %c%.2f", axisLetters[axis], (double)workplaceCoordinates[cs - 1][axis]); + reply.catf(" %c%.2f", axisLetters[axis], (double)(workplaceCoordinates[cs - 1][axis]/distanceScale)); } } return GCodeResult::ok; diff --git a/src/Heating/Sensors/DhtSensor.cpp b/src/Heating/Sensors/DhtSensor.cpp index 15da14bb..7340b951 100644 --- a/src/Heating/Sensors/DhtSensor.cpp +++ b/src/Heating/Sensors/DhtSensor.cpp @@ -144,7 +144,7 @@ DhtSensorHardwareInterface *DhtSensorHardwareInterface::Create(unsigned int rela /*static*/ void DhtSensorHardwareInterface::InitStatic() { - dhtMutex.Create(); + dhtMutex.Create("DHT"); } /*static*/ TemperatureError DhtSensorHardwareInterface::GetTemperatureOrHumidity(unsigned int relativeChannel, float& t, bool wantHumidity) diff --git a/src/Libraries/General/SafeStrtod.cpp b/src/Libraries/General/SafeStrtod.cpp index 0701bc72..1c27a01f 100644 --- a/src/Libraries/General/SafeStrtod.cpp +++ b/src/Libraries/General/SafeStrtod.cpp @@ -4,18 +4,22 @@ * Created on: 4 Apr 2018 * Author: David * - * This is a replacement for strtof() in the C standard library. That version has two problems: + * This is a replacement for strtod() and strtof() in the C standard library. Those versions have two problems: * 1. It is not reentrant. We can make it so by defining configUSE_NEWLIB_REENTRANT in FreeRTOS, but that makes the tasks much bigger. * 2. It allocates and releases heap memory, which is not nice. * * Limitations of this version * 1. Rounding to nearest double may not always be correct. - * 2. May not handle overflow for stupidly large numbers correctly. + * 2. Does not handle overflow for stupidly large numbers correctly. */ #include <cctype> #include <cmath> #include <climits> +#include <cstdlib> + +#include "SafeStrtod.h" +#undef strtoul // Undo the macro definition of strtoul in SafeStrtod.h so that we can call it in this file double SafeStrtod(const char *s, const char **p) { @@ -131,4 +135,40 @@ float SafeStrtof(const char *s, const char **p) return (float)SafeStrtod(s, p); } +unsigned long SafeStrtoul(const char *s, const char **endptr, int base) +{ + // strtoul() accepts a leading minus-sign, which we don't want to allow + while (*s == ' ' || *s == '\t') + { + ++s; + } + if (*s == '-') + { + if (endptr != nullptr) + { + *endptr = s; + } + return 0; + } + return strtoul(s, const_cast<char**>(endptr), base); +} + +unsigned long SafeStrtoul(char *s, char **endptr, int base) +{ + // strtoul() accepts a leading minus-sign, which we don't want to allow + while (*s == ' ' || *s == '\t') + { + ++s; + } + if (*s == '-') + { + if (endptr != nullptr) + { + *endptr = s; + } + return 0; + } + return strtoul(s, endptr, base); +} + // End diff --git a/src/Libraries/General/SafeStrtod.h b/src/Libraries/General/SafeStrtod.h index ae83228c..d1dc1d5c 100644 --- a/src/Libraries/General/SafeStrtod.h +++ b/src/Libraries/General/SafeStrtod.h @@ -16,15 +16,8 @@ inline long SafeStrtol(const char *s, const char **endptr = nullptr, int base = return strtol(s, const_cast<char**>(endptr), base); } -inline unsigned long SafeStrtoul(const char *s, const char **endptr = nullptr, int base = 10) -{ - return strtoul(s, const_cast<char**>(endptr), base); -} - -inline unsigned long SafeStrtoul(char *s, char **endptr = nullptr, int base = 10) -{ - return strtoul(s, endptr, base); -} +unsigned long SafeStrtoul(const char *s, const char **endptr = nullptr, int base = 10); +unsigned long SafeStrtoul(char *s, char **endptr = nullptr, int base = 10); #define strtod(s, p) Do_not_use_strtod_use_SafeStrtod_instead #define strtof(s, p) Do_not_use_strtof_use_SafeStrtof_instead diff --git a/src/Libraries/General/SafeVsnprintf.cpp b/src/Libraries/General/SafeVsnprintf.cpp index 0952c243..9b171ce0 100644 --- a/src/Libraries/General/SafeVsnprintf.cpp +++ b/src/Libraries/General/SafeVsnprintf.cpp @@ -15,7 +15,7 @@ Changes for the FreeRTOS ports: - The dot in "%-8.8s" - - The specifiers 'l' (long) and 'L' (long long) + - The specifiers 'l' (long) and 'll' (long long) - The specifier 'u' for unsigned - Dot notation for IP addresses: sprintf("IP = %xip\n", 0xC0A80164); @@ -546,14 +546,15 @@ static void tiny_print(SStringBuf& apBuf, const char *format, va_list args) if (ch == 'l') { ch = *format++; - apBuf.flags.long32 = 1; - // Makes no difference as u32 == long - } - if (ch == 'L') - { - ch = *format++; - apBuf.flags.long64 = 1; - // Does make a difference + if (ch == 'l') + { + ch = *format++; + apBuf.flags.long64 = 1; + } + else + { + apBuf.flags.long32 = 1; + } } if (ch == 'f' || ch == 'e' || ch == 'F' || ch == 'E') diff --git a/src/Libraries/General/StringRef.h b/src/Libraries/General/StringRef.h index d6d84a31..74a52ddd 100644 --- a/src/Libraries/General/StringRef.h +++ b/src/Libraries/General/StringRef.h @@ -54,7 +54,7 @@ public: const char *c_str() const { return storage; } size_t strlen() const { return Strnlen(storage, Len); } bool IsEmpty() const { return storage[0] == 0; } -// char *Pointer() { return storage; } + bool IsFull() const { return strlen() == Len; } char& operator[](size_t index) { return storage[index]; } char operator[](size_t index) const { return storage[index]; } constexpr size_t Capacity() const { return Len; } @@ -74,6 +74,7 @@ public: bool ConstantTimeEquals(String<Len> other) const; void Truncate(size_t len); + void Erase(size_t pos, size_t count = 1); private: char storage[Len + 1]; @@ -133,6 +134,20 @@ template<size_t Len> void String<Len>::Truncate(size_t len) } } +template<size_t Len> void String<Len>::Erase(size_t pos, size_t count) +{ + const size_t len = strlen(); + if (pos < len) + { + while (pos + count < len) + { + storage[pos] = storage[pos + count]; + ++pos; + } + storage[pos] = 0; + } +} + template<size_t Len> bool String<Len>::EndsWith(char c) const { const size_t len = strlen(); diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp index a969c6dc..dfa7ae75 100644 --- a/src/Movement/DDA.cpp +++ b/src/Movement/DDA.cpp @@ -266,6 +266,7 @@ bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping) } xyMoving = false; + bool axesMoving = false; bool extruding = false; // we set this true if extrusion was commanded, even if it is too small to do bool realMove = false; float accelerations[DRIVES]; @@ -345,6 +346,10 @@ bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping) } } } + else + { + axesMoving = true; + } } } @@ -413,18 +418,25 @@ bool DDA::Init(GCodes::RawMove &nextMove, bool doMotorMapping) // This means that the user gets the feed rate that he asked for. It also makes the delta calculations simpler. // First do the bed tilt compensation for deltas. directionVector[Z_AXIS] += (directionVector[X_AXIS] * k.GetTiltCorrection(X_AXIS)) + (directionVector[Y_AXIS] * k.GetTiltCorrection(Y_AXIS)); - totalDistance = NormaliseXYZ(); } + 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); + } else { - // Extruder-only movement, or movement of additional axes, or a combination. - // Currently we normalise vector sum of all drive movement to unit length. - // Alternatives would be: - // 1. Normalise the largest one to unit length. This means that when retracting multiple filaments, they all get the requested retract speed. - // 2. Normalise the sum to unit length. This means that when we use mixing, we get the requested extrusion rate at the nozzle. - // 3. Normalise the sum to the sum of the mixing coefficients (which we would have to include in the move details). - totalDistance = Normalise(directionVector, DRIVES, DRIVES); + // Extruder-only movement. Normalise so that the magnitude is the total absolute movement. This gives the correct feed rate for mixing extruders. + totalDistance = 0.0; + for (size_t d = 0; d < DRIVES; d++) + { + totalDistance += fabs(directionVector[d]); + } + if (totalDistance > 0.0) // should always be true + { + Scale(directionVector, 1.0/totalDistance, DRIVES); + } } // 5. Compute the maximum acceleration available diff --git a/src/Movement/Kinematics/Kinematics.cpp b/src/Movement/Kinematics/Kinematics.cpp index 50c1409e..51c4d1fe 100644 --- a/src/Movement/Kinematics/Kinematics.cpp +++ b/src/Movement/Kinematics/Kinematics.cpp @@ -18,6 +18,7 @@ #include "RepRap.h" #include "Platform.h" #include "GCodes/GCodeBuffer.h" +#include "GCodes/GCodes.h" const char * const Kinematics::HomeAllFileName = "homeall.g"; const char * const Kinematics::StandardHomingFileNames[] = AXES_("homex.g", "homey.g", "homez.g", "homeu.g", "homev.g", "homew.g", "homea.g", "homeb.g", "homec.g"); @@ -115,11 +116,20 @@ const char* Kinematics::GetHomingFileName(AxesBitmap toBeHomed, AxesBitmap alrea const AxesBitmap homeFirst = AxesToHomeBeforeProbing(); // Return the homing file for the lowest axis that we have been asked to home + const char * const axisNames = reprap.GetGCodes().GetAxisLetters(); + const char lettersToTry[] = "XYZUVWABC"; + static_assert(ARRAY_SIZE(lettersToTry) >= MaxAxes, "lettersToTry too short"); for (size_t axis = 0; axis < numVisibleAxes; ++axis) { if (IsBitSet(toBeHomed, axis) && (axis != Z_AXIS || !homeZLast || (alreadyHomed & homeFirst) == homeFirst)) { - return StandardHomingFileNames[axis]; + for (size_t actualAxis = 0; actualAxis < MaxAxes; actualAxis++) + { + if (axisNames[axis] == lettersToTry[actualAxis]) + { + return StandardHomingFileNames[actualAxis]; + } + } } } diff --git a/src/Movement/Kinematics/Kinematics.h b/src/Movement/Kinematics/Kinematics.h index 5c386f68..531d0a45 100644 --- a/src/Movement/Kinematics/Kinematics.h +++ b/src/Movement/Kinematics/Kinematics.h @@ -17,7 +17,7 @@ inline floatc_t fcsquare(floatc_t a) } // Different types of kinematics we support. Each of these has a class to represent it. -// These must have the same numeric assignments as the K parameter of the M669 command, as documented in ??? +// These must have the same numeric assignments as the K parameter of the M669 command, as documented in the GCodes wiki page enum class KinematicsType : uint8_t { cartesian = 0, @@ -29,6 +29,7 @@ enum class KinematicsType : uint8_t hangprinter, polar, coreXYUV, +// linearDeltaPlusZ, // reserved for @sga, see https://forum.duet3d.com/topic/5775/aditional-carterian-z-axis-on-delta-printer unknown // this one must be last! }; diff --git a/src/Networking/ESP8266WiFi/WiFiInterface.cpp b/src/Networking/ESP8266WiFi/WiFiInterface.cpp index 2f4e8b18..394db873 100644 --- a/src/Networking/ESP8266WiFi/WiFiInterface.cpp +++ b/src/Networking/ESP8266WiFi/WiFiInterface.cpp @@ -158,6 +158,8 @@ WiFiInterface::WiFiInterface(Platform& p) : platform(p), uploader(nullptr), ftpD void WiFiInterface::Init() { + interfaceMutex.Create("WiFi"); + // Make sure the ESP8266 is held in the reset state ResetWiFi(); lastTickMillis = millis(); @@ -182,6 +184,8 @@ GCodeResult WiFiInterface::EnableProtocol(NetworkProtocol protocol, int port, in else if (protocol < NumProtocols) { const Port portToUse = (port < 0) ? DefaultPortNumbers[protocol] : port; + MutexLocker lock(interfaceMutex); + if (portToUse != portNumbers[protocol] && state == NetworkState::active) { // We need to shut down and restart the protocol if it is active because the port number has changed @@ -210,6 +214,8 @@ GCodeResult WiFiInterface::DisableProtocol(NetworkProtocol protocol, const Strin { if (protocol < NumProtocols) { + MutexLocker lock(interfaceMutex); + if (state == NetworkState::active) { ShutdownProtocol(protocol); @@ -225,6 +231,8 @@ GCodeResult WiFiInterface::DisableProtocol(NetworkProtocol protocol, const Strin void WiFiInterface::StartProtocol(NetworkProtocol protocol) { + MutexLocker lock(interfaceMutex); + switch(protocol) { case HttpProtocol: @@ -246,6 +254,8 @@ void WiFiInterface::StartProtocol(NetworkProtocol protocol) void WiFiInterface::ShutdownProtocol(NetworkProtocol protocol) { + MutexLocker lock(interfaceMutex); + switch(protocol) { case HttpProtocol: @@ -432,6 +442,8 @@ void WiFiInterface::Stop() { if (state != NetworkState::disabled) { + MutexLocker lock(interfaceMutex); + digitalWrite(SamTfrReadyPin, LOW); // tell the ESP we can't receive digitalWrite(EspResetPin, LOW); // put the ESP back into reset DisableEspInterrupt(); // ignore IRQs from the transfer request pin @@ -1535,6 +1547,8 @@ int32_t WiFiInterface::SendCommand(NetworkCommand cmd, SocketNumber socketNum, u return ResponseNetworkDisabled; } + MutexLocker lock(interfaceMutex); + if (transferPending) { if (reprap.Debug(moduleNetwork)) diff --git a/src/Networking/HttpResponder.cpp b/src/Networking/HttpResponder.cpp index b96fcac2..edad5413 100644 --- a/src/Networking/HttpResponder.cpp +++ b/src/Networking/HttpResponder.cpp @@ -1296,7 +1296,7 @@ void HttpResponder::Diagnostics(MessageType mt) const /*static*/ void HttpResponder::InitStatic() { - gcodeReplyMutex.Create(); + gcodeReplyMutex.Create("HttpGCodeReply"); } // This is called from the GCodes task to store a response, which is picked up by the Network task diff --git a/src/Networking/LwipEthernet/LwipEthernetInterface.cpp b/src/Networking/LwipEthernet/LwipEthernetInterface.cpp index c203cf28..27e44fe7 100644 --- a/src/Networking/LwipEthernet/LwipEthernetInterface.cpp +++ b/src/Networking/LwipEthernet/LwipEthernetInterface.cpp @@ -140,6 +140,9 @@ LwipEthernetInterface::LwipEthernetInterface(Platform& p) : platform(p), closeDa void LwipEthernetInterface::Init() { + interfaceMutex.Create("Lwip"); + //TODO we don't yet use this mutex anywhere! + // Clear the PCBs for (size_t i = 0; i < NumTcpPorts; ++i) { diff --git a/src/Networking/Network.cpp b/src/Networking/Network.cpp index ddfc9099..40a9d742 100644 --- a/src/Networking/Network.cpp +++ b/src/Networking/Network.cpp @@ -59,8 +59,8 @@ Network::Network(Platform& p) : platform(p), responders(nullptr), nextResponderT // Note that Platform::Init() must be called before this to that Platform::IsDuetWiFi() returns the correct value void Network::Init() { - httpMutex.Create(); - telnetMutex.Create(); + httpMutex.Create("HTTP"); + telnetMutex.Create("Telnet"); #if defined(DUET_NG) interfaces[0] = (platform.IsDuetWiFi()) ? static_cast<NetworkInterface*>(new WiFiInterface(platform)) : static_cast<NetworkInterface*>(new W5500Interface(platform)); diff --git a/src/Networking/NetworkInterface.h b/src/Networking/NetworkInterface.h index 6e10ea4f..4c807971 100644 --- a/src/Networking/NetworkInterface.h +++ b/src/Networking/NetworkInterface.h @@ -41,6 +41,8 @@ public: virtual void OpenDataPort(Port port) = 0; virtual void TerminateDataPort() = 0; + Mutex interfaceMutex; // mutex to protect against multiple tasks using the same interface concurrently. Public so that sockets can lock it. + protected: Port portNumbers[NumProtocols]; // port number used for each protocol bool protocolEnabled[NumProtocols]; // whether each protocol is enabled diff --git a/src/Networking/TelnetResponder.cpp b/src/Networking/TelnetResponder.cpp index 41cbe749..f2caf8a5 100644 --- a/src/Networking/TelnetResponder.cpp +++ b/src/Networking/TelnetResponder.cpp @@ -291,7 +291,7 @@ void TelnetResponder::ProcessLine() /*static*/ void TelnetResponder::InitStatic() { - gcodeReplyMutex.Create(); + gcodeReplyMutex.Create("TelnetGCodeReply"); } /*static*/ void TelnetResponder::HandleGCodeReply(const char *reply) diff --git a/src/Networking/W5500Ethernet/W5500Interface.cpp b/src/Networking/W5500Ethernet/W5500Interface.cpp index 60a254f0..8d5a885a 100644 --- a/src/Networking/W5500Ethernet/W5500Interface.cpp +++ b/src/Networking/W5500Ethernet/W5500Interface.cpp @@ -35,6 +35,8 @@ W5500Interface::W5500Interface(Platform& p) void W5500Interface::Init() { + interfaceMutex.Create("W5500"); + // Ensure that the W5500 chip is in the reset state pinMode(W5500ResetPin, OUTPUT_LOW); lastTickMillis = millis(); @@ -53,6 +55,8 @@ GCodeResult W5500Interface::EnableProtocol(NetworkProtocol protocol, int port, i if (protocol < NumProtocols) { + MutexLocker lock(interfaceMutex); + const Port portToUse = (port < 0) ? DefaultPortNumbers[protocol] : port; if (portToUse != portNumbers[protocol] && state == NetworkState::active) { @@ -87,6 +91,8 @@ GCodeResult W5500Interface::DisableProtocol(NetworkProtocol protocol, const Stri { if (protocol < NumProtocols) { + MutexLocker lock(interfaceMutex); + if (state == NetworkState::active) { ShutdownProtocol(protocol); @@ -102,6 +108,8 @@ GCodeResult W5500Interface::DisableProtocol(NetworkProtocol protocol, const Stri void W5500Interface::StartProtocol(NetworkProtocol protocol) { + MutexLocker lock(interfaceMutex); + switch(protocol) { case HttpProtocol: @@ -126,6 +134,8 @@ void W5500Interface::StartProtocol(NetworkProtocol protocol) void W5500Interface::ShutdownProtocol(NetworkProtocol protocol) { + MutexLocker lock(interfaceMutex); + switch(protocol) { case HttpProtocol: @@ -222,6 +232,8 @@ void W5500Interface::SetMacAddress(const uint8_t mac[]) // Start up the network void W5500Interface::Start() { + MutexLocker lock(interfaceMutex); + SetIPAddress(platform.GetIPAddress(), platform.NetMask(), platform.GateWay()); pinMode(W5500ResetPin, OUTPUT_LOW); delayMicroseconds(550); // W550 reset pulse must be at least 500us long @@ -252,6 +264,8 @@ void W5500Interface::Stop() { if (state != NetworkState::disabled) { + MutexLocker lock(interfaceMutex); + if (usingDhcp) { DHCP_stop(); @@ -273,21 +287,25 @@ void W5500Interface::Spin(bool full) break; case NetworkState::establishingLink: - if (full && wizphy_getphylink() == PHY_LINK_ON) { - usingDhcp = (ipAddress[0] == 0 && ipAddress[1] == 0 && ipAddress[2] == 0 && ipAddress[3] == 0); - if (usingDhcp) - { - // IP address is all zeros, so use DHCP -// debugPrintf("Link established, getting IP address\n"); - DHCP_init(DhcpSocketNumber, platform.Random(), reprap.GetNetwork().GetHostname()); - lastTickMillis = millis(); - state = NetworkState::obtainingIP; - } - else + MutexLocker lock(interfaceMutex); + + if (full && wizphy_getphylink() == PHY_LINK_ON) { -// debugPrintf("Link established, network running\n"); - state = NetworkState::connected; + usingDhcp = (ipAddress[0] == 0 && ipAddress[1] == 0 && ipAddress[2] == 0 && ipAddress[3] == 0); + if (usingDhcp) + { + // IP address is all zeros, so use DHCP +// debugPrintf("Link established, getting IP address\n"); + DHCP_init(DhcpSocketNumber, platform.Random(), reprap.GetNetwork().GetHostname()); + lastTickMillis = millis(); + state = NetworkState::obtainingIP; + } + else + { +// debugPrintf("Link established, network running\n"); + state = NetworkState::connected; + } } } break; @@ -295,6 +313,8 @@ void W5500Interface::Spin(bool full) case NetworkState::obtainingIP: if (full) { + MutexLocker lock(interfaceMutex); + if (wizphy_getphylink() == PHY_LINK_ON) { const uint32_t now = millis(); @@ -334,45 +354,49 @@ void W5500Interface::Spin(bool full) break; case NetworkState::active: - // Check that the link is still up - if (wizphy_getphylink() == PHY_LINK_ON) { - // Maintain DHCP - if (full && usingDhcp) + MutexLocker lock(interfaceMutex); + + // Check that the link is still up + if (wizphy_getphylink() == PHY_LINK_ON) { - const uint32_t now = millis(); - if (now - lastTickMillis >= 1000) - { - lastTickMillis += 1000; - DHCP_time_handler(); - } - const DhcpRunResult ret = DHCP_run(); - if (ret == DhcpRunResult::DHCP_IP_CHANGED || ret == DhcpRunResult::DHCP_IP_ASSIGN) + // Maintain DHCP + if (full && usingDhcp) { -// debugPrintf("IP address changed\n"); - getSIPR(ipAddress); + const uint32_t now = millis(); + if (now - lastTickMillis >= 1000) + { + lastTickMillis += 1000; + DHCP_time_handler(); + } + const DhcpRunResult ret = DHCP_run(); + if (ret == DhcpRunResult::DHCP_IP_CHANGED || ret == DhcpRunResult::DHCP_IP_ASSIGN) + { +// debugPrintf("IP address changed\n"); + getSIPR(ipAddress); + } } - } - // Poll the next TCP socket - sockets[nextSocketToPoll]->Poll(full); + // Poll the next TCP socket + sockets[nextSocketToPoll]->Poll(full); - // Move on to the next TCP socket for next time - ++nextSocketToPoll; - if (nextSocketToPoll == NumW5500TcpSockets) - { - nextSocketToPoll = 0; + // Move on to the next TCP socket for next time + ++nextSocketToPoll; + if (nextSocketToPoll == NumW5500TcpSockets) + { + nextSocketToPoll = 0; + } } - } - else if (full) - { -// debugPrintf("Lost phy link\n"); - if (usingDhcp) + else if (full) { - DHCP_stop(); +// debugPrintf("Lost phy link\n"); + if (usingDhcp) + { + DHCP_stop(); + } + TerminateSockets(); + state = NetworkState::establishingLink; } - TerminateSockets(); - state = NetworkState::establishingLink; } break; } diff --git a/src/Networking/W5500Ethernet/W5500Socket.cpp b/src/Networking/W5500Ethernet/W5500Socket.cpp index 8e200352..f6bacc2b 100644 --- a/src/Networking/W5500Ethernet/W5500Socket.cpp +++ b/src/Networking/W5500Ethernet/W5500Socket.cpp @@ -7,6 +7,7 @@ #include "W5500Socket.h" #include "Network.h" +#include "NetworkInterface.h" #include "Wiznet/Ethernet/socketlib.h" #include "NetworkBuffer.h" #include "RepRap.h" @@ -32,12 +33,16 @@ void W5500Socket::Init(SocketNumber skt, Port serverPort, NetworkProtocol p) void W5500Socket::TerminateAndDisable() { + MutexLocker lock(interface->interfaceMutex); + Terminate(); state = SocketState::disabled; } void W5500Socket::ReInit() { + MutexLocker lock(interface->interfaceMutex); + // Discard any received data while (receivedData != nullptr) { @@ -56,6 +61,8 @@ void W5500Socket::ReInit() // Close a connection when the last packet has been sent void W5500Socket::Close() { + MutexLocker lock(interface->interfaceMutex); + if (state != SocketState::disabled && state != SocketState::inactive) { ExecCommand(socketNum, Sn_CR_DISCON); @@ -71,6 +78,8 @@ void W5500Socket::Close() // Terminate a connection immediately void W5500Socket::Terminate() { + MutexLocker lock(interface->interfaceMutex); + if (state != SocketState::disabled) { disconnectNoWait(socketNum); @@ -97,6 +106,8 @@ bool W5500Socket::ReadChar(char& c) { if (receivedData != nullptr) { + MutexLocker lock(interface->interfaceMutex); + const bool ret = receivedData->ReadChar(c); if (receivedData->IsEmpty()) { @@ -140,6 +151,8 @@ void W5500Socket::Poll(bool full) { if (state != SocketState::disabled) { + MutexLocker lock(interface->interfaceMutex); + switch(getSn_SR(socketNum)) { case SOCK_INIT: @@ -206,7 +219,7 @@ void W5500Socket::Poll(bool full) } } -// Try to receive more incoming data from the socket +// Try to receive more incoming data from the socket. The mutex is alrady owned. void W5500Socket::ReceiveData() { const uint16_t len = getSn_RX_RSR(socketNum); @@ -245,7 +258,7 @@ void W5500Socket::ReceiveData() } } -// Discard any received data for this transaction +// Discard any received data for this transaction. The mutex is already owned. void W5500Socket::DiscardReceivedData() { while (receivedData != nullptr) @@ -257,6 +270,8 @@ void W5500Socket::DiscardReceivedData() // Send the data, returning the length buffered size_t W5500Socket::Send(const uint8_t *data, size_t length) { + MutexLocker lock(interface->interfaceMutex); + if (CanSend() && length != 0 && getSn_SR(socketNum) == SOCK_ESTABLISHED) { // Check for previous send complete @@ -311,6 +326,8 @@ size_t W5500Socket::Send(const uint8_t *data, size_t length) // Tell the interface to send the outstanding data void W5500Socket::Send() { + MutexLocker lock(interface->interfaceMutex); + if (CanSend() && sendOutstanding) { setSn_TX_WR(socketNum, wizTxBufferPtr); diff --git a/src/Platform.cpp b/src/Platform.cpp index 64000fe1..2e74416e 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -57,6 +57,8 @@ #include <climits> +extern uint32_t _estack; // defined in the linker script + #if !defined(HAS_LWIP_NETWORKING) || !defined(HAS_WIFI_NETWORKING) || !defined(HAS_CPU_TEMP_SENSOR) || !defined(HAS_HIGH_SPEED_SD) \ || !defined(HAS_SMART_DRIVERS) || !defined(HAS_STALL_DETECT) || !defined(HAS_VOLTAGE_MONITOR) || !defined(HAS_VREF_MONITOR) || !defined(ACTIVE_LOW_HEAT_ON) \ || !defined(SUPPORT_NONLINEAR_EXTRUSION) @@ -215,8 +217,8 @@ void Platform::Init() commsParams[2] = 0; #endif - usbMutex.Create(); - auxMutex.Create(); + usbMutex.Create("USB"); + auxMutex.Create("Aux"); auxDetected = false; auxSeq = 0; @@ -224,7 +226,7 @@ void Platform::Init() SERIAL_AUX_DEVICE.begin(baudRates[1]); // this can't be done in the constructor because the Arduino port initialisation isn't complete at that point #ifdef SERIAL_AUX2_DEVICE SERIAL_AUX2_DEVICE.begin(baudRates[2]); - aux2Mutex.Create(); + aux2Mutex.Create("Aux2"); #endif compatibility = Compatibility::marlin; // default to Marlin because the common host programs expect the "OK" response to commands @@ -1074,7 +1076,7 @@ void Platform::UpdateFirmware() } #if defined(DUET_NG) || defined(DUET_M) - IoPort::WriteDigital(Z_PROBE_MOD_PIN, false); // turn the DIAG LED off + IoPort::WriteDigital(DiagPin, false); // turn the DIAG LED off #endif wdt_restart(WDT); // kick the watchdog one last time @@ -1381,7 +1383,7 @@ void Platform::Spin() { if ((stalledDrivers & mask) == 0) { - // This stall is new and we are printing, so check whether we need to perform some action in response to the stall + // This stall is new so check whether we need to perform some action in response to the stall if ((rehomeOnStallDrivers & mask) != 0) { stalledDriversToRehome |= mask; @@ -1551,7 +1553,8 @@ void Platform::Spin() switch (autoSaveState) { case AutoSaveState::starting: - if (currentVin >= autoResumeReading) + // Some users set the auto resume threshold high to disable auto resume, so prime auto save at the auto save threshold plus half a volt + if (currentVin >= autoResumeReading || currentVin > autoPauseReading + PowerVoltageToAdcReading(0.5)) { autoSaveState = AutoSaveState::normal; } @@ -1794,7 +1797,8 @@ void Platform::SoftwareReset(uint16_t reason, const uint32_t *stk) srdBuf[slot].sp = reinterpret_cast<uint32_t>(stk); for (size_t i = 0; i < ARRAY_SIZE(srdBuf[slot].stack); ++i) { - srdBuf[slot].stack[i] = stk[i]; + srdBuf[slot].stack[i] = (stk < &_estack) ? *stk : 0xFFFFFFFF; + ++stk; } } diff --git a/src/RTOSIface.cpp b/src/RTOSIface.cpp index ee9650bc..c185a454 100644 --- a/src/RTOSIface.cpp +++ b/src/RTOSIface.cpp @@ -15,11 +15,14 @@ static_assert(Mutex::TimeoutUnlimited == portMAX_DELAY, "Bad value for TimeoutUnlimited"); -void Mutex::Create() +void Mutex::Create(const char *pName) { if (handle == nullptr) { handle = xSemaphoreCreateRecursiveMutexStatic(&storage); + name = pName; + next = mutexList; + mutexList = this; } } @@ -39,10 +42,11 @@ TaskHandle Mutex::GetHolder() const } TaskBase *TaskBase::taskList = nullptr; +Mutex *Mutex::mutexList = nullptr; #else -void Mutex::Create() +void Mutex::Create(const char *pName) { } diff --git a/src/RTOSIface.h b/src/RTOSIface.h index a1964b66..151f3204 100644 --- a/src/RTOSIface.h +++ b/src/RTOSIface.h @@ -28,21 +28,34 @@ class Mutex public: Mutex() { handle = nullptr; } - void Create(); + void Create(const char *pName); bool Take(uint32_t timeout = TimeoutUnlimited) const; bool Release() const; TaskHandle GetHolder() const; - static constexpr uint32_t TimeoutUnlimited = 0xFFFFFFFF; +#ifdef RTOS + const Mutex *GetNext() const { return next; } + const char *GetName() const { return name; } +#endif Mutex(const Mutex&) = delete; Mutex& operator=(const Mutex&) = delete; + static constexpr uint32_t TimeoutUnlimited = 0xFFFFFFFF; + +#ifdef RTOS + static const Mutex *GetMutexList() { return mutexList; } +#endif + private: #ifdef RTOS SemaphoreHandle_t handle; + Mutex *next; + const char *name; StaticSemaphore_t storage; + + static Mutex *mutexList; #else void *handle; #endif @@ -60,7 +73,7 @@ public: void Suspend() const { vTaskSuspend(handle); } const TaskBase *GetNext() const { return next; } - TaskBase(const TaskBase&) = delete; // it's not save to copy these + TaskBase(const TaskBase&) = delete; // it's not safe to copy these TaskBase& operator=(const TaskBase&) = delete; // it's not safe to assign these // Ideally we would declare the destructor as deleted too, because it's unsafe to delete these because they are linked together via the 'next' field. // But that prevents us from declaring static instances of tasks. diff --git a/src/RepRap.cpp b/src/RepRap.cpp index 914b860a..e83bd6e7 100644 --- a/src/RepRap.cpp +++ b/src/RepRap.cpp @@ -154,8 +154,8 @@ RepRap::RepRap() : toolList(nullptr), currentTool(nullptr), lastWarningMillis(0) void RepRap::Init() { - toolListMutex.Create(); - messageBoxMutex.Create(); + toolListMutex.Create("ToolList"); + messageBoxMutex.Create("MessageBox"); platform->Init(); network->Init(); @@ -687,7 +687,14 @@ void RepRap::Tick() // 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, stackPtr + 5); + + platform->SoftwareReset((uint16_t)SoftwareResetReason::stuckInSpin, +#ifdef RTOS + stackPtr + 5 + 15 // discard the stack used by the FreeRTOS stack handler and our tick handler +#else + stackPtr + 5 // discard the stack used by our tick handler +#endif + ); } } } @@ -1320,6 +1327,9 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) // Fraction of file printed response->catf("],\"fractionPrinted\":%.1f", (double)((printMonitor->IsPrinting()) ? (gCodes->FractionOfFilePrinted() * 100.0) : 0.0)); + // Byte position of the file being printed + response->catf(",\"filePosition\":%lu", gCodes->GetFilePosition()); + // First Layer Duration response->catf(",\"firstLayerDuration\":%.1f", (double)(printMonitor->GetFirstLayerDuration())); diff --git a/src/Scanner.cpp b/src/Scanner.cpp index 64ab3a62..6e762bd7 100644 --- a/src/Scanner.cpp +++ b/src/Scanner.cpp @@ -13,6 +13,8 @@ #include "Platform.h" +const char* const SCANNER_ON_G = "scanner_on.g"; +const char* const SCANNER_OFF_G = "scanner_off.g"; const char* const ALIGN_ON_G = "align_on.g"; const char* const ALIGN_OFF_G = "align_off.g"; const char* const SCAN_PRE_G = "scan_pre.g"; @@ -104,7 +106,8 @@ void Scanner::Spin() } SetState(ScannerState::Disconnected); - // Cannot do anything else... + // Run a macro to perform custom actions when the scanner is removed + DoFileMacro(SCANNER_OFF_G); return; } @@ -389,18 +392,15 @@ bool Scanner::Enable() } // Register a scanner device -bool Scanner::Register() +void Scanner::Register() { - if (IsRegistered()) + if (!IsRegistered()) { - // Don't do anything if a device is already registered - return true; - } - - platform.Message(MessageType::BlockingUsbMessage, "OK\n"); - SetState(ScannerState::Idle); + platform.Message(MessageType::BlockingUsbMessage, "OK\n"); + SetState(ScannerState::Idle); - return true; + DoFileMacro(SCANNER_ON_G); + } } // Initiate a new scan diff --git a/src/Scanner.h b/src/Scanner.h index db50c5d1..0c5c279d 100644 --- a/src/Scanner.h +++ b/src/Scanner.h @@ -53,7 +53,7 @@ public: bool Enable(); // Enable 3D scanner extension. Returns true when done bool IsRegistered() const; // Is the 3D scanner registered and ready to use? - bool Register(); // Register a 3D scanner. Returns true when done + void Register(); // Register a 3D scanner instance // External scanners are automatically unregistered when the main port (USB) is closed // Start a new 3D scan. Returns true when the scan has been initiated diff --git a/src/Storage/FileInfoParser.cpp b/src/Storage/FileInfoParser.cpp index 4dfc90cf..0e2d4173 100644 --- a/src/Storage/FileInfoParser.cpp +++ b/src/Storage/FileInfoParser.cpp @@ -32,7 +32,7 @@ FileInfoParser::FileInfoParser() : parseState(notParsing), fileBeingParsed(nullptr), accumulatedParseTime(0), accumulatedReadTime(0), accumulatedSeekTime(0), fileOverlapLength(0) { parsedFileInfo.Init(); - parserMutex.Create(); + parserMutex.Create("FileInfoParser"); } bool FileInfoParser::GetFileInfo(const char *directory, const char *fileName, GCodeFileInfo& info, bool quitEarly) diff --git a/src/Storage/MassStorage.cpp b/src/Storage/MassStorage.cpp index e06946a1..c13bb8ef 100644 --- a/src/Storage/MassStorage.cpp +++ b/src/Storage/MassStorage.cpp @@ -63,9 +63,12 @@ MassStorage::MassStorage(Platform* p) : freeWriteBuffers(nullptr) void MassStorage::Init() { + static const char * const VolMutexNames[] = { "SD0", "SD1" }; + static_assert(ARRAY_SIZE(VolMutexNames) >= NumSdCards, "Incorrect VolMutexNames array"); + // Create the mutexes - fsMutex.Create(); - dirMutex.Create(); + fsMutex.Create("FileSystem"); + dirMutex.Create("DirSearch"); for (size_t i = 0; i < NumFileWriteBuffers; ++i) { @@ -79,7 +82,7 @@ void MassStorage::Init() inf.mounting = inf.isMounted = false; inf.cdPin = SdCardDetectPins[card]; inf.cardState = (inf.cdPin == NoPin) ? CardDetectState::present : CardDetectState::notPresent; - inf.volMutex.Create(); + inf.volMutex.Create(VolMutexNames[card]); } sd_mmc_init(SdWriteProtectPins, SdSpiCSPins); // initialize SD MMC stack diff --git a/src/Tasks.cpp b/src/Tasks.cpp index dc9169d6..a3a4c782 100644 --- a/src/Tasks.cpp +++ b/src/Tasks.cpp @@ -91,8 +91,8 @@ extern "C" void AppMain() extern "C" void MainTask(void *pvParameters) { - mallocMutex.Create(); - spiMutex.Create(); + mallocMutex.Create("Malloc"); + spiMutex.Create("SPI"); #endif reprap.Init(); for (;;) @@ -188,6 +188,7 @@ namespace Tasks } #ifdef RTOS + p.Message(mtype, "Tasks:"); for (const TaskBase *t = TaskBase::GetTaskList(); t != nullptr; t = t->GetNext()) { TaskStatus_t taskDetails; @@ -197,9 +198,28 @@ namespace Tasks : (taskDetails.eCurrentState == eBlocked) ? "blocked" : (taskDetails.eCurrentState == eSuspended) ? "suspended" : "invalid"; - p.MessageF(mtype, "Task %s %s, free stack %u\n", + p.MessageF(mtype, " %s(%s,%u)", taskDetails.pcTaskName, stateText, (unsigned int)(taskDetails.usStackHighWaterMark * sizeof(StackType_t))); } + p.Message(mtype, "\nMutexes:"); + + 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 + { + vTaskGetInfo(holder, &taskDetails, pdTRUE, eInvalid); + holderText = taskDetails.pcTaskName; + } + p.MessageF(mtype, " %s(%s)", m->GetName(), holderText); + } + p.MessageF(mtype, "\n"); #endif } @@ -216,7 +236,7 @@ namespace Tasks #ifdef RTOS static StaticTask_t xIdleTaskTCB; -static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ]; +static StackType_t uxIdleTaskStack[configMINIMAL_STACK_SIZE]; extern "C" void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, @@ -233,7 +253,7 @@ extern "C" void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuff } static StaticTask_t xTimerTaskTCB; -static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ]; +static StackType_t uxTimerTaskStack[configTIMER_TASK_STACK_DEPTH]; extern "C" void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, diff --git a/src/Version.h b/src/Version.h index 6c5ebe24..b2e0b33c 100644 --- a/src/Version.h +++ b/src/Version.h @@ -12,9 +12,9 @@ #ifndef VERSION #ifdef RTOS # define RTOSVER "(RTOS)" -# define MAIN_VERSION "2.0" +# define MAIN_VERSION "2.01beta1" #else -# define MAIN_VERSION "1.21.1" +# define MAIN_VERSION "1.21.2beta1" # define RTOSVER #endif @@ -22,7 +22,7 @@ #endif #ifndef DATE -# define DATE "2018-06-05b3" +# define DATE "2018-06-23b1" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman" |