Maxim DS18B20 Temperature Sensor Giving Bad Readings

I'm using the Dallas/Maxim DS18B20 to measure temperature. Every few minutes it gives a bad reading. I sample temperature several times a second. The device is powered on the Vdd pin rather than using parasitic power. After giving a start conversion command, I check for an end of conversion by issuing a read slot and waiting for a high. I have tried to implement the timing requirements specified in the data sheet (480us min write slots and 60us min read slots).
Has anyone else had a similar problem? The only thing I can think of are that there may be spikes on the 5v from the USB cable, so I'll add a capacitor for filtering. Possibly the timing is borderline and delays could be increased. Implementing CRC checking would probably sort out all these problems.

This is the code which is quite long. Maybe some experts would spot something!

boolean DS18B20Initialize(int pin)
{
	unsigned long start_time;
	boolean low_detected=false;
	int state;
	char string[17];
	
	digitalWrite(pin,HIGH); // make sure pin is high before enabling output
	pinMode(pin,OUTPUT);
	digitalWrite(pin,LOW);
	delayMicroseconds(480);
	pinMode(pin,INPUT);
	
	start_time = micros();
	
	while(!digitalRead); // wait for line to be pulled high
		
	while((micros()-start_time)<480L)
	{
	 if(!digitalRead(pin))
	  low_detected=true; // a device  responded
	}
	
	//delayMicroseconds(5); // delay before next command
	if (low_detected)
	 return(true);
	 else
	 return(false);
}

void DS18B20Write(int pin,byte data)
{
	  byte n;

	  for(n=0; n<8; n++)
	  {
		  if ((data & 0x01) == 1)  // test least significant bit
		  {
			  digitalWrite(pin, LOW);
			  pinMode(pin, OUTPUT);
			  delayMicroseconds(5); // > a low of >1us needed for a  "high" . delayMicroseconds accurate for delay > 3us so use 5us
			  pinMode(pin, INPUT);
			  delayMicroseconds(60); // sensor reads during a 60us max window 
		  }
		  else
		  {
			  digitalWrite(pin, LOW);
			  pinMode(pin, OUTPUT);
			  delayMicroseconds(60); // minimum of 60us required for a low
			  pinMode(pin, INPUT);   // release bus and let the resistor pull the input high
			  delayMicroseconds(5);  // 1us required between write slots
		  }

		  data=data>>1; // now the next bit is in the least significant bit position.
	  }
	
}

byte DS18B20Read(int pin)
{
 int n;
 int state;
 byte data=0;

	
 for (n=0; n<8; n++)
  {
   digitalWrite(pin,LOW);
   pinMode(pin,OUTPUT);
   delayMicroseconds(5); // a low of >1us needed at beginning of read slot delayMicroseconds accurate for delay > 3us so use 5us
   pinMode(pin,INPUT);   // release bus and let the resistor pull the input high
   delayMicroseconds(5); // wait another 5us and then read state of line within the 15us deadline required
   state=digitalRead(pin);
   delayMicroseconds(60); // complete the 60us read slot + add 5 us at the end
   data = (data >> 1) | (state<<7);
  }
   return(data);	
}

boolean DS18B20ReadROMCode(int pin, byte *array)
{
 int i;
 
if(!DS18B20Initialize(tempsensor_pin))
 return(false);
DS18B20Write(tempsensor_pin,0x33); // Read the sensor's ROM code
	
for(i=0;i<8;i++)
 {
  *array = DS18B20Read(tempsensor_pin);
   array++;
 }
}

void DS18B20WMatchROMCode(int pin, byte array[])
{
 int i;
	
 DS18B20Write(tempsensor_pin,0x55); // Match the sensor's ROM code
	
 for(i=0;i<8;i++)
 {
  DS18B20Write(tempsensor_pin,array[i]);
 }
 
}

float ReadTemperature(int pin)
{
 int LoByte,HiByte,TReading,SignBit, Tc_100, Whole, Fract;
 int state;
 float temperature;
 static int errors=0;


// Initialize temperature sensor

if(DS18B20Initialize(tempsensor_pin))
{
//DS18B20Write(tempsensor_pin,0xCC); // skip rom
DS18B20WMatchROMCode(tempsensor_pin,TempSensorROMCode);
DS18B20Write(tempsensor_pin,0x44); // start conversion	
}
else
{
 Message("Cant Init Sensor");
 return(1111);
}



// check to see if conversion is complete by issuing read slots and waiting for a "1"
do 
{
 digitalWrite(pin,LOW);
 pinMode(pin,OUTPUT);
 delayMicroseconds(5); // a low of >1us needed at beginning of read slot delay. delayMicroseconds accurate for delay > 3us so use 5us
 pinMode(pin,INPUT);   // release bus and let the resistor pull the input high
 delayMicroseconds(5); // wait another 5us and then read state of line within the 15us deadline required
 state=digitalRead(pin);
 delayMicroseconds(60); // complete the 60us read slot + add 5 us at the end
} while (state==LOW);



if(DS18B20Initialize(tempsensor_pin))
{
//DS18B20Write(tempsensor_pin,0xCC); // skip rom
DS18B20WMatchROMCode(tempsensor_pin,TempSensorROMCode);
DS18B20Write(tempsensor_pin,0xBE); // read scratchpad
LoByte=DS18B20Read(tempsensor_pin);
HiByte=DS18B20Read(tempsensor_pin);		
}
else
{
 Message("Cant Init Sensor");
 return(1111);
}

TReading = (HiByte << 8) + LoByte;

SignBit = TReading & 0x8000;  // test most significant bit

if (SignBit) // negative
{
 TReading = (TReading ^ 0xffff) + 1; // 2's comp
}


// Work out the whole and decimal parts
Whole = TReading >> 4;  
if(Whole > 30 || Whole < 0)
{
digitalWrite(onboard_led,HIGH);
errors++;
printNumber(errors);
}


//Fract = (TReading & 0x000F) * 6 + (TReading & 0x000F) / 4; // Convert two decimal places to an integer
 


temperature = TReading * 0.0625;  
if(SignBit)
 temperature = temperature * (-1); 

return(temperature);
}

if you use the - MilesBurton.com - ?

does it block then?

BTW there is an error in this line, can you spot it?

while(!digitalRead); // wait for line to be pulled high

robtillaart:

while(!digitalRead); // wait for line to be pulled high

digitalRead without specifying a pin.... Yes, very dumb!

Eugbug:
Every few minutes it gives a bad reading.
I sample temperature several times a second.

That (without bothering to look at any code) may be the problem. I understand the DS18B20 takes a while to do its stuff and once per second is about as fast as you should go.

Nick_Pyner:

Eugbug:
Every few minutes it gives a bad reading.
I sample temperature several times a second.

That (without bothering to look at any code) may be the problem. I understand the DS18B20 takes a while to do its stuff and once per second is about as fast as you should go.

Yes, I had considered that. The max conversion time at 12 bit resolution is quoted as 750 ms. I actually check in code for a "high" indicating an end of conversion. Values are still being output on the port several times per second. So either my code check for valid data isn't working or the sensor is converting in record time. I'll add a delay between conversions and eliminate the code check and see if it eliminates the problem.

Update --

I added a 2 second delay between issuing the start conversion command and reading the two lower bytes in the scratchpad. This didn't make any difference.

what is the length of the wires?
if it is long yo might use a smaller pull up resistor 2.2K iso 4.7K (eh, do you use a pull up resistor?)

robtillaart:
what is the length of the wires?
if it is long yo might use a smaller pull up resistor 2.2K iso 4.7K (eh, do you use a pull up resistor?)

The leads are only about 15 cm long and I'm using a 4k7 resistor. I was considering using a decoupling capacitor on 5v power line from the Arduino board to filter transients but this could possibly damage the circuitry when the USB cable is disconnected.

OK, I recognise that there can't be much wrong with the code but I have now had a look and it is incomprehensible. You might try alternative code from Hacktronics or, if you are just using one or two sensors, the Sheepdog guides.

While the 4k7 pullup is surely OK, the 15cm suggests cable you have cobbled yourself. If this is just wires, or cheapo cable, you might try using decent shielded cable. All my sensors come with 3m shielded cables. I had strife with just a short extension of cheap cable.

Hi Eugbug,
I used this sensor in a project a while back. In order to learn how to use it, I wrote a library that wrapped the one-wire library. I won't claim this to be the greatest code but it worked well for me in a Sous Vide cooker project. Maybe you can get some ideas from it. It even has documentation!

DS1820.zip (142 KB)