Dallas Temperature sensor - how to make it faster?

Hello!
I'm building a preamp for my amplifier and now I want to monitor the temperature of the radiators.
I would like to have it on main screen and also do something when temperature is too high.
So I found easy to implement library here:
http://milesburton.com/wiki/index.php?title=Dallas_Temperature_Control_Library
I have two sensors, so what do I do:

#include <DallasTemperature.h>

static const int ONE_WIRE_PIN = 2;

NewOneWire oneWire(ONE_WIRE_PIN);
DallasTemperature tempSensor0(oneWire, 0);
DallasTemperature tempSensor1(oneWire, 1);

void setup(void) {
    Serial.begin(9600);

    tempSensor0.begin();
    tempSensor1.begin();
}

void display(DallasTemperature &sensor) {
    float temperature = sensor.getTemperature();     
    Serial.print(temperature);
    Serial.print("C\t");
}

void loop(void) {
    display(tempSensor0);
    display(tempSensor1);
    Serial.println();
}

This is from example of the library.
My problem is that it takes too long for sensor.getTemperature() function to execute, which slows down the whole loop. Althought I can display temperature on my LCD I can't do nothing else.

I don't do any temperature conversions (I heard it take 750ms).
I use 3 wirer to connect to the sensors, so it's not in parasitic mode.

How do I deal with it?

Leonti

Using the plain oneWire library (long hand programming, you set your own delays) and reducing the resolution of the Dallas chip you can get much faster responses. 750 ms is for 1/16th degree resolution, it reduces if you reduce the resolution. You might need to study some examples using said library and the codes you need to set from a spec sheet. It seems to have been removed from the playground, hmmm.........

Heres a page with a sample sketch using the onewire library :

http://www.skpang.co.uk/content/view/31/42/

I agree, use the oneWire library.

I too am not using parasite mode, but the DallasTemperature library still does take the 0.75sec to take a reading. Still working on finding out what I'm doing wrong there.

The OneWire library is much faster - i can take many readings per second. Frustratingly though, it doesn't work properly when running at 8Mhz. 16Mhz though no problem.

Even if it does take 0.75 seconds, using the one wire library and coding a loop that fires every 0.75 seconds, the arduino can be doing a lot of other things in the meantime. Not just sitting there waiting for the time to elapse. My monitoring system only checks the temperatures once every couple of seconds, but its doing an awful lot of other time sensitive stuff besides. It just plain wouldn't work using the new library.

pluggy,
where would one look to learn how to run tasks in parallel (like you suggest)?.

Heres how I do it :

time = millis();
 if (time >= secs){  // 1 second loop
     secs = time + 1000;
// blah blah
// stuff you want doing every second;
}
// stuff thats going all the time in main loop

time and secs are type long, alter the 1000 value for a different time.

Heres my monitoring system :

http://pluggy.is-a-geek.com/

Have a look at the 'How this is done link' for all the gory details including the sketch.

Another example is 'Blink without delay'

I wouldn't advise wiring an LED like that though..............

Thank you all for responses!
I'm now trying to work with onewire library.
I get readings from both sensors in hex values as described in Playground:
http://www.arduino.cc/playground/Learning/OneWire

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");

I guess this code is for DS18B20 and I have DS18S20.
Right now the output is 3.25 and I now that my room is not that cold :slight_smile:
How do I modify this code for DS18S20?
Here is sample output from my sensors:

R=10 80 BC 35 1 8 0 6B P=1 34 0 4B 46 FF FF 10 10 59 3.25
   CRC=59

R=10 D8 A8 35 1 8 0 87 P=1 34 0 4B 46 FF FF 10 10 59 3.25
   CRC=59

Leonti

Was it so that DS18S20 has 9 bit resolution? Then the multiplier is not 0.0625 but 0.5 (if I recall it correctly). 0.0625 is for 12 bit resolution.

You could try instead:

Tc_100 = 50 * TReading; // multiply by 100 * 0.5
Tc_100 = 50 * TReading;    //

Change the 1 line as shown. (Untested but should work) :wink:

edit - Great minds think alike.

Thank you!
Now it works great!

Leonti

Hi all, I'm glad I found this thread as I am having issues with 3 DS18B20s using the DallasTemperature library from MilesBurton.com, it uses the onewire library to communicate with the sensors. It seems slow and doesn't always see all of my sensors, then to cap it all when one sensor is ditched sometimes one of the other 2 is picked up as a ghost.

So looking through the library and checking on the forums it would appear that there is an issue somewhere. The on-topic issue is that there are direct delay() calls in the code, with the delay being according to the resolution, this of course blocks the whole system for that amount of time :confused:
Is there a way around this through the library, I was thinking of splitting the get temperature function in to 2 functions, request temp and retrieve temp?

