Problemi con Toggle Debounce e Fade strip led

Ciao a tutti, sono agli inizi e ho qualche difficoltà (penso banali per la maggior parte di voi) nel realizzare uno sketch per comandare delle strip led che funzioni nel seguente modo:

  • da led spento premo il pulsante e il led si accende ad una determinata intensità (PWM)
  • da led acceso premo il pulsante il led si spegne

Fino a qui anche un neofita come me (scopiazzando qua e la) non ha avuto problemi, il passo seguente non mi riesce nonostante svariati tentativi (millis()):

  • Se il led è acceso e ripremo il tasto per un tempo > di 2000 ms, il led comincia a variare d'intensità, attenuando e accentuando la sua intensità, finche non viene rilasciato il pulsante, fermandosi al livello d'intensità presente in quel preciso istante.

Di seguito quello che ho fatto (con non poca fatica) fin'ora:

#define pulsantePin 2  //INPUT PULSANTE
#define ledPin 9  //OUTPUT LED
#define debounce 200  //Tempo di rimbalzo
int ledStato = LOW;  //Stato del pin OUTPUT LED
int reading;  //Lettura corrente del pin INPUT PULSANTE
int pulsanteStato = LOW;  //Lettura precedente del pin INPUT PULSANTE
long time = 0;  //Ultimo tempo in cui il pin di OUTPUT è stato attivato

void setup()
{
     pinMode(pulsantePin, INPUT);  //Imposto il pulsante come INPUT
     pinMode(ledPin, OUTPUT);  //Imposto il LED come OUTPUT
     digitalWrite(ledPin, ledStato);
}

void loop()
{
     reading = digitalRead(pulsantePin);  //Leggo il valore in ingresso dato dal pulsante

     //Se abbiamo premuto in pulsante (HIGH) e la volta prima il suo stato
     //era LOW ed è trascorso il tempo necessario
     if (millis() - time > debounce) {

 
         //Inverte l'OUTPUT
        if(ledStato == LOW and reading == HIGH){
              ledStato = HIGH;
              analogWrite(ledPin, 255);
          }
            else if(ledStato == HIGH and reading == HIGH){
              ledStato = LOW;
              digitalWrite(ledPin, LOW);
         }
         //Ricorda quando l'ultima volta è stato premuto il pulsante  
         time = millis();
     }
pulsanteStato = reading;
  delay(10);
}

Anche il debounce sembra funzionare correttamente.
Molte Grazie a chi vorrà darmi qualche indicazione.

Buongiorno e buona vigilia a tutti, leggendo nelle varie discussioni ho trovato QUESTA, dove l'ottimo nid69ita nel suo post ha scritto dell'esistenza di alcune librerie di pushbutton. Io ho provato quella di marcobrianza che fa egregiamente quasi quello che voglio. Non riesco però a capire dove viene settato il tempo di pressione del tasto (1000ms) per far iniziare il fade. Mi sarebbe anche piaciuto che l'on della strip led accendesse la strip led ad un determinato valore di brightness (p.e. 200), invece il programma svolge un toggle mantenendo l'ultimo valore di brightness memorizzato. Cmq provo a studiare un pò il programma per capire meglio dove mettere mano.
Secondo voi posso, con lo stesso arduino nano gestire 4 punti luce diversi? :cold_sweat:

/* ClickButton LED fader demo
 LED on/off and fading up/down via one button
 
 Short press turns LED on or off.
 Long press (over one second) fades the LED and changes direction for each press
 The circuit:
 - LED attached from pin 10 to resistor (say 220-ish ohms), other side of resistor to GND (ground)
 - Pushbutton attached from pin 4 to GND
 No pullup resistor needed, using the Arduino's (Atmega's) internal pullup resistor in this example.
 
 2013.02.17 - raron
*/

#include "ClickButton.h"


// the LED
const int ledPin = 10;
int ledState = 0;

// the Button
const int buttonPin1 = 4;
ClickButton button1(buttonPin1, LOW, CLICKBTN_PULLUP);

// Fade variables
int fadeValue = 64;
boolean fadeUp = false;    // false means fade down
boolean oldFadeUp = fadeUp;
const long fadeDelay = 10; // Time in milliseconds between fade steps
long adjustFaderTime = 0;  // Time to adjust the fader

