Fußgängerampel, nur anders...

Hallo Zusammen,

ich würde mich freuen wenn ihr mir weiterhelfen könntet. Ich komme mit meinen Überlegungen nicht zu einer Lösung.

Beschreibung:

Eine Fußgängerampel hat einen Ampeldrücker mit zwei Schaltern. Einer für den Sehenden, einer für den Blinden. Der Ampeldrücker selber hat drei Eingänge (Leuchtfeld "Warten", Grün, Blinder).
Somit habe ich am Arduino pro Drücker 2 Eingänge und 3Ausgänge.
Es werden insgesamt 8 Drücker an 8 einzelnen imaginären Ampeln angeschlossen.
Also insgesamt 16 Eingänge und 24 Ausgänge.
Die Eingänge habe ich mittels zweier 4051 Multiplexer (2x8=16) am Arduino realisiert. Angesteuert über drei Select Pins und einer Zählschleife werden die Anforderungen in ein Array [16] geschrieben. Soweit im Sketch fertig und funktioniert.
Die Ausgabe soll über drei Schieberegister 74HC595 (3x8=24) erfolgen.

Ablauf für eine der acht Ampeln die unabhängig voneinander laufen sollen:

Grundstellung ist Rot, erfolgt eine Anforderung des Sehenden,
wird der Blinker für das "Warte" (Ausgang 1) eingeschaltet.
Eine Umschaltzeit (t) läuft ab damit man die Aktion Warteblinker (Rotzeit) sieht, in der geprüft wird ob in dieser noch zusätzlich ein Blinder anfordert.
Sollte dieses der Fall sein wird nach t Fg-Grün (Ausgang 2) und Ton (Ausgang 3) aktiv, der Blinker schaltet ab.
Nach Ablauf t (Grünzeit) geht alles auf Anfang, also Rot. Sollte kein Blinder angefordert haben, wird nur Grün (A2) aktiv und der Blinker schaltet ab.
Erfolgt bei Rot ausschließlich eine Anfo eines Blinden wird nach t direkt ohne Blinker A2+A3 aktiv, nach Ablauf t (Grünzeit) geht es wieder zurück nach Rot wie oben.

Ich hänge mal ein Bild des Ablaufes in den Anhang.

Mein Problem:

Ich bekomme die Anforderungsschalter ins Array, hier müssten sie solange fixiert werden bis eine Rücksetzung bei Grün erfolgt.
Ebenso bei der Ausgabe, hier muss ein Zusammenhang gemäß der Logik geschaffen werden aus dem Array eine Schalthandlung solange festzuhalten bis die Zeit t abgelaufen ist ohne das Programm einzufrieren weil ja 8 Stk parallel laufen. Mit delay geht das nicht schon klar, mit millis und einer mir derzeit nicht klaren Verbindung ?
Mir fehlt also sozusagen der wichtigste Tel des Programmes wo die eigentliche Arbeit gemacht wird, und ich komm nicht drauf wie.
Meinen angefangenen Sketch hänge ich auch dran, vielleicht habt ihr einen Löungsweg?

Mein bisheriger Sketch:

// LSA Simulation
//Alle Taster der  werden an einen 4051 Multiplexer angeschlossen und über den PIN 5 eingelesen. Steuerbits sind PIN 2,3,4
//Alle Ausgaenge an die Drücker werden über ein 595 Schieberegister ausgegeben angeschlossen an PIN 10
//Autor: 
//Vers.1.0
//01.2017



//Schieberegister (Ausgabe)
                                                      //PIN verbunden mit SH_CP des 74HC595
int shiftPin=8;
                                                      //PIN verbunden mit ST_CP des 74HC595
int storePin=9;
                                                      //PIN verbunden mit DS des 74HC595
int dataPin=10;

int AnfoWert[16];                                      //Variable für die Anforderungen 
int i=0;                                               //Zähler für Schleife die die Werte aus dem Multiplexer ausliest
int bit1=0;                                            //Variablen für die Bit-Ansteuerung des Multiplexers
int bit2=0;
int bit3=0;





void setup() {
  
  
  pinMode(2, OUTPUT);                                  //Pindefinition als Ein oder Ausgang
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, INPUT);
  pinMode(8, OUTPUT);                                                          
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  
  digitalWrite(storePin,LOW);                          //Datenausgabe sicherheitshalber auf LOW
  
  Serial.begin(9600);
  
}

void loop() {
  for ( i=0; i<=7;i++) {
    
    bit1= bitRead(i, 0);                               //Aufteilung der Zahl aus dem Zähler 1 bis 8 in Binärcode auf drei Variablen bit 1 bis 3
    bit2= bitRead(i, 1);                               //damit können dann die select Pins angesteuert werden.
    bit3= bitRead(i, 2);
    
    digitalWrite(2, bit1);                             //Ausgabe der einzelnen Steuerbits auf die Selectpins des Multiplexers, also hier die Ansteuerung
    digitalWrite(3, bit2);
    digitalWrite(4, bit3);
    
    Anfo(i,5);                                        //Abspeichern der ersten 8 Werte in das Array 
    Anfo(i+8,6);                                      //Abspeichern der zweiten 8 Werte in das Array 
    Serial.print(AnfoWert[i]);                        //Ausgabe der ersten 8 Werte des Arrays 
    Serial.print(AnfoWert[i+8]);                      //Ausgabe der zweiten 8 Werte des Arrays 
    Serial.print("   ");
    }
    Serial.println ("");

}


void Anfo (int zaehler, int digitalPin) {
  AnfoWert [zaehler]= digitalRead(digitalPin);
  }

U3600:
Eine Fußgängerampel hat einen Ampeldrücker mit zwei Schaltern. Einer für den Sehenden, einer für den Blinden. Der Ampeldrücker selber hat drei Eingänge (Leuchtfeld "Warten", Grün, Blinder).
Somit habe ich am Arduino pro Drücker 2 Eingänge und 3Ausgänge.

Bei dieser Rechnung komme ich nicht mit! Pro Drücker 2 Eingänge und 1 Ausgang würde ich verstehen :slight_smile:

Lesestoff: Anleitung: Endlicher Automat mit millis() mit "westfälischer" Fußgängerampel.

vielleicht habt ihr einen Löungsweg?

Dir kann ich eine sehr primitive Ampel liefern.

... keine Ahnung was dich zu diesem Thread getrieben hat - was der Anlass war.
... will ich auch nich niederschmettern, aber:

? Warum 2 verschiedene Taster für Blinde und/oder sehende ? Einer reicht - Standard an jeder entsprechend ausgerüsteten Ampel !

? Wozu die leuchtmäßige Angabe "ACHTUNG: HIER HAT EIN BLINDER DIE AMPELANFORDERUNG GEDRÜCKT !!!"
.... Info für Leute , welche diese ..... eh nicht lesen oder nicht lesen können ?

An jeder Behinderten-Gerechten Ampel gibt es einen Summer welcher den Leuten durch unterschiedliche Töne angezeigt, wann die Grün-Phase beginnt und wann die Übergquerung beendet werden sollte, nicht mehr die Strasse betreten werden sollte.

Das finde ich sogar für Leute mit vollständigem Augenlicht sehr OK und oft hilfreich !!!

Wozu deine Gedanken- & Rechenspielerei ?

Hallo und schon mal Danke für die Antworten.
Allerdings habe ich mich wohl so unverständlich ausgedrückt das ich noch mal einhake.

Wie Terwi geschrieben hat ist es geneu richtig und da will ich auch gar nix weiter spinnen.

Schalter:

Ein sogenannter Blindentaster hat wirklich zwei getrennte Anforderungen:

Die erste ist die die alle benutzen wenn sie über die Straße wollen und die Ampel nur umschaltet wenn jemand gedrückt hat. Also das Grün kommt nur auf Anforderung des Fußgängers.
Die zweite Anforderungsmöglichkeit ist den Sehbehinderten vorbehalten, sie befindet sich meist unter dem Gerät, ausgebildet als sogenannter Vibratortaster.
Natürlich kann man an einer Ampel den Blinden immer mit dem sehenden auf Grün schalten. Das abgestrahlte Tonsignal stört aber sehr oft Anwohner, also sollte dieses nur dann kommen wenn wirklich ein Blinder gedrückt hat - in diesem Falle eben auch nur auf Bedarf.

Deshalb zwei Eingänge von jedem Drücker am Arduino.

Licht:

Licht1:

Die "leuchtmäßige Angabe" interessiert natürlich nicht den Blinden. Das Leuchtfeld auf dem Drücker, meist mit "Bitte Warten" oder "Signal kommt" wird dem Sehenden als Quittierung angezeigt damit er sieht das seine Anforderung entgegengenommen wurde. Dieses ist dann sozusagen die Antwort auf die Anforderung und Ausgang 1 wie oben beschrieben. Der Ausgang wird also blinkend gesetzt sobald der Sehende gedrückt hat und wird bei Grünbeginn abgeschaltet. Aktivierung ist also nur möglich bei Rot.

Licht2:

Ist das Fußgängergrün welches vom Arduino eingeschaltet werden soll. (Grün=High, somit Rot=Low, geht also über einen Ausgang)= Ausgang 2 am Arduino

Ton:
Das Blindenfreigabesignal als Ton wird dem Drücker über den Ausgang 3 mitgeteilt, wie oben beschrieben eben nur auf Anforderung. Übrigens vibriert der Taster unter dem Drücker parallel zur Tonausgabe.

Ich bekomme die Ein und Ausgabematrix aber nicht zusammen, lese mich aber in das Thema endlicher Automat mit Millis ein, mal sehen...

Wozu ich das brauche? Ich möchte sehbehinderten Menschen in Schulungen erklären wie diese Systeme funktionieren - und das kann man schlecht an einer realen Ampel im Verkehrslärm..

Gruß, Klaus

Hallo Klaus,
jetzt ist es klarer. Ich beschäftige mich nur mit Ampeln auf Modellen, da gibt es einen Ausgang für die grüne LED und einen weiteren für die rote.

Jetzt verstehe ich auch die zwei Taster (Eingänge) und die drei Ausgänge :slight_smile:

Was hältst Du von meinen Modifikationen, die keine Funktionsveränderungen bringen sollten?

#define ANZAHLAMPELN 8
//Schieberegister (Ausgabe)
const byte mux1Pin = 2;
const byte mux2Pin = 3;
const byte mux3Pin = 4;
const byte muxIn1Pin = 5;
const byte muxIn2Pin = 6;
const byte shiftPin = 8;       //PIN verbunden mit SH_CP des 74HC595
const byte storePin = 9;       //PIN verbunden mit ST_CP des 74HC595
const byte dataPin = 10;       //PIN verbunden mit DS des 74HC595

byte ampel;
bool AnfoSehen[ANZAHLAMPELN];                                      //Variable für die Anforderungen
bool AnfoBlind[ANZAHLAMPELN];                                      //Variable für die Anforderungen

void setup() {
  pinMode(mux1Pin, OUTPUT);                                  //Pindefinition als Ein oder Ausgang
  pinMode(mux2Pin, OUTPUT);
  pinMode(mux3Pin, OUTPUT);
  pinMode(muxIn1Pin, INPUT);
  pinMode(muxIn2Pin, INPUT);
  pinMode(shiftPin, OUTPUT);
  pinMode(storePin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  digitalWrite(storePin, LOW);                         //Datenausgabe sicherheitshalber auf LOW

  Serial.begin(9600);
}

void loop() {
  eingaenge(ampel);
  ampel++;
  ampel = ampel % ANZAHLAMPELN;
}

void eingaenge(byte a)
{
  digitalWrite(mux1Pin, bitRead(a, 0));                             //Ausgabe der einzelnen Steuerbits auf die Selectpins des Multiplexers, also hier die Ansteuerung
  digitalWrite(mux2Pin, bitRead(a, 1));
  digitalWrite(mux3Pin, bitRead(a, 2));

  AnfoSehen[a] = digitalRead(muxIn1Pin);
  AnfoBlind[a] = digitalRead(muxIn2Pin);

  Serial.print(AnfoSehen[a]);                        //Ausgabe der 8 Werte des ersten Arrays
  Serial.print(AnfoBlind[a]);                        //Ausgabe der 8 Werte des zweiten Arrays
  Serial.print("   ");
  Serial.println ("");
}

So weit so gut, bisher funktioniert das erreichte auch weiterhin. Die Schalterabfragen landen sauber in den jetzt 2 Arrays und sind im seriell Monitor darstellbar.

Gruß, Klaus

U3600:
So weit so gut, bisher funktioniert das erreichte auch weiterhin.

Hallo Klaus,
das ist wichtig, weil ich die Multiplexer zum Testen nicht habe.

Ich habe Dir mal einen Ansatz mit Ausgabe auf die Schieberegister und drei Zuständen gebaut. Profis würden das wohl anders machen, aber ich habe je Schieberegister eine Variable spendiert. Sicherlich etwas gewöhnungsbedürftig ist die Bitschubserei mittels (1 << ampel). Ich weiß nicht, wo Du stehst, ob das für Dich leicht durchschaubar ist, bin gespannt.

Wenn Du die AnforderungSehend von Ampel 1 drückst, springt der Status von ROT zu BLINKEN und die Quittung blinkt. Bei erneutem Druck springt der Status zu GRUEN, das Blinken endet und der Ausgang für Grün geht auf HIGH. Bei erneutem Druck springt der Status zu ROT.

Meine speziellen Anpassungen sind gekennzeichnet. Für mich unklar ist, ob die Taster prellen und ob sie gedrückt LOW oder HIGH sind.

Ich hoffe, es funktioniert bei Dir wie bei mir:

#define ANZAHLAMPELN 8
//Schieberegister (Ausgabe)
const byte mux1Pin = 2;
const byte mux2Pin = 3;
const byte mux3Pin = 4;
const byte muxIn1Pin = 5;
const byte muxIn2Pin = 6;
const byte clockPin = 8;       // IC-Pin 11 SH_CP des 74HC595
const byte latchPin = 9;       // IC-Pin 12 ST_CP des 74HC595
const byte dataPin = 10;       // IC-Pin 14 DS des 74HC595

const uint32_t blinkIntervall = 500;                                    // Blinkintervall in Millisekunden
byte ampel;
bool aktAnfoSehen[ANZAHLAMPELN];                                        // Anforderungen aktueller Zustand
bool altAnfoSehen[ANZAHLAMPELN];                                        // Anforderungen vorheriger Zustand
bool aktAnfoBlind[ANZAHLAMPELN];                                        // Anforderungen aktueller Zustand
bool altAnfoBlind[ANZAHLAMPELN];                                        // Anforderungen vorheriger Zustand
uint32_t altMillis[ANZAHLAMPELN];                                       // Anfang des Zeitintervalls in Millisekunden
uint32_t aktMillis;                                                     // Zeit seit Reset
byte ausGruen, ausBlink, ausTon;                                        // Ausgaenge
enum ZUSTAENDE {ROT, BLINKEN, GRUEN};
byte zustand[ANZAHLAMPELN] = {ROT, ROT, ROT, ROT, ROT, ROT, ROT, ROT};  // Zustaende mit Anfangszustand

void loop() {
  aktMillis = millis();
  eingaenge(ampel);

  switch (zustand[ampel]) {
    case ROT:
      ausGruen &= ~(1 << ampel);
      if (!altAnfoSehen[ampel] && aktAnfoSehen[ampel]) {
        zustand[ampel] = BLINKEN;
        delay(30);   // Entprellen TEST agmue
      }
      break;
    case BLINKEN:
      if (aktMillis - altMillis[ampel] >= blinkIntervall) {
        altMillis[ampel] = aktMillis;
        if (ausBlink & (1 << ampel)) {
          ausBlink &= ~(1 << ampel);    // Quittung aus
        } else {
          ausBlink |= 1 << ampel;       // Quittung an
        }
      }
      if (!altAnfoSehen[ampel] && aktAnfoSehen[ampel]) {
        zustand[ampel] = GRUEN;
        ausBlink &= ~(1 << ampel);    // Quittung aus
        delay(30);   // Entprellen TEST agmue
      }
      break;
    case GRUEN:
      ausGruen |= 1 << ampel;
      if (!altAnfoSehen[ampel] && aktAnfoSehen[ampel]) {
        zustand[ampel] = ROT;
        delay(30);   // Entprellen TEST agmue
      }
      break;
  }

  ausgaenge();
  ampel++;
  ampel = ampel % ANZAHLAMPELN;  // 0 bis (ANZAHLAMPELN - 1)
}

void ausgaenge() {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, ausTon);
  shiftOut(dataPin, clockPin, MSBFIRST, ausBlink);
  shiftOut(dataPin, clockPin, MSBFIRST, ausGruen);
  digitalWrite(latchPin, HIGH);
  /*
  Serial.print(ampel);
  Serial.print("\t");
  Serial.print(ausGruen, BIN);
  Serial.print("\t");
  Serial.print(ausBlink, BIN);
  Serial.print ("\t");
  Serial.print(ausTon, BIN);
  Serial.println ("");
  */
}

void eingaenge(byte a) {
  digitalWrite(mux1Pin, bitRead(a, 0));                             //Ausgabe der einzelnen Steuerbits auf die Selectpins des Multiplexers, also hier die Ansteuerung
  digitalWrite(mux2Pin, bitRead(a, 1));
  digitalWrite(mux3Pin, bitRead(a, 2));

  altAnfoSehen[a] = aktAnfoSehen[a];
  aktAnfoSehen[a] = digitalRead(muxIn1Pin);
  altAnfoBlind[a] = aktAnfoBlind[a];
  aktAnfoBlind[a] = digitalRead(muxIn2Pin);

  if (a == 0) {
    aktAnfoSehen[a] = digitalRead(A0);   // TEST agmue
    aktAnfoBlind[a] = digitalRead(A1);   // TEST agmue
    Serial.println ("");
  }
  /*
  Serial.print(a);
  Serial.print("\t");
  Serial.print(aktAnfoSehen[a], BIN);                       //Ausgabe der 8 Werte des ersten Arrays
  Serial.print("\t");
  Serial.print(aktAnfoBlind[a], BIN);                       //Ausgabe der 8 Werte des zweiten Arrays
  Serial.println ("");
  */
}

void setup() {
  pinMode(A0, INPUT_PULLUP);  // Anforderung Sehen TEST agmue
  pinMode(A1, INPUT_PULLUP);  // Anforderung Blind TEST agmue

  pinMode(mux1Pin, OUTPUT);                                  //Pindefinition als Ein oder Ausgang
  pinMode(mux2Pin, OUTPUT);
  pinMode(mux3Pin, OUTPUT);
  pinMode(muxIn1Pin, INPUT);
  pinMode(muxIn2Pin, INPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  digitalWrite(latchPin, LOW);                         //Datenausgabe sicherheitshalber auf LOW

  //Serial.begin(9600);
  //Serial.println ("Programmanfang");
}

Hallo!

Na das ist ja eine Menge Stoff, ich freu mich!!
Auf den ersten Blick für mich schwer nachzuvollziehen, aber ich versuche mich durchzuarbeiten.
Die Taster schalten alle HIGH.
Der Status nach Anforderung ist Rot und parallel dazu der Blinker. Dieser wird dann durch Grünbeginn gelöscht.
Das weiterschalten schaue ich mir mal an, das sollte final dann alleine durchlaufen und in Rot auf eine neue Anforderung warten.

Zuächst mal vielen Dank für den Input - ich hoffe ich komme hinter die einzelnen Schritte!
Der heutige Tag war schon sehr lehrreich, leider kömmt man alleine nur mühsam vorran wenn man sich sonst "nur" mit Modellen beschäftigt.

Viele Grüße, Klaus

So, erster Durchlauf erfolgreich.
Ich sehe beim Schalten über den Multiplexer auf dem zweiten Ausgang (Q1) des Schieberegisters:
Drücken, LOW
Drücken, High
Drücken, LOW
Drücken, LOW
Drücken , High usw

Am Eingang A0 gleiche Reaktion nur erster Ausgang Q0.

Anfo Blind sehe ich derzeit nicht an den Ausgängen, aber am Serial Monitor.

Völlig unklar ist mir derzeit wie von dir vermutet die Bitschubserei und z.B. was hier passiert:
ampel = ampel % 2;

Viele Grüße, Klaus

U3600:
... und z.B. was hier passiert:
ampel = ampel % 2;

Sorry Klaus, habe mich vertan, sollte wohl so sein:

ampel = ampel % ANZAHLAMPELN;  // 0 bis (ANZAHLAMPELN - 1)

Wobei ich mir gerade überlegt habe, es ist doch wohl besser, das mit einer for-Schleife zu machen, da auch die Ausgänge je loop für alle Ampeln abgearbeitet werden.

Prellen Deine Taster?

Im aktualisierten Sketch habe ich das Blinken in eine Funktion ausgelagert:

#define ANZAHLAMPELN 8
const byte mux1Pin = 2;
const byte mux2Pin = 3;
const byte mux3Pin = 4;
const byte muxIn1Pin = 5;
const byte muxIn2Pin = 6;
const byte clockPin = 8;       // IC-Pin 11 SH_CP des 74HC595
const byte latchPin = 9;       // IC-Pin 12 ST_CP des 74HC595
const byte dataPin = 10;       // IC-Pin 14 DS des 74HC595

const uint32_t blinkIntervall = 500;                                    // Blinkintervall in Millisekunden
byte ampel;
bool aktAnfoSehen[ANZAHLAMPELN];                                        // Anforderungen aktueller Zustand
bool altAnfoSehen[ANZAHLAMPELN];                                        // Anforderungen vorheriger Zustand
bool aktAnfoBlind[ANZAHLAMPELN];                                        // Anforderungen aktueller Zustand
bool altAnfoBlind[ANZAHLAMPELN];                                        // Anforderungen vorheriger Zustand
uint32_t aktMillis;                                                     // Zeit seit Reset
byte ausGruen, ausBlink, ausTon, merkerBlink;                                        // Ausgaenge
enum ZUSTAENDE {ROT, BLINKEN, GRUEN};
byte zustand[ANZAHLAMPELN] = {ROT, ROT, ROT, ROT, ROT, ROT, ROT, ROT};  // Zustaende mit Anfangszustand

void loop() {
  aktMillis = millis();
  for (ampel = 0; ampel < ANZAHLAMPELN; ampel++) {
    eingaenge(ampel);
    switch (zustand[ampel]) {
      case ROT:
        ausGruen &= ~(1 << ampel);
        if (!altAnfoSehen[ampel] && aktAnfoSehen[ampel]) {
          zustand[ampel] = BLINKEN;
          delay(30);   // Entprellen TEST agmue
        }
        break;
      case BLINKEN:
        merkerBlink |= 1 << ampel;
        if (!altAnfoSehen[ampel] && aktAnfoSehen[ampel]) {
          zustand[ampel] = GRUEN;
          merkerBlink &= ~(1 << ampel);    // Merker aus
          delay(30);   // Entprellen TEST agmue
        }
        break;
      case GRUEN:
        ausGruen |= 1 << ampel;
        if (!altAnfoSehen[ampel] && aktAnfoSehen[ampel]) {
          zustand[ampel] = ROT;
          delay(30);   // Entprellen TEST agmue
        }
        break;
    }
  }
  blinken();      // Blinken der Quittung
  ausgaenge();    // Schieberegister (Ausgabe)
}

void blinken() {                    // Alle blinken im selben Takt
  static bool an = false;
  static uint32_t altMillis = 0;    // Anfang des Zeitintervalls in Millisekunden
  if (aktMillis - altMillis >= blinkIntervall) {
    altMillis = aktMillis;
    if (an) {
      ausBlink = 0;                 // Anzeige aus
    } else {
      ausBlink = merkerBlink;       // Anzeige an
    }
    an = !an;
  }
}

void ausgaenge() {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, ausTon);
  shiftOut(dataPin, clockPin, MSBFIRST, ausBlink);
  shiftOut(dataPin, clockPin, MSBFIRST, ausGruen);
  digitalWrite(latchPin, HIGH);
  /*
  Serial.print(ampel);
  Serial.print("\t");
  Serial.print(ausGruen, BIN);
  Serial.print("\t");
  Serial.print(ausBlink, BIN);
  Serial.print ("\t");
  Serial.print(ausTon, BIN);
  Serial.println ("");
  */
}

void eingaenge(byte a) {
  digitalWrite(mux1Pin, bitRead(a, 0));                             //Ausgabe der einzelnen Steuerbits auf die Selectpins des Multiplexers, also hier die Ansteuerung
  digitalWrite(mux2Pin, bitRead(a, 1));
  digitalWrite(mux3Pin, bitRead(a, 2));

  altAnfoSehen[a] = aktAnfoSehen[a];
  aktAnfoSehen[a] = digitalRead(muxIn1Pin);
  altAnfoBlind[a] = aktAnfoBlind[a];
  aktAnfoBlind[a] = digitalRead(muxIn2Pin);

  if (a == 0) {
    aktAnfoSehen[a] = digitalRead(A0);   // TEST agmue
    aktAnfoBlind[a] = digitalRead(A1);   // TEST agmue
    Serial.println ("");
  }
  /*
  Serial.print(a);
  Serial.print("\t");
  Serial.print(aktAnfoSehen[a], BIN);                       //Ausgabe der 8 Werte des ersten Arrays
  Serial.print("\t");
  Serial.print(aktAnfoBlind[a], BIN);                       //Ausgabe der 8 Werte des zweiten Arrays
  Serial.println ("");
  */
}

void setup() {
  pinMode(A0, INPUT_PULLUP);  // Anforderung Sehen TEST agmue
  pinMode(A1, INPUT_PULLUP);  // Anforderung Blind TEST agmue

  pinMode(mux1Pin, OUTPUT);                                  //Pindefinition als Ein oder Ausgang
  pinMode(mux2Pin, OUTPUT);
  pinMode(mux3Pin, OUTPUT);
  pinMode(muxIn1Pin, INPUT);
  pinMode(muxIn2Pin, INPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  digitalWrite(latchPin, LOW);                         //Datenausgabe sicherheitshalber auf LOW

  //Serial.begin(9600);
  //Serial.println ("Programmanfang");
}

U3600:
Am Eingang A0 gleiche Reaktion nur erster Ausgang Q0.

A0 und A1 ersetzen mir die bei mir nicht vorhandene Matrix. Die entsprechenden Befehle kannst Du rauswerfen oder mit einer anderen Ampel testen.

U3600:
Anfo Blind sehe ich derzeit nicht an den Ausgängen, aber am Serial Monitor.

Da habe ich auch noch nix gemacht. Auch die Logik soll nur mal die Richtung vorgeben, in die man gehen könnte.

Nein die Tasten prellen nicht, sie werden durch Optokoppler geschaltet.
Sehr Cool, was jetzt nur spannend wird ist ob der kleine Arduino es tatsächlich schafft alle 8 gleichzeitig mit einem Blinken zu versorgen, das versuche ich mal testweise aufzubauen.

U3600:
... das sollte final dann alleine durchlaufen ..

Das mit dem Taster war nur zum Testen, jetzt auch mit automatischem, zeitgesteuertem Ablauf, der nun auch unterschiedslos auf AnfoBlind reagiert:

#define ANZAHLAMPELN 8
const byte mux1Pin = 2;
const byte mux2Pin = 3;
const byte mux3Pin = 4;
const byte muxIn1Pin = 5;
const byte muxIn2Pin = 6;
const byte clockPin = 8;       // IC-Pin 11 SH_CP des 74HC595
const byte latchPin = 9;       // IC-Pin 12 ST_CP des 74HC595
const byte dataPin = 10;       // IC-Pin 14 DS des 74HC595

const uint32_t blinkIntervall = 500;                                    // Blinkintervall in Millisekunden
const uint32_t intervalle[] = {0, 3000, 5000};                       // Intervalle in Millisekunden, je Zustand ein Intervall
byte ampel;
bool aktAnfoSehen[ANZAHLAMPELN];                                        // Anforderungen aktueller Zustand
bool altAnfoSehen[ANZAHLAMPELN];                                        // Anforderungen vorheriger Zustand
bool aktAnfoBlind[ANZAHLAMPELN];                                        // Anforderungen aktueller Zustand
bool altAnfoBlind[ANZAHLAMPELN];                                        // Anforderungen vorheriger Zustand
uint32_t aktMillis;                                                     // Zeit seit Reset
uint32_t altMillis[ANZAHLAMPELN];                                       // Anfang des Zeitintervalls in Millisekunden
byte ausGruen, ausBlink, ausTon, merkerBlink;                           // Ausgaenge
enum ZUSTAENDE {ROT, BLINKEN, GRUEN};
byte zustand[ANZAHLAMPELN] = {ROT, ROT, ROT, ROT, ROT, ROT, ROT, ROT};  // Zustaende mit Anfangszustand

void loop() {
  aktMillis = millis();
  for (ampel = 0; ampel < ANZAHLAMPELN; ampel++) {
    eingaenge(ampel);
    if (aktMillis - altMillis[ampel] >= intervalle[zustand[ampel]]) {
      switch (zustand[ampel]) {
        case ROT:
          ausGruen &= ~(1 << ampel);
          if ((!altAnfoSehen[ampel] && aktAnfoSehen[ampel]) || (!altAnfoBlind[ampel] && aktAnfoBlind[ampel])) {
            altMillis[ampel] = aktMillis;
            merkerBlink |= 1 << ampel;
            zustand[ampel] = BLINKEN;
          }
          break;
        case BLINKEN:
          altMillis[ampel] = aktMillis;
          merkerBlink &= ~(1 << ampel);
          ausGruen |= 1 << ampel;
          zustand[ampel] = GRUEN;
          break;
        case GRUEN:
          altMillis[ampel] = aktMillis;
          zustand[ampel] = ROT;
          break;
      }
    }
  }
  blinken();      // Blinken der Quittung
  ausgaenge();    // Schieberegister (Ausgabe)
}

void blinken() {                          // Alle blinken im selben Takt
  static bool an = false;
  static uint32_t altBlinkMillis = 0;     // Anfang des Zeitintervalls in Millisekunden
  if (aktMillis - altBlinkMillis >= blinkIntervall) {
    altBlinkMillis = aktMillis;
    if (an) {
      ausBlink = 0;                       // Anzeige aus
    } else {
      ausBlink = merkerBlink;             // Anzeige an
    }
    an = !an;
  }
}

void ausgaenge() {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, ausTon);
  shiftOut(dataPin, clockPin, MSBFIRST, ausBlink);
  shiftOut(dataPin, clockPin, MSBFIRST, ausGruen);
  digitalWrite(latchPin, HIGH);
  /*
  Serial.print(ampel);
  Serial.print("\t");
  Serial.print(ausGruen, BIN);
  Serial.print("\t");
  Serial.print(ausBlink, BIN);
  Serial.print ("\t");
  Serial.print(ausTon, BIN);
  Serial.println ("");
  */
}

