ESP3D  3.0
Firmware for ESP boards connected to 3D Printer
gcode_host.cpp
Go to the documentation of this file.
1 /*
2  gcode_host.cpp - gcode host functions class
3 
4  Copyright (c) 2014 Luc Lebosse. All rights reserved.
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either
9  version 2.1 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
21 #include "../../include/esp3d_config.h"
22 #ifdef ESP_GCODE_HOST_FEATURE
23 #include "gcode_host.h"
24 #include "../../core/settings_esp3d.h"
25 #include "../../core/commands.h"
26 #include "../../core/esp3doutput.h"
27 #include "../serial/serial_service.h"
28 #include "../filesystem/esp_filesystem.h"
29 
31 
33 {
34  _commandnumber = 0;
35  _waitwhenidle = false;
36  _error = ERROR_NO_ERROR;
37 }
38 
40 {
41  end();
42 }
43 
44 bool GcodeHost::begin(bool waitwhenidle)
45 {
46  end();
47  _waitwhenidle = waitwhenidle;
48  return true;
49 }
50 
52 {
53  _commandnumber = 0;
54  _error = ERROR_NO_ERROR;
55 }
57 {
58 }
59 
60 uint8_t GcodeHost::Checksum(const char * command, uint32_t commandSize)
61 {
62  uint8_t checksum_val =0;
63  if (command == NULL) {
64  return 0;
65  }
66  for (uint32_t i=0; i < commandSize; i++) {
67  checksum_val = checksum_val ^ ((uint8_t)command[i]);
68  }
69  return checksum_val;
70 }
71 
72 String GcodeHost::CheckSumCommand(const char* command, uint32_t commandnb)
73 {
74  String commandchecksum = "N" + String((uint32_t)commandnb)+ " " + command;
75  uint8_t crc = Checksum(commandchecksum.c_str(), commandchecksum.length());
76  commandchecksum+="*"+String(crc);
77  return commandchecksum;
78 }
79 
80 size_t GcodeHost::wait_for_data(uint32_t timeout)
81 {
82  uint32_t start = millis();
83  while ((serial_service.available() < 2) && ((millis()-start) < timeout)) {
84  Hal::wait (0); //minimum delay is 10 actually
85  }
86  return serial_service.available();
87 }
88 
90 {
91  String resetcmd = "M110 N0";
93  resetcmd = "N0 M110";
94  } else {
95  resetcmd = "M110 N0";
96  }
97  _commandnumber = 1;
98  return sendCommand(resetcmd.c_str());
99 }
100 
101 /*bool GcodeHost::endUpload(){
102 
103  return true;
104 }*/
105 
106 bool GcodeHost::wait_for_ack(uint32_t timeout, bool checksum, const char * ack)
107 {
108  _needcommandnumber = _commandnumber;
109  uint32_t start = millis();
110  String answer = "";
111  while ((millis()-start) < timeout) {
112  size_t len = serial_service.available();
113  if (len > 0) {
114  uint8_t * sbuf = (uint8_t *)malloc(len+1);
115  if(!sbuf) {
116  _error = ERROR_MEMORY_PROBLEM;
117  return false;
118  }
119  sbuf[len] = '\0';
120  answer+= (const char *)sbuf;
121  free(sbuf);
122  log_esp3d("Answer: %s",answer.c_str());
123  //check for ack
124  if (ack!=nullptr) {
125  if (answer.indexOf(ack) != -1) {
126  _error = ERROR_NO_ERROR;
127  return true;
128  }
129  } else {
130  //wait is not an ack as it can appear any time
131  if (answer.indexOf("ok") != -1) {
132  if (!checksum) {
133  _error = ERROR_NO_ERROR;
134  return true;
135  } else {
136  //check number
137  String ackstring = "ok " + String(_commandnumber);
138  if (answer.indexOf(ackstring) != -1) {
139  _error = ERROR_NO_ERROR;
140  return true;
141  }
142  }
143  }
144  }
145  //check for error
146  if ((answer.indexOf("Resend:") != -1) || (answer.indexOf("rs N") != -1)) {
147  _needcommandnumber = Get_commandNumber(answer);
148  if (_needcommandnumber == _commandnumber) {
149  _error = ERROR_RESEND;
150  } else {
151  _error = ERROR_NUMBER_MISMATCH;
152  log_esp3d("Error provived %d but need %d", _commandnumber, _needcommandnumber);
153  }
154 
155  return false;
156  }
157  if (answer.indexOf("skip") != -1) {
158  _error = ERROR_LINE_IGNORED;
159  return false;
160  }
161  }
162 
163  Hal::wait (0); //minimum delay is 10 actually
164  }
165  _error = ERROR_ACK_NUMBER;
166  return false;
167 }
168 
169 //the command MUST NOT have '\n' at the end
170 //the max command try to send the same command if resend is asked no more than MAX_TRY_2_SEND times
171 //others error cancel the sending
172 //number mismatch / skip / timeout error must be managed out of this function
173 //line number incrementation is not done in this function neither
174 bool GcodeHost::sendCommand(const char* command, bool checksum, bool wait4ack, const char * ack)
175 {
176  log_esp3d("Send command: %s", command);
177  String s;
178  if(checksum) {
179  s = CheckSumCommand(command, _commandnumber);
180  } else {
181  s = command;
182  }
183  for(uint8_t try_nb = 0; try_nb < MAX_TRY_2_SEND; try_nb ++) {
184  _error = ERROR_NO_ERROR;
185  purge();
186  if ((_error != ERROR_NO_ERROR) && wait4ack) {
187  return false;
188  } else {
189  //if no need to wait for ack the purge has no real impact but clear buffer
190  _error = ERROR_NO_ERROR;
191  }
192  uint32_t start = millis();
193  //to give a chance to not overload buffer
194  bool done = false;
195  while (((millis() - start) < DEFAULT_TIMOUT) && !done) {
196  if (serial_service.availableForWrite() > s.length()) {
197  if (strlen(command) == serial_service.write((const uint8_t*)s.c_str(), s.length())) {
198  if (serial_service.write('\n')==1) {
199  if(!wait4ack) {
200  log_esp3d("No need ack");
201  return true;
202  }
203  //process answer
204  if (wait_for_ack(DEFAULT_TIMOUT, ack)) {
205  log_esp3d("Command got ack");
206  return true;
207  } else {
208  //what is the error ?
209  log_esp3d("Error: %d", _error);
210  //no need to retry for this one
211  if (_error == ERROR_MEMORY_PROBLEM) {
212  return false;
213  }
214  //need to resend command
215  if (_error == ERROR_RESEND) {
216  done = true;
217  }
218  //the printer ask for another command line so exit
219  if ((_error == ERROR_NUMBER_MISMATCH) || (_error == ERROR_LINE_IGNORED)) {
220  return false;
221  }
222  }
223  }
224  }
225  }
226  }
227  }
228  if (_error == ERROR_NO_ERROR) {
229  _error = ERROR_CANNOT_SEND_DATA;
230  log_esp3d("Error: %d", _error);
231  }
232  return false;
233 }
234 
235 bool GcodeHost::purge(uint32_t timeout)
236 {
237  uint32_t start = millis();
238  uint8_t buf [51];
239  _error = 0;
240  log_esp3d("Purge started");
241  while (serial_service.available() > 0) {
242  if ((millis() - start ) > timeout) {
243  log_esp3d("Purge timeout\r\n");
244  _error = ERROR_TIME_OUT;
245  return false;
246  }
247  size_t len = serial_service.readBytes (buf, 50);
248  buf[len] = '\0';
249  log_esp3d("**\n%s\n", (const char *)buf);
251  String s = (const char *)buf;
252  //repetier never stop sending data so no need to wait if have 'wait' or 'busy'
253  if((s.indexOf ("wait") > -1) || (s.indexOf ("busy") > -1)) {
254  return true;
255  }
256  log_esp3d("Impossible to purge\r\n");
257  }
258  Hal::wait (0);
259  }
260  log_esp3d("Purge done");
261  return true;
262 }
263 
264 uint32_t GcodeHost::Get_commandNumber(String & response)
265 {
266  uint32_t l = 0;
267  String sresend = "Resend:";
269  sresend = "rs N";
270  }
271  int pos = response.indexOf(sresend);
272  if (pos == -1 ) {
273  log_esp3d("Cannot find label %d", _error);
274  return -1;
275  }
276  pos+=sresend.length();
277  int pos2 = response.indexOf("\n", pos);
278  String snum = response.substring(pos, pos2);
279  //remove potential unwished char
280  snum.replace("\r", "");
281  l = snum.toInt();
282  log_esp3d("Command number to resend is %s", String((uint32_t)l).c_str());
283  return l;
284 }
285 
286 bool GcodeHost::processFSFile(const char * filename, level_authenticate_type auth_type, ESP3DOutput * output)
287 {
288  bool res = true;
289  log_esp3d("Processing FS : %s", filename);
290  if (!ESP_FileSystem::exists(filename)) {
291  log_esp3d("Cannot find file");
292  return false;
293  }
294  ESP_File f = ESP_FileSystem::open(filename);
295  if (!f.isOpen()) {
296  log_esp3d("Cannot open file");
297  return false;
298  }
299  size_t filesize = f.size();
300  int8_t ch;
301  String cmd = "";
302  for (size_t c = 0; c< filesize ; c++) {
303  ch = f.read();
304  if (ch == -1) {
305  log_esp3d("Error reading file");
306  f.close();
307  return false;
308  }
309  if ((ch == 13)||(ch == 10) || (c==(filesize-1))) {
310  //for end of file without \n neither \r
311  if (!((ch == 13)||(ch == 10)) && (c==(filesize-1))) {
312  cmd+=(char)ch;
313  }
314  cmd.trim();
315  if(cmd.length() > 0) {
316  //ignore comments
317  if (cmd[0]!=';') {
318  //it is internal or not ?
319  if(esp3d_commands.is_esp_command((uint8_t *)cmd.c_str(), cmd.length())) {
320  esp3d_commands.process((uint8_t *)cmd.c_str(), cmd.length(), output, auth_type);
321  } else {
322  if (!sendCommand(cmd.c_str(),false, true)) {
323  log_esp3d("Error sending command");
324  //To stop instead of continue may need some trigger
325  res = false;
326  }
327  }
328  }
329  cmd="";
330  }
331 
332  } else {
333  cmd+=(char)ch;
334  }
335  }
336  f.close();
337  return res;
338 }
339 
340 bool GcodeHost::processscript(const char * line)
341 {
342  bool res = true;
343  String s = line;
344  s.trim();
346  if (s.startsWith(ESP_FLASH_FS_HEADER)) {
347  res = processFile(line, LEVEL_ADMIN, &output);
348  } else {
349  res = processLine(line, LEVEL_ADMIN, &output);
350  }
351  return res;
352 }
353 
354 //split line of command separated by '\n'
355 bool GcodeHost::processLine(const char * line, level_authenticate_type auth_type, ESP3DOutput * output)
356 {
357  bool res = true;
358  String s = "";
359  for (uint p = 0; p < strlen(line); p++) {
360  if ((line[p]==10) || (line[p]==13) || (p == (strlen(line)-1))) {
361  if (!((line[p]==10) || (line[p]==13)) && (p == (strlen(line)-1))) {
362  s+=line[p];
363  }
364  s.trim();
365  if (s.length()>0) {
366  //ignore comments
367  if (s[0]!=';') {
368  //it is internal or not ?
369  if(esp3d_commands.is_esp_command((uint8_t *)s.c_str(), s.length())) {
370  esp3d_commands.process((uint8_t *)s.c_str(), s.length(), output, auth_type);
371  } else {
372  //no check sum no ack
373  if (!sendCommand(s.c_str(),false, false)) {
374  log_esp3d("Error sending command");
375  //To stop instead of continue may need some trigger
376  res = false;
377  }
378  }
379  }
380  }
381  s = "";
382  } else {
383  s+=line[p];
384  }
385  }
386  return res;
387 }
388 
389 bool GcodeHost::processFile(const char * filename, level_authenticate_type auth_type, ESP3DOutput * output)
390 {
391  String FileName = filename;
392  FileName.trim();
393  log_esp3d("Processing: %s", FileName.c_str());
394  if (FileName.startsWith(ESP_FLASH_FS_HEADER)) {
395  String f = FileName.substring(strlen(ESP_FLASH_FS_HEADER),FileName.length());
396  return processFSFile(f.c_str(), auth_type, output);
397  }
398  //TODO SD = SDCard
399  //TODO UD = USB DISK
400  log_esp3d("Invalid filename");
401  return false;
402 }
403 
404 #endif //ESP_GCODE_HOST_FEATURE
esp3d_commands
Commands esp3d_commands
Definition: commands.cpp:26
REPETIER4DV
#define REPETIER4DV
Definition: settings_esp3d.h:29
ESP_File::close
void close()
ERROR_NO_ERROR
#define ERROR_NO_ERROR
Definition: gcode_host.h:32
SerialService::availableForWrite
uint availableForWrite()
Definition: serial_service.cpp:243
GcodeHost::purge
bool purge(uint32_t timeout=DEFAULT_TIMOUT)
Definition: gcode_host.cpp:235
Hal::wait
static void wait(uint32_t milliseconds)
Definition: hal.cpp:226
SerialService::write
size_t write(uint8_t c)
Definition: serial_service.cpp:212
GcodeHost::Get_commandNumber
uint32_t Get_commandNumber(String &response)
Definition: gcode_host.cpp:264
Commands::process
void process(uint8_t *sbuf, size_t len, ESP3DOutput *output, level_authenticate_type auth=LEVEL_GUEST, ESP3DOutput *outputonly=nullptr)
Definition: commands.cpp:36
gcode_host.h
GcodeHost::processFile
bool processFile(const char *filename, level_authenticate_type auth_type, ESP3DOutput *output)
Definition: gcode_host.cpp:389
SMOOTHIEWARE
#define SMOOTHIEWARE
Definition: settings_esp3d.h:32
GcodeHost::end
void end()
Definition: gcode_host.cpp:51
GcodeHost::processFSFile
bool processFSFile(const char *filename, level_authenticate_type auth_type, ESP3DOutput *output)
Definition: gcode_host.cpp:286
ERROR_LINE_IGNORED
#define ERROR_LINE_IGNORED
Definition: gcode_host.h:40
Settings_ESP3D::GetFirmwareTarget
static uint8_t GetFirmwareTarget(bool fromsettings=false)
Definition: settings_esp3d.cpp:183
ESP_File::isOpen
bool isOpen()
Definition: esp_filesystem.cpp:118
GcodeHost::wait_for_data
size_t wait_for_data(uint32_t timeout=DEFAULT_TIMOUT)
Definition: gcode_host.cpp:80
GcodeHost::~GcodeHost
~GcodeHost()
Definition: gcode_host.cpp:39
ESP_File
Definition: esp_filesystem.h:30
esp3d_gcode_host
GcodeHost esp3d_gcode_host
Definition: gcode_host.cpp:30
ESP_FLASH_FS_HEADER
#define ESP_FLASH_FS_HEADER
Definition: esp_filesystem.h:26
GcodeHost::processLine
bool processLine(const char *line, level_authenticate_type auth_type, ESP3DOutput *output)
Definition: gcode_host.cpp:355
GcodeHost::CheckSumCommand
String CheckSumCommand(const char *command, uint32_t commandnb)
Definition: gcode_host.cpp:72
level_authenticate_type
level_authenticate_type
Definition: authentication_service.h:25
REPETIER
#define REPETIER
Definition: settings_esp3d.h:33
GcodeHost::wait_for_ack
bool wait_for_ack(uint32_t timeout=DEFAULT_TIMOUT, bool checksum=false, const char *ack=nullptr)
Definition: gcode_host.cpp:106
GcodeHost::Checksum
uint8_t Checksum(const char *command, uint32_t commandSize)
Definition: gcode_host.cpp:60
serial_service
SerialService serial_service
Definition: serial_service.cpp:42
GcodeHost
Definition: gcode_host.h:42
GcodeHost::processscript
bool processscript(const char *line)
Definition: gcode_host.cpp:340
ESP_File::read
int read()
Definition: esp_filesystem.cpp:172
GcodeHost::GcodeHost
GcodeHost()
Definition: gcode_host.cpp:32
Commands::is_esp_command
bool is_esp_command(uint8_t *sbuf, size_t len)
Definition: commands.cpp:73
ERROR_RESEND
#define ERROR_RESEND
Definition: gcode_host.h:38
log_esp3d
#define log_esp3d(format,...)
Definition: debug_esp3d.h:29
SerialService::readBytes
size_t readBytes(uint8_t *sbuf, size_t len)
Definition: serial_service.cpp:258
ERROR_MEMORY_PROBLEM
#define ERROR_MEMORY_PROBLEM
Definition: gcode_host.h:37
GcodeHost::resetCommandNumbering
bool resetCommandNumbering()
Definition: gcode_host.cpp:89
GcodeHost::sendCommand
bool sendCommand(const char *command, bool checksum=false, bool wait4ack=true, const char *ack=nullptr)
Definition: gcode_host.cpp:174
ESP_ALL_CLIENTS
#define ESP_ALL_CLIENTS
Definition: esp3doutput.h:30
ERROR_TIME_OUT
#define ERROR_TIME_OUT
Definition: gcode_host.h:33
SerialService::available
int available()
Definition: serial_service.cpp:248
ERROR_CANNOT_SEND_DATA
#define ERROR_CANNOT_SEND_DATA
Definition: gcode_host.h:34
LEVEL_ADMIN
@ LEVEL_ADMIN
Definition: authentication_service.h:28
ESP_File::size
size_t size()
Definition: esp_filesystem.cpp:138
MAX_TRY_2_SEND
#define MAX_TRY_2_SEND
Definition: gcode_host.h:31
DEFAULT_TIMOUT
#define DEFAULT_TIMOUT
Definition: gcode_host.h:30
ERROR_NUMBER_MISMATCH
#define ERROR_NUMBER_MISMATCH
Definition: gcode_host.h:39
ESP_FileSystem::exists
static bool exists(const char *path)
ESP3DOutput
Definition: esp3doutput.h:48
ESP_FileSystem::open
static ESP_File open(const char *path, uint8_t mode=ESP_FILE_READ)
GcodeHost::handle
void handle()
Definition: gcode_host.cpp:56
ERROR_ACK_NUMBER
#define ERROR_ACK_NUMBER
Definition: gcode_host.h:36
GcodeHost::begin
bool begin(bool waitwhenidle=false)
Definition: gcode_host.cpp:44