1

This is my enhanced code for a daylight alarm on Arduino UNO with an added MP3-Player. The device is supposed to sleep all the time except when I hit the play button (then it is supposed to play music) or when the RTC wakes it up, both via interrupt. When the RTC wakes it up it will start its actual daylight alarm routine.

I just implemented the sleep/power-down code and got it working so that I could wake it up and put it to sleep with pins 9 (sleep) and 2 (wake up via interrupt). There was a problem though and I had to change the code.

Apparently while trying to fix it I broke something. The current behiour upon startup is: 1. Go to sleep (correct) 2. When I bridge pin 2 and ground, it wakes up. (correct) 3. When I then bridge pin 9 and ground it goes to sleep. (correct). 4. When I bridge pin 2 and ground again it does not wake up again.

I have changed the code so that in addition to playing music the builtin LED will light up.

#include <avr/sleep.h>
/************ init mp3-player ***************/
#include <SoftwareSerial.h>

#define ARDUINO_RX 4  //should connect to TX of the Serial MP3 Player module
#define ARDUINO_TX 5  //connect to RX of the module

SoftwareSerial mp3(ARDUINO_RX, ARDUINO_TX);
static int8_t Send_buf[8] = {0}; // Buffer for Send commands.  // BETTER LOCALLY
String sbyte2hex(uint8_t b);

#define CMD_SLEEP_MODE    0X0A
#define CMD_WAKE_UP       0X0B
#define CMD_SEL_DEV       0X09
#define CMD_PLAY          0X0D
#define CMD_STOP_PLAY     0X16  // Stop playing continuously. 
#define CMD_FOLDER_CYCLE  0X17
#define CMD_SET_DAC       0X1A
#define DAC_ON            0X00
#define DAC_OFF           0X01
#define DEV_TF            0X02

/**************** init the real time clock ***************/
#include <Rtc_Pcf8563.h>
Rtc_Pcf8563 rtc;

/**************** setup pins & buttons ***************/
const byte led = 11; // Pin LED is connected to
const byte sixAmPin = 6; // Pin for set-alarm-to-6am-button
const byte sevenAmPin = 7; // Pin for set-alarm-to-7am-button
const byte eightAmPin = 8; // Pin for set-alarm-to-8am-button
const byte stopPin = 9; // Pin for stop button
const byte timeResetPin = 10; // Pin for reset-clock-to-9am-button (useful e.g. after battery failure)
const byte playPin = 2; // Pin for play-music-button (and interrupt!)
const byte rtcIntPin = 3; // Pin for RTC interrupt

byte sixAmButton = 1; // State of 6am-set-button
byte sevenAmButton = 1; // State of 7am-set-button
byte eightAmButton = 1; // State of 8am-set-button
byte stopButton = 1; // State of stopButton
byte timeResetButton = 1; // State of time-set-button
byte playButton = 1; // State of play-music-button

byte brightness = 0; // LED brightness
bool wakeupMode = false; 
unsigned long prevStepTime = millis(); 
unsigned long time;

/*************** Changeable variables here **************/
byte setHour = 6;
byte setMin = 30;
byte fadeDuration = 30; // Duration of fade in minutes (default: 30)
byte afterBurn = 30; // Keep light on after alarm for this many minutes (default: 30)
/********************************************************/

unsigned long keepLight = afterBurn * 60000UL; // Calculate time to keep LED on after fade (in ms)
int fadeStepLength = fadeDuration * 60000 / 255; // Calculate step duration for increasing LED brightness (in ms) (default: fadeDuration * 60000 / 255)

