Guten Tag,
der PC ist mit einem Arduino Mega 2560 verbunden.
Der Arduino sendet alle 2 Sekunden eine 6 Byte lange Nachrricht über die
Seriel Schnittstelle an den PC.
Die 6 Bytes sollen in 6 Textboxen anzeigen werden. -> Byte 1 = Textbox 3
usw.
Ich habe versucht dies mit der Methode DataReceivedHandler zu
realisieren.
Jedoch kommt nach dem starten des Programms die Fehlermeldung (siehe
Bild):
"Ein Ausnahmefehler des Typs "System.InvalidOperationException" ist in
System.Windows.Forms.dll aufgetreten.
Zusätzliche Informationen: Ungültiger threadübergreifender Vorgang: Der
Zugriff auf das Steuerelement textBox3 erfolgte von
einem anderen Thread als dem Thread, für den es erstellt wurde."
Was kann man unternehmen das in der Funktion DataReceivedHandler die
Daten über die Textboxen ausgegeben werden kann.
Hier noch mein Code:
Jannick K. schrieb:> Ungültiger threadübergreifender Vorgang
suche doch einfach mal Google mach danach. Bei C# darf man die GUI nur
aus dem Hauptthread ansprechen. Die Events der Seriellen Schnittstelle
laufen aber in einem Extra Thread.
Du musst mit "Delegate" und "Invoke" arbeiten.
Die Ausgabe Funktioniert jetzt auch. Jedoch sind die ankommenden Daten
nicht nicht korrekt.
Wie ich bereits erwähnt habe besteht meine Nachricht aus 6 Byte:
1 Byte = konstanter Wert (255)
2 Byte = konstanter Wert (170)
3 Byte = variabler Wert (0-254)
4 Byte = konstanter Wert (238)
5 Byte = variabler Wert (0-254)
6 Byte = konstanter Wert (10)
Prinzipiell müssten sich ja lediglich die Werte 3 und 5 in den Textboxen
ändern. Bei meiner Anzeige jedoch wird beispielsweise das erste Byte mit
dem Wert 255 nicht angezeigt.
In den Textboxen 1-4 werden für mich nicht nachvollziehbare Werte
angezeigt.
Ich geh doch recht in der Annahme, dass wenn der Arduino die Nachricht
verschickt hat der DataReceivedHandler aktiv wird die 6 Byte ausliest
und dann die Textboxen aktualisiert?
Woher könnte es rühren, dass die Bytes nicht in der gewünschten
Reihenfolge angezeigt werden?
Jannick K. schrieb:> Woher könnte es rühren, dass die Bytes nicht in der gewünschten> Reihenfolge angezeigt werden?
weil es überhaupt nicht definiert ist, was der 1.Byte ist. Wenn du das
erste Byte verpasst, wird das 2.Byte zu ersten Byte.
Was wird dann wirklich übertragen?
binary Daten oder Text? gibt es eine Protokoll?
> serialPort1.Read(Addata, 0, 6);
ist außerdem falsch, wenn keine 6 Bytes ankommen, musst du auch den
return-wert auswerten, dort steht drin wie viele Daten Empfangen wurden.
Es könnte also sein, das für jedes Byte das Event kommmt.
Peter II schrieb:> Jannick K. schrieb:>> Woher könnte es rühren, dass die Bytes nicht in der gewünschten>> Reihenfolge angezeigt werden?>> weil es überhaupt nicht definiert ist, was der 1.Byte ist. Wenn du das> erste Byte verpasst, wird das 2.Byte zu ersten Byte.>> Was wird dann wirklich übertragen?>> binary Daten oder Text? gibt es eine Protokoll?
Das Protokoll ist oben Beschrieben. Eine Nachricht besteht immer aus 6
Bytes. Es werden binäre Daten übertragen. Das 1. Byte hat immer den Wert
255 usw...
Wie kann ich den das erste Byste verpassen?? Der Pc bekommt alle 2
Sekunden eine Nachricht vom Arduino. Dadurch müsste doch der Event
Handler getriggert werden und somit diese 6 Bytes auslesen.
>>> serialPort1.Read(Addata, 0, 6);> ist außerdem falsch, wenn keine 6 Bytes ankommen, musst du auch den> return-wert auswerten, dort steht drin wie viele Daten Empfangen wurden.>> Es könnte also sein, das für jedes Byte das Event kommmt.
Habe mir in hterm die ankommenden Daten angeschaut und es sind immer 6
Byte.
Wie ist es möglich den return-wert von der Read() Methode auszuwerten?
Jannick K. schrieb:> Das Protokoll ist oben Beschrieben. Eine Nachricht besteht immer aus 6> Bytes. Es werden binäre Daten übertragen. Das 1. Byte hat immer den Wert> 255 usw...
oh hatte ich übersehen.
Dann ist es am einfachsten, die Bytes einzeln zu lesen.
Nur schnell getippt, es werden noch fehler drin sein
Jannick K. schrieb:> Wie kann ich den das erste Byste verpassen?? Der Pc bekommt alle 2> Sekunden eine Nachricht vom Arduino. Dadurch müsste doch der Event> Handler getriggert werden und somit diese 6 Bytes auslesen.
in dem z.b. das Kabel nicht am PC steckt und erst beim 2 Byte angesteckt
wird.
> Habe mir in hterm die ankommenden Daten angeschaut und es sind immer 6> Byte.
das kann man nicht sehen
Hi,
dein Andurino und PC sind halt nicht synchron. Ich würde dein Protokoll
noch etwas optimieren, damit gehst Du auf Nummer sicher, eventuell ist
auch ein Alive-Signal von deinem Andurino hilfreich, welches z.B. jede
Sekunde gesendet werden muss, wenn Du das Alivesignal nicht innerhalb
deiner Timeoutzeit erhälst, dann ist die Verbindung unterbrochen oder
dein Andurino hängt.
Protokollvorschlag:
StartByte, CRC, Datalength, Data's, EndByte
Dein StartByte etc. sollte natürlich nicht in der Rangevon den Nutzdaten
liegen, aber Du kannst dich an der ASCII Tabelle orientieren.
http://www.torsten-horn.de/techdocs/ascii.htm
StartByte z.B. 0x02 und EndByte 0x03, wenn Du verschlüsselte Daten
benötigst gibt es natütlich andere Möglichkeiten.
Das Beispiel von PeterII ist nicht schlecht, aber ich würde die Daten
einfach in ein Buffer schreiben und die Auswertung außerhalb des Events
tätigen.
Erstmal vielen Danke für Eure Hilfe :).
Ich hab das Programm so angepasst, dass es jetzt immer die 6 Bytes aus
dem Eingangspuffer ausliest und mir in den Textboxen darstellt. Hierfür
wird das Globale Daten Array für 6 Bytes genutzt.
for(i=0;i<6;++i)//ReadBufferSize = Ruft die Größe des SerialPort - Eingabepuffers ab oder legt diese fest.
11
{
12
intb=serialPort1.ReadByte();//Liest synchron ein Byte aus dem Serialport Eingabepuffer
13
DataBuffer[i]=Convert.ToByte(b);
14
}
15
this.BeginInvoke(newEventHandler(DisplayText));
16
17
}
18
privatevoidDisplayText(objectsender,EventArgse)
19
{
20
textBox3.Text=Convert.ToString(DataBuffer[0]);
21
textBox4.Text=Convert.ToString(DataBuffer[1]);
22
textBox5.Text=Convert.ToString(DataBuffer[2]);
23
textBox6.Text=Convert.ToString(DataBuffer[3]);
24
textBox7.Text=Convert.ToString(DataBuffer[4]);
25
textBox8.Text=Convert.ToString(DataBuffer[5]);
26
}
Der EventHandler wird nur genutzt um die 6 Bytes in das Array zu
schreiben. Die Nachfolgende Weiterverarbeitung soll außerhalb des Events
statt finden.
Jannick K. schrieb:> Ich hab das Programm so angepasst, dass es jetzt immer die 6 Bytes aus> dem Eingangspuffer ausliest und mir in den Textboxen darstellt. Hierfür> wird das Globale Daten Array für 6 Bytes genutzt.
du hast leider viel von dem wichtigen code entfernt und neue fehler
eingebaut (auch wenn es eventuell funktioniert)
Man liest im Event nur so viele Daten wie vorhanden sind. Aus dem grunde
wurde vorher BytesToRead abgefragt.
Man Prüft ob das Startbyte auch das Startbyte ist.
Dein Programm kommt durch einander, wenn die Kommunikation mal
unterbrochen ist. Warum hast du es nicht 1:1 übernommen und nur
eventuell Fehler entfernt?
Als ich Ihren Code verwendet habe, hat sich der erste Variablen wert
nach ca 4-6 Sekunden verändert und der zweite variable Wert nach ca.
10-20 Sekunden.
Das Verstehe ich nicht, da der Arduino alle 2 Sekunden die beiden Werte
ändert. Haben Sie evtl eine Vermutung woher die Verzögerung kommt?
Prinzipiell müssten sich doch jedesmal nach 2 Sekunden 6 Bytes im
Eingangspuffer befinden die ausgelesen werden könnten oder liege ich
hier falsch?
Jannick K. schrieb:> Als ich Ihren Code verwendet habe, hat sich der erste Variablen wert> nach ca 4-6 Sekunden verändert und der zweite variable Wert nach ca.> 10-20 Sekunden.
keine Ahnung, dafür gibt es den Debugger. Eventuell ist noch ein Fehler
drin.
du kannst doch einfach mal eine Debugausgabe machen, wenn die 255
empfangen wird.
Es kommt alle 2 Sekunden "255 empfangen".
Ich hab mir auch mal die Werte von dem Ausschnitt:
1
if(DataPos>=0)
2
{
3
DataBuffer[DataPos]=Convert.ToByte(b);
4
System.Diagnostics.Debug.WriteLine(b);
5
DataPos++;
6
}
anzeigen gelassen. Hier ist ein Ausschnitt hiervon:
1
255empfangen
2
0
3
2
4
238
5
250
6
10
7
255empfangen
8
170
9
8
10
238
11
255empfangen
12
170
13
255empfangen
14
170
15
20
16
238
17
232
18
10
19
255empfangen
Ist es möglich das die Daten vom Arduino zu schnell ankommen und mein
Programm nicht die nötige Zeit hat diese zu verarbeiten? Oben sieht man
das selten alle 5 Bytes nach dem Start Byte auftauchen.
Jannick K. schrieb:> Ist es möglich das die Daten vom Arduino zu schnell ankommen und mein> Programm nicht die nötige Zeit hat diese zu verarbeiten?
wenn du nicht gerade vor einem alten 486 sitzt - nein
> Oben sieht man das selten alle 5 Bytes nach dem Start Byte auftauchen.
Bist du sicher das dein Arduino richtig sendet?
Prüfe erst mal mit einen andere Programm
http://realterm.sourceforge.net/
Ja der Arduino sendet Richtig.
Im Anhang finden Sie mal ein Ausschnitt.
Ich Verwende ein Globales Array in das die Werte rein geschrieben
werden.
Die Textboxen werden anhand eines Timers aktualisiert. Der Timer ist auf
ein Intervall von 1 Sekunde eingestellt.
Könnte hier etwas schief laufen?
Jannick K. schrieb:> Könnte hier etwas schief laufen?
eigentlich nicht, wenn schon die debug ausgaben fehlerhaft sind stimmt
etwas anderes nicht.
Kannst du mal den aktuellen code zeige, der die Ausgaben mit den lücken
ezeugt hat?
Peter II schrieb:> Jannick K. schrieb:>> Klar. Das hat die Ausgabe ausgegeben:>> sieht richtig aus, wenn auch noch etwas umständlich.>> wenn du>>
1
>byteb=serialPort1.ReadByte();
2
>
>> verwendest, kannst du dir das ganze Convert sparen.>
Dachte ich mir auch schon. Jedoch will er das nicht... Es kommt ein
Fehler mit "Der Typ int kann nicht implizit in byte konvertiert werden"
> Teste mal nur mit debug
Wenn ich das Teste sieht es richtig gut aus. Hier mal ein Auszug:
Darum hab ich mir überlegt im Event Handler nur die Bytes in ein Array
zu schreiben und die Auswertung sprich Startbyte prüfen usw in einer
anderen Funktion.
ich sehe den fehler.
int DataPos = -1;
darf nicht in der Funktion angelegt werden! mache es mal global. (ich
hatte es extra static gemacht - aber das geht so in C# nicht)
1
if(DataPos==6)
2
{
3
DataBuffer[DataPos]=Convert.ToByte(b);
4
DataPos=-1;
5
}
die Zuweisung DataBuffer[DataPos] = Convert.ToByte(b);
ist hier zu viel. Das wurde schon gemacht. An dieser stelle must du die
GUI ansprechen, weil ein Datensatz vollständig ist.
Es läuft!! Top. Vielen Dank für deine Zeit und mühe mir dabei zu helfen.
Würdest du mir evtl noch erklären warum es einen "so großen" unterschied
macht ob ich DataPos Global oder in der Funktion anlege?
Jannick K. schrieb:> Würdest du mir evtl noch erklären warum es einen "so großen" unterschied> macht ob ich DataPos Global oder in der Funktion anlege?
wie schon oben geschrieben, kann man vorher nicht wissen wie viele Daten
auf einmal in dem Event geliefert werden. Es kann passieren, das 6
Events für 6 Byte kommen, oder es kommt 1 Event mit 6 Bytes.
Aus dem Grund darf der Zähler nicht wieder bei -1 beginnen, wenn die
Funktion verlassen wird.
Jetzt kannst du auch deinen Timer loswerden, in dem du per invoke die
GUI aufrufst.
Was ist... wenn einer deiner Daten mal den Wert 255 hat? Wie fängst du
das ab?
Ich löse das übrigens immer so - das ganze lasse ich über einen extra
Thread laufen und gut. Vorausgesetzt natürlich ein linetermination:
Draco schrieb:> Was ist... wenn einer deiner Daten mal den Wert 255 hat? Wie fängst du> das ab?
muss er gar nicht, es laut Protokoll kommt das nicht vor.
> Ich löse das übrigens immer so - das ganze lasse ich über einen extra> Thread laufen und gut. Vorausgesetzt natürlich ein linetermination:
bei dir sind aber auch böse Fallstricke drin
1
stringindata=sp.ReadExisting();
Das arbeiten mit String ist hier falsch, dabei wird eine
Zeichensatzkodierung vorgenommen. damit versaut man sie die Daten.
Das mit den Extra Thread kann durchaus hilfreich sein, aber intern macht
der SerialPort auch nichts anderes. Hier bringt es zumindest keine
Vorteile.
Peter II schrieb:> Draco schrieb:>> Was ist... wenn einer deiner Daten mal den Wert 255 hat? Wie fängst du>> das ab?> muss er gar nicht, es laut Protokoll kommt das nicht vor.
Achso... ja die Liste oben habe ich jetzt erst gesehen. Dann passt das
ja.
Peter II schrieb:> Das arbeiten mit String ist hier falsch, dabei wird eine> Zeichensatzkodierung vorgenommen. damit versaut man sie die Daten.
Wieso sollte da eine Kodierung stattfinden? Ein String ist im Grunde
auch "bloß" ein Array aus Zeichen. Wenn in einem Byte in einem String
0x4B steht dann steht da auch eine 0x4B oder ein DEC 113, ein OCT 61
oder eben ein ASCII "K" - komme was wolle. "Konvertiert" in ein ASCII
wird es ja erst bei Rückgabe/Ausgabe.
Peter II schrieb:> Das mit den Extra Thread kann durchaus hilfreich sein, aber intern macht> der SerialPort auch nichts anderes. Hier bringt es zumindest keine> Vorteile.
Echt? Danke, das wusste ich nicht - habe ich auch noch nie beobachtet um
genau zu sein.
Draco schrieb:> Wieso sollte da eine Kodierung stattfinden? Ein String ist im Grunde> auch "bloß" ein Array aus Zeichen. Wenn in einem Byte in einem String> 0x4B steht dann steht da auch eine 0x4B oder ein DEC 113, ein OCT 61> oder eben ein ASCII "K" - komme was wolle. "Konvertiert" in ein ASCII> wird es ja erst bei Rückgabe/Ausgabe.
nicht bei C#!
Da muss die Encoding Eigenschaft vom SerialPort richtig setzen. In C#
ist jeder String eine UTF8 String. Alles was nicht ASCII ist, wird
umgewandelt in mehr als ein Byte.
Damit kommt bei Sonderzeichen bei dir Unsinn raus. Es gibt nicht ohne
Grund auch die Read-Methoden die ein Byte Array auslesen. String ist
wirklich nur die Text gedacht.
Jannick K. schrieb:> Hier ist ein Ausschnitt hiervon:> 255 empfangen> 0> 2> 238> 250> 10
--> da war die 1. Nachricht
> 255 empfangen> 170> 8> 238> 255 empfangen> 170
--> Da war die 2. Nachricht
> 255 empfangen> 170> 20> 238> 232> 10
--> Da war die 3. Nachricht.
> 255 empfangen>> Ist es möglich das die Daten vom Arduino zu schnell ankommen und mein> Programm nicht die nötige Zeit hat diese zu verarbeiten? Oben sieht man> das selten alle 5 Bytes nach dem Start Byte auftauchen.
Wie? Ich sehe 3 wunderbare 6-byte große Nachrichten. Nur dumm, dass der
255-Marker auch als Data in der NAchricht auftauchen kann, eh?
Eric B. schrieb:> Wie? Ich sehe 3 wunderbare 6-byte große Nachrichten. Nur dumm, dass der> 255-Marker auch als Data in der NAchricht auftauchen kann, eh?
Kann nicht passieren:
Jannick K. schrieb:> Wie ich bereits erwähnt habe besteht meine Nachricht aus 6 Byte:> 1 Byte = konstanter Wert (255)> 2 Byte = konstanter Wert (170)> 3 Byte = variabler Wert (0-254)> 4 Byte = konstanter Wert (238)> 5 Byte = variabler Wert (0-254)> 6 Byte = konstanter Wert (10)
Eric B. schrieb:> Nur dumm, dass der> 255-Marker auch als Data in der NAchricht auftauchen kann, eh?
bitte alles lesen! Er kann nicht auftauchen, es war noch ein Fehler im
code der schon lange behoben wurde.
Peter II schrieb:> Eric B. schrieb:>> Nur dumm, dass der>> 255-Marker auch als Data in der NAchricht auftauchen kann, eh?>> bitte alles lesen! Er kann nicht auftauchen, es war noch ein Fehler im> code der schon lange behoben wurde.
Ich habe alles gelesen. Wer sagt mir, dass da nicht noch mehr Fehler
drinne sind?
Entweder verliert er tatsächlich Bytes, oder die Annahme über das Format
stimmt nicht. Im minimalen Log-Abschnitt kamen die 255'er aber ganz
schön im 6-Byte Takt, nur die andere Bytes hielten sich nicht an der
Spec:
> 1 Byte = konstanter Wert (255)> 2 Byte = konstanter Wert (170)> 3 Byte = variabler Wert (0-254)> 4 Byte = konstanter Wert (238)> 5 Byte = variabler Wert (0-254)> 6 Byte = konstanter Wert (10)
Es ist ein fester Rhythmus aus Bytes die alle 2 Sek vom Arduino
geschickt werden. Das letzte Byte was in meinen Ausschnitten immer den
festen Wert von (Dec = 10) hatte hab ich noch in eine CheckSumme
umgewandelt. Sprich der Aufbau des Protokolls sieht wie folgt aus:
1 Byte = konstanter Wert (255)
2 Byte = konstanter Wert (170)
3 Byte = variabler Wert (0-254)
4 Byte = konstanter Wert (238)
5 Byte = variabler Wert (0-254)
6 Byte = CheckSumme Wert ((2 Byte + 3 Byte + 4 Byte + 5 Byte)/4).
Sprich der Arduino berechnet die Checksumme und schickt sie mit und auf
dem Pc rechne ich die Checksumme der ankommenden Daten aus. Beide werden
dann vergleichen. Somit habe ich noch eine Absicherung eingebaut.
Das Programm läuft auch stabil habe es heute über 40 Min laufen lassen
ohne das Fehler aufgetreten sind.
Jannick K. schrieb:> Das Programm läuft auch stabil habe es heute über 40 Min laufen lassen> ohne das Fehler aufgetreten sind.
Schön. Du musst aber auf jeden Fall dafür sorgen, dass das System nach
einem Fehler wieder in Tritt kommt, die Voraussetzung ist ja gegeben, da
das Protokoll mit 255 anfängt. Wenn du also die Receivefunktion
aufrufst, muss die alles verwerfen bis ein 255 kommt, und dann 6 Bytes
einlesen.
Georg
Georg schrieb:> Schön. Du musst aber auf jeden Fall dafür sorgen, dass das System nach> einem Fehler wieder in Tritt kommt, die Voraussetzung ist ja gegeben, da> das Protokoll mit 255 anfängt. Wenn du also die Receivefunktion> aufrufst, muss die alles verwerfen bis ein 255 kommt, und dann 6 Bytes> einlesen.
genau das haben wir doch gemacht? Hast du überhaupt die Kommentare
gelesen?
>1 Byte = konstanter Wert (255)>2 Byte = konstanter Wert (170)>3 Byte = variabler Wert (0-254)>4 Byte = konstanter Wert (238)>5 Byte = variabler Wert (0-254)>6 Byte = CheckSumme Wert ((2 Byte + 3 Byte + 4 Byte + 5 Byte)/4).
Sieht echt gut aus, falls Du auf der PC Seite noch kein Fehlerhändling
drin hast, dann empfehle ich Dir noch einen Timeout mit einzufügen, z.B.
nach >4 Sekunden keine Empfangssequenz eine interne Fehlermeldung.