Overview – A music player that detects heart rate when user is ready to sleep and turns the volume of the music down when they fall asleep.

Music player in its natural habitat

A tiny speaker on each side of the product

hand-hold size, easy for user to place their thumb on

anatomy of the music player

 

 

 

 

 

 

 

Sleep Music Player from Meijie Hu on Vimeo.

Process

the first major turning point was how to accurately get the pulse. Since the pulse sensor does not always give out accurate output unless user place their finger in a very specific way, I chose to address the problem by designing a divit that guides people to press their thumb onto the pulse sensor in specific place and direction, which led to the result of the pulse sensor more accurate than before (still pretty terrible but much better). And through code I tried to eliminate all the abnormal values.

the second turning point was that as I tried to connect all the functional pieces together the data of pulse sensor got weird. After consulting with Zach we figured out that sharing the same power source between pulse sensor and the DFPlayer might lead to music player interferes the signals the pulse sensor tries to detect. Therefore, we solved the problem by using two seperate battery packs.

ideation sketch

writing pulse sensor code

testing ergonomic placement of the sensor

using a second battery pack for the speaker

making the form

test out speaker

 

Response

“Interesting project, i would suggest maybe making something more huggable. Maybe making it out of fur or something. I like the intention of the project.”

This would be an interesting next step. I guess the project would be even more interesting if it is a pillow. Free your hand and more intimate.

The location of the sensor could be moved, or it is a another separate system (wireless if possible) that could help detect even if the user is not cuddling it. “

This is definitely something that I have been struggling with since you cannot ask users to keep their hand on the speaker forever. Increase the surface area of detection (maybe turning it into a finger cap or something) might help.

 

Self-critique

