Dominic Varakukala
Created December 24, 2019

Alexa Voice Controlled Humanoid

Humanoid robot voice controlled with Alexa.

BeginnerShowcase (no instructions)10 hours28
Alexa Voice Controlled Humanoid

Things used in this project

Story

Read more

Schematics

Build Instruction

Get from https://magbuhatrobotics.files.wordpress.com/2017/05/chapter-19.pdf

Code

lamda.js

JavaScript
lamda code
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()

controller.py

Python
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()

Credits

Dominic Varakukala

Dominic Varakukala

1 project • 1 follower

Comments