Anleitung: Endlicher Automat mit millis()

genau das war auch mein erster gedanke, ich hatte einen switch case gebastelt, der aber lief nur mit dem distance messer und nicht auch in verbindung mit den buttons- aber mann muss dazu sagen, ich bin totaler anfänger und dies ist mein erstes projekt. Genau das ist die frage: Wie wird delay() in einem Unterprogramm ersetzt ? Ein unterprogramm, welches man dann dann verschiedenen Bewegungen wie VORWÄRTS; TURN(); BACK; etc zuordnen kann, mit veränderbaren Zeiten (dauer)

  • ok also ich kann das ja neu posten ABER bevor ich jetzt was neues aufmache:
    Gibt es vielleicht noch mehr Leute da draußen die das Lesen?
    hat jemand eine idee?

buttonswitch.ino (4.79 KB)

Nehmen wir erst mal einen besseren Namen für ein Unterprogramm in der strukturierten Programmierung: Funktion

Funktionen können Rückgabewerte haben. Man kann eine Funktionen einen bool zurückgeben lassen. False wenn die Zeit noch nicht abgelaufen ist. True wenn sie abgelaufen ist. Dann ruft man die Funktion ständig auf und weiß in der aufrufenden Funktion ob die Zeit vorbei ist und etwas gemacht wurde. Dann brauchst du noch etwas Logik damit der eigentliche Code nur einmal ausgeführt und sonst der "Delay" Teil.

Hier musst du auch lernen was "static" macht: http://www.arduino.cc/en/Reference/Static Lokale statische Variablen behalten ihren Wert von einem Funktionsaufruf zum nächsten.

Aber lies dich generell in das Thema endliche Automaten ein. Was du da machst ist eine Paradebeispiel dafür. Das kann man nämlich auch besser lösen und die Verzögerung in einen extra Zustand, d.h. eine eigene Funktion auslagern. Dann wechselt man Funktion1 -> Verzögerung -> Funktion2

super! ich verstehe: ich mache die ganze zeit eine anfrage, darunter fällt auch die zeit und die buttons sind auch dabei. static versteh ich noch nicht ganz kommt viellicht noch.

aber was meinst du damit: "Aber lies dich generell in das Thema endliche Automaten ein. Was du da machst ist eine Paradebeispiel dafür. Das kann man nämlich auch besser lösen und die Verzögerung in einen extra Zustand, d.h. eine eigene Funktion auslagern. Dann wechselt man Funktion1 -> Verzögerung -> Funktion2"

gibt es dafür ein beispiel? 1000 dank!!

tinaki: gibt es dafür ein beispiel?

Schau mal an in welchem Thread du bist. Ist im ersten Post erklärt.

Das ist leicht anders als was du willst. Da wird jeder Zustand zeitgesteuert. Aber ist auch kein Problem nur einen Zustand WARTEN zu haben und nur in diesem den Zustandsübergang per Zeit zu machen. Aber das Grundgerüst ist das gleiche.

Endliche Automaten kann man auch auf andere Arten realisieren. Aber switch/case ist am einfachsten und reicht völlig aus wenn sich die Zustände in Grenzen halten

Beispiele von mir zum Thema Endlicher Automat mit millis() und Schalterzustand: fade millis LED zeiversetzt schalten Arduino UNO, 2Schalter, 1LED

@agmue Ich hab mich auch mal an so etwas versucht: http://forum.arduino.cc/index.php?topic=165552.0 In der neusten LCDMenuLib ist es auch enthalten.

Aber solange es die Delay Funktion in der Arduino Umgebung gibt wird dieses als erste Wahl bevorzugt.

Diesen Code in einer Lib zu verstecken wäre aber auch nicht fair :grin:

#undef delay()
#define delay(x)

An agmue

Herzlichen dank suchte schon eine kleine Ewigkeit genau das. weil das delay ein biserl verteufelt ist.

mfg. Kapitano

@Jomelo: Gegenüber dem, was Du machst, spiele ich nur in der Kreisklasse 8)

@Kapitano: Danke, freue mich :)

@agmue: Es ist echt prima, dass du hier so eine tolle Anleitung gegen dieses schon oft und zu Recht verwünschte "delay(x)" geschrieben hast.

