Probleme mit Datentypen

Hallo Zusammen,

die nachfolgende Funktion wird durch
webSocket.onEvent(webSocketEvent)
aufgerufen, wenn über die Websocketverbindung vom Client ein neues Json Paar empfangen wird.

void webSocketEvent (byte num, WStype_t type, unsigned char * payload, size_t length) { // Server hat irgendwelche Daten über websocket vom Client erhalten
  char * inhalt = (char *) malloc (JSON_SIZE_RX); // Dyn. speicher allokieren
  switch(type) {
    case WStype_DISCONNECTED: // enum that reads status, this is used for debugging
      Serial.println("Websocket: Client Disconnected");
      break; 
    case WStype_CONNECTED: // Check if a WebSocket Client is connected or not
      Serial.println("Websocket: Client Connected");
      webSocket.broadcastTXT(JSONtxt); // Konfig.variablen als Json zum Client senden zwecks ausfüllen der Eingabefelder
      break;
    case WStype_TEXT: { // check response from client, needs curly brackets due to init of error, see https://forum.arduino.cc/t/defined-names-of-switch-error-jump-to-case-label/628899/2?u=wmauss
      for(int i=0; i < length; i++) {
        Serial.print((char)payload[i]); 
        strcat(inhalt, payload[i]);       
      }
      deserializeConfig(inhalt); // Ergebnis wird in globaler Variable doc_rx gespeichert
      break;
    }  
    default: 
      break;
  }
}

Wenn ich die Fehlermeldung richtig verstehe gibt es nun eine Datentypeninkompatibilität zwischen dem Parameter payload (const char ) im Funktionsaufruf und payload[i] in strcat(), denn hier wird unsigned char erwartet.
Fehlermeldung:

src/main.cpp: In function 'void setup()':
src/main.cpp:1172:21: error: cannot convert 'void(byte, WStype_t, const char*, size_t)' {aka 'void(unsigned char, WStype_t, const char*, unsigned int)'} to 'WebSocketsServerCore::WebSocketServerEvent' {aka 'std::function<void(unsigned char, WStype_t, unsigned char*, unsigned int)>'}

Ich hatte dann noch versucht, die Datentypen umzuwandeln mittels

char * cstr =(char *)payload;

also so:

void webSocketEvent (byte num, WStype_t type, uint8_t * payload, size_t length) { // Server hat irgendwelche Daten über websocket vom Client erhalten
  char * inhalt = (char *) malloc (JSON_SIZE_RX); // Dyn. speicher allokieren
  char * cstr =(char *)payload;
  switch(type) {
    case WStype_DISCONNECTED: // enum that reads status, this is used for debugging
      Serial.println("Websocket: Client Disconnected");
      break; 
    case WStype_CONNECTED: // Check if a WebSocket Client is connected or not
      Serial.println("Websocket: Client Connected");
      webSocket.broadcastTXT(JSONtxt); // Konfig.variablen als Json zum Client senden zwecks ausfüllen der Eingabefelder
      break;
    case WStype_TEXT: { // check response from client, needs curly brackets due to init of error, see https://forum.arduino.cc/t/defined-names-of-switch-error-jump-to-case-label/628899/2?u=wmauss
      for(int i=0; i < length; i++) {
        Serial.print((char)payload[i]); 
        strcat(inhalt, cstr[i]);       
      }
      deserializeConfig(inhalt); // Ergebnis wird in globaler Variable doc_rx gespeichert
      break;
    }  
    default: 
      break;
  }
}

was aber lediglich die Fehlermeldung

src/main.cpp: In function 'void webSocketEvent(byte, WStype_t, uint8_t*, size_t)':
src/main.cpp:1000:30: error: invalid conversion from 'char' to 'const char*' [-fpermissive]
1000 | strcat(inhalt, cstr[i]);
| ~~~~~~^
| |
| char

nach sich gezogen hat.
Könnt ihr mir sagen, wie das geht?

Danke und Grüße
Werner

Tipp:
Ein Buchstabe ist kein Zeiger auf einen C-String.

Verstehe ich nicht.

Fehler: Ungültige Konvertierung von 'char' zu 'const char*'

Ist ein char, ein Buchstabe.
Es wird aber ein C-String erwartet.

char * strcat ( char * destination, const char * source )

Parameters

destination
Pointer to the destination array, which should contain a C string, and be large enough to contain the concatenated resulting string.
source
C string to be appended. This should not overlap destination.

