diff options
37 files changed, 1699 insertions, 733 deletions
@@ -110,7 +110,7 @@ </toolChain> </folderInfo> <sourceEntries> - <entry excluding="src/SAME70_TEST|src/Network2|src/Alligator|src/Duet/Lwip/lwip/src/core/ipv6|src/Duet/Lwip/lwip/test|src/DuetNG|src/DuetM|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> + <entry excluding="src/Network2|src/Alligator|src/Duet/Lwip/lwip/src/core/ipv6|src/Duet/Lwip/lwip/test|src/SAME70_TEST|src/DuetNG|src/DuetM|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> </sourceEntries> </configuration> </storageModule> @@ -228,7 +228,7 @@ </toolChain> </folderInfo> <sourceEntries> - <entry excluding="src/SAME70_TEST|src/Network2/W5500|src/Alligator|src/Duet|src/DuetM|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> + <entry excluding="src/Network2/W5500|src/Alligator|src/SAME70_TEST|src/Duet|src/DuetM|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> </sourceEntries> </configuration> </storageModule> @@ -338,7 +338,7 @@ </toolChain> </folderInfo> <sourceEntries> - <entry excluding="src/SAME70_TEST|src/Network2|src/Alligator|src/Duet|src/DuetNG|src/DuetM" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> + <entry excluding="src/Network2|src/Alligator|src/SAME70_TEST|src/Duet|src/DuetNG|src/DuetM" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> </sourceEntries> </configuration> </storageModule> @@ -461,7 +461,7 @@ </toolChain> </folderInfo> <sourceEntries> - <entry excluding="src/SAME70_TEST|src/Alligator|src/Duet|src/DuetM|src/Network2/ESP8266|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> + <entry excluding="src/Alligator|src/SAME70_TEST|src/Duet|src/DuetM|src/Network2/ESP8266|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> </sourceEntries> </configuration> </storageModule> @@ -582,7 +582,7 @@ </toolChain> </folderInfo> <sourceEntries> - <entry excluding="src/SAME70_TEST|src/Network2|src/Duet/Lwip/lwip/src/core/ipv6|src/Duet/Lwip/lwip/test|src/DuetNG|src/DuetM|src/Alligator/Lwip/lwip/src/include/ipv6|src/Alligator/Lwip/lwip/test|src/Duet/MCP4461|src/Alligator/Lwip/lwip/src/core/ipv6|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> + <entry excluding="src/Network2|src/Duet/Lwip/lwip/src/core/ipv6|src/Duet/Lwip/lwip/test|src/SAME70_TEST|src/DuetNG|src/DuetM|src/Alligator/Lwip/lwip/src/include/ipv6|src/Alligator/Lwip/lwip/test|src/Duet/MCP4461|src/Alligator/Lwip/lwip/src/core/ipv6|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> </sourceEntries> </configuration> </storageModule> @@ -703,7 +703,7 @@ </toolChain> </folderInfo> <sourceEntries> - <entry excluding="src/SAME70_TEST|src/DuetNG/DuetEthernet/Wiznet/Internet/TFTP|src/DuetNG/DuetEthernet/Ethernet3|src/DuetNG/DuetWiFi|src/DuetNG/DuetEthernet/Wiznet/Internet/FTPServer|src/Duet|src/NetworkW5500/Wiznet/Internet/SNMP|src/DuetNG/DuetEthernet/Wiznet/Internet/SNTP|src/DuetNG/DuetEthernet/Ethernet3/examples|src/DuetNG|src/DuetNG/DuetEthernet/Wiznet/Internet/DNS|src/NetworkW5500/Wiznet/Internet/TFTP|src/DuetNG/DuetEthernet/Wiznet/Application|src/DuetNG/DuetEthernet/Wiznet/Internet/MQTT|src/Alligator|src/DuetNG/DuetEthernet/Wiznet/Internet/FTPClient|src/DuetNG/DuetEthernet/Wiznet/Internet/httpServer|src/NetworkW5500/Wiznet/Internet/SNTP|src/NetworkW5500/Wiznet/Internet/DNS|src/DuetNG/DuetEthernet/Wiznet/Internet/SNMP|src/NetworkESP8266|src/Network2/ESP8266|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> + <entry excluding="src/DuetNG/DuetEthernet/Wiznet/Internet/TFTP|src/DuetNG/DuetEthernet/Ethernet3|src/DuetNG/DuetWiFi|src/DuetNG/DuetEthernet/Wiznet/Internet/FTPServer|src/Duet|src/NetworkW5500/Wiznet/Internet/SNMP|src/DuetNG/DuetEthernet/Wiznet/Internet/SNTP|src/DuetNG/DuetEthernet/Ethernet3/examples|src/DuetNG|src/DuetNG/DuetEthernet/Wiznet/Internet/DNS|src/NetworkW5500/Wiznet/Internet/TFTP|src/DuetNG/DuetEthernet/Wiznet/Application|src/DuetNG/DuetEthernet/Wiznet/Internet/MQTT|src/Alligator|src/DuetNG/DuetEthernet/Wiznet/Internet/FTPClient|src/SAME70_TEST|src/DuetNG/DuetEthernet/Wiznet/Internet/httpServer|src/NetworkW5500/Wiznet/Internet/SNTP|src/NetworkW5500/Wiznet/Internet/DNS|src/DuetNG/DuetEthernet/Wiznet/Internet/SNMP|src/NetworkESP8266|src/Network2/ESP8266|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> </sourceEntries> </configuration> </storageModule> @@ -847,7 +847,7 @@ </toolChain> </folderInfo> <sourceEntries> - <entry excluding="src/DuetM|src/Network2/W5500|src/Network2|src/DuetNG/DuetWiFi|src/Duet|src/DuetNG/DuetEthernet/Wiznet/Internet/SNTP|src/DuetNG|src/DuetNG/DuetEthernet/Wiznet/Internet/DNS|src/DuetNG/DuetEthernet/Wiznet/Application|src/DuetNG/DuetEthernet/Wiznet/Internet/MQTT|src/Alligator|src/SAME70_TEST/Lwip/src/apps/mqtt|src/DuetNG/DuetEthernet/Wiznet/Internet/TFTP|src/DuetNG/DuetEthernet/Ethernet3|src/SAME70_TEST/Lwip/src/apps/snmp|src/SAME70_TEST/Lwip/src/apps/httpd|src/DuetNG/DuetEthernet/Wiznet/Internet/FTPServer|src/SAME70_TEST/Lwip/src/apps/tftp|src/DuetNG/DuetEthernet/Ethernet3/examples|src/SAME70_TEST/Lwip/test|src/SAME70_TEST/Lwip/src/netif/ppp|src/SAME70_TEST/Lwip/src/apps/lwiperf|src/SAME70_TEST/Lwip/src/apps/sntp|src/DuetNG/DuetEthernet/Wiznet/Internet/FTPClient|src/DuetNG/DuetEthernet/Wiznet/Internet/httpServer|src/DuetNG/DuetEthernet/Wiznet/Internet/SNMP|src/Network2/ESP8266|src/RADDS|src/SAME70_TEST/Lwip/doc" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> + <entry excluding="src/Network2/W5500|src/Network2|src/SAME70_TEST/Lwip/src/netif/ppp|src/DuetNG/DuetWiFi|src/Duet|src/SAME70_TEST/Lwip/doc|src/DuetNG/DuetEthernet/Wiznet/Internet/SNTP|src/DuetNG|src/DuetNG/DuetEthernet/Wiznet/Internet/DNS|src/DuetNG/DuetEthernet/Wiznet/Application|src/DuetNG/DuetEthernet/Wiznet/Internet/MQTT|src/Alligator|src/SAME70_TEST/Lwip/test|src/SAME70_TEST/Lwip/src/apps/snmp|src/SAME70_TEST/Lwip/src/apps/httpd|src/SAME70_TEST/Lwip/src/apps/tftp|src/DuetNG/DuetEthernet/Wiznet/Internet/TFTP|src/DuetNG/DuetEthernet/Ethernet3|src/SAME70_TEST/Lwip/src/apps/lwiperf|src/SAME70_TEST/Lwip/src/apps/sntp|src/DuetNG/DuetEthernet/Wiznet/Internet/FTPServer|src/DuetNG/DuetEthernet/Ethernet3/examples|src/SAME70_TEST/Lwip/src/apps/mqtt|src/DuetNG/DuetEthernet/Wiznet/Internet/FTPClient|src/DuetNG/DuetEthernet/Wiznet/Internet/httpServer|src/DuetNG/DuetEthernet/Wiznet/Internet/SNMP|src/DuetM|src/Network2/ESP8266|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> </sourceEntries> </configuration> </storageModule> diff --git a/src/Fan.cpp b/src/Fan.cpp index d704abbe..efd73200 100644 --- a/src/Fan.cpp +++ b/src/Fan.cpp @@ -97,7 +97,7 @@ bool Fan::Configure(unsigned int mcode, int fanNum, GCodeBuffer& gb, StringRef& seen = true; int32_t heaters[Heaters + MaxVirtualHeaters]; // signed because we use H-1 to disable thermostatic mode size_t numH = ARRAY_SIZE(heaters); - gb.GetIntArray(heaters, numH); + gb.GetIntArray(heaters, numH, false); // Note that M106 H-1 disables thermostatic mode. The following code implements that automatically. heatersMonitored = 0; diff --git a/src/FilamentMonitors/Duet3DFilamentMonitor.cpp b/src/FilamentMonitors/Duet3DFilamentMonitor.cpp new file mode 100644 index 00000000..051fefa1 --- /dev/null +++ b/src/FilamentMonitors/Duet3DFilamentMonitor.cpp @@ -0,0 +1,207 @@ +/* + * Duet3DFilamentSensor.cpp + * + * Created on: 20 Jul 2017 + * Author: David + */ + +#include "Duet3DFilamentMonitor.h" +#include "GCodes/GCodeBuffer.h" +#include "Platform.h" +#include "Movement/DDA.h" // for stepClockRate + +// Constructors +Duet3DFilamentMonitor::Duet3DFilamentMonitor(int type) + : FilamentMonitor(type) +{ + InitReceiveBuffer(); +} + +void Duet3DFilamentMonitor::InitReceiveBuffer() +{ + edgeCaptureReadPointer = edgeCaptureWritePointer = 1; + edgeCaptures[0] = Platform::GetInterruptClocks(); // pretend we just had a high-to-low transition + state = RxdState::waitingForStartBit; +} + +// ISR for when the pin state changes +void Duet3DFilamentMonitor::Interrupt() +{ + uint32_t now = Platform::GetInterruptClocks(); + const size_t writePointer = edgeCaptureWritePointer; // capture volatile variable + if ((writePointer + 1) % EdgeCaptureBufferSize != edgeCaptureReadPointer) + { + if (IoPort::ReadPin(GetPin())) + { + if ((writePointer & 1) == 0) // low-to-high transitions should occur on odd indices + { + return; + } + } + else + { + if ((writePointer & 1) != 0) // high-to-low transitions should occur on even indices + { + return; + } + now -= 40; // partial correction for skew caused by debounce filter on Duet endstop inputs (measured skew = 74) + } + } + + edgeCaptures[writePointer] = now; // record the time at which this edge was detected + edgeCaptureWritePointer = (writePointer + 1) % EdgeCaptureBufferSize; +} + +// Call the following regularly to keep the status up to date +void Duet3DFilamentMonitor::PollReceiveBuffer() +{ + // For the Duet3D sensors we need to decode the received data from the transition times recorded in the edgeCaptures array + static constexpr uint32_t BitsPerSecond = 1000; // the nominal bit rate that the data is transmitted at + static constexpr uint32_t NominalBitLength = DDA::stepClockRate/BitsPerSecond; // the nominal bit length in step clocks + static constexpr uint32_t MinBitLength = (NominalBitLength * 10)/13; // allow 30% clock speed tolerance + static constexpr uint32_t MaxBitLength = (NominalBitLength * 13)/10; // allow 30% clock speed tolerance + static constexpr uint32_t ErrorRecoveryDelayBits = 8; // before a start bit we want the line to be low for this long + static constexpr uint32_t ErrorRecoveryTime = NominalBitLength * ErrorRecoveryDelayBits; + + bool again; + do + { + again = false; + const size_t writePointer = edgeCaptureWritePointer; // capture volatile variable + const uint32_t now = Platform::GetInterruptClocks(); + switch (state) + { + case RxdState::waitingForStartBit: + if (writePointer != edgeCaptureReadPointer) // if we have recorded any new edges + { + if ((edgeCaptureReadPointer & 1u) == 0) // if we are out of sync (this is normal when the last stuffing bit was a 1) + { + edgeCaptureReadPointer = (edgeCaptureReadPointer + 1u) % EdgeCaptureBufferSize; + again = true; + } + else + { + if (edgeCaptures[edgeCaptureReadPointer] - edgeCaptures[(edgeCaptureReadPointer - 1) % EdgeCaptureBufferSize] < ErrorRecoveryTime) + { + // The input line has not been idle for long enough before the start bit + edgeCaptureReadPointer = (edgeCaptureReadPointer + 1u) % EdgeCaptureBufferSize; // ignore this start bit + state = RxdState::errorRecovery1; + again = true; + } + else + { + OnStartBitReceived(); + state = RxdState::waitingForEndOfStartBit; + again = true; + } + } + } + break; + + case RxdState::waitingForEndOfStartBit: + // This state must time out because while we are in it, comparison of filament extruded is suspended + if ((writePointer - edgeCaptureReadPointer) % EdgeCaptureBufferSize >= 2) + { + // Check for a valid start bit + lastBitChangeIndex = (edgeCaptureReadPointer + 1u) % EdgeCaptureBufferSize; + startBitLength = edgeCaptures[lastBitChangeIndex] - edgeCaptures[edgeCaptureReadPointer]; + edgeCaptureReadPointer = lastBitChangeIndex; + if (startBitLength >= MinBitLength && startBitLength <= MaxBitLength) + { + valueBeingAssembled = 0; + nibblesAssembled = 0; + state = RxdState::waitingForNibble; + again = true; + //debugPrintf("sb %u\n", startBitLength); + } + else + { + // Start bit too long or too short + state = RxdState::errorRecovery2; + again = true; + } + } + else if (now - edgeCaptures[edgeCaptureReadPointer] > MaxBitLength) // check for timeout + { + edgeCaptureReadPointer = (edgeCaptureReadPointer + 1u) % EdgeCaptureBufferSize; + state = RxdState::errorRecovery2; + again = true; + } + break; + + case RxdState::waitingForNibble: + // This state must time out because while we are in it, comparison of filament extruded is suspended + { + const uint32_t nibbleStartTime = edgeCaptures[lastBitChangeIndex]; + if (now - nibbleStartTime > (13 * startBitLength)/2) + { + // 6.5 bit times have passed since the start of the bit that preceded the current nibble, so we should have a complete nibble and the following stuffing bit + uint32_t samplePoint = (startBitLength * 3)/2; // sampling time after the end of the start bit for bit 7 (MSB) + uint8_t currentNibble = 0; + size_t nextBitChangeIndex = (lastBitChangeIndex + 1u) % EdgeCaptureBufferSize; + for (uint8_t numBits = 0; numBits < 5; ++numBits) + { + if (nextBitChangeIndex != edgeCaptureWritePointer && edgeCaptures[nextBitChangeIndex] - nibbleStartTime < samplePoint) + { + lastBitChangeIndex = nextBitChangeIndex; + nextBitChangeIndex = (lastBitChangeIndex + 1u) % EdgeCaptureBufferSize; + if (nextBitChangeIndex != writePointer && edgeCaptures[nextBitChangeIndex] - nibbleStartTime < samplePoint) + { + edgeCaptureReadPointer = nextBitChangeIndex; + state = RxdState::errorRecovery3; + again = true; + break; + } + } + currentNibble <<= 1; + currentNibble |= (uint8_t)(lastBitChangeIndex & 1u); + samplePoint += startBitLength; + } + + if (state != RxdState::waitingForNibble) + { + break; + } + + // The 5th bit we received should be the inverse of the 4th bit + if ((((currentNibble >> 1u) ^ currentNibble) & 0x01u) == 0) + { + edgeCaptureReadPointer = nextBitChangeIndex; + state = RxdState::errorRecovery4; + again = true; + break; + } + + currentNibble >>= 1; + valueBeingAssembled = (valueBeingAssembled << 4) | currentNibble; + ++nibblesAssembled; + if (nibblesAssembled == 4) // if we have a complete 16-bit word + { + edgeCaptureReadPointer = nextBitChangeIndex; // ready for a new word + ProcessReceivedWord(valueBeingAssembled); + state = RxdState::waitingForStartBit; + } + again = true; + } + } + break; + + default: // error recovery states + if (reprap.Debug(moduleFilamentSensors)) + { + debugPrintf("Fil err %u\n", (unsigned int)state); + } + state = RxdState::waitingForStartBit; + again = true; + break; + } + } while (again); +} + +// Return true if we are on the process of receiving data form the filament monitor +bool Duet3DFilamentMonitor::IsReceiving() const +{ + return state == RxdState::waitingForEndOfStartBit || state == RxdState::waitingForNibble; +} + +// End diff --git a/src/FilamentMonitors/Duet3DFilamentMonitor.h b/src/FilamentMonitors/Duet3DFilamentMonitor.h new file mode 100644 index 00000000..837093e4 --- /dev/null +++ b/src/FilamentMonitors/Duet3DFilamentMonitor.h @@ -0,0 +1,56 @@ +/* + * Duet3DFilamentSensor.h + * + * Created on: 20 Jul 2017 + * Author: David + * + * This is the base class for filament monitors that use the Duet3D protocol for sending 16-bit words to the Duet. + */ + +#ifndef SRC_FILAMENTSENSORS_DUET3DFILAMENTMONITOR_H_ +#define SRC_FILAMENTSENSORS_DUET3DFILAMENTMONITOR_H_ + +#include "FilamentMonitor.h" + +class Duet3DFilamentMonitor : public FilamentMonitor +{ +public: + Duet3DFilamentMonitor(int type); + + void Interrupt() override; + +protected: + virtual void OnStartBitReceived() = 0; + virtual void ProcessReceivedWord(uint16_t val) = 0; + void InitReceiveBuffer(); + void PollReceiveBuffer(); + bool IsReceiving() const; + +private: + static constexpr size_t EdgeCaptureBufferSize = 64; // must be a power of 2 + + // Buffer used to capture received data, and associated info + uint32_t edgeCaptures[EdgeCaptureBufferSize]; + size_t edgeCaptureReadPointer; + volatile size_t edgeCaptureWritePointer; + + enum class RxdState : uint8_t + { + waitingForStartBit, + waitingForEndOfStartBit, + waitingForNibble, + errorRecovery1, + errorRecovery2, + errorRecovery3, + errorRecovery4 + }; + + RxdState state; + uint32_t startBitLength; + uint32_t errorRecoveryStartTime; + size_t lastBitChangeIndex; + uint16_t valueBeingAssembled; + uint8_t nibblesAssembled; +}; + +#endif /* SRC_FILAMENTSENSORS_DUET3DFILAMENTMONITOR_H_ */ diff --git a/src/FilamentSensors/FilamentSensor.cpp b/src/FilamentMonitors/FilamentMonitor.cpp index b7a9c9a9..4d370bea 100644 --- a/src/FilamentSensors/FilamentSensor.cpp +++ b/src/FilamentMonitors/FilamentMonitor.cpp @@ -5,9 +5,11 @@ * Author: David */ -#include "FilamentSensor.h" -#include "SimpleFilamentSensor.h" -#include "Duet3DFilamentSensor.h" +#include "FilamentMonitor.h" +#include "SimpleFilamentMonitor.h" +#include "RotatingMagnetFilamentMonitor.h" +#include "LaserFilamentMonitor.h" +#include "PulsedFilamentMonitor.h" #include "RepRap.h" #include "Platform.h" #include "GCodes/GCodeBuffer.h" @@ -15,10 +17,10 @@ #include "PrintMonitor.h" // Static data -FilamentSensor *FilamentSensor::filamentSensors[MaxExtruders] = { 0 }; +FilamentMonitor *FilamentMonitor::filamentSensors[MaxExtruders] = { 0 }; // Default destructor -FilamentSensor::~FilamentSensor() +FilamentMonitor::~FilamentMonitor() { if (pin != NoPin) { @@ -28,7 +30,7 @@ FilamentSensor::~FilamentSensor() // Try to get the pin number from the GCode command in the buffer, setting Seen if a pin number was provided and returning true if error. // Also attaches the ISR. -bool FilamentSensor::ConfigurePin(GCodeBuffer& gb, StringRef& reply, bool& seen) +bool FilamentMonitor::ConfigurePin(GCodeBuffer& gb, StringRef& reply, uint32_t interruptMode, bool& seen) { if (gb.Seen('C')) { @@ -43,7 +45,7 @@ bool FilamentSensor::ConfigurePin(GCodeBuffer& gb, StringRef& reply, bool& seen) } endstopNumber = endstop; pin = p; - attachInterrupt(pin, InterruptEntry, CHANGE, this); + attachInterrupt(pin, InterruptEntry, interruptMode, this); } else if (seen) { @@ -55,18 +57,25 @@ bool FilamentSensor::ConfigurePin(GCodeBuffer& gb, StringRef& reply, bool& seen) } // Factory function -/*static*/ FilamentSensor *FilamentSensor::Create(int type) +/*static*/ FilamentMonitor *FilamentMonitor::Create(int type) { switch (type) { case 1: // active high switch case 2: // active low switch - return new SimpleFilamentSensor(type); + return new SimpleFilamentMonitor(type); break; - case 3: // duet3d, no switch - case 4: // duet3d + switch - return new Duet3DFilamentSensor(type); + case 3: // duet3d rotating magnet, no switch + case 4: // duet3d rotating magnet + switch + return new RotatingMagnetFilamentMonitor(type); + + case 5: // duet3d laser, no switch + case 6: // duet3d laser + switch + return new LaserFilamentMonitor(type); + + case 7: // simple pulse output sensor + return new PulsedFilamentMonitor(type); break; default: // no sensor, or unknown sensor @@ -75,7 +84,7 @@ bool FilamentSensor::ConfigurePin(GCodeBuffer& gb, StringRef& reply, bool& seen) } // Return an error message corresponding to a status code -/*static*/ const char *FilamentSensor::GetErrorMessage(FilamentSensorStatus f) +/*static*/ const char *FilamentMonitor::GetErrorMessage(FilamentSensorStatus f) { switch(f) { @@ -89,12 +98,12 @@ bool FilamentSensor::ConfigurePin(GCodeBuffer& gb, StringRef& reply, bool& seen) } // ISR -/*static*/ void FilamentSensor::InterruptEntry(CallbackParameter param) +/*static*/ void FilamentMonitor::InterruptEntry(CallbackParameter param) { - static_cast<FilamentSensor*>(param.vp)->Interrupt(); + static_cast<FilamentMonitor*>(param.vp)->Interrupt(); } -/*static*/ void FilamentSensor::Spin(bool full) +/*static*/ void FilamentMonitor::Spin(bool full) { // Filament sensors for (size_t extruder = 0; extruder < MaxExtruders; ++extruder) @@ -102,16 +111,17 @@ bool FilamentSensor::ConfigurePin(GCodeBuffer& gb, StringRef& reply, bool& seen) if (filamentSensors[extruder] != nullptr) { GCodes& gCodes = reprap.GetGCodes(); - const float extrusionCommanded = (float)reprap.GetMove().GetAccumulatedExtrusion(extruder)/reprap.GetPlatform().DriveStepsPerUnit(extruder + gCodes.GetTotalAxes()); - // get and clear the Move extrusion commanded + bool wasNonPrinting; + const int32_t extruderStepsCommanded = reprap.GetMove().GetAccumulatedExtrusion(extruder, wasNonPrinting); // get and clear the net extrusion commanded if (gCodes.IsReallyPrinting() && !gCodes.IsSimulating()) { - const FilamentSensorStatus fstat = filamentSensors[extruder]->Check(full, extrusionCommanded); + const float extrusionCommanded = (float)extruderStepsCommanded/reprap.GetPlatform().DriveStepsPerUnit(extruder + gCodes.GetTotalAxes()); + const FilamentSensorStatus fstat = filamentSensors[extruder]->Check(full, wasNonPrinting, extrusionCommanded); if (full && fstat != FilamentSensorStatus::ok && extrusionCommanded > 0.0) { if (reprap.Debug(moduleFilamentSensors)) { - debugPrintf("Filament error: extruder %u reports %s\n", extruder, FilamentSensor::GetErrorMessage(fstat)); + debugPrintf("Filament error: extruder %u reports %s\n", extruder, FilamentMonitor::GetErrorMessage(fstat)); } else { @@ -128,17 +138,17 @@ bool FilamentSensor::ConfigurePin(GCodeBuffer& gb, StringRef& reply, bool& seen) } // Return the filament sensor associated with a particular extruder -/*static*/ FilamentSensor *FilamentSensor::GetFilamentSensor(unsigned int extruder) +/*static*/ FilamentMonitor *FilamentMonitor::GetFilamentSensor(unsigned int extruder) { return (extruder < MaxExtruders) ? filamentSensors[extruder] : nullptr; } // Set the filament sensor associated with a particular extruder -/*static*/ bool FilamentSensor::SetFilamentSensorType(unsigned int extruder, int newSensorType) +/*static*/ bool FilamentMonitor::SetFilamentSensorType(unsigned int extruder, int newSensorType) { if (extruder < MaxExtruders) { - FilamentSensor*& sensor = filamentSensors[extruder]; + FilamentMonitor*& sensor = filamentSensors[extruder]; const int oldSensorType = (sensor == nullptr) ? 0 : sensor->GetType(); if (newSensorType != oldSensorType) { @@ -152,7 +162,7 @@ bool FilamentSensor::ConfigurePin(GCodeBuffer& gb, StringRef& reply, bool& seen) } // Send diagnostics info -/*static*/ void FilamentSensor::Diagnostics(MessageType mtype) +/*static*/ void FilamentMonitor::Diagnostics(MessageType mtype) { bool first = true; for (size_t i = 0; i < MaxExtruders; ++i) diff --git a/src/FilamentSensors/FilamentSensor.h b/src/FilamentMonitors/FilamentMonitor.h index 2483cec3..373295d5 100644 --- a/src/FilamentSensors/FilamentSensor.h +++ b/src/FilamentMonitors/FilamentMonitor.h @@ -5,8 +5,8 @@ * Author: David */ -#ifndef SRC_FILAMENTSENSORS_FILAMENTSENSOR_H_ -#define SRC_FILAMENTSENSORS_FILAMENTSENSOR_H_ +#ifndef SRC_FILAMENTSENSORS_FILAMENTMONITOR_H_ +#define SRC_FILAMENTSENSORS_FILAMENTMONITOR_H_ #include "RepRapFirmware.h" #include "MessageType.h" @@ -20,7 +20,7 @@ enum class FilamentSensorStatus : uint8_t sensorError }; -class FilamentSensor +class FilamentMonitor { public: // Configure this sensor, returning true if error and setting 'seen' if we processed any configuration parameters @@ -28,7 +28,7 @@ public: // Call the following at intervals to check the status. This is only called when extrusion is in progress or imminent. // 'filamentConsumed' is the net amount of extrusion since the last call to this function. - virtual FilamentSensorStatus Check(bool full, float filamentConsumed) = 0; + virtual FilamentSensorStatus Check(bool full, bool hadNonPrintingMove, float filamentConsumed) = 0; // Clear the measurement state - called when we are not printing a file. Return the present/not present status if available. virtual FilamentSensorStatus Clear(bool full) = 0; @@ -40,7 +40,7 @@ public: virtual void Interrupt() = 0; // Override the virtual destructor if your derived class allocates any dynamic memory - virtual ~FilamentSensor(); + virtual ~FilamentMonitor(); // Return the type of this sensor int GetType() const { return type; } @@ -52,7 +52,7 @@ public: static void Spin(bool full); // Return the filament sensor associated with a particular extruder - static FilamentSensor *GetFilamentSensor(unsigned int extruder); + static FilamentMonitor *GetFilamentSensor(unsigned int extruder); // Set the filament sensor associated with a particular extruder static bool SetFilamentSensorType(unsigned int extruder, int newSensorType); @@ -61,9 +61,9 @@ public: static void Diagnostics(MessageType mtype); protected: - FilamentSensor(int t) : type(t), pin(NoPin) { } + FilamentMonitor(int t) : type(t), pin(NoPin) { } - bool ConfigurePin(GCodeBuffer& gb, StringRef& reply, bool& seen); + bool ConfigurePin(GCodeBuffer& gb, StringRef& reply, uint32_t interruptMode, bool& seen); int GetEndstopNumber() const { return endstopNumber; } @@ -71,15 +71,15 @@ protected: private: // Create a filament sensor returning null if not a valid sensor type - static FilamentSensor *Create(int type); + static FilamentMonitor *Create(int type); static void InterruptEntry(CallbackParameter param); - static FilamentSensor *filamentSensors[MaxExtruders]; + static FilamentMonitor *filamentSensors[MaxExtruders]; int type; int endstopNumber; Pin pin; }; -#endif /* SRC_FILAMENTSENSORS_FILAMENTSENSOR_H_ */ +#endif /* SRC_FILAMENTSENSORS_FILAMENTMONITOR_H_ */ diff --git a/src/FilamentMonitors/LaserFilamentMonitor.cpp b/src/FilamentMonitors/LaserFilamentMonitor.cpp new file mode 100644 index 00000000..cd23311d --- /dev/null +++ b/src/FilamentMonitors/LaserFilamentMonitor.cpp @@ -0,0 +1,356 @@ +/* + * LaserFilamentMonitor.cpp + * + * Created on: 9 Jan 2018 + * Author: David + */ + +#include "LaserFilamentMonitor.h" +#include "GCodes/GCodeBuffer.h" +#include "Platform.h" +#include "RepRap.h" + +LaserFilamentMonitor::LaserFilamentMonitor(int type) + : Duet3DFilamentMonitor(type), + minMovementAllowed(DefaultMinMovementAllowed), maxMovementAllowed(DefaultMaxMovementAllowed), + minimumExtrusionCheckLength(DefaultMinimumExtrusionCheckLength), comparisonEnabled(false) +{ + switchOpenMask = (type == 6) ? TypeLaserSwitchOpenMask : 0; + Init(); +} + +void LaserFilamentMonitor::Init() +{ + sensorValue = 0; + parityErrorCount = 0; + lastMeasurementTime = 0; + backwards = false; + InitReceiveBuffer(); + Reset(); +} + +void LaserFilamentMonitor::Reset() +{ + extrusionCommanded = movementMeasured = extrusionCommandedAtLastMeasurement = extrusionCommandedAtStartBit = movementMeasuredAtLastCheck = 0.0; + samplesReceived = 0; + laserMonitorState = LaserMonitorState::idle; +} + +// Configure this sensor, returning true if error and setting 'seen' if we processed any configuration parameters +bool LaserFilamentMonitor::Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) +{ + if (ConfigurePin(gb, reply, CHANGE, seen)) + { + return true; + } + + gb.TryGetFValue('E', minimumExtrusionCheckLength, seen); + + if (gb.Seen('R')) + { + seen = true; + size_t numValues = 2; + uint32_t minMax[2]; + gb.GetUnsignedArray(minMax, numValues, false); + if (numValues > 0) + { + minMovementAllowed = (float)minMax[0] * 0.01; + } + if (numValues > 1) + { + maxMovementAllowed = (float)minMax[1] * 0.01; + } + } + + if (gb.Seen('S')) + { + seen = true; + comparisonEnabled = (gb.GetIValue() > 0); + } + + if (seen) + { + Init(); + } + else + { + reply.printf("Duet3D laser filament monitor%s on endstop input %u, %s, allowed movement %ld%% to %ld%%, check every %.1fmm, ", + (switchOpenMask != 0) ? " with microswitch" : "", + GetEndstopNumber(), + (comparisonEnabled) ? "enabled" : "disabled", + lrintf(minMovementAllowed * 100.0), + lrintf(maxMovementAllowed * 100.0), + (double)minimumExtrusionCheckLength); + + if (!dataReceived) + { + reply.cat("no data received"); + } + else if ((sensorValue & TypeLaserErrorMask) != 0) + { + reply.cat("error"); + } + else + { + reply.catf("current position %.1f, brightness %u, shutter %u, ", (double)GetCurrentPosition(), qualityWord & 0x00FF, (qualityWord >> 8) & 0x3F); + if (laserMonitorState != LaserMonitorState::calibrating && totalExtrusionCommanded > 10.0) + { + reply.catf("measured minimum %ld%%, average %ld%%, maximum %ld%% over %.1fmm", + lrintf(100 * minMovementRatio), + lrintf((100 * totalMovementMeasured)/totalExtrusionCommanded), + lrintf(100 * maxMovementRatio), + (double)totalExtrusionCommanded); + } + else + { + reply.cat("no calibration data"); + } + } + } + + return false; +} + +// This is called from the poll function when we receive what could be a start bit +void LaserFilamentMonitor::OnStartBitReceived() /*override*/ +{ + extrusionCommandedAtStartBit = extrusionCommanded; // record the extrusion +} + +// This is called from the poll function when we have received a complete word of data +void LaserFilamentMonitor::ProcessReceivedWord(uint16_t val) /*override*/ +{ + // Check the parity + uint8_t data8 = (uint8_t)((val >> 8) ^ val); + data8 ^= (data8 >> 4); + data8 ^= (data8 >> 2); + data8 ^= (data8 >> 1); + if ((data8 & 1) != 0) + { + ++parityErrorCount; + return; // parity error, so ignore the data + } + + // Check whether this is a quality report + if ((val & TypeLaserQualityMask) != 0) + { + qualityWord = val & ~(TypeLaserQualityMask | TypeLaserParityMask); // record the quality report + return; + } + + // We received a position report + if (samplesReceived == 0) + { + dataReceived = true; + extrusionCommanded -= extrusionCommandedAtStartBit; + extrusionCommandedAtStartBit = 0; + movementMeasured = 0.0; // use the first measurement sample as a baseline + } + else + { + const uint16_t positionChange = (val - sensorValue) & TypeLaserPositionMask; // position change in range 0..1023 + const int32_t movement = (positionChange <= 512) ? (int32_t)positionChange : (int32_t)positionChange - 1024; + movementMeasured += (float)movement * 0.02; + } + + lastMeasurementTime = millis(); + extrusionCommandedAtLastMeasurement = extrusionCommandedAtStartBit; + sensorValue = val; + if (samplesReceived < 100) + { + ++samplesReceived; + } +} + +// Return the current wheel angle +float LaserFilamentMonitor::GetCurrentPosition() const +{ + int32_t pos = (int32_t)(sensorValue & TypeLaserPositionMask); + if (pos > 512) + { + pos -= 1024; + } + return (float)pos * 0.02; // each count is nominally 0.02mm of filament motion @ 5 * 254 cpi +} + +// Call the following at intervals to check the status. This is only called when extrusion is in progress or imminent. +// 'filamentConsumed' is the net amount of extrusion since the last call to this function. +// 'hadNonPrintingMove' is called if filamentConsumed includes extruder movement form non-printing moves. +FilamentSensorStatus LaserFilamentMonitor::Check(bool full, bool hadNonPrintingMove, float filamentConsumed) +{ + PollReceiveBuffer(); // this may update movementMeasured + + FilamentSensorStatus ret = FilamentSensorStatus::ok; + if (hadNonPrintingMove) + { + // We have had a non-printing move recently and we are configured to not check non-printing moves. Reset the counters. + movementMeasured = movementMeasuredAtLastCheck; // ignore measured extrusion since last check + } + else + { + extrusionCommanded += filamentConsumed; // include the extrusion we have just been told about + + if (full) + { + if ((sensorValue & TypeLaserErrorMask) != 0) + { + ret = FilamentSensorStatus::sensorError; + } + else if ((sensorValue & switchOpenMask) != 0) + { + ret = FilamentSensorStatus::noFilament; + } + else if (samplesReceived >= 10 && !IsReceiving()) + { + if (extrusionCommandedAtLastMeasurement >= minimumExtrusionCheckLength) + { + ret = CheckFilament(extrusionCommandedAtLastMeasurement, movementMeasured, false); + extrusionCommanded -= extrusionCommandedAtLastMeasurement; + extrusionCommandedAtLastMeasurement = 0.0; + movementMeasured = 0.0; + } + else if (extrusionCommanded >= minimumExtrusionCheckLength * 1.1 && millis() - lastMeasurementTime > 110) + { + ret = CheckFilament(extrusionCommanded, movementMeasured, true); + extrusionCommanded = 0.0; + extrusionCommandedAtLastMeasurement = 0.0; + movementMeasured = 0.0; + } + } + } + movementMeasuredAtLastCheck = movementMeasured; // save for next time + } + + return ret; +} + +// Compare the amount commanded with the amount of extrusion measured, and set up for the next comparison +FilamentSensorStatus LaserFilamentMonitor::CheckFilament(float amountCommanded, float amountMeasured, bool overdue) +{ + if (reprap.Debug(moduleFilamentSensors)) + { + debugPrintf("Extr req %.3f meas %.3f rem %.3f %s\n", (double)amountCommanded, (double)amountMeasured, (double)(extrusionCommanded - amountCommanded), + (overdue) ? " overdue" : ""); + } + + FilamentSensorStatus ret = FilamentSensorStatus::ok; + + switch (laserMonitorState) + { + case LaserMonitorState::idle: + laserMonitorState = LaserMonitorState::calibrating; + totalExtrusionCommanded = amountCommanded; + totalMovementMeasured = amountMeasured; + break; + + case LaserMonitorState::calibrating: + totalExtrusionCommanded += amountCommanded; + totalMovementMeasured += amountMeasured; + if (totalExtrusionCommanded >= 10.0) + { + backwards = (totalMovementMeasured < 0.0); + if (backwards) + { + totalMovementMeasured = -totalMovementMeasured; + amountMeasured = -amountMeasured; + } + minMovementRatio = maxMovementRatio = amountMeasured/amountCommanded; + if (comparisonEnabled) + { + if (minMovementRatio < minMovementAllowed) + { + ret = FilamentSensorStatus::tooLittleMovement; + } + else if (maxMovementRatio > maxMovementAllowed) + { + ret = FilamentSensorStatus::tooMuchMovement; + } + } + laserMonitorState = LaserMonitorState::comparing; + } + break; + + case LaserMonitorState::comparing: + { + totalExtrusionCommanded += amountCommanded; + if (backwards) + { + amountMeasured = -amountMeasured; + } + totalMovementMeasured += amountMeasured; + const float ratio = amountMeasured/amountCommanded; + if (ratio > maxMovementRatio) + { + maxMovementRatio = ratio; + } + if (ratio < minMovementRatio) + { + minMovementRatio = ratio; + } + if (comparisonEnabled) + { + if (ratio < minMovementAllowed) + { + ret = FilamentSensorStatus::tooLittleMovement; + } + else if (ratio > maxMovementAllowed) + { + ret = FilamentSensorStatus::tooMuchMovement; + } + } + } + break; + } + + return ret; +} + +// Clear the measurement state - called when we are not printing a file. Return the present/not present status if available. +FilamentSensorStatus LaserFilamentMonitor::Clear(bool full) +{ + PollReceiveBuffer(); // to keep the diagnostics up to date + Reset(); + + FilamentSensorStatus ret = FilamentSensorStatus::ok; + if (full) + { + if ((sensorValue & TypeLaserErrorMask) != 0) + { + ret = FilamentSensorStatus::sensorError; + } + else if ((sensorValue & switchOpenMask) != 0) + { + ret = FilamentSensorStatus::noFilament; + } + } + return ret; +} + +// Print diagnostic info for this sensor +void LaserFilamentMonitor::Diagnostics(MessageType mtype, unsigned int extruder) +{ + PollReceiveBuffer(); + const char* const statusText = (!dataReceived) ? "no data received" + : ((sensorValue & TypeLaserErrorMask) != 0) ? "error" + : ((sensorValue & switchOpenMask) != 0) ? "no filament" + : "ok"; + reprap.GetPlatform().MessageF(mtype, "Extruder %u sensor: position %.2f, %s, ", extruder, (double)GetCurrentPosition(), statusText); + if (dataReceived) + { + reprap.GetPlatform().MessageF(mtype, "%" PRIu32 " parity errors, ", parityErrorCount); + } + if (laserMonitorState != LaserMonitorState::calibrating && totalExtrusionCommanded > 10.0) + { + reprap.GetPlatform().MessageF(mtype, "measured minimum %ld%%, average %ld%%, maximum %ld%% over %.1fmm\n", + lrintf(100 * minMovementRatio), + lrintf((100 * totalMovementMeasured)/totalExtrusionCommanded), + lrintf(100 * maxMovementRatio), + (double)totalExtrusionCommanded); + } + else + { + reprap.GetPlatform().Message(mtype, "no calibration data\n"); + } +} + +// End diff --git a/src/FilamentMonitors/LaserFilamentMonitor.h b/src/FilamentMonitors/LaserFilamentMonitor.h new file mode 100644 index 00000000..e3db56e5 --- /dev/null +++ b/src/FilamentMonitors/LaserFilamentMonitor.h @@ -0,0 +1,81 @@ +/* + * LaserFilamentMonitor.h + * + * Created on: 9 Jan 2018 + * Author: David + */ + +#ifndef SRC_FILAMENTSENSORS_LASERFILAMENTMONITOR_H_ +#define SRC_FILAMENTSENSORS_LASERFILAMENTMONITOR_H_ + +#include "Duet3DFilamentMonitor.h" + +class LaserFilamentMonitor : public Duet3DFilamentMonitor +{ +public: + LaserFilamentMonitor(int type); + + bool Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) override; + FilamentSensorStatus Check(bool full, bool hadNonPrintingMove, float filamentConsumed) override; + FilamentSensorStatus Clear(bool full) override; + void Diagnostics(MessageType mtype, unsigned int extruder) override; + +protected: + void OnStartBitReceived(); + void ProcessReceivedWord(uint16_t val); + +private: + static constexpr float DefaultMinMovementAllowed = 0.6; + static constexpr float DefaultMaxMovementAllowed = 1.6; + static constexpr float DefaultMinimumExtrusionCheckLength = 3.0; + + static constexpr uint16_t TypeLaserParityMask = 0x8000u; + static constexpr uint16_t TypeLaserQualityMask = 0x4000u; + static constexpr uint16_t TypeLaserErrorMask = 0x2000u; + static constexpr uint16_t TypeLaserSwitchOpenMask = 0x1000u; + static constexpr uint16_t TypeLaserPositionMask = 0x03FF; // we use a 10-bit sensor position + + static constexpr size_t EdgeCaptureBufferSize = 64; // must be a power of 2 + + void Init(); + void Reset(); + float GetCurrentPosition() const; + FilamentSensorStatus CheckFilament(float amountCommanded, float amountMeasured, bool overdue); + + // Configuration parameters + float minMovementAllowed, maxMovementAllowed; + float minimumExtrusionCheckLength; + + // Other data + uint16_t sensorValue; // last known filament position (10 bits) + uint16_t qualityWord; // last received quality data + uint32_t lastMeasurementTime; // the last time we received a value + uint16_t switchOpenMask; // mask to isolate the switch open bit(s) from the sensor value + uint32_t parityErrorCount; // the number of words with bad parity we received + + float extrusionCommanded; // the amount of extrusion commanded since we last did a comparison + float extrusionCommandedAtStartBit; // the amount of extrusion commanded since the previous comparison when we received the start bit + float extrusionCommandedAtLastMeasurement; // the amount of extrusion commanded up to the start but of the last received measurement + float movementMeasured; // the accumulated revs (magnet), position (laser), or pulses since the previous comparison + float movementMeasuredAtLastCheck; // the accumulated movement measured before non-printing moves + + // Values measured for calibration + float minMovementRatio, maxMovementRatio; + float totalExtrusionCommanded; + float totalMovementMeasured; + + uint8_t samplesReceived; + bool dataReceived; + bool backwards; + bool comparisonEnabled; + + enum class LaserMonitorState : uint8_t + { + idle, + calibrating, + comparing + }; + LaserMonitorState laserMonitorState; +}; + +#endif /* SRC_FILAMENTSENSORS_LASERFILAMENTMONITOR_H_ */ diff --git a/src/FilamentMonitors/PulsedFilamentMonitor.cpp b/src/FilamentMonitors/PulsedFilamentMonitor.cpp new file mode 100644 index 00000000..d1e2522a --- /dev/null +++ b/src/FilamentMonitors/PulsedFilamentMonitor.cpp @@ -0,0 +1,251 @@ +/* + * PulsedFilamentSensor.cpp + * + * Created on: 9 Jan 2018 + * Author: David + */ + +#include "PulsedFilamentMonitor.h" +#include "GCodes/GCodeBuffer.h" +#include "Platform.h" +#include "RepRap.h" + +PulsedFilamentMonitor::PulsedFilamentMonitor(int type) + : FilamentMonitor(type), + mmPerPulse(DefaultMmPerPulse), tolerance(DefaultTolerance), minimumExtrusionCheckLength(DefaultMinimumExtrusionCheckLength) +{ + Init(); +} + +void PulsedFilamentMonitor::Init() +{ + sensorValue = 0; + calibrationStarted = dataReceived = false; + Reset(); +} + +void PulsedFilamentMonitor::Reset() +{ + extrusionCommanded = movementMeasured = extrusionCommandedAtLastMeasurement = movementMeasuredAtLastCheck = 0.0; + samplesReceived = 0; + comparisonStarted = false; +} + +// Configure this sensor, returning true if error and setting 'seen' if we processed any configuration parameters +bool PulsedFilamentMonitor::Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) +{ + if (ConfigurePin(gb, reply, RISING, seen)) + { + return true; + } + + gb.TryGetFValue('S', mmPerPulse, seen); // this is mm per rev for Duet3D sensors, mm per pulse for pulsed sensors + gb.TryGetFValue('E', minimumExtrusionCheckLength, seen); + + if (gb.Seen('R')) + { + seen = true; + tolerance = gb.GetFValue() * 0.01; + } + + if (seen) + { + Init(); + } + else + { + reply.printf("Pulse-type filament monitor on endstop input %u, sensitivity %.2fmm/pulse, check every %.1fmm, tolerance %.1f%%, ", + GetEndstopNumber(), + (double)mmPerPulse, + (double)minimumExtrusionCheckLength, + (double)(tolerance * 100.0)); + + if (!dataReceived) + { + reply.cat("no data received"); + } + else + { + reply.catf("current position %.1f, ", (double)GetCurrentPosition()); + if (calibrationStarted && fabsf(totalMovementMeasured) > 1.0 && totalExtrusionCommanded > 20.0) + { + const float measuredCalibration = totalExtrusionCommanded/totalMovementMeasured; + const float normalRatio = 1.0/measuredCalibration; + const int measuredPosTolerance = lrintf(100.0 * (((normalRatio > 0.0) ? maxMovementRatio : minMovementRatio) - normalRatio)/normalRatio); + const int measuredNegTolerance = lrintf(100.0 * (normalRatio - ((normalRatio > 0.0) ? minMovementRatio : maxMovementRatio))/normalRatio); + reply.catf("measured sensitivity %.2fmm/pulse over %.1fmm, tolerance +%d%% -%d%%", + (double)measuredCalibration, + (double)totalExtrusionCommanded, + measuredPosTolerance, + measuredNegTolerance); + } + else + { + reply.cat("no calibration data"); + } + } + } + + return false; +} + +// ISR for when the pin state changes +void PulsedFilamentMonitor::Interrupt() +{ + ++sensorValue; + extrusionCommandedAtLastMeasurement = extrusionCommanded; + if (samplesReceived < 100) + { + ++samplesReceived; + } +} + +// Call the following regularly to keep the status up to date +void PulsedFilamentMonitor::Poll() +{ + cpu_irq_disable(); + const uint16_t locSensorVal = sensorValue; + sensorValue = 0; + cpu_irq_enable(); + movementMeasured += (float)locSensorVal; + extrusionCommandedAtLastMeasurement = extrusionCommanded; +} + +// Return the current wheel angle +float PulsedFilamentMonitor::GetCurrentPosition() const +{ + return (float)sensorValue; +} + +// Call the following at intervals to check the status. This is only called when extrusion is in progress or imminent. +// 'filamentConsumed' is the net amount of extrusion since the last call to this function. +// 'hadNonPrintingMove' is called if filamentConsumed includes extruder movement form non-printing moves. +FilamentSensorStatus PulsedFilamentMonitor::Check(bool full, bool hadNonPrintingMove, float filamentConsumed) +{ + Poll(); // this may update movementMeasured + + FilamentSensorStatus ret = FilamentSensorStatus::ok; + if (!hadNonPrintingMove) + { + // We have had a non-printing move recently and we are configured to not check non-printing moves. Reset the counters. + movementMeasured = movementMeasuredAtLastCheck; // ignore measured extrusion since last check + } + else + { + extrusionCommanded += filamentConsumed; // include the extrusion we have just been told about + movementMeasuredAtLastCheck = movementMeasured; // save for next time + + if (full) + { + if (extrusionCommandedAtLastMeasurement >= minimumExtrusionCheckLength) + { + ret = CheckFilament(extrusionCommandedAtLastMeasurement, false); + } + else if (extrusionCommanded >= minimumExtrusionCheckLength * 1.1 && millis() - lastMeasurementTime > 110) + { + ret = CheckFilament(extrusionCommanded, true); + } + } + } + + return ret; +} + +// Compare the amount commanded with the amount of extrusion measured, and set up for the next comparison +FilamentSensorStatus PulsedFilamentMonitor::CheckFilament(float amountCommanded, bool overdue) +{ + const float extrusionMeasured = movementMeasured * mmPerPulse; + if (reprap.Debug(moduleFilamentSensors)) + { + debugPrintf("Extr req %.3f meas %.3f rem %.3f %s\n", (double)amountCommanded, (double)extrusionMeasured, (double)(extrusionCommanded - amountCommanded), + (overdue) ? " overdue" : ""); + } + + FilamentSensorStatus ret = FilamentSensorStatus::ok; + if (!comparisonStarted) + { + // The first measurement after we start extruding is often a long way out, so discard it + comparisonStarted = true; + calibrationStarted = false; + } + else if (tolerance >= 0.0) + { + const float minExtrusionExpected = (amountCommanded >= 0.0) + ? amountCommanded * (1.0 - tolerance) + : amountCommanded * (1.0 + tolerance); + if (extrusionMeasured < minExtrusionExpected) + { + ret = FilamentSensorStatus::tooLittleMovement; + } + else + { + const float maxExtrusionExpected = (amountCommanded >= 0.0) + ? amountCommanded * (1.0 + tolerance) + : amountCommanded * (1.0 - tolerance); + if (extrusionMeasured > maxExtrusionExpected) + { + ret = FilamentSensorStatus::tooMuchMovement; + } + } + } + + // Update the calibration accumulators, even if the user hasn't asked to do calibration + const float ratio = movementMeasured/amountCommanded; + if (calibrationStarted) + { + if (ratio < minMovementRatio) + { + minMovementRatio = ratio; + } + if (ratio > maxMovementRatio) + { + maxMovementRatio = ratio; + } + totalExtrusionCommanded += amountCommanded; + totalMovementMeasured += movementMeasured; + } + else + { + minMovementRatio = maxMovementRatio = ratio; + totalExtrusionCommanded = amountCommanded; + totalMovementMeasured = movementMeasured; + calibrationStarted = true; + } + + extrusionCommanded -= amountCommanded; + extrusionCommandedAtLastMeasurement = movementMeasured = 0.0; + + return ret; +} + +// Clear the measurement state - called when we are not printing a file. Return the present/not present status if available. +FilamentSensorStatus PulsedFilamentMonitor::Clear(bool full) +{ + Poll(); // to keep the diagnostics up to date + Reset(); + + return FilamentSensorStatus::ok; +} + +// Print diagnostic info for this sensor +void PulsedFilamentMonitor::Diagnostics(MessageType mtype, unsigned int extruder) +{ + Poll(); + const char* const statusText = (!dataReceived) ? "no data received" : "ok"; + reprap.GetPlatform().MessageF(mtype, "Extruder %u sensor: position %.2f, %s, ", extruder, (double)GetCurrentPosition(), statusText); + if (calibrationStarted && fabsf(totalMovementMeasured) > 1.0 && totalExtrusionCommanded > 20.0) + { + const float measuredMmPerRev = totalExtrusionCommanded/totalMovementMeasured; + const float normalRatio = 1.0/measuredMmPerRev; + const int measuredPosTolerance = lrintf(100.0 * (((normalRatio > 0.0) ? maxMovementRatio : minMovementRatio) - normalRatio)/normalRatio); + const int measuredNegTolerance = lrintf(100.0 * (normalRatio - ((normalRatio > 0.0) ? minMovementRatio : maxMovementRatio))/normalRatio); + reprap.GetPlatform().MessageF(mtype,"measured sensitivity %.2fmm/pulse +%d%% -%d%%\n", + (double)measuredMmPerRev, measuredPosTolerance, measuredNegTolerance); + } + else + { + reprap.GetPlatform().Message(mtype, "no calibration data\n"); + } +} + +// End diff --git a/src/FilamentMonitors/PulsedFilamentMonitor.h b/src/FilamentMonitors/PulsedFilamentMonitor.h new file mode 100644 index 00000000..93999e5e --- /dev/null +++ b/src/FilamentMonitors/PulsedFilamentMonitor.h @@ -0,0 +1,60 @@ +/* + * PulsedFilamentSensor.h + * + * Created on: 9 Jan 2018 + * Author: David + */ + +#ifndef SRC_FILAMENTSENSORS_PULSEDFILAMENTMONITOR_H_ +#define SRC_FILAMENTSENSORS_PULSEDFILAMENTMONITOR_H_ + +#include "FilamentMonitor.h" + +class PulsedFilamentMonitor : public FilamentMonitor +{ +public: + PulsedFilamentMonitor(int type); + + bool Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) override; + FilamentSensorStatus Check(bool full, bool hadNonPrintingMove, float filamentConsumed) override; + FilamentSensorStatus Clear(bool full) override; + void Diagnostics(MessageType mtype, unsigned int extruder) override; + void Interrupt() override; + +private: + static constexpr float DefaultMmPerPulse = 28.8; + static constexpr float DefaultTolerance = 0.25; + static constexpr float DefaultMinimumExtrusionCheckLength = 3.0; + + void Init(); + void Reset(); + void Poll(); + float GetCurrentPosition() const; + FilamentSensorStatus CheckFilament(float amountCommanded, bool overdue); + + // Configuration parameters + float mmPerPulse; + float tolerance; + float minimumExtrusionCheckLength; + + // Other data + uint16_t sensorValue; // last known pulses received (10 bits) + uint32_t lastMeasurementTime; // the last time we received a value + + float extrusionCommanded; // the amount of extrusion commanded since we last did a comparison + float extrusionCommandedAtLastMeasurement; // the amount of extrusion commanded up to the start but of the last received measurement + float movementMeasured; // the accumulated revs (magnet), position (laser), or pulses since the previous comparison + float movementMeasuredAtLastCheck; // the accumulated movement measured before non-printing moves + + // Values measured for calibration + float minMovementRatio, maxMovementRatio; + float totalExtrusionCommanded; + float totalMovementMeasured; + + uint8_t samplesReceived; + bool dataReceived; + bool comparisonStarted; + bool calibrationStarted; +}; + +#endif /* SRC_FILAMENTSENSORS_PULSEDFILAMENTMONITOR_H_ */ diff --git a/src/FilamentMonitors/RotatingMagnetFilamentMonitor.cpp b/src/FilamentMonitors/RotatingMagnetFilamentMonitor.cpp new file mode 100644 index 00000000..97db41b1 --- /dev/null +++ b/src/FilamentMonitors/RotatingMagnetFilamentMonitor.cpp @@ -0,0 +1,300 @@ +/* + * RotatingMagnetFilamentMonitor.cpp + * + * Created on: 9 Jan 2018 + * Author: David + */ + +#include "RotatingMagnetFilamentMonitor.h" +#include "GCodes/GCodeBuffer.h" +#include "Platform.h" +#include "RepRap.h" + +RotatingMagnetFilamentMonitor::RotatingMagnetFilamentMonitor(int type) + : Duet3DFilamentMonitor(type), + mmPerRev(DefaultMmPerRev), tolerance(DefaultTolerance), minimumExtrusionCheckLength(DefaultMinimumExtrusionCheckLength), checkNonPrintingMoves(true) +{ + switchOpenMask = (type == 4) ? TypeMagnetSwitchOpenMask : 0; + Init(); +} + +void RotatingMagnetFilamentMonitor::Init() +{ + sensorValue = 0; + calibrationStarted = dataReceived = false; + lastMeasurementTime = 0; + InitReceiveBuffer(); + Reset(); +} + +void RotatingMagnetFilamentMonitor::Reset() +{ + extrusionCommanded = movementMeasured = extrusionCommandedAtLastMeasurement = extrusionCommandedAtStartBit = movementMeasuredAtLastCheck = 0.0; + samplesReceived = 0; + comparisonStarted = false; +} + +// Configure this sensor, returning true if error and setting 'seen' if we processed any configuration parameters +bool RotatingMagnetFilamentMonitor::Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) +{ + if (ConfigurePin(gb, reply, CHANGE, seen)) + { + return true; + } + + gb.TryGetFValue('S', mmPerRev, seen); // this is mm per rev for Duet3D sensors, mm per pulse for pulsed sensors + gb.TryGetFValue('E', minimumExtrusionCheckLength, seen); + + if (gb.Seen('R')) + { + seen = true; + tolerance = gb.GetFValue() * 0.01; + } + + if (seen) + { + Init(); + } + else + { + reply.printf("Duet3D rotating magnet filament monitor%s on endstop input %u, sensitivity %.2fmm/rev, check every %.1fmm, tolerance %.1f%%, ", + (switchOpenMask != 0) ? " with microswitch" : "", + GetEndstopNumber(), + (double)mmPerRev, + (double)minimumExtrusionCheckLength, + (double)(tolerance * 100.0)); + if (!dataReceived) + { + reply.cat("no data received"); + } + else if ((sensorValue & TypeMagnetErrorMask) != 0) + { + reply.cat("error"); + } + else + { + reply.catf("current position %.1f, ", (double)GetCurrentPosition()); + if (calibrationStarted && fabsf(totalMovementMeasured) > 1.0 && totalExtrusionCommanded > 20.0) + { + const float measuredCalibration = totalExtrusionCommanded/totalMovementMeasured; + const float normalRatio = 1.0/measuredCalibration; + const int measuredPosTolerance = lrintf(100.0 * (((normalRatio > 0.0) ? maxMovementRatio : minMovementRatio) - normalRatio)/normalRatio); + const int measuredNegTolerance = lrintf(100.0 * (normalRatio - ((normalRatio > 0.0) ? minMovementRatio : maxMovementRatio))/normalRatio); + reply.catf("measured sensitivity %.2fmm/rev over %.1fmm, tolerance +%d%% -%d%%", + (double)measuredCalibration, + (double)totalExtrusionCommanded, + measuredPosTolerance, + measuredNegTolerance); + } + else + { + reply.cat("no calibration data"); + } + } + } + + return false; +} + +// This is called from the poll function when we receive what could be a start bit +void RotatingMagnetFilamentMonitor::OnStartBitReceived() /*override*/ +{ + extrusionCommandedAtStartBit = extrusionCommanded; // record the extrusion +} + +// This is called from the poll function when we have received a complete word of data +void RotatingMagnetFilamentMonitor::ProcessReceivedWord(uint16_t val) +{ + // We received a position report + if (samplesReceived == 0) + { + dataReceived = true; + extrusionCommanded -= extrusionCommandedAtStartBit; + extrusionCommandedAtStartBit = 0; + movementMeasured = 0.0; // use the first measurement sample as a baseline + } + else + { + const uint16_t angleChange = (val - sensorValue) & TypeMagnetAngleMask; // angle change in range 0..1023 + const int32_t movement = (angleChange <= 512) ? (int32_t)angleChange : (int32_t)angleChange - 1024; + movementMeasured += (float)movement/1024; + } + + lastMeasurementTime = millis(); + extrusionCommandedAtLastMeasurement = extrusionCommandedAtStartBit; + sensorValue = val; + if (samplesReceived < 100) + { + ++samplesReceived; + } +} + +// Return the current wheel angle +float RotatingMagnetFilamentMonitor::GetCurrentPosition() const +{ + return (sensorValue & TypeMagnetAngleMask) * (360.0/1024.0); +} + +// Call the following at intervals to check the status. This is only called when extrusion is in progress or imminent. +// 'filamentConsumed' is the net amount of extrusion since the last call to this function. +// 'hadNonPrintingMove' is called if filamentConsumed includes extruder movement form non-printing moves. +FilamentSensorStatus RotatingMagnetFilamentMonitor::Check(bool full, bool hadNonPrintingMove, float filamentConsumed) +{ + const bool useThisMeasurement = checkNonPrintingMoves || !hadNonPrintingMove; + + PollReceiveBuffer(); // this may update movementMeasured + + FilamentSensorStatus ret = FilamentSensorStatus::ok; + if (!useThisMeasurement) + { + // We have had a non-printing move recently and we are configured to not check non-printing moves. Reset the counters. + movementMeasured = movementMeasuredAtLastCheck; // ignore measured extrusion since last check + } + else + { + extrusionCommanded += filamentConsumed; // include the extrusion we have just been told about + movementMeasuredAtLastCheck = movementMeasured; // save for next time + + if (full) + { + if ((sensorValue & TypeMagnetErrorMask) != 0) + { + ret = FilamentSensorStatus::sensorError; + } + else if ((sensorValue & switchOpenMask) != 0) + { + ret = FilamentSensorStatus::noFilament; + } + else if (samplesReceived >= 10 && !IsReceiving()) + { + if (extrusionCommandedAtLastMeasurement >= minimumExtrusionCheckLength) + { + ret = CheckFilament(extrusionCommandedAtLastMeasurement, movementMeasured, false); + extrusionCommanded -= extrusionCommandedAtLastMeasurement; + extrusionCommandedAtLastMeasurement = 0.0; + movementMeasured = 0.0; + } + else if (extrusionCommanded >= minimumExtrusionCheckLength * 1.1 && millis() - lastMeasurementTime > 110) + { + ret = CheckFilament(extrusionCommanded, movementMeasured, true); + extrusionCommanded = 0.0; + extrusionCommandedAtLastMeasurement = 0.0; + movementMeasured = 0.0; + } + } + } + } + + return ret; +} + +// Compare the amount commanded with the amount of extrusion measured, and set up for the next comparison +FilamentSensorStatus RotatingMagnetFilamentMonitor::CheckFilament(float amountCommanded, float amountMeasured, bool overdue) +{ + if (reprap.Debug(moduleFilamentSensors)) + { + debugPrintf("Extr req %.3f meas %.3f rem %.3f %s\n", (double)amountCommanded, (double)amountMeasured, (double)(extrusionCommanded - amountCommanded), + (overdue) ? " overdue" : ""); + } + + FilamentSensorStatus ret = FilamentSensorStatus::ok; + if (!comparisonStarted) + { + // The first measurement after we start extruding is often a long way out, so discard it + comparisonStarted = true; + calibrationStarted = false; + } + else if (tolerance >= 0.0) + { + const float minExtrusionExpected = (amountCommanded >= 0.0) + ? amountCommanded * (1.0 - tolerance) + : amountCommanded * (1.0 + tolerance); + const float extrusionMeasured = amountMeasured * mmPerRev; + if (extrusionMeasured < minExtrusionExpected) + { + ret = FilamentSensorStatus::tooLittleMovement; + } + else + { + const float maxExtrusionExpected = (amountCommanded >= 0.0) + ? amountCommanded * (1.0 + tolerance) + : amountCommanded * (1.0 - tolerance); + if (extrusionMeasured > maxExtrusionExpected) + { + ret = FilamentSensorStatus::tooMuchMovement; + } + } + } + + // Update the calibration accumulators, even if the user hasn't asked to do calibration + const float ratio = amountMeasured/amountCommanded; + if (calibrationStarted) + { + if (ratio < minMovementRatio) + { + minMovementRatio = ratio; + } + if (ratio > maxMovementRatio) + { + maxMovementRatio = ratio; + } + totalExtrusionCommanded += amountCommanded; + totalMovementMeasured += amountMeasured; + } + else + { + minMovementRatio = maxMovementRatio = ratio; + totalExtrusionCommanded = amountCommanded; + totalMovementMeasured = amountMeasured; + calibrationStarted = true; + } + + return ret; +} + +// Clear the measurement state - called when we are not printing a file. Return the present/not present status if available. +FilamentSensorStatus RotatingMagnetFilamentMonitor::Clear(bool full) +{ + PollReceiveBuffer(); // to keep the diagnostics up to date + Reset(); + + FilamentSensorStatus ret = FilamentSensorStatus::ok; + if (full) + { + if ((sensorValue & TypeMagnetErrorMask) != 0) + { + ret = FilamentSensorStatus::sensorError; + } + else if ((sensorValue & switchOpenMask) != 0) + { + ret = FilamentSensorStatus::noFilament; + } + } + return ret; +} + +// Print diagnostic info for this sensor +void RotatingMagnetFilamentMonitor::Diagnostics(MessageType mtype, unsigned int extruder) +{ + PollReceiveBuffer(); + const char* const statusText = (!dataReceived) ? "no data received" + : ((sensorValue & TypeMagnetErrorMask) != 0) ? "error" + : ((sensorValue & switchOpenMask) != 0) ? "no filament" + : "ok"; + reprap.GetPlatform().MessageF(mtype, "Extruder %u sensor: position %.2f, %s, ", extruder, (double)GetCurrentPosition(), statusText); + if (calibrationStarted && fabsf(totalMovementMeasured) > 1.0 && totalExtrusionCommanded > 20.0) + { + const float measuredMmPerRev = totalExtrusionCommanded/totalMovementMeasured; + const float normalRatio = 1.0/measuredMmPerRev; + const int measuredPosTolerance = lrintf(100.0 * (((normalRatio > 0.0) ? maxMovementRatio : minMovementRatio) - normalRatio)/normalRatio); + const int measuredNegTolerance = lrintf(100.0 * (normalRatio - ((normalRatio > 0.0) ? minMovementRatio : maxMovementRatio))/normalRatio); + reprap.GetPlatform().MessageF(mtype,"measured sensitivity %.2fmm/rev +%d%% -%d%%\n", + (double)measuredMmPerRev, measuredPosTolerance, measuredNegTolerance); + } + else + { + reprap.GetPlatform().Message(mtype, "no calibration data\n"); + } +} + +// End diff --git a/src/FilamentMonitors/RotatingMagnetFilamentMonitor.h b/src/FilamentMonitors/RotatingMagnetFilamentMonitor.h new file mode 100644 index 00000000..7156e5f2 --- /dev/null +++ b/src/FilamentMonitors/RotatingMagnetFilamentMonitor.h @@ -0,0 +1,69 @@ +/* + * RotatingMagnetFilamentMonitor.h + * + * Created on: 9 Jan 2018 + * Author: David + */ + +#ifndef SRC_FILAMENTSENSORS_ROTATINGMAGNETFILAMENTMONITOR_H_ +#define SRC_FILAMENTSENSORS_ROTATINGMAGNETFILAMENTMONITOR_H_ + +#include "Duet3DFilamentMonitor.h" + +class RotatingMagnetFilamentMonitor : public Duet3DFilamentMonitor +{ +public: + RotatingMagnetFilamentMonitor(int type); + + bool Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) override; + FilamentSensorStatus Check(bool full, bool hadNonPrintingMove, float filamentConsumed) override; + FilamentSensorStatus Clear(bool full) override; + void Diagnostics(MessageType mtype, unsigned int extruder) override; + +protected: + void OnStartBitReceived(); + void ProcessReceivedWord(uint16_t val); + +private: + static constexpr float DefaultMmPerRev = 28.8; + static constexpr float DefaultTolerance = 0.25; + static constexpr float DefaultMinimumExtrusionCheckLength = 3.0; + + static constexpr uint16_t TypeMagnetErrorMask = 0x8000u; + static constexpr uint16_t TypeMagnetSwitchOpenMask = 0x4000u; + static constexpr uint16_t TypeMagnetAngleMask = 0x03FF; // we use a 10-bit sensor angle + + void Init(); + void Reset(); + float GetCurrentPosition() const; + FilamentSensorStatus CheckFilament(float amountCommanded, float amountMeasured, bool overdue); + + // Configuration parameters + float mmPerRev; + float tolerance; + float minimumExtrusionCheckLength; + bool checkNonPrintingMoves; + + // Other data + uint16_t sensorValue; // last known filament position (10 bits) + uint32_t lastMeasurementTime; // the last time we received a value + uint16_t switchOpenMask; // mask to isolate the switch open bit(s) from the sensor value + + float extrusionCommanded; // the amount of extrusion commanded since we last did a comparison + float extrusionCommandedAtStartBit; // the amount of extrusion commanded since the previous comparison when we received the start bit + float extrusionCommandedAtLastMeasurement; // the amount of extrusion commanded up to the start but of the last received measurement + float movementMeasured; // the accumulated revs (magnet), position (laser), or pulses since the previous comparison + float movementMeasuredAtLastCheck; // the accumulated movement measured before non-printing moves + + // Values measured for calibration + float minMovementRatio, maxMovementRatio; + float totalExtrusionCommanded; + float totalMovementMeasured; + + uint8_t samplesReceived; + bool dataReceived; + bool comparisonStarted; + bool calibrationStarted; +}; + +#endif /* SRC_FILAMENTSENSORS_ROTATINGMAGNETFILAMENTMONITOR_H_ */ diff --git a/src/FilamentSensors/SimpleFilamentSensor.cpp b/src/FilamentMonitors/SimpleFilamentMonitor.cpp index 793169a3..04a354e2 100644 --- a/src/FilamentSensors/SimpleFilamentSensor.cpp +++ b/src/FilamentMonitors/SimpleFilamentMonitor.cpp @@ -5,25 +5,25 @@ * Author: David */ -#include "SimpleFilamentSensor.h" +#include "SimpleFilamentMonitor.h" #include "RepRap.h" #include "Platform.h" -SimpleFilamentSensor::SimpleFilamentSensor(int type) : FilamentSensor(type), highWhenNoFilament(type == 2), filamentPresent(false) +SimpleFilamentMonitor::SimpleFilamentMonitor(int type) : FilamentMonitor(type), highWhenNoFilament(type == 2), filamentPresent(false) { } // Configure this sensor, returning true if error and setting 'seen' if we processed any configuration parameters -bool SimpleFilamentSensor::Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) +bool SimpleFilamentMonitor::Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) { - if (ConfigurePin(gb, reply, seen)) + if (ConfigurePin(gb, reply, CHANGE, seen)) { return true; } if (seen) { - Check(true, 0.0); + Check(true, false, 0.0); } else { @@ -34,14 +34,14 @@ bool SimpleFilamentSensor::Configure(GCodeBuffer& gb, StringRef& reply, bool& se } // ISR for when the pin state changes -void SimpleFilamentSensor::Interrupt() +void SimpleFilamentMonitor::Interrupt() { // Nothing needed here detachInterrupt(GetPin()); } // Call the following regularly to keep the status up to date -void SimpleFilamentSensor::Poll() +void SimpleFilamentMonitor::Poll() { const bool b = IoPort::ReadPin(GetPin()); filamentPresent = (highWhenNoFilament) ? !b : b; @@ -49,21 +49,21 @@ void SimpleFilamentSensor::Poll() // Call the following at intervals to check the status. This is only called when extrusion is in progress or imminent. // 'filamentConsumed' is the net amount of extrusion since the last call to this function. -FilamentSensorStatus SimpleFilamentSensor::Check(bool full, float filamentConsumed) +FilamentSensorStatus SimpleFilamentMonitor::Check(bool full, bool hadNonPrintingMove, float filamentConsumed) { Poll(); return (filamentPresent) ? FilamentSensorStatus::ok : FilamentSensorStatus::noFilament; } // Clear the measurement state - called when we are not printing a file. Return the present/not present status if available. -FilamentSensorStatus SimpleFilamentSensor::Clear(bool full) +FilamentSensorStatus SimpleFilamentMonitor::Clear(bool full) { Poll(); return (filamentPresent) ? FilamentSensorStatus::ok : FilamentSensorStatus::noFilament; } // Print diagnostic info for this sensor -void SimpleFilamentSensor::Diagnostics(MessageType mtype, unsigned int extruder) +void SimpleFilamentMonitor::Diagnostics(MessageType mtype, unsigned int extruder) { reprap.GetPlatform().MessageF(mtype, "Extruder %u sensor: %s\n", extruder, (filamentPresent) ? "ok" : "no filament"); } diff --git a/src/FilamentSensors/SimpleFilamentSensor.h b/src/FilamentMonitors/SimpleFilamentMonitor.h index 05122005..74badf32 100644 --- a/src/FilamentSensors/SimpleFilamentSensor.h +++ b/src/FilamentMonitors/SimpleFilamentMonitor.h @@ -5,18 +5,18 @@ * Author: David */ -#ifndef SRC_FILAMENTSENSORS_SIMPLEFILAMENTSENSOR_H_ -#define SRC_FILAMENTSENSORS_SIMPLEFILAMENTSENSOR_H_ +#ifndef SRC_FILAMENTSENSORS_SIMPLEFILAMENTMONITOR_H_ +#define SRC_FILAMENTSENSORS_SIMPLEFILAMENTMONITOR_H_ -#include "FilamentSensor.h" +#include "FilamentMonitor.h" -class SimpleFilamentSensor : public FilamentSensor +class SimpleFilamentMonitor : public FilamentMonitor { public: - SimpleFilamentSensor(int type); + SimpleFilamentMonitor(int type); bool Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) override; - FilamentSensorStatus Check(bool full, float filamentConsumed) override; + FilamentSensorStatus Check(bool full, bool hadNonPrintingMove, float filamentConsumed) override; FilamentSensorStatus Clear(bool full) override; void Diagnostics(MessageType mtype, unsigned int extruder) override; void Interrupt() override; @@ -28,4 +28,4 @@ private: bool filamentPresent; }; -#endif /* SRC_FILAMENTSENSORS_SIMPLEFILAMENTSENSOR_H_ */ +#endif /* SRC_FILAMENTSENSORS_SIMPLEFILAMENTMONITOR_H_ */ diff --git a/src/FilamentSensors/Duet3DFilamentSensor.cpp b/src/FilamentSensors/Duet3DFilamentSensor.cpp deleted file mode 100644 index ceddf765..00000000 --- a/src/FilamentSensors/Duet3DFilamentSensor.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Duet3DFilamentSensor.cpp - * - * Created on: 20 Jul 2017 - * Author: David - */ - -#include "Duet3DFilamentSensor.h" -#include "GCodes/GCodeBuffer.h" -#include "Platform.h" -#include "Movement/DDA.h" // for stepClockRate - -// Constructors -Duet3DFilamentSensor::Duet3DFilamentSensor(int type) - : FilamentSensor(type), mmPerRev(DefaultMmPerRev), - tolerance(DefaultTolerance), minimumExtrusionCheckLength(DefaultMinimumExtrusionCheckLength), withSwitch(type == 4) -{ - Init(); -} - -void Duet3DFilamentSensor::Init() -{ - samplesReceived = 0; - sensorValue = 0; - calibrationStarted = comparisonStarted = dataReceived = false; - edgeCaptureReadPointer = edgeCaptureWritePointer = 1; - edgeCaptures[0] = Platform::GetInterruptClocks(); // assume we just had a high-to-low transition - lastMeasurementTime = 0; - accumulatedExtrusionCommanded = accumulatedRevsMeasured = extrusionCommandedAtLastMeasurement = tentativeExtrusionCommanded = 0.0; - state = RxdState::waitingForStartBit; -} - -// Configure this sensor, returning true if error and setting 'seen' if we processed any configuration parameters -bool Duet3DFilamentSensor::Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) -{ - if (ConfigurePin(gb, reply, seen)) - { - return true; - } - - gb.TryGetFValue('S', mmPerRev, seen); - gb.TryGetFValue('E', minimumExtrusionCheckLength, seen); - - if (gb.Seen('R')) - { - seen = true; - tolerance = gb.GetFValue() * 0.01; - } - - if (seen) - { - Init(); - } - else - { - reply.printf("Duet3D filament monitor on E%u, %s microswitch, %.2fmm/rev, check every %.1fmm, tolerance %.1f%%, ", - GetEndstopNumber(), (withSwitch) ? "with" : "no", (double)mmPerRev, (double)minimumExtrusionCheckLength, (double)(tolerance * 100.0)); - - if (!dataReceived) - { - reply.cat("no data received"); - } - else if ((sensorValue & ErrorBit) != 0) - { - reply.cat("error"); - } - else - { - reply.catf("current angle %.1f, ", (double)GetCurrentAngle()); - if (calibrationStarted && fabsf(totalRevsMeasured) > 1.0 && totalExtrusionCommanded > 20.0) - { - const float measuredMmPerRev = totalExtrusionCommanded/totalRevsMeasured; - const float normalRatio = 1.0/measuredMmPerRev; - const int measuredPosTolerance = lrintf(100.0 * (((normalRatio > 0.0) ? maxMovementRatio : minMovementRatio) - normalRatio)/normalRatio); - const int measuredNegTolerance = lrintf(100.0 * (normalRatio - ((normalRatio > 0.0) ? minMovementRatio : maxMovementRatio))/normalRatio); - reply.catf("measured %.2fmm/rev +%d%% -%d%% over %.1fmm\n", (double)measuredMmPerRev, measuredPosTolerance, measuredNegTolerance, (double)totalExtrusionCommanded); - } - else - { - reply.cat("no calibration data"); - } - } - } - - return false; -} - -// ISR for when the pin state changes -void Duet3DFilamentSensor::Interrupt() -{ - uint32_t now = Platform::GetInterruptClocks(); - const size_t writePointer = edgeCaptureWritePointer; // capture volatile variable - if ((writePointer + 1) % EdgeCaptureBufferSize != edgeCaptureReadPointer) - { - if (IoPort::ReadPin(GetPin())) - { - if ((writePointer & 1) == 0) // low-to-high transitions should occur on odd indices - { - return; - } - } - else - { - if ((writePointer & 1) != 0) // high-to-low transitions should occur on even indices - { - return; - } - now -= 40; // partial correction for skew caused by debounce filter on Duet endstop inputs (measured skew = 74) - } - } - - edgeCaptures[writePointer] = now; // record the time at which this edge was detected - edgeCaptureWritePointer = (writePointer + 1) % EdgeCaptureBufferSize; -} - -// Call the following regularly to keep the status up to date -void Duet3DFilamentSensor::Poll() -{ - static constexpr uint32_t BitsPerSecond = 1000; // the nominal bit rate that the data is transmitted at - static constexpr uint32_t NominalBitLength = DDA::stepClockRate/BitsPerSecond; - static constexpr uint32_t MinBitLength = (NominalBitLength * 10)/13; // allow 30% clock speed tolerance - static constexpr uint32_t MaxBitLength = (NominalBitLength * 13)/10; // allow 30% clock speed tolerance - static constexpr uint32_t ErrorRecoveryDelayBits = 8; // before a start bit we want the line to be low for this long - static constexpr uint32_t ErrorRecoveryTime = NominalBitLength * ErrorRecoveryDelayBits; - - bool again; - do - { - again = false; - const size_t writePointer = edgeCaptureWritePointer; // capture volatile variable - const uint32_t now = Platform::GetInterruptClocks(); - switch (state) - { - case RxdState::waitingForStartBit: - if (writePointer != edgeCaptureReadPointer) - { - if ((edgeCaptureReadPointer & 1u) == 0) // if we are out of sync (this is normal when the last stuffing bit was a 1) - { - edgeCaptureReadPointer = (edgeCaptureReadPointer + 1u) % EdgeCaptureBufferSize; - again = true; - } - else - { - if (edgeCaptures[edgeCaptureReadPointer] - edgeCaptures[(edgeCaptureReadPointer - 1) % EdgeCaptureBufferSize] < ErrorRecoveryTime) - { - // The input line has not been low for long enough before the start bit - edgeCaptureReadPointer = (edgeCaptureReadPointer + 1u) % EdgeCaptureBufferSize; // ignore this start bit - state = RxdState::errorRecovery1; - again = true; - } - else - { - tentativeExtrusionCommanded = accumulatedExtrusionCommanded; // we have received what could be the beginning of a start bit - state = RxdState::waitingForEndOfStartBit; - again = true; - } - } - } - break; - - case RxdState::waitingForEndOfStartBit: - // This state must be made to time out because while we are in it, comparison of filament extruded is suspended - if ((writePointer - edgeCaptureReadPointer) % EdgeCaptureBufferSize >= 2) - { - // Check for a valid start bit - lastBitChangeIndex = (edgeCaptureReadPointer + 1u) % EdgeCaptureBufferSize; - startBitLength = edgeCaptures[lastBitChangeIndex] - edgeCaptures[edgeCaptureReadPointer]; - edgeCaptureReadPointer = lastBitChangeIndex; - if (startBitLength >= MinBitLength && startBitLength <= MaxBitLength) - { - valueBeingAssembled = 0; - nibblesAssembled = 0; - state = RxdState::waitingForNibble; - again = true; - //debugPrintf("sb %u\n", startBitLength); - } - else - { - // Start bit too long or too short - state = RxdState::errorRecovery2; - again = true; - } - } - else if (now - edgeCaptures[edgeCaptureReadPointer] > MaxBitLength) - { - edgeCaptureReadPointer = (edgeCaptureReadPointer + 1u) % EdgeCaptureBufferSize; - state = RxdState::errorRecovery2; - again = true; - } - break; - - case RxdState::waitingForNibble: - // This state must time out because while we are in it, comparison of filament extruded is suspended - { - const uint32_t nibbleStartTime = edgeCaptures[lastBitChangeIndex]; - if (now - nibbleStartTime > (13 * startBitLength)/2) - { - // 6.5 bit times have passed since the start of the bit that preceded the current nibble, so we should have a complete nibble and the following stuffing bit - uint32_t samplePoint = (startBitLength * 3)/2; // sampling time after the end of the start bit for bit 7 (MSB) - uint8_t currentNibble = 0; - size_t nextBitChangeIndex = (lastBitChangeIndex + 1u) % EdgeCaptureBufferSize; - for (uint8_t numBits = 0; numBits < 5; ++numBits) - { - if (nextBitChangeIndex != edgeCaptureWritePointer && edgeCaptures[nextBitChangeIndex] - nibbleStartTime < samplePoint) - { - lastBitChangeIndex = nextBitChangeIndex; - nextBitChangeIndex = (lastBitChangeIndex + 1u) % EdgeCaptureBufferSize; - if (nextBitChangeIndex != writePointer && edgeCaptures[nextBitChangeIndex] - nibbleStartTime < samplePoint) - { - edgeCaptureReadPointer = nextBitChangeIndex; - state = RxdState::errorRecovery3; - again = true; - break; - } - } - currentNibble <<= 1; - currentNibble |= (uint8_t)(lastBitChangeIndex & 1u); - samplePoint += startBitLength; - } - - if (state != RxdState::waitingForNibble) - { - break; - } - - // The 5th bit we received should be the inverse of the 4th bit - if ((((currentNibble >> 1u) ^ currentNibble) & 0x01u) == 0) - { - edgeCaptureReadPointer = nextBitChangeIndex; - state = RxdState::errorRecovery4; - again = true; - break; - } - - currentNibble >>= 1; - valueBeingAssembled = (valueBeingAssembled << 4) | currentNibble; - ++nibblesAssembled; - if (nibblesAssembled == 4) - { - edgeCaptureReadPointer = nextBitChangeIndex; // ready for a new word - if (samplesReceived == 0) - { - dataReceived = true; - accumulatedExtrusionCommanded -= tentativeExtrusionCommanded; - accumulatedRevsMeasured = 0.0; // use the first measurement sample as a baseline - } - else - { - const uint16_t angleChange = (valueBeingAssembled - sensorValue) & AngleMask; // angle change in range 0..1023 - const int32_t movement = (angleChange <= 512) ? (int32_t)angleChange : (int32_t)angleChange - 1024; - accumulatedRevsMeasured += (float)movement/1024; - } - - lastMeasurementTime = millis(); - extrusionCommandedAtLastMeasurement = tentativeExtrusionCommanded; - sensorValue = valueBeingAssembled; - if (samplesReceived < 100) - { - ++samplesReceived; - } - edgeCaptureReadPointer = nextBitChangeIndex; - state = RxdState::waitingForStartBit; - } - again = true; - } - } - break; - - default: // error recovery states - if (reprap.Debug(moduleFilamentSensors)) - { - debugPrintf("Fil err %u\n", (unsigned int)state); - } - state = RxdState::waitingForStartBit; - again = true; - break; - } - } while (again); -} - -// Return the current wheel angle -float Duet3DFilamentSensor::GetCurrentAngle() const -{ - return (sensorValue & AngleMask) * (360.0/1024.0); -} - -// Call the following at intervals to check the status. This is only called when extrusion is in progress or imminent. -// 'filamentConsumed' is the net amount of extrusion since the last call to this function. -FilamentSensorStatus Duet3DFilamentSensor::Check(bool full, float filamentConsumed) -{ - accumulatedExtrusionCommanded += filamentConsumed; - Poll(); - - FilamentSensorStatus ret = FilamentSensorStatus::ok; - if (full) - { - if ((sensorValue & ErrorBit) != 0) - { - ret = FilamentSensorStatus::sensorError; - } - else if (withSwitch && (sensorValue & SwitchOpenBit) != 0) - { - ret = FilamentSensorStatus::noFilament; - } - else if (samplesReceived >= 10 && state != RxdState::waitingForEndOfStartBit && state != RxdState::waitingForNibble) - { - if (extrusionCommandedAtLastMeasurement >= minimumExtrusionCheckLength) - { - ret = CheckFilament(extrusionCommandedAtLastMeasurement, false); - } - else if (accumulatedExtrusionCommanded >= minimumExtrusionCheckLength * 1.1 && millis() - lastMeasurementTime > 110) - { - ret = CheckFilament(accumulatedExtrusionCommanded, true); - } - } - } - - return ret; -} - -// Compare the amount commanded with the amount of extrusion measured, and set up for the next comparison -FilamentSensorStatus Duet3DFilamentSensor::CheckFilament(float amountCommanded, bool overdue) -{ - const float extrusionMeasured = accumulatedRevsMeasured * mmPerRev; - if (reprap.Debug(moduleFilamentSensors)) - { - debugPrintf("Extr req %.3f meas %.3f rem %.3f %s\n", (double)amountCommanded, (double)extrusionMeasured, (double)(accumulatedExtrusionCommanded - amountCommanded), - (overdue) ? " overdue" : ""); - } - - FilamentSensorStatus ret = FilamentSensorStatus::ok; - if (!comparisonStarted) - { - // The first measurement after we start extruding is often a long way out, so discard it - comparisonStarted = true; - calibrationStarted = false; - } - else if (tolerance >= 0.0) - { - const float minExtrusionExpected = (amountCommanded >= 0.0) - ? amountCommanded * (1.0 - tolerance) - : amountCommanded * (1.0 + tolerance); - if (extrusionMeasured < minExtrusionExpected) - { - ret = FilamentSensorStatus::tooLittleMovement; - } - else - { - const float maxExtrusionExpected = (amountCommanded >= 0.0) - ? amountCommanded * (1.0 + tolerance) - : amountCommanded * (1.0 - tolerance); - if (extrusionMeasured > maxExtrusionExpected) - { - ret = FilamentSensorStatus::tooMuchMovement; - } - } - } - - // Update the calibration accumulators, even if the user hasn't asked to do calibration - const float ratio = accumulatedRevsMeasured/amountCommanded; - if (calibrationStarted) - { - if (ratio < minMovementRatio) - { - minMovementRatio = ratio; - } - if (ratio > maxMovementRatio) - { - maxMovementRatio = ratio; - } - totalExtrusionCommanded += amountCommanded; - totalRevsMeasured += accumulatedRevsMeasured; - } - else - { - minMovementRatio = maxMovementRatio = ratio; - totalExtrusionCommanded = amountCommanded; - totalRevsMeasured = accumulatedRevsMeasured; - calibrationStarted = true; - } - - accumulatedExtrusionCommanded -= amountCommanded; - extrusionCommandedAtLastMeasurement = accumulatedRevsMeasured = 0.0; - - return ret; -} - -// Clear the measurement state - called when we are not printing a file. Return the present/not present status if available. -FilamentSensorStatus Duet3DFilamentSensor::Clear(bool full) -{ - Poll(); // to keep the diagnostics up to date - accumulatedExtrusionCommanded = accumulatedRevsMeasured = extrusionCommandedAtLastMeasurement = tentativeExtrusionCommanded = 0.0; - samplesReceived = 0; - comparisonStarted = false; - - FilamentSensorStatus ret = FilamentSensorStatus::ok; - if (full) - { - if ((sensorValue & ErrorBit) != 0) - { - ret = FilamentSensorStatus::sensorError; - } - else if (withSwitch && (sensorValue & SwitchOpenBit) != 0) - { - ret = FilamentSensorStatus::noFilament; - } - } - return ret; -} - -// Print diagnostic info for this sensor -void Duet3DFilamentSensor::Diagnostics(MessageType mtype, unsigned int extruder) -{ - Poll(); - const char* const statusText = (!dataReceived) ? "no data received" - : ((sensorValue & ErrorBit) != 0) ? "error" - : (withSwitch && (sensorValue & SwitchOpenBit) != 0) ? "no filament" - : "ok"; - reprap.GetPlatform().MessageF(mtype, "Extruder %u sensor: angle %.1f, %s, ", extruder, (double)GetCurrentAngle(), statusText); - if (calibrationStarted && fabsf(totalRevsMeasured) > 1.0 && totalExtrusionCommanded > 20.0) - { - const float measuredMmPerRev = totalExtrusionCommanded/totalRevsMeasured; - const float normalRatio = 1.0/measuredMmPerRev; - const int measuredPosTolerance = lrintf(100.0 * (((normalRatio > 0.0) ? maxMovementRatio : minMovementRatio) - normalRatio)/normalRatio); - const int measuredNegTolerance = lrintf(100.0 * (normalRatio - ((normalRatio > 0.0) ? minMovementRatio : maxMovementRatio))/normalRatio); - reprap.GetPlatform().MessageF(mtype,"%.2fmm/rev, tolerance +%d%% -%d%%\n", (double)measuredMmPerRev, measuredPosTolerance, measuredNegTolerance); - } - else - { - reprap.GetPlatform().Message(mtype, "no calibration data\n"); - } -} - -// End diff --git a/src/FilamentSensors/Duet3DFilamentSensor.h b/src/FilamentSensors/Duet3DFilamentSensor.h deleted file mode 100644 index 94e0204d..00000000 --- a/src/FilamentSensors/Duet3DFilamentSensor.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Duet3DFilamentSensor.h - * - * Created on: 20 Jul 2017 - * Author: David - */ - -#ifndef SRC_FILAMENTSENSORS_DUET3DFILAMENTSENSOR_H_ -#define SRC_FILAMENTSENSORS_DUET3DFILAMENTSENSOR_H_ - -#include "FilamentSensor.h" - -class Duet3DFilamentSensor : public FilamentSensor -{ -public: - Duet3DFilamentSensor(int type); - - bool Configure(GCodeBuffer& gb, StringRef& reply, bool& seen) override; - FilamentSensorStatus Check(bool full, float filamentConsumed) override; - FilamentSensorStatus Clear(bool full) override; - void Diagnostics(MessageType mtype, unsigned int extruder) override; - void Interrupt() override; - -private: - static constexpr float DefaultMmPerRev = 28.8; - static constexpr float DefaultTolerance = 0.2; - static constexpr float DefaultMinimumExtrusionCheckLength = 3.0; - - static constexpr uint16_t SwitchOpenBit = 0x4000u; - static constexpr uint16_t ErrorBit = 0x8000u; - static constexpr uint16_t AngleMask = 0x03FF; // 10-bit sensor angle - - static constexpr size_t EdgeCaptureBufferSize = 64; // must be a power of 2 - - void Init(); - void Poll(); - float GetCurrentAngle() const; - FilamentSensorStatus CheckFilament(float amountCommanded, bool overdue); - - // Configuration parameters - float mmPerRev; - float tolerance; - float minimumExtrusionCheckLength; - bool withSwitch; - - // Other data - uint16_t sensorValue; - float accumulatedExtrusionCommanded; - float accumulatedRevsMeasured; - float extrusionCommandedAtLastMeasurement; - float tentativeExtrusionCommanded; - - uint32_t lastMeasurementTime; - uint32_t edgeCaptures[EdgeCaptureBufferSize]; - size_t edgeCaptureReadPointer; - volatile size_t edgeCaptureWritePointer; - float minMovementRatio, maxMovementRatio; - float totalExtrusionCommanded, totalRevsMeasured; - - enum class RxdState : uint8_t - { - waitingForStartBit, - waitingForEndOfStartBit, - waitingForNibble, - errorRecovery1, - errorRecovery2, - errorRecovery3, - errorRecovery4 - }; - - RxdState state; - uint32_t startBitLength; - uint32_t errorRecoveryStartTime; - size_t lastBitChangeIndex; - uint16_t valueBeingAssembled; - uint8_t nibblesAssembled; - uint8_t samplesReceived; - bool dataReceived; - bool comparisonStarted; - bool calibrationStarted; -}; - -#endif /* SRC_FILAMENTSENSORS_DUET3DFILAMENTSENSOR_H_ */ diff --git a/src/GCodes/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer.cpp index 9add807c..5dbd1181 100644 --- a/src/GCodes/GCodeBuffer.cpp +++ b/src/GCodes/GCodeBuffer.cpp @@ -463,8 +463,7 @@ const void GCodeBuffer::GetFloatArray(float arr[], size_t& returnedLength, bool } } - // Special case if there is one entry and returnedLength requests several. - // Fill the array with the first entry. + // Special case if there is one entry and returnedLength requests several. Fill the array with the first entry. if (doPad && length == 1 && returnedLength > 1) { for(size_t i = 1; i < returnedLength; i++) @@ -487,7 +486,7 @@ const void GCodeBuffer::GetFloatArray(float arr[], size_t& returnedLength, bool } // Get a :-separated list of ints after a key letter -const void GCodeBuffer::GetIntArray(int32_t arr[], size_t& returnedLength) +const void GCodeBuffer::GetIntArray(int32_t arr[], size_t& returnedLength, bool doPad) { if (readPointer >= 0) { @@ -513,7 +512,19 @@ const void GCodeBuffer::GetIntArray(int32_t arr[], size_t& returnedLength) inList = false; } } - returnedLength = length; + + // Special case if there is one entry and returnedLength requests several. Fill the array with the first entry. + if (doPad && length == 1 && returnedLength > 1) + { + for (size_t i = 1; i < returnedLength; i++) + { + arr[i] = arr[0]; + } + } + else + { + returnedLength = length; + } readPointer = -1; } else @@ -524,7 +535,7 @@ const void GCodeBuffer::GetIntArray(int32_t arr[], size_t& returnedLength) } // Get a :-separated list of unsigned ints after a key letter -const void GCodeBuffer::GetUnsignedArray(uint32_t arr[], size_t& returnedLength) +const void GCodeBuffer::GetUnsignedArray(uint32_t arr[], size_t& returnedLength, bool doPad) { if (readPointer >= 0) { @@ -550,7 +561,20 @@ const void GCodeBuffer::GetUnsignedArray(uint32_t arr[], size_t& returnedLength) inList = false; } } - returnedLength = length; + + // Special case if there is one entry and returnedLength requests several. Fill the array with the first entry. + if (doPad && length == 1 && returnedLength > 1) + { + for (size_t i = 1; i < returnedLength; i++) + { + arr[i] = arr[0]; + } + } + else + { + returnedLength = length; + } + readPointer = -1; } else diff --git a/src/GCodes/GCodeBuffer.h b/src/GCodes/GCodeBuffer.h index 54a842ab..57886c67 100644 --- a/src/GCodes/GCodeBuffer.h +++ b/src/GCodes/GCodeBuffer.h @@ -40,8 +40,8 @@ public: bool GetQuotedString(const StringRef& str); // Get and copy a quoted string bool GetPossiblyQuotedString(const StringRef& str); // Get and copy a string which may or may not be quoted const void GetFloatArray(float arr[], size_t& length, bool doPad) __attribute__((hot)); // Get a :-separated list of floats after a key letter - const void GetIntArray(int32_t arr[], size_t& length); // Get a :-separated list of ints after a key letter - const void GetUnsignedArray(uint32_t arr[], size_t& length); // Get a :-separated list of unsigned ints after a key letter + const void GetIntArray(int32_t arr[], size_t& length, bool doPad); // Get a :-separated list of ints after a key letter + const void GetUnsignedArray(uint32_t arr[], size_t& length, bool doPad); // Get a :-separated list of unsigned ints after a key letter void TryGetFValue(char c, float& val, bool& seen); void TryGetIValue(char c, int32_t& val, bool& seen); diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 9a7c025b..7560db63 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -1439,7 +1439,7 @@ void GCodes::CheckFilament() ) { String<100> filamentErrorString; - filamentErrorString.GetRef().printf("Extruder %u reports %s", lastFilamentErrorExtruder, FilamentSensor::GetErrorMessage(lastFilamentError)); + filamentErrorString.GetRef().printf("Extruder %u reports %s", lastFilamentErrorExtruder, FilamentMonitor::GetErrorMessage(lastFilamentError)); DoPause(*autoPauseGCode, PauseReason::filament, filamentErrorString.Pointer()); lastFilamentError = FilamentSensorStatus::ok; platform.Message(LogMessage, filamentErrorString.c_str()); @@ -2011,16 +2011,25 @@ bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType) { const int drive = tool->Drive(eDrive); float extrusionAmount = requestedExtrusionAmount * tool->GetMix()[eDrive]; - if (gb.MachineState().volumetricExtrusion) + if (extrusionAmount != 0.0) { - extrusionAmount *= volumetricExtrusionFactors[drive]; - } - rawExtruderTotalByDrive[drive] += extrusionAmount; - if (!doingToolChange) // don't count extrusion done in tool change macros towards total filament consumed, it distorts the print progress - { - rawExtruderTotal += extrusionAmount; + if (gb.MachineState().volumetricExtrusion) + { + extrusionAmount *= volumetricExtrusionFactors[drive]; + } + rawExtruderTotalByDrive[drive] += extrusionAmount; + if (!doingToolChange) // don't count extrusion done in tool change macros towards total filament consumed, it distorts the print progress + { + rawExtruderTotal += extrusionAmount; + } + moveBuffer.coords[drive + numTotalAxes] = extrusionAmount * extrusionFactors[drive]; +#if HAS_SMART_DRIVERS + if (moveBuffer.moveType == 1) + { + SetBit(moveBuffer.endStopsToCheck, drive + numTotalAxes); + } +#endif } - moveBuffer.coords[drive + numTotalAxes] = extrusionAmount * extrusionFactors[drive]; } } else @@ -2032,16 +2041,25 @@ bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType) { const int drive = tool->Drive(eDrive); float extrusionAmount = eMovement[eDrive] * distanceScale; - if (gb.MachineState().volumetricExtrusion) + if (extrusionAmount != 0.0) { - extrusionAmount *= volumetricExtrusionFactors[drive]; - } - rawExtruderTotalByDrive[drive] += extrusionAmount; - if (!doingToolChange) // don't count extrusion done in tool change macros towards total filament consumed, it distorts the print progress - { - rawExtruderTotal += extrusionAmount; + if (gb.MachineState().volumetricExtrusion) + { + extrusionAmount *= volumetricExtrusionFactors[drive]; + } + rawExtruderTotalByDrive[drive] += extrusionAmount; + if (!doingToolChange) // don't count extrusion done in tool change macros towards total filament consumed, it distorts the print progress + { + rawExtruderTotal += extrusionAmount; + } + moveBuffer.coords[drive + numTotalAxes] = extrusionAmount * extrusionFactors[drive] * volumetricExtrusionFactors[drive]; +#if HAS_SMART_DRIVERS + if (moveBuffer.moveType == 1) + { + SetBit(moveBuffer.endStopsToCheck, drive + numTotalAxes); + } +#endif } - moveBuffer.coords[drive + numTotalAxes] = extrusionAmount * extrusionFactors[drive] * volumetricExtrusionFactors[drive]; } } else @@ -2068,6 +2086,7 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, StringRef& reply, bool isCoordinate axesToSenseLength = 0; // Check to see if the move is a 'homing' move that endstops are checked on. + // We handle S1 parameters affecting extrusion elsewhere. if (gb.Seen('S')) { const int ival = gb.GetIValue(); @@ -3145,7 +3164,7 @@ bool GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply) size_t dCount = numExtruders; // Sets the limit and returns the count if (gb.Seen('D')) { - gb.GetIntArray(drives, dCount); + gb.GetIntArray(drives, dCount, false); seen = true; } else @@ -3158,7 +3177,7 @@ bool GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply) size_t hCount = Heaters; if (gb.Seen('H')) { - gb.GetIntArray(heaters, hCount); + gb.GetIntArray(heaters, hCount, false); seen = true; } else @@ -3172,7 +3191,7 @@ bool GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply) { uint32_t xMapping[MaxAxes]; size_t xCount = numVisibleAxes; - gb.GetUnsignedArray(xMapping, xCount); + gb.GetUnsignedArray(xMapping, xCount, false); xMap = UnsignedArrayToBitMap<AxesBitmap>(xMapping, xCount) & LowestNBits<AxesBitmap>(numVisibleAxes); seen = true; } @@ -3187,7 +3206,7 @@ bool GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply) { uint32_t yMapping[MaxAxes]; size_t yCount = numVisibleAxes; - gb.GetUnsignedArray(yMapping, yCount); + gb.GetUnsignedArray(yMapping, yCount, false); yMap = UnsignedArrayToBitMap<AxesBitmap>(yMapping, yCount) & LowestNBits<AxesBitmap>(numVisibleAxes); seen = true; } @@ -3208,7 +3227,7 @@ bool GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply) { uint32_t fanMapping[NUM_FANS]; size_t fanCount = NUM_FANS; - gb.GetUnsignedArray(fanMapping, fanCount); + gb.GetUnsignedArray(fanMapping, fanCount, false); fanMap = UnsignedArrayToBitMap<FansBitmap>(fanMapping, fanCount) & LowestNBits<FansBitmap>(NUM_FANS); seen = true; } @@ -3650,13 +3669,13 @@ GCodeResult GCodes::SetHeaterParameters(GCodeBuffer& gb, StringRef& reply) if (!seen && oldChannel < 0) { // For backwards compatibility, if no sensor has been configured on this channel then assume the thermistor normally associated with the heater - if (heater < (int)Heaters) + if (heater < (int)Heaters && (gb.Seen('R') || gb.Seen('T') || gb.Seen('B'))) // if it has a thermistor and we are trying to configure it { channel = heater; } else { - reply.printf("Virtual heater %d is not configured", heater); + reply.printf("heater %d is not configured", heater); return GCodeResult::error; } } diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index bb2351a7..83a6e3d5 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -29,7 +29,7 @@ Licence: GPL #include "Platform.h" // for type EndStopHit #include "GCodeInput.h" #include "Tools/Filament.h" -#include "FilamentSensors/FilamentSensor.h" +#include "FilamentMonitors/FilamentMonitor.h" #include "RestorePoint.h" #include "Movement/BedProbing/Grid.h" diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index 2e9d1e55..3226396f 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -18,7 +18,7 @@ #include "PrintMonitor.h" #include "RepRap.h" #include "Tools/Tool.h" -#include "FilamentSensors/FilamentSensor.h" +#include "FilamentMonitors/FilamentMonitor.h" #include "Libraries/General/IP4String.h" #include "Version.h" @@ -402,7 +402,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) { uint32_t eDrive[MaxExtruders]; size_t eCount = numExtruders; - gb.GetUnsignedArray(eDrive, eCount); + gb.GetUnsignedArray(eDrive, eCount, false); for (size_t i = 0; i < eCount; i++) { seen = true; @@ -1330,9 +1330,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) if (!cancelWait && gb.Seen('H')) { // Wait for specified heaters to be ready - int32_t heaters[Heaters]; + uint32_t heaters[Heaters]; size_t heaterCount = Heaters; - gb.GetIntArray(heaters, heaterCount); + gb.GetUnsignedArray(heaters, heaterCount, false); for (size_t i = 0; i < heaterCount; i++) { @@ -1349,9 +1349,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) if (!cancelWait && gb.Seen('C')) { // Wait for specified chamber(s) to be ready - int32_t chamberIndices[NumChamberHeaters]; + uint32_t chamberIndices[NumChamberHeaters]; size_t chamberCount = NumChamberHeaters; - gb.GetIntArray(chamberIndices, chamberCount); + gb.GetUnsignedArray(chamberIndices, chamberCount, false); if (chamberCount == 0) { @@ -1372,7 +1372,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) // Otherwise wait only for the specified chamber heaters for (size_t i = 0; i < chamberCount; i++) { - if (chamberIndices[i] >= 0 && chamberIndices[i] < (int)NumChamberHeaters) + if (chamberIndices[i] >= 0 && chamberIndices[i] < NumChamberHeaters) { const int8_t heater = reprap.GetHeat().GetChamberHeater(chamberIndices[i]); if (heater >= 0 && !reprap.GetHeat().HeaterAtSetTemperature(heater, true)) @@ -2240,7 +2240,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) seen = true; uint32_t eVals[MaxExtruders]; size_t eCount = numExtruders; - gb.GetUnsignedArray(eVals, eCount); + gb.GetUnsignedArray(eVals, eCount, true); for (size_t e = 0; e < eCount; e++) { if (!ChangeMicrostepping(numTotalAxes + e, (int)eVals[e], mode)) @@ -2378,12 +2378,12 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) machineType = MachineType::laser; if (gb.Seen('P')) { - int lp = gb.GetIValue(); - if (lp < 0 || lp > 65535) + uint32_t lp = gb.GetUIValue(); + if (lp > 65535) { lp = NoLogicalPin; } - if (reprap.GetPlatform().SetLaserPin(lp)) + if (reprap.GetPlatform().SetLaserPin((LogicalPin)lp)) { reply.copy("Laser mode selected"); } @@ -2409,7 +2409,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) { uint32_t pins[2] = { NoLogicalPin, NoLogicalPin }; size_t numPins = 2; - gb.GetUnsignedArray(pins, numPins); + gb.GetUnsignedArray(pins, numPins, false); if (pins[0] > 65535) { pins[0] = NoLogicalPin; @@ -3057,7 +3057,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) { uint32_t eDrive[MaxExtruders]; size_t eCount = MaxExtruders; - gb.GetUnsignedArray(eDrive, eCount); + gb.GetUnsignedArray(eDrive, eCount, false); for (size_t i = 0; i < eCount; i++) { if (eDrive[i] >= numExtruders) @@ -3129,7 +3129,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) { reply.catf(" %s,", (inputType == EndStopInputType::activeHigh) ? "active high switch" - : (inputType == EndStopInputType::activeHigh) ? "active low switch" + : (inputType == EndStopInputType::activeLow) ? "active low switch" : (inputType == EndStopInputType::zProbe) ? "Z probe" : (inputType == EndStopInputType::motorStall) ? "motor stall" : "unknown type" @@ -3217,7 +3217,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) { size_t eDriveCount = MaxExtruders; uint32_t eDrives[MaxExtruders]; - gb.GetUnsignedArray(eDrives, eDriveCount); + gb.GetUnsignedArray(eDrives, eDriveCount, false); for (size_t extruder = 0; extruder < eDriveCount; extruder++) { const size_t eDrive = eDrives[extruder]; @@ -3396,10 +3396,10 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) gb.TryGetIValue('P', sensorType, seen); if (seen) { - FilamentSensor::SetFilamentSensorType(extruder, sensorType); + FilamentMonitor::SetFilamentSensorType(extruder, sensorType); } - FilamentSensor *sensor = FilamentSensor::GetFilamentSensor(extruder); + FilamentMonitor *sensor = FilamentMonitor::GetFilamentSensor(extruder); if (sensor != nullptr) { // Configure the sensor @@ -3407,7 +3407,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) result = GetGCodeResultFromError(error); if (error) { - FilamentSensor::SetFilamentSensorType(extruder, 0); // delete the sensor + FilamentMonitor::SetFilamentSensorType(extruder, 0); // delete the sensor } } else if (!seen) diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp index 349c2742..7a2a6a35 100644 --- a/src/GCodes/GCodes3.cpp +++ b/src/GCodes/GCodes3.cpp @@ -343,7 +343,7 @@ GCodeResult GCodes::CheckOrConfigureTrigger(GCodeBuffer& gb, StringRef& reply, i { uint32_t eStops[MaxExtruders]; size_t numEntries = MaxExtruders; - gb.GetUnsignedArray(eStops, numEntries); + gb.GetUnsignedArray(eStops, numEntries, false); for (size_t i = 0; i < numEntries; ++i) { if (eStops[i] < MaxExtruders) @@ -423,7 +423,7 @@ GCodeResult GCodes::DoDriveMapping(GCodeBuffer& gb, StringRef& reply) seen = true; size_t numValues = MaxDriversPerAxis; uint32_t drivers[MaxDriversPerAxis]; - gb.GetUnsignedArray(drivers, numValues); + gb.GetUnsignedArray(drivers, numValues, false); // Check all the driver numbers are in range AxisDriversConfig config; @@ -477,7 +477,7 @@ GCodeResult GCodes::DoDriveMapping(GCodeBuffer& gb, StringRef& reply) seen = true; size_t numValues = DRIVES - numTotalAxes; uint32_t drivers[MaxExtruders]; - gb.GetUnsignedArray(drivers, numValues); + gb.GetUnsignedArray(drivers, numValues, false); numExtruders = numValues; for (size_t i = 0; i < numValues; ++i) { @@ -703,7 +703,7 @@ GCodeResult GCodes::UpdateFirmware(GCodeBuffer& gb, StringRef &reply) { uint32_t modulesToUpdate[3]; size_t numUpdateModules = ARRAY_SIZE(modulesToUpdate); - gb.GetUnsignedArray(modulesToUpdate, numUpdateModules); + gb.GetUnsignedArray(modulesToUpdate, numUpdateModules, false); for (size_t i = 0; i < numUpdateModules; ++i) { uint32_t t = modulesToUpdate[i]; diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp index 258f174d..6afa6f44 100644 --- a/src/Movement/DDA.cpp +++ b/src/Movement/DDA.cpp @@ -1264,7 +1264,13 @@ void DDA::CheckEndstops(Platform& platform) #endif const size_t numAxes = reprap.GetGCodes().GetTotalAxes(); - for (size_t drive = 0; drive < numAxes; ++drive) + for (size_t drive = 0; drive < +#if HAS_STALL_DETECT + DRIVES +#else + numAxes +#endif + ; ++drive) { if (IsBitSet(endStopsToCheck, drive)) { @@ -1273,7 +1279,7 @@ void DDA::CheckEndstops(Platform& platform) { case EndStopHit::lowHit: case EndStopHit::highHit: - if ((endStopsToCheck & UseSpecialEndstop) != 0) // use only one (probably non-default) endstop while probing a tool offset + if ((endStopsToCheck & UseSpecialEndstop) != 0) // use only one (probably non-default) endstop while probing a tool offset { MoveAborted(); } @@ -1281,7 +1287,12 @@ void DDA::CheckEndstops(Platform& platform) { ClearBit(endStopsToCheck, drive); // clear this check so that we can check for more const Kinematics& kin = reprap.GetMove().GetKinematics(); - if (endStopsToCheck == 0 || kin.QueryTerminateHomingMove(drive)) + if ( endStopsToCheck == 0 // if no endstops left to check +#if HAS_STALL_DETECT + || drive >= numAxes // or we are stopping on an extruder motor stall +#endif + || kin.QueryTerminateHomingMove(drive) // or this kinematics requires us to stop the move now + ) { MoveAborted(); // no more endstops to check, or this axis uses shared motors, so stop the entire move } @@ -1289,7 +1300,7 @@ void DDA::CheckEndstops(Platform& platform) { StopDrive(drive); // we must stop the drive before we mess with its coordinates } - if (drive < reprap.GetGCodes().GetTotalAxes() && IsHomingAxes()) + if (drive < numAxes && IsHomingAxes()) { kin.OnHomingSwitchTriggered(drive, esh == EndStopHit::highHit, reprap.GetPlatform().GetDriveStepsPerUnit(), *this); reprap.GetGCodes().SetAxisIsHomed(drive); @@ -1424,7 +1435,7 @@ bool DDA::Step() // 2. Determine which drivers are due for stepping, overdue, or will be due very shortly DriveMovement* dm = firstDM; - const uint32_t elapsedTime = (Platform::GetInterruptClocks() - moveStartTime) + minInterruptInterval; + const uint32_t elapsedTime = (Platform::GetInterruptClocks() - moveStartTime) + MinInterruptInterval; uint32_t driversStepping = 0; while (dm != nullptr && elapsedTime >= dm->nextStepTime) // if the next step is due { @@ -1638,6 +1649,11 @@ int32_t DDA::GetStepsTaken(size_t drive) const return (dmp != nullptr) ? dmp->GetNetStepsTaken() : 0; } +bool DDA::IsNonPrintingExtruderMove(size_t drive) const +{ + return !isPrintingMove && FindDM(drive) != nullptr; +} + void DDA::LimitSpeedAndAcceleration(float maxSpeed, float maxAcceleration) { if (requestedSpeed > maxSpeed) diff --git a/src/Movement/DDA.h b/src/Movement/DDA.h index 21a9bee5..5de52e3f 100644 --- a/src/Movement/DDA.h +++ b/src/Movement/DDA.h @@ -74,6 +74,7 @@ public: void LimitSpeedAndAcceleration(float maxSpeed, float maxAcceleration); // Limit the speed an acceleration of this move int32_t GetStepsTaken(size_t drive) const; + bool IsNonPrintingExtruderMove(size_t drive) const; float GetProportionDone(bool moveWasAborted) const; // Return the proportion of extrusion for the complete multi-segment move already done @@ -105,17 +106,17 @@ public: // Note: the above measurements were taken some time ago, before some firmware optimisations. #if SAME70 // The system clock of the SAME70 is running at 150MHz. Use the same defaults as for the SAM4E for now. - static constexpr int32_t MinCalcIntervalDelta = (40 * stepClockRate)/1000000; // the smallest sensible interval between calculations (40us) in step timer clocks - static constexpr int32_t MinCalcIntervalCartesian = (40 * stepClockRate)/1000000; // same as delta for now, but could be lower - static constexpr uint32_t minInterruptInterval = 6; // about 6us minimum interval between interrupts, in step clocks + static constexpr uint32_t MinCalcIntervalDelta = (40 * stepClockRate)/1000000; // the smallest sensible interval between calculations (40us) in step timer clocks + static constexpr uint32_t MinCalcIntervalCartesian = (40 * stepClockRate)/1000000; // same as delta for now, but could be lower + static constexpr uint32_t MinInterruptInterval = 6; // about 6us minimum interval between interrupts, in step clocks #elif SAM4E || SAM4S - static constexpr int32_t MinCalcIntervalDelta = (40 * stepClockRate)/1000000; // the smallest sensible interval between calculations (40us) in step timer clocks - static constexpr int32_t MinCalcIntervalCartesian = (40 * stepClockRate)/1000000; // same as delta for now, but could be lower - static constexpr uint32_t minInterruptInterval = 6; // about 6us minimum interval between interrupts, in step clocks + static constexpr uint32_t MinCalcIntervalDelta = (40 * stepClockRate)/1000000; // the smallest sensible interval between calculations (40us) in step timer clocks + static constexpr uint32_t MinCalcIntervalCartesian = (40 * stepClockRate)/1000000; // same as delta for now, but could be lower + static constexpr uint32_t MinInterruptInterval = 6; // about 6us minimum interval between interrupts, in step clocks #else - static constexpr int32_t MinCalcIntervalDelta = (60 * stepClockRate)/1000000; // the smallest sensible interval between calculations (60us) in step timer clocks - static constexpr int32_t MinCalcIntervalCartesian = (60 * stepClockRate)/1000000; // same as delta for now, but could be lower - static constexpr uint32_t minInterruptInterval = 4; // about 6us minimum interval between interrupts, in step clocks + static constexpr uint32_t MinCalcIntervalDelta = (60 * stepClockRate)/1000000; // the smallest sensible interval between calculations (60us) in step timer clocks + static constexpr uint32_t MinCalcIntervalCartesian = (60 * stepClockRate)/1000000; // same as delta for now, but could be lower + static constexpr uint32_t MinInterruptInterval = 4; // about 6us minimum interval between interrupts, in step clocks #endif static void PrintMoves(); // print saved moves for debugging diff --git a/src/Movement/DriveMovement.cpp b/src/Movement/DriveMovement.cpp index 2352cc26..8bba7802 100644 --- a/src/Movement/DriveMovement.cpp +++ b/src/Movement/DriveMovement.cpp @@ -182,7 +182,9 @@ void DriveMovement::PrepareExtruder(const DDA& dda, const PrepParams& params, bo if (reprap.GetPlatform().GetExtrusionCoefficients(extruder, a, b, limit)) { const float averageExtrusionSpeed = (dda.totalDistance * dv * DDA::stepClockRate)/dda.clocksNeeded; - stepsPerMm *= 1.0 + min<float>((averageExtrusionSpeed * a) + (averageExtrusionSpeed * averageExtrusionSpeed * b), limit); + const float factor = 1.0 + min<float>((averageExtrusionSpeed * a) + (averageExtrusionSpeed * averageExtrusionSpeed * b), limit); + stepsPerMm *= factor; + totalSteps = (uint32_t)(totalSteps * factor); // round this down to avoid step errors } } #endif @@ -293,10 +295,10 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0) uint32_t shiftFactor = 0; // assume single stepping if (stepInterval < DDA::MinCalcIntervalCartesian) { - uint32_t stepsToLimit = ((nextStep <= reverseStartStep && reverseStartStep <= totalSteps) - ? reverseStartStep - : totalSteps - ) - nextStep; + const uint32_t stepsToLimit = ((nextStep <= reverseStartStep && reverseStartStep <= totalSteps) + ? reverseStartStep + : totalSteps + ) - nextStep; if (stepInterval < DDA::MinCalcIntervalCartesian/4 && stepsToLimit > 8) { shiftFactor = 3; // octal stepping diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp index b1532c70..2a3479ae 100644 --- a/src/Movement/Move.cpp +++ b/src/Movement/Move.cpp @@ -65,6 +65,7 @@ void Move::Init() for (size_t i = 0; i < MaxExtruders; ++i) { extrusionAccumulators[i] = 0; + extruderNonPrinting[i] = false; extrusionPending[i] = 0.0; } @@ -968,6 +969,10 @@ void Move::CurrentMoveCompleted() for (size_t drive = numAxes; drive < DRIVES; ++drive) { extrusionAccumulators[drive - numAxes] += currentDda->GetStepsTaken(drive); + if (currentDda->IsNonPrintingExtruderMove(drive)) + { + extruderNonPrinting[drive - numAxes] = true; + } } currentDda = nullptr; @@ -1096,7 +1101,9 @@ void Move::ResetExtruderPositions() } // Get the accumulated extruder motor steps taken by an extruder since the last call. Used by the filament monitoring code. -int32_t Move::GetAccumulatedExtrusion(size_t extruder) +// Returns the number of motor steps moves since the last call, and nonPrinting is true if we are currently executing an extruding but non-printing move +// or we completed one since the last call. +int32_t Move::GetAccumulatedExtrusion(size_t extruder, bool& nonPrinting) { const size_t drive = extruder + reprap.GetGCodes().GetTotalAxes(); if (drive < DRIVES) @@ -1104,12 +1111,27 @@ int32_t Move::GetAccumulatedExtrusion(size_t extruder) const irqflags_t flags = cpu_irq_save(); const int32_t ret = extrusionAccumulators[extruder]; const DDA * const cdda = currentDda; // capture volatile variable - const int32_t adjustment = (cdda != nullptr) ? cdda->GetStepsTaken(drive) : 0; + const int32_t adjustment = (cdda == nullptr) ? 0 : cdda->GetStepsTaken(drive); + if (adjustment == 0) + { + // Either there is no current move, or we are at the very start of this move, or it doesn't involve this extruder e.g. a travel move + nonPrinting = extruderNonPrinting[extruder]; + } + else if (cdda->IsPrintingMove()) + { + nonPrinting = extruderNonPrinting[extruder]; + extruderNonPrinting[extruder] = false; + } + else + { + nonPrinting = true; + } extrusionAccumulators[extruder] = -adjustment; cpu_irq_restore(flags); return ret + adjustment; } + nonPrinting = true; return 0.0; } diff --git a/src/Movement/Move.h b/src/Movement/Move.h index 92a64b7b..f357f29c 100644 --- a/src/Movement/Move.h +++ b/src/Movement/Move.h @@ -118,7 +118,7 @@ public: void AdjustLeadscrews(const floatc_t corrections[]); // Called by some Kinematics classes to adjust the leadscrews - int32_t GetAccumulatedExtrusion(size_t extruder); // Return ands reset the accumulated extrusion amount + int32_t GetAccumulatedExtrusion(size_t extruder, bool& nonPrinting); // Return and reset the accumulated extrusion amount bool WriteResumeSettings(FileStore *f) const; // Write settings for resuming the print @@ -170,6 +170,7 @@ private: volatile bool liveCoordinatesValid; // True if the XYZ live coordinates are reliable (the extruder ones always are) volatile int32_t liveEndPoints[DRIVES]; // The XYZ endpoints of the last completed move in motor coordinates volatile int32_t extrusionAccumulators[MaxExtruders]; // Accumulated extruder motor steps + volatile bool extruderNonPrinting[MaxExtruders]; // Set whenever the extruder starts a non-printing move float tangents[3]; // Axis compensation - 90 degrees + angle gives angle between axes float& tanXY = tangents[0]; diff --git a/src/Network2/ESP8266/Network.cpp b/src/Network2/ESP8266/Network.cpp index 8070a417..588b4873 100644 --- a/src/Network2/ESP8266/Network.cpp +++ b/src/Network2/ESP8266/Network.cpp @@ -669,7 +669,7 @@ const char* Network::TranslateNetworkState() const void Network::Diagnostics(MessageType mtype) { - platform.MessageF(mtype, "Network state is %s\n", TranslateNetworkState()); + platform.MessageF(mtype, "=== Network ===\nNetwork state is %s\n", TranslateNetworkState()); platform.MessageF(mtype, "WiFi module is %s\n", TranslateWiFiState(currentMode)); platform.MessageF(mtype, "Failed messages: pending %u, notready %u, noresp %u\n", transferAlreadyPendingCount, readyTimeoutCount, responseTimeoutCount); diff --git a/src/Network2/W5500/Network.cpp b/src/Network2/W5500/Network.cpp index 553d59f0..e135ff0e 100644 --- a/src/Network2/W5500/Network.cpp +++ b/src/Network2/W5500/Network.cpp @@ -404,8 +404,7 @@ void Network::Spin(bool full) void Network::Diagnostics(MessageType mtype) { - platform.Message(mtype, "=== Network ===\n"); - platform.MessageF(mtype, "State: %d\n", (int)state); + platform.MessageF(mtype, "=== Network ===\nState: %d\n", (int)state); HttpResponder::CommonDiagnostics(mtype); platform.Message(mtype, "Responder states:"); for (NetworkResponder *r = responders; r != nullptr; r = r->GetNext()) diff --git a/src/Network2/W5500/Wiznet/Ethernet/WizSpi.cpp b/src/Network2/W5500/Wiznet/Ethernet/WizSpi.cpp index 9c53d98c..9a695791 100644 --- a/src/Network2/W5500/Wiznet/Ethernet/WizSpi.cpp +++ b/src/Network2/W5500/Wiznet/Ethernet/WizSpi.cpp @@ -5,7 +5,7 @@ * Author: David */ -#include <Network2/W5500/Wiznet/Ethernet/WizSpi.h> +#include "WizSpi.h" #include "variant.h" #include "Pins.h" @@ -24,10 +24,13 @@ #include "matrix/matrix.h" -// Functions called by the W5500 module to transfer data to/from the W5500 via SPI -const uint32_t SpiClockFrequency = 40000000; // tried 60MHz and we got some data corruption when uploading files, so try 40MHz instead +// SPI data rate. I tried 60MHz and we got some data corruption when uploading files, so I reduced it to 40MHz. +// 2018-01-11: We have now received one report of data corruption at 40MHz, so reduced this to 30MHz +const uint32_t SpiClockFrequency = 30000000; const unsigned int SpiPeripheralChannelId = 0; // we use NPCS0 as the slave select signal +// Functions called by the W5500 module to transfer data to/from the W5500 via SPI + #if USE_PDC static Pdc *spi_pdc; diff --git a/src/Platform.cpp b/src/Platform.cpp index ef3baf88..5fa5aeda 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -26,7 +26,7 @@ #include "Movement/Move.h" #include "Network.h" #include "PrintMonitor.h" -#include "FilamentSensors/FilamentSensor.h" +#include "FilamentMonitors/FilamentMonitor.h" #include "RepRap.h" #include "Scanner.h" #include "Version.h" @@ -948,7 +948,7 @@ bool Platform::ProgramZProbe(GCodeBuffer& gb, StringRef& reply) { uint32_t zProbeProgram[MaxZProbeProgramBytes]; size_t len = MaxZProbeProgramBytes; - gb.GetUnsignedArray(zProbeProgram, len); + gb.GetUnsignedArray(zProbeProgram, len, false); if (len != 0) { for (size_t i = 0; i < len; ++i) @@ -1579,8 +1579,8 @@ void Platform::Spin() { bool reported = false; #if HAS_SMART_DRIVERS - ReportDrivers(shortToGroundDrivers, "Error: Short-to-ground", reported); - ReportDrivers(temperatureShutdownDrivers, "Error: Over temperature shutdown", reported); + ReportDrivers(ErrorMessage, shortToGroundDrivers, "short-to-ground", reported); + ReportDrivers(ErrorMessage, temperatureShutdownDrivers, "over temperature shutdown", reported); // Don't report open load because we get too many spurious open load reports //ReportDrivers(openLoadDrivers, "Error: Open load", reported); @@ -1594,7 +1594,7 @@ void Platform::Spin() || (offBoardOtw && (!offBoardDriversFanRunning || now - offBoardDriversFanStartMillis >= DriverCoolingTimeout)) ) { - ReportDrivers(temperatureWarningDrivers, "Warning: high temperature", reported); + ReportDrivers(WarningMessage, temperatureWarningDrivers, "high temperature", reported); } } #endif @@ -1702,7 +1702,7 @@ void Platform::Spin() // Report driver status conditions that require attention. // Sets 'reported' if we reported anything, else leaves 'reported' alone. -void Platform::ReportDrivers(DriversBitmap whichDrivers, const char* text, bool& reported) +void Platform::ReportDrivers(MessageType mt, DriversBitmap whichDrivers, const char* text, bool& reported) { if (whichDrivers != 0) { @@ -1715,7 +1715,7 @@ void Platform::ReportDrivers(DriversBitmap whichDrivers, const char* text, bool& } whichDrivers >>= 1; } - MessageF(WarningMessage, "%s\n", scratchString.Pointer()); + MessageF(mt, "%s\n", scratchString.Pointer()); reported = true; } } @@ -1724,8 +1724,8 @@ void Platform::ReportDrivers(DriversBitmap whichDrivers, const char* text, bool& #if HAS_STALL_DETECT -// Return true if any motor driving this axis or extruder is stalled -bool Platform::AnyMotorStalled(size_t drive) const +// Return true if any motor driving this axis is stalled +bool Platform::AnyAxisMotorStalled(size_t drive) const { const size_t numAxes = reprap.GetGCodes().GetTotalAxes(); if (drive < numAxes) @@ -1743,6 +1743,12 @@ bool Platform::AnyMotorStalled(size_t drive) const return (SmartDrivers::GetLiveStatus(extruderDrivers[drive - numAxes]) & TMC_RR_SG) != 0; } +// Return true if the motor driving this extruder is stalled +bool Platform::ExtruderMotorStalled(size_t extruder) const pre(drive < DRIVES) +{ + return (SmartDrivers::GetLiveStatus(extruderDrivers[extruder]) & TMC_RR_SG) != 0; +} + #endif #if HAS_VOLTAGE_MONITOR @@ -1925,7 +1931,7 @@ static void FanInterrupt(CallbackParameter) ++fanInterruptCount; if (fanInterruptCount == fanMaxInterruptCount) { - uint32_t now = micros(); + const uint32_t now = micros(); fanInterval = now - fanLastResetTime; fanLastResetTime = now; fanInterruptCount = 0; @@ -2681,7 +2687,8 @@ void Platform::UpdateConfiguredHeaters() EndStopHit Platform::Stopped(size_t drive) const { - if (drive < reprap.GetGCodes().GetTotalAxes()) + const size_t numAxes = reprap.GetGCodes().GetTotalAxes(); + if (drive < numAxes) { switch (endStopInputType[drive]) { @@ -2702,28 +2709,28 @@ EndStopHit Platform::Stopped(size_t drive) const case KinematicsType::coreXY: // Both X and Y motors are involved in homing X or Y motorIsStalled = (drive == X_AXIS || drive == Y_AXIS) - ? AnyMotorStalled(X_AXIS) || AnyMotorStalled(Y_AXIS) - : AnyMotorStalled(drive); + ? AnyAxisMotorStalled(X_AXIS) || AnyAxisMotorStalled(Y_AXIS) + : AnyAxisMotorStalled(drive); break; case KinematicsType::coreXYU: // Both X and Y motors are involved in homing X or Y, and both U and V motors are involved in homing U motorIsStalled = (drive == X_AXIS || drive == Y_AXIS) - ? AnyMotorStalled(X_AXIS) || AnyMotorStalled(Y_AXIS) + ? AnyAxisMotorStalled(X_AXIS) || AnyAxisMotorStalled(Y_AXIS) : (drive == U_AXIS) - ? AnyMotorStalled(U_AXIS) || AnyMotorStalled(V_AXIS) - : AnyMotorStalled(drive); + ? AnyAxisMotorStalled(U_AXIS) || AnyAxisMotorStalled(V_AXIS) + : AnyAxisMotorStalled(drive); break; case KinematicsType::coreXZ: // Both X and Z motors are involved in homing X or Z motorIsStalled = (drive == X_AXIS || drive == Z_AXIS) - ? AnyMotorStalled(X_AXIS) || AnyMotorStalled(Z_AXIS) - : AnyMotorStalled(drive); + ? AnyAxisMotorStalled(X_AXIS) || AnyAxisMotorStalled(Z_AXIS) + : AnyAxisMotorStalled(drive); break; default: - motorIsStalled = AnyMotorStalled(drive); + motorIsStalled = AnyAxisMotorStalled(drive); break; } return (!motorIsStalled) ? EndStopHit::noStop @@ -2734,14 +2741,17 @@ EndStopHit Platform::Stopped(size_t drive) const #endif case EndStopInputType::activeLow: + if (endStopPins[drive] != NoPin) + { + const bool b = IoPort::ReadPin(endStopPins[drive]); + return (b) ? EndStopHit::noStop : (endStopPos[drive] == EndStopPosition::highEndStop) ? EndStopHit::highHit : EndStopHit::lowHit; + } + break; + case EndStopInputType::activeHigh: if (endStopPins[drive] != NoPin) { - bool b = IoPort::ReadPin(endStopPins[drive]); - if (endStopInputType[drive] == EndStopInputType::activeHigh) - { - b = !b; - } + const bool b = !IoPort::ReadPin(endStopPins[drive]); return (b) ? EndStopHit::noStop : (endStopPos[drive] == EndStopPosition::highEndStop) ? EndStopHit::highHit : EndStopHit::lowHit; } break; @@ -2750,12 +2760,13 @@ EndStopHit Platform::Stopped(size_t drive) const break; } } +#if HAS_STALL_DETECT else if (drive < DRIVES) { - // Endstop not used for an axis, so no configuration data available. - // To allow us to see its status in DWC, pretend it is configured as a high-end active high endstop. - return (endStopPins[drive] != NoPin && IoPort::ReadPin(endStopPins[drive])) ? EndStopHit::highHit : EndStopHit::noStop; + // Endstop is for an extruder drive, so use stall detection + return (ExtruderMotorStalled(drive - numAxes)) ? EndStopHit::highHit : EndStopHit::noStop; } +#endif return EndStopHit::noStop; } @@ -4269,7 +4280,7 @@ bool Platform::ConfigureStallDetection(GCodeBuffer& gb, StringRef& reply) { uint32_t drives[DRIVES]; size_t dCount = DRIVES; - gb.GetUnsignedArray(drives, dCount); + gb.GetUnsignedArray(drives, dCount, false); for (size_t i = 0; i < dCount; i++) { if (drives[i] >= numSmartDrivers) @@ -4469,7 +4480,7 @@ void STEP_TC_HANDLER() { const irqflags_t flags = cpu_irq_save(); const int32_t diff = (int32_t)(tim - GetInterruptClocks()); // see how long we have to go - if (diff < (int32_t)DDA::minInterruptInterval) // if less than about 6us or already passed + if (diff < (int32_t)DDA::MinInterruptInterval) // if less than about 6us or already passed { cpu_irq_restore(flags); return true; // tell the caller to simulate an interrupt instead @@ -4502,7 +4513,7 @@ void STEP_TC_HANDLER() { const irqflags_t flags = cpu_irq_save(); const int32_t diff = (int32_t)(tim - GetInterruptClocks()); // see how long we have to go - if (diff < (int32_t)DDA::minInterruptInterval) // if less than about 6us or already passed + if (diff < (int32_t)DDA::MinInterruptInterval) // if less than about 6us or already passed { cpu_irq_restore(flags); return true; // tell the caller to simulate an interrupt instead diff --git a/src/Platform.h b/src/Platform.h index 7eb03ff0..e52abb9c 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -613,10 +613,11 @@ private: float AdcReadingToCpuTemperature(uint32_t reading) const; #if HAS_SMART_DRIVERS - void ReportDrivers(DriversBitmap whichDrivers, const char* text, bool& reported); + void ReportDrivers(MessageType mt, DriversBitmap whichDrivers, const char* text, bool& reported); #endif #if HAS_STALL_DETECT - bool AnyMotorStalled(size_t drive) const pre(drive < DRIVES); + bool AnyAxisMotorStalled(size_t drive) const pre(drive < DRIVES); + bool ExtruderMotorStalled(size_t extruder) const pre(extruder < MaxExtruders); #endif #if SAM4E || SAM4S || SAME70 diff --git a/src/PortControl.cpp b/src/PortControl.cpp index 7c8a595a..def53b56 100644 --- a/src/PortControl.cpp +++ b/src/PortControl.cpp @@ -75,7 +75,7 @@ bool PortControl::Configure(GCodeBuffer& gb, StringRef& reply) numConfiguredPorts = 0; uint32_t portNumbers[MaxPorts]; size_t numPorts = MaxPorts; - gb.GetUnsignedArray(portNumbers, numPorts); + gb.GetUnsignedArray(portNumbers, numPorts, false); for (size_t i = 0; i < numPorts; ++i) { const uint32_t pnum = portNumbers[i]; diff --git a/src/PrintMonitor.cpp b/src/PrintMonitor.cpp index 16b2e168..b4605fb9 100644 --- a/src/PrintMonitor.cpp +++ b/src/PrintMonitor.cpp @@ -400,47 +400,41 @@ bool PrintMonitor::GetFileInfo(const char *directory, const char *fileName, GCod // Look for slicer program if (parsedFileInfo.generatedBy[0] == 0) { - // Slic3r and S3D - const char* generatedByString = "generated by "; - const char* introString = ""; - const char* pos = strstr(buf, generatedByString); - if (pos != nullptr) + static const char * const GeneratedByStrings[] = + { + "generated by ", // slic3r and S3D + ";Sliced by ", // ideaMaker + "; KISSlicer", // KISSlicer + ";Sliced at: ", // Cura (old) + ";Generated with " // Cura (new) + }; + + size_t index; + const char* pos = nullptr; + for (index = 0; pos == nullptr && index < ARRAY_SIZE(GeneratedByStrings); ++index) { - pos += strlen(generatedByString); + pos = strstr(buf, GeneratedByStrings[index]); } - else + + if (pos != nullptr) { - // KISSlicer - pos = strstr(buf, "; KISSlicer"); - if (pos != nullptr) + const char* introString = ""; + switch (index) { + default: + pos += strlen(GeneratedByStrings[index]); + break; + + case 2: // KISSlicer pos += 2; + break; + + case 3: // Cura (old) + introString = "Cura at "; + pos += strlen(GeneratedByStrings[index]); + break; } - else - { - // Cura (old) - const char* slicedAtString = ";Sliced at: "; - pos = strstr(buf, slicedAtString); - if (pos != nullptr) - { - pos += strlen(slicedAtString); - introString = "Cura at "; - } - else - { - // Cura (new) - const char* generatedWithString = ";Generated with "; - pos = strstr(buf, generatedWithString); - if (pos != nullptr) - { - pos += strlen(generatedWithString); - } - } - } - } - if (pos != nullptr) - { strcpy(parsedFileInfo.generatedBy, introString); size_t i = strlen(introString); while (i < ARRAY_SIZE(parsedFileInfo.generatedBy) - 1 && *pos >= ' ') diff --git a/src/RepRap.cpp b/src/RepRap.cpp index 78eb9ac8..771b10c7 100644 --- a/src/RepRap.cpp +++ b/src/RepRap.cpp @@ -47,7 +47,7 @@ extern "C" void hsmciIdle() if (reprap.GetSpinningModule() != moduleFilamentSensors) { - FilamentSensor::Spin(false); + FilamentMonitor::Spin(false); } } @@ -223,7 +223,7 @@ void RepRap::Spin() ticksInSpinState = 0; spinningModule = moduleFilamentSensors; - FilamentSensor::Spin(true); + FilamentMonitor::Spin(true); ticksInSpinState = 0; spinningModule = noModule; @@ -272,7 +272,7 @@ void RepRap::Diagnostics(MessageType mtype) heat->Diagnostics(mtype); gCodes->Diagnostics(mtype); network->Diagnostics(mtype); - FilamentSensor::Diagnostics(mtype); + FilamentMonitor::Diagnostics(mtype); } // Turn off the heaters, disable the motors, and deactivate the Heat and Move classes. Leave everything else working. diff --git a/src/RepRapFirmware.h b/src/RepRapFirmware.h index 72f3a8a2..bfaac796 100644 --- a/src/RepRapFirmware.h +++ b/src/RepRapFirmware.h @@ -81,7 +81,7 @@ class OutputBuffer; class OutputStack; class GCodeBuffer; class GCodeQueue; -class FilamentSensor; +class FilamentMonitor; class RandomProbePointSet; class Logger; diff --git a/src/Version.h b/src/Version.h index d2599956..a2c7704f 100644 --- a/src/Version.h +++ b/src/Version.h @@ -9,11 +9,11 @@ #define SRC_VERSION_H_ #ifndef VERSION -# define VERSION "1.20.1RC2" +# define VERSION "1.21RC0" #endif #ifndef DATE -# define DATE "2018-01-01" +# define DATE "2018-01-11" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman" |