Ich kann mich noch gut an meine Anfänge gegen Ende 2014 erinnern, als ich mich selbst auf ein Ampelprogramm für eine Modelleisenbahn gestürzt habe. Dabei hat mir das delay(x) fast Finger und Ohren gleichzeitig gebrochen... damals habe ich händeringend nach Lösungen gesucht. Diese habe ich dann Mitte 2015 auch mit "millis()" gefunden und setze delay(x) heute nur noch bei kleinen Dreizeilern ein, wenn ich "mal eben" schnell etwas testen möchte. Oder innerhalb "void setup()" für eine bewusste Verzögerung nach einer Art "Splash-Screen" fürs LCD-Display.

Wäre es für deine Anleitung nicht eine geeignete Ergänzung, wenn man deine Ampel jetzt mit einer Anforderung für Fußgänger erweitert? Hier fast vor der Haustür beobachte ich nämlich täglich an einer Kreuzung, wie die Ampeln brav den Verkehr regeln. Wenn aber nun jemand über die Straße möchte drückt er einen Knopf, die Ampel zeigt blinkend "Warten" an, und in der nächsten Rot-Phase für die Autos darf der Fußgänger dann losspurten. Das wäre in meinen Augen doch eine ideale Erweiterung - für dein Tutorial - und auch für die Modelleisenbahn - oder ähnliche Gebiete?!

LG, Rudi

agmue:
@Jomelo: Gegenüber dem, was Du machst, spiele ich nur in der Kreisklasse 8)

@Kapitano: Danke, freue mich :slight_smile:

@agmue

Bitte kannst du mir einen tipp geben wo ich #include <SM.h> diese Datei herunterladen kann suche schon stunden im netz.
bin ja noch in den Kinderschuhen und muss alles ausprobieren und lernen. (Arduino ca. 4 Monate und absolut keine Vorkenntnisse).
danke im voraus
mfg. Kapitano

Ich verstehe manchmal einfach nicht warum so viele Probleme mit Delay haben. Eventuell muss man doch nur mal darüber nachdenken was ein Funktionsname bedeutet.

Wenn ich dem Prozessor sage "Mach mal Pause" dann macht der das und ich kann doch nicht erwarten das er was anderes tut, oder ?

Auf jeden Fall ein nettes Tutorial.

Ulli

beeblebrox:
Ich verstehe manchmal einfach nicht warum so viele Probleme mit Delay haben.

Die meisten Probleme entstehen, weil es delay überhaupt gibt und der Umstieg auf millis dann so schwer fällt.

Kapitano:
Bitte kannst du mir einen tipp geben wo ich #include <SM.h> diese Datei herunterladen kann suche schon stunden im netz.

Dann scheinst Du diese Seite übersehen zu haben: “Code removed by author”. Zwei Vorschläge:

  • Du schickst eine persönliche Meldung an jurs, der hat die Bibliothek möglicherweise noch auf seiner Festplatte.
  • Du machst Dir einen endlichen Automaten (SM → finite state machine → FSM) selbst. Ich mag die Variante mit switch/case, die auch für Anfänger geeignet ist :slight_smile:

RudiDL5:
Wäre es für deine Anleitung nicht eine geeignete Ergänzung, wenn man deine Ampel jetzt mit einer Anforderung für Fußgänger erweitert?

Danke für Dein Lob! Es kribbelt mir bei dem Gedanken an die Ampel schon in den Fingern. Nur tue ich mich mit der verständlichen Beschreibung schwer. Ich könnte mir auch vorstellen, ich mache es prozedural und Du mit OOP.

Deinen Vorschlag werde ich überdenken!

RudiDL5:
Wenn aber nun jemand über die Straße möchte drückt er einen Knopf, die Ampel zeigt blinkend “Warten” an, und in der nächsten Rot-Phase für die Autos darf der Fußgänger dann losspurten. Das wäre in meinen Augen doch eine ideale Erweiterung - für dein Tutorial - und auch für die Modelleisenbahn - oder ähnliche Gebiete?!

Bei einer normalen Kreuzung laufen die Fußgänger einfach mit der Fahrzeugen mit, da bedarf es keiner Anforderungstaste. Wie funktioniert das bei Deinem Vorbild?

@agmue

Danke für deine rasche Antwort. irgendwann werde auch ich so ein Genie werden wie @agmue

nochmals herzliches Danke mfg. Kapitano

@beeblebrox:

Ich verstehe manchmal einfach nicht warum so viele Probleme mit Delay haben.

Da sprichst du ein wahres Wort gelassen aus... Aber so lange von Schulen oder sogar bekannten Tools-Herstellern immer wieder "mal eben" auf das bequeme delay(x) zurückgegriffen wird, ist diese "Seuche" nicht wirklich auszurotten (habe mehrere PDF hier, in denen das vehement eingesetzt wird...).

Ja... aber...

Ich denke, delay(x) hat auch etwas Gutes! Meiner Meinung nach... Vermutlich hat der Arduino nur deshalb so viel Popularität erworben, weil delay(), pinMode(), digitalWrite() usw. enthalten sind. Ohne diese Funktionen wären wahrscheinlich mit Abstand nie so viele Exemplare verkauft worden. Und der Eine oder Andere, der "wirklich" in die Materie einsteigen will, wird sich über kurz oder lang eh von delay() verabschieden und mit millis() flirten. Spätestens bei so eine Ampelmaschine wie von @agmu...

@agmue:

Danke für Dein Lob!

Nix zu danken, war mir eine Ehre ;)

Es kribbelt mir bei dem Gedanken an die Ampel schon in den Fingern

Öhm, dir kribbelt es in den Fingern? Komisch, mir auch... aber gewaltig. Das wäre für 'ne Modelleisenbahn wirklich etwas, was man gut einsetzen kann.

da bedarf es keiner Anforderungstaste

Mag sicherlich sein. Aber hier am Ort sind im Abstand von etwa 1000m sogar zwei Fußgänger-Ampeln an einer Bundesstraße, die ausschließlich nur auf Anforderung umschalten. Sogar asynchron... So etwas in der Art stelle ich mir vor.

Die Ampel von "damals" (Ende 2014) war auch noch hust mit delay(x) gelöst. Das hatte ich mit "switch" und "Array" gelöst, welches im Sekunden-Takt abgearbeitet wurde und die entsprechenden Zustände schalteten... Das Programm ist (leider) einem defekten Lappy zum Opfer gefallen. Aber egal, das war auch nicht der große Bringer.

Und da wir schon dabei sind - was hälst du davon, "unsere" Ampel so zu bauen, dass sie auf Knopfdruck in den Nachtmodus schaltet? Als so dass "nachts" nur noch ein gelbes Warnlicht blinkt. Erst bei Tag geht es wieder (auf Anforderung zunächst) in den Tagesmodus.

Willst du noch einen Joke hören? :D Und am Schluß jibbet dann 2 Knöpfchen, die das Blaulicht von Feuerwehr UND Polizei (natürlich vollkommen asynchron und unabhängig) für eine "gewisse" Zeit einschaltet. Ist das Fahrzeug vorbei, ist auch das Blinken futsch...

Ich denke, DAS wäre doch mal eine Herausforderung?! ;) Und würde sich auf einer Modellbahn sicherlicht gut darstellen.

LG, Rudi

Da ich hier seit Tagen mit 'ner hartnäckigen Erkältung herumhänge und keine Lust auf Outdoor-Aktivitäten habe dachte ich mir, den von @agmue nett gemeinten Vorschlag umzusetzen und sein “Ampel-Programm gegen delay()” tatsächlich in OOP umzusetzen. Es ging einfacher als ursprünglich gedacht. Der nachfolgende Sketch zeigt meine Version seines obigen Programmes. Darin enthalten sind auch meine Ideen zur Ergänzung “Fußgänger-Anforderung”, “Blaulicht auf Knopfdruck” und “Umschaltung Tag-/Nachtmodus”. Der Sketch sieht ein wenig anders aus, als man es von üblichen Arduino-Sketches vielleicht gewöhnt ist, aber das Progi funktioniert wunderbar:

// ----------------------------------------------------------------------------------
// Ampel 1.0 ... für Arduino UNO      
// Mit Bibliothek "MobaTools.h", eigens für dieses Projekt erstellt

#include <MobaTools.h>   

// Parameter "Taster"
const byte tMod     =   2;        //  Taste "Modus"   (Tag-/Nacht)
const byte tPoli    =   3;        //  Taste "Polizei" (Kurzfristiger Blinker)
const byte tUeber   =   4;        //  Taste "Überweg" (Anforderung Fußgänger)

// Parameter "Ampel"
const byte aRot     =   5;        //  LED-Pin 
const byte aGelb    =   6;        //  "
const byte aGruen   =   7;        //  "
const byte aModus   =   8;        //  " (Signal Anforderung Tag-/Nacht-Modus)

const byte fFuss    =   9;        //  LED-Pins (Signal Anforderung f. Fussgänger)
const byte fWeiss   =  10;        //  " (Signal "Warten")
const byte fRot     =  11;        //  "
const byte fGruen   =  12;        //  "

const word zRot     =  30;        //  Sekunden
const word zRotgelb =   2;        //  "
const word zGruen   =  20;        //  "
const word zGelb    =   3;        //  "

// Parameter "Blaulicht"
const byte bPin     =  13;        //  Blaue LED
const word mEin     =  15;        //  Millis EIN
const word mAus     = 150;        //  Millis AUS

const byte wHol     =   2;        //  Wiederholungen
const word mZyk     = 555;        //  Millis Gesamt-Zyklus (Ein + Aus) * Wiederholung inkl. Warten
const word gAnz     =  25;        //  Anzahl kompletter Gesamt-Zyklen

// Instanzen
Taster          Modus(tMod);      //  "Nachtmodus" (Down-Aktiv)
Taster          Polizei(tPoli);   //  "Blaulicht"  (Down-Aktiv)
Taster          Ueberweg(tUeber); //  "Überweg"    (Down-Aktiv)

Warnblinker     Blaulicht( bPin, mEin, mAus, wHol, mZyk, gAnz );

Ampelschaltung  Ampel( aRot,   aGelb,        aGruen, aModus,  \
                       zRot,   zRotgelb,     zGruen, zGelb,   \
                       fWeiss, fRot, fGruen, fFuss            );  

void setup()
{
  Modus.begin();                  //  Buttons / Taster
  Polizei.begin();                //  "
  Ueberweg.begin();               //  "
  
  Ampel.begin();                  //  Ampelschaltung  
  Blaulicht.begin();              //  Blaulicht  
}

void loop()
{
  // --- Permanente Überwachung, Status-Updates etc. ---------------------
  //
  Blaulicht.Run();                //  Abarbeitung Blaulicht, nur bei Anforderung
  Ampel.Run();                    //  Abarbeitung Ampelschaltung, permanent
  
  // --- Aktionen auf Anforderung ----------------------------------------
  //
  if( Modus.KeyDown() )           //  Anforderung Tag-/Nachtmodus
    Ampel.Nachtmodus();           //  Bleibt dann bis neue Anforderung 

  if( Ueberweg.KeyDown() )        //  Anforderung Überweg 
    Ampel.Fussgaenger();          //  Wird in der Rotphase freigegeben
      
  if( Polizei.KeyDown() )         //  Anforderung Polizeieinsatz
    Blaulicht.Start();            //  Blinkt eine Weile, stoppt selbstständig       
}

Wer das Programm testen möchte muß die anhängende Library installieren und einen Blick auf die beigefügte Schaltung werfen. Es ist alles m.E. einigermaßen ausführlich kommentiert. Vielleicht hätte man es auch weniger umständlich programmieren können - aber es erfüllt seinen Zweck und zeigt, wie man mit “ohne delay()” und mit OOP coole Dinge zaubern kann.

LG, Rudi

MobaTools.zip (5.76 KB)

