Getting ADC readings after waking a ATTiny85

I have this little project of mine that involves putting a ATTiny85 to sleep to save power. The main idea is to:

1- Putting ATTiny85 to sleep, including deactivation of the ADC
2- Wake-up MC on pin change
3- Activating ADC
4- Reading the voltage from the same pin that detected the change

The problem is that I'm unable to get a decent reading on the ADC after waking up the MC. It seems be to HIGH all the time.

Here is the code:

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

// These constants won't change.  They're used to give names
// to the pins used:
const int analogInPin = 2;
const int LED = 3;

volatile int outputValue = 0;

void setup() {
  
  pinMode(LED, OUTPUT);
  pinMode(analogInPin, INPUT);
  
  digitalWrite(LED, LOW);
  digitalWrite(analogInPin, HIGH);
  
}

void MCSleep() {
  
  GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
  PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement
  
  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep
  
  cli();                                  // Disable interrupts
  PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
  sleep_disable();                        // Clear SE bit
  ADCSRA |= _BV(ADEN);                    // ADC on
  
  sei();                                  // Enable interrupts
} // sleep

// Whatever the pin this IRS should always point to the PCINT0_vect, because is the only capable of
// providing the interrupt. The ISR must be provided otherwise, the MC just resets instead of resuming.
ISR(PCINT0_vect) {
  
  // This is called when the interrupt occurs, but I don't need to do anything in it
  delay(100);
  
}

void loop() {
  
  MCSleep();

  delay(100);
  
  outputValue = analogRead(analogInPin);
  
  if (outputValue > 100){
    digitalWrite(LED, HIGH); // Door is open.
  }
  else{
    digitalWrite(LED, LOW); // Door is closed.
  }
  
  delay(200);
}

From the ATTiny85 specs it seems that after reactivating the ADC there is some extended conversion, so I tried to add a delay after waking up (which didn't work) and later on
I've tried to implement some other ideas like saving the ADCSRA value before sleeping and recover it after waking up, but unfortunately, that hasn't work either.

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

// These constants won't change.  They're used to give names
// to the pins used:
const int analogInPin = 2;
const int LED = 3;
byte keep_ADCSRA;

volatile int outputValue = 0;

void setup() {
  
  pinMode(LED, OUTPUT);
  pinMode(analogInPin, INPUT);
  
  digitalWrite(LED, LOW);
  digitalWrite(analogInPin, HIGH);
  
}

void MCSleep() {
  
  GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
  PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin
  keep_ADCSRA = ADCSRA;
  ADCSRA = 0;
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement
  
  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep
  
  cli();                                  // Disable interrupts
  PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
  sleep_disable();                        // Clear SE bit
  ADCSRA = keep_ADCSRA;
  
  sei();                                  // Enable interrupts
} // sleep

// Whatever the pin this IRS should always point to the PCINT0_vect, because is the only capable of
// providing the interrupt. The ISR must be provided otherwise, the MC just resets instead of resuming.
ISR(PCINT0_vect) {
  
  // This is called when the interrupt occurs, but I don't need to do anything in it
  delay(100);
  
}

void loop() {
  
  MCSleep();

  delay(100);
  
  outputValue = analogRead(analogInPin);
  
  if (outputValue > 100){
    digitalWrite(LED, HIGH); // Door is open.
  }
  else{
    digitalWrite(LED, LOW); // Door is closed.
  }
  
  delay(200);
}

Any ideas?

Thanks in advance.

Several things:

pinMode(analogInPin, INPUT);

you don't need to set a pin mode for analog reading

digitalWrite(analogInPin, HIGH);

This enables the pullup resistor on your analog input. No wonder the reading is too high

ISR(PCINT0_vect) {
  
  // This is called when the interrupt occurs, but I don't need to do anything in it
  delay(100);
  
}

Using delay inside an ISR won't work

delay(100);
  
  outputValue = analogRead(analogInPin);

ommit the delay, it is way too long. Just read the analog input twice and ignore the first reading

I'm curious if you've solved your issue. A year ago I tried a similar thing and found that neither analogRead() or delay() was reliable after waking up from WDT. I was porting working code from mega168 to atTiny85 and was very surprised it was so hard to make work.

olf2012:
Several things:

pinMode(analogInPin, INPUT);

you don't need to set a pin mode for analog reading

digitalWrite(analogInPin, HIGH);

This enables the pullup resistor on your analog input. No wonder the reading is too high

ISR(PCINT0_vect) {

// This is called when the interrupt occurs, but I don't need to do anything in it
  delay(100);
 
}



Using delay inside an ISR won't work



delay(100);
 
  outputValue = analogRead(analogInPin);




ommit the delay, it is way too long. Just read the analog input twice and ignore the first reading

Thank you olf2012. Although all your comments are valid ones, unfortunately, in the end, I was stuck with the same problem.

One thing that work though was to use pin 3 instead of pin 2 as the switchPin. Now I'm going through the documentation to find if I missed out any difference between both pins. But in principel the ATTiny specs say:

• Bit 5 – PCIE: Pin Change Interrupt Enable
When the PCIE bit is set (one) and the I-bit in the Status Register (SREG) is set (one), pin change interrupt is enabled. Any change on any enabled PCINT[5:0] pin will cause an interrupt. The corresponding interrupt of Pin Change Interrupt Request is executed from the PCI Interrupt Vector. PCINT[5:0] pins are enabled individually by the PCMSK0 Register.

It only says that any of those pins will detect an interrupt... and in fact it does, I'm just wondering what is the difference while doing the analogue read.

Can you post the last version that didn't work, ie, with pin 2 as switchPin? (I don't see anything by that name in the code you posted above) I'm not really sure where the code is at this point?

DrAzzy:
Can you post the last version that didn't work, ie, with pin 2 as switchPin? (I don't see anything by that name in the code you posted above) I'm not really sure where the code is at this point?

Hi DrAzzy,

OH yeah... I meant analogInPin. I can post the last non-working code anyway...

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

const int switchPin = 2;
const int door = 3;
const int sleep = 4;

int outputValue = 0;

void setup() {

    //pinMode(switchPin, INPUT);
    //digitalWrite(switchPin, HIGH);
    
    pinMode(door, OUTPUT);
    digitalWrite(door, LOW);
    
    pinMode(sleep, OUTPUT);
    digitalWrite(sleep, HIGH);

    // Flash quick sequence so we know setup has started
    for (int k = 0; k < 10; k = k + 1) {
        if (k % 2 == 0) {
            digitalWrite(door, HIGH);
            }
        else {
            digitalWrite(door, LOW);
            }
        delay(250);
        } // for
    } // setup

void MCsleep() {

    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin
    ADCSRA &= ~_BV(ADEN);                   // ADC off
    //digitalWrite(switchPin, LOW);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

    sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei();                                  // Enable interrupts
    sleep_cpu();                            // sleep

    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
    sleep_disable();                        // Clear SE bit
    //digitalWrite(switchPin, HIGH);
    ADCSRA |= _BV(ADEN);                    // ADC on

    sei();                                  // Enable interrupts
    } // sleep

// Whatever the pin this IRS should always point to the PCINT0_vect, because is the only capable of
// providing the interrupt. The ISR must be provided otherwise, the MC just resets instead of resuming.
ISR(PCINT0_vect) {
    // This is called when the interrupt occurs, but I don't need to do anything in it
    }

void loop() {
    
  MCsleep();
  delay(20);
  
  outputValue = analogRead(switchPin);
  
  if(outputValue > 200){
    digitalWrite(sleep, LOW);
    delay(1000);
    digitalWrite(door, HIGH);
    delay(2000);
    digitalWrite(sleep, HIGH);
  }
  else{
    digitalWrite(sleep, LOW);
    delay(1000);
    digitalWrite(door, LOW);
    delay(2000);
    digitalWrite(sleep, HIGH);
  }
}

Ignore the sleep pin. It's just there for putting a XBee to sleep, but currently it's just a led.

So... going through the specs I find the following:

Port B, Bit 3 – XTAL1/CLKI/ADC3/OC1B/PCINT3
• XTAL1: Chip Clock Oscillator pin 1. Used for all chip clock sources except internal calibrateble RC oscillator.
When used as a clock pin, the pin can not be used as an I/O pin.
• CLKI: Clock Input from an external clock source, see “External Clock” on page 26.
ADC3: Analog to Digital Converter, Channel 3 .
• OC1B: Inverted Output Compare Match output: The PB3 pin can serve as an external output for the
Timer/Counter1 Compare Match B when configured as an output (DDB3 set). The OC1B pin is also the inverted
output pin for the PWM mode timer function.
• PCINT3: Pin Change Interrupt source 3.

Port B, Bit 2 – SCK/ADC1/T0/USCK/SCL/INT0/PCINT2
• SCK: Master Clock output, Slave Clock input pin for SPI channel. When the SPI is enabled as a Slave, this pin
is configured as an input regardless of the setting of DDB2. When the SPI is enabled as a Master, the data
direction of this pin is controlled by DDPB2. When the pin is forced by the SPI to be an input, the pull-up can still
be controlled by the PORTB2 bit.
ADC1: Analog to Digital Converter, Channel 1 .
• T0: Timer/Counter0 counter source.
• USCK: Three-wire mode Universal Serial Interface Clock.
• SCL: Two-wire mode Serial Clock for USI Two-wire mode.
• INT0: External Interrupt source 0.
• PCINT2: Pin Change Interrupt source 2.

I wonder if the ADC channels being different number from the pin number actually being use... makes a difference... :-s

Well!... Yes... That was it! I can't believe I've missed that... since pin 3 had the same number as its analog channel I though it would apply for other pins. So basically I've only changed the switchPin to A1 and it worked! Sorry for wasting your time on this. Here is the working code:

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

const int switchPin = A1;
const int door = 3;
const int sleep = 4;

int outputValue = 0;

void setup() {

    //pinMode(switchPin, INPUT);
    //digitalWrite(switchPin, HIGH);
    
    pinMode(door, OUTPUT);
    digitalWrite(door, LOW);
    
    pinMode(sleep, OUTPUT);
    digitalWrite(sleep, HIGH);

    // Flash quick sequence so we know setup has started
    for (int k = 0; k < 10; k = k + 1) {
        if (k % 2 == 0) {
            digitalWrite(door, HIGH);
            }
        else {
            digitalWrite(door, LOW);
            }
        delay(250);
        } // for
    } // setup

void MCsleep() {

    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin
    ADCSRA &= ~_BV(ADEN);                   // ADC off
    //digitalWrite(switchPin, LOW);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

    sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei();                                  // Enable interrupts
    sleep_cpu();                            // sleep

    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
    sleep_disable();                        // Clear SE bit
    //digitalWrite(switchPin, HIGH);
    ADCSRA |= _BV(ADEN);                    // ADC on

    sei();                                  // Enable interrupts
    } // sleep

// Whatever the pin this IRS should always point to the PCINT0_vect, because is the only capable of
// providing the interrupt. The ISR must be provided otherwise, the MC just resets instead of resuming.
ISR(PCINT0_vect) {
    // This is called when the interrupt occurs, but I don't need to do anything in it
    }

void loop() {
    
  MCsleep();
  delay(20);
  
  outputValue = analogRead(switchPin);
  
  if(outputValue > 200){
    digitalWrite(sleep, LOW);
    delay(1000);
    digitalWrite(door, HIGH);
    delay(2000);
    digitalWrite(sleep, HIGH);
  }
  else{
    digitalWrite(sleep, LOW);
    delay(1000);
    digitalWrite(door, LOW);
    delay(2000);
    digitalWrite(sleep, HIGH);
  }
}