Slumber

Made by Ammani Nair

Found in Advanced IOT 2017

  • Slumber Device

An ambient device thats sits on your night stand and creates a soothing environment to help you fall asleep as soon as you hit the bed

0

An ambient device that sits on your nightstand and creates a soothing environment to help you fall asleep as soon as you hit the bed. 60 million people in the United States suffer from insomnia and have difficulty falling asleep, including myself. Most of us search for tips and tricks which will alleviate the anxiety. I came up with a nighttime routine which worked but was lengthy and required effort. The routine involves deep breathing exercises, listening to music and drinking chamomile tea (I find the aroma extremely soothing).

On most days, I come back home quite late and don’t have the energy or time to go through the routine and end up sleeping poorly. Slumber was the solution to my night anxiety problem, with Pillo as it's sidekick!  

Slumber and Pillo are 2 halves of an IoT ecosystem. Pillo acts as a sensor in your pillow or under your mattress which tracks your movement and relays information to Slumber about whether you are awake or asleep.

1

Product In Action

The following is a video of how the product will be used.

0
Slumber Device
glitch0it - https://youtu.be/pe8yAnHFHJg
0

Functional Specification

Pillo

  • Senses if the user is moving.
  • Sends a single to the Slumber device when it senses the person is awake.

Slumber

  • Emits soothing light to indicate if it is switched on.
  • Sprays a mist of a therapeutic aroma to calm the user.
  • Plays a selection of meditation or calming of music.
  • Responds to a wave gesture to activate or deactivate sleep routine actuation.
0

Priority Matrix

The two devices had a variety of functions which needed to be accomplished. The tasks which were easy and high priority were accomplished first. 

0

Scenario

Lola gets back from a busy day of work, tired and drained. She's had a few nights of insomnia in a row and it's starting to have visible effects. She prepares a quick dinner for herself and skims through what's on her docket for the next day. She has an important meeting and needs her sleep but doesn't have the time or energy for her complete nighttime routine - which doesn't always work. She needs something, which at the touch of a button, will calm her and allow her to get a good night's sleep.

User Story

Main Flow

  1. Lola pushes a button and lies down in bed
  2. Lola waves her hand on top of slumber.
  3. Soothing light turns on.
  4. Calming music starts to play.
  5. Therapeutic aroma is sprayed above the bed and is sprayed every five minutes till the user falls asleep.
  6. Once the user has fallen asleep, the music softens and stops playing.
  7. The light ebbs away.

Alternate Flow

  1. The user starts to move a lot but is still half asleep.
  2. The device senses the movement and sprays the aroma.
  3. The pillow starts to vibrate (massage the head) lightly to put the user back to deep REM sleep.
0

Process and Design Considerations

Through this project, I wanted to gain a nuanced understanding of how to create a good user experience using various outputs. A few of the consideration while designing were:

  1. The colour of the light. Blue is considered the most soothing colour but when it comes to light, red is optimum because it has the shortest wavelength. The colour red is also synonymous with danger and could lead a person to get more anxious. Through continuous testing, I found the most calming and relaxing colour was similar to orange.
  2. The aroma should not be overpowering. The timing and strength of the aroma spray were important and is dependent on the room. The actuation of the aroma, though easy, was difficult to implement in the device. It needed to be leak proof, produce the optimum amount of aroma and be housed within the form.
  3. The form and fabrication of Slumber proved to be difficult. I wanted to Slumber to resemble a nightlight that would sit on my bedside table. I wanted it to be smooth and not rectilinear. The form needed to be 3D printed, and the housing for each of various internal parts had to be adjusted to the curves in the form. 
  4. The data collected from the Pillo device needed to be sieved to be usable. The initial data had minute variations which needed to be smoothened out. The code for the Pillo device was tested multiple times to understand the optimum thresholds in the sleep pattern. 
0

Bill Of Materials

The following table is a list of the materials used in the project. Find a detailed list here

0

Pillo Device

This device will reside in a tiny box inside the pillow and use an accelerometer to track my movement on the bed. As I lie down, Pillo will sense my movements and actuate the Slumber device. Once I'm asleep and stop moving, Pillo will tell Slumber to turn off the various outputs. Through the night, Pillo is in listening mode and will continue to track my movements. If my movement passes a threshold, it will signal Slumber to start a subtle version of the various outputs.

0

Circuit Diagram

0

Legend

  1. Accelerometer
  2. Particle Photon
  3. Portable Power Supply 
0

Code

0
/*
Project: Pillo
Discription: This device will create a soothing environment to help those that suffer from insomnia and night anxiety, to fall asleep.
Author: Ammani
Date: 5/12/2017
Version: 2.0

Sourced from: //https://anasdalintakam.blogspot.in/
ADXL335
note:vcc-->5v ,but ADXL335 Vs is 3.3V
The circuit:
      5V: VCC
analog 1: x-axis
analog 2: y-axis
analog 3: z-axis
*/

//------ VARIABLES FOR THE INPUTS ------
const int xpin = A3;                  // x-axis of the accelerometer
const int ypin = A2;                  // y-axis
const int zpin = A1;                  // z-axis (only on 3-axis models)
const int numReadings = 10;

//------ VARIABLES FOR THE X-AXIS ------
int xreadings[numReadings];      // the readings from the analog input
int xreadIndex = 0;              // the index of the current reading
int xtotal = 0;                  // the running total
int xaverage = 0;                // the average
int readingX = 0;
int prevreadingX = 0;
int changeX = 0;

//------ VARIABLES FOR THE Y-AXIS ------
int yreadings[numReadings];      // the readings from the analog input
int yreadIndex = 0;              // the index of the current reading
int ytotal = 0;                  // the running total
int yaverage = 0;                // the average
int readingY = 0;
int prevreadingY = 0;
int changeY = 0;

//------ VARIABLES FOR THE Z-AXIS ------
int zreadings[numReadings];      // the readings from the analog input
int zreadIndex = 0;              // the index of the current reading
int ztotal = 0;                  // the running total
int zaverage = 0;                // the average
int readingZ = 0;
int prevreadingZ = 0;
int changeZ = 0;

//this code is for sensing ambient light using sparkfuns temt6000 ambient light sensor and arduino
int timeElapsed = -1;

void setup()
{
 // initialize the serial communications:
 Serial.begin(9600);
 for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    xreadings[thisReading] = 0;
  }
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
     yreadings[thisReading] = 0;
  }
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
     zreadings[thisReading] = 0;
   }

}
void loop()
{
  int x = analogRead(xpin);  //read from xpin

  int y = analogRead(ypin);  //read from ypin

  int z = analogRead(zpin);  //read from zpin

  float zero_G = 512.0; //ADC is 0~1023  the zero g output equal to Vs/2
                        //ADXL335 power supply by Vs 3.3V
  float scale = 102.3;  //ADXL335330 Sensitivity is 330mv/g
                         //330 * 1024/3.3/1000
 prevreadingX = readingX;
 prevreadingY = readingY;
 prevreadingZ = readingZ;
 readingX = ((float)x - 331.5)/65*9.8;
 readingY = ((float)y - 329.5)/68.5*9.8;
 readingZ = ((float)z - 340)/68*9.8;

 Serial.print( Time.timeStr());
 Serial.print(",");
 Serial.print(abs(xaverage));  //print x value on serial monitor
 Serial.print(",");
 Serial.print(abs(yaverage));  //print y value on serial monitor
 Serial.print(",");
 Serial.print(abs(zaverage));  //print z value on serial monitor
 Serial.println("");

 changeX = readingX - prevreadingX;
 changeY = readingY - prevreadingY;
 changeZ = readingZ - prevreadingZ;
 //light = light_value * 0.0976;// percentage calculation
 // x readings
 xtotal = xtotal - xreadings[xreadIndex]; // subtract the last reading:
 xreadings[xreadIndex] = changeX; // read from the sensor:
 xtotal = xtotal + xreadings[xreadIndex]; // add the reading to the total:
 xreadIndex = xreadIndex + 1; // advance to the next position in the array:

 if (xreadIndex >= numReadings) {  // if we're at the end of the array...
  xreadIndex = 0; // ...wrap around to the beginning:
 }

 xaverage = xtotal / numReadings; // calculate the average:

 // y readings
 ytotal = ytotal - yreadings[yreadIndex]; // subtract the last reading:
 yreadings[yreadIndex] = changeY; // read from the sensor:
 ytotal = ytotal + yreadings[yreadIndex]; // add the reading to the total:
 yreadIndex = yreadIndex + 1; // advance to the next position in the array:

 if (yreadIndex >= numReadings) {  // if we're at the end of the array...
  yreadIndex = 0; // ...wrap around to the beginning:
 }

 yaverage = ytotal / numReadings; // calculate the average:

 // z readings
 ztotal = ztotal - zreadings[zreadIndex]; // subtract the last reading:
 zreadings[zreadIndex] = changeZ; // read from the sensor:
 ztotal = ztotal + zreadings[zreadIndex]; // add the reading to the total:
 zreadIndex = zreadIndex + 1; // advance to the next position in the array:

 if (zreadIndex >= numReadings) {  // if we're at the end of the array...
  zreadIndex = 0; // ...wrap around to the beginning:
 }

 zaverage = ztotal / numReadings; // calculate the average:
 // Checks to see if the person is moving a lot and publishes an event
 if (timeElapsed + 10000 < millis() )
  {
    if (abs(xaverage) > 5 or abs(yaverage) > 5 or abs(zaverage) > 5)
      {
        Particle.publish( "ammani/movement", "yes");
        Serial.println("publishing");
        delay(5000);
        timeElapsed = millis();
      }
  }
 delay(100);

}
Click to Expand
0

Slumber Device

When activated, Slumber will play calming music, simulate a breathing exercise with a light pattern and fill the room with a soothing chamomile aroma. Slumber contains an on/off switch and is primarily activated by the Pillo. There is also a secondary sensor on the device itself, the PIR sensor, which will allow me to activate or deactivate the device by choice. By simply waving my hand over the top of the device, Slumber will get activated if it's in listening mode and vice-versa.

0

Circuit Diagram

0

Legend

  1. 5v Power Supply
  2. Speaker
  3. Button
  4. DF Player (MP3 Player)
  5. Particle Photon
  6. LED Strip
  7. Water Atomization (Humidifier)
  8. PIR Sensor
0

Code

The led strip requires the neopixel library which can be found here. The neopixel.cpp and neopixel.h files need to be in the same folder as the main code file. 

0
/*
Project: Slumber
Discription: This device will create a soothing environment to help those that suffer from insomnia and night anxiety, to fall asleep.
Author: Ammani
Date: 5/12/2017
Version: 2.0
*/
//------DEFINITIONS------
#include "neopixel.h"
#define PIXEL_PIN D0
#define PIXEL_COUNT 30
#define PIXEL_TYPE SK6812RGBW
// Currently, I think this is what is sent to the serial as output verification data. Haven't deleted it because I'm sure
// To understand more about this, look at DFplayer_basic_sample.ino in the audio folder
Adafruit_NeoPixel ring = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
// These are used by the DF Player
# define Start_Byte 0x7E
# define Version_Byte 0xFF
# define Command_Length 0x06
# define End_Byte 0xEF
# define Acknowledge 0x00 //Returns info with command 0x41 [0x01: info, 0x00: no info]
// These definitions are the various states the device will run through
# define STATE_OFF 0
# define STATE_WELCOME 1 // When you switch on device
# define STATE_LISTENING 2 // When the device is monitering your movement and hand gesture
# define STATE_PRIMARY 3 // The state in which the device is actuating the various outputs for the first time or when it's consciously activated by the user
# define STATE_SECONDARY 4 //  When the device detects you might be waking up and responds minimally to put you back to sleep