void eingaenge(byte a) {
  digitalWrite(mux1Pin, bitRead(a, 0));                             //Ausgabe der einzelnen Steuerbits auf die Selectpins des Multiplexers, also hier die Ansteuerung
  digitalWrite(mux2Pin, bitRead(a, 1));
  digitalWrite(mux3Pin, bitRead(a, 2));

  altAnfoSehen[a] = aktAnfoSehen[a];
  aktAnfoSehen[a] = digitalRead(muxIn1Pin);
  altAnfoBlind[a] = aktAnfoBlind[a];
  aktAnfoBlind[a] = digitalRead(muxIn2Pin);

  if (a == 0) {
    aktAnfoSehen[a] = digitalRead(A0);   // TEST agmue
    aktAnfoBlind[a] = digitalRead(A1);   // TEST agmue
    Serial.println ("");
  }
  /*
  Serial.print(a);
  Serial.print("\t");
  Serial.print(aktAnfoSehen[a], BIN);                       //Ausgabe der 8 Werte des ersten Arrays
  Serial.print("\t");
  Serial.print(aktAnfoBlind[a], BIN);                       //Ausgabe der 8 Werte des zweiten Arrays
  Serial.println ("");
  */
}

void setup() {
  pinMode(A0, INPUT_PULLUP);  // Anforderung Sehen TEST agmue
  pinMode(A1, INPUT_PULLUP);  // Anforderung Blind TEST agmue

  pinMode(mux1Pin, OUTPUT);                                  //Pindefinition als Ein oder Ausgang
  pinMode(mux2Pin, OUTPUT);
  pinMode(mux3Pin, OUTPUT);
  pinMode(muxIn1Pin, INPUT);
  pinMode(muxIn2Pin, INPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  digitalWrite(latchPin, LOW);                         //Datenausgabe sicherheitshalber auf LOW

  //Serial.begin(9600);
  //Serial.println ("Programmanfang");
}

U3600:
Sehr Cool, was jetzt nur spannend wird ist ob der kleine Arduino es tatsächlich schafft alle 8 gleichzeitig mit einem Blinken zu versorgen, das versuche ich mal testweise aufzubauen.

Die LEDs am Schieberegister kann man sowas wie dimmen. Tatsächlich blinken die LEDs, aber Du kannst nicht so schnell sehen. Man könnte noch ein "schiebe nur Daten raus, wenn sie sich geändert haben" ergänzen.

Im Sketch findest Du provisorisch enum ZUSTAENDE {ROT, BLINKEN, GRUEN};, was durch den Lichtzeichenanlagenexperten um die relevanten Zustände ergänzt werden müßte. Willst Du beispielsweise eine minimale Rotzeit haben? In der Praxis ist das ja so.

Die Rotzeit ist real min. 3 Sek. Hier nicht wirklich wichtig weil das Bild erstmal bis zur Anforderung in Rot "wartet". Kommt die Anforderung wird zum Rot das Blinken dazugeschaltet "Bitte Warten". Um nachvollziehen zu können das hier was passiert sollte diese Zeit zb. mindestens 4-6 Sekunden lang sein bevor Grün kommt.

Das Blinken an den Schieberegistern meinte ich nicht, ich meinte wenn der Arduino auf bis zu acht einzeln angeforderten Kanälen blinken soll. Hier wird es doch bestimmt irgendwann hakelig weil bis zu 8 Schleifen mit Millis abgefragt werden ob sie fertig sind, oder? Also im Bild Rot/Blinken.

Die Zustandsabfolge währe {ROT, ROTBLINKEN,GRUEN} oder ...GRÜN/TON}

Hier wird es doch bestimmt irgendwann hakelig weil bis zu 8 Schleifen mit Millis abgefragt werden ob sie fertig sind, oder?

Och...

Schätze mal, dass da noch locker 30000 Loop Durchläufe pro Sekunde drin sitzen.

U3600:
Die Zustandsabfolge währe {ROT, ROTBLINKEN,GRUEN} oder ...GRÜN/TON}

Wird nicht zwischen zwei akustischen Signalen, langsam "Tok" für Rot und schnell für Grün, unterschieden?

Willst Du die minimale Grünzeit für Autos nicht berücksichtigen?

30000 Durchläufe bei Aufgaben die mit Zeit zu tun haben? Man lernt nie aus, toll das hätte ich nicht vermutet.

Akustik:
Nein in Deutschland nicht. Hier gibt es genau festgelegte Töne in einer Norm, der DIN 32981. Der Freigabeton ist sowohl in fer Frequenz als auch in der Taktung definiert und muss eingehalten werden. Der zweite Ton ist der sogenannte Pilotton oder Orientierungston. Das dumpfe Tok oder auch etwas andere Tonlagen sind in Grenzen wählbar, verkehrslämabhängig wie auch die Freigabe und soll den Blinden das Auffinden des Ampelmastes mit dem Taster ermöglichen. Dort bekommt er seine Freigabe uber einen Vibrator und eben dem Freigabesignal.
In anderen Ländern variiert das stark. Es gibt viele Länder wo die letzten Sekunden des Fußgängergrüns blinken um den Farbwechsel anzukündigen. Laufende Fußgängerpiktogramme die am Ende schneller werden und mehr...

Das Sketch habe ich jetzt einigermaßen durchblickt wobei ich zugebe das ich das so nie alleine hinbekommen hätte. Einige Dinge habe ich immer noch nicht verstanden z.B. wie das mit dem >> funktioniert für das "Laden" der Schieberegister.
Ansonsten ist alles mit "Test" gegennzeichnete funktionsfähig, toll!

Die Grünzeit, du fragtest nach der minimalen, ist hier zum darstellen der Akustik notwendig. Die Zeit kann ja jederzeit im Sketch geändert werden, sollte aber mit 5 Sekunden zunächst ausreichen.

>>

U3600:
Akustik:
Nein in Deutschland nicht.

Dann habe ich das wohl im Ausland beobachtet.

U3600:
Die Grünzeit, du fragtest nach der minimalen, ist hier zum darstellen der Akustik notwendig. Die Zeit kann ja jederzeit im Sketch geändert werden, sollte aber mit 5 Sekunden zunächst ausreichen.

Ich meinte die minimale Grünzeit für den Autoverkehr. Möglicheweise willst Du die aber nicht berücksichtigen.

Nun habe ich auch noch ein drittes Schieberegister ergänzt und für Ampel 0 probiert. Bin gespannt, ob Eingabematrix und die anderen Ampeln funktionieren :slight_smile:

//                  +-\/-+
//             QB  1|    |16  Vcc
//             QC  2|    |15  QA
//             QD  3|    |14  SI Data
//             QE  4|    |13  /G
//             QF  5|    |12  RCK Latch
//             QG  6|    |11  SCK Clock
//             QH  7|    |10  /SCLR
//            GND  8|    |9   QH'
//                  +----+
#define ANZAHLAMPELN 8
const byte mux1Pin = 2;
const byte mux2Pin = 3;
const byte mux3Pin = 4;
const byte muxIn1Pin = 5;
const byte muxIn2Pin = 6;
const byte clockPin = 8;       // IC-Pin 11 SCK des 74HC595
const byte latchPin = 9;       // IC-Pin 12 RCK des 74HC595
const byte dataPin = 10;       // IC-Pin 14 SI des 74HC595

const uint32_t blinkIntervall = 500;                                    // Blinkintervall in Millisekunden
const uint32_t intervalle[] = {0, 4000, 6000};                          // Intervalle in Millisekunden, je Zustand ein Intervall
byte ampel;
bool aktAnfoSehen[ANZAHLAMPELN];                                        // Anforderungen aktueller Zustand
bool altAnfoSehen[ANZAHLAMPELN];                                        // Anforderungen vorheriger Zustand
bool aktAnfoBlind[ANZAHLAMPELN];                                        // Anforderungen aktueller Zustand
bool altAnfoBlind[ANZAHLAMPELN];                                        // Anforderungen vorheriger Zustand
uint32_t aktMillis;                                                     // Zeit seit Reset
uint32_t altMillis[ANZAHLAMPELN];                                       // Anfang des Zeitintervalls in Millisekunden
byte ausgGruen, ausgBlink, ausgTon, merkerBlink, merkerTon;             // Ausgaenge und Merker
enum ZUSTAENDE {ROT, BLINKEN, GRUEN};
byte zustand[ANZAHLAMPELN] = {ROT, ROT, ROT, ROT, ROT, ROT, ROT, ROT};  // Zustaende mit Anfangszustand

void loop() {
  aktMillis = millis();
  for (ampel = 0; ampel < ANZAHLAMPELN; ampel++) {
    eingaenge(ampel);
    switch (zustand[ampel]) {
      case ROT:
        if ((!altAnfoSehen[ampel] && aktAnfoSehen[ampel]) || (!altAnfoBlind[ampel] && aktAnfoBlind[ampel])) {
          altMillis[ampel] = aktMillis;
          merkerBlink |= 1 << ampel;
          if (aktAnfoBlind[ampel]) {
            merkerTon |= 1 << ampel;
          }
          zustand[ampel] = BLINKEN;
        }
        break;
      case BLINKEN:
        if (!altAnfoBlind[ampel] && aktAnfoBlind[ampel]) {
          merkerTon |= 1 << ampel;
        }
        if (aktMillis - altMillis[ampel] >= intervalle[zustand[ampel]]) {
          altMillis[ampel] = aktMillis;
          merkerBlink &= ~(1 << ampel);
          ausgGruen |= 1 << ampel;
          ausgTon |=  merkerTon && (1 << ampel);
          zustand[ampel] = GRUEN;
        }
        break;
      case GRUEN:
        if (aktMillis - altMillis[ampel] >= intervalle[zustand[ampel]]) {
          ausgGruen &= ~(1 << ampel);
          ausgTon &= ~(1 << ampel);
          zustand[ampel] = ROT;
        }
        break;
    }
  }
  blinken();      // Blinken der Quittung
  ausgaenge();    // Schieberegister (Ausgabe)
}

void blinken() {                          // Alle blinken im selben Takt
  static bool an = false;
  static uint32_t altBlinkMillis = 0;     // Anfang des Zeitintervalls in Millisekunden
  if (aktMillis - altBlinkMillis >= blinkIntervall) {
    altBlinkMillis = aktMillis;
    if (an) {
      ausgBlink = 0;                       // Anzeige aus
    } else {
      ausgBlink = merkerBlink;             // Anzeige an
    }
    an = !an;
  }
}

void ausgaenge() {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, ausgTon);
  shiftOut(dataPin, clockPin, MSBFIRST, ausgBlink);
  shiftOut(dataPin, clockPin, MSBFIRST, ausgGruen);
  digitalWrite(latchPin, HIGH);
  /*
  Serial.print(ampel);
  Serial.print("\t");
  Serial.print(ausgGruen, BIN);
  Serial.print("\t");
  Serial.print(ausgBlink, BIN);
  Serial.print ("\t");
  Serial.print(ausgTon, BIN);
  Serial.println ("");
  */
}

void eingaenge(byte a) {
  digitalWrite(mux1Pin, bitRead(a, 0));                             //Ausgabe der einzelnen Steuerbits auf die Selectpins des Multiplexers, also hier die Ansteuerung
  digitalWrite(mux2Pin, bitRead(a, 1));
  digitalWrite(mux3Pin, bitRead(a, 2));

  altAnfoSehen[a] = aktAnfoSehen[a];
  aktAnfoSehen[a] = digitalRead(muxIn1Pin);
  altAnfoBlind[a] = aktAnfoBlind[a];
  aktAnfoBlind[a] = digitalRead(muxIn2Pin);

  if (a == 0) {
    aktAnfoSehen[a] = digitalRead(A0);   // TEST agmue
    aktAnfoBlind[a] = digitalRead(A1);   // TEST agmue
    // Serial.println ("");
  }
  /*
  Serial.print(a);
  Serial.print("\t");
  Serial.print(aktAnfoSehen[a], BIN);                       //Ausgabe der 8 Werte des ersten Arrays
  Serial.print("\t");
  Serial.print(aktAnfoBlind[a], BIN);                       //Ausgabe der 8 Werte des zweiten Arrays
  Serial.println ("");
  */
}

void setup() {
  pinMode(A0, INPUT_PULLUP);  // Anforderung Sehen TEST agmue
  pinMode(A1, INPUT_PULLUP);  // Anforderung Blind TEST agmue

  pinMode(mux1Pin, OUTPUT);                                  //Pindefinition als Ein oder Ausgang
  pinMode(mux2Pin, OUTPUT);
  pinMode(mux3Pin, OUTPUT);
  pinMode(muxIn1Pin, INPUT);
  pinMode(muxIn2Pin, INPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  digitalWrite(latchPin, LOW);                         //Datenausgabe sicherheitshalber auf LOW

  //Serial.begin(9600);
  //Serial.println ("Programmanfang");
}

Scheint zu funktionieren, ich muss nun mal anfangen weitere Schieberegister anzureihen, ich habe auf meinem Brett nur zwei zum Testen aufgesteckt....
Was hast du mit Ampel 0 vor?