ich benötige eure Hilfe da ich derzeit daran verzweifle.
Für die meisten dürfte die Problematik wahrscheinlich kein Problem darstellen...
Bisher war mein Programm so aufgebaut, dass ich den einkommenden String, mit einer bestimmten Zeichenfolge eingelesen habe mit Read.StringUntil();.
Anschließend konnte ich den zwischengespeicherten String abfragen und mit If-Befehlen daraufhin was ausführen lassen.
Die verkürzte Version vom Sketch mit den wichtigen Punkten:
String btmsg; // Variable fuer den Bluetooth Befehl
void setup() // Setup Beginn
{
btSerial.begin(9600);
btSerial.println("Bluetooth available");
}
void loop() // Programmbeginn
{
if (btSerial.available())
{
btmsg = btSerial.readStringUntil('\n');
if (btmsg == "STOP")
{
Stop();
btSerial.println("STOP");
}
else if(btmsg == "VOR")
{
Vorwaerts();
btSerial.println("Vorwaerts");
}
}
}
Jetzt muss ich mein Programm erweitern indem der String eine Zeichenfolge und eine Zahl beeinhaltet. Die Zeichenfolge gibt die Aktion an welche ich ausführen möchte (z.B. PWM und Servo) und die Zahl beeinhaltet dann den Ansteuerwert (z.B. Servo den Winkel oder eine PWM-Vorgabe).
Und jetzt hoffe ich ihr könnt ihr mir helfen, wie man am besten die beiden Parameter abfragt aus einem String und in eine Variable speichert um darauf zurückzugreifen in einer If-Abfage bzw. den Wert daraus als int zuverarbeiten.
Ich hoffe es ist einigermaßen verständlich geschrieben und ihr könnt mir helfen.
Das wäre für Strings das Grundgerüst. Das Endeerkennungszeichen \n und \r wird mit Serialprintln() automatisch erzeugt.
In Terminals wie hterm sieht man das.
Wenn du aber Datentypen mit strings mischen möchtest verkompliziert das alles. Du müsstest dann aus dem Strings einzelne Elemente rausfischen. Das geht auch, dafür gibts auch Funktionen, ich rate davon ab, wenn es nicht unbedingt notwendig ist.
Kannst du nicht Zahlencodes für bestimmte Befehle definieren? Du überträgst immer einen Datentyp unsigned int. Alle Werte die meinetwegen größer 1000 sind, sind irgendwleche Steuercodes. Alles was 0 bis 255 ist, sind deine PWM Werte.
strings kann man nicht wie Zahlen vergleichen. Dafür gibts die Funktion strncmp und du brauchst ein ausreichend großen Einlesepuffer.
Sicher? Ich hab das auch mal so gemacht, mit Strings versteht sich, nicht mit char arrays (hierfür strncmp). Da er String nutzt, sollte das daran nicht scheitern.
ein String besteht letztlich aus einem char array und ist Null terminiert und ein Zeiger zeigt auf die erste Adresse. Das ist der Grund warum ein direkter Vergleich nicht möglich ist. In dem Link von dir ist das in "Arduino Komforfunktionen" versteckt. Besser erklären kann ich das leider nicht.
Wobei der Befehl nur zwei verschiedene Zeichenfolge beiinhaltet PWM / JOY.
Der Steuerwert kann Werte im Bereich von -180 - +180 annehmen (JOY) und bei PWM 0 - 100.
Der Aktionsbefehl soll abgefragt werden per If-Abfrage oder case Befehl um dann in den entsprechenden Abschnitt zu springen. Darauf wird im entsprechenden Aktionsabschnitt der Steuerwert direkt verarbeitet als Vorgabe.
Sollte es eine Möglichkeit geben, die die Programmier Umsetzung vereinfacht nehme ich diese auch sehr gerne an.
Meine Vorstellung zur Umsetzung des Strings war folgende:
Kann es sein dass du den CMDMessenger nutzen möchtest?
Denn der hat dieses alles schon eingebaut.
ein String besteht letztlich aus einem char array und ist Null terminiert und ein Zeiger zeigt auf die erste Adresse.
Aber doch nicht die String klasse, welche hier verwendet wird....
Es ist ok, wenn du die für dich ausblendest.
Aber da sie hier verwendet wird, ist deine Ansage leider unzutreffend.
combie:
Kann es sein dass du den CMDMessenger nutzen möchtest?
Denn der hat dieses alles schon eingebaut.
Nein, bisher nichts gehört vom CMDMessanger.
Das war für mich erstmal ein guter strukturierter Aufbau eines Strings.
Wäre das eine funktionierende Alternative:
Wäre die Auswertung des Strings denn viel einfacher wenn statt dem Aktionsbefehl "PWM" oder "JOY" Zahlenwerte wie "01" oder "02" verwendet werden? Sodass der Befehl dann z.B. aus "01_90\n" besteht?
Kann man dann nur mit dem Befehl parseInt(); arbeiten sodass für die beiden ankommenden Zahlenwerte jeweils eine Variable durch parseInt(); beschrieben wird? Da müsste ihr mir nur dann was für die negativen Zahlen einfallen lassen...
Aber doch nicht die String klasse, welche hier verwendet wird....
-->Jup... der Vergleichsoperator == wird hierbei mit der Funktion compare überladen (wenn ich das hier richtig deute)
Der Aktionsbefehl soll abgefragt werden per If-Abfrage oder case Befehl um dann in den entsprechenden Abschnitt zu springen. Darauf wird im entsprechenden Aktionsabschnitt der Steuerwert direkt verarbeitet als Vorgabe.
-->Dir geht es also nur darum, den String passend zu verarbeiten? Also kommt der Zeichensatz schon richtig an, beziehungsweise hast Du Dir diesen schon auf der seriellen Konsole ausgeben lassen?
Ansonsten kannst Du ja mithilfe der Trennzeichen | verschiedene Substrings generieren, ein String enthält den Befehl, auf den Du vergleichen kannst, ein String die Daten, welche Du zu einer Variablen umwandelst. Das Aufteilen des Strings dann entweder a) indem Du ein festes Muster verwendest, also z.B. das Trennzeichen befindet sich an genau der 5. Stelle... oder Du suchst das Trennzeichen mit find_first_of.
also wenn du auf Strings nicht angewiesen bist, dann verzichte darauf. Denn damit entfällt auch das "mühsame" herausfischen einzelner Strings aus dem Datenstrom. Seit paar Wochen nutze ich ein union struct als Datenpaket. Damit kannst du direkt und ohne Umschweife auf Einzelwerte zugreifen.
union Nachricht
{
struct
{
uint8_t adresse;
int32_t data1;
int16_t data2;
uint8_t data3;
};
uint8_t asArray[8]; // Summe aller struct Datentypen, für Zugriff über Index
} empfDaten, sendDaten; // zwei gleiche union Buffer anlegen
Einlesen von der seriellen wie sicherlich jetzt auch schon über den Index in das empfDaten.asArray[]
Danach direkter Zugriff auf was du vergleichen oder wissen möchstest wie zum Bsp.
empfDaten.adresse
empfDaten.data1
empfDaten.data2
empfDaten.data3
und verschicken tuste dein sendDaten.asArray[] Byteweise auch über einen Index.
Ich möchte das nun nicht als Weisheit letzter Schluss anpreisen. Aber ich finde das so dermaßen genial und einfach, dass ich auf Strings komplett verzichte. Die Handhabung ist wesentlich einfacher. Ob man nun Strings als Befehlscode festlegt oder paar Zahlen ist Jacke wie Hose.
Vielen Dank für die erneute Rückmeldung, das sieht auch verständlich aus.
Ich habe heute auch nochmal etwas versucht, wenn ich die Zeichenfolge "PWM" bzw "Joystick" doch durch eine Zahlenfolgen ersetze und dann mit dem Befehl parseInt() die Zahlenfolgen auswerte.
Kam ich auf folgenden Sketch:
int aktion;
int funktion;
char* c;
void setup()
{
Serial.begin(9600);
Serial.println("Kommunikation vorhanden");
}
void loop()
{
if (Serial.available())
{
if (Serial.find('B'))
{
char c=Serial.read();
int aktion = Serial.parseInt();
int funktion = Serial.parseInt();
Serial.print("Aktion: ");
Serial.println(aktion);
Serial.print("Funktion: ");
Serial.println(funktion);
}
}
}
Gibt es da irgendwelche Gründe davon abzusehen?
Ansonsten danke ich euch allen für eure Ideen und Hilfestellung!
Denn damit entfällt auch das "mühsame" herausfischen einzelner Strings aus dem Datenstrom.
Mühsam ist das auch nur wirklich mit der String Klasse. Mit C Strings ist es ziemlich einfach Strings zu zerlegen auch Dinge wie "Kommando,123" in wenigen Zeilen zu bearbeiten
und verschicken tuste dein sendDaten.asArray[] Byteweise auch über einen Index.
Du hast eine Union mit einem Array! Da muss man nichts byte-weise verschicken. Serial.write() kann ein Array auf einmal versenden.
Strings und wieder nicht Strings. Mal ehrlich wer soll da durchblicken? Ich nutze demzufolge nur C Strings. Auch gut. Das ist gefühlt das letzte große Thema wo ich immer noch nicht ganz durchblicke. Weil das nicht im Datenblatt steht.
Nochmal nachgefragt. Zaubern kann die Stringklasse doch auch nicht. Intern muss sie doch auf C-Strings zurückgreifen und alles zerlegen? Nur durch die fertigen Funktionen bekommt man das nicht mit?
Naja, das Verhalten, also das Interface, der String Klasse, ist recht gut dokumentiert.
Die Innereien nicht.
Aber, das ist in der OOP auch richtig und gut so.
Verbergen der Implementation, das ist ein Hauptsinn und Zweck, der OOP.
Intern muss sie doch auf C-Strings zurückgreifen und alles zerlegen?
Wahrscheinlich, aber kein MUSS.
z.B. kennt Pascal mehr String Typen, als als sich ein C Programmierer vorstellen kann
Und bei einer Stringklasse wie String "könnte" ich mir vorstellen, dass sie intern eine Variante, ähnlich den AnsiStrings nutzt.
Aber was solls:
Die Innereien von String sind kein Geheimnis. Sie liegen im Quellcode vor.
Der Programmierer muss sich nicht für die Innereien interessieren. Es reicht wenn sie funktionieren.
Tipps zum Umgang damit:
Mit möglichst wenig String Instanzen arbeiten.
In setup() schon den Speicher reservieren.
Das wirkt der Fragmentierung entgegen.
Je weniger RAM zur Verfügung steht, desto wichtiger sind die beiden Punkte.
combie:
Je weniger RAM zur Verfügung steht, desto wichtiger sind die beiden Punkte.
...und desto eher sollte man vielleicht ganz auf die String-Klassen verzichten, und die Standard-C Funktionen nutzen. Da gibt es auch 'strtok', was genau das macht, was Pupile will.
Da gibt es auch 'strtok', was genau das macht, was Pupile will.
Ja, strtok() kann ihr/ihm helfen.
Ist allerdings nur ein Teil des notwendigen Parsers.
Darum habe ich ihm auch versucht den CMDMessenger schmackhaft zu machen.
Denn der hat alles drin. Und ist einfach zu nutzen.
Der hat zwar etwas Speicherhunger, neigt aber nicht zur Fragmentierung.
combie:
Darum habe ich ihm auch versucht den CMDMessenger schmackhaft zu machen.
Denn der hat alles drin. Und ist einfach zu nutzen.
Der hat zwar etwas Speicherhunger, neigt aber nicht zur Fragmentierung.
Doc_Arduino:
Nochmal nachgefragt. Zaubern kann die Stringklasse doch auch nicht. Intern muss sie doch auf C-Strings zurückgreifen und alles zerlegen?
Natürlich verwendet sie intern C Strings. Aber die Methoden die sie nach außen zur Verfügung stellt sind nur recht eingeschränkt und umständlich. Mit C Strings kannst du mehr machen (vor allem was Konvertierung und Formatierung betrifft). Und das einfacher, da die Strings in situ behandelt werden; statt neuen Objekten oder einem Index bekommst du Zeiger zurück die man direkt an andere Funktionen übergeben kann. z.B. eine Such-Funktion liefert einen Zeiger auf den gefundenen Teil-String den man so wie er ist an eine Konvertierungs-Funktion-übergibt. Einfacher geht es nicht. Wenn du mit der String Klasse nach etwas suchst bekommst du nur den Index im String und wenn du damit subString() machst hast du einen neues Objekt