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!



FastLED Light effec...
 
Share:
Notifications
Clear all

[Solved] FastLED Light effects for Power Rangers

67 Posts
2 Users
1 Likes
1,934 Views
(@trace)
Estimable Member
Joined: 4 years ago
Posts: 170
Topic starter  

@hans Hello there. I hope you had a good start for this week.

I think I have managed to get my previous problem working. But I got another one....of course.

The problem is, I now want to have a rainbow wave toggle. That means, clicking a button makes the rainbow start, clicking again makes the rainbow go black.

This is my attempt:

boolean isRainbow = false;


void loop() {
  rainbow_wave(50, 255 / NUM_LEDS);  //speed, hue-position
}

void rainbow_wave(uint8_t thisSpeed, uint8_t deltaHue) {

  int button4 = digitalRead(Morph_Button) == LOW;

  if (button4) {
    if (isRainbow) {
      fill_solid(leds, NUM_LEDS, CRGB::Black);
      FastLED.show();
      isRainbow = false;
    } else {
      isRainbow = true;
      uint8_t thisHue = beat8(thisSpeed, 255);
      fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
      FastLED.show();
    }
  }
}

The toggle works (debouncing comes later). But the rainbow does not move. It only moves, when I hold the button, which I dont want. I want a toggle on = rainbow moves, toggle off = black. Any ideas?

 


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

I'll try to go through your questions as much as I can 😊 
It's a rough week here as we have to be at the hospital every day with my mom.

Posted by: @trace
For the sparkle: I have found a neat lil code to avoid delay times and I just added the coin color part:

void MorphAnimation( fract8 chanceOfGlitter) {
   int button4 = digitalRead(Morph_Button) == LOW;

if (button4) {
  fill_solid(leds, NUM_LEDS, coinColor);
    if( random8() < chanceOfGlitter) {
     leds[ random16(NUM_LEDS) ] += CRGB::White;}
    FastLED.show();
}
    else {
    fill_solid(leds, NUM_LEDS, CRGB::Black);
    FastLED.show();
}
}

What do you think? It works and looks good. But I cant figure out, how to set brightness for individual effects. In this case, having the coin color at maybe 100 and the sparkle color at 255. I know I cant use FastLED.setBrightness at all, cause its a global thing. I have also tried CRGB(100,0,0) for the red coin color and CRGB(255,255,255) instead of white. But it does not work. I also have done this with CHSV, which also did not work. 😶 

Try this: where it says "+= CRGB::White" means: grab the current color and add "white" to it. White is basically where red = green = blue = 255. So max brightness. Maybe you could try replacing that with "+= CRGB (50,50,50)". Something like this:

void MorphAnimation( fract8 chanceOfGlitter) {
  bool button4 = digitalRead(Morph_Button) == LOW;

  if (button4) {
    fill_solid(leds, NUM_LEDS, coinColor);
    
    if( random8() < chanceOfGlitter) {
      // leds[ random16(NUM_LEDS) ] += CRGB::White;
      leds[ random16(NUM_LEDS) ] += CRGB(50,50,50);
    }
      
    FastLED.show();
  }
  else {
    fill_solid(leds, NUM_LEDS, CRGB::Black);
    FastLED.show();
  }
}

 

ps: as you can see, I changed the indentation a little and made button4 a "bool" (boolean). You can change the "50" value to anything you want. 😊 


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

Posted by: @trace

For the coin delay part:
The compiler says "coinColor was not declared in this scope". And I think I have to read a lot more about basics, cause I know what that means, but I dont know where and how things should be declared (a missing } or ; is not a problem).

This usually refers to either a typo in the name (eg. CoinColor is not the same as coinColor) - happens to me all the time.
Or it is a scope issue, for example you defined the variable in "void setup" and are trying to use it in "void loop". This is why some variables are placed all the way in the top - so they are known everywhere (called: global scope).
Of course the last reason is: you really did not define the variable.

So in your code you will see that coinColor is not defined. And neither is prevcoinColor (!).

#include "FastLED.h"

unsigned long previousMillis = 0;
const long interval = 1000;

CRGB coinColor = CRGB::Black; // these 2 were missing
CRGB prevcoinColor = CRGB::Black; // these 2 were missing

//the rest of setup and loop//

