Go Down

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

SukkoPera

Bravo @vittorio68! Questo è quel che ci voleva!

@megamarco83: è impossibile arrivare a scrivere del codice sensato per caso, senza avere in testa l'idea precisa di quel che vuoi fare. Se non riesci a metterlo nemmeno su carta, come pensi di poter scrivere un programma? Prova a segui lo spunto di Vittorio e vai avanti!
"Code is read much more often than it is written, so plan accordingly. Design for readability."

Guida rapida a ESP8266: https://goo.gl/kzh62E

megamarco83

ciao SukkoPera in realtà io in testa ho bene in mente cosa fare!
infatti lo ho scritto sia nel flowchart, che "in prosa"
non riesco a metterlo giù usando la simbologia ch emi suggerivi dei rettangoli con gli stati!!!
ma in testa ho bene in mente come fare... :)
non so se più o meno è chiaro gardando al flowchart oppure a quello che ho scritto oggi diciamo "in prosa".....
l'esempio di Vittorio68 mi ha cmq chiarito abbastanza...ora rispondo anche a lui, poichè negli stati che ha scritto ad esempio, secondo me, manca il controllo continuo, in ogni stato, ed in ogni fase di tempo trascorso, del valore dell'ingresso.....perchè se in un qualsiasi stato, ed in qualsiasi condizione mi trovo A=0 devo avere l'uscita C=0. Ed è proprio questo che mi confonde e complica tutto...anche se credo che il flowchart che ho messo giù è corretto...
ad ogni modo ora provo a rispondere anche a Vittorio
Mi scuso con tutti voi se uso termini non adatti e molto probabilmente dico cose sbagliate...ma come dicevo...mi sto avvicinando ora alla programmazione...chiedo venia... :)

SukkoPera

Senza offesa, forse hai in testa il cosa, ma non tanto il come :). Questo perché il "come" è legato alle tecniche di programmazione che evidentemente ancora non conosci (Ti abbiamo detto di implementare una macchina a stati, il flowchart non basta!). Comunque no problem, è così che si impara: avendo un obiettivo e smanettando fino a raggiungerlo, documentandosi e sperimentando.

Quanto al diagramma di Vittorio, mi sembra che quel che dici venga contemplato, comunque in generale basta aggiungere stati fino ad aver coperto tutti i casi.
"Code is read much more often than it is written, so plan accordingly. Design for readability."

Guida rapida a ESP8266: https://goo.gl/kzh62E

megamarco83

no ma figurati, quale offesa, anzi graize che perdete tempo ad aiutarmi!!
anche percè è proprio così come dici tu!!!
cioè, ho benissimo in testa cosa voglio....mentre il come arrivarci è piuttosto nebuloso :)
del diagramma di vittorio non mi torna questo:

Proviamo a pensare insieme agli stati:

- Stato ATTESA: se A = 0 non succede nulla; se A = 1 metti C = 1 e passi nello stato TIME90 (90 minuti = 1,5 ore)

- Stato TIME90: Se A = 0 metti C = 0 e torni nello stato ATTESA; se A = 1 incrementi la variabile secondiTrascorsi; se secondiTrascorsi < 5400 non fai nulla, se secondiTrascorsi >= 5400 metti C = 0 e passi nello stato TIME20

- Stato TIME20: Se A = 0 metti C = 0 e torni nello stato ATTESA;  se A = 1 incrementi la variabile secondiTrascorsi; se secondiTrascorsi < 1200 non fai nulla, se secondiTrascorsi >= 1200 metti C = 1 e passi nello stato TIME30

- Stato TIME30: Se A = 0 metti C = 0 e torni nello stato ATTESA;  se A = 1 incrementi la variabile secondiTrascorsi; se secondiTrascorsi < 1800 non fai nulla, se secondiTrascorsi >= 1800 metti C = 0 e passi nello stato TIME20

Lo stato in cui sei lo gestisci con una variabile (la chiamiamo stato???) che assumerà quattro valori: ATTESA, TIME90, TIME20, TIME30. Per questo puoi usare una enum.

Nella loop userai uno switch per decidere cosa fare ad ogni ciclo in base allo stato in cui sei.

Si tratta solo dell'idea di base, adesso riflettici e prova a buttare giù il codice, poi postalo e lo guardiamo insieme. Se invece hai ancora dubbi chiedi pure.

Ciao.
Vittorio.
-lo stato ATTESA: SE A=0 non succede nulla se A=1 -> C=1 e passo allo stato TIME90 (tutto chiaro e tutto corretto)
- stato TIME90: Se A = 0 metti C = 0 e torni nello stato ATTESA; se A = 1 incrementi la variabile secondiTrascorsi; se secondiTrascorsi < 5400 non fai nulla, se secondiTrascorsi >= 5400 metti C = 0 e passi nello stato TIME20
ecco questo punto mi confonde un pochino....in una macchina a stati, posso fare questa operazione che "scrivo in prosa"? sarebbe: l'uscita è attiva quindi A=1 ok allora accendo C=1....e conto un secondo....poi riverifico...A=1? se si allora conto il un altro secondo (e siamo a 2) e verifico che il totale dei secondi contati sia inferiore a 5400. Siamo a 2 secondi, quidni ok -> rifaccio il controllo A=1? se si...allora conto un altro secondo (e siamo a 3) e verifico ancora che il totale dei secondi (3) sia < 5400....lo è....quindi procedo ancora....
se in una di queste fasi A=0 torno allo stato attesa
se il controllo secondi trascorsi >=5400 allora vado allo stato TIME20

-stato time20: funziona esattamente come lo stato time90 come logica...

stato time30 funziona esattamente come lo stato time20 come logica

e qui l'ultima complicazione che non capisco....ora..che ho finito lo stato time30....controllo nuovamente l'ingresso A=1? se si....devo tornare allo stato TIME20 , poi allo stato TIME30 poi allo stato TIME20 , poi allo stato TIME30, allo stato TIME20 , poi allo stato TIME30, allo stato TIME20 , poi allo stato TIME30.....e così via...controllando sempre che A=1, se in qualsiasi stato A=0 torno allo stato ATTESA

bene, ora, se ho ben compreso quello che ha scritto Vittorio...e se quello che ho scritto in prosa è verificato...ehmmm.....non saprei proprio come scriverlo.. :)

mi scuso ancora....per le castronerie e banalità che scrivo...e grazie per il tempo che mi dedicate!

megamarco83

ehmmm ci ho provato...credo di avere fatto un grosso disastro con le parentesi...
e non riesco a mettere giù la logica di come effettuare il conto del tempo...ho pensato di usare una variabile che faccio ciclare in modo da incrementarla (che si chiama tempo)
ma non ho capito come passarla ed associarla alla variabile temporale millis()
provate a darci un occhio?
....ehmmm...dovreste controllare (non ridete:) ) se ho perfino dichiarato in modo corretto le variabili...
le ho dichiarate tutte (ingressi ed uscite che dovrei usare) ma ho usato solo ingresso A ed uscita C e mi sono già incasinato così....
GRAZIE!

Code: [Select]
#define OUT_C 2
#define OUT_D 3
#define IN_A 4
#define IN_B 5
unsigned long tempo = 0;

// 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)

int STATO;

unsigned int fsm_state;
void setup() {
  pinMode(OUT_C, OUTPUT);
  pinMode(OUT_D, OUTPUT);
  pinMode(IN_A, INPUT);
  pinMode(IN_B, INPUT);
  STATO=ATTESA;
}

void loop() {
 
// definizione stati
  switch(STATO) {
   
    case ATTESA:
        if(IN_A=0){
        digitalWrite(OUT_C, LOW);
        }
       
        digitalWrite(OUT_C, HIGH);
        STATO=TIME_OFF;
  }
  break;

    case TIME_START:
    if(IN_A=0){
       digitalWrite(OUT_C, LOW);
        }
        STATO=ATTESA;

         while(IN_A=1){
            digitalWrite(OUT_C, HIGH);
            tempo=tempo+1; //in questo modo aumento il contatore del tempo fino ad arrivare ai valori impostati NEL DEFINE
                        // MA NON SO COME USARE LA FUNZONE  millis()
       
        while(tempo<=TIME_START){
            digitalWrite(OUT_C, HIGH);
            ]
     
       STATO=TIME_OFF;

   

     case TIME_OFF:
     if(IN_A=0){
       digitalWrite(OUT_C, LOW);
        }
        STATO=ATTESA;
       
      while(IN_A=1){
        digitalWrite(OUT_C, LOW);
        tempo=tempo+1; //in questo modo aumento il contatore del tempo fino ad arrivare ai valori impostati NEL DEFINE
                        // MA NON SO COME USARE LA FUNZONE  millis()
       
        while(tempo<=TIME_OFF){
            digitalWrite(OUT_C, LOW);
        }
        STATO=TIME_ON;
       

        case TIME_ON:
        if(IN_A=0){
       digitalWrite(OUT_C, LOW);
        }
        STATO=ATTESA;
       
          while(IN_A=1){
            digitalWrite(OUT_C, HIGH);
            tempo=tempo+1; //in questo modo aumento il contatore del tempo fino ad arrivare ai valori impostati NEL DEFINE
                        // MA NON SO COME USARE LA FUNZONE  millis()
          }
        while(tempo<=TIME_ON){
            digitalWrite(OUT_C, HIGH);
        }
  STATO=TIME_OFF;

}

PaoloP

Ho scritto 2 righe
Code: [Select]
// definizione stati
#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

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 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

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

}  // End loop


Non sono sicuro dei tempi di intervento e degli IF.
Non l'ho testato.

vittorio68

A occhio il codice postato da PaoloP mi pare concettualmente corretto.

@megamarco83
Ti consiglio di studiarlo per capire come funziona. Tieni presente che la il codice inserito nella loop viene eseguito continuamente. Considera che PaoloP ha già inserito il check degli input A e B nonchè la gestione degli output B e C. In realtà però la logica della macchina a stati (implementata dallo switch) controlla solo A e C. Per implementare anche B e D a mio avviso devi utilizzare variabili diverse a loro dedicate perchè, se ho capito bene, non è detto che la coppia (A,C) si trovi nello stesso stato della coppia (B,D).

Ciao.
Vittorio.

megamarco83

Ciao Vittorio e ciao Paolo
si è vero ha ragione Vittorio...le variabili: ingresso A -> uscita C e ingresso B -> uscita D
sono del tutto indipendenti tra loro, seguono la stessa logica di funzionamento, ma sono esattamente due enti distinti
quindi non è affatto detto che (A,C) si trovi nello stesso stato della coppia (B,D)
Del codice di paolo....non mi è chiaro come fa a fare i passaggi di stato...cioè capisco il filo logico, ma non capisco come fa a passare da uno stato all'altro...
esiste un debug o qualcosa di simile, che, impostando le variabili da tenere sotto controllo (magari riducendo i tempi dichiarati all'inizio del codice, giusto per non aspettare 1.5ore....possa monitorare il flusso del programma?
grazie ancora!!!!!!!!!

ehmmm infine...parlando di pura sintassi, l'istruzione
Quote
unsigned long I90 = 90 * 60 * 1000UL;
capisco bene che è per calcolare la pausa di 1.5ore...ma cosa sta a singnificare la dicitura 1000UL ?


PaoloP

UL forza l'operazione come unsigned long.
A volte, in base al compilatore (Arduino usa avr-gcc) potrebbe capitare che l'operazione venga eseguita come int andando quindi in overflow e segnando un prodotto sbagliato.

Ho creato il codice solo per la coppia A-C.
Il debug lo puoi fare inserendo dei serial.print nel codice e visualizzandoli nel serial monitor dell'IDE.

Per una verifica veloce, poiché mancano i while che bloccherebbero il ciclo, puoi inserire anche un led lampeggiante copiandoci il codice del "blink without delay".

vittorio68

Del codice di paolo....non mi è chiaro come fa a fare i passaggi di stato...
Come ti dicevo, devi considerare che la loop viene eseguita continuamente. Nel caso del codice di Paolo, ad ogni ciclo: legge gli input, esegue lo switch, imposta gli output. Poi ricomincia da capo indefinitamente.

Il codice eseguito dallo switch dipende dalla variabile stato. All'interno di ogni case (in pratica di ogni stato in cui si trova l'automa), può succedere, se opportuno, che la variabile stato cambi di valore. Guarda per esempio il case ATTESA. Questo viene eseguito quando stato == ATTESA. In questa parte di codice se A != LOW (la parte else dell'if) la variabile stato viene impostata a TIME_START.

Questo vuol dire che al prossimo ciclo della loop lo switch non eseguirà più il case ATTESA ma comincerà ad eseguire il case TIME_START (in pratica l'automa ora si trova nello stato TIME_START).

Prova a riguardare il codice in questa ottica e vedi se ti sembra più chiaro.

Ciao.
Vittorio.

megamarco83

ciao e grazie, ora ho capito decisamente meglio il codice!
ho ancora delle richieste (sopportatemi) :-)
....all'interno del void setup() c'è una dichiarazione T0=millis()
in questo caso, se ho capito bene, viene eseguita solo all'accesione di arduino la prima volta....
quindi ipoteticamente T0=300 (ipotizzando che siano trascorsi 300millisecondi da qnd ho acceso arduino

bene ora entra il loop
Tcorr=millis()
in questo caso sono passati altri secondi, quindi Tcoor=400 (sto ipotizzando)
poi fa una lettura dello stato dell'imput A e B
se A=0 allora C=low (caso attesa) e fin qui tutto chiaro

 mettiamo il caso in cui A=1
il caso attesa lo riconosce con il comando

         
Code: [Select]
else
{
stato = TIME_START; // PASSA ALLA FASE start
T0 = Tcorr; // Azzera il contatore
}
break;


quindi va alla fase TIME_START ed pone il contatore T0=Tcorr=400 (400 nel mio esempio)

nello stato TIME_START rifà il controllo con l'ingresso A, se è a 0 rimette C=0 e va in ATTESA; poniamo il caso che A=1
fa il controllo:

Code: [Select]
{
if (Tcorr - T0 >= I90)
{
T0 = Tcorr; // Azzera contatore
stato = TIME_OFF;
}
else
{
statoC = HIGH;
}
}
break;


quindi 400-300 >= 5400*1000 (cioè le 1.5ore)
la condizione non è verificata quindi pone
Code: [Select]
statoC=HIGHT;
.....e poi cosa fa?....dove va in questo caso?
non ci voleva un ciclo while, in modo che potesse ciclare?
oppure grazie al break finisce di eseguire lo switch e torna al void loop () ???

se così fosse, allora rifà l'operazione
Tcorr=millis() e supponendo che sono passati altri 100millisecondi....quindi = 500

poi rilegge gli stati, supponendo che A è ancora = 1
ritorna nello stato ATTESA, fa il controllo, ed esegue ancora:

code]else
{
            stato = TIME_START; // PASSA ALLA FASE start
            T0 = Tcorr;         // Azzera il contatore
         }
         break;[/code]
quindi va allo stato TIME_START e pone T0=Tcorr=500
A è ancora = 1

if (Tcorr - T0 >= I90)
            {
               T0 = Tcorr;      // Azzera contatore
               stato = TIME_OFF;
            }
            else
            {
               statoC = HIGH;
            }
         }
         break;[/code]

quindi fa il controllo 500-500 >= 5400*1000
non è verificato, quindi pone statoC=HIGH;
e così via....
ma in questo modo quando raggiungerà la condizione per cui sono passate 1.5ore?
ovviamente ho sbagliato io a capire il codice....
dove sbaglio?

ed infine....forse l'errore è che millis() non è il tempo che intercorre tra due iterazioni di arduino, ma il tempo reale che scorre?
grazie!!!!


SukkoPera

forse l'errore è che millis() non è il tempo che intercorre tra due iterazioni di arduino, ma il tempo reale che scorre?
Precisamente. Come detto qualche post fa: la funzione millis() ti dice quanti millisecondi sono passati dall'accesione e viene aggiornata automaticamente.
"Code is read much more often than it is written, so plan accordingly. Design for readability."

Guida rapida a ESP8266: https://goo.gl/kzh62E

megamarco83

ok grazie....il resto della mia interpretazione del codice è corretto?
il break dove mi fa "saltare"?
grazie!

SukkoPera

Il break ti porta fuori dallo switch, come supponi. Quindi solo un ramo dello switch viene eseguito a ogni iterazione di loop(), quello corrispondente al valore della variabile stato. Finché non cambia rimane sempre nello stesso stato, per cui se vale TIME_START, a meno di trovare uno stato = ATTESA, il codice di ATTESA non viene eseguito. Spero sia chiaro!
"Code is read much more often than it is written, so plan accordingly. Design for readability."

Guida rapida a ESP8266: https://goo.gl/kzh62E

megamarco83

ci rifletto ancora questa sera, me lo stampo e ci studio su....
poi magari provo ad implementare anche la variabile in ingresso B con la corrispettiva uscita D
....e....credo vi romperò ancora la scatole :)

Go Up