Tags


A stupid smart doorbell system

First written on November 6, 2018
Last updated on September 13, 2021

A smart doorbell is useful when you are not at home but you want to be notified about when and who rang.

Ideas

The idea behind my implementation is very naïve: someone rings at the doorbell, a program listening from a microphone pics up the sound and triggers a script. A webcam pointed in front of the door takes a picture which is sent as an email attachment.

Initially I wanted to use a cheap wireless doorbell. The problem is that I have no experience in soldering and I didn’t want to mess things up. However, the idea intreagued me nonetheless.

I had to find another system to get the trigger. The obvious way was to use the existing doorbell system in the house. For this purpose I found a ruby script which uses ALSA and soX to record and examine the sound. If the sound is greater than the set threshold an external script is called. I added an expected frequency range detection check which is useful to avoid some false positives. To do this I first translated the script from ruby to bash.

Hardware tests

The first thing I did was to try the Raspberry PI 3B plus, with a an unnamed USB webcam which has an integrated microphone. A Logitech USB webcam would take the pictures instead.

The Raspberry was connected via Ethernet. Unfortunately the single board computer suffers from USB bandwidth problems since the 4 USBs ports and the Ethernet socket are all shared as part of a single hub. Just in case I also tried using a powered 7 port USB hub. The results were the following:

Error starting stream. VIDIOC_STREAMON: Connection timed out
VIDIOC_DQBUF: Invalid argument
Capturing 1 frames...
VIDIOC_DQBUF: Invalid argument
No frames captured.

What follows is part of the output from $ lsusb concerning the previously cited webcam.

ID 0c76:1600 JMTek, LLC. Ion Quick Play LP turntable
ID 1871:0306 Aveo Technology Corp.

Even using the integrated WiFi card, which is not connected to the USB hub, didn’t solve the problem. I had to use a proper computer…

With an old IBM Thinkpad everything seemed to work fine so I decided to add another feature. Using the same unnamed webcam and the software motion it is possible to detect movement. I tried to use that to know when and who passes through the front door. Just like before, there were problems with the USB bandwidth. A cause of this might be that the computer has USB version 1.1 ports. I had slightly better improvements when I passed from a USB microphone to an analog one and changed the original webcam to an

  ID 058f:3880 Alcor Micro Corp.

But the bandwidth was still not enough, so I had to settle for a simple doorbell system for the moment.

Ingredients

What follows are a couple of tables of all the hardware and software ingredients I used.

Hardware

ObjectQuantityDescriptionPurposeRequired
IBM ThinkPad A311computercontrol the devices and read sensorsyes
Logitech QuickCam E10001webcamTake photosyes
Dlink DHP-306AV1powerline to ethernet adaptersif your computer is out of range from the router or switchno
Ethernet wire2CAT 5econnect the power line devicesno
USB extension cord1 connect this cable between the computer and the webcamno
analog microphone1cheap microphone with a 3.5mm jackrecord audioyes
plastic cup1put this around the microphone tip and point it to the doorbellconcentrate the soundno
paper tapesome stick the microphone to the wall near the doorbellno

Software

SoftwareDescriptionRequired
Debiani386 stable. Version 9.5.0. Operating system. I didn’t want to fiddle too much this timeyes
fswebcamtake picturesyes
OpenSSHconfigure the computer remotelyyes
NTPtime synchronization daemonyes
S-nailsend mailsyes
msmtpMTAyes
soXsound processingyes
bcarithmetic comparisions in bashyes
ALSAset the audio levelsyes
GNU Bash“The GNU Bourne Again shell”yes
eSpeakspeech synthesisno
Haveged“[…] Feed Linux’s random device”no
webfsa simple web server so you can access the pictures with in a simple mannerno

Scripts

What follows are the two scripts I used, called listen.sh and shoot.sh respectively.

#!/bin/bash
#
# listen.sh
#
# Copyright (C) 2009 Thomer M. Gil [http://thomer.com/]
#               2018 Franco Masotti
#
# Oct  22, 2009: Initial version
# Oct  29, 2018: Translation from ruby to bash. Added expected frequency range.
#
# This program is free software. You may distribute it under the terms of
# the GNU General Public License as published by the Free Software
# Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# This program detects the presence of sound and invokes a program.
#

# You need to replace MICROPHONE with the name of your microphone, as reported
# by /proc/asound/cards.
MICROPHONE='I82801CAICH3'
# Sampling format of the audio card.
FORMAT='S16_LE'
THRESHOLD='0.30'
# Expected frequencies in Hz.
EXPECTED_FREQ_MIN='600'
EXPECTED_FREQ_MAX='1250'
# Sample gathering in seconds.
SAMPLE_DURATION='3'
SCRIPT_FULL_PATH="/home/doorbell/srv_maint/scripts/doorbell/shoot.sh"

