Der Ampel-Sketch

Pins am Arduino definieren

Die zeitgesteuerte Ampel

Hierzu führen wir eine neuen Befehl ein.

Auch hier haben wir wieder mehrere Möglichkeiten, an dieser Stelle entscheiden wir uns für #define. Mit #define kannst du einem konstanten Wert (z.B. dem Pin auf dem Board) einen Namen zuweisen (bevor das Programm kompiliert wird).

#define am besten immer ganz nach oben in eurem Code schreiben.

Zur Erinnerung: Regeln zur Benennung von Variablen
  • keine Umlaute (Beispiel: grün -> geht nicht     gruen -> geht)
  • keine Leerzeichen im Namen (Beispiel:  LED rot -> geht nicht     LED_rot -> geht)
  • Groß- oder Kleinschreibung macht einen Unterschied (Rot und rot ist nicht die selbe Variable)

  • Die Namen dürfen nur 1x vorkommen
    Schreibt ihr z.B. #define Rot, dürft ihr KEINE Funktion so benennen [also NICHT: void Rot()      void rot() -> geht]

  • Nicht nur 1 Großbuchstaben (z.B. R) verwenden, sonst ist es ein Makro (wollen wir nicht)

Die zeitgesteuerte Ampel

Übung

Kannst du eine Ampel mit den Ampelphasen programmieren und die LEDs mit #define initialisieren?

Noch haben wir keine Taster oder Sensoren eingebaut, die Ampel ist also rein zeitgesteuert.

Erstelle Funktionen!

Achte dabei darauf, wiederkehrende Befehle direkt mit in den Funktionen aufzunehmen!

Denk an die richtigen Vorwiderstände.

// Zeitgesteuerte Ampel (Ereitert durch Zuweisung der Pins)

 

// Zuweisung der Pins

#define Gruen 11
#define Gelb 12
#define Rot 13

 

// selbst erstellte Funktionen


// Vorsicht bei der Benennung

// Groß- und Kleinschreibung werden unterschieden! Grundsätzlich gilt: Unterschiedliche Sachen (z.B #define und der Name der Fkt) dürfen NIE gleich heißen

 

void gruen(){
   digitalWrite(Rot, LOW);
   digitalWrite(Gelb, LOW);
   digitalWrite(Gruen, HIGH);

   delay(1000);
}

 

void gelb(){
   digitalWrite(Rot, LOW);
   digitalWrite(Gelb, HIGH);
   digitalWrite(Gruen, LOW);

   delay(1000);
}

 

void rot(){
   digitalWrite(Rot, HIGH);
   digitalWrite(Gelb, LOW);
   digitalWrite(Gruen, LOW);

   delay(1000);
}

 

void rot_gelb(){
   digitalWrite(Rot, HIGH);
   digitalWrite(Gelb, HIGH);
   digitalWrite(Gruen, LOW);

   delay(1000);
}

 

 

void setup() {   pinMode(Rot, OUTPUT);
   pinMode(Gelb, OUTPUT);
   pinMode(Gruen, OUTPUT);
}

 

void loop() {
   gruen();
   gelb();
   rot();
   rot_gelb();
   gelb();
}

Der Unterschied

Was ist jetzt eigentlich der Unterschied zu int?

Mit int initialisierst du eine Variable, mit #define kannst du einer festen Konstanten einen Wert zuweisen.

Der Vorteil liegt in 2 Dingen: #define wird vor dem Kompilieren zugewiesen und verbraucht weniger Speicherplatz auf dem Chip.

Der Nachteil: #define wird vor dem Kompilieren zugewiesen und lässt sich später nicht mehr ändern.

Übung

Erinnert ihr euch noch an den Morse-Sketch (Die letzte Übung aus Lektion 3)?

Hier hast du deinen Vornamen auf einer LED und deinen Nachnamen auf einer anderen LED gemorst, in dem du in der loop() die Variable „LED“ geändert hast.

 

Probiere das doch mal mit #define, was fällt dir aus (wenn du an der loop nichts änderst)?

Und wie müsstest du den Code ändern, damit es klappt?

 

-> Du musst hier nichts programmieren, nur überlegen. Ausprobieren kannst/solltest du es natürlich trotzdem mal.

Formuliere in wenigen Sätzen, wie dein Code dann aussehen müsste, wenn du die LED mit #define initialisierst (statt mit int).

Der Taster

Taster initialisieren

Genau wie bei den LEDs musst du dem Mikrocontroller sagen, wo du den Taster angeschlossen hast.

Taster auslesen

Mit dem Befehl digitalRead erkennt das Programm, ob der Taster gedrückt gerade wird. Hier ist der Taster (wie in dem Schaltplan unten) an Pin 13 angeschlossen

Taster anschließen

Der Wiederstand, der mit dem Taster verbunden wird (bei Arduino 10kΩ) wird auch als Pulldown-Widerstand bezeichnet.

