Controllare un Relè temporizzato con un solo pulsante

Salve,

da neofita sto provando a scrivere uno sketch che faccia questa cosa:
se premo il pulsante il relè si aziona per 20 secondi e poi si spegne;
se tengo premuto lo stesso pulsante per 3 secondi (in qualunque momento) il relè si spegne.

In pratica non riesco ad unire le due cose.
La prima da sola è molto semplice.
La seconda da sola potrebbe essere eseguita dal seguente sketch:

unsigned long inizio = 0;
int counter = 0;
unsigned long tempo = 3000;


void setup(){
  pinMode (2, INPUT); //pin del pulsante
  pinMode (3, OUTPUT); //pin del relè
   
}

void loop(){
  if (digitalRead(2)==HIGH && counter == 0){
    inizio = millis();
    while(digitalRead(2)==HIGH) {
      if ((millis()-inizio)>=tempo){
        counter = 1;
        break;
      }
    } 
    if(counter == 1) {
      digitalWrite(3, LOW); //il relè si attiva con LOW
    }
  }
  
}

Come posso unire le due "funzioni"?

Grazie per ogni suggerimento

Sicuro che lo sketch faccia ciò che dici?
Hai messo il seguente commento:

//il relè si attiva con LOW

Quindi, attivi il relè se tieni premuto il tasto per 3 secondi.
Il tutto poi funziona una ed una sola volta, in quanto counter non lo riporti mai = 0.

Per unire le due cose, ti servono due timer (due variabili unsigned long di timeout) una per il controllo del tempo di pressione del tasto, e l'altro per i 20 secondi di attivazione relè.
Poi hai bisogno di una variabile di stato che ti faccia sapere se il relè è attivo o meno.

Tieni presente che mentre controlli la pressione dei 3 secondi a relè attivo, devi verificare costantemente anche che il timeout dei 20 secondi non scada durante la pressione.
Mi spiego meglio:
Poni che il relè sia attivo da 18 secondi, ed inizi a premere il pulsante.
Dopo due secondi dalla pressione del pulsante, il relè deve comunque potersi disattivare, anche se non hai rilasciato il pulsante, e non hai raggiunto i 3 secondi fatidici.

Brado,
hai ragione al 100%.
Sto studiando davvero tanto; sono perfino arrivato a metà di "Beginning C for Arduino", ma una cosa è eseguire degli sketch belli e pronti, un'altra è crearli ex novo.
Non è che potresti mostrarmi come gestire i due timers ?

Il difficile sta proprio nello scrivere sketch ex-novo, ma con un po' di applicazione non è impossibile.

Ti butto giù un'idea di massima, ad occhio dovrebbe funzionare.

unsigned long tempo_tasto = 3000;
unsigned long tempo_rele = 20000;
unsigned long inizio_tasto = 0;
unsigned long inizio_rele = 0;
boolean stato_tasto = 0;
boolean stato_rele = 0;
loop {
  se tasto = premuto {
      se stato_rele = disattivato {
          output rele = LOW
          inizio_rele = millis()
          stato_rele = attivato
      }
      altrimenti {
           se stato_tasto = disattivato {
               inizio_tasto = millis()
               stato_tasto = attivato
           }
               
      }
      se millis()-inizio_tasto >= tempo_tasto & stato_tasto = attivato {
          output rele = HIGH
          stato_rele = disattivato
          stato_tasto = disattivato
          inizio_rele = 0
          inizio_tasto = 0
      }

  }
  altrimenti {  // tasto non premuto
      stato_tasto = disattivato
      inizio_tasto = 0
  }
  se millis()-inizio_rele >= tempo_rele {
      output rele = HIGH
      stato_rele = disattivato
      inizio_rele = 0
  }

}

Riportare le varibili inizio_tasto e inizio_rele a 0 dopo le disattivazioni non è necessario, è una mia precauzione quando utilizzo i timer.

Fammi sapere.

Ciao Brado,

nel ringraziarti ancora per la risposta, ti chiedo di guardare se è corretto il mio modo di trascrivere in codice il tuo suggerimento, perchè lo sketch non mi funziona bene.

Innanzitutto appena caricato fa subito un ciclo di 20 secondi.
E poi (cosa più importante) alla pressione del tasto per 3 secondi non accade nulla.

Dove sbaglio ?

// Sketch per attivare un Relè alla pressione di un tasto (durata 20 secondi) 
// e disattivare il Relè se il tasto è premuto 3 secondi

#define TASTO 2
#define RELE 3
unsigned long tempo_tasto = 3000; // 3 secondi
unsigned long tempo_rele = 20000; // 20 secondi
unsigned long inizio_tasto = 0;
unsigned long inizio_rele = 0;
int stato_tasto = 0;
int stato_rele = 0;

void setup() {                  
  pinMode(RELE, OUTPUT);       // imposta il pin digitale come output  
  pinMode(TASTO, INPUT);     // imposta il pin digitale come input  
}

void loop(){
  
  if (digitalRead(TASTO) == HIGH) {
  
      if (stato_rele == 0) {
        digitalWrite(RELE, LOW);
        inizio_rele = millis();
        stato_rele = 1;
      }
      else {
           if (stato_tasto == 0) {
             inizio_tasto = millis();
             stato_tasto = 1;
           }
               
      }
      if (((millis()-inizio_tasto) >= tempo_tasto) && stato_tasto == 1) {
        digitalWrite(RELE, HIGH);
        stato_rele = 0;
        stato_tasto = 0;
        inizio_rele = 0;
        inizio_tasto = 0;
      }

  }
  else {  // tasto non premuto
      stato_tasto = 0;
      inizio_tasto = 0;
  }
  if ((millis()-inizio_rele) >= tempo_rele) {
    digitalWrite(RELE, HIGH);
    stato_rele = 0;
    inizio_rele = 0;
  }

}

