Progetto relè

Salve a tutti,

stavo cercando di realizzare un piccolo progetto con una scheda a 8 relè.
Quando però sono incappato in diversi problemi tra delay mills interrupt .

il mio progetto sostanzialmente dovrebbe funzionare così:
tutto spento
schiaccio un pulsante e....:
RL1 --> blinka per X secondi (potendo scegliere anche il tempo di blink)
dopo questo tempo X RL1 rimane eccitato per un tempo infinito
indipendentemente da quello che sarà questo tempo X gli altri relè cominceranno ad eccitarsi ad intervalli di tempo che io deciderò , quindi per esempio potrebbe succedere che RL3 cominci ad eccitarsi ancora prima che il tempo X finisca ....

non so se sono riuscito a spiegarmi nel caso di chiarimenti chiedetemi , per ora sono riuscito a scrivere qualche riga ma non riesco a uscire da questo dilemma.

sarà perchè sono ancora principiante !:slight_smile:

Chi mi aiuta per favore ?

mmmm la vedo brutta per i poveri contatti del rele che deve anche "blinkare"

comunque il discorso mi pare abbastanza semplice
devi solo definire esattamente cosa il programma deve fare e poi tradurlo nelle istruzioni corrispondenti
per cui

un pin a cui sarà attaccato il pulsante con la relativa resistenza di pull-up o pull-down che nel loop controllerai
quando il pulsante cambia stato (ricordati il debounce)
attivi il pin del primo rele per i secondi/millisecondi che vuoi controllando il tempo trascorso con l'uso del timer interno ( millis() )
e trascorso un altro periodo da te impostato (sempre controllato nello stesso modo) fai partire un altro o più rele
nel frattempo se il tempo di lampeggio è terminato blocchi ad on il primo rele...

Grazie mille per il tuo aiuto .... per ora ho scritto questa istruzione che però non funziona il relè rimane sempre acceso e alla pressione del mio pulsante non succede nulla

dove cavolo sbaglio ????

void updateRL1_stateL()
{



  int reading = digitalRead(btn_START);

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

  if ((millis() - currentMillis) > RL1_blinkInterval)
  {

    if (reading != buttonState)
    {
      buttonState = reading;

      if (buttonState == HIGH)
      {
        int cntr = 10;

        do
        {
          digitalWrite(RL1, LOW);
          delay(200);
          digitalWrite(RL1, HIGH);
          delay(200);
          cntr = cntr - 1;
        } while (cntr != 0);

        digitalWrite(RL1, LOW);
        RL1_state = LOW;
      }
    }
  }
}

dovrebbe "secondo il mio progetto " quando schiaccio il tasto lui comincia a "blinkare" per un contatore di 10 e poi rimanere spento .....

ma NIENTE !!!!

direi che manca l'aggiornamento di lastbuttonstate...

ma il programma completo è meglio :wink:

:o :o :o :o
oddio sono abbastanza bloccato .... ti posto il programma magari tu che non sei completamente con la testa nel progetto riesci a trovarmi la virgola che mi manca ......

// --------CONSTANTS (won't change)---------------

const int RL1 = 12;      // the pin numbers for the LEDs
const int RL2 = 11;
const int RL3 = 10;
//const int buttonPin = 9;

const int btn_START = 4; // the pin number for the button
int lastButtonState = LOW;
int buttonState;

const int RL1_blinkInterval = 500; // number of millisecs between blinks
const int RL2_blinkInterval = 2500;
const int RL3_blinkInterval = 4500;

const int blinkDuration     = 500; // number of millisecs that Led's are on - all three leds use this

const int buttonInterval    = 300; // number of millisecs between button readings

//------------ VARIABLES (will change)---------------------

byte RL1_state = LOW;           // used to record whether the LEDs are on or off
byte RL2_state = LOW;           // LOW = off
byte RL3_state = LOW;
byte btn_START_state = LOW;

unsigned long currentMillis = 0;              // stores the value of millis() in each iteration of loop()
unsigned long previousOnBoardLedMillis = 0;   // will store last time the LED was updated
unsigned long previousLed_A_Millis = 0;
unsigned long previousLed_B_Millis = 0;

