Various bug fixes and enhancements (version 0.59a)

Fixed bug whereby incoming gcodes were URI-decoded twice, which could
cause problems if a comment contain a % character
Fixed bug whereby M109 and M190 waited for all heaters to reach target
temperature instead of just the one involved
Fixed bug whereby files whose names contain an uppercase letter G could
not be uploaded or deleted
Increase buffer size for incoming gcodes
Removed parameter to set max PWM value in M106 command. Max PWM value is
now 255 as per recent RepRapPro change.
Changed the code that deals with dropping characters in unimportant
messages sent to USB such that we only drop characters at the end of a
line (adding ".\n" to whatever we send) or drop whole lines
This commit is contained in:
David Crocker 2014-05-14 22:04:14 +01:00
parent b7701832bc
commit a416c367d2
11 changed files with 791 additions and 723 deletions

View file

@ -24,8 +24,8 @@ Licence: GPL
#define CONFIGURATION_H
#define NAME "RepRapFirmware"
#define VERSION "0.59-dc42"
#define DATE "2014-05-08"
#define VERSION "0.59a-dc42"
#define DATE "2014-05-14"
#define LAST_AUTHOR "dc42"
// Other firmware that we might switch to be compatible with.

View file

@ -68,7 +68,6 @@ void GCodes::Init()
dwellTime = longWait;
limitAxes = true;
axisIsHomed[X_AXIS] = axisIsHomed[Y_AXIS] = axisIsHomed[Z_AXIS] = false;
fanMaxPwm = 1.0;
coolingInverted = false;
}
@ -96,6 +95,9 @@ void GCodes::Reset()
cannedCycleMoveCount = 0;
cannedCycleMoveQueued = false;
gFeedRate = platform->MaxFeedrate(Z_AXIS); // Typically the slowest
speedFactor = 1.0/60.0; // default is just to convert from mm/minute to mm/second
extrusionFactor = 1.0;
}
void GCodes::doFilePrint(GCodeBuffer* gb)
@ -362,7 +364,7 @@ void GCodes::LoadMoveBufferFromGCode(GCodeBuffer *gb, bool doingG92, bool applyL
if (gb->Seen(gCodeLetters[i]))
{
// Doing an extruder. We need to store the relative distance for this move in moveBuffer and the resulting new position in lastPos.
float moveArg = gb->GetFValue() * distanceScale;
float moveArg = gb->GetFValue() * distanceScale * extrusionFactor;
if (doingG92)
{
moveBuffer[i] = 0.0; // no move required
@ -385,7 +387,9 @@ void GCodes::LoadMoveBufferFromGCode(GCodeBuffer *gb, bool doingG92, bool applyL
// Deal with feed rate
if (gb->Seen(gCodeLetters[DRIVES]))
gFeedRate = gb->GetFValue() * distanceScale * 0.016666667; // G Code feedrates are in mm/minute; we need mm/sec
{
gFeedRate = gb->GetFValue() * distanceScale * speedFactor;
}
moveBuffer[DRIVES] = gFeedRate; // We always set it, as Move may have modified the last one.
}
@ -1456,18 +1460,38 @@ void GCodes::SetHeaterParameters(GCodeBuffer *gb, char reply[STRING_LENGTH])
bool GCodes::ActOnGcode(GCodeBuffer *gb)
{
int code;
// 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.
// 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.
// So it doesn't matter inn which order we look for them.
if (gb->Seen('G'))
{
return HandleGcode(gb);
}
if (gb->Seen('T'))
{
return HandleTcode(gb);
}
// An empty buffer gets discarded
HandleReply(false, gb == serialGCode, "", 'X', 0, false);
return true;
}
bool GCodes::HandleGcode(GCodeBuffer* gb)
{
bool result = true;
bool error = false;
bool resend = false;
bool seen;
char reply[STRING_LENGTH];
reply[0] = 0;
if (gb->Seen('G'))
{
code = gb->GetIValue();
int code = gb->GetIValue();
switch (code)
{
case 0: // There are no rapid moves...
@ -1566,13 +1590,21 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
snprintf(reply, STRING_LENGTH, "invalid G Code: %s", gb->Buffer());
}
if (result)
{
HandleReply(error, gb == serialGCode, reply, 'G', code, resend);
}
return result;
}
if (gb->Seen('M'))
bool GCodes::HandleMcode(GCodeBuffer* gb)
{
code = gb->GetIValue();
bool result = true;
bool error = false;
bool resend = false;
char reply[STRING_LENGTH];
reply[0] = 0;
int code = gb->GetIValue();
switch (code)
{
case 0: // Stop
@ -1681,7 +1713,8 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
break;
case 92: // Set/report steps/mm for some axes
seen = false;
{
bool seen = false;
for (int8_t drive = 0; drive < DRIVES; drive++)
{
if (gb->Seen(gCodeLetters[drive]))
@ -1697,6 +1730,7 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
(int) platform->DriveStepsPerUnit(X_AXIS), (int) platform->DriveStepsPerUnit(Y_AXIS),
(int) platform->DriveStepsPerUnit(Z_AXIS), (int) platform->DriveStepsPerUnit(AXES)); // FIXME - needs to do multiple extruders
}
}
break;
case 98:
@ -1728,34 +1762,46 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
break;
case 106: // Fan on or off
if (gb->Seen('P'))
{
// slic3r and Cura expect PWM values to go from 0 to 255, but we expect PWM value to go from 0.0 to 1.0.
// So I've added a P parameter to allow the top end of the range to be set, so we can be compatible with those programs.
fanMaxPwm = fmax(gb->GetFValue(), 1.0);
}
if (gb->Seen('I'))
{
coolingInverted = (gb->GetIValue() > 0);
}
if (gb->Seen('S'))
{
float f = gb->GetFValue();
f = fmin(f, 255.0);
f = fmax(f, 0.0);
if (coolingInverted)
{
platform->CoolingFan(1.0 - fmax(gb->GetFValue(), 0.0)/fanMaxPwm);
platform->CoolingFan(255.0 - f);
}
else
{
platform->CoolingFan(fmax(gb->GetFValue(), 0.0)/fanMaxPwm);
platform->CoolingFan(f);
}
}
break;
case 107: // Fan off - deprecated
platform->CoolingFan(coolingInverted ? 1.0 : 0.0);
platform->CoolingFan(coolingInverted ? 255.0 : 0.0);
break;
// case 109 is later because it falls through to case 116
case 109: // Set extruder temperature and wait - deprecated
case 190: // Set bed temperature and wait - deprecated
{
int8_t heater = (code == 190) ? 0 : 1;
if (gb->Seen('S'))
{
reprap.GetHeat()->SetActiveTemperature(heater, gb->GetFValue());
reprap.GetHeat()->Activate(heater);
}
if (!AllMovesAreFinishedAndMoveBufferIsLoaded())
{
return false;
}
result = reprap.GetHeat()->HeaterAtSetTemperature(heater);
}
break;
case 110: // Set line numbers - line numbers are dealt with in the GCodeBuffer class
break;
@ -1765,7 +1811,8 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
reprap.SetDebug(gb->GetIValue());
break;
case 112: // Emergency stop - acted upon in Webserver
case 112: // Emergency stop - acted upon in Webserver, but also here in case it comes from USB etc.
reprap.EmergencyStop();
break;
case 114: // Deprecated
@ -1787,15 +1834,6 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
ELECTRONICS, DATE);
break;
case 109: // Set extruder temperature and wait - deprecated
case 190: // Set bed temperature and wait - deprecated
if (gb->Seen('S'))
{
uint8_t heater = (code == 190) ? 0 : 1;
reprap.GetHeat()->SetActiveTemperature(heater, gb->GetFValue());
reprap.GetHeat()->Activate(heater);
}
/* no break */
case 116: // Wait for everything, especially set temperatures
if (!AllMovesAreFinishedAndMoveBufferIsLoaded())
{
@ -1841,7 +1879,7 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
platform->Message(HOST_MESSAGE, "M141 - heated chamber not yet implemented\n");
break;
// case 190 is earlier because it falls through to case 116
// case 190 is with case 109 because it used the same code
case 201: // Set axis accelerations
for (int8_t drive = 0; drive < DRIVES; drive++)
@ -1894,6 +1932,20 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
}
break;
case 220: // set speed factor override percentage
if (gb->Seen('S'))
{
speedFactor = gb->GetFValue()/(60 * 100.0); // include the conversion from mm/minute to mm/second
}
break;
case 221: // set extrusion factor override percentage
if (gb->Seen('S'))
{
extrusionFactor = gb->GetFValue()/100.0;
}
break;
case 301: // Set hot end PID values
SetPidParameters(gb, 1, reply);
break;
@ -2093,22 +2145,27 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
return result;
}
if (gb->Seen('T'))
bool GCodes::HandleTcode(GCodeBuffer* gb)
{
code = gb->GetIValue();
bool error = false;
char reply[STRING_LENGTH];
reply[0] = 0;
int code = gb->GetIValue();
if (code == selectedHead)
{
if (result)
HandleReply(error, gb == serialGCode, reply, 'T', code, resend);
return result;
HandleReply(error, gb == serialGCode, reply, 'T', code, false);
return true;
}
error = true;
for (int8_t i = AXES; i < DRIVES; i++)
{
if (selectedHead == i - AXES)
{
reprap.GetHeat()->Standby(selectedHead + 1); // + 1 because 0 is the Bed
}
}
for (int8_t i = AXES; i < DRIVES; i++)
{
if (code == i - AXES)
@ -2120,19 +2177,12 @@ bool GCodes::ActOnGcode(GCodeBuffer *gb)
}
if (error)
{
snprintf(reply, STRING_LENGTH, "Invalid T Code: %s", gb->Buffer());
if (result)
HandleReply(error, gb == serialGCode, reply, 'T', code, resend);
return result;
}
// An empty buffer jumps to here and gets discarded
if (result)
HandleReply(error, gb == serialGCode, reply, 'X', code, resend);
return result;
HandleReply(error, gb == serialGCode, reply, 'T', code, false);
return true;
}
// Return the amount of filament extruded
@ -2321,7 +2371,7 @@ const char* GCodeBuffer::GetUnprecedentedString()
return gcodeBuffer; // Good idea?
}
char* result = &gcodeBuffer[readPointer + 1];
const char* result = &gcodeBuffer[readPointer + 1];
readPointer = -1;
return result;
}

View file

@ -27,7 +27,6 @@ Licence: GPL
#define GCODE_LETTERS { 'X', 'Y', 'Z', 'E', 'F' } // The drives and feedrate in a GCode
// Small class to hold an individual GCode and provide functions to allow it to be parsed
class GCodeBuffer
@ -158,6 +157,9 @@ class GCodes
bool DoFileCannedCycles(const char* fileName);
bool FileCannedCyclesReturn();
bool ActOnGcode(GCodeBuffer* gb);
bool HandleGcode(GCodeBuffer* gb);
bool HandleMcode(GCodeBuffer* gb);
bool HandleTcode(GCodeBuffer* gb);
int SetUpMove(GCodeBuffer* gb);
bool DoDwell(GCodeBuffer *gb);
bool DoDwellTime(float dwell);
@ -232,9 +234,10 @@ class GCodes
float longWait;
bool limitAxes; // Don't think outside the box.
bool axisIsHomed[3]; // these record which of the axes have been homed
float fanMaxPwm; // the M106 S value that represents 100% fan speed
bool waitingForMoveToComplete;
bool coolingInverted;
float speedFactor; // speed factor, including the conversion from mm/min to mm/sec, normally 1/60
float extrusionFactor; // extrusion factor, normally 1.0
};
//*****************************************************************************************************

