Problem with power-down on Atmega 328p

Hello,
I am working on a kitchen timer for my mother. It involves having one uC keeping track of 4 independent set timers and passing them via I2C to the second one to display. It is supposed to run on batteries, so I wanted to implement the power-down function in the counting uC (I2C slave), which before going to sleep would disable power for the master uC. It works to some extent - it correctly powers down and wakes up several times, but, usually around 8th power-down, it does not wake up. Whenever I pull the WAKEUP pin (connected to empty ISR) low, current consumption rises to around 4 mA, but the Atmega never comes back to full functionality (unless I manually reset it). Microcontroller I'm using is, as mentioned in the title, Atmega 328p and my code is below. Thanks for your help!

#define BUZZER 13
#define INTERVAL 1000
#define PAUSE 50
#define SHUTUP 2
#define WAKEUP 3
#define MASTERPOWER 8

#include <Wire.h>
#include <LowPower.h>

unsigned int timers[] = {0, 0, 0, 0};
unsigned int zeros[] = {0, 0, 0, 0};
bool ringing [] = {false, false, false, false};
bool currentlyIdle = true;
bool nothingToDo = false;
unsigned int lastAction = 0;

void setup() {
  // put your setup code here, to run once:
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1B |= (1 << CS12);
  TIMSK1 |= (1 << TOIE1);
  TCNT1 = 34286;  //setup timer 1 to generate interrupt every one second
  Wire.begin(9);

  digitalWrite(SDA, 0);
  digitalWrite(SCL, 0);   //get rid of pullups on i2c lines
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveEvent);   //setup i2c
  pinMode(SHUTUP, INPUT_PULLUP);      //setup interrupt stopping beeping
  attachInterrupt(digitalPinToInterrupt(SHUTUP), buzzeroff, FALLING);
  pinMode(WAKEUP, INPUT_PULLUP);        //setup pin used for waking up

  pinMode(MASTERPOWER, OUTPUT);
  digitalWrite(MASTERPOWER, HIGH);       //provide power for the master

  Serial.begin(9600);

}

void loop() {
  // put your main code here, to run repeatedly:
  for (int i = 0; i < 4; i++) {
    if (ringing[i]) {
      for (int j = 0; j < (i + 1); j++) {
        digitalWrite(BUZZER, HIGH);
        delay((INTERVAL - (i * PAUSE)) / (i + 1));
        digitalWrite(BUZZER, LOW);   //generate beeps according to what timer is over
        delay(PAUSE);

      }
      delay(INTERVAL);
    }
  }

  nothingToDo = memcmp(timers, zeros, 4) == 0;
  if (nothingToDo and !currentlyIdle) {
    currentlyIdle = true;
  }
  if (!nothingToDo) {                
    currentlyIdle = false;
    lastAction = millis();
  }

  if (currentlyIdle and millis() - lastAction > 20000L) {                // go to sleep after 20 seconds since last action
    digitalWrite(MASTERPOWER, LOW);                    //turn master device off
    attachInterrupt(digitalPinToInterrupt(WAKEUP), wakeUpISR, LOW);   
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    currentlyIdle = false;
    lastAction = millis();
    digitalWrite(MASTERPOWER, HIGH);               //turn master back on
    detachInterrupt(digitalPinToInterrupt(WAKEUP));




  }
}

ISR(TIMER1_OVF_vect) {                  //isr used for decrementing timers' values
  TCNT1 = 34286;

  for (int i = 0; i < 4; i++) {

    if (timers[i] != 0) {
      timers[i]--;
      if (timers[i] == 0) ringing[i] = true;
      else ringing[i] = false;
    }

  }
}

void requestEvent() {
  byte buf[8];
  buf[0] = timers[0] >> 8;
  buf[1] = timers[0] & 0xff;
  buf[2] = timers[1] >> 8;
  buf[3] = timers[1] & 0xff;
  buf[4] = timers[2] >> 8;
  buf[5] = timers[2] & 0xff;
  buf[6] = timers[3] >> 8;
  buf[7] = timers[3] & 0xff;
  Wire.write(buf, 8);
}

void receiveEvent(int howMany) {
  byte index = Wire.read();
  unsigned int value = Wire.read() << 8 | Wire.read();
  timers[index] = value;
}


void buzzeroff() {
  for (int i = 0; i < 4; i++) ringing[i] = false;
}


void wakeUpISR() {

}

Hi,
Welcome to the forum.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Why do you need a second controller for the display?

Thanks.. Tom.. :slight_smile:

If this works as intended, then I've learned something:

if (currentlyIdle and millis() - lastAction > 20000L) {   
. . .
}

Edit:
Well I found this, so I have learned something:

Operator Keyword for &&

The and operator is the text equivalent of &&. There are two ways to access the and operator in your programs: include the header file iso646.h, or compile with the /Za (Disable language extensions) compiler option.

at https://msdn.microsoft.com/en-us/library/c6s3h5a7.aspx

(I think) I need second microcontroller for few reasons:

  • First, the display works on 3v3, so it needs a regulator, which has big enough quiescent current to drain the battery within few weeks
  • Second, I had big problems with getting to work both display and Timer/Counter1. Using them at the same time created some enormous bugs
  • And finally, similar problems occurred with the encoder library I was using, and I was too lazy to write my own encoder-handling code
    I attached the circuit diagram. It's missing crystal oscillators (both 8MHz) and few capacitors here and there, but I hope it helps. Thanks for help!

Main.pdf (43.2 KB)

From an engineering point of view, it is certainly an interesting kitchen timer. If I was making something for my wife to use, I'd be considering a sand glass and would worry that that may be too complicated to use.

Anyway, all I can see is this:

  1. What bootloader are you using ? Some (especially Nanos) have or had problems with sleep/wakeup.
  2. The variables you are using in the ISR should be declared as volatile.
  3. There are some mismatches between the schematic and the code. For example, the pin for the buzzer.

Hi,
Thanks for the clear schematic, can you export it as a jpg rather than a pdf.
And it can then be put in the post as in image.

What does Q2 do?
Disconnecting the GND of the regulator will not turn the regulator ON or OFF.

What sort of accuracy and resolution are you trying to attain for a kitchen timer?
You could have used an RTC and then just used one UNO and software to load, display and monitor variables.

What are you using for a power supply?

Thanks.. Tom.. :slight_smile:

Q2 is supposed to turn off master uC and display and it seemed to accomplish it, judging by the current consumption. Where am I wrong?
I know I could have used RTC, but I really wanted to use hardware timers for something, and this project seemed to be a great candidate.
±10 sec would be alright, but I would rather have ±1 sec.
I wanted to use 3xAAA batteries.
Thanks again!
EDIT:
And I'm using the 3.3 volt pro mini bootloader.

Hi,
Disconnecting the gnd from the regulator does not turn it off.
It just turns it into a resistor for current to continue flowing through it , Vin to Vout and then to gnd through the 328 and the display.
You do not know if the 328 has properly turned off and if on "wakeup" that it is really getting the proper supply voltage increase to trigger a RESET.

Measure Vcc on the 328 when you have it "switched off" to check that there isn't any supply voltage.

Tom.... :slight_smile:

Thanks for spotting mistake in my circuit!
However, it doesn't influence the bug with waking up - µC still crashes after few power down-and-up cycles even with the rest of the circuit disconnected.
Assuming the bootloader is to blame for my problems, how to I change it?

I've checked again. The issue I referred to affected the watchdog timer (which you are not using since you are using interrupts to wake the device) so you can ignore my comment. ProMini: Reboot loop when using watchdog · Issue #150 · arduino/ArduinoCore-avr · GitHub

In the mean time, you have declared all the variables that are set in the ISR and used elsewhere in your script ( timers etc. ) as volatile ?

I have, but unfortunately with no effect.