Anleitung: Weichensteuerung mit Klasse

Motivation: Angeregt durch das Thema Weichensteuerung Schritt für Schritt möchte ich die dort von mir vorgestellte Anregung an dieser Stelle näher erläutern. Mit einer Suche im Forum nach “agmue anleitung” lassen sich meine Anleitungen vom Anfänger für den Anfänger wiederfinden.

Aufgabe: Die Weiche einer Modellbahn kann zwischen Gleis 1 und Gleis 2 wechseln. Ein auf Gleis 1 einfahrender Zug löst einen Reedkontakt aus, der Gleis 1 stromlos macht, gleichzeitig Gleis 2 mit Strom versorgt und die Weiche auf Gleis 2 umlegt.

Inhalt: Schrittkette und Zeitsteuerung in einer Klasse.

Als Anfänger schreibt man zuerst eine Lösung für eine Weiche, kopiert den Text mit etwas anderen Variablennamen. Bei vielen Weichen führt das schnell zu einem unübersichtlichen Bandwurmprogramm.

Abhilfe schaffen Variablenfelder und eine Funktion, die man mit dem Feldindex aufruft. Allerdings stehen zu einer Weiche gehörende Werte in unterschiedlichen Feldern.

Um zu einer Weiche gehörende Variablen verschiedenen Typs zusammenfassen zu können, kann man eine Struktur oder Klasse verwenden. Diese integriert dann auch noch die Funktion als Methode der Struktur oder Klasse.

Struktur und Klasse unterscheiden sich nur dadurch, daß eine Struktur am Anfang öffentlich (public) ist, während eine Klasse privat (private) ist. Ich verwende eine Klasse.

Der private Teil ist nur innerhalb einer Klasse zugänglich, der öffentliche auch von außerhalb. Meist sind nur Methoden öffentlich.

Erläuterungen zum Programm:
Um die ungeliebte Nutzung der millis() zu umgehen, nutzen wir die “Eieruhr” dieser Bibliothek.

#include <MobaTools.h>

Die Klasse fängt mit den für alle Methoden gültigen Objekten an, also Konstanten, Variablen und Instanzen anderer Bibliotheken. Diesen Objekten können Anfangswerte zugewiesen werden, wenn dies nicht im Konstruktor geschieht.

class Gleis {
  private:  // Die folgenden Objekte sind nur innerhalb der Klasse zugänglich. Am Anfang einer Klasse überflüssig.
    enum {WARTEN, IMPULSAUS};  // Benennung und Numerierung der Schritte der Schrittkette
    const byte reedPinA, reedPinG, stromRpin, weicheApin, weicheGpin;  // A = abbiegend; G = gerade
    byte schritt = WARTEN;
    MoToTimer weichenImpuls;
    const uint32_t weichenImpulsLaenge = 300; // in Millisekunden

Der Konstruktor steht im öffentlichen Teil und erzeugt Objekte. Innerhalb der runden Klammern müssen Reihenfolge und Typ genau der späteren Instanzierung entsprechen. Nach dem Doppelpunkt werden den vor dem Konstruktor festgelegten Objekten Werte zugewiesen, wobei nun die aus der Instanz kommenden Werte verwendet werden können. Dabei entspricht reedPinA(_reedPinA) der Zuweisung reedPinA=_reedPinA, wobei der grüne Teil dem Wert aus der Instanz entspricht und der blaue die interne Konstante oder Variable. Die geschweiften Klammern werden hier nicht genutzt und bleiben daher leer.

