Flankenauswertung beim Arduino Mega 2560 für Gleisbelegtmeldung

Hallo zusammen,

ich bin ganz neu hier im Forum, da ich auch gerade erst anfange mit dem Arduino zu programmieren. Etwas Programmiererfahrung habe ich bereits im Bereich JavaScript.
Ich habe einen Arduino Mega 2560 (aktuell noch auf dem Weg zu mir) und möchte diesen zusammen mit IR-Hindernissensoren (Bezeichnung gemäß Reichelt) für die Gleisbelegtmeldung auf meiner Modellbahn (Spur N; 1:160) einsetzten. Dabei soll pro zu überwachendem Gleisblock /-abschnitt je ein Sensor am Anfang und einer am Ende verbaut werden.
Mittels dem Arduino soll dann Auswertung der Sensoren erfolgen. Dabei wird in folgende Ergebnisse unterschieden:

  • Block frei
  • Block in Regelrichtung belegt
  • Block in Gegenrichtung belegt

Mittels tinkercad habe ich den (Prototypen-) Code erfolgreich getestet. Anstelle der IR-Sensor habe ich Schiebeschalter eingebaut, da ich mit diesen problemlos die notwendigen HIGH- und LOW-Signale erzeugen kann.
Allerdings habe ich aktuell noch das Problem, dass der Zähler zur Wagenerfassung bei einem LOW-Signal (Sensor erkennt Hindernis) solange hochzählt, bis wieder ein HIGH-Signal erkannt wird.
Damit nun die Wagen genau eingezählt werden, müsste eigentlich die negative Flanke - also der Wechsel von HIGH auf LOW - gezählt werden. Zum auszählen am Ende vom Block benötige ich im Gegensatz dazu dann die positive Flanke - also den Wechsle von LOW zu HIGH, wenn der Wagen vollständig den Sensor verlassen hat.
Wie stelle ich dies am Besten an?

Vielen Dank schonmal für eure Antworten, Ideen oder auch Verbesserungsvorschläge.

Viele Grüße

Mika

PS: Anbei auch der Prototypencode.

// Deklaration der Sensor_Eingangspinnne
int Sensor_Block1_2 = 10;
int Sensor_Block2_1 = 11;
int Sensor_Block2_2 = 12;
int Sensor_Block3_1 = 13;
	
// Deklaration der Blockzaehler
int Zaehler_Block1 = 0;
int Zaehler_Block2 = 0;
int Zaehler_Block3 = 0;

// Deklaration des Blockmodus
// 0 => Block frei
// 1 => Block belegt als Regelgleis
// 2 => Block belegt als Gegengleis
int Modus_Block1 = 0;
int Modus_Block2 = 0;
int Modus_Block3 = 0;

/*
IDEEN: 
Speichern des Betriebszustandes zum Ende und automatisches Lesen bzw. Laden zu Anfang
Speichern mittels Taster auslösen
*/

void setup() {
  Serial.begin(9600); // Initialisierung serielle Ausgabe
// Initialisierung Sensorpinne
  pinMode (Sensor_Block1_2, INPUT);
  pinMode (Sensor_Block2_1, INPUT);
  pinMode (Sensor_Block2_2, INPUT);
  pinMode (Sensor_Block3_1, INPUT); 
}

// Das Programm liest den aktuellen Status des Sensor_Pins aus und 
// gibt in der seriellen Konsole aus, ob ein Hindernis aktuell erkannt wird 
// oder ob kein Hindernis sich vor dem Sensor befindet