Der Widerstand stellt sicher, dass der Eingang (Pin) logisch HIGH ist, wenn der Taster gedrückt wird

Was das heißt, erklärt sich schnell durch die englischen Begriffe: Pull (Ziehen) und  down (runter)

Etwas (die Spannung) wird also runtergezogen.

Neben dem Pulldown-Widerstanfd gibt es auch Pullup-Widerstände, hier wird die Spannung nach oben gezogen

Das hoch und runter bezieht sich auf VCC oder wie bei uns (down) auf GND (=Masse).

Beim Öffnen des Kontakts (loslassen des Tasters), zieht der Pulldown-Widerstand die Spannung am Eingang auf GND (logisch LOW).

Warum brauche ich eigentlich den Pulldown-Widerstand?

Pulldown-Widerständen (genau so wie Pullup-Widerstände) stellen sicher, dass bei einem offenen Kontakt (bei uns also Taster nicht gedrückt) der richtige Eingangspegel definiert ist. (Wird der Taster gedrückt, wird der Widerstand kurzgeschlossen)

Hinweis: Was genau GND, VCC und kurzgeschlossen bedeuten, schauen wir uns später noch einmal genauer an.

Variable zuweisen

Die Information kann (muss nicht) in einer Variable gespeichert werden

Hinweis: Denkt an die Lektion über Variablen!

Der Name der Variable = Den Wert, den ihr zuweisen wollt

Taster in der if-Abfrage

Um den Taster zu verwenden, musst du eine if-Abfrage (if/else) verwenden

Mit dem Taster LEDs steuern

Übung

Probiert das doch mal aus! Schließt einen Taster und eine LED an, und programmiert die LED so, dass:

  • Taster nicht gedrückt -> LED ist aus
  • Tester wird gedrückt -> Die LED ist an, solange der Taster gedrückt wird

Erinnert ihr euch noch an die letzte Lektion?

  • Die if-Abfrage
  • Das Zusammenspiel von if und else

// LED leuchtet, wenn Taster gedrückt

 

#define Button 8
#define LED 13

 

void setup() {

   pinMode(LED, OUTPUT);
   pinMode(Taster, INPUT);
}

 

void loop() {

 

   // Wichtig: Der Name der Variablen (Button) und der Name des Pins (Taster) MÜSSEN unterschiedlich sein!

 

  Button = digitalRead(Taster);

 

   if(Button  == HIGH){

      digitalWrite(LED, HIGH);

   }

 

   else{

      digitalWrite(LED, LOW);

   }

 

}

#define Button 8
#define LED 13

 

void setup() {

   pinMode(LED, OUTPUT);
   pinMode(Taster, INPUT);
}

 

void loop() {

  Button = digitalRead(Taster);

 

  // Wenn nach if oder else NUR EINE Anweisung kommt, darf man die geschweifte Klammer weglassen

   // Ob ihr die Anweisungen untereinander oder in eine Zeile schreibt, ist dem Programm egal

 

   if(Button  == HIGH)   digitalWrite(LED, HIGH);

   else                              digitalWrite(LED, LOW);

}

Zustand merken

Es wäre doch schön, die LED auf Knopfdruck an und auszustellen.

Wie?

Wirt definieren uns einfach die beiden Werte (LED an/aus) und merken uns den Wert!

Mit dem Taster ändern wir dann nicht mehr direkt die Funktion digitalWrite (also HIGH und LOW), sondern ändern die Variable und steuern damit die LED!

Prellen

Übung

Was passiert?

Hinweis: Der Taster (kann wahlweise auf Pin 8 oder 10 angeschlossen werden)

Ihr könnt die Befehle auch in einem Befehl  „zusammenfassen“

 

 

Wird der Taster (Pin 8) angeschlossen, leuchtet die LED nur, wenn während der Taster gedrückt ist.

Wird der Taster (Button, Pin 10) angeschlossen, kann man die LED auf Tastendruck an- und ausschalten.

 

Wichtig sind die unterschiedlichen (Variablen)Namen!

delay(200) verhindert das prellen des Tasters.

Die Fußgänger-Ampel

Erweitere deine Ampel-Schaltung um eine weitere (blaue) LED.

Übung

Standartmäßig ist die Ampel grün und die Fußgänger-Ampel (blau) aus.

Wird der Taster gedrückt:

  • Läuft die Ampel bis rot (grün-gelb-rot)
  • Die blaue LED geht für 3 Sekunden an
  • Die blaue LED blinkt 3 Sekunde lang schnell
  • Die Ampel läuft zurück bis grün

Sieh dir die if-Anweisung aus Zustand merken noch einmal an!

// Fußgängerampel (Erweiterung Ampel-Sketch)

 

// Zuweisung der Pins

#define Gruen 11
#define Gelb 12
#define Rot 13

 

#define Taster 8
#define Blau 9

 

