Martin's corner on the web

Power saving techniques for the Attiny84 powered TinySensor

I have been working hard to cut power consumption on the TinySensors so that they can run on the CR2032 battery that is on the back of the PCB. I have one of the sensors is sending room temperature for two months now, powered by the CR2032 only and few more with AA two batteries. The problem with the CR203 battery is its low capacity, only 220mAh typically, compared to up to 2*2500 for two AA sized batteries. Still the CR2032 is appealing because of the small size, the sensor powered this way fits in a small neat box.

So my effort to save power started with picking the appropriate ATtiny84 version. The version of the Attiny84 will work down to 1.8V at 4Mhz, check page 175 in the datasheet. This is good because 4Mhz is the lowest speed at which the RFM12b will operate as well. The RFM12b can work down to 2.2V when you initialize it appropriately:

  // Adjust low battery voltage to 2.2V
  rf12_control(0xC040);

The BOD schematic does consume precious power so worth disabling it wither thru fuses or in the software:

#define BODS 7 //BOD Sleep bit in MCUCR
#define BODSE 2 //BOD Sleep enable bit in MCUCR
MCUCR |= _BV(BODS) | _BV(BODSE); //turn off the brown-out detector

There is also a relationship between power consumption and the speed at which the ATTiny runs. The 4Mhz is the minimum for the SPI to operate, but that doesn’t mean we can’t run on less when we are not using the RFM12b module. With the ATTiny running off its internal oscillator and not an external crystal, we can change the speed on the fly with some simple code that sets the prescaler value:

static void setPrescaler (uint8_t mode) {
    cli();
    CLKPR = bit(CLKPCE);
    CLKPR = mode;
sei();
}
usage example:
   setPrescaler(0); //  8 MHz
  //setPrescaler(1); // div 1, i.e. 4 MHz
  //setPrescaler(15); // div 8, i.e. 1 MHz div 256 (15) = 32kHz
  //setPrescaler(3); // div 8, i.e. 1 MHz

So that means while checking temperature sensors and doing non-RFM12B stuff we can run at 1Mhz (or less) and speedup for RF transmissions.

The next trick that I used is to power off the unused ATTiny hardware.

I don’t need timer 1 so I power it down:

PRR = bit(PRTIM1); // only keep timer 0 going

I enable the ADC only when I need it:

 bitClear(PRR, PRADC); // power up the ADC
 ADCSRA |= bit(ADEN); // enable the ADC

..and disable it right after:

ADCSRA &= ~ bit(ADEN); // disable the ADC
bitSet(PRR, PRADC); // power down the ADC

Power the USI only when we need the RFM12b:

bitClear(PRR, PRUSI); // enable USI h/w

..and disable it when I’m done:

bitSet(PRR, PRUSI); // disable USI h/w

I also use one of the pins of the Attiny to provide power to the attached sensor (DS18B20 or TMP36), thus ensuring I can power it up in the software by setting the pin to high and later to low when I am done. These two sensors are not ideal for low-voltage operations though as they operate at a higher voltage and will start failing at around 2.8V (although DS18b20’s datasheet states 3V low voltage). Thus you may consider different sensor, for example the MCP9700A that works down to 2.3V

I have found many sources stating that most power saving is achieved if you put pins to input and internal pull-ups enabled while sleeping, so I do that.

Also, consider the minimal data structure you want to send. A float value takes a lot of bytes to send (thus eats more battery), while a simple trick may allow you to use an int (2 bytes). For humidity and temperature readings, I multiply the reading by 100 so 23.15 (float) degrees becomes 2315 (int). The receiving end needs to multiply by 0.01 to get the original value.

Another thing is to plan how frequently you need to send. The RF module is a power hog, so you may want to plan the minimum possible send frequency. I send room temperatures every 15 minutes. You may also wake up only to read and average temperatures more frequently, but send out the averaged reading only once every 15 mins.

Here is a sample sketch that I run on one of my room sensors that utilizes the above tricks.

Comments are welcome

9 thoughts on “Power saving techniques for the Attiny84 powered TinySensor

  1. Pingback: CR2032 powered “current only” remote monitoring | Martin's corner on the web

  2. jwestra

    Nice work. Do you keep the concept “Sprint to finish” in mind when lowering the frequency to 1Mhz?

    1. admin Post author

      I am not sure what you mean by that term? I only flip between 1 and 4Mhz. The effect on battery saving is measurable.

  3. jwestra

    I means that sometimes it is more efficient to keep the it at the higher clock rate so it can finish it’s calculation sooner (and go to sleep) and therefore in total consumes less than when running slower.

    1. admin Post author

      I would love someone with oscilloscope to confirm these. I think it depends on the kind of operations you perform, simple ADCs can be ran at lower frequency.

  4. Jasper Truter

    Hi Martin,
    Long time since we corresponded! I am making progress here using the Pi and homebrew Chantrell Tiny TX boards.
    Been gathering temp data for some months already.
    I now made another monitor board and want to use some of your power saving tricks in the Tx_Tiny.ino. I am not sure exactly which version I am using from where but I have compared and updated the Chantrell version to read like your github version for the ATtiny84V.

    I added the PRUSI setting and disabling in the “rfwrite” procedure.
    bitClear(PRR, PRUSI); // enable USI h/w
    rf12_sleep(-1); //wake up RF module.
    ….
    ….
    ….
    bitSet(PRR, PRUSI); // disable USI h/w

    But I get an undefined error on the PRUSI. All the includes are as you defined it. I am using Arduino-1.0.1 with the corrections for “pins_arduino.h”. I dont get an error on MUX5 (used in the Voltage reading section) though when building Tx_Tiny.ino
    Can you point to any possible cause for this error?
    I have tried to build the sample .ino that you have mentioned above. Both PRUSI and MUX5 are reported as undefined.

    Kudos to you : You are VERY industrious on your end ; so much so that I can’t keep up to read all your updates in the blog, hehehe!
    But doing a super job , I really enjoy to follow your progress. And learning a hang of a lot, thanks for that.

    Thanks in advance,
    Best regards,
    Jasper (or Nico)

    1. Martin Post author

      Hi,
      try this:

      #define PRUSI 1

      and it should do the trick. However, it is odd why you don’t have it defined in the core itself, are you sure you have set up the IDE correctly for Attiny84s?

  5. Jasper Truter

    Hi Martin,
    Thanks for your reply, your comment helped me solve the error.
    I used the wrong selected target board in my IDE (UNO vs ATtiny84 at 8Mhz).
    This is what happens if one dont use the IDE everyday at my age! . Sorry for wasting your valuable time.

    Once correct board selected, all is well and the TinyTX´s are sampling and transmitting to RasPi.
    Presently monitoring temperatures from my A++ Fridge/Freezer. Two RFM12B´s operates well from within the hull of the apparatus to the RasPi about 10 meter away behind two brick walls.
    Next step is to connect a TxTiny to the Solar Water heater and Storage tank outside on the roof of as single story house and integrate in EMONCMS.
    Enjoy your summer, here winter is slowly approaching during nightime.

    best regards, thanks again for all articles and advice,
    Jasper