LED mit einem Taster Hoch und Runter Dimmen

Hallo Community,

ich beschäftige mich derzeit mit dem Thema dimmen von LED´s.

und habe auch schon einige skechte zu gefunden, wie zB. diesen hier was aber immer mit zwei Tastern gemacht wurde.

Ich frage mich nun ob es möglich ist eine LED mit einem Taster durch kurzes drücken ein und aus zu schalten und durch langes drücken stufenlos hoch zu dimmen bzw. runter zu dimmen und sollte der Taster vor dem erreichen der max helligkeit losgelassen werden soll diesen Zusand gespeichern werden ebenso beim runter dimmen.

Geht das und wie würde sich sowas realisieren lassen?

Gruß Joe

Geht das und wie würde sich sowas realisieren lassen?

Ja. Zustand und Zeit bei Pegeländerung merken, bei kurzer Zeit Zeit Zustand ändern bei langer Zeit Dimmwert hoch oder runterlaufen lassen.

Joe-muc: Geht das ...?

Ja, das geht.

Gruß

Gregor

Joe-muc:
wie würde sich sowas realisieren lassen?

Tasten entprellen
Einem endlichen Automaten bauen.

Also eine LED mit einem entprellten Taster ein und ausschalten bekomm ich hin

const int buttonPin = 10;    // Taster Pin
const int pwmledPin = 6;      // PWM LED Pin


int ledState = LOW;         // ledStatus startet Low
int buttonState;             
int lastButtonState = LOW;   // lastButton startet mit Low


unsigned long lastDebounceTime = 0;  
unsigned long debounceDelay = 20;    // debounce Zeit;

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(pwmledPin, OUTPUT);

  digitalWrite(pwmledPin, ledState);
}

void loop() {
  
  int reading = digitalRead(buttonPin);

  if (reading != lastButtonState) {
   
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    
    if (reading != buttonState) {
      buttonState = reading;

      if (buttonState == HIGH) {
        ledState = !ledState;
      }
    }
  }

 
  digitalWrite(pwmledPin, ledState);

  lastButtonState = reading;
}

Aber wie schaffe ich es das wenn beim eintasten der Taster gehalten wird die LED hoch dimmt wird und beim austasten dann runter?

zum Thema Faden hab ich schon mal das hier gefunden:

int brightness = 0;    
int fadeAmount = 5;

analogWrite(led, brightness);

  brightness = brightness + fadeAmount;

  if (brightness <= 0 || brightness >= 255) {
    fadeAmount = -fadeAmount;
  }

So würde ja automatisch in fünferschritten von 0 bis 255 und wieder runter gezählt.

Oder macht es mehr Sinn in vordefinierten schritten zu faden wie jurs es hier getan hat?

jurs:

const byte pwmTable[8]={0, 4, 8, 16, 32, 64, 128, 255}; // 8 Helligkeitsstufen

byte pwmIndex=0;

Zum Glück hat er nur gefragt ob's geht und nicht wie. ;) ;) ;)

uwefed: Zum Glück hat er nur gefragt ob's geht und nicht wie. ;) ;) ;)

Habe ich doch gerade ;-)

  1. Tastendruck kurz:
    Du entprellst den Taster (es reicht ein delay(10) nachdem er gedrückt wurde) und schaust solange der Taster gedrückt bleibt, ob die Zeit eines kurzen Drückens vorbei ist. Ist sie vorbei zählst Du die Helligkeit hinauf und dann hinunter. Eine kleine Verzögerung mit Millis macht daß das faden nicht zu schnell geht.
    Beim Loslassen das Tasters kontrollierst Du ob er kurz gedrückt (Anzeit kleiner als Kurzzeit max) war und macht dementsprechendes.

Grüße Uwe

uwefed: 1) Tastendruck kurz: Du entprellst den Taster (es reicht ein delay(10) nachdem er gedrückt wurde) und schaust solange der Taster gedrückt bleibt, ob die Zeit eines kurzen Drückens vorbei ist. Ist sie vorbei zählst Du die Helligkeit hinauf und dann hinunter. Eine kleine Verzögerung mit Millis macht daß das faden nicht zu schnell geht. Beim Loslassen das Tasters kontrollierst Du ob er kurz gedrückt (Anzeit kleiner als Kurzzeit max) war und macht dementsprechendes.