void coinColorSelection() {

  int button1 = digitalRead(Coin_Button_1) == LOW;
  int button2 = digitalRead(Coin_Button_2) == LOW;
  int button3 = digitalRead(Coin_Button_3) == LOW;

  prevcoinColor = coinColor; // assign old color before determining new color

unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;

//rest of this void//

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

Posted by: @trace
I have already I figured out to set two bool values (isPlayingMorph and isPlayingTheme = false). Otherwise it made some problems.

While getting it to work, to have a mp3 file playing only as long as the button is pressed:

...

Do I understand this correctly? You want to push the button to start MP3 playback and push again to stop playback?
What is happening right now with this code? (I have not yet played with myDFPlayer even though I have the module ready to test from the Star Trek project 😜 ).


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

Posted by: @trace

The problem is, I now want to have a rainbow wave toggle. That means, clicking a button makes the rainbow start, clicking again makes the rainbow go black.

I'm not sure what "fill_rainbow" does - does it require to be called over and over again to make it move?
Without having tested or focused too much, you could try this:

void rainbow_wave(uint8_t thisSpeed, uint8_t deltaHue) {

  int button4 = digitalRead(Morph_Button) == LOW;

  if (button4) {
    if (isRainbow) {
      fill_solid(leds, NUM_LEDS, CRGB::Black);
      FastLED.show();
      isRainbow = false;
    } else {
      isRainbow = true;
      uint8_t thisHue = beat8(thisSpeed, 255);
      fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
      FastLED.show();
    }
  } else {
    if(isRainbow) {
      fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
      FastLED.show(); 
    }
  }
}

   
ReplyQuote
(@trace)
Estimable Member
Joined: 4 years ago
Posts: 170
Topic starter  

@hans Hi Hans, first of all, if you dont have the time or mind to work on this right now,you dont have to. It is just for a toy and family is much more important than anything else. I hope it is nothing to much of concern with your mom at the hospital?!

As for the sparkle effect and the brightness. Yes, I have already tried using CRGB values to control the brightness of the glitter. Finally I have done it with CHSV, cause it is easier to change the color and brightness independently.

void MorphAnimation(fract8 chanceOfGlitter) {
  bool button4 = digitalRead(Morph_Button) == LOW;

  if (button4) {
    fill_solid(leds, NUM_LEDS, coinColor);
    if (random8() < chanceOfGlitter) {
      leds[random16(NUM_LEDS)] += CHSV(0, 0, 255);
    }
    FastLED.show();
  } else {
    fill_solid(leds, NUM_LEDS, CRGB::Black);
    FastLED.show();
  }
}

I have also replaced int with bool, like you have said. But....can you tell me why? Is it because of memory and we only need 1 and 0 here? So I could replace it everywhere?

The compiler error: Yes, it was just a typo. But I have also found out, that the compiler makes some errors like "there is no such directory", when copy and paste code to an unsaved new sketch. It took a while to find that out.

My solution to wait for the coin buttons to get pressed, to prevent actions for not simultanously pressed coin buttons, does not work that well:

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    // Check for button combinations and assign the corresponding colors to coinColor
    if (button1 && button2) {
      coinColor = CRGB(20, 0, 0);  //Tyranosaurus - red
      if (prevcoinColor != coinColor) {
        myDFPlayer.playMp3Folder(1);
      }

The problem is, sometimes it works fine, sometimes it does not "wait" for the button presses. I think there is a better solution for this, right? My goal was to have a lil delay before the code recognize the button presses, in case one button gets pressed just a few millis after the other. Using delay at this point influences the sparkle animation (dont know why).

Also this would eliminate another problem: When releasing the coin buttons, without any sort of "delay" the code also recognizes when one button is already released, while the other is still pressed and then plays the animation. Even the releasing happens in a few milliseconds.

For the mp3: Thats right. I want the mp3 to playback when I click the button and stop when I click the button again. And I got it working already (still without debouncing):

void PlayThemeSong() {

  int button5 = digitalRead(Activate_Button) == LOW;

  if (button5) {
    if (isPlayingTheme) {
      myDFPlayer.stop();
      delay(50);
      isPlayingTheme = false;
    } else {
      isPlayingTheme = true;
      myDFPlayer.playMp3Folder(10);
      delay(50);
    }
  }
}

 

Lets talk about the rainbow effect. I thought you could know this function, cause it is part of the fastLED library. I dont know if it needs to be called over and over again to get it moving, but your solution works (how can you not knowing something, but still figure out a solution? Thats crazy man 🤣 )
Can you explain, why your added "else" part works, but mine without the second else did not? Programming logic is still not my friend.

As you can see, I have also deleted the variables and wrote it directly into the function. That was necessary for a later idea (see below).

void rainbow_Test() {

  int button9 = digitalRead(Rainbow_Button) == LOW;

  if (button9) {
    if (isRainbow) {
      fill_solid(leds, NUM_LEDS, CRGB::Black);
      FastLED.show();
      isRainbow = false;
    } else {
      isRainbow = true;
      uint8_t thisHue = beat8(50, 255);
      uint8_t deltaHue = 255/NUM_LEDS;
      fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
      FastLED.show();
    }
  } else {
    if(isRainbow) {
      uint8_t thisHue = beat8(50, 255);
      uint8_t deltaHue = 255/NUM_LEDS;
      fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
      FastLED.show(); 
    }
  }
}

And now comes the most tricky part (I guess): What if I tell you, that I would like to have this rainbow effect executed after the button was held for 2 seconds? To get it right: I hold the button for 2 seconds, the rainbow starts and works as intended, I release the button and when I press the button again, the rainbow stops.

I have tried the OneButton Library. And while it works for other parts (like the mp3 on/off toggle), it did not for the rainbow (thats why I had to delete the variable, cause the button library uses voids without variables). And while your rainbow code works fine, I would not know how to "delete" the button part, to get it handled by the button library.

To have you not jumping up and down to read everything, here is the summary:

brightness for sparkle - works

mp3 toggle on/off  - works

Rainbow effect toggle on/off - works

CoinButton recognition delay for not exact simultanously pressed or released CoinButtons - not working (my millis attempt, seems to be faulty)

Button click/hold/release function - needs to be worked on (any ideas about a button library?)

 

 

 


   
ReplyQuote
(@trace)
Estimable Member
Joined: 4 years ago
Posts: 170
Topic starter  

@hans Addition: It seems "fill_rainbow" needs to be in void loop for working and cannot be integrated in a OneButton Library function. Because the OneButton Library function uses its own voids. But then rainbow_wave is inside this OneButton void and not as a separate void inside the loop. I hope that makes sense.

So, using another Button Library or writing hold/release functions by ourself?

Besides that, the only not functioning thing is about the Coin_Button press delay.


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

Posted by: @trace
Hi Hans, first of all, if you dont have the time or mind to work on this right now,you dont have to. It is just for a toy and family is much more important than anything else. I hope it is nothing to much of concern with your mom at the hospital?!

It is rather serious. The found cancer. So yeah, not great 😞 
However, whenever I have some time I'll always try to answer 😊 
No worries!

Posted by: @trace
I have also replaced int with bool, like you have said. But....can you tell me why? Is it because of memory and we only need 1 and 0 here? So I could replace it everywhere?

Mostly because of my OCD hahaha ... a boolean is the proper data type to be used for a yes/no or true/false kind of value.
Ideally this would be stored in a "bit" but a "bit" is too small, the "bool", as defined for the Arduino, is stored in byte. Your "int" however needs 2 bytes to be stored. In the grand scheme of this project, memory use wouldn't be my main concern. 
I'd only be concern with using the proper data type 😊 

Posted by: @trace
The problem is, sometimes it works fine, sometimes it does not "wait" for the button presses. I think there is a better solution for this, right?

Yeah, this would be the challenge for sure since we're running our code in sequence in a single task on the Arduino. So when the button isn't pressed at the right time, things may not work as hoped for (delayed, or even missed).

Posted by: @trace
Can you explain, why your added "else" part works, but mine without the second else did not?

So the deal here is that the fill_rainbow function needs to be called repeatedly to keep it going. If you stop calling it, the rainbow will stop moving.

In your initial code, you check the button and if pressed you do your thing (start rainbow etc).

However ... when the button is not pressed ("else"), then you'd want to keep the rainbow going and for that you'd need to call the fill_rainbow function again.

In speudo code:

function rainbow_Test()
{  
  if button was pressed then
   {
      if rainbow is running
        set all LEDs to black
        set rainbowrunning to false
      else
        run fill_rainbow for the first time
        set rainbowrunning to true
      end
  }
  else if button is not being pressed
  {
     if rainbow is running then
       execute fill_rainbow again
  }    
}

I hope that makes sense 😁 

Posted by: @trace
And now comes the most tricky part (I guess): What if I tell you, that I would like to have this rainbow effect executed after the button was held for 2 seconds? To get it right: I hold the button for 2 seconds, the rainbow starts and works as intended, I release the button and when I press the button again, the rainbow stops.

Now thing become more complicated.

See; while everything else is running and being monitored, you'd also want to "count" to see if the button was held for 2 seconds.

A solution could be a global variable for timing this. Let's call it something like "LastButtonTime" or something.

The idea:
  1) if the button gets pressed we check if the recorded start time (millis-LastButtonTime) is >= 2 seconds and if "LastButtonTime" is not equal to zero. If that is the case we set "rainbow" to TRUE.
  2) If the button is pressed and the LastButtonTime=0 or Rainbow=false then we set LastButtonTime=millis. 
As soon as we see that the button is NOT pressed, AND rainbow is not set to TRUE, then we need to reset this global variable to zero.

In the meanwhile, in between, we check the status of the "rainbow" boolean.
If it is set to true: execute fill_rainbow.
If it is set to false: execute fill_solid to set the strip to Black.

Make sense? 😊 


   
ReplyQuote
(@trace)
Estimable Member
Joined: 4 years ago
Posts: 170
Topic starter  

@hans Hi Hans. Thank you for your honest words. I wish you your mom and your family much strength and all hope there is.

Replacing int with bool makes sense and to use proper data type, no matter if it is relevant or not, is a good behaviour. I will change the other int´s later.

Yeah, this would be the challenge for sure since we're running our code in sequence in a single task on the Arduino. So when the button isn't pressed at the right time, things may not work as hoped for (delayed, or even missed).

So any ideas how to have the CoinSelection function wait a moment for button presses before getting executed? I did not try to place the millis function in a different place for this task. Maybe that works better.

Your pseudocode explanation is great. This makes it much easier to understand the logic behind it.

I have possibly found a solution for button hold, button click, button release "function". Take a look at this:

//↓↓=============== Long Press Button TEST ===============↓↓//

const int Activate_Button = 8;
int Activate_ButtonStatePrevious = HIGH;  // previousstate of the switch

unsigned long minActivate_ButtonLongPressDuration = 3000;  // Time we wait before we see the press as a long press
unsigned long Activate_ButtonLongPressMillis;              // Time in ms when we the button was pressed
bool Activate_ButtonStateLongPress = false;                // True if it is a long press

const int intervalActivate_Button = 50;       // Time between two readings of the button state
unsigned long previousActivate_ButtonMillis;  // Timestamp of the latest reading

unsigned long Activate_ButtonPressDuration;  // Time the button is pressed in ms

unsigned long currentMillis;
//↑↑=============== Long Press Button TEST ===============↑↑//




//↓↓=============== Rainbow LongPress Test ===============↓↓//

void RainbowLongPress() {

  // If the difference in time between the previous reading is larger than intervalButton
  if (currentMillis - previousActivate_ButtonMillis > intervalActivate_Button) {

    // Read the digital value of the button (LOW/HIGH)
    int Activate_ButtonState = digitalRead(Activate_Button);

    // If the button has been pushed AND
    // If the button wasn't pressed before AND
    // IF there was not already a measurement running to determine how long the button has been pressed
    if (Activate_ButtonState == LOW && Activate_ButtonStatePrevious == HIGH && !Activate_ButtonStateLongPress) {
      Activate_ButtonLongPressMillis = currentMillis;
      Activate_ButtonStatePrevious = LOW;
      Serial.println("Button pressed");
    }

    // Calculate how long the button has been pressed
    Activate_ButtonPressDuration = currentMillis - Activate_ButtonLongPressMillis;

    // If the button is pressed AND
    // If there is no measurement running to determine how long the button is pressed AND
    // If the time the button has been pressed is larger or equal to the time needed for a long press
    if (Activate_ButtonState == LOW && !Activate_ButtonStateLongPress && Activate_ButtonPressDuration >= minActivate_ButtonLongPressDuration) {
      Activate_ButtonStateLongPress = true;
      Serial.println("Button long pressed");
      if (isRainbow) {
        fill_solid(leds, NUM_LEDS, CRGB::Black);
        FastLED.show();
        isRainbow = false;
      } else {
        isRainbow = true;
        uint8_t thisHue = beat8(50, 255);
        uint8_t deltaHue = 255 / NUM_LEDS;
        fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
        FastLED.show();
      }
    } else {
      if (isRainbow) {
        uint8_t thisHue = beat8(50, 255);
        uint8_t deltaHue = 255 / NUM_LEDS;
        fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
        FastLED.show();
      }
    }

    // If the button is released AND
    // If the button was pressed before
    if (Activate_ButtonState == HIGH && Activate_ButtonStatePrevious == LOW) {
      Activate_ButtonStatePrevious = HIGH;
      Activate_ButtonStateLongPress = false;
      Serial.println("Button released");
    }

    previousActivate_ButtonMillis = currentMillis;
  }
}
//↑↑=============== Rainbow LongPress Test ===============↑↑//

