Go Down

Topic: aiuto gestione uscita a tempi ciclo diversi (ON=t1 OFF=t2 ON=t3 OFF=t2 ON=t3) (Read 2005 times) previous topic - next topic

megamarco83

Code: [Select]
const byte OUT_C = 2;
const byte OUT_D = 3;
const byte IN_A = 4;
const byte IN_B = 5;
byte statoA;
byte statoB;
byte statoC;
byte statoD;

unsigned long Tcorr;  //variabile tempo per ingresso A
unsigned long T0;   //variabile di tempo zero per ingresso A
unsigned long T0B;     //variabile di tempo ingresso B
unsigned long TcorrB;    //variabile di tempo zero per in ingresso B
unsigned long I90 = 90 * 60 * 1000UL;   //1.5ore di start
unsigned long I30 = 30 * 60 * 1000UL;   // 30minuti di ON
unsigned long I20 = 20 * 60 * 1000UL;   // 20minuti di PAUSA

int stato;

void setup()
{
  pinMode(OUT_C, OUTPUT);
  pinMode(OUT_D, OUTPUT);
  pinMode(IN_A, INPUT);
  pinMode(IN_B, INPUT);
  // condizioni iniziali
  stato = ATTESA;
  statoC = LOW;
  statoD = LOW;
  T0 = millis();
  T0B = millis();
}

void loop()
{
  Tcorr = millis();
  TcoorB = millis ();
  statoA = digitalRead(IN_A); // Legge ingresso A
  statoB = digitalRead(IN_B);

// definizione stati
  switch (stato)
  {

    case ATTESA:
      if(statoA == LOW)
      {
        statoC = LOW;
      }
      else
      {
        stato = TIME_START; // PASSA ALLA FASE start
        T0 = Tcorr;     // Azzera il contatore
      }

      if (statoB == LOW)
      {
        statoD = LOW;
      }
      else
      {
        stato = TIME_START;
        T0B  = TcorrB;
      }
      break;

    case TIME_START:
      if(statoA == LOW)
      {
        statoC = LOW;
        stato = ATTESA;
      }
      else
      {
        if (Tcorr - T0 >= I90)
        {
          T0 = Tcorr;   // Azzera contatore
          stato = TIME_OFF;
        }
        else
        {
          statoC = HIGH;
        }

        if(statoB == LOW)
      {
        statoD = LOW;
        stato = ATTESA;
      }
      else
      {
        if (TcorrB - T0B >= I90)
        {
          T0B = TcorrB;   // Azzera contatore
          stato = TIME_OFF;
        }
        else
        {
          statoD = HIGH;
        }

      }
      break;


    case TIME_OFF:
      if(statoA == LOW)
      {
        statoC = LOW;
        stato = ATTESA;
      }
      else
      {
        if (Tcorr - T0 >= I20)
        {
          T0 = Tcorr; // Azzera contatore
          stato = TIME_ON;
        }
        else
        {
          statoC = LOW;
        }

        if(statoB == LOW)
      {
        statoD = LOW;
        stato = ATTESA;
      }
      else
      {
        if (TcorrB - T0B >= I20)
        {
          T0B = TcorrB; // Azzera contatore
          stato = TIME_ON;
        }
        else
        {
          statoD = LOW;
        }
      }
      break;


    case TIME_ON:
      if(statoA == LOW)
      {
        statoC = LOW;
        stato = ATTESA;
      }
      else
      {
        if (Tcorr - T0 >= I30)
        {
          T0 = Tcorr; // Azzera contatore
          stato = TIME_OFF;
        }
        else
        {
          statoC = HIGH;
        }
   
      if(statoB == LOW)
      {
        statoD = LOW;
        stato = ATTESA;
      }
      else
      {
        if (TcorrB - T0B >= I30)
        {
          T0B = TcorrB; // Azzera contatore
          stato = TIME_OFF;
        }
        else
        {
          statoD = HIGH;
        }
      }
      break;

    default:
      break;

  } // End switch

  digitalWrite(OUT_C, statoC);
  digitalWrite(OUT_D, statoD);

}  // End loop


eccomi tornato a rompere le scatole :)
.....allora ieri ho stampato su carta il codice ed ho provato a debuggarlo a penna, segnandomi un impotetico valore delle variabili e poi ciclando, segnando ad ogni passaggio il cambiamento del loro valore, in modo da fare il giro di tutto il loop....
penso di avere capito...anche se ammetto che con le parentesi mi incasino sempre....all'inizio riuscivo a capire dove andare, solo perchè fiducioso che il programma fosse scritto correttamente, capivo a posteriori dove dovevo andare....
esiste un "trucchetto" per capire come "rimbalzare tra le istruzioni" ? :)

cmq alla fine mi sono fatto l'idea che per implementare anche ingresso B e uscita D
mi servono altre due variabili, per non mischiare i tempi con la variabile ingresso A e uscita C

come dicevo i due ingressi e le due uscite "vivono di vita proprio" stessa identica logica, ma possono avere due stati diversi.

ho creato quindi la variabile T0B che sarebbe la variabile per azzerare il contatore del tempo di B
e TcorrB che sarebbe la variabile che uso per segnare lo scorrere del tempo di B

il dubbio che mi viene....è poi arduino quando esegue il programma, se riesce a stare in due stati diversi....
esempio A=HIGH da 2ore....quindi si trova nello stato TIME_OFF, ora succede che B=HIGH quindi deve entrare nello stato TIME_START
avrò A con le sue variabili che cicla in TIME_OFF e B con le sue variabili che cicla in TIME_START
...io credo sia possibile, o mi sbaglio?
grazie ancora!


PaoloP

Ho scritto il codice di getto quindi ci potrebbe essere qualche errore di logica.
Per fare le cose semplici di conviene duplicare il codice dello switch creando una doppia macchina a stati: una per la coppia A-C e l'altra per la coppia B-D.
Ovviamente dovrai duplicare tutte le variabili, anche stato. Potresti creare statoAC e statoBD da usare come ingressi nei 2 switch.

Nel codice postato mancano i #define. Non compila.

megamarco83

Code: [Select]
#define OUT_C 2
#define OUT_D 3
#define IN_A 4
#define IN_B 5
#define ATTESA 0
// 1.5ORE PRIMA ATTIVAZIONE
#define TIME_START 5400
// 30MIN DI STATO ON (LAVORO)
#define TIME_ON 1800
// 20MIN DI STATO PAUSA (OFF)
#define TIME_OFF 1200


sono solo questi i define da dare?
ma avendo già dichiarato le variabili di ingresso ed uscita ad esempio:
Code: [Select]
const byte OUT_C = 2;
non bastava?

ad esempio io pensavo che se volessi cambiare il tempo di start e passare a due ore mi bastava modificare questo
unsigned long I90 = 90 * 60 * 1000UL;   //1.5ore di start
con questo:
unsigned long I90 = 120 * 60 * 1000UL;   //2ore di start

quali sono le variabili che vanno nel #define?
ed infine.... quando scrivo
#define TIME_START 5400
sto dando il valore in secondi...è corretto, oppure devo darlo in millisecondi?


avevo pensato di dulicare il codice
però ho pensato che avrei appunto dovuto creare una variabile statoAC che poteva assumere gli stati di switch: ATTESA_A ; TIME_START_A ; TIME_OFF_A ; TIME_ON_A
e poi creare una variabile statoBD che nello switch diventava: ATTESA_B; TIME_START_B ; TIME_OFF_B; TIME_ON_B

ma mi semrbava un po' uno "spreco"
a logica non dovrebbe funzionare il programma da me postato? :)

c'è qualcuno che può ed ha voglia di  fare un debug via pc, io non ho nemmeno arduino...lo sto aspettando :)


PaoloP

Allora per prima cosa meglio ripassare un po' di linguaggio C.
--> http://www.html.it/guide/guida-c/

Poi forse se attendi l'arrivo di Arduino potrai fare le prove dal vivo.

Comunque se installi l'IDE 1.6.5 puoi iniziare a stendere il programma e verificarlo. Se già non compila vuol dire che ci sono errori di sintassi. Se compila potrebbero comunque esserci errori di logica.

Se al momento non puoi installare il programma puoi usare un ambiente web per sviluppare il codice. Io uso Codebender e mi trovo bene. Vedi il link nella mia firma.
Ad esempio il codice postato da me l'ho scritto tramite codebender (https://codebender.cc/sketch:139471)

#define e variabili hanno due funzioni diverse anche se possono essere usati per lo stesso scopo.

megamarco83

ehehe ho usato il link nella tua firma proprio per verificare il codice...e coni define li compila :)

anzi sempre usando il tool della pagina web ho provato a scrivere il codice con gli switch duplicati....e me lo compila...
te lo posto, magari puoi dare un occhio se ho scritto castronerie?

Code: [Select]
// definizione stati
#define ATTESA 0
#define TIME_START 5400  // 1.5ORE PRIMA ATTIVAZIONE
#define TIME_ON 1800  // 30MIN DI STATO ON (LAVORO)
#define TIME_OFF 1200 // 20MIN DI STATO PAUSA (OFF)

#define ATTESA_B 0
#define TIME_START_B 5400
#define TIME_ON_B 1800
#define TIME_OFF_B 1200

const byte OUT_C = 2;
const byte OUT_D = 3;
const byte IN_A = 4;
const byte IN_B = 5;
byte statoA;
byte statoB;
byte statoC;
byte statoD;

unsigned long Tcorr;
unsigned long T0;
unsigned long TcorrB;
unsigned long T0B;
unsigned long I90 = 90 * 60 * 1000UL;
unsigned long I30 = 30 * 60 * 1000UL;
unsigned long I20 = 20 * 60 * 1000UL;

int stato;
// unsigned int fsm_state;

void setup()
{
pinMode(OUT_C, OUTPUT);
pinMode(OUT_D, OUTPUT);
pinMode(IN_A, INPUT);
pinMode(IN_B, INPUT);
// condizioni iniziali
stato = ATTESA;
statoC = LOW;
statoD = LOW;
T0 = millis();
}

void loop()
{
Tcorr = millis();
statoA = digitalRead(IN_A); // Legge ingresso A
statoB = digitalRead(IN_B);

// definizione stati
switch (stato)
{

case ATTESA:
if(statoA == LOW)
{
statoC = LOW;
}
else
{
stato = TIME_START; // PASSA ALLA FASE start
T0 = Tcorr; // Azzera il contatore
}
break;

case TIME_START:
if(statoA == LOW)
{
statoC = LOW;
stato = ATTESA;
}
else
{
if (Tcorr - T0 >= I90)
{
T0 = Tcorr; // Azzera contatore
stato = TIME_OFF;
}
else
{
statoC = HIGH;
}
}
break;


case TIME_OFF:
if(statoA == LOW)
{
statoC = LOW;
stato = ATTESA;
}
else
{
if (Tcorr - T0 >= I20)
{
T0 = Tcorr; // Azzera contatore
stato = TIME_ON;
}
else
{
statoC = LOW;
}
}
break;

case TIME_ON:
if(statoA == LOW)
{
statoC = LOW;
stato = ATTESA;
}
else
{
if (Tcorr - T0 >= I30)
{
T0 = Tcorr; // Azzera contatore
stato = TIME_OFF;
}
else
{
statoC = HIGH;
}
}
break;

default:
break;

} // End switch













// definizione stati
switch (stato)
{

case ATTESA_B:
if(statoB == LOW)
{
statoD = LOW;
}
else
{
stato = TIME_START_B; // PASSA ALLA FASE start
T0B = TcorrB; // Azzera il contatore
}
break;

case TIME_START_B:
if(statoB == LOW)
{
statoD = LOW;
stato = ATTESA_B;
}
else
{
if (TcorrB - T0B >= I90)
{
T0B = TcorrB; // Azzera contatore
stato = TIME_OFF_B;
}
else
{
statoD = HIGH;
}
}
break;


case TIME_OFF_B:
if(statoB == LOW)
{
statoD = LOW;
stato = ATTESA_B;
}
else
{
if (TcorrB - T0B >= I20)
{
T0B = TcorrB; // Azzera contatore
stato = TIME_ON_B;
}
else
{
statoD = LOW;
}
}
break;

case TIME_ON_B:
if(statoB == LOW)
{
statoD = LOW;
stato = ATTESA;
}
else
{
if (TcorrB - T0B >= I30)
{
T0B = TcorrB; // Azzera contatore
stato = TIME_OFF_B;
}
else
{
statoD = HIGH;
}
}
break;

default:
break;

} // End switch


digitalWrite(OUT_C, statoC);
digitalWrite(OUT_D, statoD);

}  // End loop


infine ho un dubbio sul funzionamento....il fatto di definire all'inizio con il define ad esempio il
TIME_START 5400, che implicazione ha nel programma?
il controllo per rimanere 1.5ore lo fa
 if (Tcorr - T0 >= I90)
ed all'inzio del programma ho definito
 unsigned long I90 = 90 * 60 * 1000UL;
quindi è qui che imposto 1.5ore....che ruolo ha il define TIME_START 5400 all'inzio?
grazie!

PaoloP

I #define TIME_START ecc non hanno nessuna funzione sull'intervallo di tempo. Servono solo come codice identificativo dello stato della macchina a stati per lo switch case.
Puoi sostituire 5400, 1800 e 1200 anche con 1, 2 e 3. Funziona ugualmente.

C'è un errore. Non puoi usare stato per entrambi gli switch case.
Devi rinominare stato in statoAC e l'altro in statoBD o un qualunque altro nome ti venga in mente. Le variabili devono essere diverse.

megamarco83

Ciao Paolo, cavolo hai ragione, altrimenti non sa in quale stato andare....grazie!
inoltre mi ero dimenticato di azzerare nel void main() le variabili del tempo T0B e TcorrB

ora ho modificato il codice....lo riposto qui:

Code: [Select]
// definizione stati
#define ATTESA 0
#define TIME_START 5400  // definizione PRIMA ATTIVAZIONE
#define TIME_ON 1800  // definizione STATO ON (LAVORO)
#define TIME_OFF 1200 // definizione STATO PAUSA (OFF)

#define ATTESA_B 0
#define TIME_START_B 5400
#define TIME_ON_B 1800
#define TIME_OFF_B 1200

const byte OUT_C = 2;
const byte OUT_D = 3;
const byte IN_A = 4;
const byte IN_B = 5;
byte statoA;
byte statoB;
byte statoC;
byte statoD;

unsigned long Tcorr;
unsigned long T0;
unsigned long TcorrB;
unsigned long T0B;
unsigned long I90 = 90 * 60 * 1000UL;
unsigned long I30 = 30 * 60 * 1000UL;
unsigned long I20 = 20 * 60 * 1000UL;

int statoAC;
int statoBD;


void setup()
{
pinMode(OUT_C, OUTPUT);
pinMode(OUT_D, OUTPUT);
pinMode(IN_A, INPUT);
pinMode(IN_B, INPUT);
// condizioni iniziali
statoAC = ATTESA;
statoBD = ATTESA_B;
statoC = LOW;
statoD = LOW;
T0 = millis();
T0B = millis();
}

void loop()
{
Tcorr = millis();
TcorrB = millis();
statoA = digitalRead(IN_A); // Legge ingresso A
statoB = digitalRead(IN_B);

// definizione stati
switch (statoAC)
{

case ATTESA:
if(statoA == LOW)
{
statoC = LOW;
}
else
{
statoAC = TIME_START; // PASSA ALLA FASE start
T0 = Tcorr; // Azzera il contatore
}
break;

case TIME_START:
if(statoA == LOW)
{
statoC = LOW;
statoAC = ATTESA;
}
else
{
if (Tcorr - T0 >= I90)
{
T0 = Tcorr; // Azzera contatore
statoAC = TIME_OFF;
}
else
{
statoC = HIGH;
}
}
break;


case TIME_OFF:
if(statoA == LOW)
{
statoC = LOW;
statoAC = ATTESA;
}
else
{
if (Tcorr - T0 >= I20)
{
T0 = Tcorr; // Azzera contatore
statoAC = TIME_ON;
}
else
{
statoC = LOW;
}
}
break;

case TIME_ON:
if(statoA == LOW)
{
statoC = LOW;
statoAC = ATTESA;
}
else
{
if (Tcorr - T0 >= I30)
{
T0 = Tcorr; // Azzera contatore
statoAC = TIME_OFF;
}
else
{
statoC = HIGH;
}
}
break;

default:
break;

} // End switch






// definizione stati
switch (statoBD)
{

case ATTESA_B:
if(statoB == LOW)
{
statoD = LOW;
}
else
{
statoBD = TIME_START_B; // PASSA ALLA FASE start
T0B = TcorrB; // Azzera il contatore
}
break;

case TIME_START_B:
if(statoB == LOW)
{
statoD = LOW;
statoBD = ATTESA_B;
}
else
{
if (TcorrB - T0B >= I90)
{
T0B = TcorrB; // Azzera contatore
statoBD = TIME_OFF_B;
}
else
{
statoD = HIGH;
}
}
break;


case TIME_OFF_B:
if(statoB == LOW)
{
statoD = LOW;
statoBD = ATTESA_B;
}
else
{
if (TcorrB - T0B >= I20)
{
T0B = TcorrB; // Azzera contatore
statoBD = TIME_ON_B;
}
else
{
statoD = LOW;
}
}
break;

case TIME_ON_B:
if(statoB == LOW)
{
statoD = LOW;
statoBD = ATTESA;
}
else
{
if (TcorrB - T0B >= I30)
{
T0B = TcorrB; // Azzera contatore
statoBD = TIME_OFF_B;
}
else
{
statoD = HIGH;
}
}
break;

default:
break;

} // End switch


digitalWrite(OUT_C, statoC);
digitalWrite(OUT_D, statoD);

}  // End loop


....un ultimo dubbio a riguardo della struttura del codice:
in che fase esce dallo stato in cui si trova per andare a fare il digitalwrite?

digitalWrite(OUT_C, statoC);
digitalWrite(OUT_D, statoD);

ipotizzando l'avvio del programma queste due operazioni quando vengono raggiunte la prima volta?
supponiamo che statoA == LOW; in questo modo il primo if del case ATTESA pone statoC= LOW;
l'a funzione else di attesa non viene presa in considerazione, quindi cosa succede.....salta tutti gli altri case....arriva fino a
default:
  break;
} // End switch
quindi trova il break, esce dal case ATTESA
e va a fare:
digitalWrite(OUT_C, statoC);
digitalWrite(OUT_D, statoD);
quindi ritorna allo stato ATTESA e ricomincia la verifica dell'ingresso e così via?
ho interpretato bene?

perchè ho il dubbio che
digitalWrite(OUT_C, statoC);
digitalWrite(OUT_D, statoD);
vadano messi in tutti e due gli switch sia in fondo a switch (statoAC) che switch (statoBD)
mentre io li ho messi solo alla fine del programma




PaoloP

Se trova un break salta subito alla fine dello switch.
l'opzione default: l'ho messa in caso di problemi, in pratica se l'argomento dello switch trova un caso non elencato salta al caso default. Se tutti i casi possibili sono corretti, default e il successivo break si possono cancellare.

Per come ho strutturato il programma i digitalWrite vanno posti alla fine del loop.
Puoi anche cambiare la logica del programma se vuoi. Quello scritto da me è solo una delle possibile soluzioni al tuo quesito.


megamarco83

Se trova un break salta subito alla fine dello switch.
una volta che arriva al break va alla fine dello switch, quindi arriva qui:
} // End switch

digitalWrite(OUT_C, statoC);
digitalWrite(OUT_D, statoD);
con queste due righe attiva o disattiva le uscite, e poi torna all'ingresso dello stesso stato in cui si trovava prima di arrivare al break, riesegue i controlli di quello stato e così via?




se è così....allora devo aggiungere alla fine dei vari casi di statoAC

default:
break;

} // End switch

digitalWrite(OUT_C, statoC);
digitalWrite(OUT_D, statoD);
  [questa parte non c'era nel codice che ho postato prima]

// definizione stati per BD
switch (statoBD)
{
case ATTESA_B:
                  if(statoB == LOW)
ecc ecc....




ed alla fine dei vari casi dello statoBD
                 default:
break;

} // End switch

digitalWrite(OUT_C, statoC);
digitalWrite(OUT_D, statoD);

}  // End loop


giusto?

vittorio68

Ciao,

non è necessario aggiungere le due digitalWrite alla fine del primo switch. Basta averle messe entrambe dopo il secondo switch.

Infatti il primo switch non modifica statoD mentre il secondo switch non modifica statoC. In pratica stai eseguendo prima gli switch che calcolano i nuovi valori di statoC e statoD e poi alla fine li usi entrambi.

Al limite potresti mettere la digitalWrite che usa statoC dopo il primo switch e la digitalWrite che usa statoD dopo il secondo switch. Ma non cambia nulla.

Ciao.
Vittorio.




vittorio68

scusa... ancora una cosa...

ho notato che per i valori delle #define TIME_START, TIME_ON e TIME_OFF hai usato il valore in secondi della durata dei rispettivi stati.

In realtà questo non serve. La durata infatti è memorizzata in I90, I30, I20 ed infatti sono queste le variabili che usi negli if per verificare se è passato il tempo richiesto.

Il tuo codice funziona perché i valori utilizzati per i quattro stati sono tutti diversi tra loro. Ho però il dubbio che questo ti stia confondendo impedendoti di capire realmente come funziona il programma.

In uno switch viene eseguito il pezzo di codice che si trova dopo il case il cui valore coincide con quello della variabile di controllo dello switch stesso (nel tuo caso statoAC o statoBD). In verità vengono eseguiti tutti i case che si trovano dopo il primo che matcha. Per questo motivo si usa mettere il break alla fine di ogni case, in modo cioè da interrompere l'esecuzione dei case successivi.

In conclusione puoi usare ATTESA=0, TIME_START=1, TIME_ON=2, TIME_OFF=3.

Per inciso... non serve che li differenzi per il caso AC e BD tanto i valori sono uguali.

Scusami se posso sembrare pedante, lo dico solo perché tu possa comprendere veramente quello che stai facendo.

Ciao.
Vittorio.

megamarco83

Ciao Vittorio, grazie anche a te!
quello che fate mi aiuta a capire quello che faccio, anche perchè non avendo ancora a casa arduino...me lo hanno spedito, ma mi sa che potrò metterci mano dopo le ferie ormai...
cmq senza arduino non posso nemmeno verificare che funizoni il tutto, non posso provarlo o fare debug....unfatti me lo sto facendo a mano, ipotizzando le veriabili e poi segnando a penna lungo il codice il loro cambiamento...

per quanto rigaurda i valori dei define è vero, me lo aveva già detto anche Paolo

mentre per lo switch mi confonde ancora un po' i "salti" che fa nel codice...

cioè se analizzo il caso più rapido, il primo, in cui ingresso di A=0 in questo modo il primo if del case ATTESA pone statoC= LOW;




// definizione stati
  switch (statoAC)
  {

    case ATTESA:
      if(statoA == LOW)
      {
        statoC = LOW;
      }
      else
      {
        statoAC = TIME_START; // PASSA ALLA FASE start
        T0 = Tcorr;     // Azzera il contatore
      }
      break;



la funzione else di ATTESA non viene presa in considerazione, quindi cosa succede..... va al break rosso giusto?

ed il break rosso dove lo porta? alla chiusura dell'utima parentesi graffa qui sotto....in qui c'è l'end switch?

default:
  break;
} // End switch

poichè poi qui trova:

digitalWrite(OUT_C, statoC);
digitalWrite(OUT_D, statoD);

quindi fa la scrittura dell'uscita.....e ritorna allo stato ATTESA e ricomincia la verifica dell'ingresso e così via?
ho interpretato bene?


mi trovo sempre in difficoltà nel capire l'inizio e la fine di un comando o di un ciclo...faccio fatica a seguire le parentesi insomma...

vittorio68

Tranquillo se non hai ancora la possibilità di eseguire praticamente il codice (mancanza di Arduino) le perplessità sono normali. Vedrai che poi tutto sarà più chiaro appena potrai smanettare praticamente.

Il break ti porta alla fine dello switch(statoAB). Qui prosegue entrando nello switch(statoBD) e poi arriva alle digitalWrite.

Qui la loop finisce e sostanzialmente ricomincia da capo riverificando gli input.

Go Up