Modellbahn-Basteleien

Auf Anraten ein neues (schon mal dagewesenes) Thema. Ich habe es in "zeigt her eure..." begonnen und setze es hier ein, da es wohl interessante Diskussionen gibt.
Also, Grundprinzip: 2 Kreise mit je einem Hallsensor bestückt. Funktion: Hall 1 lässt Lok 1 langsam bremsen, kurze Pause, Lok 2 fährt langsam in andere Richtung los. Hall 2 macht es dann umgekehrt. Ich habe mich am millis (Hurra, mit Erfolg) probiert. Habe auch den Weg gewählt die Aktionen der Halls in eigene Funktionen zu packen. Mich interessiert eure Meinung und wo ich was kürzer, effizienter, eleganter... machen kann. Ich bin gerade dabei eine kleine Anlage auf Nanosteuerung umzustellen (belebte Häuser, Weichenschaltung) und eben automatisierte Abläufe.
Hier der Sketch

/*
  DC-Motorsteuerung mit L298N zum Regeln
  von Anfahr- und Bremsverhalten mit 2 Hallsensoren,
  mit Pause  zwischen dem Brems- und Anfahrstart
  von Schorsch für ZFI
  23.05.21
*/

// Definition Hallpins und Hallstatus
const int hallSensorPin = 2;
const int hallSensorPin2 =3;
int hallStatus = LOW;     // 1. Hall aktiv
int hallStatus2=HIGH;  // 2. Hall paasiv

// Gleichstrommotor 1
int GSM1 = 10;
int in1 = 9;
int in2 = 8;

