[Arduino] Eingang debouncen

scooter010

Commander
Registriert
Sep. 2014
Beiträge
2.539
Moin!

Mein Projekt sieht folgendermaßen aus:
Indirekte Raumbeleuchtung mittels eines zweifarbigen LED-Streifens (WW + KW). Aufgrund der Leistungsangaben des LED-Streifen und der Raumdimensionen benötige ich in Summe 4 "Einspeisepunkte", womit meine dann 8 LED-Streifen-Abschnitte jeweils unter 3m Länge bleiben.

Zur Spannungsversorgung stehen 2 24V 240W LED-Netzteile zur Verfügung.
Als Controller sollen zwei Shelly RGBW2 dienen, die jeweils max ~200W@24V schalten können.
Als Bedienelement ist (erstmal nur) ein gewöhnlicher UP-Taster vorgesehen, der 230V an den dem Shelly beigelegten und nicht dokumentiert 230V Optokoppler gibt, dessen Ausgang am Eingang vom Shelly erfasst wird.
Der Taster soll 4 Lichtszenen ("program") im Shelly durch schalten:
  1. Heimkinohintergrundbeleuchtung
  2. Warmweißes Licht, hell genug zum Essen, aber dunkel und warm genug, damit es gemütlich ist
  3. neutralweißes Licht, ziemlich hell, für Hausaufgaben oder andere Tätigkeiten ausreichend Licht zu haben
  4. Aus (auch bei 1s Dauerdruck des Tasters)

Genug zum Vorgeplänkel, nun zum Problem
Ich bin nicht in der Lage, meinen Code zum Abfragen des Einganges so zu schreiben, dass der Eingang korrekt ausgewertet wird.
Ich habe meine mittlerweile raus bekommen zu haben, dass der Ausgang des Optokopplers ein fotosensitiver Widerstand ist, der durch zwei LED (je eine pro Stromrichtung beim Wechselstrom) angeleuchtet wird. Somit habe ich am Ausgang ein mit ca. 100Hz "flackernder" Wiederstand, wenn der Taster betätigt wird. Soweit zumindest meine Annahme.


C:
#define number_of_programs 4
#define ww int(255)       // warm-white
#define cw int(0)            // cold-white
#define nw int(127)        // neutral-white

const int LED[number_of_programs * 2] =
{
   nw,    0,
   nw+2,    5,
   ww-20, 100,
   nw,    200
};

#define SLED 2
#define ledPin_G 15     // Cold2
#define ledPin_R 12     // Warm2

#define ledPin_B 14     // Cold1
#define ledPin_W 4      // Warm1

#define adc A0        // Ampere-Metering-input
#define inp 5           // Switch Input

#define bla 13          // Build-in LED

byte program = 0;         // Status
unsigned long time_pushed = 0;
int count = 0;
int last_low = 0;

/**
 * Writes both CCT-Outputs to PWM
 *
 * @param color Color-Temperature [0 - 255] (cold ... warm)
 * @param bright Brightness [0 - 255]
 */
void cct (int color, int bright) {
  analogWrite(ledPin_B, ((255 - color) * bright)/255 );
  analogWrite(ledPin_W, (color * bright)/255 );
  analogWrite(ledPin_G, ((255 - color) * bright)/255 );
  analogWrite(ledPin_R, (color * bright)/255 );
}


void setup() {
  // LED off
  pinMode(SLED, OUTPUT);           // Status-LED
  digitalWrite(SLED, LOW);        // Status-LED off
  pinMode(bla, INPUT);

  pinMode(adc, INPUT);
  pinMode(inp, INPUT);         
}


void loop() {

  while ( digitalRead(inp) == LOW ) {
    int timestamp = millis();
    if ( (timestamp - last_low) % 10 == 0  || count == 0  ) {
      last_low = timestamp;
      count++;
      delay(1);
    }
  
  }
  /*if (digitalRead(inp) == LOW && ( timestamp - last_low > 5  || count == 0 ) ) {
    last_low = timestamp;
    count++;
    delay(8);
  
  }*/

  if ( count > 1 && count < 100 ) // 1000 ms / s, 100 Hz 
  {
    if(program < (number_of_programs - 1))
    {
      program++;
      count = 0;
    }else{
      program = 0;
      count = 0;
    }
  }
  else
    if( count >= 100 )
    {
      program = 0;
      count = 0;
    }
 
  cct(LED[program * 2], LED[(program * 2) + 1]);
 
}

Obiger Code verursacht folgendes Verhalten:
Lichtszenen werden durchgeschaltet.
Der erste Tastendruck, der länger als ~1s vom vorherigen her ist, bewirkt aus.
Ansonsten funktioniert der Code ganz gut, WENN ich den Eingang des Shelly mit einem 1yF Kondensator puffere.
Jemand eine Idee, wie ich das ganze Verbessern könnte?

Edit: Es geht mir weniger darum, den Kondensator weg zu bekommen, sondern darum, meinen Logikfehler zu entfernen, der nach 1s nichts machen immer erst mal abschaltet. Nach 1s gedrückt halten soll er aus machen, nicht nach 1s nicht drücken und dann drücken :)
 
