Building low-cost & hands-free white noise machine powered by Amazon Alexa and Raspberry Pi

Dmitry Lozitskiy
8 min readOct 11, 2018

--

Setting the goal

As many parents I was looking for a lullaby solution helping a baby to sleep through the night. I wanted it to be hands-free and I did’t want it cost me much. Thankfully I had an Amazon Echo device and a Raspberry Pi which I turned into a white noise machine with nearly $0 monthly cost to run.

I’m going to show how to build one, so you can see how easy it is and if you need — build one yourself. The pre-requisites can seem tedious so you are welcome to skip to the implementation part and do pre-reqs as you go.

Also if you don’t have Raspberry Pi you still can use this guide skipping prerequisites and hosting your media files on S3 bucket or behind CloudFront distribution, however data transfer and bandwidth monthly cost will be around $3–4 versus $0 if you follow the guide.

Prerequisites

1) Skipping forward I just say that you need a domain name, so if you don’t have one go and register one for free here

2) Once you have a domain name registered create an Amazon Route53 public zone with the same domain name. This is going to be the only paid and the most expensive step of that guide which will cost you $0.50 per month to run.

Capture all NS record values from Route53 and transfer them to your domain registrar custom DNS configuration. This means that domain registrar will delegate DNS resolution to Amazon Route53 service.

Freenom -> Services -> My Domains -> Manage Domain -> Management Tools -> Nameservers

3) One of the important prerequisites is that your local Wi-Fi network domain name should match the domain that you have just registered, you can do it by setting up a DHCP on your Wi-Fi router and also don’t forget to enable DNS proxy on the router, this will be required to split DNS query and resolve it locally. Below are sample settings from my router, but yours might look a bit different:

DHCP settings on the Wi-Fi router with registered domain name
DNS proxy enabled, which allows to split DNS query and resolve it locally

4) Once you have done previous steps you can go to AWS Certificate Manager and request a public certificate with the Common Name of you Raspberry Pi local hostname and a new registered domain name. In my case I had to request a certificate for osmc.dlozitskiy.online. Once the DNS validation is completed you will see your certificate status as “Issued”:

Public SSL Certificate issued by AWS Certificate Manager

