/* serial_service.cpp - serial services functions class Copyright (c) 2014 Luc Lebosse. All rights reserved. This code 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 code 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 code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../../include/esp3d_config.h" #if COMMUNICATION_PROTOCOL == MKS_SERIAL || \ COMMUNICATION_PROTOCOL == RAW_SERIAL || defined(ESP_SERIAL_BRIDGE_OUTPUT) #include "../../core/commands.h" #include "../../core/esp3doutput.h" #include "../../core/settings_esp3d.h" #include "serial_service.h" #if COMMUNICATION_PROTOCOL == MKS_SERIAL #include "../mks/mks_service.h" #endif // COMMUNICATION_PROTOCOL == MKS_SERIAL #include "../authentication/authentication_service.h" #if defined(ARDUINO_ARCH_ESP8266) #define MAX_SERIAL 2 HardwareSerial *Serials[MAX_SERIAL] = {&Serial, &Serial1}; #endif // ARDUINO_ARCH_ESP8266 #if defined(ARDUINO_ARCH_ESP32) #if defined(CONFIG_IDF_TARGET_ESP32C3) #define MAX_SERIAL 2 HardwareSerial *Serials[MAX_SERIAL] = {&Serial, &Serial1}; #else #define MAX_SERIAL 3 HardwareSerial *Serials[MAX_SERIAL] = {&Serial, &Serial1, &Serial2}; #endif #endif // ARDUINO_ARCH_ESP32 // Serial Parameters #define ESP_SERIAL_PARAM SERIAL_8N1 #define ESP3DSERIAL_RUNNING_PRIORITY 1 #define ESP3DSERIAL_RUNNING_CORE 1 #define SERIAL_YIELD 10 SerialService serial_service = SerialService(MAIN_SERIAL); #if defined(ESP_SERIAL_BRIDGE_OUTPUT) SerialService serial_bridge_service = SerialService(BRIDGE_SERIAL); #endif // ESP_SERIAL_BRIDGE_OUTPUT #if defined(ARDUINO_ARCH_ESP32) && defined(SERIAL_INDEPENDANT_TASK) TaskHandle_t _hserialtask = nullptr; #endif // ARDUINO_ARCH_ESP32 const long SupportedBaudList[] = {9600, 19200, 38400, 57600, 74880, 115200, 230400, 250000, 500000, 921600, 1958400}; const size_t SupportedBaudListSize = sizeof(SupportedBaudList) / sizeof(long); #define TIMEOUT_SERIAL_FLUSH 1500 // Constructor SerialService::SerialService(uint8_t id) { _buffer_size = 0; _started = false; _needauthentication = true; _id = id; switch (_id) { case MAIN_SERIAL: _rxPin = ESP_RX_PIN; _txPin = ESP_TX_PIN; _client = ESP_SERIAL_CLIENT; break; #if defined(ESP_SERIAL_BRIDGE_OUTPUT) case BRIDGE_SERIAL: _rxPin = ESP_BRIDGE_RX_PIN; _txPin = ESP_BRIDGE_TX_PIN; _client = ESP_SERIAL_BRIDGE_CLIENT; break; #endif // ESP_SERIAL_BRIDGE_OUTPUT default: _rxPin = ESP_RX_PIN; _txPin = ESP_TX_PIN; _client = ESP_SERIAL_CLIENT; break; } } // Destructor SerialService::~SerialService() { end(); } // dedicated serial task #if defined(ARDUINO_ARCH_ESP32) && defined(SERIAL_INDEPENDANT_TASK) void ESP3DSerialTaskfn(void *parameter) { for (;;) { serial_service.process(); Hal::wait(SERIAL_YIELD); // Yield to other tasks } vTaskDelete(NULL); } #endif // ARDUINO_ARCH_ESP32 // extra parameters that do not need a begin void SerialService::setParameters() { #if defined(AUTHENTICATION_FEATURE) _needauthentication = (Settings_ESP3D::read_byte(ESP_SECURE_SERIAL) == 0) ? false : true; #else _needauthentication = false; #endif // AUTHENTICATION_FEATURE } // Setup Serial bool SerialService::begin(uint8_t serialIndex) { _serialIndex = serialIndex - 1; log_esp3d("Serial %d begin for %d", _serialIndex, _id); if (_id == BRIDGE_SERIAL && Settings_ESP3D::read_byte(ESP_SERIAL_BRIDGE_ON) == 0) { log_esp3d("Serial %d for %d is disabled", _serialIndex, _id); return true; } if (_serialIndex >= MAX_SERIAL) { log_esp3d_e("Serial %d begin for %d failed, index out of range", _serialIndex, _id); return false; } _lastflush = millis(); // read from settings long br = 0; long defaultBr = 0; switch (_id) { case MAIN_SERIAL: br = Settings_ESP3D::read_uint32(ESP_BAUD_RATE); defaultBr = Settings_ESP3D::getDefaultIntegerSetting(ESP_BAUD_RATE); break; #if defined(ESP_SERIAL_BRIDGE_OUTPUT) case BRIDGE_SERIAL: br = Settings_ESP3D::read_uint32(ESP_SERIAL_BRIDGE_BAUD); defaultBr = Settings_ESP3D::getDefaultIntegerSetting(ESP_SERIAL_BRIDGE_BAUD); break; #endif // ESP_SERIAL_BRIDGE_OUTPUT default: log_esp3d_e("Serial %d begin for %d failed, unknown id", _serialIndex, _id); return false; } setParameters(); log_esp3d("Baud rate is %d , default is %d", br, defaultBr); _buffer_size = 0; // change only if different from current if (br != baudRate() || (_rxPin != -1) || (_txPin != -1)) { if (!Settings_ESP3D::isValidIntegerSetting(br, ESP_BAUD_RATE)) { br = defaultBr; } Serials[_serialIndex]->setRxBufferSize(SERIAL_RX_BUFFER_SIZE); #ifdef ARDUINO_ARCH_ESP8266 Serials[_serialIndex]->begin(br, ESP_SERIAL_PARAM, SERIAL_FULL, (_txPin == -1) ? 1 : _txPin); if (_rxPin != -1) { Serials[_serialIndex]->pins((_txPin == -1) ? 1 : _txPin, _rxPin); } #endif // ARDUINO_ARCH_ESP8266 #if defined(ARDUINO_ARCH_ESP32) Serials[_serialIndex]->begin(br, ESP_SERIAL_PARAM, ESP_RX_PIN, ESP_TX_PIN); #if defined(SERIAL_INDEPENDANT_TASK) // create serial task once log_esp3d("Serial %d for %d Task creation", _serialIndex, _id); if (_hserialtask == nullptr && _id == MAIN_SERIAL) { xTaskCreatePinnedToCore( ESP3DSerialTaskfn, /* Task function. */ "ESP3D Serial Task", /* name of task. */ 8192, /* Stack size of task */ NULL, /* parameter of the task */ ESP3DSERIAL_RUNNING_PRIORITY, /* priority of the task */ &_hserialtask, /* Task handle to keep track of created task */ ESP3DSERIAL_RUNNING_CORE /* Core to run the task */ ); } if (_hserialtask == nullptr) { log_esp3d_e("Serial %d for %d Task creation failed", _serialIndex, _id); return false; } #endif // SERIAL_INDEPENDANT_TASK #endif // ARDUINO_ARCH_ESP32 } _started = true; log_esp3d("Serial %d for %d is started", _serialIndex, _id); return true; } // End serial bool SerialService::end() { flush(); delay(100); swap(); Serials[_serialIndex]->end(); _buffer_size = 0; _started = false; return true; } // return the array of long and array size const long *SerialService::get_baudratelist(uint8_t *count) { if (count) { *count = sizeof(SupportedBaudList) / sizeof(long); } return SupportedBaudList; } // Function which could be called in other loop void SerialService::process() { if (!_started) { return; } // Do we have some data waiting size_t len = available(); if (len > 0) { // if yes read them log_esp3d("Got %d chars in serial", len); uint8_t *sbuf = (uint8_t *)malloc(len); if (sbuf) { size_t count = readBytes(sbuf, len); // push to buffer if (count > 0) { push2buffer(sbuf, count); } // freen buffer free(sbuf); } } // we cannot left data in buffer too long // in case some commands "forget" to add \n if (((millis() - _lastflush) > TIMEOUT_SERIAL_FLUSH) && (_buffer_size > 0)) { flushbuffer(); } } // Function which could be called in other loop void SerialService::handle() { // the serial bridge do not use independant task // not sure if it is sill necessary to do it for the main serial // TBC.. #if defined(ARDUINO_ARCH_ESP32) && defined(SERIAL_INDEPENDANT_TASK) if (_id == MAIN_SERIAL) { return; } #endif // ARDUINO_ARCH_ESP32 && SERIAL_INDEPENDANT_TASK0 process(); } void SerialService::flushbuffer() { ESP3DOutput output(_client); _buffer[_buffer_size] = 0x0; // dispatch command if (_started) { esp3d_commands.process(_buffer, _buffer_size, &output, _needauthentication ? LEVEL_GUEST : LEVEL_ADMIN); } _lastflush = millis(); _buffer_size = 0; } // push collected data to buffer and proceed accordingly void SerialService::push2buffer(uint8_t *sbuf, size_t len) { if (!_started) { return; } log_esp3d("buffer get %d data ", len); #if COMMUNICATION_PROTOCOL == MKS_SERIAL static bool isFrameStarted = false; static bool isCommandFrame = false; static uint8_t type; // expected size static int16_t framePos = -1; // currently received static uint datalen = 0; for (size_t i = 0; i < len; i++) { log_esp3d("Data : %c %x", sbuf[i], sbuf[i]); framePos++; _lastflush = millis(); // so frame head was detected if (isFrameStarted) { // checking it is a valid Frame header if (framePos == 1) { log_esp3d("type = %x", sbuf[i]); if (MKSService::isFrame(char(sbuf[i]))) { if (MKSService::isCommand(char(sbuf[i]))) { isCommandFrame = true; log_esp3d("type: Command"); } else { log_esp3d("type: other"); type = sbuf[i]; isCommandFrame = false; } } else { log_esp3d_e("wrong frame type"); isFrameStarted = false; _buffer_size = 0; } } else if ((framePos == 2) || (framePos == 3)) { // add size to int if (framePos == 2) { datalen = sbuf[i]; } else { datalen += (sbuf[i] << 8); log_esp3d("Data len: %d", datalen); if (datalen > (ESP3D_SERIAL_BUFFER_SIZE - 5)) { log_esp3d_e("Overflow in data len"); isFrameStarted = false; _buffer_size = 0; } } } else if (MKSService::isTail(char(sbuf[i]))) { log_esp3d("got tail"); _buffer[_buffer_size] = '\0'; log_esp3d("size is %d", _buffer_size); // let check integrity if (_buffer_size == datalen) { log_esp3d("Flushing buffer"); if (isCommandFrame) { flushbuffer(); } else { MKSService::handleFrame(type, (const uint8_t *)_buffer, _buffer_size); } } else { log_esp3d_e("Error in data len"); } // clear frame infos _buffer_size = 0; isFrameStarted = false; } else { // it is data if (_buffer_size < ESP3D_SERIAL_BUFFER_SIZE - 5) { _buffer[_buffer_size] = sbuf[i]; _buffer_size++; } else { log_esp3d_e("Overflow in data len"); isFrameStarted = false; _buffer_size = 0; } } } else { // frame is not started let see if it is a head if (MKSService::isHead(char(sbuf[i]))) { log_esp3d("got head"); // yes it is isFrameStarted = true; framePos = 0; _buffer_size = 0; } else { // no so let reset all and just ignore it // TODO should we handle these data ? log_esp3d_e("Unidentified data : %c %x", sbuf[i], sbuf[i]); isCommandFrame = false; framePos = -1; datalen = 0; } } } #else for (size_t i = 0; i < len; i++) { _lastflush = millis(); // command is defined if ((char(sbuf[i]) == '\n') || (char(sbuf[i]) == '\r')) { if (_buffer_size < ESP3D_SERIAL_BUFFER_SIZE) { _buffer[_buffer_size] = sbuf[i]; _buffer_size++; } flushbuffer(); } else if (isPrintable(char(sbuf[i]))) { if (_buffer_size < ESP3D_SERIAL_BUFFER_SIZE) { _buffer[_buffer_size] = sbuf[i]; _buffer_size++; } else { flushbuffer(); _buffer[_buffer_size] = sbuf[i]; _buffer_size++; } } else { // it is not printable char // clean buffer first if (_buffer_size > 0) { flushbuffer(); } // process char _buffer[_buffer_size] = sbuf[i]; _buffer_size++; flushbuffer(); } } #endif } // Reset Serial Setting (baud rate) bool SerialService::reset() { log_esp3d("Reset serial"); bool res = false; switch (_id) { case MAIN_SERIAL: return Settings_ESP3D::write_uint32( ESP_BAUD_RATE, Settings_ESP3D::getDefaultIntegerSetting(ESP_BAUD_RATE)); #if defined(ESP_SERIAL_BRIDGE_OUTPUT) case BRIDGE_SERIAL: res = Settings_ESP3D::write_byte( ESP_SERIAL_BRIDGE_ON, Settings_ESP3D::getDefaultByteSetting(ESP_SERIAL_BRIDGE_ON)); return res && Settings_ESP3D::write_uint32( ESP_SERIAL_BRIDGE_BAUD, Settings_ESP3D::getDefaultIntegerSetting( ESP_SERIAL_BRIDGE_BAUD)); #endif // ESP_SERIAL_BRIDGE_OUTPUT default: return res; } } void SerialService::updateBaudRate(long br) { if (br != baudRate()) { Serials[_serialIndex]->flush(); Serials[_serialIndex]->updateBaudRate(br); } } // Get current baud rate long SerialService::baudRate() { long br = 0; br = Serials[_serialIndex]->baudRate(); #ifdef ARDUINO_ARCH_ESP32 // workaround for ESP32 if (br == 115201) { br = 115200; } if (br == 230423) { br = 230400; } #endif // ARDUINO_ARCH_ESP32 return br; } size_t SerialService::write(uint8_t c) { if (!_started) { return 0; } return Serials[_serialIndex]->write(c); } size_t SerialService::write(const uint8_t *buffer, size_t size) { if (!_started) { return 0; } if ((uint)Serials[_serialIndex]->availableForWrite() >= size) { return Serials[_serialIndex]->write(buffer, size); } else { size_t sizetosend = size; size_t sizesent = 0; uint8_t *buffertmp = (uint8_t *)buffer; uint32_t starttime = millis(); // loop until all is sent or timeout while (sizetosend > 0 && ((millis() - starttime) < 100)) { size_t available = Serials[_serialIndex]->availableForWrite(); if (available > 0) { // in case less is sent available = Serials[_serialIndex]->write( &buffertmp[sizesent], (available >= sizetosend) ? sizetosend : available); sizetosend -= available; sizesent += available; starttime = millis(); } else { Hal::wait(5); } } return sizesent; } } int SerialService::availableForWrite() { if (!_started) { return 0; } return Serials[_serialIndex]->availableForWrite(); } int SerialService::available() { if (!_started) { return 0; } return Serials[_serialIndex]->available(); } int SerialService::read() { if (!_started) { return -1; } return Serials[_serialIndex]->read(); } size_t SerialService::readBytes(uint8_t *sbuf, size_t len) { if (!_started) { return -1; } return Serials[_serialIndex]->readBytes(sbuf, len); } void SerialService::flush() { if (!_started) { return; } Serials[_serialIndex]->flush(); } void SerialService::swap() { #ifdef ARDUINO_ARCH_ESP8266 Serials[_serialIndex]->swap(); #endif // ARDUINO_ARCH_ESP8266 } #endif // COMMUNICATION_PROTOCOL == MKS_SERIAL || COMMUNICATION_PROTOCOL == // RAW_SERIAL