DS18B20 750ms delay bypass?

I have 5 sensors oneWire DS18B20's that work just fine, but after some research I found that the reason my menu buttons are sluggish is that there is a 750ms delay for conversion as discussed at the bottom of the first page of this thread:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1263821196/0

So I see the code and I kinda can follow it, but I'm a bit intimidated as to how to shoe horn it into my sketch. His code uses a number of syntax that I'm not familiar with.

Is there a newer/better/streamlined method for not holding up the loop while it converts the temps for 750ms?

I'm kind of guessing not, but I figured I ask before spending the next week trying to figure out how to make it work in my 1600+ lines of code... :disappointed_relieved:

The technique I use is as follows. The main loop of the sketch has a section that gets executed once per second. It calls a function to read the temperature from the DS18B20. This function will start the temperature conversion on one call, then read the temperature on the next call and store the result in a global variable. Actually the function watches the time from a RTC, and starts the conversion when the seconds reach a certain value. So it only reads the sensor once per minute, but this would be easy to change for more (or less) frequent readings.

I'm using the OneWire library from OneWire Arduino Library, connecting 1-wire devices (DS18S20, etc) to Teensy

Would you mind sharing your code for that section? If I had a 2nd example of how to code that it would be easier for me to decipher. Thanks.

For the 3.7.0 version of the - MilesBurton.com - I have introduced the asynchronuous reading of the DS18B20 family. Although officially still in beta I've not encountered any failure with the code yet. There are sample sketches showing how to use it.

Rob

So when it says "you can choose to manage the delay in your own code " does that mean setting the resolution? And if I have mine set to 9 for all 5 sensors it should take a very small am mount of time to convert the temps, i.e. less than 100ms according to the WaitingForConversion2 sketch serial print out?

This seems to work for the most part. Basically it would seem that at resolution I now have a 10% chance of hitting a button during the conversion. Does that sound about right?

Not quite the perfect solution but it does make it a lot more responsive than it was.

I'd still be interested if there was less codely way of using the scratchpad to effectively elimination the delay/lag, but for now I'm happy.

Thanks for the help guys.

The DS18S20 has a fixed resolution of 9 bits, so setting the resolution won't help. The B series can be set from 9-12 bit.

In WaitingForConversion2.pde first a conversion request is send to the DS18B20, the sketch continues while the temp sensor does the conversion.
In the sketch a timeflag is set when thelast Request was done and the needed delay is calculated.

  sensors.setWaitForConversion(false);
  sensors.requestTemperatures();
  delayInMillis = 750 / (1 << (12 - resolution)); 
  lastTempRequest = millis();

In loop() it is checked if this delayInMillis miliseconds has passed before the temperature is fetched from the sensors. In the meanwhile one can do other things. In the example it increases just a counter. You could check if your Button is pressed and hold that in a state variable.

I'd still be interested if there was less codely way of using the scratchpad to effectively elimination the delay/lag, but for now I'm happy.

The DS18 series need conversion time (There is an ADC within the sensor that need to stabilize) and the asynchronuous method gives you most degrees of freedom.

The sketch below fetches the temperature continuously in the async way so it takes relatively little time. When the button is pressed there is allways an actual temperature available.

//
// Sample of using Async reading of Dallas Temperature Sensors
// 
#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

DeviceAddress tempDeviceAddress;

int  resolution = 12;
unsigned long lastTempRequest = 0;
int  delayInMillis = 0;
float temperature = 0.0;
int  idle = 0;

// button
#define BUTTONPIN 4


//
// SETUP
//
void setup(void)
{
  Serial.begin(115200);
  Serial.println("Dallas Temperature Control Library - Async Demo");
  Serial.print("Library Version: ");
  Serial.println(DALLASTEMPLIBVERSION);
  Serial.println("\n");

  // initialization
  sensors.begin();
  sensors.getAddress(tempDeviceAddress, 0);
  sensors.setResolution(tempDeviceAddress, resolution);
  
  sensors.setWaitForConversion(false);
  sensors.requestTemperatures();
  delayInMillis = 750 / (1 << (12 - resolution)); 
  lastTempRequest = millis(); 
  
  pinMode(13, OUTPUT); 
}

void loop(void)
{ 
  // this part keeps the var temperature up to date as possible.
  if (millis() - lastTempRequest >= delayInMillis) // waited long enough??
  {
    digitalWrite(13, LOW);
    temperature = sensors.getTempCByIndex(0);
    sensors.requestTemperatures(); 
    lastTempRequest = millis(); 
  }
  digitalWrite(13, HIGH);
  
  if (digitalRead(BUTTONPIN) == LOW)
  {
    Serial.println(temperature);
  }
}

My mistake I have DS18B20s. I basically drag and dropped your code in and it does seem to work very well, short of it is only pulling the first temp. The parasitic temps are not returning proper values.

My sketch is constantly displaying the current temps and the buttons change the pages of the menu. Previously the menu buttons would fail to do anything, assuming due to the conversion time, but now buttons work every time. So that is really good, just need to tweak it so that the other temps are displayed properly.

make temperature an array with 5 elements and fetch all 5 of them in a for loop()

  // this part keeps the var temperature up to date as possible.
  if (millis() - lastTempRequest >= delayInMillis) // waited long enough??
  {
    digitalWrite(13, LOW);
    for (int i=0; i< 5; i++)  temperature[i] = sensors.getTempCByIndex(i);  // <<<<<<<<<<<<<<<<<<<<
    sensors.requestTemperatures(); 
    lastTempRequest = millis(); 
  }

Why, originally did I only need have this "sensors.requestTemperatures();" once in the loop and the all the temps were pulled just fine. But now that it's part of the IF statement it only pulls the first one? Placing the values in an array doesn't really work for me because all of my code is based on each address already having a specific variable name.

If I comment out the getTempCByIndex line, either with the for loop or not, it still only populates the first probes temp. It's almost as if the way it's running it ignores all the parasites. It knows if they are plugged in because I get 185 F if they are in and -196.9 F if they are not...

Molehs:
Would you mind sharing your code for that section? If I had a 2nd example of how to code that it would be easier for me to decipher.

I've copied-and-pasted the pertinent code from a much larger sketch, so this is not guaranteed to run verbatim, but should give the general idea. Note that I am using the DS18B20, not DS18S20. (Too easy to miss that!) HTH.

#include <OneWire.h>           //http://www.pjrc.com/teensy/td_libs_OneWire.html
#include <Wire.h>              //http://arduino.cc/en/Reference/Libraries
#include <Time.h>              //http://www.arduino.cc/playground/Code/Time
#include "DS1307RTC.h"         //http://www.arduino.cc/playground/Code/Time (declares the RTC variable)

time_t utcNow, utcLast;		   //RTC is set to UTC
int utcH, utcM, utcS;		   //utc time parts
OneWire  ds(dsPin);            //DS18B20 temperature sensor
int tF10;                      //temperature from DS18B20    

...

void loop(void) {
    utcNow = now();
    if (utcNow != utcLast) {           //once through here per second ought to be enough
        utcLast = utcNow;
        utcH = hour(utcNow);
        utcM = minute(utcNow);
        utcS = second(utcNow);
        readTemperature();
}

/*-------------------------------------------------------------*
 * Start the temperature conversion at second 55, read it back *
 * at second 56.                                               *
 *-------------------------------------------------------------*/
int readTemperature() {
    byte dsData[12];

    if (utcS == 55) {
        ds.reset();
        ds.skip();
        ds.write(0x44);        //start temperature conversion
    }

    if (utcS == 56) {
        ds.reset();
        ds.skip();
        ds.write(0xBE);        //read scratchpad

        for ( int i=0; i<9; i++) {    //read 9 bytes
            dsData[i] = ds.read();
        }
        if (OneWire::crc8( dsData, 8) == dsData[8]) {
            tF10 = tempF_DS18B20(dsData[1], dsData[0]);
            haveTemperature = true;
        }
        else {
            Serial.print(" CRC ERR");
        }
    }
}

int tempF_DS18B20(byte tempMSB, byte tempLSB) {
    /* Convert 12-bit °C temp from DS18B20 to an integer which is °F * 10    *
     * Jack Christensen 30Jan2011                                            */

    int tC16;        //16 times the temperature in deg C (DS18B20 resolution is 1/16 °C)
    int tF160;       //160 times the temp in deg F (but without the 32 deg offset)
    int tF10;        //10 times the temp in deg F

    tC16 = (tempMSB << 8) + tempLSB;
    tF160 = tC16 * 18;
    tF10 = tF160 / 16;
    if (tF160 % 16 >= 8) tF10++;        //round up to the next tenth if needed
    tF10 = tF10 + 320;                  //add in the offset (*10)
    return tF10;
}

if (utcNow != utcLast) { //once through here per second ought to be enough

That should be enough but this code is no guarantee it will be called every second. Suppose that the executing part of the if statement takes 3 seconds to execute then the if statement will be true every time its called, but every 3 seconds two will be missed.

That said if you check the readTemperature() function it does a hard check it utcS == 55 or 56, but maybe these two values are skipped ... Don't know but can be.
Get the point?

I changed the readTemperature code to cope with skipping seconds.

(code not tested/compiled, just edited)

void readTemperature() 
{
    static boolean tempRequested == false;

    byte dsData[12];

    if (utcS >= 55)     // notice the  >=  iso  ==
    {
        ds.reset();
        ds.skip();
        ds.write(0x44);            
        tempRequested = true;
        return;      // <<<<<<<<<<<<<<<<<<<<<< we're ready  so return.
    }

    if (utcS >= 56 && tempRequested == true)
   {
        ds.reset();
        ds.skip();
        ds.write(0xBE);        //read scratchpad
        tempRequested == false;  // reset flag for next iteration

        for ( int i=0; i<9; i++) dsData[i] = ds.read();

        if (OneWire::crc8( dsData, 8) == dsData[8]) 
        {
            tF10 = tempF_DS18B20(dsData[1], dsData[0]);
            haveTemperature = true;
        }
        else {
            Serial.print(" CRC ERR");
        }
    }
}

This code is not failsafe yet, A better approach would be to separate the two activities, request a temperature conversion and fetch the temperature. Check if there was a reqwuest at least a second ago. Could be something like ...

void loop(void) 
{
    utcNow = now();
    if (utcNow != utcLast) 
    {          
        utcLast = utcNow;
        utcH = hour(utcNow);
        utcM = minute(utcNow);
        utcS = second(utcNow);
        if (utCS >= 55) requestTemperature();
        if (utcS >= 56) readTemperature();
}

boolean tempRequested = false;
long timeRequestTime = 0;

void requestTemperature()
{
    ds.reset();
    ds.skip();
    ds.write(0x44);        //start temperature conversion
    tempRequested = true;
    timeRequestTime = utcLast;
}


void readTemperature() 
{
   if (tempRequested == false) return;  // only if temp was requested 
   utcNow = now();
   if (utcNow - timeRequestTime <  1)  return;  // at least a second ago

    byte dsData[12];
    ds.reset();
    ds.skip();
    ds.write(0xBE);        //read scratchpad
    tempRequested = false;

    for ( int i=0; i<9; i++) dsData[i] = ds.read();

    if (OneWire::crc8( dsData, 8) == dsData[8]) 
    {
        tF10 = tempF_DS18B20(dsData[1], dsData[0]);
        haveTemperature = true;
    }
    else 
    {
        Serial.print(" CRC ERR");
    }
}

robtillaart:
That should be enough but this code is no guarantee it will be called every second. Suppose that the executing part of the if statement takes 3 seconds to execute then the if statement will be true every time its called, but every 3 seconds two will be missed.

Rob, that is, of course, quite correct. Thanks for pointing it out. In the case of my sketch, nothing comes close to taking even a second, so I felt the code was safe for my purposes (and there are actually several other things going on in the loop that I omitted as they had no bearing on the central question here).

Moral of the story: Assumptions that may be reasonable for one application may be completely unreasonable for the next!

Assumptions that may be reasonable for one application may be completely unreasonable for the next!

A good lesson, in fact it is even worse,

Assumptions for one application at a given time/place my even be incorrect later/somewhere else.

Small note on the conversion time. You can assume the max. delay from the datasheet or poll the data line to the sensor. It will go high when conversion is finished.
Something like;

while (ds.read() == 0) { // wait for completion }

regards,

Jeroen.

Hi Jeroen,

Read that too before but never tried in real life. Do you know the following?

Suppose you have multiple T sensors, same resolution (or not -> bonus points :slight_smile:
When will the line go high, (A) when the first sensor is ready or (B) when they are all ready or (C) something else?

if A then the signal cannot be trusted to read all sensors.
B would be very useable as one could test, fail -> continue, succeed -> getTemp()
C ??

Rob

What understand from the datasheet (and reality so it seems, it works for me :slight_smile: ) is that a sensor stops pulling the line low when finished converting. Only the last sensor that is finished will let the line go up because of the pull up resistor. (never tried parasitic powering)

small part of my code:

// tell all sensors on bus to convert temperature and wait until done (NOT PARASITIC POWERED)
void convertTempAll()         
{       
  ds.reset();                      // reset bus
  ds.skip();                        // broadcast to all sensors
  ds.write(0x44,0);             // start conversion
  while (ds.read() == 0) {  }  // wait for conversion end.  ( ~600 milliS on average at room temperature)
}

After this i read the sensors individually to get the actual data (no conversions in between readings). Especially useful as the conversion time isn't (very) dependent on number of sensors any more.

Regards,

Jeroen.

( ~600 milliS on average at room temperature)

That's interesting, that is 150 millis == 20% faster response. Currently no duino free to test :frowning: => todo list .

That is in my room, in my situation, with 7 or so sensors, with my code etc. Your mileage may vary.

Jeroen.

OK, but it is probably allways faster than waiting the fixed 750 millis which is staed in the datasheet, I assume the makers of the DS18* have specified a save margin of 5-20%.

regards,
Rob

I think the 750ms delay is only needed when you are running in parasite power mode, since you have to give the sensor time to "steal" power from the DQ line and charge up enough juice to perform the conversion.

From the Maxim Datasheet (http://datasheets.maxim-ic.com/en/ds/DS18B20.pdf):

CONVERT T [44h]
This command initiates a single temperature conversion. Following the conversion, the resulting thermal data is stored in the 2-byte temperature register in the scratchpad memory and the DS18B20 returns to its low-power idle state. If the device is being used in parasite power mode, within 10µs (max) after this command is issued the master must enable a strong pullup on the 1-Wire bus for the duration of the conversion (tCONV) as described in the Powering the DS18B20 section. If the DS18B20 is powered by an external supply, the master can issue read time slots after the Convert T command and the DS18B20 will respond by transmitting a 0 while the temperature conversion is in progress and a 1 when the conversion is done. In parasite power mode this notification technique cannot be used since the bus is pulled high by the strong pullup during the conversion.

In the code I use for reading DS18B20 sensors without parasite power I have either 100ms or zero delay between issuing the CONVERT T [44h] write to the scratchpad then reading the results of the conversion and both seem to work fine. Keep in mind I am cycling through the sensors found from a "search" request one at a time and reading each one individually. It seems the time it takes my Arduino to issue the reset to the 1-Wire bus then "select" the particular sensor it is working with so it can then read the temp from the scratchpad is enough time for the DS18B20 to finish computing the temperature.

If you like just keep lowering your delay to zero and stop when it doesn't work anymore - you can't really break it. In my experience 100ms should be more than enough time if you don't feel comfortable with zero delay.

FYI - In the code I use for reading temperature and voltage on the ADC of a DS2438 I do wait 100ms each for temp/voltage conversion, but I haven't had a need to try to shorten that yet.

Hope it helps,
willnue