Using a function multiple times with a delay between each use.

Hi Guys

I hope someone can offer me some pointers here. I have a sketch which pulses a set of leds whilst fading up and then flashing another pair in response to a button press. That part works ok.

What I'm trying to figure out is how to, in response to the single button push, call the frontLed() function 3 times with a 2 second gap between each call, then wait 20 seconds and then call another function (not yet written). All this while not interfering with the pulsing leds.

I've been wrestling with various millis based timing routines, but nothing seems to achieve what I'm after. Do I have to time the whole function then somehow build in a millis based pause, but how to do that without messing up the other functions?

Does anyone have any suggestions? The code so far follows:

// ALLOCATIONS

// ========================================================================
// Pins
const byte frontRedLed = 3;                 //
const byte frontOrangeLed = 4;
const byte indLed = 6;
const byte ledSwitchPin = 8;           //  switch pin (other connection to ground)

// ========================================================================
// led Switch
boolean ledSwitchFlag = false;             // torper switch blocker (switch routine not to be called during led flash)
byte oldLedSwitchState = HIGH;             // assume switch open because of pull-up resistor
const unsigned long ledDebounceTime = 100;  // milliseconds debounce setting
unsigned long ledSwitchPressTime;          // when the switch last changed state

// ========================================================================
// Fader Parameters
unsigned long timeThen;                     // time variable during ramp up unsigned long flashTimeNow = millis();
int ledBrightness = 0;                     // initial torp ramp up brightness (ie off)
int ledFadeAmount = 1;                     // fade up increments for torper
int ledBlip = 40 ;                         // max value to fade up to (45 max level)
unsigned long ledTimer;                    // timer for fader led max brightness period
boolean ledTimerStarted = false;           // torp flash flag
int minBeamOnTime = 100;                    // minimum on time for led
int maxBeamOnTime = 6000;                   // maximum on time for led

// ========================================================================
// Pulsing Leds
static float pulseAm = 1.57;
float pulseInc;
int swing = 70;

// ========================================================================
// SETUP

void setup ()
{
  // Pins
  pinMode (frontRedLed, OUTPUT);
  pinMode (frontOrangeLed, OUTPUT);
  pinMode (indLed, OUTPUT);
  pinMode (ledSwitchPin, INPUT_PULLUP);

  // ========================================================================

}  // end of setup

// ========================================================================
// MAIN LOOP

void loop ( )
{

  indPulse();
  ledSwitchCall();                                 // Poll switch
  frontLed();

}  // end of loop                   
// ========================================================================
// FUNCTIONS

void indPulse()
{
  pulseAm = pulseAm + 0.0005;                //speed of pulse                         
  if (pulseAm > 7.853)                            
    pulseAm = 1.57;                          // position on sine wave                               
  pulseInc = sin(pulseAm) * swing + 127 ;    // swing = amount of swing down from max brightness i.e. a value 128 is off at bottom of swing     
  analogWrite(indLed, pulseInc);         
}

void ledSwitchCall ()
{
  byte ledSwitchState = digitalRead (ledSwitchPin);          // if it has, see if switch is open or closed
  if (ledSwitchState != oldLedSwitchState)                    // has it changed since last time it was read?
  {
    if (millis () - ledSwitchPressTime >= ledDebounceTime) { // has enough time passed with stable reading?
      ledSwitchPressTime = millis ();                         // update the time
      oldLedSwitchState =  ledSwitchState;                   // remember the switch state for next time
      if (ledSwitchState == LOW)                             // if switch passes debounce test and is pressed
      {
        ledBrightness = 0;                                    // set brightness variable back to zero for next firing
        ledSwitchFlag = true;                                 // ok to do flash
      }
    }
  }
}

void frontLed ()
{
  if (ledSwitchFlag)                                           // Ok to fade?
  {
    unsigned long timeNow = millis();                          // set fader interval timer
    if (timeNow >= (timeThen + 50))                            // is it time to increase brightness?
    {
      timeThen = timeNow;
      ledBrightness = ledBrightness + ledFadeAmount;             // increment brightness value
      if (ledBrightness >= ledBlip)                             // is the led bright enough to flash?
        ledBrightness = ledBlip;                                // if so, set ledbrightness to flash value
      analogWrite (frontRedLed, ledBrightness);                // write new value to led
    }
  }
  if (ledBrightness >= ledBlip && ledTimerStarted == false) // has the brightness reached set limit and is timer started?
  {
    digitalWrite (frontOrangeLed, HIGH);                      // if so, set red fader led to max brightness
    digitalWrite (frontRedLed, HIGH);                         // and switch on orange led
    ledBrightness = 0;                                       // reset brightness back to zero for next time
    ledTimer = millis();                                     // start brightness period timer
    ledTimerStarted = true;                                    // set brightness period timer flag to started
    ledSwitchFlag = false;                                     //fade complete
  }
  if ((millis() - ledTimer) > (random(minBeamOnTime, maxBeamOnTime)) && ledTimerStarted == true) {// has enough time passed at max brightness?
    digitalWrite (frontOrangeLed, LOW);                          // if so, turn leds off
    digitalWrite (frontRedLed, LOW);
    ledTimerStarted = false;                                    // set brightness period timer flag to not started
  }
}

Many Thanks :slight_smile:

Think about the state of the system. It starts in some "waiting for first button press" state. That does whatever it needs to do to attract a person to press a button.

After the button is pushed, you start fading up but during that period you want to flash 3 times. So count the flashes. "first flash started" "first flash ended" "second flash started" "second flash ended" and so on.

If you're in a flash-started state, the LED should be on. In a flash-ended state, it should be off.

Each of those state transitions needs to record when it went into that state. Then you look at the timer while inside the state to decide when to transition to the next state.

I'm not sure I've explained myself properly, either that or I'm missing the point MorganS, and apologies if I am :-[ .

To explain it slightly differently:
In response to pressing a switch, I want to call a function that fades an led up to a set level then switches it on fully before then switching it off (and this is what my function frontLed currently successfully does). However I want it to perform three successive times with a 2 second pause between each performance, sort of like this:

/| 2s /| 2s /|

and then after another 20 seconds have passed, to call a different function.

I tried writing a function that called my existing function frontLed three times, but nothing I wrote worked even with no 2 second pause between each call of frontLed, so I got totally lost I'm afraid.

How do you ask a function to perform multiple times on a single trigger?

How do you ask a function to perform multiple times on a single trigger?

Wrap the code in the function in a for loop. If you want to repeat the code a variable number of times then pass the repeat count to the function and use that in the for loop

   if (timeNow >= (timeThen + 50))                            // is it time to increase brightness?

Wrong way around.

    const unsigned long brightnessIncreaseInterval = 50; //milliseconds
    if (timeNow - timeThen >= brightnessIncreaseInterval))                            // is it time to increase brightness?

While the maths they teach at school says the two expressions above are equivalent, with unsigned integers they are not. It is possible to add two unsigned numbers and get an answer smaller than the inputs. See Gammon Forum : Electronics : Microprocessors : millis() overflow ... a bad thing?

Also, always name your constants.

I recommend against the for() loop. Use a state machine

The demo Several Things at a Time has several functions that operate at different time intervals without interfering with each other. It may give you some ideas.

...R

Thanks Guys.

I'll sit down tonight and see if I can use a state machine approach. I know the principles of noting state changes, but actually putting it into practice is proving a wee bit elusive!

I tend to get confused as to which line of code is being actioned at any particular time during function calls. Memory is not a commodity that my brain has in abundance! Paper and pencil will have to do!

Thanks for the heads up on the millis() wrap around MorganS. I thought I had caught all those but it looks as though that one escaped!

If you are going to use a state machine then I would start by documenting how many states there are and give them meaningful names, what needs to be in place when the state is entered such as the start time, what needs to happen to cause the state to end and the destination state.

Use an enum to define the states and switch/case based on the current state to manage which portion of code is executed when in a particular state. Do not use delay() or while anywhere in the code. Keep loop() repeating as often as possible.

STDummy:
Thanks Guys.

I'll sit down tonight and see if I can use a state machine approach. I know the principles of noting state changes, but actually putting it into practice is proving a wee bit elusive!

I tend to get confused as to which line of code is being actioned at any particular time during function calls. Memory is not a commodity that my brain has in abundance! Paper and pencil will have to do!

Thanks for the heads up on the millis() wrap around MorganS. I thought I had caught all those but it looks as though that one escaped!

This will be much easier if you learn how to create a class. Most libraries in Arduino-land work by giving you a class that hides away all of its internal operation details and just gives you a small list of functions that do what you want. For Serial, you don't need to muck around with manually loading the serial registers because the HardwareSerial class handles that for you.

Creating a good class requires planning, and you probably won't get it right on the first time. Start by considering a proverbial "black box" that does what you want. What sort of functions would it need to have in order for you to be able to operate it? For something like this you would need to be able to enable it, disable it, and a 3rd function to execute its operations. As your program evolves you might find the need for additional functionality, like changing the blink rate of the LEDs or changing the function that runs at the end.

Second, try to list all the internal variables that your function will need in order to operate. If you want it to toggle a pin, you need a variable to store the pin number. If you want to measure out time intervals, you need to associated variables to keep track of that (one variable to time the start of the interval and a second for how long of an interval you are timing). If you want to perform a sequence of actions, you need another variable to keep track of where you are in that sequence. Keep listing them out until you run out.

Once you've got your list of functions and your list of internal variables, start coding those functions! Having the functionality in class member functions helps keep all that raw code out of loop(), so it stays nice and tidy and easy to read.

Classes are not just for complicated things. Anytime you have variables associated with a specific functionality, that's a prime candidate for making a function. If you want an example to study, here is something that I've designed for my own use. A very common construct in an Arduino program is to increment a number up to a certain value, then reset it to 0. For that, I've created my wrapping_integer. Behold!

class wrapping_integer
{
public:
  wrapping_integer(unsigned int lower, unsigned int length, unsigned int initial_value) : _start_length(lower), _length(length), _current_value(initial_value){}
  wrapping_integer(unsigned int lower, unsigned int length) : wrapping_integer(lower, length, lower) {}
  
  unsigned int size() const {return _length;}
  unsigned int lower_bound() const {return _start_value;}
  unsigned int upper_bound() const {return lower_bound()+size()-1;}
  
  unsigned int to_value() const {return _current_value;}
  void set_value(unsigned int new_val) // change the current position, staying within the boundaries.
  {
    if( new_val > upper_bound() )
      _current_value = upper_bound();
    else if( new_val < lower_bound() )
      _current_value = lower_bound();
    else
      _current_value = new_val;
  }
  
  bool at_left_end() const {return _current_value==lower_bound;}
  bool at_right_end() const {return _current_value==upper_bound;}
  
  // move forward one step, wrapping back to the beginning once you get past the end.
  void next()
	{
		if( at_right_stop() )
			reset();
		else
			++_val;
	}
	
  // Move backwards one step, wrapping around to the end once you go past the beginning.
	void previous()
	{
		if( at_left_stop() )
			_val = upper_bound();
		else
			--_val;
	}
  
  // Move back to the beginning.
	void reset()
	{
		_val = lower_bound();
	}

// internal variables
private:
  unsigned int _start_value;
  unsigned int _length;
  unsigned int _current_value;
};

Notice how I've moved as much code relating to the functionality of this object into the member functions. That way whenever I want to go to the next value, I just call next() and don't need to micromanage everything every time. Try to do the same thing when designing your class for this job. Make it so that you just have to set it up, then start() it, then call a run() function every pass through loop so it can check it's time intervals and do whatever needs to be done.

:o

This is leading me into territory that is marked "Here Be Dragons"!

Seriously, this is something I think I need to pursue, state machines and classes and their like. That will have to wait for a week or so though, as I rather foolishly and over-confidently, promised someone a working sketch by the end of next week. As I only have evenings available to get to grips with this problem, I think the solution (should I solve it) will have to be "down and dirty" for the moment, maybe by using for loops as suggested by UKHeliBob and maybe made more elegant later!

Thanks for the hints and tips guys, I think my first task is to read up Nick Gammon's state machine explanation and look into Robin2's piece on several things at a time.

And thanks to all who replied, including Jiggy-Ninja even though I currently only undertand around 50% of his suggestion :slight_smile: :slight_smile:

Well you know what they say. The first 90% of a program takes 90% of your time to make, and the last %10 takes the remaining 90% of your time. :smiley:

What you want to do is relatively simple, you just need to know all the concepts to combine together.

This type of code can get pretty convoluted fast with state machine approach and it's much cleaner to use fibers/coroutines instead. You need to download ffunc.h for your sketch and then have something along the lines (compiles but not sure if it works as I haven't tested it):

#include "ffunc.h"

static const byte frontRedLed=3;
static const byte frontOrangeLed=4;
static const byte indLed=6;
static const byte ledSwitchPin=8;

static const int ledBlip=40;
static const int minBeamOnTime=100;
static const int maxBeamOnTime=6000;
static const int ledDebounceTime=100;

