Version 1.17rc1

- Added support for M374 (save height map) and M375 (load height map)
- Added M376 (set bed compensation taper height)
- Added T parameter to G31 command
- M500, M501 and M502 now use config_override.g instead of flash memory.
The parameters saved and restored are:
-- M307 auto tune results
-- PID parameters, if you used M301 to override the auto tune PID
settings
-- Delta printer M665 and M666 settings
-- G31 trigger height, trigger value and X and Y offsets
- The M501 auto save option has been removed
- Removed S and T parameters from M301 command. Use M307 command
instead.
- M301 with negative P parameter no longer sets bang-bang mode. Use M307
instead.
- Added P parameter to the G31 command to specify Z probe type. This
allows you to view the parameters for the Z probe(s) and to set
parameters for a particular Z probe type without selecting that type.
G31 P or G31 P0 prints the parameters of the currently-selected Z probe.
- Z probe offsets are now applied during G30 probing with specified XY
coordinates, including during delta auto calibration
- Z probe recovery time is now applied from the end of the travel move
just before probing
- Fixed bad dive height when using G29 with a large trigger height
- Fixed bad JSON message during printing when there were no active
extruders
- Added exception handlers and store a software reset code when an
exception occurs
- Fixed reset reason text because on the Duet WiFi a watchdog reset can
look like an external reset
- G30 S-1 how printes the stopped height
- Implemented M401 and M402
This commit is contained in:
David Crocker 2016-12-12 15:27:06 +00:00
parent 4896bba4e8
commit cd5af6c7b1
30 changed files with 893 additions and 737 deletions

View file

@ -284,10 +284,7 @@
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/asf/sam/utils/header_files}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/asf/sam/utils/preprocessor}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/asf/thirdparty/CMSIS/Include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/variants/duet}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Duet/Lwip}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Duet/Lwip/lwip/src/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Duet/EMAC}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/variants/RADDS}&quot;"/>
</option>
<option id="gnu.c.compiler.option.preprocessor.def.symbols.2017188375" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols">
<listOptionValue builtIn="false" value="__SAM3X8E__"/>
@ -298,15 +295,15 @@
</tool>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.1692168928" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1755453550" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
<tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} ${workspace_loc:/${CoreName}/SAM3X8E/cores/arduino/syscalls.o} ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1176271302" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${LINK_FLAGS_1} ${workspace_loc:/${CoreName}/RADDS/cores/arduino/syscalls.o} ${INPUTS} ${LINK_FLAGS_2}" id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1176271302" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
<option id="gnu.cpp.link.option.nostdlibs.706270025" name="No startup or default libs (-nostdlib)" superClass="gnu.cpp.link.option.nostdlibs" value="false" valueType="boolean"/>
<option id="gnu.cpp.link.option.paths.1160723414" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/SAM3X8E/}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/RADDS/}&quot;"/>
</option>
<option id="gnu.cpp.link.option.libs.1006761104" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" valueType="libs">
<listOptionValue builtIn="false" value="${CoreName}"/>
</option>
<option id="gnu.cpp.link.option.flags.827167716" name="Linker flags" superClass="gnu.cpp.link.option.flags" value="-Os -Wl,--gc-sections -Wl,--fatal-warnings -mcpu=cortex-m3 -T${workspace_loc:/${CoreName}/variants/duet/linker_scripts/gcc/flash.ld} -Wl,-Map,${workspace_loc:/${ProjName}/${ConfigName}}/${ProjName}.map" valueType="string"/>
<option id="gnu.cpp.link.option.flags.827167716" name="Linker flags" superClass="gnu.cpp.link.option.flags" value="-Os -Wl,--gc-sections -Wl,--fatal-warnings -mcpu=cortex-m3 -T${workspace_loc:/${CoreName}/variants/RADDS/linker_scripts/gcc/flash.ld} -Wl,-Map,${workspace_loc:/${ProjName}/${ConfigName}}/${ProjName}.map" valueType="string"/>
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.99895855" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
@ -337,11 +334,9 @@
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/asf/sam/utils/header_files}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/asf/sam/utils/preprocessor}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/asf/thirdparty/CMSIS/Include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/variants/duet}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${CoreName}/variants/RADDS}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Duet}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Duet/Lwip}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/Duet/EMAC}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/src/RADDS}&quot;"/>
</option>
<option id="gnu.cpp.compiler.option.preprocessor.def.1179781669" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols">
<listOptionValue builtIn="false" value="__SAM3X8E__"/>
@ -352,8 +347,20 @@
</tool>
</toolChain>
</folderInfo>
<folderInfo id="cdt.managedbuild.config.gnu.cross.exe.release.516195201.976458850.1027429289.62271758" name="/" resourcePath="src/Libraries/MCP4461">
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.exe.release.1207187116" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.exe.release" unusedChildren="">
<option id="cdt.managedbuild.option.gnu.cross.path.2092504710.568185483" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path.2092504710"/>
<option id="cdt.managedbuild.option.gnu.cross.prefix.1606498191.1764935754" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix.1606498191"/>
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1617689584" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler.764246283"/>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.21931543" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler.2077096750"/>
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.324302204" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker.1692168928"/>
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.2147196606" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker.1176271302"/>
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.785177243" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver.1755453550"/>
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.1614859543" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler.863511428"/>
</toolChain>
</folderInfo>
<sourceEntries>
<entry excluding="src/Duet|src/Duet/Lwip/lwip/src/core/ipv6|src/DuetNG|src/Duet/Lwip/lwip/test" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
<entry excluding="src/Libraries/MCP4461|src/Duet|src/Duet/Lwip/lwip/src/core/ipv6|src/DuetNG|src/Duet/Lwip/lwip/test" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
</sourceEntries>
</configuration>
</storageModule>

Binary file not shown.

Binary file not shown.

View file

@ -28,11 +28,11 @@ Licence: GPL
// Firmware name is now defined in the Pins file
#ifndef VERSION
# define VERSION "1.17dev8"
# define VERSION "1.17RC1"
#endif
#ifndef DATE
# define DATE "2016-12-09"
# define DATE "2016-12-12"
#endif
#define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman"

View file

@ -690,7 +690,7 @@ void Webserver::HttpInterpreter::DoFastUpload()
// Update the file timestamp if it was specified before
if (fileLastModified != 0)
{
(void)platform->GetMassStorage()->SetLastModifiedTime(filename, fileLastModified);
(void)platform->GetMassStorage()->SetLastModifiedTime(nullptr, filename, fileLastModified);
}
// Eventually send the JSON response

View file

@ -229,7 +229,7 @@ void Webserver::FinishUpload(HttpSession& session)
else if (session.fileLastModified != 0)
{
// Upload OK, update the file timestamp if it was specified before
(void)platform->GetMassStorage()->SetLastModifiedTime(session.filenameBeingUploaded, session.fileLastModified);
(void)platform->GetMassStorage()->SetLastModifiedTime(nullptr, session.filenameBeingUploaded, session.fileLastModified);
}
}

View file

@ -13,7 +13,7 @@ unsigned int GCodeMachineState::numAllocated = 0;
// Create a default initialised GCodeMachineState
GCodeMachineState::GCodeMachineState()
: previous(nullptr), feedrate(DEFAULT_FEEDRATE/minutesToSeconds), fileState(), lockedResources(0), state(GCodeState::normal),
drivesRelative(false), axesRelative(false), doingFileMacro(false)
drivesRelative(false), axesRelative(false), doingFileMacro(false), runningM502(false)
{
}

View file

@ -43,8 +43,10 @@ enum class GCodeState : uint8_t
flashing2,
stopping,
sleeping,
// These next 5 must be contiguous
gridProbing1,
gridProbing2,
gridProbing2a,
gridProbing3,
gridProbing4
};
@ -64,7 +66,8 @@ public:
drivesRelative : 1,
axesRelative : 1,
doingFileMacro : 1,
waitWhileCooling : 1;
waitWhileCooling : 1,
runningM502 : 1;
static GCodeMachineState *Allocate()
post(!result.IsLive(); result.state == GCodeState::normal);

View file

@ -84,7 +84,6 @@ void GCodes::Init()
eofStringCounter = 0;
eofStringLength = strlen(eofString);
offSetSet = false;
zProbesSet = false;
active = true;
longWait = platform->Time();
dwellTime = longWait;
@ -158,8 +157,6 @@ void GCodes::Reset()
{
resourceOwners[i] = nullptr;
}
lastProbedTime = millis();
}
float GCodes::FractionOfFilePrinted() const
@ -175,8 +172,8 @@ bool GCodes::RunConfigFile(const char* fileName)
return DoFileMacro(*daemonGCode, fileName, false);
}
// Are we still running the config file?
bool GCodes::IsRunningConfigFile() const
// Return true if the daemon is busy running config.g or a trigger file
bool GCodes::IsDaemonBusy() const
{
return daemonGCode->MachineState().fileState.IsLive();
}
@ -229,7 +226,7 @@ void GCodes::Spin()
if ((toBeHomed & (1u << axis)) != 0 && (axis != Z_AXIS || toBeHomed == (1u << Z_AXIS)))
{
toBeHomed &= ~(1u << axis);
DoFileMacro(gb, HomingFileNames[axis]);
DoFileMacro(gb, HomingFileNames[axis], true);
break;
}
}
@ -250,7 +247,6 @@ void GCodes::Spin()
probeCount++;
if (probeCount >= numProbePoints)
{
zProbesSet = true;
reprap.GetMove()->FinishedBedProbing(0, reply);
gb.SetState(GCodeState::normal);
}
@ -304,7 +300,7 @@ void GCodes::Spin()
if (LockMovementAndWaitForStandstill(gb))
{
gb.SetState(GCodeState::pausing2);
DoFileMacro(gb, PAUSE_G);
DoFileMacro(gb, PAUSE_G, true);
}
break;
@ -445,13 +441,13 @@ void GCodes::Spin()
moveBuffer.endStopsToCheck = 0;
moveBuffer.usePressureAdvance = false;
moveBuffer.filePos = noFilePosition;
moveBuffer.coords[X_AXIS] = x - platform->GetZProbeParameters().xOffset;
moveBuffer.coords[Y_AXIS] = y - platform->GetZProbeParameters().yOffset;
moveBuffer.coords[Z_AXIS] = platform->GetZProbeDiveHeight();
moveBuffer.coords[X_AXIS] = x - platform->GetCurrentZProbeParameters().xOffset;
moveBuffer.coords[Y_AXIS] = y - platform->GetCurrentZProbeParameters().yOffset;
moveBuffer.coords[Z_AXIS] = platform->GetZProbeStartingHeight();
moveBuffer.feedRate = platform->GetZProbeTravelSpeed();
moveBuffer.xAxes = 0;
segmentsLeft = 1;
gb.SetState(GCodeState::gridProbing2);
gb.AdvanceState();
}
else
{
@ -461,9 +457,15 @@ void GCodes::Spin()
break;
case GCodeState::gridProbing2: // ready to probe the current grid probe point
if (LockMovementAndWaitForStandstill(gb)
&& millis() - lastProbedTime >= (uint32_t)(reprap.GetPlatform()->GetZProbeParameters().recoveryTime * SecondsToMillis)
)
if (LockMovementAndWaitForStandstill(gb))
{
lastProbedTime = millis();
gb.AdvanceState();
}
break;
case GCodeState::gridProbing2a: // ready to probe the current grid probe point
if (millis() - lastProbedTime >= (uint32_t)(reprap.GetPlatform()->GetCurrentZProbeParameters().recoveryTime * SecondsToMillis))
{
// Probe the bed at the current XY coordinates
// Check for probe already triggered at start
@ -481,7 +483,7 @@ void GCodes::Spin()
moveBuffer.usePressureAdvance = false;
moveBuffer.filePos = noFilePosition;
moveBuffer.coords[Z_AXIS] = -platform->GetZProbeDiveHeight();
moveBuffer.feedRate = platform->GetZProbeParameters().probeSpeed;
moveBuffer.feedRate = platform->GetCurrentZProbeParameters().probeSpeed;
moveBuffer.xAxes = 0;
segmentsLeft = 1;
gb.SetState(GCodeState::gridProbing3);
@ -499,19 +501,15 @@ void GCodes::Spin()
break;
}
lastProbedTime = millis();
const float heightError = moveBuffer.coords[Z_AXIS] - platform->ZProbeStopHeight();
reprap.GetMove()->AccessBedProbeGrid().SetGridHeight(gridXindex, gridYindex, heightError);
++numPointsProbed;
heightSum += (double)heightError;
heightSquaredSum += (double)heightError * (double)heightError;
// Move back up to the dive height
moveBuffer.moveType = 0;
moveBuffer.endStopsToCheck = 0;
moveBuffer.usePressureAdvance = false;
moveBuffer.filePos = noFilePosition;
moveBuffer.coords[Z_AXIS] = platform->GetZProbeDiveHeight();
moveBuffer.coords[Z_AXIS] = platform->GetZProbeStartingHeight();
moveBuffer.feedRate = platform->GetZProbeTravelSpeed();
moveBuffer.xAxes = 0;
segmentsLeft = 1;
@ -550,13 +548,13 @@ void GCodes::Spin()
if (gridYindex == grid.NumYpoints())
{
// Finished probing the grid
float mean, deviation;
const uint32_t numPointsProbed = reprap.GetMove()->AccessBedProbeGrid().GetStatistics(mean, deviation);
if (numPointsProbed >= 4)
{
error = SaveHeightMapToFile(reply);
const double mean = heightSum/numPointsProbed;
const double deviation = sqrt(((heightSquaredSum * numPointsProbed) - (heightSum * heightSum)))/numPointsProbed;
reply.catf(" - %u points probed, mean error %.2f, deviation %.2f", numPointsProbed, mean, deviation);
reprap.GetMove()->UseHeightMap(true);
reply.printf("%u points probed, mean error %.2f, deviation %.2f\n", numPointsProbed, mean, deviation);
error = SaveHeightMap(gb, reply);
reprap.GetMove()->AccessBedProbeGrid().UseHeightMap(true);
}
else
{
@ -783,7 +781,7 @@ void GCodes::CheckTriggers()
DoEmergencyStop();
}
else if (lowestTriggerPending < MaxTriggers // if a trigger is pending
&& !daemonGCode->MachineState().fileState.IsLive()
&& !IsDaemonBusy()
&& daemonGCode->GetState() == GCodeState::normal // and we are not already executing a trigger or config.g
)
{
@ -1061,9 +1059,10 @@ unsigned int GCodes::LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType)
{
mappedMoveArg -= currentTool->GetOffset()[mappedAxis]; // adjust requested position to compensate for tool offset
}
if (reprap.GetMove()->UsingHeightMap())
const HeightMap& heightMap = reprap.GetMove()->AccessBedProbeGrid();
if (heightMap.UsingHeightMap())
{
const unsigned int minSegments = reprap.GetMove()->AccessBedProbeGrid().GetMinimumSegments(fabs(mappedMoveArg - moveBuffer.coords[mappedAxis]));
const unsigned int minSegments = heightMap.GetMinimumSegments(fabs(mappedMoveArg - moveBuffer.coords[mappedAxis]));
if (minSegments > numSegments)
{
numSegments = minSegments;
@ -1084,12 +1083,16 @@ unsigned int GCodes::LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType)
moveArg -= currentTool->GetOffset()[axis]; // adjust requested position to compensate for tool offset
}
if (axis < Z_AXIS && moveType == 0 && reprap.GetMove()->UsingHeightMap())
if (axis < Z_AXIS && moveType == 0)
{
const unsigned int minSegments = reprap.GetMove()->AccessBedProbeGrid().GetMinimumSegments(fabs(moveArg - moveBuffer.coords[axis]));
if (minSegments > numSegments)
const HeightMap& heightMap = reprap.GetMove()->AccessBedProbeGrid();
if (heightMap.UsingHeightMap())
{
numSegments = minSegments;
const unsigned int minSegments = reprap.GetMove()->AccessBedProbeGrid().GetMinimumSegments(fabs(moveArg - moveBuffer.coords[axis]));
if (minSegments > numSegments)
{
numSegments = minSegments;
}
}
}
moveBuffer.coords[axis] = moveArg;
@ -1296,7 +1299,7 @@ void GCodes::ClearMove()
// Run a file macro. Prior to calling this, 'state' must be set to the state we want to enter when the macro has been completed.
// Return true if the file was found or it wasn't and we were asked to report that fact.
bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing)
bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, bool runningM502)
{
FileStore * const f = platform->GetFileStore(platform->GetSysDir(), fileName, false);
if (f == nullptr)
@ -1316,6 +1319,7 @@ bool GCodes::DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissi
}
gb.MachineState().fileState.Set(f);
gb.MachineState().doingFileMacro = true;
gb.MachineState().runningM502 = runningM502;
gb.SetState(GCodeState::normal);
gb.Init();
return true;
@ -1524,7 +1528,7 @@ bool GCodes::DoHome(GCodeBuffer& gb, StringRef& reply, bool& error)
if (reprap.GetMove()->IsDeltaMode())
{
SetAllAxesNotHomed();
DoFileMacro(gb, HOME_DELTA_G);
DoFileMacro(gb, HOME_DELTA_G, true);
}
else
{
@ -1542,7 +1546,7 @@ bool GCodes::DoHome(GCodeBuffer& gb, StringRef& reply, bool& error)
{
// Homing everything
SetAllAxesNotHomed();
DoFileMacro(gb, HOME_ALL_G);
DoFileMacro(gb, HOME_ALL_G, true);
}
else if ( platform->MustHomeXYBeforeZ()
&& ((toBeHomed & (1u << Z_AXIS)) != 0)
@ -1577,7 +1581,7 @@ bool GCodes::DoSingleZProbeAtPoint(GCodeBuffer& gb, int probePointIndex, float h
switch (cannedCycleMoveCount)
{
case 0: // Move Z to the dive height. This only does anything on the first move; on all the others Z is already there
cannedMoveCoords[Z_AXIS] = platform->GetZProbeDiveHeight() + max<float>(platform->ZProbeStopHeight(), 0.0);
cannedMoveCoords[Z_AXIS] = platform->GetZProbeStartingHeight();
cannedMoveType[Z_AXIS] = CannedMoveType::absolute;
cannedFeedRate = platform->GetZProbeTravelSpeed();
if (DoCannedCycleMove(gb, 0))
@ -1587,19 +1591,20 @@ bool GCodes::DoSingleZProbeAtPoint(GCodeBuffer& gb, int probePointIndex, float h
return false;
case 1: // Move to the correct XY coordinates
GetProbeCoordinates(probePointIndex, cannedMoveCoords[X_AXIS], cannedMoveCoords[Y_AXIS], cannedMoveCoords[Z_AXIS]);
(void)reprap.GetMove()->GetProbeCoordinates(probePointIndex, cannedMoveCoords[X_AXIS], cannedMoveCoords[Y_AXIS], true);
cannedMoveType[X_AXIS] = CannedMoveType::absolute;
cannedMoveType[Y_AXIS] = CannedMoveType::absolute;
// NB - we don't use the Z value
cannedFeedRate = platform->GetZProbeTravelSpeed();
if (DoCannedCycleMove(gb, 0))
{
lastProbedTime = millis();
cannedCycleMoveCount++;
}
return false;
case 2: // Probe the bed
if (millis() - lastProbedTime >= (uint32_t)(platform->GetZProbeParameters().recoveryTime * SecondsToMillis))
if (millis() - lastProbedTime >= (uint32_t)(platform->GetCurrentZProbeParameters().recoveryTime * SecondsToMillis))
{
const float height = (GetAxisIsHomed(Z_AXIS))
? 2 * platform->GetZProbeDiveHeight() // Z axis has been homed, so no point in going very far
@ -1622,7 +1627,6 @@ bool GCodes::DoSingleZProbeAtPoint(GCodeBuffer& gb, int probePointIndex, float h
case 2:
// Successful probing
lastProbedTime = millis();
if (GetAxisIsHomed(Z_AXIS))
{
lastProbedZ = moveBuffer.coords[Z_AXIS] - (platform->ZProbeStopHeight() + heightAdjust);
@ -1646,7 +1650,7 @@ bool GCodes::DoSingleZProbeAtPoint(GCodeBuffer& gb, int probePointIndex, float h
return false;
case 3: // Raise the head back up to the dive height
cannedMoveCoords[Z_AXIS] = platform->GetZProbeDiveHeight() + max<float>(platform->ZProbeStopHeight(), 0.0);
cannedMoveCoords[Z_AXIS] = platform->GetZProbeStartingHeight();
cannedMoveType[Z_AXIS] = CannedMoveType::absolute;
cannedFeedRate = platform->GetZProbeTravelSpeed();
if (DoCannedCycleMove(gb, 0))
@ -1664,7 +1668,7 @@ bool GCodes::DoSingleZProbeAtPoint(GCodeBuffer& gb, int probePointIndex, float h
// This simply moves down till the Z probe/switch is triggered. Call it repeatedly until it returns true.
// Called when we do a G30 with no P parameter.
bool GCodes::DoSingleZProbe(GCodeBuffer& gb, bool reportOnly, float heightAdjust)
bool GCodes::DoSingleZProbe(GCodeBuffer& gb, StringRef& reply, bool reportOnly, float heightAdjust)
{
switch (DoZProbe(gb, 1.1 * platform->AxisTotalLength(Z_AXIS)))
{
@ -1677,7 +1681,13 @@ bool GCodes::DoSingleZProbe(GCodeBuffer& gb, bool reportOnly, float heightAdjust
return true;
case 2: // success
if (!reportOnly)
if (reportOnly)
{
float m[DRIVES];
reprap.GetMove()->GetCurrentMachinePosition(m, false);
reply.printf("Stopped at height %.3f mm", m[Z_AXIS]);
}
else
{
moveBuffer.coords[Z_AXIS] = platform->ZProbeStopHeight() + heightAdjust;
SetPositions(moveBuffer.coords);
@ -1700,7 +1710,7 @@ int GCodes::DoZProbe(GCodeBuffer& gb, float distance)
{
if (platform->GetZProbeType() == ZProbeTypeDelta)
{
const ZProbeParameters& params = platform->GetZProbeParameters();
const ZProbeParameters& params = platform->GetCurrentZProbeParameters();
return reprap.GetMove()->DoDeltaProbe(params.extraParam, 1.0, params.probeSpeed, distance); //TODO second parameter
}
else
@ -1723,7 +1733,7 @@ int GCodes::DoZProbe(GCodeBuffer& gb, float distance)
cannedMoveCoords[Z_AXIS] = -distance;
cannedMoveType[Z_AXIS] = CannedMoveType::relative;
cannedFeedRate = platform->GetZProbeParameters().probeSpeed;
cannedFeedRate = platform->GetCurrentZProbeParameters().probeSpeed;
if (DoCannedCycleMove(gb, ZProbeActive))
{
@ -1756,24 +1766,20 @@ bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer& gb, StringRef& reply)
if (!gb.Seen('P'))
{
bool reportOnly = false;
if (gb.Seen('S') && gb.GetIValue() < 0)
{
reportOnly = true;
}
return DoSingleZProbe(gb, reportOnly, heightAdjust);
const bool reportOnly = (gb.Seen('S') && gb.GetIValue() < 0);
return DoSingleZProbe(gb, reply, reportOnly, heightAdjust);
}
int probePointIndex = gb.GetIValue();
const int probePointIndex = gb.GetIValue();
if (probePointIndex < 0 || (unsigned int)probePointIndex >= MaxProbePoints)
{
reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Z probe point index out of range.\n");
return true;
}
float x = (gb.Seen(axisLetters[X_AXIS])) ? gb.GetFValue() : moveBuffer.coords[X_AXIS];
float y = (gb.Seen(axisLetters[Y_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Y_AXIS];
float z = (gb.Seen(axisLetters[Z_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Z_AXIS];
const float x = (gb.Seen(axisLetters[X_AXIS])) ? gb.GetFValue() : moveBuffer.coords[X_AXIS];
const float y = (gb.Seen(axisLetters[Y_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Y_AXIS];
const float z = (gb.Seen(axisLetters[Z_AXIS])) ? gb.GetFValue() : moveBuffer.coords[Z_AXIS];
reprap.GetMove()->SetXBedProbePoint(probePointIndex, x);
reprap.GetMove()->SetYBedProbePoint(probePointIndex, y);
@ -1783,7 +1789,6 @@ bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer& gb, StringRef& reply)
reprap.GetMove()->SetZBedProbePoint(probePointIndex, z, false, false);
if (gb.Seen('S'))
{
zProbesSet = true;
reprap.GetMove()->FinishedBedProbing(gb.GetIValue(), reply);
}
return true;
@ -1794,8 +1799,7 @@ bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer& gb, StringRef& reply)
{
if (gb.Seen('S'))
{
zProbesSet = true;
int sParam = gb.GetIValue();
const int sParam = gb.GetIValue();
if (sParam == 1)
{
// G30 with a silly Z value and S=1 is equivalent to G30 with no parameters in that it sets the current Z height
@ -1816,20 +1820,18 @@ bool GCodes::SetSingleZProbeAtAPosition(GCodeBuffer& gb, StringRef& reply)
return false;
}
// This returns the (X, Y) points to probe the bed at probe point count. When probing, it returns false.
// If called after probing has ended it returns true, and the Z coordinate probed is also returned.
bool GCodes::GetProbeCoordinates(int count, float& x, float& y, float& z) const
{
const ZProbeParameters& rp = platform->GetZProbeParameters();
x = reprap.GetMove()->XBedProbePoint(count) - rp.xOffset;
y = reprap.GetMove()->YBedProbePoint(count) - rp.yOffset;
z = reprap.GetMove()->ZBedProbePoint(count);
return zProbesSet;
}
// Set or print the Z probe. Called by G31.
// Note that G31 P or G31 P0 prints the parameters of the currently-selected Z probe.
bool GCodes::SetPrintZProbe(GCodeBuffer& gb, StringRef& reply)
{
ZProbeParameters params = platform->GetZProbeParameters();
int32_t zProbeType = 0;
bool seenT = false;
gb.TryGetIValue('T',zProbeType, seenT);
if (zProbeType == 0)
{
zProbeType = platform->GetZProbeType();
}
ZProbeParameters params = platform->GetZProbeParameters(zProbeType);
bool seen = false;
gb.TryGetFValue(axisLetters[X_AXIS], params.xOffset, seen);
gb.TryGetFValue(axisLetters[Y_AXIS], params.yOffset, seen);
@ -1853,11 +1855,20 @@ bool GCodes::SetPrintZProbe(GCodeBuffer& gb, StringRef& reply)
if (seen)
{
platform->SetZProbeParameters(params);
if (!LockMovementAndWaitForStandstill(gb))
{
return false;
}
platform->SetZProbeParameters(zProbeType, params);
}
else if (seenT)
{
// Don't bother printing temperature coefficient and calibration temperature because we will probably remove them soon
reply.printf("Threshold %d, trigger height %.2f, offsets X%.1f Y%.1f", params.adcValue, params.height, params.xOffset, params.yOffset);
}
else
{
const int v0 = platform->ZProbe();
const int v0 = platform->GetZProbeReading();
int v1, v2;
switch (platform->GetZProbeSecondaryValues(v1, v2))
{
@ -1879,8 +1890,6 @@ bool GCodes::SetPrintZProbe(GCodeBuffer& gb, StringRef& reply)
// Called when we see an M557 command with no P parameter
bool GCodes::DefineGrid(GCodeBuffer& gb, StringRef &reply)
{
reprap.GetMove()->UseHeightMap(false);
bool seenX = false, seenY = false, seenR = false, seenS = false;
float xValues[2];
float yValues[2];
@ -1963,7 +1972,7 @@ bool GCodes::DefineGrid(GCodeBuffer& gb, StringRef &reply)
return true;
}
}
GridDefinition newGrid(xValues, yValues, radius, spacing); // create a new grid
GridDefinition newGrid(xValues, yValues, radius, spacing); // create a new grid
if (newGrid.IsValid())
{
reprap.GetMove()->AccessBedProbeGrid().SetGrid(newGrid);
@ -1981,100 +1990,90 @@ bool GCodes::DefineGrid(GCodeBuffer& gb, StringRef &reply)
// Prior to calling this the movement system must be locked.
bool GCodes::ProbeGrid(GCodeBuffer& gb, StringRef& reply)
{
int32_t sParam = 0;
bool dummy;
gb.TryGetIValue('S', sParam, dummy);
Move * const move = reprap.GetMove();
if (!move->AccessBedProbeGrid().GetGrid().IsValid())
{
reply.copy("No valid grid defined for G29 bed probing");
return true;
}
if (!AllAxesAreHomed())
{
reply.copy("Must home printer before G29 bed probing");
return true;
}
gridXindex = gridYindex = 0;
HeightMap& heightMap = move->AccessBedProbeGrid();
heightMap.UseHeightMap(false);
heightMap.ClearGridHeights();
move->SetIdentityTransform();
gb.SetState(GCodeState::gridProbing1);
return false;
}
bool GCodes::LoadHeightMap(GCodeBuffer& gb, StringRef& reply) const
{
const char* heightMapFileName;
if (gb.Seen('P'))
{
heightMapFile = gb.GetString();
heightMapFileName = gb.GetString();
}
else
{
heightMapFile = DefaultHeightMapFile;
heightMapFileName = DefaultHeightMapFile;
}
FileStore * const f = platform->GetFileStore(platform->GetSysDir(), heightMapFileName, false);
Move * const move = reprap.GetMove();
switch(sParam)
if (f == nullptr)
{
case 0: // Probe the bed and save to file
if (!move->AccessBedProbeGrid().GetGrid().IsValid())
{
reply.copy("No valid grid defined for G29 bed probing");
return true;
}
if (!AllAxesAreHomed())
{
reply.copy("Must home printer before G29 bed probing");
return true;
}
gridXindex = gridYindex = 0;
numPointsProbed = 0;
heightSum = heightSquaredSum = 0.0;
move->AccessBedProbeGrid().ClearGridHeights();
move->UseHeightMap(false);
move->SetIdentityTransform();
gb.SetState(GCodeState::gridProbing1);
return false;
case 1: // Load height map from file
{
const char* locHeightMapFileName;
if (gb.Seen('P'))
{
locHeightMapFileName = gb.GetString();
}
else
{
locHeightMapFileName = DefaultHeightMapFile;
}
FileStore * const f = platform->GetFileStore(platform->GetSysDir(), locHeightMapFileName, false);
if (f == nullptr)
{
reply.printf("Height map file %s not found", locHeightMapFileName);
return true;
}
reply.printf("Failed to load height map from file %s: ", heightMapFile); // set up error message to append to
const bool err = move->AccessBedProbeGrid().LoadFromFile(f, reply);
f->Close();
if (err)
{
move->AccessBedProbeGrid().ClearGridHeights(); // make sure we don't end up with a partial height map
}
else
{
reply.Clear(); // wipe the error message
}
move->UseHeightMap(!err);
return err;
}
case 2: // Clear height map
move->AccessBedProbeGrid().ClearGridHeights();
move->UseHeightMap(false);
return false;
default:
reply.copy("Invalid S parameter in G29 command");
reply.printf("Height map file %s not found", heightMapFileName);
return true;
}
reply.printf("Failed to load height map from file %s: ", heightMapFileName); // set up error message to append to
HeightMap& heightMap = reprap.GetMove()->AccessBedProbeGrid();
const bool err = heightMap.LoadFromFile(f, reply);
f->Close();
if (err)
{
heightMap.ClearGridHeights(); // make sure we don't end up with a partial height map
}
else
{
reply.Clear(); // wipe the error message
}
heightMap.UseHeightMap(!err);
return err;
}
// Save the height map and write the success or error message to 'reply', returning true if an error occurred
bool GCodes::SaveHeightMapToFile(StringRef& reply) const
// Save the height map and append the success or error message to 'reply', returning true if an error occurred
// Called by G29 and M374. Both use the P parameter to provide the filename.
bool GCodes::SaveHeightMap(GCodeBuffer& gb, StringRef& reply) const
{
Platform *platform = reprap.GetPlatform();
FileStore * const f = platform->GetFileStore(platform->GetSysDir(), heightMapFile, true);
const char* heightMapFileName;
if (gb.Seen('P'))
{
heightMapFileName = gb.GetString();
if (heightMapFileName[0] == 0)
{
reply.cat("No height map file name provided");
return false; // no file name provided, which is legitimate for G29
}
}
else
{
heightMapFileName = DefaultHeightMapFile;
}
Platform * const platform = reprap.GetPlatform();
FileStore * const f = platform->GetFileStore(platform->GetSysDir(), heightMapFileName, true);
bool err;
if (f == nullptr)
{
reply.printf("Failed to create height map file %s", heightMapFile);
reply.catf("Failed to create height map file %s", heightMapFileName);
err = true;
}
else
@ -2083,17 +2082,25 @@ bool GCodes::SaveHeightMapToFile(StringRef& reply) const
f->Close();
if (err)
{
platform->GetMassStorage()->Delete(platform->GetSysDir(), heightMapFile);
reply.printf("Failed to save height map to file %s", heightMapFile);
platform->GetMassStorage()->Delete(platform->GetSysDir(), heightMapFileName);
reply.catf("Failed to save height map to file %s", heightMapFileName);
}
else
{
reply.printf("Height map saved to file %s", heightMapFile);
reply.catf("Height map saved to file %s", heightMapFileName);
}
}
return err;
}
// Clear the height map
void GCodes::ClearHeightMap() const
{
HeightMap& heightMap = reprap.GetMove()->AccessBedProbeGrid();
heightMap.ClearGridHeights();
heightMap.UseHeightMap(false);
}
// 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(StringRef& s) const
@ -2868,22 +2875,28 @@ void GCodes::SetPidParameters(GCodeBuffer& gb, int heater, StringRef& reply)
if (heater >= 0 && heater < HEATERS)
{
PidParameters pp = platform->GetPidParameters(heater);
const FopDt& model = reprap.GetHeat()->GetHeaterModel(heater);
M301PidParameters pp = model.GetM301PidParameters(false);
bool seen = false;
gb.TryGetFValue('P', pp.kP, seen);
gb.TryGetFValue('I', pp.kI, seen);
gb.TryGetFValue('D', pp.kD, seen);
gb.TryGetFValue('T', pp.kT, seen);
gb.TryGetFValue('S', pp.kS, seen);
if (seen)
{
platform->SetPidParameters(heater, pp);
reprap.GetHeat()->UseModel(heater, false);
reprap.GetHeat()->SetM301PidParameters(heater, pp);
}
else if (!model.UsePid())
{
reply.printf("Heater %d is in bang-bang mode", heater);
}
else if (model.ArePidParametersOverridden())
{
reply.printf("Heater %d P:%.1f I:%.3f D:%.1f", heater, pp.kP, pp.kI, pp.kD);
}
else
{
reply.printf("Heater %d P:%.2f I:%.3f D:%.2f T:%.2f S:%.2f", heater, pp.kP, pp.kI, pp.kD, pp.kT, pp.kS);
reply.printf("Heater %d uses model-derived PID parameters. Use M307 H%d to view them", heater, heater);
}
}
}
@ -3217,6 +3230,41 @@ void GCodes::SetAllAxesNotHomed()
axesHomed = 0;
}
// Write the config-override file returning true if an error occurred
bool GCodes::WriteConfigOverrideFile(StringRef& reply, const char *fileName) const
{
FileStore * const f = platform->GetFileStore(platform->GetSysDir(), fileName, true);
if (f == nullptr)
{
reply.printf("Failed to create file %s", fileName);
return true;
}
bool ok = f->Write("; This is a system-generated file - do not edit\n");
if (ok)
{
ok = reprap.GetMove()->GetDeltaParams().WriteParameters(f);
}
if (ok)
{
ok = reprap.GetHeat()->WriteModelParameters(f);
}
if (ok)
{
ok = platform->WriteZProbeParameters(f);
}
if (!f->Close())
{
ok = false;
}
if (!ok)
{
reply.printf("Failed to write file %s", fileName);
platform->GetMassStorage()->Delete(platform->GetSysDir(), fileName);
}
return !ok;
}
// Resource locking/unlocking
// Lock the resource, returning true if success.

View file

@ -83,14 +83,13 @@ public:
void ClearMove();
void QueueFileToPrint(const char* fileName); // Open a file of G Codes to run
void DeleteFile(const char* fileName); // Does what it says
bool GetProbeCoordinates(int count, float& x, float& y, float& z) const; // Get pre-recorded probe coordinates
void GetCurrentCoordinates(StringRef& s) const; // Write where we are into a string
bool DoingFileMacro() const; // Or still busy processing a macro file?
float FractionOfFilePrinted() const; // Get fraction of file printed
void Diagnostics(MessageType mtype); // Send helpful information out
bool RunConfigFile(const char* fileName); // Start running the config file
bool IsRunningConfigFile() const; // Are we still running the config file?
bool IsDaemonBusy() const; // Return true if the daemon is busy running config.g or a trigger file
bool GetAxisIsHomed(unsigned int axis) const // Has the axis been homed?
{ return (axesHomed & (1 << axis)) != 0; }
@ -155,7 +154,8 @@ private:
void StartNextGCode(GCodeBuffer& gb, StringRef& reply); // Fetch a new or old GCode and process it
void DoFilePrint(GCodeBuffer& gb, StringRef& reply); // Get G Codes from a file and print them
bool DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing = true); // Run a GCode macro in a file, optionally report error if not found
bool DoFileMacro(GCodeBuffer& gb, const char* fileName, bool reportMissing, bool runningM502 = false);
// Run a GCode macro file, optionally report error if not found
bool DoCannedCycleMove(GCodeBuffer& gb, EndstopChecks ce); // Do a move from an internally programmed canned cycle
void FileMacroCyclesReturn(GCodeBuffer& gb); // End a macro
bool ActOnCode(GCodeBuffer& gb, StringRef& reply); // Do a G, M or T Code
@ -167,7 +167,7 @@ private:
bool DoDwellTime(float dwell); // Really wait for a bit
bool DoHome(GCodeBuffer& gb, StringRef& reply, bool& error); // Home some axes
bool DoSingleZProbeAtPoint(GCodeBuffer& gb, int probePointIndex, float heightAdjust); // Probe at a given point
bool DoSingleZProbe(GCodeBuffer& gb, bool reportOnly, float heightAdjust); // Probe where we are
bool DoSingleZProbe(GCodeBuffer& gb, StringRef& reply, bool reportOnly, float heightAdjust); // Probe where we are
int DoZProbe(GCodeBuffer& gb, float distance); // Do a Z probe cycle up to the maximum specified distance
bool SetSingleZProbeAtAPosition(GCodeBuffer& gb, StringRef& reply); // Probes at a given position - see the comment at the head of the function itself
void SetBedEquationWithProbe(int sParam, StringRef& reply); // Probes a series of points and sets the bed equation
@ -211,7 +211,11 @@ private:
bool DefineGrid(GCodeBuffer& gb, StringRef &reply); // Define the probing grid, returning true if error
bool ProbeGrid(GCodeBuffer& gb, StringRef& reply); // Start probing the grid, returning true if we didn't because of an error
bool SaveHeightMapToFile(StringRef& reply) const; // Save the height map to file
bool LoadHeightMap(GCodeBuffer& gb, StringRef& reply) const; // Load the height map from file
bool SaveHeightMap(GCodeBuffer& gb, StringRef& reply) const; // Save the height map to file
void ClearHeightMap() const; // Clear the height map
bool WriteConfigOverrideFile(StringRef& reply, const char *fileName) const; // Write the config-override file
static uint32_t LongArrayToBitMap(const long *arr, size_t numEntries); // Convert an array of longs to a bit map
@ -272,12 +276,8 @@ private:
// Z probe
float lastProbedZ; // the last height at which the Z probe stopped
uint32_t lastProbedTime; // time in milliseconds that the probe was last triggered
bool zProbesSet; // True if all Z probing is done and we can set the bed equation
volatile bool zProbeTriggered; // Set by the step ISR when a move is aborted because the Z probe is triggered
size_t gridXindex, gridYindex; // Which grid probe point is next
size_t numPointsProbed;
double heightSum, heightSquaredSum;
const char *heightMapFile;
float simulationTime; // Accumulated simulation time
uint8_t simulationMode; // 0 = not simulating, 1 = simulating, >1 are simulation modes for debugging

View file

@ -18,6 +18,9 @@ const char* RESUME_G = "resume.g";
const char* CANCEL_G = "cancel.g";
const char* STOP_G = "stop.g";
const char* SLEEP_G = "sleep.g";
const char* CONFIG_OVERRIDE_G = "config-override.g";
const char* DEPLOYPROBE_G = "deployprobe.g";
const char* RETRACTPROBE_G = "retractprobe.g";
const float MinServoPulseWidth = 544.0, MaxServoPulseWidth = 2400.0;
const uint16_t ServoRefreshFrequency = 50;
@ -66,6 +69,10 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
{
return true; // we only simulate some gcodes
}
if (gb.MachineState().runningM502 && code != 31)
{
return true; // when running M502 the only gcode we execute is G31
}
switch (code)
{
@ -162,7 +169,23 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
{
return false;
}
error = ProbeGrid(gb, reply);
{
const int sparam = (gb.Seen('S')) ? gb.GetIValue() : 0;
switch(sparam)
{
case 0: // probe and save height map
error = ProbeGrid(gb, reply);
break;
case 1: // load height map file
error = LoadHeightMap(gb, reply);
break;
default: // clear height map
reprap.GetMove()->AccessBedProbeGrid().ClearGridHeights();
break;
}
}
break;
case 30: // Z probe/manually set at a position and set that as point P
@ -182,10 +205,6 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
break;
case 31: // Return the probe value, or set probe variables
if (!LockMovementAndWaitForStandstill(gb))
{
return false;
}
result = SetPrintZProbe(gb, reply);
break;
@ -247,6 +266,10 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
{
return true; // we don't yet simulate most M codes
}
if (gb.MachineState().runningM502 && code != 301 && code != 307 && code != 558 && code != 665 && code != 666)
{
return true; // when running M502 the only mcodes we execute are 301, 307, 558, 665 and 666
}
switch (code)
{
@ -489,7 +512,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
if (isPaused)
{
gb.SetState(GCodeState::resuming1);
DoFileMacro(gb, RESUME_G);
DoFileMacro(gb, RESUME_G, true);
}
else if (!fileToPrint.IsLive())
{
@ -843,7 +866,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
case 98: // Call Macro/Subprogram
if (gb.Seen('P'))
{
DoFileMacro(gb, gb.GetString());
DoFileMacro(gb, gb.GetString(), true);
}
break;
@ -1557,10 +1580,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
break;
case 205: //M205 advanced settings: minimum travel speed S=while printing T=travel only, B=minimum segment time X= maximum xy jerk, Z=maximum Z jerk
// This is superseded in this firmware by M codes for the separate types (e.g. M566).
break;
case 206: // Offset axes - Deprecated
result = OffsetAxes(gb);
break;
@ -1826,11 +1845,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
if (seen)
{
if (reprap.GetHeat()->SetHeaterModel(heater, gain, tc, td, maxPwm, dontUsePid == 0))
{
reprap.GetHeat()->UseModel(heater, true);
}
else
if (!reprap.GetHeat()->SetHeaterModel(heater, gain, tc, td, maxPwm, dontUsePid == 0))
{
reply.copy("Error: bad model parameters");
}
@ -1841,21 +1856,18 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
else
{
reply.printf("Heater %u model: gain %.1f, time constant %.1f, dead time %.1f, max PWM %.2f, in use: %s, mode: %s",
heater, model.GetGain(), model.GetTimeConstant(), model.GetDeadTime(), model.GetMaxPwm(),
(reprap.GetHeat()->IsModelUsed(heater)) ? "yes" : "no",
(model.UsePid()) ? "PID" : "bang-bang");
const char* mode = (!model.UsePid()) ? "bang-bang"
: (model.ArePidParametersOverridden()) ? "custom PID"
: "PID";
reply.printf("Heater %u model: gain %.1f, time constant %.1f, dead time %.1f, max PWM %.2f, mode: %s",
heater, model.GetGain(), model.GetTimeConstant(), model.GetDeadTime(), model.GetMaxPwm(), mode);
if (model.UsePid())
{
// When reporting the PID parameters, we scale them by 255 for compatibility with older firmware and other firmware
const PidParams& spParams = model.GetPidParameters(false);
const float scaledSpKp = 255.0 * spParams.kP;
reply.catf("\nSetpoint change: P%.1f, I%.2f, D%.1f",
scaledSpKp, scaledSpKp * spParams.recipTi, scaledSpKp * spParams.tD);
const PidParams& ldParams = model.GetPidParameters(true);
const float scaledLoadKp = 255.0 * ldParams.kP;
reply.catf("\nLoad change: P%.1f, I%.2f, D%.1f",
scaledLoadKp, scaledLoadKp * ldParams.recipTi, scaledLoadKp * ldParams.tD);
M301PidParameters params = model.GetM301PidParameters(false);
reply.catf("\nSetpoint change: P%.1f, I%.3f, D%.1f", params.kP, params.kI, params.kD);
params = model.GetM301PidParameters(true);
reply.catf("\nLoad change: P%.1f, I%.3f, D%.1f", params.kP, params.kI, params.kD);
}
}
}
@ -1932,6 +1944,36 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
break;
case 374: // Save grid and height map to file
error = SaveHeightMap(gb, reply);
break;
case 375: // Load grid and height map from file and enable compensation
if (!LockMovementAndWaitForStandstill(gb))
{
return false;
}
error = LoadHeightMap(gb, reply);
break;
case 376: // Set taper height
{
HeightMap& heightMap = reprap.GetMove()->AccessBedProbeGrid();
if (gb.Seen('H'))
{
heightMap.SetTaperHeight(gb.GetFValue());
}
else if (heightMap.GetTaperHeight() > 0.0)
{
reply.printf("Bed compensation taper height is %.1fmm", heightMap.GetTaperHeight());
}
else
{
reply.copy("Bed compensation is not tapered");
}
}
break;
case 400: // Wait for current moves to finish
if (!LockMovementAndWaitForStandstill(gb))
{
@ -1939,6 +1981,14 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
break;
case 401: // Deploy Z probe
DoFileMacro(gb, DEPLOYPROBE_G, false);
break;
case 402: // Retract Z probe
DoFileMacro(gb, RETRACTPROBE_G, false);
break;
case 404: // Filament width and nozzle diameter
{
bool seen = false;
@ -1996,19 +2046,18 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
break;
case 500: // Store parameters in EEPROM
reprap.GetPlatform()->WriteNvData();
error = WriteConfigOverrideFile(reply, CONFIG_OVERRIDE_G);
break;
case 501: // Load parameters from EEPROM
reprap.GetPlatform()->ReadNvData();
if (gb.Seen('S'))
{
reprap.GetPlatform()->SetAutoSave(gb.GetIValue() > 0);
}
DoFileMacro(gb, "config-override.g", true);
break;
case 502: // Revert to default "factory settings"
reprap.GetPlatform()->ResetNvData();
reprap.GetHeat()->ResetHeaterModels(); // in case some heaters have no M307 commands in config.g
reprap.GetMove()->AccessDeltaParams().Init(); // in case M665 and M666 in config.g don't define all the parameters
platform->SetZProbeDefaults();
DoFileMacro(gb, "config.g", true, true);
break;
case 503: // List variable settings
@ -2281,7 +2330,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
seenType = true;
}
ZProbeParameters params = platform->GetZProbeParameters();
ZProbeParameters params = platform->GetCurrentZProbeParameters();
gb.TryGetFValue('H', params.diveHeight, seenParam); // dive height
if (gb.Seen('F')) // feed rate i.e. probing speed
@ -2307,7 +2356,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
if (seenParam)
{
platform->SetZProbeParameters(params);
platform->SetZProbeParameters(platform->GetZProbeType(), params);
}
if (!(seenAxes || seenType || seenParam))
@ -2363,7 +2412,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
break;
case 561:
case 561: // Set identity transform (also clears bed probe grid)
reprap.GetMove()->SetIdentityTransform();
break;
@ -3040,17 +3089,17 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
if (gb.Seen('L'))
{
params.SetDiagonal(gb.GetFValue() * distanceScale);
params.SetDiagonal(gb.GetFValue());
seen = true;
}
if (gb.Seen('R'))
{
params.SetRadius(gb.GetFValue() * distanceScale);
params.SetRadius(gb.GetFValue());
seen = true;
}
if (gb.Seen('B'))
{
params.SetPrintRadius(gb.GetFValue() * distanceScale);
params.SetPrintRadius(gb.GetFValue());
seen = true;
}
if (gb.Seen('X'))
@ -3075,7 +3124,7 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
// The homed height must be done last, because it gets recalculated when some of the other factors are changed
if (gb.Seen('H'))
{
params.SetHomedHeight(gb.GetFValue() * distanceScale);
params.SetHomedHeight(gb.GetFValue());
seen = true;
}
@ -3097,8 +3146,8 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
{
reply.printf("Diagonal %.3f, delta radius %.3f, homed height %.3f, bed radius %.1f"
", X %.3f" DEGREE_SYMBOL ", Y %.3f" DEGREE_SYMBOL ", Z %.3f" DEGREE_SYMBOL,
params.GetDiagonal() / distanceScale, params.GetRadius() / distanceScale,
params.GetHomedHeight() / distanceScale, params.GetPrintRadius() / distanceScale,
params.GetDiagonal(), params.GetRadius(),
params.GetHomedHeight(), params.GetPrintRadius(),
params.GetXCorrection(), params.GetYCorrection(), params.GetZCorrection());
}
else
@ -3450,6 +3499,11 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
bool GCodes::HandleTcode(GCodeBuffer& gb, StringRef& reply)
{
if (gb.MachineState().runningM502)
{
return true; // when running M502 we don't execute T commands
}
if (!LockMovementAndWaitForStandstill(gb))
{
return false;

View file

@ -8,17 +8,17 @@
#include "FOPDT.h"
#include "Core.h"
#include "Configuration.h"
#include "Storage/FileStore.h"
#include "Libraries/General/StringRef.h"
extern StringRef scratchString;
// Heater 6 on the Duet 0.8.5 is disabled by default at startup so that we can use fan 2.
// Set up sensible defaults here in case the user enables the heater without specifying values for all the parameters.
FopDt::FopDt()
: gain(DefaultHotEndHeaterGain), timeConstant(DefaultHotEndHeaterTimeConstant), deadTime(DefaultHotEndHeaterDeadTime), maxPwm(1.0),
enabled(false), usePid(true), pidParametersOverridden(false)
{
// Heater 6 on the Duet 0.8.5 is disabled by default at startup so that we can use fan 2.
// Set up sensible defaults in case the user enables the heater without specifying values for all the parameters.
enabled = false;
gain = DefaultHotEndHeaterGain;
timeConstant = DefaultHotEndHeaterTimeConstant;
deadTime = DefaultHotEndHeaterDeadTime;
maxPwm = 1.0;
usePid = true;
}
// Check the model parameters are sensible, if they are then save them and return true.
@ -45,6 +45,41 @@ bool FopDt::SetParameters(float pg, float ptc, float pdt, float pMaxPwm, bool pU
return false;
}
// Get the PID parameters as reported by M301
M301PidParameters FopDt::GetM301PidParameters(bool forLoadChange) const
{
M301PidParameters rslt;
const PidParameters& pp = GetPidParameters(forLoadChange);
const float reportedKp = pp.kP * 255.0;
rslt.kP = reportedKp;
rslt.kI = pp.recipTi * reportedKp;
rslt.kD = pp.tD * reportedKp;
return rslt;
}
// Override the PID parameters. We set both sets to the same parameters.
void FopDt::SetM301PidParameters(const M301PidParameters& pp)
{
loadChangeParams.kP = setpointChangeParams.kP = pp.kP * (1.0/255.0);
loadChangeParams.recipTi = setpointChangeParams.recipTi = pp.kI/pp.kP;
loadChangeParams.tD = setpointChangeParams.tD = pp.kD/pp.kP;
pidParametersOverridden = true;
}
// Write the model parameters to file returning true if no error
bool FopDt::WriteParameters(FileStore *f, size_t heater) const
{
scratchString.printf("M307 H%u A%.1f C%.1f D%.1f S%.2f B%d\n", heater, gain, timeConstant, deadTime, maxPwm, (usePid) ? 0 : 1);
bool ok = f->Write(scratchString.Pointer());
if (ok && pidParametersOverridden)
{
const M301PidParameters pp = GetM301PidParameters(false);
scratchString.printf("M301 H%u P%.1f I%.3f D%.1f\n", heater, pp.kP, pp.kI, pp.kD);
ok = f->Write(scratchString.Pointer());
}
return ok;
}
/* Re-calculate the PID parameters.
* For some possible formulas, see "Comparison of some well-known PID tuning formulas", Computers and Chemical Engineering 30 (2006) 14161423,
* available at http://www.ece.ualberta.ca/~marquez/journal_publications_files/papers/tan_cce_06.pdf
@ -86,6 +121,8 @@ void FopDt::CalcPidConstants()
setpointChangeParams.kP = 0.7/(gain * timeFrac);
setpointChangeParams.recipTi = 1.0/timeConstant; // Ti = time constant
setpointChangeParams.tD = deadTime * 0.7;
pidParametersOverridden = false;
}
// End

View file

@ -10,13 +10,26 @@
#ifndef SRC_HEATING_FOPDT_H_
#define SRC_HEATING_FOPDT_H_
struct PidParams
#include <cstddef>
// This is how PID parameters are stored internally
struct PidParameters
{
float kP; // controller (not model) gain
float recipTi; // reciprocal of controller integral time
float tD; // controller differential time
};
// This is how PID parameters are given in M301 commands
struct M301PidParameters
{
float kP;
float kI;
float kD;
};
class FileStore;
class FopDt
{
public:
@ -30,12 +43,17 @@ public:
float GetMaxPwm() const { return maxPwm; }
bool UsePid() const { return usePid; }
bool IsEnabled() const { return enabled; }
bool ArePidParametersOverridden() const { return pidParametersOverridden; }
M301PidParameters GetM301PidParameters(bool forLoadChange) const;
void SetM301PidParameters(const M301PidParameters& params);
const PidParams& GetPidParameters(bool forLoadChange) const
const PidParameters& GetPidParameters(bool forLoadChange) const
{
return (forLoadChange) ? loadChangeParams : setpointChangeParams;
}
bool WriteParameters(FileStore *f, size_t heater) const; // Write the model parameters to file returning true if no error
private:
void CalcPidConstants();
@ -43,11 +61,12 @@ private:
float timeConstant;
float deadTime;
float maxPwm;
bool usePid;
bool enabled;
bool usePid;
bool pidParametersOverridden;
PidParams setpointChangeParams; // parameters for handling changes in the setpoint
PidParams loadChangeParams; // parameters for handling changes in the load
PidParameters setpointChangeParams; // parameters for handling changes in the setpoint
PidParameters loadChangeParams; // parameters for handling changes in the load
};
#endif /* SRC_HEATING_FOPDT_H_ */

View file

@ -30,15 +30,35 @@ Heat::Heat(Platform* p)
}
}
// Reset all heater models to defaults. Called when running M502.
void Heat::ResetHeaterModels()
{
for (int heater = 0; heater < HEATERS; heater++)
{
if (pids[heater]->IsHeaterEnabled())
{
if (heater == DefaultBedHeater)
{
pids[heater]->SetModel(DefaultBedHeaterGain, DefaultBedHeaterTimeConstant, DefaultBedHeaterDeadTime, 1.0, false);
}
else
{
pids[heater]->SetModel(DefaultHotEndHeaterGain, DefaultHotEndHeaterTimeConstant, DefaultHotEndHeaterDeadTime, 1.0, true);
}
}
}
}
void Heat::Init()
{
for (int heater = 0; heater < HEATERS; heater++)
{
if (heater == bedHeater || heater == chamberHeater)
if (heater == DefaultBedHeater)
{
pids[heater]->Init(DefaultBedHeaterGain, DefaultBedHeaterTimeConstant, DefaultBedHeaterDeadTime, DefaultBedTemperatureLimit, false);
pids[heater]->Init(DefaultBedHeaterGain, DefaultBedHeaterTimeConstant, DefaultBedHeaterDeadTime,
DefaultBedTemperatureLimit, false);
}
#ifndef DUET_NG
#if !defined(DUET_NG) && !defined(__RADDS__)
else if (heater == HEATERS - 1)
{
// Heater 6 pin is shared with fan 1. By default we support fan 1, so disable heater 6.
@ -47,9 +67,11 @@ void Heat::Init()
#endif
else
{
pids[heater]->Init(DefaultHotEndHeaterGain, DefaultHotEndHeaterTimeConstant, DefaultHotEndHeaterDeadTime, DefaultExtruderTemperatureLimit, true);
pids[heater]->Init(DefaultHotEndHeaterGain, DefaultHotEndHeaterTimeConstant, DefaultHotEndHeaterDeadTime,
DefaultExtruderTemperatureLimit, true);
}
}
lastTime = millis() - platform->HeatSampleInterval(); // flag the PIDS as due for spinning
longWait = platform->Time();
coldExtrude = false;
@ -295,4 +317,25 @@ float Heat::GetHighestTemperatureLimit() const
return limit;
}
// Override the model-generated PID parameters
void Heat::SetM301PidParameters(size_t heater, const M301PidParameters& params)
{
pids[heater]->SetM301PidParameters(params);
}
// Write heater model parameters to file returning true if no error
bool Heat::WriteModelParameters(FileStore *f) const
{
bool ok = f->Write("; Heater model parameters\n");
for (size_t h = 0; ok && h < HEATERS; ++h)
{
const FopDt& model = pids[h]->GetModel();
if (model.IsEnabled())
{
ok = model.WriteParameters(f, h);
}
}
return ok;
}
// End

View file

@ -37,6 +37,7 @@ public:
void Spin(); // Called in a tight loop to keep everything going
void Init(); // Set everything up
void Exit(); // Shut everything down
void ResetHeaterModels(); // Reset all active heater models to defaults
bool ColdExtrude() const; // Is cold extrusion allowed?
void AllowColdExtrude(bool b); // Allow or deny cold extrusion
@ -92,12 +93,6 @@ public:
bool SetHeaterModel(size_t heater, float gain, float tc, float td, float maxPwm, bool usePid) // Set the heater process model
pre(heater < HEATERS);
bool IsModelUsed(size_t heater) const // Is the heater using the PID parameters calculated form the model?
pre(heater < HEATERS);
void UseModel(size_t heater, bool b) // Use or don't use the model to provide the PID parameters
pre(heater < HEATERS);
void GetHeaterProtection(size_t heater, float& maxTempExcursion, float& maxFaultTime) const
pre(heater < HEATERS);
@ -109,6 +104,11 @@ public:
float GetHighestTemperatureLimit() const; // Get the highest temperature limit of any heater
void SetM301PidParameters(size_t heater, const M301PidParameters& params)
pre(heater < HEATERS);
bool WriteModelParameters(FileStore *f) const; // Write heater model parameters to file returning true if no error
private:
Platform* platform; // The instance of the RepRap hardware class
PID* pids[HEATERS]; // A PID controller for each heater
@ -168,18 +168,6 @@ inline bool Heat::SetHeaterModel(size_t heater, float gain, float tc, float td,
return pids[heater]->SetModel(gain, tc, td, maxPwm, usePid);
}
// Is the heater using the PID parameters calculated form the model?
inline bool Heat::IsModelUsed(size_t heater) const
{
return pids[heater]->IsModelUsed();
}
// Use or don't use the model to provide the PID parameters
inline void Heat::UseModel(size_t heater, bool b)
{
pids[heater]->UseModel(b);
}
// Is the heater enabled?
inline bool Heat::IsHeaterEnabled(size_t heater) const
{

View file

@ -60,7 +60,6 @@ void PID::Reset()
badTemperatureCount = 0;
active = false; // default to standby temperature
tuned = false;
useModel = true; // by default we use the model in this first release
averagePWM = lastPwm = 0.0;
heatingFaultCount = 0;
temperature = BAD_ERROR_TEMPERATURE;
@ -180,7 +179,6 @@ void PID::Spin()
{
// Read the temperature
const TemperatureError err = ReadTemperature();
const PidParameters& pp = platform->GetPidParameters(heater);
// Handle any temperature reading error and calculate the temperature rate of change, if possible
if (err != TemperatureError::success)
@ -314,38 +312,19 @@ void PID::Spin()
else if (mode < HeaterMode::tuning0)
{
// Performing normal temperature control
bool usingPid = (useModel) ? model.UsePid() : pp.UsePID();
float maxPwm = (useModel) ? model.GetMaxPwm() : pp.kS;
if (usingPid)
if (model.UsePid())
{
// Using PID mode. Determine the PID parameters to use.
float kP, recipTi, tD, gain;
bool inLoadMode;
if (useModel)
{
inLoadMode = (mode == HeaterMode::stable); // use standard PID when maintaining temperature
const PidParams& params = model.GetPidParameters(inLoadMode);
kP = params.kP;
recipTi = params.recipTi;
tD = params.tD;
gain = model.GetGain();
}
else
{
inLoadMode = true; // use standard PID always
kP = (pp.kP * pp.kS) * (1.0/255.0);
recipTi = pp.kI/pp.kP;
tD = pp.kD/pp.kP;
gain = 255.0/pp.kT;
}
const bool inLoadMode = (mode == HeaterMode::stable); // use standard PID when maintaining temperature
const PidParameters& params = model.GetPidParameters(inLoadMode);
// If the P and D terms together demand that the heater is full on or full off, disregard the I term
const float errorMinusDterm = error - (tD * derivative);
const float pPlusD = kP * errorMinusDterm;
const float expectedPwm = constrain<float>((temperature - NormalAmbientTemperature)/gain, 0.0, maxPwm);
if (pPlusD + expectedPwm > maxPwm)
const float errorMinusDterm = error - (params.tD * derivative);
const float pPlusD = params.kP * errorMinusDterm;
const float expectedPwm = constrain<float>((temperature - NormalAmbientTemperature)/model.GetGain(), 0.0, model.GetMaxPwm());
if (pPlusD + expectedPwm > model.GetMaxPwm())
{
lastPwm = maxPwm;
lastPwm = model.GetMaxPwm();
// If we are heating up, preset the I term to the expected PWM at this temperature, ready for the switch over to PID
if (mode == HeaterMode::heating && error > 0.0 && derivative > 0.0)
{
@ -361,16 +340,17 @@ void PID::Spin()
// In the following we use a modified PID when the temperature is a long way off target.
// During initial heating or cooling, the D term represents expected overshoot, which we don't want to add to the I accumulator.
// When we are in load mode, the I term is much larger and the D term doesn't represent overshoot, so use normal PID.
const float errorToUse = (inLoadMode) ? error : errorMinusDterm;
iAccumulator = constrain<float>(iAccumulator + (errorToUse * kP * recipTi * platform->HeatSampleInterval() * MillisToSeconds),
0.0, maxPwm);
lastPwm = constrain<float>(pPlusD + iAccumulator, 0.0, maxPwm);
const float errorToUse = (inLoadMode || model.ArePidParametersOverridden()) ? error : errorMinusDterm;
iAccumulator = constrain<float>
(iAccumulator + (errorToUse * params.kP * params.recipTi * platform->HeatSampleInterval() * MillisToSeconds),
0.0, model.GetMaxPwm());
lastPwm = constrain<float>(pPlusD + iAccumulator, 0.0, model.GetMaxPwm());
}
}
else
{
// Using bang-bang mode
lastPwm = (error > 0.0) ? maxPwm : 0.0;
lastPwm = (error > 0.0) ? model.GetMaxPwm() : 0.0;
}
}
else
@ -772,7 +752,6 @@ void PID::FitCurve()
tuned = SetModel(gain, tc, td, model.GetMaxPwm(), true);
if (tuned)
{
useModel = true;
platform->MessageF(GENERIC_MESSAGE,
"Auto tune heater %d with PWM=%.2f completed in %u sec, maximum temperature reached %.1fC\n"
"Use M307 H%d to see the result\n",

View file

@ -65,12 +65,6 @@ public:
bool SetModel(float gain, float tc, float td, float maxPwm, bool usePid); // Set the process model
bool IsModelUsed() const // Is the model being used to determine the PID parameters?
{ return useModel; }
void UseModel(bool b) // Use or don't use the model to provide the PID parameters
{ useModel = b; }
bool IsHeaterEnabled() const // Is this heater enabled?
{ return model.IsEnabled(); }
@ -80,6 +74,9 @@ public:
void SetHeaterProtection(float pMaxTempExcursion, float pMaxFaultTime)
{ maxTempExcursion = pMaxTempExcursion; maxHeatingFaultTime = pMaxFaultTime; }
void SetM301PidParameters(const M301PidParameters& params)
{ model.SetM301PidParameters(params); }
private:
void SwitchOn(); // Turn the heater on and set the mode
@ -116,7 +113,6 @@ private:
HeaterMode mode; // Current state of the heater
bool active; // Are we active or standby?
bool tuned; // True if tuning was successful
bool useModel; // Use the model to calculate the PID parameters
uint8_t badTemperatureCount; // Count of sequential dud readings
static_assert(sizeof(previousTemperaturesGood) * 8 >= NumPreviousTemperatures, "too few bits in previousTemperaturesGood");

View file

@ -40,11 +40,8 @@
* \asf_license_stop
*
*/
#include "compiler.h"
#include "rtc.h"
#include "RepRapFirmware.h"
uint32_t get_fattime(void);
/**
* \brief Current time returned is packed into a DWORD value.
*
@ -64,28 +61,26 @@ uint32_t get_fattime(void);
*
* \return Current time.
*/
uint32_t get_fattime(void)
extern "C" uint32_t get_fattime()
{
if (rtc_get_valid_entry(RTC) != 0)
Platform *platform = reprap.GetPlatform();
if (!platform->IsDateTimeSet())
{
// Date and time have not been set, return default timestamp instead
return 0x210001;
}
// Retrieve current date and time from RTC
uint32_t ul_time;
uint32_t ul_hour, ul_minute, ul_second;
uint32_t ul_year, ul_month, ul_day, ul_week;
rtc_get_time(RTC, &ul_hour, &ul_minute, &ul_second);
rtc_get_date(RTC, &ul_year, &ul_month, &ul_day, &ul_week);
ul_time = ((ul_year - 1980) << 25)
| (ul_month << 21)
| (ul_day << 16)
| (ul_hour << 11)
| (ul_minute << 5)
| (ul_second >> 1);
time_t timeNow = platform->GetDateTime();
struct tm timeInfo;
gmtime_r(&timeNow, &timeInfo);
uint32_t ul_time = ((timeInfo.tm_year + 1900 - 1980) << 25)
| ((timeInfo.tm_mon + 1) << 21)
| (timeInfo.tm_mday << 16)
| (timeInfo.tm_hour << 11)
| (timeInfo.tm_min << 5)
| (timeInfo.tm_sec >> 1);
return ul_time;
}

View file

@ -237,7 +237,7 @@ void DeltaParameters::Adjust(size_t numFactors, const floatc_t v[])
homedCarriageHeight -= heightError;
// Note: if we adjusted the X and Y tilts, and there are any endstop adjustments, then the homed position won't be exactly in the centre
// and changing the tilt will therefore affect the homed height. We ignore this for now. If it is ever significant, a second sutocalibration
// and changing the tilt will therefore affect the homed height. We ignore this for now. If it is ever significant, a second autocalibration
// run will correct it.
}
@ -248,6 +248,32 @@ void DeltaParameters::PrintParameters(StringRef& reply) const
xCorrection, yCorrection, zCorrection, xTilt * 100.0, yTilt * 100.0);
}
// Write parameters to file if in delta mode, returning true if no error
// Values are written in mm
bool DeltaParameters::WriteParameters(FileStore *f) const
{
if (!IsDeltaMode())
{
return true;
}
bool ok = f->Write("; Delta parameters\n");
if (ok)
{
scratchString.printf("M665 L%.3f R%.3f H%.3f B%.1f X%.3f Y%.3f Z%.3f\n",
GetDiagonal(), GetRadius(), GetHomedHeight(), GetPrintRadius(), GetXCorrection(), GetYCorrection(), GetZCorrection());
ok = f->Write(scratchString.Pointer());
}
if (ok)
{
scratchString.printf("M666 X%.3f Y%.3f Z%.3f A%.2f B%.2f\n",
GetEndstopAdjustment(X_AXIS), GetEndstopAdjustment(Y_AXIS), GetEndstopAdjustment(Z_AXIS),
GetXTilt() * 100.0, GetYTilt() * 100.0);
ok = f->Write(scratchString.Pointer());
}
return ok;
}
// End

View file

@ -48,6 +48,7 @@ public:
floatc_t ComputeDerivative(unsigned int deriv, float ha, float hb, float hc); // Compute the derivative of height with respect to a parameter at a set of motor endpoints
void Adjust(size_t numFactors, const floatc_t v[]); // Perform 3-, 4-, 6- or 7-factor adjustment
void PrintParameters(StringRef& reply) const; // Print all the parameters for debugging
bool WriteParameters(FileStore *f) const; // Write parameters to file if in delta mode, returning true if no error
private:
void Recalc();

View file

@ -109,10 +109,11 @@ void GridDefinition::PrintError(StringRef& r) const
// Increase the version number in the following string whenever we change the format of the height map file.
const char *HeightMap::HeightMapComment = "RepRapFirmware height map file v1";
HeightMap::HeightMap(float *heightStorage) : gridHeights(heightStorage) { }
HeightMap::HeightMap(float *heightStorage) : gridHeights(heightStorage), useMap(false), useTaper(false) { }
void HeightMap::SetGrid(const GridDefinition& gd)
{
useMap = false;
def = gd;
ClearGridHeights();
}
@ -157,7 +158,9 @@ bool HeightMap::SaveToFile(FileStore *f) const
buf.catf(" generated at %04u-%02u-%02u %02u:%02u",
timeInfo->tm_year + 1900, timeInfo->tm_mon, timeInfo->tm_mday, timeInfo->tm_hour, timeInfo->tm_min);
}
buf.cat('\n');
float mean, deviation;
(void)GetStatistics(mean, deviation);
buf.catf(", mean error %.2f, deviation %.2f\n", mean, deviation);
if (!f->Write(buf.Pointer()))
{
return true;
@ -170,7 +173,7 @@ bool HeightMap::SaveToFile(FileStore *f) const
return true;
}
// Write the grid heights
// Write the grid heights. We use a fixed field with of 6 characters to make is easier to view.
uint32_t index = 0;
for (uint32_t i = 0; i < def.numY; ++i)
{
@ -183,11 +186,11 @@ bool HeightMap::SaveToFile(FileStore *f) const
}
if (IsHeightSet(index))
{
buf.catf("%.3f%", gridHeights[index]);
buf.catf("%7.3f%", gridHeights[index]);
}
else
{
buf.cat("0"); // write 0 with no decimal point where we didn't probe, so we can tell when we reload it
buf.cat(" 0"); // write 0 with no decimal point where we didn't probe, so we can tell when we reload it
}
++index;
}
@ -281,9 +284,86 @@ bool HeightMap::LoadFromFile(FileStore *f, StringRef& r)
return true; // an error occurred
}
// Compute the height error at the specified point
float HeightMap::ComputeHeightError(float x, float y) const
// Return number of points probed, mean and RMS deviation
unsigned int HeightMap::GetStatistics(float& mean, float& deviation) const
{
double heightSum = 0.0, heightSquaredSum = 0.0;
unsigned int numProbed = 0;
for (uint32_t i = 0; i < def.NumPoints(); ++i)
{
if (IsHeightSet(i))
{
++numProbed;
const double heightError = (double)gridHeights[i];
heightSum += heightError;
heightSquaredSum += heightError * heightError;
}
}
if (numProbed == 0)
{
mean = deviation = 0.0;
}
else
{
mean = (float)(heightSum/numProbed);
deviation = (float)sqrt(((heightSquaredSum * numProbed) - (heightSum * heightSum)))/numProbed;
}
return numProbed;
}
void HeightMap::UseHeightMap(bool b)
{
useMap = b && def.IsValid();
}
void HeightMap::SetTaperHeight(float h)
{
useTaper = (h > 1.0);
if (useTaper)
{
taperHeight = h;
recipTaperHeight = 1.0/h;
}
}
// Compute the height error at the specified point i.e. value that needs to be added to the Z coordinate
float HeightMap::ComputeHeightError(float x, float y, float z) const
{
if (!useMap || (useTaper && z >= taperHeight))
{
return 0.0;
}
const float rawError = GetInterpolatedHeightError(x, y);
return (useTaper) ? (taperHeight - z) * recipTaperHeight * rawError : rawError;
}
// Compute the inverse height error at the specified point i.e. value that needs to be subtracted form the Z coordinate
float HeightMap::ComputeInverseHeightError(float x, float y, float z) const
{
if (!useMap)
{
return 0.0;
}
const float rawError = GetInterpolatedHeightError(x, y);
if (!useTaper || rawError > taperHeight) // need check on rawError to avoid possible divide by zero
{
return rawError;
}
const float zreq = (z - rawError)/(1.0 - (rawError * recipTaperHeight));
return (zreq >= taperHeight) ? 0.0 : z - zreq;
}
// Compute the height error at the specified point
float HeightMap::GetInterpolatedHeightError(float x, float y) const
{
if (!useMap)
{
return 0.0;
}
const float xf = (x - def.xMin) * def.recipSpacing;
const float xFloor = floor(xf);
const int32_t xIndex = (int32_t)xFloor;

View file

@ -68,9 +68,8 @@ public:
const GridDefinition& GetGrid() const { return def; }
void SetGrid(const GridDefinition& gd);
float ComputeHeightError(float x, float y) const // Compute the height error at the specified point
pre(IsValid(); gridHeights != nullptr; gridHeights.upb >= NumPoints());
float ComputeHeightError(float x, float y, float z) const; // Compute the height error at the specified point
float ComputeInverseHeightError(float x, float y, float z) const; // Compute the inverse height error at the specified point
void ClearGridHeights(); // Clear all grid height corrections
void SetGridHeight(size_t xIndex, size_t yIndex, float height); // Set the height of a grid point
@ -81,15 +80,30 @@ public:
unsigned int GetMinimumSegments(float distance) const; // Return the minimum number of segments for a move by this X or Y amount
void UseHeightMap(bool b);
bool UsingHeightMap() const { return useMap; }
float GetTaperHeight() const { return (useTaper) ? taperHeight : 0.0; }
void SetTaperHeight(float h);
unsigned int GetStatistics(float& mean, float& deviation) const; // Return number of points probed, mean and RMS deviation
private:
static const char *HeightMapComment; // The start of the comment we write at the start of the height map file
GridDefinition def;
float *gridHeights; // The map of grid heights, must have at least MaxGridProbePoints entries
uint32_t gridHeightSet[MaxGridProbePoints/32]; // Bitmap of which heights are set
float taperHeight; // Height over which we taper
float recipTaperHeight; // Reciprocal of the taper height
bool useMap; // True to do bed compensation
bool useTaper; // True to taper off the compensation
uint32_t GetMapIndex(uint32_t xIndex, uint32_t yIndex) const { return (yIndex * def.NumXpoints()) + xIndex; }
bool IsHeightSet(uint32_t index) const { return (gridHeightSet[index/32] & (1 << (index & 31))) != 0; }
float GetInterpolatedHeightError(float x, float y) const // Compute the interpolated height error at the specified point
pre(useMap);
float GetHeightError(uint32_t xIndex, uint32_t yIndex) const;
float InterpolateX(uint32_t xIndex, uint32_t yIndex, float xFrac) const;
float InterpolateY(uint32_t xIndex, uint32_t yIndex, float yFrac) const;

View file

@ -98,7 +98,6 @@ void Move::Init()
simulationTime = 0.0;
longestGcodeWaitInterval = 0;
waitingForMove = false;
useGridHeights = false;
active = true;
}
@ -656,10 +655,7 @@ void Move::BedTransform(float xyzPoint[MAX_AXES], uint32_t xAxes) const
switch(numBedCompensationPoints)
{
case 0:
if (useGridHeights)
{
zCorrection += grid.ComputeHeightError(xCoord, xyzPoint[Y_AXIS]);
}
zCorrection += grid.ComputeHeightError(xCoord, xyzPoint[Y_AXIS], xyzPoint[Z_AXIS]);
break;
case 3:
@ -704,10 +700,7 @@ void Move::InverseBedTransform(float xyzPoint[MAX_AXES], uint32_t xAxes) const
switch(numBedCompensationPoints)
{
case 0:
if (useGridHeights)
{
zCorrection += grid.ComputeHeightError(xCoord, xyzPoint[Y_AXIS]);
}
zCorrection += grid.ComputeInverseHeightError(xCoord, xyzPoint[Y_AXIS], xyzPoint[Z_AXIS]);
break;
case 3:
@ -738,6 +731,7 @@ void Move::InverseBedTransform(float xyzPoint[MAX_AXES], uint32_t xAxes) const
void Move::SetIdentityTransform()
{
numBedCompensationPoints = 0;
grid.ClearGridHeights();
}
float Move::AxisCompensation(int8_t axis) const
@ -1038,23 +1032,14 @@ void Move::DoDeltaCalibration(size_t numFactors, StringRef& reply)
{
corrections[i] = 0.0;
float machinePos[DELTA_AXES];
float xp = xBedProbePoints[i], yp = yBedProbePoints[i];
if (probePointSet[i] & xyCorrected)
{
// The point was probed with the sensor at the specified XY coordinates, so subtract the sensor offset to get the head position
const ZProbeParameters& zparams = reprap.GetPlatform()->GetZProbeParameters();
xp -= zparams.xOffset;
yp -= zparams.yOffset;
}
machinePos[X_AXIS] = xp;
machinePos[Y_AXIS] = yp;
float zp = GetProbeCoordinates(i, machinePos[X_AXIS], machinePos[Y_AXIS], (probePointSet[i] & xyCorrected) != 0);
machinePos[Z_AXIS] = 0.0;
probeMotorPositions(i, A_AXIS) = deltaParams.Transform(machinePos, A_AXIS);
probeMotorPositions(i, B_AXIS) = deltaParams.Transform(machinePos, B_AXIS);
probeMotorPositions(i, C_AXIS) = deltaParams.Transform(machinePos, C_AXIS);
initialSumOfSquares += fsquare(zBedProbePoints[i]);
initialSumOfSquares += fsquare(zp);
}
// Do 1 or more Newton-Raphson iterations
@ -1446,7 +1431,7 @@ void Move::SetZBedProbePoint(size_t index, float z, bool wasXyCorrected, bool wa
}
else
{
useGridHeights = false;
grid.UseHeightMap(false);
zBedProbePoints[index] = z;
probePointSet[index] |= zSet;
if (wasXyCorrected)
@ -1493,6 +1478,22 @@ bool Move::XYProbeCoordinatesSet(int index) const
return (probePointSet[index] & xSet) && (probePointSet[index] & ySet);
}
// This returns the (X, Y) points to probe the bed at probe point count. When probing, it returns false.
// If called after probing has ended it returns true, and the Z coordinate probed is also returned.
// If 'wantNozzlePosition is true then we return the nozzle position when the point is probed, else we return the probe point itself
float Move::GetProbeCoordinates(int count, float& x, float& y, bool wantNozzlePosition) const
{
x = xBedProbePoints[count];
y = yBedProbePoints[count];
if (wantNozzlePosition)
{
const ZProbeParameters& rp = reprap.GetPlatform()->GetCurrentZProbeParameters();
x -= rp.xOffset;
y -= rp.yOffset;
}
return zBedProbePoints[count];
}
size_t Move::NumberOfProbePoints() const
{
for(size_t i = 0; i < MaxProbePoints; i++)

View file

@ -45,7 +45,9 @@ public:
void Init(); // Start me up
void Spin(); // Called in a tight loop to keep the class going
void Exit(); // Shut down
void GetCurrentUserPosition(float m[DRIVES], uint8_t moveType, uint32_t xAxes) const; // Return the position (after all queued moves have been executed) in transformed coords
void GetCurrentMachinePosition(float m[DRIVES], bool disableMotorMapping) const; // Get the current position in untransformed coords
void GetCurrentUserPosition(float m[DRIVES], uint8_t moveType, uint32_t xAxes) const; // Return the position (after all queued moves have been executed) in transformed coords
int32_t GetEndPoint(size_t drive) const { return liveEndPoints[drive]; } // Get the current position of a motor
void LiveCoordinates(float m[DRIVES], uint32_t xAxes); // Gives the last point at the end of the last complete DDA transformed to user coords
void Interrupt(); // The hardware's (i.e. platform's) interrupt should call this.
@ -69,6 +71,7 @@ public:
size_t NumberOfXYProbePoints() const; // How many XY coordinates of probe points have been set (Zs may not have been probed yet)
bool AllProbeCoordinatesSet(int index) const; // XY, and Z all set for this one?
bool XYProbeCoordinatesSet(int index) const; // Just XY set for this one?
float GetProbeCoordinates(int count, float& x, float& y, bool wantNozzlePosition) const; // Get pre-recorded probe coordinates
void FinishedBedProbing(int sParam, StringRef& reply); // Calibrate or set the bed equation after probing
float SecondDegreeTransformZ(float x, float y) const; // Used for second degree bed equation
void SetAxisCompensation(int8_t axis, float tangent); // Set an axis-pair compensation angle
@ -76,6 +79,7 @@ public:
void SetIdentityTransform(); // Cancel the bed equation; does not reset axis angle compensation
void Transform(float move[], uint32_t xAxes) const; // Take a position and apply the bed and the axis-angle compensations
void InverseTransform(float move[], uint32_t xAxes) const; // Go from a transformed point back to user coordinates
void Diagnostics(MessageType mtype); // Report useful stuff
const DeltaParameters& GetDeltaParams() const { return deltaParams; }
@ -115,8 +119,6 @@ public:
HeightMap& AccessBedProbeGrid() { return grid; } // Access the bed probing grid
void UseHeightMap(bool b) { useGridHeights = b; } // Start or stop using the height map
bool UsingHeightMap() const { return useGridHeights; } // Are we doing grid bed compensation?
void Babystep(float zMovement); // Request babystepping
private:
@ -127,7 +129,6 @@ private:
void SetProbedBedEquation(size_t numPoints, StringRef& reply); // When we have a full set of probed points, work out the bed's equation
void DoDeltaCalibration(size_t numPoints, StringRef& reply);
void BedTransform(float move[MAX_AXES], uint32_t xAxes) const; // Take a position and apply the bed compensations
void GetCurrentMachinePosition(float m[DRIVES], bool disableMotorMapping) const; // Get the current position in untransformed coords
void InverseBedTransform(float move[MAX_AXES], uint32_t xAxes) const; // Go from a bed-transformed point back to user coordinates
void AxisTransform(float move[MAX_AXES]) const; // Take a position and apply the axis-angle compensations
void InverseAxisTransform(float move[MAX_AXES]) const; // Go from an axis transformed point back to user coordinates
@ -179,7 +180,6 @@ private:
float xRectangle, yRectangle; // The side lengths of the rectangle used for second-degree bed compensation
HeightMap grid; // Grid definition and height map for G29 bed probing. The probe heights are stored in zBedProbePoints, see above.
bool useGridHeights; // True if the zBedProbePoints came from valid bed probing and relate to the current grid
float idleTimeout; // How long we wait with no activity before we reduce motor currents to idle
float lastMoveTime; // The approximate time at which the last move was completed, or 0

View file

@ -20,17 +20,13 @@
****************************************************************************************************/
#include "RepRapFirmware.h"
#include "DueFlashStorage.h"
#include "sam/drivers/tc/tc.h"
#include "sam/drivers/hsmci/hsmci.h"
#include "sd_mmc.h"
#if defined(DUET_NG)
# include "TMC2660.h"
#endif
#ifdef DUET_NG
# include "TMC2660.h"
# include "FirmwareUpdater.h"
#endif
@ -69,6 +65,8 @@ uint32_t nextInterruptScheduledAt = 0;
uint32_t lastInterruptTime = 0;
#endif
// Global functions
// Urgent initialisation function
// This is called before general init has been done, and before constructors for C++ static data have been called.
// Therefore, be very careful what you do here!
@ -115,26 +113,51 @@ extern "C"
reprap.Tick();
return 0;
}
void NMI_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::NMI); }
void HardFault_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::hardFault); }
void MemManage_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::memManage); }
void BusFault_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::busFault); }
void UsageFault_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::usageFault); }
void SVC_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::otherFault); }
void DebugMon_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::otherFault); }
void PendSV_Handler () { reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::otherFault); }
}
//*************************************************************************************************
// PidParameters class
bool PidParameters::UsePID() const
// ZProbeParameters class
void ZProbeParameters::Init(float h)
{
return kP >= 0;
adcValue = Z_PROBE_AD_VALUE;
xOffset = yOffset = 0.0;
height = h;
calibTemperature = 20.0;
temperatureCoefficient = 0.0; // no default temperature correction
diveHeight = DEFAULT_Z_DIVE;
probeSpeed = DEFAULT_PROBE_SPEED;
travelSpeed = DEFAULT_TRAVEL_SPEED;
recoveryTime = extraParam = 0.0;
invertReading = false;
}
bool PidParameters::operator==(const PidParameters& other) const
float ZProbeParameters::GetStopHeight(float temperature) const
{
return kI == other.kI && kD == other.kD && kP == other.kP && kT == other.kT && kS == other.kS;
return ((temperature - calibTemperature) * temperatureCoefficient) + height;
}
bool ZProbeParameters::WriteParameters(FileStore *f, unsigned int probeType) const
{
scratchString.printf("G31 T%u P%d X%.1f Y%.1f Z%.2f\n", probeType, adcValue, xOffset, yOffset, height);
return f->Write(scratchString.Pointer());
}
//*************************************************************************************************
// Platform class
Platform::Platform() :
autoSaveEnabled(false), board(DEFAULT_BOARD_TYPE), active(false), errorCodeBits(0),
board(DEFAULT_BOARD_TYPE), active(false), errorCodeBits(0),
auxGCodeReply(nullptr), fileStructureInitialised(false), tickState(0), debugCode(0)
{
// Output
@ -165,7 +188,6 @@ void Platform::Init()
realTime = 0;
// Comms
baudRates[0] = MAIN_BAUD_RATE;
baudRates[1] = AUX_BAUD_RATE;
#if NUM_SERIAL_CHANNELS >= 2
@ -186,9 +208,20 @@ void Platform::Init()
SERIAL_AUX2_DEVICE.begin(baudRates[2]);
#endif
static_assert(sizeof(FlashData) + sizeof(SoftwareResetData) <= FLASH_DATA_LENGTH, "NVData too large");
compatibility = marlin; // default to Marlin because the common host programs expect the "OK" response to commands
ARRAY_INIT(ipAddress, IP_ADDRESS);
ARRAY_INIT(netMask, NET_MASK);
ARRAY_INIT(gateWay, GATE_WAY);
ResetNvData();
#ifdef DUET_NG
memset(macAddress, 0xFF, sizeof(macAddress));
#else
ARRAY_INIT(macAddress, MAC_ADDRESS);
#endif
zProbeType = 0; // Default is to use no Z probe switch
zProbeAxes = Z_PROBE_AXES;
SetZProbeDefaults();
// We need to initialise at least some of the time stuff before we call MassStorage::Init()
addToTime = 0.0;
@ -218,7 +251,7 @@ void Platform::Init()
ARRAY_INIT(driveStepsPerUnit, DRIVE_STEPS_PER_UNIT);
ARRAY_INIT(instantDvs, INSTANT_DVS);
#if !defined(DUET_NG) && !defined(RADDS)
#if !defined(DUET_NG) && !defined(__RADDS__)
// Motor current setting on Duet 0.6 and 0.8.5
ARRAY_INIT(potWipes, POT_WIPES);
senseResistor = SENSE_RESISTOR;
@ -403,12 +436,14 @@ void Platform::Init()
#endif
// MCU temperature and power monitoring
#ifndef __RADDS__
temperatureAdcChannel = GetTemperatureAdcChannel();
AnalogInEnableChannel(temperatureAdcChannel, true);
currentMcuTemperature = highestMcuTemperature = 0;
lowestMcuTemperature = 4095;
mcuTemperatureAdjust = 0.0;
mcuAlarmTemperature = 80.0; // need to set the quite high here because the sensor is not be calibrated yet
#endif
mcuTemperatureAdjust = 0.0;
#ifdef DUET_NG
vInMonitorAdcChannel = PinToAdcChannel(PowerMonitorVinDetectPin);
@ -471,6 +506,13 @@ int Platform::GetThermistorNumber(size_t heater) const
return heaterTempChannels[heater];
}
void Platform::SetZProbeDefaults()
{
switchZProbeParameters.Init(0.0);
irZProbeParameters.Init(Z_PROBE_STOP_HEIGHT);
alternateZProbeParameters.Init(Z_PROBE_STOP_HEIGHT);
}
void Platform::InitZProbe()
{
zProbeOnFilter.Init(0);
@ -482,7 +524,7 @@ void Platform::InitZProbe()
zProbeModulationPin = (board == BoardType::Duet_07 || board == BoardType::Duet_085) ? Z_PROBE_MOD_PIN07 : Z_PROBE_MOD_PIN;
#endif
switch (nvData.zProbeType)
switch (zProbeType)
{
case 1:
case 2:
@ -524,12 +566,12 @@ void Platform::InitZProbe()
// Return the Z probe data.
// The ADC readings are 12 bits, so we convert them to 10-bit readings for compatibility with the old firmware.
int Platform::ZProbe() const
int Platform::GetZProbeReading() const
{
int zProbeVal = 0; // initialised to avoid spurious compiler warning
if (zProbeOnFilter.IsValid() && zProbeOffFilter.IsValid())
{
switch (nvData.zProbeType)
switch (zProbeType)
{
case 1: // Simple or intelligent IR sensor
case 3: // Alternate sensor
@ -554,7 +596,7 @@ int Platform::ZProbe() const
}
}
return (GetZProbeParameters().invertReading) ? 1000 - zProbeVal : zProbeVal;
return (GetCurrentZProbeParameters().invertReading) ? 1000 - zProbeVal : zProbeVal;
}
// Return the Z probe secondary values.
@ -562,7 +604,7 @@ int Platform::GetZProbeSecondaryValues(int& v1, int& v2)
{
if (zProbeOnFilter.IsValid() && zProbeOffFilter.IsValid())
{
switch (nvData.zProbeType)
switch (zProbeType)
{
case 2: // modulated IR sensor
v1 = (int) (zProbeOnFilter.GetSum() / (4 * Z_PROBE_AVERAGE_READINGS)); // pass back the reading with IR turned on
@ -574,18 +616,9 @@ int Platform::GetZProbeSecondaryValues(int& v1, int& v2)
return 0;
}
int Platform::GetZProbeType() const
{
return nvData.zProbeType;
}
void Platform::SetZProbeAxes(uint32_t axes)
{
nvData.zProbeAxes = axes;
if (autoSaveEnabled)
{
WriteNvData();
}
zProbeAxes = axes;
}
// Get our best estimate of the Z probe temperature
@ -606,87 +639,77 @@ float Platform::GetZProbeTemperature()
float Platform::ZProbeStopHeight()
{
return GetZProbeParameters().GetStopHeight(GetZProbeTemperature());
return GetCurrentZProbeParameters().GetStopHeight(GetZProbeTemperature());
}
float Platform::GetZProbeDiveHeight() const
{
return GetZProbeParameters().diveHeight;
return GetCurrentZProbeParameters().diveHeight;
}
float Platform::GetZProbeStartingHeight()
{
const ZProbeParameters& params = GetCurrentZProbeParameters();
return params.diveHeight + max<float>(params.GetStopHeight(GetZProbeTemperature()), 0.0);
}
float Platform::GetZProbeTravelSpeed() const
{
return GetZProbeParameters().travelSpeed;
return GetCurrentZProbeParameters().travelSpeed;
}
void Platform::SetZProbeType(int pt)
{
int newZProbeType = (pt >= 0 && pt <= 7) ? pt : 0;
if (newZProbeType != nvData.zProbeType)
{
nvData.zProbeType = newZProbeType;
if (autoSaveEnabled)
{
WriteNvData();
}
}
zProbeType = (pt >= 0 && pt <= 7) ? pt : 0;
InitZProbe();
}
const ZProbeParameters& Platform::GetZProbeParameters() const
const ZProbeParameters& Platform::GetZProbeParameters(int32_t probeType) const
{
switch (nvData.zProbeType)
switch (probeType)
{
case 1:
case 2:
return nvData.irZProbeParameters;
case 5:
return irZProbeParameters;
case 3:
case 7:
return nvData.alternateZProbeParameters;
return alternateZProbeParameters;
case 4:
case 5:
case 6:
default:
return nvData.switchZProbeParameters;
return switchZProbeParameters;
}
}
void Platform::SetZProbeParameters(const ZProbeParameters& params)
void Platform::SetZProbeParameters(int32_t probeType, const ZProbeParameters& params)
{
if (GetZProbeParameters() != params)
switch (probeType)
{
switch (nvData.zProbeType)
{
case 1:
case 2:
nvData.irZProbeParameters = params;
break;
case 1:
case 2:
case 5:
irZProbeParameters = params;
break;
case 3:
case 7:
nvData.alternateZProbeParameters = params;
break;
case 3:
case 7:
alternateZProbeParameters = params;
break;
case 4:
case 5:
case 6:
default:
nvData.switchZProbeParameters = params;
break;
}
if (autoSaveEnabled)
{
WriteNvData();
}
case 4:
case 6:
default:
switchZProbeParameters = params;
break;
}
}
// Return true if the specified point is accessible to the Z probe
bool Platform::IsAccessibleProbePoint(float x, float y) const
{
x -= GetZProbeParameters().xOffset;
y -= GetZProbeParameters().yOffset;
x -= GetCurrentZProbeParameters().xOffset;
y -= GetCurrentZProbeParameters().yOffset;
return (reprap.GetMove()->IsDeltaMode())
? x * x + y * y < reprap.GetMove()->GetDeltaParams().GetPrintRadiusSquared()
: x >= axisMinima[X_AXIS] && y >= axisMinima[Y_AXIS] && x <= axisMaxima[X_AXIS] && y <= axisMaxima[Y_AXIS];
@ -695,75 +718,7 @@ bool Platform::IsAccessibleProbePoint(float x, float y) const
// Return true if we must home X and Y before we home Z (i.e. we are using a bed probe)
bool Platform::MustHomeXYBeforeZ() const
{
return (nvData.zProbeType != 0) && ((nvData.zProbeAxes & (1 << Z_AXIS)) != 0);
}
void Platform::ResetNvData()
{
nvData.compatibility = marlin; // default to Marlin because the common host programs expect the "OK" response to commands
ARRAY_INIT(nvData.ipAddress, IP_ADDRESS);
ARRAY_INIT(nvData.netMask, NET_MASK);
ARRAY_INIT(nvData.gateWay, GATE_WAY);
#ifdef DUET_NG
memset(nvData.macAddress, 0xFF, sizeof(nvData.macAddress));
#else
ARRAY_INIT(nvData.macAddress, MAC_ADDRESS);
#endif
nvData.zProbeType = 0; // Default is to use no Z probe switch
nvData.zProbeAxes = Z_PROBE_AXES;
nvData.switchZProbeParameters.Init(0.0);
nvData.irZProbeParameters.Init(Z_PROBE_STOP_HEIGHT);
nvData.alternateZProbeParameters.Init(Z_PROBE_STOP_HEIGHT);
for (size_t i = 0; i < HEATERS; ++i)
{
PidParameters& pp = nvData.pidParams[i];
pp.kI = defaultPidKis[i];
pp.kD = defaultPidKds[i];
pp.kP = defaultPidKps[i];
pp.kT = defaultPidKts[i];
pp.kS = defaultPidKss[i];
}
#if FLASH_SAVE_ENABLED
nvData.magic = FlashData::magicValue;
nvData.version = FlashData::versionValue;
#endif
}
void Platform::ReadNvData()
{
#if FLASH_SAVE_ENABLED
DueFlashStorage::read(FlashData::nvAddress, &nvData, sizeof(nvData));
if (nvData.magic != FlashData::magicValue || nvData.version != FlashData::versionValue)
{
// Nonvolatile data has not been initialized since the firmware was last written, so set up default values
ResetNvData();
// No point in writing it back here
}
#else
Message(BOTH_ERROR_MESSAGE, "Cannot load non-volatile data, because Flash support has been disabled!\n");
#endif
}
void Platform::WriteNvData()
{
#if FLASH_SAVE_ENABLED
DueFlashStorage::write(FlashData::nvAddress, &nvData, sizeof(nvData));
#else
Message(BOTH_ERROR_MESSAGE, "Cannot write non-volatile data, because Flash support has been disabled!\n");
#endif
}
void Platform::SetAutoSave(bool enabled)
{
#if FLASH_SAVE_ENABLED
autoSaveEnabled = enabled;
#else
Message(BOTH_ERROR_MESSAGE, "Cannot enable auto-save, because Flash support has been disabled!\n");
#endif
return (zProbeType != 0) && ((zProbeAxes & (1 << Z_AXIS)) != 0);
}
// Check the prerequisites for updating the main firmware. Return True if satisfied, else print as message and return false.
@ -1011,7 +966,7 @@ void Platform::Exit()
Compatibility Platform::Emulating() const
{
return (nvData.compatibility == reprapFirmware) ? me : nvData.compatibility;
return (compatibility == reprapFirmware) ? me : compatibility;
}
void Platform::SetEmulating(Compatibility c)
@ -1025,46 +980,30 @@ void Platform::SetEmulating(Compatibility c)
{
c = me;
}
if (c != nvData.compatibility)
{
nvData.compatibility = c;
if (autoSaveEnabled)
{
WriteNvData();
}
}
compatibility = c;
}
void Platform::UpdateNetworkAddress(byte dst[4], const byte src[4])
{
bool changed = false;
for (uint8_t i = 0; i < 4; i++)
{
if (dst[i] != src[i])
{
dst[i] = src[i];
changed = true;
}
}
if (changed && autoSaveEnabled)
{
WriteNvData();
dst[i] = src[i];
}
}
void Platform::SetIPAddress(byte ip[])
{
UpdateNetworkAddress(nvData.ipAddress, ip);
UpdateNetworkAddress(ipAddress, ip);
}
void Platform::SetGateWay(byte gw[])
{
UpdateNetworkAddress(nvData.gateWay, gw);
UpdateNetworkAddress(gateWay, gw);
}
void Platform::SetNetMask(byte nm[])
{
UpdateNetworkAddress(nvData.netMask, nm);
UpdateNetworkAddress(netMask, nm);
}
// Flush messages to USB and aux, returning true if there is more to send
@ -1165,6 +1104,7 @@ void Platform::Spin()
}
// Check the MCU max and min temperatures
#ifndef __RADDS__
if (currentMcuTemperature > highestMcuTemperature)
{
highestMcuTemperature= currentMcuTemperature;
@ -1173,6 +1113,7 @@ void Platform::Spin()
{
lowestMcuTemperature = currentMcuTemperature;
}
#endif
// Diagnostics test
if (debugCode == (int)DiagnosticTestType::TestSpinLockup)
@ -1218,6 +1159,7 @@ void Platform::Spin()
void Platform::SoftwareReset(uint16_t reason)
{
wdt_restart(WDT); // kick the watchdog
if (reason == (uint16_t)SoftwareResetReason::erase)
{
EraseAndReset();
@ -1403,13 +1345,19 @@ void Platform::Diagnostics(MessageType mtype)
// Show the up time and reason for the last reset
const uint32_t now = (uint32_t)Time(); // get up time in seconds
const char* resetReasons[8] = { "power up", "backup", "watchdog", "software", "reset button", "?", "?", "?" };
const char* resetReasons[8] = { "power up", "backup", "watchdog", "software",
#ifdef DUET_NG
// On the SAM4E a watchdog reset may be reported as a user reset because of the capacitor on the NRST pin
"reset button or watchdog",
#else
"reset button",
#endif
"?", "?", "?" };
MessageF(mtype, "Last reset %02d:%02d:%02d ago, cause: %s\n",
(unsigned int)(now/3600), (unsigned int)((now % 3600)/60), (unsigned int)(now % 60),
resetReasons[(REG_RSTC_SR & RSTC_SR_RSTTYP_Msk) >> RSTC_SR_RSTTYP_Pos]);
// Show the reset code stored at the last software reset
Message(mtype, "Last software reset code & available RAM: ");
{
SoftwareResetData srdBuf[SoftwareResetData::numberOfSlots];
memset(srdBuf, 0, sizeof(srdBuf));
@ -1429,9 +1377,10 @@ void Platform::Diagnostics(MessageType mtype)
}
}
Message(mtype, "Last software reset code: ");
if (slot >= 0 && srdBuf[slot].magic == SoftwareResetData::magicValue)
{
MessageF(mtype, "0x%04x, %u (slot %d)\n", srdBuf[slot].resetReason, srdBuf[slot].neverUsedRam, slot);
MessageF(mtype, "0x%04x, available RAM %u bytes (slot %d)\n", srdBuf[slot].resetReason, srdBuf[slot].neverUsedRam, slot);
MessageF(mtype, "Spinning module during software reset: %s\n", moduleName[srdBuf[slot].resetReason & 0x0F]);
}
else
@ -1455,15 +1404,21 @@ void Platform::Diagnostics(MessageType mtype)
MessageF(mtype, "Free file entries: %u\n", numFreeFiles);
// Show the HSMCI CD pin and speed
#ifdef __RADDS__
MessageF(mtype, "SD card 0 %s\n", (sd_mmc_card_detected(0) ? "detected" : "not detected"));
#else
MessageF(mtype, "SD card 0 %s, interface speed: %.1fMBytes/sec\n", (sd_mmc_card_detected(0) ? "detected" : "not detected"), (float)hsmci_get_speed()/1000000.0);
#endif
// Show the longest SD card write time
MessageF(mtype, "SD card longest block write time: %.1fms\n", FileStore::GetAndClearLongestWriteTime());
#ifndef __RADDS__
// Show the MCU temperatures
MessageF(mtype, "MCU temperature: min %.1f, current %.1f, max %.1f\n",
AdcReadingToCpuTemperature(lowestMcuTemperature), AdcReadingToCpuTemperature(currentMcuTemperature), AdcReadingToCpuTemperature(highestMcuTemperature));
lowestMcuTemperature = highestMcuTemperature = currentMcuTemperature;
#endif
#ifdef DUET_NG
// Show the supply voltage
@ -1683,25 +1638,7 @@ bool Platform::AnyHeaterHot(uint16_t heaters, float t)
return false;
}
// Update the heater PID parameters or thermistor resistance etc.
void Platform::SetPidParameters(size_t heater, const PidParameters& params)
{
if (heater < HEATERS && params != nvData.pidParams[heater])
{
nvData.pidParams[heater] = params;
if (autoSaveEnabled)
{
WriteNvData();
}
}
}
const PidParameters& Platform::GetPidParameters(size_t heater) const
{
return nvData.pidParams[heater];
}
// power is a fraction in [0,1]
// Power is a fraction in [0,1]
void Platform::SetHeater(size_t heater, float power)
{
if (heatOnPins[heater] != NoPin)
@ -1755,7 +1692,7 @@ EndStopHit Platform::Stopped(size_t drive) const
else if (endStopType[drive] == EndStopType::noEndStop)
{
// No homing switch is configured for this axis, so see if we should use the Z probe
if (nvData.zProbeType > 0 && drive < reprap.GetGCodes()->GetNumAxes() && (nvData.zProbeAxes & (1 << drive)) != 0)
if (zProbeType > 0 && drive < reprap.GetGCodes()->GetNumAxes() && (zProbeAxes & (1 << drive)) != 0)
{
return GetZProbeResult(); // using the Z probe as a low homing stop for this axis, so just get its result
}
@ -1786,13 +1723,32 @@ uint32_t Platform::GetAllEndstopStates() const
// Return the Z probe result. We assume that if the Z probe is used as an endstop, it is used as the low stop.
EndStopHit Platform::GetZProbeResult() const
{
const int zProbeVal = ZProbe();
const int zProbeADValue = GetZProbeParameters().adcValue;
const int zProbeVal = GetZProbeReading();
const int zProbeADValue = GetCurrentZProbeParameters().adcValue;
return (zProbeVal >= zProbeADValue) ? EndStopHit::lowHit
: (zProbeVal * 10 >= zProbeADValue * 9) ? EndStopHit::lowNear // if we are at/above 90% of the target value
: EndStopHit::noStop;
}
// Write the Z probe parameters to file
bool Platform::WriteZProbeParameters(FileStore *f) const
{
bool ok = f->Write("; Z probe parameters\n");
if (ok)
{
ok = irZProbeParameters.WriteParameters(f, 1);
}
if (ok)
{
ok = alternateZProbeParameters.WriteParameters(f, 3);
}
if (ok)
{
ok = switchZProbeParameters.WriteParameters(f, 4);
}
return ok;
}
// This is called from the step ISR as well as other places, so keep it fast
void Platform::SetDirection(size_t drive, bool direction)
{
@ -2230,18 +2186,9 @@ void Platform::InitFans()
void Platform::SetMACAddress(uint8_t mac[])
{
bool changed = false;
for (size_t i = 0; i < 6; i++)
{
if (nvData.macAddress[i] != mac[i])
{
nvData.macAddress[i] = mac[i];
changed = true;
}
}
if (changed && autoSaveEnabled)
{
WriteNvData();
macAddress[i] = mac[i];
}
}
@ -2654,9 +2601,6 @@ const char* Platform::GetBoardString() const
// User I/O and servo support
bool Platform::GetFirmwarePin(int logicalPin, PinAccess access, Pin& firmwarePin, bool& invert)
{
#ifdef __RADDS__
# error This needs to be modified for RADDS
#endif
firmwarePin = NoPin; // assume failure
invert = false; // this is the common case
if (logicalPin < 0 || logicalPin > HighestLogicalPin)
@ -2832,6 +2776,7 @@ char Platform::ReadFromSource(const SerialSource source)
return 0;
}
#ifndef __RADDS__
// CPU temperature
void Platform::GetMcuTemperatures(float& minT, float& currT, float& maxT) const
{
@ -2839,6 +2784,7 @@ void Platform::GetMcuTemperatures(float& minT, float& currT, float& maxT) const
currT = AdcReadingToCpuTemperature(currentMcuTemperature);
maxT = AdcReadingToCpuTemperature(highestMcuTemperature);
}
#endif
#ifdef DUET_NG
// Power in voltage
@ -3035,13 +2981,15 @@ void Platform::Tick()
case 2: // last conversion started was the Z probe, with IR LED on
const_cast<ZProbeAveragingFilter&>(zProbeOnFilter).ProcessReading(GetRawZProbeReading());
if (nvData.zProbeType == 2) // if using a modulated IR sensor
if (zProbeType == 2) // if using a modulated IR sensor
{
digitalWrite(zProbeModulationPin, LOW); // turn off the IR emitter
}
// Read the MCU temperature as well (no need to do it in every state)
#ifndef __RADDS__
currentMcuTemperature = AnalogInReadChannel(temperatureAdcChannel);
#endif
++tickState;
break;
@ -3051,7 +2999,7 @@ void Platform::Tick()
// no break
case 0: // this is the state after initialisation, no conversion has been started
default:
if (nvData.zProbeType == 2) // if using a modulated IR sensor
if (zProbeType == 2) // if using a modulated IR sensor
{
digitalWrite(zProbeModulationPin, HIGH); // turn on the IR emitter
}

View file

@ -46,6 +46,7 @@ Licence: GPL
// Platform-specific includes
#include "Core.h"
#include "DueFlashStorage.h"
#include "Heating/TemperatureSensor.h"
#include "Heating/Thermistor.h"
#include "Heating/TemperatureError.h"
@ -54,7 +55,7 @@ Licence: GPL
#if defined(DUET_NG)
# include "DueXn.h"
#else
#elif !defined(__RADDS__)
# include "Libraries/MCP4461/MCP4461.h"
#endif
@ -125,20 +126,6 @@ const uint32_t Z_PROBE_AXES = (1 << X_AXIS) | (1 << Z_AXIS); // Axes for which t
// HEATERS - The bed is assumed to be the at index 0
// The thermistors used in the R3epRapPro Ormerod are:
// Bed thermistor: http://uk.farnell.com/epcos/b57863s103f040/sensor-miniature-ntc-10k/dp/1299930?Ntt=129-9930
// Hot end thermistor: http://www.digikey.co.uk/product-search/en?x=20&y=11&KeyWords=480-3137-ND
// Note: a negative P, I or D value means do not use PID for this heater, use bang-bang control instead.
// This allows us to switch between PID and bang-bang using the M301 and M304 commands.
// We use method 2 (see above)
const float defaultPidKis[HEATERS] = HEATERS_(5.0, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2); // Integral PID constants
const float defaultPidKds[HEATERS] = HEATERS_(500.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0); // Derivative PID constants
const float defaultPidKps[HEATERS] = HEATERS_(-1.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0); // Proportional PID constants, negative values indicate use bang-bang instead of PID
const float defaultPidKts[HEATERS] = HEATERS_(2.7, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4); // approximate PWM value needed to maintain temperature, per degC above room temperature
const float defaultPidKss[HEATERS] = HEATERS_(1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0); // PWM scaling factor, to allow for variation in heater power and supply voltage
// Define the number of temperature readings we average for each thermistor. This should be a power of 2 and at least 4 ** AD_OVERSAMPLE_BITS.
// Keep THERMISTOR_AVERAGE_READINGS * NUM_HEATERS * 2ms no greater than HEAT_SAMPLE_TIME or the PIDs won't work well.
const unsigned int ThermistorAverageReadings = 32;
@ -190,11 +177,17 @@ enum class EndStopType
/***************************************************************************************************/
// Enumeration describing the reasons for a software reset.
// The spin state gets or'ed into this, so keep the lower ~4 bits unused.
// The spin state gets or'ed into this, so keep the lower 4 bits unused.
enum class SoftwareResetReason : uint16_t
{
user = 0, // M999 command
erase = 55, // special M999 command to erase firmware and reset
erase = 0x10, // special M999 command to erase firmware and reset
NMI = 0x20,
hardFault = 0x30,
memManage = 0x40,
busFault = 0x50,
usageFault = 0x60,
otherFault = 0x70,
inAuxOutput = 0x0800, // this bit is or'ed in if we were in aux output at the time
stuckInSpin = 0x1000, // we got stuck in a Spin() function for too long
inLwipSpin = 0x2000, // we got stuck in a call to LWIP for too long
@ -240,64 +233,13 @@ struct ZProbeParameters
float extraParam; // extra parameters used by some types of probe e.g. Delta probe
bool invertReading; // true if we need to invert the reading
void Init(float h)
{
adcValue = Z_PROBE_AD_VALUE;
xOffset = yOffset = 0.0;
height = h;
calibTemperature = 20.0;
temperatureCoefficient = 0.0; // no default temperature correction
diveHeight = DEFAULT_Z_DIVE;
probeSpeed = DEFAULT_PROBE_SPEED;
travelSpeed = DEFAULT_TRAVEL_SPEED;
recoveryTime = extraParam = 0.0;
invertReading = false;
}
float GetStopHeight(float temperature) const
{
return ((temperature - calibTemperature) * temperatureCoefficient) + height;
}
bool operator==(const ZProbeParameters& other) const
{
return adcValue == other.adcValue
&& height == other.height
&& xOffset == other.xOffset
&& yOffset == other.yOffset
&& calibTemperature == other.calibTemperature
&& temperatureCoefficient == other.temperatureCoefficient
&& diveHeight == other.diveHeight
&& probeSpeed == other.probeSpeed
&& travelSpeed == other.travelSpeed
&& recoveryTime == other.recoveryTime
&& extraParam == other.extraParam
&& invertReading == other.invertReading;
}
bool operator!=(const ZProbeParameters& other) const
{
return !operator==(other);
}
};
class PidParameters
{
// If you add any more variables to this class, don't forget to change the definition of operator== in Platform.cpp!
public:
float kI, kD, kP, kT, kS;
bool UsePID() const;
bool operator==(const PidParameters& other) const;
bool operator!=(const PidParameters& other) const
{
return !operator==(other);
}
void Init(float h);
float GetStopHeight(float temperature) const;
bool WriteParameters(FileStore *f, unsigned int probeType) const;
};
// Class to perform averaging of values read from the ADC
// numAveraged should be a power of 2 for best efficiency
template<size_t numAveraged> class AveragingFilter
{
public:
@ -547,19 +489,25 @@ public:
// Z probe
void SetZProbeDefaults();
float ZProbeStopHeight();
float GetZProbeDiveHeight() const;
float GetZProbeStartingHeight();
float GetZProbeTravelSpeed() const;
int ZProbe() const;
int GetZProbeReading() const;
EndStopHit GetZProbeResult() const;
int GetZProbeSecondaryValues(int& v1, int& v2);
void SetZProbeType(int iZ);
int GetZProbeType() const;
int GetZProbeType() const { return zProbeType; }
void SetZProbeAxes(uint32_t axes);
uint32_t GetZProbeAxes() const { return nvData.zProbeAxes; }
const ZProbeParameters& GetZProbeParameters() const;
void SetZProbeParameters(const struct ZProbeParameters& params);
uint32_t GetZProbeAxes() const { return zProbeAxes; }
const ZProbeParameters& GetZProbeParameters(int32_t probeType) const;
const ZProbeParameters& GetCurrentZProbeParameters() const { return GetZProbeParameters(zProbeType); }
void SetZProbeParameters(int32_t probeType, const struct ZProbeParameters& params);
bool MustHomeXYBeforeZ() const;
bool WriteZProbeParameters(FileStore *f) const;
// Ancilliary PWM
void SetExtrusionAncilliaryPwmValue(float v);
float GetExtrusionAncilliaryPwmValue() const;
@ -573,23 +521,19 @@ public:
// Heat and temperature
float GetTemperature(size_t heater, TemperatureError& err) // Result is in degrees Celsius
pre (heater < HEATERS);
pre(heater < HEATERS);
float GetZProbeTemperature(); // Get our best estimate of the Z probe temperature
void SetHeater(size_t heater, float power) // power is a fraction in [0,1]
pre (heater < HEATERS);
pre(heater < HEATERS);
uint32_t HeatSampleInterval() const;
void SetHeatSampleTime(float st);
float GetHeatSampleTime() const;
void SetPidParameters(size_t heater, const PidParameters& params);
const PidParameters& GetPidParameters(size_t heater) const
pre (heater < HEATERS);
Thermistor& GetThermistor(size_t heater)
pre (heater < HEATERS)
pre(heater < HEATERS)
{
return thermistors[heater];
}
@ -598,7 +542,7 @@ public:
pre(heater < HEATERS; thermistor < HEATERS);
int GetThermistorNumber(size_t heater) const
pre (heater < HEATERS);
pre(heater < HEATERS);
bool IsThermistorChannel(uint8_t heater) const
pre(heater < HEATERS);
@ -627,11 +571,6 @@ public:
float GetFanRPM();
// Flash operations
void ResetNvData();
void ReadNvData();
void WriteNvData();
void SetAutoSave(bool enabled);
void UpdateFirmware();
bool CheckFirmwareUpdatePrerequisites();
@ -651,7 +590,9 @@ public:
bool Inkjet(int bitPattern);
// MCU temperature
#ifndef __RADDS
void GetMcuTemperatures(float& minT, float& currT, float& maxT) const;
#endif
void SetMcuTemperatureAdjust(float v) { mcuTemperatureAdjust = v; }
float GetMcuTemperatureAdjust() const { return mcuTemperatureAdjust; }
@ -706,35 +647,22 @@ private:
}
};
#ifdef DUET_NG
static_assert(SoftwareResetData::numberOfSlots * sizeof(SoftwareResetData) <= 512, "Can't fit software reset data in SAM4E user signature area");
#else
static_assert(SoftwareResetData::numberOfSlots * sizeof(SoftwareResetData) <= FLASH_DATA_LENGTH, "NVData too large");
#endif
// The following struct is due to be replaced by the config-override.g file.
struct FlashData
{
static const uint16_t magicValue = 0xE6C4; // value we use to recognise that the flash data has been written
static const uint16_t versionValue = 5; // increment this whenever this struct changes
static const uint32_t nvAddress = SoftwareResetData::nvAddress + (SoftwareResetData::numberOfSlots * sizeof(SoftwareResetData));
uint16_t magic;
uint16_t version;
// The remaining data could alternatively be saved to SD card.
// Note however that if we save them as G codes, we need to provide a way of saving IR and ultrasonic G31 parameters separately.
ZProbeParameters switchZProbeParameters; // Z probe values for the switch Z-probe
ZProbeParameters irZProbeParameters; // Z probe values for the IR sensor
ZProbeParameters alternateZProbeParameters; // Z probe values for the alternate sensor
int zProbeType; // the type of Z probe we are currently using
uint32_t zProbeAxes; // Z probe is used for these axes (bitmap)
PidParameters pidParams[HEATERS];
byte ipAddress[4];
byte netMask[4];
byte gateWay[4];
uint8_t macAddress[6];
Compatibility compatibility;
};
FlashData nvData;
bool autoSaveEnabled;
ZProbeParameters switchZProbeParameters; // Z probe values for the switch Z-probe
ZProbeParameters irZProbeParameters; // Z probe values for the IR sensor
ZProbeParameters alternateZProbeParameters; // Z probe values for the alternate sensor
int zProbeType; // the type of Z probe we are currently using
uint32_t zProbeAxes; // Z probe is used for these axes (bitmap)
byte ipAddress[4];
byte netMask[4];
byte gateWay[4];
uint8_t macAddress[6];
Compatibility compatibility;
BoardType board;
#ifdef DUET_NG
@ -896,9 +824,11 @@ private:
float nozzleDiameter;
// Temperature and power monitoring
#ifndef __RADDS__ // reading temperature on the RADDS messes up one of the heater pins, so don't do it
AnalogChannelNumber temperatureAdcChannel;
uint16_t currentMcuTemperature, highestMcuTemperature, lowestMcuTemperature;
uint16_t mcuAlarmTemperature;
#endif
float mcuTemperatureAdjust;
#ifdef DUET_NG
@ -1208,22 +1138,22 @@ inline bool Platform::IsRtdChannel(uint8_t heater) const
inline const uint8_t* Platform::IPAddress() const
{
return nvData.ipAddress;
return ipAddress;
}
inline const uint8_t* Platform::NetMask() const
{
return nvData.netMask;
return netMask;
}
inline const uint8_t* Platform::GateWay() const
{
return nvData.gateWay;
return gateWay;
}
inline const uint8_t* Platform::MACAddress() const
{
return nvData.macAddress;
return macAddress;
}
inline float Platform::GetPressureAdvance(size_t extruder) const
@ -1252,7 +1182,7 @@ inline void Platform::GetEndStopConfiguration(size_t axis, EndStopType& esType,
// This is called by the tick ISR to get the raw Z probe reading to feed to the filter
inline uint16_t Platform::GetRawZProbeReading() const
{
switch (nvData.zProbeType)
switch (zProbeType)
{
case 4:
{

View file

@ -86,8 +86,10 @@ const Pin HEAT_ON_PINS[HEATERS] = HEATERS_(7, 13, 12, 11, e, f, g, h); // bed, h
// Default thermistor betas
const float BED_R25 = 10000.0;
const float BED_BETA = 4066.0;
const float BED_SHC = 0.0;
const float EXT_R25 = 100000.0;
const float EXT_BETA = 4066.0;
const float EXT_SHC = 0.0;
// Thermistor series resistor value in Ohms
const float THERMISTOR_SERIES_RS = 4700.0;
@ -106,14 +108,8 @@ const Pin ATX_POWER_PIN = 40;
const Pin Z_PROBE_PIN = 5; // RADDS "ADC" pin
// Digital pin number to turn the IR LED on (high) or off (low)
// channel 1: Z_PROBE_MOD_PIN07
// channel 0: Z_PROBE_MOD_PIN
// Make channel 0 == channel 1 for RADDS. Difference in channels
// is an artifact of difference in different versions of Duet electronics
//
// D34 -- unused X-max on RADDS
const Pin Z_PROBE_MOD_PIN = 34;
const Pin Z_PROBE_MOD_PIN07 = 34;
// Use a PWM capable pin
// Firmware uses SamNonDue so feel free to use a non-Arduino pin
@ -128,12 +124,11 @@ const Pin COOLING_FAN_RPM_PIN = 25;
// SD cards
const size_t NumSdCards = 1;
const Pin SdCardDetectPins[NumSdCards] = {NoPin};
const Pin SdCardDetectPins[NumSdCards] = { 14 };
const Pin SdWriteProtectPins[NumSdCards] = { NoPin};
const Pin SdSpiCSPins[1] = {zz}; //FIXME
const Pin SdSpiCSPins[1] = { 77 };
// Definition of which pins we allow to be controlled using M42
//
// Spare pins on the Arduino Due are
//
// D5 / TIOA6 / C.25
@ -150,20 +145,16 @@ const Pin SdSpiCSPins[1] = {zz}; //FIXME
// D72 / RX LED / C.30
// D73 / TX LED / A.21
const size_t NUM_PINS_ALLOWED = 80;
#define PINS_ALLOWED { \
/* pins 00-07 */ 0b01100000, \
/* pins 08-15 */ 0, \
/* pins 16-23 */ 0, \
/* pins 24-31 */ 0, \
/* pins 32-39 */ 0b00000000, \
/* pins 40-47 */ 0, \
/* pins 48-55 */ 0, \
/* pins 56-63 */ 0b00001100, \
/* pins 64-71 */ 0b11111100, \
/* pins 72-79 */ 0b00000011 \
}
// M42 and M208 commands now use logical pin numbers, not firmware pin numbers.
// This is the mapping from logical pins 60+ to firmware pin numbers
const Pin SpecialPinMap[] =
{
5, 6, 58, 59,
66, 67, 68, 69, 70, 71, 73, 73
};
// This next definition defines the highest one.
const int HighestLogicalPin = 60 + ARRAY_SIZE(SpecialPinMap) - 1; // highest logical pin number on this electronics
// SAM3X Flash locations (may be expanded in the future)
const uint32_t IAP_FLASH_START = 0x000F0000;
@ -180,13 +171,6 @@ const uint32_t IAP_FLASH_END = 0x000FFBFF; // don't touch the last 1KB, it's us
#define STEP_TC_IRQN TC3_IRQn
#define STEP_TC_HANDLER TC3_Handler
// Hardware SPI support for SD cards
// SD select
#define SD_SS 4
#define SD_DETECT_PIN 14 // card detect switch; needs pullup asserted
#define SD_DETECT_VAL 0 // detect switch active low
#define SD_DETECT_PIO_ID ID_PIOD
#ifdef LCD_UI

View file

@ -61,7 +61,7 @@ void RepRap::Init()
if (gCodes->RunConfigFile(configFile))
{
while (gCodes->IsRunningConfigFile())
while (gCodes->IsDaemonBusy())
{
// GCodes::Spin will read the macro and ensure DoingFileMacro returns false when it's done
Spin();
@ -647,7 +647,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
response->cat(",\"sensors\":{");
// Probe
const int v0 = platform->ZProbe();
const int v0 = platform->GetZProbeReading();
int v1, v2;
switch (platform->GetZProbeSecondaryValues(v1, v2))
{
@ -759,11 +759,8 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
}
response->catf(",\"endstops\":%d", endstops);
// Delta configuration and number of axes
response->catf(",\"geometry\":\"%s\",\"axes\":%u", move->GetGeometryString(), numAxes);
// Firmware name, for PanelDue
response->catf(",\"firmwareName\":\"%s\"", NAME);
// Firmware name, machine geometry and number of axes
response->catf(",\"firmwareName\":\"%s\",\"geometry\":\"%s\",\"axes\":%u", NAME, move->GetGeometryString(), numAxes);
// Total and mounted volumes
size_t mountedCards = 0;
@ -782,7 +779,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
/* Probe */
{
const ZProbeParameters probeParams = platform->GetZProbeParameters();
const ZProbeParameters probeParams = platform->GetCurrentZProbeParameters();
// Trigger threshold
response->catf(",\"probe\":{\"threshold\":%d", probeParams.adcValue);
@ -854,11 +851,13 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
}
// MCU temperatures
#ifndef __RADDS__
{
float minT, currT, maxT;
platform->GetMcuTemperatures(minT, currT, maxT);
response->catf(",\"mcutemp\":{\"min\":%.1f,\"cur\":%.1f,\"max\":%.1f}", minT, currT, maxT);
}
#endif
#ifdef DUET_NG
// Power in voltages
@ -887,7 +886,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
}
if (ch == '[')
{
response->cat("]");
response->cat(ch); // no extruders
}
// Fraction of file printed
@ -1173,7 +1172,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
response->catf(",\"tool\":%d", toolNumber);
// Send the Z probe value
const int v0 = platform->ZProbe();
const int v0 = platform->GetZProbeReading();
int v1, v2;
switch (platform->GetZProbeSecondaryValues(v1, v2))
{
@ -1236,6 +1235,8 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
response->catf(",\"geometry\":\"%s\",\"axes\":%u,\"volumes\":%u,\"numTools\":%u,\"myName\":",
move->GetGeometryString(), numAxes, NumSdCards, GetNumberOfContiguousTools());
response->EncodeString(myName, ARRAY_SIZE(myName), false);
response->cat(",\"firmwareName\":");
response->EncodeString(NAME, strlen(NAME), false);
}
const int auxSeq = (int)platform->GetAuxSeq();

View file

@ -301,17 +301,19 @@ time_t MassStorage::GetLastModifiedTime(const char* directory, const char *fileN
return 0;
}
bool MassStorage::SetLastModifiedTime(const char *file, time_t time)
bool MassStorage::SetLastModifiedTime(const char* directory, const char *fileName, time_t time)
{
FILINFO fno;
const char *location = (directory != nullptr)
? platform->GetMassStorage()->CombineName(directory, fileName)
: fileName;
const struct tm * const timeInfo = gmtime(&time);
FILINFO fno;
fno.fdate = (WORD)(((timeInfo->tm_year - 80) * 512U) | (timeInfo->tm_mon + 1) * 32U | timeInfo->tm_mday);
fno.ftime = (WORD)(timeInfo->tm_hour * 2048U | timeInfo->tm_min * 32U | timeInfo->tm_sec / 2U);
const bool ok = (f_utime(file, &fno) == FR_OK);
const bool ok = (f_utime(location, &fno) == FR_OK);
if (!ok)
{
reprap.GetPlatform()->MessageF(HTTP_MESSAGE, "SetLastModifiedTime didn't work for file '%s'\n", file);
reprap.GetPlatform()->MessageF(HTTP_MESSAGE, "SetLastModifiedTime didn't work for file '%s'\n", location);
}
return ok;
}

View file

@ -31,7 +31,7 @@ public:
bool DirectoryExists(const char *path) const;
bool DirectoryExists(const char* directory, const char* subDirectory);
time_t GetLastModifiedTime(const char* directory, const char *fileName) const;
bool SetLastModifiedTime(const char *file, time_t time);
bool SetLastModifiedTime(const char* directory, const char *file, time_t time);
bool Mount(size_t card, StringRef& reply, bool reportSuccess);
bool Unmount(size_t card, StringRef& reply);
bool IsDriveMounted(size_t drive) const { return drive < NumSdCards && isMounted[drive]; }