C# + Arduino

Hi

Ich bin absoluter Beginner (habe seit ca 3 Wochen einen Arduino Mega) und bastle seit ca 2 Wochen an einem "Projekt", welches eine kleine Zeitmessanlage abbilden soll.

Dazu habe ich "am Start" auf zwei "Fahrspuren" je zwei IR Sensoren (PRE_STAGE und STAGE), welche bei Einfahrt eines Objektes zur Startlinie feststellen, ob das Objekt richtig steht. (Nämlich dann, wenn beide IR Lichtschranken auf der jeweiligen Seite unterbrochen sind).
Das funktioniert soweit ganz gut.
Um nun einen "Startsignal" auszulösen und die Fahrer wissen, wann Sie losfahren dürfen, habe ich eine "Ampel" mit drei gelben LEDs einer grünen LED und einer roten LED.
Löst jemand (per Softwareanwendung) den Start aus, gehen nacheinander die drei gelben Lampen und danach die grüne Lampe an. (Mit einer 500 ms Zeitverzögerung dazwischen).
Auch das funktioniert bereits reibungslos.

Nun möchte ich aber feststellen, ob der Fahrer schon losgefahren ist, BEVOR die grüne Lampe leuchtet (Fehlstarterkennung). Und da ist der Haken. Das logische ermitteln des Fehlstarts ist einfach.
=> Verlässt der Fahrer den PRE_STAGE Bereich (also der IR Sensor gibt dazu ein Signal) bevor das Grüne Licht leuchtet, ist es ein Fehlstart.

Die Startsequenz (momentan nur auf der linken Bahn) wird momentan per C# beim Click auf den Startbutton mittels folgenden Code

// In meiner C# Applikation
private void btnStart_Click(object sender, EventArgs e) {
  //...weiterer Code, der aber hier überflüssig ist
  SendCommandToArduino ("left_yellow_1");
  Thread.Sleep (_treeDelay);
  SendCommandToArduino ("left_yellow_2");
  Thread.Sleep (_treeDelay);
  SendCommandToArduino ("left_yellow_3");
  Thread.Sleep (_treeDelay);
  SendCommandToArduino ("left_green");
  //...weiterer Code, der aber hier überflüssig ist
}

an den Arduino gesendet, welcher dann per folgenden Code

void loop() {
  
  if(Serial.available()){

    delay(2);

    command = Serial.readStringUntil('\n');

  }

  ////////////
  /// TREE ///
  ////////////    
  
  if(command == "left_yellow_1"){
    
    digitalWrite(LED_LEFT_Y_1,HIGH);
    Serial.println("Y_1_LEFT_ON");
    
  }else if(command == "left_yellow_2"){
    
    digitalWrite(LED_LEFT_Y_2,HIGH);
    Serial.println("Y_2_LEFT_ON");
    
  }else if(command == "left_yellow_3"){
    
    digitalWrite(LED_LEFT_Y_3,HIGH);
    Serial.println("Y_3_LEFT_ON");
    
  } else if(command == "left_green"){
    
    digitalWrite(LED_LEFT_G,HIGH);
    
    //Switch off others
    digitalWrite(LED_LEFT_Y_1,LOW);
    digitalWrite(LED_LEFT_Y_2,LOW);
    digitalWrite(LED_LEFT_Y_3,LOW);
    
    
  } else if(command == "left_red"){

    digitalWrite(LED_LEFT_R,HIGH);
    
    //Switch off others
    digitalWrite(LED_LEFT_Y_1,LOW);
    digitalWrite(LED_LEFT_Y_2,LOW);
    digitalWrite(LED_LEFT_Y_3,LOW);
    digitalWrite(LED_LEFT_G,LOW);

    
  }
  //...weiterer Code, der aber hier überflüssig ist
}

die LEDs anschaltet.
Wenn jetzt das Fahrzeug den PRE_SATGE Bereich verlässt, sendet der IR Sensor den Befehl über den Arduino an das C# Programm. Das wiederum, kann den Befehl aber erst dann entgegennehmen, wenn die Startsequenz abgelaufen ist. Dementsprechend kommt ein eventuelles Fehlstartsignal erst dann an, wenn die grüne Lampe bereits leuchtet.

Wie kann man das programmiertechnisch (entweder am Arduino oder in C#) abbilden?

Vielen Dank für Eure Unterstützung!

Du musst das nicht-blockierend machen. Die C# Anwendung mit sleep() zu blockierend ist gar nicht gut. In einer GUI Anwendung ist das gar Irrsinn. Das ist eher dafür gedacht andere Threads anzuhalten.
Du kannst z.B. auf beiden Seiten Zustands-Automaten verwenden.

Schau dir mal die Timer Klasse in C# an. Gibt es auch als UI Komponente

Und wenn du einfache Komfortfunktionen wie readStringUntil() verwendest schaue in der Referenz nach wie die funktioniert. Das delay() ist überflüssig, da die Funktion sowieso blockiert bis das Linefeed kommt.

Danke erst einmal für Deine Schnelle Antwort.

Serenifly:
Du musst das nicht-blockierend machen.

Wie aber dann, baue ich die Wartezeit bis zum nächsten Aufleuchten einer Lampe ein?

Serenifly:
Die C# Anwendung mit sleep() zu blockierend ist gar nicht gut. In einer GUI Anwendung ist das gar Irrsinn. Das ist eher dafür gedacht andere Threads anzuhalten.

Ich weiß, daher ja auch die Frage, ob es einen anderen Weg gibt :slight_smile:

Serenifly:
Du kannst z.B. auf beiden Seiten Zustands-Automaten verwenden.

Was meinst Du mit "Zustands-Automaten"?

Serenifly:
Schau dir mal die Timer Klasse in C# an. Gibt es auch als UI Komponente

Habe ich gerade getan (also angeschaut). Ich würde daher eher ein Timer beim Start mittels

  System.Timers.Timer treeTimer = new System.Timers.Timer(500);
  treeTimer.Elapsed += OnElapsedTimedEvent;
  treeTimer.Enabled = true;

starten und dann im Event "OnElapsedTimedEvent" schauen, welche Lampe gerade brennt und mache dann halt die nächste an? So etwa? => Das würde ich hinbekommen.

Und in der "Zwischenzeit" (also zwischen zwei Elapsed Events) kann das C# Programm dann weiterhin Signale vom Arduino empfangen?

Serenifly:
Und wenn du einfache Komfortfunktionen wie readStringUntil() verwendest schaue in der Referenz nach wie die funktioniert.

Was meinst Du mit "einfache Kommunikation"? gibt es eine bessere, adäquatere Lösung, um Befehle zwischen Arduino und C# hin und herzuschieben?

Serenifly:
Das delay() ist überflüssig, da die Funktion sowieso blockiert bis das Linefeed kommt.

Das dachte ich auch, habe es aber immer wieder gesehen, dass manche, es so gemacht haben.
Ich werde es einfach entfernen (auskommentieren) und schauen, ob alles weiterhin läuft.

Vielen Dank für Deine Hilfe und ich freue mich, auf weitere Antworten!

PS: Da ich echt Newbie bin, bitte ich Dich es so einfach wie nur möglich zu erklären.

Nochmals vielen Dank!

kuli1978:
Was meinst Du mit "Zustands-Automaten"?

Das ist ein festgelegter Begriff den du nachschlagen kannst. Da gibt es auch hier im Forum sehr viele Beispiele dazu, da letztlich sehr viel Programme so aufgebaut sind

Hört sich in der Theorie nur viel komplizierter an als es ist:

Es geht nur darum sich zu merken in welchem Zustand das Programm gerade ist und je nachdem andere Dinge zu tun

Und in der "Zwischenzeit" (also zwischen zwei Elapsed Events) kann das C# Programm dann weiterhin Signale vom Arduino empfangen?

Und vor allem noch auf deine Benutzereingaben reagieren

Man kann auch andere Dinge in eigene Threads auslagern. Diese kann man dann auch anhalten wenn wirklich nötig

Was meinst Du mit "einfache Kommunikation"? gibt es eine bessere, adäquatere Lösung, um Befehle zwischen Arduino und C# hin und herzuschieben?

Ist sagte "Komfort"-Funktion. Diese fertigen Funktionen sind einfach zu verwenden, aber sie haben Nachteile. Oft wird der Komfort mit einer schlechten Laufzeit oder Speicherverschwendung erkauft. Da kann man teilweise kompensieren wenn man weiß was man tut. Aber das ist bei Anfängern natürlich nicht der Fall. Besser ist es oft man implementiert es per Hand.

Dass du das nur aufrufst wenn available() > 0 ist schon mal sehr gut. Dadurch blockiert es nicht ständig. Die Funktion wartet aber auf das Ende der Nachricht. Das ist nicht immer gut. Bei dir wird es aber erst mal ok sein. Im Moment lass das erst mal so und konzentriere dich auf andere Baustellen

Das dachte ich auch, habe es aber immer wieder gesehen, dass manche, es so gemacht haben.

Das kann man machen wenn man die Zeichen per Hand mit read() ausliest. Das ist aber da keine gute Lösung da die nötige Wartezeit von der Baudrate und Länge der Nachricht abhängt.

Und generell sollte man bei fertigem Code zum Auslesen der seriellen Schnittstelle etwas vorsichtig sein. Da gibt es sehr, sehr viel Unsinn im Netz. Meistens wird das Zeitverhalten der Übertragung nicht richtig berücksichtigt.
Wie gesagt ohne das delay() passt es erst mal. Auch die 2ms delay() stören da nicht groß. Sie sind nur überflüssig

Vielen Dank! Ich probiere weiter und melde mich, wenn ich weiteren Bedarf Deiner außerordentlich guten Antworten benötige.

Schöens WE!

kuli1978:
...
Was meinst Du mit "Zustands-Automaten"?
...
Und in der "Zwischenzeit" ...

Ein „Zustands-Automat“ oder „Endlicher Automat“ ist eine Art Programmier-Strategie, mit der Du eine Art Multitasking realisieren kannst. Du möchtest ja gleichzeitig Ampel-LEDs schalten und auf Frühstart testen.

Um zu verstehen, worum es beim Endlichen Automaten geht, liest Du Dir am besten die Nachtwächter-Erklärung durch. Was mir dazu eingefallen ist, kannst Du hier lesen.

HTH

Gregor

Seitens C# stellst Du ja nun auf einen Timer um, welcher ein Event auslöst wenn er abläuft.

Daher möchte ich präventiv darauf hinweisen, dass Du die Kommunikation mit dem COM-Port genauso machen solltest!
Es gibt dort ein DataReceived-Event welches aufgerufen wird, wenn Daten anstehen.

TriB:
Daher möchte ich präventiv darauf hinweisen, dass Du die Kommunikation mit dem COM-Port genauso machen solltest!
Es gibt dort ein DataReceived-Event welches aufgerufen wird, wenn Daten anstehen.

Das habe ich schon! :slight_smile:

comPort = new SerialPort ("COM" + portNumber.ToString (), baudRate, parity, dataBits, stopBits);
comPort.DataReceived += _comPort_DataReceived; 
comPort.Open ();
[code]

Hi.

Ich wollte mich nur mal melden und bekannt geben, dass nun alles so funktioniert, wie es auch funktionieren soll.

Durch die elapsed Events (stopWatch und tree timer) in C# musste ich mich zwar mit einigen delegates rumprügeln, um auf die Formelemente (Textboxen, Buttons usw) zugreifen zu können. Aber am Ende (wenn man es einmal begriffen hat) ist das auch kein großes Ding gewesen.

Erneut sage ich "Vielen Dank!" an Euch alle.
Ich werde Euch (natürlich mit Eurer Erlaubnis) in den Credits erwähnen.

Eine letzte Frage habe ich noch. Ich möchte nun einen Schaltplan bauen.
Dort gibt es einige Tools (Software), mit denen ich das machen kann. Ich habe schon einige auf YouTube gesehen. Jedoch war dort nie der Softwarename zu sehen.

Habt Ihr hier eventuell noch Empfehlungen?

Viele Grüße aus Leipzig!

gibt es eine bessere, adäquatere Lösung, um Befehle zwischen Arduino und C# hin und herzuschieben?

Der CmdMessenger kann Arduino/C++ und PC/C#

combie:
Der CmdMessenger kann Arduino/C++ und PC/C#

Die Möglichkeit mehrere Argumente zu übergeben, scheint mir eine Hilfe zu sein. Momentan sende ich zum Beispiel von C# aus das commando "SB#0#T#12345" um auf einem ScoreBoard der Position 0 eine Zeit (T) mit dem Wert 12345 (in Millisekunden) anzuzeigen.

Am Arduino sieht das dann so aus:

...
command = Serial.readStringUntil('\n');

if(command.substring(0,2) == "SB"){
    
  //Example SB#0#T#12345
  int scoreBoard = command.substring(3,4).toInt();
  String kind = command.substring(5,6); // Time (T) or Velocity (V)
  int value = command.substring(7).toInt();

  ShowTime(scoreBoard,kind,value);
    
}



void ShowTime(int scoreBoardNumber, String kind, unsigned int value) {

  scoreBoards.clearDisplay(scoreBoardNumber);

  int posComma = 0;
  int originalValue = value;
  
  if(value > 99999) return;

  if(kind == "T") //Time => 12.345
    posComma = 3;
  else if(kind == "V") //Velocity => 123.45
    posComma = 2;
  
  d1      = value%10;
  value   = value/10;
  d10     = value%10;
  value   = value/10;
  d100    = value%10;
  value   = value/10;
  d1000   = value%10;
  value   = value/10;
  d10000  = value%10;
  
  
  if(originalValue >= 10000){
    scoreBoards.setDigit(scoreBoardNumber,4,(byte)d10000,false);
  }
  
  if(originalValue >= 1000){
    scoreBoards.setDigit(scoreBoardNumber,3,(byte)d1000,posComma == 3);
  }else{
    scoreBoards.setDigit(scoreBoardNumber,3,(byte)0,posComma == 3);  
  }
  
  if(originalValue >= 100){
    scoreBoards.setDigit(scoreBoardNumber,2,(byte)d100,posComma == 2);
  }else{
    scoreBoards.setDigit(scoreBoardNumber,2,(byte)0,posComma == 2);
  }
  
  if(originalValue >= 10){
    scoreBoards.setDigit(scoreBoardNumber,1,(byte)d10,false);
  }else{
    scoreBoards.setDigit(scoreBoardNumber,1,(byte)0,false);
  }
  
  if(originalValue >= 0){
    scoreBoards.setDigit(scoreBoardNumber,0,(byte)d1,false);
  }else{
    scoreBoards.setDigit(scoreBoardNumber,0,(byte)0,false);
  }
  
}

Wäre hier der CmdMessager in C#/Arduino besser geeignet? Oder ist meine "Lösung" ebenfalls akzeptabel.

Viele Grüße!

kuli1978:
... Ich möchte nun einen Schaltplan bauen.
Dort gibt es einige Tools (Software), mit denen ich das machen kann. Ich habe schon einige auf YouTube gesehen. Jedoch war dort nie der Softwarename zu sehen.
Habt Ihr hier eventuell noch Empfehlungen?

Für das Zeichnen von Schaltplänen benutze ich gEDA. Das kostet nichts und ist für kleine Sachen mehr als ausreichend.

Guck hier. Ein Beispiel findest Du auf dieser Seite.

Gruß

Gregor

besser geeignet?

Das kann ich dir nicht sagen, "was" nun "besser" ist.
Es liegt oftmals im Auge des Betrachters.

Die Möglichkeit mehrere Argumente zu übergeben,

Das können ja nun beide Varianten.

Allemale ist der CmdMessenger eine fertige und erprobte Lösung.
Wenn das "besser" ist, dann ist es das.

Andererseits, kann es auch "besser" sein, zu lernen wie man Protokolle entwickelt und dann Paser und Generatoren diese Protokolle baut.
z.B. das Escapen von Sonderzeichen stellt immer wieder eine lustige Hürde dar. Sozusagen die zweite Schicht im Problem.