  public:
    Gleis(const byte _reedPinA, const byte _reedPinG, const byte _stromRpin, const byte _weicheApin, const byte _weicheGpin)
      : reedPinA(_reedPinA), reedPinG(_reedPinG), stromRpin(_stromRpin), weicheApin(_weicheApin), weicheGpin(_weicheGpin)
    {}

Jetzt springe ich zur Instanzierung. Der Feldvariablen gleis[] wird der Typ der vorher festgelegten Klasse Gleis zugewiesen. Ohne ausführliche Kommentare sieht das so aus:

Gleis gleis[] {
  //reedPinA, reedPinG, stromRpin, weicheApin, weicheGpin
  {       12,       11,         8,          7,          6 }, // 1. Weiche
  {       10,        9,         5,          4,          3 }  // 2. Weiche
};

Das Feld gleis[0] enthält im ersten Feldelement die Pins für die erste Weiche, gleis[1] enthält die Pins für die zweite Weiche. Beide Feldelemente werden als Instanzen der Klasse für die Methoden init() und stellen() in for-Schleifen aufgerufen. Die Instanzen arbeiten vollkommen unabhängig voneinander.

void setup() {
  for (Gleis &g : gleis) g.init();
}

void loop() {
  for (Gleis &g : gleis) g.stellen();
}

Eine Klasse kann auch Funktionen enthalten, die Methoden genannt werden.

Die Methode init() legt den Zustand der Pins fest. Bei LOW-aktiven Relais wird das Ausgangs-Bit erst auf HIGH gesetzt, bevor der Ausgang zum Ausgang wird. Damit wird ein kurzes Relaisschalten verhindert.

Die Methode stellen() liest die Reedkontakte und steuert die zwei Relais zur Weichenstellung an. Die Schrittkette (=endlicher Automat, =finite state machine) wartet auf ein Signal vom Reedkontakt, schaltet den Strom von einem Gleis zum anderen, steuert ein Gleisrelaus an, startet die “Eieruhr” und schaltet zum nächsten Schritt. Dieser schaltet nach Ablauf der “Eieruhr” die Weichenrelais ab.

Das Programm im Zusammenhang:

#include <MobaTools.h>  // Kann mittels Arduino-IDE installiert werden. 

class Gleis {
  private:  // Die folgenden Objekte sind nur innerhalb der Klasse zugänglich. Am Anfang einer Klasse überflüssig.
    enum {WARTEN, IMPULSAUS};  // Benennung und Numerierung der Schritte der Schrittkette
    const byte reedPinA, reedPinG, stromRpin, weicheApin, weicheGpin;  // A = abbiegend; G = gerade
    byte schritt = WARTEN;
    MoToTimer weichenImpuls;
    const uint32_t weichenImpulsLaenge = 300; // in Millisekunden
  public:
    Gleis(const byte _reedPinA, const byte _reedPinG, const byte _stromRpin, const byte _weicheApin, const byte _weicheGpin)
      : reedPinA(_reedPinA), reedPinG(_reedPinG), stromRpin(_stromRpin), weicheApin(_weicheApin), weicheGpin(_weicheGpin)
    {}
    void init() {
      pinMode(reedPinA, INPUT_PULLUP);
      pinMode(reedPinG, INPUT_PULLUP);
      digitalWrite(stromRpin, HIGH);
      pinMode(stromRpin, OUTPUT);
      digitalWrite(weicheApin, HIGH);
      pinMode(weicheApin, OUTPUT);
      digitalWrite(weicheGpin, HIGH);
      pinMode(weicheGpin, OUTPUT);
    }

    void stellen() {
      switch (schritt) {
        case WARTEN:
          if ( !digitalRead(reedPinA) ) {
            digitalWrite(stromRpin, HIGH);              // Gleis Abzweig stromlos, Gleis Gerade bestromt
            digitalWrite(weicheGpin, LOW);              // Weiche Impuls auf Gleis Gerade ein
            weichenImpuls.setTime(weichenImpulsLaenge); // "Eieruhr" aufziehen
            schritt = IMPULSAUS;
          }
          if ( !digitalRead(reedPinG) ) {
            digitalWrite(stromRpin, LOW);               // Gleis Gerade stromlos, Gleis Abzweig bestromt
            digitalWrite(weicheApin, LOW);              // Weiche Impuls auf Gleis Abzweig ein
            weichenImpuls.setTime(weichenImpulsLaenge); // "Eieruhr" aufziehen
            schritt = IMPULSAUS;
          }
          break;
        case IMPULSAUS:
          if ( weichenImpuls.expired() ) {
            digitalWrite(weicheGpin, HIGH);              // Weiche Impuls auf Gleis Gerade ein
            digitalWrite(weicheApin, HIGH);              // Weiche Impuls auf Gleis Abzweig ein
            schritt = WARTEN;
          }
          break;
      }
    }
};

Gleis gleis[] {
  //reedPinA, reedPinG, stromRpin, weicheApin, weicheGpin
  { // 1. Weiche
    12, //Zugerkennung Reedkontakt Gleis 1
    11, //Zugerkennung Reedkontakt Gleis 2
    8,  //Relais für Stromumschaltung Gleis 1 zu Gleis 2
    7,  //Relais für Weiche geradeaus
    6   //Relais für Weiche abbiegend, LEDgelb
  },
  { // 2. Weiche
    10, //Zugerkennung Reedkontakt Gleis 3
    9,  //Zugerkennung Reedkontakt Gleis 4
    5,  //Relais für Stromumschaltung Gleis 3 zu Gleis 4
    4,  //Relais für Weiche geradeaus
    3   //Relais für Weiche abbiegend
  }
};

void setup() {
  for (Gleis &g : gleis) g.init();
}

void loop() {
  for (Gleis &g : gleis) g.stellen();
}

Wenn ich mit diesem Thema einen Anfänger zur Nutzung einer Struktur oder Klasse überreden könnte, würde ich mich freuen.

6 Likes

Danke :+1: :+1: :+1:

Sehr schön :sunglasses: . Jetzt müssen die Einsteiger nur noch drauf einsteigen :wink: .

Nur eine kleine Anmerkung meinerseits: Das struct und class im Prinzip dassselbe sind, ist eine Besonderheit von C++ und aus der Geschichte der Sprachentwicklung heraus zu verstehen.
Ich würde im Sinne einer sauberen Strukturierung - so wie Du das in den Beispielen ja auch machst - struct immer nur für reine Datenstrukturen verwenden und für OOP-Objekte immer class nutzen.

nett zusammengefasst.
bin mir nicht sicher. gibts beim enum auch sowas wie static? weil das bräuchte es ja nicht in jeder Instanz, das reicht ja eigentlich einmal für die Klasse.
Und könnte man in dem Zusammenhang statt dem byte schritt = WARTEN, einen typsichere Variante aufs enum machen?
Tendenziell würde ich den Intervall weichenImpulseLaenge nicht 32bittig machen, aber das wird eh der Compiler wegoptimieren.
Vieleicht noch ein paar Worte zur Schrittkette verlieren: was die 2 Schritte sind, wie das mit dem enum und dem Switch case zusammenspielt

Ja, das geht:

#include <MobaTools.h>  // Kann mittels Arduino-IDE installiert werden. 

class Gleis {
  private:  // Die folgenden Objekte sind nur innerhalb der Klasse zugänglich. Am Anfang einer Klasse überflüssig.
    enum struct Schritte {WARTEN, IMPULSAUS};  // Benennung und Numerierung der Schritte der Schrittkette
    const byte reedPinA, reedPinG, stromRpin, weicheApin, weicheGpin;  // A = abbiegend; G = gerade
    Schritte schritt = Schritte::WARTEN;
    MoToTimer weichenImpuls;
    const uint32_t weichenImpulsLaenge = 300; // in Millisekunden
  public:
    Gleis(const byte _reedPinA, const byte _reedPinG, const byte _stromRpin, const byte _weicheApin, const byte _weicheGpin)
      : reedPinA(_reedPinA), reedPinG(_reedPinG), stromRpin(_stromRpin), weicheApin(_weicheApin), weicheGpin(_weicheGpin)
    {}
    void init() {
      pinMode(reedPinA, INPUT_PULLUP);
      pinMode(reedPinG, INPUT_PULLUP);
      digitalWrite(stromRpin, HIGH);
      pinMode(stromRpin, OUTPUT);
      digitalWrite(weicheApin, HIGH);
      pinMode(weicheApin, OUTPUT);
      digitalWrite(weicheGpin, HIGH);
      pinMode(weicheGpin, OUTPUT);
    }