void loop() {
  // Die gegenwaertigen Signale der Sensoren werden ausgelesen
  bool val1_2 = digitalRead (Sensor_Block1_2);
  bool val2_1 = digitalRead (Sensor_Block2_1);
  bool val2_2 = digitalRead (Sensor_Block2_2);
  bool val3_1 = digitalRead (Sensor_Block3_1);

//Nur Block 2 zum Test programmiert; alle Blöcke _ bis auf Variablenbezeichnung _ identisch
//Grundzustand = HIGH

//Pegelauswertung zu Testzwecken  
/*
  if (val2_1 == HIGH) {
    Serial.println("2_1 (11) High");
  }
  else {
    Serial.println("2_1(11) Low");
  }
  
  if (val2_2 == HIGH) {
    Serial.println("2_2 (12) High");
  }
  else {
    Serial.println("2-2 (12) Low");
  }  
*/  

// Frei-Meldung, wenn keine aktive UND vorhergegangene Beeinflussung  
if (val2_1 == HIGH && val2_2 == HIGH && Zaehler_Block2 == 0 ){
  Modus_Block2 = 0;
  //Serial.println("Block frei");
}

// Belegt-Meldung als Regelgleis eines freien Blocks
if (val2_1 == LOW && val2_2 == HIGH && Zaehler_Block2 == 0 && Modus_Block2 == 0){
  Modus_Block2 = 1;
  //Serial.println("Block als Regelgleis belegt");
  Zaehler_Block2++;
  Serial.println(Zaehler_Block2);
}

// Belegt-Meldung als Gegengleis eines freien Blocks
if (val2_1 == HIGH && val2_2 == LOW && Zaehler_Block2 == 0 && Modus_Block2 == 0){
  Modus_Block2 = 2;
  //Serial.println("Block als Regelgleis belegt");
  Zaehler_Block2++;
  Serial.println(Zaehler_Block2);
}

// Zählen der weiteren Fahrzeuge bei Fahrt in Regelrichtung
if (val2_1 == LOW && Zaehler_Block2 != 0 && Modus_Block2 == 1){
  Zaehler_Block2++;
  Serial.println(Zaehler_Block2);
}

// Zählen der weiteren Fahrzeuge bei Fahrt in Gegenrichtung
if (val2_2 == LOW && Zaehler_Block2 != 0 && Modus_Block2 == 2){
  Zaehler_Block2++;
  Serial.println(Zaehler_Block2);
}

// Ausbuchen der Fahrzeuge in Regelrichtung
if (val2_2 == LOW && Zaehler_Block2 != 0 && Modus_Block2 == 1){
  Zaehler_Block2--;
}

// Ausbuchen der Fahrzeuge in Gegenrichtung
if (val2_1 == LOW && Zaehler_Block2 != 0 && Modus_Block2 == 2){
  Zaehler_Block2--;
}

//Ausgabe des Blockmodus als Text im Testbetrieb
if (Modus_Block2 == 0){
  Serial.println("Block 2 frei");
}
if (Modus_Block2 == 1){
  Serial.println("Block 2 belegt; Modus: Regelgleis");
}
if (Modus_Block2 == 2){
  Serial.println("Block 2 belegt; Modus: Gegengleis");
}
Serial.println("Fahrzeuge in Block2:");
Serial.println(Zaehler_Block2);
Serial.println("--------------------------");
delay(500);

}

ich denke das kann man in einem Verfahren wie bei Encoder bestimmen: eine/zwei Lichtquelle/n und zwei Empfänger. Der Zug aktiviert eine Lichtschranke zuerst, so kann man Belegrichtung erkennen. Wenn keine Aktivierung 5 Sekunden lang, dann "Block frei".

Hallo
ich denke Du benötigst für jeden Sensor beide Flanken. Letztlich empfehle ich Dir die MobaTools , das ist eine spezielle Lib für Modellbauer. Der Autor ist hier Mitglied. Du wirst ja auch mehrere Abschnitte haben und somit das Ganze mehrfach benötigen. Damit machen Objekte Sinn.

Wenn Du das mit realen Tastern testen willst musst Du die Eingänge entprellen. Ich hab das in meinem Beispiel mit einem delay gemacht um es zu vereinfachen. Wenn Du letztlich einen Sensor mit Halbleiterausgang nimmst wird wahrscheinlich nicht nötig sein.

mal ganz einfach gehalten könnte das so aussehen

const byte sensorpin = 2;
bool sensorStatus;     // Status des Einganges
bool merker;          // Status merken 
bool flankeL;         //  Signal fallende Flanke erkannt
bool flankeH;       // Signal steigende Flanke erkannt

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(sensorpin, INPUT_PULLUP);

}

void loop() {
  // put your main code here, to run repeatedly:

  sensorStatus = !digitalRead(sensorpin); // Eingang einlesen und invertieren
  if (sensorStatus && !merker) { // Wenn Sensor H und merker L
    merker = true;
    flankeH = true;
    Serial.println("pos Flanke");
  }
  else flankeH = false;

  if (!sensorStatus && merker) { // Wenn Sensor L und merker H 
    merker = false;
    flankeL = true;
    Serial.println("neg Flanke");
  }
  else flankeL = false;

  delay(50);// schmutziges entprellen

}

Für STinkercad äh sorry tinkercad braucht man einen Account.
Beim WOKWI-Simulator braucht man das nicht.

vgs

Bei IR Sensoren wird üblicherweise eine blinkende (schell flackernde) LED benutzt, um das Umgebungslicht wirksam auszublenden, Bei vielen gleichzeitig zu überwachenden Lichtschranken stelle ich mir das etwas problematisch vor. Oder verwendest Du schon die handelsüblichen IR Empfänger für 38 kHz?

Dann müssen beim Durchfahren eines Zuges sowohl die Lichtschranke des verlassenen als auch die des betretenen Blocks gleichzeitig gezählt werden. Mir erscheint das etwas zu viel Aufwand.

Schau Dir doch mal dieses Teil bei Reichelt an TLE4905

Die Belegrichtungserkennung habe ich durch die Und-Verknüpfungen realisiert. Das habe ich geprüft, in welchem Zustand sich der Block befindet. Ist er frei und Sensor 1 spricht zu erst an, dann wird in Regelrichtung gefahren; bei Sensor zwei als erstes entsprechend in die Gegenrichtung. Der Block wird dann, solange der Fahrzeugzähler = 0 ist, in dem entsprechenden Modus gehalten.

Ich hätte es glaube ich von Anfang an hier rein schreiben sollen:
Der verwendete Sensor hat bei Reichelt die Artikelnummer ARD LINE FINDER2

Bis auf ein zwei Stellen sind die Blockabstände länger als die maximale Zuglänge. Ich denke, dass ich dort dann ein Erkennung für dieses Szenario programmieren werde, welche dafür sorgt, dass die Sensoren in der Mitte für diesen Moment in aktiv werden.

Bitte entschuldige mein evtl. dumme Frage:
Aber was genau bringen mir die Lib und die Objekte? Bitte nicht falsch verstehen: Ich meine die Frage ehr wie "Was genau macht die Lib und was machen Objekte?".

Ich habe bei meinen Suchen, bevor ich frage gestellt habe, schon ein paar mal vom entprellen gelesen. Was ist das denn?

Es mag vielleicht gerade etwas komisch wirken und ich bitte um Nachsicht, aber könntest du mir den Code bitte einmal kurz im groben erklären?
Ich bin mir sicher, dass ich bald über diese Frage lachen kann, aber momentan stehe ich in Bezug auf C noch ganz am Anfang und freue mich über alles was ich lernen kann.

Vielen Dank schonmal im Voraus und auch vielen Dank für alle bisherigen und noch kommenden Antworten!

Viele Grüße

MIka

Warum verlinkst Du das Angebot oder noch besser das Datenblatt nicht direkt?

Ja auch gut. Nur mit einem Hallsensor und einem Nydym unter den Waggons hättest Du auch die Anzahl der Waggons .

Hallo,
ich versuche Dir mal einfach zu antworten. Die Spezis hier mögen mir verzeihen. :wink:

Zum entprellen:
Bei mechanische Schaltern kann es passieren das der Kontakt beim schließen mehrfach ganz schnell hintereinander mal kurz wieder aufmacht und wieder schließt. Wenn der Controller nun sehr schnell ist bekommt er das mit und wenn dann z.B mit dem Signal etwas gezählt werden soll kann es passieren das der Zählerstand springt. Gleiches passiert wenn man mit einem Taster etwas einschalten will und mit erneutem drücken wieder aus. (toggel ) Wenn man mit einem Schalter nur etwas einschalten will spielt das keine Rolle.

