Fussgängerampel

Hallo,
ich habe ein klassisches Problem. An einer Kreuzung sind Ampeln für KFZ. Die werden alle über "delay()" gesteuert;
grün, delay(), gelb, delay(), usw.
Ein Taster soll nach dem Drücken alle KFZ-Ampeln auf "rot" schalten und allen Fußgängern an der Kreuzung "grün" geben.
Wenn ich das richtig verstanden habe, geht das nicht, weil ich in der delay() Abfolge ja nicht den Taster auswerten kann.
Nur am Anfang der loop()-Funktion z.B. Wenn ein komplettes Schalten der Ampeln jetzt z.B. 3 Minuten dauert, dann wird auch
nur alle 3 Minuten geguckt, ob der Taster für die Fußgänger gerade betätigt ist.
Kann man trotzdem delay() verwenden und einen Taster abfragen. Oder geht das nur über das Messen der verstrichenen Zeit ("Blink without delay").

Gruß
Thomas

Funktioniert, wenn Du für den Knopf einen Interrupt nimmst.

Guck Dir z.B. mal die Funktion attachInterrupt() an.

Du könntest z.B. in der Interrupt-Routine (in dem Beispiel die Funktion "blink()") eine boolean-Variable auf true setzen und das dann zur gegebenen Zeit in der loop() abfragen und entsprechend reagieren (zurücksetzen nicht vergessen!).

Lieber Thomas,

das Beispiel klingt ja gerade zu danach, als wäre das ein Projekt für die Schule. Die korrekte Antwort lautet: Vergiß deine delay() Aufrufe, schau dir einmal das Beispiel "Blink without Delay" an und baue deine Ampelsteuerung entsprechend um, damit auch der Fußgängerknopf abgefragt wird. Mit Interrupts funktioniert es übrigens nicht, da du den gesamten Zyklus umstellen mußt. Mit Interrupts könntest du nur unabhängig etwas machen.

Viel Spaß bei deiner Hausübung.

Korman

Korman:
Mit Interrupts funktioniert es übrigens nicht, da du den gesamten Zyklus umstellen mußt. Mit Interrupts könntest du nur unabhängig etwas machen.

Entschuldige, wenn ich hier einhake, und ich hab mich auch noch nicht so en Detail mit Ampelschaltungen auseinandergesetzt (insofern möge man mich bitte korrigieren, wenn ich Mist verzapfe), aber im Prinzip müsste er doch irgendwo in seinem Ablauf eine Stelle haben, an der er die Fußgängerampel auf grün stellt, wartet bis der Fußgänger über die Straße ist, und die Ampel dann wieder auf rot stellt, und diese Stelle kann er doch einfach überspringen, wenn der Knopf nicht gedrückt wurde. Oder nicht?

Warum also nicht in einem Interrupt ein Flag (z.B. eine kleine boolean Variable als Markierung) setzen und mehr nicht und wenn man dann in der loop() an besagter Stelle ankommt dieses Flag abprüfen (anschließendes Zurücksetzen nicht vergessen!) und dann entsprechend entweder den Fußgänger-grün-Abschnitt abarbeiten oder aber ihn einfach überspringen...?

Joghurt, im Prinzip hast du schon recht, aber diese ganze erforderliche Umstellung ist wahrscheinlich noch komplizierter als es einfach ohne delay()-Aufrufe zu machen.

Korman

Sehe ich auch so, Möglich mit Interrupt zu arbeiten aber der Code muß umgeschrieben werden und da ist es besser gleich den Code ohne delay zu schreiben.
Grüße Uwe

Also entweder stehe ich heute massiv auf dem Schlauch, oder...

Er hat doch schon den kompletten Ablauf fertig, mit delay().

Also benötigt er einfach noch eine kleine Interrupt-Funktion, die das Flag setzt, einen attachInterrupt() darauf im setup(), und eine kleine if-Abfrage samt Rücksetzung des Flags um den Block mit der Fußgängerampel, und fertig.

Oder hab ich echt was Fundamentales übersehen?

Habe ich nicht so verstanden. Ich hatte eher den Eindruck ,er hat das erste Beispiel im Kurs gelöst, wo die Ampel mit delay() vor sich herumblinkt. Jetzt soll er Logik einbauen damit auch eine Fußgängerampel bei Bedarf korrekt gesteuert wird - also während des delay() auf Rot schalten, dann die Fußgänger bedienen um danach wieder mit rot-gelb auf grün schalten.

Das ganze ist ein klassisches Beispiel für die Schule, man braucht nur ein paar LED und Karton. Wenn man es besonders luxuriös ist kann man auch eine kleine Modellbahnampel verwenden. Es wurde mich nicht wundern, wenn sich der liebe Thomas mit einem Problem mit Ventilatoren und Pingpongbällen in weiß und scharz in Plexiglasröhren meldet. Das wäre dann der nächste Schritt - Sensoren und dynamische Steuerung von Prozessen.

Korman

@omthomas: Mich würde mal interessieren, wie der Ablauf der Ampelphasen vorgegeben wurde, ist das ein Flussdiagramm oder eine Liste oder wie kommt sowas? Kannst Du das mal hier reinstellen?

Update: Ah, gefunden! :slight_smile:

Hallo zusammen,

das Problem mit Ventilatoren und Pingpongbällen kenne ich gar nicht.

Nein, ich überlege, wie man Arduino im Unterricht einsetzten kann. Die "normale" Ampel und Lauflichter sind ja Standard.
Das geht auch alles ganz gut mit delay();
Es wird ja erst "kompliziert" wenn ein Taster überwacht werden muss. Dann ist das mit dem dealy() vorbei und man muss
sich dem Sketch "Blink without Delay" zuwenden. Daher kam meine Frage: gibt es noch andere Lösungen einen Taster zu überwachen?
Und da klingt doch die Funktion attachInterrupt() sehr vielversprechend.

Danke für die Hinweise
Thomas

Dieser?

das Problem mit Ventilatoren und Pingpongbällen kenne ich gar nicht.

Die einfache Version hat Msquare verlinkt. Für Fortgeschrittene gibt es eine Plexiglassäule mit unten einem Ventilator, auf der Hälfte etwa eine Quersäule mit ebenfalls einem Ventilator. Auf 25% ist ein Photosensor der hell/dunkel des Balls unterscheiden kann, eventuell noch ein paar Lichtschranken um die Kreuzung und den Sensor.

Die Aufgabe: Hellen oder dunklen Pingpongball oben einwerfen. Ball wir zum Sensor gehoben mit dem vertikalen Ventilator um die Farbe zu bestimmen. Danach werden weiße Bälle nach oben ausgeworfen, schwarze durch die Querröhre auf die Seite.

Das Problem ist simple zu verstehen aber nicht ganz anspruchslos umzusetzen.

Korman

Ok, kann das jetzt gerade nicht probieren, aber vielleicht als Denkanstoß...mit einer if-Abfrage in der Steuerung könnte man das doch bestimmt lösen:

Rot_Ein
if (taster = nicht_gedrueckt)
{
delay(rotphase)
}
else
{
funktion(fussgaengerampel)
}
.
.
.

Edit: Bin gerade neu angemeldet und suche immer noch den Vorstellungs-Thread...gibt es hier so etwas? wenn ja, dann bitte mal dezent drauf hinweisen :wink:

Hi lumi, willkommen! :slight_smile:

Im Prinzip funktioniert das wie von Dir vorgeschlagen schon, allerdings muss man den Taster dann ganz genau in dem Moment gedrückt halten, in dem der Microcontroller an der if-Anweisung vorbeikommt, und es soll ja funktionieren, wenn irgendwann innerhalb eines Zyklus gedrückt wird.

