Random Bits

An attempt at creating a DIY baby monitor

Occasionally, “modern” baby monitors hit the news, but not for the best reasons. Sometimes for the lack of security, other times for exploitative practices, etc.

This also applies to many other categories of products that we can include in the so-called “Internet of Things”.

After reading a recent article about the topic, some comments with clever approaches, made me wonder how hard would it be, to build a simple and reasonably secure alternative with common tools (DIY style)?

Starting with the approach above, that uses ffmpeg and ffplay, I will describe how far I was able to go with:

  • 1 Raspberry Pi
  • 1 Standard webcam (with microphone)
  • (Optional) 1 Wi-Fi USB dongle, if board doesn’t include one.

The goal of the solution: Non-technical people can easily and securely check their baby or their pet, anywhere. They should be able to move the “monitor” around (plug out and then plug in), it should start working right away once turned on (and a known Wi-Fi connection is available).

Figuring it out

After playing a bit with the solution described in the mentioned comment, I found that while it seems ok for a quick setup, it falls short in 2 points:

  • Only works for one viewer at a time
  • The viewer IP address needs to be set on the monitor, which means a config change and a restart are required every time you change the device you are using to view the video stream.

Falling short of achieving my main goal. It does require a different approach.

After additional research, I tried several alternatives that can be found with a quick online search. Including, uv4l using WebRTC (not quite what I’m looking for), motion (no sound, but perhaps can be used together with the final solution for extra functionality) and others. None of them were easy to set up or could achieve the defined goal.

Later, I found another blog post describing how the author achieved the goal using picam. However, that software only supports the Raspberry Pi camera module, a strict hardware limitation that falls out of scope. The same for libcamera.

In the end, the easiest solution was to turn to vlc, the well known media player. It already comes installed on the Raspberry Pi OS.

I checked the documentation, which already provides a great deal of information, and started tinkering with it. It turned out to be a good fit, however couldn’t get it working exactly as I wished. Fortunately, I’m not the first to try this and someone else already wrote a great answer to a similar question.

All things set, the following command does exactly what is needed:

cvlc -vvv v4l2:///dev/video0 :input-slave=alsa://hw:1,0 --sout '#transcode{deinterlace,vcodec=mpgv,acodec=mpga,ab=128,channels=2,samplerate=44100,threads=4,audio-sync=1}:standard{access=http,mux=ts,mime=video/ts,dst=device.local.ip.address:8080}'

Then you just need to open a network stream, using VLC or other player, on any of your devices, using the following URL:


Done, phase one complete. It has a slight delay (just a couple of seconds), the quality is very reasonable, and we kept it simple (one command).

Making it persistent

Now 2 things are missing:

  1. Set and start everything once the device is turned on
  2. Be able to access it from anywhere (without exposing it to the internet and with proper access controls).

To address the first, a person can rely on systemd. The following “service” config, will start the stream when the device is turned on and connected to a Wi-Fi network:

Description=Cam Stream



This file should be placed in /etc/systemd/system/ and then enabled using the systemctl command. The file contains the single cvlc command I mentioned above.

Making it available

To address the second point (Be able to access it from anywhere), I opted to add the device to a Tailscale network and turn on Magic DNS.

This way, I can authorize which devices on the VPN will access the “cam”. Once those devices connect to the network, regardless of where they are, they can access the stream. Tailscale will handle access-control to this device and will encrypt all connections to it.

A simple ACL rule for defining the right access-controls could be:

  "acls": [
    {"action": "accept", "src": ["tag:camviewer"], "dst": ["tag:cam:8080"]},

Where tag:camviewer represents the devices allowed to access the cam stream.

Regarding the configuration described in the previous sections, a few changes might be required.

The first is replacing dst=device.local.ip.address:8080 with the new interface address created by Tailscale (dst=device.tailscale.ip.address:8080), so the stream is only available on that protected network.

You might want to edit the systemd service to only start after Tailscale is up and running:

Description=Baby Cam Streaming
Wants=tailscaled.service tailscaled.service sys-subsystem-net-devices-tailscale0.device


Then on your other devices, you would use the following URL to connect:


Note: Due to some timing related issues, you might need to prepend a sleep command with a couple of seconds, to the file.

Wrapping up

I want to finish this post, by going back to the question I asked myself before starting this exploration: “how hard would it be to build something like this?”.

I have to say that the solutions are not obvious. But it also isn’t that hard for a “power user” or for someone with basic technical knowledge to leverage existing tools and build a system that works reasonably well.

The solution I ended up assembling, can look complicated, but it is actually simple, and I quite like it.

I will just leave it here as a future reference, since I might need to return to it someday in the future, or it can be helpful to someone else. If you think there is something wrong, vulnerable or missing, please let me know in the comments.

By Gonçalo Valério

Software developer and owner of this blog. More in the "about" page.