Page 1 of 1
Forum

Welcome to the Tweaking4All community forums!
When participating, please keep the Forum Rules in mind!

Topics for particular software or systems: Start your topic link with the name of the application or system.
For example “MacOS X – Your question“, or “MS Word – Your Tip or Trick“.

Please note that switching to another language when reading a post will not bring you to the same post, in Dutch, as there is no translation for that post!



Marble-run on Fire
 
Share:
Notifications
Clear all

[Solved] Marble-run on Fire

12 Posts
2 Users
0 Reactions
2,131 Views
(@ledmeup)
Active Member
Joined: 4 years ago
Posts: 11
Topic starter  

Hans has done a remarkable work with the LED Effects he has posted (Thank you VERY much, Hans! GREAT work!)

The fire effect has kept me busy for quite a while now. As I´m into marble-runs, I had the funny idea to combine runs with all kind of electronic gizmos like led meteor showers and motors and...

When I came across the Fire-effect, I desperatelywanted to include it in the marble-run I´m working on.

(I have scratched the sketch from different sources and adapted a few things for my purpose.)

Here is the idea:

A marble runs through a lightbeam with an LDR.

My Arduino sketch recognises the break of the beam (Sketch works so far :o)

Then it activates the Fire function (Works, too :o)

But: Fire just runs once and then returns to the main code! :o(

But I need Fire to run for about 10 sec or a number of times until it returns to the main code and the LDR can be triggered again by another marble.

In a different post Hans recommended to use a variable.

for(int i=0; i<25; i++) {
Sparkle(0xff, 0xff, 0xff, 0, 1000); //Need to make 25 times
}

But that was for Sparkle and as my Grandma used to say: "A sparkle doesn´t make a fire!".

I have no idea, where to "int" a variable which counts up (or a timer) and keeps Fire running before returning to the main code.

This is my very first post here and if you have any suggestions to improve my appearence here, you are very welcome.

I would appreciate your help so much!

Here is my code:

/* LDR triggered LED Strip with WS2812B and LED effect "Fire" (by Hans)
 *  20.02.2021
 */

#include "FastLED.h"
#define NUM_LEDS 60 
CRGB leds[NUM_LEDS];
#define PIN 6 
const int analogPin = A0;    // pin that the sensor is attached to
const int ledPin1 = A2;       // pin that the green LED is attached to
const int ledPin2 = A1;       // pin that the yellow LED is attached to
const int threshold = 800;   // an arbitrary threshold level that's in the range of the analog input

void setup()
{
  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  
                              // initialize the LED pin as an output:
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
}


void loop() {
 // read the value of the ldr:
  int analogValue = analogRead(analogPin);

                              // if the analog value is high enough, turn on the LED2yellow:
  if (analogValue > threshold) {
    digitalWrite(ledPin2, HIGH);
  } else {
    digitalWrite(ledPin2, LOW);
  }

                              // if the analog value is low enough, turn on the LED1green:
if (analogValue < threshold) {
  digitalWrite(ledPin1, HIGH);
  Fire(55,120,15);  //          ruft die Funktion FIRE auf, wenn LDR unterbrochen
  
} else {
  digitalWrite(ledPin1, LOW);
}                                         //  könnte ich hier die Funktion für FIRE aufrufen?

  delay(1);        // delay in between reads for stability
}



void Fire(int Cooling, int Sparking, int SpeedDelay) {
  static byte heat[NUM_LEDS];
  int cooldown;
  {
  // Step 1.  Cool down every cell a little
  for( int i = 0; i < NUM_LEDS; i++) {
    cooldown = random(0, ((Cooling * 10) / NUM_LEDS) + 2);
    
    if(cooldown>heat[i]) {
      heat[i]=0;
    } else {
      heat[i]=heat[i]-cooldown;
    }
  }
  
  // Step 2.  Heat from each cell drifts 'up' and diffuses a little
  for( int k= NUM_LEDS - 1; k >= 2; k--) {
    heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
  }
    
  // Step 3.  Randomly ignite new 'sparks' near the bottom
  if( random(255) < Sparking ) {
    int y = random(7);
    heat[y] = heat[y] + random(160,255);
    //heat[y] = random(160,255);
  }

  // Step 4.  Convert heat to LED colors
  for( int j = 0; j < NUM_LEDS; j++) {
    setPixelHeatColor(j, heat[j] );
  }

  showStrip();
  delay(SpeedDelay);
}
}
void setPixelHeatColor (int Pixel, byte temperature) {
  // Scale 'heat' down from 0-255 to 0-191
  byte t192 = round((temperature/255.0)*191);
 
  // calculate ramp up from
  byte heatramp = t192 & 0x3F; // 0..63
  heatramp <<= 2; // scale up to 0..252
 
  // figure out which third of the spectrum we're in:
  if( t192 > 0x80) {                     // hottest
    setPixel(Pixel, 255, 255, heatramp);
  } else if( t192 > 0x40 ) {             // middle
    setPixel(Pixel, 255, heatramp, 0);
  } else {                               // coolest
    setPixel(Pixel, heatramp, 0, 0);
  }
}

void showStrip() {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.show();
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   FastLED.show();
 #endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H 
   // FastLED
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;
 #endif
}

void setAll(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

Thomas


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2796
 

Interesting "problem" 😊 

I'd probably create a new function, which keeps running the Fire effect for a certain minimal number of seconds.
Something like:

void FireTimed(int Cooling, int Sparking, int SpeedDelay, int Seconds) {
  unsigned long startTime = millis();
  unsigned long runMillis = 1000 * Seconds;
  
  while(millis()-startTime < runMillis) {
    Fire(Cooling, Sparking, SpeedDelay);
  }
}

 

In short;
- Register the time we start this function in startTime.
- Calculate milliseconds from seconds in runMillis.
- While the current time - start time < desired run time we'd like: execute the Fire effect.

So, in your void loop() code, replace "Fire(x,y,z)" with "FireTime(x,y,z,10)" (10 being 10 seconds).

Note: I have not tested this code, but I'm pretty confident this works as expected (if I didn't make any typos).

Hope this helps 😋 


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2796
 

Woops, forgot the triggering by other following marbles.

Since the Arduino is not made to multitask, we'd need to check the LDR status in the while loop as well and act accordingly.
Let's first test this new function and then look at how to work with the LDR's.

Since you're using FastLED, we can cleanup your code a little as well - which includes the new function, but is not yet considering following marbles;

/* LDR triggered LED Strip with WS2812B and LED effect "Fire" (by Hans)
 *  20.02.2021
 */

#define FASTLED_INTERNAL // just used to mute the Pragma messages when compiling
#include "FastLED.h"

#define NUM_LEDS 60
CRGB leds[NUM_LEDS];
#define PIN 6
const int analogPin = A0; // pin that the sensor is attached to
const int ledPin1 = A2; // pin that the green LED is attached to
const int ledPin2 = A1; // pin that the yellow LED is attached to
const int threshold = 800; // an arbitrary threshold level that's in the range of the analog input

void setup() {
  FastLED.addLeds < WS2811, PIN, GRB > (leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

  // initialize the LED pin as an output:
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
}

void loop() {
  // read the value of the ldr:
  int analogValue = analogRead(analogPin);

  // if the analog value is high enough, turn on the LED2yellow:
  if (analogValue > threshold) {
    digitalWrite(ledPin2, HIGH);
  } else {
    digitalWrite(ledPin2, LOW);
  }

  // if the analog value is low enough, turn on the LED1green:
  if (analogValue < threshold) {
    digitalWrite(ledPin1, HIGH);
    FireTimed(55, 120, 15, 10); //          ruft die Funktion FIRE auf, wenn LDR unterbrochen

  } else {
    digitalWrite(ledPin1, LOW);
  } //  könnte ich hier die Funktion für FIRE aufrufen?

  delay(1); // delay in between reads for stability
}

void FireTimed(int Cooling, int Sparking, int SpeedDelay, int Seconds) {
  unsigned long startTime = millis();
  unsigned long runMillis = 1000 * Seconds;

  while (millis() - startTime < runMillis) {
    Fire(Cooling, Sparking, SpeedDelay);
  }
}

void Fire(int Cooling, int Sparking, int SpeedDelay) {
  static byte heat[NUM_LEDS];
  int cooldown; {
    // Step 1.  Cool down every cell a little
    for (int i = 0; i < NUM_LEDS; i++) {
      cooldown = random(0, ((Cooling * 10) / NUM_LEDS) + 2);

      if (cooldown > heat[i]) {
        heat[i] = 0;
      } else {
        heat[i] = heat[i] - cooldown;
      }
    }

    // Step 2.  Heat from each cell drifts 'up' and diffuses a little
    for (int k = NUM_LEDS - 1; k >= 2; k--) {
      heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
    }

    // Step 3.  Randomly ignite new 'sparks' near the bottom
    if (random(255) < Sparking) {
      int y = random(7);
      heat[y] = heat[y] + random(160, 255);
      //heat[y] = random(160,255);
    }

    // Step 4.  Convert heat to LED colors
    for (int j = 0; j < NUM_LEDS; j++) {
      setPixelHeatColor(j, heat[j]);
    }

    FastLED.show();
    delay(SpeedDelay);
  }
}

void setPixelHeatColor(int Pixel, byte temperature) {
  // Scale 'heat' down from 0-255 to 0-191
  byte t192 = round((temperature / 255.0) * 191);

  // calculate ramp up from
  byte heatramp = t192 & 0x3F; // 0..63
  heatramp <<= 2; // scale up to 0..252

  // figure out which third of the spectrum we're in:
  if (t192 > 0x80) { // hottest
    leds[Pixel] = CRGB(255, 255, heatramp);
  } else if (t192 > 0x40) { // middle
    leds[Pixel] = CRGB(255, heatramp, 0);
  } else { // coolest
    leds[Pixel] = CRGB(heatramp, 0, 0);
  }
}

   
ReplyQuote
(@ledmeup)
Active Member
Joined: 4 years ago
Posts: 11
Topic starter  

Hi Hans!

You are the man!!! I wasn´t expecting an answer that soon. Thank you VERY much!

I just checked the sketch out:

It compiles without errors and runs perfectly after triggering the LDR.

The LDR can be triggered again after the 10 secs and it starts fine again. I´m very impressed!

There is just one small issue:

After the 10secs the Strip gets kind of stuck with some LEDs emitting white at the bottom and a few reds somewhere in the strip.

Maybe it´s neccessary to put all LEDs to low (or black) when the time has elapsed?

Your grateful

Thomas

Thomas


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2796
 

Hi Thomas!

You're very welcome 😊 

To make the LEDs black after the 10 seconds, you can use

fill_solid( leds, NUM_LEDS, CRGB(0,0,0));

Like so:

void FireTimed(int Cooling, int Sparking, int SpeedDelay, int Seconds) {
  unsigned long startTime = millis();
  unsigned long runMillis = 1000 * Seconds;

  while (millis() - startTime < runMillis) {
    Fire(Cooling, Sparking, SpeedDelay);
  }
  
  fill_solid( leds, NUM_LEDS, CRGB(0,0,0));
}

 

This will set the entire strip to black (off).

Hope this fixes that little residue 😊 


   
ReplyQuote
(@ledmeup)
Active Member
Joined: 4 years ago
Posts: 11
Topic starter  

@hans

Dear Hans!

Again: Thanks for your help!

I have put the codeline in the void FireTimed... exactly at the place you suggested. But the strip still gets stuck.

I double checked if I did it like you suggested.

 

Thomas


   
ReplyQuote
(@ledmeup)
Active Member
Joined: 4 years ago
Posts: 11
Topic starter  

Hi Hans!

There's a big HEUREKA!!!

This line was just missing:

FastLED.show();

Now the sketch runs perfectly and I'm so glad!

Thanks to your help. Without you I would never have made it.

 

Thomas


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2796
 

Awesome!!!! 👍 😎 

Glad to hear it works as you wanted to! Nice!

And ... you're most welcome! Being able to help someone like this just makes my day 😊 


   
ReplyQuote
(@ledmeup)
Active Member
Joined: 4 years ago
Posts: 11
Topic starter  

Now my story is continued:

As the FIRE effect runs so pretty well, I wanted to use your recommendation about the time function with the SPARKLE effect.

So the LDR is triggered, the Strip sparkles for 10 seconds, the strip shuts off and can be triggered again.

Unfortunately I'm not able to get the code running. The compiler always gives the error message:

"too few arguments to function 'void SparkleTimed(byte, byte, byte, int, int)"

I tried the alternatives, which are still in the code but it didn't work.

Even after reading your tutorial about functions (Which, by the way, is superb!!!) I'm still clueless! 😣 

Your assistance would be appreciated a lot.

Here is my code:

/* LDR triggered LED Strip with WS2812B and LED effect "SPARKLE" (by Hans)
 *  1.03.2021
 */

#define FASTLED_INTERNAL // just used to mute the Pragma messages when compiling
#include "FastLED.h"

#define NUM_LEDS 99
CRGB leds[NUM_LEDS];
#define PIN 6
const int analogPin = A0; // pin that the sensor is attached to
const int ledPin1 = A2; // pin that the green LED is attached to
const int ledPin2 = A1; // pin that the yellow LED is attached to
const int threshold = 800; // an arbitrary threshold level that's in the range of the analog input

void setup()
{
  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  // initialize the LED pin as an output:
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
}

//  REPLACE FROM HERE 

void loop() {
  // read the value of the ldr:
  int analogValue = analogRead(analogPin);

  // if the analog value is high enough, turn on the LED2(yellow):
  if (analogValue > threshold) {
    digitalWrite(ledPin2, HIGH);
  } else {
    digitalWrite(ledPin2, LOW);
  }

  // if the analog value is low enough, turn on the LED1green:
  if (analogValue < threshold) {
    digitalWrite(ledPin1, HIGH);
    SparkleTimed(0xff, 0xff, 0xff, 0);
//     SparkleTimed();
//    SparkleTimed(byte red, byte green, byte blue, int SpeedDelay, int Seconds) //          runs SPARKLE if LDR is triggered
//  SparkleTimed(0xff, 0xff, 0xff, 0, byte red, byte green, byte blue, int SpeedDelay, int Seconds);

  } else {
    digitalWrite(ledPin1, LOW);
  } 

  delay(1); // delay in between reads for stability
}

void SparkleTimed(byte red, byte green, byte blue, int SpeedDelay, int Seconds) {
  unsigned long startTime = millis();
  unsigned long runMillis = 1000 * Seconds;

  while (millis() - startTime < runMillis) {
    Sparkle(red, green, blue, SpeedDelay);
  }
  fill_solid( leds, NUM_LEDS, CRGB(0,0,0));
  // fill_solid( leds, NUM_LEDS, CRGB::Black);
      FastLED.show();
}

void Sparkle(byte red, byte green, byte blue, int SpeedDelay) {
  int Pixel = random(NUM_LEDS);
  setPixel(Pixel,red,green,blue);
  showStrip();
  delay(SpeedDelay);
  setPixel(Pixel,0,0,0);
}
//  REPLACE TO HERE 

void showStrip() {
 #ifdef ADAFRUIT_NEOPIXEL_H
   // NeoPixel
   strip.show();
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   FastLED.show();
 #endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
 #ifdef ADAFRUIT_NEOPIXEL_H
   // NeoPixel
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;
 #endif
}

void setAll(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue);
  }
  showStrip();
}

 

Thomas


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2796
 

Hi Thomas,

No problem! 

Calling Sparkle vs SparkleTimes requires an different number of parameters.

Sparkle(byte red, byte green, byte blue, int SpeedDelay);

SparkleTimed(byte red, byte green, byte blue, int SpeedDelay, int Seconds);

 

So when calling Sparkle, we need to pass 4 numbers: red, green, blue and the delay in milliseconds.

When calling SparkleTime, we need to pass 5 numbers: red, green, blue, delay in milliseconds, and seconds (how long the effect should run).

So in your code:

  if (analogValue < threshold) {
    digitalWrite(ledPin1, HIGH);
    SparkleTimed(0xff, 0xff, 0xff, 0, 5); // added "5" to make the sparkles run 5 seconds
  } else {
    digitalWrite(ledPin1, LOW);
  } 

 

This is a good experience though, since you'll get more familiar with possible error messages.
It basically says the same thing:

too few arguments to function 'void SparkleTimed(byte, byte, byte, int, int)'

Or in other words: you're missing at least one value, in this case that last "int" number.

Hope this helps 😊 

 


   
ReplyQuote
(@ledmeup)
Active Member
Joined: 4 years ago
Posts: 11
Topic starter  

@hans

Dear Hans!

I'm so grateful! The code compiles without error. I'll check it out with the strip after lunch.

Thank you very much for the lightspeed response. Amazing! Thanks a lot.

Learning to program an Arduino and finding out things is fun, although my bookmark folder looks like the catalogue of the national library meanwhile 😉 

Have a nice weekend,

Thomas


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2796
 

Awesome! Well, always feel free to ask questions here.
We all were beginners at some point.

I have bookmark folders like that as well hahah.
To later find out that I haven't even touched them years later 😉 


   
ReplyQuote
Share: