Incubatrice

Buonasera a tutti, scrivo per la prima volta sul forum e mi affaccio al mondo di arduino da poco.
Dopo aver fatto i primi esercizi per prender mano con arduino mi avvio verso il mio primo progetto vero e proprio. Vorrei creare una incubatrice. L ho gia creata fisicamente, con il dht11 che mi misura temperatura e umidità, una lampadina comandata da relè e un servomotore per inclinare il piano e quindi girare le uova. Per il dht11 e il relè che accende la lampada non ho avuto problemi ma quando ho inserito il servomotore c ho un problema con la funzione millis() perchè vorrei che il servomotore si azionasse ogni due ore ma utilizzando i delay mi si blocca tutto il programma per due ore e perdo anche il controllo della temperatura. Ho provato ad usare il millis() ma non riesco ad ottenere ciò che voglio. Non so se sono stato chiaro, spero di si. Questo è il codice:

#include <dht11.h>
#include <Servo.h>
Servo motor;
#define rele 7
#define DHTPIN 8
dht11 DHT;
unsigned long time;
unsigned long servotime;
unsigned long letturadht;
 
void setup() {
  pinMode(rele,OUTPUT);
  Serial.begin(9600);
  motor.attach(9);
  time = millis();
  servotime = millis();
  letturadht = millis();
}

void loop() {
  time = millis();
  if(time>letturadht+1000){
    int ctrl;
    ctrl = DHT.read(DHTPIN); 
    Serial.print("Temperatura: ");
    Serial.println(DHT.temperature,DEC);
    Serial.print("Umidità: ");
    Serial.println(DHT.humidity,DEC);
  }
  letturadht = millis();
  
  if(DHT.temperature<=36){
    digitalWrite(rele,HIGH);
  }else if(DHT.temperature>=39){
    digitalWrite(rele,LOW);
  }
  
  if(time>servotime+10000){
    for(int i=15; i<75; i++){
      motor.write(i);  
      delay(200);  
    }
    
    for(int i=75; i>=15; i--){
      motor.write(i);
      delay(200);
    }
    servotime=millis();
  }
}

Di preciso cos’è che non fa?
Così al volo vedo:

  • time potrebbe essere una parola riservata… cambia nome alla variabile
  • letturadht = millis(); lo porterei dentro la graffa dell’if
  • per adesso non influisce sul funzionamento, ma meglio se cambi gli if del millis() in questo modo
    if (millis() - tempo_precedente > intervallo)
    prova a cercare come gestire l’overflow di millis() così capisci il potenziale problema.

giustamente non ho spiegato il reale problema. Il problema sta nel servomotore perchè visto che deve effettuare una rotazione in due ore, usando i delay mi si fermerebbe tutto il programma per due ore e quindi perderei il controllo anche sulla temperatura che potrebbe schizzare a piu’ di 40°, mentre utilizzando la funzione millis() dovrei evitare tutto ciò, cioè il servomotore sta fermo due ore prima di effettuare la rotazione ma al contempo la temperatura viene sempre letta.
Grazie della risposta apporterò le correzioni che mi hai indicato

Il ragionamento l'avevo capito. Non ho capito cosa non va...
Non muove il servo? Non legge la temperatura? Cos'altro?

il fatto è che cosi come è adesso leggo la temperatura e umidità quando il servomotore finisce il suo ciclo, cioè ogni due ore, anche se nel codice è messo a dieci secondi per prova, e in quel tempo la temperatura potrebbe sballare parecchio di più dei 38/39 gradi consentiti

Non ho capito... il servo gira ogni 2 ore oppure il servo impiega 2 ore a girare?

il servo gira ogni due ore, al contempo devo tener presente la temperatura senza aspettare le due ore con la funzione millis()

Forse l'ora tarda mi gioca brutti scherzi, ma lo sketch così come l'hai postato dovrebbe tenere occupato il micro solo 24 secondi, che sono (se non ho sbagliato i conti) la somma dei delay() dei for (60 * 200 * 2).
Mi sono perso qualcosa?

  time = millis();
  if(time>letturadht+1000){
    ....
    ....
    ....
  }
  letturadht = millis();

letturadht = millis() dovrebbe essere dentro la if e non fuori

  time = millis();
  if(time>letturadht+1000){
    ....
    letturadht = millis();
    ....
  }

fratt:
Forse l'ora tarda mi gioca brutti scherzi, ma lo sketch così come l'hai postato dovrebbe tenere occupato il micro solo 24 secondi, che sono (se non ho sbagliato i conti) la somma dei delay() dei for (60 * 200 * 2).
Mi sono perso qualcosa?

ok, ma è proprio questo il problema. Io vorrei che il servo vada, ad esempio, da 0 a 90 in due ore e da 90 a 0 in altre due ore.

-zef-:

  time = millis();

if(time>letturadht+1000){
   ....
   ....
   ....
 }
 letturadht = millis();



letturadht = millis() dovrebbe essere dentro la if e non fuori


time = millis();
 if(time>letturadht+1000){
   ....
   letturadht = millis();
   ....
 }

grazie zef, ho già provveduto a correggere tramite segnalazione di fratt :slight_smile:

Dal momento che il tuo è un programma abbastanza semplice e già definito nella sua struttura, dunque l’obiettivo è stato quasi raggiunto e mancano solo pochi dettagli, ho apportato alcune aggiunte e correzioni che ti spiego.

Intanto ecco il programma modificato:

#include <dht11.h>
#include <Servo.h>
Servo motor;
#define rele 7
#define DHTPIN 8
dht11 DHT;
unsigned long tempo;
unsigned long servotime;
unsigned long letturadht;