long curTime, prevTime;
ffunc_callstack fcStack;

// led pulse
static float pulseAm=1.57f;
static const int swing=70;
void indPulse()
{
  pulseAm+=0.0005f;
  if(pulseAm>7.853f)                            
    pulseAm=1.57f;
  float pulseInc=sin(pulseAm)*swing+127;
  analogWrite(indLed, pulseInc);         
}

// front led animation
struct ffunc_front_led
{
  int i, j;
  FFUNC()
  {
    FFUNC_BEGIN

    // animate LED's 3 times
    for(j=0; j<3; ++j)
    {
      // brighten red LED
      for(i=0; i<ledBlip; ++i)
      {
        analogWrite(frontRedLed, i);
        FFUNC_CALL(ffunc_sleep, (0.05f));
      }

      // blink LED random time & wait 2 sec
      digitalWrite(frontOrangeLed, HIGH);
      digitalWrite(frontRedLed, HIGH);
      FFUNC_CALL(ffunc_sleep, (random(minBeamOnTime, maxBeamOnTime)/1000.0f));
      digitalWrite(frontOrangeLed, LOW);
      digitalWrite(frontRedLed, LOW);
      FFUNC_CALL(ffunc_sleep, (2.0f));
    }
    FFUNC_CALL(ffunc_sleep, (20.0f));

    /*call other func here*/

    FFUNC_END
  }
};

byte oldLedSwitchState=HIGH;
long ledSwitchTime=0;
bool isLedSwitch()
{
  if(   digitalRead(ledSwitchPin)!=oldLedSwitchState
     && curTime-ledSwitchTime>=ledDebounceTime)
  {
    ledSwitchTime=curTime;
    oldLedSwitchState=!oldLedSwitchState;
    if(oldLedSwitchState==LOW)
      return true;
  }
  return false;
}

void setup()
{
  pinMode(frontRedLed, OUTPUT);
  pinMode(frontOrangeLed, OUTPUT);
  pinMode(indLed, OUTPUT);
  pinMode(ledSwitchPin, INPUT_PULLUP);

  curTime=millis();
  prevTime=curTime;
}

void loop()
{
  curTime=millis();
  indPulse();
  if(!fcStack.tick((curTime-prevTime)/1000.0f) && isLedSwitch())
    FFUNC_START(fcStack, ffunc_front_led, ());
  prevTime=curTime;
}

Cheers, Jarkko

JarkkoL:
This type of code can get pretty convoluted fast with state machine approach and it's much cleaner to use fibers/coroutines instead.

Seems like a neat substitution of complicated for convoluted. :slight_smile:

I suspect a newbie would be better able to understand problems with convoluted.

...R

That's awesome Jarkko!

That code not only compiles but it almost does what I'm after. I say almost because currently the flash of the frontRedLed and the frontOrangeLed interrupts the pulsing of the indLed, but I may be able to find out why and adjust things to suit.

Today I will mostly be reading the ffunc readme.md file :slight_smile:

I'm glad it almost worked :slight_smile:

It seems you implemented indPulse() so that the pulsing heavily depends on the MCU speed and the overhead of the loop(), so if the loop() has some added overhead (e.g. when the front LED starts to animate) it slows down the pulsing. It's better to implement the function in a manner that it doesn't depend how fast the loop() runs.

You could either:

  1. add a delay into the loop (e.g. 10ms) to minimize the problem and adjust the pulse speed of 0.0005f to match the expected speed,
    or IMO a better option:
  2. convert indPulse() to a fiber and run it simultaneously with the front led fiber, without need to limit the loop() update speed, e.g:
#include "ffunc.h"

static const byte frontRedLed=3;
static const byte frontOrangeLed=4;
static const byte indLed=6;
static const byte ledSwitchPin=8;

static const int ledBlip=40;
static const int minBeamOnTime=100;
static const int maxBeamOnTime=6000;
static const int ledDebounceTime=100;

long curTime, prevTime;
ffunc_callstack fcs1, fcs2;

// led pulse
static const int swing=70;
struct ffunc_pulse_led
{
  float pulseAm;
  ffunc_pulse_led() {pulseAm=1.57f;}
  FFUNC()
  {
    FFUNC_BEGIN
    // pulse LED forever (update @ 20hz)
    while(true)
    {
      pulseAm+=0.25f; // adjust this to give proper pulsing speed
      if(pulseAm>7.853f)
        pulseAm=1.57f;
      analogWrite(indLed, sin(pulseAm)*swing+127);
      FFUNC_CALL(ffunc_sleep, (0.05f));
    }
    FFUNC_END
  }
};

