Go Down

Topic: Is this AVR sleep code correct? (Read 2917 times) previous topic - next topic

Marciokoko

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:

Code: [Select]

#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);
}

DKWatson

#1
Aug 21, 2018, 11:18 pm Last Edit: Aug 21, 2018, 11:19 pm by DKWatson
I run this core on all my code,

Code: [Select]
#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.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

blh64

This code:
Code: [Select]

nbr_sleeps =+;

won't compile.  It needs to be
Code: [Select]

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


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

Marciokoko

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:

Code: [Select]

#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);
}

DKWatson

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.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

Marciokoko

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:

Code: [Select]

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:

Code: [Select]
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;
  }
}

DKWatson

#6
Aug 29, 2018, 03:07 am Last Edit: Aug 29, 2018, 03:07 am by DKWatson
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.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

DKWatson

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.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

DKWatson

Last note, test running for more than 1 hour now - no change.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

Marciokoko

#9
Aug 29, 2018, 06:57 pm Last Edit: Aug 29, 2018, 07:15 pm by Marciokoko
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:

Code: [Select]

#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);
}

DKWatson

Does your battery pack keep running beyond 15 minutes if you have it connected with no sleep?
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

Marciokoko

#11
Aug 29, 2018, 07:51 pm Last Edit: Aug 29, 2018, 07:52 pm by Marciokoko
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...

Code: [Select]
// 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]);
   
}

DKWatson

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?
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

DKWatson

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);
Code: [Select]
#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.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

DKWatson

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.
Live as if you were to die tomorrow. Learn as if you were to live forever. - Mahatma Gandhi

Go Up