bool Fussgaenger;

 

void gruen(){
   digitalWrite(Rot, LOW); digitalWrite(Gelb, LOW); digitalWrite(Gruen, HIGH); delay(1000);
}

 

void gelb(){
   digitalWrite(Rot, LOW); digitalWrite(Gelb, HIGH); digitalWrite(Gruen, LOW); delay(1000);
}

 

void rot(){
   digitalWrite(Rot, HIGH); digitalWrite(Gelb, LOW); digitalWrite(Gruen, LOW); delay(1000);
}

 

void rot_gelb(){
   digitalWrite(Rot, HIGH); digitalWrite(Gelb, HIGH); digitalWrite(Gruen, LOW); delay(1000);
}

 

void Ampel(){
   Serial.println(„Fußgänger“);         // Nutze den Seriellen Monitor, um zu überprüfen, was dein Programm macht

 

   gelb();
   rot();

 

   digitalWrite(Blau, HIGH);
   delay (3000);
   digitalWrite(Blau, LOW);

 

   blinken(); blinken(); blinken(); blinken(); blinken(); blinken();

 

   rot();
   rot_gelb();
   gelb();

 

   Fussgaenger = false;                // Danach muss die Variable geändert werden, sonst wird die >Funktion erneut aufgerufen
}

 

   // „blink“ wird orange (Syntax-Highlighting), ist also eine Funktion, die es schon gibt und darf nicht als Name verwendet werden
void blinken(){
   digitalWrite(Blau, HIGH); delay (250);
   digitalWrite(Blau, LOW); delay (250);
}

 

 

void setup() {
   pinMode(Rot, OUTPUT);
   pinMode(Gelb, OUTPUT);
   pinMode(Gruen, OUTPUT);

   pinMode(Blau, OUTPUT);
   pinMode(Taster, INPUT);

   Serial.begin(9600);

}

 

void loop() {

   if(digitalRead(Taster) == HIGH){                              // Taster auslesen
      delay(50);                                                                      // entprellen

 

      if(Fussgaenger == true) Fussgaenger = false;
      else                                     Fussgaenger = true;
   }

 

 

   if(Fussgaenger == true)    Ampel();
   else                                        gruen();

 

   // Wenn er nicht so in die Funktion springt, wie du willst, lass dir die Variable hier im seriellen Monitor ausgeben
   // Serial.println(Fußgänger);
}

Übung

Simuliert doch mal das initialisieren der LEDs und lasst alle LEDs am Anfang blinken, wenn das Programm startet.

// Wir schreiben uns einfach eine kleine Funktion

 

void initialisieren(){
   digitalWrite(Rot, LOW); digitalWrite(Gelb, LOW); digitalWrite(Gruen, LOW);  digitalWrite(Gruen, LOW); delay(200);

   digitalWrite(Rot, HIGH); digitalWrite(Gelb, HIGH); digitalWrite(Gruen, HIGH); digitalWrite(Gruen, HIGH); delay(200);
}

 

void setup() {
   pinMode(Rot, OUTPUT);
   pinMode(Gelb, OUTPUT);
   pinMode(Gruen, OUTPUT);

   pinMode(Blau, OUTPUT);
   pinMode(Taster, INPUT);

   Serial.begin(9600);

 

   // Und rufen sie im setup() auf

   initialisieren(); initialisieren(); initialisieren(); initialisieren(); initialisieren();

 

   // Es kann passieren, dass die LED an Pin 13 am Anfang blinkt/flackert/leuchtet und dann erst initialisieren() aufgerufen wird
   // Hier seht ihr den Transfer der Daten-Bits. Wenn euch das stört, verwendet nicht Pin 13
}

Übung

Was wird ausgegeben, wenn ihr wie oben in der Lösung Serial.println(„Fußgänger“); bzw. Serial.println(Fussgaenger); ausgebt?

Warum/Woher kommen die Werte? Und warum die unterschiedlichen schreibweisen?

Serial.println(„Fußgänger“) gibt einen String (also den Text zwischen den “ … „) aus.

Serial.println(Fussgaenger) gibt die Variable (hier Fussgaenger) aus.

 

Da wir in englisch programmieren, wollen wir weder umlaute noch Sonderzeichen (wie ß) verwenden.  Ausgegeben wird 0 oder 1, da die Variable mit dem Typ bool initialisiert wird, kann also nur wahr (true, 1) oder falsch( false, 0) sein.

Hinweis

Vielleicht ist euch aufgefallen, dass jeder Tastendruck ignoriert wird, während die Ampel läuft [Funktion Ampel()].

Das liegt an dem delay-Befehl. Der Befehl hält euren Mikrocontroller an, so dass der Taster gar nicht mehr ausgelesen wird! Das ist natürlich nicht sehr elegant und geht noch schöner; wie das geht, schauen wir uns im Aufbaukurs 1 an.