How do I do a "wake on longpress" after putting Atmga327pu to sleep

I have a timer circuit using a few buttons to control it, a nokia 5110 display and a AtMega328PU (and xtal & caps). To turn the timeroff I have used a longpress (2 sec) of input D2 using the Onebutton library:

  attachInterrupt(0, pinInterrupt, LOW); 

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();

Now i want to wake it up using a button press via interupt on pin D2, the same button as I use to put it to sleep. This is also the menu navigation button for Down. This is not the problem, I have managed to wake it up using

leep_disable();

detachInterrupt(0);

But when I only touch the button the timer wakes up again, how can I manage a wake up after a longpress (2 sec) on the button? This is a small timer the is going with me to the range in my rangebag, and I don’t want it to wake up if I shake my rangebag.

I also have a idle timer, it the timer is unused for one hour, it turns it self off to save power.

The circuit runs on two 1,5V AAA batteried and is pulling 6,2mA running and 17µA when sleeping, almost nothing… :smiley:

The way to wake up after sleeping is to use an interrupt. There is no longpress interrupt. You could design one with hardware.

Or you could just go back to sleep if the button hasn't been pressed long enough. Use the pin change interrupt and record button down time, go back to sleep, when the button is released check the delta time and go back to sleep again if it isn't long enough.

Thanks TanHadro! I know that there is no longpress interrupt, thats why I asked if someone got an idea how to do it. And your way sounds like it could work. I understand the theory of what you are proposing, but I have no idea how to write that code. What is the delta time, and how do I find it?

Could you please show me an example? Please.

Here is my longpress routine, what can I do with it to make a longpress “on”

void PressDown()
{
Serial.println(“Sleep”);
myGLCD.clrScr();
myGLCD.update();
delay(1000);
digitalWrite(dispVcc, 1); // Turn the display off
attachInterrupt(0, pinInterrupt, LOW);

//
// Saving pover
//
cbi( ADCSRA,ADEN ); // switch Analog to Digitalconverter OFF to save mA
for (int temp = 8; temp < 13; temp ++) // Setting all the outputs to LOW, turning off the display (saving mA)
{
digitalWrite(temp, LOW);
}

//
// Put to sleep
//
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode(); // Good night

//
// Wake up again
//
sleep_disable(); // Wake from sleep

delay(500); // just wait a bit before continue
detachInterrupt(0); //remove interrupt

//
// Turn everything back to start
//
digitalWrite(dispVcc, 1); // Turn the display on
myGLCD.InitLCD();
sbi( ADCSRA,ADEN );
// LCD5110 myGLCD(8,9,10,11,12);
myGLCD.drawBitmap(0, 0, BootLogo, 84, 48);
myGLCD.update();
delay(1500); //Delay before moving past bootscreen
myGLCD.clrScr();
myGLCD.update();

MainMenu(); //Go to drawing of main menu
mWhatMenu = 1; // To choose rootmenu, submenu or timer
mRootMenu = 1; // Rootmenu containing the icons for ppc-1500, 600 and 480
mHowManyItems = 3; // Hown many items is there in the different menus
mSubMenu = 1; // Submenu containing all the matches and stages
whatToCount = 0; // 0 = Counter not started, 1 = count 3 sec, 2 = count XX sec
counting = 0;

sleepSec = 0;
MainMenu();
}

Delta time is just the difference between time stamps. You find them by calling millis() or micros() at different times and subtracting. You could actually do it using int0 and LOW if you don’t mind staying awake for as long as the button is down.

// Untested Code

  unsigned long wakeTimeStamp;

  // 
  //  Put to sleep
  //
  do
  {
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sleep_mode();  //  Good night

    //
    //  Wake up again
    //
    sleep_disable();     //  Wake from sleep
    wakeTimeStamp = millis();
    while (digitalRead(2) == LOW);  // loop while the button is down
  } while (millis() - wakeTimeStamp < 2000L);  // If it wasn't long enough, go back to sleep.  FixMe: change 2000L from 2 seconds to whatever your longpress is
  delay(500);          //  just wait a bit before continue
  detachInterrupt(0);  //remove interrupt
...

I would have thought that if your intent is to keep the Arduino asleep as much as possible, it would probably be best to add an external RC circuit with a big time constant so that the interrupt only triggers when the switch is operated for long enough.

Thank you TanHadron! XD

Those lines of code worked very nice! I don’t understand all of your code, but it worked! What does the “L” stand for in

while (millis() - wakeTimeStamp < 2000L)

The reason I use sleep is not to save power, but to hav a way to turn the unit off. In sleep mode, it can be off (sleeping) for a long time on a pair of AAA batteries. Doing it software style means I can make a spimpler pcb. I haven’t made a pcb for 25 years, so I need to keep it simple! The button is also a part of the menu buttons, so I cant have a RC circuit on one of the buttons.

After more testing, if found that it failed. It doens’t work as planned. Need some more testing.

btw. how can I use serial monitor on a breadboard project. I don’t have a simple way to program the Atmega328pu, I have to remove it from my breadboard and put it into my arduino programmer shield. Can I use the ICSP port on the programmer? How??? I am confused now!

I can now see the problem, I thought you code was plug and play but the while loops has no loops… Or is it me thats going in a loop inside my head? :fearful:

I tried to put some { } into the code to make it work, but I can’t think long enough to grasp the way the while loops work. I know this, but my head just won’t work with me :cold_sweat:

Where should I put this sleep part into my code. This is what I need to execute if the button is released if held under 2 sec. If held over 2 sec, just move past and continue the program.

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();

here is the code to check if the button is held over 2 sec. Where should I insert the code above? :blush:

  wakeTimeStamp = millis();
  while (digitalRead(2) == LOW)
  {  
    while (millis() - wakeTimeStamp < 2000L);  
  }
 
  detachInterrupt(0);

Normally I would use a “if” statement, but I cant find a way to do that neither…

I built code to fit in where your code was. Except the declaration of the wakeTimeStamp, which I usually put at the start of the function. Let me integrate it for you:

void PressDown()
{
  unsigned long wakeTimeStamp;

  Serial.println("Sleep");
  myGLCD.clrScr();
  myGLCD.update();
  delay(1000);
  digitalWrite(dispVcc, 1);    //  Turn the display off
  attachInterrupt(0, pinInterrupt, LOW); 

  //
  //  Saving pover
  //
  cbi( ADCSRA,ADEN );    // switch Analog to Digitalconverter OFF to save mA
  for (int temp = 8; temp < 13; temp ++)  //  Setting all the outputs to LOW, turning off the display (saving mA)
  {
    digitalWrite(temp, LOW);
  }

  // 
  //  Put to sleep
  //
  do
  {
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sleep_mode();  //  Good night

    //
    //  Wake up again
    //
    sleep_disable();     //  Wake from sleep
    wakeTimeStamp = millis();
    while (digitalRead(2) == LOW);  // loop while the button is down
  } while (millis() - wakeTimeStamp < 2000L);  // If it wasn't long enough, go back to sleep.  FixMe: change 2000L from 2 seconds to whatever your longpress is

  delay(500);          //  just wait a bit before continue
  detachInterrupt(0);  //remove interrupt


  //
  //  Turn everything back to start
  //
  digitalWrite(dispVcc, 1);    //  Turn the display on
  myGLCD.InitLCD();
  sbi( ADCSRA,ADEN ); 
  //  LCD5110 myGLCD(8,9,10,11,12); 
  myGLCD.drawBitmap(0, 0, BootLogo, 84, 48);
  myGLCD.update();
  delay(1500);      //Delay before moving past bootscreen
  myGLCD.clrScr();
  myGLCD.update();

  MainMenu();    //Go to drawing of main menu
  mWhatMenu = 1;       //  To choose rootmenu, submenu or timer
  mRootMenu = 1;       //  Rootmenu containing the icons for ppc-1500, 600 and 480
  mHowManyItems = 3;   //  Hown many items is there in the different menus
  mSubMenu = 1;        //  Submenu containing all the matches and stages
  whatToCount = 0;     //  0 = Counter not started, 1 = count 3 sec, 2 = count XX sec
  counting = 0;
  
  sleepSec = 0;
  MainMenu();
}

The do {} while(); construct is there to make sure that if you push a button and don’t hold it for long enough, the processor goes back to sleep. It wakes up every time you push a button. It just doesn’t STAY awake.

I still can't get it to work.

Inside the do {} while(); is first the lines to initiate the sleep mode:

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sleep_mode();  //  Good night

then, right after there is the line to get it to wake up again:

sleep_disable();     //  Wake from sleep

Then at last you wrote the lines to check if there is a long press. Those lines works, all the lines work, but not combined.

The circuit is beeing looped inside the do-while and gooing to sleep, awaking again. If the while statement is not meet, go to sleep again, and so on...

This will reset the timer, will it not? Anyway, it doesn't work for me. I want to put a "else" statement into the mix, but I don't know how to achive that!?!

How I see the program to go. 1 - go to sleep 2 - interrupt happening 3 - is the button pressed for 2 sec? - Yes, go to step 4 - No, go back to step 1 4 - Wake up! 5 - resume the programming

I am to noob to find a way to do this

Well, there is other stuff going on. If the timer interrupt is still enabled, it would be waking up the processor instead of (or in addition to) your interrupt 0 push button. If the timer interrupt is not still enabled, the millis() code is not going to increment, and the do {} while() loop will never exit. Not sure how that is being handled since you didn't post that part of the code.

Actually, the comment on the sleep_disable() line is not correct. sleep_disable() does NOT wake up the processor. Only an interrupt will do that. sleep_disable() is used to reset the sleep enable bit so the code doesn't inadvertently go to sleep some other place in the code. Notice that if the button doesn't stay down long enough, the loop calls sleep_enable() again just before going back to sleep.

So is the problem now that it isn't waking up at all, or that it is waking up too much, or that it isn't breaking out of the do {} while() loop?