Hallo

Ich will hier mal ein kleinen Guide dazu geben wie ich eine Terminalanwendung mit den PC und den Arduino über USB realisiert habe.

Hatte dabei doch ein paar Probleme und kaum verständliche Guides im Internet gefunden. Lag zum Teil dran das ich ziemlich unerfahren in den Bereich bin und auch mein Englisch nicht das Beste ist. Jedenfalls hab ich aus den diversen Quellen nun eine funktionierende Terminalanwendung gefertig und würde nun meine erkenntnisse mit euch teilen. Für andere die auch gern wissen wollen wieso das script das macht was es macht :D

Der Arduino Code

Der PC Code

Erläuterungen zum Arduino Code

Erläuterungen zum PC Code

Fangen wir erstmal an mit den Quellcode. Danach werd ich dann auf einzelne Punkte etwas genauer eingehen.

Der Arduino Code: Zum Anfang

/*
Name: Serialcommunication.ino
Created: 28.03.2018 11:32:32
Author: Sascha
*/

//diverse Variablen
bool waitfordata = false;
bool firstconnection = true;
String question = "";
String msg = "";
String msglowercase = "";
String name = "";
String age = "";
// the setup function runs once when you press reset or power the board
void setup() {


//Serialverbindung starten
Serial.begin(115200);

}
//Eine Funktion für eine kleine Übersicht über den serialport. Da meine Anwendung probleme hatte hab ich die Delays zugefügt.
void helpmessage()
{
Serial.println("Welcome by my Arduino Terminal");
delay(200);
Serial.println("------------------------------");
delay(200);
Serial.println("option");
delay(200);
Serial.println("show data---------show the complete Dataset");
delay(200);
Serial.println("show name---------show Data for Name");
delay(200);
Serial.println("show age----------show Data for Age");
delay(200);
Serial.println("help--------------show this Message");
delay(200);
Serial.println("***********************************");
}
//Funktion zum kontrollieren ob ein Befehl eingegeben wurde. Gibt ein int zurück der später ausgewertet wird
int selectoption(String x)
{
if (x == "show name")
{
return(1);
}
else if (x == "show age")
{
return(2);
}
else if (x == "show data")
{
return(3);
}
else if (x == "help")
{
return(4);
}
else if (x == "disconnect")
{
return(5);
}
else
{
return(0);
}
}

// the loop function runs over and over again until power down or reset
void loop()
{
//Wenn grad nicht auf Daten gewartet wird
if (waitfordata==false)
{
//Wenn eine neue Verbindung aufgebaut wurde
if (firstconnection == true)
{
//Schickt die Einführungstext
helpmessage();
//setzt den Bool auf False damit die Meldung nur einmal kommt
firstconnection = false;
//weißt die erste Frage zu
question = "Please insert your name?";
}
//stellt eine frage
Serial.println(question);
//setzt den bool das auf eine antwort gewartet wird
waitfordata = true;
}
else
{
//Sobald Daten im serialbuffer sind
if (Serial.available()>0)
{
//fügt die Daten der Variable zu
msg = Serial.readString();
//entfernt das letzte Zeichen (/n)
msg.remove(msg.length()-1,1);
//Kopiert die Varible um sie weiter zu verarbeiten
msglowercase = msg;
//wandelt alle Zeichen in klein Buchstaben um, für die Befehle (Um Tippfehler zu vermeiden)
msglowercase.toLowerCase();
//hier werden die Befehle verarbeitet
switch (selectoption(msglowercase))
{

case 1:
{
//Gibt den Namen zurück
Serial.println("Name = " + name);
break;
}
case 2:
{
//Gibt das Alter zurück
Serial.println("Age = " + age);
break;
}
case 3:
{
//Gibt beide daten zurück
Serial.println("Name = " + name);
delay(200);
Serial.println("Age = " + age);
break;
}
case 4:
{
//Gibt die Anfangsbegrüßung wieder aus
helpmessage();
break;
}
case 5:
{
//Wird bei der Verbindung vom PC ausgelöst
firstconnection = true;
break;
}
default:
//Wenn kein Befehl eingegeben wurde
{
//Abfrage welcher Step grad ist
if (question == "Please insert your name?")

{
//Zuweisen der Daten der Variable
name = msg;
//und Antworten mit den Daten
Serial.println("Welcome " + msg);
//nächste Frage vorbereiten
question = "Please insert your age?";

}
//Abfrage welcher Step grad ist
else if (question == "Please insert your age?")
{
//Zuweisen der Daten der Variable
age = msg;
//und Antworten mit den Daten
Serial.println("Your age is " + msg);
//nächste Frage vorbereiten
question = "Please insert your name?";
}
break;
}
}
//setzt den Arduino in den empfang Modus zurück
waitfordata = false;

}
}
}