Für eine Ampel wird weder ein Delay noch ein Interrupt benötigt.
Der Ablauf hat ja ein festes Schema, somit würde ich das Ganze in einzelne Zustände aufteilen.
Jeder einzelne Zustand hat feste Zeiten, die einzige Ausnahme ist die Grünphase der Verkehrsampel.

Wir erstellen uns also erst einmal ein paar #defines um jedem Zustand einen Namen zu geben (erhöht die Lesbarkeit des Codes).
Zu der Zuständen im Einzelnen und deren Anzahl komme ich später noch.
Wichtig: Die Zustände aufsteigend durchnummerieren.

Dann brauchen wir noch ein paar Arrays. Die Größe jedes Array entspricht der Anzahl der Zustände.
Wir haben fünf Lampen (Rot/Gelb/Grün Verkehr, Rot/Grün Fußgänger), also fünf Arrays für den Status (HIGH/LOW) der LED-Pins.
Dann brauchen wir noch ein Array in dem die Ablaufdauer jedes Zustandes gespeichert wird.

Dann benötigen wir noch zwei Variablen:
Ein int, um den aktuellen Zustand zu speichern.
Ein unsigned long, dort wird abgelegt, wann der aktuelle Zustand begonnen hat.

Legen wir nun die Zustände und den möglichen Ablauf fest:

Zustand 1 (Z_GRUEN_ROT): Verkehrsampel ist Grün, Fußgängerampel ist Rot (Zeit: variabel)
Zustand 2 (Z_GRUEN_ROT_TASTE): Verkehrsampel ist Grün, Fußgängerampel ist Rot, Taster wurde gedrückt (Zeit: fest)
Zustand 3 (Z_GELB_ROT): Verkehrsampel ist Gelb, Fußgängerampel ist Rot (Zeit: fest)
Zustand 4 (Z_ROT_ROT_A): Verkehrsampel ist Rot, Fußgängerampel ist Rot (Zeit: fest)
Zustand 5 (Z_ROT_GRUEN): Verkehrsampel ist Rot, Fußgängerampel ist Grün (Zeit: fest)
Zustand 6 (Z_ROT_ROT_E): Verkehrsampel ist Rot, Fußgängerampel ist Rot (Zeit: fest)
Zustand 7 (Z_ROTGELB_ROT): Verkehrsampel ist Rot/Gelb, Fußgängerampel ist Rot (Zeit: fest)

Innerhalb von setup() wird die Zustandsvariable auf Z_GRUEN_ROT gesetzt. Die Zeitvariable ist erst mal egal, da Z_GRUEN_ROT ja keine feste Zeitvorgabe hat.
Anhand der Werte für Z_GRUEN_ROT in den Lampen-Arrays werden die Pins gesetzt.

Innerhalb von loop() kommt nur eine if-else Abfrage ob der aktuelle Zustand Z_GRUEN_ROT ist. Die ganze Funktionalität spielt sich entweder im if oder im else Block ab. Danach ist loop() auch schon zuende und beginnt von vorne.

Solange Z_GRUEN_ROT aktuell ist, trifft die Abfrage zu.
Innerhalb der if-Schleife wird nun der Taster abgefragt.
Ist er nicht gedrückt, wars das auch schon. if wird verlassen, loop() beginnt von vorne.
Ist der Taster gedrückt, wird der aktuelle Zustand auf Z_GRUEN_ROT_TASTE gesetzt. In der Zeitvariable wird der aktuelle Wert von millis() gespeichert. Das wars, if Schleife verlassen, loop() beginnt von vorne.

