Sensore Temperatura (DS18B20)

Andiamo a vedere come collegare ed utilizzare con Arduino il sensore di temperatura DS18B20 del kit Elegoo (il produttore di questi sensori è unico, così come le librerie di comunicazione).

Il sensore digitale DS18B20 è un sensore di temperatura molto più sensibile del modulo composito visto precedentemente qui. È infatti possibile raggiungere una risoluzione da 9 a 12 bit.

Ciò che però distingue davvero questo modulo dal precedente è la possibilità di aggiungere più sensori sullo stesso bus.

Ognuno di questi sensori ha infatti un numero seriale univoco, che ci permette di rivolgerci allo specifico sensore senza fare confusione tra quelli connessi sul bus.

Il protocollo utilizzato è standard, e lo troveremo in altri sensori: OneWire. Questo protocollo utilizza un pin digitale da cui inizia un bus, e permette di comunicare con uno specifico dispositivo sul bus sapendo il suo indirizzo univoco.

Questo sensore si trova in vari formati, tra cui in alcune configurazioni adatte all’immersione in acqua e all’utilizzo all’esterno, ad esempio il seguente:

Struttura Modulo

Specifiche Tecniche

Riportiamo qui di seguito le specifiche tecniche del sensore del kit Elegoo. Per altri sensori, è semplice procurarsi il file delle specifiche in rete.

Sensore Temperatura

Intervallo misurazione-55, +125 °C
Risoluzione9-12 Bit
Accuratezza± 0.5°C (da -10°C a +85°C)
Curva errore in funzione della temperatura

Caratteristiche Modulo

Tensione AlimentazioneDC 3.3V – 5.5V
Corrente AlimentazioneMisurazione: 1.5mA (max)
Standby: 3µA (max)
Tempo letturaRisoluzione
– 9bit: 93.75ms
– 10bit: 187.5ms
– 11bit: 375ms
– 12bit: 750ms

Link Documentazione

Schema Collegamento

Normalmente, per il sensore DS18B20 avremmo bisogno di una resistenza di pullup. Inoltre possiamo trovarlo in due configurazioni:

  • Modalità parassita: il sensore non è alimentato esternamente, ma si utilizza unicamente il filo del bus sia per l’alimentazione che per la comunicazione (da qui il nome del protocollo, OneWire)
  • Modalità normale: il sensore è alimentato da un’alimentazione esterna, e usa il bus solo per comunicare
Collegamento modalità parassita
Collegamento modalità normale

Nel nostro modulo, che opera in modalità normale, la resistenza di pullup è già integrata sulla scheda.

Ci limitiamo quindi a collegare l’alimentazione ai pin di alimentazione di Arduino, mentre il pin dei dati va collegato ad uno qualsiasi dei pin digitali di Arduino.

Schema collegamento

La lunghezza massima teorica per il bus di trasmissione dipende fortemente dalla modalità di connessione dei sensori e dal cavo utilizzato.

Mentre per distanze fino ad un metro, se il cavo del sensore non passa vicino a fonti di disturbo elettromagnetico (quali fili della rete elettrica), non ci sono particolari accorgimenti da avere, nel caso contrario è bene tenere a mente le seguenti indicazioni:

  • Usare un cavo schermato. È possibile acquistare un cavo schermato soltanto a 3 fili, oppure utilizzare un cavo Ethernet standard.
  • Tenere il cavo del sensore non schermato più corto possibile.
  • Collegare sempre lo schermo del cavo a terra (non a GND!), almeno da uno dei due lati.
  • Nel caso di cavo Ethernet, maggiore la certificazione del cavo (Cat5E, Cat6, …), maggiore sarà la distanza che sarà possibile raggiungere. Si consiglia di collegare i fili del cavo come segue, per minimizzare le interferenze:
Descrizione FiloColoreCollegato a
Transmit Data+Bianco con una striscia verde
GND
Transmit Data-Verde con una striscia bianca o completamente verde
GND
Receive Data+Bianco con una striscia arancione
GND
Bi-directional+Blu con una striscia bianca o completamente blu
DATA
Bi-directional-Bianco con una striscia blu
GND
Receive Data-Arancione con una striscia bianca o completamente arancione
GND
Bi-directional+Bianco con una striscia marrone
VCC
Bi-directional-Marrone con una striscia bianca o completamente marrone
GND

In questo modo è possibile connettere sullo stesso bus fino a 20 sensori di temperatura DS18B20, oppure dislocare un singolo sensore di temperatura molto lontano e poi comunicarci con Arduino.

Codice

Per il sensore di temperatura DS18B20 su Arduino esiste un set di librerie ufficiali molto semplici da usare.

Per prima cosa, andiamo ad installare le librerie “OneWire” e “DallasTemperature” per la comunicazione. In caso di dubbi, consultare questa guida.

Si tratta di librerie ufficiali presenti nella sezione di gestione librerie di Arduino IDE.

Con un solo sensore

#include <OneWire.h>
#include <DallasTemperature.h>

#define SENSOR_BUS_PIN 2

OneWire oneWire(SENSOR_BUS_PIN); //Creiamo istanza OneWire per utilizzare il canale di comunicazione
DallasTemperature sensors(&oneWire); //Passiamo il canale di comunicazione alla libreria della Dallas

void setup() {
  Serial.begin(9600); 
  Serial.println("DS18B20 test");
  sensors.begin(); //Inizializzo comunicazione
}

void loop() {
  Serial.print("Richiesta temperatura... "); 
  //L'esecuzione si blocca sul comando per il tempo richiesto (dipende dalla risoluzione impostata)
  sensors.requestTemperatures(); //Invio comando per leggere temperatura
  //Stampo temperatura del (primo) sensore del bus
  Serial.print(sensors.getTempCByIndex(0));
  Serial.println("°C");
}

Con più sensori

#include <OneWire.h>
#include <DallasTemperature.h>

#define SENSOR_BUS_PIN 2

OneWire oneWire(SENSOR_BUS_PIN); //Creiamo istanza OneWire per utilizzare il canale di comunicazione
DallasTemperature sensors(&oneWire); //Passiamo il canale di comunicazione alla libreria della Dallas

void setup() {
  Serial.begin(9600); 
  Serial.println("DS18B20 test");
  sensors.begin(); //Inizializzo comunicazione
  
  //Scansione sensori sul bus
  Serial.print("Scansione dispositivi... ");
  Serial.print("Trovati ");
  int numSensori = sensors.getDeviceCount();
  Serial.print(numSensori, DEC);
  Serial.println(" sensori.");
  Serial.println("");

  //Mi stampo i loro indirizzi univoci
  Serial.println("Stampo indirizzi...");
  DeviceAddress Termometro;
  for (int i = 0;  i < numSensori;  i++)
  {
    Serial.print("Sensore ");
    Serial.print(i+1);
    Serial.print(" : ");
    sensors.getAddress(Termometro, i);
    printAddress(Termometro);
  }
  Serial.println("");
}
void printAddress(DeviceAddress deviceAddress){ 
  for (unsigned int i = 0; i < 8; i++){
    Serial.print("0x");
    if (deviceAddress[i] < 0x10)
      Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
    if (i < 7)
      Serial.print(", ");
  }
  Serial.println("");
}
void loop(){
}
#include <OneWire.h>
#include <DallasTemperature.h>

#define SENSOR_BUS_PIN 2

OneWire oneWire(SENSOR_BUS_PIN); //Creiamo istanza OneWire per utilizzare il canale di comunicazione
DallasTemperature sensors(&oneWire); //Passiamo il canale di comunicazione alla libreria della Dallas

int numSensori = 0;
void setup() {
  Serial.begin(9600); 
  Serial.println("DS18B20 test");
  sensors.begin(); //Inizializzo comunicazione
  
  //Scansione sensori sul bus
  Serial.print("Scansione dispositivi... ");
  Serial.print("Trovati ");
  numSensori = sensors.getDeviceCount();
  Serial.print(numSensori, DEC);
  Serial.println(" sensori.");
  Serial.println("");
}

