3

Peter Scargill found a way to use DS18B20 temperature sensor without any library and just using OneWire library, and it's amazing how blazing fast it is! (it's an old article from 2013)

#include <OneWire.h>
int16_t dallas(int x,byte start){
    OneWire ds(x);
    byte i;
    byte data[2];
    int16_t result;
    do{
        ds.reset();
        ds.write(0xCC);
        ds.write(0xBE);
        for (int i = 0; i < 2; i++) data[i] = ds.read();
        result=(data[1]<<8) |data[0];
        result>>=4; if (data[1]&128) result |=61440;
        if (data[0]&8) ++result;
        ds.reset();
        ds.write(0xCC);
        ds.write(0x44, 1);
        if (start) delay(1000);
    } while (start--);
    return result;
}
void setup (){
  dallas(A0,1);
}
void loop (){
  float currentTemp = dallas(A0,0);
}

Problem is The accuracy, he converts float to int to get accuracy of 1 degree and it's working just fine.

How can i get accuracy of .5 (or .2?) degree using this semi library?

  • Which is the pin of DS18B20? i dont understand – Uriel Aug 14 at 14:04
5

Actually he isn't really converting float to int. He never uses float since he don't need it. The datasheet of the DS18B20 shows at page 6, how the data is formatted:

data structure

  1. The 4 highest bits just resemble the sign bit 11.
  2. The first digit with full degrees is bit 4. Every bit below that has values below 1°C

So the data bytes 0b1111100001010000 will represent a temperature of -123°C and 0b1111100001011000 -122.5°C.

The author from the link then shifts this data 4 bits to the right, so that all bits representing values under 1 vanish. You can convert the received value per hand to float by also using this.

int16_t whole_degree = (result & 0x07FF) >> 4;
float temperature = whole_degree + 0.5*((data[0]&0x8)>>3) + 0.25*((data[0]&0x4)>>2) + 0.125*((data[0]&0x2)>>1) + 0.625*(data[0]&0x1);
if (data[1]&128) temperature*=-1;

As float does not have the same structure, as the data, we need to convert them. So I calculate first the number of whole degrees analog the your code. Then I add this up with the values of the lowest 4 bits. ((data[0]&0x8)>>3) and it's siblings will be 1, when the corresponding bit is set, and 0, if not.

So you get the full precision of the sensor (12 bits). Please note, that for this to work, you should not configure the sensor to less resolution. In that case you would have to ignore the corresponding lowest bits. But the sensor is in 12 bit mode at power up anyway and your code does not configure it otherwise.

Note: I'm unsure about the line result |=61440; in the original code. This sets the 4 highest bits of a 16 bit value to 1. I don't see, why this should be correct here, since a normal 16bit integer does only have 1 sign bit. In my understanding the original code would lead to false readings (way off), when reading temperatures below zero. I instead simply multiplied the resulting temperature with -1.

The whole code would look like this (I also added the statement for printing out the pure received data as binary, as I requested it in my comment. These statements are currently commented out):

#include <OneWire.h>
float dallas(int x,byte start){
    OneWire ds(x);
    byte i;
    byte data[2];
    int16_t result;
    float temperature;
    do{
        ds.reset();
        ds.write(0xCC);
        ds.write(0xBE);
        for (int i = 0; i < 2; i++) data[i] = ds.read();
        result=(data[1]<<8) |data[0];
        // Here you could print out the received bytes as binary, as requested in my comment:
        // Serial.println(result, BIN);
        int16_t whole_degree = (result & 0x07FF) >> 4; // cut out sign bits and shift
        temperature = whole_degree + 0.5*((data[0]&0x8)>>3) + 0.25*((data[0]&0x4)>>2) + 0.125*((data[0]&0x2)>>1) + 0.625*(data[0]&0x1);
        if (data[1]&128) temperature*=-1;
        ds.reset();
        ds.write(0xCC);
        ds.write(0x44, 1);
        if (start) delay(1000);
    } while (start--);
    return temperature;
}
void setup{
  //Adding Serial.begin(9600); for printing the received bytes here
  //Serial.begin(9600);
  dallas(A0,1);
}
void loop{
  float currentTemp = dallas(A0,0);
}
| improve this answer | |
  • 1
    Mhh, can you output the pure reading in binary and write this into a comment. You can do this via serial by Serial.println(result, BIN); – chrisl Jul 16 '19 at 16:52
  • 1
    @newbie I added a not-tested implementation to my answer. You can try it. There are also the statements for printing the pure reading in binary. These statements just have to be uncommented in the case, that this code is not working correctly – chrisl Jul 16 '19 at 17:47
  • 1
    Yes, surely it says that in loop, since you should not put it there, but into the function dallas(). But you can just use my provided code an uncomment the lines with Serial in dallas() and setup() – chrisl Jul 16 '19 at 17:54
  • 1
    @newbie you asked me to look at this question, but I think this answer explained it well (and upvoted it)... or do you have something specific for me to look at? – Michel Keijzers Jul 16 '19 at 19:12
  • 2
    @newbie I have corrected my code. The sign thing didn't work as I thought it would, so I now did it simply by multiplying with -1. Also I included a bit to to cut out the sign bits from whole_degree before shifting, so that the 1's there cannot make garbage values. Please again check, if this works for you. I hope, that it is correct now. I tested the calculations with a spare Arduino Nano and a mockup value (The one you posted as comment should result in 28,625°C) – chrisl Jul 16 '19 at 19:18
1

Why not rewrite the dallas() function so that it returns a float?

#include <OneWire.h>

OneWire ds(A0);

float dallas(OneWire& ds, byte start = false) {
    int16_t temp;
    do {
        ds.reset();
        ds.write(0xCC);
        ds.write(0xBE);
        ds.read_bytes((uint8_t*) &temp, sizeof(temp));
        ds.reset();
        ds.write(0xCC);
        ds.write(0x44, 1);
        if (start) delay(1000);
    } while (start--);
    return (temp * 0.0625);
}

void setup {
  dallas(ds, true);
}

void loop {
  float currentTemp = dallas(ds);
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.