Forum: Mikrocontroller und Digitale Elektronik Arduino C# Serielle Kommunikation


von Chris V. (germandev)


Lesenswert?

Hallo Community,

Ich habe folgendes Problem:
Ich bin dabei ein GUI in Visual Studio 2017 mit C# zu programmieren, mit 
dem ich die Ausgänge meines Arduino's durch Buttons steuern kann. Dazu 
sende ich einfach Daten über USB zum Arduino, welcher dann die Ausgänge 
dementsprechend aktiviert. Soweit kein Problem, allerdings möchte ich 
beim erneuten öffnen der Anwendung (z.B. nach Absturz) den aktuellen 
Status der Ausgangs-Pins abragen und visuell durch verschiedene 
Hintergrundfarben der Buttons anzeigen lassen. Das Arduino Sketch ist 
bereits fertig, doch das C# Script bereitet mir Probleme, da ich ja die 
ankommenden Daten vom Arduino vergleichen muss und wenn diese korrekt 
sind soll sich ja die Hintergrundfarbe der Buttons ändern.
Wäre sehr dankbar, wenn ihr Vorschläge bzw. Code-Beispiele für mich 
hättet ;)

Hier das Arduino Sketch:
1
int state1 = 0;
2
3
void setup() {
4
5
    Serial.begin(9600);
6
    pinMode(3, OUTPUT);
7
}
8
9
void loop() {
10
11
    char data = Serial.read();
12
13
    switch (data) {
14
15
        case 'A':
16
            digitalWrite(3, HIGH);
17
            state1 = 1;
18
            break;
19
20
        case 'B':
21
            digitalWrite(3, LOW);
22
            state1 = 0;
23
            break;
24
25
        case 'S':
26
            Serial.println(state1);
27
            break;
28
}

: Bearbeitet durch User
von Sven K. (quotschmacher)


Lesenswert?

Chris V. schrieb:
> allerdings möchte ich
> beim erneuten öffnen der Anwendung (z.B. nach Absturz) den aktuellen
> Status der Ausgangs-Pins

dann sendest du dem arduino von deinem programm (nicht script) aus beim 
start ein 'S' und wertest das ergebnis aus. was genau ist denn das 
konkrete problem?

von Dr. Sommer (Gast)


Lesenswert?

Sende einfach vom Arduino jede Sekunde den aktuellen Stand. Damit sparst 
du dir das explizite Anfragen vom PC. Funktioniert dann auch wenn man 
den Arduino neu startet.

von Joachim B. (jar)


Lesenswert?

Chris V. schrieb:
> Soweit kein Problem, allerdings möchte ich
> beim erneuten öffnen der Anwendung (z.B. nach Absturz) den aktuellen
> Status der Ausgangs-Pins abragen und visuell durch verschiedene
> Hintergrundfarben der Buttons anzeigen lassen. Das Arduino Sketch ist
> bereits fertig, doch das C# Script bereitet mir Probleme, da ich ja die
> ankommenden Daten vom Arduino vergleichen muss und wenn diese korrekt
> sind soll sich ja die Hintergrundfarbe der Buttons ändern

suche dir ein I2C EEPROM 8-pin DIL gesockelt, ich nutze das der im Modul 
Echtzeit Uhr DS3231, ist leichter und billiger auszutauschen als den 
Controller.

Schreibe jede Änderung rein in ein Array und bei Neustart wird es wieder 
eingelesen!

Serielle Kommunikation

SerialEvent
https://www.arduino.cc/en/Tutorial/SerialEvent
1
void auswertung(inputString)
2
{
3
  if(!strcmp(inputString, "help")
4
     Serial.println("do something");
5
}
6
7
void loop() {
8
  // print the string when a newline arrives:
9
  if (stringComplete) {
10
    Serial.println(inputString);
11
// hier fehlt die Auswertung, und Aegtschen
12
13
   auswertung(inputString);
14
15
16
    // clear the string:
17
    inputString = "";
18
    stringComplete = false;
19
  }
20
}

: Bearbeitet durch User
von Sven K. (quotschmacher)


Lesenswert?

warum brauche ich ein eeprom, wenn ich eine c# anwendung auf einem 
rechner neustarte?

von Joachim B. (jar)


Lesenswert?

Chris V. schrieb:
> beim erneuten öffnen der Anwendung (z.B. nach Absturz)

Sven A. schrieb:
> warum brauche ich ein eeprom, wenn ich eine c# anwendung auf einem
> rechner neustarte?

also meine Arduino starten nache jedem Ansprechen der seriellen 
Schnittstelle neu, ist dem Bootloader geschuldet und dann weiss mein 
Arduino nicht mehr wie der Status der externen Komponenten ist, Rolladen 
oben unten? Uhrzeit, Sommer Winter?, deswegen habe ich den Status im 
EEPROM um auch zur Umstellung zu wissen was gilt, sonst ist man in der 
Zeitschleife gefangen.
http://www.der-postillon.com/2013/10/in-zeitschleife-gefangener-mann-stellt.html

Wenn der vom TO das abfragen kann braucht er es ja nicht, war nur so 
eine Idee.

von Sven K. (quotschmacher)


Lesenswert?

https://msdn.microsoft.com/de-de/library/system.io.ports.serialport.dtrenable(v=vs.110).aspx

dtr steuert den reset, ist bei serialport unter c# standardmäßig 
disabled. von daher sollte kein neustart des arduinos kommen

von Chris V. (germandev)


Lesenswert?

Dr. Sommer schrieb:
> Sende einfach vom Arduino jede Sekunde den aktuellen Stand. Damit sparst
> du dir das explizite Anfragen vom PC. Funktioniert dann auch wenn man
> den Arduino neu startet.

Das Anfragen vom PC ist ja nicht das Problem. Mein Problem ist dass ich 
nicht weiß wie ich die Daten vom Arduino auswerten kann um damit dann 
irgendetwas zu machen im C# Programm (z.B. die Farbe der Buttons 
ändern). Das Problem hätte ich ja auch wenn ich jede Sekunde den Stand 
übermitteln würde.

von Joachim B. (jar)


Lesenswert?

Chris V. schrieb:
> Mein Problem ist dass ich nicht weiß wie ich die Daten vom Arduino
> auswerten kann um damit dann
> irgendetwas zu machen im C# Programm (z.B. die Farbe der Buttons
> ändern). Das Problem hätte ich ja auch wenn ich jede Sekunde den Stand
> übermitteln würde.

