Forum: Mikrocontroller und Digitale Elektronik String parsen und dem "Vorzeichen"Char zu weisen


von Stefan (Gast)


Lesenswert?

Hallo,

ich arbeite gerade an einer Bluetooth Verbindung mit dem Arduino. 
Genutzt wird ein Uno R3. Da der Uno aber im Gegensatz zum Mega nur eine 
Serielle Schnittstelle zur Verüfung hat, war ich gezwungen, diese 
Softwareseitig zu erstellen. Die eigentliche StdLib von der Arduino.cc 
Seite hatte einige Probleme, zumal entwder nur abhören oder senden 
möglich ist. Nun habe ich mit der AltSerialSoft eine gute bzw. bessere 
Alternative gefunden. Diese fühlt sich Performancetechnisch genauso wie 
die Serielle Schnittstelle an.

So, nun zu meinem Problem.

Ich möchte gerne Strings von einem Bluetooth Client an den Arduino 
schicken. Das funktioniert auch schon soweit, jedoch teils nicht 
effizent genug.

Das Problem ist, ich sende immer den ganzen Datensatz, welchen ich dann 
in einem Array abspeicher.

Ich würde aber gerne immer nur bestimmte Werte schicken, anstatt dem 
ganzen Datensatz.

Ich stell mir sowas vor.


Wenn der String "A235B\n" ankommt, soll er als Zuweisungschar das A 
nehmen. Somit soll so die Position/Index im Array bestimmt werden. B 
möchte ich als Terminierungszeichen senden.

Nun kann es vorkommen, dass als String auch "A234B,C232D\n" ankommt. 
Hier sollte er natürlich beide Werte verarbeiten.

Bislang komme ich aber nicht mehr weiter.


So sieht meine Funktion derzeit aus. Sie liest alle Zahlen ein, welche 
mit einem "," getrennt sind. Also terminierung erfolgt ein Zeichen, 
welches keine Zahl und nicht "," ist.

1
  // PC USB
2
  if(Serial.available())
3
  {
4
    char ch = Serial.read();
5
    // ASCII-Zeichen zwischen 0 und 9?
6
    if(ch >= '0' && ch <= '9') 
7
    {
8
      // Ja, Wert akkumulieren, solange fieldIndex gültig ist
9
      // Überzählige Felder werden nicht gespeichert
10
      if(fieldIndex < NUMBER_OF_FIELDS) {
11
        tmpSettings[fieldIndex] = (tmpSettings[fieldIndex] * 10) + (ch - '0');
12
      }
13
    }
14
    //Trennzeichen
15
    else if (ch == ',') 
16
    {
17
      // Feldindex inkrementieren
18
      fieldIndex++; 
19
    }
20
    else
21
    {
22
      // Jedes Zeichen außer Ziffern und Trennzeichen ',' beendet das Einlesen der Felder.
23
      // Alle gespeicheren Felder ausgeben
24
      if(fieldIndex == NUMBER_OF_FIELDS-1) for(int i = 0; i < NUMBER_OF_FIELDS; i++) settings[i] = tmpSettings[i];
25
      memset(tmpSettings, 0, NUMBER_OF_FIELDS * sizeof(int));
26
      fieldIndex = 0;
27
      runOnce = true;
28
    }
29
  }
30
}

Danke schonmal. Auch nur evtl. Tutorials Hinweise sind erwünscht.

Gruß Stefan

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Stefan schrieb:
> Ich möchte gerne Strings von einem Bluetooth Client an den Arduino
> schicken. Das funktioniert auch schon soweit, jedoch teils nicht
> effizent genug.
>
> Das Problem ist, ich sende immer den ganzen Datensatz, welchen ich dann
> in einem Array abspeicher.

Wer ist hier "ich"? Wo wird das Array abgespeichert? Auf dem BT-Client? 
Auf dem Arduino?


> Ich würde aber gerne immer nur bestimmte Werte schicken, anstatt dem
> ganzen Datensatz.

Dann mach das doch einfach. Wo kommen denn die Strings her? Wer erzeugt 
die?

von Karl H. (kbuchegg)


Lesenswert?

Stefan schrieb:

> So sieht meine Funktion derzeit aus. Sie liest alle Zahlen ein, welche
> mit einem "," getrennt sind. Also terminierung erfolgt ein Zeichen,
> welches keine Zahl und nicht "," ist.

