[SOLVED] 18B20 timing problem

i work on a termometer with 18b20, i read the temp by a variable and send it to the display.

but during the Temperature Reading Time (750ms on 12bit) the display is empty because the variable with the temp readed waiting the 750ms time for new reading.

Is there a way for using Arduino in parallel ?
1 process: reading temp
2 process: repeat for 750ms the last temp on the display

thanks

Yes. Use the Blink Without Delay example for how to schedule things. Send out the command to start a temperature conversion. 750 milliseconds later, send the command to read the scratchpad. Store the temperature in a global variable. Go back to sending out the command to start a new conversion. When you want to update the display just take the temperature from the global variable. It won't be very old.

thanks for the answer,
please look if i understand tips, this is the code that i used but do not work, the result is the same, my display are off during the 750ms conv getting.

temp is my global variable

void loop(void)
{ 
  
  
     sensors.requestTemperatures(); // Send the command to get temperatures
     
     // blink without delay technique
       unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis > interval) {
  
    previousMillis = currentMillis;   

    temp=sensors.getTempCByIndex(0); 
  }

It would be easier to help if you post the entire sketch - right now we can't see what the screen update code is doing.

Problem is the code will start a conversion every time through loop(). You want to start a new conversion AFTER you fetch the results of the last conversion:

void setup()
    {
    previousMillis = 0;
    sensors.requestTemperatures(); // Send the command to get temperatures
    }

void loop(void)
    { 
    // blink without delay technique
    unsigned long currentMillis = millis();
 
    if(currentMillis - previousMillis > 750) 
        {
        previousMillis = currentMillis;   

        temp=sensors.getTempCByIndex(0); 

        sensors.requestTemperatures(); // Send the command to get temperatures
       }
    }

nope, same problem,

this is complete code, visualizzazione is subroutine for display the value, it is correct because i use it in other code with other sensor.

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 16 (A2) on the Arduino
#define ONE_WIRE_BUS 16


// 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);
 
 // variabili temp
  int t1,t2,t3;
  float temp;
  long previousMillis = 0; 



void setup() 
{
  sensors.begin();
  sensors.setResolution(12);
  sensors.requestTemperatures(); // Send the command to get temperatures
}


void loop(void)
{ 

 // blink without delay technique 
  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis > 750) 
   {
      previousMillis = currentMillis;
    
    temp=sensors.getTempCByIndex(0); // current temperature to temp variables
    
    sensors.requestTemperatures(); // Send the command to get temperatures
   }
 
  	t1=(int)(temp/10);
	t2=(int)(temp-t1*10);
	t3=(int)(temp*10)%10;

        visualizzazione ();
}

The problem is the DallasTemperature library does the delay explicitly during the call to requestTemperatures() - look at the source!

Ah, found some of my code for driving this chip - you should be able to use start_conversion() and read_conversion_result() separately to drive the DS18B20 asynchronously. You might want to suggest to the DallasTemperature library maintainer to add asynchronous conversion...

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

#define DS18B20_PIN 8

OneWire ds(DS18B20_PIN) ;
 
byte addr[8];

void setup_tempsense ()
{
  
  if (!ds.search (addr))
  {
    Serial.println("No more addresses.") ;
    ds.reset_search () ;
    delay (250) ;
    return ;
  }
  
  Serial.print ("R=") ;
  for (byte i = 0 ; i < 8 ; i++)
  {
    Serial.print (addr[i], HEX) ;
    Serial.print (" ") ;
  }
  Serial.println () ;
  long unique = 0L ;
  for (byte i = 6 ; i >= 1 ; i--)
  {
    unique = (unique << 8) | addr [i] ;
  }
  Serial.print ("Unique=") ; Serial.println (unique, HEX) ;
    
  if (OneWire::crc8 (addr, 7) != addr[7])
  {
    Serial.println ("CRC is not valid!") ;
    return ;
  }
  if (addr[0] != 0x28)
  {
    Serial.println ("Device is not a DS18B20 family device.") ;
    return ;
  }
 
 int temp = read_temp () ;
 Serial.print (((float) temp) * 0.0625) ;
 Serial.println ("C") ;

}

int start_conversion ()
{
  // The DallasTemperature library can do all this work for you!

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


int read_conversion_result ()
{
  byte present = ds.reset () ;
  ds.select (addr) ;    
  ds.write (0xBE) ;         // Read Scratchpad

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

int read_temp ()
{
  start_conversion () ;
  delay (800) ;     // maybe 750ms is enough, play safe
  return read_conversion_result () ;
}


void setup ()
{
  Serial.begin (57600) ;
  setup_tempsense () ;
}



void loop ()
{
  float degC = (float) read_temp () ;
  degC *= 0.0625 ;
  Serial.println (degC) ;
  delay (2000) ;
}

I still don't really understand the problem, partly because I can't see the code. Since it takes 750ms to read the temperature, it's only going to change every 750ms, so why do you need to keep updating the display more rapidly? Is it a device that needs continuous refresh? If so, MarkT appears to have your solution.

Its a general problem that's going to hit lots of people - pausing one's entire application for nearly a second is not usually an option.

Oh, agreed, and you have a solution so job done. I just want to close the loop out of curiosity.

thanks to all for the interest,

MarkT the code do not work, i tried it with serial monitor, changing only the #define DS18B20_PIN 8 to 16, there is other changing to do ? I receive a fixed 85 degree every 2sec.

the last Dallas Library added async, if I understand, the link is here, but there is no example to use it MilesBurton.com

If the Dallas library doesn't support asynchronous starting of the conversion, just don't use it. The chips are easy enough to talk to:

#include <OneWire.h>

// how many milliseconds between grabbing data and logging it. 1000 ms is once a second
#define LOG_INTERVAL  10000 // mills between entries (reduce to take more/faster data)
unsigned long LastLogTime = 0;

//  Fault flags
extern const unsigned int ITemp_fault;
extern const unsigned int ETemp_fault;
extern unsigned int Faults;

// DS18S20 Temperature chip I/O
const int ONEWIRE_PIN = 9;
OneWire DS18S20(ONEWIRE_PIN);

// Can't make them const because the OneWire library doesn't take const. :(
byte InteriorThermometer[8] = {
  0x28, 0x95, 0xB9, 0x1D, 0x03, 0x00, 0x00, 0xB9};
byte ExteriorThermometer[8] = {
  0x28, 0xA9, 0x9E, 0x1D, 0x03, 0x00, 0x00, 0x80};

unsigned long lastTempConversionTime = 0;

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

void loop(void)
{
  unsigned long currentTime = millis();

  Thermometers_loop(LastLogTime, LOG_INTERVAL);

  if (currentTime - LastLogTime < LOG_INTERVAL)
    return;
    
 ///  Data Logging Starts Here

  LastLogTime = currentTime;

  Thermometers_log(Serial);
  Serial.println();
}

void Thermometers_setup(void)
{
}

void Thermometers_loop(unsigned long lastLogTime, unsigned long logInterval)
{
  unsigned long currentTime = millis();
  //  One second before logging, start the thermometers converting, 
  // as long as it has been more then two seconds since the last conversion
  if ((currentTime - lastLogTime >= (logInterval - 1000)) &&
     (currentTime - lastTempConversionTime > 2000))
     startThermometers();
}

void Thermometers_log(Print &dest)
{
  // Now read the temperatures
  float i_temperatureC = readThermometer(InteriorThermometer);
  float e_temperatureC = readThermometer(ExteriorThermometer);
 
  dest.print(i_temperatureC, 3);
  dest.print(", ");    
  dest.print(e_temperatureC, 3);
}

void startThermometers()
{
  lastTempConversionTime = millis();
  DS18S20.reset();
  DS18S20.write(0xCC);      // Address all thermometers
  DS18S20.write(0x44);         // start conversion, no parasite power on at the end
}

float readThermometer(byte addr[8])
{
  byte data[9];
  float temperature_C;

  DS18S20.reset();
  DS18S20.select(addr);    
  DS18S20.write(0xBE);         // Read Scratchpad

  for (int i = 0; i < 9; i++) 
    data[i] = DS18S20.read();

  if (data[5] != 0xFF || data[7] != 0x10)
  {
    return 999.999;
  }

  if ( OneWire::crc8( data, 8) == data[8])
  { // CRC is valid
    int temperature = (data[1] << 8) + data[0];  // Two byte binary temperature in °C/16
    // Serial.println(temperature, HEX);
    temperature_C = temperature / 16.0;
  }
  else
  {
   return 999.999;
  }
  return temperature_C;
}

Testato:
the last Dallas Library added async, if I understand, the link is here, but there is no example to use it MilesBurton.com

Looks like you just need to call setWaitForConversion(false) before you request temps.

1 Like

Here is an example for the async modus, it looks a bit like the blink without delay:

...
void setup()
{
setWaitForConversion(false);
requestTemp();
}

void loop()
{
if (waitedLongEnough)
{
getTemp();
requestTemp();
}
doSomeThingElse();
}

(real code, supported from version 3.7.0; )

//
// 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));     // resolution = {9,10,11,12}   // delayInMIllis = 94 << (resolution-9);  // gives 94, 188, 376, 752  faster but a bit less exact 
  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);
  
  // this part will print it at the push of a button
  if (digitalRead(BUTTONPIN) == LOW)
  {
    Serial.println(temperature);
  }
}

now i'm very close to the solution,
mix the two tip by john and markt i'm now have only little bit of display off problem,

befoure i had 750ms of black, now i think only 20ms.

what do you think, where are it from ? conversion time ?

this is actual loop

void loop(void)
{ 

 // blink without delay technique 
  unsigned long currentMillis = millis();
 
 if(currentMillis - previousMillis > 750) 
 {
  previousMillis = currentMillis;
    
    temp=sensors.getTempCByIndex(0); // mette la temperatura nella variabile temp
    sensors.setWaitForConversion(false);  // makes it async
    sensors.requestTemperatures(); // Send the command to get temperatures
    
 }

now i try the robtillart code

unsigned long previousMillis = 0;  // must be outside loop
float temp = 0.0;

//  UPDATED 
int timeout = 94 << (resolution - 9);  // calc the timeout for every resolution between 9..12 automatically 

void loop(void)
{ 
  if (millis() - previousMillis > timeout)   
  {
    temp = sensors.getTempCByIndex(0);  
    sensors.setWaitForConversion(false);   <<<<<<<<  strictly not needed every time
    sensors.requestTemperatures();  
    previousMillis = millis();  <<<<<<<<<<<<<<<  moved 
  }
  Serial.println(temp, 2);
}

rob your code do not work, because your formula for timeout calculation give 376 for 12bit, but the 18B20 sensor need 700-750ms.

BUT this is not a problem, because the display off problem is not related to timeout, if i put 2000ms timeout i receive

............|...........|............|
ehere "I" is the problem, in electronic we call it a Spike

if a change timeout, example to 1000ms:
......|......|.....|.....|......|......|

I think it is related to command "temp=sensors.getTempCByIndex(0);" maybe this command need 20ms and during this 20ms block the uprocessor.

if we use 18B20 normally, we have 750ms uprocessor blocked, if we use it in async we have only 20ms blocked.
maybe it is a OneWire bus limitation ?

I tried normal power supply mode but the problem remain the same :frowning:

this is the actual code

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 16 (A2) on the Arduino
#define ONE_WIRE_BUS 16


// 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);
 
 // variabili temp
  float temp;
  long previousMillis = 0; 


void setup() 
{
      
  sensors.begin();
  sensors.setResolution(12);
  sensors.setWaitForConversion(false);  // makes it async asincrono=non blocca il programma durante la lettura
  sensors.requestTemperatures(); // Send the command to get temperatures
 
}



void loop(void)
{ 

 // blink without delay technique. Il delay normale blocca il processore, questo no

 if (millis() - previousMillis > 750)   // mette la temperatura nella variabile temp dopo 750ms
 {
    temp=sensors.getTempCByIndex(0); 
    sensors.requestTemperatures(); // riparte una nuova richiesta, ma solo dopo, e non prima come facevo all'inizio
    previousMillis = millis(); // riparte il temporizzatore
 }

Can you grab & print millis before and after this statement:

temp=sensors.getTempCByIndex(0);

and see how long it takes. If your mysterious display device really needs continuous refresh, it may be the culprit in causing the black spot.