C# Anwendungen und SerialPort

Hallo ArduinoGemeinde,

ich möchte c# anwendungen schreiben die daten von meinem arduino auslesen oder eben andersherum.

Das hier habe ich schon mal in einer funktion verstaut und funzt:

try
            {
                SP = new SerialPort();
                SP.PortName = "com3";
                SP.BaudRate = 9600;
                SP.ReadTimeout = 500;
                SP.Open();
            }
            catch (Exception)
            {
                MessageBox.Show("nix dran man!");
            }

mein problem ist, dass ich wenige ausführliche beispiele gefunden habe . weiß jemand ein gutes tutorial oder weiß ein paar gute beispielanwendungen? bisher kann meine c# anwendung nur erkennen ob ein arduino dran ist oder nicht.
Auch ein paar beispiele wie welcher wert konvertiert und ausgelesen wird. Naja, ihr wisst schon einfach ein paar gute beispiele. Danke!

Hallo,

Google mit "visual studio c# serial port" angewurfen ...

jede Menge Links auch zu youtube.

das habe ich natürlich als erstes probiert...
kannst du mir einen link zu einem gutem video senden?

Hallo,

ich programmiere nur µC, kein Windows.
Ich verstehe nur nich was so schlimm daran ist sich paar Videos anzuschauen.
Das ist sowieso Geschmackssache welches einem eher in der Erklärung gefällt.

Hallo HelloItsMe,

für mich scheint es so, als suchst Du das eine, perfekte Tutorial für Deinen Anwendungsfall.
Das gibt es aber nicht.
Du musst schon nach den einzelnen Anforderungen suchen:

Bestimmtes Gerät am COM-Port erkennen:
Das ist nicht so einfach, denn der USB mappt den Arduino nicht immer auf den selben COM-Port.
Daher muss man sich leider etwas umständlich den Namen holen. z.B. so

Vielleicht reicht Dir aber auch erstmal nur zu checken welche COM-Ports gerade verfügbar sind.
GetPortNames

Konvertieren von Werten:
Convert ist Dein Freund.
Man kann das dann mit einem try-catch abfangen. Oder direkt TryParse verwenden.

Vermutlich wird das aber alleine nicht helfen. Denn Du hast noch mehrere (Design-) Probleme.
Du weißt nicht wann Du Daten vom Serial-Port empfängst. Daher solltest Du mit dem DataReceived-Event arbeiten. Ein Zeilenendzeichen solltest Du ebenfalls verwenden, damit das Programm weiß wann die Daten komplett empfangen wurden.
Nun solltest Du Dir ein Format ausdenken, damit Du auch weißt, in was Du einen Wert konvertieren musst.
Wenn Du z.B. die Pin Status abfragen möchtest, könntest Du "d0" und "d1" für digital An/Aus übertragen. Gefolgt von einem CarriageReturn (Char13). Für Analog dann entsprechend "a0" - "a1024".

Je nach dem, was Du letztendlich mit den Daten anfangen möchtest.

MSDN kennst du nicht? Die MSDN Beispiele zur SerialPort Klasse decken alles grundlegende ab.
Ich hatte aber schon Fälle in denen der SerialReceived Event nicht richtig auslöst. Abhilfe schafft dann ein Timer der nachschaut ob Daten da sind. Aber der normale EventHandler sollte es erst mal tun:

private void DataReceived(Object source, ElapsedEventArgs e)
{
    try
    {
        string str = _serialPort.ReadLine();     //liest alles bis zum definierten Newline Zeichen ein. CR/LF passt hier perfekt
   }
   catch (Exception ex)
   {
      ...
   }
}

Auch beachten, dass dieser Event in seinem eigenen Thread ausgelöst wird aus dem du nicht auf das GUI Thread zugreifen kannst. Also dabei immer mit InvokeRequired und BeginInvoke() arbeiten wenn du Daten auf einem GUI anzeigen willst! z.B. hier für eine RichTextBox:

private void TextBoxPrint(string text)
{
   if (communicationOutput.InvokeRequired)
   {
      communicationOutput.BeginInvoke((MethodInvoker)delegate
      {
         if (text != null)
             communicationOutput.AppendText(text);
      });
    }
    else
    {
       if (text != null)
          communicationOutput.AppendText(text);
    }
}

Das ist ein Punkt der in dem MSDN Tutorial nicht abgedeckt wird, weil das Konsolenanwendungen sind.

Den Com Port detektieren geht recht einfach so:

var instances = new ManagementClass("Win32_SerialPort").GetInstances();

foreach (var instance in instances)
{
   string description = instance["Description"].ToString();
   string id = instance["DeviceID"].ToString();
     

   ...
}

Die Standard Arduinos enthalten "Arduino" in description. Darauf kann man abfragen. Und "id" ist der Comport String.
Bei Arduinos mit alternativen USB/TTL Wandlern wird es komplizierter. Da kann man z.B. abfragen ob der String nicht "Communications Port" und "Kommunikationsanschluss" enthält. . Diese sind da leider nicht aufgelistet.

Da einfach mal rein debuggen. Dann sieht man was ausgelesen wird und kann entsprechend die Abfrage schreiben.

Die Referenz auf das System.Management Assembly musst du evtl. per Hand zum Projekt hinzufügen damit er das findet! Und damit meine ich nicht nur die übliche using Anweisung

erstmal danke für die schnellen antworten, ich wurschtel mich mal durch!

soooo! bedankt hab ich mich schon, also richtet sich dieser post an leute die einfach mal ein simples beispiel wollen.

so siehts aus in visual studio (wichtig: using System.IO.Ports verwenden sonst geht nix):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;

namespace ArduinoTestZweiWForms
{
    public partial class Form1 : System.Windows.Forms.Form
    {

        SerialPort  myPort;

        public Form1()
        {
            InitializeComponent();
            VerbindungsQuatsch();
        }

        private void VerbindungsQuatsch()  // fürs beispiel simpel gehalten
        {
            try
            {
                myPort = new SerialPort();
                myPort.PortName = "com3";     // !!ist nicht immer unbedingt com3!!
                myPort.BaudRate = 9600;
                myPort.ReadTimeout = 500;
                myPort.Open();
                MessageBox.Show("Arduino erkannt!");  

               

            }
            catch (Exception)
            {
                MessageBox.Show("Arduino nicht angeschlossen/erkannt");
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                string str = myPort.ReadLine();
                richTextBox1.Text = str;
            }
            catch (Exception)
            {

                return;
            }

        }

        private void richTextBox1_TextChanged(object sender, EventArgs e)
        {

        }
    }
}

am arduino hab ich einen taster angeschlossen und verwende diesen code:

int taste = LOW;
int tasteAlt= LOW;

void setup() {
 Serial.begin(9600);
 pinMode(2, INPUT);
}

void loop() {
  taste = digitalRead(2);

  if  (taste == HIGH && tasteAlt == LOW)
  {
    Serial.println("Hallo, ich bin Arduino!");
    
    tasteAlt = taste;
  }
  if  (taste == LOW && tasteAlt == HIGH)
  {
    Serial.println("der zweite string*********");
    tasteAlt = taste;
  }
  }

wenn der taster betätigt wird und im gui button1 geklickt wird, sthet in der richt text box "Hallo, ich bin Arduino!". wiederholt man den vorgang sthet in der richt text box "der zweite string*********".

würd mich freuen, wenn der thread um ein paar simple beispiele erweiter werden würde. ansonsten die posts meiner vorgänger checken (außer den google doch typen).

ps: convert ist wirklich dein freund!

Das mit dem Thread-sicheren Zugriff per BeginInvoke() hast du mal bequem ignoriert. Dann viel Glück.

Das mit dem Button ist auch etwas ungewöhnlich. Normal aktiviert man einfach den DataReceived EventHandler. Der wird automatisch ausgelöst wenn Daten ankommen. Mit einem Button der die Verbindung aktiviert und deaktiviert kann man dann auch den EventHandler an oder aus machen.

Die RichTextBox kann man übrigens auf ReadOnly setzen. Dann kann man da nichts eintippen. Und per Append() wird neuer Text hinten angehängt.

bequem ignoriert ist gut. erstmal überhaupt irgendeine kommunikation herstellen.
hab mir das auch angesehen. was es macht, versteh ich, wie ichs konkret in meine anwendung mache-ka!
wie gesagt, mehr beispielcode ist willkommen.

Das ist alles. Du schreibst direkt aus direkt auf das GUI:

               richTextBox1.Text = str;

Statt dessen übergibt man den Text an eine Methode die das asynchron über BeginInvoke() macht. Und wie gesagt besser richTextBox1.Append() verwenden. Dann wird der neue Text hinten angehängt.

Wobei es auch sein kann das es geht, weil diese komische Sache mit dem Button machst. Dann kann es sein dass das in der Tat im gleichen Thread läuft. Aber normal macht man das mit EventHandlern. Und dann ist es reiner Zufall falls das funktioniert.

Du machst beim Verbinden das:

myPort.DataReceived += new SerialDataReceivedEventHandler(DataReceived);

Und wenn man die Verbindung trennt das (bevor man Close() macht):

myPort.DataReceived -= new SerialDataReceivedEventHandler(DataReceived);

Das registriert den EventHandler mit dem Objekt und meldet ihn am Ende wieder ab

Und die DataReceived Methode sieht so aus:

private static void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
}

Diese Methode wird automatisch aufgerufen wenn Daten ankommen

Beispiel Code dazu gibt auf MSDN! Lediglich ohne die GUI Sache. Deshalb habe ich das so deutlich erwähnt.

Hier ist mal ein Demo GUI mit dem man was Senden und Empfangen kann.

Enthält auch die Com Port Erkennung. Jedenfalls für Standard Arduinos.

Für nicht Standard Arduinos geht das nicht! Ich weiß nicht wie es mit dem CH340G ist, aber beim FTDI erscheint das mit ManagementClass einfach nicht. Das hatte ich anders in Erinnerung, aber dann fiel es mit wieder ein. In dem Fall kann man das aber mit GetComPortNames kombinieren und den Port per Hand auswählen, aber das habe ich mal weggelassen damit es einfacher wird.

Der eigentliche serielle Teil ist in einer eigenen Klasse. Die Interaktion mit dem Hauptprogramm findet über einen oder zwei Delegates statt. Einen für die normale Ausgabe und einen für die Fehlerausgabe. Oder einen für beides. So kann z.B. Fehler direkt 1:1 ausgeben, aber empfangene Daten erstmal an eine Parser-Methode schicken.

Das Arduino Programm schickt einfach alle empfangenen Zeilen direkt zurück

SerialDemo.zip (77.5 KB)

Serial.ino (593 Bytes)

Hallo

Mal noch einen kleinen Tipp am Rande. Microsoft hat Forms schon 2010 Obsolet gestellt. Das heißt sie entwickeln es nicht weiter. Besser kommst du wenn du xaml benutzt und per Binding auf Properties in einem Viewmodel zugreifst. Ziehe das ganze am besten noch über das MVVM Pattern. Der Vorteil ist, das du nicht mehr Invoken musst.(Achtung, auch hier gibt es natürlich Ausnahmen) wenn du Informationen dazu suchst schau dir am Besten mal die Microsoft Virtual Academy an. Das ist eine Seite mit vielen Tutorials und Kursen. Ist absolut kostenfrei und ein Muss für C# Entwickler, welche gerade beginnen. Ich arbeite jetzt schon 5Jahre mit C#, beruflich, und schau dort immer noch regelmäßig rein.

Was mir gerade noch einfällt. Microsoft hat bei seinem IOT Projekt auch Arduino bedacht und eine Komplette Library zur Kommunikation geschrieben. Die solltest du dir ebenfalls mal anschauen. https://developer.microsoft.com/en-us/windows/iot/docs/wra
Außerdem hilft Google da mit den Suchbegriffen Windows IOT weiter.
Die Arbeit die Microsoft in die Unterstützung von Entwicklern steckt solltest du nutzen. Ich kenne bisher keine vergleichbare Unterstützung. Wie gesagt MSDN, MVA, YouTube (Basta Conference) und die neue IOT Unterstützung mit Arduino support sollte dich hier zu Ziel führen.

Hiswif:
Was mir gerade noch einfällt. Microsoft hat bei seinem IOT Projekt auch Arduino bedacht und eine Komplette Library zur Kommunikation geschrieben. Die solltest du dir ebenfalls mal anschauen. remote-wiring/README.md at develop · ms-iot/remote-wiring · GitHub

Da steht dass das auf Firmata basiert. Dann ist das nicht einfache Kommunikation, sondern um den Arduino komplett vom PC aus zu steuern.

Jup. Stimmt schon, hab ich selbst noch nicht getested. Jedoch kann man sich da ja mal anschauen wie die die Das mit dem USB serial gemacht haben.

Danke Hiswif! Deine Infos haben mir geholfen! Danke auch dem Rest! Die nächsten Fragen kommen bestimmt.

hi,

ohne zu kommunizieren, rein mit WMI (windows management instrumentation).

http://forum.arduino.cc/index.php?topic=177899.msg1319101#msg1319101

läßt sich in jeder programmiersprache verwenden...

gruß stefan