View file

@ -63,31 +63,24 @@ void Heat::Diagnostics()
platform->Message(HOST_MESSAGE, "Heat Diagnostics:\n");
}
bool Heat::AllHeatersAtSetTemperatures()
bool Heat::AllHeatersAtSetTemperatures() const
{
float dt;
for(int8_t heater = 0; heater < HEATERS; heater++)
{
dt = GetTemperature(heater);
if(pids[heater]->Active())
{
if(GetActiveTemperature(heater) < TEMPERATURE_LOW_SO_DONT_CARE)
dt = 0.0;
else
dt = fabs(dt - GetActiveTemperature(heater));
} else
{
if(GetStandbyTemperature(heater) < TEMPERATURE_LOW_SO_DONT_CARE)
dt = 0.0;
else
dt = fabs(dt - GetStandbyTemperature(heater));
}
if(dt > TEMPERATURE_CLOSE_ENOUGH)
if (!HeaterAtSetTemperature(heater))
return false;
}
return true;
}
//query an individual heater
bool Heat::HeaterAtSetTemperature(int8_t heater) const
{
float dt = GetTemperature(heater);
float target = (pids[heater]->Active()) ? GetActiveTemperature(heater) : GetStandbyTemperature(heater);
return (target < TEMPERATURE_LOW_SO_DONT_CARE) || (fabs(dt - target) <= TEMPERATURE_CLOSE_ENOUGH);
}
//******************************************************************************************************
PID::PID(Platform* p, int8_t h)