Ja und?
Ich sehe jetzt in deinem Code nirgendwo, dass das erste Zeichen (welches 
ja der Buchstabe zur Identifikation ist) speziell behandelt werden 
würde.
Ohne den abzutrennen geht schon mal gar nichts.

Mach dich mal mit dem Gedanken vertraut, dass eine Zeile aus mehreren 
Messages besteht. Jede Message kann mit einem Buchstaben beginnen, 
welche dir sagt, was mit der nachfolgenden Zahl passieren soll.

: Bearbeitet durch User
von Conny G. (conny_g)


Lesenswert?

Also ich finde kein Problem bei Deiner Routine, weshalb ist Dir das 
nicht effizient genug?
Man kann / braucht nicht viel mehr machen als die Zeichen einlesen und 
zuordnen.

Rein strukturell könntest Du es noch in Richtung State Machine 
weiterentwicklen, dann wird es etwas klarer und Du kannst noch eine 
Fehlerprüfung in den Empfang einbauen, weil der State auch festlegt, was 
jetzt kommen darf und dadurch eine verarbeitbare Fehlerbedinungen 
entsteht.

State WAITING_FOR_DATA
    warte auf Startzeichen / Startindex
   -> wenn ein Zeichen kommt gibt das den Wert an, der übertragen wird.
    Hier kann ein Wertebereich, eine Wertemenge hinterlegt sein, die 
kommen darf, alles andere: Fehler. Dann wird alles ignoriert bis zur 
Ende-Bedingung oder zu einer neuen Startbedingung: State = WAIT_FOR_END
    -> wenn der richtige Index kommt -> State = RECEIVING_DATA
State RECEIVING_DATA:
    erlaubte Zeichen 0...9, sonst Fehler: WAIT_FOR_END
State WAIT_FOR_END: verwerfe jedes Zeichen bis \n kommt, dann State = 
WAITING_FOR_DATA

und so weiter.
Hier verarbeitest Du quasi die "Grammatik" Deiner Übertragung und hast 
immer einen Pfad, der weitergeht von jedem Punkt.

Google mal nach State Machine, kann man ganz einfach aber auch ganz 
aufwändig machen.

Aber im Prinzip tun es ein paar States wie die obigen und mehrere if 
oder ein switch/case-Block.

Vg,
Conny

von Karl H. (kbuchegg)


Lesenswert?

Conny G. schrieb:
> Also ich finde kein Problem bei Deiner Routine, weshalb ist Dir das
> nicht effizient genug?
> Man kann / braucht nicht viel mehr machen als die Zeichen einlesen und
> zuordnen.

Sagt er auch nicht.
Aber es ist ein wenig sinnlos, 50 Werte zu übertragen, wenn sich nur 
einer geändert hat. Und genau da will er hin: anstatt ständig alle 50 
übertragen zu müssen, will er sich eine Kennung einbauen, so dass er 
einen einzelnen Wert samt seiner 'Adresse' auf den Weg schicken kann.

Im Grunde entdeckt er gerade, dass die ürsprüngliche 
Design-Entscheidung, einfach alle 50 Werte, jeweils durch Komma getrennt 
und mit einem \n abgeschlossen, nicht unbedingt die beste Wahl war. Er 
ist drauf und drann zu entdecken, dass man in einem Protokoll auch 
Steuerinformationen braucht, die den Empfänger bei der Interpretation 
der empfangenen Daten leiten.

: Bearbeitet durch User
von Stefan (Gast)


Lesenswert?

@rufus: Die Werte werden auf dem Arduino in settings[] abgespeichert die 
ich später in meinen Funktionen wieder aufrufe.

@kbuchegg: Ja richtig. Ich weiß nicht genau, wie ich das machen soll.

von Stefan (Gast)


Lesenswert?

Ja kbuchbegg bringt es auf den Punkt. Auch wenn es in meinem Fall nur 5 
byte und 1 uint sein werden, kommt mir das teils relativ langsam vor.

Ich habe derzeit einmal die Serielle Konsole auf dem Handy. Des weiteren 
habe ich eine kleine App mit dem App Inventor schnell 
zusammengeschrieben. Ziel ist es später, mit den Slider mehr oder 
weniger in Echtzeit den Farbton von RGBs zu bestimmen. Bei vorherigen 
Versionen bekam ich irgendwann Probleme, da er bereits neue Daten 
geschickt hat, ehe die alten verarbeitet/im Buffer geschrieben wurden.

von Stefan (Gast)


Lesenswert?

Gibt es abzufragen, wievel Buchstaben noch bis zum nächsten 
Steuerzeichen kommen? Dann könnte ich die Abfrage vervollständigen mit 
einer for-Schleife, die so häufig die Werte 0 - 9 einliest, bis ein 
Steuerzeichen kommt.
1
  if(Serial.available())
2
  {
3
    static char ch = Serial.read();
4
    char ch2;
5
    if (ch == 'A')
6
    {
7
      ch2 = Serial.read();
8
      tmpSettings[fieldIndex] = (tmpSettings[fieldIndex] * 10) + (ch2- '0');
9
      fieldIndex++;
10
          }
11
    else if (ch == 'B')
12
    {
13
      ch2 = Serial.read();
14
      tmpSettings[fieldIndex] = (tmpSettings[fieldIndex] * 10) + (ch2- '0');
15
      fieldIndex++;
16
    }
17
    else if (ch == 'C')
18
    {
19
      ch2 = Serial.read();
20
      tmpSettings[fieldIndex] = (tmpSettings[fieldIndex] * 10) + (ch2- '0');
21
      fieldIndex++;
22
    }
23
24
25
....

von Conny G. (conny_g)


Lesenswert?

Die Frage verstehe ich nicht?
Was gefällt Dir nicht an der State-Machine?

von Stefan (Gast)


Lesenswert?

Wenn du mir noch sagst, was eine State Machine genau macht! ;)

von Stefan (Gast)


Lesenswert?

Was ich an der ersten Lösung von mir nicht mag ist, dass wenn ich nur 
den ersten Wert ändern möchte, den kompletten String eingeben muss.

von Conny G. (conny_g)


Lesenswert?

Stefan schrieb:
> Wenn du mir noch sagst, was eine State Machine genau macht! ;)

Das hab ich doch oben geschrieben:
Beitrag "Re: String parsen und dem "Vorzeichen"Char zu weisen"

von Conny G. (conny_g)


Lesenswert?

Stefan schrieb:
> Was ich an der ersten Lösung von mir nicht mag ist, dass wenn ich nur
> den ersten Wert ändern möchte, den kompletten String eingeben muss.

Ja, das ist klar.
Und deshalb möchtest Du etwas schicken wie A123B, also abstrakter 
<Index-Angabe>(<Zeichen 0..9>{n Mal}])<Terminator>.

Aus meiner Sicht spräche auch nichts dagegen, die Syntax zu erweitern 
in:

<Index-Angabe>(<Zeichen 0..9>{n Mal}])<Terminator>[;<das ganze 
nochmal>[;<das ganze nochmal>]]

Und die State Machine dafür funktioniert so:
die SM wird initialisiert auf einen Startwert, der heisst "Startzeichen 
empfangen".
Dann wird immer ein Zeichen eingelesen, solange bis das Startzeichen 
empfangen wird (vielleicht ist auch jedes beliebige Zeichen das 
Startzeichen, auch ok).
Danach weisst Du von obiger "Grammatik", dass n Zeichen 0..9 kommen 
dürfen, also bist Du im Modus "n Zeichen Daten empfangen", Du füllst 
also Deinen Puffer und hörst bei n Zeichen auf, wartest dann auf das 
Ende-Zeichen, den Terminator.
Jede dieser Phasen hat einen "State"-Wert (wie ich schon geschrieben 
hatte).

Damit hast Du eine Schleife, die immer ein einzelnes Zeichen einliest 
und bewegst Dich in diesen Stati weiter.
Immer wenn was schief geht, Du etwas empfängst, was nicht passt kannst 
Du auf einen State gehen, der die Situation wieder in Ordnung bringt, 
üblicherweise "Warte auf Terminator" (und damit ignorierst Du alles, was 
dann noch kommt) oder einfach "Warte auf Startzeichen".

Das entspricht der Schleife, die  Du selbst schon gepostet hast, ist 
aber etwas aufgeräumter, transparenter, flexibler. Flexibler gerade in 
Fehlersituationen, weil Du frei zwischen den States springen kannst.
Genau deshalb würde ich auch keine for-Schleife, sondern lieber die 
State Machine machen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.