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]
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).