47
Heat.h
View file

@ -28,15 +28,15 @@ class PID
PID(Platform* p, int8_t h);
void Init();
void Spin();
void SetActiveTemperature(const float& t);
float GetActiveTemperature();
void SetStandbyTemperature(const float& t);
float GetStandbyTemperature();
void SetActiveTemperature(float t);
float GetActiveTemperature() const;
void SetStandbyTemperature(float t);
float GetStandbyTemperature() const;
void Activate();
void Standby();
bool Active();
bool Active() const;
void ResetFault();
float GetTemperature();
float GetTemperature() const;
private:
@ -61,15 +61,16 @@ class Heat
void Spin();
void Init();
void Exit();
void SetActiveTemperature(int8_t heater, const float& t);
float GetActiveTemperature(int8_t heater);
void SetStandbyTemperature(int8_t heater, const float& t);
float GetStandbyTemperature(int8_t heater);
void SetActiveTemperature(int8_t heater, float t);
float GetActiveTemperature(int8_t heater) const;
void SetStandbyTemperature(int8_t heater, float t);
float GetStandbyTemperature(int8_t heater) const;
void Activate(int8_t heater);
void Standby(int8_t heater);
float GetTemperature(int8_t heater);
float GetTemperature(int8_t heater) const;
void ResetFault(int8_t heater);
bool AllHeatersAtSetTemperatures();
bool AllHeatersAtSetTemperatures() const;
bool HeaterAtSetTemperature(int8_t heater) const; // Is a specific heater at temperature within tolerance?
void Diagnostics();
private:
@ -85,32 +86,32 @@ class Heat
//***********************************************************************************************************
inline bool PID::Active()
inline bool PID::Active() const
{
return active;
}
inline void PID::SetActiveTemperature(const float& t)
inline void PID::SetActiveTemperature(float t)
{
activeTemperature = t;
}
inline float PID::GetActiveTemperature()
inline float PID::GetActiveTemperature() const
{
return activeTemperature;
}
inline void PID::SetStandbyTemperature(const float& t)
inline void PID::SetStandbyTemperature(float t)
{
standbyTemperature = t;
}
inline float PID::GetStandbyTemperature()
inline float PID::GetStandbyTemperature() const
{
return standbyTemperature;
}
inline float PID::GetTemperature()
inline float PID::GetTemperature() const
{
return temperature;
}
@ -132,7 +133,7 @@ inline void PID::ResetFault()
}
inline void Heat::SetActiveTemperature(int8_t heater, const float& t)
inline void Heat::SetActiveTemperature(int8_t heater, float t)
{
if (heater >= 0 && heater < HEATERS)
{
@ -140,12 +141,12 @@ inline void Heat::SetActiveTemperature(int8_t heater, const float& t)
}
}
inline float Heat::GetActiveTemperature(int8_t heater)
inline float Heat::GetActiveTemperature(int8_t heater) const
{
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetActiveTemperature() : ABS_ZERO;
}
inline void Heat::SetStandbyTemperature(int8_t heater, const float& t)
inline void Heat::SetStandbyTemperature(int8_t heater, float t)
{
if (heater >= 0 && heater < HEATERS)
{
@ -153,12 +154,12 @@ inline void Heat::SetStandbyTemperature(int8_t heater, const float& t)
}
}
inline float Heat::GetStandbyTemperature(int8_t heater)
inline float Heat::GetStandbyTemperature(int8_t heater) const
{
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetStandbyTemperature() : ABS_ZERO;
}
inline float Heat::GetTemperature(int8_t heater)
inline float Heat::GetTemperature(int8_t heater) const
{
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetTemperature() : ABS_ZERO;
}

View file

@ -988,12 +988,14 @@ void Platform::SetMotorCurrent(byte drive, float current)
mcp.setVolatileWiper(potWipes[drive], pot);
}
//Changed to be compatible with existing gcode norms
// M106 S0 = fully off M106 S255 = fully on
void Platform::CoolingFan(float speed)
{
if(coolingFanPin > 0)
{
// The cooling fan output pin gets inverted if HEAT_ON == 0
analogWriteNonDue(coolingFanPin, (uint32_t)( ((HEAT_ON == 0) ? (1.0 - speed) : speed) * 255.0));
analogWriteNonDue(coolingFanPin, (uint32_t)( (HEAT_ON == 0) ? (255.0 - speed) : speed));
}
}
@ -1014,25 +1016,6 @@ void Platform::SetInterrupt(float s) // Seconds
NVIC_EnableIRQ(TC3_IRQn);
}
int8_t Line::Status() const
{
// if(alternateInput != NULL)
// return alternateInput->Status();
return inputNumChars == 0 ? nothing : byteAvailable;
}
int Line::Read(char& b)
{
// if(alternateInput != NULL)
// return alternateInput->Read(b);
if (inputNumChars == 0) return 0;
b = inBuffer[inputGetIndex];
inputGetIndex = (inputGetIndex + 1) % lineInBufsize;
--inputNumChars;
return 1;
}
//-----------------------------------------------------------------------------------------------------
FileStore* Platform::GetFileStore(const char* directory, const char* fileName, bool write)
@ -1550,12 +1533,32 @@ Line::Line()
{
}
int8_t Line::Status() const
{
// if(alternateInput != NULL)
// return alternateInput->Status();
return inputNumChars == 0 ? nothing : byteAvailable;
}
int Line::Read(char& b)
{
// if(alternateInput != NULL)
// return alternateInput->Read(b);
if (inputNumChars == 0) return 0;
b = inBuffer[inputGetIndex];
inputGetIndex = (inputGetIndex + 1) % lineInBufsize;
--inputNumChars;
return 1;
}
void Line::Init()
{
inputGetIndex = 0;
inputNumChars = 0;
outputGetIndex = 0;
outputNumChars = 0;
ignoringOutputLine = false;
SerialUSB.begin(BAUD_RATE);
inUsbWrite = false;
}
@ -1583,25 +1586,69 @@ void Line::Spin()
TryFlushOutput();
}
// Write a character to USB.
// If 'block' is true then we don't return until we have either written it to the USB port of put it in the buffer.
// Otherwise, if the buffer is full then we append ".\n" to the end of it, return immediately and ignore the rest
// of the data we are asked to print until we get a new line.
void Line::Write(char b, bool block)
{
do
if (block)
{
// We failed to print an unimportant message that (unusually) didn't finish in a newline
ignoringOutputLine = false;
}
if (ignoringOutputLine)
{
// We have already failed to write some characters of this message line, so don't write any of it.
// But try to start sending again after this line finishes.
if (b == '\n')
{
ignoringOutputLine = false;
}
TryFlushOutput(); // this may help free things up
}
else
{
for(;;)
{
TryFlushOutput();
if (outputNumChars == 0 && SerialUSB.canWrite() != 0)
{
// We can write the character directly into the USB output buffer
++inUsbWrite;
SerialUSB.write(b);
--inUsbWrite;
break;
}
else if (outputNumChars < lineOutBufSize)
else if ( outputNumChars + 2 < lineOutBufSize // save 2 spaces in the output buffer
|| (outputNumChars < lineOutBufSize && (block || b == '\n')) //...unless doing blocking output or writing newline
)
{
outBuffer[(outputGetIndex + outputNumChars) % lineOutBufSize] = b;
++outputNumChars;
break;
}
} while (block);
else if (!block)
{
if (outputNumChars + 2 == lineOutBufSize)
{
// We still have our 2 free characters, so append ".\n" to the line to indicate it was incomplete
outBuffer[(outputGetIndex + outputNumChars) % lineOutBufSize] = '.';
++outputNumChars;
outBuffer[(outputGetIndex + outputNumChars) % lineOutBufSize] = '\n';
++outputNumChars;
}
else
{
// As we don't have 2 spare characters in the buffer, we can't have written any of the current line.
// So ignore the whole line.
}
ignoringOutputLine = true;
break;
}
}
}
// else discard the character
}

View file

