Need help getting a program to loop

I should preface this by saying that I’m not a programmer and have absolutely zero experience, so if you do choose to help me, I ask that you keep that in mind and just try to explain this to me like a five year old.

Now that that’s out of the way, I’ll explain my predicament:

I’m trying to make my own Stranger Things wall of lights that display messages with corresponding letters. I’m doing this for work and I stumbled on a tutorial that was pretty detailed until it got to the code. It just said, “here’s the code. Have fun!” (Here’s the article if you’re curious: Tutorial: Make your own Stranger Things Christmas Lights for Halloween | by Mike Alvarez | Medium)

Anyway, I pasted the code into the program and it works. But it only plays through the message once. I would like it to continue to display this message with a small delay in between. I know that I need to get it into the void loop() section. I’ve tried to move it there every which way I see that makes sense. The program will run but the lights don’t light up. I’ll attach the code I’m using at the bottom here.

#include "FastLED.h"
    // the milliseconds to give each letter
#define MILLIS_PER_LETTER 1750
    // number of LEDs in the strip
#define NUM_LEDS 50
    // the data pin 
#define DATA_PIN 11
    // an array to keep track of the LEDs
CRGB leds[NUM_LEDS];
    // the message we will display
String message;
    // the time we received the message
unsigned long received;
    // we"ll use all 26 letters of the alphabet
#define NUM_LETTERS 26
    // the LED number (start counting from 0) that we light up to show our message
const int LETTER_LEDS[NUM_LETTERS] = {
     /*A*/ 7
    ,/*B*/ 8
    ,/*C*/ 9
    ,/*D*/ 10
    ,/*E*/ 11
    ,/*F*/ 12
    ,/*G*/ 13
    ,/*H*/ 14
    ,/*I*/ 32
    ,/*J*/ 31
    ,/*K*/ 30
    ,/*L*/ 29
    ,/*M*/ 28
    ,/*N*/ 26
    ,/*O*/ 25
    ,/*P*/ 24
    ,/*Q*/ 23
    ,/*R*/ 38
    ,/*S*/ 39
    ,/*T*/ 40
    ,/*U*/ 41
    ,/*V*/ 42
    ,/*W*/ 44
    ,/*X*/ 45
    ,/*Y*/ 46
    ,/*Z*/ 47
};
// how many colors to cycle through for the lights
#define NUM_COLORS 4


void setup() {
        // send print statements at 9600 baud
     Serial.begin(9600);
        // initialize the LEDS
     FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
        // set them all to be off
     fill_solid(leds, NUM_LEDS, CRGB::Black);
     FastLED.show();
        // connect to wifi.
        // this message will show until it is overwritten from Firebase and shown if Firebase fails
     message = "welcome to specs";
     received = millis();
}


void loop() {
        // how many milliseconds have elapsed since the last message came in
    unsigned long elapsed = millis() - received;

        // assuming MILLIS_PER_LETTER, what letter (index) ofthe message should we be on?
     int index = elapsed/MILLIS_PER_LETTER;
        // if the letter we should technically be on is within the bounds of the message
     if(index < message.length()) {
            // get the character letter we should print
         char letter = message.charAt(index);
            // if the character is between 'a' and 'z" (no numbers, spaces, or punctuations)
         if(letter >= 'a' && letter <= 'z'){
                // how bright to make this LED from 0 to 1, this is what makes them fade in and out
                // it calculates what percent we are completed with the letter, and makes it fade in from 0-50% and fade out from 50-100%
                // the formula can be visualized here: https://www.desmos.com/calculator/5qk8imeny4
             float brightness = 1-abs((2*(elapsed%MILLIS_PER_LETTER)/((float)MILLIS_PER_LETTER))-1);
             uint8_t value = 255 * brightness;
             
                // get the LED number the letter should be in, assuming our array starts at 'a" and ends at 'z"
             int letter_index = letter-'a';
             int led = LETTER_LEDS[letter_index];
                // get a rotation of colors, so that every NUM_COLORS lights, it loops
                // e.g. red, yellow, green, blue, red, yellow green blue
             uint8_t hue = (letter_index%NUM_COLORS*255)/NUM_COLORS;
                // set that LED to the color
             leds[led] = CHSV(hue, 255, value);
             FastLED.show();
                // set it to black so we don't have to remember the last LED we turned on
             leds[led] = CRGB::Black;
             
             Serial.print(letter);
             Serial.print("\t!");
             Serial.print(led);
             Serial.print("\t=");
             Serial.print(brightness);
             Serial.print("\t@");
             Serial.print(elapsed);
             Serial.println();
         } else {
                // if the letter wasn't a-z then, we just turn off all the leds
             FastLED.show();
         }
     } else {
            // if the letter is beyond the bounds of the message, we just turn off all the leds
        FastLED.show();
     }
}

Post code in line with code tags (and properly indented). Don't attach. We can't read it here

Here’s his sketch:

#include "FastLED.h"
// the milliseconds to give each letter
#define MILLIS_PER_LETTER 1750
// number of LEDs in the strip
#define NUM_LEDS 50
// the data pin 
#define DATA_PIN 11
// an array to keep track of the LEDs
CRGB leds[NUM_LEDS];
// the message we will display
String message;
// the time we received the message
unsigned long received;
// we"ll use all 26 letters of the alphabet
#define NUM_LETTERS 26
// the LED number (start counting from 0) that we light up to show our message
const int LETTER_LEDS[NUM_LETTERS] = {
 /*A*/ 7
,/*B*/ 8
,/*C*/ 9
,/*D*/ 10
,/*E*/ 11
,/*F*/ 12
,/*G*/ 13
,/*H*/ 14
,/*I*/ 32
,/*J*/ 31
,/*K*/ 30
,/*L*/ 29
,/*M*/ 28
,/*N*/ 26
,/*O*/ 25
,/*P*/ 24
,/*Q*/ 23
,/*R*/ 38
,/*S*/ 39
,/*T*/ 40
,/*U*/ 41
,/*V*/ 42
,/*W*/ 44
,/*X*/ 45
,/*Y*/ 46
,/*Z*/ 47
};
// how many colors to cycle through for the lights
#define NUM_COLORS 4
void setup() {
  // send print statements at 9600 baud
 Serial.begin(9600);
// initialize the LEDS
 FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
// set them all to be off
 fill_solid(leds, NUM_LEDS, CRGB::Black);
 FastLED.show();
// connect to wifi.
// this message will show until it is overwritten from Firebase and shown if Firebase fails
 message = "welcome to specs";
 received = millis();
}
void loop() {
 // how many milliseconds have elapsed since the last message came in
unsigned long elapsed = millis() - received;

// assuming MILLIS_PER_LETTER, what letter (index) ofthe message should we be on?
 int index = elapsed/MILLIS_PER_LETTER;
// if the letter we should technically be on is within the bounds of the message
 if(index < message.length()) {
 // get the character letter we should print
 char letter = message.charAt(index);
// if the character is between 'a' and ‘z" (no numbers, spaces, or punctuations)
 if(letter >= 'a' && letter <= 'z'){
 // how bright to make this LED from 0 to 1, this is what makes them fade in and out
 // it calculates what percent we are completed with the letter, and makes it fade in from 0–50% and fade out from 50–100%
 // the formula can be visualized here: https://www.desmos.com/calculator/5qk8imeny4
 float brightness = 1-abs((2*(elapsed%MILLIS_PER_LETTER)/((float)MILLIS_PER_LETTER))-1);
 uint8_t value = 255 * brightness;
 
 // get the LED number the letter should be in, assuming our array starts at ‘a" and ends at ‘z"
 int letter_index = letter-'a';
 int led = LETTER_LEDS[letter_index];
// get a rotation of colors, so that every NUM_COLORS lights, it loops
 // e.g. red, yellow, green, blue, red, yellow green blue
 uint8_t hue = (letter_index%NUM_COLORS*255)/NUM_COLORS;
// set that LED to the color
 leds[led] = CHSV(hue, 255, value);
 FastLED.show();
 // set it to black so we don’t have to remember the last LED we turned on
 leds[led] = CRGB::Black;
 
 Serial.print(letter);
 Serial.print("\t!");
 Serial.print(led);
 Serial.print("\t=");
 Serial.print(brightness);
 Serial.print("\t@");
 Serial.print(elapsed);
 Serial.println();
 } else {
 // if the letter wasn’t a-z then, we just turn off all the leds
 FastLED.show();
 }
 } else {
 // if the letter is beyond the bounds of the message, we just turn off all the leds
 FastLED.show();
 }
}

I hate it when there is no white space or indenting, but … :rolleyes:

Try modifying the loop() function by encasing all it’s code with a looping structure, just use a delay here, since it’s gonna be short, and there is nothing going on then anyway:

// ...

void loop() {

    while (1) {  // loop indefinitely

      /**** put all the stuff from your loop() function here ******/

      delay(3000);  // pause 3 seconds between runs

    }
}

If the original program is nicely written, then my mod should run, wait 3 seconds, then run again. If it doesn’t run correctly the second time, then some of the statements in the set up routine need to be executed between the delay() function and the end of the while loop. Try this and then figure out how to do that mod.

If you want to do ‘existing code modification’ you should learn enough programming your machine in order to understand what the code does.

Here is the OP’s code, with a little formatting to make it readable

#include "FastLED.h"
    // the milliseconds to give each letter
#define MILLIS_PER_LETTER 1750
    // number of LEDs in the strip
#define NUM_LEDS 50
    // the data pin 
#define DATA_PIN 11
    // an array to keep track of the LEDs
CRGB leds[NUM_LEDS];
    // the message we will display
String message;
    // the time we received the message
unsigned long received;
    // we"ll use all 26 letters of the alphabet
#define NUM_LETTERS 26
    // the LED number (start counting from 0) that we light up to show our message
const int LETTER_LEDS[NUM_LETTERS] = {
     /*A*/ 7
    ,/*B*/ 8
    ,/*C*/ 9
    ,/*D*/ 10
    ,/*E*/ 11
    ,/*F*/ 12
    ,/*G*/ 13
    ,/*H*/ 14
    ,/*I*/ 32
    ,/*J*/ 31
    ,/*K*/ 30
    ,/*L*/ 29
    ,/*M*/ 28
    ,/*N*/ 26
    ,/*O*/ 25
    ,/*P*/ 24
    ,/*Q*/ 23
    ,/*R*/ 38
    ,/*S*/ 39
    ,/*T*/ 40
    ,/*U*/ 41
    ,/*V*/ 42
    ,/*W*/ 44
    ,/*X*/ 45
    ,/*Y*/ 46
    ,/*Z*/ 47
};
// how many colors to cycle through for the lights
#define NUM_COLORS 4


void setup() {
        // send print statements at 9600 baud
     Serial.begin(9600);
        // initialize the LEDS
     FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
        // set them all to be off
     fill_solid(leds, NUM_LEDS, CRGB::Black);
     FastLED.show();
        // connect to wifi.
        // this message will show until it is overwritten from Firebase and shown if Firebase fails
     message = "welcome to specs";
     received = millis();
}


void loop() {
        // how many milliseconds have elapsed since the last message came in
    unsigned long elapsed = millis() - received;

        // assuming MILLIS_PER_LETTER, what letter (index) ofthe message should we be on?
     int index = elapsed/MILLIS_PER_LETTER;
        // if the letter we should technically be on is within the bounds of the message
     if(index < message.length()) {
            // get the character letter we should print
         char letter = message.charAt(index);
            // if the character is between 'a' and ‘z" (no numbers, spaces, or punctuations)
         if(letter >= 'a' && letter <= 'z'){
                // how bright to make this LED from 0 to 1, this is what makes them fade in and out
                // it calculates what percent we are completed with the letter, and makes it fade in from 0–50% and fade out from 50–100%
                // the formula can be visualized here: https://www.desmos.com/calculator/5qk8imeny4
             float brightness = 1-abs((2*(elapsed%MILLIS_PER_LETTER)/((float)MILLIS_PER_LETTER))-1);
             uint8_t value = 255 * brightness;
             
                // get the LED number the letter should be in, assuming our array starts at ‘a" and ends at ‘z"
             int letter_index = letter-'a';
             int led = LETTER_LEDS[letter_index];
                // get a rotation of colors, so that every NUM_COLORS lights, it loops
                // e.g. red, yellow, green, blue, red, yellow green blue
             uint8_t hue = (letter_index%NUM_COLORS*255)/NUM_COLORS;
                // set that LED to the color
             leds[led] = CHSV(hue, 255, value);
             FastLED.show();
                // set it to black so we don’t have to remember the last LED we turned on
             leds[led] = CRGB::Black;
             
             Serial.print(letter);
             Serial.print("\t!");
             Serial.print(led);
             Serial.print("\t=");
             Serial.print(brightness);
             Serial.print("\t@");
             Serial.print(elapsed);
             Serial.println();
         } else {
                // if the letter wasn’t a-z then, we just turn off all the leds
             FastLED.show();
         }
     } else {
            // if the letter is beyond the bounds of the message, we just turn off all the leds
        FastLED.show();
     }
}

…R

I reckon a simple solution is to change the name of the existing function that is called loop() to showMessage() and create a new function called loop() with this content

void loop() {
  if (millis() - prevShowMillis >= showInterval) {
     prevShowMillis += showInterval;
     received = millis();
     showMessage();
  }
}

and at the top of the code you will also need to define the variables

unsigned long prevShowMillis;
unsigned long showInterval = 5000; // show every 5 secs

...R

sorry this is just un-readable... are spaces and blank lines expensive where you live? press also ctrl-T to properly indent the code...

Sorry, guys. I updated the OP with the code Robin2 formatted for me. Hopefully that helps me find a solution.

Try modifying the loop() function by encasing all it's code with a looping structure, just use a delay here, since it's gonna be short, and there is nothing going on then anyway:

// ...

void loop() {

   while (1) {  // loop indefinitely

     /**** put all the stuff from your loop() function here ******/

     delay(3000);  // pause 3 seconds between runs

   } }




If the original program is nicely written, then my mod should run, wait 3 seconds, then run again. If it doesn't run correctly the second time, then some of the statements in the set up routine need to be executed between the delay() function and the end of the while loop. Try this and then figure out how to do that mod.

If you want to do 'existing code modification' you should learn enough programming your machine in order to understand what the code does.

This was the closest to helping. I plugged it in and the program ran but still won't repeat itself.

This was the closest to helping. I plugged it in and the program ran but still won’t repeat itself.

Some code you didn’t share does something you didn’t explain. You want it to do something else. Exactly what that is is not clear. Try to explain what you want in non-programmer’s terms, because you are not doing that well using programmer’s terms.

PaulS: Some code you didn't share does something you didn't explain. You want it to do something else. Exactly what that is is not clear. Try to explain what you want in non-programmer's terms, because you are not doing that well using programmer's terms.

Okay.

I want my LEDs to continually show the same pattern, flashing one by one until it is complete, and then repeat itself infinitely. It goes through the sequence once and stops. I clearly have no idea what I'm doing, and this is the first time I've ever even dabbled in programming.

My entire code is at the top of this thread. I copied and pasted it from a blog post written over a year ago, and I'm trying to find a solution to my problem using the resources at my disposal.

My entire code is at the top of this thread.

So, you were not being honest when you said that you made some changes, and that they were not effective?

PaulS: So, you were not being honest when you said that you made some changes, and that they were not effective?

What? The original code I started with is at the top of the thread. I tried what ChrisTenone said to do and it didn't work. It's very possible that it's user error on my part. Once again, I don't know what I'm doing, so I don't understand why we're getting caught up in semantics. I'm just looking for some help from some people that have exponentially more experience with this stuff than I do.

I'm just looking for some help from some people that have exponentially more experience with this stuff than I do.

Well, clearly that includes Chris and myself. So, Chris suggested that you make some code changes.

You either did, and you have some code to show us that proves that, or you didn't. Not really brain surgery to determine which it is.

PaulS:
Well, clearly that includes Chris and myself. So, Chris suggested that you make some code changes.

You either did, and you have some code to show us that proves that, or you didn’t. Not really brain surgery to determine which it is.

Here’s Chris’ suggestion implemented:

#include "FastLED.h"
    // the milliseconds to give each letter
#define MILLIS_PER_LETTER 1750
    // number of LEDs in the strip
#define NUM_LEDS 50
    // the data pin 
#define DATA_PIN 11
    // an array to keep track of the LEDs
CRGB leds[NUM_LEDS];
    // the message we will display
String message;
    // the time we received the message
unsigned long received;
    // we"ll use all 26 letters of the alphabet
#define NUM_LETTERS 26
    // the LED number (start counting from 0) that we light up to show our message
const int LETTER_LEDS[NUM_LETTERS] = {
     /*A*/ 7
    ,/*B*/ 8
    ,/*C*/ 9
    ,/*D*/ 10
    ,/*E*/ 11
    ,/*F*/ 12
    ,/*G*/ 13
    ,/*H*/ 14
    ,/*I*/ 32
    ,/*J*/ 31
    ,/*K*/ 30
    ,/*L*/ 29
    ,/*M*/ 28
    ,/*N*/ 26
    ,/*O*/ 25
    ,/*P*/ 24
    ,/*Q*/ 23
    ,/*R*/ 38
    ,/*S*/ 39
    ,/*T*/ 40
    ,/*U*/ 41
    ,/*V*/ 42
    ,/*W*/ 44
    ,/*X*/ 45
    ,/*Y*/ 46
    ,/*Z*/ 47
};
// how many colors to cycle through for the lights
#define NUM_COLORS 4


void setup() {
        // send print statements at 9600 baud
     Serial.begin(9600);
        // initialize the LEDS
     FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);
        // set them all to be off
     fill_solid(leds, NUM_LEDS, CRGB::Black);
     FastLED.show();
        // connect to wifi.
        // this message will show until it is overwritten from Firebase and shown if Firebase fails
     message = "welcome to specs";
     received = millis();
}


void loop() {

    while (1) {  // loop indefinitely

      // how many milliseconds have elapsed since the last message came in
    unsigned long elapsed = millis() - received;

        // assuming MILLIS_PER_LETTER, what letter (index) ofthe message should we be on?
     int index = elapsed/MILLIS_PER_LETTER;
        // if the letter we should technically be on is within the bounds of the message
     if(index < message.length()) {
            // get the character letter we should print
         char letter = message.charAt(index);
            // if the character is between 'a' and 'z" (no numbers, spaces, or punctuations)
         if(letter >= 'a' && letter <= 'z'){
                // how bright to make this LED from 0 to 1, this is what makes them fade in and out
                // it calculates what percent we are completed with the letter, and makes it fade in from 0-50% and fade out from 50-100%
                // the formula can be visualized here: https://www.desmos.com/calculator/5qk8imeny4
             float brightness = 1-abs((2*(elapsed%MILLIS_PER_LETTER)/((float)MILLIS_PER_LETTER))-1);
             uint8_t value = 255 * brightness;
             
                // get the LED number the letter should be in, assuming our array starts at 'a" and ends at 'z"
             int letter_index = letter-'a';
             int led = LETTER_LEDS[letter_index];
                // get a rotation of colors, so that every NUM_COLORS lights, it loops
                // e.g. red, yellow, green, blue, red, yellow green blue
             uint8_t hue = (letter_index%NUM_COLORS*255)/NUM_COLORS;
                // set that LED to the color
             leds[led] = CHSV(hue, 255, value);
             FastLED.show();
                // set it to black so we don't have to remember the last LED we turned on
             leds[led] = CRGB::Black;
             
             Serial.print(letter);
             Serial.print("\t!");
             Serial.print(led);
             Serial.print("\t=");
             Serial.print(brightness);
             Serial.print("\t@");
             Serial.print(elapsed);
             Serial.println();
         } else {
                // if the letter wasn't a-z then, we just turn off all the leds
             FastLED.show();
         }
     } else {
            // if the letter is beyond the bounds of the message, we just turn off all the leds
        FastLED.show();
     }
}

      delay(3000);  // pause 3 seconds between runs


  }

Since loop() already loops indefinitely, I can’t see that adding a while loop that way would help.

Here is the real issue:

     if(index < message.length()) {

What happens when index equals message.length()? The code stops doing whatever it does.

If you want it not to stop, you have to reset index to some value. Find the matching }, and add an else clause in which you set index to 0.

Finding the matching } would be a lot simpler if you put every { on a line BY ITSELF, and properly indented your code (Tools + Auto Format makes that so easy…).

Delete the while loop that Chris had you add.

Here is the core of the problem getting the code to loop

  received = millis();
}


void loop()
{
  // how many milliseconds have elapsed since the last message came in
  unsigned long elapsed = millis() - received;
  // assuming MILLIS_PER_LETTER, what letter (index) ofthe message should we be on?
  int index = elapsed / MILLIS_PER_LETTER;
  // if the letter we should technically be on is within the bounds of the message
  if (index < message.length())
  {

Once index exceeds the length of the message the code that outputs each letter is simply not executed. I do not have time to sort it out now but the code basically could do with rewriting to work more sensibly.

If you want it not to stop, you have to reset index to some value. Find the matching }, and add an else clause in which you set index to 0.

I don't think that will work due to the way that index is calculated based on the elapsed time that the program has been running.

get rid of the while(1) and just change the end of the code to read

else {
    FastLED.show();    // if the letter is beyond the bounds of the message, we just turn off all the leds
    delay(1000); // wait a bit
    received = millis(); // this way we start again from start as this will reset index
  }

just change the end of the code to read

Resetting the message received time, without resetting index, will only half solve the problem.

@cgsmith324, did you study Reply #4?

...R