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:
-rw-r--r--.cproject8
-rw-r--r--Maths/BedLevelling-2-point.wxm57
-rw-r--r--Maths/BedLevelling-3-point.wxm57
-rw-r--r--Maths/BedLevelling-4-point.wxm71
-rw-r--r--Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta9.binbin0 -> 332988 bytes
-rw-r--r--Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta9.binbin0 -> 312164 bytes
-rw-r--r--Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta9.binbin0 -> 315636 bytes
-rw-r--r--Release/Duet-WiFi/Edge/DuetWiFiServer-1.19beta9.binbin0 -> 244400 bytes
-rw-r--r--Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta9.binbin0 -> 262604 bytes
-rw-r--r--src/DuetNG/DuetWiFi/Network.cpp13
-rw-r--r--src/DuetNG/Pins_DuetNG.h2
-rw-r--r--src/Fan.cpp18
-rw-r--r--src/Fan.h4
-rw-r--r--src/GCodes/GCodeBuffer.cpp2
-rw-r--r--src/GCodes/GCodes.cpp645
-rw-r--r--src/GCodes/GCodes.h64
-rw-r--r--src/GCodes/GCodes2.cpp129
-rw-r--r--src/Heating/FOPDT.cpp5
-rw-r--r--src/Heating/FOPDT.h2
-rw-r--r--src/Heating/Heat.cpp34
-rw-r--r--src/Heating/Heat.h5
-rw-r--r--src/Heating/Pid.cpp39
-rw-r--r--src/Heating/Pid.h8
-rw-r--r--src/Heating/Sensors/TemperatureSensor.cpp2
-rw-r--r--src/Movement/DDA.cpp22
-rw-r--r--src/Movement/DDA.h2
-rw-r--r--src/Movement/Kinematics/CartesianKinematics.cpp2
-rw-r--r--src/Movement/Kinematics/CartesianKinematics.h5
-rw-r--r--src/Movement/Kinematics/CoreBaseKinematics.cpp12
-rw-r--r--src/Movement/Kinematics/CoreBaseKinematics.h10
-rw-r--r--src/Movement/Kinematics/CoreXYKinematics.cpp40
-rw-r--r--src/Movement/Kinematics/CoreXYKinematics.h4
-rw-r--r--src/Movement/Kinematics/CoreXYUKinematics.cpp54
-rw-r--r--src/Movement/Kinematics/CoreXYUKinematics.h4
-rw-r--r--src/Movement/Kinematics/CoreXZKinematics.cpp40
-rw-r--r--src/Movement/Kinematics/CoreXZKinematics.h7
-rw-r--r--src/Movement/Kinematics/Kinematics.cpp31
-rw-r--r--src/Movement/Kinematics/Kinematics.h30
-rw-r--r--src/Movement/Kinematics/LinearDeltaKinematics.cpp63
-rw-r--r--src/Movement/Kinematics/LinearDeltaKinematics.h20
-rw-r--r--src/Movement/Kinematics/ScaraKinematics.cpp69
-rw-r--r--src/Movement/Kinematics/ScaraKinematics.h3
-rw-r--r--src/Movement/Kinematics/ZLeadscrewKinematics.cpp277
-rw-r--r--src/Movement/Kinematics/ZLeadscrewKinematics.h33
-rw-r--r--src/Movement/Move.cpp107
-rw-r--r--src/Movement/Move.h16
-rw-r--r--src/Platform.cpp118
-rw-r--r--src/Platform.h26
-rw-r--r--src/PortControl.cpp49
-rw-r--r--src/PrintMonitor.cpp2
-rw-r--r--src/PrintMonitor.h4
-rw-r--r--src/RepRap.cpp60
-rw-r--r--src/RepRap.h9
-rw-r--r--src/Tools/Tool.cpp83
-rw-r--r--src/Tools/Tool.h23
-rw-r--r--src/Version.h4
56 files changed, 1782 insertions, 612 deletions
diff --git a/.cproject b/.cproject
index 6b5c3a30..e2c71f28 100644
--- a/.cproject
+++ b/.cproject
@@ -59,7 +59,7 @@
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.831729502" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.586905748" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
- <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} ${workspace_loc:/${CoreName}/SAM3X8E/cores/arduino/syscalls.o} ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.77650722" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
+ <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} &quot;${workspace_loc:/${CoreName}/SAM3X8E/cores/arduino/syscalls.o}&quot; ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.77650722" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<option id="gnu.cpp.link.option.nostdlibs.296038599" name="No startup or default libs (-nostdlib)" superClass="gnu.cpp.link.option.nostdlibs" value="false" valueType="boolean"/>
<option id="gnu.cpp.link.option.paths.75045718" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/SAM3X8E/}&quot;"/>
@@ -176,7 +176,7 @@
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.1561858475" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.367299064" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
- <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} ${workspace_loc:/${CoreName}/SAM4E8E/cores/arduino/syscalls.o} ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.2096521800" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
+ <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} &quot;${workspace_loc:/${CoreName}/SAM4E8E/cores/arduino/syscalls.o}&quot; ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.2096521800" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<option id="gnu.cpp.link.option.nostdlibs.111689408" name="No startup or default libs (-nostdlib)" superClass="gnu.cpp.link.option.nostdlibs" value="false" valueType="boolean"/>
<option id="gnu.cpp.link.option.paths.314107745" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/SAM4E8E/}&quot;"/>
@@ -298,7 +298,7 @@
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.1692168928" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1755453550" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
- <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} ${workspace_loc:/${CoreName}/RADDS/cores/arduino/syscalls.o} ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1176271302" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
+ <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} &quot;${workspace_loc:/${CoreName}/RADDS/cores/arduino/syscalls.o}&quot; ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1176271302" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<option id="gnu.cpp.link.option.nostdlibs.706270025" name="No startup or default libs (-nostdlib)" superClass="gnu.cpp.link.option.nostdlibs" value="false" valueType="boolean"/>
<option id="gnu.cpp.link.option.paths.1160723414" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/RADDS/}&quot;"/>
@@ -416,7 +416,7 @@
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.212863977" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.136873036" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
- <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} ${workspace_loc:/${CoreName}/SAM4E8E/cores/arduino/syscalls.o} ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.394147701" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
+ <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} &quot;${workspace_loc:/${CoreName}/SAM4E8E/cores/arduino/syscalls.o}&quot; ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.394147701" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<option id="gnu.cpp.link.option.nostdlibs.278980629" name="No startup or default libs (-nostdlib)" superClass="gnu.cpp.link.option.nostdlibs" value="false" valueType="boolean"/>
<option id="gnu.cpp.link.option.paths.1667456082" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/SAM4E8E/}&quot;"/>
diff --git a/Maths/BedLevelling-2-point.wxm b/Maths/BedLevelling-2-point.wxm
new file mode 100644
index 00000000..27876d05
--- /dev/null
+++ b/Maths/BedLevelling-2-point.wxm
@@ -0,0 +1,57 @@
+/* [wxMaxima batch file version 1] [ DO NOT EDIT BY HAND! ]*/
+/* [ Created with wxMaxima version 14.12.1 ] */
+
+/* [wxMaxima: input start ] */
+eq1:h=H+a*x+b*y;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq2:h0=H+a*x0+b*y0;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq3:h1=H+a*x1+b*y1;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq4:h2=H+a*(x0+y1-y0)+b*(y0-(x1-x0));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq5:h2=h0;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq6:subst(eq5,eq4);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq7:solve([eq2,eq3,eq6],[a,b,H]);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq8:subst(eq7,eq1);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq9:dhbydh0=factor(diff(rhs(eq8),h0));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq10:dhbydh1=factor(diff(rhs(eq8),h1));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq11:expand((x1-x0)^2+(y1-y0)^2)=d^2;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq12:subst(eq11,eq9);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq13:subst(eq11,eq10);
+/* [wxMaxima: input end ] */
+
+/* Maxima can't load/batch files which end with a comment! */
+"Created with wxMaxima"$
diff --git a/Maths/BedLevelling-3-point.wxm b/Maths/BedLevelling-3-point.wxm
new file mode 100644
index 00000000..6ec01b62
--- /dev/null
+++ b/Maths/BedLevelling-3-point.wxm
@@ -0,0 +1,57 @@
+/* [wxMaxima batch file version 1] [ DO NOT EDIT BY HAND! ]*/
+/* [ Created with wxMaxima version 14.12.1 ] */
+
+/* [wxMaxima: input start ] */
+eq1:h=H+a*x+b*y;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq2:h0=H+a*x0+b*y0;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq3:h1=H+a*x1+b*y1;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq4:h2=H+a*x2+b*y2;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq5:solve([eq2,eq3,eq4],[a,b,H]);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq6:subst(eq5,eq1);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq7:dhbydh0=factor(diff(rhs(eq6),h0));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq8:dhbydh1=factor(diff(rhs(eq6),h1));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq9:dhbydh2=factor(diff(rhs(eq6),h2));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq10:x1*y2-x0*y2-x2*y1+x0*y1+x2*y0-x1*y0=d;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq11:subst(eq10,eq7);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq12:subst(eq10,eq8);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq13:subst(eq10,eq9);
+/* [wxMaxima: input end ] */
+
+/* Maxima can't load/batch files which end with a comment! */
+"Created with wxMaxima"$
diff --git a/Maths/BedLevelling-4-point.wxm b/Maths/BedLevelling-4-point.wxm
new file mode 100644
index 00000000..2fe1f63f
--- /dev/null
+++ b/Maths/BedLevelling-4-point.wxm
@@ -0,0 +1,71 @@
+/* [wxMaxima batch file version 1] [ DO NOT EDIT BY HAND! ]*/
+/* [ Created with wxMaxima version 14.12.1 ] */
+
+/* [wxMaxima: input start ] */
+eq1:h=H+a*x+b*y+c*x*y;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq2:h0=H+a*x0+b*y0+c*x0*y0;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq3:h1=H+a*x1+b*y1+c*x1*y1;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq4:h2=H+a*x2+b*y2+c*x2*y2;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq5:h3=H+a*x3+b*y3+c*x3*y3;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq6:solve([eq2,eq3,eq4,eq5],[a,b,c,H]);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq7:subst(eq6,eq1);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq8:dhbydh0=factor(diff(rhs(eq7),h0));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq9:dhbydh1=factor(diff(rhs(eq7),h1));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq10:dhbydh2=factor(diff(rhs(eq7),h2));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq11:dhbydh3=factor(diff(rhs(eq7),h3));
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq12:(x1*x3*y2*y3-x0*x3*y2*y3-x1*x2*y2*y3+x0*x2*y2*y3-x2*x3*y1*y3+x0*
+x3*y1*y3+x1*x2*y1*y3-x0*x1*y1*y3+x2*x3*y0*y3-x1*x3*y0*y3-x0*x2*y0*y3+x0*x1*y0*y3+x2*x3*y1*y2-x1*x3*y1*y2-x0*x2*y1*y2+x0*x1*y1*y2-x2*x3*y0*y2+x0*x3*y0*y2+x1*x2*y0*y2-x0*x1*y0*
+y2+x1*x3*y0*y1-x0*x3*y0*y1-x1*x2*y0*y1+x0*x2*y0*y1)=d;
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq13:subst(eq12,eq8);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq14:subst(eq12,eq9);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq15:subst(eq12,eq10);
+/* [wxMaxima: input end ] */
+
+/* [wxMaxima: input start ] */
+eq16:subst(eq12,eq11);
+/* [wxMaxima: input end ] */
+
+/* Maxima can't load/batch files which end with a comment! */
+"Created with wxMaxima"$
diff --git a/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta9.bin b/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta9.bin
new file mode 100644
index 00000000..3d5075fe
--- /dev/null
+++ b/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.19beta9.bin
Binary files differ
diff --git a/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta9.bin b/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta9.bin
new file mode 100644
index 00000000..59851196
--- /dev/null
+++ b/Release/Duet-Ethernet/Edge/DuetEthernetFirmware-1.19beta9.bin
Binary files differ
diff --git a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta9.bin b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta9.bin
new file mode 100644
index 00000000..8f090f79
--- /dev/null
+++ b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.19beta9.bin
Binary files differ
diff --git a/Release/Duet-WiFi/Edge/DuetWiFiServer-1.19beta9.bin b/Release/Duet-WiFi/Edge/DuetWiFiServer-1.19beta9.bin
new file mode 100644
index 00000000..7fe6017b
--- /dev/null
+++ b/Release/Duet-WiFi/Edge/DuetWiFiServer-1.19beta9.bin
Binary files differ
diff --git a/Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta9.bin b/Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta9.bin
new file mode 100644
index 00000000..3c8d25fa
--- /dev/null
+++ b/Release/RADDS/Edge/RepRapFirmware-RADDS-1.19beta9.bin
Binary files differ
diff --git a/src/DuetNG/DuetWiFi/Network.cpp b/src/DuetNG/DuetWiFi/Network.cpp
index 1a8b1a89..405e3910 100644
--- a/src/DuetNG/DuetWiFi/Network.cpp
+++ b/src/DuetNG/DuetWiFi/Network.cpp
@@ -660,11 +660,20 @@ void Network::Diagnostics(MessageType mtype)
r.macAddress[0], r.macAddress[1], r.macAddress[2], r.macAddress[3], r.macAddress[4], r.macAddress[5]);
platform.MessageF(mtype, "WiFi Vcc %.2f, reset reason %s\n", (float)r.vcc/1024, TranslateEspResetReason(r.resetReason));
platform.MessageF(mtype, "WiFi flash size %u, free heap %u\n", r.flashSize, r.freeHeap);
- if (currentMode == WiFiState::connected)
+
+ if (currentMode == WiFiState::connected || currentMode == WiFiState::runningAsAccessPoint)
{
platform.MessageF(mtype, "WiFi IP address %d.%d.%d.%d\n",
r.ipAddress & 255, (r.ipAddress >> 8) & 255, (r.ipAddress >> 16) & 255, (r.ipAddress >> 24) & 255);
- platform.MessageF(mtype, "WiFi signal strength %ddb\n", r.rssi);
+ }
+
+ if (currentMode == WiFiState::connected)
+ {
+ platform.MessageF(mtype, "WiFi signal strength %ddb\n", (int)r.rssi);
+ }
+ else if (currentMode == WiFiState::runningAsAccessPoint)
+ {
+ platform.MessageF(mtype, "Connected clients %u\n", (unsigned int)r.numClients);
}
// status, ssid and hostName not displayed
diff --git a/src/DuetNG/Pins_DuetNG.h b/src/DuetNG/Pins_DuetNG.h
index e2a61e5b..c9e37a4e 100644
--- a/src/DuetNG/Pins_DuetNG.h
+++ b/src/DuetNG/Pins_DuetNG.h
@@ -111,7 +111,7 @@ const Pin Z_PROBE_PIN = 33; // AFE1_AD4/PC1 Z probe analog input
const Pin PowerMonitorVinDetectPin = 36; // AFE1_AD7/PC4 Vin monitor
const Pin PowerMonitor5vDetectPin = 29; // AFE1_AD1/PB3 5V regulator input monitor
-const float PowerFailVoltageRange = 11.0 * 3.3; // we use an 11:1 voltage divider
+const float PowerMonitorVoltageRange = 11.0 * 3.3; // We use an 11:1 voltage divider
const Pin VssaSensePin = 103;
diff --git a/src/Fan.cpp b/src/Fan.cpp
index 46cb3b9b..8063fc1c 100644
--- a/src/Fan.cpp
+++ b/src/Fan.cpp
@@ -319,4 +319,22 @@ void Fan::Disable()
pin = NoPin;
}
+#ifdef DUET_NG
+
+// Save the settings of this fan if it isn't thermostatic
+bool Fan::WriteSettings(FileStore *f, size_t fanNum) const
+{
+ if (heatersMonitored == 0)
+ {
+ char bufSpace[50];
+ StringRef buf(bufSpace, ARRAY_SIZE(bufSpace));
+ buf.printf("M106 P%u S%.2f\n", fanNum, val);
+ return f->Write(buf.Pointer());
+ }
+
+ return true;
+}
+
+#endif
+
// End
diff --git a/src/Fan.h b/src/Fan.h
index f8c94cd9..e11509c7 100644
--- a/src/Fan.h
+++ b/src/Fan.h
@@ -31,6 +31,10 @@ public:
void Check();
void Disable();
+#ifdef DUET_NG
+ bool WriteSettings(FileStore *f, size_t fanNum) const; // Save the settings of this fan if it isn't thermostatic
+#endif
+
private:
float val;
float lastVal;
diff --git a/src/GCodes/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer.cpp
index fcc18068..fe2e41cb 100644
--- a/src/GCodes/GCodeBuffer.cpp
+++ b/src/GCodes/GCodeBuffer.cpp
@@ -490,7 +490,7 @@ void GCodeBuffer::TryGetIValue(char c, int32_t& val, bool& seen)
}
// Try to get a float array exactly 'numVals' long after parameter letter 'c'.
-// If the wrong number of value is provided, generate an error message and return true.
+// If the wrong number of values is provided, generate an error message and return true.
// Else set 'seen' if we saw the letter and value, and return false.
bool GCodeBuffer::TryGetFloatArray(char c, size_t numVals, float vals[], StringRef& reply, bool& seen)
{
diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp
index abb830b7..266a413d 100644
--- a/src/GCodes/GCodes.cpp
+++ b/src/GCodes/GCodes.cpp
@@ -39,14 +39,7 @@
#endif
const char GCodes::axisLetters[MaxAxes] = AXES_('X', 'Y', 'Z', 'U', 'V', 'W', 'A', 'B', 'C');
-
-const char* const PAUSE_G = "pause.g";
-const char* const HomingFileNames[MaxAxes] = AXES_("homex.g", "homey.g", "homez.g", "homeu.g", "homev.g", "homew.g", "homea.g", "homeb.g", "homec.g");
-const char* const HOME_ALL_G = "homeall.g";
-const char* const HOME_DELTA_G = "homedelta.g";
-const char* const DefaultHeightMapFile = "heightmap.csv";
-const char* const LOAD_FILAMENT_G = "load.g";
-const char* const UNLOAD_FILAMENT_G = "unload.g";
+const char* const GCodes::HomingFileNames[MaxAxes] = AXES_("homex.g", "homey.g", "homez.g", "homeu.g", "homev.g", "homew.g", "homea.g", "homeb.g", "homec.g");
const size_t gcodeReplyLength = 2048; // long enough to pass back a reasonable number of files in response to M20
@@ -67,7 +60,9 @@ GCodes::GCodes(Platform& p) :
auxGCode = new GCodeBuffer("aux", AUX_MESSAGE, false);
daemonGCode = new GCodeBuffer("daemon", GENERIC_MESSAGE, false);
queuedGCode = new GCodeBuffer("queue", GENERIC_MESSAGE, false);
-
+#ifdef DUET_NG
+ autoPauseGCode = new GCodeBuffer("autopause", GENERIC_MESSAGE, false);
+#endif
codeQueue = new GCodeQueue();
}
@@ -132,6 +127,10 @@ void GCodes::Reset()
auxGCode->Reset();
auxGCode->SetCommsProperties(1); // by default, we require a checksum on the aux port
daemonGCode->Reset();
+ queuedGCode->Reset();
+#ifdef DUET_NG
+ autoPauseGCode->Reset();
+#endif
nextGcodeSource = 0;
@@ -152,6 +151,7 @@ void GCodes::Reset()
ClearMove();
ClearBabyStepping();
moveBuffer.xAxes = DefaultXAxisMapping;
+ moveBuffer.yAxes = DefaultYAxisMapping;
#if SUPPORT_IOBITS
moveBuffer.ioBits = 0;
#endif
@@ -176,6 +176,9 @@ void GCodes::Reset()
simulationMode = 0;
simulationTime = 0.0;
isPaused = false;
+#ifdef DUET_NG
+ isAutoPaused = resumeInfoSaved = false;
+#endif
doingToolChange = false;
doingManualBedProbe = false;
moveBuffer.filePos = noFilePosition;
@@ -348,6 +351,12 @@ void GCodes::Spin()
case GCodeState::toolChange2: // Select the new tool (even if it doesn't exist - that just deselects all tools) and run tpost
case GCodeState::m109ToolChange2: // Select the new tool (even if it doesn't exist - that just deselects all tools) and run tpost
reprap.SelectTool(newToolNumber);
+
+ // The user position reflects the position of the old tool, but on an IDEX machine the new tool is at a different place
+ // Also tool offsets may have changed, but as some axes may not have been homed we should avoid moving those axes when the next movement command is given.
+ // So adjust the current user position to reflect the actual position of the tool.
+ ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition);
+
gb.AdvanceState();
if (AllAxesAreHomed())
{
@@ -357,12 +366,6 @@ void GCodes::Spin()
DoFileMacro(gb, scratchString.Pointer(), false);
}
}
- else
- {
- // Tool offsets may have changed, but as some axes have not been homed we should avoid moving those axes when the next movement command is given.
- // So adjust the current user position to reflect the actual position of the tool.
- ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition);
- }
break;
case GCodeState::toolChangeComplete:
@@ -391,7 +394,11 @@ void GCodes::Spin()
gb.SetState(GCodeState::pausing2);
if (AllAxesAreHomed())
{
+#ifdef DUET_NG
+ DoFileMacro(gb, (isAutoPaused) ? POWER_FAIL_G : PAUSE_G, true);
+#else
DoFileMacro(gb, PAUSE_G, true);
+#endif
}
}
break;
@@ -399,6 +406,9 @@ void GCodes::Spin()
case GCodeState::pausing2:
if (LockMovementAndWaitForStandstill(gb))
{
+#ifdef DUET_NG
+ reprap.GetHeat().SuspendHeaters(false); // resume the heaters, we may have turned them off while we executed the pause macro
+#endif
reply.copy("Printing paused");
gb.SetState(GCodeState::normal);
}
@@ -549,6 +559,7 @@ void GCodes::Spin()
moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight();
moveBuffer.feedRate = platform.GetZProbeTravelSpeed();
moveBuffer.xAxes = DefaultXAxisMapping;
+ moveBuffer.yAxes = DefaultYAxisMapping;
segmentsLeft = 1;
gb.AdvanceState();
}
@@ -604,6 +615,7 @@ void GCodes::Spin()
moveBuffer.coords[Z_AXIS] = -platform.GetZProbeDiveHeight();
moveBuffer.feedRate = platform.GetCurrentZProbeParameters().probeSpeed;
moveBuffer.xAxes = DefaultXAxisMapping;
+ moveBuffer.yAxes = DefaultYAxisMapping;
segmentsLeft = 1;
gb.AdvanceState();
}
@@ -643,6 +655,7 @@ void GCodes::Spin()
moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight();
moveBuffer.feedRate = platform.GetZProbeTravelSpeed();
moveBuffer.xAxes = DefaultXAxisMapping;
+ moveBuffer.yAxes = DefaultYAxisMapping;
segmentsLeft = 1;
gb.SetState(GCodeState::gridProbing4);
}
@@ -717,6 +730,7 @@ void GCodes::Spin()
moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight();
moveBuffer.feedRate = platform.GetZProbeTravelSpeed();
moveBuffer.xAxes = DefaultXAxisMapping;
+ moveBuffer.yAxes = DefaultYAxisMapping;
segmentsLeft = 1;
gb.AdvanceState();
@@ -777,6 +791,7 @@ void GCodes::Spin()
: -1.1 * platform.AxisTotalLength(Z_AXIS); // Z axis not homed yet, so treat this as a homing move
moveBuffer.feedRate = platform.GetCurrentZProbeParameters().probeSpeed;
moveBuffer.xAxes = DefaultXAxisMapping;
+ moveBuffer.yAxes = DefaultYAxisMapping;
segmentsLeft = 1;
gb.AdvanceState();
}
@@ -883,6 +898,7 @@ void GCodes::Spin()
moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight();
moveBuffer.feedRate = platform.GetZProbeTravelSpeed();
moveBuffer.xAxes = DefaultXAxisMapping;
+ moveBuffer.yAxes = DefaultYAxisMapping;
segmentsLeft = 1;
gb.SetState(GCodeState::normal);
break;
@@ -893,7 +909,8 @@ void GCodes::Spin()
if (segmentsLeft == 0)
{
const uint32_t xAxes = reprap.GetCurrentXAxes();
- reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, xAxes);
+ const uint32_t yAxes = reprap.GetCurrentYAxes();
+ reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, xAxes, yAxes);
for (size_t i = numTotalAxes; i < DRIVES; ++i)
{
moveBuffer.coords[i] = 0.0;
@@ -907,6 +924,7 @@ void GCodes::Spin()
moveBuffer.filePos = (&gb == fileGCode) ? gb.GetFilePosition(fileInput->BytesCached()) : noFilePosition;
moveBuffer.canPauseAfter = false; // don't pause after a retraction because that could cause too much retraction
moveBuffer.xAxes = xAxes;
+ moveBuffer.yAxes = yAxes;
segmentsLeft = 1;
gb.SetState(GCodeState::normal);
}
@@ -920,7 +938,8 @@ void GCodes::Spin()
if (tool != nullptr)
{
const uint32_t xAxes = reprap.GetCurrentXAxes();
- reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, xAxes);
+ const uint32_t yAxes = reprap.GetCurrentYAxes();
+ reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, xAxes, yAxes);
for (size_t i = numTotalAxes; i < DRIVES; ++i)
{
moveBuffer.coords[i] = 0.0;
@@ -936,6 +955,7 @@ void GCodes::Spin()
moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() - fileInput->BytesCached() : noFilePosition;
moveBuffer.canPauseAfter = true;
moveBuffer.xAxes = xAxes;
+ moveBuffer.yAxes = yAxes;
segmentsLeft = 1;
}
gb.SetState(GCodeState::normal);
@@ -1100,6 +1120,9 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply)
fd.Close();
UnlockAll(gb);
reprap.GetPrintMonitor().StoppedPrint();
+#ifdef DUET_NG
+ platform.GetMassStorage()->Delete(platform.GetSysDir(), RESUME_AFTER_POWER_FAIL_G);
+#endif
if (platform.Emulating() == marlin)
{
// Pronterface expects a "Done printing" message
@@ -1172,7 +1195,7 @@ void GCodes::CheckTriggers()
else if (LockMovement(*daemonGCode)) // need to lock movement before executing the pause macro
{
triggersPending &= ~(1u << lowestTriggerPending); // clear the trigger
- DoPause(*daemonGCode);
+ DoPause(*daemonGCode, false);
}
}
else
@@ -1195,7 +1218,7 @@ void GCodes::DoEmergencyStop()
}
// Pause the print. Before calling this, check that we are doing a file print that isn't already paused and get the movement lock.
-void GCodes::DoPause(GCodeBuffer& gb)
+void GCodes::DoPause(GCodeBuffer& gb, bool isAuto)
{
if (&gb == fileGCode)
{
@@ -1287,8 +1310,180 @@ void GCodes::DoPause(GCodeBuffer& gb)
SaveFanSpeeds();
gb.SetState(GCodeState::pausing1);
isPaused = true;
+
+#ifdef DUET_NG
+ isAutoPaused = isAuto;
+ resumeInfoSaved = false;
+#endif
+}
+
+bool GCodes::IsPaused() const
+{
+ return isPaused && !IsPausing() && !IsResuming();
+}
+
+bool GCodes::IsPausing() const
+{
+ const GCodeState topState = fileGCode->OriginalMachineState().state;
+ return topState == GCodeState::pausing1 || topState == GCodeState::pausing2;
+}
+
+bool GCodes::IsResuming() const
+{
+ const GCodeState topState = fileGCode->OriginalMachineState().state;
+ return topState == GCodeState::resuming1 || topState == GCodeState::resuming2 || topState == GCodeState::resuming3;
+}
+
+bool GCodes::IsRunning() const
+{
+ return !IsPaused() && !IsPausing() && !IsResuming();
+}
+
+#ifdef DUET_NG
+
+// Try to pause the current SD card print, returning true if successful, false if needs to be called again
+bool GCodes::AutoPause()
+{
+ if (IsPausing() || IsResuming())
+ {
+ return false;
+ }
+
+ if (isPaused)
+ {
+ if (!resumeInfoSaved)
+ {
+ SaveResumeInfo();
+ }
+ }
+ else if (reprap.GetPrintMonitor().IsPrinting())
+ {
+ if (!LockMovement(*autoPauseGCode))
+ {
+ return false;
+ }
+ reprap.GetHeat().SuspendHeaters(true); // turn heaters off to conserve power for the motors to execute the pause
+ DoPause(*autoPauseGCode, true);
+ SaveResumeInfo();
+ platform.Message(GENERIC_MESSAGE, "Print auto-paused due to low voltage\n");
+ }
+ return true;
+}
+
+// Suspend the printer, only ever called after it has been auto paused
+bool GCodes::AutoShutdown()
+{
+ if (isPaused && isAutoPaused)
+ {
+ if (!LockMovementAndWaitForStandstill(*autoPauseGCode))
+ {
+ return false;
+ }
+ CancelPrint();
+ platform.Message(GENERIC_MESSAGE, "Print cancelled due to low voltage\n");
+ return true;
+ }
+ return true;
}
+// Resume printing, normally only ever called after it has been auto paused
+bool GCodes::AutoResume()
+{
+ if (isPaused && isAutoPaused)
+ {
+ autoPauseGCode->SetState(GCodeState::resuming1);
+ if (AllAxesAreHomed())
+ {
+ DoFileMacro(*autoPauseGCode, POWER_RESTORE_G, true);
+ }
+ platform.Message(GENERIC_MESSAGE, "Print auto-resumed\n");
+ }
+ return true;
+}
+
+// Permit printing to be resumed after it has been suspended
+bool GCodes::AutoResumeAfterShutdown()
+{
+ // Currently we don't do anything here
+ return true;
+}
+
+void GCodes::SaveResumeInfo()
+{
+ const char* const printingFilename = reprap.GetPrintMonitor().GetPrintingFilename();
+ if (printingFilename != nullptr)
+ {
+ FileStore * const f = platform.GetFileStore(platform.GetSysDir(), RESUME_AFTER_POWER_FAIL_G, true);
+ if (f == nullptr)
+ {
+ platform.MessageF(GENERIC_MESSAGE, "Error: failed to create file %s", RESUME_AFTER_POWER_FAIL_G);
+ }
+ else
+ {
+ char bufferSpace[200];
+ StringRef buf(bufferSpace, ARRAY_SIZE(bufferSpace));
+
+ // Write the header comment
+ buf.printf("; File \"%s\" resume print after auto-pause", printingFilename);
+ if (reprap.GetPlatform().IsDateTimeSet())
+ {
+ time_t timeNow = reprap.GetPlatform().GetDateTime();
+ const struct tm * const timeInfo = gmtime(&timeNow);
+ buf.catf(" at %04u-%02u-%02u %02u:%02u",
+ timeInfo->tm_year + 1900, timeInfo->tm_mon, timeInfo->tm_mday, timeInfo->tm_hour, timeInfo->tm_min);
+ }
+ buf.cat('\n');
+ bool ok = f->Write(buf.Pointer())
+ && reprap.GetHeat().WriteBedAndChamberTempSettings(f) // turn on bed and chamber heaters
+ && reprap.WriteToolSettings(f) // set tool temperatures, tool mix ratios etc.
+ && reprap.GetMove().WriteResumeSettings(f); // load grid, if we are using one
+ if (ok)
+ {
+ buf.printf("M98 P%s\n", RESUME_PROLOGUE_G); // call the prologue - must contain at least M116
+ ok = f->Write(buf.Pointer())
+ && platform.WriteFanSettings(f); // set the speeds of non-thermostatic fans
+ }
+ if (ok)
+ {
+ buf.printf("M106 S%.2f\n", lastDefaultFanSpeed);
+ ok = f->Write(buf.Pointer());
+ }
+ if (ok)
+ {
+ buf.printf("M116\nM290 S%.3f\nM23 %s\nM26 S%u\n", currentBabyStepZOffset, printingFilename, pauseRestorePoint.filePos);
+ ok = f->Write(buf.Pointer()); // write baby stepping offset, filename and file position
+ }
+ if (ok)
+ {
+ buf.copy("G1 F6000"); // start building command to restore head position
+ for (size_t axis = 0; axis < numVisibleAxes; ++axis)
+ {
+ buf.catf(" %c%.2f", axisLetters[axis], pauseRestorePoint.moveCoords[axis]);
+ }
+ buf.catf("\nG1 F%.1f P%u\nM24\n", pauseRestorePoint.feedRate * MinutesToSeconds, (unsigned int)pauseRestorePoint.ioBits);
+ ok = f->Write(buf.Pointer()); // restore feed rate and output bits
+ }
+ if (!f->Close())
+ {
+ ok = false;
+ }
+ if (ok)
+ {
+ platform.Message(GENERIC_MESSAGE, "Resume-after-power-fail state saved\n");
+ }
+ else
+ {
+ platform.GetMassStorage()->Delete(platform.GetSysDir(), RESUME_AFTER_POWER_FAIL_G);
+ platform.MessageF(GENERIC_MESSAGE, "Error: failed to write or close file %s\n", RESUME_AFTER_POWER_FAIL_G);
+ }
+ }
+
+ resumeInfoSaved = true; // say we saved it even if there was an error, to avoid constant retrying
+ }
+}
+
+#endif
+
void GCodes::Diagnostics(MessageType mtype)
{
platform.Message(mtype, "=== GCodes ===\n");
@@ -1328,7 +1523,7 @@ bool GCodes::LockMovementAndWaitForStandstill(const GCodeBuffer& gb)
}
// Get the current positions. These may not be the same as the ones we remembered from last time if we just did a special move.
- reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes());
+ reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes());
memcpy(moveBuffer.initialCoords, moveBuffer.coords, numVisibleAxes * sizeof(moveBuffer.initialCoords[0]));
ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition);
return true;
@@ -1457,6 +1652,7 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, StringRef& reply)
moveBuffer.moveType = 0;
doingArcMove = false;
moveBuffer.xAxes = reprap.GetCurrentXAxes();
+ moveBuffer.yAxes = reprap.GetCurrentYAxes();
moveBuffer.usePressureAdvance = false;
moveBuffer.filePos = (&gb == fileGCode) ? gb.GetFilePosition(fileInput->BytesCached()) : noFilePosition;
moveBuffer.virtualExtruderPosition = GetVirtualExtruderPosition();
@@ -1470,6 +1666,7 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, StringRef& reply)
{
moveBuffer.moveType = ival;
moveBuffer.xAxes = DefaultXAxisMapping;
+ moveBuffer.yAxes = DefaultYAxisMapping;
}
if (ival == 1 || ival == 3)
@@ -1547,7 +1744,7 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, StringRef& reply)
if (moveBuffer.moveType != 0)
{
// This is a raw motor move, so we need the current raw motor positions in moveBuffer.coords
- reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, moveBuffer.moveType, reprap.GetCurrentXAxes());
+ reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, moveBuffer.moveType, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes());
}
// Set up the initial coordinates
@@ -1674,6 +1871,11 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
memcpy(moveBuffer.initialCoords, moveBuffer.coords, numVisibleAxes * sizeof(moveBuffer.initialCoords[0]));
+ // Save the arc centre user coordinates for later
+ const float userArcCentreX = currentUserPosition[X_AXIS] + iParam;
+ const float userArcCentreY = currentUserPosition[Y_AXIS] + jParam;
+
+ // Work out the new user position
const bool axesRelative = gb.MachineState().axesRelative;
if (axesRelative)
{
@@ -1706,31 +1908,39 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // make sure the limits are reflected in the user position
}
- // Set up the arc centre coordinates and record which axes behave like an X axis.
- // The I and J parameters are always relative to present position.
- // For X we need to set up the arc centre for each axis that X is mapped to.
- // For simplicity we assume that X may be mapped to all axes except Y, so we set up the arc centre for all of those axes.
- arcAxesMoving = reprap.GetCurrentXAxes() & ~((1 << Y_AXIS) | (1 << Z_AXIS));
- for (size_t axis = 0; axis < numVisibleAxes; ++axis)
- {
- arcCentre[axis] = moveBuffer.initialCoords[axis] + (axis == Y_AXIS) ? jParam : iParam;
- }
+ // Compute the angle at which we stop
+ const float finalTheta = atan2(currentUserPosition[Y_AXIS] - userArcCentreY, currentUserPosition[X_AXIS] - userArcCentreX);
// Set up default move parameters
moveBuffer.endStopsToCheck = 0;
moveBuffer.moveType = 0;
moveBuffer.canPauseAfter = moveBuffer.canPauseBefore = true;
moveBuffer.xAxes = reprap.GetCurrentXAxes();
+ moveBuffer.yAxes = reprap.GetCurrentYAxes();
moveBuffer.virtualExtruderPosition = GetVirtualExtruderPosition();
moveBuffer.endStopsToCheck = 0;
moveBuffer.usePressureAdvance = true;
moveBuffer.filePos = (&gb == fileGCode) ? gb.GetFilePosition(fileInput->BytesCached()) : noFilePosition;
+ // Set up the arc centre coordinates and record which axes behave like an X axis.
+ // The I and J parameters are always relative to present position.
+ // For X and Y we need to set up the arc centre for each axis that X or Y is mapped to.
+ for (size_t axis = 0; axis < numVisibleAxes; ++axis)
+ {
+ if ((moveBuffer.xAxes & (1u << axis)) != 0)
+ {
+ arcCentre[axis] = moveBuffer.initialCoords[axis] + iParam;
+ }
+ else if ((moveBuffer.yAxes & (1u << axis)) != 0)
+ {
+ arcCentre[axis] = moveBuffer.initialCoords[axis] + jParam;
+ }
+ }
+
LoadExtrusionAndFeedrateFromGCode(gb, moveBuffer.moveType);
arcRadius = sqrtf(iParam * iParam + jParam * jParam);
arcCurrentAngle = atan2(-jParam, -iParam);
- const float finalTheta = atan2(moveBuffer.coords[Y_AXIS] - arcCentre[Y_AXIS], moveBuffer.coords[X_AXIS] - arcCentre[X_AXIS]);
// Calculate the total angle moved, which depends on which way round we are going
float totalArc = (clockwise) ? arcCurrentAngle - finalTheta : finalTheta - arcCurrentAngle;
@@ -1738,14 +1948,17 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise)
{
totalArc += 2 * PI;
}
- arcAngleIncrement = totalArc/segmentsLeft;
+
+ // Compute how many segments we need to move, but don't store it yet
+ const unsigned int segsLeft = max<unsigned int>((unsigned int)((arcRadius * totalArc)/arcSegmentLength + 0.8), 1u);
+ arcAngleIncrement = totalArc/segsLeft;
if (clockwise)
{
arcAngleIncrement = -arcAngleIncrement;
}
doingArcMove = true;
- segmentsLeft = max<unsigned int>((unsigned int)((arcRadius * totalArc)/arcSegmentLength + 0.8), 1); // must do this last for RTOS
+ segmentsLeft = segsLeft; // must do this last for RTOS
// debugPrintf("Radius %.2f, initial angle %.1f, increment %.1f, segments %u\n",
// arcRadius, arcCurrentAngle * RadiansToDegrees, arcAngleIncrement * RadiansToDegrees, segmentsLeft);
return false;
@@ -1782,11 +1995,12 @@ bool GCodes::ReadMove(RawMove& m)
{
if (doingArcMove && drive != Z_AXIS)
{
- if (drive == Y_AXIS)
+ if ((moveBuffer.yAxes & (1u << drive)) != 0)
{
+ // Y axis or a substitute Y axis
moveBuffer.initialCoords[drive] = arcCentre[drive] + arcRadius * sinf(arcCurrentAngle);
}
- else if ((arcAxesMoving & (1 << drive)) != 0)
+ else if ((moveBuffer.xAxes & (1u << drive)) != 0)
{
// X axis or a substitute X axis
moveBuffer.initialCoords[drive] = arcCentre[drive] + arcRadius * cosf(arcCurrentAngle);
@@ -1866,22 +2080,21 @@ bool GCodes::SetPositions(GCodeBuffer& gb)
{
// Don't wait for the machine to stop if only extruder drives are being reset.
// This avoids blobs and seams when the gcode uses absolute E coordinates and periodically includes G92 E0.
- bool includingAxes = false;
+ uint32_t axesIncluded = 0;
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
if (gb.Seen(axisLetters[axis]))
{
const float axisValue = gb.GetFValue();
- if (!includingAxes)
+ if (axesIncluded == 0)
{
if (!LockMovementAndWaitForStandstill(gb)) // lock movement and get current coordinates
{
return false;
}
- includingAxes = true;
}
+ axesIncluded |= (1u << axis);
currentUserPosition[axis] = axisValue;
- SetAxisIsHomed(axis);
}
}
@@ -1912,14 +2125,15 @@ bool GCodes::SetPositions(GCodeBuffer& gb)
}
}
- if (includingAxes)
+ if (axesIncluded != 0)
{
ToolOffsetTransform(currentUserPosition, moveBuffer.coords);
- if (reprap.GetMove().GetKinematics().LimitPosition(moveBuffer.coords, numVisibleAxes, axesHomed))
+ if (reprap.GetMove().GetKinematics().LimitPosition(moveBuffer.coords, numVisibleAxes, (1u << numVisibleAxes) - 1)) // pretend that all axes are homed
{
ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // make sure the limits are reflected in the user position
}
reprap.GetMove().SetNewPosition(moveBuffer.coords, true);
+ axesHomed |= reprap.GetMove().GetKinematics().AxesAssumedHomed(axesIncluded);
#if SUPPORT_ROLAND
if (reprap.GetRoland()->Active())
@@ -1985,7 +2199,6 @@ bool GCodes::DoHome(GCodeBuffer& gb, StringRef& reply, bool& error)
{
// Homing on a delta printer uses homedelta.g instead of homeall.g and we can only home all towers at once
SetAllAxesNotHomed();
- ClearBabyStepping();
DoFileMacro(gb, HOME_DELTA_G, true);
}
else
@@ -2000,10 +2213,6 @@ bool GCodes::DoHome(GCodeBuffer& gb, StringRef& reply, bool& error)
}
}
- if (toBeHomed == 0 || (toBeHomed & (1u << Z_AXIS)) != 0)
- {
- ClearBabyStepping();
- }
if (toBeHomed == 0 || toBeHomed == ((1u << numTotalAxes) - 1))
{
// Homing everything
@@ -2073,6 +2282,7 @@ bool GCodes::ExecuteG30(GCodeBuffer& gb, StringRef& reply)
moveBuffer.coords[Z_AXIS] = platform.GetZProbeStartingHeight();
moveBuffer.feedRate = platform.GetZProbeTravelSpeed();
moveBuffer.xAxes = DefaultXAxisMapping;
+ moveBuffer.yAxes = DefaultYAxisMapping;
segmentsLeft = 1;
gb.SetState(GCodeState::probingAtPoint1);
}
@@ -2361,7 +2571,7 @@ bool GCodes::SaveHeightMap(GCodeBuffer& gb, StringRef& reply) const
void GCodes::GetCurrentCoordinates(StringRef& s) const
{
float liveCoordinates[DRIVES];
- reprap.GetMove().LiveCoordinates(liveCoordinates, reprap.GetCurrentXAxes());
+ reprap.GetMove().LiveCoordinates(liveCoordinates, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes());
const Tool * const currentTool = reprap.GetCurrentTool();
if (currentTool != nullptr)
{
@@ -2507,6 +2717,7 @@ void GCodes::QueueFileToPrint(const char* fileName)
reprap.GetMove().ResetExtruderPositions();
fileToPrint.Set(f);
+ fileOffsetToPrint = 0;
}
else
{
@@ -2591,7 +2802,7 @@ bool GCodes::DoDwellTime(GCodeBuffer& gb, uint32_t dwellMillis)
}
// Set offset, working and standby temperatures for a tool. I.e. handle a G10.
-bool GCodes::SetOrReportOffsets(GCodeBuffer &gb, StringRef& reply)
+bool GCodes::SetOrReportOffsets(GCodeBuffer &gb, StringRef& reply, bool& error)
{
Tool *tool;
if (gb.Seen('P'))
@@ -2603,16 +2814,17 @@ bool GCodes::SetOrReportOffsets(GCodeBuffer &gb, StringRef& reply)
if (tool == nullptr)
{
reply.printf("Attempt to set/report offsets and temperatures for non-existent tool: %d", toolNumber);
+ error = true;
return true;
}
}
else
{
tool = reprap.GetCurrentTool();
-
if (tool == nullptr)
{
reply.printf("Attempt to set/report offsets and temperatures for no selected tool");
+ error = true;
return true;
}
}
@@ -2683,7 +2895,8 @@ bool GCodes::SetOrReportOffsets(GCodeBuffer &gb, StringRef& reply)
return true;
}
-void GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply)
+// Create a new tool definition, returning true if an error was reported
+bool GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply)
{
if (!gb.Seen('P'))
{
@@ -2693,7 +2906,7 @@ void GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply)
int adjust = gb.GetIValue();
gb.SetToolNumberAdjust(adjust);
}
- return;
+ return false;
}
// Check tool number
@@ -2701,8 +2914,8 @@ void GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply)
const int toolNumber = gb.GetIValue();
if (toolNumber < 0)
{
- platform.Message(GENERIC_MESSAGE, "Error: Tool number must be positive!\n");
- return;
+ reply.copy("Tool number must be positive");
+ return true;
}
// Check tool name
@@ -2711,8 +2924,8 @@ void GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply)
{
if (!gb.GetQuotedString(name, ARRAY_SIZE(name)))
{
- platform.Message(GENERIC_MESSAGE, "Error: Invalid tool name!\n");
- return;
+ reply.copy("Invalid tool name");
+ return true;
}
seen = true;
}
@@ -2759,7 +2972,28 @@ void GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply)
}
else
{
- xMap = 1; // by default map X axis straight through
+ xMap = DefaultXAxisMapping; // by default map X axis straight through
+ }
+
+ // Check Y axis mapping
+ uint32_t yMap;
+ if (gb.Seen('Y'))
+ {
+ long yMapping[MaxAxes];
+ size_t yCount = numVisibleAxes;
+ gb.GetLongArray(yMapping, yCount);
+ xMap = LongArrayToBitMap(yMapping, yCount) & ((1u << numVisibleAxes) - 1);
+ seen = true;
+ }
+ else
+ {
+ yMap = DefaultYAxisMapping; // by default map X axis straight through
+ }
+
+ if ((xMap & yMap) != 0)
+ {
+ reply.copy("Cannot map bith X and Y to the aame axis");
+ return true;
}
// Check for fan mapping
@@ -2789,7 +3023,7 @@ void GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply)
}
else
{
- Tool* tool = Tool::Create(toolNumber, name, drives, dCount, heaters, hCount, xMap, fanMap);
+ Tool* const tool = Tool::Create(toolNumber, name, drives, dCount, heaters, hCount, xMap, yMap, fanMap);
if (tool != nullptr)
{
reprap.AddTool(tool);
@@ -2800,6 +3034,7 @@ void GCodes::ManageTool(GCodeBuffer& gb, StringRef& reply)
{
reprap.PrintTool(toolNumber, reply);
}
+ return false;
}
// Does what it says.
@@ -2823,12 +3058,11 @@ void GCodes::SetMACAddress(GCodeBuffer& gb)
{
if (ipString[sp] == ':')
{
- mac[ipp] = strtoul(&ipString[spp], NULL, 16);
+ mac[ipp] = strtoul(&ipString[spp], nullptr, 16);
ipp++;
if (ipp > 5)
{
- platform.MessageF(GENERIC_MESSAGE, "Dud MAC address: %s\n", gb.Buffer());
- return;
+ break;
}
sp++;
spp = sp;
@@ -2838,9 +3072,9 @@ void GCodes::SetMACAddress(GCodeBuffer& gb)
sp++;
}
}
- mac[ipp] = strtoul(&ipString[spp], NULL, 16);
if (ipp == 5)
{
+ mac[ipp] = strtoul(&ipString[spp], nullptr, 16);
platform.SetMACAddress(mac);
}
else
@@ -2922,66 +3156,66 @@ void GCodes::HandleReply(GCodeBuffer& gb, bool error, const char* reply)
switch (c)
{
- case me:
- case reprapFirmware:
- if (error)
- {
- platform.Message(type, "Error: ");
- }
+ case me:
+ case reprapFirmware:
+ if (error)
+ {
+ platform.Message(type, "Error: ");
+ }
+ platform.Message(type, reply);
+ platform.Message(type, "\n");
+ return;
+
+ case marlin:
+ // We don't need to handle M20 here because we always allocate an output buffer for that one
+ if (gb.Seen('M') && gb.GetIValue() == 28)
+ {
+ platform.Message(type, response);
+ platform.Message(type, "\n");
platform.Message(type, reply);
platform.Message(type, "\n");
return;
+ }
- case marlin:
- // We don't need to handle M20 here because we always allocate an output buffer for that one
- if (gb.Seen('M') && gb.GetIValue() == 28)
- {
- platform.Message(type, response);
- platform.Message(type, "\n");
- platform.Message(type, reply);
- platform.Message(type, "\n");
- return;
- }
-
- if ((gb.Seen('M') && gb.GetIValue() == 105) || (gb.Seen('M') && gb.GetIValue() == 998))
- {
- platform.Message(type, response);
- platform.Message(type, " ");
- platform.Message(type, reply);
- platform.Message(type, "\n");
- return;
- }
-
- if (reply[0] != 0 && !gb.IsDoingFileMacro())
- {
- platform.Message(type, reply);
- platform.Message(type, "\n");
- platform.Message(type, response);
- platform.Message(type, "\n");
- }
- else if (reply[0] != 0)
- {
- platform.Message(type, reply);
- platform.Message(type, "\n");
- }
- else
- {
- platform.Message(type, response);
- platform.Message(type, "\n");
- }
+ if ((gb.Seen('M') && gb.GetIValue() == 105) || (gb.Seen('M') && gb.GetIValue() == 998))
+ {
+ platform.Message(type, response);
+ platform.Message(type, " ");
+ platform.Message(type, reply);
+ platform.Message(type, "\n");
return;
+ }
- case teacup:
- emulationType = "teacup";
- break;
- case sprinter:
- emulationType = "sprinter";
- break;
- case repetier:
- emulationType = "repetier";
- break;
- default:
- emulationType = "unknown";
+ if (reply[0] != 0 && !gb.IsDoingFileMacro())
+ {
+ platform.Message(type, reply);
+ platform.Message(type, "\n");
+ platform.Message(type, response);
+ platform.Message(type, "\n");
+ }
+ else if (reply[0] != 0)
+ {
+ platform.Message(type, reply);
+ platform.Message(type, "\n");
+ }
+ else
+ {
+ platform.Message(type, response);
+ platform.Message(type, "\n");
+ }
+ return;
+
+ case teacup:
+ emulationType = "teacup";
+ break;
+ case sprinter:
+ emulationType = "sprinter";
+ break;
+ case repetier:
+ emulationType = "repetier";
+ break;
+ default:
+ emulationType = "unknown";
}
if (emulationType != 0)
@@ -3012,72 +3246,72 @@ void GCodes::HandleReply(GCodeBuffer& gb, bool error, OutputBuffer *reply)
switch (c)
{
- case me:
- case reprapFirmware:
- if (error)
- {
- platform.Message(type, "Error: ");
- }
+ case me:
+ case reprapFirmware:
+ if (error)
+ {
+ platform.Message(type, "Error: ");
+ }
+ platform.Message(type, reply);
+ return;
+
+ case marlin:
+ if (gb.Seen('M') && gb.GetIValue() == 20)
+ {
+ platform.Message(type, "Begin file list\n");
platform.Message(type, reply);
+ platform.Message(type, "End file list\n");
+ platform.Message(type, response);
+ platform.Message(type, "\n");
return;
+ }
- case marlin:
- if (gb.Seen('M') && gb.GetIValue() == 20)
- {
- platform.Message(type, "Begin file list\n");
- platform.Message(type, reply);
- platform.Message(type, "End file list\n");
- platform.Message(type, response);
- platform.Message(type, "\n");
- return;
- }
-
- if (gb.Seen('M') && gb.GetIValue() == 28)
- {
- platform.Message(type, response);
- platform.Message(type, "\n");
- platform.Message(type, reply);
- return;
- }
-
- if ((gb.Seen('M') && gb.GetIValue() == 105) || (gb.Seen('M') && gb.GetIValue() == 998))
- {
- platform.Message(type, response);
- platform.Message(type, " ");
- platform.Message(type, reply);
- return;
- }
+ if (gb.Seen('M') && gb.GetIValue() == 28)
+ {
+ platform.Message(type, response);
+ platform.Message(type, "\n");
+ platform.Message(type, reply);
+ return;
+ }
- if (reply->Length() != 0 && !gb.IsDoingFileMacro())
- {
- platform.Message(type, reply);
- platform.Message(type, "\n");
- platform.Message(type, response);
- platform.Message(type, "\n");
- }
- else if (reply->Length() != 0)
- {
- platform.Message(type, reply);
- }
- else
- {
- OutputBuffer::ReleaseAll(reply);
- platform.Message(type, response);
- platform.Message(type, "\n");
- }
+ if ((gb.Seen('M') && gb.GetIValue() == 105) || (gb.Seen('M') && gb.GetIValue() == 998))
+ {
+ platform.Message(type, response);
+ platform.Message(type, " ");
+ platform.Message(type, reply);
return;
+ }
- case teacup:
- emulationType = "teacup";
- break;
- case sprinter:
- emulationType = "sprinter";
- break;
- case repetier:
- emulationType = "repetier";
- break;
- default:
- emulationType = "unknown";
+ if (reply->Length() != 0 && !gb.IsDoingFileMacro())
+ {
+ platform.Message(type, reply);
+ platform.Message(type, "\n");
+ platform.Message(type, response);
+ platform.Message(type, "\n");
+ }
+ else if (reply->Length() != 0)
+ {
+ platform.Message(type, reply);
+ }
+ else
+ {
+ OutputBuffer::ReleaseAll(reply);
+ platform.Message(type, response);
+ platform.Message(type, "\n");
+ }
+ return;
+
+ case teacup:
+ emulationType = "teacup";
+ break;
+ case sprinter:
+ emulationType = "sprinter";
+ break;
+ case repetier:
+ emulationType = "repetier";
+ break;
+ default:
+ emulationType = "unknown";
}
// If we get here then we didn't handle the message, so release the buffer(s)
@@ -3129,7 +3363,7 @@ bool GCodes::SetHeaterParameters(GCodeBuffer& gb, StringRef& reply)
{
if (gb.Seen('P'))
{
- int heater = gb.GetIValue();
+ const int heater = gb.GetIValue();
if ((heater >= 0 && heater < (int)Heaters) || (heater >= (int)FirstVirtualHeater && heater < (int)(FirstVirtualHeater + MaxVirtualHeaters)))
{
Heat& heat = reprap.GetHeat();
@@ -3175,7 +3409,7 @@ bool GCodes::SetHeaterParameters(GCodeBuffer& gb, StringRef& reply)
void GCodes::SetToolHeaters(Tool *tool, float temperature)
{
- if (tool == NULL)
+ if (tool == nullptr)
{
platform.Message(GENERIC_MESSAGE, "Setting temperature: no tool selected.\n");
return;
@@ -3196,6 +3430,11 @@ bool GCodes::RetractFilament(GCodeBuffer& gb, bool retract)
{
if (retract != isRetracted && (retractLength != 0.0 || retractHop != 0.0 || (!retract && retractExtra != 0.0)))
{
+ if (!LockMovement(gb))
+ {
+ return false;
+ }
+
if (segmentsLeft != 0)
{
return false;
@@ -3204,7 +3443,8 @@ bool GCodes::RetractFilament(GCodeBuffer& gb, bool retract)
// New code does the retraction and the Z hop as separate moves
// Get ready to generate a move
const uint32_t xAxes = reprap.GetCurrentXAxes();
- reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, xAxes);
+ const uint32_t yAxes = reprap.GetCurrentYAxes();
+ reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, xAxes, yAxes);
for (size_t i = numTotalAxes; i < DRIVES; ++i)
{
moveBuffer.coords[i] = 0.0;
@@ -3214,6 +3454,7 @@ bool GCodes::RetractFilament(GCodeBuffer& gb, bool retract)
moveBuffer.usePressureAdvance = false;
moveBuffer.filePos = (&gb == fileGCode) ? gb.GetFilePosition(fileInput->BytesCached()) : noFilePosition;
moveBuffer.xAxes = xAxes;
+ moveBuffer.yAxes = yAxes;
if (retract)
{
@@ -3434,7 +3675,7 @@ void GCodes::SetMachinePosition(const float positionNow[DRIVES], bool doBedCompe
// Get the current position from the Move class
void GCodes::GetCurrentUserPosition()
{
- reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes());
+ reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes());
ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition);
}
@@ -3487,12 +3728,17 @@ void GCodes::ToolOffsetTransform(const float coordsIn[MaxAxes], float coordsOut[
else
{
const uint32_t xAxes = currentTool->GetXAxisMap();
+ const uint32_t yAxes = currentTool->GetYAxisMap();
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
- if (axis != X_AXIS || (xAxes & (1 << X_AXIS)) != 0)
+ if ( (axis != X_AXIS || (xAxes & (1 << X_AXIS)) != 0)
+ && (axis != Y_AXIS || (yAxes & (1 << Y_AXIS)) != 0)
+ )
{
const float totalOffset = currentTool->GetOffset()[axis] + axisOffsets[axis];
- const size_t inputAxis = ((xAxes & (1u << axis)) != 0) ? X_AXIS : axis;
+ const size_t inputAxis = ((xAxes & (1u << axis)) != 0) ? X_AXIS
+ : ((yAxes & (1u << axis)) != 0) ? Y_AXIS
+ : axis;
coordsOut[axis] = (coordsIn[inputAxis] * axisScaleFactors[axis]) - totalOffset;
}
}
@@ -3500,7 +3746,7 @@ void GCodes::ToolOffsetTransform(const float coordsIn[MaxAxes], float coordsOut[
coordsOut[Z_AXIS] += (currentZHop + currentBabyStepZOffset);
}
-// Convert head reference point coordinates to user coordinates, allowing for X axis mapping
+// Convert head reference point coordinates to user coordinates, allowing for XY axis mapping
// Caution: coordsIn and coordsOut may address the same array!
void GCodes::ToolOffsetInverseTransform(const float coordsIn[MaxAxes], float coordsOut[MaxAxes])
{
@@ -3515,8 +3761,9 @@ void GCodes::ToolOffsetInverseTransform(const float coordsIn[MaxAxes], float coo
else
{
const uint32_t xAxes = reprap.GetCurrentXAxes();
- float xCoord = 0.0;
- size_t numXAxes = 0;
+ const uint32_t yAxes = reprap.GetCurrentYAxes();
+ float xCoord = 0.0, yCoord = 0.0;
+ size_t numXAxes = 0, numYAxes = 0;
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
coordsOut[axis] = coordsIn[axis] + currentTool->GetOffset()[axis];
@@ -3524,37 +3771,23 @@ void GCodes::ToolOffsetInverseTransform(const float coordsIn[MaxAxes], float coo
{
xCoord += coordsIn[axis]/axisScaleFactors[axis] + currentTool->GetOffset()[axis];
}
+ if ((yAxes & (1u << axis)) != 0)
+ {
+ yCoord += coordsIn[axis]/axisScaleFactors[axis] + currentTool->GetOffset()[axis];
+ }
}
if (numXAxes != 0)
{
coordsOut[X_AXIS] = xCoord/numXAxes;
}
+ if (numYAxes != 0)
+ {
+ coordsOut[Y_AXIS] = yCoord/numYAxes;
+ }
}
coordsOut[Z_AXIS] -= (currentZHop + currentBabyStepZOffset)/axisScaleFactors[Z_AXIS];
}
-bool GCodes::IsPaused() const
-{
- return isPaused && !IsPausing() && !IsResuming();
-}
-
-bool GCodes::IsPausing() const
-{
- const GCodeState topState = fileGCode->OriginalMachineState().state;
- return topState == GCodeState::pausing1 || topState == GCodeState::pausing2;
-}
-
-bool GCodes::IsResuming() const
-{
- const GCodeState topState = fileGCode->OriginalMachineState().state;
- return topState == GCodeState::resuming1 || topState == GCodeState::resuming2 || topState == GCodeState::resuming3;
-}
-
-bool GCodes::IsRunning() const
-{
- return !IsPaused() && !IsPausing() && !IsResuming();
-}
-
const char *GCodes::TranslateEndStopResult(EndStopHit es)
{
switch (es)
diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h
index a722aa15..0b46c304 100644
--- a/src/GCodes/GCodes.h
+++ b/src/GCodes/GCodes.h
@@ -63,7 +63,7 @@ struct Trigger
}
};
-// Bits for T-code P-parameter to specify which macros are supposted to be run
+// Bits for T-code P-parameter to specify which macros are supposed to be run
const int TFreeBit = 1 << 0;
const int TPreBit = 1 << 1;
const int TPostBit = 1 << 2;
@@ -84,6 +84,7 @@ public:
float virtualExtruderPosition; // the virtual extruder position of the current tool at the start of this move
FilePosition filePos; // offset in the file being printed at the start of reading this move
uint32_t xAxes; // axes that X is mapped to
+ uint32_t yAxes; // axes that Y is mapped to
EndstopChecks endStopsToCheck; // endstops to check
#if SUPPORT_IOBITS
IoBits_t ioBits; // I/O bits to set/clear at the start of this move
@@ -151,7 +152,14 @@ public:
size_t GetVisibleAxes() const { return numVisibleAxes; }
size_t GetNumExtruders() const { return numExtruders; }
- static const char axisLetters[MaxAxes]; // 'X', 'Y', 'Z'
+#ifdef DUET_NG
+ bool AutoPause();
+ bool AutoShutdown();
+ bool AutoResume();
+ bool AutoResumeAfterShutdown();
+#endif
+
+ static const char axisLetters[MaxAxes];
private:
GCodes(const GCodes&); // private copy constructor to prevent copying
@@ -198,7 +206,7 @@ private:
bool ExecuteG30(GCodeBuffer& gb, StringRef& reply); // Probes at a given position - see the comment at the head of the function itself
void SetBedEquationWithProbe(int sParam, StringRef& reply); // Probes a series of points and sets the bed equation
bool SetPrintZProbe(GCodeBuffer& gb, StringRef& reply); // Either return the probe value, or set its threshold
- bool SetOrReportOffsets(GCodeBuffer& gb, StringRef& reply); // Deal with a G10
+ bool SetOrReportOffsets(GCodeBuffer& gb, StringRef& reply, bool& error); // Deal with a G10
bool SetPositions(GCodeBuffer& gb); // Deal with a G92
bool LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType); // Set up the extrusion and feed rate of a move for the Move class
@@ -215,7 +223,7 @@ private:
bool OffsetAxes(GCodeBuffer& gb); // Set offsets - deprecated, use G10
void SetPidParameters(GCodeBuffer& gb, int heater, StringRef& reply); // Set the P/I/D parameters for a heater
bool SetHeaterParameters(GCodeBuffer& gb, StringRef& reply); // Set the thermistor and ADC parameters for a heater, returning true if an error occurs
- void ManageTool(GCodeBuffer& gb, StringRef& reply); // Create a new tool definition
+ bool ManageTool(GCodeBuffer& gb, StringRef& reply); // Create a new tool definition, returning true if an error was reported
void SetToolHeaters(Tool *tool, float temperature); // Set all a tool's heaters to the temperature. For M104...
bool ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling) const; // Wait for the heaters associated with the specified tool to reach their set temperatures
void GenerateTemperatureReport(StringRef& reply) const; // Store a standard-format temperature report in reply
@@ -239,7 +247,7 @@ private:
void CheckTriggers(); // Check for and execute triggers
void DoEmergencyStop(); // Execute an emergency stop
- void DoPause(GCodeBuffer& gb) // Pause the print
+ void DoPause(GCodeBuffer& gb, bool isAuto) // Pause the print
pre(resourceOwners[movementResource] = &gb);
void SetMappedFanSpeed(); // Set the speeds of fans mapped for the current tool
@@ -258,6 +266,10 @@ private:
MessageType GetMessageBoxDevice(GCodeBuffer& gb) const; // Decide which device to display a message box on
void DoManualProbe(GCodeBuffer& gb); // Do a manual bed probe
+#ifdef DUET_NG
+ void SaveResumeInfo();
+#endif
+
static uint32_t LongArrayToBitMap(const long *arr, size_t numEntries); // Convert an array of longs to a bit map
Platform& platform; // The RepRap machine
@@ -268,7 +280,12 @@ private:
StreamGCodeInput* serialInput; // ...
StreamGCodeInput* auxInput; // ...for the GCodeBuffers below
+#ifdef DUET_NG
+ GCodeBuffer* gcodeSources[8]; // The various sources of gcodes
+ GCodeBuffer*& autoPauseGCode = gcodeSources[7]; // GCode state machine used to run pause.g and resume.g
+#else
GCodeBuffer* gcodeSources[7]; // The various sources of gcodes
+#endif
GCodeBuffer*& httpGCode = gcodeSources[0];
GCodeBuffer*& telnetGCode = gcodeSources[1];
@@ -282,7 +299,11 @@ private:
const GCodeBuffer* resourceOwners[NumResources]; // Which gcode buffer owns each resource
bool active; // Live and running?
- bool isPaused; // true if the print has been paused
+ bool isPaused; // true if the print has been paused manually or automatically
+#ifdef DUET_NG
+ bool isAutoPaused; // true if the print was paused automatically
+ bool resumeInfoSaved; // true if we have saved resume info at this pause point
+#endif
bool runningConfigFile; // We are running config.g during the startup process
bool doingToolChange; // We are running tool change macros
@@ -296,7 +317,6 @@ private:
float arcRadius;
float arcCurrentAngle;
float arcAngleIncrement;
- uint32_t arcAxesMoving;
bool doingArcMove;
RestorePoint simulationRestorePoint; // The position and feed rate when we started a simulation
@@ -313,7 +333,8 @@ private:
float record[DRIVES]; // Temporary store for move positions
float distanceScale; // MM or inches
float arcSegmentLength; // Length of segments that we split arc moves into
- FileData fileToPrint;
+ FileData fileToPrint; // The next file to print
+ FilePosition fileOffsetToPrint; // The offset to print from
FileStore* fileBeingWritten; // A file to write G Codes (or sometimes HTML) to
uint16_t toBeHomed; // Bitmap of axes still to be homed
int oldToolNumber, newToolNumber; // Tools being changed
@@ -378,6 +399,33 @@ private:
bool displayNoToolWarning; // True if we need to display a 'no tool selected' warning
bool displayDeltaNotHomedWarning; // True if we need to display a 'attempt to move before homing on a delta printer' message
char filamentToLoad[FilamentNameLength]; // Name of the filament being loaded
+
+ static const char* const HomingFileNames[MaxAxes];
+
+ static constexpr const char* BED_EQUATION_G = "bed.g";
+ static constexpr const char* RESUME_G = "resume.g";
+ static constexpr const char* CANCEL_G = "cancel.g";
+ static constexpr const char* STOP_G = "stop.g";
+ static constexpr const char* SLEEP_G = "sleep.g";
+ static constexpr const char* CONFIG_OVERRIDE_G = "config-override.g";
+ static constexpr const char* DEPLOYPROBE_G = "deployprobe.g";
+ static constexpr const char* RETRACTPROBE_G = "retractprobe.g";
+ static constexpr const char* RESUME_PROLOGUE_G = "resurrect-prologue.g";
+ static constexpr const char* PAUSE_G = "pause.g";
+ static constexpr const char* HOME_ALL_G = "homeall.g";
+ static constexpr const char* HOME_DELTA_G = "homedelta.g";
+ static constexpr const char* DefaultHeightMapFile = "heightmap.csv";
+ static constexpr const char* LOAD_FILAMENT_G = "load.g";
+ static constexpr const char* UNLOAD_FILAMENT_G = "unload.g";
+
+#ifdef DUET_NG
+ static constexpr const char* POWER_FAIL_G = "powerfail.g";
+ static constexpr const char* POWER_RESTORE_G = "powerrestore.g";
+ static constexpr const char* RESUME_AFTER_POWER_FAIL_G = "resurrect.g";
+#endif
+
+ static constexpr const float MinServoPulseWidth = 544.0, MaxServoPulseWidth = 2400.0;
+ static const uint16_t ServoRefreshFrequency = 50;
};
//*****************************************************************************************************
diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp
index e1e24934..ae7f8b69 100644
--- a/src/GCodes/GCodes2.cpp
+++ b/src/GCodes/GCodes2.cpp
@@ -28,18 +28,6 @@
# include "FirmwareUpdater.h"
#endif
-const char* const BED_EQUATION_G = "bed.g";
-const char* const RESUME_G = "resume.g";
-const char* const CANCEL_G = "cancel.g";
-const char* const STOP_G = "stop.g";
-const char* const SLEEP_G = "sleep.g";
-const char* const CONFIG_OVERRIDE_G = "config-override.g";
-const char* const DEPLOYPROBE_G = "deployprobe.g";
-const char* const RETRACTPROBE_G = "retractprobe.g";
-
-const float MinServoPulseWidth = 544.0, MaxServoPulseWidth = 2400.0;
-const uint16_t ServoRefreshFrequency = 50;
-
// If the code to act on is completed, this returns true,
// otherwise false. It is called repeatedly for a given
// code until it returns true for that code.
@@ -132,10 +120,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
case 10: // Set/report offsets and temperatures, or retract
{
- bool modifyingTool = false;
- modifyingTool |= gb.Seen('P');
- modifyingTool |= gb.Seen('R');
- modifyingTool |= gb.Seen('S');
+ bool modifyingTool = gb.Seen('P') || gb.Seen('R') || gb.Seen('S');
for (size_t axis = 0; axis < numVisibleAxes; ++axis)
{
modifyingTool |= gb.Seen(axisLetters[axis]);
@@ -143,27 +128,19 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
if (modifyingTool)
{
- if (!SetOrReportOffsets(gb, reply))
+ if (!SetOrReportOffsets(gb, reply, error))
{
return false;
}
}
else
{
- if (!LockMovement(gb))
- {
- return false;
- }
result = RetractFilament(gb, true);
}
}
break;
case 11: // Un-retract
- if (!LockMovement(gb))
- {
- return false;
- }
result = RetractFilament(gb, false);
break;
@@ -215,7 +192,6 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
}
else
{
- ClearBabyStepping();
error = ExecuteG30(gb, reply);
}
break;
@@ -230,8 +206,6 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
return false;
}
- ClearBabyStepping();
-
// We need to unlock the movement system here in case there is no Z probe and we are doing manual probing.
// Otherwise, even though the bed probing code calls UnlockAll when doing a manual bed probe, the movement system
// remains locked because the current MachineState object already held the lock when the macro file was started,
@@ -467,7 +441,8 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
case 23: // Set file to print
case 32: // Select file and start SD print
- if (fileGCode->OriginalMachineState().fileState.IsLive())
+ // We now allow a file that is being printed to chain to another file. This is required for the resume-after-power-fail functoinality.
+ if (fileGCode->OriginalMachineState().fileState.IsLive() && (&gb) != fileGCode)
{
reply.copy("Cannot set file to print, because a file is already being printed");
error = true;
@@ -498,7 +473,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
if (code == 32)
{
+ fileToPrint.Seek(fileOffsetToPrint);
fileGCode->OriginalMachineState().fileState.MoveFrom(fileToPrint);
+ fileInput->Reset();
reprap.GetPrintMonitor().StartedPrint();
}
}
@@ -531,7 +508,9 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
else
{
+ fileToPrint.Seek(fileOffsetToPrint);
fileGCode->OriginalMachineState().fileState.MoveFrom(fileToPrint);
+ fileInput->Reset();
reprap.GetPrintMonitor().StartedPrint();
}
break;
@@ -543,7 +522,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
{
return false;
}
- DoPause(gb);
+ DoPause(gb, false);
}
break;
@@ -564,45 +543,16 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
{
return false;
}
- DoPause(gb);
+ DoPause(gb, false);
}
break;
case 26: // Set SD position
+ // This is used between executing M23 to set up the file to print, and M25 to print it
if (gb.Seen('S'))
{
- const FilePosition value = gb.GetIValue();
- if (value < 0)
- {
- reply.copy("SD positions can't be negative!");
- error = true;
- }
- else if (fileGCode->OriginalMachineState().fileState.IsLive())
- {
- if (!fileGCode->OriginalMachineState().fileState.Seek(value))
- {
- reply.copy("The specified SD position is invalid!");
- error = true;
- }
- }
- else if (fileToPrint.IsLive())
- {
- if (!fileToPrint.Seek(value))
- {
- reply.copy("The specified SD position is invalid!");
- error = true;
- }
- }
- else
- {
- reply.copy("Cannot set SD file position, because no print is in progress!");
- error = true;
- }
- }
- else
- {
- reply.copy("You must specify the SD position in bytes using the S parameter.");
- error = true;
+ // Ideally we would get an unsigned value here in case of the file offset being >2Gb
+ fileOffsetToPrint = (FilePosition)gb.GetIValue();
}
break;
@@ -611,7 +561,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
{
// Pronterface keeps sending M27 commands if "Monitor status" is checked, and it specifically expects the following response syntax
FileData& fileBeingPrinted = fileGCode->OriginalMachineState().fileState;
- reply.printf("SD printing byte %lu/%lu", fileBeingPrinted.GetPosition(), fileBeingPrinted.Length());
+ reply.printf("SD printing byte %lu/%lu", fileBeingPrinted.GetPosition() - fileInput->BytesCached(), fileBeingPrinted.Length());
}
else
{
@@ -691,7 +641,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
if (!wasSimulating)
{
// Starting a new simulation, so save the current position
- reprap.GetMove().GetCurrentUserPosition(simulationRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes());
+ reprap.GetMove().GetCurrentUserPosition(simulationRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes());
simulationRestorePoint.feedRate = gb.MachineState().feedrate;
}
}
@@ -884,10 +834,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
break;
case 101: // Un-retract
- if (!LockMovement(gb))
- {
- return false;
- }
result = RetractFilament(gb, false);
break;
@@ -897,10 +843,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
break;
case 103: // Retract
- if (!LockMovement(gb))
- {
- return false;
- }
result = RetractFilament(gb, true);
break;
@@ -2451,7 +2393,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
break;
case 563: // Define tool
- ManageTool(gb, reply);
+ error = ManageTool(gb, reply);
break;
case 564: // Think outside the box?
@@ -3179,24 +3121,16 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
else
{
// List remembered networks
- const size_t declaredBufferLength = MaxRememberedNetworks * (SsidLength + 1) + 1; // enough for all the remembered SSIDs with null terminator, plus an extra null
+ const size_t declaredBufferLength = (MaxRememberedNetworks + 1) * (SsidLength + 1) + 1; // enough for all the remembered SSIDs with newline terminator, plus an extra null
uint32_t buffer[NumDwords(declaredBufferLength + 1)];
const int32_t rslt = reprap.GetNetwork().SendCommand(NetworkCommand::networkListSsids, 0, 0, nullptr, 0, buffer, declaredBufferLength);
if (rslt >= 0)
{
char* const cbuf = reinterpret_cast<char *>(buffer);
cbuf[declaredBufferLength] = 0; // ensure null terminated
- size_t len = strlen(cbuf);
-
- // If there is a trailing newline, remove it
- if (len != 0 && cbuf[len - 1] == '\n')
- {
- --len;
- cbuf[len] = 0;
- }
// DuetWiFiServer 1.19beta7 and later include the SSID used in access point mode at the start
- const char *bufp = strchr(cbuf, '\n');
+ char *bufp = strchr(cbuf, '\n');
if (bufp == nullptr)
{
bufp = cbuf; // must be an old version of DuetWiFiServer
@@ -3206,6 +3140,15 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
++bufp; // slip the first entry
}
+ // If there is a trailing newline, remove it
+ {
+ const size_t len = strlen(bufp);
+ if (len != 0 && bufp[len - 1] == '\n')
+ {
+ bufp[len - 1] = 0;
+ }
+ }
+
if (strlen(bufp) == 0)
{
reply.copy("No remembered networks");
@@ -3312,7 +3255,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
else
{
- const size_t declaredBufferLength = MaxRememberedNetworks * (SsidLength + 1) + 1; // enough for all the remembered SSIDs with null terminator, plus an extra null
+ const size_t declaredBufferLength = (MaxRememberedNetworks + 1) * (SsidLength + 1) + 1; // enough for all the remembered SSIDs with null terminator, plus an extra null
uint32_t buffer[NumDwords(declaredBufferLength + 1)];
const int32_t rslt = reprap.GetNetwork().SendCommand(NetworkCommand::networkListSsids, 0, 0, nullptr, 0, buffer, declaredBufferLength);
if (rslt >= 0)
@@ -3500,6 +3443,14 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
break;
#endif
+ case 671: // Set Z leadscrew positions
+ if (!LockMovementAndWaitForStandstill(gb))
+ {
+ return false;
+ }
+ (void)reprap.GetMove().GetKinematics().Configure(code, gb, reply, error);
+ break;
+
case 701: // Load filament
result = LoadFilament(gb, reply, error);
break;
@@ -3797,9 +3748,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
break;
- case 911: // Set power monitor threshold voltages
- reply.printf("M911 not implemented yet");
+#ifdef DUET_NG
+ case 911: // Enable auto save
+ platform.ConfigureAutoSave(gb, reply, error);
break;
+#endif
case 912: // Set electronics temperature monitor adjustment
// Currently we ignore the P parameter (i.e. temperature measurement channel)
@@ -3961,7 +3914,7 @@ bool GCodes::HandleTcode(GCodeBuffer& gb, StringRef& reply)
newToolNumber = gb.GetIValue();
newToolNumber += gb.GetToolNumberAdjust();
- reprap.GetMove().GetCurrentUserPosition(toolChangeRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes());
+ reprap.GetMove().GetCurrentUserPosition(toolChangeRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes());
toolChangeRestorePoint.feedRate = gb.MachineState().feedrate;
if (simulationMode == 0) // we don't yet simulate any T codes
diff --git a/src/Heating/FOPDT.cpp b/src/Heating/FOPDT.cpp
index 3ef32a72..6b716024 100644
--- a/src/Heating/FOPDT.cpp
+++ b/src/Heating/FOPDT.cpp
@@ -20,7 +20,7 @@ FopDt::FopDt()
}
// Check the model parameters are sensible, if they are then save them and return true.
-bool FopDt::SetParameters(float pg, float ptc, float pdt, float pMaxPwm, bool pUsePid)
+bool FopDt::SetParameters(float pg, float ptc, float pdt, float pMaxPwm, float temperatureLimit, bool pUsePid)
{
if (pg == -1.0 && ptc == -1.0 && pdt == -1.0)
{
@@ -30,7 +30,8 @@ bool FopDt::SetParameters(float pg, float ptc, float pdt, float pMaxPwm, bool pU
}
// DC 2017-06-20: allow S down to 0.01 for one of our OEMs (use > 0.0099 because >= 0.01 doesn't work due to rounding error)
- if (pg > 10.0 && pg <= 1500.0 && pdt > 0.099 && ptc >= 2 * pdt && pMaxPwm > 0.0099 && pMaxPwm <= 1.0)
+ const float maxGain = max<float>(1500.0, temperatureLimit + 500.0);
+ if (pg > 10.0 && pg <= maxGain && pdt > 0.099 && ptc >= 2 * pdt && pMaxPwm > 0.0099 && pMaxPwm <= 1.0)
{
gain = pg;
timeConstant = ptc;
diff --git a/src/Heating/FOPDT.h b/src/Heating/FOPDT.h
index 80850865..36fd6355 100644
--- a/src/Heating/FOPDT.h
+++ b/src/Heating/FOPDT.h
@@ -35,7 +35,7 @@ class FopDt
public:
FopDt();
- bool SetParameters(float pg, float ptc, float pdt, float pMaxPwm, bool pUsePid);
+ bool SetParameters(float pg, float ptc, float pdt, float pMaxPwm, float temperatureLimit, bool pUsePid);
float GetGain() const { return gain; }
float GetTimeConstant() const { return timeConstant; }
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp
index 4b4224f8..bfb982a6 100644
--- a/src/Heating/Heat.cpp
+++ b/src/Heating/Heat.cpp
@@ -258,9 +258,9 @@ void Heat::SwitchOff(int8_t heater)
void Heat::SwitchOffAll()
{
- for (size_t heater = 0; heater < Heaters; ++heater)
+ for (PID *p : pids)
{
- pids[heater]->SwitchOff();
+ p->SwitchOff();
}
}
@@ -468,4 +468,34 @@ float Heat::GetTemperature(size_t heater, TemperatureError& err)
return t;
}
+#ifdef DUET_NG
+
+// Suspend the heaters to conserve power
+void Heat::SuspendHeaters(bool sus)
+{
+ for (PID *p : pids)
+ {
+ p->Suspend(sus);
+ }
+}
+
+// Save some resume information returning true if successful.
+// We assume that the bed and chamber heaters are either on and active, or off (not on standby).
+bool Heat::WriteBedAndChamberTempSettings(FileStore *f) const
+{
+ char bufSpace[100];
+ StringRef buf(bufSpace, ARRAY_SIZE(bufSpace));
+ if (bedHeater >= 0 && pids[bedHeater]->Active() && !pids[bedHeater]->SwitchedOff())
+ {
+ buf.printf("M140 S%.1f\n", GetActiveTemperature(bedHeater));
+ }
+ if (chamberHeater >= 0 && pids[chamberHeater]->Active() && !pids[chamberHeater]->SwitchedOff())
+ {
+ buf.printf("M141 S%.1f\n", GetActiveTemperature(chamberHeater));
+ }
+ return (buf.Length() == 0) || f->Write(buf.Pointer());
+}
+
+#endif
+
// End
diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h
index ef3998ab..a793f25f 100644
--- a/src/Heating/Heat.h
+++ b/src/Heating/Heat.h
@@ -122,6 +122,11 @@ public:
float GetTemperature(size_t heater, TemperatureError& err); // Result is in degrees Celsius
+#ifdef DUET_NG
+ void SuspendHeaters(bool sus); // Suspend the heaters to conserve power
+ bool WriteBedAndChamberTempSettings(FileStore *f) const; // Save some resume information
+#endif
+
private:
Heat(const Heat&); // Private copy constructor to prevent copying
TemperatureSensor **GetSensor(size_t heater); // Get a pointer to the temperature sensor entry
diff --git a/src/Heating/Pid.cpp b/src/Heating/Pid.cpp
index 145ff014..c8494f4e 100644
--- a/src/Heating/Pid.cpp
+++ b/src/Heating/Pid.cpp
@@ -47,7 +47,7 @@ void PID::Init(float pGain, float pTc, float pTd, float tempLimit, bool usePid)
temperatureLimit = tempLimit;
maxTempExcursion = DefaultMaxTempExcursion;
maxHeatingFaultTime = DefaultMaxHeatingFaultTime;
- model.SetParameters(pGain, pTc, pTd, 1.0, usePid);
+ model.SetParameters(pGain, pTc, pTd, 1.0, tempLimit, usePid);
Reset();
if (model.IsEnabled())
@@ -74,12 +74,16 @@ void PID::Reset()
averagePWM = lastPwm = 0.0;
heatingFaultCount = 0;
temperature = BAD_ERROR_TEMPERATURE;
+#ifdef DUET_NG
+ suspended = false;
+#endif
}
// Set the process model
bool PID::SetModel(float gain, float tc, float td, float maxPwm, bool usePid)
{
- const bool rslt = model.SetParameters(gain, tc, td, maxPwm, usePid);
+ const float temperatureLimit = reprap.GetHeat().GetTemperatureLimit(heater);
+ const bool rslt = model.SetParameters(gain, tc, td, maxPwm, temperatureLimit, usePid);
if (rslt)
{
#if !defined(DUET_NG) && !defined(__RADDS__) && !defined(__ALLIGATOR__)
@@ -91,13 +95,13 @@ bool PID::SetModel(float gain, float tc, float td, float maxPwm, bool usePid)
#endif
if (model.IsEnabled())
{
- const float safeGain = (heater == reprap.GetHeat().GetBedHeater() || heater == reprap.GetHeat().GetChamberHeater())
- ? 170.0 : 480.0;
- if (gain > safeGain)
+ const float predictedMaxTemp = gain + NormalAmbientTemperature;
+ const float noWarnTemp = (temperatureLimit - NormalAmbientTemperature) * 1.5 + 50.0; // allow 50% extra power plus enough for an extra 50C
+ if (predictedMaxTemp > noWarnTemp)
{
platform.MessageF(GENERIC_MESSAGE,
- "Warning: Heater %u appears to be over-powered. If left on at full power, its temperature is predicted to reach %uC.\n",
- heater, (unsigned int)gain + 20);
+ "Warning: Heater %u appears to be over-powered. If left on at full power, its temperature is predicted to reach %dC.\n",
+ heater, (int)predictedMaxTemp);
}
}
else
@@ -181,6 +185,13 @@ void PID::Spin()
{
if (model.IsEnabled())
{
+#ifdef DUET_NG
+ if (suspended)
+ {
+ SetHeater(0.0);
+ return;
+ }
+#endif
// Read the temperature
const TemperatureError err = ReadTemperature();
@@ -826,4 +837,18 @@ void PID::DisplayBuffer(const char *intro)
}
}
+#ifdef DUET_NG
+
+// Suspend the heater to conserve power, or resume it
+void PID::Suspend(bool sus)
+{
+ suspended = sus;
+ if (sus && model.IsEnabled())
+ {
+ SetHeater(0.0);
+ }
+}
+
+#endif
+
// End
diff --git a/src/Heating/Pid.h b/src/Heating/Pid.h
index 53cea4fb..8907063a 100644
--- a/src/Heating/Pid.h
+++ b/src/Heating/Pid.h
@@ -86,6 +86,10 @@ public:
void SetM301PidParameters(const M301PidParameters& params)
{ model.SetM301PidParameters(params); }
+#ifdef DUET_NG
+ void Suspend(bool sus); // Suspend the heater to conserve power
+#endif
+
private:
void SwitchOn(); // Turn the heater on and set the mode
@@ -128,6 +132,9 @@ private:
HeaterMode mode; // Current state of the heater
bool active; // Are we active or standby?
bool tuned; // True if tuning was successful
+#ifdef DUET_NG
+ bool suspended; // True if suspended to save power
+#endif
uint8_t badTemperatureCount; // Count of sequential dud readings
static_assert(sizeof(previousTemperaturesGood) * 8 >= NumPreviousTemperatures, "too few bits in previousTemperaturesGood");
@@ -156,7 +163,6 @@ private:
};
-
inline bool PID::Active() const
{
return active;
diff --git a/src/Heating/Sensors/TemperatureSensor.cpp b/src/Heating/Sensors/TemperatureSensor.cpp
index 82a5399d..536a7dd0 100644
--- a/src/Heating/Sensors/TemperatureSensor.cpp
+++ b/src/Heating/Sensors/TemperatureSensor.cpp
@@ -70,7 +70,7 @@ void TemperatureSensor::TryConfigureHeaterName(GCodeBuffer& gb, bool& seen)
{
char buf[MaxHeaterNameLength + 1];
bool localSeen = false;
- gb.TryGetQuotedString('H', buf, ARRAY_SIZE(buf), localSeen);
+ gb.TryGetQuotedString('S', buf, ARRAY_SIZE(buf), localSeen);
if (localSeen)
{
SetHeaterName(buf);
diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp
index 153be884..0979c504 100644
--- a/src/Movement/DDA.cpp
+++ b/src/Movement/DDA.cpp
@@ -290,6 +290,7 @@ bool DDA::Init(const GCodes::RawMove &nextMove, bool doMotorMapping)
// 3. Store some values
xAxes = nextMove.xAxes;
+ yAxes = nextMove.yAxes;
endStopsToCheck = nextMove.endStopsToCheck;
canPauseBefore = nextMove.canPauseBefore;
canPauseAfter = nextMove.canPauseAfter;
@@ -1049,21 +1050,30 @@ void DDA::Prepare()
float DDA::NormaliseXYZ()
{
// First calculate the magnitude of the vector. If there is more than one X axis, take an average of their movements (they should be equal).
- float magSquared = 0.0;
- unsigned int numXaxes = 0;
+ float xMagSquared = 0.0, yMagSquared = 0.0;
+ unsigned int numXaxes = 0, numYaxes = 0;
for (size_t d = 0; d < MaxAxes; ++d)
{
if (((1 << d) & xAxes) != 0)
{
- magSquared += fsquare(directionVector[d]);
+ xMagSquared += fsquare(directionVector[d]);
++numXaxes;
}
+ if (((1 << d) & yAxes) != 0)
+ {
+ yMagSquared += fsquare(directionVector[d]);
+ ++numYaxes;
+ }
+ }
+ if (numXaxes > 1)
+ {
+ xMagSquared /= numXaxes;
}
- if (numXaxes != 0)
+ if (numYaxes > 1)
{
- magSquared /= numXaxes;
+ yMagSquared /= numYaxes;
}
- const float magnitude = sqrtf(magSquared + fsquare(directionVector[Y_AXIS]) + fsquare(directionVector[Z_AXIS]));
+ const float magnitude = sqrtf(xMagSquared + yMagSquared + fsquare(directionVector[Z_AXIS]));
if (magnitude <= 0.0)
{
return 0.0;
diff --git a/src/Movement/DDA.h b/src/Movement/DDA.h
index 9f193482..72f4ec51 100644
--- a/src/Movement/DDA.h
+++ b/src/Movement/DDA.h
@@ -69,6 +69,7 @@ public:
float AdvanceBabyStepping(float amount); // Try to push babystepping earlier in the move queue
bool IsHomingAxes() const { return (endStopsToCheck & HomeAxes) != 0; }
uint32_t GetXAxes() const { return xAxes; }
+ uint32_t GetYAxes() const { return yAxes; }
#if SUPPORT_IOBITS
uint32_t GetMoveStartTime() const { return moveStartTime; }
@@ -142,6 +143,7 @@ private:
EndstopChecks endStopsToCheck; // Which endstops we are checking on this move
uint32_t xAxes; // Which axes are behaving as X axes
+ uint32_t yAxes; // Which axes are behaving as Y axes
FilePosition filePos; // The position in the SD card file after this move was read, or zero if not read from SD card
diff --git a/src/Movement/Kinematics/CartesianKinematics.cpp b/src/Movement/Kinematics/CartesianKinematics.cpp
index 5aa8a1f9..28fb7e27 100644
--- a/src/Movement/Kinematics/CartesianKinematics.cpp
+++ b/src/Movement/Kinematics/CartesianKinematics.cpp
@@ -7,7 +7,7 @@
#include "CartesianKinematics.h"
-CartesianKinematics::CartesianKinematics() : Kinematics(KinematicsType::cartesian)
+CartesianKinematics::CartesianKinematics() : ZLeadscrewKinematics(KinematicsType::cartesian)
{
}
diff --git a/src/Movement/Kinematics/CartesianKinematics.h b/src/Movement/Kinematics/CartesianKinematics.h
index f2ad05ec..4064e0e1 100644
--- a/src/Movement/Kinematics/CartesianKinematics.h
+++ b/src/Movement/Kinematics/CartesianKinematics.h
@@ -8,9 +8,9 @@
#ifndef SRC_MOVEMENT_KINEMATICS_CARTESIANKINEMATICS_H_
#define SRC_MOVEMENT_KINEMATICS_CARTESIANKINEMATICS_H_
-#include "Kinematics.h"
+#include "ZLeadscrewKinematics.h"
-class CartesianKinematics : public Kinematics
+class CartesianKinematics : public ZLeadscrewKinematics
{
public:
CartesianKinematics();
@@ -19,7 +19,6 @@ public:
const char *GetName(bool forStatusReport) const override;
bool CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const override;
void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const override;
- bool SupportsAutoCalibration() const override { return false; }
bool DriveIsShared(size_t drive) const override { return false; }
HomingMode GetHomingMode() const override { return homeCartesianAxes; }
};
diff --git a/src/Movement/Kinematics/CoreBaseKinematics.cpp b/src/Movement/Kinematics/CoreBaseKinematics.cpp
index f712b4da..a75a70d4 100644
--- a/src/Movement/Kinematics/CoreBaseKinematics.cpp
+++ b/src/Movement/Kinematics/CoreBaseKinematics.cpp
@@ -8,7 +8,7 @@
#include "CoreBaseKinematics.h"
#include "GCodes/GCodes.h"
-CoreBaseKinematics::CoreBaseKinematics(KinematicsType t) : Kinematics(t)
+CoreBaseKinematics::CoreBaseKinematics(KinematicsType t) : ZLeadscrewKinematics(t)
{
for (float& af : axisFactors)
{
@@ -16,16 +16,6 @@ CoreBaseKinematics::CoreBaseKinematics(KinematicsType t) : Kinematics(t)
}
}
-// Convert Cartesian coordinates to motor coordinates
-bool CoreBaseKinematics::CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const
-{
- for (size_t axis = 0; axis < numVisibleAxes; ++axis)
- {
- motorPos[axis] = (int32_t)roundf(MotorFactor(axis, machinePos) * stepsPerMm[axis]);
- }
- return true;
-}
-
// Set the parameters from a M665, M666 or M669 command
// Return true if we changed any parameters. Set 'error' true if there was an error, otherwise leave it alone.
// This function is used for CoreXY and CoreXZ kinematics, but it overridden for CoreXYU kinematics
diff --git a/src/Movement/Kinematics/CoreBaseKinematics.h b/src/Movement/Kinematics/CoreBaseKinematics.h
index 5132e7f3..12c05602 100644
--- a/src/Movement/Kinematics/CoreBaseKinematics.h
+++ b/src/Movement/Kinematics/CoreBaseKinematics.h
@@ -8,24 +8,18 @@
#ifndef SRC_MOVEMENT_KINEMATICS_COREBASEKINEMATICS_H_
#define SRC_MOVEMENT_KINEMATICS_COREBASEKINEMATICS_H_
-#include "Kinematics.h"
+#include "ZLeadscrewKinematics.h"
-class CoreBaseKinematics : public Kinematics
+class CoreBaseKinematics : public ZLeadscrewKinematics
{
public:
CoreBaseKinematics(KinematicsType t);
// Overridden base class functions. See Kinematics.h for descriptions.
- bool CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const override final;
bool Configure(unsigned int mCode, GCodeBuffer& gb, StringRef& reply, bool& error) override;
- bool SupportsAutoCalibration() const override final { return false; }
HomingMode GetHomingMode() const override { return homeCartesianAxes; }
protected:
- // Calculate the movement fraction for a single axis motor of a Cartesian-like printer.
- // The default implementation just returns directionVector[drive] but this needs to be overridden for CoreXY and CoreXZ printers.
- virtual float MotorFactor(size_t drive, const float directionVector[]) const = 0;
-
float axisFactors[MaxAxes]; // allow more than just XYZ so that we can support e.g. CoreXYU kinematics
};
diff --git a/src/Movement/Kinematics/CoreXYKinematics.cpp b/src/Movement/Kinematics/CoreXYKinematics.cpp
index 8ff27a88..cb4123e1 100644
--- a/src/Movement/Kinematics/CoreXYKinematics.cpp
+++ b/src/Movement/Kinematics/CoreXYKinematics.cpp
@@ -17,14 +17,28 @@ const char *CoreXYKinematics::GetName(bool forStatusReport) const
return (forStatusReport) ? "coreXY" : "CoreXY";
}
+// Convert Cartesian coordinates to motor coordinates returning true if successful
+bool CoreXYKinematics::CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const
+{
+ motorPos[X_AXIS] = (int32_t)roundf(((machinePos[X_AXIS] * axisFactors[X_AXIS]) + (machinePos[Y_AXIS] * axisFactors[Y_AXIS])) * stepsPerMm[X_AXIS]);
+ motorPos[Y_AXIS] = (int32_t)roundf(((machinePos[X_AXIS] * axisFactors[X_AXIS]) - (machinePos[Y_AXIS] * axisFactors[Y_AXIS])) * stepsPerMm[Y_AXIS]);
+
+ for (size_t axis = Z_AXIS; axis < numVisibleAxes; ++axis)
+ {
+ motorPos[axis] = (int32_t)roundf(machinePos[axis] * stepsPerMm[axis]);
+ }
+ return true;
+}
+
// Convert motor coordinates to machine coordinates. Used after homing and after individual motor moves.
void CoreXYKinematics::MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const
{
- // Convert the axes
- machinePos[X_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) - (motorPos[Y_AXIS] * stepsPerMm[X_AXIS]))
- /(2 * axisFactors[X_AXIS] * stepsPerMm[X_AXIS] * stepsPerMm[Y_AXIS]);
- machinePos[Y_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) + (motorPos[Y_AXIS] * stepsPerMm[X_AXIS]))
- /(2 * axisFactors[Y_AXIS] * stepsPerMm[X_AXIS] * stepsPerMm[Y_AXIS]);
+ // Convert the main axes
+ const float xyStepsMm = stepsPerMm[X_AXIS] * stepsPerMm[Y_AXIS];
+ machinePos[X_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) + (motorPos[Y_AXIS] * stepsPerMm[X_AXIS]))
+ /(2 * axisFactors[X_AXIS] * xyStepsMm);
+ machinePos[Y_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) - (motorPos[Y_AXIS] * stepsPerMm[X_AXIS]))
+ /(2 * axisFactors[Y_AXIS] * xyStepsMm);
machinePos[Z_AXIS] = motorPos[Z_AXIS]/stepsPerMm[Z_AXIS];
// Convert any additional axes
@@ -41,20 +55,4 @@ bool CoreXYKinematics::DriveIsShared(size_t drive) const
return drive == X_AXIS || drive == Y_AXIS;
}
-// Calculate the movement fraction for a single axis motor
-float CoreXYKinematics::MotorFactor(size_t drive, const float directionVector[]) const
-{
- switch(drive)
- {
- case X_AXIS:
- return (directionVector[X_AXIS] * axisFactors[X_AXIS]) + (directionVector[Y_AXIS] * axisFactors[Y_AXIS]);
-
- case Y_AXIS:
- return (directionVector[Y_AXIS] * axisFactors[Y_AXIS]) - (directionVector[X_AXIS] * axisFactors[X_AXIS]);
-
- default:
- return directionVector[drive];
- }
-}
-
// End
diff --git a/src/Movement/Kinematics/CoreXYKinematics.h b/src/Movement/Kinematics/CoreXYKinematics.h
index 1c7cc797..0b426c55 100644
--- a/src/Movement/Kinematics/CoreXYKinematics.h
+++ b/src/Movement/Kinematics/CoreXYKinematics.h
@@ -17,11 +17,9 @@ public:
// Overridden base class functions. See Kinematics.h for descriptions.
const char *GetName(bool forStatusReport) const override;
+ bool CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const override;
void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const override;
bool DriveIsShared(size_t drive) const override;
-
-protected:
- float MotorFactor(size_t drive, const float directionVector[]) const override;
};
#endif /* SRC_MOVEMENT_KINEMATICS_COREXYKINEMATICS_H_ */
diff --git a/src/Movement/Kinematics/CoreXYUKinematics.cpp b/src/Movement/Kinematics/CoreXYUKinematics.cpp
index f14ecf87..39272486 100644
--- a/src/Movement/Kinematics/CoreXYUKinematics.cpp
+++ b/src/Movement/Kinematics/CoreXYUKinematics.cpp
@@ -54,18 +54,36 @@ bool CoreXYUKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, StringRef
}
}
+// Convert Cartesian coordinates to motor coordinates
+bool CoreXYUKinematics::CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const
+{
+ motorPos[X_AXIS] = (int32_t)roundf(((machinePos[X_AXIS] * axisFactors[X_AXIS]) + (machinePos[Y_AXIS] * axisFactors[Y_AXIS])) * stepsPerMm[X_AXIS]);
+ motorPos[Y_AXIS] = (int32_t)roundf(((machinePos[X_AXIS] * axisFactors[X_AXIS]) - (machinePos[Y_AXIS] * axisFactors[Y_AXIS])) * stepsPerMm[Y_AXIS]);
+ motorPos[Z_AXIS] = (int32_t)roundf(machinePos[Z_AXIS] * stepsPerMm[Z_AXIS]);
+ motorPos[U_AXIS] = (int32_t)roundf(((machinePos[U_AXIS] * axisFactors[U_AXIS]) + (machinePos[Y_AXIS] * axisFactors[Y_AXIS])) * stepsPerMm[U_AXIS]);
+ motorPos[V_AXIS] = (int32_t)roundf(((machinePos[U_AXIS] * axisFactors[U_AXIS]) - (machinePos[Y_AXIS] * axisFactors[Y_AXIS])) * stepsPerMm[V_AXIS]);
+
+ for (size_t axis = CoreXYU_AXES; axis < numVisibleAxes; ++axis)
+ {
+ motorPos[axis] = (int32_t)roundf(machinePos[axis] * stepsPerMm[axis]);
+ }
+ return true;
+}
+
// Convert motor coordinates to machine coordinates. Used after homing and after individual motor moves.
void CoreXYUKinematics::MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const
{
- // Convert the axes
- machinePos[X_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) - (motorPos[Y_AXIS] * stepsPerMm[X_AXIS]))
- /(2 * axisFactors[X_AXIS] * stepsPerMm[X_AXIS] * stepsPerMm[Y_AXIS]);
- machinePos[Y_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) + (motorPos[Y_AXIS] * stepsPerMm[X_AXIS]))
- /(2 * axisFactors[Y_AXIS] * stepsPerMm[X_AXIS] * stepsPerMm[Y_AXIS]);
- machinePos[U_AXIS] = ((motorPos[U_AXIS] * stepsPerMm[V_AXIS]) - (motorPos[V_AXIS] * stepsPerMm[U_AXIS]))
- /(2 * axisFactors[V_AXIS] * stepsPerMm[U_AXIS] * stepsPerMm[V_AXIS]);
- machinePos[V_AXIS] = ((motorPos[U_AXIS] * stepsPerMm[V_AXIS]) + (motorPos[V_AXIS] * stepsPerMm[U_AXIS]))
- /(2 * axisFactors[V_AXIS] * stepsPerMm[U_AXIS] * stepsPerMm[V_AXIS]);
+ // Convert the main axes
+ const float xyStepsMm = stepsPerMm[X_AXIS] * stepsPerMm[Y_AXIS];
+ const float uvStepsMm = stepsPerMm[U_AXIS] * stepsPerMm[V_AXIS];
+ machinePos[X_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) + (motorPos[Y_AXIS] * stepsPerMm[X_AXIS]))
+ /(2 * axisFactors[X_AXIS] * xyStepsMm);
+ machinePos[Y_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Y_AXIS]) - (motorPos[Y_AXIS] * stepsPerMm[X_AXIS]))
+ /(2 * axisFactors[Y_AXIS] * xyStepsMm);
+ machinePos[U_AXIS] = ((motorPos[U_AXIS] * stepsPerMm[V_AXIS]) + (motorPos[V_AXIS] * stepsPerMm[U_AXIS]))
+ /(2 * axisFactors[V_AXIS] * uvStepsMm);
+ machinePos[V_AXIS] = ((motorPos[U_AXIS] * stepsPerMm[V_AXIS]) - (motorPos[V_AXIS] * stepsPerMm[U_AXIS]))
+ /(2 * axisFactors[V_AXIS] * uvStepsMm);
machinePos[Z_AXIS] = motorPos[Z_AXIS]/stepsPerMm[Z_AXIS];
@@ -84,22 +102,4 @@ bool CoreXYUKinematics::DriveIsShared(size_t drive) const
|| drive == V_AXIS; // V doesn't have endstop switches, but include it here just in case
}
-// Calculate the movement fraction for a single axis motor
-float CoreXYUKinematics::MotorFactor(size_t drive, const float directionVector[]) const
-{
- switch(drive)
- {
- case X_AXIS:
- return (directionVector[X_AXIS] * axisFactors[X_AXIS]) + (directionVector[Y_AXIS] * axisFactors[Y_AXIS]);
- case Y_AXIS:
- return (directionVector[Y_AXIS] * axisFactors[Y_AXIS]) - (directionVector[X_AXIS] * axisFactors[X_AXIS]);
- case U_AXIS: // X2, Use Y and U to calculate
- return (directionVector[U_AXIS] * axisFactors[U_AXIS]) + (directionVector[Y_AXIS] * axisFactors[Y_AXIS]);
- case V_AXIS: // Y2, Use Y and U to calculate
- return (directionVector[Y_AXIS] * axisFactors[Y_AXIS]) - (directionVector[U_AXIS] * axisFactors[U_AXIS]);
- default:
- return directionVector[drive];
- }
-}
-
// End
diff --git a/src/Movement/Kinematics/CoreXYUKinematics.h b/src/Movement/Kinematics/CoreXYUKinematics.h
index c59c693f..67034f13 100644
--- a/src/Movement/Kinematics/CoreXYUKinematics.h
+++ b/src/Movement/Kinematics/CoreXYUKinematics.h
@@ -18,11 +18,9 @@ public:
// Overridden base class functions. See Kinematics.h for descriptions.
const char *GetName(bool forStatusReport) const override;
bool Configure(unsigned int mCode, GCodeBuffer& gb, StringRef& reply, bool& error) override;
+ bool CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const override;
void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const override;
bool DriveIsShared(size_t drive) const override;
-
-protected:
- float MotorFactor(size_t drive, const float directionVector[]) const override;
};
#endif /* SRC_MOVEMENT_KINEMATICS_COREXYKINEMATICS_H_ */
diff --git a/src/Movement/Kinematics/CoreXZKinematics.cpp b/src/Movement/Kinematics/CoreXZKinematics.cpp
index ede4d8ca..ad4975c0 100644
--- a/src/Movement/Kinematics/CoreXZKinematics.cpp
+++ b/src/Movement/Kinematics/CoreXZKinematics.cpp
@@ -17,14 +17,30 @@ const char *CoreXZKinematics::GetName(bool forStatusReport) const
return (forStatusReport) ? "coreXZ" : "CoreXZ";
}
+// Convert Cartesian coordinates to motor coordinates
+bool CoreXZKinematics::CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const
+{
+ motorPos[X_AXIS] = (int32_t)roundf(((machinePos[X_AXIS] * axisFactors[X_AXIS]) + (machinePos[Z_AXIS] * axisFactors[Z_AXIS])) * stepsPerMm[X_AXIS]);
+ motorPos[Y_AXIS] = (int32_t)roundf(machinePos[Y_AXIS] * stepsPerMm[Y_AXIS]);
+ motorPos[Z_AXIS] = (int32_t)roundf(((machinePos[X_AXIS] * axisFactors[X_AXIS]) - (machinePos[Z_AXIS] * axisFactors[Z_AXIS])) * stepsPerMm[Z_AXIS]);
+
+ for (size_t axis = XYZ_AXES; axis < numVisibleAxes; ++axis)
+ {
+ motorPos[axis] = (int32_t)roundf(machinePos[axis] * stepsPerMm[axis]);
+ }
+ return true;
+}
+
// Convert motor coordinates to machine coordinates. Used after homing and after individual motor moves.
void CoreXZKinematics::MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const
{
- machinePos[X_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Z_AXIS]) - (motorPos[Z_AXIS] * stepsPerMm[X_AXIS]))
- /(2 * axisFactors[X_AXIS] * stepsPerMm[X_AXIS] * stepsPerMm[Z_AXIS]);
+ // Convert the main axes
+ const float xzStepsMmm = stepsPerMm[X_AXIS] * stepsPerMm[Z_AXIS];
+ machinePos[X_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Z_AXIS]) + (motorPos[Z_AXIS] * stepsPerMm[X_AXIS]))
+ /(2 * axisFactors[X_AXIS] * xzStepsMmm);
machinePos[Y_AXIS] = motorPos[Y_AXIS]/stepsPerMm[Y_AXIS];
- machinePos[Z_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Z_AXIS]) + (motorPos[Z_AXIS] * stepsPerMm[X_AXIS]))
- /(2 * axisFactors[Z_AXIS] * stepsPerMm[X_AXIS] * stepsPerMm[Z_AXIS]);
+ machinePos[Z_AXIS] = ((motorPos[X_AXIS] * stepsPerMm[Z_AXIS]) - (motorPos[Z_AXIS] * stepsPerMm[X_AXIS]))
+ /(2 * axisFactors[Z_AXIS] * xzStepsMmm);
// Convert any additional axes linearly
for (size_t drive = XYZ_AXES; drive < numVisibleAxes; ++drive)
@@ -40,20 +56,4 @@ bool CoreXZKinematics::DriveIsShared(size_t drive) const
return drive == X_AXIS || drive == Z_AXIS;
}
-// Calculate the movement fraction for a single axis motor of a Cartesian-like printer
-float CoreXZKinematics::MotorFactor(size_t drive, const float directionVector[]) const
-{
- switch(drive)
- {
- case X_AXIS:
- return (directionVector[X_AXIS] * axisFactors[X_AXIS]) + (directionVector[Z_AXIS] * axisFactors[Z_AXIS]);
-
- case Z_AXIS:
- return (directionVector[Z_AXIS] * axisFactors[Z_AXIS]) - (directionVector[X_AXIS] * axisFactors[X_AXIS]);
-
- default:
- return directionVector[drive];
- }
-}
-
// End
diff --git a/src/Movement/Kinematics/CoreXZKinematics.h b/src/Movement/Kinematics/CoreXZKinematics.h
index fb72648b..fb672d7a 100644
--- a/src/Movement/Kinematics/CoreXZKinematics.h
+++ b/src/Movement/Kinematics/CoreXZKinematics.h
@@ -17,12 +17,11 @@ public:
// Overridden base class functions. See Kinematics.h for descriptions.
const char *GetName(bool forStatusReport) const override;
+ bool CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const override;
void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const override;
- uint16_t AxesToHomeBeforeProbing() const override { return (1 << X_AXIS) | (1 << Y_AXIS) | (1 << Z_AXIS); }
+ uint32_t AxesToHomeBeforeProbing() const override { return (1u << X_AXIS) | (1u << Y_AXIS) | (1u << Z_AXIS); }
bool DriveIsShared(size_t drive) const override;
-
-protected:
- float MotorFactor(size_t drive, const float directionVector[]) const override;
+ bool SupportsAutoCalibration() const override { return false; }
};
#endif /* SRC_MOVEMENT_KINEMATICS_COREXZKINEMATICS_H_ */
diff --git a/src/Movement/Kinematics/Kinematics.cpp b/src/Movement/Kinematics/Kinematics.cpp
index dd55b2ed..21bfd378 100644
--- a/src/Movement/Kinematics/Kinematics.cpp
+++ b/src/Movement/Kinematics/Kinematics.cpp
@@ -101,4 +101,35 @@ void Kinematics::GetAssumedInitialPosition(size_t numAxes, float positions[]) co
}
}
+/*static*/ void Kinematics::PrintMatrix(const char* s, const MathMatrix<floatc_t>& m, size_t maxRows, size_t maxCols)
+{
+ debugPrintf("%s\n", s);
+ if (maxRows == 0)
+ {
+ maxRows = m.rows();
+ }
+ if (maxCols == 0)
+ {
+ maxCols = m.cols();
+ }
+
+ for (size_t i = 0; i < maxRows; ++i)
+ {
+ for (size_t j = 0; j < maxCols; ++j)
+ {
+ debugPrintf("%7.4f%c", m(i, j), (j == maxCols - 1) ? '\n' : ' ');
+ }
+ }
+}
+
+/*static*/ void Kinematics::PrintVector(const char *s, const floatc_t *v, size_t numElems)
+{
+ debugPrintf("%s:", s);
+ for (size_t i = 0; i < numElems; ++i)
+ {
+ debugPrintf(" %7.4f", v[i]);
+ }
+ debugPrintf("\n");
+}
+
// End
diff --git a/src/Movement/Kinematics/Kinematics.h b/src/Movement/Kinematics/Kinematics.h
index ab340b4a..737a5d3a 100644
--- a/src/Movement/Kinematics/Kinematics.h
+++ b/src/Movement/Kinematics/Kinematics.h
@@ -10,6 +10,19 @@
#include "GCodes/GCodeBuffer.h"
#include "Movement/BedProbing/RandomProbePointSet.h"
+#include "Libraries/Math/Matrix.h"
+
+#ifdef DUET_NG
+typedef double floatc_t; // type of matrix element used for calibration
+#else
+// We are more memory-constrained on the SAM3X
+typedef float floatc_t; // type of matrix element used for calibration
+#endif
+
+inline floatc_t fcsquare(floatc_t a)
+{
+ return a * a;
+}
// Different types of kinematics we support. Each of these has a class to represent it.
// These must have the same numeric assignments as the K parameter of the M669 command, as documented in ???
@@ -75,7 +88,7 @@ public:
virtual void MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const = 0;
// Return true if the kinematics supports auto calibration based on bed probing.
- // Normally returns false, but overridden for delta kinematics.
+ // Normally returns false, but overridden for delta kinematics and kinematics with multiple independently-drive Z leadscrews.
virtual bool SupportsAutoCalibration() const { return false; }
// Perform auto calibration. Override this implementation in kinematics that support it.
@@ -104,7 +117,7 @@ public:
// Return the set of axes that must have been homed before bed probing is allowed
// The default implementation requires just X and Y, but some kinematics require additional axes to be homed (e.g. delta, CoreXZ)
- virtual uint16_t AxesToHomeBeforeProbing() const { return (1 << X_AXIS) | (1 << Y_AXIS); }
+ virtual uint32_t AxesToHomeBeforeProbing() const { return (1u << X_AXIS) | (1u << Y_AXIS); }
// Return the initial Cartesian coordinates we assume after switching to this kinematics
virtual void GetAssumedInitialPosition(size_t numAxes, float positions[]) const;
@@ -125,6 +138,15 @@ public:
// Return the type of homing we do
virtual HomingMode GetHomingMode() const = 0;
+ // Return the axes that we can assume are homed after executing a G92 command to set the specified axis coordinates
+ // This default is good for Cartesian and Core printers, but not deltas or SCARA
+ virtual uint32_t AxesAssumedHomed(uint32_t g92Axes) const { return g92Axes; }
+
+#ifdef DUET_NG
+ // Write any calibration data that we need to resume a print after power fail, returning true if successful. Override where necessary.
+ virtual bool WriteResumeSettings(FileStore *f) const { return true; }
+#endif
+
// Override this virtual destructor if your constructor allocates any dynamic memory
virtual ~Kinematics() { }
@@ -147,6 +169,10 @@ protected:
// This constructor is used by derived classes that implement segmented linear motion
Kinematics(KinematicsType t, float segsPerSecond, float minSegLength, bool doUseRawG0);
+ // Debugging functions
+ static void PrintMatrix(const char* s, const MathMatrix<floatc_t>& m, size_t numRows = 0, size_t maxCols = 0);
+ static void PrintVector(const char *s, const floatc_t *v, size_t numElems);
+
float segmentsPerSecond; // if we are using segmentation, the target number of segments/second
float minSegmentLength; // if we are using segmentation, the minimum segment size
diff --git a/src/Movement/Kinematics/LinearDeltaKinematics.cpp b/src/Movement/Kinematics/LinearDeltaKinematics.cpp
index ee01fbed..e663b322 100644
--- a/src/Movement/Kinematics/LinearDeltaKinematics.cpp
+++ b/src/Movement/Kinematics/LinearDeltaKinematics.cpp
@@ -30,6 +30,7 @@ void LinearDeltaKinematics::Init()
xTilt = yTilt = 0.0;
printRadius = defaultPrintRadius;
homedHeight = defaultDeltaHomedHeight;
+ doneAutoCalibration = false;
for (size_t axis = 0; axis < DELTA_AXES; ++axis)
{
@@ -223,7 +224,7 @@ void LinearDeltaKinematics::DoAutoCalibration(size_t numFactors, const RandomPro
if (numFactors < 3 || numFactors > NumDeltaFactors || numFactors == 5)
{
- reprap.GetPlatform().MessageF(GENERIC_MESSAGE, "Delta calibration error: %d factors requested but only 3, 4, 6, 7, 8 and 9 supported\n", numFactors);
+ reply.printf("Error: Delta calibration with %d factors requested but only 3, 4, 6, 7, 8 and 9 supported", numFactors);
return;
}
@@ -239,19 +240,19 @@ void LinearDeltaKinematics::DoAutoCalibration(size_t numFactors, const RandomPro
// Transform the probing points to motor endpoints and store them in a matrix, so that we can do multiple iterations using the same data
FixedMatrix<floatc_t, MaxDeltaCalibrationPoints, DELTA_AXES> probeMotorPositions;
floatc_t corrections[MaxDeltaCalibrationPoints];
- float initialSumOfSquares = 0.0;
+ floatc_t initialSumOfSquares = 0.0;
for (size_t i = 0; i < numPoints; ++i)
{
corrections[i] = 0.0;
float machinePos[DELTA_AXES];
- const float zp = reprap.GetMove().GetProbeCoordinates(i, machinePos[X_AXIS], machinePos[Y_AXIS], probePoints.PointWasCorrected(i));
+ const floatc_t zp = reprap.GetMove().GetProbeCoordinates(i, machinePos[X_AXIS], machinePos[Y_AXIS], probePoints.PointWasCorrected(i));
machinePos[Z_AXIS] = 0.0;
probeMotorPositions(i, A_AXIS) = Transform(machinePos, A_AXIS);
probeMotorPositions(i, B_AXIS) = Transform(machinePos, B_AXIS);
probeMotorPositions(i, C_AXIS) = Transform(machinePos, C_AXIS);
- initialSumOfSquares += fsquare(zp);
+ initialSumOfSquares += fcsquare(zp);
}
// Do 1 or more Newton-Raphson iterations
@@ -356,7 +357,7 @@ void LinearDeltaKinematics::DoAutoCalibration(size_t numFactors, const RandomPro
InverseTransform(probeMotorPositions(i, A_AXIS), probeMotorPositions(i, B_AXIS), probeMotorPositions(i, C_AXIS), newPosition);
corrections[i] = newPosition[Z_AXIS];
expectedResiduals[i] = probePoints.GetZHeight(i) + newPosition[Z_AXIS];
- sumOfSquares += fsquare(expectedResiduals[i]);
+ sumOfSquares += fcsquare(expectedResiduals[i]);
}
expectedRmsError = sqrt(sumOfSquares/numPoints);
@@ -384,8 +385,10 @@ void LinearDeltaKinematics::DoAutoCalibration(size_t numFactors, const RandomPro
debugPrintf("%s\n", scratchString.Pointer());
}
- reply.printf("Calibrated %d factors using %d points, deviation before %.3f after %.3f\n",
+ reply.printf("Calibrated %d factors using %d points, deviation before %.3f after %.3f",
numFactors, numPoints, sqrt(initialSumOfSquares/numPoints), expectedRmsError);
+
+ doneAutoCalibration = true;
}
// Return the type of motion computation needed by an axis
@@ -532,7 +535,7 @@ void LinearDeltaKinematics::PrintParameters(StringRef& reply) const
angleCorrections[A_AXIS], angleCorrections[B_AXIS], angleCorrections[C_AXIS], xTilt * 100.0, yTilt * 100.0);
}
-// Write the parameters that are set by auto calibration to the config-override.g file, returning true if success
+// Write the parameters that are set by auto calibration to a file, returning true if success
bool LinearDeltaKinematics::WriteCalibrationParameters(FileStore *f) const
{
bool ok = f->Write("; Delta parameters\n");
@@ -551,6 +554,16 @@ bool LinearDeltaKinematics::WriteCalibrationParameters(FileStore *f) const
return ok;
}
+#ifdef DUET_NG
+
+// Write any calibration data that we need to resume a print after power fail, returning true if successful
+bool LinearDeltaKinematics::WriteResumeSettings(FileStore *f) const
+{
+ return !doneAutoCalibration || WriteCalibrationParameters(f);
+}
+
+#endif
+
// Get the bed tilt fraction for the specified axis
float LinearDeltaKinematics::GetTiltCorrection(size_t axis) const
{
@@ -665,38 +678,16 @@ bool LinearDeltaKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, Strin
}
}
-/*static*/ void LinearDeltaKinematics::PrintMatrix(const char* s, const MathMatrix<floatc_t>& m, size_t maxRows, size_t maxCols)
+// Return the axes that we can assume are homed after executing a G92 command to set the specified axis coordinates
+uint32_t LinearDeltaKinematics::AxesAssumedHomed(uint32_t g92Axes) const
{
- debugPrintf("%s\n", s);
- if (maxRows == 0)
+ // If all of X, Y and Z have been specified then we know the positions of all 3 tower motors, otherwise we don't
+ const uint32_t xyzAxes = (1u << X_AXIS) | (1u << Y_AXIS) | (1u << Z_AXIS);
+ if ((g92Axes & xyzAxes) != xyzAxes)
{
- maxRows = m.rows();
+ g92Axes &= ~xyzAxes;
}
- if (maxCols == 0)
- {
- maxCols = m.cols();
- }
-
- for (size_t i = 0; i < maxRows; ++i)
- {
- for (size_t j = 0; j < maxCols; ++j)
- {
- debugPrintf("%7.4f%c", m(i, j), (j == maxCols - 1) ? '\n' : ' ');
- }
- }
-}
-
-/*static*/ void LinearDeltaKinematics::PrintVector(const char *s, const floatc_t *v, size_t numElems)
-{
- debugPrintf("%s:", s);
- for (size_t i = 0; i < numElems; ++i)
- {
- debugPrintf(" %7.4f", v[i]);
- }
- debugPrintf("\n");
+ return g92Axes;
}
// End
-
-
-
diff --git a/src/Movement/Kinematics/LinearDeltaKinematics.h b/src/Movement/Kinematics/LinearDeltaKinematics.h
index 506ea52e..42173c51 100644
--- a/src/Movement/Kinematics/LinearDeltaKinematics.h
+++ b/src/Movement/Kinematics/LinearDeltaKinematics.h
@@ -10,14 +10,6 @@
#include "RepRapFirmware.h"
#include "Kinematics.h"
-#include "Libraries/Math/Matrix.h"
-
-#ifdef DUET_NG
-typedef double floatc_t; // type of matrix element used for delta calibration
-#else
-// We are more memory-constrained on the SAM3X
-typedef float floatc_t; // type of matrix element used for delta calibration
-#endif
const size_t DELTA_AXES = 3;
const size_t A_AXIS = 0;
@@ -44,11 +36,16 @@ public:
bool IsReachable(float x, float y) const override;
bool LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const override;
void GetAssumedInitialPosition(size_t numAxes, float positions[]) const override;
- uint16_t AxesToHomeBeforeProbing() const override { return (1 << X_AXIS) | (1 << Y_AXIS) | (1 << Z_AXIS); }
+ uint32_t AxesToHomeBeforeProbing() const override { return (1u << X_AXIS) | (1u << Y_AXIS) | (1u << Z_AXIS); }
MotionType GetMotionType(size_t axis) const override;
size_t NumHomingButtons(size_t numVisibleAxes) const override { return 0; }
bool DriveIsShared(size_t drive) const override { return false; }
HomingMode GetHomingMode() const override { return homeIndividualMotors; }
+ uint32_t AxesAssumedHomed(uint32_t g92Axes) const override;
+
+#ifdef DUET_NG
+ bool WriteResumeSettings(FileStore *f) const override;
+#endif
// Public functions specific to this class
float GetDiagonalSquared() const { return D2; }
@@ -68,9 +65,6 @@ private:
void Adjust(size_t numFactors, const floatc_t v[]); // Perform 3-, 4-, 6- or 7-factor adjustment
void PrintParameters(StringRef& reply) const; // Print all the parameters for debugging
- static void PrintMatrix(const char* s, const MathMatrix<floatc_t>& m, size_t numRows = 0, size_t maxCols = 0); // for debugging
- static void PrintVector(const char *s, const floatc_t *v, size_t numElems); // for debugging
-
// Delta parameter defaults
const float defaultDiagonal = 215.0;
const float defaultDeltaRadius = 105.6;
@@ -87,7 +81,6 @@ private:
float xTilt, yTilt; // How much we need to raise Z for each unit of movement in the +X and +Y directions
// Derived values
- bool deltaMode; // True if this is a delta printer
float towerX[DELTA_AXES]; // The X coordinate of each tower
float towerY[DELTA_AXES]; // The Y coordinate of each tower
float printRadiusSquared;
@@ -95,6 +88,7 @@ private:
float Xbc, Xca, Xab, Ybc, Yca, Yab;
float coreFa, coreFb, coreFc;
float Q, Q2, D2;
+ bool doneAutoCalibration; // True if we have done auto calibration
};
#endif /* LINEARDELTAKINEMATICS_H_ */
diff --git a/src/Movement/Kinematics/ScaraKinematics.cpp b/src/Movement/Kinematics/ScaraKinematics.cpp
index 8dbd5845..9b25d62c 100644
--- a/src/Movement/Kinematics/ScaraKinematics.cpp
+++ b/src/Movement/Kinematics/ScaraKinematics.cpp
@@ -9,7 +9,7 @@
ScaraKinematics::ScaraKinematics()
: Kinematics(KinematicsType::scara, DefaultSegmentsPerSecond, DefaultMinSegmentSize, true),
- proximalArmLength(DefaultProximalArmLength), distalArmLength(DefaultDistalArmLength)
+ proximalArmLength(DefaultProximalArmLength), distalArmLength(DefaultDistalArmLength), xOffset(0.0), yOffset(0.0)
{
thetaLimits[0] = DefaultMinTheta;
thetaLimits[1] = DefaultMaxTheta;
@@ -26,24 +26,24 @@ const char *ScaraKinematics::GetName(bool forStatusReport) const
}
// Convert Cartesian coordinates to motor coordinates
-// In the following, theta is the proximal arm angle relative to the X axis, psi is the distal arm angle relative to the X axis
+// In the following, theta is the proximal arm angle relative to the X axis, psi is the distal arm angle relative to the proximal arm
bool ScaraKinematics::CartesianToMotorSteps(const float machinePos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, int32_t motorPos[]) const
{
// No need to limit x,y to reachable positions here, we already did that in class GCodes
- const float x = machinePos[X_AXIS];
- const float y = machinePos[Y_AXIS];
- const float cosPsiMinusTheta = (fsquare(x) + fsquare(y) - proximalArmLengthSquared - distalArmLengthSquared) / (2.0f * proximalArmLength * distalArmLength);
+ const float x = machinePos[X_AXIS] + xOffset;
+ const float y = machinePos[Y_AXIS] + yOffset;
+ const float cosPsi = (fsquare(x) + fsquare(y) - proximalArmLengthSquared - distalArmLengthSquared) / (2.0f * proximalArmLength * distalArmLength);
// SCARA position is undefined if abs(SCARA_C2) >= 1. In reality abs(SCARA_C2) >0.95 can be problematic.
- const float square = 1.0f - fsquare(cosPsiMinusTheta);
+ const float square = 1.0f - fsquare(cosPsi);
if (square < 0.01f)
{
return false; // not reachable
}
const float sinPsiMinusTheta = sqrtf(square);
- float psiMinusTheta = acos(cosPsiMinusTheta);
- const float SCARA_K1 = proximalArmLength + distalArmLength * cosPsiMinusTheta;
+ float psi = acos(cosPsi);
+ const float SCARA_K1 = proximalArmLength + distalArmLength * cosPsi;
const float SCARA_K2 = distalArmLength * sinPsiMinusTheta;
float theta;
@@ -66,7 +66,7 @@ bool ScaraKinematics::CartesianToMotorSteps(const float machinePos[], const floa
theta = atan2f(SCARA_K1 * y + SCARA_K2 * x, SCARA_K1 * x - SCARA_K2 * y);
if (theta <= thetaLimits[1])
{
- psiMinusTheta = -psiMinusTheta;
+ psi = -psi;
break;
}
}
@@ -79,8 +79,7 @@ bool ScaraKinematics::CartesianToMotorSteps(const float machinePos[], const floa
switchedMode = true;
}
- const float psi = theta + psiMinusTheta;
-//debugPrintf("psiMinusTheta = %.2f, psi = %.2f, theta = %.2f\n", psiMinusTheta * RadiansToDegrees, psi * RadiansToDegrees, theta * RadiansToDegrees);
+//debugPrintf("psi = %.2f, theta = %.2f\n", psi * RadiansToDegrees, theta * RadiansToDegrees);
motorPos[X_AXIS] = theta * RadiansToDegrees * stepsPerMm[X_AXIS];
motorPos[Y_AXIS] = (psi * RadiansToDegrees * stepsPerMm[Y_AXIS]) - (crosstalk[0] * motorPos[X_AXIS]);
@@ -99,10 +98,10 @@ bool ScaraKinematics::CartesianToMotorSteps(const float machinePos[], const floa
void ScaraKinematics::MotorStepsToCartesian(const int32_t motorPos[], const float stepsPerMm[], size_t numVisibleAxes, size_t numTotalAxes, float machinePos[]) const
{
const float arm1Angle = ((float)motorPos[X_AXIS]/stepsPerMm[X_AXIS]) * DegreesToRadians;
- const float arm2Angle = (((float)motorPos[Y_AXIS] + ((float)motorPos[X_AXIS] * crosstalk[0]))/stepsPerMm[Y_AXIS]) * DegreesToRadians;
+ const float arm2Angle = (((float)motorPos[Y_AXIS] + ((float)motorPos[X_AXIS] * (1.0 + crosstalk[0])))/stepsPerMm[Y_AXIS]) * DegreesToRadians;
- machinePos[X_AXIS] = cosf(arm1Angle) * proximalArmLength + cosf(arm2Angle) * distalArmLength;
- machinePos[Y_AXIS] = sinf(arm1Angle) * proximalArmLength + sinf(arm2Angle) * distalArmLength;
+ machinePos[X_AXIS] = (cosf(arm1Angle) * proximalArmLength + cosf(arm2Angle) * distalArmLength) - xOffset;
+ machinePos[Y_AXIS] = (sinf(arm1Angle) * proximalArmLength + sinf(arm2Angle) * distalArmLength) - yOffset;
// On some machines (e.g. Helios), the X and/or Y arm motors also affect the Z height
machinePos[Z_AXIS] = ((float)motorPos[Z_AXIS] + ((float)motorPos[X_AXIS] * crosstalk[1]) + ((float)motorPos[Y_AXIS] * crosstalk[2]))/stepsPerMm[Z_AXIS];
@@ -125,6 +124,8 @@ bool ScaraKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, StringRef&
gb.TryGetFValue('D', distalArmLength, seen);
gb.TryGetFValue('S', segmentsPerSecond, seen);
gb.TryGetFValue('T', minSegmentLength, seen);
+ gb.TryGetFValue('X', xOffset, seen);
+ gb.TryGetFValue('Y', yOffset, seen);
if (gb.TryGetFloatArray('A', 2, thetaLimits, reply, seen))
{
return true;
@@ -173,13 +174,22 @@ bool ScaraKinematics::IsReachable(float x, float y) const
bool ScaraKinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint16_t axesHomed) const
{
bool limited = false;
- float& x = coords[X_AXIS];
- float& y = coords[Y_AXIS];
+ float x = coords[X_AXIS] + xOffset;
+ float y = coords[Y_AXIS] + yOffset;
const float r = sqrtf(fsquare(x) + fsquare(y));
if (r < minRadius)
{
- x *= minRadius/r;
- y *= minRadius/r;
+ // The user may have specified x=0 y=0 so allow for this
+ if (r < 1.0)
+ {
+ x = minRadius;
+ y = 0.0;
+ }
+ else
+ {
+ x *= minRadius/r;
+ y *= minRadius/r;
+ }
limited = true;
}
else if (r > maxRadius)
@@ -188,14 +198,21 @@ bool ScaraKinematics::LimitPosition(float coords[], size_t numVisibleAxes, uint1
y *= maxRadius/r;
limited = true;
}
+
+ if (limited)
+ {
+ coords[X_AXIS] = x - xOffset;
+ coords[Y_AXIS] = y - yOffset;
+ }
return limited;
}
// Return the initial Cartesian coordinates we assume after switching to this kinematics
void ScaraKinematics::GetAssumedInitialPosition(size_t numAxes, float positions[]) const
{
- positions[X_AXIS] = maxRadius;
- for (size_t i = Y_AXIS; i < numAxes; ++i)
+ positions[X_AXIS] = maxRadius - xOffset;
+ positions[Y_AXIS] = -yOffset;
+ for (size_t i = Z_AXIS; i < numAxes; ++i)
{
positions[i] = 0.0;
}
@@ -217,6 +234,18 @@ bool ScaraKinematics::DriveIsShared(size_t drive) const
}
}
+// Return the axes that we can assume are homed after executing a G92 command to set the specified axis coordinates
+uint32_t ScaraKinematics::AxesAssumedHomed(uint32_t g92Axes) const
+{
+ // If both X and Y have been specified then we know the positions of both arm motors, otherwise we don't
+ const uint32_t xyAxes = (1u << X_AXIS) | (1u << Y_AXIS);
+ if ((g92Axes & xyAxes) != xyAxes)
+ {
+ g92Axes &= ~xyAxes;
+ }
+ return g92Axes;
+}
+
// Recalculate the derived parameters
void ScaraKinematics::Recalc()
{
diff --git a/src/Movement/Kinematics/ScaraKinematics.h b/src/Movement/Kinematics/ScaraKinematics.h
index 73ff2892..1215bdeb 100644
--- a/src/Movement/Kinematics/ScaraKinematics.h
+++ b/src/Movement/Kinematics/ScaraKinematics.h
@@ -37,6 +37,7 @@ public:
const char* HomingButtonNames() const override { return "PDZUVW"; }
bool DriveIsShared(size_t drive) const override;
HomingMode GetHomingMode() const override { return homeSharedMotors; }
+ uint32_t AxesAssumedHomed(uint32_t g92Axes) const override;
private:
static constexpr float DefaultSegmentsPerSecond = 200.0;
@@ -56,6 +57,8 @@ private:
float thetaLimits[2]; // minimum proximal joint angle
float phiMinusThetaLimits[2]; // minimum distal joint angle
float crosstalk[3]; // if we rotate the distal arm motor, for each full rotation the Z height goes up by this amount
+ float xOffset; // where bed X=0 is relative to the proximal joint
+ float yOffset; // where bed Y=0 is relative to the proximal joint
// Derived parameters
float minRadius;
diff --git a/src/Movement/Kinematics/ZLeadscrewKinematics.cpp b/src/Movement/Kinematics/ZLeadscrewKinematics.cpp
new file mode 100644
index 00000000..bbaa8c72
--- /dev/null
+++ b/src/Movement/Kinematics/ZLeadscrewKinematics.cpp
@@ -0,0 +1,277 @@
+/*
+ * ZLeadscrewKinematics.cpp
+ *
+ * Created on: 8 Jul 2017
+ * Author: David
+ */
+
+#include "ZLeadscrewKinematics.h"
+#include "RepRap.h"
+#include "Platform.h"
+#include "Movement/Move.h"
+
+ZLeadscrewKinematics::ZLeadscrewKinematics(KinematicsType k) : Kinematics(k), numLeadscrews(0), maxCorrection(1.0)
+{
+}
+
+// Configure this kinematics. We only deal with the leadscrew coordinates here
+bool ZLeadscrewKinematics::Configure(unsigned int mCode, GCodeBuffer& gb, StringRef& reply, bool& error)
+{
+ if (mCode == 671 && GetKinematicsType() != KinematicsType::coreXZ)
+ {
+ // Configuring leadscrew positions
+ const size_t numZDrivers = reprap.GetPlatform().GetAxisDriversConfig(Z_AXIS).numDrivers;
+ if (numZDrivers < 2 || numZDrivers > MaxLeadscrews)
+ {
+ reply.copy("Configure 2 to 4 Z drivers before sending M671");
+ return true;
+ }
+
+ bool seenX = false, seenY = false;
+ if (gb.TryGetFloatArray('X', numZDrivers, leadscrewX, reply, seenX))
+ {
+ return true;
+ }
+ if (gb.TryGetFloatArray('Y', numZDrivers, leadscrewY, reply, seenY))
+ {
+ return true;
+ }
+
+ bool seenS;
+ gb.TryGetFValue('S', maxCorrection, seenS);
+
+ if (seenX && seenY)
+ {
+ numLeadscrews = numZDrivers;
+ return false; // successful configuration
+ }
+
+ if (seenX || seenY)
+ {
+ reply.copy("Specify both X and Y coordinates in M671");
+ return true;
+ }
+
+ // If no parameters provided so just report the existing setup
+ if (seenS)
+ {
+ return true; // just changed the maximum correction
+ }
+ else if (numLeadscrews < 2)
+ {
+ reply.copy("Z leadscrew coordinates are not configured");
+ }
+ else
+ {
+ reply.copy("Z leadscrew coordinates");
+ for (unsigned int i = 0; i < numLeadscrews; ++i)
+ {
+ reply.catf(" (%.1f,%.1f)", leadscrewX[i], leadscrewY[i]);
+ }
+ }
+ return false;
+ }
+ return Kinematics::Configure(mCode, gb, reply, error);
+}
+
+// Return true if the kinematics supports auto calibration based on bed probing.
+bool ZLeadscrewKinematics::SupportsAutoCalibration() const
+{
+ return numLeadscrews >= 2;
+}
+
+// Perform auto calibration. Override this implementation in kinematics that support it.
+void ZLeadscrewKinematics::DoAutoCalibration(size_t numFactors, const RandomProbePointSet& probePoints, StringRef& reply)
+{
+ if (!SupportsAutoCalibration()) // should be checked by caller, but check it here too
+ {
+ return;
+ }
+
+ if (numFactors != numLeadscrews)
+ {
+ reply.printf("Error: Number of calibration factors (%u) not equal to number of leadscrews (%u)", numFactors, numLeadscrews);
+ }
+
+ const size_t numPoints = probePoints.NumberOfProbePoints();
+
+ // Build a Nx4 matrix of derivatives with respect to the leadscrew adjustments
+ // See the wxMaxima documents for the maths involved
+ FixedMatrix<floatc_t, MaxDeltaCalibrationPoints, MaxLeadscrews> derivativeMatrix;
+ floatc_t initialSumOfSquares = 0.0;
+ for (size_t i = 0; i < numPoints; ++i)
+ {
+ float x, y;
+ const floatc_t zp = reprap.GetMove().GetProbeCoordinates(i, x, y, false);
+ initialSumOfSquares += fcsquare(zp);
+
+ switch (numFactors)
+ {
+ case 2:
+ {
+ const floatc_t d2 = fcsquare(leadscrewX[1] - leadscrewX[0]) + fcsquare(leadscrewY[1] - leadscrewY[0]);
+ // There are lot of common subexpressions in the following, but the optimiser should find them
+ derivativeMatrix(i, 0) = (fcsquare(leadscrewY[1]) - leadscrewY[0] * leadscrewY[1] - y * (leadscrewY[1] - leadscrewY[0]) + fcsquare(leadscrewX[1]) - leadscrewX[0] * leadscrewX[1] - x * (leadscrewX[1] - leadscrewX[0]))/d2;
+ derivativeMatrix(i, 1) = (fcsquare(leadscrewY[0]) - leadscrewY[0] * leadscrewY[1] + y * (leadscrewY[1] - leadscrewY[0]) + fcsquare(leadscrewX[0]) - leadscrewX[0] * leadscrewX[1] + x * (leadscrewX[1] - leadscrewX[0]))/d2;
+ }
+ break;
+
+ case 3:
+ {
+ const floatc_t d2 = leadscrewX[1] * leadscrewY[2] - leadscrewX[0] * leadscrewY[2] - leadscrewX[2] * leadscrewY[1] + leadscrewX[0] * leadscrewY[1] + leadscrewX[2] * leadscrewY[0] - leadscrewX[1] * leadscrewY[0];
+ derivativeMatrix(i, 0) = (leadscrewX[1] * leadscrewY[2] - x * leadscrewY[2] - leadscrewX[2] * leadscrewY[1] + x * leadscrewY[1] + leadscrewX[2] * y - leadscrewX[1] * y)/d2;
+ derivativeMatrix(i, 1) = (leadscrewX[0] * leadscrewY[2] - x * leadscrewY[2] - leadscrewX[2] * leadscrewY[0] + x * leadscrewY[0] + leadscrewX[2] * y - leadscrewX[0] * y)/d2;
+ derivativeMatrix(i, 2) = (leadscrewX[0] * leadscrewY[1] - x * leadscrewY[1] - leadscrewX[1] * leadscrewY[0] + x * leadscrewY[0] + leadscrewX[1] * y - leadscrewX[0] * y)/d2;
+ }
+ break;
+
+ case 4:
+ {
+ // This one is horribly complicated. It may not work on the older Duets that use single-precision maths.
+ const float &x0 = leadscrewX[0], &x1 = leadscrewX[1], &x2 = leadscrewX[2], &x3 = leadscrewX[3];
+ const float &y0 = leadscrewY[0], &y1 = leadscrewY[1], &y2 = leadscrewY[2], &y3 = leadscrewY[3];
+ const floatc_t d2 = x1 * x3 * y2 * y3
+ - x0 * x3 * y2 * y3
+ - x1 * x2 * y2 * y3
+ + x0 * x2 * y2 * y3
+ - x2 * x3 * y1 * y3
+ + x0 * x3 * y1 * y3
+ + x1 * x2 * y1 * y3
+ - x0 * x1 * y1 * y3
+ + x2 * x3 * y0 * y3
+ - x1 * x3 * y0 * y3
+ - x0 * x2 * y0 * y3
+ + x0 * x1 * y0 * y3
+ + x2 * x3 * y1 * y2
+ - x1 * x3 * y1 * y2
+ - x0 * x2 * y1 * y2
+ + x0 * x1 * y1 * y2
+ - x2 * x3 * y0 * y2
+ + x0 * x3 * y0 * y2
+ + x1 * x2 * y0 * y2
+ - x0 * x1 * y0 * y2
+ + x1 * x3 * y0 * y1
+ - x0 * x3 * y0 * y1
+ - x1 * x2 * y0 * y1
+ + x0 * x2 * y0 * y1;
+ derivativeMatrix(i, 0) = (x1*x3*y2*y3-x*x3*y2*y3-x1*x2*y2*y3+x*x2*y2*y3-x2*x3*y1*y3+x*x3*y1*y3+x1*x2*y1*y3
+ -x*x1*y1*y3+x2*x3*y*y3-x1*x3*y*y3-x*x2*y*y3+x*x1*y*y3+x2*x3*y1*y2-x1*x3*y1*y2
+ -x*x2*y1*y2+x*x1*y1*y2-x2*x3*y*y2+x*x3*y*y2+x1*x2*y*y2-x*x1*y*y2+x1*x3*y*y1-x*x3*y*y1-x1*x2*y*y1+x*x2*y*y1)/d2;
+ derivativeMatrix(i, 1) = -(x0*x3*y2*y3-x*x3*y2*y3-x0*x2*y2*y3+x*x2*y2*y3-x2*x3*y0*y3+x*x3*y0*y3+x0*x2*y0*y3
+ -x*x0*y0*y3+x2*x3*y*y3-x0*x3*y*y3-x*x2*y*y3+x*x0*y*y3+x2*x3*y0*y2-x0*x3*y0*
+ y2-x*x2*y0*y2+x*x0*y0*y2-x2*x3*y*y2+x*x3*y*y2+x0*x2*y*y2-x*x0*y*y2+x0*x3*y*y0-x*x3*y*y0-x0*x2*y*y0+x*x2*y*y0)/d2;
+ derivativeMatrix(i, 2) = (x0*x3*y1*y3-x*x3*y1*y3-x0*x1*y1*y3+x*x1*y1*y3-x1*x3*y0*y3+x*x3*y0*y3+x0*x1*y0*y3-x*x0*y0*y3+x1*x3*y*y3-x0*x3*y*y3-x*x1*y*y3+x*x0*y*y3+x1*x3*y0*y1-x0*x3*y0*y1
+ -x*x1*y0*y1+x*x0*y0*y1-x1*x3*y*y1+x*x3*y*y1+x0*x1*y*y1-x*x0*y*y1+x0*x3*y*y0-x*x3*y*y0-x0*x1*y*y0+x*x1*y*y0)/d2;
+ derivativeMatrix(i, 3) = -(x0*x2*y1*y2-x*x2*y1*y2-x0*x1*y1*y2+x*x1*y1*y2-x1*x2*y0*y2+x*x2*y0*y2+x0*x1*y0*y2
+ -x*x0*y0*y2+x1*x2*y*y2-x0*x2*y*y2-x*x1*y*y2+x*x0*y*y2+x1*x2*y0*y1-x0*x2*y0*
+ y1-x*x1*y0*y1+x*x0*y0*y1-x1*x2*y*y1+x*x2*y*y1+x0*x1*y*y1-x*x0*y*y1+x0*x2*y*y0-x*x2*y*y0-x0*x1*y*y0+x*x1*y*y0)/d2;
+ }
+ break;
+ }
+ }
+
+ if (reprap.Debug(moduleMove))
+ {
+ PrintMatrix("Derivative matrix", derivativeMatrix, numPoints, numFactors);
+ }
+
+ // Now build the normal equations for least squares fitting
+ FixedMatrix<floatc_t, MaxLeadscrews, MaxLeadscrews + 1> normalMatrix;
+ for (size_t i = 0; i < numFactors; ++i)
+ {
+ for (size_t j = 0; j < numFactors; ++j)
+ {
+ floatc_t temp = derivativeMatrix(0, i) * derivativeMatrix(0, j);
+ for (size_t k = 1; k < numPoints; ++k)
+ {
+ temp += derivativeMatrix(k, i) * derivativeMatrix(k, j);
+ }
+ normalMatrix(i, j) = temp;
+ }
+ floatc_t temp = derivativeMatrix(0, i) * -(probePoints.GetZHeight(0));
+ for (size_t k = 1; k < numPoints; ++k)
+ {
+ temp += derivativeMatrix(k, i) * -(probePoints.GetZHeight(k));
+ }
+ normalMatrix(i, numFactors) = temp;
+ }
+
+ if (reprap.Debug(moduleMove))
+ {
+ PrintMatrix("Normal matrix", normalMatrix, numFactors, numFactors + 1);
+ }
+
+ floatc_t solution[MaxLeadscrews];
+ normalMatrix.GaussJordan(solution, numFactors);
+
+ if (reprap.Debug(moduleMove))
+ {
+ PrintMatrix("Solved matrix", normalMatrix, numFactors, numFactors + 1);
+ PrintVector("Solution", solution, numFactors);
+ }
+
+ // Calculate and display the residuals, also check for errors
+ floatc_t residuals[MaxDeltaCalibrationPoints];
+ floatc_t sumOfSquares = 0.0;
+ for (size_t i = 0; i < numPoints; ++i)
+ {
+ residuals[i] = probePoints.GetZHeight(i);
+ for (size_t j = 0; j < numFactors; ++j)
+ {
+ residuals[i] += solution[j] * derivativeMatrix(i, j);
+ }
+ sumOfSquares += fcsquare(residuals[i]);
+ }
+
+ if (reprap.Debug(moduleMove))
+ {
+ PrintVector("Residuals", residuals, numPoints);
+ }
+
+ // Check that the corrections are sensible
+ bool haveNaN = false, haveLargeCorrection = false;
+ for (size_t i = 0; i < numFactors; ++i)
+ {
+ if (std::isnan(solution[i]))
+ {
+ haveNaN = true;
+ }
+ else if (fabs(solution[i]) > maxCorrection)
+ {
+ haveLargeCorrection = true;
+ }
+ }
+
+ if (haveNaN)
+ {
+ reply.printf("Error: calibration failed, computed corrections:");
+ }
+ else if (haveLargeCorrection)
+ {
+ reply.printf("Error: computed corrections exceed 1mm:");
+ }
+ else
+ {
+ //TODO adjust the motors here
+ reply.printf("Simulated calibrating %d leadscrews using %d points, deviation before %.3f after %.3f, corrections:",
+ numFactors, numPoints, sqrt(initialSumOfSquares/numPoints), sqrtf(sumOfSquares/numPoints));
+ }
+
+ // Append the corrections to the reply in all cases
+ for (size_t i = 0; i < numFactors; ++i)
+ {
+ reply.catf(" %.3f", solution[i]);
+ }
+}
+
+#ifdef DUET_NG
+
+// Write any calibration data that we need to resume a print after power fail, returning true if successful
+bool ZLeadscrewKinematics::WriteResumeSettings(FileStore *f) const
+{
+ //TODO write leadscrew corrections, there is a chance that they will be the same as before
+ return true;
+}
+
+#endif
+// End
diff --git a/src/Movement/Kinematics/ZLeadscrewKinematics.h b/src/Movement/Kinematics/ZLeadscrewKinematics.h
new file mode 100644
index 00000000..59778b8f
--- /dev/null
+++ b/src/Movement/Kinematics/ZLeadscrewKinematics.h
@@ -0,0 +1,33 @@
+/*
+ * ZLeadscrewKinematics.h
+ *
+ * Created on: 8 Jul 2017
+ * Author: David
+ */
+
+#ifndef SRC_MOVEMENT_KINEMATICS_ZLEADSCREWKINEMATICS_H_
+#define SRC_MOVEMENT_KINEMATICS_ZLEADSCREWKINEMATICS_H_
+
+#include "Kinematics.h"
+
+class ZLeadscrewKinematics : public Kinematics
+{
+public:
+ ZLeadscrewKinematics(KinematicsType k);
+ bool Configure(unsigned int mCode, GCodeBuffer& gb, StringRef& reply, bool& error) override;
+ bool SupportsAutoCalibration() const override;
+ void DoAutoCalibration(size_t numFactors, const RandomProbePointSet& probePoints, StringRef& reply) override;
+
+#ifdef DUET_NG
+ bool WriteResumeSettings(FileStore *f) const override;
+#endif
+
+private:
+ static const unsigned int MaxLeadscrews = 4;
+
+ unsigned int numLeadscrews;
+ float leadscrewX[MaxLeadscrews], leadscrewY[MaxLeadscrews];
+ float maxCorrection;
+};
+
+#endif /* SRC_MOVEMENT_KINEMATICS_ZLEADSCREWKINEMATICS_H_ */
diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp
index 47f8fa9b..e94b9a3f 100644
--- a/src/Movement/Move.cpp
+++ b/src/Movement/Move.cpp
@@ -159,7 +159,7 @@ void Move::Spin()
const bool doMotorMapping = (nextMove.moveType == 0) || (nextMove.moveType == 1 && kinematics->GetHomingMode() == Kinematics::homeCartesianAxes);
if (doMotorMapping)
{
- AxisAndBedTransform(nextMove.coords, nextMove.xAxes, nextMove.moveType == 0);
+ AxisAndBedTransform(nextMove.coords, nextMove.xAxes, nextMove.yAxes, nextMove.moveType == 0);
}
if (ddaRingAddPointer->Init(nextMove, doMotorMapping))
{
@@ -371,7 +371,7 @@ bool Move::PausePrint(RestorePoint& rp)
rp.moveCoords[axis] = prevDda->GetEndCoordinate(axis, false);
}
- InverseAxisAndBedTransform(rp.moveCoords, prevDda->GetXAxes()); // we assume that xAxes hasn't changed between the moves
+ InverseAxisAndBedTransform(rp.moveCoords, prevDda->GetXAxes(), prevDda->GetYAxes()); // we assume that xAxes hasn't changed between the moves
const size_t numTotalAxes = reprap.GetGCodes().GetTotalAxes();
for (size_t drive = numTotalAxes; drive < DRIVES; ++drive)
@@ -482,7 +482,7 @@ void Move::SetNewPosition(const float positionNow[DRIVES], bool doBedCompensatio
{
float newPos[DRIVES];
memcpy(newPos, positionNow, sizeof(newPos)); // copy to local storage because Transform modifies it
- AxisAndBedTransform(newPos, reprap.GetCurrentXAxes(), doBedCompensation);
+ AxisAndBedTransform(newPos, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes(), doBedCompensation);
SetLiveCoordinates(newPos);
SetPositions(newPos);
}
@@ -544,18 +544,18 @@ bool Move::CartesianToMotorSteps(const float machinePos[MaxAxes], int32_t motorP
return b;
}
-void Move::AxisAndBedTransform(float xyzPoint[MaxAxes], uint32_t xAxes, bool useBedCompensation) const
+void Move::AxisAndBedTransform(float xyzPoint[MaxAxes], uint32_t xAxes, uint32_t yAxes, bool useBedCompensation) const
{
AxisTransform(xyzPoint);
if (useBedCompensation)
{
- BedTransform(xyzPoint, xAxes);
+ BedTransform(xyzPoint, xAxes, yAxes);
}
}
-void Move::InverseAxisAndBedTransform(float xyzPoint[MaxAxes], uint32_t xAxes) const
+void Move::InverseAxisAndBedTransform(float xyzPoint[MaxAxes], uint32_t xAxes, uint32_t yAxes) const
{
- InverseBedTransform(xyzPoint, xAxes);
+ InverseBedTransform(xyzPoint, xAxes, yAxes);
InverseAxisTransform(xyzPoint);
}
@@ -576,36 +576,43 @@ void Move::InverseAxisTransform(float xyzPoint[MaxAxes]) const
}
// Do the bed transform AFTER the axis transform
-void Move::BedTransform(float xyzPoint[MaxAxes], uint32_t xAxes) const
+void Move::BedTransform(float xyzPoint[MaxAxes], uint32_t xAxes, uint32_t yAxes) const
{
if (!useTaper || xyzPoint[Z_AXIS] < taperHeight)
{
float zCorrection = 0.0;
const size_t numAxes = reprap.GetGCodes().GetVisibleAxes();
- unsigned int numXAxes = 0;
+ unsigned int numCorrections = 0;
// Transform the Z coordinate based on the average correction for each axis used as an X axis.
// We are assuming that the tool Y offsets are small enough to be ignored.
- for (uint32_t axis = 0; axis < numAxes; ++axis)
+ for (uint32_t xAxis = 0; xAxis < numAxes; ++xAxis)
{
- if ((xAxes & (1u << axis)) != 0)
+ if ((xAxes & (1u << xAxis)) != 0)
{
- const float xCoord = xyzPoint[axis];
- if (usingMesh)
+ const float xCoord = xyzPoint[xAxis];
+ for (uint32_t yAxis = 0; yAxis < numAxes; ++yAxis)
{
- zCorrection += grid.GetInterpolatedHeightError(xCoord, xyzPoint[Y_AXIS]);
- }
- else
- {
- zCorrection += probePoints.GetInterpolatedHeightError(xCoord, xyzPoint[Y_AXIS]);
+ if ((yAxes & (1u << yAxis)) != 0)
+ {
+ const float yCoord = xyzPoint[yAxis];
+ if (usingMesh)
+ {
+ zCorrection += grid.GetInterpolatedHeightError(xCoord, yCoord);
+ }
+ else
+ {
+ zCorrection += probePoints.GetInterpolatedHeightError(xCoord, yCoord);
+ }
+ ++numCorrections;
+ }
}
- ++numXAxes;
}
}
- if (numXAxes > 1)
+ if (numCorrections > 1)
{
- zCorrection /= numXAxes; // take an average
+ zCorrection /= numCorrections; // take an average
}
xyzPoint[Z_AXIS] += (useTaper) ? (taperHeight - xyzPoint[Z_AXIS]) * recipTaperHeight * zCorrection : zCorrection;
@@ -613,35 +620,41 @@ void Move::BedTransform(float xyzPoint[MaxAxes], uint32_t xAxes) const
}
// Invert the bed transform BEFORE the axis transform
-void Move::InverseBedTransform(float xyzPoint[MaxAxes], uint32_t xAxes) const
+void Move::InverseBedTransform(float xyzPoint[MaxAxes], uint32_t xAxes, uint32_t yAxes) const
{
float zCorrection = 0.0;
const size_t numAxes = reprap.GetGCodes().GetVisibleAxes();
- unsigned int numXAxes = 0;
+ unsigned int numCorrections = 0;
// Transform the Z coordinate based on the average correction for each axis used as an X axis.
// We are assuming that the tool Y offsets are small enough to be ignored.
- for (uint32_t axis = 0; axis < numAxes; ++axis)
+ for (uint32_t xAxis = 0; xAxis < numAxes; ++xAxis)
{
- if ((xAxes & (1u << axis)) != 0)
+ if ((xAxes & (1u << xAxis)) != 0)
{
- const float xCoord = xyzPoint[axis];
- if (usingMesh)
+ const float xCoord = xyzPoint[xAxis];
+ for (uint32_t yAxis = 0; yAxis < numAxes; ++yAxis)
{
- zCorrection += grid.GetInterpolatedHeightError(xCoord, xyzPoint[Y_AXIS]);
- }
- else
- {
- zCorrection += probePoints.GetInterpolatedHeightError(xCoord, xyzPoint[Y_AXIS]);
-
+ if ((yAxes & (1u << yAxis)) != 0)
+ {
+ const float yCoord = xyzPoint[yAxis];
+ if (usingMesh)
+ {
+ zCorrection += grid.GetInterpolatedHeightError(xCoord, yCoord);
+ }
+ else
+ {
+ zCorrection += probePoints.GetInterpolatedHeightError(xCoord, yCoord);
+ }
+ ++numCorrections;
+ }
}
- ++numXAxes;
}
}
- if (numXAxes > 1)
+ if (numCorrections > 1)
{
- zCorrection /= numXAxes; // take an average
+ zCorrection /= numCorrections; // take an average
}
if (!useTaper || zCorrection >= taperHeight) // need check on zCorrection to avoid possible divide by zero
@@ -898,18 +911,18 @@ bool Move::IsExtruding() const
}
// Return the transformed machine coordinates
-void Move::GetCurrentUserPosition(float m[DRIVES], uint8_t moveType, uint32_t xAxes) const
+void Move::GetCurrentUserPosition(float m[DRIVES], uint8_t moveType, uint32_t xAxes, uint32_t yAxes) const
{
GetCurrentMachinePosition(m, moveType == 2 || (moveType == 1 && IsDeltaMode()));
if (moveType == 0)
{
- InverseAxisAndBedTransform(m, xAxes);
+ InverseAxisAndBedTransform(m, xAxes, yAxes);
}
}
// Return the current live XYZ and extruder coordinates
// Interrupts are assumed enabled on entry
-void Move::LiveCoordinates(float m[DRIVES], uint32_t xAxes)
+void Move::LiveCoordinates(float m[DRIVES], uint32_t xAxes, uint32_t yAxes)
{
// The live coordinates and live endpoints are modified by the ISR, so be careful to get a self-consistent set of them
const size_t numVisibleAxes = reprap.GetGCodes().GetVisibleAxes(); // do this before we disable interrupts
@@ -940,7 +953,7 @@ void Move::LiveCoordinates(float m[DRIVES], uint32_t xAxes)
}
cpu_irq_enable();
}
- InverseAxisAndBedTransform(m, xAxes);
+ InverseAxisAndBedTransform(m, xAxes, yAxes);
}
// These are the actual numbers that we want to be the coordinates, so don't transform them.
@@ -1009,12 +1022,24 @@ float Move::GetProbeCoordinates(int count, float& x, float& y, bool wantNozzlePo
void Move::Simulate(uint8_t simMode)
{
simulationMode = simMode;
- if (simMode)
+ if (simMode != 0)
{
simulationTime = 0.0;
}
}
+#ifdef DUET_NG
+
+// Write settings for resuming the print
+// The GCodes module deals with the head position so all we need worry about is the bed compensation
+// We don't handle random probe point bed compensation, and we assume that if a height map is being used it is the default one.
+bool Move::WriteResumeSettings(FileStore *f) const
+{
+ return kinematics->WriteResumeSettings(f) && (!usingMesh || f->Write("G29 S1\n"));
+}
+
+#endif
+
// For debugging
void Move::PrintCurrentDda() const
{
diff --git a/src/Movement/Move.h b/src/Movement/Move.h
index c822f575..a7d4f858 100644
--- a/src/Movement/Move.h
+++ b/src/Movement/Move.h
@@ -35,9 +35,9 @@ public:
void Exit(); // Shut down
void GetCurrentMachinePosition(float m[DRIVES], bool disableMotorMapping) const; // Get the current position in untransformed coords
- void GetCurrentUserPosition(float m[DRIVES], uint8_t moveType, uint32_t xAxes) const; // Return the position (after all queued moves have been executed) in transformed coords
+ void GetCurrentUserPosition(float m[DRIVES], uint8_t moveType, uint32_t xAxes, uint32_t yAxes) const; // Return the position (after all queued moves have been executed) in transformed coords
int32_t GetEndPoint(size_t drive) const { return liveEndPoints[drive]; } // Get the current position of a motor
- void LiveCoordinates(float m[DRIVES], uint32_t xAxes); // Gives the last point at the end of the last complete DDA transformed to user coords
+ void LiveCoordinates(float m[DRIVES], uint32_t xAxes, uint32_t yAxes); // Gives the last point at the end of the last complete DDA transformed to user coords
void Interrupt(); // The hardware's (i.e. platform's) interrupt should call this.
void InterruptTime(); // Test function - not used
bool AllMovesAreFinished(); // Is the look-ahead ring empty? Stops more moves being added as well.
@@ -55,8 +55,8 @@ public:
void SetAxisCompensation(int8_t axis, float tangent); // Set an axis-pair compensation angle
float AxisCompensation(int8_t axis) const; // The tangent value
void SetIdentityTransform(); // Cancel the bed equation; does not reset axis angle compensation
- void AxisAndBedTransform(float move[], uint32_t xAxes, bool useBedCompensation) const; // Take a position and apply the bed and the axis-angle compensations
- void InverseAxisAndBedTransform(float move[], uint32_t xAxes) const; // Go from a transformed point back to user coordinates
+ void AxisAndBedTransform(float move[], uint32_t xAxes, uint32_t yAxes, bool useBedCompensation) const; // Take a position and apply the bed and the axis-angle compensations
+ void InverseAxisAndBedTransform(float move[], uint32_t xAxes, uint32_t yAxes) const; // Go from a transformed point back to user coordinates
float GetTaperHeight() const { return (useTaper) ? taperHeight : 0.0; }
void SetTaperHeight(float h);
bool UseMesh(bool b); // Try to enable mesh bed compensation and report the final state
@@ -103,6 +103,10 @@ public:
const DDA *GetCurrentDDA() const { return currentDda; } // Return the DDA of the currently-executing move
+#ifdef DUET_NG
+ bool WriteResumeSettings(FileStore *f) const; // Write settings for resuming the print
+#endif
+
static int32_t MotorEndPointToMachine(size_t drive, float coord); // Convert a single motor position to number of steps
static float MotorEndpointToPosition(int32_t endpoint, size_t drive); // Convert number of motor steps to motor position
@@ -110,8 +114,8 @@ private:
enum class IdleState : uint8_t { idle, busy, timing };
bool StartNextMove(uint32_t startTime); // start the next move, returning true if Step() needs to be called immediately
- void BedTransform(float move[MaxAxes], uint32_t xAxes) const; // Take a position and apply the bed compensations
- void InverseBedTransform(float move[MaxAxes], uint32_t xAxes) const; // Go from a bed-transformed point back to user coordinates
+ void BedTransform(float move[MaxAxes], uint32_t xAxes, uint32_t yAxes) const; // Take a position and apply the bed compensations
+ void InverseBedTransform(float move[MaxAxes], uint32_t xAxes, uint32_t yAxes) const; // Go from a bed-transformed point back to user coordinates
void AxisTransform(float move[MaxAxes]) const; // Take a position and apply the axis-angle compensations
void InverseAxisTransform(float move[MaxAxes]) const; // Go from an axis transformed point back to user coordinates
void JustHomed(size_t axis, float hitPoint, DDA* hitDDA); // Deal with setting positions after a drive has been homed
diff --git a/src/Platform.cpp b/src/Platform.cpp
index 62dd6db7..5211346f 100644
--- a/src/Platform.cpp
+++ b/src/Platform.cpp
@@ -46,10 +46,22 @@ extern char _end;
extern "C" char *sbrk(int i);
#ifdef DUET_NG
-const uint16_t driverPowerOnAdcReading = (uint16_t)(4096 * 10.0/PowerFailVoltageRange); // minimum voltage at which we initialise the drivers
-const uint16_t driverPowerOffAdcReading = (uint16_t)(4096 * 9.5/PowerFailVoltageRange); // voltages below this flag the drivers as unusable
-const uint16_t driverOverVoltageAdcReading = (uint16_t)(4096 * 29.0/PowerFailVoltageRange); // voltages above this cause driver shutdown
-const uint16_t driverNormalVoltageAdcReading = (uint16_t)(4096 * 27.5/PowerFailVoltageRange); // voltages at or below this are normal
+
+inline constexpr float AdcReadingToPowerVoltage(uint16_t adcVal)
+{
+ return adcVal * (PowerMonitorVoltageRange/4096.0);
+}
+
+inline constexpr uint16_t PowerVoltageToAdcReading(float voltage)
+{
+ return (uint16_t)(voltage * (4096.0/PowerMonitorVoltageRange));
+}
+
+constexpr uint16_t driverPowerOnAdcReading = PowerVoltageToAdcReading(10.0); // minimum voltage at which we initialise the drivers
+constexpr uint16_t driverPowerOffAdcReading = PowerVoltageToAdcReading(9.5); // voltages below this flag the drivers as unusable
+constexpr uint16_t driverOverVoltageAdcReading = PowerVoltageToAdcReading(29.0); // voltages above this cause driver shutdown
+constexpr uint16_t driverNormalVoltageAdcReading = PowerVoltageToAdcReading(27.5); // voltages at or below this are normal
+
#endif
const uint8_t memPattern = 0xA5;
@@ -430,7 +442,7 @@ void Platform::Init()
endStopLogicLevel[drive] = true; // assume all endstops use active high logic e.g. normally-closed switch to ground
}
- driveDriverBits[drive] = CalcDriverBitmap(drive);
+ driveDriverBits[drive] = driveDriverBits[drive + DRIVES] = CalcDriverBitmap(drive);
// Set up the control pins and endstops
pinMode(STEP_PINS[drive], OUTPUT_LOW);
@@ -495,9 +507,11 @@ void Platform::Init()
temperatureShutdownDrivers = temperatureWarningDrivers = shortToGroundDrivers = openLoadDrivers = 0;
onBoardDriversFanRunning = offBoardDriversFanRunning = false;
+ autoSaveEnabled = false;
+ autoSaveState = AutoSaveState::normal;
#endif
- // Allow extrusion ancilliary PWM to use FAN0 even if FAN0 has not been disabled, for backwards compatibility
+ // Allow extrusion ancillary PWM to use FAN0 even if FAN0 has not been disabled, for backwards compatibility
extrusionAncilliaryPwmValue = 0.0;
extrusionAncilliaryPwmFrequency = DefaultPinWritePwmFreq;
extrusionAncilliaryPwmLogicalPin = Fan0LogicalPin;
@@ -1405,10 +1419,60 @@ void Platform::Spin()
}
}
+#ifdef DUET_NG
+ // Check for auto-pause, shutdown or resume
+ if (autoSaveEnabled)
+ {
+ switch (autoSaveState)
+ {
+ case AutoSaveState::normal:
+ if (currentVin < autoPauseReading)
+ {
+ if (reprap.GetGCodes().AutoPause())
+ {
+ autoSaveState = AutoSaveState::autoPaused;
+ }
+ }
+ break;
+
+ case AutoSaveState::autoPaused:
+ if (currentVin < autoShutdownReading)
+ {
+ if (reprap.GetGCodes().AutoShutdown())
+ {
+ autoSaveState = AutoSaveState::autoShutdown;
+ }
+ }
+ else if (currentVin >= autoResumeReading)
+ {
+ if (reprap.GetGCodes().AutoResume())
+ {
+ autoSaveState = AutoSaveState::normal;
+ }
+ }
+ break;
+
+ case AutoSaveState::autoShutdown:
+ if (currentVin >= autoResumeReading)
+ {
+ if (reprap.GetGCodes().AutoResumeAfterShutdown())
+ {
+ autoSaveState = AutoSaveState::normal;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+#endif
+
ClassReport(longWait);
}
#ifdef DUET_NG
+
// Report driver status conditions that require attention.
// Sets 'reported' if we reported anything, else leaves 'reported' alone.
void Platform::ReportDrivers(uint16_t whichDrivers, const char* text, bool& reported)
@@ -1428,6 +1492,48 @@ void Platform::ReportDrivers(uint16_t whichDrivers, const char* text, bool& repo
reported = true;
}
}
+
+// Configure auto save on power fail
+void Platform::ConfigureAutoSave(GCodeBuffer& gb, StringRef& reply, bool& error)
+{
+ bool seen = false;
+ float autoSaveVoltages[3];
+ if (gb.TryGetFloatArray('S', 3, autoSaveVoltages, reply, seen))
+ {
+ error = true;
+ }
+ else if (seen)
+ {
+ autoSaveEnabled = (autoSaveVoltages[0] >= 5.0 && autoSaveVoltages[1] > autoSaveVoltages[0] && autoSaveVoltages[2] > autoSaveVoltages[1]);
+ if (autoSaveEnabled)
+ {
+ autoShutdownReading = PowerVoltageToAdcReading(autoSaveVoltages[0]);
+ autoPauseReading = PowerVoltageToAdcReading(autoSaveVoltages[1]);
+ autoResumeReading = PowerVoltageToAdcReading(autoSaveVoltages[2]);
+ }
+ }
+ else if (!autoSaveEnabled)
+ {
+ reply.copy("Auto save is disabled");
+ }
+ else
+ {
+ reply.printf(" Auto shutdown at %.1fV, save/pause at %.1fV, resume at %.1fV",
+ AdcReadingToPowerVoltage(autoShutdownReading), AdcReadingToPowerVoltage(autoPauseReading), AdcReadingToPowerVoltage(autoResumeReading));
+ }
+}
+
+// Save some resume information
+bool Platform::WriteFanSettings(FileStore *f) const
+{
+ bool ok = true;
+ for (size_t fanNum = 0; ok && fanNum < NUM_FANS; ++fanNum)
+ {
+ ok = fans[fanNum].WriteSettings(f, fanNum);
+ }
+ return ok;
+}
+
#endif
float Platform::AdcReadingToCpuTemperature(uint32_t adcVal) const
diff --git a/src/Platform.h b/src/Platform.h
index d1f76232..49d1aab1 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -395,8 +395,6 @@ public:
// Movement
void EmergencyStop();
- void SetPhysicalDrives(size_t drive, uint32_t physicalDrives);
- uint32_t GetPhysicalDrives(size_t drive) const;
void SetDirection(size_t drive, bool direction);
void SetDirectionValue(size_t driver, bool dVal);
bool GetDirectionValue(size_t driver) const;
@@ -565,6 +563,8 @@ public:
void GetPowerVoltages(float& minV, float& currV, float& maxV) const;
float GetTmcDriversTemperature(unsigned int board) const;
void DriverCoolingFansOn(uint32_t driverChannelsMonitored);
+ void ConfigureAutoSave(GCodeBuffer& gb, StringRef& reply, bool& error);
+ bool WriteFanSettings(FileStore *f) const; // Save some resume information
#endif
// User I/O and servo support
@@ -579,7 +579,6 @@ private:
float AdcReadingToCpuTemperature(uint32_t reading) const;
#ifdef DUET_NG
- static float AdcReadingToPowerVoltage(uint16_t reading);
void ReportDrivers(uint16_t whichDrivers, const char* text, bool& reported);
#endif
@@ -682,9 +681,9 @@ private:
float pressureAdvance[MaxExtruders];
float motorCurrents[DRIVES]; // the normal motor current for each stepper driver
float motorCurrentFraction[DRIVES]; // the percentages of normal motor current that each driver is set to
- AxisDriversConfig axisDrivers[MaxAxes]; // the driver numbers assigned to each axis
+ AxisDriversConfig axisDrivers[MaxAxes]; // the driver numbers assigned to each axis
uint8_t extruderDrivers[MaxExtruders]; // the driver number assigned to each extruder
- uint32_t driveDriverBits[DRIVES]; // the bitmap of driver port bits for each axis or extruder
+ uint32_t driveDriverBits[2 * DRIVES]; // the bitmap of driver port bits for each axis or extruder, followed by the raw versions
uint32_t slowDriverStepPulseClocks; // minimum high and low step pulse widths, in processor clocks
uint32_t slowDrivers; // bitmap of driver port bits that need extended step pulse timing
float idleCurrentFactor;
@@ -832,6 +831,16 @@ private:
bool offBoardDriversFanRunning; // true if a fan is running to cool the drivers on the DueX
uint32_t onBoardDriversFanStartMillis; // how many times we have suppressed a temperature warning
uint32_t offBoardDriversFanStartMillis; // how many times we have suppressed a temperature warning
+ uint16_t autoShutdownReading, autoPauseReading, autoResumeReading;
+ bool autoSaveEnabled;
+
+ enum class AutoSaveState : uint8_t
+ {
+ normal = 0,
+ autoPaused,
+ autoShutdown
+ };
+ AutoSaveState autoSaveState;
#endif
// RTC
@@ -1214,13 +1223,6 @@ inline OutputBuffer *Platform::GetAuxGCodeReply()
return temp;
}
-#ifdef DUET_NG
-inline float Platform::AdcReadingToPowerVoltage(uint16_t adcVal)
-{
- return adcVal * (PowerFailVoltageRange/4096.0);
-}
-#endif
-
// *** These next two functions must use the same bit assignments in the drivers bitmap ***
// The bitmaps are organised like this:
// Duet WiFi:
diff --git a/src/PortControl.cpp b/src/PortControl.cpp
index 9b9d8b34..78e89609 100644
--- a/src/PortControl.cpp
+++ b/src/PortControl.cpp
@@ -37,33 +37,36 @@ void PortControl::Exit()
void PortControl::Spin(bool full)
{
- cpu_irq_disable();
- const DDA * cdda = reprap.GetMove().GetCurrentDDA();
- if (cdda == nullptr)
+ if (numConfiguredPorts != 0)
{
- // Movement has stopped, so turn all ports off
- cpu_irq_enable();
- UpdatePorts(0);
- }
- else
- {
- const uint32_t now = Platform::GetInterruptClocks() + advanceClocks;
- uint32_t moveEndTime = cdda->GetMoveStartTime();
- DDA::DDAState st = cdda->GetState();
- do
+ cpu_irq_disable();
+ const DDA * cdda = reprap.GetMove().GetCurrentDDA();
+ if (cdda == nullptr)
+ {
+ // Movement has stopped, so turn all ports off
+ cpu_irq_enable();
+ UpdatePorts(0);
+ }
+ else
{
- moveEndTime += cdda->GetClocksNeeded();
- if ((int32_t)(moveEndTime - now) >= 0)
+ const uint32_t now = Platform::GetInterruptClocks() + advanceClocks;
+ uint32_t moveEndTime = cdda->GetMoveStartTime();
+ DDA::DDAState st = cdda->GetState();
+ do
{
- break;
- }
- cdda = cdda->GetNext();
- st = cdda->GetState();
- } while (st == DDA::executing || st == DDA::frozen);
- cpu_irq_enable();
+ moveEndTime += cdda->GetClocksNeeded();
+ if ((int32_t)(moveEndTime - now) >= 0)
+ {
+ break;
+ }
+ cdda = cdda->GetNext();
+ st = cdda->GetState();
+ } while (st == DDA::executing || st == DDA::frozen);
+ cpu_irq_enable();
- const IoBits_t bits = (st == DDA::executing || st == DDA::frozen || st == DDA::provisional) ? cdda->GetIoBits() : 0;
- UpdatePorts(bits);
+ const IoBits_t bits = (st == DDA::executing || st == DDA::frozen || st == DDA::provisional) ? cdda->GetIoBits() : 0;
+ UpdatePorts(bits);
+ }
}
}
diff --git a/src/PrintMonitor.cpp b/src/PrintMonitor.cpp
index 0c821d0e..4f5c6fae 100644
--- a/src/PrintMonitor.cpp
+++ b/src/PrintMonitor.cpp
@@ -116,7 +116,7 @@ void PrintMonitor::Spin()
else if (!gCodes.DoingFileMacro() && reprap.GetMove().IsExtruding())
{
float liveCoordinates[DRIVES];
- reprap.GetMove().LiveCoordinates(liveCoordinates, reprap.GetCurrentXAxes());
+ reprap.GetMove().LiveCoordinates(liveCoordinates, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes());
// See if we need to determine the first layer height (usually smaller than the nozzle diameter)
if (printingFileInfo.firstLayerHeight == 0.0)
diff --git a/src/PrintMonitor.h b/src/PrintMonitor.h
index 71c705da..0be4a68e 100644
--- a/src/PrintMonitor.h
+++ b/src/PrintMonitor.h
@@ -99,6 +99,10 @@ class PrintMonitor
float GetFirstLayerDuration() const;
float GetFirstLayerHeight() const;
+#ifdef DUET_NG
+ const char *GetPrintingFilename() const { return (isPrinting) ? filenameBeingPrinted : nullptr; }
+#endif
+
private:
Platform& platform;
GCodes& gCodes;
diff --git a/src/RepRap.cpp b/src/RepRap.cpp
index 146cab91..706f18d0 100644
--- a/src/RepRap.cpp
+++ b/src/RepRap.cpp
@@ -39,7 +39,7 @@ extern "C" void hsmciIdle()
// Do nothing more in the constructor; put what you want in RepRap:Init()
-RepRap::RepRap() : toolList(nullptr), currentTool(nullptr), lastWarningMillis(0), activeExtruders(0),
+RepRap::RepRap() : toolList(nullptr), currentTool(nullptr), lastStandbyTool(nullptr), lastWarningMillis(0), activeExtruders(0),
activeToolHeaters(0), ticksInSpinState(0), spinningModule(noModule), debug(0), stopped(false),
active(false), resetting(false), processingConfig(true), beepFrequency(0), beepDuration(0)
{
@@ -425,6 +425,7 @@ void RepRap::StandbyTool(int toolNumber)
if (tool != nullptr)
{
tool->Standby();
+ lastStandbyTool = tool;
if (currentTool == tool)
{
currentTool = nullptr;
@@ -554,7 +555,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
else
#endif
{
- move->LiveCoordinates(liveCoordinates, GetCurrentXAxes());
+ move->LiveCoordinates(liveCoordinates, GetCurrentXAxes(), GetCurrentYAxes());
}
if (currentTool != nullptr)
@@ -933,7 +934,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
}
}
- // Axis mapping. Currently we only map the X axis, but we return an array of arrays to allow for mapping other axes in future.
+ // Axis mapping
response->cat("],\"axisMap\":[[");
bool first = true;
for (size_t xi = 0; xi < MaxAxes; ++xi)
@@ -951,6 +952,23 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
response->catf("%u", xi);
}
}
+ response->cat("],[");
+ first = true;
+ for (size_t yi = 0; yi < MaxAxes; ++yi)
+ {
+ if ((tool->GetYAxisMap() & (1u << yi)) != 0)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ response->cat(",");
+ }
+ response->catf("%u", yi);
+ }
+ }
response->cat("]]");
// Filament (if any)
@@ -1246,7 +1264,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
// Send XYZ positions
const size_t numAxes = reprap.GetGCodes().GetVisibleAxes();
float liveCoordinates[DRIVES];
- reprap.GetMove().LiveCoordinates(liveCoordinates, GetCurrentXAxes());
+ reprap.GetMove().LiveCoordinates(liveCoordinates, GetCurrentXAxes(), GetCurrentYAxes());
const Tool* const currentTool = reprap.GetCurrentTool();
if (currentTool != nullptr)
{
@@ -1655,7 +1673,7 @@ unsigned int RepRap::GetProhibitedExtruderMovements(unsigned int extrusions, uns
return 0;
}
- Tool *tool = currentTool;
+ Tool * const tool = currentTool;
if (tool == nullptr)
{
// This should not happen, but if on tool is selected then don't allow any extruder movement
@@ -1709,6 +1727,38 @@ uint32_t RepRap::GetCurrentXAxes() const
return (currentTool == nullptr) ? DefaultXAxisMapping : currentTool->GetXAxisMap();
}
+// Get the current axes used as X axes
+uint32_t RepRap::GetCurrentYAxes() const
+{
+ return (currentTool == nullptr) ? DefaultYAxisMapping : currentTool->GetYAxisMap();
+}
+
+#ifdef DUET_NG
+
+// Save some resume information, returning true if successful
+// We assume that the tool configuration doesn't change, only the temperatures and the mix
+bool RepRap::WriteToolSettings(FileStore *f) const
+{
+ // First write the settings of all tools except the current one and the command to select them if they are on standby
+ bool ok = true;
+ for (const Tool *t = toolList; t != nullptr && ok; t = t->Next())
+ {
+ if (t != currentTool)
+ {
+ ok = t->WriteSettings(f);
+ }
+ }
+
+ // Finally write the setting of the active tool and the commands to select it
+ if (ok && currentTool != nullptr)
+ {
+ ok = currentTool->WriteSettings(f);
+ }
+ return ok;
+}
+
+#endif
+
// Helper function for diagnostic tests in Platform.cpp, to cause a deliberate divide-by-zero
/*static*/ uint32_t RepRap::DoDivide(uint32_t a, uint32_t b)
{
diff --git a/src/RepRap.h b/src/RepRap.h
index 64376104..8854d8be 100644
--- a/src/RepRap.h
+++ b/src/RepRap.h
@@ -59,10 +59,12 @@ public:
void SelectTool(int toolNumber);
void StandbyTool(int toolNumber);
Tool* GetCurrentTool() const;
+ Tool* GetLastStandbyTool() const { return lastStandbyTool; }
Tool* GetTool(int toolNumber) const;
Tool* GetCurrentOrDefaultTool() const;
- const Tool* GetFirstTool() const { return toolList; } // Return the lowest-numbered tool
+ const Tool* GetFirstTool() const { return toolList; } // Return the lowest-numbered tool
uint32_t GetCurrentXAxes() const; // Get the current axes used as X axes
+ uint32_t GetCurrentYAxes() const; // Get the current axes used as Y axes
void SetToolVariables(int toolNumber, const float* standbyTemperatures, const float* activeTemperatures);
bool IsHeaterAssignedToTool(int8_t heater) const;
unsigned int GetNumberOfContiguousTools() const;
@@ -103,6 +105,10 @@ public:
void SetAlert(const char *msg, const char *title, int mode, float timeout, bool showZControls);
void ClearAlert();
+#ifdef DUET_NG
+ bool WriteToolSettings(FileStore *f) const; // Save some resume information
+#endif
+
static void CopyParameterText(const char* src, char *dst, size_t length);
static uint32_t DoDivide(uint32_t a, uint32_t b); // helper function for diagnostic tests
static uint32_t ReadDword(const char* p); // helper function for diagnostic tests
@@ -127,6 +133,7 @@ private:
Tool* toolList; // the tool list is sorted in order of increasing tool number
Tool* currentTool;
+ Tool* lastStandbyTool;
uint32_t lastWarningMillis; // When we last sent a warning message for things that can happen very often
uint16_t activeExtruders;
diff --git a/src/Tools/Tool.cpp b/src/Tools/Tool.cpp
index d65bd898..2fdb334a 100644
--- a/src/Tools/Tool.cpp
+++ b/src/Tools/Tool.cpp
@@ -33,7 +33,7 @@
Tool * Tool::freelist = nullptr;
-/*static*/ Tool *Tool::Create(int toolNumber, const char *name, long d[], size_t dCount, long h[], size_t hCount, uint32_t xMap, uint32_t fanMap)
+/*static*/ Tool *Tool::Create(int toolNumber, const char *name, long d[], size_t dCount, long h[], size_t hCount, uint32_t xMap, uint32_t yMap, uint32_t fanMap)
{
const size_t numExtruders = reprap.GetGCodes().GetNumExtruders();
if (dCount > ARRAY_SIZE(Tool::drives))
@@ -93,10 +93,11 @@ Tool * Tool::freelist = nullptr;
t->myNumber = toolNumber;
SafeStrncpy(t->name, name, ToolNameLength);
t->next = nullptr;
- t->active = false;
+ t->state = ToolState::off;
t->driveCount = dCount;
t->heaterCount = hCount;
t->xMapping = xMap;
+ t->yMapping = yMap;
t->fanMapping = fanMap;
t->heaterFault = false;
t->mixing = false;
@@ -178,6 +179,17 @@ void Tool::Print(StringRef& reply)
}
}
+ reply.cat("; ymap:");
+ sep = ' ';
+ for (size_t yi = 0; yi < MaxAxes; ++yi)
+ {
+ if ((yMapping & (1u << yi)) != 0)
+ {
+ reply.catf("%c%c", sep, GCodes::axisLetters[yi]);
+ sep = ',';
+ }
+ }
+
reply.cat("; fans:");
sep = ' ';
for (size_t fi = 0; fi < NUM_FANS; ++fi)
@@ -189,7 +201,7 @@ void Tool::Print(StringRef& reply)
}
}
- reply.catf("; status: %s", active ? "selected" : "standby");
+ reply.catf("; status: %s", (state == ToolState::active) ? "selected" : (state == ToolState::standby) ? "standby" : "off");
}
float Tool::MaxFeedrate() const
@@ -296,7 +308,7 @@ bool Tool::AllHeatersAtHighTemperature(bool forExtrusion) const
void Tool::Activate(Tool* currentlyActive)
{
- if (!active)
+ if (state != ToolState::active)
{
if (currentlyActive != nullptr && currentlyActive != this)
{
@@ -308,20 +320,20 @@ void Tool::Activate(Tool* currentlyActive)
reprap.GetHeat().SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]);
reprap.GetHeat().Activate(heaters[heater]);
}
- active = true;
+ state = ToolState::active;
}
}
void Tool::Standby()
{
- if (active)
+ if (state != ToolState::standby)
{
for (size_t heater = 0; heater < heaterCount; heater++)
{
reprap.GetHeat().SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]);
reprap.GetHeat().Standby(heaters[heater]);
}
- active = false;
+ state = ToolState::standby;
}
}
@@ -349,7 +361,7 @@ void Tool::SetVariables(const float* standby, const float* active)
if (standby[heater] < temperatureLimit)
{
standbyTemperatures[heater] = standby[heater];
- if (currentTool == nullptr || currentTool == this)
+ if (currentTool == nullptr || currentTool == this || reprap.GetLastStandbyTool() == this)
{
reprap.GetHeat().SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]);
}
@@ -415,4 +427,59 @@ void Tool::DefineMix(const float m[])
}
}
+#ifdef DUET_NG
+
+// Write the tool's settings to file returning true if successful
+bool Tool::WriteSettings(FileStore *f) const
+{
+ char bufSpace[50];
+ StringRef buf(bufSpace, ARRAY_SIZE(bufSpace));
+
+ // Set up active and standby heater temperatures
+ bool ok = true;
+ if (heaterCount != 0)
+ {
+ buf.copy("G10 ");
+ char c = 'S';
+ for (size_t i = 0; i < heaterCount; ++i)
+ {
+ buf.catf("%c%d", c, (int)activeTemperatures[i]);
+ c = ':';
+ }
+ buf.cat(' ');
+ c = 'R';
+ for (size_t i = 0; i < heaterCount; ++i)
+ {
+ buf.catf("%c%d", c, (int)standbyTemperatures[i]);
+ c = ':';
+ }
+ buf.cat('\n');
+ ok = f->Write(buf.Pointer());
+ }
+
+ if (ok && mixing)
+ {
+ buf.printf("M567 P%d ", myNumber);
+ char c = 'E';
+ for (size_t i = 0; i < driveCount; ++i)
+ {
+ buf.catf("%c%.1f", c, mix[i]);
+ c = ':';
+ }
+ buf.catf("\nM568 P%d S1\n", myNumber);
+ ok = f->Write(buf.Pointer());
+ }
+
+ if (ok && state != ToolState::off)
+ {
+ // Select tool and set virtual extruder position
+ buf.printf("T%d P0\nG92 E%.3f\n", myNumber, virtualExtruderPosition);
+ ok = f->Write(buf.Pointer());
+ }
+
+ return ok;
+}
+
+#endif
+
// End
diff --git a/src/Tools/Tool.h b/src/Tools/Tool.h
index 7aa36b65..2bc72392 100644
--- a/src/Tools/Tool.h
+++ b/src/Tools/Tool.h
@@ -28,15 +28,16 @@ Licence: GPL
#include "RepRapFirmware.h"
-const size_t ToolNameLength = 32; // maximum allowed length for tool names
-const uint32_t DefaultXAxisMapping = 0x0001; // by default, X is mapped to X
+const size_t ToolNameLength = 32; // maximum allowed length for tool names
+const uint32_t DefaultXAxisMapping = 1u << X_AXIS; // by default, X is mapped to X
+const uint32_t DefaultYAxisMapping = 1u << Y_AXIS; // by default, Y is mapped to Y
class Filament;
class Tool
{
public:
- static Tool *Create(int toolNumber, const char *name, long d[], size_t dCount, long h[], size_t hCount, uint32_t xMap, uint32_t fanMap);
+ static Tool *Create(int toolNumber, const char *name, long d[], size_t dCount, long h[], size_t hCount, uint32_t xMap, uint32_t yMap, uint32_t fanMap);
static void Delete(Tool *t);
const float *GetOffset() const;
@@ -58,10 +59,15 @@ public:
float InstantDv() const;
void Print(StringRef& reply);
uint32_t GetXAxisMap() const { return xMapping; }
+ uint32_t GetYAxisMap() const { return yMapping; }
uint32_t GetFanMapping() const { return fanMapping; }
Filament *GetFilament() const { return filament; }
Tool *Next() const { return next; }
+#ifdef DUET_NG
+ bool WriteSettings(FileStore *f) const; // write the tool's settings to file
+#endif
+
float virtualExtruderPosition;
friend class RepRap;
@@ -91,13 +97,20 @@ private:
float standbyTemperatures[Heaters];
size_t heaterCount;
float offset[MaxAxes];
- uint32_t xMapping;
+ uint32_t xMapping, yMapping;
uint32_t fanMapping;
Filament *filament;
Tool* next;
+ enum class ToolState : uint8_t
+ {
+ off = 0,
+ active,
+ standby
+ };
+ ToolState state;
+
bool mixing;
- bool active;
bool heaterFault;
volatile bool displayColdExtrudeWarning;
};
diff --git a/src/Version.h b/src/Version.h
index cbb5afd1..aa80df63 100644
--- a/src/Version.h
+++ b/src/Version.h
@@ -9,11 +9,11 @@
#define SRC_VERSION_H_
#ifndef VERSION
-# define VERSION "1.19beta8"
+# define VERSION "1.19beta9"
#endif
#ifndef DATE
-# define DATE "2017-06-30"
+# define DATE "2017-07-10"
#endif
#define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman"