Kurze Verständnisfrage zu Variablen

michael_x:
Wenn die Treppen komplett unabhängig voneinander sind und du jurs' EVA magst, kannst du auch beliebig mehrere davon verwenden.

OK, die Idee greife ich mal auf. Wenn man es richtig macht, ist es ja praktisch kein zusätzlicher Aufwand, ein Programm "für beliebig viele Treppenbeleuchtungen" zu schreiben im Vergleich zu einem Programm "für zwei Treppenbeleuchtungen".

Ich packe dazu alle relevanten Variablen einer Treppe in ein "struct", und dann können beliebig viele Treppen geschaltet werden, solange die Treppen mit 2 Schaltern geschaltet werden und entweder eine dreiteilige mit Zeitversatz geschaltete oder eine einteilige Beleuchtung haben. Die Datenstruktur für eine Treppe sieht dabei so aus:

struct dreiSegmentTreppe_t {
  byte schalterUntenPin; // Pin des Schalters unten an der Treppe
  byte schalterObenPin;  // Pin des Schalters oben an der Treppe
  byte schalterState;    // letzter Schaltzustand (in den beiden untersten Bits codiert
  byte geschaltet;    // NICHTGESCHALTET,UNTENGESCHALTET,OBENGESCHALTET}
  unsigned long startZeit;  // enthält die Zeit, wann das Licht eingeschaltet wurde
  byte lichtA_Pin;   // Pin für Lampe-A
  byte lichtB_Pin;   // Pin für Lampe-B (falls nur eine Lampe, identisch mit lichtA_Pin)
  byte lichtC_Pin;   // Pin für Lampe-C (falls nur eine Lampe, identisch mit lichtA_Pin)
};

Die relevanten Definitionen stehen hier für jede Treppe im Code als treppen-Array gespeichert:

// Ein Array mit 2 Elementen definieren und mit Werten belegen, entsprechend 2 Treppen
dreiSegmentTreppe_t treppen[]={
  {2,3,0b00,NICHTGESCHALTET,0,8,9,10},
  {4,5,0b00,NICHTGESCHALTET,0,11,11,11},
};

Die beiden ersten Zahlen in einer Zeile stehen für den Pin des unteren und oberen Lichtschalters.
Die letzten drei Zahlen in einer Zeile stehen für die Pins der angeschlossenen drei Lichtsegmente.
Wenn eine Treppe nur ein einziges Lichtsegment hat, einfach dreimal denselben Pin definieren.

Um den Code ohne angeschlossene Beleuchtung testen zu können, gibt es Debug-Ausgaben auf Serial.

Die Schalter müssen entweder per Push-Pull angesteuert werden oder es muß ein Taster mit PullDown-Widerstand angeschlossen sein. Für invertierte Logik mit Schaltern, die die internen PullUp-Widerstände des Atmegas nutzen, müßte der Code geringfügig geändert werden.

Die Anzahl der ansteuerbaren Treppen ist nur durch die Zahl der freien I/O-Pins begrenzt. Im Sketch selbst wird die Programmlogik jeweils in einer Schleife abgearbeitet, mit der Treppennummer als Schleifenindex.

  for(int i=0;i<anzahlTreppen;i++)
  {
  // Hier der Code pro Treppe
  }

Der vollständige Sketch:

#define TIMEOUT 10000L   // Nach 60000 ms = 60 s Licht abschalten
#define TIMEDELAY 1000L  // 1000 ms = 1 s Zeitverzögerung zwischen den Segmenten
// Eine Treppe als "struct" mit verschiedenen Elementen definieren
enum {NICHTGESCHALTET,UNTENGESCHALTET,OBENGESCHALTET};

struct dreiSegmentTreppe_t {
  byte schalterUntenPin; // Pin des Schalters unten an der Treppe
  byte schalterObenPin;  // Pin des Schalters oben an der Treppe
  byte schalterState;    // letzter Schaltzustand (in den beiden untersten Bits codiert
  byte geschaltet;    // NICHTGESCHALTET,UNTENGESCHALTET,OBENGESCHALTET}
  unsigned long startZeit;  // enthält die Zeit, wann das Licht eingeschaltet wurde
  byte lichtA_Pin;   // Pin für Lampe-A
  byte lichtB_Pin;   // Pin für Lampe-B (falls nur eine Lampe, identisch mit lichtA_Pin)
  byte lichtC_Pin;   // Pin für Lampe-C (falls nur eine Lampe, identisch mit lichtA_Pin)
};

// Ein Array mit 2 Elementen definieren und mit Werten belegen, entsprechend 2 Treppen
dreiSegmentTreppe_t treppen[]={
  {2,3,0b00,NICHTGESCHALTET,0,8,9,10},
  {4,5,0b00,NICHTGESCHALTET,0,11,11,11},
};

int anzahlTreppen=sizeof(treppen)/sizeof(treppen[0]);

void eingabe()
{
  byte schalterState;
  for(int i=0;i<anzahlTreppen;i++)
  {
    if(treppen[i].geschaltet!=NICHTGESCHALTET) continue; // Schleifenindex abbrechen, falls bereits geschaltet
    schalterState=0b00;
    bitWrite(schalterState,0,digitalRead(treppen[i].schalterUntenPin));
    bitWrite(schalterState,1,digitalRead(treppen[i].schalterObenPin));
    if (schalterState!=0b00 && treppen[i].schalterState==0b00)
    {
      if(bitRead(schalterState,0)) treppen[i].geschaltet=UNTENGESCHALTET;
      else if(bitRead(schalterState,1)) treppen[i].geschaltet=OBENGESCHALTET;
      treppen[i].startZeit=millis();
    }
    treppen[i].schalterState=schalterState;
  }
}


void ausgabeSegment(char treppe, char segment, boolean newState, byte pin)
{
  if(digitalRead(pin)==newState) return; // keine Umschaltung notwendig, es bleibt wie es war
  digitalWrite(pin,newState); // Lampe 
  float f=millis()/1000.0; // Aktuelle Zeit in Gleitkommazahl wandeln
  Serial.print(treppe+1);Serial.print(segment);
  if(newState) Serial.print(" EIN  Pin-"); else Serial.print(" AUS  Pin- ");
  Serial.print(pin);Serial.print("  ");
  Serial.print(f,3);
  Serial.println("s");
}

void ausgabe()
{
  boolean lichtA, lichtB, lichtC;  
  unsigned long now=millis(); // Millisekundentimer aktuell
  for(int i=0;i<anzahlTreppen;i++)
  {
    // Erste Annahme: Das Treppenlicht sei nicht geschaltet und alle Lampen an dieser Treppe aus
    lichtA=false;lichtB=false;lichtC=false;
    // Zuerst prüfen, ob Abschaltzeit erreicht ist und ggf. abschalten
    if (treppen[i].geschaltet && now-treppen[i].startZeit>TIMEOUT+2*TIMEDELAY) treppen[i].geschaltet=false;
    if (treppen[i].geschaltet==UNTENGESCHALTET) // Es ist doch Licht geschaltet
    {
      if(now-treppen[i].startZeit<TIMEOUT) lichtA=true;
      if(now-treppen[i].startZeit>TIMEDELAY && now-treppen[i].startZeit<TIMEOUT+TIMEDELAY) lichtB=true;
      if(now-treppen[i].startZeit>2*TIMEDELAY) lichtC=true;
    }
    else if(treppen[i].geschaltet==OBENGESCHALTET) // Es ist doch Licht geschaltet
    {
      if(now-treppen[i].startZeit<TIMEOUT) lichtC=true;
      if(now-treppen[i].startZeit>TIMEDELAY && now-treppen[i].startZeit<TIMEOUT+TIMEDELAY) lichtB=true;
      if(now-treppen[i].startZeit>2*TIMEDELAY) lichtA=true;
      if(treppen[i].lichtA_Pin==treppen[i].lichtB_Pin) lichtA=lichtC;
    }
    // Jetzt unterschiedliche Logik für Treppen mit 1 oder 3 Lampen
    ausgabeSegment(i,'A', lichtA, treppen[i].lichtA_Pin);
    if(treppen[i].lichtA_Pin!=treppen[i].lichtB_Pin)
    {
      ausgabeSegment(i,'B', lichtB, treppen[i].lichtB_Pin);
      ausgabeSegment(i,'C', lichtC, treppen[i].lichtC_Pin);
    }  
  }
}


void setup() {
  Serial.begin(9600);
  for(int i=0;i<anzahlTreppen;i++)
  {
    pinMode(treppen[i].schalterUntenPin,INPUT); // unterer Schalter ist INPUT
    pinMode(treppen[i].schalterObenPin,INPUT);  // oberer Schalter ist INPUT
    pinMode(treppen[i].lichtA_Pin,OUTPUT);      // Lampen als OUTPUT
    pinMode(treppen[i].lichtB_Pin,OUTPUT);
    pinMode(treppen[i].lichtC_Pin,OUTPUT);
  }
}

void loop() {
  eingabe();
  ausgabe();  
}