Problema con millis()

Ciao a tutti!

Sto realizzando un controllo per lavatrice con Arduino e sto avendo difficoltà con una funzione.

Ho riutilizzato in parte il codice del topic “Invertire motore”, ma ora devo fare in modo che la procedura duri per un lasso di tempo specifico (es 30 minuti). Ho quindi riscritto il codice in questo modo, ma non si ferma mai. Qualcuno che può aiutarmi?

#define MotAvanti 4
#define MotIndietro 5


byte flagstato = 0;
bool giraaDestra = false;
unsigned long lavoro = 0; //tempo per cui gira il motore
unsigned long pausa = 0; //tempo pausa
unsigned long tempoCorrente = 0;
unsigned long tempoTot = 0;



void setup()
{ 
  pinMode(MotAvanti, OUTPUT);
  pinMode(MotIndietro, OUTPUT);

}


void loop()
{
  
    Agitazione(30000, 5000, 3000, millis());

}


//Procedura di rotazione del cestello con inversione e tempo totale dell'operazione
void Agitazione(unsigned long tempoTot, unsigned long lavoro, unsigned long pausa, unsigned long tempoInizio)
{

  if (millis() - tempoInizio <= tempoTot)
  {
    if (flagstato == 0)                           //se stato a zero
    {
      if (giraaDestra == false)                 //e la rotazione a destra non è abilitata
      {
        digitalWrite(MotAvanti, HIGH);           //allora giri a sinistra
      }
      else
      {
        digitalWrite(MotIndietro, HIGH);             //se la rotazione a destra è abilitata, ruota a destra
      }
      if (millis() - tempoCorrente >= lavoro)     //se il tempo di lavoro è già trascorso
      {
        if (giraaDestra == false)                 //quando dobbiamo spegnere il motore
        {
          digitalWrite(MotAvanti, LOW);            //se stava girando a sinistra, spegniamo sinistra

        }
        else
        {
          digitalWrite(MotIndietro, LOW);              //se stava girando a destra, spegniamo destra
        }
        flagstato = 1;                            //imposta stato ad 1
        tempoCorrente = millis();                 //resetta tempo
      }
    }
    else if (flagstato == 1)                      //se stato ad 1
    {
      if (millis() - tempoCorrente >= pausa)      //se il tempo di pausa è già trascorso
      {
        tempoCorrente = millis();                 //resetta tempo
        flagstato = 0;                            //imposta stato a 0
        if (giraaDestra == false)                 //se il motore stava girando a sinistra
        {
          giraaDestra = true;                     //abilitiamo giraaDestra per farlo girare a destra
        }
        else
        {
          giraaDestra = false;                    //se stava girando a destra, disabilitiamo giraaDestra per farlo girare a sinistra
        }
      }
    }
  }
}

Il codice funziona nel senso che inverte il motore correttamente, ma quando si tratta di finire, continua ad andare avanti. Ho messo 30 secondi per la durata totale, 5 per il lavoro e 3 per la pausa, ma non so come faccia a non andare.

Grazie in anticipo

Non trovo né la dichiarazione né la definizione di tempoInizio.

Ops…non lo ho ricopiato qui, ma quando l’ho testato lo avevo fatto e non andava comunque.

Ecco qua:

#define MotAvanti 4
#define MotIndietro 5


byte flagstato = 0;
bool giraaDestra = false;
unsigned long lavoro = 0; //tempo per cui gira il motore
unsigned long pausa = 0; //tempo pausa
unsigned long tempoCorrente = 0;
unsigned long tempoTot = 0;
unsigned long tempoInizio=0;



void setup()
{ 
  pinMode(MotAvanti, OUTPUT);
  pinMode(MotIndietro, OUTPUT);

}


void loop()
{
  
    Agitazione(30000, 5000, 3000, millis());

}


//Procedura di rotazione del cestello con inversione e tempo totale dell'operazione
void Agitazione(unsigned long tempoTot, unsigned long lavoro, unsigned long pausa, unsigned long tempoInizio)
{

  if (millis() - tempoInizio <= tempoTot)
  {
    if (flagstato == 0)                           //se stato a zero
    {
      if (giraaDestra == false)                 //e la rotazione a destra non è abilitata
      {
        digitalWrite(MotAvanti, HIGH);           //allora giri a sinistra
      }
      else
      {
        digitalWrite(MotIndietro, HIGH);             //se la rotazione a destra è abilitata, ruota a destra
      }
      if (millis() - tempoCorrente >= lavoro)     //se il tempo di lavoro è già trascorso
      {
        if (giraaDestra == false)                 //quando dobbiamo spegnere il motore
        {
          digitalWrite(MotAvanti, LOW);            //se stava girando a sinistra, spegniamo sinistra

        }
        else
        {
          digitalWrite(MotIndietro, LOW);              //se stava girando a destra, spegniamo destra
        }
        flagstato = 1;                            //imposta stato ad 1
        tempoCorrente = millis();                 //resetta tempo
      }
    }
    else if (flagstato == 1)                      //se stato ad 1
    {
      if (millis() - tempoCorrente >= pausa)      //se il tempo di pausa è già trascorso
      {
        tempoCorrente = millis();                 //resetta tempo
        flagstato = 0;                            //imposta stato a 0
        if (giraaDestra == false)                 //se il motore stava girando a sinistra
        {
          giraaDestra = true;                     //abilitiamo giraaDestra per farlo girare a destra
        }
        else
        {
          giraaDestra = false;                    //se stava girando a destra, disabilitiamo giraaDestra per farlo girare a sinistra
        }
      }
    }
  }
}

OK. Probabilmente ti manca solo di fermare il motore una volta trascorso il tempo.

Ciao, mi permetto un paio di consigli: utilizza un relè per on/off, l'altro per dx e sx così eviti problemi di concomitanza di uscite e faciliti la stesura del codice.
Poi il problema mi pare sia che prima spegni così:

          digitalWrite(MotIndietro, LOW);              //se stava girando a destra, spegniamo destra
        }
        flagstato = 1;                            //imposta stato ad 1
        tempoCorrente = millis();                 //resetta tempo

ma al ciclo successivo riparte perchè trova questo:

if (millis() - tempoCorrente >= lavoro)     //se il tempo di lavoro è già trascorso

Ti consiglio di dichiarare un array bidimensionale (o 2 array semplici), in uno metti il tempo, nell'altro la direzione
Ogni volta che sono trascorsi i millis nell'array incrementi il puntatore e passi al successivo. Nel frattempo fai girare il motore.
Qui un esempio che avevo fatto per una sirena (c'erano 3 sequenze selezionabili.... potrebbe servirti per fare il programma "delicati" e "sintetici" ) ;D

Stefano

Purtroppo ho già realizzato la PCB e non posso modificarla. Non si può modificare il codice che ho scritto?

Oh, il discorso dei relè è diverso dal codice, purtroppo trovo estremamente complesso quello che hai scritto.... mi muovo + facilmente con il mio.
Intanto verifica se l'obiezione che ti ho fatto (ovvero che dopo aver fermato il motore, riparte il ciclo successivo) è corretta, poi trova il modo di bloccare, magari ponendo ad 1 un flag "stop" che una volta alzato non permette al motore di ripartire se non ripremendo il pulsante che azzera il flag stop

Secondo me la tua obiezione non è corretta, @cam9500, perché quell’if è sotto all’altro:

if (millis() - tempoInizio <= tempoTot)

Per cui non verrà più eseguito, una volta scaduto tempoTot.

Secondo me, come già detto sopra, il problema è che trascorso tempoTot tutta la funzione Agitazione() (perché maiuscola?!?) viene praticamente saltata, e se il motore stava girando niente lo fermerà più.

Caspita, può essere… però Agitazione è chiamata così

 Agitazione(30000, 5000, 3000, millis());

ma i parametri corrispondono a:

void Agitazione(tempoTot, lavoro, pausa,  tempoInizio)

quindi

if (millis() - tempoInizio <= tempoTot)

sarà:

millis() -  millis() <= tempoTot

sempre vero… o no?

Ahhh sì, non avevo notato che la chiamava con millis() come ultimo argomento! Questo ovviamente non ha senso, in tempoInizio deve finirci il tempo di inizio del tutto, non va cambiato ad ogni chiamata.

....per me è un modo "strano" :slight_smile:

....per me è un modo "strano" :slight_smile:

Mattia9914 sembra un principiante, per cui ignora ciò che manca e ciò che serve. Al momento il suo intento è stato quello di testare il motore e da questo codice di test vorrebbe derivare il resto del programma.

Grazie alle mie precedenti esperienze posso dire che manca l'architettura software.

Un programma di lavaggio è composto da un certo numero di fasi (o cilci), alcuni di questi sono comuni a tutti i programmi di lavaggio, altri sono legati al programma specifico e alcuni di questi possono essere inseriti e/o rimossi, altri no. Un esempio tipico è il ciclo di prelavaggio, solo alcuni programmi di lavaggio prevedono il prelavaggio, il quale può essere attivo in modo predefinito e può o meno essere reso non attivo dall'utente, oppure in modo predefinito è non attivo ed attivabile da utente.

Ogni ciclo o fase dopo essere stata completata si autorimuove, ciò significa che dopo avere premuto "pause" e poi "play" il ciclo autorimosso non viene eseguito, quindi significa anche che viene eseguito (e completato) una sola volta per programmazione del lavaggio. Questo ultima frase "per programmazione del lavaggio" ci fornisce lo spunto per definire uno degli stati in cui si trova l'applicazione: washingProgramState = unProgrammedState | programmedState.

Se si decide che nello stato [programmedState] si può modificare il programma selezionato o cambiarlo le cose si complicano un poco, mentre si semplificano se si da la possibilità di abortire il programma selezionato.

Un ciclo o fase può essere implementato come funzione, il puntatore a questa viene posto in un membro di struttura nella fase di selezione del programma, dopo l'avvio tramite pressione "play" arriverà il momento di avviare la funzione membro di struttura, dopo l'avvio e il completamento della fase la funzione stessa scrive 0 (zero) nel membro di struttura, questo realizza l'autorimozione accennata prima. Ovviamente la funzione sarà avviata solo se il membro di struttura è diverso da 0 (zero).

Tutto questo fin qui rappresenta solo la minima parte di una applicazione embedded per la gestione della washing machine, iniziare il design prevedendo tutto dall'inizio è sconsigliato, per cui un minimo di infrastruttura serve ma senza esagerare. Per iniziare io consiglio di implementare washingProgramState.

Spero di non avere esagerato con il dettaglio.

Ciao.

Certamente sono un principiante e questo è il mio primo progetto "grosso" con Arduino, ma comunque ho già pensato alla struttura totale del programma.
Nel mio caso, ho pensato a scorporare ogni singola funziona (es. Lavaggio) in altre "sottofunzioni": invece di scrivere l'intera fase, preferisco fare una funzione per il carico dell'acqua, una per l'agitazione, una per il riscaldamento e così via. Questo mi permette, secondo il mio ragionamento, di avere una agitazione più lenta durante il riscaldamento, per esempio. Le sottofunzioni vengono poi richiamatre in tempi e luoghi diversi del codice (es. risciacquo richiama carico acqua, agitazione lavoro 7 pausa 5, scarico acqua dopo X minuti).
Il problema è che non riesco a far fermare il motore alla fine del ciclo if: ho già provato anche con un while, ma nulla da fare. Il problema è che non posso nemmeno modificare la PCB perchè risulterebbe macchinoso e poco funzionale. Proprio per questo voglio trovare un modo per risolvere questo problema.

Ringrazio in anticipo chi potrà aiutarmi, magari con anche qualche sketch.

Ma tutto quello che abbiamo scritto io e cam hai provato almeno a leggerlo e rifletterci??? >:(

Certo!
Ma essendo un principiante non sono riuscito a capirci molto di array bidimensionali e di ProgrammedState

Cose di cui nessuno di noi due ha mai parlato…

Mattia9914:
. Il problema è che non posso nemmeno modificare la PCB perchè risulterebbe macchinoso e poco funzionale. Proprio per questo voglio trovare un modo per risolvere questo problema.

Ringrazio in anticipo chi potrà aiutarmi, magari con anche qualche sketch.

Ciao, modificando il programma non è detto che si modifichi il pcb (circuito stampato.... al maschile va meglio :slight_smile: ). Anzi, per il suggerimento dei relè probabilmente si ma è un consiglio che puoi anche non seguire, fai solo attenzione ai timing che non ti ritrovi dei problemi per uno scambio troppo veloce.
Lo sketch che ti ho suggerito non è banalissimo ma l'unica cosa da imparare è un array, nulla di complicato. Un'oretta e ti metti a ridere di come è semplice ed efficace
Stefano

Grazie mille cam9500. Purtroppo il tuo link non sembra funzionare, ma mi sembra di averlo già visto in un altra discussione.
Comunque l'inversione funziona perfettamente, è solo che dopo il periodo di tempo X l'inversione continua ad essere effettuata. Proverò comunque ad intraprendere la tua soluzione appena potrò riconnettermi.

Grazie ancora.

SukkoPera:
Questo ovviamente non ha senso, in tempoInizio deve finirci il tempo di inizio del tutto, non va cambiato ad ogni chiamata.

SukkoPera:
Secondo me, come già detto sopra, il problema è che trascorso tempoTot tutta la funzione Agitazione() (perché maiuscola?!?) viene praticamente saltata, e se il motore stava girando niente lo fermerà più.

Ma queste cose le hai capite?!?!?!?!?! :stuck_out_tongue_closed_eyes:

Onestamente non ho capito quello che ha scritto Sukko Pera, anche perché preferirei avere un esempio. Se riuscissi ad averlo ne sarei grato.