Ich frage mich gerade, warum die payload zeichenweise ausgegeben bzw. an inhalt angehangen werden soll?

      for(int i=0; i < length; i++) {
         ...

Das Problem von payload ist, dass es formal ein byte Array ist, also am Ende nicht unbedingt ein 0-Byte als Endekennung enthält.
Da du die Länge hast, kannst du einfach memcpy verwenden und die Endekennung anhängen.

malloc() ist übrigens immer falsch, wenn nicht klar ist, wo das zugehörige free() ist.

Wenn du weißt, dass es tatsächlich ein c-string ist, ist dein malloc übrigens noch fragwürdiger.
Dass strcat danebengegriffen war, sollte inzwischen klar sein.

Vielen Dank an @combie, @wno158 und @michael_x! Da waren jetzt eine Menge hilfreiche Informationen dabei. Die arbeite ich ein und dann hoffe ich, dass ich heute Abend ein Stück funktionierenden Code präsentieren kann.

Grüße
Werner

Hier nun der korrigierte Quelltext.
Aufgrund von @michael_x 's Anmerkung

Das Problem von payload ist, dass es formal ein byte Array ist, also am Ende nicht unbedingt ein 0-Byte als Endekennung enthält.

ist meine Absicht, die Speicherstelle, die nach dem einfügen von payload in die Variable inhalt folgt mit einen Nullbyte zu beschreiben. Entweder stand da vorher schon ein Nullbyte, dann wird es überschrieben oder es fehlt eins, dann steht es danach dort.
Oder müßte ich das Nullbyte wiederum mit memcpy einfügen, um am Ende von inhalt nichts zu überschreiben, fall da zuvor kein Nullbyte war? Oder braucht es dann doch ein malloc, so dass nach hinten raus genug Platz da ist für solche Bytemanipulationen, wie ich sie gemacht habe?

Verstehe ich das richtig, dass es wegen memcpy und der Angabe der Länge des zu kopierenden c-Strings kein malloc() braucht?

void webSocketEvent (byte num, WStype_t type, uint8_t * payload, size_t length) { // Server hat irgendwelche Daten über websocket vom Client erhalten
  char * inhalt;
 
  switch(type) {
    case WStype_DISCONNECTED: // enum that reads status, this is used for debugging
      Serial.println("Websocket: Client Disconnected");
      break; 
    case WStype_CONNECTED: // Check if a WebSocket Client is connected or not
      Serial.println("Websocket: Client Connected");
      webSocket.broadcastTXT(JSONtxt); // Konfig.variablen als Json zum Client senden zwecks ausfüllen der Eingabefelder
      break;
    case WStype_TEXT: { // check response from client, needs curly brackets due to init of error, see https://forum.arduino.cc/t/defined-names-of-switch-error-jump-to-case-label/628899/2?u=wmauss
      memcpy(inhalt,payload,length); 
      *(inhalt + length + 1) = '\0'; // Fügt Nullbyte in das payload folgende Byte in inhalt
      deserializeConfig(inhalt); // Ergebnis wird in globaler Variable doc_rx gespeichert
      break;
    }  
    default: 
      break;
  }
}

Viele Grüße
Werner

Normalerweise kann man im Umfeld Arduino/ESP ohne malloc() auskommen (und es ist nebenbei bemerkt eine der größten Fehlerquellen, weil entweder die Größe nicht stimmt oder das free() vergessen wird oder ...). Außerdem sind wir in C++ unterwegs, da braucht man es erst recht nicht.

In Deinem speziellen Fall geht aber was schief:

char * inhalt;

Das vereinbart einen Zeiger auf char - und mehr nicht.
Ich kann nicht sehen (oder habe übersehen), dass dieser Zeiger initialisiert wird mit irgendetwas, das geeignet wäre, die payload aufzunehmen.

      memcpy(inhalt,payload,length); 
      *(inhalt + length + 1) = '\0';

DIe beiden Zeilen schreiben Dir also irgendwo in den Speicher. Das gibt mit 120% Sicherheit Bruch.

Stattdessen vielleicht char inhalt[length+1]; probieren.

kleiner Test

void func(char *payload, size_t length)
{
    char inhalt[length + 1];
    memcpy(inhalt, payload, length);
    *(inhalt + length + 1) = 0;
    Serial.print("func : ");
    Serial.println(inhalt);
}

void setup()
{
    Serial.begin(115200);
    Serial.println(__PRETTY_FUNCTION__);

    char payload[100];
    strcpy(payload, "Das ist ein Test");
    Serial.print("setup: ");
    Serial.println(payload);
    func(payload, strlen(payload) - 3);
}

void loop()
{
    // put your main code here, to run repeatedly:
}

liefert

21:15:23.351 -> void setup()
21:15:23.351 -> setup: Das ist ein Test
21:15:23.351 -> func : Das ist ein T

Vielen Dank @wno158, auch für das Beispiel. Ich habe es jetzt entsprechend abgeändert. Nun bin ich meinem Ziel eines Steuergerätes zur Steuerung von Schlafhausheizung, Wärmelampe und Gewächshaustür für ein Schildkrötengehege wieder einen kleinen Schritt näher.

Grüße
Werner

Wenn du guckst, siehst du unsigned char * payload bei der Deklaration der Funktionsparameter. ( und nicht const uint8_t* ) kannst also payload ändern und direkt dort am Ende deine 0 eintragen. Wenn du dir sicher bist, dass payload Text enthält, steht einem cast auf char* nichts im Weg. Ein Umkopieren ist jedenfalls nicht erforderlich

Da payload immer ein Json Paar ist, ist das m.E. immer Text.

Was ich nicht verstehe ist, warum ich einfach an den Inhalt einer bestehenden Variable ein Nullbyte anhängen kann ohne befürchten zu müssen, dass ich damit im Speicher etwas überschreibe, was direkt danach kommt.
Wenn ich das richtig verstanden habe, gibt length ja die Länge des char arrays ohne das Nullbyte an. Wenn nun ein Nullbyte fehlt, dann würde an der Stelle des Nullbytes (also bei length + 1) doch bereits das nächste Datum anfangen, oder?

Da hast du völlig recht.
Da payload ein Text ist, wird payload[length-1] schon das 0-Byte sein. Das ist jedenfalls das letzte Byte, das du bei Bedarf überschreiben dürftest. Evtl. ist der Text aber schon deutlich früher zuende. Hängt davon ab, was der websocket Sender gemacht hat.