dann passt aber

Chris V. schrieb:
> Ich bin dabei ein GUI in Visual Studio 2017 mit C# zu programmieren

dein ich programmiere NICHT....

du öffnest ein File Handle comN, liest ein, parst den Text und 
reagierst.

Das ist doch auf jedem Compi gleich oder irre ich mich?



unter C in lcc32 war das so:

  hCom = CreateFile(comPRTstr(comm.com__-1),GENERIC_READ | 
GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);

du wirst doch Beispiele in Visual Studio 2017 finden?
https://stackoverflow.com/questions/45515093/how-to-write-read-data-to-a-file-in-visual-studio-2017

: Bearbeitet durch User
von J. Zimmermann (Gast)


Lesenswert?

> z.B. die Farbe der Buttons ändern

In C# nichts einfacher als das:

button1.BackColor = Color.Green;

mfg
Achim

von Chris V. (germandev)


Lesenswert?

Joachim B. schrieb:
> du öffnest ein File Handle comN, liest ein, parst den Text und
> reagierst.
>
> Das ist doch auf jedem Compi gleich oder irre ich mich?
>
>
>
> unter C in lcc32 war das so:
>
>   hCom = CreateFile(comPRTstr(comm.com__-1),GENERIC_READ |
> GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);

Naja ich möchte ja die Daten nicht in eine Datei schreiben oder 
sonstiges. Ich lese einfach Daten aus der seriellen Schnittstelle, um 
diese im Programm zu visualisieren.

von Chris V. (germandev)


Lesenswert?

