Modellbauampel - schaffe es nicht die LEDs anzusteuern

Guten Abend, mein Name ist Michael und ich experimentiere schon seit einer Zeit mit dem Arduino. Ich möchte auch mein Modellbauhobby ein bischen mit dem Arduino verbinden.

LEDs gleichzeitig oder abwechselnd schnell oder langsam blinken zu lassen habe ich hinbekommen :slight_smile: jetzt hatte ich mir überlegt, eine alte FALLER-Ampel über den Arduino anzusteuern - vielleicht kennt jemand noch die FALLER AMS-Rennbahn aus den 60ern... Die Ampel hat drei Kabel in den jeweiligen "Ampelphasenfarben" und ein weißes (minus?)-Kabel.

Wenn ich jetzt eine LED der Ampel ansteuern will muß ich komischerweise den Ausgangspin mit dem weißen Kabel und die Farbe, die leuchten soll mit GND verbinden... Dann ist es natürlich essig damit, LEDs der Ampel abwechselnd leuchten zu lassen... Ich habe auch versucht, beide PINs mit dem weißen Kabel zu verbinden und dann halt z. B. rot und grün auf GND laufen zu lassen - dann leuchten aber nur beide LEDs gleichzeitig und entsprechend schwächer. Ich habe es mal mit einer Billig-Ampel aus China versucht, aber hier tritt genau das gleiche Problem auf. Habe ich hier einen Gedankenfehler oder ist es so gar nicht machbar, wenn das "Minus-Kabel" entsprechend falsch zusammengefasst worden ist. Ich verstehe nicht, wie z. B. FALLER das Problem realisiert mit deren Steuerungen oder z. B. BUSCH bietet ja auch Ampelsteuerungen an...

vielen Dank für eure Hilfe!

ich habe gerade noch mal überlegt: es müßte so laufen, daß quasi immer Spannung auf allen drei LEDs liegt, und der Arduino die jeweilige GND-Verbindung der entsprechenden Farbe "auf" oder "zu " machen müßte... geht das überhaupt?

So wie ich deine Beschreibung jetzt verstehe, sollte es funktionieren. Den weißen Draht auf +5Volt legen und die farbigen Drähte jeweils über einen passenden Vorwiderstand auf GND legen. Das geht dann auch mit einem Arduino, bei dem der zugehörige Ausgang auf GND geschaltet wird.
Ob der Vorwiderstand tatsächlich nötig ist, musst du ausmessen. Evtl. ist der ja schon verbaut, was ich nicht weiß.

Das kommt davon das die alte Schaltungen wurden sehr oft mit PNP Transistoren realisiert, also der GND Potenzial hat Plus, gesteuert wurde mit Minus

Welche AmpelNummer ist das?
Die 4036 war ausgelegt auf die Ansteuerung durch Trafo mit Wechselspannung! Darüber hinaus waren da 16V ~ die Regel.

Um das sinnvoll an den Arduino anzubinden, muss klar sein, welcher Ampeltyp das ist, um ggfls. den Vorwiderstand und die dazu in Reihe mitlaufende Diode zu ändern.

Alternativ eine Steuerung für 12/16V~ bauen. (SSR gibt es dazu passend)

Danke für eure Hinweise.. ich habe mal alle Faller-Kataloge durchsucht und mußte dann feststellen, daß die Ampeln gar nicht von Faller sondern von Kugler hergestellt worden sind. Schaltschema sieht so aus:

<> ich kann leider keine Bilder mehr hochladen, das Schema ist aber hier zu sehen:

@HotSystems: achso, d. h. ich lege alle drei PINs auf GND und er gibt dann quasi immer nur einen farbigen Draht "frei" so daß dann die Spannung über den weißen Draht fließen kann... aber eigentlich kommt doch von den drei PINs je nach Schaltzustand "Plus" oder ist das nicht generell so?

Da steht doch den weißen Draht auf + und je nach Farbe den farbigen Draht auf GND (Widerstände nicht vergessen)

Na ist doch schick, das Bild


gibt Dir doch vor, wie die funktionieren.
Die sind leuchtend nicht auf einem HIGH-Pegel sondern auf einem LOW.
Der gemeinsame (weiss) geht an +5V und je nachdem, welcher LOW geschalten ist, geht das Dingens an.

// Forensketch KuglerAmpel
// Anschluss der Kabel: weiss an +5V
//                      gruen an Pin 3
//                      gelb  an Pin 4
//                      rot   an Pin 5

const byte gruenPin = 3;
const byte gelbPin = 4;
const byte rotPin = 5;

const unsigned long schaltZeit = 2000; // zeit in ms
unsigned long lastSchalt;              // merker wann zuletzt geschalten

enum {rot, rotgelb, gruen, gelb};      // mögliche Zustände
byte zustand = rot;                    // Start

void red()                             // je nach Funktion werden die LED's gelöscht und gesetzt
{
  digitalWrite(rotPin, LOW);
}
void redYellow()
{
  red();
  yellow();
}
void green()
{
  digitalWrite(gruenPin, LOW);
}
void yellow()
{
  digitalWrite(gelbPin, LOW);
}
void aus()                              // Um auszugehen müssen die Pins auf V+ gesetzt sein
{
  digitalWrite(rotPin, HIGH);
  digitalWrite(gelbPin, HIGH);
  digitalWrite(gruenPin, HIGH);
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  pinMode(rotPin, OUTPUT);
  pinMode(gelbPin, OUTPUT);
  pinMode(gruenPin, OUTPUT);
}

void loop()
{
  if (millis() - lastSchalt >= schaltZeit) // Nach Zeitablauf
  {
    zustand ++;                            // weiterschalten
    lastSchalt = millis();                 // Schaltzeit merken
    aus();                                 // Zustand löschen
  }
  if (zustand > gelb) zustand = rot;       // Überlauf? - > Fange an von vorn
  switch (zustand)                         // Je Zustand verzweigen
  {
    case rot:
      red();
      break;
    case rotgelb:
      redYellow();
      break;
    case gruen:
      green();
      break;
    case gelb:
      yellow();
      break;
  }
}

(und wie schon geschrieben: Die Widerstände musst Du mit einbauen)

Weiß auf Plus
Rot mit Widerstand Pin 5
Gelb mit Widerstand Pin 4
Grün mit Widerstand auf Pin 3
Widerstand =100 Ohm

vermutlich musst die anderen HIGH schalten.
und dann das redYellow() noch anpassen.

Mach ich doch.

Hab ich was übersehen?

Vom Prinzip her hätte ich eigentlich das switch-Konstrukt auch in die

if (millis() - lastSchalt >= schaltZeit) // Nach Zeitablauf

Zeit packen können.

Naja, dann hast du meine Erklärung noch nicht verstanden.
Auf den weißen Draht legst du +5Volt. Die farbigen legst du auf drei Pins des Arduino. Der Pin, der eine Led zum leuchten bringen soll wird auf GND bzw. LOW geschaltet. Vorwiderstand nicht vergessen.

nein eh nicht. offenbar habs ich überlesen.

finde ich sinnvoller. Dann schaltet es nur wenn sich was ändert...

Na dann....

// Forensketch KuglerAmpel
// Anschluss der Kabel: weiss an +5V
//                      gruen an Pin 3
//                      gelb  an Pin 4
//                      rot   an Pin 5

const byte gruenPin = 3;
const byte gelbPin = 4;
const byte rotPin = 5;

const unsigned long schaltZeit = 2000; // zeit in ms
unsigned long lastSchalt;              // merker wann zuletzt geschalten

enum {rot, rotgelb, gruen, gelb};      // mögliche Zustände
byte zustand = rot;                    // Start

void red()                             // je nach Funktion werden die LED's gelöscht und gesetzt
{
  digitalWrite(rotPin, LOW);
}
void redYellow()
{
  red();
  yellow();
}
void green()
{
  digitalWrite(gruenPin, LOW);
}
void yellow()
{
  digitalWrite(gelbPin, LOW);
}
void aus()                              // Um auszugehen müssen die Pins auf V+ gesetzt sein
{
  digitalWrite(rotPin, HIGH);
  digitalWrite(gelbPin, HIGH);
  digitalWrite(gruenPin, HIGH);
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  pinMode(rotPin, OUTPUT);
  pinMode(gelbPin, OUTPUT);
  pinMode(gruenPin, OUTPUT);
  red();
}

void loop()
{
  if (millis() - lastSchalt >= schaltZeit) // Nach Zeitablauf
  {
    zustand ++;                            // weiterschalten
    if (zustand > gelb) zustand = rot;     // Überlauf? - > Fange an von vorn
    lastSchalt = millis();                 // Schaltzeit merken
    aus();                                 // Zustand löschen
    switch (zustand)                       // Je Zustand verzweigen
    {
      case rot:
        red();
        break;
      case rotgelb:
        redYellow();
        break;
      case gruen:
        green();
        break;
      case gelb:
        yellow();
        break;
    }
  }
}

(Und auch hier: Die Widerstaende müssen mit rein)

die Aufzählung würde ich trotzdem nicht vergewaltigen.
Entweder einen operator overload machen oder halt eine Funktion.

constexpr byte gruenPin {5};
constexpr byte gelbPin {6};
constexpr byte rotPin {13};

const unsigned long schaltZeit = 2000; // zeit in ms
unsigned long lastSchalt;              // merker wann zuletzt geschalten

enum Zustand {rot, rotgelb, gruen, gelb};   // mögliche Zustände
Zustand zustand = rot;                      // Start

void red()                             // je nach Funktion werden die LED's gelöscht und gesetzt
{
  digitalWrite(rotPin, LOW);
  digitalWrite(gelbPin, HIGH);
  digitalWrite(gruenPin, HIGH);
}
void redYellow()
{
  digitalWrite(rotPin, LOW);
  digitalWrite(gelbPin, LOW);
  digitalWrite(gruenPin, HIGH);
}
void green()
{
  digitalWrite(rotPin, HIGH);
  digitalWrite(gelbPin, HIGH);
  digitalWrite(gruenPin, LOW);
}
void yellow()
{
  digitalWrite(rotPin, HIGH);
  digitalWrite(gelbPin, LOW);
  digitalWrite(gruenPin, HIGH);
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  pinMode(rotPin, OUTPUT);
  pinMode(gelbPin, OUTPUT);
  pinMode(gruenPin, OUTPUT);
}

Zustand weiter(Zustand alt)
{
  switch (alt)
  {
    case rot: return rotgelb;
    case rotgelb: return gruen;
    case gruen: return gelb;
    case gelb: return rot;
  }
  return gelb;
}

void loop()
{
  if (millis() - lastSchalt >= schaltZeit) // Nach Zeitablauf
  {
    lastSchalt = millis();                 // Schaltzeit merken
    zustand = weiter(zustand);
    switch (zustand)                         // Je Zustand verzweigen
    {
      case rot:
        red();
        break;
      case rotgelb:
        redYellow(); 
        break;
      case gruen:
        green();
        break;
      case gelb:
        yellow();
        break;
    }
  }
}

wenn es das überhaupt braucht, weil eigentlich hat mans eh im switch case in der Hand:

//const byte gruenPin = 3;
//const byte gelbPin = 4;
//const byte rotPin = 5;

constexpr byte gruenPin {5};
constexpr byte gelbPin {6};
constexpr byte rotPin {13};

const unsigned long schaltZeit = 2000; // zeit in ms
unsigned long lastSchalt = 0 - schaltZeit;             // merker wann zuletzt geschalten

enum Zustand {rot, rotgelb, gruen, gelb};   // mögliche Zustände
Zustand zustand = rot;                      // Start

void red()                             // je nach Funktion werden die LED's gelöscht und gesetzt
{
  digitalWrite(rotPin, LOW);
  digitalWrite(gelbPin, HIGH);
  digitalWrite(gruenPin, HIGH);
}
void redYellow()
{
  digitalWrite(rotPin, LOW);
  digitalWrite(gelbPin, LOW);
  digitalWrite(gruenPin, HIGH);
}
void green()
{
  digitalWrite(rotPin, HIGH);
  digitalWrite(gelbPin, HIGH);
  digitalWrite(gruenPin, LOW);
}
void yellow()
{
  digitalWrite(rotPin, HIGH);
  digitalWrite(gelbPin, LOW);
  digitalWrite(gruenPin, HIGH);
}

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  pinMode(rotPin, OUTPUT);
  pinMode(gelbPin, OUTPUT);
  pinMode(gruenPin, OUTPUT);
}

/*
  Zustand weiter(Zustand alt)
  {
  switch (alt)
  {
    case rot: return rotgelb;
    case rotgelb: return gruen;
    case gruen: return gelb;
    case gelb: return rot;
  }
  return gelb;
  }
*/

void loop()
{
  if (millis() - lastSchalt >= schaltZeit) // Nach Zeitablauf
  {
    lastSchalt = millis();                 // Schaltzeit merken
    //zustand = weiter(zustand);
    switch (zustand)                         // Je Zustand verzweigen
    {
      case gelb:
        red();
        zustand = rot;
        break;
      case rot :
        redYellow();
        zustand = rotgelb;
        break;
      case rotgelb:
        green();
        zustand = gruen;
        break;
      case gruen:
        yellow();
        zustand = gelb;
        break;
    }
  }
}

Außer dem finde ich die Sketch als Zumutung für Anfänger der versteht nicht mall 10% davon
Das millis nicht benutzt werden ist ok, aber enum, switch, case und die ganze void dazu finde ich nicht so toll.
Ist aber nur maine Meinung.

1000Dank! es funktioniert perfekt mit euren Hinweisen... ich werde jetzt erstmal nach und nach die Befehle durcharbeiten, welcher Befehl was bewirkt in dem Sketch...

Nicht Befehle!
Sondern Anweisungen und Ausdrücke.

Ich traue einem "Anfänger" zu, dass er die Codevorschläge durchgeht und nachfragt, wenn etwas unklar ist oder hinterfragt, wenn unklar ist, warum Elemente umgesetzt wurden wie sie umgesetzt sind.

Das ist übrigens der Sinn von so einem Forum.
Es kommt eine Frage (vom TO)
es gibt einen Vorschlag (von xy)
es gibt eine Rückfrage (von jemanden anderen, ja das darf auch ich sein)
es gibt eine Antwort (von xy)
es ergeben sich daraus weitere Vorschläge

Ich habe etwas an xy's code kritisiert - aber das ist nicht als Angriff geschrieben und ich zeige auch meine Alternative. So läuft eine normale Diskussion ab.

void im Sinne von ungültig finde ich höchstens deinen Post, Beiträge hinter denen zweifelslos Zeit und Hinschmalz anderer Helfer steht, als Zumutung abzutun.

3 Likes

Der Sketch den noiasca gepostet hat ist ganz gut. Teilweise tradiert er aber die schlechten Angewohnheiten die man in ganz vielen Programmen antrifft.

Das hat in gewisser Weise etwas gutes weil man dadurch lernt was diese schlechten Angewohnheiten bedeuten. Was ich an diesen Angewohnheiten schlecht finde ist, dass sie es Anfängern erschweren den Code zu verstehen.

Ich behaupte:
Die Codeversion die ich geschrieben habe macht das besser weil für Anfänger leichter verständlich.
Ich nenne das TiC Tutorial intergrated Coding

Ausnahmslos JEDER Experte wird meine Codeversion mit der gleichen Geschwindigkeit verstehen wie die mit den schlechten Angewohnheiten.
Unterschied: meine ist für Anfänger leichter verständlich.
Also ihr lieben Experten WO ist das Problem?

Meine Antwort: Bequemlichkeit sich nicht die Zeit zu nehmen besser selbsterklärende Namen zu finden und mit für Experten überflüssigen Kommentaren zu ergänzen.

Wenn jetzt jemand antworten wollte "das ist aber schlechter Programmierstil so viele Kommentare reinzuschreiben!"
Das macht das Programm unübersichtlich!

Meine Antwort: "Ach Du armer Experte mut du dich durch Kommentare durchwühlen die du eh verstehst? ooch das ist aber anspruchsvoll!
Das ist ja viiiel schwieriger als wenn ein Anfänger sich durch sehr ähnlich lautende und doch nicht wirklich selbsterklärende Namen durchkämpfen muss!

Werde einfach ein noch besserer Experte der

  • konsequent immer wirklich gut selbsterklärende Namen verwendet
  • seinen Lesestil trainiert, um für Experten "überflüssige" Kommentare ganz schnell zu erfassen und zu überspringen.
constexpr byte gruenPin {5};
constexpr byte gelbPin {6};
constexpr byte rotPin {13};

const unsigned long PhasenDauer = 2000; // zeit in ms
unsigned long PhasenStartZeitpunkt; // Merker wann zuletzt eine Ampelphase begann
unsigned long aktuelleZeit;
enum AmpelPhasenMENGE {rot, rotgelb, gruen, gelb};   // mögliche Zustände der Ampel

