Cut off and loop a script at a specific time

Hi there, my name is Tanalepy and I'm new here!
I've been working on some Arduino projects and I'm getting deeper into the possibillities.
My latest project is creating a small (about 70cm high) animatronic Transformer that can talk, make noise, uses a couple of LEDs and uses servos. But I've come across a problem.

I have a standalone DFplayer mini that plays a MP3 file (lasergun sounds). I'm using a NEOpixel ring that has to show lights at the same time that the DFplayer plays a certain part of the MP3 file.
After exactly 49 seconds the DFplayer loops the MP3 file, but I can't figure out how to loop the NeoPixel script at exactly 49 seconds.
Is there an option to cut off the script at exactly 49 seconds and loop the script on the Arduino?

Welcome to the group.

Always show us a good schematic of your proposed circuit.
Show us good images of your ‘actual’ wiring.
Give links to components.

In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch.

Use the < CODE / > icon from the ‘posting menu’ to attach the copied sketch.

Hi @tanalepy ,

welcome to the arduino-forum.

It is possible to do timing down to a precision of 0,01 seconds with an arduino.

Does "standalone" mean the DF-player is not started by a signal that is coming from the arduino?

In this case it would be pretty difficult to get the lighting in synchronisation with the sound.
Still doable but more difficult.

Easiest would be to give the start-signal from the arduino to start playing the soundfile
storing the momentary run-time of your arduino and then as function loop() is looping anway to check mimimum 100 times per second if 49 seconds have passed by or not.
If really 49 seconds have passed by starting the light-effects.

This fast looping of function loop() requires a programming technique called non-blocking.

You haven't posted your actual code yet. Without seeing your code it is impossible to say if you already use non-blocking timing or not. It is likely that you do not use it if your timing is based on delay().

Non blocking timing works very different than working with delay().

Ths forum can be of great help if you post your actual code and if you ask specific questions.

The specificness can start with something like
"can somebody recommend a tutorial how to learn ....."
and then if you start working with the tutorial
"I understand line 1 that is doing ....
but I do not understand what line 2 is doing / how to modify line 2 to work in my project"
etc.

So next step is to post your momentary actual code

best regards Stefan

Hi Stefan, That's correct. I'm using delay...
I also have to connect several servo's to the Arduino and I read that the delay function would also effect the servo's, correct?
"Standalone" does indeed mean that it is not starting from the arduino.

I'll post my code here. Is there like a "hard" loop function available to loop the whole script after 49 seconds (even if this script would run 51 seconds)?

#include <Adafruit_NeoPixel.h>
#ifdef AVR
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN 6

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 12

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)

// setup() function -- runs once at startup --------------------------------

void setup() {
// These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
// Any other board, you can remove this part (but no harm leaving it):
#if defined(AVR_ATtiny85) && (F_CPU == 16000000)
clock_prescale_set(clock_div_1);
#endif
// END of Trinket-specific code.

strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(200); // Set BRIGHTNESS to about 1/5 (max = 255)
}

// loop() function -- runs repeatedly as long as board is on ---------------

void loop() {

delay(7000);
// Deze is het laden
colorWipe(strip.Color(0, 0, 255), 20); // blue
colorWipe(strip.Color(0, 0, 0), 20); // off
delay(940);

// Dit is het knipperen of schieten
theaterChase(strip.Color(0, 0, 255), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 150), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 30), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 0), 20); // off, half brightness

delay(12940);
// Deze is het laden
colorWipe(strip.Color(0, 0, 255), 20); // blue
colorWipe(strip.Color(0, 0, 0), 20); // off
delay(940);

// Dit is het knipperen of schieten
theaterChase(strip.Color(0, 0, 255), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 255), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 255), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 150), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 30), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 0), 20); // off, half brightness

delay(19020);
// Deze is het laden
colorWipe(strip.Color(0, 0, 255), 20); // blue
colorWipe(strip.Color(0, 0, 0), 20); // off
delay(940);

// Dit is het knipperen of schieten
theaterChase(strip.Color(0, 0, 255), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 150), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 30), 20); // White, half brightness
theaterChase(strip.Color(0, 0, 0), 20); // off, half brightness
delay(7760);

// kopieer bovenstaande met schieten en laden. plak hierboven

rainbow(10); // Flowing rainbow cycle along the whole strip
theaterChaseRainbow(50); // Rainbow-enhanced theaterChase variant
}

// Some functions of our own for creating animated effects -----------------

// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}

// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
for(int a=0; a<5; a++) { // Repeat 10 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in steps of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}

// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 565536. Adding 256 to firstPixelHue each time
// means we'll make 5
65536/256 = 1280 passes through this loop:
for(long firstPixelHue = 0; firstPixelHue < 0; firstPixelHue += 256) {
// strip.rainbow() can take a single argument (first pixel hue) or
// optionally a few extras: number of rainbow repetitions (default 1),
// saturation and value (brightness) (both 0-255, similar to the
// ColorHSV() function, default 255), and a true/false flag for whether
// to apply gamma correction to provide 'truer' colors (default true).
strip.rainbow(firstPixelHue);
// Above line is equivalent to:
// strip.rainbow(firstPixelHue, 1, 255, 255, true);
strip.show(); // Update strip with new contents
delay(1000); // Pause for a moment
}
}

// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
int firstPixelHue = 0; // First pixel starts at red (hue 0)
for(int a=0; a<0; a++) { // Repeat 30 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
// 'c' counts up from 'b' to end of strip in increments of 3...
for(int c=b; c<strip.numPixels(); c += 3) {
// hue of pixel 'c' is offset by an amount to make one full
// revolution of the color wheel (range 65536) along the length
// of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels();
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
}

}

}
}

what script? you have posted no script.

@tanalepy
please re-edit your post with the code
so the code is shown as a code-section
See the tutorial-link at the bottom

The code is not called "skript" the code is called a sketch.

No. There might be a possability which would be very dirty ugly programming.
This is the reason why I do not show it to you.

It is done differently.
Your code must be restructured to become non-blocking.
The non-blocking code will run the one and only function loop() in a way that it will work.

This means as long as your DF-player starts standalone there is no possability to sync the code to the sound over time.

You can have it in sync for a few repeatings by carefully tweaking the wait-time. But as longer as you repeat the more deviation between sound and light-effects will occur.

post a picture and the datasheet of the DFPlayer this sounds as it should be very easy to let the arduino send the start-signal

showing code as a code-section

non-blocking timing

best regards Stefan

Yes, delay will make the sketch run jerky simply because delay code only lets one thing wait at a time while nothing else changes and nothing can be detected.

The difference in coding is not a huge step but it allows big ones and it entails a view like a wheel driving a machine.

I have 2 demo sketches that can be run (Serial Monitor set to 115200 baud) to show the difference between blocking and non-blocking code. It's an intro to converting blocking code to non-blocking to combine sketches to run smoothly together, on-time.

Posting those as replies next. Load and run them with serial monitor set for 115200 baud and see led13 blink and monitor output. THEN look at the code.

What runs in void loop should run quick and let the next thing do likewise. We use state machines to let the same function run processes as steps. One step may see if a button press has been detected and if not then return to run next time until aha! detection! and the step to run next time changes to react to the press... that kind of thing, a new way to think of small automation.
The principles are simple. Learning will be faster with small play that hardly matters.

// CombineSketchesBlocked 2022 by GoForSmoke @ Arduino.cc Forum
// Free to post anywhere complete and as is.

// This is the delay-codes combined to show how bad it is.

// LoopCounter value
#define microsInOneSecond 1000000UL

// PrintNumbers variables
unsigned long pnWait = 1000UL; 
int setNum = 60;
int printNum = 0;

// BlinkPattern variables
const byte ledPin = 13;
byte indexMax = 12;
byte index;

enum blinkStates {BLINK_1_ON, BLINK_1_OFF, BLINK_2_ON, BLINK_2_OFF, BLINK_3_ON, BLINK_3_OFF};
blinkStates blinkStep; // state tracking for BlinkPattern() below


void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Combine Sketches Blocked, free by GoForSmoke" ));
  Serial.println( F( "This sketch shows how NOT to combine sketches.\n" ));
  Serial.println( F( "Press Enter to toggle monitor scrolling." ));

  pinMode( ledPin, OUTPUT );
  
  digitalWrite( ledPin, HIGH );  // 2 secs before the scroll starts
  delay( 500 );
  digitalWrite( ledPin, LOW );
  delay( 500 );
  digitalWrite( ledPin, HIGH );
  delay( 500 );
  digitalWrite( ledPin, LOW );
  delay( 500 );

  blinkStep = BLINK_1_ON;  // actual value is 0
};


// LoopCounter -- void loop() code to count loops per second
void LoopCounter() // tells the average response speed of void loop()
{ 
  delay( 1000 );
  Serial.println( "1" );
}


// PrintNumbers -- void loop() code to count from one value to another with wait between.
void PrintNumbers()  // a loop with a delay in it becomes...
{
  for ( printNum = 0; printNum <= setNum; printNum++ )
  {
    Serial.print( F( "Number " ));
    Serial.print( printNum );
    Serial.print( F( "   Time " ));
    Serial.println( millis() );
    delay( pnWait );
  }

  printNum = 0;
}


// BlinkPattern -- void loop() code to blink led13 using delay()
void BlinkPattern()  // does the same as above without delay()
{
  digitalWrite( ledPin, HIGH );   // --  BLINK_1_ON
  Serial.print( F( "BLINK_1_ON, time " ));
  Serial.println( millis());
  delay( 500 );
  digitalWrite( ledPin, LOW );    // --  BLINK_1_OFF
  Serial.print( F( "BLINK_1_OFF, time " ));
  Serial.println( millis());
  delay( 500 );
  for ( byte i = 0; i < 12; i++ )
  {
    digitalWrite( ledPin, HIGH );   // --  BLINK_2_ON
    Serial.print( F( "BLINK_2_ON, time " ));
    Serial.println( millis());
    delay( 250 );
    digitalWrite( ledPin, LOW );    // --  BLINK_2_OFF
    Serial.print( F( "BLINK_2_OFF, time " ));
    Serial.println( millis());
    delay( 250 );
  }
  digitalWrite( ledPin, HIGH );   // --  BLINK_3_ON
  Serial.print( F( "BLINK_3_ON, time " ));
  Serial.println( millis());
  delay( 1000 );
  digitalWrite( ledPin, LOW );    // --  BLINK_3_OFF
  Serial.print( F( "BLINK_3_ON, time " ));
  Serial.println( millis());
  delay( 1000 );
}


void StopGo()  // user keyboard block / unblock 
{
  if ( Serial.available() )
  {
    while ( Serial.available() )  // clearing the buffer
    {
      Serial.read();
      delay(1); // don't care
    }
  }
  else
  {
    return;
  }
  
  while ( ! Serial.available() ); // waiting for Go, ! is logical NOT
  
  while ( Serial.available() )  // clearing the buffer
  {
    Serial.read();
    delay(1); // don't care
  }
}

void loop()  // runs over and over, see how often
{            
  LoopCounter(); // the function runs as a task, the optimizer will inline the code.
  PrintNumbers();
  BlinkPattern();
  StopGo();
}

// CombineSketchesDemo 2022 by GoForSmoke @ Arduino.cc Forum
// Free for use, May 23, 2022 by GFS. Compiled on Arduino 2.1.0.5
// Free to post anywhere complete and as is.

// This sketch shows a general method to get rid of delays in code.
// You could upgrade code that delays to combine into this sketch.

// .. adding delays in loop cases
// revisions with forum help:
// dlloyd --  added state enums May 11. <--- changed as needed.

////////////////////////////////////////////////////////////////////////////

// This example/demo takes 3 sketches that use delay() and COMBINE THEM
// by using simple techniques the sketch demonstrates.
// Plus there's a user stop/go that violates how the 3 together work.

// Code that does not sit or loop in one spot to wait, 
// does not block other code from executing during a wait.
// In 1 ms, code can use or lose 16000 cpu cycles. So don't block!

// The 3 delay-sketches void loop() code goes into 3 functions that run as tasks. 
// Arduino void loop() runs the same functions over and over, these functions don't wait
// but instead check time and if time's not up then run the next function.
// if ( Serial.available() ) is just such a thing, not waiting around for data.

// Every delay() has a built-in timer. I replace it with a timer that only runs when set.
// The timer is first in the function so that the function can return until timeout.
// The timer only runs when the time to wait is set > 0, else the function continues.

// Where a delay was removed, the time is set and the timer runs for the next many loops.
// When the time is done, the time to wait set = 0. What was a delay() sets the timer. 

// This demo also addresses loops inside of void loop(), which can hog cycles terribly.
// It also contains a state machine, a code tool of value beyond what the demo does.
// A State Machine is code written in steps of what to do according to what's been done.
// A state variable holds what to do next time the machine runs.
// perhaps the 1st state waits for input and when it gets it the code changes state..
// to run what to do with the input. State Machines can run inside of state machines.

// task StopGo. --  Block - unBlock on a keystroke to stop serial monitor 

// task LoopCounter  --  lets you know how often void loop() ran last second.
// LoopCounter value
#define microsInOneSecond 1000000UL

// task PrintNumbers --  prints from printNum to setNum, pnWait ms apart
// PrintNumbers variables
unsigned long pnStart;
unsigned long pnWait = 1000UL; 
int setNum = 60;
int printNum = 0;

// task BlinkPattern --  blinks led13
// BlinkPattern variables
const byte ledPin = 13;
unsigned long blinkStart;
unsigned long blinkWait; 
byte indexMax = 12;
byte index;

enum blinkStates {BLINK_1_ON, BLINK_1_OFF, BLINK_2_ON, BLINK_2_OFF, BLINK_3_ON, BLINK_3_OFF};
blinkStates blinkStep; // state tracking for BlinkPattern() below


void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Combine Sketches Demo, free by GoForSmoke" ));
  Serial.println( F( "This sketch shows how to combine sketches.\n" ));
  Serial.println( F( "Press Enter to toggle monitor scrolling." ));

  pinMode( ledPin, OUTPUT );
  
  digitalWrite( ledPin, HIGH );  // 2 secs before the scroll starts
  delay( 500 );
  digitalWrite( ledPin, LOW );
  delay( 500 );
  digitalWrite( ledPin, HIGH );
  delay( 500 );
  digitalWrite( ledPin, LOW );
  delay( 500 );

  blinkStep = BLINK_1_ON;  // actual value is 0
};


/* LoopCounter -- void loop() code to count loops per second
*
*  delay( 1000 );
*  Serial.println( "1" );
*/

void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
  static unsigned long count, countStartMicros; // only this function sees these

  count++; // adds 1 to count after any use in an expression, here it just adds 1.
  if ( micros() - countStartMicros >= microsInOneSecond ) // 1 second
  {
    countStartMicros += microsInOneSecond; // for a regular second
    Serial.println( count ); // 32-bit binary into decimal text = many micros
    count = 0; // don't forget to reset the counter 
  }
}


/* PrintNumbers -- void loop() code to count from one value to another with wait between.
*
* for ( printNum = 0; printNum <= setNum; printNum++ )
* {
*   Serial.print( F( "Number " ));
*   Serial.print( printNum );
*   Serial.print( F( "   Time " ));
*   Serial.println( millis() );
*   delay( pnWait );
* }
*/

// this task runs once. suppose that StopGo made it start again?

void PrintNumbers()  // a loop with a delay in it becomes...
{
  if ( setNum < 1 )  return; // how to turn this task off, setNum = 0;
  
  // This repeat timer replaces delay()  
  // start of repeat timer
  if ( millis() - pnStart < pnWait )  // wait is not over
  {
    return; // instead of blocking, the undelayed function returns
  }

  Serial.print( F( "Number " ));
  Serial.print( printNum );
  Serial.print( F( "   Time " ));
  Serial.println( millis() );
  
  pnStart += pnWait; // starting 1 sec after last start, not the same as = millis()

  if ( ++printNum >= setNum )  
  { 
    setNum = printNum = 0;
  }
}


/* BlinkPattern -- void loop() code to blink led13 using delay()
 * 
 * digitalWrite( ledPin, HIGH );   --  BLINK_1_ON
 * Serial.print( F( "BLINK_1_ON, time " ));
 * Serial.println( millis());
 * delay( 500 );
 * digitalWrite( ledPin, LOW );    --  BLINK_1_OFF
 * Serial.print( F( "BLINK_1_OFF, time " ));
 * Serial.println( millis());
 * delay( 500 );
 * for ( i = 0; i < 12; i++ )
 * (
 *   digitalWrite( ledPin, HIGH );   --  BLINK_2_ON
 *   Serial.print( F( "BLINK_2_ON, time " ));
 *   Serial.println( millis());
 *   delay( 250 );
 *   digitalWrite( ledPin, LOW );    --  BLINK_2_OFF
 *   Serial.print( F( "BLINK_2_OFF, time " ));
 *   Serial.println( millis());
 *   delay( 250 );
 * }
 * digitalWrite( ledPin, HIGH );   --  BLINK_3_ON
 * Serial.print( F( "BLINK_3_ON, time " ));
 * Serial.println( millis());
 * delay( 1000 );
 * digitalWrite( ledPin, LOW );    --  BLINK_3_OFF
 * Serial.print( F( "BLINK_3_ON, time " ));
 * Serial.println( millis());
 * delay( 1000 );
 */