Der PC Code: Zum Anfang

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;
using System.IO.Ports;
using System.Threading;

namespace Serial_CommunicationArduino
{
public partial class Form1 : Form
{
// Delegate für die Richtextbox
public delegate void AddDataDelegate(String myString);
// eine Instance von AddDataDelegate
public AddDataDelegate myDelegate;

public Form1()
{
InitializeComponent();
//Zuweisung der AddDataDelegate Methode
this.myDelegate = new AddDataDelegate(AddDataMethode);
}

private void btn_send_Click(object sender, EventArgs e)
{
//Beim click auf der Schaltfläche wird der Text von der Textbox versendet, der Richtextbox zugefügt und in der Textbox gelöscht
if(serialPort1.IsOpen)
{
serialPort1.WriteLine(txt_msg.Text);
terminal.AppendText(txt_msg.Text + Environment.NewLine);
txt_msg.Text = "";
}
}

private void comboBox1_DropDown(object sender, EventArgs e)
{
//Beim öffnen des Reiters wird die Liste der verfügbaren Comports angezeigt
string[] ports = SerialPort.GetPortNames();
//vorher die Liste der Combobox löschen damit es keine doppelte Einträge gibts
comboBox1.Items.Clear();
foreach (string comport in ports)
{
comboBox1.Items.Add(comport);
}
}

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
//Weißt die Auswahl des Comanschlußes der Serialverbindung zu
serialPort1.PortName = comboBox1.SelectedItem.ToString();

}

private void button1_Click(object sender, EventArgs e)
{
//wenn keine Serialverbindung aktiv ist
if (!serialPort1.IsOpen)
{
try
{
//wird der Serialport geöffnet,
serialPort1.Open();
//ein Befehl zum Arduino geschickt
serialPort1.WriteLine("connect");
//und der Text der Schaltfläche geändert
button1.Text = "disconnect";
//MessageBox.Show("Connection was successfull");

}
catch
{
MessageBox.Show("Please select a Port first or try another one");
}
}
//wenn eine Serialverbindung aktiv ist
else if (serialPort1.IsOpen)
{
try
{
//wird der Serialport geschloßen,
serialPort1.Close();
//der Text der Schaltfläche geändert
button1.Text = "connect";
//und die Richtextbox geleert
terminal.Clear();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
public void AddDataMethode(string myString)
{
//Anweisung der AddDataMethode die den Text in der Richtextbox einfügt. Hab die Farbe des empfangentext noch geändert.
terminal.SelectionColor = Color.Green;
terminal.AppendText(myString);
terminal.SelectionColor = Color.Black;
}

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//ließt den serialport buffer aus
string s = serialPort1.ReadLine();
//fügt über die Delegate Methode die Daten des Serialport zu
terminal.Invoke(this.myDelegate, new Object[] { s });
}
}
}

Ein Bild von den Fenster in Windows:forms

Zum Anfang

So dann fangen wir mal an mit den erklären an. Zuerst mit den Arduino Code. Ich nutze ein Mega 2560.

void setup() {


//Serialverbindung starten
Serial.begin(115200);

}

Hier öffne ich beim start die Serialverbindung mit Serial.begin(int) am Arduino.Der Wert in den Klammern ist die Geschwindigkeit der Verbindung die an beiden Seiten gleich sein muß und auch abhängig von den Geräten ist. Mehr hab ich im Setup noch nicht zu tun.

void helpmessage()
{
Serial.println("Welcome by my Arduino Terminal");
delay(200);
Serial.println("------------------------------");
delay(200);
Serial.println("option");
delay(200);
Serial.println("show data---------show the complete Dataset");
delay(200);
Serial.println("show name---------show Data for Name");
delay(200);
Serial.println("show age----------show Data for Age");
delay(200);
Serial.println("help--------------show this Message");
delay(200);
Serial.println("***********************************");
}

Die Methode hab ich nur eingefügt damit ich das nicht mehrmals tippen muß. Ist ein kleine Anleitung die bei der ersten Verbindung und bei den Befehl help erscheint. Mit den Befehl Serial.println(string) gibt man den String in den Klammern über die Serialverbindung weiter. Mit delay(int) wartet das Programm die angegeben Zahl in Millisekunden. Hatte ohne den Delay probleme beim empfang am PC

//Funktion zum kontrollieren ob ein Befehl eingegeben wurde. Gibt ein int zurück der später ausgewertet wird
int selectoption(String x)
{
if (x == "show name")
{
return(1);
}
else if (x == "show age")
{
return(2);
}
else if (x == "show data")
{
return(3);
}
else if (x == "help")
{
return(4);
}
else if (x == "disconnect")
{
return(5);
}
else
{
return(0);
}
}

In dieser Funktion kontrollier ich ob einer der Befehle eingegeben wurde und gibt dementsprechend ein int zurück, der dann im Loop ausgewertet wird.

void loop()
{
//Wenn grad nicht auf Daten gewartet wird
if (waitfordata==false)
{
//Wenn eine neue Verbindung aufgebaut wurde
if (firstconnection == true)
{
//Schickt die Einführungstext
helpmessage();
//setzt den Bool auf False damit die Meldung nur einmal kommt
firstconnection = false;
//weißt die erste Frage zu
question = "Please insert your name?";
}
//stellt eine frage
Serial.println(question);
//setzt den bool das auf eine antwort gewartet wird
waitfordata = true;
}

In diesen Block sendet der Arduino Daten über den Serial. Um in in Sende oder Empfangbetrieb zu schalten ist der Bool waitfordata da. Der bool firstconnection ist für eine kleine Begrüßung und die Anleitung da und setzt auch die erste Frage für Ablauf des Programms.

else
{
//Sobald Daten im serialbuffer sind
if (Serial.available()>0)
{
//fügt die Daten der Variable zu
msg = Serial.readString();
//entfernt das letzte Zeichen (/n)
msg.remove(msg.length()-1,1);
//Kopiert die Varible um sie weiter zu verarbeiten
msglowercase = msg;
//wandelt alle Zeichen in klein Buchstaben um, für die Befehle (Um Tippfehler zu vermeiden)
msglowercase.toLowerCase();

Sobald im Serialbuffer Daten sind (if (Serial.availabel() >0) ) kopiere ich sie in einer Variable. Mit remove() entfern ich noch das letzte Zeichen was mit gesendet wird. In diesen Fall ein New Line Zeichen. Danach kopiere ich nochmal in eine weiter Variable die ich nun noch mit toLowerCase() in nur Kleinbuchstaben verwandel. Hab ich gemacht damit bei den Befehlen die Groß- und Kleinschreibung ignoriert wird.

switch (selectoption(msglowercase))
{

case 1:
{
//Gibt den Namen zurück
Serial.println("Name = " + name);
break;
}
case 2:
{
//Gibt das Alter zurück
Serial.println("Age = " + age);
break;
}
case 3:
{
//Gibt beide daten zurück
Serial.println("Name = " + name);
delay(200);
Serial.println("Age = " + age);
break;
}
case 4:
{
//Gibt die Anfangsbegrüßung wieder aus
helpmessage();
break;
}
case 5:
{
//Wird bei der Verbindung vom PC ausgelöst
firstconnection = true;
break;
}

Hier wird nun die Rückgabe der Funktion selectoption() ausgewertet und entsprechend reagiert. Hab mich für den Switch case entschieden da ich mit break den kompletten restlichen Teil überspringen kann.

default:
//Wenn kein Befehl eingegeben wurde
{
//Abfrage welcher Step grad ist
if (question == "Please insert your name?")

{
//Zuweisen der Daten der Variable
name = msg;
//und Antworten mit den Daten
Serial.println("Welcome " + msg);
//nächste Frage vorbereiten
question = "Please insert your age?";

}
//Abfrage welcher Step grad ist
else if (question == "Please insert your age?")
{
//Zuweisen der Daten der Variable
age = msg;
//und Antworten mit den Daten
Serial.println("Your age is " + msg);
//nächste Frage vorbereiten
question = "Please insert your name?";
}
break;
}
}

Das ist nun das standart Programm was Abläuft. Er stellt erst Frage, antwortet mit der Eingabe und stellt die nächste Frage. Da das ganze nur zum testen ist nichts besonderes.

waitfordata = false;

zum Schluß setz ich den Arduino noch wieder in den Sendemodus damit er die Reaktion auf die Eingabe ausgeben kann.

Das wars zum Arduino Teil der noch recht einfach war.

Zum Anfang

Nun zum schweren Teil (jedenfalls für mich). Das empfangen von Daten läuft bei C# und Net etwas anders ab. Es wird in ein eigenen Process verarbeitet und man kann somit nicht direkt damit Teile von der Formsanwendung ansprechen. Deswegen kommen die Delegates dazu die ich in diesen Code erzeuge.

 public partial class Form1 : Form
{
// Delegate für die Richtextbox
public delegate void AddDataDelegate(String myString);
// eine Instance von AddDataDelegate
public AddDataDelegate myDelegate;

public Form1()
{
InitializeComponent();
//Zuweisung der AddDataDelegate Methode
this.myDelegate = new AddDataDelegate(AddDataMethode);
}

Hier wird halt der Delagate ( so eine Art Zeiger auf Methoden) erzeugt und den Programmablauf hinzugefügt. Somit kann ich nun auch Außerhalb des eigentlichen Prozeßes eine Methode ausführen.

 public void AddDataMethode(string myString)
{
//Anweisung der AddDataMethode die den Text in der Richtextbox einfügt. Hab die Farbe des empfangentext noch geändert.
terminal.SelectionColor = Color.Green;
terminal.AppendText(myString);
terminal.SelectionColor = Color.Black;
}

Das ist nun die Methoden vom Delegate die Ausgeführt wird. In diesen Fall wird der übergebene String der Richtextbox terminal angehangen. Hab noch vorher die Textfarbe geändert damit man in der Box sehen kann was man selbst eingeben hat und was vom Arduino kommt.

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//ließt den serialport buffer aus
string s = serialPort1.ReadLine();
//fügt über die Delegate Methode die Daten des Serialport zu
terminal.Invoke(this.myDelegate, new Object[] { s });
}

Hier ist nun die Methode mit den Delegate. Diese Methode wird immer dann aufgerufen wenn am Serialport neue Daten eingehen. Die werden in den string s gelesen und dann per terminal.invoke (this.myDelegate, new Object[] {s}); das Richfenster dazu gezwungen sich neuzuzeichnen mit den Daten von den string. Also mir anzuzeigen was er Empfangen hat.

 private void button1_Click(object sender, EventArgs e)
{
//wenn keine Serialverbindung aktiv ist
if (!serialPort1.IsOpen)
{
try
{
//wird der Serialport geöffnet,
serialPort1.Open();
//ein Befehl zum Arduino geschickt
serialPort1.WriteLine("connect");
//und der Text der Schaltfläche geändert
button1.Text = "disconnect";
//MessageBox.Show("Connection was successfull");

}
catch
{
MessageBox.Show("Please select a Port first or try another one");
}
}
//wenn eine Serialverbindung aktiv ist
else if (serialPort1.IsOpen)
{
try
{
//wird der Serialport geschloßen,
serialPort1.Close();
//der Text der Schaltfläche geändert
button1.Text = "connect";
//und die Richtextbox geleert
terminal.Clear();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}

Das ist die Funktion zum öffnen des Serialport am PC. Ich habe es über einen einzelnen Button geregelt somit die Abfrage if (serialPort1.IsOpen). Damit kann man den Zustand abfragen. Jenachdem wie der Zustand ist wird auch der Text des Buttoms geändert. Beim Verbinden wird auch gleich ein connect zum Arduino gesendet was ihn veranläßt seine Begrüßung zu senden.

 private void comboBox1_DropDown(object sender, EventArgs e)
{
//Beim öffnen des Reiters wird die Liste der verfügbaren Comports angezeigt
string[] ports = SerialPort.GetPortNames();
//vorher die Liste der Combobox löschen damit es keine doppelte Einträge gibts
comboBox1.Items.Clear();
foreach (string comport in ports)
{
comboBox1.Items.Add(comport);
}
}

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
//Weißt die Auswahl des Comanschlußes der Serialverbindung zu
serialPort1.PortName = comboBox1.SelectedItem.ToString();

}

Mit diesen beiden Methoden kann man den Comport auswählen und zuweisen. Mit string[] ports = SerialPort.GetPortNames(); wird bei jeden öffnen der Comboleiste die Comports von den PC aufgelistet. comboBox1.Items.Clear(); leert die Comboliste vorher damit keine doppelten Einträge gibts und die Schleife fügt dann die gefunden Ports der Comboleiste hinzu. Sobald dann einer ausgewählt wurde wird er auch den Serialport1 zugewiesen.

private void btn_send_Click(object sender, EventArgs e)
{
//Beim click auf der Schaltfläche wird der Text von der Textbox versendet, der Richtextbox zugefügt und in der Textbox gelöscht
if(serialPort1.IsOpen)
{
serialPort1.WriteLine(txt_msg.Text);
terminal.AppendText(txt_msg.Text + Environment.NewLine);
txt_msg.Text = "";
}
}

Und nun noch eine Funktion die ein Text aus der Textbox absendet, in den terminal anzeigt und die Textbox wieder leert.

 

Hoffe dieser Guide macht es euch verständlich was ich da zusammengebastelt habe und am wichtigstens auch warum. Ich selber hoffe das ich grad das Thema Delegates richtig verstanden hab. Nun habt Spaß am selber ausprobieren.

 

Gruß shadow

 

P.S.: tippfehler könnt ihr gern für euch behalten:D

Wir benutzen Cookies

Wir nutzen Cookies auf unserer Website. Einige von ihnen sind essenziell für den Betrieb der Seite, während andere uns helfen, diese Website und die Nutzererfahrung zu verbessern (Tracking Cookies). Sie können selbst entscheiden, ob Sie die Cookies zulassen möchten. Bitte beachten Sie, dass bei einer Ablehnung womöglich nicht mehr alle Funktionalitäten der Seite zur Verfügung stehen.