Grüße Uwe

Und genau da liegt noch der "Knackpunkt" für mich, ich verstehe wie du es meinst weiß aber leider noch nicht wie sich sowas umsetzten lässt! Würde mich freuen wenn du mir hierbei etwas auf die sprünge helfen könntest.

Gruß Joe

Würde mich freuen wenn du mir hierbei etwas auf die sprünge helfen könntest.

Eine Zustandstabelle malen. Alle Übergänge, zwischen den Zuständen definieren.

Damit hast du dein Programmgerüst!

Ablaufsteuerung

Meine Standardantwort zu Ablaufsteuerungen: Eine Stichworte Sammlung für Google Suchen: Endlicher Automat, State Machine, Multitasking, Coroutinen, Ablaufsteuerung, Schrittkette,

BlinkWithoutDelay,

Blink Without Delay

Der Wachmann

Multitasking Macros Intervall Macro

combie: Eine Zustandstabelle malen. Alle Übergänge, zwischen den Zuständen definieren.

Damit hast du dein Programmgerüst!

Ablaufsteuerung

Super, Dankeschön ;-)

Dann werde ich mal munter weiter googeln und versuchen das alles nachzuvollziehen und zu verstehen.

Eine Frage hätte ich gleich schon mal:

Bei der Endlichen Maschine hier

