Compare commits

...

3 Commits

Author SHA1 Message Date
jappel f1e5a758c2 main cleanup 2026-03-17 22:35:07 +01:00
jappel 43497376cb First OOP draft, Added readme 2026-03-16 23:05:03 +01:00
jappel c4861d9793 First OOP parts 2026-03-16 21:40:29 +01:00
11 changed files with 951 additions and 660 deletions
+202
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)
```
+1
View File
@@ -12,3 +12,4 @@
platform = atmelavr
board = megaatmega2560
framework = arduino
lib_deps = mike-matera/ArduinoSTL@^1.3.3
+83
View File
@@ -0,0 +1,83 @@
#include "CButton.h"
#include "Arduino.h"
// 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)
{
}
void CButton::setup()
{
pinMode(m_btnPin, INPUT_PULLUP);
pinMode(m_ledPin, OUTPUT);
}
void CButton::setLEDMode(LEDMode mode)
{
m_ledMode = mode;
}
LEDMode CButton::getLEDMode()
{
return m_ledMode;
}
void CButton::work()
{
// ===== 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;
// Flankenauswertung: nur bei steigender Flanke reagieren (nicht gedrückt → gedrückt)
if (m_currentButtonState && !m_buttonState)
{
// Entprellen
delay(20);
m_currentButtonState = digitalRead(m_btnPin) == LOW;
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;
}
+36
View File
@@ -0,0 +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, 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;
short m_ledPin;
EVENT_TYPE m_eventType;
CEventQueue* m_queue;
bool m_currentButtonState;
bool m_buttonState;
LEDMode m_ledMode;
// 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_ */
+94
View File
@@ -0,0 +1,94 @@
#include "CEventQueue.h"
/*! \fn std::vector<SEvent> CEventQueue::popEvents(EVENT_TYPE eventType)
\brief Liefert einen chronologisch geordneten Vektor (FIFO) der aufgelaufenen
Events in dieser Queue zurueck. Diese werden daraufhin aus der Queue geloescht
\param eventType Falls nicht UNDEFINED, werden nur Events des angegebenen Typs
zurueckgegeben und aus der Queue geloescht, andernfalls alle
\return Chronologisch sortierter Vektor der Events (aelteste zuerst)
*/
std::vector<SEvent> CEventQueue::popEvents(EVENT_TYPE eventType)
{
std::vector<SEvent> ret;
switch(eventType)
{
case UNDEFINED:
ret = m_events;
m_events.clear();
break;
default:
{
std::vector<SEvent> without;
for(std::vector<SEvent>::iterator it=m_events.begin(); it!=m_events.end(); it++)
{
if(it->m_eventType==eventType)
{
ret.push_back(*it);
}
else
{
without.push_back(*it);
}
}
m_events = without;
}
break;
}
return ret;
}
/*! \fn SEvent CEventQueue::popEvent(bool* pValid)
\brief Liefert das chronologisch letzte (neueste) Event in der Queue zurueck
\param pValid true, falls ein Event aus der Queue zurueckgegeben wird,
false andernfalls (dann war die Queue naemlich leer)
\return Das chronologisch letzte (neueste) Event in der Queue
*/
SEvent CEventQueue::popEvent(bool* pValid)
{
return popEvent(m_events.size()-1, pValid);
}
/*! \fn SEvent CEventQueue::popEvent(unsigned short eventNumber,
* bool* pValid)
\brief Liefert das Event mit dem angegebenen Index in der Queue der gespeicherten
Events zurueck. Das Event wird daraufhin aus der Queue geloescht
\param eventNumber Index des zurueckzuliefernden Events in der Queue
\param pValid true, falls ein Event aus der Queue zurueckgegeben wird,
false andernfalls (dann existiert kein Event mit dem angegebenen Index)
\return Falls eventNumber nicht INVALID_EVENT_NUMBER, das Event mit dem
angegebenen Index in der Queue, das chronologisch erste (aelteste) Event
andernfalls
*/
SEvent CEventQueue::popEvent(unsigned short eventNumber, bool* pValid)
{
if(eventNumber<m_events.size())
{
*pValid=true;
std::vector<SEvent>::iterator it;
SEvent ret;
it=(m_events.begin() + eventNumber);
ret=*it;
m_events.erase(it);
return ret;
}
else
{
*pValid=false;
SEvent e;
e.m_eventType=UNDEFINED;
e.m_additionalInfo=0;
return e;
}
}
/*! \fn void CEventQueue::pushEvent(SEvent event)
\brief Fuegt der Queue ein Event hinzu. Sensoren und Aktoren
rufen diese Methode auf, um ueber ein Event zu informieren
\param event Hinzuzufuegendes Event
*/
void CEventQueue::pushEvent(SEvent event)
{
m_events.push_back(event);
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef CEVENTQUEUE_H_
#define CEVENTQUEUE_H_
#include "SEvent.h"
#include "ArduinoSTL.h"
class CEventQueue
{
public:
std::vector<SEvent> popEvents(EVENT_TYPE eventtype=UNDEFINED);
SEvent popEvent(bool* pValid);
SEvent popEvent(unsigned short eventNumber, bool* pValid);
void pushEvent(SEvent event);
private:
std::vector<SEvent> m_events;
};
#endif /* CEVENTQUEUE_H_ */
+363
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);
}
+94
View File
@@ -0,0 +1,94 @@
#ifndef CSEILMAINCONTROLLER_H_
#define CSEILMAINCONTROLLER_H_
#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;
// ===== 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_ */
+24
View File
@@ -0,0 +1,24 @@
#include "SEvent.h"
const char* evToStr(SEvent ev)
{
switch(ev.m_eventType)
{
case BTN_ANLAGE_EIN_AUS: return "BTN_ANLAGE_EIN_AUS";
case BTN_STATION: return "BTN_STATION";
case BTN_SERVICE: return "BTN_SERVICE";
case BTN_FAHRGAST: return "BTN_FAHRGAST";
case BTN_QUIT: return "BTN_QUIT";
case BTN_START: return "BTN_START";
case BTN_HALT: return "BTN_HALT";
case BTN_NOTHALT: return "BTN_NOTHALT";
case BTN_VORWAERTS: return "BTN_VORWAERTS";
case BTN_RUECKWAERTS: return "BTN_RUECKWAERTS";
default: return "UNDEFINED";
}
}
void printEvent(SEvent event, void* who)
{
// TBD if needed
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef SEVENT_H_
#define SEVENT_H_
enum EVENT_TYPE
{
BTN_ANLAGE_EIN_AUS, // Button Anlage Ein/Aus wurde gedrückt
BTN_STATION, // Button Station Besetzt/Unbesetzt wurde gedrückt
BTN_SERVICE, // Button Servicebetrieb wurde gedrückt
BTN_FAHRGAST, // Button Fahrgastbetrieb wurde gedrückt
BTN_QUIT, // Button Quittieren wurde gedrückt
BTN_START, // Button Start wurde gedrückt
BTN_HALT, // Button Halt wurde gedrückt
BTN_NOTHALT, // Button Nothalt wurde gedrückt
BTN_VORWAERTS, // Button Vorwaerts wurde gedrückt
BTN_RUECKWAERTS, // Button Rueckwaerts wurde gedrückt
UNDEFINED=999,
};
struct SEvent
{
EVENT_TYPE m_eventType;
unsigned int m_additionalInfo;
};
const char* evToStr(SEvent ev);
void printEvent(SEvent event, void* who);
#endif /* SEVENT_H_ */
+7 -660
View File
@@ -1,668 +1,15 @@
#include <Arduino.h>
#include "CMainController.h"
//Pindefinitionen
#define LED_ANLAGE_EIN_AUS 2
#define KNOPF_ANLAGE_EIN_AUS 3
CMainController controller;
#define LED_STATION_BESETZT_UNBESETZT 4
#define KNOPF_STATION_BESETZT_UNBESETZT 5
#define LED_SERVICEBETRIEB 6
#define KNOPF_SERVICEBETRIEB 7
#define LED_FAHRGASTBETRIEB 22
#define KNOPF_FAHRGASTBETRIEB 23
#define LED_ROT_EREIGNISANZEIGE 13
#define LED_GELB_EREIGNISANZEIGE 12
#define LED_GRUEN_EREIGNISANZEIGE 11
#define LED_QUIT_BETRIEB 9
#define KNOPF_QUIT_BETRIEB 10
#define LED_START_BETRIEB 36
#define KNOPF_START_BETRIEB 37
#define LED_HALT 38
#define KNOPF_HALT 39
#define LED_NOTHALT 40
#define KNOPF_NOTHALT 41
#define LED_VORWAERTS 30
#define KNOPF_VORWAERTS 31
#define LED_RUECKWAERTS 32
#define KNOPF_RUECKWAERTS 33
// Hilfsvariablen zum merken des Zustands
bool buttonStateAnlageEinAus = false;
bool currentButtonStateAnlageEinAus = false;
bool buttonStateStationBesetztUnbesetzt = false;
bool currentButtonStateStationBesetztUnbesetzt = false;
bool buttonStateServicebetrieb = false;
bool currentButtonStateServicebetrieb = false;
bool buttonStateFahrgastbetrieb = false;
bool currentButtonStateFahrgastbetrieb = false;
bool buttonStateQuitBetrieb = false;
bool currentButtonStateQuitBetrieb = false;
bool buttonStateStart = false;
bool currentButtonStateStart = false;
bool buttonStateHalt = false;
bool currentButtonStateHalt = false;
bool buttonStateNothalt = false;
bool currentButtonStateNothalt = false;
bool buttonStateVorwaerts = false;
bool currentButtonStateVorwaerts = false;
bool buttonStateRueckwaerts = false;
bool currentButtonStateRueckwaerts = false;
// Anlagenzustände
enum
void setup()
{
AUS,
SERVICE_QUITTIEREN,
SERVICE,
FAHRGAST_QUITTIEREN,
FAHRGAST,
} anlagenzustand;
// Fahrtrichtungen
enum Fahrtrichtung
{
HALT, // Anlage steht
NOTHALT, // Anlage steht (Notstop)
VORWAERTS_QUITTIEREN, // Richtungsknopf vorwärts gedrückt, warte auf quittieren
WARTE_START_VORWAERTS, // Anlage steht und wartet auf starten (in die entsprechende Richtung)
VORWAERTS, // Anlage fährt vorwärts
RUECKWAERTS_QUITTIEREN, // Richtungsknopf rückwärts gedrückt, warte auf quittieren
WARTE_START_RUECKWAERTS, // Anlage steht und wartet auf starten (in die entsprechende Richtung)
RUECKWAERTS, // Anlage fährt rückwärts
} fahrtrichtung;
Fahrtrichtung vorherigeRichtung = HALT;
void quittierenButtonAbfragen()
{
// ========== Quittieren Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateQuitBetrieb = digitalRead(KNOPF_QUIT_BETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateQuitBetrieb && !buttonStateQuitBetrieb)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateQuitBetrieb = digitalRead(KNOPF_QUIT_BETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateQuitBetrieb == LOW)
{
// Knopf gedrückt => Code ausführen
// ============================================================
switch(fahrtrichtung)
{
case HALT:
case NOTHALT:
if(vorherigeRichtung == RUECKWAERTS)
{
fahrtrichtung = WARTE_START_RUECKWAERTS;
}
else
{
fahrtrichtung = WARTE_START_VORWAERTS;
}
break;
case VORWAERTS_QUITTIEREN:
fahrtrichtung = WARTE_START_VORWAERTS;
break;
case RUECKWAERTS_QUITTIEREN:
fahrtrichtung = WARTE_START_RUECKWAERTS;
break;
}
// ============================================================
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateQuitBetrieb = currentButtonStateQuitBetrieb;
}
}
Serial.begin(9600);
controller.setup();
}
void startButtonAbfragen()
void loop()
{
// ========== Start Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateStart = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateStart && !buttonStateStart)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateStart = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateStart == LOW)
{
// Knopf gedrückt => Code ausführen
// ============================================================
switch(fahrtrichtung)
{
case WARTE_START_VORWAERTS:
fahrtrichtung = VORWAERTS;
vorherigeRichtung = VORWAERTS;
break;
case WARTE_START_RUECKWAERTS:
fahrtrichtung = RUECKWAERTS;
vorherigeRichtung = RUECKWAERTS;
break;
}
// ============================================================
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateStart = currentButtonStateStart;
}
}
}
// FERTIG
void haltButtonAbfragen()
{
// ========== Start Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateHalt = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateHalt && !buttonStateHalt)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateHalt = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateHalt == LOW)
{
// Knopf gedrückt => Code ausführen
// ============================================================
fahrtrichtung = HALT;
// ============================================================
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateHalt = currentButtonStateHalt;
}
}
}
//FERTIG
void nothaltButtonAbfragen()
{
// ========== Start Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateNothalt = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateNothalt && !buttonStateNothalt)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateNothalt = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateNothalt == LOW)
{
// Knopf gedrückt => Code ausführen
// ============================================================
fahrtrichtung = NOTHALT;
// ============================================================
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateNothalt = currentButtonStateNothalt;
}
}
}
//FERTIG
void vorwaertsButtonAbfragen()
{
// ========== Start Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateVorwaerts = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateVorwaerts && !buttonStateVorwaerts)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateVorwaerts = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateVorwaerts == LOW)
{
// Knopf gedrückt => Code ausführen
// ============================================================
// In jeder beliebigen Fahrtrichtung, Fahrtrichtung Ändern (HALT, NOTHALT, RUECKWAERTS_QUITTIEREN, )
fahrtrichtung = VORWAERTS_QUITTIEREN;
// ============================================================
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateVorwaerts = currentButtonStateVorwaerts;
}
}
}
//FERTIG
void rueckwaertsButtonAbfragen()
{
// ========== Start Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateRueckwaerts = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateRueckwaerts && !buttonStateRueckwaerts)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateRueckwaerts = digitalRead(KNOPF_START_BETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateRueckwaerts == LOW)
{
// Knopf gedrückt => Code ausführen
// ============================================================
fahrtrichtung = RUECKWAERTS_QUITTIEREN;
// ============================================================
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateRueckwaerts = currentButtonStateRueckwaerts;
}
}
}
void setup() {
anlagenzustand = AUS;
fahrtrichtung = HALT;
Serial.begin(9600);
// Pindefinitonen
pinMode(LED_ANLAGE_EIN_AUS, OUTPUT);
pinMode(KNOPF_ANLAGE_EIN_AUS, INPUT_PULLUP);
pinMode(LED_STATION_BESETZT_UNBESETZT, OUTPUT);
pinMode(KNOPF_STATION_BESETZT_UNBESETZT, INPUT_PULLUP);
pinMode(LED_SERVICEBETRIEB, OUTPUT);
pinMode(KNOPF_SERVICEBETRIEB, INPUT_PULLUP);
pinMode(LED_FAHRGASTBETRIEB, OUTPUT);
pinMode(KNOPF_FAHRGASTBETRIEB, INPUT_PULLUP);
pinMode(LED_QUIT_BETRIEB, OUTPUT);
pinMode(KNOPF_QUIT_BETRIEB, INPUT_PULLUP);
pinMode(LED_ROT_EREIGNISANZEIGE, OUTPUT);
pinMode(LED_GELB_EREIGNISANZEIGE, OUTPUT);
pinMode(LED_GRUEN_EREIGNISANZEIGE, OUTPUT);
}
void loop() {
switch(anlagenzustand)
{
// #################### Anlage ausgeschaltet
case AUS:
{
// ========== LEDs entsprechend des Zustands setzen
digitalWrite(LED_ANLAGE_EIN_AUS, LOW);
digitalWrite(LED_STATION_BESETZT_UNBESETZT, LOW);
digitalWrite(LED_SERVICEBETRIEB, LOW);
digitalWrite(LED_FAHRGASTBETRIEB, LOW);
digitalWrite(LED_ROT_EREIGNISANZEIGE, LOW);
digitalWrite(LED_GELB_EREIGNISANZEIGE, LOW);
digitalWrite(LED_GRUEN_EREIGNISANZEIGE, LOW);
digitalWrite(LED_QUIT_BETRIEB, LOW);
// ========== Ein/Aus Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateAnlageEinAus && !buttonStateAnlageEinAus)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateAnlageEinAus == LOW)
{
// Anlage in den Zustand "SERVICE" versetzen und damit starten
anlagenzustand = SERVICE;
digitalWrite(LED_ANLAGE_EIN_AUS, HIGH);
digitalWrite(LED_STATION_BESETZT_UNBESETZT, HIGH);
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateAnlageEinAus = currentButtonStateAnlageEinAus;
}
}
break;
}
// #################### Anlage im Service-quittieren Zustand
// Anlage wechselt in den Servicezustand, sobald der Zustandswechsel durch den Nutzer quittiert wird
case SERVICE_QUITTIEREN:
{
// ========== LEDs entsprechend des Zustands setzen
digitalWrite(LED_QUIT_BETRIEB, HIGH);
digitalWrite(LED_GELB_EREIGNISANZEIGE, HIGH);
digitalWrite(LED_GRUEN_EREIGNISANZEIGE, LOW);
// ========== Ein/Aus Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateAnlageEinAus && !buttonStateAnlageEinAus)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateAnlageEinAus == LOW)
{
// Anlage in den Zustand "AUS" versetzen und damit stoppen
anlagenzustand = AUS;
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateAnlageEinAus = currentButtonStateAnlageEinAus;
}
}
// ========== Quittieren Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateQuitBetrieb = digitalRead(KNOPF_QUIT_BETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateQuitBetrieb && !buttonStateQuitBetrieb)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateQuitBetrieb = digitalRead(KNOPF_QUIT_BETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateQuitBetrieb == LOW)
{
// Anlage in den Zustand "SERVICE" versetzen
anlagenzustand = SERVICE;
digitalWrite(LED_QUIT_BETRIEB, LOW);
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateQuitBetrieb = currentButtonStateQuitBetrieb;
}
}
break;
}
// #################### Anlage im Servicezustand
case SERVICE:
{
// ========== LEDs entsprechend des Zustands setzen
digitalWrite(LED_SERVICEBETRIEB, HIGH);
digitalWrite(LED_FAHRGASTBETRIEB, LOW);
digitalWrite(LED_ROT_EREIGNISANZEIGE, HIGH);
digitalWrite(LED_GELB_EREIGNISANZEIGE, LOW);
digitalWrite(LED_GRUEN_EREIGNISANZEIGE, LOW);
// ========== Fahrtrichtungsabhängige Steuerung
switch(fahrtrichtung)
{
// Anlage steht
case HALT:
{
quittierenButtonAbfragen(); // => "WARTE_START" Anhand Richtung
vorwaertsButtonAbfragen(); // => "VORWAERTS_QUITTIEREN"
rueckwaertsButtonAbfragen(); // => "RUECKWAERTS_QUITTIEREN"
break;
}
// Anlage steht
case NOTHALT:
{
quittierenButtonAbfragen(); // => "WARTE_START"
vorwaertsButtonAbfragen(); // => "VORWAERTS_QUITTIEREN"
rueckwaertsButtonAbfragen(); // => "RUECKWAERTS_QUITTIEREN"
break;
}
// Anlage steht
// Anlage fährt vorwärts
case VORWAERTS_QUITTIEREN:
{
quittierenButtonAbfragen(); // => "WARTE_START_VORWAERTS"
haltButtonAbfragen(); // => "HALT"
nothaltButtonAbfragen(); // => "NOTHALT"
rueckwaertsButtonAbfragen(); // => "RUECKWAERTS_QUITTIEREN"
break;
}
case WARTE_START_VORWAERTS:
{
startButtonAbfragen(); // => "VORWAERTS"
break;
}
// Anlage fährt vorwärts
case VORWAERTS:
{
haltButtonAbfragen(); // => "HALT"
nothaltButtonAbfragen(); // => "NOTHALT"
break;
}
// Anlage fährt rückwärts
case RUECKWAERTS_QUITTIEREN:
{
quittierenButtonAbfragen(); // => "WARTE_START_RUECKWAERTS"
haltButtonAbfragen(); // => "HALT"
nothaltButtonAbfragen(); // => "NOTHALT"
vorwaertsButtonAbfragen(); // => "VORWAERTS_QUITTIEREN"
break;
}
case WARTE_START_RUECKWAERTS:
{
startButtonAbfragen(); // => "RUECKWAERTS"
break;
}
// Anlage fährt rückwärts
case RUECKWAERTS:
{
haltButtonAbfragen(); // => "HALT"
nothaltButtonAbfragen(); // => "NOTHALT"
break;
}
default:
{
break;
}
}
// ========== Ein/Aus Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateAnlageEinAus && !buttonStateAnlageEinAus)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateAnlageEinAus == LOW)
{
// Anlage in den Zustand "AUS" versetzen und damit stoppen
anlagenzustand = AUS;
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateAnlageEinAus = currentButtonStateAnlageEinAus;
}
}
// ========== Fahrgast Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateFahrgastbetrieb = digitalRead(KNOPF_FAHRGASTBETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateFahrgastbetrieb && !buttonStateFahrgastbetrieb)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateFahrgastbetrieb = digitalRead(KNOPF_FAHRGASTBETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateFahrgastbetrieb == LOW)
{
// Anlage in den Zustand "SERVICE_QUITTIEREN" versetzen
anlagenzustand = SERVICE_QUITTIEREN;
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateFahrgastbetrieb = currentButtonStateFahrgastbetrieb;
}
}
break;
}
// #################### Anlage im Fahrgast-quittieren Zustand
// Anlage wechselt in den Fahrgastzustand, sobald der Zustandswechsel durch den Nutzer quittiert wird
case FAHRGAST_QUITTIEREN:
{
// ========== LEDs entsprechend des Zustands setzen
digitalWrite(LED_QUIT_BETRIEB, HIGH);
digitalWrite(LED_GELB_EREIGNISANZEIGE, HIGH);
digitalWrite(LED_ROT_EREIGNISANZEIGE, LOW);
// ========== Ein/Aus Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateAnlageEinAus && !buttonStateAnlageEinAus)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateAnlageEinAus == LOW)
{
// Anlage in den Zustand "AUS" versetzen und damit stoppen
anlagenzustand = AUS;
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateAnlageEinAus = currentButtonStateAnlageEinAus;
}
}
// ========== Quittieren Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateQuitBetrieb = digitalRead(KNOPF_QUIT_BETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateQuitBetrieb && !buttonStateQuitBetrieb)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateQuitBetrieb = digitalRead(KNOPF_QUIT_BETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateQuitBetrieb == LOW)
{
// Anlage in den Zustand "SERVICE" versetzen
anlagenzustand = SERVICE;
digitalWrite(LED_QUIT_BETRIEB, LOW);
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateQuitBetrieb = currentButtonStateQuitBetrieb;
}
}
break;
}
// #################### Anlage im Fahrgastzustand
case FAHRGAST:
{
// ========== LEDs entsprechend des Zustands setzen
digitalWrite(LED_SERVICEBETRIEB, LOW);
digitalWrite(LED_FAHRGASTBETRIEB, HIGH);
digitalWrite(LED_GRUEN_EREIGNISANZEIGE, HIGH);
digitalWrite(LED_GELB_EREIGNISANZEIGE, LOW);
// ========== Fahrtrichtungsabhängige Steuerung
// switch(fahrtrichtung)
// {
// // Anlage steht
// case HALT:
// {
// break;
// }
// // Anlage fährt vorwärts
// case VORWAERTS:
// {
// break;
// }
// // Anlage fährt rückwärts
// case RUECKWAERTS:
// {
// break;
// }
// default:
// {
// break;
// }
// }
// ========== Vorwärts Button abfragen
vorwaertsButtonAbfragen();
// ========== Ein/Aus Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateAnlageEinAus && !buttonStateAnlageEinAus)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateAnlageEinAus = digitalRead(KNOPF_ANLAGE_EIN_AUS) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateAnlageEinAus == LOW)
{
// Anlage in den Zustand "AUS" versetzen und damit stoppen
anlagenzustand = AUS;
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateAnlageEinAus = currentButtonStateAnlageEinAus;
}
}
// ========== Service Button abfragen
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateServicebetrieb = digitalRead(KNOPF_SERVICEBETRIEB) == LOW;
// Aktuellen Zustand mit vorherigem vergleichen
if (currentButtonStateServicebetrieb && !buttonStateServicebetrieb)
{
// Warten zum entprellen
delay(20);
// Taster abfragen und aktuellen Zustand zwischenspeichern
currentButtonStateServicebetrieb = digitalRead(KNOPF_SERVICEBETRIEB) == LOW;
// Prüfen ob immernoch gedrückt
if (currentButtonStateServicebetrieb == LOW)
{
// Anlage in den Zustand "SERVICE_QUITTIEREN" versetzen
anlagenzustand = SERVICE_QUITTIEREN;
// Speichern des ButtonStates als Vergleichswert für den nächsten Durchlauf
buttonStateServicebetrieb = currentButtonStateServicebetrieb;
}
}
break;
}
// ####################
default:
{
break;
}
}
controller.work();
}