@ -99,7 +99,7 @@ const unsigned int numZProbeReadingsAveraged = 8; // we average this number of r
#define MAX_FEEDRATES {50.0, 50.0, 3.0, 16.0} // mm/sec
#define ACCELERATIONS {800.0, 800.0, 10.0, 250.0} // mm/sec^2
#define DRIVE_STEPS_PER_UNIT {87.4890, 87.4890, 4000.0, 420.0}
#define INSTANT_DVS {10.0, 10.0, 0.2, 2.0} // (mm/sec)
#define INSTANT_DVS {10.0, 10.0, 0.2, 2.0} // (mm/sec) these are also the minimum feed rates which is why I (dc42) decreased X/Y from 15 to 10
// AXES
@ -195,10 +195,8 @@ const unsigned int adDisconnectedVirtual = adDisconnectedReal << adOversampleBit
#define FILE_LIST_BRACKET '"'
#define FILE_LIST_LENGTH (1000) // Maximum length of file list - can't make it much longer unless we also make jsonResponse longer
#define FLASH_LED 'F' // Type byte of a message that is to flash an LED; the next two bytes define
// the frequency and M/S ratio.
#define DISPLAY_MESSAGE 'L' // Type byte of a message that is to appear on a local display; the L is
// not displayed; \f and \n should be supported.
#define FLASH_LED 'F' // Type byte of a message that is to flash an LED; the next two bytes define the frequency and M/S ratio.
#define DISPLAY_MESSAGE 'L' // Type byte of a message that is to appear on a local display; the L is not displayed; \f and \n should be supported.
#define HOST_MESSAGE 'H' // Type byte of a message that is to be sent to the host; the H is not sent.
#define DEBUG_MESSAGE 'D' // Type byte of a message that is to be sent for debugging; the D is not sent.
@ -295,6 +293,7 @@ private:
uint16_t outputNumChars;
uint8_t inUsbWrite;
bool ignoringOutputLine;
};
class MassStorage

View file

@ -365,7 +365,7 @@ void debugPrintf(const char* fmt, ...)
va_start(p, fmt);
vsnprintf(scratchString, ARRAY_SIZE(scratchString), fmt, p);
va_end(p);
scratchString[ARRAY_SIZE(scratchString) - 1] = 0;
scratchString[ARRAY_UPB(scratchString)] = 0;
reprap.GetPlatform()->Message(DEBUG_MESSAGE, scratchString);
}

View file

@ -56,6 +56,8 @@ int StringContains(const char* string, const char* match);
// Macro to give us the number of elements in an array
#define ARRAY_SIZE(_x) (sizeof(_x)/sizeof(_x[0]))
// Macro to give us the highest valid index into an array i.e. one less than the size
#define ARRAY_UPB(_x) (ARRAY_SIZE(_x) - 1)
extern char scratchString[];

View file

