Arduino-Projekt: Parkplatz mit Schranke

Ein spannendes Technikprojekt für unseren Kurs

Projektidee

Wir bauen gemeinsam ein kleines Parkplatzsystem mit Schranke. Es erkennt ankommende Autos, überprüft die Verfügbarkeit von zwei Parkplätzen und steuert eine Schranke sowie LEDs.

Ziele des Projekts

Benötigte Bauteile

Funktionsweise

Das System hat zwei Parkplätze. Mit den Lichtsensoren wird festgestellt, ob ein Platz frei oder belegt ist:

Vor der Schranke erkennt ein Ultraschallsensor, ob ein Auto ankommt. Nur wenn mindestens ein Parkplatz frei ist, öffnet die Schranke. Sind beide Plätze belegt, bleibt sie geschlossen.

Schritt-für-Schritt Aufbau

  1. Verdrahte den Ultraschallsensor (TRIG, ECHO, VCC, GND) mit dem Arduino.
  2. Verbinde den Servomotor mit 5V, GND und einem digitalen Pin (z. B. D6).
  3. Baue die Lichtsensoren als Spannungsteiler mit Widerständen auf und schließe sie an A0 und A1 an.
  4. Setze LEDs mit Vorwiderständen ein und verbinde sie mit digitalen Pins.
  5. Prüfe jeden Teil einzeln: LEDs, Sensoren, Servo.
  6. Kombiniere danach die Logik: Schranke reagiert auf Auto + Parkplatzstatus.
Tipp: Teste zuerst nur einen Parkplatz mit LEDs und Sensor. Wenn das funktioniert, baue den zweiten dazu.
Achtung: Servomotoren brauchen manchmal mehr Strom als der Arduino liefern kann. Falls der Servo zittert, nutze eine externe 5V-Stromquelle (gemeinsame Masse verbinden!).

Arduino-Code (C++)

Der Code ist stark kommentiert, um jeden Schritt verständlich zu machen, und verwendet die millis()-Funktion statt delay() für die Schrankensteuerung, damit das Programm reaktionsfähig bleibt.

/*
 * Arduino-Projekt: Parkplatz mit Schranke
 * Dieser Sketch steuert ein Parkplatzsystem mit zwei Plätzen.
 * - Ein Ultraschallsensor (HC-SR04) erkennt ankommende Autos.
 * - Zwei Lichtsensoren (LDRs) prüfen, ob die Parkplätze belegt sind.
 * - LEDs (rot/grün) zeigen den Status jedes Parkplatzes an.
 * - Eine Ankunfts-LED leuchtet, wenn ein Auto erkannt wird.
 * - Ein Servomotor (SG90) steuert die Schranke.
 *
 * Die Schranke öffnet nur, wenn ein Auto ankommt UND mindestens ein Platz frei ist.
 */

// 1. Bibliotheken einbinden
#include <Servo.h> // Für die Ansteuerung des Servomotors

// 2. Pin-Definitionen (Konstanten)
// Ändere diese Pins, falls du eine andere Verdrahtung verwendest.
// Ultraschallsensor (Ankunft)
const int TRIG_PIN = 9;
const int ECHO_PIN = 10;
// Servomotor (Schranke)
const int SERVO_PIN = 6;
// Lichtsensoren (Parkplätze)
const int LDR1_PIN = A0; // Parkplatz 1
const int LDR2_PIN = A1; // Parkplatz 2
// LEDs
const int LED_GREEN1_PIN = 2; // Parkplatz 1 Frei
const int LED_RED1_PIN   = 3; // Parkplatz 1 Belegt
const int LED_GREEN2_PIN = 4; // Parkplatz 2 Frei
const int LED_RED2_PIN   = 5; // Parkplatz 2 Belegt
const int LED_ARRIVAL_PIN = 7; // Auto an Schranke erkannt

// 3. Schwellenwerte und Einstellungen (WICHTIG: Diese musst du anpassen!)
// Schwellenwert für die Lichtsensoren (0-1023)
// HINWEIS: Dieser Wert hängt stark von deiner Spannungsteiler-Schaltung
// und dem Umgebungslicht ab!
// Annahme: Spannungsteiler ist [5V] -- [10k Widerstand] -- [A0] -- [LDR] -- [GND]
// -> Mehr Licht = LDR Widerstand sinkt = Spannung an A0 sinkt
// -> Weniger Licht (Auto drüber) = LDR Widerstand steigt = Spannung an A0 steigt
// Teste die Werte mit dem Seriellen Monitor!
const int LDR_THRESHOLD = 500; // Beispielwert, wenn A0 > 500, dann "belegt"
// Schwellenwert für den Ultraschallsensor (in cm)
// Wenn ein Auto näher als dieser Wert ist, gilt es als "angekommen".
const int CAR_DISTANCE_THRESHOLD = 20; // z.B. 20 cm
// Positionen für den Servomotor (0-180 Grad)
const int SERVO_CLOSED_POS = 0;   // Schranke zu
const int SERVO_OPEN_POS   = 90;  // Schranke offen
// Wie lange die Schranke offen bleiben soll, *nachdem* das Auto weg ist (in Millisekunden)
const long BARRIER_OPEN_DELAY = 5000; // 5 Sekunden