Inzwischen konnte ich mir eine westfälische Ampel mit weißer Schrift “Bitte warten” als Anforderungsquittung im Original ansehen. Leider bin ich nicht der geniale Programmierer (siehe #17) und Rudi hat hinsichtlich allgemeiner Programmiererfahrung mir gegenüber einigen Vorsprung. Hinsichtlich Arduino ist er gerade auf der Überholspur.

Daher gehört mein prozedurales Programm eigentlich vor seine OOP-Variante. Der geneigte Leser möge sich das bitte so vorstellen.

Die Aufgabenstellung entspricht der von Rudi formulierten, allerdings gebe ich Oma und Opa etwas mehr Zeit zum Queren der Straße. Auch die Schaltung habe ich übernommen, nur nutze ich interne PullUp-Widerstände, bin halt faul.

Ich veröffentliche meinen Sketch in der Hoffnung, jemand kann einen Nutzen daraus ziehen. Er ist nicht für sicherheitsrelevante Anwendungen gedacht :slight_smile:

const byte pin[] = {
  2,        //  0  Taste "Modus"   (Tag-/Nacht)
  3,        //  1  Taste "Polizei" (Kurzfristiger Blinker)
  4,        //  2  Taste "Überweg" (Anforderung Fußgänger)
  5,        //  3  LED-Pin "Fahrzeuge Rot"
  6,        //  4  LED-Pin "Fahrzeuge Gelb"
  7,        //  5  LED-Pin "Fahrzeuge Grün"
  11,       //  6  LED-Pin "Fußgänger Rot"
  12,       //  7  LED-Pin "Fußgänger Grün"
  8,        //  8  LED-Pin "Signal Anforderung Tag-/Nacht-Modus"
  9,        //  9  LED-Pin "Signal Anforderung f. Fussgänger"
  10,       // 10  LED-Pin "Signal Warten"
  13        // 11  LED-Pin "Polizei Blaulicht"
};
enum {AUTOGRUEN, WARTEN, AUTOGELB, AUTOROT, FUSSGRUEN, FUSSROT, ROTGELB, GELBBLINKAN, GELBBLINKAUS};
const byte muster[] = { // Jedes Bit entspricht einem Ausgang, mit ampelStatus als Index
  // Ausgänge: Auto Rot, Auto Gelb, Auto Grün, Fuss rot, Fuss Grün
  B00110000,  // AUTOGRUEN
  B00110000,  // WARTEN
  B01010000,  // AUTOGELB
  B10010000,  // AUTOROT
  B10001000,  // FUSSGRUEN
  B10010000,  // FUSSROT
  B11010000,  // ROTGELB
  B01000000,  // GELBBLINKAN
  B00000000   // GELBBLINKAUS
};

unsigned long ampelMillis, blaulichtMillis, fussWartenMillis, aktMillis;
byte ampelStatus, aktTaster, altTaster;
const unsigned int fussWartenIntervall = 500, blaulichtIntervall = 300, blaulichtZaehlerMax = 111;
unsigned int blaulichtZaehler;
const byte intervall[] = { // Zeit in Zehntelsekunden mit ampelStatus als Index
  200, // minimale Grünphase für Autos
  0, // keine Verzögerung bei Tastendruck
  20, // Gelbphase für Autos
  20, // Rotphase für Autos, bis Fußgängerampel auf Grün geht
  100, // Grünphase für Fußgänger
  200, // Rotphase für Fußgänger, damit auch Oma und Opa die Straße verlassen können
  20, // Rot+Gelbphase für Autos
  5, // Zeit für Nachtblinklicht
  5 // Zeit für Nachtblinklicht
};

void setup() {
  for (byte j = 0; j < 3; j++) {
    pinMode(pin[j], INPUT_PULLUP); // bin faul, verwende die eingebauten PullUps
  }
  for (byte j = 3; j < sizeof(pin); j++) {
    pinMode(pin[j], OUTPUT);
  }
  ausgabe(muster[ampelStatus]);
  aktTaster = eingabe();
  altTaster = aktTaster;
}

// Mit UNO ginge das einfacher, aber hier habe ich einen ATtiny mit Portexpander im Hinterkopf
void ausgabe(byte bitmuster) {
  for (byte j = 0; j < 5; j++) {
    digitalWrite(pin[j + 3] , bitmuster & (0x80 >> j));
  }
}

// Mit UNO ginge das einfacher, aber hier habe ich einen ATtiny mit Portexpander im Hinterkopf
byte eingabe() {
  byte bitmuster = 0;
  for (byte j = 0; j < 3; j++) {
    bitmuster |= digitalRead(pin[j]) << (7 - j);
  }
  return bitmuster;
}

void loop() {
  aktMillis = millis();
  altTaster = aktTaster;
  aktTaster = eingabe();
  if ((altTaster & B01000000) && !(aktTaster & B01000000)) { // negative Flanke Taste "Polizei" (Kurzfristiger Blinker)
    blaulichtZaehler = blaulichtZaehlerMax;
  }
  if ((altTaster & B10000000) && !(aktTaster & B10000000)) { // negative Flanke Taste "Modus"   (Tag-/Nacht)
    digitalWrite(pin[8], HIGH);
  }
  if ((altTaster & B00100000) && !(aktTaster & B00100000)) { // negative Flanke Taste "Überweg" (Anforderung Fußgänger)
    digitalWrite(pin[9], HIGH);
  }

  if (aktMillis - ampelMillis >= (intervall[ampelStatus] * 100)) { // Verzögerung für endlichen Automaten
    ampelMillis = aktMillis;
    switch (ampelStatus) {
      case AUTOGRUEN: // 0  minimale Grünphase für Autos
        ampelStatus = WARTEN;
        break;
      case WARTEN: // 1  Grün für Autos, bis Umschaltung auf Nachtmodus oder ein Fußgänger will über die Straße
        if (digitalRead(pin[8])) {
          digitalWrite(pin[8], LOW);
          ampelStatus = GELBBLINKAN; // Nachtmodus
        }
        if (digitalRead(pin[9])) ampelStatus = AUTOGELB; // Ein Fußgänger will die Straße queren!
        break;
      case AUTOGELB: // 2  Gelbphase für Autos
        ampelStatus = AUTOROT;
        break;
      case AUTOROT: // 3  Rotphase für Autos, bis Fußgängerampel auf Grün geht
        digitalWrite(pin[9], LOW);
        ampelStatus = FUSSGRUEN;
        break;
      case FUSSGRUEN: // 4  Grünphase für Fußgänger
        ampelStatus = FUSSROT;
        break;
      case FUSSROT: // 5  Rotphase für Fußgänger
        ampelStatus = ROTGELB;
        break;
      case ROTGELB: // 6  Rot+Gelbphase für Autos
        ampelStatus = AUTOGRUEN;
        break;
      case GELBBLINKAN: // 7  Nachtblinklicht
        if (digitalRead(pin[8])) {
          digitalWrite(pin[8], LOW);
          ampelStatus = AUTOGRUEN;
        } else ampelStatus = GELBBLINKAUS;
        break;
      case GELBBLINKAUS: // 8  Nachtblinklicht
        if (digitalRead(pin[8])) {
          digitalWrite(pin[8], LOW);
          ampelStatus = AUTOGRUEN;
        } else ampelStatus = GELBBLINKAN;
        break;
    }
    ausgabe(muster[ampelStatus]);
  }
  // Blinker für Blaulicht
  if (blaulichtZaehler > 0) {
    if (aktMillis - blaulichtMillis >= blaulichtIntervall) {
      blaulichtMillis = aktMillis;
      digitalWrite(pin[11], !digitalRead(pin[11]));
      blaulichtZaehler--;
    }
  } else {
    digitalWrite(pin[11], LOW);
  }
  // Blinker für "Signal Warten" als Reaktion auf Anforderung durch Fußgänger
  if (digitalRead(pin[9])) {
    if (aktMillis - fussWartenMillis >= fussWartenIntervall) {
      fussWartenMillis = aktMillis;
      digitalWrite(pin[10], !digitalRead(pin[10]));
    }
  } else {
    digitalWrite(pin[10], LOW);
  }
}

Konstruktive Kritik ist wie immer willkommen!

...gegenüber einigen Vorsprung. Hinsichtlich Arduino ist er gerade auf der Überholspur...

Hallo agmue, herzlichen Dank fürs Lob! Aber bitte nicht zu viel davon, sonst hebe ich bei meiner Geschwindigkeit noch ab... :D

Dein Code sieht für mich schon recht aufgeräumt und übersichtlich aus. Einen echten Test werde ich später machen, weil auf meinem einzigen UNO-Steckbrett gerade etwas anderes in der Entwicklung ist. Aber es stimmt: Wenn man einmal in Sachen OOP "Blut geleckt hat" und die eigenen Ideen optimal funktionieren, wird man zu immer mehr Leistung beflügelt. Vor allen Dingen auch, weil die Details später "aus dem Kopf" sind und man sich "nur noch" auf die Instanzen und die übergeordnete Funktionalität konzentrieren braucht. (Ich bin schon wieder einige Schritte weiter als letztens demonstriert...)

Im Sketch schreibst du etwas von "ATTiny mit Portexpander". Ich hatte schon ähnliche Gedanken, mehrere Ampeln und andere Leuchtelemente von einer Zentrale aus über I2C zu steuern. Hier kommt dann auch eine Frage von "Doc_Arduino" auf den Plan (er trainiert ja gerade ebenfalls OOP), wie sich das mit den maximalen Leitungslängen verhält. Ich werde diese Beiträge aufmerksam verfolgen, zumal ich mit meiner eigenen Modellbahn auch noch nicht ganz "im Reinen" bin. Es wäre aber ideal, wenn meine Häuser-Modelle mit den digitalen Highlights auf einer größeren Modellbahn-Platte kombiniert werden können und daduch "Leben in die Bude kommt".

Gerade entsteht im Hinterkopf ein Bild von einer Modellbahn, auf der dann alles mit eigenen digitalen Komponenten gesteuert wird und lustig blitzt und blinkt. Haaaachhh "schwärm"...

Bis denne mal und lieben Gruß Rudi ;)

Angeregt durch das Thema Interrupt MCP23017 habe ich meinen Sketch an den über I2C angeschlossenen MCP23017 angepaßt. Anders als beim Interrupt-Thema verwende ich die Bibliothek Adafruit_MCP23017, um beispielsweise mcp.digitalWrite() verwenden zu können. Dies soll ja eine Anleitung sein, die nicht durch Bitschubserei irritiert. Ein wenig davon habe ich aber dann doch drin.

Das Blaulicht flackert inzwischen unregelmäßig, könnte man zum Fernsehflakern oder Schweißlich ausbauen.

#include <Wire.h>
#include "Adafruit_MCP23017.h"        // MCP23017 mittels I2C angeschlossen
Adafruit_MCP23017 mcp;
#define MCPI2CADR 0        // alle Adresspins an GND, Grundadresse 0x20 wird von der Bibliothek addiert.
const byte eingPin[] = {
  0,        //  0  GPA0 ICpin21  Taste "Überweg" (Anforderung Fußgänger)
  1,        //  1  GPA1 ICpin22  Taste "Modus"   (Tag-/Nacht)
  2         //  2  GPA2 ICpin23  Taste "Polizei" (Kurzfristiger Blinker)
};
const byte ausgPin[] = {
  6,        //  0  GPA6 ICpin27  LED-Pin "Signal Anforderung Tag-/Nacht-Modus"
  7,        //  1  GPA7 ICpin28  LED-Pin "Signal Anforderung f. Fussgänger"
  8,        //  2  GPB0 ICpin01  LED-Pin "Fahrzeuge Rot"
  9,        //  3  GPB1 ICpin02  LED-Pin "Fahrzeuge Gelb"
  10,       //  4  GPB2 ICpin03  LED-Pin "Fahrzeuge Grün"
  11,       //  5  GPB3 ICpin04  LED-Pin "Fußgänger Rot"
  12,       //  6  GPB4 ICpin05  LED-Pin "Fußgänger Grün"
  14,       //  7  GPB6 ICpin07  LED-Pin "Signal Warten"
  15        //  8  GPB7 ICpin08  LED-Pin "Polizei Blaulicht"
};                 

