Phonos: a voice input device. part II: code
Having already jibber jabbered about building Phonos, and then blabbing some more by recording an HPR episode about Phonos, I figured I should write a bit about the software that makes Phonos work.
Aside from utilizing some pre-existing software, it was necessary to write a few bits of software, as well as adding an update to existing software in order to get things to work the way I wanted.
Speech Recognition with Blather
For speech recognition, Phonos is running Blather. With the Phonos project, I wanted Blather to quit after recognizing a spoken command. In hindsight, this feature wasn't really needed, but it wasn't too difficult to hack the Blather source and add a command-line flag to accomplish the desired action.
And thus -x, --execute-once execute one command and then quit
became a um… 'feature'
As part of the Blather config, it is possible for a user to set a script to run when spoken words are matched or not matched to the user defined "spoken word" commands. In my first speech recognition device, a valid match would run a basic shell script to lauch a gstreamer utility to play an "affirmative" sound. An invalid match would result in a "negative" sound file being played.
For Phonos, I wanted to expand upon the options for playing affirmative and negative audio files. To accomplish this, two directories were created: "affirmative" and "negative", and when needed, a random audio file will be played from the appropriate directory.
Python code to play a random file from a directory with a gstreamer utility
#!/usr/bin/env python
import sys
import os
import random
#assume the first arg is the path to a directory of audio files
dir = sys.argv[1].replace(" ","\ ")
print dir
#get a list of the files in the directory
files = os.listdir(dir)
#pick a file at random
random_file = random.choice(files)
#join the directory and the file
audio_to_play = os.path.join( dir, random_file )
#get the absolute path of the dir/random_file
abs_file = os.path.abspath(audio_to_play)
print abs_file
#an audio command to play the file (gstreamer tools required)
cmd = "gst-launch-0.10 playbin uri=file://%s" % (abs_file)
#run the command
os.system(cmd)
Watching the Phone Cradle
During the build, the phone cradle was connected to GPIO on the CHIP, and determining the state of the cradle was a matter of:
- setting up a GPIO pin as input
- read the GPIO's value file
- do stuff when the value changes
In this case, that stuff is "launch blather with the -x flag when the cradle is lifted"
and "kill all blather instances when the cradle goes down"
cradle.py
#!/usr/bin/env python
import subprocess
import time
import os
#what number gpio?
num = "1019"
#what is the cradle "down" value?
DOWN = "0"
#control looping
looping = True
#first cradle lift
cradle_down = True
#get the pin value
def get_pin_value():
f = open("/sys/class/gpio/gpio"+num+"/value")
val = f.read().strip()
f.close()
return val
#what is the path to the gpio?
gpio_path = "/sys/class/gpio/gpio"+num
#is the pin in use?
if os.path.exists(gpio_path):
#unexport the pin by writing the pin number to /sys/class/gpio/export
f = open("/sys/class/gpio/unexport",'w')
f.write(num)
f.close()
#export the pin by writing the pin number to /sys/class/gpio/export
f = open("/sys/class/gpio/export",'w')
f.write(num)
f.close()
#get the initial value of the pin
pin_previous_value = get_pin_value()
pin_initial_value = pin_previous_value
#set the direction
f = open("/sys/class/gpio/gpio"+num+"/direction", "w")
f.write("in")
f.close()
looping = True
#loop and check the pin
while looping:
time.sleep(0.1)
new_value = get_pin_value()
if pin_previous_value != new_value:
#the value has changed, update the previous value
pin_previous_value = new_value
#is the change an up or a down?
print "new == DOWN", new_value,DOWN
if new_value == DOWN:
cradle_down = True
print 'cradle down'
#kill all instances of blather
print "pkill -f blather"
subprocess.Popen('pkill -f blather', shell=True)
else:
print 'cradle up'
#cradle is up!
if cradle_down:
cradle_down = False
#run blather as user chip
cmd = "su - chip -c '/home/chip/Projects/blather/Blather.py -x'"
print cmd
subprocess.Popen(cmd, shell=True)
systemd unit-file
Not as easy as adding a line to /etc/rc.local, and quite a bit easier than writing a script for /etc/init.d/ :)[Unit]
Description=watch the phone cradle switch for changes
After=network.target
[Service]
ExecStart=/opt/cradle.py
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
Certainly not least
The main purpose of Phonos is to convert voice commands to MQTT commands that will control the some lights in my home. To make this process easier, I needed a simple MQTT client to publish Topics and Messages to Cronos, my MQTT broker.
#!/usr/bin/env python
import sys
import paho.mqtt.client as mqtt
host = 'cronos'
#default to testing orb
topic = 'light/orb1'
message = "on"
#get topic and message from argv
if len(sys.argv) == 3 :
topic = sys.argv[1]
message = sys.argv[2]
elif len(sys.argv) == 2 :
topic = sys.argv[1]
message = ''
#create an MQTT client, connect, and publish
mclient = mqtt.Client()
mclient.connect(host, 1883, 60)
mclient.publish(topic, message)
Sweet! Now I can issue commands to:
-
turn all lights on
./cronos-mqtt lights on
-
fade a single light named 'orb' on
./cronos-mqtt light/orb fade/on
-
fade all the lights off over 60 seconds
./cronos-mqtt lights fade/off/60
-
And all of the other illumination functions available to Orb lights.
cheers! :)
jezra