faster ds18b20 temp sensor readings

Hi, there
I have a project that uses 8 dallas ds18b20 sensors for PID controlling
I ran into trouble with the dallas temperature library as the value of 750ms is hardcoded into the library…
Calling it Async poses the question when the sensors are ready…
Reading the datasheet for the sensor reveals that the dataline is pulled low when concersion is being made and then high again
so the question is then: WHY THE H**L IS THIS NOT TAKEN ADVANTAGE OF, INSTEAD OF HARDCODING THE WORST CASES CENARIO???

so ditching the dallas library and going for the OneWire instead I wrote a test to read one sensor :

CODE SNIPPET:

float getTemperature(byte addr[8]){
 byte i, dat;
 byte present = 0;
 byte data[12];
// alle sjekk er slettet
 ds.reset();
 ds.select(addr);
 // Start conversion
 ds.write(0x44);
 // TEST TEST==========================================
 dat = ds.read();
 while (!dat){
  dat = ds.read(); 
 }
 // END TEST **********************************************'
 present = ds.reset();
 ds.select(addr);
 // Issue Read scratchpad command
 ds.write(0xBE);
 // Receive 9 bytes
 for ( i = 0; i < 9; i++) {
 data[i] = ds.read();
 }

so with 1 variable that is not needed really but for ease of reading the code and 1 small while loop, the program waits only as long as needed.

So how much time did I save then??
Using milis() variable before the call to the temperature readings and subtracting the variable from millis() after the reading…
(Output from my serial monitor; temp in deg C, followed by milliseconds taken to ask for a new temp reading + converting)

--Search started--
Device is a DS18B20 : 28-3E-81-4B-03-00-00-76
Device is a DS18B20 : 28-BE-BD-4B-03-00-00-39
--Search ended--

Temperaturen er: 23.3750
638

Temperaturen er: 23.4375
639

Temperaturen er: 23.5000
639

The serialmonitor is only sent a message if the reading is not the same as the previous, to make it easier for me to follow…

so my readings from that session took about 637-639 ms to complete (12bit mode test)
savings of over 110ms average pr. call !!

that is almost a full second less spent if I call my sensors seperatly and not ASYNC

@Author of Dallas Temp Library: could you skip the CASE test for the delay of the sensors and just test for low/high line
// TEST TEST ==========================================
dat = ds.read();
while (!dat){ // while the line is low, the conversion is not ready…
dat = ds.read();
}
// END TEST **********************************************’

Enjoy less time waiting for a new temp-reading

D.

The code at…

http://sheepdogguides.com/arduino/ar3ne1tt.htm

… has a few problems in it, but could be the basis of an answer. It doesn’t use the libraries… just watch out for, and re-program to overcome, the fact that it is really reading the previous conversion shortly after starting the NEXT conversion. Sorry. I should re-write it, but maybe you’ll get to it first!

You would need to program the DS18B20 to work in a lower resolution mode if you wanted to read more often. You’d do that in the setiup() subroutine.

The post is more of a solution than a question though, to share my findings to the community. I needed to know the actual time that goes into the conversion call because of it connects to PID regulation routine. I do not want to call PID regulating routine more often than I have a new real temperature reading to use, as it serves no purpose...

My project is a seperation of few liquids with similar boilingpoints so I need the extra decimals of 12bit resolution. I need one particular temp. reading to be fast, as the valve opening acts quite fast on the temperature (less than a second).

I will ofcourse test less resolution against quality, but 110 ms off the standard reading in 12bit mode is nearing my goal.

Why I use DS18b20 is my attempt to run my project on a single UNO ;)

If I can run the project of one of my newly bought NANODES, it will be great,as then I will have internet access to my d*****t c****n i.e. use my phone or tablet to watch and set parameters

currently I have hoocked up 4x DC SSR, 2x AC SSR ( 3 solenoid-valves, 2x pumps, 2x heaters ), LCD (running in 3-wire), 4 potentiometers, 6-leds, 1-button, 8x 1-wire-ds18b20.

So I do not have many pins left............ If all fails I revert to using my Mega1280,

WHY THE H**L IS THIS NOT TAKEN ADVANTAGE OF, INSTEAD OF HARDCODING THE WORST CASES CENARIO???

Because coding the worst case scenario is far more robust and easier to implement. Btw shouting doesn't help but I understand the "frustration" ;)

This topic is under attention of the "makers" of the library (outside of the forum) but afaik one needs time to implement and test these thoroughly before publish a new version to the public.

I wrote the part for the async operations - as I also had trouble with the hard coded delays. The test and review phase took 10-12 weeks before it was stable enough to publish. Part of the problem is keeping the library backwards compatible to prevent "killing" existing sketches. It took so many weeks because I prefered preventing a lot of "doesn't work as expected" mails/posts.

WRT the async mode, the idea is to start a new conversion directly after reading the sensor. Then while you process the new temperature the DS18B20's do a new measurement in parallel. As my sketch took about 600 millis to do the processing, I only had to wait 150ms before new reading was ready. Your proposed addition would make the Async mode complete

That said, feel free to post your updated version of the library to Miles Burton, who maintains the main source.

Rob

@robtillaart Thanks for the reply,it is appreciated. I rather not change your library as you now it better than I do. That being said, starting the Async routine by checking if reading is ready would be nice. Mabye adding a new function to the library would not be as hard as changing the old ones? then it would be 3 things I would like to see:

1: boolean sensorConversionIsReady(); Just one test showing if line is pulled low or high on selected sensor. A flag to indicate if Async procedure can be completed. If sensor is not ready,then prosess some more loops of the program. If sensor is ready, then we can end the Async by asking for the temperature from the scratchpad.

2: waitForConversion(); A call to make prior to ending Async transaction this is my while loop I posted, the program has done its processing and there is nothing left but wait for the temperature to show up. So we wait and then ask for the reading

3: show availeble ready sensors just return an array of the sensor addresses that have the conversion completed.

First two would be rather nice to have, and in two formats, one taking adress referance and the other using the selected sensor.

If you could implement this, It would ease the uncertanty of knowing if sensors have new data to send...

thanks. D.

EDIT: btw, when I say I saved 110ms in my first post it is in reality more, as the call to ds18b20 to take a new reading takes about 20ms So, the hardcoded delay is after that call so it is more like 130ms savings as I timed the whole loop, not just the conversion part... Your 600ms loop would probably just need 20-30ms more wait to complete the conversion not 150ms ;)

I need one particular temp. reading to be fast, as the valve opening acts quite fast on the temperature (less than a second).

What is the precision you need? Sometimes one uses 2 temp sensors, one for speed and one for precision...


wrt your porposal

1 and 2 sounds as the easy part, something like ...

boolean sensorConversionIsReady()
{
  return digitalRead(DS_PIN) == LOW);
}

void waitForConversion()
{
  while(sensorConversionReady());
}

3: show availeble ready sensors just return an array of the sensor addresses that have the conversion completed.

This last one is more difficult as it need to test every individual sensor IIRC. That takes time. Furthermore , the array would return 8 bytes per sensor, so that would add up in terms of memory. Or returning an unsigned long in which each bit represents one sensor in the address array? Maybe it is simpler to place the sensors not on one bus (one pin) but to have one pin per sensor (GND and +5V can be shared).

Not clear for me yet

as the sensor pulls the line low when converting, you would test for it geting high not low :slight_smile:

boolean sensorConversionIsReady()
{
return digitalRead(DS_PIN) == HIGH);
}

Regarding availableDevices:
The best way is probably to make a function that returns the next available ready sensor’s index number or selecting the nearest ready sensor
like nextReadySensor() or something like that, and while there are more sensors in the array, would return the next ready sensor having a higher index number than that is selected.
Having all available ready sensors would probably be harder to use and require multiple calls to use, and then more devices are probably ready :wink:
the Array would yes be more space eating and more complex to use
like:

for (i=0 ; i < getReadyDeviceCount() ; i++) {
Serial.print("device: ");
Serial.print(getReadyDevice[i]);  // getReadyDevice[i] would then give the sensor address of the ready array
Serial.print(" Temperature: ");
Serial.print(sensors.getTempC(getReadyDevice[i]));  // and get the temperature
}

There are traps using the line pulled low, as it is not done just for temp conversion but still it is the best indication :slight_smile:

FYI here’s my code for reading lots of DS18B20’s at once and accumulating stats - based on the example code but with non-blocking start conversion.

#include "Wire.h"
#include "OneWire.h"

#define DS18B20_PIN 8

OneWire ds(DS18B20_PIN) ;

// maximum sensors on the bus
#define M 20

// actual sensors detected
byte m = 0 ;

// addresses 
byte addr[M][8];

// stats
int N = 0 ;
double sums[M];
double sqsums[M];


void setup_tempsense ()
{
  m = 0 ;
  while (ds.search (addr[m]))
  {
    Serial.print ("Found address ") ;
    Serial.print (m, DEC) ;
    Serial.print (":  ") ;
    show_id (m) ;
    Serial.println () ;
    if (OneWire::crc8 (addr[m], 7) != addr[m][7])
    {
      Serial.println ("CRC is not valid!") ;
      return ;
    }
    if (addr[m][0] != 0x28)
    {
      Serial.println ("Device is not a DS18B20 family device.") ;
      return ;
    }
    m++ ;
  }

  Serial.println("No more addresses.") ;
  ds.reset_search () ;
  
}

void show_id (byte m)
{
    for (byte i = 6 ; i >= 1 ; i--)
    {
      Serial.print (addr[m][i] >> 4, HEX) ;
      Serial.print (addr[m][i] & 15, HEX) ;
    }
}

// asynchrounous start conversion
int start_conversion (byte i)
{
  // The DallasTemperature library can do all this work for you!

  ds.reset () ;
  ds.select (addr[i]) ;
  ds.write (0x44) ;         // start conversion, with parasite power on at the end
}

// asynchrounous read result
int read_conversion_result (byte i)
{
  byte present = ds.reset () ;
  ds.select (addr[i]) ;    
  ds.write (0xBE) ;         // Read Scratchpad

  byte data[9];
  for (byte i = 0 ; i < 9 ; i++)           // we need 9 bytes
  {
    data[i] = ds.read();
  }
  int temp = (data [1] << 8) | data[0] ;   // just want temperature
  return temp ;
}


// synchronous call to read temperature from one sensor
int read_temp (byte i)
{
  start_conversion (i) ;
  delay (800) ;     // maybe 750ms is enough, maybe not
  return read_conversion_result (i) ;
}


void setup ()
{
  Serial.begin (57600) ;
  delay (500) ;
  setup_tempsense () ;
  N = 0 ;
  for (byte i = 0 ; i < M ; i++)
  {
    sums [i] = 0.0 ;
    sqsums [i] = 0.0 ;
  }
}


void display_stats (byte i)
{
  if (N == 0)
    return ;
  float mean = sums[i] / N ;
  float meansq = sqsums[i] / N ;
  float devi = meansq - mean*mean ;
  show_id (i) ;
  Serial.print (" mean=") ; Serial.print (mean) ;
  Serial.print (" sd=") ; Serial.println (sqrt (devi)) ;
}


void loop ()
{
  // occasionally output accumulated stats
  if ((N & 15) == 0)
  {
    Serial.println ("-----") ;
    for (byte i = 0 ; i < m ; i++)
      display_stats (i) ;
    Serial.println ("-----") ;
  }

  // start all the conversions
  for (byte i = 0 ; i < m ; i++)
  {
    start_conversion (i) ;
  }
  // share the wait
  delay (750) ;
  // read all the results
  for (byte i = 0 ; i < m ; i++)
  {
    Serial.print ("dev ") ;
    show_id (i) ;
    Serial.print (":  ") ;
    int temp = read_conversion_result (i) ;
    float centigrade = ((float) temp) * 0.0625 ;
    Serial.print (centigrade) ;
    Serial.println ("C") ;
    // maintain stats:
    sums [i] += centigrade ;
    sqsums [i] += centigrade*centigrade ;
  }
  N ++ ;  // maintain stats
  Serial.println () ;

  delay (1000) ;
}

Using only a DS18B20 sensor with PID is tricky if the system needs a quick response.

But with several sensors, the reading time can be divided by the number of sensors.

Let’s try an example of 4 sensors,

If each sensor needs a time x, we can achieve a reading time of x / 4 or 1/4 of the time x,

See an image, from an example with 4 sensors, all configured for 10 bits of temperature data, each sensor needs up to 187.5ms for conversion, we will round to the value of 200ms.

But if we do an average of all 4 readings, we can get an update every 50ms ie the time of each sensor remains the same, but it is possible to start the conversion of one sensor and then read another sensor that is already With converted value.

bits … time … resolution … (Average time for 4 sensors)
09 … 093.75ms … 0.5000 … (023,4375ms)
10 … 187.50ms … 0.2500 … (046,8750ms)
11 … 375.00ms … 0.1250 … (093,7500ms)
12 … 750.00ms … 0.0625 … (187,5000ms)

bits … time … resolution … (Average time for 8 sensors)
09 … 093.75ms … 0.5000 … (11,71875ms)
10 … 187.50ms … 0.2500 … (23,43750ms)
11 … 375.00ms … 0.1250 … (46,87500ms)
12 … 750.00ms … 0.0625 … (93,75000ms)