// other
long currentTime;
int function = 0;


void setup()
{
  pinMode(ledPin,OUTPUT);  
}


void loop()
{
  currentTime = (long)millis();

  button1.Update();

  if (button1.clicks != 0) function = button1.clicks;
  
  // Toggle LED on single clicks
  if(button1.clicks == 1) ledState = !ledState;

  // fade if button is held down during single-click
  if(function == -1 && button1.depressed == true)
  {
    ledState = true;  // force lights on, since we want to fade it up or down
    
    if (oldFadeUp == fadeUp) fadeUp = !fadeUp; // Switch direction
    
    if ( currentTime - adjustFaderTime > fadeDelay)
    {
      adjustFaderTime = currentTime + fadeDelay;
      if (fadeUp) fadeValue++; else fadeValue--;

      // Some boundary checking
      // Using signed ints, we can check for below 0 and above 255 (byte limit)
      if (fadeValue > 255) fadeValue = 255;
      if (fadeValue < 0)   fadeValue = 0;
      
    }

  } else {
    // Save old fade direction for next time
    oldFadeUp = fadeUp;
    // Reset function
    function = 0;
  }
  

  // update the LED
  if (ledState) analogWrite(ledPin,fadeValue); else analogWrite(ledPin, 0);
}

A che serve quella libreria?... Solo a vedere se il pulsante è premuto???...!!!
Ah, no, rileva pressioni corte o lunghe. Serve solo per non imparare a farlo da soli. Tutto il resto sta nel programma. :frowning:
P.s.: l'antirimbalzo si fa con condensatore da 100~220nF in parallelo al pulsante.

Sì, puoi gestire 4 uscite PWM. Devi usare 4 pulsanti, 4 variabili bool che memorizzano lo stato up/down per ciascuna uscita e 4 variabili byte che conservano i valori PWM. Puoi anche memorizzare in EEPROM il valore di ciascuna uscita PWM (fallo dopo 2 secondi dal rilascio del pulsante per non memorizzare inutilmente ogni variazione del valore, perché usurerebbe inutilmente la EEPROM).

Datman:
A che serve quella libreria?... Solo a vedere se il pulsante è premuto???...!!!
Ah, no, rileva pressioni corte o lunghe. Serve solo per non imparare a farlo da soli. Tutto il resto sta nel programma. :frowning:
P.s.: l'antirimbalzo si fa con condensatore da 100~220nF in parallelo al pulsante.

Sì, puoi gestire 4 uscite PWM. Devi usare 4 pulsanti, 4 variabili bool che memorizzano lo stato up/down per ciascuna uscita e 4 variabili byte che conservano i valori PWM. Puoi anche memorizzare in EEPROM il valore di ciascuna uscita PWM (fallo dopo 2 secondi dal rilascio del pulsante per non memorizzare inutilmente ogni variazione del valore, perché usurerebbe inutilmente la EEPROM).

Molte grazie per la risposta
Mi rendo conto che per molti di voi sono cose banali, ti assicuro che ci sto mettendo veramente impegno per cercare di capire il sistema, tieni presente che le poche esperienze a livello di programmazione le ho fatte usando linguaggio ladder su PLC PILZ. Sicuramente mi piacerebbe di più usare un sistema fatto da me, piuttosto di piluccare cose fatte da altri.
Considerando che questo con questo sistema vorrei pilotare 4 punti luce in casa, posso mettere il condensatori anche in un circuito domestico? Usare i 9V per alimentare sia il nano che per il circuito pulsanti è fattibile? Inoltre, mentre con un pulsante sto regolando il fade in sala, se prova ad accendere la luce in camera si accende/ regola?

In questo forum, come da regolamento, non si può parlare di circuiti funzionanti a tensione di rete.
Comunque, per usare lampadine a corrente alternata, diciamo a 24V, bisogna fare diversamente.
Per le luci di casa, chiedi a un elettricista di montare dei regolatori a tocco o a pulsante, a cui possono anche essere collegati altri pulsanti come estensione.

nero773:
... vorrei pilotare 4 punti luce in casa ...

... ti ricordo il REGOLAMENTO, punto 15 e suoi sottopunti.