Nun wird es spannend:
Da der aktuelle Zustand ja nun nicht mehr Z_GRUEN_ROT ist, wird in den else-Teil der if-Abfrage gesprungen.
Dort wird zuerst die Ablaufdauer des aktuellen Zustands (egal welcher das ist) aus dem Array ausgelesen. Solange der Wert der Zeitvariable + Ablaufdauer größer als millis() ist, ist die Zeit für den aktuellen Zustand noch nicht abgelaufen. Es gibt also nichts zu tun, else wird verlassen, loop() beginnt von vorne.
Ist die Ablaufzeit erreicht, so wird in den nächsten Zustand übergegangen. Da die Abfolge immer gleich ist, kann man die Zustandsvariable einfach um eins hochzählen. Ist der Wert > Z_ROTGELB_ROT wird er auf Z_GRUEN_ROT gesetzt.
Anhand der Werte für den aktuellen Zustand in den Lampen-Arrays werden wieder die Pins gesetzt.
In der Zeitvariable wird wieder der aktuelle Wert von millis() gespeichert.
Das wars, else verlassen, loop() beginnt von vorne.

Zur Info: Der Zustand Z_GRUEN_ROT_TASTE wird benötigt, da ja nach dem drücken der Taste nicht sofort eine Ampelschaltung erfolgt.

Was noch eine sinnvolle Erweiterung wäre:
Den Zeitpunkt der letzten Ampelschaltung speichern. Wenn die Fußgängerampel erst vor kurzem Grün war, sollte die Pause nach dem Drücken der Taste länger sein.

Ich bin mir jetzt nicht sicher ob ich jetzt alles bedacht habe und der Ablauf so einwandfrei funktioniert. Aber vom Ansatz sollte es passen.

Hallo Mafu,

für eine "einfache" Fussgängerampel ist Deine Idee sehr gut und funktioniert auch.

Ich bin bei meinem Problem aber von eine Kreuzung ausgegangen, an der an zwei Straßen Verkehrsampel stehen. In loop() wird der Ampelablauf gesteuert.
Und jetzt kommt so ein Fussgänger und will diesen Vorgang unterbrechen: ALLE KFZ-Ampel werden auf rot und die Fußgänger auf grün geschaltet (Rundumgrün - gibt es in der Realität auch, dient der Sicherheit von Fussgängern).
Es wird also in eine laufende Steuerung eingegriffen - und da bedarf es leider sehr wohl eines Interrupts bzw einer Zeitmessung.

Bei Deinem Beispiel, wo die KFZ-Ampel immer gün hat, kann die Überprüfung des Tasters natürlich sofort erfolgen; es passiert ja weiter nichts in Deinem loop().
Bei mir aber kann ein loop() auch z.B. 5 Minuten dauern - alle Kreuzungen hatten dann einmal rot, gelb, grün - und dann erst wird der Taster wieder abgefragt.

Das ist der große Unterschied.

Vielen Dank für Dein Beispiel
Thomas

Ist dann fast genauso durchführbar und ebenso einfach. Der Unterschied ist dann nur, dass bereits in der loop() Schleife zwischen acht Basiszuständen durchgewechselt wird.
ROT_GRUEN, ROT_GELB, ROT_ROT, ROTGELB_ROT, GRUEN_ROT, GELB_ROT, ROT_ROT, ROT_ROTGELB
Das geht genauso mit Zustandszähler hochzählen nach Zeitablauf.
Dabei wird jedesmal die Taste abgefragt. Sobald sie gedrückt wurde, dies in einer Variable (Tastenflag) speichern. Sobald dann einer der ROT_ROT Zustände erreicht und gleichzeitig das Tastenflag gesetzt ist einfach die Fußgängerschleife durchführen. Nach deren Ende das Tastenflag wieder löschen und der normale Ampelfluss geht weiter.

So eine Kreuzung mit Rundumgrün für Fußgänger hab ich ehrlich gesagt noch nirgends gesehen.

Du hast recht; das funktioniert.
Es wird deutlich, das man um eine Zeitmessung nicht rumkommt.

Ich werde das mal aufbauen.

Gruß
Thomas

Alle KFZ-Ampeln auf rot ist in Deutschland wenig üblich,
es gibt aber solche Schaltungen:

T.

Danke für die Info.
So hab ich auch wieder was gelernt.