Things used in this project

Hardware components:
Amazon tap
Amazon Alexa Amazon Tap
×1
Pi 3 02
Raspberry Pi 3 Model B
×1
Relay (generic)
×2
Huawei 3G Dongle
×1
11026 02
Jumper wires (generic)
×1
09590 01
LED (generic)
×1
Software apps and online services:
Dp image kit 02
Amazon Alexa Alexa Skills Kit
Screen%20shot%202015 07 20%20at%206.10.26%20pm
Amazon Web Services AWS Lambda
Ha 2up iot
Amazon Web Services AWS IoT
Raspberry Pi Raspbian Jessie

Schematics

VUI Diagram
VUI diagram for the project
Alexaboat%20vui%20diagram
Connections
Relays I used had 1 more pin on them. So I'm not sure if I drew this right. But it shows the basic idea.
Connections.fzz

Code

Intent Schema.jsonJSON
{
   "intents":[
      {
         "intent":"Open",
         "slots":[
            {
               "name":"Task",
               "type":"Input"
            }
         ]
      },
      {
         "intent":"Close",
         "slots":[
            {
               "name":"Task",
               "type":"Input"
            }
         ]
      },
      {
         "intent":"Anchor",
         "slots":[
            {
               "name":"Direction",
               "type":"LITERAL"
            },
            {
               "name":"Distance",
               "type":"NUMBER"
            }
         ]
      }
   ]
}
Sample Utterances.jsonJSON
Open {Task}
Close {Task}
Anchor {up | Direction} {twenty five | Distance} meters
Anchor {down | Direction} {one | Distance} meters
package.jsonJSON
for use with Lambda
{
  "name": "Bearcat",
  "version": "0.0.1",
  "description": "MQTT from lambda",
  "main": "index.js",
  "dependencies": {
    "aws-iot-device-sdk": "*"
  }
}
index.jsJavaScript
for use with Lambda
/**
 * Control your boat with your voice, using Amazon Alexa, Lambda, IOT, MQTT.
 */

var awsIot = require('aws-iot-device-sdk');
var config = require("./config");

var deviceName = "AlexaBoat";

var mqtt_config = {
    "keyPath": "./certs/YourKey-private.pem.key",
    "certPath": "./certs/YourKey-certificate.pem.crt",
    "caPath": "./certs/rootCA.key",
    "host": config.host,
    "port": 8883,
    "clientId": "RaspberryPi-" + deviceName, 
    "region":"us-west-1",
    "debug":true
};

var ctx = null;
var client = null;

// Route the incoming request based on type (LaunchRequest, IntentRequest, etc.) The JSON body of the request is provided in the event parameter.
exports.handler = function (event, context) {
    try {
        ctx = context;

        if (event.session.application.applicationId !== config.app_id) {
             ctx.fail("Invalid Application ID");
         }

        client = awsIot.device(mqtt_config);

        client.on("connect",function(){
            console.log("Connected to AWS IoT");
        });


        if (event.session.new) {
            onSessionStarted({requestId: event.request.requestId}, event.session);
        }

        if (event.request.type === "LaunchRequest") {
            onLaunch(event.request, event.session);
        }  else if (event.request.type === "IntentRequest") {
            onIntent(event.request, event.session);
        } else if (event.request.type === "SessionEndedRequest") {
            onSessionEnded(event.request, event.session);
            ctx.succeed();
        }
    } catch (e) {
        console.log("EXCEPTION in handler:  " + e);
        ctx.fail("Exception: " + e);
    }
};

/**
 * Called when the session starts.
 */
function onSessionStarted(sessionStartedRequest, session) {
}


/**
 * Called when the user launches the skill without specifying what they want.
 */
function onLaunch(launchRequest, session, callback) {

    // Dispatch to your skill's launch.
    getWelcomeResponse(callback);
}

/**
 * Called when the user specifies an intent for this skill.
 */
