From 2a818bcfc016e6d9b8be51ad0b1e152783da731c Mon Sep 17 00:00:00 2001 From: David Crocker Date: Sun, 3 Aug 2014 11:17:48 +0100 Subject: [PATCH] Version 0.78h Added a few more improvements from RRP's dev branch Corrected M201 output Added a few more minor fixes --- Configuration.h | 6 +- GCodes.cpp | 484 ++++++++++-------- Heat.cpp | 41 +- Heat.h | 13 + Platform.cpp | 106 ++-- Platform.h | 74 ++- ...-dc42.bin => RepRapFirmware-078h-dc42.bin} | Bin 210684 -> 211860 bytes RepRapFirmware.cpp | 11 +- Reprap.h | 26 + Tool.cpp | 97 +++- Tool.h | 53 +- Webserver.cpp | 1 - 12 files changed, 583 insertions(+), 329 deletions(-) rename Release/{RepRapFirmware-078g-dc42.bin => RepRapFirmware-078h-dc42.bin} (62%) diff --git a/Configuration.h b/Configuration.h index 55721d1..9c8c53e 100644 --- a/Configuration.h +++ b/Configuration.h @@ -24,8 +24,8 @@ Licence: GPL #define CONFIGURATION_H #define NAME "RepRapFirmware" -#define VERSION "0.78g-dc42" -#define DATE "2014-07-30" +#define VERSION "0.78h-dc42" +#define DATE "2014-08-01" #define AUTHORS "reprappro, dc42. zpl" // Other firmware that we might switch to be compatible with. @@ -50,6 +50,8 @@ enum Compatibility #define TEMPERATURE_CLOSE_ENOUGH (2.5) // Celsius #define TEMPERATURE_LOW_SO_DONT_CARE (40.0) // Celsius +#define HOT_ENOUGH_TO_EXTRUDE (160.0) // Celsius +#define TIME_TO_HOT (120.0) // Seconds // If temperatures fall outside this range, something nasty has happened. diff --git a/GCodes.cpp b/GCodes.cpp index 938b93d..dc7830a 100644 --- a/GCodes.cpp +++ b/GCodes.cpp @@ -90,12 +90,12 @@ void GCodes::Reset() probeCount = 0; cannedCycleMoveCount = 0; cannedCycleMoveQueued = false; - speedFactor = 1.0/60.0; // default is just to convert from mm/minute to mm/second - speedFactorChange = 1.0; - for (size_t i = 0; i < DRIVES - AXES; ++i) - { - extrusionFactors[i] = 1.0; - } + speedFactor = 1.0/60.0; // default is just to convert from mm/minute to mm/second + speedFactorChange = 1.0; + for (size_t i = 0; i < DRIVES - AXES; ++i) + { + extrusionFactors[i] = 1.0; + } } void GCodes::DoFilePrint(GCodeBuffer* gb) @@ -164,7 +164,7 @@ void GCodes::Spin() int8_t i = 0; do { - char b = webserver->ReadGCode(); + char b = webserver->ReadGCode(); if (webGCode->Put(b)) { // we have a complete gcode @@ -245,23 +245,20 @@ void GCodes::Diagnostics() bool GCodes::AllMovesAreFinishedAndMoveBufferIsLoaded() { - // Last one gone? + // Last one gone? + if(moveAvailable) + return false; - if(moveAvailable) - return false; - - // Wait for all the queued moves to stop so we get the actual last position and feedrate - - if(!reprap.GetMove()->AllMovesAreFinished()) - return false; - reprap.GetMove()->ResumeMoving(); - - // Load the last position; If Move can't accept more, return false - should never happen - - if(!reprap.GetMove()->GetCurrentUserPosition(moveBuffer)) - return false; + // Wait for all the queued moves to stop so we get the actual last position and feedrate + if(!reprap.GetMove()->AllMovesAreFinished()) + return false; + reprap.GetMove()->ResumeMoving(); - return true; + // Load the last position; If Move can't accept more, return false - should never happen + if(!reprap.GetMove()->GetCurrentUserPosition(moveBuffer)) + return false; + + return true; } // Save (some of) the state of the machine for recovery in the future. @@ -269,58 +266,58 @@ bool GCodes::AllMovesAreFinishedAndMoveBufferIsLoaded() bool GCodes::Push() { - if(stackPointer >= STACK) - { - platform->Message(BOTH_ERROR_MESSAGE, "Push(): stack overflow!\n"); - return true; - } - - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) - return false; - - drivesRelativeStack[stackPointer] = drivesRelative; - axesRelativeStack[stackPointer] = axesRelative; - feedrateStack[stackPointer] = moveBuffer[DRIVES]; - fileStack[stackPointer].CopyFrom(fileBeingPrinted); - stackPointer++; - platform->PushMessageIndent(); - return true; + if(stackPointer >= STACK) + { + platform->Message(BOTH_ERROR_MESSAGE, "Push(): stack overflow!\n"); + return true; + } + + if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) + return false; + + drivesRelativeStack[stackPointer] = drivesRelative; + axesRelativeStack[stackPointer] = axesRelative; + feedrateStack[stackPointer] = moveBuffer[DRIVES]; + fileStack[stackPointer].CopyFrom(fileBeingPrinted); + stackPointer++; + platform->PushMessageIndent(); + return true; } // Recover a saved state. Call repeatedly till it returns true. bool GCodes::Pop() { - if(stackPointer < 1) - { - platform->Message(BOTH_ERROR_MESSAGE, "Pop(): stack underflow!\n"); - return true; - } - - if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) - return false; - - stackPointer--; - drivesRelative = drivesRelativeStack[stackPointer]; - axesRelative = axesRelativeStack[stackPointer]; - fileBeingPrinted.MoveFrom(fileStack[stackPointer]); - platform->PopMessageIndent(); + if(stackPointer < 1) + { + platform->Message(BOTH_ERROR_MESSAGE, "Pop(): stack underflow!\n"); + return true; + } - // Remember for next time if we have just been switched to absolute drive moves - // DC 2014-07-16: the following code is wrong, it messes up the absolute extruder position (typically it resets it to zero) and does nothing useful as far as I can see. - // So I am commenting it out. - //for(int8_t i = AXES; i < DRIVES; i++) - //{ - // lastPos[i - AXES] = moveBuffer[i]; - //} - - // Set the correct feedrate - - moveBuffer[DRIVES] = feedrateStack[stackPointer]; - - endStopsToCheck = 0; - moveAvailable = true; - return true; + if(!AllMovesAreFinishedAndMoveBufferIsLoaded()) + return false; + + stackPointer--; + drivesRelative = drivesRelativeStack[stackPointer]; + axesRelative = axesRelativeStack[stackPointer]; + fileBeingPrinted.MoveFrom(fileStack[stackPointer]); + platform->PopMessageIndent(); + + // Remember for next time if we have just been switched to absolute drive moves + // DC 2014-07-16: the following code is wrong, it messes up the absolute extruder position (typically it resets it to zero) and does nothing useful as far as I can see. + // So I am commenting it out. + //for(int8_t i = AXES; i < DRIVES; i++) + //{ + // lastPos[i - AXES] = moveBuffer[i]; + //} + + // Set the correct feedrate + + moveBuffer[DRIVES] = feedrateStack[stackPointer]; + + endStopsToCheck = 0; + moveAvailable = true; + return true; } // Move expects all axis movements to be absolute, and all @@ -346,8 +343,8 @@ bool GCodes::LoadMoveBufferFromGCode(GCodeBuffer *gb, bool doingG92, bool applyL platform->Message(BOTH_ERROR_MESSAGE, "Attempting to extrude with no tool selected.\n"); return false; } - float eMovement[DRIVES-AXES]; int eMoveCount = tool->DriveCount(); + float eMovement[DRIVES-AXES]; gb->GetFloatArray(eMovement, eMoveCount); if(tool->DriveCount() != eMoveCount) { @@ -386,7 +383,7 @@ bool GCodes::LoadMoveBufferFromGCode(GCodeBuffer *gb, bool doingG92, bool applyL { if(gb->Seen(axisLetters[axis])) { - float moveArg = gb->GetFValue()*distanceScale; + float moveArg = gb->GetFValue() * distanceScale; if (axesRelative && !doingG92) { moveArg += moveBuffer[axis]; @@ -550,7 +547,7 @@ bool GCodes::FileCannedCyclesReturn() // To execute any move, call this until it returns true. // moveToDo[] entries corresponding with false entries in action[] will -// be ignored. Recall that moveToDo[DRIVES] should contain the feed rate +// be ignored. Recall that moveToDo[DRIVES] should contain the feedrate // you want (if action[DRIVES] is true). bool GCodes::DoCannedCycleMove(EndstopChecks ce) @@ -1127,11 +1124,11 @@ void GCodes::QueueFileToPrint(const char* fileName) void GCodes::DeleteFile(const char* fileName) { - if(!platform->GetMassStorage()->Delete(platform->GetGCodeDir(), fileName)) - { - snprintf(scratchString, STRING_LENGTH, "Unsuccessful attempt to delete: %s\n", fileName); - platform->Message(BOTH_ERROR_MESSAGE, scratchString); - } + if(!platform->GetMassStorage()->Delete(platform->GetGCodeDir(), fileName)) + { + snprintf(scratchString, STRING_LENGTH, "Unsuccessful attempt to delete: %s\n", fileName); + platform->Message(BOTH_ERROR_MESSAGE, scratchString); + } } // Send the config file to USB in response to an M503 command. @@ -1168,7 +1165,7 @@ bool GCodes::SendConfigToLine() bool GCodes::DoDwell(GCodeBuffer *gb) { - if(!gb->Seen('P')) + if(!gb->Seen('P')) return true; // No time given - throw it away float dwell = 0.001 * (float) gb->GetLValue(); // P values are in milliseconds; we need seconds @@ -1208,48 +1205,48 @@ bool GCodes::DoDwellTime(float dwell) void GCodes::SetOrReportOffsets(char* reply, GCodeBuffer *gb) { - if(gb->Seen('P')) - { - int8_t toolNumber = gb->GetIValue(); - toolNumber += gb->GetToolNumberAdjust(); - Tool* tool = reprap.GetTool(toolNumber); - if(tool == NULL) - { - snprintf(reply, STRING_LENGTH, "Attempt to set/report offsets and temperatures for non-existent tool: %d\n", toolNumber); - return; - } - float standby[HEATERS]; - float active[HEATERS]; - tool->GetVariables(standby, active); - int hCount = tool->HeaterCount(); - if(hCount > 0) - { - bool setting = false; - if(gb->Seen('R')) - { - gb->GetFloatArray(standby, hCount); - setting = true; - } - if(gb->Seen('S')) - { - gb->GetFloatArray(active, hCount); - setting = true; - } - if(setting) - { - tool->SetVariables(standby, active); - } - else - { - reply[0] = 0; - snprintf(reply, STRING_LENGTH, "Tool %d - Active/standby temperature(s): ", toolNumber); - for(int8_t heater = 0; heater < hCount; heater++) - { - sncatf(reply, STRING_LENGTH, "%.1f/%.1f ", active[heater], standby[heater]); - } - } - } - } + if(gb->Seen('P')) + { + int8_t toolNumber = gb->GetIValue(); + toolNumber += gb->GetToolNumberAdjust(); + Tool* tool = reprap.GetTool(toolNumber); + if(tool == NULL) + { + snprintf(reply, STRING_LENGTH, "Attempt to set/report offsets and temperatures for non-existent tool: %d\n", toolNumber); + return; + } + float standby[HEATERS]; + float active[HEATERS]; + tool->GetVariables(standby, active); + int hCount = tool->HeaterCount(); + if(hCount > 0) + { + bool setting = false; + if(gb->Seen('R')) + { + gb->GetFloatArray(standby, hCount); + setting = true; + } + if(gb->Seen('S')) + { + gb->GetFloatArray(active, hCount); + setting = true; + } + if(setting) + { + tool->SetVariables(standby, active); + } + else + { + reply[0] = 0; + snprintf(reply, STRING_LENGTH, "Tool %d - Active/standby temperature(s): ", toolNumber); + for(int8_t heater = 0; heater < hCount; heater++) + { + sncatf(reply, STRING_LENGTH, "%.1f/%.1f ", active[heater], standby[heater]); + } + } + } + } } void GCodes::AddNewTool(GCodeBuffer *gb, char *reply) @@ -1661,13 +1658,13 @@ void GCodes::SetToolHeaters(Tool *tool, float temperature) bool GCodes::ActOnCode(GCodeBuffer *gb) { // M-code parameters might contain letters T and G, e.g. in filenames. - // I assume that G-and T-code parameters never contain the letter M. + // dc42 assumes that G-and T-code parameters never contain the letter M. // Therefore we must check for an M-code first. if (gb->Seen('M')) { return HandleMcode(gb); } - // I don't think a G-code parameter ever contains letter T, or a T-code ever contains letter G. + // dc42 doesn't think a G-code parameter ever contains letter T, or a T-code ever contains letter G. // So it doesn't matter in which order we look for them. if (gb->Seen('G')) { @@ -1913,7 +1910,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) break; case 82: // Use absolute extruder positioning - if (drivesRelative) + if (drivesRelative) // don't reset the absolute extruder position if it was already absolute { for (int8_t extruder = AXES; extruder < DRIVES; extruder++) { @@ -1941,9 +1938,9 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) case 85: // Set inactive time break; - case 92: // Set/report steps/mm for some axes - { - bool seen = false; + case 92: // Set/report steps/mm for some axes + { + bool seen = false; for(int8_t axis = 0; axis < AXES; axis++) { if(gb->Seen(axisLetters[axis])) @@ -1991,8 +1988,8 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) { reprap.GetMove()->SetStepHypotenuse(); } - } - break; + } + break; case 98: if (gb->Seen('P')) @@ -2024,17 +2021,17 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) } break; - case 105: // Deprecated... - strncpy(reply, "T:", STRING_LENGTH); - for(int8_t heater = 1; heater < HEATERS; heater++) - { - if(reprap.GetHeat()->GetStatus(heater) != Heat::HS_off) - { - sncatf(reply, STRING_LENGTH, "%.1f ", reprap.GetHeat()->GetTemperature(heater)); - } - } - sncatf(reply, STRING_LENGTH, "B: %.1f ", reprap.GetHeat()->GetTemperature(0)); - break; + case 105: // Deprecated... + strncpy(reply, "T:", STRING_LENGTH); + for(int8_t heater = 1; heater < HEATERS; heater++) + { + if(reprap.GetHeat()->GetStatus(heater) != Heat::HS_off) + { + sncatf(reply, STRING_LENGTH, "%.1f ", reprap.GetHeat()->GetTemperature(heater)); + } + } + sncatf(reply, STRING_LENGTH, "B: %.1f ", reprap.GetHeat()->GetTemperature(0)); + break; case 106: // Fan on or off if (gb->Seen('I')) @@ -2048,7 +2045,8 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) f = max(f, 0.0); if (coolingInverted) { - platform->CoolingFan(255.0 - f); + // Check if 1.0 or 255.0 may be used as the maximum value + platform->CoolingFan((f <= 1.0 ? 1.0 : 255.0) - f); } else { @@ -2153,7 +2151,6 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) } break; - //TODO M119 case 119: { snprintf(reply, STRING_LENGTH, "Endstops - "); @@ -2188,7 +2185,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) sncatf(reply, STRING_LENGTH, "%c: %s%c ", axisLetters[axis], es, comma); } } - break; + break; case 120: result = Push(); @@ -2231,27 +2228,27 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) } break; - case 140: // Set bed temperature - if(gb->Seen('S')) - { - if(HOT_BED >= 0) - { - reprap.GetHeat()->SetActiveTemperature(HOT_BED, gb->GetFValue()); - reprap.GetHeat()->Activate(HOT_BED); - } - } - if(gb->Seen('R')) - { - if(HOT_BED >= 0) - { - reprap.GetHeat()->SetStandbyTemperature(HOT_BED, gb->GetFValue()); - } - } - break; - - case 141: // Chamber temperature - platform->Message(HOST_MESSAGE, "M141 - heated chamber not yet implemented\n"); - break; + case 140: // Set bed temperature + if(gb->Seen('S')) + { + if(HOT_BED >= 0) + { + reprap.GetHeat()->SetActiveTemperature(HOT_BED, gb->GetFValue()); + reprap.GetHeat()->Activate(HOT_BED); + } + } + if(gb->Seen('R')) + { + if(HOT_BED >= 0) + { + reprap.GetHeat()->SetStandbyTemperature(HOT_BED, gb->GetFValue()); + } + } + break; + + case 141: // Chamber temperature + platform->Message(HOST_MESSAGE, "M141 - heated chamber not yet implemented\n"); + break; // case 160: //number of mixing filament drives TODO: With tools defined, is this needed? // if(gb->Seen('S')) @@ -2261,25 +2258,25 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) // } // break; - case 190: // Deprecated... - if(gb->Seen('S')) - { - if(HOT_BED >= 0) - { - reprap.GetHeat()->SetActiveTemperature(HOT_BED, gb->GetFValue()); - reprap.GetHeat()->Activate(HOT_BED); - if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) // tell Move not to wait for more moves - { - return false; - } - result = reprap.GetHeat()->HeaterAtSetTemperature(HOT_BED); - } - } - break; + case 190: // Deprecated... + if(gb->Seen('S')) + { + if(HOT_BED >= 0) + { + reprap.GetHeat()->SetActiveTemperature(HOT_BED, gb->GetFValue()); + reprap.GetHeat()->Activate(HOT_BED); + if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) // tell Move not to wait for more moves + { + return false; + } + result = reprap.GetHeat()->HeaterAtSetTemperature(HOT_BED); + } + } + break; - case 201: // Set/print axis accelerations FIXME - should these be in /min not /sec ? - { - bool seen = false; + case 201: // Set/print axis accelerations FIXME - should these be in /min not /sec ? + { + bool seen = false; for(int8_t axis = 0; axis < AXES; axis++) { if(gb->Seen(axisLetters[axis])) @@ -2311,16 +2308,16 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) if(!seen) { - snprintf(reply, STRING_LENGTH, "Axis limits - "); - char comma = ','; - for(int8_t axis = 0; axis < AXES; axis++) + snprintf(reply, STRING_LENGTH, "Accelerations: X: %f, Y: %f, Z: %f, E: ", + platform->Acceleration(X_AXIS)/distanceScale, platform->Acceleration(Y_AXIS)/distanceScale, + platform->Acceleration(Z_AXIS)/distanceScale); + for(int8_t drive = AXES; drive < DRIVES; drive++) { - if(axis == AXES - 1) + sncatf(reply, STRING_LENGTH, "%f", platform->Acceleration(drive)/distanceScale); + if(drive < DRIVES-1) { - comma = ' '; + sncatf(reply, STRING_LENGTH, ":"); } - sncatf(reply, STRING_LENGTH, "%c: %.1f min, %.1f max%c ", axisLetters[axis], - platform->AxisMinimum(axis), platform->AxisMaximum(axis), comma); } } } @@ -2383,7 +2380,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) result = OffsetAxes(gb); break; - case 208: // Set/print maximum axis lengths. If there is an S parameter with value 1 then we set the min value, alse we set the max value. + case 208: // Set/print maximum axis lengths. If there is an S parameter with value 1 then we set the min value, else we set the max value. { bool setMin = (gb->Seen('S') ? (gb->GetIValue() == 1): false); bool seen = false; @@ -2406,11 +2403,17 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) if (!seen) { - snprintf(reply, STRING_LENGTH, "X:%.1f Y:%.1f Z:%.1f", - (setMin) ? platform->AxisMinimum(X_AXIS) : platform->AxisMaximum(X_AXIS), - (setMin) ? platform->AxisMinimum(Y_AXIS) : platform->AxisMaximum(Y_AXIS), - (setMin) ? platform->AxisMinimum(Z_AXIS) : platform->AxisMaximum(Z_AXIS) - ); + snprintf(reply, STRING_LENGTH, "Axis limits - "); + char comma = ','; + for(int8_t axis = 0; axis < AXES; axis++) + { + if(axis == AXES - 1) + { + comma = ' '; + } + sncatf(reply, STRING_LENGTH, "%c: %.1f min, %.1f max%c ", axisLetters[axis], + platform->AxisMinimum(axis), platform->AxisMaximum(axis), comma); + } } } break; @@ -2494,7 +2497,23 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) SetPidParameters(gb, 1, reply); break; - case 302: // Allow cold extrudes + case 302: // Allow, deny or report cold extrudes + if (gb->Seen('P')) + { + if (gb->GetIValue() > 0) + { + reprap.AllowColdExtrude(); + } + else + { + reprap.DenyColdExtrude(); + } + } + else + { + snprintf(reply, STRING_LENGTH, "Cold extrudes are %s, use M302 P[1/0] to allow or deny them", + reprap.ColdExtrude() ? "enabled" : "disabled"); + } break; case 304: // Set/report heated bed PID values @@ -2661,28 +2680,47 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) } break; - case 558: // Set Z probe type - if(gb->Seen('P')) - { - platform->SetZProbeType(gb->GetIValue()); - } - else - { - snprintf(reply, STRING_LENGTH, "Z Probe: %d", platform->GetZProbeType()); - } + case 558: // Set or report Z probe type and for which axes it is used + { + bool seen = false; + if(gb->Seen('P')) + { + platform->SetZProbeType(gb->GetIValue()); + seen = true; + } + + bool zProbeAxes[AXES]; + platform->GetZProbeAxes(zProbeAxes); + for(int axis=0; axisSeen(axisLetters[axis])) + { + zProbeAxes[axis] = (gb->GetIValue() > 0); + seen = true; + } + } + + if (seen) + { + platform->SetZProbeAxes(zProbeAxes); + } + else + { + snprintf(reply, STRING_LENGTH, "Z Probe type is %d and it is used for these axes:", platform->GetZProbeType()); + for(int axis=0; axisSeen('P')) - { - str = gb->GetString(); - } - else - { - str = platform->GetConfigFile(); - } + const char* str = (gb->Seen('P') ? gb->GetString() : platform->GetConfigFile()); bool ok = OpenFileToWrite(platform->GetSysDir(), str, gb); if (ok) { @@ -2698,15 +2736,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb) case 560: // Upload reprap.htm or another web interface file { - const char* str; - if (gb->Seen('P')) - { - str = gb->GetString(); - } - else - { - str = INDEX_PAGE; - } + const char* str = (gb->Seen('P') ? gb->GetString() : INDEX_PAGE); bool ok = OpenFileToWrite(platform->GetWebDir(), str, gb); if (ok) { @@ -2899,7 +2929,8 @@ bool GCodes::ChangeTool(int newToolNumber) { toolChangeSequence++; } - } else + } + else { toolChangeSequence++; } @@ -2921,7 +2952,8 @@ bool GCodes::ChangeTool(int newToolNumber) { toolChangeSequence++; } - } else + } + else { toolChangeSequence++; } @@ -2940,7 +2972,8 @@ bool GCodes::ChangeTool(int newToolNumber) { toolChangeSequence++; } - } else + } + else { toolChangeSequence++; } @@ -3057,7 +3090,9 @@ bool GCodeBuffer::Put(char c) // Strip out the line number and checksum while (gcodeBuffer[gcodePointer] != ' ' && gcodeBuffer[gcodePointer]) + { gcodePointer++; + } // Anything there? @@ -3181,7 +3216,8 @@ const void GCodeBuffer::GetFloatArray(float a[], int& returnedLength) { a[i] = a[0]; } - } else + } + else { returnedLength = length; } diff --git a/Heat.cpp b/Heat.cpp index 03cc739..48c511b 100644 --- a/Heat.cpp +++ b/Heat.cpp @@ -32,7 +32,9 @@ Heat::Heat(Platform* p, GCodes* g) void Heat::Init() { for(int8_t heater=0; heater < HEATERS; heater++) + { pids[heater]->Init(); + } lastTime = platform->Time(); longWait = lastTime; active = true; @@ -41,7 +43,9 @@ void Heat::Init() void Heat::Exit() { for(int8_t heater=0; heater < HEATERS; heater++) + { pids[heater]->SwitchOff(); + } platform->Message(HOST_MESSAGE, "Heat class exited.\n"); active = false; } @@ -116,6 +120,7 @@ void PID::Init() temperatureFault = false; active = false; // Default to standby temperature switchedOff = true; + heatingUp = false; } void PID::SwitchOn() @@ -147,7 +152,8 @@ void PID::Spin() return; } - // We are switched on. Check for faults. + // We are switched on. Check for faults. Temperature silly-low or silly-high mean open-circuit + // or shorted thermistor respectively. if(temperature < BAD_LOW_TEMPERATURE || temperature > BAD_HIGH_TEMPERATURE) { @@ -157,8 +163,9 @@ void PID::Spin() platform->SetHeater(heater, 0.0); temperatureFault = true; switchedOff = true; - snprintf(scratchString, STRING_LENGTH, "Temperature measurement fault on heater %d, T = %.1f\n", heater, temperature); + snprintf(scratchString, STRING_LENGTH, "Temperature fault on heater %d, T = %.1f\n", heater, temperature); platform->Message(BOTH_MESSAGE, scratchString); + reprap.FlagTemperatureFault(heater); } } else @@ -166,6 +173,36 @@ void PID::Spin() badTemperatureCount = 0; } + // Now check how long it takes to warm up. If too long, maybe the thermistor is not in contact with the heater + + if(heatingUp && heater != HOT_BED) // FIXME - also check bed warmup time? + { + float tmp = standbyTemperature; + if(active) + { + tmp = activeTemperature; + } + tmp -= TEMPERATURE_CLOSE_ENOUGH; + if(temperature < tmp) + { + float tim = platform->Time() - timeSetHeating; + if(tim > TIME_TO_HOT) + { + platform->SetHeater(heater, 0.0); + temperatureFault = true; + switchedOff = true; + snprintf(scratchString, STRING_LENGTH, "Heating fault on heater %d, T = %.1f C; still not at temperature after %f seconds.\n", + heater, temperature, tim); + platform->Message(BOTH_MESSAGE, scratchString); + reprap.FlagTemperatureFault(heater); + } + } + else + { + heatingUp = false; + } + } + float targetTemperature = (active) ? activeTemperature : standbyTemperature; float error = targetTemperature - temperature; const PidParameters& pp = platform->GetPidParameters(heater); diff --git a/Heat.h b/Heat.h index f6e08ec..4dc862a 100644 --- a/Heat.h +++ b/Heat.h @@ -60,6 +60,8 @@ class PID int8_t heater; // The index of our heater int8_t badTemperatureCount; // Count of sequential dud readings bool temperatureFault; // Has our heater developed a fault? + float timeSetHeating; // When we were switched on + bool heatingUp; // Are we heating up? }; /** @@ -139,12 +141,22 @@ inline void PID::Activate() { SwitchOn(); active = true; + if(!heatingUp) + { + timeSetHeating = platform->Time(); + } + heatingUp = activeTemperature > temperature; } inline void PID::Standby() { SwitchOn(); active = false; + if(!heatingUp) + { + timeSetHeating = platform->Time(); + } + heatingUp = standbyTemperature > temperature; } inline void PID::ResetFault() @@ -158,6 +170,7 @@ inline void PID::SwitchOff() platform->SetHeater(heater, 0.0); active = false; switchedOff = true; + heatingUp = false; } diff --git a/Platform.cpp b/Platform.cpp index 06834ed..ace7941 100644 --- a/Platform.cpp +++ b/Platform.cpp @@ -134,6 +134,7 @@ void Platform::Init() nvData.macAddress = MAC_ADDRESS; nvData.zProbeType = 0; // Default is to use the 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); @@ -232,21 +233,21 @@ void Platform::Init() { if (stepPins[i] >= 0) { - if(i == E0_DRIVE || i == E3_DRIVE) //STEP_PINS {14, 25, 5, X2, 41, 39, X4, 49} + if(i == E0_DRIVE || i == E3_DRIVE) // STEP_PINS {14, 25, 5, X2, 41, 39, X4, 49} pinModeNonDue(stepPins[i], OUTPUT); else pinMode(stepPins[i], OUTPUT); } if (directionPins[i] >= 0) { - if(i == E0_DRIVE) //DIRECTION_PINS {15, 26, 4, X3, 35, 53, 51, 48} + if(i == E0_DRIVE) // DIRECTION_PINS {15, 26, 4, X3, 35, 53, 51, 48} pinModeNonDue(directionPins[i], OUTPUT); else pinMode(directionPins[i], OUTPUT); } if (enablePins[i] >= 0) { - if(i == Z_AXIS || i==E0_DRIVE || i==E2_DRIVE) //ENABLE_PINS {29, 27, X1, X0, 37, X8, 50, 47} + if(i == Z_AXIS || i == E0_DRIVE || i == E2_DRIVE) // ENABLE_PINS {29, 27, X1, X0, 37, X8, 50, 47} pinModeNonDue(enablePins[i], OUTPUT); else pinMode(enablePins[i], OUTPUT); @@ -272,14 +273,14 @@ void Platform::Init() { if (heatOnPins[i] >= 0) { - if(i == E0_HEATER || i==E1_HEATER) //HEAT_ON_PINS {6, X5, X7, 7, 8, 9} + if(i == E0_HEATER || i == E1_HEATER) // HEAT_ON_PINS {6, X5, X7, 7, 8, 9} { - digitalWriteNonDue(heatOnPins[i], HIGH); // turn the heater off + digitalWriteNonDue(heatOnPins[i], HIGH); // turn the heater off pinModeNonDue(heatOnPins[i], OUTPUT); } else { - digitalWrite(heatOnPins[i], HIGH); // turn the heater off + digitalWrite(heatOnPins[i], HIGH); // turn the heater off pinMode(heatOnPins[i], OUTPUT); } } @@ -391,6 +392,23 @@ int Platform::GetZProbeType() const return nvData.zProbeType; } +void Platform::SetZProbeAxes(const bool axes[AXES]) +{ + for(int axis=0; axis 0) - { // Z probe is used for both X and Z. - if (drive != Y_AXIS) - { - int zProbeVal = ZProbe(); - int zProbeADValue = - (nvData.zProbeType == 3) ? - nvData.alternateZProbeParameters.adcValue : nvData.irZProbeParameters.adcValue; - if (zProbeVal >= zProbeADValue) - return lowHit; - else if (zProbeVal * 10 >= zProbeADValue * 9) // if we are at/above 90% of the target value - return lowNear; - else - return noStop; - } + if (nvData.zProbeType > 0 && drive < AXES && nvData.zProbeAxes[drive]) + { + int zProbeVal = ZProbe(); + int zProbeADValue = + (nvData.zProbeType == 3) ? + nvData.alternateZProbeParameters.adcValue : nvData.irZProbeParameters.adcValue; + if (zProbeVal >= zProbeADValue) + return lowHit; + else if (zProbeVal * 10 >= zProbeADValue * 9) // if we are at/above 90% of the target value + return lowNear; + else + return noStop; } if (lowStopPins[drive] >= 0) @@ -965,9 +982,13 @@ void Platform::SetDirection(byte drive, bool direction) if(directionPins[drive] < 0) return; if(drive == E0_DRIVE) //DIRECTION_PINS {15, 26, 4, X3, 35, 53, 51, 48} + { digitalWriteNonDue(directionPins[drive], direction); + } else + { digitalWrite(directionPins[drive], direction); + } } void Platform::Disable(byte drive) @@ -1041,13 +1062,28 @@ float Platform::MotorCurrent(byte drive) return (float)pot * maxStepperDigipotVoltage / (0.256 * 8.0 * senseResistor); } -//Changed to be compatible with existing gcode norms -// M106 S0 = fully off M106 S255 = fully on +// This is a bit of a compromise - old RepRaps used fan speeds in the range +// [0, 255], which is very hardware dependent. It makes much more sense +// to specify speeds in [0.0, 1.0]. This looks at the value supplied (which +// the G Code reader will get right for a float or an int) and attempts to +// do the right thing whichever the user has done. This will only not work +// for an old-style fan speed of 1/255... + void Platform::CoolingFan(float speed) { if(coolingFanPin >= 0) { - byte p =(byte)speed; + byte p; + + if(speed <= 1.0) + { + p = (byte)(255.0 * max(0.0, speed)); + } + else + { + p = (byte)speed; + } + // The cooling fan output pin gets inverted if HEAT_ON == 0 analogWriteNonDue(coolingFanPin, (HEAT_ON == 0) ? (255 - p) : p); } @@ -1057,17 +1093,17 @@ void Platform::CoolingFan(float speed) void Platform::SetInterrupt(float s) // Seconds { - if (s <= 0.0) - { - //NVIC_DisableIRQ(TC3_IRQn); - Message(HOST_MESSAGE, "Negative interrupt!\n"); - s = STANDBY_INTERRUPT_RATE; - } - uint32_t rc = (uint32_t)( (((long)(TIME_TO_REPRAP*s))*84l)/128l ); - TC_SetRA(TC1, 0, rc/2); //50% high, 50% low - TC_SetRC(TC1, 0, rc); - TC_Start(TC1, 0); - NVIC_EnableIRQ(TC3_IRQn); + if (s <= 0.0) + { + //NVIC_DisableIRQ(TC3_IRQn); + Message(HOST_MESSAGE, "Negative interrupt!\n"); + s = STANDBY_INTERRUPT_RATE; + } + uint32_t rc = (uint32_t)( (((long)(TIME_TO_REPRAP*s))*84l)/128l ); + TC_SetRA(TC1, 0, rc/2); //50% high, 50% low + TC_SetRC(TC1, 0, rc); + TC_Start(TC1, 0); + NVIC_EnableIRQ(TC3_IRQn); } //----------------------------------------------------------------------------------------------------- diff --git a/Platform.h b/Platform.h index 65ce675..87a98ba 100644 --- a/Platform.h +++ b/Platform.h @@ -99,6 +99,7 @@ Licence: GPL #define Z_PROBE_STOP_HEIGHT (0.7) // mm #define Z_PROBE_PIN (10) // Analogue pin number #define Z_PROBE_MOD_PIN (52) // Digital pin number to turn the IR LED on (high) or off (low) +#define Z_PROBE_AXES {true, false, true} // Axes for which the Z-probe is normally used const unsigned int numZProbeReadingsAveraged = 8; // we average this number of readings with IR on, and the same number with IR off #define MAX_FEEDRATES {100.0, 100.0, 3.0, 20.0, 20.0, 20.0, 20.0, 20.0} // mm/sec @@ -107,6 +108,12 @@ const unsigned int numZProbeReadingsAveraged = 8; // we average this number of r #define INSTANT_DVS {15.0, 15.0, 0.2, 2.0, 2.0, 2.0, 2.0, 2.0} // (mm/sec) #define NUM_MIXING_DRIVES 1; //number of mixing drives +#define E0_DRIVE 3 //the index of the first Extruder drive +#define E1_DRIVE 4 //the index of the second Extruder drive +#define E2_DRIVE 5 //the index of the third Extruder drive +#define E3_DRIVE 6 //the index of the fourth Extruder drive +#define E4_DRIVE 7 //the index of the fifth Extruder drive + // AXES #define AXIS_MAXIMA {220, 200, 200} // mm @@ -118,12 +125,6 @@ const unsigned int numZProbeReadingsAveraged = 8; // we average this number of r #define Y_AXIS 1 // The index of the Y axis #define Z_AXIS 2 // The index of the Z axis -#define E0_DRIVE 3 //the index of the first Extruder drive -#define E1_DRIVE 4 //the index of the second Extruder drive -#define E2_DRIVE 5 //the index of the third Extruder drive -#define E3_DRIVE 6 //the index of the fourth Extruder drive -#define E4_DRIVE 7 //the index of the fifth Extruder drive - // HEATERS - The bed is assumed to be the at index 0 #define TEMP_SENSE_PINS {5, 4, 0, 7, 8, 9} // Analogue pin numbers @@ -168,15 +169,10 @@ const float defaultFullBand[HEATERS] = {5.0, 30.0, 30.0, 30.0, 30.0, 30.0}; // const float defaultPidMin[HEATERS] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // minimum value of I-term const float defaultPidMax[HEATERS] = {255, 180, 180, 180, 180, 180}; // maximum value of I-term, must be high enough to reach 245C for ABS printing -#define STANDBY_TEMPERATURES {ABS_ZERO, ABS_ZERO} // We specify one for the bed, though it's not needed -#define ACTIVE_TEMPERATURES {ABS_ZERO, ABS_ZERO} -#define COOLING_FAN_PIN X6 // pin D34 is PWM capable but not an Arduino PWM pin - use X6 instead -#define HEAT_ON 0 // 0 for inverted heater (eg Duet v0.6) 1 for not (e.g. Duet v0.4) - #define STANDBY_TEMPERATURES {ABS_ZERO, ABS_ZERO, ABS_ZERO, ABS_ZERO, ABS_ZERO, ABS_ZERO} // We specify one for the bed, though it's not needed #define ACTIVE_TEMPERATURES {ABS_ZERO, ABS_ZERO, ABS_ZERO, ABS_ZERO, ABS_ZERO, ABS_ZERO} -#define COOLING_FAN_PIN X6 //pin D34 is PWM capable but not an Arduino PWM pin - use X6 instead -#define HEAT_ON 0 // 0 for inverted heater (e.g. Duet v0.6) 1 for not (e.g. Duet v0.4) +#define COOLING_FAN_PIN X6 //pin D34 is PWM capable but not an Arduino PWM pin - use X6 instead +#define HEAT_ON 0 // 0 for inverted heater (e.g. Duet v0.6) 1 for not (e.g. Duet v0.4) // For the theory behind ADC oversampling, see http://www.atmel.com/Images/doc8003.pdf const unsigned int adOversampleBits = 1; // number of bits we oversample when reading temperatures @@ -191,10 +187,10 @@ const unsigned int adDisconnectedVirtual = adDisconnectedReal << adOversampleBit #define HOT_BED 0 // The index of the heated bed; set to -1 if there is no heated bed #define E0_HEATER 1 //the index of the first extruder heater -#define E1_HEATER 2 //the index of the first extruder heater -#define E2_HEATER 3 //the index of the first extruder heater -#define E3_HEATER 4 //the index of the first extruder heater -#define E4_HEATER 5 //the index of the first extruder heater +#define E1_HEATER 2 //the index of the second extruder heater +#define E2_HEATER 3 //the index of the third extruder heater +#define E3_HEATER 4 //the index of the fourth extruder heater +#define E4_HEATER 5 //the index of the fifth extruder heater /****************************************************************************************************/ @@ -346,17 +342,17 @@ class FileStore //: public InputOutput public: int8_t Status(); // Returns OR of IOStatus - bool Read(char& b); - int Read(char* buf, unsigned int nBytes); - bool Write(char b); - bool Write(const char *s, unsigned int len); - bool Write(const char* s); - bool Close(); - bool Seek(unsigned long pos); - bool GoToEnd(); // Position the file at the end (so you can write on the end). - unsigned long Length(); // File size in bytes - void Duplicate(); - bool Flush(); + bool Read(char& b); // Read 1 byte + int Read(char* buf, unsigned int nBytes); // Read a block of nBytes length + bool Write(char b); // Write 1 byte + bool Write(const char *s, unsigned int len); // Write a block of len bytes + bool Write(const char* s); // Write a string + bool Close(); // Shut the file and tidy up + bool Seek(unsigned long pos); // Jump to pos in the file + bool GoToEnd(); // Position the file at the end (so you can write on the end). + unsigned long Length(); // File size in bytes + void Duplicate(); // Create a second reference to this file + bool Flush(); // Write remaining buffer data friend class Platform; @@ -610,6 +606,8 @@ public: int GetZProbeSecondaryValues(int& v1, int& v2); void SetZProbeType(int iZ); int GetZProbeType() const; + void SetZProbeAxes(const bool axes[AXES]); + void GetZProbeAxes(bool (&axes)[AXES]); void SetZProbing(bool starting); bool GetZProbeParameters(struct ZProbeParameters& params) const; bool SetZProbeParameters(const struct ZProbeParameters& params); @@ -617,8 +615,8 @@ public: // Mixing support - void SetMixingDrives(int); - int GetMixingDrives(); +// void SetMixingDrives(int); +// int GetMixingDrives(); int8_t SlowestDrive() const; @@ -655,6 +653,7 @@ private: 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 + bool zProbeAxes[AXES]; // Z probe is used for these axes PidParameters pidParams[HEATERS]; byte ipAddress[4]; byte netMask[4]; @@ -991,21 +990,6 @@ inline float Platform::AxisTotalLength(int8_t axis) const return axisMaxima[axis] - axisMinima[axis]; } -inline void Platform::SetMixingDrives(int num_drives) -{ - if(num_drives>(DRIVES-AXES)) - { - Message(HOST_MESSAGE, "More mixing extruder drives set with M160 than exist in firmware configuration\n"); - return; - } - numMixingDrives = num_drives; -} - -inline int Platform::GetMixingDrives() -{ - return numMixingDrives; -} - //******************************************************************************************************** // Drive the RepRap machine - Heat and temperature diff --git a/Release/RepRapFirmware-078g-dc42.bin b/Release/RepRapFirmware-078h-dc42.bin similarity index 62% rename from Release/RepRapFirmware-078g-dc42.bin rename to Release/RepRapFirmware-078h-dc42.bin index eb5a419d8b6adae62173fe88df8736c209beff73..134ee779fb2c1529ee99c3fc8cb7f07a68a8ced0 100644 GIT binary patch delta 48532 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 delta 47776 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 diff --git a/RepRapFirmware.cpp b/RepRapFirmware.cpp index e5d4a54..90beba9 100644 --- a/RepRapFirmware.cpp +++ b/RepRapFirmware.cpp @@ -189,13 +189,12 @@ void RepRap::Init() currentTool = NULL; const uint32_t wdtTicks = 256; // number of watchdog ticks @ 32768Hz/128 before the watchdog times out (max 4095) WDT_Enable(WDT, (wdtTicks << WDT_MR_WDV_Pos) | (wdtTicks << WDT_MR_WDD_Pos) | WDT_MR_WDRSTEN); // enable watchdog, reset the mcu if it times out - active = true; // must do this before we start the network, else the watchdog may time out + coldExtrude = true; // DC42 changed default to true for compatibility because for now we are aiming for copatibility with RRP 0.78 + active = true; // must do this before we start the network, else the watchdog may time out + + snprintf(scratchString, STRING_LENGTH, "%s Version %s dated %s\n", NAME, VERSION, DATE); + platform->Message(HOST_MESSAGE, scratchString); - platform->Message(HOST_MESSAGE, NAME); - platform->Message(HOST_MESSAGE, " Version "); - platform->Message(HOST_MESSAGE, VERSION); - platform->Message(HOST_MESSAGE, ", dated "); - platform->Message(HOST_MESSAGE, DATE); platform->Message(HOST_MESSAGE, ".\n\nExecuting "); platform->Message(HOST_MESSAGE, platform->GetConfigFile()); platform->Message(HOST_MESSAGE, "...\n\n"); diff --git a/Reprap.h b/Reprap.h index 17e0744..a1b7991 100644 --- a/Reprap.h +++ b/Reprap.h @@ -41,7 +41,12 @@ class RepRap Tool* GetCurrentTool(); Tool* GetTool(int toolNumber); void SetToolVariables(int toolNumber, float* standbyTemperatures, float* activeTemperatures); + void AllowColdExtrude(); + void DenyColdExtrude(); + bool ColdExtrude() const; void PrintTool(int toolNumber, char* reply) const; + void FlagTemperatureFault(int8_t dudHeater); + void ClearTemperatureFault(int8_t wasDudHeater); Platform* GetPlatform() const; Move* GetMove() const; Heat* GetHeat() const; @@ -74,6 +79,7 @@ class RepRap bool resetting; uint16_t activeExtruders; uint16_t activeHeaters; + bool coldExtrude; }; inline Platform* RepRap::GetPlatform() const { return platform; } @@ -86,6 +92,26 @@ inline bool RepRap::Debug() const { return debug; } inline Tool* RepRap::GetCurrentTool() { return currentTool; } inline uint16_t RepRap::GetExtrudersInUse() const { return activeExtruders; } inline uint16_t RepRap::GetHeatersInUse() const { return activeHeaters; } +inline bool RepRap::ColdExtrude() const { return coldExtrude; } +inline void RepRap::AllowColdExtrude() { coldExtrude = true; } +inline void RepRap::DenyColdExtrude() { coldExtrude = false; } + +inline void RepRap::FlagTemperatureFault(int8_t dudHeater) +{ + if(toolList != NULL) + { + toolList->FlagTemperatureFault(dudHeater); + } +} + +inline void RepRap::ClearTemperatureFault(int8_t wasDudHeater) +{ + reprap.GetHeat()->ResetFault(wasDudHeater); + if(toolList != NULL) + { + toolList->ClearTemperatureFault(wasDudHeater); + } +} inline void RepRap::SetDebug(bool d) { diff --git a/Tool.cpp b/Tool.cpp index d13503f..c840691 100644 --- a/Tool.cpp +++ b/Tool.cpp @@ -32,6 +32,8 @@ Tool::Tool(int toolNumber, long d[], int dCount, long h[], int hCount) active = false; driveCount = dCount; heaterCount = hCount; + heaterFault = false; + mixing = false; if(driveCount > 0) { @@ -43,9 +45,12 @@ Tool::Tool(int toolNumber, long d[], int dCount, long h[], int hCount) return; } drives = new int[driveCount]; + mix = new float[driveCount]; + float r = 1.0/(float)driveCount; for(int8_t drive = 0; drive < driveCount; drive++) { drives[drive] = d[drive]; + mix[drive] = r; } } @@ -70,7 +75,7 @@ Tool::Tool(int toolNumber, long d[], int dCount, long h[], int hCount) } } -void Tool::Print(char* reply) const +void Tool::Print(char* reply) { snprintf(reply, STRING_LENGTH, "Tool %d - drives: ", myNumber); char comma = ','; @@ -97,6 +102,7 @@ void Tool::Print(char* reply) const sncatf(reply, STRING_LENGTH, " status: %s", active ? "selected" : "standby"); } + float Tool::MaxFeedrate() const { if(driveCount <= 0) @@ -138,17 +144,83 @@ float Tool::InstantDv() const // Add a tool to the end of the linked list. // (We must already be in it.) -void Tool::AddTool(Tool* t) +void Tool::AddTool(Tool* tool) { - Tool* last = this; - Tool* n = next; + Tool* t = this; + Tool* last; + while(t != NULL) + { + if(t->Number() == tool->Number()) + { + reprap.GetPlatform()->Message(HOST_MESSAGE, "Add tool: tool number already in use.\n"); + return; + } + last = t; + t = t->Next(); + } + tool->next = NULL; // Defensive... + last->next = tool; +} + +// There is a temperature fault on a heater. +// Disable all tools using that heater. +// This function must be called for the first +// entry in the linked list. + +void Tool::FlagTemperatureFault(int8_t heater) +{ + Tool* n = this; while(n != NULL) { - last = n; + n->SetTemperatureFault(heater); n = n->Next(); } - t->next = NULL; // Defensive... - last->next = t; +} + +void Tool::ClearTemperatureFault(int8_t heater) +{ + Tool* n = this; + while(n != NULL) + { + n->ResetTemperatureFault(heater); + n = n->Next(); + } +} + +void Tool::SetTemperatureFault(int8_t dudHeater) +{ + for(int8_t heater = 0; heater < heaterCount; heater++) + { + if(dudHeater == heaters[heater]) + { + heaterFault = true; + return; + } + } +} + +void Tool::ResetTemperatureFault(int8_t wasDudHeater) +{ + for(int8_t heater = 0; heater < heaterCount; heater++) + { + if(wasDudHeater == heaters[heater]) + { + heaterFault = false; + return; + } + } +} + +bool Tool::AllHeatersAtHighTemperature() const +{ + for(int8_t heater = 0; heater < heaterCount; heater++) + { + if(reprap.GetHeat()->GetTemperature(heaters[heater]) < HOT_ENOUGH_TO_EXTRUDE) + { + return false; + } + } + return true; } void Tool::Activate(Tool* currentlyActive) @@ -200,6 +272,17 @@ void Tool::GetVariables(float* standby, float* active) const } } +bool Tool::ToolCanDrive() const +{ + if(heaterFault) + return false; + + if(reprap.ColdExtrude() || AllHeatersAtHighTemperature()) + return true; + + return false; +} + // Update the number of active drives and extruders in use to reflect what this tool uses void Tool::UpdateExtruderAndHeaterCount(uint16_t &numExtruders, uint16_t &numHeaters) const { diff --git a/Tool.h b/Tool.h index d7f64ce..5ffafa4 100644 --- a/Tool.h +++ b/Tool.h @@ -34,14 +34,20 @@ public: void GetOffset(float& xx, float& yy, float& zz) const; int DriveCount() const; int Drive(int driveNumber) const; + bool ToolCanDrive() const; int HeaterCount() const; int Heater(int heaterNumber) const; int Number() const; void SetVariables(float* standby, float* active); void GetVariables(float* standby, float* active) const; + void DefineMix(float* m); + float* GetMix() const; + void TurnMixingOn(); + void TurnMixingOff(); + bool Mixing() const; float MaxFeedrate() const; float InstantDv() const; - void Print(char* reply) const; + void Print(char* reply); friend class RepRap; @@ -50,13 +56,20 @@ protected: Tool* Next() const; void Activate(Tool* currentlyActive); void Standby(); - void AddTool(Tool* t); + void AddTool(Tool* tool); + void FlagTemperatureFault(int8_t dudHeater); + void ClearTemperatureFault(int8_t wasDudHeater); void UpdateExtruderAndHeaterCount(uint16_t &extruders, uint16_t &heaters) const; private: + void SetTemperatureFault(int8_t dudHeater); + void ResetTemperatureFault(int8_t wasDudHeater); + bool AllHeatersAtHighTemperature() const; int myNumber; int* drives; + float* mix; + bool mixing; int driveCount; int* heaters; float* activeTemperatures; @@ -64,13 +77,9 @@ private: int heaterCount; Tool* next; bool active; + bool heaterFault; }; -inline int Tool::DriveCount() const -{ - return driveCount; -} - inline int Tool::Drive(int driveNumber) const { return drives[driveNumber]; @@ -96,7 +105,37 @@ inline int Tool::Number() const return myNumber; } +inline void Tool::DefineMix(float* m) +{ + for(int8_t drive = 0; drive < driveCount; drive++) + { + mix[drive] = m[drive]; + } +} +inline float* Tool::GetMix() const +{ + return mix; +} +inline void Tool::TurnMixingOn() +{ + mixing = true; +} + +inline void Tool::TurnMixingOff() +{ + mixing = false; +} + +inline bool Tool::Mixing() const +{ + return mixing; +} + +inline int Tool::DriveCount() const +{ + return driveCount; +} #endif /* TOOL_H_ */ diff --git a/Webserver.cpp b/Webserver.cpp index 57db36a..a9f1be5 100644 --- a/Webserver.cpp +++ b/Webserver.cpp @@ -495,7 +495,6 @@ void Webserver::ConnectionLost(const ConnectionState *cs) if (interpreter->DebugEnabled()) { debugPrintf("Webserver: ConnectionLost called with port %d\n", local_port); - platform->Message(DEBUG_MESSAGE, scratchString); } interpreter->ConnectionLost(local_port);