void BlinkPattern()  // does the same as above without delay()
{
  // This one-shot timer replaces every delay() removed in one spot.  
  // start of one-shot timer
  if ( blinkWait > 0 ) // one-shot timer only runs when set
  {
    if ( millis() - blinkStart < blinkWait )
    {
      return; // instead of blocking, the undelayed function returns
    }
    else
    {
      blinkWait = 0; // time's up! turn off the timer and run the blinkStep case
    }
  }
  // end of one-shot timer

  // here each case has a timed wait but cases could change Step on pin or serial events.
  switch( blinkStep )  // runs the case numbered in blinkStep
  {
    case BLINK_1_ON :
    digitalWrite( ledPin, HIGH );
    Serial.print( F( "BLINK_1_ON, time " ));
    Serial.println( blinkStart = millis()); // able to set a var to a value I pass to function
    blinkWait = 500; // for the next half second, this function will return on entry.
    blinkStep = BLINK_1_OFF;   // when the switch-case runs again it will be case 1 that runs
    break; // exit switch-case

    case BLINK_1_OFF :
    digitalWrite( ledPin, LOW );
    Serial.print( F( "BLINK_1_OFF, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 500;
    blinkStep = BLINK_2_ON;
    break;

    case BLINK_2_ON :
    digitalWrite( ledPin, HIGH );
    Serial.print( F( "BLINK_2_ON, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 250;
    blinkStep = BLINK_2_OFF;
    break;

    case BLINK_2_OFF :
    digitalWrite( ledPin, LOW );
    Serial.print( F( "BLINK_2_OFF, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 250;
    // ******  this replaces the for-loop in non-blocking code.  ******
    if ( index++ < indexMax ) // index gets incremented after the compare
    {
      blinkStep = BLINK_2_ON;
    }
    else
    {
      index = 0;
      blinkStep = BLINK_3_ON;
    }  // end of how to for-loop in a state machine without blocking execution.
    break;

    case BLINK_3_ON :
    digitalWrite( ledPin, HIGH );
    Serial.print( F( "BLINK_3_ON, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 1000;
    blinkStep = BLINK_3_OFF;
    break;

    case BLINK_3_OFF :
    digitalWrite( ledPin, LOW );
    Serial.print( F( "BLINK_3_OFF, time " ));
    Serial.println( blinkStart = millis());
    blinkWait = 1000;
    blinkStep = BLINK_1_ON; // start again
    break;
  }
}


void StopGo()  // user keyboard block / unblock 
{
  if ( Serial.available() )
  {
    while ( Serial.available() )  // clearing the buffer
    {
      Serial.read();
      delay(1); // don't care
    }
  }
  else
  {
    return;
  }
  
  while ( ! Serial.available() ); // waiting for Go, ! is logical NOT
  
  while ( Serial.available() )  // clearing the buffer
  {
    Serial.read();
    delay(1); // don't care
  }
}

void loop()  // runs over and over, see how often
{            
  LoopCounter(); // the function runs as a task, the optimizer will inline the code.
  PrintNumbers();
  BlinkPattern();
  StopGo();
}

We haven’t yet seen the schematic requested.

Your code is hard to read because you did not format it as requested in the guidance. (By using <Ctrl + t>)

Meanwhile I’d echo Stefan’s recommendation to trigger the action required directly from the DFR MP3 player module.

A sequence of delays() can be replaced by a non-blocking sequencer

The basic principle of your code is

do_A
delay(1000)
do_B
delay(500)
do_C
delay 250
repeat 5 times
  do_E
  delay(400)
  do_F
  delay(250)

etc. etc. etc.

.
.
With non-blocking timing is done this way

in case of 

  modeA:
    do_A;
    change to modeWait_A

  modeWait_A:
    check if 1000 milliseconds of time have passed by
       if yes change to mode_B

  modeB:
    do_B;
    change to modeWait_B

  modeWait_B:
    check if 500 milliseconds of time have passed by
       if yes change to mode_C


etc. etc. 

This is the basic principle.
You need to learn a few more things to really write code for it.
But it is very important that you have understood the basic principle

The much more common name for this is a state-machine
each thing that in the above example has a name like

  modeA:
  modeWait_A:
  modeB:
etc. etc.

is a state and this means the state-machine proceeds from state to state
first mode_A,
second modeWait_A
third mode_B

You should learn the details of non-blocking timing
because the non-blocking timing is used in every

  modeWait_A:
  modeWait_B:
etc. etc. etc.

and after that you should learn how state-machine as programmed

So here is a tutorial about non-blocking timing

and here is a tutorial about state-machines

best regards Stefan

1 Like

Thanks everybody for your replies.
I've changed my mind. Instead of looping the sketch after 49 seconds, I've timed it with non-blocking codes.

This is what I have now. I do encounter another problem when adding servos to the sketch, but I'll make another post about that.

#include <Adafruit_NeoPixel.h>

#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

#define LED_PIN    6
#define LED_COUNT 12

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

unsigned long previousMillis = 0;
const unsigned long interval1 = 6900;
const unsigned long interval2 = 1440;
const unsigned long interval3 = 13940;
const unsigned long interval4 = 17620;
const unsigned long interval5 = 11600;
unsigned long intervalStartTime = 0;
int state = 0;

void setup() {
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  strip.begin();
  strip.show();
  strip.setBrightness(200);
}




void loop() {
  unsigned long currentMillis = millis();
/*shot 1*/
  switch (state) {
    case 0:
      if (currentMillis - previousMillis >= interval1) {
        previousMillis = currentMillis;
        state = 1;
      }
      break;

    case 1:
      colorWipe(strip.Color(0, 0, 255), 20);
      state = 2;
      break;

    case 2:
      colorWipe(strip.Color(0, 0, 0), 20);
      state = 3;
      intervalStartTime = currentMillis;
      break;

    case 3:
      if (currentMillis - intervalStartTime >= interval2) {
        state = 4;
      }
      break;

    case 4:
      theaterChase(strip.Color(0, 0, 255), 20);
      state = 5;
      break;

    case 5:
      theaterChase(strip.Color(0, 0, 150), 20);
      state = 6;
      break;

    case 6:
      theaterChase(strip.Color(0, 0, 30), 20);
      state = 7;
      break;

    case 7:
      theaterChase(strip.Color(0, 0, 0), 20);
      state = 8;
      intervalStartTime = currentMillis;
      break;


/* shot 2 */
    case 8:
      if (currentMillis - intervalStartTime >= interval2) {
        state = 9;
      }
      break;

    case 9:
      if (currentMillis - previousMillis >= interval3) {
        previousMillis = currentMillis;
        state = 10;
      }
      break;

    case 10:
      colorWipe(strip.Color(0, 0, 255), 20);
      state = 11;
      break;

    case 11:
      colorWipe(strip.Color(0, 0, 0), 20);
      state = 12;
      intervalStartTime = currentMillis;
      break;

    case 12:
      if (currentMillis - intervalStartTime >= interval2) {
        state = 13;
      }
      break;

    case 13:
      theaterChase(strip.Color(0, 0, 255), 20);
      state = 14;
      break;

    case 14:
      theaterChase(strip.Color(0, 0, 150), 20);
      state = 15;
      break;

    case 15:
      theaterChase(strip.Color(0, 0, 100), 20);
      state = 16;
      break;

    case 16:
      theaterChase(strip.Color(0, 0, 50), 20);
      state = 17;
      intervalStartTime = currentMillis;
      break;

       case 17:
      theaterChase(strip.Color(0, 0, 25), 20);
      state = 18;
      break;

    case 18:
      theaterChase(strip.Color(0, 0, 0), 20);
      state = 19;
      intervalStartTime = currentMillis;
      break;
/* shot 3 */
    case 19:
      if (currentMillis - intervalStartTime >= interval4) {
        state = 20; // Reset to the initial state
      }
      break;


 case 20:
      colorWipe(strip.Color(0, 0, 255), 20);
      state = 21;
      break;

    case 21:
      colorWipe(strip.Color(0, 0, 0), 20);
      state = 22;
      intervalStartTime = currentMillis;
      break;

    case 22:
      if (currentMillis - intervalStartTime >= interval2) {
        state = 23;
      }
      break;

    case 23:
      theaterChase(strip.Color(0, 0, 255), 20);
      state = 24;
      break;

    case 24:
      theaterChase(strip.Color(0, 0, 150), 20);
      state = 25;
      break;

    case 25:
      theaterChase(strip.Color(0, 0, 30), 20);
      state = 26;
      break;

    case 26:
      theaterChase(strip.Color(0, 0, 0), 20);
      state = 27;
      intervalStartTime = currentMillis;
      break;


case 27:
      if (currentMillis - intervalStartTime >= interval5) {
        state = 0;
      }
      break;
      
  }
}

void colorWipe(uint32_t color, int wait) {
  for (int i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, color);
    strip.show();
    delay(wait);
  }
}

void theaterChase(uint32_t color, int wait) {
  for (int a = 0; a < 5; a++) {
    for (int b = 0; b < 3; b++) {
      strip.clear();
      for (int c = b; c < strip.numPixels(); c += 3) {
        strip.setPixelColor(c, color);
      }
      strip.show();
      delay(wait);
    }
  }
}

Not so fast. Theater chase and color wipe both use delay() and get used a lot.

In your state machine, you have a few timers that if you put the switch-case inside of a timer like

if ( millis() - start >= wait )  // this is the only timer needed
{                                             // exit a case after setting start
  wait = 0;                             // and wait and this timer waits
  switch( state )
  {
//  cases go here
//  when start = millis() and wait > 0, the machine stops.
//  run multiple state machines as independent tasks
//  if you need something to run while the first waits...
//  this applies when coordinating servos with lights.
  }
}

It's important to not try and make one single state machine wait for lights while also running motors. It probably -can- be done but only with added complexity to force-synchronize what doesn't need to be. When your code doesn't block, you can mix converted sketches that did block but worked only un-blocked they both work as before. Each acts independently so if the light state machine is running out a wait, the servo state machine keeps on running.

I did a demo that blinked and faded leds at random with a blink task, a fade task and and choose next led task. Chosen ledchooses which one to fade and blink turns fade on and off. Little parts with little to debug.
It saves time over Grand Complexity every time.

added: I would make the clear and theater functions work as 1-shot tasks that run when flagged by some other function and do the thing once, but over many void loop cycles.

Stick with it, make intelligent replies and myself and others will give you help to do this and able to do much more.

inside your functions colorWipe() and theaterChase() you use for-loops and delay()

this means as long as you are inside these functions code-execution stays inside the for-loops
= blocking code.

Here is a visualisation of how blocking-code is executed and how non-blocking code that does the same thing as a for-loop is executed

Your example gave me an idea how to write a function that will be a non-blocking for-loop

I have chosen this name to make the transition from blocking for-loops to non-blocking coding a bit easier. Though this name does not really fit in all details. Because the function requires to be embedded into the one big loop that is used to still make everything looping which is

void loop() itself

best regards Stefan

void loop() yes, is the driver. The code has to store any indexes and change them to cover what ( i=0; i<x; i++ ) does.

Inside of a state machine, the for-type-loop can be achieved by changing state in the end-loop state to the start-loop state a counted number of times with our trusty index variable.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.