//------- VARIABLES, INPUTS & OUTPUTS -------
int onSwitch = A0; // Input for the button
int aromapin = D1; // Output to diffuser
int Command; // Input for the DF Player
int Parameter1; // Input for the DF Player
int Parameter2; // Input for the DF Player
int state = 0; //
int pirState = D5;
int pirSense = 0;
int head  = 0;
int tail = -10; // Index of first 'on' and 'off' pixels

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600); // Input for the DF Player
  pinMode(aromapin, OUTPUT);
  pinMode(onSwitch, INPUT);
  Particle.subscribe( "ammani/movement" , callingMovement );
  pinMode(pirState, INPUT);
  ring.begin();
  ring.show();
  Particle.variable("pir", &pirSense, INT);
  delay(30);
  execute_CMD(0x3F, 0, 0);     //Initialises the MP3 player
  execute_CMD(0x06, 0, 30);   // Initialize sound to very low volume. Adapt according used speaker and wanted volume
  execute_CMD(0x16, 0 , 1);
}

void loop()
{
  // This command switches between all the states of the device
  switch( state )
  { case STATE_OFF:
      doOffState();
      break;
    case STATE_WELCOME:
      Serial.println("Welcome State");
      doWelcomeState();
      break;
    case STATE_LISTENING:
    //Serial.println("Listening State");
      doListeningState();
      break;
    case STATE_PRIMARY:
    //  Serial.println("Primary State");
      doPrimaryState();
      break;
    case STATE_SECONDARY:
      Serial.println("Secondary State");
      doSecondaryState();
      break;
  }
}

// ------ VARIABLES USED FOR TIMING AND TO INITIATE STATES ------
long lastDebounceTime = 0;
long debounceDelay = 5000;
long lastMovementCalled = 0;
bool primaryStateOn = FALSE;
bool movement = FALSE;
long enteredWelcomeStateAt = -1;
bool secondaryStateOn = FALSE;
bool isPrimaryStatePlaying = false;
bool aromaOn = false;
long startedPlayingAt = -1;
int primaryStateDuration = 1000 * 30;
int aromaStartTime = -1;
int aromaTime = 1000 * 10;
bool isSecondaryStatePlaying = false;
long startedSecondaryPlayingAt = -1;
int secondaryStateDuration = 1000 * 30;

// ------ FUNCTIONS FOR ALL THE STATES ------
void doOffState(){
    off();
    execute_CMD(0x16, 0 ,1);
    if (analogRead(onSwitch) < 100){
    state = STATE_WELCOME;
    }
}

void doWelcomeState()
{
  if( enteredWelcomeStateAt == -1 ) enteredWelcomeStateAt = millis();
  star();
  if (analogRead(onSwitch) > 100){
    off();
    enteredWelcomeStateAt = -1;
    lastDebounceTime = millis();
    head  = 0;
    tail = -10;
    state  = STATE_OFF;
    }
  else if( enteredWelcomeStateAt + 8000 < millis() )
  {
    lastDebounceTime = millis();
    enteredWelcomeStateAt = -1;
    head  = 0;
    tail = -10;
    changeColor(ring.Color(10,160,0,0));
    Serial.println("listeningstate");
    state = STATE_LISTENING;
  }
}

void doListeningState()
{
  pirSense = digitalRead(pirState);
  if (analogRead(onSwitch) > 100){
    off();
    state  = STATE_OFF;
    }
  else if (movement == TRUE )
    {
      movement == FALSE;
      secondaryStateOn = TRUE;
      state = STATE_SECONDARY;
    }
  else if ( lastDebounceTime + debounceDelay < millis())
    {if (pirSense == 1)
    {
      lastDebounceTime = millis();
      primaryStateOn = TRUE;
      state = STATE_PRIMARY;
    }
    }

}

