Help with building an alarm clock with fading lights, music and a touch screen

Hi everyone,

I am trying to build a wake up light (an alarm clock with a LED strip) using:

  1. Arduino mega
  2. a tft touch screen
  3. a real tim clock
  4. mini dfp player
  5. a LED strip

I am trying to do this project one step at a time and now I am trying to get the alarm function to work. For the alarm function I need the LED strip and the mp3 volume to fade in.
As I want to be able to switch of the alarm during this function I am building it without using delays. So I am using the millis() functionalities.

I have two problems:
The fading of the volume does not work, there is no sound coming out of the MP3 player anymore.
The LED fading stopped working once I implemented the volume fade.

Any help is much appreciated!
Hereby the code:

#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h> // MP3 library

SoftwareSerial mySoftwareSerial(47, 45); // RX, TX  Define so the MP3 can write back to the computer
DFRobotDFPlayerMini myDFPlayer;

//MP3
//Define Mp3
# define startByte 0x7E
# define endByte 0xEF
# define versionByte 0xFF
# define dataLength 0x06
# define infoReq 0x01
# define Acknowledge 0x00

// define directions for LED fade
#define UP 0

// declarations for the volume fade
unsigned long previousFadeMillis_volume;
byte volumeIncrement = 1;
byte volumeDirection = UP;
int volumeInterval = 100;
int volumeValue = 10;
const int maxvolume = 20;

//declarations for the LED fade

const byte pwmLED = 46;

// constants for min and max PWM
const int minPWM = 0;
const int maxPWM = 180;

// State Variable for Fade Direction
byte fadeDirection = UP;

// Global Fade Value
// but be bigger than byte and signed, for rollover
int fadeValue = 0;

// How smooth to fade?
byte fadeIncrement = 1;

// millis() timing Variable, just for fading
unsigned long previousFadeMillis;

// How fast to increment?
int fadeInterval = 100;

void setup() {
  // put pwmLED into known state (off)
  analogWrite(pwmLED, fadeValue);


  //MP3

  mySoftwareSerial.begin(9600);
  Serial.begin(9600);
  //   Serial.begin(115200);

  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));

  if (!myDFPlayer.begin(mySoftwareSerial, false)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while (true);
  }
  Serial.println(F("DFPlayer Mini online."));

  myDFPlayer.setTimeOut(500); //Set serial communication time out 500ms

  //----Set volume----
  myDFPlayer.volume(volumeValue);  //Set volume value (0~30).

  //----Set different EQ----
  myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);

  //----Set device we use SD as default----
  myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);

}

void loop() {
  // get the current time, for this time around loop
  // all millis() timer checks will use this time stamp
  unsigned long currentMillis = millis();

  playAlarm(currentMillis);
  myDFPlayer.start();

}


void playAlarm(unsigned long thisMillis)
{
  // LED fade
  // is it time to update yet?
  // if not, nothing happens
  if (thisMillis - previousFadeMillis >= fadeInterval) {
    // yup, it's time!
    if (fadeDirection == UP) {
      fadeValue = fadeValue + fadeIncrement;
      if (fadeValue >= maxPWM) {
        // At max, limit and change direction
        fadeValue = maxPWM;
      }
    }
  }
  // Volume fade
  if (thisMillis -  previousFadeMillis_volume >= volumeInterval) {
    // yup, it's time!
    if (volumeDirection == UP) {
      volumeValue =  volumeValue + volumeIncrement;
      if (volumeValue >= maxvolume) {
        // At max, limit and change direction
        volumeValue = maxvolume;
      }
    }
  }
  // Only need to update when it changes
  analogWrite(pwmLED, fadeValue);
  myDFPlayer.volume(volumeValue);

  // reset millis for the next iteration (fade timer only)
  previousFadeMillis = thisMillis;
  previousFadeMillis_volume = thisMillis;

}

Hi and welcome to the forum.

As you can see, the forum has made a mess of your code. Indentation is lost and formatting changes at random. This is because you did not use code tags. Please edit your first post and fix that. The easiest way is to click “Tools–>Auto format” in the Arduino IDE, then click “Edit–>Copy to forum” and paste into your post.

Question: why are you using software serial? Mega has several hardware serial ports available, and they should always be used in preference to software serial.

Hello,
take a look into your sketch and see when do you reload the millis() into the timer functions for previousFadeMillis and previousFadeMillis_volume. I gues that this causes your malfunction of you sketch.

Thank you for the reply!
I edited my post. This looks much better :slight_smile:

As an answer to your question:
I used the code I found in a tutorial and on github, which both use software serial and pins 10 and 11.

As the pins 10 and 11 are taken by the touch screen in my project, my guess was to use PWM pins. but as I discover now, these are not the same as tx and rx pins and 47 isn’t even a PWM pin.
But even with this the mp3 in itself to works quite well with simple play, pause, next and back commands. The volume control is my struggle. It did work with a delay function like this:

  myDFPlayer.play(1);
  brightness = 30;
  analogWrite(led, brightness);
  delay(5000);
  myDFPlayer.volume(6);
  brightness = 60;
  analogWrite(led, brightness);
  delay(5000);
  myDFPlayer.volume(8);
  brightness = 90;
  analogWrite(led, brightness);
  delay(5000);
  myDFPlayer.volume(10);
  brightness = 120;
  analogWrite(led, brightness);
  delay(5000);
  myDFPlayer.volume(15);
  delay(5000);
  brightness = 5;
  analogWrite(led, brightness);
  myDFPlayer.volume(2);

But I think this is not the way to go with building this, as it is quite an ugly long code and there are a lot of delays in there, which I guess will not allow to build a snooze / off button.

If you suggest to use other ports I would be happy to try it out! However, the tft touch screen shield blocks ports 0-13 and a0-a5.

1 Like

Thanks for the reply!
I changed the location, now the fading of the light works again :+1:
The fading of the volume still doesn’t unfortunately.

#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h> // MP3 library

SoftwareSerial mySoftwareSerial(47, 45); // RX, TX  Define so the MP3 can write back to the computer
DFRobotDFPlayerMini myDFPlayer;

//MP3
//Define Mp3
# define startByte 0x7E
# define endByte 0xEF
# define versionByte 0xFF
# define dataLength 0x06
# define infoReq 0x01
# define Acknowledge 0x00

// define directions for LED fade
#define UP 0

// declarations for the volume fade
unsigned long previousFadeMillis_volume;
byte volumeIncrement = 1;
byte volumeDirection = UP;
int volumeInterval = 100;
int volumeValue = 10;
const int maxvolume = 20;

//declarations for the LED fade

const byte pwmLED = 46;

// constants for min and max PWM
const int minPWM = 0;
const int maxPWM = 180;

// State Variable for Fade Direction
byte fadeDirection = UP;

// Global Fade Value
// but be bigger than byte and signed, for rollover
int fadeValue = 0;

// How smooth to fade?
byte fadeIncrement = 1;

// millis() timing Variable, just for fading
unsigned long previousFadeMillis;

// How fast to increment?
int fadeInterval = 1000;

void setup() {
  // put pwmLED into known state (off)
  analogWrite(pwmLED, fadeValue);


  //MP3

  mySoftwareSerial.begin(9600);
  Serial.begin(9600);
  //   Serial.begin(115200);

  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));

  if (!myDFPlayer.begin(mySoftwareSerial, false)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while (true);
  }
  Serial.println(F("DFPlayer Mini online."));

  myDFPlayer.setTimeOut(500); //Set serial communication time out 500ms

  //----Set volume----
  myDFPlayer.volume(volumeValue);  //Set volume value (0~30).

  //----Set different EQ----
  myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);

  //----Set device we use SD as default----
  myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);

}

void loop() {
  // get the current time, for this time around loop
  // all millis() timer checks will use this time stamp
  unsigned long currentMillis = millis();

  playAlarm(currentMillis);
  myDFPlayer.start();

}


void playAlarm(unsigned long thisMillis)
{
  // LED fade
  // is it time to update yet?
  // if not, nothing happens
  if (thisMillis - previousFadeMillis >= fadeInterval) {
    // yup, it's time!
    if (fadeDirection == UP) {
      fadeValue = fadeValue + fadeIncrement;
      if (fadeValue >= maxPWM) {
        // At max, limit and change direction
        fadeValue = maxPWM;
      }
    }
    // Only need to update when it changes
    analogWrite(pwmLED, fadeValue);
    // reset millis for the next iteration (fade timer only)
    previousFadeMillis = thisMillis;
  }
  // Volume fade
  if (thisMillis -  previousFadeMillis_volume >= volumeInterval) {
    // yup, it's time!
    if (volumeDirection == UP) {
      volumeValue =  volumeValue + volumeIncrement;
      if (volumeValue >= maxvolume) {
        // At max, limit and change direction
        volumeValue = maxvolume;
      }
    }

    // Only need to update when it changes
    myDFPlayer.volume(volumeValue);

    // reset millis for the next iteration (fade timer only)
    previousFadeMillis_volume = thisMillis;

  }
}
myDFPlayer.start();

I’m not sure why you have this in loop(), I suggest you try removing it.

I suggest you find out if any hardware serial ports are not blocked and use one of those.

Okay, since last update the code stopped working totally. So I took a step back and try to get the DFplayer working again. I noticed that I can not get it to work on other pins than 11 and 10 using this code:

#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>

// Use pins 2 and 3 to communicate with DFPlayer Mini

static const uint8_t PIN_MP3_TX = 11; // Connects to module's TX GEEL
static const uint8_t PIN_MP3_RX = 10; // Connects to module's RX GROEN

SoftwareSerial softwareSerial(PIN_MP3_RX, PIN_MP3_TX);

// Create the Player object
DFRobotDFPlayerMini player;

void setup() {
  // Init USB serial port for debugging
  Serial.begin(9600);
  // Init serial port for DFPlayer Mini
  softwareSerial.begin(9600);

  // Start communication with DFPlayer Mini
  if (player.begin(softwareSerial)) {
    Serial.println("OK");

    // Set volume to maximum (0 to 30).
    player.volume(15);
    // Play the first track
    player.play(2);

  } else {
    Serial.println("Connecting to DFPlayer Mini failed!");
  }
}

void loop() {
}

However, pins 10 and 11 are not available in my project as the touch screen uses these pins. Do you have any other pins I could try on my arduino mega ? And how would that work with hardware serial ports? Can I use the same code? All the examples I found use softwareSerial and software serial pins.
Thanks for the help!

Find a pinout diagram for the mega and find if there is a hardware serial port that is not blocked by your touchscreen shield. If so, use that port. There will be some changes to the sketch, but they will not be difficult.

I have 3 pairs of hardware serials available, pins 14 - 19.
If I understand correctly, these pins are optimized for communication with other hardware devices, while the software.serial simulates this?
Am I right that I should only change “softwareSerial” into “Serial”? Or does this has more implications for the code?

The serial port that communicates with the serial monitor on your pc is called "Serial". The other serial ports are called "Serial1", "Serial2" and so on. To use one of those extra ports to communicate with the MP3 player, you can remove these lines

#include <SoftwareSerial.h>

SoftwareSerial mySoftwareSerial(47, 45); // RX, TX  Define so the MP3 can write back to the computer

and replace every occurrence of "mySoftwareSerial" with "SerialN" where "N" is the serial port you have chosen to use.

1 Like

Yes, that works! Great :slight_smile:
Thanks a lot.
I also got the fading to work, but there is a strange problem.
Here is the code:

#include <DFRobotDFPlayerMini.h>

// Create the Player object
DFRobotDFPlayerMini player;

unsigned long previousFadeMillis_volume;
byte volumeIncrement = 1;
int volumeInterval = 500;
int volumeValue = 5;
const int maxvolume = 20;

