My second Pi hacking project: a noise monitor. The setup uses a 7-segment, 4-digit LED backpack from Adafruit, a red LED, USB mic and some clever/simple Python code.
Ambient noise is measured from the USB mic via a python ALSA interface. The measured level is displayed on the LED screen (up to 4 digits). Measurement is done in 0.01 second intervals, but the display only changes when a set threshold and time is exceeded. When the noise exceeds a set point, the red LED lights to indicate a noisy environment.
Setting up the 4-digit display was amazingly simple. Seriously. One only has to buy and soldier the backpack (leaving the heavy work to Adafruit), connect it to the Pi and call set_digit() from python to change the display state. Amazing.
It truly is. The Pi and ready-made components and tutorials enabled me to go from zero to this in just under a day of continuous learning.
#!/usr/bin/python
# Sound level indicator on a 7-segment 4-digit LCD, for Raspberry Pi
# Uses a USB mic to measure sound level
#
# https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/blob/master/Adafruit_LEDBackpack/Adafruit_7Segment.py
# https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/blob/master/Adafruit_LEDBackpack/Adafruit_LEDBackpack.py
# https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/blob/master/Adafruit_I2C/Adafruit_I2C.py
#
# Author Ando Roots <ando [at] sqroot [dot] eu> 2013
# http://sqroot.eu
import alsaaudio, time, audioop, datetime
from Adafruit_7Segment import SevenSegment
import RPi.GPIO as GPIO
class SoundMonitor:
# Red LED address
RED_LED = 23
# Light red LED when measured sound value exceeds the preset limit
WARNING_THRESHOLD = 800
# Tolerance as a percentage (0.5 is 50%)
# LCD output is changed when the measured value exceeds this tolerance
TOLERANCE = 0.15
# Minimum time in seconds between each LCD value change
MAX_CHANGE_FREQ = 0.5
# 7-segment display (run i2cdetect -y 1 to get the addr)
segment = SevenSegment(address=0x70)
# Alsaaudio instance
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE,alsaaudio.PCM_NORMAL, 'sysdefault:CARD=Set')
def __init__(self):
# LED
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.RED_LED, GPIO.OUT)
# Set attributes: Mono, 8000 Hz, 16 bit little endian samples
self.inp.setchannels(1)
self.inp.setrate(8000)
self.inp.setformat(alsaaudio.PCM_FORMAT_S16_LE)
self.inp.setperiodsize(160)
# Write a 4-digit number to the LCD
def write_number(self, value):
value = int(value)
if value > 9999:
print "Value %s is out of bounds, setting to 9999" % value
value = 9999
value = str(value).zfill(4)
self.segment.writeDigit(0, int(value[0]))
self.segment.writeDigit(1, int(value[1]))
self.segment.writeDigit(3, int(value[2]))
self.segment.writeDigit(4, int(value[3]))
return self
# Get the current sound level
def get_sound_level(self):
# Read data from device
l,data = self.inp.read()
if not l:
return 1
# Return the maximum of the absolute value of all samples in a fragment.
value = float(audioop.max(data, 2))
if value < 1:
value = 1.0
return value
# Main loop
def run(self):
print "Running the program - press Ctrl + C to exit."
time.sleep(2)
saved_value = 1
last_changed = time.time()
while True:
value = self.get_sound_level()
red_state = value > self.WARNING_THRESHOLD
print "Level: %s; Red: %s;" % (value, red_state)
if abs((saved_value - value) / value) > self.TOLERANCE:
if time.time() > last_changed + self.MAX_CHANGE_FREQ:
self.write_number(value) # Change LCD output
GPIO.output(self.RED_LED, red_state) # Light red LED?
saved_value = value
last_changed = time.time()
time.sleep(.001)
# Cleanup on exit
def __del__(self):
self.segment.disp.clear()
GPIO.cleanup()
# Run the program
monitor = SoundMonitor().run()
