Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2018-01-14 13:50:40 +0300
committerDavid Crocker <dcrocker@eschertech.com>2018-01-22 23:11:10 +0300
commitcc8de620887759dbb6ade8b5b0bffc6e9194222e (patch)
tree81ffec739da90acfd0d4eddfaf4e1d0abf95b7b1 /src
parente2f506e855785d0f0d9786f1285deebf1d51bad8 (diff)
Version 1.21RC0
Fix stepper driver warning/error messages Reduce SPI speed on Duet Ethernet M350 Enn with just one E value given is now applied to all extruders Corrected nonlinear extrusion steps calculation Refactored the filament monitor support Added laser filament monitor support Added pulse-type filament monitor support Corrected display of M574 endstop type when the type is active low Added support for extruder "endstops" using motor load detection M305 Pn with no other parameters no longer allocates a thermistor to the heater
Diffstat (limited to 'src')
-rw-r--r--src/Fan.cpp2
-rw-r--r--src/FilamentMonitors/Duet3DFilamentMonitor.cpp207
-rw-r--r--src/FilamentMonitors/Duet3DFilamentMonitor.h56
-rw-r--r--src/FilamentMonitors/FilamentMonitor.cpp (renamed from src/FilamentSensors/FilamentSensor.cpp)58
-rw-r--r--src/FilamentMonitors/FilamentMonitor.h (renamed from src/FilamentSensors/FilamentSensor.h)22
-rw-r--r--src/FilamentMonitors/LaserFilamentMonitor.cpp356
-rw-r--r--src/FilamentMonitors/LaserFilamentMonitor.h81
-rw-r--r--src/FilamentMonitors/PulsedFilamentMonitor.cpp251
-rw-r--r--src/FilamentMonitors/PulsedFilamentMonitor.h60
-rw-r--r--src/FilamentMonitors/RotatingMagnetFilamentMonitor.cpp300
-rw-r--r--src/FilamentMonitors/RotatingMagnetFilamentMonitor.h69
-rw-r--r--src/FilamentMonitors/SimpleFilamentMonitor.cpp (renamed from src/FilamentSensors/SimpleFilamentSensor.cpp)20
-rw-r--r--src/FilamentMonitors/SimpleFilamentMonitor.h (renamed from src/FilamentSensors/SimpleFilamentSensor.h)14
-rw-r--r--src/FilamentSensors/Duet3DFilamentSensor.cpp434
-rw-r--r--src/FilamentSensors/Duet3DFilamentSensor.h83
-rw-r--r--src/GCodes/GCodeBuffer.cpp36
-rw-r--r--src/GCodes/GCodeBuffer.h4
-rw-r--r--src/GCodes/GCodes.cpp67
-rw-r--r--src/GCodes/GCodes.h2
-rw-r--r--src/GCodes/GCodes2.cpp36
-rw-r--r--src/GCodes/GCodes3.cpp8
-rw-r--r--src/Movement/DDA.cpp26
-rw-r--r--src/Movement/DDA.h19
-rw-r--r--src/Movement/DriveMovement.cpp12
-rw-r--r--src/Movement/Move.cpp26
-rw-r--r--src/Movement/Move.h3
-rw-r--r--src/Network2/ESP8266/Network.cpp2
-rw-r--r--src/Network2/W5500/Network.cpp3
-rw-r--r--src/Network2/W5500/Wiznet/Ethernet/WizSpi.cpp9
-rw-r--r--src/Platform.cpp71
-rw-r--r--src/Platform.h5
-rw-r--r--src/PortControl.cpp2
-rw-r--r--src/PrintMonitor.cpp62
-rw-r--r--src/RepRap.cpp6
-rw-r--r--src/RepRapFirmware.h2
-rw-r--r--src/Version.h4
36 files changed, 1692 insertions, 726 deletions
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"