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
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2020-02-16 17:10:43 +0300
committerDavid Crocker <dcrocker@eschertech.com>2020-02-16 17:10:43 +0300
commit315f4f47613fe09e613b6c639a7ddc37af3516d2 (patch)
tree45133ce063bf3bb2c40f522471f0074038168149 /src/Heating
parent9885ed7cc55cb19fde1be14c8adfd11dfa7f86cf (diff)
Replaced HeaterProtection instances by HeaterMonitors
Diffstat (limited to 'src/Heating')
-rw-r--r--src/Heating/FOPDT.cpp2
-rw-r--r--src/Heating/FOPDT.h2
-rw-r--r--src/Heating/Heat.cpp333
-rw-r--r--src/Heating/Heat.h21
-rw-r--r--src/Heating/Heater.cpp242
-rw-r--r--src/Heating/Heater.h39
-rw-r--r--src/Heating/HeaterMonitor.cpp99
-rw-r--r--src/Heating/HeaterMonitor.h81
-rw-r--r--src/Heating/HeaterProtection.cpp71
-rw-r--r--src/Heating/HeaterProtection.h96
-rw-r--r--src/Heating/LocalHeater.cpp76
-rw-r--r--src/Heating/LocalHeater.h37
-rw-r--r--src/Heating/RemoteHeater.cpp32
-rw-r--r--src/Heating/RemoteHeater.h3
-rw-r--r--src/Heating/Sensors/TemperatureSensor.h1
15 files changed, 522 insertions, 613 deletions
diff --git a/src/Heating/FOPDT.cpp b/src/Heating/FOPDT.cpp
index 95f4ec54..c2347cf1 100644
--- a/src/Heating/FOPDT.cpp
+++ b/src/Heating/FOPDT.cpp
@@ -138,7 +138,7 @@ void FopDt::CalcPidConstants() noexcept
#if SUPPORT_CAN_EXPANSION
-void FopDt::SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModel& msg) noexcept
+void FopDt::SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModel& msg) const noexcept
{
msg.heater = heater;
msg.gain = gain;
diff --git a/src/Heating/FOPDT.h b/src/Heating/FOPDT.h
index 0a7c033e..fa911f9c 100644
--- a/src/Heating/FOPDT.h
+++ b/src/Heating/FOPDT.h
@@ -65,7 +65,7 @@ public:
#endif
#if SUPPORT_CAN_EXPANSION
- void SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModel& msg) noexcept;
+ void SetupCanMessage(unsigned int heater, CanMessageUpdateHeaterModel& msg) const noexcept;
#endif
private:
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp
index 914dbfde..f1d6e1ec 100644
--- a/src/Heating/Heat.cpp
+++ b/src/Heating/Heat.cpp
@@ -20,7 +20,7 @@ Licence: GPL
#include "Heat.h"
#include "LocalHeater.h"
-#include "HeaterProtection.h"
+#include "HeaterMonitor.h"
#include "Platform.h"
#include "RepRap.h"
#include "Sensors/TemperatureSensor.h"
@@ -67,13 +67,6 @@ constexpr ObjectModelArrayDescriptor Heat::heatersArrayDescriptor =
[] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Heat*)self)->heaters[context.GetLastIndex()]); }
};
-constexpr ObjectModelArrayDescriptor Heat::sensorsArrayDescriptor =
-{
- &sensorsLock,
- [] (const ObjectModel *self, const ObjectExplorationContext&) noexcept -> size_t { return ((const Heat*)self)->GetNumSensorsToReport(); },
- [] (const ObjectModel *self, ObjectExplorationContext& context) noexcept -> ExpressionValue { return ExpressionValue(((const Heat*)self)->FindSensor(context.GetLastIndex()).Ptr()); }
-};
-
// Macro to build a standard lambda function that includes the necessary type conversions
#define OBJECT_MODEL_FUNC(...) OBJECT_MODEL_FUNC_BODY(Heat, __VA_ARGS__)
@@ -84,10 +77,9 @@ constexpr ObjectModelTableEntry Heat::objectModelTable[] =
{ "coldExtrudeTemperature", OBJECT_MODEL_FUNC(self->extrusionMinTemp, 1), ObjectModelEntryFlags::none},
{ "coldRetractTemperature", OBJECT_MODEL_FUNC(self->retractionMinTemp, 1), ObjectModelEntryFlags::none},
{ "heaters", OBJECT_MODEL_FUNC_NOSELF(&heatersArrayDescriptor), ObjectModelEntryFlags::live },
- { "sensors", OBJECT_MODEL_FUNC_NOSELF(&sensorsArrayDescriptor), ObjectModelEntryFlags::live },
};
-constexpr uint8_t Heat::objectModelTableDescriptor[] = { 1, 4 };
+constexpr uint8_t Heat::objectModelTableDescriptor[] = { 1, 3 };
DEFINE_GET_OBJECT_MODEL_TABLE(Heat)
@@ -112,11 +104,6 @@ Heat::Heat() noexcept
h = -1;
}
- for (size_t index : ARRAY_INDICES(heaterProtections))
- {
- heaterProtections[index] = new HeaterProtection(index);
- }
-
for (Heater*& h : heaters)
{
h = nullptr;
@@ -142,58 +129,7 @@ GCodeResult Heat::SetOrReportHeaterModel(GCodeBuffer& gb, const StringRef& reply
const auto h = FindHeater(heater);
if (h.IsNotNull())
{
- const FopDt& model = h->GetModel();
- bool seen = false;
- float gain = model.GetGain(),
- tc = model.GetTimeConstant(),
- td = model.GetDeadTime(),
- maxPwm = model.GetMaxPwm(),
- voltage = model.GetVoltage();
- int32_t dontUsePid = model.UsePid() ? 0 : 1;
- int32_t inversionParameter = 0;
-
- gb.TryGetFValue('A', gain, seen);
- gb.TryGetFValue('C', tc, seen);
- gb.TryGetFValue('D', td, seen);
- gb.TryGetIValue('B', dontUsePid, seen);
- gb.TryGetFValue('S', maxPwm, seen);
- gb.TryGetFValue('V', voltage, seen);
- gb.TryGetIValue('I', inversionParameter, seen);
-
- if (seen)
- {
- const bool inverseTemperatureControl = (inversionParameter == 1 || inversionParameter == 3);
- const GCodeResult rslt = h->SetModel(gain, tc, td, maxPwm, voltage, dontUsePid == 0, inverseTemperatureControl, reply);
- if (rslt != GCodeResult::ok)
- {
- return rslt;
- }
- }
- else if (!model.IsEnabled())
- {
- reply.printf("Heater %u is disabled", heater);
- }
- else
- {
- const char* const mode = (!model.UsePid()) ? "bang-bang"
- : (model.ArePidParametersOverridden()) ? "custom PID"
- : "PID";
- reply.printf("Heater %u model: gain %.1f, time constant %.1f, dead time %.1f, max PWM %.2f, calibration voltage %.1f, mode %s", heater,
- (double)model.GetGain(), (double)model.GetTimeConstant(), (double)model.GetDeadTime(), (double)model.GetMaxPwm(), (double)model.GetVoltage(), mode);
- if (model.IsInverted())
- {
- reply.cat(", inverted temperature control");
- }
- if (model.UsePid())
- {
- // When reporting the PID parameters, we scale them by 255 for compatibility with older firmware and other firmware
- M301PidParameters params = model.GetM301PidParameters(false);
- reply.catf("\nComputed PID parameters for setpoint change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD);
- params = model.GetM301PidParameters(true);
- reply.catf("\nComputed PID parameters for load change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD);
- }
- }
- return GCodeResult::ok;
+ return h->SetOrReportModel(heater, gb, reply);
}
reply.printf("Heater %u not found", heater);
@@ -299,15 +235,6 @@ void Heat::ResetHeaterModels() noexcept
void Heat::Init() noexcept
{
- // Initialise the heater protection items first
- for (size_t index : ARRAY_INDICES(heaterProtections))
- {
- HeaterProtection * const prot = heaterProtections[index];
-
- const float tempLimit = (IsBedOrChamberHeater(index)) ? DefaultBedTemperatureLimit : DefaultHotEndTemperatureLimit;
- prot->Init(tempLimit);
- }
-
extrusionMinTemp = HOT_ENOUGH_TO_EXTRUDE;
retractionMinTemp = HOT_ENOUGH_TO_RETRACT;
coldExtrude = false;
@@ -635,12 +562,21 @@ HeaterStatus Heat::GetStatus(int heater) const noexcept
void Heat::SetBedHeater(size_t index, int heater) noexcept
{
- const auto h = FindHeater(bedHeaters[index]);
- if (h.IsNotNull())
{
- h->SwitchOff();
+ const auto h = FindHeater(bedHeaters[index]);
+ if (h.IsNotNull())
+ {
+ h->SwitchOff();
+ }
}
bedHeaters[index] = heater;
+ {
+ const auto h = FindHeater(bedHeaters[index]);
+ if (h.IsNotNull())
+ {
+ h->SetDefaultMonitors();
+ }
+ }
}
bool Heat::IsBedHeater(int heater) const noexcept
@@ -657,12 +593,19 @@ bool Heat::IsBedHeater(int heater) const noexcept
void Heat::SetChamberHeater(size_t index, int heater) noexcept
{
+ {
+ const auto h = FindHeater(chamberHeaters[index]);
+ if (h.IsNotNull())
+ {
+ h->SwitchOff();
+ }
+ }
+ chamberHeaters[index] = heater;
const auto h = FindHeater(chamberHeaters[index]);
if (h.IsNotNull())
{
- h->SwitchOff();
+ h->SetDefaultMonitors();
}
- chamberHeaters[index] = heater;
}
bool Heat::IsChamberHeater(int heater) const noexcept
@@ -677,12 +620,12 @@ bool Heat::IsChamberHeater(int heater) const noexcept
return false;
}
-void Heat::SetActiveTemperature(int heater, float t) noexcept
+void Heat::SetTemperature(int heater, float t, bool activeNotStandby) THROWS(GCodeException)
{
const auto h = FindHeater(heater);
if (h.IsNotNull())
{
- h->SetActiveTemperature(t);
+ h->SetTemperature(t, activeNotStandby);
}
}
@@ -692,15 +635,6 @@ float Heat::GetActiveTemperature(int heater) const noexcept
return (h.IsNull()) ? ABS_ZERO : h->GetActiveTemperature();
}
-void Heat::SetStandbyTemperature(int heater, float t) noexcept
-{
- const auto h = FindHeater(heater);
- if (h.IsNotNull())
- {
- h->SetStandbyTemperature(t);
- }
-}
-
float Heat::GetStandbyTemperature(int heater) const noexcept
{
const auto h = FindHeater(heater);
@@ -709,42 +643,14 @@ float Heat::GetStandbyTemperature(int heater) const noexcept
float Heat::GetHighestTemperatureLimit(int heater) const noexcept
{
- float limit = BadErrorTemperature;
- if (heater >= 0 && heater < (int)MaxHeaters)
- {
- for (const HeaterProtection *prot : heaterProtections)
- {
- if (prot->GetHeater() == heater && prot->GetTrigger() == HeaterProtectionTrigger::TemperatureExceeded)
- {
- const float t = prot->GetTemperatureLimit();
- if (limit == BadErrorTemperature || t > limit)
- {
- limit = t;
- }
- }
- }
- }
- return limit;
+ const auto h = FindHeater(heater);
+ return (h.IsNull()) ? BadErrorTemperature : h->GetHighestTemperatureLimit();
}
float Heat::GetLowestTemperatureLimit(int heater) const noexcept
{
- float limit = ABS_ZERO;
- if (heater >= 0 && heater < (int)MaxHeaters)
- {
- for (const HeaterProtection *prot : heaterProtections)
- {
- if (prot->GetHeater() == heater && prot->GetTrigger() == HeaterProtectionTrigger::TemperatureTooLow)
- {
- const float t = prot->GetTemperatureLimit();
- if (limit == ABS_ZERO || t < limit)
- {
- limit = t;
- }
- }
- }
- }
- return limit;
+ const auto h = FindHeater(heater);
+ return (h.IsNull()) ? ABS_ZERO : h->GetLowestTemperatureLimit();
}
// Get the current temperature of a real or virtual heater
@@ -831,14 +737,15 @@ bool Heat::IsBedOrChamberHeater(int heater) const noexcept
float Heat::GetHighestTemperatureLimit() const noexcept
{
float limit = ABS_ZERO;
- for (HeaterProtection *prot : heaterProtections)
+ ReadLocker lock(heatersLock);
+ for (const Heater *h : heaters)
{
- if (prot->GetHeater() >= 0 && prot->GetTrigger() == HeaterProtectionTrigger::TemperatureExceeded)
+ if (h != nullptr)
{
- const float t = prot->GetTemperatureLimit();
- if (t > limit)
+ const float tlimit = h->GetHighestTemperatureLimit();
+ if (tlimit > limit)
{
- limit = t;
+ limit = tlimit;
}
}
}
@@ -903,11 +810,7 @@ GCodeResult Heat::TuneHeater(GCodeBuffer& gb, const StringRef& reply) THROWS(GCo
gb.MustSee('S');
const float temperature = gb.GetFValue();
const float maxPwm = (gb.Seen('P')) ? gb.GetFValue() : h->GetModel().GetMaxPwm();
- if (!h->CheckGood())
- {
- reply.copy("Heater is not ready to perform PID auto-tuning");
- }
- else if (maxPwm < 0.1 || maxPwm > 1.0)
+ if (maxPwm < 0.1 || maxPwm > 1.0)
{
reply.copy("Invalid PWM value");
}
@@ -916,8 +819,7 @@ GCodeResult Heat::TuneHeater(GCodeBuffer& gb, const StringRef& reply) THROWS(GCo
if (heaterBeingTuned == -1)
{
heaterBeingTuned = (int8_t)heater;
- h->StartAutoTune(temperature, maxPwm, reply);
- return GCodeResult::ok;
+ return h->StartAutoTune(temperature, maxPwm, reply);
}
else
{
@@ -1042,164 +944,17 @@ const char *Heat::GetHeaterSensorName(size_t heater) const noexcept
}
// Configure heater protection (M143). Returns true if an error occurred
-GCodeResult Heat::SetHeaterProtection(GCodeBuffer& gb, const StringRef& reply)
+GCodeResult Heat::HandleM143(GCodeBuffer& gb, const StringRef& reply)
{
- WriteLocker lock(heatersLock);
-
- bool seen = false;
- int32_t heaterNumber = 1; // default to extruder 1 if no heater number provided
- gb.TryGetIValue('H', heaterNumber, seen);
- const int index = (gb.Seen('P')) ? gb.GetIValue() : heaterNumber;
-
- if ( index < 0
- || (index >= (int)MaxHeaters && index < (int)FirstExtraHeaterProtection)
- || index >= (int)(FirstExtraHeaterProtection + MaxExtraHeaterProtections)
- )
+ const size_t heaterNumber = (gb.Seen('H')) ? gb.GetLimitedUIValue('H', MaxHeaters) : 1;
+ const auto h = FindHeater(heaterNumber);
+ if (h.IsNull())
{
- reply.printf("Invalid heater protection item '%d'", index);
+ reply.printf("Heater %u does not exist", heaterNumber);
return GCodeResult::error;
}
- HeaterProtection &item = (index >= (int)FirstExtraHeaterProtection)
- ? *heaterProtections[index - FirstExtraHeaterProtection + MaxHeaters]
- : *heaterProtections[index];
- // Set heater to control
- if (seen && heaterNumber != item.GetHeater())
- {
- const int oldHeaterNumber = item.GetHeater();
- item.SetHeater(heaterNumber);
- UpdateHeaterProtection(oldHeaterNumber);
- UpdateHeaterProtection(heaterNumber);
- }
-
- // Set sensor that supervises the heater
- if (gb.Seen('X'))
- {
- item.SetSensorNumber(gb.GetIValue());
- seen = true;
- }
-
- // Set trigger action
- if (gb.Seen('A'))
- {
- const int action = gb.GetIValue();
- if (action < 0 || action > (int)MaxHeaterProtectionAction)
- {
- reply.printf("Invalid heater protection action '%d'", action);
- }
-
- seen = true;
- item.SetAction(static_cast<HeaterProtectionAction>(action));
- }
-
- // Set trigger condition
- if (gb.Seen('C'))
- {
- const int trigger = gb.GetIValue();
- if (trigger < 0 || trigger > (int)MaxHeaterProtectionTrigger)
- {
- reply.printf("Invalid heater protection trigger '%d'", trigger);
- }
-
- seen = true;
- item.SetTrigger(static_cast<HeaterProtectionTrigger>(trigger));
- }
-
- // Set temperature limit
- if (gb.Seen('S'))
- {
- const float limit = gb.GetFValue();
- if (limit <= BadLowTemperature || limit >= BadErrorTemperature)
- {
- reply.copy("Invalid temperature limit");
- return GCodeResult::error;
- }
-
- seen = true;
- item.SetTemperatureLimit(limit);
- }
-
- // Report current parameters
- if (!seen)
- {
- if (item.GetHeater() < 0)
- {
- reply.printf("Temperature protection item %d is not configured", index);
- }
- else
- {
- const char *actionString, *triggerString;
- switch (item.GetAction())
- {
- case HeaterProtectionAction::GenerateFault:
- actionString = "generate a heater fault";
- break;
- case HeaterProtectionAction::PermanentSwitchOff:
- actionString = "permanently switch off";
- break;
- case HeaterProtectionAction::TemporarySwitchOff:
- actionString = "temporarily switch off";
- break;
- default:
- actionString = "(undefined)";
- break;
- }
-
- switch (item.GetTrigger())
- {
- case HeaterProtectionTrigger::TemperatureExceeded:
- triggerString = "exceeds";
- break;
- case HeaterProtectionTrigger::TemperatureTooLow:
- triggerString = "falls below";
- break;
- default:
- triggerString = "(undefined)";
- break;
- }
-
- reply.printf("Temperature protection item %d is configured for heater %d and uses sensor %d to %s if the temperature %s %.1f" DEGREE_SYMBOL "C",
- index, item.GetHeater(), item.GetSensorNumber(), actionString, triggerString, (double)item.GetTemperatureLimit());
- }
- }
-
- return GCodeResult::ok;
-}
-
-// Updates the PIDs and HeaterProtection items after a heater change. Caller must already have a write lock on the heaters.
-void Heat::UpdateHeaterProtection(int heaterNumber) noexcept
-{
- auto h = FindHeater(heaterNumber);
- if (h.IsNotNull())
- {
- // Rebuild linked lists
- h->SetHeaterProtection(nullptr);
- HeaterProtection *firstProtectionItem = nullptr;
- HeaterProtection *lastElementInList = nullptr;
- for (HeaterProtection *prot : heaterProtections)
- {
- if (prot->GetHeater() == heaterNumber)
- {
- if (firstProtectionItem == nullptr)
- {
- firstProtectionItem = prot;
- prot->SetNext(nullptr);
- }
- else if (lastElementInList == nullptr)
- {
- firstProtectionItem->SetNext(prot);
- lastElementInList = prot;
- }
- else
- {
- lastElementInList->SetNext(prot);
- lastElementInList = prot;
- }
- }
- }
-
- h->SetHeaterProtection(firstProtectionItem);
- }
+ return h->ConfigureMonitor(gb, reply);
}
// Get the temperature of a sensor
diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h
index 6878bb8e..3789a721 100644
--- a/src/Heating/Heat.h
+++ b/src/Heating/Heat.h
@@ -34,7 +34,7 @@ Licence: GPL
#include <RTOSIface/RTOSIface.h>
class TemperatureSensor;
-class HeaterProtection;
+class HeaterMonitor;
class GCodeBuffer;
class CanMessageSensorTemperatures;
class CanMessageHeatersStatus;
@@ -43,6 +43,7 @@ class Heat INHERIT_OBJECT_MODEL
{
public:
Heat() noexcept;
+ Heat(const Heat&) = delete;
// Methods that don't relate to a particular heater
void HeaterTask() noexcept;
@@ -79,9 +80,7 @@ public:
GCodeResult TuneHeater(GCodeBuffer& gb, const StringRef& reply) THROWS_GCODE_EXCEPTION;
GCodeResult ConfigureSensor(GCodeBuffer& gb, const StringRef& reply) THROWS_GCODE_EXCEPTION; // Create a sensor or change the parameters for an existing sensor
GCodeResult SetPidParameters(unsigned int heater, GCodeBuffer& gb, const StringRef& reply) THROWS_GCODE_EXCEPTION; // Set the P/I/D parameters for a heater
- GCodeResult SetHeaterProtection(GCodeBuffer &gb, const StringRef &reply) THROWS_GCODE_EXCEPTION; // Configure heater protection (M143)
-
- void UpdateHeaterProtection(int heaterNumber) noexcept; // Updates the PIDs and HeaterProtection items when a heater is remapped
+ GCodeResult HandleM143(GCodeBuffer &gb, const StringRef &reply) THROWS_GCODE_EXCEPTION; // Configure heater protection (M143)
void SensorsTask() noexcept;
static void EnsureSensorsTask() noexcept;
@@ -119,16 +118,16 @@ public:
float GetStandbyTemperature(int heater) const noexcept;
float GetHighestTemperatureLimit(int heater) const noexcept;
float GetLowestTemperatureLimit(int heater) const noexcept;
- float GetHeaterTemperature(int heater) const noexcept; // Get the current temperature of a heater
float GetTargetTemperature(int heater) const noexcept; // Get the target temperature
+ float GetHeaterTemperature(int heater) const noexcept; // Get the current temperature of a heater
HeaterStatus GetStatus(int heater) const noexcept; // Get the off/standby/active status
bool HeaterAtSetTemperature(int heater, bool waitWhenCooling, float tolerance) const noexcept;
GCodeResult ConfigureHeater(size_t heater, GCodeBuffer& gb, const StringRef& reply);
GCodeResult ConfigureHeaterMonitoring(size_t heater, GCodeBuffer& gb, const StringRef& reply);
- void SetActiveTemperature(int heater, float t) noexcept;
- void SetStandbyTemperature(int heater, float t) noexcept;
+ void SetActiveTemperature(int heater, float t) THROWS(GCodeException) { SetTemperature(heater, t, true); }
+ void SetStandbyTemperature(int heater, float t) THROWS(GCodeException) { SetTemperature(heater, t, false); }
GCodeResult Activate(int heater, const StringRef& reply) noexcept; // Turn on a heater
void Standby(int heater, const Tool* tool) noexcept; // Set a heater to standby
void SwitchOff(int heater) noexcept; // Turn off a specific heater
@@ -143,24 +142,22 @@ public:
void ProcessRemoteHeatersReport(CanAddress src, const CanMessageHeatersStatus& msg) noexcept;
#endif
+ static ReadWriteLock sensorsLock; // needs to be public so that the OMT in EndstopsManager can lock it
+
protected:
DECLARE_OBJECT_MODEL
OBJECT_MODEL_ARRAY(heaters)
- OBJECT_MODEL_ARRAY(sensors)
private:
- Heat(const Heat&) = delete; // Private copy constructor to prevent copying
-
ReadLockedPointer<Heater> FindHeater(int heater) const noexcept;
void DeleteSensor(unsigned int sn) noexcept;
void InsertSensor(TemperatureSensor *newSensor) noexcept;
+ void SetTemperature(int heater, float t, bool activeNotStandby) THROWS(GCodeException);
static ReadWriteLock heatersLock;
- static ReadWriteLock sensorsLock;
uint8_t volatile sensorCount;
TemperatureSensor * volatile sensorsRoot; // The sensor list
- HeaterProtection *heaterProtections[MaxHeaters + MaxExtraHeaterProtections]; // Heater protection instances to guarantee legal heater temperature ranges
Heater* heaters[MaxHeaters]; // A local or remote heater
const Tool* lastStandbyTools[MaxHeaters]; // The last tool that caused the corresponding heater to be set to standby
diff --git a/src/Heating/Heater.cpp b/src/Heating/Heater.cpp
index a3a64add..24b10302 100644
--- a/src/Heating/Heater.cpp
+++ b/src/Heating/Heater.cpp
@@ -9,8 +9,10 @@
#include "RepRap.h"
#include "Platform.h"
#include "Heat.h"
-#include "HeaterProtection.h"
+#include "HeaterMonitor.h"
#include "Sensors/TemperatureSensor.h"
+#include <GCodes/GCodeBuffer/GCodeBuffer.h>
+#include <GCodes/GCodeException.h>
#if SUPPORT_OBJECT_MODEL
@@ -41,19 +43,100 @@ DEFINE_GET_OBJECT_MODEL_TABLE(Heater)
Heater::Heater(unsigned int num) noexcept
: heaterNumber(num), sensorNumber(-1), activeTemperature(0.0), standbyTemperature(0.0),
maxTempExcursion(DefaultMaxTempExcursion), maxHeatingFaultTime(DefaultMaxHeatingFaultTime),
- heaterProtection(nullptr), active(false)
+ active(false)
{
}
Heater::~Heater() noexcept
{
+ for (HeaterMonitor& h : monitors)
+ {
+ h.Disable();
+ }
+}
+
+void Heater::SetSensorNumber(int sn) noexcept
+{
+ if (sn != sensorNumber)
+ {
+ sensorNumber = sn;
+ SetDefaultMonitors();
+ }
+}
+
+void Heater::SetDefaultMonitors() noexcept
+{
+ for (HeaterMonitor& h : monitors)
+ {
+ h.Disable();
+ }
+
+ if (sensorNumber >= 0 && sensorNumber < (int)MaxSensors)
+ {
+ const float limit = (reprap.GetHeat().IsBedOrChamberHeater(heaterNumber)) ? DefaultBedTemperatureLimit : DefaultHotEndTemperatureLimit;
+ monitors[0].Set(sensorNumber, limit, HeaterMonitorAction::GenerateFault, HeaterMonitorTrigger::TemperatureExceeded);
+ }
+}
+
+GCodeResult Heater::SetOrReportModel(unsigned int heater, GCodeBuffer& gb, const StringRef& reply) noexcept
+{
+ bool seen = false;
+ float gain = model.GetGain(),
+ tc = model.GetTimeConstant(),
+ td = model.GetDeadTime(),
+ maxPwm = model.GetMaxPwm(),
+ voltage = model.GetVoltage();
+ int32_t dontUsePid = model.UsePid() ? 0 : 1;
+ int32_t inversionParameter = 0;
+
+ gb.TryGetFValue('A', gain, seen);
+ gb.TryGetFValue('C', tc, seen);
+ gb.TryGetFValue('D', td, seen);
+ gb.TryGetIValue('B', dontUsePid, seen);
+ gb.TryGetFValue('S', maxPwm, seen);
+ gb.TryGetFValue('V', voltage, seen);
+ gb.TryGetIValue('I', inversionParameter, seen);
+
+ if (seen)
+ {
+ const bool inverseTemperatureControl = (inversionParameter == 1 || inversionParameter == 3);
+ const GCodeResult rslt = SetModel(gain, tc, td, maxPwm, voltage, dontUsePid == 0, inverseTemperatureControl, reply);
+ if (rslt != GCodeResult::ok)
+ {
+ return rslt;
+ }
+ }
+ else if (!model.IsEnabled())
+ {
+ reply.printf("Heater %u is disabled", heater);
+ }
+ else
+ {
+ const char* const mode = (!model.UsePid()) ? "bang-bang"
+ : (model.ArePidParametersOverridden()) ? "custom PID"
+ : "PID";
+ reply.printf("Heater %u model: gain %.1f, time constant %.1f, dead time %.1f, max PWM %.2f, calibration voltage %.1f, mode %s", heater,
+ (double)model.GetGain(), (double)model.GetTimeConstant(), (double)model.GetDeadTime(), (double)model.GetMaxPwm(), (double)model.GetVoltage(), mode);
+ if (model.IsInverted())
+ {
+ reply.cat(", inverted temperature control");
+ }
+ if (model.UsePid())
+ {
+ // When reporting the PID parameters, we scale them by 255 for compatibility with older firmware and other firmware
+ M301PidParameters params = model.GetM301PidParameters(false);
+ reply.catf("\nComputed PID parameters for setpoint change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD);
+ params = model.GetM301PidParameters(true);
+ reply.catf("\nComputed PID parameters for load change: P%.1f, I%.3f, D%.1f", (double)params.kP, (double)params.kI, (double)params.kD);
+ }
+ }
+ return GCodeResult::ok;
}
// Set the process model returning true if successful
GCodeResult Heater::SetModel(float gain, float tc, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept
{
- const float temperatureLimit = GetHighestTemperatureLimit();
- const bool rslt = model.SetParameters(gain, tc, td, maxPwm, temperatureLimit, voltage, usePid, inverted);
+ const bool rslt = model.SetParameters(gain, tc, td, maxPwm, GetHighestTemperatureLimit(), voltage, usePid, inverted);
if (rslt)
{
if (model.IsEnabled())
@@ -64,7 +147,7 @@ GCodeResult Heater::SetModel(float gain, float tc, float td, float maxPwm, float
return rslt;
}
const float predictedMaxTemp = gain + NormalAmbientTemperature;
- const float noWarnTemp = (temperatureLimit - NormalAmbientTemperature) * 1.5 + 50.0; // allow 50% extra power plus enough for an extra 50C
+ const float noWarnTemp = (GetHighestTemperatureLimit() - NormalAmbientTemperature) * 1.5 + 50.0; // allow 50% extra power plus enough for an extra 50C
if (predictedMaxTemp > noWarnTemp)
{
reply.printf("heater %u appears to be over-powered. If left on at full power, its temperature is predicted to reach %dC.\n",
@@ -90,6 +173,88 @@ GCodeResult Heater::SetFaultDetectionParameters(float pMaxTempExcursion, float p
return UpdateFaultDetectionParameters(reply);
}
+GCodeResult Heater::ConfigureMonitor(GCodeBuffer &gb, const StringRef &reply) THROWS(GCodeException)
+{
+ // Get any parameters that have been provided
+ const bool seenP = gb.Seen('P');
+ const size_t index = (seenP) ? gb.GetLimitedUIValue('P', MaxMonitorsPerHeater) : 0;
+
+ const bool seenSensor = gb.Seen('T');
+ const int monitoringSensor = (seenSensor) ? gb.GetLimitedUIValue('T', MaxSensors) : GetSensorNumber();
+
+ const bool seenAction = gb.Seen('A');
+ const HeaterMonitorAction action = (seenAction)
+ ? static_cast<HeaterMonitorAction>(gb.GetLimitedUIValue('A', (unsigned int)MaxHeaterMonitorAction + 1))
+ : HeaterMonitorAction::GenerateFault;
+
+ const bool seenCondition = gb.Seen('C');
+ const HeaterMonitorTrigger trigger = (seenCondition)
+ ? static_cast<HeaterMonitorTrigger>(gb.GetLimitedIValue('C', -1, (int)MaxHeaterMonitorTrigger))
+ : HeaterMonitorTrigger::TemperatureExceeded;
+
+ const bool seenLimit = gb.Seen('S');
+ const float limit = (seenLimit) ? gb.GetFValue() : monitors[index].GetTemperatureLimit();
+ if (limit <= BadLowTemperature || limit >= BadErrorTemperature)
+ {
+ reply.copy("Invalid temperature limit");
+ return GCodeResult::error;
+ }
+
+ if (seenSensor || seenLimit || seenAction || seenCondition)
+ {
+ monitors[index].Set(monitoringSensor, limit, action, trigger);
+ return UpdateHeaterMonitors(reply);
+ }
+
+ // Else we are reporting on one or all of the monitors
+ if (seenP)
+ {
+ monitors[index].Report(heaterNumber, index, reply);
+ }
+ else
+ {
+ for (size_t i = 0; i < MaxMonitorsPerHeater; ++i)
+ {
+ monitors[i].Report(heaterNumber, i, reply);
+ }
+ }
+ return GCodeResult::ok;
+}
+
+float Heater::GetHighestTemperatureLimit() const noexcept
+{
+ float limit = BadErrorTemperature;
+ for (const HeaterMonitor& prot : monitors)
+ {
+ if (prot.GetTrigger() == HeaterMonitorTrigger::TemperatureExceeded)
+ {
+ const float t = prot.GetTemperatureLimit();
+ if (limit == BadErrorTemperature || t > limit)
+ {
+ limit = t;
+ }
+ }
+ }
+ return limit;
+}
+
+float Heater::GetLowestTemperatureLimit() const noexcept
+{
+ float limit = ABS_ZERO;
+ for (const HeaterMonitor& prot : monitors)
+ {
+ if (prot.GetTrigger() == HeaterMonitorTrigger::TemperatureTooLow)
+ {
+ const float t = prot.GetTemperatureLimit();
+ if (limit == ABS_ZERO || t < limit)
+ {
+ limit = t;
+ }
+ }
+ }
+ return limit;
+}
+
HeaterStatus Heater::GetStatus() const noexcept
{
const HeaterMode mode = GetMode();
@@ -128,19 +293,19 @@ void Heater::Standby() noexcept
}
}
-void Heater::SetActiveTemperature(float t) noexcept
+void Heater::SetTemperature(float t, bool activeNotStandby) THROWS(GCodeException)
{
if (t > GetHighestTemperatureLimit())
{
- reprap.GetPlatform().MessageF(ErrorMessage, "Temperature %.1f" DEGREE_SYMBOL "C too high for heater %u\n", (double)t, GetHeaterNumber());
+ throw GCodeException(-1, -1, "Temperature too high for heater %" PRIu32, (uint32_t)GetHeaterNumber());
}
else if (t < GetLowestTemperatureLimit())
{
- reprap.GetPlatform().MessageF(ErrorMessage, "Temperature %.1f" DEGREE_SYMBOL "C too low for heater %u\n", (double)t, GetHeaterNumber());
+ throw GCodeException(-1, -1, "Temperature too low for heater %" PRIu32, (uint32_t)GetHeaterNumber());
}
else
{
- activeTemperature = t;
+ ((activeNotStandby) ? activeTemperature : standbyTemperature) = t;
if (GetMode() > HeaterMode::suspended && active)
{
String<1> dummy;
@@ -149,66 +314,9 @@ void Heater::SetActiveTemperature(float t) noexcept
}
}
-void Heater::SetStandbyTemperature(float t) noexcept
-{
- if (t > GetHighestTemperatureLimit())
- {
- reprap.GetPlatform().MessageF(ErrorMessage, "Temperature %.1f" DEGREE_SYMBOL "C too high for heater %u\n", (double)t, GetHeaterNumber());
- }
- else if (t < GetLowestTemperatureLimit())
- {
- reprap.GetPlatform().MessageF(ErrorMessage, "Temperature %.1f" DEGREE_SYMBOL "C too low for heater %u\n", (double)t, GetHeaterNumber());
- }
- else
- {
- standbyTemperature = t;
- if (GetMode() > HeaterMode::suspended && !active)
- {
- String<1> dummy;
- (void)SwitchOn(dummy.GetRef());
- }
- }
-}
-
-// Get the highest temperature limit
-float Heater::GetHighestTemperatureLimit() const noexcept
-{
- return reprap.GetHeat().GetHighestTemperatureLimit(GetHeaterNumber());
-}
-
-// Get the lowest temperature limit
-float Heater::GetLowestTemperatureLimit() const noexcept
-{
- return reprap.GetHeat().GetLowestTemperatureLimit(GetHeaterNumber());
-}
-
-void Heater::SetHeaterProtection(HeaterProtection *h) noexcept
-{
- heaterProtection = h;
-}
-
-// Check heater protection elements and return true if everything is good
-bool Heater::CheckProtection() const noexcept
-{
- for (HeaterProtection *prot = heaterProtection; prot != nullptr; prot = prot->Next())
- {
- if (!prot->Check())
- {
- // Something is not right
- return false;
- }
- }
- return true;
-}
-
-bool Heater::CheckGood() const noexcept
-{
- return GetMode() != HeaterMode::fault && CheckProtection();
-}
-
void Heater::SetModelDefaults() noexcept
{
- if (reprap.GetHeat().IsBedOrChamberHeater(GetHeaterNumber()))
+ if (reprap.GetHeat().IsBedOrChamberHeater(heaterNumber))
{
model.SetParameters(DefaultBedHeaterGain, DefaultBedHeaterTimeConstant, DefaultBedHeaterDeadTime, 1.0, DefaultBedTemperatureLimit, 0.0, false, false);
}
diff --git a/src/Heating/Heater.h b/src/Heating/Heater.h
index c237195e..5108d548 100644
--- a/src/Heating/Heater.h
+++ b/src/Heating/Heater.h
@@ -11,6 +11,7 @@
#include <RepRapFirmware.h>
#include <NamedEnum.h>
#include "FOPDT.h"
+#include "HeaterMonitor.h"
#include "GCodes/GCodeResult.h"
#include <ObjectModel/ObjectModel.h>
@@ -18,7 +19,7 @@
# include "CanId.h"
#endif
-class HeaterProtection;
+class HeaterMonitor;
struct CanHeaterReport;
// Enumeration to describe the status of a heater. Note that the web interface returns the numerical values, so don't change them.
@@ -29,6 +30,7 @@ class Heater INHERIT_OBJECT_MODEL
public:
Heater(unsigned int num) noexcept;
virtual ~Heater() noexcept;
+ Heater(const Heater&) = delete;
// Configuration methods
virtual GCodeResult ConfigurePortAndSensor(const char *portName, PwmFrequency freq, unsigned int sensorNumber, const StringRef& reply) = 0;
@@ -40,36 +42,35 @@ public:
virtual GCodeResult ResetFault(const StringRef& reply) noexcept = 0; // Reset a fault condition - only call this if you know what you are doing
virtual void SwitchOff() noexcept = 0;
virtual void Spin() noexcept = 0;
- virtual void StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept = 0; // Start an auto tune cycle for this PID
+ virtual GCodeResult StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept = 0; // Start an auto tune cycle for this PID
virtual void GetAutoTuneStatus(const StringRef& reply) const = 0; // Get the auto tune status or last result
- virtual void Suspend(bool sus) noexcept = 0; // Suspend the heater to conserve power or while doing Z probing
+ virtual void Suspend(bool sus) noexcept = 0; // Suspend the heater to conserve power or while doing Z probing
virtual float GetAccumulator() const noexcept = 0; // Get the inertial term accumulator
#if SUPPORT_CAN_EXPANSION
virtual void UpdateRemoteStatus(CanAddress src, const CanHeaterReport& report) noexcept = 0;
#endif
- HeaterStatus GetStatus() const noexcept; // Get the status of the heater
+ HeaterStatus GetStatus() const noexcept; // Get the status of the heater
unsigned int GetHeaterNumber() const noexcept { return heaterNumber; }
const char *GetSensorName() const noexcept; // Get the name of the sensor for this heater, or nullptr if it hasn't been named
- void SetActiveTemperature(float t) noexcept;
+ void SetTemperature(float t, bool activeNotStandby) THROWS(GCodeException);
float GetActiveTemperature() const noexcept { return activeTemperature; }
- void SetStandbyTemperature(float t) noexcept;
float GetStandbyTemperature() const noexcept { return standbyTemperature; }
GCodeResult Activate(const StringRef& reply) noexcept; // Switch from idle to active
- void Standby() noexcept; // Switch from active to idle
+ void Standby() noexcept; // Switch from active to idle
void GetFaultDetectionParameters(float& pMaxTempExcursion, float& pMaxFaultTime) const noexcept
{ pMaxTempExcursion = maxTempExcursion; pMaxFaultTime = maxHeatingFaultTime; }
-
GCodeResult SetFaultDetectionParameters(float pMaxTempExcursion, float pMaxFaultTime, const StringRef& reply) noexcept;
- float GetHighestTemperatureLimit() const noexcept; // Get the highest temperature limit
+ GCodeResult ConfigureMonitor(GCodeBuffer &gb, const StringRef &reply) THROWS(GCodeException);
+
+ float GetHighestTemperatureLimit() const noexcept;
float GetLowestTemperatureLimit() const noexcept; // Get the lowest temperature limit
- void SetHeaterProtection(HeaterProtection *h) noexcept;
- const FopDt& GetModel() const noexcept { return model; } // Get the process model
- GCodeResult SetModel(float gain, float tc, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept; // Set the process model
+ const FopDt& GetModel() const noexcept { return model; } // Get the process model
+ GCodeResult SetOrReportModel(unsigned int heater, GCodeBuffer& gb, const StringRef& reply) noexcept;
void SetModelDefaults() noexcept;
bool IsHeaterEnabled() const noexcept // Is this heater enabled?
@@ -78,7 +79,7 @@ public:
void SetM301PidParameters(const M301PidParameters& params) noexcept
{ model.SetM301PidParameters(params); }
- bool CheckGood() const noexcept;
+ void SetDefaultMonitors() noexcept;
protected:
DECLARE_OBJECT_MODEL
@@ -102,31 +103,31 @@ protected:
lastTuningMode = tuning3
};
+protected:
virtual void ResetHeater() noexcept = 0;
virtual HeaterMode GetMode() const noexcept = 0;
virtual GCodeResult SwitchOn(const StringRef& reply) noexcept = 0;
virtual GCodeResult UpdateModel(const StringRef& reply) noexcept = 0;
virtual GCodeResult UpdateFaultDetectionParameters(const StringRef& reply) noexcept = 0;
+ virtual GCodeResult UpdateHeaterMonitors(const StringRef& reply) noexcept = 0;
int GetSensorNumber() const noexcept { return sensorNumber; }
- void SetSensorNumber(int sn) noexcept { sensorNumber = sn; }
+ void SetSensorNumber(int sn) noexcept;
float GetMaxTemperatureExcursion() const noexcept { return maxTempExcursion; }
float GetMaxHeatingFaultTime() const noexcept { return maxHeatingFaultTime; }
float GetTargetTemperature() const noexcept { return (active) ? activeTemperature : standbyTemperature; }
- HeaterProtection *GetHeaterProtections() const noexcept { return heaterProtection; }
+ GCodeResult SetModel(float gain, float tc, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept; // Set the process model
- FopDt model;
+ HeaterMonitor monitors[MaxMonitorsPerHeater]; // embedding them in the Heater uses less memory than dynamic allocation
private:
- bool CheckProtection() const noexcept; // Check heater protection elements and return true if everything is good
-
+ FopDt model;
unsigned int heaterNumber;
int sensorNumber; // the sensor number used by this heater
float activeTemperature; // The required active temperature
float standbyTemperature; // The required standby temperature
float maxTempExcursion; // The maximum temperature excursion permitted while maintaining the setpoint
float maxHeatingFaultTime; // How long a heater fault is permitted to persist before a heater fault is raised
- HeaterProtection *heaterProtection; // The first element of assigned heater protection items
bool active; // Are we active or standby?
};
diff --git a/src/Heating/HeaterMonitor.cpp b/src/Heating/HeaterMonitor.cpp
new file mode 100644
index 00000000..bb1874b3
--- /dev/null
+++ b/src/Heating/HeaterMonitor.cpp
@@ -0,0 +1,99 @@
+/*
+ * HeaterProtection.cpp
+ *
+ * Created on: 16 Nov 2017
+ * Author: Christian
+ */
+
+#include "HeaterMonitor.h"
+
+#include <Platform.h>
+#include <RepRap.h>
+#include "Heat.h"
+
+HeaterMonitor::HeaterMonitor() noexcept
+ : sensorNumber(-1), trigger(HeaterMonitorTrigger::Disabled), badTemperatureCount(0)
+{
+}
+
+// Check if any action needs to be taken. Returns true if everything is OK
+bool HeaterMonitor::Check() noexcept
+{
+ if (sensorNumber >= 0 && trigger != HeaterMonitorTrigger::Disabled)
+ {
+ TemperatureError err;
+ const float temperature = reprap.GetHeat().GetSensorTemperature(sensorNumber, err);
+
+ if (err != TemperatureError::success)
+ {
+ badTemperatureCount++;
+ if (badTemperatureCount > MaxBadTemperatureCount)
+ {
+ reprap.GetPlatform().MessageF(ErrorMessage, "Temperature reading error on sensor %d\n", sensorNumber);
+ return false;
+ }
+ }
+ else
+ {
+ badTemperatureCount = 0;
+ switch (trigger)
+ {
+ case HeaterMonitorTrigger::TemperatureExceeded:
+ return (temperature <= limit);
+
+ case HeaterMonitorTrigger::TemperatureTooLow:
+ return (temperature >= limit);
+
+ default:
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+// Append a report of this monitor to the string
+void HeaterMonitor::Report(unsigned int heater, unsigned int index, const StringRef& reply) const noexcept
+{
+ reply.lcatf("Heater %d monitor %d ", heater, index);
+ if (trigger == HeaterMonitorTrigger::Disabled)
+ {
+ reply.cat("is disabled");
+ }
+ else
+ {
+ const char *actionString, *triggerString;
+ switch (action)
+ {
+ case HeaterMonitorAction::GenerateFault:
+ actionString = "generate a heater fault";
+ break;
+ case HeaterMonitorAction::PermanentSwitchOff:
+ actionString = "permanently switch off";
+ break;
+ case HeaterMonitorAction::TemporarySwitchOff:
+ actionString = "temporarily switch off";
+ break;
+ default:
+ actionString = "(undefined)";
+ break;
+ }
+
+ switch (trigger)
+ {
+ case HeaterMonitorTrigger::TemperatureExceeded:
+ triggerString = "exceeds";
+ break;
+ case HeaterMonitorTrigger::TemperatureTooLow:
+ triggerString = "falls below";
+ break;
+ default:
+ triggerString = "(undefined)";
+ break;
+ }
+
+ reply.catf("uses sensor %d to %s if the reading %s %.1f" DEGREE_SYMBOL "C", sensorNumber, actionString, triggerString, (double)limit);
+ }
+}
+
+// End
diff --git a/src/Heating/HeaterMonitor.h b/src/Heating/HeaterMonitor.h
new file mode 100644
index 00000000..42bdac38
--- /dev/null
+++ b/src/Heating/HeaterMonitor.h
@@ -0,0 +1,81 @@
+/*
+ * HeaterMonitor.h
+ *
+ * Created on: 16 Nov 2017
+ * Author: Christian
+ */
+
+#ifndef HEATERMONITOR_H
+#define HEATERMONITOR_H
+
+#include <RepRapFirmware.h>
+#include <General/FreelistManager.h>
+
+// Condition of a heater monitor event
+enum class HeaterMonitorTrigger : int8_t
+{
+ Disabled = -1,
+ TemperatureExceeded = 0,
+ TemperatureTooLow
+};
+
+const HeaterMonitorTrigger MaxHeaterMonitorTrigger = HeaterMonitorTrigger::TemperatureTooLow;
+
+// The action to trigger when the target condition is met
+enum class HeaterMonitorAction : uint8_t
+{
+ GenerateFault = 0,
+ PermanentSwitchOff,
+ TemporarySwitchOff
+};
+
+const HeaterMonitorAction MaxHeaterMonitorAction = HeaterMonitorAction::TemporarySwitchOff;
+
+// A note about using this class. Its size is currently 8 bytes, and will be 12 bytes of object model support is added.
+// - If we allocate them statically within the heater object, then assuming 3 per heater we need 24 bytes, or 36 bytes with OM support.
+// - If we allocate them dynamically then there is an overhead of at least 8 bytes per object. All heaters have at least 2.
+// So each heater needs 12 bytes for the pointers plus 32 bytes (without OM support) or 40 bytes (with OM support). Total 44 or 52 bytes.
+// For unconfigured heaters, the cost is 24 or 36 bytes each using static allocation, or 4 bytes each using dynamic allocation.
+// Summary:
+// - Static allocation saves 20 bytes (no OM) or 16 bytes (with OM) per configured heater, if the 3rd heater monitor is not used (and more if it is used)
+// - Dynamic allocation saves 20 bytes (no OM) or 32 bytes (with OM) per unconfigured heater
+// For now we use static allocation, i.e. we embed the heater monitor in the heater object.
+class HeaterMonitor
+{
+public:
+ HeaterMonitor() noexcept;
+
+ void Set(int sn, float lim, HeaterMonitorAction act, HeaterMonitorTrigger trig) noexcept;
+ void Disable() noexcept;
+ bool Check() noexcept; // Check if any action needs to be taken
+
+ int GetSensorNumber() const noexcept { return sensorNumber; } // Get the supervisory sensor number
+ float GetTemperatureLimit() const noexcept { return limit; } // Get the temperature limit
+ HeaterMonitorAction GetAction() const noexcept { return action; } // Get the action to trigger when a temperature event occurs
+ HeaterMonitorTrigger GetTrigger() const noexcept { return trigger; } // Get the condition for a temperature event
+
+ void Report(unsigned int heater, unsigned int index, const StringRef& reply) const noexcept; // Append a report of this monitor to the string
+
+private:
+ float limit; // temperature limit
+ int8_t sensorNumber; // the sensor that we use to monitor the heater
+ HeaterMonitorAction action; // what action we take of we detect a fault
+ HeaterMonitorTrigger trigger; // what is treated a fault
+ uint8_t badTemperatureCount; // how many consecutive sensor reading faults we have had
+};
+
+inline void HeaterMonitor::Set(int sn, float lim, HeaterMonitorAction act, HeaterMonitorTrigger trig) noexcept
+{
+ sensorNumber = sn;
+ limit = lim;
+ action = act;
+ trigger = trig;
+ badTemperatureCount = 0;
+}
+
+inline void HeaterMonitor::Disable() noexcept
+{
+ trigger = HeaterMonitorTrigger::Disabled;
+}
+
+#endif
diff --git a/src/Heating/HeaterProtection.cpp b/src/Heating/HeaterProtection.cpp
deleted file mode 100644
index f13911d0..00000000
--- a/src/Heating/HeaterProtection.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * HeaterProtection.cpp
- *
- * Created on: 16 Nov 2017
- * Author: Christian
- */
-
-#include "HeaterProtection.h"
-
-#include "Platform.h"
-#include "RepRap.h"
-#include "Heat.h"
-
-
-HeaterProtection::HeaterProtection(size_t index) noexcept : next(nullptr)
-{
- // By default each heater protection element is mapped to its corresponding heater.
- // All other heater protection elements are unused and can be optionally assigned.
- heater = (index >= MaxHeaters) ? -1 : (int8_t)index;
- sensorNumber = -1;
-}
-
-void HeaterProtection::Init(float tempLimit) noexcept
-{
- next = nullptr;
- limit = tempLimit;
- action = HeaterProtectionAction::GenerateFault;
- trigger = HeaterProtectionTrigger::TemperatureExceeded;
-
- badTemperatureCount = 0;
-}
-
-// Check if any action needs to be taken. Returns true if everything is OK
-bool HeaterProtection::Check() noexcept
-{
- if (sensorNumber >= 0)
- {
- TemperatureError err;
- const float temperature = reprap.GetHeat().GetSensorTemperature(sensorNumber, err);
-
- if (err != TemperatureError::success)
- {
- badTemperatureCount++;
- if (badTemperatureCount > MaxBadTemperatureCount)
- {
- reprap.GetPlatform().MessageF(ErrorMessage, "Temperature reading error on sensor %d\n", sensorNumber);
- return false;
- }
- }
- else
- {
- badTemperatureCount = 0;
- switch (trigger)
- {
- case HeaterProtectionTrigger::TemperatureExceeded:
- return (temperature <= limit);
-
- case HeaterProtectionTrigger::TemperatureTooLow:
- return (temperature >= limit);
- }
- }
- }
- return true;
-}
-
-void HeaterProtection::SetHeater(int newHeater) noexcept
-{
- heater = newHeater;
-}
-
-// End
diff --git a/src/Heating/HeaterProtection.h b/src/Heating/HeaterProtection.h
deleted file mode 100644
index 19f5df36..00000000
--- a/src/Heating/HeaterProtection.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * HeaterProtection.h
- *
- * Created on: 16 Nov 2017
- * Author: Christian
- */
-
-#ifndef HEATERPROTECTION_H
-#define HEATERPROTECTION_H
-
-#include "RepRapFirmware.h"
-
-
-// Condition of a heater protection event
-enum class HeaterProtectionTrigger : uint8_t
-{
- TemperatureExceeded,
- TemperatureTooLow
-};
-
-const HeaterProtectionTrigger MaxHeaterProtectionTrigger = HeaterProtectionTrigger::TemperatureTooLow;
-
-// The action to trigger when the target condition is met
-enum class HeaterProtectionAction : uint8_t
-{
- GenerateFault = 0,
- PermanentSwitchOff,
- TemporarySwitchOff
-};
-
-const HeaterProtectionAction MaxHeaterProtectionAction = HeaterProtectionAction::TemporarySwitchOff;
-
-
-class Heat;
-
-class HeaterProtection
-{
-public:
- friend class Heat;
-
- HeaterProtection(size_t index) noexcept;
- void Init(float tempLimit) noexcept;
-
- HeaterProtection *Next() const noexcept { return next; }
- void SetNext(HeaterProtection *n) noexcept { next = n; }
-
- bool Check() noexcept; // Check if any action needs to be taken
-
- int GetHeater() const noexcept { return heater; }
- void SetHeater(int newHeater) noexcept; // Set the heater to control
-
- int GetSensorNumber() const noexcept { return sensorNumber; } // Get the supervisory sensor number
- void SetSensorNumber(int sn) noexcept; // Set the supervisory sensor number
-
- float GetTemperatureLimit() const noexcept { return limit; } // Get the temperature limit
- void SetTemperatureLimit(float newLimit) noexcept; // Set the temperature limit
-
- HeaterProtectionAction GetAction() const noexcept { return action; } // Get the action to trigger when a temperature event occurs
- void SetAction(HeaterProtectionAction newAction) noexcept; // Set the action to trigger when a temperature event occurs
-
- HeaterProtectionTrigger GetTrigger() const noexcept { return trigger; } // Get the condition for a temperature event
- void SetTrigger(HeaterProtectionTrigger newTrigger) noexcept; // Set the condition for a temperature event
-
-private:
- HeaterProtection *next; // link to next HeaterProtection item for the same heater
-
- float limit; // temperature limit
- int heater; // number of the heater we are protecting
- int sensorNumber; // the sensor that we use to monitor the heater
- HeaterProtectionAction action; // what action we take of we detect a fault
- HeaterProtectionTrigger trigger; // what is treated a fault
-
- size_t badTemperatureCount; // how many consecutive sensor reading faults we have had
-};
-
-inline void HeaterProtection::SetSensorNumber(int sn) noexcept
-{
- sensorNumber = sn;
-}
-
-inline void HeaterProtection::SetTemperatureLimit(float newLimit) noexcept
-{
- limit = newLimit;
-}
-
-inline void HeaterProtection::SetAction(HeaterProtectionAction newAction) noexcept
-{
- action = newAction;
-}
-
-inline void HeaterProtection::SetTrigger(HeaterProtectionTrigger newTrigger) noexcept
-{
- trigger = newTrigger;
-}
-
-#endif
diff --git a/src/Heating/LocalHeater.cpp b/src/Heating/LocalHeater.cpp
index 5cb822e4..f4429f87 100644
--- a/src/Heating/LocalHeater.cpp
+++ b/src/Heating/LocalHeater.cpp
@@ -9,7 +9,7 @@
#include "GCodes/GCodes.h"
#include "GCodes/GCodeBuffer/GCodeBuffer.h"
#include "Heat.h"
-#include "HeaterProtection.h"
+#include "HeaterMonitor.h"
#include "Platform.h"
#include "RepRap.h"
@@ -395,24 +395,24 @@ void LocalHeater::Spin() noexcept
}
// Verify that everything is operating in the required temperature range
- for (HeaterProtection *prot = GetHeaterProtections(); prot != nullptr; prot = prot->Next())
+ for (HeaterMonitor& prot : monitors)
{
- if (!prot->Check())
+ if (!prot.Check())
{
lastPwm = 0.0;
- switch (prot->GetAction())
+ switch (prot.GetAction())
{
- case HeaterProtectionAction::GenerateFault:
+ case HeaterMonitorAction::GenerateFault:
mode = HeaterMode::fault;
reprap.GetGCodes().HandleHeaterFault(GetHeaterNumber());
- reprap.GetPlatform().MessageF(ErrorMessage, "Heating fault on heater %u\n", GetHeaterNumber());
+ reprap.GetPlatform().MessageF(ErrorMessage, "Heating fault on heater %u: heater monitor was triggered\n", GetHeaterNumber());
break;
- case HeaterProtectionAction::TemporarySwitchOff:
+ case HeaterMonitorAction::TemporarySwitchOff:
// Do nothing, the PWM value has already been set above
break;
- case HeaterProtectionAction::PermanentSwitchOff:
+ case HeaterMonitorAction::PermanentSwitchOff:
SwitchOff();
break;
}
@@ -474,41 +474,49 @@ float LocalHeater::GetExpectedHeatingRate() const noexcept
}
// Auto tune this PID
-void LocalHeater::StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept
+GCodeResult LocalHeater::StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept
{
// Starting an auto tune
if (!GetModel().IsEnabled())
{
- reply.printf("Error: heater %u cannot be auto tuned while it is disabled", GetHeaterNumber());
+ reply.printf("heater %u cannot be auto tuned while it is disabled", GetHeaterNumber());
+ return GCodeResult::error;
}
- else if (lastPwm > 0.0 || GetAveragePWM() > 0.02)
+
+ if (lastPwm > 0.0 || GetAveragePWM() > 0.02)
{
- reply.printf("Error: heater %u must be off and cold before auto tuning it", GetHeaterNumber());
+ reply.printf("heater %u must be off and cold before auto tuning it", GetHeaterNumber());
+ return GCodeResult::error;
}
- else
+
+ const float limit = GetHighestTemperatureLimit();
+ if (targetTemp > limit)
{
- const TemperatureError err = ReadTemperature();
- if (err != TemperatureError::success)
- {
- reply.printf("Error: heater %u reported error '%s' at start of auto tuning", GetHeaterNumber(), TemperatureErrorString(err));
- }
- else
- {
- mode = HeaterMode::tuning0;
- tuningReadingsTaken = 0;
- tuned = false; // assume failure
-
- // We don't normally allow dynamic memory allocation when running. However, auto tuning is rarely done and it
- // would be wasteful to allocate a permanent array just in case we are going to run it, so we make an exception here.
- tuningTempReadings = new float[MaxTuningTempReadings];
- tuningTempReadings[0] = temperature;
- tuningReadingInterval = HeatSampleIntervalMillis;
- tuningPwm = maxPwm;
- tuningTargetTemp = targetTemp;
- reply.printf("Auto tuning heater %u using target temperature %.1f" DEGREE_SYMBOL "C and PWM %.2f - do not leave printer unattended",
- GetHeaterNumber(), (double)targetTemp, (double)maxPwm);
- }
+ reply.printf("heater %u target temperature must be no hiher than the temperature limit for this heater (%.1fC)", GetHeaterNumber(), (double)limit);
+ return GCodeResult::error;
+ }
+
+ const TemperatureError err = ReadTemperature();
+ if (err != TemperatureError::success)
+ {
+ reply.printf("heater %u reported error '%s' at start of auto tuning", GetHeaterNumber(), TemperatureErrorString(err));
+ return GCodeResult::error;
}
+
+ mode = HeaterMode::tuning0;
+ tuningReadingsTaken = 0;
+ tuned = false; // assume failure
+
+ // We don't normally allow dynamic memory allocation when running. However, auto tuning is rarely done and it
+ // would be wasteful to allocate a permanent array just in case we are going to run it, so we make an exception here.
+ tuningTempReadings = new float[MaxTuningTempReadings];
+ tuningTempReadings[0] = temperature;
+ tuningReadingInterval = HeatSampleIntervalMillis;
+ tuningPwm = maxPwm;
+ tuningTargetTemp = targetTemp;
+ reply.printf("Auto tuning heater %u using target temperature %.1f" DEGREE_SYMBOL "C and PWM %.2f - do not leave printer unattended",
+ GetHeaterNumber(), (double)targetTemp, (double)maxPwm);
+ return GCodeResult::ok;
}
// Get the auto tune status or last result
diff --git a/src/Heating/LocalHeater.h b/src/Heating/LocalHeater.h
index 3ad48f5a..c4e283ef 100644
--- a/src/Heating/LocalHeater.h
+++ b/src/Heating/LocalHeater.h
@@ -18,7 +18,7 @@
#include "Hardware/IoPorts.h"
#include "GCodes/GCodeResult.h"
-class HeaterProtection;
+class HeaterMonitor;
class LocalHeater : public Heater
{
@@ -38,7 +38,7 @@ public:
float GetTemperature() const noexcept override; // Get the current temperature
float GetAveragePWM() const noexcept override; // Return the running average PWM to the heater. Answer is a fraction in [0, 1].
float GetAccumulator() const noexcept override; // Return the integral accumulator
- void StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept override; // Start an auto tune cycle for this PID
+ GCodeResult StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept override; // Start an auto tune cycle for this PID
void GetAutoTuneStatus(const StringRef& reply) const noexcept override; // Get the auto tune status or last result
void Suspend(bool sus) noexcept override; // Suspend the heater to conserve power or while doing Z probing
@@ -52,10 +52,11 @@ protected:
GCodeResult SwitchOn(const StringRef& reply) noexcept override; // Turn the heater on and set the mode
GCodeResult UpdateModel(const StringRef& reply) noexcept override; // Called when the heater model has been changed
GCodeResult UpdateFaultDetectionParameters(const StringRef& reply) noexcept override { return GCodeResult::ok; }
+ GCodeResult UpdateHeaterMonitors(const StringRef& reply) noexcept override { return GCodeResult::ok; }
private:
void SetHeater(float power) const noexcept; // Power is a fraction in [0,1]
- TemperatureError ReadTemperature() noexcept; // Read and store the temperature of this heater
+ TemperatureError ReadTemperature() noexcept; // Read and store the temperature of this heater
void DoTuningStep() noexcept; // Called on each temperature sample when auto tuning
static bool ReadingsStable(size_t numReadings, float maxDiff) noexcept
pre(numReadings >= 2; numReadings <= MaxTuningTempReadings);
@@ -65,27 +66,27 @@ private:
void DisplayBuffer(const char *intro) noexcept; // Debug helper
float GetExpectedHeatingRate() const noexcept; // Get the minimum heating rate we expect
- PwmPort port; // The port that drives the heater
- float temperature; // The current temperature
- float previousTemperatures[NumPreviousTemperatures]; // The temperatures of the previous NumDerivativeSamples measurements, used for calculating the derivative
- size_t previousTemperatureIndex; // Which slot in previousTemperature we fill in next
- float iAccumulator; // The integral LocalHeater component
- float lastPwm; // The last PWM value we output, before scaling by kS
- float averagePWM; // The running average of the PWM, after scaling.
- uint32_t timeSetHeating; // When we turned on the heater
- uint32_t lastSampleTime; // Time when the temperature was last sampled by Spin()
+ PwmPort port; // The port that drives the heater
+ float temperature; // The current temperature
+ float previousTemperatures[NumPreviousTemperatures]; // The temperatures of the previous NumDerivativeSamples measurements, used for calculating the derivative
+ size_t previousTemperatureIndex; // Which slot in previousTemperature we fill in next
+ float iAccumulator; // The integral LocalHeater component
+ float lastPwm; // The last PWM value we output, before scaling by kS
+ float averagePWM; // The running average of the PWM, after scaling.
+ uint32_t timeSetHeating; // When we turned on the heater
+ uint32_t lastSampleTime; // Time when the temperature was last sampled by Spin()
- uint16_t heatingFaultCount; // Count of questionable heating behaviours
+ uint16_t heatingFaultCount; // Count of questionable heating behaviours
- uint8_t previousTemperaturesGood; // Bitmap indicating which previous temperature were good readings
- HeaterMode mode; // Current state of the heater
- bool tuned; // True if tuning was successful
- uint8_t badTemperatureCount; // Count of sequential dud readings
+ uint8_t previousTemperaturesGood; // Bitmap indicating which previous temperature were good readings
+ HeaterMode mode; // Current state of the heater
+ bool tuned; // True if tuning was successful
+ uint8_t badTemperatureCount; // Count of sequential dud readings
static_assert(sizeof(previousTemperaturesGood) * 8 >= NumPreviousTemperatures, "too few bits in previousTemperaturesGood");
// Variables used during heater tuning
- static const size_t MaxTuningTempReadings = 128; // The maximum number of readings we keep. Must be an even number.
+ static const size_t MaxTuningTempReadings = 128; // The maximum number of readings we keep. Must be an even number.
static float *tuningTempReadings; // the readings from the heater being tuned
static float tuningStartTemp; // the temperature when we turned on the heater
diff --git a/src/Heating/RemoteHeater.cpp b/src/Heating/RemoteHeater.cpp
index ace55336..ba00d3e2 100644
--- a/src/Heating/RemoteHeater.cpp
+++ b/src/Heating/RemoteHeater.cpp
@@ -128,14 +128,15 @@ float RemoteHeater::GetAccumulator() const noexcept
return 0.0; // not supported
}
-void RemoteHeater::StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept
+GCodeResult RemoteHeater::StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept
{
- //TODO
+ reply.copy("remote heater auto tune not implemented");
+ return GCodeResult::error;
}
void RemoteHeater::GetAutoTuneStatus(const StringRef& reply) const noexcept
{
- //TODO
+ reply.copy("remote heater auto tune not implemented");
}
void RemoteHeater::Suspend(bool sus) noexcept
@@ -189,7 +190,7 @@ GCodeResult RemoteHeater::UpdateModel(const StringRef& reply) noexcept
{
const CanRequestId rid = CanInterface::AllocateRequestId(boardAddress);
CanMessageUpdateHeaterModel * const msg = buf->SetupRequestMessage<CanMessageUpdateHeaterModel>(rid, CanInterface::GetCanAddress(), boardAddress);
- model.SetupCanMessage(GetHeaterNumber(), *msg);
+ GetModel().SetupCanMessage(GetHeaterNumber(), *msg);
return CanInterface::SendRequestAndGetStandardReply(buf, rid, reply);
}
@@ -214,6 +215,29 @@ GCodeResult RemoteHeater::UpdateFaultDetectionParameters(const StringRef& reply)
return GCodeResult::error;
}
+GCodeResult RemoteHeater::UpdateHeaterMonitors(const StringRef& reply) noexcept
+{
+ CanMessageBuffer *buf = CanMessageBuffer::Allocate();
+ if (buf != nullptr)
+ {
+ const CanRequestId rid = CanInterface::AllocateRequestId(boardAddress);
+ CanMessageSetHeaterMonitors * const msg = buf->SetupRequestMessage<CanMessageSetHeaterMonitors>(rid, CanInterface::GetCanAddress(), boardAddress);
+ msg->heater = GetHeaterNumber();
+ msg->numMonitors = MaxMonitorsPerHeater;
+ for (size_t i = 0; i < MaxMonitorsPerHeater; ++i)
+ {
+ msg->monitors[i].limit = monitors[i].GetTemperatureLimit();
+ msg->monitors[i].sensor = monitors[i].GetSensorNumber();
+ msg->monitors[i].action = (uint8_t)monitors[i].GetAction();
+ msg->monitors[i].trigger = (int8_t)monitors[i].GetTrigger();
+ }
+ return CanInterface::SendRequestAndGetStandardReply(buf, rid, reply);
+ }
+
+ reply.copy("No CAN buffer");
+ return GCodeResult::error;
+}
+
void RemoteHeater::UpdateRemoteStatus(CanAddress src, const CanHeaterReport& report) noexcept
{
if (src == boardAddress)
diff --git a/src/Heating/RemoteHeater.h b/src/Heating/RemoteHeater.h
index dab1b70d..5801fe7e 100644
--- a/src/Heating/RemoteHeater.h
+++ b/src/Heating/RemoteHeater.h
@@ -28,7 +28,7 @@ public:
float GetTemperature() const noexcept override; // Get the current temperature
float GetAveragePWM() const noexcept override; // Return the running average PWM to the heater. Answer is a fraction in [0, 1].
float GetAccumulator() const noexcept override; // Return the integral accumulator
- void StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept override; // Start an auto tune cycle for this PID
+ GCodeResult StartAutoTune(float targetTemp, float maxPwm, const StringRef& reply) noexcept override; // Start an auto tune cycle for this PID
void GetAutoTuneStatus(const StringRef& reply) const noexcept override; // Get the auto tune status or last result
void Suspend(bool sus) noexcept override; // Suspend the heater to conserve power or while doing Z probing
void UpdateRemoteStatus(CanAddress src, const CanHeaterReport& report) noexcept override;
@@ -39,6 +39,7 @@ protected:
GCodeResult SwitchOn(const StringRef& reply) noexcept override; // Turn the heater on and set the mode
GCodeResult UpdateModel(const StringRef& reply) noexcept override; // Called when the heater model has been changed
GCodeResult UpdateFaultDetectionParameters(const StringRef& reply) noexcept override;
+ GCodeResult UpdateHeaterMonitors(const StringRef& reply) noexcept override;
private:
static constexpr uint32_t RemoteStatusTimeout = 2000;
diff --git a/src/Heating/Sensors/TemperatureSensor.h b/src/Heating/Sensors/TemperatureSensor.h
index fc522974..7b561c4f 100644
--- a/src/Heating/Sensors/TemperatureSensor.h
+++ b/src/Heating/Sensors/TemperatureSensor.h
@@ -14,6 +14,7 @@ class TemperatureSensor INHERIT_OBJECT_MODEL
{
public:
TemperatureSensor(unsigned int sensorNum, const char *type) noexcept;
+ TemperatureSensor(const TemperatureSensor&) = delete;
// Virtual destructor
virtual ~TemperatureSensor() noexcept;