Wie kann ich Zahlenwerte über eine Serielle Kommunikation senden?

Guten Tag,

Ich bin an einem Projekt, wo ich 3 Zahlenwerte über Serielle Kommunikation an ein anderes Arduino senden will. Das klappt auch schon ganz gut aber leider werden ja bekanntlich die Zahlen vom Empfänger nacheinander Empfangen und auch so weiterverarbeitet. So sind die Zahlen im Serial Monitor des Empfängers untereinander. Ich möchte aber die drei Zahlenwerte jeweils durch ein Komma getrennt auf eine SD-Karte speichern. Wie kann ich das machen?

Mein Ansatz wäre die Zahlen alle in einem String "gleichzeitig" zu senden und zu empfangen. Geht das?

Liebe Grüsse

Serial.print und read hast vermutlich schon entdeckt. sinnvoll wäre eine anfangskennung für den string zu definieren und ein ende. so kann du erkennen ob der text vollständig ist. das ganze mit read in einen puffer schreiben und auswerten. fehlerhaftes am besten verwerfen, mit Rückmeldung an den absender.

Dann zeige uns doch mal, was Du schon hast. Setze Deinen Code bitte in Codetags(</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Dann können wir evtl. die Fehler sehen.

Ich vermute die Ursache ist println anstelle von print.

Gruß Tommy

Einfach das Linefeed am Ende von println() abfragen. Um es im seriellen Monitor zu testen kannst du den so einstellen dass automatisch ein LF gesendet wird:

const int SERIAL_BUFFER_SIZE = 21;
char serialBuffer[SERIAL_BUFFER_SIZE];

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

void loop()
{
  if (readSerial(Serial))      //liefert true wenn das LF eingelesen wurde
  {
    Serial.print("String: "); Serial.println(serialBuffer);

    int var1 = atoi(strtok(serialBuffer, ","));
    int var2 = atoi(strtok(NULL, ","));
    int var3 = atoi(strtok(NULL, ","));

    Serial.println(var1);
    Serial.println(var2);
    Serial.println(var3);
    Serial.println();
  }
}

bool readSerial(Stream& stream)
{
  static byte index;

  while (stream.available())
  {
    char c = stream.read();

    if (c == '\n' && index > 0)       //wenn LF eingelesen und String länger als 0 ist
    {
      serialBuffer[index] = '\0';     //String terminieren
      index = 0;
      return true;                    //melden dass String fertig eingelesen wurde
    }
    else if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1)   //solange noch Platz im Puffer ist
    {
      serialBuffer[index++] = c;    //Zeichen abspeichern und Index inkrementieren
    }
  }
  return false;        //noch nicht fertig
}

Ausgabe z.B.:

String: 123,25,20
123
25
20

Drei Zahlen senden geht einfach so:

Serial.print(var1);
Serial.print(',');
Serial.print(var2);
Serial.print(',');
Serial.print(var3);
Serial.println();

Oder etwas schöner mit der Streaming Bibliothek

kann man so machen, nur dass eine startkennung noch fehlt. damit garantiert ist, dass die erste zahl auch die erste zahl ist. z.b. wenn beim öffnen des ports grad gesendet wird, und evtl. zahlen verpasst werden.
dann wäre es eindeutiger.

@Seriifly:Gibt es einen vernünftigen Grund, warum die Funktion ein Stream& als Parameter hat, aber mit einem globalen char SerialBuffer[SERIAL_BUFFER_SIZE] und einem lokalen statischen index darauf arbeitet?
@harryberlin: readSerial synchronisiert sich auf '\n', das Testprogramm wertet 3 Zahlen, durch mindestens 2 Kommas getrennt, aus. Das einzige unbehandelte Problem ist das "undefined behavior" bei atoi(NULL). Aber Fehlerbehandlung wird bei Arduino traditionell ignoriert.

Mit Stream& als Parameter geht das mit allen Klassen die von Stream erben. Also auch Serial1, Serial2, etc. Oder SoftwareSerial. Und übrigens auch File (um von SD Karten zu lesen), auch wenn dann der Funktionsname nicht mehr passt.
Da sind wir wieder beim Thema wieso man OOP braucht. Neben der Print Klasse das zweite typische Beispiel für Polymorphie in der Arduino Umgebung

Den Puffer global zu haben ist ganz praktisch. Den Index braucht man global nicht. Du kannst auch den Puffer lokal machen und dann Zeiger herumreichen wenn du willst. Man kann statt bool einen char* haben und NULL zurückgeben wenn man noch nicht fertig ist.
Wenn du wiedereintrietsfähigen Code braucht um von mehreren Quellen gleichzeitig zu lessen kannst du das auch leicht anpassen

wiedereintrittsfähig ist nochmal eine Nummer komplizierter als mehrfach aufrufen. Mit einem (1) globalen Puffer und 1 index kannst du nur 1 Stream lesen. Mit dieser Einschränkung kann man schon leben, aber dann kannst du alle genannten Variablen auch lokal statisch machen. Zurückliefern von NULL / char* auf die gelesene Zeile ist eine gute Idee.

Hallo Zusammen,

Ich konnte nun das Problem lösen, nachdem ich von euren Tipps leider hoffnungslos überfordert war. In meiner Lösung brauche ich nur 3 Zeilen Code um einen String zu senden, indem ich die sprintf Funktion verwende. Das ist viel eleganter und auch besser nachvollziehbar.
Die Idee habe ich von hier:

Meint Code sieht nun so aus:

]void Transmit(){
  
char* buffer="00,00,00";
sprintf(buffer, "%02d,%02d,%02d", t,m,h);
Serial.println(buffer);
}[/code

Möglicherweise könnte Dich dann auch dieses Thema interessieren: Serial Communication / String auslesen und nur bestimmten Teil verwenden

Auch Streaming könnte für Dich interessant sein, habe ich im Thema OOP für Anfänger - Einstieg mit nachvollziehbarer Erklärung verwendet:

#include <Streaming.h>
...
Serial << "pin1: " << pin1 << "\tpin2: " << pin2 << "\twechselIntervall: " << wechselIntervall << endl;

von euren Tipps leider hoffnungslos überfordert

Ja da hast du recht. Wenn sprintf die richtige Antwort war, waren wir nicht sehr hilfreich. Sorry.

Nicola666:

void Transmit(){

char* buffer="00,00,00";
sprintf(buffer, "%02d,%02d,%02d", t,m,h);
Serial.println(buffer);
}

Somwhere\SprintfX\SprintfX.ino: In function 'void Transmit(byte, byte, byte)':

Somwhere\SprintfX\SprintfX.ino:4:18: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]

char* buffer = "00,00,00";

Du überschreibst eine im RAM befindliche Textkonstante mit unabsehbaren Folgen.

Schau mal wie die Variable test ihren Wert verändert:

void Transmit(byte t, byte m, byte h) {
  char* buffer = "00,00,00";
  sprintf(buffer, "%02d,%02d,%02d", t, m, h);
  Serial.println(buffer);
}
char* test = "00,00,00";

void setup() {
  Serial.begin(250000);
  Transmit(10, 10, 10);
  Serial.println(test);
}
void loop() {}
10,10,10
10,10,10

%02d produziert auch mehr als zwei Zeichen, wenn der Wert >100 oder <-9 ist,
aus deinem Schnipsel kann man nicht ablesen was t, m und h sein sollen, aber auch Bytes oder Chars können das.
Dann wird nicht nur eine Konstante überschrieben, sondern auch noch irgendetwas anderes,
was dahinter im Speicher liegt.

Die Variante mit sprint benötigt

Der Sketch verwendet 3378 Bytes (1%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 212 Bytes (2%) des dynamischen Speichers, 7980 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.

Eine Variante ohne sprintf (und ohne Überschreiben von Konstanten)

void Transmit(byte t, byte m, byte h) {
  printByte2digits(t);
  Serial.write(',');
  printByte2digits(m);
  Serial.write(',');
  printByte2digits(h);
  Serial.println();
}
void printByte2digits(byte val) {
  if (val < 10) {
    Serial.write('0');
  }
  Serial.print(val);
}
char* test = "00,00,00";

void setup() {
  Serial.begin(250000);
  Transmit(10, 10, 10);
  Serial.println(test);
}
void loop() {}
10,10,10
00,00,00

benötigt

Der Sketch verwendet 2074 Bytes (0%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 196 Bytes (2%) des dynamischen Speichers, 7996 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.

Tja, array und pointer sind doch was anderes :wink:

char buffer[] = "00,00,00"; // wäre in diesem Fall besser
char buffer[9]; // genauso gut.  (so hättest du es aus deinem verlinkten Beispiel auch übernehmen können. )

Für printByte2digits hast du ein Bienchen verdient, Whandall.

Aber wer die Funktion printf noch nicht kannte, dem fehlt auch etwas Basiswissen.

michael_x:
Für printByte2digits hast du ein Bienchen verdient, Whandall.

Ein 'r' in dem Satz wäre mir lieber. :wink:

man könnte printByte2digits noch erweitern um Transmit (optisch) "eleganter" zu machen.

void printByte2digitsAnd(byte val, bool kommaOderNeueZeile = true) {
  if (val < 10) {
    Serial.write('0');
  }
  Serial.print(val);
  if (kommaOderNeueZeile) {
    Serial.write(',');
  } else {
    Serial.println();
  }
}
void Transmit(byte t, byte m, byte h) {
  printByte2digitsAnd(t);
  printByte2digitsAnd(m);
  printByte2digitsAnd(h, false);
}
char* test = "00,00,00";

void setup() {
  Serial.begin(250000);
  Transmit(10, 10, 10);
  Serial.println(test);
}
void loop() {}
10,10,10
00,00,00
Der Sketch verwendet 2070 Bytes (0%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 196 Bytes (2%) des dynamischen Speichers, 7996 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.

Ob r oder n : Kannst auch beides haben.

Danke. :smiley: