Arduino darstellen mit C# über SerialPort

Hallo zusammen,

ich kann die Temperaturwerte einlesen mit dem Arduino. Das funktioniert auch.

Jetzt möchte ich die Werte mit C# darstellen. An dem Code ist etwas falsch. Weiß jemand wie man diese Funktion richtig programmiert, damit die Senorwerte über das Label ausgegeben werden?

private void label1_Click(object sender, EventArgs e)

Ich bin für jede Hilfe dankbar!

Das ist der Code in C#:

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 WindowsFormsApp5
{
   public partial class Form1 : Form
   {
       SerialPort myport;

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

       private void Verbindung()
       {
           try
           {
               myport = new SerialPort();
               myport.BaudRate = 9600;
               myport.PortName = "COM5";
               myport.Open();
               MessageBox.Show("Arduino erkannt");
           }
           catch (Exception)
           {
               MessageBox.Show("Arduino nicht angeschlossen");
           }
       }


       private void label1_Click(object sender, EventArgs e)
       {
           string temp = myport.ReadLine();
           label1.Text = temp;
       }
       
   }
}

laoleo:
Das ist der Code in C#: …

Blöderweise wird der Arduino nicht in C# sondern in C/C++ programmiert. Ich finde diese Sprachen eh schöner (weil ich C# gar nicht kenne :slight_smile:

Falls es um den Programmteil auf dem PC geht: Nimm Linux und den GNU C+±Compiler. Damit hatte ich bislang nie Probleme.

Ansonsten: Fasse den Code in Code-Tags, damit er scroll- und lesbar wird. Du kannst das auch nachträglich tun: Posting bearbeiten, Code markieren, Code-Tag in der Symbolleiste (ganz links) klicken, speichern.

Gruß

Gregor

Das Auslesen der seriellen Schnittstelle sollte über einen Event Handler erfolgen. Nicht wenn man manuell klickt. Und dann im Event Handler über Invoke auf das GUI Element zugreifen

Hier eine Demo und etwas Erklärungen dazu:

Der serielle Kram ist ein einer eigenen Klasse. Das ist etwas mehr als unbedingt nötig, aber dafür robuster und flexibler.

Was da mit einer RichTextBox gemacht ist geht genauso mit einem Label. Du kannst dir eine Methode schreiben die ein Label und einen String als Parameter hat:

private void UpdateLabel(Label label, string text)
{
  if (label.InvokeRequired)
  {
    label.BeginInvoke((MethodInvoker)delegate
    {
      label.Text = text;
    });
  }
  else
     label.Text = text;
}

Und statt TextPoxPrint kannst du eine Parser-Methode nehmen die den String untersucht und je nach Inhalt auf ein anderes Label schreibt. Oder generell eine andere Komponente. Was da praktisch ist hängt von der Anwendungen ab. Für zwei Temperaturen kann man die Daten z.B. so senden:
T1:10.0
T2: 15.5
Dann Split() machen und abfragen was im ersten Teil-String steht

Es kann auch reichen einen einzelnen Buchstaben voranzustellen

Serenifly:
Der serielle Kram ist ein einer eigenen Klasse. …

BTW: Gestern bin ich auf der Suche nach etwas Anderem über die Info gestolpert, dass die SoftwareSerial veraltet/obsolet sei und man eine NewSoftwareSerial-Bibliothek verwenden soll.

Weißt Du davon etwas? Eine Bibliothek mit diesem Namen konnte ich jedenfalls nicht finden.

Gruß

Gregor

NewSoftwareSerial ?
AltSoftSerial

"Alt", nicht von Zeit, sondern von alternativ.

Ist zwar Off Topic, aber die Info ist total veraltet. Die Änderungen in NewSoftSerial wurde schon vor etlichen Versionen in die normal SoftwareSerial übernommen

Ich hab meinen Code jetzt ein wenig erweitert und ein EventHandler eingebaut. Mit switch möchte ich drei Werte drei Labels zuordnen. Allerdings werden mir in dieser Form keine Werte angezeigt beim Ausführen des Programms.

Meine Frage wäre: Was fehlt bei dem Code noch, damit er automatisch beim öffnen bei jedem Label, den entsprechenden Wert anzeigt?

Mfg laoleo

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 WindowsFormsApp6
{
    public partial class Form1 : Form // Vererbung der Klasse
    {
        SerialPort myport;

        public Form1() //Konstruktor 
        {
            InitializeComponent();
            Verbindung(); //Verbindng zum SerialPort schaffen und entsprechender Ausgabe ob erkannt oder nicht
            myport.DataReceived += new SerialDataReceivedEventHandler(myport_DataReceivedHandler);//Wird automatisch aufgerufen wenn Daten ankommen.
        }
        private void Verbindung()
        {
            try
            {
                myport = new SerialPort();
                myport.BaudRate = 9600;
                myport.PortName = "COM5";
                myport.ReadTimeout = 500;
                myport.Open();
                MessageBox.Show("Arduino erkannt");
               

            }
            catch (Exception)
            {
                MessageBox.Show("Arduino nicht angeschlossen");
            }
               
            
        }
        private void myport_DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            try //Könnte eine Ausnahme auslösen
            {
                string line= myport.ReadLine();
                int _currentLine=0;
                 _currentLine++;
            
                switch(_currentLine)
                    {
                        case 1:
                                label1.Text = line;
                            break;
                        case 2:
                                label5.Text = line;
                            break;
                        case 3:
                                label6.Text = line;
                            break;

                    }
            }
            catch (Exception) //Behandelt die Ausnahme
            {
                return;

            }
        }


        private void label1_Click(object sender, EventArgs e) //EventArgs: Basisklasse für Klassen, die Ereignisdaten enthalten
        {
            
        }

       
        private void label2_Click(object sender, EventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void label5_Click(object sender, EventArgs e)
        {

        }

        private void label1_Move(object sender, EventArgs e)
        {

        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {

        }

        private void label6_Click(object sender, EventArgs e)
        {

        }
    }
}

Rufe die Invoke Methode der jeweiligen Komponente auf. Nicht der ganzen Form. Siehe den Code in Post #2. Ich bin mir nicht sicher ob das wirklich nötig ist und es nicht doch einfach über die Form geht, aber es funktioniert auf jeden Fall :slight_smile:

Und mit einem MethodInvoker kann man den delegate wie oben gezeigt schön lokal verpacken. Ohne einen extra zu erstellen. So braucht man nur pro Komponenten-Typ eine Methode für die Ausgabe. UpdateLabel() hat als Parameter das Label auf das man schreiben möchte und den Text. Dann kannst du 10 Labels haben und das geht mit allen.

Ansonsten, empfängst du überhaupt was? Mal einen Breakpoint im Event Handler gesetzt ob der aufgerufen wird?

Sorry, ich hatte den falschen code gepostet. Jetzt ist er richtig.
Manuell, also mit Klicken auf dem label, hat der Label Werte angezeigt. Mein Ziel ist das alles beim Starten des Programms sich selbst anzeigt ohne das ein clicken etc. notwendig ist.

Wie wäre den der Ablauf mit der Updatelabel-methode?

Meine Vorstellung wäre so:

myport.DataReceived += new SerialDataReceivedEventHandler(myport_DataReceivedHandler);

ruft auf:
private void myport_DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)

In dieser Methode dann im switch dann zum Beispiel für Label1:

case1: UpdateLabel( label, line)

Mfg laoleo

Ja. Der Event Handler ruft eine Parser Methode auf die den empfangenen String untersucht und dann je nach Inhalt die passende Anzeigen-Methode aufruft.

Ich habe das z.B. mal so gemacht dass alles was "Text:" am Anfang hat in der RichTextBox ausgegeben wird. Alles was "Plot:" am Anfang hat in einem Graphen. Und der Rest wird je nach Wert auf Labels ausgegeben.

Mein Ziel ist das alles beim Starten des Programms sich selbst anzeigt ohne das ein clicken etc. notwendig ist.

Was eventuell praktisch sein kann ist ein Knopf um die Verbindung aufzubauen und zu unterbrechen

Das Zählen der Zeilen kann so aber mit einer lokalen nicht-statischen Variable nicht funktionieren. Außerdem bist du damit nicht mit dem Arduino synchronisiert! Du kannst da irgendwo mittendrin einsteigen.
Am besten du stellst in der Zeile irgendwas voran um zu kennzeichnen was die Zeile darstellen soll. z.B.
"1:xxx"
"2:xxx"
Oder (für Temperaturen)
"T1:xxx"
"T2:xxx"

Das mit Invoke ist nötig weil du nicht aus aus einem anderen Thread auf das GUI Thread zugreifen kannst. Das geht zwar manchmal (zufällig), aber da kommen auch sehr schnell Exceptions

Vielleicht weiß jemand von euch was noch fehlt bzw. was falsch ist?

Mit C# kenne ich mich nicht so aus.
Ahne aber, was du falsch machst.

Der dritte Thread zum gleichen Thema.
Ich glaube, viel leichter kann man die Helfer nicht verprellen.

Wieso machst du noch mehr Threads zu dem Thema auf? Wie gesagt, überprüfe ob du überhaupt etwas empfängst. Wenn nicht verwende mal die Serial Klasse die ich in dem Link oben angehängt habe

Die kannst du z.B. so initialisieren:

_serial = new SerialCommunicator(Parser, Error);

Das sind jeweils zwei Methoden mit nur einem String als Parameter. Die erste verarbeitet eine empfangene Zeile. Die zweite gibt Fehler aus.

Man kann auch nur eine Methode für beides übergeben, aber bei deiner Anwendung ist das nicht gut. Oder man übergibt für den zweiten null, dann werden keine Fehler ausgeben.

Ich hab festgestellt, dass myport.DataReceived nicht myport_DataReceivedHandler aufruft. Weiß nicht wieso :/..

Wenn ich im Konstruktor Update seperat aufrufe liest er den Wert ein und gibt ihn auch im richtigen Label aus.

Ich hatte schon Fälle in denen der Event Handler nicht zuverlässig funktioniert. Nicht in dem Sinne dass es gar nicht geht, aber in denen er nicht flüssig aufgerufen wird.
Was auch geht ist wenn man sich ein Timer Objekt anlegt (gibt es auch als GUI Komponente!) und dieses z.B. alle 10-50ms eine Funktion aufrufen lässt. In dieser fragt man mit BytesToRead > 0 (das ist ein Property, keine Methode) ab ob Daten da sind, und wenn ja macht readLine() um eine Zeile auszulesen

Es kann vielleicht sein dass die Verbindung zu früh aufgebaut wird. Sollte zwar im Konstruktor der Form gehen, aber wer weiß. Vielleicht mal den Load() Event Handler probieren. Ist aber reine Spekulation. Ich mache das normal nicht gleich beim Laden der Form sondern über einen Button.

P.S.: du machst readLine(). Dann solltest du vorher myport.NewLine = "\r\n" machen damit das Endzeichen auf CR + LF steht.
P.P.S.: Es ist unsinnig die Methode UpdateLabel1() zu nennen und die Variablen darin label1. Die Methode funktioniert mit einem beliebigen Label Objekt

Vielen dank für die schnelle Hilfe. Ich werd mich dran versuchen:)