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

Picture of 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

Picture of AWS Account

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

Step 5: Alexa Skill - Information

Picture of 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

Picture of 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

Picture of 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

Picture of 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

Picture of 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

DũngL10 (author)2017-10-07

i have problem: services respones There was an error calling the remote endpoint, which returned HTTP 502 : Bad Gateway

charanksri (author)2017-08-28

Hello, I get the 502 bad gateway error and when I try to access the ngrok site, it says this:

The connection to https://eacbfc14.ngrok.io was successfully tunneled to your ngrok client, but the client failed to establish a connection to the local address localhost:5000.

Make sure that a web service is running on localhost:5000 and that it is a valid address.

The error encountered was: dial tcp [::1]:5000: getsockopt: connection refused

Please help. Thanks.

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)

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)

DavidSchh (author)hyptechdev2017-08-23

thanks man. that worked for me

BenjaminF10 (author)nswilhelm2017-08-11

Excellent, thanks!

dvelazquez (author)nswilhelm2016-12-12

This helped me get it running.

Thanks

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

NimeshG2 (author)khaemels2017-06-12

Try

pip install cryptography

this will solve your problem.

DavidSchh (author)NimeshG22017-08-21

This didn't work 4 me :( I got this massage:

...

distutils.errors.DistutilsError: Setup script exited with error: command 'arm-linux-gnueabihf-gcc' failed with exit status 1

----------------------------------------

Cleaning up...

Command /usr/bin/python -c "import setuptools, tokenize;__file__='/tmp/pip-build-VlklCH/cryptography/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-ZpDcP0-record/install-record.txt --single-version-externally-managed --compile failed with error code 1 in /tmp/pip-build-VlklCH/cryptography

Storing debug log for failure in /home/pi/.pip/pip.log

SharjeelG (author)NimeshG22017-06-13

This works for me..

Thank you..

mpuh2017 (author)2017-08-20

I can't find a solution to my problem.

I keep getting

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

Flask-ask has been installed.

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

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?

MarcusS84 (author)MarcusS842017-02-24

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

myfritz.net dns-service

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

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

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.

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

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": "?"

RogerioNeiva (author)monupurohit2017-06-13

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

Thanks

PatrickD126 (author)RogerioNeiva2017-06-13

I updated the guide. Please see step 11.

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

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.

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

could you share your code? please

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

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

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.

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

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


Try:
sudo pip install python-dateutil --upgrade

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

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

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'

AndrewK260 (author)IvanS2392017-07-22

Just update flask-ask.

sudo pip install flask-ask --upgrade

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.

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 ??

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?

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

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

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

Test OK! Great!

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?

RobertH503 (author)logicron2017-04-21

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

RogerioNeiva (author)logicron2017-03-26

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

Thank you and regards

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 ?

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

Tany44 (author)2017-03-28

Can somebody help me understand the code

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

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

Tany44 (author)nswilhelm2017-03-26

Thanks. This helped a lot.