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>2021-10-21 17:42:03 +0300
committerDavid Crocker <dcrocker@eschertech.com>2021-10-21 17:42:03 +0300
commitcdb43e776df2eda0b526a6e1a43d4ee0fbf4ac91 (patch)
tree4dacd1947bc7fe84bd224f5300d88d15e0db5918
parent7f6d0bee5de31caffc05df7d517ef73127e940cb (diff)
Implementation of heater in expansion mode mostly done
Still need to implement tuning cycle report
-rw-r--r--src/CAN/CommandProcessor.cpp20
-rw-r--r--src/Heating/FOPDT.cpp11
-rw-r--r--src/Heating/FOPDT.h1
-rw-r--r--src/Heating/Heat.cpp122
-rw-r--r--src/Heating/Heat.h9
-rw-r--r--src/Heating/Heater.cpp65
-rw-r--r--src/Heating/Heater.h19
-rw-r--r--src/Heating/LocalHeater.cpp189
-rw-r--r--src/Heating/LocalHeater.h7
-rw-r--r--src/Heating/RemoteHeater.cpp11
-rw-r--r--src/Heating/RemoteHeater.h4
11 files changed, 429 insertions, 29 deletions
diff --git a/src/CAN/CommandProcessor.cpp b/src/CAN/CommandProcessor.cpp
index a59d9146..a206e43a 100644
--- a/src/CAN/CommandProcessor.cpp
+++ b/src/CAN/CommandProcessor.cpp
@@ -424,45 +424,45 @@ void CommandProcessor::ProcessReceivedMessage(CanMessageBuffer *buf) noexcept
rslt = EutGetInfo(buf->msg.getInfo, replyRef, extra);
break;
-#if 0 // heater commands not implemented yet
// Heater commands
case CanMessageType::m950Heater:
requestId = buf->msg.generic.requestId;
- rslt = Heat::ConfigureHeater(buf->msg.generic, replyRef);
+ rslt = reprap.GetHeat().ConfigureHeater(buf->msg.generic, replyRef);
break;
case CanMessageType::heaterFeedForward:
requestId = buf->msg.heaterFeedForward.requestId;
- rslt = Heat::FeedForward(buf->msg.heaterFeedForward, replyRef);
+ rslt = reprap.GetHeat().FeedForward(buf->msg.heaterFeedForward, replyRef);
break;
+
case CanMessageType::updateHeaterModelNew:
requestId = buf->msg.heaterModelNew.requestId;
- rslt = Heat::ProcessM307New(buf->msg.heaterModelNew, replyRef);
+ rslt = reprap.GetHeat().ProcessM307New(buf->msg.heaterModelNew, replyRef);
break;
case CanMessageType::setHeaterTemperature:
requestId = buf->msg.setTemp.requestId;
- rslt = Heat::SetTemperature(buf->msg.setTemp, replyRef);
+ rslt = reprap.GetHeat().SetTemperature(buf->msg.setTemp, replyRef);
break;
case CanMessageType::heaterTuningCommand:
requestId = buf->msg.heaterTuningCommand.requestId;
- rslt = Heat::TuningCommand(buf->msg.heaterTuningCommand, replyRef);
+ rslt = reprap.GetHeat().TuningCommand(buf->msg.heaterTuningCommand, replyRef);
break;
case CanMessageType::setHeaterFaultDetection:
requestId = buf->msg.setHeaterFaultDetection.requestId;
- rslt = Heat::SetFaultDetection(buf->msg.setHeaterFaultDetection, replyRef);
+ rslt = reprap.GetHeat().SetFaultDetection(buf->msg.setHeaterFaultDetection, replyRef);
break;
case CanMessageType::setHeaterMonitors:
requestId = buf->msg.setHeaterMonitors.requestId;
- rslt = Heat::SetHeaterMonitors(buf->msg.setHeaterMonitors, replyRef);
+ rslt = reprap.GetHeat().SetHeaterMonitors(buf->msg.setHeaterMonitors, replyRef);
break;
-#endif
+
case CanMessageType::m308New:
requestId = buf->msg.generic.requestId;
- rslt = reprap.GetHeat().EutProcessM308(buf->msg.generic, replyRef);
+ rslt = reprap.GetHeat().ProcessM308(buf->msg.generic, replyRef);
break;
// Fan commands
diff --git a/src/Heating/FOPDT.cpp b/src/Heating/FOPDT.cpp
index a4021864..911e9100 100644
--- a/src/Heating/FOPDT.cpp
+++ b/src/Heating/FOPDT.cpp
@@ -140,9 +140,14 @@ M301PidParameters FopDt::GetM301PidParameters(bool forLoadChange) const noexcept
// Override the PID parameters. We set both sets to the same parameters.
void FopDt::SetM301PidParameters(const M301PidParameters& pp) noexcept
{
- loadChangeParams.kP = setpointChangeParams.kP = pp.kP * (1.0/255.0);
- loadChangeParams.recipTi = setpointChangeParams.recipTi = pp.kI/pp.kP;
- loadChangeParams.tD = setpointChangeParams.tD = pp.kD/pp.kP;
+ SetRawPidParameters(pp.kP * (1.0/255.0), pp.kI/pp.kP, pp.kD/pp.kP);
+}
+
+void FopDt::SetRawPidParameters(float p_kP, float p_recipTi, float p_tD) noexcept
+{
+ loadChangeParams.kP = setpointChangeParams.kP = p_kP;
+ loadChangeParams.recipTi = setpointChangeParams.recipTi = p_recipTi;
+ loadChangeParams.tD = setpointChangeParams.tD = p_tD;
pidParametersOverridden = true;
}
diff --git a/src/Heating/FOPDT.h b/src/Heating/FOPDT.h
index 7edee12a..23802165 100644
--- a/src/Heating/FOPDT.h
+++ b/src/Heating/FOPDT.h
@@ -66,6 +66,7 @@ public:
bool ArePidParametersOverridden() const noexcept { return pidParametersOverridden; }
M301PidParameters GetM301PidParameters(bool forLoadChange) const noexcept;
void SetM301PidParameters(const M301PidParameters& params) noexcept;
+ void SetRawPidParameters(float p_kP, float p_recipTi, float p_tD) noexcept;
const PidParameters& GetPidParameters(bool forLoadChange) const noexcept
{
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp
index 76667f81..50d2171d 100644
--- a/src/Heating/Heat.cpp
+++ b/src/Heating/Heat.cpp
@@ -1320,7 +1320,127 @@ void Heat::ProcessRemoteHeaterTuningReport(CanAddress src, const CanMessageHeate
#if SUPPORT_REMOTE_COMMANDS
-GCodeResult Heat::EutProcessM308(const CanMessageGeneric& msg, const StringRef& reply) noexcept
+static GCodeResult UnknownHeater(unsigned int heater, const StringRef& reply) noexcept
+{
+ reply.printf("Board %u does not have heater %u", CanInterface::GetCanAddress(), heater);
+ return GCodeResult::error;
+}
+
+GCodeResult Heat::ConfigureHeater(const CanMessageGeneric& msg, const StringRef& reply) noexcept
+{
+ CanMessageGenericParser parser(msg, M950HeaterParams);
+ uint16_t heater;
+ if (!parser.GetUintParam('H', heater))
+ {
+ return GCodeResult::remoteInternalError;
+ }
+
+ if (heater >= MaxHeaters)
+ {
+ reply.copy("Heater number out of range");
+ return GCodeResult::error;
+ }
+
+ PwmFrequency freq = DefaultFanPwmFreq;
+ const bool seenFreq = parser.GetUintParam('Q', freq);
+
+ String<StringLength50> pinName;
+ if (parser.GetStringParam('C', pinName.GetRef()))
+ {
+ uint16_t sensorNumber;
+ if (!parser.GetUintParam('T', sensorNumber))
+ {
+ reply.copy("Missing sensor number");
+ return GCodeResult::error;
+ }
+
+ WriteLocker lock(heatersLock);
+
+ Heater *oldHeater = nullptr;
+ std::swap(oldHeater, heaters[heater]);
+ delete oldHeater;
+
+ Heater *newHeater = new LocalHeater(heater);
+ const GCodeResult rslt = newHeater->ConfigurePortAndSensor(pinName.c_str(), freq, sensorNumber, reply);
+ if (rslt == GCodeResult::ok || rslt == GCodeResult::warning)
+ {
+ heaters[heater] = newHeater;
+ }
+ else
+ {
+ delete newHeater;
+ }
+ return rslt;
+ }
+
+ const auto h = FindHeater(heater);
+ if (h.IsNull())
+ {
+ return UnknownHeater(heater, reply);
+ }
+
+ if (seenFreq)
+ {
+ return h->SetPwmFrequency(freq, reply);
+ }
+
+ return h->ReportDetails(reply);
+}
+
+GCodeResult Heat::ProcessM307New(const CanMessageUpdateHeaterModelNew& msg, const StringRef& reply) noexcept
+{
+ const auto h = FindHeater(msg.heater);
+ return (h.IsNotNull()) ? h->SetOrReportModelNew(msg.heater, msg, reply) : UnknownHeater(msg.heater, reply);
+}
+
+GCodeResult Heat::SetTemperature(const CanMessageSetHeaterTemperature& msg, const StringRef& reply) noexcept
+{
+ const auto h = FindHeater(msg.heaterNumber);
+ return (h.IsNotNull()) ? h->SetTemperature(msg, reply) : UnknownHeater(msg.heaterNumber, reply);
+}
+
+GCodeResult Heat::SetFaultDetection(const CanMessageSetHeaterFaultDetectionParameters& msg, const StringRef& reply) noexcept
+{
+ const auto h = FindHeater(msg.heater);
+ return (h.IsNotNull())
+ ? h->SetFaultDetectionParameters(msg.maxTempExcursion, msg.maxFaultTime, reply)
+ : UnknownHeater(msg.heater, reply);
+}
+
+GCodeResult Heat::SetHeaterMonitors(const CanMessageSetHeaterMonitors& msg, const StringRef& reply) noexcept
+{
+ const auto h = FindHeater(msg.heater);
+ return (h.IsNotNull()) ? h->SetHeaterMonitors(msg, reply) : UnknownHeater(msg.heater, reply);
+}
+
+GCodeResult Heat::TuningCommand(const CanMessageHeaterTuningCommand& msg, const StringRef& reply) noexcept
+{
+ if (heaterBeingTuned != -1 && heaterBeingTuned != (int)msg.heaterNumber)
+ {
+ reply.printf("Heater %d is already being tuned", heaterBeingTuned);
+ return GCodeResult::error;
+ }
+ const auto h = FindHeater(msg.heaterNumber);
+ if (h.IsNull())
+ {
+ return UnknownHeater(msg.heaterNumber, reply);
+ }
+ heaterBeingTuned = (int)msg.heaterNumber; // setting this is OK even if we are stopping or fail to start tuning, because we check it in the heater task loop
+ return h->TuningCommand(msg, reply);
+}
+
+GCodeResult Heat::FeedForward(const CanMessageHeaterFeedForward& msg, const StringRef& reply) noexcept
+{
+ const auto h = FindHeater(msg.heaterNumber);
+ if (h.IsNull())
+ {
+ return UnknownHeater(msg.heaterNumber, reply);
+ }
+ h->FeedForwardAdjustment(msg.fanPwmAdjustment, msg.extrusionAdjustment);
+ return GCodeResult::ok;
+}
+
+GCodeResult Heat::ProcessM308(const CanMessageGeneric& msg, const StringRef& reply) noexcept
{
CanMessageGenericParser parser(msg, M308NewParams);
uint16_t sensorNum;
diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h
index 7bdfbb52..172bd80a 100644
--- a/src/Heating/Heat.h
+++ b/src/Heating/Heat.h
@@ -141,7 +141,14 @@ public:
#endif
#if SUPPORT_REMOTE_COMMANDS
- GCodeResult EutProcessM308(const CanMessageGeneric& msg, const StringRef& reply) noexcept;
+ GCodeResult ConfigureHeater(const CanMessageGeneric& msg, const StringRef& reply) noexcept;
+ GCodeResult ProcessM307New(const CanMessageUpdateHeaterModelNew& msg, const StringRef& reply) noexcept;
+ GCodeResult ProcessM308(const CanMessageGeneric& msg, const StringRef& reply) noexcept;
+ GCodeResult SetFaultDetection(const CanMessageSetHeaterFaultDetectionParameters& msg, const StringRef& reply) noexcept;
+ GCodeResult SetHeaterMonitors(const CanMessageSetHeaterMonitors& msg, const StringRef& reply) noexcept;
+ GCodeResult SetTemperature(const CanMessageSetHeaterTemperature& msg, const StringRef& reply) noexcept;
+ GCodeResult TuningCommand(const CanMessageHeaterTuningCommand& msg, const StringRef& reply) noexcept;
+ GCodeResult FeedForward(const CanMessageHeaterFeedForward& msg, const StringRef& reply) noexcept;
#endif
static ReadWriteLock sensorsLock; // needs to be public so that the OMT in EndstopsManager can lock it
diff --git a/src/Heating/Heater.cpp b/src/Heating/Heater.cpp
index 40b00321..19125f73 100644
--- a/src/Heating/Heater.cpp
+++ b/src/Heating/Heater.cpp
@@ -636,4 +636,69 @@ void Heater::SetAsBedOrChamberHeater() noexcept
}
}
+#if SUPPORT_REMOTE_COMMANDS
+
+GCodeResult Heater::SetHeaterMonitors(const CanMessageSetHeaterMonitors& msg, const StringRef& reply) noexcept
+{
+ for (size_t i = 0; i < min<size_t>(msg.numMonitors, MaxMonitorsPerHeater); ++i)
+ {
+ monitors[i].Set(msg.monitors[i].sensor, msg.monitors[i].limit, (HeaterMonitorAction)msg.monitors[i].action, (HeaterMonitorTrigger)msg.monitors[i].trigger);
+ }
+ return GCodeResult::ok;
+}
+
+GCodeResult Heater::SetOrReportModelNew(unsigned int heater, const CanMessageUpdateHeaterModelNew& msg, const StringRef& reply) noexcept
+{
+ const GCodeResult rslt = SetModel(msg.heatingRate, msg.coolingRate, msg.coolingRateChangeFanOn, msg.deadTime, msg.maxPwm, msg.standardVoltage, msg.usePid, msg.inverted, reply);
+ if (msg.pidParametersOverridden && (rslt == GCodeResult::ok || rslt == GCodeResult::warning))
+ {
+ SetRawPidParameters(msg.kP, msg.recipTi, msg.tD);
+ }
+ return rslt;
+}
+
+GCodeResult Heater::SetTemperature(const CanMessageSetHeaterTemperature& msg, const StringRef& reply) noexcept
+{
+ switch (msg.command)
+ {
+ case CanMessageSetHeaterTemperature::commandNone:
+ activeTemperature = standbyTemperature = msg.setPoint;
+ return GCodeResult::ok;
+
+ case CanMessageSetHeaterTemperature::commandOff:
+ activeTemperature = standbyTemperature = msg.setPoint;
+ SwitchOff();
+ return GCodeResult::ok;
+
+ case CanMessageSetHeaterTemperature::commandOn:
+ activeTemperature = standbyTemperature = msg.setPoint;
+ return SwitchOn(reply);
+
+ case CanMessageSetHeaterTemperature::commandResetFault:
+ activeTemperature = standbyTemperature = msg.setPoint;
+ return ResetFault(reply);
+
+ case CanMessageSetHeaterTemperature::commandSuspend:
+ Suspend(true);
+ return GCodeResult::ok;
+
+ case CanMessageSetHeaterTemperature::commandUnsuspend:
+ activeTemperature = standbyTemperature = msg.setPoint;
+ Suspend(false);
+ return GCodeResult::ok;
+
+ case CanMessageSetHeaterTemperature::commandReset:
+ ResetHeater();
+ return GCodeResult::ok;
+
+ default:
+ break;
+ }
+
+ reply.printf("Unknown command %u to heater %u", msg.command, heaterNumber);
+ return GCodeResult::ok;
+}
+
+#endif
+
// End
diff --git a/src/Heating/Heater.h b/src/Heating/Heater.h
index ccff80fe..c9029ed4 100644
--- a/src/Heating/Heater.h
+++ b/src/Heating/Heater.h
@@ -20,12 +20,20 @@
# include "CanId.h"
#endif
+
#define TUNE_WITH_HALF_FAN 0
class HeaterMonitor;
struct CanMessageHeaterTuningReport;
struct CanHeaterReport;
+#if SUPPORT_REMOTE_COMMANDS
+struct CanMessageUpdateHeaterModelNew;
+struct CanMessageSetHeaterTemperature;
+struct CanMessageSetHeaterMonitors;
+struct CanMessageHeaterTuningCommand;
+#endif
+
// Enumeration to describe the status of a heater. Note that the web interface returns the numerical values, so don't change them.
NamedEnum(HeaterStatus, uint8_t, off, standby, active, fault, tuning, offline);
@@ -81,6 +89,13 @@ public:
const FopDt& GetModel() const noexcept { return model; } // Get the process model
GCodeResult SetOrReportModel(unsigned int heater, GCodeBuffer& gb, const StringRef& reply) THROWS(GCodeException);
+#if SUPPORT_REMOTE_COMMANDS
+ virtual GCodeResult TuningCommand(const CanMessageHeaterTuningCommand& msg, const StringRef& reply) noexcept = 0;
+ GCodeResult SetOrReportModelNew(unsigned int heater, const CanMessageUpdateHeaterModelNew& msg, const StringRef& reply) noexcept;
+ GCodeResult SetTemperature(const CanMessageSetHeaterTemperature& msg, const StringRef& reply) noexcept;
+ GCodeResult SetHeaterMonitors(const CanMessageSetHeaterMonitors& msg, const StringRef& reply) noexcept;
+#endif
+
bool IsHeaterEnabled() const noexcept // Is this heater enabled?
{ return model.IsEnabled(); }
@@ -126,6 +141,10 @@ protected:
void CalculateModel(HeaterParameters& params) noexcept; // calculate G, td and tc from the accumulated readings
void SetAndReportModel(bool usingFans) noexcept;
+#if SUPPORT_REMOTE_COMMANDS
+ void SetRawPidParameters(float p_kP, float p_recipTi, float p_tD) noexcept { model.SetRawPidParameters(p_kP, p_recipTi, p_tD); }
+#endif
+
HeaterMonitor monitors[MaxMonitorsPerHeater]; // embedding them in the Heater uses less memory than dynamic allocation
bool tuned; // true if tuning was successful
diff --git a/src/Heating/LocalHeater.cpp b/src/Heating/LocalHeater.cpp
index c3a20f7c..1e2192f8 100644
--- a/src/Heating/LocalHeater.cpp
+++ b/src/Heating/LocalHeater.cpp
@@ -14,6 +14,33 @@
#include <Platform/RepRap.h>
#include <Tools/Tool.h>
+#if SUPPORT_REMOTE_COMMANDS
+
+# include <CAN/CanInterface.h>
+
+namespace ExpansionMode
+{
+
+// Variables used during heater tuning
+static float tuningHighTemp; // the target upper temperature
+static float tuningLowTemp; // the target lower temperature
+static float tuningPeakTempDrop; // must be well below TuningHysteresis
+
+static uint32_t dHigh;
+static uint32_t dLow;
+static uint32_t tOn;
+static uint32_t tOff;
+static float heatingRate;
+static float coolingRate;
+static float tuningVoltage; // the VIN voltage with the heater on
+
+static uint16_t cyclesDone;
+static bool tuningCycleComplete;
+
+}
+
+#endif
+
// Member functions and constructors
LocalHeater::LocalHeater(unsigned int heaterNum) noexcept : Heater(heaterNum), mode(HeaterMode::off)
@@ -596,6 +623,25 @@ void LocalHeater::DoTuningStep() noexcept
break;
case HeaterMode::tuning1: // Heating up
+#if SUPPORT_REMOTE_COMMANDS
+ if (CanInterface::InExpansionMode())
+ {
+ if (temperature >= ExpansionMode::tuningHighTemp) // if reached target
+ {
+ // Move on to next phase
+ lastPwm = 0.0;
+ SetHeater(0.0);
+ peakTemp = afterPeakTemp = temperature;
+ lastOffTime = peakTime = afterPeakTime = now;
+ mode = HeaterMode::tuning2;
+ }
+ else
+ {
+ lastPwm = tuningPwm;
+ }
+ return;
+ }
+#endif
tuningPhase = 1;
{
const bool isBedOrChamberHeater = reprap.GetHeat().IsBedOrChamberHeater(GetHeaterNumber());
@@ -613,24 +659,55 @@ void LocalHeater::DoTuningStep() noexcept
reprap.GetPlatform().Message(GenericMessage, "Auto tune cancelled because target temperature was not reached\n");
break;
}
+ }
- if (temperature >= tuningTargetTemp) // if reached target
- {
- // Move on to next phase
- lastPwm = 0.0;
- SetHeater(0.0);
- peakTemp = afterPeakTemp = temperature;
- lastOffTime = peakTime = afterPeakTime = now;
- tuningVoltage.Clear();
- idleCyclesDone = 0;
- mode = HeaterMode::tuning2;
- tuningPhase = 2;
- ReportTuningUpdate();
- }
+ if (temperature >= tuningTargetTemp) // if reached target
+ {
+ // Move on to next phase
+ lastPwm = 0.0;
+ SetHeater(0.0);
+ peakTemp = afterPeakTemp = temperature;
+ lastOffTime = peakTime = afterPeakTime = now;
+ tuningVoltage.Clear();
+ idleCyclesDone = 0;
+ mode = HeaterMode::tuning2;
+ tuningPhase = 2;
+ ReportTuningUpdate();
}
return;
case HeaterMode::tuning2: // Heater is off, record the peak temperature and time
+#if SUPPORT_REMOTE_COMMANDS
+ if (CanInterface::InExpansionMode())
+ {
+ if (temperature >= peakTemp)
+ {
+ peakTemp = afterPeakTemp = temperature;
+ peakTime = afterPeakTime = now;
+ }
+ else if (temperature < ExpansionMode::tuningLowTemp)
+ {
+ // Temperature has dropped below the low limit.
+ // If we have been doing idle cycles, see whether we can switch to collecting data, and turn the heater on.
+ // If we have been collecting data, see if we have enough, and either turn the heater on to start another cycle or finish tuning.
+
+ // Save the data (don't know whether we need it yet)
+ ExpansionMode::dHigh = peakTime - lastOffTime;
+ ExpansionMode::tOff = now - lastOffTime;
+ ExpansionMode::coolingRate = (afterPeakTemp - temperature) * SecondsToMillis/(now - afterPeakTime);
+ lastOnTime = peakTime = afterPeakTime = now;
+ peakTemp = afterPeakTemp = temperature;
+ lastPwm = tuningPwm; // turn on heater at specified power
+ mode = HeaterMode::tuning3;
+ }
+ else if (afterPeakTime == peakTime && ExpansionMode::tuningHighTemp - temperature >= ExpansionMode::tuningPeakTempDrop)
+ {
+ afterPeakTime = now;
+ afterPeakTemp = temperature;
+ }
+ return;
+ }
+#endif
if (temperature >= peakTemp)
{
peakTemp = afterPeakTemp = temperature;
@@ -729,6 +806,40 @@ void LocalHeater::DoTuningStep() noexcept
return;
case HeaterMode::tuning3: // Heater is turned on, record the lowest temperature and time
+#if SUPPORT_REMOTE_COMMANDS
+ if (CanInterface::InExpansionMode())
+ {
+ if (temperature <= peakTemp)
+ {
+ peakTemp = afterPeakTemp = temperature;
+ peakTime = afterPeakTime = now;
+ }
+ else if (temperature >= ExpansionMode::tuningHighTemp)
+ {
+ // We have reached the target temperature, so record a data point and turn the heater off
+# if HAS_VOLTAGE_MONITOR
+ ExpansionMode::tuningVoltage = reprap.GetPlatform().GetCurrentPowerVoltage(); // save this while the heater is on
+# else
+ tuningVoltage = 0.0;
+# endif
+ ExpansionMode::dLow = peakTime - lastOnTime;
+ ExpansionMode::tOn = now - lastOnTime;
+ ExpansionMode::heatingRate = (temperature - afterPeakTemp) * SecondsToMillis/(now - afterPeakTime);
+ lastOffTime = peakTime = afterPeakTime = now;
+ peakTemp = afterPeakTemp = temperature;
+ lastPwm = 0.0; // turn heater off
+ mode = HeaterMode::tuning2;
+ ++ExpansionMode::cyclesDone;
+ ExpansionMode::tuningCycleComplete = true;
+ }
+ else if (afterPeakTime == peakTime && temperature - ExpansionMode::tuningLowTemp >= ExpansionMode::tuningPeakTempDrop)
+ {
+ afterPeakTime = now;
+ afterPeakTemp = temperature;
+ }
+ return;
+ }
+#endif
#if HAS_VOLTAGE_MONITOR
tuningVoltage.Add(reprap.GetPlatform().GetCurrentPowerVoltage());
#endif
@@ -800,4 +911,56 @@ void LocalHeater::RaiseHeaterFault(const char *format, ...) noexcept
reprap.FlagTemperatureFault(GetHeaterNumber());
}
+#if SUPPORT_REMOTE_COMMANDS
+
+// Start or stop running heater tuning cycles
+GCodeResult LocalHeater::TuningCommand(const CanMessageHeaterTuningCommand& msg, const StringRef& reply) noexcept
+{
+ if (msg.on)
+ {
+ if (lastPwm > 0.0 || GetAveragePWM() > 0.02)
+ {
+ reply.printf("heater %u must be off and cold before auto tuning it", GetHeaterNumber());
+ return GCodeResult::error;
+ }
+
+ // We could do some more checks here but the main board should have done all the checks needed already
+ ExpansionMode::tuningHighTemp = msg.highTemp;
+ ExpansionMode::tuningLowTemp = msg.lowTemp;
+ tuningPwm = msg.pwm;
+ ExpansionMode::tuningPeakTempDrop = msg.peakTempDrop;
+ timeSetHeating = millis();
+ ExpansionMode::tuningCycleComplete = false;
+ ExpansionMode::cyclesDone = 0;
+ mode = HeaterMode::tuning1;
+ }
+ else
+ {
+ SwitchOff();
+ }
+ return GCodeResult::ok;
+}
+
+// Get a heater tuning cycle report, if we have one. Caller must fill in the heater number.
+/*static*/ bool LocalHeater::GetTuningCycleData(CanMessageHeaterTuningReport& msg) noexcept
+{
+ if (ExpansionMode::tuningCycleComplete)
+ {
+ msg.cyclesDone = ExpansionMode::cyclesDone;
+ msg.dhigh = ExpansionMode::dHigh;
+ msg.dlow = ExpansionMode::dLow;
+ msg.ton = ExpansionMode::tOn;
+ msg.toff = ExpansionMode::tOff;
+ msg.heatingRate = ExpansionMode::heatingRate;
+ msg.coolingRate = ExpansionMode::coolingRate;
+ msg.voltage = ExpansionMode::tuningVoltage;
+ ExpansionMode::tuningCycleComplete = false;
+ return true;
+ }
+
+ return false;
+}
+
+#endif
+
// End
diff --git a/src/Heating/LocalHeater.h b/src/Heating/LocalHeater.h
index e1b6b74b..5dc2c6bd 100644
--- a/src/Heating/LocalHeater.h
+++ b/src/Heating/LocalHeater.h
@@ -40,13 +40,18 @@ public:
void Suspend(bool sus) noexcept override; // Suspend the heater to conserve power or while doing Z probing
void FeedForwardAdjustment(float fanPwmChange, float extrusionChange) noexcept override;
void SetExtrusionFeedForward(float pwm) noexcept override; // Set extrusion feedforward
-
#if SUPPORT_CAN_EXPANSION
bool IsLocal() const noexcept override { return true; }
void UpdateRemoteStatus(CanAddress src, const CanHeaterReport& report) noexcept override { }
void UpdateHeaterTuning(CanAddress src, const CanMessageHeaterTuningReport& msg) noexcept override { }
#endif
+#if SUPPORT_REMOTE_COMMANDS
+ GCodeResult TuningCommand(const CanMessageHeaterTuningCommand& msg, const StringRef& reply) noexcept override;
+
+ static bool GetTuningCycleData(CanMessageHeaterTuningReport& msg) noexcept; // get a heater tuning cycle report, if we have one
+#endif
+
protected:
void ResetHeater() noexcept override;
HeaterMode GetMode() const noexcept override { return mode; }
diff --git a/src/Heating/RemoteHeater.cpp b/src/Heating/RemoteHeater.cpp
index 8e522f6c..e3e52912 100644
--- a/src/Heating/RemoteHeater.cpp
+++ b/src/Heating/RemoteHeater.cpp
@@ -513,6 +513,17 @@ void RemoteHeater::StopTuning() noexcept
}
}
+#if SUPPORT_REMOTE_COMMANDS
+
+// This should never be called
+GCodeResult RemoteHeater::TuningCommand(const CanMessageHeaterTuningCommand& msg, const StringRef& reply) noexcept
+{
+ reply.copy("not supported on remote heaters");
+ return GCodeResult::error;
+}
+
+#endif
+
#endif
// End
diff --git a/src/Heating/RemoteHeater.h b/src/Heating/RemoteHeater.h
index 91c96ab0..a6c09029 100644
--- a/src/Heating/RemoteHeater.h
+++ b/src/Heating/RemoteHeater.h
@@ -35,6 +35,10 @@ public:
void UpdateRemoteStatus(CanAddress src, const CanHeaterReport& report) noexcept override;
void UpdateHeaterTuning(CanAddress src, const CanMessageHeaterTuningReport& msg) noexcept override;
+#if SUPPORT_REMOTE_COMMANDS
+ GCodeResult TuningCommand(const CanMessageHeaterTuningCommand& msg, const StringRef& reply) noexcept override;
+#endif
+
protected:
void ResetHeater() noexcept override;
HeaterMode GetMode() const noexcept override;