void doPrimaryState(){
  pirSense = digitalRead(pirState);
  Serial.println("primarystate");
  Serial.println(millis()-aromaStartTime);
  yellowcolor();
  if (primaryStateOn == TRUE and isPrimaryStatePlaying == FALSE)
    {
      Serial.println("2nd loop");
      startedPlayingAt = millis();
      aromaStartTime = millis();
      aromaOn = true;
      execute_CMD(0x01,0,1);   //Plays the next track in SD card once timer is started
      digitalWrite(aromapin,HIGH);
      isPrimaryStatePlaying = true;
      Serial.println("aroma on");
    }
  if (aromaOn)
    {
      if (aromaStartTime + aromaTime < millis())
      {
        digitalWrite(aromapin, LOW);
        aromaOn = false;
        Serial.println("aroma off");
      }
    }
  else
    if ( aromaStartTime + 20000 < millis())
    {
      digitalWrite(aromapin,HIGH);
      Serial.println("aroma on");
      aromaStartTime = millis();
    }
  if( isPrimaryStatePlaying && startedPlayingAt + primaryStateDuration < millis() )
    {
      primaryStateOn = FALSE;
      isPrimaryStatePlaying = FALSE;
      changeColor(ring.Color(10,160,0,0));
      Serial.println("3rdloop");
      digitalWrite(aromapin, LOW);
      Serial.println("aroma off");
      Serial.println("listeningstate");
      aromaStartTime = -1;
      startedPlayingAt = -1;
      state = STATE_LISTENING;
    }
  else
    {
      //return; //
    }

  if (analogRead(onSwitch) > 100){
      off();
      state  = STATE_OFF;
      }
  else if (lastDebounceTime + debounceDelay < millis())
  {
    if (pirSense == 1)
    {
      Serial.println("The Pir break");
      lastDebounceTime = millis();
      execute_CMD(0x16,0,1);
      primaryStateOn = FALSE;
      isPrimaryStatePlaying = FALSE;
      digitalWrite(aromapin, LOW);
      Serial.println("aroma off");
      changeColor(ring.Color(10,160,0,0));
      state = STATE_LISTENING;

    }
  }

}

void doSecondaryState(){
  pirSense = digitalRead(pirState);
  Serial.println("secondary state");
  Serial.println(millis()-aromaStartTime);
  orangecolor();
  if (secondaryStateOn == TRUE and isSecondaryStatePlaying == FALSE)
    {
      Serial.println("2nd loop");
      startedSecondaryPlayingAt = millis();
      aromaStartTime = millis();
      aromaOn = true;
      execute_CMD(0x01,0,1);
      digitalWrite(aromapin,HIGH);
      isSecondaryStatePlaying = true;
      Serial.println("aroma on");
    }
  if (aromaOn)
    {
      if (aromaStartTime + aromaTime < millis())
      {
        digitalWrite(aromapin, LOW);
        aromaOn = false;
        Serial.println("aroma off");
      }
    }
  else
    if ( aromaStartTime + 20000 < millis())
    {
      digitalWrite(aromapin,HIGH);
      Serial.println("aroma on");
      aromaStartTime = millis();
    }
  if( isSecondaryStatePlaying && startedSecondaryPlayingAt + secondaryStateDuration < millis() )
    {
      secondaryStateOn = FALSE;
      isSecondaryStatePlaying = FALSE;
      changeColor(ring.Color(10,160,0,0));
      Serial.println("3rdloop");
      digitalWrite(aromapin, LOW);
      Serial.println("aroma off");
      Serial.println("listeningstate");
      aromaStartTime = -1;
      startedSecondaryPlayingAt = -1;
      state = STATE_LISTENING;
    }
  else
    {
      //return; //
    }

  if (analogRead(onSwitch) > 100){
      off();
      state  = STATE_OFF;
      }
  else if (lastDebounceTime + debounceDelay < millis())
  {
    if (pirSense == 1)
    {
      Serial.println("The Pir break");
      lastDebounceTime = millis();
      execute_CMD(0x16,0,1);
      secondaryStateOn = FALSE;
      isSecondaryStatePlaying = FALSE;
      digitalWrite(aromapin, LOW);
      Serial.println("aroma off");
      changeColor(ring.Color(10,160,0,0));
      state = STATE_LISTENING;

    }
  }

}

