venerdì 26 giugno 2015

Arduino datalogger per temperature su SD Card + LCD

Ciao a tutti,
esco un pò dai soliti progetti di cnc o fresatura per dedicarmi ad un progettino che avevo in mente da un pò di tempo. Sto parlando di un datalogger con Arduino

Mi serviva un dispositivo per poter registrare a intervalli regolari di tempo 2 temperature, quella dell'acqua della piscina e quella esterna per monitorare quanto l'escursione notturna e l'evaporazione abbassa la temperatura della vasca.

Il tutto è nato dal fatto che quest'anno per contrastare i bagni freddi a temperature intorno i 26° ho comprato una copertura isotermica per trattenere i raggi UV e per evitare l'evaporazione notturna.

I risultati sono stati da subito eccezionali, l'acqua nei giorni scorsi ha toccato temperature intorno ai 33° ma ha anche fatto un bel caldo per cui il datalogger mi serve appunto per verificare con e senza copertura isotermica quanto cambiano le cose.

Questo è il mio primo progetto in assoluto dove ho scritto anche codice per Arduino per cui accetto volentieri commenti e critiche perchè possa migliorarmi. Alcune parti di codice sono ridondate, ho sprecato un pò di ram ma per il mio scopo va più che bene.

Ringrazio Mauro Alfierilogicaprogrammabile.it e maffucci.it per i loro esaustivi post che mi hanno illuminato e aiutato a completare questo progetto.

I componenti per il progetto sono:

  • Scheda Arduino Uno
  • Modulo Real Time Clock I2C DS1307
  • Lettore SD Card Reader/Writer PIC + SD Card
  • Sonda di Temperatura DS18B20 da 1m Waterproof
  • Sensore di temperatura LM35
  • Lcd 16x2 QC1602A
Purtroppo non sono riuscito a documentare tutti i passaggi, forse perchè ero troppo preso e concentrato ma posso dirvi dove ho recuperato le informazioni.

Risorse dalle quali mi sono ispirato per gli schemi dei collegamenti:
  • Sonda di temperatura DS18B20 - qui
  • Modulo RTC - qui
  • Modulo SD - qui
  • Lcd 16x2 QC1602A - qui
  • LM35 - qui

In prima battuta ho cablato tutti i moduli su una breadboard e scritto lo Sketch per intero





Questo è lo sketch:

//includo la libreria per la
//comunicazione su bus I2C
#include 
#include 
#include 
#include 
#include 


const int chipSelect = 4;

int counter = 0;
int minute_interval = 10; // in minuti
int log_interval = minute_interval*60; // ricavo i secondi
 
File dataFile;

int DS18B20_Pin = 9;
 
OneWire ds(DS18B20_Pin);

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 7, 5, 6, 3, 2);


//LM35
int pinoSensor = 0;
int valorLido = 0;
float temperaturaExt = 0;

 
void setup()
{
  Serial.begin(9600);
  
  pinMode(chipSelect, OUTPUT);
  
  lcd.begin(16, 2);
  
  
  Wire.begin();
 
  //inizia la comunicazione con il DS1307
  
  Wire.beginTransmission(0x68);
  Wire.write((byte)0x00);
  Wire.endTransmission();
  
  Wire.requestFrom(0x68, 7);
  // estrapolazione dei 7 byte dei registri

  byte secondi = Wire.read();
  byte minuti = Wire.read();
  byte ora = Wire.read();
  byte giorno_sett = Wire.read();
  byte giorno_mese = Wire.read();
  byte mese = Wire.read();
  byte anno = Wire.read();
  
  String o = String(ora, HEX);
  char ORA[10];
  sprintf(ORA,  "%02d", o.toInt());
  
  String m = String(minuti, HEX);
  char MINUTI[10];
  sprintf(MINUTI,  "%02d", m.toInt());
  
  String s = String(secondi, HEX);
  char SECONDI[10];
  sprintf(SECONDI,  "%02d", s.toInt());
  
  String g = String(giorno_mese, HEX);
  char GIORNO[10];
  sprintf(GIORNO,  "%02d", g.toInt());
  
  String me = String(mese, HEX);
  char MESE[10];
  sprintf(MESE,  "%02d", me.toInt());
  
  String a = String(anno, HEX);
  char ANNO[10];
  sprintf(ANNO,  "%02d", a.toInt());
  
  
  lcd.setCursor(0, 0);
  lcd.print("Initialize SD");
  
  Serial.print("Initializing SD card...");
  
  //Serial.println("CS pin "+chipSelect);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
  
  
  dataFile = SD.open("piscina.log", FILE_WRITE);
    if (dataFile) {
        dataFile.print(GIORNO);
        dataFile.print("/");
        dataFile.print(MESE);
        dataFile.print("/");
        dataFile.print(ANNO);
        dataFile.print(";");
        dataFile.print(ORA);
        dataFile.print(":");
        dataFile.print(MINUTI);
        dataFile.print(":");
        dataFile.print(SECONDI);
        dataFile.print(";");
        dataFile.print("accensione");
        dataFile.println("");
   
        dataFile.close();
    } else {
      Serial.println("error opening dati.log");
    }
  

  delay(1000);
  
}
 