// Gleichstrommotor 2
int GSM2 = 5; //IN B
int in3 = 7; // IN 3
int in4 = 6;  // IN 4
unsigned long anfang = 0;
const long warte = 2000; //Pause zwische M1 und M2 im Loop
void setup()
{
  Serial.begin(9600);
  pinMode(GSM1, OUTPUT);    
  pinMode(GSM2, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
pinMode(hallSensorPin, INPUT);
pinMode(hallSensorPin2, INPUT);
if (hallStatus ==LOW) 
  {                        // Beschleunigen M1 als Zustand nach Einschalten
  for (int i = 0; i <=240; i += 20) //in 40-iger Schritten bis Maximalgeschwindigkeit
    { 
    digitalWrite(in1, HIGH);  
    digitalWrite(in2, LOW);   // Motor 1 beginnt zu rotieren
    analogWrite(GSM1, i); // Motor 1 beschleunigt 
    delay(100);
    }
  }
}
void HALL1A() // Funktion für Hallsensor1
{
// Bremsen M2
  if (hallStatus ==LOW) 
  {
  for (int i = 240; i >=0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
    { 
    digitalWrite(in3, HIGH);  
    digitalWrite(in4, LOW);   // Motor 2 beginnt zu rotieren
    analogWrite(GSM2, i); // Motor 2 bremst
    delay(500);
    }
  }
// Beschleunigen M1
   if (hallStatus ==LOW) 
   {
    anfang =millis();
    while (millis()-anfang < warte); //nach dem Bremsen M1 warten=Halt, dann 
    for (int i = 0; i <=240; i += 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
      { 
    digitalWrite(in1, HIGH);  
    digitalWrite(in2, LOW);   // Motor 1 beginnt zu rotieren
    analogWrite(GSM1, i); // Motor 1 beschleunigt 
    delay(100);
      }
   }
}
void HALL1B() //Funktion für Hallsensor 2
{
// Bremsen M1
 if (hallStatus2 ==LOW) 
 {
  for (int i = 240; i >=0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
    { 
    digitalWrite(in1, HIGH);  
    digitalWrite(in2, LOW);   // Motor 1 beginnt zu rotieren
    analogWrite(GSM1, i); // Motor 1 bremst
    delay(500);
    }
  }
  anfang =millis();
  while (millis()- anfang < warte);
// Beschleunigen M2
  if (hallStatus2 ==LOW) 
  {
  for (int i = 0; i <=240; i += 40) 
    { //in 40-iger Schritten bis Maximalgeschwindigkeit
    digitalWrite(in3, HIGH);  
    digitalWrite(in4, LOW);   // Motor 2 beginnt zu rotieren
    analogWrite(GSM2, i); // Motor 2 beschleunigt 
    delay(100);
    }
  } 
}
 //=============================
void loop()
{
  hallStatus=digitalRead(hallSensorPin);
  hallStatus2=digitalRead(hallSensorPin2);
 HALL1A();
 HALL1B(); 
}

Was fest vergeben wird als const definieren.
Was nur zwei Zustände hat als bool definieren.
Was als byte geht als byte definieren.
Vermeide mischen von Typen (long / unsigned long) Da wo es geht versuchen immer unsigned einzusetzen.
Kommentare sollen beschreiben zu was das da ist oder was das machen soll.
Variablen sollten nach Möglichkeit beschreibende Namen haben. GSM1 kann ich mir auch als GleiSMelder ausdenken, aber andere denken da vielleicht an das Simkartenmodul SIM700L. :wink:

Das ist was schönes um darus ne Struktur zu machen... Da wirst noch viel Spass haben...

Hier mal die Zuweisungen.

bool hallStatus = LOW;     // 1. Hall aktiv
bool hallStatus2=HIGH;  // 2. Hall paasiv

// Gleichstrommotor 1
const byte GSM1 = 10;
const byte in1 = 9;
const byte  in2 = 8;

// Gleichstrommotor 2
const byte GSM2 = 5; //IN B
const byte in3 = 7; // IN 3
const byte in4 = 6;  // IN 4
unsigned long anfang = 0;
const unsigned long warte = 2000; //Pause zwische M1 und M2 im Loop

Und wenn der Threadowner nun mal den kompletten Sketch entsprechend anpasst und auch mal STRG-T in der IDE drückt, dann könnte man sich in dieser Reihenfolge vorantasten

  • struct
  • Array
  • keine blockierenden Abschnitte (keine delay...)
  • Vereinfachung auf OOP

Vieleicht noch ein kleiner optischer Hinweis, ändere doch den Titel im ersten Post auf "Modellbahn Basteleien" - erstens muss MoBa bei den Arduinern nicht so als Abkürzung bekannt sein, und wenn doch, verbindet das der Insider Arduiner eher mit den "MoBa" Tools - und die verwendest du ja noch nicht in deinem Sketch.

So, habe korrigiert. Titel und Variablentypen. STRG+T zum Formatieren und danke für die Hinweise. Habe mich nochmal zu den Variablentypen belesen, um nachzuvollziehen, wozu das Ganze. Macht jetzt mehr Sinn.
Zum GSM-Namen - da steht es eigentlich als Kommentar drüber :face_with_hand_over_mouth:
Abgesehen davon, ist denn das Auslagern der Hall-Funktionen in eigene Funktionen sinnvoll? Meine Überlegung war dabei, dass ich so leichter Änderungen vornehmen kann, als wenn das im Loop direkt steht. Außerdem hatte ich das Gefühl, dass es so strukturierter wirkt.
Hier nochmal der korrigierte Sketch:

/*
  DC-Motorsteuerung mit L298N zum Regeln
  von Anfahr- und Bremsverhalten mit 2 Hallsensoren,
  mit Pause  zwischen dem Brems- und Anfahrstart
  von Schorsch für ZFI
  23.05.21
*/

// Definition Hallpins und Hallstatus
const int hallSensorPin = 2;
const int hallSensorPin2 = 3;
bool hallStatus = LOW;     // 1. Hall aktiv
bool hallStatus2 = HIGH; // 2. Hall paasiv

// Gleichstrommotor 1
const byte GSM1 = 10;
const byte in1 = 9;
const byte in2 = 8;

// Gleichstrommotor 2
const byte GSM2 = 5; //IN B
const byte in3 = 7; // IN 3
const byte in4 = 6;  // IN 4
unsigned long anfang = 0;
const unsigned long warte = 2000; //Pause zwische M1 und M2 im Loop
void setup()
{
  Serial.begin(9600);
  pinMode(GSM1, OUTPUT);
  pinMode(GSM2, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  pinMode(hallSensorPin, INPUT);
  pinMode(hallSensorPin2, INPUT);
  if (hallStatus == LOW)
  { // Beschleunigen M1 als Zustand nach Einschalten
    for (int i = 0; i <= 240; i += 20) //in 40-iger Schritten bis Maximalgeschwindigkeit
    {
      digitalWrite(in1, HIGH);
      digitalWrite(in2, LOW);   // Motor 1 beginnt zu rotieren
      analogWrite(GSM1, i); // Motor 1 beschleunigt
      delay(100); // delay(100) steuert die Geschwindigkeit des Hochzählens im for
    }
  }
}
void HALL1A() // Funktion für Hallsensor1
{
  // Bremsen M2
  if (hallStatus == LOW)
  {
    for (int i = 240; i >= 0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
    {
      digitalWrite(in3, HIGH);
      digitalWrite(in4, LOW);   // Motor 2 beginnt zu rotieren
      analogWrite(GSM2, i); // Motor 2 bremst
      delay(500);
    }
  }
  // Beschleunigen M1
  if (hallStatus == LOW)
  {
    anfang = millis();
    while (millis() - anfang < warte); //nach dem Bremsen M1 warten=Halt, dann
    for (int i = 0; i <= 240; i += 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
    {
      digitalWrite(in1, HIGH);
      digitalWrite(in2, LOW);   // Motor 1 beginnt zu rotieren
      analogWrite(GSM1, i); // Motor 1 beschleunigt
      delay(100);
    }
  }
}
void HALL1B() //Funktion für Hallsensor 2
{
  // Bremsen M1
  if (hallStatus2 == LOW)
  {
    for (int i = 240; i >= 0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
    {
      digitalWrite(in1, HIGH);
      digitalWrite(in2, LOW);   // Motor 1 beginnt zu rotieren
      analogWrite(GSM1, i); // Motor 1 bremst
      delay(500);
    }
  }
  anfang = millis();
  while (millis() - anfang < warte);
  // Beschleunigen M2
  if (hallStatus2 == LOW)
  {
    for (int i = 0; i <= 240; i += 40)
    { //in 40-iger Schritten bis Maximalgeschwindigkeit
      digitalWrite(in3, HIGH);
      digitalWrite(in4, LOW);   // Motor 2 beginnt zu rotieren
      analogWrite(GSM2, i); // Motor 2 beschleunigt
      delay(100);
    }
  }
}
//=============================
void loop()
{
  hallStatus = digitalRead(hallSensorPin);
  hallStatus2 = digitalRead(hallSensorPin2);
  HALL1A();
  HALL1B();
}

Das wird noch besser.
Die Variablendeklaration für den Hallstatus brauchst Du nicht da, wo sie jetzt ist.

Wenn Du die nur in den Funktionen abfragst, dann am Beispiel von einem Zweig:

void HALL1A() // Funktion für Hallsensor1
{
  bool hallStatus = digitalRead(hallSensorPin);
  // Bremsen M2
  if (hallStatus == LOW)
  {
    for (int i = 240; i >= 0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
    {
      digitalWrite(in3, HIGH);
      digitalWrite(in4, LOW);   // Motor 2 beginnt zu rotieren
      analogWrite(GSM2, i); // Motor 2 bremst
      delay(500);
    }
  }
  // Beschleunigen M1
  if (hallStatus == LOW)
  {
    anfang = millis();
    while (millis() - anfang < warte); //nach dem Bremsen M1 warten=Halt, dann
    for (int i = 0; i <= 240; i += 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
    {
      digitalWrite(in1, HIGH);
      digitalWrite(in2, LOW);   // Motor 1 beginnt zu rotieren
      analogWrite(GSM1, i); // Motor 1 beschleunigt
      delay(100);
    }
  }
}

Wobei mir noch nicht klar ist, warum Du beides auf low abfragst.
Und dann hauen Dir die ganzen delays die Füsse weg.

Tipp von mir: Nochmal die Nachtwächtererklärung BlinkwithoutDelay - Die Nachtwächtererklärung und das BlinkWithoutDelay-Beispiel nehmen.

Mir sieht das sehr nach einer Schrittkette aus.
Ich hab nur noch nicht verstanden, ob die beiden (hall1a / hall1b) zusammengehören oder getrennt zu betrachten sind.

die Grundidee von Funktionen ist schon in Ordnung. Aber wenn du dir deinen code ansiehst, findest du nicht dass sich die zwei Funktionen sehr ähneln und sich nur in den verwendeten Variablen unterscheiden sonst aber das gleiche machen?

Wenn dir das auch Verbesserungswürdig erscheint, dann gib bescheid.

(P.S.: schau dir noch mal die Variablengröße deiner hallSensorPinX an...)

Hallo,
ich hab jetzt nicht die Ahnung von Modellbahn und was da so benötigt wird. Ich würde das auch in eine Schrittkette packen, wobei mir derzeit nicht klar ist wann denn die erste Lok wieder losfahren kann. Eventuell ein 3. Sensor auf dem Gleis der Lok2 wenn diese die Weiche wieder freigegeben hat , oder halt einfach nach einer Wartezeit. (Anschlusszug im Bahnhof)
Schrittkette

  1. warten auf Sensor1
  2. Lok 1 bremsen
  3. Wartezeit
  4. Lok2 beschleunigen
  5. (optional) Wartezeit
  6. zurück auf 1.

für Beschleunigen und bremsen gibt´s eine Funktion mit den Parametern für die Pins und die "Rampenzeit" und Beschleunigen/Bremsen Als Rückgabewert der Funktion ein bool "Rampe fertig. Das delay() ersetzen durch millis()

bool rampe (byte pinOn, byte pinR_L, byte pinSpeed, uint32_t rampZeit, bool beschl)

Zudem würde ich die Pins 1,2,3,4 mit Namen versehen. z.B

const byte pinLok1On =1;
const byte pinLok1Vor=2;

Wenn man nun das ganze mehrfach benötigt bietet sich auch eine Classe für die rampe an.
Heinz

alles gekauft, aber dennoch würde ich einen Anfänger in diesen Schritten zum Thema führen:

Schritt:
0 Variablen Zusammenräumen
1 Arrays, For Schleifen, sizeof
2 Strukturen
3 ein wenig Ordnung bringen in den Ablauf - die finite State Machine
4 von der einfachen Strucktur zur Objektorientierten Programmierung

schön langsam und eins nach dem anderen, wenn der TO so weit ist...

@my_xy_projekt: Das mit dem Verlagern der Variablen für den Hallstatus erschließt sich mir nicht. bringt das, Speicherplatz?? LOW-Abfrage: weil bei LOW durchgeschaltet wird, also wenn der Magnet den Hallsensor passiert. Das ist ja nur ein kurzer Moment und reicht zum Auslösen der Folgeaktionen. Die delays steuern nur das Tempo des Hochzählens/Runterzählens für das Anfahren/Bremsen. Beim Testen ist mir nichts auf die Füße gefallen, geht einfach nur. Hall1A und Hall1B sind getrennt zu betrachten. Das sind quasi "Kurz-Taster".
@Rentner Heinz
es gibt keine Weiche, stell dir 2 Gleisovale vor. Da gibt es einen Bahnhofsbereich. In den fährt ein Lok von rechts und eine von links ein. Ohne alles würden die einfach nur kreiseln. Jetzt ist aber vor jeder Einfahrt pro Kreis in Fahrtrichtung ein Hallsensor im Gleis verbaut. Magnet unter der Lok1 löst das Low kurz aus. diese beginnt zu bremsen bis zu Halt, dann wird der Hallstatus des anderen Gleises auf Low gesetzt und Lok2 beschleunigt. Irgendwann kommt sie über dem Hallsensor2 vorbei, fährt bremsend bis Halt in ihren Bahnhofsbereich und Gleis1 (Hallsensor 1) wird wieder Low, Lok 1 beschleunigt...
Klar kann man das mit Rampe machen, oder auch map. Ich habe das for gewählt, weil ich darin über die Schrittweite (+40 oder anderes) bequem die Anfahr/Bremsgeschwindigkeit regeln kann. Die Fahrspannung ist ja konstant. Ich muss dazu sagen, das ich in Spur Z unterwegs bin. Die kleinen Loks reagieren alle ganz unterschiedlich auf Spannungsänderung. So kann ich bei Lokwechsel schneller die "Empfindlichkeit" anpassen. Ach, die Pins habe ich so benannt, weil sie genauso auf dem L298N-Treibermodul beschriftet sind, erleichtert das verkabeln :face_with_raised_eyebrow:
@noiasca
ganz ehrlich? von den 5 Punkten habe ich den Punkt 0 und 5 verstanden.
Aber die Hilfe für eine Vereinfachung der Funktionen ("gib Bescheid") nehme ich dankbar an.
Und an alle: Das ganze ist eine Tischanlage, der Gleisplan sieht verwirrend aus, ist im Endergebnis aber nur 2 getrennte Gleisreise., Wer will guggt hier:
https://papperlapapp220.de/Modelle/Anlagen/wild,-wild-West/

Hi,
es geht nicht um den Speicherplatz, sondern um die Gültigkeit der Variablen.
Nur da wo sie tatsächlich gebraucht werden, diese auch deklarieren und verwenden.
Spart Dir später suchen, wenn die versehentlich an anderer Stelle verändert werden.
Was beim LOW passieren soll, habe ich schon verstanden. Nur eben nicht warum Du den Status 2mal abfragst.
Einmal LOW ist eben LOW. ein zweites if braucht es da nicht.
Das mit den delay() fällt Dir irgendwann auf die Füsse, weil Du in der Zeit nichts anderes machen kannst.

Ansonsten bin ich noch immer dabei das als Schrittkette zu machen und nicht die ganzen Bedingungen verketten.

Die Liste dient ja nur dazu einen Fahrplan zu haben, den man gehen kann.
Übrigens die Liste hat zwar 5 Punkte aber sie geht nur von 0 bis 4.
Das ist der erste große Punkt den du nachfolgend verstehen musst:
C++ beginnt beim Zählen mit 0 ... (nicht mit 1).

So jetzt empfehle ich erst mal zu lesen was Arrays sind:
https://www.arduino.cc/reference/de/language/variables/data-types/array/

Kompliziertes Thema.
Dann einfach noch mal:
https://www.arduino.cc/reference/de/language/variables/data-types/array/

Immer dann wenn du versuchst, Variablen durchzunummerieren, dann "schreit" das förmlich nach Arrays.

Angewandt auf deinen Sketch würde das in etwa so aussehen:

/*
  DC-Motorsteuerung mit L298N zum Regeln
  von Anfahr- und Bremsverhalten mit 2 Hallsensoren,
  mit Pause  zwischen dem Brems- und Anfahrstart
  von Schorsch für ZFI

  Schritt:
  0 Variablen Zusammenräumen
  1 Arrays, For Schleifen  <--
  2 Strukturen
  3 ein wenig Ordnung bringen in den Ablauf - die finite State Machine
  4 von der einfachen Strucktur zur Objektorientierten Klasse
*/

const byte hallSensorPin[] = {2, 3};  // Definition Hallpins und Hallstatus
bool hallStatus[] = {LOW, LOW};       // Hall aktiv oder inaktiv
const byte gsm[] = {10, 5};           // Pin für Gleich Strom Motor  - Variablen beginnen immer klein
const byte in1[] = {9, 8};            // Pin zum L298N
const byte in2[] = {7, 6};            // Pin zum L298N

const size_t anzMotoren = sizeof(gsm); // wir lassen uns die größe des Byte arrays der gsm 

unsigned long anfang = 0;
const unsigned long warte = 2000; //Pause zwische M1 und M2 im Loop

void setup()
{
  Serial.begin(9600);
  for (size_t i = 0; i < anzMotoren; i++)
  { 
    pinMode(gsm[i], OUTPUT);
    pinMode(in1[i], OUTPUT);
    pinMode(in2[i], OUTPUT);
    pinMode(hallSensorPin[i], INPUT);
  }

  if (hallStatus[0] == LOW)
  { // Beschleunigen M1 als Zustand nach Einschalten
    for (int i = 0; i <= 240; i += 20) //in 40-iger Schritten bis Maximalgeschwindigkeit
    {
      digitalWrite(in1[0], HIGH);
      digitalWrite(in2[0], LOW);   // Motor 1 beginnt zu rotieren
      analogWrite(gsm[0], i); // Motor 1 beschleunigt
      delay(100); // delay(100) steuert die Geschwindigkeit des Hochzählens im for
    }
  }
}

void hall(byte bremser, byte anfahrer) // Funktion für Hallsensor1
{
  // den Bremser bremsen
  if (hallStatus[anfahrer] == LOW)
  {
    for (int i = 240; i >= 0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
    {
      digitalWrite(in1[bremser], HIGH);
      digitalWrite(in2[bremser], LOW);   // Motor bremser beginnt zu rotieren
      analogWrite(gsm[bremser], i); // Motor bremser bremst
      delay(500);
    }
  }
  // den Anfahrer anfahren
  if (hallStatus[anfahrer] == LOW)
  {
    anfang = millis();
    while (millis() - anfang < warte); //nach dem Bremsen warten=Halt, dann
    for (int i = 0; i <= 240; i += 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
    {
      digitalWrite(in1[anfahrer], HIGH);
      digitalWrite(in2[anfahrer], LOW);   // Motor beginnt zu rotieren
      analogWrite(gsm[anfahrer], i); // Motor beschleunigt
      delay(100);
    }
  }
}

//=============================
void loop()
{
  hallStatus[0] = digitalRead(hallSensorPin[0]);
  hallStatus[1] = digitalRead(hallSensorPin[1]);
  hall(0, 1);
  hall(1, 0);
}

wenn ich mich nicht vertippt habe, dann macht das das gleiche wie dein Sketch.
Vieleicht findest Fehler - dann bitte ausbessern und neu posten.

Der Sketch ist um gut 20 Zeilen kürzer.
Schön immer noch nicht, aber darum gehts nun noch nicht.
Aktuell gehts mir nur um die Arrays.

Und wenn du dich fragst, warum ich in1 und in2 nicht angegriffen habe ... nun ja, da bin ich der Meinung das ist eher ein direction und enable, egal, lassen wir mal die Beschriftung vom ersten Eingang...

Der Sinn von Funktionen ist eigentlich dass man keinen duplizierten Code hat. Wenn man also mehrmals das Gleiche macht, sollte man nicht einfach zwei Funktionen schreiben die jeweils nur andere Variablen/Konstanten verwenden. Sondern die verwendeten Werte als Parameter übergeben

z.B. vereinfacht:

void loop()
{
   schalten(in1, in2, GSM1);
}

void schalten(byte pin1, byte pin2, byte speed)
{
  ...
    digitalWrite(pin1, HIGH);    //es bringt eigentlich nichts das in einer for-Schleife immer wieder zu tun
    digitalWrite(pin2, LOW); 
    analogWrite(speed, i);
 ...
}

Das geht auch mit Variablen die in der Funktion verändert werden. Allerdings muss man diese unbedingt als Referenz übergeben wenn man den Wert beim nächsten Aufruf wieder braucht

@my_xy_projekt
Variablen - verstanden. Status abfrage 2 mal, auch. Zur Erklärung: Ich habe lange gesessen und "häppchenweise" probiert. Erst mal 1 Magnet und M1 soll beschleunigen. Dann das Bremsen ausprobiert, beides zusammengeführt und da haben wir den Salat, beim Kopieren rutscht das eben mal mit rein, grhhh. Wird korrigiert. Danke.
@noiasca
war mir klar, das mit 0 beginnen, hätte exakt schreiben müssen "den 1 .und 5. Punkt..." Aber recht hast du. Arrays habe ich schon angewendet, ich nutze das schon auf der Anlage für die Beleuchtung der Häuser. Da bot sich das an, weil ich da auch gleich Gruppen von Häusern befeuern konnte. Ich taste mich seit Weihnachten in die Materie rein, muss aber an der richtigen Stelle immer erst auf die Idee kommen. Da ist dein Hinweis mit "immer wenn nummeriert wird..." sehr praktisch. Ich werde meinen Sketch mal umbauen, aber schön laaangsam, soll ja funktionieren.
@Serenifly
Das probiere ich in der Kopie der Kopie der Kopie aus. Danke, Ideen braucht der Mensch

Danke, daß Du meinem Rat gefolgt bist.

Du könntest noch einen Link zu Deiner Projektvorstellung ergänzen.

Das freut mich, aber warum eigentlich? Dein Programm funktioniert doch und warum willst Du Zeit mit Programmieren verschwenden, wo Du doch so tolle Anlagen bauen kannst. Warum will der erfolgreiche Bildhauer plötzlich Maler werden? Was kannst Du mit Deinen bisherigen Fähigkeiten nicht realisieren, was fehlt Dir?

Am Ende kommt dann sowas raus: Anleitung: Weichensteuerung mit Klasse

Willst Du das? Daher meine Fragen.

Deine Westernstadt (Klasse übrigens!) hat ein Andreaskreuz mit einer speziellen Blinkfolge:

// 
// Modellbahn-Basteleien
const byte akreuzPin = 13;	// Pin für Andreaskreuz

void setup()
{
  pinMode(akreuzPin, OUTPUT);
}

void akreuz()
{
  digitalWrite(akreuzPin, HIGH);
  delay(500);
  digitalWrite(akreuzPin, LOW);
  delay(200);
  digitalWrite(akreuzPin, HIGH);
  delay(200);
  digitalWrite(akreuzPin, LOW);
  delay(200);
}

void loop()
{
  akreuz();
}

Also µC gekauft, LED angeschlossen und freuen!

Oder?

Du möchtest das Andreaskreuz in die Weichensteuerung integrieren? Dann muß sie blockadearm programmiert werden:

const byte akreuzPin = 13;	// Pin für Andreaskreuz
const uint32_t KURZ = 200, LANG = 500;
uint32_t jetzt = millis();  // aktuelle Zeit in ms nach Reset

void setup()
{
  pinMode(akreuzPin, OUTPUT);
}

void akreuz()
{
  static uint32_t intervall = 0;
  static uint32_t vorhin = 0;
  static byte schritt = 0;

  if (jetzt - vorhin >= intervall) {
    vorhin = jetzt;
    switch (schritt++) {
      case 0:
        digitalWrite(akreuzPin, HIGH);
        intervall = LANG;
        break;
      case 1:
        digitalWrite(akreuzPin, LOW);
        intervall = KURZ;
        break;
      case 2:
        digitalWrite(akreuzPin, HIGH);
        intervall = KURZ;
        break;
      case 3:
        digitalWrite(akreuzPin, LOW);
        intervall = KURZ;
        schritt = 0;
        break;
    }
  }
}

void loop()
{
  jetzt = millis();
  akreuz();
}

Dieses Programm arbeitet blockadearm mit millis().

Extra für Modellbahner hat @MicroBahner die MobaTools entwickelt, die auch einen blockadearmen Zeitgeber "Eieruhr" enthalten:

#include <MobaTools.h>  // Kann mittels Arduino-IDE installiert werden. 
MoToTimer Akreuz;

const byte akreuzPin = 13;	// Pin für Andreaskreuz
const uint32_t KURZ = 200, LANG = 500;

void setup()
{
  pinMode(akreuzPin, OUTPUT);
}

void akreuz()
{
  static byte schritt = 0;
  if (!Akreuz.running()) {
    switch (schritt++) {
      case 0:
        digitalWrite(akreuzPin, HIGH);
        Akreuz.setTime(LANG); // "Eieruhr" aufziehen
        break;
      case 1:
        digitalWrite(akreuzPin, LOW);
        Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
        break;
      case 2:
        digitalWrite(akreuzPin, HIGH);
        Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
        break;
      case 3:
        digitalWrite(akreuzPin, LOW);
        Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
        schritt = 0;
        break;
    }
  }
}

void loop()
{
  akreuz();
}

Inzwischen haben die Frauen bemerkt, wie viel sicherer so ein blinkender Übergang ist (auch wenn er derzeit ständig blinkt) und sie wollen mehr Andreaskreuze! Also tun die Männer, was sie immer tun, sie bauen neue. Dazu wird die Funktion in die Methode einer Klasse gepackt und diese Klasse durch mehrere Instanzen genutzt. Da in dieser Gegend wegen der Wiedererkennung blinkende Übergänge unterschiedliche Blinkzeiten haben sollen, können die Instanzen unterschiedliche Zeiten für KURZ oder LANG haben:

#include <MobaTools.h>  // Kann mittels Arduino-IDE installiert werden. 

class AndreasKreuze {
  private:  // Die folgenden Objekte sind nur innerhalb der Klasse zugänglich. Am Anfang einer Klasse überflüssig.
    const byte akreuzPin;
    const uint32_t KURZ, LANG;
    MoToTimer Akreuz;
    byte schritt = 0;
  public:
    AndreasKreuze(const byte _akreuzPin, const uint32_t _KURZ, const uint32_t _LANG)
      : akreuzPin(_akreuzPin), KURZ(_KURZ), LANG(_LANG)
    {}

    void init() {
      pinMode(akreuzPin, OUTPUT);
    }

    void blinken() {
      if (!Akreuz.running()) {
        switch (schritt++) {
          case 0:
            digitalWrite(akreuzPin, HIGH);
            Akreuz.setTime(LANG); // "Eieruhr" aufziehen
            break;
          case 1:
            digitalWrite(akreuzPin, LOW);
            Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
            break;
          case 2:
            digitalWrite(akreuzPin, HIGH);
            Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
            break;
          case 3:
            digitalWrite(akreuzPin, LOW);
            Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
            schritt = 0;
            break;
        }
      }
    }
};

AndreasKreuze andreasKreuze[] {
  //akreuzPin, KURZ, LANG;
  {13, 200, 500},  // am Bahnhof von Norden
  {8, 200, 500},  // am Bahnhof von Süden
  {9, 500, 200},  // am Wasserturm
  {10, 222, 222}   // am Saloon
};

void setup()
{
  for (AndreasKreuze &a : andreasKreuze) a.init();     // alle Instanzen durchlaufen die Methode init
}

void loop()
{
  for (AndreasKreuze &a : andreasKreuze) a.blinken();  // alle Instanzen durchlaufen die Methode blinken
}

Geht das so in Deine Richtung hinsichtlich Interesse am Programmieren?

Hallo agmue,
vielen Dank für die Tipps. Link erstellt. Kurz zur Erklärung. Seit 2011 bin ich dem Modellbahnwahn in Spur Z verfallen. Ich bin kein Fan von Fertigteilen, das kannst du auf meiner Webseite sehen. Außer den Häusern mache ich auch in Elektrik/Elektronik und habe bis vor kurzem mit Leiterplatte, Relais, Poti & Co. hantiert. Jede Anlage verlangt nach individuellen Einstellungen. Dann kam der Nano-Weihnachtskalender... Ich habe mir die MoBa-Tools angesehen. Prima, aber wenn man was individuelles braucht, muss man anpassen. Das bedeutet, dass man versteht was da ist, um es ändern zu können. Deshalb fange ich "unten" an und erfinde dabei das berühmte Fahrrad zum 2. Mal. Und ich mache es nicht nur für mich, sondern auch für andere Einsteiger aus der Z-Fan-Gemeinde und stelle es hier zur Verfügung.
Arduino für Anfänger. Sicher gibt es besseres aber die Leute sind dankbar.
Deine Erklärung mit dem Andreaskreuz ist Klasse. So liebe ich das, danke.
Und Es gab berühmte Maler, die waren auch gute Bildhauer :stuck_out_tongue_winking_eye:

Den Schraubendreher, den Hammer, die Bohrmaschine, den Kleber ... erfindest Du alles neu? Manche Werkzeuge wie Programmbibliotheken darf man ruhig als Hilfsmittel akzeptieren, finde ich. Ihre Anwendung muß man allerdings verstehen, das kann ich nur bekräftigen.

Deine Anlage ist so einmalig wie das dazugehörige Programm. Landschaftsbau und Programmieren sind kreative Prozesse und passen daher gut zusammen.

Freut mich, Du darfst da gerne was in Deine Anleitung übernehmen.


Nachdem die Andreaskreuze (ich verlege sie auf die Pins A0 bis A3) schön blinken, sollten auch Züge fahren.

Mir fällt auf, daß in1 bis in4 nicht verändert werden, weshalb man sie auf feste Potentiale legen könnte, was µC-Pins frei macht für neue Aufgaben.

In Deinem Programm in #1 sehe ich die Zustände BREMSEN, PAUSE, BESCHLEUNIGEN, denen ich noch den Zustand "WARTEN auf Sensorimpuls" hinzufüge. Mit der Schrittkette und den MobaTools als Zeitverwalter ergibt sich dieses Programm für die zwei Züge:

#include <MobaTools.h>  // Kann mittels Arduino-IDE installiert werden. 
MoToTimer Zeit;
// Definition Hallpins und Hallstatus
const byte hallSensorPin1 = 2;
const byte hallSensorPin2 = 3;

// Gleichstrommotor 1
const byte GSM1 = 10;
const byte in1 = 9;
const byte in2 = 8;

// Gleichstrommotor 2
const byte GSM2 = 5; //IN B
const byte in3 = 7; // IN 3
const byte in4 = 6;  // IN 4

void setup()
{
  Serial.begin(9600);
  Serial.println("\nStart");
  pinMode(hallSensorPin1, INPUT_PULLUP);
  pinMode(hallSensorPin2, INPUT_PULLUP);
  // Motor 1 Richtung H-Brücke
  pinMode(GSM1, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
  // Motor 2 Richtung H-Brücke
  pinMode(GSM2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  digitalWrite(in3, HIGH);
  digitalWrite(in4, LOW);
}

void HALL1A() // Funktion für Hallsensor 1
{
  const uint32_t PAUSENZEIT = 2000;
  const byte BESCHLEUNIGUNG = 40;
  const byte MAXGESCHWINDIGKEIT = 240;
  static byte geschwindigkeit = 0;
  enum struct Schritte {WARTEN, BREMSEN, PAUSE, BESCHLEUNIGEN};  // Benennung und Numerierung der Schritte der Schrittkette
  static Schritte schritt = Schritte::BESCHLEUNIGEN;

  switch (schritt) {
    case Schritte::WARTEN:
      if (!digitalRead(hallSensorPin1)) {
        schritt = Schritte::BREMSEN;
      }
      break;
    case Schritte::BREMSEN:
      if (!Zeit.running()) {
        if (geschwindigkeit >= BESCHLEUNIGUNG) {
          geschwindigkeit -= BESCHLEUNIGUNG;
          analogWrite(GSM2, geschwindigkeit); // Motor 2 bremst
          Zeit.setTime(500);
          Serial.print("geschwindigkeit GSM2: "); Serial.println(geschwindigkeit);
        } else {
          geschwindigkeit = 0;
          analogWrite(GSM2, geschwindigkeit); // Motor 2 steht
          Zeit.setTime(PAUSENZEIT);
          schritt = Schritte::PAUSE;
        }
      }
      break;
    case Schritte::PAUSE:
      if (!Zeit.running()) {
        schritt = Schritte::BESCHLEUNIGEN;
      }
      break;
    case Schritte::BESCHLEUNIGEN:
      if (!Zeit.running()) {
        if (geschwindigkeit <= MAXGESCHWINDIGKEIT - BESCHLEUNIGUNG) {
          geschwindigkeit += BESCHLEUNIGUNG;
          analogWrite(GSM1, geschwindigkeit); // Motor 1 beschleunigt
          Zeit.setTime(100);
          Serial.print("geschwindigkeit GSM1: "); Serial.println(geschwindigkeit);
        } else {
          geschwindigkeit = MAXGESCHWINDIGKEIT;
          analogWrite(GSM1, geschwindigkeit); // Motor 1 auf maximaler Geschwindigkeit
          Zeit.setTime(PAUSENZEIT);
          schritt = Schritte::WARTEN;
        }
      }
      break;
  }
}

void HALL1B() //Funktion für Hallsensor 2
{
  const uint32_t PAUSENZEIT = 2000;
  const byte BESCHLEUNIGUNG = 40;
  const byte MAXGESCHWINDIGKEIT = 240;
  static byte geschwindigkeit = MAXGESCHWINDIGKEIT;
  enum struct Schritte {WARTEN, BREMSEN, PAUSE, BESCHLEUNIGEN};  // Benennung und Numerierung der Schritte der Schrittkette
  static Schritte schritt = Schritte::WARTEN;

  switch (schritt) {
    case Schritte::WARTEN:
      if (!digitalRead(hallSensorPin2)) {
        schritt = Schritte::BREMSEN;
      }
      break;
    case Schritte::BREMSEN:
      if (!Zeit.running()) {
        if (geschwindigkeit >= BESCHLEUNIGUNG) {
          geschwindigkeit -= BESCHLEUNIGUNG;
          analogWrite(GSM1, geschwindigkeit); // Motor 1 bremst
          Zeit.setTime(500);
          Serial.print("geschwindigkeit GSM1: "); Serial.println(geschwindigkeit);
        } else {
          geschwindigkeit = 0;
          analogWrite(GSM1, geschwindigkeit); // Motor 1 steht
          Zeit.setTime(PAUSENZEIT);
          schritt = Schritte::PAUSE;
        }
      }
      break;
    case Schritte::PAUSE:
      if (!Zeit.running()) {
        schritt = Schritte::BESCHLEUNIGEN;
      }
      break;
    case Schritte::BESCHLEUNIGEN:
      if (!Zeit.running()) {
        if (geschwindigkeit <= MAXGESCHWINDIGKEIT - BESCHLEUNIGUNG) {
          geschwindigkeit += BESCHLEUNIGUNG;
          analogWrite(GSM2, geschwindigkeit); // Motor 2 beschleunigt
          Zeit.setTime(100);
          Serial.print("geschwindigkeit GSM2: "); Serial.println(geschwindigkeit);
        } else {
          geschwindigkeit = MAXGESCHWINDIGKEIT;
          analogWrite(GSM2, geschwindigkeit); // Motor 2 auf maximaler Geschwindigkeit
          Zeit.setTime(PAUSENZEIT);
          schritt = Schritte::WARTEN;
        }
      }
      break;
  }
}
//=============================
void loop()
{
  HALL1A();
  HALL1B();
}

Die Funktionen sehen doch recht gleich aus, also kann man sie mit Hilfe von Feldern zusammenfassen:

#include <MobaTools.h>  // Kann mittels Arduino-IDE installiert werden. 
MoToTimer Zeit;
// Definition Hallpins und Hallstatus
const byte hallSensorPin[] = {2, 3};

// Gleichstrommotor
const byte GSM[] = {10, 5};
const byte in1[] = {9, 7};
const byte in2[] = {8, 6};

void setup()
{
  Serial.begin(9600);
  Serial.println("\nStart");
  for (byte j = 0; j < sizeof(hallSensorPin); j++) {
    pinMode(hallSensorPin[j], INPUT_PULLUP);
    pinMode(GSM[j], OUTPUT);
    pinMode(in1[j], OUTPUT);
    pinMode(in2[j], OUTPUT);
    digitalWrite(in1[j], HIGH);
    digitalWrite(in2[j], LOW);
  }
}

void HALL(byte hallA) // Funktion für Hallsensor
{
  byte hallB = 1;
  if (hallA == 1) hallB = 0;
  const uint32_t PAUSENZEIT = 2000;
  const uint8_t BESCHLEUNIGUNG = 40;
  const uint16_t BREMSINTERVALL = 500;
  const uint16_t BESCHLEUNIGUNGSINTERVALL = 100;
  const byte MAXGESCHWINDIGKEIT = 240;
  static byte geschwindigkeit[] = {0, 0};
  enum struct Schritte {WARTEN, BREMSEN, PAUSE, BESCHLEUNIGEN};  // Benennung und Numerierung der Schritte der Schrittkette
  static Schritte schritt[] = {Schritte::BESCHLEUNIGEN, Schritte::WARTEN};

  switch (schritt[hallA]) {
    case Schritte::WARTEN:
      if (!digitalRead(hallSensorPin[hallA])) {
        schritt[hallA] = Schritte::BREMSEN;
      }
      break;
    case Schritte::BREMSEN:
      if (!Zeit.running()) {
        if (geschwindigkeit[hallB] > BESCHLEUNIGUNG) {
          geschwindigkeit[hallB] -= BESCHLEUNIGUNG;
          analogWrite(GSM[hallB], geschwindigkeit[hallB]); // Motor bremst
          Serial.print("hallB: "); Serial.print(hallB); Serial.print("\tgeschwindigkeit[hallB]: "); Serial.println(geschwindigkeit[hallB]);
          Zeit.setTime(BREMSINTERVALL);
        } else {
          geschwindigkeit[hallB] = 0;
          analogWrite(GSM[hallB], geschwindigkeit[hallB]); // Motor steht
          Serial.print("hallB: "); Serial.print(hallB); Serial.print("\tgeschwindigkeit[hallB]: "); Serial.println(geschwindigkeit[hallB]);
          Zeit.setTime(PAUSENZEIT);
          schritt[hallA] = Schritte::PAUSE;
        }
      }
      break;
    case Schritte::PAUSE:
      if (!Zeit.running()) {
        schritt[hallA] = Schritte::BESCHLEUNIGEN;
      }
      break;
    case Schritte::BESCHLEUNIGEN:
      if (!Zeit.running()) {
        if (geschwindigkeit[hallA] < MAXGESCHWINDIGKEIT - BESCHLEUNIGUNG) {
          geschwindigkeit[hallA] += BESCHLEUNIGUNG;
          analogWrite(GSM[hallA], geschwindigkeit[hallA]); // Motor beschleunigt
          Serial.print("hallA: "); Serial.print(hallA); Serial.print("\tgeschwindigkeit[hallA]: "); Serial.println(geschwindigkeit[hallA]);
          Zeit.setTime(BESCHLEUNIGUNGSINTERVALL);
        } else {
          geschwindigkeit[hallA] = MAXGESCHWINDIGKEIT;
          analogWrite(GSM[hallA], geschwindigkeit[hallA]); // Motor 1 auf maximaler Geschwindigkeit
          Serial.print("hallA: "); Serial.print(hallA); Serial.print("\tgeschwindigkeit[hallA]: "); Serial.println(geschwindigkeit[hallA]);
          Zeit.setTime(PAUSENZEIT);
          schritt[hallA] = Schritte::WARTEN;
        }
      }
      break;
  }
}

void loop()
{
  HALL(0);
  HALL(1);
}

Wenn nun die Züge durch die Landschaft fahren, sollten auch die Andreaskreuze blinken. Weil das konzeptionell bereits eingeplant ist, lassen sich beide Programme einfach kombinieren:

#include <MobaTools.h>  // Kann mittels Arduino-IDE installiert werden. 
MoToTimer Zeit;
// Definition Hallpins und Hallstatus
const byte hallSensorPin[] = {2, 3};

// Gleichstrommotor
const byte GSM[] = {10, 5};
const byte in1[] = {9, 7};
const byte in2[] = {8, 6};

class AndreasKreuze {
  private:  // Die folgenden Objekte sind nur innerhalb der Klasse zugänglich. Am Anfang einer Klasse überflüssig.
    const byte akreuzPin;
    const uint32_t KURZ, LANG;
    MoToTimer Akreuz;
    byte schritt = 0;
  public:
    AndreasKreuze(const byte _akreuzPin, const uint32_t _KURZ, const uint32_t _LANG)
      : akreuzPin(_akreuzPin), KURZ(_KURZ), LANG(_LANG)
    {}

    void init() {
      pinMode(akreuzPin, OUTPUT);
    }

    void blinken() {
      if (!Akreuz.running()) {
        switch (schritt++) {
          case 0:
            digitalWrite(akreuzPin, HIGH);
            Akreuz.setTime(LANG); // "Eieruhr" aufziehen
            break;
          case 1:
            digitalWrite(akreuzPin, LOW);
            Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
            break;
          case 2:
            digitalWrite(akreuzPin, HIGH);
            Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
            break;
          case 3:
            digitalWrite(akreuzPin, LOW);
            Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
            schritt = 0;
            break;
        }
      }
    }
};

AndreasKreuze andreasKreuze[] {
  //akreuzPin, KURZ, LANG;
  {A3, 200, 500},  // am Bahnhof von Norden
  {A2, 200, 500},  // am Bahnhof von Süden
  {A1, 500, 200},  // am Wasserturm
  {A0, 222, 222}   // am Saloon
};

void setup()
{
  Serial.begin(9600);
  Serial.println("\nStart");
  for (byte j = 0; j < sizeof(hallSensorPin); j++) {
    pinMode(hallSensorPin[j], INPUT_PULLUP);
    pinMode(GSM[j], OUTPUT);
    pinMode(in1[j], OUTPUT);
    pinMode(in2[j], OUTPUT);
    digitalWrite(in1[j], HIGH);
    digitalWrite(in2[j], LOW);
  }
  for (AndreasKreuze &a : andreasKreuze) a.init();     // alle Instanzen durchlaufen die Methode init
}

void HALL(byte hallA) // Funktion für Hallsensor
{
  byte hallB = 1;
  if (hallA == 1) hallB = 0;
  const uint32_t PAUSENZEIT = 2000;
  const uint8_t BESCHLEUNIGUNG = 40;
  const uint16_t BREMSINTERVALL = 500;
  const uint16_t BESCHLEUNIGUNGSINTERVALL = 100;
  const byte MAXGESCHWINDIGKEIT = 240;
  static byte geschwindigkeit[] = {0, 0};
  enum struct Schritte {WARTEN, BREMSEN, PAUSE, BESCHLEUNIGEN};  // Benennung und Numerierung der Schritte der Schrittkette
  static Schritte schritt[] = {Schritte::BESCHLEUNIGEN, Schritte::WARTEN};

  switch (schritt[hallA]) {
    case Schritte::WARTEN:
      if (!digitalRead(hallSensorPin[hallA])) {
        schritt[hallA] = Schritte::BREMSEN;
      }
      break;
    case Schritte::BREMSEN:
      if (!Zeit.running()) {
        if (geschwindigkeit[hallB] > BESCHLEUNIGUNG) {
          geschwindigkeit[hallB] -= BESCHLEUNIGUNG;
          analogWrite(GSM[hallB], geschwindigkeit[hallB]); // Motor bremst
          Serial.print("hallB: "); Serial.print(hallB); Serial.print("\tgeschwindigkeit[hallB]: "); Serial.println(geschwindigkeit[hallB]);
          Zeit.setTime(BREMSINTERVALL);
        } else {
          geschwindigkeit[hallB] = 0;
          analogWrite(GSM[hallB], geschwindigkeit[hallB]); // Motor steht
          Serial.print("hallB: "); Serial.print(hallB); Serial.print("\tgeschwindigkeit[hallB]: "); Serial.println(geschwindigkeit[hallB]);
          Zeit.setTime(PAUSENZEIT);
          schritt[hallA] = Schritte::PAUSE;
        }
      }
      break;
    case Schritte::PAUSE:
      if (!Zeit.running()) {
        schritt[hallA] = Schritte::BESCHLEUNIGEN;
      }
      break;
    case Schritte::BESCHLEUNIGEN:
      if (!Zeit.running()) {
        if (geschwindigkeit[hallA] < MAXGESCHWINDIGKEIT - BESCHLEUNIGUNG) {
          geschwindigkeit[hallA] += BESCHLEUNIGUNG;
          analogWrite(GSM[hallA], geschwindigkeit[hallA]); // Motor beschleunigt
          Serial.print("hallA: "); Serial.print(hallA); Serial.print("\tgeschwindigkeit[hallA]: "); Serial.println(geschwindigkeit[hallA]);
          Zeit.setTime(BESCHLEUNIGUNGSINTERVALL);
        } else {
          geschwindigkeit[hallA] = MAXGESCHWINDIGKEIT;
          analogWrite(GSM[hallA], geschwindigkeit[hallA]); // Motor 1 auf maximaler Geschwindigkeit
          Serial.print("hallA: "); Serial.print(hallA); Serial.print("\tgeschwindigkeit[hallA]: "); Serial.println(geschwindigkeit[hallA]);
          Zeit.setTime(PAUSENZEIT);
          schritt[hallA] = Schritte::WARTEN;
        }
      }
      break;
  }
}

void loop()
{
  HALL(0);
  HALL(1);
  for (AndreasKreuze &a : andreasKreuze) a.blinken();  // alle Instanzen durchlaufen die Methode blinken
}

Wie man sieht, kann so ein Nano mehrere Dinge quasie gleichzeitig erledigen. Es sind auch noch Kapazitäten frei (benutzt 10% des Programmspeicherplatzes, 17% des dynamischen Speichers) beispielsweise für ein flackerndes Lagerfeuer oder dergleichen. Mittels Portexpandern wie MAX23017 oder MAX23S17 sind auch noch mehr Pins möglich.

TODO: Natürlich sollten die Andreaskreuze nur blinken, wenn sich ein Zug nähert. Dazu wären aber zusätzliche Sensoren notwendig.

@schorsch55: Ich hoffe, Du kannst was verwenden :slightly_smiling_face:

Hallo @agmue ,
das ist der Wahnsinn. Vielen Dank, ich werde mich einfuchsen und das Ganze auf einer kleinen Testanlage ausprobieren. Sowohl die Motoren/Blink Sketche, als auch den Einbau von Weichenschaltung mit Relais/Servo. Die habe ich als "Einzelstücke" schon fertig, wie auch verschiedene "Blinkereien", Schrittmotoren-Sketche für die geplanten Aktionen von Funktionsmodellen wie Karussellen, Kränen, Windmühlen etc. Ich habe jetzt erst mal satt zu tun. Ich werde bestimmt etwas von dir verwenden und das auch in meinen Anleitungen deklarieren, wenn es dir recht ist. Vielleicht hast du ja einen direkten Link, den ich einbauen kann.
Ich melde mich auf jeden Fall, besonders wenn es Probleme gibt, sei gerüstet.

Bei meinem Kohlekranfunktionsmodell habe ich einen automatischen Ablauf implementiert, der zum Drehen einen Schrittmotor verwendet und zum Heben ein Servo. Ein befreundeter Modellbahner hat ein wunderschönes Kranmodell auf diese Art animiert.

Kannst Du gerne verwenden, aber das geistige Eigentum ist doch eher vernachlässigbar gering, daher erscheint mir meine Erwähnung nicht notwendig.

Nö, sowas habe ich nicht.

Klar doch :cowboy_hat_face:

Moin agmue,
jetzt hast du den Salat, natürlich habe ich Fragen!! Ich bastele mich gerade durch deine Beispiele, die natürlich funktionieren. Aber zum besseren Verständnis, aus dem void akreuz:

Blockquote

if (jetzt - vorhin >= intervall) {
vorhin = jetzt;
switch (schritt++) {
case 0:
digitalWrite(akreuzPin, HIGH);
intervall = LANG;
break;
case 1:
digitalWrite(akreuzPin, LOW);
intervall = KURZ;
break;

Blockquote
Das bedeutet, das im 1 Falle steht if (0-0>=0),
setze vorhin (0) = jetzt (0)
erhöhe schritt um 1
falls das 0 ist;
schalte den akreuzPin auf an mit dem Intervall lang (500)
break =und brich ab
usw bis zum 4. Fall case = 3
Wo ich Verständnisproblem habe ist der Einstieg ins if. wird den mit dem switch nicht gleich durch schritt++ aus 0 eine 1 ???
Und was genau bewirkt das break? Laut Referenz heißt es dort:
break wird benutzt, um eine do, for oder while-Schleife zu verlassen, ohne dass die Bedingung zum Schleifenabbruch erfüllt ist. Das erschließt sich mir gar nicht. Hilf mal bitte dem Arduino-DAU

Und im gleichen Zusammenhang mit dem MoToTimer Akreuz:
void akreuz()
{
static byte schritt = 0;
if (!Akreuz.running()) {
switch (schritt++) {
case 0:
digitalWrite(akreuzPin, HIGH);
Akreuz.setTime(LANG); // "Eieruhr" aufziehen
break;
case 1:
digitalWrite(akreuzPin, LOW);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
break;
Das if(!Akreuz.running() heißt dann wohl: Wenn der Timer Akreuz NICHT läuft, ... und dann wieder das switch. Auch das stellt mich vor ein Rätsel. Wenn ich das verstanden habe, gehts mit der Klasse weiter.
Ich danke schon mal.