ATmega328P waking from sleep due to pin fluctuations

Hi all,

I have an Arduino Pro Mini (it is one of those botched 5V versions with 8MHz external oscillator but the VR is irrelevant - I run it directly off the VCC pin) for a project that I need to run off battery for long periods - how long before the battery is charged is out of my control. Thus, when the system is on battery only, it will be in sleep (power down mode) until it is interrupted from a sensor, or when power is connected. When the battery is detected to be low, the interrupt for the sensor is disabled and the system only wakes on power on. To achieve this, I initially used the two external interrupts available INT0 and INT1 on pins 2 & 3. The sensor is a simple microswitch which shorts to ground when actuated.

This works flawlessly when I ran the Pro Mini on 5V, but when it was running at 3.3V, it started waking from sleep due to fluctuations in the sensor pin values. It wakes without triggering any ISR, I debugged using the BAD_ISR vector to check if there was any interrupt triggering unintentionally. However, it wakes and goes straight to executing straight after the call to sleep. The fluctuations need to be fast (The microswitch bounces rapidly when clicked, which is how the problem was discovered), in the order of milliseconds (reproducible by hand by sticking a wire from a GPIO pin to GND and out rapidly, but more difficult than using the bouncing switch). However, on further probing, I discovered that it wakes not only due to fluctuations on pins with the external interrupt, but any GPIO pin. I did this by connecting identical microswitches to random input pins. To further troubleshoot, I changed to use pin change interrupts, which did not change anything about the problem. Once I disable all interrupts, this issue disappears and the Arduino will be stuck in an infinite deep sleep. However, as long as any external interrupts or pin change interrupts are enabled, pins that are masked away or from different pin change interrupts can trigger this wake behavior without causing any ISR to be fired. Below are 2 snippets of code that can reproduce the problem highlighted:

Summary/TLDR: Pin changes without interrupts enabled on them wake ATmega328P only when running on 3.3v and only when interrupts to other pins are enabled, no ISR triggered.

Edit:
Oh yes, I forgot to include the question... Does anyone know why this is happening or can reproduce this problem? Thank you!!

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

volatile enum opmodes{sleep, interrupt}opmode;

ISR(INT0_vect){
  //Disable all Interrupts
  EIMSK = 0;
  opmode = interrupt;
  Serial.println("INT0");
}

ISR(INT1_vect){
  //Disable all Interrupts
  EIMSK = 0;
  opmode = interrupt;
  Serial.println("INT1");
}

ISR(BADISR_vect){
  Serial.println("Whut");
}

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  pinMode(12, INPUT);
}

void loop() {
  opmode = sleep;
  Serial.println("Sleep");
  Serial.flush();
  delay(20);
  EIFR = bit(INTF0); // clear flag for interrupt 0
  EIMSK = bit(INT0); // enable interrupt 0
  power_adc_disable();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  cli();
  if (opmode == sleep){
    sleep_enable();
    sei();
    sleep_cpu();
    sleep_disable();
  }
  sei();
  power_adc_enable();
}
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

volatile enum opmodes{sleep, interrupt}opmode;

ISR(PCINT0_vect){
  //Disable all Interrupts
  PCICR = 0;
  opmode = interrupt;
  Serial.println("PCINT0");
}

ISR(PCINT2_vect){
  //Disable all Interrupts
  PCICR = 0;
  opmode = interrupt;
  Serial.println("PCINT2");
}

ISR(BADISR_vect){
  Serial.println("Whut");
}

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  pinMode(12, INPUT);
  PCMSK0 = bit(PCINT4);           //enable pcinterrupt 0 on pin 12
  PCMSK2 = bit(PCINT18);          //enable pcinterrupt 2 on pin 2
  PCICR = 0;
  PCICR = bit(PCIE0); // enable pcinterrupt 0
}

void loop() {
  opmode = sleep;
  Serial.println("Sleep");
  Serial.flush();
  delay(20);
  PCIFR = bit(PCIF0); // clear flag for pcinterrupt 0
  PCICR = bit(PCIE0);  // enable pcinterrupt 0 only
  power_adc_disable();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  cli();
  if (opmode == sleep){
    sleep_enable();
    sei();
    sleep_cpu();
    sleep_disable();
  }
  sei();
  power_adc_enable();
}

For minimum power consumption, you should make sure to set all your unused pins to OUTPUT mode. Floating pins in input can cause all sorts of odd behaviors. (although I guess that doesn't quite explain why you're seeing the wakeups...)

Yes, I guess this problem wouldn't exist if I didn't leave the pins floating in sleep mode. Even the bouncing switch wouldn't be a problem! (how could I not think of that? :confused: ) Thanks for the reminder!

But now I'm curious as to why the microcontroller behaves as such. Let's hope someone has an answer for that, I'm pretty much stumped after 2 whole days of testing.

Interesting... Possibly crosstalk between Int0 ad other pins? Try to add a small cap to Int0 pin (Digital2) to see if it persists.

Will try when I have the time, but based on the ISR not being triggered, I suspect it is really a case of 'weird behavior when there are floating pins', especially since the floating pins were intentionally toggled between defined voltages and floating.

I believe it CANNOT be caused by other pins floating (or doing anything). During sleep digital buffers are disabled on all pins not needed for enabled interrupts.
The physical pin is disconnected from internal logic and the internal logic is connected to GND instead.

To trigger the ISR you need to hold the low level long enough to complete wake up procedure. Since typical Arduinos is clocked from resonator/crystal the wake up time must be long enough for the crystal to stabilize - a lot of ms, much longer than any glitch caused by the method you describe.
If the low level disappears before the MCU wakes up the interrupt is not registered and ISR is not entered - program execution continues after SLEEP instruction. The Datasheet describes this.