Minor updates

Added chrishamm's FTP fix
Fix spurious extra command word in http parser
Minor improvements to Heat and PrintMonitor
This commit is contained in:
David Crocker 2015-12-31 12:28:35 +00:00
parent aa7bac3cc0
commit 96e8d2010b
7 changed files with 212 additions and 202 deletions

View file

@ -30,7 +30,7 @@ Licence: GPL
#endif
#ifndef DATE
#define DATE "2015-12-30"
#define DATE "2015-12-31"
#endif
#define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman"

View file

@ -1,7 +0,0 @@
duet
thermistor
debounce
struct
arduino
extruder
deprecated

366
Heat.cpp
View file

@ -22,35 +22,34 @@ Licence: GPL
const float invHeatPwmAverageCount = HEAT_SAMPLE_TIME/HEAT_PWM_AVERAGE_TIME;
Heat::Heat(Platform* p) : platform(p), active(false), coldExtrude(false),
bedHeater(BED_HEATER), chamberHeater(-1)
Heat::Heat(Platform* p) : platform(p), active(false), coldExtrude(false), bedHeater(BED_HEATER), chamberHeater(-1)
{
for (size_t heater = 0; heater < HEATERS; heater++)
{
pids[heater] = new PID(platform, heater);
}
for (size_t heater = 0; heater < HEATERS; heater++)
{
pids[heater] = new PID(platform, heater);
}
}
void Heat::Init()
{
for (size_t heater = 0; heater < HEATERS; heater++)
{
pids[heater]->Init();
}
lastTime = platform->Time();
longWait = lastTime;
coldExtrude = false;
active = true;
for (size_t heater = 0; heater < HEATERS; heater++)
{
pids[heater]->Init();
}
lastTime = platform->Time();
longWait = lastTime;
coldExtrude = false;
active = true;
}
void Heat::Exit()
{
for (size_t heater = 0; heater < HEATERS; heater++)
{
pids[heater]->SwitchOff();
}
platform->Message(HOST_MESSAGE, "Heat class exited.\n");
active = false;
for (size_t heater = 0; heater < HEATERS; heater++)
{
pids[heater]->SwitchOff();
}
platform->Message(HOST_MESSAGE, "Heat class exited.\n");
active = false;
}
void Heat::Spin()
@ -72,14 +71,14 @@ void Heat::Spin()
void Heat::Diagnostics()
{
platform->Message(GENERIC_MESSAGE, "Heat Diagnostics:\n");
for (size_t heater=0; heater < HEATERS; heater++)
{
if (pids[heater]->active)
{
platform->MessageF(GENERIC_MESSAGE, "Heater %d: I-accumulator = %.1f\n", heater, pids[heater]->temp_iState);
}
}
platform->Message(GENERIC_MESSAGE, "Heat Diagnostics:\n");
for (size_t heater=0; heater < HEATERS; heater++)
{
if (pids[heater]->active)
{
platform->MessageF(GENERIC_MESSAGE, "Heater %d: I-accumulator = %.1f\n", heater, pids[heater]->temp_iState);
}
}
}
bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const
@ -87,7 +86,9 @@ bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const
for (int8_t heater = (includingBed) ? 0 : 1; heater < HEATERS; heater++)
{
if (!HeaterAtSetTemperature(heater))
{
return false;
}
}
return true;
}
@ -96,7 +97,9 @@ bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const
bool Heat::HeaterAtSetTemperature(int8_t heater) const
{
if (pids[heater]->SwitchedOff()) // If it hasn't anything to do, it must be right wherever it is...
{
return true;
}
float dt = GetTemperature(heater);
float target = (pids[heater]->Active()) ? GetActiveTemperature(heater) : GetStandbyTemperature(heater);
@ -111,33 +114,32 @@ PID::PID(Platform* p, int8_t h) : platform(p), heater(h)
void PID::Init()
{
SetHeater(0.0);
temperature = platform->GetTemperature(heater);
activeTemperature = ABS_ZERO;
standbyTemperature = ABS_ZERO;
lastTemperature = temperature;
temp_iState = 0.0;
badTemperatureCount = 0;
temperatureFault = false;
active = false; // Default to standby temperature
switchedOff = true;
heatingUp = false;
averagePWM = 0.0;
SetHeater(0.0);
temperature = platform->GetTemperature(heater);
activeTemperature = ABS_ZERO;
standbyTemperature = ABS_ZERO;
lastTemperature = temperature;
temp_iState = 0.0;
badTemperatureCount = 0;
temperatureFault = false;
active = false; // Default to standby temperature
switchedOff = true;
heatingUp = false;
averagePWM = 0.0;
// Time the sensor was last sampled. During startup, we use the current
// time as the initial value so as to not trigger an immediate warning from
// the Tick ISR.
lastSampleTime = platform->Time();
// Time the sensor was last sampled. During startup, we use the current
// time as the initial value so as to not trigger an immediate warning from
// the Tick ISR.
lastSampleTime = platform->Time();
}
void PID::SwitchOn()
{
// if(reprap.Debug())
// {
// snprintf(scratchString, STRING_LENGTH, "Heater %d switched on.\n", heater);
// platform->Message(BOTH_MESSAGE, scratchString);
// }
switchedOff = false;
if (reprap.Debug(Module::moduleHeat))
{
platform->MessageF(GENERIC_MESSAGE, "Heater %d switched on.\n", heater);
}
switchedOff = temperatureFault;
}
void PID::SetHeater(float power) const
@ -147,153 +149,154 @@ void PID::SetHeater(float power) const
void PID::Spin()
{
// For temperature sensors which do not require frequent sampling and averaging,
// their temperature is read here and error/safety handling performed. However,
// unlike the Tick ISR, this code is not executed at interrupt level and consequently
// runs the risk of having undesirable delays between calls. To guard against this,
// we record for each PID object when it was last sampled and have the Tick ISR
// take action if there is a significant delay since the time of last sampling.
lastSampleTime = platform->Time();
// For temperature sensors which do not require frequent sampling and averaging,
// their temperature is read here and error/safety handling performed. However,
// unlike the Tick ISR, this code is not executed at interrupt level and consequently
// runs the risk of having undesirable delays between calls. To guard against this,
// we record for each PID object when it was last sampled and have the Tick ISR
// take action if there is a significant delay since the time of last sampling.
lastSampleTime = platform->Time();
// Always know our temperature, regardless of whether we have been switched on or not
// Always know our temperature, regardless of whether we have been switched on or not
Platform::TempError err = Platform::TempError::errOpen; // Initially assign an error; call should update but if it doesn't, we'll treat as an error
temperature = platform->GetTemperature(heater, &err); // In the event of an error, err is set and BAD_ERROR_TEMPERATURE is returned
Platform::TempError err = Platform::TempError::errOpen; // Initially assign an error; call should update but if it doesn't, we'll treat as an error
temperature = platform->GetTemperature(heater, &err); // In the event of an error, err is set and BAD_ERROR_TEMPERATURE is returned
// If we're not switched on, or there's a fault, turn the power off and go home.
// If we're not switched on, then nothing is using us. This probably means that
// we don't even have a thermistor connected. So don't even check for faults if we
// are not switched on. This is safe, as the next bit of code always turns our
// heater off in that case anyway.
if (temperatureFault || switchedOff)
{
SetHeater(0.0); // Make sure...
averagePWM *= (1.0 - invHeatPwmAverageCount);
return;
}
// If we're not switched on, or there's a fault, turn the power off and go home.
// If we're not switched on, then nothing is using us. This probably means that
// we don't even have a thermistor connected. So don't even check for faults if we
// are not switched on. This is safe, as the next bit of code always turns our
// heater off in that case anyway.
if (temperatureFault || switchedOff)
{
SetHeater(0.0); // Make sure...
averagePWM *= (1.0 - invHeatPwmAverageCount);
return;
}
// 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)
{
if (platform->DoThermistorAdc(heater) || !(Platform::TempErrorPermanent(err)))
{
// Error may be a temporary error and may correct itself after a few additional reads
badTemperatureCount++;
}
else
{
// Error condition is not expected to correct itself (e.g., digital device such
// as a MAX31855 reporting that the temp sensor is shorted -- even a temporary
// short in such a situation warrants manual attention and correction).
badTemperatureCount = MAX_BAD_TEMPERATURE_COUNT + 1;
}
// 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)
{
if (platform->DoThermistorAdc(heater) || !(Platform::TempErrorPermanent(err)))
{
// Error may be a temporary error and may correct itself after a few additional reads
badTemperatureCount++;
}
else
{
// Error condition is not expected to correct itself (e.g., digital device such
// as a MAX31855 reporting that the temp sensor is shorted -- even a temporary
// short in such a situation warrants manual attention and correction).
badTemperatureCount = MAX_BAD_TEMPERATURE_COUNT + 1;
}
if (badTemperatureCount > MAX_BAD_TEMPERATURE_COUNT)
{
SetHeater(0.0);
temperatureFault = true;
//switchedOff = true;
platform->MessageF(GENERIC_MESSAGE, "Temperature fault on heater %d%s%s, T = %.1f\n",
if (badTemperatureCount > MAX_BAD_TEMPERATURE_COUNT)
{
SetHeater(0.0);
temperatureFault = true;
//switchedOff = true;
platform->MessageF(GENERIC_MESSAGE, "Temperature fault on heater %d%s%s, T = %.1f\n",
heater,
(err != Platform::TempError::errOk) ? ", " : "",
(err != Platform::TempError::errOk) ? Platform::TempErrorStr(err) : "",
temperature);
reprap.FlagTemperatureFault(heater);
}
}
else
{
reprap.FlagTemperatureFault(heater);
}
}
else
{
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 != BED_HEATER) // FIXME - also check bed warmup time?
{
float tmp = (active) ? activeTemperature : standbyTemperature;
if (temperature < tmp - TEMPERATURE_CLOSE_ENOUGH)
{
float tim = platform->Time() - timeSetHeating;
float limit = platform->TimeToHot();
if (tim > limit && limit > 0.0)
{
SetHeater(0.0);
temperatureFault = true;
//switchedOff = true;
platform->MessageF(GENERIC_MESSAGE, "Heating fault on heater %d, T = %.1f C; still not at temperature %.1f after %f seconds.\n", heater, temperature, tmp, tim);
reprap.FlagTemperatureFault(heater);
}
}
else
{
heatingUp = false;
}
}
// 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 != BED_HEATER) // FIXME - also check bed warmup time?
{
float tmp = (active) ? activeTemperature : standbyTemperature;
if (temperature < tmp - TEMPERATURE_CLOSE_ENOUGH)
{
float tim = platform->Time() - timeSetHeating;
float limit = platform->TimeToHot();
if (tim > limit && limit > 0.0)
{
SetHeater(0.0);
temperatureFault = true;
//switchedOff = true;
platform->MessageF(GENERIC_MESSAGE, "Heating fault on heater %d, T = %.1f C; still not at temperature %.1f after %f seconds.\n", heater, temperature, tmp, tim);
reprap.FlagTemperatureFault(heater);
}
}
else
{
heatingUp = false;
}
}
float targetTemperature = (active) ? activeTemperature : standbyTemperature;
float error = targetTemperature - temperature;
const PidParameters& pp = platform->GetPidParameters(heater);
if (!pp.UsePID())
{
float heaterValue = (error > 0.0) ? min<float>(pp.kS, 1.0) : 0.0;
SetHeater(heaterValue);
averagePWM = averagePWM * (1.0 - invHeatPwmAverageCount) + heaterValue;
return;
}
if(error < -pp.fullBand)
{
// actual temperature is well above target
temp_iState = (targetTemperature + pp.fullBand - 25.0) * pp.kT; // set the I term to our estimate of what will be needed ready for the switch to PID
SetHeater(0.0);
averagePWM *= (1.0 - invHeatPwmAverageCount);
lastTemperature = temperature;
return;
}
if(error > pp.fullBand)
{
// actual temperature is well below target
temp_iState = (targetTemperature - pp.fullBand - 25.0) * pp.kT; // set the I term to our estimate of what will be needed ready for the switch to PID
float heaterValue = min<float>(pp.kS, 1.0);
SetHeater(heaterValue);
averagePWM = averagePWM * (1.0 - invHeatPwmAverageCount) + heaterValue;
lastTemperature = temperature;
return;
}
float sampleInterval = platform->HeatSampleTime();
temp_iState += error * pp.kI * sampleInterval;
if (temp_iState < pp.pidMin)
{
temp_iState = pp.pidMin;
}
else if (temp_iState > pp.pidMax)
{
temp_iState = pp.pidMax;
}
float temp_dState = pp.kD * (temperature - lastTemperature) / sampleInterval;
float result = (pp.kP * error + temp_iState - temp_dState) * pp.kS / 255.0;
float targetTemperature = (active) ? activeTemperature : standbyTemperature;
float error = targetTemperature - temperature;
const PidParameters& pp = platform->GetPidParameters(heater);
lastTemperature = temperature;
if (!pp.UsePID())
{
float heaterValue = (error > 0.0) ? min<float>(pp.kS, 1.0) : 0.0;
SetHeater(heaterValue);
averagePWM = averagePWM * (1.0 - invHeatPwmAverageCount) + heaterValue;
return;
}
if (result < 0.0)
{
result = 0.0;
}
else if (result > 1.0)
{
result = 1.0;
}
if (error < -pp.fullBand)
{
// actual temperature is well above target
temp_iState = (targetTemperature + pp.fullBand - 25.0) * pp.kT; // set the I term to our estimate of what will be needed ready for the switch to PID
SetHeater(0.0);
averagePWM *= (1.0 - invHeatPwmAverageCount);
lastTemperature = temperature;
return;
}
if (!temperatureFault)
{
SetHeater(result);
}
if (error > pp.fullBand)
{
// actual temperature is well below target
temp_iState = (targetTemperature - pp.fullBand - 25.0) * pp.kT; // set the I term to our estimate of what will be needed ready for the switch to PID
float heaterValue = min<float>(pp.kS, 1.0);
SetHeater(heaterValue);
averagePWM = averagePWM * (1.0 - invHeatPwmAverageCount) + heaterValue;
lastTemperature = temperature;
return;
}
averagePWM = averagePWM * (1.0 - invHeatPwmAverageCount) + result;
float sampleInterval = platform->HeatSampleTime();
temp_iState += error * pp.kI * sampleInterval;
if (temp_iState < pp.pidMin)
{
temp_iState = pp.pidMin;
}
else if (temp_iState > pp.pidMax)
{
temp_iState = pp.pidMax;
}
float temp_dState = pp.kD * (temperature - lastTemperature) / sampleInterval;
float result = (pp.kP * error + temp_iState - temp_dState) * pp.kS / 255.0;
lastTemperature = temperature;
if (result < 0.0)
{
result = 0.0;
}
else if (result > 1.0)
{
result = 1.0;
}
if (!temperatureFault)
{
SetHeater(result);
}
averagePWM = averagePWM * (1.0 - invHeatPwmAverageCount) + result;
// debugPrintf("Heater %d: e=%f, P=%f, I=%f, d=%f, r=%f\n", heater, error, pp.kP*error, temp_iState, temp_dState, result);
}
@ -304,4 +307,3 @@ float PID::GetAveragePWM() const
}
// End

View file

@ -137,14 +137,14 @@ a lot of data that needs to be copied, this should be set high. */
#define LWIP_TCP (1)
#define TCP_TTL (255)
/* TCP receive window. */
#define TCP_WND (2 * 1432)
#define TCP_WND (2 * 1460)
/* Controls if TCP should queue segments that arrive out of
order. Define to 0 if your device is low on memory. */
#define TCP_QUEUE_OOSEQ 1
/* TCP Maximum segment size. */
#define TCP_MSS (1432) // 1432 is optimal for Windows clients
#define TCP_MSS (1460) // 1432 is optimal for Windows clients
/* TCP sender buffer space (bytes). */
#define TCP_SND_BUF (2 * 1432) //changed from 2150 to pass LWIP sanity checks
#define TCP_SND_BUF (2 * 1460) //changed from 2150 to pass LWIP sanity checks
/* TCP sender buffer space (pbufs). This must be at least = 2 * TCP_SND_BUF/TCP_MSS for things to work. */
#define TCP_SND_QUEUELEN (3 * TCP_SND_BUF / TCP_MSS)
/* Maximum number of retransmissions of data segments. */

View file

@ -470,7 +470,7 @@ bool PrintMonitor::GetFileInfo(const char *directory, const char *fileName, GCod
}
// Go to the last sector and proceed from there on
const FilePosition seekFromEnd = fileBeingParsed->Length() % GCODE_READ_SIZE;
const FilePosition seekFromEnd = ((fileBeingParsed->Length() - 1) % GCODE_READ_SIZE) + 1;
fileBeingParsed->Seek(fileBeingParsed->Length() - seekFromEnd);
fileOverlapLength = 0;
parseState = parsingFooter;

Binary file not shown.

View file

@ -209,7 +209,7 @@ void Webserver::Spin()
{
telnetInterpreter->SendGCodeReply(transaction);
}
// Process other messages (unless this is an HTTP request which may need special treatement)
// Process other messages (unless this is an HTTP request which may need special treatment)
else if (interpreter != httpInterpreter || httpInterpreter->IsReady())
{
for(size_t i = 0; i < 500; i++)
@ -236,6 +236,7 @@ void Webserver::Spin()
}
}
}
// else the HTTP server is not ready
}
else
{
@ -1042,6 +1043,10 @@ bool Webserver::HttpInterpreter::NeedMoreData()
// At this stage we've processed the first chunk of a POST upload request. Store the
// initial payload and reset the HTTP reader again in order to process new requests
WriteUploadedData(clientMessage + (clientPointer - uploadedBytes), uploadedBytes);
if (reprap.Debug(moduleWebserver))
{
platform->MessageF(HOST_MESSAGE, "Wrote %lu bytes of file\n", uploadedBytes);
}
ResetState();
return false;
}
@ -1228,7 +1233,6 @@ bool Webserver::HttpInterpreter::CharFromClient(char c)
case '\t':
clientMessage[clientPointer++] = 0;
qualifiers[numQualKeys].key = clientMessage + clientPointer; // so that we can read the whole value even if it contains a null
++numCommandWords;
commandWords[numCommandWords] = clientMessage + clientPointer;
state = doingCommandWord;
break;
@ -1431,12 +1435,18 @@ bool Webserver::HttpInterpreter::ProcessMessage()
{
if (reprap.Debug(moduleWebserver))
{
platform->MessageF(HOST_MESSAGE, "HTTP requests with %d command words:", numCommandWords);
platform->Message(HOST_MESSAGE, "HTTP req, command words {");
for (size_t i = 0; i < numCommandWords; ++i)
{
platform->MessageF(HOST_MESSAGE, " %s", commandWords[i]);
}
platform->Message(HOST_MESSAGE, "\n");
platform->Message(HOST_MESSAGE, " }, parameters {");
for (size_t i = 0; i < numQualKeys; ++i)
{
platform->MessageF(HOST_MESSAGE, " %s=%s", qualifiers[i].key, qualifiers[i].value);
}
platform->Message(HOST_MESSAGE, " }\n");
}
if (numCommandWords < 2)
@ -1498,6 +1508,10 @@ bool Webserver::HttpInterpreter::ProcessMessage()
FileStore *file = platform->GetFileStore("0:/", qualifiers[0].value, true);
if (StartUpload(file))
{
if (reprap.Debug(moduleWebserver))
{
platform->MessageF(HOST_MESSAGE, "Start uploading file %s length %lu\n", qualifiers[0].value, postFileLength);
}
// Start new file upload
uploadingTextData = false;
uploadedBytes = numContinuationBytes = 0;
@ -2307,27 +2321,28 @@ void Webserver::FtpInterpreter::ProcessLine()
else if (StringStartsWith(clientMessage, "RETR"))
{
ReadFilename(4);
FileStore *fs = platform->GetFileStore(currentDir, filename, false);
FileStore *file = platform->GetFileStore(currentDir, filename, false);
if (fs == nullptr)
if (file == nullptr)
{
SendReply(550, "Failed to open file.");
}
else
{
snprintf(ftpResponse, ftpResponseLength, "Opening data connection for %s (%lu bytes).", filename, fs->Length());
snprintf(ftpResponse, ftpResponseLength, "Opening data connection for %s (%lu bytes).", filename, file->Length());
SendReply(150, ftpResponse);
if (network->AcquireDataTransaction())
{
// send the file via data port
NetworkTransaction *dataTransaction = network->GetTransaction();
dataTransaction->SetFileToWrite(fs);
dataTransaction->SetFileToWrite(file);
dataTransaction->Commit(false);
state = doingPasvIO;
}
else
{
file->Close();
SendReply(500, "Unknown error.");
network->CloseDataPort();
state = authenticated;