enum {AUTOGRUEN, WARTEN, AUTOGELB, AUTOROT, FUSSGRUEN, FUSSROT, ROTGELB, GELBBLINKAN, GELBBLINKAUS};
const byte muster[] = {        // Jedes Bit entspricht einem Ausgang, mit ampelStatus als Index
  // Ausgänge: Auto Rot, Auto Gelb, Auto Grün, Fuss rot, Fuss Grün
  B00110000,  // AUTOGRUEN
  B00110000,  // WARTEN
  B01010000,  // AUTOGELB
  B10010000,  // AUTOROT
  B10001000,  // FUSSGRUEN
  B10010000,  // FUSSROT
  B11010000,  // ROTGELB
  B01000000,  // GELBBLINKAN
  B00000000   // GELBBLINKAUS
};
const byte intervall[] = {        // Zeit in Zehntelsekunden mit ampelStatus als Index
  200,  // minimale Grünphase für Autos
  0,    // keine Verzögerung bei Tastendruck
  20,   // Gelbphase für Autos
  20,   // Rotphase für Autos, bis Fußgängerampel auf Grün geht
  100,  // Grünphase für Fußgänger
  200,  // Rotphase für Fußgänger, damit auch Oma und Opa die Straße verlassen können
  20,   // Rot+Gelbphase für Autos
  5,    // Zeit für Nachtblinklicht an
  5     // Zeit für Nachtblinklicht aus
};
unsigned long ampelMillis, blaulichtMillis, fussWartenMillis, aktMillis;
bool aktTaster[sizeof(eingPin)], altTaster[sizeof(eingPin)];
byte ampelStatus = 1;
const unsigned int fussWartenIntervall = 500, blaulichtZaehlerMax = 111;
const unsigned int blaulichtIntervall[] = {200, 75, 100, 75};
const byte anzBlaulichtIntervall = sizeof(blaulichtIntervall) / sizeof(blaulichtIntervall[0]);
unsigned int blaulichtZaehler;

void setup() {
  mcp.begin(MCPI2CADR);
  for (byte j = 0; j < sizeof(eingPin); j++) {
    mcp.pinMode(eingPin[j], INPUT);
    mcp.pullUp(eingPin[j], HIGH);   // bin faul, verwende die eingebauten PullUps; Taster gegen GND
    aktTaster[j] = mcp.digitalRead(eingPin[j]);
    altTaster[j] = aktTaster[j];
  }
  for (byte j = 0; j < sizeof(ausgPin); j++) {
    mcp.pinMode(ausgPin[j], OUTPUT);
  }
  for (byte j = 0; j < sizeof(ausgPin); j++) {  // LED-Test
    mcp.digitalWrite(ausgPin[j], HIGH);
    delay(100);
  }
  for (byte j = 0; j < sizeof(ausgPin); j++) {
    mcp.digitalWrite(ausgPin[j], LOW);
    delay(100);
  }
}