// ------ FUNCTION FOR THE DF PLAYER ------
void execute_CMD(byte CMD, byte Par1, byte Par2) // Excecute the command and parameters
{
 // Calculate the checksum (2 bytes)
 int16_t checksum = -(Version_Byte + Command_Length + CMD + Acknowledge + Par1 + Par2);

 // Build the command line
 byte Command_line[10] = { Start_Byte, Version_Byte, Command_Length, CMD, Acknowledge, Par1, Par2, checksum >> 8, checksum & 0xFF, End_Byte};

 //Send the command line to the module
 for (byte k=0; k<10; k++)
 {
  Serial1.write( Command_line[k]);
 }
}

// ------ FUNCTIONS FOR THE LED STRIP ------

uint32_t color = 0x00ff08;      // 'On' color (starts red)

void star(){
  ring.setPixelColor(head, color); // 'On' pixel at head
  ring.setPixelColor(tail, 0);     // 'Off' pixel at tail
  ring.show();                     // Refresh strip
  delay(5);                        // Pause 20 milliseconds (~50 FPS)

  if(++head >= ring.numPixels()) {         // Increment head index.  Off end of strip?
    head = 0;                       //  Yes, reset head index to start
    /*if((color >>= 8) == 0)          //  Next color (R->G->B) ... past blue now?
      color = 0xFF0000;             //   Yes, reset to red*/
  }
  if(++tail >= ring.numPixels()) tail = 0; // Increment, reset tail index
}

void yellowcolor(){
  for(int i = 0; i <= 10; i += 5) {
    changeColor(ring.Color(i,i*13,0,0));
    delay(50);
  }
  delay(500);
  for(int i = 10; i >= 0; i -= 5) {
    changeColor(ring.Color(i,i*13,0,0));
    delay(50);
  }
}

void orangecolor(){
  for(int i = 0; i <= 10; i += 5) {
    changeColor(ring.Color(i,i*10,0,0));
    delay(50);
  }
  delay(500);
  for(int i = 10; i >= 0; i -= 5) {
    changeColor(ring.Color(i,i*10,0,0));
    delay(50);
  }
}

void off() {
  changeColor(ring.Color(0, 0, 0, 0));
}

void changeColor(uint32_t color) {
  for(uint16_t i=0; i < ring.numPixels(); i++) {
    ring.setPixelColor(i, color);
    ring.show();
  }
}

// ------ FUNCTION TO RESPOND TO THE PILLOW SENSOR ------
void callingMovement(const char *event, const char *data)
{
  Serial.println("Reached Calling Movement Handler");
  if (data == "yes"){
  moving();
  }
}

void moving()
{
  if ( lastMovementCalled + 20000 < millis() )
  {
  lastMovementCalled = millis();
  movement = TRUE;
  }
}
Click to Expand
0

Final Presentation

At the final showcase, I illustrated the various states of the Slumber, the connection between Pillo and Slumber and the gesture activation. The device was well received and our visitors below the age of 5 were enamoured by it!

0

Next Steps

The primary purpose of this eco-system is to track one's sleep and simulate a soothing environment. In the future, the device could also incorporate an alarm system. The alarm could be designed in such a way to calmly wake me up by slowly increasing the intensity of the light and playing music. 

x
Share this Project


About

An ambient device thats sits on your night stand and creates a soothing environment to help you fall asleep as soon as you hit the bed

Created

May 14th, 2017