ESP32 Serial Kommunikation mit Arduino Uno (GRBL)

Hallo ich wollte GCode Daten vom ESP32 zu meiner CNC Plotter Maschine schicken die CNC Plotter Maschine läuft sehr gut über den Arduino Uno (GRBL 0.9)

Das ist meine Skizze wie ich die Beiden verbunden habe

Hier ist der Code für den ESP32

#include <WiFi.h>
#include <LITTLEFS.h>
#include <ESPAsyncWebServer.h>
#include <WebSocketsServer.h>

#define FORMAT_LITTLEFS_IF_FAILED false
#define RXD2 16
#define TXD2 17

// Constants
const char *ssid = "Admin";
const char *password = "123456789";
const int http_port = 80;
const int led_pin = 2;

// Globals
AsyncWebServer server(http_port);
WebSocketsServer webSocket = WebSocketsServer(81);

/***********************************************************
   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);
    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());
  }
  break;

  // For everything else: do nothing
  case WStype_TEXT:
  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()
{

  // 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);

  // Start access point
  WiFi.softAP(ssid, password);

  // Print our IP address
  Serial.println();
  Serial.println("AP running");
  Serial.print("My IP address: ");
  Serial.println(WiFi.softAPIP());

  server.serveStatic("/", LITTLEFS, "/");
  // On HTTP request for root, provide index.html file
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
            { request->send(LITTLEFS, "/index.html", String(), false); });

  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
        } 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 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);
}

void loop()
{
  webSocket.loop();
}

void changePenPos(int pos)
{
  if (pos == 0)
  {
    Serial.println("Changed Pen Position to Down");
  }
  else if (pos == 1)
  {
    Serial.println("Changed Pen Position to Up");
  }
}

void ReceivedGCodeData(uint8_t *payload, size_t length)
{
  // Tokenize the payload into lines
  char *token;
  char *saveptr;
  token = strtok_r((char *)payload, "\n", &saveptr);

  while (token != NULL)
  {
    // Send each line to the Serial port
    if (strcmp(token, "M7") == 0)
    {
      changePenPos(0);
      Serial2.write(token);
    }
    else if (strcmp(token, "M8") == 0)
    {
      changePenPos(1);
      Serial2.write(token);
    }
    else if (strcmp(token, "M2") == 0)
    {
      // Drawing send fully
      Serial.println("Drawing send fully");
      Serial2.write(token);
    }
    else if (strncmp(token, "G1", 2) == 0)
    {
      // Handle tokens that start with "G1"
      Serial.println(token);
      Serial2.write(token);
    }
    else if (strncmp(token, "G0", 2) == 0)
    {
      // Handle "G0"
      Serial.println(token);
      Serial2.write(token);
    }
    else if (strcmp(token, "G21") == 0)
    {
      // Handle "G21"
      Serial.println(token);
      Serial2.write(token);
    }
    else if (strcmp(token, "G90") == 0)
    {
      // Handle "G90"
      Serial.println(token);
      Serial2.write(token);
    }
    else if (strcmp(token, "F750") == 0)
    {
      // Handle "F750"
      Serial.println(token);
      Serial2.write(token);
    }

    // Get the next line
    token = strtok_r(NULL, "\n", &saveptr);

    // Add a delay here
    delay(100); // delay for 100 milliseconds
  }
}

// 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 uint8_t *buffer = NULL;
  static size_t bufferSize = 0;

  if (!index)
  {
    Serial.println("Gcode received");

    // Reset the buffer and size
    if (buffer != NULL)
    {
      free(buffer);
    }
    buffer = (uint8_t *)malloc(len);
    bufferSize = 0;
  }

  if (len)
  {

    // Append the incoming data to the buffer
    buffer = (uint8_t *)realloc(buffer, bufferSize + len);
    memcpy(buffer + bufferSize, data, len);
    bufferSize += len;
  }

  if (final)
  {
    Serial.println("Gcode upload complete");

    // Send the buffer to ReceivedGCodeData
    ReceivedGCodeData(buffer, bufferSize);

    // Reset the buffer and size
    free(buffer);
    buffer = NULL;
    bufferSize = 0;
  }
}

Der GCode kommt erfolgreich bei der ReceivedGCodeData funktion an und wird auch angezeigt, jedoch passiert nichts am CNC Plotter woran kann das liegen

Andere Serial am Uno nehmen und mit Software Serial arbeiten.
0 und 1 ist zuständig für Verbindung mit PC.

Du hast darauf geachtet, das RX vom ESP auf TX vom UNO und TX vom ESP auf den RX vom UNO gehen? (Ob vor oder nach dem Pegelwandler gekreuzt wird, ist egal)

Bin schonmal etwas weiter gekommen anstatt Serial2.write musste ich Serial2.println() nehmen aber maximal kann ich vielleicht 7 linien senden muss ich da irgendwie nen buffer beachten?

void ReceivedGCodeData(uint8_t *payload, size_t length)
{
  // Tokenize the payload into lines
  char *token;
  char *saveptr;
  token = strtok_r((char *)payload, "\n", &saveptr);

  while (token != NULL)
  {
    // Send each line to the Serial port
    if (strcmp(token, "M7") == 0)
    {
      changePenPos(0);
      Serial2.println(token);
    }
    else if (strcmp(token, "M8") == 0)
    {
      changePenPos(1);
      Serial2.println(token);
    }
    else if (strcmp(token, "M2") == 0)
    {
      // Drawing send fully
      Serial.println("Drawing send fully");
      Serial2.println(token);
    }
    else if (strncmp(token, "G1", 2) == 0)
    {
      // Handle tokens that start with "G1"
      Serial.println(token);
      Serial2.println(token);
    }
    else if (strncmp(token, "G0", 2) == 0)
    {
      // Handle "G0"
      Serial.println(token);
      Serial2.println(token);
    }
    else if (strcmp(token, "G21") == 0)
    {
      // Handle "G21"
      Serial.println(token);
      Serial2.println(token);
    }
    else if (strcmp(token, "G90") == 0)
    {
      // Handle "G90"
      Serial.println(token);
      Serial2.println(token);
    }
    else if (strcmp(token, "F750") == 0)
    {
      // Handle "F750"
      Serial.println(token);
      Serial2.println(token);
    }

    // Get the next line
    token = strtok_r(NULL, "\n", &saveptr);

    // Delay for 100ms
    delay(100);
  }
}

Da bin ich raus - eigentlich sollten die beiden miteinenader problemlos kommunizieren.

Das tuhen sie jetzt aber ich muss ja mehrere Zeilen an Daten rübersenden ich denke nicht das ich einfach 300 zeilen Gcode senden kann

Warum nicht?
Normalerweise bekommst Du doch eine Antwort, ob die Daten verarbeitet wurden.
Wenn ja, dann nächstes Datenpaket senden - wenn nicht das aktuelle nochmal.

Tatsächlich habe ich auch daran gedacht

Ich versuche es mal

#include <queue>
std::queue<String> gcodeQueue;
bool waitingForOk = false;

void loop()
{
  webSocket.loop();

  // Check for "ok" response on Serial2
  while (Serial2.available())
  {
    String response = Serial2.readStringUntil('\n');
    response.trim();
    if (response == "ok")
    {

      Serial.println("Received OK");
      waitingForOk = false;
    }
    else if (response == "[Pgm End]")
    {
      Serial.println("Program End");
      webSocket.broadcastTXT("Program End");
    }
  }

  sendNextGCodeCommand();
}

void changePenPos(int pos)
{
  if (pos == 0)
  {
    Serial.println("Changed Pen Position to Down");
  }
  else if (pos == 1)
  {
    Serial.println("Changed Pen Position to Up");
  }
}

void ReceivedGCodeData(uint8_t *payload, size_t length)
{
  // Tokenize the payload into lines
  char *token;
  char *saveptr;
  token = strtok_r((char *)payload, "\n", &saveptr);
  while (token != NULL)
  {

    if (strcmp(token, "M7") == 0)
    {
      // Add each line to the queue
      gcodeQueue.push(String(token));
    }
    else if (strcmp(token, "M8") == 0)
    {
      // Add each line to the queue
      gcodeQueue.push(String(token));
    }
    else if (strcmp(token, "M2") == 0)
    {
      // Add each line to the queue
      gcodeQueue.push(String(token));
    }
    else if (strncmp(token, "G1", 2) == 0)
    {
      // Add each line to the queue
      gcodeQueue.push(String(token));
    }
    else if (strncmp(token, "G0", 2) == 0)
    {
      // Add each line to the queue
      gcodeQueue.push(String(token));
    }
    else if (strcmp(token, "G21") == 0)
    {
      // Add each line to the queue
      gcodeQueue.push(String(token));
    }
    else if (strcmp(token, "G90") == 0)
    {
      // Add each line to the queue
      gcodeQueue.push(String(token));
    }
    else if (strcmp(token, "F1000") == 0)
    {
      // Add each line to the queue
      gcodeQueue.push(String(token));
    }

    token = strtok_r(NULL, "\n", &saveptr);
  }
}

void sendNextGCodeCommand()
{
  if (!waitingForOk && !gcodeQueue.empty())
  {
    int packetSize = 2; // Number of lines to send at once
    for (int i = 0; i < packetSize; i++)
    {
      if (!gcodeQueue.empty())
      {
        String command = gcodeQueue.front();
        gcodeQueue.pop();
        Serial.println("Sending GCode: " + command);
        Serial2.println(command);
      }
      else
      {
        break;
      }
    }
    waitingForOk = true;
    delay(200);
  }
}

Habe jetzt schonmal etwas versucht kann man das optimieren?

Also so funktioniert das schonmal ohne Datenverlust

Moin @anton_pro ,

bin kein Fan von if-else-if-else-if ... :wink:

Würde das so lösen (nur mal umgeschrieben, nicht getestet):

void ReceivedGCodeData(uint8_t *payload, size_t length)
{
  // Tokenize the payload into lines
  char *token;
  char *saveptr;
  token = strtok_r((char *)payload, "\n", &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 (strcmp(token, "F1000") == 0) { mode = 1; }
    if (mode == 1) { gcodeQueue.push(String(token));}
    token = strtok_r(NULL, "\n", &saveptr);
  }
}

Wenn noch weitere Befehle auszuwerten sein sollten, würde es sich ggf. anbieten, diese in einem Array zu speichern und in einer Schleife abzuarbeiten:

Beispielsketch
/*
  Forum: https://forum.arduino.cc/t/esp32-serial-kommunikation-mit-arduino-uno-grbl/1239346
  Wokwi: https://wokwi.com/projects/393623376239161345

  ec2021

*/

