Serial Communication / String auslesen und nur bestimmten Teil verwenden

Hi an alle fröhlichen programmierer da draußen,

ich bin mit Arduino soweit vertraut, dass ich mir zutraue dieses Projekt zu verwirklichen. Leider komme ich nur mit der Serial Communication / Strings im allgemeinen nicht zurecht.
Hab schon etliche Threads hier durchgeschaut, finde nichts was mir direkt weiterhilft...

Darum nun zur Erklärung meines vorhabens / Problems.

Ich bekomme von einer Uhr den Seriellen Zeitstring 1x Pro sec. Diesen String möchte ich auswerten um die Uhrzeit zu erhalten.

Soweit so gut, der String sieht so aus:

<STX>D:tt.mm.jj;T:w;U:hh.mm.ss;uvxy<ETX>

Ich brauche also hh, mm und ss in einer Variable.
Ich kann über die COM Schnittstelle RXD & TXD auf Pin 0 / 1 des Arduinos führen um die Serielle Kommunikation durchzuführen.(?)

Bis hierhin und nicht weiter...
MIr fehlen da leider die notwendigen programmier Kenntnisse um dieses Problem zu lösen.

Ich hoffe ihr könnt mir da weiterhelfen, Denkanstöße geben, die man auch als "nicht Programmierer" versteht.

Grüße

Ich kann über die COM Schnittstelle RXD & TXD auf Pin 0 / 1 des Arduinos führen um die Serielle Kommunikation durchzuführen.(?)

Leider sagst du nicht welchen Arduino du verwenden möchtest.

Bei vielen sind Pin 0 und 1 schon mit einem USB-Serial Wandler verbunden.
Und dann solltest du sie nicht mehr für eigene Zwecke missbrauchen.

Einen einfachen Parser bekommen wir mit der AVR-LibC geschenkt dazu:

// <STX>D:tt.mm.jj;T:w;U:hh.mm.ss;uvxy<ETX>

char input[]="D:tt.mm.jj;T:w;U:23.mm.ss;uvxy"; // Stunde 23

void setup() 
{
  Serial.begin(9600);      
  Serial.println("Start");
  
  int hh=0; // <<-- da drin soll die Stunde landen
  sscanf(input,"D:tt.mm.jj;T:w;U:%i.mm.ss;uvxy", &hh);
  Serial.print("Die Stunde sagt: "); Serial.println(hh);
}

void loop() 
{

}

combie:
Leider sagst du nicht welchen Arduino du verwenden möchtest.

Bei vielen sind Pin 0 und 1 schon mit einem USB-Serial Wandler verbunden.
Und dann solltest du sie nicht mehr für eigene Zwecke missbrauchen.

Einen einfachen Parser bekommen wir mit der AVR-LibC geschenkt dazu:

// <STX>D:tt.mm.jj;T:w;U:hh.mm.ss;uvxy<ETX>

char input[]="D:tt.mm.jj;T:w;U:23.mm.ss;uvxy"; // Stunde 23

void setup()
{
  Serial.begin(9600);     
  Serial.println("Start");
 
  int hh=0; // <<-- da drin soll die Stunde landen
  sscanf(input,"D:tt.mm.jj;T:w;U:%i.mm.ss;uvxy", &hh);
  Serial.print("Die Stunde sagt: "); Serial.println(hh);
}

void loop()
{

}

Hi, mir steht der Arduino Uno und Mega zur Verfügung. Der Uno ist allerdings jener, den ich gern dafür verwenden würde.
Kannst du mir die Funktion des Befehls sscanf erklären? Woher weiß der "Prozessor" was genau ich haben möchte? und wo er es hinpacken muss?

Einfach mal sscanf c++ in Google einzugeben ist zu kompliziert für Dich?

Da findest Du z.B. das hier.

Gruß Tommy

Ich glaube aber mit scanf eine sichere serielle Kommunikation aufzubauen ist nicht unbedingt die beste Lösung.
Da verlässt man sich ganz schön auf die "richtige" Blockung.

