Hallo und zwar habe ich ein kleines phänomen, beim Übertragen der Daten von der GUI zum Arduino, der diese wiederum in die SD-Karte abspeichert.
Kurz erklärt, wenn ich den Sendbutton meiner GUI betätigte, sollen die Drei Parameter an dem Arduino gesendet werden und in die SD (csv.datei) gepspeichert werden.
D.h. bei jedem senden werden 3 parameter gesendet und es stehen immer 3 parametr pro zeile.
So zusagen so: 3;4;4
2;1;0
Das klappt auch nur ab und an passiert es, dass die parameter folgendermaßen gespeichert werden:
3;4;4;2;1;0 und das soll nicht sein.
Mit GUI meinst du sicherlich ein Programm auf einem anderen Rechner (PC, Schläppi, MAC, ...) - oder ?
Gegenfrage: Wie wertest du denn auf dem Arduino die eingehenden Daten aus ?
Machst du das selbst zu Fuß oder verwendest du z.B. den Messenger ?
Passen die COM-Einstellungen auf beiden Seiten ?
Hi also die GUI das ist eine selbstprogrammierte Benutzeroberfläche.
Hmm welchen messenger?
Meinst du mit EInstellung die Baudrate? Wenn ja, hab sie auf beiden Seiten auf 9600
Hier mal den code vom arduino:
void setup()
{
//Serialport verbindung mit 9600Baudrate
Serial.begin(9600);
/*########LED Konfig#########*/
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2,OUTPUT);
/*#######ENABLE-PIN########*/
pinMode(EN,OUTPUT);
/*###########################*/
/*ChipSelect Pin , den standard als Ausgangs sonst geht SD library nicht*/
pinMode(53, OUTPUT);
/*######################################################################*/
/*############################Taster-Konfig####################################*/
pinMode(kipp_left, INPUT_PULLUP); //kippstellung links in pin30 INPUT_PULLUP->interner pullup, somit pullup aktivieren
pinMode(kipp_right, INPUT_PULLUP);//kippstellung rechts in pin31 und wird input-widerstand aktiviert (intern)
/*kippleft= low -> linksstellung
kippright= low= rechtsstellung
kippleft & kippright = HIGH -> mittelstellung */
/*##############################################################################*/
}
void loop()
{
//wird ein zeichen über Serialport empfangen?
if (Serial.available() > 0)
{
//falls ja, schreibe diesen in inByte
inByte = Serial.read();
/*##################################I2C################################*/
//falls inByte= $ -> ASCII(36), Arduino im Sendemodus
if (inByte==36)//falls $=36 ankommt arduino sendet, sonst empfangen
{
blink_send();
read_SD();
}//endif
if (inByte==63) //falls ?=63 in Byte dann führe funktion i2c-scan durch
{
i2c_scan();
}
if(inByte==38)
{
digitalWrite(EN,HIGH);
Serial.println("EN-EIN");
}
//falls Arduino &(38) empfängt , d.h. er empfänngt von GUI
else //(inByte==38)
{
blink_receive();
write_SD();
}//endif
/*###############################SPI###################################*/
//falls Arduinio ascii(33)->! empfängt, dann soll er in datei für spi dieparameter schreiben
if(inByte==33)
{
write_spi();
}//endif
if (inByte==35)
{
read_spi();
}//endif
}//endif serial available
void write_SD()
{
if (Serial.available()>0)
{
if (inByte ==13) //enter
{
Serial.print("Initializing SD Card...");
if (!SD.begin(chipSelect))
{
Serial.println("Initialization failed");
return; //tue nichts
}//endif
Serial.println("SD Card initialized");
if (SD.exists("test2.csv"))
{
Serial.print("File exists");
}
else
{
Datei=SD.open("test2.csv", O_CREAT | O_WRITE);
Datei.println("Adresse;Register;Data");
Datei.close();
}
//Schreibzugriff
Datei= SD.open("test2.csv", O_CREAT | O_WRITE); //Das gleiche wie FILE_WRITE. doch es beschleunigt das Schrieben auf SD
//Datei=SD.open("test2.txt", FILE_WRITE);
//falls Datei ok, schreibe
if (Datei)
{
//delay(150); //verzögerung, da schreiben auf SD karte langsamer ist
//Datei.write(Test); //ohne zeilenumbruch
//Datei.write(13); //schreibe nach jedem eintrag ein carriage return line feed, damit strings zeilenweise einetragen werden
Datei.println(Text);
//inhalt von Array Text in SD schreiben(commands.txt)-> println mit zeilenumbruch
Datei.close(); //Damit änderung gespeichert werden, diese Datei schliessen
Serial.println("Data stored in SD card");
/*Nachdem enter gekommen ist und daten gespeichert wurden
setze Array wieder auf 0 und lösche inhalt*/
index=0; //index auf 0 initialisieren
memset(&Text[0], 0, sizeof(Text)); //durch memset wird Array gelöscht
Serial.println("Array initialized");
}//endif datei vorhanden?
}//endif kontroll enter
else//fals kein enter,soll weiter in array speichern
{
if (index <400)//schauen , ob array overflow hat
{
Text[index]=inByte; //solange kein ´Enter vom Arduino empfangen wird, schreibe in Array
index++; //und zähle position hoch -> z.b. wenn ich hallo sende-> jeder buchstabe bekommt eine position
}//endif check array of overfolw
}//end else
}//endif serial available
}//end fu
Und das is der code von der GUI:
Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_i2c_receive.Click
RichTextBox1.ForeColor = Color.Black
'/------Kontrollvariale an Arduino =1 , somit weiß er das er an GUI senden soll---/
If Not SerialPort_i2c.IsOpen = True Then
SerialPort_i2c.Open()
End If
'SerialPort_i2c.Write(Chr(kontrol_receive)) 'muss in char gekcastet werden ,da arduino diesen in byte umwandelt
SerialPort_i2c.Write("$") 'kontrollvariable
End Sub
rivate Sub btn_i2c_send_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_i2c_send.Click
'Mit try wird versucht Übertragung an Arduino durchzuführen'
Try
If CheckBox_EN.Checked = True Then
SerialPort_i2c.Write("&")
End If
SerialPort_i2c.Write(Numeric_addr_i2c.Value & ";") '; damit in csv datei in spalten
SerialPort_i2c.Write(Numeric_reg_i2c.Value & ";")
If CheckBox_i2c.Checked = True Then
SerialPort_i2c.Write(Numeric_data_i2c.Value) 'durch vbr = Enter, d.h. wenn ein ganzer String wird ein enter gesendet und arduino weiß ok gsnzer String
End If
'SerialPort_i2c.Write(Numeric_data_i2c.Value) 'durch vbr = Enter, d.h. wenn ein ganzer String wird ein enter gesendet und arduino weiß ok gsnzer String
SerialPort_i2c.Write(vbCr)
MsgBox("Transfer successful")
If SerialPort_i2c.IsOpen = True Then
SerialPort_i2c.Close()
End If
'End If
'Falls Übertragung fehlschlägt, Catch block um fehler auszubügeln und eigene message von vb'
Catch ex As Exception
MsgBox("Transfer failed - Please check the connection" & vbCrLf & ex.Message)
End Try
End Sub
Ob das in eine neue Zeile kommt, liegt doch nur an der Routine die die SD-Karte beschreibt, oder? Das Linefeed sollte ja nicht seriell gesendet werden, sondern nach jedem Datensatz per Hand eingefügt werden. Da sehe ich nicht wie er das verschlucken kann.
CR und LF sind aber zwei getrennte Zeichen. 13 ist ein CR. LF ist 10. write("\n\r") könnte da auch funktionieren. Das sind Kürzel dafür.
//delay(150); //verzögerung, da schreiben auf SD karte langsamer ist
//Datei.write(Test); //ohne zeilenumbruch
//Datei.write(13); //schreibe nach jedem eintrag ein carriage return line feed, damit strings zeilenweise einetragen werden
Datei.println(Text);
//inhalt von Array Text in SD schreiben(commands.txt)-> println mit zeilenumbruch
Datei.close(); //Damit änderung gespeichert werden, diese Datei schliessen
Kannst du da nicht einfach Datei.println() machen? Das sollte alles erledigen ohne dass man zusätzliche Steuerzeichen braucht. Ansonsten schreibt man da normalerweise glaube ich nur ein LF. Ein CR braucht man nicht. So ist zumindest auf dem PC, wenn ich mich da korrekt erinnere.
Du machst aber print(text) und danach nur ein CR. Versuche mal statt dessen ein LF mit 10 oder “\n”
EDIT: sehe gerade dass das eh auskommentiert ist. Aber ich weiß trotzdem nicht wieso das an der seriellen Übertragung liegen soll.
Du kannst übrigens bei ASCII direkt auf die Characters abfragen. Wenn du z.B. wissen willst ob ein “A” angekommen ist, musst du nicht auf den ASCII-Code abfragen sondern kannst “if(inByte == ‘A’)” schreiben. Machst das ganze leserlicher.
ok werde es versuchen, werde mich nochmal melden um zu berichten.
Sonst an sich is jetzt kein logik fehler in dem code, oder? Weil da hab ich auch gesucht und konnte nichts finden
So genau habe ich mir das jetzt nicht angeguckt (ist auch schwer sich da richtig rein zuarbeiten als Außenstehender), aber ich würde sagen wenn die Zeichen zuverlässig ankommen passt es. Du hast sie ja glaube ich mal testweise mit Serial zurückgeschickt um das auszuprobieren. Oder immerhin eine Bestätigung geschickt.
Das SD Zeug sollte auch funktionieren. Open -> Write -> Close. So kompliziert ist das in der Theorie nicht.
Vielleicht passt was mit dem Text array nicht richtig. Sollte aber durch memset korrekt terminiert sein...
Ich hab da neulich auch an so was ähnlichem gebastelt, damit ich was einheitliches und (halbwegs) sicheres habe, um zwischen meinem Steuer-PC und dem Arduino zu “sprechen”.
Logo sicher/vielleicht nicht unbedingt “State of the Art”, aber funktioniert gut und sauber.
Diese Unmenge an SerialPrint gehören da im Endzustand nicht hin - soll ja auch nur als Anleitung dienen…
#include <Messenger.h>
#define MAXCMD 20
#define MAXVAL 20
#define MAXMOT 5
// Instantiate Messenger object with the message function and the default separator (the space character)
Messenger message = Messenger(',');
// Define messenger function
void messageCompleted() {
boolean cmdOK, motOK, numOK, endOK = false;
int val[MAXVAL]; // max. MAXVAL (20) values per command
int cmd, mot, num, i = 0;
if ( message.checkString("##") )
{
Serial.println(" Start - Command recognized !");
Serial.print("Command-Number: ");
cmd = message.readInt();
cmdOK = ((cmd > 0) && (cmd <= MAXCMD));
Serial.println(cmd,DEC);
Serial.print("Motor-Number: ");
mot = message.readInt();
motOK = ((mot > 0) && (mot <= MAXMOT));
Serial.println(mot,DEC);
Serial.print("Number of values: ");
num = message.readInt();
numOK = (num < MAXVAL);
Serial.println(num,DEC);
while ( message.available() )
{
Serial.print("Value #");
Serial.print(i,DEC);
Serial.print(": ");
if (i < (MAXVAL)) // remind val only if it fits to array 0 to 19 !
{
val[i] = message.readInt();
}
Serial.println(val[i],DEC);
if ( message.checkString("**") )
{
Serial.println(" Stop - Command recognized !");
endOK = true;
}
i++; // increment the var-counter
}
if (numOK) {numOK = ((i + 1) >= num);} // only if it was OK before !
if (cmdOK && motOK && numOK && endOK) // evaluate only if everything is OK !
{
switch (cmd)
{ case 1: // Get available Mem
Serial.println("... do something with cmd = 1");
break;
case 2:
Serial.println("... do something with cmd = 2");
break;
case 10:
Serial.println("... do something with cmd = 10");
break;
case 20:
Serial.println("... do something with cmd = 20");
break;
default:
{
Serial.print("A not yet defined command is called: ");
Serial.println(cmd);
}
}
}
else
{
Serial.print("incorrect/incomplete command - analysis/function cancelled");
}
}
else
Serial.println("Command must begin with ##");
}
void setup()
{
Serial.begin(115200);
message.attach(messageCompleted);
}
void loop()
{
// The following line is the most effective way of
// feeding the serial data to Messenger
while ( Serial.available( ) ) message.process(Serial.read( ) );
// ... do something else in the loop ...
}
Funktioneren tut es dann so:
Der Messenger “pult” eine eingegangene Zeile seitens Serial in einzelne Sequenzen - jeweils mit Komma (oder einem anderen Zeichen) getrennt auseinander.
Jedes Kommando seitens des PC muss mit “##,” beginnen - der Sicherheit halber. Könnte man auch weglassen …
Danach folgt eine Latte von Zahlen (ebenfalls duch “,” getrennt), die wie folgt interpretiert werden:
Ein Kommando ala “Zahl,” - darin unterscheide ich dann, welche Funktion aufgerufen wird.
Die Angabe, welcher Motor gesteuert werden soll. (For my use only :.) Das kann man weglassen oder für andere Dinge missbrauchen.
Die Anzahl folgender Datenwerte. Je nach dem wie MAXVAL definiert ist, werden auch nur max. so viele in ein Array gelesen.
Nun im Prinzip eine Anzahl von “Zahl,” entsprechend num. Darf aber auch mehr sein - wird ignoriert (Es fehlt noch der Check, ob auch genügend Zeichen lt. num gekommen sind …) Ergänzt.
Zum Schluss muss (!) ein “**” (2 Sternchen) folgen, damit sichergestellt ist, dass der gesamte Befehl richtig rübergekommen ist.
In den jeweiligen Case’s von Switch kann man nun nach Lust und Laune auf die jeweiligen Kommandos reagieren, Werte übergeben oder auch welche lesen, oder …
Das kann man nun zusammenkürzen oder aufblasen, wie es dnn beliebt.
SO hab es nochmal probiert, es ist etwas besser geworden, allerdings habe ich manchmal das problem, dass manchmal das Steuerzeichen, was ich mitsende mitgeschrieben wird?
Iwas läuft da nicht sauber ab
Habt ihr noch eine Idee?
Wenn du ein Array ala int val[MAXVAL] mit MAXVAL = 20 definierst, so "darfst" du da problemlos Werte von val[0] = ... bis val[19] schreiben (das Array beginnt immer bei 0 !).
Schreibst du einen Wert nach val[20], meckert der Compiler zwar nicht (jedenfalls der Arduino), aber du beschreibst damit einen Bereich, der dir "nicht gehört". Das könnte fatale Folgen für die weitere Programm ausführung haben ....
Wenn du gar keinen val beschreibst stört das nicht - auch nicht wenn die Anzahl der zu speichernden Werte eben unter Maxval liegt.
Schau dir das mal genau an mit dem Messenger.
In der Loop wird jedesmal auf Serial.available geprüft und wenn vorhanden in den "Speicher" des Messengers geschrieben.
Wenn ein (CR)-Kommando via Serial kommt wertet der Messenger alle gesammelten Bytes als "fertiges" Kommando aus.
Dabei wird dann intern die Zeichenkette anhand ihrer Trennzeichen (ich habe ein Komma genommen - ist definierbar) und bildet daraus einzelne "Sequenzen", die man dann mit den div. readxxxx Befehlen auslesen kann.
Man sollte allerdings schn wissen, was da kommt.
Spann's dir mal in Ruhe rein ...
Nachtrag - milito war da eben schneller:
Schau doch mal am Ende der while-Schleife in meinem kleinen Proggie: Da sende ich neben dem Anfang (2x #) auch am Ende zwei "Sonderzeichen" (2 x *), die ich mit readchar("**") auslese.
Falss diese Zeichen übrigens nicht als nächste "da" sind, wird auch kein Element aus der Messenger-Liste entfernt !
Weil: Diese Abfrage steht ja in der while-Schleife !
Guckst du !
Hallo,danke nochmals hab das etwas bessern können, doch Problem ist, dass er mir auch die Kontrollvariablen, die der Arduino von der GUI erhält mit in die Datei schreibt also so etwa: ??88;4;120
Und das ? wird von der GUI abgesendet, wenn ich den Button Scan(I2c-scanner) durhführe.
Nun die Frage, soll ich vllt, nachdem ich diese Kontrollvariable sende, den Port wieder schliessen?
Siehe hier den Codeabschnitt:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_scani2c.Click
If Not SerialPort_i2c.IsOpen = True Then
SerialPort_i2c.Open()
End If
SerialPort_i2c.Write(kontrol_scan)
Den Port schließen würde ich tunlichst unterlassen !
Denn jedesmal, wenn du den Port schliesst / öffnest, wurd der Arduino resettet und das Programm beginnt von vorne !
... ich hatte mich auch schon darüber gewundert.
Lies doch noch mal meine Kommandozeilen-Auswertung ein paar Postings zuvor.
Wenn du alle deine "Befehle" oder Werte mit einem Komma (oder auch ein anderes Zeichen trennst, zerlegt dir der messenger eine ganze Zeile (bis zum CR) in entsprechend viele einzelne Segmente, die du dann jeweils mit messenger.next mit dem Inhalt abrufen kannst.
Interssieren dich dann z.B. die ?? oder irgendwelche CRC-Werte nicht, dann ignorier sie einfach - aus der Segmentliste muss du die alledings soch auslesen.
Hi habs es versucht, komme habe aber immer noch das Problem wie kann ich es lösen , bin echt am verzweifeln
:(.
Warum tunlichst vermeiden die ports zu schliessen oder /öffnen, ich kann sie doch nicht öffen lassen?
Das Problem ist, dass er diese Kontrollvariablen nicht immer mit auflistes sondern ab und an .
Kann mir das auch nicht mehr erklären .
Nach Tagen der Proggerei eben zu diesem Thema mit meiner App/GUI auf dem PC sind ein paar Erleuchtungen aufgetaucht.
In diesem Posting/Fred hatte ich bereits dazu etwas geschrieben:
D.h., der Arduino “resettet immer” beim Aufbau einer seriellen Kommunikation ist nur teilweise / bedingt richtig - nämlich nur in dem Fall, wenn das steuernde Programm (egal welche Plattform / welches Gerät) an der DTR-Leitung (DTR = Data Terminal Ready, siehe http://de.wikipedia.org/wiki/RS-232#Verkabelung_und_Stecker - etwas nach unten scrollen) “zieht” und damit eben besagten Reset auslöst.
Beim Neubeschreiben ist das natürlich zwingend erforderlich, aber warum muss der Monitor das auch unbedingt machen ?
Wer das also nicht möchte, muss sich schon seine eigene “Äbb” schreiben, worin sie / er volle Kontrolle über die serielle Schnittstelle und deren Kommunikation hat.
Wie ich in meinem Besipiel etwas weiter vorne schon beschrieben haben, wird ja die “normale” Kommunikation" in Richtung Arduino in der Art gelöst, das in der LOOP ein Befehl in der Art von while ( Serial.available( ) ) message.process(Serial.read( ) );
steht. Hier in Verbindung mit dem messenger, der ganze Zeilen bis zum “CR” ausliest und man damit recht praktisch einen Befehl erkennen kann.
Obiges wird nun einmal in der Loop ausgewertet und ggf. in weiteren Funktionen (sei es im Sketch selbst oder in Funktionen aus LIBs) ausgewertet. Man beachte & Bedenke: Der Arduino ist nun mal kein Multitasking System, sondern arbeitet seine Sachen alle der Reihe nach ab !
Sind diese “Arbeiten” nun etwas zeit- und / oder rechenintensiv, dauert es entsprechend, bis die LOOP wieder gesendete Zeichen abarbeiten kann. Und: Der serielle Speicher ist nur 64 Byte groß !
D.h., bei mehreren längeren Befehlen (warum auch immer) oder auch vielen kleinen Kommandos nacheinander könnte ( ! ) der Puffer (schnell) überlaufen und Zeichen/Befehler werden verschluckt. Sehr unschön … und vor allem bei fehlendem Logger o.ä. kaum nachzuvollziehen - weil: … passiert nur manchmal !?
Ich habe mir dazu “senderseitig” eine App gebastelt (auf dem PC - in Delphi 7), welche ein Kommando sendet und welches der Arduino nach Ausführung zunächst bestätigen muss - je nach dem u.a. auch, ob der Befehl nun erfolgreich war oder eben auch nicht → mit entsprechender Reaktion darauf.
Funktioniert so weit prima, aber wie immer das große ABER:
Möglicherweise hat ja auch der Arduino was zu sagen und meldet sich mal so spontan zwischendurch, wenn mal grade auf ein Befehls-Echo oder auch nicht gewartet wird …
In meinem Fall mit meiner MegaStepper-Lib z.B.:
Not-Aus-Schalter wurde gedrückt
Endschalter haben ausgelöst (-> Not-Stopp)
Temperatur(en) oder Ströme des / der Motoren zu hoch (Warnung !)
“Das (Fahr-) Ziel wurde erreicht !” (mit ner akustischen Meldung von “Uschi” … :D)
… oder x-beliebig denkbares, was für die steuernde Appikation an Werten / Stati von Interesse ist.
Wie ihr euch vorstellen könnt, laufen solche Informationen (samt Auswertung der “Echo’s” gemäß Murphy’s Gesetzen immer zur unpassenden Zeit ein - insbesondere wenn gerade ein neuer Befehl unterwegs ist…
Ich bin da schon recht weit, aber ‘bis fertig’ zum weiterreichen dauerts noch ne kleine Weile.