Using FastLED beatsin functions one after the other

Hi All,

I'm new to Arduino and been scratching my head around my recent project. I'm looking for some guidance on how to loop around two beatsin16 functions one after the other.

At the moment I have two sub-routines that consist of the beatsin16 function. Each is set-up slightly different with BPMs, duration and phase. I'm happy how each sub-routine operates individually, but the end goal is to run Routine 1 once, wait until this is complete (duration is 750ms ), then run Routine 2 once, wait until this is complete (duration is 1500ms) before repeat to Routine 1 again and so on..

Whenever I introduce a time delay in the main Loop() to run Routine 1, delay 750ms, then Routine 2, this just breaks down the beatsin functions and they don't operate as they did individually.

Any help is much appreciated.

#include <FastLED.h>

#define NUM_LEDS  60
#define LED_PIN   3

CRGB leds [NUM_LEDS];

void setup() {
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness (100); 
      
}

void loop () {
  Routine1();
  delay(750);
  Routine2();
  delay(1500);
}


//Sub-Routines below. 

void Routine1 () {

  const uint32_t interval = 750UL; // half a 20BPM beat
  static unsigned long lastOffsetTime = 0;
  static uint32_t t0 = 0; // timebase
  static uint16_t phase = 0; // which segment of sine
  if(millis() - lastOffsetTime >= interval){
    lastOffsetTime += interval;
    t0 = millis() ; // update timebase
  }
  
  uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, t0, phase);

  leds [sinBeat] = CRGB::Blue;

  fadeToBlackBy (leds, NUM_LEDS, 50);

  FastLED.show();

}




void Routine2 () {

  const uint32_t interval1 = 1500UL; // half a 20BPM beat
  static unsigned long lastOffsetTime1 = 0;
  static uint32_t t01 = 0; // timebase
  static uint16_t phase1 = 32768/2; // which segment of sine
  if(millis() - lastOffsetTime1 >= interval1){
    lastOffsetTime1 += interval1;
    t01 = millis() ; // update timebase
  }
  
  uint16_t sinBeat1 = beatsin16(33.3, 0, NUM_LEDS - 1, t01, phase1);

  leds [sinBeat1] = CRGB::Red;

  fadeToBlackBy (leds, NUM_LEDS, 50);

  FastLED.show();

}





Welcome to the forum

The program will not return from the Routine1() function until it is complete so why the need to introduce delay()s ?

Hi,

The delay was introduce to try and wait for Routine 1 was complete before starting Routine 2. This was my initial thinking but clearly doesn't work.

Any idea round this?

That's exactly what your code does now without the delay. Perhaps you should better explain the problem.

Yes.

Please post a sketch that runs just one of the functions and does so to your satisfaction.

It looks like the two are mostly the same, but there are enough oddities that I request you also please post a sketch that runs just the other and does so to your satisfaction.

There are things in both that seem to be entirely without purpose. So it would be good to see each in its original context.

a7

Even without the delay, this still doesn't work as I want. Without the delay, both Routine 1 and Routine 2 start simultaneously.

No, they don't. With or without the delay, Routine1() run to completion, then Routine2() runs.

Hi @alto777,

I'm happy with how both sub-routines work individually. The sketch for these are below.

My end goal is for Routine1 to complete once - this is for a trailing LED to light up in accordance to the beatsin function defined (the defined BPM, duration and phase position are all good). Once this function has completed one cycle (duration is 750ms) is to then start Routine2 for one cycle (duration is 1500ms). Routine2 is similar but this moves the trailing LED in the opposite direction and for a different BPM and, duration and phase position.

So in the main loop i can't seem to get it to complete the routines in this order. They either don't work at all or both routines start simultaneously.

void Routine1 () {

  const uint32_t interval = 750UL; // half a 20BPM beat
  static unsigned long lastOffsetTime = 0;
  static uint32_t t0 = 0; // timebase
  static uint16_t phase = 0; // which segment of sine
  if(millis() - lastOffsetTime >= interval){
    lastOffsetTime += interval;
    t0 = millis() ; // update timebase
  }
  
  uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, t0, phase);

  leds [sinBeat] = CRGB::Blue;

  fadeToBlackBy (leds, NUM_LEDS, 50);

  FastLED.show();

}




void Routine2 () {

  const uint32_t interval1 = 1500UL; // half a 20BPM beat
  static unsigned long lastOffsetTime1 = 0;
  static uint32_t t01 = 0; // timebase
  static uint16_t phase1 = 32768/2; // which segment of sine
  if(millis() - lastOffsetTime1 >= interval1){
    lastOffsetTime1 += interval1;
    t01 = millis() ; // update timebase
  }
  
  uint16_t sinBeat1 = beatsin16(33.3, 0, NUM_LEDS - 1, t01, phase1);

  leds [sinBeat1] = CRGB::Red;

  fadeToBlackBy (leds, NUM_LEDS, 50);

  FastLED.show();

}