unsigned long previousButtonMillis = 0;       // time when button press last checked

unsigned long previousServoMillis = 0;        // the time when the servo was last moved




void setup() {

  // set the Led pins as output:
  pinMode(RL1, OUTPUT);
  pinMode(RL2, OUTPUT);
  pinMode(RL3, OUTPUT);
  //  pinMode(buttonLed_Pin, OUTPUT);

  // set the button pin as input with a pullup resistor to ensure it defaults to HIGH
  pinMode(btn_START, INPUT_PULLUP);
  //    pinMode(btn_START, INPUT);
}



void loop() {

  // Notice that none of the action happens in loop() apart from reading millis()
  //   it just calls the functions that have the action code

  currentMillis = millis();   // capture the latest value of millis()
  //   this is equivalent to noting the time from a clock
  //   use the same time for all LED flashes to keep them synchronized

  readButton();               // call the functions that do the work
  updateRL1_state();
  //  updateRL1_stateL();
  //  updateRL2_state();
  //  updateRL3_state();
  switchLeds();


}



void updateRL1_state() {

  if (RL1_state == LOW) {
    // if the Led is off, we must wait for the interval to expire before turning it on
    if (currentMillis - previousOnBoardLedMillis >= RL1_blinkInterval) {
      // time is up, so change the state to HIGH
      RL1_state = HIGH;
      // and save the time when we made the change
      previousOnBoardLedMillis += RL1_blinkInterval;



      // NOTE: The previous line could alternatively be
      //              previousOnBoardLedMillis = currentMillis
      //        which is the style used in the BlinkWithoutDelay example sketch
      //        Adding on the interval is a better way to ensure that succesive periods are identical

    }
  }
  else
  { // i.e. if RL1_state is HIGH

    // if the Led is on, we must wait for the duration to expire before turning it off
    if (currentMillis - previousOnBoardLedMillis >= blinkDuration) {
      // time is up, so change the state to LOW
      RL1_state = LOW;
      // and save the time when we made the change
      previousOnBoardLedMillis += blinkDuration;
    }
  }
}
void updateRL2_state() {

  if (RL2_state == LOW) {
    if (currentMillis - previousLed_A_Millis >= RL2_blinkInterval) {
      RL2_state = HIGH;
      previousLed_A_Millis += RL2_blinkInterval;
    }
  }
  else {
    if (currentMillis - previousLed_A_Millis >= blinkDuration) {
      RL2_state = LOW;
      previousLed_A_Millis += blinkDuration;
    }
  }
}
void updateRL3_state() {

  if (RL3_state == LOW) {
    if (currentMillis - previousLed_B_Millis >= RL3_blinkInterval) {
      RL3_state = HIGH;
      previousLed_B_Millis += RL3_blinkInterval;
    }
  }
  else {
    if (currentMillis - previousLed_B_Millis >= blinkDuration) {
      RL3_state = LOW;
      previousLed_B_Millis += blinkDuration;
    }
  }
}
void switchLeds() {
  // this is the code that actually switches the LEDs on and off

  digitalWrite(RL1, RL1_state);
  digitalWrite(RL2, RL2_state);
  digitalWrite(RL3, RL3_state);
  //  digitalWrite(buttonLed_Pin, btn_START_state);
}
void readButton() {

  // this only reads the button state after the button interval has elapsed
  //  this avoids multiple flashes if the button bounces
  // every time the button is pressed it changes btn_START_state causing the Led to go on or off
  // Notice that there is no need to synchronize this use of millis() with the flashing Leds

  if (millis() - previousButtonMillis >= buttonInterval) {

    if (digitalRead(btn_START) == LOW) {
      btn_START_state = ! btn_START_state; // this changes it to LOW if it was HIGH
      //   and to HIGH if it was LOW
      previousButtonMillis += buttonInterval;
    }
  }

}

quando parte il programma
entra nella funzione readButton()
e se tu premi il pulsante e sono passati almeno 300 millisecondi
btn_START_state (piuttosto complicato anche da scrivere) diventa il contrario quindi la prima volta diventa "vero"
poi entri nella funzione
updateRL1_state()
e qui vedo che non usi la variabile btn_START_state
quindi che tu abbia premuto o meno il pulsante, se sono passati RL1_blinkInterval = 500 millisecondi
metti RL1_state = HIGH
quindi accendi il led
e appena sono passati blinkDuration = 500 millisecondi lo spegni
e questo appunto di continuo :slight_smile:
quindi
nella funzione
updateRL1_state()
la prima cosa da controllare è che btn_START_state sia vero, poi andrai a cambiare RL1_state in funzione dei tempi

ok ok ok ..... niente non ne uscivo ..... ho deciso di prendere un foglio bianco e riscrivere più o meno da capo ....

ora si comporta quasi come vorrei ...
inizio tutto spento ,
premo il pulsante ,
il RL1 fa il suo ciclo con un contatore che conta fino a 10 .........
e poi...
indipendentemente da quello che fa il programma quando io schiaccio il pulsante, il RL2 deve continuare a lampeggiare per il tempo che imposto come anche il RL3,RL4,RL5....RL8.

ma quando entra nella funzione del pulsante premuto tutto si stoppa ....eppure non uso nessun delay, non capisco !!!

che ne dici ?

//----------------Collegamento pulsante----------------------------------------
int buttonPin = 4;
//----------------Variabili che non cambiano -------------------------------------
int buttonState;
int lastButtonState = LOW;
long lastDebounceTime = 0;
long debounceDelay = 50;
unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
//-----------------Collegamento Relè-------------------------------------------
const int RL1_pin =  12;
const int RL2_pin =  11;
const int RL3_pin =  10;
const int RL4_pin =  9;
const int RL5_pin =  8;
const int RL6_pin =  7;
const int RL7_pin =  6;
const int RL8_pin =  5;

//-----------------Stato iniziale Relè----------------------------------------------
int RL1_state = HIGH;    //Se è LOW è ACCESO! (al contrario del normale)
int RL2_state = HIGH;
int RL3_state = HIGH;
int RL4_state = HIGH;
int RL5_state = HIGH;
int RL6_state = HIGH;
int RL7_state = HIGH;
int RL8_state = HIGH;


//----------------Tempi di lampeggio------------------------------------
const long RL1_blink = 500;  //Questo è il tempo ON/OFF del relè 1
const long RL2_blink = 1000;
const long RL3_blink = 1000;
const long RL4_blink = 1000;
const long RL5_blink = 1000;
const long RL6_blink = 1000;
const long RL7_blink = 1000;
const long RL8_blink = 1000;

//---------------------------------------------------------------

void setup() {

  //  Serial.begin(9600);

  pinMode(buttonPin, INPUT);

  pinMode(RL1_pin, OUTPUT);
  pinMode(RL2_pin, OUTPUT);
  pinMode(RL3_pin, OUTPUT);
  pinMode(RL4_pin, OUTPUT);
  pinMode(RL5_pin, OUTPUT);
  pinMode(RL6_pin, OUTPUT);
  pinMode(RL7_pin, OUTPUT);
  pinMode(RL8_pin, OUTPUT);



  digitalWrite(RL1_pin, RL1_state);
  digitalWrite(RL2_pin, RL2_state);
  digitalWrite(RL3_pin, RL3_state);
  digitalWrite(RL4_pin, RL4_state);
  digitalWrite(RL5_pin, RL5_state);
  digitalWrite(RL6_pin, RL6_state);
  digitalWrite(RL7_pin, RL7_state);
  digitalWrite(RL8_pin, RL8_state);
}

void loop() {
  //------------------Comportamento relè 1 alla pressione del tasto ---------------------------------------------
updateRL2_state();


  int reading = digitalRead(buttonPin);

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

  if ((millis() - lastDebounceTime) > debounceDelay)
  {
    if (reading != buttonState)
    {
      buttonState = reading;
      if (buttonState == HIGH)
      {
        int cntr = 10;
        do
        {
          
          unsigned long currentMillis = millis();
          if (currentMillis - previousMillis >= RL1_blink) {
            previousMillis = currentMillis;
            if (RL1_state == LOW) {
              RL1_state = HIGH;
            } else {
              RL1_state = LOW;
            }
            digitalWrite(RL1_pin, RL1_state);

            cntr = cntr - 1;
          }
         
        } while (cntr != 0);

        digitalWrite(RL1_pin, LOW);
        RL1_state = LOW;
      }
      
    }
     
  }
  digitalWrite(RL1_pin, RL1_state);
  lastButtonState = reading;
  //---------------------------------------------------------------



}

void updateRL2_state() {

  unsigned long currentMillis2 = millis();
  if (currentMillis2 - previousMillis2 >= RL2_blink) {
    previousMillis2 = currentMillis2;
    if (RL2_state == LOW) {
      RL2_state = HIGH;
    } else {
      RL2_state = LOW;
    }
    digitalWrite(RL2_pin, RL2_state);
  }
}

RL2 continuerebbe a lampeggiare se si continuasse a chiamare la funzione 'updateRL2_state', ma entrando nel ciclo do/while finché il ciclo non finisce la funzione non viene più chiamata.

Quindi le soluzioni sono due, o si richiama 'updateRL2_state' anche da dentro il do/while (funziona ma è una pezza perché alla prossima modifica si è da capo), oppure si riscrive la logica eliminando il do/while e lasciando ciclare solo il loop principale.

eh ma se metto updateRL2_state nel do/while.... quando il mio contatore finisce i suoi 10 cicli anche updateRL2_state smette di funzionare ,

e cosa posso usare al posto di do/while nel ciclo loop che mi esegue quel blink del RL1 una volta sola fino a che il contatore non è a 0 secondo te ?

ciclo for ? però continuerà comunque a contare da 10 a 0 in loop ..... giusto ?

Per quanto riguarda il richiamo di 'updateRL2_state' ovviamente andrebbe sia dentro che fuori dal while, ma è evidente che volendo realizzare diverse cose che avanzano con tempi indipendenti bisognerebbe richiamare tutte le altre dentro i cicli di ciascuna, per cui non è una buona idea.

Anche con il for sarebbe la stessa cosa. Il problema non è il tipo di ciclo ma la struttura dell'esecuzione.