Ulli

Nach seiner Angabe hat die Zeichenkette einen definierten Aufbau. Warum sollte man dann nicht sscanf benutzen?

Gruß Tommy

ZomNic:
Hi, mir steht der Arduino Uno und Mega zur Verfügung.

Der Mega ist wegen mehrerer serieller Schnittstellen besser geeignet, zumindest zum Probieren.

Von einer anderen Sprache kenne ich was explodierendes, was mich zu diesen Zeilen, abgeleitet aus #1, inspiriert:

// <STX>D:tt.mm.jj;T:w;U:hh.mm.ss;uvxy<ETX>

char input[] = "D:27.09.18;T:5;U:18.12.05;uvxy"; // Stunde 18

void setup()
{
  Serial.begin(9600);
  Serial.println("Start");

  //int hh = 0; // <<-- da drin soll die Stunde landen
  char * pch_hh, * pch_mm, * pch_ss;
  pch_mm = strtok (input, ":.;");
  for (byte j = 0; j < 7; j++) {
    pch_hh = strtok (NULL, ":.;");
  }
  pch_mm = strtok (NULL, ":.;");
  pch_ss = strtok (NULL, ":.;");
  Serial.print(pch_hh);
  Serial.print(' ');
  Serial.print(pch_mm);
  Serial.print(' ');
  Serial.print(pch_ss);
  Serial.println();
}

void loop() {}

strtok wäre jetzt auch mein Ansatz gewesen, oder auch Zeigerarithmetik in Verbindung mit atoi(), da da die Daten immer an der gleichen Indexposition stehen

Aus meiner Sicht , kann ich Tommy nur zustimmen!

Tommy56:
Nach seiner Angabe hat die Zeichenkette einen definierten Aufbau. Warum sollte man dann nicht sscanf benutzen?

Eigentlich kann ich mir nur ein Argument gegen sscanf vorstellen:
Man kennt es noch nicht.

Und das ist allerdings auch eine Chance!

Tipp:
Es finden sich in der Libc noch mehr nützliche Funktionen.
Es lohnt sich diese mal zu untersuchen.

Genau deswegen. (s)scanf als Gegenstück zu (s)printf ist schon einen Blick wert, auch wenn die float-Unterstützung aus Speichergründen beim Arduino entfallen ist.
Float benutzt man eh nur selten und wenn, dann besser nur zur Ausgabe.

Man kann so eine Date-Time-Zeichenkette in 1 Zeile zerlegen und in Zahlenvariablen packen.

Gruß Tommy

combie:
Und das ist allerdings auch eine Chance!

// <STX>D:tt.mm.jj;T:w;U:hh.mm.ss;uvxy<ETX>

char input[] = "D:27.09.18;T:5;U:18.12.07;uvxy"; // Stunde 18

void setup()
{
  Serial.begin(9600);
  Serial.println("Start");

  int hh = 0, mm = 0, ss = 0; // Hier sollen die Zahlenwerte landen
  sscanf(input, "D:%*d.%*d.%*d;T:%*d;U:%d.%d.%d;uvxy", &hh, &mm, &ss);
  Serial.print("Die Zeit ist: "); Serial.print(hh); Serial.print(" "); Serial.print(mm); Serial.print(" "); Serial.println(ss);
}

void loop() {}

Ja, der Stern entwickelt einen gewissen Charme, dem kann ich mich nicht entziehen. Hut ab vor den wissenden Menschen, die ihr Wissen mit uns teilen 8)

EDIT 2018.09.28 21:50: %i in %d geändert, siehe #18.

das mit dem * sind ja wirklich neue Aussichten, wenn das tatsächlich so funktioniert, wie ich es interpretiere.

Warum sollte es nicht funktionieren (s)scanf gehört zu den uralten Funktionen von C. Das ist seit Jahrzehnten erprobt. (Wenn keiner einen Fehler in die Implementation baut)

Gruß Tommy

Na ja, es sieht halt zu einfach aus.

Auch Funktionen wie fgets sind leider viel zu wenig bekannt.

Gruß Tommy

ElEspanol:
Na ja, es sieht halt zu einfach aus.

Magie!

Den Brüdern sprintf und sscanf könnte man auch noch den Datentype float beibringen.

Kostet zwar Speicher, aber wenn man das Feature braucht, ist man (bin ich) froh, es nicht selber schreiben zu müssen.

Dazu einfach eine Datei namens platform.local.txt neben die schon vorhandene platform.txt legen.

# erlaubt float in sprintf und seinen Brüdern.
#compiler.c.elf.extra_flags=-Wl,-u,vfprintf -lprintf_flt -lm

# erlaubt float in sscanf und seinen Brüdern.
#compiler.c.elf.extra_flags=-Wl,-u,vfscanf -lscanf_flt -lm

# erlaubt float in sscanf,sprintf und ihren Brüdern.
#compiler.c.elf.extra_flags=-Wl,-u,vfprintf -lprintf_flt  -Wl,-u,vfscanf -lscanf_flt -lm

Die jeweilig gewünschte Zeile durch das weglassen des # vor "compiler "aktivieren.

Das kostet aber gut RAM.
Bei den ESP8266 ist es aktiviert, die haben ja auch mehr davon.

Gruß Tommy

Eher Flash, als Ram.

Ca 3,5kByte Mehrbedarf in Vollausstattung

Das ist die Kröte, die man dann zu schlucken hat.
Nix gibts kostenlos.

Ich als C++ Dilettant bin ja immer an "schönen" String-Auswertungen interessiert und habe hier im Forum auch schon viel Lehrreiches und Nützliches erfahren - bei dieser Gelegenheit: Danke an alle "Mitwirkenden"! :slight_smile:

Das mit dem * ist wirklich "cool" habe das auch gleich ausprobiert und es funktioniert wunderbar. :slight_smile:
Werde ich sicher zukünftig verwenden - hab was dazugelernt - Danke!

Leider funktioniert der Code aus #10 nicht ganz wie erwartet.

agmue:

// <STX>D:tt.mm.jj;T:w;U:hh.mm.ss;uvxy<ETX>

char input[] = "D:27.09.18;T:5;U:18.12.07;uvxy"; // Stunde 18

void setup()
{
 Serial.begin(9600);
 Serial.println("Start");

int hh = 0, mm = 0, ss = 0; // Hier sollen die Zahlenwerte landen
 sscanf(input, "D:%*d.%*d.%*d;T:%*d;U:%i.%i.%i;uvxy", &hh, &mm, &ss);
 Serial.print("Die Zeit ist: "); Serial.print(hh); Serial.print(" "); Serial.print(mm); Serial.print(" "); Serial.println(ss);
}

void loop() {}

Mit den Beispieldaten

char input[] = "D:27.09.18;T:5;U:18.12.07;uvxy";

und der Auswertung:

sscanf(input, "D:%*d.%*d.%*d;T:%*d;U:%i.%i.%i;uvxy", &hh, &mm, &ss);

funktioniet es ja "gerade" noch :slight_smile:
Aber wenn man die Beispieldaten nur ein klein wenig verändert, dann gibt es "unerwünschte" Ergebnisse. Beispiel:

char input[] = "D:27.09.18;T:5;U:18.08.09;uvxy";

Also geändert wurde auf 18.08.09
Das Problem sind die führenden Nullen, die bei sscanf und %i dazu führen, dass die Zahl als Oktalzahl angesehen wird und da gibt es eben kein "08" oder "09".
Der "Auswertung" müsste also so lauten, dann funktioniert es:

sscanf(input, "D:%*d.%*d.%*d;T:%*d;U:%d.%d.%d;uvxy", &hh, &mm, &ss);

also %d statt %i.

uxomm:
also %d statt %i.

Ich habe das von combie kritiklos übernommen, aber auch nicht an oktal gedacht.

Danke, ich habe es in #10 geändert.