Is this AVR sleep code correct?

I need my nano to run a motor for 20 seconds (via l298n), then stop, go to sleep for 8 seconds for 99 times, on the 100th time, it should wake up and run the motor again, then continue with its 8 second wakes. The 8 second wakes is to pulse the battery pack so it wont fall asleep.

I just want to double check before running this on the nano since Ive read that sleeping the chip the wrong way may ruin it.

My code:

#include <avr/wdt.h>            // library for default watchdog functions
#include <avr/interrupt.h>      // library for interrupts handling
#include <avr/sleep.h>          // library for sleep
#include <avr/power.h>          // library for power control

int nbr_sleeps=0; 

#define led 13 //used only as indicator
#define motorPin 10
#define pulsePin 8

ISR(WDT_vect){
        // not hanging, just waiting
        // reset the watchdog
        wdt_reset();
}

void configure_wdt(void){
  cli();                           // disable interrupts for changing the registers
  MCUSR = 0;                       // reset status register flags
                                   // Put timer in interrupt-only mode:                                       
  WDTCSR |= 0b00011000;            // Set WDCE (5th from left) and WDE (4th from left) to enter config mode,
                                   // using bitwise OR assignment (leaves other bits unchanged).
  WDTCSR =  0b01000000 | 0b100001; // set WDIE: interrupt enabled
                                   // clr WDE: reset disabled
                                   // and set delay interval (right side of bar) to 8 seconds
  sei();                           // re-enable interrupts
 
}

void sleep(int ncycles){  

 nbr_sleeps =+;
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 power_adc_disable();
 
 sleep_mode();

 // CPU is now asleep and program execution completely halts!
 // Once awake, execution will resume at this point if the
 // watchdog is configured for resume rather than restart

 // When awake, disable sleep mode
 sleep_disable();

 power_all_enable();

}

void setup(){
  
  // use led 13 and put it in low mode
  pinMode(led, OUTPUT);
  pinMode(pulsePin, OUTPUT); // for pulse
   
  // configure the watchdog
  configure_wdt();

}

void loop(){
  // sleep for 8 seconds
  sleep(1);

  digitalWrite(pulsePin, HIGH);
  delayMicroseconds(100); // Approximately 10% duty cycle @ 1KHz
  digitalWrite(pulsePin, LOW);
  delayMicroseconds(1000 - 100);

  if (nbr_sleeps==99) runMotor();
}

void runMotor() {
    // blink three times to indicate motor will run
 digitalWrite(led, HIGH);   
 delay(500);               
 digitalWrite(led, LOW);   
 delay(500); 
 digitalWrite(led, HIGH);   
 delay(500);               
 digitalWrite(led, LOW);   
 delay(500); 
 digitalWrite(led, HIGH);   
 delay(500);               
 digitalWrite(led, LOW);   
 delay(500); 

  //Turn motor on
 digitalWrite(motorPin, HIGH);
 delay(20000);
 digitalWrite(motorPin, LOW);
}

I run this core on all my code,

#include    <avr/sleep.h>
enum pulse  {p16, p32, p64, p125, p25, p50, p1, p2, p4 = 32, p8};
#define wdr() __asm__ __volatile__ ("wdr \n")
byte all_ok = 1;

main or setup
  set_sleep_mode(SLEEP_MODE_IDLE); // or whatever
  set_pulse(p8);  // p8 is for 8 seconds

loop
  all_ok = 1;
  wdr();
  sleep_mode();

void set_pulse(enum pulse p)  // turns WDT on and sets duration
{
    wdr();
    WDTCSR = (1 << WDCE) | (1 << WDE);
    WDTCSR = (1 << WDIE) | (p << 0);
}

ISR(WDT_vect)
{
    if (all_ok) (all_ok = 0); // all_ok is set as a matter of normal ops
    else    // if all_ok is not set, reset system
    {
        WDTCSR = (1 << WDCE) | (1 << WDE);
        WDTCSR = (1 << WDE);
    }
    wd_count++; // arbitrary counter - user defined
    Set(wd_wakeup); // arbitrary flag - user defined
}

Works un-modified on 328p and 2560.

This code:

nbr_sleeps =+;

won't compile. It needs to be

nbr_sleeps ++; // or nbr_spleeps += 1;

Also, you never reset nbr_sleeps back to zero after it reaches 99.

Ok im using this code and instead of a motor and a led, Im using 2 leds for now. So one led blinks every 8 seconds, 1 sleep cycle, and the other blinks every 100 cycles of 800 seconds.

The 8 second led blinks fine, every 8, but the battery pack turns off after about 1.5 minutes:

#include <avr/wdt.h>            // library for default watchdog functions
#include <avr/interrupt.h>      // library for interrupts handling
#include <avr/sleep.h>          // library for sleep
#include <avr/power.h>          // library for power control

int nbr_sleeps=0; 

#define led 13
#define motorPin 10
#define pulsePin 7

ISR(WDT_vect){
        // not hanging, just waiting
        // reset the watchdog
        wdt_reset();
}

void configure_wdt(void){
  cli();                           // disable interrupts for changing the registers
  MCUSR = 0;                       // reset status register flags
                                   // Put timer in interrupt-only mode:                                       
  WDTCSR |= 0b00011000;            // Set WDCE (5th from left) and WDE (4th from left) to enter config mode,
                                   // using bitwise OR assignment (leaves other bits unchanged).
  WDTCSR =  0b01000000 | 0b100001; // set WDIE: interrupt enabled
                                   // clr WDE: reset disabled
                                   // and set delay interval (right side of bar) to 8 seconds
  sei();                           // re-enable interrupts
 
}

void sleep(int ncycles){  

 nbr_sleeps ++;
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 power_adc_disable();
 
 sleep_mode();

 // CPU is now asleep and program execution completely halts!
 // Once awake, execution will resume at this point if the
 // watchdog is configured for resume rather than restart

 // When awake, disable sleep mode
 sleep_disable();

 power_all_enable();

}

void setup(){

  Serial.begin(9600);
  // use led 13 and put it in low mode
  pinMode(led, OUTPUT);
  pinMode(pulsePin, OUTPUT); // for pulse
   
  // configure the watchdog
  configure_wdt();

}

void loop(){
  Serial.println("will sleep...");
  // sleep for 8 seconds
  sleep(1);

  digitalWrite(pulsePin, HIGH);
  delayMicroseconds(1000); // Approximately 10% duty cycle @ 1KHz
  digitalWrite(pulsePin, LOW);
  delayMicroseconds(1000 - 100);
  
  Serial.println("pulsed led...");
  
  if (nbr_sleeps==99) {
    Serial.println("running motor...");
    runMotor();
    nbr_sleeps=0;
  }
}

void runMotor() {

  //Turn motor on
 digitalWrite(motorPin, HIGH);
 delay(20000);
 digitalWrite(motorPin, LOW);
}

Is the battery pack like what you would use to charge a phone? Most battery packs will automatically switch off after a timeout when no load is detected. It may be that the load imposed by the brief 'wakeup' is not enough to keep the battery pack turned on. Not a bad feature to preserve the life of the battery. You may find that a small delay during your wakeup (try 1ms to start) will keep the battery pack alive.

Yes, that is what Im trying to do, keep it awake. So I could either add it in the sleep() function after it wakes up:

void sleep(int ncycles){  

 nbr_sleeps ++;
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 power_adc_disable();
 
 sleep_mode();

 // CPU is now asleep and program execution completely halts!
 // Once awake, execution will resume at this point if the
 // watchdog is configured for resume rather than restart

 delay(1000);
 // When awake, disable sleep mode
 sleep_disable();

 power_all_enable();

}

or in the loop before it sleeps:

void loop(){
  Serial.println("will sleep...");
  delay(1000);
  // sleep for 8 seconds
  sleep(1);

  digitalWrite(pulsePin, HIGH);
  delayMicroseconds(1000); // Approximately 10% duty cycle @ 1KHz
  digitalWrite(pulsePin, LOW);
  delayMicroseconds(1000 - 100);
  
  Serial.println("pulsed led...");
  
  if (nbr_sleeps==99) {
    Serial.println("running motor...");
    runMotor();
    nbr_sleeps=0;
  }
}

Your problem caused me to run a test. My battery pack has three discernible modes: off, sleep and on. When using SLEEP_MODE_IDLE, the pack remained on all the time. With SLEEP_MODE_PWR_DOWN, the pack would come on for about 12 seconds (WDT set at 8 ) and then go to sleep for 2-3 minutes before turning on again for another 12 seconds (or so). All the while the UNO kept running. This I know as I flash the LED for 30ms during wakeup. That 30ms LED flash may be enough to keep the pack alive as I also know that under normal circumstances, when a device is removed from the pack it switches (after a delay) to sleep mode before turning off.

FWIW, in the time it's taken to run the tests and post a reply, the SLEEP_MODE_PWR_DOWN test has been running for about 22 minutes.

Last note, test running for more than 1 hour now - no change.

I added a 1 second delay with the LED ON, so that the pulsePin which is connected to an LED, stays on for 1 second, but even then the battery pack powers down after 1.5 minutes. Here is the code Im using:

#include <avr/wdt.h>            // library for default watchdog functions
#include <avr/interrupt.h>      // library for interrupts handling
#include <avr/sleep.h>          // library for sleep
#include <avr/power.h>          // library for power control

int nbr_sleeps=0; 

#define led 13
#define motorPin 10
#define pulsePin 7

ISR(WDT_vect){
        // not hanging, just waiting
        // reset the watchdog
        wdt_reset();
}

void configure_wdt(void){
  cli();                           // disable interrupts for changing the registers
  MCUSR = 0;                       // reset status register flags
                                   // Put timer in interrupt-only mode:                                       
  WDTCSR |= 0b00011000;            // Set WDCE (5th from left) and WDE (4th from left) to enter config mode,
                                   // using bitwise OR assignment (leaves other bits unchanged).
  WDTCSR =  0b01000000 | 0b100001; // set WDIE: interrupt enabled
                                   // clr WDE: reset disabled
                                   // and set delay interval (right side of bar) to 8 seconds
  sei();                           // re-enable interrupts
 
}

void sleep(int ncycles){  

 nbr_sleeps ++;
 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 power_adc_disable();
 
 sleep_mode();

 // CPU is now asleep and program execution completely halts!
 // Once awake, execution will resume at this point if the
 // watchdog is configured for resume rather than restart

 // When awake, disable sleep mode
 sleep_disable();

 power_all_enable();

}

void setup(){

  Serial.begin(9600);
  // use led 13 and put it in low mode
  pinMode(led, OUTPUT);
  pinMode(pulsePin, OUTPUT); // for pulse
   
  // configure the watchdog
  configure_wdt();

}

void loop(){
  Serial.println("will sleep...");
  // sleep for 8 seconds
  sleep(1);

  digitalWrite(pulsePin, HIGH);
  //delayMicroseconds(1000); // Approximately 10% duty cycle @ 1KHz
  delay(1000);
  digitalWrite(pulsePin, LOW);
  delayMicroseconds(1000 - 100);
  
  Serial.println("pulsed led...");
  
  if (nbr_sleeps==99) {
    Serial.println("running motor...");
    runMotor();
    nbr_sleeps=0;
  }
}

void runMotor() {

  //Turn motor on
 digitalWrite(motorPin, HIGH);
 delay(20000);
 digitalWrite(motorPin, LOW);
}

Does your battery pack keep running beyond 15 minutes if you have it connected with no sleep?

If i plug in my phone to the battery pack, the battery pack keeps charging my phone for quite a while, I dunno how long, I haven't timed it but it gets it up to 70-80% charge from 20% or so, so it takes about an hour or more.

If I plug the nano to it, it shuts down, with this code and the LED, after 1.5 minutes or so.

If i plug it into the nano without the sleep code, with a different code like this one that sends rf data constantly, the nano turns off after about 1.5 minutes as well...

// Include RadioHead Amplitude Shift Keying Library
#include <RH_ASK.h>
// Include dependant SPI Library 
#include <SPI.h> 
#include "Wire.h"       //For communicate
#include "I2Cdev.h"     //For communicate with MPU6050
#include "MPU6050.h"    //The main library of the MPU6050

//Define the object to access and cotrol the Gyro and Accelerometer (We don't use the Gyro data)
MPU6050 mpu;
int16_t ax, ay, az;
int16_t gx, gy, gz;

//Define packet for the direction (X axis and Y axis)
int data[2];

// Create Amplitude Shift Keying Object
RH_ASK rf_driver;

void setup() {
  Serial.begin(9600);
  mpu.initialize();              //Initialize the MPU object

  // Initialize ASK Object
  rf_driver.init();

}

void loop() {
  delay(2000);  // Delay so DHT-22 sensor can stabalize

  //With this function, the acceleration and gyro values of the axes are taken. 
  //If you want to control the car axis differently, you can change the axis name in the map command.
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  
    //In two-way control, the X axis (data [0]) of the MPU6050 allows the robot to move forward and backward. 
  //Y axis (data [0]) allows the robot to right and left turn.
  data[0] = map(ax, -17000, 17000, 300, 400 ); //Send aX axis data
  data[1] = map(ay, -17000, 17000, 100, 200);  //Send aY axis data
    
  rf_driver.send((const uint8_t*)data, sizeof(data));
  //(uint8_t *)msg, strlen(msg));
  rf_driver.waitPacketSent();
  Serial.println(data[0]);
  Serial.println(data[1]);
    
}

One thing you'll have to correct in your code is in function sleep, nbr_sleeps =+; should be nbr_sleeps ++;.
Take set_sleep_mode out and put it in setup.
The parameter you're passing does nothing, but I'll assume that's for code yet to come?

I've made the changes I mentioned above and included nbr_sleeps = 0; at the end of runMotor as it needs to be reset. Compiles fine (with the exception of the unused parameter).

I added a 30ms led flash during each wakeup.

In loop I changed if (nbr... to if (nbr_sleeps==3) runMotor; and it runs fine. Haven't tested it for 15 minutes with my battery pack, but I see no reason that it should stop.

This is the code I'm running (without comments);

#include <avr/wdt.h> 
#include <avr/interrupt.h> 
#include <avr/sleep.h> 
#include <avr/power.h>
#include <util/delay.h>

#if (defined (__AVR_ATmega328P__) || defined (__AVR_ATmega328__))
    #define UNO
    #define LED_PORT        PORTB
    #define LED_PIN         PB5
    char BOARD[]      = {"m328p"};
#endif

int nbr_sleeps=0;

#define led 13
#define motorPin 10
#define pulsePin 8

inline void     flash_led()     {LED_PORT |= (1 << LED_PIN); _delay_ms(30);
                                    LED_PORT &= ~(1 << LED_PIN);}
ISR(WDT_vect)
{
    wdt_reset();
}

void configure_wdt(void)
{
    cli();
    MCUSR = 0;
    WDTCSR |= 0b00011000;
    WDTCSR =  0b01000000 | 0b100001;
    sei();
}

void sleep(int ncycles)
{ 
    nbr_sleeps ++;
    power_adc_disable();
    sleep_mode();
    flash_led();
    sleep_disable();
    power_all_enable();
}

void setup()
{
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    pinMode(led, OUTPUT);
    pinMode(pulsePin, OUTPUT);
    configure_wdt();
}

void loop()
{
    sleep(1);
    digitalWrite(pulsePin, HIGH);
    delayMicroseconds(100);
    digitalWrite(pulsePin, LOW);
    delayMicroseconds(1000 - 100);
    if (nbr_sleeps==3) runMotor();
}

void runMotor() 
{
    digitalWrite(led, HIGH);   
    delay(500);               
    digitalWrite(led, LOW);   
    delay(500);
    digitalWrite(led, HIGH);   
    delay(500);               
    digitalWrite(led, LOW);   
    delay(500);
    digitalWrite(led, HIGH);   
    delay(500);               
    digitalWrite(led, LOW);   
    delay(500);
    //Turn motor on
    digitalWrite(motorPin, HIGH);
    delay(20000);
    digitalWrite(motorPin, LOW);
    nbr_sleeps=0;
}

And it works fine.

The only other thing I'm going to suggest just now, is to set your sleep mode to idle and run your test again. See if that keeps your battery pack alive.

It's after 0100 where I am and I need a few hours rest.

I do not think that you can brick your arduino with sleep (please correct me if I'm wrong), but you can brick it by changing fuses - but I'm not sure that is possible on a nano.

Whenever I program a stand alone AVR/MCU, I dedicate a single digital input pin as sort of a "sleep aborter" by wiring a R10K from GND to PIN (not really nescessary). When I want to cancel any sleep, I rewire it to R1K from VIN to PIN. The code would be something like:

#define SLEEP_ABORT_PIN 5

void loop()
{
  if (digitalRead(SLEEP_ABORT_PIN) == HIGH) return; //Abort regular operation
  //Perform regular operation
}

This should ensure that you always have an escape route when things go bad :slight_smile:

Different battery packs have radically different behavior with regards to turning themselves off under low/no load.

Some will only turn off if the load is damned near zero, so waking up and blinking an led would be enough. Others require loads on the order of 100mA to stay awake (I have one that won't charge my Go-tcha (a fitbit-sized companion device for pokemon go - it turns off because charging the tiny battery doesn't draw enough current to keep it from turning off). And of course, this parameter is never spec'ed by the manufacturer.

You can't brick an arduino using sleep mode. You can nearly-soft-brick nano (except the "new bootloader" official ones manufactured this year) and pro mini with the WDT if it's used to generate a reset event (when this happens the WDT stays on after the reset, with the minimum timeout, and if the bootloader isn't smart enough to check for this and turn it off, it will reset-loop while the bootloader is running) - this can be recovered from by manually resetting to upload (IIRC even reset doesn't turn it off, so you need to power cycle it and hold reset until the right point in the upload process) or by rebootloading with an ISP programmer. It can be prevented entirely by bootloading them as an Uno and treating them as such within the IDE.

Danois90:
I dedicate a single digital input pin as sort of a "sleep aborter" by wiring a R10K from GND to PIN (not really nescessary)

Yes it is, otherwise the pin will float and reads will yield a random value. Better to set it input_pullup, and trigger the abort by grounding it.

But as noted above, it's not possible to brick a board with sleep mode. The reset pin will always bring it back.

@DrAzzy - Good info.

Thanks Dr Watson

Even with sleep idle the battery pack shuts down after 1.5 minutes

You don't think I've heard all the Sherlock jokes. One reason I keep my education quiet.

Seems like I've got myself a pretty useful battery pack, the only one in fact that hasn't given op the ghost. It goes into it's own power down mode while still maintaining a trickle current. Wakes up every couple of minutes for 12 seconds to see if anything is still attached and keeps on plugging. Seems to be branded for the local market (Indonesia) using the same name as the local beer, All I can say is I got it at Ace Hardware.