Grbl grblHAL better support for realtime commands (#1064)

* Add a realtime command detector 
* Move `\r` as printable char and not replaced as `\n`
* Refactorize Serial service code to avoid redondant code between esp32 and esp8266
* Implement isrealtimeCommand check for serial and centralize function in string helper
* Add new type message : realtimecmd
* Update simulator to handle commands and realtime commands
* Add simple serial test tool
*  Generate error if use HAS_DISPLAY with grbl/grblHAL
* Implement isRealTimeCommand for BT client
* Simplify BT push2buffer code
* Implement support for realtimecommand in telnet
* Implement isRealTimeCommand on websocket RX
* Simplify push2RXbuffer for websocket
* Implement isRealTimeCommand for USB serial
* Bump version
This commit is contained in:
Luc 2024-12-08 17:26:19 +08:00 committed by GitHub
parent 7b99d8a020
commit fe23f0cb1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 766 additions and 587 deletions

View File

@ -443,7 +443,7 @@
/* Printer screen
* If your printer has a display
*/
#define PRINTER_HAS_DISPLAY
//#define PRINTER_HAS_DISPLAY
/* ESP3D screen
* Screen connected to ESP board
@ -644,9 +644,9 @@
// LOG_OUTPUT_SERIAL2
// LOG_OUTPUT_TELNET
// LOG_OUTPUT_WEBSOCKET
// #define ESP_LOG_FEATURE LOG_OUTPUT_SERIAL0
#define ESP_LOG_FEATURE LOG_OUTPUT_SERIAL0
// #define ESP3D_DEBUG_LEVEL LOG_LEVEL_DEBUG
#define ESP3D_DEBUG_LEVEL LOG_LEVEL_DEBUG
#ifdef ESP_LOG_FEATURE
#define LOG_ESP3D_BAUDRATE 115200
@ -657,7 +657,7 @@
// #define ESP_BENCHMARK_FEATURE
// Disable sanity check at compilation
// #define ESP_NO_SANITY_CHECK
#define ESP_NO_SANITY_CHECK
/************************************
*

View File

@ -278,7 +278,7 @@ void Esp3D::restart_now() {
#if defined(FILESYSTEM_FEATURE)
ESP_FileSystem::end();
#endif // FILESYSTEM_FEATURE
#if COMMUNICATION_PROTOCOL == RAW_SERIAL || COMMUNICATION_PROTOCOL == MKS_SERIAL
#if (COMMUNICATION_PROTOCOL == RAW_SERIAL || COMMUNICATION_PROTOCOL == MKS_SERIAL) & defined(ARDUINO_ARCH_ESP8266)
esp3d_serial_service.swap();
#endif // COMMUNICATION_PROTOCOL == RAW_SERIAL || COMMUNICATION_PROTOCOL ==
// MKS_SERIAL

View File

@ -107,8 +107,6 @@ ESP3DCommands::ESP3DCommands() {
ESP3DCommands::~ESP3DCommands() {}
bool ESP3DCommands::isRealTimeCommand(char *cmd, size_t len) { return false; }
// check if current line is an [ESPXXX] command
bool ESP3DCommands::is_esp_command(uint8_t *sbuf, size_t len) {
// TODO
@ -1156,10 +1154,6 @@ bool ESP3DCommands::dispatchIdValue(bool json, const char *Id,
}
bool ESP3DCommands::formatCommand(char *cmd, size_t len) {
if (isRealTimeCommand(cmd, len)) {
// TODO: what if is realtime command ?
return true;
}
uint sizestr = strlen(cmd);
if (len > sizestr + 2) {
cmd[sizestr] = '\n';
@ -1205,10 +1199,6 @@ void ESP3DCommands::process(ESP3DMessage *msg) {
esp3d_log("Execute internal command %d", cmdId);
execute_internal_command(cmdId, espcmdpos, msg);
} else {
/*esp3d_log("Dispatch command, len %d, from %d(%s) to %d(%s)", msg->size,
static_cast<uint8_t>(msg->origin), GETCLIENTSTR(msg->origin),
static_cast<uint8_t>(msg->target), GETCLIENTSTR(msg->target));*/
// Work around to avoid to dispatch single \n or \r to everyone as it is
// part of previous ESP3D command
if (msg->size == 1 &&
@ -1237,7 +1227,7 @@ bool ESP3DCommands::dispatch(ESP3DMessage *msg, uint8_t *sbuf, size_t len) {
// check is need \n at the end of the command
if (msg->type == ESP3DMessageType::unique ||
msg->type == ESP3DMessageType::tail) {
esp3d_log("unique or tail message :*%s*", (char *)sbuf);
esp3d_log_d("unique or tail message :*%s*", (char *)sbuf);
if (!formatCommand((char *)sbuf, len)) {
esp3d_log("format command failed");
String tmpstr = "";
@ -1254,7 +1244,7 @@ bool ESP3DCommands::dispatch(ESP3DMessage *msg, uint8_t *sbuf, size_t len) {
return false;
}
} else {
esp3d_log("format command success, no need to update");
esp3d_log_d("format command success, no need to update");
if (!esp3d_message_manager.setDataContent(msg, sbuf, len)) {
esp3d_log_e("set data content failed");
esp3d_message_manager.deleteMsg(msg);
@ -1263,6 +1253,9 @@ bool ESP3DCommands::dispatch(ESP3DMessage *msg, uint8_t *sbuf, size_t len) {
}
} else {
esp3d_log("not unique or tail message");
if (msg->type == ESP3DMessageType::realtimecmd){
esp3d_log_d("realtime command");
}
if (!esp3d_message_manager.setDataContent(msg, sbuf, len)) {
esp3d_log_e("set data content failed");
esp3d_message_manager.deleteMsg(msg);
@ -1472,6 +1465,12 @@ bool ESP3DCommands::dispatch(ESP3DMessage *msg) {
#ifdef PRINTER_HAS_DISPLAY
case ESP3DClientType::remote_screen:
if (ESP3DSettings::GetFirmwareTarget() == GRBL ||
ESP3DSettings::GetFirmwareTarget() == GRBLHAL) {
esp3d_log_e("Remote screen message is not supported for GRBL");
sendOk = false;
break;
}
esp3d_log("Remote screen message");
// change target to output client
msg->target = getOutputClient();

View File

@ -227,7 +227,6 @@ class ESP3DCommands {
bool isFirst = false);
bool dispatchAuthenticationError(ESP3DMessage* msg, uint cmdid, bool json);
bool formatCommand(char* cmd, size_t len);
bool isRealTimeCommand(char* cmd, size_t len);
ESP3DClientType getOutputClient(bool fromSettings = false);
void setOutputClient(ESP3DClientType output_client) {
_output_client = output_client;

View File

@ -60,7 +60,7 @@ class WebServer;
#endif //pdTRUE
#endif //ESP8266
enum class ESP3DMessageType : uint8_t { head, core, tail, unique };
enum class ESP3DMessageType : uint8_t { head, core, tail, unique, realtimecmd };
union ESP3DRequest {
uint id;

View File

@ -22,6 +22,7 @@
#include <Arduino.h>
#include "../include/esp3d_config.h"
#include "esp3d_settings.h"
#if defined(WIFI_FEATURE) || defined(ETH_FEATURE)
#include "../modules/network/netconfig.h"
@ -174,16 +175,16 @@ const char* esp3d_string::urlEncode(const char* s) {
static String encoded;
encoded = "";
char temp[4];
for (int i = 0; i < strlen(s); i++) {
temp[0] =s[i];
if (temp[0] == 32) { //space
for (size_t i = 0; i < strlen(s); i++) {
temp[0] = s[i];
if (temp[0] == 32) { // space
encoded.concat('+');
} else if ((temp[0] >= 48 && temp[0] <= 57) /*0-9*/
|| (temp[0] >= 65 && temp[0] <= 90) /*A-Z*/
|| (temp[0] >= 97 && temp[0] <= 122) /*a-z*/
} else if ((temp[0] >= 48 && temp[0] <= 57) /*0-9*/
|| (temp[0] >= 65 && temp[0] <= 90) /*A-Z*/
|| (temp[0] >= 97 && temp[0] <= 122) /*a-z*/
) {
encoded.concat(temp[0]);
} else { //character needs encoding
} else { // character needs encoding
snprintf(temp, 4, "%%%02X", temp[0]);
encoded.concat(temp);
}
@ -208,7 +209,7 @@ const char* esp3d_string::formatBytes(uint64_t bytes) {
bool esp3d_string::isPrintableChar(char ch) {
int c = static_cast<int>(ch);
if (c == 9 || (c >= 32 && c <= 126) || c >= 128) {
if (c == '\t' || c == '\r' || (c >= ' ' && c <= '~') || c >= 128) {
return true;
}
return false;
@ -254,7 +255,7 @@ const char* esp3d_string::formatDuration(uint64_t duration) {
display = true;
}
if (hours > 0 || display) {
result+= String(hours) + "h ";
result += String(hours) + "h ";
display = true;
}
if (minutes > 0 || display) {
@ -264,4 +265,21 @@ const char* esp3d_string::formatDuration(uint64_t duration) {
result += String(seconds) + "s";
return result.c_str();
}
bool esp3d_string::isRealTimeCommand(char c) {
if (ESP3DSettings::GetFirmwareTarget() == GRBL ||
ESP3DSettings::GetFirmwareTarget() == GRBLHAL) {
// Standard characters
if (c == '?' || c == '!' || c == '~' || c == 0x18) { // 0x18 is ^X
return true;
}
// Range >= 0x80 et <= 0xA4
const unsigned char uc = static_cast<unsigned char>(c);
if (uc >= 0x80 && uc <= 0xA4) {
return true;
}
}
return false;
}

View File

@ -31,6 +31,7 @@ const char* formatBytes(uint64_t bytes);
bool isPrintableChar(char c);
const char* expandString(const char* s, bool formatspace = false);
const char * urlEncode(const char* s);
bool isRealTimeCommand(char c);
} // namespace esp3d_string
#endif //_ESP3D_STRING_H

View File

@ -22,7 +22,7 @@
#define _VERSION_ESP3D_H
// version and sources location
#define FW_VERSION "3.0.0.a244"
#define FW_VERSION "3.0.0.a245"
#define REPOSITORY "https://github.com/luc-github/ESP3D/tree/3.0"
#endif //_VERSION_ESP3D_H

View File

@ -176,7 +176,7 @@ void BTService::handle() {
// we cannot left data in buffer too long
// in case some commands "forget" to add \n
if (((millis() - _lastflush) > TIMEOUT_BT_FLUSH) && (_buffer_size > 0)) {
flushbuffer();
flushBuffer();
}
}
@ -189,51 +189,48 @@ void BTService::initAuthentication() {
}
ESP3DAuthenticationLevel BTService::getAuthentication() { return _auth; }
void BTService::flushbuffer() {
_buffer[_buffer_size] = 0x0;
// dispatch command
ESP3DMessage *msg = esp3d_message_manager.newMsg(
ESP3DClientType::bluetooth, esp3d_commands.getOutputClient(), _buffer,
_buffer_size, _auth);
if (msg) {
// process command
esp3d_commands.process(msg);
void BTService::flushData(const uint8_t *data, size_t size,
ESP3DMessageType type) {
ESP3DMessage *message = esp3d_message_manager.newMsg(
ESP3DClientType::bluetooth, esp3d_commands.getOutputClient(), data,
size, _auth);
if (message) {
message->type = type;
esp3d_log("Process Message");
esp3d_commands.process(message);
} else {
esp3d_log_e("Cannot create message");
}
_lastflush = millis();
}
void BTService::flushChar(char c) {
flushData((uint8_t *)&c, 1, ESP3DMessageType::realtimecmd);
}
void BTService::flushBuffer() {
_buffer[_buffer_size] = 0x0;
flushData((uint8_t *)_buffer, _buffer_size, ESP3DMessageType::unique);
_buffer_size = 0;
}
// push collected data to buffer and proceed accordingly
void BTService::push2buffer(uint8_t *sbuf, size_t len) {
if (!_buffer || !_started) {
return;
}
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_BT_BUFFER_SIZE) {
_buffer[_buffer_size] = sbuf[i];
_buffer_size++;
}
flushbuffer();
} else if (esp3d_string::isPrintableChar(char(sbuf[i]))) {
if (_buffer_size < ESP3D_BT_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
if (esp3d_string::isRealTimeCommand(sbuf[i])) {
flushChar(sbuf[i]);
} else {
_buffer[_buffer_size] = sbuf[i];
_buffer_size++;
flushbuffer();
if (_buffer_size > ESP3D_BT_BUFFER_SIZE ||
_buffer[_buffer_size - 1] == '\n') {
flushBuffer();
}
}
}
}

View File

@ -57,7 +57,9 @@ class BTService {
uint8_t _buffer[ESP3D_BT_BUFFER_SIZE + 1]; // keep space of 0x0 terminal
size_t _buffer_size;
void push2buffer(uint8_t* sbuf, size_t len);
void flushbuffer();
void flushBuffer();
void flushChar(char c);
void flushData(const uint8_t* data, size_t size, ESP3DMessageType type);
bool _started;
};

View File

@ -81,7 +81,7 @@ bool GcodeHost::push(const uint8_t *sbuf, size_t len) {
esp3d_log("Push got %d bytes", len);
for (size_t i = 0; i < len; i++) {
// it is a line process it
if (sbuf[i] == '\n' || sbuf[i] == '\r') {
if (sbuf[i] == '\n') {
flush();
} else {
// fill buffer until it is full
@ -263,7 +263,7 @@ void GcodeHost::readNextCommand() {
} else {
_processedSize++;
_currentPosition++;
if (!(((char)c == '\n') || ((char)c == '\r'))) {
if (!((char)c == '\n')) {
_currentCommand += (char)c;
} else {
processing = false;
@ -290,7 +290,7 @@ void GcodeHost::readNextCommand() {
} else {
_processedSize++;
_currentPosition++;
if (!(((char)c == '\n') || ((char)c == '\r'))) {
if (!(char)c == '\n' ) {
_currentCommand += (char)c;
} else {
processing = false;

View File

@ -29,19 +29,9 @@
#include "../../../core/esp3d_commands.h"
#include "../../../core/esp3d_message.h"
#include "../../../core/esp3d_settings.h"
#include "../../../core/esp3d_string.h"
#include "../../authentication/authentication_service.h"
const unsigned char realTimeCommands[] = {
'!', '~', '?', 0x18, 0x84, 0x85, 0x90, 0x92, 0x93, 0x94, 0x95,
0x96, 0x97, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0xA0, 0xA1};
bool isRealTimeCommand(unsigned char c) {
for (unsigned int i = 0; i < sizeof(realTimeCommands); i++) {
if (c == realTimeCommands[i]) {
return true;
}
}
return false;
}
// Handle web command query and send answer//////////////////////////////
void HTTP_Server::handle_web_command() {
@ -53,32 +43,42 @@ void HTTP_Server::handle_web_command() {
}
// esp3d_log("Authentication = %d", auth_level);
String cmd = "";
bool isRealTimeCommand = false;
if (_webserver->hasArg("cmd")) {
cmd = _webserver->arg("cmd");
esp3d_log_d("Command is %s", cmd.c_str());
if (!cmd.endsWith("\n")) {
if (ESP3DSettings::GetFirmwareTarget() == GRBL) {
esp3d_log_d("Command is not ending with \\n");
if (ESP3DSettings::GetFirmwareTarget() == GRBL || ESP3DSettings::GetFirmwareTarget() == GRBLHAL) {
uint len = cmd.length();
if (!((len == 1 && isRealTimeCommand(cmd[0])) ||
(len == 2 && isRealTimeCommand(cmd[1])))) {
if (!((len == 1 && esp3d_string::isRealTimeCommand(cmd[0])) ||
(len == 2 && esp3d_string::isRealTimeCommand(cmd[1])))) {
cmd += "\n";
esp3d_log_d("Command is not realtime, adding \\n");
} else { // no need \n for realtime command
esp3d_log_d("Command is realtime, no need to add \\n");
isRealTimeCommand = true;
// remove the 0XC2 that should not be there
if (len == 2 && isRealTimeCommand(cmd[1]) && cmd[1] == 0xC2) {
if (len == 2 && esp3d_string::isRealTimeCommand(cmd[1]) && cmd[1] == 0xC2) {
cmd[0] = cmd[1];
cmd[1] = 0x0;
esp3d_log_d("Command is realtime, removing 0xC2");
}
}
} else {
esp3d_log_d("Command is not realtime, adding \\n");
cmd += "\n"; // need to validate command
}
} else {
esp3d_log_d("Command is ending with \\n");
}
esp3d_log("Web Command: %s", cmd.c_str());
esp3d_log_d("Message type is %s for %s", isRealTimeCommand ? "realtimecmd" : "unique", cmd.c_str());
if (esp3d_commands.is_esp_command((uint8_t *)cmd.c_str(), cmd.length())) {
ESP3DMessage *msg = esp3d_message_manager.newMsg(
ESP3DClientType::http, esp3d_commands.getOutputClient(),
(uint8_t *)cmd.c_str(), cmd.length(), auth_level);
if (msg) {
msg->type = ESP3DMessageType::unique;
msg->type = ESP3DMessageType::unique; //ESP3D command is always unique
msg->request_id.code = 200;
// process command
esp3d_commands.process(msg);
@ -91,7 +91,7 @@ void HTTP_Server::handle_web_command() {
// no need to wait to answer then
_webserver->send(200, "text/plain", "ESP3D says: command forwarded");
esp3d_commands.dispatch(cmd.c_str(), esp3d_commands.getOutputClient(),
no_id, ESP3DMessageType::unique,
no_id, isRealTimeCommand ? ESP3DMessageType::realtimecmd :ESP3DMessageType::unique,
ESP3DClientType::http, auth_level);
}
} else if (_webserver->hasArg("ping")) {

View File

@ -257,7 +257,8 @@ bool HTTP_Server::dispatch(ESP3DMessage* msg) {
}
if ((msg->size > 0 && msg->data) || (msg->type == ESP3DMessageType::tail)) {
if (msg->type == ESP3DMessageType::head ||
msg->type == ESP3DMessageType::unique) {
msg->type == ESP3DMessageType::unique ||
msg->type == ESP3DMessageType::realtimecmd) {
set_http_headers();
int code = 200;
if (msg->request_id.code != 0) {

View File

@ -0,0 +1,176 @@
/*
esp3d_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 == RAW_SERIAL || \
defined(ESP_SERIAL_BRIDGE_OUTPUT) || COMMUNICATION_PROTOCOL == MKS_SERIAL
#include "../../core/esp3d_commands.h"
#include "../../core/esp3d_settings.h"
#include "../../core/esp3d_string.h"
#include "../authentication/authentication_service.h"
#include "serial_service.h"
extern HardwareSerial *Serials[];
// Serial Parameters
ESP3DSerialService esp3d_serial_service = ESP3DSerialService(MAIN_SERIAL);
#if defined(ESP_SERIAL_BRIDGE_OUTPUT)
ESP3DSerialService serial_bridge_service = ESP3DSerialService(BRIDGE_SERIAL);
#endif // ESP_SERIAL_BRIDGE_OUTPUT
const uint32_t SupportedBaudList[] = {9600, 19200, 38400, 57600, 74880,
115200, 230400, 250000, 500000, 921600,
1000000, 1958400, 2000000};
const size_t SupportedBaudListSize =
sizeof(SupportedBaudList) / sizeof(uint32_t);
// Destructor
ESP3DSerialService::~ESP3DSerialService() { end(); }
// extra parameters that do not need a begin
void ESP3DSerialService::setParameters() {
#if defined(AUTHENTICATION_FEATURE)
_needauthentication =
(ESP3DSettings::readByte(ESP_SECURE_SERIAL) == 0) ? false : true;
#else
_needauthentication = false;
#endif // AUTHENTICATION_FEATURE
}
void ESP3DSerialService::initAuthentication() {
#if defined(AUTHENTICATION_FEATURE)
_auth = ESP3DAuthenticationLevel::guest;
#else
_auth = ESP3DAuthenticationLevel::admin;
#endif // AUTHENTICATION_FEATURE
}
ESP3DAuthenticationLevel ESP3DSerialService::getAuthentication() {
if (_needauthentication) {
return _auth;
}
return ESP3DAuthenticationLevel::admin;
}
// return the array of uint32_t and array size
const uint32_t *ESP3DSerialService::get_baudratelist(uint8_t *count) {
if (count) {
*count = sizeof(SupportedBaudList) / sizeof(uint32_t);
}
return SupportedBaudList;
}
void ESP3DSerialService::flushChar(char c) { flushData((uint8_t *)&c, 1, ESP3DMessageType::realtimecmd); }
void ESP3DSerialService::flushBuffer() {
_buffer[_buffer_size] = 0x0;
flushData((uint8_t *)_buffer, _buffer_size, ESP3DMessageType::unique);
_buffer_size = 0;
}
void ESP3DSerialService::updateBaudRate(uint32_t br) {
if (br != _baudRate) {
Serials[_serialIndex]->flush();
Serials[_serialIndex]->updateBaudRate(br);
_baudRate = br;
}
}
// Get current baud rate
uint32_t ESP3DSerialService::baudRate() { return _baudRate; }
size_t ESP3DSerialService::writeBytes(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 {
ESP3DHal::wait(5);
}
}
return sizesent;
}
}
size_t ESP3DSerialService::readBytes(uint8_t *sbuf, size_t len) {
if (!_started) {
return -1;
}
return Serials[_serialIndex]->readBytes(sbuf, len);
}
void ESP3DSerialService::flush() {
if (!_started) {
return;
}
Serials[_serialIndex]->flush();
}
bool ESP3DSerialService::dispatch(ESP3DMessage *message) {
bool done = false;
// Only is serial service is started
if (_started) {
// Only if message is not null
if (message) {
// if message is not null
if (message->data && message->size != 0) {
if (writeBytes(message->data, message->size) == message->size) {
flush();
// Delete message now
esp3d_message_manager.deleteMsg(message);
done = true;
} else {
esp3d_log_e("Error while sending data");
}
} else {
esp3d_log_e("Error null data");
}
} else {
esp3d_log_e("Error null message");
}
}
return done;
}
#endif // COMMUNICATION_PROTOCOL == RAW_SERIAL ||
// defined(ESP_SERIAL_BRIDGE_OUTPUT)

View File

@ -29,6 +29,7 @@
#endif // ARDUINO_ARCH_ESP32
#define ESP3D_SERIAL_BUFFER_SIZE 1024
#define ESP_SERIAL_PARAM SERIAL_8N1
extern const uint32_t SupportedBaudList[];
extern const size_t SupportedBaudListSize;
@ -47,7 +48,9 @@ class ESP3DSerialService final {
uint8_t serialIndex() { return _serialIndex; }
const uint32_t *get_baudratelist(uint8_t *count);
void flush();
#if defined(ARDUINO_ARCH_ESP8266)
void swap();
#endif // ARDUINO_ARCH_ESP8266
size_t writeBytes(const uint8_t *buffer, size_t size);
size_t readBytes(uint8_t *sbuf, size_t len);
inline bool started() { return _started; }
@ -77,8 +80,12 @@ class ESP3DSerialService final {
SemaphoreHandle_t _mutex;
ESP3DMessageFIFO _messagesInFIFO;
#endif // ARDUINO_ARCH_ESP32
#if defined(ARDUINO_ARCH_ESP8266)
void push2buffer(uint8_t *sbuf, size_t len);
void flushbuffer();
#endif // ARDUINO_ARCH_ESP8266
void flushBuffer();
void flushChar(char c);
void flushData(const uint8_t* data, size_t size, ESP3DMessageType type);
};
extern ESP3DSerialService esp3d_serial_service;

View File

@ -19,7 +19,8 @@
*/
#if defined(ARDUINO_ARCH_ESP32)
#include "../../include/esp3d_config.h"
#if COMMUNICATION_PROTOCOL == RAW_SERIAL || defined(ESP_SERIAL_BRIDGE_OUTPUT) || COMMUNICATION_PROTOCOL == MKS_SERIAL
#if COMMUNICATION_PROTOCOL == RAW_SERIAL || \
defined(ESP_SERIAL_BRIDGE_OUTPUT) || COMMUNICATION_PROTOCOL == MKS_SERIAL
#include "../../core/esp3d_commands.h"
#include "../../core/esp3d_settings.h"
#include "../../core/esp3d_string.h"
@ -38,19 +39,7 @@ HardwareSerial *Serials[MAX_SERIAL] = {&Serial, &Serial1, &Serial2};
#endif
// Serial Parameters
#define ESP_SERIAL_PARAM SERIAL_8N1
ESP3DSerialService esp3d_serial_service = ESP3DSerialService(MAIN_SERIAL);
#if defined(ESP_SERIAL_BRIDGE_OUTPUT)
ESP3DSerialService serial_bridge_service = ESP3DSerialService(BRIDGE_SERIAL);
#endif // ESP_SERIAL_BRIDGE_OUTPUT
const uint32_t SupportedBaudList[] = {9600, 19200, 38400, 57600, 74880,
115200, 230400, 250000, 500000, 921600,
1000000, 1958400, 2000000};
const size_t SupportedBaudListSize = sizeof(SupportedBaudList) / sizeof(uint32_t);
#define TIMEOUT_SERIAL_FLUSH 1500
// Constructor
ESP3DSerialService::ESP3DSerialService(uint8_t id) {
_buffer_size = 0;
@ -85,34 +74,6 @@ ESP3DSerialService::ESP3DSerialService(uint8_t id) {
_messagesInFIFO.setMaxSize(0); // no limit
_baudRate = 0;
}
// Destructor
ESP3DSerialService::~ESP3DSerialService() { end(); }
// extra parameters that do not need a begin
void ESP3DSerialService::setParameters() {
#if defined(AUTHENTICATION_FEATURE)
_needauthentication =
(ESP3DSettings::readByte(ESP_SECURE_SERIAL) == 0) ? false : true;
#else
_needauthentication = false;
#endif // AUTHENTICATION_FEATURE
}
void ESP3DSerialService::initAuthentication() {
#if defined(AUTHENTICATION_FEATURE)
_auth = ESP3DAuthenticationLevel::guest;
#else
_auth = ESP3DAuthenticationLevel::admin;
#endif // AUTHENTICATION_FEATURE
}
ESP3DAuthenticationLevel ESP3DSerialService::getAuthentication() {
if (_needauthentication) {
return _auth;
}
return ESP3DAuthenticationLevel::admin;
}
void ESP3DSerialService::receiveSerialCb() { esp3d_serial_service.receiveCb(); }
#if defined(ESP_SERIAL_BRIDGE_OUTPUT)
@ -130,15 +91,16 @@ void ESP3DSerialService::receiveCb() {
while ((millis() - now) < SERIAL_COMMUNICATION_TIMEOUT) {
if (Serials[_serialIndex]->available()) {
_buffer[_buffer_size] = Serials[_serialIndex]->read();
_buffer_size++;
now = millis();
if (_buffer_size > ESP3D_SERIAL_BUFFER_SIZE ||
_buffer[_buffer_size - 1] == '\n' ||
_buffer[_buffer_size - 1] == '\r') {
if (_buffer[_buffer_size - 1] == '\r') {
_buffer[_buffer_size - 1] = '\n';
if (esp3d_string::isRealTimeCommand(_buffer[_buffer_size])) {
flushChar(_buffer[_buffer_size]);
_buffer[_buffer_size] = '\0'; //remove realtime command from buffer
} else {
_buffer_size++;
if (_buffer_size > ESP3D_SERIAL_BUFFER_SIZE ||
_buffer[_buffer_size - 1] == '\n') {
flushBuffer();
}
flushbuffer();
}
}
}
@ -229,12 +191,21 @@ bool ESP3DSerialService::end() {
return true;
}
// return the array of uint32_t and array size
const uint32_t *ESP3DSerialService::get_baudratelist(uint8_t *count) {
if (count) {
*count = sizeof(SupportedBaudList) / sizeof(uint32_t);
void ESP3DSerialService::flushData(const uint8_t *data, size_t size, ESP3DMessageType type) {
ESP3DMessage *message = esp3d_message_manager.newMsg(
_origin,
_id == MAIN_SERIAL ? ESP3DClientType::all_clients
: esp3d_commands.getOutputClient(),
data, size, getAuthentication());
if (message) {
message->type = type;
esp3d_log("Message sent to fifo list");
_messagesInFIFO.push(message);
} else {
esp3d_log_e("Cannot create message");
}
return SupportedBaudList;
_lastflush = millis();
}
// Function which could be called in other loop
@ -258,179 +229,30 @@ void ESP3DSerialService::handle() {
}
}
void ESP3DSerialService::flushbuffer() {
_buffer[_buffer_size] = 0x0;
if (_buffer_size == 1 && _buffer[0] == '\n') {
_buffer_size = 0;
return;
// Reset Serial Setting (baud rate)
bool ESP3DSerialService::reset() {
esp3d_log("Reset serial");
bool res = false;
switch (_id) {
case MAIN_SERIAL:
return ESP3DSettings::writeUint32(
ESP_BAUD_RATE,
ESP3DSettings::getDefaultIntegerSetting(ESP_BAUD_RATE));
#if defined(ESP_SERIAL_BRIDGE_OUTPUT)
case BRIDGE_SERIAL:
res = ESP3DSettings::writeByte(
ESP_SERIAL_BRIDGE_ON,
ESP3DSettings::getDefaultByteSetting(ESP_SERIAL_BRIDGE_ON));
return res &&
ESP3DSettings::writeUint32(ESP_SERIAL_BRIDGE_BAUD,
ESP3DSettings::getDefaultIntegerSetting(
ESP_SERIAL_BRIDGE_BAUD));
#endif // ESP_SERIAL_BRIDGE_OUTPUT
default:
return res;
}
// dispatch command
ESP3DMessage *message = esp3d_message_manager.newMsg(
_origin,
_id == MAIN_SERIAL ? ESP3DClientType::all_clients
: esp3d_commands.getOutputClient(),
(uint8_t *)_buffer, _buffer_size, getAuthentication());
if (message) {
// process command
message->type = ESP3DMessageType::unique;
esp3d_log("Message sent to fifo list");
_messagesInFIFO.push(message);
} else {
esp3d_log_e("Cannot create message");
}
_lastflush = millis();
_buffer_size = 0;
}
// push collected data to buffer and proceed accordingly
void ESP3DSerialService::push2buffer(uint8_t *sbuf, size_t len) {
/* if (!_started) {
return;
}
esp3d_log("buffer get %d data ", len);
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 (esp3d_string::isPrintableChar(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();
}
}
*/}
// Reset Serial Setting (baud rate)
bool ESP3DSerialService::reset() {
esp3d_log("Reset serial");
bool res = false;
switch (_id) {
case MAIN_SERIAL:
return ESP3DSettings::writeUint32(
ESP_BAUD_RATE,
ESP3DSettings::getDefaultIntegerSetting(ESP_BAUD_RATE));
#if defined(ESP_SERIAL_BRIDGE_OUTPUT)
case BRIDGE_SERIAL:
res = ESP3DSettings::writeByte(
ESP_SERIAL_BRIDGE_ON,
ESP3DSettings::getDefaultByteSetting(ESP_SERIAL_BRIDGE_ON));
return res && ESP3DSettings::writeUint32(
ESP_SERIAL_BRIDGE_BAUD,
ESP3DSettings::getDefaultIntegerSetting(
ESP_SERIAL_BRIDGE_BAUD));
#endif // ESP_SERIAL_BRIDGE_OUTPUT
default:
return res;
}
}
void ESP3DSerialService::updateBaudRate(uint32_t br) {
if (br != _baudRate) {
Serials[_serialIndex]->flush();
Serials[_serialIndex]->updateBaudRate(br);
_baudRate = br;
}
}
// Get current baud rate
uint32_t ESP3DSerialService::baudRate() {
return _baudRate;
}
size_t ESP3DSerialService::writeBytes(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 {
ESP3DHal::wait(5);
}
}
return sizesent;
}
}
size_t ESP3DSerialService::readBytes(uint8_t *sbuf, size_t len) {
if (!_started) {
return -1;
}
return Serials[_serialIndex]->readBytes(sbuf, len);
}
void ESP3DSerialService::flush() {
if (!_started) {
return;
}
Serials[_serialIndex]->flush();
}
void ESP3DSerialService::swap() {
// Nothing to do
}
bool ESP3DSerialService::dispatch(ESP3DMessage *message) {
bool done = false;
// Only is serial service is started
if (_started) {
// Only if message is not null
if (message) {
// if message is not null
if (message->data && message->size != 0) {
if (writeBytes(message->data, message->size) == message->size) {
flush();
// Delete message now
esp3d_message_manager.deleteMsg(message);
done = true;
} else {
esp3d_log_e("Error while sending data");
}
} else {
esp3d_log_e("Error null data");
}
} else {
esp3d_log_e("Error null message");
}
}
return done;
}
#endif // COMMUNICATION_PROTOCOL == RAW_SERIAL ||
// defined(ESP_SERIAL_BRIDGE_OUTPUT)

View File

@ -32,17 +32,10 @@
#define MAX_SERIAL 2
HardwareSerial *Serials[MAX_SERIAL] = {&Serial, &Serial1};
// Serial Parameters
#define ESP_SERIAL_PARAM SERIAL_8N1
ESP3DSerialService esp3d_serial_service = ESP3DSerialService(MAIN_SERIAL);
const uint32_t SupportedBaudList[] = {9600, 19200, 38400, 57600, 74880,
115200, 230400, 250000, 500000, 921600,
1000000, 1958400, 2000000};
const size_t SupportedBaudListSize = sizeof(SupportedBaudList) / sizeof(uint32_t);
#define TIMEOUT_SERIAL_FLUSH 1500
// Serial Parameters
// Constructor
ESP3DSerialService::ESP3DSerialService(uint8_t id) {
_buffer_size = 0;
@ -59,33 +52,6 @@ ESP3DSerialService::ESP3DSerialService(uint8_t id) {
_origin = ESP3DClientType::serial;
}
// Destructor
ESP3DSerialService::~ESP3DSerialService() { end(); }
// extra parameters that do not need a begin
void ESP3DSerialService::setParameters() {
#if defined(AUTHENTICATION_FEATURE)
_needauthentication =
(ESP3DSettings::readByte(ESP_SECURE_SERIAL) == 0) ? false : true;
#else
_needauthentication = false;
#endif // AUTHENTICATION_FEATURE
}
void ESP3DSerialService::initAuthentication() {
#if defined(AUTHENTICATION_FEATURE)
_auth = ESP3DAuthenticationLevel::guest;
#else
_auth = ESP3DAuthenticationLevel::admin;
#endif // AUTHENTICATION_FEATURE
}
ESP3DAuthenticationLevel ESP3DSerialService::getAuthentication() {
if (_needauthentication) {
return _auth;
}
return ESP3DAuthenticationLevel::admin;
}
// Setup Serial
bool ESP3DSerialService::begin(uint8_t serialIndex) {
_serialIndex = serialIndex - 1;
@ -133,12 +99,21 @@ bool ESP3DSerialService::end() {
return true;
}
// return the array of uint32_t and array size
const uint32_t *ESP3DSerialService::get_baudratelist(uint8_t *count) {
if (count) {
*count = sizeof(SupportedBaudList) / sizeof(uint32_t);
void ESP3DSerialService::flushData(const uint8_t *data, size_t size, ESP3DMessageType type) {
ESP3DMessage *message = esp3d_message_manager.newMsg(
_origin,
_id == MAIN_SERIAL ? ESP3DClientType::all_clients
: esp3d_commands.getOutputClient(),
data, size, getAuthentication());
if (message) {
message->type = type;
esp3d_log("Process Message");
esp3d_commands.process(message);
} else {
esp3d_log_e("Cannot create message");
}
return SupportedBaudList;
_lastflush = millis();
}
// Function which could be called in other loop
@ -165,36 +140,13 @@ void ESP3DSerialService::handle() {
// 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();
flushBuffer();
}
}
void ESP3DSerialService::flushbuffer() {
_buffer[_buffer_size] = 0x0;
// dispatch command
if (_started) {
ESP3DMessage *message = esp3d_message_manager.newMsg(
_origin,
_id == MAIN_SERIAL ? ESP3DClientType::all_clients
: esp3d_commands.getOutputClient(),
(uint8_t *)_buffer, _buffer_size, getAuthentication());
if (message) {
// process command
message->type = ESP3DMessageType::unique;
esp3d_commands.process(message);
} else {
esp3d_log_e("Cannot create message");
}
}
_lastflush = millis();
_buffer_size = 0;
}
// push collected data to buffer and proceed accordingly
void ESP3DSerialService::push2buffer(uint8_t *sbuf, size_t len) {
if (!_started) {
if (!_started || !_buffer) {
return;
}
esp3d_log("buffer get %d data ", len);
@ -250,7 +202,7 @@ void ESP3DSerialService::push2buffer(uint8_t *sbuf, size_t len) {
if (_buffer_size == datalen) {
esp3d_log("Flushing buffer");
if (isCommandFrame) {
flushbuffer();
flushBuffer();
} else {
MKSService::handleFrame(type, (const uint8_t *)_buffer,
_buffer_size);
@ -294,31 +246,15 @@ void ESP3DSerialService::push2buffer(uint8_t *sbuf, size_t len) {
#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 (esp3d_string::isPrintableChar(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
if (esp3d_string::isRealTimeCommand(sbuf[i])) {
flushChar(sbuf[i]);
} else {
_buffer[_buffer_size] = sbuf[i];
_buffer_size++;
flushbuffer();
if (_buffer_size > ESP3D_SERIAL_BUFFER_SIZE ||
_buffer[_buffer_size - 1] == '\n') {
flushBuffer();
}
}
}
#endif
@ -331,94 +267,12 @@ bool ESP3DSerialService::reset() {
ESP_BAUD_RATE, ESP3DSettings::getDefaultIntegerSetting(ESP_BAUD_RATE));
}
void ESP3DSerialService::updateBaudRate(uint32_t br) {
if (br != baudRate()) {
Serials[_serialIndex]->flush();
Serials[_serialIndex]->updateBaudRate(br);
}
}
// Get current baud rate
uint32_t ESP3DSerialService::baudRate() {
return Serials[_serialIndex]->baudRate();
}
size_t ESP3DSerialService::writeBytes(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 {
ESP3DHal::wait(5);
}
}
return sizesent;
}
}
size_t ESP3DSerialService::readBytes(uint8_t *sbuf, size_t len) {
if (!_started) {
return -1;
}
return Serials[_serialIndex]->readBytes(sbuf, len);
}
void ESP3DSerialService::flush() {
if (!_started) {
return;
}
Serials[_serialIndex]->flush();
}
void ESP3DSerialService::swap() {
#ifdef ARDUINO_ARCH_ESP8266
Serials[_serialIndex]->swap();
#endif // ARDUINO_ARCH_ESP8266
}
bool ESP3DSerialService::dispatch(ESP3DMessage *message) {
bool done = false;
// Only is serial service is started
if (_started) {
// Only if message is not null
if (message) {
// if message is not null
if (message->data && message->size != 0) {
if (writeBytes(message->data, message->size) == message->size) {
flush();
// Delete message now
esp3d_message_manager.deleteMsg(message);
done = true;
} else {
esp3d_log_e("Error while sending data");
}
} else {
esp3d_log_e("Error null data");
}
} else {
esp3d_log_e("Error null message");
}
}
return done;
}
#endif // COMMUNICATION_PROTOCOL == MKS_SERIAL || COMMUNICATION_PROTOCOL ==
// RAW_SERIAL
#endif // ARDUINO_ARCH_ESP8266

View File

@ -99,7 +99,7 @@ size_t Serial_2_Socket::write(const uint8_t *buffer, size_t size) {
for (int i = 0; i < size; i++) {
_TXbuffer[_TXbufferSize] = buffer[i];
_TXbufferSize++;
if (buffer[i] == (const uint8_t)'\n' || buffer[i] == (const uint8_t)'\r') {
if (buffer[i] == (const uint8_t)'\n') {
esp3d_log("S2S: %s TXSize: %d", (const char *)_TXbuffer, _TXbufferSize);
flush();
}

View File

@ -186,7 +186,7 @@ void Telnet_Server::handle() {
// we cannot left data in buffer too long
// in case some commands "forget" to add \n
if (((millis() - _lastflush) > TIMEOUT_TELNET_FLUSH) && (_buffer_size > 0)) {
flushbuffer();
flushBuffer();
}
}
@ -214,57 +214,48 @@ void Telnet_Server::initAuthentication() {
}
ESP3DAuthenticationLevel Telnet_Server::getAuthentication() { return _auth; }
void Telnet_Server::flushbuffer() {
if (!_buffer || !_started) {
_buffer_size = 0;
return;
}
_buffer[_buffer_size] = 0x0;
ESP3DMessage *msg = esp3d_message_manager.newMsg(
ESP3DClientType::telnet, esp3d_commands.getOutputClient(), _buffer,
_buffer_size, _auth);
if (msg) {
// process command
esp3d_commands.process(msg);
void Telnet_Server::flushData(const uint8_t *data, size_t size, ESP3DMessageType type) {
ESP3DMessage *message = esp3d_message_manager.newMsg(
ESP3DClientType::telnet, esp3d_commands.getOutputClient(), data,
size, _auth);
if (message) {
message->type = type;
esp3d_log("Process Message");
esp3d_commands.process(message);
} else {
esp3d_log_e("Cannot create message");
}
_lastflush = millis();
}
void Telnet_Server::flushChar(char c) { flushData((uint8_t *)&c, 1, ESP3DMessageType::realtimecmd); }
void Telnet_Server::flushBuffer() {
_buffer[_buffer_size] = 0x0;
flushData((uint8_t *)_buffer, _buffer_size, ESP3DMessageType::unique);
_buffer_size = 0;
}
void Telnet_Server::push2buffer(uint8_t *sbuf, size_t len) {
if (!_buffer) {
if (!_buffer || !_started) {
return;
}
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_TELNET_BUFFER_SIZE) {
_buffer[_buffer_size] = sbuf[i];
_buffer_size++;
}
flushbuffer();
} else if (esp3d_string::isPrintableChar(char(sbuf[i]))) {
if (_buffer_size < ESP3D_TELNET_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
if (esp3d_string::isRealTimeCommand(sbuf[i])) {
flushChar(sbuf[i]);
} else {
_buffer[_buffer_size] = sbuf[i];
_buffer_size++;
flushbuffer();
if (_buffer_size > ESP3D_TELNET_BUFFER_SIZE ||
_buffer[_buffer_size - 1] == '\n') {
flushBuffer();
}
}
}
}

View File

@ -62,7 +62,9 @@ class Telnet_Server {
uint8_t* _buffer;
size_t _buffer_size;
void push2buffer(uint8_t* sbuf, size_t len);
void flushbuffer();
void flushBuffer();
void flushChar(char c);
void flushData(const uint8_t* data, size_t size, ESP3DMessageType type);
};
extern Telnet_Server telnet_server;

View File

@ -174,15 +174,15 @@ void ESP3DUsbSerialService::receiveCb(const uint8_t *data, size_t data_len,
}
if (xSemaphoreTake(_buffer_mutex, portMAX_DELAY) == pdTRUE) {
for (size_t i = 0; i < data_len; i++) {
_buffer[_buffer_size] = data[i];
_buffer_size++;
if (_buffer_size > ESP3D_USB_SERIAL_BUFFER_SIZE ||
_buffer[_buffer_size - 1] == '\n' ||
_buffer[_buffer_size - 1] == '\r') {
if (_buffer[_buffer_size - 1] == '\r') {
_buffer[_buffer_size - 1] = '\n';
if (esp3d_string::isRealTimeCommand(data[i])) {
flushChar(data[i]);
} else {
_buffer[_buffer_size] = data[i];
_buffer_size++;
if (_buffer_size > ESP3D_USB_SERIAL_BUFFER_SIZE ||
_buffer[_buffer_size - 1] == '\n') {
flushBuffer();
}
flushbuffer();
}
}
xSemaphoreGive(_buffer_mutex);
@ -397,26 +397,31 @@ void ESP3DUsbSerialService::handle() {
}
}
void ESP3DUsbSerialService::flushbuffer() {
_buffer[_buffer_size] = 0x0;
if (_buffer_size == 1 && _buffer[0] == '\n') {
_buffer_size = 0;
void ESP3DUsbSerialService::flushData(const uint8_t *data, size_t size,
ESP3DMessageType type) {
if (!data || !_started) {
return;
}
// dispatch command
ESP3DMessage *message = esp3d_message_manager.newMsg(
_origin, ESP3DClientType::all_clients, (uint8_t *)_buffer, _buffer_size,
getAuthentication());
_origin, ESP3DClientType::all_clients, data, size, getAuthentication());
if (message) {
// process command
message->type = ESP3DMessageType::unique;
message->type = type;
esp3d_log("Message sent to fifo list");
_messagesInFIFO.push(message);
} else {
esp3d_log_e("Cannot create message");
}
_lastflush = millis();
}
void ESP3DUsbSerialService::flushChar(char c) {
flushData((uint8_t *)&c, 1, ESP3DMessageType::realtimecmd);
}
void ESP3DUsbSerialService::flushBuffer() {
_buffer[_buffer_size] = 0x0;
flushData((uint8_t *)_buffer, _buffer_size, ESP3DMessageType::unique);
_buffer_size = 0;
}

View File

@ -79,7 +79,9 @@ class ESP3DUsbSerialService final {
TaskHandle_t _xHandle;
ESP3DMessageFIFO _messagesInFIFO;
void flushbuffer();
void flushBuffer();
void flushChar(char c);
void flushData(const uint8_t* data, size_t size, ESP3DMessageType type);
};
extern ESP3DUsbSerialService esp3d_usb_serial_service;

View File

@ -281,36 +281,20 @@ size_t WebSocket_Server::writeBytes(const uint8_t *buffer, size_t size) {
}
void WebSocket_Server::push2RXbuffer(uint8_t *sbuf, size_t len) {
if (!_RXbuffer || !_started) {
if (!_RXbuffer || !_started || !sbuf) {
return;
}
for (size_t i = 0; i < len; i++) {
_lastRXflush = millis();
// command is defined
if ((char(sbuf[i]) == '\n') || (char(sbuf[i]) == '\r')) {
if (_RXbufferSize < RXBUFFERSIZE) {
_RXbuffer[_RXbufferSize] = sbuf[i];
_RXbufferSize++;
}
flushRXbuffer();
} else if (esp3d_string::isPrintableChar(char(sbuf[i]))) {
if (_RXbufferSize < RXBUFFERSIZE) {
_RXbuffer[_RXbufferSize] = sbuf[i];
_RXbufferSize++;
} else {
flushRXbuffer();
_RXbuffer[_RXbufferSize] = sbuf[i];
_RXbufferSize++;
}
} else { // it is not printable char
// clean buffer first
if (_RXbufferSize > 0) {
flushRXbuffer();
}
// process char
if (esp3d_string::isRealTimeCommand(sbuf[i])) {
flushRXChar(sbuf[i]);
} else {
_RXbuffer[_RXbufferSize] = sbuf[i];
_RXbufferSize++;
flushRXbuffer();
if (_RXbufferSize > RXBUFFERSIZE ||
_RXbuffer[_RXbufferSize - 1] == '\n') {
flushRXbuffer();
}
}
}
}
@ -324,22 +308,33 @@ void WebSocket_Server::initAuthentication() {
}
ESP3DAuthenticationLevel WebSocket_Server::getAuthentication() { return _auth; }
void WebSocket_Server::flushRXChar(char c) {
flushRXData((uint8_t *)&c, 1, ESP3DMessageType::realtimecmd);
}
void WebSocket_Server::flushRXbuffer() {
if (!_RXbuffer || !_started) {
_RXbufferSize = 0;
_RXbuffer[_RXbufferSize] = 0x0;
flushRXData((uint8_t *)_RXbuffer, _RXbufferSize, ESP3DMessageType::unique);
_RXbufferSize = 0;
}
void WebSocket_Server::flushRXData(const uint8_t *data, size_t size,
ESP3DMessageType type) {
if (!data || !_started) {
return;
}
_RXbuffer[_RXbufferSize] = 0x0;
ESP3DMessage *msg = esp3d_message_manager.newMsg(
_type, esp3d_commands.getOutputClient(), _RXbuffer, _RXbufferSize, _auth);
if (msg) {
// process command
esp3d_commands.process(msg);
ESP3DMessage *message = esp3d_message_manager.newMsg(
_type, esp3d_commands.getOutputClient(),
data, size, _auth);
if (message) {
message->type = type;
esp3d_log("Process Message");
esp3d_commands.process(message);
} else {
esp3d_log_e("Cannot create message");
}
_lastRXflush = millis();
_RXbufferSize = 0;
}
void WebSocket_Server::handle() {

View File

@ -70,6 +70,8 @@ class WebSocket_Server {
uint8_t _current_id;
void flushTXbuffer();
void flushRXbuffer();
void flushRXChar(char c);
void flushRXData(const uint8_t* data, size_t size, ESP3DMessageType type);
uint8_t *_RXbuffer;
uint16_t _RXbufferSize;
};

View File

@ -5,12 +5,30 @@ import serial.tools.list_ports
import esp3d_common as common
import marlin
import grbl
import grblhal
import repetier
import smoothieware
def isRealTimeCommand(c: int) -> bool:
# Convertit en entier si ce n'est pas déjà le cas
if isinstance(c, bytes):
c = c[0]
elif isinstance(c, str):
c = ord(c)
# Standard characters
if c in [ord('?'), ord('!'), ord('~'), 0x18]: # 0x18 is ^X
return True
# Range >= 0x80 et <= 0xA4
if 0x80 <= c <= 0xA4:
return True
return False
def main():
if len(sys.argv) < 2:
print("Please use one of the follwowing FW: marlin, repetier, smoothieware or grbl.")
print("Please use one of the follwowing FW: marlin, repetier, smoothieware, grbl or grblhal.")
return
fw_name = sys.argv[1].lower()
@ -23,41 +41,65 @@ def main():
fw = smoothieware
elif fw_name == "grbl":
fw = grbl
elif fw_name == "grblhal":
fw = grblhal
else:
print("Firmware not supported : {}".format(fw_name))
return
ports = serial.tools.list_ports.comports()
portTFT = ""
portBoard = ""
print(common.bcolors.COL_GREEN+"Serial ports detected: "+common.bcolors.END_COL)
for port, desc, hwid in sorted(ports):
print(common.bcolors.COL_GREEN+" - {}: {} ".format(port, desc)+common.bcolors.END_COL)
desc.capitalize()
if (desc.find("SERIAL") != -1 or desc.find("UART") != -1):
portTFT = port
portBoard = port
print(common.bcolors.COL_GREEN +
"Found " + portTFT + " for ESP3D"+common.bcolors.END_COL)
"Found " + portBoard + " for ESP3D"+common.bcolors.END_COL)
break
print(common.bcolors.COL_GREEN+"Open port " + str(port)+common.bcolors.END_COL)
if (portTFT == ""):
if (portBoard == ""):
print(common.bcolors.COL_RED+"No serial port found"+common.bcolors.END_COL)
exit(0)
ser = serial.Serial(portTFT, 1000000)
ser = serial.Serial(portBoard, 115200, timeout=1)
print(common.bcolors.COL_GREEN+"Now Simulating: " + fw_name + common.bcolors.END_COL)
starttime = common.current_milli_time()
# loop forever, just unplug the port to stop the program or do ctrl-c
buffer = bytearray()
while True:
try:
if ser.in_waiting:
line = ser.readline().decode('utf-8').strip()
print(common.bcolors.COL_BLUE+line+common.bcolors.END_COL)
#ignore log lines from TFT
if not line.startswith("["):
response = fw.processLine(line, ser)
if (response != ""):
# Lire un caractère
char = ser.read(1)
if not char: # Timeout
continue
# Vérifier si c'est une commande temps réel
if isRealTimeCommand(char[0]) and (fw_name == "grbl" or fw_name == "grblhal"):
# Traiter immédiatement la commande temps réel
cmd = char.decode('utf-8', errors='replace')
print(common.bcolors.COL_BLUE + f"RealTime command: {cmd}" + common.bcolors.END_COL)
response = fw.processLine(cmd, ser)
if response:
common.send_echo(ser, response)
else:
# Ajouter au buffer
buffer.extend(char)
# Si on trouve une fin de ligne, traiter la ligne
if char == b'\n':
line = buffer.decode('utf-8').strip()
print(common.bcolors.COL_BLUE + line + common.bcolors.END_COL)
if not line.startswith("["):
response = fw.processLine(line, ser)
if response:
common.send_echo(ser, response)
buffer.clear()
except KeyboardInterrupt:
print(common.bcolors.COL_GREEN+"End of program"+common.bcolors.END_COL)
exit(0)
break
except Exception as e:
print(f"Error: {e}")
buffer.clear()
# call main function

View File

@ -0,0 +1,231 @@
#!/usr/bin/python
# Marlin GCODE parser / responder
import time
import re
import random
import esp3d_common as common
positions = {
"X": 0.0,
"Y": 0.0,
"Z": 0.0,
"A": 0.0,
"B": 0.0,
"C": 0.0
}
modes = {
"absolute": True
}
report_counter = 0
def wait(durationms, ser):
nowtime = common.current_milli_time()
while (common.current_milli_time() < nowtime + durationms):
if ser.in_waiting:
line = ser.readline().decode('utf-8').strip()
print(common.bcolors.COL_PURPLE+line+common.bcolors.END_COL)
def ok(line):
if (not line.startswith("N")):
return "ok (" + line + ")"
N = re.findall(r'N\d*', line)
if (len(N) > 0):
return "ok " + N[0][1:]
# build the response for the busy response,
# simulating the delay of the busy response
def send_busy(ser, nb):
v = nb
while (v > 0):
# FIXME: the message is not this one on grbl
# common.send_echo(ser, "echo:busy: processing")
wait(1000, ser)
v = v - 1
# G0/G1 response
def G0_G1_response(cmd, line, ser):
global positions
X_val = ""
Y_val = ""
Z_val = ""
A_val = ""
B_val = ""
C_val = ""
# extract X
X = re.findall(r'X[+]*[-]*\d+[\.]*\d*', cmd)
if (len(X) > 0):
X_val = X[0][1:]
# extract Y
Y = re.findall(r'Y[+]*[-]*\d+[\.]*\d*', cmd)
if (len(Y) > 0):
Y_val = Y[0][1:]
# extract Z
Z = re.findall(r'Z[+]*[-]*\d+[\.]*\d*', cmd)
if (len(Z) > 0):
Z_val = Z[0][1:]
# extract A
A = re.findall(r'A[+]*[-]*\d+[\.]*\d*', cmd)
if (len(A) > 0):
A_val = A[0][1:]
# extract B
B = re.findall(r'B[+]*[-]*\d+[\.]*\d*', cmd)
if (len(B) > 0):
B_val = B[0][1:]
# extract C
C = re.findall(r'C[+]*[-]*\d+[\.]*\d*', cmd)
if (len(C) > 0):
C_val = C[0][1:]
if (modes["absolute"]):
if (X_val != ""):
positions["X"] = float(X_val)
if (Y_val != ""):
positions["Y"] = float(Y_val)
if (Z_val != ""):
positions["Z"] = float(Z_val)
if (A_val!= ""):
positions["A"] = float(A_val)
if (B_val!= ""):
positions["B"] = float(B_val)
if (C_val!= ""):
positions["C"] = float(C_val)
return ok(line)
else:
if (X_val != ""):
positions["X"] += float(X_val)
if (Y_val != ""):
positions["Y"] += float(Y_val)
if (Z_val != ""):
positions["Z"] += float(Z_val)
if (A_val!= ""):
positions["A"] += float(A_val)
if (B_val!= ""):
positions["B"] += float(B_val)
if (C_val!= ""):
positions["C"] += float(C_val)
return ok(line)
# $H response
def Home_response(cmd, line, ser):
global positions
send_busy(ser, 3)
if (cmd.find("X") != -1):
positions["X"] = 0.00
if (cmd.find("Y") != -1):
positions["Y"] = 0.00
if (cmd.find("Z") != -1):
positions["Z"] = 0.00
if (cmd.find("A")!= -1):
positions["A"] = 0.00
if (cmd.find("B")!= -1):
positions["B"] = 0.00
if (cmd.find("C")!= -1):
positions["C"] = 0.00
if (cmd == "G28"):
positions["X"] = 0.00
positions["Y"] = 0.00
positions["Z"] = 0.00
positions["A"] = 0.00
positiones["B"] = 0.00
positions["C"] = 0.00
return ok(line)
# Absolute mode
def G90_response(cmd, line, ser):
global modes
modes["absolute"] = True
return ok(line)
# Relative mode
def G91_response(cmd, line, ser):
global modes
modes["absolute"] = False
return ok(line)
def Jog_response(cmd, line, ser):
if (cmd.find("G91")!= -1):
G91_response(cmd, line, ser)
elif (cmd.find("G90")!= -1):
G90_response(cmd, line, ser)
cmd = cmd.replace("G90", "")
cmd = cmd.replace("G91", "")
cmd = cmd.replace("G21", "")
cmd = cmd.strip()
return G0_G1_response(cmd, line, ser)
# status response
# "<Idle|MPos:0.000,0.000,0.000,1.000,1.000|FS:0,0|WCO:0.000,0.000,0.000,1.000,1.000>\n"
# "<Idle|MPos:0.000,0.000,0.000,1.000,1.000|FS:0,0|A:S|Pn:P>\n"
# "<Idle|MPos:0.000,0.000,0.000,1.000,1.000|FS:0,0|Ov:100,100,100|Pn:XYZ>\n"
def status_response(cmd, line, ser):
global positions
global report_counter
wpco = ""
ov = ""
fs = "|FS:0,0"
astate = ""
pn = ""
status = "Idle"
report_counter += 1
if report_counter == 11:
report_counter = 1
if report_counter == 1:
wpco = "|WCO:0.000,0.000,0.000,1.000,1.000,1.000"
if report_counter == 2:
#FIXME: use variable to report the override values
ov = "|Ov:100,100,100"
pn = "|Pn:XYZ"
if report_counter >= 3:
astate = "|A:S"
pn = "|Pn:P"
position = "|MPos:" + "{:.3f}".format(positions["X"]) + "," + "{:.3f}".format(
positions["Y"]) + "," + "{:.3f}".format(positions["Z"]) + "," + "{:.3f}".format(positions["A"]) + "," + "{:.3f}".format(positions["B"]) + "," + "{:.3f}".format(positions["C"])
return "<" + status + position + fs + wpco + ov + astate + pn + ">\n"
# List of supported methods
methods = [
{"str": "G0", "fn": G0_G1_response},
{"str": "G1", "fn": G0_G1_response},
{"str": "$H", "fn": Home_response},
{"str": "$J=", "fn": Jog_response},
{"str": "G90", "fn": G90_response},
{"str": "G91", "fn": G91_response},
{"str": "?", "fn": status_response},
]
# Process a line of GCODE
def processLine(line, ser):
time.sleep(0.01)
cmd = line
if (line.startswith("N")):
p = line.find(' ')
cmd = line[p+1:]
p = cmd.rfind('*')
cmd = cmd[:p]
global methods
for method in methods:
if cmd.startswith(method["str"]):
return method["fn"](cmd, line, ser)
if line.startswith("M") or line.startswith("G") or line.startswith("N") or line.startswith("$"):
return ok(line)
if line.find("[esp") != -1 or line.find("[0;") != -1 or line.find("[1;") != -1:
return ""
if line.startswith("ESP-ROM") or line.startswith("Build:") or line.startswith("SPIWP:") or line.startswith("mode:")or line.startswith("load:") or line.startswith("entry "):
return ""
#FIXME: this is not grbl response if the command is not recognized
return "echo:Unknown command: \"" + line + "\"\nok"

View File

@ -0,0 +1,33 @@
import serial
import time
# Configuration du port série
ser = serial.Serial(
port='COM4', # Port série Windows
baudrate=115200, # Vitesse en bauds
timeout=1 # Timeout en secondes
)
def format_char(c):
# Convertit un caractère en sa représentation lisible
if c == b'\r':
return '\\r'
elif c == b'\n':
return '\\n'
else:
return f"{c.decode('ascii', errors='replace')}({ord(c)})"
try:
print("Lecture du port série COM4. Ctrl+C pour arrêter.")
while True:
if ser.in_waiting > 0:
# Lire un caractère à la fois
char = ser.read(1)
print(format_char(char), end=' ', flush=True)
time.sleep(0.01) # Petit délai pour ne pas surcharger le CPU
except KeyboardInterrupt:
print("\nArrêt du programme")
finally:
ser.close()
print("Port série fermé")