BennoK:
ich versuche mich an folgendem Projekt
...
Soweit kein Problem.
Gratulation zur tollen Projektbeschreibung!
Endlich mal jemand, der genau verrät, was er vorhat und programmieren möchte, ohne einfach nur ein paar Zeilen verwurstelten Code zu zeigen, der irgendwie entwurstelt werden soll, ohne dass die Forenteilnehmer wissen, worum es eigentlich geht.
BennoK:
Nun meine Frage, ist es möglich eine Art "Momentaufnahme" der Auslesevariable für die Sekunden zu erstellen um diese dann zum Berechnen zu benutzen und dann wieder mit der aktuellen Zeit zu vergleichen?
Momentaufnahme ist gut. Dein Problem bietet sich geradezu für eine schulmäßige Abarbeitung an.
Damit meine ich, das Programm in drei Funktionsbereiche zu trennen, und zwar nach dem EVA-Prinzip:
- Eingabe (Zustand der PIR-Sensoren auslesen und bei Änderungen einige Variablen anders setzen)
- Verarbeitung (den derzeitigen logischen Schaltzustand der Lampen ermitteln)
- Ausgabe (den ermittelten logischen Schaltzustand tatsächlich an den Lampen physikalisch schalten)
Codevorschlag für die loop-Funktion, wie man ein typisches Programm immer aufbauen kann:
void loop() {
eingabe();
verarbeitung();
ausgabe();
}
D.h. verschiedene Programmbereiche haben verschiedene Aufgaben, die von verschiedenen Programmteilen bzw. Funktionen verarbeitet werden.
Das geht in Deinem kurzen Codeabschnitt schon mal wild durcheinander: Von Eingabe (digitalRead) über die Verarbeitung bis zur Ausgabe (digitalWrite) ist bei Dir alles in einer einzigen if-Abfrage untergebracht. Außer bei ganz trivialen Programmen geht das meist ziemlich in die Hose und führt zu extrem unübersichtlichem Programmcode, der manchmal gar nicht funktioniert und bei dem die eigentliche Programmlogik am Ende undurchschaubar und fehleranfällig wird.
Der Grund ist die fehlende Trennung von "logischem Schaltzustand" und "physikalischem Schaltzustand". Wenn Du im Programm nämlich die Programmlogik von der Schaltlogik trennst, dann kannst Du einen Lichtbalken in Gedanken während der Verarbeitung x-mal an und ausschalten und zig Bedingungen berücksichtigen, die dafür zuständig sind. Und erst am Ende, wenn alle Bedingungen verarbeitet sind, wird die Ausgabefunktion aufgerufen, die nach dem vollständigen Verarbeitungsschritt den letztendlich gültigen logischen Schaltzustand an der Lampe tatsächlich schaltet. Das braucht nicht lange zu dauern, sondern kann zigtausendmal pro Sekunde durchlaufen.
Die Aufteilung des Programms nach dem EVA-Prinzip wäre das eine.
Das andere Ding ist ein übersichtlicher Code durch symbolische Konstanten. Denn Du hast diverse Dinge, die mit kleinen Zahlenkonstanten im Programm vorkommen:
- Du hast zwei Treppen
- Du hast vier PIR-Sensoren
- Du hast vier Lichtbalken
Wenn damit programmiert wird, entsteht eine Übersichtlichkeit durch symbolische Konstaten und diese scheinst Du bereits teilweise zu verwenden, wie ich an dem "BALKEN_4" im Code bereits sehe. Das müßte im Code konsequent durchgezogen werden, und zwar nicht nur für die Relais-Pins. Du verwendest "BALKEN_4" offenbar für den Relais-Pin, mit dem der vierte Lichtbalken physikalisch geschaltet wird.
Tatsächlich benötigt werden aber auch symbolische Konstanten, um nicht nur die physikalischen Pins damit zu benennen, sondern auch die logischen Schaltzustände, die während der Verarbeitung ermittelt/gesetzt werden. Geeignete Datenstrukturen für sein Programm zu verwenden ist genau so wichtig wie die Programmlogik. Wenn die grundlegenden Datenstrukturen Mist sind, kann die Programmlogik auch kein schönes Programm mehr daraus machen.
Ich würde mit symbolischen Konstanten und Arrrays arbeiten, also z.B. für die Balken:
enum {BALKEN_1A, BALKEN_1B, BALKEN_1C, BALKEN2};
#define NUMBALKEN 4
int outputPins[NUMBALKEN]={6,7,8,9};
boolean outputStatus[NUMBALKEN]={LOW,LOW,LOW,LOW};
Damit hast Du mit dem outputStatus-Array unabhängige Variablen für einen logischen Schaltzustand, den Du beliebig ändern kannst:
outputStatus[BALKEN2]=LOW;
if (dieses) outputStatus[BALKEN2]=HIGH;
if (jenes) outputStatus[BALKEN2]=LOW;
if (diesUNDdas) outputStatus[BALKEN2]=HIGH;
Und erst wenn alle möglichen Eventualitäten in der Schaltlogik berücksichtigt sind und der logische Schaltzustand feststeht, nachdem er sich möglicherweise mehrmals geändert hat, wenn verschiedene logische Bedingungen berücksichtigt haben, wird am Ende in der Ausgabefunktion an allen Ausgangspins der jeweils gültige logische Schaltzustand tatsächlich geschaltet:
void ausgabe()
{
for (int i=0;i<NUMBALKEN;i++)
digitalWrite(outputPins[i],outputStatus[i]);
}
Falls das jetzt etwas viel Stoff war und nicht ganz verständlich, hier nochmal die Kurzfassung:
- Trenne das Programm in logische Funktionseinheiten "Eingabe", "Verarbeitung", "Ausgabe"
- Trenne für die Verarbeitung die logischen Schaltzustände von den physikalischen Schaltzuständen
Soll ich Dir mal ein Programmgrundgerüst zusammenbauen?