Relè impazzito, forse codice errato

Questo è il primo codice che scrivo, abbiate pietà!
Sembra funzionare tutto, solo che riscontro alcuni errori.
Il primo errore è che il relè si accende e si spegne molto velocemente come impazzito. Il relè si accende quando la temperatura letta da un sensore è più bassa di quella che ho impostato (sto facendo un termostato).
Inoltre ho notato che se uso il "delay" l'orario letto dall'rtc non è preciso, salta sempre un secondo su ogni minuto che passa.
Adesso l'orario va bene, ma il relè scatta in continuazione.
Inserisco il codice che sto utilizzando adesso:

#include <WProgram.h>
#include <Wire.h>
#include <DS1307.h>
#include <LiquidCrystal.h>
#define led 13 // led collegato a digital pin 13
#define relay 11 // relè collegato a digital pin 11
int letture=4; // numero di letture del sensore
int PIN=0; // lm35 collegato ad analog in pin 0
float TEMP=0.0; // variabile per la temperatura
int count=0; // variabile per il conteggio della temperatura nel ciclo for
int rtc[7]; // array per i valori dell'rtc ds1307
LiquidCrystal lcd(2, 3, 4, 5, 6, 7, 8); // impostazione pin del display

float temperatura() // definzione funzione temperatura
{
TEMP=0.0; // inizializzo il valore della temperatura
for(count=0;count<letture;count++) // ripeto il ciclo sotto per 4 volte
{
TEMP+=analogRead(PIN); // sommo il valore letto dal sensore a TEMP
delayMicroseconds(250000); // aspetto 0.25 secondi
}
TEMP/=letture; // divido il valore della temperatura restituito dal ciclo for
// per il numero di letture effettuate
TEMP = ( 5.0 * TEMP * 100.0) / 1024.0; // converto la temperatura
}

void setup()
{
pinMode(led,OUTPUT); // setto il pin 13 del led come uscita
pinMode(relay,OUTPUT); // setto il pin 11 del relè come uscita
lcd.begin(16,2); // inizializzo il display lcd (16 colonne, 2 righe)

// lo script qui sotto serve per impostare l'orario;
// dopo aver fatto l'upload del programma occorre commentare il blocco e rifare l'upload
// altrimenti ogni volta che si toglie corrente all'arduino, l'ora verrà reimpostata secondo
// lo script sotto. L'orario verrà memorizzato dal DS1307 se provvisto di batteria ausiliaria.

RTC.stop();
RTC.set(DS1307_SEC,1);
RTC.set(DS1307_MIN,59);
RTC.set(DS1307_HR,22);
RTC.set(DS1307_DOW,2);
RTC.set(DS1307_DATE,18);
RTC.set(DS1307_MTH,2);
RTC.set(DS1307_YR,9);
RTC.start();
}

void loop()
{
lcd.setCursor(0,0); // scrivo sulla prima riga del display
RTC.get(rtc,true);
if (rtc[2]<=9) // inserisco uno 0 se l'ora letta va da 0 a 9
{ // e passo il valore al display, idem con minuti e secondi
lcd.print("0");
lcd.print(rtc[2]); // rtc[2] indica le ore
}
else
{
lcd.print(rtc[2]);
}
lcd.print(":");
if (rtc[1]<=9) // rtc[1] indica i minuti
{
lcd.print("0");
lcd.print(rtc[1]);
}
else
{
lcd.print(rtc[1]);
}
lcd.print(":");
if (rtc[0]<=9) // rtc[0] indica i secondi
{
lcd.print("0");
lcd.print(rtc[0]);
}
else
{
lcd.print(rtc[0]);
}
if (rtc[2]==23 && rtc[1]==0 &&rtc[0]==1) // creazione del timer, confronto le ore,
{ // i minuti e i secondi con dei valori da me impostati
digitalWrite(led, HIGH); // quando i valori corrispondono accendo il led
}
if (rtc[2]==23 && rtc[1]==1 && rtc[0]==1) // come sopra, ma stavolta
{ // spengo il led
digitalWrite(led, LOW);
}
lcd.setCursor(0,1); // scrivo sulla seconda riga del display
temperatura(); // richiamo la funzione temperatura sopra creata
lcd.print(TEMP); // scrivo sul display la temperatura e la stringa ^C
lcd.print("^C"); // vorrei mettere il simbolo giusto per i gradi (°) ma non va
if (TEMP<=37.5) // ecco il termostato, ovvero quando la temperatura è minore di 37,5°
{
digitalWrite(relay, HIGH); // accendo il relè
}
else // in caso contrario
{
digitalWrite(relay, LOW); // spengo il relè
}
}

Il relè con un altro script funzionava bene, usavo il delay sul ciclo for per fare una media della temperatura rilevata ma però ho notato che l'orario saltava sempre di un secondo. Allego il vecchio script (solo la parte della lettura del sensore):

TEMP=0.0;
for(count=0;count<letture;count++)
{
TEMP+=analogRead(PIN);
delay(250); // qui, se uso il delay, poi salta un secondo
}
TEMP/=letture;
TEMP = ( 5.0 * TEMP * 100.0) / 1024.0;

Questo codice non lo usavo dentro una funzione, ma stava nel void loop. Ho creato poi la funzione "temperatura" e al posto del delay ho usato delayMicroseconds, adesso che è dentro una funzione l'orologio sembra andare ma il relè scatta in continuazione quando mi avvicino a 37,5°.
Dove sto sbagliando?

Pelletta, dovresti eliminare il delay perchè a mio avviso piuttosto dannoso.
A breve dovrei scrivere un articolo sul mio blog ma il tempo è quello che è.
Con il delay tu FERMI il processore. Al contrario ti consiglio l'uso di millis() per avere una sorta di multitasking.

Al posto di

void setup(){}
void loop(){
//fai qualcosa
delay(3000);
// e se nel frattempo succede qualcosa di altro come fai ad accorgertene?
}

Puoi usare:

unsigned long time;
unsigned long now;
void setup(){
  time=millis(); //millis ti da il numero di millisecondi passati dall'accensione
  now=millis();
}

void loop(){
  now=millis();
  if(time+3000<=now){  // se sono passati 3 secondi...
      // fai quello che devi fare e
      time=millis(); //aggiorna la variabile time
  }

   //nel frattempo... aspettando che passino i 3 secondi... se 
   //succede qualcosa fai altro ;-)
}

Il codice si complica un pochino ma te lo assicuro: i vantaggi sono tantissimi.
Il tuo processore è un vero multitasking.
PS: Il codice è buttato di getto quindi potrebbe esserci qualche imprecisione.
Informati bene sul millis e a presto

Grazie per l'aiuto, ho provato ad usare millis() anzichè delay() come mi avevi consigliato e sembra funzionare. Non è stato facile visto che ci ho sbattuto la testa per un giorno, ma alla fine (forse) ho capito come funziona. Allego lo script che ho dolorosamente partorito, spero che se qualcuno dovesse trovare qualche errore o imprecisione me lo faccia notare, altrimenti se va bene spero che sia d'aiuto a qualcuno. Lo script ogni secondo rileva la temperatura trasmessa da un sensore per quattro volte, con un ritardo di 0.25 secondi a lettura; la temperatura rilevata va ogni volta ad incrementare la variabile a lei riservata, TEMP in questo caso. Successivamente viene fatta la media aritmetica sul valore della variabile per ottenere una misurazione più precisa. Ecco lo script:

var=0;
TEMP=0.0;
while(var < 4)
{
if (millis() - previousMillis > interval)
{
previousMillis = millis();
var++;
TEMP+=analogRead(PIN);
}
}
TEMP/=Letture;
TEMP = ( 4.95 * TEMP * 100.0) / 1024.0;
// Serial.print("TEMP ");
// Serial.println(TEMP);

A proposito, secondo voi va bene come ho fatto per calcolare la temperatura? Ho confrontato la temperatura ottenuta con due termometri digitali che ho a casa soltanto che ogni termometro indica una cifra diversa, quindi come faccio a sapere chi dei tre ha ragione?
Anche qui se avete consigli, non siate timidi!
Saluti

Salve a tutti, ho inserito il codice che ho postato sopra e ho riscontrato che mi da qualche problema con la rotazione di un servo. Prima di aggiungere lo script sopracitato il servo funzionava a dovere, quindi è lo script che no va. In pratica il servo si muove a singhiozzo, ovvero si muove a scatti però il senso di rotazione e il tempo che deve rimanere acceso lo rispetta. Sicuramente è qualcosa nella funzione che provoca l'intermittenza del servo, adesso ci lavoro un pò sopra e come sempre se avete consigli, fatevi avanti.

Saluti

Ciao Pelletta, si è ovvio che il servo si muova a scatti.
Purtroppo ci si mette un po' a comprendere il funzionamento del millis perchè l'ottica è davvero differente (io ci ho messo almeno due settimane a comprendere le implicazioni) ma alla fine paga.
Quello che sbagli secondo me è l'entrata nel while.
Prova a eliminare il while e a sostituirlo con un if che ti azzeri le variabili e dovrebbe funzionare.

Per il capire quale dei due termometri abbia ragione... mi sa che non puoi far altro se non interrogare un terzo termometro di cui ti fidi :frowning:

Ah, un altro consiglio (perchè anche io ho fatto uno sketch che misura la temperatura: occhio che così facendo non hai giovamento nel prendere 4 misure e calcolare la temperatura perchè la ricalcoli ad ogni ciclo.
Secondo me sarebbe meglio fare una cosa simile:

var=0;    // NOTA BENE: FUORI DAL LOOP
TEMP=0.0;

void loop(){
  if (millis() - previousMillis > interval){ 
          previousMillis = millis();
          var++;
          TEMP+=analogRead(PIN);
  }

  if(var == 4){
    var = 0;
    TEMP/=Letture;
    TEMP = ( 4.95 * TEMP * 100.0) / 1024.0;  
  }
}

Confermaci che il codice funzioni e sicuramente il servo sarà fluido :wink:

Ho provato con lo script che mi hai postato, sostanzialmente funziona, però ho notato che la temperatura risulta più elevata del normale di alcuni gradi. Ho poi deciso di sistemare lo script facendogli fare soltanto una rilevazione di temperatura ogni 2 secondi, in modo che il relè non si ecciti continuamente. Grazie comunque dell'aiuto e perdonami se ho potuto rispondere soltanto oggi ma ultimamente la mia linea adsl fa i capricci.

Potrebbe essere il sensore di temperatura che usi.. Ultimamente mi sono trovato bene con il ds1624 della maxim.
Fede