@ -82,7 +82,7 @@ byte Webserver::ReadGCode()
}
// Process a received string of gcodes
void Webserver::LoadGcodeBuffer(const char* gc, bool convertWeb)
void Webserver::LoadGcodeBuffer(const char* gc)
{
char gcodeTempBuf[GCODE_LENGTH];
uint16_t gtp = 0;
@ -97,40 +97,6 @@ void Webserver::LoadGcodeBuffer(const char* gc, bool convertWeb)
return;
}
if (c == '+' && convertWeb)
{
c = ' ';
}
else if (c == '%' && convertWeb)
{
c = *gc++;
if (c != 0)
{
unsigned char uc;
if (isalpha(c))
{
uc = 16 * (c - 'A' + 10);
}
else
{
uc = 16 * (c - '0');
}
c = *gc++;
if (c != 0)
{
if (isalpha(c))
{
uc += c - 'A' + 10;
}
else
{
uc += c - '0';
}
c = uc;
}
}
}
if (c == '\n')
{
gcodeTempBuf[gtp] = 0;
@ -145,7 +111,7 @@ void Webserver::LoadGcodeBuffer(const char* gc, bool convertWeb)
inComment = true;
}
if (gtp == ARRAY_SIZE(gcodeTempBuf) - 1)
if (gtp == ARRAY_UPB(gcodeTempBuf))
{
// gcode is too long, we haven't room for another character and a null
if (c != ' ' && !inComment)
@ -219,7 +185,7 @@ void Webserver::ProcessGcode(const char* gc)
{
char c;
size_t i = 0;
while (i < STRING_LENGTH && configFile->Read(c))
while (i < ARRAY_UPB(gcodeReply) && configFile->Read(c))
{
gcodeReply[i++] = c;
}
@ -360,7 +326,7 @@ void Webserver::JsonReport(bool ok, const char* request)
{
if (ok)
{
jsonResponse[STRING_LENGTH] = 0;
jsonResponse[ARRAY_UPB(jsonResponse)] = 0;
if (reprap.Debug())
{
platform->Message(HOST_MESSAGE, "JSON response: ");
@ -395,8 +361,8 @@ void Webserver::GetJsonResponse(const char* request)
if (StringStartsWith(request, "gcode") && StringStartsWith(clientQualifier, "gcode="))
{
LoadGcodeBuffer(&clientQualifier[6], true);
snprintf(jsonResponse, STRING_LENGTH, "{\"buff\":%u}", GetReportedGcodeBufferSpace());
LoadGcodeBuffer(&clientQualifier[6]);
snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"buff\":%u}", GetReportedGcodeBufferSpace());
JsonReport(true, request);
return;
}
@ -404,7 +370,7 @@ void Webserver::GetJsonResponse(const char* request)
if (StringStartsWith(request, "files"))
{
const char* fileList = platform->GetMassStorage()->FileList(platform->GetGCodeDir(), false);
snprintf(jsonResponse, STRING_LENGTH, "{\"files\":[%s]}", fileList);
snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"files\":[%s]}", fileList);
JsonReport(true, request);
return;
}
@ -416,11 +382,11 @@ void Webserver::GetJsonResponse(const char* request)
bool found = GetFileInfo(clientQualifier + 5, length, height, filament);
if (found)
{
snprintf(jsonResponse, STRING_LENGTH, "{\"size\":%lu,\"height\":\"%.2f\",\"filament\":\"%.1f\"}", length, height, filament);
snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"size\":%lu,\"height\":\"%.2f\",\"filament\":\"%.1f\"}", length, height, filament);
}
else
{
snprintf(jsonResponse, STRING_LENGTH, "{}");
snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{}");
}
JsonReport(true, request);
return;
@ -428,7 +394,7 @@ void Webserver::GetJsonResponse(const char* request)
if (StringStartsWith(request, "name"))
{
snprintf(jsonResponse, STRING_LENGTH, "{\"myName\":\"%s\"}", myName);
snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"myName\":\"%s\"}", myName);
JsonReport(true, request);
return;
}
@ -436,21 +402,21 @@ void Webserver::GetJsonResponse(const char* request)
if (StringStartsWith(request, "password"))
{
CheckPassword();
snprintf(jsonResponse, STRING_LENGTH, "{\"password\":\"%s\"}", (gotPassword) ? "right" : "wrong");
snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"password\":\"%s\"}", (gotPassword) ? "right" : "wrong");
JsonReport(true, request);
return;
}
if (StringStartsWith(request, "axes"))
{
strncpy(jsonResponse, "{\"axes\":", STRING_LENGTH);
strncpy(jsonResponse, "{\"axes\":", ARRAY_UPB(jsonResponse));
char ch = '[';
for (int8_t drive = 0; drive < AXES; drive++)
{
sncatf(jsonResponse, STRING_LENGTH, "%c\"%.1f\"", ch, platform->AxisLength(drive));
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "%c\"%.1f\"", ch, platform->AxisLength(drive));
ch = ',';
}
strncat(jsonResponse, "]}", STRING_LENGTH);
strncat(jsonResponse, "]}", ARRAY_UPB(jsonResponse));
JsonReport(true, request);
return;
}
@ -467,36 +433,36 @@ void Webserver::GetStatusResponse(uint8_t type)
// New-style status request
// Send the printing/idle status
char ch = (reprap.IsStopped()) ? 'S' : (gc->PrintingAFile()) ? 'P' : 'I';
snprintf(jsonResponse, STRING_LENGTH, "{\"status\":\"%c\",\"heaters\":", ch);
snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"status\":\"%c\",\"heaters\":", ch);
// Send the heater temperatures
ch = '[';
for (int8_t heater = 0; heater < HEATERS; heater++)
{
sncatf(jsonResponse, STRING_LENGTH, "%c\"%.1f\"", ch, reprap.GetHeat()->GetTemperature(heater));
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "%c\"%.1f\"", ch, reprap.GetHeat()->GetTemperature(heater));
ch = ',';
}
// Send XYZ and extruder positions
float liveCoordinates[DRIVES + 1];
reprap.GetMove()->LiveCoordinates(liveCoordinates);
strncat(jsonResponse, "],\"pos\":", STRING_LENGTH); // announce the XYZ position
strncat(jsonResponse, "],\"pos\":", ARRAY_UPB(jsonResponse)); // announce the XYZ position
ch = '[';
// We currently provide the extruder 0 value here as well as XYZ. This is only expected by V0.69 and V0.70 of the web interface so it can be removed soon.
for (int8_t drive = 0; drive < AXES + 1; drive++)
//for (int8_t drive = 0; drive < AXES; drive++)
{
sncatf(jsonResponse, STRING_LENGTH, "%c\"%.2f\"", ch, liveCoordinates[drive]);
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "%c\"%.2f\"", ch, liveCoordinates[drive]);
ch = ',';
}
sncatf(jsonResponse, STRING_LENGTH, "],\"extr\":"); // announce the extruder positions
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "],\"extr\":"); // announce the extruder positions
ch = '[';
for (int8_t drive = AXES; drive < DRIVES; drive++) // loop through extruders
{
sncatf(jsonResponse, STRING_LENGTH, "%c\"%.3f\"", ch, gc->GetExtruderPosition(drive - AXES));
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "%c\"%.3f\"", ch, gc->GetExtruderPosition(drive - AXES));
ch = ',';
}
strncat(jsonResponse, "]", STRING_LENGTH);
strncat(jsonResponse, "]", ARRAY_UPB(jsonResponse));
}
else
{
@ -504,10 +470,10 @@ void Webserver::GetStatusResponse(uint8_t type)
// These are all returned in a single vector called "poll".
// This is a poor choice of format because we can't easily tell which is which unless we already know the number of heaters and extruders.
char c = (gc->PrintingAFile()) ? 'P' : 'I';
snprintf(jsonResponse, STRING_LENGTH, "{\"poll\":[\"%c\",", c); // Printing
snprintf(jsonResponse, ARRAY_UPB(jsonResponse), "{\"poll\":[\"%c\",", c); // Printing
for (int8_t heater = 0; heater < HEATERS; heater++)
{
sncatf(jsonResponse, STRING_LENGTH, "\"%.1f\",", reprap.GetHeat()->GetTemperature(heater));
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "\"%.1f\",", reprap.GetHeat()->GetTemperature(heater));
}
// Send XYZ and extruder positions
float liveCoordinates[DRIVES + 1];
@ -515,7 +481,7 @@ void Webserver::GetStatusResponse(uint8_t type)
for (int8_t drive = 0; drive < DRIVES; drive++) // loop through extruders
{
char ch = (drive == DRIVES - 1) ? ']' : ','; // append ] to the last one but , to the others
sncatf(jsonResponse, STRING_LENGTH, "\"%.2f\"%c", liveCoordinates[drive], ch);
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), "\"%.2f\"%c", liveCoordinates[drive], ch);
}
}
@ -525,43 +491,43 @@ void Webserver::GetStatusResponse(uint8_t type)
switch (platform->GetZProbeSecondaryValues(v1, v2))
{
case 1:
sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d (%d)\"", v0, v1);
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"probe\":\"%d (%d)\"", v0, v1);
break;
case 2:
sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d (%d, %d)\"", v0, v1, v2);
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"probe\":\"%d (%d, %d)\"", v0, v1, v2);
break;
default:
sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d\"", v0);
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"probe\":\"%d\"", v0);
break;
}
// Send the amount of buffer space available for gcodes
sncatf(jsonResponse, STRING_LENGTH, ",\"buff\":%u", GetReportedGcodeBufferSpace());
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"buff\":%u", GetReportedGcodeBufferSpace());
// Send the home state. To keep the messages short, we send 1 for homed and 0 for not homed, instead of true and false.
if (type != 0)
{
sncatf(jsonResponse, STRING_LENGTH, ",\"homed\":[%d,%d,%d]",
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"homed\":[%d,%d,%d]",
(gc->GetAxisIsHomed(0)) ? 1 : 0,
(gc->GetAxisIsHomed(1)) ? 1 : 0,
(gc->GetAxisIsHomed(2)) ? 1 : 0);
}
else
{
sncatf(jsonResponse, STRING_LENGTH, ",\"hx\":%d,\"hy\":%d,\"hz\":%d",
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"hx\":%d,\"hy\":%d,\"hz\":%d",
(gc->GetAxisIsHomed(0)) ? 1 : 0,
(gc->GetAxisIsHomed(1)) ? 1 : 0,
(gc->GetAxisIsHomed(2)) ? 1 : 0);
}
// Send the response sequence number
sncatf(jsonResponse, STRING_LENGTH, ",\"seq\":%u", (unsigned int) seq);
sncatf(jsonResponse, ARRAY_UPB(jsonResponse), ",\"seq\":%u", (unsigned int) seq);
// Send the response to the last command. Do this last because it is long and may need to be truncated.
strncat(jsonResponse, ",\"resp\":\"", STRING_LENGTH);
size_t jp = strnlen(jsonResponse, STRING_LENGTH);
strncat(jsonResponse, ",\"resp\":\"", ARRAY_UPB(jsonResponse));
size_t jp = strnlen(jsonResponse, ARRAY_UPB(jsonResponse));
const char *p = gcodeReply;
while (*p != 0 && jp < STRING_LENGTH - 2) // leave room for the final '"}'
while (*p != 0 && jp < ARRAY_SIZE(jsonResponse) - 3) // leave room for the final '"}\0'
{
char c = *p++;
char esc;
@ -588,7 +554,7 @@ void Webserver::GetStatusResponse(uint8_t type)
}
if (esc)
{
if (jp == STRING_LENGTH - 3)
if (jp == ARRAY_SIZE(jsonResponse) - 4)
{
break;
}
@ -601,7 +567,7 @@ void Webserver::GetStatusResponse(uint8_t type)
}
}
jsonResponse[jp] = 0;
strncat(jsonResponse, "\"}", STRING_LENGTH);
strncat(jsonResponse, "\"}", ARRAY_UPB(jsonResponse));
}
/*
@ -629,8 +595,8 @@ void Webserver::ParseGetPost()
platform->Message(HOST_MESSAGE, "\n");
}
int i = 5;
int j = 0;
size_t i = 5;
size_t j = 0;
clientRequest[j] = 0;
clientQualifier[0] = 0;
while (clientLine[i] != ' ' && clientLine[i] != '?')
@ -644,13 +610,17 @@ void Webserver::ParseGetPost()
{
i++;
j = 0;
for(;;)
while(j < ARRAY_UPB(clientQualifier))
{
char c = clientLine[i++];
if (c == ' ')
{
break;
}
else if (c == '+')
{
clientQualifier[j++] = ' ';
}
else if (c == '%' && isalnum(clientLine[i]) && isalnum(clientLine[i + 1]))
{
c = clientLine[i++];
@ -686,7 +656,7 @@ void Webserver::ParseClientLine()
postSeen = false;
getSeen = true;
if (!clientRequest[0])
strncpy(clientRequest, INDEX_PAGE, STRING_LENGTH);
strncpy(clientRequest, INDEX_PAGE, ARRAY_SIZE(clientRequest));
return;
}
@ -698,7 +668,7 @@ void Webserver::ParseClientLine()
getSeen = false;
if (!clientRequest[0])
{
strncpy(clientRequest, INDEX_PAGE, STRING_LENGTH);
strncpy(clientRequest, INDEX_PAGE, ARRAY_SIZE(clientRequest));
}
return;
}
@ -707,15 +677,15 @@ void Webserver::ParseClientLine()
if (postSeen && ((bnd = StringContains(clientLine, "boundary=")) >= 0))
{
if (strlen(&clientLine[bnd]) >= POST_LENGTH - 4)
if (strlen(&clientLine[bnd]) >= ARRAY_SIZE(postBoundary) - 4)
{
platform->Message(HOST_MESSAGE, "Post boundary buffer overflow.\n");
return;
}
postBoundary[0] = '-';
postBoundary[1] = '-';
strncpy(&postBoundary[2], &clientLine[bnd], POST_LENGTH - 3);
strncat(postBoundary, "--", POST_LENGTH);
strncpy(&postBoundary[2], &clientLine[bnd], ARRAY_SIZE(postBoundary) - 3);
strncat(postBoundary, "--", ARRAY_SIZE(postBoundary));
return;
}
@ -731,10 +701,11 @@ void Webserver::ParseClientLine()
while (clientLine[bnd] && clientLine[bnd] != '"')
{
postFileName[i++] = clientLine[bnd++];
if (i >= POST_LENGTH)
if (i >= ARRAY_SIZE(postFileName))
{
i = 0;
platform->Message(HOST_MESSAGE, "Post filename buffer overflow.\n");
break;
}
}
postFileName[i] = 0;
@ -805,11 +776,11 @@ bool Webserver::CharFromClient(char c)
clientLineIsBlank = false;
clientLine[clientLinePointer] = c;
clientLinePointer++;
if (clientLinePointer >= STRING_LENGTH)
if (clientLinePointer + 2 >= ARRAY_SIZE(clientLine))
{
platform->Message(HOST_MESSAGE, "Client read buffer overflow. Data:\n");
clientLine[STRING_LENGTH] = '\n'; // note that clientLine is now STRING_LENGTH+2 characters long to make room for these
clientLine[STRING_LENGTH + 1] = 0;
clientLine[ARRAY_SIZE(clientLine) - 2] = '\n';
clientLine[ARRAY_SIZE(clientLine) - 1] = 0;
platform->Message(HOST_MESSAGE, clientLine);
reprap.GetNetwork()->SendAndClose(NULL); // close the connection
@ -942,20 +913,20 @@ void Webserver::HandleReply(const char *s, bool error)
if (error)
{
strcpy(gcodeReply, "Error: ");
strncat(gcodeReply, s, STRING_LENGTH);
strncat(gcodeReply, s, ARRAY_UPB(gcodeReply));
}
else
{
strncpy(gcodeReply, s, STRING_LENGTH);
strncpy(gcodeReply, s, ARRAY_UPB(gcodeReply));
}
gcodeReply[STRING_LENGTH] = 0; // array is dimensioned to STRING_LENGTH+1
gcodeReply[ARRAY_UPB(gcodeReply)] = 0;
}
++seq;
}
void Webserver::AppendReply(const char *s)
{
strncat(gcodeReply, s, STRING_LENGTH);
strncat(gcodeReply, s, ARRAY_UPB(gcodeReply));
}
// Get the actual amount of gcode buffer space we have

View file

@ -33,11 +33,13 @@ Licence: GPL
#define KO_START "rr_"
#define KO_FIRST 3
#define POST_LENGTH (1300) // max amount of POST data we can accept
const unsigned int postLength = 1400; // max amount of POST data we can accept
const unsigned int webInputLength = 1400; // max size of web interface requests and related stuff
const unsigned int gcodeBufLength = 2048; // size of our gcode ring buffer, ideally a power of 2
const unsigned int minReportedFreeBuf = 100; // the minimum free buffer we report if not zero
const unsigned int maxReportedFreeBuf = 900; // the max we own up to having free, to avoid overlong messages
const unsigned int maxReportedFreeBuf = 1024; // the max we own up to having free, to avoid overlong messages
const unsigned int jsopnReplyLength = 1200; // size of buffer used to hold JSON reply
class Webserver
{
@ -62,7 +64,7 @@ class Webserver
void SendFile(const char* nameOfFileToSend);
void ParseQualifier();
void CheckPassword();
void LoadGcodeBuffer(const char* gc, bool convertWeb);
void LoadGcodeBuffer(const char* gc);
bool PrintHeadString();
bool PrintLinkTable();
void GetGCodeList();
@ -86,18 +88,18 @@ class Webserver
float lastTime;
float longWait;
bool receivingPost;
char postBoundary[POST_LENGTH];
char postBoundary[postLength];
int boundaryCount;
char postFileName[POST_LENGTH];
char postFileName[postLength];
FileStore* postFile;
bool postSeen;
bool getSeen;
bool clientLineIsBlank;
char clientLine[STRING_LENGTH+2]; // 2 chars extra so we can append \n\0
char clientRequest[STRING_LENGTH];
char clientQualifier[STRING_LENGTH];
char jsonResponse[STRING_LENGTH+1];
char clientLine[webInputLength];
char clientRequest[webInputLength];
char clientQualifier[webInputLength];
char jsonResponse[jsopnReplyLength];
char gcodeBuffer[gcodeBufLength];
unsigned int gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer
int clientLinePointer;