Neopixel Treppenlicht mit Taster

Guten Abend

Ich bin gerade etwas am verzweifeln. Ich versuche mich schon seit einigen Wochen an einem Projekt wo ich dachte: ,,so schwer kann das doch nicht sein!`` Denkste, mittlerweile habe ich echt grossen Respekt vor Programmierern. Ich werde bestimmt nicht mehr motzen wenn ein Spiel nicht ganz einwandfrei läuft :slight_smile: Ich habe sehr viel im Internet gesucht aber keine passende Lösung für mich gefunden, oder dann habe ich sie wohl nicht verstanden.

Mein Ziel wäre folgendes:
Ich möchte im Handlauf der Treppe ein Neopixel LED-Band einlassen (SK 6812, also eigentlich wie WS2812 aber halt RGBW). Ein Taster unten an der Treppe, einer oben an der Treppe.

Funktion:
Normalbetrieb nur mit weissem Licht:
Drückt man den Taster unten soll das Licht von unten nach oben einschalten (Lauflicht) und erst mal so bleiben. Dann geht man ja entweder die Treppe nach oben oder auch nicht, deswegen dann zwei Optionen.

  1. man drückt erneut den Taster unten, das Licht soll von oben nach unten ausschalten
  2. man drückt den Taster oben, das Licht soll einem folgen und von unten nach oben ausschalten.
    Das ganze dann auch umgekehrt (Licht ist aus, man drückt oben, Licht geht von oben nach unten an, entweder oben noch einmal drücken oder unten)

Als kleiner Party Trick würde ich dann gerne durch langen Tastendruck einen Regenbogen starten und beenden.

Mein Problem ist vor allem, dass ich nicht verstehe wie ein Schalter mehrere Funktionen haben kann.
Einmal müsste ja das Licht von unten nach oben und wenn ich noch einmal drücke umgekehrt von oben nach unten.
Das ist bestimmt ein Anfänger Problem, weil mir die Basics fehlen. Aber mir fehlt leider etwas die Zeit mich da noch besser einzuarbeiten. Ich mache noch eine Weiterbildung zur Zeit und für beides reicht es im Moment nicht. Gerne würde ich aber bei dem Projekt auch einmal weiterkommen, weshalb ich nun hier gelandet bin.

Ich danke Euch auf jeden Fall schon für eure Hilfe.

Hier noch mein bisheriger Code:

#include <Adafruit_NeoPixel.h>   // Vorprogrammierte Befehle für Neopixel

#define LED_PIN     6            // Welcher Anschluss ist am LED Band
#define LED_ANZAHL  40           // Wie viele NeoPixel hat das LED Band
#define HELLIGKEIT 50            // definiert die Helligkeit der einzelnen LED (zwischen 0 und max. 255)
                                 // jede LED hat 20mA bei voller Helligkeit
                                 // (max. ist 60mA wenn RGB voll an = weisses Licht durch mischen)

#define GESCHWINDIGKEIT 50       // Wie schnell das Licht wandert (Pause zwischen einzelnen LED in ms)

//Beschreibung vom LED Band:
Adafruit_NeoPixel strip(LED_ANZAHL, LED_PIN, NEO_GRBW + NEO_KHZ800);
// Hier werden die oben eingegbenen Werte verarbeitet (könnte man auch hier reinschreiben, oben ist es aber sauberer)
// Verdrahtung der Pixel (Wie ist das Band aufgebaut, die meisten sind GRB (nicht RGB, auch wenn sie überall so heissen)
// Frequenz des Bandes (meistens 800 kHz, kann auch 400 kHz sein)

int schalterZustandUnten = 0;
int schalterZustandOben = 0;
// die Variable schalterZustandUnten ist zuerst 0, ändert sich aber während betätigung des Schalters auf 1

// bis hier Grundeinstellungen
// ab hier Setup (Einstellung welche Pins Eingänge / Ausgänge)

void setup() {

pinMode (2,INPUT);
pinMode (4,INPUT);
// Bestimmt das Pin 2 und 4 Eingänge sind

strip.begin();           // Startet Neopixel
strip.show();            // Sendet Befehle an Neopixel
strip.setBrightness(HELLIGKEIT); // Helligkeit des Bandes auf obige Einstellung setzen

}

// ab hier Programm das ausgeführt wird
void loop() {
  
schalterZustandUnten = digitalRead (2); // überprüft den Zustand am Pin 2 und speichert diesen in der Variable (schalterZustandUnten)
schalterZustandOben = digitalRead (4);  //dito


if (schalterZustandUnten == HIGH)
{
    for(int unten=0; unten<LED_ANZAHL; unten++) {                    //Die Variabel unten ist 0 und kleiner als die LED Anzahl zähle +1

    strip.setPixelColor(unten, strip.Color(0, 0, 0, HELLIGKEIT));    //unten = LED, also LED 0 beginnt weiss zu Leuchten, immer +1 bis LED Anzahl erreicht
    strip.show();                                                    // sende die info
    delay(GESCHWINDIGKEIT);                                          // Wie schnell das Licht von einer LED zur nächsten wandert
    }
}                                                                    // Alle Leuchten und warten bis Schalter Oben gedrückt wird
if (schalterZustandOben == HIGH)
{    
    for(int unten1=0; unten1<LED_ANZAHL; unten1++) {                 //Die Variabel unten1 ist 0 und kleiner als die LED Anzahl zähle +1

    strip.setPixelColor(unten1, strip.Color(0, 0, 0, 0));            //unten1 = LED, also LED 0 bekommt als weiss den Wert 0 (aus), immer +1 bis LED Anzahl erreicht
    strip.show();  
    delay(GESCHWINDIGKEIT); 
    }
}
}

Dein Problem ist nicht so schwierig.
Du musst dir nur merken, was dein Taster aktuell für eine Funktion gestaret hat und daraus dann die zweite Funktion starten.
So wird abhängig vom der Situation immer die richtige Funktion gestartet.

Danke für die schnelle Antwort.

HotSystems:
Du musst dir nur merken, was dein Taster aktuell für eine Funktion gestaret hat

Wie mache ich denn das? Ist das die bool Funktion? Da habe ich auch etwas ausprobiert zusammen mit einem Counter. Da habe ich dann aber den Fehler: was not declared in this scope erhalten und dass verworfen.

Hier dieser Code mit bool (den Rest habe ich rausgelöscht im Setup):

//Tastereinstellungen
#define tasterPin 2                              //Taster unten ist an Pin 2
#define debcounce_delay 15                //Entprellzeit für den Taster

  bool tasterState, tasterState_alt;       //Variabel zum Merken des Tasterstandes
  uint8_t counter;                                //Zähler wie oft der Taster gedrückt wird



// bis hier Grundeinstellungen
// ab hier Setup (Einstellung welche Pins Eingänge / Ausgänge)

void setup() {

pinMode(tasterPin,INPUT);                     // Bestimmt das Pin 2 ein Eingang ist



}

// ab hier Programm das ausgeführt wird

void loop() {

tasterAbfrage();
  if (counter == 0) funktion1();
  if (counter == 1) funktion2();
}
void tasterAbfrage()
{
  static uint32_t debounce_time;
  if (millis()-debounce_time>debounce_delay)tasterState = digitalRead(tasterPin); //einlesen des Taster

if (tasterState != tasterState_alt)        //beim drücken des Tasters
{
  debounce_time=millis();                  //kurz warten

  if (tasterState)                         //wenn Taster gedrückt  
  {
    counter++;                             //zähle +1
    if (counter > 1) counter = 0;          //wenn auf 1 gezählt, gehe wieder auf 0
  }
    tasterState_alt = tasterState;         //Taster Status aktualisieren
}

void funktion1()
{

    for(int unten=0; unten<LED_ANZAHL; unten++) {

    strip.setPixelColor(unten, strip.Color(0, 0, 0, HELLIGKEIT));
    strip.show();   // sende die info
    delay(GESCHWINDIGKEIT); // Wie schnell das Licht von einer LED zur nächsten wandert
    }
    
 void funktion2() 
    for(int unten=0; unten<LED_ANZAHL; unten++) {

    strip.setPixelColor(unten, strip.Color(0, 0, 0, 0));
    strip.show();   // sende die info
    delay(GESCHWINDIGKEIT); // Wie schnell das Licht von einer LED zur nächsten wandert
    }
}
}

Ja, so ungefähr
Das ist nicht der Tasterstatus, sondern ich würde es Funktionsrichtung nennen. Aber bool ist ok dafür.
Rauf ist true und runter ist false, oder so ähnlich.

Hi

Ich würde diesen Status als byte deklarieren und mir 'merken', was ich wo ausgelöst habe.
0 nix
1 Tasterdruck kurz oben bei AUS
2 Tasterdruck kurz unten bei AUS
3 Tasterdruck kurz oben bei AN
4 Tasterdruck kurz unten bei AN
... to be continue ...

Du bist im Status 0 und irgendwo wird ein Taster gedrückt, somit wird Status entweder 1 oder 2.
Aus 1 oder 2 kannst Du per Knopfdruck oben oder unten in den Status 3 oder 4 wechseln.
Wenn die Animation beendet ist (Licht wieder aus), Status auf 0.

MALE Dir auf, was wann in welcher Richtung passieren können soll.
Du hattest noch einen langen Tastendruck erwähnt - Dieser kann bis jetzt in sämtlichen Status (ist ebenfalls die Mehrzahl von Status) ebenfalls ausgelöst werden - also auch hier musst Du Dir überlegen, was dann passieren soll.

MfG

Stromer:
Ich habe sehr viel im Internet gesucht aber keine passende Lösung für mich gefunden, oder dann habe ich sie wohl nicht verstanden.

Was Du übersehen haben könntest:

Unterstützung bei Treppenbeleuchung

NEO Pixel - Treppenlauflicht

Treppenbeleuchtung mit 2 PIR Sensoren und FastLED

Treppen Beleuchtung mit LED Strips

Projekt Treppenbeleuchtung

Vielen Dank für die Anregungen und vielen Beispiele. Ich habe natürlich nur über Google gesucht und nicht direkt hier im Forum, so findet man offenbar viel mehr.
Ich werde das bei Gelegenheit alles mal durchlesen und würde mich bei Fragen gerne wieder melden.

Stromer:
... so findet man offenbar viel mehr.

Nee, alle Programme, die ich veröffentliche, schlummern auch noch auf meiner Festplatte, wobei ich immer den Link und das Thema notiere.

Ich schreibe Programme, um zu helfen, aber auch um etwas zu lernen. Also will ich die Sachen wiederfinden. Du profitierst davon :slight_smile:

Seit ich mich mit Arduino beschäftige, habe ich den endlichen Automaten (Schrittkette, finite state machine) als sehr hilfreich entdeckt. Wenn Du in diesem Forum nach "agmue anleitung" suchst, stößt Du auf verschiedene von mir verfaßte Texte. Ob sie Dir helfen, kannst nur Du herausfinden. Der Autor stände für Fragen zur Verfügung :slight_smile:

Danke noch einmal für die bisherige Hilfe.
Ich habe nun einiges probiert und bin erneut auf ein Problem gestossen. Ich weiss da nicht genau nach was ich suchen muss um dies zu lösen.
Ich habe zu Test zwecken für das eigentliche Projekt den Serial Monitor benutzt und versucht mir die Taster Zustände zu merken.

Hier mein Code dazu:

int taster_unten ;
int taster_oben ;
int neuer_Zustand_unten = 0;
int neuer_Zustand_oben = 0;

void setup() {
  pinMode (2,INPUT);
  pinMode (4,INPUT);
  
Serial.begin(9600);
}