    void stellen() {
      switch (schritt) {
        case Schritte::WARTEN:
          if ( !digitalRead(reedPinA) ) {
            digitalWrite(stromRpin, HIGH);              // Gleis Abzweig stromlos, Gleis Gerade bestromt
            digitalWrite(weicheGpin, LOW);              // Weiche Impuls auf Gleis Gerade ein
            weichenImpuls.setTime(weichenImpulsLaenge); // "Eieruhr" aufziehen
            schritt = Schritte::IMPULSAUS;
          }
          if ( !digitalRead(reedPinG) ) {
            digitalWrite(stromRpin, LOW);               // Gleis Gerade stromlos, Gleis Abzweig bestromt
            digitalWrite(weicheApin, LOW);              // Weiche Impuls auf Gleis Abzweig ein
            weichenImpuls.setTime(weichenImpulsLaenge); // "Eieruhr" aufziehen
            schritt = Schritte::IMPULSAUS;
          }
          break;
        case Schritte::IMPULSAUS:
          if ( weichenImpuls.expired() ) {
            digitalWrite(weicheGpin, HIGH);              // Weiche Impuls auf Gleis Gerade ein
            digitalWrite(weicheApin, HIGH);              // Weiche Impuls auf Gleis Abzweig ein
            schritt = Schritte::WARTEN;
          }
          break;
        default:
          schritt = Schritte::WARTEN;
      }
    }
};

Gleis gleis[] {
  //reedPinA, reedPinG, stromRpin, weicheApin, weicheGpin
  { // 1. Weiche
    12, //Zugerkennung Reedkontakt Gleis 1
    11, //Zugerkennung Reedkontakt Gleis 2
    8,  //Relais für Stromumschaltung Gleis 1 zu Gleis 2
    7,  //Relais für Weiche geradeaus
    6   //Relais für Weiche abbiegend, LEDgelb
  },
  { // 2. Weiche
    10, //Zugerkennung Reedkontakt Gleis 3
    9,  //Zugerkennung Reedkontakt Gleis 4
    5,  //Relais für Stromumschaltung Gleis 3 zu Gleis 4
    4,  //Relais für Weiche geradeaus
    3   //Relais für Weiche abbiegend
  }
};

void setup() {
  for (Gleis &g : gleis) g.init();
}

void loop() {
  for (Gleis &g : gleis) g.stellen();
}

Auch wenn ich selbst keine Modellbahn besitze, ist es doch eine interessante und gelungene Beschreibung. Da können sicher viele etwas daraus lernen.
Danke dafür.

weis nicht ob ich da ein enum class machen würd’. Bist eh im Scope von Gleis.

Darf ich was beisteuern:
Wie ein Youtuber sagen würde: “Die Abenteuer der Rückseite - nichts wovor man Angst haben müsste.”
oder
noiasca: “Keine Angst vor Millis()”

/*
    Anleitung: Weichensteuerung mit Klasse
    https://forum.arduino.cc/t/anleitung-weichensteuerung-mit-klasse/856934
    by agmue
    (mit MobaTools 1440/41)

    Variante: "keine Angst vor millis()"
    by noiasca
    1322/39
    
 */

class Gleis {
  private:  // Die folgenden Objekte sind nur innerhalb der Klasse zugänglich. Am Anfang einer Klasse überflüssig.
    enum Schritte {WARTEN, IMPULSAUS};  // Benennung und Numerierung der Schritte der Schrittkette
    const byte reedPinA, reedPinG, stromRpin, weicheApin, weicheGpin;  // A = abbiegend; G = gerade
    Schritte schritt = WARTEN;
    uint32_t previousMillis;                  // Zeitstempel der letzten Aktivierung - n
    const uint32_t weichenImpulsLaenge = 300; // in Millisekunden
    
  public:
    Gleis(const byte reedPinA, const byte reedPinG, const byte stromRpin, const byte weicheApin, const byte weicheGpin)
      : reedPinA(reedPinA), reedPinG(reedPinG), stromRpin(stromRpin), weicheApin(weicheApin), weicheGpin(weicheGpin)
    {}
    void init() {
      pinMode(reedPinA, INPUT_PULLUP);
      pinMode(reedPinG, INPUT_PULLUP);
      digitalWrite(stromRpin, HIGH);
      pinMode(stromRpin, OUTPUT);
      digitalWrite(weicheApin, HIGH);
      pinMode(weicheApin, OUTPUT);
      digitalWrite(weicheGpin, HIGH);
      pinMode(weicheGpin, OUTPUT);
    }

    void stellen() {
      switch (schritt) {
        case WARTEN:
          if ( digitalRead(reedPinA) == LOW ) {         // - n
            digitalWrite(stromRpin, HIGH);              // Gleis Abzweig stromlos, Gleis Gerade bestromt
            digitalWrite(weicheGpin, LOW);              // Weiche Impuls auf Gleis Gerade ein
            previousMillis = millis();                  // "Eieruhr" aufziehen  - n
            schritt = IMPULSAUS;
          }
          if ( digitalRead(reedPinG) == LOW ) {         // - n
            digitalWrite(stromRpin, LOW);               // Gleis Gerade stromlos, Gleis Abzweig bestromt
            digitalWrite(weicheApin, LOW);              // Weiche Impuls auf Gleis Abzweig ein
            previousMillis = millis();                  // "Eieruhr" aufziehen - n
            schritt = IMPULSAUS;
          }
          break;
        case IMPULSAUS:
          if ( millis() - previousMillis > weichenImpulsLaenge ) {    // - n
            digitalWrite(weicheGpin, HIGH);              // Weiche Impuls auf Gleis Gerade ein
            digitalWrite(weicheApin, HIGH);              // Weiche Impuls auf Gleis Abzweig ein
            schritt = WARTEN;
          }
          break;
        default:
          schritt = WARTEN;
      }
    }
};

Gleis gleis[] {
  //reedPinA, reedPinG, stromRpin, weicheApin, weicheGpin
  { // 1. Weiche
    12, //Zugerkennung Reedkontakt Gleis 1
    11, //Zugerkennung Reedkontakt Gleis 2
    8,  //Relais für Stromumschaltung Gleis 1 zu Gleis 2
    7,  //Relais für Weiche geradeaus
    6   //Relais für Weiche abbiegend, LEDgelb
  },
  { // 2. Weiche
    10, //Zugerkennung Reedkontakt Gleis 3
    9,  //Zugerkennung Reedkontakt Gleis 4
    5,  //Relais für Stromumschaltung Gleis 3 zu Gleis 4
    4,  //Relais für Weiche geradeaus
    3   //Relais für Weiche abbiegend
  }
};

void setup() {
  for (Gleis &g : gleis) g.init();
}

void loop() {
  for (Gleis &g : gleis) g.stellen();
}

Nicht dass ich was gegen die MobaTools hätte, aber bei so einem Beispiel schadet es doch nicht zu sehen, dass man dafür auch nur ein “BlinkWithoutDelay” braucht.
(ich hoffe es stimmt noch.
Persönliche Vorlieben: Die Underscores in der Initializierungsliste habe ich mir erlaubt zu entfernen, und das digitalRead … nein da mag ich lieber explizit den Vergleich auf das was das API zurückgibt, nämlich eine define LOW oder HIGH.)

Hallo,

ob enum struct oder enum class ist egal. Aus reiner Gewohnheit schreibe ich enum class.

Eine enum Deklaration kann man nicht static machen. Ein Byte hier und da macht das Kraut nicht fett. Dafür bleibt alles gekapselt. Man schafft sich keine neuen Probleme die man mit Klassen gerade gelöst.

Und was bringt es für einen Vorteil? Ihr schafft es schon noch, dass soweit zu bringen, dass das wieder kein Einsteiger versteht.

Oder ist das doch eher für Eingefleischte und Fortgeschrittene gedacht? Dann habe ich das Ganze halt nicht verstanden, OK kann auch sein.

Aber ich behaupte mal, einen Anfänger schreckt das nur ab. Für den ist das ist viel zu viel auf einmal.

1 Like

Das war nur die Antwort auf die Frage von noiasca. Mehr nicht.
Am Besten ist ich halte mich hier raus.

Den Unterschied zwischen enum und enum class merkst du evtl. wenn du mal zwei verschiedene Aufzählungen hast. Mit klassischen enums können zwei Aufzählungen nicht die gleichen Konstanten verwenden. z.B. Dinge wie WARTEN oder AUS können da zu Überschneidungen führen. Mit enum class ist dagegen sauber getrennt

In diesem Fall ist es tatsächlich egal

Kann ja auch sein,dass ich das hier falsch verstehe, und ich mich raushalten sollte :roll_eyes: :wink:

agmue
Dankedankedanke für die anfängerfreundliche Erklärung des Scripts, das sich explizit auf meine Anwendung bezieht. Ich werde genau an der Stelle weiter machen. Ich bin mir auch sicher, dass es hierfür viele Anwendungen und Interessierte gibt.

Ganz sicher ist es so, dass viele Wege nach Rom führen. Das ursprüngliche “Anfängerscript” von mir funktioniert jezt schließlich auch… aber es ist doch richtig gut, eine saubere Lösung mal Schritt für Schritt vorgelegt und verständlich zerlegt zu bekommen. DANKE!

In einem solchen allgemeinen Tutorial gibt es auch immer Vorschläge, was man anders machen kann. Ich als Anfänger finde das hier legitim und anregend, weil man etwas lernen kann. Danke. Es ist interessant. Deswegen braucht sich keiner zurückziehen, denke ich. Redet es nicht kaputt. Ich sehe alternative, frei wählbare Lösungen => das geht ebenfalls so und so => prima, wieder was gelernt => Punkt, oder, hier besser }

Etwas anderes mag es sein, wenn man im eigenen Fred durchaus lösungsorientiert und evtl bereits angefrustet nach irgendeinem verständlichen Ansatz für ein persönliches Problem sucht. Viele Ansätze verwirren dann eher und das erzeugt mehr Unverständnis, Gegendruck und Unsicherheit. Zu viel Input.
agmue hat mich da in die richtige Richtung geschubst, mein Script umgekrempelt und mich damit ins eisige Wasser geworfen. Super. Das triggert meine masochistische Ader :smiley: Es geht weiter… vor allem, da es bereits eine Lösung gibt. Rest ist reine Neugier.

Danke für die lobenden Worte!

Das sollte ziemlich eindeutig sein. Ich möchte Anregungen geben.

Dennoch verstehe ich Programmieren als eine kreative Tätigkeit, die bei verschiedenen Personen zu unterschiedlichen Ergebnissen führen sollte. Vielfalt ist der Schlüssel zum Erfolg!

Ich auch nicht, denn meine zwei Lokomotiven und ein paar Wagen aus Kindertagen hängen in der Vitrine an der Wand. Aus Geldmangel gab es Häuser aus Pappe, Büsche aus Plastikschwamm und Erde aus Kaffeesatz, aber das kreative Spielen mit meinem Vater ist unbezahlbar!

Ich gestehe, die MobaTools hier verwendet zu haben, weil ich etwas Werbung für sie machen möchte. Auf Grund der sehr vielen Fragen zu diesem Thema scheint es sich wohl um eine schwer zu überwindende Hürde zu handeln. Daher finde ich ein Hilfsmittel zur Überwindung dieser Hürde sinnvoll. Außerdem gibt es noch andere Dinge in dieser Bibliothek zu entdecken.

Dank Deines alternativen Vorschlags kann man den Unterschied gut erkennen und daran lernen.

Die habe ich da normalerweise auch nicht stehen, aber reedPinA=reedPinA läßt sich nur schwer erklären. Es sollte aber auch deutlich werden, daß previousMillis(-300), schritt(WARTEN) möglich ist.

Oder eventuell so:

#define LOW AKTIV
#define HIGH INAKTIV
...
if ( digitalRead(reedPinA) == AKTIV ) {
...

Als Themensteller: Nein und nein. Alle Beiträge beziehen sich auf das Thema und was für den Leser sinnvoll ist, soll der entscheiden, nicht ich.

Danke für Dein Lob!

Für zwei Weichen lohnt es nicht, eine Klasse zu lernen. Aber Dein Arduino kann mehr, viel mehr. Und Du willst es auch, daher ist ein richtiger Einstieg sinnvoll.

Bitte keine defines. Da kommste vom Regen in die Traufe. Entweder auch hier enum oder zwei constexpr Variablen. Ich würde Letzteres verwenden.

Da bin ich dabei. Zugegebenermaßen mach ich das aus alter Gewohnheit ( wie ich mit C angefangen habe, gab’s noch kein C++ :wink: ) auch manchmal noch so, aber gerade dabei sollte es für Einsteiger kein Problem sein sich solche alten Verfahren garnicht erst anzugewöhnen. ( Wobei es durchaus noch Anwendungsfälle dafür gibt, aber das ist dann wieder eher nichts für Einsteiger )

Meinen “verträumten” Beitrag habe ich entfernt!

Hallo,

3 Vorschläge wie man diese definieren könnte.

constexpr byte AKTIV {LOW};
constexpr byte INAKTIV {HIGH};
   oder
constexpr bool AKTIV {false};
constexpr bool INAKTIV {true};
   oder
constexpr byte AKTIV {0};
constexpr byte INAKTIV {1};

Warum keine defines? Die kann man ungewollt überschreiben.

#define PIN 5
#define PIN 8

Im Besten Fall gibts eine Warnung.

Oder ganz anders

if ( !digitalRead(reedPinA) ) {
...

So mag ich es, danke für die Vorschläge!

So habe ich es verwendet, jetzt drehen wir uns im Kreis :wink:

Hallo,

hab nachgeschaut, ja hattest du schon. Ich würde es auch dabei belassen. Wer mit Klassen rummacht sollte auch damit klarkommen. :wink:
Das Thema auf true Abfrage gabs ja schon einmal vor langer Zeit. Ist so am sichersten. Also mit letzteren ohne speziellen Vergleich.