diff options
49 files changed, 1596 insertions, 446 deletions
@@ -219,7 +219,6 @@ <option id="gnu.cpp.compiler.option.preprocessor.def.1610427238" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols"> <listOptionValue builtIn="false" value="__SAM4E8E__"/> <listOptionValue builtIn="false" value="DUET_NG"/> - <listOptionValue builtIn="false" value="DUET_WIFI"/> <listOptionValue builtIn="false" value="_XOPEN_SOURCE"/> </option> <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.151249281" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/> @@ -768,7 +767,7 @@ </tool> <tool id="cdt.managedbuild.tool.gnu.cross.c.linker.1102044150" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/> <tool id="cdt.managedbuild.tool.gnu.cross.archiver.1402882499" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/> - <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LinkFlags1} "${workspace_loc}/${CoreName}/SAM4E8E/cores/arduino/syscalls.o" ${INPUTS} ${LinkFlags2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1768134875" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker"> + <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LinkFlags1} "${workspace_loc}/${CoreName}/SAM4E8E_RTOS/cores/arduino/syscalls.o" ${INPUTS} ${LinkFlags2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1768134875" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker"> <option id="gnu.cpp.link.option.nostdlibs.399544265" name="No startup or default libs (-nostdlib)" superClass="gnu.cpp.link.option.nostdlibs" useByScannerDiscovery="false" value="false" valueType="boolean"/> <option id="gnu.cpp.link.option.paths.605414111" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" useByScannerDiscovery="false" valueType="libPaths"> <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/SAM4E8E_RTOS/}""/> @@ -776,9 +775,9 @@ </option> <option id="gnu.cpp.link.option.libs.847860449" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" useByScannerDiscovery="false" valueType="libs"> <listOptionValue builtIn="false" value="${CoreName}"/> - <listOptionValue builtIn="false" value="FreeRtos"/> + <listOptionValue builtIn="false" value="FreeRTOS"/> </option> - <option id="gnu.cpp.link.option.flags.161856294" name="Linker flags" superClass="gnu.cpp.link.option.flags" useByScannerDiscovery="false" value="-Os --specs=nano.specs -u _printf_float -u _scanf_float -Wl,--gc-sections -Wl,--fatal-warnings -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -T${workspace_loc:/${CoreName}/variants/duetNG/linker_scripts/gcc/flash.ld} -Wl,-Map,${workspace_loc:/${ProjName}/${ConfigName}}/${BuildArtifactFileBaseName}.map" valueType="string"/> + <option id="gnu.cpp.link.option.flags.161856294" name="Linker flags" superClass="gnu.cpp.link.option.flags" useByScannerDiscovery="false" value="-Os --specs=nano.specs -Wl,--gc-sections -Wl,--fatal-warnings -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -T${workspace_loc:/${CoreName}/variants/duetNG/linker_scripts/gcc/flash.ld} -Wl,-Map,${workspace_loc:/${ProjName}/${ConfigName}}/${BuildArtifactFileBaseName}.map" valueType="string"/> <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1270956612" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input"> <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/> <additionalInput kind="additionalinput" paths="$(LIBS)"/> @@ -818,7 +817,6 @@ <listOptionValue builtIn="false" value="__SAM4E8E__"/> <listOptionValue builtIn="false" value="RTOS"/> <listOptionValue builtIn="false" value="DUET_NG"/> - <listOptionValue builtIn="false" value="DUET_WIFI"/> <listOptionValue builtIn="false" value="_XOPEN_SOURCE"/> </option> <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.65828751" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/> @@ -832,16 +830,138 @@ </storageModule> <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/> </cconfiguration> + <cconfiguration id="cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622.649587786"> + <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622.649587786" moduleId="org.eclipse.cdt.core.settings" name="DuetM_RTOS"> + <macros> + <stringMacro name="LinkFlags2" type="VALUE_TEXT" value="-Wl,--end-group -lm"/> + <stringMacro name="LinkFlags1" type="VALUE_TEXT" value="-mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--warn-unresolved-symbols -Wl,--start-group"/> + <stringMacro name="CoreName" type="VALUE_TEXT" value="CoreNG"/> + <stringMacro name="GccPath" type="VALUE_TEXT" value="C:\Program Files (x86)\GNU Tools ARM Embedded\6 2017-q2-update\bin"/> + </macros> + <externalSettings/> + <extensions> + <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/> + <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + </extensions> + </storageModule> + <storageModule moduleId="cdtBuildSystem" version="4.0.0"> + <configuration artifactExtension="elf" artifactName="DuetMaestroFirmware" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622.649587786" name="DuetM_RTOS" parent="cdt.managedbuild.config.gnu.cross.exe.release" postannouncebuildStep="Generating binary file" postbuildStep="arm-none-eabi-objcopy -O binary ${workspace_loc:/${ProjName}/${ConfigName}}/DuetMaestroFirmware.elf ${workspace_loc:/${ProjName}/${ConfigName}}/DuetMaestroFirmware.bin"> + <folderInfo id="cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622.649587786." name="/" resourcePath=""> + <toolChain id="cdt.managedbuild.toolchain.gnu.cross.exe.release.1494327638" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.exe.release"> + <option id="cdt.managedbuild.option.gnu.cross.path.1101985181" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path" useByScannerDiscovery="false" value="${GccPath}" valueType="string"/> + <option id="cdt.managedbuild.option.gnu.cross.prefix.455634880" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix" useByScannerDiscovery="false" value="arm-none-eabi-" valueType="string"/> + <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.1648156947" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/> + <builder buildPath="${workspace_loc:/RepRapFirmware}/Release" id="cdt.managedbuild.builder.gnu.cross.1787405692" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.builder.gnu.cross"/> + <tool id="cdt.managedbuild.tool.gnu.cross.assembler.1187253546" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler"> + <inputType id="cdt.managedbuild.tool.gnu.assembler.input.456980885" superClass="cdt.managedbuild.tool.gnu.assembler.input"/> + </tool> + <tool commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}" id="cdt.managedbuild.tool.gnu.cross.c.compiler.1037043643" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler"> + <option defaultValue="gnu.c.optimization.level.most" id="gnu.c.compiler.option.optimization.level.293020747" name="Optimization Level" superClass="gnu.c.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.c.optimization.level.more" valueType="enumerated"/> + <option id="gnu.c.compiler.option.debugging.level.1647722987" name="Debug Level" superClass="gnu.c.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.c.debugging.level.none" valueType="enumerated"/> + <option id="gnu.c.compiler.option.misc.verbose.412653021" name="Verbose (-v)" superClass="gnu.c.compiler.option.misc.verbose" useByScannerDiscovery="false" value="false" valueType="boolean"/> + <option id="gnu.c.compiler.option.misc.other.1084863157" name="Other flags" superClass="gnu.c.compiler.option.misc.other" useByScannerDiscovery="false" value="-c -std=gnu99 -mcpu=cortex-m4 -mthumb -ffunction-sections -fdata-sections -nostdlib -Wdouble-promotion -fsingle-precision-constant "-Wa,-ahl=$*.s"" valueType="string"/> + <option id="gnu.c.compiler.option.include.paths.275997920" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath"> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/cores/arduino}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/libraries/Storage}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/common/utils}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/common/services/ioport}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/drivers}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/utils}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/utils/cmsis/sam4s/include}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/utils/header_files}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/utils/preprocessor}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/thirdparty/CMSIS/Include}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/variants/sam4s}""/> + </option> + <option id="gnu.c.compiler.option.preprocessor.def.symbols.833298868" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols"> + <listOptionValue builtIn="false" value="__SAM4S8C__"/> + <listOptionValue builtIn="false" value="RTOS"/> + <listOptionValue builtIn="false" value="DUET_M"/> + </option> + <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.667707888" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/> + </tool> + <tool id="cdt.managedbuild.tool.gnu.cross.c.linker.411798791" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/> + <tool id="cdt.managedbuild.tool.gnu.cross.archiver.2116733751" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/> + <tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LinkFlags1} "${workspace_loc}/${CoreName}/SAM4S_RTOS/cores/arduino/syscalls.o" ${INPUTS} ${LinkFlags2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1011842834" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker"> + <option id="gnu.cpp.link.option.nostdlibs.91014132" name="No startup or default libs (-nostdlib)" superClass="gnu.cpp.link.option.nostdlibs" useByScannerDiscovery="false" value="false" valueType="boolean"/> + <option id="gnu.cpp.link.option.paths.2085138159" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" useByScannerDiscovery="false" valueType="libPaths"> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/SAM4S_RTOS/}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/FreeRTOS/SAM4S}""/> + </option> + <option id="gnu.cpp.link.option.libs.214117190" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" useByScannerDiscovery="false" valueType="libs"> + <listOptionValue builtIn="false" value="${CoreName}"/> + <listOptionValue builtIn="false" value="FreeRTOS"/> + </option> + <option id="gnu.cpp.link.option.flags.1077151865" name="Linker flags" superClass="gnu.cpp.link.option.flags" useByScannerDiscovery="false" value="-Os --specs=nano.specs -Wl,--gc-sections -Wl,--fatal-warnings -mcpu=cortex-m4 -T${workspace_loc:/${CoreName}/variants/sam4s/linker_scripts/gcc/flash.ld} -Wl,-Map,${workspace_loc:/${ProjName}/${ConfigName}}/${BuildArtifactFileBaseName}.map" valueType="string"/> + <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.480654714" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input"> + <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/> + <additionalInput kind="additionalinput" paths="$(LIBS)"/> + </inputType> + </tool> + <tool command="g++" id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.1849650984" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler"> + <option id="gnu.cpp.compiler.option.optimization.level.1017091230" name="Optimization Level" superClass="gnu.cpp.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.more" valueType="enumerated"/> + <option id="gnu.cpp.compiler.option.debugging.level.326047343" name="Debug Level" superClass="gnu.cpp.compiler.option.debugging.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/> + <option id="gnu.cpp.compiler.option.other.verbose.1316817271" name="Verbose (-v)" superClass="gnu.cpp.compiler.option.other.verbose" useByScannerDiscovery="false" value="false" valueType="boolean"/> + <option id="gnu.cpp.compiler.option.other.other.434744183" name="Other flags" superClass="gnu.cpp.compiler.option.other.other" useByScannerDiscovery="false" value="-c -std=gnu++14 -mcpu=cortex-m4 -mthumb -ffunction-sections -fdata-sections -fno-threadsafe-statics -fno-rtti -fno-exceptions -nostdlib -Wdouble-promotion -fsingle-precision-constant "-Wa,-ahl=$*.s"" valueType="string"/> + <option id="gnu.cpp.compiler.option.include.paths.1102345734" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath"> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/cores/arduino}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/libraries/Flash}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/libraries/SharedSpi}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/libraries/Storage}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/libraries/Wire}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/common/utils}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/common/services/clock}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/common/services/ioport}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/drivers}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/services/flash_efc}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/utils}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/utils/cmsis/sam4s/include}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/utils/header_files}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/sam/utils/preprocessor}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/asf/thirdparty/CMSIS/Include}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${CoreName}/variants/sam4s}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/src}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/src/DuetM}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/src/Networking}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/DuetWiFiSocketServer/src/include}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/FreeRTOS/src/include}""/> + <listOptionValue builtIn="false" value=""${workspace_loc:/FreeRTOS/src/portable/GCC/ARM_CM3}""/> + </option> + <option id="gnu.cpp.compiler.option.preprocessor.def.1832014133" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols"> + <listOptionValue builtIn="false" value="__SAM4S8C__"/> + <listOptionValue builtIn="false" value="RTOS"/> + <listOptionValue builtIn="false" value="DUET_M"/> + <listOptionValue builtIn="false" value="_XOPEN_SOURCE"/> + </option> + <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1979789995" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/> + </tool> + </toolChain> + </folderInfo> + <sourceEntries> + <entry excluding="src/DuetNG|src/Networking/ESP8266WiFi|src/Networking/LwipEthernet|src/Alligator|src/SAME70_TEST|src/Duet|src/RADDS" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> + </sourceEntries> + </configuration> + </storageModule> + <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/> + </cconfiguration> </storageModule> <storageModule moduleId="cdtBuildSystem" version="4.0.0"> <project id="RepRapFirmware.cdt.managedbuild.target.gnu.cross.exe.1494358155" name="Executable" projectType="cdt.managedbuild.target.gnu.cross.exe"/> </storageModule> <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/> <storageModule moduleId="refreshScope" versionNumber="2"> + <configuration configurationName="DuetM_RTOS"/> <configuration configurationName="Alligator"/> <configuration configurationName="SAME70"/> - <configuration configurationName="RADDS"/> <configuration configurationName="Duet2"/> + <configuration configurationName="RADDS"/> + <configuration configurationName="Duet2_RTOS"/> <configuration configurationName="DuetM"/> <configuration configurationName="Duet085"/> </storageModule> diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml index c7d9cc75..da7dcfe5 100644 --- a/.settings/language.settings.xml +++ b/.settings/language.settings.xml @@ -77,4 +77,15 @@ </provider> </extension> </configuration> + <configuration id="cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.241502451.170574622.649587786" name="DuetM_RTOS"> + <extension point="org.eclipse.cdt.core.LanguageSettingsProvider"> + <provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/> + <provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/> + <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/> + <provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="1189573167678144698" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true"> + <language-scope id="org.eclipse.cdt.core.gcc"/> + <language-scope id="org.eclipse.cdt.core.g++"/> + </provider> + </extension> + </configuration> </project> diff --git a/src/BugList.txt b/src/BugList.txt index 9b77d7cc..fdcfa1d3 100644 --- a/src/BugList.txt +++ b/src/BugList.txt @@ -97,11 +97,15 @@ Done in 1.21 release: - [done] configuration and readback of pulse-type filament monitors is wrong - [done] Terminate WiFi sockets that abort - [done similar, ok] PR to report actual fan speed -- DWC Machine properties doesn't show correct state of active low endstops -- Use machine coordinates when runninng system macros +- [done] DWC Machine properties doesn't show correct state of active low endstops +- [done] Use machine coordinates when runninng system macros +- [done] Axis limits on arc moves Investigations: -SCARA https://www.duet3d.com/forum/thread.php?pid=43636#p43636 +[awaiting reply] SCARA https://www.duet3d.com/forum/thread.php?pid=43636#p43636 +[can't reproduce - printing over USB?] Jerky printing https://forum.duet3d.com/topic/4502/jerk-accel-settings-not-working-for-me/38 +BLTouch deploying an extra time, https://www.duet3d.com/forum/thread.php?pid=44891#p44891 +Printer stall, https://forum.duet3d.com/topic/4862/duet-wifi-printer-stalls-stops-no-errors/4 Remaining: - [done in 2.0] BUG: G1 E5 S1 on delta says "Error: G0/G1: attempt to move delta motors to absolute positions" @@ -110,12 +114,17 @@ Remaining: - [done in 2.0, test] M114 to report user coordinates first, https://www.duet3d.com/forum/thread.php?pid=43073#p43073 - [done in 2.0, test] Allow Z jerk down to 0.2mm/sec - [done in 2.0, ok] BUG: no call to HttpResponder::CheckSessions -- BUG: G29 height map is way below bed. Happened on Ormerod. When probing the bed with simple G30 and compensation is applied, correct for the height map offset -- Motor stall detection Z probe. Run deploy/retract macros before/after each probe, as for bltouch, or not? +- [done in 2.0] Update user coordinates after using G10 to change offsets, https://www.duet3d.com/forum/thread.php?pid=44900#p44900 +- [done in 2.0, test] G2/G3: allow X and Y to be not specified, and I or J not to be specified, see https://www.duet3d.com/forum/thread.php?pid=44915#p44915 +- [done in 2.0, test] BUG: G29 height map is way below bed. Happened on Ormerod. When probing the bed with simple G30 and compensation is applied, correct for the height map offset +- [done in 2.0] Motor stall detection Z probe. Run deploy/retract macros before/after each probe, as for bltouch, or not? +- [done in 2.0] Simulation to assume machine starts homed, restore homed status at end +- [done in 2.0] more changes from chrishamm (multiple spindles, fan map) +- [no fault] Report workspace coordinates to 2dp not 1dp to DWC (https://www.duet3d.com/forum/thread.php?pid=43789#p43789) +- [done in 2.0] G0 not to use G1 F parameter, move at machine limits instead (CNC, https://www.duet3d.com/forum/thread.php?pid=43825#p43825) + - Unexpected heaters off/tool selection behaviour, https://www.duet3d.com/forum/thread.php?pid=43059#p43059 - warn when using : where ; was probably meant -- check out G30 H parameter, https://www.duet3d.com/forum/thread.php?pid=42322#p42322 -- another chrishamm change, in his branch - Error message if you attempt movement with VIN < minimum - min/max RSSI display? - save theta, phi in move and then in DDA? @@ -125,19 +134,17 @@ Remaining: - Return "Powered down" status when VIN power has never been seen or has gone down after sending M81 - Auto mount main SD card when inserted - Workplace offsets are supposed to be persistant (check NIST), https://www.duet3d.com/forum/thread.php?pid=43755#p43755 -- [no fault] Report workspace coordinates to 2dp not 1dp to DWC (https://www.duet3d.com/forum/thread.php?pid=43789#p43789) - At the end of a simulation, restore the original workplace coordinate selection -- Simulation to assume machine starts homed, restore homed status at end -- G0 not to use G1 F parameter, move at machine limits instead (CNC, https://www.duet3d.com/forum/thread.php?pid=43825#p43825) - apostrophe in quoted filename - looks like we get a Pop underflow message when you send DWC jog commands and axes are not homed - Add warning message when print exceeds bounds - Does bltouch need a default recovery time? - Should bltouch use digital probe mode? Some users having problems with P25 in G31 command. +- M584 when assigning a drive, unmap any existing assignment. Also allow an axis to be mapped to driver -1. +- Add S4 option to G1 command, like S1 but no endstop checks (needed for CoreXY, CoreXZ) -- How should we lift X on a CoreXZ printer before homing? - if a homing command in an SD print file is aborted due to e.g. G1 Z5 in the homing file, error message should be written to both DWC and PanelDue -- stall detect on Z axis +- [no fault] stall detect on Z axis - Files generated by Cura doesn't detect layer changes, see https://www.duet3d.com/forum/thread.php?pid=40865#p40865 - [re-test using new DuetWiFiServer] "Failed to change mode" messages after M552 S2/S0/S1 cycle - [can't reproduce] "Attempt to seek on a non-open file", https://www.duet3d.com/forum/thread.php?pid=41175#p41175 @@ -160,21 +167,20 @@ Remaining: - Add timeout to hsmci_send_cmd_execute, see https://www.duet3d.com/forum/thread.php?pid=35654#p35654 - Bug: pressure advance attempts high speed or acceleration extruder movements on bench setup (was it caused by hitting limits?) - Bug: https://www.duet3d.com/forum/thread.php?pid=34772#p34772 (needs RRF fix too?) -- M40 to run eject.g [don't do, not NIST standard] +- M40 to run eject.g [don't do, not NIST standard?] - configurable minimum extrusion temperature (per extruder?) - case-insensitive http headers -Remaining: - Send reduce power command to PanelDue when main power turned off? - Document multiple bed and chamber heaters -- Check all classes for correct initialisation +- [mostly done] Check all classes for correct initialisation - sd_mmc_spi doesn't acquire/release the SPI bus. Need to change this for RTOS. - If wifi module gets stuck in starting state, reset it again +- If wifi disconnects when in client mode, keep retrying the connection Bug investigations: - [done] step errors, https://www.duet3d.com/forum/thread.php?pid=33741#p33741 -- SeeMeCNC scaling problem, see email of 2018-04-01 -- dual Z motors, https://www.duet3d.com/forum/thread.php?pid=44396#p44396 +- [no fault, possibly related to low extruder steps/mm] SeeMeCNC scaling problem, see email of 2018-04-01 Investigations: @@ -201,8 +207,8 @@ Later versions: - Implement G1 S4? (like S2 but always relative) - Add work coordinates? https://www.duet3d.com/forum/thread.php?pid=27128#p27128 - Don't do pressure advance during accel/decel of sequences of short segments -- Axis limits on arc moves - Arc move with same finish and start coordinates to do complete circle - Add T parameter to M207 - look at supporting SIZE in FTP - Make mp.delta.hmz0sK, hmz0scK and dsk 64-bit in SAM4E versions, to increase movement limit - also increase K2? + diff --git a/src/Configuration.h b/src/Configuration.h index 89d1555d..4de9d976 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -228,6 +228,7 @@ constexpr float FILAMENT_WIDTH = 1.75; // Millimetres constexpr unsigned int MaxStackDepth = 5; // Maximum depth of stack // CNC and laser support +constexpr size_t MaxSpindles = 4; // Maximum number of configurable spindles constexpr float DefaultMaxSpindleRpm = 10000; // Default spindle RPM at full PWM constexpr float DefaultMaxLaserPower = 255.0; // Power setting in M3 command for full power diff --git a/src/Display/ST7920/lcd7920.cpp b/src/Display/ST7920/lcd7920.cpp index 08efe23f..da560b22 100644 --- a/src/Display/ST7920/lcd7920.cpp +++ b/src/Display/ST7920/lcd7920.cpp @@ -3,6 +3,7 @@ #include "RepRapFirmware.h" #include "Pins.h" +#include "Tasks.h" #if SUPPORT_12864_LCD @@ -537,7 +538,7 @@ void Lcd7920::sendLcdData(uint8_t data) // Send a command to the lcd. Data1 is sent as-is, data2 is split into 2 bytes, high nibble first. void Lcd7920::sendLcd(uint8_t data1, uint8_t data2) { - sspi_acquire(); // TODO when using RTOS, wait for shared SPI to be available + MutexLocker lock(Tasks::GetSpiMutex()); sspi_master_setup_device(&device); delayMicroseconds(1); sspi_select_device(&device); @@ -550,7 +551,6 @@ void Lcd7920::sendLcd(uint8_t data1, uint8_t data2) delayMicroseconds(1); sspi_deselect_device(&device); delayMicroseconds(1); - sspi_release(); } void Lcd7920::ensureBasicMode() diff --git a/src/DuetM/Pins_DuetM.h b/src/DuetM/Pins_DuetM.h index 0f5cd8ae..23c7dec0 100644 --- a/src/DuetM/Pins_DuetM.h +++ b/src/DuetM/Pins_DuetM.h @@ -112,6 +112,7 @@ constexpr float PowerMonitorVoltageRange = 11.0 * 3.3; // We use an 11:1 vo // Digital pin number to turn the IR LED on (high) or off (low), also controls the DIAG LED constexpr Pin Z_PROBE_MOD_PIN = 62; +constexpr Pin DiagPin = Z_PROBE_MOD_PIN; // Cooling fans constexpr size_t NUM_FANS = 3; diff --git a/src/Fan.cpp b/src/Fan.cpp index 95f26850..c472456f 100644 --- a/src/Fan.cpp +++ b/src/Fan.cpp @@ -13,6 +13,7 @@ void Fan::Init(Pin p_pin, bool hwInverted) { + isConfigured = false; val = lastVal = 0.0; minVal = 0.1; // 10% minimum fan speed blipTime = 100; // 100ms fan blip @@ -129,6 +130,7 @@ bool Fan::Configure(unsigned int mcode, int fanNum, GCodeBuffer& gb, const Strin if (seen) { + isConfigured = true; Refresh(); } else if (!gb.Seen('R') && !gb.Seen('S')) @@ -23,12 +23,14 @@ public: // If errors were discovered while processing parameters, put an appropriate error message in 'reply' and set 'error' to true. // If no relevant parameters are found, print the existing ones to 'reply' and return false. bool Configure(unsigned int mcode, int fanNum, GCodeBuffer& gb, const StringRef& reply, bool& error); + bool IsConfigured() const { return isConfigured && IsEnabled(); } bool IsEnabled() const { return pin != NoPin; } float GetConfiguredPwm() const { return val; } // returns the configured PWM. Actual PWM may be different, e.g. due to blipping or for thermostatic fans. void Init(Pin p_pin, bool hwInverted); void SetPwm(float speed); + bool HasMonitoredHeaters() const { return heatersMonitored != 0; } void SetHeatersMonitored(HeatersMonitoredBitmap h); bool Check(); // update the fan PWM returning true if it is a thermostatic fan that is on void Disable(); @@ -46,6 +48,7 @@ private: HeatersMonitoredBitmap heatersMonitored; PwmFrequency freq; Pin pin; + bool isConfigured; bool inverted; bool hardwareInverted; bool blipping; diff --git a/src/GCodes/GCodeBuffer.cpp b/src/GCodes/GCodeBuffer.cpp index 8877a83a..afdcae81 100644 --- a/src/GCodes/GCodeBuffer.cpp +++ b/src/GCodes/GCodeBuffer.cpp @@ -437,7 +437,7 @@ float GCodeBuffer::GetFValue() { if (readPointer >= 0) { - const float result = strtof(&gcodeBuffer[readPointer + 1], 0); + const float result = SafeStrtof(&gcodeBuffer[readPointer + 1], 0); readPointer = -1; return result; } @@ -453,8 +453,8 @@ const void GCodeBuffer::GetFloatArray(float arr[], size_t& returnedLength, bool if (readPointer >= 0) { size_t length = 0; - bool inList = true; - while (inList) + const char *p = gcodeBuffer + readPointer + 1; + for (;;) { if (length >= returnedLength) // array limit has been set in here { @@ -463,16 +463,14 @@ const void GCodeBuffer::GetFloatArray(float arr[], size_t& returnedLength, bool returnedLength = 0; return; } - arr[length] = strtof(&gcodeBuffer[readPointer + 1], 0); + const char *q; + arr[length] = SafeStrtof(p, &q); length++; - do - { - readPointer++; - } while(gcodeBuffer[readPointer] && (gcodeBuffer[readPointer] != ' ') && (gcodeBuffer[readPointer] != LIST_SEPARATOR)); - if (gcodeBuffer[readPointer] != LIST_SEPARATOR) + if (*q != LIST_SEPARATOR) { - inList = false; + break; } + p = q + 1; } // Special case if there is one entry and returnedLength requests several. Fill the array with the first entry. @@ -503,8 +501,8 @@ const void GCodeBuffer::GetIntArray(int32_t arr[], size_t& returnedLength, bool if (readPointer >= 0) { size_t length = 0; - bool inList = true; - while(inList) + const char *p = gcodeBuffer + readPointer + 1; + for (;;) { if (length >= returnedLength) // Array limit has been set in here { @@ -513,16 +511,14 @@ const void GCodeBuffer::GetIntArray(int32_t arr[], size_t& returnedLength, bool returnedLength = 0; return; } - arr[length] = strtol(&gcodeBuffer[readPointer + 1], 0, 0); + const char *q; + arr[length] = SafeStrtol(p, &q); length++; - do - { - readPointer++; - } while(gcodeBuffer[readPointer] != 0 && (gcodeBuffer[readPointer] != ' ') && (gcodeBuffer[readPointer] != LIST_SEPARATOR)); - if (gcodeBuffer[readPointer] != LIST_SEPARATOR) + if (*q != LIST_SEPARATOR) { - inList = false; + break; } + p = q + 1; } // Special case if there is one entry and returnedLength requests several. Fill the array with the first entry. @@ -552,8 +548,8 @@ const void GCodeBuffer::GetUnsignedArray(uint32_t arr[], size_t& returnedLength, if (readPointer >= 0) { size_t length = 0; - bool inList = true; - while(inList) + const char *p = gcodeBuffer + readPointer + 1; + for (;;) { if (length >= returnedLength) // Array limit has been set in here { @@ -562,16 +558,14 @@ const void GCodeBuffer::GetUnsignedArray(uint32_t arr[], size_t& returnedLength, returnedLength = 0; return; } - arr[length] = strtoul(&gcodeBuffer[readPointer + 1], 0, 0); + const char *q; + arr[length] = SafeStrtoul(p, &q); length++; - do - { - readPointer++; - } while(gcodeBuffer[readPointer] != 0 && (gcodeBuffer[readPointer] != ' ') && (gcodeBuffer[readPointer] != LIST_SEPARATOR)); - if (gcodeBuffer[readPointer] != LIST_SEPARATOR) + if (*q != LIST_SEPARATOR) { - inList = false; + break; } + p = q + 1; } // Special case if there is one entry and returnedLength requests several. Fill the array with the first entry. @@ -703,7 +697,7 @@ int32_t GCodeBuffer::GetIValue() { if (readPointer >= 0) { - const int32_t result = strtol(&gcodeBuffer[readPointer + 1], 0, 0); + const int32_t result = SafeStrtol(&gcodeBuffer[readPointer + 1]); readPointer = -1; return result; } @@ -717,7 +711,7 @@ uint32_t GCodeBuffer::GetUIValue() { if (readPointer >= 0) { - const uint32_t result = strtoul(&gcodeBuffer[readPointer + 1], 0, 0); + const uint32_t result = SafeStrtoul(&gcodeBuffer[readPointer + 1]); readPointer = -1; return result; } @@ -815,8 +809,8 @@ bool GCodeBuffer::GetIPAddress(uint8_t ip[4]) unsigned int n = 0; for (;;) { - char *pp; - const unsigned long v = strtoul(p, &pp, 10); + const char *pp; + const unsigned long v = SafeStrtoul(p, &pp); if (pp == p || v > 255) { readPointer = -1; @@ -867,12 +861,12 @@ bool GCodeBuffer::GetMacAddress(uint8_t mac[6]) return false; } - const char* p = &gcodeBuffer[readPointer + 1]; + const char* p = gcodeBuffer + readPointer + 1; unsigned int n = 0; for (;;) { - char *pp; - const unsigned long v = strtoul(p, &pp, 16); + const char *pp; + const unsigned long v = SafeStrtoul(p, &pp, 16); if (pp == p || v > 255) { readPointer = -1; diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 3872a734..73d2af62 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -115,8 +115,6 @@ void GCodes::Init() isRetracted = false; lastAuxStatusReportType = -1; // no status reports requested yet - spindleRpm = 0.0; - spindleMaxRpm = DefaultMaxSpindleRpm; laserMaxPower = DefaultMaxLaserPower; heaterFaultState = HeaterFaultState::noFault; @@ -872,7 +870,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) if (platform.GetZProbeType() == ZProbeType::blTouch) { - DoFileMacro(gb, RETRACTPROBE_G, false); // bltouch needs to be retracted whehn it triggers + DoFileMacro(gb, RETRACTPROBE_G, false); // bltouch needs to be retracted when it triggers } } break; @@ -1144,10 +1142,10 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) if (tapsDone == 1 && !hadProbingError) { // Reset the Z axis origin according to the height error so that we can move back up to the dive height - moveBuffer.coords[Z_AXIS] -= g30zHeightError; - g30zHeightError = 0; // there is no zero height error form this probe + moveBuffer.coords[Z_AXIS] = platform.ZProbeStopHeight(); reprap.GetMove().SetNewPosition(moveBuffer.coords, false); - ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); + reprap.GetMove().SetZeroHeightError(moveBuffer.coords); + g30zHeightError = 0; // there is no longer any height error from this probe SetAxisIsHomed(Z_AXIS); // this is only correct if the Z axis is Cartesian-like, but other architectures must be homed before probing anyway } } @@ -1215,6 +1213,7 @@ void GCodes::RunStateMachine(GCodeBuffer& gb, const StringRef& reply) // Setting the Z height with G30 moveBuffer.coords[Z_AXIS] -= g30zHeightError; reprap.GetMove().SetNewPosition(moveBuffer.coords, false); + reprap.GetMove().SetZeroHeightError(moveBuffer.coords); ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); } gb.AdvanceState(); @@ -1523,6 +1522,7 @@ void GCodes::EndSimulation(GCodeBuffer *gb) RestorePosition(simulationRestorePoint, gb); ToolOffsetTransform(currentUserPosition, moveBuffer.coords); reprap.GetMove().SetNewPosition(simulationRestorePoint.moveCoords, true); + axesHomed = axesHomedBeforeSimulation; } // Check for and execute triggers @@ -2108,7 +2108,7 @@ void GCodes::Pop(GCodeBuffer& gb) // Set up the extrusion and feed rate of a move for the Move class // 'moveType' is the S parameter in the G0 or G1 command, or zero for a G2 or G3 command // Returns true if this gcode is valid so far, false if it should be discarded -bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType) +bool GCodes::LoadExtrusionFromGCode(GCodeBuffer& gb) { // Zero every extruder drive as some drives may not be moved for (size_t drive = numTotalAxes; drive < DRIVES; drive++) @@ -2117,16 +2117,6 @@ bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType) } moveBuffer.hasExtrusion = false; - // Deal with feed rate - if (moveType >= 0 && gb.Seen(feedrateLetter)) - { - const float rate = gb.GetFValue() * distanceScale; - gb.MachineState().feedrate = (moveType == 0) - ? rate * speedFactor - : rate * SecondsToMinutes; // don't apply the speed factor to homing and other special moves - } - moveBuffer.feedRate = gb.MachineState().feedrate; - // If we are extruding, check that we have a tool to extrude with if (gb.Seen(extrudeLetter)) { @@ -2227,6 +2217,20 @@ bool GCodes::LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType) return true; } +// Set up the feed rate of a move +// 'moveType' is the S parameter in the G1 command, or zero for a G2 or G3 command +void GCodes::LoadFeedrateFromGCode(GCodeBuffer& gb) +{ + if (moveBuffer.moveType >= 0 && gb.Seen(feedrateLetter)) + { + const float rate = gb.GetFValue() * distanceScale; + gb.MachineState().feedrate = (moveBuffer.moveType == 0) + ? rate * speedFactor + : rate * SecondsToMinutes; // don't apply the speed factor to homing and other special moves + } + moveBuffer.feedRate = gb.MachineState().feedrate; +} + // Check that enough axes have been homed, returning true if insufficient axes homed bool GCodes::CheckEnoughAxesHomed(AxesBitmap axesMoved) { @@ -2378,9 +2382,17 @@ const char* GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated) } // Deal with extrusion and feed rate - LoadExtrusionAndFeedrateFromGCode(gb, moveBuffer.moveType); + if (isCoordinated) + { + LoadExtrusionFromGCode(gb); + LoadFeedrateFromGCode(gb); + } + else + { + moveBuffer.feedRate = platform.GetMaxTravelAcceleration(); + } - // Set up the move. We must assign segmentsLeft last, so that when we move to RTOS, the move won't be picked up by the Move process before it is complete. + // Set up the move. We must assign segmentsLeft last, so that when Move runs as a separate task the move won't be picked up by the Move process before it is complete. // Note that if this is an extruder-only move, we don't do axis movements to allow for tool offset changes, we defer those until an axis moves. if (moveBuffer.moveType != 0) { @@ -2450,16 +2462,55 @@ const char* GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated) // Currently, we do not process new babystepping when executing an arc move const char* GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise) { - // Get the axis parameters. X Y I J are compulsory, Z is optional. - const char* const missingParameter = "G2/G3: missing parameter"; - if (!gb.Seen('X')) return missingParameter; - const float xParam = gb.GetFValue() * distanceScale; - if (!gb.Seen('Y')) return missingParameter; - const float yParam = gb.GetFValue() * distanceScale; - if (!gb.Seen('I')) return missingParameter; - const float iParam = gb.GetFValue() * distanceScale; - if (!gb.Seen('J')) return missingParameter; - const float jParam = gb.GetFValue() * distanceScale; + // Get the axis parameters + float xParam, yParam; + bool seenXY = false; + if (gb.Seen('X')) + { + xParam = gb.GetFValue() * distanceScale; + seenXY = true; + } + else + { + xParam = currentUserPosition[X_AXIS]; + } + + if (gb.Seen('Y')) + { + yParam = gb.GetFValue() * distanceScale; + seenXY = true; + } + else + { + yParam = currentUserPosition[Y_AXIS]; + } + + float iParam, jParam; + bool seenIJ = false; + if (gb.Seen('I')) + { + iParam = gb.GetFValue() * distanceScale; + seenIJ = true; + } + else + { + iParam = 0.0; + } + + if (gb.Seen('J')) + { + jParam = gb.GetFValue() * distanceScale; + seenIJ = true; + } + else + { + jParam = 0.0; + } + + if (!seenXY && seenIJ) // at least one of XY and IJ must be specified + { + return "G2/G3: missing parameter"; + } memcpy(moveBuffer.initialCoords, moveBuffer.coords, numVisibleAxes * sizeof(moveBuffer.initialCoords[0])); @@ -2543,7 +2594,9 @@ const char* GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise) } } - LoadExtrusionAndFeedrateFromGCode(gb, moveBuffer.moveType); + LoadExtrusionFromGCode(gb); + LoadFeedrateFromGCode(gb); + moveBuffer.usePressureAdvance = moveBuffer.hasExtrusion; arcRadius = sqrtf(iParam * iParam + jParam * jParam); @@ -2921,10 +2974,8 @@ GCodeResult GCodes::ProbeGrid(GCodeBuffer& gb, const StringRef& reply) return GCodeResult::error; } - Move& move = reprap.GetMove(); - move.AccessHeightMap().SetGrid(defaultGrid); - move.AccessHeightMap().ClearGridHeights(); - move.SetIdentityTransform(); + reprap.GetMove().AccessHeightMap().SetGrid(defaultGrid); + ClearBedMapping(); gridXindex = gridYindex = 0; gb.SetState(GCodeState::gridProbing1); @@ -2935,9 +2986,9 @@ GCodeResult GCodes::ProbeGrid(GCodeBuffer& gb, const StringRef& reply) return GCodeResult::ok; } -bool GCodes::LoadHeightMap(GCodeBuffer& gb, const StringRef& reply) const +GCodeResult GCodes::LoadHeightMap(GCodeBuffer& gb, const StringRef& reply) { - reprap.GetMove().SetIdentityTransform(); // stop using old-style bed compensation and clear the height map + ClearBedMapping(); String<MaxFilenameLength> heightMapFileName; bool seen = false; @@ -2951,24 +3002,23 @@ bool GCodes::LoadHeightMap(GCodeBuffer& gb, const StringRef& reply) const if (f == nullptr) { reply.printf("Height map file %s not found", heightMapFileName.c_str()); - return true; + return GCodeResult::error; } reply.printf("Failed to load height map from file %s: ", heightMapFileName.c_str()); // set up error message to append to - HeightMap& heightMap = reprap.GetMove().AccessHeightMap(); - const bool err = heightMap.LoadFromFile(f, reply); + const bool err = reprap.GetMove().LoadHeightMapFromFile(f, reply); f->Close(); - if (err) - { - heightMap.ClearGridHeights(); // make sure we don't end up with a partial height map - } - else + reprap.GetMove().UseMesh(!err); + + if (!err) { - reply.Clear(); // wipe the error message + reply.Clear(); // wipe the error message + // Update the current position to allow for any bed compensation at the current XY coordinates + reprap.GetMove().GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes()); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // update user coordinates to reflect any height map offset at the current position } - reprap.GetMove().UseMesh(!err); - return err; + return (err) ? GCodeResult::error : GCodeResult::ok; } // Save the height map and append the success or error message to 'reply', returning true if an error occurred @@ -2992,7 +3042,7 @@ bool GCodes::SaveHeightMap(GCodeBuffer& gb, const StringRef& reply) const } else { - err = reprap.GetMove().AccessHeightMap().SaveToFile(f); + err = reprap.GetMove().SaveHeightMapToFile(f); f->Close(); if (err) { @@ -3007,6 +3057,13 @@ bool GCodes::SaveHeightMap(GCodeBuffer& gb, const StringRef& reply) const return err; } +// Stop using bed compensation +void GCodes::ClearBedMapping() +{ + reprap.GetMove().SetIdentityTransform(); + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // update user coordinates to remove any height map offset there was at the current position +} + // Return the current coordinates as a printable string. // Coordinates are updated at the end of each movement, so this won't tell you where you are mid-movement. void GCodes::GetCurrentCoordinates(const StringRef& s) const @@ -3309,6 +3366,11 @@ GCodeResult GCodes::SetOrReportOffsets(GCodeBuffer &gb, const StringRef& reply) } } + if (settingOffset) + { + ToolOffsetInverseTransform(moveBuffer.coords, currentUserPosition); // update user coordinates to reflect the new tool offset, in case we have this tool selected + } + // Deal with setting temperatures bool settingTemps = false; size_t hCount = tool->HeaterCount(); @@ -3924,9 +3986,7 @@ GCodeResult GCodes::SetHeaterParameters(GCodeBuffer& gb, const StringRef& reply) } } - bool hadError = false; - heat.ConfigureHeaterSensor(305, (unsigned int)heater, gb, reply, hadError); - return GetGCodeResultFromError(hadError); + return heat.ConfigureHeaterSensor(305, (unsigned int)heater, gb, reply); } else { @@ -4209,7 +4269,10 @@ void GCodes::StopPrint(StopPrintReason reason) switch (machineType) { case MachineType::cnc: - platform.SetSpindlePwm(0); + for (size_t i = 0; i < MaxSpindles; i++) + { + platform.AccessSpindle(i).TurnOff(); + } break; case MachineType::laser: diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index 9cbe4d5c..dd516481 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -203,8 +203,6 @@ public: const char *GetAxisLetters() const { return axisLetters; } // Return a null-terminated string of axis letters indexed by drive - const float GetSpindleRpm() const { return spindleRpm; } - #if SUPPORT_12864_LCD bool ProcessCommandFromLcd(const char *cmd); // Process a GCode command from the 12864 LCD returning true if the command was accepted #endif @@ -247,41 +245,42 @@ private: bool HandleMcode(GCodeBuffer& gb, const StringRef& reply); // Do an M code bool HandleTcode(GCodeBuffer& gb, const StringRef& reply); // Do a T code bool HandleResult(GCodeBuffer& gb, GCodeResult rslt, const StringRef& reply); - void HandleReply(GCodeBuffer& gb, GCodeResult rslt, const char *reply); // Handle G-Code replies + void HandleReply(GCodeBuffer& gb, GCodeResult rslt, const char *reply); // Handle G-Code replies void HandleReply(GCodeBuffer& gb, bool error, OutputBuffer *reply); const char* DoStraightMove(GCodeBuffer& gb, bool isCoordinated) __attribute__((hot)); // Execute a straight move returning any error message - const char* DoArcMove(GCodeBuffer& gb, bool clockwise) // Execute an arc move returning any error message + const char* DoArcMove(GCodeBuffer& gb, bool clockwise) // Execute an arc move returning any error message pre(segmentsLeft == 0; resourceOwners[MoveResource] == &gb); - void FinaliseMove(const GCodeBuffer& gb); // Adjust the move parameters to account for segmentation and/or part of the move having been done already - bool CheckEnoughAxesHomed(AxesBitmap axesMoved); // Check that enough axes have been homed - void AbortPrint(GCodeBuffer& gb); // Cancel any print in progress - - GCodeResult DoDwell(GCodeBuffer& gb); // Wait for a bit - GCodeResult DoDwellTime(GCodeBuffer& gb, uint32_t dwellMillis); // Really wait for a bit - GCodeResult DoHome(GCodeBuffer& gb, const StringRef& reply); // Home some axes - GCodeResult ExecuteG30(GCodeBuffer& gb, const StringRef& reply); // Probes at a given position - see the comment at the head of the function itself - void InitialiseTaps(); // Set up to do the first of a possibly multi-tap probe - void SetBedEquationWithProbe(int sParam, const StringRef& reply); // Probes a series of points and sets the bed equation + void FinaliseMove(const GCodeBuffer& gb); // Adjust the move parameters to account for segmentation and/or part of the move having been done already + bool CheckEnoughAxesHomed(AxesBitmap axesMoved); // Check that enough axes have been homed + void AbortPrint(GCodeBuffer& gb); // Cancel any print in progress + + GCodeResult DoDwell(GCodeBuffer& gb); // Wait for a bit + GCodeResult DoDwellTime(GCodeBuffer& gb, uint32_t dwellMillis); // Really wait for a bit + GCodeResult DoHome(GCodeBuffer& gb, const StringRef& reply); // Home some axes + GCodeResult ExecuteG30(GCodeBuffer& gb, const StringRef& reply); // Probes at a given position - see the comment at the head of the function itself + void InitialiseTaps(); // Set up to do the first of a possibly multi-tap probe + void SetBedEquationWithProbe(int sParam, const StringRef& reply); // Probes a series of points and sets the bed equation GCodeResult SetPrintZProbe(GCodeBuffer& gb, const StringRef& reply); // Either return the probe value, or set its threshold GCodeResult SetOrReportOffsets(GCodeBuffer& gb, const StringRef& reply); // Deal with a G10 - GCodeResult SetPositions(GCodeBuffer& gb); // Deal with a G92 - GCodeResult DoDriveMapping(GCodeBuffer& gb, const StringRef& reply); // Deal with a M584 - GCodeResult ProbeTool(GCodeBuffer& gb, const StringRef& reply); // Deal with a M585 - GCodeResult SetDateTime(GCodeBuffer& gb,const StringRef& reply); // Deal with a M905 - GCodeResult SavePosition(GCodeBuffer& gb,const StringRef& reply); // Deal with G60 - - bool LoadExtrusionAndFeedrateFromGCode(GCodeBuffer& gb, int moveType); // Set up the extrusion and feed rate of a move for the Move class - - bool Push(GCodeBuffer& gb); // Push feedrate etc on the stack - void Pop(GCodeBuffer& gb); // Pop feedrate etc - void DisableDrives(); // Turn the motors off + GCodeResult SetPositions(GCodeBuffer& gb); // Deal with a G92 + GCodeResult DoDriveMapping(GCodeBuffer& gb, const StringRef& reply); // Deal with a M584 + GCodeResult ProbeTool(GCodeBuffer& gb, const StringRef& reply); // Deal with a M585 + GCodeResult SetDateTime(GCodeBuffer& gb,const StringRef& reply); // Deal with a M905 + GCodeResult SavePosition(GCodeBuffer& gb,const StringRef& reply); // Deal with G60 + + bool LoadExtrusionFromGCode(GCodeBuffer& gb); // Set up the extrusion of a move + void LoadFeedrateFromGCode(GCodeBuffer& gb); // Set up the feed rate of a move + + bool Push(GCodeBuffer& gb); // Push feedrate etc on the stack + void Pop(GCodeBuffer& gb); // Pop feedrate etc + void DisableDrives(); // Turn the motors off bool OpenFileToWrite(GCodeBuffer& gb, const char* directory, const char* fileName, const FilePosition size, const bool binaryWrite, const uint32_t fileCRC32); - // Start saving GCodes in a file - void FinishWrite(GCodeBuffer& gb); // Finish writing to the file and respond - bool SendConfigToLine(); // Deal with M503 + // Start saving GCodes in a file + void FinishWrite(GCodeBuffer& gb); // Finish writing to the file and respond + bool SendConfigToLine(); // Deal with M503 - GCodeResult OffsetAxes(GCodeBuffer& gb); // Set offsets + GCodeResult OffsetAxes(GCodeBuffer& gb); // Set offsets #if SUPPORT_WORKPLACE_COORDINATES GCodeResult GetSetWorkplaceCoordinates(GCodeBuffer& gb, const StringRef& reply, bool compute); // Set workspace coordinates @@ -328,13 +327,16 @@ private: GCodeResult SetOrReportZProbe(GCodeBuffer& gb, const StringRef &reply); // Handle M558 GCodeResult DefineGrid(GCodeBuffer& gb, const StringRef &reply); // Define the probing grid, returning true if error - bool LoadHeightMap(GCodeBuffer& gb, const StringRef& reply) const; // Load the height map from file + GCodeResult LoadHeightMap(GCodeBuffer& gb, const StringRef& reply); // Load the height map from file bool SaveHeightMap(GCodeBuffer& gb, const StringRef& reply) const; // Save the height map to file + void ClearBedMapping(); // Stop using bed compensation GCodeResult ProbeGrid(GCodeBuffer& gb, const StringRef& reply); // Start probing the grid, returning true if we didn't because of an error GCodeResult CheckOrConfigureTrigger(GCodeBuffer& gb, const StringRef& reply, int code); // Handle M581 and M582 GCodeResult UpdateFirmware(GCodeBuffer& gb, const StringRef &reply); // Handle M997 GCodeResult SendI2c(GCodeBuffer& gb, const StringRef &reply); // Handle M260 GCodeResult ReceiveI2c(GCodeBuffer& gb, const StringRef &reply); // Handle M261 + GCodeResult SimulateFile(GCodeBuffer& gb, const StringRef &reply, const StringRef& file); // Handle M37 to simulate a whole file + GCodeResult ChangeSimulationMode(GCodeBuffer& gb, const StringRef &reply, uint32_t newSimulationMode); // handle M37 to change the simulation mode GCodeResult WriteConfigOverrideFile(GCodeBuffer& gb, const StringRef& reply) const; // Write the config-override file void CopyConfigFinalValues(GCodeBuffer& gb); // Copy the feed rate etc. from the daemon to the input channels @@ -421,6 +423,7 @@ private: bool doingArcMove; bool abortedArcMove; + AxesBitmap axesHomedBeforeSimulation; // axes that were homed when we started the simulation RestorePoint simulationRestorePoint; // The position and feed rate when we started a simulation RestorePoint numberedRestorePoints[NumRestorePoints]; // Restore points accessible using the R parameter in the G0/G1 command @@ -521,9 +524,7 @@ private: FilamentSensorStatus lastFilamentError; size_t lastFilamentErrorExtruder; - // CNC and laser - float spindleRpm; - float spindleMaxRpm; + // Laser float laserMaxPower; // Heater fault handler diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index 7ffafebf..01a694a8 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -225,11 +225,11 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, const StringRef& reply) break; case 1: // load height map file - result = GetGCodeResultFromError(LoadHeightMap(gb, reply)); + result = LoadHeightMap(gb, reply); break; default: // clear height map - reprap.GetMove().SetIdentityTransform(); + ClearBedMapping(); break; } } @@ -394,8 +394,19 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) switch (machineType) { case MachineType::cnc: - spindleRpm = gb.GetFValue(); - platform.SetSpindlePwm(spindleRpm/spindleMaxRpm); + { + const float rpm = gb.GetFValue(); + const uint32_t slot = gb.Seen('P') ? gb.GetUIValue() : 0; + if (slot >= MaxSpindles) + { + reply.copy("Invalid spindle index"); + result = GCodeResult::error; + } + else + { + platform.AccessSpindle(slot).SetRpm(rpm); + } + } break; case MachineType::laser: @@ -423,7 +434,17 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) { if (machineType == MachineType::cnc) { - platform.SetSpindlePwm(-gb.GetFValue()/spindleMaxRpm); + const float rpm = gb.GetFValue(); + const uint32_t slot = gb.Seen('P') ? gb.GetUIValue() : 0; + if (slot >= MaxSpindles) + { + reply.copy("Invalid spindle index"); + result = GCodeResult::error; + } + else + { + platform.AccessSpindle(slot).SetRpm(-rpm); + } } else { @@ -436,7 +457,28 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) switch (machineType) { case MachineType::cnc: - platform.SetSpindlePwm(0.0); + if (gb.Seen('P')) + { + // Turn off specific spindle + const uint32_t slot = gb.GetUIValue(); + if (slot >= MaxSpindles) + { + reply.copy("Invalid spindle index"); + result = GCodeResult::error; + } + else + { + platform.AccessSpindle(slot).TurnOff(); + } + } + else + { + // Turn off every spindle if no 'P' parameter is present + for (size_t i = 0; i < MaxSpindles; i++) + { + platform.AccessSpindle(i).TurnOff(); + } + } break; case MachineType::laser: @@ -859,78 +901,27 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) case 37: // Simulation mode on/off, or simulate a whole file { bool seen = false; - uint32_t newSimulationMode; String<MaxFilenameLength> simFileName; gb.TryGetPossiblyQuotedString('P', simFileName.GetRef(), seen); if (seen) { - newSimulationMode = 1; // default to simulation mode 1 when a filename is given + result = SimulateFile(gb, reply, simFileName.GetRef()); } else { + uint32_t newSimulationMode; gb.TryGetUIValue('S', newSimulationMode, seen); - } - - if (seen && newSimulationMode != simulationMode) - { - if (!LockMovementAndWaitForStandstill(gb)) - { - return false; - } - - const bool wasSimulating = (simulationMode != 0); - simulationMode = (uint8_t)newSimulationMode; - reprap.GetMove().Simulate(simulationMode); - if (simFileName.IsEmpty() || simulationMode == 0) + if (seen) { - // It's a simulation mode change command - exitSimulationWhenFileComplete = false; - if (simulationMode != 0) - { - simulationTime = 0.0; - if (!wasSimulating) - { - // Starting a new simulation, so save the current position - reprap.GetMove().GetCurrentUserPosition(simulationRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes()); - simulationRestorePoint.feedRate = gb.MachineState().feedrate; - } - } - else if (wasSimulating) - { - EndSimulation(&gb); - } + result = ChangeSimulationMode(gb, reply, newSimulationMode); } else { - // Simulate a whole file and then stop simulating - simulationTime = 0.0; - if (!wasSimulating) - { - // Starting a new simulation, so save the current position - reprap.GetMove().GetCurrentUserPosition(simulationRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes()); - simulationRestorePoint.feedRate = gb.MachineState().feedrate; - } - if (QueueFileToPrint(simFileName.c_str(), reply)) - { - exitSimulationWhenFileComplete = true; - reprap.GetPrintMonitor().StartingPrint(simFileName.c_str()); - StartPrinting(true); - reply.printf("Simulating print of file %s", simFileName.c_str()); - } - else - { - simulationMode = 0; - reprap.GetMove().Simulate(0); - result = GCodeResult::error; - } + reply.printf("Simulation mode: %s, move time: %.1f sec, other time: %.1f sec", + (simulationMode != 0) ? "on" : "off", (double)reprap.GetMove().GetSimulationTime(), (double)simulationTime); } } - else - { - reply.printf("Simulation mode: %s, move time: %.1f sec, other time: %.1f sec", - (simulationMode != 0) ? "on" : "off", (double)reprap.GetMove().GetSimulationTime(), (double)simulationTime); - } } break; @@ -2443,7 +2434,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) { return false; } - result = GetGCodeResultFromError(LoadHeightMap(gb, reply)); + result = LoadHeightMap(gb, reply); break; case 376: // Set taper height @@ -2568,38 +2559,55 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) break; case 453: // Select CNC mode - machineType = MachineType::cnc; - if (gb.Seen('P')) { - uint32_t pins[2] = { NoLogicalPin, NoLogicalPin }; - size_t numPins = 2; - gb.GetUnsignedArray(pins, numPins, false); - if (pins[0] > 65535) + uint32_t slot = gb.Seen('S') ? gb.GetUIValue() : 0; + if (slot >= MaxSpindles) { - pins[0] = NoLogicalPin; + reply.copy("Invalid spindle index"); + result = GCodeResult::error; + break; } - if (numPins < 2 || pins[1] < 0 || pins[1] > 65535) + + Spindle& spindle = platform.AccessSpindle(slot); + if (gb.Seen('P')) { - pins[1] = NoLogicalPin; + uint32_t pins[2] = { NoLogicalPin, NoLogicalPin }; + size_t numPins = 2; + gb.GetUnsignedArray(pins, numPins, false); + if (pins[0] > 65535) + { + pins[0] = NoLogicalPin; + } + if (numPins < 2 || pins[1] < 0 || pins[1] > 65535) + { + pins[1] = NoLogicalPin; + } + const bool invert = (gb.Seen('I') && gb.GetIValue() > 0); + if (!spindle.SetPins(pins[0], pins[1], invert)) + { + reply.copy("Bad P parameter"); + result = GCodeResult::error; + break; + } } - const bool invert = (gb.Seen('I') && gb.GetIValue() > 0); - if (platform.SetSpindlePins(pins[0], pins[1], invert)) + if (gb.Seen('F')) { - reply.copy("CNC mode selected"); + spindle.SetPwmFrequency(gb.GetFValue()); } - else + if (gb.Seen('R')) { - reply.copy("Bad P parameter"); - result = GCodeResult::error; + spindle.SetMaxRpm(max<float>(1.0, gb.GetFValue())); + } + if (gb.Seen('T')) + { + spindle.SetToolNumber(gb.GetIValue()); + } + + if (machineType != MachineType::cnc) + { + machineType = MachineType::cnc; + reply.copy("CNC mode selected"); } - } - if (result == GCodeResult::ok && gb.Seen('F')) - { - platform.SetSpindlePwmFrequency(gb.GetFValue()); - } - if (result == GCodeResult::ok && gb.Seen('R')) - { - spindleMaxRpm = max<float>(1.0, gb.GetFValue()); } break; @@ -2927,7 +2935,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, const StringRef& reply) break; case 561: // Set identity transform (also clears bed probe grid) - reprap.GetMove().SetIdentityTransform(); + ClearBedMapping(); break; case 562: // Reset temperature fault - use with great caution diff --git a/src/GCodes/GCodes3.cpp b/src/GCodes/GCodes3.cpp index 1575faf8..3d300246 100644 --- a/src/GCodes/GCodes3.cpp +++ b/src/GCodes/GCodes3.cpp @@ -13,6 +13,7 @@ #include "Movement/Move.h" #include "RepRap.h" #include "Tools/Tool.h" +#include "PrintMonitor.h" #if HAS_WIFI_NETWORKING # include "FirmwareUpdater.h" @@ -335,6 +336,70 @@ GCodeResult GCodes::DefineGrid(GCodeBuffer& gb, const StringRef &reply) return GCodeResult::error; } +// Handle M37 to simulate a whole file +GCodeResult GCodes::SimulateFile(GCodeBuffer& gb, const StringRef &reply, const StringRef& file) +{ + if (reprap.GetPrintMonitor().IsPrinting()) + { + reply.copy("cannot simulate while a file is being printed"); + return GCodeResult::error; + } + + if (QueueFileToPrint(file.c_str(), reply)) + { + if (simulationMode == 0) + { + axesHomedBeforeSimulation = axesHomed; + axesHomed = LowestNBits<AxesBitmap>(numVisibleAxes); // pretend all axes are homed + reprap.GetMove().GetCurrentUserPosition(simulationRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes()); + simulationRestorePoint.feedRate = gb.MachineState().feedrate; + } + simulationTime = 0.0; + exitSimulationWhenFileComplete = true; + simulationMode = 1; + reprap.GetMove().Simulate(simulationMode); + reprap.GetPrintMonitor().StartingPrint(file.c_str()); + StartPrinting(true); + reply.printf("Simulating print of file %s", file.c_str()); + return GCodeResult::ok; + } + + return GCodeResult::error; +} + +// handle M37 to change the simulation mode +GCodeResult GCodes::ChangeSimulationMode(GCodeBuffer& gb, const StringRef &reply, uint32_t newSimulationMode) +{ + if (newSimulationMode != simulationMode) + { + if (!LockMovementAndWaitForStandstill(gb)) + { + return GCodeResult::notFinished; + } + + if (newSimulationMode == 0) + { + EndSimulation(&gb); + } + else + { + if (simulationMode == 0) + { + // Starting a new simulation, so save the current position + axesHomedBeforeSimulation = axesHomed; + axesHomed = LowestNBits<AxesBitmap>(numVisibleAxes); // pretend all axes are homed + reprap.GetMove().GetCurrentUserPosition(simulationRestorePoint.moveCoords, 0, reprap.GetCurrentXAxes(), reprap.GetCurrentYAxes()); + simulationRestorePoint.feedRate = gb.MachineState().feedrate; + } + simulationTime = 0.0; + } + exitSimulationWhenFileComplete = false; + simulationMode = (uint8_t)newSimulationMode; + reprap.GetMove().Simulate(simulationMode); + } + return GCodeResult::ok; +} + // Handle M558 GCodeResult GCodes::SetOrReportZProbe(GCodeBuffer& gb, const StringRef &reply) { diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp index 9db52383..4d803c9c 100644 --- a/src/Heating/Heat.cpp +++ b/src/Heating/Heat.cpp @@ -577,17 +577,16 @@ bool Heat::SetHeaterChannel(size_t heater, int channel) } // Configure the temperature sensor for a channel -bool Heat::ConfigureHeaterSensor(unsigned int mcode, size_t heater, GCodeBuffer& gb, const StringRef& reply, bool& error) +GCodeResult Heat::ConfigureHeaterSensor(unsigned int mcode, size_t heater, GCodeBuffer& gb, const StringRef& reply) { TemperatureSensor ** const spp = GetSensor(heater); if (spp == nullptr || *spp == nullptr) { reply.printf("heater %d is not configured", heater); - error = true; - return false; + return GCodeResult::error; } - return (*spp)->Configure(mcode, heater, gb, reply, error); + return (*spp)->Configure(mcode, heater, gb, reply); } // Get a pointer to the temperature sensor entry, or nullptr if the heater number is bad diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h index 620987de..e813f821 100644 --- a/src/Heating/Heat.h +++ b/src/Heating/Heat.h @@ -28,6 +28,7 @@ Licence: GPL #include "RepRapFirmware.h" #include "Pid.h" #include "MessageType.h" +#include "GCodes/GCodeResult.h" class TemperatureSensor; class HeaterProtection; @@ -127,7 +128,7 @@ public: int GetHeaterChannel(size_t heater) const; // Return the channel used by a particular heater, or -1 if not configured bool SetHeaterChannel(size_t heater, int channel); // Set the channel used by a heater, returning true if bad heater or channel number - bool ConfigureHeaterSensor(size_t heater, unsigned int mcode, GCodeBuffer& gb, const StringRef& reply, bool& error); // Configure the temperature sensor for a channel + GCodeResult ConfigureHeaterSensor(size_t heater, unsigned int mcode, GCodeBuffer& gb, const StringRef& reply); // Configure the temperature sensor for a channel const char *GetHeaterName(size_t heater) const; // Get the name of a heater, or nullptr if it hasn't been named HeaterProtection& AccessHeaterProtection(size_t index) const; // Return the protection parameters of the given index diff --git a/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp b/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp index dec51ba3..98e59374 100644 --- a/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp +++ b/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp @@ -49,7 +49,7 @@ void CurrentLoopTemperatureSensor::Init() } // Configure this temperature sensor -bool CurrentLoopTemperatureSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) +GCodeResult CurrentLoopTemperatureSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) { if (mCode == 305) { @@ -68,7 +68,7 @@ bool CurrentLoopTemperatureSensor::Configure(unsigned int mCode, unsigned int he reply.catf(", temperature range %.1f to %.1fC", (double)tempAt4mA, (double)tempAt20mA); } } - return false; + return GCodeResult::ok; } TemperatureError CurrentLoopTemperatureSensor::GetTemperature(float& t) diff --git a/src/Heating/Sensors/CurrentLoopTemperatureSensor.h b/src/Heating/Sensors/CurrentLoopTemperatureSensor.h index 79e67978..24c51e5a 100644 --- a/src/Heating/Sensors/CurrentLoopTemperatureSensor.h +++ b/src/Heating/Sensors/CurrentLoopTemperatureSensor.h @@ -14,7 +14,7 @@ class CurrentLoopTemperatureSensor : public SpiTemperatureSensor { public: CurrentLoopTemperatureSensor(unsigned int channel); - bool Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) override; + GCodeResult Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) override; void Init() override; TemperatureError GetTemperature(float& t) override; diff --git a/src/Heating/Sensors/DhtSensor.cpp b/src/Heating/Sensors/DhtSensor.cpp index 49e99d65..dec683cf 100644 --- a/src/Heating/Sensors/DhtSensor.cpp +++ b/src/Heating/Sensors/DhtSensor.cpp @@ -38,8 +38,9 @@ DhtSensor::~DhtSensor() numInstances--; } -bool DhtSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) +GCodeResult DhtSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) { + GCodeResult rslt = GCodeResult::ok; if (mCode == 305) { bool seen = false; @@ -62,8 +63,8 @@ bool DhtSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& type = DhtSensorType::Dht22; break; default: - error = true; reply.copy("Invalid DHT sensor type"); + rslt = GCodeResult::error; break; } } @@ -91,7 +92,7 @@ bool DhtSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& reply.catf(", sensor type %s", sensorTypeString); } } - return false; + return rslt; } void DhtSensor::Init() diff --git a/src/Heating/Sensors/DhtSensor.h b/src/Heating/Sensors/DhtSensor.h index cd02e64e..ad748ee1 100644 --- a/src/Heating/Sensors/DhtSensor.h +++ b/src/Heating/Sensors/DhtSensor.h @@ -29,7 +29,7 @@ public: DhtSensor(unsigned int channel); ~DhtSensor(); - bool Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) override; + GCodeResult Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) override; void Init() override; TemperatureError GetTemperature(float& t) override; diff --git a/src/Heating/Sensors/RtdSensor31865.cpp b/src/Heating/Sensors/RtdSensor31865.cpp index 7c3077de..fba74178 100644 --- a/src/Heating/Sensors/RtdSensor31865.cpp +++ b/src/Heating/Sensors/RtdSensor31865.cpp @@ -42,7 +42,7 @@ RtdSensor31865::RtdSensor31865(unsigned int channel) } // Configure this temperature sensor -bool RtdSensor31865::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) +GCodeResult RtdSensor31865::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) { if (mCode == 305) { @@ -86,7 +86,7 @@ bool RtdSensor31865::Configure(unsigned int mCode, unsigned int heater, GCodeBuf reply.catf(", %s wires, reject %dHz, reference resistor %u ohms", (cr0 & 0x10) ? "3" : "2/4", (cr0 & 0x01) ? 50 : 60, (unsigned int)rref); } } - return false; + return GCodeResult::ok; } // Perform the actual hardware initialization for attaching and using this device on the SPI hardware bus. diff --git a/src/Heating/Sensors/RtdSensor31865.h b/src/Heating/Sensors/RtdSensor31865.h index 81b343b7..316007d4 100644 --- a/src/Heating/Sensors/RtdSensor31865.h +++ b/src/Heating/Sensors/RtdSensor31865.h @@ -14,7 +14,7 @@ class RtdSensor31865 : public SpiTemperatureSensor { public: RtdSensor31865(unsigned int channel); - bool Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) override; + GCodeResult Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) override; void Init() override; TemperatureError GetTemperature(float& t) override; diff --git a/src/Heating/Sensors/TemperatureSensor.cpp b/src/Heating/Sensors/TemperatureSensor.cpp index 37e100aa..04815119 100644 --- a/src/Heating/Sensors/TemperatureSensor.cpp +++ b/src/Heating/Sensors/TemperatureSensor.cpp @@ -44,7 +44,7 @@ void TemperatureSensor::SetHeaterName(const char *newName) } // Default implementation of Configure, for sensors that have no configurable parameters -bool TemperatureSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) +GCodeResult TemperatureSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) { bool seen = false; if (mCode == 305) @@ -56,7 +56,7 @@ bool TemperatureSensor::Configure(unsigned int mCode, unsigned int heater, GCode CopyBasicHeaterDetails(heater, reply); } } - return seen; + return GCodeResult::ok; } void TemperatureSensor::CopyBasicHeaterDetails(unsigned int heater, const StringRef& reply) const diff --git a/src/Heating/Sensors/TemperatureSensor.h b/src/Heating/Sensors/TemperatureSensor.h index bd14618c..fdb04775 100644 --- a/src/Heating/Sensors/TemperatureSensor.h +++ b/src/Heating/Sensors/TemperatureSensor.h @@ -3,6 +3,7 @@ #include "RepRapFirmware.h" #include "Heating/TemperatureError.h" // for result codes +#include "GCodes/GCodeResult.h" class GCodeBuffer; @@ -14,7 +15,7 @@ public: // Configure the sensor from M305 parameters. // If we find any parameters, process them and return true. If an error occurs while processing them, set 'error' to true and write an error message to 'reply. // if we find no relevant parameters, report the current parameters to 'reply' and return 'false'. - virtual bool Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error); + virtual GCodeResult Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply); // Initialise or re-initialise the temperature sensor virtual void Init() = 0; diff --git a/src/Heating/Sensors/Thermistor.cpp b/src/Heating/Sensors/Thermistor.cpp index cccd2c1f..009a5402 100644 --- a/src/Heating/Sensors/Thermistor.cpp +++ b/src/Heating/Sensors/Thermistor.cpp @@ -39,7 +39,7 @@ void Thermistor::Init() } // Configure the temperature sensor -bool Thermistor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) +GCodeResult Thermistor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) { bool seen = false; if (mCode == 305) @@ -93,7 +93,7 @@ bool Thermistor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& } } - return seen; + return GCodeResult::ok; } // Get the temperature diff --git a/src/Heating/Sensors/Thermistor.h b/src/Heating/Sensors/Thermistor.h index e2081f3a..e733417e 100644 --- a/src/Heating/Sensors/Thermistor.h +++ b/src/Heating/Sensors/Thermistor.h @@ -22,7 +22,7 @@ class Thermistor : public TemperatureSensor { public: Thermistor(unsigned int channel, bool p_isPT1000); // create an instance with default values - bool Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) override; // configure the sensor from M305 parameters + GCodeResult Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) override; // configure the sensor from M305 parameters void Init() override; TemperatureError GetTemperature(float& t) override; diff --git a/src/Heating/Sensors/ThermocoupleSensor31856.cpp b/src/Heating/Sensors/ThermocoupleSensor31856.cpp index 118415f6..97b66269 100644 --- a/src/Heating/Sensors/ThermocoupleSensor31856.cpp +++ b/src/Heating/Sensors/ThermocoupleSensor31856.cpp @@ -60,7 +60,7 @@ ThermocoupleSensor31856::ThermocoupleSensor31856(unsigned int channel) } // Configure this temperature sensor -bool ThermocoupleSensor31856::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) +GCodeResult ThermocoupleSensor31856::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) { if (mCode == 305) { @@ -90,7 +90,7 @@ bool ThermocoupleSensor31856::Configure(unsigned int mCode, unsigned int heater, else { reply.copy("Bad thermocouple type letter in M305 command"); - return true; + return GCodeResult::error; } } @@ -100,7 +100,7 @@ bool ThermocoupleSensor31856::Configure(unsigned int mCode, unsigned int heater, reply.catf(", thermocouple type %c, reject %dHz", TypeLetters[thermocoupleType], (cr0 & 0x01) ? 50 : 60); } } - return false; + return GCodeResult::ok; } // Perform the actual hardware initialization for attaching and using this device on the SPI hardware bus. diff --git a/src/Heating/Sensors/ThermocoupleSensor31856.h b/src/Heating/Sensors/ThermocoupleSensor31856.h index 09b61baf..777e9f02 100644 --- a/src/Heating/Sensors/ThermocoupleSensor31856.h +++ b/src/Heating/Sensors/ThermocoupleSensor31856.h @@ -14,7 +14,7 @@ class ThermocoupleSensor31856 : public SpiTemperatureSensor { public: ThermocoupleSensor31856(unsigned int channel); - bool Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply, bool& error) override; + GCodeResult Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, const StringRef& reply) override; void Init() override; TemperatureError GetTemperature(float& t) override; diff --git a/src/Libraries/General/IP4String.cpp b/src/Libraries/General/IP4String.cpp index 722e5fa2..c085f089 100644 --- a/src/Libraries/General/IP4String.cpp +++ b/src/Libraries/General/IP4String.cpp @@ -7,6 +7,7 @@ #include "IP4String.h" #include "StringRef.h" +#include "SafeVsnprintf.h" IP4String::IP4String(const uint8_t ip[4]) { diff --git a/src/Libraries/General/strtod.cpp b/src/Libraries/General/SafeStrtod.cpp index 8cc7b963..c1eb03cc 100644 --- a/src/Libraries/General/strtod.cpp +++ b/src/Libraries/General/SafeStrtod.cpp @@ -18,8 +18,14 @@ #include <cmath> #include <climits> -extern "C" double strtod(const char *s, char **p) +double SafeStrtod(const char *s, const char **p) { + // 0. Skip white space + while (*s == ' ' || *s == '\t') + { + ++s; + } + // 1. Check for a sign const bool negative = (*s == '-'); if (negative || *s == '+') @@ -121,15 +127,9 @@ extern "C" double strtod(const char *s, char **p) return (negative) ? -retvalue : retvalue; } -extern "C" float strtof(const char *s, char **p) -{ - return (float)strtod(s, p); -} - -// We need to define this one too because it is called internally, probably by sscanf -extern "C" double _strtod_r(struct _reent *r, const char *s, char **p) +float SafeStrtof(const char *s, const char **p) { - return strtod(s, p); + return (float)SafeStrtod(s, p); } // End diff --git a/src/Libraries/General/SafeStrtod.h b/src/Libraries/General/SafeStrtod.h new file mode 100644 index 00000000..0d4e222d --- /dev/null +++ b/src/Libraries/General/SafeStrtod.h @@ -0,0 +1,30 @@ +/* + * SafeStrtod.h + * + * Created on: 8 Apr 2018 + * Author: David + */ + +#ifndef SRC_LIBRARIES_GENERAL_SAFESTRTOD_H_ +#define SRC_LIBRARIES_GENERAL_SAFESTRTOD_H_ + +double SafeStrtod(const char *s, const char **p = nullptr); +float SafeStrtof(const char *s, const char **p = nullptr); + +inline long SafeStrtol(const char *s, const char **endptr = nullptr, int base = 10) +{ + return strtol(s, const_cast<char**>(endptr), base); +} + +inline unsigned long SafeStrtoul(const char *s, const char **endptr = nullptr, int base = 10) +{ + return strtoul(s, const_cast<char**>(endptr), base); +} + +#define strtod(s, p) Do_not_use_strtod_use_SafeStrtod_instead +#define strtof(s, p) Do_not_use_strtof_use_SafeStrtof_instead +#define strtol(s, ...) Do_not_use_strtol_use_SafeStrtol_instead +#define strtoul(s, ...) Do_not_use_strtoul_use_SafeStrtoul_instead +#define atof(s) Do_not_use_atof_use_SafeStrtof_instead + +#endif /* SRC_LIBRARIES_GENERAL_SAFESTRTOD_H_ */ diff --git a/src/Libraries/General/SafeVsnprintf.cpp b/src/Libraries/General/SafeVsnprintf.cpp new file mode 100644 index 00000000..fb153f88 --- /dev/null +++ b/src/Libraries/General/SafeVsnprintf.cpp @@ -0,0 +1,636 @@ +/* + * vsnprintf.cpp + * + * Created on: 8 Apr 2018 + + Original copyright 2001, 2002 Georges Menie (www.menie.org) + stdarg version contributed by Christian Ettinger + Converted to C++ and adapted to support floating point formats by D. Crocker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Changes for the FreeRTOS ports: + + - The dot in "%-8.8s" + - The specifiers 'l' (long) and 'L' (long long) + - The specifier 'u' for unsigned + - Dot notation for IP addresses: + sprintf("IP = %xip\n", 0xC0A80164); + will produce "IP = 192.168.1.100\n" + sprintf("IP = %pip\n", pxIPv6_Address); +*/ + +#include <cstdarg> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <climits> +#include <cmath> + +// The following should be enough for 32-bit int/long and 64-bit long long +constexpr size_t MaxLongDigits = 10; // to print 4294967296 +constexpr size_t MaxUllDigits = 20; // to print 18446744073709551616 + +struct xPrintFlags +{ + int base; + int width; + int printLimit; + unsigned int + letBase : 8, + padZero : 1, + padRight : 1, + isSigned : 1, + isNumber : 1, + isString : 1, + long32 : 1, + long64 : 1; +}; + +struct SStringBuf +{ + char *str; + const char *orgStr; + const char *nulPos; + int curLen; + struct xPrintFlags flags; + + SStringBuf(char *s, size_t maxLen); + void Init(); +}; + +SStringBuf::SStringBuf(char *apBuf, size_t maxLen) +{ + str = apBuf; + orgStr = apBuf; + nulPos = apBuf + maxLen - 1; + curLen = 0; + Init(); +} + +void SStringBuf::Init() +{ + memset(&flags, 0, sizeof(flags)); +} + +/*-----------------------------------------------------------*/ + +// Store the specified character in the string buffer. +// If it won't fit leaving room for a null, store a null and return false. +// If it is null, store it and return false. +// Else store it and return true. +static bool strbuf_printchar(SStringBuf& apStr, char c) +{ + if (c != 0 && apStr.str < apStr.nulPos) + { + *apStr.str++ = c; + apStr.curLen++; + return true; + } + *apStr.str = '\0'; + return false; +} + +/*-----------------------------------------------------------*/ + +// Print the string s to the string buffer adding any necessary padding +static bool prints(SStringBuf& apBuf, const char *apString ) +{ + int count; + if (apBuf.flags.printLimit > 0 && apBuf.flags.isString) + { + // It's a string so printLimit is the max number of characters to print from the string. + // Don't call strlen on it because it might not be null terminated. + count = 0; + for (const char *s = apString; count < apBuf.flags.printLimit && *s != 0; ++s) + { + ++count; + } + } + else + { + count = (int)strlen(apString); + } + + int rightSpacesNeeded = 0; + const bool hasMinimumDigits = (apBuf.flags.isNumber && apBuf.flags.printLimit > 0); + if (hasMinimumDigits || apBuf.flags.width > 0) + { + // We may have some padding to do + int leftSpacesNeeded = 0, leftZerosNeeded = 0; + if (hasMinimumDigits && count < apBuf.flags.printLimit) + { + leftZerosNeeded = apBuf.flags.printLimit - count; + } + if (count + leftZerosNeeded < apBuf.flags.width) + { + const int remainingPaddingNeeded = apBuf.flags.width - (count + leftZerosNeeded); + if (apBuf.flags.padRight) + { + rightSpacesNeeded = remainingPaddingNeeded; + } + else if (apBuf.flags.padZero) + { + leftZerosNeeded += remainingPaddingNeeded; + } + else + { + leftSpacesNeeded = remainingPaddingNeeded; + } + } + + // Do the left padding + while (leftSpacesNeeded > 0) + { + if (!strbuf_printchar(apBuf, ' ')) + { + return false; + } + --leftSpacesNeeded; + } + while (leftZerosNeeded > 0) + { + if (!strbuf_printchar(apBuf, '0')) + { + return false; + } + --leftZerosNeeded; + } + } + + // Now print the actual string + while (count > 0) + { + if (!strbuf_printchar(apBuf, *apString++)) + { + return false; + } + --count; + } + + // Now the right padding + while (rightSpacesNeeded > 0) + { + if (!strbuf_printchar(apBuf, ' ')) + { + return false; + } + --rightSpacesNeeded; + } + + return true; +} + +/*-----------------------------------------------------------*/ + +static bool printll(SStringBuf& apBuf, long long i) +{ + apBuf.flags.isNumber = true; /* Parameter for prints */ + if (i == 0LL) + { + return prints(apBuf, "0"); + } + + bool neg = false; + unsigned long long u = i; + if ((apBuf.flags.isSigned) && (apBuf.flags.base == 10) && (i < 0LL)) + { + neg = true; + u = -i; + } + + char print_buf[MaxUllDigits + 2]; + char *s = print_buf + sizeof print_buf - 1; + *s = '\0'; + while (u != 0) + { + const lldiv_t lldiv_result = lldiv(u, (long long)apBuf.flags.base); + unsigned int t = lldiv_result.rem; + if (t >= 10) + { + t += apBuf.flags.letBase - '0' - 10; + } + *--s = t + '0'; + u = lldiv_result.quot; + } + + if (neg) + { + if (apBuf.flags.width != 0 && apBuf.flags.padZero) + { + if (!strbuf_printchar(apBuf, '-')) + { + return false; + } + --apBuf.flags.width; + } + else + { + *--s = '-'; + } + } + + return prints(apBuf, s); +} + +/*-----------------------------------------------------------*/ + +static bool printi(SStringBuf& apBuf, int i) +{ + apBuf.flags.isNumber = true; /* Parameter for prints */ + + if (i == 0) + { + return prints(apBuf, "0"); + } + + bool neg = false; + unsigned int u = i; + unsigned base = apBuf.flags.base; + if ((apBuf.flags.isSigned) && (base == 10) && (i < 0)) + { + neg = true; + u = -i; + } + + char print_buf[MaxLongDigits + 2]; + char *s = print_buf + sizeof print_buf - 1; + *s = '\0'; + + switch (base) + { + case 16: + while (u != 0) + { + unsigned int t = u & 0xF; + if (t >= 10) + { + t += apBuf.flags.letBase - '0' - 10; + } + *--s = t + '0'; + u >>= 4; + } + break; + + case 8: + case 10: + // GCC compiles very efficient + while (u != 0) + { + const unsigned int t = u % base; + *--s = t + '0'; + u /= base; + } + break; +#if 0 + // The generic case, not yet in use + default: + while (u != 0) + { + const unsigned int t = u % base; + if (t >= 10) + { + t += apBuf.flags.letBase - '0' - 10; + } + *--s = t + '0'; + u /= base; + } + break; +#endif + } + + if (neg) + { + if (apBuf.flags.width && apBuf.flags.padZero) + { + if (!strbuf_printchar(apBuf, '-')) + { + return false; + } + --apBuf.flags.width; + } + else + { + *--s = '-'; + } + } + + return prints(apBuf, s); +} + +/*-----------------------------------------------------------*/ + +// Print a number in scientific format +// apBuf.flags.printLimit is the number of decimal digits required +static bool printFloat(SStringBuf& apBuf, double d, char formatLetter) +{ + if (std::isnan(d)) + { + return prints(apBuf, "nan"); + } + if (std::isinf(d)) + { + return prints(apBuf, "inf"); + } + + double ud = fabs(d); + if (ud > (double)LONG_LONG_MAX && (formatLetter == 'f' || formatLetter == 'F')) + { + --formatLetter; // number is too big to print easily in fixed point format, so use exponent format + } + + int exponent = 0; + if (formatLetter == 'e' || formatLetter == 'E') + { + // Using exponent format, so calculate the exponent and normalise ud to be >=1.0 but <=10.0 + // The following loops are inefficient, however we don't expect to print very large or very small numbers + while (ud > (double)100000.0) + { + ud /= (double)100000.0; + exponent += 5; + } + while (ud > (double)10.0) + { + ud /= (double)10.0; + ++exponent; + } + if (ud != (double)0.0) + { + while (ud < (double)0.00001) + { + ud *= (double)100000.0; + exponent -= 5; + } + while (ud < (double)1.0) + { + ud *= (double)10.0; + --exponent; + } + } + // ud is now at least 1.0 but less than 10.0 and exponent is the exponent + } + + // Multiply ud by 10 to the power of the number of decimal digits required, or until it becomes too big to print easily + if (apBuf.flags.printLimit < 0) + { + apBuf.flags.printLimit = 6; // set the default number of decimal digits + } + int digitsAfterPoint = 0; + long limit = 10; + while (digitsAfterPoint < apBuf.flags.printLimit && ud < LONG_LONG_MAX/10 && limit <= LONG_LONG_MAX/10) + { + ud *= (double)10.0; + limit *= 10; + ++digitsAfterPoint; + } + + char print_buf[MaxUllDigits + MaxLongDigits + 5]; + char *s = print_buf + sizeof print_buf - 1; + *s = '\0'; + + long long u = llrint(ud); + + if (formatLetter == 'e' || formatLetter == 'E') + { + // Rounding ud may have caused 9.99999... to become 10 + if (ud >= limit) + { + ud /= 10; + ++exponent; + } + + // Store the exponent + int iexp = abs(exponent); + do + { + *--s = (iexp % 10) + '0'; + iexp = iexp/10; + } while (iexp != 0); + *--s = (exponent < 0) ? '-' : '+'; + *--s = formatLetter; + } + + // Store the non-exponent part + do + { + if (digitsAfterPoint == 0) + { + *--s = '.'; + } + --digitsAfterPoint; + + const lldiv_t lldiv_result = lldiv(u, 10); + *--s = (char)((unsigned int)lldiv_result.rem + '0'); + u = lldiv_result.quot; + } + while (u != 0 || digitsAfterPoint >= 0); + + if (d < (double)0.0) + { + if (apBuf.flags.width != 0 && apBuf.flags.padZero) + { + if (!strbuf_printchar(apBuf, '-')) + { + return false; + } + --apBuf.flags.width; + } + else + { + *--s = '-'; + } + } + + return prints(apBuf, s); +} + +/*-----------------------------------------------------------*/ + +static void tiny_print(SStringBuf& apBuf, const char *format, va_list args) +{ + for (;;) + { + char ch; + while ((ch = *format++) != '%') + { + if (!strbuf_printchar(apBuf, ch)) // note: this returns false if ch == 0 + { + return; + } + } + + // If we get here then ch == '%'. Get the next character. + ch = *format++; + if (ch == '\0') + { + break; + } + if (ch == '%') + { + if (strbuf_printchar(apBuf, ch) == 0) + { + return; + } + continue; + } + + apBuf.Init(); + + if (ch == '-') + { + ch = *format++; + apBuf.flags.padRight = true; + } + while (ch == '0') + { + ch = *format++; + apBuf.flags.padZero = true; + } + if (ch == '*') + { + ch = *format++; + apBuf.flags.width = va_arg(args, int); + } + else + { + while(ch >= '0' && ch <= '9') + { + apBuf.flags.width *= 10; + apBuf.flags.width += ch - '0'; + ch = *format++; + } + } + if (ch == '.') + { + ch = *format++; + if (ch == '*') + { + apBuf.flags.printLimit = va_arg(args, int); + ch = *format++; + } + else + { + while (ch >= '0' && ch <= '9') + { + apBuf.flags.printLimit *= 10; + apBuf.flags.printLimit += ch - '0'; + ch = *format++; + } + } + } + if (apBuf.flags.printLimit == 0) + { + apBuf.flags.printLimit = -1; // -1: make it unlimited + } + if (ch == 's') + { + const char *s = va_arg(args, const char *); + apBuf.flags.isString = true; + if (!prints(apBuf, (s != nullptr) ? s : "(null)")) + { + break; + } + continue; + } + if (ch == 'c') + { + // char are converted to int then pushed on the stack + if (!strbuf_printchar(apBuf, (char)va_arg(args, int))) + { + return; + } + + continue; + } + if (ch == 'l') + { + ch = *format++; + apBuf.flags.long32 = 1; + // Makes no difference as u32 == long + } + if (ch == 'L') + { + ch = *format++; + apBuf.flags.long64 = 1; + // Does make a difference + } + + if (ch == 'f' || ch == 'e' || ch == 'F' || ch == 'E') + { + if (!printFloat(apBuf, va_arg(args, double), ch)) + { + break; + } + continue; + } + + apBuf.flags.base = 10; + apBuf.flags.letBase = 'a'; + + if (ch == 'd' || ch == 'u' || ch == 'i') + { + apBuf.flags.isSigned = (ch != 'u'); + if (apBuf.flags.long64) + { + if (!printll(apBuf, va_arg(args, long long))) + { + break; + } + } + else if (!printi(apBuf, va_arg(args, int))) + { + break; + } + continue; + } + + apBuf.flags.base = 16; // from here all hexadecimal + if (ch == 'x' || ch == 'X' || ch == 'p' || ch == 'o') + { + if (ch == 'X') + { + apBuf.flags.letBase = 'A'; + } + else if (ch == 'o') + { + apBuf.flags.base = 8; + } + if (apBuf.flags.long64) + { + if (!printll(apBuf, va_arg(args, long long))) + { + break; + } + } + else if (!printi(apBuf, va_arg(args, int))) + { + break; + } + continue; + } + } + strbuf_printchar(apBuf, '\0'); +} + +/*-----------------------------------------------------------*/ + +int SafeVsnprintf(char *apBuf, size_t aMaxLen, const char *apFmt, va_list args) +{ + SStringBuf strBuf(apBuf, aMaxLen); + tiny_print(strBuf, apFmt, args); + return strBuf.curLen; +} + +int SafeSnprintf(char* buffer, size_t buf_size, const char* format, ...) +{ + va_list vargs; + va_start(vargs, format); + const int ret = SafeVsnprintf(buffer, buf_size, format, vargs); + va_end(vargs); + return ret; +} + +// End diff --git a/src/Libraries/General/SafeVsnprintf.h b/src/Libraries/General/SafeVsnprintf.h new file mode 100644 index 00000000..4a8db786 --- /dev/null +++ b/src/Libraries/General/SafeVsnprintf.h @@ -0,0 +1,20 @@ +/* + * SafeVsnprintf.h + * + * Created on: 8 Apr 2018 + * Author: David + */ + +#ifndef SRC_LIBRARIES_GENERAL_SAFEVSNPRINTF_H_ +#define SRC_LIBRARIES_GENERAL_SAFEVSNPRINTF_H_ + +#include <cstdarg> +#include <cstddef> + +int SafeVsnprintf(char *buffer, size_t maxLen, const char *format, va_list args); +int SafeSnprintf(char* buffer, size_t maxLen, const char* format, ...); + +#define vsnprintf(b, m, f, a) static_assert(false, "Do not use vsnprintf, use SafeVsnprintf instead") +#define snprintf(b, m, f, ...) static_assert(false, "Do not use snprintf, use SafeSnprintf instead") + +#endif /* SRC_LIBRARIES_GENERAL_SAFEVSNPRINTF_H_ */ diff --git a/src/Libraries/General/StringRef.cpp b/src/Libraries/General/StringRef.cpp index 5f9c99fe..a65306fb 100644 --- a/src/Libraries/General/StringRef.cpp +++ b/src/Libraries/General/StringRef.cpp @@ -9,6 +9,7 @@ #include <cstring> #include <cstdio> #include "WMath.h" +#include "SafeVsnprintf.h" #ifdef RTOS # include "RTOSIface.h" @@ -25,36 +26,6 @@ size_t strnlen(const char *s, size_t n) return rslt; } -// Thread safe version of vsnprintf. The standard one uses a buffer in the _reent structure. -extern "C" int SafeVsnprintf(char* buffer, size_t buf_size, const char* format, va_list vlist) -{ -#ifdef RTOS - TaskCriticalSectionLocker lock; -#endif - return vsnprintf(buffer, buf_size, format, vlist); -} - -extern "C" int SafeSnprintf(char* buffer, size_t buf_size, const char* format, ...) -{ - va_list vargs; - va_start(vargs, format); - const int ret = SafeVsnprintf(buffer, buf_size, format, vargs); - va_end(vargs); - return ret; -} - -extern "C" int SafeSscanf(const char* s, const char* format, ...) -{ - va_list vargs; - va_start(vargs, format); -#ifdef RTOS - TaskCriticalSectionLocker lock; -#endif - const int ret = vsscanf(s, format, vargs); - va_end(vargs); - return ret; -} - //************************************************************************************************* // StringRef class member implementations diff --git a/src/Libraries/General/StringRef.h b/src/Libraries/General/StringRef.h index c8800aaa..6c74fd71 100644 --- a/src/Libraries/General/StringRef.h +++ b/src/Libraries/General/StringRef.h @@ -15,11 +15,6 @@ // Need to declare strnlen here because it isn't ISO standard size_t strnlen(const char *s, size_t n); -// Thread safe versions of vsnprintf etc. -extern "C" int SafeVsnprintf(char* buffer, size_t buf_size, const char* format, va_list vlist); -extern "C" int SafeSnprintf(char* buffer, size_t buf_size, const char* format, ...) __attribute__ ((format (printf, 3, 4))); -extern "C" int SafeSscanf(const char* s, const char* format, ...) __attribute__ ((format (scanf, 2, 3))); - // Class to describe a string buffer, including its length. This saves passing buffer lengths around everywhere. class StringRef { diff --git a/src/Movement/BedProbing/Grid.cpp b/src/Movement/BedProbing/Grid.cpp index 31e39218..379a4a60 100644 --- a/src/Movement/BedProbing/Grid.cpp +++ b/src/Movement/BedProbing/Grid.cpp @@ -88,7 +88,7 @@ void GridDefinition::WriteHeadingAndParameters(const StringRef& s) const // Check the parameter label line, returning -1 if not recognised, else the version we found /*static*/ int GridDefinition::CheckHeading(const StringRef& s) { - for (size_t i =0; i < ARRAY_SIZE(HeightMapLabelLines); ++i) + for (size_t i = 0; i < ARRAY_SIZE(HeightMapLabelLines); ++i) { if (StringStartsWith(s.c_str(), HeightMapLabelLines[i])) { @@ -101,34 +101,82 @@ void GridDefinition::WriteHeadingAndParameters(const StringRef& s) const // Read the grid parameters from a string returning true if success bool GridDefinition::ReadParameters(const StringRef& s, int version) { - bool ok; - switch (version) + // 2018-04-08: rewrote this not to use sscanf because that function isn't thread safe + isValid = false; // assume failure + const char *p = s.c_str(); + const char *q; + + xMin = SafeStrtof(p, &q); + if (p == q || *q != ',') { - case 1: - ok = (SafeSscanf(s.c_str(), "%f,%f,%f,%f,%f,%f,%f,%lu,%lu", &xMin, &xMax, &yMin, &yMax, &radius, &xSpacing, &ySpacing, &numX, &numY) == 9); - break; + return false; + } + p = q + 1; - case 0: - ok = (SafeSscanf(s.c_str(), "%f,%f,%f,%f,%f,%f,%lu,%lu", &xMin, &xMax, &yMin, &yMax, &radius, &xSpacing, &numX, &numY) == 8); - if (ok) - { - ySpacing = xSpacing; - } - break; + xMax = SafeStrtof(p, &q); + if (p == q || *q != ',') + { + return false; + } + p = q + 1; - default: - ok = false; + yMin = SafeStrtof(p, &q); + if (p == q || *q != ',') + { + return false; } + p = q + 1; - if (ok) + yMax = SafeStrtof(p, &q); + if (p == q || *q != ',') { - CheckValidity(); + return false; + } + p = q + 1; + + radius = SafeStrtof(p, &q); + if (p == q || *q != ',') + { + return false; + } + p = q + 1; + + xSpacing = SafeStrtof(p, &q); + if (p == q || *q != ',') + { + return false; + } + p = q + 1; + + if (version == 0) + { + ySpacing = xSpacing; } else { - isValid = false; + ySpacing = SafeStrtof(p, &q); + if (p == q || *q != ',') + { + return false; + } + p = q + 1; + } + + numX = SafeStrtoul(p, &q); + if (p == q || *q != ',') + { + return false; + } + p = q + 1; + + numY = SafeStrtoul(p, &q); + if (p == q) + { + return false; } - return ok; + + CheckValidity(); + return true; } // Print what is wrong with the grid, appending it to the existing string @@ -209,7 +257,7 @@ unsigned int HeightMap::GetMinimumSegments(float deltaX, float deltaY) const } // Save the grid to file returning true if an error occurred -bool HeightMap::SaveToFile(FileStore *f) const +bool HeightMap::SaveToFile(FileStore *f, float zOffset) const { String<500> bufferSpace; const StringRef buf = bufferSpace.GetRef(); @@ -225,7 +273,7 @@ bool HeightMap::SaveToFile(FileStore *f) const } float mean, deviation; (void)GetStatistics(mean, deviation); - buf.catf(", mean error %.3f, deviation %.3f\n", (double)mean, (double)deviation); + buf.catf(", mean error %.3f, deviation %.3f\n", (double)(mean + zOffset), (double)deviation); if (!f->Write(buf.c_str())) { return true; @@ -251,7 +299,7 @@ bool HeightMap::SaveToFile(FileStore *f) const } if (IsHeightSet(index)) { - buf.catf("%7.3f", (double)gridHeights[index]); + buf.catf("%7.3f", (double)(gridHeights[index] + zOffset)); } else { @@ -322,6 +370,10 @@ bool HeightMap::LoadFromFile(FileStore *f, const StringRef& r) const char *p = buffer; for (uint32_t col = 0; col < def.numX; ++col) { + while (*p == ' ' || *p == '\t') + { + ++p; + } if (*p == '0' && (p[1] == ',' || p[1] == 0)) { // Values of 0 with no decimal places in un-probed values, so leave the point set as not valid @@ -329,8 +381,8 @@ bool HeightMap::LoadFromFile(FileStore *f, const StringRef& r) } else { - char* np = nullptr; - const float f = strtof(p, &np); + const char* np; + const float f = SafeStrtof(p, &np); if (np == p) { r.catf("number expected at line %" PRIu32 " column %d", row + 3, (p - buffer) + 1); diff --git a/src/Movement/BedProbing/Grid.h b/src/Movement/BedProbing/Grid.h index 13ce856f..6b1880b6 100644 --- a/src/Movement/BedProbing/Grid.h +++ b/src/Movement/BedProbing/Grid.h @@ -8,7 +8,6 @@ #ifndef SRC_MOVEMENT_GRID_H_ #define SRC_MOVEMENT_GRID_H_ -#include <cstdint> #include "RepRapFirmware.h" #include "Libraries/General/StringRef.h" @@ -68,7 +67,7 @@ public: void ClearGridHeights(); // Clear all grid height corrections void SetGridHeight(size_t xIndex, size_t yIndex, float height); // Set the height of a grid point - bool SaveToFile(FileStore *f) const // Save the grid to file returning true if an error occurred + bool SaveToFile(FileStore *f, float zOffset) const // Save the grid to file returning true if an error occurred pre(IsValid()); bool LoadFromFile(FileStore *f, const StringRef& r); // Load the grid from file returning true if an error occurred diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp index fedc39b4..8a06e4ac 100644 --- a/src/Movement/Move.cpp +++ b/src/Movement/Move.cpp @@ -3,10 +3,39 @@ * * Created on: 7 Dec 2014 * Author: David + + A note on bed levelling: + + As at version 1.21 we support two types of bed compensation: + 1. The old 3, 4 and 5-point compensation using a RandomProbePointSet. We will probably discontinue this soon. + 2. Mesh bed levelling + + There is an interaction between using G30 to home Z or set a precise Z=0 height just before a print, and bed compensation. + Consider the following sequence: + 1. Home Z, using either G30 or an endstop. + 2. Run G29 to generate a height map. If the Z=0 point has drifted off, the height map may have a Z offset. + 3. Use G30 to get an accurate Z=0 point. We want to keep the shape of the height map, but get rid of the offset. + 4. Run G29 to generate a height map. This should generate a height map with on offset at the point we just probed. + 5. Cancel bed compensation. The height at the point we just probed should be zero. + + So as well as maintaining a height map, we maintain a Z offset from it. The procedure is: + 1. Whenever bed compensation is not being used, the Z offset should be zero. + 2. Whenever we run G29 to probe the bed, we have a choice: + (a) accept that the map may have a height offset; and set the Z offset to zero. This is what we do currently. + (b) normalise the height map to zero, adjust the Z=0 origin, and set the Z offset to zero. + 3. When we run G30 to reset the Z=0 height, and we have a height map loaded, we adjust the Z offset to be the negative of the + height map indication of that point. + 4. If we now cancel the height map, we also clear the Z offset, and the height at the point we probed remains correct. + 5. If we now run G29 to probe again, the height map should have near zero offset at the point we probed, if there has been no drift. + + Before we introduced the Z offset, at step 4 we would have a potentially large Z error as if the G30 hadn't been run, + and at step 5 the new height map would have an offset again. + */ #include "Move.h" #include "Platform.h" +#include "Tools/Tool.h" static constexpr uint32_t UsualMinimumPreparedTime = DDA::stepClockRate/10; // 100ms static constexpr uint32_t AbsoluteMinimumPreparedTime = DDA::stepClockRate/20; // 50ms @@ -71,6 +100,7 @@ void Move::Init() usingMesh = false; useTaper = false; + zShift = 0.0; idleTimeout = DefaultIdleTimeout; moveState = MoveState::idle; @@ -731,6 +761,12 @@ void Move::AxisTransform(float xyzPoint[MaxAxes], AxesBitmap xAxes, AxesBitmap y } } +// Get the height error at an XY position +float Move::GetInterpolatedHeightError(float xCoord, float yCoord) const +{ + return (usingMesh) ? heightMap.GetInterpolatedHeightError(xCoord, yCoord) : probePoints.GetInterpolatedHeightError(xCoord, yCoord); +} + // Invert the Axis transform AFTER the bed transform void Move::InverseAxisTransform(float xyzPoint[MaxAxes], AxesBitmap xAxes, AxesBitmap yAxes) const { @@ -778,14 +814,7 @@ void Move::BedTransform(float xyzPoint[MaxAxes], AxesBitmap xAxes, AxesBitmap yA if (IsBitSet(yAxes, yAxis)) { const float yCoord = xyzPoint[yAxis]; - if (usingMesh) - { - zCorrection += heightMap.GetInterpolatedHeightError(xCoord, yCoord); - } - else - { - zCorrection += probePoints.GetInterpolatedHeightError(xCoord, yCoord); - } + zCorrection += GetInterpolatedHeightError(xCoord, yCoord); ++numCorrections; } } @@ -797,6 +826,7 @@ void Move::BedTransform(float xyzPoint[MaxAxes], AxesBitmap xAxes, AxesBitmap yA zCorrection /= numCorrections; // take an average } + zCorrection += zShift; xyzPoint[Z_AXIS] += (useTaper) ? (taperHeight - xyzPoint[Z_AXIS]) * recipTaperHeight * zCorrection : zCorrection; } } @@ -820,14 +850,7 @@ void Move::InverseBedTransform(float xyzPoint[MaxAxes], AxesBitmap xAxes, AxesBi if (IsBitSet(yAxes, yAxis)) { const float yCoord = xyzPoint[yAxis]; - if (usingMesh) - { - zCorrection += heightMap.GetInterpolatedHeightError(xCoord, yCoord); - } - else - { - zCorrection += probePoints.GetInterpolatedHeightError(xCoord, yCoord); - } + zCorrection += GetInterpolatedHeightError(xCoord, yCoord); ++numCorrections; } } @@ -839,6 +862,8 @@ void Move::InverseBedTransform(float xyzPoint[MaxAxes], AxesBitmap xAxes, AxesBi zCorrection /= numCorrections; // take an average } + zCorrection += zShift; + if (!useTaper || zCorrection >= taperHeight) // need check on zCorrection to avoid possible divide by zero { xyzPoint[Z_AXIS] -= zCorrection; @@ -853,12 +878,43 @@ void Move::InverseBedTransform(float xyzPoint[MaxAxes], AxesBitmap xAxes, AxesBi } } +// Normalise the bed transform to have zero height error at these coordinates +void Move::SetZeroHeightError(const float coords[MaxAxes]) +{ + float tempCoords[MaxAxes]; + memcpy(tempCoords, coords, sizeof(tempCoords)); + AxisTransform(tempCoords, DefaultXAxisMapping, DefaultYAxisMapping); + zShift = -GetInterpolatedHeightError(tempCoords[X_AXIS], tempCoords[Y_AXIS]); +} + void Move::SetIdentityTransform() { probePoints.SetIdentity(); heightMap.ClearGridHeights(); heightMap.UseHeightMap(false); usingMesh = false; + zShift = 0.0; +} + +// Load the height map from file, returning true if an error occurred with the error reason appended to the buffer +bool Move::LoadHeightMapFromFile(FileStore *f, const StringRef& r) +{ + const bool ret = heightMap.LoadFromFile(f, r); + if (ret) + { + heightMap.ClearGridHeights(); // make sure we don't end up with a partial height map + } + else + { + zShift = 0.0; + } + return ret; +} + +// Save the height map to a file returning true if an error occurred +bool Move::SaveHeightMapToFile(FileStore *f) const +{ + return heightMap.SaveToFile(f, zShift); } void Move::SetTaperHeight(float h) diff --git a/src/Movement/Move.h b/src/Movement/Move.h index d033e000..d9a83cb9 100644 --- a/src/Movement/Move.h +++ b/src/Movement/Move.h @@ -63,6 +63,7 @@ public: // Take a position and apply the bed and the axis-angle compensations void InverseAxisAndBedTransform(float move[], AxesBitmap xAxes, AxesBitmap yAxes) const; // Go from a transformed point back to user coordinates + void SetZeroHeightError(const float coords[MaxAxes]); // Set zero height error at these 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 @@ -113,6 +114,8 @@ public: void ResetMoveCounters() { scheduledMoves = completedMoves = 0; } HeightMap& AccessHeightMap() { return heightMap; } // Access the bed probing grid + bool LoadHeightMapFromFile(FileStore *f, const StringRef& r); // Load the height map from a file returning true if an error occurred + bool SaveHeightMapToFile(FileStore *f) const; // Save the height map to a file returning true if an error occurred const DDA *GetCurrentDDA() const { return currentDda; } // Return the DDA of the currently-executing move @@ -144,6 +147,7 @@ private: void AxisTransform(float move[MaxAxes], AxesBitmap xAxes, AxesBitmap yAxes) const; // Take a position and apply the axis-angle compensations void InverseAxisTransform(float move[MaxAxes], AxesBitmap xAxes, AxesBitmap yAxes) const; // Go from an axis transformed point back to user coordinates void SetPositions(const float move[DRIVES]); // Force the machine coordinates to be these + float GetInterpolatedHeightError(float xCoord, float yCoord) const; // Get the height error at an XY position bool DDARingAdd(); // Add a processed look-ahead entry to the DDA ring DDA* DDARingGet(); // Get the next DDA ring entry to be run @@ -177,13 +181,13 @@ private: float& tanYZ = tangents[1]; float& tanXZ = tangents[2]; - float recipTaperHeight; // Reciprocal of the taper height - bool useTaper; // True to taper off the compensation - HeightMap heightMap; // The grid definition in use and height map for G29 bed probing RandomProbePointSet probePoints; // G30 bed probe points - bool usingMesh; // true if we are using the height map, false if we are using the random probe point set float taperHeight; // Height over which we taper + float recipTaperHeight; // Reciprocal of the taper height + float zShift; // Height to add to the bed transform + bool usingMesh; // true if we are using the height map, false if we are using the random probe point set + bool useTaper; // True to taper off the compensation uint32_t idleTimeout; // How long we wait with no activity before we reduce motor currents to idle, in milliseconds uint32_t lastStateChangeTime; // The approximate time at which the state last changed, except we don't record timing->idle diff --git a/src/Networking/NetworkResponder.h b/src/Networking/NetworkResponder.h index 6bda80e3..b43c02b9 100644 --- a/src/Networking/NetworkResponder.h +++ b/src/Networking/NetworkResponder.h @@ -13,7 +13,7 @@ #include "NetworkDefs.h" #include "Storage/FileData.h" #include "NetworkBuffer.h" -#include "Outputmemory.h" +#include "OutputMemory.h" // Forward declarations class NetworkResponder; diff --git a/src/Platform.cpp b/src/Platform.cpp index 01431f09..719a6252 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -620,6 +620,7 @@ void Platform::InitZProbe() case ZProbeType::digital: case ZProbeType::unfilteredDigital: case ZProbeType::blTouch: + case ZProbeType::zMotorStall: default: AnalogInEnableChannel(zProbeAdcChannel, false); pinMode(zProbePin, INPUT_PULLUP); @@ -671,6 +672,19 @@ int Platform::GetZProbeReading() const zProbeVal = GetRawZProbeReading()/4; break; + case ZProbeType::zMotorStall: +#if HAS_STALL_DETECT + { + const bool stalled = (reprap.GetMove().GetKinematics().GetKinematicsType() == KinematicsType::coreXZ) + ? AnyAxisMotorStalled(X_AXIS) || AnyAxisMotorStalled(Z_AXIS) + : AnyAxisMotorStalled(Z_AXIS); + zProbeVal = (stalled) ? 1000 : 0; + } +#else + zProbeVal = 1000; +#endif + break; + default: return 0; } @@ -760,6 +774,7 @@ const ZProbe& Platform::GetZProbeParameters(ZProbeType probeType) const case ZProbeType::digital: case ZProbeType::unfilteredDigital: case ZProbeType::blTouch: + case ZProbeType::zMotorStall: return irZProbeParameters; case ZProbeType::alternateAnalog: return alternateZProbeParameters; @@ -780,6 +795,7 @@ void Platform::SetZProbeParameters(ZProbeType probeType, const ZProbe& params) case ZProbeType::digital: case ZProbeType::unfilteredDigital: case ZProbeType::blTouch: + case ZProbeType::zMotorStall: irZProbeParameters = params; break; @@ -1510,13 +1526,9 @@ void Platform::Spin() reported = true; } #elif defined(DUET_NG) - if ( -# if defined(DUET_WIFI) - board == BoardType::DuetWiFi_102 -# elif defined(DUET_ETHERNET) - board == BoardType::DuetEthernet_102 -# endif - && digitalRead(VssaSensePin)) + if ( (board == BoardType::DuetWiFi_102 || board == BoardType::DuetEthernet_102) + && digitalRead(VssaSensePin) + ) { Message(ErrorMessage, "VSSA fault, check thermistor wiring\n"); reported = true; @@ -3175,6 +3187,13 @@ void Platform::EnableSharedFan(bool enable) #endif +// Check if the given fan can be controlled manually so that DWC can decide whether or not to show the corresponding fan +// controls. This is the case if no thermostatic control is enabled and if the fan was configured at least once before. +bool Platform::IsFanControllable(size_t fan) const +{ + return (fan < NUM_FANS) ? (!fans[fan].HasMonitoredHeaters() && fans[fan].IsConfigured()) : false; +} + // Get current fan RPM float Platform::GetFanRPM() const { @@ -3963,50 +3982,11 @@ bool Platform::SetExtrusionAncilliaryPwmPin(LogicalPin logicalPin, bool invert) return extrusionAncilliaryPwmPort.Set(logicalPin, PinAccess::pwm, invert); } -// CNC and laser support -void Platform::SetSpindlePwm(float pwm) -{ - if (pwm >= 0) - { - spindleReversePort.WriteAnalog(0.0); - spindleForwardPort.WriteAnalog(pwm); - } - else - { - spindleForwardPort.WriteAnalog(0.0); - spindleReversePort.WriteAnalog(-pwm); - } -} - void Platform::SetLaserPwm(float pwm) { laserPort.WriteAnalog(pwm); } -bool Platform::SetSpindlePins(LogicalPin lpf, LogicalPin lpr, bool invert) -{ - const bool ok1 = spindleForwardPort.Set(lpf, PinAccess::pwm, invert); - if (lpr == NoLogicalPin) - { - spindleReversePort.Clear(); - return ok1; - } - const bool ok2 = spindleReversePort.Set(lpr, PinAccess::pwm, invert); - return ok1 && ok2; -} - -void Platform::GetSpindlePins(LogicalPin& lpf, LogicalPin& lpr, bool& invert) const -{ - lpf = spindleForwardPort.GetLogicalPin(invert); - lpr = spindleReversePort.GetLogicalPin(); -} - -void Platform::SetSpindlePwmFrequency(float freq) -{ - spindleForwardPort.SetFrequency(freq); - spindleReversePort.SetFrequency(freq); -} - bool Platform::SetLaserPin(LogicalPin lp, bool invert) { return laserPort.Set(lp, PinAccess::pwm, invert); diff --git a/src/Platform.h b/src/Platform.h index 2cd061e4..9c9477d2 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -38,6 +38,7 @@ Licence: GPL #include "Storage/FileData.h" #include "Storage/MassStorage.h" // must be after Pins.h because it needs NumSdCards defined #include "MessageType.h" +#include "Spindle.h" #include "ZProbe.h" #include "ZProbeProgrammer.h" @@ -390,35 +391,35 @@ public: // Movement void EmergencyStop(); - void SetDirection(size_t drive, bool direction); + void SetDirection(size_t axisOrExtruder, bool direction); void SetDirectionValue(size_t driver, bool dVal); bool GetDirectionValue(size_t driver) const; void SetEnableValue(size_t driver, int8_t eVal); bool GetEnableValue(size_t driver) const; void EnableDriver(size_t driver); void DisableDriver(size_t driver); - void EnableDrive(size_t drive); - void DisableDrive(size_t drive); + void EnableDrive(size_t axisOrExtruder); + void DisableDrive(size_t axisOrExtruder); void DisableAllDrives(); void SetDriversIdle(); - void SetMotorCurrent(size_t drive, float current, int code); - float GetMotorCurrent(size_t drive, int code) const; + void SetMotorCurrent(size_t axisOrExtruder, float current, int code); + float GetMotorCurrent(size_t axisOrExtruder, int code) const; void SetIdleCurrentFactor(float f); float GetIdleCurrentFactor() const { return idleCurrentFactor; } bool SetDriverMicrostepping(size_t driver, unsigned int microsteps, int mode); unsigned int GetDriverMicrostepping(size_t drive, int mode, bool& interpolation) const; - bool SetMicrostepping(size_t drive, int microsteps, int mode); - unsigned int GetMicrostepping(size_t drive, int mode, bool& interpolation) const; + bool SetMicrostepping(size_t axisOrExtruder, int microsteps, int mode); + unsigned int GetMicrostepping(size_t axisOrExtruder, int mode, bool& interpolation) const; void SetDriverStepTiming(size_t driver, const float microseconds[4]); void GetDriverStepTiming(size_t driver, float microseconds[4]) const; - float DriveStepsPerUnit(size_t drive) const; + float DriveStepsPerUnit(size_t axisOrExtruder) const; const float *GetDriveStepsPerUnit() const { return driveStepsPerUnit; } - void SetDriveStepsPerUnit(size_t drive, float value); - float Acceleration(size_t drive) const; + void SetDriveStepsPerUnit(size_t axisOrExtruder, float value); + float Acceleration(size_t axisOrExtruder) const; const float* Accelerations() const; - void SetAcceleration(size_t drive, float value); + void SetAcceleration(size_t axisOrExtruder, float value); float GetMaxPrintingAcceleration() const { return maxPrintingAcceleration; } void SetMaxPrintingAcceleration(float acc) @@ -427,19 +428,19 @@ public: { return maxTravelAcceleration; } void SetMaxTravelAcceleration(float acc) { maxTravelAcceleration = acc; } - float MaxFeedrate(size_t drive) const; + float MaxFeedrate(size_t axisOrExtruder) const; const float* MaxFeedrates() const; - void SetMaxFeedrate(size_t drive, float value); - float GetInstantDv(size_t drive) const; - void SetInstantDv(size_t drive, float value); - EndStopHit Stopped(size_t drive) const; - bool EndStopInputState(size_t drive) const; + void SetMaxFeedrate(size_t axisOrExtruder, float value); + float GetInstantDv(size_t axis) const; + void SetInstantDv(size_t axis, float value); + EndStopHit Stopped(size_t axisOrExtruder) const; + bool EndStopInputState(size_t axis) const; float AxisMaximum(size_t axis) const; void SetAxisMaximum(size_t axis, float value, bool byProbing); float AxisMinimum(size_t axis) const; void SetAxisMinimum(size_t axis, float value, bool byProbing); float AxisTotalLength(size_t axis) const; - float GetPressureAdvance(size_t drive) const; + float GetPressureAdvance(size_t extruder) const; void SetPressureAdvance(size_t extruder, float factor); void SetEndStopConfiguration(size_t axis, EndStopPosition endstopPos, EndStopInputType inputType) @@ -450,13 +451,13 @@ public: uint32_t GetAllEndstopStates() const; void SetAxisDriversConfig(size_t axis, const AxisDriversConfig& config); - const AxisDriversConfig& GetAxisDriversConfig(size_t drive) const - { return axisDrivers[drive]; } + const AxisDriversConfig& GetAxisDriversConfig(size_t axis) const + { return axisDrivers[axis]; } void SetExtruderDriver(size_t extruder, uint8_t driver); uint8_t GetExtruderDriver(size_t extruder) const { return extruderDrivers[extruder]; } - uint32_t GetDriversBitmap(size_t drive) const // get the bitmap of driver step bits for this axis or extruder - { return driveDriverBits[drive]; } + uint32_t GetDriversBitmap(size_t axisOrExtruder) const // get the bitmap of driver step bits for this axis or extruder + { return driveDriverBits[axisOrExtruder]; } static void StepDriversLow(); // set all step pins low static void StepDriversHigh(uint32_t driverMap); // set the specified step pins high uint32_t GetSlowDriversBitmap() const { return slowDriversBitmap; } @@ -515,6 +516,7 @@ public: #if defined(DUET_06_085) void EnableSharedFan(bool enable); // enable/disable the fan that shares its PWM pin with the last heater #endif + bool IsFanControllable(size_t fan) const; bool WriteFanSettings(FileStore *f) const; // Save some resume information float GetFanRPM() const; @@ -585,14 +587,9 @@ public: void ExtrudeOff(); // CNC and laser support - void SetSpindlePwm(float pwm); - void SetLaserPwm(float pwm); - - bool SetSpindlePins(LogicalPin lpf, LogicalPin lpr, bool invert); - void GetSpindlePins(LogicalPin& lpf, LogicalPin& lpr, bool& invert) const; - void SetSpindlePwmFrequency(float freq); - float GetSpindlePwmFrequency() const { return spindleForwardPort.GetFrequency(); } + Spindle& AccessSpindle(size_t slot) { return spindles[slot]; } + void SetLaserPwm(float pwm); bool SetLaserPin(LogicalPin lp, bool invert); LogicalPin GetLaserPin(bool& invert) const { return laserPort.GetLogicalPin(invert); } void SetLaserPwmFrequency(float freq); @@ -906,9 +903,10 @@ private: uint32_t timeLastUpdatedMillis; // the milliseconds counter when we last incremented the time // CNC and laser support + Spindle spindles[MaxSpindles]; float extrusionAncilliaryPwmValue; PwmPort extrusionAncilliaryPwmPort; - PwmPort spindleForwardPort, spindleReversePort, laserPort; + PwmPort laserPort; // Power on/off bool deferredPowerDown; @@ -1190,6 +1188,7 @@ inline uint16_t Platform::GetRawZProbeReading() const return (b) ? 4000 : 0; } + case ZProbeType::zMotorStall: default: return 4000; } diff --git a/src/RepRap.cpp b/src/RepRap.cpp index 0321ee37..143201f2 100644 --- a/src/RepRap.cpp +++ b/src/RepRap.cpp @@ -1028,9 +1028,27 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) } #endif - // Spindle - const double spindleRpm = gCodes->GetSpindleRpm(); - response->catf(",\"spindle\":{\"current\":%1.f,\"active\":%1.f}", spindleRpm, spindleRpm); + // Spindles + response->cat(",\"spindles\":["); + for (size_t i = 0; i < MaxSpindles; i++) + { + if (i > 0) + { + response->cat(','); + } + + const Spindle& spindle = platform->AccessSpindle(i); + response->catf("{\"current\":%1.f,\"active\":%1.f", (double)spindle.GetCurrentRpm(), (double)spindle.GetRpm()); + if (type == 2) + { + response->catf(",\"tool\":%d}", spindle.GetToolNumber()); + } + else + { + response->cat('}'); + } + } + response->cat(']'); /* Extended Status Response */ if (type == 2) @@ -1039,6 +1057,17 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) response->catf(",\"coldExtrudeTemp\":%1.f", (double)(heat->ColdExtrude() ? 0.0 : HOT_ENOUGH_TO_EXTRUDE)); response->catf(",\"coldRetractTemp\":%1.f", (double)(heat->ColdExtrude() ? 0.0 : HOT_ENOUGH_TO_RETRACT)); + // Controllable Fans + FansBitmap controllableFans = 0; + for (size_t fan = 0; fan < NUM_FANS; fan++) + { + if (platform->IsFanControllable(fan)) + { + SetBit(controllableFans, fan); + } + } + response->catf(",\"controllableFans\":%lu", controllableFans); + // Maximum hotend temperature - DWC just wants the highest one response->catf(",\"tempLimit\":%1.f", (double)(heat->GetHighestTemperatureLimit())); diff --git a/src/RepRapFirmware.h b/src/RepRapFirmware.h index 92c3039e..25fe4bf2 100644 --- a/src/RepRapFirmware.h +++ b/src/RepRapFirmware.h @@ -35,6 +35,8 @@ typedef uint16_t PwmFrequency; // type used to represent a PWM frequency. 0 som #include "Configuration.h" #include "Pins.h" +#include "Libraries/General/SafeStrtod.h" +#include "Libraries/General/SafeVsnprintf.h" #include "Libraries/General/StringRef.h" // Module numbers and names, used for diagnostics and debug diff --git a/src/Scanner.cpp b/src/Scanner.cpp index 919151ab..493090dd 100644 --- a/src/Scanner.cpp +++ b/src/Scanner.cpp @@ -258,7 +258,7 @@ void Scanner::ProcessCommand() // Progress indicator: PROGRESS <PERCENT> else if (StringStartsWith(buffer, "PROGRESS ")) { - float parsedProgress = atof(&buffer[9]); + const float parsedProgress = SafeStrtof(&buffer[9]); progress = constrain<float>(parsedProgress, 0.0f, 100.0f); } diff --git a/src/Spindle.cpp b/src/Spindle.cpp new file mode 100644 index 00000000..7c4f531b --- /dev/null +++ b/src/Spindle.cpp @@ -0,0 +1,57 @@ +/* + * Spindle.cpp + * + * Created on: Mar 21, 2018 + * Author: Christian + */ + +#include "Spindle.h" + +bool Spindle::SetPins(LogicalPin lpf, LogicalPin lpr, bool invert) +{ + const bool ok1 = spindleForwardPort.Set(lpf, PinAccess::pwm, invert); + if (lpr == NoLogicalPin) + { + spindleReversePort.Clear(); + return ok1; + } + const bool ok2 = spindleReversePort.Set(lpr, PinAccess::pwm, invert); + return ok1 && ok2; +} + +void Spindle::GetPins(LogicalPin& lpf, LogicalPin& lpr, bool& invert) const +{ + lpf = spindleForwardPort.GetLogicalPin(invert); + lpr = spindleReversePort.GetLogicalPin(); +} + +void Spindle::SetPwmFrequency(float freq) +{ + spindleReversePort.SetFrequency(freq); + spindleForwardPort.SetFrequency(freq); +} + +void Spindle::SetRpm(float rpm) +{ + const float pwm = abs(rpm / maxRpm); + if (rpm >= 0.0) + { + spindleReversePort.WriteAnalog(0.0); + spindleForwardPort.WriteAnalog(pwm); + } + else + { + spindleReversePort.WriteAnalog(pwm); + spindleForwardPort.WriteAnalog(0.0); + } + currentRpm = configuredRpm = rpm; +} + +void Spindle::TurnOff() +{ + spindleReversePort.WriteAnalog(0.0); + spindleForwardPort.WriteAnalog(0.0); + currentRpm = 0.0; +} + +// End diff --git a/src/Spindle.h b/src/Spindle.h new file mode 100644 index 00000000..5e05993c --- /dev/null +++ b/src/Spindle.h @@ -0,0 +1,41 @@ +/* + * Spindle.h + * + * Created on: Mar 21, 2018 + * Author: Christian + */ + +#ifndef SPINDLE_H +#define SPINDLE_H + +#include "RepRapFirmware.h" +#include "IoPorts.h" + +class Spindle +{ +private: + PwmPort spindleForwardPort, spindleReversePort; + bool inverted; + float currentRpm, configuredRpm, maxRpm; + int toolNumber; + +public: + Spindle() : inverted(false), currentRpm(0.0), configuredRpm(0.0), maxRpm(DefaultMaxSpindleRpm), toolNumber(-1) { } + + bool SetPins(LogicalPin lpr, LogicalPin lpf, bool invert); + void GetPins(LogicalPin& lpf, LogicalPin& lpr, bool& invert) const; + + int GetToolNumber() const { return toolNumber; } + void SetToolNumber(int tool) { toolNumber = tool; } + + void SetPwmFrequency(float freq); + void SetMaxRpm(float max) { maxRpm = max; } + + float GetCurrentRpm() const { return currentRpm; } + float GetRpm() const { return configuredRpm; } + void SetRpm(float rpm); + + void TurnOff(); +}; + +#endif diff --git a/src/Storage/FileInfoParser.cpp b/src/Storage/FileInfoParser.cpp index b2f50937..af87f5c1 100644 --- a/src/Storage/FileInfoParser.cpp +++ b/src/Storage/FileInfoParser.cpp @@ -382,7 +382,7 @@ bool FileInfoParser::FindFirstLayerHeight(const char* buf, size_t len, float& he if (buf[i] == 'Z') { //debugPrintf("Found at offset %u text: %.100s\n", i, &buf[i + 1]); - const float flHeight = strtof(&buf[i + 1], nullptr); + const float flHeight = SafeStrtof(&buf[i + 1], nullptr); if ((height == 0.0 || flHeight < height) && (flHeight <= reprap.GetPlatform().GetNozzleDiameter() * 3.0)) { height = flHeight; // Only report first Z height if it's somewhat reasonable @@ -475,7 +475,7 @@ bool FileInfoParser::FindHeight(const char* buf, size_t len, float& height) cons } else { - height = strtof(zpos, nullptr); + height = SafeStrtof(zpos, nullptr); foundHeight = true; } break; // carry on looking for a later G1 Z command @@ -500,7 +500,7 @@ bool FileInfoParser::FindHeight(const char* buf, size_t len, float& height) cons static const char kisslicerHeightString[] = " END_LAYER_OBJECT z="; if (len > 31 && StringStartsWith(buf, kisslicerHeightString)) { - height = strtof(buf + sizeof(kisslicerHeightString)/sizeof(char) - 1, nullptr); + height = SafeStrtof(buf + sizeof(kisslicerHeightString)/sizeof(char) - 1, nullptr); return true; } } @@ -542,8 +542,8 @@ bool FileInfoParser::FindLayerHeight(const char *buf, size_t len, float& layerHe { ++pos; } - char *tailPtr; - const float val = strtof(pos, &tailPtr); + const char *tailPtr; + const float val = SafeStrtof(pos, &tailPtr); if (tailPtr != pos) // if we found and converted a number { layerHeight = val; @@ -627,8 +627,8 @@ unsigned int FileInfoParser::FindFilamentUsed(const char* buf, size_t len, float } while (isDigit(*p)) { - char* q; - filamentUsed[filamentsFound] = strtof(p, &q); + const char* q; + filamentUsed[filamentsFound] = SafeStrtof(p, &q); p = q; if (*p == 'm') { @@ -656,8 +656,8 @@ unsigned int FileInfoParser::FindFilamentUsed(const char* buf, size_t len, float while (filamentsFound < maxFilaments && (p = strstr(p, filamentUsedStr2)) != nullptr) { p += strlen(filamentUsedStr2); - char *q; - unsigned int num = strtoul(p, &q, 10); + const char *q; + unsigned long num = SafeStrtoul(p, &q); if (q != p && num < maxFilaments) { p = q; @@ -667,7 +667,7 @@ unsigned int FileInfoParser::FindFilamentUsed(const char* buf, size_t len, float } if (isDigit(*p)) { - filamentUsed[filamentsFound] = strtof(p, &q); + filamentUsed[filamentsFound] = SafeStrtof(p, nullptr); ++filamentsFound; } } @@ -687,7 +687,7 @@ unsigned int FileInfoParser::FindFilamentUsed(const char* buf, size_t len, float } if (isDigit(*p)) { - filamentUsed[filamentsFound] = strtof(p, nullptr); // S3D reports filament usage in mm, no conversion needed + filamentUsed[filamentsFound] = SafeStrtof(p, nullptr); // S3D reports filament usage in mm, no conversion needed ++filamentsFound; } } @@ -712,7 +712,7 @@ unsigned int FileInfoParser::FindFilamentUsed(const char* buf, size_t len, float if (isDigit(*p)) { - filamentUsed[filamentsFound] = strtof(p, nullptr); + filamentUsed[filamentsFound] = SafeStrtof(p, nullptr); ++filamentsFound; } } @@ -725,7 +725,7 @@ unsigned int FileInfoParser::FindFilamentUsed(const char* buf, size_t len, float p = strstr(buf, filamentVolumeStr); if (p != nullptr) { - const float filamentCMM = strtof(p + strlen(filamentVolumeStr), nullptr) * 1000.0; + const float filamentCMM = SafeStrtof(p + strlen(filamentVolumeStr), nullptr) * 1000.0; filamentUsed[filamentsFound++] = filamentCMM / (Pi * fsquare(reprap.GetPlatform().GetFilamentWidth() / 2.0)); } } diff --git a/src/Version.h b/src/Version.h index e9fafac7..7a63d15b 100644 --- a/src/Version.h +++ b/src/Version.h @@ -15,11 +15,11 @@ #endif #ifndef VERSION -# define VERSION "2.0" RTOSVER "alpha1" +# define VERSION "2.0" RTOSVER "beta1" #endif #ifndef DATE -# define DATE "2018-04-05b2" +# define DATE "2018-04-10b1" #endif #define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman" diff --git a/src/ZProbe.h b/src/ZProbe.h index 0ebea544..75c43f80 100644 --- a/src/ZProbe.h +++ b/src/ZProbe.h @@ -22,7 +22,8 @@ enum class ZProbeType : uint8_t zSwitch = 7, unfilteredDigital = 8, blTouch = 9, - numTypes = 10 // must be 1 higher than the last type + zMotorStall = 10, + numTypes = 11 // must be 1 higher than the last type }; class ZProbe |