MOBA Wechsel-/Pendelsteuerung

Die Position der Weichen sollten auch einen Stromausfall resp. Das herunterfahren der Anlage am Ende des Ausstellungstages verkraften. Um dies zu realisieren, habe ich ein FRAM Baustein via I2C Bus eingesetzt.

Wenn der Automat aktiviert wurde per Taste: Run, sollte mit einer Taste: Anhalten (orange Led) die Züge auf ihre Abstell- resp. Hauptgleise einfahren und anhalten -> Stoppen des Automaten für essen, Ausstellungsende, etc.

Im automatischen betrieb, wenn irgendwas ist, so sollten per Taster Stop (rote Led) die Fahrspannung abgestellt werden und alle Sensorspeicher/Blockvariablen zurückgesetzt werden.

Automatisches und manuelles Fahren muss möglich sein, unabhängig der Reed magnetkontakte (Umschalter)

Vor- Rückwärts sollte gefahren werden (nur im manuellen Modus notwendig)

Die Steuerung sollte leicht weiter ausgebaut werden können von der Dimensionierung her. - (Relais, freie Ein- und Ausgänge auf den MCP23017 Bausteinen (Achtung der GB7 und GA7 Pin‘s können nicht mehr als Eingang benutzt werden auf neueren Versionen ab 2020),

Potentiometer für die Fahrregelung der Zugsmotoren 0..9V Gleichspannung. Es wird analog gefahren um kompatibel zu den original hergestellten Zügen zu gewährleisten für Gastfahrer.

Die Weichen müssten auch manuell gestellt werden können (2 Taster), Anzeige der Weichenstellung: orange Led für Abgebogen, blaue Led für gerade aus.

Taster für Weichen- resp. Fahrstrassen, 3 Taster.

Das Fahrpult sollte von der übrigen Steuerung abgesetzt werden können, die Distanz ist jedoch sehr kurz mit ca. 80cm, daher kommt der I2C Bus zum Einsatz.

Einige Werte sollten auf einem LCD Display (ebenfalls via I2C) dargestellt werden: aktuelle Fahrgeschwindigkeit, Trimmerspannung,Status und Fehlermeldungen, …

Es sollten 0, 1 oder 2 Haltestellen möglich sein. In dieser Wildwest Anlage kommt nur eine Haltestelle zum Einsatz.

Die Steuerung muss eine USB Speisung für die Beleuchtung zur Verfügung stellen.

Hardware

Weil der Zeitrahmen für die Steuerung sehr eng lag, wurde beschlossen auf erhältliche Fertig Module zu setzen und nicht alles selber zu Entwickeln, löten und Ätzen. Trotzdem sind es nun 4 selber gezeichnete und gelötete Platinen geworden.

Die Speisung wird via 12V Steckernetzteil 5A zugeführt

2x DC-DC Step Down Module liefern die grossen Ströme fürs Relais Modul 5V und für die H-Brücken L298 9V für die Weichenservomotoren und das BTS 7960 9V Modul für die Fahrspannung

Eine kleinen Platine mit dem ULN2808A Chip zwischen der Platine Nr 2 und dem Relais Modul wechselt den Spannungspegel von 0V / 5V resp. 5V / 0V so dass die Ausgänge/Relais mit Logisch 1 für Ein und logisch 0 für Aus angesprochen werden kann. (Dies hatte speziell bei der Initialisierung, wenn der Baustein in der setup() Routine von Eingang auf Ausganggewechselt hat geklackt)

Weil 6 PWM Ausgänge benötigt wedren, wird der Arduino Mega 2560 Rev. 3 ausgewählt. (Bei Uno sind ja nur die Pins 3 und 9 benutzbar wenn millis(), I2C Bus und SPI Bus benutzt wird.)

Ein I2C FRAM IC sichert die Einstellungen wie die Weichenstellung beim Stellen der Weichen und liest die Daten beim Starten (setup() ) der Anlage ein. (blaue kleine Platine rechts vom Arduino)

LCD Modul 4 Zeilen x 20 Zeichen für die Anzeige via I2C Print mit einemmPCA8574 Baustein angeschlossen via I2C Bus.

Platine Nr 1, (rechts oben100mm x 100mm) enthält die Steuerspeisungen für 5V und 9V via klassischen Längsregler LM7805 und LM7809 mit je einem kleinen Kühlkörper. Ebenfalls wurden da die Pin Headers für die Potentiometer (inkl. Res.) und Eingänge mit einem RN Netzwerk von 10k auf GND gezogen für den Microcontroller Arduino Mega. Verteiler für 5V und 9V via Pin Headers oder/und Schraubklemmen, Sicherung 2.5A. Der vorgesehene Platz für den I2C Multiplexer wird für diese Steuerung nicht benötigt, der I2C Pin Header Verteiler schon.

Platine Nr. 2, (links oben 100mm x 100mm) sind 2 mcp23017 Bausteine mit je 16 Ausgängen und 14 Eingängen für die Reed Magnetsensoren, Jumper Header J11, Relais Ansteuerung mittels Inverter IC ULN 2803A, die Durchschlaufung der Signale von den beiden H-Brücken für 4 Weichen mit dem L298 Chip (2x rote Platinen) und der H-Brücke mit dem BTS7960er Chip (blaue Platine) für die Fahrspannung 0..9V

Platine Nr. 3, (unten Mitte 160mm x 100mm) mit Pin Headers zum Verteilen von 0V, 5V, I2C Bus und die analog Signale für den Fahrregler und die Trimmerspannung sowie die Steuerleitungen für vor-rückwärts, automatischer-/manueller Betrieb, Taster für Weichen, Start, Stop, Anhalten, Weichenstrassen, 2x Jumper für den I2C Busabschluss mit 4.7k, 2x ein mcp23017 (bis vor 2020 mein Lieblingsbaustein, nebst den Arduino‘s) Entstärkondensatoren auf der Rückseite der Platine. -> Wer die Leds betrachtet, sieht das Layout der Fahrstrassen und Weichen. Die roten Led’s sind die Reed Magnetkontakte.

Platine Nr. 4. (links unten 100mm x 100mm) Zusatzspeisung 12V zu 5V mit einem LM7805 für die Fahrreglerbedienplatine und das LCD zur Entlastung des Reglers auf der Platine Nr. 1 inkl. Sicherung 1A, Betriebs-Led 12V (gelb), 5V (rot), etc.

Bild betr. Hardware:

Fehlt noch die Software, das heisst die Funktionen (bis jetzt) und neu (mit Erkenntnissen der objektorientierten Programmierung) folgt ....

Ok, das mit den Variablen, resp. Standard initialisierung das leuchtet ein. danke für Deine wirklich wertvollen Inputs und Erklärungen. Werde, wenn ich die Beschreibung fertig habe, die daraus ergebenen Klassen und Funktionen erstellen.

Der Aufbau war schon. Am Ende jeder Ausstellung wird dies transportfähig in Kisten verpackt um es am neuen Ort wieder auf zu stellen. Unten in der Galerie ab Bild 030_Collab_01 siehst Du es fertig aufgebaut

Diese Funktion funktioniert, es blinkt im 500mS Takt aber, bei der Schrittkette: switch(), Case bleibt er immer im ersten Case: Er schaltet nicht in den Zweig DAUER1

   // Funktion um EIN unregelmässiges Blinken Ein zu Aus Zeitverhältnis zu haben
    // Weichen stellen mit einer OOP Funktion
    void zeitSwap() {
      switch (blinkStatus) {
        case Blink_Stat::EIN:
          // -> Funktionsaufruf -> LedX(EIN);
          blinkStatus = Blink_Stat::DAUER1;
          // -> Hier müsste eine &function1() rEIN, per Parameterübergabe von der Klasse vererbt
          zeitVorher = millis();  // Seit aktuell EINlesen
          ledStatus = true;
          Blink_Stat.err = 1;
          break;

        case Blink_Stat::DAUER1:
          if (millis() - zeitVorher >= zeitDauer) {
            // zeitVorher = zeitVorher + zeitDauer;
            blinkStatus = Blink_Stat::AUS;
            ledStatus = false;
            // zeitVorher = millis();
          }
         Blink_Stat.err = 2;
...

Es blinkt nicht und schaltet nicht weiter: Blink_Stat.err = 0; im seriellen Monitor. In jedem Case inkl. default, wird der Variablen err eine Zahl von 0 .. 5 zugewiesen eine
-blink1.errTest()-: 0
-blink1.errTest()-: 0
-blink1.errTest()-: 0
-blink1.errTest()-: 0

Ich vermute, dass etwas an den Zeit Parametern nicht gut ist. Nur was?

Habe die Duo Led inkl. eines Vorwiedersandes an Pin 6 und Pin 7 Angeschlossen und wechsle die Polarität von beiden Pins:

  blink1.zeitSwap(); // muss immer aufgerufen werden je loop Durchlauf
  digitalWrite(6, blink1.getblinkStatus());
  digitalWrite(7, !blink1.getblinkStatus());

:thinking:

Hallo,

die Schrittkette steht im Zustand "NICHTS" und es gibt kein Ereignis was das ändert. Zudem "NICHTS" unglücklich auch noch default ist.

Neues Bsp. Wenn das wie gewünscht funktioniert, wäre der nächste Schritt die Led Geschichte in die Klasse zu übernehmen.
Damit die Led oder was auch immer geschalten wird zur Klasse gehört. Dann kannste die übliche begin() bzw. init() Methode schreiben um den Led Pin mittels pinMode zu konfigurieren und andere Dinge. Damit die Klasse und alles was zu ihr gehört zusammen bleibt. getLedStatus() könnte auch die Klassen eigene Blinkmethode werden.

Habs mal reduziert, den Ablauf musste jetzt auf dich einwirken lassen und ggf. für dich passend ändern.

Sketch
enum class Blink_Stat {START, WAITAUS, WAITEIN};

class MTimer
{
  private:
    const unsigned long einZeit;
    const unsigned long ausZeit;
    Blink_Stat blinkStatus;
    bool ledStatus = false;                
    unsigned long lastMillis;

  public:
    MTimer(unsigned long a):
      einZeit(a),
      ausZeit(a),
      blinkStatus (Blink_Stat::START),
      lastMillis (0)
    {}
    
    MTimer(unsigned long a, unsigned long b):
      einZeit(a),
      ausZeit(b),
      blinkStatus (Blink_Stat::START),
      lastMillis (0)
    {}
    

    //----------------------------------------------------
    // Funktion um EIN unregelmässiges Blinken Ein zu Aus Zeitverhältnis zu haben
    // Weichen stellen mit einer OOP Funktion
    void zeitSwap() {
      switch (blinkStatus) {
        case Blink_Stat::START:
          lastMillis = millis();  
          ledStatus = true;
          blinkStatus = Blink_Stat::WAITAUS;
          Serial.println("WAITAUS");
          break;
                
        case Blink_Stat::WAITAUS:
          if (millis() - lastMillis >= einZeit) {
            lastMillis = millis();  
            ledStatus = false;
            blinkStatus = Blink_Stat::WAITEIN;
            Serial.println("WAITEIN");
          }
          break;

        case Blink_Stat::WAITEIN:
          if (millis() - lastMillis >= ausZeit) {
            lastMillis = millis();  
            ledStatus = true;
            blinkStatus = Blink_Stat::WAITAUS;
            Serial.println("WAITAUS");
          }
        break;
      }
    }

    bool getLedStatus() { return ledStatus; }
   
};

const unsigned long intervallLedEin = 1000;
const unsigned long intervallLedAus = 2000;

MTimer blinker1(intervallLedEin);
MTimer blinker2(intervallLedEin, intervallLedAus);

const byte pinLed1 = 28;
const byte pinLed2 = 29;

void setup() {
  Serial.begin(250000);
  pinMode(pinLed1, OUTPUT);
  pinMode(pinLed2, OUTPUT);
}

void loop() {
  

  blinker1.zeitSwap(); // muss immer aufgerufen werden je loop Durchlauf
  blinker2.zeitSwap(); // muss immer aufgerufen werden je loop Durchlauf
  digitalWrite(pinLed1, blinker1.getLedStatus());  
  digitalWrite(pinLed2, blinker2.getLedStatus()); 
  

}

Hallo Doc_Arduino
Da hast Du natürlich sehr recht, der kommt ja gar nicht in die Gänge da der Staus nicht wechselt, das habe ich jetzt lauter OOP und Hektik nicht festgestellt dass es da fehlt. :thinking: Du kannst Dich extrem gut in halbfertigen Code und mein fehlerhaftes Hirn/Überlegungen eindenken :+1: Den Zustand NICHTS, war angedacht, das sich der Task nach der Ausführung selber beendet, ohne dass man da noch was tun muss. (Ähnlich wie Task.Sleep()).

  • Was ich zuwenig gedacht habe ist, dass dies für die Weichenmotoren, Bahnhofhalt, etc. funktioniert da diese Schrittketten vom Reed Sensor nur einmal aufgerufen werden.
  • Beim Blinklicht am z.B. Bahnhübergang oder auch als blinkende Led Anzeige auf dem Steuerpult / Bahnhofseinfahrt blinkt dies natürlich mehrfach (eine zeitlang).
    -> Werde den Code am WoE umsetzen/anpassen und mich danach melden :grinning: ,

Hallo,

ich würde keine Multikulti-Klasse schreiben die alles kann. Ich würde eine Klasse für eine Blinkersteuerung schreiben und eine andere Klasse für die Weichensteuerung usw.. Das macht die Codepflege später einfacher, weil man keine Kompromisse eingehen muss. Man kann für die Aufgabe speziell eine Klasse sprich Methoden schreiben und jederzeit ändern.

OK, ich trenne sie nunn nach blinken und nach Weichensteuerung, da ich da auch andere Schrittketten habe. Die verwendete LCD Library: LiquidCrystal_PCF8574.cpp hat ganz viele delaysMicroseconds() enthalten, manchmal werte bis zu 50000uS. Muss das sein? Im Setup Teil gehts ja noch aber im Betrieb ist das nicht gut. Daher wurde das Timing besser mit einer langsameren Update millis() "Schlaufe." Zuerst mal als ZIP.
LiquidCrystal_PCF8574.zip (10,6 KB)

Hallo,

das muss man relativieren und dann ist das alles kein Problem mehr. Ich habe mir die Lib angeschaut. Die delays sind nur in den Methoden
begin(), clear(), home() enthalten.
Alle 3 Methoden verwendet man in der Regel zu 99,99999999999999% nur einmal im setup(). Hier sollte das keine Rolle spielen. Alle anderen Methoden die man zur Laufzeit verwendet haben kein delay(). Von daher sehe ich kein Problem. clear() bspw. sollte man ohne Grund zur Laufzeit nie verwenden. Besser, weil schneller, ist es einfach den Cursor neu positionieren und überschreiben.

1 Like

Hallo Doc_Arduino
In der Zwischenzeit sind wieder 2 Ausstellungen durch und überstanden. Jetzt kommt dann noch das Museum für ca. 4 Monate. Ev. noch eine jahresausstellung, mal schauen.
Habe den objektorientierten Code an 2 Stellen eingesetzt und es lief wie von Dir angedeutet nicht besser, da der Fehler im Gerüst, den Verschachtellungen der Funktionen liegt. Die bestehende Steuerung wurde mit einem Anfangskonzept erstellt und auf die aktuelle Funktionalität erweitert. Kurz und schmerzvoll, die Steuerung muss neu erstellt werden auf der von Euch vorgeschlagenen Basis. ich möchte mich besonders für Deine stete und unkomplizierte Hilfe bis am Schluss bedanken. Du hast immer gefühlt wo es bei mir klemmt da immer die richtigen Hilfestellungen und Tipps gegeben. Danke dafür.

Hallo,

Bitteschön, wenn eine Rückmeldung kommt, freut man sich umso mehr.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.