Progetto serra con Arduino e Rtc DS1307

Buongiorno, volevo condividere con voi un problema riscontrato con un progetto definitivo. Ho usato un Arduino UNO R4 WiFi, un RTC DS1307, un display TFT SPI e un relè a 5V.

Si tratta di un’irrigazione automatica e lì per lì funziona tutto, il problema sorge dopo qualche giorno, in cui l’orologio mi va “in palla” e inizia a segnare 00:00 oppure 00:22 è si ferma lì.

Se viene resettato, il tutto riparte senza problemi all’orario corrente, quindi l’ora viene mantenuta.

Allego il codice:

//Ultimo aggiornamento 01/08/2025

#include "icons.h"
//LIBRERIE 
#include <Adafruit_BusIO_Register.h>
#include <Adafruit_GenericDevice.h>
#include <Adafruit_I2CDevice.h>
#include <Adafruit_I2CRegister.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>
#include <RTClib.h>
#include <Wire.h>

//OGGETTI
RTC_DS1307 rtc;

#define PIN_POMPA 4

//VARIABILI
int hh, mm;
int terra; 
int th_terra = 680;
bool TIMER;
bool POMPA;
bool stato_pompa = false;

//TIMERS
unsigned long t1, dt1;
unsigned long t2, dt2;
unsigned long t3, dt3;
unsigned long t4, dt4;
unsigned long ton_pompa;

//DISPLAY
#define TFT_CS  10
#define TFT_RST  9
#define TFT_DC   8
#define TFT_MOSI 11  
#define TFT_SCLK 13

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

void setup() {
  Serial.begin(9600);
  delay(1500);
  Serial.print("START");
  if (!rtc.begin()) {
    Serial.println("Verifica connessioni");
    while(true);
  }
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  pinMode(2, INPUT_PULLUP);
  pinMode(PIN_POMPA, OUTPUT);
  digitalWrite(PIN_POMPA, LOW);
  inizializza_tft();
  leggiOra();
  leggiSensori();
}

void loop() {  
 dt1 = millis() -t1;
  if (dt1 >= 1000) {
   t1 = millis();
   leggiSensori();
   leggiOra();       
  }
  dt2 = millis() - t2;
  if (dt2 >= 8500) {
   t2 = millis();
   stampaValori();    
   Serial.print("timer =");
   Serial.println(TIMER);    
   Serial.print("pompa =");
   Serial.println(POMPA);   

  }
  if (POMPA) {
    dt4 = millis() - t4;
    if (dt4 >= ton_pompa) {
      t4 = millis();
      stato_pompa = !stato_pompa;
    }
     if (stato_pompa) {
      ton_pompa = 2000;
      digitalWrite(PIN_POMPA, HIGH);
    } else {
      ton_pompa = 10000;
      digitalWrite(PIN_POMPA, LOW);
   }
  }
}

 void leggiOra() {
  DateTime now = rtc.now();
  hh = now.hour();
  mm = now.minute();
  String time = String(hh) + ":" + String(mm);
  char TIME[5];
  sprintf(TIME, "%02d:%02d",hh, mm);
  Serial.println(TIME);
  if ((hh <= 7) || (hh >= 18)) {
     TIMER = true;
    } else { 
     TIMER = false;
   } 
  }

 void leggiSensori() {
 for (int i = 0; i < 10; i++) {
    terra+= analogRead(A0);
  }
 terra = terra/10;
 if (TIMER) {
   if (terra >= th_terra) {
    POMPA = true;
   }else {
    POMPA = false;
    digitalWrite(PIN_POMPA, LOW);
   }
  }
 }

 void inizializza_tft() {
 tft.init(240, 320);       
 tft.fillScreen(ST77XX_WHITE);
 tft.setRotation(3);
 tft.fillRect(0, 0, 120, 220, ST77XX_BLACK);
 tft.drawBitmap(0, 0,image_data_icons, 120, 240, ST77XX_WHITE);
}

 void stampaValori(){
 tft.setTextSize(6);
 tft.setTextColor(ST77XX_BLACK);
 String time = String(hh) + ":" + String(mm);
 char TIME[5];
 sprintf(TIME, "%02d:%02d",hh, mm);
 tft.fillRect(125, 0, 200, 240, ST77XX_WHITE);
 tft.setCursor(130,150);
 tft.print(TIME);
 tft.setCursor(150, 40);
 tft.setTextSize(6);
 tft.print(terra);
}




Ti segnalo che, nella sezione in lingua Inglese, si può scrivere solo in Inglese ... quindi, per favore, la prossima volta presta più attenzione in quale sezione metti i tuoi post; questa volta esso è stato spostato, da un moderatore della sezione di lingua Inglese, nella sezione di lingua Italiana.

Grazie.

Scusa, non mi ero accorto! Grazie

1 Like

@marco199:

A quanto detto da pert aggiungo ...

... cortesemente, come prima cosa, leggi attentamente il REGOLAMENTO della sezione Italiana del forum, (... e, per evitare future possibili discussioni/incomprensioni, prestando sempre molta attenzione al punto 15), dopo di che, come da suddetto regolamento (punto 16.7), fai la tua presentazione NELL'APPOSITA DISCUSSIONE (... quello che vedi in blu è un link, fai click su di esso per raggiungere la discussione) spiegando bene quali esperienze hai in elettronica e programmazione, affinché noi possiamo conoscere la tua esperienza ed esprimerci con termini adeguati.