Hi @gfvalvo,

My goal is for Routine1 to complete once then start Routine2. When omit the delay, both routines start simultaneously.

I've run this through here:

You will likely need to write your own beatsin function, as it is you have no idea at which point in the LED strip the function will start.

There also needs to be a way to detect when the code has gone through a complete cycle, then you can run the first function until the cycle finished, then run the second function until its cycle finishes, etc.

Please post a complete it compiles we can run it ourselves sketch.

Two, actually. Complete sketches that each actually run and do the correct thing.

a7

They absolutely do not. Run the code below and observe the printouts on the Serial Monitor. Note that each Routine starts and completes before switching to the other Routine.

#include <FastLED.h>

#define NUM_LEDS  28
#define LED_PIN   3

CRGB leds [NUM_LEDS];

void setup() {
  Serial.begin(115200);
  delay(2000);
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness (255); 
      
}

void loop () {
  Routine1();
  Routine2();
  delay(250);
  
}


//Sub-Routines below. 

void Routine1() {
  Serial.println("Entered Routine1");
  const uint32_t interval = 750UL; // half a 20BPM beat
  static unsigned long lastOffsetTime = 0;
  static uint32_t t0 = 0; // timebase
  static uint16_t phase = 0; // which segment of sine
  if(millis() - lastOffsetTime >= interval){
    lastOffsetTime += interval;
    t0 = millis() ; // update timebase
  }
  
  uint16_t sinBeat = beatsin16(20, 0, NUM_LEDS - 1, t0, phase);

  leds [sinBeat] = CRGB::Blue;

  fadeToBlackBy (leds, NUM_LEDS, 50);

  FastLED.show();
  Serial.println("Completed Routine1");
}




void Routine2() {
  Serial.println("Entered Routine2");
  const uint32_t interval1 = 1500UL; // half a 20BPM beat
  static unsigned long lastOffsetTime1 = 0;
  static uint32_t t01 = 0; // timebase
  static uint16_t phase1 = 32768/2; // which segment of sine
  if(millis() - lastOffsetTime1 >= interval1){
    lastOffsetTime1 += interval1;
    t01 = millis() ; // update timebase
  }
  
  uint16_t sinBeat1 = beatsin16(33.3, 0, NUM_LEDS - 1, t01, phase1);

  leds [sinBeat1] = CRGB::Red;

  fadeToBlackBy (leds, NUM_LEDS, 50);

  FastLED.show();
  Serial.println("Completed Routine2");
}

The function completes, but the OP needs to clarify, as I read it the desire is for the LED strip to complete a full cycle of the sin wave within each function, not just display a single point from each function.

Both functions are designed to be called frequently. Very frequently.

To do one for awhile, then the other, call one a few thousand times, then a call the other one a few thousand times.

Or maybe add some kind of counter and indication of having "completed" whatever portion of their otherwise infinite activity you want to chop it down to.

a7

Thanks @gfvalvo.

I should have clarified earlier. My end goal it so have Routine1 to start and completely stop after 1 cycle (duration is 750ms) before starting Routine 2.

My confusion was that it looked as if they both started simultaneously because the each routine was continuously running albeit at different rates.

Agreed. An engineer needs to write their requirements / problem statement with absolute clarity. I insist upon that with the engineers who work for me.

@w1cyh welcome to the forum.

I called one of the functions 333 times with a for statement placed in the loop() function, then killed the loop with an infinite while.

I got one red traveler going from left to right.

  for (int ii = 0; ii < 333; ii++) {
    Routine1();
  }

So do that twice.

It messes up the nice non-blocking code. If you care, that can be fixed. The counting until "complete" idea would be the approach.

HTH

a7

@alto777 - thanks for this.

what do you mean by 'killed the loop with an infinite while'? - apologies if this obvious, i'm only just started with code

Just so I could see what only calling it a fixed number of times just once looks like:

void loop() {
  for (int ii = 0; ii < 333; ii++) {
     Routine1();
  }

  while (1);  // hangs here forever
}

I placed a while statement that never finishes after the for statement.

Obvsly you don't wanna do that usually.

Im in transit, so I can't try

void loop() {
  for (int ii = 0; ii < 333; ii++) {
     Routine1();
  }

  for (int ii = 0; ii < 333; ii++) {
     Routine2();
  }
}

That should do a red traveler and then a blue traveler and then a red traveler…

I am compelled to repeat that this may work, but it will mean you can't do much of anything else.

In a sketch that does very much, the loop() function should not have within it any loops that take significant time, as it would be time away from paying attention to switches and sensors and processes other than making the travelers go back and forth.

Ideally the loop() could be free to run thousands of times a second and pay attention to everything as long as all players behave.

Your Routine() and Routine2() were design to "play nice" and not hog the resource which is processor time.

What larger project is this to be part of? That is to ask if all this matters in any way.

a7

@alto777 thanks for your lengthy response with this.
I will try and play with this when i get home tonight.