Da viele anscheinend mein Problem nicht ganz verstanden haben, hier mal 
ein Beispiel zu meinem Versuchsaufbau in C#. Mein Problem ist jetzt 
diese if-Abfrage, die so nicht funktioniert. Dafür suche ich eine Lösung
1
namespace SerialTest
2
{
3
    public partial class Form1 : Form
4
    {
5
        private SerialPort serialport;
6
7
        public Form1()
8
        {
9
            InitializeComponent();
10
11
            serialport = new SerialPort();
12
            serialport.BaudRate = 9600;
13
            serialport.PortName = "COM3";
14
            serialport.Open();
15
        }
16
17
        private void button_Click(object sender, EventArgs e)
18
        {
19
            serialport.WriteLine("S");
20
21
            string input = serialport.ReadLine();
22
23
            if (input.Equals("0"))
24
            {
25
                button.BackColor = Color.Transparent;
26
            }
27
            else if (input.Equals("1"))
28
            {
29
                button.BackColor = Color.Green;
30
            }
31
        }
32
    }
33
}

von c-hater (Gast)


Lesenswert?

Chris V. schrieb:
> Da viele anscheinend mein Problem nicht ganz verstanden haben, hier mal
> ein Beispiel zu meinem Versuchsaufbau in C#. Mein Problem ist jetzt
> diese if-Abfrage, die so nicht funktioniert.

>
1
>             if (input.Equals("0"))
2
>             {
3
>                 button.BackColor = Color.Transparent;
4
>             }
5
>             else if (input.Equals("1"))
6
>             {
7
>                 button.BackColor = Color.Green;
8
>             }
9
>

Kein Wunder, dass sie nicht funktioniert. Du prüfst auf Sachen, die dein 
Arduino zu keiner Zeit sendet. Der Fehler liegt also nicht im 
C#-Programm, sondern im Arduino-Programm.

Tip: Eine ASCII-Tabelle könnte dir helfen...

von Fred R. (Firma: www.ramser-elektro.at/shop) (fred_ram)


Lesenswert?

Du weist, dass du den Pinstatus auch abfragen kannst?
Serial.print(digitalRead(OutputPin1));

von Tany (Gast)


Lesenswert?

c-hater schrieb:
> in Wunder, dass sie nicht funktioniert. Du prüfst auf Sachen, die dein
> Arduino zu keiner Zeit sendet

Das ist nicht wahr.

