From 35a9a1a7074858f7784c362bc291809a88ea5f4a Mon Sep 17 00:00:00 2001 From: David Crocker Date: Sat, 12 Nov 2016 18:40:20 +0000 Subject: Version 1.17-dev1 Support R and S parameters in M109 and M190 as Marlin does Added support for M191 Added support for Steinhart-Hart C parameter Thermistor disconnected threshold now depends on thermistor parameters --- src/Configuration.h | 7 +- src/Duet/Pins_Duet.h | 8 +-- src/DuetNG/Pins_DuetNG.h | 6 +- src/GCodes/GCodes.cpp | 176 +++++++++++++++++++++++++++------------------ src/GCodes/GCodes.h | 2 +- src/Heating/Heat.cpp | 15 ++-- src/Heating/Heat.h | 2 +- src/Heating/Thermistor.cpp | 72 +++++++++++++++++++ src/Heating/Thermistor.h | 56 +++++++++++++++ src/Platform.cpp | 159 +++++++++++++--------------------------- src/Platform.h | 63 ++++------------ src/PrintMonitor.cpp | 2 +- src/Reprap.cpp | 19 ++--- 13 files changed, 332 insertions(+), 255 deletions(-) create mode 100644 src/Heating/Thermistor.cpp create mode 100644 src/Heating/Thermistor.h (limited to 'src') diff --git a/src/Configuration.h b/src/Configuration.h index 42e01f49..4392bdd9 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -26,11 +26,11 @@ Licence: GPL // Firmware name is now defined in the Pins file #ifndef VERSION -# define VERSION "1.16" +# define VERSION "1.17dev1" #endif #ifndef DATE -# define DATE "2016-11-08" +# define DATE "2016-11-12" #endif #define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman" @@ -103,6 +103,7 @@ const float AllowedTemperatureDerivativeNoise = 0.25; // How much fluctuation in const float MaxAmbientTemperature = 45.0; // We expect heaters to cool to this temperature or lower when switched off const float NormalAmbientTemperature = 25.0; // The ambient temperature we assume - allow for the printer heating its surroundings a little const float DefaultMaxTempExcursion = 10.0; // How much error we tolerate when maintaining temperature before deciding that a heater fault has occurred +const float MinimumConnectedTemperature = -5.0; // Temperatures below this we treat as a disconnected thermistor static_assert(DefaultMaxTempExcursion > TEMPERATURE_CLOSE_ENOUGH, "DefaultMaxTempExcursion is too low"); @@ -200,7 +201,7 @@ const float FILAMENT_WIDTH = 1.75; // Millimetres #define EOF_STRING "" -// Firmware update files are now defined in the Pins file +// Firmware update file names are now defined in the Pins file // List defaults diff --git a/src/Duet/Pins_Duet.h b/src/Duet/Pins_Duet.h index 6e2a2d91..c097a01a 100644 --- a/src/Duet/Pins_Duet.h +++ b/src/Duet/Pins_Duet.h @@ -38,10 +38,8 @@ const size_t NUM_SERIAL_CHANNELS = 3; // The number of serial IO channels (USB // DRIVES const Pin ENABLE_PINS[DRIVES] = { 29, 27, X1, X0, 37, X8, 50, 47, X13 }; -const bool ENABLE_VALUES[DRIVES] = { false, false, false, false, false, false, false, false, false }; // What to send to enable a drive const Pin STEP_PINS[DRIVES] = { 14, 25, 5, X2, 41, 39, X4, 49, X10 }; const Pin DIRECTION_PINS[DRIVES] = { 15, 26, 4, X3, 35, 53, 51, 48, X11 }; -const bool DIRECTIONS[DRIVES] = { BACKWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS }; // What each axis needs to make it go forwards - defaults // Endstops // RepRapFirmware only has a single endstop per axis. @@ -67,11 +65,13 @@ const Pin HEAT_ON_PINS[HEATERS] = { 6, X5, X7, 7, 8, 9, X17 }; // Heater Chann // Hot end thermistor: http://www.digikey.co.uk/product-search/en?x=20&y=11&KeyWords=480-3137-ND const float BED_R25 = 10000.0; const float BED_BETA = 3988.0; +const float BED_SHC = 0.0; const float EXT_R25 = 100000.0; -const float EXT_BETA = 4138.0; +const float EXT_BETA = 4388.0; +const float EXT_SHC = 0.0; // Thermistor series resistor value in Ohms -const float THERMISTOR_SERIES_RS = 1000.0; +const float THERMISTOR_SERIES_RS = 4700.0; // Number of SPI temperature sensors to support diff --git a/src/DuetNG/Pins_DuetNG.h b/src/DuetNG/Pins_DuetNG.h index 841aeb47..d5da9a10 100644 --- a/src/DuetNG/Pins_DuetNG.h +++ b/src/DuetNG/Pins_DuetNG.h @@ -43,10 +43,8 @@ const Pin ExpansionStart = 200; // Pin numbers at/above this are on the I/O const Pin GlobalTmcEnablePin = 38; // The pin that drives ENN of all TMC2660 drivers on production boards (on pre-production boards they are grounded) const Pin ENABLE_PINS[DRIVES] = { 78, 41, 42, 49, 57, 87, 88, 89, 90, 31 }; -const bool ENABLE_VALUES[DRIVES] = { false, false, false, false, false, false, false, false, false, false }; // What to send to enable a drive const Pin STEP_PINS[DRIVES] = { 70, 71, 72, 69, 68, 66, 65, 64, 67, 91 }; const Pin DIRECTION_PINS[DRIVES] = { 75, 76, 77, 01, 73, 92, 86, 80, 81, 32 }; -const bool DIRECTIONS[DRIVES] = { FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS }; // What each axis needs to make it go forwards - defaults const Pin DueX_SG = 96; // DueX stallguard detect pin = PE0 (was E2_STOP) const Pin DueX_INT = 17; // DueX interrupt pin = PA17 (was E6_STOP) @@ -63,12 +61,12 @@ const Pin TEMP_SENSE_PINS[HEATERS] = { 45, 47, 44, 61, 62, 63, 59, 18 }; // Ther const Pin HEAT_ON_PINS[HEATERS] = { 19, 20, 16, 35, 37, 40, 43, 15 }; // Heater pin numbers (heater 7 pin TBC) // Default thermistor parameters -// Bed thermistor: now assuming 100K -// Hot end thermistor: http://www.digikey.co.uk/product-search/en?x=20&y=11&KeyWords=480-3137-ND const float BED_R25 = 100000.0; const float BED_BETA = 3988.0; +const float BED_SHC = 0.0; const float EXT_R25 = 100000.0; const float EXT_BETA = 4388.0; +const float EXT_SHC = 0.0; // Thermistor series resistor value in Ohms const float THERMISTOR_SERIES_RS = 4700.0; diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 771daa2d..65e67dd8 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -2479,24 +2479,34 @@ void GCodes::SetHeaterParameters(GCodeBuffer *gb, StringRef& reply) int heater = gb->GetIValue(); if (heater >= 0 && heater < HEATERS) { - PidParameters pp = platform->GetPidParameters(heater); + Thermistor& th = platform->GetThermistor(heater); bool seen = false; // We must set the 25C resistance and beta together in order to calculate Rinf. Check for these first. - float r25 = pp.GetThermistorR25(); - float beta = pp.GetBeta(); + float r25 = th.GetR25(); + float beta = th.GetBeta(); + float shC = th.GetShc(); + float seriesR = th.GetSeriesR(); + gb->TryGetFValue('T', r25, seen); gb->TryGetFValue('B', beta, seen); - - if (seen) // if seen R25 or Beta or both + gb->TryGetFValue('C', shC, seen); + gb->TryGetFValue('R', seriesR, seen); + if (seen) { - pp.SetThermistorR25AndBeta(r25, beta); // recalculate Rinf + th.SetParameters(r25, beta, shC, seriesR); } - // Now do the other parameters - gb->TryGetFValue('R', pp.thermistorSeriesR, seen); - gb->TryGetFValue('L', pp.adcLowOffset, seen); - gb->TryGetFValue('H', pp.adcHighOffset, seen); + if (gb->Seen('L')) + { + th.SetLowOffset((int8_t)constrain(gb->GetIValue(), -100, 100)); + seen = true; + } + if (gb->Seen('H')) + { + th.SetHighOffset((int8_t)constrain(gb->GetIValue(), -100, 100)); + seen = true; + } if (gb->Seen('X')) { @@ -2515,14 +2525,11 @@ void GCodes::SetHeaterParameters(GCodeBuffer *gb, StringRef& reply) seen = true; } - if (seen) - { - platform->SetPidParameters(heater, pp); - } - else + if (!seen) { - reply.printf("T:%.1f B:%.1f R:%.1f L:%.1f H:%.1f X:%d", r25, beta, pp.thermistorSeriesR, - pp.adcLowOffset, pp.adcHighOffset, platform->GetThermistorNumber(heater)); + reply.printf("T:%.1f B:%.1f C:%.2e R:%.1f L:%d H:%d X:%d", + th.GetR25(), th.GetBeta(), th.GetShc(), th.GetSeriesR(), + th.GetLowOffset(), th.GetHighOffset(), platform->GetThermistorNumber(heater)); } } else @@ -3573,13 +3580,29 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply) platform->SetFanValue(0, 0.0); //T3P3 as deprecated only applies to fan0 break; - case 109: // Deprecated + case 109: // Deprecated in RRF, but widely generated by slicers if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) + { return false; - - if (gb->Seen('S')) + } { - float temperature = gb->GetFValue(); + float temperature; + bool waitWhenCooling; + if (gb->Seen('R')) + { + waitWhenCooling = true; + temperature = gb->GetFValue(); + } + else if (gb->Seen('S')) + { + waitWhenCooling = false; + temperature = gb->GetFValue(); + } + else + { + break; // no target temperature given + } + Tool *tool; if (gb->Seen('T')) { @@ -3597,7 +3620,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply) } } SetToolHeaters(tool, temperature); - result = ToolHeatersAtSetTemperatures(tool); + result = ToolHeatersAtSetTemperatures(tool, waitWhenCooling); } break; @@ -3663,7 +3686,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply) // Wait for the heaters associated with the specified tool to be ready int toolNumber = gb->GetIValue(); toolNumber += gb->GetToolNumberAdjust(); - if (!ToolHeatersAtSetTemperatures(reprap.GetTool(toolNumber))) + if (!ToolHeatersAtSetTemperatures(reprap.GetTool(toolNumber), true)) { return false; } @@ -3678,7 +3701,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply) gb->GetLongArray(heaters, heaterCount); for(size_t i=0; iHeaterAtSetTemperature(heaters[i])) + if (!reprap.GetHeat()->HeaterAtSetTemperature(heaters[i], true)) { return false; } @@ -3692,7 +3715,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply) const int8_t chamberHeater = reprap.GetHeat()->GetChamberHeater(); if (chamberHeater != -1) { - if (!reprap.GetHeat()->HeaterAtSetTemperature(chamberHeater)) + if (!reprap.GetHeat()->HeaterAtSetTemperature(chamberHeater, true)) { return false; } @@ -3925,71 +3948,88 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply) break; case 190: // Set bed temperature and wait + case 191: // Set chamber temperature and wait if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) // tell Move not to wait for more moves { return false; } - if (gb->Seen('S')) { - const int8_t bedHeater = reprap.GetHeat()->GetBedHeater(); - if (bedHeater >= 0) + const int8_t heater = (code == 191) ? reprap.GetHeat()->GetChamberHeater() : reprap.GetHeat()->GetBedHeater(); + if (heater >= 0) { - reprap.GetHeat()->SetActiveTemperature(bedHeater, gb->GetFValue()); - reprap.GetHeat()->Activate(bedHeater); - result = reprap.GetHeat()->HeaterAtSetTemperature(bedHeater); + float temperature; + bool waitWhenCooling; + if (gb->Seen('R')) + { + waitWhenCooling = true; + temperature = gb->GetFValue(); + } + else if (gb->Seen('S')) + { + waitWhenCooling = false; + temperature = gb->GetFValue(); + } + else + { + break; // no target temperature given + } + + reprap.GetHeat()->SetActiveTemperature(heater, temperature); + reprap.GetHeat()->Activate(heater); + result = reprap.GetHeat()->HeaterAtSetTemperature(heater, waitWhenCooling); } } break; case 201: // Set/print axis accelerations - { - bool seen = false; - for (size_t axis = 0; axis < numAxes; axis++) { - if (gb->Seen(axisLetters[axis])) + bool seen = false; + for (size_t axis = 0; axis < numAxes; axis++) { - platform->SetAcceleration(axis, gb->GetFValue() * distanceScale); - seen = true; + if (gb->Seen(axisLetters[axis])) + { + platform->SetAcceleration(axis, gb->GetFValue() * distanceScale); + seen = true; + } } - } - if (gb->Seen(extrudeLetter)) - { - seen = true; - float eVals[MaxExtruders]; - size_t eCount = numExtruders; - gb->GetFloatArray(eVals, eCount, true); - for (size_t e = 0; e < eCount; e++) + if (gb->Seen(extrudeLetter)) { - platform->SetAcceleration(numAxes + e, eVals[e] * distanceScale); + seen = true; + float eVals[MaxExtruders]; + size_t eCount = numExtruders; + gb->GetFloatArray(eVals, eCount, true); + for (size_t e = 0; e < eCount; e++) + { + platform->SetAcceleration(numAxes + e, eVals[e] * distanceScale); + } } - } - if (gb->Seen('P')) - { - // Set max average printing acceleration - platform->SetMaxAverageAcceleration(gb->GetFValue() * distanceScale); - seen = true; - } - - if (!seen) - { - reply.printf("Accelerations: "); - for (size_t axis = 0; axis < numAxes; ++axis) + if (gb->Seen('P')) { - reply.catf("%c: %.1f, ", axisLetters[axis], platform->Acceleration(axis) / distanceScale); + // Set max average printing acceleration + platform->SetMaxAverageAcceleration(gb->GetFValue() * distanceScale); + seen = true; } - reply.cat("E:"); - char sep = ' '; - for (size_t extruder = 0; extruder < numExtruders; extruder++) + + if (!seen) { - reply.catf("%c%.1f", sep, platform->Acceleration(extruder + numAxes) / distanceScale); - sep = ':'; + reply.printf("Accelerations: "); + for (size_t axis = 0; axis < numAxes; ++axis) + { + reply.catf("%c: %.1f, ", axisLetters[axis], platform->Acceleration(axis) / distanceScale); + } + reply.cat("E:"); + char sep = ' '; + for (size_t extruder = 0; extruder < numExtruders; extruder++) + { + reply.catf("%c%.1f", sep, platform->Acceleration(extruder + numAxes) / distanceScale); + sep = ':'; + } + reply.catf(", avg. printing: %.1f", platform->GetMaxAverageAcceleration()); } - reply.catf(", avg. printing: %.1f", platform->GetMaxAverageAcceleration()); } - } break; case 203: // Set/print maximum feedrates @@ -5948,13 +5988,13 @@ void GCodes::CancelPrint() } // Return true if all the heaters for the specified tool are at their set temperatures -bool GCodes::ToolHeatersAtSetTemperatures(const Tool *tool) const +bool GCodes::ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling) const { if (tool != NULL) { for (size_t i = 0; i < tool->HeaterCount(); ++i) { - if (!reprap.GetHeat()->HeaterAtSetTemperature(tool->Heater(i))) + if (!reprap.GetHeat()->HeaterAtSetTemperature(tool->Heater(i), waitWhenCooling)) { return false; } diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index 18dd79a0..4397384e 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -212,7 +212,7 @@ private: void SetHeaterParameters(GCodeBuffer *gb, StringRef& reply); // Set the thermistor and ADC parameters for a heater void ManageTool(GCodeBuffer *gb, StringRef& reply); // Create a new tool definition void SetToolHeaters(Tool *tool, float temperature); // Set all a tool's heaters to the temperature. For M104... - bool ToolHeatersAtSetTemperatures(const Tool *tool) const; // Wait for the heaters associated with the specified tool to reach their set temperatures + bool ToolHeatersAtSetTemperatures(const Tool *tool, bool waitWhenCooling) const; // Wait for the heaters associated with the specified tool to reach their set temperatures void SetAllAxesNotHomed(); // Flag all axes as not homed void SetPositions(float positionNow[DRIVES]); // Set the current position to be this const char *TranslateEndStopResult(EndStopHit es); // Translate end stop result to text diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp index f654ec61..256ad1a2 100644 --- a/src/Heating/Heat.cpp +++ b/src/Heating/Heat.cpp @@ -22,7 +22,7 @@ Licence: GPL #include "Pid.h" Heat::Heat(Platform* p) - : platform(p), active(false), coldExtrude(false), bedHeater(BED_HEATER), chamberHeater(-1), heaterBeingTuned(-1), lastHeaterTuned(-1) + : platform(p), active(false), coldExtrude(false), bedHeater(DefaultBedHeater), chamberHeater(-1), heaterBeingTuned(-1), lastHeaterTuned(-1) { for (size_t heater = 0; heater < HEATERS; heater++) { @@ -105,12 +105,9 @@ void Heat::Diagnostics(MessageType mtype) bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const { - size_t firstHeater = (bedHeater == -1) ? E0_HEATER : - (includingBed) ? min(bedHeater, E0_HEATER) : E0_HEATER; - - for(size_t heater = firstHeater; heater < HEATERS; heater++) + for(int8_t heater = 0; heater < HEATERS; heater++) { - if (!HeaterAtSetTemperature(heater)) + if (!HeaterAtSetTemperature(heater, true) && (includingBed || heater != bedHeater)) { return false; } @@ -119,7 +116,7 @@ bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const } //query an individual heater -bool Heat::HeaterAtSetTemperature(int8_t heater) const +bool Heat::HeaterAtSetTemperature(int8_t heater, bool waitWhenCooling) const { // If it hasn't anything to do, it must be right wherever it is... if (heater < 0 || heater >= HEATERS || pids[heater]->SwitchedOff() || pids[heater]->FaultOccurred()) @@ -129,7 +126,9 @@ bool Heat::HeaterAtSetTemperature(int8_t heater) const const float dt = GetTemperature(heater); const float target = (pids[heater]->Active()) ? GetActiveTemperature(heater) : GetStandbyTemperature(heater); - return (target < TEMPERATURE_LOW_SO_DONT_CARE) || (fabsf(dt - target) <= TEMPERATURE_CLOSE_ENOUGH); + return (target < TEMPERATURE_LOW_SO_DONT_CARE) + || (fabsf(dt - target) <= TEMPERATURE_CLOSE_ENOUGH) + || (target < dt && !waitWhenCooling); } Heat::HeaterStatus Heat::GetStatus(int8_t heater) const diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h index e546ce40..1ec813a7 100644 --- a/src/Heating/Heat.h +++ b/src/Heating/Heat.h @@ -66,7 +66,7 @@ public: void SwitchOffAll(); // Turn all heaters off void ResetFault(int8_t heater); // Reset a heater fault - only call this if you know what you are doing bool AllHeatersAtSetTemperatures(bool includingBed) const; // Is everything at temperature within tolerance? - bool HeaterAtSetTemperature(int8_t heater) const; // Is a specific heater at temperature within tolerance? + bool HeaterAtSetTemperature(int8_t heater, bool waitWhenCooling) const; // Is a specific heater at temperature within tolerance? void Diagnostics(MessageType mtype); // Output useful information float GetAveragePWM(size_t heater) const // Return the running average PWM to the heater as a fraction in [0, 1]. diff --git a/src/Heating/Thermistor.cpp b/src/Heating/Thermistor.cpp new file mode 100644 index 00000000..991dc441 --- /dev/null +++ b/src/Heating/Thermistor.cpp @@ -0,0 +1,72 @@ +/* + * Thermistor.cpp + * + * Created on: 10 Nov 2016 + * Author: David + */ + +#include "Thermistor.h" +#include "Pins.h" +#include "Configuration.h" + +// The Steinhart-Hart equation for thermistor resistance is: +// 1/T = A + B ln(R) + C [ln(R)]^3 +// +// The simplified (beta) equation assumes C=0 and is: +// 1/T = A + (1/Beta) ln(R) +// +// The parameters that can be configured in RRF are R25 (the resistance at 25C), Beta, and optionally C. + +// Create an instance with default values +Thermistor::Thermistor() : adcLowOffset(0), adcHighOffset(0) +{ + SetParameters(EXT_R25, EXT_BETA, EXT_SHC, THERMISTOR_SERIES_RS); +} + +// Initialise the instance +void Thermistor::SetParameters(float p_r25, float p_beta, float p_shC, float p_seriesR) +{ + r25 = p_r25; + beta = p_beta; + shC = p_shC; + seriesR = p_seriesR; + CalcDerivedParameters(); +} + +// Calculate temperature from an ADC reading in the range 0..1 +float Thermistor::CalcTemperature(int32_t adcReading) const +{ + const float denom = (float)(AdcRange + (int)adcHighOffset - adcReading) - 0.5; + if (denom <= 0.0) + { + return ABS_ZERO; + } + const float resistance = seriesR * ((float)(adcReading - (int)adcLowOffset) + 0.5)/denom; + const float logResistance = log(resistance); + const float recipT = shA + shB * logResistance + shC * logResistance * logResistance * logResistance; + return (recipT > 0.0) ? (1.0/recipT) + ABS_ZERO : BAD_ERROR_TEMPERATURE; +} + +// Calculate expected ADC reading at a particular temperature, rounded down as the ADC does +int32_t Thermistor::CalcAdcReading(float temperature) const +{ + const double bDFiv3c = shB/(3.0 * shC); + const double halfY = (shA - 1.0/(temperature - ABS_ZERO))/(2.0 * shC); + const double x = sqrt((bDFiv3c * bDFiv3c * bDFiv3c) + (halfY * halfY)); + const double oneThird = 1.0/3.0; + const float resistance = exp(pow(x - halfY, oneThird) - pow(x + halfY, oneThird)); + const float fraction = resistance/(resistance + seriesR); + const int32_t actualAdcRange = AdcRange + (int)adcHighOffset - (int)adcLowOffset; + const int32_t val = (int32_t)(fraction * (float)actualAdcRange) + (int)adcLowOffset; + return constrain(val, 0, AdcRange - 1); +} + +// Calculate shA and shB from the other parameters +void Thermistor::CalcDerivedParameters() +{ + shB = 1.0/beta; + const double lnR25 = log(r25); + shA = 1.0/(25.0 - ABS_ZERO) - shB * lnR25 - shC * lnR25 * lnR25 * lnR25; +} + +// End diff --git a/src/Heating/Thermistor.h b/src/Heating/Thermistor.h new file mode 100644 index 00000000..58c504c2 --- /dev/null +++ b/src/Heating/Thermistor.h @@ -0,0 +1,56 @@ +/* + * Thermistor.h + * + * Created on: 10 Nov 2016 + * Author: David + */ + +#ifndef SRC_HEATING_THERMISTOR_H_ +#define SRC_HEATING_THERMISTOR_H_ + +#include "Core.h" + +// The Steinhart-Hart equation for thermistor resistance is: +// 1/T = A + B ln(R) + C [ln(R)]^3 +// +// The simplified (beta) equation assumes C=0 and is: +// 1/T = A + (1/Beta) ln(R) +// +// The parameters that can be configured in RRF are R25 (the resistance at 25C), Beta, and optionally C. + +class Thermistor +{ +public: + Thermistor(); // create an instance with default values + float CalcTemperature(int32_t adcReading) const; // calculate temperature from an ADC reading in the range 0..1 + int32_t CalcAdcReading(float temperature) const; // calculate expected ADC reading at a particular temperature + + float GetR25() const { return r25; } + float GetBeta() const { return beta; } + float GetShc() const { return shC; } + float GetSeriesR() const { return seriesR; } + int8_t GetLowOffset() const { return adcLowOffset; } + int8_t GetHighOffset() const { return adcHighOffset; } + + void SetParameters(float p_r25, float p_beta, float p_shC, float p_seriesR); // initialise the main parameters + void SetLowOffset(int8_t p_offset) { adcLowOffset = p_offset; } + void SetHighOffset(int8_t p_offset) { adcHighOffset = p_offset; } + + // For the theory behind ADC oversampling, see http://www.atmel.com/Images/doc8003.pdf + static const unsigned int AdcOversampleBits = 1 ; // we use 1-bit oversampling + +private: + void CalcDerivedParameters(); // calculate shA and shB + + // The following are configurable parameters + float r25, beta, shC, seriesR; // parameters declared in the M305 command + int8_t adcLowOffset, adcHighOffset; // ADC low and high end offsets + + // The following are derived from the configurable parameters + float shA, shB; // derived parameters + + static const unsigned int AdcBits = 12; // the ADCs in the SAM processors are 12-bit + static const int32_t AdcRange = 1 << (AdcBits + AdcOversampleBits); // The readings we pass in should be in range 0..(AdcRange - 1) +}; + +#endif /* SRC_HEATING_THERMISTOR_H_ */ diff --git a/src/Platform.cpp b/src/Platform.cpp index 20409663..b04ac586 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -120,23 +120,9 @@ bool PidParameters::UsePID() const return kP >= 0; } -float PidParameters::GetThermistorR25() const -{ - return thermistorInfR * exp(thermistorBeta / (25.0 - ABS_ZERO)); -} - -void PidParameters::SetThermistorR25AndBeta(float r25, float beta) -{ - thermistorInfR = r25 * exp(-beta / (25.0 - ABS_ZERO)); - thermistorBeta = beta; -} - bool PidParameters::operator==(const PidParameters& other) const { - return kI == other.kI && kD == other.kD && kP == other.kP && kT == other.kT && kS == other.kS - && thermistorBeta == other.thermistorBeta && thermistorInfR == other.thermistorInfR - && thermistorSeriesR == other.thermistorSeriesR && adcLowOffset == other.adcLowOffset - && adcHighOffset == other.adcHighOffset; + return kI == other.kI && kD == other.kD && kP == other.kP && kT == other.kT && kS == other.kS; } //************************************************************************************************* @@ -232,15 +218,14 @@ void Platform::Init() // DRIVES - ARRAY_INIT(directions, DIRECTIONS); - ARRAY_INIT(enableValues, ENABLE_VALUES); ARRAY_INIT(endStopPins, END_STOP_PINS); ARRAY_INIT(maxFeedrates, MAX_FEEDRATES); ARRAY_INIT(accelerations, ACCELERATIONS); ARRAY_INIT(driveStepsPerUnit, DRIVE_STEPS_PER_UNIT); ARRAY_INIT(instantDvs, INSTANT_DVS); -#if !defined(DUET_NG) +#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; maxStepperDigipotVoltage = MAX_STEPPER_DIGIPOT_VOLTAGE; @@ -263,21 +248,7 @@ void Platform::Init() idleCurrentFactor = DEFAULT_IDLE_CURRENT_FACTOR; - // HEATERS - Bed is assumed to be the first - - ARRAY_INIT(tempSensePins, TEMP_SENSE_PINS); - ARRAY_INIT(heatOnPins, HEAT_ON_PINS); - ARRAY_INIT(spiTempSenseCsPins, SpiTempSensorCsPins); - - configuredHeaters = (BED_HEATER >= 0) ? (1 << BED_HEATER) : 0; - heatSampleTicks = HEAT_SAMPLE_TIME * SecondsToMillis; - - // Enable pullups on all the SPI CS pins. This is required if we are using more than one device on the SPI bus. - // Otherwise, when we try to initialise the first device, the other devices may respond as well because their CS lines are not high. - for (size_t i = 0; i < MaxSpiTempSensors; ++i) - { - setPullup(SpiTempSensorCsPins[i], true); - } + // SD card interfaces for (size_t i = 0; i < NumSdCards; ++i) { const Pin p = SdCardDetectPins[i]; @@ -296,6 +267,9 @@ void Platform::Init() for (size_t drive = 0; drive < DRIVES; drive++) { + enableValues[drive] = false; // assume active low enable signal + directions[drive] = true; // drive moves forwards by default + // Map axes and extruders straight through if (drive < MAX_AXES) { @@ -362,7 +336,20 @@ void Platform::Init() extrusionAncilliaryPWM = 0.0; - // HEATERS - Bed is assumed to be index 0 + ARRAY_INIT(tempSensePins, TEMP_SENSE_PINS); + ARRAY_INIT(heatOnPins, HEAT_ON_PINS); + ARRAY_INIT(spiTempSenseCsPins, SpiTempSensorCsPins); + + configuredHeaters = (DefaultBedHeater >= 0) ? (1 << DefaultBedHeater) : 0; + heatSampleTicks = HEAT_SAMPLE_TIME * SecondsToMillis; + + // Enable pullups on all the SPI CS pins. This is required if we are using more than one device on the SPI bus. + // Otherwise, when we try to initialise the first device, the other devices may respond as well because their CS lines are not high. + for (size_t i = 0; i < MaxSpiTempSensors; ++i) + { + setPullup(SpiTempSensorCsPins[i], true); + } + for (size_t heater = 0; heater < HEATERS; heater++) { if (heatOnPins[heater] != NoPin) @@ -374,11 +361,17 @@ void Platform::Init() thermistorAdcChannels[heater] = chan; AnalogInEnableChannel(chan, true); - SetThermistorNumber(heater, heater); // map the thermistor straight through + SetThermistorNumber(heater, heater); // map the thermistor straight through + thermistors[heater].SetParameters( + (heater == DefaultBedHeater) ? BED_R25 : EXT_R25, + (heater == DefaultBedHeater) ? BED_BETA : EXT_BETA, + (heater == DefaultBedHeater) ? BED_SHC : EXT_SHC, + THERMISTOR_SERIES_RS); // initialise the thermistor parameters thermistorFilters[heater].Init(0); } SetTemperatureLimit(DEFAULT_TEMPERATURE_LIMIT); + // Fans InitFans(); // Hotend configuration @@ -460,15 +453,6 @@ bool Platform::AnyFileOpen(const FATFS *fs) const void Platform::SetTemperatureLimit(float t) { temperatureLimit = t; - for (size_t heater = 0; heater < HEATERS; heater++) - { - // Calculate and store the ADC average sum that corresponds to an overheat condition, so that we can check it quickly in the tick ISR - float thermistorOverheatResistance = nvData.pidParams[heater].GetRInf() - * exp(-nvData.pidParams[heater].GetBeta() / (temperatureLimit - ABS_ZERO)); - float thermistorOverheatAdcValue = (AD_RANGE_REAL + 1) * thermistorOverheatResistance - / (thermistorOverheatResistance + nvData.pidParams[heater].thermistorSeriesR); - thermistorOverheatSums[heater] = (uint32_t) (thermistorOverheatAdcValue + 0.9) * THERMISTOR_AVERAGE_READINGS; - } } // Specify which thermistor channel a particular heater uses @@ -734,14 +718,11 @@ void Platform::ResetNvData() for (size_t i = 0; i < HEATERS; ++i) { PidParameters& pp = nvData.pidParams[i]; - pp.thermistorSeriesR = defaultThermistorSeriesRs[i]; - pp.SetThermistorR25AndBeta(defaultThermistor25RS[i], defaultThermistorBetas[i]); pp.kI = defaultPidKis[i]; pp.kD = defaultPidKds[i]; pp.kP = defaultPidKps[i]; pp.kT = defaultPidKts[i]; pp.kS = defaultPidKss[i]; - pp.adcLowOffset = pp.adcHighOffset = 0.0; } #if FLASH_SAVE_ENABLED @@ -1579,40 +1560,18 @@ float Platform::GetTemperature(size_t heater, TemperatureError& err) const volatile ThermistorAveragingFilter& filter = thermistorFilters[heater]; if (filter.IsValid()) { - int rawTemp = filter.GetSum()/(THERMISTOR_AVERAGE_READINGS >> AD_OVERSAMPLE_BITS); - - // If the ADC reading is N then for an ideal ADC, the input voltage is at least N/(AD_RANGE + 1) and less than (N + 1)/(AD_RANGE + 1), times the analog reference. - // So we add 0.5 to to the reading to get a better estimate of the input. - float reading = (float) rawTemp + 0.5; + const int32_t averagedReading = filter.GetSum()/(ThermistorAverageReadings >> Thermistor::AdcOversampleBits); + const float temp = thermistors[heater].CalcTemperature(averagedReading); - // Recognise the special case of thermistor disconnected. - // For some ADCs, the high-end offset is negative, meaning that the ADC never returns a high enough value. We need to allow for this here. - const PidParameters& p = nvData.pidParams[heater]; - if (p.adcHighOffset < 0.0) - { - rawTemp -= (int) p.adcHighOffset; - } - if (rawTemp >= (int)AD_DISCONNECTED_VIRTUAL) + if (temp < MinimumConnectedTemperature) { // thermistor is disconnected err = TemperatureError::openCircuit; return ABS_ZERO; } - // Correct for the low and high ADC offsets - reading -= p.adcLowOffset; - reading *= (AD_RANGE_VIRTUAL + 1) / (AD_RANGE_VIRTUAL + 1 + p.adcHighOffset - p.adcLowOffset); - - float resistance = reading * p.thermistorSeriesR / ((AD_RANGE_VIRTUAL + 1) - reading); - if (resistance > p.GetRInf()) - { - err = TemperatureError::success; - return ABS_ZERO + p.GetBeta() / log(resistance / p.GetRInf()); - } - - // Thermistor short circuit, return a high temperature - err = TemperatureError::shortCircuit; - return BAD_ERROR_TEMPERATURE; + err = TemperatureError::success; + return temp; } // Filter is not ready yet @@ -2196,7 +2155,7 @@ void Platform::InitFans() { #ifdef DUET_NG // Set fan 1 to be thermostatic by default, monitoring all heaters except the default bed heater - fans[1].SetHeatersMonitored(0xFFFF & ~(1 << BED_HEATER)); + fans[1].SetHeatersMonitored(0xFFFF & ~(1 << DefaultBedHeater)); fans[1].SetValue(1.0); // set it full on #else // Fan 1 on the Duet 0.8.5 shares its control pin with heater 6. Set it full on to make sure the heater (if present) is off. @@ -2940,41 +2899,25 @@ void Platform::Tick() { case 1: // last conversion started was a thermistor case 3: + if (IsThermistorChannel(currentHeater)) { - if (IsThermistorChannel(currentHeater)) - { - ThermistorAveragingFilter& currentFilter = const_cast(thermistorFilters[currentHeater]); - currentFilter.ProcessReading(AnalogInReadChannel(thermistorAdcChannels[heaterTempChannels[currentHeater]])); - if (currentFilter.IsValid() && (configuredHeaters & (1 << currentHeater)) != 0) - { - uint32_t sum = currentFilter.GetSum(); - if (sum < thermistorOverheatSums[currentHeater] || sum >= AD_DISCONNECTED_REAL * THERMISTOR_AVERAGE_READINGS) - { - // We have an over-temperature or disconnected reading from this thermistor, so turn off the heater - SetHeater(currentHeater, 0.0); - LogError(ErrorCode::BadTemp); - } - } - } - else - { - // Thermocouple case: oversampling is not necessary as the MAX31855 is itself continuously sampling and - // averaging. As such, the temperature reading is taken directly by Platform::GetTemperature() and - // periodically called by PID::Spin() where temperature fault handling is taken care of. However, we - // must guard against overly long delays between successive calls of PID::Spin(). - // Do not call Time() here, it isn't safe. We use millis() instead. - if ((millis() - reprap.GetHeat()->GetLastSampleTime(currentHeater)) > maxPidSpinDelay) - { - SetHeater(currentHeater, 0.0); - LogError(ErrorCode::BadTemp); - } - } + // Because we are in the tick ISR and no other ISR reads the averaging filter, we can cast away 'volatile' here + ThermistorAveragingFilter& currentFilter = const_cast(thermistorFilters[currentHeater]); + currentFilter.ProcessReading(AnalogInReadChannel(thermistorAdcChannels[heaterTempChannels[currentHeater]])); + } - ++currentHeater; - if (currentHeater == HEATERS) - { - currentHeater = 0; - } + // Guard against overly long delays between successive calls of PID::Spin(). + // Do not call Time() here, it isn't safe. We use millis() instead. + if ((configuredHeaters & (1 << currentHeater)) != 0 && (millis() - reprap.GetHeat()->GetLastSampleTime(currentHeater)) > maxPidSpinDelay) + { + SetHeater(currentHeater, 0.0); + LogError(ErrorCode::BadTemp); + } + + ++currentHeater; + if (currentHeater == HEATERS) + { + currentHeater = 0; } ++tickState; break; diff --git a/src/Platform.h b/src/Platform.h index 94ea54bc..7d20dc00 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -47,6 +47,7 @@ Licence: GPL #include "Core.h" #include "Heating/TemperatureSensor.h" +#include "Heating/Thermistor.h" #include "Heating/TemperatureError.h" #include "OutputMemory.h" #include "Libraries/Fatfs/ff.h" @@ -124,32 +125,10 @@ 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 -const float defaultThermistorBetas[HEATERS] = HEATERS_(BED_BETA, EXT_BETA, EXT_BETA, EXT_BETA, EXT_BETA, EXT_BETA, EXT_BETA, EXT_BETA); // Bed thermistor: B57861S104F40; Extruder thermistor: RS 198-961 -const float defaultThermistorSeriesRs[HEATERS] = HEATERS_(THERMISTOR_SERIES_RS, THERMISTOR_SERIES_RS, THERMISTOR_SERIES_RS, THERMISTOR_SERIES_RS, - THERMISTOR_SERIES_RS, THERMISTOR_SERIES_RS, THERMISTOR_SERIES_RS, THERMISTOR_SERIES_RS); -const float defaultThermistor25RS[HEATERS] = HEATERS_(BED_R25, EXT_R25, EXT_R25, EXT_R25, EXT_R25, EXT_R25, EXT_R25, EXT_R25); // Thermistor ohms at 25 C = 298.15 K - -// Note on hot end PID parameters: -// The system is highly nonlinear because the heater power is limited to a maximum value and cannot go negative. -// If we try to run a traditional PID when there are large temperature errors, this causes the I-accumulator to go out of control, -// which causes a large amount of overshoot at lower temperatures. There are at least two ways of avoiding this: -// -// 1. Allow the PID to operate even with very large errors, but choose a very small I-term, just the right amount so that when heating up -// from cold, the I-accumulator is approximately the value needed to maintain the correct power when the target temperature is reached. -// This works well most of the time. However if the Duet board is reset when the extruder is hot and is then -// commanded to heat up again before the extruder has cooled, the I-accumulator doesn't grow large enough, so the -// temperature undershoots. The small value of the I-term then causes it to take a long time to reach the correct temperature. -// -// 2. Only allow the PID to operate when the temperature error is small enough for the PID to operate in the linear region. -// So we set FULL_PID_BAND to a small value. It needs to be at least 15C because that is how much the temperature overshoots by -// on an Ormerod when we turn the heater off from full power at about 180C. When we transition to PID, we set the I-term to the -// value we expect to be needed to maintain the target temperature. We use an additional T parameter to allow this value to be -// estimated. -// -// The default values use method (2). -// + // 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. @@ -160,28 +139,20 @@ const float defaultPidKps[HEATERS] = HEATERS_(-1.0, 10.0, 10.0, 10.0, 10.0, 10.0 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 -// For the theory behind ADC oversampling, see http://www.atmel.com/Images/doc8003.pdf -const unsigned int AD_OVERSAMPLE_BITS = 1; // Number of bits we oversample when reading temperatures - // 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 THERMISTOR_AVERAGE_READINGS = 32; -const unsigned int AD_RANGE_REAL = 4095; // The ADC that measures temperatures gives an int this big as its max value -const unsigned int AD_RANGE_VIRTUAL = ((AD_RANGE_REAL + 1) << AD_OVERSAMPLE_BITS) - 1; // The max value we can get using oversampling -const unsigned int AD_DISCONNECTED_REAL = AD_RANGE_REAL - 3; // We consider an ADC reading at/above this value to indicate that the thermistor is disconnected -const unsigned int AD_DISCONNECTED_VIRTUAL = AD_DISCONNECTED_REAL << AD_OVERSAMPLE_BITS; +const unsigned int ThermistorAverageReadings = 32; const uint32_t maxPidSpinDelay = 5000; // Maximum elapsed time in milliseconds between successive temp samples by Pid::Spin() permitted for a temp sensor -const size_t BED_HEATER = 0; // Index of the heated bed -const size_t E0_HEATER = 1; // Index of the first extruder heater +const size_t DefaultBedHeater = 0; // Index of the default bed heater +const size_t DefaultE0Heater = 1; // Index of the default first extruder heater /****************************************************************************************************/ // File handling const size_t MAX_FILES = 10; // Must be large enough to handle the max number of simultaneous web requests + files being printed - const size_t FILE_BUFFER_SIZE = 256; /****************************************************************************************************/ @@ -312,21 +283,10 @@ struct ZProbeParameters class PidParameters { // If you add any more variables to this class, don't forget to change the definition of operator== in Platform.cpp! -private: - float thermistorBeta, thermistorInfR; // private because these must be changed together - public: float kI, kD, kP, kT, kS; - float thermistorSeriesR; - float adcLowOffset, adcHighOffset; - - float GetBeta() const { return thermistorBeta; } - float GetRInf() const { return thermistorInfR; } bool UsePID() const; - float GetThermistorR25() const; - void SetThermistorR25AndBeta(float r25, float beta); - bool operator==(const PidParameters& other) const; bool operator!=(const PidParameters& other) const { @@ -393,7 +353,7 @@ private: //invariant(index < numAveraged) }; -typedef AveragingFilter ThermistorAveragingFilter; +typedef AveragingFilter ThermistorAveragingFilter; typedef AveragingFilter ZProbeAveragingFilter; // Enumeration of error condition bits @@ -613,6 +573,11 @@ public: float GetHeatSampleTime() const; void SetPidParameters(size_t heater, const PidParameters& params); const PidParameters& GetPidParameters(size_t heater) const; + Thermistor& GetThermistor(size_t heater) + pre (heater < HEATERS) + { + return thermistors[heater]; + } void SetThermistorNumber(size_t heater, size_t thermistor); int GetThermistorNumber(size_t heater) const; bool IsThermistorChannel(uint8_t heater) const; @@ -713,7 +678,7 @@ private: 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 = 4; // increment this whenever this struct changes + static const uint16_t versionValue = 5; // increment this whenever this struct changes static const uint32_t nvAddress = (SoftwareResetData::nvAddress + sizeof(SoftwareResetData) + 3) & (~3); uint16_t magic; @@ -816,6 +781,7 @@ private: Pin tempSensePins[HEATERS]; Pin heatOnPins[HEATERS]; + Thermistor thermistors[HEATERS]; TemperatureSensor SpiTempSensors[MaxSpiTempSensors]; Pin spiTempSenseCsPins[MaxSpiTempSensors]; uint32_t configuredHeaters; // bitmask of all heaters in use @@ -889,7 +855,6 @@ private: unsigned int heaterTempChannels[HEATERS]; AnalogChannelNumber thermistorAdcChannels[HEATERS]; AnalogChannelNumber zProbeAdcChannel; - uint32_t thermistorOverheatSums[HEATERS]; uint8_t tickState; size_t currentHeater; int debugCode; diff --git a/src/PrintMonitor.cpp b/src/PrintMonitor.cpp index 67dd85a7..6901e555 100644 --- a/src/PrintMonitor.cpp +++ b/src/PrintMonitor.cpp @@ -88,7 +88,7 @@ void PrintMonitor::Spin() // Check if this heater is assigned to a tool and if it has reached its set temperature yet if (reprap.IsHeaterAssignedToTool(heater)) { - if (!reprap.GetHeat()->HeaterAtSetTemperature(heater)) + if (!reprap.GetHeat()->HeaterAtSetTemperature(heater, false)) { nozzleAtHighTemperature = false; break; diff --git a/src/Reprap.cpp b/src/Reprap.cpp index 6b39b3ff..1a60b022 100644 --- a/src/Reprap.cpp +++ b/src/Reprap.cpp @@ -680,7 +680,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) // Current temperatures ch = '['; - for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++) + for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++) { response->catf("%c%.1f", ch, heat->GetTemperature(heater)); ch = ','; @@ -690,7 +690,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) // Active temperatures response->catf(",\"active\":"); ch = '['; - for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++) + for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++) { response->catf("%c%.1f", ch, heat->GetActiveTemperature(heater)); ch = ','; @@ -700,7 +700,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) // Standby temperatures response->catf(",\"standby\":"); ch = '['; - for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++) + for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++) { response->catf("%c%.1f", ch, heat->GetStandbyTemperature(heater)); ch = ','; @@ -710,7 +710,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) // Heater statuses (0=off, 1=standby, 2=active, 3=fault) response->cat(",\"state\":"); ch = '['; - for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++) + for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++) { response->catf("%c%d", ch, static_cast(heat->GetStatus(heater))); ch = ','; @@ -748,6 +748,9 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) // Delta configuration and number of axes response->catf(",\"geometry\":\"%s\",\"axes\":%u", move->GetGeometryString(), numAxes); + // Firmware name, for PanelDue + response->catf(",\"firmwareName\":\"%s\"", NAME); + // Total and mounted volumes size_t mountedCards = 0; for(size_t i = 0; i < NumSdCards; i++) @@ -1041,7 +1044,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) { ch = '['; } - for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++) + for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++) { response->catf("%c%.1f", ch, heat->GetTemperature(heater)); ch = ','; @@ -1059,7 +1062,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) { ch = '['; } - for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++) + for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++) { response->catf("%c%.1f", ch, heat->GetActiveTemperature(heater)); ch = ','; @@ -1077,7 +1080,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) { ch = '['; } - for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++) + for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++) { response->catf("%c%.1f", ch, heat->GetStandbyTemperature(heater)); ch = ','; @@ -1095,7 +1098,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq) { ch = '['; } - for (size_t heater = E0_HEATER; heater < GetToolHeatersInUse(); heater++) + for (size_t heater = DefaultE0Heater; heater < GetToolHeatersInUse(); heater++) { response->catf("%c%d", ch, static_cast(heat->GetStatus(heater))); ch = ','; -- cgit v1.2.3