agmue: ``` // Ampel Belegung der Ausgaenge const byte RotPin = 2; const byte GelbPin = 3; const byte GruenPin = 4; // const boolean ein = HIGH; const boolean aus = LOW; // const int ZEITROTPHASE = 3000; const int ZEITGELBPHASE = 1000; unsigned long ampelMillis; unsigned long ampelIntervall; // enum ZUSTAENDE {ROT, ROTGELB, GRUEN, GELB}; byte zustand = ROT;

void setup() {   // Definiert die Pins als Ausgang   pinMode(RotPin, OUTPUT);   pinMode(GelbPin, OUTPUT);   pinMode(GruenPin, OUTPUT); }

void loop() {   // Ampelschaltung   if (millis() - ampelMillis >= ampelIntervall) {     switch (zustand) {       case ROT:         digitalWrite(RotPin, HIGH);         digitalWrite(GelbPin, LOW);         digitalWrite(GruenPin, LOW);         zustand = ROTGELB;         ampelMillis = millis();         ampelIntervall = ZEITROTPHASE;         break;       case ROTGELB:         digitalWrite(RotPin, HIGH);         digitalWrite(GelbPin, HIGH);         digitalWrite(GruenPin, LOW);         zustand = GRUEN;         ampelMillis = millis();         ampelIntervall = ZEITGELBPHASE;         break;       case GRUEN:         digitalWrite(RotPin, LOW);         digitalWrite(GelbPin, LOW);         digitalWrite(GruenPin, HIGH);         zustand = GELB;         ampelMillis = millis();         ampelIntervall = ZEITROTPHASE;         break;       case GELB:         digitalWrite(RotPin, LOW);         digitalWrite(GelbPin, HIGH);         digitalWrite(GruenPin, LOW);         zustand = ROT;         ampelMillis = millis();         ampelIntervall = ZEITGELBPHASE;         break;     }   } }

werden oben zwei constanten defieniert: const boolean ein = HIGH; const boolean aus = LOW;

Für was werden diese gebraucht, da ich sie im Programm nicht weiter finde?

Gruß Joe

Joe-muc: Eine Frage hätte ich gleich schon mal: Bei der Endlichen Maschine hier werden oben zwei constanten defieniert: const boolean ein = HIGH; const boolean aus = LOW; Für was werden diese gebraucht, da ich sie im Programm nicht weiter finde?

Wenn diese Variablen bzw. Konstanten im weiteren Programm nicht benutzt werden, kannst Du sie weglassen. Ich nehme an, dass sie dazu gedacht waren, sie statt HIGH und LOW zu verwenden.

Gruß

Gregor

Das sollte wohl der besseren Verständlichkeit dienen und wurde eben dann nicht benutzt. Und auch nicht raus gelöscht

Edit: Gregor war schneller

Ah Danke euch beiden.

Hatte mich nur gewundert, da ich immer versuchen neuen code Zeile für Zeile zu verstehen hatte es für mich einfachen keinen Sinn ergeben. Schön zu wissen das ich es also richtig verstanden hatte ;-))

So, es ist.

So bin schon mal etwas weiter gekommen
habe ein Programm von jurs gefunden mit dem ich taster auf verschiedene klicks abfragen kann.

// Tastenerkennung SHORTCLICK, DOUBLECLICK, LONGCLICK
#define INPUTMODE INPUT_PULLUP  // INPUT oder INPUT_PULLUP
#define PRELLZEIT 5             // Prellzeit in Millisekunden
#define SHORTCLICKTIME 250      // Längste Zeit für einen SHORTCLICK
#define DOUBLECLICKTIME 400     // Längste Zeit für den zweiten Klick beim DOUBLECLICK
#define LONGCLICKTIME 600       // Mindestzeit für einen LONGGLICK
byte buttonPins[]={2,3,A3}; // Arduino Pins (mehrere als Array möglich)
#define NUMBUTTONS sizeof(buttonPins)
byte buttonState[NUMBUTTONS];  // Aktueller Status des Buttons HIGH/LOW
enum {NONE, FIRSTDOWN, FIRSTUP, SHORTCLICK, DOUBLECLICK, LONGCLICK};
byte buttonResult[NUMBUTTONS]; // Aktueller Klickstatus der Buttons NONE/SHORTCLICK/LONGCLICK

void setup() {
  Serial.begin(9600);
  Serial.println("Button test");
  for (int i=0;i<NUMBUTTONS;i++) pinMode(buttonPins[i],INPUTMODE);
}

boolean eingabe()
// Rückgabewert false ==> Prellzeit läuft, Taster wurden nicht abgefragt
// Rückgabewert true ==> Taster wurden abgefragt und Status gesetzt
{
  static unsigned long lastRunTime;
  static unsigned long buttonDownTime[NUMBUTTONS];
  unsigned long now=millis();
  if (now-lastRunTime<PRELLZEIT) return false; // Prellzeit läuft noch
  lastRunTime=now;
  for (int i=0;i<NUMBUTTONS;i++)
  {
    byte curState=digitalRead(buttonPins[i]);
    if (INPUTMODE==INPUT) curState=!curState; // Vertauschte Logik bei INPPUT_PULLUP
    if (buttonResult[i]>=SHORTCLICK) buttonResult[i]=NONE; // Letztes buttonResult löschen
    if (curState!=buttonState[i]) // Flankenwechsel am Button festgestellt
    {
      if (curState)   // Taster wird gedrückt, Zeit merken
      {
        if (buttonResult[i]==FIRSTUP && now-buttonDownTime[i]<DOUBLECLICKTIME)
          buttonResult[i]=DOUBLECLICK;
        else
        {  
          buttonDownTime[i]=now;
          buttonResult[i]=FIRSTDOWN;
        }
      }
      else  // Taster wird losgelassen
      {
        if (buttonResult[i]==FIRSTDOWN) buttonResult[i]=FIRSTUP;
        if (now-buttonDownTime[i]>=LONGCLICKTIME) buttonResult[i]=LONGCLICK;
      }
    }
    else // kein Flankenwechsel, Up/Down Status ist unverändert
    {
      if (buttonResult[i]==FIRSTUP && now-buttonDownTime[i]>DOUBLECLICKTIME)
        buttonResult[i]=SHORTCLICK;
    }
    buttonState[i]=curState;
  } // for
  return true;
}


void verarbeitung()
{
  // dummy function
}


void ausgabe()
// Klickstatus von geklickten Buttons mit Zeitstempel ausgeben
{
  for (int i=0;i<NUMBUTTONS;i++)
  {
    if (buttonResult[i]>=SHORTCLICK) // Ein Button wurde geklickt
    {
      Serial.print(millis()/1000.0,3); // Zeitstempel mit Millisekundenauflösung
      Serial.print("\tPin-");Serial.print(buttonPins[i]);
      if (buttonResult[i]==SHORTCLICK) Serial.println(" CLICK");
      else if(buttonResult[i]==DOUBLECLICK) Serial.println(" DOUBLE CLICK");
      else if(buttonResult[i]==LONGCLICK) Serial.println(" LONG CLICK");
    }
  }
}

void benchmark() {
  static unsigned long counterStartTime;
  static unsigned long counter;
  counter++;
  if (counter>=1000000L)
  {
    Serial.print("Average Time per loop(): ");
    Serial.print((micros()-counterStartTime)/1000000.0);
    Serial.println(" microseconds");
    counter=0;
    counterStartTime=micros();
  }
}


void loop() {
  if (eingabe())
  {
    verarbeitung();
    ausgabe();
  }
  benchmark();
}

Nun zu meiner Frage. Hier im Code wird ja der Longklick ja erst dazu wenn er geklickt wurde, eine gewisse Zeit vorüber ist und er wieder losgelassen wird.

Für mein Vorhaben muss ich doch aber einen kurzen klick zum Ein und Ausschalten erkennen und sollte der Taster länger gedrückt bleiben soll er ja rauf und runter Dimmen!

Das heißt wenn ich einen shortklich erkenne kann ich damit schalten und wenn ButtonDown länger als Defenierte Zeit bestehen bleibt zum dimmen nutzen. Lieg ich da richtig oder bin ich da auf dem Holzweg??

Gruß Joe

Das heißt wenn ich einen shortklich erkenne kann ich damit schalten und wenn ButtonDown länger als Defenierte Zeit bestehen bleibt zum dimmen nutzen. Lieg ich da richtig oder bin ich da auf dem Holzweg??

Wenn du für deine Applikation einen ShortClick und einen LongClick brauchst. Dann musst du deine Taster so auswerten, dass dir diese Werte generiert werden.

Also nein, dann bist du nicht auf einem Holzweg.

Tipp: Male einen Zeitstrahl auf Papier

Male einen Tastendruck auf den Strahl. Mit prellen beim drücken und prellen beim los lassen Male den ShortClick und den LongClick da rein.

Das ist die erste Grundlage für deinen endlichen Automaten.

Das Bild reicht, um die nötigen Zustände zu definieren. Auch um die nötigen Zustandsänderungen darin zu erkennen.

Baue einen zweiten Automaten für die Lichtsteuerung.

Halte die Kommunikation, zwischen den Automaten, einfach.

Danke combie für die guten tipps...

aber sowas in der Art hatte ich schon gemacht ;-))

mit der Tasterabfrage komme ich derweilen schon gut vorran, bin nun am überlegen was mehr Sinn für das dimmen macht am PWM von 0 auf 255 in fünfer oder 10 Schritten zählen lassen oder in vorher definierten schritten zB. 4, 8, 16, 32 64, 128, 255

Was macht da eurer Mrinung mehr Sinn?

Da sowohl die LED, als auch das Auge nichtlinear sind, sollte man die Werte daran anpassen. Eine Aufstellung findest Du hier.

Gruß Tommy

ardubu:
Zustand und Zeit bei Pegeländerung merken, bei kurzer Zeit Zeit Zustand ändern bei langer Zeit Dimmwert hoch oder runterlaufen lassen.

#define EIN 2
#define DIM 9
#define DRUECKZEIT 300
uint32_t aktMillis, prellMillis, tasterMillis, dimMillis, intervall;
int dimWert, dimDelta = 5;
bool aktTaster, altTaster, pruef, dimmen = true;

void setup() {
  Serial.begin(9600);
  Serial.println("Start");
  pinMode(EIN, INPUT_PULLUP);  // gedrückt ist LOW
}

void dim() {
  dimWert += dimDelta;
  if (dimWert > 255) {
    dimWert = 255;
    dimDelta = -dimDelta;
  }
  if (dimWert < 0) {
    dimWert = 0;
    dimDelta = -dimDelta;
  }
  analogWrite(DIM, dimWert);
}

void loop() {
  aktMillis = millis();
  altTaster = aktTaster;
  if (aktMillis - prellMillis >= 30) {
    prellMillis = aktMillis;
    aktTaster = digitalRead(EIN);
  }
  if (!aktTaster && altTaster) {
    tasterMillis = aktMillis;
    pruef = true;
  }
  if (pruef && aktMillis - tasterMillis >= DRUECKZEIT) {
    pruef = false;
    if (aktTaster) {
      if (dimWert < 11) {
        dimWert = 255;
      } else {
        dimWert = 0;
      }
      analogWrite(DIM, dimWert);
      dimmen = false;
    } else {
      dimmen = true;
    }
  }
  if (dimmen && aktTaster && !altTaster) {
    dimmen = false;
  }
  if (dimmen && aktMillis - dimMillis >= 30) {
    dimMillis = aktMillis;
    dim();
  }
}

combie:
Das ist die erste Grundlage für deinen endlichen Automaten.

#define EIN 2  // Tasterpin
#define DIM 9  // LED-Pin
#define ENTPRELLZEIT 30
#define DRUECKZEIT 300
#define DIMVERZOEGERUNG 100
const byte pwmtable[] PROGMEM = {0, 2, 3, 4, 6, 8, 11, 16, 23, 32, 45, 64, 90, 128, 181, 255};
const byte dimElemente = sizeof(pwmtable);  // Anzahl Elemente der Tabelle
enum {WARTEN, ZEIT, DIMMEN};  // mögliche Zustände des endlichen Automaten
byte zustand = WARTEN;
uint32_t aktMillis,   // Zeit seit Reset in ms
prellMillis,   // relative Entprellzeit in ms
tasterMillis,   // relative Zeit für Tastendruck in ms
dimMillis;   // relative Zeit zum Dimmen in ms
int dimWert,   // Wert für die LED; mit Vorzeichen, damit auch -1 möglich ist
dimIndex; // Feldindex
bool rauf = true, // Dimmrichtung
aktTaster,  // aktueller Zustand Taster
altTaster; // alter Zustand Taster

void setup() {
  pinMode(EIN, INPUT_PULLUP);  // gedrückt ist LOW
}

void dim() {
  if (rauf) {
    dimIndex++;
  } else {
    dimIndex--;
  }
  if (dimIndex >= dimElemente) {
    dimIndex = dimElemente - 2;
    rauf = false; // Richtung ändern
  }
  if (dimIndex < 0) {
    dimIndex = 1;
    rauf = true; // Richtung ändern
  }
  dimWert = pgm_read_byte(&pwmtable[dimIndex]); // Wert aus der Tabelle ausgeben
  analogWrite(DIM, dimWert);
}

void loop() {
  aktMillis = millis(); // eindeutig für diese Schleife
  altTaster = aktTaster; 
  if (aktMillis - prellMillis >= ENTPRELLZEIT) { // Taster nur gelegentlich lesen, dadurch entprellen
    prellMillis = aktMillis;
    aktTaster = digitalRead(EIN);
  }
  switch (zustand) { // endlicher Automat
    case WARTEN:
      if (!aktTaster && altTaster) { // wenn Taster gedrückt (Flanke)
        tasterMillis = aktMillis; // Stoppuhr starten
        zustand = ZEIT;
      }
      break;
    case ZEIT:
      if (aktMillis - tasterMillis >= DRUECKZEIT) {
        zustand = DIMMEN; // nach der DRUECKZEIT, also langer Druck
      }
      if (aktTaster) { // vor der DRUECKZEIT, also kurzer Druck
        if (dimWert < 127) {
          dimWert = 255; // AN
        } else {
          dimWert = 0; // AUS
        }
        analogWrite(DIM, dimWert);
        zustand = WARTEN;
      }
      break;
    case DIMMEN:
      if (aktTaster) { // wenn Taster Losgelassen, dann Dimmwert beibehalten
        zustand = WARTEN;
      }
      if (aktMillis - dimMillis >= DIMVERZOEGERUNG) { // nächster Dimmschritt
        dimMillis = aktMillis;
        dim();
      }
  }
}

Endliche Automaten gefallen mir :slight_smile: