@Hans Hi Hans, how are you? And are there news (hopefully good) about your mother?
I kept working on the Power Rangers code and utilized parts of the code we have already done, to make some progress.
There was still the task to have a button release function and I just realized that the "click and hold button"- function has some kind of button release function in it. Because the click function recognize the button release, not the button press.
This way I was able to set some timers to have a function getting executed after the button is pressed for a moment and not right when I press it. And with this I was able to use it just like a button release function for different animations and sounds.
Using the DFPlayer mini busy pin was also usefull to start/stop some animations. So the animation is only be executed as long as the mp3 is playing.
The only problem left is the non blocking delay part for the button recognition. Maybe it is good enough as it is, but I need to test it in real life conditions first.
Also I was able to make a blink pattern with different on off timings, so I can sync a simple blinking animation for a simple mp3 by looking at the timings within the mp3 and set them in code by hand. The way I did it seems to be more complicated than necessary. But it was possible to implement it to the coin release void.
Feel free to tell me what you think. Maybe some other ideas for too complicated parts or any flaws?
Here is the full code:
#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 open button
#define Activate_Button 8 // Activate button
#define Busy_Pin 9
CRGB prevcoinColor = CRGB::Black;
CRGB coinColor = CRGB::Black;
boolean isPlayingMorph = false;
boolean isPlayingTheme = false;
boolean isRainbow = false;
boolean isPlayingNoCoin = false;
//↓↓=============== Button Selection Delay (not working correctly but maybe good enough) ===============↓↓//
unsigned long previousMillis = 0;
const long interval = 1500;
//↑↑=============== Button Selection Delay (not working correctly but maybe good enough) ===============↑↑//
//↓↓=============== Theme_Short_Rainbow_Long ===============↓↓//
long buttonTimer = 0;
long longPressTime = 3000; //activate functions when button hold for 3 seconds
boolean buttonActive = false;
boolean longPressActive = false;
//↑↑=============== Theme_Short_Rainbow_Long ===============↑↑//
//↓↓=============== Morpher Jammed ===============↓↓//
long buttonTimer2 = 0;
long longPressTime2 = 1500; // activate functions when button hold for 1,5 seconds
boolean buttonActive2 = false;
boolean longPressActive2 = false;
//↑↑=============== Morpher Jammed ===============↑↑//
//↓↓=============== Coin Release ===============↓↓//
long buttonTimer3 = 0;
long longPressTime3 = 3000; //Pseudo-time, necessary for button release function to work
boolean buttonActive3 = false;
boolean longPressActive3 = false;
//↑↑=============== Coin Release ===============↑↑//
//↓↓=============== Morpher Close ===============↓↓//
long buttonTimer4 = 50; //standard is 0, 50 for testing
long longPressTime4 = 3000; //Pseudo-time, necessary for button release function to work
boolean buttonActive4 = false;
boolean longPressActive4 = false;
//↑↑=============== Morpher Close ===============↑↑//
//↓↓=============== No Coin Morph ===============↓↓//
long buttonTimer5 = 0;
long longPressTime5 = 1000; //activate functions when button hold for 1 second
boolean buttonActive5 = false;
boolean longPressActive5 = false;
//↑↑=============== No Coin Morph ===============↑↑//
//↓↓=============== Coin Release Animation Timings ===============↓↓//
// Define the on and off timings
const double onTime1 = 100;
const double offTime1 = 160;
const double onTime2 = 100;
const double offTime2 = 170;
const double onTime3 = 100;
const double offTime3 = 40;
const double onTime4 = 100;
const double offTime4 = 160;
const double onTime5 = 70;
const double offTime5 = 215;
const double onTime6 = 80;
const double offTime6 = 500;
int state = 0; // Define the current state of the state machine
//↑↑=============== Coin Release Animation Timings ===============↑↑//
//↓↓=============== 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); //probably not needed
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 click and hold functions (red button at the toy)
//↓↓=============== DFPlayer mini setup ===============↓↓/
mySoftwareSerial.begin(9600);
Serial.begin(115200);
if (!myDFPlayer.begin(mySoftwareSerial, true, false)) { //Use softwareSerial to communicate with mp3. true, false added removes popping sound
while (true)
;
}
myDFPlayer.setTimeOut(500); //Set serial communictaion time out 500ms
myDFPlayer.volume(1); //Set volume value (0~30).
myDFPlayer.EQ(DFPLAYER_EQ_BASS); //Set equalizer NORMAL, POP, ROCK, JAZZ, CLASSIC, BASS
myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);
}
//↑↑=============== DFPlayer mini setup ===============↑↑//
void loop() {
coinColorSelection(); //Coin Insert Color Recognition function
CoinInsertAnimation(1, 50); //Coin Insert Animation (3 times for longer animation)
CoinInsertAnimation(1, 50); //Coin Insert Animation (3 times for longer animation)
CoinInsertAnimation(1, 50); //Coin Insert Animation (3 times for longer animation)
MorphAnimation(100); //Sparkle with CoinColor and PlayMorph Sound (higher number = more sparkle)
PlayMorph(); //Morph Sound for MorphAnimation
Morph_Theme_Short_Rainbow_Long(); //Short press plays and stops Theme Song, long press shows Rainbow
NoCoinSparkle(1000); //sparkle animation when no coin is inserted but Morpher gets open (higher number = more sparkle)
NoCoinSound(); //Sound when no coin is inserted
Morpher_Jammed(); //Animation and Sound when Morpher is jammed but activate button is pressed (hold)
Coin_Release(); //Sound when the coin gets released
Morpher_Closing(); //Animation and Sound when Morpher gets closed
NoCoinMorph(); //Animation and Sound when no coin is inserted, Morpher gets open and activate button is pressed (hold)
//CoinReleaseAnimation(); // TEST
}
//↓↓=============== Coin Color Selection ===============↓↓//
void coinColorSelection() {
bool button1 = digitalRead(Coin_Button_1) == LOW;
bool button2 = digitalRead(Coin_Button_2) == LOW;
bool 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 - blue
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);
bool button4 = digitalRead(Morph_Button) == LOW;
bool button1 = digitalRead(Coin_Button_1) == LOW;
bool button2 = digitalRead(Coin_Button_2) == LOW;
bool button3 = digitalRead(Coin_Button_3) == LOW;
if ((button4) && (button1 | button2 | button3)) {
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); // probably not needed
//FastLED.show();
}
}
}
//↑↑=============== Morphing LED Animation ===============↑↑//
//↓↓=============== Morphing Sound ===============↓↓//
void PlayMorph() {
FastLED.setBrightness(40);
bool button4 = digitalRead(Morph_Button) == LOW;
bool button1 = digitalRead(Coin_Button_1) == LOW;
bool button2 = digitalRead(Coin_Button_2) == LOW;
bool button3 = digitalRead(Coin_Button_3) == LOW;
if ((button4) && (button1 | button2 | button3)) {
if (!isPlayingMorph) {
fill_solid(leds, NUM_LEDS, coinColor);
FastLED.show();
myDFPlayer.playMp3Folder(14); // Morpher opens Sound
delay(800);
myDFPlayer.loopFolder(2); // Morphing Sound
delay(50);
isPlayingMorph = true;
}
} else {
if (isPlayingMorph) {
myDFPlayer.stop();
delay(50);
isPlayingMorph = false;
}
}
}
//↑↑=============== Morphing Sound ===============↑↑//
//↓↓=============== Theme Song and Rainbow Test ===============↓↓//
void Morph_Theme_Short_Rainbow_Long() {
FastLED.setBrightness(10);
bool button1 = digitalRead(Coin_Button_1) == LOW;
bool button2 = digitalRead(Coin_Button_2) == LOW;
bool button3 = digitalRead(Coin_Button_3) == LOW;
bool button4 = digitalRead(Morph_Button) == LOW;
if ((digitalRead(Activate_Button) == LOW) && (button1 | button2 | button3) && button4) {
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 (millis() - buttonTimer >= 500) {
if (isPlayingTheme) {
myDFPlayer.stop();
delay(50);
isPlayingTheme = false;
} else {
isPlayingTheme = true;
myDFPlayer.playMp3Folder(10); // ThemeSong
delay(50);
}
}
}
buttonActive = false;
}
}
if (button4 == LOW) {
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
isRainbow = false;
} else if (isRainbow) {
uint8_t thisHue = beat8(50, 255);
uint8_t deltaHue = 255 / NUM_LEDS;
fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
FastLED.show();
}
}
//↑↑=============== Theme Song and Rainbow Test ===============↑↑//
//↓↓=============== NoCoin Animation Test ===============↓↓//
void NoCoinSparkle(fract8 chanceOfGlitter) {
FastLED.setBrightness(255);
bool button4 = digitalRead(Morph_Button) == LOW;
bool button1 = digitalRead(Coin_Button_1) == HIGH;
bool button2 = digitalRead(Coin_Button_2) == HIGH;
bool button3 = digitalRead(Coin_Button_3) == HIGH;
if ((button4) && (button1 && button2 && button3)) {
while (digitalRead(Busy_Pin) == LOW) {
if (random8() < chanceOfGlitter) {
leds[random16(NUM_LEDS)] += CHSV(random8(), 255, 255);
}
FastLED.show();
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
}
}
}
//↑↑=============== NoCoin Animation Test ===============↑↑//
//↓↓=============== NoCoin Sound ===============↓↓//
void NoCoinSound() {
bool button4 = digitalRead(Morph_Button) == LOW;
bool button1 = digitalRead(Coin_Button_1) == HIGH;
bool button2 = digitalRead(Coin_Button_2) == HIGH;
bool button3 = digitalRead(Coin_Button_3) == HIGH;
if ((button4) && (button1 && button2 && button3)) {
if (!isPlayingNoCoin) {
myDFPlayer.playMp3Folder(11); // crackle Sound
delay(50);
isPlayingNoCoin = true;
}
} else {
if (isPlayingNoCoin) {
myDFPlayer.stop();
delay(50);
isPlayingNoCoin = false;
}
}
}
//↑↑=============== NoCoin Sound ===============↑↑//
//↓↓=============== Morpher Jammed ===============↓↓//
void Morpher_Jammed() {
FastLED.setBrightness(10);
bool button4 = digitalRead(Morph_Button) == HIGH;
if ((digitalRead(Activate_Button) == LOW) && button4) {
if (buttonActive2 == false) {
buttonActive2 = true;
buttonTimer2 = millis();
}
if ((millis() - buttonTimer2 > longPressTime2) && (longPressActive2 == false)) {
longPressActive2 = true;
myDFPlayer.playMp3Folder(8); // Zordon Speech
delay(50);
while (digitalRead(Busy_Pin) == LOW) {
uint8_t thisHue = beat8(100, 255);
uint8_t deltaHue = 255 / NUM_LEDS;
fill_rainbow(leds, NUM_LEDS, thisHue, deltaHue);
FastLED.show();
}
}
} else {
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
if (buttonActive2 == true) {
if (longPressActive2 == true) {
longPressActive2 = false;
}
buttonActive2 = false;
}
}
}
//↑↑=============== Morpher Jammed ===============↑↑//
//↓↓=============== Coin Release Test ===============↓↓//
void Coin_Release() {
FastLED.setBrightness(40);
bool button1 = digitalRead(Coin_Button_1) == LOW;
bool button2 = digitalRead(Coin_Button_2) == LOW;
bool button3 = digitalRead(Coin_Button_3) == LOW;
if (button1 | button2 | button3) {
if (buttonActive3 == false) {
buttonActive3 = true;
buttonTimer3 = millis();
}
} else {
if (buttonActive3 == true) {
if (longPressActive3 == true) {
longPressActive3 = false;
} else {
myDFPlayer.playMp3Folder(12); // Power Rangers Beep
delay(50);
while (digitalRead(Busy_Pin) == LOW) {
switch (state) {
case 0: // On state 1
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = coinColor; // Set all LEDs to white
}
FastLED.show(); // Update LEDs
delay(onTime1);
state = 1; // Transition to next state
break;
case 1: // Off state 1
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black; // Set all LEDs to black
}
FastLED.show(); // Update LEDs
delay(offTime1);
state = 2; // Transition to next state
break;
case 2: // On state 2
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = coinColor; // Set all LEDs to blue
}
FastLED.show(); // Update LEDs
delay(onTime2);
state = 3; // Transition to next state
break;
case 3: // Off state 2
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black; // Set all LEDs to black
}
FastLED.show(); // Update LEDs
delay(offTime2);
state = 4; // Transition to the first state (on state)
break;
case 4: // On state 3
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = coinColor; // Set all LEDs to blue
}
FastLED.show(); // Update LEDs
delay(onTime3);
state = 5; // Transition to next state
break;
case 5: // Off state 3
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black; // Set all LEDs to black
}
FastLED.show(); // Update LEDs
delay(offTime3);
state = 6; // Transition to the first state (on state)
break;
case 6: // On state 4
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = coinColor; // Set all LEDs to blue
}
FastLED.show(); // Update LEDs
delay(onTime4);
state = 7; // Transition to next state
break;
case 7: // Off state 4
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black; // Set all LEDs to black
}
FastLED.show(); // Update LEDs
delay(offTime4);
state = 8; // Transition to the first state (on state)
break;
case 8: // On state 5
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = coinColor; // Set all LEDs to blue
}
FastLED.show(); // Update LEDs
delay(onTime5);
state = 9; // Transition to next state
break;
case 9: // Off state 5
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black; // Set all LEDs to black
}
FastLED.show(); // Update LEDs
delay(offTime5);
state = 10; // Transition to the first state (on state)
break;
case 10: // On state 6
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = coinColor; // Set all LEDs to blue
}
FastLED.show(); // Update LEDs
delay(onTime6);
state = 11; // Transition to next state
break;
case 11: // Off state 6
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB::Black; // Set all LEDs to black
}
FastLED.show(); // Update LEDs
delay(offTime6);
state = 0; // Transition to the first state (on state)
break;
}
}
}
buttonActive3 = false;
}
}
}
//↑↑=============== Coin Release Test ===============↑↑//
//↓↓=============== Morpher Closing Test ===============↓↓//
void Morpher_Closing() {
FastLED.setBrightness(60);
bool button4 = digitalRead(Morph_Button) == LOW;
if (button4) {
if (buttonActive4 == false) {
buttonActive4 = true;
buttonTimer4 = millis();
}
} else {
if (buttonActive4 == true) {
if (longPressActive4 == true) {
longPressActive4 = false;
} else {
myDFPlayer.playMp3Folder(7); // Morpher Closing Sound
delay(50);
while (digitalRead(Busy_Pin) == LOW) {
fill_solid(leds, NUM_LEDS, coinColor);
FastLED.show();
}
}
buttonActive4 = false;
}
}
}
//↑↑=============== Morpher Closing Test ===============↑↑//
//↓↓=============== No Coin Morph ===============↓↓//
void NoCoinMorph() {
bool button1 = digitalRead(Coin_Button_1) == HIGH;
bool button2 = digitalRead(Coin_Button_2) == HIGH;
bool button3 = digitalRead(Coin_Button_3) == HIGH;
bool button4 = digitalRead(Morph_Button) == LOW;
if ((digitalRead(Activate_Button) == LOW) && (button1 && button2 && button3) && button4) {
if (buttonActive5 == false) {
buttonActive5 = true;
buttonTimer5 = millis();
}
if ((millis() - buttonTimer5 > longPressTime5) && (longPressActive5 == false)) {
longPressActive5 = true;
myDFPlayer.playMp3Folder(13); // Alarm Alpha 5
delay(50);
while (digitalRead(Busy_Pin) == LOW) {
fill_solid(leds, NUM_LEDS, CHSV(0, 255, 30));
float breath = (exp(sin(millis() / 430.0 * PI)) - 0.36787944) * 108.0;
FastLED.setBrightness(breath);
FastLED.show();
}
}
} else {
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
if (buttonActive5 == true) {
if (longPressActive5 == true) {
longPressActive5 = false;
}
buttonActive5 = false;
}
}
}
//↑↑=============== No Coin Morph ===============↑↑//