Interrupt and sleep mode problem

Hello forum,

I have a problem with one of my projects.
Long story short, I want to have a standalone atmega328p (8MHz internal clock) powered by batteries (2x 1.5V AA) that - if I push a button - awakes from power down sleep mode, queries a DS182S20 temperature sensor and shows me the temperature on a 4 digit 7segment LED for 3 seconds.

First of all, it works.
BUT. I have some problems:

  1. if I push the button, the LED lights up and shows me the temperature of the last time I pushed the button. Then it turns of, only to light up again and show me the temperature it reads now.
  2. If I measure the power drawn using a multimeter, directly after powering on the atmega328p I get around 3-4mA for about 1-2s and then it falls down to 0.19mA. I think this is the sleep mode.
    When I push the button it wakes up, draws around 30-40mA during the LEDs are on and then falls back to 0.8mA and stays there. Why is it not falling down to 0.19mA again?
    Removing the DS18S20 during this 0.8mA time changes nothing.

I would be happy if someone could point me in the right direction!
Here is my code. (I am a beginner, so it might be a bit clumsy, sorry.)

Thanks alot!
Stefan

#include <OneWire.h>
#include <DallasTemperature.h>
#include <avr/sleep.h>


//7segment 
byte seven_seg_digits[12][7] = {                           { 1,1,1,1,1,1,0 },  // = 0
                                                           { 0,1,1,0,0,0,0 },  // = 1
                                                           { 1,1,0,1,1,0,1 },  // = 2
                                                           { 1,1,1,1,0,0,1 },  // = 3
                                                           { 0,1,1,0,0,1,1 },  // = 4
                                                           { 1,0,1,1,0,1,1 },  // = 5
                                                           { 1,0,1,1,1,1,1 },  // = 6
                                                           { 1,1,1,0,0,0,0 },  // = 7
                                                           { 1,1,1,1,1,1,1 },  // = 8
                                                           { 1,1,1,0,0,1,1 },  // = 9
                                                           { 1,1,0,0,0,1,1 },  // = ° 
                                                           { 0,0,0,0,0,0,0 }   //all off
                                                           };

//interrupt button
int buttonPin = 2;                                                          

//ds18s20 on pin12
OneWire  ds(12);
DallasTemperature sensors(&ds);
byte addr[8];
volatile float celsius_ds;
 
 
void setup() {  
  //find ds18s20 address
  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(50);
    return;
  }
  //reset ds18s20
  ds.reset();
  sensors.begin();
  //set ds18s20 resolution
  sensors.setResolution(addr, 8);
 
  //interrupt button
  pinMode(buttonPin, INPUT);
 
  //use analog input as outputs
  pinMode(14, OUTPUT);  
  pinMode(15, OUTPUT);
  pinMode(16, OUTPUT);
  pinMode(17, OUTPUT);
  
  //7segment connections
  pinMode(3, OUTPUT);//seg a
  pinMode(4, OUTPUT);//seg b
  pinMode(5, OUTPUT);//seg c
  pinMode(6, OUTPUT); //seg d
  pinMode(7, OUTPUT);//seg e
  pinMode(8, OUTPUT);//seg f
  pinMode(9, OUTPUT);//seg g
  pinMode(10, OUTPUT);//dot
  
}

//sleep function for powersave
void sleepNow()
{
   //maximum power save
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);  
   
   // enables the sleep bit in the mcucr register
   sleep_enable();
   
   //use interrupt 0 at pin2
   attachInterrupt(0,wakeUpNow, HIGH);
   
   //enter sleep mode
   sleep_mode();

   //sleep mode end, prog continues from here after ISR ran
   //disable sleep mode
   sleep_disable();

   //detach interrup                   
    detachInterrupt(0);
}

void wakeUpNow() {
  //detach interrupt to prevent bouncing of the interrupt button
  detachInterrupt(0);
  
  //get temperatur from ds18s20
  sensors.requestTemperatures();
  celsius_ds=sensors.getTempC(addr);  
  //give it 100ms time 
  delay(100);
  
  int zehner=0;
  int einer=0;
  int zehntel=0;
  int hundertstel=0;
  
  //show temperature on 7segment for ca. 3s
  int show=3;
 
  int tmp=0;
  tmp=int(celsius_ds*100);
  zehner=tmp/1000;
  einer=(tmp%1000)/100;
  zehntel=(tmp%100)/10;
  hundertstel=(tmp%100)%10;
  if (hundertstel > 5) ++zehntel;

  //call procedure that shows the actual temperature
  showTemp(zehner,einer,zehntel,show);
  
  //ISR ends, re-attach interrupt
  attachInterrupt(0,wakeUpNow, HIGH);
}


//procedure that writes digit on 7segment
void sevenSegWrite(byte digit) {
  byte pin = 3;
  for (byte segCount = 0; segCount < 7; ++segCount) {
    digitalWrite(pin, seven_seg_digits[digit][segCount]);
    ++pin;
  }
}

//procedure that shows the actual temperature
void showTemp(int zehner,int einer,int zehntel,int show)
{
    int has_shown=0;
    
    //show in ms
    show=show*1000;
    while (has_shown < show) {
      
      //show decimal dot
      digitalWrite(10,1);
      //select digit 0 on ULN2003AN 1,0,0,0
      digitalWrite(14,1);
      digitalWrite(15,0);
      digitalWrite(16,0);
      digitalWrite(17,0);
      //show 10^1 digit
      sevenSegWrite(zehner);
      delay(2); 
  
      //select digit 1 on ULN2003AN 0,1,0,0
      digitalWrite(14,0);
      digitalWrite(15,1);
      digitalWrite(16,0);
      digitalWrite(17,0);
      //show 10^0 digit
      sevenSegWrite(einer);
      delay(2); 
  
      //select digit 2 on ULN2003AN 0,0,1,0
      digitalWrite(14,0);
      digitalWrite(15,0);
      digitalWrite(16,1);
      digitalWrite(17,0);
      //show 10^-1 digit
      sevenSegWrite(zehntel);
      delay(2);
   
      //select digit 3 on ULN2003AN 0,0,0,1
      digitalWrite(14,0);
      digitalWrite(15,0);
      digitalWrite(16,0);
      digitalWrite(17,1);
      //show °
      sevenSegWrite(10);
      delay(2);
      
      //took about 10ms to show the full temperature
      has_shown+=10;
   }
   //clear display and dot
   sevenSegWrite(11);
   digitalWrite(10,0);

}


void loop() {
  //go to sleep
  sleepNow();
}

I have read about the different currents before on this forum. I think there can be a few different causes.

Most of us use 3 AA batteries.

Could you re-organize the sketch ?
Place (almost all) code from the interrupt in the loop.
The interrupt routine could perhaps be empty.

At the end of the loop(), go into sleep mode. The interrupt wakes the ATmega, and the loop() is run again, until it goes to sleep at the end of loop().

After that, you might have to look for timing for Serial and EEPROM that need some time before going to sleep. You don't use those, so that was easy.

Next step is to use a dummy for the temperature to check if the OneWire or DallasTemperature library influences the sleep mode.
celsius_ds = 10.2;

Nick Gammon wrote a very nice page about sleep mode : Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors

delay(100);

Don’t use delay() in an ISR
http://www.gammon.com.au/forum/?id=11488

And “Keep Them Short.”

thanks for your replies, the links where very helpful!

I followed your recommendations and did two things:

  1. moved all of the ISR function to loop(), right before call of sleepNow();
    → that solved the twice shown temperature and the old temperature to be shown

  2. removed the complete OneWire and DallasTemperature libraries and added a dummy value to celsius_ds variable.
    → sadly, the power consumption remained as before 0.79mA

  3. I then removed the ULN2003A for testing purposes
    → power consumption dropped to 0.25mA/250µA, a lot less1

Next,I wondered why the ULN2003A draws that much power while doing nothing. It is simply a darlington array of transitors, which I use to select the correct 7segment digit cathode (the 4digit 7segment display uses common anode for all segments).
Then I noticed, at the end of showTemp() the outputs that controll the cathodes are left in the last state! I added coded to turn them all of and now the power consumption at sleep mode is 230µA:

That value still seems quite high, but I think it might be my cheapskate multimeter that does not show the correct values if trying to measure so tiny currents…

FYI, here is the code I use right now.
Thanks for your help!

#include <OneWire.h>
#include <DallasTemperature.h>
#include <avr/sleep.h>


//7segment 
byte seven_seg_digits[12][7] = {                           { 1,1,1,1,1,1,0 },  // = 0
                                                           { 0,1,1,0,0,0,0 },  // = 1
                                                           { 1,1,0,1,1,0,1 },  // = 2
                                                           { 1,1,1,1,0,0,1 },  // = 3
                                                           { 0,1,1,0,0,1,1 },  // = 4
                                                           { 1,0,1,1,0,1,1 },  // = 5
                                                           { 1,0,1,1,1,1,1 },  // = 6
                                                           { 1,1,1,0,0,0,0 },  // = 7
                                                           { 1,1,1,1,1,1,1 },  // = 8
                                                           { 1,1,1,0,0,1,1 },  // = 9
                                                           { 1,1,0,0,0,1,1 },  // = ° 
                                                           { 0,0,0,0,0,0,0 }   //all off
                                                           };

//interrupt button
int buttonPin = 2;                                                          

//ds18s20 on pin12
OneWire  ds(12);
DallasTemperature sensors(&ds);
byte addr[8];
volatile float celsius_ds;
 
 
void setup() {  
  
  //find ds18s20 address
  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(50);
    return;
  }
  //reset ds18s20
  ds.reset();
  sensors.begin();
  //set ds18s20 resolution
  sensors.setResolution(addr, 8);
 
  //interrupt button
  pinMode(buttonPin, INPUT);
 
  //use analog input as outputs
  pinMode(14, OUTPUT);  
  pinMode(15, OUTPUT);
  pinMode(16, OUTPUT);
  pinMode(17, OUTPUT);
  
  //7segment connections
  pinMode(3, OUTPUT);//seg a
  pinMode(4, OUTPUT);//seg b
  pinMode(5, OUTPUT);//seg c
  pinMode(6, OUTPUT); //seg d
  pinMode(7, OUTPUT);//seg e
  pinMode(8, OUTPUT);//seg f
  pinMode(9, OUTPUT);//seg g
  pinMode(10, OUTPUT);//dot
  
}

//sleep function for powersave
void sleepNow()
{
  
  
   //maximum power save
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);  
   
   // enables the sleep bit in the mcucr register
   sleep_enable();
   
   //use interrupt 0 at pin2
   attachInterrupt(0,wakeUpNow, HIGH);

   // disable ADC
   ADCSRA = 0;  
   
   // turn off brown-out enable in software
   MCUCR = _BV (BODS) | _BV (BODSE);  // turn on brown-out enable select
   MCUCR = _BV (BODS);        // this must be done within 4 clock cycles of above
  
   //enter sleep mode
   sleep_mode();

   //sleep mode end, prog continues from here after ISR ran
   //disable sleep mode
   sleep_disable();

   //detach interrup                   
    detachInterrupt(0);
}

void wakeUpNow() {
 
}


//procedure that writes digit on 7segment
void sevenSegWrite(byte digit) {
  byte pin = 3;
  for (byte segCount = 0; segCount < 7; ++segCount) {
    digitalWrite(pin, seven_seg_digits[digit][segCount]);
    ++pin;
  }
}

//procedure that shows the actual temperature
void showTemp(int zehner,int einer,int zehntel,int show)
{
    int has_shown=0;
    
    //show in ms
    show=show*1000;
    while (has_shown < show) {
      
      //show decimal dot
      digitalWrite(10,1);
      //select digit 0 on ULN2003AN 1,0,0,0
      digitalWrite(14,1);
      digitalWrite(15,0);
      digitalWrite(16,0);
      digitalWrite(17,0);
      //show 10^1 digit
      sevenSegWrite(zehner);
      delay(2); 
  
      //select digit 1 on ULN2003AN 0,1,0,0
      digitalWrite(14,0);
      digitalWrite(15,1);
      digitalWrite(16,0);
      digitalWrite(17,0);
      //show 10^0 digit
      sevenSegWrite(einer);
      delay(2); 
  
      //select digit 2 on ULN2003AN 0,0,1,0
      digitalWrite(14,0);
      digitalWrite(15,0);
      digitalWrite(16,1);
      digitalWrite(17,0);
      //show 10^-1 digit
      sevenSegWrite(zehntel);
      delay(2);
   
      //select digit 3 on ULN2003AN 0,0,0,1
      digitalWrite(14,0);
      digitalWrite(15,0);
      digitalWrite(16,0);
      digitalWrite(17,1);
      //show °
      sevenSegWrite(10);
      delay(2);
      
      //took about 10ms to show the full temperature
      has_shown+=10;
   }
   //clear display and dot
   sevenSegWrite(11);
   digitalWrite(10,0);
   
   //turn off all outputs on the ULN2003A
   digitalWrite(14,0);
   digitalWrite(15,0);
   digitalWrite(16,0);
   digitalWrite(17,0);
}


void loop() {
 //detach interrupt to prevent bouncing of the interrupt button
  detachInterrupt(0);
  
  //get temperatur from ds18s20
  sensors.requestTemperatures();
  celsius_ds=sensors.getTempC(addr);  
  //give it 100ms time 
  delay(100);
  
  int zehner=0;
  int einer=0;
  int zehntel=0;
  int hundertstel=0;
  
  //show temperature on 7segment for ca. 3s
  int show=3;
 
  int tmp=0;
  tmp=int(celsius_ds*100);
  zehner=tmp/1000;
  einer=(tmp%1000)/100;
  zehntel=(tmp%100)/10;
  hundertstel=(tmp%100)%10;
  if (hundertstel > 5) ++zehntel;

  //call procedure that shows the actual temperature
  showTemp(zehner,einer,zehntel,show);
  
  //re-attach interrupt
  attachInterrupt(0,wakeUpNow, HIGH);  
  
  //go to sleep
  sleepNow();
}

This link should give you some ideas about saving power. This was a battery-powered temperature sensor and clock.

thanks for the link, I will read it asap.

One problem shows still up, even if I declared it solved in my previous posting.
Each button press brings me the temperature of the last reading.
I left the circuit alone for a while and pressed the button, it showed me 19.4°C. Then I pressed again and it fell down to 18.3°C
I think something with the variable might have to be declared different.
Maybe it is the "volatile" celsius_ds, will check this this evening.

The celsius_ds doesn't have to be volatile anymore, and also not global. You can declare it in the loop().

I don't know why you are reading the previous temperature. Perhaps it has to do with the DallasTemperature library.

yes, I already moved it into loop(). same result.
To see what happens, I tried to read temperature several times and do the degree celsius calculations twice, it all does not change, that the ds18s20 seems to deliver old values after waking from sleep.
weird.

I think I will try a thermistor and see what happens.
thanks for your help so far!

I think there is a large delay (like 1 second) needed after power up and after starting the conversion. I don't know how that is done in the DallasTemperature library.
http://playground.arduino.cc/Learning/OneWire