function onIntent(intentRequest, session ) {
    try{
    var intent = intentRequest.intent,
    intentName = intentRequest.intent.name;

    console.log("REQUEST to string =" + JSON.stringify(intentRequest));

    console.log("Ufuk =" + intentName);

    var callback = null;
    // Dispatch to your skill's intent handlers
    if ("Open" === intentName) {
        doCommandIntent(intent, session);
    }
    else if ("Close" === intentName) {
        doCommandIntent(intent, session);
    }
    else if ("Anchor" === intentName) {
        anchorCommandIntent(intent, session);
    }
    else if("AMAZON.HelpIntent"=== intentName) {
        helpCommandIntent(session);
    }
    else if("AMAZON.StopIntent"=== intentName) {
        ctx.succeed(buildResponse(null, buildSpeechletResponse(null, "Goodbye", "", true)));
    }
    else if("AMAZON.CancelIntent"=== intentName) {
        ctx.succeed(buildResponse(null, buildSpeechletResponse(null, "Goodbye", "", true)));
    }
    else {
        ctx.succeed(buildResponse(null, buildSpeechletResponse(null, "I can't help you with that. Invalid Request", "", true)));
    }
    }
    catch(ex)
    {
        ctx.succeed(buildResponse(null, buildSpeechletResponse(null, "There is an error with the request. Please try again.", "", false)));
    }
}

/**
 * Called when the user ends the session.
 * Is not called when the skill returns shouldEndSession=true.
 */
function onSessionEnded(sessionEndedRequest, session) {
    // Add cleanup logic here
}

// --------------- Functions that control the skill's behavior -----------------------

function getWelcomeResponse() {
    // If we wanted to initialize the session to have some attributes we could add those here.
    var sessionAttributes = {};
    var cardTitle = "Welcome";
    var speechOutput = "Welcome to the Boat Hand. You can say Open Lights, or, you can say Anchor Up for Twenty Meters... What can I help you with?";

    var repromptText = "Your Boat is ready for command.";
    var shouldEndSession = false;

    ctx.succeed(buildResponse(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession)));
}


function doCommandIntent(intent, session, callback) {
    var repromptText = null;
    var sessionAttributes = {};
    var shouldEndSession = true;
    var speechOutput = "";

    repromptText = "Tell me what is the command for the boat.";
    
    var task = intent.slots.Task.value;
    
    var punctuationless = task.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g,"");
    var task = punctuationless.replace(/\s{2,}/g," ");
    
    var validTasks = [ "LIGHTS" ];
    
    if (validTasks.indexOf(task.toString().toUpperCase()) < 0)
    {
        speechOutput = "I couldn't understand the command "+task+". Please try again.";
        ctx.succeed(buildResponse(sessionAttributes, buildSpeechletResponse(null, speechOutput, repromptText, false)));
    }
    else
    {
        var cardTitle = "Executing Open command " + task ;
        speechOutput = "Executing command " + task;
        mqttPublish(intent, sessionAttributes, cardTitle, speechOutput, repromptText, shouldEndSession);
    }
}

function anchorCommandIntent(intent, session, callback) {
    var repromptText = null;
    var sessionAttributes = {};
    var shouldEndSession = true;
    var speechOutput = "";

    repromptText = "Tell me what is the command for the boat.";

    var direction = intent.slots.Direction.value;
    var distance = intent.slots.Distance.value;
    
    
    var validDirections = [ "UP","DOWN" ];
    
    console.log("ufuk " + new RegExp(validDirections.join("|")).test(direction.toUpperCase()));
    
    if(distance === "?" || direction === "?")
    {
        throw "error";
    }
    else if(new RegExp(validDirections.join("|")).test(direction.toUpperCase()))
    {
        var cardTitle = "Executing command Anchor" ;
        speechOutput = "Executing Anchor " + direction + " for " + distance + " meters";
        mqttPublish(intent, sessionAttributes, cardTitle, speechOutput, repromptText, shouldEndSession);
    }
    else{
        speechOutput = "I couldn't understand the command "+ direction+". Please try again.";
        ctx.succeed(buildResponse(sessionAttributes, buildSpeechletResponse(null, speechOutput, repromptText, false)));
    }
}