Zuletzt bearbeitet:
Habe ich bereits versucht. Aber ich habe binnen 3h (von 21-00 Uhr...) irgendwann so viel verusucht, dass ich es vielleicht nochmal in Ruhe probieren sollte. INPUT_PULLUP ist eine Funktion vom ESP 8266 und nicht vom Board, oder? Ich will nur sicherstellen, dass der Shelly das auch unterstützt.
 
@scooter010 Vom Arduino ist das ne Funktion. Flashst du den Arduino-Code auf einen ESP? Keine Ahnung, ob das geht. Kann auch sein, dass dieser interne Widerstand nicht so gut ist oder genau in die falsche Richtung geht.
In dem Fall probier am besten mal mit Hardware pull-ups/-down rum, wie im zweiten Link.
 
input_pullup funktioniert nicht. Der Eingang reagiert dann garnicht.
Aber das ist auch nicht mein Problem, der Eingang "flackert" nicht weil er auf kein Potenzial gezogen wird, er flackert, weil er nunmal per Optokoppler angesteuert wird. das Shelly das selbst in Software ganz ohne pull up und Kondensatoren hin bekommt, dachte ich, muss dass auch voll in Software lösbar sein.
 
scooter010 schrieb:
er flackert, weil er nunmal per Optokoppler angesteuert wird
Je nach Optokoppler kann das schon am fehlenden Potenzial liegen. Ich hab solche Widerstände auch schon im Zusammenhang mit Optokopplern nutzen müssen.

scooter010 schrieb:
input_pullup funktioniert nicht. Der Eingang reagiert dann garnicht.
Dann geht der interne in genau die falsche Richtung (VCC, wenn ich mich nicht irre). Würde es dann mal in Hardware gegen GND probieren.
 
Also, weitere Recherche hat ergeben, dass der Eingang immer an einem 3,3V Pull-Up hängt. Der Eingang wird durch den Optokoppler gepulsed (ca. netzfrequenz) auf GND gezogen.

Es blieb also alleinig die programmiertechnische Frage, die ich nun wie folgt gelöst habe:

C:
#define number_of_programs 4
#define time_debounce 100
#define time_off 1000
#define ww int(255)        // warm-white
#define cw int(0)          // cold-white
#define nw int(127)        // neutral-white

const int LED[number_of_programs * 2] =
{
   nw,    0,
   nw+2,    5,
   ww-20, 100,
   nw,    200
};

#define SLED 2
#define ledPin_G 15     // Cold2
#define ledPin_R 12     // Warm2

#define ledPin_B 14     // Cold1
#define ledPin_W 4      // Warm1

#define adc A0          // Ampere-Metering-input
#define inp 5           // Switch Input

#define bla 13          // Build-in LED

byte program = 0;         // Status
unsigned long dura = 0;
unsigned long last_pulse = 0;
int count = 0;



/**
 * Writes both CCT-Outputs to PWM
 *
 * @param color Color-Temperature [0 - 255] (cold ... warm)
 * @param bright Brightness [0 - 255]
 */
void cct (int color, int bright) {
  analogWrite(ledPin_B, ((255 - color) * bright)/255 );
  analogWrite(ledPin_W, (color * bright)/255 );
  analogWrite(ledPin_G, ((255 - color) * bright)/255 );
  analogWrite(ledPin_R, (color * bright)/255 );
}


void setup() {
  // LED off
  pinMode(SLED, OUTPUT);           // Status-LED
  digitalWrite(SLED, HIGH);        // Status-LED off
  pinMode(bla, INPUT);

  pinMode(adc, INPUT);
  pinMode (inp, INPUT);
             

}


void loop() {
 
  //sensorVal = analogRead(adc);      // TODO: implement power-metering after implementing mqtt
 
 
  if ( pulseIn(inp, LOW, 30000) ) {
    count++;
    dura = 0;
    last_pulse = millis();
  }

  if ( last_pulse - millis() > 60 ) {

    if ( count > 1 && count < 33 )
   
      if(program < (number_of_programs - 1))
     
        program++;
       
      else
     
        program = 0;
     
   
    else
      if( count >= 33 )
      {
        program = 0;
      }
     
    count = 0;
    last_pulse = 0;
  }
 
  cct(LED[program * 2], LED[(program * 2) + 1]);

}

Credits dafür an Tasmota welche mich auf pulseIn() brachten.
Auch der Kondensator ist nicht mehr notwendig.
 
@scooter010

Ein paar Anmerkungen zum Code:

  • Räume deine Loop auf, also versuche diese Anweisung auch nochmal in eine Funktion zu gliedern.
  • Wenn du einen 8-Bit Wert speichern willst, würde ich anstatt int etwas wie (u)int8_t schreiben, wenn du einen 16-Bit wert speichern willst : (u)int16_t. Weil du dann immer weißt, welcher Typ verwendet wird. Arduino macht teilweise komische Dinge bei den Datentypen
  • Anstatt vieler IFs, würde ich dir einen Blick auf Switch-Case empfehlen.
  • Auf welcher Hardware läuft dein Code?
  • Wie ist das timing, vom Ablauf her?
Wie gesagt, alles nur als Tipp gemeint ;)
 
Zuletzt bearbeitet:
Achso nochwas:

Du solltest evtl. Dir auch mal interrupts angucken, das würde einiges nochmal vereinfachen ;)

Wie gesagt, alles nur gut gemeint :)
 
Zurück
Oben