First OOP draft, Added readme

This commit is contained in:
Julian Appel 2026-03-16 23:05:03 +01:00
parent c4861d9793
commit 43497376cb
6 changed files with 736 additions and 41 deletions

202
Seilbahn/README.md Normal file
View File

@ -0,0 +1,202 @@
# Doppelmayr Modell-Seilbahn
Arduino-basierte Steuerung einer Doppelmayr Modell-Seilbahn. Die Steuerung bildet das Bedienpanel einer echten Anlage nach — mit Taster-LED-Kombinationen, Betriebsmodi und einer vollständigen Fahrtrichtungssteuerung.
## Hardware
**Mikrocontroller:** Arduino Mega 2560
**Bedienpanel:**
| Taster / LED | Funktion |
|----------------------------|-------------------------------------------------------|
| Anlage Ein/Aus | Anlage ein- und ausschalten |
| Station Besetzt/Unbesetzt | Stationsstatus anzeigen |
| Servicebetrieb | In den Servicemodus wechseln |
| Fahrgastbetrieb | In den Fahrgastmodus wechseln |
| Quittieren | Zustandswechsel bestätigen |
| Start | Fahrt starten |
| Halt | Anlage anhalten |
| Nothalt | Anlage sofort stoppen |
| Vorwärts | Fahrtrichtung Vorwärts wählen |
| Rückwärts | Fahrtrichtung Rückwärts wählen |
**Ereignisanzeige (3-farbig, ohne Taster):**
| LED | Bedeutung |
|-------|----------------------------------|
| Rot | Servicebetrieb aktiv |
| Gelb | Zustandswechsel wartet auf Quittierung |
| Grün | Fahrgastbetrieb aktiv |
## Pinbelegung
| Signal | Pin |
|-----------------------------|-----|
| LED Anlage Ein/Aus | 2 |
| Taster Anlage Ein/Aus | 3 |
| LED Station | 4 |
| Taster Station | 5 |
| LED Servicebetrieb | 6 |
| Taster Servicebetrieb | 7 |
| LED Quittieren | 9 |
| Taster Quittieren | 10 |
| LED Grün Ereignisanzeige | 11 |
| LED Gelb Ereignisanzeige | 12 |
| LED Rot Ereignisanzeige | 13 |
| LED Fahrgastbetrieb | 22 |
| Taster Fahrgastbetrieb | 23 |
| LED Vorwärts | 30 |
| Taster Vorwärts | 31 |
| LED Rückwärts | 32 |
| Taster Rückwärts | 33 |
| LED Start | 36 |
| Taster Start | 37 |
| LED Halt | 38 |
| Taster Halt | 39 |
| LED Nothalt | 40 |
| Taster Nothalt | 41 |
## Software
### Architektur
```
main.cpp
└── CMainController
├── CEventQueue (zentrale Event-Queue)
├── CButton × 10 (je ein Objekt pro Taster/LED-Paar)
└── Zustandsmaschinen (AnlagenZustand + FahrtRichtung)
```
Die Steuerung basiert auf einer **Event-Queue**: Jeder `CButton` erkennt Tastendrücke per Flankenauswertung mit Entprellung und schreibt einen `SEvent` in die zentrale Queue. Der `CMainController` verarbeitet diese Events jeden Loop-Durchlauf und aktualisiert die Zustände sowie alle LEDs.
**Loop-Ablauf in `CMainController::work()`:**
1. `pollButtons()` — alle relevanten Buttons abfragen, Events in Queue schreiben
2. `processEvents()` — Events aus Queue lesen, Zustand ändern
3. `updateLEDs()` — alle LEDs anhand des aktuellen Zustands setzen
### LED-Modi
LEDs können drei Zustände haben, die in `CButton` verwaltet werden:
| Modus | Verhalten |
|----------------|------------------------------------|
| `LEDMode::OFF` | Aus |
| `LEDMode::ON` | Dauerhaft an |
| `LEDMode::BLINK` | Blinkt (500 ms Intervall) |
Alle LEDs im `BLINK`-Modus blinken synchron, da der Blink-Timer als `static` Member in `CButton` für alle Instanzen geteilt wird.
### Zustandsmaschinen
#### AnlagenZustand
```mermaid
stateDiagram-v2
[*] --> AUS
AUS --> SERVICE : BTN_ANLAGE_EIN_AUS
SERVICE --> AUS : BTN_ANLAGE_EIN_AUS
SERVICE --> FAHRGAST_QUITTIEREN : BTN_FAHRGAST
FAHRGAST_QUITTIEREN --> AUS : BTN_ANLAGE_EIN_AUS
FAHRGAST_QUITTIEREN --> FAHRGAST : BTN_QUIT
FAHRGAST --> AUS : BTN_ANLAGE_EIN_AUS
FAHRGAST --> SERVICE_QUITTIEREN : BTN_SERVICE
SERVICE_QUITTIEREN --> AUS : BTN_ANLAGE_EIN_AUS
SERVICE_QUITTIEREN --> SERVICE : BTN_QUIT
```
#### FahrtRichtung
Aktiv wenn `AnlagenZustand == SERVICE` oder `AnlagenZustand == FAHRGAST`.
```mermaid
stateDiagram-v2
[*] --> HALT
HALT --> VORWAERTS_QUITTIEREN : BTN_VORWAERTS
HALT --> RUECKWAERTS_QUITTIEREN : BTN_RUECKWAERTS
HALT --> WARTE_START_VORWAERTS : BTN_QUIT\n[vorherigeRichtung != RUECKWAERTS]
HALT --> WARTE_START_RUECKWAERTS : BTN_QUIT\n[vorherigeRichtung == RUECKWAERTS]
NOTHALT --> VORWAERTS_QUITTIEREN : BTN_VORWAERTS
NOTHALT --> RUECKWAERTS_QUITTIEREN : BTN_RUECKWAERTS
NOTHALT --> WARTE_START_VORWAERTS : BTN_QUIT\n[vorherigeRichtung != RUECKWAERTS]
NOTHALT --> WARTE_START_RUECKWAERTS : BTN_QUIT\n[vorherigeRichtung == RUECKWAERTS]
VORWAERTS_QUITTIEREN --> WARTE_START_VORWAERTS : BTN_QUIT
VORWAERTS_QUITTIEREN --> RUECKWAERTS_QUITTIEREN : BTN_RUECKWAERTS
VORWAERTS_QUITTIEREN --> HALT : BTN_HALT
VORWAERTS_QUITTIEREN --> NOTHALT : BTN_NOTHALT
WARTE_START_VORWAERTS --> VORWAERTS : BTN_START
VORWAERTS --> HALT : BTN_HALT
VORWAERTS --> NOTHALT : BTN_NOTHALT
RUECKWAERTS_QUITTIEREN --> WARTE_START_RUECKWAERTS : BTN_QUIT
RUECKWAERTS_QUITTIEREN --> VORWAERTS_QUITTIEREN : BTN_VORWAERTS
RUECKWAERTS_QUITTIEREN --> HALT : BTN_HALT
RUECKWAERTS_QUITTIEREN --> NOTHALT : BTN_NOTHALT
WARTE_START_RUECKWAERTS --> RUECKWAERTS : BTN_START
RUECKWAERTS --> HALT : BTN_HALT
RUECKWAERTS --> NOTHALT : BTN_NOTHALT
```
## PlatformIO
Das Projekt verwendet [PlatformIO](https://platformio.org) als Build-System — eine Alternative zur Arduino IDE mit deutlich besserer Projekt- und Bibliotheksverwaltung.
### Voraussetzungen
- [VS Code](https://code.visualstudio.com/) mit der [PlatformIO-Extension](https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide)
- oder PlatformIO Core (CLI) via `pip install platformio`
### Projekt bauen und flashen
```bash
# Projekt kompilieren
pio run
# Auf den Arduino flashen (USB)
pio run --target upload
# Seriellen Monitor öffnen (9600 Baud)
pio device monitor
```
### Konfiguration (`platformio.ini`)
```ini
[env:megaatmega2560]
platform = atmelavr
board = megaatmega2560
framework = arduino
lib_deps = mike-matera/ArduinoSTL@^1.3.3
```
- **board:** Arduino Mega 2560
- **lib_deps:** ArduinoSTL — stellt `std::vector` für die EventQueue bereit, da die Arduino-Standardbibliothek keine STL enthält
### Projektstruktur
```
Seilbahn/
├── platformio.ini # Board- und Bibliothekskonfiguration
├── src/
│ ├── main.cpp # setup() und loop()
│ ├── CMainController # Hauptsteuerung, besitzt alle Objekte
│ ├── CButton # Taster-LED-Abstraktion mit Entprellung
│ ├── CEventQueue # FIFO-Queue für Button-Events
│ └── SEvent # Event-Typ und -Struktur
├── include/ # Globale Header (leer)
├── lib/ # Projektlokale Bibliotheken (leer)
└── test/ # Unit-Tests (PlatformIO-Testframework)
```

View File

@ -12,3 +12,4 @@
platform = atmelavr
board = megaatmega2560
framework = arduino
lib_deps = mike-matera/ArduinoSTL@^1.3.3

View File

@ -1,47 +1,83 @@
#include "CButton.h"
#include "Arduino.h"
CButton::CButton(short btnPin, short ledPin)
// Einmalige Definition der static-Member (Speicher wird hier reserviert)
unsigned long CButton::s_lastBlinkTime = 0;
bool CButton::s_blinkState = false;
CButton::CButton(short btnPin, short ledPin, EVENT_TYPE eventType, CEventQueue* queue)
: m_btnPin(btnPin)
, m_ledPin(ledPin)
, m_eventType(eventType)
, m_queue(queue)
, m_currentButtonState(false)
, m_buttonState(false)
, m_ledMode(LEDMode::OFF)
{
m_btnPin = btnPin;
m_ledPin = ledPin;
m_currentButtonState = false;
m_buttonState = false;
}
bool CButton::getLED()
void CButton::setup()
{
return digitalRead(m_ledPin);
pinMode(m_btnPin, INPUT_PULLUP);
pinMode(m_ledPin, OUTPUT);
}
void CButton::setLED(bool status)
void CButton::setLEDMode(LEDMode mode)
{
digitalWrite(m_ledPin, status);
m_ledMode = mode;
}
LEDMode CButton::getLEDMode()
{
return m_ledMode;
}
void CButton::work()
{
// Taster abfragen und aktuellen Zustand zwischenspeichern
// ===== LED aktualisieren =====
switch (m_ledMode)
{
case LEDMode::ON:
digitalWrite(m_ledPin, HIGH);
break;
case LEDMode::OFF:
digitalWrite(m_ledPin, LOW);
break;
case LEDMode::BLINK:
// s_lastBlinkTime und s_blinkState sind static → werden von allen
// Instanzen geteilt. Der erste Button der in diesem Loop work() aufruft
// aktualisiert den Timer — alle anderen lesen denselben Zustand.
// So blinken alle LEDs im BLINK-Modus exakt synchron.
if (millis() - s_lastBlinkTime >= BLINK_INTERVAL_MS)
{
s_lastBlinkTime = millis();
s_blinkState = !s_blinkState;
}
digitalWrite(m_ledPin, s_blinkState ? HIGH : LOW);
break;
}
// ===== Button abfragen =====
m_currentButtonState = digitalRead(m_btnPin) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
// Flankenauswertung: nur bei steigender Flanke reagieren (nicht gedrückt → gedrückt)
if (m_currentButtonState && !m_buttonState)
{
// Warten zum entprellen
// Entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
m_currentButtonState = digitalRead(m_btnPin) == LOW;
// Prüfen ob immernoch gedrückt
if (m_currentButtonState == LOW)
{
// Knopf gedrückt => Code ausführen
// ============================================================
fahrtrichtung = HALT;
// ============================================================
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
m_buttonState = m_currentButtonState;
}
if (m_currentButtonState)
{
SEvent event;
event.m_eventType = m_eventType;
event.m_additionalInfo = 0;
m_queue->pushEvent(event);
}
}
// Immer aktualisieren — so wird der Button nach dem Loslassen wieder erkannt
m_buttonState = m_currentButtonState;
}

View File

@ -1,24 +1,36 @@
#ifndef CBUTTON_H_
#define CBUTTON_H_
#include "SEvent.h"
#include "CEventQueue.h"
enum class LEDMode { OFF, ON, BLINK };
class CButton
{
public:
CButton(short btnPin, short ledPin);
void connect(); // Button mit Controller "verbinden"
void work(); // Button periodisch abfragen
bool getLED(); // LED status abfragen
void setLED(bool status); // LED an/ausschalten
CButton(short btnPin, short ledPin, EVENT_TYPE eventType, CEventQueue* queue);
void setup(); // pinMode setzen (im setup() aufrufen)
void work(); // Button abfragen + LED-Zustand aktualisieren
void setLEDMode(LEDMode mode);
LEDMode getLEDMode();
private:
short m_btnPin; // Pinnummer Button am Arduino
short m_ledPin; // Pinnummer LED am Arduino
short m_btnPin;
short m_ledPin;
EVENT_TYPE m_eventType;
CEventQueue* m_queue;
bool m_currentButtonState; // Hilfsvariable für aktuellen Status
bool m_buttonState; // Hilfsvariable für letzten Status
bool m_currentButtonState;
bool m_buttonState;
}
LEDMode m_ledMode;
#endif /* CBUTTON_H_ */
// static: einmal für alle CButton-Instanzen — sorgt für synchrones Blinken
static unsigned long s_lastBlinkTime;
static bool s_blinkState;
static const int BLINK_INTERVAL_MS = 500;
};
#endif /* CBUTTON_H_ */

View File

@ -0,0 +1,363 @@
#include "CMainController.h"
#include "Arduino.h"
// Initialisierungsliste: m_queue wird zuerst gebaut (steht zuerst im Header),
// danach die Buttons — jeder bekommt &m_queue, was zu diesem Zeitpunkt bereits
// gültig ist.
CMainController::CMainController()
: m_queue()
, m_btnAnlageEinAus (PIN_KNOPF_ANLAGE_EIN_AUS, PIN_LED_ANLAGE_EIN_AUS, BTN_ANLAGE_EIN_AUS, &m_queue)
, m_btnStation (PIN_KNOPF_STATION, PIN_LED_STATION, BTN_STATION, &m_queue)
, m_btnServicebetrieb(PIN_KNOPF_SERVICEBETRIEB, PIN_LED_SERVICEBETRIEB, BTN_SERVICE, &m_queue)
, m_btnFahrgastbetrieb(PIN_KNOPF_FAHRGASTBETRIEB, PIN_LED_FAHRGASTBETRIEB, BTN_FAHRGAST, &m_queue)
, m_btnQuitBetrieb (PIN_KNOPF_QUIT_BETRIEB, PIN_LED_QUIT_BETRIEB, BTN_QUIT, &m_queue)
, m_btnStart (PIN_KNOPF_START_BETRIEB, PIN_LED_START_BETRIEB, BTN_START, &m_queue)
, m_btnHalt (PIN_KNOPF_HALT, PIN_LED_HALT, BTN_HALT, &m_queue)
, m_btnNothalt (PIN_KNOPF_NOTHALT, PIN_LED_NOTHALT, BTN_NOTHALT, &m_queue)
, m_btnVorwaerts (PIN_KNOPF_VORWAERTS, PIN_LED_VORWAERTS, BTN_VORWAERTS, &m_queue)
, m_btnRueckwaerts (PIN_KNOPF_RUECKWAERTS, PIN_LED_RUECKWAERTS, BTN_RUECKWAERTS, &m_queue)
, m_anlagenzustand(AnlagenZustand::AUS)
, m_fahrtrichtung(FahrtRichtung::HALT)
, m_vorherigeRichtung(FahrtRichtung::HALT)
{
}
// ============================================================
// setup(): einmalig beim Start aufrufen
// ============================================================
void CMainController::setup()
{
// Jeden Button mit setup() initialisieren (setzt pinMode für Taster + LED)
m_btnAnlageEinAus.setup();
m_btnStation.setup();
m_btnServicebetrieb.setup();
m_btnFahrgastbetrieb.setup();
m_btnQuitBetrieb.setup();
m_btnStart.setup();
m_btnHalt.setup();
m_btnNothalt.setup();
m_btnVorwaerts.setup();
m_btnRueckwaerts.setup();
// Die drei Ereignis-LEDs haben keinen Button → direkt hier initialisieren
pinMode(PIN_LED_ROT_EREIGNISANZEIGE, OUTPUT);
pinMode(PIN_LED_GELB_EREIGNISANZEIGE, OUTPUT);
pinMode(PIN_LED_GRUEN_EREIGNISANZEIGE, OUTPUT);
}
// ============================================================
// work(): jeden Loop-Durchlauf aufrufen
// ============================================================
void CMainController::work()
{
pollButtons(); // 1. Buttons abfragen → Events landen in der Queue
processEvents(); // 2. Events verarbeiten → Zustand ändern
updateLEDs(); // 3. LEDs anhand des neuen Zustands setzen
}
// ============================================================
// pollButtons(): work() aller Buttons aufrufen
// ============================================================
void CMainController::pollButtons()
{
// Welche Buttons abgefragt werden, hängt vom aktuellen Anlagenzustand ab.
// So können z.B. im AUS-Zustand nur relevante Buttons Events erzeugen.
switch (m_anlagenzustand)
{
case AnlagenZustand::AUS:
m_btnAnlageEinAus.work();
break;
case AnlagenZustand::SERVICE_QUITTIEREN:
case AnlagenZustand::FAHRGAST_QUITTIEREN:
m_btnAnlageEinAus.work();
m_btnQuitBetrieb.work();
break;
case AnlagenZustand::SERVICE:
m_btnAnlageEinAus.work();
m_btnFahrgastbetrieb.work();
m_btnQuitBetrieb.work();
m_btnStart.work();
m_btnHalt.work();
m_btnNothalt.work();
m_btnVorwaerts.work();
m_btnRueckwaerts.work();
break;
case AnlagenZustand::FAHRGAST:
m_btnAnlageEinAus.work();
m_btnServicebetrieb.work();
m_btnQuitBetrieb.work();
m_btnStart.work();
m_btnHalt.work();
m_btnNothalt.work();
m_btnVorwaerts.work();
m_btnRueckwaerts.work();
break;
default:
break;
}
}
// ============================================================
// processEvents(): alle Events in der Queue verarbeiten
// ============================================================
void CMainController::processEvents()
{
bool valid;
SEvent ev = m_queue.popEvent(&valid);
while (valid)
{
switch (m_anlagenzustand)
{
// ==================== AUS ====================
case AnlagenZustand::AUS:
if (ev.m_eventType == BTN_ANLAGE_EIN_AUS)
m_anlagenzustand = AnlagenZustand::SERVICE;
break;
// ==================== SERVICE_QUITTIEREN ====================
// Zustandswechsel nach SERVICE oder FAHRGAST wurde ausgelöst,
// Nutzer muss erst quittieren bevor es weitergeht.
case AnlagenZustand::SERVICE_QUITTIEREN:
if (ev.m_eventType == BTN_ANLAGE_EIN_AUS)
m_anlagenzustand = AnlagenZustand::AUS;
else if (ev.m_eventType == BTN_QUIT)
m_anlagenzustand = AnlagenZustand::SERVICE;
break;
// ==================== SERVICE ====================
case AnlagenZustand::SERVICE:
if (ev.m_eventType == BTN_ANLAGE_EIN_AUS)
m_anlagenzustand = AnlagenZustand::AUS;
else if (ev.m_eventType == BTN_FAHRGAST)
m_anlagenzustand = AnlagenZustand::FAHRGAST_QUITTIEREN;
else
processFahrtrichtungEvent(ev); // Fahrtrichtungs-Events weiterleiten
break;
// ==================== FAHRGAST_QUITTIEREN ====================
case AnlagenZustand::FAHRGAST_QUITTIEREN:
if (ev.m_eventType == BTN_ANLAGE_EIN_AUS)
m_anlagenzustand = AnlagenZustand::AUS;
else if (ev.m_eventType == BTN_QUIT)
m_anlagenzustand = AnlagenZustand::FAHRGAST;
break;
// ==================== FAHRGAST ====================
case AnlagenZustand::FAHRGAST:
if (ev.m_eventType == BTN_ANLAGE_EIN_AUS)
m_anlagenzustand = AnlagenZustand::AUS;
else if (ev.m_eventType == BTN_SERVICE)
m_anlagenzustand = AnlagenZustand::SERVICE_QUITTIEREN;
else
processFahrtrichtungEvent(ev);
break;
default:
break;
}
ev = m_queue.popEvent(&valid); // nächstes Event holen
}
}
// ============================================================
// processFahrtrichtungEvent(): Fahrtrichtungs-Statemachine
// Wird von processEvents() aufgerufen wenn der Anlagenzustand
// SERVICE oder FAHRGAST ist und kein Anlagen-Event vorliegt.
// ============================================================
void CMainController::processFahrtrichtungEvent(SEvent ev)
{
switch (m_fahrtrichtung)
{
// ===== Anlage steht (normal oder Nothalt) =====
case FahrtRichtung::HALT:
case FahrtRichtung::NOTHALT:
if (ev.m_eventType == BTN_QUIT)
{
// Richtung von vor dem Halt wiederherstellen
if (m_vorherigeRichtung == FahrtRichtung::RUECKWAERTS)
m_fahrtrichtung = FahrtRichtung::WARTE_START_RUECKWAERTS;
else
m_fahrtrichtung = FahrtRichtung::WARTE_START_VORWAERTS;
}
else if (ev.m_eventType == BTN_VORWAERTS)
m_fahrtrichtung = FahrtRichtung::VORWAERTS_QUITTIEREN;
else if (ev.m_eventType == BTN_RUECKWAERTS)
m_fahrtrichtung = FahrtRichtung::RUECKWAERTS_QUITTIEREN;
break;
// ===== Vorwärts gewählt, wartet auf Quittieren =====
case FahrtRichtung::VORWAERTS_QUITTIEREN:
if (ev.m_eventType == BTN_QUIT)
m_fahrtrichtung = FahrtRichtung::WARTE_START_VORWAERTS;
else if (ev.m_eventType == BTN_HALT)
m_fahrtrichtung = FahrtRichtung::HALT;
else if (ev.m_eventType == BTN_NOTHALT)
m_fahrtrichtung = FahrtRichtung::NOTHALT;
else if (ev.m_eventType == BTN_RUECKWAERTS)
m_fahrtrichtung = FahrtRichtung::RUECKWAERTS_QUITTIEREN;
break;
// ===== Quittiert, wartet auf Start =====
case FahrtRichtung::WARTE_START_VORWAERTS:
if (ev.m_eventType == BTN_START)
{
m_fahrtrichtung = FahrtRichtung::VORWAERTS;
m_vorherigeRichtung = FahrtRichtung::VORWAERTS;
}
break;
// ===== Anlage fährt vorwärts =====
case FahrtRichtung::VORWAERTS:
if (ev.m_eventType == BTN_HALT)
m_fahrtrichtung = FahrtRichtung::HALT;
else if (ev.m_eventType == BTN_NOTHALT)
m_fahrtrichtung = FahrtRichtung::NOTHALT;
break;
// ===== Rückwärts gewählt, wartet auf Quittieren =====
case FahrtRichtung::RUECKWAERTS_QUITTIEREN:
if (ev.m_eventType == BTN_QUIT)
m_fahrtrichtung = FahrtRichtung::WARTE_START_RUECKWAERTS;
else if (ev.m_eventType == BTN_HALT)
m_fahrtrichtung = FahrtRichtung::HALT;
else if (ev.m_eventType == BTN_NOTHALT)
m_fahrtrichtung = FahrtRichtung::NOTHALT;
else if (ev.m_eventType == BTN_VORWAERTS)
m_fahrtrichtung = FahrtRichtung::VORWAERTS_QUITTIEREN;
break;
// ===== Quittiert, wartet auf Start =====
case FahrtRichtung::WARTE_START_RUECKWAERTS:
if (ev.m_eventType == BTN_START)
{
m_fahrtrichtung = FahrtRichtung::RUECKWAERTS;
m_vorherigeRichtung = FahrtRichtung::RUECKWAERTS;
}
break;
// ===== Anlage fährt rückwärts =====
case FahrtRichtung::RUECKWAERTS:
if (ev.m_eventType == BTN_HALT)
m_fahrtrichtung = FahrtRichtung::HALT;
else if (ev.m_eventType == BTN_NOTHALT)
m_fahrtrichtung = FahrtRichtung::NOTHALT;
break;
default:
break;
}
}
// ============================================================
// updateLEDs(): alle LEDs anhand des aktuellen Zustands setzen
// Wird jedes Loop aufgerufen → LEDs sind immer konsistent.
// ============================================================
void CMainController::updateLEDs()
{
// ===== Anlage Ein/Aus =====
// LED leuchtet solange die Anlage eingeschaltet ist
if (m_anlagenzustand != AnlagenZustand::AUS)
m_btnAnlageEinAus.setLEDMode(LEDMode::ON);
else
m_btnAnlageEinAus.setLEDMode(LEDMode::OFF);
// ===== Station Besetzt/Unbesetzt =====
// LED leuchtet solange die Anlage eingeschaltet ist
if (m_anlagenzustand != AnlagenZustand::AUS)
m_btnStation.setLEDMode(LEDMode::ON);
else
m_btnStation.setLEDMode(LEDMode::OFF);
// ===== Servicebetrieb =====
// LED leuchtet im Servicezustand (auch während Quittieren)
if (m_anlagenzustand == AnlagenZustand::SERVICE
|| m_anlagenzustand == AnlagenZustand::SERVICE_QUITTIEREN)
m_btnServicebetrieb.setLEDMode(LEDMode::ON);
else
m_btnServicebetrieb.setLEDMode(LEDMode::OFF);
// ===== Fahrgastbetrieb =====
// LED leuchtet im Fahrgastzustand (auch während Quittieren)
if (m_anlagenzustand == AnlagenZustand::FAHRGAST
|| m_anlagenzustand == AnlagenZustand::FAHRGAST_QUITTIEREN)
m_btnFahrgastbetrieb.setLEDMode(LEDMode::ON);
else
m_btnFahrgastbetrieb.setLEDMode(LEDMode::OFF);
// ===== Quittieren =====
// LED blinkt wenn ein Zustandswechsel auf Quittierung wartet
if (m_anlagenzustand == AnlagenZustand::SERVICE_QUITTIEREN
|| m_anlagenzustand == AnlagenZustand::FAHRGAST_QUITTIEREN)
m_btnQuitBetrieb.setLEDMode(LEDMode::BLINK);
else
m_btnQuitBetrieb.setLEDMode(LEDMode::OFF);
// ===== Ereignisanzeige (3-farbig) =====
// Keine Buttons → direkt per digitalWrite gesetzt
// Rot: Servicebetrieb aktiv
if (m_anlagenzustand == AnlagenZustand::SERVICE)
digitalWrite(PIN_LED_ROT_EREIGNISANZEIGE, HIGH);
else
digitalWrite(PIN_LED_ROT_EREIGNISANZEIGE, LOW);
// Gelb: Zustandswechsel wartet auf Quittierung
if (m_anlagenzustand == AnlagenZustand::SERVICE_QUITTIEREN
|| m_anlagenzustand == AnlagenZustand::FAHRGAST_QUITTIEREN)
digitalWrite(PIN_LED_GELB_EREIGNISANZEIGE, HIGH);
else
digitalWrite(PIN_LED_GELB_EREIGNISANZEIGE, LOW);
// Grün: Fahrgastbetrieb aktiv
if (m_anlagenzustand == AnlagenZustand::FAHRGAST)
digitalWrite(PIN_LED_GRUEN_EREIGNISANZEIGE, HIGH);
else
digitalWrite(PIN_LED_GRUEN_EREIGNISANZEIGE, LOW);
// ===== Fahrtrichtungs-LEDs =====
// Nur relevant wenn die Anlage eingeschaltet ist
// Halt: leuchtet wenn Anlage steht (normaler Halt)
if (m_anlagenzustand != AnlagenZustand::AUS && m_fahrtrichtung == FahrtRichtung::HALT)
m_btnHalt.setLEDMode(LEDMode::ON);
else
m_btnHalt.setLEDMode(LEDMode::OFF);
// Nothalt: leuchtet wenn Nothalt aktiv
if (m_anlagenzustand != AnlagenZustand::AUS && m_fahrtrichtung == FahrtRichtung::NOTHALT)
m_btnNothalt.setLEDMode(LEDMode::ON);
else
m_btnNothalt.setLEDMode(LEDMode::OFF);
// Start: blinkt wenn quittiert wurde und auf Start gewartet wird
if (m_anlagenzustand != AnlagenZustand::AUS &&
(m_fahrtrichtung == FahrtRichtung::WARTE_START_VORWAERTS || m_fahrtrichtung == FahrtRichtung::WARTE_START_RUECKWAERTS) )
m_btnStart.setLEDMode(LEDMode::BLINK);
else
m_btnStart.setLEDMode(LEDMode::OFF);
// Vorwärts: leuchtet dauerhaft nur wenn Anlage tatsächlich vorwärts fährt,
// blinkt während Quittieren und Warten auf Start
if (m_anlagenzustand != AnlagenZustand::AUS && m_fahrtrichtung == FahrtRichtung::VORWAERTS)
m_btnVorwaerts.setLEDMode(LEDMode::ON);
else if (m_anlagenzustand != AnlagenZustand::AUS &&
(m_fahrtrichtung == FahrtRichtung::VORWAERTS_QUITTIEREN || m_fahrtrichtung == FahrtRichtung::WARTE_START_VORWAERTS))
m_btnVorwaerts.setLEDMode(LEDMode::BLINK);
else
m_btnVorwaerts.setLEDMode(LEDMode::OFF);
// Rückwärts: leuchtet dauerhaft nur wenn Anlage tatsächlich rückwärts fährt,
// blinkt während Quittieren und Warten auf Start
if (m_anlagenzustand != AnlagenZustand::AUS && m_fahrtrichtung == FahrtRichtung::RUECKWAERTS)
m_btnRueckwaerts.setLEDMode(LEDMode::ON);
else if (m_anlagenzustand != AnlagenZustand::AUS &&
(m_fahrtrichtung == FahrtRichtung::RUECKWAERTS_QUITTIEREN || m_fahrtrichtung == FahrtRichtung::WARTE_START_RUECKWAERTS))
m_btnRueckwaerts.setLEDMode(LEDMode::BLINK);
else
m_btnRueckwaerts.setLEDMode(LEDMode::OFF);
}

View File

@ -1,13 +1,94 @@
#ifndef CSEILMAINCONTROLLER_H_
#define CSEILMAINCONTROLLER_H_
#include
#include "CButton.h"
#include "CEventQueue.h"
#include "SEvent.h"
// enum class = scoped enum → Werte sind nicht global sichtbar, sondern nur als
// AnlagenZustand::AUS, AnlagenZustand::SERVICE etc.
// Das verhindert Namenskonflikte mit den gleichnamigen Werten in main.cpp.
enum class AnlagenZustand
{
AUS,
SERVICE_QUITTIEREN,
SERVICE,
FAHRGAST_QUITTIEREN,
FAHRGAST,
};
enum class FahrtRichtung
{
HALT,
NOTHALT,
VORWAERTS_QUITTIEREN,
WARTE_START_VORWAERTS,
VORWAERTS,
RUECKWAERTS_QUITTIEREN,
WARTE_START_RUECKWAERTS,
RUECKWAERTS,
};
class CMainController
{
public:
CMainController();
void setup(); // pinMode aller Buttons und LEDs setzen
void work(); // Buttons abfragen, Events verarbeiten, LEDs aktualisieren
private:
}
// ===== Pin-Definitionen =====
static const short PIN_LED_ANLAGE_EIN_AUS = 2;
static const short PIN_KNOPF_ANLAGE_EIN_AUS = 3;
static const short PIN_LED_STATION = 4;
static const short PIN_KNOPF_STATION = 5;
static const short PIN_LED_SERVICEBETRIEB = 6;
static const short PIN_KNOPF_SERVICEBETRIEB = 7;
static const short PIN_LED_FAHRGASTBETRIEB = 22;
static const short PIN_KNOPF_FAHRGASTBETRIEB = 23;
static const short PIN_LED_ROT_EREIGNISANZEIGE = 13;
static const short PIN_LED_GELB_EREIGNISANZEIGE = 12;
static const short PIN_LED_GRUEN_EREIGNISANZEIGE = 11;
static const short PIN_LED_QUIT_BETRIEB = 9;
static const short PIN_KNOPF_QUIT_BETRIEB = 10;
static const short PIN_LED_START_BETRIEB = 36;
static const short PIN_KNOPF_START_BETRIEB = 37;
static const short PIN_LED_HALT = 38;
static const short PIN_KNOPF_HALT = 39;
static const short PIN_LED_NOTHALT = 40;
static const short PIN_KNOPF_NOTHALT = 41;
static const short PIN_LED_VORWAERTS = 30;
static const short PIN_KNOPF_VORWAERTS = 31;
static const short PIN_LED_RUECKWAERTS = 32;
static const short PIN_KNOPF_RUECKWAERTS = 33;
#endif /* CSEILMAINCONTROLLER_H_ */
// ===== EventQueue =====
// Muss VOR den Buttons deklariert sein! Member werden in Deklarationsreihenfolge
// initialisiert — die Buttons bekommen einen Zeiger auf diese Queue.
CEventQueue m_queue;
// ===== Buttons =====
CButton m_btnAnlageEinAus;
CButton m_btnStation;
CButton m_btnServicebetrieb;
CButton m_btnFahrgastbetrieb;
CButton m_btnQuitBetrieb;
CButton m_btnStart;
CButton m_btnHalt;
CButton m_btnNothalt;
CButton m_btnVorwaerts;
CButton m_btnRueckwaerts;
// ===== Zustandsvariablen =====
AnlagenZustand m_anlagenzustand;
FahrtRichtung m_fahrtrichtung;
FahrtRichtung m_vorherigeRichtung; // Merkt sich die letzte Fahrtrichtung für Quittieren nach HALT/NOTHALT
// ===== Private Hilfsmethoden =====
void pollButtons(); // Alle work()-Methoden der Buttons aufrufen
void processEvents(); // Events aus der Queue lesen und Zustand ändern
void processFahrtrichtungEvent(SEvent ev); // Fahrtrichtungs-Statemachine
void updateLEDs(); // Alle LEDs anhand des aktuellen Zustands setzen
};
#endif /* CMAINCONTROLLER_H_ */