function helpCommandIntent(intent, session, callback) {
    var repromptText = null;
    var sessionAttributes = {};
    var shouldEndSession = false;
    var speechOutput = "";

    var cardTitle = "Help" ;
    var speechOutput = "You can say Open Lights, or, you can say Anchor Up for Twenty Meters... What can I help you with?";
    var repromptText = "What can I help you with?";
        
    ctx.succeed(buildResponse(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession)));
}

function mqttPublish(intent, sessionAttributes, cardTitle, speechOutput, repromptText, shouldEndSession)
{
    var strIntent = JSON.stringify(intent);
    console.log("mqttPublish:  INTENT text = " + strIntent);
    
    client.publish(config.topic, strIntent, function() {
        client.end();
    });
    
    client.on("close", (function () {
        console.log("MQTT CLIENT CLOSE - thinks it's done, successfully. ");
        ctx.succeed(buildResponse(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession)));
    }));

    client.on("error", (function (err, granted) {
        console.log("MQTT CLIENT ERROR!!  " + err);
    }));
}


// --------------- Helpers that build all of the responses -----------------------

function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
    if(title != null)
        return {
            outputSpeech: {
                type: "PlainText",
                text: output
            },
            card: {
                type: "Simple",
                title: title,
                content: output
            },
            reprompt: {
                outputSpeech: {
                    type: "PlainText",
                    text: repromptText
                }
            },
            shouldEndSession: shouldEndSession
        };
    else
        return {
            outputSpeech: {
                type: "PlainText",
                text: output
            },
            reprompt: {
                outputSpeech: {
                    type: "PlainText",
                    text: repromptText
                }
            },
            shouldEndSession: shouldEndSession
        }
}

function buildResponse(sessionAttributes, speechletResponse) {
    return {
        version: "1.0",
        sessionAttributes: sessionAttributes,
        response: speechletResponse
    }
}
config.jsJavaScript
for use with Lambda
var config = {};

config.host = "Host Domain Here";
config.topic = "Topic Here";
config.app_id = "APP ID Here"

module.exports = config;
alexaTest.pyPython
For testing Relays
import RPi.GPIO as GPIO            # import RPi.GPIO module
from time import sleep             # lets us have a delay
GPIO.setmode(GPIO.BCM)             # choose BCM or BOARD
GPIO.setup(10, GPIO.OUT)           # set GPIO number as an output 

try:
    while True:
        GPIO.output(10, 1)         # set GPIO to 1/GPIO.HIGH/True
        print "HIGH!"
        sleep(4)                   # wait 4 seconds
        GPIO.output(10, 0)         # set GPIO to 0/GPIO.LOW/False
        print "LOW!"
        sleep(4)                   # wait 4 seconds

except KeyboardInterrupt:          # trap a CTRL+C keyboard interrupt
    GPIO.cleanup()                 # resets all GPIO ports used by this program
alexaBoat.pyPython
For Controlling Relays
#!/usr/bin/python

"""python boatstation for boat to be controlled by Amazon Alexa"""

import paho.mqtt.client as mqtt
import json, time, sys, ssl
import logging, logging.handlers
import RPi.GPIO as GPIO  

water = 24
lights = 17
navigation = 21
anchorDown = 11
anchorUp = 10

GPIO.setmode(GPIO.BCM)  

GPIO.setup(lights, GPIO.OUT)
GPIO.setup(water, GPIO.OUT)
GPIO.setup(navigation, GPIO.OUT)
GPIO.setup(anchorUp, GPIO.OUT)
GPIO.setup(anchorDown, GPIO.OUT)
GPIO.output(lights, GPIO.HIGH)
GPIO.output(water, GPIO.HIGH)
GPIO.output(navigation, GPIO.HIGH)
GPIO.output(anchorUp, GPIO.HIGH)
GPIO.output(anchorDown, GPIO.HIGH)

cert_path = "/home/pi/Desktop/pi_pi/certs/"
host = "Your Host"
topic = "Your Topic"
root_cert = cert_path + "rootCA.key"
cert_file = cert_path + "YourCert-certificate.pem.crt"
key_file = cert_path + "YourCert-private.pem.key"

globalmessage = ""  # to send status back to MQTT
isConnected = False
logger = logging.getLogger('alexaboat')

def do_command(data,cmd):  #   {"name":"CommandIntent","slots":{"Task":{"name":"Task","value":"launch"}}}
    task = str(data["slots"]["Task"]["value"])
    logger.info("TASK = " + task)
    global globalmessage

    if task.upper() == "LIGHTS":
        globalmessage = "executing command lights"
        print globalmessage
        toggle(lights,cmd)
    elif task.upper() == "WATER":
        globalmessage = "executing command water"
        print globalmessage
        toggle(water,cmd)
    elif task.upper() == "NAVIGATION":
        globalmessage = "executing command navigaiton"
        print globalmessage
        toggle(navigation,cmd)

def anchor_command(data):  #   {"name":"CommandIntent","slots":{"Task":{"name":"Task","value":"launch"}}}
    try:
        direction = str(data["slots"]["Direction"]["value"])
        distance = float(data["slots"]["Distance"]["value"])

        logger.info("Anchor = " + direction + " distance "+ str(distance))
        global globalmessage

        globalmessage = "executing command anchor" + direction+" " + str(distance) +" "+ str(distance/0.48)
        print globalmessage

        if direction.upper() == "UP":
   		anchor = anchorUp
	elif direction.upper() == "DOWN":
		anchor = anchorDown

        GPIO.output(anchor, GPIO.LOW)
        time.sleep(distance/0.48)
        GPIO.output(anchor, GPIO.HIGH)
        
    except (ValueError):
        logger.info("Anchor Error")
    
def toggle(gpio,cmd):
    logger.info("GPIO: " + str(gpio) + "CMD: " + str(cmd))
    GPIO.output(gpio, cmd)
        
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    isConnected = True
    logger.info("connected with result code " + str(rc))
    # Subscribing in on_connect() means that if we lose the connection and      
    # reconnect then subscriptions will be renewed.    
    client.subscribe(topic)

# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    logger.info("msg received, payload = " + str(msg.payload))
    data = json.loads(str(msg.payload))
    # Figure out INTENT -- was it Open, Close, Anchor, etc
    if "name" in data:
        if data["name"] == "Open":
            do_command(data,GPIO.LOW)
        if data["name"] == "Close":
            do_command(data,GPIO.HIGH)
        elif data["name"] == "Anchor":
            anchor_command(data)
        
def on_log(client, userdata, level, buf):
    logger.debug(buf)

def logger_init():
    logger.setLevel(logging.DEBUG)
    log_file_size = 1024 * 1024 * 1  # 1 MB
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(process)d - %(name)s : %(message)s')
    fh = logging.handlers.RotatingFileHandler('/home/pi/Desktop/pi_pi/logs/alexaboat.log', maxBytes=log_file_size, backupCount=5)
    fh.setFormatter(formatter)
    sh = logging.StreamHandler(sys.stdout)
    sh.setFormatter(formatter)
    logger.addHandler(fh)
    logger.addHandler(sh)
    logger.info('******************************************')
    logger.info('Starting up...')

logger_init()

client = mqtt.Client(client_id="alexaBoat.py")
client.on_connect = on_connect
client.on_message = on_message
client.on_log = on_log
client.tls_set(root_cert,
               certfile = cert_file,
               keyfile = key_file,
               cert_reqs=ssl.CERT_REQUIRED,
               tls_version=ssl.PROTOCOL_TLSv1_2,
               ciphers=None)

logger.info('connecting to mqtt broker')
client.connect(host, 8883, 60)

run = True

try:
    while run:
        client.loop()
        time.sleep(1)

        try:
            mypayload = '''{
                "StatusMessage": "%s"
            }''' % (globalmessage)

            if globalmessage != "":
                client.publish(topic, mypayload)
                globalmessage = ""

        except (TypeError):
            pass
    
except KeyboardInterrupt:
    print "Bye Bye!"
    GPIO.cleanup()    

Credits

12519107 788723347925954 1136277278 n
Ufuk ARSLAN
1 project • 6 followers
Entrepreneur; Tech-Lover; Co-Founder at Mobilist; Proud member of CanavarIT
Contact

Replications

Did you replicate this project? Share it!

I made one

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Sign up / LoginProjectsPlatformsTopicsContestsLiveAppsBetaBlog