audio, LPC800, LPC812, microphone, NXP, PDM, Raspberry Pi, RPi, SPL, SPLear
This post demonstrates using a SPLear™ sound level measurement module with a Raspberry Pi from a simple Lua script. 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.
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 discussed here and running the demo seen in the video is checked in as [3b4f54236e].
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 previous 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)
sudo raspi-config to turn off the console serial port, which only needs to be done once, and requires a reboot to take effect.
The RPi has a single UART that is readily accessible from the GPIO header and known to Linux as
Serial ports in Linux tend to retain their baud rate and other settings unless some program explicitly changes them. This will allow us to write a simple program to talk to the SPLear without needing to get bogged down in the serial port API. Instead, we can use the
stty utility to configure the port as needed before we run our program.
The previous post made the minimum configuration required to receive text from the SPLear. We need to refine that configuration next.
Configuring the UART for the SPLear
The data protocol is deliberately very simple, as well as intended to be usable from a human with a terminal. It accepts a number of single-character commands which immediately display some information or change the operating mode. Any unrecognized character will cause it to return to a default configuration of emitting a summary line every 2 seconds.
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
The extra processing is really handy for an interactive user, as it provides for Ctrl+C to interrupt a job, Ctrl+Z to suspend a job, as well as the common line editing characters so that basic editing is available to all programs because it was implemented in the terminal device driver.
However, none of that is useful or even wanted on a connection used for a serial protocol. In this case, the setting that echoes typed characters back at the terminal causes the SPLear to revert to its default message format.
Controlling the SPLear
If you are talking to the SPLear’s UART directly from a terminal, you can send it a
? and it will display a helpful table of the single-character commands it understands:
SPLear prints: s: peak+SPL at 0.5 Hz S: SPL 10 Hz e: Eek! n: no printing R: muLaw at 8152 Hz
This help text doesn’t mention that any other character (except for any that are undocumented) is treated the same as
s. This is why it is important that the serial driver not echo any characters.
Using SPL in the RPi
e mode prints a line any time the SPL was suddenly louder than the previous reading. Let’s emulate that in a simple program written in Lua running in the RPi, based on the stream of every SPL sample provided by the
#! lua local port = assert(io.open('/dev/ttyAMA0','r+')) port:setvbuf"no" -- no output buffer at all port:write"S" -- so this write is immediate local spl, spl0 = 0,0 while true do local line = port:read"*l" -- read a single line spl = tonumber(line) if spl then if spl - spl0 > 6 then print("EEK! "..spl, os.date"%Y-%m-%d %H:%M:%S") elseif spl0 - spl > 6 then print("ssh "..spl, os.date"%Y-%m-%d %H:%M:%S") end spl0 = spl end end
Note: My RPi has Lua version 5.1.5 installed. I don’t happen to recall whether that was already in the Raspbian distribution, or whether I used a package manager to install it. The logic here is pretty simple, so it should be readily translated to Perl or Python if those are more to your taste. I like Lua.
The script assumes that
stty has been used to make the baud rate and parity right, and make the port driver sufficiently raw and non-echoing. A more complete program would do that for you.
The script opens the UART for reading and writing, and turns off write buffering. It then sends the
S command to get 10 Hz SPL samples in decimal ASCII, one per line, from the SPLear.
Once configured, it reads a line at a time from the SPLear. It will actually see 20 lines per second since the SPLear is sending both CR and LF characters to end the line. We could adjust the
stty command to ignore the CR characters, but it is also easy to ignore the blank lines.
Each line is converted to a number, and then compared to the value from the last SPL window. If it changed by more than 6 counts (about 4.5 dB SPL) from the last time, one of two messages is printed along with a time stamp in ISO 8601 order.
pi@treepi ~ $ lua eek.lua EEK! 25 2015-04-30 15:46:59 EEK! 46 2015-04-30 15:47:01 ssh 27 2015-04-30 15:47:01 EEK! 43 2015-04-30 15:47:32 ssh 27 2015-04-30 15:47:32
The script has the small defect that it treats the very first time around the loop as more than 6 different. This defect has been left as an exercise for the reader…
For testing, I have a second SPLear connected to a terminal. When I make a sudden noise, both the native
e mode and this emulation in Lua report the event, and usually with SPL values within one count of each other.
Note that the builtin
e mode does not report the quiet edge of events.
What about performance?
A casual observer might notice that this Lua script is an infinite loop, comparing each SPL value to a previous reading. Isn’t that a big waste of CPU time on the RPi?
Not actually at all. The Linux kernel is really good at scheduling processes that are blocked waiting for I/O operations to finish, and waiting for a character from a terminal device is just about the oldest example of blocking I/O in the Linux and Unix world. This is what every shell prompt is spending most of its time doing.
Our sample script is no different.
When it does get some input, the actual process does very little in any case. I checked my RPi with the script running using
top, and the load average remains hovering around 0.01, which is about as close to a completely unloaded system as you can ever expect to see. Furthermore, the process actually running the Lua script almost never appears in the list of the top dozen processes.
The next step is to continue to use that SPL level to do something, which will certainly be the subject of future posts. Watch this space!
Let us know what you think!
(Written with StackEdit.)