// 4. Globale Variablen
Servo myservo; // Servo-Objekt erstellen
// Zustandsvariablen für die Logik
bool spot1_occupied = false;
bool spot2_occupied = false;
bool car_at_gate    = false;
bool barrier_is_open = false;
// Timer für die Schranke (verwendet millis())
unsigned long barrier_timer_start = 0;

// 5. Setup-Funktion (wird einmal beim Start ausgeführt)
void setup() {
  Serial.begin(9600); // Seriellen Monitor starten (zum Testen der Sensorwerte)
  
  // Pins für LEDs als AUSGANG definieren
  pinMode(LED_GREEN1_PIN, OUTPUT);
  pinMode(LED_RED1_PIN,   OUTPUT);
  pinMode(LED_GREEN2_PIN, OUTPUT);
  pinMode(LED_RED2_PIN,   OUTPUT);
  pinMode(LED_ARRIVAL_PIN, OUTPUT);

  // Pins für Ultraschallsensor
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);

  // LDR-Pins sind standardmäßig Eingänge (analogRead)

  // Servo initialisieren
  myservo.attach(SERVO_PIN);
  myservo.write(SERVO_CLOSED_POS); // Sicherstellen, dass die Schranke am Anfang zu ist
  Serial.println("Parkplatzsystem gestartet.");
}

// 6. Loop-Funktion (wird ständig wiederholt)
void loop() {
  // Schritt 1: Alle Sensoren auslesen und Zustände aktualisieren
  checkParkingSpots();
  checkCarArrival();

  // Schritt 2: LEDs basierend auf den Zuständen steuern
  updateParkingLEDs();
  updateArrivalLED();

  // Schritt 3: Die Schrankenlogik ausführen
  controlBarrier();

  // (Optional) Sensorwerte zur Kalibrierung ausgeben
  // printSensorValues(); 
  
  delay(50); // Kleine Verzögerung, um das System stabil zu halten
}

// 7. Hilfsfunktionen
/*
 * Liest die beiden LDR-Sensoren aus und setzt die
 * globalen 'spot1_occupied' und 'spot2_occupied' Variablen.
 */
void checkParkingSpots() {
  int ldr1_value = analogRead(LDR1_PIN);
  int ldr2_value = analogRead(LDR2_PIN);

  // Logik umkehren, falls LDRs anders verschaltet sind!
  // (z.B. < LDR_THRESHOLD)
  spot1_occupied = (ldr1_value > LDR_THRESHOLD);
  spot2_occupied = (ldr2_value > LDR_THRESHOLD);
}

/*
 * Fragt den Ultraschallsensor ab und setzt
 * die globale 'car_at_gate' Variable.
 */
void checkCarArrival() {
  long duration;
  int distance_cm;

  // Impuls senden
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Impuls empfangen und Zeit messen
  duration = pulseIn(ECHO_PIN, HIGH);
  
  // Zeit in Distanz umrechnen (cm)
  distance_cm = duration * 0.034 / 2;

  // Prüfen, ob die Distanz innerhalb des Schwellenwerts liegt
  if (distance_cm > 0 && distance_cm < CAR_DISTANCE_THRESHOLD) {
    car_at_gate = true;
  } else {
    car_at_gate = false;
  }
}

/*
 * Steuert die rot/grün LEDs für beide Parkplätze.
 */
void updateParkingLEDs() {
  // Parkplatz 1
  digitalWrite(LED_GREEN1_PIN, !spot1_occupied); // Grün AN, wenn NICHT belegt
  digitalWrite(LED_RED1_PIN,   spot1_occupied); // Rot AN, wenn belegt

  // Parkplatz 2
  digitalWrite(LED_GREEN2_PIN, !spot2_occupied);
  digitalWrite(LED_RED2_PIN,   spot2_occupied);
}

/*
 * Steuert die Ankunfts-LED.
 */
void updateArrivalLED() {
  digitalWrite(LED_ARRIVAL_PIN, car_at_gate);
}

/*
 * Hauptlogik: Steuert die Schranke (Servo).
 * Verwendet millis() für eine nicht-blockierende Steuerung.
 */