void loop() {
  
 taster_unten = digitalRead (2);
 taster_oben = digitalRead (4);

if (taster_unten == HIGH && neuer_Zustand_oben == 0)
{
  { neuer_Zustand_unten = 1;

  Serial.print ("Strip von unten nach oben ein");          //unten wird gedrückt,oben nicht, der zustand unten ist neu 1 (wurde gedrückt)
  }                                                       // Das Licht schaltet von unten nach oben ein
        
//MINDESTENS HIER MACHE ICH ETWAS FALSCH
  
if (neuer_Zustand_unten == 1 && taster_unten == HIGH)
{
  neuer_Zustand_unten = 0;

  Serial.print ("Strip von oben nach unten aus");
  }                                                        //unten ist gedrückt worden, ich drücke noch einmal, der zustand unten ist neu 0(ein zweites mal gedrückt).
                                                           //Das Licht schaltet von oben nach unten aus (Ausgangslage)

if (taster_oben = HIGH && neuer_Zustand_unten == 1)
{ neuer_Zustand_unten = 0 ;
  Serial.print ("Strip von unten nach oben aus");          //unten ist gedrückt worden,ich drücke oben, der zustand unten ist neu 0 (zurück in Ausgangslage)
                                                           //Das Licht schaltet von unten nach oben aus (Ausgangslage)

//Hier weiter mint umgekehrtem Betrieb
                                                           
}

Ich habe es als Kommentar im Code markiert, mein Fehler ist zwischen den Bedingungen.
Wenn die erste erfüllt wird, wird die zweite auch gleich erfüllt weil wohl der Taster noch als gedrückt erkannt wird. Delay Zeiten haben mich nicht weitergebracht, im Gegenteil.
Ich habe schon vom entprellen gelesen und dies versucht, aber daran liegt es nicht (Vermutlich muss ich dies später auch noch einbauen aber es ist sonst noch ein grundsätzlicher Fehler den ich nicht weg bringe).

Dazu jetzt 2 Fragen

  1. postmaster meinte ich soll Byte verwenden, was ist den der Unterschied zu int? Beide speichern doch ganze Zahlen, klar Byte würde für meine Aufgabe vom Bereich reichen. Gibt es andere Vorteile? Speicherplatz des Arduino?

2. Die Hauptfrage

Was mache ich falsch bzw. nach was muss ich suchen um mein Problem (das nach funktion1 gleich Funktion2 anläuft) zu lösen?
Gerne dürft ihr mir da auch ein Beispiel machen.

Danke schon einmal

Stromer:
Speicherplatz des Arduino?

Ja, denn der ist kostbar.

Solltest Du erwägen, auf einen anderen µC-Typ zu wechseln, so wäre die Schreibweise uint8_t, int8_t, uint16_t, int16_t und so weiter empfehlenswert, weil dadurch die Anzahl der verwendeten Bits eindeutig ist.

Hi

Du setzt am Anfang von loop() die Variablen taster_unten und taster_oben auf die Zustände der Pins.
Dann fragst Du den 'taster_unten' ab und setzt 'neuer_Zustand_unten' auf 1.
Direkt darunter fragst Du ebenfalls 'taster_unten' ab, zusätzlich, ob der 'neue_Zstand_unten' auf 1 ist.

Wenn in der oberen IF 'taster_unten' gedrückt war (nur so konnte 'neuer_Zustand_unten' auf 1 gesetzt werden), wird JETZT 'taster_unten' Nichts an Seinem Wert geändert haben - Das ist eine Variable, Die NICHTS direkt mit dem Pin zu tun hat.
Somit greift die 2.te IF ebenfalls, da Ihre Bedingungen erfüllt sind durch die Änderung der ersten IF und Deren Bedingungen.

WAS willst Du erreichen?
MALE Dir auf, WAS WANN WESHALB passieren soll, nennt sich Programm Ablauf Plan (BAP).

MfG

noiasca:
du hast also ZWEI Taster an ZWEI Pins?

Wie schaltest du diese: mit externen PULL-UP oder PULL-DOWN Widerständen?
Schaltest du gegen Plus oder Minus?
mach ein Schaltbild auf Papier und hänge das Bild rein.

Genau, mit externen Pulldown Widerständen weil dies bei allen Anfänger Beispielen bisher so war. Schaltplan im Anhang.
Verwendet ihr die internen Widerstände oder sind externe besser?

agmue:
Solltest Du erwägen, auf einen anderen µC-Typ zu wechseln, so wäre die Schreibweise uint8_t, int8_t, uint16_t, int16_t und so weiter empfehlenswert, weil dadurch die Anzahl der verwendeten Bits eindeutig ist.

Andere als Arduino oder gibt es da bei den verschiedenen Modellen unterschiede?
Darüber habe ich mir noch nicht gross Gedanken gemacht bevor ich die Basics noch nicht verstehe.
Im Moment verwende ich den Arduino Uno aus dem Starterkit, für das Projekt dachte ich dann an eine kleinere Variante, den Nano oder so falls der ausreicht. Habe ich wie gesagt noch nicht gross angeschaut bevor das Programm noch nicht fertig ist.

postmaster-ino:
Hi

Wenn in der oberen IF 'taster_unten' gedrückt war (nur so konnte 'neuer_Zustand_unten' auf 1 gesetzt werden), wird JETZT 'taster_unten' Nichts an Seinem Wert geändert haben - Das ist eine Variable, Die NICHTS direkt mit dem Pin zu tun hat.

WAS willst Du erreichen?
MALE Dir auf, WAS WANN WESHALB passieren soll, nennt sich Programm Ablauf Plan (BAP).

MfG

ACHSOOOOOOO. Ich blödi. Ich dachte diese Variable passt sich immer von selbst dem Taster Zustand an. Wenn gedrückt = 1 wenn losgelassen von selber = 0

Dann ist das also nicht so. Das hilft mir hoffe ich sehr viel weiter, besten Dank für eure vielen Tipps.

Ich muss zugeben am Anfang habe ich gehofft jemand postet mir einfach den Code rein und gut ist :smiley:
Aber jetzt möchte ich das ganze schon selber schaffen. Finde es super gebt ihr ,,nur`` Tipps und das so schnell.

Edit:

noiasca:
Mach mal das Beispiel 02.Digital | Debounce - baue es nach und berichte!

habe da noch kurz reingeschaut das könnte mir auch helfen. Aber es ist schon spät werde das ein andermal besser anschauen. Danke noch einmal

So sieht man es sofort (kannst Du auch):

Stromer:
Verwendet ihr die internen Widerstände oder sind externe besser?

Weil ich faul bin, normalerweise die internen (ca. 50 kOhm). Weil Du aber große Entfernungen (Treppenlänge) überbrücken mußt, sind externe kleine 1 - 10 kOhm vermutlich störsicherer. Allerdings würde ich PullUps verwenden und dann gegen GND schalten.

Stromer:
Andere als Arduino oder gibt es da bei den verschiedenen Modellen unterschiede?

Bei Arduino findest Du eine Menge Varianten, aber auch Teensy, ESP32 oder ESP8266 lassen sich per Arduino-IDE programmieren. Der UNO hat einen 8-Bit-µC, andere 32-Bit-µCs. Blöderweise bedetet der Typ int beim UNO 16 Bits, bei anderen aber 32. Bei vielen LEDs geht schnell der Speicher zur Neige, denn pro Leuchtpunkt benötigst Du vier Bytes (RGBW). Bei 40 ist das unproblematisch.

Allerdings brauchen die WS2812-LEDs recht lange für ein Lauflicht, das war in manchen Beiträgen als zu langsam beschrieben worden. Daher nutze ich, wenn möglich APA102 oder baugleiche mit zwei Pins (Takt und Daten), weil man die ca. zehnmal schneller ansteuern kann. Dazu eignet sich dann auch ein schnellerer Prozessor als beim UNO besser. Beispielsweise meine Baranimation läuft daher mit einem Teensy 3.2. Wenn man APA102 verwendet, eignen sich die SPI-Anschlüsse von UNO/Nano für eine schnelle Ausgabe.

Ein Beispiel mit DotStar (=APA102), das man aber leicht für NeoPixel umschreiben kann, soll zeigen, wie mit einer Schrittkette die doppelte Funktion eines Tasters erzeugt werden kann:

#include <Adafruit_DotStar.h>
#include <SPI.h>
#define NUMPIXELS 122    // Number of LEDs in strip

// Hardware SPI is a little faster, but must be wired to specific pins
// (Arduino Uno = pin 11 for data, 13 for clock, other boards are different).
Adafruit_DotStar strip(NUMPIXELS, DOTSTAR_BRG);

const byte pin_taster_oben = 4;
const byte pin_taster_unten = 2;
const uint32_t intervall = 20;
uint32_t jetzt;
enum {AUS, NACH_OBEN_AN, NACH_UNTEN_AN, AN, NACH_OBEN_AUS, NACH_UNTEN_AUS};
byte schritt = AUS;

void setup() {
  strip.begin(); // Initialize pins for output
  strip.show();  // Turn all LEDs off ASAP

  pinMode (pin_taster_oben, INPUT_PULLUP);
  pinMode (pin_taster_unten, INPUT_PULLUP);
}

void loop() {
  jetzt = millis();
  switch (schritt) {
    case AUS:
      if (!digitalRead(pin_taster_unten)) schritt = NACH_OBEN_AN;
      if (!digitalRead(pin_taster_oben)) schritt = NACH_UNTEN_AN;
      break;
    case NACH_OBEN_AN:
      if (rauf(true)) schritt = AN;
      break;
    case NACH_UNTEN_AN:
      if (runter(true)) schritt = AN;
      break;
    case AN:
      if (!digitalRead(pin_taster_unten)) schritt = NACH_UNTEN_AUS;
      if (!digitalRead(pin_taster_oben)) schritt = NACH_OBEN_AUS;
      break;
    case NACH_OBEN_AUS:
      if (rauf(false)) schritt = AUS;
      break;
    case NACH_UNTEN_AUS:
      if (runter(false)) schritt = AUS;
      break;
  }
}

bool rauf(bool an) {
  static uint32_t vorhin = 0;
  static uint16_t led = 0;
  if (jetzt - vorhin >= intervall) {
    vorhin = jetzt;
    if (an) {
      strip.setPixelColor(led, 0x000055);
    } else {
      strip.setPixelColor(led, 0);
    }
    strip.show();                     // Refresh strip
    if (led < NUMPIXELS) {
      led++;
    } else {
      led = 0;
      return true;
    }
  }
  return false;
}
bool runter(bool an) {
  static uint32_t vorhin = 0;
  static uint16_t led = NUMPIXELS - 1;
  if (jetzt - vorhin >= intervall) {
    vorhin = jetzt;
    if (an) {
      strip.setPixelColor(led, 0x550000);
    } else {
      strip.setPixelColor(led, 0);
    }
    strip.show();                     // Refresh strip
    if (led > 0) {
      led--;
    } else {
      led = NUMPIXELS - 1;
      return true;
    }
  }
  return false;
}

postmaster-ino:
WAS willst Du erreichen?
MALE Dir auf, WAS WANN WESHALB passieren soll, nennt sich Programm Ablauf Plan (BAP).

MfG

Du meinst so etwas?
Edit habe es leider nicht geschafft das Bild hier einzufügen.

noiasca:
Mach mal das Beispiel 02.Digital | Debounce - baue es nach und berichte!

Das habe ich und bei dem Beispiel noch den zweiten Taster eingefügt. So kann ich jetzt mit beiden Tastern die interne LED ein- und ausschalten. Ich denke ich habe dieses Programm ziemlich verstanden (wenn ich es sehe, einfach so selber schreiben könnte ich es nicht), nur weiss ich noch nicht wie ich das bei mir anwenden kann. Bei dem Bsp wird ja eine Funktion immer umgekehrt, also HIGH oder LOW.
Aber ich kann ja bei den Neopixel nicht einen Ausgang von HIGH auf LOW oder umgekehrt setzen, oder auch nicht die Funktion da diese ja immer eine andere ist.

Ich müsste wie in Beitrag #4 gesagt die Zustände merken um die jeweilige Funktion zu starten.
Aber mit welchen datentypen ich das schaffe verstehe ich noch nicht. Da gibt es so viele für einen Anfänger :astonished:

Oder hast du mir mit deinem Beispiel die Lösung schon serviert agmue? Ich habe das schon etwas angeschaut aber noch nicht wirklich verstanden. Du hast wirklich viele Beispiele und verstehst deine Sache, für mich ist dies aber noch etwas zu viel.

Ich verstehe auch noch nicht ganz, wieso das so nicht funktioniert.

void loop() {
  
 taster_unten = digitalRead (2);
 taster_oben = digitalRead (4);

if (taster_unten == HIGH && neuer_Zustand_oben == 0)
{
    neuer_Zustand_unten = 1;
    taster_unten = 0;

  Serial.print ("Strip von unten nach oben ein");
  }
if (neuer_Zustand_unten == 1 && taster_unten == HIGH)
{
  neuer_Zustand_unten = 0;
  taster_unten = 0;

  Serial.print ("Strip von oben nach unten aus");
  }

if (taster_oben = HIGH && neuer_Zustand_unten == 1)
{ neuer_Zustand_unten = 0 ;
  neuer_Zustand_oben = 0;
  taster_oben = 0;
  Serial.print ("Strip von unten nach oben aus");
}
}

Für mich liest sich das eigentlich als gut, aber wenn ich taster unten drücke und die erste if bedingung erfülle (Strip von unten nach oben ein) startet jetzt anstelle der zweiten gleich die dritte if bedingung (Strip von unten nach oben aus). obwohl der taster oben nicht gedrückt wurde.
bin ich hier so auf dem Holzweg?

Kann ich meine Idee nur mit if und variablen lösen? plus millis für die Taster Entprellung, oder brauche ich switch/ case und andere?

Vielen Dank für die Antworten

ein Plan - mit dir kann man arbeiten!
Schaut doch gut aus.

Jetzt machst dir für deine 2 (oder 3) Teilschritte je eine Funktion die nur genau das macht.

Wenn du die 3 Funktionen hast - löst du diese mit deinen Tastern aus.

Stromer:
Oder hast du mir mit deinem Beispiel die Lösung schon serviert agmue?

Abgesehen von Feinheiten (gleichzeitig will einer rauf und einer runter), ja. Wenn Du mit wenigen Änderungen auf NeoPixel umstellst, kannst Du es testen. Bei mir läuft es :slight_smile:

Stromer:
... für mich ist dies aber noch etwas zu viel.

Viele Wege führen nach Rom, aber einfacher und anfängerfreundlicher wirst Du es kaum bekommen. Doch der Autor dieser Zeilen steht für Fragen zur Verfügung. Außerdem gibt es Mitstreiter, die meine Programme besser erklären können als ich selbst.

Da switch/case nur eine andere Schreibweise für if/else ist, kannst Du eine Schrittkette auch mit if/else aufbauen. Ich finde switch/case komfortabler.

So ich hatte heute endlich mal wieder etwas Zeit mich in das Projekt einzuarbeiten.

Zuerst einmal vielen Dank für alle eure Tipps (postmaster-ino/noiasca mit dem einzeichnen der Funktionen in den Plan und auch allen andern).

Besonders an Agume mit dem Beispiel, Switch/Case scheint ja wirklich ideal zu sein für mein Projekt (wenn ich es denn richtig verstanden habe).
Ich habe auf deinen Rat hin versucht dein Beispiel an mein Projekt anzupassen. Leider war ich noch nicht erfolgreich. Ich bekomme den Fehler:

exit status 1
'runter' was not declared in this scope

Aber runter wird doch mit bool deklariert?

Hier einmal mein aktueller Code:

// Einstellungen Neo Pixel Strip
#include <Adafruit_NeoPixel.h>  

#define LED_PIN     6            
#define LED_ANZAHL  40           
#define HELLIGKEIT 50                                         
#define GESCHWINDIGKEIT 50      

Adafruit_NeoPixel strip(LED_ANZAHL, LED_PIN, NEO_GRBW + NEO_KHZ800);

//Einstellungen Taster Steuerung
const byte taster_oben = 2;
const byte taster_unten = 4;


enum {AUS, NACH_OBEN_AN, NACH_UNTEN_AN, AN, NACH_OBEN_AUS, NACH_UNTEN_AUS};
byte schritt = AUS;

void setup() {

 pinMode (taster_oben, INPUT);
 pinMode (taster_unten, INPUT);

strip.begin();                            
strip.show();                           
strip.setBrightness(HELLIGKEIT);

}

void loop() {

 switch (schritt) {
  case AUS:
    if (digitalRead(taster_unten)) schritt = NACH_OBEN_AN;
    if (digitalRead(taster_oben)) schritt = NACH_UNTEN_AN;
    break;
  case NACH_OBEN_AN:
    if (rauf(true)) schritt = AN;
    break;
  case NACH_UNTEN_AN:
    if (runter(true)) schritt = AN;
    break;
  case AN:
    if (digitalRead(taster_unten)) schritt = NACH_UNTEN_AUS;
    if (digitalRead(taster_oben)) schritt = NACH_OBEN_AUS;
    break;
  case NACH_OBEN_AUS:
    if (rauf(false)) schritt = AUS;
    break;
  case NACH_UNTEN_AUS:
    if (runter(false)) schritt = AUS;
    break;
 }

}

bool rauf(bool an){
  if (an){
      for(int unten=0; unten<LED_ANZAHL; unten++) {

    strip.setPixelColor(unten, strip.Color(0, 0, 0, HELLIGKEIT));
    strip.show();   
    delay(GESCHWINDIGKEIT); 
    }
    
  }else {
    for(int unten=0; unten<LED_ANZAHL; unten++) {

    strip.setPixelColor(unten, strip.Color(0, 0, 0, 0));
    strip.show();   
    delay(GESCHWINDIGKEIT); 
    }
  return false;
}


bool runter(bool an){
  if (an){
    for(int oben=LED_ANZAHL; oben>=0; oben--) {

    strip.setPixelColor(oben, strip.Color(0, 0, 0, HELLIGKEIT));
    strip.show();  
    delay(GESCHWINDIGKEIT); 
    }
    
  }else {
    for(int oben=LED_ANZAHL; oben>=0; oben--) {

    strip.setPixelColor(oben, strip.Color(0, 0, 0, 0));
    strip.show();
    delay(GESCHWINDIGKEIT);
    }
  }
  return false;
}

Dann hätte ich noch einige Fragen zu deinem Beispiel Agume, ob ich das auch richtig verstanden habe.

  1. byte schritt = AUS; Ich dachte byte speichert Zahlen, kann es auch Wörter speichern? Oder ist AUS = 0? AUS wird ja aber dann zu NACH_OBEN_AN oder NACH_UNTEN_AN. Werden die Wörter im Hintergrund ev. zu Zahlen?

Zum Switch/Case Ablauf von oben nach unten:

  1. müsste bei if (digitalRead(taster_unten)) schritt = NACH_OBEN_AN; nicht noch ein == HIGH hin (oder LOW je nach dem ob Pullup oder Pulldown) Damit erkannt wird das der Taster gedrückt wurde? Oder ist dies bei Switch/Case nicht nötig und wird so erkannt?

  2. if (rauf(true)) schritt = AN; das verstehe ich noch nicht ganz. Heist das die Variable rauf wird true, oder wenn die Variable true ist?

  3. Für mich würde es Sinn ergeben wenn die Version mit rauf wird true stimmt, sonst kann ich mir nicht ganz erklären wie das Licht eingeschaltet wird. Wieso heisst es dann unten wo das Licht geschalten wird bool rauf(bool an)? Ist true und an das selbe also 1?

Heisst zusammengefasst ist es richtig, wenn bei Switch/Case der Schritt NACH_OBEN_AN kommt wird bool rauf zu true/an und das Licht eingeschalten

Ich glaube das wäre erst einmal alles. Wieso ich aber den Fehler mit nicht deklariert erhalte ist mir noch nicht ganz klar.

Vielen Dank, dass ihr euch Zeit für mich nehmt.

Stromer:
Ich bekomme den Fehler:

exit status 1
'runter' was not declared in this scope

Aber runter wird doch mit bool deklariert?

Mit Strg+t kannst Du den Text syntaktisch formatieren lassen, was die fehlende Klammer zu Tage bringt:

  }
    return false;
  }
  // hier fehlt die Klammer!

  bool runter(bool an) {
    if (an) {
    return false;
  }
}

bool runter(bool an) {
  if (an) {

Stromer:

  1. byte schritt = AUS; Ich dachte byte speichert Zahlen, kann es auch Wörter speichern? Oder ist AUS = 0? AUS wird ja aber dann zu NACH_OBEN_AN oder NACH_UNTEN_AN. Werden die Wörter im Hintergrund ev. zu Zahlen?

Ich habe eine sehr einfache Variante der Enumeration declaration verwendet. Ich habe den Zahlen Namen gegeben, könnte man sagen.

Stromer:
2. müsste bei if (digitalRead(taster_unten)) schritt = NACH_OBEN_AN; nicht noch ein == HIGH hin (oder LOW je nach dem ob Pullup oder Pulldown) Damit erkannt wird das der Taster gedrückt wurde? Oder ist dies bei Switch/Case nicht nötig und wird so erkannt?

Das hat nichts mit switch/case zu tun, sondern damit, daß in der Klammer von if ein Ausdruck stehen muß, der 0 oder ungleich 0 liefert. In der Klammer kann ein Vergleich stehen, muß aber nicht. Daher führen (digitalRead(taster_unten) == true) und (digitalRead(taster_unten) == HIGH) und (digitalRead(taster_unten)) zu gleichen Ergebnissen.

Stromer:
3. if (rauf(true)) schritt = AN; das verstehe ich noch nicht ganz. Heist das die Variable rauf wird true, oder wenn die Variable true ist?

Die Funktion rauf() schaltet die LEDs je nach Parameter ein oder aus und liefert true zurück, wenn alle LEDs an oder aus sind, denn erst dann geht es zum nächsten Schritt.

Stromer:
Heisst zusammengefasst ist es richtig, wenn bei Switch/Case der Schritt NACH_OBEN_AN kommt wird ... das Licht eingeschaltet

Ja, kann man so sagen.

Nun etwas klarer?

agmue:
Mit Strg+t kannst Du den Text syntaktisch formatieren lassen, was die fehlende Klammer zu Tage bringt.

Bingo, die habe ich nicht gefunden obwohl ich noch nach so etwas gesucht habe :blush: Danke für den Typ mit Strg + t :grin:

agmue:
Ich habe eine sehr einfache Variante der Enumeration declaration verwendet. Ich habe den Zahlen Namen gegeben, könnte man sagen.

Ah okey, dann passiert das im enum und nicht byte.

Wie eine Funktion gestartet wird ist mir aber noch immer nicht ganz klar. Klar kann ich es einfach von dir kopieren aber ich würde es auch gerne verstehen.

Du hast geschrieben:

agmue:
Die Funktion rauf() schaltet die LEDs je nach Parameter ein oder aus und liefert true zurück, wenn alle LEDs an oder aus sind, denn erst dann geht es zum nächsten Schritt.

Das klingt so einfach aber die einzelnen Schritte verstehe ich noch nicht wirklich.

 case NACH_OBEN_AN:
    if (rauf(true)) schritt = AN;


bool rauf(bool an){
  if (an){

Ich denke mal etwas laut:
Wenn true zurück kommt geht es einen Schritt weiter, das ist mir klar. Aber wie die Funktion ausgelöst wird verstehe ich noch nicht wirklich. Im Switch Schritt steht ja eigentlich nur rauf(), das aktiviert dann so zu sagen unten bool rauf? was hat dort die Klammer (bool an) genau für eine Wirkung oder besser gesagt wo kommt das an her? Hat das einen Zusammenhang mit dem enum AN oder bin ich da auf dem Holzweg? Es muss ja unterschieden werden ob if(an) oder eben nicht an (also die else Funktion).

Naja ich muss das noch etwas überdenken.

Einen weiteren Fehler habe ich offenbar bei der Rückmeldung return false. Ich kann nämlich das Lauflicht einmal ein- und einmal ausschalten, dann geht nichts mehr. Ich nehme an das muss an der false Rückmeldung liegen?

bool rauf(bool an) {
  if (an) {
    for (int unten = 0; unten < LED_ANZAHL; unten++) {

      strip.setPixelColor(unten, strip.Color(0, 0, 0, HELLIGKEIT));
      strip.show();
      delay(GESCHWINDIGKEIT);
    }
    return true;
  }

  else {
    for (int unten = 0; unten < LED_ANZAHL; unten++) {

      strip.setPixelColor(unten, strip.Color(0, 0, 0, 0));
      strip.show();
      delay(GESCHWINDIGKEIT);
    }
    return false;
  }
}

Ich habe es schon an diversen Stellen der } versucht aber leider bis jetzt erfolglos

Stromer:
Ah okey, dann passiert das im enum

Bis dahin richtig :slight_smile:

Stromer:
Ah okey, dann passiert das im enum und nicht byte.

Das sind jetzt die bekannten Äpfel und Birnen:

enum: eine Funktionalität (nicht Funktion in C++), um Zahlen Namen zu geben. Wenn man es mit mehr Aufwand macht, bekommen die Zahlen auch einen Typ und es findet ene Typprüfung statt.

byte: Typ von Variablen und Konstanten, womit der Wertebereich festgelegt wird, bei byte 0 bis 255.

Stromer:
... aber ich würde es auch gerne verstehen.

Das wäre gut, weil es sich sozusagen um den Pfiff des Programms handelt, den man gut auch für andere Programme verwenden kann.

Alternativ könnte man das auch mit einem Merker machen, der von der Schrittkette gesetzt wird, die Schrittkette geht gleich zum nächsten Schritt. Wegen des Merkers wird die Treppenbeleuchtung geändert, bis diese Aktion abgeschlossen ist und der Merker zurückgesetzt werden kann.

Ich habe mir den Merker gespart, indem ich der Funktion einen Rückgabewert vom Typ bool spendiere:

bool rauf(bool an){}

Dadurch bleibt die Schrittkette in dem Schritt, bis true zurückkommt, das hast Du richtig erkannt.

Stromer:
Im Switch Schritt steht ja eigentlich nur rauf(), das aktiviert dann so zu sagen unten bool rauf?

Mit rauf(true) wird die Funktion rauf mit dem Parameter true aufgerufen. Die Funktion übernimmt den Parameter true in die Variable an, die den Typ bool hat. Ist die Animation noch aktiv, liefert sie mittels return false; ein false zurück, ist sie fertig mittels return true; ein true.

Die Bedingung if wertet den Rückgabewert aus und macht bei false nichts, bleibt also in dem Schritt, während bei true zum nächsten Schritt gewechselt wird.

Meine LEDs hängen gerade an einer anderen Hardware, testen kann ich daher erst später.