Dallas 18B20 sensor

I'm not sure I understand the how the functions in the library work. I use the following extensively:

Temp1.requestTemperatures(); // where Temp1 is properly established in my declarations

Temp1.getTempCByIndex(0);

These have worked reliably for several years. Recently, however, I have noticed two anomalies:

  1. In some of my units (some are pro-mini and some are nano) I don't get a valid temperature until the second pass. I suspect the reading is always one loop late.

  2. I read these temperatures in a void function called readtemps() and assign the values to global variables. Lately, in a couple of uploaded sketches, I have gotten only zero(0.0) values. I can "fix" that by inserting a random delay, a few microseconds, into the function, but the delay isn't between the two calls above, but after the "getTempCByIndex() call. This makes no sense to me.

My main question is, do these functions work like Serial.print(), where there is some background serial communication going on after the function has returned. E.G., can you "step on" the background process and kill it so that you get odd results? I was under the impression that the "requestTemp" function stimulated the sensor to start a conversion and the "getTemp" function read the result, in each case the functions did not return until they had finished their operation?

I only use one DS18B20 per wire, no multiple sensors. Is there a good tutorial where I can use more basic calls to the library that are faster than waiting (up to 700 msec, I understand) for a conversion to complete (do the request then come back and do the get later?).

Thanks.

Dr_Quark: I was under the impression that the "requestTemp" function stimulated the sensor to start a conversion and the "getTemp" function read the result, in each case the functions did not return until they had finished their operation?

That's the default behaviour but you can use setWaitForConversion to change it so that requestTemp issues the request to the sensors and then returns. In that case it's then up to you to wait for the conversion to complete. IIRC, the time varies depending on how much precision you want, maxing out at 750ms for 12 bit.

I posted some code here that does that some years back. You could probably find it by searching for setWaitForConversion and my username.

My main question is, do these functions work like Serial.print(), where there is some background serial communication going on after the function has returned. E.G., can you "step on" the background process and kill it so that you get odd results?

My main question is why you don't look at the source code and see.

The Arduino has one processor. There are no background processes.

Is there a good tutorial where I can use more basic calls to the library that are faster than waiting (up to 700 msec, I understand) for a conversion to complete (do the request then come back and do the get later?).

There is always the source code.

// sets the value of the waitForConversion flag
// TRUE : function requestTemperature() etc returns when conversion is ready
// FALSE: function requestTemperature() etc returns immediately (USE WITH CARE!!)
//        (1) programmer has to check if the needed delay has passed
//        (2) but the application can do meaningful things in that time
void DallasTemperature::setWaitForConversion(bool flag){
    waitForConversion = flag;
}
// sends command for all devices on the bus to perform a temperature conversion
void DallasTemperature::requestTemperatures(){

    _wire->reset();
    _wire->skip();
    _wire->write(STARTCONVO, parasite);

    // ASYNC mode?
    if (!waitForConversion) return;
    blockTillConversionComplete(bitResolution, NULL);

}
bool DallasTemperature::isConversionAvailable(const uint8_t* deviceAddress){

    // Check if the clock has been raised indicating the conversion is complete
    ScratchPad scratchPad;
    readScratchPad(deviceAddress, scratchPad);
    return scratchPad[0];

}

Then, you use any of the get temperature methods to get the results.

PaulS,

You say "The Arduino has one processor. There are no background processes." This may be true for the processor itself, but all Arduino implementations I'm aware of have a hardware serial I/O that operates in the background. That I/O system can be "stepped on" by something as simple as changing an RS485 line from Tx to Rx before the buffer is empty.

Further, there are interrupt routines (like time() and millis()) that are keeping track of ticks. I don't generally do stuff that can "step on" interrupts, but I do consider interrupt operations as "background processes" that one needs to be aware of.

In that sense, I was wondering if there might be an interrupt process for communicating with the DS18B20 that I was unaware of.

As for "My main question is why you don't look at the source code and see" I can only say that I've tried that a couple of times and find that you experts use nifty C++ stuff I'm not yet familiar with, so libraries are a challenge. Besides, I have a spouse who thinks I should be doing the honey-do list instead of coding, so taking the time to delve into a library is also a challenge. But I will do more of that as time goes on. Thanks. :confused:

Dr_Quark:
I only use one DS18B20 per wire, no multiple sensors. Is there a good tutorial where I can use more basic calls to the library that are faster than waiting (up to 700 msec, I understand) for a conversion to complete (do the request then come back and do the get later?).

OneWire.h Library Functions could be used as more basic calls; also, codes can be written to let the DS18B20 sensor take as much time as it requires to finish the conversion. The following sketch may be helpful for you.

//12-bit default resolution; external power supply
#include<OneWire.h>
#define OneWireBusPin 10
OneWire ds(OneWireBusPin);  //2
byte addr1[8];         //to hold 64-bit ROM Codes of DS1
byte data[9];        //buffer to hold data coming from DS18B20
float celsius;
byte busStatus;

void setup()
{
  Serial.begin(9600);
  ds.reset();
  ds.search(addr1);  //collect 64-bit ROM code from sensor (DS1)
}

void loop()
{
  ds.reset();       //bring 1-Wire into idle state
  ds.select(addr1); //slect with DS-1 with address addr1
  ds.write(0x44);    //conversion command
  do   //let sensor take as much time as it needs to finish conversion
  {
    busStatus = ds.read();
  }
  while (busStatus != 0xFF);
  //---------------------------
  ds.reset();
  ds.select(addr1);  //selecting the desired DS18B20
  ds.write(0xBE);    //Function command to read Scratchpad Memory (9 Byte)
  ds.read_bytes(data, 9); //data comes from DS and are saved into buffer data[8]
  //---------------------------------

  int16_t raw = (data[1] << 8) | data[0]; //---data[0] and data[1] contains temperature data : 12-bit resolution-----
  celsius = (float)raw / 16.0;  //12-bit resolution
  Serial.println(celsius);
  delay(1000);
}

but all Arduino implementations I'm aware of have a hardware serial I/O that operates in the background.

They don't operate "in the background". They interrupt THE code that is running, do something VERY quick, and then the code resumes where it was.

but I do consider interrupt operations as "background processes" that one needs to be aware of.

You should alter your thinking, then, IMHO.

find that you experts use nifty C++ stuff I'm not yet familiar with, so libraries are a challenge.

Point taken, but I hardly consider the use of comments to be "nifty stuff I'm not yet familiar with". All I did was read the comments to know what function does what.

Besides, I have a spouse who thinks I should be doing the honey-do list instead of coding

Well, there is that...

FWIW, I’ve found that if your goal is to “keep the loop looping” (i.e. maximize number of loop iterations) while you’re waiting for the (slow) DS18B20 conversion process to finish, then do the following:

  • Use ‘.setWaitForConversion(false);’ so that control returns as soon as the temperature conversion process is kicked off.

  • use ‘.millisToWaitForConversion(uint8_t bitResolution)’ to get the maximum conversion time for the current resolution setting.

  • use ’.requestTemperatures();’ to start the temperature conversion process.

  • Use a millis timer (not ‘delay()’) to wait the required amount of time. During this waiting period your loop can be looping (i.e. doing useful stuff).

  • Request the temperature conversion results.

I’ve found that you get far more trips through the loop this way rather than by calling ‘.isConversionComplete()’. That’s because calling the function on every pass through loop is expensive (slow) and blocking. It also has interrupts disabled during part of its execution.

Also, calling ‘.getTempCByIndex(uint8_t deviceIndex)’ is slow and inefficient because it has to do a One Wire address scan every time it's called. Much better is to save all the DS18B20 devices’ One Wire addresses during ‘setup()’. Then, call ‘.getTempC(const uint8_t* deviceAddress)’ passing it a pointer to the address of the device you’re interested in.

That's because calling the function on every pass through loop is expensive (slow) and blocking.

bool DallasTemperature::isConversionAvailable(const uint8_t* deviceAddress){

    // Check if the clock has been raised indicating the conversion is complete
    ScratchPad scratchPad;
    readScratchPad(deviceAddress, scratchPad);
    return scratchPad[0];
}

Creating an instance of the ScratchPad class hardly takes a lot of time. Populating the instance requires I2C communication, to transfer 10 bytes (1 to, and 9 from, the DS device) which is pretty darned fast. Returning the first value of an array can hardly be considered expensive (slow).

PaulS: ``` bool DallasTemperature::isConversionAvailable(const uint8_t* deviceAddress){

    // Check if the clock has been raised indicating the conversion is complete     ScratchPad scratchPad;     readScratchPad(deviceAddress, scratchPad);     return scratchPad[0]; }



Creating an instance of the ScratchPad class hardly takes a lot of time. Populating the instance requires I2C communication, to transfer 10 bytes (1 to, and 9 from, the DS device) which is pretty darned fast. Returning the first value of an array can hardly be considered expensive (slow).

Except that this device doesn't use I2C. You should download the datasheet and read up on the One Wire protocol.

gfvalvo:
Except that this device doesn’t use I2C. You should download the datasheet and read up on the One Wire protocol.

The readScratchPad() method uses _wire as the instance name. I saw that, and thought TwoWire instance. I see now that it is an instance of the OneWire class.

Still, does sending 10 bytes take THAT long?

All I can tell you is that I've found my method to be faster, YMMV.

gfvalvo: All I can tell you is that I've found my method to be faster, YMMV.

Nothing wrong with waiting longer than necessary to get the next temperature value. After all, it will almost always be the same as the last value.

I’m not talking about getting the next temperature faster. I’m talking about using less processor cycles in ‘loop()’ determining if that next value is ready yet. Checking a millis() timer on every pass through loop is less expensive than calling the library function on every pass through loop.

Using the millis() method, in fact, does get you the new temperature a little slower. But, as you imply, so what? It does however, allow you to spend more processor cycles on doing useful things in loop() during the time you’re waiting for the conversion to finish.

Anyway, I’m simply reporting my findings. Y’all are, of course, free to ignore them.

gfvalvo, thanks for this recommendation. Since I only have one DS18B20 on a data pin, I thought I had seen a method for skipping the addressing and just using basic commands to read the registers. Anyway, I’m going to look into your idea. Saving the addresses during setup is going to take a little study on my part.

Also, calling ‘.getTempCByIndex(uint8_t deviceIndex)’ is slow and inefficient because it has to do a One Wire address scan every time it’s called. Much better is to save all the DS18B20 devices’ One Wire addresses during ‘setup()’. Then, call ‘.getTempC(const uint8_t* deviceAddress)’ passing it a pointer to the address of the device you’re interested in.

And thanks for all the other comments, too. I’ve got some code to read…

Dr_Quark: I don't get a valid temperature until the second pass. I suspect the reading is always one loop late.

I only use one DS18B20 per wire, no multiple sensors. Is there a good tutorial where I can use more basic calls to the library that are faster than waiting (up to 700 msec, I understand) for a conversion to complete (do the request then come back and do the get later?).

If the "invalid temperature" is 85, it is simply because you are not allowing sufficient time from startup for the sensor to do its job. .I really can't see the point of chasing a workround for this as it only happens onceand it is usually hidden by other other activities in setup, like checking SD card etc.