Qui si parla solo di bassissima tensione, altrimenti ... il thread viene chiuso. :confused:

Guglielmo

Datman:
In questo forum, come da regolamento, non si può parlare di circuiti funzionanti a tensione di rete.
Comunque, per usare lampadine a corrente alternata, diciamo a 24V, bisogna fare diversamente.
Per le luci di casa, chiedi a un elettricista di montare dei regolatori a tocco o a pulsante, a cui possono anche essere collegati altri pulsanti come estensione.

Come già chiarito più e più volte in precedenza, si tratta di 4 strip led a 12Vdc pilotate in PWM tramite MOSFET.
Ho già fatto 2 anni fa un circuito con strip led 12Vdc + dimmer con telecomando e alimentatore 230Vac/12Vdc. Ora vorrei fare altri punti luce simili, ma tutti gestiti da Arduino. Non comprendo questo astio nei miei confronti. Scrivo senza presunzione e gentilmente chiedendo un aiuto, sono palesemente inesperto ma con voglia di imparare (coi miei limiti). Se non piacciono o infastidiscono le mie domande basta passare oltre senza rispondere senza dover essere per forza supponenti o scortesi.

nero773:
Non comprendo questo astio nei miei confronti.

Non c'è alcun astio e .. se avessi letto bene quanto ti ho indicato (specie il punto 17.1), avresti capito perché siamo MOLTO attenti a certe discussioni. Dato che "prevenire e sempre meglio che curare", come qualcuno di noi sente odore di tensioni oltra la "bassissima tensione", drizza le antenne e avverte l'utente che di tali cose NON si può parlare.

Quindi, se le luci sono 12/24V ... nessun problema, ma dato che non era specificato ... meglio avvertire :smiley:

Guglielmo

gpb01:
Non c'è alcun astio e .. se avessi letto bene quanto ti ho indicato (specie il punto 17.1), avresti capito perché siamo MOLTO attenti a certe discussioni. Dato che "prevenire e sempre meglio che curare", come qualcuno di noi sente odore di tensioni oltra la "bassissima tensione", drizza le antenne e avverte l'utente che di tali cose NON si può parlare.

Quindi, se le luci sono 12/24V ... nessun problema, ma dato che non era specificato ... meglio avvertire :smiley:

Guglielmo

Il punto 17 l'ho letto ieri con attenzione, é un pericolo a cui non avevo pensato ma é totalmente condivisibile. Invece, per quello che mi riguarda sono molto meticoloso ed attento ai pericoli, per lavoro ho a che fare con tensioni fino a 1000Vdc e potenze fino a qualche MW, ti assicuro che sto molto attento (mi è anche capitato di vedere un morto per elettrocuzione e non é bello).
Per il mio progetto, in questo istante, sto provando da ore (lo farò fino a domattina) a pilotare una strip led da 5A 12Vdc da 0 a max a ciclo continuo per vedere se il MOSFET scalda troppo. Sto molto attento a tutto ciò che può causare un pericolo.
La domanda fatta per il circuito pulsanti (con eventuale condensatore per denunce) di casa era per capire se le resistenze in gioco in un circuito domestico possa causare problemi ai 9 Vdc degli ingressi digitali dell'arduino.

nero773:
...Mi sarebbe anche piaciuto che l'on della strip led accendesse la strip led ad un determinato valore di brightness (p.e. 200), invece il programma svolge un toggle mantenendo l'ultimo valore di brightness memorizzato. ...

