Raspberry PI controlled Light Clapper

This is one of my first Raspberry PI projects, and consists of a Raspberry connected to a microphone which detects an hand clap and then controls via GPIO a relay that powers the lamp.

The code used for the detecting the clap (which is not perfect, because it analyzes only the volume of the microphone) is this:
[actually this is not the same code used in the video above, it has been improved, in fact the delay between the two claps is reduced in this script]

ATTENTION: UGLY CODE FOLLOWS.
The code has been written only to provide a proof-of-concept project, and it’s written joining various pieces of code found in the Internet, so don’t expect this code to be bug free or well written. It is not.
You should use it to understand the way it works and to write something better or to adapt it to your own project.
#!/usr/bin/python

import urllib
import urllib2
import os, sys
import ast
import json
import os
import getpass, poplib
from email import parser
import RPi.GPIO as GPIO

file = 'test.flac'

import alsaaudio, time, audioop

class Queue: 
    """A sample implementation of a First-In-First-Out
       data structure."""
    def __init__(self):
        self.size = 35
        self.in_stack = []
        self.out_stack = []
        self.ordered = []
        self.debug = False
    def push(self, obj):
        self.in_stack.append(obj)
    def pop(self):
        if not self.out_stack:
            while self.in_stack:
                self.out_stack.append(self.in_stack.pop())
        return self.out_stack.pop()
    def clear(self):
        self.in_stack = []
        self.out_stack = []
    def makeOrdered(self):
        self.ordered = []
        for i in range(self.size):
            #print i
            item = self.pop()
            self.ordered.append(item)
            self.push(item)

        if self.debug:
            i = 0
            for k in self.ordered:   
                if i == 0: print "-- v1 --"
                if i == 5: print "-- v2 --"
                if i == 15: print "-- v3 --"
                if i == 20: print "-- v4 --"
                if i == 25: print "-- v5 --"

                for h in range(int(k/3)):
                    sys.stdout.write('#')
                print ""
                i=i+1

    def totalAvg(self):
        tot = 0
        for el in self.in_stack:
            tot += el

        for el in self.out_stack:
            tot += el

        return float(tot) / (len(self.in_stack) + len(self.out_stack))

    def firstAvg(self):
        tot = 0
        for i in range(5):
            tot += self.ordered[i]
        return tot/5.0

    def secondAvg(self):
        tot = 0
        for i in range(5,15):
            tot += self.ordered[i]
        return tot/10.0

    def thirdAvg(self):
        tot = 0
        for i in range(15,20):
            tot += self.ordered[i]
        return tot/5.0

    def fourthAvg(self):
        tot = 0
        for i in range(20,30):
            tot += self.ordered[i]
        return tot/10.0

    def fifthAvg(self):
        tot = 0
        for i in range(30,35):
            tot += self.ordered[i]
        return tot/5.0

def wait_for_sound():
    GPIO.setmode(GPIO.BOARD)    
    GPIO.setup(11, GPIO.OUT)

    # Open the device in nonblocking capture mode. The last argument could
    # just as well have been zero for blocking mode. Then we could have
    # left out the sleep call in the bottom of the loop
    card = 'sysdefault:CARD=Microphone'
    inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE,alsaaudio.PCM_NONBLOCK, card)

    # Set attributes: Mono, 8000 Hz, 16 bit little endian samples
    inp.setchannels(1)
    inp.setrate(16000)
    inp.setformat(alsaaudio.PCM_FORMAT_S16_LE)

    # The period size controls the internal number of frames per period.
    # The significance of this parameter is documented in the ALSA api.
    # For our purposes, it is suficcient to know that reads from the device
    # will return this many frames. Each frame being 2 bytes long.
    # This means that the reads below will return either 320 bytes of data
    # or 0 bytes of data. The latter is possible because we are in nonblocking
    # mode.
    inp.setperiodsize(160)

    last = 0
    max = 0
    clapped = False
    out = False
    fout = open("/var/www/cgi-bin/clapper/killme", "w")
    fout.write('0n');
    fout.close()

    queue = Queue();
    avgQueue = Queue();

    n = 0; 
    n2=0;
    while True:
        fin = open("/var/www/cgi-bin/clapper/killme", "r")
        if fin.readline() == "1n":
            break;
        fin.close()
        # Read data from device
        l,data = inp.read()
        if l:
            err = False
            volume = -1
            try:
                volume = audioop.max(data, 2)
            except: 
                print "err";
                err = True
            if err: continue

            queue.push(volume)
            avgQueue.push(volume)
            n = n + 1
            n2 = n2 + 1
            if n2 > 500:
                avgQueue.pop()

            if n > queue.size:
                avg = avgQueue.totalAvg()
                print "avg last fragments: " + str(avg)

                low_limit = avg + 10
                high_limit = avg + 30

                queue.pop();
                queue.makeOrdered();
                v1 = queue.firstAvg();
                v2 = queue.secondAvg();
                v3 = queue.thirdAvg();
                v4 = queue.fourthAvg();
                v5 = queue.fifthAvg();
                if False:
                    print "v1: "+str(v1)+"n"
                    print "v2: "+str(v2)+"n"
                    print "v3: "+str(v3)+"n"
                    print "v4: "+str(v4)+"n"
                    print "v5: "+str(v5)+"n"
                #if v1 < low_limit: print str(n)+": v1 ok"                 #if v2 > high_limit: print str(n)+": v2 ok"
                #if v3 < low_limit: print str(n)+": v3 ok"                 #if v4 > high_limit: print str(n)+": v4 ok"
                #if v5 < low_limit: print str(n)+": v5 ok"

                if v1 < low_limit and v2 > high_limit and v3 < low_limit and v4 > high_limit and v5 < low_limit:
                    print str(time.time())+": sgaMED"
                    out = not out
                    GPIO.output(11, out)
                    queue.clear()
                    n = 0

        time.sleep(.01)

wait_for_sound()

 


The code was found on the Internet and then adapted for my purposes. It uses a standard USB microphone, and should work with most of the linux-compatible USB mics (I think even webcam integrated mics).

Blog

Questo è un posto di prova. Ho pensato che visto che anche chi non ha nulla da dire tiene un blog, anch’io avrei potuto farlo. Non sono tanto sicuro che sia vero però.