#include #include // a basic DS1307 library that returns time as a time_t #include #include #include // TODO: implementa festivi come riscaldamento // TODO: implementa questi: // Quando annaffio const int periodoInnaffio[][3] = { {1, 1, 0 }, {5, 1, 1}, {10, 1, 0} }; // Quando riscaldo const int periodoRiscaldo[][3] = { {1, 1, 1 }, {4, 1, 0}, {10, 15, 1} }; // Programma delle temperature desiderate. Parti sempre da 0,0,gradi. struct puntoTemp { int ora; int minuto; double temp; }; struct puntoTemp pgmTemp[] = { {0, 0, 18.0 }, {6, 0, 20.0}, {8, 0, 18.0}, {12, 0, 20.0}, {14, 0, 18.0}, {18, 0, 20.0}, {21, 0, 18.0} }; struct puntoTemp pgmTempWE[] = { {0, 0, 18.0 }, {6, 0, 20.0}, {21, 0, 18.0} }; double tolleranzaRisc = .1; // Data wire is plugged into pin 2 on the Arduino - termometro const int oneWireBus = 2; // x mettere a punto esegui questo e incolla come comando il risultato dentro un minicom // perl -e 'print( "T=" . ( time() + ( ( (localtime)[8] ? 7200 : 3600 ) ) ) . "\n")' const int pgmInnaffia[] = {5 * 60, 5 * 60, 5 * 60, 20 * 60}; // Prima lo mantenevo in minuti, ma preferisco adesso metterlo in secondi x coerenza con le altre grandezze // VEDI Innaffiatoio.ino x un abbozzo di sequenze se volessi aggiungere la possibilita' di definirle a runtime // Iniziamo l'annaffiata quotidiana alle 7:00 const int inizioGiro[][2] = { {7, 0 }, {21, 0} }; // Secondi di stacco che impongo tra una attivazione di un innaffiatotio e l'altro // Tentativo di eliminare i blocchi che accadono a volte nel passaggio repentino tra un innaffiatore e l'altro // Da vedere: se devo spegnere o meno il rele dei 24 volt (pinRete) const int stacco = 10; // Pin 13 has an LED connected on most Arduino boards. // give it a name: const int rele = 13; // Ora messi: pinRete in A2; pinRisc in A1; termometro in 2 con guardandolo in faccia, gnd - sig - + const int pinRisc = A1; const int pinRete = A2; const int pinZone[] = {3, 4, 5, 6}; const int lcdPins[] = {12, 11, 10, 9, 8, 7}; //=====[ CONSTANTS ]============================================================ #define DEBUG 1 // 0 = debugging disabled, 1 = enabled const int bSize = 50; // Aumento visto che immagino di potere anche inviare due comandi, es Temp=20;Timeout=3600 char Buffer[bSize]; // Serial buffer int risc = 0; // se il riscaldamento e' acceso adesso double forzarisc = 0.0; // force temperature. Attention, if negative value, means not forced. time_t fineforzarisc = 0L; // quando terminare una forzatura. Per non dimenticarsi una forzatura attiva... int attiva[] = {0, 0, 0, 0}; // imposta a 1 il posto della zona che vuoi attivare int rete = 0; // imposta a 1 per attivare il rele dei 24 volt int forza[] = {0, 0, 0, 0}; // Lo uso x forzare a acceso un certo stato di una certa linea. Andra' spenta esplicitamente!! time_t fineforza = -1L; // quando terminare una forzatura. Per non dimenticarsi una forzatura attiva... int forzarete = 0; // Lo uso x forzare accensione rete 24v. Andra' spenta esplicitamente!! time_t giro = -1L; // quando iniziare il giro. Uso questa variabile separata x gestire le forzature di giri. time_t ultimaattivazione = 0; // tiene traccia dell'ultima attivazione di un irrigatore, per poi spegnere la rete 24v time_t nonprima = 0; // non annaffia prima di. Usato per saltare qualche giorno se piove. int verb = 0; long numgiro = 0L; // Setup a oneWire instance to communicate with any OneWire devices // (not just Maxim/Dallas temperature ICs) OneWire oneWire(oneWireBus); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); LiquidCrystal lcd(lcdPins[0], lcdPins[1], lcdPins[2], lcdPins[3], lcdPins[4], lcdPins[5]); void scalda() { char bf[84]; // call sensors.requestTemperatures() to issue a global temperature // request to all devices on the bus sensors.requestTemperatures(); // Send the command to get temperatures double temperatura; // uso questa variabile per tenere la lettura della temperatura temperatura = sensors.getTempCByIndex(0); // Why "byIndex"? // You can have more than one IC on the same bus. // 0 refers to the first IC on the wire int w = winter(); int wd = weekday(); int we = wd == 1 || wd == 7; int fz = 0; double requiredTemp = 0.0; if( now() < fineforzarisc ) { requiredTemp = forzarisc; fz = 1; } else { fineforzarisc = 0L; forzarisc = 0.0; int i = hour() * 100 + minute(); puntoTemp *pt; int ptn; if( we ) { pt = pgmTempWE; ptn = sizeof(pgmTempWE)/sizeof(pgmTempWE[0]); } else { pt = pgmTemp; ptn = sizeof(pgmTemp)/sizeof(pgmTemp[0]); } for( int n = 0; n < ptn; n++ ) { if( i >= pt[n].ora * 100 + pt[n].minuto ) { requiredTemp = pt[n].temp; } } fz = 0; } sprintf(bf, "%02d:%02d:%02d ", hour(), minute(), second()); Serial.print(bf); Serial.print("Temp: "); Serial.print(temperatura); Serial.print(" Req: "); Serial.print(requiredTemp); if( fz || w ) { if( ! risc ) { risc = temperatura <= requiredTemp - tolleranzaRisc ? 1 : 0; } else { risc = temperatura >= requiredTemp + tolleranzaRisc ? 0 : 1; } if( risc ) { Serial.print(" On"); digitalWrite(pinRisc, LOW); } else { Serial.print(" Off"); digitalWrite(pinRisc, HIGH); } } else { risc = 0; Serial.print(" ==="); digitalWrite(pinRisc, HIGH); } if ( fz || verb ) { // sprintf in Arduino (o AVR) non compila il supporto float in printf //sprintf(bf, " t %2.2f", forzarisc); Serial.print(" forza "); dtostrf(forzarisc, 5, 2, bf); Serial.print(bf); TimeElements tmp; breakTime(fineforzarisc, tmp); sprintf(bf, " fine %04d-%02d-%02d %02d:%02d:%02d", tmp.Year + 1970, tmp.Month, tmp.Day, tmp.Hour, tmp.Minute, tmp.Second); Serial.print(bf); } Serial.println(""); lcd.setCursor(0, 0); sprintf(bf, "%02d:%02d %02d-%02d ", hour(), minute(), day(), month()); lcd.print(bf); if( we ) { lcd.print("we"); } else { lcd.print(" "); } lcd.setCursor(0, 1); dtostrf(temperatura, 4, 1, bf); lcd.print(bf); lcd.print("/"); dtostrf(requiredTemp, 4, 1, bf); lcd.print(bf); if( fz ) { lcd.print("F"); } else if( ! w ) { lcd.print("="); } else { lcd.print(" "); } } void annaffia() { char bf[84]; long diff = now() - giro; // Se e' l'orario indicato da inizioGiro, allora diciamo che il giro va iniziato adesso. Calcoliamo i tempi in unixtime (per noi, in secondi) // Mi serve fare tutto questo 'giro' per gestire la forzatura. // Notare che in questo modo pero' il giro inizia solo se la presente funzione viene invocata nel minuto indicato da inizioGiro; // inoltro quando passiamo dall'orario inizioGiro, sopprimiamo l'eventuale forzatura di un nuovo giro, se in atto. Pazienza... for( int n = 0; n < sizeof(inizioGiro)/sizeof(inizioGiro[0]); n++ ) { if( hour() == inizioGiro[n][0] && minute() == inizioGiro[n][1] && abs(diff) > 60 ) { giro = now(); diff = 0; break; } } if( forza[0] || forza[1] || forza[2] || forza[3] ) { if( now() < fineforza ) { attiva[0] = forza[0] ? 1 : 0; attiva[1] = forza[1] ? 1 : 0; attiva[2] = forza[2] ? 1 : 0; attiva[3] = forza[3] ? 1 : 0; ultimaattivazione = now(); } else { attiva[0] = forza[0] = 0; attiva[1] = forza[1] = 0; attiva[2] = forza[2] = 0; attiva[3] = forza[3] = 0; } } else if( now() < nonprima || ! summer() ) { for( int n = 0; n < sizeof(forza)/sizeof(forza[0]); n++ ) { attiva[n] = 0; } } else { int inizio = 0; int fine = 0; for( int n = 0; n < sizeof(forza)/sizeof(forza[0]); n++ ) { fine = inizio + pgmInnaffia[n]; attiva[n] = diff >= inizio && diff < fine ? 1 : 0; /* sprintf(bf, "now %ld giro %ld zona %d attiva %d diff %ld inizio %d fine %d ", now(), giro, n, attiva[n], diff, inizio, fine); Serial.println(bf); */ if( attiva[n] ) ultimaattivazione = now(); inizio = fine + stacco; } } rete = forzarete ? 1 : now() - ultimaattivazione < stacco ? 1 : 0; if( verb ) { sprintf(bf, "%04d-%02d-%02d ", year(), month(), day()); Serial.print(bf); } sprintf(bf, "%02d:%02d:%02d %01d %01d%01d%01d%01d", hour(), minute(), second(), rete, attiva[0], attiva[1], attiva[2], attiva[3]); Serial.print(bf); if( forza[0] || forza[1] || forza[2] || forza[3] || forzarete || verb ) { sprintf(bf, " r %01d f %01d%01d%01d%01d", forzarete, forza[0], forza[1], forza[2], forza[3]); Serial.print(bf); } if( giro <= now() || verb ) { TimeElements gtm; breakTime(giro, gtm); sprintf(bf, " g %04d-%02d-%02d %02d:%02d:%02d", gtm.Year + 1970, gtm.Month, gtm.Day, gtm.Hour, gtm.Minute, gtm.Second); Serial.print(bf); } if( now() <= nonprima || verb ) { TimeElements np; breakTime(nonprima, np); sprintf(bf, " s %04d-%02d-%02d %02d:%02d:%02d", np.Year + 1970, np.Month, np.Day, np.Hour, np.Minute, np.Second); Serial.print(bf); } Serial.println(""); lcd.setCursor(0, 2); sprintf(bf, "%01d %01d%01d%01d%01d", rete, attiva[0], attiva[1], attiva[2], attiva[3]); lcd.print(bf); if( forza[0] || forza[1] || forza[2] || forza[3] || forzarete ) { sprintf(bf, " %01d %01d%01d%01d%01d", forzarete, forza[0], forza[1], forza[2], forza[3]); lcd.print(bf); } for( int n = 0; n < sizeof(pinZone)/sizeof(pinZone[0]); n++ ) { digitalWrite(pinZone[n], attiva[n] ? LOW : HIGH); // Stranamente, settando LOW il NO si chiude, e settandolo HIGH si chiude il NC } digitalWrite(pinRete, rete ? LOW : HIGH); } void gestisciForzature(char * Data) { for( int n = 0; n < sizeof(forza)/sizeof(forza[0]); n++ ) { forza[n] = strchr(Data, '0' + n) == NULL ? 0 : 1; if( forza[n] ) fineforza = now() + ( n > 2 ? 20 : 5 ) * 60; } // Annulliamo eventuali giri precedentemente richiesti giro = -1L; } void help() { Serial.println("t=nn.dd Force temperature "); Serial.println("g Inizia un giro immediatamente"); Serial.println("f=n Forza a 1 le zone indicate "); Serial.println(" La prima zona e' la 0. "); Serial.println(" Per resettare, f= senza argomenti"); Serial.println("r Forza la rete 24V "); Serial.println(" Ripetere per resettare "); Serial.println("s=n Salta n giorni da ora "); Serial.println("T=nnnnn Imposta l'ora al detto unix time"); Serial.println("v Verbose mode "); Serial.println("h Visualizza questo help "); } int SerialParser() { int ret = 0; int ByteCount = -1; char Command[10]; // Arbitrary Value for command size char Data[15]; // ditto for data size if (Serial.available() > 0) { memset(Buffer, 0, sizeof(Buffer)); // Clear contents of Buffer ByteCount = Serial.readBytesUntil('\n',Buffer,bSize); if (ByteCount > 0) { ret = 1; char * b = Buffer; while( b && *b ) { strcpy(Command,strtok(b,"=")); if( strcmp(Command, "t") == 0 ) { b+=2; // 2 = length "t=" strcpy(Data,b); if( strlen(Data) ) { forzarisc = atof(Data); fineforzarisc = now() + 60 * 60 * 4; // Mi rifiuto di forzare x + di 4 ore, visto che mi dimentico sempre di resettarlo. } else { fineforzarisc = 0L; forzarisc = 0.0; } } else if( strcmp(Command, "f") == 0 ) { b+=2; // 2 = length "f=" ; avanziamo perche' la strtok rimpiazza il delimiter con null, e dopo non troverei + il ; strcpy(Data,b); gestisciForzature(Data); } else if( strcmp(Command, "T") == 0 ) { // metto a punto l'ora b+=2; // 2 = length "T=" strcpy(Data,b); unsigned long pctime = 0L; pctime = atol(Data); if (pctime != 0) { if( ! RTC.set(pctime) ) { // set the RTC and the system time to the received value Serial.println("Errore settando RTC"); } setTime(pctime); } } else if( strcmp(Command, "r") == 0 ) { b+=1; // 1 = length "r" forzarete = 1 - forzarete; } else if( strcmp(Command, "g") == 0 ) { b+=1; // 1 = length "g" giro = now(); memset(forza, 0, sizeof(forza)); } else if( strcmp(Command, "s") == 0 ) { b+=2; // 2 = length "s=" strcpy(Data,b); unsigned long days = 0L; days = atol(Data); if (days != 0) { nonprima = now() + days * 60*60*24; } } else if( strcmp(Command, "v") == 0 ) { b+=1; // 1 = length "v" verb = 1 - verb; } else if( strcmp(Command, "h") == 0 ) { b+=1; // 1 = length "h" help(); } if( b = strchr( b, ';' ) ) b++; } } //Serial.flush(); // da Arduino 1.0 significa flush dell'output, non dell'input } return ret; } // Not exactly summer, but water period int summer() { int ret = 0; int i = month() * 100 + day(); for( int n = 0; n < sizeof(periodoInnaffio)/sizeof(periodoInnaffio[0]); n++ ) { if( i >= periodoInnaffio[n][0] * 100 + periodoInnaffio[n][1] ) { ret = periodoInnaffio[n][2]; } } return ret; } // Not exactly winter int winter() { int ret = 0; int i = month() * 100 + day(); for( int n = 0; n < sizeof(periodoRiscaldo)/sizeof(periodoRiscaldo[0]); n++ ) { if( i >= periodoRiscaldo[n][0] * 100 + periodoRiscaldo[n][1] ) { ret = periodoRiscaldo[n][2]; } } return ret; } void setup(void) { // initialize the digital pin as an output. pinMode(rele, OUTPUT); pinMode(pinRisc, OUTPUT); pinMode(pinRete, OUTPUT); for( int n = 0; n < sizeof(pinZone)/sizeof(pinZone[0]); n++ ) { pinMode(pinZone[n], OUTPUT); } risc = 0; forzarisc = 0.0; fineforzarisc = 0L; rete = 0; forzarete = 0; fineforza = -1L; giro = -1L; memset(attiva, 0, sizeof(attiva)); memset(forza, 0, sizeof(forza)); verb = 0; numgiro = 0L; // start serial port // ATTENZIONE lo porto a 115200, prima ero a 9600 Serial.begin(115200); sensors.begin(); lcd.begin(20, 4); setSyncProvider(RTC.get); // the function to get the time from the RTC if (timeStatus() != timeSet) Serial.println("Unable to sync with the RTC"); else Serial.println("RTC has set the system time"); RTC.sqw(1); } void loop(void) { numgiro++; SerialParser(); scalda(); annaffia(); delay(1000); }