DS Onewire program working about 90% of the time. Shooting for 100%

I am trying to roll my own DS Onewire program (not using the library) to read a DS18B20. I notice that most of the code online assumes a 4.7K pull-up resistor on the DQ line but I wanted to use the internal pull-ups on the arduino. It is actually working quite well except that every 10 readings or so my arduino just receives junk from the onewire device. This happens more frequently the cooler the temperature gets. At roughly freezing temperatures, it starts happening about every 5 readings or so. I couldn’t find anything in the data sheet that suggests that the communication timings should be different at different temperatures so I am a bit confused. I tried playing with different delays in my receive_bit() function but nothing seemed to help. Any insight that people can give me would be great. The whole sketch is below.

#define datapin 2


void setup()
{
  Serial.begin(9600);
  Serial.write(254);
  Serial.write(0x01);
  pinMode(datapin, INPUT);
  digitalWrite(datapin, HIGH);
}

void loop()
{
  
  Serial.write(254);
  Serial.write(128);
  
  long HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;
  
  reset_onewire();
  send_byte(0xCC);  //skip ROM
  //for(int i = 0; i<8; i++)
  {
    //Serial.print(receive_byte(), HEX);
    //Serial.print(" ");
  }
  
  //Serial.println();
  //Serial.println(reset_onewire());
  //send_byte(0xCC);
  send_byte(0x44); //take a temperature reading
  strong_pullup(); //active high for at least 750ms
  reset_onewire();
  send_byte(0xCC);
  send_byte(0xBE);
  
 
  
  LowByte = receive_byte();
  HighByte = receive_byte();
  //Serial.print(LowByte,BIN);
  //Serial.println(HighByte, BIN);
  TReading = (HighByte << 8) + LowByte;
  SignBit = TReading & 0x8000;  // test most sig bit
  if (SignBit) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
    TReading *= -1;
  }
  //Serial.println(TReading);
  Tc_100 = (((TReading * 625)*18)/10)+320000;    // multiply by (100 * 0.0625) or 6.25
  //Serial.println(Tc_100);
  //Tc_100 = ((Tc_100*900)/500) + 3200;
  
  Whole = Tc_100 / 10000;  // separate off the whole and fractional portions
  Fract = Tc_100 % 10000;
  if(SignBit) Tc_100 = 10000 - Tc_100;

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

  Serial.println("        ");
  delay(1000);
}

//drive the data pin active low
void pinLow()
{
  digitalWrite(datapin, LOW);
  pinMode(datapin, OUTPUT);
}

//let the pin float back up to VCC
//through the internal pullup resistor
void pinHigh()
{
  pinMode(datapin, INPUT);
  digitalWrite(datapin, HIGH);
}

void strong_pullup()
{
  pinMode(datapin, OUTPUT);
  digitalWrite(datapin, HIGH);
  delay(1000);
  pinMode(datapin, INPUT);
  digitalWrite(datapin, HIGH);
}

//send reset pulse and listen for presence pulse
boolean reset_onewire()
{
  boolean b;
  pinLow();
  delayMicroseconds(500);
  pinHigh();
  delayMicroseconds(90);
  b = digitalRead(datapin);
  delayMicroseconds(500);
  return !b;  //return true if we get a low pulse
}

//send a one bit
void writeOne()
{
  pinLow();
  delayMicroseconds(10);
  pinHigh();
  delayMicroseconds(50);
}

//send a zero bit
void writeZero()
{
  pinLow();
  delayMicroseconds(60);
  pinHigh();
  delayMicroseconds(3);
}

//shift out a single bit
void send_bit(boolean b)
{
  if(b)
  {
    writeOne();
  }
  else
  {
    writeZero();
  }
}

//receive a single bit
boolean receive_bit()
{
  boolean b;
  pinLow();
  delayMicroseconds(3);
  pinHigh();
  delayMicroseconds(7);
  b = digitalRead(datapin);
  delayMicroseconds(45);
  //Serial.print(b);
  return b; 
}

//shift out a full byte
void send_byte(byte v)
{
  byte mask;
  for(mask = 1; mask; mask<<=1)
  {
    send_bit(mask & v);

  }
  //Serial.println();
  
}

//receive a full byte
//00000000
//
byte receive_byte()
{
  byte data = 0;
  byte mask = 0;
  
  for(mask = 1; mask; mask <<= 1)
  {
    if(receive_bit()) data |= mask;
  }
  
  return data;
}

Four things: 1) how long is the cable?

2) IIRC the internal pull up is way bigger than the 4K7 - 50K ? - so the signal is pulled to 5V much slower possibly causing read errors as the signal isn't stable

3) furthermore in your communication you have function call overhead e.g. for writeOne() and writeZero(). you could make them inline (just adding inline for function definition) or adjust timing.

4) constructs like this

  pinLow();
  delayMicroseconds(3);
  pinHigh();

are quite critical as function calls can take a few miliseconds.

so advise: have a really good look at the timing!

robtillaart: function calls can take a few miliseconds.

Did you mean *micro*seconds?

robtillaart: Four things: 1) how long is the cable?

It's about 12 inches.

2) IIRC the internal pull up is way bigger than the 4K7 - 50K ? - so the signal is pulled to 5V much slower possibly causing read errors as the signal isn't stable

I was worried about this but then when I started to get valid data out after sending a read rom command, I figured it should work out.

3) furthermore in your communication you have function call overhead e.g. for writeOne() and writeZero(). you could make them inline (just adding inline for function definition) or adjust timing.

What do you mean by "inline"? Is that just putting the pin changes in the routine rather than calling another function to do it?

4) constructs like this

  pinLow();
  delayMicroseconds(3);
  pinHigh();

are quite critical as function calls can take a few miliseconds.

so advise: have a really good look at the timing!

I assume you mean microseconds for the function calls. It there a way to determine how many microseconds the function call is eating up?

Thanks for your help with this. I really appreciate it!

This is kind of an example of what I am getting out. It works great for a few times…then crap….then great……then more crap. Seems like if it works once it should work each time.

?65.4125        
þ?526.2125        
þ?65.4125        
þ?65.4125        
þ?65.4125        
þ?65.4125        
þ?65.4125        
þ?526.2125        
þ?65.4125        
þ?65.4125        
þ?65.5250        
þ?65.5250        
þ?65.4125        
þ?526.2125        
þ?65.4125        
þ?65.4125        
þ?65.5250        
þ?65.5250        
þ?65.4125        
þ?526.2125        
þ?65.4125        
þ?65.4125        
þ?65.5250        
þ?65.5250        
þ?65.4125        
þ?65.4125        
þ?65.4125        
þ?65.5250        
þ?65.5250        
þ?65.5250        
þ?65.5250        
þ?65.5250        
þ?65.5250        
þ?65.4125        
þ?65.5250        
þ?67.3250        
þ?65.5250        
þ?987.1250        
þ?65.4125        
þ?65.5250        
þ?65.5250        
þ?65.5250        
þ?65.5250        
þ?65.5250        
þ?67.3250        
þ?65.5250        
þ?987.1250        
þ?65.5250        
þ?65.5250        
þ?65.5250        
þ?65.4125        
þ?65.5250        
þ?65.4125        
þ?67.2125        
þ?65.4125        
þ?987.0125        
þ?67.2125        
þ?65.4125        
þ?65.4125        
þ?65.4125        
þ?65.4125        
þ?987.0125        
þ?67.2125        
þ?65.4125        
þ?987.0125        
þ?67.2125        
þ?65.4125        
þ?987.0

Seems like if it works once it should work each time.

Nope. Wishing and hoping aren't going to make it work when you've ignored the specification.

Use a 4.7k pullup resistor as is required.

Pete

el_supremo:

Seems like if it works once it should work each time.

Nope. Wishing and hoping aren't going to make it work when you've ignored the specification.

Use a 4.7k pullup resistor as is required.

Pete

I'm sure you are right :-(

I will say that I have a new piece of information though. I unplugged the Serial LCD display that I had hooked to the MCU and all of a sudden the thing is running like a top. Still the occasional error but it is more like 1 in 100 readings now. Not sure what the LCD display has to do with the missed readings. I guess I could just grab all of the bytes from the scratchpad and throw out any readings that don't match up with the CRC. Certainly the correct course of action would be to use the 4.7k resistor but then I would need a 5V spot on the pin header to plug it in to and I am already using the only 5V spot for the LCD. Bummer.

You simply can't expect these things to work if you violate the specifications, which you are doing by using the internal pullup.

Unless you are using the DS18B20 in parasitic power mode (2 wire cable) then just put the 4.7K resistor between Vcc and DQ at the device itself. Another option is to power the device using another port pin and connect the 4.7K resistor between it and DQ.

jremington: Another option is to power the device using another port pin and connect the 4.7K resistor between it and DQ.

That's a pretty good idea! I have lots of extra port pins.