void setup() {
  /*DEBUG*/
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  /*DEBUG END*/

  // Serial.begin(9600); //uncomment after testing
  mp3.begin(9600);
  delay(500);

  sendCommand(CMD_SEL_DEV, 0, DEV_TF);
  delay(500);

  // declare LED pin to be an output:
  pinMode(led, OUTPUT);

  // declare button pins as inputs:
  pinMode(stopPin, INPUT_PULLUP);
  pinMode(sixAmPin, INPUT_PULLUP);
  pinMode(sevenAmPin, INPUT_PULLUP);
  pinMode(eightAmPin, INPUT_PULLUP);
  pinMode(timeResetPin, INPUT_PULLUP);
  pinMode(playPin, INPUT_PULLUP);
  pinMode(rtcIntPin, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(playPin), wakeUpPlay, FALLING);
  attachInterrupt(digitalPinToInterrupt(rtcIntPin), wakeAlarm, FALLING);  
}

void loop() {
  goToSleep();
}

void wakeup() {
  // If wake-up-mode is true and brightness < 255, add +1 to brightness every 7 seconds (256 steps)
  wakeupMode = true;
  while (wakeupMode == true) {
    //update clock
    time = millis();

    // If current time is wakeup-time, increase LED brightness slowly over the course of 30 minutes
    if (brightness < 255 ) {
      // Delay brightening in steps. Do only if interval has passed.
      if (time > prevStepTime + fadeStepLength) {
        brightness ++;
        prevStepTime = millis(); // Resets counter to restart interval
      }
     }
    // Turn off light and exit wakupMode x minutes after max brightness
    else if (brightness == 255 && time > prevStepTime + keepLight) {
      brightness = 0;
      wakeupMode = false; // Exit wakeupMode loop
    }
    readButtons(); // Check for button input
    analogWrite(led, brightness); // Set LED brightness accordingly
  }
  goToSleep;
}

void readButtons() {
  // Always run this:
  // Check if alarm gets set for 6, 7 or 8am
  sixAmButton = digitalRead(sixAmPin);
  if (sixAmButton == LOW) {
    rtc.setAlarm(5, 30, 99, 99);
  }
  sevenAmButton = digitalRead(sevenAmPin);
  if (sevenAmButton == LOW) {
    rtc.setAlarm(6, 30, 99, 99);
  }
  eightAmButton = digitalRead(eightAmPin);
  if (eightAmButton == LOW) {
    rtc.setAlarm(7, 30, 99, 99);
  }

  // If stopButton is pressed: stop playing music, turn off LED, sleep mp3-player, sleep arduino
  stopButton = digitalRead(stopPin);
  if (stopButton == LOW) {
    wakeupMode = false;
    brightness = 0; // uncomment after testing
    analogWrite(led, brightness); // Set LED brightness accordingly
    sendCommand(CMD_STOP_PLAY);
    sendCommand(CMD_SLEEP_MODE);
    delay(200);
    goToSleep();
  }

  // If timeResetButton is pressed, reset RTC to 9am
  if (timeResetButton == LOW) {
    rtc.setTime(9, 0, 0);
  }

  // If Play music button is pressed, start playing music
  playButton = digitalRead(playPin);
  if (playButton == LOW) {
    playMusic();
    }
}

void playMusic(){
  digitalWrite(LED_BUILTIN, HIGH);
  sendCommand(CMD_WAKE_UP);
  delay(200);
  sendCommand(CMD_FOLDER_CYCLE, 1, 0);
  while (1){
    readButtons();
  }
}

void wakeUpPlay(){
  sleep_disable();
  detachInterrupt(digitalPinToInterrupt(playPin));
  detachInterrupt(digitalPinToInterrupt(rtcIntPin));
  playMusic();
}

void wakeAlarm(){
  sleep_disable();
  detachInterrupt(digitalPinToInterrupt(playPin));
  detachInterrupt(digitalPinToInterrupt(rtcIntPin));
  wakeup();
}

void goToSleep() {
  sleep_enable();
  attachInterrupt(digitalPinToInterrupt(playPin), wakeUpPlay, FALLING);
  attachInterrupt(digitalPinToInterrupt(rtcIntPin), wakeAlarm, FALLING);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  digitalWrite(LED_BUILTIN, LOW);
  delay(500);
  sleep_cpu();
}

/**************************/
/** Stuff for MP3 player **/
/**************************/

/********************************************************************************/
/*Function: Send command to the MP3                                             */
/*Parameter: byte command                                                       */
/*Parameter: byte dat1 parameter for the command                                */
/*Parameter: byte dat2 parameter for the command                                */

void sendCommand(byte command){
  sendCommand(command, 0, 0);
}

void sendCommand(byte command, byte dat1, byte dat2){
  delay(20);
  Send_buf[0] = 0x7E;    //
  Send_buf[1] = 0xFF;    //
  Send_buf[2] = 0x06;    // Len
  Send_buf[3] = command; //
  Send_buf[4] = 0x01;    // 0x00 NO, 0x01 feedback
  Send_buf[5] = dat1;    // datah
  Send_buf[6] = dat2;    // datal
  Send_buf[7] = 0xEF;    //
  Serial.print("Sending: ");
  for (uint8_t i = 0; i < 8; i++)
  {
    mp3.write(Send_buf[i]) ;
    Serial.print(sbyte2hex(Send_buf[i]));
  }
  Serial.println();
}

/********************************************************************************/
/*Function: sbyte2hex. Returns a byte data in HEX format.                       */
/*Parameter:- uint8_t b. Byte to convert to HEX.                                */
/*Return: String                                                                */

String sbyte2hex(uint8_t b)
{
  String shex;

  shex = "0X";

  if (b < 16) shex += "0";
  shex += String(b, HEX);
  shex += " ";
  return shex;
}

/********************************************************************************/
/*Function: shex2int. Returns a int from an HEX string.                         */
/*Parameter: s. char *s to convert to HEX.                                      */
/*Parameter: n. char *s' length.                                                */
/*Return: int                                                                   */

int shex2int(char *s, int n){
  int r = 0;
  for (int i=0; i<n; i++){
     if(s[i]>='0' && s[i]<='9'){
      r *= 16; 
      r +=s[i]-'0';
     }else if(s[i]>='A' && s[i]<='F'){
      r *= 16;
      r += (s[i] - 'A') + 10;
     }
  }
  return r;
}

I can only imagine that the problem lies in the way I send Arduino to sleep when playing music (i.e. the way I call goToSleep(); from the wakeUpPlay()-class). Waking up from sleep does work when sleep was induced from the main loop (apparently). Do you have any idea what is wrong? (I thought about reducing my code to the bare minimum so it is less confusing for you - but then I'd probably change too much to find the bug).

2
  • reducing the code to bare minimum is the way to go for finding the bug ...... just comment out sections of code
    – jsotola
    Commented Oct 4, 2018 at 17:18
  • True - I did reduce it but couldn't find the problem. But for asking the questioned I figured it would be better to present the code in its entirety.
    – fertchen
    Commented Oct 5, 2018 at 7:02

1 Answer 1

3

All your music playing code is inside a interrupt. While an interrupt is running all other interrupts are disabled. It's advised to keeps interrupt routines as short as possible.

You could have the interrupt routine set a boolean variable. Then in the loop function read that variable and call the play-music function.

volatile boolean startPlayback = false;

...

void wakeUpPlay(){
  startPlayback = true;
  sleep_disable();
  detachInterrupt(digitalPinToInterrupt(playPin));
  detachInterrupt(digitalPinToInterrupt(rtcIntPin));
}

...

void loop() {
  goToSleep();
  if( startPlayback )
  {
    startPlayback = false;
    playMusic();
  }
}

Do something similar with wakeAlarm.

1
  • Thanks. I did wonder where the program ends up once it got inside the wakeUpPlay routine - or where it would go afterwards. But I did not take the step back necessary to get a look at where it "came from" and so I just did not realize it was essentially still inside the interrupt.
    – fertchen
    Commented Oct 5, 2018 at 10:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.