Objekte
Du benötigst mehrfach gleiche Funktionalität. Von mehreren Sensoren willst Du die Eingänge eventuell entprellen und beide Flanken erkennen. Dazu kann man die Programmteile kopieren und die Variablen ändern damit sie nicht gleich sind und doppelt vorkommen . Man kann das auch mit Arrays lösen.
Oder man verwendet Objekte von einer Klasse. Dabei stellt die Klasse bestimmte "Funktionen" zur Verfügung. Von der Klasse kann man beliebig viele "Kopien" mit unterschiedlichen Namen anlegen und diese dann verwenden. Funktionen nennt man dann Methoden und Kopien Objekte.
In den MobaTools wirst Du Methoden finden die entprellen und Flanken erkennen. Damit musst Du das nicht mehr selbst programmieren.

Es gibt gute C++ Bücher die das genauer erklären. Oder Du schaust Dir mal meinen Senf dazu an
Von delay() bis zur Methode

Mit meinem kleinen Sketch wollte ich Dir zeigen wie man beide Flanken eines Einganges erkennen kann das also selber machen kann.

Heinz

Ich frag mal, wie Du Dir das mit dem Sensor vorstellst.
Sollen die nur ein- und ausfahren feststellen oder auch die Wagenanzahl?
Ist sichergestellt, das die Kupplungen nicht in der Auswertung stören?

Bis dahin gehe ich mal auf Deinen Code ein.
Gebe Deinen Variablen Namen, die man versteht.
Benutze keine magischen Zahlen! Wenn Du zählen willst, dann benutze Arrays.
Array sind nicht schwer zu verstehen.
Wenn Du dann noch das, was zusammengehört auch zusamaamenfasst, kommst Du irgendwann auf ein Struct und dort ggfls. auf array of struct - aber das ist dann schon hohe "Kunst" und ich habe mich auch sehr schwer damit getan.

Deine Bedingungen brechen Dir irgendwann das Genick.
Du wirst entweder eine Bedingung nicht erfasst haben, oder eine Bedingung durch eine andere ausschliessen.

Für Deine Serillen Ausgaben "Frei" / "Belegt" bau Dir Funktionen, damit die Ausgabe nur kommt, wenn der Zustand eintritt.
Im Moment floodest Du den seriellen Monitor.
Da siehst Du nix.

Gewöhne Dir von Anfang an, das F()-Makro zu benutzen.
Alt:
Serial.println("Block 2 frei");
Neu:
Serial.println(F("Block 2 frei"));

Unterstriche in Variablen... Bin ich ein Gegner von.
Wichtiger so schreiben, das fortlaufend lesbar.
Aus Zaehler_Block wird dann zaehlerBlock

Werte, die nicht negativ werden können, unsigned machen.
Pins brauchen kein int. Denen reicht ein byte.
Und da die sich nicht verändern können, mach sie const.
Alles was ich geschrieben habe, wird dann:
const byte sensorBlockPin[] = {10,11,12,13};

Da gibts noch viel mehr...
Aber denk drüber nach, ob Du die eine oder andere Konvention übernimmst und das anderen einfacher machst da mitzulesen.

3 Likes

Es sollen sowohl Ein- und Ausfahrten erfasst als auch die Fahrzeuganzahl gezählt werden. Um die Kupplungen nicht mikt zu zählen, habe ich mir überelgt, den Sensor aus der Gleismitte raus zu montieren.

@Rentner Vielen, vielen Dank für die Erklärungen und deine Tutorial. Ich findes es wirklich sehr gut beschrieben und sehr aufschlussreich :smiley: Auch nochmal vielen Dank für deinen Code. In deinen if-Abfragen hast du ja nur zwei zwei Status mit UND verknüpft, aber keinen Sollszustand bzw. Vergleich angestellt. Ich bin mir ziemlich sicher, dass du den nciht vergessen hast; aber was hat es damit auf sich?
Ich werde jetzt auch mal die MoBa Tools runterladen.

@my_xy_projekt Auch dir vielen lieben Dank für deine Rückmeldung und deine Verbesserungsvorschläge. Ich werde mich jetzt mal mit den Arrays beschäftigen (hab mir mit dem Arduino zusammen direkt noch zwei Bücher zur Erklärung gekauft; sollte morgen ankommen) und auch die anderen Vorschläge in meinen Code übernehmen.
Bei den Bedingungen werde ich nochmal überlegen, ob und wie ich diese verkürzen kann.

