ATTiny85v and Sleep Functions

While that's certainly true, if most of the awake time is spent waiting for things to happen (e.g. waiting for an analogRead to complete), then the slower clock is definitely worthwhile - as long as you program the ADC clock prescaler so as to maintain a reasonably high ADC conversion speed.

What you could do if you were concerned about that would be to start an ADC conversion, go to sleep, and let the ADC completion wake you. I have an example of doing that right here:

Scroll down slightly to "Sleep during ADC conversion". That would let you drop to the lower power consumption during the conversion and would also have the beneficial side-effect of reducing ADC noise.

In any case, as I suggested further up this thread, once you get consumption down to a certain low level, your main worry is battery self-discharge. Of course any power consumption on top of that is extra, but it may only be a few percent extra, and who can be certain exactly how much capacity a battery has anyway?

Here's an example:

// Temperature monitoring system

// Author: Nick Gammon
// Date:   16 March 2013
// Version: 1.1 - uses sleep during ADC conversion

// Thermistor calculation adapted from: http://learn.adafruit.com/thermistor/using-a-thermistor

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

// Flash LED if temperature outside this range:
const float lowTemp = 19.0;      // degrees C
const float highTemp = 26.0;     // degrees C

// the bigger this is, the less power we consume
const int timeBetweenReadings = 30;  // seconds

const byte warningLED = 8;    // which LED to flash

#define DEBUGGING false

// Wiring:   Gnd <----> 5K Thermistor <----> |   <----->>   4.7K resistor  <-----> AREF
//                                           |
//                                           v
//                                           A0

// which analog pin to connect
const byte THERMISTORPIN = 0;

// temp. for nominal resistance (almost always 25 C) 
const int TEMPERATURENOMINAL = 25;

// resistance at TEMPERATURENOMINAL (above)
const int THERMISTORNOMINAL = 5000;

// how many samples to take and average, more takes longer but is more 'smooth'
const int NUMSAMPLES = 5;

// The beta coefficient of the thermistor (usually 3000-4000) 
const int BCOEFFICIENT = 3960;

// the value of the 'other' resistor (measure to make sure)
const int SERIESRESISTOR = 4640;

// how many Kelvin 0 degrees Celsius is
const float KELVIN = 273.15;

// what was our last reading
float lastReading;  // degrees

// how many seconds till we take another reading (updated as we enter sleep mode)
float nextReadingTime = 0;  // seconds

// watchdog intervals
// sleep bit patterns for WDTCSR
enum 
{
  WDT_16_MS  =  0b000000,
  WDT_32_MS  =  0b000001,
  WDT_64_MS  =  0b000010,
  WDT_128_MS =  0b000011,
  WDT_256_MS =  0b000100,
  WDT_512_MS =  0b000101,
  WDT_1_SEC  =  0b000110,
  WDT_2_SEC  =  0b000111,
  WDT_4_SEC  =  0b100000,
  WDT_8_SEC  =  0b100001,
 };  // end of WDT intervals enum

void setup (void) 
  { 
    
#if DEBUGGING
  Serial.begin(115200); 
#endif // DEBUGGING

  pinMode (warningLED, OUTPUT);
  digitalWrite (warningLED, LOW);
  
  // stop timer 0
  TCCR0A = 0;
  TCCR0B = 0;
  
  // set the analog reference (high two bits of ADMUX) and select the
  // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
  // to 0 (the default).
  ADMUX = _BV (REFS0) | _BV (REFS1) | (THERMISTORPIN & 0x07);  // Internal 1.1V reference
  
  lastReading = getTemperature ();
  }  // end of setup
  
// watchdog interrupt
ISR (WDT_vect) 
  {
  wdt_disable();  // disable watchdog
  }

void myWatchdogEnable (const byte interval) 
  {
  // clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset
  WDTCSR = _BV (WDCE) | _BV (WDE);
  // set interrupt mode and an interval 
  WDTCSR = _BV (WDIE) | interval;    // set WDIE, and requested delay
  wdt_reset();  // pat the dog

  // disable ADC
  byte old_ADCSRA = ADCSRA;
  ADCSRA = 0;  
  
  // turn off various modules
  byte old_PRR = PRR;
  PRR = 0xFF; 

  // timed sequence coming up
  noInterrupts ();
  
  // ready to sleep
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();

  // turn off brown-out enable in software
  MCUCR = _BV (BODS) | _BV (BODSE);
  MCUCR = _BV (BODS); 
  interrupts ();
  sleep_cpu ();  

  // cancel sleep as a precaution
  sleep_disable();
  PRR = old_PRR;
  ADCSRA = old_ADCSRA;
  
  } // end of myWatchdogEnable

// when ADC completed, take an interrupt 
EMPTY_INTERRUPT (ADC_vect);
  
float getTemperature ()
  {
  byte i;
  float average = 0.0;
  
  // take N samples in a rowy 
  for (i = 0; i < NUMSAMPLES; i++) 
    {
    // ensure not interrupted before we sleep
    noInterrupts ();
    
    // start the conversion
    ADCSRA |= _BV (ADSC) | _BV (ADIE);
    set_sleep_mode (SLEEP_MODE_ADC);    // sleep during sample
    interrupts ();
    sleep_mode (); 

    // reading should be done, but better make sure

    // ADSC is cleared when the conversion finishes
    while (bit_is_set (ADCSRA, ADSC))
      { }
    
    // get ADC data
    byte low, high;
    
    // we have to read ADCL first; doing so locks both ADCL
    // and ADCH until ADCH is read.  reading ADCL second would
    // cause the results of each conversion to be discarded,
    // as ADCL and ADCH would be locked when it completed.
    low = ADCL;
    high = ADCH;
  
    int adcReading = (high << 8) | low;
  
    average += adcReading;
    }  // end of for each of NUMSAMPLES
    
  average /= NUMSAMPLES; 

  // convert the value to resistance 
  average = 1023 / average - 1; 
  average = SERIESRESISTOR / average; 

  float steinhart = average / THERMISTORNOMINAL;
  steinhart = log (steinhart); 
  steinhart /= BCOEFFICIENT; 
  steinhart += 1.0 / (TEMPERATURENOMINAL + KELVIN); 
  steinhart = 1.0 / steinhart; 
  steinhart -= KELVIN;   // back to celsius
  return steinhart;
  }  // end of getTemperature
  
void takeReading ()
  {
  lastReading = getTemperature ();
#if DEBUGGING
  Serial.print ("Temperature = "); 
  Serial.print (lastReading);
  Serial.println (" *C");
#endif // DEBUGGING

  if ((lastReading < lowTemp) || (lastReading > highTemp))
    nextReadingTime = 5;    // if out of range, read again in 5 seconds
  else
    nextReadingTime = timeBetweenReadings;    // when to take another reading
  }  // end of takeReading
  
  
void loop (void) 
  { 
  if (nextReadingTime <= 0)
    takeReading ();
    
  byte waitTime;
  
  if (lastReading < lowTemp)
    {
    waitTime = WDT_512_MS;
    nextReadingTime -= 0.5;
    digitalWrite (warningLED, ! digitalRead (warningLED));
#if DEBUGGING
    Serial.println ("Too low!");
#endif // DEBUGGING
    }
  else if (lastReading > highTemp)
    {
    waitTime = WDT_256_MS;
    nextReadingTime -= 0.25;
    digitalWrite (warningLED, ! digitalRead (warningLED));
#if DEBUGGING
    Serial.println ("Too HIGH!");
#endif // DEBUGGING
    }
  else
    {
    // temperature OK - sleep for 8 seconds
    waitTime = WDT_8_SEC;
    nextReadingTime -= 8; 
    digitalWrite (warningLED, LOW);
    }
    
#if DEBUGGING
  // force serial data out, wait for UART to empty
  Serial.flush ();
#endif // DEBUGGING

  myWatchdogEnable (waitTime);
  }  // end of loop

That sleeps during the ADC conversion which should reduce noise.

This rather interesting image shows the power consumption during waking:

I measured the voltage before and after a 1K resistor in series, and took the maths function (CH1 - CH2) to see the current consumption.

Since we have 2.68V drop over 1K, that is 2.68 mA current at the maximum points (less during the ADC conversions).

The 5 x ADC conversion points are clearly visible, followed by a delay in calculating the temperature. The time interval is about 2.5 mS.

My earlier post didn't take into account that this is only done every 32 seconds, so we are consuming 2.68 mA over 2.5 mS, and only doing that once every 32 seconds. So the overall power consumption is low by my reckoning (0.2 uA on average).

The individual conversions are taking 220 uS each which sounds about right as normally an ADC conversion takes 104 uS, but running at half the clock speed (8 MHz) the conversions would now take 208 uS, plus a bit extra for adding to the average.

Just as a follow-up, I've been tweaking this design a bit, documented here:

(That's part of my "power saving" web page: Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors ).

I modified it a bit in the end to have two LEDs - a blue "too cold" one, and a red "too hot" one.

I reduced the "on" time for the LEDs down to 16 mS, to reduce power consumed by them when flashing. They are still perfectly visible. I'm not sure how well that will work with a closed-circuit TV, the on time might be too short to be captured, but you could experiment with that.

The same general concept could be used for things like a wine cellar, or even a fridge monitor. Set the temperature range to that it warns you if the fridge is too hot or too cold. Maybe add in a piezo speaker so you can hear a beeping sound.

I've got it sitting here hooked up to 3 x AA NiMh batteries. The battery voltage is being monitored, and so far hasn't changed at all.

fungus:
It's much easier to edit your "boards.txt" and create a new option in there called "ATiny85, 128kHz clock" (or whatever). The entries for each board have fuse settings.

Then you just select it and do "burn bootloader" from the IDE.

Did this. Created a new entry for 128kHz in the boards.txt file. Changed the Low Fuse from 0x62 to 0xe4 to set it to 128kHz. Changed f_cpu to 128000L. I selected that, and chose Burn Bootloader, and it seemed to work, but I didn't look at the output in detail. I tried to upload a blink sketch using the new board entry, and it fails. If I try to select the old 1MHz entry as I was using previously, it still fails. So obviously it chnaged the fuses, but that either destroyed the chip or I am simply unable to now access it. What are the avrdude commands I should use to check the fuses and can anybody tell me EXACTLY how I should have done this? I want to be able to run at 128kHz, or change back to the default 1MHz (8MHz with Divide by 8 turned on). Thanks!

I think you need to reduce the avrdude clock rate. The SPI clock during programming must be less than 1/4 of the clock rate of the target chip, so you need to program it at less than 32kHz. According to USBtiny-based AVR Programmer, the default SPI clock for avrdude is 100kHz.

Looks like I need to edit the avrdude.conf file, un-comment the default_bitclock setting, and change that to something. But what? I am having troubles finding settings for that. Is there anything else I need to change? I edited the boards.txt file, and that is it so far.

jrburke99:
Looks like I need to edit the avrdude.conf file, un-comment the default_bitclock setting, and change that to something.

No. You have to configure the programmer to use a lower bitrate. What are you using for a programmer?

I needed to program my attiny85 @128KHz manually from command line with avrdude using the -B option. I used -B50 seemed to work okay. However, this only works if your programmer supports software sck speed selection.

Alternatively, I have used some USBasp that had a hardware jumper for slow sck clock selection. This method also allows it to work with the Arduino IDE.

I am using an Arduino, with the latest IDE 1.0.4. I have uploaded the Arduino ISP sketch, and the ATtiny is on a breadboard and connected as suggested all around the Internet. It worked fine when I uploaded the blink sketch. After I set the fuses to run at 128kHz, it now is no longer programming.

I think Nick Gammon has an ArduinoISP version that supports a low bitrate.

This version supports a low bitrate... GitHub - Coding-Badly/TinyISP: ArduinoISP replacement with: Tiny Debug Knock Bang, Monitor, Tiny Tuner 2, and other amenities

It looks like Lady Ada published a version that supports a low bitrate... ArduinoISP/ArduinoISP.ino at master · adafruit/ArduinoISP · GitHub (remove the comment from line #65)

I couldn't find any ArduinoISP on Nick's site. I believe he only has a bootloader program.

I tried the Lady Ada's version but the LOW_SPEED option didn't work. Increasing the EXTRA_SPI_DELAY also didn't help any (tried a range from 125 to 600). Still got a "programmer is out of sync" error.

But Coding-Badly's TinyISP did work!
(on a ATtiny13@128KHz)

I added this line to the file _TinyISP_BuildOptions_h:

 #define PROGRAMMER_SPI_CLOCK  SLOW

I have a sketch that uploads from a SD card, bypassing avrdude completely:

jrburke99:
Looks like I need to edit the avrdude.conf file, un-comment the default_bitclock setting, and change that to something. But what? I am having troubles finding settings for that. Is there anything else I need to change? I edited the boards.txt file, and that is it so far.

As I understand it, the default_bitclock avrdude.conf entry is (like the -B option) actually a delay time in microseconds, and the default is 10. So you would need to increase it to 30 or more. However, this will only work if the programmer you are using has a mechanism for configuring the clock rate that avrdude understands. Which programmer are you using?

I am using what came with the newest Arduino IDE, which looks to be ArduinoISP version 04m3 by Randall Bohn.

I can use TinyISP instead? There are a lot of files in the repository. What exactly do I need? And will that allow me to program the ATtiny85v after I have set the fuses to run at 128kHz?

jrburke99:
I am using what came with the newest Arduino IDE, which looks to be ArduinoISP version 04m3 by Randall Bohn.

Which will never work programming a processor clocked at 128KHz.

I can use TinyISP instead?

I do.

There are a lot of files in the repository. What exactly do I need?

All of them. The files are a "sketch" and belong in a subdirectory in your Sketch directory. One of the files has a .pde extension. That's the file you load in the Arduino IDE.

And will that allow me to program the ATtiny85v after I have set the fuses to run at 128kHz?

Yes. After you apply the modification graciously described by @hiduino...

Not sure if this answers any previous questions, but I am using "AVRISP mkII" as the programmer when loading the ArduinoISP sketch to the Arduino (the one that comes with Arduino 1.0.4 as I mentioned above), and after I then connect the Attiny85v on a breadboard to the Arduino, I select "Arduino as ISP" as the programmer.

I will try loading the TinyISP with the small modification and see if that works.

Success! TinyISP works! ATtiny85v is now blinking at 128kHz. Thanks to all!

As an added bonus you get Serial-style debugging without having to fiddle with wiring...

http://www.ernstc.dk/arduino/tinycom.html
http://www.rudiswiki.de/wiki9/AVRTinyISP