Commits (3)
Title: Measuring sound level with Zabbix
Description: Test consumer electronics with enterprise tools!
Date: March 22, 2019
Cover: /media/zabbix/graph-real.png
Featured_Project: true
Here's a way to observe sound level from a microphone using an IT monitoring system.
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.
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.
# Zabbix
Zabbix is an open-source monitoring tool. I already use it to keep an eye on my servers. Maybe it can help here?
[Zabbix](https://zabbix.com) 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
......@@ -55,17 +56,19 @@ First, some housekeeping on the Zabbix server:
2. Create a new **Item** for the `soundmonitor` host. Let's call it `soundlevel`. ![Item](/media/zabbix/item.png)
3. Put the Item on a **Graph**. ![Graph](/media/zabbix/graph.png)
Now we can test the `zabbix_sender` command.
Let's test the `zabbix_sender` command...
zabbix_sender -z zabbix.example.net -s soundmonitor -k soundlevel -o "0.5"
Take a look at the graph. It should now have our fake datapoint!
...and check the graph. It should have our fake datapoint.
![Graph 0.5](/media/zabbix/graph-0.5.png)
# All together
There it is!
# Assembling the pieces
We can use [command substitution](https://www.gnu.org/software/bash/manual/html_node/Command-Substitution.html) to string together a one-liner.
......@@ -76,9 +79,9 @@ zabbix_sender -z zabbix.example.net \
-o "$(rec -n stat trim 0 .5 2>&1 | awk '/^Maximum amplitude/ { print $3 }')"
This takes the numeric sound level from the `rec|awk` combination and passes it as the `-o` argument of `zabbix_sender`.
This takes the numeric sound level from `rec | awk` and passes it as an argument to `zabbix_sender`.
Now, we just need to put this in a loop...
Now just put that in a loop...
while true; do
......@@ -89,8 +92,42 @@ while true; do
...and we have a working pipeline.
# Ta-da!
![Graph (for real)](/media/zabbix/graph-real.png)
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`
After=network.target network-online.target
This takes care of a few things:
1. Waits until a network is available.
2. Sets the `AUDIODEV` and `AUDIODRIVER` environment variables required by `rec`.
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.
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
\ No newline at end of file
"name": "ant.sr",
"icons": [
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
......@@ -5,7 +5,7 @@ from __future__ import unicode_literals
AUTHOR = 'Anton Sarukhanov'
SITENAME = 'Anton Sarukhanov'
SITESUBTITLE = 'Full-Stack Developer'
#SITEURL = 'https://ant.sr/'
# SITEURL = 'https://ant.sr/'
PLUGINS = ['advthumbnailer']
......@@ -13,10 +13,40 @@ MD_EXTENSIONS = ['toc(permalink=🔗)', 'codehilite(linenums=False)', 'extra']
PATH = 'content'
THEME = 'theme'
STATIC_PATHS = ['media']
STATIC_PATHS = ['media', 'extra']
FILENAME_METADATA = '(?P<slug>.*)'
'extra/manifest.json': {'path': 'manifest.json'},
'extra/favicon.ico': {'path': 'favicon.ico'},
'extra/android-icon-36x36.png': {'path': 'android-icon-36x36.png'},
'extra/android-icon-48x48.png': {'path': 'android-icon-48x48.png'},
'extra/android-icon-72x72.png': {'path': 'android-icon-72x72.png'},
'extra/android-icon-96x96.png': {'path': 'android-icon-96x96.png'},
'extra/android-icon-144x144.png': {'path': 'android-icon-144x144.png'},
'extra/android-icon-192x192.png': {'path': 'android-icon-192x192.png'},
'extra/apple-icon.png': {'path': 'apple-icon.png'},
'extra/apple-icon-57x57.png': {'path': 'apple-icon-57x57.png'},
'extra/apple-icon-60x60.png': {'path': 'apple-icon-60x60.png'},
'extra/apple-icon-72x72.png': {'path': 'apple-icon-72x72.png'},
'extra/apple-icon-76x76.png': {'path': 'apple-icon-76x76.png'},
'extra/apple-icon-114x114.png': {'path': 'apple-icon-114x114.png'},
'extra/apple-icon-120x120.png': {'path': 'apple-icon-120x120.png'},
'extra/apple-icon-144x144.png': {'path': 'apple-icon-144x144.png'},
'extra/apple-icon-152x152.png': {'path': 'apple-icon-152x152.png'},
'extra/apple-icon-180x180.png': {'path': 'apple-icon-180x180.png'},
'extra/apple-icon-precomposed.png': {'path': 'apple-icon-precomposed.png'},
'extra/browserconfig.xml': {'path': 'browserconfig.xml'},
'extra/ms-icon-70x70.png': {'path': 'ms-icon-70x70.png'},
'extra/ms-icon-144x144.png': {'path': 'ms-icon-144x144.png'},
'extra/ms-icon-150x150.png': {'path': 'ms-icon-150x150.png'},
'extra/favicon-16x16.png': {'path': 'favicon-16x16.png'},
'extra/favicon-32x32.png': {'path': 'favicon-32x32.png'},
'extra/favicon-96x96.png': {'path': 'favicon-96x96.png'},
'extra/ms-icon-310x310.png': {'path': 'ms-icon-310x310.png'},
ARTICLE_URL = '{slug}'
ARTICLE_SAVE_AS = '{slug}/index.html'
......@@ -44,7 +74,7 @@ FEED_RSS = 'feed'
FEED_DOMAIN = 'https://ant.sr'
MENUITEMS = (('Resume', '/media/resume.pdf'),
('Code', 'https://git.xhost.io/users/anton/projects'),)
('Code', 'https://git.xhost.io/users/anton/projects'),)
PIWIK_URL = 'analytics.ant.sr'
......@@ -95,6 +95,14 @@ img.profile {
/* Headings */
h6 {
margin-top: 1em;
h1 {
font-size: 1.5em;
font-family: 'Headland One', serif;
......@@ -216,11 +224,10 @@ pre {
code {
max-width: 100%;
border: 1px solid #f28c26;
border-radius: .3em;
background: #eee;
border-radius: .15em;
border: 2px solid rgba(242, 140, 38, 0.55);
font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
padding: .1em;
padding: .05em .1em;
margin: .1em;
font-size: .9em;
display: inline-block;
......@@ -371,6 +378,7 @@ main {
main img {
max-width: 100%;
margin: .5em auto;
main .hero {
display: block;
......@@ -386,6 +394,10 @@ main header h2 ~ .time {
margin-top: -1em;
main li {
margin-top: .5em;
/********** Footer **********/
body > footer {
......@@ -5,6 +5,23 @@
<title>{% block title %}{{ SITENAME }}{% endblock title %}</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" sizes="57x57" href="/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
{% if FEED_ALL_ATOM %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_ALL_ATOM }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Full Atom Feed" />
{% endif %}