#include <WiFi.h>
#include <LITTLEFS.h>
#include <ESPAsyncWebServer.h>
#include <WebSocketsServer.h>
#include <queue>
#include <vector>
#include <ArduinoJson.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <map>
#include <set>
#include <sstream>
#define FORMAT_LITTLEFS_IF_FAILED false
#define RXD2 16
#define TXD2 17
// Constants
const int http_port = 80;
const int led_pin = 2;
// Globals
AsyncWebServer server(http_port);
WebSocketsServer webSocket = WebSocketsServer(81);
// Gcode Queue
std::queue<char *> gcodeQueue;
bool waitingForOk = false;
/***********************************************************
Functions
*/
// Callback: receiving any WebSocket message
void onWebSocketEvent(uint8_t client_num,
WStype_t type,
uint8_t *payload,
size_t length)
{
// Figure out the type of WebSocket event
switch (type)
{
// Client has disconnected
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected!\n", client_num);
if (client_num == 0)
{
digitalWrite(led_pin, LOW);
}
break;
// New client has connected
case WStype_CONNECTED:
{
IPAddress ip = webSocket.remoteIP(client_num);
Serial.printf("[%u] Connection from ", client_num);
Serial.println(ip.toString());
if (client_num == 0)
{
// main client connected
// Get the config.json file
File configjson = LITTLEFS.open("/config/config.json", "r");
if (!configjson)
{
Serial.println("Failed to open config.json");
return;
}
// Parse the JSON file
StaticJsonDocument<512> doc;
DeserializationError error = deserializeJson(doc, configjson);
// Close the file as soon as you're done with it
configjson.close();
if (error)
{
Serial.println("Failed to parse config.json");
return;
}
// Get the "turnoffboardled" boolean from the config.json file
bool turnoffboardled = doc["turnoffboardled"];
if (!turnoffboardled)
{
// main client connected and "turnoffboardled" is false
digitalWrite(led_pin, HIGH);
}
}
}
break;
// For everything else: do nothing
case WStype_TEXT:
Serial.printf("[%u] Received text: %s\n", client_num, payload);
if (strcmp((char *)payload, "LEDON") == 0)
{
digitalWrite(led_pin, HIGH);
}
else if (strcmp((char *)payload, "LEDOFF") == 0)
{
digitalWrite(led_pin, LOW);
}
break;
case WStype_BIN:
case WStype_ERROR:
case WStype_FRAGMENT_TEXT_START:
case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT:
case WStype_FRAGMENT_FIN:
default:
break;
}
}
// Callback: send 404 if requested file does not exist
void onPageNotFound(AsyncWebServerRequest *request)
{
IPAddress remote_ip = request->client()->remoteIP();
Serial.println("[" + remote_ip.toString() +
"] HTTP GET request of " + request->url());
request->send(404, "text/plain", "Not found");
}
/***********************************************************
Main
*/
void setup()
{
// Set LED pin as output
pinMode(led_pin, OUTPUT);
// Start Serial port
Serial.begin(115200);
Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2);
// Make sure we can read the file system
if (!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED))
{
Serial.println("Error mounting SPIFFS");
return;
}
int tBytes = LITTLEFS.totalBytes();
int uBytes = LITTLEFS.usedBytes();
Serial.println("File system info");
Serial.print("Total bytes: ");
Serial.println(tBytes);
Serial.print("Used bytes: ");
Serial.println(uBytes);
// Get the Network name and password
File networkjson = LITTLEFS.open("/config/config.json", "r");
if (!networkjson || networkjson.isDirectory())
{
Serial.println("Failed to open config.json");
return;
}
// Parse the JSON file
StaticJsonDocument<512> doc;
DeserializationError error = deserializeJson(doc, networkjson);
// Close the file as soon as you're done with it
networkjson.close();
if (error)
{
Serial.println("Failed to parse config.json");
return;
}
// Get network name and password
const char *networkname = doc["networkname"];
const char *networkpassword = doc["networkpassword"];
Serial.println("Network name: " + String(networkname));
Serial.println("Network password: " + String(networkpassword));
// Start access point
WiFi.softAP(networkname, networkpassword);
// Print our IP address
Serial.println();
Serial.println("AP running");
Serial.print("My IP address: ");
Serial.println(WiFi.softAPIP());
server.serveStatic("/", LITTLEFS, "/");
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
AsyncWebServerResponse* response = request->beginResponse(LITTLEFS, "/index.html", "text/html");
response->addHeader("Content-Encoding", "gzip");
request->send(response); });
server.on(
"/upload", HTTP_POST, [](AsyncWebServerRequest *request)
{ request->send(200); },
handleUpload);
server.on(
"/gcode", HTTP_POST, [](AsyncWebServerRequest *request)
{ request->send(200); },
handleGcode);
// Handle file delete
server.on("/delete", HTTP_DELETE, [](AsyncWebServerRequest *request)
{
if (request->hasParam("filename")) { // if the request has a parameter named "filename"
String filename = request->getParam("filename")->value(); // get the value of the "filename" parameter
if (LITTLEFS.remove("/" + filename)) { // attempt to delete the file
request->send(200, "text/plain", "Successfull deleted file: " + filename); // if successful, send a 200 OK response
Serial.println("Deleted file: " + filename);
} else {
request->send(500, "text/plain", "Failed to delete file"); // if unsuccessful, send a 500 Internal Server Error response
}
} else {
request->send(400, "text/plain", "Bad request"); // if the request does not have a "filename" parameter, send a 400 Bad Request response
} });
// Handle GET request for file list
server.on("/files", HTTP_GET, [](AsyncWebServerRequest *request)
{
if (request->hasParam("directory")) {
String directory = request->getParam("directory")->value();
String fileList = "";
File root = LITTLEFS.open(directory);
if (root && root.isDirectory()) {
File file = root.openNextFile();
while (file) {
String fileNameWithPath = file.name();
String fileName = fileNameWithPath.substring(directory.length());
fileList += fileName;
fileList += "\n";
file = root.openNextFile();
}
}
Serial.println(fileList);
request->send(200, "text/plain", fileList);
} else {
request->send(400, "text/plain", "Bad request");
} });
// Handle bytes check
server.on("/space", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(200, "text/plain", String(LITTLEFS.totalBytes()) + "," + String(LITTLEFS.usedBytes())); });
server.on("/writeinput", HTTP_POST, [](AsyncWebServerRequest *request)
{
if (request->hasParam("text", true)) {
String text = request->getParam("text", true)->value();
Serial.println("Text: " + text);
TextToGCODE(text);
request->send(200);
} else {
request->send(400);
} });
// Handle requests for pages that do not exist
server.onNotFound(onPageNotFound);
// Start web server
server.begin();
// Start WebSocket server and assign callback
webSocket.begin();
// enable heartbeat for websocket
webSocket.enableHeartbeat(15000, 3000, 2);
webSocket.onEvent(onWebSocketEvent);
}
#define RX_BUFFER_SIZE 128 // adjust this to your needs
int g_count = 0;
int l_count = 0;
int error_count = 0;
bool verbose = true;
int len = 0;
void loop()
{
webSocket.loop();
char *currentBlock = nullptr; // Declare currentBlock outside the if block
if (!gcodeQueue.empty())
{
bool okReceived = false;
currentBlock = gcodeQueue.front();
gcodeQueue.pop();
len = strlen(currentBlock) + 1;
Serial.println(" LEN: " + String(len));
while (len >= RX_BUFFER_SIZE - 1 || Serial2.available())
{
char out_temp[RX_BUFFER_SIZE];
Serial2.readBytesUntil('\n', out_temp, RX_BUFFER_SIZE);
if (strstr(out_temp, "ok") == NULL && strstr(out_temp, "error") == NULL && strstr(out_temp, "[Pgm End]") == NULL)
{
Serial.println(" MSG: \"" + String(out_temp) + "\""); // Debug response
}
else
{
if (!okReceived && (strstr(out_temp, "ok") != NULL || strstr(out_temp, "[Pgm End]") != NULL))
{
okReceived = true;
g_count += 1;
len -= strlen(currentBlock) + 1;
if (verbose)
Serial.println(" REC<" + String(g_count) + ": \"" + String(out_temp) + "\"");
// If [Pgm End] is received, send "PE" over the websocket
if (strstr(out_temp, "[Pgm End]") != NULL)
{
Serial.println("Program End");
webSocket.broadcastTXT("PE");
}
}
if (strstr(out_temp, "error") != NULL)
error_count += 1;
}
}
// Append newline character to currentBlock
char currentBlockWithNewline[strlen(currentBlock) + 2];
strcpy(currentBlockWithNewline, currentBlock);
strcat(currentBlockWithNewline, "\n");
// Convert currentBlockWithNewline to byte array and send it
Serial2.write((const uint8_t *)currentBlockWithNewline, strlen(currentBlockWithNewline));
if (verbose)
Serial.println("SND>" + String(l_count) + ": \"" + String(currentBlock) + "\"");
l_count += 1;
// Yield to allow the ESP32 to handle the WebSocket events
yield();
}
// Wait until all responses have been received.
while (l_count > g_count)
{
bool okReceived = false;
char out_temp[RX_BUFFER_SIZE];
Serial2.readBytesUntil('\n', out_temp, RX_BUFFER_SIZE);
if (strstr(out_temp, "ok") == NULL && strstr(out_temp, "error") == NULL && strstr(out_temp, "[Pgm End]") == NULL)
{
Serial.println(" MSG: \"" + String(out_temp) + "\""); // Debug response
}
else
{
if (!okReceived && (strstr(out_temp, "ok") != NULL || strstr(out_temp, "[Pgm End]") != NULL))
{
okReceived = true;
g_count += 1;
len -= strlen(currentBlock) + 1;
if (verbose)
Serial.println(" REC<" + String(g_count) + ": \"" + String(out_temp) + "\"");
// If [Pgm End] is received, send "PE" over the websocket
if (strstr(out_temp, "[Pgm End]") != NULL)
{
Serial.println("Program End");
webSocket.broadcastTXT("PE");
}
}
if (strstr(out_temp, "error") != NULL)
error_count += 1;
}
// Yield to allow the ESP32 to handle the WebSocket events
yield();
}
}
void ReceivedGCodeData(uint8_t *payload, size_t length)
{
// Tokenize the payload into lines
char *token;
char *saveptr;
token = strtok_r((char *)payload, ",", &saveptr);
while (token != NULL)
{
byte mode = 0;
if (strcmp(token, "M7") == 0)
{
mode = 1;
}
if (strcmp(token, "M8") == 0)
{
mode = 1;
}
if (strcmp(token, "M2") == 0)
{
mode = 1;
}
if (strncmp(token, "G1", 2) == 0)
{
mode = 1;
}
if (strncmp(token, "G0", 2) == 0)
{
mode = 1;
}
if (strcmp(token, "G21") == 0)
{
mode = 1;
}
if (strcmp(token, "G90") == 0)
{
mode = 1;
}
if (strncmp(token, "G92", 3) == 0)
{
mode = 1;
}
if (strcmp(token, "F1250") == 0)
{
mode = 1;
}
if (mode == 1)
{
char *gcodeLine = new char[20];
strcpy(gcodeLine, token);
gcodeQueue.push(gcodeLine);
}
token = strtok_r(NULL, ",", &saveptr);
}
// Flush the Serial2 buffer
if (gcodeQueue.empty())
{
serialFlush();
}
}
void serialFlush()
{
while (Serial2.available() > 0)
{
char t = Serial2.read();
}
}
// handles uploads
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
{
String logmessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url();
Serial.println(logmessage);
if (!index)
{
logmessage = "Upload Start: " + String(filename);
// open the file on first call and store the file handle in the request object
request->_tempFile = LITTLEFS.open("/" + filename, "w");
Serial.println(logmessage);
}
if (len)
{
// stream the incoming chunk to the opened file
request->_tempFile.write(data, len);
logmessage = "Writing file: " + String(filename) + " index=" + String(index) + " len=" + String(len);
Serial.println(logmessage);
}
if (final)
{
logmessage = "Upload Complete: " + String(filename) + ",size: " + String(index + len);
// close the file handle as the upload is now done
request->_tempFile.close();
Serial.println(logmessage);
}
}
// handles gcode file uploads
void handleGcode(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
{
static std::vector<uint8_t> buffer;
if (!index)
{
Serial.println("Gcode received");
// First check if gcodequeue is empty and if yes wake up grbl
if (gcodeQueue.empty())
{
Serial.println("Queue is empty");
const char *message = "\r\n\r\n";
Serial2.write((const uint8_t *)message, strlen(message));
}
// Clear the buffer
buffer.clear();
}
if (len)
{
// Append the incoming data to the buffer
buffer.insert(buffer.end(), data, data + len);
}
if (final)
{
Serial.println("Gcode upload complete");
// Send the buffer to ReceivedGCodeData
ReceivedGCodeData(buffer.data(), buffer.size());
// Clear the buffer
buffer.clear();
}
}
void TextToGCODE(String text)
{
// Get the config variables from the config.json
File configjson = LITTLEFS.open("/config/config.json", "r");
if (!configjson || configjson.isDirectory())
{
Serial.println("Failed to open config.json");
return;
}
// Parse the JSON file
StaticJsonDocument<512> doc;
DeserializationError error = deserializeJson(doc, configjson);
// Close the file as soon as you're done with it
configjson.close();
if (error)
{
Serial.println("Failed to parse config.json");
return;
}
// Declare the variables
float lineSpacing;
float lineLength;
bool restatreservoir = doc["restatreservoir"];
int linesselected = doc["linesselected"];
bool usecustomchars = doc["usecustomchars"];
bool useverticalwriting = doc["useverticalwriting"];
bool fulldrawingarea = doc["fulldrawingarea"];
String quality = doc["quality"];
float qualityfactor = 0;
if (quality == "low")
{
qualityfactor = 0.6;
}
else if (quality == "medium")
{
qualityfactor = 0.4;
}
else if (quality == "high")
{
qualityfactor = 0.2;
}
if (!useverticalwriting)
{
lineLength = 40;
}
else if (useverticalwriting && !fulldrawingarea)
{
lineLength = 29.5;
}
else if (useverticalwriting && fulldrawingarea)
{
lineLength = 39.5;
}
if (!useverticalwriting && !fulldrawingarea)
{
switch (linesselected)
{
case 1:
lineSpacing = 0;
break;
case 2:
lineSpacing = 15;
break;
case 3:
lineSpacing = 10;
break;
case 4:
lineSpacing = 7.5;
break;
default:
lineSpacing = 0;
break;
}
}
else if (useverticalwriting && !fulldrawingarea)
{
switch (linesselected)
{
case 1:
lineSpacing = 0;
break;
case 2:
lineSpacing = 17;
break;
case 3:
lineSpacing = 12;
break;
case 4:
lineSpacing = 9;
break;
default:
lineSpacing = 0;
break;
}
}
else if (!useverticalwriting && fulldrawingarea)
{
switch (linesselected)
{
case 1:
lineSpacing = 0;
break;
case 2:
lineSpacing = 18;
break;
case 3:
lineSpacing = 12;
break;
case 4:
lineSpacing = 9;
break;
default:
lineSpacing = 0;
break;
}
}
else if (useverticalwriting && fulldrawingarea)
{
switch (linesselected)
{
case 1:
lineSpacing = 0;
break;
case 2:
lineSpacing = 18;
break;
case 3:
lineSpacing = 12;
break;
case 4:
lineSpacing = 9.33;
break;
default:
lineSpacing = 0;
break;
}
}
Serial.println("GCode Dir: chars/default");
Serial.println("Line Length: " + String(lineLength));
Serial.println("Line Spacing: " + String(lineSpacing));
// Pass this to the mainwrite function
mainwrite(text, "chars/default", lineLength, lineSpacing, linesselected, useverticalwriting, fulldrawingarea, usecustomchars, qualityfactor, restatreservoir);
}
class Instr
{
public:
std::string command;
float x, y;
Instr(std::string command, float x = NAN, float y = NAN) : command(command), x(x), y(y) {}
Instr translated(float dx, float dy, bool useverticalwriting)
{
if (!isnan(x) && !isnan(y))
{
float newX = x + dx;
float newY = y + dy;
if (useverticalwriting)
{
// Rotate 90 degrees to the left
float temp = newX;
newX = newY;
newY = -temp;
}
return Instr(command, newX, newY);
}
return Instr(command);
}
};
class Letter
{
public:
std::vector<Instr> instructions; // Declare instructions field
float width; // Declare width field
Letter() : width(0) {} // Default constructor
Letter(std::vector<Instr> instructions, float width) : instructions(instructions), width(width) {}
Letter translated(float dx, float dy, bool useverticalwriting)
{
std::vector<Instr> newInstructions;
for (auto &instr : instructions)
{
newInstructions.push_back(instr.translated(dx, dy, useverticalwriting));
}
return Letter(newInstructions, width);
}
Letter scaled(float scalingFactor)
{
// Maximum scaling factor (only downscale, not upscale)
const float maxScalingFactor = 1.0;
// If the scaling factor is greater than the maximum, set it to the maximum
if (scalingFactor > maxScalingFactor)
scalingFactor = maxScalingFactor;
std::vector<Instr> newInstructions;
for (auto &instr : instructions)
{
newInstructions.push_back(Instr(instr.command, instr.x * scalingFactor, instr.y));
}
return Letter(newInstructions, width * scalingFactor);
}
};
float getScaleFactor(int linesselected, bool useverticalwriting, bool fulldrawingarea)
{
float scaleFactor;
if (linesselected == 1)
{
if (fulldrawingarea)
{
scaleFactor = useverticalwriting ? 4.0 : 3.9;
}
else
{
scaleFactor = useverticalwriting ? 4.0 : 3.2;
}
}
else if (linesselected == 2)
{
if (fulldrawingarea)
{
scaleFactor = useverticalwriting ? 2.2 : 2.1;
}
else
{
scaleFactor = useverticalwriting ? 2.3 : 1.75;
}
}
else if (linesselected == 3)
{
if (fulldrawingarea)
{
scaleFactor = useverticalwriting ? 1.6 : 1.5;
}
else
{
scaleFactor = useverticalwriting ? 1.5 : 1.25;
}
}
else if (linesselected == 4)
{
if (fulldrawingarea)
{
scaleFactor = useverticalwriting ? 1.2 : 1.2;
}
else
{
scaleFactor = useverticalwriting ? 1.2 : 0.9;
}
}
return scaleFactor;
}
String getRandomVariant(String character, std::vector<String> allchars)
{
std::vector<String> variants;
for (const auto &item : allchars)
{
if (item.indexOf("/Variante") != -1 &&
item.indexOf("=" + character + ".gcode") != -1)
{
variants.push_back(item);
}
}
if (!variants.empty())
{
return variants[rand() % variants.size()];
}
return "";
}
std::map<String, Letter> readLetters(String directory, String text, int linesselected, bool useverticalwriting, bool fulldrawingarea, bool usecustomchars)
{
std::map<String, Letter> letters;
std::set<String> processedChars; // Keep track of processed characters
Serial.println("Reading Letters for: " + text);
// Get all chars
std::vector<String> allchars;
if (usecustomchars)
{
File root = LITTLEFS.open("/chars/custom");
if (root && root.isDirectory())
{
File file = root.openNextFile();
while (file)
{
String fileNameWithPath = file.name();
String fileName = fileNameWithPath.substring(13);
if (fileName.endsWith(".gcode"))
{
allchars.push_back(fileName);
}
file = root.openNextFile();
}
}
Serial.println("All chars:");
for (auto &c : allchars)
{
Serial.println(c);
}
}
for (unsigned int i = 0; i < text.length(); i += 8)
{
String c = text.substring(i, i + 8);
// Skip if character has already been processed
if (processedChars.find(c) != processedChars.end())
{
continue;
}
// Add character to processed set
processedChars.insert(c);
Serial.printf("Character: %s", c.c_str());
Serial.println();
// Handle new line character
if (c == "0a000000")
{
continue;
}
// Handle white space
if (c == "20000000")
{
// Create a new Letter with a width of 5 and add it to the letters map
letters[c] = Letter(std::vector<Instr>(), 5);
continue;
}
String path = "/" + directory + "/" + c + ".gcode";
// Handle custom characters
if (usecustomchars)
{
// Get a random variant
String variant = getRandomVariant(c, allchars);
if (variant.length() == 0)
{
path = "/" + directory + "/" + c + ".gcode";
}
else
{
path = "/chars/custom" + variant;
}
}
Serial.println(path);
if (!LITTLEFS.exists(path))
continue;
File file = LITTLEFS.open(path, "r");
if (!file)
continue;
std::vector<Instr> instructions;
float scaleFactor = getScaleFactor(linesselected, useverticalwriting, fulldrawingarea);
while (file.available())
{
const char *line = file.readStringUntil('\n').c_str();
std::istringstream iss(line);
std::string segment;
while (std::getline(iss, segment, ','))
{
std::istringstream segmentStream(segment);
std::string command;
float x = 0.0, y = 0.0;
// Extract command
segmentStream >> command;
if (command == "M7" || command == "M8")
{
instructions.push_back(Instr(command));
continue;
}
// Extract X and Y values
std::string temp;
while (segmentStream >> temp)
{
if (temp[0] == 'X')
x = atof(temp.substr(1).c_str()); // Convert string to float
else if (temp[0] == 'Y')
y = atof(temp.substr(1).c_str()); // Convert string to float
}
instructions.push_back(Instr(command, x * scaleFactor, y * scaleFactor));
}
}
file.close();
float width = 0;
for (auto &instr : instructions)
{
if (instr.x > width)
width = instr.x;
}
letters[c] = Letter(instructions, width);
}
return letters;
}
int currentline = 1;
std::vector<Instr> textToGcode(String text, std::map<String, Letter> &letters, float lineLength, float lineSpacing, bool useverticalwriting)
{
// Calculate total width of characters
float totalWidth = 0;
for (unsigned int i = 0; i < text.length(); i += 8)
{
String s = text.substring(i, i + 8);
if (letters.find(s) != letters.end())
{
totalWidth += letters[s].width;
}
}
Serial.println("Total Width: " + String(totalWidth));
// Calculate starting x-coordinate for center alignment
float startX = 0;
if (totalWidth < lineLength)
{
startX = (lineLength - totalWidth) / 2;
}
std::vector<Instr> gcode;
float x = startX; // Start from startX
float y = 0; // Apply padding on top
// Calculate scaling factor
float scalingFactor = lineLength / totalWidth;
currentline = 1;
for (unsigned int i = 0; i < text.length(); i += 8)
{
String s = text.substring(i, i + 8);
if (s == "0a000000" || x > lineLength)
{
currentline++;
x = startX; // Reset x-coordinate for new line
y += lineSpacing; // Apply padding on bottom
}
if (letters.find(s) != letters.end())
{
Letter letter = letters[s].scaled(scalingFactor).translated(x, y, useverticalwriting);
gcode.insert(gcode.end(), letter.instructions.begin(), letter.instructions.end());
x += letter.width; // Adjust x-coordinate by scaled width
}
}
return gcode;
}
std::vector<Instr> compressGcode(std::vector<Instr> &gcode, float scalingquality = 0)
{
std::vector<Instr> compressedGcode;
float lastX = NAN, lastY = NAN;
for (const auto &instr : gcode)
{
if (instr.command == "G1" && !isnan(instr.x) && !isnan(instr.y))
{
if (!isnan(lastX) && !isnan(lastY))
{
if (abs(instr.x - lastX) < scalingquality && abs(instr.y - lastY) < scalingquality)
{
// Skip this command
continue;
}
}
lastX = instr.x;
lastY = instr.y;
}
compressedGcode.push_back(instr);
}
return compressedGcode;
}
void mainwrite(String text, String gcodedir, float lineLength, float lineSpacing, int linesselected, bool useverticalwriting, bool fulldrawingarea, bool usecustomchars, float qualityfactor, bool restatreservoir)
{
// Wakeup grbl
if (gcodeQueue.empty())
{
const char *message = "\r\n\r\n";
Serial2.write((const uint8_t *)message, strlen(message));
}
// Get the letters
std::map<String, Letter> letters = readLetters(gcodedir, text, linesselected, useverticalwriting, fulldrawingarea, usecustomchars);
// Convert text to gcode
std::vector<Instr> gcode = textToGcode(text, letters, lineLength, lineSpacing, useverticalwriting);
if (gcode.empty())
{
Serial.println("No gcode generated");
return;
}
// Compress the gcode with the quality factor
gcode = compressGcode(gcode, qualityfactor);
// Create a new Instr for each command
Instr instrG21 = {"G21", NAN, NAN};
Instr instrG90 = {"G90", NAN, NAN};
Instr instrF1250 = {"F1250", NAN, NAN};
Instr instrM8 = {"M8", NAN, NAN};
Instr instrG92 = {"G92", NAN, NAN};
// Insert the new instructions at the beginning of the gcode vector
if (useverticalwriting && !restatreservoir && fulldrawingarea)
{
instrG92.x = 0;
instrG92.y = -39.5;
gcode.insert(gcode.begin(), {instrG21, instrG90, instrF1250, instrM8, instrG92});
}
else if (useverticalwriting && !fulldrawingarea && !restatreservoir)
{
instrG92.x = 0;
instrG92.y = -29.5;
gcode.insert(gcode.begin(), {instrG21, instrG90, instrF1250, instrM8, instrG92});
}
else if (!useverticalwriting && !restatreservoir)
{
instrG92.x = 0;
instrG92.y = 0;
gcode.insert(gcode.begin(), {instrG21, instrG90, instrF1250, instrM8, instrG92});
}
else if (useverticalwriting && !fulldrawingarea && restatreservoir)
{
instrG92.x = 0;
instrG92.y = 10;
gcode.insert(gcode.begin(), {instrG21, instrG90, instrF1250, instrM8, instrG92});
}
else if (!useverticalwriting && restatreservoir)
{
instrG92.x = 0;
instrG92.y = 39.5;
gcode.insert(gcode.begin(), {instrG21, instrG90, instrF1250, instrM8, instrG92});
}
Instr instrM2 = {"M2", NAN, NAN};
Instr instrG0 = {"G0", NAN, NAN};
Instr instrM7 = {"M7", NAN, NAN};
// Add M2 to the end of the gcode vector
if (currentline == linesselected)
{
gcode.push_back(instrM2);
}
// Add the appropriate G0 and M7 instructions based on the conditions
if (useverticalwriting && !restatreservoir && fulldrawingarea)
{
instrG0.x = 0;
instrG0.y = -39.5;
gcode.push_back(instrG0);
}
else if (useverticalwriting && !fulldrawingarea && !restatreservoir)
{
instrG0.x = 0;
instrG0.y = -29.5;
gcode.push_back(instrG0);
}
else if (!useverticalwriting && !restatreservoir)
{
instrG0.x = 0;
instrG0.y = 0;
gcode.push_back(instrG0);
}
else if (useverticalwriting && !fulldrawingarea && restatreservoir)
{
instrG0.x = 0;
instrG0.y = -29.5;
gcode.push_back(instrG0);
instrG0.y = 10;
gcode.push_back(instrG0);
gcode.push_back(instrM7);
}
else if (!useverticalwriting && restatreservoir)
{
instrG0.x = 0;
instrG0.y = 0;
gcode.push_back(instrG0);
instrG0.y = 39.5;
gcode.push_back(instrG0);
gcode.push_back(instrM7);
}
// Flush the Serial2 buffer
if (gcodeQueue.empty())
{
serialFlush();
}
// Print the gcode
/*for (const auto &instr : gcode)
{
Serial.printf("%s X%.2f Y%.2f\n", instr.command.c_str(), instr.x, instr.y);
}*/
// Send the gcode to the queue
for (const auto &instr : gcode)
{
char *gcodeLine;
if (instr.command == "M7" || instr.command == "M8" || instr.command == "M2" || instr.command == "G21" || instr.command == "G90" || instr.command == "F1250")
{
gcodeLine = new char[instr.command.length() + 1];
sprintf(gcodeLine, "%s", instr.command.c_str());
}
else
{
gcodeLine = new char[20];
sprintf(gcodeLine, "%s X%.2f Y%.2f", instr.command.c_str(), instr.x, instr.y);
}
gcodeQueue.push(gcodeLine);
}
}
Hallo das ist mein ESP32 Code oftmals Crasht der ESP32 beim Schreiben ich weiß nicht warum, also in Writeinput kommt ein Text an nähmlich in einem UTF32 Format bedeutet jede 8 Charakter ist quasi ein Buchstabe danach lese ich jeden Buchstaben aus dem Spiffs und Skaliere den GCode optimal hier sollte aber das Problem nicht liegen jetzt zum senden also ich habe eine Globale Variable gcodeQueue da pushe ich immer meine Gcodes wenn ich das nicht mache passiert auch nie was mit dem ESP32 also das Problem liegt beim Senden ist das ein Memory Leak oder was könnte das sein?