Gestire 2 led con millis ed interruttore

Ed eccomi qui con la bandiera bianca al vento.. Ho cercato in rete ( ma il problema ovviamente sono io) uno sketch che più si addicesse al mio caso per poi modifcarlo in base alle mie esigenze ma anzichè chiarire le idee probabilmente me le sto complicando.
La situzione da riprodurre è la seguente:

  1. accendo un interruttore;
  2. si accende subito il led1 che si spegnerà poi dopo 2 minuti e comunque allo spegnimento dell'interruttore;
  3. accensione led2 dopo 30 secondi dall'accensione dell'interruttore e che si spegnerà allo spegnimento dell'interruttore o (ma questo è già un altro step) alla pressione di un pulsante.

In rete ci sono tantissimi esempi ognuno scritto in maniera differente ma anzichè dire ad Arduino di andare in loop purtroppo ci sono finito io dentro ad un loop..
So di utilizzare la funzione millis altrimenti andrei a bloccare Arduino per i 2 minuti di istruzione che devo dare.
Sono riuscito a far accednere il led all'accensione dell'interruttore ma quando ho inserito l'istruzione millis questa non veniva eseguita. Ringrazio a chiunque voglia darmi una dritta su come risolvere il problema..

:warning:
Ti segnalo che, nella sezione in lingua Inglese, si può scrivere SOLO in Inglese ... quindi, per favore, la prossima volta presta più attenzione in quale sezione metti i tuoi post; questa volta esso è stato spostato, da un moderatore della sezione di lingua Inglese, nella sezione di lingua Italiana ... la prossima volta potrebbe venire direttamente eliminato.
Grazie.

A quanto detto da UKHeliBob aggiungo ...

... evitate di utilizzare la traduzione automatica fatta dal browser ... vi impedisce di capire la lingua della sezione dove andate a scrivere!

Guglielmo

nessuna traduzione automatica. Credevo che la lingua inglese fosse di default per le linee guida generali del forum. Ad ogni modo ora ho imparato. Grazie per le attenzioni.

Benvenuto, Rocco.
Prima di tutto, dovresti pubblicare il programma che hai scritto.

int chiaveBatt; // Variabile dello stato dell'interruttore
int Stato_Sistema=0; //Variabile dello stato del sistema
int spiaAtt;
int spiaIR;
unsigned long t1, t2;

void setup() {
  t1 = millis();
  t2 = millis();
  pinMode(13, OUTPUT); //spia attesa
  pinMode(7, OUTPUT); //spia IR
  pinMode(4, INPUT); //chiave batterie
  
}

void loop() {
chiaveBatt = digitalRead(4); //Verifica se l'interruttore batterie è stato acceso. Se si assegna un valore 1 alla variabile.
//Se l'interruttore è satto acceso incrementa il valore della varibile Stato_Sistema.
if (chiaveBatt == HIGH){
  Stato_Sistema=Stato_Sistema+1;
  }
delay(150);//Ritardo in millisecondi per stabilizzare il sistema ed evitare letture doppie.
//In base allo stato del sistema il programma eseguirà determinate istruzioni.
switch(Stato_Sistema){
    case 0: 
          digitalWrite(spiaAtt, HIGH);
          delay(50);//Ritardo in millisencondi per stabilizzare il sistema.
          break;
    case 1:
          if ((millis() - t1) >= 120000) 
          digitalWrite(spiaAtt, LOW);
          t1 = millis();
          delay(50);//Ritardo in millisencondi per stabilizzare il sistema.
          break;
    case 2: 
          if ((millis() - t2) <= 30000) 
          digitalWrite(spiaIR, LOW);
          t2 = millis();
          delay(50);//Ritardo in millisencondi per stabilizzare il sistema.
          Stato_Sistema=0;//Azzera la variabile dello stato del sistema.
          delay(50);//Ritardo in millisencondi per stabilizzare il sistema.
          break;   
    }      
}

Ecco il codice secondo la mia intepretazione. Arduino non mi da errori ma avviando la simulazione con tinkercad non succede nulla.

Se l'interruttore accende e spegne anche Arduino, lo spegnimento del LED allo spegnimento dell'interruttore sarà automatico.

LED1 si accende dal setup.
Quando millis()>=30000 si accende LED2.
Quando millis()>=120000 si spegne LED1.
Puoi farlo anche con la macchina a stati finiti.

P.S.: perché hai messo tutti quei delay(50)?...

L'istruzione "stato_macchina" è stata inserita perché ho necessità di far spegnere il led1 dopo 120000 millis e comunque allo spegnimento dell'interruttore ma a questo punto capisco che sono fuori strada. Mentre il led2 si deve accendere dopo 30000 millis e resta acceso finquando un secondo pulsante (che non ho ancora inserito nello sketch perché voglio fare un passo alla volta) lo farà spegnere e comunque il led2 deve spegnersi all'apertura dell'interruttore.
I 50 millis li ho inseriti perché su un altro sketch simile spiegavano che serve a stabilizzare il sistema.

O sono inutili, oppure il programma è sbagliato... :smile:

Sono io che sono ancora al livello "0" però la sfida è appena cominciata :crossed_fingers:

Questo è l'errore principale... Pensare di trovare qualcosa già pronto che risolva il tuo problema va bene quando replichi esattamente per filo e per segno un progetto pensato da altri.
Gli esempi ed i tutorial che si trovano online dovrebbero soltanto fornirti un'idea di come procedere ovvero spiegare con dovizia il principio di funzionamento di un algoritmo e del perché si fanno determinate scelte software piuttosto che altre.
Il condizionale è d'obbligo perché la maggior parte sono scritti con i piedi purtroppo, e se ti va bene sono poco più degli esempi già inclusi nelle librerie (che dovrebbe sempre essere il primo posto dove andare a guardare per come la vedo io).

Il mio consiglio è di smettere questa frenesia da copiaincolla, prendere carta e penna e descrivere a parole il principio di funzionamento del tuo codice come hai fatto qui nel forum.

L'algoritmo che descrivi è semplice, devi solo capire come e perché usare un'istruzione o un costrutto piuttosto che un altro.

Con '0' risparmi un byte! :smile:
Segnatelo; tra qualche tempo capirai che significa... :slightly_smiling_face:

  1. I delay servono a realizzare il debounce barbaro minimo. In effetti se il ciclo di loop gira lentamente, i rimbalzi dei contatti si risolvono nei tempi di attesa. Però basta un solo delay a inizio o fine loop, e bisogna assolutamente leggere i contatti una sola volta per ogni ciclo, evitando digitalRead sparse in vari punti del codice. Inoltre questo non protegge dai disturbi impulsivi che possono capitare nel momento esatto della lettura degli ingressi. Per pochi ingressi, e se il software è ancora difficile da affrontare, meglio filtrare gli ingressi con dei condensatori e dimenticarsi del debunce software (barbaro minimo, o altro che sia).

  2. È importante sapere se l'interruttore "accende" il tutto, o se è solo un ingresso che viene letto dal programma ad Arduino già acceso e funzionante.

  3. Per quanto elementare, ci troviamo di fronte a due processi paralleli difficili da gestire con un solo switch. Sarebbe il tipico caso in cui una "logica ladder" tipo PLC è più semplice da scrivere.

Di pronto non cerco nulla altrimenti viene meno la soddisfazione finale di vedere il progetto funzionante. Come scritto anche sopra sono agli inizi e cerco di imparare/apprendere da qualsiasi fonte...lo sketch che ho postato è frutto di un ragionamento scritto a penna (ho imparato che è una delle prime regole di programmazione). Sicuramente al momento non ho la padronanza e le conoscenze specifiche per scrivere uno sketch funzionante al primo colpo però mi sto impegnando a studiare ed ecco perché sono qui :wink:

3 Likes

Ottimo! E' l'approccio giusto.

Per quanto riguarda il tuo codice ci sono diversi errori, alcuni macroscopici altri di concetto.

Ad esempio le variabili spiaIR e spiaAtt che usi per i pin di uscita, dov'è che le assegni?

int spiaAtt;

Questa è una dichiarazione di variabile globale di nome spiaAtt. Per le variabili globali non esplicitamente inizializzate il loro valore è implicitamente inizializzato al valore di default che per i tipi numerici (int, byte, unsigned long ecc) vale 0 (zero). Sostanzialmente equivale a scrivere

int spiaAtt = 0;

Usi questa variabile qui:

digitalWrite(spiaAtt, HIGH);

Che possiamo anche scrivere così:

digitalWrite(0, 1);

Al fine di analizzare il comportamento di un programma torna utile mostrare il valore di ogni variabile, per cui si può scrivere:

// g0
chiaveBatt = digitalRead(4); 
if (chiaveBatt(1) == 1) {
  Stato_Sistema = Stato_Sistema + 1;
}
// g0

dove (1) è il valore della variabile. Ora sembra più evidente che il tutto inserito in un ciclo infinito (la funzione loop() è un ciclo infinito) comporta l'incremento di una unita per ogni ciclo eseguito. Esempio Stato_Sistema assume questi valori:

// loop 0
Stato_Sistema(0)
g0
Stato_Sistema(1)
// loop 1
Stato_Sistema(1)
g0
Stato_Sistema(2)
// loop 2
Stato_Sistema(2)
g0
Stato_Sistema(3)

Il tutto associato allo switch case si comporta eseguendo un case diverso ad ogni ciclo di loop. Ma case 3 non esiste e Stato_Sistema verrà incrementato fino ad assumere valori negativi per poi tornare a 0.

Da principiante è naturale non avere un metodo di analisi e a questo livello un metodo di analisi qualsiasi è meglio di non averne uno.

La situazione (stato complessivo delle variabili) può cambiare da loop 1 a loop 100, dove ad esempio nel loop 100 chiaveBatt vale 0 (zero) e allora
Stato_Sistema non viene più incrementata ma conserva il suo valore.

PS: vale tutto quanto detto da altri.

Ciao.

1 Like

La cosa interessante del funzionamento richiesto in questo topic è che a memoria non ricordo altri casi in cui la soluzione generale tramite FSM richiedesse due dsitinte FSM.


Trasformazione della descrizione in grafico a stati (il LED è acceso solo nello stato 1):

3456546456

Lo stato 0 è lo stato di partenza iniziale o "di riposo".


Stessa trasformazione (qui il LED sarà acceso solo nello stato 2):

(Grafici ottenuti con: https://www.cs.unc.edu/~otternes/comp455/fsm_designer/)


Sono due processi completamente distinti (chiamiamoli A e B), con temporizzazioni diverse, che in comune hanno solo il segnale di comando key (dove !key significa assenza di comando, o interruttore/chiave aperto). Ogni processo dovrà avere la sua variabile di stato personale (chiamiamole sA e sB).

Per ogni stato di ogni processo si può quindi scrivere un mini flowchart, ad esempio per il processo A:

E si può quindi procedere alla codifica pari pari, qui un esempio quasi completo del processo A:

switch (sA) {
    case 0:
        if (key) {
            t1 = millis();
            ...accendi LED1...
            sA = 1;
        }
    break;

    case 1:
       if (key) {
           if (millis()-t1 >= 2*MINUTE) {
               ...spegni LED1...
               sA = 2;
           }
       } else {
          ...spegni led 1...
          sA = 0;
       }
    break;

    case 2:
        if (!key) sA = 0;
    break;
}

Con questo dovresti avere sufficienti informazioni per completare la codifica del processo A, disegnare i flow del processo B e scrivere la sua codifica, nonché affrontare nello stesso modo qualsiasi altro compito di automazione a stati.
Poi c'è invece la soluzione "ladder logic" che richiede cinque righe in tutto :wink:
1 Like
int chiaveBatt; // Variabile dello stato dell'interruttore
int sA=0; //Variabile dello stato del sistema spia attesa
int sB=0; //Variabile dello stato del sistema spia IR
int spiaAtt;
int spiaIR;
unsigned long t1, t2;

void setup() {
  t1 = millis();
  t2 = millis();
  pinMode(9, OUTPUT); //spia attesa
  pinMode(8, OUTPUT); //spia IR
  pinMode(7, INPUT); //chiave batterie
  
}

void loop() {
chiaveBatt = digitalRead(7); //Verifica se l'interruttore batterie è stato acceso

switch (sA) {
    case 0:
        if (chiaveBatt) {
            t1 = millis();
            digitalWrite(spiaAtt, HIGH);
            sA = 1;
        }
    break;

    case 1:
       if (chiaveBatt) {
           if (millis()-t1 >= 1000) {
               digitalWrite(spiaAtt, LOW);
               sA = 2;
           }
       } else {
          digitalWrite(spiaAtt, LOW);
          sA = 0;
       }
    break;

    case 2:
        if (!chiaveBatt) sA = 0;
    break;
  }
  
}

Ho "pesonalizzato" lo sketch in base al nome delle mie variabili ed ho provato a caricarlo su tinkercad. Ho configurato il circuito con i 2 led e l'interruttore (mi sono portato avanti per lo step 2) ma in simulaizone il led1 non si accende.
Intanto ringrazio tutti per i preziosi consigli e dritte che mi state dando. Sicuramente oggi ho fatto un piccolo passo avanti perchè sto capendo le vostre logiche di programmazione. Il mio progetto prevede un bel pò di combinazioni di interruttori e spie accese/spente con un criterio ben definito ed è stato utilissimo capire come impostare il ragionamento logico schematizzato. Provo a capire dove sto sbagliando per risolvere il problema del led che comuque non si accende.
Allegoun immagine relativa al "banco di prova" dei led con interruttore anche se al momento è presente solo lo sketch che dovrebbe gestire un solo led. Quando avrò risolto il problema allora passerò al secondo led e poi amplierò le istruzioni con altri comandi.

Dov'è che il led si dovrebbe accendere? Qui:

digitalWrite(spiaAtt, HIGH);

Sicuramente no, il perchè te lo ha già detto Mauro nel post #16.

Ciao, Ale.

1 Like

Ti ho indicato perché il led non si accende.

int spiaAtt = 9; 
int spiaIR = 8;

Ciao.