Monday 23 January 2012

Wireless sensor node - sleeping (4)

Now that I have RF comms working I can start to consider the other aspects of building wireless sensor nodes. 

The next item on my todo list is to improve the battery life of my wireless sensors. Note: The content of this article was heavily based on the following blog posts:
The first step in this process is to understand the expected battery life before I make any optimisations. The amount of energy stored in batteries is normally measured in milliamp hours (mAh). Therefore, to work out battery life we just need to measure the current used by the circuit. Current is measured by placing a multimeter (e.g. this one) in series with the circuit. This is how I do this on my circuit. 



Using this setup I can see that my circuit uses about 8.5mA (Note: the first picture actually shows a lower current as I took it at the end of this article). To work out the battery life from this number you just divide the available mAh by this reading. Note that using multiple batteries only increases the voltage, it does not increase the mAh and if each battery is different, the overall mAh available is the lowest value. 

My 3 AA batteries are all 1700mAh and I measured a current of 8.5mA. This gives a battery life of 1700/8.5 = 200 hours ~ 8 days. Not bad but not ideal - I don't want a new weekly chore of changing batteries! Especially if I deploy 5+ of these nodes!

Thankfully there is a straightforward code change we can make to dramatically reduce our current usage. The key point is that I don't plan to take readings constantly. I expect I will configure each wireless sensor to take readings no more often than once every 5 minutes. The rest of the time, the code will just sit in a delay() call. However, during this period of inactivity we can actually take advantage of a feature of the ATtiny85 to use much less power. 

The ATtiny85 supports being put into a sleep mode. In this mode, the power usage is MUCH lower and no code is executed. We can configure this sleep to end when either an input pin changes or when a fixed amount of time has passed. The second of these is what I will use. The timeout which wakes us is called the watchdog timer. Full details about the ATtiny including how to use the watchdog timer can be seen in the datasheet.

Without further ado, here is the code which I have added to make use of the watchdog timer. This comes in several parts.

1) Includes and defines:

#include <avr/sleep.h>
#include <avr/wdt.h>

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

2) Prep work. Note that I have excluded a lengthy comment from this quote which is itself a cutdown extract from the datasheet. The interested reader should refer to the ATTiny85 datasheet as the proper reference for what this code is doing. 

// Watchdog timeout values
// 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
// 6=1sec, 7=2sec, 8=4sec, 9=8sec
void setup_watchdog(int ii)

 // The prescale value is held in bits 5,2,1,0
 // This block moves ii itno these bits
 byte bb;
 if (ii > 9 ) ii=9;
 bb=ii & 7;
 if (ii > 7) bb|= (1<<5);
 bb|= (1<<WDCE);
 
 // Reset the watchdog reset flag
 MCUSR &= ~(1<<WDRF);
 // Start timed sequence
 WDTCR |= (1<<WDCE) | (1<<WDE);
 // Set new watchdog timeout value
 WDTCR = bb;
 // Enable interrupts instead of reset
 WDTCR |= _BV(WDIE);
}

void setup()
{  
  ...
  setup_watchdog(8);
}

3) Support methods:

void system_sleep()
{
 cbi(ADCSRA,ADEN); // Switch Analog to Digital converter OFF
 set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode
 sleep_mode(); // System sleeps here
 sbi(ADCSRA,ADEN);  // Switch Analog to Digital converter ON
}

// wait for totalTime ms
// the wait interval is to the nearest 4 seconds
void deepsleep(int waitTime)
{
  // Calculate the delay time
  int waitCounter = 0;
  while (waitCounter != waitTime)
  {
    system_sleep();
    waitCounter++;
  }
}

4) Actually going to sleep - note that deepsleep replaced delay. 

void loop()
{
  Tdata +=1;
  sendMsg(Tdata);
  // deep sleep for 2 * 4 seconds = 8 seconds
  deepsleep(2);
}

The result of all this is that the ATtiny spends most of its time asleep. Using a multimeter I measured the sleep current of my circuit to be 0.006mA. This can be used to calculate the overall battery life by entering the following figures into this website:
  • Battery capacity: 1700mAh
  • Sleep current consumption: 0.006mA
  • Wake current consumption: 8.5mA
  • Duration of wakeup: 2000ms
  • Number of wakeups per hour: 12 (once every 5 minutes)
The result: 2.6 years! Much better! 120x better than the previous battery life which I calculated.

As ever, the full source code is available here.