DS18B20 Sensor Negative Temperature

Hi there,

I'm using the following code and it works fine until it hits 0*C, then it gives out weird stuff. Is there an easy way to change it so that it works with negative numbers? I'm assuming the issue is with shifting bits around but I honestly don't know exactly where's at fault. I also don't see "unsigned" used for any variables. I know one example online used that and got weird values for under 0, but the code below looks so nice I hate to toss it lol. If I can change it easily, then great! Thanks for looking.

// This function returns the temperature of the DS18S20 in Deg C
float getTemp(){
  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
  
  delay(1000); // Wait for temperature conversion to complete

  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;
}

Read the data sheet. The top 5 bits of the MSB are sign bits. 0 if positive and 1 if negative.

Sorry for my ignorance, but how would I check for that in the code?

it gives out weird stuff

How about you give examples of the weird stuff?

Pete

Like really large numbers (up in the thousands) that are obviously not right.

Your problem is integer division. Use this:

float TemperatureSum = tempRead / 16.0;

Pete

Why not test it yourself by feeding your own values for MSB and LSB and printing the result? The datasheet even gives you known values to test. For example , MSB=0xFE and LSB=0x6F should give -25.0625.

Pete

Thanks, I will try that! Great idea.

The problem isn't integer division. The code I used for testing was slightly different than yours which made it look like it was integer division.

However, this does duplicate your code but it produces the expected result (on a NANO).

  byte MSB = 0xfe;
  byte LSB = 0x6f;

  float ft = ((MSB << 8) | LSB);
  float ts = ft/16;
  Serial.println(ts,4);

Pete

Oh boy. I'm using an ESP8266 module and the following code outputs "4070.9375" but it works on my Arduino Uno. I also tried changing it to "16.0" and it still didn't work. What could cause the difference?

void setup() {
  Serial.begin(115200);
  delay(1000);
  
  byte MSB = 0xfe;
  byte LSB = 0x6f;

  float ft = ((MSB << 8) | LSB);
  float ts = ft/16;
  Serial.println("");
  Serial.println(ts,4);
}

void loop() {
  // Nothing here
}

OK, I can't test this but I think it is the problem - try this:

float ft = (int16_t) ((MSB << 8) | LSB);

Pete

Haha that sure did it, thanks! :slight_smile:

But could you please explain why that fixed it between ESP8266 and Arduino Uno?

'byte' is unsigned. When you put MSB and LSB together on UNO or NANO, it seems that (MSB << 8)|LSB results in the correct signed number. But on the ESP8266 it looks like it results in an unsigned number. This will turn all negative numbers into large positive ones. For example, 0xFFF8 is -0.5 degrees, but as a positive number it is 65528 and dividing by 16 gives you 4095.5.
Throwing (int16_t) on the front forces the compiler to treat the result as a signed 16-bit integer which then gives the correct result.

Pete

OK, thanks for the explanation!