void loop()
{
  
  counter++;
  
  // LM35 temperatura esterna
  valorLido = analogRead(pinoSensor);
  temperaturaExt = (valorLido * 0.00480);
  temperaturaExt = temperaturaExt * 100;
  
  
  //inizzializza la trasmissione partendo
  //dall'indirizzo 0x00
  Wire.beginTransmission(0x68);
  Wire.write((byte)0x00);
  Wire.endTransmission();
 

  Wire.requestFrom(0x68, 7);
  //regupero i 7 byte relativi ai
  //corrispondenti registri
  byte secondi = Wire.read();
  byte minuti = Wire.read();
  byte ora = Wire.read();
  byte giorno_sett = Wire.read();
  byte giorno_mese = Wire.read();
  byte mese = Wire.read();
  byte anno = Wire.read();
  
  String o = String(ora, HEX);
  char ORA[10];
  sprintf(ORA,  "%02d", o.toInt());
  
  String m = String(minuti, HEX);
  char MINUTI[10];
  sprintf(MINUTI,  "%02d", m.toInt());
  
  String s = String(secondi, HEX);
  char SECONDI[10];
  sprintf(SECONDI,  "%02d", s.toInt());
  
  String g = String(giorno_mese, HEX);
  char GIORNO[10];
  sprintf(GIORNO,  "%02d", g.toInt());
  
  String me = String(mese, HEX);
  char MESE[10];
  sprintf(MESE,  "%02d", me.toInt());
  
  String a = String(anno, HEX);
  char ANNO[10];
  sprintf(ANNO,  "%02d", a.toInt());
  
  // prima riga
  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 0);
  String orario;
  
  
  
  float temperature = getTemperature();
 
  int temperatura = int(temperature); 
  
  
  lcd.print(ORA);
      lcd.print(":");
      lcd.print(MINUTI);
      lcd.print(" ");
      lcd.print(temperature,1);
      lcd.print("  ");
      lcd.print(temperaturaExt,1);

  String frase = "...";
  
  if(temperatura <20 ){
      frase = "troppo fredda..";
  }
  
  if(temperatura >=20 && temperatura <27 ){
      frase = "da temerari..";
  }
  
  if(temperatura >=27 && temperatura <29 ){
      frase = "Da bagno...corto";
  }
  
  if(temperatura >=29 && temperatura <30 ){
      frase = "Da bagno!";
  }
  
  if(temperatura >=30 && temperatura <32 ){
      frase = "Da super bagno!";
  }
  
  if(temperatura >=32 && temperatura <33){
      frase = "Tuffati dai!";
  }
  
  if(temperatura >=33){
      frase = "Butta la pasta..";
  }
  
 
  // Seconda riga dalla posizione zero
  lcd.setCursor(0, 1);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print(frase);
  
  // log su SD
  if(counter==log_interval){
      counter = 0;  
  }
  
  if(counter==1){
    dataFile = SD.open("piscina.log", FILE_WRITE);
    if (dataFile) {
        
        dataFile.print(GIORNO);
        dataFile.print("/");
        dataFile.print(MESE);
        dataFile.print("/");
        dataFile.print(ANNO);
        dataFile.print(";");
        dataFile.print(ORA);
        dataFile.print(":");
        dataFile.print(MINUTI);
        dataFile.print(":");
        dataFile.print(SECONDI);
        dataFile.print(";");
        dataFile.print(temperature,1);
        dataFile.print(";");
        dataFile.print(temperaturaExt,1);
        dataFile.print(";");
        dataFile.print(frase);
        dataFile.println("");
        dataFile.close();
        
        Serial.println("log su SD..");
        lcd.setCursor(15, 0);
        lcd.print("*");
    }else{
      
      lcd.print("                ");
      lcd.setCursor(0, 1);
      lcd.print("errore SD file");
      
    }
    counter = 1;
  }
  
  
  Serial.println("");
  Serial.print("Temperatura esterna: ");
  Serial.println(temperaturaExt,1);
  
  Serial.print("Temperatura acqua: ");
  Serial.println(temperature,1);
 
  delay(1000);
}




float getTemperature(){
  //returns the temperature from one DS18B20 in DEG Celsius
 
  byte data[12];
  byte addr[8];
 
  if ( !ds.search(addr)) {
      //no more sensors on chain, reset search
      ds.reset_search();
      return -1000;
  }
 
  if ( OneWire::crc8( addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return -1000;
  }
 
  if ( addr[0] != 0x10 && addr[0] != 0x28) {
      Serial.print("Device is not recognized");
      return -1000;
  }
 
  ds.reset();
  ds.select(addr);
  ds.write(0x44,1); // start conversion, with parasite power on at the end
 
  byte present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE); // Read Scratchpad
 
  for (int i = 0; i < 9; i++) { // we need 9 bytes
    data[i] = ds.read();
  }
 
  ds.reset_search();
 
  byte MSB = data[1];
  byte LSB = data[0];
 
  float tempRead = ((MSB << 8) | LSB); //using two's compliment
  float TemperatureSum = tempRead / 16;
 
  return TemperatureSum;
 
}





Poi ho comprato una scatolina che potesse contenere i vari componenti e l'ho forata con la mia cnc laser autocostruita








Dalla scatola ho fatto uscire la sonda LM35 per misurare la temperatura esterna






Infine ho fatto una prolunga alla sonda che poi ho immerso in acqua



I datalogger scrive su un file ogni 10 minuti i valori della temperatura esterna e dell'acqua della piscina. Tali dati li ho poi messi su un foglio di calcolo di google per disegnarne un grafico.



Infine ho sviluppato in php e javascript una semplice interfaccia per consultare i dati raccolti.

La linea azzurra indica l'andamento della temperatura dell'acqua mentre quella nera indica la temperatura esterna.