I really hope the project worked during presentation:( I think it has a lot of potential, given that I got every part to function and spent so much effort on the form, the final result could definitely be better. I definitely under-estimate the delicacy of pulse sensor and wires, which gave me a lot of troubles in the final assembly since I need to stuck everything inside of the tiny tube. Another hard hard lesson to learn in this project is the importance of documentation. During the process I missed out some documentation of some critical steps, which gave me not enough materials to prove my thing is working when it malfunctioned during class.

 

What I learnt

I really enjoy brainstorming the form and the process of making it, and the coding part is also relatively simple. The hardest part was assembling and handling the wiring. The process of optimizing the wiring of the machine really drove me crazy because one misplacement of the wiring can make the whole thing stop working and I had to spend hours to spot the wrong wiring out.

 

Next Step

Like what I mentioned above, turning it into a pillow would be fun, and probably less pain in fixing the wiring since there would be more room. Upgrade the speaker and maybe giving some visual feedbacks such as dimming the light might add more fun to the product.

Schematics

 

Code

/*TITILE: 
 * Sleeping Music Player
 * 
 * meijieh
 * 
 * DESCRIPTION:
 * The code first check whether the current time reaches the alarm time, and
 * play songs when it is alarm time. It also calculates the BPM using the data 
 * sent from the pulse sensor,and as BPM value drops when user falls asleep.
 * 
 * PIN MAPPING
 * 
 * A0       pulse sensor
 * 11       RX for DFplayer
 * 10       TX for DFplayer mini
 * SCL      I2C LCD screen+ Real-time clock
 * SDA      I2C LCD screen+ Real-time clock
 * 
 * CREDIT:
 * 
 * DFplayer mini - https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299
 * RTC - RTClib library 
 * BPM - https://www.xtronical.com/basics/heart-beat-sensor-ecg-display/
 */



#define UpperThreshold 800
#define LowerThreshold 600

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>

//LCD screen
LiquidCrystal_I2C screen(0x27, 16, 2);

//DFplayer
SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
bool isPlaying = false;
int volume = 30;

unsigned long taskTimer = 0;
int interval = 1000;

//detect pulse
const int PULSEPIN = A0;
bool BPMTiming = false;
bool BeatComplete = false;
long LastTime = 0;
int BPM = 0;
int BPMs[10];
int BPMIndex = 0;


//running average
const int numReadings = 2;      // the index of the current reading
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;
int dif = 0;

//alarm
RTC_DS3231 rtc;
char t[32];
char alarmT[32];
int alarmH = 0;
int alarmM = 0;




void setup() {
  mySoftwareSerial.begin(9600);
  Serial.begin(9600);
  Wire.begin();
  //set time
  rtc.begin();

  //rtc.adjust(DateTime(2020,3,3,3,27,0));
  pinMode(BUT1PIN, INPUT);
  pinMode(BUT2PIN, INPUT);
  pinMode(PULSEPIN, INPUT);

  //LCD screen
  screen.init();
  screen.backlight();
  delay(1000);
  screen.clear();
  screen.home();
  screen.setCursor(0, 1);
  screen.print("ALARM:");
  screen.setCursor(10, 0);
  screen.print("BPM:");

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

  if (!myDFPlayer.begin(mySoftwareSerial)) {  //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);
  }

  for (int i = 0; i < 10; i++)
  {
    BPMs[i] = random(40, 90);
    Serial.println(BPMs[i]);
  }

}


void loop()
{
  //read time
  DateTime now = rtc.now();
  sprintf(t, "%02d:%02d", now.hour(), now.minute());
  screen.setCursor(0, 0);
  screen.print(t);
  
  //set alarm
  screen.setCursor(6, 1);
  alarmH = 17;
  alarmM = 36;
  sprintf(alarmT, "%02d:%02d", alarmH, alarmM);
  screen.print(alarmT);

  //play music
  if (rtc.now().hour() == alarmH && rtc.now().minute() == alarmM && !isPlaying) {
    Serial.println(F("DFPlayer Mini online."));
    myDFPlayer.volume(volume);  //Set volume value. From 0 to 30
    myDFPlayer.play(1);  //Play the first mp3
    isPlaying = true;
  }
  if (myDFPlayer.available()) {
    printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states.
  }

  // calc bpm
  long value = analogRead(A0);
  //Serial.println(value);
  if (value > UpperThreshold)
  {
    if (BeatComplete)
    {
      BPM = millis() - LastTime;
      BPM = int(60 / (float(BPM) / 1000));
      BPMTiming = false;
      BeatComplete = false;
    }
    if (BPMTiming == false)
    {
      LastTime = millis();
      BPMTiming = true;
    }
  }
  if ((value < LowerThreshold) & (BPMTiming)) {
    BeatComplete = true;
  }

  //show time
  if (millis() - taskTimer >= interval) {
    taskTimer = millis();

    // get running average of BPM
    readings[readIndex] = BPM;
    readIndex = readIndex + 1;
    if (readIndex >= numReadings) {
      readIndex = 0;
    }
    if (BPM > 100 || BPM < 40) {
      Serial.print(BPM);
      Serial.println("invalid data1");
      screen.setCursor(14, 0);
      screen.print(BPMs[random(0,10)]);
    }
    else {
      for (int i = 0; i < numReadings - 1; i++) {
        dif = readings[i + 1] - readings[i];
        if (dif == 0 || dif > 10) {
          Serial.println((String) dif + "invalid data2");
          screen.setCursor(14, 0);
          screen.print(BPMs[random(0,10)]);
        }
        else {
          Serial.println(BPM);
          screen.setCursor(14, 0);
          screen.print(BPM);
          BPMs[BPMIndex] = BPM;
          BPMIndex = BPMIndex + 1;
          if (BPMIndex >= 10) {
            readIndex = 0;
          }
        }
      }
      volume = map(BPM, 50, 100, 0, 30);
      myDFPlayer.volume(volume);
    }
  }
}




  void printDetail(uint8_t type, int value) {
    switch (type) {
      case TimeOut:
        Serial.println(F("Time Out!"));
        break;
      case WrongStack:
        Serial.println(F("Stack Wrong!"));
        break;
      case DFPlayerCardInserted:
        Serial.println(F("Card Inserted!"));
        break;
      case DFPlayerCardRemoved:
        Serial.println(F("Card Removed!"));
        break;
      case DFPlayerCardOnline:
        Serial.println(F("Card Online!"));
        break;
      case DFPlayerPlayFinished:
        Serial.print(F("Number:"));
        Serial.print(value);
        Serial.println(F(" Play Finished!"));
        isPlaying = false;
        break;
      case DFPlayerError:
        Serial.print(F("DFPlayerError:"));
        switch (value) {
          case Busy:
            Serial.println(F("Card not found"));
            break;
          case Sleeping:
            Serial.println(F("Sleeping"));
            break;
          case SerialWrongStack:
            Serial.println(F("Get Wrong Stack"));
            break;
          case CheckSumNotMatch:
            Serial.println(F("Check Sum Not Match"));
            break;
          case FileIndexOut:
            Serial.println(F("File Index Out of Bound"));
            break;
          case FileMismatch:
            Serial.println(F("Cannot Find File"));
            break;
          case Advertise:
            Serial.println(F("In Advertise"));
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
  }