Pi Hacking #2

Noise Level Monitor

Published on . Takes less than 1 minute to read.

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.

SoundMonitor.py
#!/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()

View gist on GitHub

Hardwarepythonprojectbreadboardraspberrypi