Grazie,

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposita discussione, nel rispetto del succitato regolamento nessuno ti risponderà (eventuali risposte o tuoi ulteriori post, verrebbero temporaneamente nascosti), quindi ti consiglio di farla al più presto. :wink:

P.P.S.: Evitate di utilizzare la traduzione automatica fatta dal browser ... vi impedisce di capire la lingua della sezione dove andate a scrivere ...

Ok, ho fatto la presentazione e letto il regolamento

A che servono gli String time?…

TIMER è vero dalle 18.00 alle 7.59, verificando solo le ore. Così dev’essere?

I nomi delle variabili non sono comprensibili… Per esempio:
if(POMPA) {stato_pompa =! stato_pompa}
non si capisce che cosa faccia: se stato_pompa è lo stato, che cos’è POMPA? Tra l’altro, essendo scritto maiuscolo, dovrebbe essere una costante, ma non lo è.
Che cosa sono terra e th_terra?
Metti anche dei commenti (utili!).

TIMER verifica le ore, esatto.

Gli string time servono a creare una stringa per poi fare quello che si chiama padding, ovvero aggiungere gli zeri necessari ad avere il formato “hh:mm”. Ad esempio 08:07 e non 8:7.

Le variabili sono dichiarate all'inizio: POMPA è booleano, quindi è true o false; stato_pompa è lo stato del pin, quindi 0 o 1. Se POMPA è true, lo stato si inverte.

terra è la lettura del sensore sul pin A0, mentre th_terra è la soglia impostata oltre il quale POMPA passa dallo stato LOW allo stato HIGH.

In pratica uso un sensore di umidità del terreno, salvo il valore letto nella variabile terra e lo verifico con la soglia impostata th_terra. Se TIMER è vero e terra è maggiore o uguale a th_terra, allora POMPA è vero (accendo il pin per il tempo impostato)

Toglili

Non è escluso che siano la causa
Comunque fa sempre bene estirpare gli String

Qualunque cosa si possa fare con essi si fa (meglio) con le stringhe di C

2 Likes

Soprattutto, time non è usata!

Intendi creare un array di caratteri di tipo char time?

Se la usi sì

Ma se non la usi estirpa e basta

1 Like

La variabile String time l’hai definita ed inizializzata, ma non la stai usando da nessuna parte. Sono piuttosto certo che il tuo problema non dipenda da questo…

Lo zero padding lo stai facendo usando l’istruzione sprintf che è una delle istruzioni di manipolazione delle C-string classiche.

Nel tuo codice ci sono alcuni problemi di gestione delle variabili globali, come ad esempio la variabile terra che non viene azzerata quando ricominci la lettura. Stessa cosa con lo stato della pompa ed il relativo timer.

Ad ogni modo, io credo che il tuo problema si di tutt’altra natura ovvero che il blocco della scheda sia dovuto al relè e al carico induttivo che esso va a pilotare, cosa del resto piuttosto comune quando non si usano le dovute accortezze (se cerchi nel forum, puoi trovare decine di post in merito).

1 Like

2 cifre di ore

2 di minuti

Il due punti

Fa 5 caratteri

E il terminatore?

Questo anche spiega come mai scriva l'ora sbagliata, quando si impalla

Anch'io avevo pensato al relè, ma il carico induttivo è separato dai 5V di Arduino, in quanto funziona a 12V e hanno in comune le masse.
Il timer dt4 viene resettato ad ogni chiamata quando viene invertito lo stato_pompa. Il tutto provato con il Serial monitor, il timer dt4 si resetta quando si rientra nell'if.
Ad ogni modo proverò ad inserire uno snubber sul contatto del relè

Vero, ma non dovrebbe impallarsi sempre?

Già questo è sufficiente il più delle volte. Se riesci separa galvanicamente il circuito dei 12V da quello dei 5V compreso il GND.

1 Like

Le variabili locali sono allocate a runtime nello stack e dopo l’uso lo stack non viene ripulito, per cui ci si trova spazzatura. Potrebbe esserci rimasto uno zero e allora funziona, ma è un caso.

Ciao.

Spiegati meglio, come si fa a "ripulire"?

Non serve ripulirla

Ci scrivi sopra subito dopo quindi non serve

Invece serve, assolutamente, dimensionarla ad almeno 6

1 Like

Come ti ha consigliato @Maurotec

La variabile TIME è allocata nello stack ed inizializzata con tutti 0, quindi sarà correttamente terminata a prescindere dal contenuto se rimani nel limite della dimensione dell’array. Non hai bisogno di fare altro.

char TIME[6] = {0};

Se usi snprintf(), sei sicuro che anche in caso di stringa più lunga del previsto, le aree di memoria adiacenti non verranno sovrascritte accidentalmente. In questo caso il rischio è pressoché nullo, ma è bene prenderla come abitudine.

snprintf(TIME, sizeof(TIME), "%02d:%02d",hh, mm); 

edit:

scusate, mi sono sovrapposto.

1 Like