Close

Fighting for power in sleep mode

A project log for Wireless soil moisture sensor

NRF24L01 ATTINY85 work together to measure and broadcast soil moisture sensor data

andriymalyshenkoandriy.malyshenko 08/13/2020 at 21:200 Comments

So idea of using this device in wild is to have it sleeping most of the time, then it should wake once in a while, do measuremrnt, send it out and go back to sleep. Therefore battery life will depend heavily on the power consumption in sleep mode, since wakup cycle can be as rare as once per few hours.

So for the first test i want to ensure that when PW_ON pin is low, all my peripherals are off and not drawing current. And here it is, not even a microamp when PW_ON is zero. That's a good start.

Next test I don't care for any of the periferials, they will be off in the sleep mode, now i need to make sure MCU draw as less current as possible. 

Okay so here is the first attempt to use sleep mode

My fuses are

board_fuses.efuse = 0xFF
board_fuses.hfuse = 0xDE
board_fuses.lfuse = 0xF1

BOD set on 1.8V

main.h

#include <avr/io.h>

#define SLEEP_16ms 0
#define SLEEP_32ms 1
#define SLEEP_64ms 2
#define SLEEP_128ms 3
#define SLEEP_256ms 4
#define SLEEP_512ms 5
#define SLEEP_1024ms 6
#define SLEEP_2048ms 7
#define SLEEP_4096ms 8
#define SLEEP_8192ms 9

#define PIN_PWR_SW PINB3
#define PIN_PWR_SW_ _BV(PINB3)

void setup_watchdog(int mode);
void sleep(int mode);

main.c

#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "main.h"
#include <Arduino.h>

void setup() {
  DDRB = DDRB | PIN_PWR_SW_;
  PORTB = 0x00;
}

void loop() {
  PORTB |= PIN_PWR_SW_;
  _delay_ms(1024);
  PORTB &= ~PIN_PWR_SW_;
  sleep(SLEEP_2048ms);
}

// =============== SLEEP MODE ===============

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

void sleep(int mode) {
  setup_watchdog(mode);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();                        // System actually sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out
}

void setup_watchdog(int ii) {
  uint8_t bb = ii & 7;
  if (ii > 7) 
    bb |= (1 << 5);
  bb |= (1 << WDCE);
  int ww = bb;

  MCUSR &= ~(1 << WDRF);
  // start timed sequence
  WDTCR |= (1 << WDCE) | (1 << WDE);
  // set new watchdog timeout value
  WDTCR = bb;
  WDTCR |= _BV(WDIE);
}

This gives me two figures, (A) active mcu, soil sensor, nrf and (B) all off, mcu sleeping. Once again case (A) is non optimised, for now i case only for case (B).

(A) Active: 8mA, 

(B) Sleep: 0,23mA

Good start.

Now by handbook i'll disable ADC during sleep mode:

void sleep(int mode) {
  setup_watchdog(mode);
  cbi(ADCSRA, ADEN);                   // switch Analog to Digitalconverter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();                        // System actually sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out
  sbi(ADCSRA, ADEN);                   // switch Analog to Digitalconverter ON
}

(A) Active: 8mA, 

(B) Sleep: 24uA

That's 10 times improvement compared to pervious setup.

Now there is a hint to disable BOD during sleep

I'll try to do it softwarewise

void sleep(int mode) {
  setup_watchdog(mode);
  cbi(ADCSRA, ADEN);                   // switch Analog to Digitalconverter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  cli();                               // Disable Interrupts
  sleep_bod_disable();                 // Disable BOD
  sei();
  sleep_mode();                        // System actually sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out
  sbi(ADCSRA, ADEN);                   // switch Analog to Digitalconverter ON
}

 Unfortunately makes no difference on current consumption, does Attiny85 supports it anyway?

So let's disable it completely

New fuses

board_fuses.efuse = 0xFF
board_fuses.hfuse = 0xDF
board_fuses.lfuse = 0xF1

And now i'm down to

(A) Active: 8mA, 

(B) Sleep: 4,5uA

So another 5 times imprevement.

I tried also to set all pins to input mode, but this made no difference as well.

Now that's really the best I've managed so far. Datasheet says it should be under 1uA, and possibly as low as 0.2uA in my conditions, but i'm failing to get the same result. So if you know what I'm missing here, please help me out. Could it be the crappy multimeter tool?

BTW, 200mAH battery would power it for 5 years under constant sleep conditions, which is not so bad, but not as cool as some 40 years:)

Discussions