Using the Arduino as a One-Shot

I have been trying to make my Arduino act like a one-shot. When an input goes LOW for a brief moment, I want an output to go HIGH for 30 seconds. It can't cause a delay while this is happening as I want the Arduino to continue down the sketch to see if other inputs go LOW and if so, make it's output go HIGH for 30 seconds.

I have tried several sketches with no luck. Does someone know how to do this?? Any help appreciated.

Use the approach demonstrated in the 'blink without delay' example sketch to carry out actions after an interval.

In this case you would poll the inputs; when the input changes from HIGH to LOW you would set the corresponding output HIGH and record when it was set high.

For any output that is HIGH, you would also compare the current time with the time that the output went HIGH to find how long it has been HIGH for; if that exceeds 30 seconds then you would set the output LOW again. You don't say what you would want to happen if the input went LOW again before the 30 seconds had elapsed, but I assume you can figure that out.

If you choose your input pins with care then you can read 6 at a time on an UNO using Port read, 8 at a time for some Mega ports.

I should mention I am new to this....

So far I have this for my code:

int led = 13;     
int button = 10;

int ledState = LOW;             
long previousMillis = 0;        

long interval = 550;           

void setup() {
  
  pinMode(led, OUTPUT);
  pinMode(button, INPUT);  
}

void loop()
{
  if(digitalRead(button) == HIGH) {
  unsigned long currentMillis = millis();
   if(currentMillis - previousMillis > interval) {
      previousMillis = currentMillis;  

    if (ledState == LOW)
    ledState = HIGH;
   
    else
      ledState = LOW;
    digitalWrite(led, ledState);
  }
  }
  else {
    digitalWrite(led, LOW);
  }

}

The led begins to blink when the button is pressed and goes out when the button is released. I would like the LED to remain lit for 30 seconds even if the button is pressed for only 2 seconds.

Moderator edit:
</mark> <mark>[code]</mark> <mark>

</mark> <mark>[/code]</mark> <mark>
tags added.

Well, take out these lines:

  else {
    digitalWrite(led, LOW);
  }

They set the LED to low, as soon as you release the button.

Thanks.... I have done that. Now I just need the output to stay active for 30 seconds once the button has been pressed.

Better make "interval" larger than 550 mS then.

Also, your test for interval needs to be somewhere else. You have it under the part where you test the switch.

In the longer term, I think that currentMillis would be better off being a global variable as I can foresee the time when it will need to be an array in order to deal neatly with more than one input and output and you won't be able to declare it each time as you do now.

Thanks for the help guys. I will play around with it and see what happens.

UKHeliBob:
In the longer term, I think that currentMillis would be better off being a global variable as I can foresee the time when it will need to be an array in order to deal neatly with more than one input and output and you won't be able to declare it each time as you do now.

To get elapsed time to work tight I use if ( now - start >= wait ) then time is up. With millis() giving "now" I find that I still need "start" +and+ "wait" and don't always get good results trying to shorten the if to if ( now >= end-time ) because without the subtraction it doesn't work past rollover. So you need 2 unsigned longs per timer. I'd use a class and code the time check right into the array objects. Read millis() once and check many "if(now-start>=wait)" timers long before the next change in millis(). An UNO only has 2K RAM, and each timer needs 8 bytes of storage. But the math is done in cycle steps with 16000 cycles per millisecond. Unsigned longs are 4 byte, they process in steps but still less than microsecond steps. You can do !lots! of timers.

This is the exact thing I am trying to do. I get lots of suggestions but I still cant get this to work. Can anyone give the correct working sketch so at least I can pull it apart to understand what is happening. I copied this sketch & deleted the bits suggested and I can't get it to work. Please no smart ass comments, I am trying to learn.

Can anyone give the correct working sketch so at least I can pull it apart to understand what is happening.

Possibly, but you wouldn't learn anything. Post the code you have now. Describe what it does that you do want, what it does that you don't want, and what it doesn't do that you do want. Then, we can point out what is wrong AND why. Then, you'll actually learn something.

Thanks PaulS
I have copied the sketch, deleted the last bit as suggested. I have added the PULLUP command which prevents the floating problem and inverted the HIGH to LOW in the digital read to allow for the state now having to go low because of the pullup.

int led = 12;     
int button = 2;

int ledState = LOW;             
long previousMillis = 0;        

long interval = 550;           

void setup() {
  
  pinMode(led, OUTPUT);
  pinMode(button, INPUT_PULLUP);  
}

void loop()
{
  if(digitalRead(button) == LOW) {
  unsigned long currentMillis = millis();
   if(currentMillis - previousMillis > interval) {
      previousMillis = currentMillis;  

    if (ledState == LOW)
    ledState = HIGH;
   
    else
      ledState = LOW;
    digitalWrite(led, ledState);
  }
  }
  

}

Just tested that code on my Uno, you're not even CLOSE to what you want to do.

I did this exact thing on my latest project. When my Arduino detects an overload on the output, I light an LED for 1/4 of a second.

Here's my code reworked to use a button instead of an overload flag:

//***************************
// Pin declarations
//***************************
const byte led_pin = 13;
const byte b1_pin = 12;

//***************************
// Global Variables
//***************************
// Variable to work with the button.
byte b1_prev_state = HIGH;
byte b1_edge = 0;

byte os1_timeout_flag = 0; // Is the one-shot currently triggered
unsigned long os1_timeout_interval = 5000; // Interval for one-shot
unsigned long os1_trigger_start = 0; // When was the last one-shot event triggered

void setup()
{
  pinMode( led_pin, OUTPUT );
  pinMode( b1_pin, INPUT_PULLUP );
}

void loop()
{
  // Check the button state.
  byte b1_curr_state = digitalRead( b1_pin );
  if( b1_curr_state == b1_prev_state )
    b1_edge = 1;
  else
    b1_edge = 0;
  b1_prev_state = b1_curr_state;
  
  unsigned long t_now_ms = millis();
    
  // On a negative edge, set the timeout flag and the trigger timestamp.
  // Write the LED high.
  if( b1_curr_state==LOW && b1_edge )
  {
    os1_timeout_flag = 1;
    os1_trigger_start = t_now_ms;
    digitalWrite( led_pin, HIGH );
  }
  
  // if the timeout flag is set and the timeout interval is exceeded
  // turn off the LED and unset the timeout flag.
  if( os1_timeout_flag && (t_now_ms-os1_trigger_start > os1_timeout_interval) )
  {
    os1_timeout_flag = 0;
    digitalWrite( led_pin, LOW );
  }
  
}

Thanks Jiggy-Ninja, this works well with a short momentary contact, but the led stays on when the switch is held closed. I am trying to get the 1 second led / buzzer on for 1 second regardless if there is a short duration switch or a long duration switch. I also want this not to sound the buzzer for 5mins and requires the switch contacts to open then close if the switch was closed for over 5 min. So if the beam is blocked there isnt a 1 second buzzer every 5 minutes.

You have a whole thread on this with solutions already given.

For you GoForSmoke it is clear, if I had your understanding of it I wouldn't still be here.
The last sketch partially worked but if the button was held down the buzzer would keep buzzing. I cannot work out to get this working as I want. I can make things blink without delay. I can make a quick pulse stay on. I can make a long pulse give the 1 sec buzzer, but I cant put them all together to make them work. I have googled it, I have bought a few books for beginners. I can do all the projects & change the durations. But I cant successfully get this thing to work.

When you put your other code inside of

if ( the button is pushed )
{
stuff that used to work all the time
}

then it's only going to run when the button is pushed

It doesn't work because I made a mistake in the edge detection section. Oops :stuck_out_tongue:

I'll leave it to you to find. It'll be a good exercise in debugging.