It also seems like it is set to autorequest the temperature, which is fine for one slave sensor but can cause issues if they are all set to autorequest as they all try and respond at the same time causing collisions.

I'd already used the millis()-lastmillis statement to reduce delays in my code, which doesn't actually stop them :frowning: I need my code to be responsive for other things that it will be doing whilst its monitoring temperatures.

Here's the code from the library that I think needs splitting, remove the delay and split the 2 parts:

// returns a float with the temperature in degrees C.
float DallasTemperature::getTemperature()
{
    // If we're ready to rock, begin communication channel
    if (isValid() != SLAVEGOOD) return 0;  // return a value outside our range

   // you can send the convert request to all devices on the 
   // same bus so you can read them all after the conversion 
   // delay
   if (autoRequest)
   {
      pDataWire.reset();
      pDataWire.select(arSlaveAddr);
      pDataWire.write(STARTCONVO, parasite); // start conversion
     conversionDelay(); <<********** REMOVE THIS ************


***** SPLIT HERE *****
 // The temp is the first two bytes so just request those 2
[color=#00ff00]    readScratchPad(2);

    int16_t rawTemperature = (((int16_t) tempMSB) << 8) | tempLSB;

    switch (arSlaveAddr[0]) {
        case DS18B20MODEL:
        case DS1822MODEL:
           switch (resolution)
            {
               case TEMP_12_BIT:
                  return (float)rawTemperature * 0.0625;
                  break;
               case TEMP_11_BIT:
                  return (float)(rawTemperature >> 1) * 0.125;
                  break;
               case TEMP_10_BIT:
                  return (float)(rawTemperature >> 2) * 0.25;
                  break;
               case TEMP_9_BIT:
                  return (float)(rawTemperature >> 3) * 0.5;
                  break;
            }
            break;
        case DS18S20MODEL:
            return (float)rawTemperature * 0.5;
            break;
    }

I'm not sure I entirely understand how each of the dallas temperature functions work, I think that it should work where autorequest? is set for one sensor, do a globaltemprequest() then get temperature does a delay on the autorequest sensor, then on subsequent sensors you can just grab the data directly as the delay on the first one is long enough for the other 2 to perform conversion? If that's how its supposed to work that cuts my delay down for millis()-lastmillis code from 2.25s to 750ms, better but still not good.

I should mention in my current code I am using a 128x128 4096 color LCD sheild, it has a set of buttons that run off an interrupt, this becomes unresponsive whilst the heater code is running.

Hi,

As long as the sensor is not parasitically powered there is also the possibility to check if the conversion is completed. This is described on page 3 of the datasheet.

If the DS18S20
is powered by an external supply, the master can issue “read-time slots” (see the 1-Wire Bus System
section) after the Convert T command and the DS18S20 will respond by transmitting 0 while the
temperature conversion is in progress and 1 when the conversion is done. If the DS18S20 is powered with
parasite power, this notification technique cannot be used since the bus must be pulled high by a strong
pullup during the entire temperature conversion.

My "convert temperature' function looks like this.

void convertTempAll()  {       
  ds.reset();                  
  ds.skip();                       // tell all sensors on bus
  ds.write(0x44,0);            // to convert temperature
  while (ds.read() == 0) {  // reading timeslot (0 = not done with conversion)
    delay(100);                  // and wait until done (not parasitically powered)
  }
}

Not exactly an answer to remove the delay but at least you can check if its done or not. Average conversiondelay i've experienced is around 0.6 seconds with 8 sensors on the same bus. Also i'm only using the OneWire library.

Jeroen

Thanks Yot, I'm developing a multiple DS18B20 sensor unit, I have them all wired so they they are getting their own power from the arduino, so they shouldn't be in parasite mode. I'm really considering ditching the dallas library as it seems to be a bit all over the place and the ghosting stuff is just plain odd.

I'm going to try the latest version to see if its better for the ghosting, as I really need this to get going a bit quicker :slight_smile:

Just curious, what do you exactly mean with 'ghosting'?

Jeroen

When I was coding for the Dallas the playgrounds example code was using Onewire and it was comparatively easy to split this up to do the conversion and reading in seperate parts of the code. (I do the initial conversion in setup then do the reading followed by the next conversion inside a loop as in one of my earlier posts). This dedicated library is only of any use if reading temperatures and displaying them is all the Arduino is doing and it makes no allowance if you bother to wire up the third pin. The onewire code is messy but at least its granular.

Maybe it will be useful for someone - reading temperatures from 2 sensors with no delay whatsoever :slight_smile:
Code is not perfect - first sketch, but works.

#include <OneWire.h>

int oneWirePin = 7;

OneWire ds(oneWirePin); 

int temp_0 [2]; //arrays to store temeratures
int temp_1 [2];

void read_temp(byte addr[8], int number); //reads temerature on given address and saves it to the array (temp_0 or temp_1)
void send_for_temp(byte addr[8]); //asks for temerature on given address

long previousMillis = 0;        // will store last time DS was updated
long interval = 1000;           // interval at which to read temp (milliseconds)

void setup() {
  Serial.begin (9600); //for debug

ds.search(ds_addr0); //find addresses of 2 devices we have
ds.search(ds_addr1);

send_for_temp(ds_addr0); //ask firs and second for temperature
send_for_temp(ds_addr1);
/* this is more elegant: have to try it
  ds.reset();                  
  ds.skip();                       // tell all sensors on bus
  ds.write(0x44,0);            // to convert temperature

*/
}

void loop() {
//do your cool stuff here


   if (millis() - previousMillis > interval) {
     previousMillis = millis();   
//reading data from old requests:
read_temp(ds_addr0, 0);
read_temp(ds_addr1, 1);

//sending new requests:
send_for_temp(ds_addr0);
send_for_temp(ds_addr1);
   }
//print data from arrays - if it's not updated - will print old   
Serial.print(temp_0[0]); 
Serial.print("."); 
Serial.print(temp_0[1]); 
Serial.print(" ");
Serial.print(temp_1[0]); 
Serial.print("."); 
Serial.print(temp_1[1]); 
}

void send_for_temp(byte addr[8]){
  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);         // start conversion, with parasite power on at the end
}

void read_temp(byte addr[8], int number){
  byte i;
  byte data[12];
  
  ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  for (i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
  }
  
int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;

  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 = 50 * TReading; // multiply by 100 * 0.5  
  Whole = Tc_100 / 100;  // separate off the whole and fractional portions
  Fract = Tc_100 % 100;

  //code we acrually need:
  
  switch (number){
  case 0: 
  temp_0[0] = Whole;
  temp_0[1] = Fract;
  break;
  case 1: 
  temp_1[0] = Whole;
  temp_1[1] = Fract;
  break;
  default: break;
  }
}

hi Yot, I have 3 sensors declared like this:

#define NUM_DS_SENSORS  3

static const byte ONE_WIRE_PIN = 26;
NewOneWire oneWire(ONE_WIRE_PIN);
DallasTemperature tempSensor0(oneWire, 0);
DallasTemperature tempSensor1(oneWire, 1);
DallasTemperature tempSensor2(oneWire, 2);
DallasTemperature* heater_sensor[NUM_DS_SENSORS] = {&tempSensor0,&tempSensor1,&tempSensor2};

in my init function I do a heater_sensor*->begin() for each device.*
*then I iterate through a loop to display the 64bit unique code, at this point I either have 3 unique 64bit numbers which means all sensors are working or I get the ghosting situation where I get 2 unique numbers returned and the 3rd number is one of the 2 previous numbers (the sensors that get the same number also display the same temperature). something has obviously gone wrong at this point. I've used the same code on just the tempSensor0,1,2 type declaration and the pointer array and get the same outcome. *
btw. can anyone tell me if I've declared my sensors correctly?
I wanted an array and it was the only way that I could declare one.

Hi Reggie.

Sorry but i cant give any direct tips with the ghosting problem or with the declaring of sensors. Partially because i do not use the DallasTemperature library. The thing i can do is give my sketch that does what i want (without ghosting) Maybe the "fillArrayWithAdresses();" function gives some help. but... i'm not pretending that i know anything about, eh, anything. Its the thinkering that i like. Please keep that in mind. :slight_smile:

I have 7 dallas DS18S20 sensors connected to pin 53 of an arduino mega with a resistor between 5V and the data pins of the sensors. So they are not parasitically powered.

The sketch does the following;

fills an array with the unique serial codes,
spits them out through serial port,
tells all sensors to convert temperature and waits until finished,
fills another array with scratchpad data,
reproduces it through serial port,
fills yet another array with degrees celsius and
finally spits that through the serial port.

The processing from raw data to deg. C. comes partially from this forum and partially from the datasheet. Im not good at the different kinds of math (bit, int, float) on a microcontroller so there could (will) be some weard action going on.

#include <OneWire.h>

byte done = 0;                 // Do we have all Unique ID's
byte addr[8];                  // to store ID while searching
byte i;                        // for looping
byte addresses[7][8];          // to store 7 ID's of 8 wide
byte j;                        // for looping
byte rawdata[7][9];            // to store 7 sets of raw data
float temperaturesC[7];        // to store 7 temperatures

int HighByte, LowByte, CountPerC, CountRemain;
float ResolutionFactor;
int rawtemp = 0;
float tempc = 0;

OneWire ds(53);                // DS18S20 Temperature chip i/o on pin 53

//------------------- PROGRAM --------------------------
//------------------------------------------------------
void setup(void) {
  Serial.begin(9600);
}

void loop(void) {  
  fillArrayWithAdresses();
  
  reproduceAdressesFromArray();

  delay(1000);

  convertTempAll();

  fillArrayWithRawData();

  reproduceRawDataFromArray();

  delay(1000);

  fillArrayWithProcessedData();

  reproduceProcessedDataFromArray();

  delay(1000);
     
  //------------------ END OF PROGRAM --------------------
  //------------------------------------------------------
}

//------------------ FUNCTIONS -------------------------
//------------------------------------------------------

void reproduceProcessedDataFromArray()  {
  for( j = 0; j < 7; j++) {
    Serial.print("Sensor ");
    Serial.print(j, DEC);
    Serial.print("= ");
    Serial.print(temperaturesC[j]);
    Serial.print(" deg. C ");
    Serial.println();
  }
  Serial.println();
}

void fillArrayWithProcessedData() {
  for( j = 0; j < 7; j++) {
    LowByte = rawdata[j][0];
    HighByte = rawdata[j][1];
    CountRemain = rawdata[j][6];
    CountPerC = rawdata[j][7];    
    rawtemp = (HighByte << 8) + LowByte;
    ResolutionFactor = CountPerC - CountRemain;
    ResolutionFactor = ResolutionFactor / CountPerC;
    tempc = (float(rawtemp) / 2.0);
    tempc = tempc - 0.25;
    tempc = tempc + ResolutionFactor;
    temperaturesC[j] = tempc;
  }
}

void convertTempAll()  {       // tell all sensors on bus to convert temperature
  ds.reset();                  // and wait until done (NOT PARASITIC POWERED)
  ds.skip();
  ds.write(0x44,0);
  while (ds.read() == 0) {
    delay(100);
  }
}

void fillArrayWithRawData()  {
  for( j = 0; j < 7; j++) {
    for( i = 0; i < 8; i++)  {
      addr[i] = addresses[j][i];
    }
    ds.reset();
    ds.select(addr);
    ds.write(0xBE);
    for ( i = 0; i < 9; i++)  {
      rawdata[j][i]=ds.read();
    }
  }
}

void reproduceRawDataFromArray()  {
  for( j = 0; j < 7; j++) {
    Serial.print("Raw data=");
    for( i = 0; i < 8; i++) {
      Serial.print(rawdata[j][i], HEX);
      Serial.print(":");
    }
    Serial.println();
  }
}

void reproduceAdressesFromArray()  {
  for( j = 0; j < 7; j++) {
    Serial.print("Unique ID=");
    for( i = 0; i < 9; i++) {
      Serial.print(addresses[j][i], HEX);
      Serial.print(":");
    }
    Serial.println();
  }
}

void fillArrayWithAdresses()  {
  j = 0;
  done = 0;
  while (done==0) {                 // while searching is not done
    if ( ds.search(addr) != 1) {    // if there are no more unique id's
      ds.reset_search();
      done = 1;                    // we have all unique id's
      return;
    }
    else {                          // there are more unique id's
      for( i = 0; i < 8; i++) {
        addresses[j][i] = addr[i];
      }
      j = j + 1;
    }
  }
}

my DS18B20s are all hooked to pin 26 and they also have a resistor between 5v and the data pin.

It looks like I may have over estimated the library, I want the amount of sensors to be dynamic (1 to n) which is easily set with an array declaration. I think I should be able to do the same to just the onewires and still work them just the same. the library looked pretty comprehensive, at the expense of performance for the rest of the system.

I've had to build my own functions for the Nokia_LCD library as it is lacking in quite a few things, I wanted to avoid having to do the same for the dallas sensors, oh well, at least my code will be as tight as it can be :slight_smile:

There is an interesting part to the Dallas library that I might keep NewOneWire.cpp and .h, it adds a destructor to mix, so if a sensor dies you can let go of the instance and try and rediscover it, I think :smiley:

my DS18B20s are all hooked to pin 26 and they also have a resistor between 5v and the data pin.

Leonti, your code looks interesting, I'll have a look at all of this in more detail a little later on.