Smart Sleeping Device
When it comes to sleep, there is one device staring above all - the alarm clock, but why stop here? We do so much things during sleep, like dreaming, snoring, gather energy... There is a lot of room for more technology to bring in, and thats what we are going to do in this project. How? with the Adafruit Circuit Playground Express. We are going to build a device for the following tasks:
1. Lucid Dreams helper device
2. NBA middle of the night smart alarm clock
3. Snoring monitoring device
Using Integromat and Blynk
Full code can be found in this github repo: https://github.com/omerdrukman/SmartSleepingDevice
Supplies
Circuit Playground Express board with ESP8266 chip
Sleeping eye cover
Lucid Dreamer Explained
If you still don't know what lucid dream is, go and read about it!
in general, a lucid dream is a type of dream where the dreamer becomes aware that they are dreaming. There are a lot of techniques to become aware of the dream, and one of them can be with the help of a small device, recognising that the person is dreaming, buy inspecting eye movement, and start flashing lights to the user closed eyes, in order to "tell" him it is only a dream, and in the same time not wake him up.
We will do it by using two of the circuit sensors:
1. IR sensor - to identify eye movement
2. Accelerometer sensor - to identify head movement
We will take advantage of the close IR sensor and leds of the circuit, and apply the circuit on the eye cover, so we will be able to put it on when we are going to sleep.
We want to "catch" the moment, where the eyes move, but not the whole device, to avoid flashing lights when the user just moving around.
Accelerometer Movement
In this step, we'll write the function that calculate the movement of the device. We'll do it in the following steps:
1. Calculate current distance vector
2. Wait a few milliseconds
3. Calculate again the current distance vector
4. Compare it to previous vector with some threshold
X = CircuitPlayground.motionX(); Y = CircuitPlayground.motionY(); Z = CircuitPlayground.motionZ(); double newVector = X*X; newVector += Y*Y; newVector += Z*Z; newVector = sqrt(newVector); // Are we moving? if (abs(10*newVector - 10*storedVector) > MOVE_THRESHOLD) { is_moving = true; } else { is_moving = false; }
IR Sensor Proximity
This is more tricky. We need to be very sensitive for the eye movement during a dream. This might take a few experiments to find the right threshold value. We need to take a lot of params into account, the sensitivity of the sensor, the jumps it makes, and the correct way to normalise the values. After a few experiments of my own I found this way to be working the best:
1. We will sum the difference between the last value of the sensor and the current one, for 3 seconds
2. Every 3 seconds we will calculate the "difference between the differences", of the previous 3 seconds and the current one. This way we can ignore the sensitivity of the sensor
3. When we identify large jump (over a pre defined threshold, in this case 30), this means there is an eye movement
4. To maintain the dream state, we decide a threshold of 7 examine points, in which there is no movement, to get out of dreaming state.
difference += abs(prox.lastDist() - last_dist); if (millis() - last_prox > 3000) { if (last_difference == 0) { last_difference = difference; } else if (difference > last_difference + 30) { is_eye_moving = true; count_shifts = 0; } else { count_shifts++; if (count_shifts > 7) { is_eye_moving = false; } }
Bring It All Togather
Now we only need to light the led, when we identify eye movement, but not device movement:
if (!is_moving && is_eye_moving) { CircuitPlayground.setPixelColor(0, 0xFF0000); } else { CircuitPlayground.clearPixels(); }
Now we can take the circuit and attach it to the eye cover... good night!
NBA Smat Alarmer
If you are not from the US you must have notice a big problem in your life - middle of the night NBA games. As a basketball fan I find it very hard to watch NBA games as it destroy my sleep and the day after the game. Still, I won't give that up, so the least I can do is to be waking only to closed matches games, but how can I know the live score if I'm sleeping? Thats where the Circuit Playground express comes in.
In these steps we'll create a telegram bot, which help us define a game alert. Afterwards, the circuit will question the NBA games api (through RapidAPI) for the live score, when the difference of the score is lower than a threshold, the circuit will start the alarm clock, and we will wake up for the game. All the communications to the internet and the Telegram bot will be happening through Integromat and Blynk.
Create Telegram Bot
How do we create a Telegram bot? using another bot of course!
1. In telegram search for @BotFather
2. Start a conversation and type in "/start"
3. Then type in "/newbot"
4. Give the new bot a name
5. In this point you will be given the bot token - we will use it later
Send Messages From the Circuit to the Bot
To send messages we will use Integromat webhook, that will be triggered through Blynk.
Before creating the webhook, we need to get the Telegram chat id of the bot. We can do it using Telegram API and request the url: https://api.telegram.org/botTOKEN/getUpdates
Then, in Integromat create a new scenario and create a webhook. Take the given webhook url and paste it in Blynk webhook: https://hook.integromat.com/<TOKEN>?message=/pin/
That way a virtual pin in the circuit can trigger this webhook, activating send message to the Telegram bot.
Blynk.virtualWrite(V11, "Type message here.");
In integromat connect the webhook to the telegram module "send messages", and paste the chat id and create the connection with the bot token.
Receive Messages From the Bot to the Circuit
To receive messages, we'll do the same but a bit different. We'll create a new scenario and this time we'll start by Telegram module and choose "watch for updates", we will type our bot token, and this will cause the module to be triggered when a message is sent. We'll connect it to HTTP module to be able to pass the message through Blynk to the device:
http://BLYNK_HOST_IP/BLYNK_TOKEN/update/V10?value= {{1.message.text}}
BLYNK_WRITE(V10) { // Get message param int pinValue = param.asInt(); nbaValue = pinValue; }
Setting Up the State Machine Flow
For the whole setup, we'll use the finite state machine library to handle the process. We'll start by asking the game id, and then the time of the game. The score difference threshold will be defined through Blynk slider.
State nbaWaitForGame = State(onNBASaveGameWaitInit, onNBASaveGameWaitUpdate, NULL); State nbaGetGameId = State(onNBASaveGameIdInit, onNBASaveGameIdUpdate, NULL); State nbaGetGameTime= State(onNBASaveGameTimeInit, onNBASaveGameTimeUpdate, NULL); State nbaQueringGame = State(onNBASaveGameQueringInit, onNBAGameQuering, NULL); State nbaAlarm = State(onNBASaveGameAlarmInit, onNBAAlarm, NULL); FSM nbaGamesMachine = FSM(nbaWaitForGame);
void onNBASaveGameWaitUpdate() { if (nbaValue >= 0) { Blynk.virtualWrite(V11, "What is the game id?"); nbaGamesMachine.transitionTo(nbaGetGameId); } }
Quering the Game Score
This part is the most complicated. After saving the game id the game time, we'll start quering the score in the set time for a few intervals. We'll do it in the following steps:
1. Create an account in the RapidAPI, for the NBA Api, this way we will be able to determine the game score.
2. Use Integromat to make the request, we can use the same webhook we've created, and use router and filter to pass on the message, and make the request. We will send the game id.
3. Inegromat will extract the game id and make an https request to the api. The api will send the score.
4. Create a Text Parser module and Math module in Integromat to extract the score and calculate the difference.
5. Send the result to the circuit through Blynk webhook.
6. If the score difference is smaller than the threshold defined by the Blynk slider, activate the alarm clock and wake the user!
void onNBAGameQuering() { if (millis() - last_game_check > CHECK_INTERVAL) { String message = "gameId:" + gameId; Blynk.virtualWrite(V11, message); last_game_check = millis(); } if (nbaValue >= 0) { if (nbaValue <= game_diff_threshold) { nbaGamesMachine.transitionTo(nbaAlarm); } else if (nbaValue > 100) { Blynk.virtualWrite(V11, "Game Canceled."); nbaGamesMachine.transitionTo(nbaWaitForGame); } } }
Add Snoring Monitoring
This feature is here because of two reasons: 1. Monitoring and scientific causes and 2. Prove people I'm not snoring. We'll be listening the sound during the night through the sound sensor. When hear a noise record it in a google sheet doc:
1. Listen for noise:
if (slideSwitch && (millis() - soundPreviousMillis > soundCheckTime)) { Serial.println("Snore on"); soundValue = CircuitPlayground.mic.soundPressureLevel(10); if (soundValue > soundThreshold) { String message = "Snore:" + soundValue; Blynk.virtualWrite(V11, message); } }
2. When detecting noise, we will notify Integromat webhook, The same we've created before (so we won't have to pay more), and use text filter to receive only snoring messages, then we will get the value and add it to a row in google sheet.
Gather Some Good Sleep
Enjoy!
Ideas for improvements:
Monitoring your lucid dreams over night
Expanding the game api for different sports
Creating a specific alarm tone for a specific score difference, so you will know the score difference range when you hear the alarm and decide in that moment to awake or not.
That's it for me.