su questo specifico punto provo a buttare li un suggerimento (prendilo per quello che vale perche' non sono un programmatore) ... se tu nel momento in cui spegni la strip (subito dopo lo spegnimento) impostassi la variabile che contiene la luminosita' a 200, non ti si accenderebbe come vuoi la volta successiva ? ... :wink:

nero773:
Usare i 9V per alimentare sia il nano che per il circuito pulsanti è fattibile?

Si, ma ai terminali di Arduino (direttamente collegati al micro sulla scheda) non devono arrivare più di 5 (o 3,3 se è un Arduino funzionante a 3,3V).

Inoltre, mentre con un pulsante sto regolando il fade in sala, se prova ad accendere la luce in camera si accende/ regola?

Questo dipende esclusivamente da come è progettata la logica del programma (flusso di esecuzione delle istruzioni). È perfettamente possibile avere (anche decine di) comandi/azioni contemporanee gestite in parallelo... dove per parallelo si intende, in termini PLC, all'interno dello stesso ciclo di scansione, che qui è un ciclo di loop.

A margine: tutte le operazioni a livello software che fai con un PLC, le fai anche in C con Arduino. Il ladder è un piccolo sottoinsieme specifico e specializzato di tutte le operazioni che si possono fare con un linguaggio completamente general purpose.

Etemenanki:
su questo specifico punto provo a buttare li un suggerimento ...

Grazie. Ottima idea, stasera provo a metterla in pratica. Gentilissimo!

Claudio_FF:
Si, ma ai terminali di Arduino (direttamente collegati al micro sulla scheda) ...

Grazie per le preziose informazioni.
In effetti pensavo di mettere tutto in un unico loop, ma il fatto é che l'arduino nano esegue le operazioni in modo sequenziale, quindi il dubbio é che il processore non è in grado di gestire altre richieste mentre (p.e.) sta regolando il fade tramite un altro pulsante premuto.
Per il circuito invece, io penso di alimentare l'arduino nano tramite un regolatore di tensione 12/9 ai PIN Vin e GNDin che dovrebbe accettare tensioni fino a 20V. Pensavo di usare i positivo in uscita dal regolatore per mandarlo ai pulsanti, i ritorni agli ingressi dell'arduino.

>nero773: Quando si quota un post, NON è necessario riportarlo (inutilmente) tutto; bastano poche righe per far capire di cosa si parla ed a cosa ci si riferisce, inoltre, se si risponde al post immediatamente precedente, normalmente NON è necessario alcun "quote" dato che è sottinteso. :slight_smile:

Gli utenti da device "mobile" (piccoli schermi) ringrazieranno per la cortesia :wink:

Guglielmo

P.S.: Ho troncato io i "quote" dei tuoi post qui sopra :wink:

nero773:
In effetti pensavo di mettere tutto in un unico loop

La funzione loop è già il loop principale. Non dovrebbero servirne altri. Poi se scrivere tutto li dentro o separare le parti in diverse funzioni richiamate dalla loop è solo una scelta implementativa, di chiarezza, comodità ecc.

arduino nano esegue le operazioni in modo sequenziale, quindi il dubbio é che il processore non è in grado di gestire altre richieste mentre (p.e.) sta regolando il fade tramite un altro pulsante premuto.

Anche il ladder viene eseguito in modo sequenziale, eppure si possono controllare più cose indipendentemente, compreso realizzare sequenze con tempi ben precisi che non interferiscono l'una con l'altra. Ogni rung viene valutato molto velocemente e alla fine l'intero ciclo di scansione richiede pochissimi millisecondi. Su Arduino basta progettare il programma con lo stesso criterio, una serie di 'if' da valutare ed eseguire velocemente se veri. Se c'è bisogno di realizzare processi che richiedono un certo tempo (ad esempio con pause), questo si crea attraverso l'esecuzione ripetuta di molti passaggi (cicli di loop) singolarmente velocissimi. E il tutto si coordina attraverso variabili che abilitano/disabilitano le varie condizioni e tengono nota dello stato corrente di funzionamento.

Si tratta solo di formalizzare la logica discorsiva del primo post in una forma più... formale :slight_smile:
Ad esempio:

SE faseLogica spento

    SE clic
        luminosita=200
        faseLogica=acceso

ALTRIMENTI SE faseLogica acceso

    SE clic
        luminosita=0
        faseLogica=spento

    ALTRIMENTI SE pressioneContinua
        fai un passo fader

Ora questa logica non si occupa ne di debounce, ne di riconoscere pressioni e tempi, diventerebbe troppo complessa, viene semplicemente guidata da due variabili 'clic' e 'pressioneContinua' prodotte da qualcosa di precedente.

Questo qualcosa può essere un riconoscitore di clic/pressioni lunghe, ad esempio:

clic = 0

SE fasePulsante 0

    SE premuto
        carica tempo longpress
        fasePulsante=1

ALTRIMENTI SE fasePulsante 1

    SE rilasciato
        clic=1
        fasePulsante=0

    ALTRIMENTI SE timeout 2000
        pressioneContinua=1
        fasePulsante=2

ALTRIMENTI SE fasePulsante 2

    SE rilasciato
        pressioneContinua=0
        fasePulsante=0

E anche questa logica non fa debounce e riconoscimento di pressioni, riceve solo due variabili 'premuto' 'rilasciato', che possono essere prodotte da una sezione ancora precedente che finalmente legge l'ingresso e riconosce i fronti di pressione e rilascio (edge detect):

reading = (digitalRead(pulsantePin) == PRESSLEVEL);
premuto = (reading  && !pulsanteStato);
rilasciato = (!reading && pulsanteStato);
pulsanteStato = reading;


|    pulsantePin                reading      |
|-------] [-----------------------( )--------|
|                                            |
|     reading  pulsanteStato    premuto      |
|-------] [------]/[--------------( )--------|
|                                            |
|     reading  pulsanteStato   rilasciato    |
|-------]/[------] [--------------( )--------|
|                                            |
|     reading                 pulsanteStato  |
|-------] [-----------------------( )--------|

A questo punto se il debounce è realizzato con i condensatori sugli ingressi... finito, le singole parti sono semplici e si occupano solo di un piccolo compito specifico. Basta replicare per ogni pulsante.

Poi con più esperienza si potrebbero realizzare delle funzioni uniche valide per tutti i pulsanti, a cui passare di volta in volta il "contesto" su cui lavorare, cioè i dati dei singoli pulsanti/uscite, ma è un'ottimizzazione / compattamento / refactoring non indispensabile. Va bene come esercizio futuro :wink:

Molto chiaro ed esaustivo! Ora faccio qualche esperimento.

>ClaudioFF
Ho provato ad interpretare i tuoi consigli ed è uscito l' obbobrio che segue:

int PINpb1 = 2;
int LED1 = 9;
bool StatoPB1 = LOW;
int Step =0;
int lumin = 200;
int increm = 1;
unsigned long Tpress = 0;


void setup() {
  Serial.begin(9600);
  pinMode(PINpb1, INPUT);
  pinMode(LED1, OUTPUT);
 // LED1 = LOW;
}

void loop() {
  StatoPB1 = digitalRead (PINpb1);
//  Tpress =0;
  Serial.print ("Stato=  ");
  Serial.println (Step);
  //Serial.print ("  /  Tempo Pressione=  ");
  //Serial.println (Tpress);
  switch (Step){
    case 0:
      Spento();
    break;
    case 1:
      Acceso();
    break;
//    case 2:
//      Fade();
//    break;
  }
}

void Spento() {
  digitalWrite(LED1, LOW);
  if (StatoPB1 == HIGH) {
    Step = 1;
    delay(300);
  }
}

void Acceso() {
  analogWrite(LED1,200);
  
  if (StatoPB1 == HIGH) {
    unsigned long tempo1 = millis();
      if ((tempo1 - Tpress) < 5000) {
        Step = 0;
        Tpress = tempo1; 
        delay(300);}    
      else if ((tempo1 - Tpress) > 5000) {
        analogWrite(LED1,lumin);
          if (StatoPB1 == HIGH) {
            lumin = lumin + increm;
            if (lumin <= 0 || lumin >= 255) {
              increm = -increm;
              Tpress = tempo1;
              delay(300);
             }  
          }       
      }
  }
}
//void Fade() {
//  unsigned long tempo2 = millis();
//  analogWrite(LED1,lumin);
//  if (StatoPB1 == HIGH) {
//    lumin = lumin + increm;
//    if (lumin <= 0 || lumin >= 255) {
//      increm = -increm; 
//    }
//  }

Non mi riesce proprio ( sto provando da 2 giorni) a far entrare il processo nella regolazione di luminosità. Ho provato ad usare 2 pulsanti per gestire questi stati e il sistema funziona, ma purtroppo nella realtà ho solo un pulsante.
Devo anche capire se è conveniente (e come) fare il sistema a stati, il progetto, quando sarà finito sarà composto da 6 punti luce (24VDC) comandato da 6 gruppi di pulsanti e mi sembra troppo macchinoso.
Ho lasciato il codice "sporco" per un eventuale ritorno su strade tentate.
Potrebbe essere corretto per la gestione tenere traccia anche degli stati del pulsante?
Per imparare ad usare il millis() ho fatto lo sketch (funzionante) che segue, il quale determina il tempo di pressione e rilascio di un tasto.

 int PINpb1 = 2;
bool StatoPB = LOW;
bool StatoPBold = LOW;


unsigned long Tpress = 0;

void setup() {
 Serial.begin(9600);
 pinMode(PINpb1, INPUT);

}

void loop() {
 StatoPB = digitalRead (PINpb1);
 if ((StatoPB == HIGH) && (StatoPBold == LOW)) {
   unsigned long tempoH = millis();
   StatoPBold = StatoPB;
   Serial.print ("Tempo off  ");
   Serial.println (tempoH - Tpress);
   Tpress = tempoH;
 }
  else if ((StatoPB == LOW) && (StatoPBold == HIGH)) {
   unsigned long tempoL = millis();
   StatoPBold = StatoPB;
   Serial.print ("Tempo on  ");
   Serial.println (tempoL - Tpress);
   Tpress = tempoL;
  }
}[code]

Se voleste darmi ancora qualcuno dei vostri preziosi consigli, ve ne sarei Grato!

Un abbozzo c'è. Il problema è aver messo troppe competenze tutte assieme, tutte a carico di ogni stato, fino al controllo del livello letto dal pulsante e (ipotizzo) del debounce e del rilascio (ottenuti impropriamente con quei delay 300)... così se ne esce pazzi :slight_smile:

È molto più semplice e "discorsivo":

void Spento()
{
    if (onClic)            // se clic accende
    { 
        lumin = 200;
        Step = 1;
    }
}

void Acceso() 
{
    if (onClic)            // se clic spegne
    { 
        lumin = 0;
        Step = 0;
    }
    else if (longPress)    // se longpress passo fading
    { 
        Fade();
    }
}

Il fading è appena un po' più complesso, perché ad ogni incremento/decremento immagino debba seguire una piccola pausa (qualche dieci millisecondi)

void Fade()
{
    if (!fadePause)                   // singolo passo fading
    {
        lumin = lumin + increm;
        if (lumin <= 0  ||  lumin >= 255) { increm = -increm; }
        fadePause = 1;
        t1 = millis();
    }
 
    if (fadePause && (millis()-t1 > 10))   // timeout pausa tra incrementi
    {
        fadePause = 0;
    }
}

La scrittura dell'uscita può essere unica:

// macchina logica applicativa
switch (Step) 
{
    case 0: Spento(); break;
    case 1: Acceso(); break;
}

// aggiorna uscite
analogWrite(LED1, lumin);

La semplificazione è quella di non doversi occupare, a questo livello di logica, dei livelli LOW/HIGH del pulsante, la cui gestione va demandata ad una sezione precedente che si occuperà solo di quello (e a cui non interesserà nulla di sapere perché e come funzionerà la logica seguente).

In sostanza si tratta di produrre le variabili 'onClic' e 'longPress' partendo dalle letture grezze del pulsante, in modo che queste variabili possano essere comodamente utilizzate nella logica seguente molto più semplice. 'onClic' deve essere una variabile "impulsiva", che va a 1 per un ciclo nel momento in cui viene rilevato il clic (pressione + successivo rilascio entro un certo tempo T), mentre 'longPress' deve essere una variabile "continua", che rimane a 1 per tutto il tempo in cui il pulsante resta premuto oltre il tempo T.

Ragionando in ladder sono QUATTRO righe di programma compresa la digitalRead :wink:

Provo a mettermi al lavoro con queste nuove informazioni, riguardo le tue spiegazioni una cosa non mi è chiara, devo comunque gestire lo stato 2 (fade)? , durante i test che ho fatto nei giorni scorsi, quando entravo nello Step 2, iniziava il fading ma a ciclo continuo indipendente dal pulsante.

Si può fare in entrambi i modi. Come l'ho scritto io 'Fade' non è uno stato ma solo una funzione chiamata dallo stato 1. Se lo si vuole trasformare in stato come lo avevi pensato tu, si può tranquillamente fare, ma va aggiunto il controllo di fine pressione lunga per tornare nello stato 1

if (!longPress) { Step = 1; }