From f7dd60c39f6125ed5e6aa276022712dd8369d1cd Mon Sep 17 00:00:00 2001 From: Luc Date: Wed, 2 Oct 2019 10:02:18 +0800 Subject: [PATCH] Add basic functions for GCODE host first pass to be used in auto start Macro / [ESP700]/ Serial File Upload --- esp3d/configuration.h | 3 + esp3d/src/include/version.h | 2 +- esp3d/src/modules/gcode_host/gcode_host.cpp | 270 ++++++++++++++++++++ esp3d/src/modules/gcode_host/gcode_host.h | 68 +++++ 4 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 esp3d/src/modules/gcode_host/gcode_host.cpp create mode 100644 esp3d/src/modules/gcode_host/gcode_host.h diff --git a/esp3d/configuration.h b/esp3d/configuration.h index 77a9f542..0789f1a1 100644 --- a/esp3d/configuration.h +++ b/esp3d/configuration.h @@ -159,6 +159,9 @@ //if you do not know what is that then better left it commented //#define ESP_ACCESS_CONTROL_ALLOW_ORIGIN +//ESP_GCODE_HOST_FEATURE : allow to send GCODE with ack +#define ESP_GCODE_HOST_FEATURE + //Extra features ///////////////////////////////////////////////////////////////////////// /************************************ * diff --git a/esp3d/src/include/version.h b/esp3d/src/include/version.h index 401d97a0..01b932a0 100644 --- a/esp3d/src/include/version.h +++ b/esp3d/src/include/version.h @@ -22,7 +22,7 @@ #define _VERSION_ESP3D_H //version and sources location -#define FW_VERSION "3.0.0.a19" +#define FW_VERSION "3.0.0.a20" #define REPOSITORY "https://github.com/luc-github/ESP3D" #endif //_VERSION_ESP3D_H diff --git a/esp3d/src/modules/gcode_host/gcode_host.cpp b/esp3d/src/modules/gcode_host/gcode_host.cpp new file mode 100644 index 00000000..3558e03d --- /dev/null +++ b/esp3d/src/modules/gcode_host/gcode_host.cpp @@ -0,0 +1,270 @@ +/* + gcode_host.cpp - gcode host functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "../../include/esp3d_config.h" +#ifdef ESP_GCODE_HOST_FEATURE +#include "gcode_host.h" +#include "../../core/settings_esp3d.h" +#include "../serial/serial_service.h" + +GcodeHost esp3d_gcode_host; + +GcodeHost::GcodeHost() +{ + _commandnumber = 0; + _waitwhenidle = false; + _error = ERROR_NO_ERROR; +} + +GcodeHost::~GcodeHost() +{ + end(); +} + +bool GcodeHost::begin(bool waitwhenidle) +{ + end(); + _waitwhenidle = waitwhenidle; + return true; +} + +void GcodeHost::end() +{ + _commandnumber = 0; + _error = ERROR_NO_ERROR; +} +void GcodeHost::handle() +{ +} + +uint8_t GcodeHost::Checksum(const char * command, uint16_t commandSize) +{ + uint8_t checksum_val =0; + if (command == NULL) return 0; + for (uint16_t i=0; i < commandSize; i++) + { + checksum_val = checksum_val ^ ((uint8_t)command[i]); + } + return checksum_val; +} + +String GcodeHost::CheckSumCommand(const char* command, uint16_t commandnb){ + String commandchecksum = "N" + String((uint16_t)commandnb)+ " " + command; + uint8_t crc = Checksum(commandchecksum.c_str(), commandchecksum.length()); + commandchecksum+="*"+String(crc); + return commandchecksum; +} + +size_t GcodeHost::wait_for_data(uint32_t timeout){ + uint32_t start = millis(); + while ((serial_service.available() < 2) && ((millis()-start) < timeout)) + { + Hal::wait (0); //minimum delay is 10 actually + } + return serial_service.available(); +} + +bool GcodeHost::resetCommandNumbering(){ + String resetcmd = "M110 N0"; + if (Settings_ESP3D::GetFirmwareTarget() == SMOOTHIEWARE)resetcmd = "N0 M110"; + else resetcmd = "M110 N0"; + _commandnumber = 1; + return sendCommand(resetcmd.c_str()); +} + +/*bool GcodeHost::endUpload(){ + + return true; +}*/ + +bool GcodeHost::wait_for_ack(uint32_t timeout, bool checksum, const char * ack){ + _needcommandnumber = _commandnumber; + uint32_t start = millis(); + String answer = ""; + while ((millis()-start) < timeout) + { + size_t len = serial_service.available(); + if (len > 0){ + uint8_t * sbuf = (uint8_t *)malloc(len+1); + if(!sbuf){ + _error = ERROR_MEMORY_PROBLEM; + return false; + } + sbuf[len] = '\0'; + answer+= (const char *)sbuf; + free(sbuf); + log_esp3d("Answer: %s",anwer.c_str()); + //check for ack + if (ack!=nullptr){ + if (answer.indexOf(ack) != -1){ + _error = ERROR_NO_ERROR; + return true; + } + } else{ + if (answer.indexOf("ok") != -1){ + if (!checksum){ + _error = ERROR_NO_ERROR; + return true; + } else{ + //check number + String ackstring = "ok " + String(_commandnumber); + if (answer.indexOf(ackstring) != -1){ + _error = ERROR_NO_ERROR; + return true; + } + } + } + } + //check for error + if ((answer.indexOf("Resend:") != -1) || (answer.indexOf("rs N") != -1)){ + _needcommandnumber = Get_commandNumber(answer); + if (_needcommandnumber == _commandnumber) { + _error = ERROR_RESEND; + } else { + _error = ERROR_NUMBER_MISMATCH; + log_esp3d("Error provived %d but need %d", _commandnumber, _needcommandnumber); + } + + return false; + } + if (answer.indexOf("skip") != -1){ + _error = ERROR_LINE_IGNORED; + return false; + } + } + + Hal::wait (0); //minimum delay is 10 actually + } + _error = ERROR_ACK_NUMBER; + return false; +} + +//the command MUST NOT have '\n' at the end +//the max command try to send the same command if resend is asked no more than MAX_TRY_2_SEND times +//others error cancel the sending +//number mismatch / skip / timeout error must be managed out of this function +//line number incrementation is not done in this function neither +bool GcodeHost::sendCommand(const char* command, bool checksum, bool wait4ack, const char * ack){ + log_esp3d("Send command: %s", command); + String s; + if(checksum){ + s = CheckSumCommand(command, _commandnumber); + } else{ + s = command; + } + for(uint8_t try_nb = 0; try_nb < MAX_TRY_2_SEND; try_nb ++) { + _error = ERROR_NO_ERROR; + purge(); + if ((_error != ERROR_NO_ERROR) && wait4ack){ + return false; + } else { + //if no need to wait for ack the purge has no real impact but clear buffer + _error = ERROR_NO_ERROR; + } + uint32_t start = millis(); + //to give a chance to not overload buffer + bool done = false; + while (((millis() - start) < DEFAULT_TIMOUT) && !done) { + if (serial_service.availableForWrite() > s.length()){ + if (strlen(command) == serial_service.write((const uint8_t*)s.c_str(), s.length())) { + if (serial_service.write('\n')==1){ + if(!wait4ack){ + log_esp3d("No need ack"); + return true; + } + //process answer + if (wait_for_ack(DEFAULT_TIMOUT, ack)) { + log_esp3d("Command got ack"); + return true; + } else { + //what is the error ? + log_esp3d("Error: %d", _error); + //no need to retry for this one + if (_error == ERROR_MEMORY_PROBLEM) { + return false; + } + //need to resend command + if (_error == ERROR_RESEND) { + done = true; + } + //the printer ask for another command line so exit + if ((_error == ERROR_NUMBER_MISMATCH) || (_error == ERROR_LINE_IGNORED)){ + return false; + } + } + } + } + } + } + } + if (_error == ERROR_NO_ERROR) { + _error = ERROR_CANNOT_SEND_DATA; + log_esp3d("Error: %d", _error); + } + return false; +} + +bool GcodeHost::purge(uint32_t timeout){ + uint32_t start = millis(); + uint8_t buf [51]; + _error = 0; + log_esp3d("Purge started") + while (serial_service.available() > 0){ + if ((millis() - start ) > timeout) { + log_esp3d("Purge timeout\r\n") + _error = ERROR_TIME_OUT; + return false; + } + size_t len = serial_service.readBytes (buf, 50); + buf[len] = '\0'; + log_esp3d("**\n%s\n", (const char *)buf); + if ( (Settings_ESP3D::GetFirmwareTarget() == REPETIER4DV) || (Settings_ESP3D::GetFirmwareTarget() == REPETIER) || _waitwhenidle) { + String s = (const char *)buf; + //repetier never stop sending data so no need to wait if have 'wait' or 'busy' + if((s.indexOf ("wait") > -1) || (s.indexOf ("busy") > -1))return true; + log_esp3d("Impossible to purge\r\n") + } + Hal::wait (0); + } + log_esp3d("Purge done") + return true; +} + +uint16_t GcodeHost::Get_commandNumber(String & response){ + int64_t l = 0; + String sresend = "Resend:"; + if ( Settings_ESP3D::GetFirmwareTarget() == SMOOTHIEWARE){ + sresend = "rs N"; + } + int pos = response.indexOf(sresend); + if (pos == -1 ) { + log_esp3d("Cannot find label", _error); + return -1; + } + pos+=sresend.length(); + int pos2 = response.indexOf("\n", pos); + String snum = response.substring(pos, pos2); + //remove potential unwished char + snum.replace("\r", ""); + l = snum.toInt(); + log_esp3d("Command number to resend is %s", String(l).c_str()); + return l; +} +#endif //ESP_GCODE_HOST_FEATURE diff --git a/esp3d/src/modules/gcode_host/gcode_host.h b/esp3d/src/modules/gcode_host/gcode_host.h new file mode 100644 index 00000000..2cea929e --- /dev/null +++ b/esp3d/src/modules/gcode_host/gcode_host.h @@ -0,0 +1,68 @@ +/* + gcode_host.h - gcode host functions class + + Copyright (c) 2014 Luc Lebosse. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + + +#ifndef _GCODE_HOST_H +#define _GCODE_HOST_H + +#define DEFAULT_TIMOUT 2000 +#define MAX_TRY_2_SEND 5 +#define ERROR_NO_ERROR 0 +#define ERROR_TIME_OUT 1 +#define ERROR_CANNOT_SEND_DATA 2 +#define ERROR_LINE_NUMBER 3 +#define ERROR_ACK_NUMBER 4 +#define ERROR_MEMORY_PROBLEM 5 +#define ERROR_RESEND 6 +#define ERROR_NUMBER_MISMATCH 7 +#define ERROR_LINE_IGNORED 8 + +class GcodeHost +{ +public: + GcodeHost(); + ~GcodeHost(); + bool begin(bool waitwhenidle = false); + void end(); + void handle(); + bool sendCommand(const char* command, bool checksum = false, bool wait4ack = true, const char * ack=nullptr); + uint16_t currentCommandNumber(){return _commandnumber;} + void setCommandNumber(uint16_t n){_commandnumber = n;} + bool resetCommandNumbering(); + uint8_t Checksum(const char * command, uint16_t commandSize); + String CheckSumCommand(const char* command, uint16_t commandnb); + size_t wait_for_data(uint32_t timeout = DEFAULT_TIMOUT); + bool wait_for_ack(uint32_t timeout = DEFAULT_TIMOUT, bool checksum=false, const char * ack=nullptr); + bool purge(uint32_t timeout = DEFAULT_TIMOUT); + uint16_t Get_commandNumber(String & response); + bool waitWhenIdle(){ return _waitwhenidle;} + uint8_t getErrorNum(){ return _error;} +private: + uint16_t _commandnumber; + uint16_t _needcommandnumber; + bool _waitwhenidle; + uint8_t _error; +}; + +extern GcodeHost esp3d_gcode_host; + +#endif //_GCODE_HOST_H +