It took me 5 hours straight finding this. And it did not work. Until I realized that it is not made for Input_Pullup, so I switched LOW for HIGH and it works great.

But....there is a big but....As you can see, I was able to implement the Rainbow animation. When I activate it, the rainbow runs nicely but flickers alot. I found out, it is because of this void:

//↓↓=============== Morphing LED Animation ===============↓↓//

void MorphAnimation(fract8 chanceOfGlitter) {
  int button4 = digitalRead(Morph_Button) == LOW;

  if (button4) {
    fill_solid(leds, NUM_LEDS, coinColor);
    if (random8() < chanceOfGlitter) {
      leds[random16(NUM_LEDS)] += CHSV(0, 0, 255);
    }
    FastLED.show();
  } else {
    fill_solid(leds, NUM_LEDS, CRGB::Black);
    FastLED.show();
  }
}
//↑↑=============== Morphing LED Animation ===============↑↑//

Because of the fill_solid - Black part, the code runs into it, while executing the rainbow and therefor flickers. But the fill_solid part is needed. So I have tried to change the rainbow function. I have found this:

for (int j = 0; j < 255; j++) {
    for (int i = 0; i < NUM_LEDS; i++) {
      leds[i] = CHSV(i - (j * 2), SATURATION, BRIGHTNESS);  
    }
    FastLED.show();
    delay(25);

And it works. BUT....I dont know why, it runs through the colors and stops. It does not play continously.

So have you any ideas what we can do? Either changing something so that the code does not run through fill_solid in the Void MorphAnimation while it is playing the rainbow, or figuring out why this rainbow animation just stops?!


   
ReplyQuote
(@trace)
Estimable Member
Joined: 4 years ago
Posts: 170
Topic starter  

@hans And another rainbow animation would be great, because fill_rainbow does not support brightness. And setting a brightness inside this void by using setBrightness() does not work because it then effects other voids and I cant set a brightness for every void (due to interlocking effects).


   
ReplyQuote
(@trace)
Estimable Member
Joined: 4 years ago
Posts: 170
Topic starter  

@hans I have found a strange behaviour with the rainbow function:

When having the Morph Animation (sparkle with coin color background) and the Rainbow Animation alone and without the hold button function, it works fine.

Having the Rainbow Animation with the hold button function alone, it works too.

Then adding the Morph Animation to it, it starts to flicker.

Adding the hold button function, needs currentMillis=millis inside its code (or inside loop). So this seems to trigger the flicker effect....BUT ONLY when Morph animation is present. What?!

So there must be some kind of interference between those two animations and the fact the hold button function needs currentMillis=millis.

Even using your snow sparkle effect triggers the flicker (it also may have something to do with fill_solid - black, as i mentioned before).

Summary:

Morph + Rainbow = works

Rainbow + hold button = works

Morph + Rainbow + hold button = flicker


   
ReplyQuote
(@trace)
Estimable Member
Joined: 4 years ago
Posts: 170
Topic starter  

@hans And to make it more confusing:

For the Rainbow part where we say to fill it black due to the toggle function, we don´t need this part with the button hold function. So maybe changing something here makes a different?

      if (isRainbow) {
        fill_solid(leds, NUM_LEDS, CRGB::Black);        //not needed when using hold button function
        FastLED.show();                                                //
        isRainbow = false;  
      } else {
        isRainbow = true;
        uint8_t thisHue = beat8(50, 255);
        uint8_t deltaHue = 255 / NUM_LEDS;
        fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
        FastLED.show();
      }
    } else {
      if (isRainbow) {
        uint8_t thisHue = beat8(50, 255);
        uint8_t deltaHue = 255 / NUM_LEDS;
        fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
        FastLED.show();
      }
    }

   
