Go Down

Topic: Preserve battery with Sleep ? (Read 8912 times) previous topic - next topic

pekkaa

Quote
And I have no idea how much openlog (http://www.sparkfun.com/products/9530) consume while idling.


Read the page you linked:

Quote
2mA idle, 6mA at maximum recording rate

pekkaa

Quote
You absolutely need to add the resistors


I'll take that back. I looked at the picture on the Sparfun's webpage again and noticed the leds on the pcb already seem to have the resistors in front of them. However, this leads to an other problem: If the resistors are rated for 5V, then the led's wont be very bright if you power them from 3.3V.


nickgammon


Next question will be do I need a watchdog or just sleep function is enough?


It depends what you are trying to achieve. Once asleep, either an interrupt (a LOW interrupt) or the watchdog (and various other things, read the data sheet) will wake it up.

If you just want it awake when you press the button, the interrupt will do. If you want it to take a reading every 5 minutes, you need the watchdog. If you want both, you need both.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Yeast

I already changed the circuit to LOW on button press and attach interrupt as attachInterrupt(1, wakeUpNow, LOW);
And when ideal time exceed 8 seconds it called upon sleepNow() function which is almost identical to the example.
The battery still only run for a day. Did I forget something ??

Code: [Select]

void sleepNow()         
{
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   
    sleep_enable();
    attachInterrupt(1,wakeUpNow, LOW);
    sleep_mode();     
    sleep_disable();   
    detachInterrupt(1);   
}


**I haven't check the mA yet, I need to find a multimeter first.

nickgammon

I think we need to see more of your code - like all of it.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Yeast

Code: [Select]

#include <i2cmaster.h>
#include <avr/sleep.h>
#include <NewSoftSerial.h>

//data logger TX/RX pins
NewSoftSerial logger(7,8);
boolean enable_log = true;
//boolean enable_log = false;

int ledPins[] = {10,9,4,11,12,4};

//sleep function()
boolean sleep_enable = true;
//boolean sleep_enable = false;
int inactive_counter;
int softButtonPin = 3;

//temperature-related variable
float r_temperature;
float avg_temperature;
int sample_rate = 250;

//stability
int dcheck = 15;

//application variables
int current_position;
double reading_a1;
double reading_a2;
double reading_b1;
double reading_b2;
boolean debug_mode = true;
boolean is_sleeping;
//boolean debug_mode = false;

//buzzer
boolean soundon = true;
//boolean soundon = false;
int buzzerPin = 5;
int noteFreqArr[] = {
49.4, 52.3, 55.4, 58.7, 62.2, 65.9, 69.9, 74, 78.4, 83.1, 88, 93.2,
98.8, 105, 111, 117, 124, 132, 140, 148, 157, 166, 176, 186,
198, 209, 222, 235, 249, 264, 279, 296, 314, 332, 352, 373,
395, 419, 444, 470, 498, 527, 559, 592, 627, 665, 704, 746,
790, 837, 887, 940, 996, 1050, 1110, 1180, 1250, 1320, 1400, 1490,
1580, 1670, 1770, 1870, 1990, 2100};

void setup()
{
   
 
 current_position=0;
 
 //IR Sensor
 i2c_init(); //Initialise the i2c bus
 PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
   
 if(soundon)
   pinMode(buzzerPin, OUTPUT);
 
 is_sleeping = false;
 inactive_counter=0;
 pinMode(softButtonPin, INPUT);
 attachInterrupt(1, wakeUpNow, LOW);
 
 for(int i=0; i<6; i++)
 {  
   pinMode(ledPins[i], OUTPUT);
   analogWrite(ledPins[i], 0);
 }
 
 Serial.begin(9600);
 
 if(enable_log)
 {
   logger.begin(9600);
   logger.println("Time,Result,A1,A2,B1,B2,Difference");
 }
}

void loop()
{    
 if(current_position%2==0 && dcheck_high())
 {
   inactive_counter =0;
   switch(current_position)
   {
      case 0: if(debug_mode)
                Serial.print("Check 1.1: ");
              nextStep();
              break;
      case 2: if(debug_mode)
                Serial.print("Check 1.2: ");
              nextStep();
              break;
      case 4: if(debug_mode)
                Serial.print("Check 2.1:");
              nextStep();
              break;
      case 6: if(debug_mode)
                Serial.print("Check 2.2:");
              nextStep();
              break;
   }
   while(dcheck_high()){}
   delay(100);
 }
 else if(current_position%2==1 && dcheck_low())
 {
   inactive_counter =0;
   if(current_position<6)
   {
     nextStep();
     if(debug_mode)
        Serial.println("Released, change side");
      delay(100);
   }
 }else
 {  
    if(sleep_enable)
    {
      inactive_counter++;
      delay(50);
     
      if(inactive_counter > 150)
      {
        if(debug_mode){Serial.println("Enter Sleep Mode"); delay(100);}
        is_sleeping = true;
        LED_clear();
        sleepNow();
      }
    }
 }
}

void nextStep()
{
 if(sleep_enable)
     inactive_counter=0;
 current_position++;
 action(current_position);
}

void action(int current_pos)
{
 switch(current_pos)
 {
   case 0: LED_clear();
           break;
   case 1: LED_clear();
           reading_a1 = take_measurement(1);
          if(debug_mode)
             Serial.println(reading_a1);
          LED_1stcompleted(false);
          if(soundon)
                playsound();
         
          break;  
   case 3: LED_clear();
           reading_a2 = take_measurement(1);
          if(debug_mode)
             Serial.println(reading_a2);
          LED_1stcompleted(true);
          if(soundon)
          {
              playsound();
              delay(200);
              playswitch();
          }
           break;          
   case 5: //LED_1stcompleted();
           reading_b1 = take_measurement(2);
           if(debug_mode)
             Serial.println(reading_b1);
           LED_2ndcompleted(false);
            if(soundon)
             playsound();
           break;
   case 7: //LED_1stcompleted();
           reading_b2 = take_measurement(2);
           if(debug_mode)
             Serial.println(reading_b2);
           LED_2ndcompleted(true);
            if(soundon)
             playsound();
          delay(100);
          action(8);
          break;
   
   case 8: LED_clear();
           playcompleted();
           if(is_okie()){LED_OK();}
           else
           {
             if(((reading_a1+reading_a2)/2) > ((reading_b1+reading_b2)/2))
               LED_NOT(1);
             else if(((reading_b1+reading_b2)/2)>((reading_a1+reading_a2)/2))
               LED_NOT(2);          
           }
           if(enable_log)
             logging();
           delay(1000);
           reset_application();
           break;
   default:break;
 }
}

Yeast

Code: [Select]

void logging()
{
 unsigned long m = millis();
 m = m/1000;
 
 logger.print(m);
 logger.print(",");
 if(is_okie())
   logger.print("OKIE,");
 else
   logger.print("NOT OKIE,");
 logger.print(reading_a1);
 logger.print(",");
 logger.print(reading_a2);
 logger.print(",");
 logger.print(reading_b1);
 logger.print(",");
 logger.print(reading_b2);  
 logger.print(",");
 logger.println(((reading_a1+reading_a2)/2) - ((reading_b1+reading_b2)/2));
}

boolean dcheck_high()
{
 int check = dcheck;
 while(check>0)
 {
   check--;
   if(digitalRead(softButtonPin)==HIGH)
     return false;
 }
 return true;
}

boolean dcheck_low()
{
 int check = dcheck;
 
 while(check>0)
 {
   check--;
   if(digitalRead(softButtonPin)==LOW)
     return false;
 }
 return true;
}

double read_temperature()
{
 int dev = 0x5A<<1;
 int data_low = 0;
 int data_high = 0;
 int pec = 0;
 
 i2c_start_wait(dev+I2C_WRITE);
 i2c_write(0x07);
 
 // read
 i2c_rep_start(dev+I2C_READ);
 data_low = i2c_readAck(); //Read 1 byte and then send ack
 data_high = i2c_readAck(); //Read 1 byte and then send ack
 pec = i2c_readNak();
 i2c_stop();
 
 //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
 double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
 double tempData = 0x0000; // zero out the data
 int frac; // data past the decimal point
 
 // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
 tempData = (double)(((data_high & 0x007F) << 8) + data_low);
 tempData = (tempData * tempFactor)-0.01;
 
 return tempData - 273.15;
}

double take_measurement(int index)
{
 avg_temperature = 0.0;
 
 for(int i=0;i<3;i++)
 {
   LED_inprogress(index);
   delay(100);
 }
 
 for(int i=0;i<sample_rate;i++){
   if(i%25==0) {LED_inprogress(index);}
   r_temperature = read_temperature();
   avg_temperature += r_temperature;    
 }
 return avg_temperature/sample_rate;
}

boolean is_okie()
{
 float temperature_diff = abs(((reading_a1+reading_a2)/2) - ((reading_b1+reading_b2)/2));
 if(debug_mode)
   Serial.println(temperature_diff);
 return (temperature_diff < 2.0);
}

void LED_clear()
{
 setcolor(1,0,0,0);
 setcolor(2,0,0,0);
}

void LED_1stcompleted(boolean both)
{
 if(both)
   setcolor(1,0,255,0);
 else
   setcolor(1,255,255,0);
 setcolor(2,0,0,0);
}

void LED_2ndcompleted(boolean both)
{
 setcolor(1,0,255,0);
 if(both)
   setcolor(2,0,255,0);
 else
   setcolor(2,255,255,0);
}

void LED_OK()
{
 setcolor(1,0,255,0);
 setcolor(2,0,255,0);
 delay(2000);
 setcolor(1,0,0,0);
 setcolor(2,0,0,0);
}

void LED_NOT(int index)
{
 switch(index)
 {
   case 1: setcolor(1,255,0,0);
           setcolor(2,0,255,0);
           break;
   case 2: setcolor(1,0,255,0);
           setcolor(2,255,0,0);
           break;
 }
 delay(2000);
}

void LED_inprogress(int index)
{
 setcolor(index,200,200,0);
 delay(100);
 setcolor(index,0,0,0);
 delay(100);  
}

void setcolor (int ledset, unsigned char red, unsigned char green, unsigned char blue)    
{
         ledset = (ledset-1)*3;
         analogWrite(ledPins[ledset], red);          
         analogWrite(ledPins[ledset+1], green);
         analogWrite(ledPins[ledset+2], blue);
}

//Buzzer related function()
void playNote(int noteInt, long length, long breath = 100) {
   length = length - breath;
   buzz(buzzerPin, noteFreqArr[noteInt], length);
   if(breath > 0) { //take a short pause or 'breath' if specified
       delay(breath);
   }
}

void playcompleted()
{
   playNote(51,200);
   playNote(52,200);
   playNote(53,200);
   playNote(54,200);    
   playNote(55,200);
}

void playswitch()
{
 playNote(50,200);
 playNote(53,200);
 playNote(55,200);
}

void playsound()
{
    playNote(50,200);
    playNote(60,150);
    playNote(50,200);
}

void buzz(int targetPin, long frequency, long length) {
   long delayValue = 1000000/frequency/2;
   long numCycles = frequency * length/ 1000;
   for (long i=0; i < numCycles; i++){
       digitalWrite(targetPin,HIGH);
       delayMicroseconds(delayValue);
       digitalWrite(targetPin,LOW);
       delayMicroseconds(delayValue);
   }
}

void reset_application()
{
//  if(debug_mode)
//      Serial.print("reset_application");
 delay(100);
 inactive_counter=0;
 current_position = 0;
 LED_clear();  
}

//sleep related funtion()
void wakeUpNow()        
{
 if(is_sleeping)
 {
   Serial.println("is up");
   is_sleeping = false;
   reset_application();
 }
}

void sleepNow()        
{   set_sleep_mode(SLEEP_MODE_PWR_DOWN);  
   sleep_enable();        
   attachInterrupt(1,wakeUpNow, LOW);
   sleep_mode();            
   sleep_disable();        
   detachInterrupt(1);    
}

48X24X48X

In PWR_DOWN mode, there's 3 things that *can* be on: WDT, ADC, BOD. The ADC consumes about 90-100 uA. BOD consumes about 17 uA. And WDT consumes about 4 uA. I believe there's something else is consuming rather than these 3 possible factor as if you add those 3 up, it would still let you run more than a day.

nickgammon

#23
Oct 31, 2011, 05:04 am Last Edit: Nov 01, 2011, 12:37 am by Nick Gammon Reason: 1
Well I can't get that to compile - various multiply defined symbols relating to the I2C library. Anyway I don't understand this:

Code: [Select]
void reset_application()
{
//  if(debug_mode)
//      Serial.print("reset_application");
 delay(100);
 inactive_counter=0;
 current_position = 0;
 LED_clear();  
}

//sleep related funtion()
void wakeUpNow()        
{
 if(is_sleeping)
 {
   Serial.println("is up");
   is_sleeping = false;
   reset_application();
 }
}

void sleepNow()        
{   set_sleep_mode(SLEEP_MODE_PWR_DOWN);  
   sleep_enable();        
   attachInterrupt(1,wakeUpNow, LOW);
   sleep_mode();            
   sleep_disable();        
}


Now wakeUpNow is an ISR, right? So interrupts are disabled. So when it calls  reset_application then interrupts are disabled. So delay (100) won't work.

Why test is_sleeping? You won't be there unless it is sleeping, will you?

You should detach the interrupt in is_sleeping. Then do the rest once interrupts are active again. eg.

Here:


Code: [Select]
//sleep related funtion()
void wakeUpNow()        
{
   detachInterrupt(1);    
}

void sleepNow()        
{  
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);  
   sleep_enable();        
   attachInterrupt(1,wakeUpNow, LOW);
   sleep_mode();            
   sleep_disable();        
   Serial.println("is up");
   is_sleeping = false;
   reset_application();
}


Here:

Code: [Select]
void setup()
{
   
...

 pinMode(softButtonPin, INPUT);
 attachInterrupt(1, wakeUpNow, LOW);
 


Don't attach that interrupt until you are ready. And in any case I would enable the pull-up resistor.

Also I don't see you turning stuff off before going to sleep. The LED pins are still configured as outputs. You haven't turned off the ADC or anything. To turn off the various internal modules (just before sleeping):

Code: [Select]
// turn off various modules
 PRR = 0b11101111;


(EDIT: changed 0x11101111 to 0b11101111)

After you wake put them back:

Code: [Select]
// stop power reduction
 PRR = 0;

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Yeast

So I need to convert the OUTPUT pins to INPUT before sleep and reassign them after wake up?
I change the code as such...

Code: [Select]

void wakeUpNow()       
{
  detachInterrupt(1);
  // stop power reduction
  PRR = 0;
  pinMode(buzzerPin, OUTPUT);
  for(int i=0; i<6; i++)
  { 
    pinMode(ledPins[i], OUTPUT);
  }
}

void sleepNow()         
{   
    // turn off various modules
    PRR = 0x11101111;
     
    pinMode(buzzerPin, INPUT);
    for(int i=0; i<6; i++)
    { 
      pinMode(ledPins[i], INPUT);
    }
   
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
    sleep_enable();         
    attachInterrupt(1,wakeUpNow, LOW);
    sleep_mode();           
    sleep_disable();         
    reset_application();     
}



I already have a 10K resistor connected to the button pin. Do I still need to enable the internal pull-up resistor?
Did the PRR = 0x11101111; just turn off the internal module of arduino or everything connected to it too?

Did I have everything off now ?? If not how do I switch everything off except the interrupt pin.
(I attach the i2cmaster and newsoftserial together with this post)

Anyway I tried to take the reading for running, sleeping
on Arduino Fio: running 8mA, Running Max at 20mA and sleep at 4mA
on Arduino Pro Mini: running 10mA, Running Max at 20mA and sleep at 6mA

**In previous sketch I forget to detach the interrupt and it keep falling into wakeup() function so I did another check but it is all unnecessary so now I remove it all as suggested.

nickgammon

As I measured here:

http://www.gammon.com.au/forum/?id=11149

When asleep you should be consuming 25 uA or thereabouts. You are getting 4 mA which is about 240 times that much. So something is seriously wrong. I'll try getting the sketch to compile later and see what I measure.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

I'm wondering about your hardware setup when it consumes so much power.

I measured about 270 uA with your original code, which is a lot less than the 4 mA you are getting.

I've just looked at the circuit for the Arduino Pro Mini. As far as I can see, it has a permanent LED connected between Vcc and Gnd via a 10K resistor. That would use close to a 1 mA wouldn't it? I would be trying to disconnect that.

Anyway, I amended your sleepNow to add a couple of things, and got the current consumption down to 75 uA.

In particular:


  • Turned off I2C (you would need to turn it on again afterwards of course)

  • Corrected my bug where PRR should be 0b11101111 not 0x11101111

  • Turned off the ADC converter

  • Enabled the pull-up on the wake-up pin



Code: [Select]
void sleepNow()         
{   

  pinMode(buzzerPin, INPUT);
  for(int i=0; i<6; i++)
  { 
    pinMode(ledPins[i], INPUT);
  }

  // turn off I2C
  TWCR &= ~(_BV(TWEN) | _BV(TWIE) | _BV(TWEA));

  // turn off I2C pull-ups
  digitalWrite (A4, LOW);
  digitalWrite (A5, LOW);

  // disable ADC
  ADCSRA = 0; 

  // turn off various modules
  PRR = 0b11101111;

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
  sleep_enable();         
  digitalWrite (3, HIGH);
  pinMode (3, INPUT);   
  attachInterrupt(1,wakeUpNow, LOW);
  sleep_mode();           
  sleep_disable();         
  reset_application();     
}


Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Yeast

I did have a few changes to the original hardware.
I no longer used the lilypad RGB but use RGB Diffused (http://www.sparkfun.com/products/9264).
I connect R to 180 ohm resistor and G to 100 ohm before linking to respective pins.

Question: Why do I need to have it in the sleepnow() since it is already declare in the setup?
Code: [Select]

digitalWrite (3, HIGH);
pinMode (3, INPUT);


Even with the additional turning off code it still only goes down to 4mA for arduino fio.

Yeast

While the IR sensor uses the exact same setting as


nickgammon

Before addressing those questions, following up from an idea here:

http://jeelabs.org/2009/05/16/power-consumption-more-savings/

I discovered that by programming the fuse to disable brown-out detection it reduced the consumption on my test from about 75 uA to 6 uA, a huge saving.

Quote
Question: Why do I need to have it in the sleepnow() since it is already declare in the setup?


You don't, I had it there because I was experimenting with turning all pins to inputs, and low, then I put that one back.




The diagram you posted doesn't seem to match the Arduino Fio or the Arduino Pro Mini.

If you are using a board with an on-board LED, a USB conversion chip, or a voltage regulator, these are all likely to consume power. I am testing with a "bare bones" board which is basically the processor chip, a couple of decoupling capacitors, and a resonator. This, basically:

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Go Up