// definiert eine Variable mit Namen "AmpelPhase" 
// diese Variable hat den Typ "AmpelPhasenMENGE" 
AmpelPhasenMENGE AmpelPhase = rot;  // weise am Anfang der Variable das Element "rot" zu                     

// LEDs haben eine gemeinsame Anode
// Kathodenanschluss auf LOW schalten bedeutet
// Anode +5V  Kathode am IO-pin LOW = 0V  SpannungsUNTERschied 
// => Strom fließt => LED leuchtet

// Anode +5V  Kathode am IO-pin HIGH = +5V  KEIN Spannungsunterschied 
// => KEIN Strom fließt LED ist aus

void red() {                  // je nach Funktion werden die LED's gelöscht und gesetzt
  digitalWrite(rotPin, LOW);  
  digitalWrite(gelbPin, HIGH);
  digitalWrite(gruenPin, HIGH);
}

void redYellow(){
  digitalWrite(rotPin, LOW);
  digitalWrite(gelbPin, LOW);
  digitalWrite(gruenPin, HIGH);
}

void green() {
  digitalWrite(rotPin, HIGH);
  digitalWrite(gelbPin, HIGH);
  digitalWrite(gruenPin, LOW);
}

void yellow() {
  digitalWrite(rotPin, HIGH);
  digitalWrite(gelbPin, LOW);
  digitalWrite(gruenPin, HIGH);
}

void setup() {
  Serial.begin(115200);
  Serial.println(F("setup Start"));
  pinMode(rotPin, OUTPUT);
  pinMode(gelbPin, OUTPUT);
  pinMode(gruenPin, OUTPUT);
}


// Definition einer function mit Namen "naechstePhase" 
// die Werte vom Typ AmpelPhasenMENGE zurückgibt
// man übergibt der function die momentan aktuelle Ampelphase
// und die function gibt die nächste Ampelphase als Rückgabewert zurück
AmpelPhasenMENGE naechstePhase(AmpelPhasenMENGE jetzigePhase) {

// Der Parameter hat den Namen "jetzigePhase" 
// und ist ebenfalls vom Variablentyp AmpelPhasenMENGE

  switch (jetzigePhase) {  // wenn die Variable "jetzigePhase" 
    
    case rot:  // den Wert "rot" hat  
      return rotgelb;  // beende function mit Rückgabewert rotgelb
    
    case rotgelb:  // den Wert "rotgelb" hat  
      return gruen;    // beende function mit Rückgabewert gruen
    
    case gruen:   // den Wert "gruen" hat  
      return gelb;     // beende function mit Rückgabewert gelb
    
    case gelb:   // den Wert "gelb" hat  
      return rot;      // beende function mit Rückgabewert rot
  }
  // wenn ein Wert übergeben wurde der KEINEM der obigen vier Werte
  // entpsricht
  return gelb;  // beende functiom mit Rückgabewert gelb
}


void loop() {

  // function millis() liefert die aktuelle "Uhrzeit" 
  // im Sinne von Anzahl Millisekunden seit Programmstart
  aktuelleZeit = millis(); 
  
  // berechne die vergangene Zeit seit PhasenStartZeitpunkt
  // und vergleiche ob größer / gleich PhasenDauer
  if (aktuelleZeit - PhasenStartZeitpunkt >= PhasenDauer) {
    // wenn mehr Millisekunden als in Variable "PhasenDauer"
    // angegeben sind, vergangen sind 
    PhasenStartZeitpunkt = aktuelleZeit; // speichere die aktuelle Zeit 

    // ermittle aus der momentanen Ampelphase die nächste Phase
    AmpelPhase = naechstePhase(AmpelPhase); 

    // schalte die LEDs abhängig von der aktuellen Phase
    switch (AmpelPhase) {                          // Je Zustand verzweigen
    
      case rot:
        red();
        break; // springe SOFORT ans ENDE-SWITCH-CASE
        
      case rotgelb:
        redYellow(); 
        break; // springe SOFORT ans ENDE-SWITCH-CASE
        
      case gruen:
        green();
        break; // springe SOFORT ans ENDE-SWITCH-CASE
        
      case gelb:
        yellow();
        break; // springe SOFORT ans ENDE-SWITCH-CASE
        
    } //ENDE-SWITCH-CASE
  }
}

vgs