Was genau meinst du hier mit dem Pin[] oder ist das eine Art Platzhalter? Wie greife ich die einzelnen Pinne in der Abfrage dann wieder auf?
Wenn ich es richtig verstanden habe, sorgt das F()-Makro dafür, dass der Text über den Programmspeicher und nicht über den RAM läuft, oder?
MIt der von dir gezeigten Pindeklaration benötige ich ja viel weniger Zeilen :flushed: Ich werde das definitiv übernehemn, da ich auch denke, dass es so für alle einfacher den Code bei Fragen oder Problemen zu lesen.

Viele Grüße

Mika

Und das bei Spur N ? wie willst Du diese denn verstecken? Einen Hallsensor baust Du ins Gleisbett ein und siehst fast nichts von diesem.

Ich würde ein Loch in das Schwellenband und den Oberbau bohren und im Anschluss den Sensor von unten montieren. Im nicht sichtbaren Bereich erfolgt die Montag seitlich.

Ein Hallsensor würde doch nur in Kombination mit einem Magneten funktionieren, oder?

Ja richtig. Aber besser wäre es wenn Du dich hier im Forum an die Aktiven Modellbahner wenden würdest.

Bei Loks und Triebzügen bzw. Zügen mit fester Wagenreihung wäre das ja kein Problem.
Da ich aber auf der Anlage auch rangiere und so immer neue Wagenreihungen habe, müsste ja unter jeden Wagen ein Magnet gebaut werden. Oder war genau das deine Idee?
Welche Magneten und Sensoren würdest du empfehlen?

Wie schon geschrieben den TLE7905 und wenn möglich Neodym dann noch nach Datenblatt den 7905 beschalten und Du hast ein sauberes Rechtecksignal welches Du gut am Arduino oder Portexpandern weiterverarbeiten kannst.

In einigen Fällen kann man Magnete unschlagbar preiswert aus selbstklebenden Magnet-Haftleisten ausschneiden. Bei Bedarf kann man unterschiedlich lange Streifen für mehrfache Flußwechsel beim Überfahren einsetzen, um Zusatzinformationen über den jeweiligen Wagen oder Lok zu übertragen.

Für den Einsatz muß dann der Hall-Sensor ausreichend empfindlich sein, um den Abstand zwischen Magnetstreifen und Sensor zu überbrücken.

1 Like

Ich fang mal zwischendrin an :slight_smile:

bei Dir hast Du als Kommentar:

Ich plädiere dafür der Variablen oder in diesem Fall der Konstanten schon im Namen mitzugeben, was das ist.
Das wirst Du gleich sehen:

Stell Dir das Array als Schrank mit (hier) 4 Schubladen vor.
Im Sprachgebrauch Elemente.
Jedes Element hat eine eindeutige Nummer.
Es gibt nur eine Besonderheit.
Das erste Element wird nicht mit 1 sondern mit 0 gezählt.
Der Zugriff erfolgt dann über die Position im Array.
Dein digitalRead(Sensor_Block1_2); wäre dann: digitalRead(sensorBlockPin[0]);
Für digitalRead(Sensor_Block2_1) äquivalent: digitalRead(sensorBlockPin[1]);
Durch das mögliche durchzählen der Elemente lässt sich das später u.a. sehr schön mit einem Dreizeiler für alle Pins abhandeln.

Ich habe die Vermutung, das ganz viel davon zusammengefasst werden könnte.
Aber dazu bräuchte ich etwas Zeit um Deine Idee dahinter zu verfolgen :wink:
Na mal schauen, wie sich das entwickelt..

Hallo,

Du beziehst Dich z.B auf die Zeile

if (sensorStatus && !merker) 

das ist identisch mit

if (sensorStatus && !merker ==true) 

in beiden Fällen ist das was innerhalb der Klammer steht erfüllt, (wahr) also true, HIGH, oder 1 was immer Du verwenden willst.

Wenn Du z.B sowas hast
If( a==123)

steht ja auch nur ein true in der Klammer wenn a =123 ist ansonsten ein false :wink:

ich finde das einfacher zu lesen und ich vergesse das zweite" = "nicht, ok bei numerischen Abfragen gehts halt nicht.
Heinz