Uint8_t in std::strings splitten

Hallo, ich habe ein kleines Problem erstmal der Code:

void ReceivedGCodeData(uint8_t *payload, size_t length)
{

  // Print the payload
  /*for (size_t i = 0; i < length; i++)
  {
    DEBUG_PRINT((char)payload[i]);
  }*/

  // Flush the Serial2 buffer
  if (gcodeQueue.empty())
  {
    serialFlush();
  }

  // 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)
    {
      std::string gcodeLine = token;
      gcodeQueue.push(gcodeLine);
    }
    token = strtok_r(NULL, ",", &saveptr);
  }
  // Print the esp free heap size by using esp_get_free_heap_size()
  DEBUG_PRINTF("Free heap size: %d\n", esp_get_free_heap_size());

  // Print the whole gcodequeue in the serial monitor
  /*std::queue<std::string> tempQueue = gcodeQueue;
  while (!tempQueue.empty())
  {
    DEBUG_PRINTLN(tempQueue.front().c_str());
    tempQueue.pop();
  }*/
}

void serialFlush()
{
  while (Serial2.available() > 0)
  {
    char t = Serial2.read();
  }
}

So in der Funktion kommt der payload vom Webserver request ich habe diesen payload mir auch loggen lassen und der sieht jedes mal sauber aus wenn ich mir aber dann die gcodequeue loggen lasse.

So sieht ein payload aus

G21,G90,G92X0Y0,M8,G0X22.43Y34.74,M7,G1X22.32Y34.74,G1X20.43Y34.42,G1X18.42Y34.32,G1X16.42Y34.42,G1X16.00Y32.42,G1X16.00Y30.31,G1X16.11Y28.31,G1X16.42Y26.41,G1X16.11Y24.72,G1X13.89Y24.62,G1X11.57Y24.62,G1X9.47Y24.62,G1X7.25Y24.62,G1X5.14Y24.62,G1X6.51Y23.57,G1X7.57Y22.51,G1X8.62Y21.35,G1X9.57Y20.19,G1X10.62Y18.93,G1X11.68Y17.56,G1X12.73Y15.98,G1X13.58Y14.82,G1X14.52Y13.45,G1X15.37Y11.97,G1X16.21Y10.81,G1X17.16Y9.23,G1X18.00Y7.86,G1X18.85Y6.38,G1X19.69Y5.12,G1X20.74Y6.38,G1X21.38Y7.75,G1X22.11Y9.12,G1X22.85Y10.49,G1X23.70Y11.87,G1X24.54Y13.34,G1X25.38Y14.82,G1X26.22Y16.50,G1X27.17Y18.29,G1X28.12Y19.67,G1X29.18Y21.14,G1X29.91Y22.51,G1X30.76Y23.99,G1X29.39Y24.83,G1X27.49Y24.72,G1X25.49Y24.51,G1X23.48Y24.51,G1X21.48Y24.62,G1X20.85Y26.09,G1X21.06Y27.89,G1X21.17Y29.78,G1X21.17Y31.79,G1X21.17Y34.11,M8,M2,G0X0Y0,GCODE

und manchmal wird einfach nach dem G0X0Y0 also das Gcode ist nur dafür da um das G0X0Y0 noch mitzunehmen, und manchmal (nicht immer) kommen einfach plötzlich verschiedene abschnitte vom payload nochmal in der gcodequeue am ende doppelt vor das heißt nach dem er G0X0Y0 gemacht hat kommt einfach nochmal ein abschnitt vom payload kein Plan warum das so ist, es ist auch nicht immer so, stimmt irgendwas nicht mit der ReceivedGCodeData funktion das kann ja eigentlich nur der Grund sein, wie gesagt habe mir den payload geloggt der schaut Gut aus aber das was dann wirklich in die GcodeQueue gepusht wird ist irgendwie komisch also immer zum schluss kommen wiederholte gcodes nochmal rein (meistens die Gcodes vom ende des eigentlichen gcodes)

schreibmalvernünftigesätzeundteiledeinentextinmehrereabschnitteauf

dann kann man es auch lesen

Und dann poste als code-sections was du da als gcodes bekommst.
Dann kann man sich das eins zu eins anschauen.

Wenn Sie das Payload erhalten und die Länge festlegen, ist es dann eine korrekt formatierte cString? Haben Sie ein Nullzeichen am Ende des Payloads integriert?

Hi! Also der payload kommt vom AsyncWebServerRequest so sieht das ganze aus:

// 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)
  {
    DEBUG_PRINTLN("Gcode received");

    // First check if gcodequeue is empty and if yes wake up grbl
    if (gcodeQueue.empty())
    {
      DEBUG_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)
  {
    DEBUG_PRINTLN("Gcode upload complete");

    // Print the buffer
    /*for (const auto &byte : buffer)
    {
      DEBUG_PRINT((char)byte);
    }*/

    // Send the buffer to ReceivedGCodeData
    ReceivedGCodeData(buffer.data(), buffer.size());

    // Clear the buffer and free the memory
    std::vector<uint8_t>().swap(buffer);
  }
}

