Stopwatch + DS18B20 temp sensor + PCD8544 display (NOKIA 5110 LCD)

Hi everyone,

I’m developing a mobile device for photographic processing(developing film,paper) with Arduino Pro Mini 8MHZ what can display the temperatures using the DS18B20 temp sensor( internal and external) and meanwhile measuring the temperatures can be able to do stopwatch (START/PAUSE and RESET) and has a battery monitor function as well.

The problem is that the temperature measuring function slows down the stopwatch.

Everything works fine if i remove the temp measuring function from the sketch.

I would like to keep the resolution at 11bit. I was looking for solutions but could not make it work. I was reading about the async mode in the DallasTemperature library but didnt understand well.

Is this possible to make it work like running the stopwatch accurately and meanwhile measuring the temperatures?

Is there any programming solution to solve this problem? Or should i use different temp sensor, like TMP36?

Many thanks in advance.

There is the whole sketch:

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

//      SCK  - Pin 8
//      MOSI - Pin 9
//      DC   - Pin 10
//      RST  - Pin 11
//      CS   - Pin 12
//



OneWire oneWire(2);
DallasTemperature sensors(&oneWire);


DeviceAddress tempSensorext = {0x28, 0xFF, 0x41, 0xF2, 0x4C, 0x04, 0x00, 0xAB};  
DeviceAddress tempSensorin = {0x28, 0xFF, 0x2B, 0x05, 0x20, 0x16, 0x04, 0xF2}; 



float tempext;
float tempin;
int battVolts;


LCD5110 myGLCD(8,9,10,11,12);

extern uint8_t SmallFont[];
extern uint8_t TinyFont[];
extern uint8_t BigNumbers[];
extern uint8_t MediumNumbers[];


const int BUTTON1 = 5;     //The pin for button1 START/PAUSE
const int BUTTON2 = 6;     //The pin for button2 RESET

long lastPressedBtn1 = 0;    //Last millisecond we pressed button1
long lastPressedBtn2 = 0;    //Last millisecond we pressed button2;

boolean run = false;         //Should the system run?

int button1State = LOW;      //The value the button1 reads
int button1CurrState = LOW;  //The last value of button1
int button2State = LOW;      //The value of button1
int button2CurrState = LOW;  //The last value of button2



long timer = 0; //The timer

int second = 0;
int minute = 0;
int tenth = 0;



void setup(void)
{
  sensors.setResolution(tempSensorext, 11);
  sensors.setResolution(tempSensorin, 11);
  
  myGLCD.InitLCD();
  myGLCD.setFont(SmallFont);
  myGLCD.setFont(TinyFont);
  myGLCD.setFont(BigNumbers);
  myGLCD.setFont(MediumNumbers);
  
  myGLCD.setContrast(70);

  pinMode(BUTTON1, INPUT);
  pinMode(BUTTON2, INPUT);

  updateLCD();
}


void loop()

{

  // battery monitoring  stuff
 
    battVolts=getBandgap();  

    int batt_lcd=battVolts;
    batt_lcd = map(batt_lcd, 240, 370, 0, 100);


    
if (battVolts >370) {
      batt_lcd=100;
      myGLCD.drawLine(0,6,84,6);
      myGLCD.printNumI(batt_lcd, 69,0);
      myGLCD.setFont(TinyFont);
      myGLCD.print("%",81,0);
    }

    

    
  sensors.requestTemperaturesByAddress(tempSensorext); // Send the command to get temperatures
  sensors.requestTemperaturesByAddress(tempSensorin);
  
  tempext = sensors.getTempC(tempSensorext);
  tempin = sensors.getTempC(tempSensorin);
   



// starting the stopwatch functions
   
  tickClock(); 
  button1State = digitalRead(BUTTON1);
  button2State = digitalRead(BUTTON2); 
  checkStart();
  checkClear();  

  

 
    


// Displaying the values( temp in, temp out, battery)

  myGLCD.setFont(MediumNumbers);
  myGLCD.printNumF(float(tempext), 2, CENTER, 10);
  
  
  myGLCD.setFont(SmallFont);
  myGLCD.print("C",78,10);
  myGLCD.drawCircle(77,10,1);
 


  myGLCD.drawLine(0,29,84,29);     
 


  myGLCD.setFont(TinyFont);
  myGLCD.print("i",0,0);
  myGLCD.print("n",3,0);
  myGLCD.printNumF(float(tempin),2,10,0);
  myGLCD.print("C",35,0);
  myGLCD.drawCircle(34,0,0);

  
  myGLCD.drawLine(0,6,84,6);
  myGLCD.printNumI(batt_lcd, 69,0);
  
  myGLCD.setFont(TinyFont);
  myGLCD.print("%",81,0);

  myGLCD.update();  

  
  }


  

// Functions for the stopwatch based on https://github.com/mariusmu/arduino-stopwatch/blob/master/Stoppeklokke.ino


void checkStart() {
  if(button1State != button1CurrState) {    //Check if the button state has changed
    if(button1State == HIGH && (millis()-lastPressedBtn1) > 100 || lastPressedBtn1 == 0) {
      lastPressedBtn1 = millis();
      run = !run; //Switch the running state
    }    
  }
  button1CurrState = button1State;    //Set the current state equals the button state
}

/**
 * Check for button 2 press
 * If the second button is pressed, 
 * be sure that there is at least 100 since the last press
 */
void checkClear() {
  if(button2State != button2CurrState) {
    if(button2State == HIGH && (millis()-lastPressedBtn2) > 100 || lastPressedBtn2 == 0) {  
      lastPressedBtn2 = millis();
      clearAll(); //Clear the display
    }    
  }
  button2CurrState = button2State;   //Set btn2 current state equals the btn2State
}

/** 
 * Tick one tenth of a second if it should be running
 */
void tick() {
  if(run) {
    updateLCD();
    
    
    
    if(tenth == 9) {
      tenth = 0;
      
      if(second == 59) {
        second = 0;
        minute++;
        
      } else {
        second++;
      }
    } else {
    tenth++;
    }
  }
}



void updateLCD() {
  


  myGLCD.setFont(MediumNumbers);
  myGLCD.printNumI(minute,15,32, 2, ' 0' );

  myGLCD.printNumI(second,45,32, 2 ,' 0' );
  myGLCD.update();

}

/**
 * Clear the display
 * Set all time variables equal 0
 */
void clearAll() {
  run = false;
  second = 0;
  minute = 0;
  tenth = 0;
  updateLCD(); 
  

}


void tickClock() {
  Serial.println(millis() /10);
  if((timer - millis()/100) >= 100 || timer == 0) {
    tick();
    timer = millis()/100;
  }
}





// Battery monitoring


// Function created to obtain chip's actual Vcc voltage value, using internal bandgap reference
// This demonstrates ability to read processors Vcc voltage and the ability to maintain A/D calibration with changing Vcc
// Now works for 168/328 and mega boards.
// Thanks to "Coding Badly" for direct register control for A/D mux
// 1/9/10 "retrolefty"
// http://forum.arduino.cc/index.php?topic=92074.0

  int getBandgap(void) // Returns actual value of Vcc (x 100)
    {
        
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
     // For mega boards
     const long InternalReferenceVoltage = 1115L;  // Adjust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc reference
        // MUX4 MUX3 MUX2 MUX1 MUX0  --> 11110 1.1V (VBG)         -Selects channel 30, bandgap voltage, to measure
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR)| (0<<MUX5) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
  
#else
     // For 168/328 boards
     const long InternalReferenceVoltage = 1075L;  // Adjust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc external reference
        // MUX3 MUX2 MUX1 MUX0  --> 1110 1.1V (VBG)         -Selects channel 14, bandgap voltage, to measure
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
       
#endif
     delay(50);  // Let mux settle a little to get a more stable A/D conversion
        // Start a conversion  
     ADCSRA |= _BV( ADSC );
        // Wait for it to complete
     while( ( (ADCSRA & (1<<ADSC)) != 0 ) );
        // Scale the value
     int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L; // calculates for straight line value 
     return results;
 
    }

I'm not going to read the code - what with all that insulting whitespace in there - but I think your problems are elsewhere. You might find you can be better off using some sort of programmable interrupt facility as the timer.

Check "blink without delay"

This can give you a fixed time window in which to do things. I'm not even sure this approach is necessary, but it is a more elegant path, and might help analyse the problem. You are already aware that the the DS18b20 takes time to do its job but that doesn't mean everything else has to wait for it. The only situation where the DS18B20 itself fails is where you simply require readings faster than it can deliver, and I can't see how this can be with a photographic process timer. Even then, I don't think it will slow things down, but simply return the 85 error code.

Even with a simple delayed loop, I can read and display three DS18B20 at one second intervals and do a hell of a lot more than you are doing. So I think DS18B20 is innocent, and so is its attendant code.

Thanks for your reply, i will check the "blink without delay" out as soon as i can. Sorry for the lot of whitespace in the code, i'm not an expert programmer just an enthusiast of arduino. The whitespace makes the code more managable to me.

This is just gonna be a simple stopwatch, when i develop my films it is necessary to use stopwatch and measure the elapsed time and check the temps of the chemicals, i used to use my phone as a stopwatch...

I tried to use the "blink without delay" example, but unfortunately the stopwatch still inaccurate.
Any ideas?

No. I'm afraid I don't know anything about buttons, stopwatches, or voltage readings. I do know a bit about DS18B20 and I don't think they are the cause of your problem. Having said that, I do hope you are not using parasitic power as, by doing so, the sensors run at the same speed they do with with 12bit resolution. Even then, I still doubt that it is the cause of your problem. Further, I would have thought that the DS18B20 is the ideal package for the job.

I recognise that you said

Everything works fine if i remove the temp measuring function from the sketch.

but I submit the real problem is likely to be the way you have integrated the temp measuring into what I understand to be first and foremost a simple stopwatch programme, rather than the procedure itself. Maybe the stopwatch is the problem.

Perhaps a block diagramme of what you are doing, including time intervals, would help.

While I refused to read your previous code, nobody is going to read your current code if you won't post it, and doing so with irrelevant comments deleted, and several dozen fewer blank lines will be nice. You never ever need more than one blank line at a time. Also

ADCSRA |= _BV( ADSC ); // Start a conversion

is a bit easier to live with than

// Start a conversion
ADCSRA |= _BV( ADSC );

balazsbondar:
I tried to use the "blink without delay" example, but unfortunately the stopwatch still inaccurate.
Any ideas?

Post your code.

Thanks for your replies, i modified the code and yes, you were right it’s more clear to read the code without the whitespaces and the comments between the code.

There is the code with the method of the “blink without delay”. As i understand it is checking the temp sensors in every seconds.
However the stopwatch is still inaccurate. :frowning:

Should i use another method for the stopwatch or it just had integrated in a wrong way?

#include <DallasTemperature.h>
#include <OneWire.h>
#include <LCD5110_Graph.h>
OneWire oneWire(2);
DallasTemperature sensors(&oneWire);
DeviceAddress tempSensorext = {0x28, 0xFF, 0x41, 0xF2, 0x4C, 0x04, 0x00, 0xAB};  
DeviceAddress tempSensorin = {0x28, 0xFF, 0x2B, 0x05, 0x20, 0x16, 0x04, 0xF2}; 
float tempext;
float tempin;
int battVolts;
LCD5110 myGLCD(8,9,10,11,12);
extern uint8_t SmallFont[];
extern uint8_t TinyFont[];
extern uint8_t BigNumbers[];
extern uint8_t MediumNumbers[];
const int BUTTON1 = 5;     //The pin for button1 START/PAUSE
const int BUTTON2 = 6;     //The pin for button2 RESET
long lastPressedBtn1 = 0;    //Last millisecond we pressed button1
long lastPressedBtn2 = 0;    //Last millisecond we pressed button2;
boolean run = false;         //Should the system run?
int button1State = LOW;      //The value the button1 reads
int button1CurrState = LOW;  //The last value of button1
int button2State = LOW;      //The value of button1
int button2CurrState = LOW;  //The last value of button2
long timer = 0; //The timer
int second = 0;
int minute = 0;
int tenth = 0;
unsigned long previousMillis = 0;    //from the "blink without delay" example
const long interval = 1000;             //from the "blink without delay" example

void setup()
{
  sensors.setResolution(tempSensorext, 11);
  sensors.setResolution(tempSensorin, 11);
  myGLCD.InitLCD();
  myGLCD.setFont(SmallFont);
  myGLCD.setFont(TinyFont);
  myGLCD.setFont(BigNumbers);
  myGLCD.setFont(MediumNumbers);
  myGLCD.setContrast(70);
  pinMode(BUTTON1, INPUT);
  pinMode(BUTTON2, INPUT);
  updateLCD();
}

void loop()
{
  tickClock();                                        // starting the stopwatch functions
  button1State = digitalRead(BUTTON1);
  button2State = digitalRead(BUTTON2); 
  checkStart();
  checkClear();  

  unsigned long currentMillis = millis();                     // from the "blink without delay" example
  if (currentMillis - previousMillis >= interval) {
                                                                               // save the last time measured the temps
    previousMillis = currentMillis;
    
  sensors.requestTemperaturesByAddress(tempSensorext); // Send the command to get temperatures
  sensors.requestTemperaturesByAddress(tempSensorin);
  tempext = sensors.getTempC(tempSensorext);
  tempin = sensors.getTempC(tempSensorin);
     
  battVolts=getBandgap();                            //Battery monitoring
  int batt_lcd=battVolts;
  batt_lcd = map(batt_lcd, 240, 370, 0, 100);
  if (battVolts >370) {
      batt_lcd=100;
      }
    
  myGLCD.setFont(MediumNumbers);             
  myGLCD.printNumF(float(tempext), 2, CENTER, 10);    // Displaying the tempext 
  myGLCD.setFont(TinyFont);
  myGLCD.printNumF(float(tempin),2,10,0);             // Displaying the tempin 
  myGLCD.setFont(TinyFont);
  myGLCD.printNumI(batt_lcd, 69,0);
  myGLCD.setFont(SmallFont);
  myGLCD.print("C",78,10);
  myGLCD.drawCircle(77,10,1);
  myGLCD.drawLine(0,29,84,29);     
  myGLCD.setFont(TinyFont);
  myGLCD.print("i",0,0);
  myGLCD.print("n",3,0);
  myGLCD.print("C",35,0);
  myGLCD.drawCircle(34,0,0);
  myGLCD.drawLine(0,6,84,6);
  myGLCD.printNumI(batt_lcd, 69,0);
  myGLCD.setFont(TinyFont);
  myGLCD.print("%",81,0);
  myGLCD.update();  
  }
}
// Functions for the stopwatch based on https://github.com/mariusmu/arduino-stopwatch/blob/master/Stoppeklokke.ino

void checkStart() {
  if(button1State != button1CurrState) {                   //Check if the button state has changed
    if(button1State == HIGH && (millis()-lastPressedBtn1) > 100 || lastPressedBtn1 == 0) {
      lastPressedBtn1 = millis();
      run = !run; //Switch the running state
    }    
  }
  button1CurrState = button1State;                        //Set the current state equals the button state
}

void checkClear() {                                      //Check for button 2 press  If the second button is pressed, be sure that there is at least 100 since the last pres
  if(button2State != button2CurrState) {
    if(button2State == HIGH && (millis()-lastPressedBtn2) > 100 || lastPressedBtn2 == 0) {  
      lastPressedBtn2 = millis();
      clearAll(); //Clear the display
    }    
  }
  button2CurrState = button2State;   //Set btn2 current state equals the btn2State
}

void tick() {   //  Tick one tenth of a second if it should be running
  if(run) {
    updateLCD();
      if(tenth == 9) {
      tenth = 0;
      if(second == 59) {
        second = 0;
        minute++;
        } 
       else {
        second++;
      }
    } else {
    tenth++;
    }
  }
}

void updateLCD() {
  myGLCD.setFont(MediumNumbers);
  myGLCD.printNumI(minute,15,32, 2, ' 0' );
  myGLCD.printNumI(second,45,32, 2 ,' 0' );
  myGLCD.update();
}

void clearAll() {                //Clear the display - Set all time variables equal 0
  run = false;
  second = 0;
  minute = 0;
  tenth = 0;
  updateLCD(); 
 }

void tickClock() {
  Serial.println(millis() /10);
  if((timer - millis()/100) >= 100 || timer == 0) {
    tick();
    timer = millis()/100;
  }
}

// Battery monitoring
// Function created to obtain chip's actual Vcc voltage value, using internal bandgap reference
// This demonstrates ability to read processors Vcc voltage and the ability to maintain A/D calibration with changing Vcc
// http://forum.arduino.cc/index.php?topic=92074.0

  int getBandgap(void) // Returns actual value of Vcc (x 100)
    {
        
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
     // For mega boards
     const long InternalReferenceVoltage = 1115L;  // Adjust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc reference
        // MUX4 MUX3 MUX2 MUX1 MUX0  --> 11110 1.1V (VBG)         -Selects channel 30, bandgap voltage, to measure
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR)| (0<<MUX5) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
  
#else
     // For 168/328 boards
     const long InternalReferenceVoltage = 1075L;  // Adjust this value to your boards specific internal BG voltage x1000
        // REFS1 REFS0          --> 0 1, AVcc internal ref. -Selects AVcc external reference
        // MUX3 MUX2 MUX1 MUX0  --> 1110 1.1V (VBG)         -Selects channel 14, bandgap voltage, to measure
     ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
       
#endif
     delay(50);  // Let mux settle a little to get a more stable A/D conversion
        // Start a conversion  
     ADCSRA |= _BV( ADSC );
        // Wait for it to complete
     while( ( (ADCSRA & (1<<ADSC)) != 0 ) );
        // Scale the value
     int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L; // calculates for straight line value 
     return results;
   }

You still have a delay of 50 millis on your battery function. I also think the way you are keeping trak of time is incorrect. it looks like you are adding a tenth of a second each time through the loop.... you should keep track of time using millis() instead.

Thanks for your reply,
Would this method be fine for keeping the track of time http://playground.arduino.cc/Code/Stopwatch ?

I'm not that keen to buy further into this, but you have linked to a stopwatch that presumably works, so you might try fitting your stuff in the count window. This is what I was thinking about and when I was talking about interrupts I was wrong.

   if ( (millis() - previousMillis >1000 ) 
   {
         previousMillis = millis();                         
         do your stuff         
   }

I see that guy is worse than you are when it comes to blank lines........

Yay, i could make it work. It's just the bare minimum, but its accurate!!!!! I just have to make the buttons work like it was Start/Pause and Reset.

Many many thanks for the support!!!

unsigned long start, finished, elapsed;
void setup()
{
bla bla
}
void loop()
{
if (digitalRead(5)==LOW)
  {
    start = millis(); // saves start time to calculate the elapsed time
    }
  else
    {
    finished = millis(); // saves stop time to calculate the elapsed time

  float h, m, s, ms;
  unsigned long over;
  
  elapsed = finished - start;
  
  h = int(elapsed / 3600000);
  over = elapsed % 3600000;
  m    = int(over / 60000);
  over = over % 60000;
  s    = int(over / 1000);
  ms   = over % 1000;

  myGLCD.setFont(MediumNumbers);
  myGLCD.printNumI(h,0,32, 2, ' 0' );
  myGLCD.printNumI(m,30,32, 2, ' 0' );
  myGLCD.printNumI(s,60,32, 2 ,' 0' );
  myGLCD.update();

Interesting, i was testing the stopwatch and found difference about 6secs in 1 hour. I started the stopwatch on my cellphone almost the same time when i started the stopwatch on arduino.
Is this could be because of the millis() or this is normal behaviour in arduino?

I think 6sec/hour is a bit rough, but that should be OK for a photo process timer. Assuming your code is kosher, don't expect too much accuracy from this as you are just using the on-board clock and there is nothing special about it. As I understand it, the main problem is absence of temperature compensation. You can improve things dramatically by using a realtime clock module. Ironically, the favourite module, the DS1307, isn't temp compensated either, but it is better than what you have. The DS3231 does have temp compensation, and only costs about $2-50.

I imagine you intend to get serious about this eventually, and using an RTC like the DS3231 will then probably become part of the agenda. You may then find that the Time and TimeAlarms libraries might cut through a lot of the chaff you are currently cultivating. You can then just press a single button labelled "Ektachrome" and Arduino does everything - including using the DS18B20s to calculate temperature compensation.

I think 6secs difference in 1 hour is fine for me. Maybe later i will try to use the RTC module you said. But usually the developing times are between 10mins and 20 mins. So thats absolutely fine for me.

I just got stuck now with integrate the Start/Pause function using one button as a toggle switch.

balazsbondar:
I just got stuck now with integrate the Start/Pause function using one button as a toggle switch.

I'm not really into this and you might be better off starting a new thread. I guess you need an 0/1 flag to go with the button pushes so that the programmes knows where to go on the next push (not forgetting to reset the flag then).

Right, thanks a lot. I'm gonna start a new thread about it. I could make only the Start work...