HWDEVICE="$(cat /proc/asound/cards | grep "${MICROPHONE}" | awk '{print $1}')"
while true; do
  out="$(/usr/bin/arecord -D plughw:"${HWDEVICE}",0 -d "${SAMPLE_DURATION}" \
    -f "${FORMAT}" 2>/dev/null | /usr/bin/sox -t .wav - -n stat 2>&1)"
  # As you can see, no regex are used here -> this might fail.
  amplitude="$(echo "${out}" | grep 'Maximum amplitude' | awk '{print $3}')"
  frequency="$(echo "${out}" | grep 'Rough   frequency' | awk '{print $3}')"
  if [ "$(echo ""${amplitude}">="${THRESHOLD}"" | bc -l)" -eq 1 ]; then
        if [ "$(echo ""${frequency}">="${EXPECTED_FREQ_MIN}"" | bc -l)" -eq 1 ] \
        && [ "$(echo ""${frequency}"<="${EXPECTED_FREQ_MAX}"" | bc -l)" -eq 1 ]; then
                echo "match: f=${frequency} Hz; a=${amplitude}"
                ${SCRIPT_FULL_PATH}
        else
                echo "no match: f=${frequency} Hz; a=${amplitude}"
        fi
  else
        echo "no match: a=${amplitude}"
  fi
done
#!/bin/bash
#
# shoot.sh
#
# Copyright (C) 2018 Franco Masotti
#
# Oct  29, 2018: Initial version.
#
# This program is free software. You may distribute it under the terms of
# the GNU General Public License as published by the Free Software
# Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# This program takes a picture and sends it via mail.
#

BASE_DIR="/home/doorbell/srv_maint/scripts/doorbell"
PICTURE_FILE="/var/www/doorbell.jpg"
MESSAGE="event: $(date +"%F %T")"
CAMERA="/dev/video0"
RESOLUTION="640x480"
CAPTURE_FRAMES="1"
SKIP_FRAMES="50"
BRIGHTNESS="100%"
CONTRAST="100%"
LOG_FILE="/var/log/doorbell.log"
MAIL_SUBJECT="doorbell"
MAIL_SENDER="Home"
MAIL_RECEIVER_ALIAS="all"


pushd "${BASE_DIR}"

fswebcam \
        --set brightness="${BRIGHTNESS}" \
        --set contrast="${CONTRAST}" \
        -r "${RESOLUTION}" \
        -R \
        --font 'dejavu:10' \
        -S "${SKIP_FRAMES}" \
        -F "${CAPTURE_FRAMES}" \
        -d v4l2:"${CAMERA}" "${PICTURE_FILE}" &
pid="$!"

# Below: put generic commands.
# Speak out.
espeak -s 125 -v it "Doorbell" &
# Keep a log file.
echo "${MESSAGE}" >> "${LOG_FILE}" &

# Wait for the picture to be taken.
wait "${pid}"

# Below: put commands which need the photo as dependency.
echo "${MESSAGE}" | mail \
-r "${MAIL_SENDER}" \
-s "${MAIL_SUBJECT}" \
-a "${PICTURE_FILE}" \
"${MAIL_RECEIVER_ALIAS}" &

popd

Instructions

Installation

# apt-get update
# apt-get dist-upgrade
# apt-get install alsa-utils fswebcam openssh ntp s-nail msmtp sox bc alsa-utils bash espeak haveged webfs
# useradd -U -m -s /bin/bash doorbell
# passwd doorbell
# exit
$ mkdir ~/srv_maint/scripts/doorbell
$ cp listen.sh shoot.sh ~/srv_maint/scripts/doorbell
$ echo "@reboot /home/doorbell/srv_maint/scripts/doorbell/listen.sh" > ~/srv_maint/cron_entries.txt
$ crontab ~/srv_maint/cron_entries.txt
# touch /var/log/doorbell.log
# chown doorbell:doorbell /var/log/doorbell.log
# debian config file for webfsd.  It is read by the start/stop script.

# document root
web_root="/var/www"

# hostname (default to the fqdn)
web_host=""

# ip address to bind to (default: any)
web_ip=""

# port to listen on (default: 8000)
web_port="80"

# virtual host support (default: false)
web_virtual="false"

# network timeout (default: 60 seconds)
web_timeout=""

# number of parallel connections (default: 32)
web_conn=""

# index file for directories (default: none, webfsd will send listings)
web_index=""

# size of the directory cache (default: 128)
web_dircache=""

# access log (common log format, default: none)
web_accesslog=""

# use bufferred logging (default: true)
web_logbuffering="true"

# write start/stop/errors to the syslog (default: false)
web_syslog="false"

# user/group to use
web_user="www-data"
web_group="www-data"

# cgi path (below document root -- default: none)
web_cgipath=""

# extra options, including arguments
web_extras=""

Calibration

# alsactl store

Possible roadmap

~


Post a new comment