void setup() {
  // Init USB serial port for debugging
  Serial.begin(9600);
  // Init serial port for DFPlayer Mini
  Serial1.begin(9600);

  // Start communication with DFPlayer Mini
  if (player.begin(Serial1)) {
    Serial.println("OK");

    //Start playing the MP3
    player.play(1);

  } else {
    Serial.println("Connecting to DFPlayer Mini failed!");
  }

}

void loop() {
  unsigned long currentMillis = millis();
  playAlarm(currentMillis);
  Serial.println(volumeValue);
}

void playAlarm(unsigned long thisMillis)
{
  if (thisMillis -  previousFadeMillis_volume >= volumeInterval) {
    // yup, it's time!
    volumeValue =  volumeValue + volumeIncrement;
    if (volumeValue >= maxvolume) {
      // At max, limit
      volumeValue = maxvolume;
    }
  }
  // Only need to update when it changes
  player.volume(volumeValue);

  // reset millis for the next iteration (fade timer only)
  previousFadeMillis_volume = thisMillis;

}

This code seems to work fine.
The problem occurs when I enlarge the volumeInterval to more than 500. Then the fading stops working at all.
Do you have any idea why this happens?

Edit: I changed volumeIncrement to volumeInterval.

It seems a strange question. Either I don't understand the question, of you asked the wrong question. But it sounds like you asked "how many times can I add 500 to 5 before it becomes larger than 20?". Obviously the answer is "one time", which would result in a very sudden "fade".

My mistake, I am sorry. I meant that the volumeInterval cannot be changed into something higher than 500 ms. I will edit this in my previous reply.

I should have guessed that was what you meant, question makes sense now.

Try changing volumeInterval to unsigned long instead of int.

EDIT: Ah, you have a } in the wrong place!

  // Only need to update when it changes

But it's getting updated even when it hasn't changed, because of that misplaced }

1 Like

That wrong placed } was indeed the problem. Thanks again!
Do you have a tip how I can avoid such an error?

Hereby the updated code:

#include <DFRobotDFPlayerMini.h>

// Create the Player object
DFRobotDFPlayerMini player;

unsigned long previousFadeMillis_volume;
byte volumeIncrement = 1;
int volumeInterval = 5000;
int volumeValue = 5;
const int maxvolume = 20;

void setup() {
  // Init USB serial port for debugging
  Serial.begin(9600);
  // Init serial port for DFPlayer Mini
  Serial1.begin(9600);

  // Start communication with DFPlayer Mini
  if (player.begin(Serial1)) {
    Serial.println("OK");

    //Start playing the MP3
    player.volume(volumeValue);
    player.play(1);

  } else {
    Serial.println("Connecting to DFPlayer Mini failed!");
  }

}

void loop() {
  unsigned long currentMillis = millis();
  playAlarm(currentMillis);
  Serial.println(volumeValue);
}

void playAlarm(unsigned long thisMillis)
{
  if (thisMillis -  previousFadeMillis_volume >= volumeInterval) {
    // yup, it's time!
    volumeValue =  volumeValue + volumeIncrement;
    if (volumeValue >= maxvolume) {
      // At max, limit
      volumeValue = maxvolume;
    }
    // Only need to update when it changes
    player.volume(volumeValue);


    // reset millis for the next iteration (fade timer only)
    previousFadeMillis_volume = thisMillis;
  }
}

My next step will be to combine the mp3 code with the LED. I will keep you posted..

All I can suggest is to pay attention to indentation. Code that should always be executed together should be at the same level of indentation. Use Auto-Format to keep your indentation correct. You seem to be doing that.

You will have more dimming/fading levels with the led (255) than with the MP3 (30, but you might not want to use all of them). So I suggest you change the code above to control timing the dimming of the LEDs, then use map() function to calculate the MP3 volume from the led dimming level. That should help keep things simpler than having two independant pieces of code to fade LEDs separately from MP3 and then having to synchronise them.

It has been a while since my last post due to my holiday. But I am back at it again.
I've got the light and music fading to work with this code:

#include <DFRobotDFPlayerMini.h>

// Create the Player object
DFRobotDFPlayerMini player;


// define directions for LED fade
#define UP 0

// declarations for the volume fade
unsigned long previousFadeMillis_volume;
byte volumeIncrement = 1;
int volumeInterval = 5000;
int volumeValue = 5;
const int maxvolume = 20;

//declarations for the LED fade

const byte pwmLED = 46;

// constants for min and max PWM
const int minPWM = 0;
const int maxPWM = 180;

// State Variable for Fade Direction
byte fadeDirection = UP;

// Global Fade Value
// but be bigger than byte and signed, for rollover
int fadeValue = 0;

// How smooth to fade?
byte fadeIncrement = 1;

// millis() timing Variable, just for fading
unsigned long previousFadeMillis;

// How fast to increment?
int fadeInterval = 5000;

void setup() {

  // put pwmLED into known state (off)
  analogWrite(pwmLED, fadeValue);

  //MP3
  // Init USB serial port for debugging
  Serial.begin(9600);
  // Init serial port for DFPlayer Mini
  Serial1.begin(9600);

  // Start communication with DFPlayer Mini
  if (player.begin(Serial1)) {
    Serial.println("OK");

    //Start playing the MP3
    player.volume(volumeValue);
    player.play(1);

  } else {
    Serial.println("Connecting to DFPlayer Mini failed!");
  }

}

void loop() {
  unsigned long currentMillis = millis();
  playAlarm(currentMillis);
  Serial.println(volumeValue);
  Serial.println(millis());
   Serial.println(previousFadeMillis_volume);
}

void playAlarm(unsigned long thisMillis)
{
  // LED fade
  // is it time to update yet?
  // if not, nothing happens
  if (thisMillis - previousFadeMillis >= fadeInterval) {
    // yup, it's time!
    if (fadeDirection == UP) {
      fadeValue = fadeValue + fadeIncrement;
      if (fadeValue >= maxPWM) {
        // At max, limit and change direction
        fadeValue = maxPWM;
      }
    }
    // Only need to update when it changes
    analogWrite(pwmLED, fadeValue);
    // reset millis for the next iteration (fade timer only)
    previousFadeMillis = thisMillis;
  }
  if (thisMillis -  previousFadeMillis_volume >= volumeInterval) {
    // yup, it's time!
    volumeValue =  volumeValue + volumeIncrement;
    if (volumeValue >= maxvolume) {
      // At max, limit
      volumeValue = maxvolume;
    }
    // Only need to update when it changes
    player.volume(volumeValue);


    // reset millis for the next iteration (fade timer only)
    previousFadeMillis_volume = thisMillis;
  }
}

Thanks for the mapping suggestion @PaulRB , but with this code I do use two different functions, as I want the music to start later than the lights.

Next up is adding a snooze button to this code.

Okay, I can not seem to get the snooze / start button to work.
I would like it to do the following:

If the snooze button is pressed + Alarm state is off > Start playing the alarm & Change Alarm State to on
If the snooze button is pressed + Alarm state is on > Stop playing the alarm & Change Alarm State to off

I tried the following code. But it never seems to stop the alarm. It only restarts it. Any tips on how to make it actually stop the alarm?

#include <DFRobotDFPlayerMini.h>

// Create the Player object
DFRobotDFPlayerMini player;

// define directions for LED fade
#define UP 0

// declarations for the volume fade
unsigned long previousFadeMillis_volume;
byte volumeIncrement = 1;
int volumeInterval = 5000;
int volumeValue = 5;
const int maxvolume = 20;

//declarations for the LED fade
const byte pwmLED = 46;

// constants for min and max PWM
const int minPWM = 0;
const int maxPWM = 180;

// State Variable for Fade Direction
byte fadeDirection = UP;

// Global Fade Value
// but be bigger than byte and signed, for rollover
int fadeValue = 0;

// How smooth to fade?
byte fadeIncrement = 1;

// millis() timing Variable, just for fading
unsigned long previousFadeMillis;

// How fast to increment?
int fadeInterval = 5000;

//Button

const int SnoozePin = 22;     // the number of the pushbutton pin

int Alarmstate = 0;  //Is the alarm playing?
int SnoozePressed = 0;  //Should the alarm be playing?

//int buttonState = 0;         // variable for reading the pushbutton status
//int alarmState = 0;       // variable for reading the alarm status

void setup() {

  // put pwmLED into known state (off)
  analogWrite(pwmLED, fadeValue);

  //Button
  pinMode(SnoozePin, INPUT);

  //MP3
  // Init USB serial port for debugging
  Serial.begin(9600);
  // Init serial port for DFPlayer Mini
  Serial1.begin(9600);

  // Start communication with DFPlayer Mini
  if (player.begin(Serial1)) {
    Serial.println("OK");

  } else {
    Serial.println("Connecting to DFPlayer Mini failed!");
  }

}

void loop() {
  unsigned long currentMillis = millis();
  playAlarm(currentMillis);
  Serial.println(millis());
  Serial.print("Volume value:"); Serial.println(volumeValue);
  Serial.print("Fade value:"); Serial.println(fadeValue);
  Serial.print("Snooze button pressed:"); Serial.println(SnoozePressed);
  Serial.print("Alarm state:"); Serial.println(Alarmstate);

  // Snooze button
  if (SnoozePressed) {
    if (digitalRead(SnoozePin) == LOW) {
      SnoozePressed = 0;
    }
  } else {
    if (digitalRead(SnoozePin) == HIGH) {
      SnoozePressed = 1;
    }
    if (Alarmstate == 0) {
      Alarmstate = 1;
    } else {
      Alarmstate = 0;
    }
  }
}

//Alarm Code
void playAlarm(unsigned long thisMillis)
{
  //Alarmstate (if it is high, than play the alarm, otherwise stop playing)
  if (Alarmstate) {
    // LED fade
    // is it time to update yet?
    // if not, nothing happens
    if (thisMillis - previousFadeMillis >= fadeInterval) {
      // yup, it's time!
      if (fadeDirection == UP) {
        fadeValue = fadeValue + fadeIncrement;
        if (fadeValue >= maxPWM) {
          // At max, limit
          fadeValue = maxPWM;
        }
      }
      // Only need to update when it changes
      analogWrite(pwmLED, fadeValue);
      // reset millis for the next iteration (fade timer only)
      previousFadeMillis = thisMillis;
    }
    //MP3 Fade
    player.volume(volumeValue);
    player.play(1);
    if (thisMillis -  previousFadeMillis_volume >= volumeInterval) {
      // yup, it's time!
      volumeValue =  volumeValue + volumeIncrement;
      if (volumeValue >= maxvolume) {
        // At max, limit
        volumeValue = maxvolume;
      }
      // Only need to update when it changes
      player.volume(volumeValue);
      // reset millis for the next iteration (fade timer only)
      previousFadeMillis_volume = thisMillis;
    }
  }
  else {
    // Do nothing
  }
}

Thanks!

What type of button is this?

Is it in fact a toggle switch?

How is it wired?

Could the input pin be floating?

Could the button be bouncing?

Should your code be detecting the state change of the pin rather than simply it's current state?

Thanks for the help!
As a side note, what do you advice regarding to posting on the forum:
Every problem in a seperate post or all problems in replies to my original post?
I did the latter with this question, but I noticed I got very little replies.

Back to the topic..
What type of button is this?
Is it in fact a toggle switch?
How is it wired?
Could the input pin be floating?
I am using a press button as you can see in this photo (the yellow button):
I am using a resistor, so I don't think it is floating.


Could the button be bouncing?
I tried adding a delay in the code, but this did not really make a difference.

Should your code be detecting the state change of the pin rather than simply it's current state?
Yes I would like it to detect the state change, but it should also know whether the alarm was on or off at the moment the button is pressed.