2015-10-16 11:04:50 +02:00
/*
This file is part of Repetier - Firmware .
Repetier - Firmware is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Repetier - Firmware is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Repetier - Firmware . If not , see < http : //www.gnu.org/licenses/>.
This firmware is a nearly complete rewrite of the sprinter firmware
by kliment ( https : //github.com/kliment/Sprinter)
which based on Tonokip RepRap firmware rewrite based off of Hydra - mmm firmware .
Functions in this file are used to communicate using ascii or repetier protocol .
*/
# include "Repetier.h"
# ifndef FEATURE_CHECKSUM_FORCED
# define FEATURE_CHECKSUM_FORCED false
# endif
GCode GCode : : commandsBuffered [ GCODE_BUFFER_SIZE ] ; ///< Buffer for received commands.
uint8_t GCode : : bufferReadIndex = 0 ; ///< Read position in gcode_buffer.
uint8_t GCode : : bufferWriteIndex = 0 ; ///< Write position in gcode_buffer.
uint8_t GCode : : commandReceiving [ MAX_CMD_SIZE ] ; ///< Current received command.
uint8_t GCode : : commandsReceivingWritePosition = 0 ; ///< Writing position in gcode_transbuffer.
uint8_t GCode : : sendAsBinary ; ///< Flags the command as binary input.
uint8_t GCode : : wasLastCommandReceivedAsBinary = 0 ; ///< Was the last successful command in binary mode?
uint8_t GCode : : commentDetected = false ; ///< Flags true if we are reading the comment part of a command.
uint8_t GCode : : binaryCommandSize ; ///< Expected size of the incoming binary command.
bool GCode : : waitUntilAllCommandsAreParsed = false ; ///< Don't read until all commands are parsed. Needed if gcode_buffer is misused as storage for strings.
uint32_t GCode : : lastLineNumber = 0 ; ///< Last line number received.
uint32_t GCode : : actLineNumber ; ///< Line number of current command.
int8_t GCode : : waitingForResend = - 1 ; ///< Waiting for line to be resend. -1 = no wait.
volatile uint8_t GCode : : bufferLength = 0 ; ///< Number of commands stored in gcode_buffer
millis_t GCode : : timeOfLastDataPacket = 0 ; ///< Time, when we got the last data packet. Used to detect missing uint8_ts.
uint8_t GCode : : formatErrors = 0 ;
2016-07-02 18:11:43 +02:00
PGM_P GCode : : fatalErrorMsg = NULL ; ///< message unset = no fatal error
2015-10-16 11:04:50 +02:00
/** \page Repetier-protocol
\ section Introduction
The repetier - protocol was developed , to overcome some shortcommings in the standard
RepRap communication method , while remaining backward compatible . To use the improved
features of this protocal , you need a host which speaks it . On Windows the recommended
host software is Repetier - Host . It is developed in parallel to this firmware and supports
all implemented features .
\ subsection Improvements
- With higher speeds , the serial connection is more likely to produce communication failures .
The standard method is to transfer a checksum at the end of the line . This checksum is the
XORd value of all characters send . The value is limited to a range between 0 and 127. It can
not detect two identical missing characters or a wrong order . Therefore the new protocol
uses Fletchers checksum , which overcomes these shortcommings .
- The new protocol send data in binary format . This reduces the data size to less then 50 % and
it speeds up decoding the command . No slow conversion from string to floats are needed .
*/
/** \brief Computes size of binary data from bitfield.
In the repetier - protocol in binary mode , the first 2 uint8_ts define the
data . From this bitfield , this function computes the size of the command
including the 2 uint8_ts of the bitfield and the 2 uint8_ts for the checksum .
Gcode Letter to Bit and Datatype :
- N : Bit 0 : 16 - Bit Integer
- M : Bit 1 : 8 - Bit unsigned uint8_t
- G : Bit 2 : 8 - Bit unsigned uint8_t
- X : Bit 3 : 32 - Bit Float
- Y : Bit 4 : 32 - Bit Float
- Z : Bit 5 : 32 - Bit Float
- E : Bit 6 : 32 - Bit Float
- : Bit 7 : always set to distinguish binary from ASCII line .
- F : Bit 8 : 32 - Bit Float
- T : Bit 9 : 8 Bit Integer
- S : Bit 10 : 32 Bit Value
- P : Bit 11 : 32 Bit Integer
- V2 : Bit 12 : Version 2 command for additional commands / sizes
- Ext : Bit 13 : There are 2 more uint8_ts following with Bits , only for future versions
- Int : Bit 14 : Marks it as internal command ,
- Text : Bit 15 : 16 Byte ASCII String terminated with 0
Second word if V2 :
- I : Bit 0 : 32 - Bit float
- J : Bit 1 : 32 - Bit float
- R : Bit 2 : 32 - Bit float
- D : Bit 3 : 32 - Bit float
- C : Bit 4 : 32 - Bit float
- H : Bit 5 : 32 - Bit float
- A : Bit 6 : 32 - Bit float
- B : Bit 7 : 32 - Bit float
- K : Bit 8 : 32 - Bit float
- L : Bit 9 : 32 - Bit float
- O : Bit 0 : 32 - Bit float
*/
uint8_t GCode : : computeBinarySize ( char * ptr ) // unsigned int bitfield) {
{
uint8_t s = 4 ; // include checksum and bitfield
uint16_t bitfield = * ( uint16_t * ) ptr ;
if ( bitfield & 1 ) s + = 2 ;
if ( bitfield & 8 ) s + = 4 ;
if ( bitfield & 16 ) s + = 4 ;
if ( bitfield & 32 ) s + = 4 ;
if ( bitfield & 64 ) s + = 4 ;
if ( bitfield & 256 ) s + = 4 ;
if ( bitfield & 512 ) s + = 1 ;
if ( bitfield & 1024 ) s + = 4 ;
if ( bitfield & 2048 ) s + = 4 ;
if ( bitfield & 4096 ) // Version 2 or later
{
s + = 2 ; // for bitfield 2
uint16_t bitfield2 = * ( uint16_t * ) ( ptr + 2 ) ;
if ( bitfield & 2 ) s + = 2 ;
if ( bitfield & 4 ) s + = 2 ;
if ( bitfield2 & 1 ) s + = 4 ;
if ( bitfield2 & 2 ) s + = 4 ;
if ( bitfield2 & 4 ) s + = 4 ;
if ( bitfield2 & 8 ) s + = 4 ;
if ( bitfield2 & 16 ) s + = 4 ;
if ( bitfield2 & 32 ) s + = 4 ;
if ( bitfield2 & 64 ) s + = 4 ;
if ( bitfield2 & 128 ) s + = 4 ;
if ( bitfield2 & 256 ) s + = 4 ;
if ( bitfield2 & 512 ) s + = 4 ;
if ( bitfield2 & 1024 ) s + = 4 ;
if ( bitfield2 & 2048 ) s + = 4 ;
if ( bitfield2 & 4096 ) s + = 4 ;
if ( bitfield2 & 8192 ) s + = 4 ;
if ( bitfield2 & 16384 ) s + = 4 ;
if ( bitfield2 & 32768 ) s + = 4 ;
if ( bitfield & 32768 ) s + = RMath : : min ( 80 , ( uint8_t ) ptr [ 4 ] + 1 ) ;
}
else
{
if ( bitfield & 2 ) s + = 1 ;
if ( bitfield & 4 ) s + = 1 ;
if ( bitfield & 32768 ) s + = 16 ;
}
return s ;
}
void GCode : : requestResend ( )
{
HAL : : serialFlush ( ) ;
commandsReceivingWritePosition = 0 ;
if ( sendAsBinary )
waitingForResend = 30 ;
else
waitingForResend = 14 ;
Com : : println ( ) ;
Com : : printFLN ( Com : : tResend , lastLineNumber + 1 ) ;
Com : : printFLN ( Com : : tOk ) ;
}
/**
Check if result is plausible . If it is , an ok is send and the command is stored in queue .
If not , a resend and ok is send .
*/
void GCode : : checkAndPushCommand ( )
{
if ( hasM ( ) )
{
if ( M = = 110 ) // Reset line number
{
lastLineNumber = actLineNumber ;
Com : : printFLN ( Com : : tOk ) ;
waitingForResend = - 1 ;
return ;
}
if ( M = = 112 ) // Emergency kill - freeze printer
{
Commands : : emergencyStop ( ) ;
}
# ifdef DEBUG_COM_ERRORS
if ( M = = 666 ) // force an communication error
2016-07-02 18:11:43 +02:00
{
2015-10-16 11:04:50 +02:00
lastLineNumber + + ;
return ;
2016-07-02 18:11:43 +02:00
} else if ( M = = 668 ) {
lastLineNumber = 0 ; // simulate a reset so lines are out of resend buffer
2015-10-16 11:04:50 +02:00
}
# endif // DEBUG_COM_ERRORS
}
if ( hasN ( ) )
{
if ( ( ( ( lastLineNumber + 1 ) & 0xffff ) ! = ( actLineNumber & 0xffff ) ) )
{
if ( static_cast < uint16_t > ( lastLineNumber - actLineNumber ) < 40 )
{
// we have seen that line already. So we assume it is a repeated resend and we ignore it
commandsReceivingWritePosition = 0 ;
Com : : printFLN ( Com : : tSkip , actLineNumber ) ;
Com : : printFLN ( Com : : tOk ) ;
}
else if ( waitingForResend < 0 ) // after a resend, we have to skip the garbage in buffers, no message for this
{
if ( Printer : : debugErrors ( ) )
{
Com : : printF ( Com : : tExpectedLine , lastLineNumber + 1 ) ;
Com : : printFLN ( Com : : tGot , actLineNumber ) ;
}
requestResend ( ) ; // Line missing, force resend
}
else
{
- - waitingForResend ;
commandsReceivingWritePosition = 0 ;
Com : : printFLN ( Com : : tSkip , actLineNumber ) ;
Com : : printFLN ( Com : : tOk ) ;
}
return ;
}
lastLineNumber = actLineNumber ;
2016-07-02 18:11:43 +02:00
} else if ( lastLineNumber & & ! ( hasM ( ) & & M = = 117 ) ) { // once line number always line number!
if ( Printer : : debugErrors ( ) )
{
Com : : printErrorFLN ( PSTR ( " Missing linenumber " ) ) ;
}
requestResend ( ) ;
return ;
}
if ( GCode : : hasFatalError ( ) & & ! ( hasM ( ) & & M = = 999 ) ) {
GCode : : reportFatalError ( ) ;
} else {
pushCommand ( ) ;
}
2015-10-16 11:04:50 +02:00
# ifdef DEBUG_COM_ERRORS
2016-07-02 18:11:43 +02:00
if ( hasM ( ) & & M = = 667 )
return ; // omit ok
2015-10-16 11:04:50 +02:00
# endif
# if ACK_WITH_LINENUMBER
Com : : printFLN ( Com : : tOkSpace , actLineNumber ) ;
# else
Com : : printFLN ( Com : : tOk ) ;
# endif
wasLastCommandReceivedAsBinary = sendAsBinary ;
waitingForResend = - 1 ; // everything is ok.
}
void GCode : : pushCommand ( )
{
# if !ECHO_ON_EXECUTE
commandsBuffered [ bufferWriteIndex ] . echoCommand ( ) ;
# endif
if ( + + bufferWriteIndex > = GCODE_BUFFER_SIZE ) bufferWriteIndex = 0 ;
bufferLength + + ;
}
/**
Get the next buffered command . Returns 0 if no more commands are buffered . For each
returned command , the gcode_command_finished ( ) function must be called .
*/
GCode * GCode : : peekCurrentCommand ( )
{
if ( bufferLength = = 0 ) return NULL ; // No more data
return & commandsBuffered [ bufferReadIndex ] ;
}
/** \brief Removes the last returned command from cache. */
void GCode : : popCurrentCommand ( )
{
if ( ! bufferLength ) return ; // Should not happen, but safety first
# if ECHO_ON_EXECUTE
echoCommand ( ) ;
# endif
if ( + + bufferReadIndex = = GCODE_BUFFER_SIZE ) bufferReadIndex = 0 ;
bufferLength - - ;
}
void GCode : : echoCommand ( )
{
if ( Printer : : debugEcho ( ) )
{
Com : : printF ( Com : : tEcho ) ;
printCommand ( ) ;
}
}
void GCode : : debugCommandBuffer ( )
{
Com : : printF ( PSTR ( " CommandBuffer " ) ) ;
for ( int i = 0 ; i < commandsReceivingWritePosition ; i + + )
Com : : printF ( Com : : tColon , ( int ) commandReceiving [ i ] ) ;
Com : : println ( ) ;
Com : : printFLN ( PSTR ( " Binary: " ) , ( int ) sendAsBinary ) ;
if ( ! sendAsBinary )
{
Com : : print ( ( char * ) commandReceiving ) ;
Com : : println ( ) ;
}
}
/** \brief Execute commands in progmem stored string. Multiple commands are seperated by \n */
void GCode : : executeFString ( FSTRINGPARAM ( cmd ) )
{
char buf [ 80 ] ;
uint8_t buflen ;
char c ;
GCode code ;
do
{
// Wait for a free place in command buffer
// Scan next command from string
uint8_t comment = 0 ;
buflen = 0 ;
do
{
c = HAL : : readFlashByte ( cmd + + ) ;
if ( c = = 0 | | c = = ' \n ' ) break ;
if ( c = = ' ; ' ) comment = 1 ;
if ( comment ) continue ;
buf [ buflen + + ] = c ;
}
while ( buflen < 79 ) ;
if ( buflen = = 0 ) // empty line ignore
continue ;
buf [ buflen ] = 0 ;
// Send command into command buffer
if ( code . parseAscii ( ( char * ) buf , false ) & & ( code . params & 518 ) ) // Success
{
# ifdef DEBUG_PRINT
debugWaitLoop = 7 ;
# endif
Commands : : executeGCode ( & code ) ;
Printer : : defaultLoopActions ( ) ;
}
}
while ( c ) ;
}
/** \brief Read from serial console or sdcard.
This function is the main function to read the commands from serial console or from sdcard .
It must be called frequently to empty the incoming buffer .
*/
void GCode : : readFromSerial ( )
{
if ( bufferLength > = GCODE_BUFFER_SIZE ) return ; // all buffers full
if ( waitUntilAllCommandsAreParsed & & bufferLength ) return ;
waitUntilAllCommandsAreParsed = false ;
millis_t time = HAL : : timeInMilliseconds ( ) ;
if ( ! HAL : : serialByteAvailable ( ) )
{
if ( ( waitingForResend > = 0 | | commandsReceivingWritePosition > 0 ) & & time - timeOfLastDataPacket > 200 )
2016-07-02 18:11:43 +02:00
{
2015-10-16 11:04:50 +02:00
// Com::printF(PSTR("WFR:"),waitingForResend);Com::printF(PSTR(" CRWP:"),commandsReceivingWritePosition);commandReceiving[commandsReceivingWritePosition] = 0;Com::printFLN(PSTR(" GOT:"),(char*)commandReceiving);
requestResend ( ) ; // Something is wrong, a started line was not continued in the last second
timeOfLastDataPacket = time ;
}
# ifdef WAITING_IDENTIFIER
else if ( bufferLength = = 0 & & time - timeOfLastDataPacket > 1000 ) // Don't do it if buffer is not empty. It may be a slow executing command.
{
Com : : printFLN ( Com : : tWait ) ; // Unblock communication in case the last ok was not received correct.
timeOfLastDataPacket = time ;
}
# endif
}
while ( HAL : : serialByteAvailable ( ) & & commandsReceivingWritePosition < MAX_CMD_SIZE ) // consume data until no data or buffer full
{
timeOfLastDataPacket = time ; //HAL::timeInMilliseconds();
commandReceiving [ commandsReceivingWritePosition + + ] = HAL : : serialReadByte ( ) ;
// first lets detect, if we got an old type ascii command
if ( commandsReceivingWritePosition = = 1 )
{
if ( waitingForResend > = 0 & & wasLastCommandReceivedAsBinary )
{
if ( ! commandReceiving [ 0 ] )
waitingForResend - - ; // Skip 30 zeros to get in sync
else
waitingForResend = 30 ;
commandsReceivingWritePosition = 0 ;
continue ;
}
if ( ! commandReceiving [ 0 ] ) // Ignore zeros
{
commandsReceivingWritePosition = 0 ;
continue ;
}
sendAsBinary = ( commandReceiving [ 0 ] & 128 ) ! = 0 ;
}
if ( sendAsBinary )
{
if ( commandsReceivingWritePosition < 2 ) continue ;
if ( commandsReceivingWritePosition = = 5 | | commandsReceivingWritePosition = = 4 )
binaryCommandSize = computeBinarySize ( ( char * ) commandReceiving ) ;
if ( commandsReceivingWritePosition = = binaryCommandSize )
{
GCode * act = & commandsBuffered [ bufferWriteIndex ] ;
if ( act - > parseBinary ( commandReceiving , true ) ) // Success
act - > checkAndPushCommand ( ) ;
else
requestResend ( ) ;
commandsReceivingWritePosition = 0 ;
return ;
}
}
else // Ascii command
{
char ch = commandReceiving [ commandsReceivingWritePosition - 1 ] ;
if ( ch = = 0 | | ch = = ' \n ' | | ch = = ' \r ' | | ( ! commentDetected & & ch = = ' : ' ) ) // complete line read
{
commandReceiving [ commandsReceivingWritePosition - 1 ] = 0 ;
//Com::printF(PSTR("Parse ascii:"));Com::print((char*)commandReceiving);Com::println();
commentDetected = false ;
if ( commandsReceivingWritePosition = = 1 ) // empty line ignore
{
commandsReceivingWritePosition = 0 ;
continue ;
}
GCode * act = & commandsBuffered [ bufferWriteIndex ] ;
if ( act - > parseAscii ( ( char * ) commandReceiving , true ) ) // Success
act - > checkAndPushCommand ( ) ;
else
requestResend ( ) ;
commandsReceivingWritePosition = 0 ;
return ;
}
else
{
2016-07-02 18:11:43 +02:00
if ( ch = = ' ; ' ) commentDetected = true ; // ignore new data until line end
2015-10-16 11:04:50 +02:00
if ( commentDetected ) commandsReceivingWritePosition - - ;
}
}
if ( commandsReceivingWritePosition = = MAX_CMD_SIZE )
{
requestResend ( ) ;
return ;
}
}
# if SDSUPPORT
2016-01-15 20:42:10 +01:00
if ( sd . sdmode = = 0 | | sd . sdmode > = 100 | | commandsReceivingWritePosition ! = 0 ) // not reading or incoming serial command
2015-10-16 11:04:50 +02:00
return ;
while ( sd . filesize > sd . sdpos & & commandsReceivingWritePosition < MAX_CMD_SIZE ) // consume data until no data or buffer full
{
timeOfLastDataPacket = HAL : : timeInMilliseconds ( ) ;
int n = sd . file . read ( ) ;
if ( n = = - 1 )
{
Com : : printFLN ( Com : : tSDReadError ) ;
UI_ERROR ( " SD Read Error " ) ;
// Second try in case of recoverable errors
sd . file . seekSet ( sd . sdpos ) ;
n = sd . file . read ( ) ;
if ( n = = - 1 )
{
Com : : printErrorFLN ( PSTR ( " SD error did not recover! " ) ) ;
sd . sdmode = 0 ;
break ;
}
UI_ERROR ( " SD error fixed " ) ;
}
sd . sdpos + + ; // = file.curPosition();
commandReceiving [ commandsReceivingWritePosition + + ] = ( uint8_t ) n ;
// first lets detect, if we got an old type ascii command
if ( commandsReceivingWritePosition = = 1 )
{
sendAsBinary = ( commandReceiving [ 0 ] & 128 ) ! = 0 ;
}
if ( sendAsBinary )
{
if ( commandsReceivingWritePosition < 2 ) continue ;
if ( commandsReceivingWritePosition = = 4 | | commandsReceivingWritePosition = = 5 )
binaryCommandSize = computeBinarySize ( ( char * ) commandReceiving ) ;
if ( commandsReceivingWritePosition = = binaryCommandSize )
{
GCode * act = & commandsBuffered [ bufferWriteIndex ] ;
if ( act - > parseBinary ( commandReceiving , false ) ) // Success, silently ignore illegal commands
pushCommand ( ) ;
commandsReceivingWritePosition = 0 ;
if ( sd . sdmode = = 2 )
sd . sdmode = 0 ;
return ;
}
}
else
{
char ch = commandReceiving [ commandsReceivingWritePosition - 1 ] ;
bool returnChar = ch = = ' \n ' | | ch = = ' \r ' ;
if ( returnChar | | sd . filesize = = sd . sdpos | | ( ! commentDetected & & ch = = ' : ' ) | | commandsReceivingWritePosition > = ( MAX_CMD_SIZE - 1 ) ) // complete line read
{
if ( returnChar | | ch = = ' : ' )
commandReceiving [ commandsReceivingWritePosition - 1 ] = 0 ;
else
commandReceiving [ commandsReceivingWritePosition ] = 0 ;
commentDetected = false ;
if ( commandsReceivingWritePosition = = 1 ) // empty line ignore
{
commandsReceivingWritePosition = 0 ;
continue ;
}
GCode * act = & commandsBuffered [ bufferWriteIndex ] ;
if ( act - > parseAscii ( ( char * ) commandReceiving , false ) ) // Success
pushCommand ( ) ;
commandsReceivingWritePosition = 0 ;
if ( sd . sdmode = = 2 )
sd . sdmode = 0 ;
return ;
}
else
{
2016-07-02 18:11:43 +02:00
if ( ch = = ' ; ' ) commentDetected = true ; // ignore new data until line end
2015-10-16 11:04:50 +02:00
if ( commentDetected ) commandsReceivingWritePosition - - ;
}
}
}
sd . sdmode = 0 ;
Com : : printFLN ( Com : : tDonePrinting ) ;
commandsReceivingWritePosition = 0 ;
commentDetected = false ;
Printer : : setMenuMode ( MENU_MODE_SD_PRINTING , false ) ;
# endif
}
/**
Converts a binary uint8_tfield containing one GCode line into a GCode structure .
Returns true if checksum was correct .
*/
bool GCode : : parseBinary ( uint8_t * buffer , bool fromSerial )
{
internalCommand = ! fromSerial ;
2016-01-15 20:42:10 +01:00
unsigned int sum1 = 0 , sum2 = 0 ; // for fletcher-16 checksum
2015-10-16 11:04:50 +02:00
// first do fletcher-16 checksum tests see
// http://en.wikipedia.org/wiki/Fletcher's_checksum
uint8_t * p = buffer ;
uint8_t len = binaryCommandSize - 2 ;
while ( len )
{
uint8_t tlen = len > 21 ? 21 : len ;
len - = tlen ;
do
{
sum1 + = * p + + ;
if ( sum1 > = 255 ) sum1 - = 255 ;
sum2 + = sum1 ;
if ( sum2 > = 255 ) sum2 - = 255 ;
}
while ( - - tlen ) ;
}
sum1 - = * p + + ;
sum2 - = * p ;
if ( sum1 | sum2 )
{
if ( Printer : : debugErrors ( ) )
{
Com : : printErrorFLN ( Com : : tWrongChecksum ) ;
}
return false ;
}
p = buffer ;
2016-01-15 20:42:10 +01:00
params = * ( uint16_t * ) p ;
2015-10-16 11:04:50 +02:00
p + = 2 ;
uint8_t textlen = 16 ;
if ( isV2 ( ) )
{
2016-01-15 20:42:10 +01:00
params2 = * ( uint16_t * ) p ;
p + = 2 ;
2015-10-16 11:04:50 +02:00
if ( hasString ( ) )
textlen = * p + + ;
}
else params2 = 0 ;
if ( params & 1 )
{
actLineNumber = N = * ( uint16_t * ) p ;
2016-01-15 20:42:10 +01:00
p + = 2 ;
2015-10-16 11:04:50 +02:00
}
if ( isV2 ( ) ) // Read G,M as 16 bit value
{
2016-01-15 20:42:10 +01:00
if ( hasM ( ) )
2015-10-16 11:04:50 +02:00
{
M = * ( uint16_t * ) p ;
p + = 2 ;
}
2016-01-15 20:42:10 +01:00
if ( hasG ( ) )
2015-10-16 11:04:50 +02:00
{
G = * ( uint16_t * ) p ;
p + = 2 ;
}
}
else
{
2016-01-15 20:42:10 +01:00
if ( hasM ( ) )
2015-10-16 11:04:50 +02:00
{
M = * p + + ;
}
2016-01-15 20:42:10 +01:00
if ( hasG ( ) )
2015-10-16 11:04:50 +02:00
{
G = * p + + ;
}
}
//if(code->params & 8) {memcpy(&code->X,p,4);p+=4;}
2016-01-15 20:42:10 +01:00
if ( hasX ( ) )
2015-10-16 11:04:50 +02:00
{
X = * ( float * ) p ;
p + = 4 ;
}
2016-01-15 20:42:10 +01:00
if ( hasY ( ) )
2015-10-16 11:04:50 +02:00
{
Y = * ( float * ) p ;
p + = 4 ;
}
2016-01-15 20:42:10 +01:00
if ( hasZ ( ) )
2015-10-16 11:04:50 +02:00
{
Z = * ( float * ) p ;
p + = 4 ;
}
2016-01-15 20:42:10 +01:00
if ( hasE ( ) )
2015-10-16 11:04:50 +02:00
{
E = * ( float * ) p ;
p + = 4 ;
}
2016-01-15 20:42:10 +01:00
if ( hasF ( ) )
2015-10-16 11:04:50 +02:00
{
F = * ( float * ) p ;
p + = 4 ;
}
2016-01-15 20:42:10 +01:00
if ( hasT ( ) )
2015-10-16 11:04:50 +02:00
{
T = * p + + ;
}
2016-01-15 20:42:10 +01:00
if ( hasS ( ) )
2015-10-16 11:04:50 +02:00
{
S = * ( int32_t * ) p ;
p + = 4 ;
}
2016-01-15 20:42:10 +01:00
if ( hasP ( ) )
2015-10-16 11:04:50 +02:00
{
P = * ( int32_t * ) p ;
p + = 4 ;
}
if ( hasI ( ) )
{
I = * ( float * ) p ;
p + = 4 ;
}
if ( hasJ ( ) )
{
J = * ( float * ) p ;
p + = 4 ;
}
if ( hasR ( ) )
{
R = * ( float * ) p ;
p + = 4 ;
}
if ( hasD ( ) )
{
D = * ( float * ) p ;
p + = 4 ;
}
if ( hasC ( ) )
{
C = * ( float * ) p ;
p + = 4 ;
}
if ( hasH ( ) )
{
H = * ( float * ) p ;
p + = 4 ;
}
if ( hasA ( ) )
{
A = * ( float * ) p ;
p + = 4 ;
}
if ( hasB ( ) )
{
B = * ( float * ) p ;
p + = 4 ;
}
if ( hasK ( ) )
{
K = * ( float * ) p ;
p + = 4 ;
}
if ( hasL ( ) )
{
L = * ( float * ) p ;
p + = 4 ;
}
if ( hasO ( ) )
{
O = * ( float * ) p ;
p + = 4 ;
}
if ( hasString ( ) ) // set text pointer to string
{
text = ( char * ) p ;
text [ textlen ] = 0 ; // Terminate string overwriting checksum
2016-01-15 20:42:10 +01:00
waitUntilAllCommandsAreParsed = true ; // Don't destroy string until executed
2015-10-16 11:04:50 +02:00
}
formatErrors = 0 ;
return true ;
}
/**
Converts a ascii GCode line into a GCode structure .
*/
bool GCode : : parseAscii ( char * line , bool fromSerial )
{
char * pos = line ;
params = 0 ;
params2 = 0 ;
internalCommand = ! fromSerial ;
char c ;
while ( ( c = * ( pos + + ) ) )
2016-07-02 18:11:43 +02:00
{
2016-01-15 20:42:10 +01:00
if ( c = = ' ( ' | | c = = ' % ' ) break ; // alternative comment or program block
2015-10-16 11:04:50 +02:00
switch ( c )
{
case ' N ' :
case ' n ' :
{
actLineNumber = parseLongValue ( pos ) ;
params | = 1 ;
N = actLineNumber ;
break ;
}
case ' G ' :
case ' g ' :
{
G = parseLongValue ( pos ) & 0xffff ;
params | = 4 ;
if ( G > 255 ) params | = 4096 ;
break ;
}
case ' M ' :
case ' m ' :
{
M = parseLongValue ( pos ) & 0xffff ;
2016-01-15 20:42:10 +01:00
params | = 2 ;
2015-10-16 11:04:50 +02:00
if ( M > 255 ) params | = 4096 ;
// handle non standard text arguments that some M codes have
2016-01-15 20:42:10 +01:00
if ( M = = 20 | | M = = 23 | | M = = 28 | | M = = 29 | | M = = 30 | | M = = 32 | | M = = 36 | | M = = 117 )
2015-10-16 11:04:50 +02:00
{
// after M command we got a filename or text
char digit ;
while ( ( digit = * pos ) )
{
if ( digit < ' 0 ' | | digit > ' 9 ' ) break ;
pos + + ;
}
while ( ( digit = * pos ) )
{
if ( digit ! = ' ' ) break ;
pos + + ;
// skip leading whitespaces (may be no white space)
}
text = pos ;
while ( * pos )
{
2016-01-15 20:42:10 +01:00
if ( ( M ! = 117 & & M ! = 20 & & * pos = = ' ' ) | | * pos = = ' * ' ) break ;
2015-10-16 11:04:50 +02:00
pos + + ; // find a space as file name end
}
* pos = 0 ; // truncate filename by erasing space with nul, also skips checksum
waitUntilAllCommandsAreParsed = true ; // don't risk string be deleted
params | = 32768 ;
}
break ;
}
case ' X ' :
case ' x ' :
{
X = parseFloatValue ( pos ) ;
params | = 8 ;
break ;
}
case ' Y ' :
case ' y ' :
{
Y = parseFloatValue ( pos ) ;
params | = 16 ;
break ;
}
case ' Z ' :
case ' z ' :
{
Z = parseFloatValue ( pos ) ;
params | = 32 ;
break ;
}
case ' E ' :
case ' e ' :
{
E = parseFloatValue ( pos ) ;
params | = 64 ;
break ;
}
case ' F ' :
case ' f ' :
{
F = parseFloatValue ( pos ) ;
params | = 256 ;
break ;
}
case ' T ' :
case ' t ' :
{
T = parseLongValue ( pos ) & 0xff ;
params | = 512 ;
break ;
}
case ' S ' :
case ' s ' :
{
S = parseLongValue ( pos ) ;
params | = 1024 ;
break ;
}
case ' P ' :
case ' p ' :
{
P = parseLongValue ( pos ) ;
params | = 2048 ;
break ;
}
case ' I ' :
case ' i ' :
{
I = parseFloatValue ( pos ) ;
params2 | = 1 ;
params | = 4096 ; // Needs V2 for saving
break ;
}
case ' J ' :
case ' j ' :
{
J = parseFloatValue ( pos ) ;
params2 | = 2 ;
params | = 4096 ; // Needs V2 for saving
break ;
}
case ' R ' :
case ' r ' :
{
R = parseFloatValue ( pos ) ;
params2 | = 4 ;
params | = 4096 ; // Needs V2 for saving
break ;
}
case ' D ' :
case ' d ' :
{
D = parseFloatValue ( pos ) ;
params2 | = 8 ;
params | = 4096 ; // Needs V2 for saving
break ;
2016-07-02 18:11:43 +02:00
}
2016-01-15 20:42:10 +01:00
case ' C ' :
case ' c ' :
{
D = parseFloatValue ( pos ) ;
params2 | = 16 ;
params | = 4096 ; // Needs V2 for saving
break ;
2016-07-02 18:11:43 +02:00
}
2016-01-15 20:42:10 +01:00
case ' H ' :
case ' h ' :
{
D = parseFloatValue ( pos ) ;
params2 | = 32 ;
params | = 4096 ; // Needs V2 for saving
break ;
2016-07-02 18:11:43 +02:00
}
2016-01-15 20:42:10 +01:00
case ' A ' :
case ' a ' :
{
D = parseFloatValue ( pos ) ;
params2 | = 64 ;
params | = 4096 ; // Needs V2 for saving
break ;
2016-07-02 18:11:43 +02:00
}
2016-01-15 20:42:10 +01:00
case ' B ' :
case ' b ' :
{
D = parseFloatValue ( pos ) ;
params2 | = 128 ;
params | = 4096 ; // Needs V2 for saving
break ;
2016-07-02 18:11:43 +02:00
}
2016-01-15 20:42:10 +01:00
case ' K ' :
case ' k ' :
{
D = parseFloatValue ( pos ) ;
params2 | = 256 ;
params | = 4096 ; // Needs V2 for saving
break ;
2016-07-02 18:11:43 +02:00
}
2016-01-15 20:42:10 +01:00
case ' L ' :
case ' l ' :
{
D = parseFloatValue ( pos ) ;
params2 | = 512 ;
params | = 4096 ; // Needs V2 for saving
break ;
2016-07-02 18:11:43 +02:00
}
2016-01-15 20:42:10 +01:00
case ' O ' :
case ' o ' :
{
D = parseFloatValue ( pos ) ;
params2 | = 1024 ;
params | = 4096 ; // Needs V2 for saving
break ;
2016-07-02 18:11:43 +02:00
}
2015-10-16 11:04:50 +02:00
case ' * ' : //checksum
{
uint8_t checksum_given = parseLongValue ( pos ) ;
uint8_t checksum = 0 ;
2016-01-15 20:42:10 +01:00
while ( line ! = ( pos - 1 ) ) checksum ^ = * line + + ;
2015-10-16 11:04:50 +02:00
# if FEATURE_CHECKSUM_FORCED
Printer : : flag0 | = PRINTER_FLAG0_FORCE_CHECKSUM ;
# endif
if ( checksum ! = checksum_given )
{
if ( Printer : : debugErrors ( ) )
{
Com : : printErrorFLN ( Com : : tWrongChecksum ) ;
}
return false ; // mismatch
}
break ;
}
default :
break ;
} // end switch
} // end while
2016-01-15 20:42:10 +01:00
if ( hasFormatError ( ) | | ( params & 518 ) = = 0 ) // Must contain G, M or T command and parameter need to have variables!
2015-10-16 11:04:50 +02:00
{
formatErrors + + ;
if ( Printer : : debugErrors ( ) )
Com : : printErrorFLN ( Com : : tFormatError ) ;
2016-01-15 20:42:10 +01:00
if ( formatErrors < 3 ) return false ;
2015-10-16 11:04:50 +02:00
}
else formatErrors = 0 ;
return true ;
}
/** \brief Print command on serial console */
void GCode : : printCommand ( )
{
if ( hasN ( ) ) {
Com : : print ( ' N ' ) ;
2016-01-15 20:42:10 +01:00
Com : : print ( ( int32_t ) N ) ;
2015-10-16 11:04:50 +02:00
Com : : print ( ' ' ) ;
}
if ( hasM ( ) )
{
Com : : print ( ' M ' ) ;
Com : : print ( ( int ) M ) ;
Com : : print ( ' ' ) ;
}
if ( hasG ( ) )
{
Com : : print ( ' G ' ) ;
Com : : print ( ( int ) G ) ;
Com : : print ( ' ' ) ;
}
if ( hasT ( ) )
{
Com : : print ( ' T ' ) ;
Com : : print ( ( int ) T ) ;
Com : : print ( ' ' ) ;
}
if ( hasX ( ) )
{
Com : : printF ( Com : : tX , X ) ;
}
if ( hasY ( ) )
{
Com : : printF ( Com : : tY , Y ) ;
}
if ( hasZ ( ) )
{
Com : : printF ( Com : : tZ , Z ) ;
}
if ( hasE ( ) )
{
Com : : printF ( Com : : tE , E , 4 ) ;
}
if ( hasF ( ) )
{
Com : : printF ( Com : : tF , F ) ;
}
if ( hasS ( ) )
{
Com : : printF ( Com : : tS , S ) ;
}
if ( hasP ( ) )
{
Com : : printF ( Com : : tP , P ) ;
}
if ( hasI ( ) )
{
Com : : printF ( Com : : tI , I ) ;
}
if ( hasJ ( ) )
{
Com : : printF ( Com : : tJ , J ) ;
}
if ( hasR ( ) )
{
Com : : printF ( Com : : tR , R ) ;
}
if ( hasString ( ) )
{
Com : : print ( text ) ;
}
Com : : println ( ) ;
}
2016-07-02 18:11:43 +02:00
void GCode : : fatalError ( FSTRINGPARAM ( message ) ) {
fatalErrorMsg = message ;
# if SDSUPPORT
if ( sd . sdmode ! = 0 ) { // stop sd print to prevent damage
sd . stopPrint ( ) ;
}
# endif
if ( Printer : : currentPosition [ Z_AXIS ] < Printer : : zMin + Printer : : zLength - 15 )
PrintLine : : moveRelativeDistanceInStepsReal ( 0 , 0 , 10 * Printer : : axisStepsPerMM [ Z_AXIS ] , 0 , Printer : : homingFeedrate [ Z_AXIS ] , true , true ) ;
EVENT_FATAL_ERROR_OCCURED
Commands : : waitUntilEndOfAllMoves ( ) ;
Printer : : kill ( true ) ;
reportFatalError ( ) ;
}
void GCode : : reportFatalError ( ) {
Com : : printF ( Com : : tFatal ) ;
Com : : printF ( fatalErrorMsg ) ;
Com : : printFLN ( PSTR ( " Printer stopped and heaters disabled due to this error. Fix error and restart with M999. " ) ) ;
UI_ERROR_P ( fatalErrorMsg )
}
void GCode : : resetFatalError ( ) {
TemperatureController : : resetAllErrorStates ( ) ;
fatalErrorMsg = NULL ;
UI_ERROR ( " " ) ;
EVENT_CONTINUE_FROM_FATAL_ERROR
Com : : printFLN ( PSTR ( " info:Continue from fatal state " ) ) ;
}
# if JSON_OUTPUT
// --------------------------------------------------------------- //
// Code that gets gcode information is adapted from RepRapFirmware //
// Originally licensed under GPL //
// Authors: reprappro, dc42, dcnewman, others //
// Source: https://github.com/dcnewman/RepRapFirmware //
// Copy date: 15 Nov 2015 //
// --------------------------------------------------------------- //
void GCodeFileInfo : : init ( SdBaseFile & file ) {
this - > fileSize = file . fileSize ( ) ;
this - > filamentNeeded = 0.0 ;
this - > objectHeight = 0.0 ;
this - > layerHeight = 0.0 ;
if ( ! file . isOpen ( ) ) return ;
bool genByFound = false , layerHeightFound = false , filamentNeedFound = false ;
# if CPU_ARCH==ARCH_AVR
# define GCI_BUF_SIZE 120
# else
# define GCI_BUF_SIZE 1024
# endif
// READ 4KB FROM THE BEGINNING
char buf [ GCI_BUF_SIZE ] ;
for ( int i = 0 ; i < 4096 ; i + = GCI_BUF_SIZE - 50 ) {
if ( ! file . seekSet ( i ) ) break ;
file . read ( buf , GCI_BUF_SIZE ) ;
if ( ! genByFound & & findGeneratedBy ( buf , this - > generatedBy ) ) genByFound = true ;
if ( ! layerHeightFound & & findLayerHeight ( buf , this - > layerHeight ) ) layerHeightFound = true ;
if ( ! filamentNeedFound & & findFilamentNeed ( buf , this - > filamentNeeded ) ) filamentNeedFound = true ;
if ( genByFound & & layerHeightFound & & filamentNeedFound ) goto get_objectHeight ;
}
// READ 4KB FROM END
for ( int i = 0 ; i < 4096 ; i + = GCI_BUF_SIZE - 50 ) {
if ( ! file . seekEnd ( - 4096 + i ) ) break ;
file . read ( buf , GCI_BUF_SIZE ) ;
if ( ! genByFound & & findGeneratedBy ( buf , this - > generatedBy ) ) genByFound = true ;
if ( ! layerHeightFound & & findLayerHeight ( buf , this - > layerHeight ) ) layerHeightFound = true ;
if ( ! filamentNeedFound & & findFilamentNeed ( buf , this - > filamentNeeded ) ) filamentNeedFound = true ;
if ( genByFound & & layerHeightFound & & filamentNeedFound ) goto get_objectHeight ;
}
get_objectHeight :
// MOVE FROM END UP IN 1KB BLOCKS UP TO 30KB
for ( int i = GCI_BUF_SIZE ; i < 30000 ; i + = GCI_BUF_SIZE - 50 ) {
if ( ! file . seekEnd ( - i ) ) break ;
file . read ( buf , GCI_BUF_SIZE ) ;
if ( findTotalHeight ( buf , this - > objectHeight ) ) break ;
}
file . seekSet ( 0 ) ;
}
bool GCodeFileInfo : : findGeneratedBy ( char * buf , char * genBy ) {
// Slic3r & S3D
const char * generatedByString = PSTR ( " generated by " ) ;
char * pos = strstr_P ( buf , generatedByString ) ;
if ( pos ) {
pos + = strlen_P ( generatedByString ) ;
size_t i = 0 ;
while ( i < GENBY_SIZE - 1 & & * pos > = ' ' ) {
char c = * pos + + ;
if ( c = = ' " ' | | c = = ' \\ ' ) {
// Need to escape the quote-mark for JSON
if ( i > GENBY_SIZE - 3 ) break ;
genBy [ i + + ] = ' \\ ' ;
}
genBy [ i + + ] = c ;
}
genBy [ i ] = 0 ;
return true ;
}
// CURA
const char * slicedAtString = PSTR ( " ;Sliced at: " ) ;
pos = strstr_P ( buf , slicedAtString ) ;
if ( pos ) {
strcpy_P ( genBy , PSTR ( " Cura " ) ) ;
return true ;
}
// UNKNOWN
strcpy_P ( genBy , PSTR ( " Unknown " ) ) ;
return false ;
}
bool GCodeFileInfo : : findLayerHeight ( char * buf , float & layerHeight ) {
// SLIC3R
layerHeight = 0 ;
const char * layerHeightSlic3r = PSTR ( " ; layer_height " ) ;
char * pos = strstr_P ( buf , layerHeightSlic3r ) ;
if ( pos ) {
pos + = strlen_P ( layerHeightSlic3r ) ;
while ( * pos = = ' ' | | * pos = = ' t ' | | * pos = = ' = ' | | * pos = = ' : ' ) {
+ + pos ;
}
layerHeight = strtod ( pos , NULL ) ;
return true ;
}
// CURA
const char * layerHeightCura = PSTR ( " Layer height: " ) ;
pos = strstr_P ( buf , layerHeightCura ) ;
if ( pos ) {
pos + = strlen_P ( layerHeightCura ) ;
while ( * pos = = ' ' | | * pos = = ' t ' | | * pos = = ' = ' | | * pos = = ' : ' ) {
+ + pos ;
}
layerHeight = strtod ( pos , NULL ) ;
return true ;
}
return false ;
}
bool GCodeFileInfo : : findFilamentNeed ( char * buf , float & filament ) {
const char * filamentUsedStr = PSTR ( " filament used " ) ;
const char * pos = strstr_P ( buf , filamentUsedStr ) ;
filament = 0 ;
if ( pos ! = NULL ) {
pos + = strlen_P ( filamentUsedStr ) ;
while ( * pos = = ' ' | | * pos = = ' t ' | | * pos = = ' = ' | | * pos = = ' : ' ) {
+ + pos ; // this allows for " = " from default slic3r comment and ": " from default Cura comment
}
if ( isDigit ( * pos ) ) {
char * q ;
filament + = strtod ( pos , & q ) ;
if ( * q = = ' m ' & & * ( q + 1 ) ! = ' m ' ) {
filament * = 1000.0 ; // Cura outputs filament used in metres not mm
}
}
return true ;
}
return false ;
}
bool GCodeFileInfo : : findTotalHeight ( char * buf , float & height ) {
int len = 1024 ;
bool inComment , inRelativeMode = false ;
unsigned int zPos ;
for ( int i = len - 5 ; i > 0 ; i - - ) {
if ( inRelativeMode ) {
inRelativeMode = ! ( buf [ i ] = = ' G ' & & buf [ i + 1 ] = = ' 9 ' & & buf [ i + 2 ] = = ' 1 ' & & buf [ i + 3 ] < = ' ' ) ;
} else if ( buf [ i ] = = ' G ' ) {
// Ignore G0/G1 codes if absolute mode was switched back using G90 (typical for Cura files)
if ( buf [ i + 1 ] = = ' 9 ' & & buf [ i + 2 ] = = ' 0 ' & & buf [ i + 3 ] < = ' ' ) {
inRelativeMode = true ;
} else if ( ( buf [ i + 1 ] = = ' 0 ' | | buf [ i + 1 ] = = ' 1 ' ) & & buf [ i + 2 ] = = ' ' ) {
// Look for last "G0/G1 ... Z#HEIGHT#" command as generated by common slicers
// Looks like we found a controlled move, however it could be in a comment, especially when using slic3r 1.1.1
inComment = false ;
size_t j = i ;
while ( j ! = 0 ) {
- - j ;
char c = buf [ j ] ;
if ( c = = ' \n ' | | c = = ' \r ' ) break ;
if ( c = = ' ; ' ) {
// It is in a comment, so give up on this one
inComment = true ;
break ;
}
}
if ( inComment ) continue ;
// Find 'Z' position and grab that value
zPos = 0 ;
for ( int j = i + 3 ; j < len - 2 ; j + + ) {
char c = buf [ j ] ;
if ( c < ' ' ) {
// Skip all whitespaces...
while ( j < len - 2 & & c < = ' ' ) {
c = buf [ + + j ] ;
}
// ...to make sure ";End" doesn't follow G0 .. Z#HEIGHT#
if ( zPos ! = 0 ) {
//debugPrintf("Found at offset %u text: %.100s\n", zPos, &buf[zPos + 1]);
height = strtod ( & buf [ zPos + 1 ] , NULL ) ;
return true ;
}
break ;
} else if ( c = = ' ; ' ) break ;
else if ( c = = ' Z ' ) zPos = j ;
}
}
}
}
return false ;
}
# endif // JSON_OUTPUT