Introduction: White Noise Night Light

About: I build things.

This is a project I made for my 1-year-old for Christmas. Honestly though, it was a sanity present for me and my wife. It's a white noise machine that can play multiple different sounds selected through a web interface, and also incorporates lights that change color based on the time (red lights mean be in bed, yellow mean you can play in your room, and green means it's ok to come out). Since my son is too young to tell time, a color-based night light seemed like a really good idea.

It's a really easy project, and since I've already written the code, it's maybe a 1 out of 5 on the difficulty scale. If you have toddlers that bother the heck out of you way too early in the morning, you'll want to make one.

Step 1: Parts List

1. Raspberry Pi Zero W

2. Some kind of case (I used this one from Amazon)

3. Blinkt from Pimoroni

4. Speaker Phat from Pimoroni (You could also use some other DAC with cheap speakers)

You will need to have some basic soldering skills to put together the speaker phat, there is a link with step-by-step instructions from the product page, so you can't screw it up.

5. [OPTIONAL] Panel mount micro USB cable - from Adafruit

6. some connecting wires or jumpers

That's it!

Step 2: Burn Raspbian and Hook Up the Boards

I am using Raspian Stretch lite for this build. So burn that to a microSD with whatever tool works for you, then fire up the pi. If you need help getting a headless pi to connect to your home's wifi and enable ssh, there are plenty of tutorials online that can show you how to do that, but you will need to make sure you've done that for the rest of this to work.

Using http://pinout.xyz you can pull up both boards and get their pinouts on this site. The Blinkt boards only requires 4 connections, and the Speaker Phat needs 9.

It should look like the photo when finished. Now we need to test that everything is working.

Step 3: Installing the Required Software

Now that we're connected up, we'll need to install the software required to run the Blinkt and Speaker Phat boards. SSH in to the pi, and you'll find yourself in the home directory. Enter the following:

curl https://get.pimoroni.com/blinkt | bash

and then once that's complete, this:

curl -sS https://get.pimoroni.com/speakerphat | bash

That will install everything needed for both the Pimoroni boards. If you enter the ls command, you should see a Pimoroni directory. So now let's write some code and test the Blinkt board.

Step 4: Writing Some Code and Testing It Out.

Make a directory called "scripts" by typing mkdir scripts and we'll keep everything we need to run in there. So cd scripts to get yourself in to that folder.

Now, what we want are red dim lights for night time, yellow dim lights for quiet play time, and slightly brighter green lights when it's ok to come out. For me, I wanted red lights from 7:30pm to 6:15am, and at 6:15am they would turn yellow for an hour, and then finally green at 7:15am. I also wanted them to turn off at 8:30am when no one was likely to be in the room.

There are two ways to do this. The first (the way I chose to do it) is with four different scripts, run from cron jobs. The other way to do it, is one script, that includes a time function that is run at startup. I actually wrote the script to do it that way first, but it seemed less efficient than doing it based on cron, so I switched it up. If you want the "one script" method, let me know and I can post it in the comments.

So, let's start with the red script. Type touch red.py, then nano red.py. Then enter the following code.

#!/usr/bin/env python
import blinkt
blinkt.set_clear_on_exit(False)
blinkt.set_brightness(0.3)
blinkt.set_pixel(3, 128, 0, 0)
blinkt.set_pixel(4, 128, 0, 0) #sets pixels 3 and 4 to red
blinkt.show()

Do the same for yellow.py and green.py.

yellow.py:

#!/usr/bin/env python
import blinkt
blinkt.set_clear_on_exit(False)
blinkt.set_brightness(0.2)
blinkt.set_pixel(2, 128, 128, 0)
blinkt.set_pixel(3, 128, 128, 0)
blinkt.set_pixel(4, 128, 128, 0)
blinkt.set_pixel(5, 128, 128, 0) #sets pixels 2, 3, 4, and 5 to yellow
blinkt.show()

green.py:

#!/usr/bin/env python
import blinkt
blinkt.set_clear_on_exit(False)
blinkt.set_brightness(0.2)
blinkt.set_all(0, 128, 0) #sets all pixels to green
blinkt.show()

And finally, we want a script to clear the Blinkt when it's not needed (lightsout.py):

#!/usr/bin/env python
import blinkt
blinkt.set_clear_on_exit(True)
blinkt.set_brightness(0.1)
blinkt.set_all(0, 0, 0) #sets all pixels to off
blinkt.show()

That's it. To test type python red.py and see if the two middle pixels light up red. Then type python lightsout.py to clear it. That's it! Next we'll have to set those in the cron tab so they run at when we want them to.

Step 5: Adding Those Scripts to Cron

In the SSH terminal type crontab -e

scroll to the end of the file and add the following lines:

15 6 * * * python /home/pi/scripts/yellow.py
15 7 * * * python /home/pi/scripts/green.py 30 8 * * * python /home/pi/scripts/lightsout.py 30 19 * * * python /home/pi/scripts/red.py

That sets up the scripts to run at the times described in the previous step, go ahead and adjust these to suit your needs.

That's it for the nightlight! Super easy. Now let's move on to setting up the White Noise portion of this build.

Step 6: Testing the Speaker Phat

The easiest way (in my opinion) to test out the Speaker Phat is by installing sox and running some static from the command line.

sudo apt-get install sox

Once that's installed, we can try some sample play commands. This one should sound like waves.

play -n synth brownnoise synth pinknoise mix synth 0 0 0 10 10 40 trapezium amod 0.1 30

How relaxing! Ctrl+c will stop it. But, what's that? There are a bunch of LEDs across the face of the Speaker Phat lighting up, and we can't have that interfering with our Blinkt lights. So lets turn those off.

To do this, we'll need to modify the /etc/asound.conf file and remove the VU meter plugin, so it wont even try to drive the LEDs in the first place. I did this by simply renaming it. Type this command mv /etc/asound.conf /etc/asound.conf.bak I found this through a bit of Googling, so there might be a better way.

Sox works, and that's great, but I planned on using some loopable MP3s for the white noise part of this machine, so I needed a different player, preferably something really light-weight. mpg123 is what I settled on. Install that now with sudo apt-get install mpg123

Ok, now that we know the Speaker Phat works as expected, time to build the interface and corresponding scripts.

Step 7: Setting Up a Small Webserver and Webapp

Flask is a micro web framework written in Python. It provides all the functionality we need for the webserver (which is going to act as an app). Install it with the following command:

pip3 install flask

That will take some time, so wait it out. Once it's complete, we're going to need to build up the folders we will need to pull from when the website runs, and these folders have specific names. Let's start with a place to host the website. From the home directory, make a new directory called www with mkdir www. Now cd www in to that directory. In here we need two more directories, one called static and another called templates.

We also need a place to put our loopable MP3s. I made a directory in the home directory called "sounds" for this. I found my MP3s by searching for loopable whitenoise MP3s on Google. Lots of free places to pull from. I used WinSCP to upload the files.

You might want to test them with the omxplayer command below playing with the --vol -### part to dial in the right volume level for your room. Again a Ctrl+C will stop the player.

Now that we have all those in place, lets write some python to stand up the webserver when the pi starts up. Go back to the www directory and start a new file called webapp.py (nano webapp.py) and insert the following code

webbapp.py:

#!/usr/bin/python
from flask import Flask, render_template, request, redirect
import os

app = Flask(__name__)

@app.route('/')

def index():
    return render_template('index.html')

@app.route('/rain', methods = ['POST'])
def rain():
	os.system("mpg123 -f 8000 --loop -1 ~/scripts/sounds/rain.mp3")  <br>	return redirect('/')

@app.route('/waves', methods = ['POST'])	
def waves():
	os.system("mpg123 -f 20500 --loop -1 ~/scripts/sounds/waves.mp3")
	return redirect('/')

@app.route('/whitenoise', methods = ['POST'])	
def whitenoise():
	os.system("mpg123 --loop -1 ~/scripts/sounds/whitenoise.mp3")  
	return redirect('/')

@app.route('/stop', methods = ['POST'])	
def stop():
	os.system("killall mpg123")  
	return redirect('/')

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

As you can see this webapp will have 5 pages, one for the index, 3 for 3 different sounds (wave, rain and whitenoise) and 1 more to stop. All 4 non-index pages redirect back to index ('/') after they execute the command sent to omxplayer, so we only need to create one index.html, and nothing else. I'm using killall here as a stop function, because I couldn't find a better way to send a "stop" command to omxplayer. If you know of a better way of doing this, I'd love to hear it!

Now let's put together the index.html.

Step 8: Building the Website

These are the images I used for my build, but feel free to make your own. They all need to be saved in the static folder we made earlier. The index.html file we're going to make here needs to be in the templates folder. That's pretty important, otherwise, none of it will work. Here is the code for my index.html (again, this is just simple html, so modify it in whatever way works for you).

Since Instructables won't let me post raw HTML, here is a link to the file as it exists on my Dropbox:

https://www.dropbox.com/s/n5xf2btftk5sz9b/index.ht...

But in case that ever dies, the HTML file is just some fancy CSS, and a simple 2x2 table with those 4 icons as buttons with post values like so:

form action="/whitenoise" method="post"
  input type="image" src="/static/whitenoise.png" value="White Noise"

Should be pretty easy to make yourself one.

Last step is to make sure the webapp.py runs at startup, again, I did this by adding it to the crontab. So once again type crontab -e and add the following to the end:

@reboot python3 /home/pi/www/webapp.py

Then reboot the pi, point a browser on another machine (your phone) to the IP of the pi (best if you can make this static) and see if it worked. Click the buttons and see if you get noise.

On an Android phone you can bookmark a website to your homescreen, which is what I did with this to get it to look and feel like an app. If you want to really make it look "pro" find or create a suitable .ico file and give the website its own icon which will appear on your phone's home screen and look much more like an app. Lots of tutorials online on how to add an icon (favicon) to a website.

Step 9: Jam Everything in to a Case

Now that everything has been tested and is working, time to stuff it all in to a case.

I built a standoff mount for the Raspberry Pi Zero using some leftover plastic I had around the house. I then drilled some holes for the panel mount micro USB, and using some jewelers files squared off the hole. The panel mount cable is a bit stiff, so I may buy a right angle adapter for the micro USB port on the Pi sometime in the future.

I cut a small opening in the top of the case for the speaker to play out through by drilling two holes and connecting them with a Dremel. Then drilled the holes in the lid to mount the Speaker Phat. For what it's worth, after taking this photo I went back and made a few more holes because the noise was really getting trapped inside the case. I mounted the Blinkt using that poster putty stuff because the thing doesn't have any mount holes, but the putty seems to hold well, so it'll do.

Step 10: That's It!

Plug it in and you're done. Here's mine running just after 8pm. The green LED on the pi itself isn't as bright as this photo makes it appear.

Some later edits I made:

I added 4 more pages to the webapp.py and index.html files. Those 4 being "red", "yellow", "green", and "off". Pretty self explanatory. I wanted the ability to switch it from green back to yellow if the wife and I were feeling extra tired and didn't want to be bothered.

@app.route('/red', methods = ['POST'])	
def red(): os.system("python ~/scripts/red.py") return redirect('/')

Basically that 4 times, running the 4 different scripts, then some more buttons in index that call those pages.

The other change I already noted, but I drilled some more holes and widened the existing opening around the speaker because the noise wasn't escaping the enclosure well enough.

If I make any additional modifications, I'll be sure to come back here and list them.

Step 11: Addendum

After building this I realized that my wife and I both frequently leave our phones downstairs when putting the kid down for bed or a nap. So I added a momentary push button to physical pin 36, and included the following code (I called it button.py) to run at startup in rc.local:

#!/usr/bin/env python
import RPi.GPIO as GPIO
import time
import os
import subprocess
import re

GPIO.setmode(GPIO.BOARD)   # Use Physical Pin Numbering Scheme
button=36                  # Button is connected to physical pin 16

GPIO.setup(button,GPIO.IN,pull_up_down=GPIO.PUD_UP) # Make button an input, Activate Pull UP Resistor

while True:
  if GPIO.input(button)==0:			# Wait for button press
    returnprocess = False			# Initially set to sound being off
    s = subprocess.Popen(["ps", "ax"],stdout=subprocess.PIPE)
    for x in s.stdout:
      if re.search("mpg123", x):
        returnprocess = True

    if returnprocess == False:
      os.system("mpg123 --loop -1 /home/pi/scripts/sounds/whitenoise.mp3 &")
      os.system("python /home/pi/scripts/red.py")

    else:
      os.system("killall mpg123")
      os.system("python /home/pi/scripts/lightsout.py")

As you can see, I also switched to mpg123 from omxplayer because it's much more lightweight and simple to use.

HOWEVER, for some reason when I put this script in rc.local it does indeed run at startup without any problems. But the audio is really really choppy. When I run the script as normal, in putty, no such issues. I'm having a bear of a time troubleshooting this, so if anyone has any ideas, please let me know! Thanks.