Go Down

Topic: Uno sleep awake issue... (Read 85 times) previous topic - next topic

Mogaraghu

I have a simple code to count incoming digital pulses. The maximum frequency is less than 500Hz. What i want to do is count these pulses and show the client in a format he needs. Right now simply printing to Serial.

Also this instrument is battery operated and to conserve power i want to sleep when no pulse is there. And just to avoid the CPU going to sleep when there is a small gap in pulse arrival i have a logic implemented to start a 2 sec countdown.

This is where i have a problem - during trials i intentionally stop the pulse to see the effect of it. The time the CPU takes to go to sleep is not exactly 2 sec always... any reason can be seen ??

Query : Can i remove this "stayAwakeSec" logic altogether [or] improve it to be reliable ?

Code: [Select]

#include <avr/sleep.h>

volatile unsigned long pulseOfGFM ;
unsigned long loopMillis;
unsigned int loopInterval = 500;
unsigned long elapsed_msec ;

byte interruptPin   = 2;
byte lowBatLED      = 5;
byte gfmPulse       = 6;

boolean run_CPU ;
boolean flashState ;

unsigned int stayAwakeSec = 2;  // Remain ON for 2 sec when there is gap in pulses..to avoid frequent sleep - awake..

//###########################################################
void setup() {
  Serial.begin(9600);
  Serial.println(F("*** STARTING PULSE COUNTER *** "));

  pinMode(gfmPulse, OUTPUT);
  pinMode(lowBatLED, OUTPUT);

  pinMode(interruptPin, INPUT_PULLUP);                         //Setup the Interrupt
  attachInterrupt(digitalPinToInterrupt(interruptPin), wakeNcount, RISING);

  run_CPU = 1;                                                 // Setup and remain awake for some time !( remove this based on need )

  digitalWrite(gfmPulse, HIGH);                                // Check health of LEDs...
  digitalWrite(lowBatLED, HIGH);
  delay(1000);
  digitalWrite(gfmPulse, LOW);
  digitalWrite(lowBatLED, LOW);
}

//###########################################################
void loop(void)
{
   if ( millis() - loopMillis > loopInterval)
  {
    loopMillis = millis();
    batteryCheck();
    wakeOrSleep();
  }
} // End of Loop...

//###########################################################

// ISR for Interrupt 0. Wake the CPU and count the GFM pulses

void wakeNcount()
{
  static bool ledState;
  run_CPU = 1;
  pulseOfGFM += 1 ;
  ledState = !ledState;
  digitalWrite( gfmPulse, ledState);
}

//***********************************************************

// FUnction to check battery and flash LED if less than a particular volts..
// result is in mV ...so 5 V is 5000

void batteryCheck()
{
  const long IntRefVolt = 1100;                              // Adjust to suit your board BG volts
  ADMUX = bit (REFS0) | bit (MUX3) | bit (MUX2) | bit (MUX1);
  ADCSRA |= bit( ADSC );                                     // Start conversion
  while (ADCSRA & bit (ADSC)) { }                            // wait for conversion to complete
  int results = (((IntRefVolt * 1023) / ADC) + 5) ;
  if ( results < 3200 )                                      // Set for 3.2V as low point...
  {
    flashState = !flashState;
    digitalWrite(lowBatLED, flashState);
  }
}

//***********************************************************

// Function to decide if to sleep or wake and print pulses to serial...

void wakeOrSleep()       
    {
      if (run_CPU == 1)
      {
        sleep_disable();
        if ( (millis() - elapsed_msec) >= stayAwakeSec * 1000 ) run_CPU = 0;
        Serial.println( pulseOfGFM);
      }

      else
      {
        elapsed_msec = millis();
        digitalWrite(gfmPulse, LOW);
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
        sleep_mode();
      }
    }
   
//***********************************************************
   


J-M-L

#1
Aug 18, 2017, 01:05 pm Last Edit: Aug 18, 2017, 01:06 pm by J-M-L
run_CPU probably needs to be volatile (and don't use 1 or 0 for a boolean even if that works and is the same, use true and false that will make it more readable)


why do you do the maths all the time ? stayAwakeSec * 1000 --> put 2000 in stayAwakemilliSec and declare that as const unsigned long
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

Mogaraghu

I intentionally allowed the CPU to do the maths and make it easy for the human to enter direct the required seconds. Just to avoid mistakes with too many zeros.. afterall the CPU is going to sleep most of the time. Let it do some work ....

I guess I notice the goof up... when going to sleep at say 10.00.00 Hrs, i save the millis() value to elapsed_msec variable. Now maybe i resume the pulses at 10.00.05 hrs. The saved elapsed_msec variable is now irrelevant to do the effective timing.

Needs some workaround... or maybe i will fully eliminate the elapsec_sec functionality altogether. Wake for every pulse ....

J-M-L

#3
Aug 20, 2017, 05:19 am Last Edit: Aug 20, 2017, 05:53 am by J-M-L
Well... if the user can change that and puts 66 there your code has a bug (integer overflow)

you could (no obligation of course) have coded it this way:
Code: [Select]

const unsigned long nbSec = 2; // user can edit this one
const unsigned long nbMilliSec = nbSec * 1000ul; // hide the complexity, but done at compile time and no integer overflow
....
//now can use nbMilliSec



Not sure I get your point - it Depends what type of sleep you chose but in the current code, with SLEEP_MODE_PWR_DOWN the Timer0 clock used to count millis() will not be running anymore so millis() does not increment whilst sleeping

Besides run_CPU needing to be volatile, when you go through the loop your wait for half a second before going to check wakeOrSleep() and in there  if 2 seconds or more have elapsed you set the run_CPU flag, but that flag will only be checked again from main loop half a second later before going to sleep, if no interrupt has occurred in between, so technically you go to sleep 2.5 seconds after the last pulse (plus the time to read the ADC). Is that the lag you see?

You have a few things to be aware of

- you'll be interrupted 500 times per seconds when the signal comes, during that interrupt millis() wont count so you will drift a bit over time (should not be  visible for your code though). Microsecond will stay  accurate in interrupt

- sleeping a UNO does not save much battery as savings are overshadowed by the overhead of the voltage regulator and USB interface chip. (See Nick's article -->Need to go for a bare minimum arduino on a board. turning off the ADC before sleeping and activating it again after sleep would save further energy as well as some extra other things)



Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

Go Up