Go Down

Topic: Fußgängerampel Erweiterung (Read 10608 times) previous topic - next topic

schlier

Jun 28, 2015, 07:14 pm Last Edit: Jun 29, 2015, 09:45 pm by schlier
Hallo zusammen

ich bin dabei eine Ampelsteuerung für meinen Sohn zu basteln und wollte fragen ob Sie mir helfen könnten. Ein Sketch funktioniert sehr gut nur leider wollte ich etwas anderes aber mir fehlt leider die Erfahrung.

Ich habe lediglich eine Fußgängerampel die im Normalzustand zwischen rot und grün nach festen variabeln hin und her wechselt.
Wenn man in der Rot Licht Phase den Anfordergungstaster drückt soll dieser leuchten und die Ampel nach x Sekunden auf Grün schalten.
Wenn die Ampel bereits grün ist und der Anforderungstaster wird gedrückt soll nichts passieren.
Falls niemand den Anforderungstaster drückt soll nach einer Zeit X die komplette Ampel ausgehen (Standby)


V2 (wünschenswert)
Ich habe noch einen zweiten Taster mit dem ich verschiedene Programme abrufen möchte.
5 Sekunden drücken und die Ampel geht in den Auswahlmodus.
Ampel aus und Anforderungstaster leuchtet.
Drücke ich nun den Anforderungstaster einmal blink der Anforderungstaster einmal kurz, dann pause und wieder kurz. So sollte ich angezeigt bekommen das Programm 1 gewählt ist.
Wenn ich zweimal den Anforderungstaster drücke blinkt der Taster 2mal kurz, Pause und dann wieder.
Zum Auswahl des Programmes muss dann am 2. Taster das Programm bestätigt werden.

V3
mit der Arduino DS3232RTC Platine würde ich gerne die Uhrzeit abfragen und ab Uhrzeit x die Ampel ausschalten und nur den Anforderungstaster als Nachtlicht leuchten lassen.

V4
eventuell später erweiterbar mit Ethernet, um per Webbrowser die Zustände abfragen zu können und auch die Programme auswählen können.

Könnte mir jemand behilflich sein?



Serenifly

Bitte Code Tags verwenden. Sonst ist das nicht lesbar.

Hier sind wir wieder mal beim Thema endliche Zustandsautomaten (finite state machine). Mit delay() wird das nichts. Du musst verschiedene Zustände anlegen. Und dann zeitgesteuert zwischen den Zuständen wechseln. Die Zeitsteuerung muss nicht-blockierend mit millis() erfolgen damit man zwischendurch was anderes erledigen kann, z.B. Taster, Serial oder Ethernet abfragen.

agmue

Hallo und willkommen im Forum!

Ich habe eine Anleitung Endlicher Automat mit millis() verfaßt, die Dir (im Forum ist diese Anrede üblich) möglicherweise helfen könnte. Am gewählten Thema kann man erkennen, daß ich von der Modellbahn mit LEDs als Hausbeleuchtung zum Arduino gekommen bin.
Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

jurs

#3
Jun 28, 2015, 08:54 pm Last Edit: Jun 28, 2015, 09:24 pm by jurs
Könnte mir jemand behilflich sein?

Anbei ein Sketch den man eventuell umbauen könnte
In Sachen "Finite State Machine" (FSM, Zustandsautomat) stimme ich meinem Vorredner voll zu. Eine Ampelsteuerung ist ein klassischer Fall für einen Zustandsautomaten mit verschiedenen Zuständen, aus denen heraus in andere Zustände umgeschaltet wird.

Weiterhin darf ein Programm, das interaktiv reagieren soll, z.B. auf die Bettelknopf-Grünanforderung, niemals mit "delay()" blockiert werden.

Und "Interrupts" haben in so einem Programm und bei einer Tasterabfrage schon gar nichts verloren.

Deine Programmvorlage dürfte daher für Dein Vorhaben ziemlich unbrauchbar sein.

In Sachen FSM kann ich Dir diese Library empfehlen:
http://playground.arduino.cc/Code/SMlib

Lade Dir diese mal herunter, installiere sie und arbeite die Beispiele durch.

Die Arbeitsweise ist wie folgt: Für jeden verschiedenen Zustand kannst Du zwei parameterlose Funktionen definieren:
- eine Funktion, die einmalig läuft, wenn der Zustand aktiv wird
- eine Funktion, die ständig läuft und abhängig von Deinen Bedingungen in einen anderen Zustand umschaltet

Du kannst Dich dann vollständig darauf konzentrieren,
- was muß ausgeführt werden, wenn dieser Zustand aktiv wird?
- unter welchen Bedingungen muß aus diesem in einen anderen Zustand geschaltet werden?

Alle Zustände fein säuberlich voneinander getrennt, zwei Funktionen für jeden Zustand.

Und um die richtigen Funktionsaufrufe kümmert sich die Library automatisch, wenn Du den aktiven Zustand in einen anderen umschaltest. Nur Deine Programmlogik mußt Du natürlich selbst in sich konsistent halten. Also dass beispielsweise kein Zustand existiert, aus dem heraus niemals wieder ein anderer Zustand aktiviert wird.

Wenn Du Probleme damit hast, sag Bescheid wo es klemmt.

Edit/Nachtrag:
Gerade mal in die Library reingeschaut, das "example_3" ist quasi das, worauf Du aufbauen kannst.

sschultewolter

#4
Jun 29, 2015, 12:42 am Last Edit: Jun 29, 2015, 01:57 pm by sschultewolter
--edit--
Beitrag vom Vorposter entfernt
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

schlier

Das mit den Millis glaub ich verstanden zu haben.
Nur in Sachen FSM, Zustandsautomat mit dem Beispiel 3 blicke ich nicht durch

agmue

Aus meinem Beispiel habe ich jetzt eine reine Fußgängerampel gemacht, wo die Aotofahrer so lange Grün haben, bis der Anforderungstaster betätigt wird:
Code: [Select]
// Ampel Belegung der Ausgaenge
const byte RotPin = 2;
const byte GelbPin = 3;
const byte GruenPin = 4;
const byte FGAnfPin = 5;  // Rueckmeldung "Gruen kommt"
const byte FGRotPin = 6;
const byte FGGruenPin = 7;
const byte FGTasterPin = 8;  // Anforderungstaster

//
const boolean ein = HIGH;
const boolean aus = LOW;
//
const int ZEITROTPHASE = 3000;
const int ZEITGELBPHASE = 1000;
const int ZEITMINGRUENPHASE = 2000;
unsigned long ampelMillis;
unsigned long ampelIntervall;
//
enum ZUSTAENDE {ROT, ROTGELB, GRUEN, GRUEN_WARTEN, GELB};
byte zustand = ROT;

void setup() {
  // Definiert die Pins als Ausgang
  pinMode(RotPin, OUTPUT);
  pinMode(GelbPin, OUTPUT);
  pinMode(GruenPin, OUTPUT);
  pinMode(FGAnfPin, OUTPUT);
  pinMode(FGRotPin, OUTPUT);
  pinMode(FGGruenPin, OUTPUT);
  pinMode(FGTasterPin, INPUT_PULLUP);
}

void loop() {
  //
  // Ampelschaltung
  if (digitalRead(FGTasterPin) == LOW && (zustand == GRUEN || zustand == GRUEN_WARTEN)) {
    digitalWrite(FGAnfPin, HIGH);
  }
  if (millis() - ampelMillis >= ampelIntervall) {
    switch (zustand) {
      case ROT:
        digitalWrite(RotPin, HIGH);
        digitalWrite(GelbPin, LOW);
        digitalWrite(GruenPin, LOW);
        digitalWrite(FGRotPin, LOW);
        digitalWrite(FGGruenPin, HIGH);
        digitalWrite(FGAnfPin, LOW);
        zustand = ROTGELB;
        ampelMillis = millis();
        ampelIntervall = ZEITROTPHASE;
        break;
      case ROTGELB:
        digitalWrite(RotPin, HIGH);
        digitalWrite(GelbPin, HIGH);
        digitalWrite(GruenPin, LOW);
        digitalWrite(FGRotPin, HIGH);
        digitalWrite(FGGruenPin, LOW);
        zustand = GRUEN;
        ampelMillis = millis();
        ampelIntervall = ZEITGELBPHASE;
        break;
      case GRUEN:                        // Minimale Gruenphase
        digitalWrite(RotPin, LOW);
        digitalWrite(GelbPin, LOW);
        digitalWrite(GruenPin, HIGH);
        digitalWrite(FGRotPin, HIGH);
        digitalWrite(FGGruenPin, LOW);
        zustand = GRUEN_WARTEN;
        ampelMillis = millis();
        ampelIntervall = ZEITMINGRUENPHASE;
        break;
      case GRUEN_WARTEN:                 // Warten auf Anforderung
        digitalWrite(RotPin, LOW);
        digitalWrite(GelbPin, LOW);
        digitalWrite(GruenPin, HIGH);
        digitalWrite(FGRotPin, HIGH);
        digitalWrite(FGGruenPin, LOW);
        if (digitalRead(FGAnfPin) == HIGH) {
          zustand = GELB;
        }
        ampelMillis = millis();
        ampelIntervall = 0;
        break;
      case GELB:
        digitalWrite(RotPin, LOW);
        digitalWrite(GelbPin, HIGH);
        digitalWrite(GruenPin, LOW);
        digitalWrite(FGRotPin, HIGH);
        digitalWrite(FGGruenPin, LOW);
        zustand = ROT;
        ampelMillis = millis();
        ampelIntervall = ZEITGELBPHASE;
        break;
    }
  }
}

Möglicherweise hilft es Dir ja irgendwie beim Verständnis.
Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

jurs

#7
Jun 29, 2015, 11:42 pm Last Edit: Jun 30, 2015, 12:16 pm by jurs
Nur in Sachen FSM, Zustandsautomat mit dem Beispiel 3 blicke ich nicht durch
Ich habe Dir einen Beispielcode für eine Fußgänger-Bettelampel mit dieser FSM-Library gemacht, das Du bei Bedarf weiter ausbauen kannst:
Code: [Select]

#include <SM.h>

SM Ampel(FahrbahnGruenInit, FahrbahnGruen); //Anfangsstatus setzen

byte fahrGruenPin=2;
byte fahrGelbPin=3;
byte fahrRotPin=4;
byte fussGruenPin=5;
byte fussRotPin=6;
byte anforderungPin=10;

#define buttonMode INPUT_PULLUP // INPUT_PULLUP oder INPUT

void setAmpel(boolean fahrGruen, boolean fahrGelb, boolean fahrRot, boolean fussGruen, boolean fussRot)
{ // diese Funktion setzt den Zustand aller Ampel-LEDs
  digitalWrite(fahrGruenPin, fahrGruen);
  if (fahrGruen) Serial.print("A-Gruen\t");
  digitalWrite(fahrGelbPin, fahrGelb);
  if (fahrGelb) Serial.print("A-Gelb\t");
  digitalWrite(fahrRotPin, fahrRot);
  if (fahrRot) Serial.print("A-Rot\t");
  digitalWrite(fussGruenPin, fussGruen);
  if (fussGruen) Serial.print("F-Gruen\t");
  digitalWrite(fussRotPin, fussRot);
  if (fussRot) Serial.print("F-Rot\t");
  Serial.println();
}

State FahrbahnGruenInit()
{
  Serial.println("Fahrbahn gruen");
  setAmpel(true, false, false, false, true);
}

State FahrbahnGruen()
{ // Fahrbahn Grünzeit: Bis zur Bettelanforderung
  byte anforderung=digitalRead(anforderungPin);
  if (buttonMode==INPUT_PULLUP) anforderung=!anforderung;
  if (anforderung) Ampel.Set(FahrbahnAnforderungInit,FahrbahnAnforderung);
}

State FahrbahnAnforderungInit()
{
  Serial.println("Bettelampel-Anforderung registriert");
}

State FahrbahnAnforderung()
{ // 10 Sekunden nach der Bettelanforderung bekommt die Fahrbahn gelb
  if(Ampel.Timeout(10000)) Ampel.Set(FahrbahnGelbInit, FahrbahnGelb);
}

State FahrbahnGelbInit()
{
  Serial.println("Fahrbahn gelb");
  setAmpel(false, true, false, false, true);
}

State FahrbahnGelb()
{ // 2 Sekunden Gelbphase
  if(Ampel.Timeout(2000)) Ampel.Set(FahrbahnRotInit, FahrbahnRot);
}

State FahrbahnRotInit()
{
  Serial.println("Fahrbahn rot");
  setAmpel(false, false, true, false, true);
}

State FahrbahnRot()
{ // Rotphase 2 Sekunden für Auto und Fussgänger gleichzeitig
  if(Ampel.Timeout(2000)) Ampel.Set(FussgaengerGruenInit, FussgaengerGruen);
}

State FussgaengerGruenInit()
{
  Serial.println("Fussgaenger gruen");
  setAmpel(false, false, true, true, false);
}

State FussgaengerGruen()
{ // Grünphase der Fussgängerampel: 12 Sekunden
  if(Ampel.Timeout(12000)) Ampel.Set(FussgaengerRotInit, FussgaengerRot);
}

State FussgaengerRotInit()
{
  Serial.println("Fussgaenger rot");
  setAmpel(false, false, true, false, true);
}

State FussgaengerRot()
{ // Räumzeit des Fussgängerstreifens bei rot: 8 Sekunden
  if(Ampel.Timeout(8000)) Ampel.Set(FahrbahnGruenInit, FahrbahnGruen);
}

void setup()
{
  Serial.begin(9600);
  pinMode(fahrGruenPin, OUTPUT);
  pinMode(fahrGelbPin, OUTPUT);
  pinMode(fahrRotPin, OUTPUT);
  pinMode(fussGruenPin, OUTPUT);
  pinMode(fussRotPin, OUTPUT);
  pinMode(anforderungPin, buttonMode);
}//setup()

void loop()
{
  EXEC(Ampel); // Finite State Machine rennt endlos
}//loop()


Alle Zustandsänderungen werden zur Kontrolle auch auf Serial ausgegeben.

Die Programmlogik ist wie folgt:
Auto-Grünphase: unendlich lange, bis der Bettelknopf vom Fußgänger gedrückt wird
Auto-Grünphase nach Bettelanforderung: weitere 10 Sekunden
Auto-Gelbphase: 2 Sekunden
Doppel-Rotphase für Auto und Fußgänger: 2 Sekunden
Grünphase für Fußgänger: 12 Sekunden
Doppel-Rotphase für Auto und Fußgänger: 8 Sekunden
und dann wieder alles auf Anfang

Bei Button bin ich davon ausgegangen, dass Du nur den Button ohne Pull-Widerstand anschließt. Falls Du einen Button mit PullDown-Widerstand in Deiner Hardwareschaltung verwendest, den "buttonMode" im Programm auf INPUT statt INPUT_PULLUP umstellen!

Edit/Nachtrag: Die Erweiterung dieses Beispiels um ein "Anforderungslicht" nach dem Drücken des Buttons und um einen "Standby-Betrieb" ist extrem einfach, bei Bedarf könnte ich auch eine Version posten, die bei Fahrbahn-Grün und 60 Sekunden Nicht-Anforderung die Schaltung in "Standby" schickt (alle Lichter aus), und die aus dem Standby-Betrieb dann beim Drücken des Anforderung-Buttons dann gleich direkt in den Zustand "FahrbahnAnforderung" springt.

agmue

@jurs: Schöner Vergleich von meinem Anfänger- und Deinem Proficode! Ich hatte im Stillen gehofft, daß Du mal wieder etwas Deines Könnens zeigst.  8) Gruß in den Norden!
Die Vorstellungskraft ist wichtiger als Wissen, denn Wissen ist begrenzt. (Albert Einstein)

Serenifly

switch/case ist völlig legitimer Weg für Zustandsautomaten. Ja, das ist ein Einstiegsmuster für die Implementierung für FSMs. Das macht es aber nicht zwangsläufig schlecht. Für einfache Anwendungen reicht das. Das Problem damit ist, dass der Code unübersichtlich wird wenn man mehr Zustände hat und diese komplexer werden. Hier ist das aber ok.

schlier

@jurs
Wow was für ein Code, davon bin ich noch weit entfernt.
Ich werde die Tage einmal damit rum spielen.
Da ich ja nur eine Fußgängerampel habe, werde ich versuchen es so zu definieren das die Fußgängerampel immer zwischen rot und Grün hin und her schaltet, wobei lange Phase Rot. Wenn man dann Anfordert soll sie zügig umspringen.
Den Standby will ich nach x Phasen ohne Drücken eines Tasters einleiten.

Hättest Du auch eine Idee wie das mit den verschiebenden Programm Modi machen könnte?

Vielen vielen Dank, bin begeistert

Serenifly

Hättest Du auch eine Idee wie das mit den verschiebenden Programm Modi machen könnte?
Verstehe erst mal wie ein Zustandsautomat allgemein funktioniert. Dann verstehst du da auch wie man da zwischen verschiedenen Programmteilen umschalten kann. Egal was die machen.

Siehe auch der Code von agmue. Da wird es vielleicht deutlicher. Man hat eine Variable einem sagt in welcher Phase man sich befindet und je nachdem macht man was anderes.

Hier sind andere Varianten wie man das realisieren kann:
http://forum.arduino.cc/index.php?topic=332907.0
Die zweite Version ist zu kompliziert für dich, aber die erste könnte noch verständlich sein. Dabei lässt man einfach jede Funktionen einen Zeiger auf die nachfolgende Funktion zurück geben. Wenn sich nichts ändert ist das ein Zeiger auf die aktuelle Funktion. Aber man kann auch einen Taster abfragen und in eine andere Funktion springen (so ähnlich funktioniert auch die Library die jurs verwendet intern).

schlier

#12
Jul 05, 2015, 08:55 pm Last Edit: Jul 05, 2015, 09:20 pm by schlier
Hallo,

habe ein wenig gespielt und lasse nun die Ampel selber auch die Fußgängerampel grün schalten. Nur bei Anforderung geht noch eine LED an und die Ampel schaltet schneller auf grün.
Die Zeiten habe ich noch nicht richtig angepasst.

Wie kann ich denn die Anforderungspin blinken lassen ohne Delay?

Code: [Select]
#include <SM.h>

SM Ampel(FahrbahnGruenInit, FahrbahnGruen); //Anfangsstatus setzen

byte fahrGruenPin=11;
byte fahrGelbPin=12;
byte fahrRotPin=13;
byte fussGruenPin=10;
byte fussRotPin=9;
byte anforderungSignalPin=8;
byte anforderungPin=2;

#define buttonMode INPUT_PULLUP // INPUT_PULLUP oder INPUT

void setAmpel(boolean fahrGruen, boolean fahrGelb, boolean fahrRot, boolean fussGruen, boolean fussRot, boolean anforderungSignal)
{ // diese Funktion setzt den Zustand aller Ampel-LEDs
  digitalWrite(fahrGruenPin, fahrGruen);
  if (fahrGruen) Serial.print("A-Gruen\t");
  digitalWrite(fahrGelbPin, fahrGelb);
  if (fahrGelb) Serial.print("A-Gelb\t");
  digitalWrite(fahrRotPin, fahrRot);
  if (fahrRot) Serial.print("A-Rot\t");
  digitalWrite(fussGruenPin, fussGruen);
  if (fussGruen) Serial.print("F-Gruen\t");
  digitalWrite(fussRotPin, fussRot);
  if (fussRot) Serial.print("F-Rot\t");
  digitalWrite(anforderungSignalPin, anforderungSignal);
  if (anforderungSignal) Serial.print("anforderungSignal\t");
  Serial.println();
}

State FahrbahnGruenInit()
{
  Serial.println("Fahrbahn gruen");
  setAmpel(true, false, false, false, true, false);
}

State FahrbahnGruen()
{ // Fahrbahn Grünzeit: Bis zur Bettelanforderung
  byte anforderung=digitalRead(anforderungPin);
  if (buttonMode==INPUT_PULLUP) anforderung=!anforderung;
  if (anforderung) Ampel.Set(FahrbahnAnforderungInit,FahrbahnAnforderung);
  if (Ampel.Timeout(10000)) Ampel.Set(FahrbahnGelbInit, FahrbahnGelb);
}

State FahrbahnAnforderungInit()
{
  Serial.println("Bettelampel-Anforderung registriert");
  setAmpel(true, false, false, false, true, true);
}

State FahrbahnAnforderung()
{ // 5 Sekunden nach der Bettelanforderung bekommt die Fahrbahn gelb
  if(Ampel.Timeout(5000)) Ampel.Set(FahrbahnGelbInit, FahrbahnGelb);
}

State FahrbahnGelbInit()
{
  Serial.println("Fahrbahn gelb");
  setAmpel(false, true, false, false, true, false);
}

State FahrbahnGelb()
{ // 2 Sekunden Gelbphase
  if(Ampel.Timeout(2000)) Ampel.Set(FahrbahnRotInit, FahrbahnRot);
}

State FahrbahnRotInit()
{
  Serial.println("Fahrbahn rot");
  setAmpel(false, false, true, false, true, false);
}

State FahrbahnRot()
{ // Rotphase 2 Sekunden für Auto und Fussgänger gleichzeitig
  if(Ampel.Timeout(2000)) Ampel.Set(FussgaengerGruenInit, FussgaengerGruen);
}

State FussgaengerGruenInit()
{
  Serial.println("Fussgaenger gruen");
  setAmpel(false, false, true, true, false, false);
}

State FussgaengerGruen()
{ // Grünphase der Fussgängerampel: 10 Sekunden
  if(Ampel.Timeout(10000)) Ampel.Set(FussgaengerRotInit, FussgaengerRot);
}

State FussgaengerRotInit()
{
  Serial.println("Fussgaenger rot");
  setAmpel(false, false, true, false, true, false);
}

State FussgaengerRot()
{ // Räumzeit des Fussgängerstreifens bei rot: 8 Sekunden
  if(Ampel.Timeout(8000)) Ampel.Set(FahrbahnRotGelbInit, FahrbahnRotGelb);
}
State FahrbahnRotGelbInit()
{
  Serial.println("Fahrbahn rot gelb");
  setAmpel(false, true, true, false, true, false);
}
State FahrbahnRotGelb()
{ // Rotgelb 2 Sekunden für Auto
  if(Ampel.Timeout(2000)) Ampel.Set(FahrbahnGruenInit, FahrbahnGruen);
}


void setup()
{
  Serial.begin(9600);
  pinMode(fahrGruenPin, OUTPUT);
  pinMode(fahrGelbPin, OUTPUT);
  pinMode(fahrRotPin, OUTPUT);
  pinMode(fussGruenPin, OUTPUT);
  pinMode(fussRotPin, OUTPUT);
  pinMode(anforderungSignalPin, OUTPUT);
  pinMode(anforderungPin, buttonMode);
}//setup()

void loop()
{
  EXEC(Ampel); // Finite State Machine rennt endlos
}//loop()

Serenifly

So z.B.:

Code: [Select]

void blink(byte pin, unsigned long interval)
{
  static unsigned long previousMillis;

  if (millis() - previousMillis > interval)
  {
    previousMillis = millis();
    digitalWrite(pin, !digitalRead(pin));
  }
}

schlier

#14
Jul 05, 2015, 09:22 pm Last Edit: Jul 05, 2015, 09:23 pm by schlier
und wie füge ich dies nun ein?

Aktuell steuere ich die LED ja so an


Code: [Select]
void setAmpel(boolean fahrGruen, boolean fahrGelb, boolean fahrRot, boolean fussGruen, boolean fussRot, boolean anforderungSignal)


State FahrbahnAnforderungInit()
{
 Serial.println("Bettelampel-Anforderung registriert");
 setAmpel(true, false, false, false, true, true);
}

State FahrbahnAnforderung()
{ // 5 Sekunden nach der Bettelanforderung bekommt die Fahrbahn gelb
 if(Ampel.Timeout(5000)) Ampel.Set(FahrbahnGelbInit, FahrbahnGelb);
}

Go Up