void loop() {
  Serial.print("Richiesta temperatura... "); 
  //L'esecuzione si blocca sul comando per il tempo richiesto (dipende dalla risoluzione impostata)
  sensors.requestTemperatures(); //Invio comando per leggere temperatura
  Serial.println("OK");
  //Stampo temperatura del sensore i-esimo
  for(int i=0;i<numSensori;i++){
    printSensor(i);
  }
}

void printSensor(int i){
  Serial.print("Sensore ");
  Serial.print(i+1);
  Serial.print(": ");
  float tempC = sensors.getTempCByIndex(i);
  Serial.print(tempC);
  Serial.print("°C | ");
  Serial.print(DallasTemperature::toFahrenheit(tempC));
  Serial.println("°F");
}
#include <OneWire.h>
#include <DallasTemperature.h>

#define SENSOR_BUS_PIN 2

OneWire oneWire(SENSOR_BUS_PIN); //Creiamo istanza OneWire per utilizzare il canale di comunicazione
DallasTemperature sensors(&oneWire); //Passiamo il canale di comunicazione alla libreria della Dallas

//Ottenuti con il codice mostrato prima
DeviceAddress sensore1 = { 0x28, 0xAA, 0x4F, 0x44, 0x50, 0x14, 0x01, 0x17 };
DeviceAddress sensore2 = { 0x28, 0x6F, 0x0F, 0x29, 0x00, 0x00, 0x80, 0xF9 };

void setup() {
  Serial.begin(9600); 
  Serial.println("DS18B20 test");
  sensors.begin(); //Inizializzo comunicazione
}

void loop() {
  Serial.print("Richiesta temperatura... "); 
  //L'esecuzione si blocca sul comando per il tempo richiesto (dipende dalla risoluzione impostata)
  sensors.requestTemperatures(); //Invio comando per leggere temperatura di tutti i sensori connessi
  /*
   * È anche possibile leggere la temperatura singolarmente
   * con il comando sensors.requestTemperaturesByAddress(sensore1).
   * Il comando è però più lento nel caso di letture da più sensori
   */
  Serial.println("OK");
  //Stampo temperatura del sensore 1
  Serial.print("Sensore 1: ");
  printSensor(sensore1);
  //Stampo temperatura del sensore 2
  Serial.print("Sensore 2: ");
  printSensor(sensore2);
}

void printSensor(DeviceAddress addr){
  float tempC = sensors.getTempC(addr);
  Serial.print(tempC);
  Serial.print("°C | ");
  Serial.print(DallasTemperature::toFahrenheit(tempC));
  Serial.print("°F (Risoluzione ");
  Serial.print(sensors.getResolution(addr), DEC);
  Serial.println("bit)");
}

Impostare la risoluzione dei sensori

La risoluzione del sensore è scritta in una sua memoria retentiva interna (EEPROM). Questo significa che non va persa quando togliamo l’alimentazione al sensore.

Tuttavia le memorie EEPROM possono essere scritte solo un numero finito di volte. Per il nostro sensore DS18B20 abbiamo 55000 scritture, ciascuna rimane inalterata per 10 anni.

È quindi buona cosa utilizzare uno script apposito per impostare la risoluzione, e non farlo nel codice che andremo ad utilizzare per la lettura. Così facendo evitiamo nel tempo di danneggiare la EEPROM e dover buttare il sensore.

Inoltre per impostare la risoluzione è necessario conoscere l’indirizzo univoco del sensore. Questo può essere fatto con il codice mostrato prima.

#include <OneWire.h>
#include <DallasTemperature.h>

#define SENSOR_BUS_PIN 2

OneWire oneWire(SENSOR_BUS_PIN); //Creiamo istanza OneWire per utilizzare il canale di comunicazione
DallasTemperature sensors(&oneWire); //Passiamo il canale di comunicazione alla libreria della Dallas

//Ottenuti con il codice mostrato prima
DeviceAddress sensore1 = { 0x28, 0xAA, 0x4F, 0x44, 0x50, 0x14, 0x01, 0x17 };

void setup() {
  Serial.begin(9600); 
  Serial.println("DS18B20 set risoluzione");
  sensors.begin(); //Inizializzo comunicazione
  //Come valori di risoluzione sono consentiti: 9, 10, 11, 12
  sensors.setResolution(sensore1, 9);
  
  Serial.print("Risoluzione Sensore: ");
  Serial.println(sensors.getResolution(sensore1), DEC); 
  Serial.println();
}

void loop() {
}

Cambiare la risoluzione influirà notevolmente sul tempo di lettura, e ci permetterà di evitare cicli lenti nel codice qualora non servisse una precisione elevata.

4 comments

  1. Vittorio

    Spiegazione Super Esauriente e fatta mooolto bene, complimenti.
    Ho una domanda però, dici di utilizzare un cavo con sezione 0,6 (equivalente a 19 AWG), ma cavi LAN twistati nemmeno quelli in categoria 8 arrivano a quelle sezioni.
    Per caso ti riferisci a due casi distinti? Sezione 0,6 o cavo LAN?
    Ovviamente mi riferisco al caso di avere diverse sonde…
    Inoltre ho notato che se l’alimentazione viene fornita a 3,3 V è opportuno abbassare la resistenza di pullup.
    Complimenti ancora…
    Vittorio

    1. Andrea Aspesi

      Grazie Vittorio. L’info sulla sezione era in realtà un refuso, ho provveduto a toglierla. Un normale cavo LAN twistato (ad esempio un CAT 5/5E) dovrebbe andare più che bene.
      Come giustamente osservi tu, minore la tensione, maggiore è in genere la sensibilità del circuito integrato alla caduta di tensione dovuta al cavo. Lo stesso vale anche per un cavo lungo: se non funziona con 4.7k, è possibile provare ad abbassare la resistenza di pull-up per “forzare” maggiormente verso Vdd la linea. Ovviamente la resistenza deve essere abbastanza alta da permettere al sensore di poter tirare verso GND la linea senza superare i suoi limiti interni, quindi per il valore minimo di resistenza è sempre opportuno leggere la documentazione dell’integrato del sensore.
      Se hai trovato un valore che funziona bene con 3.3V, ti invito a mettere la tua configurazione qui sotto per i prossimi lettori!

  2. Giulio

    Spiegazione super esauriente, con esempi molto chiari.
    Vorrei sapere, però, se esiste un wiki della libreria con la classica elencazione dei tipi, metodi e costruttori inclusi per poter apprezzare tutti i dettagli degli esempi proposti ed eventualmente modificarli secondo esigenza con cognizione di causa.
    Sono andato sul sito
    https://github.com/milesburton/Arduino-Temperature-Control-Library
    ma il link per ulteriore documentazione https://www.milesburton.com/Dallas_Temperature_Control_Library
    pare non funzionare.
    Puoi aiutarmi a trovare il documento ?
    Giulio

    1. Andrea Aspesi

      Ciao Giulio, spesso capita che in una libreria non sia inclusa una wiki con metodi e costruttori.
      Puoi però facilmente estrarla guardando il file .h della libreria, che spesso contiene i commenti (o almeno dovrebbe se è scritta bene 😉 )
      I metodi e i costruttori che puoi usare sono riportati nella classe sotto “public”. Te ne riporto alcuni qui per te, a titolo di esempio:

      
      DallasTemperature();
      DallasTemperature(OneWire*);
      DallasTemperature(OneWire*, uint8_t);
      
      // initialise bus
      void begin(void);
      
      // returns the number of devices found on the bus
      uint8_t getDeviceCount(void);
      
      // returns the number of DS18xxx Family devices on bus
      uint8_t getDS18Count(void);
      
      // returns true if address is valid
      bool validAddress(const uint8_t*);
      
      // returns true if address is of the family of sensors the lib supports.
      bool validFamily(const uint8_t* deviceAddress);
      
      // finds an address at a given index on the bus
      bool getAddress(uint8_t*, uint8_t);
      
      ...
      

      Quando sei in dubbio sul significato di un parametro, ti può aiutare andare a controllare anche il file .cpp
      Ad esempio, per il metodo getAddress, troverai:

      
      //...
      bool DallasTemperature::getAddress(uint8_t* deviceAddress, uint8_t index) {
         //...
      }
      //...
      

Comments are closed.