c++arduinoavrwakeupsleep-mode

LowPower library: Powerdown (sleep) an ATMega32u4 works but wakeup doesn't happen


Situation:

I made/developing this controller (with many functions) with use of a Pro Micro (ATMega32u4) and want to extend it with a wake-up functionality when touching it (with use of a touch sensor). This all to save some energy when not used. So when the device is picked up/touched, the device woke up. When there is no contact, it will go to sleep after a while. The reach distance of the touch sensor will be extended with a small strip of copper around the case.

Picture of the device:

Image of device

I use pin 9 of the touch sensor and the pin 16 of the IR-sensor to wake up but it doesn't work. Both device functions works OK, I can read the touch state (low=untouched or high=touched) and I can receive IR-commands because it is already fully implemented. When receiving a signal, those pins will be high for some period.

Software question:

Is there something wrong with my code (wrong parameters?)? The device goes to sleep when executing this code (but never woke up):

#include <avr/sleep.h>         // To enter sleep mode, save power
#include <LowPower.h>          // To enter sleep mode, save power 

......

#define TEP_IR_RECV_PIN         16
#define TEP_PIN_TOUCHSENSOR     9

......
......

void sleep()
{
  attachInterrupt( TEP_PIN_TOUCHSENSOR, wakeup, HIGH );
  attachInterrupt( TEP_IR_RECV_PIN, wakeup, HIGH ); 

  // Enter power down state with ADC and BOD module disabled.
  // Wake up when wake up pin is high.
  LowPower.powerDown( SLEEP_FOREVER, ADC_OFF, BOD_OFF );

  // Disable external pin interrupt on wake up pin.
  detachInterrupt( TEP_PIN_TOUCHSENSOR );
  detachInterrupt( TEP_IR_RECV_PIN ); 
}

Sleep function of my EnjoyPad class (will be triggered when pushing power button on remote control or when reach timeout when not touched or doing anything):

void TEnjoyPad::setSleepMode()
{
  // Notify user device entering sleep mode, beep twice
  setBeep( 200 );
  setBeep( 200, 100, false );

  // To be sure: Trigger end / unhandled down events and reset states
  reset(init);

  // Handle sleep event
  eventSleep();

  // Stop all peripherals
  end();

  // Finally go to sleep, code stops here
  TEP_LIB_FUNCS::sleep();

  // Device woke up
  setBeep( 500, 100, false );

  // Restart all peripherals
  begin();

  // Handle wake up event
  eventAwake();
}

Any ideas what could be wrong? If I need to change a pin for the touch sensor, it is possible but I want to be sure it is the correct pin because I need to solder it (not on a breadboard). Pins left: 10, 14 and A3

NOTICE: I have tested, with just a delay replacement instead of going to sleep, the functions after the sleep are working just fine. So I can hear a beep and device comes on again. There is no problem with the code after sleep(), it just doesn't want to wake-up.


Solution

  • Okay, I figured it out myself. It seems that pin 9 and pin 16 are not usable with attachInterrupt() so it will never work. Also the usage was not correct, it must be something like this:

    attachInterrupt( digitalPinToInterrupt( TEP_PIN_TOUCHSENSOR ), wakeup, HIGH );
    

    Came up with the idea to use a pin change interrupt to wake up the device and this seems to work. However, recovering from sleep mode cannot restore all peripherals without malfunctioning so it needs a hardware reset, it must start from scratch. Also because of changes when sleeping, for example cable disconnected from computer (it also has a battery) and USB connection is lost. Not all of the classes I use are mine and not optimized for such powerdown things.

    Anyway, I change the code to this, just to show you the changes, maybe it can help others too:


    #include <LowPower.h>          // To enter sleep mode, save power 
    #include <avr/wdt.h>           // For watchDog device reset
    
    
    ......
    
    #define TEP_IR_RECV_PIN         16
    #define TEP_PIN_TOUCHSENSOR     9
    
        ......
        ......
    
    namespace TEP_LIB_FUNCS
    {
    
    void pciSetup(uint8_t iPin)
    {
        *digitalPinToPCMSK( iPin ) |= bit( digitalPinToPCMSKbit( iPin ) );  // enable pin
        PCIFR|= bit( digitalPinToPCICRbit( iPin )); // clear any outstanding interrupt
        PCICR|= bit( digitalPinToPCICRbit( iPin )); // enable interrupt for the group
    }
    
    #if defined( TEP_PIN_TOUCHSENSOR )
     #if TEP_PIN_TOUCHSENSOR >= 8 && TEP_PIN_TOUCHSENSOR <= 13 
     ISR(PCINT0_vect) 
     {    
      #define TEP_SLEEP_WAKEUP_PIN_ENABLED 0 
      // Event handler for pins: D8 to D13 
      // Pin change wakeup event handler, does nothing but is required. 
      // Do not remove this.
     } 
     #elif defined(A0) && defined(A5) && TEP_PIN_TOUCHSENSOR >= A0 && TEP_PIN_TOUCHSENSOR <= A5 
     ISR(PCINT1_vect) 
     {    
      #define TEP_SLEEP_WAKEUP_PIN_ENABLED 1 
      // Event handler for pins: A0 to A5 
      // Pin change wakeup event handler, does nothing but is required. 
      // Do not remove this.
     } 
     #elif TEP_PIN_TOUCHSENSOR >= 0 && TEP_PIN_TOUCHSENSOR <= 7 
     ISR(PCINT2_vect) 
     {    
      #define TEP_SLEEP_WAKEUP_PIN_ENABLED 0 
      // Event handler for pins: A0 to A5 
      // Pin change wakeup event handler, does nothing but is required. 
      // Do not remove this.
     } 
     #endif
    #endif
    
    void sleep()
    {
      // Possible pins Micro, Leonardo, other 32u4-based:  0, 1, 2, 3, 7.
      // Because we use all pins already by other functions, we cannot
      // use attachInterrupt(), it doesn't work.
      // see also: https://www.arduino.cc/en/Reference/AttachInterrupt
      // Instead of this we use a change event interrupt to wake up 
      // the device from sleep state.  
      //
      // The device can be woke up by using:
      // - The reset button
      // - The touch sensor
    
      // Okay, lets go
      // We enable interrupts here to be sure it is going to work
     interrupts();
    
     #ifdef TEP_SLEEP_WAKEUP_PIN_ENABLED
      // Set pin change interrupt enabled for sensor pin
      // (See also https://playground.arduino.cc/Main/PinChangeInterrupt)
       // old code: attachInterrupt( digitalPinToInterrupt( TEP_PIN_TOUCHSENSOR ), wakeup, HIGH );
       // old code: attachInterrupt( digitalPinToInterrupt( TEP_IR_RECV_PIN ), wakeup, HIGH ); 
      pciSetup( TEP_PIN_TOUCHSENSOR );
     #endif
    
     // Enter power down state with ADC and BOD module disabled.
     // Wake up when wake up pin has changed.
     LowPower.powerDown( SLEEP_FOREVER, ADC_OFF, BOD_OFF );
    
     // Disable external pin interrupt on wake up pin.
      // old code: detachInterrupt( TEP_PIN_TOUCHSENSOR );
      // old code: detachInterrupt( TEP_IR_RECV_PIN ); 
    }
    
    
     void softReset()
     {
       wdt_enable(WDTO_15MS);  
       while(true) {}
     } 
    
      // Function Implementation
     void init(void)
     {
       MCUSR = 0;
       wdt_disable();
     }
    
    } // end namespace TEP_LIB_FUNCS
    

    And also the sleep function of my EnjoyPad class (will be triggered when pushing power button on remote control or when reach timeout when not touched or doing anything):

    void TEnjoyPad::setSleepMode()
    {
      // Notify user device entering sleep mode, beep twice
     setBeep( 200 );
     setBeep( 200, 100, false );
    
      // To be sure: Trigger end / unhandled down events and reset states
     reset(init);
    
      // Shut down any 
     broadcastSleepMode();
    
      // Handle sleep event
     eventSleep();
    
      // Stop all peripherals
     end();
    
      // Turn off onboard led
     digitalWrite( 13, LOW );
     pinMode( 13, INPUT ); 
    
      // Finally go to sleep, code stops here
     TEP_LIB_FUNCS::sleep();
    
      // Device woke up
     setBeep( 500, 100, false );
    
      // Handle wake up event
     eventAwake();
    
      // Restart whole device, start from scratch.
      // This seems to be the best way to guarantee all 
      // peripherals will be initialized properly
      // and without errors.
     TEP_LIB_FUNCS::softReset();
    }
    

    NOTICE: Restarting this type of MCU is fast, there is no remarkable difference in speed when you wake up and continue or wake up and reset.

    That's it for the moment, tanks for watching ;-)

    ----------

    EDIT: When adding pciSetup( TEP_IR_RECV_PIN ); it also seems to work perfectly (because there is already an interrupt handler defined for it). So the device can also woke up when receiving an IR-command. Didn't expect this was possible but tried it, neat feature.