Übertragen von GCodes von ESP32 Webserver zu ESP32

Hallo ich habe ein Projekt am laufen wo ich in der ESP32 Webserver app Gcodes erstelle, bedeutet man malt etwas in der App das wird dann in eine GCode Datei / Daten verarbeitet jetzt muss ich nur diese 100 - 1000 Zeilen große GCode Datei von dem Webserver zum ESP32 rübersenden. Wie mache ich das am besten? Mit nem Websocket klappt das nicht da das bei größeren Mengen nicht hinhaut

Das ist der ESP32 Code der die Daten an ReceivedGCodeData weitersendet (halt über den Websocket)

#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);
int currentpenposition = 0;

/***********************************************************
   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:
    ReceivedGCodeData(payload, length);

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

  // Handle file upload
  server.on(
      "/upload", HTTP_POST, [](AsyncWebServerRequest *request)
      { request->send(200); },
      [](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
      {
        static File uploadFile;

        if (!index)
        {
          Serial.print(filename);
          uploadFile = LITTLEFS.open("/" + filename, "w");
          if (!uploadFile)
          {
            return request->send(500, "text/plain", "Failed to open file for writing");
          }
        }

        uploadFile.write(data, len);

        if (final)
        {
          uploadFile.close();
        }
      });

  // 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); // 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();
  webSocket.onEvent(onWebSocketEvent);
}

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

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

void ReceivedGCodeData(uint8_t *payload, size_t length)
{
  // Example code to parse and handle GCode data
  Serial.println("Received GCodeData");

  // Null-terminate the string

  // 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.print(token);
    }
    else if (strcmp(token, "M8") == 0)
    {
      changePenPos(1);
      Serial2.print(token);
    }
    else if (strcmp(token, "M2") == 0)
    {
      // Drawing send fully
      Serial.println("Drawing send fully");
      webSocket.broadcastTXT("DrawingSubmitted");
    }
    else
    {
      Serial.println(token);
      Serial2.print(token);
    }

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

Ich möchte zu meinem Verständnis etwas nachfragen:

Du hast einen ersten ESP32 auf dem eine "Webserver-App" läuft (was immer auch eine "App" auf einem ESP32 sein mag)
Mit dieser ESP32-App erstellst du eine Zeichnung
in der ESP32-App wird die Zeichnung in G-Code umgewandelt
dieser GCode soll jetzt von der ESP32-App an einen
zweiten ESP32 gesendet werden?

Was genau bekommst du denn als Reaktion?
Gibt es eine wie auch immer geartete Reaktion von

  • der ESP32-App?
  • dem empfangenden ESP32?
    passiert einfach gar nichts?

Was passiert wenn du eine Datei mit

  • 10 bytes ?
  • 50 bytes?
  • 80 bytes ?

bei welcher Größe funktioniert es dann nicht mehr?

Oder
Zeilenweise mit TCP senden.

Oder mit UDP senden und ein eigenes Acknowledge-protokoll oben drauf setzen.

Bei UDP braucht man ein eigenes Ackonwledgement weil es UDP egal ist ob der Empfängr wirklich etwas empfangen hat.

@anton_pro

wenn die Daten als File im Filesystem vorliegen, könntest du am anderen ESP einen FTP Server installieren und auf der Sender Seite einen FTP Client.

Wenn du es zu Fuß machen willst, evtl. nur eine einfache TCP Übertragung?

Was hast auf der "Empfänger" Seite schon zur Verfügung? Wenn dort auch ein HTTP Server läuft, dann würde ich die paar KB einfach mittels POST übertragen.

Danke für die schnelle antwort, naja das ist halt ein offline projekt ich möchte daten von der app zum esp32 rübersenden die app läuft auf dem ESP32 auf nem Access Point ich denke mal ich werde die daten so wie du sagst über nen POST zum Server senden

Das ist die Funktion um sie anzunehmen und weiterzugeben

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

  String logmessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url();
  Serial.println(logmessage);

  if (!index)
  {
    logmessage = "Gcode received: " + String(filename);
    Serial.println(logmessage);

    // 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)
  {
    logmessage = "Gcode upload complete: " + String(filename) + ",size: " + String(index + len);
    Serial.println(logmessage);

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

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

Das ist die Funktion zum hochladen von einer Gcode datei

// Function to upload a file to the ESP32
function uploadGcode(file) {
    return new Promise((resolve, reject) => {
        var formData = new FormData();
        formData.append('file', file);

        fetch('/gcode', {
            method: 'POST',
            body: formData
        })
            .then(response => {
                if (response.status === 200) {
                    console.log('Gcode send successfully');

                    if (activesendtype === "drawing") {
                        if (document.getElementById("drawButton") !== null) {
                            document.getElementById("drawButton").innerText = "Gesendet";
                            document.getElementById("drawButton").disabled = true;
                            document.getElementById("drawButton").style.backgroundColor = "#7AE582";
                        }
                        activesendtype = null;
                    }

                    resolve();
                } else {
                    throw new Error('Server responded with status: ' + response.status);
                }
            })
            .catch(error => {
                console.error('Error uploading file:', error);
                reject(error);
            });
    });
}

Ich habe eine Bitte:
Du schreibst immer von einer "App"?

Das ist im Zusammenhang mit microcontrollern eine nicht gebräuchliche Bezeichnung.

Im üblichen Sprachgebrauch gibt es "Apps" auf Smartphones und Tablets. Und seit einigen Jahren auch auf Windows PCs
Aber eben nicht auf Microcontrollern.

Die Formulierung "Die App läuft auf dem ESP32 auf nem Access Point
Macht für mich überhaupt keinen Sinn.

Ein ESP32 kann IN der Betriebsart Access-Point betrieben werden.
Was dann bedeutet der ESP32 macht sein eigenes WLAN-Netz auf.
Und deine "App" (was immer das auch sein mag) balanciert dann auf den 2,4 GHz-Funkwellen des ESP32???? Ne wohl nicht!

Meinst du mit

Meinst du mit "App"
Es gibt einen ESP32 der als Access-Point konfiguriert ist und das C++-Programm das auf diesem ESP32 läuft?

dann schreibst du

Unter dieser Formulierung stelle ich mir vor:
Du hast eine "App" auf einem TABLET.
Auf diesem TABLET läuft eine App mit der man per Eingabestift zeichnen kann.
Diese TABLET-app sendet dann Daten per WLAN an einen ESP32

Ja und diese Formulierungen passen eben nicht richtig zusammen.

Also es gibt eine Funkverbindung zwischen zwei Geräten Gerät_A und Gerät_B

Was ist Gerät_A?

  • ein Smartphone?
  • ein Tablet?
  • ein PC?
  • ein ESP32 der im Access-Point-Modus C++-Code ausführt (wie auch immer dieser ESP32 Zeichnungen erstellen mag)

Dann sollen G-Code-Daten OFFLINE (was immer in diesem Zusammenhang offline bedeuten mag) an das zweite Gerät_B übertragen werden
Dieses Gerät_B ist dann ein zweiter ESP32?

Es ging mir nur um von meinem Handy aus zum ESP32 "über WLAN" die Daten zu senden, mit der App meine ich die Website die auf dem SPIFFS liegt im ESP32