Ciao,

ad occhio mi sembra corretto.

Prova ad eliminare tutte le parentesi degli if, in questo modo:

if (millis()-inizio_tasto >= tempo_tasto && stato_tasto == 1) {

e

if (millis()-inizio_rele >= tempo_rele) {

Tolte le parentesi.
Funziona come prima.
Alla pressione prolungata non accade nulla. :confused:

// Sketch per attivare un Relè alla pressione di un tasto (durata 20 secondi) 
// e disattivare il Relè se il tasto è premuto 3 secondi

#define TASTO 2
#define RELE 3
unsigned long tempo_tasto = 3000; // 3 secondi
unsigned long tempo_rele = 20000; // 20 secondi
unsigned long inizio_tasto = 0;
unsigned long inizio_rele = 0;
int stato_tasto = 0;
int stato_rele = 0;

void setup() {                  
  pinMode(RELE, OUTPUT);       // imposta il pin digitale come output  
  pinMode(TASTO, INPUT);     // imposta il pin digitale come input  
}

void loop(){
  
  if (digitalRead(TASTO) == HIGH) {
  
      if (stato_rele == 0) {
        digitalWrite(RELE, LOW);
        inizio_rele = millis();
        stato_rele = 1;
      }
      else {
           if (stato_tasto == 0) {
             inizio_tasto = millis();
             stato_tasto = 1;
           }
               
      }
      if (millis()-inizio_tasto >= tempo_tasto && stato_tasto == 1) {
        digitalWrite(RELE, HIGH);
        stato_rele = 0;
        stato_tasto = 0;
        inizio_rele = 0;
        inizio_tasto = 0;
      }

  }
  else {  // tasto non premuto
      stato_tasto = 0;
      inizio_tasto = 0;
  }
  if (millis()-inizio_rele >= tempo_rele) {
    digitalWrite(RELE, HIGH);
    stato_rele = 0;
    inizio_rele = 0;
  }

}

Posti anche lo schema elettrico ?

Allora, sono riuscito a scrivere un codice funzionante, spero serva anche ad altri (visto che in rete non sembra esserci nessun esempio di questo particolare sketch)
Ho dovuto riscriverlo quasi completamente.
Credo che non sia "bellissimo"; magari qualcuno può indicarci come renderlo più "elegante". ;D

// Relè e Pulsante (Zamundo) v1.0
  
#define RELE 3                // rele collegato al pin digitale 3  
#define BUTTON 2              // pulsante collegato al pin digitale 2  
int rele = 0;                  
int val = 0; 
int tasto = 0; 
unsigned long inizio_rele = 0;
unsigned long inizio_tasto = 0;
unsigned long durata_rele = 10000; // 10 secondi per velocizzare le prove
unsigned long durata_tasto = 2000; //  2 secondi per stesso motivo




void setup() {                  
  pinMode(RELE, OUTPUT);       // imposta il pin digitale come output  
  pinMode(BUTTON, INPUT);     // imposta il pin digitale come input 
 }  
  
void loop() {  
  val = digitalRead(BUTTON);  // legge il valore dell'input e lo conserva 
      
if (val == HIGH) {  // controlla se è accaduto qualcosa
    inizio_rele = millis ();
    tasto = tasto + 1;
    rele = 1;
    if (tasto == 1) { // permette di eseguire inizio_tasto=millis solo UNA volta
       inizio_tasto = millis();}
    if (((millis()-inizio_tasto) > durata_tasto) && (tasto >= 1)) {
        rele = 0;
        tasto = 0;
        inizio_tasto = 0;
        digitalWrite(RELE, HIGH);
        delay(4000); // ritardo fondamentale perchè altrimenti allo scadere dei 2 secondi tenendo premuto il pulsante riparte tutto
  } 
}   
else {
tasto = 0;
inizio_tasto = 0;
}
   
 
  if (rele == 1) {
    digitalWrite(RELE, LOW);} //accende il rele
      
    
  else {  
    digitalWrite(RELE, HIGH);    //spegne il rele 
    inizio_rele = 0; 
  } 
 if ((millis()-inizio_rele) >= durata_rele) {
    rele = 0;
    inizio_rele = 0;
    digitalWrite(RELE, HIGH);
  }
     
  delay(50);    //  serve per evitare leggeri rimbalzi
}

PS @Brado: Fra poco posto il circuito.... grazie del tuo prezioso aiuto.

Se metti il delay, mandi a pallino tutto il discorso fatto con millis ...

Il delay ha lo scopo di "inibire" per un pò il pulsante, perchè altrimenti trascorsi 2 secondi + 1 ms di pressione riparte il timer dei 10 secondi.

Ecco allegato, appena fatto, lo schema del circuito.

Se premi il pulsante in quei 4 secondi lo perdi, contento tu

Ciao Brado,
quello che scrivi è molto vero.
Non riesco a far funzionare lo sketch sul modello da te proposto.
A te funziona ?

Ho scritto un programma che per ora gira, ma sono sicuro che si potrebbe fare molto meglio.

Nell'attesa che tu o qualcun altro interessato mostri una strada migliore.

Un caro saluto

Lo sketch va più che bene grazie mille io lo uso come salita e discesa quindi ho replicato il tutto
Non riesco però ad alternarli xche
Non deve funzionare salita e discesa in contemporanea.qualcuno mi può aiutare vi ringrazio anticipatamente