int NumOre = 2;			//si può impostare un tempo da una a più ore
unsigned long OraAzionamServo = 60 * NumOre * 60 * 1000;
bool inversione = 0;		//inverte il senso di rotazione del servomotore ogni tot ore

void setup() {
  pinMode(rele, OUTPUT);
  Serial.begin(9600);
  motor.attach(9);
  tempo = servotime = letturadht = millis();
}

void loop() {

  tempo = millis();

  if (tempo - letturadht >= 1000) {	//ogni secondo faccio una lettura
    int ctrl;
    ctrl = DHT.read(DHTPIN);
    Serial.print("Temperatura: ");
    Serial.println(DHT.temperature, DEC);
    Serial.print("Umidità: ");
    Serial.println(DHT.humidity, DEC);
    letturadht = tempo;
  }

  if (DHT.temperature <= 36) {
    digitalWrite(rele, HIGH);
  }
  else if (DHT.temperature >= 39) {
    digitalWrite(rele, LOW);
  }

  if (tempo - servotime >= OraAzionamServo) {

    int i = 0;

    if (inversione) {
      for (i = 75; i >= 15; i--) {
        motor.write(i);
        delay(200);
      }
      for (i = 15; i < 75; i++) {
        motor.write(i);
        delay(200);
      }
    }
    else {
      for (i = 15; i < 75; i++) {
        motor.write(i);
        delay(200);
      }
      for (i = 75; i >= 15; i--) {
        motor.write(i);
        delay(200);
      }
    }
    inversione = !inversione;
    servotime = letturadht = millis();
  }

}

Spiegazione delle parti aggiunte:

int NumOre = 2; //si può impostare un tempo da una a più ore
unsigned long OraAzionamServo = 60 * NumOre * 60 * 1000;
bool inversione = 0; //inverte il senso di rotazione del servomotore ogni tot ore

Qua vengono definiti il tempo in cui azionare il servomotore e la variabile booleana inversione, entrambe sono variabili globali. Nella variabile NumOre puoi impostare un ammontare di ore arbitrario.

if (tempo - letturadht >= 1000) { //ogni secondo faccio una lettura

letturadht = tempo;
}

Questo è il metodo che ti era stato consigliato per utilizzare millis.

if (tempo - servotime >= OraAzionamServo) {

inversione = !inversione;
servotime = letturadht = millis();
}

Adesso siamo arrivati al nocciolo della questione.
L’istruzione “inversione = !inversione;” commuta, treamite l’operatore negazione !, lo stato della variabile 0 a 1 o da 1 a 0, a seconda dell’ultimo stato logico assunto dalla stessa. Quindi il precedente if-else, testando lo stato di inversione, riesce a eseguire la rotazione del motore in un senso e poi nell’altro ogni 2 ore. Nell’if-else le due istruzioni for di movimentazione sono state invertite nel primo caso (if) e lasciate inalterate rispetto al tuo codice originale nel secondo (else).
La prima volta che il motore viene movimentato, sarà eseguito l’else visto che lo stato iniziale di inversione è =0.

EDIT: occhio che non so se è del tutto corretto aver invertito i due cicli for. Non ho valutato se l’albero del motore torna al punto di origine nel caso dell’inversione. Prova a diminuire il tempo di rotazione, con l’accortezza di far funzionare il motore a vuoto cioè senza carico applicato, e verifica cosa succede. Se la rotazione non avviene correttamente, modifica i cicli for nella sezione dell’inversione.

servotime = letturadht = millis();

Quest’istruzione aggiorna il tempo di entrambe le variabili marcatempo uguagliandole al tempo ritornato da millis(). Il motivo è che l’uso del delay, che “fa perdere tempo”, comporta che il tempo registrato precedentemente nelle variabili letturadht e tempo non sia più attendibile visto che durante la rotazione del motore è trascorso del tempo e tale tempo non è stato conteggiato nelle variabili stesse.

Spero di essermi spiegato bene e soprattutto che il codice funzioni :D.

Bisogna considerare che millis() non è “preciso”. Sarebbe utile utilizzare un qualche rtc esterno per avere le due ore esatte.

Non avevo prestato attenzione a questo punto:

il fatto è che cosi come è adesso leggo la temperatura e umidità quando il servomotore finisce il suo ciclo, cioè ogni due ore, anche se nel codice è messo a dieci secondi per prova, e in quel tempo la temperatura potrebbe sballare parecchio di più dei 38/39 gradi consentiti

La cosa più semplice che si può fare è di interrompere il riscaldamento, cioè spegnere la lampada, durante la rotazione delle uova se ciò non incide troppo sul tempo ciclo e non arreca danni di altro tipo. Se poi si riesce ad accorciare al massimo il tempo di rotazione in modo da renderlo il più trascurabile possibile, perciò diminuire i 200 ms nei delay(200), ancora meglio.

if (tempo - servotime >= OraAzionamServo) {
digitalWrite(rele, LOW);
...
cicli for per rotazione motore
...
digitalWrite(rele, HIGH);
}

Se puoi/vuoi posta qualche foto del tuo progetto, mi ha incuriosito :D.

lyon25:
ok, ma è proprio questo il problema. Io vorrei che il servo vada, ad esempio, da 0 a 90 in due ore e da 90 a 0 in altre due ore.

Ok, adesso è chiaro...
Allora dovresti eliminare i for e usare il loop() come ciclo per muovere il servo.
Tipo imposti una variabile per la direzione e una per la posizione del servo. Ad ogni giro del loop() controlli se è passato il tempo (2h / 60 passi del servo). Se è passato allora incrementi / decrementi la variabile posizione e sposti il servo. Se arrivi a 15 o 75 inverti la direzione.