diff options
-rw-r--r-- | src/Heating/Heat.cpp | 7 | ||||
-rw-r--r-- | src/Heating/Heat.h | 2 | ||||
-rw-r--r-- | src/Heating/Heater.cpp | 95 | ||||
-rw-r--r-- | src/Heating/Heater.h | 48 | ||||
-rw-r--r-- | src/Heating/LocalHeater.cpp | 84 | ||||
-rw-r--r-- | src/Heating/LocalHeater.h | 31 | ||||
-rw-r--r-- | src/Heating/RemoteHeater.cpp | 94 | ||||
-rw-r--r-- | src/Heating/RemoteHeater.h | 33 |
8 files changed, 257 insertions, 137 deletions
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp index 076f81c0..89875819 100644 --- a/src/Heating/Heat.cpp +++ b/src/Heating/Heat.cpp @@ -1092,13 +1092,6 @@ size_t Heat::GetNumSensorsToReport() const noexcept return s->GetSensorNumber() + 1; } -// Get the temperature of a heater -float Heat::GetHeaterTemperature(size_t heater) const noexcept -{ - const auto h = FindHeater(heater); - return (h.IsNull()) ? ABS_ZERO : h->GetTemperature(); -} - // Suspend the heaters to conserve power or while doing Z probing void Heat::SuspendHeaters(bool sus) noexcept { diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h index 3d86ed5e..8b67f807 100644 --- a/src/Heating/Heat.h +++ b/src/Heating/Heat.h @@ -104,8 +104,6 @@ public: float GetAveragePWM(size_t heater) const noexcept // Return the running average PWM to the heater as a fraction in [0, 1]. pre(heater < MaxHeaters); - float GetHeaterTemperature(size_t heater) const noexcept; // Result is in degrees Celsius - const Tool* GetLastStandbyTool(int heater) const noexcept pre(heater >= 0; heater < MaxHeaters) { diff --git a/src/Heating/Heater.cpp b/src/Heating/Heater.cpp index 952f50ce..d3bd47f5 100644 --- a/src/Heating/Heater.cpp +++ b/src/Heating/Heater.cpp @@ -60,8 +60,42 @@ DEFINE_GET_OBJECT_MODEL_TABLE(Heater) #endif +// Static members of class Heater + +float Heater::tuningPwm; // the PWM to use, 0..1 +float Heater::tuningTargetTemp; // the target temperature +DeviationAccumulator Heater::tuningStartTemp; // the temperature when we turned on the heater +uint32_t Heater::tuningBeginTime; // when we started the tuning process +DeviationAccumulator Heater::dHigh; +DeviationAccumulator Heater::dLow; +DeviationAccumulator Heater::tOn; +DeviationAccumulator Heater::tOff; +DeviationAccumulator Heater::heatingRate; +DeviationAccumulator Heater::coolingRate; +uint32_t Heater::lastOffTime; +uint32_t Heater::lastOnTime; +float Heater::peakTemp; // max or min temperature +uint32_t Heater::peakTime; // the time at which we recorded peakTemp +float Heater::afterPeakTemp; // temperature after max from which we start timing the cooling rate +uint32_t Heater::afterPeakTime; // the time at which we recorded afterPeakTemp +float Heater::lastCoolingRate; +FansBitmap Heater::tuningFans; +unsigned int Heater::tuningPhase; +uint8_t Heater::idleCyclesDone; + +// Clear all the counters except tuning voltage and start temperature +/*static*/ void Heater::ClearCounters() noexcept +{ + dHigh.Clear(); + dLow.Clear(); + tOn.Clear(); + tOff.Clear(); + heatingRate.Clear(); + coolingRate.Clear(); +} + Heater::Heater(unsigned int num) noexcept - : heaterNumber(num), sensorNumber(-1), activeTemperature(0.0), standbyTemperature(0.0), + : tuned(false), heaterNumber(num), sensorNumber(-1), activeTemperature(0.0), standbyTemperature(0.0), maxTempExcursion(DefaultMaxTempExcursion), maxHeatingFaultTime(DefaultMaxHeatingFaultTime), active(false), modelSetByUser(false), monitorsSetByUser(false) { @@ -170,17 +204,17 @@ GCodeResult Heater::SetOrReportModel(unsigned int heater, GCodeBuffer& gb, const } // Set the process model returning true if successful -GCodeResult Heater::SetModel(float heatingRate, float coolingRateFanOff, float coolingRateFanOn, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept +GCodeResult Heater::SetModel(float hr, float coolingRateFanOff, float coolingRateFanOn, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept { GCodeResult rslt; - if (model.SetParameters(heatingRate, coolingRateFanOff, coolingRateFanOn, td, maxPwm, GetHighestTemperatureLimit(), voltage, usePid, inverted)) + if (model.SetParameters(hr, coolingRateFanOff, coolingRateFanOn, td, maxPwm, GetHighestTemperatureLimit(), voltage, usePid, inverted)) { if (model.IsEnabled()) { rslt = UpdateModel(reply); if (rslt == GCodeResult::ok) { - const float predictedMaxTemp = heatingRate/coolingRateFanOff + NormalAmbientTemperature; + const float predictedMaxTemp = hr/coolingRateFanOff + NormalAmbientTemperature; const float noWarnTemp = (GetHighestTemperatureLimit() - NormalAmbientTemperature) * 1.5 + 50.0; // allow 50% extra power plus enough for an extra 50C if (predictedMaxTemp > noWarnTemp) { @@ -192,6 +226,7 @@ GCodeResult Heater::SetModel(float heatingRate, float coolingRateFanOff, float c else { ResetHeater(); + tuned = false; rslt = GCodeResult::ok; } } @@ -205,6 +240,58 @@ GCodeResult Heater::SetModel(float heatingRate, float coolingRateFanOff, float c return rslt; } +// Start an auto tune cycle for this heater +GCodeResult Heater::StartAutoTune(GCodeBuffer& gb, const StringRef& reply, FansBitmap fans) THROWS(GCodeException) +{ + // Get the target temperature (required) + gb.MustSee('S'); + const float targetTemp = gb.GetFValue(); + + // Get the optional PWM + const float maxPwm = (gb.Seen('P')) ? gb.GetFValue() : GetModel().GetMaxPwm(); + if (maxPwm < 0.1 || maxPwm > 1.0) + { + reply.copy("Invalid PWM value"); + return GCodeResult::error; + } + + if (!GetModel().IsEnabled()) + { + reply.printf("heater %u cannot be auto tuned while it is disabled", GetHeaterNumber()); + return GCodeResult::error; + } + + const float limit = GetHighestTemperatureLimit(); + if (targetTemp >= limit) + { + reply.printf("heater %u target temperature must be below the temperature limit for this heater (%.1fC)", GetHeaterNumber(), (double)limit); + return GCodeResult::error; + } + + TemperatureError err; + const float currentTemp = reprap.GetHeat().GetSensorTemperature(GetSensorNumber(), err); + if (err != TemperatureError::success) + { + reply.printf("heater %u reported error '%s' at start of auto tuning", GetHeaterNumber(), TemperatureErrorString(err)); + return GCodeResult::error; + } + + const bool seenA = gb.Seen('A'); + const float ambientTemp = (seenA) ? gb.GetFValue() : currentTemp; + if (ambientTemp + 20 >= targetTemp) + { + reply.printf("Target temperature must be at least 20C above ambient temperature"); + } + + const GCodeResult rslt = StartAutoTune(reply, fans, targetTemp, maxPwm, seenA, ambientTemp); + if (rslt == GCodeResult::ok) + { + 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 rslt; +} + GCodeResult Heater::SetFaultDetectionParameters(float pMaxTempExcursion, float pMaxFaultTime, const StringRef& reply) noexcept { maxTempExcursion = pMaxTempExcursion; diff --git a/src/Heating/Heater.h b/src/Heating/Heater.h index eaed5621..4600ab0c 100644 --- a/src/Heating/Heater.h +++ b/src/Heating/Heater.h @@ -14,6 +14,7 @@ #include "HeaterMonitor.h" #include <GCodes/GCodeResult.h> #include <ObjectModel/ObjectModel.h> +#include <Math/DeviationAccumulator.h> #if SUPPORT_CAN_EXPANSION # include "CanId.h" @@ -37,12 +38,11 @@ public: virtual GCodeResult SetPwmFrequency(PwmFrequency freq, const StringRef& reply) = 0; virtual GCodeResult ReportDetails(const StringRef& reply) const noexcept = 0; - virtual float GetTemperature() const noexcept = 0; // Get the current temperature + virtual float GetTemperature() const noexcept = 0; // Get the current temperature and error status virtual float GetAveragePWM() const noexcept = 0; // Return the running average PWM to the heater. Answer is a fraction in [0, 1]. 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 GCodeResult StartAutoTune(GCodeBuffer& gb, const StringRef& reply, FansBitmap fans) THROWS(GCodeException) = 0; // Start an auto tune cycle for this heater virtual void GetAutoTuneStatus(const StringRef& reply) const noexcept = 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 float GetAccumulator() const noexcept = 0; // Get the inertial term accumulator @@ -60,6 +60,7 @@ public: 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 + GCodeResult StartAutoTune(GCodeBuffer& gb, const StringRef& reply, FansBitmap fans) THROWS(GCodeException); // Start an auto tune cycle for this heater void GetFaultDetectionParameters(float& pMaxTempExcursion, float& pMaxFaultTime) const noexcept { pMaxTempExcursion = maxTempExcursion; pMaxFaultTime = maxHeatingFaultTime; } @@ -113,15 +114,52 @@ protected: virtual GCodeResult UpdateModel(const StringRef& reply) noexcept = 0; virtual GCodeResult UpdateFaultDetectionParameters(const StringRef& reply) noexcept = 0; virtual GCodeResult UpdateHeaterMonitors(const StringRef& reply) noexcept = 0; + virtual GCodeResult StartAutoTune(const StringRef& reply, FansBitmap fans, float targetTemp, float pwm, bool seenA, float ambientTemp) noexcept = 0; int GetSensorNumber() const noexcept { return sensorNumber; } 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; } - GCodeResult SetModel(float heatingRate, float coolingRateFanOff, float coolingRateFanOn, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept; // Set the process model - - HeaterMonitor monitors[MaxMonitorsPerHeater]; // embedding them in the Heater uses less memory than dynamic allocation + GCodeResult SetModel(float hr, float coolingRateFanOff, float coolingRateFanOn, float td, float maxPwm, float voltage, bool usePid, bool inverted, const StringRef& reply) noexcept; // Set the process model + + HeaterMonitor monitors[MaxMonitorsPerHeater]; // embedding them in the Heater uses less memory than dynamic allocation + bool tuned; // true if tuning was successful + + // Constants used during heater tuning + static constexpr uint32_t TempSettleTimeout = 20000; // how long we allow the initial temperature to settle + static constexpr unsigned int TuningHeaterMinIdleCycles = 3; // minimum number of idle cycles after heating up, including the initial overshoot and cool down + static constexpr unsigned int TuningHeaterMaxIdleCycles = 10; + static constexpr unsigned int MinTuningHeaterCycles = 5; + static constexpr unsigned int MaxTuningHeaterCycles = 25; + static constexpr float TuningHysteresis = 5.0; + static constexpr float TuningPeakTempDrop = 2.0; // must be well below TuningHysteresis + static constexpr float FeedForwardMultiplier = 1.3; // how much we over-compensate feedforward to allow for heat reservoirs during tuning + static constexpr float HeaterSettledCoolingTimeRatio = 0.93; + + // Variables used during heater tuning + static float tuningPwm; // the PWM to use, 0..1 + static float tuningTargetTemp; // the target temperature + static DeviationAccumulator tuningStartTemp; // the temperature when we turned on the heater + static uint32_t tuningBeginTime; // when we started the tuning process + static DeviationAccumulator dHigh; + static DeviationAccumulator dLow; + static DeviationAccumulator tOn; + static DeviationAccumulator tOff; + static DeviationAccumulator heatingRate; + static DeviationAccumulator coolingRate; + static uint32_t lastOffTime; + static uint32_t lastOnTime; + static float peakTemp; // max or min temperature + static uint32_t peakTime; // the time at which we recorded peakTemp + static float afterPeakTemp; // temperature after max from which we start timing the cooling rate + static uint32_t afterPeakTime; // the time at which we recorded afterPeakTemp + static float lastCoolingRate; + static FansBitmap tuningFans; + static unsigned int tuningPhase; + static uint8_t idleCyclesDone; + + static void ClearCounters() noexcept; private: FopDt model; diff --git a/src/Heating/LocalHeater.cpp b/src/Heating/LocalHeater.cpp index 949c6096..9a5db5c9 100644 --- a/src/Heating/LocalHeater.cpp +++ b/src/Heating/LocalHeater.cpp @@ -16,49 +16,12 @@ #define TUNE_WITH_HALF_FAN 0 -// Private constants -const uint32_t InitialTuningReadingInterval = 250; // the initial reading interval in milliseconds -const uint32_t TempSettleTimeout = 20000; // how long we allow the initial temperature to settle - -// Variables used during heater tuning -static float tuningPwm; // the PWM to use, 0..1 -static float tuningTargetTemp; // the target temperature -static DeviationAccumulator tuningStartTemp; // the temperature when we turned on the heater -static uint32_t tuningBeginTime; // when we started the tuning process -static DeviationAccumulator dHigh; -static DeviationAccumulator dLow; -static DeviationAccumulator tOn; -static DeviationAccumulator tOff; -static DeviationAccumulator heatingRate; -static DeviationAccumulator coolingRate; -static uint32_t lastOffTime; -static uint32_t lastOnTime; -static float peakTemp; // max or min temperature -static uint32_t peakTime; // the time at which we recorded peakTemp -static float afterPeakTemp; // temperature after max from which we start timing the cooling rate -static uint32_t afterPeakTime; // the time at which we recorded afterPeakTemp -static float lastCoolingRate; -static FansBitmap tuningFans; -static unsigned int tuningPhase; -static uint8_t idleCyclesDone; - static LocalHeater::HeaterParameters fanOffParams, fanOnParams; #if HAS_VOLTAGE_MONITOR static DeviationAccumulator tuningVoltage; // sum of the voltage readings we take during the heating phase #endif -// Clear all the counters except tuning voltage and start temperature -static void ClearCounters() noexcept -{ - dHigh.Clear(); - dLow.Clear(); - tOn.Clear(); - tOff.Clear(); - heatingRate.Clear(); - coolingRate.Clear(); -} - // Member functions and constructors LocalHeater::LocalHeater(unsigned int heaterNum) noexcept : Heater(heaterNum), mode(HeaterMode::off) @@ -99,7 +62,6 @@ void LocalHeater::ResetHeater() noexcept previousTemperatureIndex = 0; iAccumulator = 0.0; badTemperatureCount = 0; - tuned = false; averagePWM = lastPwm = 0.0; heatingFaultCount = 0; temperature = BadErrorTemperature; @@ -478,60 +440,18 @@ float LocalHeater::GetExpectedHeatingRate() const noexcept } // Auto tune this heater. The caller has already checked that on other heater is being tuned. -GCodeResult LocalHeater::StartAutoTune(GCodeBuffer& gb, const StringRef& reply, FansBitmap fans) THROWS(GCodeException) +GCodeResult LocalHeater::StartAutoTune(const StringRef& reply, FansBitmap fans, float targetTemp, float pwm, bool seenA, float ambientTemp) noexcept { - // Get the target temperature (required) - gb.MustSee('S'); - const float targetTemp = gb.GetFValue(); - - // Get the optional PWM - const float maxPwm = (gb.Seen('P')) ? gb.GetFValue() : GetModel().GetMaxPwm(); - if (maxPwm < 0.1 || maxPwm > 1.0) - { - reply.copy("Invalid PWM value"); - return GCodeResult::error; - } - - if (!GetModel().IsEnabled()) - { - reply.printf("heater %u cannot be auto tuned while it is disabled", GetHeaterNumber()); - return GCodeResult::error; - } - if (lastPwm > 0.0 || GetAveragePWM() > 0.02) { reply.printf("heater %u must be off and cold before auto tuning it", GetHeaterNumber()); return GCodeResult::error; } - const float limit = GetHighestTemperatureLimit(); - if (targetTemp >= limit) - { - reply.printf("heater %u target temperature must be below 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; - } - - const bool seenA = gb.Seen('A'); - const float ambientTemp = (seenA) ? gb.GetFValue() : temperature; - if (ambientTemp + 20 >= targetTemp) - { - reply.printf("Target temperature must be at least 20C above ambient temperature"); - } - - 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); - tuningFans = fans; reprap.GetFansManager().SetFansValue(tuningFans, 0.0); - tuningPwm = maxPwm; + tuningPwm = pwm; tuningTargetTemp = targetTemp; tuningStartTemp.Clear(); tuningBeginTime = millis(); diff --git a/src/Heating/LocalHeater.h b/src/Heating/LocalHeater.h index 0486bccf..80afa0ad 100644 --- a/src/Heating/LocalHeater.h +++ b/src/Heating/LocalHeater.h @@ -17,7 +17,6 @@ #include "TemperatureError.h" #include <Hardware/IoPorts.h> #include <GCodes/GCodeResult.h> -#include <Math/DeviationAccumulator.h> class HeaterMonitor; @@ -41,15 +40,14 @@ public: GCodeResult SetPwmFrequency(PwmFrequency freq, const StringRef& reply) noexcept override; GCodeResult ReportDetails(const StringRef& reply) const noexcept override; - void Spin() noexcept override; // Called in a tight loop to keep things running - void SwitchOff() noexcept override; // Not even standby - all heater power off - GCodeResult ResetFault(const StringRef& reply) noexcept override; // Reset a fault condition - only call this if you know what you are doing - 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 - GCodeResult StartAutoTune(GCodeBuffer& gb, const StringRef& reply, FansBitmap fans) THROWS(GCodeException) override; // Start an auto tune cycle for this heater + void Spin() noexcept override; // Called in a tight loop to keep things running + void SwitchOff() noexcept override; // Not even standby - all heater power off + GCodeResult ResetFault(const StringRef& reply) noexcept override; // Reset a fault condition - only call this if you know what you are doing + float GetTemperature() const noexcept override; // Get the latest 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 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 Suspend(bool sus) noexcept override; // Suspend the heater to conserve power or while doing Z probing void PrintCoolingFanPwmChanged(float pwmChange) noexcept override; #if SUPPORT_CAN_EXPANSION @@ -59,10 +57,11 @@ public: protected: void ResetHeater() noexcept override; HeaterMode GetMode() const noexcept override { return mode; } - 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 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; } + GCodeResult StartAutoTune(const StringRef& reply, FansBitmap fans, float targetTemp, float pwm, bool seenA, float ambientTemp) noexcept override; // Start an auto tune cycle for this heater private: void SetHeater(float power) const noexcept; // Power is a fraction in [0,1] @@ -87,19 +86,9 @@ private: 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"); - - static constexpr unsigned int TuningHeaterMinIdleCycles = 3; // minimum number of idle cycles after heating up, including the initial overshoot and cool down - static constexpr unsigned int TuningHeaterMaxIdleCycles = 10; - static constexpr unsigned int MinTuningHeaterCycles = 5; - static constexpr unsigned int MaxTuningHeaterCycles = 25; - static constexpr float TuningHysteresis = 5.0; - static constexpr float TuningPeakTempDrop = 2.0; // must be well below TuningHysteresis - static constexpr float FeedForwardMultiplier = 1.3; // how much we over-compensate feedforward to allow for heat reservoirs during tuning - static constexpr float HeaterSettledCoolingTimeRatio = 0.93; }; #endif /* SRC_LOCALHEATER_H_ */ diff --git a/src/Heating/RemoteHeater.cpp b/src/Heating/RemoteHeater.cpp index 4012e387..1620bf1d 100644 --- a/src/Heating/RemoteHeater.cpp +++ b/src/Heating/RemoteHeater.cpp @@ -18,7 +18,7 @@ #include <CanMessageBuffer.h> RemoteHeater::RemoteHeater(unsigned int num, CanAddress board) noexcept - : Heater(num), boardAddress(board), lastMode(HeaterMode::offline), averagePwm(0), lastTemperature(0.0), whenLastStatusReceived(0) + : Heater(num), boardAddress(board), lastMode(HeaterMode::offline), averagePwm(0), tuningState(TuningState::notTuning), lastTemperature(0.0), whenLastStatusReceived(0) { } @@ -33,12 +33,41 @@ RemoteHeater::~RemoteHeater() noexcept void RemoteHeater::Spin() noexcept { - // Nothing needed here unless we want to copy the sensor temperature across. For now we don't store the temperature locally. + switch (tuningState) + { + case TuningState::notTuning: + break; + + case TuningState::stabilising: + //TODO wait for temp to stabilise + { + String<StringLength100> reply; + if (SendTuningCommand(reply.GetRef(), true) != GCodeResult::ok) + { + reprap.GetPlatform().MessageF(ErrorMessage, "Heater tuning cancelled: %s\n", reply.c_str()); + SwitchOff(); + tuningState = TuningState::notTuning; + } + } + break; + + case TuningState::heatingUp: + //TODO + break; + + case TuningState::cyclingFanOff: + //TODO + break; + + case TuningState::cyclingFanOn: + //TODO + break; + } } void RemoteHeater::ResetHeater() noexcept { - //TODO + // This is only called by UpdateModel. Nothing needed here. } GCodeResult RemoteHeater::ConfigurePortAndSensor(const char *portName, PwmFrequency freq, unsigned int sn, const StringRef& reply) @@ -128,10 +157,43 @@ float RemoteHeater::GetAccumulator() const noexcept return 0.0; // not supported } -GCodeResult RemoteHeater::StartAutoTune(GCodeBuffer& gb, const StringRef& reply, FansBitmap fans) THROWS(GCodeException) +GCodeResult RemoteHeater::StartAutoTune(const StringRef& reply, FansBitmap fans, float targetTemp, float pwm, bool seenA, float ambientTemp) noexcept { - reply.copy("remote heater auto tune not implemented"); - return GCodeResult::error; + CanMessageBuffer * const buf = CanMessageBuffer::Allocate(); + if (buf == nullptr) + { + reply.copy("No CAN buffer"); + return GCodeResult::error; + } + + tuningFans = fans; + reprap.GetFansManager().SetFansValue(tuningFans, 0.0); + + tuningPwm = pwm; + tuningTargetTemp = targetTemp; + tuningStartTemp.Clear(); + tuningBeginTime = millis(); + tuningPhase = 0; + tuned = false; // assume failure + + if (seenA) + { + tuningStartTemp.Add(ambientTemp); + ClearCounters(); + timeSetHeating = millis(); + GCodeResult rslt = SendTuningCommand(reply, true); + if (rslt != GCodeResult::ok) + { + return rslt; + } + tuningState = TuningState::heatingUp; + } + else + { + tuningState = TuningState::stabilising; + } + + return GCodeResult::ok; } void RemoteHeater::GetAutoTuneStatus(const StringRef& reply) const noexcept @@ -249,6 +311,26 @@ void RemoteHeater::UpdateRemoteStatus(CanAddress src, const CanHeaterReport& rep } } +GCodeResult RemoteHeater::SendTuningCommand(const StringRef& reply, bool on) noexcept +{ + CanMessageBuffer * const buf = CanMessageBuffer::Allocate(); + if (buf == nullptr) + { + reply.copy("No CAN buffer"); + return GCodeResult::error; + } + + const CanRequestId rid = CanInterface::AllocateRequestId(boardAddress); + auto msg = buf->SetupRequestMessage<CanMessageHeaterTuningCommand>(rid, CanInterface::GetCanAddress(), boardAddress); + msg->heaterNumber = GetHeaterNumber(); + msg->on = on; + msg->highTemp = tuningTargetTemp; + msg->lowTemp = tuningTargetTemp - TuningHysteresis; + msg->pwm = tuningPwm; + msg->peakTempDrop = TuningPeakTempDrop; + return CanInterface::SendRequestAndGetStandardReply(buf, rid, reply); +} + #endif // End diff --git a/src/Heating/RemoteHeater.h b/src/Heating/RemoteHeater.h index 907908e7..d39558d6 100644 --- a/src/Heating/RemoteHeater.h +++ b/src/Heating/RemoteHeater.h @@ -22,34 +22,47 @@ public: GCodeResult SetPwmFrequency(PwmFrequency freq, const StringRef& reply) override; GCodeResult ReportDetails(const StringRef& reply) const noexcept override; - void Spin() noexcept override; // Called in a tight loop to keep things running - void SwitchOff() noexcept override; // Not even standby - all heater power off - GCodeResult ResetFault(const StringRef& reply) noexcept override; // Reset a fault condition - only call this if you know what you are doing - 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 - GCodeResult StartAutoTune(GCodeBuffer& gb, const StringRef& reply, FansBitmap fans) THROWS(GCodeException) override; // Start an auto tune cycle for this heater + void Spin() noexcept override; // Called in a tight loop to keep things running + void SwitchOff() noexcept override; // Not even standby - all heater power off + GCodeResult ResetFault(const StringRef& reply) noexcept override; // Reset a fault condition - only call this if you know what you are doing + float GetTemperature() const noexcept override; // Get the latest 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 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 Suspend(bool sus) noexcept override; // Suspend the heater to conserve power or while doing Z probing void PrintCoolingFanPwmChanged(float pwmChange) noexcept override; void UpdateRemoteStatus(CanAddress src, const CanHeaterReport& report) noexcept override; protected: void ResetHeater() noexcept override; HeaterMode GetMode() const noexcept override; - 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 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; + GCodeResult StartAutoTune(const StringRef& reply, FansBitmap fans, float targetTemp, float pwm, bool seenA, float ambientTemp) noexcept override; // Start an auto tune cycle for this heater private: + enum class TuningState : uint8_t + { + notTuning = 0, + stabilising, + heatingUp, + cyclingFanOff, + cyclingFanOn + }; + + GCodeResult SendTuningCommand(const StringRef& reply, bool on) noexcept; + static constexpr uint32_t RemoteStatusTimeout = 2000; CanAddress boardAddress; HeaterMode lastMode; uint8_t averagePwm; + TuningState tuningState; float lastTemperature; uint32_t whenLastStatusReceived; + uint32_t timeSetHeating; // When we turned on the heater at the start of auto tuning }; #endif |