5) You need an AWS S3 bucket with public permissions, you can achieve this by setting following bucket policy on your bucket:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your_bucket_name/*"
}
]
}

6) Once your SSL and S3 are ready you can go and create an AWS CloudFront Web distribution. There are only two things that you need to modify on the configuration page:

  • Set Origin Domain Name to a bucket you created in a previous step
  • For SSL Certificate choose Custom SSL certificate (example.com) and select one that you have created earlier in AWS Certificate Manager

Click “Create Distribution” and capture “Domain name” on distributions page, it will be something like d***.cloudfront.net

7) Go to AWS Route53 zone and create an A record with your Raspberry Pi hostname which should be an alias record to your CloudFront domain name captured in previous step:

8) Now let’s issue another SSL certificate which we will need to install on our Raspberry Pi device soon. Go here and request another free SSL certificate with the same CN that you have used in AWS Certificate Manager earlier:

Go through “Manual Verification (DNS)” of domain ownership, you will be asked to add a TXT record to your AWS Route53 zone, simply do that and after couple minutes you will be able to download your free SSL certificate signed by known Certificate Authority.

9) Select a lullaby of your choice, I have used this collection to choose mine

10) Install and configure apache on your Raspberry Pi following the commands below:

apt-get install apache2

Make sure that /etc/apache2/ports.conf has SSL port configured to 443 for both ssl_module and mod_gnutls:

<IfModule ssl_module>
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>

Modify config of a default site /etc/apache2/sites-enabled/000-default.conf so it looks like below:

<VirtualHost *:443>
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
SSLEngine on
SSLCertificateFile /etc/apache2/certs/cert.pem
SSLCertificateKeyFile /etc/apache2/certs/key.pem
SSLCertificateChainFile /etc/apache2/certs/ca.pem
</VirtualHost>

Place private key, certificate and chain files from the prerequisite 8 to locations /etc/apache2/certs/key.pem, etc/apache2/certs/cert.pem, /etc/apache2/certs/ca.pem respectively

Place lullaby file you have downloaded in prerequisite 9 to this location /var/www/html and start apache server:

apachectl start

11) Reboot your Raspberri Pi and Echo devices to get a new domain name from Wi-Fi router DHCP assigned to them

Now if you did everything right when you type in your browser https://<your pi hostname>.<your domain name>/lullaby.mp3 you should be able to play it without your browser complaining about SSL certificate. One thing to note here is that the lullaby is playing from your Rasberry Pi device, without your HTTP request traversing internet because of split DNS.

Implementation

AudioPlayer Play Directive restrictions

If you look into Play Directive of Alexa AudioPlayer Interface Reference you will notice that audioItem.stream.url description says:

The audio file must be hosted at an Internet-accessible HTTPS endpoint. HTTPS is required, and the domain hosting the files must present a valid, trusted SSL certificate. Self-signed certificates cannot be used. Many content hosting services provide this. For example, you could host your files at a service such as Amazon Simple Storage Service (Amazon S3) (an Amazon Web Services offering).

Which effectively means that Alexa can’t play an audio stream unless the file hosted on an internet accessible endpoint. There are no issues with hosting the file on AWS S3 and let Echo device stream it directly from S3 or using CloudFront URL in front of S3, however this will cost you around $3-4 per month which doesn’t satisfy our low-cost requirement, that’s why we will have to trick Alexa to think that the audio is on the internet facing HTTPS endpoint by using split DNS technique.

Skill Architecture

We want our skill to be as simple as possible and not to have any custom intents and utterances. The skill should react only to the launch request and stop intent: “Alexa, start lullaby”, “Alexa, play lullaby”or “Alexa, open lullaby” to start and “Alexa, stop” to stop playing. Let’s go through the steps in detail:

Skill Architecture

Step 1 — User asks Echo device to launch the skill by simply saying: “Alexa, start lullaby”

Step 2 — Echo device sends the request as an audio to Alexa Voice Service, where AVS through it’s ASR & NLU capability recognizes the skill name and the request type as “LaunchRequest”

Step 3 — Alexa Voice Service invokes a particular skill endpoint which in our case is defined as AWS Lambda function ARN. In the request to Lambda AVS passes an identified request type “LaunchRequest” from a previous step

Step 4 — Lambda function responds to the LaunchRequest with the Audio Player Play Directive which contains a URL of the file to play and a behavior “REPLACE_ALL” to immediately replace everything in the Echo device play queue with the file URL

Step 5 — Alexa Voice Service goes and checks the SSL certificate of the URL returned by Lambda function. Important to point out that AVS doesn’t check if the file is available by this URL

Step 6 — AVS passes the response to the Echo device

Step 7 — Echo device sends Wi-Fi router a DNS request with a hostname from a URL provided in the previous step

Step 8 — Router using split DNS technique replies to Echo device with the IP of a Raspberry Pi device

Step 9 — Echo device goes and validates SSL certificate on the provided local Raspberry Pi IP address

Step 10 — Echo device starts streaming the music file hosted on Raspberry Pi using local IP address

Skill implementation

Lambda function code is located here inside index.js file: https://github.com/Dlozitskiy/alexa-white-noise-machine.git, you can create function from existing AWS blueprint for Alexa and change the function code, this way you will not need to upload package dependencies for ASK SDK.

As the same backend function can be used to serve multiple skills I figured out that the map with the skill ID to the music file name will be handy:

const sounds =
{ [process.env.skill1_id]: process.env.skill1_file,
[process.env.skill2_id]: process.env.skill2_file,
[process.env.skill3_id]: process.env.skill3_file
};

Don’t forget to set those environment variables on your Lambda function:

You may notice that there is an m3u file in the map, yes — Alexa supports m3u with the URL of an internet audio stream, so you can listen to your favorite internet radio for example.

Worth to note that the skill will react to PlaybackNearlyFinished Audio Player event by enqueuing the same file again and again, so the file will be played infinitely until you ask Alexa to stop:

Looping the playback by reacting to PlaybackNearlyFinished event

For the skill implementation you can take an interaction model en-US.json from the same repository and paste it to your skill JSON editor in Amazon Developer Console.

The last thing before you start building your skill — make sure that under your skill interfaces you have enabled “Audio Player” interface.

That’s all! It’s been a long reading but now after you finished the build you should be able to invoke your skill by simply saying “Alexa, start lullaby”.

Conclusion

Well done, you have reached to the very end of this guide, I hope you have found it useful and it inspired you to build you own lullaby or white noise skill. Let’s see what we have achieved here:

  • $0.5 monthly cost to run the skill versus $4.5 of running the same on S3 or CloudFront
  • Custom invocation name without restrictions of having two words in it — we can use one word invocation name as we are not going to publish the skill
  • No custom intents or utterances which makes it very concise and easy to use
  • Baby sleeps through the night because of awesome and cutting edge lullaby that we built

Please thumbs up the post if you like its content and want to see more posts like that!

--

--

Responses (1)