Mi sembra che hai cercato di curare un certo ordine, pur con alcuni "peccati" del principiante (ad esempio tutte le variabili, a parte quelle per i tempi, possono essere byte e non int, dimezzando l'uso della memoria).

Quelle che hai chiamato "Variabili che non cambiano" sono in realtà variabili globali, che permangono durante tutta l'esecuzione (differenza delle variabili locali delle funzioni che svaniscono al termine delle funzioni stesse), ma non è che i valori non possono cambiare.

La funzione 'updateRL2_state', a parte il fatto che potrebbe essere ridotta, è strutturata correttamente come un piccolo processo da richiamare continuamente, e che non si prende mai tutto il tempo macchina con delay o cicli bloccanti. Anche tutto il resto dovrebbe essere pensato nello stesso modo: programmazione a stati, ricordando che il concetto fondamentale è che "ogni ritardo o attesa è creato permanendo in un certo stato per un certo numero di cicli".

Questo è un esempio di come potrebbe essere ristrutturato il tuo codice. Nota che nelle funzioni non compaiono mai i numeri dei pin, o i livelli HIGH o LOW, questi sono tutti definiti con le etichette/nomi di comodo all'inizio in modo da non confondersi nel resto del programma (per convenzione tutti questi nomi si scrivono in maiuscolo per distinguerli dalle variabili). Ci sono solo tre "trucchetti" un po' avanzati, uno in ogni funzione. La funzione 'updateRL1_process' prende il posto del ciclo while, ed è la variabile di stato 's' a stabilire quale parte deve essere eseguita ad ogni giro di loop. Inoltre ogni funzione ha le sue variabili static, che permangono come quelle globali pur essendo locali (e quindi si possono usare nomi semplici senza prefissi/suffissi). Le uniche globali rimaste sono 'onPress' (che viene impostata a true nel momento in cui il pulsante viene premuto) e 'currentMillis' che all'inizio di ogni ciclo viene caricata con il tempo attuale e serve a tutte le altre funzioni. In questo programma ogni funzione è una "micromacchina" indipendente, con le proprie variabili di lavoro interne, che comunica con le altre tramite poche variabili globali indispensabili. Il loop è un semplice "scheduler" che attiva ciclicamente migliaia di volte al secondo ogni "macchina".

//----------------Collegamento pulsante

#define  BUTTON_PIN   4
#define  PRESS_LEVEL  HIGH  //livello pulsante premuto
#define  DEBTIME      50

//-----------------Collegamento Rele'

#define  RL1_PIN      12
#define  RL2_PIN      11
#define  RL_OFF       HIGH  //livello rele' spento
#define  RL_ON        LOW   //livello rele' acceso

//----------------Tempi di lampeggio

#define  RL1_BLINK    500
#define  RL2_BLINK    1000

//----------------Variabili globali

bool          onPress;        //true per un ciclo quando viene premuto puls.
unsigned long currentMillis;  //tempo attuale aggiornato ad ogni ciclo

//--------------------------------------------------------------------
void setup() 
{
    pinMode(BUTTON_PIN, INPUT);

    pinMode(RL1_PIN, OUTPUT);
    pinMode(RL2_PIN, OUTPUT);

    digitalWrite(RL1_PIN, RL_OFF);  //rele' spenti
    digitalWrite(RL2_PIN, RL_OFF);
}
//--------------------------------------------------------------------
void loop() 
{
    currentMillis = millis();
    readPuls();
    updateRL1_process();
    updateRL2_state();
}
//--------------------------------------------------------------------
void readPuls()
{
    static byte prior_in = digitalRead(BUTTON_PIN);
    static unsigned long t = currentMillis;
    byte in = digitalRead(BUTTON_PIN);
    onPress = false;
    if (in == prior_in)
        t = currentMillis;
    else if (currentMillis - t >= DEBTIME)
    {
        prior_in = in;
        onPress = in == PRESS_LEVEL;
    }
}
//--------------------------------------------------------------------
void updateRL1_process()
{
    static byte s = 0;
    static byte cntr = 10;
    static unsigned long t;
    switch (s)
    {
    case 0:
        if (onPress) 
        {
            digitalWrite(RL1_PIN, RL_ON);
            t = currentMillis;
            s = 1; 
        }
        break;
    case 1:
        if (currentMillis - t >= RL1_BLINK) 
        {
            digitalWrite(RL1_PIN, RL_OFF);
            t += RL1_BLINK;
            s = 2; 
        }
        break;
    case 2:
        if (currentMillis - t >= RL1_BLINK) 
        {
            digitalWrite(RL1_PIN, RL_ON);
            if (cntr-- > 0) 
            {
                t += RL1_BLINK;
                s = 1; 
            }
            else
                s = 3;
        }
    }
}
//--------------------------------------------------------------------
void updateRL2_state() 
{
    static byte rl_state = RL_OFF;
    static unsigned long t = currentMillis;
    if (currentMillis - t >= RL2_BLINK) 
    {
        t += RL2_BLINK;
        digitalWrite(RL2_PIN, rl_state = !rl_state);
    }
}
//--------------------------------------------------------------------

Claudio..... è fenomenale ! funziona proprio come volevo !!!
...e si , ho ancora un sacco da imparare !!
adesso mi devo studiare bene anche quello che mi hai scritto così che io possa impararlo bene ad usare in progetti futuri !
ancora una richiesta .....se io dovessi ...posticipare l'accensione di RL2 dopo 30 secondi dall'accensione di "ARDUINO!" cioè io attacco l'alimentazione e solo dopo 30 secondi il RL entra in "updateRL2_state" posso usare un comunissimo delay ??

però dove metterlo ???

nel loop?
nel updateRL2_state?

Dipende da vosa deve fare in quei 30 secondi. Se la risposta é aspettare allora metti una bella delay in setup() e dopo essa chiami la funzione. Se la rispista é 'fare il resto normalmente' allora serve un controllo di millis() nel loop(). La funzione verrà chiamata solo qui:

void loop()
{
//cose
if (millus()>30000)
{
Funzione();
}

Almeno per come la ho capita io

si praticamente prima che facciano qualcosa il RL2, RL3,....RL8 deve aspettare tipo 30 secondi ..MA ! se prima di questi 30 secondi schiaccio il bottone comunque RL1 fa il suo lavoro !

Isolando tutto il codice che fa lavlrare RL2/8 in una funzione (non so se lo hai già fatto, non ho guardato) basta chiamarla solamente nel corpo di una if che ha per condizione millis>3000. Nella funzione NON deve essere presente testo che invluda il lavoro di RL1.
A te farlo praticamente

LucaMu:
e solo dopo 30 secondi il RL entra in "updateRL2_state" posso usare un comunissimo delay ?? però dove metterlo ???

Speravo che l'esempio e il link servissero da esempio di studio per capire la logica di come realizzare qualsiasi cosa con più processi in parallelo e senza delay...

Una possibile soluzione, quasi del tutto funzionante (almeno funzionante nelle prime 1192 ore), l'ha scritta Silente: non si tratta di mettere un ritardo, ma di non richiamare per un certo numero di cicli le funzioni che si vogliono temporaneamente inattive.

Claudio , certo che è un'esempio per studiare il richiamo delle funzioni nel ciclo loop però comunque io devo realizzare questo progetto , quindi avrei bisogno di questo codice . Se tu guardi nei vecchi commenti già avevo pensato di fare un ragionamento di micromacchine nello schedular del loop. ma c'èra qualcosa che non funzionava

ora grazie alle tue dritte va tutto per il meglio ma volevo ancora implementare questo piccola miglioria al codice .

Grazie comunque del tuo utilissimo sostegno

quindi mi dite che potrei mettere un "contatore" che aspetta i miei 30 secondi e poi fa partire le funzioni su RL2 ecc ?

o posso usare il mills ?

Ma la soluzione l'ha già scritta silente (a parte il millus che ovviamente è millis).

Se proprio vogliamo evitare il problema delle 1192 ore:

bool trascorso = false;

void loop()
{
    //cose sempre
    if (millis() > 30000) trascorso = true;
    if (trascorso)
    {
        //cose da 30 sec in poi
    }
}

Claudio

scusami abbi pazienza ...sono un pò arruginito e quasi sicuramente non tanto capace,avrei tanto bisogno di finire questo progetto

ho necessità che quei 30 secondi partano dopo che io premo il pulsante ,

quindi :

premo il pulsante rl1 comincia a eccitarsi per un tempo x o con un contatore fino a 10 come abbiamo messo fino ad ora , nel frattempo rl2 rl3 rl4 ecc aspettano un tempo y trascorso il quale cominciano a lavorare anche loro ... è possibile ?

sarebbe possibile anche modificare quel contatore in vece in un tempo ?

Grazie in anticipo

ok ok ok fooooooooooorse ho capito
però.....
mi serve un trucchetto......

ho fatto così...

 bool trascorso = false;
  static byte s = 0;
  static byte cntr = 10;
  static unsigned long t;
 
  switch (s)
  {
    case 0:
      if (onPress)
      {
        if (t > 30000 )trascorso = true;
        if (trascorso)
        {
          updateRL2_state();
        }
        digitalWrite(RL1_PIN, RL_ON);
        t = currentMillis;
        s = 1;
      }
      break;
    case 1:
      if (currentMillis - t >= RL1_BLINK)
      {
        digitalWrite(RL1_PIN, RL_OFF);
        t += RL1_BLINK;
        s = 2;
      }
      break;
    case 2:
      if (currentMillis - t >= RL1_BLINK)
      {
        digitalWrite(RL1_PIN, RL_ON);
        if (cntr-- > 0)
        {
          t += RL1_BLINK;
          s = 1;
        }
        else
          s = 3;
      }

  }


}

solo che se etto t > 30000 ..... t si ferma a 14313 e se invece metto
millis() >30000.... comunque il rl2 parte a prescindere da onPress....

come potrei risolvere ???