Errori lettura temperatura DS18B20.

Salve a tutti, probabilmente l' argomento è ciclico ma ho letto tutti (credo) i thread a riguardo qui sul forum - e non solo - senza riuscire a trovare una soluzione.

Il sensore è appunto il famoso Dallas DS18B20 prodotto da Maxim. Mi sono arrivati in mattinata, già testati dal venditore. ho provveduto quindi a rintracciarne l' indirizzo attraverso arduino stesso trovandone il valore dei byte che lo compongono, il tutto viene riconosciuto tranquillamente e apparentemente sembra lavorare bene.

Ho collegato la sonda in questo modo:

Dai 5V dell' arduino sono passato per una resistenza da 4.7K (misurati 4.61) per poi andare al Pin 2 del dallas.
Ho portato il Pin 3 a massa.
Al Pin 2 ho collegato il Digital 10 dell' arduino.

Il tutto funziona con questo codice:

#include <OneWire.h>
 
//init the one wire interface on pin 10
OneWire  ow(10);
 
//write here the address you receive from the other program
byte sensor[8] = {0x28,0xAA,0x33,0xF0,0x02,0x00,0x00,0x79}; //SONDA "b296" DS18B20
 
void setup(void) {
  Serial.begin(9600);
}
 
void writeTimeToScratchpad(byte* address){
  //reset the bus
  ow.reset();
  //select our sensor
  ow.select(address);
  //CONVERT T function call (44h) which puts the temperature into thescratchpad
  ow.write(0x44,1);
  //sleep a second for the write to take place
  delay(1000);
}
 
void readTimeFromScratchpad(byte* address, byte* data){
  //reset the bus
  ow.reset();
  //select our sensor
  ow.select(address);
  //read the scratchpad (BEh)
  ow.write(0xBE);
  for (byte i=0;i<9;i++){
    data[i] = ow.read();
  }
}
 
float getTemperature(byte* address){
  int tr;
  byte data[12];
 
  writeTimeToScratchpad(address);
 
  readTimeFromScratchpad(address,data);
 
  //put in temp all the 8 bits of LSB (least significant byte)
  tr = data[0];
 
  //check for negative temperature
  if (data[1] > 0x80){
    tr = !tr + 1; //two's complement adjustment
    tr = tr * -1; //flip value negative.
  }
 
  //COUNT PER Celsius degree (10h)
  int cpc = data[7];
  //COUNT REMAIN (0Ch)
  int cr = data[6];
 
  //drop bit 0
  tr = tr >> 1;
 
  //calculate the temperature based on this formula :
  //TEMPERATURE = TEMP READ - 0.25 + (COUNT PER Celsius Degree - COUNT REMAIN) / (COUNT PER Celsius Degree)
 
  return tr - (float)0.25 + (cpc - cr)/(float)cpc;
}
 
//fahrenheit to celsius conversion
float f2c(float val){
  float aux = val - 32;
  return (aux * 5 / 9);
}
 
//celsius to fahrenheit conversion
float c2f(float val){
  float aux = (val * 9 / 5);
  return (aux + 32);
}
 
void loop(void) {
  float temp;
  float tmp2;
  tmp2 = getTemperature(sensor);
  temp = c2f(tmp2);
 
  Serial.print("Temp = ");
  Serial.print(temp);
  Serial.print(" F or ");
  Serial.print(tmp2);
  Serial.println(" C");
 // Serial.flush();
  //wait 1 second/s
  delay(1000);
}

Nel serial monitor ho però questo tipo di risultati:

Temp = 66.43 F or 19.12 C
Temp = 66.43 F or 19.12 C
Temp = 66.43 F or 19.12 C
Temp = 66.43 F or 19.12 C
Temp = 66.43 F or 19.12 C
Temp = 66.43 F or 19.12 C
Temp = 66.54 F or 19.19 C
Temp = 66.54 F or 19.19 C
Temp = 66.43 F or 19.12 C
Temp = 66.54 F or 19.19 C
Temp = 66.43 F or 19.12 C
Temp = 66.54 F or 19.19 C
Temp = 91.18 F or 32.88 C
Temp = 117.95 F or 47.75 C
Temp = 126.16 F or 52.31 C
Temp = 132.46 F or 55.81 C
Temp = 126.16 F or 52.31 C
Temp = 113.79 F or 45.44 C
Temp = 103.55 F or 39.75 C
Temp = 97.36 F or 36.31 C
Temp = 104.00 F or 40.00 C
Temp = 89.04 F or 31.69 C
Temp = 104.00 F or 40.00 C
Temp = 104.00 F or 40.00 C
Temp = 89.04 F or 31.69 C
Temp = 89.04 F or 31.69 C
Temp = 104.00 F or 40.00 C
Temp = 104.00 F or 40.00 C
Temp = 88.93 F or 31.62 C
Temp = 104.00 F or 40.00 C
Temp = 104.00 F or 40.00 C
Temp = 84.99 F or 29.44 C
Temp = 82.96 F or 28.31 C
Temp = 80.94 F or 27.19 C
Temp = 78.91 F or 26.06 C
Temp = 78.80 F or 26.00 C
Temp = 76.89 F or 24.94 C
Temp = 76.77 F or 24.87 C
Temp = 74.75 F or 23.75 C
Temp = 74.64 F or 23.69 C
Temp = 74.64 F or 23.69 C
Temp = 74.64 F or 23.69 C
Temp = 74.52 F or 23.62 C
Temp = 74.52 F or 23.62 C
Temp = 74.52 F or 23.62 C
Temp = 74.52 F or 23.62 C
Temp = 72.61 F or 22.56 C
Temp = 74.52 F or 23.62 C
Temp = 74.52 F or 23.62 C

La temperatura parte giustamente da valori di 19°C circa (diciamo che è giusta tenendo conto che la comparo con un termometro da tavolo che oscilla tra i 19 e i 18°C (non segna le cifre decimali).
Poi, semplicemente ALITANDO sopra il sensore per diciamo un 3-4 secondi le temperature schizzano a 55° per poi rioscillare tra i 31 e i 40 (40° sembra essere una temperatura strana in quanto è precisa al decimo e me la ritrovo in parecchie prove, quando ad esempio tolgo e metto "a caldo" il cavetto al pin 10.)
Si stabilizza poi a 23° circa, ma la stanza è sempre a 19° come prima.

Il tutto nell' arco di una decina di secondi. (refresh del codice=1000ms).

Potreste aiutarmi ad ottimizzare il codice per cortesia?

Vi ringrazio.

Alessandro.

#include <OneWire.h>
 
// DS18S20 Temperature chip i/o
OneWire ds(10);  // on pin 10
 
void setup(void) {
  // initialize inputs/outputs
  // start serial port
  Serial.begin(9600);
}
 
int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;
 
void loop(void) {
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];
 
  if ( !ds.search(addr)) {
//      Serial.print("No more addresses.\n");
      ds.reset_search();
      return;
  }
 
//  Serial.print("R=");
//  for( i = 0; i < 8; i++) {
//    Serial.print(addr[i], HEX);
//    Serial.print(" ");
//  }
 
//  if ( OneWire::crc8( addr, 7) != addr[7]) {
//      Serial.print("CRC is not valid!\n");
//      return;
//  }
 
//  if ( addr[0] == 0x10) {
//      Serial.print("Device is a DS18S20 family device.\n");
//  }
//  else if ( addr[0] == 0x28) {
//      Serial.print("Device is a DS18B20 family device.\n");
//  }
//  else {
//      Serial.print("Device family is not recognized: 0x");
//      Serial.println(addr[0],HEX);
//      return;
//  }
 
  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end
 
  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
 
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad
 
//  Serial.print("P=");
//  Serial.print(present,HEX);
//  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
//    Serial.print(data[i], HEX);
//    Serial.print(" ");
  }
//  Serial.print(" CRC=");
//  Serial.print( OneWire::crc8( data, 8), HEX);
//  Serial.println();
  
  LowByte = data[0];
  HighByte = data[1];
  TReading = (HighByte << 8) + LowByte;
  SignBit = TReading & 0x8000;  // test most sig bit
  if (SignBit) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
  Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

  Whole = Tc_100 / 100;  // separate off the whole and fractional portions
  Fract = Tc_100 % 100;


  if (SignBit) // If its negative
  {
     Serial.print("-");
  }
  Serial.print(Whole);
  Serial.print(".");
  if (Fract < 10)
  {
     Serial.print("0");
  }
  Serial.print(Fract);

  Serial.print("\n");
}

Questo codice trovato nel playground riguardante il OneWire ed appunto il sensore DS18B20 (modello B precisiamo - con sensibilità di 0.0625 invece che 0.5, da quello che ho capito) sembra funzionare per il momento.
L' ho provato comparando i risultati con un termostato ambientale (regolatore termosifoni in parole povere :grin:) e differisce di 0.3-0.5 gradi quindi più che accettabile tenendo conto di un eventuale errore intrinseco nello strumento stesso di rilevazione.
Ho notato però che il sensore è "lento" a tornare alla temperatura ambiente. Facendo la SCIENTIFICA PROVA dell' alitarci sopra :smiley: ho visto che per tornare a temperatura ambiente (19°) dai 25 a cui era arrivato gli ci vogliono circa 110/120 cicli (1secondo a ciclo quindi circa 2 minuti). Credo la cosa sia normale e per le mie necessità non mi crea problemi quindi non mi fascio la testa.

Per ora ripeto, sembra funzionare, in caso mi farò vivo nuovamente :smiley:

PS: ogni tanto mi forniste un valore sballato (diciamo in maniera del tutto casuale a quanto sembra) di 85.00 gradi proverò lasciandogli maggior "respiro" con 15 secondi per ogni ciclo.

Uhm, a meno che non abbia mangiato pesante ( :smiley: ) mi pare assurdo che arrivi a 55°C!! Con lo stesso chip, facendo la solita prova, potevo superare i 30°C, qualcuno in piu' se insistevo, ma non oltre. Ovviamente mi pare corretto considerando che il nostro alito non possa essere superiore a quello della nostra temperatura corporea... Per la stessa ragione mi pare strano il tuo dato!!

Prova anche questo codice, che usa la libreria DallasTemperature, che se non ricordo male, dovrebbe funzionare meglio

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

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 10

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// arrays to hold device address
DeviceAddress insideThermometer;

void setup(void)
{
  Serial.begin(9600);
  sensors.begin();
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); 
  sensors.setResolution(insideThermometer, 12);
}


void printTemperature(DeviceAddress deviceAddress) // function to print the temperature for a device
{
  float tempC = sensors.getTempC(deviceAddress);
  Serial.println(tempC);
}

void loop(void)
{ 
  sensors.requestTemperatures(); // Send the command to get temperatures
  printTemperature(insideThermometer); // Use a simple function to print out the data
  
  delay(1000);
}