ReplyQuote
(@trace)
Estimable Member
Joined: 4 years ago
Posts: 170
Topic starter  

@hans Hi Hans, sorry for "spamming"! You got more importent things on your mind. And I dont want to take more time from you than "needed". So thats why I try to figure stuff out by myself, but run into problems very often due to my basic programming skills.

But I got some great (unimportant) news.

I found a way to make things working, with just some minor flaws. Here is the full working code so far (what do you think?):

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

#define PIN 6
#define NUM_LEDS 26

CRGB leds[NUM_LEDS];

#define Coin_Button_1 3    // Coin button 1
#define Coin_Button_2 4    // Coin button 2
#define Coin_Button_3 5    // Coin button 3
#define Morph_Button 7     // Morph button
#define Activate_Button 8  // Activate button
//#define Test_Button 9

CRGB prevcoinColor = CRGB::Black;
CRGB coinColor = CRGB::Black;

boolean isPlayingMorph = false;
boolean isPlayingTheme = false;
boolean isRainbow = false;

//↓↓=============== Button Selection Delay (not working correctly) ===============↓↓//
unsigned long previousMillis = 0;
const long interval = 1000;
//↑↑=============== Button Selection Delay (not working correctly) ===============↑↑//


//↓↓=============== Theme_Short_Rainbow_Long ===============↓↓//
long buttonTimer = 0;
long longPressTime = 3000;

boolean buttonActive = false;
boolean longPressActive = false;
//↑↑=============== Theme_Short_Rainbow_Long ===============↑↑//


//↓↓=============== DFPlayer mini initiation ===============↓↓//
#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

SoftwareSerial mySoftwareSerial(10, 11);  // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
//↑↑=============== DFPlayer mini initiation ===============↑↑//


void setup() {
  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  //FastLED.setBrightness(30);   //set global brightness -> is off due to brightness settings for each void
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();

  pinMode(Coin_Button_1, INPUT_PULLUP);     //button for Coin recognition
  pinMode(Coin_Button_2, INPUT_PULLUP);     //button for Coin recognition
  pinMode(Coin_Button_3, INPUT_PULLUP);     //button for Coin recognition
  pinMode(Morph_Button, INPUT_PULLUP);      //button for when Morpher is opened 
  pinMode(Activate_Button, INPUT_PULLUP);   //button for playing Theme Song (click) and rainbow animation (hold)
  //pinMode(Test_Button, INPUT_PULLUP);

  //↓↓=============== DFPlayer mini setup ===============↓↓/
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);
  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    while (true)
      ;
  }
  myDFPlayer.setTimeOut(500);  //Set serial communictaion time out 500ms
  myDFPlayer.volume(1);        //Set volume value (0~30).
  myDFPlayer.EQ(DFPLAYER_EQ_BASS);
  myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);
}
//↑↑=============== DFPlayer mini setup ===============↑↑//

void loop() {

  coinColorSelection();              //Coin Insert Color Recognition
  CoinInsertAnimation(1, 50);        //Coin Insert Animation (2 times for longer animation)
  CoinInsertAnimation(1, 50);        //Coin Insert Animation (2 times for longer animation)
  MorphAnimation(30);                //Sparkle with CoinColor and PlayMorph Sound
  PlayMorph();                       //Morph Sound for MorphAnimation
  Morph_Theme_Short_Rainbow_Long();  //Short press plays and stops Theme Song, long press shows Rainbow
}

//↓↓=============== Coin Color Selection ===============↓↓//
void coinColorSelection() {

  int button1 = digitalRead(Coin_Button_1) == LOW;
  int button2 = digitalRead(Coin_Button_2) == LOW;
  int button3 = digitalRead(Coin_Button_3) == LOW;

  prevcoinColor = coinColor;  // assign old color before determining new color

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    // Check for button combinations and assign the corresponding colors to coinColor
    if (button1 && button2) {
      coinColor = CRGB(20, 0, 0);           //Tyranosaurus - red
      if (prevcoinColor != coinColor) {
        myDFPlayer.playMp3Folder(1);
      }
    } else if (button1 && button3) {
      coinColor = CRGB(0, 20, 0);           //Dragonzord - green
      if (prevcoinColor != coinColor) {
        myDFPlayer.playMp3Folder(4);
      }
    } else if (button2 && button3) {
      coinColor = CRGB(20, 0, 10);          //Pterodactyl - pink
      if (prevcoinColor != coinColor) {
        myDFPlayer.playMp3Folder(5);
      }
    } else if (button1) {
      coinColor = CRGB(20, 20, 0);          //Sabertoothtiger - yellow
      if (prevcoinColor != coinColor) {
        myDFPlayer.playMp3Folder(2);
      }
    } else if (button2) {
      coinColor = CRGB(0, 5, 5);            //Mastodon - "black" (skyblue)
      if (prevcoinColor != coinColor) {
        myDFPlayer.playMp3Folder(6);
      }
    } else if (button3) {
      coinColor = CRGB(0, 0, 20);           //Triceratops
      if (prevcoinColor != coinColor) {
        myDFPlayer.playMp3Folder(3);
      }
    } else {
      coinColor = CRGB::Black;
    }
  }
}
//↑↑=============== Coin Color Selection ===============↑↑//

//↓↓=============== Coin insert LED Animation ===============↓↓//
void CoinInsertAnimation(int EyeSize, int SpeedDelay) {
FastLED.setBrightness(255);
  if (prevcoinColor != coinColor) {

    byte red = coinColor.red;
    byte green = coinColor.green;
    byte blue = coinColor.blue;

    for (int i = 0; i <= ((NUM_LEDS - EyeSize) / 2); i++) {
      fill_solid(leds, NUM_LEDS, CRGB::Black);
      FastLED.show();

      leds[i] = CRGB(red / 10, green / 10, blue / 10);

      for (int j = 1; j <= EyeSize; j++) {
        leds[i + j] = CRGB(red, green, blue);
      }
      leds[i + EyeSize + 1] = CRGB(red / 10, green / 10, blue / 10);

      leds[NUM_LEDS - i] = CRGB(red / 10, green / 10, blue / 10);
      for (int j = 1; j <= EyeSize; j++) {
        leds[NUM_LEDS - i - j] = CRGB(red, green, blue);
      }
      leds[NUM_LEDS - i - EyeSize - 1] = CRGB(red / 10, green / 10, blue / 10);

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

    for (int i = ((NUM_LEDS - EyeSize) / 2); i >= 0; i--) {
      fill_solid(leds, NUM_LEDS, CRGB::Black);
      FastLED.show();

      leds[i] = CRGB(red / 10, green / 10, blue / 10);
      for (int j = 1; j <= EyeSize; j++) {
        leds[i + j] = CRGB(red, green, blue);
      }
      leds[i + EyeSize + 1] = CRGB(red / 10, green / 10, blue / 10);

      leds[NUM_LEDS - i] = CRGB(red / 10, green / 10, blue / 10);
      for (int j = 1; j <= EyeSize; j++) {
        leds[NUM_LEDS - i - j] = CRGB(red, green, blue);
      }
      leds[NUM_LEDS - i - EyeSize - 1] = CRGB(red / 10, green / 10, blue / 10);

      FastLED.show();
      delay(SpeedDelay);

      fill_solid(leds, NUM_LEDS, CRGB::Black);
      FastLED.show();
    }
  }
}
//↑↑=============== Coin insert LED Animation ===============↑↑//

//↓↓=============== Morphing LED Animation ===============↓↓//

void MorphAnimation(fract8 chanceOfGlitter) {
FastLED.setBrightness(255);
  int button4 = digitalRead(Morph_Button) == LOW;

  if (button4) {

    if (!isRainbow) {
      fill_solid(leds, NUM_LEDS, CRGB::Black);
      FastLED.show();

      fill_solid(leds, NUM_LEDS, coinColor);
      if (random8() < chanceOfGlitter) {
        leds[random16(NUM_LEDS)] += CHSV(0, 0, 255);
      }
      FastLED.show();

      fill_solid(leds, NUM_LEDS, CRGB::Black);
      FastLED.show();
    }
  }
}
//↑↑=============== Morphing LED Animation ===============↑↑//

//↓↓=============== Morphing Sound ===============↓↓//
void PlayMorph() {

  int button4 = digitalRead(Morph_Button) == LOW;

  if (button4) {
    if (!isPlayingMorph) {       
      myDFPlayer.loopFolder(2);  // morphing Sound
      delay(50);
      isPlayingMorph = true;
    }
  } else {  
    if (isPlayingMorph) {
      myDFPlayer.stop();
      delay(50);
      isPlayingMorph = false;
    }
  }
}
//↑↑=============== Morphing Sound ===============↑↑//


//↓↓=============== Theme Song Test ===============↓↓//

void Morph_Theme_Short_Rainbow_Long() {
FastLED.setBrightness(10);
  if (digitalRead(Activate_Button) == LOW) {
    if (buttonActive == false) {
      buttonActive = true;
      buttonTimer = millis();
    }

    if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {
      longPressActive = true;
      if (isRainbow) {
        fill_solid(leds, NUM_LEDS, CRGB::Black);
        FastLED.show();
        isRainbow = false;
      } else {
        isRainbow = true;
        uint8_t thisHue = beat8(50, 255);
        uint8_t deltaHue = 255 / NUM_LEDS;
        fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
        FastLED.show();
      }
    }
  } else {
    if (buttonActive == true) {
      if (longPressActive == true) {
        longPressActive = false;

      } else {
        if (isPlayingTheme) {  
          myDFPlayer.stop();   
          delay(50);
          isPlayingTheme = false;
        } else {
          isPlayingTheme = true;  
          myDFPlayer.playMp3Folder(10);   // ThemeSong
          delay(50);
        }
      }
      buttonActive = false;
    }
  }
  if (isRainbow) {
    uint8_t thisHue = beat8(50, 255);
    uint8_t deltaHue = 255 / NUM_LEDS;
    fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
    FastLED.show();
  }
}

I have replaced the button click and hold function with a "smaller" version. This version does not need millis in the loop, therefore it does not interfere with the morph animation (which has created the rainbow flicker issue).

Also changed the morph animation void so that it turns off when rainbow is on, otherwise there was a overlaping of those two animations and it did not look good.

One minor flaw is a very subtle flickering within the morph animation sparkle. But it is not visible under some diffuse material.

The brightness problem is also solved. It was a misconception on my side. I just had set the brightness to a lower value for the coin and morph animation. But it needs to be set to 255 and is then handled by the CRGB values. This way I can set the brightness within every void and reducing the rainbow brightness without reducing any others effects brightness.

There are just two things left:

First is, how to set a delay time for the button recognition for not simultaniously pushed coin buttons? (the existing one does not work well)

Second is, finding a implementable function for button release detection, so I can have an animation and sound for when a coin is released or the morpher gets closed and so on.

But most important: I wish you, your mom and your family a blessed Christmas. Get some peaceful days.


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

Hi Trace!

Hope you had a good Christmas - as you can imagine, it was a little busy overhere now with moms situation.
I see that you have resolved some challenges: well done! 👍 

As for the two challenges;

Bot these issues, are something where we will probably need to work around the lack of multitasking on an Arduino.
I assume you're using an Arduino Uno. (correct?)

Doing two things at the same time can be done in several ways:
Either we have two cores (which we don't), or we have to figure a way to switch back and forth often (feels sloppy) or we try to utilize interrupts (tricky).

If I think about interrupts: An interrupt can be fired under several conditions. (see "All LED Effects project - Catching the button" or the Arduino Interrupt documentation).

There are several modi we can use:

  • LOW to trigger the interrupt whenever the pin is low,
  • CHANGE to trigger the interrupt whenever the pin changes value,
  • RISING to trigger when the pin goes from low to high,
  • FALLING for when the pin goes from high to low.

Newer or more advanced Arduinos, like the Due, Zero and MKR1000 boards allow also:

  • HIGH to trigger the interrupt whenever the pin is high.

So in our case, we can consider using "CHANGE" to register button state changes (being pressed, or released, and measure the time between the changes). Worth thinking about and experimenting with I would say.

For example, when we press a button, we log the time it has been pressed, etc etc.
We could consider looking at the Arduino Timer library, which waits for the delay you have in mind and see if the state of the button still matches what you're looking for.

I can imagine this taking a bit of reading and experimenting - I'd have to do the same thing to get a feel for it.


   
ReplyQuote
 Hans
(@hans)
Famed Member Admin
Joined: 11 years ago
Posts: 2695
 
Just a quick:
🍸 🥂 Happy New Year! 👍 🍾 
... for you and your family!

   
ReplyQuote
Page 4 / 5
Share: