Key goals summary:
- Improve aesthetics
- Get voice responses telling me my location upon a voice command
After the success of the Arc Raspberry I wanted to improve my design, make it more wearable and make it cooler. Again, inspired by Tony Stark I thought of making my own J.A.R.V.I.S.
After some milling around on the internet reading up on speech recognition I came across Jasper.
My imagination lit up with thoughts of a robotic voice telling me where I am, I could simply use the same code from the MKI and implement alongside Jasper – it seemed so simple; the truth, however…
I opted again for a Raspberry Pi A+, this time with a bigger case hoping to fit in more components, I also changed the choice of GPS module to a HAT based solution, rather than the USB based solution of the first.
As this time the device was going to speak instead of have visual clues as to its current location I decided the colour should remain the same – blue being the obvious choice.
- Battery Charger
- Raspberry Pi A+
- GPS Hat
- Micro USB cable
- USB sound card
- Memory Card
- Another old fan from a laptop cooler
- Various other bits again
These blog posts are to serve as a demonstration/log of all my builds rather than a build guide, so please forgive any brevity in description of the work performed making these projects. One day I may decide to organise all my notes etc. and make a full build guide.
Until then, onward:
With the GPS HAT I could put the PiGlow module onto it and get in a more central position, rather than having it sit on the Pi’s GPIO pins directly; which on the MKI made it sit off centre of the faceplate. The other advantage to having the HAT is no USB connection required or cabling to handle with it, reducing the size further – I really wanted to be able to wear this one out and about.
At one point I had a run in with the soldering iron:
Since Jasper requires the ability to listen as well as speak I required a USB sound card to get the microphone and speaker outputs, thanks to their guide over at the Jasper website I was able to get this set up relatively easily. The overall installation of Jasper takes quite some time so much caffeine was consumed during.
As can be seen above the MKI was rather… rustic in its construction; which while apt considering the conditions Stark made his first reactor in, could be improved. You may have noticed ‘Sugru’ up there in the components section, this stuff is like magic; its a soft adhesive rubber like material that can be moulded and stuck to things – once exposed to air it begins to solidify and adhere to whatever material you attach it to. More info here.
I used the Sugru to cover up some components, such as the battery charger module, as well as assist with the overall construction and strength of the device.
Once the hardware was assembled the device was ready to rumble for the software:
With some configuration of Jasper and choosing the voice I wanted, it was time to move onto making a module that contained my GPS code that would be called and activated upon when a certain word was said, for instance ‘location’. I also wanted to get a proper way to shut down the MKII as the MKI I turned off simply by flicking the switch on the battery circuit (not a good idea, can corrupt the SD Card and/or the Raspbian installation).
As I said before I thought this would be simple, it was not. It took once again many hours of chasing red herrings and actual bugs before finally getting it to work. Fortunately now I knew the main functionality worked I didn’t have to wander around in the cold trying to get a result. Instead I was sat at my window with the device waiting for a GPS fix so I could test it worked. After what seemed an eternity of saying “Jasper… Location” – it finally worked and spoke my location to me; it was a moment of pure elation.
The main module code:
# -*- coding: utf-8-*- #import modules import re, sys, string, os, gps from time import sleep import random import re #key words for the main jasper program to use for recognition WORDS = ["LOCATION"] #alphabet for grid reference upper = 'ABCDEFGHIJKLMNOPQRSTUVWX' #locations stored as grid references Bury = ["JO02IG", "JO02IF", "JO02JG", "JO02JF"] Thetford = ["JO02IK", "JO02JK", "JO02IJ", "JO02JJ"] Cambridge = ["JO02AF", "JO02AE", "JO02BE", "JO02BF", "JO02CE", "JO02CF"] #the main function that is called upon voice recognition def handle(text, mic, profile): location="nosignal" messages=("") sessiongps = gps.gps("localhost", "2947") sessiongps.stream(gps.WATCH_ENABLE | gps.WATCH_NEWSTYLE) while True: #gather statistics from tpv (latitude + longitude) then write both to respective variables report = sessiongps.next() if report['class'] == 'TPV': if hasattr(report, 'lat'): dec_lat=`report.lat` if report['class'] == 'TPV': if hasattr(report, 'lon'): dec_lon=`report.lon` #/below code source: http://ham.stackexchange.com/questions/221/how-can-one-convert-from-lat-long-to-grid-square adj_lat = float(dec_lat) + 90.0 adj_lon = float(dec_lon) + 180.0 mhloc="" grid_lat_sq = upper[int(adj_lat/10)]; grid_lon_sq = upper[int(adj_lon/20)]; grid_lat_field = str(int(adj_lat%10)) grid_lon_field = str(int((adj_lon/2)%10)) adj_lat_remainder = (adj_lat - int(adj_lat)) * 60 adj_lon_remainder = ((adj_lon) - int(adj_lon/2)*2) * 60 grid_lat_subsq = upper[int(adj_lat_remainder/2.5)] grid_lon_subsq = upper[int(adj_lon_remainder/5)] mhloc = grid_lon_sq + grid_lat_sq + grid_lon_field + grid_lat_field + grid_lon_subsq + grid_lat_subsq #/end #check grid reference against stored lists and assign resulting location to a variable if mhloc in Bury: location="Bury Saint Edmunds" elif mhloc in Thetford: location="Thetford" elif mhloc in Cambridge: location="Cambridge" else: location="unknown" break #set location variable to nosignal if no data is recieved from the GPS location="nosignal" #break out of the loop once data has been gathered from the GPS break #speech output for unknown locations if location == "unknown": #list of different messages to say for variety messages = ["I'm not entirely sure where you are, Sir.", "I don't know this location, Sir. Off travelling are we?"] #choose a message from above at random message = random.choice(messages) #output the message to the text to speech engine mic.say(message) #speech output for no signal scenario elif location == "nosignal": #list of different messages to say for variety messages = ["I can't get a signal, Sir.", "There doesn't appear to be any data coming from the GPS module, Sir, may I suggest waiting for a fix?" ,"There is no signal from a GPS satallite; either the GPS module has no fix, or you're no longer on earth, Sir."] #choose a message from above at random message = random.choice(messages) #output the message to the text to speech engine mic.say(message) #speech output for known locations - the location retrieved from the lists is put directly into the text to speech engine as a string else: #list of different messages to say for variety messages = ["You're in " + str(location) + "Sir.", "You appear to be in " + str(location) + "Sir."] #choose a message from above at random message = random.choice(messages) #output the message to the text to speech engine mic.say(message) #checks the key words are valid after being interpreted by the speech to text engine def isValid(text): return bool(re.search(r'\blocation\b', text, re.IGNORECASE))
The shut down module code:
# -*- coding: utf-8-*- #import modules import re, sys, string, os from time import sleep import random #key words for the main jasper program to use for recognition WORDS = ["SHUTDOWN"] #the main function that is called upon voice recognition def handle(text, mic, profile): #list of different messages to say for variety messages = ["Shutting the Arc Raspberry down, Sir.", "Turning off, Sir.", "Good night, Sir.", "Have a good day, Sir"] #choose a message from above at random message = random.choice(messages) #output the message to the text to speech engine mic.say(message) #sleep for 5 seconds to ensure the text to speech output has completed sleep(5) #issue a shutdown command to the operating system os.system('sudo halt') #checks the key words are valid after being interpreted by the speech to text engine def isValid(text): return bool(re.search(r'\bshutdown\b', text, re.IGNORECASE))
This code runs under the main Jasper module so no need to run from rc.local, however; the code to make the PiGlow turn on needs to be in there as well as the code for starting the GPS again:
sleep 15 sudo killall gpsd sleep 15 sudo gpsd /dev/ttyAMA0 -F /var/run/gpsd.sock python /home/pi/MKII/arcblue.py &
Jasper itself is called from crontab: (‘crontab -e’ command) and the code within crontab:
@reboot python /home/pi/jasper/jasper.py &
@reboot means whenever the OS boots, again the & symbol makes the script run silently.
And the code for making the device glow blue is rather simple:
from time import sleep from PyGlow import PyGlow pyglow = PyGlow() pyglow.all(0) try: # Loop until users quits with CTRL-C while True : pyglow = PyGlow() pyglow.color("blue", 255) pyglow.color("white", 25) sleep(1) except KeyboardInterrupt: # Reset GPIO settings pyglow.all(0)
The final product:
The look of the final product in action:
Once again I was happy with what I accomplished. My thoughts moved forward to what the MKIII would be, this version was wearable – but still too big to practically wear for the theatre release of Age of Ultron, I needed something smaller.
Along came The Pi Zero…