// front led animation
struct ffunc_front_led
{
  int i, j;
  FFUNC()
  {
    FFUNC_BEGIN

    // animate LED's 3 times
    for(j=0; j<3; ++j)
    {
      // brighten red LED
      for(i=0; i<ledBlip; ++i)
      {
        analogWrite(frontRedLed, i);
        FFUNC_CALL(ffunc_sleep, (0.05f));
      }

      // blink LED random time & wait 2 sec
      digitalWrite(frontOrangeLed, HIGH);
      digitalWrite(frontRedLed, HIGH);
      FFUNC_CALL(ffunc_sleep, (random(minBeamOnTime, maxBeamOnTime)/1000.0f));
      digitalWrite(frontOrangeLed, LOW);
      digitalWrite(frontRedLed, LOW);
      FFUNC_CALL(ffunc_sleep, (2.0f));
    }
    FFUNC_CALL(ffunc_sleep, (20.0f));

    /*call other func here*/

    FFUNC_END
  }
};

byte oldLedSwitchState=HIGH;
long ledSwitchTime=0;
bool isLedSwitch()
{
  if(   digitalRead(ledSwitchPin)!=oldLedSwitchState
     && curTime-ledSwitchTime>=ledDebounceTime)
  {
    ledSwitchTime=curTime;
    oldLedSwitchState=!oldLedSwitchState;
    if(oldLedSwitchState==LOW)
      return true;
  }
  return false;
}

void setup()
{
  pinMode(frontRedLed, OUTPUT);
  pinMode(frontOrangeLed, OUTPUT);
  pinMode(indLed, OUTPUT);
  pinMode(ledSwitchPin, INPUT_PULLUP);
  FFUNC_START(fcs1, ffunc_pulse_led, ()); // start pulse LED fiber
  
  curTime=millis();
}

void loop()
{
  prevTime=curTime;
  curTime=millis();
  float deltaTime=(curTime-prevTime)/1000.0f;
  fcs1.tick(deltaTime);
  if(!fcs2.tick(deltaTime) && isLedSwitch())
    FFUNC_START(fcs2, ffunc_front_led, ());
}

Robin2:
Seems like a neat substitution of complicated for convoluted. :slight_smile:

Feel free to put the money where your mouth is and provide a simpler alternative solution (:

Cheers, Jarkko

JarkkoL:
Feel free to put the money where your mouth is and provide a simpler alternative solution (:

See Reply #5

...R

I'd like to thank ALL you guys for the help you've given me.

I managed to get a working sketch, based on JarkkoL's sketch. I won't pretend I understand all of it fully yet, but I do understand and appreciate its principles.

I also am working on using the lots of things at the same time principles in a sketch to do the same job, and it'll be interesting to compare the two.

JarkkoL:
it's much cleaner to use fibers/coroutines instead.

I can't comment on the truth of that, since I have nfc what fibers or coroutines are. When you throw stuff like that out into the ring you could at least give a wee bit of an explanation of a) what they are and b) why they are better. Otherwise, it just seems you're showing off.

STDummy:
I won't pretend I understand all of it [JarkkoL's sketch] fully yet

Well the yet is promising; perhaps when you have it sussed you can come and explain it to the rest of us me...

STDummy:
I managed to get a working sketch, based on JarkkoL's sketch. I won't pretend I understand all of it fully yet, but I do understand and appreciate its principles.

That's good to hear (: Good luck with the rest of the project!

Robin2:
See Reply #5

The fact that you can't proves the point. That solution just doesn't scale past very trivial cases and is not the best tool for this job. Part of good software engineering is to identify what the good tool is and expand your toolset as needed. When solutions become complex and convoluted good software engineers think "hmm, maybe there's a better way" and explores the options. If you have a screw, hammer may not be the best tool to screw it down.

elvon_blunden:
I can't comment on the truth of that, since I have nfc what fibers or coroutines are

The great thing is that if you genuinely want to know, with just a little bit of googling you can find out (: I also gave a bit of an introduction in the library with links. Note that fibers/coroutines are not programming components I invented (unfortunately), but are generally known components (like state machines) and there's thus a lot of information in the internet available about them if you want to learn more.

Summary : fibers and coroutines are a means of hiding timing functions in a library