Ich bräuchte mal Hilfe bei der Umwandlung von String zu Char

Hallo,
Eigentlich sollte es ja kein Problem sein ein String in ein Char umzuwandeln ....
Jetzt ist es aber leider etwas Schwerer ....

Es geht erstmal um den Code eines 433 MHz Senders / Emfängers

der Testcode Funktioniert ohne Probleme aber jetzt kommt der Knackpunkt

void sendMessage(String sMessage)
{
  char *msg = "Testnachricht";       // this is your message to send
  //char *msg;
  //Serial.println("Sende: " + sMessage);
  //sMessage.toCharArray(msg, sMessage.length());
  //Serial.println(String(msg));
  vw_send((uint8_t *)msg, strlen(msg));
  vw_wait_tx();                                          // Wait for message to finish
  delay(200);
}

So Funktioniert es ohne Probleme

void sendMessage(String sMessage)
{
  //char *msg = "Testnachricht";       // this is your message to send
  char *msg;
  Serial.println("Sende: " + sMessage);
  sMessage.toCharArray(msg, sMessage.length());
  Serial.println(String(msg));
  vw_send((uint8_t *)msg, strlen(msg));
  vw_wait_tx();                                          // Wait for message to finish
  delay(200);
}

und so nicht ;-(

ich weis das das Problem bei der Umwandlung vom String (sMessage) in die *msg variable auftritt...
Aber ich verstehe nicht warum ??!?!?!?

Ich habe dazu auch mal gleich ein Paar verständnis fragen :slight_smile:

  1. was bedeutet der * vor msg !?!?!? --> Ich dachte es wäre damit ein Char Arry ...
  2. was genau macht --> (uint8_t *)msg so wie ich das verstehe boxt er die msg Variable in ein INT ??!?!?!!?!?
  3. muss ich überhaupt den Umweg über das CharArray gehen oder kann ich da auch gleich den String reinballern ....
  4. oder soll ich den String vorab in ein bytearry umwandeln ?

Hilfe wäre echt super Klasse

Danke schon mal im Vorraus

In C sind Strings Null-Terminierte char Arrays. Und Array Variablen sind Zeiger auf das erste Element. Deshalb das *

Diese Library arbeitet aber mit Byte Arrays (d.h. unsigned char statt char). Deshalb der Cast. uint8_t* ist das gleiche wie byte* oder unsigned char*. Lediglich prozessor-unabhängig.
Boxing ist das nicht. Damit bezeichnet man in strikt OO Sprachen das verpacken eines primitiven Datentyps in ein Objekt.

Das hier ist absolut tödlich:

char* msg;
sMessage.toCharArray(msg, sMessage.length());

Für msg existiert kein Speicher!! Du musst du schon ein richtiges Array anlegen. Nicht nur einen Zeiger der irgendwo ins Nirwana zeigt:

char msg[20];

Nur dann existiert auch wirklich Speicher in den du was reinschreiben kannst

Noch besser ist wenn du auf String Objekte verzichtest und gleich char Arrays nimmst. Eine Funktion die einen C String als Parameter hat sieht so aus:

void sendMessage(char* sMessage)

also sollte es so klappen ?

void sendMessage(String sMessage)
{
  //char *msg = "Testnachricht";       // this is your message to send
  char msg[sMessage.length()];
  Serial.println("Sende: " + sMessage);
  sMessage.toCharArray(msg, sMessage.length());
  Serial.println(String(msg));
  vw_send((uint8_t *)msg, strlen(msg));
  vw_wait_tx();                                          // Wait for message to finish
  delay(200);
}

Die Länge von Arrays muss zur Compile-Zeit bekannt sein:

char msg[20];
sMessage.toCharArray(msg, sizeof(msg));

Das ist Unsinn:

Serial.println(String(msg));

println() kann auch direkt C Strings ausgeben ohne dass du daraus ein String Objekt machst.

Und wenn es unbedingt String Objekte sein müssen:

void sendMessage(String& sMessage)

Dann wir der Parameter per Referenz und nicht als Wert übergeben, d.h. das Objekt wird nicht kopiert.

EDIT:
Es kann sein dass lokale Arrays Variabler Länge als gcc Erweiterung gehen:
Variable Length (Using the GNU Compiler Collection (GCC))
Irgendwas hatte ich da mal in Erinnerung. Standard C++ ist es aber nicht, weshalb man das leicht erst mal als falsch abtut.

Wenn es nur um das zusammensetzen verschiedener Texte geht, kannst du die "Texte" so zusammensetzen:

// Deklaration

char SenderMsg0[10];   
char SenderMsg1[10];   
char SenderMsg2[10];   
char Sender_Msg[30];   


// in der Loop oder Funktion

  strcat(Sender_Msg, SenderMsg0);  
  strcat(Sender_Msg, SenderMsg1);  
  strcat(Sender_Msg, SenderMsg3);  

// Sender_Msg wird dem Sender übergeben

und dann dem Sender übergeben.

Besser strncat() verwenden. Dann vermeidet man einen Puffer-Überlauf.

Serenifly:
Besser strncat() verwenden. Dann vermeidet man einen Puffer-Überlauf.

Anstatt "strcat"?

Ok, bisher hatte ich da noch kein Problem, werde es aber beherzigen.

Ich setze das ebenfalls für die Übertragung mit einem 433 MHz-TX ein, da werden verschiedene Messwerte und ein Kanalcode übertragen, das geht soweit sehr gut.

Wenn du darauf achtest dass der Puffer immer größer als die Summe der Teil-Strings ist geht das auch gut.

Pufferüberläufe sind aber eine der größten Fehlerquellen. Deshalb gibt es Funktionen wie strncpy(), strncat() und snprintf().

Serenifly:
Wenn du darauf achtest dass der Puffer immer größer als die Summe der Teil-Strings ist geht das auch gut.

Ok, bei meinem obigen Beispiel ist es ja überschaubar und sollte dann mit funktionieren.
Sind bei mir nur feste Texte bzw. Messwerte, das passt.

okay jetzt bin ich verwirrt !?!?!?!

also ich wollte das so zusammenbauen das die länge der Nachricht nicht feststeht oder bekannt ist .. (ich kenne Sie wirklich nicht) zur aktuellen Situation ist das vermutlich wirklich egal da ich nur einen Temperaturwert übermittle ... später wollte ich mehr Daten übertragen.

Beispielstring zur übertragung:
Datum Zeit | Messert | Einheit | Sensor |

oder lege ich das Char Proformer mit 500 an und muss die Nachrichtenlänge notfalls auf 500 begrenzen ?
okay das zusammenbauen der Char Arrays ist nun klar ....
aber die frage wie ich das Nachrichten Char Dimensioniere steht im raum ?

wie hat ihr das den gelöst ?

Und wenn es unbedingt String Objekte sein müssen:
Code: [Select]

void sendMessage(String& sMessage)

Dann wir der Parameter per Referenz und nicht als Wert übergeben, d.h. das Objekt wird nicht kopiert.

habe ich versucht bekomme das Programm dann aber nicht Kompeliert oder muss ich bei der Übergabe das & auch mit anhängen ?
invalid initialization of non-const reference of type 'String&' from an rvalue of type 'String'

MultiStorm:
Beispielstring zur übertragung:
Datum Zeit | Messert | Einheit | Sensor |

wie hat ihr das den gelöst ?

So wie oben beschrieben! Post #4

Dann machst du dein char eben lang genug.
So viele Werte sind das doch nicht.
Das kann man doch vorher bestimmen.

Du wirst doch wissen wie lange die Nachrichten maximal in etwa sind. Wie kommst du da auf 500 Bytes... :confused:

Oder mache das:

w_send((uint8_t *)const_cast<char*>(msg.c_str()), strlen(msg));

Nicht getestet. Damit spart man sich diese dämliche Umwandlung. c_str() greift direkt auf das interne Array zu, aber liefert einen const Zeiger. Der const_cast castet das const weg. Dann wird der Zeiger nochmal auf byte* gecastet

muss ich bei der Übergabe das & auch mit anhängen ?

Nein. Tue dir einen Gefallen und lerne etwas C++ Grundlagen.

jupp habs umgebaut:

void sendMessage(String& sMessage)
{
  //char *msg = "Testnachricht";       // this is your message to send
  char msg[500];
  Serial.println("Sende: " + sMessage);
  sMessage.toCharArray(msg, sizeof(msg));
  //Serial.println(String(msg));
  vw_send((uint8_t *)msg, strlen(msg));
  vw_wait_tx();                                          // Wait for message to finish
  delay(200);
}

leider führt das & hinter String immer noch zu einem Fehler

void sendMessage(String& sMessage)

bekomme immer gesagt:

aber danke schonmal für die ganzen Lektionen !!!
invalid initialization of non-const reference of type 'String&' from an rvalue of type 'String'

Wieso 500 Bytes? Wenn du nicht viel machst, mag das ok sein, aber der UNO hat nur 2k RAM.

Wie rufst du die Funktion auf? Eigentlich sollte das gehen. Ansonsten lasse es weg. Ist zwar schöner, aber nicht essentiell.

die 500 waren nur ne schätzung .... habe es in zwischen auf 100 gesetzt
und ich habe ein Mega :slight_smile:

Quote
muss ich bei der Übergabe das & auch mit anhängen ?
Nein. Tue dir einen Gefallen und lerne etwas C++ Grundlagen.

Das versuche ich ja gerade ...
ich Programmiere meist in Hochsprachen wie C# oder Java und da ist das alles ein wenig anders :slight_smile:

Aktuell Sieht der aufruf so aus:

float temp1;
  float temp2;
  float temp;

  temp1 = getTemp(adresseOfTopSensor1);
  temp2 = getTemp(adresseOfTopSensor2);
  temp = ((temp1 + temp2) / 2);
  sendMessage(String(temp, 2));

wenn ich das & in im Funktionskopf weglasse läuft alles sauer ...

Wieso überhaupt die String Klasse hier? Floats lassen sich doch bequem mit dtostrf() in einen C String formatieren:

https://www.mikrocontroller.net/topic/86391

und Aktuell Sieht der String den ich mal übertragen will so aus:
(habe ihn gerade mal von der SD Karte Kopiert

14.01.2016 10:10:42|SensorValue|DS18B20|40 255 74 131 3 21 2 6|Grad|21.19

da kommen schnell mal ein paar byte zusammen :slight_smile:

Ich habe es mal ausprobiert. Lokale variable Arrays gehen in gcc tatsächlich:

void setup()
{
  Serial.begin(9600);

  String msg = "Test String";
  printString(msg);
}

void loop()
{
}

void printString(String& str)
{
  Serial.println(str.length());
  char buffer[str.length() + 1];
  Serial.println(sizeof(buffer));
  str.toCharArray(buffer, sizeof(buffer));
  Serial.println(buffer);
}

Das Array muss unbedingt eins größer sein als length(), damit noch Platz für den Null-Terminator ist!

Ist wie gesagt nicht Standard C++ (sondern ein Feature dass aus C90 übernommen wurde), deshalb habe ich es erst für falsch gehalten. Das +1 hattest du aber nicht.

Aber besser wäre das in Reply #12 wenn es geht...

habe das & weggelassen und alles ist schön :slight_smile:

Danke an dieser Stelle noch einmal für eure Hilfe :slight_smile: