Power-off timer

I need to have a timer to switch off power to the Pro Mini (5V) after 5 hours. Also a button to cancel time-out earlier. (The LED output is for test purposes. Later this “high at time-out” will switch off the supply SMPS, or “low” to enable/run.)

Q : Is there a better / simpler / safer way than my sketch?

(I am using LCD and other sketches so I’m not sure if this long timer can/will interfere.)

(Works at 20 minutes but what can happen within 5 hours?)

Thanks very much in advance.

#include <elapsedMillis.h>

int led = A1;
const int buttonPin = A0;
int buttonState = 0  ;

elapsedMillis timer0;

#define interval 1200000

boolean timer0Fired;

void setup() {

  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  timer0Fired = false;
  timer0 = 0; 
}

void loop() {
  pinMode (A0, INPUT_PULLUP) ;
  buttonState  = digitalRead (buttonPin);
  if ((!timer0Fired) && (timer0 > interval)) {
    timer0Fired = true;  
    digitalWrite(led, HIGH); // turn led off after 5 sec
  }
  if (buttonState == LOW)
  {
    digitalWrite (led, HIGH); 
  }
}

My first attempt doesn’t work so I’ve rewritten the sketch. The “run” output keeps the SMPS regulator (off-board) running during the ON period, and a press of the button after start-up cancels the timer correctly.

The only hiccup I can see is that there’s a delay of about 3 secs at startup (boot?) until the “run” output goes high. I can bridge this with a cap but if there’s a better way to have the output HIGH immediately at startup (or quicker than 3 secs) it would be better. Can this be done?

Is it okay to use the analog pins for digital purposes like this?

For testing purposes I have set the timeout period 10 secs but will it work okay at 5 hours?

Also, is the sketch reliable and free from possible interference (delays) with my ADC & LCD display?

Comments good or bad are appreciated. Thanks in advance.

#include <elapsedMillis.h>

int run = A1;
const int buttonPin = A0;
int buttonState = 0  ;
long previousMillis = 0 ;

elapsedMillis timer0;

#define interval 10000

boolean timer0Fired;

void setup() 
{

  pinMode(run, OUTPUT);
  digitalWrite(run, HIGH);
   timer0Fired = false;
  timer0 = 0; 
}
void loop() 
{
  unsigned long currentMillis  =  millis();
  if (currentMillis - previousMillis  >  interval)  
  {
    previousMillis = currentMillis;
  
  }
  buttonState  = digitalRead (buttonPin);
  if ((!timer0Fired) && (timer0 > interval) )   
  {
    timer0Fired = true;  
    digitalWrite(run, LOW); 
  }
  if (buttonState == HIGH)
  {
    digitalWrite (run, LOW); 
  }
}

Newbie10:
The only hiccup I can see is that there’s a delay of about 3 secs at startup (boot?) until the “run” output goes high. I can bridge this with a cap but if there’s a better way to have the output HIGH immediately at startup (or quicker than 3 secs) it would be better. Can this be done?

To answer just the question above ... it's the bootloader. When your Arduino 1st runs the bootloader runs and if there's no program to be loaded (or PC even attached) then after a few seconds your code/sketch starts running. That's the majority of your delay.

You can remove the delay by removing the bootloader program. Of course then you’ll have to load programs via a programmer and the ICSP pins.

https://learn.sparkfun.com/tutorials/in … bootloader

Newbie10:
Comments good or bad are appreciated. Thanks in advance.

OK, what's this do ? It seems to do nothing of any use.
unsigned long currentMillis  =  millis();
  if (currentMillis - previousMillis  >  interval) 
  {
    previousMillis = currentMillis;
 
  }

If A1 refers to the analog pin A1, then why isn’t it declared as a constant like A0 ? Why are you using analog pins as digital inputs and outputs ? They can be, no problem, just curious as to why.

The millis() function returns an unsigned long quantity. You should (ideally) keep the same type variable to store millis() or to compare to any millis() output. That is both previousMillis and interval should be declared as unsigned longs.

This would seem to shut the power off, not cancel the timer and leave power on, when the button is pushed.

if (buttonState == HIGH)
  {
    digitalWrite (run, LOW);
  }

How do you have the “cancel” button wired ? It would seem to use an external pull-down resistor, as is shown in the beginning Arduino tutorials. I wish they didn’t do that. It’s wasteful and most people use a momentary button connecting to ground on a push. That way you can use the internal pull-up resistor and have 1 less component. Do this …

http://arduino.cc/en/Tutorial/InputPullupSerial

Given millis() starts at 0 on powerup, it returns the time, in msec, that the Arduino has been on. No need for any other library function, given the duration is 5 hours (a few msec off won’t matter). So here’s how I’d modify your code.

const int runPin = A1;                //POWER CONTROL PIN
const int buttonPin = A0;             //CANCEL TIMEOUT PIN

unsigned long interval = 18000000;    //5 HRS IN MSEC

boolean timerStopped = false;
boolean buttonState;

void setup()
{
  pinMode(buttonPin, INPUT_PULLUP);  //N.O. MOMENTARY SW TO GND
  pinMode(runPin, OUTPUT);
  digitalWrite(runPin, HIGH);        //ENABLE POWER TO STAY ON
}

void loop()
{
  buttonState  = digitalRead(buttonPin);
  if (buttonState == LOW)           //IF SWITCH PUSHED ...
  {
    timerStopped = true;            //CANCEL POWER OFF TIMER, BUTTON WAS PUSHED
  }
  if ((!timerStopped) && (millis() > interval))   
  {
    digitalWrite (runPin, LOW);     //TIME HAS RUN OUT, TURN OFF POWER
  }


  //any code to run while Arduino is on goes below here
 
}

Okay about the bootloader.

A cap will sort that out.

I’m using most digital pins and no analogs so I thought why not use them?

I tried the ‘button on = LOW’, but as I am switching the SMPS off with the time-out/button it won’t work. (If the supply is off, a LOW signal to switch it on doesn’t do much.)

True, my OFF signal doesn’t clip the timer but it does disconnect supply which amounts to the same thing, but yes - clumsy as far as writing a neat sketch.

I am using a 2m 8-core “data cable” to connect the INPUT UNIT (containing the SMPS, on/off transistor, current, voltage measuring inputs, the ADC, etc.) [left of dotted line] to the DISPLAY UNIT (the Arduino Pro-Mini, LCD and button).

See (simplified) drawing : the “24V” at left side should read “+12…24V”, being main supply (pic was badly taken).

I like your sketch : nice and simple.

All that’s needed now is to change the button from pull-up to pull-down and I will be able to use it.

Many thanks for your help.

Newbie10:
I tried the ‘button on = LOW’, but as I am switching the SMPS off with the time-out/button it won’t work. (If the supply is off, a LOW signal to switch it on doesn’t do much.)

True, my OFF signal doesn’t clip the timer but it does disconnect supply which amounts to the same thing, but yes - clumsy as far as writing a neat sketch.

I'm a little confused by what functions the button is supposed to do. Are you switching the SMPS off with the same button that cancels the time-out/off timer ? Why ? If you can turn the SMPS off via external methods, why cancel the timer to keep it on ? The power will go away and the timer, and any other processing by the Arduino, will halt. I had the idea that the button was to keep the power on, beyond it's 5 hr limit. If it does other functions, I'm not sure my code does what you want it to do.

How do you turn the SMPS on ? If you use the button to turn it on and also for cancelling the timer, then I can see potential problems w/my coding. ie - think what the code does if you hold the button for a “long time”.

As for you transistor circuit … it doesn’t look correct to me. You’re using an NPN as a high side switch and that isn’t going to work. There’s no way to keep the base 1 diode drop above the emitter voltage (12v min) when the “keep on” signal from the Arduino (to the base) is at least 1 diode drop below 5v. You need to use a PNP or a FET, P channel or even an N channel with the appropriate driver circuitry. You can find examples of all of these circuits on this forum or on others. Google is your friend.

One thing to be wary of is the 20 mA current limitation (sourcing or sinking) of an Arduino pin. A big “holding” cap could violate this and smoke the pin. Another method would be to use a 1 shot that triggers w/power up and supplies the needed “keep on” signal in parallel with the one from the Arduino.

The button is to turn the SMPS on and a following press to turn it off. If user wants it on longer than a few minutes the timer must switch off at about 5 hours to conserve the battery. User can press button again after time-out to continue.

My drawing of the transistor was symbolic, like the SMPS. I’ve drawn that part in full now for your comment.

The Arduino’s A1 output is essential to keep the SMPS on after the first (start) button press.

(I didn’t think the 4u7 caps can hurt anything but if you disagree please comment. I have the first RC from the main supply to the button so the button-press only gives a pulse. Without this RC, a button held down too long could be seen by A0 input and thereby cancel the A1 output.)

On the bench it seems to work okay, even with my ugly sketch.

Now to clean it up …

I really appreciate your comments and help. Thank you.

Newbie10:
The button is to turn the SMPS on and a following press to turn it off. If user wants it on longer than a few minutes the timer must switch off at about 5 hours to conserve the battery. User can press button again after time-out to continue.

I'm still confused as to exactly what the [u]last line above[/u] means for the logic to be implemented. The first button press turns the SMPS and system (and thus the Arduino) on. If you hold the button too long the Arduino will detect that press. You can either not worry about this as the user is unlikely to hold for that long (a few seconds) or you can code for it.

Assuming the prior, then the second press is the 1’st one the Arduino “sees”. If this turns off the SMPS (by external means) then the Arduino will follow shortly no matter what. If you’re counting on the Arduino to switch it off, then there needs to be code to do that. Fairly simple. But now you also want to cancel the 5 hour timeout ? Or not ? Does that 2’nd press keep the Arduino on and tell it to cancel the 5 hr timeout or tell it to turn off or what ? After the timeout I assume the Arduino has turned off the SMPS and so it’s (shortly thereafter) off as well. Then you’re back to the next button press turning the system (back) on, which isn’t really “continuing” (to be on).

As for your circuit … I don’t see any circuit element that keeps the power on while the Arduino is booting but after the initial button press has been released. The big cap is on the “wrong” side of the switch. All it does there is delay, for a few time constants, the voltage to the switch after the raw 12-24 V power comes on. You want (I thought) some circuit that effectively stretches the 1’st button press so the user can release the button before the Arduino complete’s it’s bootup. You also want this stretched input to NOT be seen by the Arduino, while the button press can be (so as to see the 2’nd press). That means the element must be after the switch but isolated from the A0 input and in parallel with the A1 output. You ain’t got that so far as I can see.

Here’s a hint … do you care if the SMPS stays on for a few secs after the Arduino tells it to go off (via the A1 output) ?

Quite right : poor grammar and bad explanation. I’ll fix that, particularly for other interested folk.

The main supply (+12…24V) charges C1 via R3. The purpose of this RC is so that when the button is pressed a single pulse is triggered to start the SMPS, the Arduino and the signal to start the timer.

The moment the button is pressed this pulse (of 12…24V) is sent via D2 and R4 to T2 switching it on, which in turn switches T1 on to supply the SMPS with current from the main supply. The Arduino now has power and bootload starts up.

At the same time of this particular button press the pulse is sent via D1 and R5 to Arduino pin A0 to start the 5 hour timer - the voltage is clamped to 5.1V by the zener to avoid possible damage to Arduino.

[The charge time of C1 is set by R3/C1. The pulse duration is set by the size of C1 and must be sufficient to cover the loading resulting from switching T2 on (and obviously T2, the SMPS and Arduino) long enough for proper bootloading PLUS the result of keeping pin A0 on during this time to ensure that the timer is started.

When the timer starts pin A1 goes HIGH. This voltage via D3 and R4 keeps T2, T1, the SMPS and the Arduino on. So now the Arduino is running and the timer has started.

When the 5 hours is up pin A1 switches LOW and turns T2, T1, SMPS and Arduino off.

However, if the button is pressed a second time before the 5 hours is up a new pulse via D1 and R5 to pin A0 interrupts the timer switching A1 LOW which then performs exactly the same as time-out, i.e. full turn-off.

(The pulse is long enough to ride over the bootloader period and timer-trigger, but short enough to avoid possible problems of holding the button down too long. In my bench test case R3 = 470k, C1 = 4u7 works perfectly.)

I hope this makes more sense.

Thanks again.

Just for clarity let’s summarise the real necessity of the sketch :

  1. start 5 hour timer with HIGH on pin A0 [or this could be auto on start-up] and set pin A1 HIGH

  2. when the timer time-out happens set A1 LOW (which will turn off the everything)

  3. if A0 goes HIGH while the timer is running, set A1 to LOW (which will turn everything off).

Newbie10:
I hope this makes more sense. Thanks again, Mac.

Just for clarity let’s summarise the real necessity of the sketch :

  1. start 5 hour timer with HIGH on pin A0 [or this could be auto on start-up]; set pin A1 HIGH

  2. if timer times out set A1 LOW

  3. if A0 goes HIGH while the timer is running, set A1 LOW.

Much clearer ! And much different from what I had in mind and so the code I posted before is not what you want. So there is no requirement (hardware or software) to "stretch" that 1'st button press, as I had inferred from this.

The only hiccup I can see is that there’s a delay of about 3 secs at startup (boot?) until the “run” output goes high. I can bridge this with a cap but if there’s a better way to have the output HIGH immediately at startup (or quicker than 3 secs) it would be better. Can this be done?

The user is required to hold that button until the Arduino boots. Presumably the Arduino will give the user some indication (?LCD?) that it's OK to release the on button.

With that in mind and looking at the schematic, I see no real need for C1 or D1. And I might look at how R1 and R2 are arranged.

The user is required to hold that button until the Arduino boots. Presumably the Arduino will give the user some indication (?LCD?) that it’s OK to release the on button.

With that in mind and looking at the schematic, I see no real need for C1 or D1. And I might look at how R1 and R2 are arranged.

Pressing the button unloads C1 long enough to start SMPS and bootload and input A0. A too-long press won’t do anything nasty like try to turn off.

Yes, there’s no reason for D2, but neither the zener = the Arduino input pin has a built-in diode on the pin (cathode to Vdd) so its input voltage is protected.

R1 = 1M2, R2 = 47K

R1 keeps T2 off. R2 enough to turn T2 on.

Now, about a sketch to make this neat and tidy which I can include in the main sketch (which has no timers except PWM on pin 9 for LCD)…

Thanks so much.

Newbie10:
Now, about a sketch to make this neat and tidy which I can include in the main sketch (which has no timers except PWM on pin 9 for LCD)…

I'd think you can figure this out now. If not, this might be correct ... I didn't give it a lot of thought. That will be your job.
const int runPin = A1;                //POWER CONTROL PIN
const int buttonPin = A0;             //CANCEL TIMEOUT PIN

unsigned long interval = 18000000;    //5 HRS IN MSEC

boolean firstPress = false;           //INDICATES FIRST PRESS (PWR ON) AND RELEASE OF SWITCH
boolean buttonState;                  //BOOLEAN TO STORE SWITCH STATE

void setup()
{
  pinMode(buttonPin, INPUT);         //N.O. MOMENTARY SW TO 5V
  pinMode(runPin, OUTPUT);
  digitalWrite(runPin, HIGH);        //ENABLE POWER TO STAY ON
}

void loop()
{
  buttonState  = digitalRead(buttonPin);
  if (!firstPress) 
  {
    if (buttonState == HIGH)         //IF SWITCH PUSHED ...
    {
      //send message to relase button on LCD

    }
    else {
      firstPress = true;            //BUTTON HAS BEEN RELEASED
    }
  }
  else
  {
    if (buttonState == HIGH)        //IF SWITCH PUSHED AGAIN
    {
      digitalWrite (runPin, LOW);   //TIMER CANCELLED, TURN OFF POWER
    }
  }
  
  if (millis() > interval)
  {
    digitalWrite (runPin, LOW);     //TIME HAS RUN OUT, TURN OFF POWER
  }

  //any code to run while Arduino is on goes below here

}

FWIW you could have a single momentary press on / press off button control that does not require the user to hold the button through the boot-up period. I hinted at one such solution above. See what happens when you move the capacitor to be after the switch. Your SMPS is represented by Rload below. Q1 to be an NPN, or better yet N channel MOSFET, of your choice.

(click on to open)

(the Zener, D3, should act as a pull-down but perhaps 20+k btw A0 and gnd is warranted)

Now, about a sketch to make this neat and tidy which I can include in the main sketch (which has no timers except PWM on pin 9 for LCD)…

I goofed. I forgot about the 1 second timer which I use in the LCD to switch from one input value to another (timPin = 13)("alt. display"). It worked fine before I added the 5hr timer but now there's conflict between the two timers : "conflicting eclaration 'long unsigned int interval".

I tried using a different timer, without success.

How can I fix that?

Thanks so much in advance.

const int timPin = 13;          //alt. display
int timState = OUTPUT;
long previousMillis = 0 ;
long interval = 2000;           // time set for alt. display


const int runPin = A1;                //POWER CONTROL PIN
const int buttonPin = A0;             //CANCEL TIMEOUT PIN

unsigned long interval = 15000;    //5 HRS IN MSEC - set 15 sec for testing

boolean firstPress = false;           //INDICATES FIRST PRESS (PWR ON) AND RELEASE OF SWITCH
boolean buttonState;                  //BOOLEAN TO STORE SWITCH STATE

void setup()
{
  pinMode  (9, HIGH);           //LCD backlight brightness
  analogWrite  (9,  150);       // set 50...250)
  
  pinMode(buttonPin, INPUT);         //N.O. MOMENTARY SW TO 5V
  pinMode(runPin, OUTPUT);
  digitalWrite(runPin, HIGH);        //ENABLE POWER TO STAY ON
  
  pinMode (timPin, OUTPUT) ;    // alt. display
}
void loop()
{
  buttonState  = digitalRead(buttonPin);
  if (!firstPress)
  {
    if (buttonState == HIGH)         //IF SWITCH PUSHED ...
    {
      //send message to relase button on LCD

    }
    else {
      firstPress = true;            //BUTTON HAS BEEN RELEASED
         }
  }
  else
  {
    if (buttonState == HIGH)        //IF SWITCH PUSHED AGAIN
    {
      digitalWrite (runPin, LOW);   //TIMER CANCELLED, TURN OFF POWER
    }
  }
 
  if (millis() > interval)
    {
    digitalWrite (runPin, LOW);     //TIME HAS RUN OUT, TURN OFF POWER
  }
  unsigned long currentMillis = millis() ;
  if (currentMillis - previousMillis > interval)  {
    previousMillis = currentMillis ;
    if (timState == LOW)
      timState = HIGH;
    else
      timState = LOW;
    digitalWrite (timPin, timState);
  
  }
}

Let’s keep things simple shall we. Declare all variables “up top” (for now). Don’t declare them in setup() or void() functions.

Then anything that’s to be used with, compared to, or subtracted from the millis() call should be of the same type and that is an unsigned long. That means previousMillis and interval and anything else I missed should be unsigned longs.

Lastly (almost) you seem to use the variable interval for 2 different things. One is the 5 hr (or 15 sec) power off time out and the other is something to do with timState and I’m unclear what that is trying to do. Might you want to use another constant for a different timing value ??? Like comparing it to a new constant, oneSec ? Can you guess what that might be initialized to “up top” ?

You’re using 1 “clock” for timing, it’s called the millis() function. It starts at zero upon power-up or reset and just counts thereafter. You can tell the duration of some activity in the same way you would by looking at the clock on the wall. If something starts at 2 pm and it’s now 3:30 pm, that activity has been running for 1.5 hrs (3:30 - 2:00). That’s storing the starting time and finding the time it is now and comparing the two. If that activity needs to stop after 2 hrs, you can form an if() question that asks that. Multiple activities can use the same “clock on the wall” if they keep separate start times and durations for each activity.

Then you have this oddness …

pinMode (9, HIGH)

pinMode tells the compiler to make a pin an input or an output, not to set it HIGH or LOW. And then there’s this …

int timState = OUTPUT;

Huh ? timState is a variable, not a pin. And given you’re setting HIGH or LOW (same as true or false) later in the code, it could be a boolean rather than an integer.

http://arduino.cc/en/Reference/BooleanVariables

I get the feeling that you need to understand the difference btw constants and variables a bit better. And why constants, with human understandable names, are often used in place of pin numbers (it’s just is easier to remember that the pin that shuts things off is offPin rather than trying to remember, in all the places in code where it might be used, that it’s also pin6). You don’t have to “name” each pin but it can help when the code gets longer and longer.

What I need is two (three?) timers running at the same time :

(a) the 5 hour timer with cancel/stop/interrupt option by the 2nd pressing (the following button press after start-up).

(a) a second timer which runs constantly from start-up and alternates every 1000mS (pin 13 called “tim”), to switch some display functions on the LCD from one value to another.

The two timers must obviously have 2 seperate outputs :

(a) the 5 hour timer on pin A1 (output) switches the SMPS on (at start-up) and off (at tme-out OR the second press of button with pin A0 before time-out).

(a) “tim” pin 13 : drives the LCD to display the alternate values (“alt. display”) which works.

(c) Pin 9 is a PWM output driving the LCD backlight. This should have no effect on anything after start-up.)

To summarise :

  1. 5 hour timer : initiated by start-up (or button A0) but can be cancelled by 2nd press of button (via A0)

  2. 1000mS square wave on pin 13 (for display purposes and independant/continuous of the 5 hour timer)

  3. PWM on pin 9 for LCD backlight brightness.

My corrected sketch which doesn’t work :

const int runPin = A1;                //POWER CONTROL PIN
const int buttonPin = A0;             //CANCEL TIMEOUT PIN
unsigned long interval = 15000;    //5 HRS IN MSEC
boolean firstPress = false;           //INDICATES FIRST PRESS (PWR ON) AND RELEASE OF SWITCH
boolean buttonState;                  //BOOLEAN TO STORE SWITCH STATE

const int timPin = 13;          //alt. display
int timState = OUTPUT;
long previousMillis = 0 ;
 // unsigned long interval = 2000;           // XXXXXXXXXX    time set for alt. display


void setup()
{
  pinMode(buttonPin, INPUT);         //N.O. MOMENTARY SW TO 5V
  pinMode(runPin, OUTPUT);
  digitalWrite(runPin, HIGH);        //ENABLE POWER TO STAY ON 
  
  pinMode  (9, OUTPUT);           //LCD backlight brightness
  analogWrite  (9,  150);       // set 50...250)

}
void loop()
{
  buttonState  = digitalRead(buttonPin);
  if (!firstPress)
  {
    if (buttonState == HIGH)         //IF SWITCH PUSHED ...
    {
      //send message to release button on LCD   ????

    }
    else {
      firstPress = true;            //BUTTON HAS BEEN RELEASED
    }
  }
  else
  {
    if (buttonState == HIGH)        //IF SWITCH PUSHED AGAIN
    {
      digitalWrite (runPin, LOW);   //TIMER CANCELLED, TURN OFF POWER
    }
  }
  if (millis() > interval)
  {
    digitalWrite (runPin, LOW);     //TIME HAS RUN OUT, TURN OFF POWER
  }
  unsigned long currentMillis = millis() ;
  if (currentMillis - previousMillis > interval)  {
    previousMillis = currentMillis ;
    if (timState == LOW)
      timState = HIGH;
    else
      timState = LOW;
    digitalWrite (timPin, timState);

  }
}

What do each of the following statements do … or not do ?

int timState = OUTPUT;            //in the code below, timState is HIGH or LOW
long previousMillis = 0 ;         //what did I say about times used with the millis() function ??
//unsigned long interval = 2000;  //time set for alt. display ... right idea, wrong name & value

Isn’t the variable (it probably should be a constant) interval used for the “5 hr time out” (presently set to 15 secs for testing) ? How, if it’s = 15 secs can it be used for a 1 sec (0.5 sec on, 0.5 sec off) timer for the timPin ? How can you hope for the following to work ?

  unsigned long currentMillis = millis() ;
  if (currentMillis - previousMillis > interval)  {    //interval ?? what is interval equal to ?? 
    previousMillis = currentMillis ;  //how long after previousMillis was set will you get here again ??
    if (timState == LOW)
      timState = HIGH;
    else
      timState = LOW;
    digitalWrite (timPin, timState);

Why not use another constant, set to 500 ms, for the above ? Walk through the code, loop by loop, and ask yourself the same questions you’re asking the code to do.

ps - what did I say about declaring constants and variables ? For now don’t do it inside of the setup() and void() functions.

pps - my last post on this since you’re not reading them.