Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2017-07-11 00:03:29 +0300
committerDavid Crocker <dcrocker@eschertech.com>2017-07-11 00:03:44 +0300
commit68b457a29ae43fac1ef3f36b92177528664506e7 (patch)
tree040532f8c80bb886b6a023585bd634dec737ffe3
parentea0ea134efa034a3b8dd1d882619d1d4a8714f39 (diff)
Version 1.19beta9
New and changed features: - Experimental resume-after-power-failure support on Duet WiFi/Ethernet. See https://duet3d.com/wiki/Setting_up_to_resume_a_print_after_a_power_failure. - Partly implemented bed levelling using multiple independent Z motors (M671). See https://duet3d.com/wiki/Bed_levelling_using_multiple_independent_Z_motors. - If the G10 command is used to set the standby temperature of a tool that is on standby, the live temperature is adjusted accordingly - SCARA parameters configured using M669 now include X and Y bed origin offsets - Baby stepping is no longer cleared when you home the printer or probe the bed - The Y axis can now be mapped in a similar way to the X axis - When the WiFi module is in access point mode, M122 displays the number of connected clients (needs DuetWiFiServer 1.19beta9) - The M305 command now uses the S parameter to set the heater name instead of the H parameter - The meaning of the first M669 crosstalk parameter for a SCARA printer has changed. A zero value now means that the proximal motor does not affect the proximal-to-distal arm angle. - The CoreXY kinematics calculations have been changed to conform to the way they are defined in other firmwares and at CoreXY.com. See the important upgrade notes. The CoreXZ and CoreXYU kinematics have been changed similarly. - The maximum allowed target temperature for auto tuning now depends on the configured maximum temperature for that heater - The heater gain that provokes a warning when setting heater model parameters with M307 or after auto tuning now depends on the configured maximum temperature for that heater Bug fixes: - G2 and G3 arc movement commands didn't work when the X axis was mapped - On an IDEX machine there was unwanted movement of the new tool after a tool change occurred - Duet WiFiServer 1.19-beta9: fixed a bug that sometimes caused WiFi connection failures - On the Duet WiFi, if the own access point parameters were configurds but the list of remembered host access points was empty, the own access point name appeared in the remembered list displayed by M587 - On a SCARA mnachine, sending G92 X0 Y0 caused the Duet to return position data containing NaNs to Duet Web Control, which caused it to disconnect Upgrade notes: - **Important!** On a CoreXY machine, you need to either swap the X and Y motor connections, or set the Y axis factor to -1 in the M667 command. Similarly for CoreXZ and CoreXYU machines.
-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"