Ich wüsste nicht ein nullzeichen integriert zu haben

void ReceivedGCodeData(uint8_t *payload, size_t length)
{

  // Print the payload
  /*for (size_t i = 0; i < length; i++)
  {
    DEBUG_PRINT((char)payload[i]);
  }*/

  // Flush the Serial2 buffer
  if (gcodeQueue.empty())
  {
    serialFlush();
  }

  // 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)
    {
      std::string gcodeLine = token;
      gcodeQueue.push(gcodeLine);
      DEBUG_PRINTLN("Gcode line: " + String(gcodeLine.c_str()));
    }
    token = strtok_r(NULL, ",", &saveptr);
  }
  // Print the esp free heap size by using esp_get_free_heap_size()
  DEBUG_PRINTF("Free heap size: %d\n", esp_get_free_heap_size());

  // Print the whole gcodequeue in the serial monitor
  /*std::queue<std::string> tempQueue = gcodeQueue;
  while (!tempQueue.empty())
  {
    DEBUG_PRINTLN(tempQueue.front().c_str());
    tempQueue.pop();
  }*/
}

Ich habe etwas herausgefunden wenn ich mir die gcodeline nachdem ich sie gepusht habe printen lasse dann funktioniert es immer, wahrscheinlich weil etwas zeit draufgeht oder so kein Plan

Und wenn ich beide DEBUG_PRINTS lösche dann ist das problem auch weg also

DEBUG_PRINTLN("Gcode line: " + String(gcodeLine.c_str()));
und
DEBUG_PRINTF("Free heap size: %d\n", esp_get_free_heap_size());

Was ist buffer? (type?) (➜ Snippets R Us!)

Es scheint, als ob Sie diesen Puffer als einen Bereich binärer Speicherung behandeln, ohne spezielle Endmarke. Wenn Sie dann Funktionen wie strtok_r() oder strcmp() verwenden möchten, um den Inhalt zu analysieren, muss der Puffer unbedingt wie eine cString funktionieren und Sie müssen ein Nullzeichen am Ende haben.

static std::vector<uint8_t> buffer;

Das ist der buffer

Ah ja, ich hatte das für std::vector<uint8_t> übersehen.

Ihr Callback fügt die Rohdaten in den Vektor buffer ein (buffer.insert(buffer.end(), data, data + len);), aber data enthält kein Nullzeichen am Ende der Zeichenkette.

Wenn len 0 ist (oder final true ist), bedeutet dies, dass Sie alles erhalten haben und Sie trotzdem das abschließende Nullzeichen am Ende des Vektors einfügen sollten.

Dadurch, dass Sie ReceivedGCodeData(buffer.data(), buffer.size()); aufrufen, haben Sie eine cString als Payload.

Das bedeutet einfach wenn final true ist dann einfach \0 an den buffer hängen?

PS:

https://cplusplus.com/reference/vector/vector/clear/
buffer.clear();

Wenn ich buffer.clear verwende dann wird der Memory nicht freigegeben, durch diese Funktion wird der Memory freigegeben. buffer.clear macht das nicht

Clear content

Removes all elements from the vector (which are destroyed), leaving the container with a size of 0.

A reallocation is not guaranteed to happen, and the vector capacity is not guaranteed to change due to calling this function. A typical alternative that forces a reallocation is to use swap:

vector<T>().swap(x);   // clear x reallocating 

Die zugrundeliegende Frage lautet: Warum möchten Sie eine Neuverteilung erzwingen - den Vektor vergrößern und möglicherweise Löcher in den Heap stoßen? Möglicherweise behalten Sie tatsächlich einen Speicherplatz, der zu groß ist, aber zumindest haben Sie für das nächste Mal zusammenhängenden Platz.

Naja weil manchmal ist der buffer so groß das er um die 80000 bytes im Heap nimmt weil ich sehr viele Daten sende und dann immer 80000 bytes zu speichern naja...

Das bedeutet also, dass Sie sicherstellen müssen, dass immer 80 KB zusammenhängender Speicher im Heap verfügbar sind, damit Ihr Code funktioniert.

Wenn Sie den Puffer schrittweise aufbauen und bei jeder Hinzufügung von Daten eine Neuzuweisung und eine Verschiebung des Puffers im Heap durch Kopieren der Daten erfolgt (je mehr Sie den Puffer inkrementell vergrößern, desto mehr Anfragen werden generiert, da ein größerer Puffer gefunden werden muss, während der alte erhalten bleibt, um die Kopie zu machen, was auch die Ausführung verlangsamt). Abhängig davon, was der restliche Code macht, bedeutet dies im Laufe der Zeit, dass es immer schwieriger werden kann, diese zusammenhängenden Puffer zu finden.

ja genau es muss immer der Speicher verfügbar sein den er durch den Puffer genutzt hat. Aber ich verstehe immer noch nicht das Problem, was beudeutet das jetzt. Warum aber wenn ich die beiden Print statements weg mache ist das Problem weg das er die std::strings hinzufügt?

// 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)
  {
    DEBUG_PRINTLN("Gcode received");

    // First check if gcodequeue is empty and if yes wake up grbl
    if (gcodeQueue.empty())
    {
      DEBUG_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)
  {
    DEBUG_PRINTLN("Gcode upload complete");

    // Append a null character to the end of the buffer
    buffer.push_back('\0');

    // Print the buffer
    /*for (const auto &byte : buffer)
    {
      DEBUG_PRINT((char)byte);
    }*/

    // Send the buffer to ReceivedGCodeData
    ReceivedGCodeData(buffer.data(), buffer.size());

    // Clear the buffer and free the memory
    std::vector<uint8_t>().swap(buffer);
  }
}

Kommt aber trotzdem immernoch vor ich habe jetzt wie du gesagt hast ein Nullzeichen am ende vom Buffer hinzugefügt so richtig?

Erfüllt es, was Sie beim Parsen des GCODE wollen?

#include <queue>

uint8_t data[] = "G21,G90,G92X0Y0,M8,G0X22.43Y34.74,M7,G1X22.32Y34.74,G1X20.43Y34.42,G1X18.42Y34.32,G1X16.42Y34.42,G1X16.00Y32.42,G1X16.00Y30.31,G1X16.11Y28.31,G1X16.42Y26.41,G1X16.11Y24.72,G1X13.89Y24.62,G1X11.57Y24.62,G1X9.47Y24.62,G1X7.25Y24.62,G1X5.14Y24.62,G1X6.51Y23.57,G1X7.57Y22.51,G1X8.62Y21.35,G1X9.57Y20.19,G1X10.62Y18.93,G1X11.68Y17.56,G1X12.73Y15.98,G1X13.58Y14.82,G1X14.52Y13.45,G1X15.37Y11.97,G1X16.21Y10.81,G1X17.16Y9.23,G1X18.00Y7.86,G1X18.85Y6.38,G1X19.69Y5.12,G1X20.74Y6.38,G1X21.38Y7.75,G1X22.11Y9.12,G1X22.85Y10.49,G1X23.70Y11.87,G1X24.54Y13.34,G1X25.38Y14.82,G1X26.22Y16.50,G1X27.17Y18.29,G1X28.12Y19.67,G1X29.18Y21.14,G1X29.91Y22.51,G1X30.76Y23.99,G1X29.39Y24.83,G1X27.49Y24.72,G1X25.49Y24.51,G1X23.48Y24.51,G1X21.48Y24.62,G1X20.85Y26.09,G1X21.06Y27.89,G1X21.17Y29.78,G1X21.17Y31.79,G1X21.17Y34.11,M8,M2,G0X0Y0,GCODE";
std::queue<String> gcodeQueue;

void receivedGCodeData(uint8_t *payload, size_t length) {
  char *token;
  char *saveptr;
  token = strtok_r((char *)payload, ",", &saveptr);
  
  while (token != NULL) {
    bool gcodePushNeeded = false;
    if (strcmp(token, "M7") == 0)             gcodePushNeeded = true;
    else if (strcmp(token, "M8") == 0)        gcodePushNeeded = true;
    else if (strcmp(token, "M2") == 0)        gcodePushNeeded = true;
    else if (strncmp(token, "G1", 2) == 0)    gcodePushNeeded = true;
    else if (strncmp(token, "G0", 2) == 0)    gcodePushNeeded = true;
    else if (strcmp(token, "G21") == 0)       gcodePushNeeded = true;
    else if (strcmp(token, "G90") == 0)       gcodePushNeeded = true;
    else if (strncmp(token, "G92", 3) == 0)   gcodePushNeeded = true;
    else if (strcmp(token, "F1250") == 0)     gcodePushNeeded = true;

    if (gcodePushNeeded) gcodeQueue.push(String(token));
    token = strtok_r(NULL, ",", &saveptr);
  }

  std::queue<String> tempQueue = gcodeQueue; // copy
  while (!tempQueue.empty()) {
    Serial.println(tempQueue.front());
    tempQueue.pop();
  }
}

void setup() {
  Serial.begin(115200);
  receivedGCodeData(data, sizeof data);
}

void loop() {}

Mir wurde gesagt das std::string für die Queue besser ist um Speicher zu sparen