ATtiny85 Sleep States

As part of my adventures into IoT I've been making sensors and controllers for the house, Temperature and Humidity, and a Heatpump controller. One of the other aspects of house monitoring that has always peaked my interest is how much electricity we're using at any time. There are a few ways of doing this

Current clamps do the job but from what I have read they need to be coupled to reading the voltage too so that they are accruate. Pulse counting on the surface seems to be simple... however I was looking to make my counter battery powered so it would need to sip current so tham I'm not changing batteries every couple of weeks. Queue looking into using a ATtiny85 as the counter, this seemed like the ideal candidate to do the hard work of counting pulses and then a larger more powerful microcontroller could request the current count and pass it up to the controller.

Getting the ATtiny up and running didn't really take much effort but making it go into a deep sleep and wake up as required was a lesson in sleep-states, interupts and the need to have the electronics done correctly. So where to begin...

Counting Pulses

This as it turned out was the easy peasy (ok not hard) part of this, enable interupts, define which pin is going to be monitored for changes and off you go. Not so fast... Hold on do we trigger on rising or falling? How do you make a analogue signal into a digital one? Why do I need to make my counter variable volatile? Cue much googlefu to find out about these things; Falling is the correct way to trigger the counter, you need a small circuit to convert analogue to digital, if the variable is not volatile the the compiler puts the variable in the wrong place in memory and it doesn't get updated correctly in the interrupt.

So first we need to enable pin change interrupts on the desired port and then enable global interrupts.

#include "avr/interrupt.h"
void setup() {
  sbi(GIMSK,PCIE);  // Enable pin change interrupt
  sbi(PCMSK,PB3);   // Set PB3 as the pin to watch
  sei();
}

Now we need to define our interrupt handler

ISR(PCINT0_vect) {
  if (PINB & (1<<PB3)) { // this is the leading edge
    ;
  } else {               // this is the falling edge
    count++;
  }
}

Putting the Baby to Sleep

One of the things that attracted me to the ATtiny was that it sips current. <marketing blurb>0.1µA at 1.8v in power down</marketing blurb> I suspect that this has everything shutdown and that most of the interrupts are also disabled.

After a lot of reading around I finally managed to get the ATtiny into the lowest power state. however for i2c wake to work as expected I needed to add in a delay() into the the loop()... not sure why this is needed but without it it never wakes.