Story
This project is an early prototype of a voice controlled robot using the mindstorm ev3 and the amazon echo dot using alexa voice controlled framework
Build Order
open Alexa skills kit. click on start a skill. click create skill. fill skill name. and again click create skill.
go to build tab. go to invocation on left. fill "my smart home" in invocation name. (note : if you change this invocation name with something else, you will have to change its name in skill.json file in the code section also which we will be putting in next step.).click save model.
go to json editor tab and paste the file skill.json in the code section
sign in to AWS connsole. create AWS account if you don't have.after signing in, type Lambda in find services and click on it.
click on create function. select author from scratch.type function name. select python 2.7 in runtime field. click on create function.
click on add trigger.select alexa skills kit from dropdown. go back to amazon developer console where we pasted the skill.json. go to endpoint located on left.copy your skill id and paste it in skill id section on your AWS side.click add.reload the page.in code entry type select edit code inline and paste the lambda_function.js in code section. replace 'your-device-id' with your particle photon's device id which you can get from particle web ide. Also replace 'your-access-token' with your particle access token.click on save
lamda_function.js
const Alexa = require('ask-sdk-core');
const persistenceAdapter = require('ask-sdk-s3-persistence-adapter');
const Util = require('./util');
const Common = require('./common');
const DEFAULT_PERSISTENT_ATTRIBUTES = require('./default_attributes.json')
// The namespace of the custom directive to be sent by this skill
const NAMESPACE = 'Custom.Mindstorms.Gadget';
// The name of the custom directive to be sent this skill
const NAME_CONTROL = 'control';
const Https = require('https');
nk https://developer.amazon.com/docs/alexa-gadgets-toolkit/send-gadget-custom-directive-from-skill.html#respond}
*/
exports.build = function (endpointId, namespace, name, payload) {
// Construct the custom directive that needs to be sent
// Gadget should declare the capabilities in the discovery response to
// receive the directives under the following namespace.
return {
type: 'CustomInterfaceController.SendDirective',
header: {
name: name,
namespace: namespace
},
endpoint: {
endpointId: endpointId
},
payload
};
};
exports.putSessionAttribute = function(handlerInput, key, value) {
const attributesManager = handlerInput.attributesManager;
let sessionAttributes = attributesManager.getSessionAttributes();
sessionAttributes[key] = value;
attributesManager.setSessionAttributes(sessionAttributes);
};
exports.getConnectedEndpoints = function(apiEndpoint, apiAccessToken) {
// The preceding https:// need to be stripped off before making the call
apiEndpoint = (apiEndpoint || '').replace('https://', '');
return new Promise(((resolve, reject) => {
const options = {
host: apiEndpoint,
path: '/v1/endpoints',
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + apiAccessToken
}
};
const request = Https.request(options, (response) => {
response.setEncoding('utf8');
let returnData = '';
response.on('data', (chunk) => {
returnData += chunk;
});
response.on('end', () => {
resolve(JSON.parse(returnData));
});
response.on('error', (error) => {
reject(error);
});
});
request.end();
}));
};
// function to return the endpoint associated with the EV3 robot
const getEndpointID = async function (handlerInput) {
// get the stored endpointId from the attributesManager
const attributesManager = handlerInput.attributesManager;
var endpointId = attributesManager.getSessionAttributes().endpointId || [];
// if there is no stored endpointId, query the connected endpoints and store the new endpointId
if (endpointId.length === 0) {
const request = handlerInput.requestEnvelope;
let { apiEndpoint, apiAccessToken } = request.context.System;
let apiResponse = await Util.getConnectedEndpoints(apiEndpoint, apiAccessToken);
if ((apiResponse.endpoints || []).length !== 0) {
endpointId = apiResponse.endpoints[0].endpointId || [];
Util.putSessionAttribute(handlerInput, 'endpointId', endpointId);
}
}
return endpointId;
}
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle: async function (handlerInput) {
// check for a connected EV3 brick
const endpointId = getEndpointID(handlerInput);
// speak an error message to the user if there is no EV3 brick connected
if (endpointId.length === 0) {
return handlerInput.responseBuilder
.speak("I couldn't find an EV3 Brick connected to this Echo device.")
.getResponse();
}
return handlerInput.responseBuilder
.speak(`Welcome, you can start issuing commands`)
.reprompt(`Awaiting commands`)
.getResponse();
}
};
exports.handler = Alexa.SkillBuilders.custom()
.withPersistenceAdapter(
new persistenceAdapter.S3PersistenceAdapter({ bucketName: process.env.S3_PERSISTENCE_BUCKET })
)
.addRequestHandlers(
LaunchRequestHandler,
Common.SessionEndedRequestHandler,
Common.IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
)
.addRequestInterceptors(Common.RequestInterceptor)
.addErrorHandlers(
Common.ErrorHandler,
)
.lambda()
copy ARN from top and paste it in default region section in amazonn developer console.click save endpoint.click on build tab on top and click on build model in right section.
configure the EV3 to your wifi and make sure that the it is in sync mode. This allow for the EV3 bluetooth function to activate. Once everything is setup correctly flash the EV3 using Visual Studion with the controller.py .
controller.py is the EV3 main code that used to run the EV3 hummanoid. It more or less the gadget code in term of this context.
import time
import json
from math import *
from ev3dev2.motor import LargeMotor, MediumMotor
from ev3dev2.sensor.lego import ColorSensor, InfraredSensor
from ev3dev2.led import Leds
class Robot(Gadget):
def __init__(self):
super().__init__()
# initialize all of the motors
print('Initializing devices')
self.leds = Leds()
self.motor_hand = LargeMotor(address='outA')
self.motor_claw = MediumMotor(address='outC')
def on_connected(self, device_addr):
self.leds.set_color('LEFT', 'GREEN')
self.leds.set_color('RIGHT', 'GREEN')
print("{} connected to Echo device".format(self.friendly_name))
def on_disconnected(self, device_addr):
self.leds.set_color('LEFT', 'BLACK')
self.leds.set_color('RIGHT', 'BLACK')
print("{} disconnected from Echo device".format(self.friendly_name))
# the function called to receive gadget control directives from the Alexa Skill through the connected Alexa device
def on_custom_mindstorms_gadget_control(self, directive):
# decode the directive payload into a JSON object
payload = json.loads(directive.payload.decode("utf-8"))
print("Control payload: {}".format(payload))
# determine which command to be executed
control_type = payload['type']
if control_type == 'On':
# get the source and destination states for this command
src_state = State[payload['state']]
self.motors_claw.on(20)
time.sleep(2)
self.motors_claw.off(brake=True)
elif control_type == 'Off':
# get the source and destination states for this command
src_state = State[payload['state']]
self.motors_claw.on(-20)
time.sleep(2)
self.motors_claw.off(brake=True)
# called at program startup
def main():
# create a robot instance
robot = Robot()
# run the main function to handle Alexa Gadget code
robot.main()
# poweroff after the execution has been completed (or program exited)
robot.poweroff()
if __name__ == '__main__':
main()
Comments