Go Down

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

karnhack

La colpa è sempre dell'ignoranza, nel senso di ignorare le cose e l'unico modo per uscirne è cercare di capire...
Siccome il mio problema attuale sono un botto di termini che non ricordo, ho trovato un link che mi sono stampato ed in una paginetta semplifica le istruzioni standard di arduino. Sembra una cavolata ma mi sta schiarendo parecchio le idee perchè con un colpo d'occhio riesco a collocare termini che a memoria non ricorderei. Tra l'altro cercare sul web a volte complica la vita invece di risolvere i problemi. Ora sono stanco ma domattina non vedo l'ora di mettermi al lavoro.
Se voi non mi mollate io non mollo! Siete avvisati!  Grazie per la comprensione e Buonanotte    :smiley-mr-green:

Maurotec

Occhio che quella guida contiene qualche errore, si spera migliori.

Uno di questi è: insigned che non esiste, come pure Insigned.

Il linguaggio C/C++ prevede una "istruzione" per dare un nome a piacere ad un tipo di dato predefinito.
L'istruzione in questione è typedef.
Il termine istruzione in questo caso è ambiguo.

I tipi predefiniti in C sono:
char
int
long
long long
float
double

Questi sono tutti tipi con segno (signed) anche se non è specificato prima.
Per i primi 4 si può specificare unsigned, cioè:
unsigned char
uinsigned int
unsigned long
unsigned long long

Grazie alla "istruzione" typedef arduino ha ridefinito il tipo unsigned char dandogli il nome byte.
In poche parole dentro al codice delle librerie c'è,
Code: [Select]

typedef unsigned int word;
typedef bool boolean;
typedef uint8_t byte;

Ecco che spunta uint8_t, già visto prima ma anche word

Pertanto usare bool al posto di boolean è equivalente.
Come pure uint8_t al posto di unsigned char o ancora byte

Evidentemente si fa prima a scrivere byte che unsigned char

Per finire uint8_t è disponibile fino a 64 al posto di 8, cioè uint16_t, uint32_t e uint64_t
tutti senza segno, basta non digitare la u per averli con segno.

Ecco che la potenza del linguaggio anziché semplificare complica, ma aumenta la flessibilità.

PS: Spero che questo tu possa digerirlo senza puntare gli occhi verso la finestra :D

Standardoil

Comunque comodo, nonostante i problemini elencati, e altri ancora, come flush() che non lavora più come dedcritto
Poi però è necessario imparare a consultare il reference, proprio per queste ragioni
Prima legge di Nelson (che sono io): Se vuoi il mio aiuto dimostrami almeno che hai letto il nostro "aiutateCi ad aiutarVi"

Non bado a studenti, che copino altrove

Tu hai problema-Io ti domando-Tu non mi rispondi: vuol dire che non ti serve più

karnhack

#48
May 28, 2019, 01:01 pm Last Edit: May 28, 2019, 01:03 pm by karnhack
Ecco cosa ho combinato:

Code: [Select]
switch (fase)
    {
      case RIEMPIMENTO_START:
        if (analogRead(pinTroppopieno) == 0) {  
          digitalWrite(pinElettrovalvola, SPENTO); // se il contatto è chiuso lascialo chiuso
          }
        else
          {
          digitalWrite(pinElettrovalvola, ACCESO);  // altrimenti apri l'elettrovalvola
          }
        fase = RIEMPIMENTO_STOP;  // e vai alla fase RIEMPIMENTO_STOP
        break;
      case RIEMPIMENTO_STOP:
        if (analogRead(pinTroppopieno) == 1023) {  
          digitalWrite(pinElettrovalvola, ACCESO); // se il contatto è aperto lascialo aperto
        }
        while (analogRead(pinTroppopieno) == 0) // finchè il contatto è chiuso
        {
        digitalWrite(pinElettrovalvola, SPENTO); // allora spegni l'elettrovalvola
        }
        fase = RIPOSO;  // e vai alla fase RIPOSO
        break;


in realtà nel "case RIEMPIMENTO_STOP" la prima lettura è inutile perchè se è passato a questa fase è perchè  il contatto è aperto, però ho bisogno che ripeta questa lettura finchè poi si verifica la condizione while.
Per quanto riguarda ==1023 e ==0 per ora non avendo la resistenza da 10K li ho messi così, ma mi riserverò di andare a verificare i valori e gli operatori di comparazione 8)  dopo che avrò la resistenza
Che ne dite può andare o punto la finestra?....

Con i due fili di acciaio piuttosto avresti il problema dell'elettrolisi
https://forum.arduino.cc/index.php?topic=583927.msg3980712#msg3980712
Per quanto riguarda l'ossidazione da elettrolisi ho pensato ad un altra via, la via asciutta... Siccome ho intenzione di mettere una valvola meccanica a galleggiante come sistema di sicurezza, a sto punto la userò anche per tenere sospeso e fuori dall'acqua il contatto aperto chiuso.

Claudio_FF

#49
May 28, 2019, 01:32 pm Last Edit: May 28, 2019, 01:46 pm by Claudio_FF
Risposta breve: ci stiamo infognando come non previsto. Forse ho dato troppe cose per scontate.

Per la risposta lunga tocca attendere la sera.

Intanto medita su cosa fa quel while e sul perchè è paragonabile a un buco nero... finchè contatto chiuso chiudi valvola... e rimani sempre li perchè il CICLO while non termina mai FINCHÉ il contatto continua a restare chiuso.
Una domanda ben posta è già mezza risposta.

karnhack

#50
May 28, 2019, 01:46 pm Last Edit: May 28, 2019, 01:47 pm by karnhack
Ma se sposto la FASE RIPOSO dentro la parentesi {} del while non dovrei risolvere uscendo dalla fase RIEMPIMENTO_STOP?
Code: [Select]

while (analogRead(pinTroppopieno) == 0) // finchè il contatto è chiuso
        {
        digitalWrite(pinElettrovalvola, SPENTO); // allora spegni l'elettrovalvola
        fase = RIPOSO; // e vai alla fase RIPOSO
        }
        break;

Maurotec

Quote
Ma se sposto la FASE RIPOSO dentro la parentesi {} del while non dovrei risolvere uscendo dalla fase RIEMPIMENTO_STOP?
No, purtroppo non basta impostare la variabile fase = RIPOSO per passare al CASE RIPOSO. Non funziona perché il while ripete in ciclo tra { e } fintanto che la condizione viene valutata vera.

Quote
Che ne dite può andare o punto la finestra?....
IF (FINESTRA == APERTA)
    Butta();
ELSE
    OPEN(FINESTRA);
    Butta();
 :D

karnhack

#52
May 28, 2019, 02:59 pm Last Edit: May 28, 2019, 03:19 pm by karnhack
 8)

Code: [Select]
switch (fase)
    {
      case RIEMPIMENTO_START:
        if (digitalRead(pinTroppopieno) == 1) {   
          digitalWrite(pinElettrovalvola, SPENTO); // se il contatto è chiuso lascia l'elettrovalvola chiusa
          }
        while (digitalRead(pinTroppopieno) == 0)
          {
          digitalWrite(pinElettrovalvola, ACCESO);  // altrimenti apri l'elettrovalvola
          }
        fase = RIEMPIMENTO_STOP;  // e vai alla fase RIEMPIMENTO_STOP
        break;
      case RIEMPIMENTO_STOP:
        digitalWrite(pinElettrovalvola, SPENTO); // allora spegni l'elettrovalvola
        fase = RIPOSO; // e vai alla fase RIPOSO
        break;

Claudio_FF

#53
May 28, 2019, 05:36 pm Last Edit: May 28, 2019, 05:45 pm by Claudio_FF
L'ultimo codice almeno funziona :) Però stai ragionando in modo procedurale e non sfruttando (i vantaggi del)la logica a fasi, anzi, la bypassi completamente trasformandola di nuovo in procedurale puro.

Quindi è del tutto inutile tenere in piedi lo switch, la variabile fase ecc... il tuo codice funzionerebbe ugualmente scrivendolo così nel loop:

Code: [Select]
if (digitalRead(pinTroppopieno) == 1) {  
  digitalWrite(pinElettrovalvola, SPENTO); // se il contatto è chiuso lascia l'elettrovalvola chiusa
  }
while (digitalRead(pinTroppopieno) == 0)
  {
  digitalWrite(pinElettrovalvola, ACCESO);  // altrimenti apri l'elettrovalvola
  }
digitalWrite(pinElettrovalvola, SPENTO); // allora spegni l'elettrovalvola

Siccome il primo if è ridondante, allora basterebbe:
Code: [Select]
while (digitalRead(pinTroppopieno) == 0)
  {
  digitalWrite(pinElettrovalvola, ACCESO);  // altrimenti apri l'elettrovalvola
  }
digitalWrite(pinElettrovalvola, SPENTO); // allora spegni l'elettrovalvola

Poi diamo anche a quegli uni e zeri un significato comprensibile come già fatto per le uscite, e rendiamo i commenti coerenti con le istruzioni:
Code: [Select]
while (digitalRead(pinTroppopieno) == APERTO) // finche' contatto aperto
{
    digitalWrite(pinElettrovalvola, ACCESO);  // valvola aperta
}
digitalWrite(pinElettrovalvola, SPENTO);      // poi chiudila

Se abbandoniamo la logica a fasi allora si torna all'algoritmo "classico"... solo da tradurre sintatticamente C. In rosso il riempimento, in verde il riposo, arancio la concimazione, blu irrigazione...

peristalsi off
agitatore off
pompa off
ripeti:
    finché contatto aperto
        valvola aperta
    valvola chiusa

    finché non orario e non mese:
        nulla

    se mese:
        peristalsi on
        agitatore on
        pausa
        peristalsi off
        agitatore off

    pompa on
    pausa
    pompa off


Una domanda ben posta è già mezza risposta.

karnhack

#54
May 28, 2019, 06:23 pm Last Edit: May 28, 2019, 06:54 pm by karnhack
L'ultimo codice almeno funziona :) Però stai ragionando in modo procedurale e non sfruttando (i vantaggi del)la logica a fasi, anzi, la bypassi completamente trasformandola di nuovo in procedurale puro.

Quindi è del tutto inutile tenere in piedi lo switch, la variabile fase ecc...
Il fatto è che non avendo ancora padroneggiato e sudato sull'IF...ELSE...WHILE non capisco appieno i vantaggi dello switch. Poi non pensavo neanche si potesse partire dal while ma credevo fosse indispensabile partire sempre con if... per questo motivo ho agito in modo coatto facendo :

Code: [Select]
case RIEMPIMENTO_STOP:
        digitalWrite(pinElettrovalvola, SPENTO); // allora spegni l'elettrovalvola
        fase = RIPOSO; // e vai alla fase RIPOSO


Ho aggiunto una fase chiamata PRIMA_ACCENSIONE perchè altrimenti incasino la cosa, mi spiego.
La prima volta che avvierò l'irrigatore, dopo aver riempito il contenitore ho bisogno che l'acqua decanti per 24h. Se vado a mettere questa funzione che serve solo al primo avvio nella fase RIPOSO poi dopo aver innaffiato non potrò ripetere il ciclo ritornando su RIPOSO.

Claudio_FF

#55
May 28, 2019, 07:05 pm Last Edit: May 28, 2019, 07:08 pm by Claudio_FF
non avendo ancora padroneggiato e sudato sull'IF...ELSE...WHILE non capisco appieno i vantaggi dello switch
Post #15, lo switch è solo un 'if/else if' scritto in altro modo, quindi il problema secondo me non è lo switch, ma la struttura logica e come avviene l'esecuzione.

Quote
Poi non pensavo neanche si potesse partire dal while ma credevo fosse indispensabile partire sempre con if
Appunto, ogni passo avanti tocca farne tre indietro. La programmazione, la struttura, l'idea di cosa usare vengono prima della codifica. Questo è quello che intendo dicendo che il programma prima deve funzionare su carta. E di quali cose è formato? Solo da sequenze, ramificazioni (if), ripetizioni (while) combinate come si vuole.

Quote
La prima volta che avvierò l'irrigatore, dopo aver riempito il contenitore ho bisogno che l'acqua decanti per 24h. Se vado a mettere questa funzione che serve solo al primo avvio nella fase RIPOSO poi dopo aver innaffiato non potrò ripetere il ciclo ritornando su RIPOSO
A questo serve la chiara separazione in fasi. Lo switch è solo un if per eseguire ad ogni giro di loop il solo codice della fase attualmente attiva. Ma nella fase vanno messe istruzioni congruenti con il significato della fase stessa. RIEMPIMENTO_STOP sarebbe una non fase, ma se ci sono altre fasi che io non vedo per cui diventa utile non posso saperlo  ;)
Una domanda ben posta è già mezza risposta.

karnhack

#56
May 28, 2019, 09:22 pm Last Edit: May 28, 2019, 09:32 pm by karnhack
RIEMPIMENTO_STOP l'ho eliminato, anzi sostituito con PRIMA_ACCENSIONE dove dovrò impostare un ritardo di 24H rispetto all'ora attuale e per il momento vedo solo due vie salvo un vostro consiglio.
La prima via è la più semplice, sempre che funzioni
Code: [Select]
delay(86400000);
Ma non credo mi stiate dedicando il vostro tempo per fare le cose alla meno peggio, e per questo vi ringrazio.

La seconda via che mi viene in mente è quella sfruttare la EEPROM di arduino, quindi dovrei procedere includendo la libreria, poi successivamente leggere l'RTC e scrivere sulla EEPROM l'ora ed i minuti
Code: [Select]
int ORE = now.hour();
    int MINUTI = now.minute();

Poi dovrei mettere un delay di 60 sec in modo da far passare 1 minuto per non trovarmi alla stessa ora mentre va a fare il confronto

Infine sempre che questa cosa sia fattibile dovrei porre una condizione:
Se ora e minuti dell'RTC == ora e minuti dell'EEPROM (passa alla fase successiva)

Ora però, sempre che questa sia la via da seguire, ho un problema (Houston)
Nel codice di inizializzazione (void setup) l'RTC non è ancora avviato pertanto non saprei proprio dove andare a prendere le ore ed i minuti da scrivere sull'eeprom

Code: [Select]
EEPROM.write(ORE,MINUTI);

Ho evitato di fare prove perchè so che la EEPROM ha un numero di scritture limitate quindi vorrei evitare di fare danni prima di capire sequesta via è fattibile. :smiley-mr-green:

Oppure posso scrivere sull'EEPROM direttamente dalla fase PRIMA_ACCENSIONE ?

maubarzi

#57
May 28, 2019, 09:32 pm Last Edit: May 28, 2019, 09:36 pm by maubarzi
L'RTC, una volta impostata l'ora, continua ad andare senza problemi, gli basta solo la sua batteria tampone.
In genere lo inizializzi nel setup e all'esecuzione successiva tiri via tutto perchè non serve reinizializzarlo ogni volta.
Lo puoi fare anche solo con uno sketch che fa solo quello.
Poi, invece, potresti aver la necessità di correggere l'ora, ogni tanto, perchè qualcosa sballa sempre. Quindi devi pensare a come fare. Magari con dei pulsanti per fare le variazioni come nei normali orologi digitali.

Per la EEPROM, sopporta decine di migliaia (se non centinaia di migliaia) di cicli di scrittura, quindi qualche prova non fa grossi danni.
Nessuna buona azione resterà impunita!

Preistoria -> medioevo -> rinascimento -> risorgimento -> rincoglionimento!

karnhack

#58
May 28, 2019, 09:44 pm Last Edit: May 28, 2019, 10:12 pm by karnhack
L'RTC, una volta impostata l'ora, continua ad andare senza problemi, gli basta solo la sua batteria tampone.
In genere lo inizializzi nel setup e all'esecuzione successiva tiri via tutto perchè non serve reinizializzarlo ogni volta.
Lo puoi fare anche con uno sketch che fa solo quello.
Poi, invece, potresti aver la necessità di correggere l'ora, ogni tanto, perchè qualcosa sballa sempre. Quindi devi pensare a come fare. Magari con dei pulsanti per fare le variazioni come nei normali orologi digitali.

@maubarzi forse non hai seguito lo svolgersi del "progetto", cmq ti ringrazio per l'interesse e ti aggiorno.
L'RTC è impostato correttamente, ora ho necessità di prendere l'ora e impostare un evento (che poi sarebbe uno switch ad un case successivo) a 24 ore di distanza.
Devo capire questa cosa come si può fare e per ora stavo pensando di utilizzare la EEPROM evitando così di perdermi in complicati calcoli matematici. Allego il progetto.

PS Scusa, ho notato solo dopo che eri presente sin dai primi post  :-[

maubarzi

#59
May 28, 2019, 11:02 pm Last Edit: May 28, 2019, 11:11 pm by maubarzi
Tranquillo, rispondevo a queste due cose:
Nel codice di inizializzazione (void setup) l'RTC non è ancora avviato pertanto non saprei proprio dove andare a prendere le ore ed i minuti da scrivere sull'eeprom
Ho evitato di fare prove perchè so che la EEPROM ha un numero di scritture limitate quindi vorrei evitare di fare danni prima di capire sequesta via è fattibile. :smiley-mr-green:
Sulla prima avevo capito che non avevi ancora inizializzato l'RTC con un orario buono, se invece non hai ancora inizializzato l'oggetto basta invertire le istruzioni nel setup e inizializzarlo prima di fare altro.

Sulla seconda ti rassicuravo sul fatto che potevi fare tranquillamente prove con la EEPROM perchè i limiti di riscrittura sono ben ampi da sopportare qualche test a vuoto.

L'attesa delle 24h mi sa che è abbastanza critica, e scrivere sulla EEPROM mi pare una buona idea anche per proteggerti da anomalie quali riavvii o spegnimenti non previsti.
Dovessero capitare, non perderesti il conteggio del tempo di attesa.
Sarebbero poche scritture al mese e quindi nessun problema per la durata delle EEPROM

Quando fai la prima accensione, ti potresti salvare data e ora di ripartenza, sommando 24 ore alla data/ora in cui fai questa prima accensione.
Poi, prima di ogni annaffiatura verifichi se è passata questa data/ora e solo se è passata procedi con l'annaffiatura.

Questo test lo puoi fare in due punto a seconda di come ti trovi meglio.
1) prima di cambiare stato. E quindi verifichi periodicamente la condizione e quando si verifica cambi stato.
2) dopo il cambio stato per decidere se far partire l'operazione del nuovo stato oppure non ancora.
Nessuna buona azione resterà impunita!

Preistoria -> medioevo -> rinascimento -> risorgimento -> rincoglionimento!

Go Up