Anton Sarukhanov

Full-Stack Developer

Measuring sound level with Zabbix

Test consumer electronics with enterprise tools!

Here's how I watched the sound level from a microphone with a Raspberry Pi and an IT monitoring system.

Why?🔗

I wanted to...

  • measure how long an old MP3 player could play until the battery died
  • ... without having to listen to the music
  • ... and I wanted to find out as soon as it stopped playing.

Photo

Zabbix🔗

Zabbix is an open-source enterprise IT monitoring tool. I already use it to keep an eye on my servers. Maybe it can help here?

Measuring volume🔗

We can use the rec tool (part of the SoX audio processor) to grab audio from the microphone and analyze it1.

rec -n stat trim 0 .5
  • -n: record to null instead of a file
  • stat: calculate statistics
  • trim 0 .5: record .5 seconds from the start (0)

We can use the Maximum amplitude statistic to infer whether the music is playing.

rec -n stat trim 0 .5 2>&1 | awk '/^Maximum amplitude/ { print $3 }'

All that's left is to track this value over time, and alert us when it falls below some threshold.

zabbix_sender🔗

Zabbix can observe metrics in a few different ways (agent, remote network-based checks, etc). Here, I just want the easiest way to watch a numeric value with minimal configuration overhead. Seems like a job for zabbix_sender.

This is the syntax:

zabbix_sender -z <zabbix-server> -s <monitored-host> -k <metric> -o <value>

First, some housekeeping on the Zabbix server:

  1. Create a Host. Let's call it soundmonitor. Host
  2. Create a new Item for the soundmonitor host. Let's call it soundlevel. Item
  3. Put the Item on a Graph. Graph

Let's test the zabbix_sender command...

zabbix_sender -z zabbix.example.net -s soundmonitor -k soundlevel -o "0.5"

...and check the graph. It should have our fake datapoint.

Graph 0.5

There it is!

Assembling the pieces🔗

We can use command substitution to string together a one-liner.

zabbix_sender -z zabbix.example.net \
  -s soundmonitor                   \
  -k soundlevel                     \
  -o "$(rec -n stat trim 0 .5 2>&1 | awk '/^Maximum amplitude/ { print $3 }')"

This takes the numeric sound level from rec | awk and passes it as an argument to zabbix_sender.

Now just put that in a loop...

while true; do
  zabbix_sender -z zabbix.example.net \
    -s soundmonitor                   \
    -k soundlevel                     \
    -o "$(rec -n stat trim 0 .5 2>&1 | awk '/^Maximum amplitude/ { print $3 }')";
done

...and we have a working pipeline.

Ta-da!🔗

Graph (for real)

Pausing the iPod produced that valley in the middle. Based on this test, I set a Trigger for soundlevel falling below 0.5 for 60 seconds or longer.

Extra credit🔗

The Raspberry Pi might lose power and restart. We can address that with a systemd service running the zabbix_sender loop.

/etc/systemd/system/zs.service🔗

[Unit]
Description=ZabbixSoundMonitor
Wants=network-online.target
After=network.target network-online.target

[Service]
Environment="AUDIODEV=hw:1,0"
Environment="AUDIODRIVER=alsa"
ExecStart=/usr/local/bin/zs.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

This takes care of a few things:

  1. Waits until a network is available.
  2. Sets the AUDIODEV and AUDIODRIVER environment variables2.
  3. Runs the script at /usr/local/bin/zs.sh (which contains the loop we wrote).

Enable the new service with systemctl enable zs.service, and start it with service zs start. It will automatically start on reboot.


  1. Thanks to StackExchange user "nandhp" for this clue. Source: audio - Monitoring the microphone level with a command line tool in Linux - Super User 

  2. Apparently required by rec. Suitable values for a Raspberry Pi were suggested in this forum thread: sox default device - Raspberry Pi Forums