Introduction: Control Raspberry Pi GPIO With Amazon Echo and Python

The main goal of this instructable to use Alexa's voice commands with an Amazon Echo to control the GPIO on a Raspberry Pi using Python. This instructable has been adapted from the Memory Game example at Flask-Ask: A New Python Framework for Rapid Alexa Skills Kit Development.

An Echo is not required but is highly recommended. This is a very simple example but demonstrates how easy it is to expose the Raspberry Pi's functionality to Alexa with just a few lines of Python.

The main reason I am writing this is because I could not find a similar guide elsewhere and felt the functionality was cool enough that it deserved one. There are currently several different ways of adding custom Smart Home programming to Alexa but as far as I know this is the first method using Raspberry Pi and Python.

Step 1: Initial Setup

I used a Raspberry Pi 3 and a fresh Raspbian Jessie-lite image downloaded from https://www.raspberrypi.org/downloads/raspbian/

Two terminal sessions will be needed so using SSH to access the Pi is recommended. Once logged in, enter the following commands to install the required packages and python libraries:

sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get install python2.7-dev python-dev python-pip
sudo pip install Flask flask-ask

Step 2: Setup Ngrok

I couldn't think of a better explanation so here is a quote from Amazon's guide I linked earlier:

ngrok is a command-line program that opens a secure tunnel to localhost and exposes that tunnel behind an HTTPS endpoint. ngrok makes it so Alexa can talk to your code right away. Follow the next three steps to generate a public HTTPS endpoint to 127.0.0.1:5000.

Visit https://ngrok.com/download and get the latest Linux ARM release as a zip and unzip inside the home directory:

unzip /home/pi/ngrok-stable-linux-arm.zip

Next, run it from the command line with:

sudo ./ngrok http 5000

Your screen should look like the image above. Note the 'Forwarding' URL that starts with https, it will be used later.

Note: Unfortunately the ngrok URL changes every time the service is started so it is not a permanent solution if you are trying to run this full time. I'd recommend a service like Yaler or Page Kite if you need a more permanent URL to use with your new Skill.

Step 3: Python Script

Open a new terminal session and create a new python file named gpio_control.py:

nano gpio_control.py

Copy/paste the following code into the new file:

from flask import Flask
from flask_ask import Ask, statement, convert_errors import RPi.GPIO as GPIO import logging

GPIO.setmode(GPIO.BCM)

app = Flask(__name__) ask = Ask(app, '/')

logging.getLogger("flask_ask").setLevel(logging.DEBUG)

@ask.intent('GPIOControlIntent', mapping={'status': 'status', 'pin': 'pin'}) def gpio_control(status, pin):

try: pinNum = int(pin) except Exception as e: return statement('Pin number not valid.')

GPIO.setup(pinNum, GPIO.OUT)

if status in ['on', 'high']: GPIO.output(pinNum, GPIO.HIGH) if status in ['off', 'low']: GPIO.output(pinNum, GPIO.LOW)

return statement('Turning pin {} {}'.format(pin, status))

Save and close the file.

Start the flask server with:

sudo python gpio_control.py

Leave both ngrok and gpio_control.py running while we setup the new skill in AWS...

Step 4: AWS Account

First create or login to your AWS Developer Account and open your list of Alexa skills.

Step 5: Alexa Skill - Information

Set the Skill Name to 'GPIO Control' and the Invocation Name to the word(s) you want to use to activate the skill. Click 'Next' to continue.

Step 6: Alexa Skill - Interaction Model

Copy/paste the following into the 'Intent Schema' box:

{
"intents": [{

"intent": "GPIOControlIntent",

"slots": [{

"name": "status",

"type": "GPIO_CONTROL"

}, {

"name": "pin",

"type": "AMAZON.NUMBER"

}]

}]

}

Next, click 'Add Slot Type' and under 'Enter Type' write in 'GPIO_CONTROL'. Under 'Enter Values' write:

on
off

This is just a simple example. 'high', 'low' or pretty much any other word could be added.

Copy/paste the following into the 'Sample Utterances' box:

GPIOControlIntent to turn pin {pin} {status}

Click on 'Save' and then 'Next'.

Step 7: Alexa Skill - Configuration

Select 'HTTPS' as the Service Endpoint Type and select a region.

Enter the ngrok URL from step 2 and click 'Next'. The URL should be something like:

https://ed6ea04d.ngrok.io

Step 8: Alexa Skill - SSL Certificate

Select the 'My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority' option and click 'Next'.

Step 9: Alexa Skill - Testing

If everything was setup correctly you should now see a screen similar to the first image above. The Skill is now enabled and can be accessed through any Amazon Echo device(or at http://echosim.io/) that is connected to your AWS Developer Account using the following syntax:

Alexa, tell Raspberry Pi to turn pin {pin number} {on or off}

For example:

Alexa, tell Raspberry Pi to turn pin twenty one on

If you are having any issues, the simplest way to test the Skill would be to use the Voice or Service Simulator found on the Test page. Try entering the following into the 'Enter Utterance' box under the Service Simulator:

 turn pin twenty one on

Then hit 'Ask GPIO Control' to run the test.

Step 10: Wrapping Up

After testing there is no need to continue through the Skill setup any further as the rest applies to releasing the skill publicly. As far as I can tell there is nothing stopping someone from running this continuously in a 'development' mode for personal use though... My ultimate goal is to use this to integrate Alexa into my existing Raspberry Pi-based Smart Home setup so that everything can be controlled hands-free.

This was my first Instructable so any feedback is appreciated. If you have any questions or trouble setting this up I will do my best to help. Good luck!

Step 11: Bonus Points - Using Words Instead of Pin Number

If you've followed all of the above steps and want to add some more functionality you can create an intent that can be called with actual words like 'fan' or 'light' by following the steps below.

Add the following code to gpio_control.py:

@ask.intent('LocationControlIntent', mapping={'status': 'status', 'location': 'location'})
def location_control(status, location):

locationDict = { 'fan': 12, 'light': 21 }

targetPin = locationDict[location]

GPIO.setup(targetPin, GPIO.OUT)

if status in ['on', 'high']: GPIO.output(targetPin, GPIO.HIGH) if status in ['off', 'low']: GPIO.output(targetPin, GPIO.LOW)

return statement('Turning {} {}!'.format(location, status))

Copy/paste the following into the 'Intent Schema' box:

{   

"intents": [{
        "intent": "LocationControlIntent",
        "slots": [{
            "name": "status",
            "type": "GPIO_CONTROL"
        },
                  {
            "name": "location",
            "type": "LOCATION"
        }]
    }]

}

Next, click 'Add Slot Type' and under 'Enter Type' write in 'LOCATION'. Under 'Enter Values' write down any words you want to be able to use to trigger this intent, for example:

fan
light
television

Copy/paste the following into the 'Sample Utterances' box:

LocationControlIntent to turn {location} {status}
LocationControlIntent to change the {location} to {status}

Click on 'Save' and it will update your settings and after that the new phrases will be usable. Example:

Alexa, tell Raspberry Pi to turn fan on
Alexa, tell Raspberry Pi to change the light to off

Comments

author
DanielP561 (author)2017-01-23

There is way to create your own certificate if you have a dns forward IP. I do have a dyndns,org account and it is working great. Just follow the method display by amazon. Learn how to create a self signed certificate.

Add the ssl_context into your python script et voila! no need to ngrok.

if __name__ == '__main__':

app.run(host='192.168.1.22',port=5000,debug=True,ssl_context=('certificate.pem','private-key.pem'))

My router forward the port 5000 to my raspberry Pi IP which is 192.168.1.22

author
MarcusS84 (author)DanielP5612017-02-24

I tried this but it's not working :-(

If I config skill to connect to my dns-adress, the request does not even come trough to flask-server. If I try URL in webbrowser I get "405 Method not allowed" what would be correct.

At the end if I use curl on my rpi to call flask-server it is reached but I got "ValueError: No JSON object could be decoded". so what is wrong?

author
MarcusS84 (author)MarcusS842017-02-24

additional info: I use fritz!box 6360 cable in germany as router. using

myfritz.net dns-service

author
DanielP561 (author)MarcusS842017-02-24

The router forward the port 5000 to the Pi but outside it is the https port which is 443.

InternetIP port 443 to intranet IP port 5000

author
MarcusS84 (author)DanielP5612017-02-24

actually on port 443 webinterface of fritz!box is already running! so I suggested is is outside 5000 to inside 5000

author
markus72 (author)MarcusS842017-08-12

@MarcusS84

Have you figured it out how to configure it to use the alexa skill over https with the flask-sever and myFritz?

I could need some help ...

I also get the message 'The remote endpoint could not be called, or the response it returned was invalid.'

If I try URL in a browser I get "405 Method not allowed", so the server is reachble.

Thanks.

author
DanielP561 (author)markus722017-08-12

I play a lot to get them working and the only which was ok is to forward port 443 to 5000. I thinks is because of the key encryption.

https://stackoverflow.com/questions/34369780/does-alexa-skill-kit-listen-on-other-port-of-ssl-besides-443

author
monupurohit made it! (author)2017-02-27

Thanks Patrick

I was able to turn on/off LED connect to raspberry pi with Alexa.

Could you/anyone help in for how to change the pin with device name.

i.e. how to change the command

from

Alexa, tell Raspberry Pi to turn pin twenty one on

to

Alexa, tell Raspberry Pi to turn "FAN" on

In service request i'm getting:

"name": "GPIOControlIntent",
"slots": {
"pin": {
"name": "pin",
"value": "?"

Alexa and raspberrypi.jpg
author
RogerioNeiva (author)monupurohit2017-06-13

Hi, I also want this type of command. Did you find out how to do it ?

Thanks

author
PatrickD126 (author)RogerioNeiva2017-06-13

I updated the guide. Please see step 11.

author
RogerioNeiva (author)PatrickD1262017-06-14

Thanks but I am missing something. Please bare with me:

1. I added the code to the end of gpio_control.py. I did not delete the former code

2. I deleted the former Intent Schema Box because it was giving me Save errors

3. I created the slot types but did not delete the previous one

4. I created the 2 new Utterances and deleted the previous one

Now, in test it seems to work (turn fan on) but, and here is my question, how do I map FAN to GPIO pin 25 ?

Thanks for your help. Wonderful job.

Regards

author
PatrickD126 (author)RogerioNeiva2017-06-14

I updated the code for the LocationControlIntent. That should get it working with a fan and can be customized based on the location/pin combination you are interested in.

author
RogerioNeiva (author)PatrickD1262017-06-15

Patrick, It's working great. I am controlling now 6 GPIO pins (Up, Down, Stop - Left and Right blinds).

Next step will be to replace ngrok to a permanent solution and launch

gpio_control.py program automatically so as to enable everything when booting.

Again, thanks for your help

author

could you share your code? please

author

Hi, no problem. I am on vacation now until the end of August and I do not have the code with me. I can beginning of September if this suits you. I also have to change a line of code and send you screenshots of the Alexa intents and utterances.

Regards

author

could you share your code? I really appreciate it I'm trying to do the same thing , thank you Roger

author
RogerioNeiva (author)PatrickD1262017-06-14

Hi, it's working great, thanks. Tomorrow I will change the word in order to move my blinds up & down with voice commands.

Thanks for you help.

author
nswilhelm (author)2016-12-07

I'm new to python and Pi...and Linux so I might have missed something but for me to get this to work I had to add this code to the end of gpio_control.py

if __name__ == '__main__':

port = 5000 #the custom port you want

app.run(host='0.0.0.0', port=port)

author
BenjaminF10 (author)nswilhelm2017-08-11

Excellent, thanks!

author
hyptechdev (author)nswilhelm2017-06-06

thank you, I have to add your code to work:

from flask import Flask
from flask_ask import Ask, statement, convert_errors
import RPi.GPIO as GPIO
import logging

GPIO.setmode(GPIO.BCM)

app = Flask(__name__)
ask = Ask(app, '/')

logging.getLogger("flask_ask").setLevel(logging.DEBUG)

@ask.intent('GPIOControlIntent', mapping={'status': 'status', 'pin': 'pin'})
def gpio_control(status, pin):

try:
pinNum = int(pin)
except Exception as e:
return statement('Pin number not valid.')

GPIO.setup(pinNum, GPIO.OUT)

if pinNum == 5 or pinNum == 6 or pinNum == 26 or pinNum == 27:
if status in ['on', 'high']: GPIO.output(pinNum, GPIO.LOW) #reversed pins on relay
if status in ['off', 'low']: GPIO.output(pinNum, GPIO.HIGH)
else:
if status in ['on', 'high']: GPIO.output(pinNum, GPIO.HIGH)
if status in ['off', 'low']: GPIO.output(pinNum, GPIO.LOW)

return statement('Turning pin {} {}'.format(pin, status))

if __name__ == '__main__':
port = 5000 #the custom port you want, 5000 must be open/or port forward from the router

app.run(host='0.0.0.0', port=port)

author
dvelazquez (author)nswilhelm2016-12-12

This helped me get it running.

Thanks

author
BenjaminF10 (author)2017-08-11

Seems like in Rasbiann Jessie 4.9 (July 2017) this breaks a bit.

To fix manually install libffi-dev and libssl-dev like this

sudo apt-get install libffi-dev
sudo apt-get install libssl-dev

author
mohamadfarhang (author)2017-08-09

thanks for your instruction

I couldn't get it to work I get this Error when I type sudo python gpio_control.py , can anyone help me please?

raceback (most recent call last):
File "gpio_control.py", line 2, in <module>
from flask_ask import Ask, statement, convert_errors
File "/usr/local/lib/python2.7/dist-packages/flask_ask/__init__.py", line 9, i n <module>
from .core import (
File "/usr/local/lib/python2.7/dist-packages/flask_ask/core.py", line 7, in <m odule>
import aniso8601
File "/usr/local/lib/python2.7/dist-packages/aniso8601/__init__.py", line 12, in <module>
from aniso8601.duration import parse_duration
File "/usr/local/lib/python2.7/dist-packages/aniso8601/duration.py", line 10, in <module>
import dateutil.relativedelta
ImportError: No module named dateutil.relativedelta


author

Try:
sudo pip install python-dateutil --upgrade

source:
https://stackoverflow.com/questions/8418636/python-cannot-find-dateutil-relativedelta

author
mohamadfarhang (author)2017-08-09

can anyone help me please with this error? I tried everything , I really appreciate it if you could help me

Traceback (most recent call last):
File "gpio_control.py", line 2, in <module>
from flask_ask import Ask, statement, convert_errors
File "/usr/local/lib/python2.7/dist-packages/flask_ask/__init__.py", line 9, in <module>
from .core import (
File "/usr/local/lib/python2.7/dist-packages/flask_ask/core.py", line 7, in <module>
import aniso8601
File "/usr/local/lib/python2.7/dist-packages/aniso8601/__init__.py", line 12, in <module>
from aniso8601.duration import parse_duration
File "/usr/local/lib/python2.7/dist-packages/aniso8601/duration.py", line 10, in <module>
import dateutil.relativedelta
ImportError: No module named dateutil.relativedelta

author
IvanS239 (author)2017-07-09

Hi, when I try the script by test in amazon developed site, I got some errors:

[2017-07-09 14:19:19,120] ERROR in app: Exception on / [POST]

Traceback (most recent call last):

File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1982, in wsgi_app

response = self.full_dispatch_request()

File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1614, in full_dispatch_request

rv = self.handle_user_exception(e)

File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1517, in handle_user_exception

reraise(exc_type, exc_value, tb)

File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1612, in full_dispatch_request

rv = self.dispatch_request()

File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1598, in dispatch_request

return self.view_functions[rule.endpoint](**req.view_args)

File "/usr/local/lib/python2.7/dist-packages/flask_ask/core.py", line 486, in _flask_view_func

ask_payload = self._alexa_request(verify=self.ask_verify_requests)

File "/usr/local/lib/python2.7/dist-packages/flask_ask/core.py", line 450, in _alexa_request

timestamp = aniso8601.parse_datetime(alexa_request_payload['request']['timestamp'])

File "/usr/local/lib/python2.7/dist-packages/aniso8601/time.py", line 120, in parse_datetime

isodatestr, isotimestr = isodatetimestr.split(delimiter)

AttributeError: 'long' object has no attribute 'split'

author
AndrewK260 (author)IvanS2392017-07-22

Just update flask-ask.

sudo pip install flask-ask --upgrade

author
khaemels (author)2017-06-03

I got the following errors when i run the gpio_control.py

Traceback (most recent call last):

File "gpio_control.py", line 2, in <module>

from flask_ask import Ask, statement, convert_errors

File "/usr/local/lib/python2.7/dist-packages/flask_ask/__init__.py", line 9, in <module>

from .core import (

File "/usr/local/lib/python2.7/dist-packages/flask_ask/core.py", line 12, in <module>

from . import verifier, logger

File "/usr/local/lib/python2.7/dist-packages/flask_ask/verifier.py", line 8, in <module>

from OpenSSL import crypto

File "/usr/local/lib/python2.7/dist-packages/OpenSSL/__init__.py", line 8, in <module>

from OpenSSL import rand, crypto, SSL

File "/usr/local/lib/python2.7/dist-packages/OpenSSL/rand.py", line 12, in <module>

from OpenSSL._util import (

File "/usr/local/lib/python2.7/dist-packages/OpenSSL/_util.py", line 6, in <module>

from cryptography.hazmat.bindings.openssl.binding import Binding

ImportError: No module named cryptography.hazmat.bindings.openssl.binding

author
NimeshG2 (author)khaemels2017-06-12

Try

pip install cryptography

this will solve your problem.

author
SharjeelG (author)NimeshG22017-06-13

This works for me..

Thank you..

author
NimeshG2 made it! (author)2017-06-12

Very nicely explained. Worked for me perfectly. Only that I had to install couple of dependencies separately. Also instruction model in amazon developer account doesn't let you do all the things the way they are mentioned here. But still doable, thank you so much for this.

IMG_5711.PNG
author
SharjeelG (author)2017-06-06

Traceback (most recent call last):

File "gpio_control.py", line 2, in <module>

from flask_ask import Ask, statement, convert_errors

ImportError: No module named 'flask_ask'

I have installed flask ask but still it showing this error.

Can u help ??

author
jllwton (author)2017-05-14

I'm getting a "405 method not allowed" error from ngrok when Amazon attempts to connect to the Echo. At first I thought it was an issue with port forwarding on my router (BT Smart Hub), so I tried connecting via the Pi's own browser to http://localhost:5000/ but that too returns the same error. Has anyone encountered and solved this issue, please?

author
RogerioNeiva (author)2017-04-29

I have a very strange situation. Simulator works fine but when I tell Alexa the command, it gives a sound of acknowledge and does nothing. It seems that it understood the command but failed to execute it.

But if I use the REVERB app, either for iOS or Android, it works fine. It seems Alexa is not capable to forward the commands but RVEREB is. Any ideas ?

Thank you

author

I just found out that changing Alexa's language from UK English to US English solved the situation. Now it works perfectly.

author
David_Lin made it! (author)2017-04-21

Test OK! Great!

1492840717111-950481477.jpg
author
logicron (author)2016-12-14

So my command "turn pin twenty one on" is working in Service Simulator, it turn on the pin on and off, all good. It also appears Enabled in the Amazon Alexa app under Your Skills (for some reason you cant see developed skills through the browser). But it seems Alexa does not understand my command so when i say "Alexa, tell Raspberry Pi to turn pin twenty one on" (Invocation Name is Raspberry Pie just like in the guide) either she answers "i dont understand" or "i cant see a device named Raspberry Pi". Anyone experiencing something similar?

author
RobertH503 (author)logicron2017-04-21

ditto here logicron. test works great, skill shows up on my iphone and alexa says "can't find that..."

author
RogerioNeiva (author)logicron2017-03-26

I am experiencing this. Did you solve it ? Can you please tell me how ?

Thank you and regards

author
surendrakane (author)2017-04-02

Great instructable ! Was able to do in 30 mins <.
Some explanation on the python code would be great. For example where is the gpio_control function call ?
How the intent schema works ?

author
RogerioNeiva (author)2017-04-01

OK, thanks for offering help.

So my command "turn pin twenty five on" is working in Service Simulator, it turns the pin on and off, all good. It also appears Enabled in the Amazon Alexa app under Your Skills with the correct invocation name. But it seems Alexa does not understand my command so when I say "Alexa, tell Butler to turn pin twenty five on" (Invocation Name is Butler) either she answers "I dont understand" or "I cant see a device named Raspberry Pi" or a beep but no action. I am not getting any verbal response from Alexa something like "Turning pin 25 off" either.

Thank you

author
Tany44 (author)2017-03-28

Can somebody help me understand the code

author
logicron (author)2016-12-11

Thank you for a great Instructable! Im getting an error when i use the Service Simulator to turn pin twenty one on. I get error message in the Service Responce "There was an error calling the remote endpoint, which returned HTTP 502 : Bad Gateway". Can anyone help me please

author
nswilhelm (author)logicron2016-12-12

yeah, i had that too. just add this code after the "return statement..."

if __name__ == '__main__':

port = 5000 #the custom port you want

app.run(host='0.0.0.0', port=port)

....also make sure ngrok then uses that same port number

pi.png
author
Tany44 (author)nswilhelm2017-03-26

Thanks. This helped a lot.

author
logicron (author)nswilhelm2016-12-13

It works for me now with the Service Simulator, but i cant get Alexa to understand the command when i ask her myself. Did you also experience that?

author
logicron (author)nswilhelm2016-12-13

Thank you that worked!

author
dvelazquez (author)logicron2016-12-11

I get the same error.

Should gpio_control.py keep running? it closes after I call it with sudo python gpio_control.py....

author
logicron (author)dvelazquez2016-12-13

Yes it seems you need to run both gpio_control.py and ngrok. Works for me now after nswilhelm told me to add the code below.

if __name__ == '__main__':

port = 5000 #the custom port you want

app.run(host='0.0.0.0', port=port)

author
LuisA297 (author)2017-03-21

It took a while but I made it work, congratulations