Arduino powered by a capacitor – timing with RTC

atmega_capacitors_pv_5There are already some posts about my testsystem, Arduino powered by a capacitor: The start, first tests, reducing consumption and optimized tests. With different power saving techniques power consumption could be reduced by a factor of around 300. Now let’s look how we can go further and come step by step to a system that can be used in real life.

The basic idea is a microcontroller system e. g. for datalogging of slowly changing signals like room temperature or solar irradiation. In these applications the controller is up for a very short time and then sleeps until the next event. In the basic tests with a standalone ATmega328P timing between two logging events was done by the internal watchdog timer. Now let’s try to improve this…

Timing source

The ATmega328 has several internal timers: timer0 to 2 and the watchdog. They all have the disadvantage to increase supply current during sleep, even the watchdog timer consumes 6µA. So the idea was to use an external “timer” that wakes up the ATmega regularly via external interrupt. Regarding lowest power consumption a good choice is a RTC like the DS1337. It has I2C interface, a supply current of less than 1µA (with I2C inactive) and two alarms that can be used to create external interrupts. The DS1337, other than the well-known DS1307, has a supply voltage range from 1.8V to 5.5V which fits very well to our ATmega-capacitor-system as the ATmega works in the same voltage range. The DS1337 can create alarms e. g. every second, minute or hour. This makes it an excellent timing source for datalogging applications.

The standard clock source of a RTC is a quartz. This gives us much more precision in timing than we had before using the watchdog method. Also the RTC gives us the possibility to make timestamps while logging the data.

So we have two more components (the RTC and the quartz) but we (hopefully) get a lower supply current during sleep, a more precise timing and the possibility to have timestamps.

Wakeup by interrupt

In the first step we will use the square wave output of the RTC with a frequency on 1Hz to have the same interval as in the watchdog example. This will give us a direct comparison between the two versions. When we look at the RTC we see the square wave signal with a duty cycle of 50% because it is created by simple frequency division. This means that the signal is 500ms HIGH and then 500ms LOW.

The RTC gives us the signal for the external interrupt that wakes up the controller. While in sleep we use SLEEP_MODE_PWR_DOWN to have the lowest possible supply current. But this also brings us a restriction: Other than in active mode the external interrupt pins can only detect a LOW level but not a rising or falling edge. This means that a interrupt is triggered whenever the signal is LOW, e. g. directly after the interrupt is detected. Now our RTC output signal is much longer than our interrupt handling and thus multiple interrupts are triggered from one output pulse of the RTC. In the post about the ATmega328P wakeup from sleep via interrupt this is described in detail. You will also find the solution to this problem in using a simple RC network for signal conditioning.

Example sketch

This is the modified sketch that does the timing no more with the watchdog timer but with the square wave output of the RTC. Parts of this code are from Nick Gammon’s page and John Vaughters’ page. Thank you both!

#include <avr/sleep.h>
#include <EEPROM.h>
#include <Wire.h>

// Variables declarations:
// EEPROM address
int addr = 0;
// the measured voltage as integer
unsigned int voltage;
// keep the state of register ADCSRA
byte keep_ADCSRA;

// I2C bus address declarations:
#define DS1337_I2C_ADDRESS 0x68  // DS1337

// Pin declarations:
// The pin that supplies the voltage divider:
int dividerpin = 8;
// The analog input where the voltage is measured:
int voltagepin = 0;

void wake ()
{
  // cancel sleep as a precaution
  sleep_disable();
  // must do this as the pin will probably stay low for a while
  detachInterrupt (0);
}

void setup() {
  // set digital pins for low power consumption
  for (int i = 0; i < 14; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);
  }

  // initialize I2C bus
  Wire.begin();
  // initialize RTC
  Wire.beginTransmission(DS1337_I2C_ADDRESS);  // Open I2C line in write mode
  Wire.write((byte)0x0E);                      // Set the register pointer
  Wire.write((byte)0x00);                      // Set the status register
  Wire.endTransmission();                      // End write mode

  // set analog reference voltage
  analogReference(INTERNAL);
}

void loop() {
  while(addr < 1024) {
    voltage = analogRead(voltagepin);
    // write data to EEPROM
    // Lowbyte first
    EEPROM.write(addr, voltage % 256);
    addr++;
    EEPROM.write(addr, voltage / 256);
    addr++;
    // shut down ADC
    keep_ADCSRA = ADCSRA;
    ADCSRA = 0;

    // Go to sleep until the next interrupt
    set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
    sleep_enable();

    // Do not interrupt before we go to sleep, or the
    // ISR will detach interrupts and we won't wake.
    noInterrupts ();

    // will be called when pin D2 goes low  
    attachInterrupt (0, wake, LOW);

    // 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

    interrupts();
    sleep_cpu();

    // continues here after wakeup
    // cancel sleep as a precaution
    sleep_disable();
    ADCSRA = keep_ADCSRA;
    digitalWrite(dividerpin, HIGH);
  }
}

 Tests

Measuring the supply current in sleep mode shows what I’ve expected: The current is 1µA while the square wave output of the RTC is HIGH and 2µA when it’s LOW. During LOW an additional current goes through R1 and R2 of the interrupt signal contitioning RC network. R2 is 4.7MΩ so it brings 1µA additional current.

And what happens to the total uptime? In my tests it stays more or less the same as in the first version with timing done by the watchdog. There, supply current was 6µA during sleep. This made me a bit disappointed, but now let’s look how to reduce runtime of the controller. As it takes a rather long time to write data to the onboard EEPROM and the controller is running during the write cycle there should be some savings possible.

Result

With the RTC the total supply current of the system goes down from 6µA to 1 – 2µA. Also timing accuracy is increased a lot and there is an easy possibility to write timestamps if necessary. This couldn’t have been done without the RTC when the controller is in SLEEP_MODE_PWR_DOWN. The reduction of the supply current does’t have a visible effect on the total uptime at the moment so other points of optimization should be regarded. Look at the next posts

Enjoy

heliosoph

2 thoughts on “Arduino powered by a capacitor – timing with RTC

    1. heliosoph Post author

      Thanks for your reply! I’m glad to know that you enjoy my writing. More is due to come…

      heliosoph

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

7 + 3 =