Chris V. schrieb:
> private void button_Click(object sender, EventArgs e)
>         {
>             serialport.WriteLine("S");
>
>             string input = serialport.ReadLine();


Chris V. schrieb:
> switch (data) {
>.....

>         case 'S':
>             Serial.println(state1);
>             break;

von Joachim B. (jar)


Lesenswert?

Chris V. schrieb:
> Naja ich möchte ja die Daten nicht in eine Datei schreiben oder
> sonstiges. Ich lese einfach Daten aus der seriellen Schnittstelle, um
> diese im Programm zu visualisieren.

Chris V. schrieb:
> Da viele anscheinend mein Problem nicht ganz verstanden haben

nein DU verstehst nicht, auch die serielle Schnittstelle ist ein File 
unter windows (DOS, Linux usw.), das musst du zum Lesen öffnen sonst 
kommst du nicht an den Inhalt

Serial.println(state1);

dann sende doch "gruen" "rot"

Chris V. schrieb:
> string input = serialport.ReadLine();
>
>             if (input.Equals("0"))

"0" != 0
"1" != 1

if (input.Equals("gruen"))
if (input.Equals("rot"))

: Bearbeitet durch User
von Tany (Gast)


Lesenswert?

Joachim B. schrieb:
> das musst du zum Lesen öffnen sonst
> kommst du nicht an den Inhalt

Hat der doch. Der Comport ist anfangs an geöffnet und  nirgendwo 
geschlossen worden.
Ich vermute, dass der Puffer geleert worden, nachdem C# ein Zeichenkette 
erhalten hat.

von Maxx (Gast)


Lesenswert?

Chris V. schrieb:
> if (input.Equals("0"))
>             {
>                 button.BackColor = Color.Transparent;
>             }
>             else if (input.Equals("1"))
>             {
>                 button.BackColor = Color.Green;
>             }

Bei C# GUI und diesem Programmierstil erschauere ich.
Ja, das ist der Weg, den glaub ich, jeder C-Starter in C# nimmt, aber er 
wird schnell unendlich unübersichtlich. Eine GUI ist kein uC.

Investiere die Zeit dich für 1-3 Stunden in die Grundlagen WPF/MVVM 
einzulesen. Du wirst es dir in alle Ewigkeiten danken.

von Michael D. (Firma: indEAS) (indeas)


Lesenswert?

Mein Vorschlag:
- Der PC ist der Master und der Arduino ist der Slave, der nur dann 
antwortet wenn er gefragt wird.
- Jedes Telegramm hat ein Startzeichen (z.B. *) und einen Endzeichen 
(z.B. <CR>)
- Der PC löscht öffnet den COM löscht den Empfangspuffer schickt ein 
Telegramm (*xxxxxx<cr>) und wartet z.B. 50ms.
- der Slave anwortet
- der PC übernimmt die Daten aus dem InBuffer und schließt die 
COM-Schnittstelle wieder.
- Dann kann der PC in Ruhe schauen was vom Slave gemeldet wurde.

Vorteile:
- Ist sicher und zuverlassig (timeout)
- erzeugt keine Abstürze oder Endlosschleifen
- Implemtierung am Slave kann man einfach mit LK Term oder anderen 
Terminalprgrammen testen.

(so mache ich es immer und es funktioniert sehr zuverlässig)

von Tany (Gast)


Lesenswert?

Noch eine eventuelle Fehlerquelle:

Chris V. schrieb:
> string input = serialport.ReadLine();

Also String Input ist ein Zeichenkette (string) und kein Char.
Da liefert ein Vergleich zwischen string und char immer "false" zurück.

von Joachim B. (jar)


Lesenswert?

Tany schrieb:
> Hat der doch. Der Comport ist anfangs an geöffnet und  nirgendwo
> geschlossen worden.

OK aber das passt trotzdem nicht

state1 = 0;
case 'S':
  Serial.println(state1);

zu dem hier

if (input.Equals("0"))


er schickt 0 und will lesen "0"
ausserdem schrieb er:

Chris V. schrieb:
> Naja ich möchte ja die Daten nicht in eine Datei schreiben

trotzdem muss er ein File Handle öffnen

von Sven K. (quotschmacher)


Lesenswert?

Joachim B. schrieb:
> nein DU verstehst nicht, auch die serielle Schnittstelle ist ein File
> unter windows (DOS, Linux usw.), das musst du zum Lesen öffnen sonst
> kommst du nicht an den Inhalt

dafür gibt es ja direkt die serialport-klasse. da muss man dann kein 
file öffnen.
https://msdn.microsoft.com/de-de/library/system.io.ports.serialport(v=vs.110).aspx

von Joachim B. (jar)


Lesenswert?

Sven A. schrieb:
> dafür gibt es ja direkt die serialport-klasse. da muss man dann kein
> file öffnen.

OK ich bin nicht der c# progger, aber was ist mit

Joachim B. schrieb:
> state1 = 0;
> case 'S':
>   Serial.println(state1);
>
> zu dem hier
>
> if (input.Equals("0"))

?

das sieht immer noch falsch aus

von Sven K. (quotschmacher)


Lesenswert?

Joachim B. schrieb:
> das sieht immer noch falsch aus

ist es auch definitiv.

Joachim B. schrieb:
> "0" != 0
> "1" != 1

genau so sieht es nämlich aus

von Tany (Gast)


Lesenswert?

Chris V. schrieb:
> case 'S':
>             Serial.println(state1);
>             break;


Versuche damit:
.....
Serial.println(state1+'0');
.....
und statt  input.Equals() vlt. [if "0" in string] oder [Pos('0', 
string)>0] (ich weiß nicht wie mann in C# formuliert...)
Die funktion serialport.ReadLine() liefert ein String mit Zeilenvorschub 
zurück, man kann mit funktion Trim trimern, bevor man vergleich
[if (input.Equals("0"))...]
.....

von Johnny B. (johnnyb)


Lesenswert?

Mach den Empfang der Daten mittels Event:

Für Deinen konkreten Fall machst Du einen Event-Handler und meldest ihn
beim SerialPort an (dieser Code wird automatisch erzeugt, wenn Du Dir
das im Designer zusammenklickst):
1
serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

Der Handler zum empfangen der Daten könnte etwa so aussehen:
1
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
2
{
3
  int bytecount = ((SerialPort)sender).BytesToRead;
4
  byte[] rxbuffer = new byte[bytecount];
5
  ((SerialPort)sender).Read(rxbuffer, 0, bytecount);
6
7
  // Im Array "rxbuffer" stehen nun die empfangenen Daten drin
8
9
  // Wenn Du's stringmässig verarbeiten willst:
10
  string str = System.Text.Encoding.ASCII.GetString(rxbuffer);
11
}

Guck mal in diesen Thread, da ist alles drin was Du brauchst:

Beitrag "C# UART timing Problem?"

: Bearbeitet durch User
von doppelschwarz (Gast)


Lesenswert?

Was empfängst Du denn genau mit dieser Zeile? Welchen Wert hat input?
1
  string input = serialport.ReadLine();

Habe gerade mal nachgeschaut, die Methode wartet ab, bis ein NewLine 
kommt und entfernt dann das NewLine (siehe 
https://msdn.microsoft.com/de-de/library/system.io.ports.serialport.readline(v=vs.110).aspx). 
Die Frage ist natürlich, ob das richtige NewLine kommt (\r, \n oder 
\r\n). Einfach mal einen Breakpoint setzen und schauen, wo es hängt und 
dann melden.

Falls Du Dein Programm später noch erweitern willst empfehle ich Dir, 
den serialPort_DataReceived-Event zu verwenden. Damit wartet Dein 
Programm nicht auf die Eingabe, ist also nicht blockiert bis etwas 
kommt, sondern läuft weiter. Einfach bei den Eigenschaften des 
SerialPorts auf Ereignisse umschalten (Blitzsymbol) und auf DataReceived 
doppelklicken, dann wird automatisch die Methode erstellt.

von Sven K. (quotschmacher)


Lesenswert?

Chris V. schrieb:
> Da viele anscheinend mein Problem nicht ganz verstanden haben,

dann ist es meistens falsch erläutert...

Dr. Sommer schrieb:
> Sende einfach vom Arduino jede Sekunde den aktuellen Stand.

würde ich auch bevorzugen und dann den eventhandler dazu

Tany schrieb:
> [Pos('0',
> string)>0]

das bringt auch nichts. dann haben wir immer noch (ich schreibe jetzt 
mal in dezimal) 0!= 48 (die ascii präsentation von 0, auf die hier 
geprüft wird)

von Einer K. (Gast)


Lesenswert?

Chris V. schrieb:
> as Anfragen vom PC ist ja nicht das Problem. Mein Problem ist dass ich
> nicht weiß wie ich die Daten vom Arduino auswerten kann um damit dann
> irgendetwas zu machen im C# Programm (z.B. die Farbe der Buttons
> ändern). Das Problem hätte ich ja auch wenn ich jede Sekunde den Stand
> übermitteln würde.

Suchtipp:
"Arduino CmdMessenger"

von Tany (Gast)


Lesenswert?

Sven A. schrieb:
> (ich schreibe jetzt mal in dezimal) 0!= 48


> Versuche damit:
> .....
> Serial.println(state1+'0');

Beitrag #5474420 wurde vom Autor gelöscht.
von Sven K. (quotschmacher)


Lesenswert?

direkt über dem zitierten... mir war vorhin so, als hätte ich das 
irgendwo gelesen, hab's dann aber auf die schnelle übersehen

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.