void loop() {
  aktMillis = millis();
  for (byte j = 0; j < sizeof(eingPin); j++) {
    altTaster[j] = aktTaster[j];
    aktTaster[j] = mcp.digitalRead(eingPin[j]);
  }
  if (altTaster[0] && !aktTaster[0]) {        // negative Flanke Taste "Überweg" (Anforderung Fußgänger)
    mcp.digitalWrite(ausgPin[0], HIGH);
  }
  if (altTaster[1] && !aktTaster[1]) {        // negative Flanke Taste "Modus"   (Tag-/Nacht)
    mcp.digitalWrite(ausgPin[1], HIGH);
  }
  if (altTaster[2] && !aktTaster[2]) {        // negative Flanke Taste "Polizei" (Kurzfristiger Blinker)
    blaulichtZaehler = blaulichtZaehlerMax;
  }

  if (aktMillis - ampelMillis >= (intervall[ampelStatus] * 100)) {    // Verzögerung für endlichen Automaten
    ampelMillis = aktMillis;
    switch (ampelStatus) {
      case AUTOGRUEN:           // 0  minimale Grünphase für Autos
        ampelStatus = WARTEN;
        break;
      case WARTEN:              // 1  Grün für Autos, bis Umschaltung auf Nachtmodus oder ein Fußgänger will über die Straße
        if (mcp.digitalRead(ausgPin[1])) {
          mcp.digitalWrite(ausgPin[1], LOW);
          ampelStatus = GELBBLINKAN;                                // Nachtmodus
        }
        if (mcp.digitalRead(ausgPin[0])) ampelStatus = AUTOGELB;            // Ein Fußgänger will die Straße queren!
        break;
      case AUTOGELB:            // 2  Gelbphase für Autos
        ampelStatus = AUTOROT;
        break;
      case AUTOROT:             // 3  Rotphase für Autos, bis Fußgängerampel auf Grün geht
        mcp.digitalWrite(ausgPin[0], LOW);
        ampelStatus = FUSSGRUEN;
        break;
      case FUSSGRUEN:           // 4  Grünphase für Fußgänger
        ampelStatus = FUSSROT;
        break;
      case FUSSROT:             // 5  Rotphase für Fußgänger
        ampelStatus = ROTGELB;
        break;
      case ROTGELB:             // 6  Rot+Gelbphase für Autos
        ampelStatus = AUTOGRUEN;
        break;
      case GELBBLINKAN:         // 7  Nachtblinklicht
        if (mcp.digitalRead(ausgPin[1])) {
          mcp.digitalWrite(ausgPin[1], LOW);
          ampelStatus = AUTOGRUEN;
        } else ampelStatus = GELBBLINKAUS;
        break;
      case GELBBLINKAUS:        // 8  Nachtblinklicht
        if (mcp.digitalRead(ausgPin[1])) {
          mcp.digitalWrite(ausgPin[1], LOW);
          ampelStatus = AUTOGRUEN;
        } else ampelStatus = GELBBLINKAN;
        break;
    }
    ausgabe(muster[ampelStatus]);
  }
  // Blinker für Blaulicht
  if (blaulichtZaehler > 0) {
    if (aktMillis - blaulichtMillis >= blaulichtIntervall[blaulichtZaehler % anzBlaulichtIntervall]) {
      blaulichtMillis = aktMillis;
      mcp.digitalWrite(ausgPin[8], !mcp.digitalRead(ausgPin[8]));
      blaulichtZaehler--;
    }
  } else {
    mcp.digitalWrite(ausgPin[8], LOW);
  }
  // Blinker für "Signal Warten" als Reaktion auf Anforderung durch Fußgänger
  if (mcp.digitalRead(ausgPin[0])) {
    if (aktMillis - fussWartenMillis >= fussWartenIntervall) {
      fussWartenMillis = aktMillis;
      mcp.digitalWrite(ausgPin[7], !mcp.digitalRead(ausgPin[7]));
    }
  } else {
    mcp.digitalWrite(ausgPin[7], LOW);
  }
}

void ausgabe(byte bitmuster) {
  for (byte j = 0; j < 5; j++) {
    mcp.digitalWrite(ausgPin[j + 2] , bitmuster & (0x80 >> j));
  }
}

Gut zu wissen das es für die Dinger eine Bibliothek gibt :)