Go Down

Topic: Irrigazione automatica (Read 3918 times) previous topic - next topic

Claudio_FF

#15
May 25, 2019, 12:02 pm Last Edit: May 28, 2019, 06:53 pm by Claudio_FF
Ok, allora ci sono innanzitutto delle grosse lagune con le basi del C :)

Se lo switch non è chiaro,  meglio prima vedere questo confronto con un if del tutto equivalente, è solo una struttura decisionale scritta in altro modo. Entrambi i codici eseguono solo 'istruzioni_a' o solo 'istruzioni_b' a seconda del valore della variabile 'x'. Se il valore della variabile non corrisponde a nessuno dei test precedenti vengono eseguite 'istruzioni_c'.

Code: [Select]
switch (x)
{
    case 20:
        ...istruzioni_a...
    break;
    
    case 30:
        ...istruzioni_b...
    break;

    default:
        ...istruzioni_c...
    break;
}

Code: [Select]
if (x == 20)
{
    ...istruzioni_a...
}
else if (x == 30)
{
    ...istruzioni_b...
}
else
{
    ...istruzioni_c...
}


Detto ciò con le define crei dei nomi di comodo/etichette, quindi scrivere un valore o il nome creato con la define è la stessa cosa, perché alla compilazione nei punti dove hai scritto i nomi questi verranno sostituiti con i valori. Quindi si:
Code: [Select]
byte currentPhase = PH_START;
è esattamente come scrivere:
Code: [Select]
byte currentPhase = 0;
Ma la prima forma è più chiara perché non serve andare in cerca di (o ricordarsi) cosa voglia dire lo zero. E questo è un consiglio sempre valido: evitare di cospargere il codice di numerini che non è chiaro cosa rappresentano, ad esempio la riga seguente è una scrittura su un pin di uscita, ma non abbiamo alcuna idea sulla funzione del pin, e neppure se vuol dire che quello che vi è collegato lo stiamo accendendo o spegnendo (ad esempio se è un relé in logica negativa lo stiamo spegnendo, ma senza commenti e schemi tocca tirare a indovinare):
Code: [Select]
digitalWrite(3, HIGH);
Invece la riga seguente è chiarissima, sappiamo che stiamo accendendo e anche cosa. È sufficiente scrivere all'inizio del codice le define coerenti con i collegamenti hardware e con i livelli HIGH o LOW presenti sui pin di ingresso o voluti sui pin di uscita:
Code: [Select]
digitalWrite(POMPA, LIVELLOACCESO);


NEXTPHASE è una macro, concetto più avanzato che personalmente non avrei introdotto in questo esempio, di fatto opera sempre con una sostituzione ed è come se scrivessi:
Code: [Select]
currentPhase = currentPhase + 1;


Code: [Select]
currentPhase = PH_RUN;   //invece qui contrariamente alla riga sopra
non stiamo usando una logica variabile (passatemi il termine) ma stiamo
semplicemente dicendo che la fase attuale è 1

Esatto. Tra l'altro i valori sono del tutto convenzionali, una fase può essere la 50 e quella successiva la 25, per cui l'incremento di 1 della fase ha senso solo nei casi specifici di una sequenza lineare, che so... le fasi notte alba giorno tramonto di un presepio... ma vedi ben che dopo il tramonto non basta incrementarla, va riportata al valore di notte.

E per concludere grazie, se non mi fossi fermato a rispondere a questo post adesso sarei in bicicletta sotto la pioggia :D :D :D
Non devono essere gli altri a dover "indovinare" cosa volete o cosa avete fatto, o a dover interpretare un codice mal incolonnato, dovete essere voi a spiegarlo e scriverlo molto chiaramente, infatti una domanda ben posta è già mezza risposta!

karnhack

@Claudio_FF devo dire che hai il dono della chiarezza, senza nulla togliere allo sforzo che gli altri stanno facendo per farmi capire.
Purtroppo le mie lacune sono aggravate dal fatto che non ho studiato la materia in giovane età e che non comprendo bene l'inglese, lingua con cui sono scritte la maggior parte delle guide che si trovano in giro.
Detto questo, vorrei provare a buttare giù un po di righe guardando gli esempi finora esposti, tralasciando per ora l'RTC e concentrandomi sulle funzioni delle cosiddette fasi... sempre che questo modo di procedere non mi incasini ancora di più le cose.

Claudio_FF

#17
May 25, 2019, 02:43 pm Last Edit: May 25, 2019, 02:47 pm by Claudio_FF
tralasciando per ora l'RTC e concentrandomi sulle funzioni delle cosiddette fasi... sempre che questo modo di procedere non mi incasini ancora di più le cose.
In un normale diagramma di flusso le fasi di funzionamento ci sarebbero comunque, solo che sarebbero "embeddate" e implicite nella la sequenza delle istruzioni invece di essere esplicite. Anche se non sembra in realtà con le fasi esplicite le cose si semplificano, ci si accorge di questo appena si vuole modificare qualcosa o aggiungere qualche funzionalità (soprattutto fare qualcosa in multitasking).

Per l'RTC... se scrivi una funzioncina 'leggi_rtc' fittizia che restituisce un orario fittizio a te comodo, il fatto di non usare l'RTC reale è ininfluente per il resto del programma. Poi basta modificare solo quella funzione per leggere l'RTC vero.

Ogni sforzo fatto per comprendere la struttura di questo progetto diventa strada spianata per qualsiasi altro progetto futuro.

Ora quello che c'è da dettagliare meglio è a cosa prestare attenzione in ogni fase, e quali azioni svolgere in quel caso.

Non devono essere gli altri a dover "indovinare" cosa volete o cosa avete fatto, o a dover interpretare un codice mal incolonnato, dovete essere voi a spiegarlo e scriverlo molto chiaramente, infatti una domanda ben posta è già mezza risposta!

karnhack

#18
May 25, 2019, 02:47 pm Last Edit: May 25, 2019, 02:48 pm by karnhack
Anche se non ho fatto chissà che sono già contento di non aver scopiazzato qua e la. Intanto allego i progressi e rinnovo la mia gratitudine per gli aiuti

Code: [Select]
#include <Wire.h>
#include <RTClib.h>

#define POMPA 5 // definisce il pin della pompa
#define ELETTROVALVOLA 6
#define TROPPOPIENO A0 // ??? devo ancora capire come funziona il sensore del troppopieno (contatto aperto-chiuso) ???

int ACCESO = LOW; // sostituisce la logica del pin (LOW/HIGH) con il termine ACCESO/SPENTO per una migliore comprensione della fuzione
int SPENTO = HIGH;

RTC_DS1307 RTC;

void setup() {
  pinMode(POMPA, OUTPUT); // imposta il pin (POMPA) come output
  pinMode(ELETTROVALVOLA, OUTPUT);
  pinMode(TROPPOPIENO, INPUT);

  digitalWrite(POMPA, SPENTO); // all'avvio di arduino spegne la pompa
  digitalWrite(ELETTROVALVOLA, SPENTO);
 
/***************************RTC e WIRE**************************/
  Serial.begin(9600);
  Serial.println( "START" );
   
  Wire.begin();
  RTC.begin();

//  RTC.adjust(DateTime(2019, 05, 23, 12, 53, 00)); // decommentare per regolare ora e data (yyyy,mm,dd,hh,mm,ss)

  if (! RTC.isrunning()) {
    Serial.println("RTC non è attivo!");
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }
}






void loop() {
  if ( RTC.isrunning()) {
    DateTime now = RTC.now();
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(' ');
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
    }
}

karnhack

Per quanto riguarda il controllo dei periodi orari, meglio lavorare con i minuti, in questo modo basta evitare un periodo a cavallo della mezzanotte:
Code: [Select]
int adesso = now.hour()*60 + now.minute();  // tempi da 0 a 1439 minuti
int inizio = Pompa[0]*60 + Pompa[1];
int fine   = Pompa[2]*60 + Pompa[3];

Mi dite gentilmente a che servono i numeri nelle parentesi quadre?


Maurotec

Quote
Mi dite gentilmente a che servono i numeri nelle parentesi quadre?
Siamo messi male. :)
Si chiamano array (o vettori) link

Code: [Select]

int ACCESO = LOW;

Anche qui serve studiare i tipi di variabile.
più corretto è,
Code: [Select]

const byte acceso = LOW;

Mentre per le macro (preprocessore C) corretto è,
Code: [Select]

#define ACCESO LOW



Ciao.

karnhack


karnhack

Ecco ora mi sono arenato... se switch è l'ora attuale, nel mio caso ADESSO, però ADESSO viene ricavato da
Code: [Select]
now.hour()*60 + now.minute();  // tempi da 0 a 1439 minuti
come faccio a prelevare anche il mese senza superare i 1439 minuti? c'è un operatore che non sia + e che mi permette di farlo? Allego il codice

Claudio_FF

#23
May 25, 2019, 05:35 pm Last Edit: May 25, 2019, 05:47 pm by Claudio_FF
Per gli array la sintesi è questa:

-Una variabile è una scatoletta che può contenere un valore.
-Un array è una fila di scatolette ciascuna identificata da un indice (che parte da 0 per la prima)

Ho scritto Pompa[n] perché nel codice del primo post gli orari di inizio e fine sono contenuti in un array di quattro posizioni:
Code: [Select]
int Pompa[] = {13, 29, 13, 30};
e per ottenere i valori bisogna indicare non solo la variabile array, ma anche quale delle sue scatolette:

       .----.----.----.----.
Pompa  | 13 | 29 | 13 | 30 |
       '----'----'----'----'
Indici   0    1    2    3

Per il resto il fatto che tu stia in qualche modo parlando di switch legato alla variabile 'adesso' e di sommare il mese mi fa temere qualcosa di brutto brutto, ma magari non è così :) Prima di pensare a come codificarla, non mi è chiaro come vorresti usare l'informazione del mese, perché il mese ce l'hai già in chiaro con now.month()
Non devono essere gli altri a dover "indovinare" cosa volete o cosa avete fatto, o a dover interpretare un codice mal incolonnato, dovete essere voi a spiegarlo e scriverlo molto chiaramente, infatti una domanda ben posta è già mezza risposta!

karnhack

Te l'ho già detto che hai il dono della chiarezza?  :)

Ho capito anche io dopo aver fatto la domanda che stavo facendo una cavolata... poi aggiungendo il MESE col metodo a scatoletta (vettore, array) mi sono reso conto che posso verificare quella condizione in un altro modo.

Code: [Select]
int MESE[] = {3,5,7,9,11};  // (GEN 1, FEB 2, MAR 3, APR 4, MAG 5, GIU 6, LUG 7, AGO 8, SET 9, OTT 10, NOV 11, DIC 12)

Però ora mi sono arenato di nuovo con sto benedetto switch, in quanto arduino mi restituisce un errore incomprensibile tipo
Code: [Select]
Prova:102:1: error: stray '\302' in program.

In pratica sto provando a fare così ma mi da errore:

Code: [Select]
switch (ADESSO)
    {
      case RIEMPIMENTO:
      // istruzioni a
      break;

      case RIPOSO:
      // istruzioni a
      break;

      case CONCIMAZIONE:
      // istruzioni a
      break;

      default:
      if ((ADESSO >= INIZIO) && (ADESSO < FINE))
      {
       digitalWrite(PinPOMPA, ACCESO);
      }
      else
      {
       digitalWrite(PinPOMPA, SPENTO);
      }
      break;
    }


Allego il codice nel caso vogliate dare un occhiata al casino che sto facendo  :D

karnhack

#25
May 25, 2019, 05:59 pm Last Edit: May 25, 2019, 06:02 pm by karnhack
No scusa, nessun errore, avevo copiato ed incollato qualche carattere invisibile dal tuo esempio! FUNZIONA!!!  :D  :D

Claudio_FF

#26
May 25, 2019, 06:05 pm Last Edit: May 25, 2019, 06:44 pm by Claudio_FF
No no, l'errore c'è, ed è quello che temevo :)

Lo switch deve funzionare in base al valore della variabile di fase, non del valore orario... Di fatto con lo switch hai scritto l'equivalente esatto di questo if... un sacco di condizioni che non risulteranno mai vere, o meglio, risulteranno vere solo a mezzanotte, a mezzanotte e un minuto, a mezzanotte e due minuti ecc, mentre per tutto il resto del tempo viene eseguito solo l'else:

Code: [Select]
if (ADESSO == RIEMPIMENTO)
{
    // istruzioni a
}
else if (ADESSO == RIPOSO)
{
    // istruzioni a
}
else if (ADESSO == CONCIMAZIONE)
{
    // istruzioni a
}
else
{
    if ((ADESSO >= INIZIO) && (ADESSO < FINE))
    {
        digitalWrite(PinPOMPA, ACCESO);
    }
    else
    {
        digitalWrite(PinPOMPA, SPENTO);
    }
}

Di solito nella parte default dello switch non c'è bisogno di scrivere nulla. Avviene tutto nei case (o nelle funzioni richiamate dai case). Ed è all'interno dei case che si controlla il valore dell'orario decidendo cosa fare.

Code: [Select]

      case RIPOSO:  // attendo l'orario, e in quel momento passo a fase irrigazione
          if (in_orario())
          {
              accendi_pompa();
              fase = IRRIGAZIONE;
          }
      break;

      case IRRIGAZIONE:  // attendo la fine orario, e in quel momento passo a riempimento
          if (!in_orario())
          {
              spegni_pompa();
              apri_valvola();
              fase = RIEMPIMENTO;
          }
      break;

Se scrivi tante piccole funzioncine (come accendi_pompa, in_orario ecc) hai l'intera logica scritta in italiano.

In maiuscolo andrebbero scritte solo le costanti, in modo da distinguerle dalle variabili minuscole.
Non devono essere gli altri a dover "indovinare" cosa volete o cosa avete fatto, o a dover interpretare un codice mal incolonnato, dovete essere voi a spiegarlo e scriverlo molto chiaramente, infatti una domanda ben posta è già mezza risposta!

Maurotec

Quanta fretta ma dove corri.. https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=2ahUKEwju47j4ibfiAhVQyKQKHRtABHUQyCkwAHoECAoQBQ&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DYoBuv7HR-Fw&usg=AOvVaw34Sz9Sb-XmZdDvDzkl9Xsd

Freno a mano tirato su.
Esistono delle convenzioni che se rispettate permettono al programmatore di ricavare informazioni in un solo colpo di occhio. Ad esempio se per convenzione scriviamo le macro tutte in maiuscolo e le variabili in minuscolo abbiamo il seguente vantaggio, cioè osservando una piccola porzione di codice di un programma che conta 10000 righe sappiamo che PH_START è un macro, mentre phStatus è una variabile. Ovviamente possiamo modificare il valore di una variabile ma non di una macro.

Stessa potenza c'è nell'uso del tipo di variabili, come dire se uso il tipo int vuole dire che mi serve contenga anche valori negativi. Quando so a priori che la variabile non conterrà mai valori negativi il tipo corretto è byte, uint8_t, uint16_t, uint32_t, boolean ecc, in sostanza tutti tipi senza segno.

Quote
No no, l'errore c'è, ed è quello che temevo :)
Che vuoi fare l'ebbrezza di avere scritto qualcosa che funziona senza copiare da alla testa. :)

PS: lo so, la domanda è; da dove spuntano fuori uint8_t, uint16_t ecc?

Ciao.

karnhack

Che vuoi fare l'ebbrezza di avere scritto qualcosa che funziona senza copiare da alla testa. :)
E' stato breve ma intenso, il momento di gloria intendo...
Già non funziona più, appena ho tirato fuori il codice da default:

Non capisco una cosa però, a parte quello che mi avete detto. Lo switch richiede che il case sia dichiarato... si ma in che modo? facendo delle prove avevo messo dei numeri a caso e funzionava lo stesso:
Code: [Select]
#define RIEMPIMENTO 0 // perchè 0?
#define RIPOSO 1 // perchè 1?
#define CONCIMAZIONE 2 // perchè 2?
#define IRRIGAZIONE 3

Claudio_FF

#29
May 25, 2019, 06:58 pm Last Edit: May 25, 2019, 06:59 pm by Claudio_FF
facendo delle prove avevo messo dei numeri a caso e funzionava lo stesso:
Dal post #11: «il valore associato ad ogni fase è naturalmente una convenzione»
Dal post #15: «Tra l'altro i valori sono del tutto convenzionali»

Al punto che dandogli dei nomi con 'define' o con 'const byte' ci possiamo dimenticare dei valori... purché siano tutti diversi l'uno dall'altro.
Non devono essere gli altri a dover "indovinare" cosa volete o cosa avete fatto, o a dover interpretare un codice mal incolonnato, dovete essere voi a spiegarlo e scriverlo molto chiaramente, infatti una domanda ben posta è già mezza risposta!

Go Up