DS18B20 750ms delay bypass?

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

You don't need to use delay at all, just because the temperature sensor wants 750 ms doesn't mean you have to stop the microcontoller to give it what it wants. Several of my sketches contain this line :

#include <DallasTemperature.h> // this is stephen's modified version 
// added requestTemperaturesnodelay() function based on similar existing function
// but with no delays

I appended this this code to DallasTemperature.cpp in the library and called it with requestTemperaturesnodelay in the sketch and allow for the timing in the sketch (hint : blink without delay).

// sends command for all devices on the bus to perform a temperature without any delays built in (this must be allowed for in the sketch)
void DallasTemperature::requestTemperaturesnodelay(void)
{
  _wire->reset();
  _wire->skip();
  _wire->write(STARTCONVO, parasite);
}

every second it calls the the dallastemp.getTempCByIndex function immediately followed by requestTemperaturesnodelay so its ready for next time round, the initial request is done in setup, the DS1820 gets a whole second for its conversion, and the sketch doesn't stop whilst its doing it.

I agree that managing tasks outside the confines of the main loop is a more robust solution, especially when you require user interaction. I was just trying to give a simple solution.

FYI - Not sure how you are determining 1 second has passed, but if you are counting millis() you might want to check into millis() rollover handling.

Questions about millis():
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1200662708

willnue

after 52 days it will overflow, is there a problem with that? or will your app run for 52 days or more?

Senso,
I always thought it was a little over 9 hours, but I see now it is 50 days. Either way, you just need to be mindful that it will occur if your application requires long run times...

willnue

I think I'm a bit lost with all of this. I'll lay out what I've got and perhaps I can get a more tailored solution. This is what I have running right now.

I've assigned a name to each sensor:

DeviceAddress  TankTemp = { 0x28, 0xD3, 0x05, 0xB0, 0x02, 0x00, 0x00, 0x8F };
DeviceAddress SumpTemp = { 0x28, 0xE7, 0x08, 0xB0, 0x02, 0x00, 0x00, 0x52 };
DeviceAddress BaseTemp = { 0x28, 0xE0, 0xDE, 0xAF, 0x02, 0x00, 0x00, 0xB7 };
DeviceAddress UpTemp = { 0x28, 0x4A, 0xD3, 0xAF, 0x02, 0x00, 0x00, 0xC8 };
DeviceAddress LEDTemp = { 0x28, 0x2B, 0xF6, 0xAF, 0x02, 0x00, 0x00, 0x16 };

In the setup:

  sensors.begin();
  sensors.setResolution(SumpTemp, 10);
  sensors.setResolution(TankTemp, 10);
  sensors.setResolution(UpTemp, 10);
  sensors.setResolution(BaseTemp, 10);
  sensors.setResolution(LEDTemp, 10);

in the loop:

sensors.requestTemperatures();

Example of a pulled temp:

DallasTemperature::toFahrenheit(sensors.getTempC(TankTemp)

I have 5 of those temps/sensors (all wired up parasitically) on my main screen that I'd like updated about once per second. As well I have heaters/fans/chillers triggered by those temps. All of that works. But due to the delay I get lag trying to navigate screen/menus with momentary buttons with "uniquePress".

It seems like it should be something fairly simple to suit my needs, but I just don't understand enough to get it coded. I appreciate the help you all have tried to offer so far, but I need just a little more.

The dallas temperature library comes with an example with multiple (2) sensors. Please check that one, copy it and adapt to 3 4 5 sensors

Furthermore you might check the use of arrays - http://www.arduino.cc/en/Reference/Array -

#define DASIZE 5
#define TANKTEMP 0
#define SUMPTEMP 1
etc

DeviceAddress DA[DASIZE ] = {
  { 0x28, 0xD3, 0x05, 0xB0, 0x02, 0x00, 0x00, 0x8F },
  { 0x28, 0xE7, 0x08, 0xB0, 0x02, 0x00, 0x00, 0x52 },
  { 0x28, 0xE0, 0xDE, 0xAF, 0x02, 0x00, 0x00, 0xB7 },
  { 0x28, 0x4A, 0xD3, 0xAF, 0x02, 0x00, 0x00, 0xC8 },
  { 0x28, 0x2B, 0xF6, 0xAF, 0x02, 0x00, 0x00, 0x16 }
}
....

sensors.begin();
for (int i=0; i< DASIZE. i++)   sensors.setResolution(DA[i], 10);
...

sensors.requestTemperatures();
for (int i=0; i< DASIZE. i++)  Serial.println(sensors.getTempC(DA[i]));