struct tokenStruct {
    char name[8];
    byte num;
};

const tokenStruct tokens[] = {
  {"M2",0},
  {"M7",0},
  {"M8",0},
  {"G0",2},
  {"G1",2},
  {"G21",0},
  {"G90",0},
  {"F1000",0},
};
const int noOfTokens = sizeof(tokens)/sizeof(tokens[0]);

void setup(){
  Serial.begin(115200);
  check("M1");
  check("M2");
  check("M21");
  check("G0");
  check("G012345");
  check("F1000");
}

void loop(){
}

void check(char * line){
  Serial.print(line);
  if (isValid(line)) {
      Serial.println(" is valid");
  } else {
      Serial.println(" is not valid");
  }
}

boolean isValid(char * token){
  for (int i = 0; i<noOfTokens;i++){
    if (tokens[i].num == 0){
      if (strcmp(token,tokens[i].name) == 0) {
        return true;
      }
    } else {
      if (strncmp(token,tokens[i].name, tokens[i].num) == 0) {
        return true;
      }
    }
  }
  return false;
}

Zum Testen bei Wokwi: https://wokwi.com/projects/393623376239161345

Bei void sendNextGCodeCommand() findet sich ein delay(200). Ist das erforderlich, wenn der Sketch eh auf eine Ok-Rückmeldung wartet? U.U. verlängert das nur unnötig den Transfer ..

void sendNextGCodeCommand()
{
  if (!waitingForOk && !gcodeQueue.empty())
  {
    int packetSize = 2; // Number of lines to send at once
    for (int i = 0; i < packetSize; i++)
    {
      if (!gcodeQueue.empty())
      {
        String command = gcodeQueue.front();
        gcodeQueue.pop();
        Serial.println("Sending GCode: " + command);
        Serial2.println(command);
      }
      else
      {
        break;
      }
    }
    waitingForOk = true;
    // +++++++++++++ Warum hier ein delay, wenn die Software eh auf ein Ok wartet? ++++++++++++
    delay(200);
  }
}

Viel Erfolg!
ec2021

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.