Variablen und Pins deklarieren - Uno R3

Ich scheitere derzeit etwas an der richtigen Deklaration von Variablen.

Das Ziel ist, dass Pins einen Namen kriegen und es eine weitere Variable gibt, die ich zum Zwischenspeichern nutzen will. Diese Variable soll zu Beginn schon einen bestimmten Wert gespeichert haben.

Allerdings weiß ich nicht so richtig, wie sich beides voneinander unterscheidet, denn beides sieht für mich ziemlich gleich aus.

Pin benamsen:

const bool bltaster = 5;

Variable deklarieren und Wert zuweisen:

int lichtdauer = 20;

Allein an der Stelle streikt die IDE beim kompilieren, was verständlich ist, sieht ja aus, als wöllte ich dem nicht existierenden Pin 20 einen Namen geben. Beides hab ich ins Setup geschrieben. Wo liegt der Fehler und wie mach ich es richtig?
Bitte für ganz Dumme erklären...

Welcher Fehler?

Ich kann nur finden, daß

keinen Sinn macht, weil bool nur 2 Werte kennt (true/false, 0/1).

Gegen Verwechslungen sollten Pin-Namen eben "Pin" beinhalten, also eher
const bltasterPin = 5;

Für die richtige Verwendung von Namen ist der Programmierer (Du!) zuständig, der muß also die Bedeutung der Namen erkennen.

Das macht nicht viel Sinn, da die Namen dann nur in setup() bekannt sind. Wenn sie auch im Rest des Programms verwendbar sein sollen, dann sollten sie vor setup() stehen.

Das ist das Problem, ich bin ziemlicher Anfänger und habe dementsprechend wenig Plan von der Sache.

bltaster soll auch nur true oder false speichern und sich seinen Wert von Pin 5 holen.

Also kommt die Benennung von Pins und Variablen vor setup und der Pinmodus dahinter?

Werte holt man mit digitalRead()

Mein Rat:
Ein schönes dickes modernes C++ Buch auswendig lernen.
Zudem die gesamte Arduino Referenz, und auch die Beispielen, wenigstens einmal überfliegen.

Also könnte ich schreiben

pinMode(5, INPUT);

und ins Setup

bltaster = digitalRead(5)?

Hinsetzen, lesen und auswendig lernen kann ich nicht. Ich kann es wirklich nicht. Ich muss machen und probieren. Ganz langsam komm ich auch voran. Viele Beispiele und Erklärungen helfen (mir) leider nicht sehr weit, da oft noch jede Menge anderer Code um den Teil gepackt ist, den ich brauche.

fang doch einfach mal mit den Beispielen aus der IDE an:

da hast doch alles drinnen was du brauchst.
Eine Variable, eine Konstante, setzen von einem Pin ...

z.B. Blink Without Delay:

// constants won't change. Used here to set a pin number:
const int ledPin =  LED_BUILTIN;// the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change:
const long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}

Dagegen hilft es, die Beispielprogramme in der IDE anzuschauen. In Deinem Fall wäre das Debounce Beispiel ziemlich hilfreich, auch für den nächsten Schritt.
Lesen bildet! :wink:

Richtig, alle Namen (von Variablen, Pins...) müssen zuerst deklariert werden. Dabei kann bereits ain Wert zugewiesen werden, bei const muß der Wert sogar gleich zugewiesen werden.

Wenn für Pins die Nummern und Werte benannt werden sollen, dann müssen die auch unterschiedliche Namen bekommen. Z.B.

const blPin = 5; //konstant
bool blWert; //variabel
void setup() {
  pinMode(blPin, INPUT_PULLUP);
}
void loop() {
  blWert = digitalRead(blPin);
}

Nee, bitte mit Variablentyp, sonst wird das nix. Weil Pin-Nummern in der Regel kleiner als 256 sind, bietet sich byte an:
const byte blPin = 5; //konstant
oder
const unit8_t blPin = 5; //konstant
Das in den Beispielen häufig verwendete const int foo = pinNr; ist Quatsch, weil es ein Byte mehr verwendet als nötig.

Ok, damit bin ich schon einen Schritt weiter :slight_smile:

Ich denke, ich werde wirklich mal die Beispiele zurate ziehen. Sonst werf ich noch frustiert mein schönes Schaltschränkchen vom Balkon.

Bei Variablen ja, bei Konstanten bitte nicht! Der Compiler ist clever genug, den richtigen Typ herauszufinden, und alle const kann er ganz wegoptimieren.

Hast Recht, ich war da wohl bei Delphi. Da geht alles viel einfacher, intuitiver, logischer und komfortabler als in C.

Ich habe aber anhand des belegten Speichers schon beobachtet, daß der Arduino Compiler schon mal int nur als Byte gespeichert hat.

Wenn der Optimizer/Kompiler schon zur Kompilezeit feststellen/garantieren kann, dass diese Variable den Wertebereich von unsigned char nicht verlässt, darf er das.

Wobei mir scheint, dass Aussagen zum Optimizer bedenklich/Wackelkandidaten sind, wenn man nicht alle Randbedingungen zu 100% auf dem Schirm hat.


constexpr    byte blPin0 {5}; //konstant // meine Empfehlung
const         int blPin1 = 5; //konstant
const        byte blPin2 = 5; //konstant
const        auto blPin3 = 5; //konstant // ab C++14 oder C++17
const decltype(5) blPin4 = 5; //konstant
const         int blPin5 = 5.1234E-2; //konstant // nicht typesicher




using PinType = const byte;
PinType bl {5}; // Typesicherer, auf pin im Bezeichner kann verzichtet werden

Das ist ein ernstes Problem.
Ohne den Willen sich die Grundlagen anzueignen, wird das ein langes und vermutlich frustrierendes stochern im Nebel.

Am Willen liegt es wirklich nicht, ich hab auch schon eine schöne PDF, die vieles erklärt. Aber lernen tu ich nur wirklich durch probieren und üben. An sich soll ein relativ einfaches Programm entstehen. Ein paar mal wenn das, dann das, 4 Sensoren gleicher Bauart müssen eingelesen werden und es werden 3 Relais geschaltet.

Bei

#define blPin 5

muß der Compiler alle Entscheidungen treffen und muß das natürlich auch können. Wenn C++ so viel besser als C sein soll, wie kann man dann obiges #define mit gleicher Wirkung in reinem C++ ohne Preprozessor ersetzen?

Dein define ist untypisiert!
Die 5 ist laut Sprachdefinition ein int Literal. Zu dem wird es am Einsatzort bei der Ersetzung.
Weder C noch C++ kennen untypisierte Variablen/Konstanten.
Deine Frage ist also irgendwo irrational, bzw. nicht oder nicht so einfach zu beantworten.

Das was als positive Antwort, dem am nächsten kommt, wäre wohl sowas:

constexpr    byte blPin  {5};

Natürlich, und das soll es ja auch sein!
Ohne Pin-Namen müßte man ja schreiben digitalRead(5) etc.
und diese 5 hat auch keinen Typ. Mit #define erreicht man genau das selbe, ohne Typ aber mit Namen.

Doch hat sie!
Dort erscheint die 5 als int Literal.
Und nicht als Byte!
Wenn erforderlich wird implizit konvertiert oder gewarnt/error

Bemerke:
digitalRead(3.14) geht ohne murren durch
auch dein define mit 3.14
Allerdings

constexpr    byte blPin  {3.14};

haut dir der Compiler um die Ohren.

Der Einsatz von define, wenn es auch anderes geht, bringt aus meiner Sicht keinerlei Vorteile.
Bitte nenne ein gutes Beispiel!
Nur ein einziger wohl begründeter Fall, dafür wäre ich dir dankbar

Ja, ich kann auch besser praktisch durch Versuch und Irrtum lernen, wobei eine Doku zum Nachschlagen schon auch wichtig ist. Bei den Beispielen gibt es gute und auch schlechte und es ist schwer, da zu unterscheiden. Außerdem gibt es verschiedene Blickwinkel. Schau'n wir uns mal das Beispiel aus #7 an:

// constants won't change. Used here to set a pin number:
const int ledPin =  LED_BUILTIN;// the number of the LED pin
  1. LED_BUILTIN ist die konstante Festlegung eines Wertes mit dem Pin, an dem eine LED angeschlossen ist. Bei einem UNO ist das 13, was man wissen muß. Das ist gut gemeint, aber so ganz am Anfang auch kryptisch. Ich hatte es anfänglich nicht verstanden, möglicherweise mein Problem :roll_eyes:
  2. Der Typ int ist Standard, aber benötigt zwei Byte auf einem UNO, der knapp ist mit Resourcen. Besser finde ich daher byte oder uint8_t als Typ, weil der nur ein Byte benötigt und vorzeichenlos ist.
  3. Aber der optimierende Kompiler erkennt das Einsparpotential und nutzt trotz int nur ein Byte.

Ups! Was also tun?

Höhere Programmiersprachen sind von Menschen für Menschen gemacht, weshalb ich einen Stil bevorzuge, bei dem ich einem imaginären Leser etwas mitteilen kann:

  • const sagt, dieser Wert soll nicht verändert werden.
  • byte oder uint8_t sagt, alle Pins haben Werte kleiner 255 und passen in ein Byte.
  • ledPin ist ein Name für einen Pin, an der eine LED dranhängt.

Dem optimierenden Kompiler überlasse ich dann die Optimierung.

In naher Zukunft bin dann ich der nicht mehr imaginäre Leser und freue mich über einen mir verständlichen Programmierstil :slightly_smiling_face:

int ledState = LOW;             // ledState used to set the LED

Der Typ int gefällt mir hier überhaupt nicht, meine Reaktion: "Da hat einer nicht nachgedacht!" Beim UNO ist Variablenspeicher rar. Der Zustand eines digitalen Eingangs kann aber nur HIGH oder LOW sein, es genügt also ein Bit. Der Typ int hat aber sechzehn, was für eine Verschwendung!

Wenn Du bool ledState = LOW; schreibst, gibst Du dem Kompiler die Möglichkeit, das Beste daraus zu machen und dem imaginären Leser eine klare Information.

const long interval = 1000;           // interval at which to blink (milliseconds)

Da das Intervall positiv ist, bevorzuge ich const unsigned long interval = 1000; oder const uint32_t interval = 1000; als Typ.

Es geht auch etwas kürzer:

void loop() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    digitalWrite( ledPin, !digitalRead(ledPin) );
  }
}

Mein Tipp: Wenn Du nicht sicher bist, konsultiere eine Doku Deiner Wahl und wenn Du dann immer noch unsicher bist, dann frage hier einfach nochmal :slightly_smiling_face:

Sprach-Referenz

Darüberhinaus auch C++ Reference

Jetzt viel Spaß bei der restlichen Diskussion :wink:

Aber nicht im Speicher, und darauf kommt es doch an. Wieso hackst Du darauf herum, daß der kleinstmögliche Datentyp verwendet werden soll, um Konstante in den Speicher zu zwingen, statt auf die Belegung von Speicherplatz einfach zu verzichten?

Wenn es dir NUR auf den Speicherverbrauch ankommt, dann ist die constexpr Definition gleichwertig.
Denn davon kommt auch nichts in den RAM.

Das Argument hat sich damit erledigt, nehme ich mal an.....

Zudem beharre ich nicht auf den kleinsten Datentyp, sondern auf die Nutzung der Vorteile, welche einem eine strenge Typisierung bietet.
Die ohne Not über Bord zu werfen halte ich für falsch.