void controlBarrier() {
  // Prüfen, ob Plätze frei sind
  bool spots_available = !spot1_occupied || !spot2_occupied;

  if (car_at_gate && spots_available) {
    // Fall 1: Auto ist da UND Plätze sind frei
    if (!barrier_is_open) {
      // Schranke öffnen, falls sie noch zu ist
      myservo.write(SERVO_OPEN_POS);
      barrier_is_open = true;
      Serial.println("Schranke öffnet.");
    }
    // WICHTIG: Timer jedes Mal zurücksetzen, solange das Auto da steht
    barrier_timer_start = millis(); 
    
  } else if (car_at_gate && !spots_available) {
    // Fall 2: Auto ist da ABER alles ist belegt
    if (barrier_is_open) {
      myservo.write(SERVO_CLOSED_POS);
      barrier_is_open = false;
      Serial.println("Parkplatz voll. Schranke schließt.");
    }
    
  } else if (!car_at_gate && barrier_is_open) {
    // Fall 3: Kein Auto (mehr) da, ABER Schranke ist noch offen
    // Prüfen, ob die Wartezeit (DELAY) abgelaufen ist
    if (millis() - barrier_timer_start > BARRIER_OPEN_DELAY) {
      // Zeit ist um -> Schranke schließen
      myservo.write(SERVO_CLOSED_POS);
      barrier_is_open = false;
      Serial.println("Auto durchgefahren. Schranke schließt.");
    }
  }
  // Fall 4: Kein Auto da, Schranke ist zu -> Nichts tun.
}

/*
 * (Optionale) Debug-Funktion: Gibt Sensorwerte im Seriellen Monitor aus.
 * Entferne die '//' vor 'printSensorValues();' in der loop(), um dies zu nutzen.
 */
void printSensorValues() {
  Serial.print("LDR1: ");
  Serial.print(analogRead(LDR1_PIN));
  Serial.print(" (Belegt: ");
  Serial.print(spot1_occupied);
  Serial.print(") | LDR2: ");
  Serial.print(analogRead(LDR2_PIN));
  Serial.print(" (Belegt: ");
  Serial.print(spot2_occupied);
  Serial.print(") | Distanz: ");
  Serial.print(getDistance()); // Ruft die Distanzmessung erneut auf
  Serial.print("cm (Auto: ");
  Serial.print(car_at_gate);
  Serial.println(")");
}

// Kleine Hilfsfunktion, um die Distanz nur für das Debugging zu bekommen
int getDistance() {
  long duration;
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  duration = pulseIn(ECHO_PIN, HIGH);
  return duration * 0.034 / 2;
}

Wichtige Hinweise zum Programm

1. Kalibrierung der LDRs (Lichtsensoren)

Der wichtigste Schritt, damit das Programm funktioniert, ist die Einstellung des LDR_THRESHOLD.

  1. Lade den Code auf dein Arduino.
  2. Öffne den Seriellen Monitor (Werkzeuge -> Serieller Monitor).
  3. Entferne die Kommentarzeichen (//) vor der Zeile printSensorValues(); in der loop()-Funktion und lade den Code erneut hoch.
  4. Du siehst jetzt die Live-Werte deiner LDRs (0-1023).
  5. Notiere den Wert, wenn der Parkplatz frei ist (z.B. 350).
  6. Decke den Sensor ab (z.B. mit einem Spielzeugauto oder deiner Hand), um "belegt" zu simulieren. Notiere diesen Wert (z.B. 700).
  7. Dein LDR_THRESHOLD muss zwischen diesen beiden Werten liegen (z.B. 500).
  8. Trage diesen Wert oben im Code bei const int LDR_THRESHOLD = 500; ein.
Hinweis zur LDR-Schaltung: Der Code geht von einer Schaltung aus, bei der der LDR-Wert steigt, wenn es dunkler wird (Auto darüber). Falls deine Schaltung umgekehrt ist (Wert sinkt bei Dunkelheit), musst du die Logik in checkParkingSpots() umdrehen: spot1_occupied = (ldr1_value < LDR_THRESHOLD);

2. Kalibrierung des Ultraschallsensors

Stelle einen Gegenstand (z.B. eine Schachtel) in der Entfernung vor den Sensor, in der ein Auto erkannt werden soll. Lies den "Distanz"-Wert im Seriellen Monitor ab und passe CAR_DISTANCE_THRESHOLD entsprechend an.

3. Stromversorgung des Servos (Wichtig!)

Wie im Projekttext erwähnt: Wenn dein Servo zittert oder sich nicht bewegt, reicht der Strom vom USB-Anschluss des Arduino nicht aus.

Lösung: Verwende eine externe 5V-Stromquelle (z.B. ein altes Handynetzteil mit abgeschnittenem Kabel oder ein Batteriepack).

Fragen zum Nachdenken

Mögliche Erweiterungen

Viel Erfolg bei eurem Projekt! 🚗