- Settings split on heater index

-General impl of hookRegistered functions
-registerRegexMsg recevies a compiled regex
-single regex for all pid types of respond
This commit is contained in:
sergiuToporjinschi
2022-02-04 10:14:46 +02:00
parent c3f86acc7d
commit 8b6cc8c444
9 changed files with 168 additions and 61 deletions
+1 -1
View File
@@ -74,7 +74,7 @@ class API(octoprint.plugin.SimpleApiPlugin):
self._printer.extrude(amount=int(extrudeLength), speed=int(extrudeSpeed)) self._printer.extrude(amount=int(extrudeLength), speed=int(extrudeSpeed))
@staticmethod @staticmethod
def m92GCodeResponse(self, line, event): def m92GCodeResponse(self, line, regex, event):
reg = re.compile("echo:\s*(?P<command>(?P<gCode>M\d{1,3}) X(?P<xVal>\d{1,3}.\d{1,3}) Y(?P<yVal>\d{1,3}.\d{1,3}) Z(?P<zVal>\d{1,3}.\d{1,3}) E(?P<eVal>\d{1,3}.\d{1,3}))") reg = re.compile("echo:\s*(?P<command>(?P<gCode>M\d{1,3}) X(?P<xVal>\d{1,3}.\d{1,3}) Y(?P<yVal>\d{1,3}.\d{1,3}) Z(?P<zVal>\d{1,3}.\d{1,3}) E(?P<eVal>\d{1,3}.\d{1,3}))")
isM92command = reg.match(line) isM92command = reg.match(line)
if isM92command: if isM92command:
+28 -20
View File
@@ -12,17 +12,20 @@ CMD_PID_START = "pid_start"
CMD_PID_LOAD_CURRENT_VALUES = "pid_getCurrentValues" CMD_PID_LOAD_CURRENT_VALUES = "pid_getCurrentValues"
CMD_PID_GET_VALUES = "pid_getValues" CMD_PID_GET_VALUES = "pid_getValues"
regexPID = "Kp:\s*(?P<P>\d{1,3}.\d{1,3})\s*Ki:\s*(?P<I>\d{1,3}.\d{1,3})\s*Kd:\s*(?P<D>\d{1,3}.\d{1,3})" #this regex matches:
regexGetPid = "\s*Kp:\s*(?P<p>\d{1,3}.\d{1,3})\s*Ki:\s*(?P<i>\d{1,3}.\d{1,3})\s*Kd:\s*(?P<d>\d{1,3}.\d{1,3})" # !!DEBUG:send echo: Kp: 30.56 Ki: 3.03 Kd: 77.16
PIDStarted = "PID Autotune start" # !!DEBUG:send Kp: 30.56 Ki: 3.03 Kd: 77.16
PIDStoped = "PID Autotune finished!" # !!DEBUG:send echo: p:18.84 i:1.18 d:201.41
PIDStopedP = "#define DEFAULT_Kp\s*(?P<P>\d{1,3}.\d{1,3})" # !!DEBUG:send p:18.84 i:1.18 d:201.41
PIDStopedI = "#define DEFAULT_Ki\s*(?P<I>\d{1,3}.\d{1,3})" # !!DEBUG:send echo: M304 P131.06 I11.79 D971.23
PIDStopedD = "#define DEFAULT_Kd\s*(?P<D>\d{1,3}.\d{1,3})" # !!DEBUG:send M304 P131.06 I11.79 D971.23
regexFinished = "PID Autotune finished!.*\n#define DEFAULT_Kp\s*(?P<P>\d{1,3}.\d{1,3}).*\n#define DEFAULT_Ki\s*(?P<I>\d{1,3}.\d{1,3})\n#define DEFAULT_Kd\s*(?P<D>\d{1,3}.\d{1,3})" allPIDsFormats = r".*p:{0,1}\s{0,1}?(?P<p>\d{1,3}.\d{1,3})\sk*i:{0,1}\s{0,1}?(?P<i>\d{1,3}.\d{1,3})\sk*d:{0,1}\s{0,1}?(?P<d>\d{1,3}.\d{1,3})"
class API(octoprint.plugin.SimpleApiPlugin): class API(octoprint.plugin.SimpleApiPlugin):
pidHotEndCycles = [] pidHotEndCycles = []
pidCurrentValues = {} pidCurrentValues = {}
#catch for "echo: p:28.27 i:2.82 d:70.81" or "M301 P27.08 I2.51 D73.09"
getPid = re.compile(allPIDsFormats, flags=re.IGNORECASE)
@staticmethod @staticmethod
def apiCommands(): def apiCommands():
return { return {
@@ -35,14 +38,10 @@ class API(octoprint.plugin.SimpleApiPlugin):
def apiGateWay(self, command, data): def apiGateWay(self, command, data):
self._logger.debug("DIPGateway") self._logger.debug("DIPGateway")
if command == CMD_PID_LOAD_CURRENT_VALUES: if command == CMD_PID_LOAD_CURRENT_VALUES:
#catch for "echo: p:28.27 i:2.82 d:70.81" or "M301 P27.08 I2.51 D73.09"
getPid = re.compile(r".*p:??(?P<p>\d{1,3}.\d{1,3})\s*i:?(?P<i>\d{1,3}.\d{1,3})\s*d:?(?P<d>\d{1,3}.\d{1,3})", flags=re.IGNORECASE)
hasResult301 = Event() hasResult301 = Event()
hasResult304 = Event() hasResult304 = Event()
self.registerRegexMsg(getPid, self.m301_m304CodeResponse, hasResult301, "hotEnd") self.registerRegexMsg(self.getPid, self.m301_m304CodeResponse, hasResult301, "hotEnd")
self.registerRegexMsg(getPid, self.m301_m304CodeResponse, hasResult304, "bed") self.registerRegexMsg(self.getPid, self.m301_m304CodeResponse, hasResult304, "bed")
self._printer.commands(["M301", "M304"]) self._printer.commands(["M301", "M304"])
hasResult301.wait(5) hasResult301.wait(5)
@@ -53,14 +52,16 @@ class API(octoprint.plugin.SimpleApiPlugin):
}) })
if command == CMD_PID_START: if command == CMD_PID_START:
self.pidHotEndCycles = [] self.pidHotEndCycles = {
"hotEnd": [],
"bed":[]
}
#Two cycles are for tuning #Two cycles are for tuning
for i in range(0, data['noCycles'] - 2): for i in range(0, data['noCycles'] - 2):
self.registerRegexMsg(regexGetPid, self.m106CodeResponse) #response type !!DEBUG:send Kp: 30.56 Ki: 3.03 Kd: 77.16
self.registerRegexMsg(self.getPid, self.m106CodeResponse, data["heater"])
self._printer.commands(["M106 S%(fanSpeed)s" % data, "M303 C%(noCycles)s E%(hotEndIndex)s S%(targetTemp)s U1" % data]) self._printer.commands(["M106 S%(fanSpeed)s" % data, "M303 C%(noCycles)s E%(hotEndIndex)s S%(targetTemp)s U1" % data])
self._logger.debug("cycles %s", self.pidHotEndCycles)
if command == CMD_PID_SAVE: if command == CMD_PID_SAVE:
self._logger.debug("DIPSave-") self._logger.debug("DIPSave-")
@@ -87,6 +88,13 @@ class API(octoprint.plugin.SimpleApiPlugin):
event.set() event.set()
@staticmethod @staticmethod
def m106CodeResponse(self, line): def m106CodeResponse(self, line, regex, storingKey):
self._logger.debug("m106CodeResponse: %s", line) self._logger.debug("m106CodeResponse: %s", line)
self.pidHotEndCycles.append(line) match = regex.match(line)
if match:
self.pidHotEndCycles[storingKey].append({
"P": match.group("p"),
"I": match.group("i"),
"D": match.group("d")
})
self._logger.debug("cycles %s", self.pidHotEndCycles)
+11 -4
View File
@@ -15,10 +15,17 @@ defaultSettings = {
"markLength": 120 "markLength": 120
}, },
"pid": { "pid": {
"fanSpeed": 255, "hotEnd":{
"noCycles": 5, "fanSpeed": 255,
"hotEndIndex": 0, "noCycles": 8,
"targetTemp": 200, "hotEndIndex": 0,
"targetTemp": 200,
},"bed":{
"fanSpeed": 255,
"noCycles": 8,
"hotEndIndex": -1,
"targetTemp": 70,
}
} }
} }
+2 -2
View File
@@ -88,7 +88,7 @@ class Hooks():
if command is None or not reg.match(command.upper()): if command is None or not reg.match(command.upper()):
self._logger.warn("registerGCodeAnswer: Attempt to register gCodeAnswer without a function or valid gCode command") self._logger.warn("registerGCodeAnswer: Attempt to register gCodeAnswer without a function or valid gCode command")
return return
self.registerRegexMsg(".*\s*(?P<gCode>[M,G]\d{1,4})", func, *arguments) self.registerRegexMsg(reg, func, *arguments)
def registerRegexMsg(self, regex, func, *arguments): def registerRegexMsg(self, regex, func, *arguments):
if regex is None or func is None or not isinstance(func, collections.Callable): if regex is None or func is None or not isinstance(func, collections.Callable):
@@ -96,7 +96,7 @@ class Hooks():
return return
self.gCodeWaiters.append({ self.gCodeWaiters.append({
"regex": re.compile(regex), "regex": regex,
"func": func, "func": func,
"args": arguments, "args": arguments,
"callCount": 0 "callCount": 0
@@ -48,7 +48,7 @@ $(function () {
self.steps["E"](response.data.E); self.steps["E"](response.data.E);
} }
self.loadEStepsActive = ko.observable(true) self.loadEStepsActive = ko.observable(true);
self.loadESteps = function () { self.loadESteps = function () {
self.loadEStepsActive(false); self.loadEStepsActive(false);
OctoPrint.simpleApiCommand("CalibrationTools", "eSteps_load").done(function (response) { OctoPrint.simpleApiCommand("CalibrationTools", "eSteps_load").done(function (response) {
@@ -20,10 +20,18 @@ $(function () {
self.isAdmin = ko.observable(false); self.isAdmin = ko.observable(false);
self.pid = { self.pid = {
fanSpeed: ko.observable(255), "hotEnd": {
noCycles: ko.observable(5), fanSpeed: ko.observable(255),
hotEndIndex: ko.observable(0), noCycles: ko.observable(8),
targetTemp: ko.observable(200) hotEndIndex: ko.observable(0),
targetTemp: ko.observable(200)
},
"bed": {
fanSpeed: ko.observable(255),
noCycles: ko.observable(8),
index: ko.observable(-1),
targetTemp: ko.observable(200)
}
}; };
/** /**
@@ -48,19 +56,45 @@ $(function () {
}; };
self.onBeforeBinding = self.onUserLoggedIn = self.onUserLoggedOut = function () { self.onBeforeBinding = self.onUserLoggedIn = self.onUserLoggedOut = function () {
self.pid.fanSpeed(self.settingsViewModel.settings.plugins.CalibrationTools.pid.fanSpeed()); self.pid.hotEnd.fanSpeed(self.settingsViewModel.settings.plugins.CalibrationTools.pid.hotEnd.fanSpeed());
self.pid.noCycles(self.settingsViewModel.settings.plugins.CalibrationTools.pid.noCycles()); self.pid.hotEnd.hotEndIndex(self.settingsViewModel.settings.plugins.CalibrationTools.pid.hotEnd.hotEndIndex());
self.pid.hotEndIndex(self.settingsViewModel.settings.plugins.CalibrationTools.pid.hotEndIndex()); self.pid.hotEnd.noCycles(self.settingsViewModel.settings.plugins.CalibrationTools.pid.hotEnd.noCycles());
self.pid.targetTemp(self.settingsViewModel.settings.plugins.CalibrationTools.pid.targetTemp()); self.pid.hotEnd.targetTemp(self.settingsViewModel.settings.plugins.CalibrationTools.pid.hotEnd.targetTemp());
self.pid.bed.index(-1);
self.pid.bed.noCycles(self.settingsViewModel.settings.plugins.CalibrationTools.pid.bed.noCycles());
self.pid.bed.targetTemp(self.settingsViewModel.settings.plugins.CalibrationTools.pid.bed.targetTemp());
self.isAdmin(self.loginStateViewModel.isAdmin()); self.isAdmin(self.loginStateViewModel.isAdmin());
} }
self.startPidHotEnd = function () { self.startPidHotEnd = function () {
OctoPrint.simpleApiCommand("CalibrationTools", "pid_start", { OctoPrint.simpleApiCommand("CalibrationTools", "pid_start", {
"fanSpeed": self.pid.fanSpeed(), "fanSpeed": Number(self.pid.hotEnd.fanSpeed()),
"noCycles": self.pid.noCycles(), "noCycles": Number(self.pid.hotEnd.noCycles()),
"hotEndIndex": self.pid.hotEndIndex(), "hotEndIndex": Number(self.pid.hotEnd.hotEndIndex()),
"targetTemp": self.pid.targetTemp() "targetTemp": Number(self.pid.hotEnd.targetTemp())
}).done(function (response) {
new PNotify({
title: "PID HotEnd tuning has started",
text: "In progress",
type: "info"
});
console.log(response);
}).fail(function (response) {
new PNotify({
title: "Error on starting PID autotune ",
text: response.responseJSON.error,
type: "error",
hide: false
});
});
}
self.startPidBed = function () {
OctoPrint.simpleApiCommand("CalibrationTools", "pid_start", {
"heater": "bed",
"fanSpeed": self.pid.bed.fanSpeed(),
"noCycles": self.pid.bed.noCycles(),
"hotEndIndex": self.pid.bed.hotEndIndex(),
"targetTemp": self.pid.bed.targetTemp()
}).done(function (response) { }).done(function (response) {
new PNotify({ new PNotify({
title: "PID HotEnd tuning has started", title: "PID HotEnd tuning has started",
@@ -5,7 +5,7 @@
<div class="span6"><input id="userControlsTemp" type="checkbox" data-bind="checked: settings.plugins.CalibrationTools.eSteps.userControlsTemp"></div> <div class="span6"><input id="userControlsTemp" type="checkbox" data-bind="checked: settings.plugins.CalibrationTools.eSteps.userControlsTemp"></div>
</div> </div>
<div class="row-fluid"> <div class="row-fluid">
<div class="span6"><label for="turnOffHotend" class="checkbox pull-right" style="margin-top: 5px;">Cooldown extruder when saving to EEPROM</label></div> <div class="span6"><label for="turnOffHotend" class="checkbox pull-right" style="margin-top: 5px;">Cooling extruder when saving to EEPROM</label></div>
<div class="span6"><input id="turnOffHotend" type="checkbox" data-bind="checked: settings.plugins.CalibrationTools.eSteps.turnOffHotend, <div class="span6"><input id="turnOffHotend" type="checkbox" data-bind="checked: settings.plugins.CalibrationTools.eSteps.turnOffHotend,
disable: settings.plugins.CalibrationTools.eSteps.userControlsTemp"> disable: settings.plugins.CalibrationTools.eSteps.userControlsTemp">
</div> </div>
@@ -25,8 +25,12 @@
{{ snipped.subSection("X-Y-Z-Steps", true) }} {{ snipped.subSection("X-Y-Z-Steps", true) }}
{{ snipped.subSection("PID", true) }} {{ snipped.subSection("Hot-end PID", true) }}
{{ snipped.field("Fan speed", "Default value for fan speed while tuning", "number", "settings.plugins.CalibrationTools.pid.fanSpeed", "true", "", 1, 0, 255) }} {{ snipped.field("Fan speed", "Default value for fan speed while tuning", "number", "settings.plugins.CalibrationTools.pid.hotEnd.fanSpeed", "true", "", 1, 0, 255) }}
{{ snipped.field("Number of cycles", "Default number of cycles to sample while tuning", "number", "settings.plugins.CalibrationTools.pid.noCycles", "true", "", 1, 3, 200) }} {{ snipped.field("Number of cycles", "Default number of cycles to sample while tuning", "number", "settings.plugins.CalibrationTools.pid.hotEnd.noCycles", "true", "", 1, 3, 200) }}
{{ snipped.field("HotEnd index", "Default number of cycles to sample while tuning", "number", "settings.plugins.CalibrationTools.pid.hotEndIndex", "true", "", 1, 0) }} {{ snipped.field("HotEnd index", "Default number of cycles to sample while tuning", "number", "settings.plugins.CalibrationTools.pid.hotEnd.hotEndIndex", "true", "", 1, 0) }}
{{ snipped.field("Target temperature", "Default target temperature for tuning", "number", "settings.plugins.CalibrationTools.pid.targetTemp", "true", "&ordmC", 1, 3, 200) }} {{ snipped.field("Target temperature", "Default target temperature for tuning", "number", "settings.plugins.CalibrationTools.pid.hotEnd.targetTemp", "true", "&ordmC", 1, 3, 200) }}
{{ snipped.subSection("Bed PID", true) }}
{{ snipped.field("Fan speed", "Default value for fan speed while tuning", "number", "settings.plugins.CalibrationTools.pid.bed.fanSpeed", "true", "", 1, 0, 255) }}
{{ snipped.field("Number of cycles", "Default number of cycles to sample while tuning", "number", "settings.plugins.CalibrationTools.pid.bed.noCycles", "true", "", 1, 3, 200) }}
{{ snipped.field("Target temperature", "Default target temperature for tuning", "number", "settings.plugins.CalibrationTools.pid.bed.targetTemp", "true", "&ordmC", 1, 3, 200) }}
@@ -61,7 +61,7 @@
</div> </div>
<div class="span6"> <div class="span6">
<div class="input-append"> <div class="input-append">
<input type="number" id="eSteps" title="Current value for number of steps/mm for E axe in EEPROM" class="input-small" step="0.01" data-bind="value: $root.steps.E, enable:false"> <input type="number" id="eSteps" title="Current value for number of steps/mm for E axe in EEPROM" class="input-small" step="0.01" data-bind="value: $root.steps.E(), enable:false">
<span class="add-on" title="Current value for number of steps/mm for E axe in EEPROM">steps/mm</span> <span class="add-on" title="Current value for number of steps/mm for E axe in EEPROM">steps/mm</span>
<button class="btn" data-bind="click: $root.loadESteps, enable: $root.loadEStepsActive() && $root.controlViewModel.isOperational() && (!$root.controlViewModel.isPrinting())" <button class="btn" data-bind="click: $root.loadESteps, enable: $root.loadEStepsActive() && $root.controlViewModel.isOperational() && (!$root.controlViewModel.isPrinting())"
title="Loads current value of steps/mm from EEPROM by calling M92"> title="Loads current value of steps/mm from EEPROM by calling M92">
@@ -1,6 +1,6 @@
{% import "macros.jinja2" as snipped %} {% import "macros.jinja2" as snipped %}
{{ snipped.subSection("Hot-end tuning", true) }} {{ snipped.subSection("Current PID values", true) }}
<div class="row-fluid"> <div class="row-fluid">
<div class="span5"> <div class="span5">
<label for="hotEndPid" class="pull-right" style="margin-top: 5px;" title="Current hot-end PID"> <label for="hotEndPid" class="pull-right" style="margin-top: 5px;" title="Current hot-end PID">
@@ -15,9 +15,6 @@
<span class="add-on numberDisplay" title="Integral gain" data-bind="text: $root.pidCurrentValues.hotEnd.I()"></span> <span class="add-on numberDisplay" title="Integral gain" data-bind="text: $root.pidCurrentValues.hotEnd.I()"></span>
<span class="add-on" title="Derivative">D</span> <span class="add-on" title="Derivative">D</span>
<span class="add-on numberDisplay" title="Derivative" data-bind="text: $root.pidCurrentValues.hotEnd.D()"></span> <span class="add-on numberDisplay" title="Derivative" data-bind="text: $root.pidCurrentValues.hotEnd.D()"></span>
<button class="btn" data-bind="click: $root.getCurrentValues, enable: $root.controlViewModel.isOperational() && (!$root.controlViewModel.isPrinting())" title="Load current PIDs">
<i class="fas fa-sync-alt" style="color:false" data-color="false"></i>
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -38,7 +35,67 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row-fluid">
<div class="span5">
</div>
<div class="span7">
<div class="input-prepend input-append" id="bedPid">
<button class="btn" data-bind="click: $root.getCurrentValues, enable: $root.controlViewModel.isOperational() && (!$root.controlViewModel.isPrinting())" title="Load current PIDs">
<i class="fas fa-sync-alt" style="color:false" data-color="false"></i>&nbsp&nbsp
Get current values
</button>
</div>
</div>
</div>
{{ snipped.subSection("Hot-end tuning", true) }}
<div class="row-fluid">
<div class="span5">
<label for="fanSpeed" class="pull-right" style="margin-top: 5px;" title="Fan speed">
Turn fan to max
</label>
</div>
<div class="span7">
<div class="input-prepend input-append">
{{ snipped.linkToMarlin("M106", "Marlin website") }}
<span class="add-on" title="Command for turning the fan to 100%">M106&nbsp;&nbsp;&nbsp;S</span>
<input type="number" id="fanSpeed" title="Command for turning the fan to 100%" class="input-mini" step="1" min="0" max="255" data-bind="value: $root.pid.hotEnd.fanSpeed">
</div>
</div>
</div>
<div class="row-fluid">
<div class="span5">
<label for="tunningPIDNoCycles" class="pull-right" style="margin-top: 5px;" title="x">
Tuning PID
</label>
</div>
<div class="span7">
<div class="input-prepend input-append">
{{ snipped.linkToMarlin("M303", "Marlin website") }}
<span class="add-on" title="Command for triggering tool PID tunning">M303&nbsp;&nbsp;&nbsp;C</span>
<input type="number" id="tunningPIDNoCycles" class="input-mini" step="1" min="0" title="Tool number 0 for first hot end" data-bind="value: $root.pid.hotEnd.noCycles">
<span class="add-on" title="">&nbsp;&nbsp;&nbsp;E</span>
<input type="number" id="tunningPIDHotEnd" class="input-mini" step="1" min="0" title="Hotend index 0 for first hot end" data-bind="value: $root.pid.hotEnd.hotEndIndex">
<span class="add-on" title="">&nbsp;&nbsp;&nbsp;S</span>
<input type="number" id="tunningPIDHotEndTemp" class="input-mini" step="1" min="100" max="280" title="Target temperature" data-bind="value: $root.pid.hotEnd.targetTemp">
<span class="add-on" title="">&nbsp;&nbsp;&nbsp;U1</span>
{{ snipped.m500Icon() }}
</div>
</div>
</div>
<div class="row-fluid" style="margin-bottom: 5px;">
<div class="span5"></div>
<div class="span7">
<button class="btn btn-success" data-bind="click: $root.startPidHotEnd, enable: $root.controlViewModel.isOperational() && (!$root.controlViewModel.isPrinting())"
title="This will trigger PID auto tuning (M106 Sx; M303 Ex Sx U1; M500)">
<i class="fas fa-play" style="color:false" data-color="false"></i>&nbsp&nbsp
Start hot-end PID tunning
</button>
</div>
</div>
{{ snipped.subSection("Heated bed tuning", true) }}
<div class="row-fluid"> <div class="row-fluid">
<div class="span5"> <div class="span5">
<label for="fanSpeed" class="pull-right" style="margin-top: 5px;" title="Fan speed"> <label for="fanSpeed" class="pull-right" style="margin-top: 5px;" title="Fan speed">
@@ -62,12 +119,12 @@
<div class="span7"> <div class="span7">
<div class="input-prepend input-append"> <div class="input-prepend input-append">
{{ snipped.linkToMarlin("M303", "Marlin website") }} {{ snipped.linkToMarlin("M303", "Marlin website") }}
<span class="add-on" title="Command for triggering tool PID tunning">M303&nbsp;&nbsp;&nbsp;C</span> <span class="add-on" title="Command for triggering bed PID tunning">M304&nbsp;&nbsp;&nbsp;C</span>
<input type="number" id="tunningPIDNoCycles" class="input-mini" step="1" min="0" title="Tool number 0 for first hot end" data-bind="value: $root.pid.noCycles"> <input type="number" id="tunningPIDNoCycles" class="input-mini" step="1" min="0" title="Number of cycles to run in tuning" data-bind="value: $root.pid.bed.noCycles">
<span class="add-on" title="">&nbsp;&nbsp;&nbsp;E</span> <span class="add-on" title="">&nbsp;&nbsp;&nbsp;E</span>
<input type="number" id="tunningPIDTool" class="input-mini" step="1" min="0" title="Hotend index 0 for first hot end" data-bind="value: $root.pid.hotEndIndex"> <input type="number" id="tunningPIDBed" class="input-mini" step="1" min="0" title="Bed index" data-bind="value: $root.pid.bed.index, enable: false">
<span class="add-on" title="">&nbsp;&nbsp;&nbsp;S</span> <span class="add-on" title="">&nbsp;&nbsp;&nbsp;S</span>
<input type="number" id="tunningPIDToolTemp" class="input-mini" step="1" min="100" max="280" title="Target temperature" data-bind="value: $root.pid.targetTemp"> <input type="number" id="tunningPIDBedTemp" class="input-mini" step="1" min="20" max="80" title="Target temperature" data-bind="value: $root.pid.bed.targetTemp">
<span class="add-on" title="">&nbsp;&nbsp;&nbsp;U1</span> <span class="add-on" title="">&nbsp;&nbsp;&nbsp;U1</span>
{{ snipped.m500Icon() }} {{ snipped.m500Icon() }}
</div> </div>
@@ -77,17 +134,14 @@
<div class="row-fluid" style="margin-bottom: 5px;"> <div class="row-fluid" style="margin-bottom: 5px;">
<div class="span5"></div> <div class="span5"></div>
<div class="span7"> <div class="span7">
<button class="btn btn-success" data-bind="click: $root.startPidHotEnd, enable: $root.controlViewModel.isOperational() && (!$root.controlViewModel.isPrinting())" <button class="btn btn-success" data-bind="click: $root.startPidBed, enable: $root.controlViewModel.isOperational() && (!$root.controlViewModel.isPrinting())"
title="This will trigger PID auto tuning (M106 Sx; M303 Ex Sx U1; M500)"> title="This will trigger PID auto tuning (M304 E-1 Sx U1; M500)">
<i class="fas fa-play" style="color:false" data-color="false"></i>&nbsp&nbsp <i class="fas fa-play" style="color:false" data-color="false"></i>&nbsp&nbsp
Start PID tunning Start bed PID tunning
</button> </button>
</div> </div>
</div> </div>
{{ snipped.subSection("Heated bed tuning", true) }}
{{ snipped.subSection("Note", true) }} {{ snipped.subSection("Note", true) }}
{{ snipped.quote("It is recommended to run the tuning with conditions as close to printing as possible. This means filament loaded and the part cooling fan set to your normal speed. It is not essential, but you may {{ snipped.quote("It is recommended to run the tuning with conditions as close to printing as possible. This means filament loaded and the part cooling fan set to your normal speed. It is not essential, but you may
prefer to start this process with the hot end at room temperature.", prefer to start this process with the hot end at room temperature.",