, , , , , , , , , , ,

This post describes how to send an email when a SPLear™ sound level measurement module connected to a Raspberry Pi signals a loud noise. The SPLear measures sound pressure level (SPL) from ambient sound with a MEMS PDM microphone, and reports that measurement in several ways. We connected it to the Raspberry Pi using a UART, and so can choose one of several reporting modes from the SPLear, as well as capture raw sample values using a simple serial protocol. A simple program written in Lua monitors the SPL samples and decides when to send email.

This is part of our series of articles on the general subject of audio signal processing from air to information.

See the announcement post for a more complete description of the board. The key components are the bottom port microphone and the NXP LPC812 ARM CPU. The basic firmware implements an analog output driven by a PWM, an LED controlled by PWM to brighten when loud, a logic level UART, and access to most pins of the mic and the CPU for use with custom firmware.

Much of the firmware has been discussed and published in past posts on this blog, and the source code is available from a public repository. The firmware used here is checked in as [3b4f54236e].

Configuration for Sending Mail

Out of the box, Raspbian Linux doesn’t supply any email components. However, since all the useful pieces are available it is easy to add them. Naturally, you need to have your RPi connected to a network that can reach the internet, but without that connection, sending mail is not going to be very exciting.

I found a nice summary of two ways to configure the RPi here. I picked sSMTP since it is a lighter weight solution and I really don’t plan to make my RPi into any sort of email hub. Installation can be as simple as:

sudo apt-get install ssmtp mailutils mpack

but you might need to do sudo apt-get update before that works. I also did sudo apt-get upgrade since it has been a while since I first set up my RPi and it never hurts to be prompted to check for and install upgrades.

One limitation of using sSMTP is that it ignores /etc/aliases, and so can’t be used to automatically forward mail to root and other system accounts. If you need that capability in your RPi, then you will want to use something else for mail handling.

I’m using GMail for sending mail, and don’t want or need to be able to receive mail on the RPi. I have GMail configured for 2-factor authorization, so I used its application password feature to issue a password for my RPi to use. This is a 16-character password that is managed by Google, and is specific to that mail account from that single device. Generate one for your RPi, and don’t use the same one in any other device connected to GMail.

Once apt-get is done installing packages, edit the file /etc/ssmtp/ssmtp.conf as root and add or adjust settings as needed for your mail provider. This sample is appropriate for GMail, other providers will vary. Some of these settings will already be in the installed copy of the file, others are new.


Double check to make sure you replace “YOU” with your GMail login name and PASSWORD with your (application specific) GMail password.

Test with a simple email message:

echo Hi | mail -s "hello, world." you@example.com 

A short message should be delivered.

Sending mail when scared

The script in the previous post chattered any time enough noise was made, but the threshold was (deliberately) pretty small at about 4.5 dB. Just sitting near the mic, noises that much above background happen frequently.

So we will set the threshold to be a much louder noise, say 12 dB louder than recent samples.

We’d also like to get something more interesting than just a simple “eek” kind of message. So we will keep a buffer of SPL samples, and send some recent and future samples after the trigger condition is met.

Here’s one way to to this:

#! lua

address = "user@example.com"

-- today's date in (almost) ISO-8601 format
local function isodate()
  return os.date"%Y-%m-%d %H:%M:%S"

-- Send an email to addr with subject and body. If body
-- is nil then just repeat the subject. This assumes that
-- a "mail" command is available with the usual options.
local function email(addr, subj, body)
  body = body or subj
  local mail = ("mail -s '%q' %s"):format(subj,addr)
  local b = assert(io.popen(mail, "w"))
  b:write([[I heard a noise on ]], isodate(), "\r\n\r\n")
  b:write(body, "\r\n\r\n")
  b:write"(This an automated message.)\r\n"

-- Implement a buffer of samples. For simplicity, let the
-- index increment without bound. Note that this will have
-- a problem after 2^53 samples, which is about 30 million
-- years at 10 samples per second.
local recent = {
    local n = self.n
    self[n] = spl
    self[n-30] = nil
    self.n = n + 1
    local t = {}
    for i=self.n-31,self.n-1 do
      if self[i] then t[#t+1] = self[i] end
    return table.concat(t,", ")

-- Quick and dirty way to force the UART to have the
-- tty driver settings we need for clean raw access
-- to the port.
os.execute("stty -F /dev/ttyAMA0 115200"
        .." pass8 raw -iexten"
        .." -echo -echoe -echok -echoke -echoctl")

-- Open the UART for both reading and writing, and send
-- the SPLear command to go into raw SPL sample mode. The
-- output must be unbuffered because the current SPLear
-- firmware would revert to 0.5 Hz summary mode if it
-- receives a newline.
local port = assert(io.open('/dev/ttyAMA0','r+'))
port:setvbuf"no"   -- no output buffer at all
port:write"S"      -- so this write is immediate

-- Loop reading the SPL samples and watching for loud
-- noises, keeping a log of recent samples as we go.
-- When a loud noise is heard, keep logging for a bit
-- before sending email. Include the log in the email.
local spl, spl0
local triggered = false
local eek = ""
while true do
  local line = port:read"*l" -- read a single line
  spl = tonumber(line)
  if spl then
    spl = 0.75 * spl
    io.write("SPL "..(triggered and "TRG " or ""), spl, "\r")

    -- Watch for the trigger condition: 12 dB louder
    if spl0 and (spl - spl0 > 12.) then
      triggered = 25
      eek = "EEK! "..spl.." dB"

    -- If we were triggered, count down by samples
    if triggered then
      triggered = triggered - 1
      -- If done, send the mail
      if triggered == 0 then
        triggered = false
        local body = eek.."\r\n"..recent:text()
        print("email", address, eek, body)
        email(address, eek, body)
    spl0 = spl

This script has a number of obvious shortcomings. First, nothing is configurable. Edit the script to set the destination address, the trigger threshold, the trigger condition, and the boiler plate text in the message body.

Second, it has a bug where multiple triggers within the window will cause it to treat the last one received as the trigger. The usual behavior would be to treat the first as the trigger. Fixing this is left as an exercise for the reader.

Finally, the fixed window of 30 samples is presented in the email body as a single line of text with comma separated SPL values. That line really should be broken at 75 columns or so, or replaced entirely by a cute graph in an attached image. Fixing that is left as an exercise for the reader, although I’m tempted to do the image in a future post.

It does use some system components and other tricks.

It uses stty to properly configure the UART before it opens and uses it. It would be possible to more completely wrap the Linux serial port API and call it directly from Lua, but the stty command is convenient and works.

It uses the /usr/bin/mail command to actually send the mail, so you can set the variable address at the top of the script to any legal email address (or addresses) understood by that command. It passes the message body in via stdin, and terminates the body by closing the pipe. This approach doesn’t make using MIME attachments easy, but switching from mail to mpack would be simple to do and that would enable easy attachment creation.

It uses an extremely simple object to implement the sample buffer. The implementation uses a count of samples added, and simply sets samples older than the planned buffer length to nil. Since at 10 Hz, the integer index will overflow the range of exact integers in a Lua number after 30 million years, we don’t bother to worry about that case.

Raspberry Pi Background

The Raspberry Pi (or RPi for short) is a very inexpensive single board computer that can run Linux, and supports USB, ethernet, and HDMI for both video and audio output. My RPi is booting Raspbian, is headless, and has been configured to speak as little as possible on its UART. The earlier post documented the configuration steps and the connection to the SPLear.

In short, wire it as follows:

RPi GPIO header  SPLear J1
---------------  -----------
 2 (5V)          J1.2 (VCC)
 6 (GND)         J1.1 (GND)
 8 (UART0_TxD)   J1.6 (RXD)
10 (UART0_RxD)   J1.7 (TXD)

I used sudo raspi-config to turn off the console serial port, which only needs to be done once, and requires a reboot to take effect.

From software, the UART is known to Linux as /dev/ttyAMA0.

At physical level, the SPLear is speaking asynchronous serial at 115200 baud, 8 bit, no parity, 1 stop bit, with 3.3V logic level signals.

We configure the port for the required baud rate, parity, and also to disable most of the “interesting” processing that the Linux tty device driver performs:

pi@treepi ~ $ stty -F /dev/ttyAMA0 115200 pass8
pi@treepi ~ $ stty -F /dev/ttyAMA0 raw -iexten
pi@treepi ~ $ stty -F /dev/ttyAMA0 -echo -echoe -echok -echoke -echoctl 

Turning off echo and the other port processing is sensible for a port wired to a device. Most devices don’t like the echo, and all the other processing is only asking for trouble if binary data will be transferred. Notice that the SPLear can send binary data if the ability to deliver µLaw audio is used.

What Now?

The next step is to continue to find interesting things to do with the measured SPL level, which will certainly be the subject of future posts. Watch this space!

Please let us know what you think about the SPLear!

If you have a project involving embedded systems, micro-controllers, electronics design, audio, video, or more we can help. Check out our main site and call or email us with your needs. No project is too small!

+1 626 303-1602
Cheshire Engineering Corp.
120 W Olive Ave
Monrovia, CA 91016

(Written with StackEdit.)